# Copyright 2019 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Spatial-Smoothing detector.
"""
import numpy as np
from scipy import ndimage
from mindspore import Model
from mindspore import Tensor
from mindarmour.utils.logger import LogUtil
from mindarmour.utils._check_param import check_model, check_numpy_param, \
check_pair_numpy_param, check_int_positive, check_param_type, \
check_param_in_range, check_equal_shape, check_value_positive
from .detector import Detector
LOGGER = LogUtil.get_instance()
TAG = 'SpatialSmoothing'
def _median_filter_np(inputs, size=2):
"""median filter using numpy"""
return ndimage.filters.median_filter(inputs, size=size, mode='reflect')
[文档]class SpatialSmoothing(Detector):
"""
Detect method based on spatial smoothing.
Using Gaussian filtering, median filtering, and mean filtering, to blur
the original image. When the model has a large threshold difference
between the predicted values before and after the sample is blurred,
it is judged as an adversarial example.
Args:
model (Model): Target model.
ksize (int): Smooth window size. Default: ``3``.
is_local_smooth (bool): If ``True``, trigger local smooth. If ``False``, none
local smooth. Default: ``True``.
metric (str): Distance method. Default: ``'l1'``.
false_positive_ratio (float): False positive rate over
benign samples. Default: ``0.05``.
Examples:
>>> import mindspore.ops.operations as P
>>> from mindspore import Model
>>> from mindarmour.adv_robustness.detectors import SpatialSmoothing
>>> class Net(nn.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)
>>> 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,
metric='l1', false_positive_ratio=0.05):
super(SpatialSmoothing, self).__init__()
self._ksize = check_int_positive('ksize', ksize)
self._is_local_smooth = check_param_type('is_local_smooth',
is_local_smooth,
bool)
self._model = check_model('model', model, Model)
self._metric = metric
self._fpr = check_param_in_range('false_positive_ratio',
false_positive_ratio,
0, 1)
self._threshold = None
[文档] def fit(self, inputs, labels=None):
"""
Train detector to decide the threshold. The proper threshold make
sure the actual false positive rate over benign sample is less than
the given value.
Args:
inputs (numpy.ndarray): Benign samples.
labels (numpy.ndarray): Default ``None``.
Returns:
float, threshold, distance larger than which is reported
as positive, i.e. adversarial.
"""
inputs = check_numpy_param('inputs', inputs)
raw_pred = self._model.predict(Tensor(inputs)).asnumpy()
smoothing_pred = self._model.predict(Tensor(self.transform(inputs))).asnumpy()
dist = self._dist(raw_pred, smoothing_pred)
index = int(len(dist)*(1 - self._fpr))
threshold = np.sort(dist, axis=None)[index]
self._threshold = threshold
return self._threshold
[文档] def detect(self, inputs):
"""
Detect if an input sample is an adversarial example.
Args:
inputs (numpy.ndarray): Suspicious samples to be judged.
Returns:
list[int], whether a sample is adversarial. if res[i]=1, then the
input sample with index i is adversarial.
"""
inputs = check_numpy_param('inputs', inputs)
raw_pred = self._model.predict(Tensor(inputs)).asnumpy()
smoothing_pred = self._model.predict(Tensor(self.transform(inputs))).asnumpy()
dist = self._dist(raw_pred, smoothing_pred)
res = [0]*len(dist)
for i, elem in enumerate(dist):
if elem > self._threshold:
res[i] = 1
return res
[文档] def detect_diff(self, inputs):
"""
Return the raw distance value (before apply the threshold) between
the input sample and its smoothed counterpart.
Args:
inputs (numpy.ndarray): Suspicious samples to be judged.
Returns:
float, distance.
"""
inputs = check_numpy_param('inputs', inputs)
raw_pred = self._model.predict(Tensor(inputs)).asnumpy()
smoothing_pred = self._model.predict(Tensor(self.transform(inputs))).asnumpy()
dist = self._dist(raw_pred, smoothing_pred)
return dist
def transform(self, inputs):
inputs = check_numpy_param('inputs', inputs)
return _median_filter_np(inputs, self._ksize)
[文档] def set_threshold(self, threshold):
"""
Set the parameters threshold.
Args:
threshold (float): Detection threshold.
"""
self._threshold = check_value_positive('threshold', threshold)
def _dist(self, before, after):
"""
Calculate the distance between the model outputs of a raw sample and
its smoothed counterpart.
Args:
before (numpy.ndarray): Model output of raw samples.
after (numpy.ndarray): Model output of smoothed counterparts.
Returns:
float, distance based on specified norm.
"""
before, after = check_pair_numpy_param('before', before, 'after', after)
before, after = check_equal_shape('before', before, 'after', after)
res = []
diff = after - before
for _, elem in enumerate(diff):
if self._metric == 'l1':
res.append(np.linalg.norm(elem, ord=1))
elif self._metric == 'l2':
res.append(np.linalg.norm(elem, ord=2))
else:
res.append(np.linalg.norm(elem, ord=1))
return res