Circle Loss 详解:统一 softmax 与 triplet 的自适应度量学习损失

发布时间:2026/10/17 10:51:26
Circle Loss 详解:统一 softmax 与 triplet 的自适应度量学习损失 做图像检索和特征学习的同学大概率都被损失函数折磨过。分类任务里随手一个交叉熵就能跑出不错的结果一旦换成检索、验证这类需要学习 embedding 的任务损失函数的选择就变得特别微妙triplet 的采样能让你调到头秃softmax 训出来的特征又总觉得 margin 不太够AMSoftmax、ArcFace 这些加了 margin 的变体效果虽好但公式越堆越多彼此之间到底什么关系也没人讲得清楚。第一次读到 Circle LossCVPR 2020的时候最大的感受就是它把这些看起来八竿子打不着的损失函数统一到了同一个“相似度优化”框架里并且顺手给出了一种自适应加权的改进思路。这篇笔记不打算逐行念论文而是想把我自己读懂这篇论文的完整过程、核心推导、实验结论以及落到代码里的经验一次性讲明白。如果你正在做人脸验证、图片检索、细粒度分类、行人重识别这类任务这篇内容应该能帮你省下不少来回翻论文的时间。1. 为什么这篇论文值得读先理清损失函数家族1.1 两个流派其实在做同一件事度量学习metric learning领域的损失函数表面上五花八门本质上就分两大流派。第一类是基于分类标签的 class-level 方法。最典型的就是 softmax 交叉熵以及它的各种加 margin 变体。基本做法是在特征 embedding 后面接一个全连接分类层用交叉熵做监督。但很多人忽略了这一点当把分类层权重和特征向量都做 L2 归一化之后softmax loss 的每一个 logit 其实就是“当前样本特征和某个类别原型向量”的余弦相似度。换句话说softmax 看似在“做分类”实际每一轮迭代都在做一件事——把样本和它自己类别的原型拉近同时把样本和其他类别的原型推远。这不就是相似度优化吗第二类是基于样本对的 pair-level 方法。triplet loss 是代表它直接构造 anchor、positive、negative 三个样本要求正样本对的相似度比负样本对的相似度大出一个 margin。它的优势是非常灵活不依赖任何分类头直接优化样本之间的几何关系但缺点也同样出名采样策略稍不合适训练就崩或者干脆长时间不收敛。这两派表面上一个靠分类器间接优化一个直接对样本对优化但拆开看它们都在操纵两个量类内相似度s_p样本和同类代表之间的相似度和类间相似度s_n样本和异类代表之间的相似度。所谓代表可以是分类器中的类别原型也可以是 batch 里的一个正样本或负样本。搞明白这件事之后再去读 Circle Loss会发现作者的切入点特别清晰既然所有损失函数都在优化s_p和s_n那为什么不直接在它们身上做文章而是各自发明一套看起来完全不同的公式1.2 传统损失的决策边界都是直线为了看清主流损失函数到底在干什么可以做一个简化。假设我们只看一对相似度——一个类内相似度s_p、一个类间相似度s_n。对于 triplet loss要求是s_p - s_n m损失可以写成max(0, s_n - s_p m)。这个形式说明模型只关心两个相似度的差值差值达标就不管了没达标就线性地拉一下。对于带 margin 的 softmax 变体比如 AMSoftmax要求同样是s_p - s_n m这种形式只是计算的相似度是样本和类别原型之间的。逻辑上仍然是在拉大差值。如果把s_p和s_n分别当作横轴和纵轴画出来这些损失函数的决策边界全都是一条直线s_p - s_n m。直线意味着什么呢意味着在模型眼里类内和类间这两个相似度是完全对称的、可以互相补偿的——只要差值达标不管s_p是 0.9 还是 0.3不管s_n是 -0.8 还是 0.5模型都觉得“已经搞定了”。但实际情况里这个假设太粗糙。我举一个非常典型的例子一个样本的s_p0.8、s_n0.2差值 0.6triplet 设 margin 为 0.3模型就认为已经达标、不再优化然而s_p0.8和s_n0.2都处于“还算行但远远不够好”的模糊地带。反过来一个样本的s_p0.9、s_n0.95差值虽然只有 0.05但两个相似度本身一个很高一个很低其实已经处在非常理想的空间。这种区分能力传统直线边界完全给不了。这也就是 Circle Loss 这篇论文切入的核心痛点能不能别只看差值而是分别给s_p和s_n设定各自的目标2. Circle Loss 怎么改的从对称目标到自适应权重2.1 把“差值约束”换成“绝对目标约束”Circle Loss 的作者做了一个很直接的调整不再要求s_p - s_n m而是分别给类内相似度和类间相似度设定目标值。用O_p表示类内相似度的目标用O_n表示类间相似度的目标。因为常用 embedding 会做 L2 归一化相似度取值范围大约在 [-1, 1]论文里取O_p 1 mO_n -m。这里 m 就是 margin通常取 0.25 左右。为什么这么设因为归一化后的相似度最大就是 11m是一个略高于理论上界的值意思就是“类内相似度给我往死里拉高拉到比 1 还高才满意”-m是略低于 0 的一个值意思是“类间相似度得压到负数区域才够干净”。于是原来的一条差值约束落到了两个具体的进度条上。这个调整带来一个非常实际的变化不再存在“差值达标就万事大吉”的情况。即使s_p已经到 0.9只要s_n还是 0.0两个进度条仍然在同时被优化即使s_n已经压到 -0.5只要s_p还停在 0.5模型也仍然知道该往哪个方向继续用力。这在传统 triplet 的逻辑里是很难实现的——差值约束天然会掩盖这种“一个指标已经不错、另一个还有大量空间”的状态。2.2 公式怎么读log-sum-exp 与自适应权重Circle Loss 的完整公式如下多正例和多负例场景L log[ 1 sum_i exp( gamma * alpha_n_i * (s_n_i - O_n) ) * sum_j exp( gamma * alpha_p_j * (O_p - s_p_j) ) ]其中alpha_n_i [s_n_i - O_n]_ / sum_k [s_n_k - O_n]_ alpha_p_j [O_p - s_p_j]_ / sum_k [O_p - s_p_k]_第一次看到这个公式容易懵但拆开读并不复杂。先看[·]_就是 ReLU负数取 0正数保留。也就是说当s_n_i大于O_n即类间相似度太高时这个负样本才会参与优化当s_p_j小于O_p即类内相似度不够高时这个正样本才会参与优化。这本身就是一种天然的门控机制——已经达标的相似度不再贡献梯度。再看alpha_n_i和alpha_p_j这是最关键的自适应权重。以alpha_n_i为例分子是s_n_i - O_n表示这个负样本离目标多远分母是对所有参与负样本的“离目标距离”求和。所以一个负样本离目标越远它在所有负样本中获得的权重占比就越大。换句话说每个样本的梯度不是均分的而是根据它自身偏离最优状态的程度按比例分配——越离谱的样本被惩罚得越狠已经接近目标的样本轻轻推一下就好。gamma是缩放因子控制了整个惩罚曲面的陡峭程度论文实验里通常取 64 到 80类似 AMSoftmax 里的 scale 参数。2.3 为什么叫 Circle圆形决策边界的几何直觉论文里有一张很经典的二维示意图横轴是s_n纵轴是s_p。传统损失函数的决策边界是一条直线。Circle Loss 因为给s_p和s_n分别设定了目标并做了自适应加权决策边界变成了一条圆弧。圆弧和直线最大的差别在于它不再强制要求“两个相似度的差值必须保持一致”。在圆弧上如果s_n已经压得很低即使s_p暂时没有拉到很高样本也可能已经处于“安全区”反过来如果s_p已经非常高那么对s_n的要求也可以适当放宽。这种非对称容忍比直线边界更符合检索任务的实际需求——我们最终关心的是排序质量而不是机械地维持某个固定差值。严格说起来论文里的“圆”是在特定简化假设下给出的几何解释实际多正例多负例时的边界形状没那么标准但核心直觉是对的把每个相似度当作独立变量来优化比把它们合并成差值来优化拥有更灵活的解空间。训练过程中的梯度不再“一视同仁”而是根据每个相似度距离自己目标的远近来动态分配。这是整篇论文最值钱的思想。2.4 梯度行为的直观理解如果从梯度角度再对比一次优势会看得更清楚。Triplet loss 的梯度在有效区间内是恒定的也就是说一个已经非常差的负样本和一个勉强不合格的负样本得到的梯度强度是一样的。AMSoftmax 这类损失加入了 scale 因子梯度会随着相似度变化而变化但本质还是对“差值”在做整体调整。Circle Loss 因为引入了离目标距离作为权重梯度就会呈现一种更符合直觉的特性某个相似度越离谱它收到的梯度越大越接近理想区间收敛步伐越小。这种软性的、逐步收敛的行为让训练在后期更加稳定。我在实际复现里一个很明显的感受是Circle Loss 在训练后期不容易出现来回震荡loss 曲线比 triplet 要平稳不少很大程度就归功于这种自适应权重。3. 它和 AMSoftmax、Triplet Loss 到底什么关系3.1 一张表看懂差异很多论文笔记在对比损失函数时只罗列公式缺少一个能直接对照的视角。我把三种损失的核心差异整理成了表格维度Triplet LossAMSoftmaxCircle Loss优化对象样本对相似度样本与类别原型相似度统一处理样本对/类别原型相似度边界形式直线s_p - s_n m直线s_p - s_n m圆弧s_p、s_n 分别逼近目标梯度分配有效区间内恒定由 scale 影响但整体固定按离目标距离自适应加权是否依赖分类头否是可选对采样敏感度高非常依赖难例采样低低到中等明显优于 triplet超参数量1margin2margin scale2margin gamma从这个表格能看出 Circle Loss 的一个显著优势它把 triplet 和 AMSoftmax 的优点结合起来了。既像 triplet 一样可以直接优化样本对、不依赖分类头又像 AMSoftmax 一样在优化过程中天然带上了 margin 和 scale 的调节能力同时还加入了自适应权重这个传统两者都没有的机制。3.2 从 AMSoftmax 出发和从 Triplet 出发都能推导到 Circle Loss这篇论文最精彩的部分就是证明了 AMSoftmax 和 Triplet Loss 可以统一到同一个框架里。从 AMSoftmax 出发它的类内相似度就是样本和正确类别原型的相似度类间相似度就是样本和其他类别原型的相似度。把这两个相似度代入 Circle Loss 的统一公式继续保留分类头就得到了一套“分类头 Circle Loss”的训练方式去掉分类头直接用样本对计算相似度就退化成纯 pair-wise 的场景。从 Triplet Loss 出发就更直接。Triplet 的s_p和s_n本来就来自 batch 内的正负样本对把它套进 Circle Loss相当于把原来的“差值 margin”拆分成了两个独立的目标并额外引入了自适应权重。作者在论文里通过数学推导说明了在没有自适应权重、并且目标值取特定组合的退化情况下Circle Loss 可以还原成传统的 triplet 或带 margin 的 softmax。这个“退化成统一”的证明虽然只是几句推导但价值极高——它说明 Circle Loss 不是拍脑袋发明的又一个花式损失而是一个具备理论解释力的泛化框架。3.3 论文实验里最值得关注的结论论文在细粒度图片检索、行人重识别、人脸识别几个经典 benchmark 上都做了实验。以细粒度检索为例常见的 baseline 是“softmax triplet 联合训练”Circle Loss 把它替换掉之后在多个数据集上都有稳定涨幅。人脸识别任务上Circle Loss 搭配通用 backbone 也能达到和当时主流 margin-based softmax 相当甚至更好的结果。我个人觉得最有价值的实验结论反而不是涨点本身而是一个调参结论Circle Loss 对 margin 的选择不是特别敏感。传统 triplet 的 margin 一旦设大了容易不收敛、设小了又没区分度Circle Loss 因为给s_p和s_n各自设了目标margin 的影响被自适应权重缓解了一大截。我在自己的检索项目里试着把 m 从 0.1 调到 0.4mAP 的波动基本在 1 个点以内这在 triplet 上是很难想象的。4. 真正上手时要注意的事4.1 一个可以直接跑起来的 PyTorch 简化实现论文原版实现要考虑多种相似度来源和类别维度工程上有点绕。我先给一个最直观的简化版适用于“batch 内样本对相似度”这种最常见场景每个样本只属于一个类别一个 batch 里可能出现多个类别。import torch import torch.nn.functional as F def circle_loss(embeddings, labels, m0.25, gamma80): embeddings: (N, D)建议事先已经 L2 归一化 labels: (N,)每个样本的类别标签 device embeddings.device # 相似度矩阵范围在 [-1, 1] sim_mat embeddings embeddings.t() # (N, N) # 去掉对角线自相似度 idx torch.arange(sim_mat.size(0), devicedevice) sim_mat[idx, idx] -1.0 # 让对角线不参与计算 # 构造正负 mask label_mat labels.unsqueeze(1) labels.unsqueeze(0) # (N, N) pos_mask label_mat (sim_mat -1.0) # 排除对角线 neg_mask ~label_mat # 分别取每个 anchor 的正负相似度 s_p torch.where(pos_mask, sim_mat, torch.zeros_like(sim_mat)) s_n torch.where(neg_mask, sim_mat, torch.zeros_like(sim_mat)) # 目标值 O_p 1 m O_n -m loss_list [] for i in range(sim_mat.size(0)): sp s_p[i][pos_mask[i]] # 属于同类的相似度 sn s_n[i][neg_mask[i]] # 属于不同类的相似度 # 自适应权重分子 alpha_p F.relu(O_p - sp) # 正例离目标越远权重越大 alpha_n F.relu(sn - O_n) # 负例离目标越远权重越大 # 归一化权重 alpha_p alpha_p / (alpha_p.sum() 1e-12) alpha_n alpha_n / (alpha_n.sum() 1e-12) # Circle Loss 的对数形式 term_p torch.sum(torch.exp(gamma * alpha_p * (O_p - sp))) term_n torch.sum(torch.exp(gamma * alpha_n * (sn - O_n))) loss_i torch.log(1 term_n * term_p) loss_list.append(loss_i) return torch.stack(loss_list).mean()这个实现里有两个细节值得说明。第一相似度矩阵直接用 embedding 的内积前提是 embedding 已经做过 L2 归一化否则内积不能当作余弦相似度使用。第二权重归一化时加了一个1e-12防止除以零凡是训练初期 anchor 没有正样本或者没有负样本的场景这个保护都很必要。如果觉得循环遍历 batch 太慢可以改成全矩阵向量化版本但思路完全一致。对于 batch size 在 64 到 128 的实验这个循环版也已经足够用。4.2 超参怎么设m 和 gamma 的选择经验论文里推荐的配置是m0.25、gamma80。这个配置在很多任务上都能直接作为一个不错的起点不需要从头开始搜。关于gamma它实际上控制的是损失曲面的锐利程度。gamma太小所有相似度的惩罚都变得平缓训练容易拖沓gamma太大训练初期容易被个别极端样本主导导致不稳定。我试过 32、64、80、128 这几个值64 和 80 在多数数据集上没有本质差别但 128 在 batch size 较小的场景下会遇到偶发震荡。建议优先用 64 或 80再根据收敛速度微调。关于m论文里给的 0.25 是一个兼顾区分度与训练稳定性的值。m可以理解为“类内相似度要比 0 高出多少、类间相似度要比 0 低多少”的强度指标。m偏大能让学到的 embedding 分布更分散但也可能压缩模型的可表达空间m偏小则容易让不同类别的特征挤在一起。先跑 0.25再在 0.1 到 0.4 之间小范围网格搜一下。还有一个容易被忽略的细节gamma和m是联动的。由于O_p 1 m、O_n -m当m变大时正负样本的目标值距离拉大等效于在原始损失上放大了梯度此时可以适当把gamma调小一点来平衡。我一般会固定gamma80只动m直到找到一个合适的点再回头微调gamma。4.3 什么场景适合换 Circle Loss什么场景别硬换Circle Loss 并不是万能药我有几条比较实际的经验判断。如果你的任务是闭集分类类别固定、每个类别样本量足够直接上 ArcFace 或 AMSoftmax 这类成熟的分类损失可能更省事。Circle Loss 相比它们没有压倒性优势还会引入 batch 内相似度矩阵计算徒增复杂度。如果你的任务是检索、验证这种需要学通用 embedding 的场景——比如人脸验证、商品检索、图像检索而且你之前用的是 triplet 或者“softmax triplet”组合那 Circle Loss 是非常值得替换的选项。它直接优化样本对相似度不依赖分类头同时减轻了 triplet 的采样敏感问题。如果你的类别特别多且训练时会动态新增类别——比如增量学习、开放集识别Circle Loss 和大多数 pair-based 方法一样都不适合。因为每次新增类别都需要重新构造正负样本对batch 内随机采样很容易让新类别的样本被淹没。另外Circle Loss 的效果和 batch 构造方式强相关。batch 里每个类别至少要有 2 个样本否则就没有正样本对可算。类别数量太多导致每个类只有 1 个样本的情况代码里会出现空的正样本集loss 直接失效。4.4 我在复现过程中踩过的坑第一个坑是 L2 归一化。Circle Loss 的相似度默认是余弦相似度如果输入 embedding 没做归一化相似度矩阵的范围会膨胀到 [-3, 3] 甚至更大O_p 1 m这个目标值就完全失去意义。我在某个检索项目里第一次直接拿未归一化的 embedding 去算loss 前期震荡得特别夸张后来才发现是这个问题。第二个坑是正负样本对的数量不平衡。如果一个 anchor 只有 1 个正样本却有 99 个负样本那么 alpha 归一化后负样本的权重会被均匀稀释正样本的权重反而变成 1.0优化方向会严重偏向拉高s_p而不去压低s_n。解决方法是保证 batch 内类别均衡采样每个类尽量采相同数量的样本。第三个坑是难例 epoch 的梯度爆炸。Circle Loss 虽然把梯度分配做得更合理了但遇到特别难的负样本比如两个极其相似的类的样本被分到同一个 batchs_n_i - O_n会很大经过gamma80放大后单个负样本可能贡献出非常大的梯度导致 embedding 突然漂移。保险做法是梯度裁剪max_norm1.0或者clip_grad_norm_都能有效控制这种偶发爆炸。第四个坑在实现细节里相似度矩阵对角线必须提前处理。自己和自己算出来的相似度恒为 1.0如果不把它排除掉正样本集合里会混入 anchor 自己等于告诉模型“我和我自己的相似度已经很高了”这会严重干扰正例的权重分布。我的做法是先把对角线强制设成一个不可能被选中的值再生成 mask。5. 局限性和这篇论文带来的更长期影响5.1 它解决不了的问题Circle Loss 的本质仍然是 pair-based 优化所以 pair-based 方法的通病它一个都不少。计算开销是其中一个。在 batch 内构造全量相似度矩阵是 O(N²) 空间复杂度batch size 上到 256 以上显存占用会变得很紧张。对比 AMSoftmax 只需要一次分类头前向计算Circle Loss 的工程成本明显更高。对标签噪声的敏感度也值得注意。triplet loss 对个别噪声标签的容忍度低Circle Loss 因为给每个偏离目标的相似度分配了更大权重遇到标注错误的正负样本对时反而可能把错误拟合得更凶。如果数据集的噪声比例较高最好先做清洗或者搭配标签平滑策略使用。论文实验里还有个现实问题是Circle Loss 的人脸识别结果和 ArcFace 这类专门为闭集分类设计的大间隔损失相比优势非常微弱。在人脸这种类别数可能上十万、每个类别样本数量少的超大规模分类场景中ArcFace 的分类头训练方式依然是最稳妥的选择。Circle Loss 更适合的是真正的开放检索场景而不是闭集分类。5.2 为什么它的思想比涨点更重要这篇论文值得记住的地方不只是一个新的损失函数而是它提供了一种看待所有度量学习损失的方式别被公式的繁复形式迷惑先拆解出它到底在怎么组合s_p和s_n再分析它给了这两个相似度什么样的梯度。从这个视角看回去softmax、triplet、contrastive loss、AMSoftmax、Multi-Similarity loss本质上全都是在定义“类内相似度和类间相似度的相互作用函数”。Circle Loss 的真正贡献是证明了这种拆解是可行的并且在这个拆解之上可以用“分别设目标 自适应权重”的方式获得更灵活的优化行为。后续很多基于相似度分解的损失改进工作都能看到这篇论文思路的影子。我在自己的项目里用过一段时间的 Circle Loss最终印象最深的不是那几个点的 mAP 提升而是它教会了我一个新的习惯看任何 loss 论文的时候先用笔把它的s_p和s_n定义写清楚再判断它到底在优化什么。这种“统一视角”带来的思维习惯比一个现成的损失函数值钱得多。如果你也在做检索或者 embedding 学习我建议不要只把它当一个可选的 loss 来用而是花点时间推一遍它的公式——推导过程本身就能帮你把这一整块损失函数的版图全部理顺。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询