news 2026/10/14 8:09:48

对抗训练鲁棒性提升:损失函数温度缩放与MMC解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
对抗训练鲁棒性提升:损失函数温度缩放与MMC解析

最近在复现对抗训练的实验时,碰到一个很典型的现象:同一套PGD训练流程,把交叉熵损失换成论文里那个“不起眼”的改进版本,干净样本准确率几乎不变,但白盒鲁棒准确率直接涨了三个多点。一开始我以为是自己训练脚本里数据增强没对齐,排查了半天,最后发现问题的根源就在损失函数本身。

这件事促使我把softmax交叉熵在对抗训练下的行为彻底过了一遍,也借着两篇文章把这块的脉络理清楚了。一篇是ICLR 2020上的《Rethinking Softmax Cross-Entropy Loss for Adversarial Robustness》,另一篇是基于类似思路发展出来的MMC(Max-Margin Cosine)损失。如果你现在正卡在对抗训练鲁棒性上不去、或者搞不懂为什么换个损失函数会有这么大影响,这篇内容大概率能帮你省下不少试错时间。

1. 先聊点背景:对抗训练为什么“带不动”softmax交叉熵

1.1 softmax交叉熵在普通训练里到底做了什么

softmax交叉熵大概是深度学习里最常用的分类损失,没有之一。它的形式很简单:

[ L_{CE} = -\log \frac{e^{z_y}}{\sum_{j=1}^{C} e^{z_j}} ]

这里的 ( z = W^T f + b ) 是最后一层的logits,( y ) 是真实类别。交叉熵的优化目标本质上是在拉大正确类别logit和错误类别logit之间的距离,让正确类别的概率输出尽量接近1。

在普通的自然训练中,这个损失表现得非常稳定:只要数据分布偏差不大,模型能很快收敛,特征空间里同类样本会聚集,不同类样本会分开。此时模型输出的logits通常有比较大的范数,置信度也比较高。

但问题恰恰藏在这个“大范数”里。标准训练不需要模型对“微小扰动”保持稳定,它只需要在数据流形的真实样本点上做出正确判断就够了。分类边界可以离真实样本点很近,甚至允许边界两侧的样本密集排列。

1.2 对抗训练引入了一个“反直觉”的博弈过程

对抗训练的标准形式是min-max优化:

[ \min_{\theta} \max_{|\delta|p \le \epsilon} L{CE}(f_\theta(x+\delta), y) ]

换句话说,每一步训练都要先找到让损失最大的扰动,然后再用这个扰动样本更新模型。PGD(Projected Gradient Descent)是常用的内层优化方法,通过多步梯度上升来逼近最坏扰动。

这个过程的本质是把分类边界“推离”样本点。理想情况下,模型需要在以每个真实样本为中心、半径为 (\epsilon) 的球内都做出正确判断。这相当于把决策边界从样本附近挪到球面附近。

但由于深度网络的分类边界非常复杂,这个“推离”过程并非均匀发生。不同方向上边界的移动速度不一样,模型会倾向于把容易防守的方向先推远,留下一些薄弱方向。

1.3 对抗训练下损失函数会“悄悄”改变特征分布

一个被很多人忽略的细节是:对抗训练不仅改变了决策边界,还会改变最后一层特征的分布形态。

很多复现实验里都会观察到,标准训练收敛后最后一层特征范数普遍在几十的量级,而经过PGD对抗训练后,特征范数会明显缩小,甚至掉到个位数。logits的方差也随之缩小,softmax输出的概率分布趋于平缓,模型整体变得“不自信”。

粗看这好像不是坏事——模型谦虚一点,对扰动不那么敏感。但实际上,当logits范数过小时,损失函数的梯度信号会变得非常微弱。梯度变小意味着对抗训练的内层优化更难找到强扰动,外层更新也难以有效推开边界。整个训练过程会陷入一种“看似鲁棒、实则梯度消失”的假象。

2. 那篇ICLR 2020论文到底提出了什么“反常识”方案

2.1 对logits范数的系统性分析

论文团队做了大量实验对比标准训练和对抗训练下logits的变化,确认了一个核心现象:对抗训练会显著压缩logits的动态范围。

这背后其实有一个逻辑链条。对抗扰动在输入空间引入的高频噪声,经过网络的前向传播,会在深层特征上产生较大的相对偏移。对模型来说,最省事的抵抗方式就是降低整体特征响应的幅度,让扰动带来的偏移相对变小。于是模型在训练中会隐式学到“压低特征范数”的偏好。

但问题在于,softmax交叉熵对logits的绝对尺度很敏感。logits整体缩小后,正确类别的优势也会被稀释。即使分类边界仍然正确,梯度的大小和方向都会变得更加不稳定。在对抗样本的局部邻域内,这种不稳定性会被进一步放大。

2.2 最简单的修复:给logits加一个温度缩放

论文给出的方案出奇简单——直接在logits上除以一个常数:

[ L = L_{CE}\left( \frac{z}{\tau} \right) ]

这里的 (\tau) 是一个温度参数。当 (\tau > 1) 时,等效于把logits整体放大,恢复softmax输出的梯度幅度;当 (\tau < 1) 时,等效于把logits压缩,让概率分布更尖锐。

直觉上理解这件事,可以用一个生活类比:把logits比作扬声器的音量,对抗训练相当于把音量旋钮调低了,导致整个系统信噪比下降。温度缩放就是重新把音量拧大,让信号在噪声背景下依然能区分出来。

实际训练时,我一般会把 (\tau) 设在5到20之间。具体取值取决于模型结构和数据集,稍后会有更详细的调参建议。

2.3 论文里还做了什么:权重归一化与特征缩放

光缩放logits还不够,因为对抗扰动对特征的影响幅度并不均匀。论文进一步设计了针对特征空间的修正。

一个常见配套做法是权重归一化(weight normalization):把最后一层权重 (W) 按行或者整体进行归一化,去掉权重尺度的影响,让分类主要依赖方向信息。这样做的好处是,即使特征的绝对数值被压缩,类别判断仍能基于相对角度展开。

另一个做法是对特征本身做尺度调整,例如对特征向量计算范数后再乘上一个可学习的缩放系数,相当于为每个样本保留“信心值”,但限制极端情况。

论文的实验结果证实:仅改变温度缩放,就能在多个数据集上显著提升PGD攻击下的鲁棒准确率,这说明了logits尺度问题的重要性。

2.4 为什么这个方案能提升鲁棒性

更深层的原因是:对抗鲁棒性本质上要求分类函数在 (\epsilon)-球邻域内保持稳定。如果logits范数过小,微小扰动就能让各类logits的相对大小发生翻转。温度缩放等价于给决策边界增加了一个“缓冲带”,让logits的相对关系更难被扰动翻转。

用数学一点的语言描述:对logits进行缩放不会改变干净样本上的argmax结果,但会改变损失函数在扰动邻域内的曲率。更大的logits尺度对应更大的局部梯度,使得模型在对抗训练中更容易把边界推远。

不过这里有一个陷阱要注意:温度缩放不能解决所有问题。如果模型本身容量不足,或者训练收敛不充分,单纯放大logits只会放大噪声。所以这个方案必须配合完整的PGD对抗训练流程使用。

3. MMC损失:把分类边界从欧氏空间搬到余弦空间

3.1 softmax边界的“先天不足”

常规softmax交叉熵对应的分类边界是一个线性超平面:

[ (W_{y} - W_{j})^T f + (b_y - b_j) = 0 ]

这个边界有两个问题。第一,它同时依赖于特征的范数和方向。第二,在特征范数很低时,边界对方向的变化非常敏感。

ICLR 2020那篇用温度缩放恢复了一部分尺度信息,但仍然没有从根本上改变“边界形状由线性超平面决定”这个事实。于是就有了进一步的想法:为什么不把分类边界直接定义在方向空间上?

3.2 MMC的核心设计思路

MMC(Max-Margin Cosine)损失的做法很直接:

  1. 将特征向量和权重向量都做L2归一化,使分类只依赖余弦相似度。
  2. 在正确类别的余弦相似度上减去一个margin (m),强制要求正确类别的角度间隔更大。
  3. 将所有logits乘以一个固定缩放因子 (s),恢复梯度尺度。

公式形式如下:

[ L_{MMC} = -\log \frac{e^{s(\cos\theta_y - m)}}{e^{s(\cos\theta_y - m)} + \sum_{j \neq y} e^{s\cos\theta_j}} ]

其中 (\cos\theta_j = \tilde{W}_j^T \tilde{f})。

相比普通softmax,MMC等于在特征空间里增加了一个“角度缓冲带”。不仅要求正确类别与错误类别的余弦相似度有差距,还要求这个差距至少为 (m)。由于距离度量完全基于角度,样本特征范数对决策结果几乎没有影响,这正好补上了对抗训练里特征范数被压缩后带来的脆弱点。

3.3 MMC和温度缩放应该怎么配合

实际实验下来,这两者并不是替代关系,而是互补关系。

温度缩放解决的是“梯度幅度”问题,MMC解决的是“决策边界形状”问题。一个理想的组合是在对抗训练中使用带温度缩放的交叉熵作为基础损失,同时在MMC中天然包含了缩放因子 (s),此时不需要额外对logits除以温度,只需调整 (s) 的取值。

我个人的经验是:先用ICLR 2020的logits缩放方案快速验证训练流程是否正常,再切换MMC进一步拉开鲁棒性差距。不要一上来就同时改多个组件,否则出了问题很难定位。

4. 实战复现:把两个损失函数真正跑起来

4.1 环境与训练框架的准备

复现时我用的是一台单卡32G GPU,数据集选用CIFAR-10,模型结构是ResNet-18。这个配置比较适合快速迭代损失函数设计。框架使用PyTorch,依赖主要是torch、torchvision、numpy。

训练流程分为两个阶段:前期用标准交叉熵训练正常收敛,后期切换对抗训练微调。直接从头跑对抗训练不是不行,但收敛速度慢得多。

4.2 PGD对抗训练的最小可复现模板

下面这段代码是PGD训练的核心循环,我在多个项目里反复改过,稳定性还不错:

def pgd_attack(model, images, labels, eps=8/255, alpha=2/255, steps=10): images_adv = images.clone().detach().requires_grad_(True) for _ in range(steps): outputs = model(images_adv) loss = F.cross_entropy(outputs, labels) grad = torch.autograd.grad(loss, images_adv)[0] images_adv = images_adv + alpha * grad.sign() images_adv = torch.clamp(images_adv, images - eps, images + eps) images_adv = torch.clamp(images_adv, 0, 1).detach().requires_grad_(True) return images_adv

有几个细节值得注意:扰动初始化采用随机起点会比零起点效果好;梯度符号方法已足够,不需要上梯度投影;每步更新后要记得同时做 (\epsilon)-球裁剪和图像有效范围裁剪。

4.3 FeatureScale CE损失的接入

ICLR 2020方案的核心改动只有一行,写起来非常清爽:

class FeatureScaleCELoss(nn.Module): def __init__(self, tau=8.0): super().__init__() self.tau = tau def forward(self, logits, labels): return F.cross_entropy(logits / self.tau, labels)

接入对抗训练时,把原来计算损失的代码替换成这个模块即可。干净样本和对抗样本都使用同样的损失函数。

关于 (\tau) 的初始选择,我自己的经验是先看模型正常训练结束时的logits标准差。如果标准差在0.4左右,(\tau) 取8到12比较合适。目标是让缩放后的有效logits回到标准训练时的量级。

4.4 MMC损失的实现细节

MMC实现时需要同时拿到特征和最后一层权重:

class MMCLoss(nn.Module): def __init__(self, margin=0.2, s=16.0): super().__init__() self.margin = margin self.s = s def forward(self, features, weights, labels): features = F.normalize(features, dim=1) weights = F.normalize(weights, dim=1) cos_theta = torch.matmul(features, weights.t()) batch_size = labels.size(0) target_logits = cos_theta[torch.arange(batch_size), labels] target_logits = target_logits - self.margin cos_theta[torch.arange(batch_size), labels] = target_logits logits = cos_theta * self.s return F.cross_entropy(logits, labels)

这里有两个容易踩坑的点:

第一,margin只能作用在目标类别对应的logits上,不能全局操作,否则所有类别的logits都被拉低,margin就失效了。

第二,缩放因子 (s) 和温度缩放不同,它不能太小。太小的 (s) 会让梯度变得平缓,margin的效果被稀释。常用区间是16到32,类别数越多,(s) 要适当取大一点。

4.5 超参数调整的实操建议

从我的复现结果来看,超参敏感度的排序大致是:

margin > s > tau > 学习率

margin对最终鲁棒准确率影响最大。margin过小时,特征空间里的角度间隔不够,鲁棒性提升有限;margin过大时,训练容易不稳定,损失会震荡甚至发散。

5. 踩过的坑与排查思路

5.1 温度参数过大导致训练发散

有一组实验里我把 (\tau) 设成了50,结果干净样本准确率都掉了。原因是logits放大后,正确类别的概率被推到接近1,交叉熵梯度几乎为零,模型陷入饱和。

排查思路很简单:训练过程中每多少个batch打印一次logits的均值和标准差。如果标准差突然变得异常大或者接近零,都要警惕。(\tau) 应该作为一个动态调整的参数,而不是固定不变。

5.2 MMC训练不收敛时的排查顺序

MMC训练不收敛,通常从三个角度排查:

  • 确认特征和权重是否真的做了归一化,这步没做的话整个损失退化成带margin的普通交叉熵。
  • 确认margin是否过大。CIFAR-10上margin超过0.3基本就不稳定。
  • 确认缩放因子 (s) 是否在合理范围。s过小会导致损失曲率不足。

有次我把margin设为0.6,训练损失不断上升,后来降到0.25,问题立刻缓解。

5.3 特征范数监控应该怎么做

不管用哪个损失函数,我都建议把特征范数作为一个常规指标记录下来。做法是在训练循环里对网络最后的特征向量做一次L2范数统计:

features = model.features(x) feat_norm = features.norm(dim=1).mean().item()

如果发现对抗训练后特征范数下降一个数量级以上,说明模型在走“压低响应”的捷径。此时即使损失函数表面正常,鲁棒性也会受限。

5.4 什么时候用MMC,什么时候只用温度缩放

如果是新手刚接触对抗训练,或者只想快速验证一个防御思路是否行得通,直接用温度缩放的交叉熵就够了。它的改动最小、行为可预期。

如果已经用温度缩放取得了一定鲁棒性提升,但还想压榨更多,再把MMC引进来。我自己的经验是,MMC对白盒攻击的鲁棒性提升更明显,但对黑盒迁移攻击的提升不一定稳定,需要针对场景做取舍。

5.5 损失函数评估的小技巧

评估一个新损失函数是否有效时,不要只看干净准确率和PGD准确率两个数。建议额外记录:

  • CW攻击下的准确率
  • 不同扰动半径下的鲁棒准确率曲线
  • 损失值在对抗扰动下的变化幅度

这些指标能帮你判断模型到底是“真的鲁棒”,还是“只在特定攻击强度下鲁棒”。

另外,每次修改损失函数后,最好固定训练轮数和学习率调度策略,只改动损失那一行,确保变量控制。

6. 我对这两个方案的个人体会

用了一段时间这些变体损失后,我最大的感受是:对抗训练研究里很多“提升”并不是模型学会了更复杂的特征,而是损失函数把模型的注意力重新拉回了正确的方向。softmax交叉熵这个组件太常用了,以至于我们常常忘了它也会成为瓶颈。

温度缩放和MMC的共同价值,在于它们都让模型在特征范数被压缩的不利条件下,依然保持足够强的梯度信号和合理的决策边界。相比那些复杂的正则化项,这类“损失函数层面的细节修复”其实更值得优先尝试,因为它们几乎没有额外计算开销,也不会让训练流程变得臃肿。

如果你也在复现对抗训练相关实验,我的建议是先把logits和特征范数的变化规律摸清楚,再决定要不要引入新的损失函数。很多时候,问题不在模型不在数据,就在于那个被所有人都忽略的交叉熵。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 8:09:13

SpringBoot2+Vue3构建多媒体素材管理系统实战

1. 项目概述这个基于SpringBoot2Vue3MyBatis-PlusMySQL8.0的多媒体素材管理系统&#xff0c;是一个典型的现代化全栈Web应用。我在实际开发中发现&#xff0c;这类系统特别适合中小型创意团队、教育机构和企业市场部门使用&#xff0c;能够有效解决多媒体文件分散存储、版本混乱…

作者头像 李华
网站建设 2026/10/14 8:08:46

推理场景CUDA优化:Kernel设计、CUDA Graphs与多GPU通信实践

系列走到第九篇&#xff0c;前两篇把 CUDA 算子优化的基础打法过了一遍——访存模式、bank conflict、指令级并行这些老生常谈的东西&#xff0c;今天换个角度&#xff0c;聊推理场景里真正拉开差距的三个方向&#xff1a;推理 Kernel 的设计思路、CUDA Graphs 的工程落地&…

作者头像 李华
网站建设 2026/10/14 8:08:42

Pandas多表合并与拼接实战:merge、concat核心参数与避坑指南

1. 为什么合并操作是数据处理的分水岭刚接触表格数据处理的人&#xff0c;往往会在某个时刻突然意识到&#xff1a;真正让人头疼的从来不是单个表格的清洗&#xff0c;而是把好几张表拼到一起。单表操作无非是筛选、排序、改列名&#xff0c;逻辑再绕也绕不出那一亩三分地。可一…

作者头像 李华
网站建设 2026/10/14 8:08:32

SpringBoot食品仓库管理系统开发实践

1. 项目概述这个食品仓库管理系统是基于SpringBoot框架开发的企业级应用解决方案&#xff0c;专门针对食品行业的仓储管理痛点设计。我在实际开发过程中发现&#xff0c;食品类商品相比普通商品具有保质期敏感、存储条件特殊、批次管理严格等特点&#xff0c;传统仓储系统往往难…

作者头像 李华
网站建设 2026/10/14 8:07:56

学习率η调优实战:从原理到LR Range Test的完整指南

1. 为什么超参 η 值得单独拎出来讲但凡动手训过模型的人&#xff0c;大概率都有过这种体验&#xff1a;网络结构调了半天&#xff0c;数据清洗做得一丝不苟&#xff0c;结果模型死活不收敛&#xff0c;loss 曲线像心电图一样上蹿下跳。最后一查&#xff0c;问题出在学习率上—…

作者头像 李华
网站建设 2026/10/14 8:06:40

Python Flask+Vue构建生鲜溯源系统实战

1. 项目背景与核心价值在生鲜食品行业&#xff0c;消费者对食品安全和透明度的需求与日俱增。去年参与某有机农场ERP系统改造时&#xff0c;农场主向我展示了一本厚达300页的纸质生产记录册——从播种日期到采收工人的体温数据全部手写记录。这种传统方式不仅查询效率低下&…

作者头像 李华