| @@ -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') | |||