第 7 天日志:对抗样本攻击(四)对抗训练
| 项目 | 内容 |
|---|---|
| 模块 | 模块二 对抗样本攻击 |
| 进度 | 第 7 天 / 共 10 天 |
| 数据集 | MNIST |
| 模型 | MNISTCNN(多种对抗训练变体) |
一、任务目标
- 理解对抗训练的理论基础与 Min-Max 优化框架;
- 掌握 PGD 对抗训练与 TRADES 正则化训练及超参数选择;
- 评估对抗训练防御效果,分析鲁棒性与清洁精度的权衡;
- 用自适应攻击(更强 PGD、C&W、迁移攻击)检验防御边界与薄弱环节;
- 设计综合性对抗防御体系。
二、实验原理
2.1 Min-Max 优化框架(Madry et al., 2018)
对抗训练求解一个 Min-Max 问题:内层最大化损失(找最强对抗样本),外层
最小化损失(训练模型在该对抗样本上分类正确):
minθ E(x,y)max∥δ∥∞≤εL(fθ(x+δ),y) \min_\theta \; \mathbb{E}_{(x,y)} \Big \\max_{\\\|\\delta\\\|_\\infty \\le \\varepsilon} \\mathcal{L}\\big(f_\\theta(x+\\delta), y\\big) \\Big θminE(x,y)∥δ∥∞≤εmaxL(fθ(x+δ),y)
内层用 PGD 近似求解("PGD 攻击是通用的一阶攻击",内层越强、外层鲁棒性越好)。
2.2 TRADES(Zhang et al., 2019)
TRADES 显式分解"自然误差"与"边界误差",用 KL 散度正则化:
minθ EL(fθ(x),y)+β⋅KL(fθ(x) ∥ fθ(xadv)) \min_\theta \; \mathbb{E} \big \\mathcal{L}(f_\\theta(x), y) + \\beta \\cdot \\mathrm{KL}\\big(f_\\theta(x) \\,\\\|\\, f_\\theta(x_{adv})\\big) \\big θminEL(fθ(x),y)+β⋅KL(fθ(x)∥fθ(xadv))
其中 xadvx_{adv}xadv 最大化 KL(fθ(x)∥fθ(x′))\mathrm{KL}(f_\theta(x) \| f_\theta(x'))KL(fθ(x)∥fθ(x′))。β 控制
"鲁棒性---清洁精度"的权衡强度。
2.3 自适应攻击
评估鲁棒模型必须用"自适应攻击"(针对防御机制专门设计):更强的 PGD(更多
迭代、随机重启)、C&W 优化攻击、迁移攻击等,避免高估防御效果。
2.4 对抗训练的理论基础:从 ERM 到鲁棒风险最小化
标准训练最小化经验风险(Empirical Risk Minimization, ERM):
minθ E(x,y)∼DL(fθ(x),y) \min_\theta \; \mathbb{E}_{(x,y)\sim \mathcal{D}} \big \\mathcal{L}(f_\\theta(x), y) \\big θminE(x,y)∼DL(fθ(x),y)
ERM 只优化"干净样本"上的损失,完全不约束边界附近的行为,因此学到的
边界可以紧贴数据点------模型"准确但不鲁棒"。对抗训练最小化鲁棒风险
(Robust Risk):
minθ E(x,y)max∥δ∥∞≤εL(fθ(x+δ),y) \min_\theta \; \mathbb{E}_{(x,y)} \Big \\max_{\\\|\\delta\\\|_\\infty \\le \\varepsilon} \\mathcal{L}\\big(f_\\theta(x+\\delta), y\\big) \\Big θminE(x,y)∥δ∥∞≤εmaxL(fθ(x+δ),y)
鲁棒风险强迫边界与数据点保持至少 ε\varepsilonε 的距离。Tsipras et al.
(2019)证明了一个重要的不可能性结果:在数据类条件分布存在重叠(或特征
可分性不足)时,不存在一个模型能同时达到最优的干净精度与最优的鲁棒
精度 ------鲁棒性与清洁精度之间存在内在权衡。本实验第 3.1 节的权衡曲线
正是该理论的实证表现。
2.5 PGD:通用的一阶攻击
Madry et al.(2018)的核心论断:能抵抗足够强 PGD 的模型,也基本抵抗
其他一阶攻击。理由:
- 对凸问题,带符号梯度的迭代投影收敛到全局最优;
- 对深度网络(非凸),随机重启的 PGD 是对"一阶最优性"的经验近似;
- 若内层攻击弱(步数少、无重启),模型实际只在"弱攻击"下鲁棒,会被
更强的攻击穿透。
因此对抗训练的超参数关键是内层 PGD 的 ε、步数 T、步长 α:
- T 太小 → 内层攻击弱 → 防御被"更聪明的攻击"绕过;
- T 太大 → 训练成本线性上升(每步都需反向传播)。
本实验用 T=5 平衡成本与效果,并用第 3.2 节 T=50 + 3 次重启的自适应攻击
验证边界:对 PGD-AT 模型 PGD-20 与 PGD-50×3 的 ASR 几乎一致(2.0%),
说明 5 步训练已覆盖该类攻击的"主要脆弱方向"。
2.6 TRADES 的误差分解与 β 敏感性
TRADES 的理论出发点是"自然误差与对抗误差的分解"。记 fθf_\thetafθ 的自然
误差 为 Rnat(f)=P(f(x)≠y)\mathcal{R}_{nat}(f) = \mathbb{P}(f(x) \neq y)Rnat(f)=P(f(x)=y),对抗误差 为
Radv(f)=P(∃δ:f(x+δ)≠y)\mathcal{R}_{adv}(f) = \mathbb{P}(\exists \delta: f(x+\delta) \neq y)Radv(f)=P(∃δ:f(x+δ)=y)。
Zhang et al.(2019)证明对抗误差可被上界分解为两项之和:
Radv(f)≤Rnat(f)+Emax∥δ∥≤εKL(f(x) ∥ f(x+δ)) \mathcal{R}{adv}(f) \le \mathcal{R}{nat}(f) + \mathbb{E}\big \\max_{\\\|\\delta\\\|\\le\\varepsilon} \\mathrm{KL}\\big(f(x) \\,\\\|\\, f(x+\\delta)\\big) \\big Radv(f)≤Rnat(f)+E∥δ∥≤εmaxKL(f(x)∥f(x+δ))
第二项称为边界误差 :衡量"在扰动球内模型输出的不稳定程度"。TRADES
直接最小化这两项(用 KL 正则化逼近内层 max),因此比"只在对抗点监督
标签"的 PGD-AT 更精细地控制了边界形状。
β 是两类误差的相对权重:β 过小近似 PGD-AT;β 过大则 KL 项主导,模型趋向
输出恒定分布("什么都一样"),训练崩溃------本实验 β=6 在 6 轮训练预算下
退化为均匀预测(干净精度 48.4%)即为该机制的极端表现。实际中 β 应与训练
轮数、学习率联合调节(原论文使用 100 轮 + 步长调度)。
补充要点(来自原理补充版本):
记自然误差 Rnat(f)=P(f(x)≠y)\mathcal{R}_{nat}(f) = \mathbb{P}(f(x) \neq y)Rnat(f)=P(f(x)=y),对抗误差
Radv(f)=P(∃δ:f(x+δ)≠y)\mathcal{R}_{adv}(f) = \mathbb{P}(\exists \delta: f(x+\delta) \neq y)Radv(f)=P(∃δ:f(x+δ)=y)。
Zhang et al.(2019)证明对抗误差可被分解上界:
Radv(f)≤Rnat(f)+Emax∥δ∥≤εKL(f(x),∥,f(x+δ)) \mathcal{R}{\mathrm{adv}}(f) \le \mathcal{R}{\mathrm{nat}}(f) + \mathbb{E}\Big \\max_{\\lVert\\delta\\rVert\\le\\varepsilon} \\operatorname{KL}\\big(f(x),\\Vert,f(x+\\delta)\\big) \\Big Radv(f)≤Rnat(f)+E∥δ∥≤εmaxKL(f(x),∥,f(x+δ))
第二项称边界误差 :衡量扰动球内模型输出的不稳定程度。TRADES 直接
最小化这两项(KL 正则化逼近内层 max),比"只在对抗点监督标签"的 PGD-AT
更精细地控制边界形状。
β 是两类误差的相对权重:
- β 过小 → 近似 PGD-AT;
- β 过大 → KL 项主导,模型趋向输出恒定分布("对什么都一样"),训练崩溃。
本实验 β=6 在 6 轮训练预算下退化为均匀预测(干净精度仅 48.4%),β=3 则
正常收敛(干净 98.43%、鲁棒 95.5%)------定量演示了 β 的敏感性。原论文
β=6 需要 100 轮训练 + 学习率调度,说明超参数必须与训练预算联合调节。
2.7 自适应攻击与鲁棒性评估方法学
"评估攻击是否自适应"是鲁棒性研究的方法学核心:
- 静态攻击 (固定 ε 的 PGD)可能只是"碰巧没找到"对抗样本,而非模型
真的鲁棒; - 自适应攻击 针对防御的具体机制设计:知道对抗训练用的 ε 就用更大 ε +
更多迭代 + 随机重启;知道是 PGD-AT 就尝试 C&W(不同范数/不同目标
函数);知道有预处理就尝试"端到端"优化整个管线; - AutoAttack (Croce & Hein, 2020)把 4 种互补攻击(APGD-CE、APGD-DLR、
FAB、Square)组合为标准化评估套件,是目前鲁棒性基准的事实标准。
本实验用 PGD-50×3、C&W、迁移攻击构成自适应集:结果显示 C&W 能攻破
PGD-AT(95--100%)而 TRADES β=3 对其最鲁棒(12%)------说明"单一攻击评估
会系统性高估防御",必须用攻击族交叉检验。
2.8 权衡曲线的实证解读
| 模型 | 清洁准确率 | PGD(0.1) 鲁棒准确率 |
|---|---|---|
| 干净基线 | 98.83% | 86.60% |
| PGD-AT(ε=0.1--0.3) | 98.93--99.23% | 96.60--97.80% |
| TRADES β=1 / β=3 | 98.70% / 98.43% | 93.60% / 95.50% |
规律:对抗训练把鲁棒性提升约 10 个百分点,代价是清洁精度损失 <0.4%;
训练 ε 越大,对大扰动攻击(ε=0.3)越鲁棒(PGD-AT ε=0.2 训练后 ε=0.3
攻击仅 7.5% vs ε=0.05 训练的 18%)------"在哪个预算上训练,就在哪个预算上
鲁棒"。这为工程选型提供了直接依据:防御预算应匹配威胁模型中的扰动上限。
2.9 实现要点与可复用代码
python
# ① PGD 对抗训练(Min-Max)------内层攻击 + 外层更新
def pgd_ce(model, x, y, eps, alpha, iters):
was_training = model.training # ★ 保存训练模式
model.eval()
x_adv = torch.clamp(x + torch.empty_like(x).uniform_(-eps, eps), -3, 3)
for _ in range(iters):
x_adv = x_adv.clone().requires_grad_(True)
F.cross_entropy(model(x_adv), y).backward() # 内层 max
x_adv = x_adv.detach() + alpha * x_adv.grad.sign()
x_adv = torch.clamp(x + torch.clamp(x_adv - x, -eps, eps), -3, 3)
model.train(was_training) # ★ 必须恢复,否则 BN 统计被破坏
return x_adv
for xb, yb in train_loader:
x_adv = pgd_ce(model, xb, yb, eps, alpha, iters)
loss = F.cross_entropy(model(x_adv), yb) # 外层 min
optimizer.zero_grad(); loss.backward(); optimizer.step()
# ② TRADES 的正确 KL 方向(★ 本项目最隐蔽的坑)
# 目标:KL( f(x) || f(x_adv) ) = Σ_c f_c(x)·log[ f_c(x) / f_c(x_adv) ]
kl = F.kl_div(
input = F.log_softmax(logits_adv, dim=1), # q = f(x_adv)
target = F.softmax(logits, dim=1), # p = f(x)
reduction="batchmean")
loss = F.cross_entropy(logits, yb) + beta * kl
# ✗ 错误写法:kl_div(log_softmax(logits), softmax(logits_adv.detach()))
# 方向反了且目标被 detach,β 越大越会把模型推向"输出均匀"的退化解
# ③ 自适应攻击组合(评估防御必须用)
attacks = {
"PGD-20": lambda m, x, y: pgd(m, x, y, eps=0.1, alpha=0.02, iters=20),
"PGD-50×3重启": lambda m, x, y: best_of_restarts(pgd, m, x, y, eps=0.1, iters=50, n=3),
"PGD-ε0.3": lambda m, x, y: pgd(m, x, y, eps=0.3, alpha=0.06, iters=30),
"C&W": lambda m, x, y: cw_l2_attack(m, x, y, iters=80), # 换范数
"迁移": lambda m, x, y: pgd(surrogate, x, y, eps=0.1, iters=100), # 换模型
}
对抗训练配置与预算(本项目实际使用)
| 方法 | ε | 内层步数 | 训练轮数 | 单模型耗时 |
|---|---|---|---|---|
| PGD-AT | 0.05/0.1/0.2/0.3 | 5 | 6 | 8--12 分钟 |
| TRADES | 0.1 | 5 | 6 | 8--12 分钟 |
2.10 常见误解、纠正与自测题
常见误解
- "对抗训练能防住所有攻击" → C&W 攻破全部 PGD-AT 模型(95%--100%);
防御是"在某范数、某预算内"的。 - "PGD 迭代次数越多防御越好" → 内层步数决定防御质量,但 5 步与 50 步
的自适应攻击差距不大;关键是训练时的 ε 覆盖威胁预算。 - "TRADES 一定比 PGD-AT 好" → TRADES β=3 对 C&W 更鲁棒(12% vs 100%),
但对 ε=0.3 的 PGD 更弱(34.5% vs 7.5%)------两者盲区不同,宜组合使用。 - "β 越大正则越强越好" → β=6 在短预算下训练崩溃(干净精度 48.4%);
超参数必须与训练轮数、学习率联合调整。 - "换个攻击评估就行" → 必须针对防御机制设计自适应攻击 ;
本实验正是靠 C&W 才发现 PGD-AT 的范数盲区。 - "鲁棒性可以免费获得" → 对抗训练需要 5--10 倍训练成本(内层每次都要
反向传播),工程上要权衡。
自测题(附答案要点)
- 写出 Min-Max 对抗训练目标,并说明内层用什么求解。
要点 :minθEmax∥δ∥≤εL(fθ(x+δ),y)\min_\theta\mathbb{E}\\max_{\\\|\\delta\\\|\\le\\varepsilon} \\mathcal{L}(f_\\theta(x+\\delta),y)minθEmax∥δ∥≤εL(fθ(x+δ),y);内层用 PGD(多步符号梯度 + 投影)。 - TRADES 的误差分解定理说了什么?
要点 :Radv≤Rnat+EmaxδKL(f(x)∥f(x+δ))\mathcal{R}{adv}\le\mathcal{R}{nat}+\mathbb{E}\\max_\\delta \\mathrm{KL}(f(x)\\\|f(x+\\delta))Radv≤Rnat+EmaxδKL(f(x)∥f(x+δ)),即对抗误差 ≤ 自然误差 + 边界误差,
TRADES 显式最小化这两项。 - 为什么写错 KL 方向不会报错但会训练失败?
要点 :把 f(x)f(x)f(x) 推向 f(xadv)f(x_{adv})f(xadv) 的退化解是"输出趋于均匀",
损失正常下降(趋近 log C),准确率却停在随机水平。 - 内层攻击函数为什么必须恢复
train()模式?
要点 :eval()是全局状态,会让 BatchNorm 使用固定统计量,破坏训练
动力学(本项目 β=12 崩溃的第二个原因)。 - PGD-AT 模型对 C&W 为何如此脆弱?如何弥补?
要点 :PGD-AT 只覆盖 L∞ 球,C&W 优化 L2 距离可找到未覆盖方向;
可用 TRADES(边界平滑)或同时在多范数上训练来弥补。 - 评估一个防御方案时,最少需要哪些攻击?
要点 :同类更强攻击(更多迭代/重启)、不同范数攻击(C&W)、
迁移攻击,三者组合(理想情况用 AutoAttack 套件)。
三、实验结果
3.1 对抗训练模型对比(鲁棒性评估:PGD ε=0.1, 20 步)
| 模型 | 清洁准确率 | 鲁棒准确率 |
|---|---|---|
| 干净基线 | 98.83% | 86.60% |
| PGD-AT ε=0.05 | 98.93% | 96.60% |
| PGD-AT ε=0.10 | 98.93% | 97.80% |
| PGD-AT ε=0.20 | 99.23% | 97.80% |
| PGD-AT ε=0.30 | 99.03% | 97.60% |
| TRADES β=1 | 98.70% | 93.60% |
| TRADES β=3 | 98.43% | 95.50% |
| TRADES β=6(未收敛) | 48.40% | 34.00% |
3.2 自适应攻击测试(400 样本)
| 模型 | PGD-20 | PGD-50×3重启 | PGD ε=0.3 | C&W | 迁移 |
|---|---|---|---|---|---|
| 干净基线 | 12.75% | 14.25% | 96.00% | 100% | 1.75% |
| PGD-AT ε=0.05 | 3.00% | 3.00% | 18.00% | 100% | 2.25% |
| PGD-AT ε=0.10 | 2.00% | 2.00% | 12.00% | 100% | 1.00% |
| PGD-AT ε=0.20 | 2.00% | 2.00% | 7.50% | 100% | 1.00% |
| PGD-AT ε=0.30 | 1.50% | 1.50% | 9.50% | 95% | 0.50% |
| TRADES β=1 | 4.75% | 4.75% | 51.00% | 92% | 2.00% |
| TRADES β=3 | 2.50% | 2.75% | 34.50% | 12% | 1.00% |
| TRADES β=6 | 66.00% | 66.25% | 86.50% | 77% | 51.75% |
四、结果分析与讨论
4.1 鲁棒性---清洁精度权衡
- PGD-AT 各 ε 下鲁棒准确率 96.6%--97.8%,清洁准确率 98.9%--99.2%,权衡
损失很小(对比基线 86.6% 鲁棒); - 训练扰动 ε 越大,对大扰动攻击 (ε=0.3)的防御越强(7.5%--9.5% vs
ε=0.05 训练的 18%),代价是清洁精度略降------体现了"在哪个扰动预算上
训练,就在哪个预算上鲁棒"的规律; - TRADES 的 β 是敏感的旋钮:β=1 鲁棒 93.6%、β=3 鲁棒 95.5%;β=6 在本实验
的短训练预算(6 轮)下完全不收敛(清洁 48.4%)------论文中 β=6 需要 100 轮
训练。这直接演示了"超参数 + 训练预算"对对抗训练成败的决定性作用。
4.2 防御边界与薄弱环节
- 更强 PGD 收益有限 :PGD-50×3 重启 vs PGD-20 的 ASR 几乎不变(如
PGD-AT ε=0.2 都是 2.0%)------对抗训练的防御边界对"同类攻击强度"稳定; - C&W 是 PGD-AT 的薄弱点 :对 PGD-AT 模型攻击成功率 95%--100%!原因:
PGD-AT 只在 L∞ 球内防御,而 C&W 优化的是 L2 扰动,可以找到 PGD-AT
未覆盖的"低失真但有效"方向; - TRADES β=3 对 C&W 最鲁棒 (仅 12%):TRADES 通过 KL 正则化平滑了
logit 边界,间接抵抗了 L2 优化攻击------说明不同对抗训练方法的防御
"形状"不同,需要多种攻击联合评估; - 迁移攻击对鲁棒模型几乎无效 (0.5%--2.25%):对抗训练显著破坏了对抗
样本的跨模型迁移性。
4.3 综合防御体系
结合第 4--7 天实验,综合防御体系:
- 对抗训练(主防御) :PGD-AT ε=0.2 + TRADES β=3 双模型,覆盖 L∞ 与
L2 攻击面; - 输入预处理:中值滤波去噪(第 6 天验证 PGD ASR 95.5%→41.5%);
- 模型集成:多模型投票(第 5 天);
- 认证防御:随机平滑提供可验证保证(第 6 天);
- 检测与监控:随机化一致性检测 + 查询限制(第 4--5 天);
- 持续审计:定期用 C&W 等高强度自适应攻击重新评估,防止"防御退化"。
4.4 理论预测 vs 实测数据
| 理论预测 | 实测结果 | 是否吻合 |
|---|---|---|
| 对抗训练大幅提升鲁棒性 | PGD(0.1) 下:基线 86.60% → PGD-AT 96.60%--97.80% | ✅ |
| 代价是清洁精度小幅下降 | 98.83% → 98.93%--99.23%(部分配置甚至更高) | ✅ 权衡很小 |
| 训练 ε 决定鲁棒预算 | ε=0.3 攻击下:ε=0.05 训练 18.0% vs ε=0.2 训练 7.5% | ✅ |
| TRADES 的 β 需与训练预算匹配 | β=1/3 正常(干净 98.70%/98.43%);β=6 在 6 轮下仅 48.40% | ✅ |
| 更强同类攻击(更多迭代/重启)收益有限 | PGD-20 vs PGD-50×3 对 PGD-AT 模型:均 2.0% | ✅ 防御边界稳定 |
| 换范数攻击可突破 L∞ 训练 | C&W(L2)对 PGD-AT:95%--100% | ✅ 关键薄弱点 |
| 对抗训练破坏迁移性 | 迁移攻击对鲁棒模型仅 0.5%--2.25% | ✅ |
模型 × 攻击 ASR 矩阵(第 7 天核心数据)
| 模型 | PGD-20 | PGD-50×3 | PGD(ε=0.3) | C&W | 迁移 |
|---|---|---|---|---|---|
| 干净基线 | 12.75% | 14.25% | 96.00% | 100% | 1.75% |
| PGD-AT ε=0.05 | 3.00% | 3.00% | 18.00% | 100% | 2.25% |
| PGD-AT ε=0.10 | 2.00% | 2.00% | 12.00% | 100% | 1.00% |
| PGD-AT ε=0.20 | 2.00% | 2.00% | 7.50% | 100% | 1.00% |
| PGD-AT ε=0.30 | 1.50% | 1.50% | 9.50% | 95% | 0.50% |
| TRADES β=1 | 4.75% | 4.75% | 51.00% | 92% | 2.00% |
| TRADES β=3 | 2.50% | 2.75% | 34.50% | 12% | 1.00% |
五、试错与调试记录
5.1 问题速览
| # | 问题 | 类型 | 影响 | 解决方式 |
|---|---|---|---|---|
| 1 | TRADES β=12 训练崩溃(9.9%) | 代码+方法 | 模型完全失效 | 修正 KL 方向 + 恢复训练模式 |
| 2 | 修正后 β=6 仍未收敛(48.4%) | 超参数 | 一个数据点不可用 | 补 β=3 并解释敏感性 |
| 3 | 自适应攻击脚本模型名不匹配 | 代码 | 绘图缺失数据 | 修正模型清单并续跑 |
| 4 | 6 个模型训练耗时 | 工程 | 单次全量约 70 分钟 | 模型缓存 + 断点续跑 |
5.2 问题 1:TRADES β=12 训练崩溃,验证准确率仅 9.9%
现象
TRADES 训练(β=12)过程中,验证准确率一直停留在 9.9%--10.8%
(10 类随机水平),损失稳定在约 1.92(接近 log10≈2.30\log 10 \approx 2.30log10≈2.30),
即模型输出接近均匀分布,完全没有学到分类。
定位过程
-
对比 β=1 的实验(同结构、同轮数)能正常收敛到 98.7%,排除数据与模型
结构问题;
-
检查 KL 项方向 :TRADES 的目标是
KL(f(x) ∥ f(xadv))\mathrm{KL}\big(f(x)\,\|\,f(x_{adv})\big)KL(f(x)∥f(xadv)),而代码写成
pythonkl = F.kl_div(F.log_softmax(logits, 1), F.softmax(logits_adv.detach(), 1)) # 方向反了kl_div(input, target)计算的是 KL(target∥input)\mathrm{KL}(\text{target}\|\text{input})KL(target∥input),因此上式实际是 KL(f(xadv)∥f(x))\mathrm{KL}(f(x_{adv})\|f(x))KL(f(xadv)∥f(x)),且 f(xadv)f(x_{adv})f(xadv) 被
detach,梯度只会把 f(x)f(x)f(x) 往 f(xadv)f(x_{adv})f(xadv) 方向推------方向与目标相反; -
检查训练模式 :TRADES 内层生成对抗样本的函数开头调用了
model.eval(),但没有恢复train();由于该函数在训练循环内被调用,模型此后一直以 eval 模式训练,BatchNorm 使用固定统计量且不再更新,
训练动力学被破坏。
根本原因
两个独立缺陷叠加:(a) KL 散度方向写反;(b) 内层攻击函数没有恢复模型的
训练模式。
解决方案
python
# (a) 修正方向:KL(f(x) || f(x_adv))
kl = F.kl_div(F.log_softmax(logits_adv, 1),
F.softmax(logits, 1), reduction="batchmean")
# (b) 内层攻击保存并恢复训练模式
was_training = model.training
model.eval()
...
model.train(was_training)
修正后 β=3 正常收敛(干净 98.43%、鲁棒 95.5%)。
为什么这样解决
- KL 方向必须与理论一致,否则正则项把模型推向"输出模糊"的退化解
(这正是 β 越大崩溃越彻底的原因); - 训练模式必须显式保存与恢复:
model.eval()是全局状态 ,
在训练循环中被调用后不恢复,会静默破坏后续所有批次。
可积累的经验
- 使用
F.kl_div时先写清"谁是谁的目标分布",方向错误是本项目中最隐蔽的
bug 之一(不会报错,只会训练失败); - 任何在训练循环中被调用的"评估性质"函数,都要用
was_training = model.training ... model.train(was_training)模式; - 训练完全失效(准确率≈随机、损失≈log C)时,优先检查:损失方向、
训练模式、标签对齐这三件事。
对本项目的作用
修正后 TRADES 成为可用方案,并产出关键结论"TRADES β=3 对 C&W 最鲁棒
(ASR 仅 12%)"------这是第 7 天最重要的防御发现。同时,"训练模式必须恢复"
被写入项目编码规范,第 8 天 DP-SGD、第 9 天提取脚本都据此检查。
5.3 问题 2:修正后 β=6 仍不收敛(超参数敏感性)
现象
修正 KL 方向后重训 β=6:验证准确率从 44% 缓慢升到 48.4% ,仍未收敛
(对照:β=1 达 98.70%、β=3 达 98.43%)。
定位过程
- 排除了代码问题(同一实现下 β=1、β=3 均正常);
- 分析目标函数:CE+β⋅KLCE + \beta\cdot KLCE+β⋅KL,β 越大 KL 项占比越高;
在 KL 尚未收敛(模型输出仍不稳定)时,过大的 β 会让优化器优先压低 KL,
而降低 KL 最快的方式是"输出变得更模糊/更均匀"------与分类目标冲突; - 对照文献:TRADES 原文使用 β=6 但训练 100 轮 并配合学习率调度;
本实验预算为 6 轮,属于"β 与训练预算不匹配"。
根本原因
超参数 β 与训练预算(轮数、学习率)不匹配,导致优化停留在"KL 优先"的
次优区域。
解决方案
- 补做 β=3 实验点,使权衡曲线覆盖"收敛良好"的参数区间;
- 在日志与报告中明确记录 β=6 未收敛的现象与原因,不隐藏、不伪造;
- 给出实践建议:β 应与训练轮数联合调节,必要时采用学习率调度。
为什么这样解决
- 保留失败数据点能揭示超参数敏感性,比"只报告成功参数"更有教学价值;
- 补一个可用的中间点(β=3)保证权衡曲线的完整性(干净精度 vs 鲁棒性)。
可积累的经验
- 对抗训练的超参数(ε、β、内层步数)必须与训练预算联合调优;
- 复现论文超参数时要同时复现训练预算,否则结论不可比;
- 失败数据点在报告中同样是有价值的证据,应保留并解释。
对本项目的作用
使第 7 天的权衡曲线包含"收敛良好(β=1、3)"与"未收敛(β=6)"两类点,
更真实地反映了超参数敏感性;这一经验也被第 10 天总结报告采纳为
"对抗训练的工程注意事项"。
5.4 问题 3:自适应攻击脚本引用了已改名的模型
现象
自适应攻击脚本运行日志出现:
[WARN] 模型不存在: trades_beta12
结果 JSON 中缺少该项,绘图时该模型的柱子缺失。
定位过程
脚本的模型清单里保留了旧名字 trades_beta12(在问题 1 修复过程中,
该配置已改名为 trades_beta6,并新增了 trades_beta3);而磁盘上已无
对应权重文件。
根本原因
配置改名后,引用清单未同步更新(典型的"配置漂移")。
解决方案
修正模型清单为
[clean_baseline, pgd_at_eps005/010/020/030, trades_beta1/3/6],
重跑脚本;由于所有模型权重已缓存,重跑仅耗时约 4.5 分钟(含自适应攻击
评估),未重新训练。
为什么这样解决
- 治本是让"脚本引用的模型名"与"实际训练产物名"保持一致;
- 依托前几天的缓存机制,修复代价很小;若在早期(无缓存),这一处疏忽
会导致 70 分钟重训。
可积累的经验
- 模型/产物命名应集中定义(如统一的配置表),避免多处硬编码;
- 脚本启动时校验所需产物是否存在 并给出清晰告警(本次 WARN 起到了
保护作用,避免了静默出错); - 缓存机制不仅能省时间,也能显著降低"配置漂移"的修复成本。
对本项目的作用
促成了本项目"配置集中 + 启动校验 + 缓存复用"的组合做法,在第 8、9 天
的多模型实验中有效避免了"缺一个模型导致整图缺失"的问题。
5.5 问题 4:6 个对抗训练模型的全量训练耗时约 70 分钟
现象(工程挑战而非缺陷)
PGD-AT(ε=0.05/0.1/0.2/0.3)与 TRADES(β=1/3/6)合计 6 个模型,
单个模型 6 轮训练约 8--12 分钟,全量约 70 分钟;期间任何一次崩溃都会
造成大段损失。
解决方案
- 每个模型训练完立即
torch.save权重并写入结果 JSON(增量保存); - 脚本开头做"权重存在则加载"的续跑判断;
- 复用第 4 天已训练的 PGD-AT ε=0.1 模型(避免重复训练);
- 先跑单模型验证实现(KL 方向、训练模式),再批量训练其余模型。
为什么这样解决
- 训练是最昂贵的环节,必须"一次训练、多次使用";
- 先验证再批量,避免"6 个模型全部训错"的最坏情况(问题 1 正是先在
一个模型上暴露出来的)。
可积累的经验
- 批量实验前用最小可复现单元(单个模型)验证实现正确性;
- 增量落盘 + 续跑 + 复用已有产物,是长实验的三件套;
- 训练计划要标出"可复用资产"(如第 4 天的 ε=0.1 模型),避免重复劳动。
对本项目的作用
该策略使第 7 天在修复 β 配置后,只需补训 1--2 个模型而非全部重训;
最终产出的 6 个模型也成为第 6 天认证防御、第 10 天总结分析的基础资产。
5.6 复现与验证速查
复现问题 1:KL 方向写反 + 训练模式未恢复
第一步:确认 KL 方向
python
# ✓ 正确:KL( f(x) || f(x_adv) ) ------ 以 f(x_adv) 为 q(input)、f(x) 为 p(target)
kl_ok = F.kl_div(F.log_softmax(logits_adv, 1), F.softmax(logits, 1),
reduction="batchmean")
# ✗ 错误(本项目最初的写法):方向反了且 target 被 detach
kl_bad = F.kl_div(F.log_softmax(logits, 1), F.softmax(logits_adv.detach(), 1),
reduction="batchmean")
第二步:确认训练模式是否恢复
python
was_training = model.training # 训练循环中应为 True
model.eval() # 内层攻击需固定 BN 统计
# ... 生成对抗样本 ...
print(model.training) # ✗ 若仍为 False,说明忘记恢复
model.train(was_training) # ✓ 恢复
第三步:用验证曲线确认修复
| 配置 | 验证准确率曲线 | 最终干净准确率 | 鲁棒准确率 |
|---|---|---|---|
| 修复前(方向反 + eval 未恢复) | 停在 9.9%--10.8% | 9.90% | 11.60% |
| 修复后 β=3 | 1 轮 96.2% → 6 轮 98.4% | 98.43% | 95.50% |
判读 :准确率≈随机、损失≈log 10(2.30)时,按顺序检查"损失方向 →
训练模式 → 标签对齐"三件事。
复现问题 2:β 与训练预算的匹配关系
python
for beta in [1.0, 3.0, 6.0]:
model = train_trades(build_model(), train_loader, val_loader,
epochs=6, beta=beta, eps=0.1, alpha=0.02, iters=5)
print(beta, evaluate(model, val_loader)["accuracy"])
# 1.0 → 0.9870 3.0 → 0.9843 6.0 → 0.4840(未收敛)
| β | 训练轮数 | 干净准确率 | 鲁棒准确率 | 结论 |
|---|---|---|---|---|
| 1 | 6 | 98.70% | 93.60% | 可用 |
| 3 | 6 | 98.43% | 95.50% | 可用(权衡最佳) |
| 6 | 6 | 48.40% | 34.00% | 未收敛(论文需约 100 轮) |
复现问题 3:配置漂移的发现与修复
text
[WARN] 模型不存在: trades_beta12
python
# 修复:模型清单集中定义 + 启动时逐一校验
MODELS = ["clean_baseline", "pgd_at_eps005", "pgd_at_eps010", "pgd_at_eps020",
"pgd_at_eps030", "trades_beta1", "trades_beta3", "trades_beta6"]
missing = [m for m in MODELS if not ckpt_path(m).exists()]
assert not missing, f"缺少模型权重: {missing}"
修复验证 :重跑自适应攻击仅 4.5 分钟(模型全部命中缓存),并补齐
trades_beta3 的结果行。
本日排错速查
text
训练不收敛(准确率≈随机、损失≈log C)时的排查顺序:
① KL 方向:kl_div(input=log_softmax(q), target=softmax(p)) 中的 p/q 是否写对
② 训练模式:eval() 之后是否 train(was_training)
③ 标签对齐:内层攻击的标签与数据标签是否一致
④ 超参数:β 与 epochs 是否匹配(β 越大越需要更多轮)
⑤ 产物:启动时校验缺失的模型权重,避免静默缺数据
六、经验提炼
6.1 知识收获与心得
- 对抗训练的本质是"用攻击者的最强工具训练自己",Min-Max 框架是攻防
统一的数学语言; - 鲁棒性与清洁精度的权衡客观存在,但通过超参数可以调整到可接受区间;
- "防御有效性"必须用自适应攻击检验:PGD-AT 在 PGD 下 97.8% 鲁棒,却
被 C&W 几乎完全攻破------单一的防御评估会高估安全性; - 不同防御方法补足了彼此的盲区(PGD-AT 抗 L∞、TRADES 抗 L2),组合
防御才是工程实践的正解。
6.2 工程与排错经验
- KL 方向不能想当然 :
kl_div(input, target)计算的是
KL(target∥input)\mathrm{KL}(\text{target}\|\text{input})KL(target∥input),写反不报错但训练失败; - 训练模式是全局状态 :
eval()之后必须恢复train(); - 超参数要与训练预算匹配:β 大 + 轮数少 = 不收敛,失败点也要记录;
- 配置集中 + 启动校验:模型改名后引用清单必须同步,否则静默缺数据;
- 先单模型验证,再批量训练:把最坏情况的损失控制在一个模型内。