{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "id": "XGCdmDAKpLuf" }, "outputs": [], "source": [ "##### Copyright 2019 The TensorFlow Authors.\n", "\n", "Licensed under the Apache License, Version 2.0 (the \"License\");" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "id": "GF4d1XplpLGF", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", "# you may not use this file except in compliance with the License.\n", "# You may obtain a copy of the License at\n", "#\n", "# https://www.apache.org/licenses/LICENSE-2.0\n", "#\n", "# Unless required by applicable law or agreed to in writing, software\n", "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", "# See the License for the specific language governing permissions and\n", "# limitations under the License." ] }, { "cell_type": "markdown", "metadata": { "id": "W1L3zJP6pPGD" }, "source": [ "# 使用 FGSM 的对抗样本\n", "\n", "\n", " \n", " \n", " \n", " \n", "
在 TensorFlow.org 上查看在 Google Colab 中运行 在 GitHub 上查看源代码下载笔记本
" ] }, { "cell_type": "markdown", "metadata": { "id": "8dn1-g8BpPDx" }, "source": [ "本教程使用快速梯度符号法 (FGSM) 攻击(请参阅由 Goodfellow *等人*合著的 [Explaining and Harnessing Adversarial Example](https://arxiv.org/abs/1412.6572))创建*对抗样本* 。这是第一种也是最受欢迎的能够欺骗神经网络的攻击算法之一。\n", "\n", "## 什么是对抗样本?\n", "\n", "对抗样本是以扰乱神经网络为目的而创建的专门输入,可导致给定输入被错误分类。这些臭名昭著的输入是肉眼无法区分的,但会导致网络无法识别图像的内容。这类攻击有多种类型,但这里重点讨论快速梯度符号法攻击,这是一种*白盒*攻击,其目的是确保错误分类。白盒攻击的特点是攻击者可以完全访问被攻击模型。下方显示的是对抗图像最著名的示例之一,该图取自上述论文。\n", "\n", "![Adversarial Example](images/adversarial_example.png)\n", "\n", "在这里,基于熊猫图像,攻击者向原始图像添加了较小的扰动(失真),这导致模型以较高的置信度将该图像标记为长臂猿。下文说明了添加这些扰动的过程。\n", "\n", "## 快速梯度符号法\n", "\n", "快速梯度符号法通过利用神经网络的梯度来创建对抗样本。对于输入图像,该方法使用相对于输入图像的损失梯度来创建损失最大化的新图像。此新图像被称为对抗图像。可以使用以下表达式予以总结:$$adv_x = x + \\epsilon*\\text{sign}(\\nabla_xJ(\\theta, x, y))$$\n", "\n", "其中\n", "\n", "- adv_x:对抗图像。\n", "- x:原始输入图像。\n", "- y:原始输入标签。\n", "- $\\epsilon$:确保扰动很小的乘数。\n", "- $\\theta$:模型参数。\n", "- $J$:损失。\n", "\n", "这里有一个有趣的属性,梯度是相对于输入图像计算的。因为其目标是创建一个损失最大化的图像。一种实现此目的的方法是找出图像中每个像素对损失值的贡献程度,然后相应地添加一个扰动。这非常有效,因为通过使用链式法则并确定所需梯度,即可轻松地确定每个输入像素对损失的贡献。因此,梯度是相对于图像计算的。另外,由于不再对模型进行训练(因此,梯度计算值与可训练变量(即模型参数)无关),模型参数将保持恒定。唯一的目标是欺骗已经受过训练的模型。\n", "\n", "那么,让我们来尝试欺骗一个经过预训练的模型。在本教程中,该模型为 [MobileNetV2](https://tensorflow.google.cn/versions/r2.0/api_docs/python/tf/keras/applications/MobileNetV2) 模型,在 [ImageNet](http://www.image-net.org/) 上预训练。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "vag2WYR6yTOC", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "import tensorflow as tf\n", "import matplotlib as mpl\n", "import matplotlib.pyplot as plt\n", "\n", "mpl.rcParams['figure.figsize'] = (8, 8)\n", "mpl.rcParams['axes.grid'] = False" ] }, { "cell_type": "markdown", "metadata": { "id": "wiTHY8dqxzx7" }, "source": [ "让我们加载预训练的 MobileNetV2 模型和 ImageNet 类名称。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "nqhk2vYx6Ag0", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "pretrained_model = tf.keras.applications.MobileNetV2(include_top=True,\n", " weights='imagenet')\n", "pretrained_model.trainable = False\n", "\n", "# ImageNet labels\n", "decode_predictions = tf.keras.applications.mobilenet_v2.decode_predictions" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "f2cLrJH0zpfC", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "# Helper function to preprocess the image so that it can be inputted in MobileNetV2\n", "def preprocess(image):\n", " image = tf.cast(image, tf.float32)\n", " image = tf.image.resize(image, (224, 224))\n", " image = tf.keras.applications.mobilenet_v2.preprocess_input(image)\n", " image = image[None, ...]\n", " return image\n", "\n", "# Helper function to extract labels from probability vector\n", "def get_imagenet_label(probs):\n", " return decode_predictions(probs, top=1)[0][0]" ] }, { "cell_type": "markdown", "metadata": { "id": "iEZaMVFgSUA-" }, "source": [ "## 原始图像\n", "\n", "让我们使用这张[拉布拉多寻回犬](https://commons.wikimedia.org/wiki/File:YellowLabradorLooking_new.jpg)(拍摄者:Mirko,[CC-BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/),Wikimedia Common),借此创建对抗样本。第一步是对其进行预处理,以便可以将其作为输入馈送至 MobileNetV2 模型。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "wpYrQ4OQSYWk", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "image_path = tf.keras.utils.get_file('YellowLabradorLooking_new.jpg', 'https://storage.googleapis.com/download.tensorflow.org/example_images/YellowLabradorLooking_new.jpg')\n", "image_raw = tf.io.read_file(image_path)\n", "image = tf.image.decode_image(image_raw)\n", "\n", "image = preprocess(image)\n", "image_probs = pretrained_model.predict(image)" ] }, { "cell_type": "markdown", "metadata": { "id": "mvPlta_uSbuI" }, "source": [ "让我们看一下图像。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "99Jc-SNoSZot", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "plt.figure()\n", "plt.imshow(image[0] * 0.5 + 0.5) # To change [-1, 1] to [0,1]\n", "_, image_class, class_confidence = get_imagenet_label(image_probs)\n", "plt.title('{} : {:.2f}% Confidence'.format(image_class, class_confidence*100))\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": { "id": "kElVTbF690CF" }, "source": [ "## 创建对抗图像\n", "\n", "### 实现快速梯度符号法\n", "\n", "第一步是创建扰动,将用于使原始图像失真从而产生对抗图像。如上文所述,需要相对于图像来计算梯度。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "FhZxlOnuBCVr", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "loss_object = tf.keras.losses.CategoricalCrossentropy()\n", "\n", "def create_adversarial_pattern(input_image, input_label):\n", " with tf.GradientTape() as tape:\n", " tape.watch(input_image)\n", " prediction = pretrained_model(input_image)\n", " loss = loss_object(input_label, prediction)\n", "\n", " # Get the gradients of the loss w.r.t to the input image.\n", " gradient = tape.gradient(loss, input_image)\n", " # Get the sign of the gradients to create the perturbation\n", " signed_grad = tf.sign(gradient)\n", " return signed_grad" ] }, { "cell_type": "markdown", "metadata": { "id": "RbuftX0eSlDQ" }, "source": [ "由此产生的扰动也可以进行可视化。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "rVjnb6M7Smv4", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "# Get the input label of the image.\n", "labrador_retriever_index = 208\n", "label = tf.one_hot(labrador_retriever_index, image_probs.shape[-1])\n", "label = tf.reshape(label, (1, image_probs.shape[-1]))\n", "\n", "perturbations = create_adversarial_pattern(image, label)\n", "plt.imshow(perturbations[0] * 0.5 + 0.5); # To change [-1, 1] to [0,1]" ] }, { "cell_type": "markdown", "metadata": { "id": "DKKSFHjwCyQH" }, "source": [ "让我们尝试一下调整 ϵ 值,并观察结果图像。您会注意到,随着 ϵ 值的增大,欺骗网络变得更加容易。但需予以权衡,因为这会导致扰动变得更易被识别。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "dBtG0Kl5SspV", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "def display_images(image, description):\n", " _, label, confidence = get_imagenet_label(pretrained_model.predict(image))\n", " plt.figure()\n", " plt.imshow(image[0]*0.5+0.5)\n", " plt.title('{} \\n {} : {:.2f}% Confidence'.format(description,\n", " label, confidence*100))\n", " plt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "3DA8g-Zp69J4", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "epsilons = [0, 0.01, 0.1, 0.15]\n", "descriptions = [('Epsilon = {:0.3f}'.format(eps) if eps else 'Input')\n", " for eps in epsilons]\n", "\n", "for i, eps in enumerate(epsilons):\n", " adv_x = image + eps*perturbations\n", " adv_x = tf.clip_by_value(adv_x, -1, 1)\n", " display_images(adv_x, descriptions[i])" ] }, { "cell_type": "markdown", "metadata": { "id": "fxt5VfnXHQT6" }, "source": [ "## 后续步骤\n", "\n", "现在,您已了解对抗攻击,请在不同的数据集和不同的架构上进行尝试。您也可以创建和训练自己的模型,然后尝试使用相同的方法来欺骗该模型。您还可以尝试查看更改 ϵ 值后预测的置信度将如何变化。\n", "\n", "本教程中展示的攻击虽然强大,但也只是研究对抗攻击的开端,此后已有多篇论文创建了更为强大的攻击。除了对抗攻击之外,研究还延伸到防御的创建,旨在创建稳健的机器学习模型。您可以查看这篇[研究论文](https://arxiv.org/abs/1810.00069)来获取对抗攻击和防御的完整列表。\n", "\n", "有关对抗攻击和防御的更多实现,您可以参见对抗示例库 [CleverHans](https://github.com/tensorflow/cleverhans)。" ] } ], "metadata": { "accelerator": "GPU", "colab": { "collapsed_sections": [], "name": "adversarial_fgsm.ipynb", "toc_visible": true }, "kernelspec": { "display_name": "Python 3", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 0 }