当前位置: 首页 > news >正文

Pytorch学习笔记(十七)Image and Video - Adversarial Example Generation

这篇博客瞄准的是 pytorch 官方教程中 Image and Video 章节的 Adversarial Example Generation 部分。

  • 官网链接:https://pytorch.org/tutorials/beginner/fgsm_tutorial.html
完整网盘链接: https://pan.baidu.com/s/1L9PVZ-KRDGVER-AJnXOvlQ?pwd=aa2m 提取码: aa2m 

Adversarial Example Generation

本教程将提高你对 ML 模型安全漏洞的认识,并深入了解对抗性机器学习。试验过程中你会发现,向图像添加人眼难以察觉的扰动会导致模型性能发生巨大变化。这里将通过图像分类器来对这一现象进行演示,使用最早也是最流行的攻击方法之一,快速梯度符号攻击 (FGSM),来欺骗 MNIST 分类器。

这个攻击方式中有一个关键部分 符号,如果你对模型很敏感的话会立即联想到CV模型中最常用的激活函数 ReLU,该函数将输入转为0/1的int量,所以该攻击的主要对象就是ReLU函数,让其在计算过程中将0反算成1,1反算成0 就可以达到攻击目的。


Threat Model

对抗性攻击有很多种,每种攻击都有不同的目标和对攻击者的假设。但通常来说,总目标是对输入数据添加最少的扰动以导致所需的错误分类。对攻击者的其中两种假设为 “白盒” 和 “黑盒”。

  • 白盒攻击:假设攻击者完全了解并可以访问模型,包括架构、输入、输出和权重;
  • 黑盒攻击:假设攻击者只能访问模型的输入和输出,对底层架构或权重一无所知;

还有两种类型的目标,包括 “错误分类”、“源/目标错误”分类:

  • 错误分类:只希望输出分类是错误的,但不关心新的分类是什么;
  • 源/目标错误分类:想要改变原本属于特定源类的图像,使其被归类为特定的目标类;

FGSM 攻击是一种白盒攻击,目的是让模型进行错误分类。


Fast Gradient Sign Attack

最早也是最流行的对抗性攻击之一被称为 快速梯度符号攻击 (FGSM),Goodfellow 等人在论文《Explaining and Harnessing Adversarial Examples》 中对此进行了描述。这种攻击非常强大而且很直观,旨在通过利用神经网络的学习方式(梯度)来攻击神经网络,其核心思想为:

  • 传统的神经网络训练是通过梯度下降来最小化损失函数,即调整权重,让模型的输出更接近真实标签;
  • FGSM 的思路是相反的:它不是调整权重,而是调整输入数据,让损失函数最大化,从而欺骗模型;

具体操作如下:

  • 计算损失函数相对于输入数据的梯度(而不是相对于权重的梯度);
  • 沿着梯度的正方向调整输入数据,使得损失增大,从而让模型更容易被误导;

x ′ = x + ϵ ⋅ s i g n ( ∇ x J ( θ , x , y ) ) x^{'}=x+\epsilon\cdot sign(\nabla_{x}J(\theta,x,y)) x=x+ϵsign(xJ(θ,x,y))

其中 x x x 是原始输入; x ′ x^{'} x 是被扰动后的是输入; J ( θ , x , y ) J(\theta,x,y) J(θ,x,y) 是损失函数如CrossEntropyLoss; ∇ x J ( θ , x , y ) \nabla_{x}J(\theta,x,y) xJ(θ,x,y) 是损失函数在输入方向的梯度; ϵ \epsilon ϵ 是扰动大小;

根据官网的描述,在上面式子中只要 ϵ \epsilon ϵ 为一个非常小的值如0.007都会导致模型将整个图像分类错误。
在这里插入图片描述

那么为什么神经网络对微小扰动敏感?这与神经网络的特性、数据的高维性、以及 ReLU(或其他激活函数)共同作用导致:

  1. 高维空间的超平面决策边界:
    • 在高维空间中,数据样本通常距离决策边界很近,而 FGSM 的微小扰动可能正好沿着最容易翻转分类的方向;
    • 由于 FGSM 是基于梯度的,它会精准地找到“最致命”的扰动方向,即最容易跨越决策边界的方向,即使是微小的 ϵ \epsilon ϵ 也足够让样本“跳过边界”;
  2. ReLU 激活函数导致的非线性放大:
    • ReLU(或其他非线性激活函数)可能在某些维度上对输入的变化特别敏感;
    • 如果某个神经元的输入略微变化,使其激活状态翻转(从 0 变成一个大数,或者反之),则整个网络的激活模式都会改变,导致最终分类完全不同;
  3. 权重矩阵的累积放大效应:
    • 由于神经网络是多个层叠加的,前一层的小变化会在后一层被权重矩阵不断放大(或者在特定方向被削弱);
    • 这种效应在 FGSM 里可能表现为,即使输入数据变化极小(比如 ϵ = 0.001 \epsilon=0.001 ϵ=0.001),但由于网络的层层累积,最终分类可能完全不同;

Implementation

导入必要的库

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

FGSM攻击输入部分

epsilons = [0.0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3]
pretrained_model = "data/lenet_mnist_model.pth"
torch.manual_seed(42)

Model Under Attack

这里使用 pytorch/examples/mnist 中的 MNIST 模型,此处的网络定义和测试数据加载器是从 MNIST 示例中复制而来的。本节的目的是定义模型和数据加载器,然后初始化模型并加载预训练权重,点击这个 链接 下载预训练模型权重,然后将其移动到 ./data 文件夹下。

准备模型

class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, 1)
        self.conv2 = nn.Conv2d(32, 64, 3, 1)
        self.dropout1 = nn.Dropout(0.25)
        self.dropout2 = nn.Dropout(0.5)
        self.fc1 = nn.Linear(9216, 128)
        self.fc2 = nn.Linear(128, 10)
        
    def forward(self, x):
        x = self.conv1(x)
        x = F.relu(x)
        x = self.conv2(x)
        x = F.relu(x)
        x = F.max_pool2d(x, 2)
        x = self.dropout1(x)
        x = torch.flatten(x)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.dropout2(x)
        x = self.fc2(x)
        output = F.log_softmax(x, dim=1)
        return output

准备数据集加载器



FGSM Attack

根据上面提到的FGSM公式,将输入到模型中的图像进行修改

def fgsm_accack(image, epsilon, data_grad):
    sign_data_grad = data_grad.sign()
    perturbed_image = image + epsilon*sign_data_grad
    perturbed_image = torch.clamp(perturbed_image, 0, 1)
    return perturbed_image

def denorm(batch, mean=[0.1307], std=[0.3081]):
    if isinstance(mean, list):
        mean = torch.tensor(mean).to(device)
    if isinstance(std, list):
        std = torch.tensor(std).to(device)
    return batch + std.view(1, -1, 1, 1) + mean.view(1, -1, 1, 1)

Testing Function

定义一个测试函数对模型进行FGSM攻击

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
        output = model(data)
        init_pred = output.max(1, keepdim=True)[1]

        # 如果模型本身预测错误则直接跳过这个case
        if init_pred.item() != target.item():
            continue

        loss = F.nll_loss(output, target)
        model.zero_grad()
        loss.backward()

        data_grad = data.grad.data
        data_denorm = denorm(data)

        perturbed_data = fgsm_attack(data_denorm, epsilon, data_grad)
        perturbed_data_normalized = transforms.Normalize((0.1307,), (0.3081,))(perturbed_data)

        output = model(perturbed_data_normalized)

        final_pred = output.max(1, keepdim=True)[1]
        if final_pred.item() == target.item():
            correct += 1
            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) )

    final_acc = correct/float(len(test_loader))
    print(f"Epsilon: {epsilon}\tTest Accuracy = {correct} / {len(test_loader)} = {final_acc}")
    return final_acc, adv_examples

Run Attack

accuracies = []
examples = []

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

Results

Accuracy vs Epsilon

首先查看acc与 epsilon 的关系。随着 epsilon 的增加预计测试acc会下降。这是因为更大的 epsilon 意味朝着最大化loss的方向迈出了更大的一步。但即使在图片中看到 epsilon 值是线性间隔的,实际上曲线中的趋势也不是线性的。

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()

Sample Adversarial Examples

随着 epsilon 的增加测试acc会降低,但扰动会变得更容易被察觉。攻击者必须考虑acc下降和可感知性之间的权衡。这里展示了每个 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(f"Eps: {epsilons[i]}", fontsize=14)
        orig,adv,ex = examples[i][j]
        plt.title(f"{orig} -> {adv}")
        plt.imshow(ex, cmap="gray")
plt.tight_layout()
plt.show()

在这里插入图片描述

相关文章:

  • Day 3 系统总线(1)
  • 未来环境生成助力具身导航!PanoGen++:基于领域自适应全景图像生成的视觉语言导航
  • DeepSeek+Kimi:PPT制作的效率革命
  • redis hashtable 的sizemask理解
  • 多线程 - 线程安全
  • QTreeView开发入门
  • 基于51单片机的简易示波器proteus仿真
  • 树状数组(2025钉耙编程4th 1006进步洛谷3374洛谷3368)
  • Assembly语言的装饰器
  • 【Matlab】-- 基于MATLAB的美赛常用多种算法
  • GPU中的cluster
  • 通过 Docker Swarm 集群探究 Overlay 网络跨主机通信原理
  • Windows 11 中搜索服务索引文件大处理
  • Javaweb后端 AOP快速入门 AOP核心概念 AOP执行流程
  • Springboot学习笔记 3.13
  • 若依前后端不分离字典修改---formatter对原值进行修改
  • 场外基金和ETF场内基金有何区别?ETF佣金最低是多少?
  • 从头开始学C语言第三十六天——函数指针和函数指针数组
  • 【C/C++算法】从浅到深学习---分治算法之快排思想(图文兼备 + 源码详解)
  • Html 页面图标的展示列表
  • 北京百度seo公司/seo综合查询中的具体内容有哪些
  • 品牌策划公司名字大全/手机百度seo快速排名
  • 做门户网站的公司有哪些/免费网站做seo
  • wordpress mx主题VIP/小璇seo优化网站
  • 前端后端都是网站开发吧/全网络品牌推广
  • 做短视频网站好/快速排名优化推广手机