在侦探事务所的暗房里,你手握一支极细的“幽灵笔”。桌上摆着一张嫌疑人的照片——它就是机器学习的输入样本;旁边坐着一位眼睛极尖的侦探——它就是待攻击的深度模型。你的任务很简单:在照片上做一点手脚,让侦探把照片里的人认成另一个人。可侦探太警觉,改动稍大就会暴露。于是你只能采取最笨也最管用的办法:一笔一笔地描,每笔都细到肉眼几乎察觉不到;每描几笔就退后检查,一旦发现改动超出了“肉眼看不出来”的边界,就用橡皮擦把越界的痕迹擦掉,拉回界限以内。如此反复几十次后,照片在人眼看来几乎原封未动,侦探却会斩钉截铁地报出错误的名字。 这个“一笔一笔描、超界就拉回”的过程,就是对抗攻击世界里大名鼎鼎的 PGD(Projected Gradient Descent,投影梯度下降)。它把“欺骗模型”变成一个有纪律的数学问题:每次沿让模型犯错的梯度方向走一小步,一旦走出允许的扰动范围,就把样本投影回边界内,再继续走,直到骗局成功。PGD 是评估模型鲁棒性最常用的基准攻击方法,任何号称安全的模型,在 PGD 面前都要先挨一轮狂风暴雨般的测试。
学透 PGD 是 AI 安全工程师的必修课:它既是发现模型弱点的“放大镜”,也是衡量防御能力的“标尺”,让你设计的模型在真实攻防中经得起检验。
目录
可视化演示
\u5c55\u793aPGD\u6295\u5f71\u68af\u5ea6\u4e0b\u964d\u5bf9\u6297\u653b\u51fb\u7684\u8fed\u4ee3\u8fc7\u7a0b\uff1a\u5728\u68af\u5ea6\u65b9\u5411\u5c0f\u6b65\u524d\u8fdb\u540e\u6295\u5f71\u56de\u03b5\u7403\uff0c\u4ee5\u53ca\u5bf9\u6297\u635f\u5931\u4e0a\u5347\u7684\u6548\u679c\u3002
本节配有交互动效(进入视口后自动加载,约 3KB)。
\u653b\u51fb\u8f68\u8ff9\u6a21\u5f0f \u539f\u59cb\u8f93\u5165x\u5728\u68af\u5ea6\u65b9\u5411\u8fed\u4ee3\u524d\u8fdb \u6bcf\u6b65\u540e\u6295\u5f71\u56de\u03b5\u7403\u5185(\u865a\u7ebf\u5706)\u4fdd\u8bc1\u6270\u52a8\u5fae\u5c0f x_{t+1} = \u03a0_\u03b5(x_t + \u03b1\u00b7sign(\u2207_x L))
攻击原理:小步快跑,反复试探
暗房里的幽灵笔有一个天赋:每次落笔前,它都能感觉到侦探的目光会往哪儿躲——这种感觉对应到数学,就是模型损失对输入求得的梯度。你绝不能一笔画出一个惊叹号,那样会立刻暴露;你只能顺着梯度的方向蹭一小步,停住,重新感受目光,再蹭一小步。
把“蹭一小步”重复很多次,就是 PGD 的第一层灵魂:小步迭代,积小错为大错。与此同时,每一步都必须被限制在以原始照片为中心、半径 ε 的“允许改动区”内;一旦走过头,就立刻用橡皮擦把越界部分擦掉、拉回边界,这就是投影。两件事写成递推式,就是 PGD 的核心更新公式:
正是这股“蹭一步、擦一下、再蹭”的笨功夫,让每个微不可察的改动像滴水穿石一样,最终在侦探眼皮底下换掉了照片里的关键信息。
- \(x^{t}\)(当前照片)第 \(t\) 轮修改后的样本,\(x^{0}\) 是未经过任何修改的原始输入。
- \(\Pi_{x+\mathcal{S}}\)(投影算子)把可能越界的样本拉回以原始样本为球心的允许区域 \(\mathcal{S}\) 内。
- \(\alpha \cdot \operatorname{sign}(\nabla_x J(x^t, y))\)(梯度符号步)只朝损失增大的方向迈进一小步,步长为 \(\alpha\),稳定且不易被个别极端梯度带偏。
- 循环迭代(多步累积)把上面的更新式重复 \(T\) 次,每一次的微小改动像水滴一样累积,最终形成有效的攻击。
核心公式:投影与最大损失
幽灵笔配的橡皮擦不是普通橡皮——它总能准确地把越界痕迹擦回“合法范围”的最边缘,不偏不倚。这个动作在数学上叫“投影算子”。给定一个点 \(z\),它在允许区域 \(\mathcal{S}\) 上的投影,就是 \(\mathcal{S}\) 里离 \(z\) 最近的的那个点:
有了投影,攻击者还要回答“往哪走”。答案是:往让模型最困惑的方向走。于是 PGD 的本质,是在扰动范围内求解一个“最大损失”问题:
- \(\arg\min\)(找最近点)在允许区域 \(\mathcal{S}\) 中找出距 \(z\) 最近的那个样本,作为“拉回”的目的地。
- \(\lVert \cdot \rVert_2\)(欧氏距离)衡量两张图片像素差别的标准尺子,距离越小代表改动越轻微。
- \(\lVert \delta \rVert_\infty \le \varepsilon\)(无穷范数约束)任何像素的改动量都不超过 \(\varepsilon\),这是“人眼看不出来”的数学表达。
- \(\max\)(最大化损失)在允许范围内寻找让模型损失最大的扰动,也就是让模型最“懵”的方向。
超参设置:笔锋的粗细与力道
幽灵笔的笔锋不是固定的:笔尖细,画出的痕迹就隐蔽,但需要更多笔才能见效;笔尖粗,见效快,却容易暴露。对应到 PGD,就是三个关键旋钮:扰动上限 \(\varepsilon\)、步长 \(\alpha\)、迭代次数 \(T\)。
\(\varepsilon\) 是“暴露线”。它定义了每个像素允许被改动的最大幅度,直接决定对抗样本能否瞒过人的眼睛:
实际使用中,MNIST 常用 \(\varepsilon = 0.3\)、\(\alpha = 0.01\)、\(T = 40\);CIFAR-10 常用 \(\varepsilon = 0.03\)、\(\alpha = 0.003\)、\(T = 20\)。攻击目标还可以是“无目标”(只要模型认错)或“有目标”(骗它认成指定类别),后者难度更高。
- \(\delta\)(扰动)原始样本加上它就成了对抗样本,\(x^{\text{adv}} = x + \delta\)。
- \(\lVert \cdot \rVert_\infty\)(无穷范数)取所有像素中改动最大的那个值,它代表“最扎眼”的一笔。
- \(\varepsilon\)(扰动上限)允许的最大“可见度”,一般取 0.1 到 0.3 之间(对应像素值 0-1 归一化)。
- 步长与迭代次数的关系每步走 \(\alpha\),走 \(T\) 步,总探索范围应覆盖整个 \(\varepsilon\) 球,因此常用 \(\alpha = \varepsilon / T\) 或分段衰减。
与FGSM对比:单笔与多笔
暗房里还有一位急性子学徒,他的笔法叫“一笔流”:拿起幽灵笔,使出全身力气在照片上重重画一笔,然后立刻收工。这个方法就是 FGSM(Fast Gradient Sign Method,快速梯度符号法)。它的优点是快,缺点是精度差——好比一眼判断方向但不看路况,容易冲过头或走错路。
FGSM 的公式只有一步:
这一笔和多笔的差别,就像飞镖:FGSM 闭眼猛掷一镖,PGD 睁开眼一镖一镖校准,最终总能更靠近靶心——所以 PGD 被公认为“最强的一阶攻击”。
- \(x^{\text{adv}}\)(对抗样本)最终交付给模型的“伪造照片”。
- \(\varepsilon\)(扰动上限)FGSM 中它同时扮演“步长”和“总预算”,一步到顶。
- \(\operatorname{sign}(\nabla_x J)\)(梯度符号)只保留梯度的正负方向,让每个像素的改动幅度整齐划一。
- 多步化与随机起步PGD 在 FGSM 基础上增加迭代与投影,并常在起点加随机扰动,避免陷入平庸的局部最优。
防御策略:侦探的封堵
侦探也不是吃素的。在被幽灵笔捉弄很多次之后,他开始主动做“对抗训练”:把那些被改过的照片一张张收集起来,反复辨认,逼迫自己记住哪些微小的“笔触”是伪造的痕迹。这样一来,下次再有人耍同样的把戏,他一眼就能识破。
对抗训练的数学表达是一个“最小最大”博弈:防御方要让模型在最坏扰动下的损失也尽量小:
除对抗训练外,还有输入预处理(JPEG 压缩、随机去噪)、梯度掩蔽等防御手段。但要警惕:梯度掩蔽会让 PGD 一时失效,却往往只是“把梯度藏起来”,并非真正鲁棒,容易在更强攻击下现出原形。
- \(\min_{\theta}\)(训练模型)寻找一组模型参数,让整体损失最小,即“把侦探练得更敏锐”。
- \(\max_{\lVert \delta \rVert_\infty \le \varepsilon}\)(最坏扰动)对每个样本,先用 PGD 找到让当前模型损失最大的扰动,再拿这个对抗样本训练。
- \(\mathbb{E}_{(x,y)\sim\mathcal{D}}\)(期望)对所有训练样本的损失取平均,代表“整体鲁棒性”而不是个例。
应用场景:实战破案
幽灵笔不是实验室里的玩具,它更像安全测评界的标准“闹钟”。当一家公司宣称自己的模型“绝对安全”时,安全团队会默默掏出 PGD,对模型进行一轮系统性的压力测试。攻击能不能得手,用攻击成功率(ASR)来衡量:
在金融风控、自动驾驶、内容审核等场景中,安全团队用 PGD 生成“略带恶意”的样本,在模型上线前做红队演练;学术圈的鲁棒性评测也以 PGD 攻击结果作为论文中必须报告的指标。
- \(f(x_i^{\text{adv}})\)(模型的预测)把生成好的对抗样本送给模型,看它输出哪个类别。
- \(\mathbb{1}[\cdot]\)(指示函数)条件成立记 1,不成立记 0,用来统计“被骗的个数”。
- \(\frac{1}{N}\sum_{i=1}^{N}\)(平均)在 \(N\) 个测试样本上求平均,得到攻击成功率的点估计。
局限挑战:笔法的天花板
幽灵笔再妙,也有力不能及之处。最直接的问题是慢:每走一步,都要对模型做一次前向计算和反向传播。如果迭代 \(T\) 次,总开销就是单次推理的很多倍,具体可以写成计算复杂度:
除了算力瓶颈,PGD 的黑盒迁移性有限——在替代模型上生成的对抗样本,未必能稳定骗过目标模型;防御方也会不断升级,用动态输入变换或更强对抗训练来封堵。更根本的是,模型鲁棒性与干净样本精度之间存在此消彼长的权衡,把 ε 调得过大,模型会“不敢判断”,正常准确率也跟着掉下来。
所以在真实攻防中,PGD 是一把双刃剑:它帮你发现弱点,也提醒你——没有一劳永逸的安全,只有不断加固的防线。
- \(T\)(迭代次数)PGD 每轮都需要重新计算梯度并投影,\(T\) 越大越接近最优攻击,成本也越高。
- \(C\)(单轮计算量)一次前向传递加一次反向传播的开销,对于深度网络可能高达数百万次浮点运算。
- \(O(\cdot)\)(复杂度记号)表示总成本随 \(T\) 线性上涨,是“快不了”的根本原因。
AI 应用场景
金融风控模型的鲁棒性测试
阶段:模型评估与安全测试
解决的问题:传统模型测试只用干净样本,无法暴露恶意申请者在极端情况下的绕过风险。PGD 能对每个特征施加“几乎不可察觉”的扰动,系统性地检验风控模型是否会被微调后的申请数据骗过。
具体案例:某银行信贷审批模型基于收入、负债等特征做自动化决策。安全团队用 PGD 对历史客户的申请特征施加 \(\varepsilon=0.05\) 的扰动,发现原本应该“拒绝”的样本中,有 7% 被改判为“通过”。团队据此在模型上线前补做对抗训练,堵住了漏洞。
自动驾驶视觉系统的红队演练
阶段:红队演练与鲁棒性评估
解决的问题:自动驾驶公司需要知道视觉模型对路面干扰的敏感度。物理世界无法刻意制造大量极端天气或贴纸攻击,但 PGD 可以在数字空间快速生成标准化的对抗压力测试样本。
具体案例:在停车标志图像上施加 PGD 扰动(\(\varepsilon=0.03\)),再把对抗样本输入目标检测模型,测试其是否会将“Stop Sign”误识别为“限速 40”。测试结果用于决定是否引入对抗训练或输入预处理模块。
学术论文鲁棒性宣称的复现验证
阶段:算法审计与第三方评测
解决的问题:许多论文声称模型“免疫对抗攻击”,但这类宣称往往只在特定攻击下成立。审稿人或评测机构需要用公认的强攻击基准(PGD)来独立复现,验证鲁棒性数字是否真实可靠。
具体案例:研究团队投稿时报告在某防御模型上 PGD 攻击成功率仅 2%。第三方评测人员用相同的 \(\varepsilon\) 和迭代次数重新攻击,发现成功率高达 15%,进而怀疑原论文存在梯度掩蔽,要求作者补充实验。
Python 演示
import torchimport torch.nn as nn def pgd_attack(model, x, y, eps=0.3, alpha=0.05, iters=30, random_start=True): """PGD 对抗攻击:小步沿梯度方向走,再将扰动投影回 eps 球内。""" x_adv = x.clone().detach() if random_start: noise = torch.empty_like(x_adv).uniform_(-eps, eps) x_adv = x_adv + noise for _ in range(iters): x_adv = x_adv.detach().clone().requires_grad_(True) loss = nn.CrossEntropyLoss()(model(x_adv), y) model.zero_grad() loss.backward() grad = x_adv.grad x_adv = x_adv.detach() + alpha * grad.sign() delta = torch.clamp(x_adv - x, -eps, eps) x_adv = x + delta return x_adv.detach() class ToyModel(nn.Module): """一个线性二分类器:x1+x2 > 0.2 判定为类别 0。""" def __init__(self): super().__init__() self.fc = nn.Linear(2, 2) with torch.no_grad(): self.fc.weight.copy_(torch.tensor([[1.0, 1.0], [-1.0, -1.0]])) self.fc.bias.copy_(torch.tensor([-0.2, 0.2])) def forward(self, x): return self.fc(x) def main(): torch.manual_seed(0) model = ToyModel() model.eval() x = torch.tensor([[0.3, 0.4]], dtype=torch.float32) y = torch.tensor([0]) with torch.no_grad(): pred_before = model(x).argmax(dim=1).item() x_adv = pgd_attack(model, x, y, eps=0.3, alpha=0.05, iters=30) with torch.no_grad(): pred_after = model(x_adv).argmax(dim=1).item() print("原始预测类别:", pred_before) print("攻击后预测类别:", pred_after) print("攻击成功:", pred_before != pred_after) print("扰动最大幅度:", (x_adv - x).abs().max().item()) if __name__ == "__main__": main()
noise = torch.empty_like(x_adv).uniform_(-eps, eps)在 ε 球内随机初始化扰动,让攻击起点不固定,提升稳定性。x_adv = x_adv.detach().clone().requires_grad_(True)每轮迭代都重建一个需要梯度的叶子张量,避免计算图跨轮累积。loss = nn.CrossEntropyLoss()(model(x_adv), y)用交叉熵损失衡量模型“错得多离谱”。grad = x_adv.grad拿到损失对输入的梯度,即“侦探目光最容易被带偏的方向”。x_adv = x_adv.detach() + alpha * grad.sign()沿梯度符号方向走一小步,步长为 α。delta = torch.clamp(x_adv - x, -eps, eps)投影步骤的核心:把超出 ε 的扰动裁剪回来。x_adv = x + delta把合法扰动加回原始样本,得到这一轮的对抗样本。
常见误区
误区PGD 迭代了这么多步,照片肯定被改得乱七八糟,人眼一眼就能看出来。
正解对抗样本是否可见由 \(\varepsilon\) 决定,而不是迭代次数。只要 \(\varepsilon\) 足够小(比如像素扰动不超过 0.1),每一次微调就像幽灵笔细到看不见的笔触,人眼很难察觉,但模型已经认错。迭代多只是让扰动在合法范围内走得更聪明,并不会自动放大改动幅度。
为何常见:人们容易把“迭代多”和“改动大”混为一谈,忽略了投影操作始终把扰动按在 \(\varepsilon\) 球内的关键机制。
误区攻击时必须拿到目标模型的梯度,否则 PGD 根本没法启动,黑盒下它完全没用。
正解PGD 可以先用一个本地替代模型计算梯度、生成对抗样本,再凭借跨模型迁移性去攻击黑盒目标。黑盒场景下 PGD 不是最强的,但远非“毫无用处”,它仍然是衡量迁移攻击效果的重要基准。
为何常见:初学者把 PGD 的梯度计算误解为必须在目标模型上完成,忽视了“替代模型 + 迁移攻击”这一工程中常见的套路。
误区把 ε 调到很大,PGD 就一定天下无敌,什么模型都能骗过。
正解ε 一旦过大,扰动就不再隐蔽,防御方可以轻易检测出可疑样本并拒绝服务;同时超大扰动可能让样本跨过决策边界后又绕回正确类别。攻击者追求的是“隐蔽且有效”的平衡,而不是一味莽撞。
为何常见:直观上觉得“力气越大越好”,但对抗攻击是限制作战,敏捷和隐蔽往往比蛮力更重要。
误区FGSM 一步生成,PGD 循环生成,代码结构完全不一样,它们肯定没关系。
正解PGD 本质上就是“FGSM 的迭代版”:把 FGSM 的单步更新公式重复执行 T 次,每一步重新计算梯度并投影回 ε 球。当迭代次数 T=1 时,PGD 就退化为 FGSM。
为何常见:循环和单步在代码形态上差异显著,人们容易忽略它们共享同一个数学骨架和符号梯度机制。
误区只要用 PGD 生成对抗样本做过对抗训练,模型就彻底安全了,以后所有攻击都可以无视。
正解对抗训练只是把“模型在特定 ε 强度下的鲁棒性”提升,但更强的扰动预算、自适应攻击或新的攻击算法依然可能攻破它。安全是持续的军备竞赛,没有一劳永逸的免疫。
为何常见:把“防御缓解”误当成“绝对免疫”,忽视了对抗攻防的本质是动态博弈。
面试题
Q1. PGD 中投影算子 \(\Pi_{x+\mathcal{S}}\) 的核心作用是什么?
- A.加快模型训练收敛速度
- B.将扰动限制在合法区域内← 正确答案
- C.放大梯度的数值大小
- D.把图像转为灰度格式
答案:B。投影算子负责把越界的样本拉回以原始样本为中心的 ε 球内,保证扰动始终符合“人眼看不出来”的约束。A 说的是训练优化,C 与符号梯度无关,D 是图像预处理,都不属于投影的职责。
Q2. PGD 每一步更新使用 \(\operatorname{sign}(\nabla_x J)\) 而不是原始梯度的主要原因是什么?
- A.让每一步移动距离稳定可控← 正确答案
- B.降低梯度的计算开销
- C.让模型参数变得更精简
- D.随机化攻击的起始方向
答案:A。符号函数只保留梯度方向,把每一步的改动幅度统一为 α,避免被个别极端梯度带偏。它并不减少计算量、不改变模型参数,也不负责随机初始化。
Q3. 在 L∞ 范数约束下,\(\varepsilon\) 的数学含义是什么?
- A.允许每个像素改动的最大值← 正确答案
- B.所有像素改动总量上限
- C.梯度步长的累积次数
- D.模型输出维度的数量
答案:A。L∞ 范数取所有像素改动中的最大值,所以 \(\lVert \delta \rVert_\infty \le \varepsilon\) 表示“任何一个像素的改动都不超过 ε”。B 对应 L1 范数,C 是迭代次数的概念,D 与输入约束无关。
Q4. FGSM 与 PGD 的关系是什么?
- A.PGD 是 FGSM 的迭代版本← 正确答案
- B.FGSM 是 PGD 的相反算法
- C.两者没有直接数学联系
- D.两者只能用于不同网络
答案:A。PGD 把 FGSM 的单步梯度符号更新重复 T 次,并在每次更新后加上投影操作。当 T=1 时,PGD 就退化为 FGSM,所以二者是“多步版”与“单步版”的关系。
Q5. 以下哪些是 PGD 攻击中需要设置的超参数?
- A.扰动上限 ε 的取值← 正确答案
- B.每步更新的步长 α← 正确答案
- C.攻击迭代的次数 T← 正确答案
- D.训练集的数据总量
答案:ABC。ε、α、T 分别控制扰动预算、每步幅度和迭代轮数,是 PGD 最核心的三个超参数。训练集数据总量属于模型训练范畴,与攻击算法的超参设置无关。
Q6. 对抗训练的数学本质是求解什么?
- A.最小化干净样本的损失
- B.求解最小最大鞍点问题← 正确答案
- C.只最大化模型参数
- D.消除所有梯度噪声
答案:B。对抗训练的目标是 \(\min_\theta \max_\delta J(\theta, x+\delta, y)\),外层最小化模型参数,内层用 PGD 找最坏扰动,构成一个 min-max 鞍点问题。只最小化干净样本损失或只做参数迁移都不符合定义。
Q7. 关于 PGD 攻击的随机起点,下列说法正确的是?
- A.在 ε 球内随机初始化扰动← 正确答案
- B.提升攻击的稳定性和效果← 正确答案
- C.每轮攻击会略微增加开销← 正确答案
- D.可完全替代投影操作
答案:ABC。随机起点让攻击从 ε 球内不同位置出发,避免陷入局部最优,从而提高攻击稳定性;它需要额外生成一次随机扰动,因此增加少量开销。但随机初始化不能替代投影,投影依然是每步之后必须执行的约束操作。
Q8. 对抗攻击成功的标志是什么?
- A.模型预测结果明显改变← 正确答案
- B.模型训练速度明显加快
- C.模型权重变得接近零值
- D.输入图像尺寸大幅变小
答案:A。攻击成功的定义是:模型对加了扰动的对抗样本的预测,与对原始干净样本的预测不一致。训练提速、权重归零、图像缩放都是无关现象。
Q9. 为什么 PGD 被称为“最强的一阶攻击”之一?
- A.它只使用一阶梯度信息← 正确答案
- B.它需要计算二阶海森矩阵
- C.它能防御任何对抗样本
- D.它完全不需要模型梯度
答案:A。PGD 只依赖损失函数对输入的一阶导数(梯度),不涉及二阶导数,却在多种扰动预算下接近最优攻击效果,因此被称为“最强的一阶攻击”。它不能防御攻击,反而是用来发起攻击的。
Q10. 防御 PGD 攻击的有效手段包括哪些?
- A.对抗训练增强鲁棒性← 正确答案
- B.推理阶段输入去噪← 正确答案
- C.使用随机预处理策略← 正确答案
- D.无限扩大模型参数量
答案:ABC。对抗训练引入 PGD 对抗样本增强模型鲁棒性;输入去噪和随机预处理能破坏攻击者对梯度的精确估计,都是常见防御思路。盲目扩大参数量既不解决本质脆弱性,还可能导致过拟合,不属于有效防御。