PyTorch深度学习实战(28)——对抗攻击(Adversarial Attack)

发布时间:2023年12月27日

0. 前言

近年来,深度学习在图像分类、目标检测、图像分割等诸多领域取得了突破性进展,深度学习模型已经能够以接近甚至超越人类水平的完成某些特定任务。但最近的研究表明,深度学习模型容易受到输入数据中细微扰动的影响,从而导致模型输出错误的预测。在图像领域,此类扰动通常很小对于人眼而言甚至无法察觉,但它们却能够愚弄深度学习模型。针对深度学习模型的这种对抗攻击,限制了深度学习的成功在更广泛领域的应用。本节中,我们将介绍对抗攻击 (Adversarial Attack) 的基本概念,并使用 PyTorch 实现对抗攻击生成可欺骗神经网络的图像。

1. 对抗攻击

深度学习在执行各种计算机视觉任务方面都有着优异的准确性,但尽管深度学习模型的精确度很高,现代深度网络却容易被微小扰动形式的对抗攻击所干扰,这些扰动对虽然对人类视觉系统而言几乎无法感知,但却可能导致神经网络分类器完全改变其对图像的预测。甚至,被攻击的模型对错误的预测结果具有很高的置信度。对抗攻击 (Adversarial Attack) 是针对机器学习模型的一种攻击方式,通过精心构造的数据输入,来欺骗机器学习模型以使其产生错误的结果。
包含恶意扰动的数据通常称为对抗样本 (Adversarial Example),而对抗攻击 (Adversarial Attack) 则是构建对抗样本的并对目标模型实施攻击的过程。例如,如下图所示,通过在图像中添加不明显的扰动,并不会影响人类对其内容的判断,但深度神经网络却对扰动后的图像输出了完全错误的分类结果。

对抗样本示例

2. 对抗攻击模型分析

本质上,对抗攻击是对输入图像值(像素)进行更改。在本节中,我们将学习如何调整输入图像,以使训练后性能良好的深度学习模型对修改后的图像输出为指定类别而非原始类别:

  1. 使用红狐图像
  2. 指定模型预测对抗样本的目标类别
  3. 导入预训练模型,冻结模型参数 (gradients = False)
  4. 指定计算输入图像像素值(而非神经网络的权重)的梯度,因为在进行对抗攻击时,我们无法控制模型权重,只能修改输入图像
  5. 计算与模型预测和目标类别对应的损失
  6. 执行反向传播,获取与每个输入像素值相关的梯度
  7. 根据每个输入像素值对应的梯度方向更新输入图像像素值
  8. 重复步骤 5-7,直到模型以较高的置信度将修改后的图像预测为指定类别

3. 使用 PyTorch 实现对抗攻击

在本节中,我们使用 PyTorch 实现上述对抗攻击策略,以生成对抗样本。

(1) 导入相关库、读取输入图像和预训练 ResNet50 模型,另外需要冻结模型参数:

import numpy as np
import torch
from torch import nn
from matplotlib import pyplot as plt

from torchvision.models import resnet50
model = resnet50(pretrained=True)
for param in model.parameters():
    param.requires_grad = False
model = model.eval()

import requests
from PIL import Image
file = '5.png'
original_image = Image.open(file).convert('RGB')
original_image = np.array(original_image)
original_image = torch.Tensor(original_image)

(2) 导入 Imagenet 类别文件并为每个类别分配 ID

image_net_classes = 'https://gist.githubusercontent.com/yrevar/942d3a0ac09ec9e5eb3a/raw/238f720ff059c1f82f368259d1ca4ffa5dd8f9f5/imagenet1000_clsidx_to_labels.txt'
image_net_classes = requests.get(image_net_classes).text
image_net_ids = eval(image_net_classes)
image_net_classes = {i:j for j,i in image_net_ids.items()}

(3) 定义函数执行图像归一化函数 image2tensor() 与逆归一化函数 tensor2image()

from torchvision import transforms as T
from torch.nn import functional as F
normalize = T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
denormalize = T.Normalize([-0.485/0.229, -0.456/0.224, -0.406/0.225], [1/0.229, 1/0.224, 1/0.225])

def image2tensor(input):
    x = normalize(input.clone().permute(2,0,1)/255.)[None]
    return x
def tensor2image(input):
    x = (denormalize(input[0].clone()).permute(1,2,0)*255.).type(torch.uint8)
    return x

(4) 定义预测给定图像类别的函数 predict_on_image()

def predict_on_image(input):
    model.eval()
    plt.imshow(input)
    # show(input)
    plt.show()
    input = image2tensor(input)
    pred = model(input)
    pred = F.softmax(pred, dim=-1)[0]
    prob, clss = torch.max(pred, 0)
    clss = image_net_ids[clss.item()]
    print(f'PREDICTION: `{clss}` @ {prob.item()}')

在以上代码中,将输入图像转换为张量(使用 image2tensor() 函数),并使用预训练模型预测模型类别 clss 及对应概率 prob

(5) 定义对抗攻击函数 attack

attack() 函数使用 imagemodeltarget 作为输入:

from tqdm import trange
losses = []
def attack(image, model, target, epsilon=1e-6):

将图像转换为张量,并指定计算输入图像梯度:

    input = image2tensor(image)
    input.requires_grad = True

计算模型对给定输入 input 的预测结果,然后计算目标类别 target 对应的损失值:

    pred = model(input)
    loss = nn.CrossEntropyLoss()(pred, target)

通过反向传播最小化损失值:

    loss.backward()
    losses.append(loss.mean().item())

根据梯度方向小幅度更新图像:

    output = input - epsilon * input.grad.sign()

在以上代码中,对输入值进行小幅度(乘以 epsilon )更新。也就是说,我们并未直接通过梯度大小更新图像,而是通过在梯度方向上 (input.grad.sign()) 乘以一个非常小的值 (epsilon) 后更新图像。

使用 tensor2image 方法将张量转换回图像后返回输出:

    output = tensor2image(output)
    del input
    return output.detach()

(6) 将图像修改为指定类别。

指定对抗攻击的目标类别 desired_targets

modified_images = []
desired_targets = ['lemon', 'comic book', 'sax, saxophone']

循环遍历 desired_targets,并在每次迭代中将目标类别转换为相应索引:

for target in desired_targets:
    target = torch.tensor([image_net_classes[target]])

修改图像进行对抗攻击,并将结果追加到列表 modified_images 中:

    image_to_attack = original_image.clone()
    for _ in trange(10):
        image_to_attack = attack(image_to_attack, model, target)
    modified_images.append(image_to_attack)

绘制修改后的图像,并显示相应的类别:

for image in [original_image, *modified_images]:
    predict_on_image(image)
# PREDICTION: `lemon` @ 0.9999375343322754
# PREDICTION: `comic book` @ 0.9998908042907715
# PREDICTION: `sax, saxophone` @ 0.9997311234474182

对抗攻击结果
可以看到,即使对图像进行非常微小的改动(甚至在人眼看来并无差别),模型也会对扰动样本以极高的置信度预测输出错误的类别。

小结

尽管深度神经网络在各种计算机视觉任务上具有很高的准确性,但研究表明它们容易受到微小扰动的影响,从而导致它们输出完全错误的预测结果。由于深度学习是当前机器学习和人工智能的核心技术,这一缺陷引起了研究人员广泛的兴趣。本文首先介绍了对抗攻击的基本概念,然后利用 PyTorch 实现了一种经典的对抗攻击算法,通过在图中添加微小扰动令红狐图像被错误的预测为指定类别,我们也可以通过改变攻击的目标索引,来使图像被错误分类为其它类别。

系列链接

PyTorch深度学习实战(1)——神经网络与模型训练过程详解
PyTorch深度学习实战(2)——PyTorch基础
PyTorch深度学习实战(3)——使用PyTorch构建神经网络
PyTorch深度学习实战(4)——常用激活函数和损失函数详解
PyTorch深度学习实战(5)——计算机视觉基础
PyTorch深度学习实战(6)——神经网络性能优化技术
PyTorch深度学习实战(7)——批大小对神经网络训练的影响
PyTorch深度学习实战(8)——批归一化
PyTorch深度学习实战(9)——学习率优化
PyTorch深度学习实战(10)——过拟合及其解决方法
PyTorch深度学习实战(11)——卷积神经网络
PyTorch深度学习实战(12)——数据增强
PyTorch深度学习实战(13)——可视化神经网络中间层输出
PyTorch深度学习实战(14)——类激活图
PyTorch深度学习实战(15)——迁移学习
PyTorch深度学习实战(16)——面部关键点检测
PyTorch深度学习实战(17)——多任务学习
PyTorch深度学习实战(18)——目标检测基础
PyTorch深度学习实战(19)——从零开始实现R-CNN目标检测
PyTorch深度学习实战(20)——从零开始实现Fast R-CNN目标检测
PyTorch深度学习实战(21)——从零开始实现Faster R-CNN目标检测
PyTorch深度学习实战(22)——从零开始实现YOLO目标检测
PyTorch深度学习实战(23)——使用U-Net架构进行图像分割
PyTorch深度学习实战(24)——从零开始实现Mask R-CNN实例分割
PyTorch深度学习实战(25)——自编码器(Autoencoder)
PyTorch深度学习实战(26)——卷积自编码器(Convolutional Autoencoder)
PyTorch深度学习实战(27)——变分自编码器(Variational Autoencoder, VAE)

文章来源:https://blog.csdn.net/LOVEmy134611/article/details/132133963
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。