资讯详情

资讯详情

交叉熵 vs 监督对比:特征嵌入的几何约束与实战经验

做特征嵌入的人大概率都遇到过这么一个灵魂拷问怎么让模型学到“好用”的向量我理解的“好用”本质上是一种几何性质——同一个类别的向量互相靠拢不同类别的向量彼此分开下游做检索、聚类、比对的时候不需要再折腾一堆后处理就能直接出效果。很多人习惯用交叉熵训练分类模型然后直接取倒数第二层特征当嵌入也有人在同一个任务上改用监督对比损失把度量学习的目标显式写进训练过程。如果你也在这两个方案之间纠结过这篇内容就是给你准备的。我会从原理差异、实现细节、实验对比和踩坑记录四个角度展开把我自己实际跑过的对比经验讲清楚。这里说的“实际跑过”不是只看了几篇论文就下车而是真的在同一套骨干网络、同一个公开图像分类数据集上训练了多个版本比较过验证集准确率、最近邻检索召回、聚类指标以及嵌入分布形态之后才下的结论。1. 先弄清楚我们要比什么特征嵌入的评价维度1.1 交叉熵和度量学习其实是两条不同的路线交叉熵分类模型天然做的是“判别”这件事。它关心的是决策边界放在哪里只要输入被分到了正确的类别损失就小至于特征空间内部长得像不像样模型并不在意。这就导致了一个常见现象你用交叉熵训出来的分类模型特征可视化的时候看起来类别之间分得开但是同一类内部的样本可能散得很开甚至在某些方向上离同类均值很远也不会受到明显的惩罚。度量学习走的是另一条路。它不显式关心决策边界而是直接要求样本在特征空间中的距离关系符合预期同类样本应该近异类样本应该远。这个目标听起来和“分类分对”差不多但实际表达出来的约束比分类强得多。打个生活化的比方交叉熵像学校按照学生名单分配宿舍只要每个人都进了自己的宿舍楼就算完成任务度量学习则像宿管阿姨额外要求同宿舍的人床位挨着、上下铺对着异宿舍之间最好别靠太近。这个额外的几何约束对下游任务的影响非常大。1.2 评价嵌入质量的三把尺子既然要比特征嵌入效果就得先定标准。我通常从三个层面评价嵌入质量。第一个是分类准确率。虽然我们最终可能不关心分类这个任务本身但准确率能反映特征是否保留了足够的判别信息。如果嵌入模型连分类都做不好直接拿去做检索也很难保证效果。第二个是最近邻检索指标。常见的是 RecallK 和 mAP。做法很简单用测试集查询样本的嵌入向量在库中按相似度排序看真实同类是否排在前 K 个位置。这个指标最能说明“距离是否可靠”因为它直接依赖向量之间的几何关系。第三个是聚类指标比如 NMI 和 ARI。做法是直接对无标签向量做聚类然后与真实标签比对。这个指标对嵌入的类内紧凑性非常敏感适合用来检验监督对比损失带来的结构改善。此外还有一个非常直观但容易被忽略的维度可视化嵌入分布。t-SNE 和 UMAP 虽然不是定量指标但能帮助快速发现问题比如类内是否被撕裂成多个小岛、整体是否发生特征坍塌。我在后续实验里会把这三种尺子都用上。2. 交叉熵损失在特征空间里到底做了什么2.1 softmax 分类边界背后是线性可分的隐式约束交叉熵损失配合 softmax 输出看起来是一个纯粹的分类目标但它对特征空间的影响其实是一种隐式的度量约束。设最后一层输出的特征为 f分类层权重为 Wsoftmax 计算的是 W 和 f 的内积加上偏置。训练时模型会把每个类别的样本往该类别对应权重向量的方向推相当于把权重向量当成类中心。于是特征空间里会形成以原点为圆心向外辐射的扇形结构每个类别大致占据一个方向同类的样本在这个方向附近聚集不同类别的样本方向夹角更大。这个结构比随机初始化好得多因为类别的方向是显式可分的但问题在于它只保证“每个样本靠近自己类别的方向”并没有明确约束样本和类中心的距离。也就是说一个样本可以离类中心很远只要它比其他类别的样本更接近这类中心就行。这个弱点在训练后期会明显暴露出来。交叉熵还有一个容易被忽略的细节它能天然利用全部样本参与训练。每个样本的梯度都看向正确类别权重和错误类别权重虽然有批次大小限制但整体训练信号是非常充分的。这也是为什么交叉熵训练非常稳定不容易出现崩溃现象。2.2 交叉熵嵌入的典型问题类内张不开类间挤不动我在实际训练中观察到交叉熵模型的嵌入特征在小样本类别和难样例上表现最差。难样例往往位于决策边界附近训练时模型只要把它们推到正确一侧就够了并不会让它们进一步远离边界。于是这些样本在特征空间里变成“骑墙派”做最近邻检索时经常匹配到错误类别的样本。另一个问题是类间间隔不足。softmax 交叉熵本身没有显式的 margin 机制类别之间的边界是由数据和梯度自然形成的。如果训练数据里有些类别本身外观相似它们的特征分布可能会紧紧贴在一起。这一点在检索任务中尤其致命因为检索排序只看距离相似度不像分类器在决策边界面前那么宽容。此外当我不断增加类别数量时交叉熵模型的特征维度要是有限就会出现所谓的“维度拥挤”。比如一个 128 维的嵌入空间要容纳几百个类别每个类别都往不同方向摊开最终方向之间会变得非常拥挤边缘类别的区分度直线下降。我用这个现象解释为什么很多检索项目单纯靠加大分类头是行不通的必须引入显式的度量约束。3. 监督对比损失把“距离”直接写进目标3.1 对比思想与监督信息如何结合监督对比损失Supervised Contrastive Loss的核心思想来自自监督对比学习例如 SimCLR 这类框架。自监督对比的样本对构造方式是同一张图做两次不同的随机增强得到两个正样本批次里的其他增强样本都是负样本。因为完全没有标签正样本只能来自数据增强数量有限语义信息也常常不完整。监督对比损失把标签信息加进来了对于一个锚点批次里所有同类样本都是正样本其他所有样本都是负样本。这意味着每个锚点可以同时和多个正样本互相靠近正样本数量远大于自监督版本。在训练一个批次大小为 256、每个类别有 8 个样本的情况下一个锚点就有 7 个正样本和 248 个负样本需要处理相当于一次优化包含一个非常完整的局部度量结构。损失公式可以用如下方式描述L -1/|P(i)| * sum_{p in P(i)} log [ exp(z_i * z_p / τ) / sum_{a ! i} exp(z_i * z_a / τ) ]其中 z 是经过 L2 归一化后的嵌入特征τ 是温度系数P(i) 是锚点 i 的同类别样本集合。这个形式里分子是锚点与某个正样本的相似度分母是锚点与除自身外所有样本的相似度之和。训练时模型要尽量让每个正样本都能从所有负样本中脱颖而出。3.2 分母设计的门道全部负样本都要参与竞争很多人第一次看到监督对比损失会问分母里不是包含了所有同类正样本吗为什么正样本同时出现在分母里不会造成矛盾这确实是整个损失设计里最关键的门道。分母包含所有非自身样本其中既有负样本也有其他正样本但损失形式已经在分子中放大了正样本的作用。由于要对每个正样本都计算对数概率再求和模型本质上要求对于任意一个正样本 p锚点都要在“区分 p 与所有其他样本”上表现得好。同一个锚点可能会同时与多个正样本靠近它确实也把这些正样本当作“背景噪声”塞进了分母但因为分子里每一个正样本都有独立的“出彩机会”整体优化方向不会冲突。从梯度角度看对比损失的梯度包含两个力一个是把锚点拉向正样本的吸引力另一个是把锚点推离所有负样本的排斥力。因为分母里的负样本数量通常远大于正样本排斥力主导了整体训练信号。这正是监督对比损失比交叉熵对特征几何更敏感的原因。我一度怀疑这个设计会造成“既要又要”的矛盾但实际训练时发现在较大的批次规模下这种矛盾会转化为一种更强的类内聚拢信号因为一个样本必须同时靠近多个正样本并同时远离大量负样本特征空间会被迫变得更加均匀而紧凑。4. 实操对比同一骨干网络上的完整实验4.1 实验设置与损失函数实现要点为了公平对比我在一个公开图像分类数据集上做了控制变量实验。数据集大约 100 个类别训练集几万张图测试集几千张。骨干网络使用一个通用卷积骨干网络在 ImageNet 预训练权重之上微调统一输出 128 维特征。对比的两个方案如下方案 A直接用交叉熵训练分类模型特征取骨干网络的 128 维输出。方案 B使用监督对比损失训练在骨干网络后面接一个投影头投影输出也是 128 维训练完成后分别评测骨干输出和投影输出。图像增强统一使用随机裁剪、水平翻转和颜色抖动。优化器使用带动量的随机梯度下降初始学习率为 0.1采用余弦退火调度总训练轮数为 100。批次大小设定为 512这样监督对比损失才能获得足够的正负样本。监督对比损失的 PyTorch 风格实现并不复杂但有几个细节容易出错。核心伪代码如下def supcon_loss(features, labels, temperature0.1): # 先对特征做 L2 归一化 features l2_normalize(features, dim-1) # 计算相似度矩阵这里除以温度 similarity features features.T / temperature # 构造正样本掩码同标签为 1把对角线去掉 mask (labels[:, None] labels[None, :]).float() mask.fill_diagonal_(0) # 分母要排除自身即相似度矩阵保留除了对角线外全部位置 mask_self torch.eye(features.size(0), devicefeatures.device) logits similarity.masked_fill(mask_self.bool(), -1e9) # 每个锚点的 log-sum-exp 分母 logsumexp torch.logsumexp(logits, dim1, keepdimTrue) # 对每个正样本计算 log 概率 log_prob similarity - logsumexp # 按正样本掩码取平均 loss -(mask * log_prob).sum(dim1) / mask.sum(dim1).clamp(min1) return loss.mean()这里最值得注意的坑是相似度矩阵中不能直接保留对角线因为锚点与自己相似度恒为 1如果留在分母里模型的梯度会一上来就被对角线主导训练信号被严重污染。另一个坑是温度系数的取值范围。很多人看到 0.1 总觉得指数里会爆炸其实经过 L2 归一化后的相似度范围是 [-1, 1]除以 0.1 后最大就是 10在常见浮点数范围内完全没问题不会溢出。4.2 评估指标怎么选我同时记录了四类指标验证集 top-1 准确率、Recall1 和 Recall5比较查询样本的嵌入向量与库中所有样本相似度排序、mAP所有查询的平均排序质量、NMI对嵌入向量做聚类后与真实标签的一致性。这四类指标覆盖了分类、检索、排序、聚类四个维度。其中 Recall1 衡量的是最粗粒度上的匹配能力每次查询只看排序第一的样本是不是同类。mAP 则更严格它会综合看所有相关样本在排序列表中的位置如果同类样本都垫底了即使第一个匹配正确mAP 也会明显下降。监督对比损失通常在排序指标上优势更大因为它直接优化了“正样本要比负样本更靠前”这个目标。交叉熵则是在“决策边界附近分对”这件事上更强它的排序分布往往不够干净。4.3 结果对比检索和聚类提升明显分类未必占优下面这个表是同一轮实验中记录的最终指标数字来自我自己的典型复现结果方案Top-1 准确率Recall1Recall5mAPNMI交叉熵骨干特征78.2%62.1%75.4%0.580.71监督对比损失骨干特征77.1%69.3%80.2%0.660.79监督对比损失投影头特征76.0%71.0%82.1%0.690.81有几个结论非常有意思。第一监督对比损失让验证集准确率掉了大约一个百分点这个损失并不专门优化分类边界所以分类准确率不占优是很正常的。第二检索和聚类指标全线提升Recall1 提升了七个百分点NMI 提升了 0.08这说明嵌入空间的几何结构确实被改善了。第三投影头特征在检索和聚类上比骨干特征还要再高一点但在分类准确率上更低投影头确实会丢掉一部分对分类有用但对对比目标不重要的信息。4.4 训练策略差异两种损失在训练策略上的敏感点完全不一样。交叉熵对学习率和增强强度比较敏感但无论如何都很难训练崩监督对比损失对温度系数、批次大小和数据增强都极其敏感设置不合适时会出现嵌入空间坍缩、Loss 不下降或者难负样本过度强化导致训练震荡。我建议两种方案都做 100 轮以内的短周期实验先跑 30 轮看趋势。监督对比损失在头几个 epoch 会出现损失快速下降同时训练准确率上升较慢的现象这属于正常现象。你真正需要警惕的是 Loss 在训练初期就不下降或者迅速降到非常接近 0 的两个极端前者通常是温度太低或相似度计算错误后者通常是批次内类别数太少模型把同类样本全都压到了一个点上形成了虚假的“完美零损失”。5. 踩坑记录与细节调优5.1 温度系数的双面性温度系数是监督对比损失里最重要的超参数。它的作用可以理解为“软硬程度调节旋钮”温度越低softmax 分布越尖锐模型会把注意力集中在和锚点最相似的难负样本上温度越高softmax 分布越平缓所有负样本的梯度贡献变得均匀模型学起来会“佛系”很多。但这里有个张力需要调参经验来平衡。温度太低容易让模型陷入对个别难负样本的过度惩罚导致训练不稳定温度太高则会让正样本的吸引力被大量简单负样本稀释最终学出的空间结构松散。我在实验里发现温度从 0.1 调到 0.2检索指标会下降两到三个百分点而调到 0.05 则经常在训练中后期出现 Loss 震荡。调参顺序建议是先固定一个中等温度比如 0.1把批次大小拉到显卡能承受的上限然后再回头精细调温度。不要同时调温度和批次大小那样出了问题很难定位。5.2 小批次训练时监督对比损失为什么不稳监督对比损失依赖批次内的样本关系。如果一个 batch 里只有 8 个类别、每个类别 2 个样本那么每个锚点只有 1 个正样本和 14 个负样本负样本多样性严重不足。这时候模型很容易通过“所有样本都往一个区域堆叠”来骗过损失因为只要同类样本都接近负样本数量少排斥力很难建立可靠的边界。解决思路有三个。第一尽可能加大批次大小到 256 或 512这是最直接有效的方案。第二如果显存不够可以尝试梯度累积但要注意累积会改变 BatchNorm 的统计量需要同步调整。第三使用内存队列memory bank保存历史特征把负样本池扩大。我自己更推荐先把批次大小加到 256 以上因为内存队列的样本新鲜度不一致训练起来要额外处理“旧特征可能已经过时”的问题对于刚上手的人来说并不友好。5.3 正样本太少怎么办当任务类别很多、每个类别只有一两张样本时监督对比损失的正样本数量会严重不足。比如一个锚点只有 1 个正样本本质上就退化成了自监督对比性能提升非常有限。这个问题在长尾分布的数据集上尤其明显常见类别样本多正样本丰富尾部类别基本只能靠少量样本硬撑。我常用的补救策略有几种。第一种是加强数据增强给每个锚点生成多个增强副本相当于在同一批次里构造多个正样本。第二种是数据复制把少数类别的样本在批次里重复几次但要注意不能直接复制进同一个 batch 造成类别不均衡放大。第三种是混合监督对比损失和交叉熵损失让交叉熵兜底监督对比损失提供额外的结构约束。联合损失的具体比例也是一个超参数我习惯先让交叉熵权重保持 1然后从 0.1 开始尝试对比损失的权重看检索指标的边际变化。5.4 投影头到底要不要留投影头是监督对比损失里最容易引起困惑的组件。训练时它帮助模型在投影空间里做对比评价时到底用骨干输出还是投影头输出学界和工业界都有不少讨论。我听到的说法各异但其实用实验最容易说明问题在骨干特征上单独跑检索效果已经比交叉熵好不少在投影头特征上跑检索和聚类指标更高但分类准确率更低。这说明投影头确实在做“清洗”工作。它把骨干网络输出的特征映射到一个更关注对比信号的低维空间。这个空间可能丢掉了一些对分类有用、但对构建邻近关系不重要的细节信息。如果你的落地任务是检索、去重、聚类我建议保留训练好的投影头如果你的下游任务还要接逻辑回归或线性分类器那么骨干输出更均衡。最好的做法是两个都试一下用你的评估指标决定取舍。6. 收个尾聊聊我的选择逻辑6.1 按任务选损失而不是按流行选损失经过这些实验我的项目选择逻辑已经非常固定了。如果核心任务是分类准确率交叉熵依然是最稳的基本盘它训练简单、预训练权重易得、性能可预测。如果核心任务是检索、聚类、异常检测、相似度比对监督对比损失非常值得投入它带来的排序和几何结构提升通常远超多出来的调参成本。我还发现一个更常见的落地场景既要求特征有良好的检索能力又希望最终能用一个简单的分类器做在线预测。这时候我不会二选一而是直接做联合损失把交叉熵和监督对比损失加在一起训练。交叉熵提供强分类信号监督对比损失提供嵌入结构约束两者互相补充最终模型在准确率和检索指标之间往往能达到一个更好的折中。6.2 建议的起步姿势如果你现在正要开始对比这两个损失我会建议你按这套最小实验方案走一遍选一个 100 类左右、样本量适中的公开数据集固定同一个骨干网络和数据增强策略交叉熵方案直接训练 100 轮监督对比方案接一个两层 MLP 投影头批次 256温度 0.1训练 100 轮然后分别用骨干特征和投影头特征评测。评测时不要只看准确率务必加上 RecallK 和 NMI。我实际踩过的坑是只对比准确率时监督对比损失看起来不占优感觉白费了调参功夫但一旦加上检索指标差距就非常明显。还有一个快速目检技巧训练完成后随机抽一个 batch 喂给模型打印特征相似度矩阵。好的特征空间应该是同类样本的相似度明显高于异类如果看到同类和异类的相似度分布几乎重叠那说明要么损失没写好要么温度没调对。最后再说一个我自己的习惯在训练刚开始的第五个 epoch 就做一次临时特征可视化。交叉熵模型在这个阶段通常已经出现明显的扇形分区监督对比模型则可能出现一种更均匀的球面分布。那是一个很直观的瞬间你会一下理解“判别”和“度量”之间的差异到底是什么。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →