4. 对抗训练方法:基础对抗训练、集成对抗训练、TRADES算法、在资源受限设备上的优化
对抗训练,说白了就是给模型“打疫苗”。
你想想看,一个模型在干净数据上跑得再好,遇到精心构造的对抗样本,立马就“翻车”了。我在做边缘设备部署时,就吃过这个亏——模型在实验室准确率99%,到了摄像头前,被人贴个贴纸就识别错了。嗯,从那以后,我把对抗训练列为了必选项。
4.1 基础对抗训练:最朴素的“以毒攻毒”
基础对抗训练的思路很简单:把对抗样本混进训练数据里,让模型学会“抗揍”。
具体做法是,每次迭代时,先用当前模型生成对抗样本,然后把这些样本和原始样本一起喂给模型训练。我习惯用PGD(Projected Gradient Descent)来生成对抗样本,因为它比FGSM更稳定。
核心公式:
min_θ E_{(x,y)~D} [ max_{δ∈S} L(f_θ(x+δ), y) ]
说白了就是:找一个最坏的扰动δ,让模型在这个扰动下还能正确分类。
# 基础对抗训练伪代码(PyTorch风格)
for epoch in range(epochs):
for x, y in dataloader:
# 1. 生成对抗样本
x_adv = pgd_attack(model, x, y, epsilon=0.03, steps=7)
# 2. 混合训练
loss = criterion(model(x), y) + criterion(model(x_adv), y)
# 3. 反向传播
loss.backward()
optimizer.step()
我的经验:epsilon别设太大。我曾经设了0.1,结果模型直接“学废了”,连正常图片都认不准。建议从0.01开始调。
4.2 集成对抗训练:人多力量大
基础对抗训练有个问题:它只针对当前模型生成的对抗样本。换个攻击方式,可能又不行了。
集成对抗训练的思路是:用多个不同的模型来生成对抗样本,然后一起训练目标模型。这样模型见过的“花招”更多,鲁棒性自然更强。
我在项目中用过三种集成方式:
- 模型集成:用不同初始化或不同架构的模型生成样本
- 攻击集成:同时用FGSM、PGD、C&W等多种攻击方法
- 参数集成:同一个模型,不同epsilon或步数设置
注意:集成对抗训练的计算量是线性增长的。在边缘设备上,我建议最多用3个模型集成,否则训练时间会让人崩溃。
4.3 TRADES算法:鲁棒性与准确性的平衡术
TRADES(TRadeoff-inspired Adversarial Defense via Surrogate-loss minimization)是我个人比较推崇的方法。它解决了一个核心矛盾:鲁棒性和准确性往往不可兼得。
TRADES的损失函数分两部分:
L = L_ce(f(x), y) + β * KL(f(x) || f(x_adv))
第一部分保证正常分类准确率,第二部分让模型在干净样本和对抗样本上的输出分布尽量一致。
β这个超参数很关键。我调参时发现:
| β值 | 干净样本准确率 | 对抗样本准确率 | 我的评价 |
|---|---|---|---|
| 0.5 | 92% | 45% | 太弱,防不住 |
| 1.0 | 88% | 62% | 推荐,平衡不错 |
| 2.0 | 82% | 68% | 鲁棒性够了,但准确率掉太多 |
避坑指南:我曾经把β设到5.0,结果模型对所有样本都输出均匀分布——它“摆烂”了。TRADES不是β越大越好,1.0左右是个不错的起点。
4.4 在资源受限设备上的优化
好了,理论说完了,咱们聊聊实战中最头疼的问题:边缘设备算力有限,怎么跑对抗训练?
我总结了几条实用策略:
- 减少PGD步数:标准PGD要7-10步,边缘设备上我降到3步。效果差不了太多,但速度快了3倍。
- 混合精度训练:用FP16代替FP32,显存占用减半,速度提升40%。我在Jetson Nano上实测过,效果不错。
- 提前停止:对抗训练不需要收敛到极致。我一般训练到验证集鲁棒准确率不再提升就停,能省30%时间。
- 知识蒸馏:先用大模型做对抗训练,然后把鲁棒性“蒸馏”给小模型。这个方法我在树莓派上用过,效果出奇的好。
# 边缘设备优化版对抗训练
def efficient_adv_train(model, dataloader, device='cpu'):
for x, y in dataloader:
# 只用3步PGD
x_adv = fast_pgd(model, x, y, steps=3, eps=0.03)
# 混合精度计算
with torch.cuda.amp.autocast():
loss = criterion(model(x), y) + criterion(model(x_adv), y)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
重要提醒:边缘设备上的对抗训练,千万别照搬服务器端的参数。我建议先在服务器上跑小规模实验确定超参数范围,再移植到边缘设备上微调。否则一次训练跑三天,发现参数不对,那滋味...嗯,我经历过。
最后说一句,对抗训练不是万能的。它主要防御的是白盒攻击,对黑盒攻击效果有限。在实际部署中,我通常会把对抗训练和输入预处理(比如随机裁剪、JPEG压缩)结合起来用,效果更好。
下一章我们会聊模型量化与对抗鲁棒性的关系,这也是个坑很多的话题,到时候细说。