PyTorch无现成cosine_similarity_loss,因nn.CosineSimilarity是度量函数(输出[-1,1]张量),非损失函数;需手动转为标量loss,如1-cos_sim.mean()或结合标签用MSE回归。

PyTorch里没有现成的cosine_similarity_loss,得自己组合
PyTorch不提供名为“余弦相似度损失”的独立Loss类,因为torch.nn.CosineSimilarity本身是**度量函数**(返回相似度值,范围[-1,1]),不是损失函数(需要最小化)。直接拿它当loss用会出错:比如输入batch为2时,输出是长度为2的tensor,而标准loss函数(如nn.MSELoss)要求标量或可求均值的张量。
常见错误现象:RuntimeError: 0D or 1D target tensor expected, multi-dimensional tensor given——这是把nn.CosineSimilarity的输出直接喂给nn.BCELoss或没做reduce导致的。
实操建议:
- 用
nn.CosineSimilarity(dim=1)计算每对向量的相似度,再用1 - similarity转为距离型loss(越小越好) - 对结果调用
.mean()得到标量loss,适配optimizer.step() - 若需支持标签监督(如相似对为1、不相似为0),则改用
nn.BCEWithLogitsLoss配合cosine_sim输出(此时需先sigmoid或保留logits)
最简可用的自定义余弦损失实现
以下是最小可行代码,适用于对比学习中拉近正样本对、推远负样本对的基础场景:
立即学习“Python免费学习笔记(深入)”;
import torch
import torch.nn as nn
<p>class CosineSimilarityLoss(nn.Module):
def <strong>init</strong>(self, reduction='mean'):
super().<strong>init</strong>()
self.cosine_sim = nn.CosineSimilarity(dim=1, eps=1e-8)
self.reduction = reduction</p><pre class="brush:php;toolbar:false;">def forward(self, x1, x2, target=None):
# x1, x2: [B, D], target: [B], 取值为1(相似)或0(不相似)
cos_sim = self.cosine_sim(x1, x2) # [B]
if target is None:
# 无监督:最大化相似度 → 最小化 (1 - cos_sim)
loss = 1 - cos_sim
else:
# 有监督:target=1希望cos_sim≈1,target=0希望cos_sim≈-1
# 转为二分类任务:用 -cos_sim 作为logits(越大越不相似),或用 cos_sim 直接回归
# 这里采用更稳定的回归方式:MSE(cos_sim, target * 2 - 1)
labels = 2 * target.float() - 1 # [B] → {1, -1}
loss = (cos_sim - labels) ** 2
return getattr(torch, self.reduction)(loss) if self.reduction == 'mean' else loss注意:dim=1表示在特征维度(即向量长度方向)做余弦计算,适用于[batch, dim]输入;若你的输入是[seq_len, batch, dim],要改成dim=2。
和nn.TripletMarginLoss混用时的坑
很多用户想用余弦相似度替代欧氏距离做triplet loss,但nn.TripletMarginLoss默认基于L2距离,不能直接塞进cosine相似度值。强行替换会导致梯度反传异常或收敛失败。
正确做法是重写triplet逻辑:
- 用
nn.CosineSimilarity(dim=1)分别算pos_sim = cos(a, p)和neg_sim = cos(a, n) - triplet loss本质是
max(0, margin + neg_sim - pos_sim)(注意符号反转:cos越大越相似) - margin值通常设为0.1~0.5,不宜过大,否则
neg_sim - pos_sim容易恒为负,loss长期为0 - 避免对
pos_sim和neg_sim单独取1 - x再套用原triplet公式——这会破坏cosine本身的几何意义
性能与数值稳定性要点
nn.CosineSimilarity内部做了归一化,但若输入含大量零向量或极小范数向量,仍可能触发除零警告或nan梯度。
实操建议:
- 在送入前检查范数:
torch.norm(x, dim=1) ,对异常样本做clip或mask - 显式加
eps=1e-8参数(如示例所示),比默认1e-6更稳妥 - 训练初期cosine值集中在0附近,loss下降慢属正常;不要因此过早降低学习率或切换优化器
- FP16训练下,
cosine_sim输出可能因精度丢失出现略大于1或小于-1的值(如1.0001),用torch.clamp(cos_sim, -1.0, 1.0)预处理更安全
真正难的不是写这几行代码,而是确定你的任务到底需要“相似度最大化”还是“距离最小化”,以及标签语义是否和cosine的[-1,1]区间对齐。这点错,后面全白调。


















