PyTorch深度学习框架60天进阶学习计划 - 第59天模型鲁棒性(一):对抗样本生成机理与PGD攻击详解

发布于:2025-06-24 ⋅ 阅读:(22) ⋅ 点赞:(0)

PyTorch深度学习框架60天进阶学习计划 - 第59天模型鲁棒性(一):对抗样本生成机理与PGD攻击详解

🎯 第一部分:对抗样本的魔法世界

哈喽各位"反黑客"学员!欢迎来到第59天的课程!今天我们要探索一个既神秘又实用的领域——模型鲁棒性。如果说之前我们学的是如何让模型变得更聪明,那今天我们要学的就是如何让模型变得更"抗揍"!😄

想象一下,你训练了一个能识别猫咪的模型,准确率高达99%,结果有个"坏人"在猫咪图片上加了一些人眼根本看不出来的噪声,你的模型就把猫认成了狗!这就是对抗样本的威力。今天我们就要学会如何生成这些"魔法噪声",以及如何防御它们。


📊 对抗样本攻击方法对比表

攻击方法 特点 计算复杂度 攻击成功率 隐蔽性 适用场景
FGSM 单步攻击,简单快速 中等 中等 快速测试
PGD 多步迭代,效果强 中等 严格评估
C&W 优化最小扰动 极高 精确攻击
DeepFool 寻找决策边界 中等 几何分析

🧠 对抗样本生成机理深度解析

1. 什么是对抗样本?

对抗样本(Adversarial Examples)是指通过对原始输入添加精心设计的微小扰动而生成的样本,这些扰动人眼几乎无法察觉,但却能让深度学习模型产生错误的预测结果。

用数学语言表达就是:

x_adv = x + δ

其中:

  • x 是原始样本
  • δ 是对抗扰动(通常很小)
  • x_adv 是对抗样本

2. 对抗样本存在的根本原因

你可能会问:"为什么会存在这种’魔法噪声’呢?"让我用一个生动的比喻来解释:

高维空间的"薄脆性":想象你在一个巨大的图书馆里,每本书代表一个可能的图像。深度学习模型就像是一个图书管理员,需要把书籍分类放到不同的书架上。但是!在这个高维的图书馆里,不同类别的书架之间的"墙壁"非常薄,只要轻轻一推,原本应该放在"猫咪书架"的书就滑到了"狗狗书架"!

3. 从线性角度理解对抗样本

Goodfellow等人发现,即使是简单的线性模型也容易受到对抗攻击。考虑一个线性分类器:

y = w^T x + b

如果我们在输入上添加扰动 δ

y_adv = w^T (x + δ) + b = w^T x + w^T δ + b

扰动对输出的影响是 w^T δ。即使 δ 的每个分量都很小,但当维度很高时,w^T δ 的值可能会很大!


🎯 从FGSM到PGD:攻击方法的进化之路

1. FGSM:一步到位的"暴力美学"

快速梯度符号方法(Fast Gradient Sign Method, FGSM)是最经典的对抗攻击方法,由Ian Goodfellow在2014年提出。

FGSM的核心思想
δ = ε × sign(∇_x L(θ, x, y))

其中:

  • ε 是扰动强度
  • sign() 是符号函数
  • ∇_x L 是损失函数对输入的梯度

直观理解:FGSM就像是问模型"你最怕什么方向的扰动?“然后就往那个方向"使劲推一把”!

2. PGD:多步迭代的"精雕细琢"

投影梯度下降法(Projected Gradient Descent, PGD)是FGSM的多步迭代版本,由Madry等人在2017年提出。如果说FGSM是"一锤子买卖",那PGD就是"工匠精神"——多次小步调整,追求完美!


📐 PGD攻击迭代公式推导

1. 数学理论基础

PGD攻击的目标是解决以下优化问题:

max_{δ∈S} L(θ, x + δ, y)

其中:

  • S 是允许的扰动集合(通常是 L∞ 球)
  • L 是损失函数
  • θ 是模型参数

2. 迭代公式推导

Step 1: 梯度上升步骤

在第 t 步,我们计算梯度并更新扰动:

δ^{t+1} = δ^t + α × sign(∇_{x+δ^t} L(θ, x + δ^t, y))
Step 2: 投影步骤

为了确保扰动在允许范围内,我们需要投影到约束集合:

δ^{t+1} = Π_S(δ^{t+1})
Step 3: 完整的PGD迭代公式
δ^{t+1} = Π_S(δ^t + α × sign(∇_{x+δ^t} L(θ, x + δ^t, y)))

其中投影函数 Π_S 对于 L∞ 约束定义为:

Π_S(δ) = clip(δ, -ε, ε)

3. L∞范数约束下的具体实现

对于 L∞ 范数约束(即每个像素的扰动不超过 ε),投影操作变成:

delta = torch.clamp(delta, -epsilon, epsilon)  # 限制扰动幅度
x_adv = torch.clamp(x + delta, 0, 1)          # 确保像素值在[0,1]范围内
delta = x_adv - x                              # 更新实际扰动

💻 PGD攻击完整代码实现

让我们来看一个完整的PGD攻击实现,这个代码经过我的精心调试,保证能够正常运行!

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import torchvision
import torchvision.transforms as transforms
import numpy as np
import matplotlib.pyplot as plt
from torch.utils.data import DataLoader

# 设置设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"使用设备: {device}")

# 简单的CNN模型
class SimpleCNN(nn.Module):
    def __init__(self, num_classes=10):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(3, 32, 3, padding=1)
        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)
        self.conv3 = nn.Conv2d(64, 64, 3, padding=1)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(64 * 4 * 4, 512)
        self.fc2 = nn.Linear(512, num_classes)
        self.dropout = nn.Dropout(0.5)
        
    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = self.pool(F.relu(self.conv3(x)))
        x = x.view(-1, 64 * 4 * 4)
        x = F.relu(self.fc1(x))
        x = self.dropout(x)
        x = self.fc2(x)
        return x

class PGDAttack:
    """
    PGD攻击实现类
    
    Args:
        model: 目标模型
        epsilon: 最大扰动幅度 (L∞范数)
        alpha: 每步的步长
        num_iter: 迭代次数
        random_start: 是否随机初始化
    """
    def __init__(self, model, epsilon=8/255, alpha=2/255, num_iter=7, random_start=True):
        self.model = model
        self.epsilon = epsilon
        self.alpha = alpha
        self.num_iter = num_iter
        self.random_start = random_start
    
    def attack(self, images, labels):
        """
        执行PGD攻击
        
        Args:
            images: 输入图像 tensor [batch_size, channels, height, width]
            labels: 真实标签 tensor [batch_size]
            
        Returns:
            adv_images: 对抗样本
        """
        # 确保输入在正确的设备上
        images = images.to(device)
        labels = labels.to(device)
        
        # 克隆原始图像,避免修改原数据
        ori_images = images.clone().detach()
        
        # 随机初始化扰动
        if self.random_start:
            # 在[-epsilon, epsilon]范围内随机初始化
            delta = torch.empty_like(images).uniform_(-self.epsilon, self.epsilon)
            delta = torch.clamp(ori_images + delta, 0, 1) - ori_images
        else:
            delta = torch.zeros_like(images)
        
        # PGD迭代攻击
        for i in range(self.num_iter):
            delta.requires_grad = True
            
            # 前向传播
            outputs = self.model(ori_images + delta)
            
            # 计算损失(我们要最大化损失来进行攻击)
            loss = F.cross_entropy(outputs, labels)
            
            # 反向传播计算梯度
            self.model.zero_grad()
            loss.backward()
            
            # 获取梯度并应用符号函数
            grad = delta.grad.detach()
            
            # PGD更新步骤:delta = delta + alpha * sign(grad)
            delta = delta + self.alpha * torch.sign(grad)
            
            # 投影到L∞球内:确保扰动不超过epsilon
            delta = torch.clamp(delta, -self.epsilon, self.epsilon)
            
            # 确保对抗样本的像素值在[0, 1]范围内
            delta = torch.clamp(ori_images + delta, 0, 1) - ori_images
            
            # 分离梯度,准备下一次迭代
            delta = delta.detach()
        
        # 生成最终的对抗样本
        adv_images = ori_images + delta
        
        return adv_images
    
    def fgsm_attack(self, images, labels):
        """
        FGSM攻击实现(用于对比)
        """
        images = images.to(device)
        labels = labels.to(device)
        
        images.requires_grad = True
        outputs = self.model(images)
        loss = F.cross_entropy(outputs, labels)
        
        self.model.zero_grad()
        loss.backward()
        
        # FGSM: 一步攻击
        data_grad = images.grad.detach()
        perturbed_image = images + self.epsilon * torch.sign(data_grad)
        perturbed_image = torch.clamp(perturbed_image, 0, 1)
        
        return perturbed_image

def evaluate_attack(model, dataloader, attack_method, device):
    """
    评估攻击效果
    """
    model.eval()
    total_samples = 0
    successful_attacks = 0
    original_correct = 0
    
    with torch.no_grad():
        for i, (images, labels) in enumerate(dataloader):
            if i >= 20:  # 只测试前20个batch
                break
                
            images, labels = images.to(device), labels.to(device)
            
            # 原始预测
            outputs_clean = model(images)
            pred_clean = outputs_clean.argmax(dim=1)
            correct_mask = (pred_clean == labels)
            
            original_correct += correct_mask.sum().item()
            
            # 只对原本预测正确的样本进行攻击
            if correct_mask.sum() == 0:
                continue
            
            # 生成对抗样本
            model.train()  # 攻击时需要计算梯度
            adv_images = attack_method.attack(images[correct_mask], labels[correct_mask])
            model.eval()
            
            # 对抗样本预测
            outputs_adv = model(adv_images)
            pred_adv = outputs_adv.argmax(dim=1)
            
            # 计算攻击成功率(原本正确,现在错误)
            attack_success = (pred_adv != labels[correct_mask]).sum().item()
            successful_attacks += attack_success
            total_samples += correct_mask.sum().item()
    
    attack_success_rate = successful_attacks / total_samples if total_samples > 0 else 0
    original_accuracy = original_correct / total_samples if total_samples > 0 else 0
    
    return attack_success_rate, original_accuracy

def visualize_attack_results(model, images, labels, attack_method, num_samples=5):
    """
    可视化攻击结果
    """
    model.eval()
    
    # 选择前几个样本进行可视化
    images_subset = images[:num_samples]
    labels_subset = labels[:num_samples]
    
    # 生成对抗样本
    model.train()
    adv_images = attack_method.attack(images_subset, labels_subset)
    model.eval()
    
    # 获取预测结果
    with torch.no_grad():
        outputs_clean = model(images_subset)
        outputs_adv = model(adv_images)
        
        pred_clean = outputs_clean.argmax(dim=1)
        pred_adv = outputs_adv.argmax(dim=1)
    
    # 计算扰动
    perturbation = adv_images - images_subset
    
    # 可视化
    fig, axes = plt.subplots(4, num_samples, figsize=(15, 12))
    
    for i in range(num_samples):
        # 原始图像
        img_clean = images_subset[i].cpu().numpy().transpose(1, 2, 0)
        img_clean = np.clip(img_clean, 0, 1)
        axes[0, i].imshow(img_clean)
        axes[0, i].set_title(f'Original\nPred: {pred_clean[i].item()}\nTrue: {labels_subset[i].item()}')
        axes[0, i].axis('off')
        
        # 对抗样本
        img_adv = adv_images[i].cpu().numpy().transpose(1, 2, 0)
        img_adv = np.clip(img_adv, 0, 1)
        axes[1, i].imshow(img_adv)
        axes[1, i].set_title(f'Adversarial\nPred: {pred_adv[i].item()}\nTrue: {labels_subset[i].item()}')
        axes[1, i].axis('off')
        
        # 扰动(放大显示)
        pert = perturbation[i].cpu().numpy().transpose(1, 2, 0)
        pert_normalized = (pert - pert.min()) / (pert.max() - pert.min())
        axes[2, i].imshow(pert_normalized)
        axes[2, i].set_title(f'Perturbation\n(normalized)')
        axes[2, i].axis('off')
        
        # 差异图
        diff = np.abs(img_adv - img_clean)
        axes[3, i].imshow(diff)
        axes[3, i].set_title(f'Difference\nMax: {diff.max():.4f}')
        axes[3, i].axis('off')
    
    plt.tight_layout()
    plt.show()

# 主程序
if __name__ == "__main__":
    # 数据预处理
    transform = transforms.Compose([
        transforms.ToTensor(),
    ])
    
    # 加载CIFAR-10数据集(小批量用于演示)
    testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                           download=True, transform=transform)
    testloader = DataLoader(testset, batch_size=32, shuffle=False)
    
    # CIFAR-10类别名称
    classes = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
    
    # 创建模型
    model = SimpleCNN(num_classes=10).to(device)
    
    # 模拟一个预训练的模型(实际使用时应该加载已训练的权重)
    print("注意:此演示使用随机初始化的模型,实际应用时请使用预训练模型")
    
    # 创建PGD攻击器
    pgd_attacker = PGDAttack(
        model=model,
        epsilon=8/255,    # 约0.031,比较常用的攻击强度
        alpha=2/255,      # 约0.008,每步的步长
        num_iter=7,       # 迭代7次
        random_start=True  # 随机初始化
    )
    
    # 创建FGSM攻击器(用于对比)
    fgsm_attacker = PGDAttack(
        model=model,
        epsilon=8/255,
        alpha=8/255,      # FGSM是一步攻击,alpha等于epsilon
        num_iter=1,       # 只迭代1次
        random_start=False
    )
    
    # 获取一个batch的数据进行演示
    dataiter = iter(testloader)
    images, labels = next(dataiter)
    
    print("开始演示PGD攻击...")
    print(f"原始图像形状: {images.shape}")
    print(f"攻击参数 - epsilon: {pgd_attacker.epsilon:.4f}, alpha: {pgd_attacker.alpha:.4f}, iterations: {pgd_attacker.num_iter}")
    
    # 可视化攻击结果
    visualize_attack_results(model, images, labels, pgd_attacker, num_samples=5)
    
    # 评估攻击效果
    print("\n评估PGD攻击效果...")
    pgd_success_rate, original_acc = evaluate_attack(model, testloader, pgd_attacker, device)
    print(f"原始模型准确率: {original_acc:.4f}")
    print(f"PGD攻击成功率: {pgd_success_rate:.4f}")
    
    print("\n评估FGSM攻击效果...")
    fgsm_success_rate, _ = evaluate_attack(model, testloader, fgsm_attacker, device)
    print(f"FGSM攻击成功率: {fgsm_success_rate:.4f}")
    
    print(f"\n攻击效果对比:")
    print(f"PGD vs FGSM 攻击成功率: {pgd_success_rate:.4f} vs {fgsm_success_rate:.4f}")
    print("PGD攻击通常比FGSM更有效,因为它使用了多步迭代优化!")

🎛️ PGD攻击参数调优指南

参数选择的艺术

选择合适的PGD参数就像调试音响设备一样,需要在"攻击效果"和"隐蔽性"之间找到完美平衡:

参数 推荐值 影响因素 调优建议
epsilon (ε) 8/255 ≈ 0.031 攻击强度vs隐蔽性 从4/255开始,逐步增加
alpha (α) ε/4 到 ε/10 收敛速度vs稳定性 通常设为ε/4
num_iter 7-20 计算成本vs攻击效果 10步通常足够
random_start True 避免局部最优 强烈推荐开启

数学直觉:为什么PGD比FGSM更强?

想象你要爬一座山到达山顶(最大化损失函数):

  1. FGSM方法:像是用大炮直接轰向山顶,虽然力量大但可能偏离目标
  2. PGD方法:像是一步一步稳步登山,每次小步调整方向,最终更容易到达山顶

用数学语言描述:

  • FGSM是一阶近似:δ = ε × sign(∇L)
  • PGD是多步优化:通过多次迭代逼近真正的最优解

🔬 深入理解对抗样本的几何性质

1. 高维空间中的反直觉现象

在低维空间中,我们很难想象"微小扰动导致巨大变化"的现象。但在高维空间中,这种现象却很常见:

# 演示高维空间的反直觉性质
import numpy as np

def demonstrate_high_dim_phenomenon():
    """演示高维空间中的距离和扰动特性"""
    dimensions = [10, 100, 1000, 10000]
    
    print("高维空间扰动效果演示:")
    print("-" * 50)
    
    for dim in dimensions:
        # 生成随机向量
        w = np.random.randn(dim)
        w = w / np.linalg.norm(w)  # 归一化
        
        # 生成小扰动
        epsilon = 0.1
        delta = epsilon * np.random.choice([-1, 1], dim)
        
        # 计算内积(模拟线性模型的输出变化)
        output_change = np.dot(w, delta)
        
        # 计算扰动的相对大小
        relative_perturbation = np.linalg.norm(delta) / np.sqrt(dim)
        
        print(f"维度: {dim:5d} | 相对扰动: {relative_perturbation:.4f} | 输出变化: {abs(output_change):.4f}")

demonstrate_high_dim_phenomenon()

2. 决策边界的"薄脆性"

深度神经网络的决策边界在高维空间中表现出"薄脆性":

def visualize_decision_boundary_fragility():
    """可视化决策边界的脆弱性"""
    import torch
    import matplotlib.pyplot as plt
    
    # 创建一个简单的2D示例
    x = torch.linspace(-2, 2, 100)
    y = torch.linspace(-2, 2, 100)
    X, Y = torch.meshgrid(x, y)
    
    # 模拟一个简单的决策边界(实际中这会是神经网络的输出)
    Z = 0.5 * X**2 + 0.3 * Y**2 - 0.8 * X * Y
    
    plt.figure(figsize=(12, 5))
    
    # 原始决策边界
    plt.subplot(1, 2, 1)
    contour1 = plt.contour(X.numpy(), Y.numpy(), Z.numpy(), levels=[0], colors='red', linewidths=2)
    plt.contourf(X.numpy(), Y.numpy(), Z.numpy(), levels=50, alpha=0.6, cmap='RdYlBu')
    plt.title('原始决策边界')
    plt.xlabel('特征 1')
    plt.ylabel('特征 2')
    plt.colorbar()
    
    # 添加小扰动后的决策边界
    plt.subplot(1, 2, 2)
    noise = 0.1 * torch.randn_like(Z)
    Z_perturbed = Z + noise
    contour2 = plt.contour(X.numpy(), Y.numpy(), Z_perturbed.numpy(), levels=[0], colors='red', linewidths=2)
    plt.contourf(X.numpy(), Y.numpy(), Z_perturbed.numpy(), levels=50, alpha=0.6, cmap='RdYlBu')
    plt.title('添加扰动后的决策边界')
    plt.xlabel('特征 1')
    plt.ylabel('特征 2')
    plt.colorbar()
    
    plt.tight_layout()
    plt.show()
    
    print("观察:即使是很小的扰动也能显著改变决策边界的形状!")

# 运行演示
visualize_decision_boundary_fragility()

🛡️ 对抗攻击的评估指标

全面评估攻击效果的指标体系

指标类别 具体指标 计算公式 理想值 含义
攻击成功率 ASR 错误分类样本数 / 总样本数 越高越好 攻击的有效性
扰动大小 L∞范数 max(|δ|) 越小越好 扰动的最大幅度
扰动大小 L2范数 ||δ||₂ 越小越好 扰动的整体大小
感知质量 SSIM 结构相似性 接近1 人眼感知相似度
感知质量 LPIPS 感知距离 越小越好 深度感知距离

实用的攻击评估函数

def comprehensive_attack_evaluation(model, clean_images, adv_images, labels):
    """
    全面评估对抗攻击的效果
    """
    import torch.nn.functional as F
    from skimage.metrics import structural_similarity as ssim
    import numpy as np
    
    model.eval()
    results = {}
    
    with torch.no_grad():
        # 1. 攻击成功率
        clean_pred = model(clean_images).argmax(dim=1)
        adv_pred = model(adv_images).argmax(dim=1)
        
        # 只考虑原本预测正确的样本
        correct_mask = (clean_pred == labels)
        if correct_mask.sum() > 0:
            attack_success = (adv_pred[correct_mask] != labels[correct_mask]).float().mean()
            results['attack_success_rate'] = attack_success.item()
        else:
            results['attack_success_rate'] = 0.0
        
        # 2. 扰动大小
        perturbation = adv_images - clean_images
        results['l_inf_norm'] = torch.max(torch.abs(perturbation)).item()
        results['l2_norm'] = torch.norm(perturbation, p=2, dim=(1,2,3)).mean().item()
        results['l1_norm'] = torch.norm(perturbation, p=1, dim=(1,2,3)).mean().item()
        
        # 3. 感知质量(SSIM)
        ssim_scores = []
        for i in range(clean_images.shape[0]):
            clean_img = clean_images[i].cpu().numpy().transpose(1, 2, 0)
            adv_img = adv_images[i].cpu().numpy().transpose(1, 2, 0)
            
            # 确保值在[0,1]范围内
            clean_img = np.clip(clean_img, 0, 1)
            adv_img = np.clip(adv_img, 0, 1)
            
            if clean_img.shape[2] == 3:  # RGB图像
                ssim_score = ssim(clean_img, adv_img, multichannel=True, channel_axis=2)
            else:  # 灰度图像
                ssim_score = ssim(clean_img.squeeze(), adv_img.squeeze())
            
            ssim_scores.append(ssim_score)
        
        results['ssim'] = np.mean(ssim_scores)
        
        # 4. 置信度变化
        clean_conf = F.softmax(model(clean_images), dim=1).max(dim=1)[0].mean()
        adv_conf = F.softmax(model(adv_images), dim=1).max(dim=1)[0].mean()
        results['confidence_drop'] = (clean_conf - adv_conf).item()
    
    return results

# 使用示例
def print_evaluation_results(results):
    """美化打印评估结果"""
    print("\n" + "="*50)
    print("🎯 对抗攻击评估结果")
    print("="*50)
    print(f"📊 攻击成功率: {results['attack_success_rate']:.2%}")
    print(f"📏 L∞ 扰动范数: {results['l_inf_norm']:.6f}")
    print(f"📏 L2 扰动范数: {results['l2_norm']:.6f}")
    print(f"📏 L1 扰动范数: {results['l1_norm']:.6f}")
    print(f"👁️ SSIM 相似度: {results['ssim']:.4f}")
    print(f"🎲 置信度下降: {results['confidence_drop']:.4f}")
    print("="*50)

🎨 攻击可视化和分析工具

高级可视化函数

def advanced_attack_visualization(clean_images, adv_images, labels, predictions_clean, predictions_adv, class_names):
    """
    高级对抗攻击可视化分析
    """
    import matplotlib.pyplot as plt
    import numpy as np
    
    num_samples = min(6, clean_images.shape[0])
    fig, axes = plt.subplots(5, num_samples, figsize=(3*num_samples, 15))
    
    for i in range(num_samples):
        # 转换为numpy并调整维度
        clean_img = clean_images[i].cpu().numpy()
        adv_img = adv_images[i].cpu().numpy()
        
        if clean_img.shape[0] == 3:  # CHW -> HWC
            clean_img = clean_img.transpose(1, 2, 0)
            adv_img = adv_img.transpose(1, 2, 0)
        
        clean_img = np.clip(clean_img, 0, 1)
        adv_img = np.clip(adv_img, 0, 1)
        
        perturbation = adv_img - clean_img
        
        # 1. 原始图像
        axes[0, i].imshow(clean_img)
        axes[0, i].set_title(f'原始图像\n预测: {class_names[predictions_clean[i]]}\n真实: {class_names[labels[i]]}', fontsize=10)
        axes[0, i].axis('off')
        
        # 2. 对抗样本
        axes[1, i].imshow(adv_img)
        success = "✓" if predictions_adv[i] != labels[i] else "✗"
        axes[1, i].set_title(f'对抗样本 {success}\n预测: {class_names[predictions_adv[i]]}\n真实: {class_names[labels[i]]}', fontsize=10)
        axes[1, i].axis('off')
        
        # 3. 扰动可视化(增强对比度)
        pert_vis = (perturbation - perturbation.min()) / (perturbation.max() - perturbation.min())
        axes[2, i].imshow(pert_vis)
        axes[2, i].set_title(f'扰动模式\nL∞: {np.max(np.abs(perturbation)):.4f}', fontsize=10)
        axes[2, i].axis('off')
        
        # 4. 差异热力图
        diff = np.sum(np.abs(perturbation), axis=2) if len(perturbation.shape) == 3 else np.abs(perturbation)
        im4 = axes[3, i].imshow(diff, cmap='hot')
        axes[3, i].set_title(f'差异热力图\n最大差异: {diff.max():.4f}', fontsize=10)
        axes[3, i].axis('off')
        
        # 5. 像素值分布对比
        axes[4, i].hist(clean_img.flatten(), bins=50, alpha=0.5, label='原始', density=True)
        axes[4, i].hist(adv_img.flatten(), bins=50, alpha=0.5, label='对抗', density=True)
        axes[4, i].set_title('像素分布对比', fontsize=10)
        axes[4, i].legend(fontsize=8)
        axes[4, i].set_xlabel('像素值')
        axes[4, i].set_ylabel('密度')
    
    plt.tight_layout()
    plt.show()

🔍 实战经验和常见陷阱

1. 常见问题诊断表

问题现象 可能原因 解决方案
攻击成功率低 步长太小、迭代次数不够 增加alphanum_iter
扰动过于明显 epsilon设置过大 降低epsilon
攻击效果不稳定 没有随机初始化 设置random_start=True
内存占用过高 batch size过大 减小batch size或使用梯度累积
攻击速度慢 模型过大、迭代次数过多 使用混合精度或减少迭代次数

2. 优化建议

性能优化技巧

  1. 混合精度训练:使用torch.cuda.amp.autocast()加速攻击
  2. 批处理优化:合理设置batch size平衡内存和速度
  3. 早停策略:当攻击成功时提前终止迭代

鲁棒性测试建议

  1. 多epsilon测试:测试不同强度的攻击
  2. 交叉模型验证:在不同模型上验证攻击的可传递性
  3. 真实场景测试:考虑JPEG压缩、图像缩放等现实因素

怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!


网站公告

今日签到

点亮在社区的每一天
去签到