Merge pull request !313 from 张澍坤/mastertags/v1.6.0
@@ -459,7 +459,7 @@ class LeastLikelyClassMethod(FastGradientSignMethod): | |||||
>>> net = Net() | >>> net = Net() | ||||
>>> inputs = np.array([[0.1, 0.2, 0.6], [0.3, 0, 0.4]]) | >>> inputs = np.array([[0.1, 0.2, 0.6], [0.3, 0, 0.4]]) | ||||
>>> labels = np.array([[0, 1, 0, 0, 0], [0, 0, 1, 0, 0]]) | >>> labels = np.array([[0, 1, 0, 0, 0], [0, 0, 1, 0, 0]]) | ||||
>>> attack = LeastLikelyClassMethod(network, loss_fn=SoftmaxCrossEntropyWithLogits(sparse=False)) | |||||
>>> attack = LeastLikelyClassMethod(net, loss_fn=SoftmaxCrossEntropyWithLogits(sparse=False)) | |||||
>>> adv_x = attack.generate(inputs, labels) | >>> adv_x = attack.generate(inputs, labels) | ||||
""" | """ | ||||
@@ -199,7 +199,7 @@ class BasicIterativeMethod(IterativeGradientMethod): | |||||
>>> return out | >>> return out | ||||
>>> | >>> | ||||
>>> net = Net() | >>> net = Net() | ||||
>>> attack = BasicIterativeMethod(netw, loss_fn=SoftmaxCrossEntropyWithLogits(sparse=False)) | |||||
>>> attack = BasicIterativeMethod(net, loss_fn=SoftmaxCrossEntropyWithLogits(sparse=False)) | |||||
""" | """ | ||||
def __init__(self, network, eps=0.3, eps_iter=0.1, bounds=(0.0, 1.0), | def __init__(self, network, eps=0.3, eps_iter=0.1, bounds=(0.0, 1.0), | ||||
is_targeted=False, nb_iter=5, loss_fn=None): | is_targeted=False, nb_iter=5, loss_fn=None): | ||||
@@ -36,32 +36,24 @@ class AdversarialDefense(Defense): | |||||
optimizer (Cell): Optimizer used to train the network. Default: None. | optimizer (Cell): Optimizer used to train the network. Default: None. | ||||
Examples: | Examples: | ||||
>>> class Net(Cell): | |||||
>>> def __init__(self): | |||||
>>> super(Net, self).__init__() | |||||
>>> self._reshape = P.Reshape() | |||||
>>> self._full_con_1 = Dense(28*28, 120) | |||||
>>> self._full_con_2 = Dense(120, 84) | |||||
>>> self._full_con_3 = Dense(84, 10) | |||||
>>> self._relu = ReLU() | |||||
>>> | |||||
>>> def construct(self, x): | |||||
>>> out = self._reshape(x, (-1, 28*28)) | |||||
>>> out = self._full_con_1(out) | |||||
>>> out = self.relu(out) | |||||
>>> out = self._full_con_2(out) | |||||
>>> out = self.relu(out) | |||||
>>> out = self._full_con_3(out) | |||||
>>> return out | |||||
>>> import numpy as np | |||||
>>> from mindspore.nn.optim.momentum import Momentum | |||||
>>> from mindarmour.adv_robustness.defenses import AdversarialDefense | |||||
>>> from mindspore import nn | |||||
>>> from tests.ut.python.utils.mock_net import Net | |||||
>>> | >>> | ||||
>>> net = Net() | >>> net = Net() | ||||
>>> lr = 0.0001 | |||||
>>> lr = 0.001 | |||||
>>> momentum = 0.9 | >>> momentum = 0.9 | ||||
>>> loss_fn = SoftmaxCrossEntropyWithLogits(sparse=True) | |||||
>>> optimizer = Momentum(net.trainable_params(), lr, momentum) | |||||
>>> batch_size = 32 | |||||
>>> num_class = 10 | |||||
>>> | |||||
>>> loss_fn = SoftmaxCrossEntropyWithLogits(sparse=False) | |||||
>>> optimizer = Momentum(net.trainable_params(), learning_rate=lr, momentum=momentum) | |||||
>>> adv_defense = AdversarialDefense(net, loss_fn, optimizer) | >>> adv_defense = AdversarialDefense(net, loss_fn, optimizer) | ||||
>>> inputs = np.random.rand(32, 1, 28, 28).astype(np.float32) | |||||
>>> labels = np.random.randint(0, 10).astype(np.int32) | |||||
>>> inputs = np.random.rand(batch_size, 1, 32, 32).astype(np.float32) | |||||
>>> labels = np.random.randint(10, size=batch_size).astype(np.int32) | |||||
>>> labels = np.eye(num_classes)[labels].astype(np.float32) | |||||
>>> adv_defense.defense(inputs, labels) | >>> adv_defense.defense(inputs, labels) | ||||
""" | """ | ||||
@@ -116,11 +108,31 @@ class AdversarialDefenseWithAttacks(AdversarialDefense): | |||||
ValueError: If replace_ratio is not between 0 and 1. | ValueError: If replace_ratio is not between 0 and 1. | ||||
Examples: | Examples: | ||||
>>> import numpy as np | |||||
>>> from mindspore.nn.optim.momentum import Momentum | |||||
>>> from mindarmour.adv_robustness.attacks import FastGradientSignMethod | |||||
>>> from mindarmour.adv_robustness.attacks import ProjectedGradientDescent | |||||
>>> from mindarmour.adv_robustness.defenses import AdversarialDefense | |||||
>>> from mindspore import nn | |||||
>>> from tests.ut.python.utils.mock_net import Net | |||||
>>> | |||||
>>> net = Net() | >>> net = Net() | ||||
>>> fgsm = FastGradientSignMethod(net) | |||||
>>> pgd = ProjectedGradientDescent(net) | |||||
>>> ead = AdversarialDefenseWithAttacks(net, [fgsm, pgd]) | |||||
>>> ead.defense(inputs, labels) | |||||
>>> lr = 0.001 | |||||
>>> momentum = 0.9 | |||||
>>> batch_size = 32 | |||||
>>> num_class = 10 | |||||
>>> loss_fn = nn.SoftmaxCrossEntropyWithLogits(sparse=False) | |||||
>>> optimizer = Momentum(net.trainable_params(), learning_rate=lr, momentum=momentum) | |||||
>>> | |||||
>>> fgsm = FastGradientSignMethod(net, loss_fn=loss_fn) | |||||
>>> pgd = ProjectedGradientDescent(net, loss_fn=loss_fn) | |||||
>>> ead = AdversarialDefenseWithAttack(net, [fgsm, pgd], loss_fn=loss_fn, | |||||
>>> optimizer=optimizer) | |||||
>>> | |||||
>>> inputs = np.random.rand(batch_size, 1, 32, 32).astype(np.float32) | |||||
>>> labels = np.random.randint(num_class, size=batch_size).astype(np.int32) | |||||
>>> labels = np.eye(num_classes)[labels].astype(np.float32) | |||||
>>> loss = ead.defense(inputs, labels) | |||||
""" | """ | ||||
def __init__(self, network, attacks, loss_fn=None, optimizer=None, | def __init__(self, network, attacks, loss_fn=None, optimizer=None, | ||||
@@ -187,11 +199,31 @@ class EnsembleAdversarialDefense(AdversarialDefenseWithAttacks): | |||||
ValueError: If replace_ratio is not between 0 and 1. | ValueError: If replace_ratio is not between 0 and 1. | ||||
Examples: | Examples: | ||||
>>> import numpy as np | |||||
>>> from mindspore.nn.optim.momentum import Momentum | |||||
>>> from mindarmour.adv_robustness.attacks import FastGradientSignMethod | |||||
>>> from mindarmour.adv_robustness.attacks import ProjectedGradientDescent | |||||
>>> from mindarmour.adv_robustness.defenses import EnsembleAdversarialDefense | |||||
>>> from mindspore import nn | |||||
>>> from tests.ut.python.utils.mock_net import Net | |||||
>>> | |||||
>>> net = Net() | >>> net = Net() | ||||
>>> fgsm = FastGradientSignMethod(net) | |||||
>>> pgd = ProjectedGradientDescent(net) | |||||
>>> ead = EnsembleAdversarialDefense(net, [fgsm, pgd]) | |||||
>>> ead.defense(inputs, labels) | |||||
>>> lr = 0.001 | |||||
>>> momentum = 0.9 | |||||
>>> batch_size = 32 | |||||
>>> num_class = 10 | |||||
>>> loss_fn = nn.SoftmaxCrossEntropyWithLogits(sparse=False) | |||||
>>> optimizer = Momentum(net.trainable_params(), learning_rate=lr, momentum=momentum) | |||||
>>> | |||||
>>> fgsm = FastGradientSignMethod(net, loss_fn=loss_fn) | |||||
>>> pgd = ProjectedGradientDescent(net, loss_fn=loss_fn) | |||||
>>> ead = EnsembleAdversarialDefense(net, [fgsm, pgd], loss_fn=loss_fn, | |||||
>>> optimizer=optimizer) | |||||
>>> | |||||
>>> inputs = np.random.rand(batch_size, 1, 32, 32).astype(np.float32) | |||||
>>> labels = np.random.randint(num_class, size=batch_size).astype(np.int32) | |||||
>>> labels = np.eye(num_classes)[labels].astype(np.float32) | |||||
>>> loss = ead.defense(inputs, labels) | |||||
""" | """ | ||||
def __init__(self, network, attacks, loss_fn=None, optimizer=None, | def __init__(self, network, attacks, loss_fn=None, optimizer=None, | ||||
@@ -36,9 +36,27 @@ class NaturalAdversarialDefense(AdversarialDefenseWithAttacks): | |||||
eps (float): Step size of the attack method(FGSM). Default: 0.1. | eps (float): Step size of the attack method(FGSM). Default: 0.1. | ||||
Examples: | Examples: | ||||
>>> import numpy as np | |||||
>>> from mindspore.nn.optim.momentum import Momentum | |||||
>>> from mindarmour.adv_robustness.defenses import NaturalAdversarialDefense | |||||
>>> from mindspore import nn | |||||
>>> from tests.ut.python.utils.mock_net import Net | |||||
>>> | |||||
>>> net = Net() | >>> net = Net() | ||||
>>> adv_defense = NaturalAdversarialDefense(net) | |||||
>>> adv_defense.defense(inputs, labels) | |||||
>>> lr = 0.001 | |||||
>>> momentum = 0.9 | |||||
>>> batch_size = 32 | |||||
>>> num_class = 10 | |||||
>>> | |||||
>>> loss_fn = nn.SoftmaxCrossEntropyWithLogits(sparse=False) | |||||
>>> optimizer = Momentum(net.trainable_params(), learning_rate=lr, momentum=momentum) | |||||
>>> | |||||
>>> nad = NaturalAdversarialDefense(net, loss_fn=loss_fn, optimizer=optimizer) | |||||
>>> | |||||
>>> inputs = np.random.rand(batch_size, 1, 32, 32).astype(np.float32) | |||||
>>> labels = np.random.randint(num_class, size=batch_size).astype(np.int32) | |||||
>>> labels = np.eye(num_classes)[labels].astype(np.float32) | |||||
>>> loss = nad.defense(inputs, labels) | |||||
""" | """ | ||||
def __init__(self, network, loss_fn=None, optimizer=None, | def __init__(self, network, loss_fn=None, optimizer=None, | ||||
bounds=(0.0, 1.0), replace_ratio=0.5, eps=0.1): | bounds=(0.0, 1.0), replace_ratio=0.5, eps=0.1): | ||||
@@ -41,9 +41,27 @@ class ProjectedAdversarialDefense(AdversarialDefenseWithAttacks): | |||||
norm_level (str): Norm type. 'inf' or 'l2'. Default: 'inf'. | norm_level (str): Norm type. 'inf' or 'l2'. Default: 'inf'. | ||||
Examples: | Examples: | ||||
>>> import numpy as np | |||||
>>> from mindspore.nn.optim.momentum import Momentum | |||||
>>> from mindarmour.adv_robustness.defenses import ProjectedAdversarialDefense | |||||
>>> from mindspore import nn | |||||
>>> from tests.ut.python.utils.mock_net import Net | |||||
>>> | |||||
>>> net = Net() | >>> net = Net() | ||||
>>> adv_defense = ProjectedAdversarialDefense(net) | |||||
>>> adv_defense.defense(inputs, labels) | |||||
>>> lr = 0.001 | |||||
>>> momentum = 0.9 | |||||
>>> batch_size = 32 | |||||
>>> num_class = 10 | |||||
>>> | |||||
>>> loss_fn = nn.SoftmaxCrossEntropyWithLogits(sparse=False) | |||||
>>> optimizer = Momentum(net.trainable_params(), learning_rate=lr, momentum=momentum) | |||||
>>> | |||||
>>> pad = ProjectedAdversarialDefense(net, loss_fn=loss_fn, optimizer=optimizer) | |||||
>>> | |||||
>>> inputs = np.random.rand(batch_size, 1, 32, 32).astype(np.float32) | |||||
>>> labels = np.random.randint(num_class, size=batch_size).astype(np.int32) | |||||
>>> labels = np.eye(num_classes)[labels].astype(np.float32) | |||||
>>> loss = pad.defense(inputs, labels) | |||||
""" | """ | ||||
def __init__(self, | def __init__(self, | ||||
network, | network, | ||||
@@ -73,9 +73,35 @@ class SimilarityDetector(Detector): | |||||
Default: 0.001 | Default: 0.001 | ||||
Examples: | Examples: | ||||
>>> detector = SimilarityDetector(model) | |||||
>>> detector.fit(ori, labels) | |||||
>>> adv_ids = detector.detect(adv) | |||||
>>> import numpy as np | |||||
>>> from mindspore.ops.operations import Add | |||||
>>> from mindspore.nn import Cell | |||||
>>> from mindspore import Model | |||||
>>> from mindspore import context | |||||
>>> from mindarmour.adv_robustness.detectors import SimilarityDetector | |||||
>>> | |||||
>>> class EncoderNet(Cell): | |||||
>>> def __init__(self, encode_dim): | |||||
>>> super(EncoderNet, self).__init__() | |||||
>>> self._encode_dim = encode_dim | |||||
>>> self.add = Add() | |||||
>>> def construct(self, inputs): | |||||
>>> return self.add(inputs, inputs) | |||||
>>> def get_encode_dim(self): | |||||
>>> return self._encode_dim | |||||
>>> | |||||
>>> np.random.seed(5) | |||||
>>> x_train = np.random.rand(10, 32, 32, 3).astype(np.float32) | |||||
>>> perm = np.random.permutation(x_train.shape[0]) | |||||
>>> benign_queries = x_train[perm[:10], :, :, :] | |||||
>>> suspicious_queries = x_train[perm[-1], :, :, :] + np.random.normal(0, 0.05, (10,) + x_train.shape[1:]) | |||||
>>> suspicious_queries = suspicious_queries.astype(np.float32) | |||||
>>> encoder = Model(EncoderNet(encode_dim=256)) | |||||
>>> detector = SimilarityDetector(max_k_neighbor=3, trans_model=encoder) | |||||
>>> num_nearest_neighbors, thresholds = detector.fit(inputs=x_train) | |||||
>>> detector.set_threshold(num_nearest_neighbors[-1], thresholds[-1]) | |||||
>>> detector.detect(benign_queries) | |||||
>>> detections = detector.get_detection_interval() | |||||
""" | """ | ||||
def __init__(self, trans_model, max_k_neighbor=1000, chunk_size=1000, | def __init__(self, trans_model, max_k_neighbor=1000, chunk_size=1000, | ||||
@@ -33,6 +33,42 @@ class EnsembleDetector(Detector): | |||||
detectors (Union[tuple, list]): List of detector methods. | detectors (Union[tuple, list]): List of detector methods. | ||||
policy (str): Decision policy, could be 'vote', 'all' or 'any'. | policy (str): Decision policy, could be 'vote', 'all' or 'any'. | ||||
Default: 'vote' | Default: 'vote' | ||||
Examples: | |||||
>>> import numpy as np | |||||
>>> from mindspore.ops.operations import Add | |||||
>>> from mindspore.nn import Cell | |||||
>>> from mindspore import Model | |||||
>>> from mindspore import context | |||||
>>> from mindarmour.adv_robustness.detectors import ErrorBasedDetector | |||||
>>> from mindarmour.adv_robustness.detectors import RegionBasedDetector | |||||
>>> from mindarmour.adv_robustness.detectors import EnsembleDetector | |||||
>>> | |||||
>>> class Net(Cell): | |||||
>>> def __init__(self): | |||||
>>> super(Net, self).__init__() | |||||
>>> self.add = Add() | |||||
>>> def construct(self, inputs): | |||||
>>> return self.add(inputs, inputs) | |||||
>>> | |||||
>>> class AutoNet(Cell): | |||||
>>> def __init__(self): | |||||
>>> super(AutoNet, self).__init__() | |||||
>>> self.add = Add() | |||||
>>> def construct(self, inputs): | |||||
>>> return self.add(inputs, inputs) | |||||
>>> | |||||
>>> np.random.seed(6) | |||||
>>> adv = np.random.rand(4, 4).astype(np.float32) | |||||
>>> model = Model(Net()) | |||||
>>> auto_encoder = Model(AutoNet()) | |||||
>>> random_label = np.random.randint(10, size=4) | |||||
>>> labels = np.eye(10)[random_label] | |||||
>>> magnet_detector = ErrorBasedDetector(auto_encoder) | |||||
>>> region_detector = RegionBasedDetector(model) | |||||
>>> region_detector.fit(adv, labels) | |||||
>>> detectors = [magnet_detector, region_detector] | |||||
>>> detector = EnsembleDetector(detectors) | |||||
>>> adv_ids = detector.detect(adv) | |||||
""" | """ | ||||
def __init__(self, detectors, policy="vote"): | def __init__(self, detectors, policy="vote"): | ||||
@@ -48,6 +48,20 @@ class ErrorBasedDetector(Detector): | |||||
bounds (tuple): (clip_min, clip_max). Default: (0.0, 1.0). | bounds (tuple): (clip_min, clip_max). Default: (0.0, 1.0). | ||||
Examples: | Examples: | ||||
>>> import numpy as np | |||||
>>> from mindspore.ops.operations import Add | |||||
>>> from mindspore.nn import Cell | |||||
>>> from mindspore import Model | |||||
>>> from mindspore import context | |||||
>>> from mindarmour.adv_robustness.detectors import ErrorBasedDetector | |||||
>>> class Net(Cell): | |||||
>>> def __init__(self): | |||||
>>> super(Net, self).__init__() | |||||
>>> self.add = Add() | |||||
>>> | |||||
>>> def construct(self, inputs): | |||||
>>> return self.add(inputs, inputs) | |||||
>>> | |||||
>>> np.random.seed(5) | >>> np.random.seed(5) | ||||
>>> ori = np.random.rand(4, 4, 4).astype(np.float32) | >>> ori = np.random.rand(4, 4, 4).astype(np.float32) | ||||
>>> np.random.seed(6) | >>> np.random.seed(6) | ||||
@@ -55,7 +69,7 @@ class ErrorBasedDetector(Detector): | |||||
>>> model = Model(Net()) | >>> model = Model(Net()) | ||||
>>> detector = ErrorBasedDetector(model) | >>> detector = ErrorBasedDetector(model) | ||||
>>> detector.fit(ori) | >>> detector.fit(ori) | ||||
>>> detected_res = detector.detect(adv) | |||||
>>> adv_ids = detector.detect(adv) | |||||
>>> adv_trans = detector.transform(adv) | >>> adv_trans = detector.transform(adv) | ||||
""" | """ | ||||
@@ -171,16 +185,29 @@ class DivergenceBasedDetector(ErrorBasedDetector): | |||||
In form of (clip_min, clip_max). Default: (0.0, 1.0). | In form of (clip_min, clip_max). Default: (0.0, 1.0). | ||||
Examples: | Examples: | ||||
>>> import numpy as np | |||||
>>> from mindspore.ops.operations import Add | |||||
>>> from mindspore.nn import Cell | |||||
>>> from mindspore import Model | |||||
>>> from mindspore import context | |||||
>>> from mindarmour.adv_robustness.detectors import ErrorBasedDetector | |||||
>>> class PredNet(Cell): | |||||
>>> def __init__(self): | |||||
>>> super(Net, self).__init__() | |||||
>>> self.add = Add() | |||||
>>> | |||||
>>> def construct(self, inputs): | |||||
>>> return self.add(inputs, inputs) | |||||
>>> | |||||
>>> np.random.seed(5) | >>> np.random.seed(5) | ||||
>>> ori = np.random.rand(4, 4, 4).astype(np.float32) | >>> ori = np.random.rand(4, 4, 4).astype(np.float32) | ||||
>>> np.random.seed(6) | >>> np.random.seed(6) | ||||
>>> adv = np.random.rand(4, 4, 4).astype(np.float32) | >>> adv = np.random.rand(4, 4, 4).astype(np.float32) | ||||
>>> encoder = Model(Net()) | |||||
>>> model = Model(PredNet()) | >>> model = Model(PredNet()) | ||||
>>> detector = DivergenceBasedDetector(encoder, model) | >>> detector = DivergenceBasedDetector(encoder, model) | ||||
>>> threshold = detector.fit(ori) | >>> threshold = detector.fit(ori) | ||||
>>> detector.set_threshold(threshold) | >>> detector.set_threshold(threshold) | ||||
>>> detected_res = detector.detect(adv) | |||||
>>> adv_ids = detector.detect(adv) | |||||
>>> adv_trans = detector.transform(adv) | >>> adv_trans = detector.transform(adv) | ||||
""" | """ | ||||
@@ -52,8 +52,30 @@ class RegionBasedDetector(Detector): | |||||
input labels are one-hot-encoded. Default: False. | input labels are one-hot-encoded. Default: False. | ||||
Examples: | Examples: | ||||
>>> import numpy as np | |||||
>>> from mindspore.ops.operations import Add | |||||
>>> from mindspore.nn import Cell | |||||
>>> from mindspore import Model | |||||
>>> from mindspore import context | |||||
>>> from mindarmour.adv_robustness.detectors import ErrorBasedDetector | |||||
>>> class Net(Cell): | |||||
>>> def __init__(self): | |||||
>>> super(Net, self).__init__() | |||||
>>> self.add = Add() | |||||
>>> | |||||
>>> def construct(self, inputs): | |||||
>>> return self.add(inputs, inputs) | |||||
>>> | |||||
>>> np.random.seed(5) | |||||
>>> ori = np.random.rand(4, 4).astype(np.float32) | |||||
>>> labels = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 1, 0], | |||||
[0, 1, 0, 0]]).astype(np.int32) | |||||
>>> np.random.seed(6) | |||||
>>> adv = np.random.rand(4, 4).astype(np.float32) | |||||
>>> model = Model(Net()) | |||||
>>> detector = RegionBasedDetector(model) | >>> detector = RegionBasedDetector(model) | ||||
>>> detector.fit(ori, labels) | |||||
>>> radius = detector.fit(ori, labels) | |||||
>>> detector.set_radius(radius) | |||||
>>> adv_ids = detector.detect(adv) | >>> adv_ids = detector.detect(adv) | ||||
""" | """ | ||||
@@ -49,9 +49,30 @@ class SpatialSmoothing(Detector): | |||||
benign samples. Default: 0.05. | benign samples. Default: 0.05. | ||||
Examples: | Examples: | ||||
>>> import numpy as np | |||||
>>> from mindspore.ops.operations as P | |||||
>>> from mindspore.nn import Cell | |||||
>>> from mindspore import Model | |||||
>>> from mindspore import context | |||||
>>> from mindarmour.adv_robustness.detectors import SpatialSmoothing | |||||
>>> class Net(Cell): | |||||
>>> def __init__(self): | |||||
>>> super(Net, self).__init__() | |||||
>>> self._softmax = P.Softmax() | |||||
>>> | |||||
>>> def construct(self, inputs): | |||||
>>> return self._softmax(inputs) | |||||
>>> | |||||
>>> input_shape = (50, 3) | |||||
>>> np.random.seed(1) | |||||
>>> input_np = np.random.randn(*input_shape).astype(np.float32) | |||||
>>> np.random.seed(2) | |||||
>>> adv_np = np.random.randn(*input_shape).astype(np.float32) | |||||
>>> model = Model(Net()) | |||||
>>> detector = SpatialSmoothing(model) | >>> detector = SpatialSmoothing(model) | ||||
>>> detector.fit(ori, labels) | |||||
>>> adv_ids = detector.detect(adv) | |||||
>>> threshold = detector.fit(input_np) | |||||
>>> detector.set_threshold(threshold.item()) | |||||
>>> detected_res = np.array(detector.detect(adv_np)) | |||||
""" | """ | ||||
def __init__(self, model, ksize=3, is_local_smooth=True, | def __init__(self, model, ksize=3, is_local_smooth=True, | ||||