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