@@ -0,0 +1,25 @@ | |||||
<!-- Thanks for sending a pull request! Here are some tips for you: | |||||
If this is your first time, please read our contributor guidelines: https://gitee.com/mindspore/mindspore/blob/master/CONTRIBUTING.md | |||||
--> | |||||
**What type of PR is this?** | |||||
> Uncomment only one ` /kind <>` line, hit enter to put that in a new line, and remove leading whitespaces from that line: | |||||
> | |||||
> /kind bug | |||||
> /kind task | |||||
> /kind feature | |||||
**What this PR does / why we need it**: | |||||
**Which issue(s) this PR fixes**: | |||||
<!-- | |||||
*Automatically closes linked issue when PR is merged. | |||||
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`. | |||||
--> | |||||
Fixes # | |||||
**Special notes for your reviewer**: | |||||
@@ -0,0 +1,28 @@ | |||||
*.dot | |||||
*.ir | |||||
*.dat | |||||
*.pyc | |||||
*.csv | |||||
*.gz | |||||
*.tar | |||||
*.zip | |||||
*.rar | |||||
*.ipynb | |||||
.idea/ | |||||
build/ | |||||
dist/ | |||||
local_script/ | |||||
example/dataset/ | |||||
example/mnist_demo/MNIST_unzip/ | |||||
example/mnist_demo/trained_ckpt_file/ | |||||
example/mnist_demo/model/ | |||||
example/cifar_demo/model/ | |||||
example/dog_cat_demo/model/ | |||||
mindarmour.egg-info/ | |||||
*model/ | |||||
*MNIST/ | |||||
*out.data/ | |||||
*defensed_model/ | |||||
*pre_trained_model/ | |||||
*__pycache__/ | |||||
*kernel_meta |
@@ -0,0 +1,201 @@ | |||||
Apache License | |||||
Version 2.0, January 2004 | |||||
http://www.apache.org/licenses/ | |||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||||
1. Definitions. | |||||
"License" shall mean the terms and conditions for use, reproduction, | |||||
and distribution as defined by Sections 1 through 9 of this document. | |||||
"Licensor" shall mean the copyright owner or entity authorized by | |||||
the copyright owner that is granting the License. | |||||
"Legal Entity" shall mean the union of the acting entity and all | |||||
other entities that control, are controlled by, or are under common | |||||
control with that entity. For the purposes of this definition, | |||||
"control" means (i) the power, direct or indirect, to cause the | |||||
direction or management of such entity, whether by contract or | |||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||||
outstanding shares, or (iii) beneficial ownership of such entity. | |||||
"You" (or "Your") shall mean an individual or Legal Entity | |||||
exercising permissions granted by this License. | |||||
"Source" form shall mean the preferred form for making modifications, | |||||
including but not limited to software source code, documentation | |||||
source, and configuration files. | |||||
"Object" form shall mean any form resulting from mechanical | |||||
transformation or translation of a Source form, including but | |||||
not limited to compiled object code, generated documentation, | |||||
and conversions to other media types. | |||||
"Work" shall mean the work of authorship, whether in Source or | |||||
Object form, made available under the License, as indicated by a | |||||
copyright notice that is included in or attached to the work | |||||
(an example is provided in the Appendix below). | |||||
"Derivative Works" shall mean any work, whether in Source or Object | |||||
form, that is based on (or derived from) the Work and for which the | |||||
editorial revisions, annotations, elaborations, or other modifications | |||||
represent, as a whole, an original work of authorship. For the purposes | |||||
of this License, Derivative Works shall not include works that remain | |||||
separable from, or merely link (or bind by name) to the interfaces of, | |||||
the Work and Derivative Works thereof. | |||||
"Contribution" shall mean any work of authorship, including | |||||
the original version of the Work and any modifications or additions | |||||
to that Work or Derivative Works thereof, that is intentionally | |||||
submitted to Licensor for inclusion in the Work by the copyright owner | |||||
or by an individual or Legal Entity authorized to submit on behalf of | |||||
the copyright owner. For the purposes of this definition, "submitted" | |||||
means any form of electronic, verbal, or written communication sent | |||||
to the Licensor or its representatives, including but not limited to | |||||
communication on electronic mailing lists, source code control systems, | |||||
and issue tracking systems that are managed by, or on behalf of, the | |||||
Licensor for the purpose of discussing and improving the Work, but | |||||
excluding communication that is conspicuously marked or otherwise | |||||
designated in writing by the copyright owner as "Not a Contribution." | |||||
"Contributor" shall mean Licensor and any individual or Legal Entity | |||||
on behalf of whom a Contribution has been received by Licensor and | |||||
subsequently incorporated within the Work. | |||||
2. Grant of Copyright License. Subject to the terms and conditions of | |||||
this License, each Contributor hereby grants to You a perpetual, | |||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||||
copyright license to reproduce, prepare Derivative Works of, | |||||
publicly display, publicly perform, sublicense, and distribute the | |||||
Work and such Derivative Works in Source or Object form. | |||||
3. Grant of Patent License. Subject to the terms and conditions of | |||||
this License, each Contributor hereby grants to You a perpetual, | |||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||||
(except as stated in this section) patent license to make, have made, | |||||
use, offer to sell, sell, import, and otherwise transfer the Work, | |||||
where such license applies only to those patent claims licensable | |||||
by such Contributor that are necessarily infringed by their | |||||
Contribution(s) alone or by combination of their Contribution(s) | |||||
with the Work to which such Contribution(s) was submitted. If You | |||||
institute patent litigation against any entity (including a | |||||
cross-claim or counterclaim in a lawsuit) alleging that the Work | |||||
or a Contribution incorporated within the Work constitutes direct | |||||
or contributory patent infringement, then any patent licenses | |||||
granted to You under this License for that Work shall terminate | |||||
as of the date such litigation is filed. | |||||
4. Redistribution. You may reproduce and distribute copies of the | |||||
Work or Derivative Works thereof in any medium, with or without | |||||
modifications, and in Source or Object form, provided that You | |||||
meet the following conditions: | |||||
(a) You must give any other recipients of the Work or | |||||
Derivative Works a copy of this License; and | |||||
(b) You must cause any modified files to carry prominent notices | |||||
stating that You changed the files; and | |||||
(c) You must retain, in the Source form of any Derivative Works | |||||
that You distribute, all copyright, patent, trademark, and | |||||
attribution notices from the Source form of the Work, | |||||
excluding those notices that do not pertain to any part of | |||||
the Derivative Works; and | |||||
(d) If the Work includes a "NOTICE" text file as part of its | |||||
distribution, then any Derivative Works that You distribute must | |||||
include a readable copy of the attribution notices contained | |||||
within such NOTICE file, excluding those notices that do not | |||||
pertain to any part of the Derivative Works, in at least one | |||||
of the following places: within a NOTICE text file distributed | |||||
as part of the Derivative Works; within the Source form or | |||||
documentation, if provided along with the Derivative Works; or, | |||||
within a display generated by the Derivative Works, if and | |||||
wherever such third-party notices normally appear. The contents | |||||
of the NOTICE file are for informational purposes only and | |||||
do not modify the License. You may add Your own attribution | |||||
notices within Derivative Works that You distribute, alongside | |||||
or as an addendum to the NOTICE text from the Work, provided | |||||
that such additional attribution notices cannot be construed | |||||
as modifying the License. | |||||
You may add Your own copyright statement to Your modifications and | |||||
may provide additional or different license terms and conditions | |||||
for use, reproduction, or distribution of Your modifications, or | |||||
for any such Derivative Works as a whole, provided Your use, | |||||
reproduction, and distribution of the Work otherwise complies with | |||||
the conditions stated in this License. | |||||
5. Submission of Contributions. Unless You explicitly state otherwise, | |||||
any Contribution intentionally submitted for inclusion in the Work | |||||
by You to the Licensor shall be under the terms and conditions of | |||||
this License, without any additional terms or conditions. | |||||
Notwithstanding the above, nothing herein shall supersede or modify | |||||
the terms of any separate license agreement you may have executed | |||||
with Licensor regarding such Contributions. | |||||
6. Trademarks. This License does not grant permission to use the trade | |||||
names, trademarks, service marks, or product names of the Licensor, | |||||
except as required for reasonable and customary use in describing the | |||||
origin of the Work and reproducing the content of the NOTICE file. | |||||
7. Disclaimer of Warranty. Unless required by applicable law or | |||||
agreed to in writing, Licensor provides the Work (and each | |||||
Contributor provides its Contributions) on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||||
implied, including, without limitation, any warranties or conditions | |||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||||
PARTICULAR PURPOSE. You are solely responsible for determining the | |||||
appropriateness of using or redistributing the Work and assume any | |||||
risks associated with Your exercise of permissions under this License. | |||||
8. Limitation of Liability. In no event and under no legal theory, | |||||
whether in tort (including negligence), contract, or otherwise, | |||||
unless required by applicable law (such as deliberate and grossly | |||||
negligent acts) or agreed to in writing, shall any Contributor be | |||||
liable to You for damages, including any direct, indirect, special, | |||||
incidental, or consequential damages of any character arising as a | |||||
result of this License or out of the use or inability to use the | |||||
Work (including but not limited to damages for loss of goodwill, | |||||
work stoppage, computer failure or malfunction, or any and all | |||||
other commercial damages or losses), even if such Contributor | |||||
has been advised of the possibility of such damages. | |||||
9. Accepting Warranty or Additional Liability. While redistributing | |||||
the Work or Derivative Works thereof, You may choose to offer, | |||||
and charge a fee for, acceptance of support, warranty, indemnity, | |||||
or other liability obligations and/or rights consistent with this | |||||
License. However, in accepting such obligations, You may act only | |||||
on Your own behalf and on Your sole responsibility, not on behalf | |||||
of any other Contributor, and only if You agree to indemnify, | |||||
defend, and hold each Contributor harmless for any liability | |||||
incurred by, or claims asserted against, such Contributor by reason | |||||
of your accepting any such warranty or additional liability. | |||||
END OF TERMS AND CONDITIONS | |||||
APPENDIX: How to apply the Apache License to your work. | |||||
To apply the Apache License to your work, attach the following | |||||
boilerplate notice, with the fields enclosed by brackets "[]" | |||||
replaced with your own identifying information. (Don't include | |||||
the brackets!) The text should be enclosed in the appropriate | |||||
comment syntax for the file format. We also recommend that a | |||||
file or class name and description of purpose be included on the | |||||
same "printed page" as the copyright notice for easier | |||||
identification within third-party archives. | |||||
Copyright [yyyy] [name of copyright owner] | |||||
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. |
@@ -0,0 +1,2 @@ | |||||
MindSpore MindArmour | |||||
Copyright 2019-2020 Huawei Technologies Co., Ltd |
@@ -0,0 +1,74 @@ | |||||
# MindArmour | |||||
- [What is MindArmour](#what-is-mindarmour) | |||||
- [Setting up](#setting-up-mindarmour) | |||||
- [Docs](#docs) | |||||
- [Community](#community) | |||||
- [Contributing](#contributing) | |||||
- [Release Notes](#release-notes) | |||||
- [License](#license) | |||||
## What is MindArmour | |||||
A tool box for MindSpore users to enhance model security and trustworthiness. | |||||
MindArmour is designed for adversarial examples, including four submodule: adversarial examples generation, adversarial example detection, model defense and evaluation. The architecture is shown as follow: | |||||
 | |||||
## Setting up MindArmour | |||||
### Dependencies | |||||
This library uses MindSpore to accelerate graph computations performed by many machine learning models. Therefore, installing MindSpore is a pre-requisite. All other dependencies are included in `setup.py`. | |||||
### Installation | |||||
#### Installation for development | |||||
1. Download source code from Gitee. | |||||
```bash | |||||
git clone https://gitee.com/mindspore/mindarmour.git | |||||
``` | |||||
2. Compile and install in MindArmour directory. | |||||
```bash | |||||
$ cd mindarmour | |||||
$ python setup.py install | |||||
``` | |||||
#### `Pip` installation | |||||
1. Download whl package from [MindSpore website](https://www.mindspore.cn/versions/en), then run the following command: | |||||
``` | |||||
pip install mindarmour-{version}-cp37-cp37m-linux_{arch}.whl | |||||
``` | |||||
2. Successfully installed, if there is no error message such as `No module named 'mindarmour'` when execute the following command: | |||||
```bash | |||||
python -c 'import mindarmour' | |||||
``` | |||||
## Docs | |||||
Guidance on installation, tutorials, API, see our [User Documentation](https://gitee.com/mindspore/docs). | |||||
## Community | |||||
- [MindSpore Slack](https://join.slack.com/t/mindspore/shared_invite/enQtOTcwMTIxMDI3NjM0LTNkMWM2MzI5NjIyZWU5ZWQ5M2EwMTQ5MWNiYzMxOGM4OWFhZjI4M2E5OGI2YTg3ODU1ODE2Njg1MThiNWI3YmQ) - Ask questions and find answers. | |||||
## Contributing | |||||
Welcome contributions. See our [Contributor Wiki](https://gitee.com/mindspore/mindspore/blob/master/CONTRIBUTING.md) for more details. | |||||
## Release Notes | |||||
The release notes, see our [RELEASE](RELEASE.md). | |||||
## License | |||||
[Apache License 2.0](LICENSE) |
@@ -0,0 +1,11 @@ | |||||
# Release 0.1.0-alpha | |||||
Initial release of MindArmour. | |||||
## Major Features | |||||
- Support adversarial attack and defense on the platform of MindSpore. | |||||
- Include 13 white-box and 7 black-box attack methods. | |||||
- Provide 5 detection algorithms to detect attacking in multiple way. | |||||
- Provide adversarial training to enhance model security. | |||||
- Provide 6 evaluation metrics for attack methods and 9 evaluation metrics for defense methods. |
@@ -0,0 +1,3 @@ | |||||
# MindArmour Documentation | |||||
The MindArmour documentation is in the [MindSpore Docs](https://gitee.com/mindspore/docs) repository. |
@@ -0,0 +1,62 @@ | |||||
# 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. | |||||
import mindspore.dataset as ds | |||||
import mindspore.dataset.transforms.vision.c_transforms as CV | |||||
import mindspore.dataset.transforms.c_transforms as C | |||||
from mindspore.dataset.transforms.vision import Inter | |||||
import mindspore.common.dtype as mstype | |||||
def generate_mnist_dataset(data_path, batch_size=32, repeat_size=1, | |||||
num_parallel_workers=1, sparse=True): | |||||
""" | |||||
create dataset for training or testing | |||||
""" | |||||
# define dataset | |||||
ds1 = ds.MnistDataset(data_path) | |||||
# define operation parameters | |||||
resize_height, resize_width = 32, 32 | |||||
rescale = 1.0 / 255.0 | |||||
shift = 0.0 | |||||
# define map operations | |||||
resize_op = CV.Resize((resize_height, resize_width), | |||||
interpolation=Inter.LINEAR) | |||||
rescale_op = CV.Rescale(rescale, shift) | |||||
hwc2chw_op = CV.HWC2CHW() | |||||
type_cast_op = C.TypeCast(mstype.int32) | |||||
one_hot_enco = C.OneHot(10) | |||||
# apply map operations on images | |||||
if not sparse: | |||||
ds1 = ds1.map(input_columns="label", operations=one_hot_enco, | |||||
num_parallel_workers=num_parallel_workers) | |||||
type_cast_op = C.TypeCast(mstype.float32) | |||||
ds1 = ds1.map(input_columns="label", operations=type_cast_op, | |||||
num_parallel_workers=num_parallel_workers) | |||||
ds1 = ds1.map(input_columns="image", operations=resize_op, | |||||
num_parallel_workers=num_parallel_workers) | |||||
ds1 = ds1.map(input_columns="image", operations=rescale_op, | |||||
num_parallel_workers=num_parallel_workers) | |||||
ds1 = ds1.map(input_columns="image", operations=hwc2chw_op, | |||||
num_parallel_workers=num_parallel_workers) | |||||
# apply DatasetOps | |||||
buffer_size = 10000 | |||||
ds1 = ds1.shuffle(buffer_size=buffer_size) | |||||
ds1 = ds1.batch(batch_size, drop_remainder=True) | |||||
ds1 = ds1.repeat(repeat_size) | |||||
return ds1 |
@@ -0,0 +1,46 @@ | |||||
# mnist demo | |||||
## Introduction | |||||
The MNIST database of handwritten digits, available from this page, has a training set of 60,000 examples, and a test set of 10,000 examples. It is a subset of a larger set available from MNIST. The digits have been size-normalized and centered in a fixed-size image. | |||||
## run demo | |||||
### 1. download dataset | |||||
```sh | |||||
$ cd example/mnist_demo | |||||
$ mkdir MNIST_unzip | |||||
$ cd MNIST_unzip | |||||
$ mkdir train | |||||
$ mkdir test | |||||
$ cd train | |||||
$ wget "http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz" | |||||
$ wget "http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz" | |||||
$ gzip train-images-idx3-ubyte.gz -d | |||||
$ gzip train-labels-idx1-ubyte.gz -d | |||||
$ cd ../test | |||||
$ wget "http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz" | |||||
$ wget "http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz" | |||||
$ gzip t10k-images-idx3-ubyte.gz -d | |||||
$ gzip t10k-images-idx3-ubyte.gz -d | |||||
$ cd ../../ | |||||
``` | |||||
### 1. trian model | |||||
```sh | |||||
$ python mnist_train.py | |||||
``` | |||||
### 2. run attack test | |||||
```sh | |||||
$ mkdir out.data | |||||
$ python mnist_attack_jsma.py | |||||
``` | |||||
### 3. run defense/detector test | |||||
```sh | |||||
$ python mnist_defense_nad.py | |||||
$ python mnist_similarity_detector.py | |||||
``` |
@@ -0,0 +1,64 @@ | |||||
# 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. | |||||
import mindspore.nn as nn | |||||
import mindspore.ops.operations as P | |||||
from mindspore.common.initializer import TruncatedNormal | |||||
def conv(in_channels, out_channels, kernel_size, stride=1, padding=0): | |||||
weight = weight_variable() | |||||
return nn.Conv2d(in_channels, out_channels, | |||||
kernel_size=kernel_size, stride=stride, padding=padding, | |||||
weight_init=weight, has_bias=False, pad_mode="valid") | |||||
def fc_with_initialize(input_channels, out_channels): | |||||
weight = weight_variable() | |||||
bias = weight_variable() | |||||
return nn.Dense(input_channels, out_channels, weight, bias) | |||||
def weight_variable(): | |||||
return TruncatedNormal(0.2) | |||||
class LeNet5(nn.Cell): | |||||
""" | |||||
Lenet network | |||||
""" | |||||
def __init__(self): | |||||
super(LeNet5, self).__init__() | |||||
self.conv1 = conv(1, 6, 5) | |||||
self.conv2 = conv(6, 16, 5) | |||||
self.fc1 = fc_with_initialize(16*5*5, 120) | |||||
self.fc2 = fc_with_initialize(120, 84) | |||||
self.fc3 = fc_with_initialize(84, 10) | |||||
self.relu = nn.ReLU() | |||||
self.max_pool2d = nn.MaxPool2d(kernel_size=2, stride=2) | |||||
self.reshape = P.Reshape() | |||||
def construct(self, x): | |||||
x = self.conv1(x) | |||||
x = self.relu(x) | |||||
x = self.max_pool2d(x) | |||||
x = self.conv2(x) | |||||
x = self.relu(x) | |||||
x = self.max_pool2d(x) | |||||
x = self.reshape(x, (-1, 16*5*5)) | |||||
x = self.fc1(x) | |||||
x = self.relu(x) | |||||
x = self.fc2(x) | |||||
x = self.relu(x) | |||||
x = self.fc3(x) | |||||
return x |
@@ -0,0 +1,118 @@ | |||||
# 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. | |||||
import sys | |||||
import time | |||||
import numpy as np | |||||
import pytest | |||||
from scipy.special import softmax | |||||
from mindspore import Model | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks.carlini_wagner import CarliniWagnerL2Attack | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.evaluations.attack_evaluation import AttackEvaluate | |||||
from lenet5_net import LeNet5 | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
sys.path.append("..") | |||||
from data_processing import generate_mnist_dataset | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'CW_Test' | |||||
@pytest.mark.level1 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_carlini_wagner_attack(): | |||||
""" | |||||
CW-Attack test | |||||
""" | |||||
# upload trained network | |||||
ckpt_name = './trained_ckpt_file/checkpoint_lenet-10_1875.ckpt' | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
# get test data | |||||
data_list = "./MNIST_unzip/test" | |||||
batch_size = 32 | |||||
ds = generate_mnist_dataset(data_list, batch_size=batch_size) | |||||
# prediction accuracy before attack | |||||
model = Model(net) | |||||
batch_num = 3 # the number of batches of attacking samples | |||||
test_images = [] | |||||
test_labels = [] | |||||
predict_labels = [] | |||||
i = 0 | |||||
for data in ds.create_tuple_iterator(): | |||||
i += 1 | |||||
images = data[0].astype(np.float32) | |||||
labels = data[1] | |||||
test_images.append(images) | |||||
test_labels.append(labels) | |||||
pred_labels = np.argmax(model.predict(Tensor(images)).asnumpy(), | |||||
axis=1) | |||||
predict_labels.append(pred_labels) | |||||
if i >= batch_num: | |||||
break | |||||
predict_labels = np.concatenate(predict_labels) | |||||
true_labels = np.concatenate(test_labels) | |||||
accuracy = np.mean(np.equal(predict_labels, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy before attacking is : %s", accuracy) | |||||
# attacking | |||||
num_classes = 10 | |||||
attack = CarliniWagnerL2Attack(net, num_classes, targeted=False) | |||||
start_time = time.clock() | |||||
adv_data = attack.batch_generate(np.concatenate(test_images), | |||||
np.concatenate(test_labels), batch_size=32) | |||||
stop_time = time.clock() | |||||
pred_logits_adv = model.predict(Tensor(adv_data)).asnumpy() | |||||
# rescale predict confidences into (0, 1). | |||||
pred_logits_adv = softmax(pred_logits_adv, axis=1) | |||||
pred_labels_adv = np.argmax(pred_logits_adv, axis=1) | |||||
accuracy_adv = np.mean(np.equal(pred_labels_adv, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy after attacking is : %s", | |||||
accuracy_adv) | |||||
test_labels = np.eye(10)[np.concatenate(test_labels)] | |||||
attack_evaluate = AttackEvaluate(np.concatenate(test_images).transpose(0, 2, 3, 1), | |||||
test_labels, adv_data.transpose(0, 2, 3, 1), | |||||
pred_logits_adv) | |||||
LOGGER.info(TAG, 'mis-classification rate of adversaries is : %s', | |||||
attack_evaluate.mis_classification_rate()) | |||||
LOGGER.info(TAG, 'The average confidence of adversarial class is : %s', | |||||
attack_evaluate.avg_conf_adv_class()) | |||||
LOGGER.info(TAG, 'The average confidence of true class is : %s', | |||||
attack_evaluate.avg_conf_true_class()) | |||||
LOGGER.info(TAG, 'The average distance (l0, l2, linf) between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_lp_distance()) | |||||
LOGGER.info(TAG, 'The average structural similarity between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_ssim()) | |||||
LOGGER.info(TAG, 'The average costing time is %s', | |||||
(stop_time - start_time)/(batch_num*batch_size)) | |||||
if __name__ == '__main__': | |||||
test_carlini_wagner_attack() |
@@ -0,0 +1,120 @@ | |||||
# 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. | |||||
import sys | |||||
import time | |||||
import numpy as np | |||||
import pytest | |||||
from scipy.special import softmax | |||||
from mindspore import Model | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks.deep_fool import DeepFool | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.evaluations.attack_evaluation import AttackEvaluate | |||||
from lenet5_net import LeNet5 | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
sys.path.append("..") | |||||
from data_processing import generate_mnist_dataset | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'DeepFool_Test' | |||||
@pytest.mark.level1 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_deepfool_attack(): | |||||
""" | |||||
DeepFool-Attack test | |||||
""" | |||||
# upload trained network | |||||
ckpt_name = './trained_ckpt_file/checkpoint_lenet-10_1875.ckpt' | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
# get test data | |||||
data_list = "./MNIST_unzip/test" | |||||
batch_size = 32 | |||||
ds = generate_mnist_dataset(data_list, batch_size=batch_size) | |||||
# prediction accuracy before attack | |||||
model = Model(net) | |||||
batch_num = 3 # the number of batches of attacking samples | |||||
test_images = [] | |||||
test_labels = [] | |||||
predict_labels = [] | |||||
i = 0 | |||||
for data in ds.create_tuple_iterator(): | |||||
i += 1 | |||||
images = data[0].astype(np.float32) | |||||
labels = data[1] | |||||
test_images.append(images) | |||||
test_labels.append(labels) | |||||
pred_labels = np.argmax(model.predict(Tensor(images)).asnumpy(), | |||||
axis=1) | |||||
predict_labels.append(pred_labels) | |||||
if i >= batch_num: | |||||
break | |||||
predict_labels = np.concatenate(predict_labels) | |||||
true_labels = np.concatenate(test_labels) | |||||
accuracy = np.mean(np.equal(predict_labels, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy before attacking is : %s", accuracy) | |||||
# attacking | |||||
classes = 10 | |||||
attack = DeepFool(net, classes, norm_level=2, | |||||
bounds=(0.0, 1.0)) | |||||
start_time = time.clock() | |||||
adv_data = attack.batch_generate(np.concatenate(test_images), | |||||
np.concatenate(test_labels), batch_size=32) | |||||
stop_time = time.clock() | |||||
pred_logits_adv = model.predict(Tensor(adv_data)).asnumpy() | |||||
# rescale predict confidences into (0, 1). | |||||
pred_logits_adv = softmax(pred_logits_adv, axis=1) | |||||
pred_labels_adv = np.argmax(pred_logits_adv, axis=1) | |||||
accuracy_adv = np.mean(np.equal(pred_labels_adv, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy after attacking is : %s", | |||||
accuracy_adv) | |||||
test_labels = np.eye(10)[np.concatenate(test_labels)] | |||||
attack_evaluate = AttackEvaluate(np.concatenate(test_images).transpose(0, 2, 3, 1), | |||||
test_labels, adv_data.transpose(0, 2, 3, 1), | |||||
pred_logits_adv) | |||||
LOGGER.info(TAG, 'mis-classification rate of adversaries is : %s', | |||||
attack_evaluate.mis_classification_rate()) | |||||
LOGGER.info(TAG, 'The average confidence of adversarial class is : %s', | |||||
attack_evaluate.avg_conf_adv_class()) | |||||
LOGGER.info(TAG, 'The average confidence of true class is : %s', | |||||
attack_evaluate.avg_conf_true_class()) | |||||
LOGGER.info(TAG, 'The average distance (l0, l2, linf) between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_lp_distance()) | |||||
LOGGER.info(TAG, 'The average structural similarity between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_ssim()) | |||||
LOGGER.info(TAG, 'The average costing time is %s', | |||||
(stop_time - start_time)/(batch_num*batch_size)) | |||||
if __name__ == '__main__': | |||||
test_deepfool_attack() |
@@ -0,0 +1,119 @@ | |||||
# 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. | |||||
import sys | |||||
import time | |||||
import numpy as np | |||||
import pytest | |||||
from scipy.special import softmax | |||||
from mindspore import Model | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks.gradient_method import FastGradientSignMethod | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.evaluations.attack_evaluation import AttackEvaluate | |||||
from lenet5_net import LeNet5 | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
sys.path.append("..") | |||||
from data_processing import generate_mnist_dataset | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'FGSM_Test' | |||||
@pytest.mark.level1 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_fast_gradient_sign_method(): | |||||
""" | |||||
FGSM-Attack test | |||||
""" | |||||
# upload trained network | |||||
ckpt_name = './trained_ckpt_file/checkpoint_lenet-10_1875.ckpt' | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
# get test data | |||||
data_list = "./MNIST_unzip/test" | |||||
batch_size = 32 | |||||
ds = generate_mnist_dataset(data_list, batch_size, sparse=False) | |||||
# prediction accuracy before attack | |||||
model = Model(net) | |||||
batch_num = 3 # the number of batches of attacking samples | |||||
test_images = [] | |||||
test_labels = [] | |||||
predict_labels = [] | |||||
i = 0 | |||||
for data in ds.create_tuple_iterator(): | |||||
i += 1 | |||||
images = data[0].astype(np.float32) | |||||
labels = data[1] | |||||
test_images.append(images) | |||||
test_labels.append(labels) | |||||
pred_labels = np.argmax(model.predict(Tensor(images)).asnumpy(), | |||||
axis=1) | |||||
predict_labels.append(pred_labels) | |||||
if i >= batch_num: | |||||
break | |||||
predict_labels = np.concatenate(predict_labels) | |||||
true_labels = np.argmax(np.concatenate(test_labels), axis=1) | |||||
accuracy = np.mean(np.equal(predict_labels, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy before attacking is : %s", accuracy) | |||||
# attacking | |||||
attack = FastGradientSignMethod(net, eps=0.3) | |||||
start_time = time.clock() | |||||
adv_data = attack.batch_generate(np.concatenate(test_images), | |||||
np.concatenate(test_labels), batch_size=32) | |||||
stop_time = time.clock() | |||||
np.save('./adv_data', adv_data) | |||||
pred_logits_adv = model.predict(Tensor(adv_data)).asnumpy() | |||||
# rescale predict confidences into (0, 1). | |||||
pred_logits_adv = softmax(pred_logits_adv, axis=1) | |||||
pred_labels_adv = np.argmax(pred_logits_adv, axis=1) | |||||
accuracy_adv = np.mean(np.equal(pred_labels_adv, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy after attacking is : %s", accuracy_adv) | |||||
attack_evaluate = AttackEvaluate(np.concatenate(test_images).transpose(0, 2, 3, 1), | |||||
np.concatenate(test_labels), | |||||
adv_data.transpose(0, 2, 3, 1), | |||||
pred_logits_adv) | |||||
LOGGER.info(TAG, 'mis-classification rate of adversaries is : %s', | |||||
attack_evaluate.mis_classification_rate()) | |||||
LOGGER.info(TAG, 'The average confidence of adversarial class is : %s', | |||||
attack_evaluate.avg_conf_adv_class()) | |||||
LOGGER.info(TAG, 'The average confidence of true class is : %s', | |||||
attack_evaluate.avg_conf_true_class()) | |||||
LOGGER.info(TAG, 'The average distance (l0, l2, linf) between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_lp_distance()) | |||||
LOGGER.info(TAG, 'The average structural similarity between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_ssim()) | |||||
LOGGER.info(TAG, 'The average costing time is %s', | |||||
(stop_time - start_time)/(batch_num*batch_size)) | |||||
if __name__ == '__main__': | |||||
test_fast_gradient_sign_method() |
@@ -0,0 +1,138 @@ | |||||
# 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. | |||||
import sys | |||||
import time | |||||
import numpy as np | |||||
import pytest | |||||
from scipy.special import softmax | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks.black.genetic_attack import GeneticAttack | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.evaluations.attack_evaluation import AttackEvaluate | |||||
from lenet5_net import LeNet5 | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
sys.path.append("..") | |||||
from data_processing import generate_mnist_dataset | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'Genetic_Attack' | |||||
class ModelToBeAttacked(BlackModel): | |||||
"""model to be attack""" | |||||
def __init__(self, network): | |||||
super(ModelToBeAttacked, self).__init__() | |||||
self._network = network | |||||
def predict(self, inputs): | |||||
"""predict""" | |||||
result = self._network(Tensor(inputs.astype(np.float32))) | |||||
return result.asnumpy() | |||||
@pytest.mark.level1 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_genetic_attack_on_mnist(): | |||||
""" | |||||
Genetic-Attack test | |||||
""" | |||||
# upload trained network | |||||
ckpt_name = './trained_ckpt_file/checkpoint_lenet-10_1875.ckpt' | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
# get test data | |||||
data_list = "./MNIST_unzip/test" | |||||
batch_size = 32 | |||||
ds = generate_mnist_dataset(data_list, batch_size=batch_size) | |||||
# prediction accuracy before attack | |||||
model = ModelToBeAttacked(net) | |||||
batch_num = 3 # the number of batches of attacking samples | |||||
test_images = [] | |||||
test_labels = [] | |||||
predict_labels = [] | |||||
i = 0 | |||||
for data in ds.create_tuple_iterator(): | |||||
i += 1 | |||||
images = data[0].astype(np.float32) | |||||
labels = data[1] | |||||
test_images.append(images) | |||||
test_labels.append(labels) | |||||
pred_labels = np.argmax(model.predict(images), axis=1) | |||||
predict_labels.append(pred_labels) | |||||
if i >= batch_num: | |||||
break | |||||
predict_labels = np.concatenate(predict_labels) | |||||
true_labels = np.concatenate(test_labels) | |||||
accuracy = np.mean(np.equal(predict_labels, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy before attacking is : %g", accuracy) | |||||
# attacking | |||||
attack = GeneticAttack(model=model, pop_size=6, mutation_rate=0.05, | |||||
per_bounds=0.1, step_size=0.25, temp=0.1, | |||||
sparse=True) | |||||
targeted_labels = np.random.randint(0, 10, size=len(true_labels)) | |||||
for i in range(len(true_labels)): | |||||
if targeted_labels[i] == true_labels[i]: | |||||
targeted_labels[i] = (targeted_labels[i] + 1) % 10 | |||||
start_time = time.clock() | |||||
success_list, adv_data, query_list = attack.generate( | |||||
np.concatenate(test_images), targeted_labels) | |||||
stop_time = time.clock() | |||||
LOGGER.info(TAG, 'success_list: %s', success_list) | |||||
LOGGER.info(TAG, 'average of query times is : %s', np.mean(query_list)) | |||||
pred_logits_adv = model.predict(adv_data) | |||||
# rescale predict confidences into (0, 1). | |||||
pred_logits_adv = softmax(pred_logits_adv, axis=1) | |||||
pred_lables_adv = np.argmax(pred_logits_adv, axis=1) | |||||
accuracy_adv = np.mean(np.equal(pred_lables_adv, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy after attacking is : %g", | |||||
accuracy_adv) | |||||
test_labels_onehot = np.eye(10)[true_labels] | |||||
attack_evaluate = AttackEvaluate(np.concatenate(test_images), | |||||
test_labels_onehot, adv_data, | |||||
pred_logits_adv, targeted=True, | |||||
target_label=targeted_labels) | |||||
LOGGER.info(TAG, 'mis-classification rate of adversaries is : %s', | |||||
attack_evaluate.mis_classification_rate()) | |||||
LOGGER.info(TAG, 'The average confidence of adversarial class is : %s', | |||||
attack_evaluate.avg_conf_adv_class()) | |||||
LOGGER.info(TAG, 'The average confidence of true class is : %s', | |||||
attack_evaluate.avg_conf_true_class()) | |||||
LOGGER.info(TAG, 'The average distance (l0, l2, linf) between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_lp_distance()) | |||||
LOGGER.info(TAG, 'The average structural similarity between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_ssim()) | |||||
LOGGER.info(TAG, 'The average costing time is %s', | |||||
(stop_time - start_time)/(batch_num*batch_size)) | |||||
if __name__ == '__main__': | |||||
test_genetic_attack_on_mnist() |
@@ -0,0 +1,150 @@ | |||||
# 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. | |||||
import sys | |||||
import numpy as np | |||||
import pytest | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks.black.hop_skip_jump_attack import HopSkipJumpAttack | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
from mindarmour.utils.logger import LogUtil | |||||
from lenet5_net import LeNet5 | |||||
sys.path.append("..") | |||||
from data_processing import generate_mnist_dataset | |||||
context.set_context(mode=context.GRAPH_MODE) | |||||
context.set_context(device_target="Ascend") | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'HopSkipJumpAttack' | |||||
class ModelToBeAttacked(BlackModel): | |||||
"""model to be attack""" | |||||
def __init__(self, network): | |||||
super(ModelToBeAttacked, self).__init__() | |||||
self._network = network | |||||
def predict(self, inputs): | |||||
"""predict""" | |||||
if len(inputs.shape) == 3: | |||||
inputs = inputs[np.newaxis, :] | |||||
result = self._network(Tensor(inputs.astype(np.float32))) | |||||
return result.asnumpy() | |||||
def random_target_labels(true_labels): | |||||
target_labels = [] | |||||
for label in true_labels: | |||||
while True: | |||||
target_label = np.random.randint(0, 10) | |||||
if target_label != label: | |||||
target_labels.append(target_label) | |||||
break | |||||
return target_labels | |||||
def create_target_images(dataset, data_labels, target_labels): | |||||
res = [] | |||||
for label in target_labels: | |||||
for i in range(len(data_labels)): | |||||
if data_labels[i] == label: | |||||
res.append(dataset[i]) | |||||
break | |||||
return np.array(res) | |||||
@pytest.mark.level1 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_hsja_mnist_attack(): | |||||
""" | |||||
hsja-Attack test | |||||
""" | |||||
# upload trained network | |||||
ckpt_name = './trained_ckpt_file/checkpoint_lenet-10_1875.ckpt' | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
net.set_train(False) | |||||
# get test data | |||||
data_list = "./MNIST_unzip/test" | |||||
batch_size = 32 | |||||
ds = generate_mnist_dataset(data_list, batch_size=batch_size) | |||||
# prediction accuracy before attack | |||||
model = ModelToBeAttacked(net) | |||||
batch_num = 5 # the number of batches of attacking samples | |||||
test_images = [] | |||||
test_labels = [] | |||||
predict_labels = [] | |||||
i = 0 | |||||
for data in ds.create_tuple_iterator(): | |||||
i += 1 | |||||
images = data[0].astype(np.float32) | |||||
labels = data[1] | |||||
test_images.append(images) | |||||
test_labels.append(labels) | |||||
pred_labels = np.argmax(model.predict(images), axis=1) | |||||
predict_labels.append(pred_labels) | |||||
if i >= batch_num: | |||||
break | |||||
predict_labels = np.concatenate(predict_labels) | |||||
true_labels = np.concatenate(test_labels) | |||||
accuracy = np.mean(np.equal(predict_labels, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy before attacking is : %s", | |||||
accuracy) | |||||
test_images = np.concatenate(test_images) | |||||
# attacking | |||||
norm = 'l2' | |||||
search = 'grid_search' | |||||
target = False | |||||
attack = HopSkipJumpAttack(model, constraint=norm, stepsize_search=search) | |||||
if target: | |||||
target_labels = random_target_labels(true_labels) | |||||
target_images = create_target_images(test_images, predict_labels, | |||||
target_labels) | |||||
attack.set_target_images(target_images) | |||||
success_list, adv_data, query_list = attack.generate(test_images, target_labels) | |||||
else: | |||||
success_list, adv_data, query_list = attack.generate(test_images, None) | |||||
adv_datas = [] | |||||
gts = [] | |||||
for success, adv, gt in zip(success_list, adv_data, true_labels): | |||||
if success: | |||||
adv_datas.append(adv) | |||||
gts.append(gt) | |||||
if len(gts) > 0: | |||||
adv_datas = np.concatenate(np.asarray(adv_datas), axis=0) | |||||
gts = np.asarray(gts) | |||||
pred_logits_adv = model.predict(adv_datas) | |||||
pred_lables_adv = np.argmax(pred_logits_adv, axis=1) | |||||
accuracy_adv = np.mean(np.equal(pred_lables_adv, gts)) | |||||
LOGGER.info(TAG, 'mis-classification rate of adversaries is : %s', | |||||
accuracy_adv) | |||||
if __name__ == '__main__': | |||||
test_hsja_mnist_attack() |
@@ -0,0 +1,124 @@ | |||||
# 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. | |||||
import sys | |||||
import time | |||||
import numpy as np | |||||
import pytest | |||||
from scipy.special import softmax | |||||
from mindspore import Model | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks.jsma import JSMAAttack | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.evaluations.attack_evaluation import AttackEvaluate | |||||
from lenet5_net import LeNet5 | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
sys.path.append("..") | |||||
from data_processing import generate_mnist_dataset | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'JSMA_Test' | |||||
@pytest.mark.level1 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_jsma_attack(): | |||||
""" | |||||
JSMA-Attack test | |||||
""" | |||||
# upload trained network | |||||
ckpt_name = './trained_ckpt_file/checkpoint_lenet-10_1875.ckpt' | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
# get test data | |||||
data_list = "./MNIST_unzip/test" | |||||
batch_size = 32 | |||||
ds = generate_mnist_dataset(data_list, batch_size=batch_size) | |||||
# prediction accuracy before attack | |||||
model = Model(net) | |||||
batch_num = 3 # the number of batches of attacking samples | |||||
test_images = [] | |||||
test_labels = [] | |||||
predict_labels = [] | |||||
i = 0 | |||||
for data in ds.create_tuple_iterator(): | |||||
i += 1 | |||||
images = data[0].astype(np.float32) | |||||
labels = data[1] | |||||
test_images.append(images) | |||||
test_labels.append(labels) | |||||
pred_labels = np.argmax(model.predict(Tensor(images)).asnumpy(), | |||||
axis=1) | |||||
predict_labels.append(pred_labels) | |||||
if i >= batch_num: | |||||
break | |||||
predict_labels = np.concatenate(predict_labels) | |||||
true_labels = np.concatenate(test_labels) | |||||
targeted_labels = np.random.randint(0, 10, size=len(true_labels)) | |||||
for i in range(len(true_labels)): | |||||
if targeted_labels[i] == true_labels[i]: | |||||
targeted_labels[i] = (targeted_labels[i] + 1) % 10 | |||||
accuracy = np.mean(np.equal(predict_labels, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy before attacking is : %g", accuracy) | |||||
# attacking | |||||
classes = 10 | |||||
attack = JSMAAttack(net, classes) | |||||
start_time = time.clock() | |||||
adv_data = attack.batch_generate(np.concatenate(test_images), | |||||
targeted_labels, batch_size=32) | |||||
stop_time = time.clock() | |||||
pred_logits_adv = model.predict(Tensor(adv_data)).asnumpy() | |||||
# rescale predict confidences into (0, 1). | |||||
pred_logits_adv = softmax(pred_logits_adv, axis=1) | |||||
pred_lables_adv = np.argmax(pred_logits_adv, axis=1) | |||||
accuracy_adv = np.mean(np.equal(pred_lables_adv, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy after attacking is : %g", | |||||
accuracy_adv) | |||||
test_labels = np.eye(10)[np.concatenate(test_labels)] | |||||
attack_evaluate = AttackEvaluate( | |||||
np.concatenate(test_images).transpose(0, 2, 3, 1), | |||||
test_labels, adv_data.transpose(0, 2, 3, 1), | |||||
pred_logits_adv, targeted=True, target_label=targeted_labels) | |||||
LOGGER.info(TAG, 'mis-classification rate of adversaries is : %s', | |||||
attack_evaluate.mis_classification_rate()) | |||||
LOGGER.info(TAG, 'The average confidence of adversarial class is : %s', | |||||
attack_evaluate.avg_conf_adv_class()) | |||||
LOGGER.info(TAG, 'The average confidence of true class is : %s', | |||||
attack_evaluate.avg_conf_true_class()) | |||||
LOGGER.info(TAG, 'The average distance (l0, l2, linf) between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_lp_distance()) | |||||
LOGGER.info(TAG, 'The average structural similarity between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_ssim()) | |||||
LOGGER.info(TAG, 'The average costing time is %s', | |||||
(stop_time - start_time) / (batch_num*batch_size)) | |||||
if __name__ == '__main__': | |||||
test_jsma_attack() |
@@ -0,0 +1,132 @@ | |||||
# 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. | |||||
import sys | |||||
import time | |||||
import numpy as np | |||||
import pytest | |||||
from scipy.special import softmax | |||||
from mindspore import Model | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks.lbfgs import LBFGS | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.evaluations.attack_evaluation import AttackEvaluate | |||||
from lenet5_net import LeNet5 | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
sys.path.append("..") | |||||
from data_processing import generate_mnist_dataset | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'LBFGS_Test' | |||||
@pytest.mark.level1 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_lbfgs_attack(): | |||||
""" | |||||
LBFGS-Attack test | |||||
""" | |||||
# upload trained network | |||||
ckpt_name = './trained_ckpt_file/checkpoint_lenet-10_1875.ckpt' | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
# get test data | |||||
data_list = "./MNIST_unzip/test" | |||||
batch_size = 32 | |||||
ds = generate_mnist_dataset(data_list, batch_size=batch_size, sparse=False) | |||||
# prediction accuracy before attack | |||||
model = Model(net) | |||||
batch_num = 3 # the number of batches of attacking samples | |||||
test_images = [] | |||||
test_labels = [] | |||||
predict_labels = [] | |||||
i = 0 | |||||
for data in ds.create_tuple_iterator(): | |||||
i += 1 | |||||
images = data[0].astype(np.float32) | |||||
labels = data[1] | |||||
test_images.append(images) | |||||
test_labels.append(labels) | |||||
pred_labels = np.argmax(model.predict(Tensor(images)).asnumpy(), | |||||
axis=1) | |||||
predict_labels.append(pred_labels) | |||||
if i >= batch_num: | |||||
break | |||||
predict_labels = np.concatenate(predict_labels) | |||||
true_labels = np.argmax(np.concatenate(test_labels), axis=1) | |||||
accuracy = np.mean(np.equal(predict_labels, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy before attacking is : %s", accuracy) | |||||
# attacking | |||||
is_targeted = True | |||||
if is_targeted: | |||||
targeted_labels = np.random.randint(0, 10, size=len(true_labels)).astype(np.int32) | |||||
for i in range(len(true_labels)): | |||||
if targeted_labels[i] == true_labels[i]: | |||||
targeted_labels[i] = (targeted_labels[i] + 1) % 10 | |||||
else: | |||||
targeted_labels = true_labels.astype(np.int32) | |||||
targeted_labels = np.eye(10)[targeted_labels].astype(np.float32) | |||||
attack = LBFGS(net, is_targeted=is_targeted) | |||||
start_time = time.clock() | |||||
adv_data = attack.batch_generate(np.concatenate(test_images), | |||||
targeted_labels, | |||||
batch_size=batch_size) | |||||
stop_time = time.clock() | |||||
pred_logits_adv = model.predict(Tensor(adv_data)).asnumpy() | |||||
# rescale predict confidences into (0, 1). | |||||
pred_logits_adv = softmax(pred_logits_adv, axis=1) | |||||
pred_labels_adv = np.argmax(pred_logits_adv, axis=1) | |||||
accuracy_adv = np.mean(np.equal(pred_labels_adv, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy after attacking is : %s", | |||||
accuracy_adv) | |||||
attack_evaluate = AttackEvaluate(np.concatenate(test_images).transpose(0, 2, 3, 1), | |||||
np.concatenate(test_labels), | |||||
adv_data.transpose(0, 2, 3, 1), | |||||
pred_logits_adv, | |||||
targeted=is_targeted, | |||||
target_label=np.argmax(targeted_labels, | |||||
axis=1)) | |||||
LOGGER.info(TAG, 'mis-classification rate of adversaries is : %s', | |||||
attack_evaluate.mis_classification_rate()) | |||||
LOGGER.info(TAG, 'The average confidence of adversarial class is : %s', | |||||
attack_evaluate.avg_conf_adv_class()) | |||||
LOGGER.info(TAG, 'The average confidence of true class is : %s', | |||||
attack_evaluate.avg_conf_true_class()) | |||||
LOGGER.info(TAG, 'The average distance (l0, l2, linf) between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_lp_distance()) | |||||
LOGGER.info(TAG, 'The average structural similarity between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_ssim()) | |||||
LOGGER.info(TAG, 'The average costing time is %s', | |||||
(stop_time - start_time)/(batch_num*batch_size)) | |||||
if __name__ == '__main__': | |||||
test_lbfgs_attack() |
@@ -0,0 +1,168 @@ | |||||
# 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. | |||||
import sys | |||||
import numpy as np | |||||
import pytest | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks.black.natural_evolutionary_strategy import NES | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
from mindarmour.utils.logger import LogUtil | |||||
from lenet5_net import LeNet5 | |||||
sys.path.append("..") | |||||
from data_processing import generate_mnist_dataset | |||||
context.set_context(mode=context.GRAPH_MODE) | |||||
context.set_context(device_target="Ascend") | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'HopSkipJumpAttack' | |||||
class ModelToBeAttacked(BlackModel): | |||||
"""model to be attack""" | |||||
def __init__(self, network): | |||||
super(ModelToBeAttacked, self).__init__() | |||||
self._network = network | |||||
def predict(self, inputs): | |||||
"""predict""" | |||||
if len(inputs.shape) == 3: | |||||
inputs = inputs[np.newaxis, :] | |||||
result = self._network(Tensor(inputs.astype(np.float32))) | |||||
return result.asnumpy() | |||||
def random_target_labels(true_labels, labels_list): | |||||
target_labels = [] | |||||
for label in true_labels: | |||||
while True: | |||||
target_label = np.random.choice(labels_list) | |||||
if target_label != label: | |||||
target_labels.append(target_label) | |||||
break | |||||
return target_labels | |||||
def _pseudorandom_target(index, total_indices, true_class): | |||||
""" pseudo random_target """ | |||||
rng = np.random.RandomState(index) | |||||
target = true_class | |||||
while target == true_class: | |||||
target = rng.randint(0, total_indices) | |||||
return target | |||||
def create_target_images(dataset, data_labels, target_labels): | |||||
res = [] | |||||
for label in target_labels: | |||||
for i in range(len(data_labels)): | |||||
if data_labels[i] == label: | |||||
res.append(dataset[i]) | |||||
break | |||||
return np.array(res) | |||||
@pytest.mark.level1 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_nes_mnist_attack(): | |||||
""" | |||||
hsja-Attack test | |||||
""" | |||||
# upload trained network | |||||
ckpt_name = './trained_ckpt_file/checkpoint_lenet-10_1875.ckpt' | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
net.set_train(False) | |||||
# get test data | |||||
data_list = "./MNIST_unzip/test" | |||||
batch_size = 32 | |||||
ds = generate_mnist_dataset(data_list, batch_size=batch_size) | |||||
# prediction accuracy before attack | |||||
model = ModelToBeAttacked(net) | |||||
# the number of batches of attacking samples | |||||
batch_num = 5 | |||||
test_images = [] | |||||
test_labels = [] | |||||
predict_labels = [] | |||||
i = 0 | |||||
for data in ds.create_tuple_iterator(): | |||||
i += 1 | |||||
images = data[0].astype(np.float32) | |||||
labels = data[1] | |||||
test_images.append(images) | |||||
test_labels.append(labels) | |||||
pred_labels = np.argmax(model.predict(images), axis=1) | |||||
predict_labels.append(pred_labels) | |||||
if i >= batch_num: | |||||
break | |||||
predict_labels = np.concatenate(predict_labels) | |||||
true_labels = np.concatenate(test_labels) | |||||
accuracy = np.mean(np.equal(predict_labels, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy before attacking is : %s", | |||||
accuracy) | |||||
test_images = np.concatenate(test_images) | |||||
# attacking | |||||
scene = 'Query_Limit' | |||||
if scene == 'Query_Limit': | |||||
top_k = -1 | |||||
elif scene == 'Partial_Info': | |||||
top_k = 5 | |||||
elif scene == 'Label_Only': | |||||
top_k = 5 | |||||
success = 0 | |||||
queries_num = 0 | |||||
nes_instance = NES(model, scene, top_k=top_k) | |||||
test_length = 32 | |||||
advs = [] | |||||
for img_index in range(test_length): | |||||
# Initial image and class selection | |||||
initial_img = test_images[img_index] | |||||
orig_class = true_labels[img_index] | |||||
initial_img = [initial_img] | |||||
target_class = random_target_labels([orig_class], true_labels) | |||||
target_image = create_target_images(test_images, true_labels, | |||||
target_class) | |||||
nes_instance.set_target_images(target_image) | |||||
tag, adv, queries = nes_instance.generate(initial_img, target_class) | |||||
if tag[0]: | |||||
success += 1 | |||||
queries_num += queries[0] | |||||
advs.append(adv) | |||||
advs = np.reshape(advs, (len(advs), 1, 32, 32)) | |||||
adv_pred = np.argmax(model.predict(advs), axis=1) | |||||
adv_accuracy = np.mean(np.equal(adv_pred, true_labels[:test_length])) | |||||
LOGGER.info(TAG, "prediction accuracy after attacking is : %s", | |||||
adv_accuracy) | |||||
if __name__ == '__main__': | |||||
test_nes_mnist_attack() |
@@ -0,0 +1,119 @@ | |||||
# 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. | |||||
import sys | |||||
import time | |||||
import numpy as np | |||||
import pytest | |||||
from scipy.special import softmax | |||||
from mindspore import Model | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks.iterative_gradient_method import ProjectedGradientDescent | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.evaluations.attack_evaluation import AttackEvaluate | |||||
from lenet5_net import LeNet5 | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
sys.path.append("..") | |||||
from data_processing import generate_mnist_dataset | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'PGD_Test' | |||||
@pytest.mark.level1 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_projected_gradient_descent_method(): | |||||
""" | |||||
PGD-Attack test | |||||
""" | |||||
# upload trained network | |||||
ckpt_name = './trained_ckpt_file/checkpoint_lenet-10_1875.ckpt' | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
# get test data | |||||
data_list = "./MNIST_unzip/test" | |||||
batch_size = 32 | |||||
ds = generate_mnist_dataset(data_list, batch_size, sparse=False) | |||||
# prediction accuracy before attack | |||||
model = Model(net) | |||||
batch_num = 32 # the number of batches of attacking samples | |||||
test_images = [] | |||||
test_labels = [] | |||||
predict_labels = [] | |||||
i = 0 | |||||
for data in ds.create_tuple_iterator(): | |||||
i += 1 | |||||
images = data[0].astype(np.float32) | |||||
labels = data[1] | |||||
test_images.append(images) | |||||
test_labels.append(labels) | |||||
pred_labels = np.argmax(model.predict(Tensor(images)).asnumpy(), | |||||
axis=1) | |||||
predict_labels.append(pred_labels) | |||||
if i >= batch_num: | |||||
break | |||||
predict_labels = np.concatenate(predict_labels) | |||||
true_labels = np.argmax(np.concatenate(test_labels), axis=1) | |||||
accuracy = np.mean(np.equal(predict_labels, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy before attacking is : %s", accuracy) | |||||
# attacking | |||||
attack = ProjectedGradientDescent(net, eps=0.3) | |||||
start_time = time.clock() | |||||
adv_data = attack.batch_generate(np.concatenate(test_images), | |||||
np.concatenate(test_labels), batch_size=32) | |||||
stop_time = time.clock() | |||||
np.save('./adv_data', adv_data) | |||||
pred_logits_adv = model.predict(Tensor(adv_data)).asnumpy() | |||||
# rescale predict confidences into (0, 1). | |||||
pred_logits_adv = softmax(pred_logits_adv, axis=1) | |||||
pred_labels_adv = np.argmax(pred_logits_adv, axis=1) | |||||
accuracy_adv = np.mean(np.equal(pred_labels_adv, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy after attacking is : %s", accuracy_adv) | |||||
attack_evaluate = AttackEvaluate(np.concatenate(test_images).transpose(0, 2, 3, 1), | |||||
np.concatenate(test_labels), | |||||
adv_data.transpose(0, 2, 3, 1), | |||||
pred_logits_adv) | |||||
LOGGER.info(TAG, 'mis-classification rate of adversaries is : %s', | |||||
attack_evaluate.mis_classification_rate()) | |||||
LOGGER.info(TAG, 'The average confidence of adversarial class is : %s', | |||||
attack_evaluate.avg_conf_adv_class()) | |||||
LOGGER.info(TAG, 'The average confidence of true class is : %s', | |||||
attack_evaluate.avg_conf_true_class()) | |||||
LOGGER.info(TAG, 'The average distance (l0, l2, linf) between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_lp_distance()) | |||||
LOGGER.info(TAG, 'The average structural similarity between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_ssim()) | |||||
LOGGER.info(TAG, 'The average costing time is %s', | |||||
(stop_time - start_time)/(batch_num*batch_size)) | |||||
if __name__ == '__main__': | |||||
test_projected_gradient_descent_method() |
@@ -0,0 +1,138 @@ | |||||
# 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. | |||||
import sys | |||||
import numpy as np | |||||
import pytest | |||||
from scipy.special import softmax | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks.black.pointwise_attack import PointWiseAttack | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.evaluations.attack_evaluation import AttackEvaluate | |||||
from lenet5_net import LeNet5 | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
sys.path.append("..") | |||||
from data_processing import generate_mnist_dataset | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'Pointwise_Attack' | |||||
LOGGER.set_level('INFO') | |||||
class ModelToBeAttacked(BlackModel): | |||||
"""model to be attack""" | |||||
def __init__(self, network): | |||||
super(ModelToBeAttacked, self).__init__() | |||||
self._network = network | |||||
def predict(self, inputs): | |||||
"""predict""" | |||||
if len(inputs.shape) == 3: | |||||
inputs = inputs[np.newaxis, :] | |||||
result = self._network(Tensor(inputs.astype(np.float32))) | |||||
return result.asnumpy() | |||||
@pytest.mark.level1 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_pointwise_attack_on_mnist(): | |||||
""" | |||||
Salt-and-Pepper-Attack test | |||||
""" | |||||
# upload trained network | |||||
ckpt_name = './trained_ckpt_file/checkpoint_lenet-10_1875.ckpt' | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
# get test data | |||||
data_list = "./MNIST_unzip/test" | |||||
batch_size = 32 | |||||
ds = generate_mnist_dataset(data_list, batch_size=batch_size) | |||||
# prediction accuracy before attack | |||||
model = ModelToBeAttacked(net) | |||||
batch_num = 3 # the number of batches of attacking samples | |||||
test_images = [] | |||||
test_labels = [] | |||||
predict_labels = [] | |||||
i = 0 | |||||
for data in ds.create_tuple_iterator(): | |||||
i += 1 | |||||
images = data[0].astype(np.float32) | |||||
labels = data[1] | |||||
test_images.append(images) | |||||
test_labels.append(labels) | |||||
pred_labels = np.argmax(model.predict(images), axis=1) | |||||
predict_labels.append(pred_labels) | |||||
if i >= batch_num: | |||||
break | |||||
predict_labels = np.concatenate(predict_labels) | |||||
true_labels = np.concatenate(test_labels) | |||||
accuracy = np.mean(np.equal(predict_labels, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy before attacking is : %g", accuracy) | |||||
# attacking | |||||
is_target = False | |||||
attack = PointWiseAttack(model=model, is_targeted=is_target) | |||||
if is_target: | |||||
targeted_labels = np.random.randint(0, 10, size=len(true_labels)) | |||||
for i in range(len(true_labels)): | |||||
if targeted_labels[i] == true_labels[i]: | |||||
targeted_labels[i] = (targeted_labels[i] + 1) % 10 | |||||
else: | |||||
targeted_labels = true_labels | |||||
success_list, adv_data, query_list = attack.generate( | |||||
np.concatenate(test_images), targeted_labels) | |||||
success_list = np.arange(success_list.shape[0])[success_list] | |||||
LOGGER.info(TAG, 'success_list: %s', success_list) | |||||
LOGGER.info(TAG, 'average of query times is : %s', np.mean(query_list)) | |||||
adv_preds = [] | |||||
for ite_data in adv_data: | |||||
pred_logits_adv = model.predict(ite_data) | |||||
# rescale predict confidences into (0, 1). | |||||
pred_logits_adv = softmax(pred_logits_adv, axis=1) | |||||
adv_preds.extend(pred_logits_adv) | |||||
accuracy_adv = np.mean(np.equal(np.max(adv_preds, axis=1), true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy after attacking is : %g", | |||||
accuracy_adv) | |||||
test_labels_onehot = np.eye(10)[true_labels] | |||||
attack_evaluate = AttackEvaluate(np.concatenate(test_images), | |||||
test_labels_onehot, adv_data, | |||||
adv_preds, targeted=is_target, | |||||
target_label=targeted_labels) | |||||
LOGGER.info(TAG, 'mis-classification rate of adversaries is : %s', | |||||
attack_evaluate.mis_classification_rate()) | |||||
LOGGER.info(TAG, 'The average confidence of adversarial class is : %s', | |||||
attack_evaluate.avg_conf_adv_class()) | |||||
LOGGER.info(TAG, 'The average confidence of true class is : %s', | |||||
attack_evaluate.avg_conf_true_class()) | |||||
LOGGER.info(TAG, 'The average distance (l0, l2, linf) between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_lp_distance()) | |||||
if __name__ == '__main__': | |||||
test_pointwise_attack_on_mnist() |
@@ -0,0 +1,131 @@ | |||||
# 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. | |||||
import sys | |||||
import time | |||||
import numpy as np | |||||
import pytest | |||||
from scipy.special import softmax | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks.black.pso_attack import PSOAttack | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.evaluations.attack_evaluation import AttackEvaluate | |||||
from lenet5_net import LeNet5 | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
sys.path.append("..") | |||||
from data_processing import generate_mnist_dataset | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'PSO_Attack' | |||||
class ModelToBeAttacked(BlackModel): | |||||
"""model to be attack""" | |||||
def __init__(self, network): | |||||
super(ModelToBeAttacked, self).__init__() | |||||
self._network = network | |||||
def predict(self, inputs): | |||||
"""predict""" | |||||
result = self._network(Tensor(inputs.astype(np.float32))) | |||||
return result.asnumpy() | |||||
@pytest.mark.level1 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_pso_attack_on_mnist(): | |||||
""" | |||||
PSO-Attack test | |||||
""" | |||||
# upload trained network | |||||
ckpt_name = './trained_ckpt_file/checkpoint_lenet-10_1875.ckpt' | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
# get test data | |||||
data_list = "./MNIST_unzip/test" | |||||
batch_size = 32 | |||||
ds = generate_mnist_dataset(data_list, batch_size=batch_size) | |||||
# prediction accuracy before attack | |||||
model = ModelToBeAttacked(net) | |||||
batch_num = 3 # the number of batches of attacking samples | |||||
test_images = [] | |||||
test_labels = [] | |||||
predict_labels = [] | |||||
i = 0 | |||||
for data in ds.create_tuple_iterator(): | |||||
i += 1 | |||||
images = data[0].astype(np.float32) | |||||
labels = data[1] | |||||
test_images.append(images) | |||||
test_labels.append(labels) | |||||
pred_labels = np.argmax(model.predict(images), axis=1) | |||||
predict_labels.append(pred_labels) | |||||
if i >= batch_num: | |||||
break | |||||
predict_labels = np.concatenate(predict_labels) | |||||
true_labels = np.concatenate(test_labels) | |||||
accuracy = np.mean(np.equal(predict_labels, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy before attacking is : %s", accuracy) | |||||
# attacking | |||||
attack = PSOAttack(model, bounds=(0.0, 1.0), pm=0.5, sparse=True) | |||||
start_time = time.clock() | |||||
success_list, adv_data, query_list = attack.generate( | |||||
np.concatenate(test_images), np.concatenate(test_labels)) | |||||
stop_time = time.clock() | |||||
LOGGER.info(TAG, 'success_list: %s', success_list) | |||||
LOGGER.info(TAG, 'average of query times is : %s', np.mean(query_list)) | |||||
pred_logits_adv = model.predict(adv_data) | |||||
# rescale predict confidences into (0, 1). | |||||
pred_logits_adv = softmax(pred_logits_adv, axis=1) | |||||
pred_labels_adv = np.argmax(pred_logits_adv, axis=1) | |||||
accuracy_adv = np.mean(np.equal(pred_labels_adv, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy after attacking is : %s", | |||||
accuracy_adv) | |||||
test_labels_onehot = np.eye(10)[np.concatenate(test_labels)] | |||||
attack_evaluate = AttackEvaluate(np.concatenate(test_images), | |||||
test_labels_onehot, adv_data, | |||||
pred_logits_adv) | |||||
LOGGER.info(TAG, 'mis-classification rate of adversaries is : %s', | |||||
attack_evaluate.mis_classification_rate()) | |||||
LOGGER.info(TAG, 'The average confidence of adversarial class is : %s', | |||||
attack_evaluate.avg_conf_adv_class()) | |||||
LOGGER.info(TAG, 'The average confidence of true class is : %s', | |||||
attack_evaluate.avg_conf_true_class()) | |||||
LOGGER.info(TAG, 'The average distance (l0, l2, linf) between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_lp_distance()) | |||||
LOGGER.info(TAG, 'The average structural similarity between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_ssim()) | |||||
LOGGER.info(TAG, 'The average costing time is %s', | |||||
(stop_time - start_time)/(batch_num*batch_size)) | |||||
if __name__ == '__main__': | |||||
test_pso_attack_on_mnist() |
@@ -0,0 +1,142 @@ | |||||
# 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. | |||||
import sys | |||||
import numpy as np | |||||
import pytest | |||||
from scipy.special import softmax | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks.black.salt_and_pepper_attack import SaltAndPepperNoiseAttack | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.evaluations.attack_evaluation import AttackEvaluate | |||||
from lenet5_net import LeNet5 | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
sys.path.append("..") | |||||
from data_processing import generate_mnist_dataset | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'Salt_and_Pepper_Attack' | |||||
LOGGER.set_level('DEBUG') | |||||
class ModelToBeAttacked(BlackModel): | |||||
"""model to be attack""" | |||||
def __init__(self, network): | |||||
super(ModelToBeAttacked, self).__init__() | |||||
self._network = network | |||||
def predict(self, inputs): | |||||
"""predict""" | |||||
if len(inputs.shape) == 3: | |||||
inputs = inputs[np.newaxis, :] | |||||
result = self._network(Tensor(inputs.astype(np.float32))) | |||||
return result.asnumpy() | |||||
@pytest.mark.level1 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_salt_and_pepper_attack_on_mnist(): | |||||
""" | |||||
Salt-and-Pepper-Attack test | |||||
""" | |||||
# upload trained network | |||||
ckpt_name = './trained_ckpt_file/checkpoint_lenet-10_1875.ckpt' | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
# get test data | |||||
data_list = "./MNIST_unzip/test" | |||||
batch_size = 32 | |||||
ds = generate_mnist_dataset(data_list, batch_size=batch_size) | |||||
# prediction accuracy before attack | |||||
model = ModelToBeAttacked(net) | |||||
batch_num = 3 # the number of batches of attacking samples | |||||
test_images = [] | |||||
test_labels = [] | |||||
predict_labels = [] | |||||
i = 0 | |||||
for data in ds.create_tuple_iterator(): | |||||
i += 1 | |||||
images = data[0].astype(np.float32) | |||||
labels = data[1] | |||||
test_images.append(images) | |||||
test_labels.append(labels) | |||||
pred_labels = np.argmax(model.predict(images), axis=1) | |||||
predict_labels.append(pred_labels) | |||||
if i >= batch_num: | |||||
break | |||||
LOGGER.debug(TAG, 'model input image shape is: {}'.format(np.array(test_images).shape)) | |||||
predict_labels = np.concatenate(predict_labels) | |||||
true_labels = np.concatenate(test_labels) | |||||
accuracy = np.mean(np.equal(predict_labels, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy before attacking is : %g", accuracy) | |||||
# attacking | |||||
is_target = False | |||||
attack = SaltAndPepperNoiseAttack(model=model, | |||||
is_targeted=is_target, | |||||
sparse=True) | |||||
if is_target: | |||||
targeted_labels = np.random.randint(0, 10, size=len(true_labels)) | |||||
for i in range(len(true_labels)): | |||||
if targeted_labels[i] == true_labels[i]: | |||||
targeted_labels[i] = (targeted_labels[i] + 1) % 10 | |||||
else: | |||||
targeted_labels = true_labels | |||||
LOGGER.debug(TAG, 'input shape is: {}'.format(np.concatenate(test_images).shape)) | |||||
success_list, adv_data, query_list = attack.generate( | |||||
np.concatenate(test_images), targeted_labels) | |||||
success_list = np.arange(success_list.shape[0])[success_list] | |||||
LOGGER.info(TAG, 'success_list: %s', success_list) | |||||
LOGGER.info(TAG, 'average of query times is : %s', np.mean(query_list)) | |||||
adv_preds = [] | |||||
for ite_data in adv_data: | |||||
pred_logits_adv = model.predict(ite_data) | |||||
# rescale predict confidences into (0, 1). | |||||
pred_logits_adv = softmax(pred_logits_adv, axis=1) | |||||
adv_preds.extend(pred_logits_adv) | |||||
accuracy_adv = np.mean(np.equal(np.max(adv_preds, axis=1), true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy after attacking is : %g", | |||||
accuracy_adv) | |||||
test_labels_onehot = np.eye(10)[true_labels] | |||||
attack_evaluate = AttackEvaluate(np.concatenate(test_images), | |||||
test_labels_onehot, adv_data, | |||||
adv_preds, targeted=is_target, | |||||
target_label=targeted_labels) | |||||
LOGGER.info(TAG, 'mis-classification rate of adversaries is : %s', | |||||
attack_evaluate.mis_classification_rate()) | |||||
LOGGER.info(TAG, 'The average confidence of adversarial class is : %s', | |||||
attack_evaluate.avg_conf_adv_class()) | |||||
LOGGER.info(TAG, 'The average confidence of true class is : %s', | |||||
attack_evaluate.avg_conf_true_class()) | |||||
LOGGER.info(TAG, 'The average distance (l0, l2, linf) between original ' | |||||
'samples and adversarial samples are: %s', | |||||
attack_evaluate.avg_lp_distance()) | |||||
if __name__ == '__main__': | |||||
test_salt_and_pepper_attack_on_mnist() |
@@ -0,0 +1,144 @@ | |||||
# 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. | |||||
"""defense example using nad""" | |||||
import sys | |||||
import logging | |||||
import numpy as np | |||||
import pytest | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore import nn | |||||
from mindspore.nn import SoftmaxCrossEntropyWithLogits | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks import FastGradientSignMethod | |||||
from mindarmour.defenses import NaturalAdversarialDefense | |||||
from mindarmour.utils.logger import LogUtil | |||||
from lenet5_net import LeNet5 | |||||
sys.path.append("..") | |||||
from data_processing import generate_mnist_dataset | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'Nad_Example' | |||||
@pytest.mark.level1 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_nad_method(): | |||||
""" | |||||
NAD-Defense test. | |||||
""" | |||||
# 1. load trained network | |||||
ckpt_name = './trained_ckpt_file/checkpoint_lenet-10_1875.ckpt' | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
loss = SoftmaxCrossEntropyWithLogits(is_grad=False, sparse=False) | |||||
opt = nn.Momentum(net.trainable_params(), 0.01, 0.09) | |||||
nad = NaturalAdversarialDefense(net, loss_fn=loss, optimizer=opt, | |||||
bounds=(0.0, 1.0), eps=0.3) | |||||
# 2. get test data | |||||
data_list = "./MNIST_unzip/test" | |||||
batch_size = 32 | |||||
ds_test = generate_mnist_dataset(data_list, batch_size=batch_size, | |||||
sparse=False) | |||||
inputs = [] | |||||
labels = [] | |||||
for data in ds_test.create_tuple_iterator(): | |||||
inputs.append(data[0].astype(np.float32)) | |||||
labels.append(data[1]) | |||||
inputs = np.concatenate(inputs) | |||||
labels = np.concatenate(labels) | |||||
# 3. get accuracy of test data on original model | |||||
net.set_train(False) | |||||
acc_list = [] | |||||
batchs = inputs.shape[0] // batch_size | |||||
for i in range(batchs): | |||||
batch_inputs = inputs[i*batch_size : (i + 1)*batch_size] | |||||
batch_labels = np.argmax(labels[i*batch_size : (i + 1)*batch_size], axis=1) | |||||
logits = net(Tensor(batch_inputs)).asnumpy() | |||||
label_pred = np.argmax(logits, axis=1) | |||||
acc_list.append(np.mean(batch_labels == label_pred)) | |||||
LOGGER.debug(TAG, 'accuracy of TEST data on original model is : %s', | |||||
np.mean(acc_list)) | |||||
# 4. get adv of test data | |||||
attack = FastGradientSignMethod(net, eps=0.3) | |||||
adv_data = attack.batch_generate(inputs, labels) | |||||
LOGGER.debug(TAG, 'adv_data.shape is : %s', adv_data.shape) | |||||
# 5. get accuracy of adv data on original model | |||||
net.set_train(False) | |||||
acc_list = [] | |||||
batchs = adv_data.shape[0] // batch_size | |||||
for i in range(batchs): | |||||
batch_inputs = adv_data[i*batch_size : (i + 1)*batch_size] | |||||
batch_labels = np.argmax(labels[i*batch_size : (i + 1)*batch_size], axis=1) | |||||
logits = net(Tensor(batch_inputs)).asnumpy() | |||||
label_pred = np.argmax(logits, axis=1) | |||||
acc_list.append(np.mean(batch_labels == label_pred)) | |||||
LOGGER.debug(TAG, 'accuracy of adv data on original model is : %s', | |||||
np.mean(acc_list)) | |||||
# 6. defense | |||||
net.set_train() | |||||
nad.batch_defense(inputs, labels, batch_size=32, epochs=10) | |||||
# 7. get accuracy of test data on defensed model | |||||
net.set_train(False) | |||||
acc_list = [] | |||||
batchs = inputs.shape[0] // batch_size | |||||
for i in range(batchs): | |||||
batch_inputs = inputs[i*batch_size : (i + 1)*batch_size] | |||||
batch_labels = np.argmax(labels[i*batch_size : (i + 1)*batch_size], axis=1) | |||||
logits = net(Tensor(batch_inputs)).asnumpy() | |||||
label_pred = np.argmax(logits, axis=1) | |||||
acc_list.append(np.mean(batch_labels == label_pred)) | |||||
LOGGER.debug(TAG, 'accuracy of TEST data on defensed model is : %s', | |||||
np.mean(acc_list)) | |||||
# 8. get accuracy of adv data on defensed model | |||||
acc_list = [] | |||||
batchs = adv_data.shape[0] // batch_size | |||||
for i in range(batchs): | |||||
batch_inputs = adv_data[i*batch_size : (i + 1)*batch_size] | |||||
batch_labels = np.argmax(labels[i*batch_size : (i + 1)*batch_size], axis=1) | |||||
logits = net(Tensor(batch_inputs)).asnumpy() | |||||
label_pred = np.argmax(logits, axis=1) | |||||
acc_list.append(np.mean(batch_labels == label_pred)) | |||||
LOGGER.debug(TAG, 'accuracy of adv data on defensed model is : %s', | |||||
np.mean(acc_list)) | |||||
if __name__ == '__main__': | |||||
LOGGER.set_level(logging.DEBUG) | |||||
test_nad_method() |
@@ -0,0 +1,326 @@ | |||||
# 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. | |||||
"""evaluate example""" | |||||
import sys | |||||
import os | |||||
import time | |||||
import numpy as np | |||||
from scipy.special import softmax | |||||
from lenet5_net import LeNet5 | |||||
from mindspore import Model | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore import nn | |||||
from mindspore.nn import Cell | |||||
from mindspore.ops.operations import TensorAdd | |||||
from mindspore.nn import SoftmaxCrossEntropyWithLogits | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks import FastGradientSignMethod | |||||
from mindarmour.attacks import GeneticAttack | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
from mindarmour.defenses import NaturalAdversarialDefense | |||||
from mindarmour.evaluations import BlackDefenseEvaluate | |||||
from mindarmour.evaluations import DefenseEvaluate | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.detectors.black.similarity_detector import SimilarityDetector | |||||
sys.path.append("..") | |||||
from data_processing import generate_mnist_dataset | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'Defense_Evaluate_Example' | |||||
def get_detector(train_images): | |||||
encoder = Model(EncoderNet(encode_dim=256)) | |||||
detector = SimilarityDetector(max_k_neighbor=50, trans_model=encoder) | |||||
detector.fit(inputs=train_images) | |||||
return detector | |||||
class EncoderNet(Cell): | |||||
""" | |||||
Similarity encoder for input data | |||||
""" | |||||
def __init__(self, encode_dim): | |||||
super(EncoderNet, self).__init__() | |||||
self._encode_dim = encode_dim | |||||
self.add = TensorAdd() | |||||
def construct(self, inputs): | |||||
""" | |||||
construct the neural network | |||||
Args: | |||||
inputs (Tensor): input data to neural network. | |||||
Returns: | |||||
Tensor, output of neural network. | |||||
""" | |||||
return self.add(inputs, inputs) | |||||
def get_encode_dim(self): | |||||
""" | |||||
Get the dimension of encoded inputs | |||||
Returns: | |||||
int, dimension of encoded inputs. | |||||
""" | |||||
return self._encode_dim | |||||
class ModelToBeAttacked(BlackModel): | |||||
""" | |||||
model to be attack | |||||
""" | |||||
def __init__(self, network, defense=False, train_images=None): | |||||
super(ModelToBeAttacked, self).__init__() | |||||
self._network = network | |||||
self._queries = [] | |||||
self._defense = defense | |||||
self._detector = None | |||||
self._detected_res = [] | |||||
if self._defense: | |||||
self._detector = get_detector(train_images) | |||||
def predict(self, inputs): | |||||
""" | |||||
predict function | |||||
""" | |||||
query_num = inputs.shape[0] | |||||
results = [] | |||||
if self._detector: | |||||
for i in range(query_num): | |||||
query = np.expand_dims(inputs[i].astype(np.float32), axis=0) | |||||
result = self._network(Tensor(query)).asnumpy() | |||||
det_num = len(self._detector.get_detected_queries()) | |||||
self._detector.detect([query]) | |||||
new_det_num = len(self._detector.get_detected_queries()) | |||||
# If attack query detected, return random predict result | |||||
if new_det_num > det_num: | |||||
results.append(result + np.random.rand(*result.shape)) | |||||
self._detected_res.append(True) | |||||
else: | |||||
results.append(result) | |||||
self._detected_res.append(False) | |||||
results = np.concatenate(results) | |||||
else: | |||||
results = self._network(Tensor(inputs.astype(np.float32))).asnumpy() | |||||
return results | |||||
def get_detected_result(self): | |||||
return self._detected_res | |||||
def test_black_defense(): | |||||
# load trained network | |||||
current_dir = os.path.dirname(os.path.abspath(__file__)) | |||||
ckpt_name = os.path.abspath(os.path.join( | |||||
current_dir, './trained_ckpt_file/checkpoint_lenet-10_1875.ckpt')) | |||||
# ckpt_name = './trained_ckpt_file/checkpoint_lenet-10_1875.ckpt' | |||||
wb_net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(wb_net, load_dict) | |||||
# get test data | |||||
data_list = "./MNIST_unzip/test" | |||||
batch_size = 32 | |||||
ds_test = generate_mnist_dataset(data_list, batch_size=batch_size, | |||||
sparse=False) | |||||
inputs = [] | |||||
labels = [] | |||||
for data in ds_test.create_tuple_iterator(): | |||||
inputs.append(data[0].astype(np.float32)) | |||||
labels.append(data[1]) | |||||
inputs = np.concatenate(inputs).astype(np.float32) | |||||
labels = np.concatenate(labels).astype(np.float32) | |||||
labels_sparse = np.argmax(labels, axis=1) | |||||
target_label = np.random.randint(0, 10, size=labels_sparse.shape[0]) | |||||
for idx in range(labels_sparse.shape[0]): | |||||
while target_label[idx] == labels_sparse[idx]: | |||||
target_label[idx] = np.random.randint(0, 10) | |||||
target_label = np.eye(10)[target_label].astype(np.float32) | |||||
attacked_size = 50 | |||||
benign_size = 500 | |||||
attacked_sample = inputs[:attacked_size] | |||||
attacked_true_label = labels[:attacked_size] | |||||
benign_sample = inputs[attacked_size:attacked_size + benign_size] | |||||
wb_model = ModelToBeAttacked(wb_net) | |||||
# gen white-box adversarial examples of test data | |||||
wb_attack = FastGradientSignMethod(wb_net, eps=0.3) | |||||
wb_adv_sample = wb_attack.generate(attacked_sample, | |||||
attacked_true_label) | |||||
wb_raw_preds = softmax(wb_model.predict(wb_adv_sample), axis=1) | |||||
accuracy_test = np.mean( | |||||
np.equal(np.argmax(wb_model.predict(attacked_sample), axis=1), | |||||
np.argmax(attacked_true_label, axis=1))) | |||||
LOGGER.info(TAG, "prediction accuracy before white-box attack is : %s", | |||||
accuracy_test) | |||||
accuracy_adv = np.mean(np.equal(np.argmax(wb_raw_preds, axis=1), | |||||
np.argmax(attacked_true_label, axis=1))) | |||||
LOGGER.info(TAG, "prediction accuracy after white-box attack is : %s", | |||||
accuracy_adv) | |||||
# improve the robustness of model with white-box adversarial examples | |||||
loss = SoftmaxCrossEntropyWithLogits(is_grad=False, sparse=False) | |||||
opt = nn.Momentum(wb_net.trainable_params(), 0.01, 0.09) | |||||
nad = NaturalAdversarialDefense(wb_net, loss_fn=loss, optimizer=opt, | |||||
bounds=(0.0, 1.0), eps=0.3) | |||||
wb_net.set_train(False) | |||||
nad.batch_defense(inputs[:5000], labels[:5000], batch_size=32, epochs=10) | |||||
wb_def_preds = wb_net(Tensor(wb_adv_sample)).asnumpy() | |||||
wb_def_preds = softmax(wb_def_preds, axis=1) | |||||
accuracy_def = np.mean(np.equal(np.argmax(wb_def_preds, axis=1), | |||||
np.argmax(attacked_true_label, axis=1))) | |||||
LOGGER.info(TAG, "prediction accuracy after defense is : %s", accuracy_def) | |||||
# calculate defense evaluation metrics for defense against white-box attack | |||||
wb_def_evaluate = DefenseEvaluate(wb_raw_preds, wb_def_preds, | |||||
np.argmax(attacked_true_label, axis=1)) | |||||
LOGGER.info(TAG, 'defense evaluation for white-box adversarial attack') | |||||
LOGGER.info(TAG, | |||||
'classification accuracy variance (CAV) is : {:.2f}'.format( | |||||
wb_def_evaluate.cav())) | |||||
LOGGER.info(TAG, 'classification rectify ratio (CRR) is : {:.2f}'.format( | |||||
wb_def_evaluate.crr())) | |||||
LOGGER.info(TAG, 'classification sacrifice ratio (CSR) is : {:.2f}'.format( | |||||
wb_def_evaluate.csr())) | |||||
LOGGER.info(TAG, | |||||
'classification confidence variance (CCV) is : {:.2f}'.format( | |||||
wb_def_evaluate.ccv())) | |||||
LOGGER.info(TAG, 'classification output stability is : {:.2f}'.format( | |||||
wb_def_evaluate.cos())) | |||||
# calculate defense evaluation metrics for defense against black-box attack | |||||
LOGGER.info(TAG, 'defense evaluation for black-box adversarial attack') | |||||
bb_raw_preds = [] | |||||
bb_def_preds = [] | |||||
raw_query_counts = [] | |||||
raw_query_time = [] | |||||
def_query_counts = [] | |||||
def_query_time = [] | |||||
def_detection_counts = [] | |||||
# gen black-box adversarial examples of test data | |||||
bb_net = LeNet5() | |||||
load_param_into_net(bb_net, load_dict) | |||||
bb_model = ModelToBeAttacked(bb_net, defense=False) | |||||
attack_rm = GeneticAttack(model=bb_model, pop_size=6, mutation_rate=0.05, | |||||
per_bounds=0.1, step_size=0.25, temp=0.1, | |||||
sparse=False) | |||||
attack_target_label = target_label[:attacked_size] | |||||
true_label = labels_sparse[:attacked_size + benign_size] | |||||
# evaluate robustness of original model | |||||
# gen black-box adversarial examples of test data | |||||
for idx in range(attacked_size): | |||||
raw_st = time.time() | |||||
raw_sl, raw_a, raw_qc = attack_rm.generate( | |||||
np.expand_dims(attacked_sample[idx], axis=0), | |||||
np.expand_dims(attack_target_label[idx], axis=0)) | |||||
raw_t = time.time() - raw_st | |||||
bb_raw_preds.extend(softmax(bb_model.predict(raw_a), axis=1)) | |||||
raw_query_counts.extend(raw_qc) | |||||
raw_query_time.append(raw_t) | |||||
for idx in range(benign_size): | |||||
raw_st = time.time() | |||||
bb_raw_pred = softmax( | |||||
bb_model.predict(np.expand_dims(benign_sample[idx], axis=0)), | |||||
axis=1) | |||||
raw_t = time.time() - raw_st | |||||
bb_raw_preds.extend(bb_raw_pred) | |||||
raw_query_counts.extend([0]) | |||||
raw_query_time.append(raw_t) | |||||
accuracy_test = np.mean( | |||||
np.equal(np.argmax(bb_raw_preds[0:len(attack_target_label)], axis=1), | |||||
np.argmax(attack_target_label, axis=1))) | |||||
LOGGER.info(TAG, "attack success before adv defense is : %s", | |||||
accuracy_test) | |||||
# improve the robustness of model with similarity-based detector | |||||
bb_def_model = ModelToBeAttacked(bb_net, defense=True, | |||||
train_images=inputs[0:6000]) | |||||
# attack defensed model | |||||
attack_dm = GeneticAttack(model=bb_def_model, pop_size=6, | |||||
mutation_rate=0.05, | |||||
per_bounds=0.1, step_size=0.25, temp=0.1, | |||||
sparse=False) | |||||
for idx in range(attacked_size): | |||||
def_st = time.time() | |||||
def_sl, def_a, def_qc = attack_dm.generate( | |||||
np.expand_dims(attacked_sample[idx], axis=0), | |||||
np.expand_dims(attack_target_label[idx], axis=0)) | |||||
def_t = time.time() - def_st | |||||
det_res = bb_def_model.get_detected_result() | |||||
def_detection_counts.append(np.sum(det_res[-def_qc[0]:])) | |||||
bb_def_preds.extend(softmax(bb_def_model.predict(def_a), axis=1)) | |||||
def_query_counts.extend(def_qc) | |||||
def_query_time.append(def_t) | |||||
for idx in range(benign_size): | |||||
def_st = time.time() | |||||
bb_def_pred = softmax( | |||||
bb_def_model.predict(np.expand_dims(benign_sample[idx], axis=0)), | |||||
axis=1) | |||||
def_t = time.time() - def_st | |||||
det_res = bb_def_model.get_detected_result() | |||||
def_detection_counts.append(np.sum(det_res[-1])) | |||||
bb_def_preds.extend(bb_def_pred) | |||||
def_query_counts.extend([0]) | |||||
def_query_time.append(def_t) | |||||
accuracy_adv = np.mean( | |||||
np.equal(np.argmax(bb_def_preds[0:len(attack_target_label)], axis=1), | |||||
np.argmax(attack_target_label, axis=1))) | |||||
LOGGER.info(TAG, "attack success rate after adv defense is : %s", | |||||
accuracy_adv) | |||||
bb_raw_preds = np.array(bb_raw_preds).astype(np.float32) | |||||
bb_def_preds = np.array(bb_def_preds).astype(np.float32) | |||||
# check evaluate data | |||||
max_queries = 6000 | |||||
def_evaluate = BlackDefenseEvaluate(bb_raw_preds, bb_def_preds, | |||||
np.array(raw_query_counts), | |||||
np.array(def_query_counts), | |||||
np.array(raw_query_time), | |||||
np.array(def_query_time), | |||||
np.array(def_detection_counts), | |||||
true_label, max_queries) | |||||
LOGGER.info(TAG, 'query count variance of adversaries is : {:.2f}'.format( | |||||
def_evaluate.qcv())) | |||||
LOGGER.info(TAG, 'attack success rate variance of adversaries ' | |||||
'is : {:.2f}'.format(def_evaluate.asv())) | |||||
LOGGER.info(TAG, 'false positive rate (FPR) of the query-based detector ' | |||||
'is : {:.2f}'.format(def_evaluate.fpr())) | |||||
LOGGER.info(TAG, 'the benign query response time variance (QRV) ' | |||||
'is : {:.2f}'.format(def_evaluate.qrv())) | |||||
if __name__ == '__main__': | |||||
test_black_defense() |
@@ -0,0 +1,182 @@ | |||||
# 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. | |||||
import sys | |||||
import numpy as np | |||||
import pytest | |||||
from scipy.special import softmax | |||||
from mindspore import Model | |||||
from mindspore import context | |||||
from mindspore import Tensor | |||||
from mindspore.nn import Cell | |||||
from mindspore.ops.operations import TensorAdd | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.attacks.black.pso_attack import PSOAttack | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
from mindarmour.detectors.black.similarity_detector import SimilarityDetector | |||||
from lenet5_net import LeNet5 | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
sys.path.append("..") | |||||
from data_processing import generate_mnist_dataset | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'Similarity Detector test' | |||||
class ModelToBeAttacked(BlackModel): | |||||
""" | |||||
model to be attack | |||||
""" | |||||
def __init__(self, network): | |||||
super(ModelToBeAttacked, self).__init__() | |||||
self._network = network | |||||
self._queries = [] | |||||
def predict(self, inputs): | |||||
""" | |||||
predict function | |||||
""" | |||||
query_num = inputs.shape[0] | |||||
for i in range(query_num): | |||||
self._queries.append(inputs[i].astype(np.float32)) | |||||
result = self._network(Tensor(inputs.astype(np.float32))) | |||||
return result.asnumpy() | |||||
def get_queries(self): | |||||
return self._queries | |||||
class EncoderNet(Cell): | |||||
""" | |||||
Similarity encoder for input data | |||||
""" | |||||
def __init__(self, encode_dim): | |||||
super(EncoderNet, self).__init__() | |||||
self._encode_dim = encode_dim | |||||
self.add = TensorAdd() | |||||
def construct(self, inputs): | |||||
""" | |||||
construct the neural network | |||||
Args: | |||||
inputs (Tensor): input data to neural network. | |||||
Returns: | |||||
Tensor, output of neural network. | |||||
""" | |||||
return self.add(inputs, inputs) | |||||
def get_encode_dim(self): | |||||
""" | |||||
Get the dimension of encoded inputs | |||||
Returns: | |||||
int, dimension of encoded inputs. | |||||
""" | |||||
return self._encode_dim | |||||
@pytest.mark.level1 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_similarity_detector(): | |||||
""" | |||||
Similarity Detector test. | |||||
""" | |||||
# load trained network | |||||
ckpt_name = './trained_ckpt_file/checkpoint_lenet-10_1875.ckpt' | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
# get mnist data | |||||
data_list = "./MNIST_unzip/test" | |||||
batch_size = 1000 | |||||
ds = generate_mnist_dataset(data_list, batch_size=batch_size) | |||||
model = ModelToBeAttacked(net) | |||||
batch_num = 10 # the number of batches of input samples | |||||
all_images = [] | |||||
true_labels = [] | |||||
predict_labels = [] | |||||
i = 0 | |||||
for data in ds.create_tuple_iterator(): | |||||
i += 1 | |||||
images = data[0].astype(np.float32) | |||||
labels = data[1] | |||||
all_images.append(images) | |||||
true_labels.append(labels) | |||||
pred_labels = np.argmax(model.predict(images), axis=1) | |||||
predict_labels.append(pred_labels) | |||||
if i >= batch_num: | |||||
break | |||||
all_images = np.concatenate(all_images) | |||||
true_labels = np.concatenate(true_labels) | |||||
predict_labels = np.concatenate(predict_labels) | |||||
accuracy = np.mean(np.equal(predict_labels, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy before attacking is : %s", accuracy) | |||||
train_images = all_images[0:6000, :, :, :] | |||||
attacked_images = all_images[0:10, :, :, :] | |||||
attacked_labels = true_labels[0:10] | |||||
# generate malicious query sequence of black attack | |||||
attack = PSOAttack(model, bounds=(0.0, 1.0), pm=0.5, sparse=True, | |||||
t_max=1000) | |||||
success_list, adv_data, query_list = attack.generate(attacked_images, | |||||
attacked_labels) | |||||
LOGGER.info(TAG, 'pso attack success_list: %s', success_list) | |||||
LOGGER.info(TAG, 'average of query counts is : %s', np.mean(query_list)) | |||||
pred_logits_adv = model.predict(adv_data) | |||||
# rescale predict confidences into (0, 1). | |||||
pred_logits_adv = softmax(pred_logits_adv, axis=1) | |||||
pred_lables_adv = np.argmax(pred_logits_adv, axis=1) | |||||
accuracy_adv = np.mean(np.equal(pred_lables_adv, attacked_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy after attacking is : %g", | |||||
accuracy_adv) | |||||
benign_queries = all_images[6000:10000, :, :, :] | |||||
suspicious_queries = model.get_queries() | |||||
# explicit threshold not provided, calculate threshold for K | |||||
encoder = Model(EncoderNet(encode_dim=256)) | |||||
detector = SimilarityDetector(max_k_neighbor=50, trans_model=encoder) | |||||
detector.fit(inputs=train_images) | |||||
# test benign queries | |||||
detector.detect(benign_queries) | |||||
fpr = len(detector.get_detected_queries()) / benign_queries.shape[0] | |||||
LOGGER.info(TAG, 'Number of false positive of attack detector is : %s', | |||||
len(detector.get_detected_queries())) | |||||
LOGGER.info(TAG, 'False positive rate of attack detector is : %s', fpr) | |||||
# test attack queries | |||||
detector.clear_buffer() | |||||
detector.detect(suspicious_queries) | |||||
LOGGER.info(TAG, 'Number of detected attack queries is : %s', | |||||
len(detector.get_detected_queries())) | |||||
LOGGER.info(TAG, 'The detected attack query indexes are : %s', | |||||
detector.get_detected_queries()) | |||||
if __name__ == '__main__': | |||||
test_similarity_detector() |
@@ -0,0 +1,88 @@ | |||||
# Copyright 2020 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. | |||||
# ============================================================================ | |||||
import os | |||||
import sys | |||||
import mindspore.nn as nn | |||||
from mindspore import context, Tensor | |||||
from mindspore.train.callback import ModelCheckpoint, CheckpointConfig, LossMonitor | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindspore.train import Model | |||||
import mindspore.ops.operations as P | |||||
from mindspore.nn.metrics import Accuracy | |||||
from mindspore.ops import functional as F | |||||
from mindspore.common import dtype as mstype | |||||
from mindarmour.utils.logger import LogUtil | |||||
from lenet5_net import LeNet5 | |||||
sys.path.append("..") | |||||
from data_processing import generate_mnist_dataset | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'Lenet5_train' | |||||
class CrossEntropyLoss(nn.Cell): | |||||
""" | |||||
Define loss for network | |||||
""" | |||||
def __init__(self): | |||||
super(CrossEntropyLoss, self).__init__() | |||||
self.cross_entropy = P.SoftmaxCrossEntropyWithLogits() | |||||
self.mean = P.ReduceMean() | |||||
self.one_hot = P.OneHot() | |||||
self.on_value = Tensor(1.0, mstype.float32) | |||||
self.off_value = Tensor(0.0, mstype.float32) | |||||
def construct(self, logits, label): | |||||
label = self.one_hot(label, F.shape(logits)[1], self.on_value, self.off_value) | |||||
loss = self.cross_entropy(logits, label)[0] | |||||
loss = self.mean(loss, (-1,)) | |||||
return loss | |||||
def mnist_train(epoch_size, batch_size, lr, momentum): | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend", | |||||
enable_mem_reuse=False) | |||||
lr = lr | |||||
momentum = momentum | |||||
epoch_size = epoch_size | |||||
mnist_path = "./MNIST_unzip/" | |||||
ds = generate_mnist_dataset(os.path.join(mnist_path, "train"), | |||||
batch_size=batch_size, repeat_size=1) | |||||
network = LeNet5() | |||||
network.set_train() | |||||
net_loss = CrossEntropyLoss() | |||||
net_opt = nn.Momentum(network.trainable_params(), lr, momentum) | |||||
config_ck = CheckpointConfig(save_checkpoint_steps=1875, keep_checkpoint_max=10) | |||||
ckpoint_cb = ModelCheckpoint(prefix="checkpoint_lenet", directory='./trained_ckpt_file/', config=config_ck) | |||||
model = Model(network, net_loss, net_opt, metrics={"Accuracy": Accuracy()}) | |||||
LOGGER.info(TAG, "============== Starting Training ==============") | |||||
model.train(epoch_size, ds, callbacks=[ckpoint_cb, LossMonitor()], dataset_sink_mode=False) # train | |||||
LOGGER.info(TAG, "============== Starting Testing ==============") | |||||
param_dict = load_checkpoint("trained_ckpt_file/checkpoint_lenet-10_1875.ckpt") | |||||
load_param_into_net(network, param_dict) | |||||
ds_eval = generate_mnist_dataset(os.path.join(mnist_path, "test"), batch_size=batch_size) | |||||
acc = model.eval(ds_eval) | |||||
LOGGER.info(TAG, "============== Accuracy: %s ==============", acc) | |||||
if __name__ == '__main__': | |||||
mnist_train(10, 32, 0.001, 0.9) |
@@ -0,0 +1,13 @@ | |||||
""" | |||||
MindArmour, a tool box of MindSpore to enhance model security and | |||||
trustworthiness against adversarial examples. | |||||
""" | |||||
from .attacks import Attack | |||||
from .attacks.black.black_model import BlackModel | |||||
from .defenses.defense import Defense | |||||
from .detectors.detector import Detector | |||||
__all__ = ['Attack', | |||||
'BlackModel', | |||||
'Detector', | |||||
'Defense'] |
@@ -0,0 +1,39 @@ | |||||
""" | |||||
This module includes classical black-box and white-box attack algorithms | |||||
in making adversarial examples. | |||||
""" | |||||
from .gradient_method import * | |||||
from .iterative_gradient_method import * | |||||
from .deep_fool import DeepFool | |||||
from .jsma import JSMAAttack | |||||
from .carlini_wagner import CarliniWagnerL2Attack | |||||
from .lbfgs import LBFGS | |||||
from . import black | |||||
from .black.hop_skip_jump_attack import HopSkipJumpAttack | |||||
from .black.genetic_attack import GeneticAttack | |||||
from .black.natural_evolutionary_strategy import NES | |||||
from .black.pointwise_attack import PointWiseAttack | |||||
from .black.pso_attack import PSOAttack | |||||
from .black.salt_and_pepper_attack import SaltAndPepperNoiseAttack | |||||
__all__ = ['FastGradientMethod', | |||||
'RandomFastGradientMethod', | |||||
'FastGradientSignMethod', | |||||
'RandomFastGradientSignMethod', | |||||
'LeastLikelyClassMethod', | |||||
'RandomLeastLikelyClassMethod', | |||||
'IterativeGradientMethod', | |||||
'BasicIterativeMethod', | |||||
'MomentumIterativeMethod', | |||||
'ProjectedGradientDescent', | |||||
'DeepFool', | |||||
'CarliniWagnerL2Attack', | |||||
'JSMAAttack', | |||||
'LBFGS', | |||||
'GeneticAttack', | |||||
'HopSkipJumpAttack', | |||||
'NES', | |||||
'PointWiseAttack', | |||||
'PSOAttack', | |||||
'SaltAndPepperNoiseAttack' | |||||
] |
@@ -0,0 +1,97 @@ | |||||
# 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. | |||||
""" | |||||
Base Class of Attack. | |||||
""" | |||||
from abc import abstractmethod | |||||
import numpy as np | |||||
from mindarmour.utils._check_param import check_pair_numpy_param, \ | |||||
check_int_positive | |||||
from mindarmour.utils.logger import LogUtil | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'Attack' | |||||
class Attack: | |||||
""" | |||||
The abstract base class for all attack classes creating adversarial examples. | |||||
""" | |||||
def __init__(self): | |||||
pass | |||||
def batch_generate(self, inputs, labels, batch_size=64): | |||||
""" | |||||
Generate adversarial examples in batch, based on input samples and | |||||
their labels. | |||||
Args: | |||||
inputs (numpy.ndarray): Samples based on which adversarial | |||||
examples are generated. | |||||
labels (numpy.ndarray): Labels of samples, whose values determined | |||||
by specific attacks. | |||||
batch_size (int): The number of samples in one batch. | |||||
Returns: | |||||
numpy.ndarray, generated adversarial examples | |||||
Examples: | |||||
>>> inputs = Tensor([[0.2, 0.4, 0.5, 0.2], [0.7, 0.2, 0.4, 0.3]]) | |||||
>>> labels = [3, 0] | |||||
>>> advs = attack.batch_generate(inputs, labels, batch_size=2) | |||||
""" | |||||
arr_x, arr_y = check_pair_numpy_param('inputs', inputs, 'labels', labels) | |||||
len_x = arr_x.shape[0] | |||||
batch_size = check_int_positive('batch_size', batch_size) | |||||
batchs = int(len_x / batch_size) | |||||
rest = len_x - batchs*batch_size | |||||
res = [] | |||||
for i in range(batchs): | |||||
x_batch = arr_x[i*batch_size: (i + 1)*batch_size] | |||||
y_batch = arr_y[i*batch_size: (i + 1)*batch_size] | |||||
adv_x = self.generate(x_batch, y_batch) | |||||
# Black-attack methods will return 3 values, just get the second. | |||||
res.append(adv_x[1] if isinstance(adv_x, tuple) else adv_x) | |||||
if rest != 0: | |||||
x_batch = arr_x[batchs*batch_size:] | |||||
y_batch = arr_y[batchs*batch_size:] | |||||
adv_x = self.generate(x_batch, y_batch) | |||||
# Black-attack methods will return 3 values, just get the second. | |||||
res.append(adv_x[1] if isinstance(adv_x, tuple) else adv_x) | |||||
adv_x = np.concatenate(res, axis=0) | |||||
return adv_x | |||||
@abstractmethod | |||||
def generate(self, inputs, labels): | |||||
""" | |||||
Generate adversarial examples based on normal samples and their labels. | |||||
Args: | |||||
inputs (numpy.ndarray): Samples based on which adversarial | |||||
examples are generated. | |||||
labels (numpy.ndarray): Labels of samples, whose values determined | |||||
by specific attacks. | |||||
Raises: | |||||
NotImplementedError: It is an abstract method. | |||||
""" | |||||
msg = 'The function generate() is an abstract function in class ' \ | |||||
'`Attack` and should be implemented in child class.' | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) |
@@ -0,0 +1,75 @@ | |||||
# 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. | |||||
""" | |||||
Black model. | |||||
""" | |||||
from abc import abstractmethod | |||||
import numpy as np | |||||
from mindarmour.utils.logger import LogUtil | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'BlackModel' | |||||
class BlackModel: | |||||
""" | |||||
The abstract class which treats the target model as a black box. The model | |||||
should be defined by users. | |||||
""" | |||||
def __init__(self): | |||||
pass | |||||
@abstractmethod | |||||
def predict(self, inputs): | |||||
""" | |||||
Predict using the user specified model. The shape of predict results | |||||
should be (m, n), where n represents the number of classes this model | |||||
classifies. | |||||
Args: | |||||
inputs (numpy.ndarray): The input samples to be predicted. | |||||
Raises: | |||||
NotImplementedError: It is an abstract method. | |||||
""" | |||||
msg = 'The function predict() is an abstract function in class ' \ | |||||
'`BlackModel` and should be implemented in child class by user.' | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) | |||||
def is_adversarial(self, data, label, is_targeted): | |||||
""" | |||||
Check if input sample is adversarial example or not. | |||||
Args: | |||||
data (numpy.ndarray): The input sample to be check, typically some | |||||
maliciously perturbed examples. | |||||
label (numpy.ndarray): For targeted attacks, label is intended | |||||
label of perturbed example. For untargeted attacks, label is | |||||
original label of corresponding unperturbed sample. | |||||
is_targeted (bool): For targeted/untargeted attacks, select True/False. | |||||
Returns: | |||||
bool. | |||||
- If True, the input sample is adversarial. | |||||
- If False, the input sample is not adversarial. | |||||
""" | |||||
logits = self.predict(np.expand_dims(data, axis=0))[0] | |||||
predicts = np.argmax(logits) | |||||
if is_targeted: | |||||
return predicts == label | |||||
return predicts != label |
@@ -0,0 +1,230 @@ | |||||
# 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. | |||||
""" | |||||
Genetic-Attack. | |||||
""" | |||||
import numpy as np | |||||
from scipy.special import softmax | |||||
from mindarmour.attacks.attack import Attack | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
from mindarmour.utils._check_param import check_numpy_param, check_model, \ | |||||
check_pair_numpy_param, check_param_type, check_value_positive, \ | |||||
check_int_positive, check_param_multi_types | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'GeneticAttack' | |||||
def _mutation(cur_pop, step_noise=0.01, prob=0.005): | |||||
""" | |||||
Generate mutation samples in genetic_attack. | |||||
Args: | |||||
cur_pop (numpy.ndarray): Samples before mutation. | |||||
step_noise (float): Noise range. Default: 0.01. | |||||
prob (float): Mutation probability. Default: 0.005. | |||||
Returns: | |||||
numpy.ndarray, samples after mutation operation in genetic_attack. | |||||
Examples: | |||||
>>> mul_pop = self._mutation_op([0.2, 0.3, 0.4], step_noise=0.03, | |||||
>>> prob=0.01) | |||||
""" | |||||
cur_pop = check_numpy_param('cur_pop', cur_pop) | |||||
perturb_noise = np.clip(np.random.random(cur_pop.shape) - 0.5, | |||||
-step_noise, step_noise) | |||||
mutated_pop = perturb_noise*( | |||||
np.random.random(cur_pop.shape) < prob) + cur_pop | |||||
return mutated_pop | |||||
class GeneticAttack(Attack): | |||||
""" | |||||
The Genetic Attack represents the black-box attack based on the genetic algorithm, | |||||
which belongs to differential evolution algorithms. | |||||
This attack was proposed by Moustafa Alzantot et al. (2018). | |||||
References: `Moustafa Alzantot, Yash Sharma, Supriyo Chakraborty, | |||||
"GeneticAttack: Practical Black-box Attacks with | |||||
Gradient-FreeOptimization" <https://arxiv.org/abs/1805.11090>`_ | |||||
Args: | |||||
model (BlackModel): Target model. | |||||
pop_size (int): The number of particles, which should be greater than | |||||
zero. Default: 6. | |||||
mutation_rate (float): The probability of mutations. Default: 0.005. | |||||
per_bounds (float): Maximum L_inf distance. | |||||
max_steps (int): The maximum round of iteration for each adversarial | |||||
example. Default: 1000. | |||||
step_size (float): Attack step size. Default: 0.2. | |||||
temp (float): Sampling temperature for selection. Default: 0.3. | |||||
bounds (tuple): Upper and lower bounds of data. In form of (clip_min, | |||||
clip_max). Default: (0, 1.0) | |||||
adaptive (bool): If True, turns on dynamic scaling of mutation | |||||
parameters. If false, turns on static mutation parameters. | |||||
Default: False. | |||||
sparse (bool): If True, input labels are sparse-encoded. If False, | |||||
input labels are one-hot-encoded. Default: True. | |||||
Examples: | |||||
>>> attack = GeneticAttack(model) | |||||
""" | |||||
def __init__(self, model, pop_size=6, | |||||
mutation_rate=0.005, per_bounds=0.15, max_steps=1000, | |||||
step_size=0.20, temp=0.3, bounds=(0, 1.0), adaptive=False, | |||||
sparse=True): | |||||
super(GeneticAttack, self).__init__() | |||||
self._model = check_model('model', model, BlackModel) | |||||
self._per_bounds = check_value_positive('per_bounds', per_bounds) | |||||
self._pop_size = check_int_positive('pop_size', pop_size) | |||||
self._step_size = check_value_positive('step_size', step_size) | |||||
self._temp = check_value_positive('temp', temp) | |||||
self._max_steps = check_int_positive('max_steps', max_steps) | |||||
self._mutation_rate = check_value_positive('mutation_rate', | |||||
mutation_rate) | |||||
self._adaptive = check_param_type('adaptive', adaptive, bool) | |||||
self._bounds = check_param_multi_types('bounds', bounds, [list, tuple]) | |||||
for b in self._bounds: | |||||
_ = check_param_multi_types('bound', b, [int, float]) | |||||
# initial global optimum fitness value | |||||
self._best_fit = -1 | |||||
# count times of no progress | |||||
self._plateau_times = 0 | |||||
# count times of changing attack step | |||||
self._adap_times = 0 | |||||
self._sparse = check_param_type('sparse', sparse, bool) | |||||
def generate(self, inputs, labels): | |||||
""" | |||||
Generate adversarial examples based on input data and targeted | |||||
labels (or ground_truth labels). | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
labels (numpy.ndarray): Targeted labels. | |||||
Returns: | |||||
- numpy.ndarray, bool values for each attack result. | |||||
- numpy.ndarray, generated adversarial examples. | |||||
- numpy.ndarray, query times for each sample. | |||||
Examples: | |||||
>>> advs = attack.generate([[0.2, 0.3, 0.4], | |||||
>>> [0.3, 0.3, 0.2]], | |||||
>>> [1, 2]) | |||||
""" | |||||
inputs, labels = check_pair_numpy_param('inputs', inputs, | |||||
'labels', labels) | |||||
# if input is one-hot encoded, get sparse format value | |||||
if not self._sparse: | |||||
if labels.ndim != 2: | |||||
raise ValueError('labels must be 2 dims, ' | |||||
'but got {} dims.'.format(labels.ndim)) | |||||
labels = np.argmax(labels, axis=1) | |||||
adv_list = [] | |||||
success_list = [] | |||||
query_times_list = [] | |||||
for i in range(inputs.shape[0]): | |||||
is_success = False | |||||
target_label = labels[i] | |||||
iters = 0 | |||||
x_ori = inputs[i] | |||||
# generate particles | |||||
ori_copies = np.repeat( | |||||
x_ori[np.newaxis, :], self._pop_size, axis=0) | |||||
# initial perturbations | |||||
cur_pert = np.clip(np.random.random(ori_copies.shape)*self._step_size, | |||||
(0 - self._per_bounds), | |||||
self._per_bounds) | |||||
query_times = 0 | |||||
while iters < self._max_steps: | |||||
iters += 1 | |||||
cur_pop = np.clip( | |||||
ori_copies + cur_pert, self._bounds[0], self._bounds[1]) | |||||
pop_preds = self._model.predict(cur_pop) | |||||
query_times += cur_pop.shape[0] | |||||
all_preds = np.argmax(pop_preds, axis=1) | |||||
success_pop = np.equal(target_label, all_preds).astype(np.int32) | |||||
success = max(success_pop) | |||||
if success == 1: | |||||
is_success = True | |||||
adv = cur_pop[np.argmax(success_pop)] | |||||
break | |||||
target_preds = pop_preds[:, target_label] | |||||
others_preds_sum = np.sum(pop_preds, axis=1) - target_preds | |||||
fit_vals = target_preds - others_preds_sum | |||||
best_fit = max(target_preds - np.max(pop_preds)) | |||||
if best_fit > self._best_fit: | |||||
self._best_fit = best_fit | |||||
self._plateau_times = 0 | |||||
else: | |||||
self._plateau_times += 1 | |||||
adap_threshold = (lambda z: 100 if z > -0.4 else 300)(best_fit) | |||||
if self._plateau_times > adap_threshold: | |||||
self._adap_times += 1 | |||||
self._plateau_times = 0 | |||||
if self._adaptive: | |||||
step_noise = max(self._step_size, 0.4*(0.9**self._adap_times)) | |||||
step_p = max(self._step_size, 0.5*(0.9**self._adap_times)) | |||||
else: | |||||
step_noise = self._step_size | |||||
step_p = self._mutation_rate | |||||
step_temp = self._temp | |||||
elite = cur_pert[np.argmax(fit_vals)] | |||||
select_probs = softmax(fit_vals/step_temp) | |||||
select_args = np.arange(self._pop_size) | |||||
parents_arg = np.random.choice( | |||||
a=select_args, size=2*(self._pop_size - 1), | |||||
replace=True, p=select_probs) | |||||
parent1 = cur_pert[parents_arg[:self._pop_size - 1]] | |||||
parent2 = cur_pert[parents_arg[self._pop_size - 1:]] | |||||
parent1_probs = select_probs[parents_arg[:self._pop_size - 1]] | |||||
parent2_probs = select_probs[parents_arg[self._pop_size - 1:]] | |||||
parent2_probs = parent2_probs / (parent1_probs + parent2_probs) | |||||
# duplicate the probabilities to all features of each particle. | |||||
dims = len(x_ori.shape) | |||||
for _ in range(dims): | |||||
parent2_probs = parent2_probs[:, np.newaxis] | |||||
parent2_probs = np.tile(parent2_probs, ((1,) + x_ori.shape)) | |||||
cross_probs = (np.random.random(parent1.shape) > | |||||
parent2_probs).astype(np.int32) | |||||
childs = parent1*cross_probs + parent2*(1 - cross_probs) | |||||
mutated_childs = _mutation( | |||||
childs, step_noise=self._per_bounds*step_noise, | |||||
prob=step_p) | |||||
cur_pert = np.concatenate((mutated_childs, elite[np.newaxis, :])) | |||||
if is_success: | |||||
LOGGER.debug(TAG, 'successfully find one adversarial sample ' | |||||
'and start Reduction process.') | |||||
adv_list.append(adv) | |||||
else: | |||||
LOGGER.debug(TAG, 'fail to find adversarial sample.') | |||||
adv_list.append(elite + x_ori) | |||||
LOGGER.debug(TAG, | |||||
'iteration times is: %d and query times is: %d', | |||||
iters, | |||||
query_times) | |||||
success_list.append(is_success) | |||||
query_times_list.append(query_times) | |||||
del ori_copies, cur_pert, cur_pop | |||||
return np.asarray(success_list), \ | |||||
np.asarray(adv_list), \ | |||||
np.asarray(query_times_list) |
@@ -0,0 +1,510 @@ | |||||
# 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. | |||||
""" | |||||
Hop-skip-jump attack. | |||||
""" | |||||
import numpy as np | |||||
from mindarmour.attacks.attack import Attack | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
from mindarmour.utils._check_param import check_pair_numpy_param, check_model, \ | |||||
check_numpy_param, check_int_positive, check_value_positive, \ | |||||
check_value_non_negative, check_param_type | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'HopSkipJumpAttack' | |||||
def _clip_image(image, clip_min, clip_max): | |||||
""" | |||||
Clip an image, or an image batch, with upper and lower threshold. | |||||
""" | |||||
return np.clip(image, clip_min, clip_max) | |||||
class HopSkipJumpAttack(Attack): | |||||
""" | |||||
HopSkipJumpAttack proposed by Chen, Jordan and Wainwright is a | |||||
decision-based attack. The attack requires access to output labels of | |||||
target model. | |||||
References: `Chen J, Michael I. Jordan, Martin J. Wainwright. | |||||
HopSkipJumpAttack: A Query-Efficient Decision-Based Attack. 2019. | |||||
arXiv:1904.02144 <https://arxiv.org/abs/1904.02144>`_ | |||||
Args: | |||||
model (BlackModel): Target model. | |||||
init_num_evals (int): The initial number of evaluations for gradient | |||||
estimation. Default: 100. | |||||
max_num_evals (int): The maximum number of evaluations for gradient | |||||
estimation. Default: 1000. | |||||
stepsize_search (str): Indicating how to search for stepsize; Possible | |||||
values are 'geometric_progression', 'grid_search', 'geometric_progression'. | |||||
num_iterations (int): The number of iterations. Default: 64. | |||||
gamma (float): Used to set binary search threshold theta. Default: 1.0. | |||||
For l2 attack the binary search threshold `theta` is: | |||||
math:`gamma / d^{3/2}`. For linf attack is math:`gamma / d^2`. | |||||
constraint (str): The norm distance to optimize. Possible values are 'l2', | |||||
'linf'. Default: l2. | |||||
batch_size (int): Batch size. Default: 32. | |||||
clip_min (float, optional): The minimum image component value. | |||||
Default: 0. | |||||
clip_max (float, optional): The maximum image component value. | |||||
Default: 1. | |||||
sparse (bool): If True, input labels are sparse-encoded. If False, | |||||
input labels are one-hot-encoded. Default: True. | |||||
Raises: | |||||
ValueError: If stepsize_search not in ['geometric_progression', | |||||
'grid_search'] | |||||
ValueError: If constraint not in ['l2', 'linf'] | |||||
Examples: | |||||
>>> x_test = np.asarray(np.random.random((sample_num, | |||||
>>> sample_length)), np.float32) | |||||
>>> y_test = np.random.randint(0, class_num, size=sample_num) | |||||
>>> instance = HopSkipJumpAttack(user_model) | |||||
>>> adv_x = instance.generate(x_test, y_test) | |||||
""" | |||||
def __init__(self, model, init_num_evals=100, max_num_evals=1000, | |||||
stepsize_search='geometric_progression', num_iterations=20, | |||||
gamma=1.0, constraint='l2', batch_size=32, clip_min=0.0, | |||||
clip_max=1.0, sparse=True): | |||||
super(HopSkipJumpAttack, self).__init__() | |||||
self._model = check_model('model', model, BlackModel) | |||||
self._init_num_evals = check_int_positive('initial_num_evals', | |||||
init_num_evals) | |||||
self._max_num_evals = check_int_positive('max_num_evals', max_num_evals) | |||||
self._batch_size = check_int_positive('batch_size', batch_size) | |||||
self._clip_min = check_value_non_negative('clip_min', clip_min) | |||||
self._clip_max = check_value_non_negative('clip_max', clip_max) | |||||
self._sparse = check_param_type('sparse', sparse, bool) | |||||
self._np_dtype = np.dtype('float32') | |||||
if stepsize_search in ['geometric_progression', 'grid_search']: | |||||
self._stepsize_search = stepsize_search | |||||
else: | |||||
msg = "stepsize_search must be in ['geometric_progression'," \ | |||||
" 'grid_search'], but got {}".format(stepsize_search) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
self._num_iterations = check_int_positive('num_iterations', | |||||
num_iterations) | |||||
self._gamma = check_value_positive('gamma', gamma) | |||||
if constraint in ['l2', 'linf']: | |||||
self._constraint = constraint | |||||
else: | |||||
msg = "constraint must be in ['l2', 'linf'], " \ | |||||
"but got {}".format(constraint) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
self.queries = 0 | |||||
self.is_adv = True | |||||
self.y_targets = None | |||||
self.image_targets = None | |||||
self.y_target = None | |||||
self.image_target = None | |||||
def _generate_one(self, sample): | |||||
""" | |||||
Return a tensor that constructs adversarial examples for the given | |||||
input. | |||||
Args: | |||||
sample (Tensor): Input samples. | |||||
Returns: | |||||
Tensor, generated adversarial examples. | |||||
""" | |||||
shape = list(np.shape(sample)) | |||||
dim = int(np.prod(shape)) | |||||
# Set binary search threshold. | |||||
if self._constraint == 'l2': | |||||
theta = self._gamma / (np.sqrt(dim)*dim) | |||||
else: | |||||
theta = self._gamma / (dim*dim) | |||||
wrap = self._hsja(sample, self.y_target, self.image_target, dim, theta) | |||||
if wrap is None: | |||||
self.is_adv = False | |||||
else: | |||||
self.is_adv = True | |||||
return self.is_adv, wrap, self.queries | |||||
def set_target_images(self, target_images): | |||||
""" | |||||
Setting target images for target attack. | |||||
Args: | |||||
target_images (numpy.ndarray): Target images. | |||||
""" | |||||
self.image_targets = check_numpy_param('target_images', target_images) | |||||
def generate(self, inputs, labels): | |||||
""" | |||||
Generate adversarial images in a for loop. | |||||
Args: | |||||
inputs (numpy.ndarray): Origin images. | |||||
labels (numpy.ndarray): Target labels. | |||||
Returns: | |||||
- numpy.ndarray, bool values for each attack result. | |||||
- numpy.ndarray, generated adversarial examples. | |||||
- numpy.ndarray, query times for each sample. | |||||
Examples: | |||||
>>> generate([[0.1,0.2,0.2],[0.2,0.3,0.4]],[2,6]) | |||||
""" | |||||
if labels is not None: | |||||
inputs, labels = check_pair_numpy_param('inputs', inputs, | |||||
'labels', labels) | |||||
if not self._sparse: | |||||
labels = np.argmax(labels, axis=1) | |||||
x_adv = [] | |||||
is_advs = [] | |||||
queries_times = [] | |||||
if labels is not None: | |||||
self.y_targets = labels | |||||
for i, x_single in enumerate(inputs): | |||||
self.queries = 0 | |||||
if self.image_targets is not None: | |||||
self.image_target = self.image_targets[i] | |||||
if self.y_targets is not None: | |||||
self.y_target = self.y_targets[i] | |||||
is_adv, adv_img, query_time = self._generate_one(x_single) | |||||
x_adv.append(adv_img) | |||||
is_advs.append(is_adv) | |||||
queries_times.append(query_time) | |||||
return np.asarray(is_advs), \ | |||||
np.asarray(x_adv), \ | |||||
np.asarray(queries_times) | |||||
def _hsja(self, sample, target_label, target_image, dim, theta): | |||||
""" | |||||
The main algorithm for HopSkipJumpAttack. | |||||
Args: | |||||
sample (numpy.ndarray): Input image. Without the batchsize | |||||
dimension. | |||||
target_label (int): Integer for targeted attack, None for | |||||
nontargeted attack. Without the batchsize dimension. | |||||
target_image (numpy.ndarray): An array with the same size as | |||||
input sample, or None. Without the batchsize dimension. | |||||
Returns: | |||||
numpy.ndarray, perturbed images. | |||||
""" | |||||
original_label = None | |||||
# Original label for untargeted attack. | |||||
if target_label is None: | |||||
original_label = self._model.predict(sample) | |||||
original_label = np.argmax(original_label) | |||||
# Initialize perturbed image. | |||||
# untarget attack | |||||
if target_image is None: | |||||
perturbed = self._initialize(sample, original_label, target_label) | |||||
if perturbed is None: | |||||
msg = 'Can not find an initial adversarial example' | |||||
LOGGER.info(TAG, msg) | |||||
return perturbed | |||||
else: | |||||
# Target attack | |||||
perturbed = target_image | |||||
# Project the initial perturbed image to the decision boundary. | |||||
perturbed, dist_post_update = self._binary_search_batch(sample, | |||||
np.expand_dims(perturbed, 0), | |||||
original_label, | |||||
target_label, | |||||
theta) | |||||
# Calculate the distance of perturbed image and original sample | |||||
dist = self._compute_distance(perturbed, sample) | |||||
for j in np.arange(self._num_iterations): | |||||
current_iteration = j + 1 | |||||
# Select delta. | |||||
delta = self._select_delta(dist_post_update, current_iteration, dim, | |||||
theta) | |||||
# Choose number of evaluations. | |||||
num_evals = int(min([self._init_num_evals*np.sqrt(j + 1), | |||||
self._max_num_evals])) | |||||
# approximate gradient. | |||||
gradf = self._approximate_gradient(perturbed, num_evals, | |||||
original_label, target_label, | |||||
delta, theta) | |||||
if self._constraint == 'linf': | |||||
update = np.sign(gradf) | |||||
else: | |||||
update = gradf | |||||
# search step size. | |||||
if self._stepsize_search == 'geometric_progression': | |||||
# find step size. | |||||
epsilon = self._geometric_progression_for_stepsize( | |||||
perturbed, | |||||
update, | |||||
dist, | |||||
current_iteration, | |||||
original_label, | |||||
target_label) | |||||
# Update the sample. | |||||
perturbed = _clip_image(perturbed + epsilon*update, | |||||
self._clip_min, self._clip_max) | |||||
# Binary search to return to the boundary. | |||||
perturbed, dist_post_update = self._binary_search_batch( | |||||
sample, | |||||
perturbed[None], | |||||
original_label, | |||||
target_label, | |||||
theta) | |||||
elif self._stepsize_search == 'grid_search': | |||||
epsilons = np.logspace(-4, 0, num=20, endpoint=True)*dist | |||||
epsilons_shape = [20] + len(np.shape(sample))*[1] | |||||
perturbeds = perturbed + epsilons.reshape( | |||||
epsilons_shape)*update | |||||
perturbeds = _clip_image(perturbeds, self._clip_min, | |||||
self._clip_max) | |||||
idx_perturbed = self._decision_function(perturbeds, | |||||
original_label, | |||||
target_label) | |||||
if np.sum(idx_perturbed) > 0: | |||||
# Select the perturbation that yields the minimum distance | |||||
# after binary search. | |||||
perturbed, dist_post_update = self._binary_search_batch( | |||||
sample, perturbeds[idx_perturbed], | |||||
original_label, target_label, theta) | |||||
# compute new distance. | |||||
dist = self._compute_distance(perturbed, sample) | |||||
LOGGER.debug(TAG, | |||||
'iteration: %d, %s distance %4f', | |||||
j + 1, | |||||
self._constraint, dist) | |||||
perturbed = np.expand_dims(perturbed, 0) | |||||
return perturbed | |||||
def _decision_function(self, images, original_label, target_label): | |||||
""" | |||||
Decision function returns 1 if the input sample is on the desired | |||||
side of the boundary, and 0 otherwise. | |||||
""" | |||||
images = _clip_image(images, self._clip_min, self._clip_max) | |||||
prob = [] | |||||
self.queries += len(images) | |||||
for i in range(0, len(images), self._batch_size): | |||||
batch = images[i:i + self._batch_size] | |||||
length = len(batch) | |||||
prob_i = self._model.predict(batch)[:length] | |||||
prob.append(prob_i) | |||||
prob = np.concatenate(prob) | |||||
if target_label is None: | |||||
res = np.argmax(prob, axis=1) != original_label | |||||
else: | |||||
res = np.argmax(prob, axis=1) == target_label | |||||
return res | |||||
def _compute_distance(self, original_img, perturbation_img): | |||||
""" | |||||
Compute the distance between original image and perturbation images. | |||||
""" | |||||
if self._constraint == 'l2': | |||||
distance = np.linalg.norm(original_img - perturbation_img) | |||||
else: | |||||
distance = np.max(abs(original_img - perturbation_img)) | |||||
return distance | |||||
def _approximate_gradient(self, sample, num_evals, original_label, | |||||
target_label, delta, theta): | |||||
""" | |||||
Gradient direction estimation. | |||||
""" | |||||
# Generate random noise based on constraint. | |||||
noise_shape = [num_evals] + list(np.shape(sample)) | |||||
if self._constraint == 'l2': | |||||
random_noise = np.random.randn(*noise_shape) | |||||
else: | |||||
random_noise = np.random.uniform(low=-1, high=1, size=noise_shape) | |||||
axis = tuple(range(1, 1 + len(np.shape(sample)))) | |||||
random_noise = random_noise / np.sqrt( | |||||
np.sum(random_noise**2, axis=axis, keepdims=True)) | |||||
# perturbed images | |||||
perturbed = sample + delta*random_noise | |||||
perturbed = _clip_image(perturbed, self._clip_min, self._clip_max) | |||||
random_noise = (perturbed - sample) / theta | |||||
# Whether the perturbed images are on the desired side of the boundary. | |||||
decisions = self._decision_function(perturbed, original_label, | |||||
target_label) | |||||
decision_shape = [len(decisions)] + [1]*len(np.shape(sample)) | |||||
# transform decisions value from 1, 0 to 1, -2 | |||||
re_decision = 2*np.array(decisions).astype(self._np_dtype).reshape( | |||||
decision_shape) - 1.0 | |||||
if np.mean(re_decision) == 1.0: | |||||
grad_direction = np.mean(random_noise, axis=0) | |||||
elif np.mean(re_decision) == -1.0: | |||||
grad_direction = - np.mean(random_noise, axis=0) | |||||
else: | |||||
re_decision = re_decision - np.mean(re_decision) | |||||
grad_direction = np.mean(re_decision*random_noise, axis=0) | |||||
# The gradient direction. | |||||
grad_direction = grad_direction / (np.linalg.norm(grad_direction) + 1e-10) | |||||
return grad_direction | |||||
def _project(self, original_image, perturbed_images, alphas): | |||||
""" | |||||
Projection input samples onto given l2 or linf balls. | |||||
""" | |||||
alphas_shape = [len(alphas)] + [1]*len(np.shape(original_image)) | |||||
alphas = alphas.reshape(alphas_shape) | |||||
if self._constraint == 'l2': | |||||
projected = (1 - alphas)*original_image + alphas*perturbed_images | |||||
else: | |||||
projected = _clip_image(perturbed_images, original_image - alphas, | |||||
original_image + alphas) | |||||
return projected | |||||
def _binary_search_batch(self, original_image, perturbed_images, | |||||
original_label, target_label, theta): | |||||
""" | |||||
Binary search to approach the model decision boundary. | |||||
""" | |||||
# Compute distance between perturbed image and original image. | |||||
dists_post_update = np.array([self._compute_distance(original_image, | |||||
perturbed_image,) | |||||
for perturbed_image in perturbed_images]) | |||||
# Get higher thresholds | |||||
if self._constraint == 'l2': | |||||
highs = np.ones(len(perturbed_images)) | |||||
thresholds = theta | |||||
else: | |||||
highs = dists_post_update | |||||
thresholds = np.minimum(dists_post_update*theta, theta) | |||||
# Get lower thresholds | |||||
lows = np.zeros(len(perturbed_images)) | |||||
# Update thresholds. | |||||
while np.max((highs - lows) / thresholds) > 1: | |||||
mids = (highs + lows) / 2.0 | |||||
mid_images = self._project(original_image, perturbed_images, mids) | |||||
decisions = self._decision_function(mid_images, original_label, | |||||
target_label) | |||||
lows = np.where(decisions == [0], mids, lows) | |||||
highs = np.where(decisions == [1], mids, highs) | |||||
out_images = self._project(original_image, perturbed_images, highs) | |||||
# Select the best choice based on the distance of the output image. | |||||
dists = np.array( | |||||
[self._compute_distance(original_image, out_image) for out_image in | |||||
out_images]) | |||||
idx = np.argmin(dists) | |||||
dist = dists_post_update[idx] | |||||
out_image = out_images[idx] | |||||
return out_image, dist | |||||
def _initialize(self, sample, original_label, target_label): | |||||
""" | |||||
Implementation of BlendedUniformNoiseAttack | |||||
""" | |||||
num_evals = 0 | |||||
while True: | |||||
random_noise = np.random.uniform(self._clip_min, self._clip_max, | |||||
size=np.shape(sample)) | |||||
success = self._decision_function(random_noise[None], | |||||
original_label, | |||||
target_label) | |||||
if success: | |||||
break | |||||
num_evals += 1 | |||||
if num_evals > 1e3: | |||||
return None | |||||
# Binary search. | |||||
low = 0.0 | |||||
high = 1.0 | |||||
while high - low > 0.001: | |||||
mid = (high + low) / 2.0 | |||||
blended = (1 - mid)*sample + mid*random_noise | |||||
success = self._decision_function(blended[None], original_label, | |||||
target_label) | |||||
if success: | |||||
high = mid | |||||
else: | |||||
low = mid | |||||
initialization = (1 - high)*sample + high*random_noise | |||||
return initialization | |||||
def _geometric_progression_for_stepsize(self, perturbed, update, dist, | |||||
current_iteration, original_label, | |||||
target_label): | |||||
""" | |||||
Search for stepsize in the way of Geometric progression. | |||||
Keep decreasing stepsize by half until reaching the desired side of | |||||
the decision boundary. | |||||
""" | |||||
epsilon = dist / np.sqrt(current_iteration) | |||||
while True: | |||||
updated = perturbed + epsilon*update | |||||
success = self._decision_function(updated, original_label, | |||||
target_label) | |||||
if success: | |||||
break | |||||
epsilon = epsilon / 2.0 | |||||
return epsilon | |||||
def _select_delta(self, dist_post_update, current_iteration, dim, theta): | |||||
""" | |||||
Choose the delta based on the distance between the input sample | |||||
and the perturbed sample. | |||||
""" | |||||
if current_iteration == 1: | |||||
delta = 0.1*(self._clip_max - self._clip_min) | |||||
else: | |||||
if self._constraint == 'l2': | |||||
delta = np.sqrt(dim)*theta*dist_post_update | |||||
else: | |||||
delta = dim*theta*dist_post_update | |||||
return delta |
@@ -0,0 +1,432 @@ | |||||
# 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. | |||||
""" | |||||
Natural-evolutionary-strategy Attack. | |||||
""" | |||||
import time | |||||
import numpy as np | |||||
from scipy.special import softmax | |||||
from mindarmour.attacks.attack import Attack | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
from mindarmour.utils._check_param import check_pair_numpy_param, check_model, \ | |||||
check_numpy_param, check_int_positive, check_value_positive, check_param_type | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'NES' | |||||
def _one_hot(index, total): | |||||
arr = np.zeros((total)) | |||||
arr[index] = 1.0 | |||||
return arr | |||||
def _bound(image, epislon): | |||||
lower = np.clip(image - epislon, 0, 1) | |||||
upper = np.clip(image + epislon, 0, 1) | |||||
return lower, upper | |||||
class NES(Attack): | |||||
""" | |||||
The class is an implementation of the Natural Evolutionary Strategies Attack, | |||||
including three settings: Query-Limited setting, Partial-Information setting | |||||
and Label-Only setting. | |||||
References: `Andrew Ilyas, Logan Engstrom, Anish Athalye, and Jessy Lin. | |||||
Black-box adversarial attacks with limited queries and information. In | |||||
ICML, July 2018 <https://arxiv.org/abs/1804.08598>`_ | |||||
Args: | |||||
model (BlackModel): Target model. | |||||
scene (str): Scene in 'Label_Only', 'Partial_Info' or | |||||
'Query_Limit'. | |||||
max_queries (int): Maximum query numbers to generate an adversarial | |||||
example. Default: 500000. | |||||
top_k (int): For Partial-Info or Label-Only setting, indicating how | |||||
much (Top-k) information is available for the attacker. For | |||||
Query-Limited setting, this input should be set as -1. Default: -1. | |||||
num_class (int): Number of classes in dataset. Default: 10. | |||||
batch_size (int): Batch size. Default: 96. | |||||
epsilon (float): Maximum perturbation allowed in attack. Default: 0.3. | |||||
samples_per_draw (int): Number of samples draw in antithetic sampling. | |||||
Default: 96. | |||||
momentum (float): Momentum. Default: 0.9. | |||||
learning_rate (float): Learning rate. Default: 1e-2. | |||||
max_lr (float): Max Learning rate. Default: 1e-2. | |||||
min_lr (float): Min Learning rate. Default: 5e-5. | |||||
sigma (float): Step size of random noise. Default: 1e-3. | |||||
plateau_length (int): Length of plateau used in Annealing algorithm. | |||||
Default: 20. | |||||
plateau_drop (float): Drop of plateau used in Annealing algorithm. | |||||
Default: 2.0. | |||||
adv_thresh (float): Threshold of adversarial. Default: 0.15. | |||||
zero_iters (int): Number of points to use for the proxy score. | |||||
Default: 10. | |||||
starting_eps (float): Starting epsilon used in Label-Only setting. | |||||
Default: 1.0. | |||||
starting_delta_eps (float): Delta epsilon used in Label-Only setting. | |||||
Default: 0.5. | |||||
label_only_sigma (float): Sigma used in Label-Only setting. | |||||
Default: 1e-3. | |||||
conservative (int): Conservation used in epsilon decay, it will | |||||
increase if no convergence. Default: 2. | |||||
sparse (bool): If True, input labels are sparse-encoded. If False, | |||||
input labels are one-hot-encoded. Default: True. | |||||
Examples: | |||||
>>> SCENE = 'Label_Only' | |||||
>>> TOP_K = 5 | |||||
>>> num_class = 5 | |||||
>>> nes_instance = NES(user_model, SCENE, top_k=TOP_K) | |||||
>>> initial_img = np.asarray(np.random.random((32, 32)), np.float32) | |||||
>>> target_image = np.asarray(np.random.random((32, 32)), np.float32) | |||||
>>> orig_class = 0 | |||||
>>> target_class = 2 | |||||
>>> nes_instance.set_target_images(target_image) | |||||
>>> tag, adv, queries = nes_instance.generate([initial_img], [target_class]) | |||||
""" | |||||
def __init__(self, model, scene, max_queries=10000, top_k=-1, num_class=10, | |||||
batch_size=128, epsilon=0.3, samples_per_draw=128, | |||||
momentum=0.9, learning_rate=1e-3, max_lr=5e-2, min_lr=5e-4, | |||||
sigma=1e-3, plateau_length=20, plateau_drop=2.0, | |||||
adv_thresh=0.25, zero_iters=10, starting_eps=1.0, | |||||
starting_delta_eps=0.5, label_only_sigma=1e-3, conservative=2, | |||||
sparse=True): | |||||
super(NES, self).__init__() | |||||
self._model = check_model('model', model, BlackModel) | |||||
self._scene = scene | |||||
self._max_queries = check_int_positive('max_queries', max_queries) | |||||
self._num_class = check_int_positive('num_class', num_class) | |||||
self._batch_size = check_int_positive('batch_size', batch_size) | |||||
self._samples_per_draw = check_int_positive('samples_per_draw', | |||||
samples_per_draw) | |||||
self._goal_epsilon = check_value_positive('epsilon', epsilon) | |||||
self._momentum = check_value_positive('momentum', momentum) | |||||
self._learning_rate = check_value_positive('learning_rate', | |||||
learning_rate) | |||||
self._max_lr = check_value_positive('max_lr', max_lr) | |||||
self._min_lr = check_value_positive('min_lr', min_lr) | |||||
self._sigma = check_value_positive('sigma', sigma) | |||||
self._plateau_length = check_int_positive('plateau_length', | |||||
plateau_length) | |||||
self._plateau_drop = check_value_positive('plateau_drop', plateau_drop) | |||||
# partial information arguments | |||||
self._k = top_k | |||||
self._adv_thresh = check_value_positive('adv_thresh', adv_thresh) | |||||
# label only arguments | |||||
self._zero_iters = check_int_positive('zero_iters', zero_iters) | |||||
self._starting_eps = check_value_positive('starting_eps', starting_eps) | |||||
self._starting_delta_eps = check_value_positive('starting_delta_eps', | |||||
starting_delta_eps) | |||||
self._label_only_sigma = check_value_positive('label_only_sigma', | |||||
label_only_sigma) | |||||
self._conservative = check_int_positive('conservative', conservative) | |||||
self._sparse = check_param_type('sparse', sparse, bool) | |||||
self.target_imgs = None | |||||
self.target_img = None | |||||
self.target_class = None | |||||
def generate(self, inputs, labels): | |||||
""" | |||||
Main algorithm for NES. | |||||
Args: | |||||
inputs (numpy.ndarray): Benign input samples. | |||||
labels (numpy.ndarray): Target labels. | |||||
Returns: | |||||
- numpy.ndarray, bool values for each attack result. | |||||
- numpy.ndarray, generated adversarial examples. | |||||
- numpy.ndarray, query times for each sample. | |||||
Raises: | |||||
ValueError: If the top_k less than 0 in Label-Only or Partial-Info | |||||
setting. | |||||
ValueError: If the target_imgs is None in Label-Only or | |||||
Partial-Info setting. | |||||
ValueError: If scene is not in ['Label_Only', 'Partial_Info', | |||||
'Query_Limit'] | |||||
Examples: | |||||
>>> advs = attack.generate([[0.2, 0.3, 0.4], [0.3, 0.3, 0.2]], | |||||
>>> [1, 2]) | |||||
""" | |||||
inputs, labels = check_pair_numpy_param('inputs', inputs, | |||||
'labels', labels) | |||||
if not self._sparse: | |||||
labels = np.argmax(labels, axis=1) | |||||
if self._scene == 'Label_Only' or self._scene == 'Partial_Info': | |||||
if self._k < 0: | |||||
msg = "In 'Label_Only' or 'Partial_Info' mode, " \ | |||||
"'top_k' must more than 0." | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
if self.target_imgs is None: | |||||
msg = "In 'Label_Only' or 'Partial_Info' mode, " \ | |||||
"'target_imgs' must be set." | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
elif self._scene == 'Query_Limit': | |||||
self._k = self._num_class | |||||
else: | |||||
msg = "scene must be string in 'Label_Only', " \ | |||||
"'Partial_Info' or 'Query_Limit' " | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
is_advs = [] | |||||
advs = [] | |||||
queries = [] | |||||
for sample, label, target_img in zip(inputs, labels, self.target_imgs): | |||||
is_adv, adv, query = self._generate_one(sample, label, target_img) | |||||
is_advs.append(is_adv) | |||||
advs.append(adv) | |||||
queries.append(query) | |||||
return is_advs, advs, queries | |||||
def set_target_images(self, target_images): | |||||
""" | |||||
Set target samples for target attack. | |||||
Args: | |||||
target_images (numpy.ndarray): Target samples for target attack. | |||||
""" | |||||
self.target_imgs = check_numpy_param('target_images', target_images) | |||||
def _generate_one(self, origin_image, target_label, target_image): | |||||
""" | |||||
Main algorithm for NES. | |||||
Args: | |||||
origin_image (numpy.ndarray): Benign input sample. | |||||
target_label (int): Target label. | |||||
Returns: | |||||
- bool. | |||||
- If True: successfully make an adversarial example. | |||||
- If False: unsuccessfully make an adversarial example. | |||||
- numpy.ndarray, an adversarial example. | |||||
- int, number of queries. | |||||
""" | |||||
self.target_class = target_label | |||||
origin_image = check_numpy_param('origin_image', origin_image) | |||||
self._epsilon = self._starting_eps | |||||
lower, upper = _bound(origin_image, self._epsilon) | |||||
goal_epsilon = self._goal_epsilon | |||||
delta_epsilon = self._starting_delta_eps | |||||
if self._scene == 'Label_Only' or self._scene == 'Partial_Info': | |||||
adv = target_image | |||||
else: | |||||
adv = origin_image.copy() | |||||
# for backtracking and momentum | |||||
num_queries = 0 | |||||
gradient = 0 | |||||
last_ls = [] | |||||
max_iters = int(np.ceil(self._max_queries // self._samples_per_draw)) | |||||
for i in range(max_iters): | |||||
start = time.time() | |||||
# early stop | |||||
eval_preds = self._model.predict(adv) | |||||
eval_preds = np.argmax(eval_preds, axis=1) | |||||
padv = np.equal(eval_preds, self.target_class) | |||||
if padv and self._epsilon <= goal_epsilon: | |||||
LOGGER.debug(TAG, 'early stopping at iteration %d', i) | |||||
return True, adv, num_queries | |||||
# antithetic sampling noise | |||||
noise_pos = np.random.normal( | |||||
size=(self._batch_size // 2,) + origin_image.shape) | |||||
noise = np.concatenate((noise_pos, -noise_pos), axis=0) | |||||
eval_points = adv + self._sigma*noise | |||||
prev_g = gradient | |||||
loss, gradient = self._get_grad(origin_image, eval_points, noise) | |||||
gradient = self._momentum*prev_g + (1.0 - self._momentum)*gradient | |||||
# plateau learning rate annealing | |||||
last_ls.append(loss) | |||||
last_ls = self._plateau_annealing(last_ls) | |||||
# search for learning rate and epsilon decay | |||||
current_lr = self._max_lr | |||||
prop_delta_eps = 0.0 | |||||
if loss < self._adv_thresh and self._epsilon > goal_epsilon: | |||||
prop_delta_eps = delta_epsilon | |||||
while current_lr >= self._min_lr: | |||||
# in partial information only or label only setting | |||||
if self._scene == 'Label_Only' or self._scene == 'Partial_Info': | |||||
proposed_epsilon = max(self._epsilon - prop_delta_eps, | |||||
goal_epsilon) | |||||
lower, upper = _bound(origin_image, proposed_epsilon) | |||||
proposed_adv = adv - current_lr*np.sign(gradient) | |||||
proposed_adv = np.clip(proposed_adv, lower, upper) | |||||
num_queries += 1 | |||||
if self._preds_in_top_k(self.target_class, proposed_adv): | |||||
# The predicted label of proposed adversarial examples is in | |||||
# the top k observations. | |||||
if prop_delta_eps > 0: | |||||
delta_epsilon = max(prop_delta_eps, 0.1) | |||||
last_ls = [] | |||||
adv = proposed_adv | |||||
self._epsilon = max( | |||||
self._epsilon - prop_delta_eps / self._conservative, | |||||
goal_epsilon) | |||||
break | |||||
elif current_lr >= self._min_lr*2: | |||||
current_lr = current_lr / 2 | |||||
LOGGER.debug(TAG, "backtracking learning rate to %.3f", | |||||
current_lr) | |||||
else: | |||||
prop_delta_eps = prop_delta_eps / 2 | |||||
if prop_delta_eps < 2e-3: | |||||
LOGGER.debug(TAG, "Did not converge.") | |||||
return False, adv, num_queries | |||||
current_lr = self._max_lr | |||||
LOGGER.debug(TAG, | |||||
"backtracking epsilon to %.3f", | |||||
self._epsilon - prop_delta_eps) | |||||
# update the number of queries | |||||
if self._scene == 'Label_Only': | |||||
num_queries += self._samples_per_draw*self._zero_iters | |||||
else: | |||||
num_queries += self._samples_per_draw | |||||
LOGGER.debug(TAG, | |||||
'Step %d: loss %.4f, lr %.2E, eps %.3f, time %.4f.', | |||||
i, | |||||
loss, | |||||
current_lr, | |||||
self._epsilon, | |||||
time.time() - start) | |||||
return False, adv, num_queries | |||||
def _plateau_annealing(self, last_loss): | |||||
last_loss = last_loss[-self._plateau_length:] | |||||
if last_loss[-1] > last_loss[0] and len( | |||||
last_loss) == self._plateau_length: | |||||
if self._max_lr > self._min_lr: | |||||
LOGGER.debug(TAG, "Annealing max learning rate.") | |||||
self._max_lr = max(self._max_lr / self._plateau_drop, | |||||
self._min_lr) | |||||
last_loss = [] | |||||
return last_loss | |||||
def _softmax_cross_entropy_with_logit(self, logit): | |||||
logit = softmax(logit, axis=1) | |||||
onehot_label = np.zeros(self._num_class) | |||||
onehot_label[self.target_class] = 1 | |||||
onehot_labels = np.tile(onehot_label, (len(logit), 1)) | |||||
entropy = -onehot_labels*np.log(logit) | |||||
loss = np.mean(entropy, axis=1) | |||||
return loss | |||||
def _query_limit_loss(self, eval_points, noise): | |||||
""" | |||||
Loss in Query-Limit setting. | |||||
""" | |||||
LOGGER.debug(TAG, 'enter the function _query_limit_loss().') | |||||
loss = self._softmax_cross_entropy_with_logit( | |||||
self._model.predict(eval_points)) | |||||
return loss, noise | |||||
def _partial_info_loss(self, eval_points, noise): | |||||
""" | |||||
Loss in Partial-Info setting. | |||||
""" | |||||
LOGGER.debug(TAG, 'enter the function _partial_info_loss.') | |||||
logit = self._model.predict(eval_points) | |||||
loss = np.sort(softmax(logit, axis=1))[:, -self._k:] | |||||
inds = np.argsort(logit)[:, -self._k:] | |||||
good_loss = np.where(np.equal(inds, self.target_class), loss, | |||||
np.zeros(np.shape(inds))) | |||||
good_loss = np.max(good_loss, axis=1) | |||||
losses = -np.log(good_loss) | |||||
return losses, noise | |||||
def _label_only_loss(self, origin_image, eval_points, noise): | |||||
""" | |||||
Loss in Label-Only setting. | |||||
""" | |||||
LOGGER.debug(TAG, 'enter the function _label_only_loss().') | |||||
tiled_points = np.tile(np.expand_dims(eval_points, 0), | |||||
[self._zero_iters, | |||||
*[1]*len(eval_points.shape)]) | |||||
noised_eval_im = tiled_points \ | |||||
+ np.random.randn(self._zero_iters, | |||||
self._batch_size, | |||||
*origin_image.shape) \ | |||||
*self._label_only_sigma | |||||
noised_eval_im = np.reshape(noised_eval_im, ( | |||||
self._zero_iters*self._batch_size, *origin_image.shape)) | |||||
logits = self._model.predict(noised_eval_im) | |||||
inds = np.argsort(logits)[:, -self._k:] | |||||
real_inds = np.reshape(inds, (self._zero_iters, self._batch_size, -1)) | |||||
rank_range = np.arange(1, self._k + 1, 1, dtype=np.float32) | |||||
tiled_rank_range = np.tile(np.reshape(rank_range, (1, 1, self._k)), | |||||
[self._zero_iters, self._batch_size, 1]) | |||||
batches_in = np.where(np.equal(real_inds, self.target_class), | |||||
tiled_rank_range, | |||||
np.zeros(np.shape(tiled_rank_range))) | |||||
loss = 1 - np.mean(batches_in) | |||||
return loss, noise | |||||
def _preds_in_top_k(self, target_class, prop_adv_): | |||||
# query limit setting | |||||
if self._k == self._num_class: | |||||
return True | |||||
# label only and partial information setting | |||||
eval_preds = self._model.predict(prop_adv_) | |||||
if not target_class in eval_preds.argsort()[:, -self._k:]: | |||||
return False | |||||
return True | |||||
def _get_grad(self, origin_image, eval_points, noise): | |||||
"""Calculate gradient.""" | |||||
losses = [] | |||||
grads = [] | |||||
for _ in range(self._samples_per_draw // self._batch_size): | |||||
if self._scene == 'Label_Only': | |||||
loss, np_noise = self._label_only_loss(origin_image, | |||||
eval_points, | |||||
noise) | |||||
elif self._scene == 'Partial_Info': | |||||
loss, np_noise = self._partial_info_loss(eval_points, noise) | |||||
else: | |||||
loss, np_noise = self._query_limit_loss(eval_points, noise) | |||||
# only support three channel images | |||||
losses_tiled = np.tile(np.reshape(loss, (-1, 1, 1, 1)), | |||||
(1,) + origin_image.shape) | |||||
grad = np.mean(losses_tiled*np_noise, axis=0) / self._sigma | |||||
grads.append(grad) | |||||
losses.append(np.mean(loss)) | |||||
return np.array(losses).mean(), np.mean(np.array(grads), axis=0) |
@@ -0,0 +1,326 @@ | |||||
# 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. | |||||
""" | |||||
Pointwise-Attack. | |||||
""" | |||||
import numpy as np | |||||
from mindarmour.attacks.attack import Attack | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
from mindarmour.attacks.black.salt_and_pepper_attack import \ | |||||
SaltAndPepperNoiseAttack | |||||
from mindarmour.utils._check_param import check_model, check_pair_numpy_param, \ | |||||
check_int_positive, check_param_type | |||||
from mindarmour.utils.logger import LogUtil | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'PointWiseAttack' | |||||
class PointWiseAttack(Attack): | |||||
""" | |||||
The Pointwise Attack make sure use the minimum number of changed pixels | |||||
to generate adversarial sample for each original sample.Those changed pixels | |||||
will use binary seach to make sure the distance between adversarial sample | |||||
and original sample is as close as possible. | |||||
References: `L. Schott, J. Rauber, M. Bethge, W. Brendel: "Towards the | |||||
first adversarially robust neural network model on MNIST", ICLR (2019) | |||||
<https://arxiv.org/abs/1805.09190>`_ | |||||
Args: | |||||
model (BlackModel): Target model. | |||||
max_iter (int): Max rounds of iteration to generate adversarial image. | |||||
search_iter (int): Max rounds of binary search. | |||||
is_targeted (bool): If True, targeted attack. If False, untargeted | |||||
attack. Default: False. | |||||
init_attack (Attack): Attack used to find a starting point. Default: | |||||
None. | |||||
sparse (bool): If True, input labels are sparse-encoded. If False, | |||||
input labels are one-hot-encoded. Default: True. | |||||
Examples: | |||||
>>> attack = PointWiseAttack(model) | |||||
""" | |||||
def __init__(self, | |||||
model, | |||||
max_iter=1000, | |||||
search_iter=10, | |||||
is_targeted=False, | |||||
init_attack=None, | |||||
sparse=True): | |||||
super(PointWiseAttack, self).__init__() | |||||
self._model = check_model('model', model, BlackModel) | |||||
self._max_iter = check_int_positive('max_iter', max_iter) | |||||
self._search_iter = check_int_positive('search_iter', search_iter) | |||||
self._is_targeted = check_param_type('is_targeted', is_targeted, bool) | |||||
if init_attack is None: | |||||
self._init_attack = SaltAndPepperNoiseAttack(model, | |||||
is_targeted=self._is_targeted) | |||||
else: | |||||
self._init_attack = init_attack | |||||
self._sparse = check_param_type('sparse', sparse, bool) | |||||
def generate(self, inputs, labels): | |||||
""" | |||||
Generate adversarial examples based on input samples and targeted labels. | |||||
Args: | |||||
inputs (numpy.ndarray): Benign input samples used as references to create | |||||
adversarial examples. | |||||
labels (numpy.ndarray): For targeted attack, labels are adversarial | |||||
target labels. For untargeted attack, labels are ground-truth labels. | |||||
Returns: | |||||
- numpy.ndarray, bool values for each attack result. | |||||
- numpy.ndarray, generated adversarial examples. | |||||
- numpy.ndarray, query times for each sample. | |||||
Examples: | |||||
>>> is_adv_list, adv_list, query_times_each_adv = attack.generate( | |||||
>>> [[0.1, 0.2, 0.6], [0.3, 0, 0.4]], | |||||
>>> [2, 3]) | |||||
""" | |||||
arr_x, arr_y = check_pair_numpy_param('inputs', inputs, 'labels', | |||||
labels) | |||||
if not self._sparse: | |||||
arr_y = np.argmax(arr_y, axis=1) | |||||
ini_bool, ini_advs, ini_count = self._initialize_starting_point(arr_x, | |||||
arr_y) | |||||
is_adv_list = list() | |||||
adv_list = list() | |||||
query_times_each_adv = list() | |||||
for sample, sample_label, start_adv, ite_bool, ite_c in zip(arr_x, | |||||
arr_y, | |||||
ini_advs, | |||||
ini_bool, | |||||
ini_count): | |||||
if ite_bool: | |||||
LOGGER.info(TAG, 'Start optimizing.') | |||||
ori_label = np.argmax( | |||||
self._model.predict(np.expand_dims(sample, axis=0))[0]) | |||||
ini_label = np.argmax(self._model.predict(np.expand_dims(start_adv, axis=0))[0]) | |||||
is_adv, adv_x, query_times = self._decision_optimize(sample, | |||||
sample_label, | |||||
start_adv) | |||||
adv_label = np.argmax( | |||||
self._model.predict(np.expand_dims(adv_x, axis=0))[0]) | |||||
LOGGER.debug(TAG, 'before ini attack label is :{}'.format(ori_label)) | |||||
LOGGER.debug(TAG, 'after ini attack label is :{}'.format(ini_label)) | |||||
LOGGER.debug(TAG, 'INPUT optimize label is :{}'.format(sample_label)) | |||||
LOGGER.debug(TAG, 'after pointwise attack label is :{}'.format(adv_label)) | |||||
is_adv_list.append(is_adv) | |||||
adv_list.append(adv_x) | |||||
query_times_each_adv.append(query_times + ite_c) | |||||
else: | |||||
LOGGER.info(TAG, 'Initial sample is not adversarial, pass.') | |||||
is_adv_list.append(False) | |||||
adv_list.append(start_adv) | |||||
query_times_each_adv.append(ite_c) | |||||
is_adv_list = np.array(is_adv_list) | |||||
adv_list = np.array(adv_list) | |||||
query_times_each_adv = np.array(query_times_each_adv) | |||||
LOGGER.debug(TAG, 'ret list is: {}'.format(adv_list)) | |||||
return is_adv_list, adv_list, query_times_each_adv | |||||
def _decision_optimize(self, unperturbed_img, input_label, perturbed_img): | |||||
""" | |||||
Make the perturbed samples more similar to unperturbed samples, | |||||
while maintaining the perturbed_label. | |||||
Args: | |||||
unperturbed_img (numpy.ndarray): Input sample as reference to create | |||||
adversarial example. | |||||
input_label (numpy.ndarray): Input label. | |||||
perturbed_img (numpy.ndarray): Starting point to optimize. | |||||
Returns: | |||||
numpy.ndarray, a generated adversarial example. | |||||
Raises: | |||||
ValueError: if input unperturbed and perturbed samples have different size. | |||||
""" | |||||
query_count = 0 | |||||
img_size = unperturbed_img.size | |||||
img_shape = unperturbed_img.shape | |||||
perturbed_img = perturbed_img.reshape(-1) | |||||
unperturbed_img = unperturbed_img.reshape(-1) | |||||
recover = np.copy(perturbed_img) | |||||
if unperturbed_img.dtype != perturbed_img.dtype: | |||||
msg = 'unperturbed sample and perturbed sample must have the same' \ | |||||
' dtype, but got dtype of unperturbed is: {}, dtype of perturbed ' \ | |||||
'is: {}'.format(unperturbed_img.dtype, perturbed_img.dtype) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
LOGGER.debug(TAG, 'Before optimize, the mse distance between original ' | |||||
'sample and adversarial sample is: {}' | |||||
.format(self._distance(perturbed_img, unperturbed_img))) | |||||
# recover pixel if image is adversarial | |||||
for _ in range(self._max_iter): | |||||
is_improve = False | |||||
# at the premise of adversarial feature, recover pixels | |||||
pixels_ind = np.arange(img_size) | |||||
mask = unperturbed_img != perturbed_img | |||||
np.random.shuffle(pixels_ind) | |||||
for ite_ind in pixels_ind: | |||||
if mask[ite_ind]: | |||||
recover[ite_ind] = unperturbed_img[ite_ind] | |||||
query_count += 1 | |||||
is_adv = self._model.is_adversarial( | |||||
recover.reshape(img_shape), input_label, self._is_targeted) | |||||
if is_adv: | |||||
is_improve = True | |||||
perturbed_img[ite_ind] = recover[ite_ind] | |||||
break | |||||
else: | |||||
recover[ite_ind] = perturbed_img[ite_ind] | |||||
if not is_improve or (self._distance( | |||||
perturbed_img, unperturbed_img) <= self._get_threthod()): | |||||
break | |||||
LOGGER.debug(TAG, 'first round: Query count {}'.format(query_count)) | |||||
LOGGER.debug(TAG, 'Starting binary searches.') | |||||
# tag the optimized pixels. | |||||
mask = unperturbed_img != perturbed_img | |||||
for _ in range(self._max_iter): | |||||
is_improve = False | |||||
pixels_ind = np.arange(img_size) | |||||
np.random.shuffle(pixels_ind) | |||||
for ite_ind in pixels_ind: | |||||
if not mask[ite_ind]: | |||||
continue | |||||
recover[ite_ind] = unperturbed_img[ite_ind] | |||||
query_count += 1 | |||||
is_adv = self._model.is_adversarial(recover.reshape(img_shape), | |||||
input_label, | |||||
self._is_targeted) | |||||
if is_adv: | |||||
is_improve = True | |||||
mask[ite_ind] = True | |||||
perturbed_img[ite_ind] = recover[ite_ind] | |||||
LOGGER.debug(TAG, | |||||
'Reset {}th pixel value to original, ' | |||||
'mse distance: {}.'.format( | |||||
ite_ind, | |||||
self._distance(perturbed_img, | |||||
unperturbed_img))) | |||||
break | |||||
else: | |||||
# use binary searches | |||||
optimized_value, b_query = self._binary_search( | |||||
perturbed_img, | |||||
unperturbed_img, | |||||
ite_ind, | |||||
input_label, img_shape) | |||||
query_count += b_query | |||||
if optimized_value != perturbed_img[ite_ind]: | |||||
is_improve = True | |||||
mask[ite_ind] = True | |||||
perturbed_img[ite_ind] = optimized_value | |||||
LOGGER.debug(TAG, | |||||
'Reset {}th pixel value to original, ' | |||||
'mse distance: {}.'.format( | |||||
ite_ind, | |||||
self._distance(perturbed_img, | |||||
unperturbed_img))) | |||||
break | |||||
if not is_improve or (self._distance( | |||||
perturbed_img, unperturbed_img) <= self._get_threthod()): | |||||
LOGGER.debug(TAG, 'second optimized finish.') | |||||
break | |||||
LOGGER.info(TAG, 'Optimized finished, query count is {}'.format(query_count)) | |||||
# this method use to optimized the adversarial sample | |||||
return True, perturbed_img.reshape(img_shape), query_count | |||||
def _binary_search(self, perturbed_img, unperturbed_img, ite_ind, | |||||
input_label, img_shape): | |||||
""" | |||||
For original pixel of inputs, use binary search to get the nearest pixel | |||||
value with original value with adversarial feature. | |||||
Args: | |||||
perturbed_img (numpy.ndarray): Adversarial sample. | |||||
unperturbed_img (numpy.ndarray): Input sample. | |||||
ite_ind (int): The index of pixel in inputs. | |||||
input_label (numpy.ndarray): Input labels. | |||||
img_shape (tuple): Shape of the original sample. | |||||
Returns: | |||||
float, adversarial pixel value. | |||||
""" | |||||
query_count = 0 | |||||
adv_value = perturbed_img[ite_ind] | |||||
non_adv_value = unperturbed_img[ite_ind] | |||||
for _ in range(self._search_iter): | |||||
next_value = (adv_value + non_adv_value) / 2 | |||||
recover = np.copy(perturbed_img) | |||||
recover[ite_ind] = next_value | |||||
query_count += 1 | |||||
is_adversarial = self._model.is_adversarial( | |||||
recover.reshape(img_shape), input_label, self._is_targeted) | |||||
if is_adversarial: | |||||
adv_value = next_value | |||||
else: | |||||
non_adv_value = next_value | |||||
return adv_value, query_count | |||||
def _initialize_starting_point(self, inputs, labels): | |||||
""" | |||||
Use init_attack to generate original adversarial inputs. | |||||
Args: | |||||
inputs (numpy.ndarray): Benign input sample used as references to create | |||||
adversarial examples. | |||||
labels (numpy.ndarray): If is targeted attack, labels is adversarial | |||||
labels, if is untargeted attack, labels is true labels. | |||||
Returns: | |||||
numpy.ndarray, adversarial image(s) generate by init_attack method. | |||||
""" | |||||
is_adv, start_adv, query_c = self._init_attack.generate(inputs, labels) | |||||
return is_adv, start_adv, query_c | |||||
def _distance(self, perturbed_img, unperturbed_img): | |||||
""" | |||||
Calculate Mean Squared Error (MSE) to evaluate the optimized process. | |||||
Args: | |||||
perturbed_img (numpy.ndarray): Adversarial sample to be optimized. | |||||
unperturbed_img (numpy.ndarray): As a reference benigh sample. | |||||
Returns: | |||||
float, Calculation of Mean Squared Error (MSE). | |||||
""" | |||||
return np.square(np.subtract(perturbed_img, unperturbed_img)).mean() | |||||
def _get_threthod(self, method='MSE'): | |||||
""" | |||||
Return a float number, when distance small than this number, | |||||
optimize will abort early. | |||||
Args: | |||||
method: distance method. Default: MSE. | |||||
Returns: | |||||
float, the optimized level, the smaller of number, the better | |||||
of adversarial sample. | |||||
""" | |||||
predefined_threshold = 0.01 | |||||
if method == 'MSE': | |||||
return predefined_threshold | |||||
return predefined_threshold |
@@ -0,0 +1,302 @@ | |||||
# 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. | |||||
""" | |||||
PSO-Attack. | |||||
""" | |||||
import numpy as np | |||||
from mindarmour.attacks.attack import Attack | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
from mindarmour.utils._check_param import check_model, check_pair_numpy_param, \ | |||||
check_numpy_param, check_value_positive, check_int_positive, \ | |||||
check_param_type, check_equal_shape, check_param_multi_types | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'PSOAttack' | |||||
class PSOAttack(Attack): | |||||
""" | |||||
The PSO Attack represents the black-box attack based on Particle Swarm | |||||
Optimization algorithm, which belongs to differential evolution algorithms. | |||||
This attack was proposed by Rayan Mosli et al. (2019). | |||||
References: `Rayan Mosli, Matthew Wright, Bo Yuan, Yin Pan, "They Might NOT | |||||
Be Giants: Crafting Black-Box Adversarial Examples with Fewer Queries | |||||
Using Particle Swarm Optimization", arxiv: 1909.07490, 2019. | |||||
<https://arxiv.org/abs/1909.07490>`_ | |||||
Args: | |||||
model (BlackModel): Target model. | |||||
step_size (float): Attack step size. Default: 0.5. | |||||
per_bounds (float): Relative variation range of perturbations. Default: 0.6. | |||||
c1 (float): Weight coefficient. Default: 2. | |||||
c2 (float): Weight coefficient. Default: 2. | |||||
c (float): Weight of perturbation loss. Default: 2. | |||||
pop_size (int): The number of particles, which should be greater | |||||
than zero. Default: 6. | |||||
t_max (int): The maximum round of iteration for each adversarial example, | |||||
which should be greater than zero. Default: 1000. | |||||
pm (float): The probability of mutations. Default: 0.5. | |||||
bounds (tuple): Upper and lower bounds of data. In form of (clip_min, | |||||
clip_max). Default: None. | |||||
targeted (bool): If True, turns on the targeted attack. If False, | |||||
turns on untargeted attack. Default: False. | |||||
reduction_iters (int): Cycle times in reduction process. Default: 3. | |||||
sparse (bool): If True, input labels are sparse-encoded. If False, | |||||
input labels are one-hot-encoded. Default: True. | |||||
Examples: | |||||
>>> attack = PSOAttack(model) | |||||
""" | |||||
def __init__(self, model, step_size=0.5, per_bounds=0.6, c1=2.0, c2=2.0, | |||||
c=2.0, pop_size=6, t_max=1000, pm=0.5, bounds=None, | |||||
targeted=False, reduction_iters=3, sparse=True): | |||||
super(PSOAttack, self).__init__() | |||||
self._model = check_model('model', model, BlackModel) | |||||
self._step_size = check_value_positive('step_size', step_size) | |||||
self._per_bounds = check_value_positive('per_bounds', per_bounds) | |||||
self._c1 = check_value_positive('c1', c1) | |||||
self._c2 = check_value_positive('c2', c2) | |||||
self._c = check_value_positive('c', c) | |||||
self._pop_size = check_int_positive('pop_size', pop_size) | |||||
self._pm = check_value_positive('pm', pm) | |||||
self._bounds = check_param_multi_types('bounds', bounds, [list, tuple]) | |||||
for b in self._bounds: | |||||
_ = check_param_multi_types('bound', b, [int, float]) | |||||
self._targeted = check_param_type('targeted', targeted, bool) | |||||
self._t_max = check_int_positive('t_max', t_max) | |||||
self._reduce_iters = check_int_positive('reduction_iters', | |||||
reduction_iters) | |||||
self._sparse = check_param_type('sparse', sparse, bool) | |||||
def _fitness(self, confi_ori, confi_adv, x_ori, x_adv): | |||||
""" | |||||
Calculate the fitness value for each particle. | |||||
Args: | |||||
confi_ori (float): Maximum confidence or target label confidence of | |||||
the original benign inputs' prediction confidences. | |||||
confi_adv (float): Maximum confidence or target label confidence of | |||||
the adversarial samples' prediction confidences. | |||||
x_ori (numpy.ndarray): Benign samples. | |||||
x_adv (numpy.ndarray): Adversarial samples. | |||||
Returns: | |||||
- float, fitness values of adversarial particles. | |||||
- int, query times after reduction. | |||||
Examples: | |||||
>>> fitness = self._fitness(2.4, 1.2, [0.2, 0.3, 0.1], [0.21, | |||||
>>> 0.34, 0.13]) | |||||
""" | |||||
x_ori = check_numpy_param('x_ori', x_ori) | |||||
x_adv = check_numpy_param('x_adv', x_adv) | |||||
fit_value = abs( | |||||
confi_ori - confi_adv) - self._c / self._pop_size*np.linalg.norm( | |||||
(x_adv - x_ori).reshape(x_adv.shape[0], -1), axis=1) | |||||
return fit_value | |||||
def _mutation_op(self, cur_pop): | |||||
""" | |||||
Generate mutation samples. | |||||
""" | |||||
cur_pop = check_numpy_param('cur_pop', cur_pop) | |||||
perturb_noise = np.random.random(cur_pop.shape) - 0.5 | |||||
mutated_pop = perturb_noise*(np.random.random(cur_pop.shape) | |||||
< self._pm) + cur_pop | |||||
mutated_pop = np.clip(mutated_pop, cur_pop*(1 - self._per_bounds), | |||||
cur_pop*(1 + self._per_bounds)) | |||||
return mutated_pop | |||||
def _reduction(self, x_ori, q_times, label, best_position): | |||||
""" | |||||
Decrease the differences between the original samples and adversarial samples. | |||||
Args: | |||||
x_ori (numpy.ndarray): Original samples. | |||||
q_times (int): Query times. | |||||
label (int): Target label ot ground-truth label. | |||||
best_position (numpy.ndarray): Adversarial examples. | |||||
Returns: | |||||
numpy.ndarray, adversarial examples after reduction. | |||||
Examples: | |||||
>>> adv_reduction = self._reduction(self, [0.1, 0.2, 0.3], 20, 1, | |||||
>>> [0.12, 0.15, 0.25]) | |||||
""" | |||||
x_ori = check_numpy_param('x_ori', x_ori) | |||||
best_position = check_numpy_param('best_position', best_position) | |||||
x_ori, best_position = check_equal_shape('x_ori', x_ori, | |||||
'best_position', best_position) | |||||
x_ori_fla = x_ori.flatten() | |||||
best_position_fla = best_position.flatten() | |||||
pixel_deep = self._bounds[1] - self._bounds[0] | |||||
nums_pixel = len(x_ori_fla) | |||||
for i in range(nums_pixel): | |||||
diff = x_ori_fla[i] - best_position_fla[i] | |||||
if abs(diff) > pixel_deep*0.1: | |||||
old_poi_fla = np.copy(best_position_fla) | |||||
best_position_fla[i] = np.clip( | |||||
best_position_fla[i] + diff*0.5, | |||||
self._bounds[0], self._bounds[1]) | |||||
cur_label = np.argmax( | |||||
self._model.predict(np.expand_dims( | |||||
best_position_fla.reshape(x_ori.shape), axis=0))[0]) | |||||
q_times += 1 | |||||
if self._targeted: | |||||
if cur_label != label: | |||||
best_position_fla = old_poi_fla | |||||
else: | |||||
if cur_label == label: | |||||
best_position_fla = old_poi_fla | |||||
return best_position_fla.reshape(x_ori.shape), q_times | |||||
def generate(self, inputs, labels): | |||||
""" | |||||
Generate adversarial examples based on input data and targeted | |||||
labels (or ground_truth labels). | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
labels (numpy.ndarray): Targeted labels or ground_truth labels. | |||||
Returns: | |||||
- numpy.ndarray, bool values for each attack result. | |||||
- numpy.ndarray, generated adversarial examples. | |||||
- numpy.ndarray, query times for each sample. | |||||
Examples: | |||||
>>> advs = attack.generate([[0.2, 0.3, 0.4], [0.3, 0.3, 0.2]], | |||||
>>> [1, 2]) | |||||
""" | |||||
inputs, labels = check_pair_numpy_param('inputs', inputs, | |||||
'labels', labels) | |||||
if not self._sparse: | |||||
labels = np.argmax(labels, axis=1) | |||||
# generate one adversarial each time | |||||
if self._targeted: | |||||
target_labels = labels | |||||
adv_list = [] | |||||
success_list = [] | |||||
query_times_list = [] | |||||
pixel_deep = self._bounds[1] - self._bounds[0] | |||||
for i in range(inputs.shape[0]): | |||||
is_success = False | |||||
q_times = 0 | |||||
x_ori = inputs[i] | |||||
confidences = self._model.predict(np.expand_dims(x_ori, axis=0))[0] | |||||
q_times += 1 | |||||
true_label = labels[i] | |||||
if self._targeted: | |||||
t_label = target_labels[i] | |||||
confi_ori = confidences[t_label] | |||||
else: | |||||
confi_ori = max(confidences) | |||||
# step1, initializing | |||||
# initial global optimum fitness value, cannot set to be 0 | |||||
best_fitness = -np.inf | |||||
# initial global optimum position | |||||
best_position = x_ori | |||||
x_copies = np.repeat(x_ori[np.newaxis, :], self._pop_size, axis=0) | |||||
cur_noise = np.clip((np.random.random(x_copies.shape) - 0.5) | |||||
*self._step_size, | |||||
(0 - self._per_bounds)*(x_copies + 0.1), | |||||
self._per_bounds*(x_copies + 0.1)) | |||||
par = np.clip(x_copies + cur_noise, | |||||
x_copies*(1 - self._per_bounds), | |||||
x_copies*(1 + self._per_bounds)) | |||||
# initial advs | |||||
par_ori = np.copy(par) | |||||
# initial optimum positions for particles | |||||
par_best_poi = np.copy(par) | |||||
# initial optimum fitness values | |||||
par_best_fit = -np.inf*np.ones(self._pop_size) | |||||
# step2, optimization | |||||
# initial velocities for particles | |||||
v_particles = np.zeros(par.shape) | |||||
is_mutation = False | |||||
iters = 0 | |||||
while iters < self._t_max: | |||||
last_best_fit = best_fitness | |||||
ran_1 = np.random.random(par.shape) | |||||
ran_2 = np.random.random(par.shape) | |||||
v_particles = self._step_size*( | |||||
v_particles + self._c1*ran_1*(best_position - par)) \ | |||||
+ self._c2*ran_2*(par_best_poi - par) | |||||
par = np.clip(par + v_particles, | |||||
(par_ori + 0.1*pixel_deep)*( | |||||
1 - self._per_bounds), | |||||
(par_ori + 0.1*pixel_deep)*( | |||||
1 + self._per_bounds)) | |||||
if iters > 30 and is_mutation: | |||||
par = self._mutation_op(par) | |||||
if self._targeted: | |||||
confi_adv = self._model.predict(par)[:, t_label] | |||||
else: | |||||
confi_adv = np.max(self._model.predict(par), axis=1) | |||||
q_times += self._pop_size | |||||
fit_value = self._fitness(confi_ori, confi_adv, x_ori, par) | |||||
for k in range(self._pop_size): | |||||
if fit_value[k] > par_best_fit[k]: | |||||
par_best_fit[k] = fit_value[k] | |||||
par_best_poi[k] = par[k] | |||||
if fit_value[k] > best_fitness: | |||||
best_fitness = fit_value[k] | |||||
best_position = par[k] | |||||
iters += 1 | |||||
cur_pre = self._model.predict(np.expand_dims(best_position, | |||||
axis=0))[0] | |||||
is_mutation = False | |||||
if (best_fitness - last_best_fit) < last_best_fit*0.05: | |||||
is_mutation = True | |||||
cur_label = np.argmax(cur_pre) | |||||
q_times += 1 | |||||
if self._targeted: | |||||
if cur_label == t_label: | |||||
is_success = True | |||||
else: | |||||
if cur_label != true_label: | |||||
is_success = True | |||||
if is_success: | |||||
LOGGER.debug(TAG, 'successfully find one adversarial ' | |||||
'sample and start Reduction process') | |||||
# step3, reduction | |||||
if self._targeted: | |||||
best_position, q_times = self._reduction( | |||||
x_ori, q_times, t_label, best_position) | |||||
else: | |||||
best_position, q_times = self._reduction( | |||||
x_ori, q_times, true_label, best_position) | |||||
break | |||||
if not is_success: | |||||
LOGGER.debug(TAG, | |||||
'fail to find adversarial sample, iteration ' | |||||
'times is: %d and query times is: %d', | |||||
iters, | |||||
q_times) | |||||
adv_list.append(best_position) | |||||
success_list.append(is_success) | |||||
query_times_list.append(q_times) | |||||
del x_copies, cur_noise, par, par_ori, par_best_poi | |||||
return np.asarray(success_list), \ | |||||
np.asarray(adv_list), \ | |||||
np.asarray(query_times_list) |
@@ -0,0 +1,166 @@ | |||||
# 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. | |||||
""" | |||||
SaltAndPepperNoise-Attack. | |||||
""" | |||||
import time | |||||
import numpy as np | |||||
from mindarmour.attacks.attack import Attack | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
from mindarmour.utils._check_param import check_model, check_pair_numpy_param, \ | |||||
check_param_type, check_int_positive, check_param_multi_types | |||||
from mindarmour.utils._check_param import normalize_value | |||||
from mindarmour.utils.logger import LogUtil | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'SaltAndPepperNoise-Attack' | |||||
class SaltAndPepperNoiseAttack(Attack): | |||||
""" | |||||
Increases the amount of salt and pepper noise to generate adversarial | |||||
samples. | |||||
Args: | |||||
model (BlackModel): Target model. | |||||
bounds (tuple): Upper and lower bounds of data. In form of (clip_min, | |||||
clip_max). Default: (0.0, 1.0) | |||||
max_iter (int): Max iteration to generate an adversarial example. | |||||
Default: 100 | |||||
is_targeted (bool): If True, targeted attack. If False, untargeted | |||||
attack. Default: False. | |||||
sparse (bool): If True, input labels are sparse-encoded. If False, | |||||
input labels are one-hot-encoded. Default: True. | |||||
Examples: | |||||
>>> attack = SaltAndPepperNoiseAttack(model) | |||||
""" | |||||
def __init__(self, model, bounds=(0.0, 1.0), max_iter=100, | |||||
is_targeted=False, sparse=True): | |||||
super(SaltAndPepperNoiseAttack, self).__init__() | |||||
self._model = check_model('model', model, BlackModel) | |||||
self._bounds = check_param_multi_types('bounds', bounds, [tuple, list]) | |||||
for b in self._bounds: | |||||
_ = check_param_multi_types('bound', b, [int, float]) | |||||
self._max_iter = check_int_positive('max_iter', max_iter) | |||||
self._is_targeted = check_param_type('is_targeted', is_targeted, bool) | |||||
self._sparse = check_param_type('sparse', sparse, bool) | |||||
def generate(self, inputs, labels): | |||||
""" | |||||
Generate adversarial examples based on input data and target labels. | |||||
Args: | |||||
inputs (numpy.ndarray): The original, unperturbed inputs. | |||||
labels (numpy.ndarray): The target labels. | |||||
Returns: | |||||
- numpy.ndarray, bool values for each attack result. | |||||
- numpy.ndarray, generated adversarial examples. | |||||
- numpy.ndarray, query times for each sample. | |||||
Examples: | |||||
>>> adv_list = attack.generate(([[0.1, 0.2, 0.6], | |||||
>>> [0.3, 0, 0.4]], | |||||
>>> [[0, 1, 0, 0, 0, 0, 0, 0, 0, 0], | |||||
>>> [0, , 0, 1, 0, 0, 0, 0, 0, 0, 0]]) | |||||
""" | |||||
arr_x, arr_y = check_pair_numpy_param('inputs', inputs, 'labels', | |||||
labels) | |||||
if not self._sparse: | |||||
arr_y = np.argmax(arr_y, axis=1) | |||||
is_adv_list = list() | |||||
adv_list = list() | |||||
query_times_each_adv = list() | |||||
for sample, label in zip(arr_x, arr_y): | |||||
start_t = time.time() | |||||
is_adv, perturbed, query_times = self._generate_one(sample, label) | |||||
is_adv_list.append(is_adv) | |||||
adv_list.append(perturbed) | |||||
query_times_each_adv.append(query_times) | |||||
LOGGER.info(TAG, 'Finished one sample, adversarial is {}, ' | |||||
'cost time {:.2}s' | |||||
.format(is_adv, time.time() - start_t)) | |||||
is_adv_list = np.array(is_adv_list) | |||||
adv_list = np.array(adv_list) | |||||
query_times_each_adv = np.array(query_times_each_adv) | |||||
return is_adv_list, adv_list, query_times_each_adv | |||||
def _generate_one(self, one_input, label, epsilons=10): | |||||
""" | |||||
Increases the amount of salt and pepper noise to generate adversarial | |||||
samples. | |||||
Args: | |||||
one_input (numpy.ndarray): The original, unperturbed input. | |||||
label (numpy.ndarray): The target label. | |||||
epsilons (int) : Number of steps to try probability between 0 | |||||
and 1. Default: 10 | |||||
Returns: | |||||
- numpy.ndarray, bool values for result. | |||||
- numpy.ndarray, adversarial example. | |||||
- numpy.ndarray, query times for this sample. | |||||
Examples: | |||||
>>> one_adv = self._generate_one(input, label) | |||||
""" | |||||
# use binary search to get epsilons | |||||
low_ = 0.0 | |||||
high_ = 1.0 | |||||
query_count = 0 | |||||
input_shape = one_input.shape | |||||
input_dtype = one_input.dtype | |||||
one_input = one_input.reshape(-1) | |||||
depth = np.abs(np.subtract(self._bounds[0], self._bounds[1])) | |||||
best_adv = np.copy(one_input) | |||||
best_eps = high_ | |||||
find_adv = False | |||||
for _ in range(self._max_iter): | |||||
min_eps = low_ | |||||
max_eps = (low_ + high_) / 2 | |||||
for _ in range(epsilons): | |||||
adv = np.copy(one_input) | |||||
noise = np.random.uniform(low=low_, high=high_, size=one_input.size) | |||||
eps = (min_eps + max_eps) / 2 | |||||
# add salt | |||||
adv[noise < eps] = -depth | |||||
# add pepper | |||||
adv[noise >= (high_ - eps)] = depth | |||||
# normalized sample | |||||
adv = normalize_value(np.expand_dims(adv, axis=0), 'l2').astype(input_dtype) | |||||
query_count += 1 | |||||
ite_bool = self._model.is_adversarial(adv.reshape(input_shape), | |||||
label, | |||||
is_targeted=self._is_targeted) | |||||
if ite_bool: | |||||
find_adv = True | |||||
if best_eps > eps: | |||||
best_adv = adv | |||||
best_eps = eps | |||||
max_eps = eps | |||||
LOGGER.debug(TAG, 'Attack succeed, epsilon is {}'.format(eps)) | |||||
else: | |||||
min_eps = eps | |||||
if find_adv: | |||||
break | |||||
return find_adv, best_adv.reshape(input_shape), query_count |
@@ -0,0 +1,419 @@ | |||||
# 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. | |||||
""" | |||||
Carlini-wagner Attack. | |||||
""" | |||||
import numpy as np | |||||
from mindspore import Tensor | |||||
from mindspore.nn import Cell | |||||
from mindarmour.attacks.attack import Attack | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.utils._check_param import check_numpy_param, check_model, \ | |||||
check_pair_numpy_param, check_int_positive, check_param_type, \ | |||||
check_param_multi_types, check_value_positive, check_equal_shape | |||||
from mindarmour.utils.util import GradWrap | |||||
from mindarmour.utils.util import jacobian_matrix | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'CW' | |||||
def _best_logits_of_other_class(logits, target_class, value=1): | |||||
""" | |||||
Choose the index of the largest logits exclude target class. | |||||
Args: | |||||
logits (numpy.ndarray): Predict logits of samples. | |||||
target_class (numpy.ndarray): Target labels. | |||||
value (float): Maximum value of output logits. Default: 1. | |||||
Returns: | |||||
numpy.ndarray, the index of the largest logits exclude the target | |||||
class. | |||||
Examples: | |||||
>>> other_class = _best_logits_of_other_class([[0.2, 0.3, 0.5], | |||||
>>> [0.3, 0.4, 0.3]], [2, 1]) | |||||
""" | |||||
LOGGER.debug(TAG, "enter the func _best_logits_of_other_class.") | |||||
logits, target_class = check_pair_numpy_param('logits', logits, | |||||
'target_class', target_class) | |||||
res = np.zeros_like(logits) | |||||
for i in range(logits.shape[0]): | |||||
res[i][target_class[i]] = value | |||||
return np.argmax(logits - res, axis=1) | |||||
class CarliniWagnerL2Attack(Attack): | |||||
""" | |||||
The Carlini & Wagner attack using L2 norm. | |||||
References: `Nicholas Carlini, David Wagner: "Towards Evaluating | |||||
the Robustness of Neural Networks" <https://arxiv.org/abs/1608.04644>`_ | |||||
Args: | |||||
network (Cell): Target model. | |||||
num_classes (int): Number of labels of model output, which should be | |||||
greater than zero. | |||||
box_min (float): Lower bound of input of the target model. Default: 0. | |||||
box_max (float): Upper bound of input of the target model. Default: 1.0. | |||||
bin_search_steps (int): The number of steps for the binary search | |||||
used to find the optimal trade-off constant between distance | |||||
and confidence. Default: 5. | |||||
max_iterations (int): The maximum number of iterations, which should be | |||||
greater than zero. Default: 1000. | |||||
confidence (float): Confidence of the output of adversarial examples. | |||||
Default: 0. | |||||
learning_rate (float): The learning rate for the attack algorithm. | |||||
Default: 5e-3. | |||||
initial_const (float): The initial trade-off constant to use to balance | |||||
the relative importance of perturbation norm and confidence | |||||
difference. Default: 1e-2. | |||||
abort_early_check_ratio (float): Check loss progress every ratio of | |||||
all iteration. Default: 5e-2. | |||||
targeted (bool): If True, targeted attack. If False, untargeted attack. | |||||
Default: False. | |||||
fast (bool): If True, return the first found adversarial example. | |||||
If False, return the adversarial samples with smaller | |||||
perturbations. Default: True. | |||||
abort_early (bool): If True, Adam will be aborted if the loss hasn't | |||||
decreased for some time. If False, Adam will continue work until the | |||||
max iterations is arrived. Default: True. | |||||
sparse (bool): If True, input labels are sparse-coded. If False, | |||||
input labels are onehot-coded. Default: True. | |||||
Examples: | |||||
>>> attack = CarliniWagnerL2Attack(network) | |||||
""" | |||||
def __init__(self, network, num_classes, box_min=0.0, box_max=1.0, | |||||
bin_search_steps=5, max_iterations=1000, confidence=0, | |||||
learning_rate=5e-3, initial_const=1e-2, | |||||
abort_early_check_ratio=5e-2, targeted=False, | |||||
fast=True, abort_early=True, sparse=True): | |||||
LOGGER.info(TAG, "init CW object.") | |||||
super(CarliniWagnerL2Attack, self).__init__() | |||||
self._network = check_model('network', network, Cell) | |||||
self._num_classes = check_int_positive('num_classes', num_classes) | |||||
self._min = check_param_type('box_min', box_min, float) | |||||
self._max = check_param_type('box_max', box_max, float) | |||||
self._bin_search_steps = check_int_positive('search_steps', | |||||
bin_search_steps) | |||||
self._max_iterations = check_int_positive('max_iterations', | |||||
max_iterations) | |||||
self._confidence = check_param_multi_types('confidence', confidence, | |||||
[int, float]) | |||||
self._learning_rate = check_value_positive('learning_rate', | |||||
learning_rate) | |||||
self._initial_const = check_value_positive('initial_const', | |||||
initial_const) | |||||
self._abort_early = check_param_type('abort_early', abort_early, bool) | |||||
self._fast = check_param_type('fast', fast, bool) | |||||
self._abort_early_check_ratio = check_value_positive('abort_early_check_ratio', | |||||
abort_early_check_ratio) | |||||
self._targeted = check_param_type('targeted', targeted, bool) | |||||
self._net_grad = GradWrap(self._network) | |||||
self._sparse = check_param_type('sparse', sparse, bool) | |||||
self._dtype = None | |||||
def _loss_function(self, logits, new_x, org_x, org_or_target_class, | |||||
constant, confidence): | |||||
""" | |||||
Calculate the value of loss function and gradients of loss w.r.t inputs. | |||||
Args: | |||||
logits (numpy.ndarray): The output of network before softmax. | |||||
new_x (numpy.ndarray): Adversarial examples. | |||||
org_x (numpy.ndarray): Original benign input samples. | |||||
org_or_target_class (numpy.ndarray): Original/target labels. | |||||
constant (float): A trade-off constant to use to balance loss | |||||
and perturbation norm. | |||||
confidence (float): Confidence level of the output of adversarial | |||||
examples. | |||||
Returns: | |||||
numpy.ndarray, norm of perturbation, sum of the loss and the | |||||
norm, and gradients of the sum w.r.t inputs. | |||||
Raises: | |||||
ValueError: If loss is less than 0. | |||||
Examples: | |||||
>>> L2_loss, total_loss, dldx = self._loss_function([0.2 , 0.3, | |||||
>>> 0.5], [0.1, 0.2, 0.2, 0.4], [0.12, 0.2, 0.25, 0.4], [1], 2, 0) | |||||
""" | |||||
LOGGER.debug(TAG, "enter the func _loss_function.") | |||||
logits = check_numpy_param('logits', logits) | |||||
org_x = check_numpy_param('org_x', org_x) | |||||
new_x, org_or_target_class = check_pair_numpy_param('new_x', | |||||
new_x, | |||||
'org_or_target_class', | |||||
org_or_target_class) | |||||
new_x, org_x = check_equal_shape('new_x', new_x, 'org_x', org_x) | |||||
other_class_index = _best_logits_of_other_class( | |||||
logits, org_or_target_class, value=np.inf) | |||||
loss1 = np.sum((new_x - org_x)**2, | |||||
axis=tuple(range(len(new_x.shape))[1:])) | |||||
loss2 = np.zeros_like(loss1, dtype=self._dtype) | |||||
loss2_grade = np.zeros_like(new_x, dtype=self._dtype) | |||||
jaco_grad = jacobian_matrix(self._net_grad, new_x, self._num_classes) | |||||
if self._targeted: | |||||
for i in range(org_or_target_class.shape[0]): | |||||
loss2[i] = max(0, logits[i][other_class_index[i]] | |||||
- logits[i][org_or_target_class[i]] | |||||
+ confidence) | |||||
loss2_grade[i] = constant[i]*(jaco_grad[other_class_index[ | |||||
i]][i] - jaco_grad[org_or_target_class[i]][i]) | |||||
else: | |||||
for i in range(org_or_target_class.shape[0]): | |||||
loss2[i] = max(0, logits[i][org_or_target_class[i]] | |||||
- logits[i][other_class_index[i]] + confidence) | |||||
loss2_grade[i] = constant[i]*(jaco_grad[org_or_target_class[ | |||||
i]][i] - jaco_grad[other_class_index[i]][i]) | |||||
total_loss = loss1 + constant*loss2 | |||||
loss1_grade = 2*(new_x - org_x) | |||||
for i in range(org_or_target_class.shape[0]): | |||||
if loss2[i] < 0: | |||||
msg = 'loss value should greater than or equal to 0, ' \ | |||||
'but got loss2 {}'.format(loss2[i]) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
if loss2[i] == 0: | |||||
loss2_grade[i, ...] = 0 | |||||
total_loss_grade = loss1_grade + loss2_grade | |||||
return loss1, total_loss, total_loss_grade | |||||
def _to_attack_space(self, inputs): | |||||
""" | |||||
Transform input data into attack space. | |||||
Args: | |||||
inputs (numpy.ndarray): Input data. | |||||
Returns: | |||||
numpy.ndarray, transformed data which belongs to attack space. | |||||
Examples: | |||||
>>> x_att = self._to_attack_space([0.2, 0.3, 0.3]) | |||||
""" | |||||
LOGGER.debug(TAG, "enter the func _to_attack_space.") | |||||
inputs = check_numpy_param('inputs', inputs) | |||||
mean = (self._min + self._max) / 2 | |||||
diff = (self._max - self._min) / 2 | |||||
inputs = (inputs - mean) / diff | |||||
inputs = inputs*0.999999 | |||||
return np.arctanh(inputs) | |||||
def _to_model_space(self, inputs): | |||||
""" | |||||
Transform input data into model space. | |||||
Args: | |||||
inputs (numpy.ndarray): Input data. | |||||
Returns: | |||||
numpy.ndarray, transformed data which belongs to model space | |||||
and the gradient of x_model w.r.t. x_att. | |||||
Examples: | |||||
>>> x_att = self._to_model_space([10, 21, 9]) | |||||
""" | |||||
LOGGER.debug(TAG, "enter the func _to_model_space.") | |||||
inputs = check_numpy_param('inputs', inputs) | |||||
inputs = np.tanh(inputs) | |||||
the_grad = 1 - np.square(inputs) | |||||
mean = (self._min + self._max) / 2 | |||||
diff = (self._max - self._min) / 2 | |||||
inputs = inputs*diff + mean | |||||
the_grad = the_grad*diff | |||||
return inputs, the_grad | |||||
def generate(self, inputs, labels): | |||||
""" | |||||
Generate adversarial examples based on input data and targeted labels. | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
labels (numpy.ndarray): The ground truth label of input samples | |||||
or target labels. | |||||
Returns: | |||||
numpy.ndarray, generated adversarial examples. | |||||
Examples: | |||||
>>> advs = attack.generate([[0.1, 0.2, 0.6], [0.3, 0, 0.4]], [1, 2]] | |||||
""" | |||||
LOGGER.debug(TAG, "enter the func generate.") | |||||
inputs, labels = check_pair_numpy_param('inputs', inputs, | |||||
'labels', labels) | |||||
if not self._sparse: | |||||
labels = np.argmax(labels, axis=1) | |||||
self._dtype = inputs.dtype | |||||
att_original = self._to_attack_space(inputs) | |||||
reconstructed_original, _ = self._to_model_space(att_original) | |||||
# find an adversarial sample | |||||
const = np.ones_like(labels, dtype=self._dtype)*self._initial_const | |||||
lower_bound = np.zeros_like(labels, dtype=self._dtype) | |||||
upper_bound = np.ones_like(labels, dtype=self._dtype)*np.inf | |||||
adversarial_res = inputs.copy() | |||||
adversarial_loss = np.ones_like(labels, dtype=self._dtype)*np.inf | |||||
samples_num = labels.shape[0] | |||||
adv_flag = np.zeros_like(labels) | |||||
for binary_search_step in range(self._bin_search_steps): | |||||
if (binary_search_step == self._bin_search_steps - 1) and \ | |||||
(self._bin_search_steps >= 10): | |||||
const = min(1e10, upper_bound) | |||||
LOGGER.debug(TAG, | |||||
'starting optimization with const = %s', | |||||
str(const)) | |||||
att_perturbation = np.zeros_like(att_original, dtype=self._dtype) | |||||
loss_at_previous_check = np.ones_like(labels, dtype=self._dtype)*np.inf | |||||
# create a new optimizer to minimize the perturbation | |||||
optimizer = _AdamOptimizer(att_perturbation.shape) | |||||
for iteration in range(self._max_iterations): | |||||
x_input, dxdp = self._to_model_space( | |||||
att_original + att_perturbation) | |||||
logits = self._network(Tensor(x_input)).asnumpy() | |||||
current_l2_loss, current_loss, dldx = self._loss_function( | |||||
logits, x_input, reconstructed_original, | |||||
labels, const, self._confidence) | |||||
# check if attack success (include all examples) | |||||
if self._targeted: | |||||
is_adv = (np.argmax(logits, axis=1) == labels) | |||||
else: | |||||
is_adv = (np.argmax(logits, axis=1) != labels) | |||||
for i in range(samples_num): | |||||
if is_adv[i]: | |||||
adv_flag[i] = True | |||||
if current_l2_loss[i] < adversarial_loss[i]: | |||||
adversarial_res[i] = x_input[i] | |||||
adversarial_loss[i] = current_l2_loss[i] | |||||
if np.all(adv_flag): | |||||
if self._fast: | |||||
LOGGER.debug(TAG, "succeed find adversarial examples.") | |||||
msg = 'iteration: {}, logits_att: {}, ' \ | |||||
'loss: {}, l2_dist: {}' \ | |||||
.format(iteration, | |||||
np.argmax(logits, axis=1), | |||||
current_loss, current_l2_loss) | |||||
LOGGER.debug(TAG, msg) | |||||
return adversarial_res | |||||
dldx, inputs = check_equal_shape('dldx', dldx, 'inputs', inputs) | |||||
gradient = dldx*dxdp | |||||
att_perturbation += \ | |||||
optimizer(gradient, self._learning_rate) | |||||
# check if should stop iteration early | |||||
flag = True | |||||
iter_check = iteration % (np.ceil( | |||||
self._max_iterations*self._abort_early_check_ratio)) | |||||
if self._abort_early and iter_check == 0: | |||||
# check progress | |||||
for i in range(inputs.shape[0]): | |||||
if current_loss[i] <= .9999*loss_at_previous_check[i]: | |||||
flag = False | |||||
# stop Adam if all samples has no progress | |||||
if flag: | |||||
LOGGER.debug(TAG, | |||||
'step:%d, no progress yet, stop iteration', | |||||
binary_search_step) | |||||
break | |||||
loss_at_previous_check = current_loss | |||||
for i in range(samples_num): | |||||
# update bound based on search result | |||||
if adv_flag[i]: | |||||
LOGGER.debug(TAG, | |||||
'example %d, found adversarial with const=%f', | |||||
i, const[i]) | |||||
upper_bound[i] = const[i] | |||||
else: | |||||
LOGGER.debug(TAG, | |||||
'example %d, failed to find adversarial' | |||||
' with const=%f', | |||||
i, const[i]) | |||||
lower_bound[i] = const[i] | |||||
if upper_bound[i] == np.inf: | |||||
const[i] *= 10 | |||||
else: | |||||
const[i] = (lower_bound[i] + upper_bound[i]) / 2 | |||||
return adversarial_res | |||||
class _AdamOptimizer: | |||||
""" | |||||
AdamOptimizer is used to calculate the optimum attack step. | |||||
Args: | |||||
shape (tuple): The shape of perturbations. | |||||
Examples: | |||||
>>> optimizer = _AdamOptimizer(att_perturbation.shape) | |||||
""" | |||||
def __init__(self, shape): | |||||
self._m = np.zeros(shape) | |||||
self._v = np.zeros(shape) | |||||
self._t = 0 | |||||
def __call__(self, gradient, learning_rate=0.001, | |||||
beta1=0.9, beta2=0.999, epsilon=1e-8): | |||||
""" | |||||
Calculate the optimum perturbation for each iteration. | |||||
Args: | |||||
gradient (numpy.ndarray): The gradient of the loss w.r.t. to the | |||||
variable. | |||||
learning_rate (float): The learning rate in the current iteration. | |||||
Default: 0.001. | |||||
beta1 (float): Decay rate for calculating the exponentially | |||||
decaying average of past gradients. Default: 0.9. | |||||
beta2 (float): Decay rate for calculating the exponentially | |||||
decaying average of past squared gradients. Default: 0.999. | |||||
epsilon (float): Small value to avoid division by zero. | |||||
Default: 1e-8. | |||||
Returns: | |||||
numpy.ndarray, perturbations. | |||||
Examples: | |||||
>>> perturbs = optimizer([0.2, 0.1, 0.15], 0.005) | |||||
""" | |||||
gradient = check_numpy_param('gradient', gradient) | |||||
self._t += 1 | |||||
self._m = beta1*self._m + (1 - beta1)*gradient | |||||
self._v = beta2*self._v + (1 - beta2)*gradient**2 | |||||
alpha = learning_rate*np.sqrt(1 - beta2**self._t) / (1 - beta1**self._t) | |||||
pertur = -alpha*self._m / (np.sqrt(self._v) + epsilon) | |||||
return pertur |
@@ -0,0 +1,154 @@ | |||||
# 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. | |||||
""" | |||||
DeepFool Attack. | |||||
""" | |||||
import numpy as np | |||||
from mindspore import Tensor | |||||
from mindspore.nn import Cell | |||||
from mindarmour.attacks.attack import Attack | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.utils.util import GradWrap | |||||
from mindarmour.utils.util import jacobian_matrix | |||||
from mindarmour.utils._check_param import check_pair_numpy_param, check_model, \ | |||||
check_value_positive, check_int_positive, check_norm_level, \ | |||||
check_param_multi_types, check_param_type | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'DeepFool' | |||||
class DeepFool(Attack): | |||||
""" | |||||
DeepFool is an untargeted & iterative attack achieved by moving the benign | |||||
sample to the nearest classification boundary and crossing the boundary. | |||||
Reference: `DeepFool: a simple and accurate method to fool deep neural | |||||
networks <https://arxiv.org/abs/1511.04599>`_ | |||||
Args: | |||||
network (Cell): Target model. | |||||
num_classes (int): Number of labels of model output, which should be | |||||
greater than zero. | |||||
max_iters (int): Max iterations, which should be | |||||
greater than zero. Default: 50. | |||||
overshoot (float): Overshoot parameter. Default: 0.02. | |||||
norm_level (int): Order of the vector norm. Possible values: np.inf | |||||
or 2. Default: 2. | |||||
bounds (tuple): Upper and lower bounds of data range. In form of (clip_min, | |||||
clip_max). Default: None. | |||||
sparse (bool): If True, input labels are sparse-coded. If False, | |||||
input labels are onehot-coded. Default: True. | |||||
Examples: | |||||
>>> attack = DeepFool(network) | |||||
""" | |||||
def __init__(self, network, num_classes, max_iters=50, overshoot=0.02, | |||||
norm_level=2, bounds=None, sparse=True): | |||||
super(DeepFool, self).__init__() | |||||
self._network = check_model('network', network, Cell) | |||||
self._max_iters = check_int_positive('max_iters', max_iters) | |||||
self._overshoot = check_value_positive('overshoot', overshoot) | |||||
self._norm_level = check_norm_level(norm_level) | |||||
self._num_classes = check_int_positive('num_classes', num_classes) | |||||
self._net_grad = GradWrap(self._network) | |||||
self._bounds = check_param_multi_types('bounds', bounds, [list, tuple]) | |||||
self._sparse = check_param_type('sparse', sparse, bool) | |||||
for b in self._bounds: | |||||
_ = check_param_multi_types('bound', b, [int, float]) | |||||
def generate(self, inputs, labels): | |||||
""" | |||||
Generate adversarial examples based on input samples and original labels. | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
labels (numpy.ndarray): Original labels. | |||||
Returns: | |||||
numpy.ndarray, adversarial examples. | |||||
Raises: | |||||
NotImplementedError: If norm_level is not in [2, np.inf, '2', 'inf']. | |||||
Examples: | |||||
>>> advs = generate([[0.2, 0.3, 0.4], [0.3, 0.4, 0.5]], [1, 2]) | |||||
""" | |||||
inputs, labels = check_pair_numpy_param('inputs', inputs, | |||||
'labels', labels) | |||||
if not self._sparse: | |||||
labels = np.argmax(labels, axis=1) | |||||
inputs_dtype = inputs.dtype | |||||
iteration = 0 | |||||
origin_labels = labels | |||||
cur_labels = origin_labels.copy() | |||||
weight = np.squeeze(np.zeros(inputs.shape[1:])) | |||||
r_tot = np.zeros(inputs.shape) | |||||
x_origin = inputs | |||||
while np.any(cur_labels == origin_labels) and iteration < self._max_iters: | |||||
preds = self._network(Tensor(inputs)).asnumpy() | |||||
grads = jacobian_matrix(self._net_grad, inputs, self._num_classes) | |||||
for idx in range(inputs.shape[0]): | |||||
diff_w = np.inf | |||||
label = origin_labels[idx] | |||||
if cur_labels[idx] != label: | |||||
continue | |||||
for k in range(self._num_classes): | |||||
if k == label: | |||||
continue | |||||
w_k = grads[k, idx, ...] - grads[label, idx, ...] | |||||
f_k = preds[idx, k] - preds[idx, label] | |||||
if self._norm_level == 2 or self._norm_level == '2': | |||||
diff_w_k = abs(f_k) / (np.linalg.norm(w_k) + 1e-8) | |||||
elif self._norm_level == np.inf \ | |||||
or self._norm_level == 'inf': | |||||
diff_w_k = abs(f_k) / (np.linalg.norm(w_k, ord=1) + 1e-8) | |||||
else: | |||||
msg = 'ord {} is not available.' \ | |||||
.format(str(self._norm_level)) | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) | |||||
if diff_w_k < diff_w: | |||||
diff_w = diff_w_k | |||||
weight = w_k | |||||
if self._norm_level == 2 or self._norm_level == '2': | |||||
r_i = diff_w*weight / (np.linalg.norm(weight) + 1e-8) | |||||
elif self._norm_level == np.inf or self._norm_level == 'inf': | |||||
r_i = diff_w*np.sign(weight) \ | |||||
/ (np.linalg.norm(weight, ord=1) + 1e-8) | |||||
else: | |||||
msg = 'ord {} is not available in normalization.' \ | |||||
.format(str(self._norm_level)) | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) | |||||
r_tot[idx, ...] = r_tot[idx, ...] + r_i | |||||
if self._bounds is not None: | |||||
clip_min, clip_max = self._bounds | |||||
inputs = x_origin + (1 + self._overshoot)*r_tot*(clip_max | |||||
- clip_min) | |||||
inputs = np.clip(inputs, clip_min, clip_max) | |||||
else: | |||||
inputs = x_origin + (1 + self._overshoot)*r_tot | |||||
cur_labels = np.argmax( | |||||
self._network(Tensor(inputs.astype(inputs_dtype))).asnumpy(), | |||||
axis=1) | |||||
iteration += 1 | |||||
inputs = inputs.astype(inputs_dtype) | |||||
del preds, grads | |||||
return inputs |
@@ -0,0 +1,402 @@ | |||||
# 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. | |||||
""" | |||||
Gradient-method Attack. | |||||
""" | |||||
from abc import abstractmethod | |||||
import numpy as np | |||||
from mindspore import Tensor | |||||
from mindspore.nn import Cell | |||||
from mindspore.nn import SoftmaxCrossEntropyWithLogits | |||||
from mindarmour.attacks.attack import Attack | |||||
from mindarmour.utils.util import WithLossCell | |||||
from mindarmour.utils.util import GradWrapWithLoss | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.utils._check_param import check_pair_numpy_param, check_model, \ | |||||
normalize_value, check_value_positive, check_param_multi_types, \ | |||||
check_norm_level, check_param_type | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'SingleGrad' | |||||
class GradientMethod(Attack): | |||||
""" | |||||
Abstract base class for all single-step gradient-based attacks. | |||||
Args: | |||||
network (Cell): Target model. | |||||
eps (float): Proportion of single-step adversarial perturbation generated | |||||
by the attack to data range. Default: 0.07. | |||||
alpha (float): Proportion of single-step random perturbation to data range. | |||||
Default: None. | |||||
bounds (tuple): Upper and lower bounds of data, indicating the data range. | |||||
In form of (clip_min, clip_max). Default: None. | |||||
loss_fn (Loss): Loss function for optimization. | |||||
""" | |||||
def __init__(self, network, eps=0.07, alpha=None, bounds=None, | |||||
loss_fn=None): | |||||
super(GradientMethod, self).__init__() | |||||
self._network = check_model('network', network, Cell) | |||||
self._eps = check_value_positive('eps', eps) | |||||
self._dtype = None | |||||
if bounds is not None: | |||||
self._bounds = check_param_multi_types('bounds', bounds, | |||||
[list, tuple]) | |||||
for b in self._bounds: | |||||
_ = check_param_multi_types('bound', b, [int, float]) | |||||
else: | |||||
self._bounds = bounds | |||||
if alpha is not None: | |||||
self._alpha = check_value_positive('alpha', alpha) | |||||
else: | |||||
self._alpha = alpha | |||||
if loss_fn is None: | |||||
loss_fn = SoftmaxCrossEntropyWithLogits(is_grad=False, | |||||
sparse=False) | |||||
with_loss_cell = WithLossCell(self._network, loss_fn) | |||||
self._grad_all = GradWrapWithLoss(with_loss_cell) | |||||
self._grad_all.set_train() | |||||
def generate(self, inputs, labels): | |||||
""" | |||||
Generate adversarial examples based on input samples and original/target labels. | |||||
Args: | |||||
inputs (numpy.ndarray): Benign input samples used as references to create | |||||
adversarial examples. | |||||
labels (numpy.ndarray): Original/target labels. | |||||
Returns: | |||||
numpy.ndarray, generated adversarial examples. | |||||
Examples: | |||||
>>> adv_x = attack.generate([[0.1, 0.2, 0.6], [0.3, 0, 0.4]], | |||||
>>> [[0, 1, 0, 0, 0, 0, 0, 0, 0, 0],[0, , 0, 1, 0, 0, 0, 0, 0, 0, | |||||
>>> 0]]) | |||||
""" | |||||
inputs, labels = check_pair_numpy_param('inputs', inputs, | |||||
'labels', labels) | |||||
self._dtype = inputs.dtype | |||||
gradient = self._gradient(inputs, labels) | |||||
# use random method or not | |||||
if self._alpha is not None: | |||||
random_part = self._alpha*np.sign(np.random.normal( | |||||
size=inputs.shape)).astype(self._dtype) | |||||
perturbation = (self._eps - self._alpha)*gradient + random_part | |||||
else: | |||||
perturbation = self._eps*gradient | |||||
if self._bounds is not None: | |||||
clip_min, clip_max = self._bounds | |||||
perturbation = perturbation*(clip_max - clip_min) | |||||
adv_x = inputs + perturbation | |||||
adv_x = np.clip(adv_x, clip_min, clip_max) | |||||
else: | |||||
adv_x = inputs + perturbation | |||||
return adv_x | |||||
@abstractmethod | |||||
def _gradient(self, inputs, labels): | |||||
""" | |||||
Calculate gradients based on input samples and original/target labels. | |||||
Args: | |||||
inputs (numpy.ndarray): Benign input samples used as references to | |||||
create adversarial examples. | |||||
labels (numpy.ndarray): Original/target labels. | |||||
Raises: | |||||
NotImplementedError: It is an abstract method. | |||||
""" | |||||
msg = 'The function _gradient() is an abstract method in class ' \ | |||||
'`GradientMethod`, and should be implemented in child class.' | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) | |||||
class FastGradientMethod(GradientMethod): | |||||
""" | |||||
This attack is a one-step attack based on gradients calculation, and | |||||
the norm of perturbations includes L1, L2 and Linf. | |||||
References: `I. J. Goodfellow, J. Shlens, and C. Szegedy, "Explaining | |||||
and harnessing adversarial examples," in ICLR, 2015. | |||||
<https://arxiv.org/abs/1412.6572>`_ | |||||
Args: | |||||
network (Cell): Target model. | |||||
eps (float): Proportion of single-step adversarial perturbation generated | |||||
by the attack to data range. Default: 0.07. | |||||
alpha (float): Proportion of single-step random perturbation to data range. | |||||
Default: None. | |||||
bounds (tuple): Upper and lower bounds of data, indicating the data range. | |||||
In form of (clip_min, clip_max). Default: (0.0, 1.0). | |||||
norm_level (Union[int, numpy.inf]): Order of the norm. | |||||
Possible values: np.inf, 1 or 2. Default: 2. | |||||
is_targeted (bool): If True, targeted attack. If False, untargeted | |||||
attack. Default: False. | |||||
loss_fn (Loss): Loss function for optimization. | |||||
Examples: | |||||
>>> attack = FastGradientMethod(network) | |||||
""" | |||||
def __init__(self, network, eps=0.07, alpha=None, bounds=(0.0, 1.0), | |||||
norm_level=2, is_targeted=False, loss_fn=None): | |||||
super(FastGradientMethod, self).__init__(network, | |||||
eps=eps, | |||||
alpha=alpha, | |||||
bounds=bounds, | |||||
loss_fn=loss_fn) | |||||
self._norm_level = check_norm_level(norm_level) | |||||
self._is_targeted = check_param_type('is_targeted', is_targeted, bool) | |||||
def _gradient(self, inputs, labels): | |||||
""" | |||||
Calculate gradients based on input samples and original/target labels. | |||||
Args: | |||||
inputs (numpy.ndarray): Input sample. | |||||
labels (numpy.ndarray): Original/target label. | |||||
Returns: | |||||
numpy.ndarray, gradient of inputs. | |||||
Examples: | |||||
>>> grad = self._gradient([[0.2, 0.3, 0.4]], | |||||
>>> [[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]) | |||||
""" | |||||
sens = Tensor(np.array([1.0], self._dtype)) | |||||
out_grad = self._grad_all(Tensor(inputs), Tensor(labels), sens) | |||||
if isinstance(out_grad, tuple): | |||||
out_grad = out_grad[0] | |||||
gradient = out_grad.asnumpy() | |||||
if self._is_targeted: | |||||
gradient = -gradient | |||||
return normalize_value(gradient, self._norm_level) | |||||
class RandomFastGradientMethod(FastGradientMethod): | |||||
""" | |||||
Fast Gradient Method use Random perturbation. | |||||
References: `Florian Tramer, Alexey Kurakin, Nicolas Papernot, "Ensemble | |||||
adversarial training: Attacks and defenses" in ICLR, 2018 | |||||
<https://arxiv.org/abs/1705.07204>`_ | |||||
Args: | |||||
network (Cell): Target model. | |||||
eps (float): Proportion of single-step adversarial perturbation generated | |||||
by the attack to data range. Default: 0.07. | |||||
alpha (float): Proportion of single-step random perturbation to data range. | |||||
Default: 0.035. | |||||
bounds (tuple): Upper and lower bounds of data, indicating the data range. | |||||
In form of (clip_min, clip_max). Default: (0.0, 1.0). | |||||
norm_level (Union[int, numpy.inf]): Order of the norm. | |||||
Possible values: np.inf, 1 or 2. Default: 2. | |||||
is_targeted (bool): If True, targeted attack. If False, untargeted | |||||
attack. Default: False. | |||||
loss_fn (Loss): Loss function for optimization. | |||||
Raises: | |||||
ValueError: eps is smaller than alpha! | |||||
Examples: | |||||
>>> attack = RandomFastGradientMethod(network) | |||||
""" | |||||
def __init__(self, network, eps=0.07, alpha=0.035, bounds=(0.0, 1.0), | |||||
norm_level=2, is_targeted=False, loss_fn=None): | |||||
if eps < alpha: | |||||
raise ValueError('eps must be larger than alpha!') | |||||
super(RandomFastGradientMethod, self).__init__(network, | |||||
eps=eps, | |||||
alpha=alpha, | |||||
bounds=bounds, | |||||
norm_level=norm_level, | |||||
is_targeted=is_targeted, | |||||
loss_fn=loss_fn) | |||||
class FastGradientSignMethod(GradientMethod): | |||||
""" | |||||
Use the sign instead of the value of the gradient to the input. This attack is | |||||
often referred to as Fast Gradient Sign Method and was introduced previously. | |||||
References: `Ian J. Goodfellow, J. Shlens, and C. Szegedy, "Explaining | |||||
and harnessing adversarial examples," in ICLR, 2015 | |||||
<https://arxiv.org/abs/1412.6572>`_ | |||||
Args: | |||||
network (Cell): Target model. | |||||
eps (float): Proportion of single-step adversarial perturbation generated | |||||
by the attack to data range. Default: 0.07. | |||||
alpha (float): Proportion of single-step random perturbation to data range. | |||||
Default: None. | |||||
bounds (tuple): Upper and lower bounds of data, indicating the data range. | |||||
In form of (clip_min, clip_max). Default: (0.0, 1.0). | |||||
is_targeted (bool): If True, targeted attack. If False, untargeted | |||||
attack. Default: False. | |||||
loss_fn (Loss): Loss function for optimization. | |||||
Examples: | |||||
>>> attack = FastGradientSignMethod(network) | |||||
""" | |||||
def __init__(self, network, eps=0.07, alpha=None, bounds=(0.0, 1.0), | |||||
is_targeted=False, loss_fn=None): | |||||
super(FastGradientSignMethod, self).__init__(network, | |||||
eps=eps, | |||||
alpha=alpha, | |||||
bounds=bounds, | |||||
loss_fn=loss_fn) | |||||
self._is_targeted = check_param_type('is_targeted', is_targeted, bool) | |||||
def _gradient(self, inputs, labels): | |||||
""" | |||||
Calculate gradients based on input samples and original/target | |||||
labels. | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
labels (numpy.ndarray): Original/target labels. | |||||
Returns: | |||||
numpy.ndarray, gradient of inputs. | |||||
Examples: | |||||
>>> grad = self._gradient([[0.2, 0.3, 0.4]], | |||||
>>> [[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]) | |||||
""" | |||||
sens = Tensor(np.array([1.0], self._dtype)) | |||||
out_grad = self._grad_all(Tensor(inputs), Tensor(labels), sens) | |||||
if isinstance(out_grad, tuple): | |||||
out_grad = out_grad[0] | |||||
gradient = out_grad.asnumpy() | |||||
if self._is_targeted: | |||||
gradient = -gradient | |||||
gradient = np.sign(gradient) | |||||
return gradient | |||||
class RandomFastGradientSignMethod(FastGradientSignMethod): | |||||
""" | |||||
Fast Gradient Sign Method using random perturbation. | |||||
References: `F. Tramer, et al., "Ensemble adversarial training: Attacks | |||||
and defenses," in ICLR, 2018 <https://arxiv.org/abs/1705.07204>`_ | |||||
Args: | |||||
network (Cell): Target model. | |||||
eps (float): Proportion of single-step adversarial perturbation generated | |||||
by the attack to data range. Default: 0.07. | |||||
alpha (float): Proportion of single-step random perturbation to data range. | |||||
Default: 0.035. | |||||
bounds (tuple): Upper and lower bounds of data, indicating the data range. | |||||
In form of (clip_min, clip_max). Default: (0.0, 1.0). | |||||
is_targeted (bool): True: targeted attack. False: untargeted attack. | |||||
Default: False. | |||||
loss_fn (Loss): Loss function for optimization. | |||||
Raises: | |||||
ValueError: eps is smaller than alpha! | |||||
Examples: | |||||
>>> attack = RandomFastGradientSignMethod(network) | |||||
""" | |||||
def __init__(self, network, eps=0.07, alpha=0.035, bounds=(0.0, 1.0), | |||||
is_targeted=False, loss_fn=None): | |||||
if eps < alpha: | |||||
raise ValueError('eps must be larger than alpha!') | |||||
super(RandomFastGradientSignMethod, self).__init__(network, | |||||
eps=eps, | |||||
alpha=alpha, | |||||
bounds=bounds, | |||||
is_targeted=is_targeted, | |||||
loss_fn=loss_fn) | |||||
class LeastLikelyClassMethod(FastGradientSignMethod): | |||||
""" | |||||
Least-Likely Class Method. | |||||
References: `F. Tramer, et al., "Ensemble adversarial training: Attacks | |||||
and defenses," in ICLR, 2018 <https://arxiv.org/abs/1705.07204>`_ | |||||
Args: | |||||
network (Cell): Target model. | |||||
eps (float): Proportion of single-step adversarial perturbation generated | |||||
by the attack to data range. Default: 0.07. | |||||
alpha (float): Proportion of single-step random perturbation to data range. | |||||
Default: None. | |||||
bounds (tuple): Upper and lower bounds of data, indicating the data range. | |||||
In form of (clip_min, clip_max). Default: (0.0, 1.0). | |||||
loss_fn (Loss): Loss function for optimization. | |||||
Examples: | |||||
>>> attack = LeastLikelyClassMethod(network) | |||||
""" | |||||
def __init__(self, network, eps=0.07, alpha=None, bounds=(0.0, 1.0), | |||||
loss_fn=None): | |||||
super(LeastLikelyClassMethod, self).__init__(network, | |||||
eps=eps, | |||||
alpha=alpha, | |||||
bounds=bounds, | |||||
is_targeted=True, | |||||
loss_fn=loss_fn) | |||||
class RandomLeastLikelyClassMethod(FastGradientSignMethod): | |||||
""" | |||||
Least-Likely Class Method use Random perturbation. | |||||
References: `F. Tramer, et al., "Ensemble adversarial training: Attacks | |||||
and defenses," in ICLR, 2018 <https://arxiv.org/abs/1705.07204>`_ | |||||
Args: | |||||
network (Cell): Target model. | |||||
eps (float): Proportion of single-step adversarial perturbation generated | |||||
by the attack to data range. Default: 0.07. | |||||
alpha (float): Proportion of single-step random perturbation to data range. | |||||
Default: 0.035. | |||||
bounds (tuple): Upper and lower bounds of data, indicating the data range. | |||||
In form of (clip_min, clip_max). Default: (0.0, 1.0). | |||||
loss_fn (Loss): Loss function for optimization. | |||||
Raises: | |||||
ValueError: eps is smaller than alpha! | |||||
Examples: | |||||
>>> attack = RandomLeastLikelyClassMethod(network) | |||||
""" | |||||
def __init__(self, network, eps=0.07, alpha=0.035, bounds=(0.0, 1.0), | |||||
loss_fn=None): | |||||
if eps < alpha: | |||||
raise ValueError('eps must be larger than alpha!') | |||||
super(RandomLeastLikelyClassMethod, self).__init__(network, | |||||
eps=eps, | |||||
alpha=alpha, | |||||
bounds=bounds, | |||||
is_targeted=True, | |||||
loss_fn=loss_fn) |
@@ -0,0 +1,432 @@ | |||||
# 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. | |||||
""" Iterative gradient method attack. """ | |||||
from abc import abstractmethod | |||||
import numpy as np | |||||
from mindspore.nn import SoftmaxCrossEntropyWithLogits | |||||
from mindspore import Tensor | |||||
from mindspore.nn import Cell | |||||
from mindarmour.attacks.attack import Attack | |||||
from mindarmour.attacks.gradient_method import FastGradientSignMethod | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.utils.util import WithLossCell | |||||
from mindarmour.utils.util import GradWrapWithLoss | |||||
from mindarmour.utils._check_param import check_pair_numpy_param, \ | |||||
normalize_value, check_model, check_value_positive, check_int_positive, \ | |||||
check_param_type, check_norm_level, check_param_multi_types | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'IterGrad' | |||||
def _reshape_l1_projection(values, eps=3): | |||||
""" | |||||
`Implementation of L1 ball projection from:`_. | |||||
.. _`Implementation of L1 ball projection from:`: | |||||
https://stanford.edu/~jduchi/projects/DuchiShSiCh08.pdf | |||||
Args: | |||||
values (numpy.ndarray): Input data reshape into 2-dims. | |||||
eps (float): L1 radius. Default: 3. | |||||
Returns: | |||||
numpy.ndarray, containing the projection. | |||||
""" | |||||
abs_x = np.abs(values) | |||||
abs_x = np.sum(abs_x, axis=1) | |||||
indexes_b = (abs_x > eps) | |||||
x_b = values[indexes_b] | |||||
batch_size_b = x_b.shape[0] | |||||
if batch_size_b == 0: | |||||
return values | |||||
# make the projection on l1 ball for elements outside the ball | |||||
b_mu = -np.sort(-np.abs(x_b), axis=1) | |||||
b_vv = np.arange(x_b.shape[1]).astype(np.float) | |||||
b_st = (np.cumsum(b_mu, axis=1)-eps)/(b_vv+1) | |||||
selected = (b_mu - b_st) > 0 | |||||
rho = np.sum((np.cumsum((1-selected), axis=1) == 0), axis=1)-1 | |||||
theta = np.take_along_axis(b_st, np.expand_dims(rho, axis=1), axis=1) | |||||
proj_x_b = np.maximum(0, np.abs(x_b)-theta)*np.sign(x_b) | |||||
# gather all the projected batch | |||||
proj_x = np.copy(values) | |||||
proj_x[indexes_b] = proj_x_b | |||||
return proj_x | |||||
def _projection(values, eps, norm_level): | |||||
""" | |||||
Implementation of values normalization within eps. | |||||
Args: | |||||
values (numpy.ndarray): Input data. | |||||
eps (float): Project radius. | |||||
norm_level (Union[int, char, numpy.inf]): Order of the norm. Possible | |||||
values: np.inf, 1 or 2. | |||||
Returns: | |||||
numpy.ndarray, normalized values. | |||||
Raises: | |||||
NotImplementedError: If the norm_level is not in [1, 2, np.inf, '1', | |||||
'2', 'inf']. | |||||
""" | |||||
if norm_level in (1, '1'): | |||||
sample_batch = values.shape[0] | |||||
x_flat = values.reshape(sample_batch, -1) | |||||
proj_flat = _reshape_l1_projection(x_flat, eps) | |||||
return proj_flat.reshape(values.shape) | |||||
if norm_level in (2, '2'): | |||||
return eps*normalize_value(values, norm_level) | |||||
if norm_level in (np.inf, 'inf'): | |||||
return eps*np.sign(values) | |||||
msg = 'Values of `norm_level` different from 1, 2 and `np.inf` are ' \ | |||||
'currently not supported.' | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) | |||||
class IterativeGradientMethod(Attack): | |||||
""" | |||||
Abstract base class for all iterative gradient based attacks. | |||||
Args: | |||||
network (Cell): Target model. | |||||
eps (float): Proportion of adversarial perturbation generated by the | |||||
attack to data range. Default: 0.3. | |||||
eps_iter (float): Proportion of single-step adversarial perturbation | |||||
generated by the attack to data range. Default: 0.1. | |||||
bounds (tuple): Upper and lower bounds of data, indicating the data range. | |||||
In form of (clip_min, clip_max). Default: (0.0, 1.0). | |||||
nb_iter (int): Number of iteration. Default: 5. | |||||
loss_fn (Loss): Loss function for optimization. | |||||
""" | |||||
def __init__(self, network, eps=0.3, eps_iter=0.1, bounds=(0.0, 1.0), nb_iter=5, | |||||
loss_fn=None): | |||||
super(IterativeGradientMethod, self).__init__() | |||||
self._network = check_model('network', network, Cell) | |||||
self._eps = check_value_positive('eps', eps) | |||||
self._eps_iter = check_value_positive('eps_iter', eps_iter) | |||||
self._nb_iter = check_int_positive('nb_iter', nb_iter) | |||||
self._bounds = check_param_multi_types('bounds', bounds, [list, tuple]) | |||||
for b in self._bounds: | |||||
_ = check_param_multi_types('bound', b, [int, float]) | |||||
if loss_fn is None: | |||||
loss_fn = SoftmaxCrossEntropyWithLogits(is_grad=False, sparse=False) | |||||
self._loss_grad = GradWrapWithLoss(WithLossCell(self._network, loss_fn)) | |||||
self._loss_grad.set_train() | |||||
@abstractmethod | |||||
def generate(self, inputs, labels): | |||||
""" | |||||
Generate adversarial examples based on input samples and original/target labels. | |||||
Args: | |||||
inputs (numpy.ndarray): Benign input samples used as references to create | |||||
adversarial examples. | |||||
labels (numpy.ndarray): Original/target labels. | |||||
Raises: | |||||
NotImplementedError: This function is not available in | |||||
IterativeGradientMethod. | |||||
Examples: | |||||
>>> adv_x = attack.generate([[0.1, 0.9, 0.6], | |||||
>>> [0.3, 0, 0.3]], | |||||
>>> [[0, , 1, 0, 0, 0, 0, 0, 0, 0], | |||||
>>> [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]]) | |||||
""" | |||||
msg = 'The function generate() is an abstract method in class ' \ | |||||
'`IterativeGradientMethod`, and should be implemented ' \ | |||||
'in child class.' | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) | |||||
class BasicIterativeMethod(IterativeGradientMethod): | |||||
""" | |||||
The Basic Iterative Method attack, an iterative FGSM method to generate | |||||
adversarial examples. | |||||
References: `A. Kurakin, I. Goodfellow, and S. Bengio, "Adversarial examples | |||||
in the physical world," in ICLR, 2017 <https://arxiv.org/abs/1607.02533>`_ | |||||
Args: | |||||
network (Cell): Target model. | |||||
eps (float): Proportion of adversarial perturbation generated by the | |||||
attack to data range. Default: 0.3. | |||||
eps_iter (float): Proportion of single-step adversarial perturbation | |||||
generated by the attack to data range. Default: 0.1. | |||||
bounds (tuple): Upper and lower bounds of data, indicating the data range. | |||||
In form of (clip_min, clip_max). Default: (0.0, 1.0). | |||||
is_targeted (bool): If True, targeted attack. If False, untargeted | |||||
attack. Default: False. | |||||
nb_iter (int): Number of iteration. Default: 5. | |||||
loss_fn (Loss): Loss function for optimization. | |||||
attack (class): The single step gradient method of each iteration. In | |||||
this class, FGSM is used. | |||||
Examples: | |||||
>>> attack = BasicIterativeMethod(network) | |||||
""" | |||||
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): | |||||
super(BasicIterativeMethod, self).__init__(network, | |||||
eps=eps, | |||||
eps_iter=eps_iter, | |||||
bounds=bounds, | |||||
nb_iter=nb_iter, | |||||
loss_fn=loss_fn) | |||||
self._is_targeted = check_param_type('is_targeted', is_targeted, bool) | |||||
self._attack = FastGradientSignMethod(self._network, | |||||
eps=self._eps_iter, | |||||
bounds=self._bounds, | |||||
is_targeted=self._is_targeted, | |||||
loss_fn=loss_fn) | |||||
def generate(self, inputs, labels): | |||||
""" | |||||
Simple iterative FGSM method to generate adversarial examples. | |||||
Args: | |||||
inputs (numpy.ndarray): Benign input samples used as references to | |||||
create adversarial examples. | |||||
labels (numpy.ndarray): Original/target labels. | |||||
Returns: | |||||
numpy.ndarray, generated adversarial examples. | |||||
Examples: | |||||
>>> adv_x = attack.generate([[0.3, 0.2, 0.6], | |||||
>>> [0.3, 0.2, 0.4]], | |||||
>>> [[0, 0, 1, 0, 0, 0, 0, 0, 0, 0], | |||||
>>> [0, 0, 0, 0, 0, 0, 1, 0, 0, 0]]) | |||||
""" | |||||
inputs, labels = check_pair_numpy_param('inputs', inputs, | |||||
'labels', labels) | |||||
arr_x = inputs | |||||
if self._bounds is not None: | |||||
clip_min, clip_max = self._bounds | |||||
clip_diff = clip_max - clip_min | |||||
for _ in range(self._nb_iter): | |||||
adv_x = self._attack.generate(inputs, labels) | |||||
perturs = np.clip(adv_x - arr_x, (0 - self._eps)*clip_diff, | |||||
self._eps*clip_diff) | |||||
adv_x = arr_x + perturs | |||||
inputs = adv_x | |||||
else: | |||||
for _ in range(self._nb_iter): | |||||
adv_x = self._attack.generate(inputs, labels) | |||||
adv_x = np.clip(adv_x, arr_x - self._eps, arr_x + self._eps) | |||||
inputs = adv_x | |||||
return adv_x | |||||
class MomentumIterativeMethod(IterativeGradientMethod): | |||||
""" | |||||
The Momentum Iterative Method attack. | |||||
References: `Y. Dong, et al., "Boosting adversarial attacks with | |||||
momentum," arXiv:1710.06081, 2017 <https://arxiv.org/abs/1710.06081>`_ | |||||
Args: | |||||
network (Cell): Target model. | |||||
eps (float): Proportion of adversarial perturbation generated by the | |||||
attack to data range. Default: 0.3. | |||||
eps_iter (float): Proportion of single-step adversarial perturbation | |||||
generated by the attack to data range. Default: 0.1. | |||||
bounds (tuple): Upper and lower bounds of data, indicating the data range. | |||||
In form of (clip_min, clip_max). Default: (0.0, 1.0). | |||||
is_targeted (bool): If True, targeted attack. If False, untargeted | |||||
attack. Default: False. | |||||
nb_iter (int): Number of iteration. Default: 5. | |||||
decay_factor (float): Decay factor in iterations. Default: 1.0. | |||||
norm_level (Union[int, numpy.inf]): Order of the norm. Possible values: | |||||
np.inf, 1 or 2. Default: 'inf'. | |||||
loss_fn (Loss): Loss function for optimization. | |||||
""" | |||||
def __init__(self, network, eps=0.3, eps_iter=0.1, bounds=(0.0, 1.0), | |||||
is_targeted=False, nb_iter=5, decay_factor=1.0, | |||||
norm_level='inf', loss_fn=None): | |||||
super(MomentumIterativeMethod, self).__init__(network, | |||||
eps=eps, | |||||
eps_iter=eps_iter, | |||||
bounds=bounds, | |||||
nb_iter=nb_iter, | |||||
loss_fn=loss_fn) | |||||
self._is_targeted = check_param_type('is_targeted', is_targeted, bool) | |||||
self._decay_factor = check_value_positive('decay_factor', decay_factor) | |||||
self._norm_level = check_norm_level(norm_level) | |||||
def generate(self, inputs, labels): | |||||
""" | |||||
Generate adversarial examples based on input data and origin/target labels. | |||||
Args: | |||||
inputs (numpy.ndarray): Benign input samples used as references to | |||||
create adversarial examples. | |||||
labels (numpy.ndarray): Original/target labels. | |||||
Returns: | |||||
numpy.ndarray, generated adversarial examples. | |||||
Examples: | |||||
>>> adv_x = attack.generate([[0.5, 0.2, 0.6], | |||||
>>> [0.3, 0, 0.2]], | |||||
>>> [[0, 0, 0, 0, 0, 0, 0, 0, 1, 0], | |||||
>>> [0, 0, 0, 0, 0, 1, 0, 0, 0, 0]]) | |||||
""" | |||||
inputs, labels = check_pair_numpy_param('inputs', inputs, | |||||
'labels', labels) | |||||
arr_x = inputs | |||||
momentum = 0 | |||||
if self._bounds is not None: | |||||
clip_min, clip_max = self._bounds | |||||
clip_diff = clip_max - clip_min | |||||
for _ in range(self._nb_iter): | |||||
gradient = self._gradient(inputs, labels) | |||||
momentum = self._decay_factor*momentum + gradient | |||||
adv_x = inputs + self._eps_iter*np.sign(momentum) | |||||
perturs = np.clip(adv_x - arr_x, (0 - self._eps)*clip_diff, | |||||
self._eps*clip_diff) | |||||
adv_x = arr_x + perturs | |||||
adv_x = np.clip(adv_x, clip_min, clip_max) | |||||
inputs = adv_x | |||||
else: | |||||
for _ in range(self._nb_iter): | |||||
gradient = self._gradient(inputs, labels) | |||||
momentum = self._decay_factor*momentum + gradient | |||||
adv_x = inputs + self._eps_iter*np.sign(momentum) | |||||
adv_x = np.clip(adv_x, arr_x - self._eps, arr_x + self._eps) | |||||
inputs = adv_x | |||||
return adv_x | |||||
def _gradient(self, inputs, labels): | |||||
""" | |||||
Calculate the gradient of input samples. | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
labels (numpy.ndarray): Original/target labels. | |||||
Returns: | |||||
numpy.ndarray, gradient of labels w.r.t inputs. | |||||
Examples: | |||||
>>> grad = self._gradient([[0.5, 0.3, 0.4]], | |||||
>>> [[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]) | |||||
""" | |||||
sens = Tensor(np.array([1.0], inputs.dtype)) | |||||
# get grad of loss over x | |||||
out_grad = self._loss_grad(Tensor(inputs), Tensor(labels), sens) | |||||
if isinstance(out_grad, tuple): | |||||
out_grad = out_grad[0] | |||||
gradient = out_grad.asnumpy() | |||||
if self._is_targeted: | |||||
gradient = -gradient | |||||
return normalize_value(gradient, self._norm_level) | |||||
class ProjectedGradientDescent(BasicIterativeMethod): | |||||
""" | |||||
The Projected Gradient Descent attack is a variant of the Basic Iterative | |||||
Method in which, after each iteration, the perturbation is projected on an | |||||
lp-ball of specified radius (in addition to clipping the values of the | |||||
adversarial sample so that it lies in the permitted data range). This is | |||||
the attack proposed by Madry et al. for adversarial training. | |||||
References: `A. Madry, et al., "Towards deep learning models resistant to | |||||
adversarial attacks," in ICLR, 2018 <https://arxiv.org/abs/1706.06083>`_ | |||||
Args: | |||||
network (Cell): Target model. | |||||
eps (float): Proportion of adversarial perturbation generated by the | |||||
attack to data range. Default: 0.3. | |||||
eps_iter (float): Proportion of single-step adversarial perturbation | |||||
generated by the attack to data range. Default: 0.1. | |||||
bounds (tuple): Upper and lower bounds of data, indicating the data range. | |||||
In form of (clip_min, clip_max). Default: (0.0, 1.0). | |||||
is_targeted (bool): If True, targeted attack. If False, untargeted | |||||
attack. Default: False. | |||||
nb_iter (int): Number of iteration. Default: 5. | |||||
norm_level (Union[int, numpy.inf]): Order of the norm. Possible values: | |||||
np.inf, 1 or 2. Default: 'inf'. | |||||
loss_fn (Loss): Loss function for optimization. | |||||
""" | |||||
def __init__(self, network, eps=0.3, eps_iter=0.1, bounds=(0.0, 1.0), | |||||
is_targeted=False, nb_iter=5, norm_level='inf', loss_fn=None): | |||||
super(ProjectedGradientDescent, self).__init__(network, | |||||
eps=eps, | |||||
eps_iter=eps_iter, | |||||
bounds=bounds, | |||||
is_targeted=is_targeted, | |||||
nb_iter=nb_iter, | |||||
loss_fn=loss_fn) | |||||
self._norm_level = check_norm_level(norm_level) | |||||
def generate(self, inputs, labels): | |||||
""" | |||||
Iteratively generate adversarial examples based on BIM method. The | |||||
perturbation is normalized by projected method with parameter norm_level . | |||||
Args: | |||||
inputs (numpy.ndarray): Benign input samples used as references to | |||||
create adversarial examples. | |||||
labels (numpy.ndarray): Original/target labels. | |||||
Returns: | |||||
numpy.ndarray, generated adversarial examples. | |||||
Examples: | |||||
>>> adv_x = attack.generate([[0.6, 0.2, 0.6], | |||||
>>> [0.3, 0.3, 0.4]], | |||||
>>> [[0, 0, 0, 0, 0, 0, 0, 0, 0, 1], | |||||
>>> [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) | |||||
""" | |||||
inputs, labels = check_pair_numpy_param('inputs', inputs, | |||||
'labels', labels) | |||||
arr_x = inputs | |||||
if self._bounds is not None: | |||||
clip_min, clip_max = self._bounds | |||||
clip_diff = clip_max - clip_min | |||||
for _ in range(self._nb_iter): | |||||
adv_x = self._attack.generate(inputs, labels) | |||||
perturs = _projection(adv_x - arr_x, | |||||
self._eps, | |||||
norm_level=self._norm_level) | |||||
perturs = np.clip(perturs, (0 - self._eps)*clip_diff, | |||||
self._eps*clip_diff) | |||||
adv_x = arr_x + perturs | |||||
inputs = adv_x | |||||
else: | |||||
for _ in range(self._nb_iter): | |||||
adv_x = self._attack.generate(inputs, labels) | |||||
perturs = _projection(adv_x - arr_x, | |||||
self._eps, | |||||
norm_level=self._norm_level) | |||||
adv_x = arr_x + perturs | |||||
adv_x = np.clip(adv_x, arr_x - self._eps, arr_x + self._eps) | |||||
inputs = adv_x | |||||
return adv_x |
@@ -0,0 +1,196 @@ | |||||
# 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. | |||||
""" | |||||
JSMA-Attack. | |||||
""" | |||||
import numpy as np | |||||
from mindspore import Tensor | |||||
from mindspore.nn import Cell | |||||
from mindarmour.attacks.attack import Attack | |||||
from mindarmour.utils.util import GradWrap | |||||
from mindarmour.utils.util import jacobian_matrix | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.utils._check_param import check_pair_numpy_param, check_model, \ | |||||
check_param_type, check_int_positive, check_value_positive, \ | |||||
check_value_non_negative | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'JSMA' | |||||
class JSMAAttack(Attack): | |||||
""" | |||||
JSMA is an targeted & iterative attack based on saliency map of | |||||
input features. | |||||
Reference: `The limitations of deep learning in adversarial settings | |||||
<https://arxiv.org/abs/1511.07528>`_ | |||||
Args: | |||||
network (Cell): Target model. | |||||
num_classes (int): Number of labels of model output, which should be | |||||
greater than zero. | |||||
box_min (float): Lower bound of input of the target model. Default: 0. | |||||
box_max (float): Upper bound of input of the target model. Default: 1.0. | |||||
theta (float): Change ratio of one pixel (relative to | |||||
input data range). Default: 1.0. | |||||
max_iteration (int): Maximum round of iteration. Default: 100. | |||||
max_count (int): Maximum times to change each pixel. Default: 3. | |||||
increase (bool): If True, increase perturbation. If False, decrease | |||||
perturbation. Default: True. | |||||
sparse (bool): If True, input labels are sparse-coded. If False, | |||||
input labels are onehot-coded. Default: True. | |||||
Examples: | |||||
>>> attack = JSMAAttack(network) | |||||
""" | |||||
def __init__(self, network, num_classes, box_min=0.0, box_max=1.0, | |||||
theta=1.0, max_iteration=1000, max_count=3, increase=True, | |||||
sparse=True): | |||||
super(JSMAAttack).__init__() | |||||
LOGGER.debug(TAG, "init jsma class.") | |||||
self._network = check_model('network', network, Cell) | |||||
self._min = check_value_non_negative('box_min', box_min) | |||||
self._max = check_value_non_negative('box_max', box_max) | |||||
self._num_classes = check_int_positive('num_classes', num_classes) | |||||
self._theta = check_value_positive('theta', theta) | |||||
self._max_iter = check_int_positive('max_iteration', max_iteration) | |||||
self._max_count = check_int_positive('max_count', max_count) | |||||
self._increase = check_param_type('increase', increase, bool) | |||||
self._net_grad = GradWrap(self._network) | |||||
self._bit_map = None | |||||
self._sparse = check_param_type('sparse', sparse, bool) | |||||
def _saliency_map(self, data, bit_map, target): | |||||
""" | |||||
Compute the saliency map of all pixels. | |||||
Args: | |||||
data (numpy.ndarray): Input sample. | |||||
bit_map (numpy.ndarray): Bit map to control modify frequency of | |||||
each pixel. | |||||
target (int): Target class. | |||||
Returns: | |||||
tuple, indices of selected pixel to modify. | |||||
Examples: | |||||
>>> p1_ind, p2_ind = self._saliency_map([0.2, 0.3, 0.5], | |||||
>>> [1, 0, 1], 1) | |||||
""" | |||||
jaco_grad = jacobian_matrix(self._net_grad, data, self._num_classes) | |||||
jaco_grad = jaco_grad.reshape(self._num_classes, -1) | |||||
alpha = jaco_grad[target]*bit_map | |||||
alpha_trans = np.reshape(alpha, (alpha.shape[0], 1)) | |||||
alpha_two_dim = alpha + alpha_trans | |||||
# pixel influence on other classes except target class | |||||
other_grads = [jaco_grad[class_ind] for class_ind in range( | |||||
self._num_classes)] | |||||
beta = np.sum(other_grads, axis=0)*bit_map - alpha | |||||
beta_trans = np.reshape(beta, (beta.shape[0], 1)) | |||||
beta_two_dim = beta + beta_trans | |||||
if self._increase: | |||||
alpha_two_dim = (alpha_two_dim > 0)*alpha_two_dim | |||||
beta_two_dim = (beta_two_dim < 0)*beta_two_dim | |||||
else: | |||||
alpha_two_dim = (alpha_two_dim < 0)*alpha_two_dim | |||||
beta_two_dim = (beta_two_dim > 0)*beta_two_dim | |||||
sal_map = (-1*alpha_two_dim*beta_two_dim) | |||||
two_dim_index = np.argmax(sal_map) | |||||
p1_ind = two_dim_index % len(data.flatten()) | |||||
p2_ind = two_dim_index // len(data.flatten()) | |||||
return p1_ind, p2_ind | |||||
def _generate_one(self, data, target): | |||||
""" | |||||
Generate one adversarial example. | |||||
Args: | |||||
data (numpy.ndarray): Input sample (only one). | |||||
target (int): Target label. | |||||
Returns: | |||||
numpy.ndarray, adversarial example or zeros (if failed). | |||||
Examples: | |||||
>>> adv = self._generate_one([0.2, 0.3 ,0.4], 1) | |||||
""" | |||||
ori_shape = data.shape | |||||
temp = data.flatten() | |||||
bit_map = np.ones_like(temp) | |||||
fake_res = np.zeros_like(data) | |||||
counter = np.zeros_like(temp) | |||||
perturbed = np.copy(temp) | |||||
for _ in range(self._max_iter): | |||||
pre_logits = self._network(Tensor(np.expand_dims( | |||||
perturbed.reshape(ori_shape), axis=0))) | |||||
per_pred = np.argmax(pre_logits.asnumpy()) | |||||
if per_pred == target: | |||||
LOGGER.debug(TAG, 'find one adversarial sample successfully.') | |||||
return perturbed.reshape(ori_shape) | |||||
if np.all(bit_map == 0): | |||||
LOGGER.debug(TAG, 'fail to find adversarial sample') | |||||
return perturbed.reshape(ori_shape) | |||||
p1_ind, p2_ind = self._saliency_map(perturbed.reshape( | |||||
ori_shape)[np.newaxis, :], bit_map, target) | |||||
if self._increase: | |||||
perturbed[p1_ind] += self._theta*(self._max - self._min) | |||||
perturbed[p2_ind] += self._theta*(self._max - self._min) | |||||
else: | |||||
perturbed[p1_ind] -= self._theta*(self._max - self._min) | |||||
perturbed[p2_ind] -= self._theta*(self._max - self._min) | |||||
counter[p1_ind] += 1 | |||||
counter[p2_ind] += 1 | |||||
if (perturbed[p1_ind] >= self._max) or ( | |||||
perturbed[p1_ind] <= self._min) \ | |||||
or (counter[p1_ind] > self._max_count): | |||||
bit_map[p1_ind] = 0 | |||||
if (perturbed[p2_ind] >= self._max) or ( | |||||
perturbed[p2_ind] <= self._min) \ | |||||
or (counter[p2_ind] > self._max_count): | |||||
bit_map[p2_ind] = 0 | |||||
perturbed = np.clip(perturbed, self._min, self._max) | |||||
LOGGER.debug(TAG, 'fail to find adversarial sample.') | |||||
return fake_res | |||||
def generate(self, inputs, labels): | |||||
""" | |||||
Generate adversarial examples in batch. | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
labels (numpy.ndarray): Target labels. | |||||
Returns: | |||||
numpy.ndarray, adversarial samples. | |||||
Examples: | |||||
>>> advs = generate([[0.2, 0.3, 0.4], [0.3, 0.4, 0.5]], [1, 2]) | |||||
""" | |||||
inputs, labels = check_pair_numpy_param('inputs', inputs, | |||||
'labels', labels) | |||||
if not self._sparse: | |||||
labels = np.argmax(labels, axis=1) | |||||
LOGGER.debug(TAG, 'start to generate adversarial samples.') | |||||
res = [] | |||||
for i in range(inputs.shape[0]): | |||||
res.append(self._generate_one(inputs[i], labels[i])) | |||||
LOGGER.debug(TAG, 'finished.') | |||||
return np.asarray(res) |
@@ -0,0 +1,224 @@ | |||||
# 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. | |||||
""" | |||||
LBFGS-Attack. | |||||
""" | |||||
import numpy as np | |||||
import scipy.optimize as so | |||||
from mindspore import Tensor | |||||
from mindspore.nn import Cell | |||||
from mindspore.nn import SoftmaxCrossEntropyWithLogits | |||||
from mindarmour.attacks.attack import Attack | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.utils.util import WithLossCell | |||||
from mindarmour.utils.util import GradWrapWithLoss | |||||
from mindarmour.utils._check_param import check_pair_numpy_param, check_model, \ | |||||
check_int_positive, check_value_positive, check_param_type, \ | |||||
check_param_multi_types | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'LBFGS' | |||||
class LBFGS(Attack): | |||||
""" | |||||
Uses L-BFGS-B to minimize the distance between the input and the adversarial example. | |||||
References: `Pedro Tabacof, Eduardo Valle. "Exploring the Space of | |||||
Adversarial Images" <https://arxiv.org/abs/1510.05328>`_ | |||||
Args: | |||||
network (Cell): The network of attacked model. | |||||
eps (float): Attack step size. Default: 1e-5. | |||||
bounds (tuple): Upper and lower bounds of data. Default: (0.0, 1.0) | |||||
is_targeted (bool): If True, targeted attack. If False, untargeted | |||||
attack. Default: True. | |||||
nb_iter (int): Number of iteration of lbfgs-optimizer, which should be | |||||
greater than zero. Default: 150. | |||||
search_iters (int): Number of changes in step size, which should be | |||||
greater than zero. Default: 30. | |||||
loss_fn (Functions): Loss function of substitute model. Default: None. | |||||
sparse (bool): If True, input labels are sparse-coded. If False, | |||||
input labels are onehot-coded. Default: False. | |||||
Examples: | |||||
>>> attack = LBFGS(network) | |||||
""" | |||||
def __init__(self, network, eps=1e-5, bounds=(0.0, 1.0), is_targeted=True, | |||||
nb_iter=150, search_iters=30, loss_fn=None, sparse=False): | |||||
super(LBFGS, self).__init__() | |||||
self._network = check_model('network', network, Cell) | |||||
self._eps = check_value_positive('eps', eps) | |||||
self._is_targeted = check_param_type('is_targeted', is_targeted, bool) | |||||
self._nb_iter = check_int_positive('nb_iter', nb_iter) | |||||
self._search_iters = check_int_positive('search_iters', search_iters) | |||||
if loss_fn is None: | |||||
loss_fn = SoftmaxCrossEntropyWithLogits(is_grad=False, sparse=False) | |||||
with_loss_cell = WithLossCell(self._network, loss_fn) | |||||
self._grad_all = GradWrapWithLoss(with_loss_cell) | |||||
self._dtype = None | |||||
self._bounds = check_param_multi_types('bounds', bounds, [list, tuple]) | |||||
self._sparse = check_param_type('sparse', sparse, bool) | |||||
for b in self._bounds: | |||||
_ = check_param_multi_types('bound', b, [int, float]) | |||||
box_max, box_min = bounds | |||||
if box_max < box_min: | |||||
self._box_min = box_max | |||||
self._box_max = box_min | |||||
else: | |||||
self._box_min = box_min | |||||
self._box_max = box_max | |||||
def generate(self, inputs, labels): | |||||
""" | |||||
Generate adversarial examples based on input data and target labels. | |||||
Args: | |||||
inputs (numpy.ndarray): Benign input samples used as references to create | |||||
adversarial examples. | |||||
labels (numpy.ndarray): Original/target labels. | |||||
Returns: | |||||
numpy.ndarray, generated adversarial examples. | |||||
Examples: | |||||
>>> adv = attack.generate([[0.1, 0.2, 0.6], [0.3, 0, 0.4]], [2, 2]) | |||||
""" | |||||
LOGGER.debug(TAG, 'start to generate adv image.') | |||||
arr_x, arr_y = check_pair_numpy_param('inputs', inputs, 'labels', labels) | |||||
self._dtype = arr_x.dtype | |||||
adv_list = list() | |||||
for original_x, label_y in zip(arr_x, arr_y): | |||||
adv_list.append(self._optimize( | |||||
original_x, label_y, epsilon=self._eps)) | |||||
return np.array(adv_list) | |||||
def _forward_one(self, cur_input): | |||||
"""Forward one sample in model.""" | |||||
cur_input = np.expand_dims(cur_input, axis=0) | |||||
out_logits = self._network(Tensor(cur_input)).asnumpy() | |||||
return out_logits | |||||
def _gradient(self, cur_input, labels, shape): | |||||
""" Return model gradient to minimize loss in l-bfgs-b.""" | |||||
label_dtype = labels.dtype | |||||
sens = Tensor(np.array([1], self._dtype)) | |||||
labels = np.expand_dims(labels, axis=0).astype(label_dtype) | |||||
# input shape should like original shape | |||||
reshape_input = np.expand_dims(cur_input.reshape(shape), | |||||
axis=0) | |||||
out_grad = self._grad_all(Tensor(reshape_input), Tensor(labels), sens) | |||||
if isinstance(out_grad, tuple): | |||||
out_grad = out_grad[0] | |||||
return out_grad.asnumpy() | |||||
def _loss(self, cur_input, start_input, cur_eps, shape, labels): | |||||
""" | |||||
The l-bfgs-b loss used is Mean Square Error distance from original | |||||
input plus crossentropy loss. | |||||
""" | |||||
cur_input = cur_input.astype(self._dtype) | |||||
mse_distance = np.mean(np.square(start_input - cur_input)) / \ | |||||
((self._box_max - self._box_min)**2) | |||||
logits = self._forward_one(cur_input.reshape(shape)).flatten() | |||||
logits = logits - np.max(logits) | |||||
if self._sparse: | |||||
target_class = labels | |||||
else: | |||||
target_class = np.argmax(labels) | |||||
if self._is_targeted: | |||||
crossentropy = np.log(np.sum(np.exp(logits))) - logits[target_class] | |||||
gradient = self._gradient(cur_input, labels, shape).flatten() | |||||
else: | |||||
crossentropy = logits[target_class] - np.log(np.sum(np.exp(logits))) | |||||
gradient = -self._gradient(cur_input, labels, shape).flatten() | |||||
return (mse_distance + cur_eps*crossentropy).astype(self._dtype), \ | |||||
gradient.astype(np.float64) | |||||
def _lbfgsb(self, start_input, cur_eps, shape, labels, bounds): | |||||
""" | |||||
A wrapper. | |||||
Method reference to `scipy.optimize.fmin_l_bfgs_b`_ | |||||
.. _`scipy.optimize.fmin_l_bfgs_b`: https://docs.scipy.org/doc/scipy/ | |||||
reference/generated/scipy.optimize.fmin_l_bfgs_b.html | |||||
""" | |||||
approx_grad_eps = (self._box_max - self._box_min) / 100 | |||||
max_matrix_variable = 15 | |||||
cur_input, _, detail_info = so.fmin_l_bfgs_b( | |||||
self._loss, | |||||
start_input, | |||||
args=(start_input, cur_eps, shape, labels), | |||||
approx_grad=False, | |||||
bounds=bounds, | |||||
m=max_matrix_variable, | |||||
maxiter=self._nb_iter, | |||||
epsilon=approx_grad_eps) | |||||
LOGGER.debug(TAG, str(detail_info)) | |||||
# LBFGS-B does not always exactly respect the boundaries | |||||
if np.amax(cur_input) > self._box_max or np.amin( | |||||
cur_input) < self._box_min: # pragma: no coverage | |||||
LOGGER.debug(TAG, | |||||
'Input out of bounds (min, max = %s, %s).' | |||||
' Performing manual clip.', | |||||
np.amin(cur_input), | |||||
np.amax(cur_input)) | |||||
cur_input = np.clip(cur_input, self._box_min, self._box_max) | |||||
cur_input = cur_input.astype(self._dtype) | |||||
cur_input = cur_input.reshape(shape) | |||||
adv_prediction = self._forward_one(cur_input) | |||||
LOGGER.debug(TAG, 'input one sample label is :{}'.format(labels)) | |||||
if not self._sparse: | |||||
labels = np.argmax(labels) | |||||
if self._is_targeted: | |||||
return cur_input, np.argmax(adv_prediction) == labels | |||||
return cur_input, np.argmax(adv_prediction) != labels | |||||
def _optimize(self, start_input, labels, epsilon): | |||||
""" | |||||
Given loss fuction and gradient, use l_bfgs_b algorithm to update input | |||||
sample. The epsilon will be doubled until an adversarial example is found. | |||||
Args: | |||||
start_input (numpy.ndarray): Benign input samples used as references | |||||
to create adversarial examples. | |||||
labels (numpy.ndarray): Target labels. | |||||
epsilon: (float): Attack step size. | |||||
max_iter (int): Number of iteration. | |||||
""" | |||||
# store the shape for later and operate on the flattened input | |||||
ori_shape = start_input.shape | |||||
start_input = start_input.flatten().astype(self._dtype) | |||||
bounds = [self._bounds]*len(start_input) | |||||
# finding initial cur_eps | |||||
iter_c = epsilon | |||||
for _ in range(self._search_iters): | |||||
iter_c = 2*iter_c | |||||
generate_x, is_adversarial = self._lbfgsb(start_input, | |||||
iter_c, | |||||
ori_shape, | |||||
labels, | |||||
bounds) | |||||
LOGGER.debug(TAG, 'Tested iter_c = %f', iter_c) | |||||
if is_adversarial: | |||||
LOGGER.debug(TAG, 'find adversarial successfully.') | |||||
return generate_x | |||||
LOGGER.debug(TAG, 'failed to not adversarial.') | |||||
return generate_x |
@@ -0,0 +1,15 @@ | |||||
""" | |||||
This module includes classical defense algorithms in defencing adversarial | |||||
examples and enhancing model security and trustworthy. | |||||
""" | |||||
from .adversarial_defense import AdversarialDefense | |||||
from .adversarial_defense import AdversarialDefenseWithAttacks | |||||
from .adversarial_defense import EnsembleAdversarialDefense | |||||
from .natural_adversarial_defense import NaturalAdversarialDefense | |||||
from .projected_adversarial_defense import ProjectedAdversarialDefense | |||||
__all__ = ['AdversarialDefense', | |||||
'AdversarialDefenseWithAttacks', | |||||
'NaturalAdversarialDefense', | |||||
'ProjectedAdversarialDefense', | |||||
'EnsembleAdversarialDefense'] |
@@ -0,0 +1,169 @@ | |||||
# 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. | |||||
""" | |||||
Adversarial Defense. | |||||
""" | |||||
import numpy as np | |||||
from mindspore import Tensor | |||||
from mindspore.nn import Cell | |||||
from mindspore.nn.optim.momentum import Momentum | |||||
from mindspore.nn import SoftmaxCrossEntropyWithLogits | |||||
from mindspore.nn import WithLossCell, TrainOneStepCell | |||||
from mindarmour.utils._check_param import check_pair_numpy_param, check_model, \ | |||||
check_param_in_range, check_param_type, check_param_multi_types | |||||
from mindarmour.defenses.defense import Defense | |||||
class AdversarialDefense(Defense): | |||||
""" | |||||
Adversarial training using given adversarial examples. | |||||
Args: | |||||
network (Cell): A MindSpore network to be defensed. | |||||
loss_fn (Functions): Loss function. Default: None. | |||||
optimizer (Cell): Optimizer used to train the network. Default: None. | |||||
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 | |||||
>>> | |||||
>>> net = Net() | |||||
>>> lr = 0.0001 | |||||
>>> momentum = 0.9 | |||||
>>> loss_fn = SoftmaxCrossEntropyWithLogits(is_grad=False, sparse=True) | |||||
>>> optimizer = Momentum(net.trainable_params(), lr, momentum) | |||||
>>> 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) | |||||
>>> adv_defense.defense(inputs, labels) | |||||
""" | |||||
def __init__(self, network, loss_fn=None, optimizer=None): | |||||
super(AdversarialDefense, self).__init__(network) | |||||
network = check_model('network', network, Cell) | |||||
if loss_fn is None: | |||||
loss_fn = SoftmaxCrossEntropyWithLogits(is_grad=False, sparse=True) | |||||
if optimizer is None: | |||||
optimizer = Momentum( | |||||
params=network.trainable_params(), | |||||
learning_rate=0.01, | |||||
momentum=0.9) | |||||
loss_net = WithLossCell(network, loss_fn) | |||||
self._train_net = TrainOneStepCell(loss_net, optimizer) | |||||
self._train_net.set_train() | |||||
def defense(self, inputs, labels): | |||||
""" | |||||
Enhance model via training with input samples. | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
labels (numpy.ndarray): Labels of input samples. | |||||
Returns: | |||||
numpy.ndarray, loss of defense operation. | |||||
""" | |||||
inputs, labels = check_pair_numpy_param('inputs', inputs, 'labels', | |||||
labels) | |||||
loss = self._train_net(Tensor(inputs), Tensor(labels)) | |||||
return loss.asnumpy() | |||||
class AdversarialDefenseWithAttacks(AdversarialDefense): | |||||
""" | |||||
Adversarial defense with attacks. | |||||
Args: | |||||
network (Cell): A MindSpore network to be defensed. | |||||
attacks (list[Attack]): List of attack method. | |||||
loss_fn (Functions): Loss function. Default: None. | |||||
optimizer (Cell): Optimizer used to train the network. Default: None. | |||||
bounds (tuple): Upper and lower bounds of data. In form of (clip_min, | |||||
clip_max). Default: (0.0, 1.0). | |||||
replace_ratio (float): Ratio of replacing original samples with | |||||
adversarial, which must be between 0 and 1. Default: 0.5. | |||||
Raises: | |||||
ValueError: If replace_ratio is not between 0 and 1. | |||||
Examples: | |||||
>>> net = Net() | |||||
>>> fgsm = FastGradientSignMethod(net) | |||||
>>> pgd = ProjectedGradientDescent(net) | |||||
>>> ead = AdversarialDefenseWithAttacks(net, [fgsm, pgd]) | |||||
>>> ead.defense(inputs, labels) | |||||
""" | |||||
def __init__(self, network, attacks, loss_fn=None, optimizer=None, | |||||
bounds=(0.0, 1.0), replace_ratio=0.5): | |||||
super(AdversarialDefenseWithAttacks, self).__init__(network, | |||||
loss_fn, | |||||
optimizer) | |||||
self._attacks = check_param_type('attacks', attacks, list) | |||||
self._bounds = check_param_multi_types('bounds', bounds, [tuple, list]) | |||||
for elem in self._bounds: | |||||
_ = check_param_multi_types('bound', elem, [int, float]) | |||||
self._replace_ratio = check_param_in_range('replace_ratio', | |||||
replace_ratio, | |||||
0, 1) | |||||
def defense(self, inputs, labels): | |||||
""" | |||||
Enhance model via training with adversarial examples generated from input samples. | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
labels (numpy.ndarray): Labels of input samples. | |||||
Returns: | |||||
numpy.ndarray, loss of adversarial defense operation. | |||||
""" | |||||
inputs, labels = check_pair_numpy_param('inputs', inputs, 'labels', | |||||
labels) | |||||
x_len = inputs.shape[0] | |||||
n_adv = int(np.ceil(self._replace_ratio*x_len)) | |||||
n_adv_per_attack = int(n_adv / len(self._attacks)) | |||||
adv_ids = np.random.choice(x_len, size=n_adv, replace=False) | |||||
start = 0 | |||||
for attack in self._attacks: | |||||
idx = adv_ids[start:start + n_adv_per_attack] | |||||
inputs[idx] = attack.generate(inputs[idx], labels[idx]) | |||||
start += n_adv_per_attack | |||||
loss = self._train_net(Tensor(inputs), Tensor(labels)) | |||||
return loss.asnumpy() | |||||
EnsembleAdversarialDefense = AdversarialDefenseWithAttacks |
@@ -0,0 +1,86 @@ | |||||
# 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. | |||||
""" | |||||
Base Class of Defense. | |||||
""" | |||||
from abc import abstractmethod | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.utils._check_param import check_pair_numpy_param, \ | |||||
check_int_positive | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'Defense' | |||||
class Defense: | |||||
""" | |||||
The abstract base class for all defense classes defending adversarial | |||||
examples. | |||||
Args: | |||||
network (Cell): A MindSpore-style deep learning model to be defensed. | |||||
""" | |||||
def __init__(self, network): | |||||
self._network = network | |||||
@abstractmethod | |||||
def defense(self, inputs, labels): | |||||
""" | |||||
Defense model with samples. | |||||
Args: | |||||
inputs (numpy.ndarray): Samples based on which adversarial | |||||
examples are generated. | |||||
labels (numpy.ndarray): Labels of input samples. | |||||
Raises: | |||||
NotImplementedError: It is an abstract method. | |||||
""" | |||||
msg = 'The function defense() is an abstract function in class ' \ | |||||
'`Defense` and should be implemented in child class.' | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) | |||||
def batch_defense(self, inputs, labels, batch_size=32, epochs=5): | |||||
""" | |||||
Defense model with samples in batch. | |||||
Args: | |||||
inputs (numpy.ndarray): Samples based on which adversarial | |||||
examples are generated. | |||||
labels (numpy.ndarray): Labels of input samples. | |||||
batch_size (int): Number of samples in one batch. | |||||
epochs (int): Number of epochs. | |||||
Returns: | |||||
numpy.ndarray, loss of batch_defense operation. | |||||
Raises: | |||||
ValueError: If batch_size is 0. | |||||
""" | |||||
inputs, labels = check_pair_numpy_param('inputs', inputs, 'labels', | |||||
labels) | |||||
x_len = len(inputs) | |||||
batch_size = check_int_positive('batch_size', batch_size) | |||||
iters_per_epoch = int(x_len / batch_size) | |||||
loss = None | |||||
for _ in range(epochs): | |||||
for step in range(iters_per_epoch): | |||||
x_batch = inputs[step*batch_size:(step + 1)*batch_size] | |||||
y_batch = labels[step*batch_size:(step + 1)*batch_size] | |||||
loss = self.defense(x_batch, y_batch) | |||||
return loss |
@@ -0,0 +1,56 @@ | |||||
# 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. | |||||
""" | |||||
Natural Adversarial Defense. | |||||
""" | |||||
from mindarmour.defenses.adversarial_defense import \ | |||||
AdversarialDefenseWithAttacks | |||||
from mindarmour.attacks.gradient_method import FastGradientSignMethod | |||||
class NaturalAdversarialDefense(AdversarialDefenseWithAttacks): | |||||
""" | |||||
Adversarial training based on FGSM. | |||||
Reference: `A. Kurakin, et al., "Adversarial machine learning at scale," in | |||||
ICLR, 2017. <https://arxiv.org/abs/1611.01236>`_ | |||||
Args: | |||||
network (Cell): A MindSpore network to be defensed. | |||||
loss_fn (Functions): Loss function. Default: None. | |||||
optimizer (Cell): Optimizer used to train the network. Default: None. | |||||
bounds (tuple): Upper and lower bounds of data. In form of (clip_min, | |||||
clip_max). Default: (0.0, 1.0). | |||||
replace_ratio (float): Ratio of replacing original samples with | |||||
adversarial samples. Default: 0.5. | |||||
eps (float): Step size of the attack method(FGSM). Default: 0.1. | |||||
Examples: | |||||
>>> net = Net() | |||||
>>> adv_defense = NaturalAdversarialDefense(net) | |||||
>>> adv_defense.defense(inputs, labels) | |||||
""" | |||||
def __init__(self, network, loss_fn=None, optimizer=None, | |||||
bounds=(0.0, 1.0), replace_ratio=0.5, eps=0.1): | |||||
attack = FastGradientSignMethod(network, | |||||
eps=eps, | |||||
alpha=None, | |||||
bounds=bounds) | |||||
super(NaturalAdversarialDefense, self).__init__( | |||||
network, | |||||
[attack], | |||||
loss_fn=loss_fn, | |||||
optimizer=optimizer, | |||||
bounds=bounds, | |||||
replace_ratio=replace_ratio) |
@@ -0,0 +1,69 @@ | |||||
# 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. | |||||
""" | |||||
Projected Adversarial Defense. | |||||
""" | |||||
from mindarmour.defenses.adversarial_defense import \ | |||||
AdversarialDefenseWithAttacks | |||||
from mindarmour.attacks.iterative_gradient_method import \ | |||||
ProjectedGradientDescent | |||||
class ProjectedAdversarialDefense(AdversarialDefenseWithAttacks): | |||||
""" | |||||
Adversarial training based on PGD. | |||||
Reference: `A. Madry, et al., "Towards deep learning models resistant to | |||||
adversarial attacks," in ICLR, 2018. <https://arxiv.org/abs/1611.01236>`_ | |||||
Args: | |||||
network (Cell): A MindSpore network to be defensed. | |||||
loss_fn (Functions): Loss function. Default: None. | |||||
optimizer (Cell): Optimizer used to train the nerwork. Default: None. | |||||
bounds (tuple): Upper and lower bounds of input data. In form of | |||||
(clip_min, clip_max). Default: (0.0, 1.0). | |||||
replace_ratio (float): Ratio of replacing original samples with | |||||
adversarial samples. Default: 0.5. | |||||
eps (float): PGD attack parameters, epsilon. Default: 0.3. | |||||
eps_iter (int): PGD attack parameters, inner loop epsilon. | |||||
Default:0.1. | |||||
nb_iter (int): PGD attack parameters, number of iteration. | |||||
Default: 5. | |||||
norm_level (str): Norm type. 'inf' or 'l2'. Default: 'inf'. | |||||
Examples: | |||||
>>> net = Net() | |||||
>>> adv_defense = ProjectedAdversarialDefense(net) | |||||
>>> adv_defense.defense(inputs, labels) | |||||
""" | |||||
def __init__(self, | |||||
network, | |||||
loss_fn=None, | |||||
optimizer=None, | |||||
bounds=(0.0, 1.0), | |||||
replace_ratio=0.5, | |||||
eps=0.3, | |||||
eps_iter=0.1, | |||||
nb_iter=5, | |||||
norm_level='inf'): | |||||
attack = ProjectedGradientDescent(network, | |||||
eps=eps, | |||||
eps_iter=eps_iter, | |||||
nb_iter=nb_iter, | |||||
bounds=bounds, | |||||
norm_level=norm_level, | |||||
loss_fn=loss_fn) | |||||
super(ProjectedAdversarialDefense, self).__init__( | |||||
network, [attack], loss_fn=loss_fn, optimizer=optimizer, | |||||
bounds=bounds, replace_ratio=replace_ratio) |
@@ -0,0 +1,18 @@ | |||||
""" | |||||
This module includes detector methods on distinguishing adversarial examples | |||||
from benign examples. | |||||
""" | |||||
from .mag_net import ErrorBasedDetector | |||||
from .mag_net import DivergenceBasedDetector | |||||
from .ensemble_detector import EnsembleDetector | |||||
from .region_based_detector import RegionBasedDetector | |||||
from .spatial_smoothing import SpatialSmoothing | |||||
from . import black | |||||
from .black.similarity_detector import SimilarityDetector | |||||
__all__ = ['ErrorBasedDetector', | |||||
'DivergenceBasedDetector', | |||||
'RegionBasedDetector', | |||||
'SpatialSmoothing', | |||||
'EnsembleDetector', | |||||
'SimilarityDetector'] |
@@ -0,0 +1,284 @@ | |||||
# 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. | |||||
""" | |||||
Similarity Detector. | |||||
""" | |||||
import itertools | |||||
import numpy as np | |||||
from mindspore import Tensor | |||||
from mindspore import Model | |||||
from mindarmour.detectors.detector import Detector | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.utils._check_param import check_model, check_numpy_param, \ | |||||
check_int_positive, check_value_positive, check_param_type, \ | |||||
check_param_in_range | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'SimilarityDetector' | |||||
def _pairwise_distances(x_input, y_input): | |||||
""" | |||||
Compute the Euclidean Distance matrix from a vector array x_input and | |||||
y_input. | |||||
Args: | |||||
x_input (numpy.ndarray): input data, [n_samples_x, n_features] | |||||
y_input (numpy.ndarray): input data, [n_samples_y, n_features] | |||||
Returns: | |||||
numpy.ndarray, distance matrix, [n_samples_a, n_samples_b] | |||||
""" | |||||
out = np.empty((x_input.shape[0], y_input.shape[0]), dtype='float') | |||||
iterator = itertools.product( | |||||
range(x_input.shape[0]), range(y_input.shape[0])) | |||||
for i, j in iterator: | |||||
out[i, j] = np.linalg.norm(x_input[i] - y_input[j]) | |||||
return out | |||||
class SimilarityDetector(Detector): | |||||
""" | |||||
The detector measures similarity among adjacent queries and rejects queries | |||||
which are remarkably similar to previous queries. | |||||
Reference: `Stateful Detection of Black-Box Adversarial Attacks by Steven | |||||
Chen, Nicholas Carlini, and David Wagner. at arxiv 2019 | |||||
<https://arxiv.org/abs/1907.05587>`_ | |||||
Args: | |||||
trans_model (Model): A MindSpore model to encode input data into lower | |||||
dimension vector. | |||||
max_k_neighbor (int): The maximum number of the nearest neighbors. | |||||
Default: 1000. | |||||
chunk_size (int): Buffer size. Default: 1000. | |||||
max_buffer_size (int): Maximum buffer size. Default: 10000. | |||||
tuning (bool): Calculate the average distance for the nearest k | |||||
neighbours, if tuning is true, k=K. If False k=1,...,K. | |||||
Default: False. | |||||
fpr (float): False positive ratio on legitimate query sequences. | |||||
Default: 0.001 | |||||
Examples: | |||||
>>> detector = SimilarityDetector(model) | |||||
>>> detector.fit(Tensor(ori), Tensor(labels)) | |||||
>>> adv_ids = detector.detect(Tensor(adv)) | |||||
""" | |||||
def __init__(self, trans_model, max_k_neighbor=1000, chunk_size=1000, | |||||
max_buffer_size=10000, tuning=False, fpr=0.001): | |||||
super(SimilarityDetector, self).__init__() | |||||
self._max_k_neighbor = check_int_positive('max_k_neighbor', | |||||
max_k_neighbor) | |||||
self._trans_model = check_model('trans_model', trans_model, Model) | |||||
self._tuning = check_param_type('tuning', tuning, bool) | |||||
self._chunk_size = check_int_positive('chunk_size', chunk_size) | |||||
self._max_buffer_size = check_int_positive('max_buffer_size', | |||||
max_buffer_size) | |||||
self._fpr = check_param_in_range('fpr', fpr, 0, 1) | |||||
self._num_of_neighbors = None | |||||
self._threshold = None | |||||
self._num_queries = 0 | |||||
# Stores recently processed queries | |||||
self._buffer = [] | |||||
# Tracks indexes of detected queries | |||||
self._detected_queries = [] | |||||
def fit(self, inputs, labels=None): | |||||
""" | |||||
Process input training data to calculate the threshold. | |||||
A proper threshold should make sure the false positive | |||||
rate is under a given value. | |||||
Args: | |||||
inputs (numpy.ndarray): Training data to calculate the threshold. | |||||
labels (numpy.ndarray): Labels of training data. | |||||
Returns: | |||||
- list[int], number of the nearest neighbors. | |||||
- list[float], calculated thresholds for different K. | |||||
Raises: | |||||
ValueError: The number of training data is less than | |||||
max_k_neighbor! | |||||
""" | |||||
data = check_numpy_param('inputs', inputs) | |||||
data_len = data.shape[0] | |||||
if data_len < self._max_k_neighbor: | |||||
raise ValueError('The number of training data must be larger than ' | |||||
'max_k_neighbor!') | |||||
data = self._trans_model.predict(Tensor(data)).asnumpy() | |||||
data = data.reshape((data.shape[0], -1)) | |||||
distances = [] | |||||
for i in range(data.shape[0] // self._chunk_size): | |||||
distance_mat = _pairwise_distances( | |||||
x_input=data[i*self._chunk_size:(i + 1)*self._chunk_size, :], | |||||
y_input=data) | |||||
distance_mat = np.sort(distance_mat, axis=-1) | |||||
distances.append(distance_mat[:, :self._max_k_neighbor]) | |||||
# the rest | |||||
distance_mat = _pairwise_distances(x_input=data[(data.shape[0] // | |||||
self._chunk_size)* | |||||
self._chunk_size:, :], | |||||
y_input=data) | |||||
distance_mat = np.sort(distance_mat, axis=-1) | |||||
distances.append(distance_mat[:, :self._max_k_neighbor]) | |||||
distance_matrix = np.concatenate(distances, axis=0) | |||||
start = 1 if self._tuning else self._max_k_neighbor | |||||
thresholds = [] | |||||
num_nearest_neighbors = [] | |||||
for k in range(start, self._max_k_neighbor + 1): | |||||
avg_dist = distance_matrix[:, :k].mean(axis=-1) | |||||
index = int(len(avg_dist)*self._fpr) | |||||
threshold = np.sort(avg_dist, axis=None)[index] | |||||
num_nearest_neighbors.append(k) | |||||
thresholds.append(threshold) | |||||
if thresholds: | |||||
self._threshold = thresholds[-1] | |||||
self._num_of_neighbors = num_nearest_neighbors[-1] | |||||
return num_nearest_neighbors, thresholds | |||||
def detect(self, inputs): | |||||
""" | |||||
Process queries to detect black-box attack. | |||||
Args: | |||||
inputs (numpy.ndarray): Query sequence. | |||||
Raises: | |||||
ValueError: The parameters of threshold or num_of_neighbors is | |||||
not available. | |||||
""" | |||||
if self._threshold is None or self._num_of_neighbors is None: | |||||
msg = 'Explicit detection threshold and number of nearest ' \ | |||||
'neighbors must be provided using set_threshold(), ' \ | |||||
'or call fit() to calculate.' | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
queries = check_numpy_param('inputs', inputs) | |||||
queries = self._trans_model.predict(Tensor(queries)).asnumpy() | |||||
queries = queries.reshape((queries.shape[0], -1)) | |||||
for query in queries: | |||||
self._process_query(query) | |||||
def _process_query(self, query): | |||||
""" | |||||
Process each query to detect black-box attack. | |||||
Args: | |||||
query (numpy.ndarray): Query input. | |||||
""" | |||||
if len(self._buffer) < self._num_of_neighbors: | |||||
self._buffer.append(query) | |||||
self._num_queries += 1 | |||||
return | |||||
k = self._num_of_neighbors | |||||
if self._buffer: | |||||
queries = np.stack(self._buffer, axis=0) | |||||
dists = np.linalg.norm(queries - query, axis=-1) | |||||
k_nearest_dists = np.partition(dists, k - 1)[:k, None] | |||||
k_avg_dist = np.mean(k_nearest_dists) | |||||
self._buffer.append(query) | |||||
self._num_queries += 1 | |||||
if len(self._buffer) >= self._max_buffer_size: | |||||
self.clear_buffer() | |||||
# an attack is detected | |||||
if k_avg_dist < self._threshold: | |||||
self._detected_queries.append(self._num_queries) | |||||
self.clear_buffer() | |||||
def clear_buffer(self): | |||||
""" | |||||
Clear the buffer memory. | |||||
""" | |||||
while self._buffer: | |||||
self._buffer.pop() | |||||
def set_threshold(self, num_of_neighbors, threshold): | |||||
""" | |||||
Set the parameters num_of_neighbors and threshold. | |||||
Args: | |||||
num_of_neighbors (int): Number of the nearest neighbors. | |||||
threshold (float): Detection threshold. Default: None. | |||||
""" | |||||
self._num_of_neighbors = check_int_positive('num_of_neighbors', | |||||
num_of_neighbors) | |||||
self._threshold = check_value_positive('threshold', threshold) | |||||
def get_detection_interval(self): | |||||
""" | |||||
Get the interval between adjacent detections. | |||||
Returns: | |||||
list[int], number of queries between adjacent detections. | |||||
""" | |||||
detected_queries = self._detected_queries | |||||
interval = [] | |||||
for i in range(len(detected_queries) - 1): | |||||
interval.append(detected_queries[i + 1] - detected_queries[i]) | |||||
return interval | |||||
def get_detected_queries(self): | |||||
""" | |||||
Get the indexes of detected queries. | |||||
Returns: | |||||
list[int], sequence number of detected malicious queries. | |||||
""" | |||||
detected_queries = self._detected_queries | |||||
return detected_queries | |||||
def detect_diff(self, inputs): | |||||
""" | |||||
Detect adversarial samples from input samples, like the predict_proba | |||||
function in common machine learning model. | |||||
Args: | |||||
inputs (Union[numpy.ndarray, list, tuple]): Data been used as | |||||
references to create adversarial examples. | |||||
Raises: | |||||
NotImplementedError: This function is not available | |||||
in class `SimilarityDetector`. | |||||
""" | |||||
msg = 'The function detect_diff() is not available in the class ' \ | |||||
'`SimilarityDetector`.' | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) | |||||
def transform(self, inputs): | |||||
""" | |||||
Filter adversarial noises in input samples. | |||||
Raises: | |||||
NotImplementedError: This function is not available | |||||
in class `SimilarityDetector`. | |||||
""" | |||||
msg = 'The function transform() is not available in the class ' \ | |||||
'`SimilarityDetector`.' | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) |
@@ -0,0 +1,101 @@ | |||||
# 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. | |||||
""" | |||||
Base Class of Detector. | |||||
""" | |||||
from abc import abstractmethod | |||||
from mindarmour.utils.logger import LogUtil | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'Detector' | |||||
class Detector: | |||||
""" | |||||
The abstract base class for all adversarial example detectors. | |||||
""" | |||||
def __init__(self): | |||||
pass | |||||
@abstractmethod | |||||
def fit(self, inputs, labels=None): | |||||
""" | |||||
Fit a threshold and refuse adversarial examples whose difference from | |||||
their denoised versions are larger than the threshold. The threshold is | |||||
determined by a certain false positive rate when applying to normal samples. | |||||
Args: | |||||
inputs (numpy.ndarray): The input samples to calculate the threshold. | |||||
labels (numpy.ndarray): Labels of training data. | |||||
Raises: | |||||
NotImplementedError: It is an abstract method. | |||||
""" | |||||
msg = 'The function fit() is an abstract function in class ' \ | |||||
'`Detector` and should be implemented in child class.' | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) | |||||
@abstractmethod | |||||
def detect(self, inputs): | |||||
""" | |||||
Detect adversarial examples from input samples. | |||||
Args: | |||||
inputs (Union[numpy.ndarray, list, tuple]): The input samples to be | |||||
detected. | |||||
Raises: | |||||
NotImplementedError: It is an abstract method. | |||||
""" | |||||
msg = 'The function detect() is an abstract function in class ' \ | |||||
'`Detector` and should be implemented in child class.' | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) | |||||
@abstractmethod | |||||
def detect_diff(self, inputs): | |||||
""" | |||||
Calculate the difference between the input samples and de-noised samples. | |||||
Args: | |||||
inputs (Union[numpy.ndarray, list, tuple]): The input samples to be | |||||
detected. | |||||
Raises: | |||||
NotImplementedError: It is an abstract method. | |||||
""" | |||||
msg = 'The function detect_diff() is an abstract function in class ' \ | |||||
'`Detector` and should be implemented in child class.' | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) | |||||
@abstractmethod | |||||
def transform(self, inputs): | |||||
""" | |||||
Filter adversarial noises in input samples. | |||||
Args: | |||||
inputs (Union[numpy.ndarray, list, tuple]): The input samples to be | |||||
transformed. | |||||
Raises: | |||||
NotImplementedError: It is an abstract method. | |||||
""" | |||||
msg = 'The function transform() is an abstract function in class ' \ | |||||
'`Detector` and should be implemented in child class.' | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) |
@@ -0,0 +1,126 @@ | |||||
# 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. | |||||
""" | |||||
Ensemble Detector. | |||||
""" | |||||
import numpy as np | |||||
from mindarmour.detectors.detector import Detector | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.utils._check_param import check_numpy_param, \ | |||||
check_param_multi_types | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'EnsembleDetector' | |||||
class EnsembleDetector(Detector): | |||||
""" | |||||
Ensemble detector. | |||||
Args: | |||||
detectors (Union[tuple, list]): List of detector methods. | |||||
policy (str): Decision policy, could be 'vote', 'all' or 'any'. | |||||
Default: 'vote' | |||||
""" | |||||
def __init__(self, detectors, policy="vote"): | |||||
super(EnsembleDetector, self).__init__() | |||||
self._detectors = check_param_multi_types('detectors', detectors, | |||||
[list, tuple]) | |||||
self._num_detectors = len(detectors) | |||||
self._policy = policy | |||||
def fit(self, inputs, labels=None): | |||||
""" | |||||
Fit detector like a machine learning model. This method is not available | |||||
in this class. | |||||
Args: | |||||
inputs (numpy.ndarray): Data to calculate the threshold. | |||||
labels (numpy.ndarray): Labels of data. | |||||
Raises: | |||||
NotImplementedError: This function is not available in ensemble. | |||||
""" | |||||
msg = 'The function fit() is not available in the class ' \ | |||||
'`EnsembleDetector`.' | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) | |||||
def detect(self, inputs): | |||||
""" | |||||
Detect adversarial examples from input samples. | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
Returns: | |||||
list[int], whether a sample is adversarial. if res[i]=1, then the | |||||
input sample with index i is adversarial. | |||||
Raises: | |||||
ValueError: If policy is not supported. | |||||
""" | |||||
inputs = check_numpy_param('inputs', inputs) | |||||
x_len = inputs.shape[0] | |||||
counts = np.zeros(x_len) | |||||
res = np.zeros(x_len, dtype=np.int) | |||||
for detector in list(self._detectors): | |||||
idx = detector.detect(inputs) | |||||
counts[idx] += 1 | |||||
if self._policy == "vote": | |||||
idx_adv = np.argwhere(counts > self._num_detectors / 2) | |||||
elif self._policy == "all": | |||||
idx_adv = np.argwhere(counts == self._num_detectors) | |||||
elif self._policy == "any": | |||||
idx_adv = np.argwhere(counts > 0) | |||||
else: | |||||
msg = 'Policy {} is not supported.'.format(self._policy) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
res[idx_adv] = 1 | |||||
return list(res) | |||||
def detect_diff(self, inputs): | |||||
""" | |||||
This method is not available in this class. | |||||
Args: | |||||
inputs (Union[numpy.ndarray, list, tuple]): Data been used as | |||||
references to create adversarial examples. | |||||
Raises: | |||||
NotImplementedError: This function is not available in ensemble. | |||||
""" | |||||
msg = 'The function detect_diff() is not available in the class ' \ | |||||
'`EnsembleDetector`.' | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) | |||||
def transform(self, inputs): | |||||
""" | |||||
Filter adversarial noises in input samples. | |||||
This method is not available in this class. | |||||
Raises: | |||||
NotImplementedError: This function is not available in ensemble. | |||||
""" | |||||
msg = 'The function transform() is not available in the class ' \ | |||||
'`EnsembleDetector`.' | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) |
@@ -0,0 +1,228 @@ | |||||
# 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. | |||||
""" | |||||
Error-Based detector. | |||||
""" | |||||
import numpy as np | |||||
from scipy import stats | |||||
from scipy.special import softmax | |||||
from mindspore import Tensor | |||||
from mindspore import Model | |||||
from mindarmour.detectors.detector import Detector | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.utils._check_param import check_numpy_param, check_model, \ | |||||
check_param_in_range, check_param_multi_types, check_int_positive, \ | |||||
check_value_positive | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'MagNet' | |||||
class ErrorBasedDetector(Detector): | |||||
""" | |||||
The detector reconstructs input samples, measures reconstruction errors and | |||||
rejects samples with large reconstruction errors. | |||||
Reference: `MagNet: a Two-Pronged Defense against Adversarial Examples, | |||||
by Dongyu Meng and Hao Chen, at CCS 2017. | |||||
<https://arxiv.org/abs/1705.09064>`_ | |||||
Args: | |||||
auto_encoder (Model): An (trained) auto encoder which | |||||
represents the input by reduced encoding. | |||||
false_positive_rate (float): Detector's false positive rate. | |||||
Default: 0.01. | |||||
bounds (tuple): (clip_min, clip_max). Default: (0.0, 1.0). | |||||
""" | |||||
def __init__(self, auto_encoder, false_positive_rate=0.01, | |||||
bounds=(0.0, 1.0)): | |||||
super(ErrorBasedDetector, self).__init__() | |||||
self._auto_encoder = check_model('auto_encoder', auto_encoder, Model) | |||||
self._false_positive_rate = check_param_in_range('false_positive_rate', | |||||
false_positive_rate, | |||||
0, 1) | |||||
self._threshold = 0.0 | |||||
self._bounds = check_param_multi_types('bounds', bounds, [list, tuple]) | |||||
for b in self._bounds: | |||||
_ = check_param_multi_types('bound', b, [int, float]) | |||||
def fit(self, inputs, labels=None): | |||||
""" | |||||
Find a threshold for a given dataset to distinguish adversarial examples. | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
labels (numpy.ndarray): Labels of input samples. Default: None. | |||||
Returns: | |||||
float, threshold to distinguish adversarial samples from benign ones. | |||||
""" | |||||
inputs = check_numpy_param('inputs', inputs) | |||||
marks = self.detect_diff(inputs) | |||||
num = int(inputs.shape[0]*self._false_positive_rate) | |||||
marks = np.sort(marks) | |||||
if num <= len(marks): | |||||
self._threshold = marks[-num] | |||||
return self._threshold | |||||
def detect(self, inputs): | |||||
""" | |||||
Detect if input samples are adversarial or not. | |||||
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) | |||||
dist = self.detect_diff(inputs) | |||||
res = [0]*len(dist) | |||||
for i, elem in enumerate(dist): | |||||
if elem > self._threshold: | |||||
res[i] = 1 | |||||
return res | |||||
def detect_diff(self, inputs): | |||||
""" | |||||
Detect the distance between the original samples and reconstructed samples. | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
Returns: | |||||
float, the distance between reconstructed and original samples. | |||||
""" | |||||
inputs = check_numpy_param('inputs', inputs) | |||||
x_trans = self._auto_encoder.predict(Tensor(inputs)) | |||||
diff = np.abs(inputs - x_trans.asnumpy()) | |||||
dims = tuple(np.arange(len(inputs.shape))[1:]) | |||||
marks = np.mean(np.power(diff, 2), axis=dims) | |||||
return marks | |||||
def transform(self, inputs): | |||||
""" | |||||
Reconstruct input samples. | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
Returns: | |||||
numpy.ndarray, reconstructed images. | |||||
""" | |||||
inputs = check_numpy_param('inputs', inputs) | |||||
x_trans = self._auto_encoder.predict(Tensor(inputs)) | |||||
if self._bounds is not None: | |||||
clip_min, clip_max = self._bounds | |||||
x_trans = np.clip(x_trans.asnumpy(), clip_min, clip_max) | |||||
return x_trans | |||||
def set_threshold(self, threshold): | |||||
""" | |||||
Set the parameters threshold. | |||||
Args: | |||||
threshold (float): Detection threshold. Default: None. | |||||
""" | |||||
self._threshold = check_value_positive('threshold', threshold) | |||||
class DivergenceBasedDetector(ErrorBasedDetector): | |||||
""" | |||||
This class implement a divergence-based detector. | |||||
Reference: `MagNet: a Two-Pronged Defense against Adversarial Examples, | |||||
by Dongyu Meng and Hao Chen, at CCS 2017. | |||||
<https://arxiv.org/abs/1705.09064>`_ | |||||
Args: | |||||
auto_encoder (Model): Encoder model. | |||||
model (Model): Targeted model. | |||||
option (str): Method used to calculate Divergence. Default: "jsd". | |||||
t (int): Temperature used to overcome numerical problem. Default: 1. | |||||
bounds (tuple): Upper and lower bounds of data. | |||||
In form of (clip_min, clip_max). Default: (0.0, 1.0). | |||||
""" | |||||
def __init__(self, auto_encoder, model, option="jsd", | |||||
t=1, bounds=(0.0, 1.0)): | |||||
super(DivergenceBasedDetector, self).__init__(auto_encoder, | |||||
bounds=bounds) | |||||
self._auto_encoder = auto_encoder | |||||
self._model = check_model('targeted model', model, Model) | |||||
self._threshold = 0.0 | |||||
self._option = option | |||||
self._t = check_int_positive('t', t) | |||||
self._bounds = check_param_multi_types('bounds', bounds, [tuple, list]) | |||||
for b in self._bounds: | |||||
_ = check_param_multi_types('bound', b, [int, float]) | |||||
def detect_diff(self, inputs): | |||||
""" | |||||
Detect the distance between original samples and reconstructed samples. | |||||
The distance is calculated by JSD. | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
Returns: | |||||
float, the distance. | |||||
Raises: | |||||
NotImplementedError: If the param `option` is not supported. | |||||
""" | |||||
inputs = check_numpy_param('inputs', inputs) | |||||
x_len = inputs.shape[0] | |||||
x_transformed = self._auto_encoder.predict(Tensor(inputs)) | |||||
x_origin = self._model.predict(Tensor(inputs)) | |||||
x_trans = self._model.predict(x_transformed) | |||||
y_pred = softmax(x_origin.asnumpy() / self._t, axis=1) | |||||
y_trans_pred = softmax(x_trans.asnumpy() / self._t, axis=1) | |||||
if self._option == 'jsd': | |||||
marks = [_jsd(y_pred[i], y_trans_pred[i]) for i in range(x_len)] | |||||
else: | |||||
msg = '{} is not implemented.'.format(self._option) | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) | |||||
return np.array(marks) | |||||
def _jsd(prob_dist_p, prob_dist_q): | |||||
""" | |||||
Compute the Jensen-Shannon Divergence between two probability distributions | |||||
with equal weights. | |||||
Args: | |||||
prob_dist_p (numpy.ndarray): Probability distribution p. | |||||
prob_dist_q (numpy.ndarray): Probability distribution q. | |||||
Returns: | |||||
float, the Jensen-Shannon Divergence. | |||||
""" | |||||
prob_dist_p = check_numpy_param('prob_dist_p', prob_dist_p) | |||||
prob_dist_q = check_numpy_param('prob_dist_q', prob_dist_q) | |||||
norm_dist_p = prob_dist_p / (np.linalg.norm(prob_dist_p, ord=1) + 1e-12) | |||||
norm_dist_q = prob_dist_q / (np.linalg.norm(prob_dist_q, ord=1) + 1e-12) | |||||
norm_mean = 0.5*(norm_dist_p + norm_dist_q) | |||||
return 0.5*(stats.entropy(norm_dist_p, norm_mean) | |||||
+ stats.entropy(norm_dist_q, norm_mean)) |
@@ -0,0 +1,235 @@ | |||||
# 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. | |||||
""" | |||||
Region-Based detector | |||||
""" | |||||
import time | |||||
import numpy as np | |||||
from mindspore import Model | |||||
from mindspore import Tensor | |||||
from mindarmour.detectors.detector import Detector | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.utils._check_param import check_numpy_param, check_param_type, \ | |||||
check_pair_numpy_param, check_model, check_int_positive, \ | |||||
check_value_positive, check_value_non_negative, check_param_in_range, \ | |||||
check_equal_shape | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'RegionBasedDetector' | |||||
class RegionBasedDetector(Detector): | |||||
""" | |||||
This class implement a region-based detector. | |||||
Reference: `Mitigating evasion attacks to deep neural networks via | |||||
region-based classification <https://arxiv.org/abs/1709.05583>`_ | |||||
Args: | |||||
model (Model): Target model. | |||||
number_points (int): The number of samples generate from the | |||||
hyper cube of original sample. Default: 10. | |||||
initial_radius (float): Initial radius of hyper cube. Default: 0.0. | |||||
max_radius (float): Maximum radius of hyper cube. Default: 1.0. | |||||
search_step (float): Incremental during search of radius. Default: 0.01. | |||||
degrade_limit (float): Acceptable decrease of classification accuracy. | |||||
Default: 0.0. | |||||
sparse (bool): If True, input labels are sparse-encoded. If False, | |||||
input labels are one-hot-encoded. Default: False. | |||||
Examples: | |||||
>>> detector = RegionBasedDetector(model) | |||||
>>> detector.fit(Tensor(ori), Tensor(labels)) | |||||
>>> adv_ids = detector.detect(Tensor(adv)) | |||||
""" | |||||
def __init__(self, model, number_points=10, initial_radius=0.0, | |||||
max_radius=1.0, search_step=0.01, degrade_limit=0.0, | |||||
sparse=False): | |||||
super(RegionBasedDetector, self).__init__() | |||||
self._model = check_model('targeted model', model, Model) | |||||
self._number_points = check_int_positive('number_points', number_points) | |||||
self._initial_radius = check_value_non_negative('initial_radius', | |||||
initial_radius) | |||||
self._max_radius = check_value_positive('max_radius', max_radius) | |||||
self._search_step = check_value_positive('search_step', search_step) | |||||
self._degrade_limit = check_value_non_negative('degrade_limit', | |||||
degrade_limit) | |||||
self._sparse = check_param_type('sparse', sparse, bool) | |||||
self._radius = None | |||||
def set_radius(self, radius): | |||||
"""Set radius.""" | |||||
self._radius = check_param_in_range('radius', radius, | |||||
self._initial_radius, | |||||
self._max_radius) | |||||
def fit(self, inputs, labels=None): | |||||
""" | |||||
Train detector to decide the best radius. | |||||
Args: | |||||
inputs (numpy.ndarray): Benign samples. | |||||
labels (numpy.ndarray): Ground truth labels of the input samples. | |||||
Default:None. | |||||
Returns: | |||||
float, the best radius. | |||||
""" | |||||
inputs, labels = check_pair_numpy_param('inputs', inputs, | |||||
'labels', labels) | |||||
LOGGER.debug(TAG, 'enter fit() function.') | |||||
time_start = time.time() | |||||
search_iters = (self._max_radius | |||||
- self._initial_radius) / self._search_step | |||||
search_iters = np.round(search_iters).astype(int) | |||||
radius = self._initial_radius | |||||
pred = self._model.predict(Tensor(inputs)) | |||||
raw_preds = np.argmax(pred.asnumpy(), axis=1) | |||||
if not self._sparse: | |||||
labels = np.argmax(labels, axis=1) | |||||
raw_preds, labels = check_equal_shape('raw_preds', raw_preds, 'labels', | |||||
labels) | |||||
raw_acc = np.sum(raw_preds == labels) / inputs.shape[0] | |||||
for _ in range(search_iters): | |||||
rc_preds = self._rc_forward(inputs, radius) | |||||
rc_preds, labels = check_equal_shape('rc_preds', rc_preds, 'labels', | |||||
labels) | |||||
def_acc = np.sum(rc_preds == labels) / inputs.shape[0] | |||||
if def_acc >= raw_acc - self._degrade_limit: | |||||
radius += self._search_step | |||||
continue | |||||
break | |||||
self._radius = radius - self._search_step | |||||
LOGGER.debug(TAG, 'best radius is: %s', self._radius) | |||||
LOGGER.debug(TAG, | |||||
'time used to train detector of %d samples is: %s seconds', | |||||
inputs.shape[0], | |||||
time.time() - time_start) | |||||
return self._radius | |||||
def _generate_hyper_cube(self, inputs, radius): | |||||
""" | |||||
Generate random samples in the hyper cubes around input samples. | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
radius (float): The scope to generate hyper cubes around input samples. | |||||
Returns: | |||||
numpy.ndarray, randomly chosen samples in the hyper cubes. | |||||
""" | |||||
LOGGER.debug(TAG, 'enter _generate_hyper_cube().') | |||||
res = [] | |||||
for _ in range(self._number_points): | |||||
res.append(np.clip((inputs + np.random.uniform( | |||||
-radius, radius, len(inputs))), 0.0, 1.0).astype(inputs.dtype)) | |||||
return np.asarray(res) | |||||
def _rc_forward(self, inputs, radius): | |||||
""" | |||||
Generate region-based predictions for input samples. | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
radius (float): The scope to generate hyper cubes around input samples. | |||||
Returns: | |||||
numpy.ndarray, classification result for input samples. | |||||
""" | |||||
LOGGER.debug(TAG, 'enter _rc_forward().') | |||||
res = [] | |||||
for _, elem in enumerate(inputs): | |||||
hyper_cube_x = self._generate_hyper_cube(elem, radius) | |||||
hyper_cube_preds = [] | |||||
for ite_hyper_cube_x in hyper_cube_x: | |||||
model_inputs = Tensor(np.expand_dims(ite_hyper_cube_x, axis=0)) | |||||
ite_preds = self._model.predict(model_inputs).asnumpy()[0] | |||||
hyper_cube_preds.append(ite_preds) | |||||
pred_labels = np.argmax(hyper_cube_preds, axis=1) | |||||
bin_count = np.bincount(pred_labels) | |||||
# count the number of different class and choose the max one | |||||
# as final class | |||||
hyper_cube_tag = np.argmax(bin_count, axis=0) | |||||
res.append(hyper_cube_tag) | |||||
return np.asarray(res) | |||||
def detect(self, inputs): | |||||
""" | |||||
Tell whether input samples are adversarial or not. | |||||
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. | |||||
""" | |||||
LOGGER.debug(TAG, 'enter detect().') | |||||
self._radius = check_param_type('radius', self._radius, float) | |||||
inputs = check_numpy_param('inputs', inputs) | |||||
time_start = time.time() | |||||
res = [1]*inputs.shape[0] | |||||
raw_preds = np.argmax(self._model.predict(Tensor(inputs)).asnumpy(), | |||||
axis=1) | |||||
rc_preds = self._rc_forward(inputs, self._radius) | |||||
for i in range(inputs.shape[0]): | |||||
if raw_preds[i] == rc_preds[i]: | |||||
res[i] = 0 | |||||
LOGGER.debug(TAG, | |||||
'time used to detect %d samples is : %s seconds', | |||||
inputs.shape[0], | |||||
time.time() - time_start) | |||||
return res | |||||
def detect_diff(self, inputs): | |||||
""" | |||||
Return raw prediction results and region-based prediction results. | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
Returns: | |||||
numpy.ndarray, raw prediction results and region-based prediction results of input samples. | |||||
""" | |||||
LOGGER.debug(TAG, 'enter detect_diff().') | |||||
inputs = check_numpy_param('inputs', inputs) | |||||
raw_preds = self._model.predict(Tensor(inputs)) | |||||
rc_preds = self._rc_forward(inputs, self._radius) | |||||
return raw_preds.asnumpy(), rc_preds | |||||
def transform(self, inputs): | |||||
""" | |||||
Generate hyper cube for input samples. | |||||
Args: | |||||
inputs (numpy.ndarray): Input samples. | |||||
Returns: | |||||
numpy.ndarray, hyper cube corresponds to every sample. | |||||
""" | |||||
LOGGER.debug(TAG, 'enter transform().') | |||||
inputs = check_numpy_param('inputs', inputs) | |||||
res = [] | |||||
for _, elem in enumerate(inputs): | |||||
res.append(self._generate_hyper_cube(elem, self._radius)) | |||||
return np.asarray(res) |
@@ -0,0 +1,171 @@ | |||||
# 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.detectors.detector import Detector | |||||
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 | |||||
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. | |||||
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: | |||||
>>> detector = SpatialSmoothing(model) | |||||
>>> detector.fit(Tensor(ori), Tensor(labels)) | |||||
>>> adv_ids = detector.detect(Tensor(adv)) | |||||
""" | |||||
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)) | |||||
smoothing_pred = self._model.predict(Tensor(self.transform(inputs))) | |||||
dist = self._dist(raw_pred.asnumpy(), smoothing_pred.asnumpy()) | |||||
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)) | |||||
smoothing_pred = self._model.predict(Tensor(self.transform(inputs))) | |||||
dist = self._dist(raw_pred.asnumpy(), smoothing_pred.asnumpy()) | |||||
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)) | |||||
smoothing_pred = self._model.predict(Tensor(self.transform(inputs))) | |||||
dist = self._dist(raw_pred.asnumpy(), smoothing_pred.asnumpy()) | |||||
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. Default: None. | |||||
""" | |||||
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 |
@@ -0,0 +1,14 @@ | |||||
""" | |||||
This module includes various metrics to evaluate the result of attacks or | |||||
defenses. | |||||
""" | |||||
from .attack_evaluation import AttackEvaluate | |||||
from .defense_evaluation import DefenseEvaluate | |||||
from .visual_metrics import RadarMetric | |||||
from . import black | |||||
from .black.defense_evaluation import BlackDefenseEvaluate | |||||
__all__ = ['AttackEvaluate', | |||||
'BlackDefenseEvaluate', | |||||
'DefenseEvaluate', | |||||
'RadarMetric'] |
@@ -0,0 +1,275 @@ | |||||
# 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. | |||||
""" | |||||
Attack evaluation. | |||||
""" | |||||
import numpy as np | |||||
from scipy.ndimage.filters import convolve | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.utils._check_param import check_pair_numpy_param, \ | |||||
check_param_type, check_numpy_param, check_equal_shape | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'AttackEvaluate' | |||||
def _compute_ssim(img_1, img_2, kernel_sigma=1.5, kernel_width=11): | |||||
""" | |||||
compute structural similarity. | |||||
Args: | |||||
img_1 (numpy.ndarray): The first image to be compared. | |||||
img_2 (numpy.ndarray): The second image to be compared. | |||||
kernel_sigma (float): Gassian kernel param. Default: 1.5. | |||||
kernel_width (int): Another Gassian kernel param. Default: 11. | |||||
Returns: | |||||
float, structural similarity. | |||||
""" | |||||
img_1, img_2 = check_equal_shape('images_1', img_1, 'images_2', img_2) | |||||
if len(img_1.shape) > 2: | |||||
total_ssim = 0 | |||||
for i in range(img_1.shape[2]): | |||||
total_ssim += _compute_ssim(img_1[:, :, i], img_2[:, :, i]) | |||||
return total_ssim / 3 | |||||
# Create gaussian kernel | |||||
gaussian_kernel = np.zeros((kernel_width, kernel_width)) | |||||
for i in range(kernel_width): | |||||
for j in range(kernel_width): | |||||
gaussian_kernel[i, j] = (1 / (2*np.pi*(kernel_sigma**2)))*np.exp( | |||||
- (((i - 5)**2) + ((j - 5)**2)) / (2*(kernel_sigma**2))) | |||||
img_1 = img_1.astype(np.float32) | |||||
img_2 = img_2.astype(np.float32) | |||||
img_sq_1 = img_1**2 | |||||
img_sq_2 = img_2**2 | |||||
img_12 = img_1*img_2 | |||||
# Mean | |||||
img_mu_1 = convolve(img_1, gaussian_kernel) | |||||
img_mu_2 = convolve(img_2, gaussian_kernel) | |||||
# Mean square | |||||
img_mu_sq_1 = img_mu_1**2 | |||||
img_mu_sq_2 = img_mu_2**2 | |||||
img_mu_12 = img_mu_1*img_mu_2 | |||||
# Variances | |||||
img_sigma_sq_1 = convolve(img_sq_1, gaussian_kernel) | |||||
img_sigma_sq_2 = convolve(img_sq_2, gaussian_kernel) | |||||
# Covariance | |||||
img_sigma_12 = convolve(img_12, gaussian_kernel) | |||||
# Centered squares of variances | |||||
img_sigma_sq_1 = img_sigma_sq_1 - img_mu_sq_1 | |||||
img_sigma_sq_2 = img_sigma_sq_2 - img_mu_sq_2 | |||||
img_sigma_12 = img_sigma_12 - img_mu_12 | |||||
k_1 = 0.01 | |||||
k_2 = 0.03 | |||||
c_1 = (k_1*255)**2 | |||||
c_2 = (k_2*255)**2 | |||||
# Calculate ssim | |||||
num_ssim = (2*img_mu_12 + c_1)*(2*img_sigma_12 + c_2) | |||||
den_ssim = (img_mu_sq_1 + img_mu_sq_2 + c_1)*(img_sigma_sq_1 | |||||
+ img_sigma_sq_2 + c_2) | |||||
res = np.average(num_ssim / den_ssim) | |||||
return res | |||||
class AttackEvaluate: | |||||
""" | |||||
Evaluation metrics of attack methods. | |||||
Args: | |||||
inputs (numpy.ndarray): Original samples. | |||||
labels (numpy.ndarray): Original samples' label by one-hot format. | |||||
adv_inputs (numpy.ndarray): Adversarial samples generated from original | |||||
samples. | |||||
adv_preds (numpy.ndarray): Probability of all output classes of | |||||
adversarial examples. | |||||
targeted (bool): If True, it is a targeted attack. If False, it is an | |||||
untargeted attack. Default: False. | |||||
target_label (numpy.ndarray): Targeted classes of adversarial examples, | |||||
which is one dimension whose size is adv_inputs.shape[0]. | |||||
Default: None. | |||||
Raises: | |||||
ValueError: If target_label is None when targeted is True. | |||||
Examples: | |||||
>>> x = np.random.normal(size=(3, 512, 512, 3)) | |||||
>>> adv_x = np.random.normal(size=(3, 512, 512, 3)) | |||||
>>> y = np.array([[0.1, 0.1, 0.2, 0.6], | |||||
>>> [0.1, 0.7, 0.0, 0.2], | |||||
>>> [0.8, 0.1, 0.0, 0.1]]) | |||||
>>> adv_y = np.array([[0.1, 0.1, 0.2, 0.6], | |||||
>>> [0.1, 0.0, 0.8, 0.1], | |||||
>>> [0.0, 0.9, 0.1, 0.0]]) | |||||
>>> attack_eval = AttackEvaluate(x, y, adv_x, adv_y) | |||||
>>> mr = attack_eval.mis_classification_rate() | |||||
""" | |||||
def __init__(self, inputs, labels, adv_inputs, adv_preds, | |||||
targeted=False, target_label=None): | |||||
self._inputs, self._labels = check_pair_numpy_param('inputs', | |||||
inputs, | |||||
'labels', | |||||
labels) | |||||
self._adv_inputs, self._adv_preds = check_pair_numpy_param('adv_inputs', | |||||
adv_inputs, | |||||
'adv_preds', | |||||
adv_preds) | |||||
targeted = check_param_type('targeted', targeted, bool) | |||||
self._targeted = targeted | |||||
if target_label is not None: | |||||
target_label = check_numpy_param('target_label', target_label) | |||||
self._target_label = target_label | |||||
self._true_label = np.argmax(self._labels, axis=1) | |||||
self._adv_label = np.argmax(self._adv_preds, axis=1) | |||||
idxes = np.arange(self._adv_preds.shape[0]) | |||||
if self._targeted: | |||||
if target_label is None: | |||||
msg = 'targeted attack need target_label, but got None.' | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
self._adv_preds, self._target_label = check_pair_numpy_param('adv_pred', | |||||
self._adv_preds, | |||||
'target_label', | |||||
target_label) | |||||
self._success_idxes = idxes[self._adv_label == self._target_label] | |||||
else: | |||||
self._success_idxes = idxes[self._adv_label != self._true_label] | |||||
def mis_classification_rate(self): | |||||
""" | |||||
Calculate misclassification rate(MR). | |||||
Returns: | |||||
float, ranges between (0, 1). The higher, the more successful the attack is. | |||||
""" | |||||
return self._success_idxes.shape[0]*1.0 / self._inputs.shape[0] | |||||
def avg_conf_adv_class(self): | |||||
""" | |||||
Calculate average confidence of adversarial class (ACAC). | |||||
Returns: | |||||
float, ranges between (0, 1). The higher, the more successful the attack is. | |||||
""" | |||||
idxes = self._success_idxes | |||||
success_num = idxes.shape[0] | |||||
if success_num == 0: | |||||
return 0 | |||||
if self._targeted: | |||||
return np.mean(self._adv_preds[idxes, self._target_label[idxes]]) | |||||
return np.mean(self._adv_preds[idxes, self._adv_label[idxes]]) | |||||
def avg_conf_true_class(self): | |||||
""" | |||||
Calculate average confidence of true class (ACTC). | |||||
Returns: | |||||
float, ranges between (0, 1). The lower, the more successful the attack is. | |||||
""" | |||||
idxes = self._success_idxes | |||||
success_num = idxes.shape[0] | |||||
if success_num == 0: | |||||
return 0 | |||||
return np.mean(self._adv_preds[idxes, self._true_label[idxes]]) | |||||
def avg_lp_distance(self): | |||||
""" | |||||
Calculate average lp distance (lp-dist). | |||||
Returns: | |||||
- float, return average l0, l2, or linf distance of all success | |||||
adversarial examples, return value includes following cases. | |||||
- If return value :math:`>=` 0, average lp distance. The lower, | |||||
the more successful the attack is. | |||||
- If return value is -1, there is no success adversarial examples. | |||||
""" | |||||
idxes = self._success_idxes | |||||
success_num = idxes.shape[0] | |||||
if success_num == 0: | |||||
return -1, -1, -1 | |||||
l0_dist = 0 | |||||
l2_dist = 0 | |||||
linf_dist = 0 | |||||
avoid_zero_div = 1e-14 | |||||
for i in idxes: | |||||
diff = (self._adv_inputs[i] - self._inputs[i]).flatten() | |||||
data = self._inputs[i].flatten() | |||||
l0_dist += np.linalg.norm(diff, ord=0) \ | |||||
/ (np.linalg.norm(data, ord=0) + avoid_zero_div) | |||||
l2_dist += np.linalg.norm(diff, ord=2) \ | |||||
/ (np.linalg.norm(data, ord=2) + avoid_zero_div) | |||||
linf_dist += np.linalg.norm(diff, ord=np.inf) \ | |||||
/ (np.linalg.norm(data, ord=np.inf) + avoid_zero_div) | |||||
return l0_dist / success_num, l2_dist / success_num, \ | |||||
linf_dist / success_num | |||||
def avg_ssim(self): | |||||
""" | |||||
Calculate average structural similarity (ASS). | |||||
Returns: | |||||
- float, average structural similarity. | |||||
- If return value ranges between (0, 1), the higher, the more | |||||
successful the attack is. | |||||
- If return value is -1: there is no success adversarial examples. | |||||
""" | |||||
success_num = self._success_idxes.shape[0] | |||||
if success_num == 0: | |||||
return -1 | |||||
total_ssim = 0.0 | |||||
for _, i in enumerate(self._success_idxes): | |||||
total_ssim += _compute_ssim(self._adv_inputs[i], self._inputs[i]) | |||||
return total_ssim / success_num | |||||
def nte(self): | |||||
""" | |||||
Calculate noise tolerance estimation (NTE). | |||||
References: `Towards Imperceptible and Robust Adversarial Example Attacks | |||||
against Neural Networks <https://arxiv.org/abs/1801.04693>`_ | |||||
Returns: | |||||
float, ranges between (0, 1). The higher, the more successful the | |||||
attack is. | |||||
""" | |||||
idxes = self._success_idxes | |||||
success_num = idxes.shape[0] | |||||
adv_y = self._adv_preds[idxes] | |||||
adv_y_2 = np.copy(adv_y) | |||||
adv_y_2[range(success_num), np.argmax(adv_y_2, axis=1)] = 0 | |||||
net = np.mean(np.abs(np.max(adv_y_2, axis=1) - np.max(adv_y, axis=1))) | |||||
return net |
@@ -0,0 +1,204 @@ | |||||
# 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. | |||||
""" | |||||
Evaluating Defense against Black-box Attacks. | |||||
""" | |||||
import numpy as np | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.utils._check_param import check_pair_numpy_param, \ | |||||
check_equal_length, check_int_positive, check_numpy_param | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'BlackDefenseEvaluate' | |||||
class BlackDefenseEvaluate: | |||||
""" | |||||
Evaluation metrics of anti-black-box defense method. | |||||
Args: | |||||
raw_preds (numpy.ndarray): Predict results of some certain samples on | |||||
raw model. | |||||
def_preds (numpy.ndarray): Predict results of some certain samples on | |||||
defensed model. | |||||
raw_query_counts (numpy.ndarray): Number of queries to generate | |||||
adversarial examples on raw model, which is one dimensional whose | |||||
size is raw_preds.shape[0]. For benign samples, query count must be | |||||
set to 0. | |||||
def_query_counts (numpy.ndarray): Number of queries to generate | |||||
adversarial examples on defensed model, which is one dimensional | |||||
whose size is raw_preds.shape[0]. | |||||
For benign samples, query count must be set to 0. | |||||
raw_query_time (numpy.ndarray): The total time duration to generate | |||||
an adversarial example on raw model, which is one dimensional | |||||
whose size is raw_preds.shape[0]. | |||||
def_query_time (numpy.ndarray): The total time duration to generate an | |||||
adversarial example on defensed model, which is one dimensional | |||||
whose size is raw_preds.shape[0]. | |||||
def_detection_counts (numpy.ndarray): Total number of detected queries | |||||
during each adversarial example generation, which is one dimensional | |||||
whose size is raw_preds.shape[0]. For a benign sample, the | |||||
def_detection_counts is set to 1 if the query is identified as | |||||
suspicious, and 0 otherwise. | |||||
true_labels (numpy.ndarray): True labels in one-dim whose size is | |||||
raw_preds.shape[0]. | |||||
max_queries (int): Attack budget, the maximum number of queries. | |||||
Examples: | |||||
>>> raw_preds = np.array([[0.1, 0.1, 0.2, 0.6], | |||||
>>> [0.1, 0.7, 0.0, 0.2], | |||||
>>> [0.8, 0.1, 0.0, 0.1]]) | |||||
>>> def_preds = np.array([[0.1, 0.1, 0.1, 0.7], | |||||
>>> [0.1, 0.6, 0.2, 0.1], | |||||
>>> [0.1, 0.2, 0.1, 0.6]]) | |||||
>>> raw_query_counts = np.array([0,20,10]) | |||||
>>> def_query_counts = np.array([0,50,60]) | |||||
>>> raw_query_time = np.array([0.1, 2, 1]) | |||||
>>> def_query_time = np.array([0.2, 6, 5]) | |||||
>>> def_detection_counts = np.array([1, 5, 10]) | |||||
>>> true_labels = np.array([3, 1, 0]) | |||||
>>> max_queries = 100 | |||||
>>> def_eval = BlackDefenseEvaluat(raw_preds, | |||||
>>> def_preds, | |||||
>>> raw_query_counts, | |||||
>>> def_query_counts, | |||||
>>> raw_query_time, | |||||
>>> def_query_time, | |||||
>>> def_detection_counts, | |||||
>>> true_labels, | |||||
>>> max_queries) | |||||
>>> def_eval.qcv() | |||||
""" | |||||
def __init__(self, raw_preds, def_preds, raw_query_counts, def_query_counts, | |||||
raw_query_time, def_query_time, def_detection_counts, | |||||
true_labels, max_queries): | |||||
self._raw_preds, self._def_preds = check_pair_numpy_param('raw_preds', | |||||
raw_preds, | |||||
'def_preds', | |||||
def_preds) | |||||
self._num_samples = self._raw_preds.shape[0] | |||||
self._raw_query_counts, _ = check_equal_length('raw_query_counts', | |||||
raw_query_counts, | |||||
'number of sample', | |||||
self._raw_preds) | |||||
self._def_query_counts, _ = check_equal_length('def_query_counts', | |||||
def_query_counts, | |||||
'number of sample', | |||||
self._raw_preds) | |||||
self._raw_query_time, _ = check_equal_length('raw_query_time', | |||||
raw_query_time, | |||||
'number of sample', | |||||
self._raw_preds) | |||||
self._def_query_time, _ = check_equal_length('def_query_time', | |||||
def_query_time, | |||||
'number of sample', | |||||
self._raw_preds) | |||||
self._num_adv_samples = self._raw_query_counts[ | |||||
self._raw_query_counts > 0].shape[0] | |||||
self._num_adv_samples = check_int_positive( | |||||
'the number of adversarial samples', | |||||
self._num_adv_samples) | |||||
self._num_ben_samples = self._num_samples - self._num_adv_samples | |||||
self._max_queries = check_int_positive('max_queries', max_queries) | |||||
self._def_detection_counts = check_numpy_param('def_detection_counts', | |||||
def_detection_counts) | |||||
self._true_labels = check_numpy_param('true_labels', true_labels) | |||||
def qcv(self): | |||||
""" | |||||
Calculate query count variance (QCV). | |||||
Returns: | |||||
float, the higher, the stronger the defense is. If num_adv_samples=0, | |||||
return -1. | |||||
""" | |||||
if self._num_adv_samples == 0: | |||||
return -1 | |||||
avg_def_query_count = \ | |||||
np.sum(self._def_query_counts) / self._num_adv_samples | |||||
avg_raw_query_count = \ | |||||
np.sum(self._raw_query_counts) / self._num_adv_samples | |||||
if (avg_def_query_count == self._max_queries) \ | |||||
and (avg_raw_query_count < self._max_queries): | |||||
query_variance = 1 | |||||
else: | |||||
query_variance = \ | |||||
min(avg_def_query_count - avg_raw_query_count, | |||||
self._max_queries) / self._max_queries | |||||
return query_variance | |||||
def asv(self): | |||||
""" | |||||
Calculate attack success rate variance (ASV). | |||||
Returns: | |||||
float, the lower, the stronger the defense is. If num_adv_samples=0, | |||||
return -1. | |||||
""" | |||||
adv_def_preds = self._def_preds[self._def_query_counts > 0] | |||||
adv_raw_preds = self._raw_preds[self._raw_query_counts > 0] | |||||
adv_true_labels = self._true_labels[self._raw_query_counts > 0] | |||||
def_succ_num = np.sum(np.argmax(adv_def_preds, axis=1) | |||||
!= adv_true_labels) | |||||
raw_succ_num = np.sum(np.argmax(adv_raw_preds, axis=1) | |||||
!= adv_true_labels) | |||||
if self._num_adv_samples == 0: | |||||
return -1 | |||||
return (raw_succ_num - def_succ_num) / self._num_adv_samples | |||||
def fpr(self): | |||||
""" | |||||
Calculate false positive rate (FPR) of the query-based detector. | |||||
Returns: | |||||
float, the lower, the higher usability the defense is. If | |||||
num_adv_samples=0, return -1. | |||||
""" | |||||
ben_detect_counts = \ | |||||
self._def_detection_counts[self._def_query_counts == 0] | |||||
num_fp = ben_detect_counts[ben_detect_counts > 0].shape[0] | |||||
if self._num_ben_samples == 0: | |||||
return -1 | |||||
return num_fp / self._num_ben_samples | |||||
def qrv(self): | |||||
""" | |||||
Calculate the benign query response time variance (QRV). | |||||
Returns: | |||||
float, the lower, the higher usability the defense is. If | |||||
num_adv_samples=0, return -1. | |||||
""" | |||||
if self._num_ben_samples == 0: | |||||
return -1 | |||||
raw_num_queries = self._num_ben_samples | |||||
def_num_queries = self._num_ben_samples | |||||
ben_raw_query_time = self._raw_query_time[self._raw_query_counts == 0] | |||||
ben_def_query_time = self._def_query_time[self._def_query_counts == 0] | |||||
avg_raw_query_time = np.sum(ben_raw_query_time) / raw_num_queries | |||||
avg_def_query_time = np.sum(ben_def_query_time) / def_num_queries | |||||
return (avg_def_query_time - | |||||
avg_raw_query_time) / (avg_raw_query_time + 1e-12) |
@@ -0,0 +1,152 @@ | |||||
# 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. | |||||
""" | |||||
Defense Evaluation. | |||||
""" | |||||
import numpy as np | |||||
import scipy.stats as st | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.utils._check_param import check_numpy_param | |||||
from mindarmour.utils._check_param import check_pair_numpy_param | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'DefenseEvaluate' | |||||
class DefenseEvaluate: | |||||
""" | |||||
Evaluation metrics of defense methods. | |||||
Args: | |||||
raw_preds (numpy.ndarray): Prediction results of some certain samples | |||||
on raw model. | |||||
def_preds (numpy.ndarray): Prediction results of some certain samples on | |||||
defensed model. | |||||
true_labels (numpy.ndarray): Ground-truth labels of samples, a | |||||
one-dimension array whose size is raw_preds.shape[0]. | |||||
Examples: | |||||
>>> raw_preds = np.array([[0.1, 0.1, 0.2, 0.6], | |||||
>>> [0.1, 0.7, 0.0, 0.2], | |||||
>>> [0.8, 0.1, 0.0, 0.1]]) | |||||
>>> def_preds = np.array([[0.1, 0.1, 0.1, 0.7], | |||||
>>> [0.1, 0.6, 0.2, 0.1], | |||||
>>> [0.1, 0.2, 0.1, 0.6]]) | |||||
>>> true_labels = np.array([3, 1, 0]) | |||||
>>> def_eval = DefenseEvaluate(raw_preds, | |||||
>>> def_preds, | |||||
>>> true_labels) | |||||
>>> def_eval.cav() | |||||
""" | |||||
def __init__(self, raw_preds, def_preds, true_labels): | |||||
self._raw_preds, self._def_preds = check_pair_numpy_param('raw_preds', | |||||
raw_preds, | |||||
'def_preds', | |||||
def_preds) | |||||
self._true_labels = check_numpy_param('true_labels', true_labels) | |||||
self._num_samples = len(true_labels) | |||||
def cav(self): | |||||
""" | |||||
Calculate classification accuracy variance (CAV). | |||||
Returns: | |||||
float, the higher, the more successful the defense is. | |||||
""" | |||||
def_succ_num = np.sum(np.argmax(self._def_preds, axis=1) | |||||
== self._true_labels) | |||||
raw_succ_num = np.sum(np.argmax(self._raw_preds, axis=1) | |||||
== self._true_labels) | |||||
return (def_succ_num - raw_succ_num) / self._num_samples | |||||
def crr(self): | |||||
""" | |||||
Calculate classification rectify ratio (CRR). | |||||
Returns: | |||||
float, the higher, the more successful the defense is. | |||||
""" | |||||
cond1 = np.argmax(self._def_preds, axis=1) == self._true_labels | |||||
cond2 = np.argmax(self._raw_preds, axis=1) != self._true_labels | |||||
rectify_num = np.sum(cond1*cond2) | |||||
return rectify_num*1.0 / self._num_samples | |||||
def csr(self): | |||||
""" | |||||
Calculate classification sacrifice ratio (CSR), the lower the better. | |||||
Returns: | |||||
float, the lower, the more successful the defense is. | |||||
""" | |||||
cond1 = np.argmax(self._def_preds, axis=1) != self._true_labels | |||||
cond2 = np.argmax(self._raw_preds, axis=1) == self._true_labels | |||||
sacrifice_num = np.sum(cond1*cond2) | |||||
return sacrifice_num*1.0 / self._num_samples | |||||
def ccv(self): | |||||
""" | |||||
Calculate classification confidence variance (CCV). | |||||
Returns: | |||||
- float, the lower, the more successful the defense is. | |||||
- If return value == -1, len(idxes) == 0. | |||||
""" | |||||
idxes = np.arange(self._num_samples) | |||||
cond1 = np.argmax(self._def_preds, axis=1) == self._true_labels | |||||
cond2 = np.argmax(self._raw_preds, axis=1) == self._true_labels | |||||
idxes = idxes[cond1*cond2] | |||||
def_max = np.max(self._def_preds, axis=1) | |||||
raw_max = np.max(self._raw_preds, axis=1) | |||||
if idxes.shape[0] == 0: | |||||
return -1 | |||||
conf_variance = np.mean(np.abs(def_max[idxes] - raw_max[idxes])) | |||||
return conf_variance | |||||
def cos(self): | |||||
""" | |||||
References: `Calculate classification output stability (COS) | |||||
<https://en.wikipedia.org/wiki/Jensen%E2%80%93Shannon_divergence>`_ | |||||
Returns: | |||||
float. | |||||
- If return value >= 0, is effective defense. The lower, the | |||||
more successful the defense. | |||||
- If return value == -1, idxes == 0. | |||||
""" | |||||
idxes = np.arange(self._num_samples) | |||||
cond1 = np.argmax(self._def_preds, axis=1) == self._true_labels | |||||
cond2 = np.argmax(self._raw_preds, axis=1) == self._true_labels | |||||
idxes = idxes[cond1*cond2] | |||||
if idxes.size == 0: | |||||
return -1 | |||||
def_preds = self._def_preds[idxes] | |||||
raw_preds = self._raw_preds[idxes] | |||||
js_total = 0.0 | |||||
mean_value = 0.5*(def_preds + raw_preds) | |||||
for i, value in enumerate(mean_value): | |||||
js_total += 0.5*st.entropy(def_preds[i], value) \ | |||||
+ 0.5*st.entropy(raw_preds[i], value) | |||||
return js_total / len(idxes) |
@@ -0,0 +1,141 @@ | |||||
# 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. | |||||
""" | |||||
Radar map. | |||||
""" | |||||
from math import pi | |||||
import numpy as np | |||||
import matplotlib.pyplot as plt | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.utils._check_param import check_param_type, check_numpy_param, \ | |||||
check_param_multi_types, check_equal_length | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'RadarMetric' | |||||
class RadarMetric: | |||||
""" | |||||
Radar chart to show the robustness of a model by multiple metrics. | |||||
Args: | |||||
metrics_name (Union[tuple, list]): An array of names of metrics to show. | |||||
metrics_data (numpy.ndarray): The (normalized) values of each metrics of | |||||
multiple radar curves, like [[0.5, 0.8, ...], [0.2,0.6,...], ...]. | |||||
Each set of values corresponds to one radar curve. | |||||
labels (Union[tuple, list]): Legends of all radar curves. | |||||
title (str): Title of the chart. | |||||
scale (str): Scalar to adjust axis ticks, such as 'hide', 'norm', | |||||
'sparse' or 'dense'. Default: 'hide'. | |||||
Raises: | |||||
ValueError: If scale not in ['hide', 'norm', 'sparse', 'dense']. | |||||
Examples: | |||||
>>> metrics_name = ['MR', 'ACAC', 'ASS', 'NTE', 'ACTC'] | |||||
>>> def_metrics = [0.9, 0.85, 0.6, 0.7, 0.8] | |||||
>>> raw_metrics = [0.5, 0.3, 0.55, 0.65, 0.7] | |||||
>>> metrics_data = [def_metrics, raw_metrics] | |||||
>>> metrics_labels = ['before', 'after'] | |||||
>>> rm = RadarMetric(metrics_name, | |||||
>>> metrics_data, | |||||
>>> metrics_labels, | |||||
>>> title='', | |||||
>>> scale='sparse') | |||||
>>> rm.show() | |||||
""" | |||||
def __init__(self, metrics_name, metrics_data, labels, title, scale='hide'): | |||||
self._metrics_name = check_param_multi_types('metrics_name', | |||||
metrics_name, | |||||
[tuple, list]) | |||||
self._metrics_data = check_numpy_param('metrics_data', metrics_data) | |||||
self._labels = check_param_multi_types('labels', labels, (tuple, list)) | |||||
_, _ = check_equal_length('metrics_name', metrics_name, | |||||
'metrics_data', self._metrics_data[0]) | |||||
_, _ = check_equal_length('labels', labels, 'metrics_data', metrics_data) | |||||
self._title = check_param_type('title', title, str) | |||||
if scale in ['hide', 'norm', 'sparse', 'dense']: | |||||
self._scale = scale | |||||
else: | |||||
msg = "scale must be in ['hide', 'norm', 'sparse', 'dense'], but " \ | |||||
"got {}".format(scale) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
self._nb_var = len(metrics_name) | |||||
# divide the plot / number of variable | |||||
self._angles = [n / self._nb_var*2.0*pi for n in | |||||
range(self._nb_var)] | |||||
self._angles += self._angles[:1] | |||||
# add one more point | |||||
data = [self._metrics_data, self._metrics_data[:, [0]]] | |||||
self._metrics_data = np.concatenate(data, axis=1) | |||||
def show(self): | |||||
""" | |||||
Show the radar chart. | |||||
""" | |||||
# Initialise the spider plot | |||||
plt.clf() | |||||
axis_pic = plt.subplot(111, polar=True) | |||||
axis_pic.spines['polar'].set_visible(False) | |||||
axis_pic.set_yticklabels([]) | |||||
# If you want the first axis to be on top: | |||||
axis_pic.set_theta_offset(pi / 2) | |||||
axis_pic.set_theta_direction(-1) | |||||
# Draw one axe per variable + add labels labels yet | |||||
plt.xticks(self._angles[:-1], self._metrics_name) | |||||
# Draw y labels | |||||
axis_pic.set_rlabel_position(0) | |||||
if self._scale == 'hide': | |||||
plt.yticks([0.0], color="grey", size=7) | |||||
elif self._scale == 'norm': | |||||
plt.yticks([0.2, 0.4, 0.6, 0.8], | |||||
["0.2", "0.4", "0.6", "0.8"], | |||||
color="grey", size=7) | |||||
elif self._scale == 'sparse': | |||||
plt.yticks([0.5], ["0.5"], color="grey", size=7) | |||||
elif self._scale == 'dense': | |||||
ticks = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9] | |||||
labels = ["0.1", "0.2", "0.3", "0.4", "0.5", "0.6", | |||||
"0.7", "0.8", "0.9"] | |||||
plt.yticks(ticks, labels, color="grey", size=7) | |||||
else: | |||||
# default | |||||
plt.yticks([0.0], color="grey", size=7) | |||||
plt.ylim(0, 1) | |||||
# plot border | |||||
axis_pic.plot(self._angles, [1]*(self._nb_var + 1), color='grey', | |||||
linewidth=1, linestyle='solid') | |||||
for i in range(len(self._labels)): | |||||
axis_pic.plot(self._angles, self._metrics_data[i], linewidth=1, | |||||
linestyle='solid', label=self._labels[i]) | |||||
axis_pic.fill(self._angles, self._metrics_data[i], alpha=0.1) | |||||
# Add legend | |||||
plt.legend(loc='upper right', bbox_to_anchor=(0., 0.)) | |||||
plt.title(self._title, y=1.1, color='g') | |||||
plt.show() |
@@ -0,0 +1,7 @@ | |||||
""" | |||||
Util methods of MindArmour.""" | |||||
from .logger import LogUtil | |||||
from .util import GradWrap | |||||
from .util import GradWrapWithLoss | |||||
__all__ = ['LogUtil', 'GradWrapWithLoss', 'GradWrap'] |
@@ -0,0 +1,269 @@ | |||||
# 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. | |||||
""" check parameters for MindArmour. """ | |||||
import numpy as np | |||||
from mindarmour.utils.logger import LogUtil | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'check parameters' | |||||
def _check_array_not_empty(arg_name, arg_value): | |||||
"""Check parameter is empty or not.""" | |||||
if isinstance(arg_value, (tuple, list)): | |||||
if not arg_value: | |||||
msg = '{} must not be empty'.format(arg_name) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
if isinstance(arg_value, np.ndarray): | |||||
if arg_value.size <= 0: | |||||
msg = '{} must not be empty'.format(arg_name) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
return arg_value | |||||
def check_param_type(arg_name, arg_value, valid_type): | |||||
"""Check parameter type.""" | |||||
if not isinstance(arg_value, valid_type): | |||||
msg = '{} must be {}, but got {}'.format(arg_name, | |||||
valid_type, | |||||
type(arg_value).__name__) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
return arg_value | |||||
def check_param_multi_types(arg_name, arg_value, valid_types): | |||||
"""Check parameter type.""" | |||||
if not isinstance(arg_value, tuple(valid_types)): | |||||
msg = 'type of {} must be in {}, but got {}' \ | |||||
.format(arg_name, valid_types, type(arg_value).__name__) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
return arg_value | |||||
def check_int_positive(arg_name, arg_value): | |||||
"""Check positive integer.""" | |||||
arg_value = check_param_type(arg_name, arg_value, int) | |||||
if arg_value <= 0: | |||||
msg = '{} must be greater than 0, but got {}'.format(arg_name, | |||||
arg_value) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
return arg_value | |||||
def check_value_non_negative(arg_name, arg_value): | |||||
"""Check non negative value.""" | |||||
arg_value = check_param_multi_types(arg_name, arg_value, (int, float)) | |||||
if float(arg_value) < 0.0: | |||||
msg = '{} must not be less than 0, but got {}'.format(arg_name, | |||||
arg_value) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
return arg_value | |||||
def check_value_positive(arg_name, arg_value): | |||||
"""Check positive value.""" | |||||
arg_value = check_param_multi_types(arg_name, arg_value, (int, float)) | |||||
if float(arg_value) <= 0.0: | |||||
msg = '{} must be greater than zero, but got {}'.format(arg_name, | |||||
arg_value) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
return arg_value | |||||
def check_param_in_range(arg_name, arg_value, lower, upper): | |||||
""" | |||||
Check range of parameter. | |||||
""" | |||||
if arg_value <= lower or arg_value >= upper: | |||||
msg = '{} must be between {} and {}, but got {}'.format(arg_name, | |||||
lower, | |||||
upper, | |||||
arg_value) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
return arg_value | |||||
def check_model(model_name, model, model_type): | |||||
""" | |||||
Check the type of input `model` . | |||||
Args: | |||||
model_name (str): Name of model. | |||||
model (Object): Model object. | |||||
model_type (Class): Class of model. | |||||
Returns: | |||||
Object, if the type of `model` is `model_type`, return `model` itself. | |||||
Raises: | |||||
ValueError: If model is not an instance of `model_type` . | |||||
""" | |||||
if isinstance(model, model_type): | |||||
return model | |||||
msg = '{} should be an instance of {}, but got {}' \ | |||||
.format(model_name, | |||||
model_type, | |||||
type(model).__name__) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
def check_numpy_param(arg_name, arg_value): | |||||
""" | |||||
None-check and Numpy-check for `value` . | |||||
Args: | |||||
arg_name (str): Name of parameter. | |||||
arg_value (Union[list, tuple, numpy.ndarray]): Value for check. | |||||
Returns: | |||||
numpy.ndarray, if `value` is not empty, return `value` with type of | |||||
numpy.ndarray. | |||||
Raises: | |||||
ValueError: If value is empty. | |||||
ValueError: If value type is not in (list, tuple, numpy.ndarray). | |||||
""" | |||||
_ = _check_array_not_empty(arg_name, arg_value) | |||||
if isinstance(arg_value, (list, tuple)): | |||||
arg_value = np.asarray(arg_value) | |||||
elif isinstance(arg_value, np.ndarray): | |||||
arg_value = np.copy(arg_value) | |||||
else: | |||||
msg = 'type of {} must be in (list, tuple, numpy.ndarray)'.format( | |||||
arg_name) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
return arg_value | |||||
def check_pair_numpy_param(inputs_name, inputs, labels_name, labels): | |||||
""" | |||||
Dimension-equivalence check for `inputs` and `labels`. | |||||
Args: | |||||
inputs_name (str): Name of inputs. | |||||
inputs (Union[list, tuple, numpy.ndarray]): Inputs. | |||||
labels_name (str): Name of labels. | |||||
labels (Union[list, tuple, numpy.ndarray]): Labels of `inputs`. | |||||
Returns: | |||||
- Union[list, tuple, numpy.ndarray], if `inputs` 's dimension equals to | |||||
`labels`, return inputs with type of numpy.ndarray. | |||||
- Union[list, tuple, numpy.ndarray], if `inputs` 's dimension equals to | |||||
`labels` , return labels with type of numpy.ndarray. | |||||
Raises: | |||||
ValueError: If inputs.shape[0] is not equal to labels.shape[0]. | |||||
""" | |||||
inputs = check_numpy_param(inputs_name, inputs) | |||||
labels = check_numpy_param(labels_name, labels) | |||||
if inputs.shape[0] != labels.shape[0]: | |||||
msg = '{} shape[0] must equal {} shape[0], bot got shape of ' \ | |||||
'inputs {}, shape of labels {}'.format(inputs_name, labels_name, | |||||
inputs.shape, labels.shape) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
return inputs, labels | |||||
def check_equal_length(para_name1, value1, para_name2, value2): | |||||
"""check weather the two parameters have equal length.""" | |||||
if len(value1) != len(value2): | |||||
msg = 'The dimension of {0} must equal to the ' \ | |||||
'{1}, but got {0} is {2}, ' \ | |||||
'{1} is {3}'.format(para_name1, para_name2, len(value1), | |||||
len(value2)) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
return value1, value2 | |||||
def check_equal_shape(para_name1, value1, para_name2, value2): | |||||
"""check weather the two parameters have equal shape.""" | |||||
if value1.shape != value2.shape: | |||||
msg = 'The shape of {0} must equal to the ' \ | |||||
'{1}, but got {0} is {2}, ' \ | |||||
'{1} is {3}'.format(para_name1, para_name2, value1.shape[0], | |||||
value2.shape[0]) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
return value1, value2 | |||||
def check_norm_level(norm_level): | |||||
""" | |||||
check norm_level of regularization. | |||||
""" | |||||
accept_norm = [1, 2, '1', '2', 'l1', 'l2', 'inf', 'linf', np.inf] | |||||
if norm_level not in accept_norm: | |||||
msg = 'norm_level must be in {}, but got {}'.format(accept_norm, | |||||
norm_level) | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
return norm_level | |||||
def normalize_value(value, norm_level): | |||||
""" | |||||
Normalize gradients for gradient attacks. | |||||
Args: | |||||
value (numpy.ndarray): Inputs. | |||||
norm_level (Union[int, str]): Normalized level. | |||||
Returns: | |||||
numpy.ndarray, normalized value. | |||||
Raises: | |||||
NotImplementedError: If norm_level is not in [1, 2 , np.inf, '1', '2', | |||||
'inf] | |||||
""" | |||||
norm_level = check_norm_level(norm_level) | |||||
ori_shape = value.shape | |||||
value_reshape = value.reshape((value.shape[0], -1)) | |||||
avoid_zero_div = 1e-12 | |||||
if norm_level in (1, '1', 'l1'): | |||||
norm = np.linalg.norm(value_reshape, ord=1, axis=1, keepdims=True) + \ | |||||
avoid_zero_div | |||||
norm_value = value_reshape / norm | |||||
elif norm_level in (2, '2', 'l2'): | |||||
norm = np.linalg.norm(value_reshape, ord=2, axis=1, keepdims=True) + \ | |||||
avoid_zero_div | |||||
norm_value = value_reshape / norm | |||||
elif norm_level in (np.inf, 'inf'): | |||||
norm = np.max(abs(value_reshape), axis=1, keepdims=True) + \ | |||||
avoid_zero_div | |||||
norm_value = value_reshape / norm | |||||
else: | |||||
msg = 'Values of `norm_level` different from 1, 2 and ' \ | |||||
'`np.inf` are currently not supported, but got {}.' \ | |||||
.format(norm_level) | |||||
LOGGER.error(TAG, msg) | |||||
raise NotImplementedError(msg) | |||||
return norm_value.reshape(ori_shape) |
@@ -0,0 +1,154 @@ | |||||
# 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. | |||||
""" Util for log module. """ | |||||
import logging | |||||
_LOGGER = logging.getLogger('MA') | |||||
def _find_caller(): | |||||
""" | |||||
Bind findCaller() method, which is used to find the stack frame of the | |||||
caller so that we can note the source file name, line number and | |||||
function name. | |||||
""" | |||||
return _LOGGER.findCaller() | |||||
class LogUtil: | |||||
""" | |||||
Logging module. | |||||
Raises: | |||||
SyntaxError: If create this class. | |||||
""" | |||||
_instance = None | |||||
_logger = None | |||||
_extra_fmt = ' [%s] [%s] ' | |||||
def __init__(self): | |||||
raise SyntaxError('can not instance, please use get_instance.') | |||||
@staticmethod | |||||
def get_instance(): | |||||
""" | |||||
Get instance of class `LogUtil`. | |||||
Returns: | |||||
Object, instance of class `LogUtil`. | |||||
""" | |||||
if LogUtil._instance is None: | |||||
LogUtil._instance = object.__new__(LogUtil) | |||||
LogUtil._logger = _LOGGER | |||||
LogUtil._init_logger() | |||||
return LogUtil._instance | |||||
@staticmethod | |||||
def _init_logger(): | |||||
""" | |||||
Initialize logger. | |||||
""" | |||||
LogUtil._logger.setLevel(logging.WARNING) | |||||
log_fmt = '[%(levelname)s] %(name)s(%(process)d:%(thread)d,' \ | |||||
'%(processName)s):%(asctime)s%(message)s' | |||||
log_fmt = logging.Formatter(log_fmt) | |||||
# create console handler with a higher log level | |||||
console_handler = logging.StreamHandler() | |||||
console_handler.setFormatter(log_fmt) | |||||
# add the handlers to the logger | |||||
LogUtil._logger.handlers = [] | |||||
LogUtil._logger.addHandler(console_handler) | |||||
LogUtil._logger.propagate = False | |||||
def set_level(self, level): | |||||
""" | |||||
Set the logging level of this logger, level must be an integer or a | |||||
string. | |||||
Args: | |||||
level (Union[int, str]): Level of logger. | |||||
""" | |||||
self._logger.setLevel(level) | |||||
def add_handler(self, handler): | |||||
""" | |||||
Add other handler supported by logging module. | |||||
Args: | |||||
handler (logging.Handler): Other handler supported by logging module. | |||||
Raises: | |||||
ValueError: If handler is not an instance of logging.Handler. | |||||
""" | |||||
if isinstance(handler, logging.Handler): | |||||
self._logger.addHandler(handler) | |||||
else: | |||||
raise ValueError('handler must be an instance of logging.Handler,' | |||||
' but got {}'.format(type(handler))) | |||||
def debug(self, tag, msg, *args): | |||||
""" | |||||
Log '[tag] msg % args' with severity 'DEBUG'. | |||||
Args: | |||||
tag (str): Logger tag. | |||||
msg (str): Logger message. | |||||
args (Any): Auxiliary value. | |||||
""" | |||||
caller_info = _find_caller() | |||||
file_info = ':'.join([caller_info[0], str(caller_info[1])]) | |||||
self._logger.debug(self._extra_fmt + msg, file_info, tag, *args) | |||||
def info(self, tag, msg, *args): | |||||
""" | |||||
Log '[tag] msg % args' with severity 'INFO'. | |||||
Args: | |||||
tag (str): Logger tag. | |||||
msg (str): Logger message. | |||||
args (Any): Auxiliary value. | |||||
""" | |||||
caller_info = _find_caller() | |||||
file_info = ':'.join([caller_info[0], str(caller_info[1])]) | |||||
self._logger.info(self._extra_fmt + msg, file_info, tag, *args) | |||||
def warn(self, tag, msg, *args): | |||||
""" | |||||
Log '[tag] msg % args' with severity 'WARNING'. | |||||
Args: | |||||
tag (str): Logger tag. | |||||
msg (str): Logger message. | |||||
args (Any): Auxiliary value. | |||||
""" | |||||
caller_info = _find_caller() | |||||
file_info = ':'.join([caller_info[0], str(caller_info[1])]) | |||||
self._logger.warning(self._extra_fmt + msg, file_info, tag, *args) | |||||
def error(self, tag, msg, *args): | |||||
""" | |||||
Log '[tag] msg % args' with severity 'ERROR'. | |||||
Args: | |||||
tag (str): Logger tag. | |||||
msg (str): Logger message. | |||||
args (Any): Auxiliary value. | |||||
""" | |||||
caller_info = _find_caller() | |||||
file_info = ':'.join([caller_info[0], str(caller_info[1])]) | |||||
self._logger.error(self._extra_fmt + msg, file_info, tag, *args) |
@@ -0,0 +1,147 @@ | |||||
# 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. | |||||
""" Util for MindArmour. """ | |||||
import numpy as np | |||||
from mindspore import Tensor | |||||
from mindspore.nn import Cell | |||||
from mindspore.ops.composite import GradOperation | |||||
from mindarmour.utils.logger import LogUtil | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'util' | |||||
def jacobian_matrix(grad_wrap_net, inputs, num_classes): | |||||
""" | |||||
Calculate the Jacobian matrix for inputs. | |||||
Args: | |||||
grad_wrap_net (Cell): A network wrapped by GradWrap. | |||||
inputs (numpy.ndarray): Input samples. | |||||
num_classes (int): Number of labels of model output. | |||||
Returns: | |||||
numpy.ndarray, the Jacobian matrix of inputs. (labels, batch_size, ...) | |||||
Raises: | |||||
ValueError: If grad_wrap_net is not a instance of class `GradWrap`. | |||||
""" | |||||
if not isinstance(grad_wrap_net, GradWrap): | |||||
msg = 'grad_wrap_net be and instance of class `GradWrap`.' | |||||
LOGGER.error(TAG, msg) | |||||
raise ValueError(msg) | |||||
grad_wrap_net.set_train() | |||||
grads_matrix = [] | |||||
for idx in range(num_classes): | |||||
sens = np.zeros((inputs.shape[0], num_classes)).astype(np.float32) | |||||
sens[:, idx] = 1.0 | |||||
grads = grad_wrap_net(Tensor(inputs), Tensor(sens)) | |||||
grads_matrix.append(grads.asnumpy()) | |||||
return np.asarray(grads_matrix) | |||||
class WithLossCell(Cell): | |||||
""" | |||||
Wrap the network with loss function. | |||||
Args: | |||||
network (Cell): The target network to wrap. | |||||
loss_fn (Function): The loss function is used for computing loss. | |||||
Examples: | |||||
>>> data = Tensor(np.ones([1, 1, 32, 32]).astype(np.float32)*0.01) | |||||
>>> label = Tensor(np.ones([1, 10]).astype(np.float32)) | |||||
>>> net = NET() | |||||
>>> loss_fn = nn.SoftmaxCrossEntropyWithLogits() | |||||
>>> loss_net = WithLossCell(net, loss_fn) | |||||
>>> loss_out = loss_net(data, label) | |||||
""" | |||||
def __init__(self, network, loss_fn): | |||||
super(WithLossCell, self).__init__() | |||||
self._network = network | |||||
self._loss_fn = loss_fn | |||||
def construct(self, data, label): | |||||
""" | |||||
Compute loss based on the wrapped loss cell. | |||||
Args: | |||||
data (Tensor): Tensor data to train. | |||||
label (Tensor): Tensor label data. | |||||
Returns: | |||||
Tensor, compute result. | |||||
""" | |||||
out = self._network(data) | |||||
return self._loss_fn(out, label) | |||||
class GradWrapWithLoss(Cell): | |||||
""" | |||||
Construct a network to compute the gradient of loss function in input space | |||||
and weighted by `weight`. | |||||
""" | |||||
def __init__(self, network): | |||||
super(GradWrapWithLoss, self).__init__() | |||||
self._grad_all = GradOperation(name="get_all", | |||||
get_all=True, | |||||
sens_param=True) | |||||
self._network = network | |||||
def construct(self, inputs, labels, weight): | |||||
""" | |||||
Compute gradient of `inputs` with labels and weight. | |||||
Args: | |||||
inputs (Tensor): Inputs of network. | |||||
labels (Tensor): Labels of inputs. | |||||
weight (Tensor): Weight of each gradient, `weight` has the same | |||||
shape with labels. | |||||
Returns: | |||||
Tensor, gradient matrix. | |||||
""" | |||||
gout = self._grad_all(self._network)(inputs, labels, weight) | |||||
return gout[0] | |||||
class GradWrap(Cell): | |||||
""" | |||||
Construct a network to compute the gradient of network outputs in input | |||||
space and weighted by `weight`, expressed as a jacobian matrix. | |||||
""" | |||||
def __init__(self, network): | |||||
super(GradWrap, self).__init__() | |||||
self.grad = GradOperation(name="grad", get_all=False, | |||||
sens_param=True) | |||||
self.network = network | |||||
def construct(self, inputs, weight): | |||||
""" | |||||
Compute jacobian matrix. | |||||
Args: | |||||
inputs (Tensor): Inputs of network. | |||||
weight (Tensor): Weight of each gradient, `weight` has the same | |||||
shape with labels. | |||||
Returns: | |||||
Tensor, Jacobian matrix. | |||||
""" | |||||
gout = self.grad(self.network)(inputs, weight) | |||||
return gout |
@@ -0,0 +1,38 @@ | |||||
#!/bin/bash | |||||
# 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. | |||||
set -e | |||||
BASEPATH=$(cd "$(dirname $0)"; pwd) | |||||
OUTPUT_PATH="${BASEPATH}/output" | |||||
PYTHON=$(which python3) | |||||
mk_new_dir() { | |||||
local create_dir="$1" # the target to make | |||||
if [[ -d "${create_dir}" ]];then | |||||
rm -rf "${create_dir}" | |||||
fi | |||||
mkdir -pv "${create_dir}" | |||||
} | |||||
mk_new_dir "${OUTPUT_PATH}" | |||||
${PYTHON} ${BASEPATH}/setup.py bdist_wheel | |||||
mv ${BASEPATH}/dist/*whl ${OUTPUT_PATH} | |||||
echo "------Successfully created mindarmour package------" |
@@ -0,0 +1,7 @@ | |||||
numpy >= 1.17.0 | |||||
scipy >= 1.3.3 | |||||
matplotlib >= 3.1.3 | |||||
pytest >= 4.3.1 | |||||
wheel >= 0.32.0 | |||||
setuptools >= 40.8.0 | |||||
mindspore |
@@ -0,0 +1,102 @@ | |||||
# 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. | |||||
import os | |||||
import stat | |||||
from setuptools import find_packages | |||||
from setuptools import setup | |||||
from setuptools.command.egg_info import egg_info | |||||
from setuptools.command.build_py import build_py | |||||
version = '0.1.0' | |||||
cur_dir = os.path.dirname(os.path.realpath(__file__)) | |||||
pkg_dir = os.path.join(cur_dir, 'build') | |||||
try: | |||||
from wheel.bdist_wheel import bdist_wheel as _bdist_wheel | |||||
class bdist_wheel(_bdist_wheel): | |||||
def finalize_options(self): | |||||
_bdist_wheel.finalize_options(self) | |||||
self.root_is_pure = False | |||||
except ImportError: | |||||
bdist_wheel = None | |||||
def write_version(file): | |||||
file.write("__version__ = '{}'\n".format(version)) | |||||
def build_depends(): | |||||
"""generate python file""" | |||||
version_file = os.path.join(cur_dir, 'mindarmour/', 'version.py') | |||||
with open(version_file, 'w') as f: | |||||
write_version(f) | |||||
build_depends() | |||||
def update_permissions(path): | |||||
""" | |||||
Update permissions. | |||||
Args: | |||||
path (str): Target directory path. | |||||
""" | |||||
for dirpath, dirnames, filenames in os.walk(path): | |||||
for dirname in dirnames: | |||||
dir_fullpath = os.path.join(dirpath, dirname) | |||||
os.chmod(dir_fullpath, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC | stat.S_IRGRP | stat.S_IXGRP) | |||||
for filename in filenames: | |||||
file_fullpath = os.path.join(dirpath, filename) | |||||
os.chmod(file_fullpath, stat.S_IREAD) | |||||
class EggInfo(egg_info): | |||||
"""Egg info.""" | |||||
def run(self): | |||||
super().run() | |||||
egg_info_dir = os.path.join(cur_dir, 'mindarmour.egg-info') | |||||
update_permissions(egg_info_dir) | |||||
class BuildPy(build_py): | |||||
"""BuildPy.""" | |||||
def run(self): | |||||
super().run() | |||||
mindarmour_dir = os.path.join(pkg_dir, 'lib', 'mindarmour') | |||||
update_permissions(mindarmour_dir) | |||||
setup( | |||||
name='mindarmour', | |||||
version='0.1.0', | |||||
description="A smart AI security and trustworthy tool box.", | |||||
packages=find_packages(), | |||||
include_package_data=True, | |||||
zip_safe=False, | |||||
cmdclass={ | |||||
'egg_info': EggInfo, | |||||
'build_py': BuildPy, | |||||
'bdist_wheel': bdist_wheel | |||||
}, | |||||
install_requires=[ | |||||
'scipy >= 1.3.3', | |||||
'numpy >= 1.17.0', | |||||
'matplotlib >= 3.1.3', | |||||
'mindspore' | |||||
], | |||||
) | |||||
print(find_packages()) |
@@ -0,0 +1,311 @@ | |||||
# Copyright 2020 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. | |||||
import numpy as np | |||||
import math | |||||
from mindspore import nn | |||||
from mindspore.ops import operations as P | |||||
from mindspore.common.tensor import Tensor | |||||
from mindspore import context | |||||
def variance_scaling_raw(shape): | |||||
value = np.random.normal(size=shape).astype(np.float32) | |||||
return Tensor(value) | |||||
def weight_variable(shape): | |||||
value = np.random.normal(size=shape).astype(np.float32) | |||||
return Tensor(value) | |||||
def sweight_variable(shape): | |||||
value = np.random.uniform(size=shape).astype(np.float32) | |||||
return Tensor(value) | |||||
def weight_variable_0(shape): | |||||
zeros = np.zeros(shape).astype(np.float32) | |||||
return Tensor(zeros) | |||||
def weight_variable_1(shape): | |||||
ones = np.ones(shape).astype(np.float32) | |||||
return Tensor(ones) | |||||
def conv3x3(in_channels, out_channels, stride=1, padding=0): | |||||
"""3x3 convolution """ | |||||
weight_shape = (out_channels, in_channels, 3, 3) | |||||
weight = variance_scaling_raw(weight_shape) | |||||
return nn.Conv2d(in_channels, out_channels, | |||||
kernel_size=3, stride=stride, padding=padding, weight_init=weight, has_bias=False, pad_mode="same") | |||||
def conv1x1(in_channels, out_channels, stride=1, padding=0): | |||||
"""1x1 convolution""" | |||||
weight_shape = (out_channels, in_channels, 1, 1) | |||||
weight = variance_scaling_raw(weight_shape) | |||||
return nn.Conv2d(in_channels, out_channels, | |||||
kernel_size=1, stride=stride, padding=padding, weight_init=weight, has_bias=False, pad_mode="same") | |||||
def conv7x7(in_channels, out_channels, stride=1, padding=0): | |||||
"""1x1 convolution""" | |||||
weight_shape = (out_channels, in_channels, 7, 7) | |||||
weight = variance_scaling_raw(weight_shape) | |||||
return nn.Conv2d(in_channels, out_channels, | |||||
kernel_size=7, stride=stride, padding=padding, weight_init=weight, has_bias=False, pad_mode="same") | |||||
def bn_with_initialize(out_channels): | |||||
shape = (out_channels) | |||||
mean = weight_variable_0(shape) | |||||
var = weight_variable_1(shape) | |||||
beta = weight_variable_0(shape) | |||||
gamma = sweight_variable(shape) | |||||
bn = nn.BatchNorm2d(out_channels, momentum=0.99, eps=0.00001, gamma_init=gamma, | |||||
beta_init=beta, moving_mean_init=mean, moving_var_init=var) | |||||
return bn | |||||
def bn_with_initialize_last(out_channels): | |||||
shape = (out_channels) | |||||
mean = weight_variable_0(shape) | |||||
var = weight_variable_1(shape) | |||||
beta = weight_variable_0(shape) | |||||
gamma = sweight_variable(shape) | |||||
bn = nn.BatchNorm2d(out_channels, momentum=0.99, eps=0.00001, gamma_init=gamma, | |||||
beta_init=beta, moving_mean_init=mean, moving_var_init=var) | |||||
return bn | |||||
def fc_with_initialize(input_channels, out_channels): | |||||
weight_shape = (out_channels, input_channels) | |||||
weight = np.random.normal(size=weight_shape).astype(np.float32) | |||||
weight = Tensor(weight) | |||||
bias_shape = (out_channels) | |||||
bias_value = np.random.uniform(size=bias_shape).astype(np.float32) | |||||
bias = Tensor(bias_value) | |||||
return nn.Dense(input_channels, out_channels, weight, bias) | |||||
class ResidualBlock(nn.Cell): | |||||
expansion = 4 | |||||
def __init__(self, | |||||
in_channels, | |||||
out_channels, | |||||
stride=1, | |||||
down_sample=False): | |||||
super(ResidualBlock, self).__init__() | |||||
out_chls = out_channels // self.expansion | |||||
self.conv1 = conv1x1(in_channels, out_chls, stride=stride, padding=0) | |||||
self.bn1 = bn_with_initialize(out_chls) | |||||
self.conv2 = conv3x3(out_chls, out_chls, stride=1, padding=0) | |||||
self.bn2 = bn_with_initialize(out_chls) | |||||
self.conv3 = conv1x1(out_chls, out_channels, stride=1, padding=0) | |||||
self.bn3 = bn_with_initialize_last(out_channels) | |||||
self.relu = P.ReLU() | |||||
self.add = P.TensorAdd() | |||||
def construct(self, x): | |||||
identity = x | |||||
out = self.conv1(x) | |||||
out = self.bn1(out) | |||||
out = self.relu(out) | |||||
out = self.conv2(out) | |||||
out = self.bn2(out) | |||||
out = self.relu(out) | |||||
out = self.conv3(out) | |||||
out = self.bn3(out) | |||||
out = self.add(out, identity) | |||||
out = self.relu(out) | |||||
return out | |||||
class ResidualBlockWithDown(nn.Cell): | |||||
expansion = 4 | |||||
def __init__(self, | |||||
in_channels, | |||||
out_channels, | |||||
stride=1, | |||||
down_sample=False): | |||||
super(ResidualBlockWithDown, self).__init__() | |||||
out_chls = out_channels // self.expansion | |||||
self.conv1 = conv1x1(in_channels, out_chls, stride=stride, padding=0) | |||||
self.bn1 = bn_with_initialize(out_chls) | |||||
self.conv2 = conv3x3(out_chls, out_chls, stride=1, padding=0) | |||||
self.bn2 = bn_with_initialize(out_chls) | |||||
self.conv3 = conv1x1(out_chls, out_channels, stride=1, padding=0) | |||||
self.bn3 = bn_with_initialize_last(out_channels) | |||||
self.relu = P.ReLU() | |||||
self.downSample = down_sample | |||||
self.conv_down_sample = conv1x1(in_channels, out_channels, stride=stride, padding=0) | |||||
self.bn_down_sample = bn_with_initialize(out_channels) | |||||
self.add = P.TensorAdd() | |||||
def construct(self, x): | |||||
identity = x | |||||
out = self.conv1(x) | |||||
out = self.bn1(out) | |||||
out = self.relu(out) | |||||
out = self.conv2(out) | |||||
out = self.bn2(out) | |||||
out = self.relu(out) | |||||
out = self.conv3(out) | |||||
out = self.bn3(out) | |||||
identity = self.conv_down_sample(identity) | |||||
identity = self.bn_down_sample(identity) | |||||
out = self.add(out, identity) | |||||
out = self.relu(out) | |||||
return out | |||||
class MakeLayer0(nn.Cell): | |||||
def __init__(self, block, layer_num, in_channels, out_channels, stride): | |||||
super(MakeLayer0, self).__init__() | |||||
self.a = ResidualBlockWithDown(in_channels, out_channels, stride=1, down_sample=True) | |||||
self.b = block(out_channels, out_channels, stride=stride) | |||||
self.c = block(out_channels, out_channels, stride=1) | |||||
def construct(self, x): | |||||
x = self.a(x) | |||||
x = self.b(x) | |||||
x = self.c(x) | |||||
return x | |||||
class MakeLayer1(nn.Cell): | |||||
def __init__(self, block, layer_num, in_channels, out_channels, stride): | |||||
super(MakeLayer1, self).__init__() | |||||
self.a = ResidualBlockWithDown(in_channels, out_channels, stride=stride, down_sample=True) | |||||
self.b = block(out_channels, out_channels, stride=1) | |||||
self.c = block(out_channels, out_channels, stride=1) | |||||
self.d = block(out_channels, out_channels, stride=1) | |||||
def construct(self, x): | |||||
x = self.a(x) | |||||
x = self.b(x) | |||||
x = self.c(x) | |||||
x = self.d(x) | |||||
return x | |||||
class MakeLayer2(nn.Cell): | |||||
def __init__(self, block, layer_num, in_channels, out_channels, stride): | |||||
super(MakeLayer2, self).__init__() | |||||
self.a = ResidualBlockWithDown(in_channels, out_channels, stride=stride, down_sample=True) | |||||
self.b = block(out_channels, out_channels, stride=1) | |||||
self.c = block(out_channels, out_channels, stride=1) | |||||
self.d = block(out_channels, out_channels, stride=1) | |||||
self.e = block(out_channels, out_channels, stride=1) | |||||
self.f = block(out_channels, out_channels, stride=1) | |||||
def construct(self, x): | |||||
x = self.a(x) | |||||
x = self.b(x) | |||||
x = self.c(x) | |||||
x = self.d(x) | |||||
x = self.e(x) | |||||
x = self.f(x) | |||||
return x | |||||
class MakeLayer3(nn.Cell): | |||||
def __init__(self, block, layer_num, in_channels, out_channels, stride): | |||||
super(MakeLayer3, self).__init__() | |||||
self.a = ResidualBlockWithDown(in_channels, out_channels, stride=stride, down_sample=True) | |||||
self.b = block(out_channels, out_channels, stride=1) | |||||
self.c = block(out_channels, out_channels, stride=1) | |||||
def construct(self, x): | |||||
x = self.a(x) | |||||
x = self.b(x) | |||||
x = self.c(x) | |||||
return x | |||||
class ResNet(nn.Cell): | |||||
def __init__(self, block, layer_num, num_classes=100): | |||||
super(ResNet, self).__init__() | |||||
self.num_classes = num_classes | |||||
self.conv1 = conv7x7(3, 64, stride=2, padding=0) | |||||
self.bn1 = bn_with_initialize(64) | |||||
self.relu = P.ReLU() | |||||
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, pad_mode="same") | |||||
self.layer1 = MakeLayer0(block, layer_num[0], in_channels=64, out_channels=256, stride=1) | |||||
self.layer2 = MakeLayer1(block, layer_num[1], in_channels=256, out_channels=512, stride=2) | |||||
self.layer3 = MakeLayer2(block, layer_num[2], in_channels=512, out_channels=1024, stride=2) | |||||
self.layer4 = MakeLayer3(block, layer_num[3], in_channels=1024, out_channels=2048, stride=2) | |||||
self.pool = P.ReduceMean(keep_dims=True) | |||||
self.squeeze = P.Squeeze(axis=(2, 3)) | |||||
self.fc = fc_with_initialize(512*block.expansion, num_classes) | |||||
def construct(self, x): | |||||
x = self.conv1(x) | |||||
x = self.bn1(x) | |||||
x = self.relu(x) | |||||
x = self.maxpool(x) | |||||
x = self.layer1(x) | |||||
x = self.layer2(x) | |||||
x = self.layer3(x) | |||||
x = self.layer4(x) | |||||
x = self.pool(x, (2, 3)) | |||||
x = self.squeeze(x) | |||||
x = self.fc(x) | |||||
return x | |||||
def resnet50_cifar10(num_classes): | |||||
return ResNet(ResidualBlock, [3, 4, 6, 3], num_classes) |
@@ -0,0 +1,76 @@ | |||||
# Copyright 2020 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. | |||||
""" | |||||
Fuction: | |||||
Test fgsm attack about resnet50 network | |||||
Usage: | |||||
py.test test_cifar10_attack_fgsm.py | |||||
""" | |||||
import os | |||||
import numpy as np | |||||
import pytest | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.nn import Cell | |||||
from mindspore.common import dtype as mstype | |||||
from mindspore.ops import operations as P | |||||
from mindspore.ops import functional as F | |||||
from mindarmour.attacks.gradient_method import FastGradientSignMethod | |||||
from resnet_cifar10 import resnet50_cifar10 | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
class CrossEntropyLoss(Cell): | |||||
def __init__(self): | |||||
super(CrossEntropyLoss, self).__init__() | |||||
self.cross_entropy = P.SoftmaxCrossEntropyWithLogits() | |||||
self.mean = P.ReduceMean() | |||||
self.one_hot = P.OneHot() | |||||
self.on_value = Tensor(1.0, mstype.float32) | |||||
self.off_value = Tensor(0.0, mstype.float32) | |||||
def construct(self, logits, label): | |||||
label = self.one_hot(label, F.shape(logits)[1], self.on_value, self.off_value) | |||||
loss = self.cross_entropy(logits, label)[0] | |||||
loss = self.mean(loss, (-1,)) | |||||
return loss | |||||
@pytest.mark.level0 | |||||
@pytest.mark.env_single | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.platform_x86_ascend_inference | |||||
def test_fast_gradient_sign_method(): | |||||
""" | |||||
FGSM-Attack test | |||||
""" | |||||
context.set_context(mode=context.GRAPH_MODE) | |||||
# get network | |||||
net = resnet50_cifar10(10) | |||||
# create test data | |||||
test_images = np.random.rand(64, 3, 224, 224).astype(np.float32) | |||||
test_labels = np.random.randint(10, size=64).astype(np.int32) | |||||
# attacking | |||||
loss_fn = CrossEntropyLoss() | |||||
attack = FastGradientSignMethod(net, eps=0.1, loss_fn=loss_fn) | |||||
adv_data = attack.batch_generate(test_images, test_labels, batch_size=32) | |||||
assert np.any(adv_data != test_images) |
@@ -0,0 +1,144 @@ | |||||
# 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. | |||||
""" | |||||
Genetic-Attack test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
import mindspore.ops.operations as M | |||||
from mindspore import Tensor | |||||
from mindspore.nn import Cell | |||||
from mindspore import context | |||||
from mindarmour.attacks.black.genetic_attack import GeneticAttack | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
# for user | |||||
class ModelToBeAttacked(BlackModel): | |||||
"""model to be attack""" | |||||
def __init__(self, network): | |||||
super(ModelToBeAttacked, self).__init__() | |||||
self._network = network | |||||
def predict(self, inputs): | |||||
"""predict""" | |||||
result = self._network(Tensor(inputs.astype(np.float32))) | |||||
return result.asnumpy() | |||||
class SimpleNet(Cell): | |||||
""" | |||||
Construct the network of target model. | |||||
Examples: | |||||
>>> net = SimpleNet() | |||||
""" | |||||
def __init__(self): | |||||
""" | |||||
Introduce the layers used for network construction. | |||||
""" | |||||
super(SimpleNet, self).__init__() | |||||
self._softmax = M.Softmax() | |||||
def construct(self, inputs): | |||||
""" | |||||
Construct network. | |||||
Args: | |||||
inputs (Tensor): Input data. | |||||
""" | |||||
out = self._softmax(inputs) | |||||
return out | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_genetic_attack(): | |||||
""" | |||||
Genetic_Attack test | |||||
""" | |||||
batch_size = 6 | |||||
net = SimpleNet() | |||||
inputs = np.random.rand(batch_size, 10) | |||||
model = ModelToBeAttacked(net) | |||||
labels = np.random.randint(low=0, high=10, size=batch_size) | |||||
labels = np.eye(10)[labels] | |||||
labels = labels.astype(np.float32) | |||||
attack = GeneticAttack(model, pop_size=6, mutation_rate=0.05, | |||||
per_bounds=0.1, step_size=0.25, temp=0.1, | |||||
sparse=False) | |||||
_, adv_data, _ = attack.generate(inputs, labels) | |||||
assert np.any(inputs != adv_data) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_supplement(): | |||||
batch_size = 6 | |||||
net = SimpleNet() | |||||
inputs = np.random.rand(batch_size, 10) | |||||
model = ModelToBeAttacked(net) | |||||
labels = np.random.randint(low=0, high=10, size=batch_size) | |||||
labels = np.eye(10)[labels] | |||||
labels = labels.astype(np.float32) | |||||
attack = GeneticAttack(model, pop_size=6, mutation_rate=0.05, | |||||
per_bounds=0.1, step_size=0.25, temp=0.1, | |||||
adaptive=True, | |||||
sparse=False) | |||||
# raise error | |||||
_, adv_data, _ = attack.generate(inputs, labels) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_value_error(): | |||||
"""test that exception is raised for invalid labels""" | |||||
batch_size = 6 | |||||
net = SimpleNet() | |||||
inputs = np.random.rand(batch_size, 10) | |||||
model = ModelToBeAttacked(net) | |||||
labels = np.random.randint(low=0, high=10, size=batch_size) | |||||
# labels = np.eye(10)[labels] | |||||
labels = labels.astype(np.float32) | |||||
attack = GeneticAttack(model, pop_size=6, mutation_rate=0.05, | |||||
per_bounds=0.1, step_size=0.25, temp=0.1, | |||||
adaptive=True, | |||||
sparse=False) | |||||
# raise error | |||||
with pytest.raises(ValueError) as e: | |||||
assert attack.generate(inputs, labels) |
@@ -0,0 +1,166 @@ | |||||
# 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. | |||||
import sys | |||||
import os | |||||
import numpy as np | |||||
import pytest | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks.black.hop_skip_jump_attack import HopSkipJumpAttack | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
from mindarmour.utils.logger import LogUtil | |||||
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), | |||||
"../../../../../")) | |||||
from example.mnist_demo.lenet5_net import LeNet5 | |||||
context.set_context(mode=context.GRAPH_MODE) | |||||
context.set_context(device_target="Ascend") | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'HopSkipJumpAttack' | |||||
class ModelToBeAttacked(BlackModel): | |||||
"""model to be attack""" | |||||
def __init__(self, network): | |||||
super(ModelToBeAttacked, self).__init__() | |||||
self._network = network | |||||
def predict(self, inputs): | |||||
"""predict""" | |||||
if len(inputs.shape) == 3: | |||||
inputs = inputs[np.newaxis, :] | |||||
result = self._network(Tensor(inputs.astype(np.float32))) | |||||
return result.asnumpy() | |||||
def random_target_labels(true_labels): | |||||
target_labels = [] | |||||
for label in true_labels: | |||||
while True: | |||||
target_label = np.random.randint(0, 10) | |||||
if target_label != label: | |||||
target_labels.append(target_label) | |||||
break | |||||
return target_labels | |||||
def create_target_images(dataset, data_labels, target_labels): | |||||
res = [] | |||||
for label in target_labels: | |||||
for i in range(len(data_labels)): | |||||
if data_labels[i] == label: | |||||
res.append(dataset[i]) | |||||
break | |||||
return np.array(res) | |||||
# public variable | |||||
def get_model(): | |||||
# upload trained network | |||||
current_dir = os.path.dirname(os.path.abspath(__file__)) | |||||
ckpt_name = os.path.join(current_dir, | |||||
'../../test_data/trained_ckpt_file/checkpoint_lenet-10_1875.ckpt') | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
net.set_train(False) | |||||
model = ModelToBeAttacked(net) | |||||
return model | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_hsja_mnist_attack(): | |||||
""" | |||||
hsja-Attack test | |||||
""" | |||||
current_dir = os.path.dirname(os.path.abspath(__file__)) | |||||
# get test data | |||||
test_images_set = np.load(os.path.join(current_dir, | |||||
'../../test_data/test_images.npy')) | |||||
test_labels_set = np.load(os.path.join(current_dir, | |||||
'../../test_data/test_labels.npy')) | |||||
# prediction accuracy before attack | |||||
model = get_model() | |||||
batch_num = 1 # the number of batches of attacking samples | |||||
predict_labels = [] | |||||
i = 0 | |||||
for img in test_images_set: | |||||
i += 1 | |||||
pred_labels = np.argmax(model.predict(img), axis=1) | |||||
predict_labels.append(pred_labels) | |||||
if i >= batch_num: | |||||
break | |||||
predict_labels = np.concatenate(predict_labels) | |||||
true_labels = test_labels_set[:batch_num] | |||||
accuracy = np.mean(np.equal(predict_labels, true_labels)) | |||||
LOGGER.info(TAG, "prediction accuracy before attacking is : %s", | |||||
accuracy) | |||||
test_images = test_images_set[:batch_num] | |||||
# attacking | |||||
norm = 'l2' | |||||
search = 'grid_search' | |||||
target = False | |||||
attack = HopSkipJumpAttack(model, constraint=norm, stepsize_search=search) | |||||
if target: | |||||
target_labels = random_target_labels(true_labels) | |||||
target_images = create_target_images(test_images_set, test_labels_set, | |||||
target_labels) | |||||
LOGGER.info(TAG, 'len target labels : %s', len(target_labels)) | |||||
LOGGER.info(TAG, 'len target_images : %s', len(target_images)) | |||||
LOGGER.info(TAG, 'len test_images : %s', len(test_images)) | |||||
attack.set_target_images(target_images) | |||||
success_list, adv_data, _ = attack.generate(test_images, target_labels) | |||||
else: | |||||
success_list, adv_data, query_list = attack.generate(test_images, None) | |||||
assert (adv_data != test_images).any() | |||||
adv_datas = [] | |||||
gts = [] | |||||
for success, adv, gt in zip(success_list, adv_data, true_labels): | |||||
if success: | |||||
adv_datas.append(adv) | |||||
gts.append(gt) | |||||
if len(gts) > 0: | |||||
adv_datas = np.concatenate(np.asarray(adv_datas), axis=0) | |||||
gts = np.asarray(gts) | |||||
pred_logits_adv = model.predict(adv_datas) | |||||
pred_lables_adv = np.argmax(pred_logits_adv, axis=1) | |||||
accuracy_adv = np.mean(np.equal(pred_lables_adv, gts)) | |||||
LOGGER.info(TAG, 'mis-classification rate of adversaries is : %s', | |||||
accuracy_adv) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_value_error(): | |||||
model = get_model() | |||||
norm = 'l2' | |||||
with pytest.raises(ValueError) as e: | |||||
assert HopSkipJumpAttack(model, constraint=norm, stepsize_search='bad-search') |
@@ -0,0 +1,217 @@ | |||||
# 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. | |||||
import sys | |||||
import numpy as np | |||||
import os | |||||
import pytest | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks.black.natural_evolutionary_strategy import NES | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
from mindarmour.utils.logger import LogUtil | |||||
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), | |||||
"../../../../../")) | |||||
from example.mnist_demo.lenet5_net import LeNet5 | |||||
context.set_context(mode=context.GRAPH_MODE) | |||||
context.set_context(device_target="Ascend") | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'HopSkipJumpAttack' | |||||
class ModelToBeAttacked(BlackModel): | |||||
"""model to be attack""" | |||||
def __init__(self, network): | |||||
super(ModelToBeAttacked, self).__init__() | |||||
self._network = network | |||||
def predict(self, inputs): | |||||
"""predict""" | |||||
if len(inputs.shape) == 3: | |||||
inputs = inputs[np.newaxis, :] | |||||
result = self._network(Tensor(inputs.astype(np.float32))) | |||||
return result.asnumpy() | |||||
def random_target_labels(true_labels): | |||||
target_labels = [] | |||||
for label in true_labels: | |||||
while True: | |||||
target_label = np.random.randint(0, 10) | |||||
if target_label != label: | |||||
target_labels.append(target_label) | |||||
break | |||||
return target_labels | |||||
def _pseudorandom_target(index, total_indices, true_class): | |||||
""" pseudo random_target """ | |||||
rng = np.random.RandomState(index) | |||||
target = true_class | |||||
while target == true_class: | |||||
target = rng.randint(0, total_indices) | |||||
return target | |||||
def create_target_images(dataset, data_labels, target_labels): | |||||
res = [] | |||||
for label in target_labels: | |||||
for i in range(len(data_labels)): | |||||
if data_labels[i] == label: | |||||
res.append(dataset[i]) | |||||
break | |||||
return np.array(res) | |||||
def get_model(current_dir): | |||||
ckpt_name = os.path.join(current_dir, | |||||
'../../test_data/trained_ckpt_file/checkpoint_lenet-10_1875.ckpt') | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
net.set_train(False) | |||||
model = ModelToBeAttacked(net) | |||||
return model | |||||
def get_dataset(current_dir): | |||||
# upload trained network | |||||
# get test data | |||||
test_images = np.load(os.path.join(current_dir, | |||||
'../../test_data/test_images.npy')) | |||||
test_labels = np.load(os.path.join(current_dir, | |||||
'../../test_data/test_labels.npy')) | |||||
return test_images, test_labels | |||||
def nes_mnist_attack(scene, top_k): | |||||
""" | |||||
hsja-Attack test | |||||
""" | |||||
current_dir = os.path.dirname(os.path.abspath(__file__)) | |||||
test_images, test_labels = get_dataset(current_dir) | |||||
model = get_model(current_dir) | |||||
# prediction accuracy before attack | |||||
batch_num = 5 # the number of batches of attacking samples | |||||
predict_labels = [] | |||||
i = 0 | |||||
for img in test_images: | |||||
i += 1 | |||||
pred_labels = np.argmax(model.predict(img), axis=1) | |||||
predict_labels.append(pred_labels) | |||||
if i >= batch_num: | |||||
break | |||||
predict_labels = np.concatenate(predict_labels) | |||||
true_labels = test_labels | |||||
accuracy = np.mean(np.equal(predict_labels, true_labels[:batch_num])) | |||||
LOGGER.info(TAG, "prediction accuracy before attacking is : %s", | |||||
accuracy) | |||||
test_images = test_images | |||||
# attacking | |||||
if scene == 'Query_Limit': | |||||
top_k = -1 | |||||
elif scene == 'Partial_Info': | |||||
top_k = top_k | |||||
elif scene == 'Label_Only': | |||||
top_k = top_k | |||||
success = 0 | |||||
queries_num = 0 | |||||
nes_instance = NES(model, scene, top_k=top_k) | |||||
test_length = 1 | |||||
advs = [] | |||||
for img_index in range(test_length): | |||||
# INITIAL IMAGE AND CLASS SELECTION | |||||
initial_img = test_images[img_index] | |||||
orig_class = true_labels[img_index] | |||||
initial_img = [initial_img] | |||||
target_class = random_target_labels([orig_class]) | |||||
target_image = create_target_images(test_images, true_labels, | |||||
target_class) | |||||
nes_instance.set_target_images(target_image) | |||||
tag, adv, queries = nes_instance.generate(initial_img, target_class) | |||||
if tag[0]: | |||||
success += 1 | |||||
queries_num += queries[0] | |||||
advs.append(adv) | |||||
advs = np.reshape(advs, (len(advs), 1, 32, 32)) | |||||
assert (advs != test_images[:batch_num]).any() | |||||
adv_pred = np.argmax(model.predict(advs), axis=1) | |||||
adv_accuracy = np.mean(np.equal(adv_pred, true_labels[:test_length])) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_nes_query_limit(): | |||||
# scene is in ['Query_Limit', 'Partial_Info', 'Label_Only'] | |||||
scene = 'Query_Limit' | |||||
nes_mnist_attack(scene, top_k=-1) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_nes_partial_info(): | |||||
# scene is in ['Query_Limit', 'Partial_Info', 'Label_Only'] | |||||
scene = 'Partial_Info' | |||||
nes_mnist_attack(scene, top_k=5) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_nes_label_only(): | |||||
# scene is in ['Query_Limit', 'Partial_Info', 'Label_Only'] | |||||
scene = 'Label_Only' | |||||
nes_mnist_attack(scene, top_k=5) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_value_error(): | |||||
"""test that exception is raised for invalid labels""" | |||||
with pytest.raises(ValueError): | |||||
assert nes_mnist_attack('Label_Only', -1) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_none(): | |||||
current_dir = os.path.dirname(os.path.abspath(__file__)) | |||||
model = get_model(current_dir) | |||||
test_images, test_labels = get_dataset(current_dir) | |||||
nes = NES(model, 'Partial_Info') | |||||
with pytest.raises(ValueError): | |||||
assert nes.generate(test_images, test_labels) |
@@ -0,0 +1,90 @@ | |||||
# 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. | |||||
""" | |||||
PointWise Attack test | |||||
""" | |||||
import sys | |||||
import os | |||||
import numpy as np | |||||
import pytest | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks.black.pointwise_attack import PointWiseAttack | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), | |||||
"../../../../../")) | |||||
from example.mnist_demo.lenet5_net import LeNet5 | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'Pointwise_Test' | |||||
LOGGER.set_level('INFO') | |||||
class ModelToBeAttacked(BlackModel): | |||||
"""model to be attack""" | |||||
def __init__(self, network): | |||||
super(ModelToBeAttacked, self).__init__() | |||||
self._network = network | |||||
def predict(self, inputs): | |||||
"""predict""" | |||||
result = self._network(Tensor(inputs.astype(np.float32))) | |||||
return result.asnumpy() | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_pointwise_attack_method(): | |||||
""" | |||||
Pointwise attack method unit test. | |||||
""" | |||||
np.random.seed(123) | |||||
# upload trained network | |||||
current_dir = os.path.dirname(os.path.abspath(__file__)) | |||||
ckpt_name = os.path.join(current_dir, | |||||
'../../test_data/trained_ckpt_file/checkpoint_lenet-10_1875.ckpt') | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
# get one mnist image | |||||
input_np = np.load(os.path.join(current_dir, | |||||
'../../test_data/test_images.npy'))[:3] | |||||
labels = np.load(os.path.join(current_dir, | |||||
'../../test_data/test_labels.npy'))[:3] | |||||
model = ModelToBeAttacked(net) | |||||
pre_label = np.argmax(model.predict(input_np), axis=1) | |||||
LOGGER.info(TAG, 'original sample predict labels are :{}'.format(pre_label)) | |||||
LOGGER.info(TAG, 'true labels are: {}'.format(labels)) | |||||
attack = PointWiseAttack(model, sparse=True, is_targeted=False) | |||||
is_adv, adv_data, query_times = attack.generate(input_np, pre_label) | |||||
LOGGER.info(TAG, 'adv sample predict labels are: {}' | |||||
.format(np.argmax(model.predict(adv_data), axis=1))) | |||||
assert np.any(adv_data[is_adv][0] != input_np[is_adv][0]), 'Pointwise attack method: ' \ | |||||
'generate value must not be equal' \ | |||||
' to original value.' |
@@ -0,0 +1,166 @@ | |||||
# 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. | |||||
""" | |||||
PSO-Attack test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
from mindspore import Tensor | |||||
import mindspore.nn as nn | |||||
from mindspore.nn import Cell | |||||
from mindspore import context | |||||
from mindarmour.attacks.black.pso_attack import PSOAttack | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
# for user | |||||
class ModelToBeAttacked(BlackModel): | |||||
"""model to be attack""" | |||||
def __init__(self, network): | |||||
super(ModelToBeAttacked, self).__init__() | |||||
self._network = network | |||||
def predict(self, inputs): | |||||
"""predict""" | |||||
result = self._network(Tensor(inputs.astype(np.float32))) | |||||
return result.asnumpy() | |||||
class SimpleNet(Cell): | |||||
""" | |||||
Construct the network of target model. | |||||
Examples: | |||||
>>> net = SimpleNet() | |||||
""" | |||||
def __init__(self): | |||||
""" | |||||
Introduce the layers used for network construction. | |||||
""" | |||||
super(SimpleNet, self).__init__() | |||||
self._relu = nn.ReLU() | |||||
def construct(self, inputs): | |||||
""" | |||||
Construct network. | |||||
Args: | |||||
inputs (Tensor): Input data. | |||||
""" | |||||
out = self._relu(inputs) | |||||
return out | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_pso_attack(): | |||||
""" | |||||
PSO_Attack test | |||||
""" | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
batch_size = 6 | |||||
net = SimpleNet() | |||||
inputs = np.random.rand(batch_size, 10) | |||||
model = ModelToBeAttacked(net) | |||||
labels = np.random.randint(low=0, high=10, size=batch_size) | |||||
labels = np.eye(10)[labels] | |||||
labels = labels.astype(np.float32) | |||||
attack = PSOAttack(model, bounds=(0.0, 1.0), pm=0.5, sparse=False) | |||||
_, adv_data, _ = attack.generate(inputs, labels) | |||||
assert np.any(inputs != adv_data) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_pso_attack_targeted(): | |||||
""" | |||||
PSO_Attack test | |||||
""" | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
batch_size = 6 | |||||
net = SimpleNet() | |||||
inputs = np.random.rand(batch_size, 10) | |||||
model = ModelToBeAttacked(net) | |||||
labels = np.random.randint(low=0, high=10, size=batch_size) | |||||
labels = np.eye(10)[labels] | |||||
labels = labels.astype(np.float32) | |||||
attack = PSOAttack(model, bounds=(0.0, 1.0), pm=0.5, targeted=True, | |||||
sparse=False) | |||||
_, adv_data, _ = attack.generate(inputs, labels) | |||||
assert np.any(inputs != adv_data) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_x86_gpu_inference | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_pso_attack_gpu(): | |||||
""" | |||||
PSO_Attack test | |||||
""" | |||||
context.set_context(device_target="GPU") | |||||
batch_size = 6 | |||||
net = SimpleNet() | |||||
inputs = np.random.rand(batch_size, 10) | |||||
model = ModelToBeAttacked(net) | |||||
labels = np.random.randint(low=0, high=10, size=batch_size) | |||||
labels = np.eye(10)[labels] | |||||
labels = labels.astype(np.float32) | |||||
attack = PSOAttack(model, bounds=(0.0, 1.0), pm=0.5, sparse=False) | |||||
_, adv_data, _ = attack.generate(inputs, labels) | |||||
assert np.any(inputs != adv_data) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_x86_cpu | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_pso_attack_cpu(): | |||||
""" | |||||
PSO_Attack test | |||||
""" | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="CPU") | |||||
batch_size = 6 | |||||
net = SimpleNet() | |||||
inputs = np.random.rand(batch_size, 10) | |||||
model = ModelToBeAttacked(net) | |||||
labels = np.random.randint(low=0, high=10, size=batch_size) | |||||
labels = np.eye(10)[labels] | |||||
labels = labels.astype(np.float32) | |||||
attack = PSOAttack(model, bounds=(0.0, 1.0), pm=0.5, sparse=False) | |||||
_, adv_data, _ = attack.generate(inputs, labels) | |||||
assert np.any(inputs != adv_data) |
@@ -0,0 +1,123 @@ | |||||
# 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. | |||||
""" | |||||
SaltAndPepper Attack Test | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
import mindspore.ops.operations as M | |||||
from mindspore import Tensor | |||||
from mindspore.nn import Cell | |||||
from mindspore import context | |||||
from mindarmour.attacks.black.salt_and_pepper_attack import \ | |||||
SaltAndPepperNoiseAttack | |||||
from mindarmour.attacks.black.black_model import BlackModel | |||||
context.set_context(mode=context.GRAPH_MODE) | |||||
context.set_context(device_target="Ascend") | |||||
# for user | |||||
class ModelToBeAttacked(BlackModel): | |||||
"""model to be attack""" | |||||
def __init__(self, network): | |||||
super(ModelToBeAttacked, self).__init__() | |||||
self._network = network | |||||
def predict(self, inputs): | |||||
"""predict""" | |||||
result = self._network(Tensor(inputs.astype(np.float32))) | |||||
return result.asnumpy() | |||||
# for user | |||||
class SimpleNet(Cell): | |||||
""" | |||||
Construct the network of target model. | |||||
Examples: | |||||
>>> net = SimpleNet() | |||||
""" | |||||
def __init__(self): | |||||
""" | |||||
Introduce the layers used for network construction. | |||||
""" | |||||
super(SimpleNet, self).__init__() | |||||
self._softmax = M.Softmax() | |||||
def construct(self, inputs): | |||||
""" | |||||
Construct network. | |||||
Args: | |||||
inputs (Tensor): Input data. | |||||
""" | |||||
out = self._softmax(inputs) | |||||
return out | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_salt_and_pepper_attack_method(): | |||||
""" | |||||
Salt and pepper attack method unit test. | |||||
""" | |||||
batch_size = 6 | |||||
np.random.seed(123) | |||||
net = SimpleNet() | |||||
inputs = np.random.rand(batch_size, 10) | |||||
model = ModelToBeAttacked(net) | |||||
labels = np.random.randint(low=0, high=10, size=batch_size) | |||||
labels = np.eye(10)[labels] | |||||
labels = labels.astype(np.float32) | |||||
attack = SaltAndPepperNoiseAttack(model, sparse=False) | |||||
is_adv, adv_data, query_times = attack.generate(inputs, labels) | |||||
assert np.any(adv_data[0] != inputs[0]), 'Salt and pepper attack method: ' \ | |||||
'generate value must not be equal' \ | |||||
' to original value.' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_salt_and_pepper_attack_in_batch(): | |||||
""" | |||||
Salt and pepper attack method unit test in batch. | |||||
""" | |||||
batch_size = 32 | |||||
np.random.seed(123) | |||||
net = SimpleNet() | |||||
inputs = np.random.rand(batch_size*2, 10) | |||||
model = ModelToBeAttacked(net) | |||||
labels = np.random.randint(low=0, high=10, size=batch_size*2) | |||||
labels = np.eye(10)[labels] | |||||
labels = labels.astype(np.float32) | |||||
attack = SaltAndPepperNoiseAttack(model, sparse=False) | |||||
adv_data = attack.batch_generate(inputs, labels, batch_size=32) | |||||
assert np.any(adv_data[0] != inputs[0]), 'Salt and pepper attack method: ' \ | |||||
'generate value must not be equal' \ | |||||
' to original value.' |
@@ -0,0 +1,74 @@ | |||||
# 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. | |||||
""" | |||||
Batch-generate-attack test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
import mindspore.ops.operations as P | |||||
from mindspore.nn import Cell | |||||
import mindspore.context as context | |||||
from mindarmour.attacks.gradient_method import FastGradientMethod | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
# for user | |||||
class Net(Cell): | |||||
""" | |||||
Construct the network of target model. | |||||
Examples: | |||||
>>> net = Net() | |||||
""" | |||||
def __init__(self): | |||||
""" | |||||
Introduce the layers used for network construction. | |||||
""" | |||||
super(Net, self).__init__() | |||||
self._softmax = P.Softmax() | |||||
def construct(self, inputs): | |||||
""" | |||||
Construct network. | |||||
Args: | |||||
inputs (Tensor): Input data. | |||||
""" | |||||
out = self._softmax(inputs) | |||||
return out | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_batch_generate_attack(): | |||||
""" | |||||
Attack with batch-generate. | |||||
""" | |||||
input_np = np.random.random((128, 10)).astype(np.float32) | |||||
label = np.random.randint(0, 10, 128).astype(np.int32) | |||||
label = np.eye(10)[label].astype(np.float32) | |||||
attack = FastGradientMethod(Net()) | |||||
ms_adv_x = attack.batch_generate(input_np, label, batch_size=32) | |||||
assert np.any(ms_adv_x != input_np), 'Fast gradient method: generate value' \ | |||||
' must not be equal to original value.' |
@@ -0,0 +1,90 @@ | |||||
# 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. | |||||
""" | |||||
CW-Attack test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
import mindspore.ops.operations as M | |||||
from mindspore.nn import Cell | |||||
from mindspore import context | |||||
from mindarmour.attacks.carlini_wagner import CarliniWagnerL2Attack | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
# for user | |||||
class Net(Cell): | |||||
""" | |||||
Construct the network of target model. | |||||
Examples: | |||||
>>> net = Net() | |||||
""" | |||||
def __init__(self): | |||||
""" | |||||
Introduce the layers used for network construction. | |||||
""" | |||||
super(Net, self).__init__() | |||||
self._softmax = M.Softmax() | |||||
def construct(self, inputs): | |||||
""" | |||||
Construct network. | |||||
Args: | |||||
inputs (Tensor): Input data. | |||||
""" | |||||
out = self._softmax(inputs) | |||||
return out | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_cw_attack(): | |||||
""" | |||||
CW-Attack test | |||||
""" | |||||
net = Net() | |||||
input_np = np.array([[0.1, 0.2, 0.7, 0.5, 0.4]]).astype(np.float32) | |||||
label_np = np.array([3]).astype(np.int64) | |||||
num_classes = input_np.shape[1] | |||||
attack = CarliniWagnerL2Attack(net, num_classes, targeted=False) | |||||
adv_data = attack.generate(input_np, label_np) | |||||
assert np.any(input_np != adv_data) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_cw_attack_targeted(): | |||||
""" | |||||
CW-Attack test | |||||
""" | |||||
net = Net() | |||||
input_np = np.array([[0.1, 0.2, 0.7, 0.5, 0.4]]).astype(np.float32) | |||||
target_np = np.array([1]).astype(np.int64) | |||||
num_classes = input_np.shape[1] | |||||
attack = CarliniWagnerL2Attack(net, num_classes, targeted=True) | |||||
adv_data = attack.generate(input_np, target_np) | |||||
assert np.any(input_np != adv_data) |
@@ -0,0 +1,119 @@ | |||||
# 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. | |||||
""" | |||||
DeepFool-Attack test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
import mindspore.ops.operations as M | |||||
from mindspore.nn import Cell | |||||
from mindspore import context | |||||
from mindspore import Tensor | |||||
from mindarmour.attacks.deep_fool import DeepFool | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
# for user | |||||
class Net(Cell): | |||||
""" | |||||
Construct the network of target model. | |||||
Examples: | |||||
>>> net = Net() | |||||
""" | |||||
def __init__(self): | |||||
""" | |||||
Introduce the layers used for network construction. | |||||
""" | |||||
super(Net, self).__init__() | |||||
self._softmax = M.Softmax() | |||||
def construct(self, inputs): | |||||
""" | |||||
Construct network. | |||||
Args: | |||||
inputs (Tensor): Input data. | |||||
""" | |||||
out = self._softmax(inputs) | |||||
return out | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_deepfool_attack(): | |||||
""" | |||||
Deepfool-Attack test | |||||
""" | |||||
net = Net() | |||||
input_shape = (1, 5) | |||||
_, classes = input_shape | |||||
input_np = np.array([[0.1, 0.2, 0.7, 0.5, 0.4]]).astype(np.float32) | |||||
input_me = Tensor(input_np) | |||||
true_labels = np.argmax(net(input_me).asnumpy(), axis=1) | |||||
attack = DeepFool(net, classes, max_iters=10, norm_level=2, | |||||
bounds=(0.0, 1.0)) | |||||
adv_data = attack.generate(input_np, true_labels) | |||||
# expected adv value | |||||
expect_value = np.asarray([[0.10300991, 0.20332647, 0.59308802, 0.59651263, | |||||
0.40406296]]) | |||||
assert np.allclose(adv_data, expect_value), 'mindspore deepfool_method' \ | |||||
' implementation error, ms_adv_x != expect_value' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_deepfool_attack_inf(): | |||||
""" | |||||
Deepfool-Attack test | |||||
""" | |||||
net = Net() | |||||
input_shape = (1, 5) | |||||
_, classes = input_shape | |||||
input_np = np.array([[0.1, 0.2, 0.7, 0.5, 0.4]]).astype(np.float32) | |||||
input_me = Tensor(input_np) | |||||
true_labels = np.argmax(net(input_me).asnumpy(), axis=1) | |||||
attack = DeepFool(net, classes, max_iters=10, norm_level=np.inf, | |||||
bounds=(0.0, 1.0)) | |||||
adv_data = attack.generate(input_np, true_labels) | |||||
assert np.any(input_np != adv_data) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_value_error(): | |||||
net = Net() | |||||
input_shape = (1, 5) | |||||
_, classes = input_shape | |||||
input_np = np.array([[0.1, 0.2, 0.7, 0.5, 0.4]]).astype(np.float32) | |||||
input_me = Tensor(input_np) | |||||
true_labels = np.argmax(net(input_me).asnumpy(), axis=1) | |||||
with pytest.raises(NotImplementedError): | |||||
# norm_level=0 is not available | |||||
attack = DeepFool(net, classes, max_iters=10, norm_level=1, | |||||
bounds=(0.0, 1.0)) | |||||
assert attack.generate(input_np, true_labels) |
@@ -0,0 +1,242 @@ | |||||
# 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. | |||||
""" | |||||
Gradient-Attack test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
import mindspore.nn as nn | |||||
from mindspore.nn import Cell | |||||
import mindspore.context as context | |||||
from mindspore.nn import SoftmaxCrossEntropyWithLogits | |||||
from mindarmour.attacks.gradient_method import FastGradientMethod | |||||
from mindarmour.attacks.gradient_method import FastGradientSignMethod | |||||
from mindarmour.attacks.gradient_method import LeastLikelyClassMethod | |||||
from mindarmour.attacks.gradient_method import RandomFastGradientMethod | |||||
from mindarmour.attacks.gradient_method import RandomFastGradientSignMethod | |||||
from mindarmour.attacks.gradient_method import RandomLeastLikelyClassMethod | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
# for user | |||||
class Net(Cell): | |||||
""" | |||||
Construct the network of target model. | |||||
Examples: | |||||
>>> net = Net() | |||||
""" | |||||
def __init__(self): | |||||
""" | |||||
Introduce the layers used for network construction. | |||||
""" | |||||
super(Net, self).__init__() | |||||
self._relu = nn.ReLU() | |||||
def construct(self, inputs): | |||||
""" | |||||
Construct network. | |||||
Args: | |||||
inputs (Tensor): Input data. | |||||
""" | |||||
out = self._relu(inputs) | |||||
return out | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_fast_gradient_method(): | |||||
""" | |||||
Fast gradient method unit test. | |||||
""" | |||||
input_np = np.asarray([[0.1, 0.2, 0.7]], np.float32) | |||||
label = np.asarray([2], np.int32) | |||||
label = np.eye(3)[label].astype(np.float32) | |||||
attack = FastGradientMethod(Net()) | |||||
ms_adv_x = attack.generate(input_np, label) | |||||
assert np.any(ms_adv_x != input_np), 'Fast gradient method: generate value' \ | |||||
' must not be equal to original value.' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_x86_gpu_inference | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_fast_gradient_method_gpu(): | |||||
""" | |||||
Fast gradient method unit test. | |||||
""" | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="GPU") | |||||
input_np = np.asarray([[0.1, 0.2, 0.7]], np.float32) | |||||
label = np.asarray([2], np.int32) | |||||
label = np.eye(3)[label].astype(np.float32) | |||||
attack = FastGradientMethod(Net()) | |||||
ms_adv_x = attack.generate(input_np, label) | |||||
assert np.any(ms_adv_x != input_np), 'Fast gradient method: generate value' \ | |||||
' must not be equal to original value.' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_x86_cpu | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_fast_gradient_method_cpu(): | |||||
""" | |||||
Fast gradient method unit test. | |||||
""" | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="CPU") | |||||
input_np = np.asarray([[0.1, 0.2, 0.7]], np.float32) | |||||
label = np.asarray([2], np.int32) | |||||
loss = SoftmaxCrossEntropyWithLogits(is_grad=False, sparse=True) | |||||
attack = FastGradientMethod(Net(), loss_fn=loss) | |||||
ms_adv_x = attack.generate(input_np, label) | |||||
assert np.any(ms_adv_x != input_np), 'Fast gradient method: generate value' \ | |||||
' must not be equal to original value.' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_random_fast_gradient_method(): | |||||
""" | |||||
Random fast gradient method unit test. | |||||
""" | |||||
input_np = np.asarray([[0.1, 0.2, 0.7]], np.float32) | |||||
label = np.asarray([2], np.int32) | |||||
label = np.eye(3)[label].astype(np.float32) | |||||
attack = RandomFastGradientMethod(Net()) | |||||
ms_adv_x = attack.generate(input_np, label) | |||||
assert np.any(ms_adv_x != input_np), 'Random fast gradient method: ' \ | |||||
'generate value must not be equal to' \ | |||||
' original value.' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_fast_gradient_sign_method(): | |||||
""" | |||||
Fast gradient sign method unit test. | |||||
""" | |||||
input_np = np.asarray([[0.1, 0.2, 0.7]], np.float32) | |||||
label = np.asarray([2], np.int32) | |||||
label = np.eye(3)[label].astype(np.float32) | |||||
attack = FastGradientSignMethod(Net()) | |||||
ms_adv_x = attack.generate(input_np, label) | |||||
assert np.any(ms_adv_x != input_np), 'Fast gradient sign method: generate' \ | |||||
' value must not be equal to' \ | |||||
' original value.' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_random_fast_gradient_sign_method(): | |||||
""" | |||||
Random fast gradient sign method unit test. | |||||
""" | |||||
input_np = np.random.random((1, 28)).astype(np.float32) | |||||
label = np.asarray([2], np.int32) | |||||
label = np.eye(28)[label].astype(np.float32) | |||||
attack = RandomFastGradientSignMethod(Net()) | |||||
ms_adv_x = attack.generate(input_np, label) | |||||
assert np.any(ms_adv_x != input_np), 'Random fast gradient sign method: ' \ | |||||
'generate value must not be equal to' \ | |||||
' original value.' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_least_likely_class_method(): | |||||
""" | |||||
Least likely class method unit test. | |||||
""" | |||||
input_np = np.asarray([[0.1, 0.2, 0.7]], np.float32) | |||||
label = np.asarray([2], np.int32) | |||||
label = np.eye(3)[label].astype(np.float32) | |||||
attack = LeastLikelyClassMethod(Net()) | |||||
ms_adv_x = attack.generate(input_np, label) | |||||
assert np.any(ms_adv_x != input_np), 'Least likely class method: generate' \ | |||||
' value must not be equal to' \ | |||||
' original value.' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_random_least_likely_class_method(): | |||||
""" | |||||
Random least likely class method unit test. | |||||
""" | |||||
input_np = np.asarray([[0.1, 0.2, 0.7]], np.float32) | |||||
label = np.asarray([2], np.int32) | |||||
label = np.eye(3)[label].astype(np.float32) | |||||
attack = RandomLeastLikelyClassMethod(Net(), eps=0.1, alpha=0.01) | |||||
ms_adv_x = attack.generate(input_np, label) | |||||
assert np.any(ms_adv_x != input_np), 'Random least likely class method: ' \ | |||||
'generate value must not be equal to' \ | |||||
' original value.' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_assert_error(): | |||||
""" | |||||
Random least likely class method unit test. | |||||
""" | |||||
input_np = np.asarray([[0.1, 0.2, 0.7]], np.float32) | |||||
label = np.asarray([2], np.int32) | |||||
label = np.eye(3)[label].astype(np.float32) | |||||
with pytest.raises(ValueError) as e: | |||||
assert RandomLeastLikelyClassMethod(Net(), eps=0.05, alpha=0.21) | |||||
assert str(e.value) == 'eps must be larger than alpha!' |
@@ -0,0 +1,136 @@ | |||||
# 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. | |||||
""" | |||||
Iterative-gradient Attack test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
from mindspore.ops import operations as P | |||||
from mindspore.nn import Cell | |||||
from mindspore import context | |||||
from mindarmour.attacks import BasicIterativeMethod | |||||
from mindarmour.attacks import MomentumIterativeMethod | |||||
from mindarmour.attacks import ProjectedGradientDescent | |||||
from mindarmour.attacks import IterativeGradientMethod | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
# for user | |||||
class Net(Cell): | |||||
""" | |||||
Construct the network of target model. | |||||
Examples: | |||||
>>> net = Net() | |||||
""" | |||||
def __init__(self): | |||||
super(Net, self).__init__() | |||||
self._softmax = P.Softmax() | |||||
def construct(self, inputs): | |||||
""" | |||||
Construct network. | |||||
Args: | |||||
inputs (Tensor): Input data. | |||||
""" | |||||
out = self._softmax(inputs) | |||||
return out | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_basic_iterative_method(): | |||||
""" | |||||
Basic iterative method unit test. | |||||
""" | |||||
input_np = np.asarray([[0.1, 0.2, 0.7]], np.float32) | |||||
label = np.asarray([2], np.int32) | |||||
label = np.eye(3)[label].astype(np.float32) | |||||
for i in range(5): | |||||
net = Net() | |||||
attack = BasicIterativeMethod(net, nb_iter=i + 1) | |||||
ms_adv_x = attack.generate(input_np, label) | |||||
assert np.any( | |||||
ms_adv_x != input_np), 'Basic iterative method: generate value' \ | |||||
' must not be equal to original value.' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_momentum_iterative_method(): | |||||
""" | |||||
Momentum iterative method unit test. | |||||
""" | |||||
input_np = np.asarray([[0.1, 0.2, 0.7]], np.float32) | |||||
label = np.asarray([2], np.int32) | |||||
label = np.eye(3)[label].astype(np.float32) | |||||
for i in range(5): | |||||
attack = MomentumIterativeMethod(Net(), nb_iter=i + 1) | |||||
ms_adv_x = attack.generate(input_np, label) | |||||
assert np.any(ms_adv_x != input_np), 'Basic iterative method: generate' \ | |||||
' value must not be equal to' \ | |||||
' original value.' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_projected_gradient_descent_method(): | |||||
""" | |||||
Projected gradient descent method unit test. | |||||
""" | |||||
input_np = np.asarray([[0.1, 0.2, 0.7]], np.float32) | |||||
label = np.asarray([2], np.int32) | |||||
label = np.eye(3)[label].astype(np.float32) | |||||
for i in range(5): | |||||
attack = ProjectedGradientDescent(Net(), nb_iter=i + 1) | |||||
ms_adv_x = attack.generate(input_np, label) | |||||
assert np.any( | |||||
ms_adv_x != input_np), 'Projected gradient descent method: ' \ | |||||
'generate value must not be equal to' \ | |||||
' original value.' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_error(): | |||||
with pytest.raises(ValueError): | |||||
# check_param_multi_types | |||||
assert IterativeGradientMethod(Net(), bounds=None) | |||||
attack = IterativeGradientMethod(Net(), bounds=(0.0, 1.0)) | |||||
with pytest.raises(NotImplementedError): | |||||
input_np = np.asarray([[0.1, 0.2, 0.7]], np.float32) | |||||
label = np.asarray([2], np.int32) | |||||
label = np.eye(3)[label].astype(np.float32) | |||||
assert attack.generate(input_np, label) |
@@ -0,0 +1,161 @@ | |||||
# 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. | |||||
""" | |||||
JSMA-Attack test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
import mindspore.nn as nn | |||||
from mindspore.nn import Cell | |||||
from mindspore import context | |||||
from mindspore import Tensor | |||||
from mindarmour.attacks.jsma import JSMAAttack | |||||
# for user | |||||
class Net(Cell): | |||||
""" | |||||
Construct the network of target model. | |||||
Examples: | |||||
>>> net = Net() | |||||
""" | |||||
def __init__(self): | |||||
""" | |||||
Introduce the layers used for network construction. | |||||
""" | |||||
super(Net, self).__init__() | |||||
self._relu = nn.ReLU() | |||||
def construct(self, inputs): | |||||
""" | |||||
Construct network. | |||||
Args: | |||||
inputs (Tensor): Input data. | |||||
""" | |||||
out = self._relu(inputs) | |||||
return out | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_jsma_attack(): | |||||
""" | |||||
JSMA-Attack test | |||||
""" | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
net = Net() | |||||
input_shape = (1, 5) | |||||
batch_size, classes = input_shape | |||||
np.random.seed(5) | |||||
input_np = np.random.random(input_shape).astype(np.float32) | |||||
label_np = np.random.randint(classes, size=batch_size) | |||||
ori_label = np.argmax(net(Tensor(input_np)).asnumpy(), axis=1) | |||||
for i in range(batch_size): | |||||
if label_np[i] == ori_label[i]: | |||||
if label_np[i] < classes - 1: | |||||
label_np[i] += 1 | |||||
else: | |||||
label_np[i] -= 1 | |||||
attack = JSMAAttack(net, classes, max_iteration=5) | |||||
adv_data = attack.generate(input_np, label_np) | |||||
assert np.any(input_np != adv_data) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_jsma_attack_2(): | |||||
""" | |||||
JSMA-Attack test | |||||
""" | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
net = Net() | |||||
input_shape = (1, 5) | |||||
batch_size, classes = input_shape | |||||
np.random.seed(5) | |||||
input_np = np.random.random(input_shape).astype(np.float32) | |||||
label_np = np.random.randint(classes, size=batch_size) | |||||
ori_label = np.argmax(net(Tensor(input_np)).asnumpy(), axis=1) | |||||
for i in range(batch_size): | |||||
if label_np[i] == ori_label[i]: | |||||
if label_np[i] < classes - 1: | |||||
label_np[i] += 1 | |||||
else: | |||||
label_np[i] -= 1 | |||||
attack = JSMAAttack(net, classes, max_iteration=5, increase=False) | |||||
adv_data = attack.generate(input_np, label_np) | |||||
assert np.any(input_np != adv_data) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_x86_gpu_inference | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_jsma_attack_gpu(): | |||||
""" | |||||
JSMA-Attack test | |||||
""" | |||||
context.set_context(device_target="GPU") | |||||
net = Net() | |||||
input_shape = (1, 5) | |||||
batch_size, classes = input_shape | |||||
np.random.seed(5) | |||||
input_np = np.random.random(input_shape).astype(np.float32) | |||||
label_np = np.random.randint(classes, size=batch_size) | |||||
ori_label = np.argmax(net(Tensor(input_np)).asnumpy(), axis=1) | |||||
for i in range(batch_size): | |||||
if label_np[i] == ori_label[i]: | |||||
if label_np[i] < classes - 1: | |||||
label_np[i] += 1 | |||||
else: | |||||
label_np[i] -= 1 | |||||
attack = JSMAAttack(net, classes, max_iteration=5) | |||||
adv_data = attack.generate(input_np, label_np) | |||||
assert np.any(input_np != adv_data) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_x86_cpu | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_jsma_attack_cpu(): | |||||
""" | |||||
JSMA-Attack test | |||||
""" | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="CPU") | |||||
net = Net() | |||||
input_shape = (1, 5) | |||||
batch_size, classes = input_shape | |||||
np.random.seed(5) | |||||
input_np = np.random.random(input_shape).astype(np.float32) | |||||
label_np = np.random.randint(classes, size=batch_size) | |||||
ori_label = np.argmax(net(Tensor(input_np)).asnumpy(), axis=1) | |||||
for i in range(batch_size): | |||||
if label_np[i] == ori_label[i]: | |||||
if label_np[i] < classes - 1: | |||||
label_np[i] += 1 | |||||
else: | |||||
label_np[i] -= 1 | |||||
attack = JSMAAttack(net, classes, max_iteration=5) | |||||
adv_data = attack.generate(input_np, label_np) | |||||
assert np.any(input_np != adv_data) |
@@ -0,0 +1,72 @@ | |||||
# 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. | |||||
""" | |||||
LBFGS-Attack test. | |||||
""" | |||||
import sys | |||||
import numpy as np | |||||
import pytest | |||||
import os | |||||
from mindspore import context | |||||
from mindspore.train.serialization import load_checkpoint, load_param_into_net | |||||
from mindarmour.attacks.lbfgs import LBFGS | |||||
from mindarmour.utils.logger import LogUtil | |||||
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), | |||||
"../../../../")) | |||||
from example.mnist_demo.lenet5_net import LeNet5 | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'LBFGS_Test' | |||||
LOGGER.set_level('DEBUG') | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_lbfgs_attack(): | |||||
""" | |||||
LBFGS-Attack test | |||||
""" | |||||
np.random.seed(123) | |||||
# upload trained network | |||||
current_dir = os.path.dirname(os.path.abspath(__file__)) | |||||
ckpt_name = os.path.join(current_dir, | |||||
'../test_data/trained_ckpt_file/checkpoint_lenet-10_1875.ckpt') | |||||
net = LeNet5() | |||||
load_dict = load_checkpoint(ckpt_name) | |||||
load_param_into_net(net, load_dict) | |||||
# get one mnist image | |||||
input_np = np.load(os.path.join(current_dir, | |||||
'../test_data/test_images.npy'))[:1] | |||||
label_np = np.load(os.path.join(current_dir, | |||||
'../test_data/test_labels.npy'))[:1] | |||||
LOGGER.debug(TAG, 'true label is :{}'.format(label_np[0])) | |||||
classes = 10 | |||||
target_np = np.random.randint(0, classes, 1) | |||||
while target_np == label_np[0]: | |||||
target_np = np.random.randint(0, classes) | |||||
target_np = np.eye(10)[target_np].astype(np.float32) | |||||
attack = LBFGS(net, is_targeted=True) | |||||
LOGGER.debug(TAG, 'target_np is :{}'.format(target_np[0])) | |||||
adv_data = attack.generate(input_np, target_np) |
@@ -0,0 +1,107 @@ | |||||
# 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. | |||||
""" | |||||
mocked model for UT of defense algorithms. | |||||
""" | |||||
import numpy as np | |||||
from mindspore import nn | |||||
from mindspore import Tensor | |||||
from mindspore.nn import Cell | |||||
from mindspore.nn import WithLossCell, TrainOneStepCell | |||||
from mindspore.nn.optim.momentum import Momentum | |||||
from mindspore.ops import operations as P | |||||
from mindspore import context | |||||
from mindspore.common.initializer import TruncatedNormal | |||||
from mindarmour.attacks import FastGradientSignMethod | |||||
def conv(in_channels, out_channels, kernel_size, stride=1, padding=0): | |||||
weight = weight_variable() | |||||
return nn.Conv2d(in_channels, out_channels, | |||||
kernel_size=kernel_size, stride=stride, padding=padding, | |||||
weight_init=weight, has_bias=False, pad_mode="valid") | |||||
def fc_with_initialize(input_channels, out_channels): | |||||
weight = weight_variable() | |||||
bias = weight_variable() | |||||
return nn.Dense(input_channels, out_channels, weight, bias) | |||||
def weight_variable(): | |||||
return TruncatedNormal(0.02) | |||||
class Net(nn.Cell): | |||||
""" | |||||
Lenet network | |||||
""" | |||||
def __init__(self): | |||||
super(Net, self).__init__() | |||||
self.conv1 = conv(1, 6, 5) | |||||
self.conv2 = conv(6, 16, 5) | |||||
self.fc1 = fc_with_initialize(16*5*5, 120) | |||||
self.fc2 = fc_with_initialize(120, 84) | |||||
self.fc3 = fc_with_initialize(84, 10) | |||||
self.relu = nn.ReLU() | |||||
self.max_pool2d = nn.MaxPool2d(kernel_size=2, stride=2) | |||||
self.reshape = P.Reshape() | |||||
def construct(self, x): | |||||
x = self.conv1(x) | |||||
x = self.relu(x) | |||||
x = self.max_pool2d(x) | |||||
x = self.conv2(x) | |||||
x = self.relu(x) | |||||
x = self.max_pool2d(x) | |||||
x = self.reshape(x, (-1, 16*5*5)) | |||||
x = self.fc1(x) | |||||
x = self.relu(x) | |||||
x = self.fc2(x) | |||||
x = self.relu(x) | |||||
x = self.fc3(x) | |||||
return x | |||||
if __name__ == '__main__': | |||||
num_classes = 10 | |||||
batch_size = 32 | |||||
sparse = False | |||||
context.set_context(mode=context.GRAPH_MODE) | |||||
context.set_context(device_target='Ascend') | |||||
# create test data | |||||
inputs_np = np.random.rand(batch_size, 1, 32, 32).astype(np.float32) | |||||
labels_np = np.random.randint(num_classes, size=batch_size).astype(np.int32) | |||||
if not sparse: | |||||
labels_np = np.eye(num_classes)[labels_np].astype(np.float32) | |||||
net = Net() | |||||
# test fgsm | |||||
attack = FastGradientSignMethod(net, eps=0.3) | |||||
attack.generate(inputs_np, labels_np) | |||||
# test train ops | |||||
loss_fn = nn.SoftmaxCrossEntropyWithLogits(is_grad=False, sparse=sparse) | |||||
optimizer = Momentum(filter(lambda x: x.requires_grad, net.get_parameters()), | |||||
0.01, 0.9) | |||||
loss_net = WithLossCell(net, loss_fn) | |||||
train_net = TrainOneStepCell(loss_net, optimizer) | |||||
train_net.set_train() | |||||
train_net(Tensor(inputs_np), Tensor(labels_np)) | |||||
@@ -0,0 +1,66 @@ | |||||
# 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. | |||||
""" | |||||
Adversarial defense test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
import logging | |||||
from mindspore import nn | |||||
from mindspore import Tensor | |||||
from mindspore import context | |||||
from mindspore.nn.optim.momentum import Momentum | |||||
from mindarmour.defenses.adversarial_defense import AdversarialDefense | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mock_net import Net | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'Ad_Test' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_ad(): | |||||
"""UT for adversarial defense.""" | |||||
num_classes = 10 | |||||
batch_size = 16 | |||||
sparse = False | |||||
context.set_context(mode=context.GRAPH_MODE) | |||||
context.set_context(device_target='Ascend') | |||||
# create test data | |||||
inputs = np.random.rand(batch_size, 1, 32, 32).astype(np.float32) | |||||
labels = np.random.randint(num_classes, size=batch_size).astype(np.int32) | |||||
if not sparse: | |||||
labels = np.eye(num_classes)[labels].astype(np.float32) | |||||
net = Net() | |||||
loss_fn = nn.SoftmaxCrossEntropyWithLogits(is_grad=False, sparse=sparse) | |||||
optimizer = Momentum(learning_rate=Tensor(np.array([0.001], np.float32)), | |||||
momentum=Tensor(np.array([0.9], np.float32)), | |||||
params=net.trainable_params()) | |||||
ad_defense = AdversarialDefense(net, loss_fn=loss_fn, optimizer=optimizer) | |||||
LOGGER.set_level(logging.DEBUG) | |||||
LOGGER.debug(TAG, '--start adversarial defense--') | |||||
loss = ad_defense.defense(inputs, labels) | |||||
LOGGER.debug(TAG, '--end adversarial defense--') | |||||
assert np.any(loss >= 0.0) |
@@ -0,0 +1,70 @@ | |||||
# 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. | |||||
""" | |||||
ensemble adversarial defense test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
import logging | |||||
from mindspore import nn | |||||
from mindspore import context | |||||
from mindspore.nn.optim.momentum import Momentum | |||||
from mindarmour.attacks.gradient_method import FastGradientSignMethod | |||||
from mindarmour.attacks.iterative_gradient_method import \ | |||||
ProjectedGradientDescent | |||||
from mindarmour.defenses.adversarial_defense import EnsembleAdversarialDefense | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mock_net import Net | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'Ead_Test' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_ead(): | |||||
"""UT for ensemble adversarial defense.""" | |||||
num_classes = 10 | |||||
batch_size = 16 | |||||
sparse = False | |||||
context.set_context(mode=context.GRAPH_MODE) | |||||
context.set_context(device_target='Ascend') | |||||
# create test data | |||||
inputs = np.random.rand(batch_size, 1, 32, 32).astype(np.float32) | |||||
labels = np.random.randint(num_classes, size=batch_size).astype(np.int32) | |||||
if not sparse: | |||||
labels = np.eye(num_classes)[labels].astype(np.float32) | |||||
net = Net() | |||||
loss_fn = nn.SoftmaxCrossEntropyWithLogits(is_grad=False, sparse=sparse) | |||||
optimizer = Momentum(net.trainable_params(), 0.001, 0.9) | |||||
net = Net() | |||||
fgsm = FastGradientSignMethod(net) | |||||
pgd = ProjectedGradientDescent(net) | |||||
ead = EnsembleAdversarialDefense(net, [fgsm, pgd], loss_fn=loss_fn, | |||||
optimizer=optimizer) | |||||
LOGGER.set_level(logging.DEBUG) | |||||
LOGGER.debug(TAG, '---start ensemble adversarial defense--') | |||||
loss = ead.defense(inputs, labels) | |||||
LOGGER.debug(TAG, '---end ensemble adversarial defense--') | |||||
assert np.any(loss >= 0.0) |
@@ -0,0 +1,65 @@ | |||||
# 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. | |||||
""" | |||||
Natural adversarial defense test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
import logging | |||||
from mindspore import nn | |||||
from mindspore import context | |||||
from mindspore.nn.optim.momentum import Momentum | |||||
from mindarmour.defenses.natural_adversarial_defense import \ | |||||
NaturalAdversarialDefense | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mock_net import Net | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'Nad_Test' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_nad(): | |||||
"""UT for natural adversarial defense.""" | |||||
num_classes = 10 | |||||
batch_size = 16 | |||||
sparse = False | |||||
context.set_context(mode=context.GRAPH_MODE) | |||||
context.set_context(device_target='Ascend') | |||||
# create test data | |||||
inputs = np.random.rand(batch_size, 1, 32, 32).astype(np.float32) | |||||
labels = np.random.randint(num_classes, size=batch_size).astype(np.int32) | |||||
if not sparse: | |||||
labels = np.eye(num_classes)[labels].astype(np.float32) | |||||
net = Net() | |||||
loss_fn = nn.SoftmaxCrossEntropyWithLogits(is_grad=False, sparse=sparse) | |||||
optimizer = Momentum(net.trainable_params(), 0.001, 0.9) | |||||
# defense | |||||
nad = NaturalAdversarialDefense(net, loss_fn=loss_fn, optimizer=optimizer) | |||||
LOGGER.set_level(logging.DEBUG) | |||||
LOGGER.debug(TAG, '---start natural adversarial defense--') | |||||
loss = nad.defense(inputs, labels) | |||||
LOGGER.debug(TAG, '---end natural adversarial defense--') | |||||
assert np.any(loss >= 0.0) |
@@ -0,0 +1,66 @@ | |||||
# 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. | |||||
""" | |||||
Projected adversarial defense test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
import logging | |||||
from mindspore import nn | |||||
from mindspore import context | |||||
from mindspore.nn.optim.momentum import Momentum | |||||
from mindarmour.defenses.projected_adversarial_defense import \ | |||||
ProjectedAdversarialDefense | |||||
from mindarmour.utils.logger import LogUtil | |||||
from mock_net import Net | |||||
LOGGER = LogUtil.get_instance() | |||||
TAG = 'Pad_Test' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_pad(): | |||||
"""UT for projected adversarial defense.""" | |||||
num_classes = 10 | |||||
batch_size = 16 | |||||
sparse = False | |||||
context.set_context(mode=context.GRAPH_MODE) | |||||
context.set_context(device_target='Ascend') | |||||
# create test data | |||||
inputs = np.random.rand(batch_size, 1, 32, 32).astype(np.float32) | |||||
labels = np.random.randint(num_classes, size=batch_size).astype(np.int32) | |||||
if not sparse: | |||||
labels = np.eye(num_classes)[labels].astype(np.float32) | |||||
# construct network | |||||
net = Net() | |||||
loss_fn = nn.SoftmaxCrossEntropyWithLogits(is_grad=False, sparse=sparse) | |||||
optimizer = Momentum(net.trainable_params(), 0.001, 0.9) | |||||
# defense | |||||
pad = ProjectedAdversarialDefense(net, loss_fn=loss_fn, optimizer=optimizer) | |||||
LOGGER.set_level(logging.DEBUG) | |||||
LOGGER.debug(TAG, '---start projected adversarial defense--') | |||||
loss = pad.defense(inputs, labels) | |||||
LOGGER.debug(TAG, '---end projected adversarial defense--') | |||||
assert np.any(loss >= 0.0) |
@@ -0,0 +1,101 @@ | |||||
# 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. | |||||
""" | |||||
Similarity-detector test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
from mindspore.nn import Cell | |||||
from mindspore import Model | |||||
from mindspore import context | |||||
from mindspore.ops.operations import TensorAdd | |||||
from mindarmour.detectors.black.similarity_detector import SimilarityDetector | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
class EncoderNet(Cell): | |||||
""" | |||||
Similarity encoder for input data | |||||
""" | |||||
def __init__(self, encode_dim): | |||||
super(EncoderNet, self).__init__() | |||||
self._encode_dim = encode_dim | |||||
self.add = TensorAdd() | |||||
def construct(self, inputs): | |||||
""" | |||||
construct the neural network | |||||
Args: | |||||
inputs (Tensor): input data to neural network. | |||||
Returns: | |||||
Tensor, output of neural network. | |||||
""" | |||||
return self.add(inputs, inputs) | |||||
def get_encode_dim(self): | |||||
""" | |||||
Get the dimension of encoded inputs | |||||
Returns: | |||||
int, dimension of encoded inputs. | |||||
""" | |||||
return self._encode_dim | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_similarity_detector(): | |||||
""" | |||||
Similarity detector unit test | |||||
""" | |||||
# Prepare dataset | |||||
np.random.seed(5) | |||||
x_train = np.random.rand(1000, 32, 32, 3).astype(np.float32) | |||||
perm = np.random.permutation(x_train.shape[0]) | |||||
# Generate query sequences | |||||
benign_queries = x_train[perm[:1000], :, :, :] | |||||
suspicious_queries = x_train[perm[-1], :, :, :] + np.random.normal( | |||||
0, 0.05, (1000,) + x_train.shape[1:]) | |||||
suspicious_queries = suspicious_queries.astype(np.float32) | |||||
# explicit threshold not provided, calculate threshold for K | |||||
encoder = Model(EncoderNet(encode_dim=256)) | |||||
detector = SimilarityDetector(max_k_neighbor=50, 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() | |||||
# compare | |||||
expected_value = 0 | |||||
assert len(detections) == expected_value | |||||
detector.clear_buffer() | |||||
detector.detect(suspicious_queries) | |||||
# compare | |||||
expected_value = [1051, 1102, 1153, 1204, 1255, | |||||
1306, 1357, 1408, 1459, 1510, | |||||
1561, 1612, 1663, 1714, 1765, | |||||
1816, 1867, 1918, 1969] | |||||
assert np.all(detector.get_detected_queries() == expected_value) | |||||
@@ -0,0 +1,112 @@ | |||||
# 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. | |||||
""" | |||||
EnsembleDetector Test | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
from mindspore.nn import Cell | |||||
from mindspore.ops.operations import TensorAdd | |||||
from mindspore.train.model import Model | |||||
from mindspore import context | |||||
from mindarmour.detectors.mag_net import ErrorBasedDetector | |||||
from mindarmour.detectors.region_based_detector import RegionBasedDetector | |||||
from mindarmour.detectors.ensemble_detector import EnsembleDetector | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
class Net(Cell): | |||||
""" | |||||
Construct the network of target model. | |||||
""" | |||||
def __init__(self): | |||||
super(Net, self).__init__() | |||||
self.add = TensorAdd() | |||||
def construct(self, inputs): | |||||
""" | |||||
Construct network. | |||||
Args: | |||||
inputs (Tensor): Input data. | |||||
""" | |||||
return self.add(inputs, inputs) | |||||
class AutoNet(Cell): | |||||
""" | |||||
Construct the network of target model. | |||||
""" | |||||
def __init__(self): | |||||
super(AutoNet, self).__init__() | |||||
self.add = TensorAdd() | |||||
def construct(self, inputs): | |||||
""" | |||||
Construct network. | |||||
Args: | |||||
inputs (Tensor): Input data. | |||||
""" | |||||
return self.add(inputs, inputs) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_ensemble_detector(): | |||||
""" | |||||
Compute mindspore result. | |||||
""" | |||||
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) | |||||
# use this to enable radius in region_detector | |||||
region_detector.fit(adv, labels) | |||||
detectors = [magnet_detector, region_detector] | |||||
detector = EnsembleDetector(detectors) | |||||
detected_res = detector.detect(adv) | |||||
expected_value = np.array([0, 1, 0, 0]) | |||||
assert np.all(detected_res == expected_value) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_error(): | |||||
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) | |||||
# use this to enable radius in region_detector | |||||
detectors = [magnet_detector, region_detector] | |||||
detector = EnsembleDetector(detectors) | |||||
with pytest.raises(NotImplementedError): | |||||
assert detector.fit(adv, labels) |
@@ -0,0 +1,164 @@ | |||||
# 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. | |||||
""" | |||||
Mag-net detector test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
import mindspore.ops.operations as P | |||||
from mindspore.nn import Cell | |||||
from mindspore.ops.operations import TensorAdd | |||||
from mindspore import Model | |||||
from mindspore import context | |||||
from mindarmour.detectors.mag_net import ErrorBasedDetector | |||||
from mindarmour.detectors.mag_net import DivergenceBasedDetector | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
class Net(Cell): | |||||
""" | |||||
Construct the network of target model. | |||||
""" | |||||
def __init__(self): | |||||
super(Net, self).__init__() | |||||
self.add = TensorAdd() | |||||
def construct(self, inputs): | |||||
""" | |||||
Construct network. | |||||
Args: | |||||
inputs (Tensor): Input data. | |||||
""" | |||||
return self.add(inputs, inputs) | |||||
class PredNet(Cell): | |||||
""" | |||||
Construct the network of target model. | |||||
""" | |||||
def __init__(self): | |||||
super(PredNet, self).__init__() | |||||
self.shape = P.Shape() | |||||
self.reshape = P.Reshape() | |||||
self._softmax = P.Softmax() | |||||
def construct(self, inputs): | |||||
""" | |||||
Construct network. | |||||
Args: | |||||
inputs (Tensor): Input data. | |||||
""" | |||||
data = self.reshape(inputs, (self.shape(inputs)[0], -1)) | |||||
return self._softmax(data) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_mag_net(): | |||||
""" | |||||
Compute mindspore result. | |||||
""" | |||||
np.random.seed(5) | |||||
ori = np.random.rand(4, 4, 4).astype(np.float32) | |||||
np.random.seed(6) | |||||
adv = np.random.rand(4, 4, 4).astype(np.float32) | |||||
model = Model(Net()) | |||||
detector = ErrorBasedDetector(model) | |||||
detector.fit(ori) | |||||
detected_res = detector.detect(adv) | |||||
expected_value = np.array([1, 1, 1, 1]) | |||||
assert np.all(detected_res == expected_value) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_mag_net_transform(): | |||||
""" | |||||
Compute mindspore result. | |||||
""" | |||||
np.random.seed(6) | |||||
adv = np.random.rand(4, 4, 4).astype(np.float32) | |||||
model = Model(Net()) | |||||
detector = ErrorBasedDetector(model) | |||||
adv_trans = detector.transform(adv) | |||||
assert np.any(adv_trans != adv) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_mag_net_divergence(): | |||||
""" | |||||
Compute mindspore result. | |||||
""" | |||||
np.random.seed(5) | |||||
ori = np.random.rand(4, 4, 4).astype(np.float32) | |||||
np.random.seed(6) | |||||
adv = np.random.rand(4, 4, 4).astype(np.float32) | |||||
encoder = Model(Net()) | |||||
model = Model(PredNet()) | |||||
detector = DivergenceBasedDetector(encoder, model) | |||||
threshold = detector.fit(ori) | |||||
detector.set_threshold(threshold) | |||||
detected_res = detector.detect(adv) | |||||
expected_value = np.array([1, 0, 1, 1]) | |||||
assert np.all(detected_res == expected_value) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_mag_net_divergence_transform(): | |||||
""" | |||||
Compute mindspore result. | |||||
""" | |||||
np.random.seed(6) | |||||
adv = np.random.rand(4, 4, 4).astype(np.float32) | |||||
encoder = Model(Net()) | |||||
model = Model(PredNet()) | |||||
detector = DivergenceBasedDetector(encoder, model) | |||||
adv_trans = detector.transform(adv) | |||||
assert np.any(adv_trans != adv) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_value_error(): | |||||
np.random.seed(6) | |||||
adv = np.random.rand(4, 4, 4).astype(np.float32) | |||||
encoder = Model(Net()) | |||||
model = Model(PredNet()) | |||||
detector = DivergenceBasedDetector(encoder, model, option='bad_op') | |||||
with pytest.raises(NotImplementedError): | |||||
assert detector.detect_diff(adv) |
@@ -0,0 +1,115 @@ | |||||
# 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. | |||||
""" | |||||
Region-based detector test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
from mindspore.nn import Cell | |||||
from mindspore import Model | |||||
from mindspore import context | |||||
from mindspore.ops.operations import TensorAdd | |||||
from mindarmour.detectors.region_based_detector import \ | |||||
RegionBasedDetector | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
class Net(Cell): | |||||
""" | |||||
Construct the network of target model. | |||||
""" | |||||
def __init__(self): | |||||
super(Net, self).__init__() | |||||
self.add = TensorAdd() | |||||
def construct(self, inputs): | |||||
""" | |||||
Construct network. | |||||
Args: | |||||
inputs (Tensor): Input data. | |||||
""" | |||||
return self.add(inputs, inputs) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_region_based_classification(): | |||||
""" | |||||
Compute mindspore result. | |||||
""" | |||||
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) | |||||
radius = detector.fit(ori, labels) | |||||
detector.set_radius(radius) | |||||
detected_res = detector.detect(adv) | |||||
expected_value = np.array([0, 0, 1, 0]) | |||||
assert np.all(detected_res == expected_value) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_value_error(): | |||||
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()) | |||||
# model should be mindspore model | |||||
with pytest.raises(ValueError): | |||||
assert RegionBasedDetector(Net()) | |||||
with pytest.raises(ValueError): | |||||
assert RegionBasedDetector(model, number_points=-1) | |||||
with pytest.raises(ValueError): | |||||
assert RegionBasedDetector(model, initial_radius=-1) | |||||
with pytest.raises(ValueError): | |||||
assert RegionBasedDetector(model, max_radius=-2.2) | |||||
with pytest.raises(ValueError): | |||||
assert RegionBasedDetector(model, search_step=0) | |||||
with pytest.raises(ValueError): | |||||
assert RegionBasedDetector(model, sparse='False') | |||||
detector = RegionBasedDetector(model) | |||||
with pytest.raises(ValueError): | |||||
# radius must not empty | |||||
assert detector.detect(adv) | |||||
radius = detector.fit(ori, labels) | |||||
detector.set_radius(radius) | |||||
with pytest.raises(ValueError): | |||||
# adv type should be in (list, tuple, numpy.ndarray) | |||||
assert detector.detect(adv.tostring()) |
@@ -0,0 +1,116 @@ | |||||
# 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 test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
import mindspore.ops.operations as M | |||||
from mindspore import Model | |||||
from mindspore.nn import Cell | |||||
from mindspore import context | |||||
from mindarmour.detectors.spatial_smoothing import SpatialSmoothing | |||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") | |||||
# for use | |||||
class Net(Cell): | |||||
""" | |||||
Construct the network of target model. | |||||
""" | |||||
def __init__(self): | |||||
super(Net, self).__init__() | |||||
self._softmax = M.Softmax() | |||||
def construct(self, inputs): | |||||
""" | |||||
Construct network. | |||||
Args: | |||||
inputs (Tensor): Input data. | |||||
""" | |||||
return self._softmax(inputs) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_spatial_smoothing(): | |||||
""" | |||||
Compute mindspore result. | |||||
""" | |||||
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) | |||||
# mock user model | |||||
model = Model(Net()) | |||||
detector = SpatialSmoothing(model) | |||||
# Training | |||||
threshold = detector.fit(input_np) | |||||
detector.set_threshold(threshold.item()) | |||||
detected_res = np.array(detector.detect(adv_np)) | |||||
idx = np.where(detected_res > 0) | |||||
expected_value = np.array([10, 39, 48]) | |||||
assert np.all(idx == expected_value) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_spatial_smoothing_diff(): | |||||
""" | |||||
Compute mindspore result. | |||||
""" | |||||
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) | |||||
# mock user model | |||||
model = Model(Net()) | |||||
detector = SpatialSmoothing(model) | |||||
# Training | |||||
detector.fit(input_np) | |||||
diffs = detector.detect_diff(adv_np) | |||||
expected_value = np.array([0.20959496, 0.69537306, 0.13034256, 0.7421039, | |||||
0.41419053, 0.56346416, 0.4277994, 0.3240941, | |||||
0.048190027, 0.6806958, 1.1405756, 0.587804, | |||||
0.40533313, 0.2875523, 0.36801508, 0.61993587, | |||||
0.49286827, 0.13222921, 0.68012404, 0.4164942, | |||||
0.25758877, 0.6008735, 0.60623455, 0.34981924, | |||||
0.3945489, 0.879787, 0.3934811, 0.23387678, | |||||
0.63480926, 0.56435543, 0.16067612, 0.57489645, | |||||
0.21772699, 0.55924356, 0.5186635, 0.7094835, | |||||
0.0613693, 0.13305652, 0.11505881, 1.2404268, | |||||
0.50948, 0.15797901, 0.44473758, 0.2495422, | |||||
0.38254014, 0.543059, 0.06452079, 0.36902517, | |||||
1.1845329, 0.3870097]) | |||||
assert np.allclose(diffs, expected_value, 0.0001, 0.0001) | |||||
@@ -0,0 +1,73 @@ | |||||
# 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. | |||||
""" | |||||
Black-box defense evaluation test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
from mindarmour.evaluations.black.defense_evaluation import BlackDefenseEvaluate | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_def_eval(): | |||||
""" | |||||
Tests for black-box defense evaluation | |||||
""" | |||||
# prepare data | |||||
raw_preds = np.array([[0.1, 0.1, 0.2, 0.6], [0.1, 0.7, 0.0, 0.2], | |||||
[0.8, 0.1, 0.0, 0.1], [0.1, 0.1, 0.2, 0.6], | |||||
[0.1, 0.7, 0.0, 0.2], [0.8, 0.1, 0.0, 0.1], | |||||
[0.1, 0.1, 0.2, 0.6], [0.1, 0.7, 0.0, 0.2], | |||||
[0.8, 0.1, 0.0, 0.1], [0.1, 0.1, 0.2, 0.6]]) | |||||
def_preds = np.array([[0.1, 0.1, 0.2, 0.6], [0.1, 0.7, 0.0, 0.2], | |||||
[0.8, 0.1, 0.0, 0.1], [0.1, 0.1, 0.2, 0.6], | |||||
[0.1, 0.7, 0.0, 0.2], [0.8, 0.1, 0.0, 0.1], | |||||
[0.1, 0.1, 0.2, 0.6], [0.1, 0.7, 0.0, 0.2], | |||||
[0.8, 0.1, 0.0, 0.1], [0.1, 0.1, 0.2, 0.6]]) | |||||
raw_query_counts = np.array([0, 0, 0, 0, 0, 10, 10, 20, 20, 30]) | |||||
def_query_counts = np.array([0, 0, 0, 0, 0, 30, 30, 40, 40, 50]) | |||||
raw_query_time = np.array([0.1, 0.1, 0.1, 0.1, 0.1, 2, 2, 4, 4, 6]) | |||||
def_query_time = np.array([0.3, 0.3, 0.3, 0.3, 0.3, 4, 4, 8, 8, 12]) | |||||
def_detection_counts = np.array([1, 0, 0, 0, 1, 5, 5, 5, 10, 20]) | |||||
true_labels = np.array([3, 1, 0, 3, 1, 0, 3, 1, 0, 3]) | |||||
# create obj | |||||
def_eval = BlackDefenseEvaluate(raw_preds, | |||||
def_preds, | |||||
raw_query_counts, | |||||
def_query_counts, | |||||
raw_query_time, | |||||
def_query_time, | |||||
def_detection_counts, | |||||
true_labels, | |||||
max_queries=100) | |||||
# run eval | |||||
qcv = def_eval.qcv() | |||||
asv = def_eval.asv() | |||||
fpr = def_eval.fpr() | |||||
qrv = def_eval.qrv() | |||||
res = [qcv, asv, fpr, qrv] | |||||
# compare | |||||
expected_value = [0.2, 0.0, 0.4, 2.0] | |||||
assert np.allclose(res, expected_value, 0.0001, 0.0001) |
@@ -0,0 +1,95 @@ | |||||
# 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. | |||||
""" | |||||
Attack evaluation test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
from mindarmour.evaluations.attack_evaluation import AttackEvaluate | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_attack_eval(): | |||||
# prepare test data | |||||
np.random.seed(1024) | |||||
inputs = np.random.normal(size=(3, 512, 512, 3)) | |||||
labels = np.array([[0.1, 0.1, 0.2, 0.6], | |||||
[0.1, 0.7, 0.0, 0.2], | |||||
[0.8, 0.1, 0.0, 0.1]]) | |||||
adv_x = inputs + np.ones((3, 512, 512, 3))*0.001 | |||||
adv_y = np.array([[0.1, 0.1, 0.2, 0.6], | |||||
[0.1, 0.0, 0.8, 0.1], | |||||
[0.0, 0.9, 0.1, 0.0]]) | |||||
# create obj | |||||
attack_eval = AttackEvaluate(inputs, labels, adv_x, adv_y) | |||||
# run eval | |||||
mr = attack_eval.mis_classification_rate() | |||||
acac = attack_eval.avg_conf_adv_class() | |||||
l_0, l_2, l_inf = attack_eval.avg_lp_distance() | |||||
ass = attack_eval.avg_ssim() | |||||
nte = attack_eval.nte() | |||||
res = [mr, acac, l_0, l_2, l_inf, ass, nte] | |||||
# compare | |||||
expected_value = [0.6666, 0.8500, 1.0, 0.0009, 0.0001, 0.9999, 0.75] | |||||
assert np.allclose(res, expected_value, 0.0001, 0.0001) | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_value_error(): | |||||
# prepare test data | |||||
np.random.seed(1024) | |||||
inputs = np.random.normal(size=(3, 512, 512, 3)) | |||||
labels = np.array([[0.1, 0.1, 0.2, 0.6], | |||||
[0.1, 0.7, 0.0, 0.2], | |||||
[0.8, 0.1, 0.0, 0.1]]) | |||||
adv_x = inputs + np.ones((3, 512, 512, 3))*0.001 | |||||
adv_y = np.array([[0.1, 0.1, 0.2, 0.6], | |||||
[0.1, 0.0, 0.8, 0.1], | |||||
[0.0, 0.9, 0.1, 0.0]]) | |||||
# create obj | |||||
with pytest.raises(ValueError) as e: | |||||
assert AttackEvaluate(inputs, labels, adv_x, adv_y, targeted=True) | |||||
assert str(e.value) == 'targeted attack need target_label, but got None.' | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_value_error(): | |||||
# prepare test data | |||||
np.random.seed(1024) | |||||
inputs = np.array([]) | |||||
labels = np.array([]) | |||||
adv_x = inputs | |||||
adv_y = np.array([]) | |||||
# create obj | |||||
with pytest.raises(ValueError) as e: | |||||
assert AttackEvaluate(inputs, labels, adv_x, adv_y) | |||||
assert str(e.value) == 'inputs must not be empty' |
@@ -0,0 +1,51 @@ | |||||
# 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. | |||||
""" | |||||
Defense evaluation test. | |||||
""" | |||||
import numpy as np | |||||
import pytest | |||||
from mindarmour.evaluations.defense_evaluation import DefenseEvaluate | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_def_eval(): | |||||
# prepare data | |||||
raw_preds = np.array([[0.1, 0.1, 0.2, 0.6], | |||||
[0.1, 0.7, 0.0, 0.2], | |||||
[0.8, 0.1, 0.0, 0.1]]) | |||||
def_preds = np.array([[0.1, 0.1, 0.1, 0.7], | |||||
[0.1, 0.6, 0.2, 0.1], | |||||
[0.1, 0.2, 0.1, 0.6]]) | |||||
true_labels = np.array([3, 1, 0]) | |||||
# create obj | |||||
def_eval = DefenseEvaluate(raw_preds, def_preds, true_labels) | |||||
# run eval | |||||
cav = def_eval.cav() | |||||
crr = def_eval.crr() | |||||
csr = def_eval.csr() | |||||
ccv = def_eval.ccv() | |||||
cos = def_eval.cos() | |||||
res = [cav, crr, csr, ccv, cos] | |||||
# compare | |||||
expected_value = [-0.3333, 0.0, 0.3333, 0.0999, 0.0450] | |||||
assert np.allclose(res, expected_value, 0.0001, 0.0001) |
@@ -0,0 +1,57 @@ | |||||
# 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. | |||||
""" | |||||
Radar map test. | |||||
""" | |||||
import pytest | |||||
from mindarmour.evaluations.visual_metrics import RadarMetric | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_radar_metric(): | |||||
# prepare data | |||||
metrics_name = ['MR', 'ACAC', 'ASS', 'NTE', 'RGB'] | |||||
def_metrics = [0.9, 0.85, 0.6, 0.7, 0.8] | |||||
raw_metrics = [0.5, 0.3, 0.55, 0.65, 0.7] | |||||
metrics_data = [def_metrics, raw_metrics] | |||||
metrics_labels = ['before', 'after'] | |||||
# create obj | |||||
rm = RadarMetric(metrics_name, metrics_data, metrics_labels, title='', | |||||
scale='sparse') | |||||
@pytest.mark.level0 | |||||
@pytest.mark.platform_arm_ascend_training | |||||
@pytest.mark.platform_x86_ascend_training | |||||
@pytest.mark.env_card | |||||
@pytest.mark.component_mindarmour | |||||
def test_value_error(): | |||||
# prepare data | |||||
metrics_name = ['MR', 'ACAC', 'ASS', 'NTE', 'RGB'] | |||||
def_metrics = [0.9, 0.85, 0.6, 0.7, 0.8] | |||||
raw_metrics = [0.5, 0.3, 0.55, 0.65, 0.7] | |||||
metrics_data = [def_metrics, raw_metrics] | |||||
metrics_labels = ['before', 'after'] | |||||
with pytest.raises(ValueError): | |||||
assert RadarMetric(metrics_name, metrics_data, metrics_labels, | |||||
title='', scale='bad_s') | |||||
with pytest.raises(ValueError): | |||||
assert RadarMetric(['MR', 'ACAC', 'ASS'], metrics_data, metrics_labels, | |||||
title='', scale='bad_s') | |||||