如何利用Adversarial Attack攻击神经网络

释放双眼,带上耳机,听听看~!
本文介绍了Adversarial Attack的攻击方式,以及如何利用FGSA攻击神经网络,通过代码实现手写数字识别的白盒攻击。

 本文正在参加「金石计划」

一、前言

虽然人工智能技术已经能完成各种超出想象的事情,但是人工智能仍卸不掉人工智障的标签。即使强如ChatGPT也会犯一些非常弱智的错误。网上流传的关于人工智能的表情包,比如下面这个:

如何利用Adversarial Attack攻击神经网络

又或者下面这张:

如何利用Adversarial Attack攻击神经网络

这里问题现在还不能完全避免,这也不是我们今天的主题。我们今天的主题是如何找到这类可以让AI犯错的例子,或者说如何创造出这种例子。这种技术被称为Adversarial Attack(对抗攻击)。

二、Adversarial Attack

攻击神经网络的方式有很多,基于不同的先验知识可以分为黑盒攻击白盒攻击。其中黑盒攻击假定我们不知道网络结构、网络权重,只知道网络的输入输出。而白盒攻击假定我们对模型了如指掌,我们知道网络的结构、网络权重、网络输入输出等。

基于不同的目的,可以分为源/目标误分类针对性误分类误分类置信度降低。其中误分类攻击目的最简单,就是让模型分类错误,这也是本文要实现的一种攻击。

三、Fast Gradient Sign Attack

实现攻击的方式也是多种多样的,本文使用一种名为Fast Gradient Sign Attack(FGSA)的攻击方式,这种方式利用梯度信息对输入进修改,来达到攻击的目的。

3.1 模型的训练

要理解FGSA我们需要知道模型的训练方式,我们可以把模型看做一个函数Fθ(X),其中θ是模型的参数,另外可以用损失函数L(X,Y;θ)来表示模型的好坏。在训练的过程中,可以让参数θ延梯度gθ的反方向更新:θ-=ηgθ。此时我们的目的是希望模型能得到一个比较低的损失值。

3.2 攻击模型

攻击模型利用的同样是梯度,在训练的时候我们希望模型得到好的结果,因此我们更新模型的参数,并且是沿着loss降低的方向更新。而攻击模型的目的是希望得到一个让模型出错的输入,因此我们更新输入信息,并且沿着loss上升的方向更新。

四、代码实现

接下来我们用代码来实现FGSA攻击,这里使用白盒攻击。所以需要先实现一个网络,这里以手写数字为例。

4.1 手写数字识别

白盒攻击的特点是我们知道网络的全部细节,因此我们自己实现一个网络,这个网络的所有细节我们都可以知道。代码如下:

import torch
import torch.nn as nn
from torch import optim
from torchvision.transforms import ToTensor
from torchvision.datasets import MNIST
from torch.utils.data.dataloader import DataLoader


class DigitalRecognition(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(28 * 28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10)
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits


# 加载数据
train_dataset = MNIST("./", True, ToTensor(), download=True)
train_loader = DataLoader(train_dataset, 64)
# 构建模型
model = DigitalRecognition()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.002)
# 训练
for epoch in range(10):
    for data, target in train_loader:
        # forward
        pred = model(data)
        loss = criterion(pred, target)
        optimizer.zero_grad()
        # backward
        loss.backward()
        optimizer.step()
    print(f"epoch: {epoch}, loss: {loss.item()}")
torch.save(model.state_dict(), 'digital.pth')

这里为了方便,省略了测试相关代码,准确率的计算也省去了。代码运行完成后,可以得到一个digital.pth文件。训练部分的代码我们可以是任意的,只需要得到一个模型即可。

4.2 FGSA

得到模型后,我们就可以开始生成攻击数据了。我们编写一个函数,这个函数输入模型的输入X,以及loss对X的梯度,得到一个带有攻击性的输入,代码如下:

def fgsm_attack(image, epsilon, data_grad):
    """
    image: 需要更新的输入
    epsilon:更新程度,epsilon越大图像变化越大,攻击越有效
    data_grad:输入对应的梯度,即loss对image的导数
    """
    # 获取梯度的正负信息
    sign_data_grad = data_grad.sign()
    # 让输入添加梯度信息,即让输入添加能让loss增大的信息
    perturbed_image = image + epsilon * sign_data_grad
    # 把结果映射到0-1之间
    perturbed_image = torch.clamp(perturbed_image, 0, 1)
    return perturbed_image

其中image是我们已有的数据,epsilon是超参数,我们只需要得到image对应的梯度就可以实现攻击了。求image梯度的代码如下:

import torch
import torch.nn as nn
import torch.nn.functional as F
from torchvision import utils
from torchvision.transforms import ToTensor
from torchvision.datasets import MNIST
from torch.utils.data.dataloader import DataLoader
import matplotlib.pyplot as plt

def fgsm_attack(image, epsilon, data_grad):
    pass

# 加载数据
train_dataset = MNIST("./", True, ToTensor(), download=True)
train_loader = DataLoader(train_dataset, 64)
model = DigitalRecognition()
model.load_state_dict(torch.load('digital.pth'))
model.eval()

for data, target in train_loader:
    # 设置输入自动求导
    data.requires_grad = True
    output = model(data)
    loss = F.nll_loss(output, target)
    model.zero_grad()
    loss.backward()
    # loss对data的梯度
    data_grad = data.grad.data
    # 对data进行修改
    perturbed_data = fgsm_attack(data, .15, data_grad)
    # 对攻击数据预测
    output = model(perturbed_data)
    grid = utils.make_grid(perturbed_data, normalize=True)
    with torch.no_grad():
        grid = grid.cpu().numpy().transpose((1, 2, 0))
        print(output.argmax(dim=1).cpu().numpy().reshape((8, 8)))
        plt.imshow(grid)
        plt.show()
    break

在调用loss.backward()后,pytorch帮我们自动求了loss对data的导数,然后调用fgsm_attack,把输入和梯度传进去对数据添加攻击信息。下面是带有攻击性的输入图像:

如何利用Adversarial Attack攻击神经网络

下面的矩阵是各个图像对应的预测结果:

[[3 0 3 3 4 8 8 9]
 [3 2 9 2 8 8 3 3]
 [8 3 3 8 3 0 4 3]
 [2 3 8 3 8 2 8 8]
 [3 2 8 2 8 0 6 4]
 [9 3 5 1 8 5 0 8]
 [1 9 8 0 5 7 8 7]
 [0 4 8 2 0 8 8 9]]

添加攻击信息后的图像对人来说很容易辨别,但是网络无法正确预测。从结果来看我们达到了攻击的目的。

本网站的内容主要来自互联网上的各种资源,仅供参考和信息分享之用,不代表本网站拥有相关版权或知识产权。如您认为内容侵犯您的权益,请联系我们,我们将尽快采取行动,包括删除或更正。
AI教程

NewBing使用前置条件及重定向方法

2023-12-19 9:32:14

AI教程

实战OpenCV手势识别 | 极智视界

2023-12-19 9:41:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索