FGSM对抗攻击算法实现

        本篇文章是博主在人工智能等领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对人工智能等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅解。文章分类在Pytorch

       Pytorch(3)---FGSM对抗攻击算法实现》

FGSM对抗攻击算法实现

目录

一、实验目的  

二、实验内容

三、原理介绍

四、代码解析

五、运行结果


一、实验目的  

        掌握利用快速梯度符号攻击(FGSM)对上一个实验的深度学习卷积神经网络CNN手写数字识别模型进行对抗攻击,愚弄MNIST分类器。


二、实验内容

        Fast Gradient Sign Attack(FGSM),简称快速梯度下降符号攻击,直接利用神经网络的学习方式--梯度更新来攻击神经网络,这种攻击时根据相同的反向传播梯度调整输入数据来最大化损失,换句话说,攻击使用了输入数据相关的梯度损失方式,通过调整输入数据,使损失最大化。

        文献参考:https://arxiv.org/abs/1412.6572


三、原理介绍

        在我们进入代码之前,让我们看一下著名的 FGSM熊猫示例和摘录一些符号。

        上图展示了Fast adversarial examples应用在深度网络上。通过加上一个人类感知不到的小向量,向量的值为ϵ乘输入像素点关于误差的梯度值的符号值。这里 ϵ=0.007,符合经过深度网络转换为实数后8bit图像编码的最小数量级。图中加上噪声后,熊猫被识别为长臂猿,并且置信度为99.3%,所以此方法叫做fast gradient sign method,简称FGSM,我们称之为快速梯度下降法。由此产生的扰动图像 x',则被目标网络错误地分类为“长臂猿”,当它仍然显然是一只“熊猫”。

        接下来,我们将通过代码向大家描述FGSM算法的原理,以及对抗攻击的实现过程。


四、代码解析

        在代码运行前,需要导入运行过程中可能会使用的工具包

# -*- coding: utf-8 -*-
import torch.nn as nn
import torch.nn.functional as F
import torch
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import numpy as np
use_cuda = True
device = torch.device("cuda" if (use_cuda and torch.cuda.is_available()) else "cpu")

模型定义与测试数据加载

        首先,我们需要对分类模型进行定义,包括其网络结构以及输入输出; 此处的 Net 定义和测试数据加载是与上一节MNIST识别中的代码相似。

# LeNet 模型定义
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5) # 卷积层
        self.conv2 = nn.Conv2d(10, 20, kernel_size=5) # 卷积层
        self.conv2_drop = nn.Dropout2d() # dropout层
        self.fc1 = nn.Linear(320, 50)
        self.fc2 = nn.Linear(50, 10)

    def forward(self, x): 
        x = F.relu(F.max_pool2d(self.conv1(x), 2)) # 卷积层
        x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2)) # 卷积层
        x = x.view(-1, 320) # 展平
        x = F.relu(self.fc1(x)) # 全连接层
        x = F.dropout(x, training=self.training) # dropout层
        x = self.fc2(x) # 全连接层
        return F.log_softmax(x, dim=1) # 输出


# MNIST 测试数据集和数据加载器声明
transform = transforms.Compose([transforms.ToTensor()]) #数据类型定义为Tensor张量
test_dataset = datasets.MNIST(root='./data/', train=False, download=False, transform=transform)  # train=True训练集,=False测试集
test_loader = DataLoader(test_dataset, batch_size=1, shuffle=True) # 批大小为1,打乱数据

# 自动选择 GPU 或 CPU
print("CUDA Available: ",torch.cuda.is_available())
device = torch.device("cuda" if (use_cuda and torch.cuda.is_available()) else "cpu")

定义受攻击的模型

        首先我们需要初始化Net网络,接着导入预训练模型作为本次受攻击的模型对象,此处的预训练模型可以是上一节实验中自己训练好保存的模型,也可以下载并使用本地提供的预训练模型,并加载预先训练模型的权重(weight)。

# 初始化网络
model = Net().to(device)

# 加载预训练模型,可以是自己训练好的模型,也可用本地提供的模型lenet_mnist_model.pth
# 添加加载预训练模型的相对路径(路径的前缀可用"./"代替)
pretrained_model =  "./lenet_mnist_model.pth"
model.load_state_dict(torch.load(pretrained_model, map_location='cpu'))

# 将模型设置为评估模式。在本例中,这是针对 Dropout layers 的
model.eval()

FGSM 攻击实现

现在,我们可以通过以下方式定义创建对抗性示例的函数:

fgsm_attack函数需要三个输入,图像是原始干净的图像(x),epsilon是像素级扰动量(ϵ)和 data_grad是输入图像的损耗的梯度。为了保持数据的原始范围,扰动图像被裁剪到 [0,1]范围内。最后,对对扰动量设定不同的数值,探究在不同扰动大小下的攻击效果。

此处需要同学们添加对抗样本生成的一行代码。

# FGSM attack code
def fgsm_attack(image, epsilon, data_grad):  # 此函数的功能是进行fgsm攻击,需要输入三个变量,干净的图片,扰动量和输入图片梯度
    sign_data_grad = data_grad.sign()  # 梯度符号
    # print(sign_data_grad)
    #对抗样本perturbed_image由原始图像叠加epsilon像素级扰动量epsilon与梯度符号sign_data_grad的乘积组成
    perturbed_image = image + epsilon*sign_data_grad
    perturbed_image = torch.clamp(perturbed_image, 0, 1)  # 为了保持图像的原始范围,将受干扰的图像裁剪到一定的范围【0,1】
    return perturbed_image

epsilons = [0, .05, .1, .15, .2, .25, .3]

测试攻击效果

        最后,本实验的核心结果来自test模块。每次调用此测试函数都会执行一个完整的测试步骤MNIST测试集并报告最终精度。但是,请注意此函数还采用epsilon输入。这是因为“测试”函数报告受攻击的模型的准确性来自具有实力的对手ϵ。更具体地说,对于测试集中的每个样本,该函数计算输入数据(data_grad)的损失会产生扰动带有“fgsm_attack”(perturbed_data)的图像,然后输入模型验证其是否具有对抗性。除了测试模型的精度,该函数还保存并返回一些成功的对抗性示例将在实验的最后部分进行可视化。

def test(model, device, test_loader, epsilon):
    correct = 0
    adv_examples = []
    for data, target in test_loader:
        data, target = data.to(device), target.to(device)
        data.requires_grad = True
        #添加代码表示模型(model)的输出output,已知模型(model)的输入为data
        # 此处的model已在前面定义过,此处直接调用即可
        output = model(data)
        init_pred = output.max(1, keepdim=True)[1]  # 选取最大的类别概率
        loss = F.nll_loss(output, target)
        model.zero_grad()
        loss.backward()
        data_grad = data.grad.data
        perturbed_data = fgsm_attack(data, epsilon, data_grad)
        output = model(perturbed_data)
        final_pred = output.max(1, keepdim=True)[1]
        if final_pred.item() == target.item():  # 判断类别是否相等
            correct += 1
        if len(adv_examples) < 6:
            adv_ex = perturbed_data.squeeze().detach().cpu().numpy()
            adv_examples.append((init_pred.item(), final_pred.item(), adv_ex))

    final_acc = correct / float(len(test_loader))  # 算正确率
    print("Epsilon: {}\tTest Accuracy = {} / {} = {}".format(epsilon, correct, len(test_loader), final_acc))
    return final_acc, adv_examples


accuracies = []
examples = []

实施攻击

实现的最后一部分是实际运行攻击。在这里我们为epsilons输入中的每个 ϵ 值运行完整的测试步骤。对于每一个ε,我们还保存了最终的精度和一些成功的结果.在接下来的内容中,我们将列举一些对抗性的例子。注意随着ε值的增加,模型精度降低。而且注意 ϵ=0 表示原始测试精度,即不进行攻击。

# Run test for each epsilon
for eps in epsilons:
    acc, ex = test(model, device, test_loader, eps)
    accuracies.append(acc)
    examples.append(ex)

结果分析

        准确性与ϵ相结合对比分析,第一个结果是准确性与 ϵ的关系图。如前所述,实验前期随着 ϵ的增加,我们预计测试精度会降低。这是因为更大的 ϵ意味着原始图像在梯度方向将最大化损失。实验结果也证实了我们的猜想。

plt.figure(figsize=(5,5))
plt.plot(epsilons, accuracies, "*-")
plt.title("Accuracy vs Epsilon")
plt.xlabel("Epsilon")
plt.ylabel("Accuracy")
plt.plot(epsilons, accuracies)

展示对抗样本

实验的最后,我们可以将不同扰动值Epsilons下的对抗样本进行可视化。在每张对抗样本的上方会显示样本的原始标签以及被攻击后的分类标签,从图中我们不难发现,当扰动值为0.15时,模型出现分类出错的情况,这证明攻击有效,随着扰动值不断增加,攻击成功率越高,攻击效果越好。

cnt = 0
plt.figure(figsize=(8, 10))
for i in range(len(epsilons)):
    for j in range(len(examples[i])):
        cnt += 1
        plt.subplot(len(epsilons), len(examples[0]), cnt)
        plt.xticks([], [])
        plt.yticks([], [])
        if j == 0:
            plt.ylabel("Eps: {}".format(epsilons[i]), fontsize=14)
        orig, adv, ex = examples[i][j]
        plt.title("{} -> {}".format(orig, adv),color=("green" if orig==adv else "red"))
        plt.imshow(ex, cmap="gray")
plt.tight_layout()
plt.show()

五、运行结果


        文章若有不当和不正确之处,还望理解与指出。由于部分文字、图片等来源于互联网,无法核实真实出处,如涉及相关争议,请联系博主删除。如有错误、疑问和侵权,欢迎评论留言联系作者,或者关注VX公众号:Rain21321,联系作者。

猜你喜欢

转载自blog.csdn.net/qq_51399582/article/details/134699501
今日推荐