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