资讯详情

Circle Loss统一度量学习:自适应加权与难例挖掘实战

📅 2026/10/14 7:34:54 | 华诺云谱 👁 阅读
Circle Loss统一度量学习:自适应加权与难例挖掘实战
1. 论文核心思想拆解1.1 从“对”到“圆”一个直觉性的切入第一次读到Circle Loss这篇论文的时候我正被一个度量学习的项目折磨得够呛。当时用的是最经典的Triplet Loss调参调到怀疑人生——margin设大了模型不收敛设小了又学不到有区分度的特征。相信做过人脸识别、行人重识别或者图像检索的朋友都有类似体验。Circle Loss的核心贡献用一个词概括就是把“对相似度优化”这件事从“绝对距离”的框架里解放出来转而用“相对关系”来重新定义损失函数。听起来有点绕我换个说法。传统方法比如Triplet Loss它做的事情是给定一个锚点样本让正样本对的距离小于负样本对的距离并且中间要隔开一个固定的margin。这个margin是一个硬性的、对所有样本一视同仁的阈值。问题在于不同样本的“难易程度”天然不同——有些正样本对本来就离得很近有些负样本对本来就离得很远你用同一个margin去约束所有样本就像用同一把尺子去量不同尺寸的衣服怎么都不合身。Circle Loss做的事情是给每个相似度分数一个自适应的权重。简单来说如果一个正样本对的相似度已经很高了那它就不需要被过度优化权重自动降低如果一个负样本对的相似度也很高也就是很难区分那它就应该被重点关注权重自动升高。这个权重不是手动设定的而是从优化目标本身推导出来的。论文标题里的“Circle”这个词来源于损失函数在二维平面上可视化之后呈现出的圆形决策边界。这个几何直觉非常漂亮后面我会详细展开。1.2 统一视角为什么说它是“统一”的论文副标题叫“A Unified Perspective of Pair Similarity Optimization”这个“统一”体现在哪里我理解下来主要有两层意思。第一层它把基于类内/类间距离的损失和基于相似度分数的损失统一到了同一个数学框架下。你之前可能见过Contrastive Loss、Triplet Loss、N-pair Loss、Lifted Structured Loss等等这些方法看起来形式各异但Circle Loss的作者指出它们本质上都在做同一件事最小化类内相似度与类间相似度之间的差异。只不过不同方法对这个差异的定义和加权方式不同。第二层它把分类损失和度量学习损失也打通了。如果你用过Softmax配合归一化特征做分类你会发现它和度量学习其实有千丝万缕的联系。Circle Loss从这个角度出发给出了一个更通用的优化目标既可以用于分类任务也可以用于检索和验证任务。这个统一视角的价值在于它让我们不再纠结于“该选哪个损失函数”而是理解了这些损失函数背后的共同逻辑从而可以根据具体任务灵活设计。1.3 核心公式的直观理解Circle Loss的公式看起来有点复杂但拆开来看其实逻辑很清晰。我先写出它的基本形式L log[1 sum_{j} exp(gamma * alpha_n_j * (s_n_j - delta_n)) * sum_{i} exp(gamma * alpha_p_i * (delta_p - s_p_i))]其中s_p是正样本对的相似度s_n是负样本对的相似度alpha_p和alpha_n是自适应权重delta_p和delta_n是正负样本的margingamma是一个缩放因子。这个公式的关键在于alpha_p和alpha_n的定义alpha_p_i [O_p - s_p_i]_ alpha_n_j [s_n_j - O_n]_这里的O_p和O_n是“最优值”通常设为1m和-mm是一个小的margin。[·]_表示取正值也就是ReLU操作。这个设计的精妙之处在于当正样本对的相似度s_p_i越接近最优值O_p时alpha_p_i越小该样本对的梯度贡献就越小反之当负样本对的相似度s_n_j越高越难区分时alpha_n_j越大梯度贡献就越大。这就是自适应加权的核心机制。我个人的理解是这相当于给每个样本对分配了一个“注意力权重”让模型把精力集中在那些真正难分的样本对上而不是平均用力。这和Focal Loss的思想有异曲同工之妙只不过Focal Loss用在分类任务中Circle Loss用在度量学习中。2. 与传统方法的对比分析2.1 Triplet Loss的局限性要理解Circle Loss为什么好得先搞清楚Triplet Loss为什么不够好。Triplet Loss的形式很简单L max(0, d(a,p) - d(a,n) margin)它要求正样本对的距离d(a,p)比负样本对的距离d(a,n)小至少一个margin。这个设计有几个明显的问题。第一个问题是margin的刚性。所有样本对共享同一个margin但不同样本的分布差异很大。对于已经学得很好的样本对这个margin可能过于宽松导致它们不再贡献梯度对于很难分的样本对这个margin又可能过于严格导致训练不稳定。第二个问题是梯度消失。当d(a,n) - d(a,p) margin时损失为0梯度也为0。这意味着一旦某个三元组被“解决”了它就不再参与训练。但在实际数据中大部分三元组在训练初期就被解决了真正难的三元组很少导致训练效率低下。第三个问题是采样策略敏感。Triplet Loss的效果高度依赖于三元组的采样方式。随机采样效果很差需要精心设计hard negative mining或者semi-hard mining策略。但这些策略本身又引入了额外的超参数和计算开销。2.2 Circle Loss的改进逻辑Circle Loss针对上述问题做了系统性的改进。对于margin刚性问题Circle Loss引入了自适应的margin。注意看公式中的alpha_p和alpha_n它们实际上起到了动态调整margin的作用。对于容易的样本对有效margin变小对于困难的样本对有效margin变大。这相当于给每个样本对量身定制了一个margin。对于梯度消失问题Circle Loss采用了softmax形式的损失而不是hinge loss。这意味着即使某个样本对已经被很好地分开它仍然会贡献一个小的梯度不会完全消失。当然这个梯度会随着相似度接近最优值而衰减但不会突然变成零。对于采样策略问题Circle Loss的自加权机制天然地实现了“难例挖掘”的效果。难分的样本对自动获得更大的权重不需要额外的采样策略。这大大简化了训练流程。我用一个表格来直观对比特性Triplet LossCircle LossMargin固定自适应梯度消失存在缓解采样策略敏感不敏感损失形式HingeSoftmax类内/类间分别处理统一框架超参数数量较多较少2.3 与Contrastive Loss和N-pair Loss的关系Contrastive Loss只考虑正负样本对不考虑样本之间的相对关系信息利用不充分。N-pair Loss考虑了多个负样本但权重仍然是均匀的。Circle Loss可以看作是这些方法的推广当alpha_p和alpha_n都设为常数时Circle Loss退化为带权重的N-pair Loss当gamma趋于无穷时它趋近于Triplet Loss。这个“退化关系”很重要它说明Circle Loss不是凭空造出来的而是在现有方法基础上的自然推广。理解了这一点你就知道为什么论文说它是“统一视角”了。3. 实操细节与参数选择3.1 关键参数的含义与推荐值Circle Loss有几个关键参数需要设置我结合自己的实验经验逐一说明。缩放因子gamma这个参数控制损失的“锐度”。gamma越大损失越接近hard mining训练越激进gamma越小损失越平滑训练越稳定。论文推荐的取值范围是64到512我实测下来256是一个比较稳妥的起点。如果你的数据集噪声比较大建议从128开始如果数据集干净且难例多可以尝试512。类内margin m_p和类间margin m_n论文中通常设m_p 1 - mm_n m其中m是一个0到1之间的小值。常用的设置是m 0.25也就是m_p 0.75m_n 0.25。这个设置的含义是正样本对的相似度目标是0.75以上负样本对的相似度目标是0.25以下。注意这里的相似度是余弦相似度范围在-1到1之间。最优值O_p和O_n通常设为O_p 1 mO_n -m。当m 0.25时O_p 1.25O_n -0.25。这两个值决定了自适应权重的“零点”。温度系数有些实现中会额外引入一个温度系数来控制softmax的平滑程度。这个系数和gamma有重叠通常只调一个就够了。3.2 特征归一化的必要性Circle Loss假设相似度是余弦相似度这意味着特征必须先做L2归一化。这一点非常关键我见过不少人在复现时忘了归一化结果loss完全不收敛。归一化的操作很简单就是在特征输出层之后加一个L2 normalizeimport torch import torch.nn.functional as F def l2_norm(x, eps1e-12): norm torch.norm(x, p2, dim1, keepdimTrue) return x / (norm eps)注意eps不要设得太小否则当特征向量接近零向量时会出现数值不稳定。1e-12是一个比较安全的默认值。另外归一化之后特征的尺度信息就丢失了。对于某些任务比如需要尺度信息的检索这可能是个问题。但在大多数度量学习任务中方向比尺度更重要所以归一化利大于弊。3.3 损失函数的代码实现下面给出一个PyTorch版本的Circle Loss实现我加了详细注释import torch import torch.nn as nn import torch.nn.functional as F class CircleLoss(nn.Module): def __init__(self, m0.25, gamma256): super(CircleLoss, self).__init__() self.m m self.gamma gamma self.O_p 1 m self.O_n -m self.delta_p 1 - m self.delta_n m def forward(self, embeddings, labels): # embeddings: [batch_size, feature_dim] # labels: [batch_size] # L2归一化 embeddings F.normalize(embeddings, p2, dim1) # 计算相似度矩阵 sim_mat torch.matmul(embeddings, embeddings.t()) # 构建正负样本掩码 labels labels.view(-1, 1) mask_pos (labels labels.t()).float() mask_neg 1 - mask_pos # 去掉对角线自己和自己 eye torch.eye(labels.size(0), devicelabels.device) mask_pos mask_pos - eye mask_neg mask_neg - eye # 计算alpha_p和alpha_n alpha_p F.relu(self.O_p - sim_mat.detach()) alpha_n F.relu(sim_mat.detach() - self.O_n) # 计算logits logit_p -self.gamma * alpha_p * (sim_mat - self.delta_p) logit_n self.gamma * alpha_n * (sim_mat - self.delta_n) # 用mask过滤 logit_p logit_p * mask_pos (1 - mask_pos) * (-1e9) logit_n logit_n * mask_neg (1 - mask_neg) * (-1e9) # 计算损失 loss F.softplus( torch.logsumexp(logit_n, dim1) torch.logsumexp(logit_p, dim1) ).mean() return loss这段代码有几个细节需要注意。第一alpha_p和alpha_n的计算用了detach()这是为了防止梯度通过alpha回传因为论文中alpha被视为权重而非可学习参数。第二mask操作中用了-1e9来屏蔽无效位置这是softmax的常见技巧。第三最终损失用logsumexp和softplus组合数值稳定性比直接exp要好。3.4 训练策略与学习率调度Circle Loss对学习率比较敏感。我试过几种调度策略效果比较好的是warmup cosine decay。前5个epoch做线性warmup从1e-6升到初始学习率然后按余弦函数衰减到1e-6。初始学习率建议设在1e-4到1e-3之间具体取决于batch size和网络结构。batch size越大学习率可以适当调大。如果发现loss震荡严重先检查学习率是不是太高了。另外权重衰减也很重要。Circle Loss本身有正则化效果但适当的weight decay比如1e-4可以进一步防止过拟合。不过要注意weight decay不要加在归一化层和偏置项上这是常规操作。4. 几何直觉与可视化理解4.1 为什么叫“Circle Loss”论文中最吸引我的一张图是损失函数在二维相似度平面上的等高线图。横轴是正样本对的相似度s_p纵轴是负样本对的相似度s_n。对于Triplet Loss决策边界是一条直线s_n - s_p margin对于Circle Loss决策边界是一条圆弧。这个圆弧的几何意义是它到最优点(s_p1, s_n-1)的“加权距离”是恒定的。权重就是alpha_p和alpha_n。由于alpha_p和alpha_n随s_p和s_n变化所以等高线不是直线而是曲线整体呈现出圆形的形状。这个几何视角帮助我理解了一个关键问题为什么自适应权重能提升性能因为它在相似度空间中重新定义了“距离”——不是欧氏距离而是一种加权距离。在这种距离度量下靠近最优点的区域被“压缩”了远离最优点的区域被“拉伸”了从而让优化过程更加关注那些远离最优点的困难样本。4.2 梯度分析从梯度的角度看Circle Loss对正样本对的梯度正比于alpha_p对负样本对的梯度正比于alpha_n。当s_p接近O_p时alpha_p趋近于0梯度消失当s_n接近O_n时alpha_n趋近于0梯度也消失。这意味着只有那些“不够好”的样本对才会产生显著梯度。具体来说正样本对中相似度低于O_p的会产生梯度负样本对中相似度高于O_n的会产生梯度。这正好对应了“难例挖掘”的直觉。更有意思的是梯度的幅度还受到gamma的调制。gamma越大梯度对相似度变化的敏感度越高优化越“激进”。这解释了为什么gamma需要根据数据集难度来调整。4.3 与Softmax的深层联系论文中有一个很重要的观察当只有正样本对没有负样本对时Circle Loss退化为Softmax Cross-Entropy。这个联系不是巧合而是揭示了分类和度量学习的本质统一性。在分类任务中Softmax做的是让正确类别的logit远大于其他类别。在度量学习中Circle Loss做的是让正样本对的相似度远大于负样本对。两者都是在做“相对比较”只不过一个在logit空间一个在相似度空间。这个统一视角的实践价值在于你可以用同一套代码框架同时处理分类和检索任务只需要改变标签的定义方式。对于多任务学习场景这大大简化了工程实现。5. 常见问题与排查技巧5.1 Loss不收敛怎么办这是最常见的问题我总结了一个排查清单症状可能原因解决方法Loss震荡不下降学习率过高降低学习率加warmupLoss直接变NaN特征未归一化检查L2 norm层Loss下降但精度不涨gamma过小增大gamma到256或512Loss下降很慢batch size太小增大batch size或梯度累积正负样本不平衡采样策略问题使用类平衡采样我踩过最大的坑是忘记归一化。当时loss一直卡在某个值下不去排查了半天才发现特征没有做L2 norm。归一化之后loss立刻正常下降。所以如果你遇到诡异的问题先检查归一化。另一个坑是alpha的计算用了detach但忘了在正确的位置。如果alpha参与了梯度回传会导致训练不稳定因为alpha本身依赖于相似度会形成循环依赖。记住alpha是权重不是可学习参数。5.2 Batch Size的影响Circle Loss对batch size比较敏感因为它需要在一个batch内计算所有样本对的相似度。Batch size太小负样本对数量不足学到的特征区分度不够Batch size太大显存吃不消而且可能引入太多简单样本对稀释了难例的权重。我的经验是batch size在128到512之间比较合适。如果显存不够可以用梯度累积来模拟大batch。另外可以使用跨GPU的相似度计算把多个GPU上的特征汇聚到一起算loss这样等效batch size可以翻倍。具体实现上PyTorch的DistributedDataParallel配合all_gather操作可以实现跨卡的特征汇聚。不过这会增加通信开销需要权衡。5.3 难例挖掘还需要吗理论上Circle Loss的自适应权重已经实现了软性的难例挖掘不需要额外的hard mining。但在实际应用中我发现适度的hard mining仍然有帮助特别是在训练后期。具体做法是在每个batch内先计算所有样本对的相似度然后只保留相似度最高的若干个负样本对参与loss计算。这样可以进一步聚焦难例加速收敛。不过要注意不要过度挖掘否则容易导致训练崩溃。一个折中方案是semi-hard mining选择那些相似度高于正样本对但又不是最高的负样本对。这样既保证了难度又避免了噪声样本的干扰。5.4 多模态场景的适配Circle Loss最初是为视觉任务设计的但它的框架其实可以推广到多模态场景。比如图文匹配任务中正样本对是匹配的图文对负样本对是不匹配的。这时候相似度是跨模态的余弦相似度归一化需要在各自的模态内分别做。我试过在图文检索任务中用Circle Loss效果比Triplet Loss好不少。关键是要分别对图像特征和文本特征做归一化然后再计算跨模态相似度。如果只对拼接后的特征做归一化效果会差很多。另外多模态场景中负样本的构造很重要。除了随机负样本还可以用模态内负样本同模态但不匹配和模态间负样本跨模态但不匹配来增加难度。Circle Loss的自适应权重可以自动平衡这些不同难度的负样本。6. 实际项目中的经验总结6.1 数据集准备的关键点Circle Loss的效果高度依赖于数据质量。在准备数据集时有几个点需要特别注意。类别平衡如果类别严重不平衡少数类的样本对会被多数类的样本对淹没。建议使用类平衡采样或者给少数类更高的采样权重。标签噪声Circle Loss对标签噪声比较敏感因为错误的标签会导致正负样本对完全颠倒。如果数据集有噪声建议先用小学习率预热或者使用标签平滑技术。数据增强适度的数据增强可以增加样本多样性提升泛化能力。但要注意增强后的样本仍然要保持语义一致性否则会引入噪声。对于度量学习任务常用的增强包括随机裁剪、颜色抖动、水平翻转等。6.2 网络结构的选择Circle Loss是一个损失函数它不依赖于特定的网络结构。但在实际项目中网络结构的选择会影响最终效果。对于人脸识别任务常用的backbone包括ResNet、MobileFaceNet、ArcFace等。对于行人重识别常用的有ResNet-50、OSNet等。对于图像检索可以用预训练的ViT或者CLIP模型提取特征。我的建议是先用一个成熟的backbone跑通流程再根据需求调整。不要一上来就追求最复杂的结构那样调试成本太高。另外特征维度也是一个需要权衡的参数。维度太高容易过拟合维度太低表达能力不足。常用的维度是128、256、512。我一般从256开始试根据验证集效果调整。6.3 评估指标与验证策略度量学习任务常用的评估指标包括RecallK、mAP、CMC等。对于验证集建议使用k折交叉验证或者固定划分确保结果可复现。一个容易被忽视的点是验证频率。如果每个epoch都验证可能会因为验证集太小导致结果波动大。建议每几个epoch验证一次或者使用滑动平均来平滑结果。另外测试时的特征归一化要和训练时保持一致。如果训练时用了L2 norm测试时也要用。这个细节看似简单但经常有人忘记。6.4 与其他损失的组合使用Circle Loss可以和其他损失函数组合使用比如和分类损失联合训练。这种多任务学习的策略通常能带来性能提升因为分类损失提供了额外的监督信号。一个常见的组合是Circle Loss Cross-Entropy Loss。Circle Loss负责学习有区分度的特征Cross-Entropy负责分类决策。两者的权重需要调节一般Circle Loss的权重在1.0左右Cross-Entropy的权重在0.1到0.5之间。我试过在一个人脸识别项目中用这个组合相比单独用Circle Loss验证集准确率提升了大约1.5个百分点。不过这也增加了调参的复杂度需要根据具体任务权衡。6.5 训练时长与收敛判断Circle Loss的收敛速度比Triplet Loss快通常几十个epoch就能看到明显效果。但具体需要多少epoch取决于数据集大小和任务难度。判断收敛的一个实用方法是监控正负样本对的平均相似度。当正样本对的平均相似度稳定在0.8以上负样本对的平均相似度稳定在0.2以下时基本可以认为收敛了。另一个方法是监控loss的滑动平均。如果连续多个epoch loss变化小于某个阈值比如1e-4就可以停止训练了。不过要注意loss不再下降不代表性能不再提升有时候特征还在继续优化只是loss的变化不明显。我在实际项目中的体会是Circle Loss最大的价值不是它比某个特定损失函数好多少而是它提供了一个统一的思考框架。理解了这个框架之后你再去看其他损失函数会有一种“原来它们都在做同一件事”的顿悟感。这种认知上的提升比单纯涨几个点的精度更有意义。最后分享一个小技巧如果你在复现Circle Loss时遇到困难不妨先把gamma设小一点比如64把m设大一点比如0.5这样训练会更稳定。等跑通了再逐步调整到论文推荐的参数。调参这件事稳扎稳打比一步到位更靠谱。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑