从FGSM到CW:对抗样本生成与模型鲁棒性评估实践

发布时间:2026/9/18 17:08:50
从FGSM到CW:对抗样本生成与模型鲁棒性评估实践 简介《基于深度学习的对抗样本生成技术研究综述》是一篇聚焦深度学习安全领域的学术综述内容详实、结构清晰适合人工智能、数据研究及网络安全方向的研究生和工程师阅读。文中从对抗样本的起源与基本原理入手将生成方法划分为特定目标攻击与无特定目标攻击两类系统梳理了FGSM、PGD、CW等典型算法并结合MNIST数据集对主要方法进行实验验证同时总结了对抗样本在计算机视觉、医学图像分析、自然语言处理等领域的应用以及当前面临的挑战与未来研究方向。资源共1个PDF文件压缩包大小约1.75MB全文含基金项目、作者简介等学术信息便于学术溯源与引用。目前已有396人学习浏览可作为文献调研、论文写作或模型鲁棒性研究的重要参考资料。1. 从 L-BFGS 到 FGSM对抗样本的起源与反直觉假设一张肉眼完全看不出变化的熊猫图片在 ResNet 的分类结果里却可能是一辆吉普车。这种添加了微小扰动、却能以高置信度骗过深度模型的输入就是对抗样本。2013 年 Szegedy 用 L-BFGS 优化首次暴露出这个现象时学界更愿意相信是深度网络非线性和正则化不足的偶然直到 2014 年 Goodfellow 用一次梯度符号更新就能稳定复现对抗样本生成才从“优化碰巧”变成“可以随手动刀”。这篇刊于《广州大学学报自然科学版》2020 年第 2 期的综述系统梳理了从 FGSM 到 CW 的主流生成方法并用 MNIST 做了对比验证。对刚进入深度学习安全方向的研究生来说它是一张算法地图对做模型鲁棒性验收的工程师而言它给出了衡量模型脆弱性的标准动作清单。2. 威胁模型分类白盒/黑盒、定向/非定向与 Lp 距离度量综述先把对抗样本定义成数学形式对于分类器 f原始样本 X扰动 δ对抗样本 X_adversarial X δ要求距离 D(X_adversarial, X) ε。这个 D 通常是 L0、L2、L∞ 三种范数。优化方向则取决于攻击目标无特定目标攻击最大化损失函数让输出偏离正确类别特定目标攻击最小化到指定类别 t 的损失让模型输出攻击者想要的结果。这两条优化式是所有生成算法的共同骨架。理解这个骨架之后再去看 FGSM、DeepFool、CW 的公式会发现它们只是约束方式不同。2.1 四个分类维度先对齐再选算法这篇综述从期望输出、扰动范围、攻击方式、攻击策略四个维度切分对抗样本生成方法这一点比多数博客只讲“白盒/黑盒”要完整得多。期望输出维度就是常见的非定向与定向攻击非定向只求分类错误定向还要指定错误类别。扰动范围维度则分成特定范围、语境范围和通用范围特定范围的扰动只对单张输入有效语境范围针对一类场景比如雪天或雨天的交通标志通用范围则是找一个固定扰动让整个数据集的大部分样本都被骗过去。攻击方式维度区分白盒与黑盒而黑盒又被细分成输出透明、标签透明、查询限制、完整黑盒。攻击策略维度按优化手段分为一阶方法、二阶方法、进化与随机抽样。四个维度组合起来攻击的主体形态才真正清晰。2.2 白盒不一定是梯度齐全黑盒也有信息差常见误用是把“白盒攻击”等同于“有梯度”把“黑盒攻击”等同于“零信息”。综述里的划分要细得多输出透明的黑盒攻击能拿到完整或部分分类概率标签透明的黑盒攻击只能拿到最终标签查询限制还要额外控制查询次数。这就决定了攻击者能利用什么信息也决定了防御方该把什么信息藏起来。举个例子CW 攻击依赖完整梯度信息适合白盒场景而迁移攻击只需要另一个替代模型的梯度属于输出透明或标签透明场景的常用路线。如果生产环境的推理接口只返回 top-1 标签且加了限流那查询驱动的黑盒攻击成本会明显上升工程上应该优先考虑替代模型迁移。2.3 干扰度量、代表算法与适用场景下表把综述中提到的主要算法按分类维度做了归拢方便按场景检索。分类维度子类代表算法一句话特征期望输出无特定目标FGSM、I-FGSM、DeepFool、Universal、Distributed只求错不求错成什么期望输出特定目标L-BFGS、JSMA、CW、Adversarial Patch、DDN指定目标类别欺骗精度更高扰动范围特定FGSM、CW逐样本生成扰动扰动范围语境Functional Adversarial按颜色等函数统一变换整体均匀变化更难察觉扰动范围通用Universal Adversarial一个扰动骗整个数据分布的大部分样本攻击方式白盒FGSM、I-FGSM、JSMA、CW直接利用模型内部梯度或决策边界攻击方式黑盒MI-FGSM、Curls Whey靠替代模型或查询反馈逼近目标这张表帮我做模型鲁棒性评估时省了不少时间先确定业务场景是单张图还是批量图、攻击者能拿到什么信息再挑对应算法。特别要注意“语境范围”和“通用范围”经常被混为一谈。语境扰动只针对特定上下文影响某一类场景通用扰动则威胁整个模型的可靠性两者上线后的安全含义完全不同。3. 白盒攻击族落地FGSM、I-FGSM、PGD 与 CW 的 MNIST 复现综述对白盒攻击的公式推演着墨最多。虽然算法一个接一个但落到 MNIST 复现时核心动作只有三件事算损失函数对输入的梯度、沿梯度方向走一步、把结果裁剪回合法像素范围。我一般用 PyTorch 跑 MNIST 验证训练一个轻量卷积网络再在上面依次叠加攻击函数。3.1 FGSM 公式到 PyTorch一次符号梯度更新FGSM 的更新式是 X_adversarial X ε · sign(∇_x J(X, θ, y_true))。这里的 J 是交叉熵损失sign 表示只取梯度方向不取大小。对应 PyTorch 代码import torch import torch.nn.functional as F def fgsm_attack(model, x, y, eps8 / 255): # x 取值 [0,1]分离出可导副本避免污染原始输入 x_adv x.clone().detach().requires_grad_(True) logits model(x_adv) loss F.cross_entropy(logits, y) model.zero_grad() loss.backward() gradient x_adv.grad.data # 沿损失增大方向走一步符号函数对应论文式(6) x_adv x_adv eps * gradient.sign() # 像素约束回合法区间 x_adv torch.clamp(x_adv, 0, 1) return x_adv这段代码与综述里的式6一一对应。requires_grad_ 让输入进入计算图cross_entropy 就是损失函数 Jsign() 取梯度符号。与常规梯度下降相反这里要让损失增大所以对梯度方向做加法而不是减法。clamp 负责把像素按式3约束回 [0,1]^m对应原论文里的 X δ ∈ [0,1]^m。eps 是扰动强度MNIST 上常用 0.05 到 0.3eps 越大攻击成功率越高超过 0.3 后人眼就能明显看出噪声。注意如果输入已经是 ImageNet 那样的 0-255 像素需要把 eps 换算到 255 域再参与计算。3.2 I-FGSM小步迭代与双重 Clip 约束FGSM 一步到位步长偏大得到的扰动往往不在最优方向上。Kurakin 在综述中被引用的工作是把 FGSM 拆成 T 次小步迭代每一步用较小的 alpha并且每步都做裁剪。def ifgsm_attack(model, x, y, eps8 / 255, alpha2 / 255, iters10): x_adv x.clone().detach() for _ in range(iters): x_adv x_adv.clone().detach().requires_grad_(True) loss F.cross_entropy(model(x_adv), y) model.zero_grad() loss.backward() with torch.no_grad(): x_adv x_adv alpha * x_adv.grad.sign() # 相对原始图的扰动裁剪防止总扰动超过 eps delta torch.clamp(x_adv - x, -eps, eps) # 绝对像素值裁剪保证在合法图像范围 x_adv torch.clamp(x delta, 0, 1) return x_adv代码里有两个裁剪先 clip 相对原始图像的 delta确保累计扰动不超过 eps再 clip 绝对像素范围。容易被漏掉的是相对裁剪漏掉以后迭代会越走越远最后得到的图像在视觉上已经不像原始输入。参数设置上综述引用原文时步长 alpha 取 1 个像素255 域迭代次数 T min(ε 4, 1.25ε)。转成 [0,1] 域时我一般用 eps8/255、alpha2/255、iters10。这个组合在 MNIST 上扰动不太明显又能把准确率压下去一截。3.3 PGD随机起点与最坏情况下限综述没有单列 PGD但在介绍分布式攻击 DAA 时明确指出当超参数 c1 或粒子数 M1 时该方法退化为 PGD。实现上 PGD 等于 I-FGSM 加一个随机起点初始化时在 ε 球内随机挑一个扰动再做迭代投影。def pgd_attack(model, x, y, eps8 / 255, alpha2 / 255, iters40, random_startTrue): delta torch.zeros_like(x) if random_start: delta torch.empty_like(x).uniform_(-eps, eps) x_adv torch.clamp(x delta, 0, 1) for _ in range(iters): x_adv x_adv.clone().detach().requires_grad_(True) loss F.cross_entropy(model(x_adv), y) model.zero_grad() loss.backward() with torch.no_grad(): x_adv x_adv alpha * x_adv.grad.sign() delta torch.clamp(x_adv - x, -eps, eps) x_adv torch.clamp(x delta, 0, 1) return x_adv与 I-FGSM 相比随机起点的意义是逃离局部最优。综述里讨论 Madry 等人的观点时提到在 100 个随机起点处找到的交叉熵损失局部最大值并不相同这也说明只用单次攻击评估模型会高估鲁棒性。评估时建议跑 3 到 5 个随机种子取最低准确率作为模型脆弱性下限。3.4 CW变量替换绕过不可微约束CW 攻击在综述里被定义为对 L-BFGS、FGSM、JSMA 的总结改进核心是把“分类为目标类别 t”这个硬约束转成优化目标的一部分min ‖δ‖_p c·f(Xδ)。f 函数要满足当且仅当模型预测为目标类时 f ≤ 0。常见的 L2 实现会用 tanh 变量替换让对抗样本自动落在合法像素区间从而避免反复裁剪带来的梯度截断。def cw_l2_attack(model, x, y_target, c1.0, lr0.01, iters200): # 标准初始化用 arctanh 反解 w使 adv 一开始贴近原图 w torch.arctanh((2 * x - 1) * 0.999).clone().detach().requires_grad_(True) optimizer torch.optim.Adam([w], lrlr) for _ in range(iters): # tanh 将 adv 限定在 [0,1] adv 0.5 * (torch.tanh(w) 1) delta adv - x logits model(adv) num_classes logits.size(1) mask F.one_hot(y_target, num_classes).bool() target_logit logits.gather(1, y_target.unsqueeze(1)).squeeze(1) other_logit logits.masked_fill(mask, -1e9).max(dim1).values # targeted 版本的 margin loss f_loss torch.clamp(other_logit - target_logit, min0).sum() loss delta.pow(2).sum() c * f_loss optimizer.zero_grad() loss.backward() optimizer.step() return 0.5 * (torch.tanh(w) 1)这段代码里最关键的是变量替换w 是自由可导参数adv 通过 tanh 映射到 [0,1]这样默认满足像素范围约束优化器可以放心更新。f_loss 计算的是“非目标类的最大 logit 减去目标类 logit”大于 0 说明还没有攻破这个 margin 会持续压向负数。c 是平衡距离和欺骗成功率权重c 太小则攻击很难成功c 太大则扰动范数偏大实际调参时通常从 0.1 到 10 扫一轮。3.5 MNIST 验证的工程注意点复现时先把训练集和测试集归一化到 [0,1]训练一个结构简单的卷积网络。MNIST 用 LeNet-5 级别的容量就够了优化器选 Adam学习率 1e-3训练 5 轮左右。注意固定随机种子否则每次评估的 clean accuracy 和对抗准确率都会漂移。算法eps[0,1] 域alphaiters复现建议FGSM0.1 ~ 0.3-1快速跑通流程I-FGSM0.2 ~ 0.3eps / iters10 ~ 20相对裁剪不要漏PGD0.2 ~ 0.3eps / iters40 ~ 100随机起点 3 个以上CW不设 epslr0.01200 起攻破慢但扰动小还需要强调一点原论文给出的 MNIST 验证结果只表明各算法的相对优劣具体攻击成功率会随模型结构、训练状态、随机种子变化。直接照搬论文数字没有意义正确做法是把自己的模型扛过一轮 FGSM 和 PGD 之后再下结论不要拿综述里的实验数据当作自己模型的鲁棒性证据。4. 黑盒与可迁移攻击替代模型、MI-FGSM 与集成策略真实生产环境的模型通常只暴露推理接口拿不到权重这就进入黑盒攻击范畴。综述把黑盒分成输出透明、标签透明、查询限制、完整黑盒四个档位落到工程实践时最常用的路线是替代模型迁移而不是在线查询目标模型。4.1 黑盒攻击的关键是迁移性迁移攻击的思路很直接先用公开数据训练一个替代模型或者在 ImageNet 预训练模型里挑一个结构相近的对替代模型做白盒攻击再把生成的对抗样本投到目标模型上。对抗样本能跨模型迁移本质是因为不同深度模型在相近任务上拟合到的决策边界存在大面积重叠攻击替代模型的扰动方向往往也在目标模型的脆弱方向上。所以替代模型的训练质量直接决定迁移成功率。常见的做法是让替代模型的训练数据尽量贴近目标模型的任务分布哪怕只有无标签数据也可以。遇到过替代模型与目标模型结构差异较大的情况可以加一层随机缩放和 padding让对抗样本对几何变换更不敏感迁移成功率通常能明显提升。4.2 MI-FGSM动量机制抑制局部震荡FGSM 和 I-FGSM 直接沿当前梯度方向更新黑盒场景下替代模型的梯度噪声比较大容易在决策边界附近来回震荡。Dong 等人的 MI-FGSM 把动量引入迭代累积历史梯度方向让更新更平滑对抗样本的可迁移性也更强。综述里的更新式是 g_{t1} μ·g_t ∇_x J / ||∇_x J||_1对应实现为def mi_fgsm_attack(model, x, y, eps8 / 255, iters10, mu1.0): alpha eps / iters g torch.zeros_like(x) x_adv x.clone().detach() for _ in range(iters): x_adv x_adv.clone().detach().requires_grad_(True) loss F.cross_entropy(model(x_adv), y) model.zero_grad() loss.backward() grad x_adv.grad.data # 动量累积前先做 L1 归一化对应论文式(15) grad grad / grad.abs().mean(dim(1, 2, 3), keepdimTrue) g mu * g grad with torch.no_grad(): x_adv x_adv alpha * g.sign() delta torch.clamp(x_adv - x, -eps, eps) x_adv torch.clamp(x delta, 0, 1) return x_adv对照公式看grad 归一化那一步是稳定动量的关键直接把原始梯度塞进累积会放大异常像素点的影响。mu 是衰减因子综述里取 1表示历史梯度完全保留alpha 被设定为 eps / T保证总迭代步长不超限。实际使用中mu 从 0.9 到 1.0 都可以试数值越小动量效果越弱但有时反而对特定目标模型更有效。4.3 集成攻击用多模型投票降低过拟合单个替代模型与目标模型偏差较大时迁移效果不稳定。常见做法是取多个模型的 logits 平均把平均输出当成一个集成模型再做迭代攻击。集成攻击生成的对抗样本不容易过拟合到某一个替代模型上跨模型成功率会稳定不少。选集成成员时不要全选同系列模型比如全是 ResNet 就只覆盖一类决策边界。我一般选一个卷积网络、一个带注意力结构的模型再加一个训练轮次不同的同结构副本三者差异足够大迁移效果才明显。综述后面提到的 Curls Whey 方法本质上也是替代模型加双向迭代逼近目标其中的 Whcy 噪声压缩和二进制搜索都是为了在保持攻击成功率的同时减小扰动范数工程上可以当作迁移攻击的增强技巧来用。4.4 查询驱动的黑盒攻击边界如果目标系统只返回标签不返回概率可以用边界攻击或 NES 这类查询驱动方法但综述把这块归在标签透明和查询限制维度没有展开推导。这里要提醒的是查询次数上限直接决定你能不能采用边界二分搜索。生产环境中的推理接口通常有频率限制频繁试探会触发风控导致 IP 被封或账号受限。先评估目标接口单次查询成本和限频阈值再决定是走迁移攻击还是在线查询路线比直接套攻击算法更重要。5. 鲁棒性评估技巧对抗训练与 eps 扫描生成对抗样本不是为了攻击而攻击最终目的是暴露模型脆弱性并推动防御。综述在最后讨论了应用和前景落到实际工作中对抗训练是最直接的闭环手段。5.1 对抗训练混合损失对抗训练的思路是把对抗样本加进训练集让模型在更新时同时看到干净样本和对抗样本。简单实现是在每个训练步先对当前 batch 做一次快速攻击再把两种损失加起来def adv_train_step(model, optimizer, x, y, eps8 / 255): model.train() optimizer.zero_grad() x_adv ifgsm_attack(model, x, y, epseps, alpha2 / 255, iters10) loss F.cross_entropy(model(x_adv), y) F.cross_entropy(model(x), y) loss.backward() optimizer.step()这里把干净样本和对抗样本的损失相加是为了避免模型只对对抗样本过拟合导致 clean accuracy 明显下降。训练时生成的对抗样本每个 batch 都在变相当于隐式做了数据增强模型对扰动的容忍度会整体抬升。5.2 eps 扫描得到鲁棒性曲线不要只看单个攻击成功率应该把 eps 从 0 开始逐步增大记录每个 eps 下的测试准确率画出一条“准确率-扰动强度”曲线。eps0 时就是模型正常精度曲线下降越慢模型越稳。如果 eps 只加了 0.1准确率就从 99% 跌到 30% 以下说明这个模型离上线标准还差很远。扫描代码很短但每次评估都要固定测试集和随机种子。5.3 跨域迁移要重新标定参数MNIST 上的经验不能直接搬到其他任务。医疗图像分析、自然语言处理这些领域输入维度、特征分布、类别语义完全不同。比如医学图像通常是 16 位灰度像素范围不是 [0,255]直接套用 255 域推导的 epsilon 会失真NLP 的对抗样本修改的是 token 而不是连续像素更不能复用图像攻击代码。做法是把扰动强度按像素百分比来定义先在验证集上扫一轮 eps找到视觉不可察觉和攻击成功率之间的拐点再用这个拐点作为安全评估基准。建议你在自己的模型上先把 eps 从 0 到 0.5 完整扫一遍记录每个点的准确率和对应的对抗样本可视化结果再结合业务能接受的最低准确率决定要不要引入对抗训练。本文还有配套的精品资源点击获取