【信息与内容安全】实验一:对抗样本攻击实验

对抗性样本攻击实验

摘要:根据 PyTorch 官网教程中 Adversarial Example Generation 章节内容,完整实现 Fast Gradient Sign Attack (FGSM) 算法。

本文仅作翻译与稍许修改

题目描述

根据PyTorch官网教程中Adversarial Example Generation章节内容,完整实现 Fast Gradient Sign Attack (FGSM) 算法。
网址: https://pytorch.org/tutorials/beginner/fgsm_tutorial.html

欺骗模型的方法

本文改编自:https://pytorch.org/tutorials/beginner/fgsm_tutorial.html

添加不可察觉对图像的扰动可以导致截然不同的模型性能。

我们将探讨该主题通过图像分类器上的示例。具体来说,我们将使用其中之一第一种也是最流行的攻击方法,快速梯度符号攻击(FGSM),以愚弄MNIST分类器。

有许多类别的对抗性攻击,每种攻击都有不同的目标和对攻击者知识的假设。但是,总体而言,首要目标是向输入数据添加最少量的扰动,从而导致所需的错误分类。攻击者的知识有几种假设,其中两种是:白盒黑匣子

  • 白盒攻击假设攻击者具有对模型的全部了解和访问权限,包括体系结构、输入、输出和权重。
  • 黑盒攻击假定攻击者只能访问模型的输入和输出,而对底层体系结构或权重一无所知。

还有几种类型的目标,包括错误分类源/目标错误分类

  • 错误分类的目标意味着对手只希望输出分类是错误的,但并不关心新分类是什么。
  • 源/目标错误分类意味着对手想要更改原始属于特定源类的图像,以便将其分类为特定目标类。

在这种情况下,FGSM 攻击是 白盒 攻击,其目标是错误分类。有了这些背景信息,我们现在就可以详细讨论攻击。

FGSM 快速梯度符号攻击

Fast Gradient Sign Attack(FGSM)

它旨在通过以下方式攻击神经网络:
利用他们的学习方式,梯度。这个想法很简单,而是而不是通过根据反向传播梯度,攻击调整输入数据以最大化基于相同反向传播梯度的损失。换句话说,攻击使用损失的梯度 w.r.t 输入数据,然后调整输入数据以最大化损失。

原理介绍

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

从图中可以看出, x \mathbf{x} x 是原始输入图像
正确归类为“熊猫”, y y y是地面真相标签
for x \mathbf{x} x θ \mathbf{\theta} θ 表示模型
参数, J ( θ , x , y ) J(\mathbf{\theta}, \mathbf{x}, y) Jθxy 是损失
用于训练网络。攻击反向传播
梯度返回输入数据进行计算
∇ x J ( θ , x , y ) \nabla_{x} J(\mathbf{\theta}, \mathbf{x}, y) xJθxy.然后,它调整
按一小步输入数据( ϵ \epsilon ϵ 0.007 0.007 0.007中的
图片)在方向(即
s i g n ( ∇ x J ( θ , x , y ) ) sign(\nabla_{x} J(\mathbf{\theta}, \mathbf{x}, y)) signxJθxy
最大化损失。由此产生的扰动图像 x ′ x' x,则
被目标网络错误地分类为“长臂猿”,当它仍然显然是一只“熊猫”。

下面进入实现。

from __future__ import print_function
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
import numpy as np
import matplotlib.pyplot as plt

# NOTE: This is a hack to get around "User-agent" limitations when downloading MNIST datasets
#       see, https://github.com/pytorch/vision/issues/3497 for more information
# from six.moves import urllib
# opener = urllib.request.build_opener()
# opener.addheaders = [('User-agent', 'Mozilla/5.0')]
# urllib.request.install_opener(opener)

FGSM 实现

在本节中,我们将讨论本教程的输入参数,定义受攻击的模型,然后对攻击进行编码并运行一些测试。

本教程只有三个输入,定义为遵循:

  • epsilons - 用于运行的 ϵ \epsilon ϵ值的列表。是的
    重要的是在列表中保留 0,因为它表示模型
    原始测试集上的性能。此外,直观地说,我们会
    预期 ϵ \epsilon ϵ越大,扰动越明显
    但攻击在退化模型方面越有效
    准确性。由于此处的数据范围是 [ 0 , 1 ] [0,1] [01],因此没有 epsilon
    值应超过 1。
  • pretrained_model - 通往预训练的MNIST模型的路径
  • use_cuda - 使用 CUDA 或不使用。
epsilons = [0, .05, .1, .15, .2, .25, .3]
pretrained_model = "./data/lenet_mnist_model.pth"
use_cuda=True

受攻击的模型

如前所述,受到攻击的模型与来自 pytorch/examples/mnist

您可以训练并保存自己的MNIST模型,也可以下载并使用提供的模型。此处的 Net 定义和测试数据加载器具有
是从 MNIST 示例复制的。

本节的目的是定义模型和数据加载器,然后初始化模型并加载预先训练的weight。

# 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 测试数据集和数据加载器声明
test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('./data', 
                   train=False, # 不使用训练集
                   download=True,  # 下载数据集
                   transform=transforms.Compose([
                        transforms.ToTensor(), 
                    ])),
    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")

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

# 加载预训练模型
model.load_state_dict(torch.load(pretrained_model, map_location='cpu'))

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

Net(
  (conv1): Conv2d(1, 10, kernel_size=(5, 5), stride=(1, 1))
  (conv2): Conv2d(10, 20, kernel_size=(5, 5), stride=(1, 1))
  (conv2_drop): Dropout2d(p=0.5, inplace=False)
  (fc1): Linear(in_features=320, out_features=50, bias=True)
  (fc2): Linear(in_features=50, out_features=10, bias=True)
)

FGSM 攻击函数

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

扰动原始输入。fgsm_attack函数需要三个输入,图像是原始干净的图像( x x x),epsilon
像素级扰动量 ( ϵ \epsilon ϵ) 和 data_grad
是输入图像的损耗的梯度
∇ x J ( θ , x , y ) \nabla_{x} J(\mathbf{\theta}, \mathbf{x}, y) xJ(θxy)).功能
然后创建扰动图像作为

KaTeX parse error: No such environment: align at position 8: \begin{̲a̲l̲i̲g̲n̲}̲perturbed\_imag…

最后,为了保持数据的原始范围,
扰动图像被裁剪到 [ 0 , 1 ] [0,1] [01]范围内。

# FGSM attack code
def fgsm_attack(image, epsilon, data_grad):
    # 收集数据梯度的元素符号
    sign_data_grad = data_grad.sign()
    # 通过调整输入图像的每个像素来创建扰动图像
    perturbed_image = image + epsilon*sign_data_grad
    # 添加剪切以保持 [0,1] 范围
    perturbed_image = torch.clamp(perturbed_image, 0, 1)
    # 返回被扰动的图像
    return perturbed_image

测试攻击效果函数

最后,本教程的核心结果来自test。每次调用此测试函数都会执行一个完整的测试步骤MNIST测试集并报告最终精度。但是,请注意此函数还采用 epsilon 输入。这是因为“测试”函数报告受攻击的模型的准确性来自具有实力的对手 ϵ \epsilon ϵ。更具体地说,对于测试集中的每个样本,该函数计算输入数据( d a t a _ g r a d data\_grad data_grad)的损失会产生扰动带有“fgsm_attack”( p e r t u r b e d _ d a t a perturbed\_data perturbed_data)的图像,然后检查以查看如果令人不安的例子是对抗性的。除了测试模型的精度,该函数还保存并返回一些成功的对抗性示例将在以后可视化。

def test( model, device, test_loader, epsilon):

    # 正确率计数器
    correct = 0
    adv_examples = []

    # 循环访问测试集中的所有示例
    for data, target in test_loader:

        # 将数据和标签发送到 device
        data, target = data.to(device), target.to(device)

        # 设置 tensor 的 requires_grad属性。这对攻击很重要
        data.requires_grad = True

        # 通过模型向前传递数据
        output = model(data)
        init_pred = output.max(1, keepdim=True)[1] # 获取最大对数概率(log-probability)的索引 

        # 如果最初的预测是错误的,直接跳过
        if init_pred.item() != target.item():
            continue

        # 计算loss
        loss = F.nll_loss(output, target)

        # Zero all existing gradients
        model.zero_grad()

        # Calculate gradients of model in backward pass
        loss.backward()

        # 收集数据梯度
        data_grad = data.grad.data

        # Call FGSM Attack
        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
            # 保存 0 epsilon 示例的特殊情况
            if (epsilon == 0) and (len(adv_examples) < 5):
                adv_ex = perturbed_data.squeeze().detach().cpu().numpy() 
                adv_examples.append( (init_pred.item(), final_pred.item(), adv_ex) )
        else:
            # 保存一些攻击成功示例以供以后可视化
            if len(adv_examples) < 5:
                adv_ex = perturbed_data.squeeze().detach().cpu().numpy()
                adv_examples.append( (init_pred.item(), final_pred.item(), adv_ex) )

    # 计算此 epsilon 的最终精度
    final_acc = correct/float(len(test_loader))
    print("Epsilon: {}\tTest Accuracy = {} / {} = {}".format(epsilon, correct, len(test_loader), final_acc))

    # 返回准确性和对抗性示例
    return final_acc, adv_examples

实施攻击

实现的最后一部分是实际运行攻击。在这里我们为epsilons输入中的每个 ϵ \epsilon ϵ 值运行完整的测试步骤。

对于每一个ε,我们还保存了最终的精度和一些成功的结果.在接下来的章节中,我们将列举一些对抗性的例子。注意随着ε值的增加,模型精度降低。而且注意 ϵ = 0 \epsilon=0 ϵ=0 表示原始测试精度,即不进行攻击。

accuracies = []
examples = []

# 对每个ε运行测试
for eps in epsilons:
    acc, ex = test(model, device, test_loader, eps)
    accuracies.append(acc)
    examples.append(ex)
Epsilon: 0	Test Accuracy = 9810 / 10000 = 0.981
Epsilon: 0.05	Test Accuracy = 9426 / 10000 = 0.9426
Epsilon: 0.1	Test Accuracy = 8510 / 10000 = 0.851
Epsilon: 0.15	Test Accuracy = 6826 / 10000 = 0.6826
Epsilon: 0.2	Test Accuracy = 4301 / 10000 = 0.4301
Epsilon: 0.25	Test Accuracy = 2082 / 10000 = 0.2082
Epsilon: 0.3	Test Accuracy = 869 / 10000 = 0.0869

结果分析

准确性 vs ϵ \epsilon ϵ

第一个结果是准确性与 ϵ \epsilon ϵ 的关系图。

如前所述,早些时候,随着 ϵ \epsilon ϵ 的增加,我们预计测试精度会降低。

这是因为更大的 ϵ \epsilon ϵ 意味着我们在方向将最大化损失。请注意,即使 ϵ \epsilon ϵ 值是线性间隔的,准确性也不是线性的。

例如, ϵ = 0.05 \epsilon=0.05 ϵ=0.05时的精度仅低约4%
大于 ϵ = 0 \epsilon=0 ϵ=0,但 ϵ = 0.2 \epsilon=0.2 ϵ=0.2 时的准确率为 25%
低于 ϵ = 0.15 \epsilon=0.15 ϵ=0.15。另外,请注意模型的准确性
达到 10 类分类器的随机精度,介于
ϵ = 0.25 \epsilon=0.25 ϵ=0.25 ϵ = 0.3 \epsilon=0.3 ϵ=0.3

plt.figure(figsize=(5,5))
plt.plot(epsilons, accuracies, "*-")
plt.yticks(np.arange(0, 1.1, step=0.1))
plt.xticks(np.arange(0, .35, step=0.05))
plt.title("Accuracy vs Epsilon")
plt.xlabel("Epsilon")
plt.ylabel("Accuracy")
plt.show()

Image

对抗样本实例

还记得没有免费午餐的想法吗?在这种情况下,随着 ϵ \epsilon ϵ 的增加,测试正确率降低但是扰动变得更加容易感知。

实际上,攻击者必须考虑权衡。在这里,我们在每个 ϵ \epsilon ϵ 上展示一些成功的对抗示例价值。图的每一行都显示不同的 ϵ \epsilon ϵ 值。第一个行是 ϵ = 0 \epsilon=0 ϵ=0 示例,表示原始“干净”的图像,无扰动。每个图像的标题显示原始分类 - >对抗性分类。请注意,扰动在 ϵ = 0.15 \epsilon=0.15 ϵ=0.15 时开始变得明显,并且很明显在 ϵ = 0.3 \epsilon=0.3 ϵ=0.3

然而,在所有情况下,人类都是仍然能够识别正确的类。

# 绘制每个 epsilon 处的对抗性样本的几个示例
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()

Image

参考

Adversarial Example Generation — PyTorch Tutorials 1.11.0+cu102 documentation

  • 4
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
模型后门攻击对抗样本攻击和数据投毒攻击都是机器学习模型中的安全问题,它们之间的相似和不同如下: 1. 相似点:目的相同 这三种攻击方式的目的都是为了欺骗机器学习模型,使其产生错误的结果。攻击者可能会窃取敏感信息、干扰模型的正常运行或者使模型产生错误的结果。 2. 不同点:攻击方式不同 模型后门攻击是通过在训练集中注入后门或者在模型中插入后门来实现的。对抗样本攻击是通过对输入数据进行修改,使其在人眼看来几乎无异,但模型却会将其判定错误。数据投毒攻击则是通过在训练集中注入恶意数据,使得模型在测试时产生错误的结果。 3. 不同点:攻击难度不同 模型后门攻击通常需要攻击者具有一定的训练数据和模型架构的了解,攻击难度较高。对抗样本攻击和数据投毒攻击相对来说比较容易实现,只需要攻击者掌握一定的攻击技巧即可。 4. 不同点:攻击效果不同 模型后门攻击通常是面向特定的目标,攻击效果较为明显。对抗样本攻击和数据投毒攻击则可能会对整个模型产生影响,攻击效果相对较弱。 综上所述,这三种攻击方式在攻击方式、攻击难度和攻击效果等方面存在差异,但它们的目的都是为了欺骗机器学习模型。在机器学习模型的安全性方面,需要综合考虑这些攻击方式,采取相应的防御措施来保证模型的安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值