在pytorch中,提供了两个损失函数,都与triplet loss相关。但是使用的方式不一样。
这个就是最正宗的Triplet Loss的实现。它的输入是anchor, positive, negative三个B*N的张量(表示Batchsize个N为的特征向量),输出triplet loss的值。
criterion = torch.nn.TripletMarginLoss(margin=1.0, p=2.0, eps=1e-06, swap=False, size_average=None, reduce=None, reduction='mean')
参数:
margin (float) – 默认为1
p (int) – norm degree,默认为2
reduction (string) – 指定返回各损失值(none),批损失均值(mean),批损失和(sum),默认返回批损失均值(mean)
loss = criterion(anchor, positive, negative)
输入三个二维张量,每个向量的尺寸:(B,N),B为批量大小,N为张量维度
最后对这B个loss,求平均或者求和(根据reduction的设置不同而定)
import torch
criterion = torch.nn.TripletMarginLoss(margin=0.3, p=2, reduction='mean')
anchor = torch.Tensor([[1, 0, 1], [1,1,1]]) #2*3的向量,表示batchsize=2,3维向量。
positive = torch.Tensor([[4, 1, 5], [2,2,2]])
negative = torch.Tensor([[3, 1, 2], [1,1,2]])
loss = criterion(anchor, positive, negative)
print(loss)
结果为:
具体怎么算出来的呢?
对于第一组向量:a = [1,0,1], p=[4,1,5], n =[3,1,2]
dap = math.sqrt((1-4)^2 + (0-1)^2 + (1-5)^2 ) = math.sqrt(9 +1 + 16) = 5.099
dan = math.sqrt((1-3)^2 + (0-1)^2 + (1-2)^2 ) = math.sqrt(4 + 1 +1) = 2.4495
max (dap - dan + 0.3 , 0 ) = 2.9495
对于第二组向量:a = [1,1,1], p=[2,2,2], n =[1,1,2]
dap = math.sqrt((1-2)^2 + (1-2)^2 + (1-2)^2) = math.sqrt(3) = 1.732
dan = math.sqrt((1-1)^2 + (1-1)^2 + (1-2)^2) = 1
max(dap - dan + 0.3, 0) = 1.032
最后求平均:(2.9495 + 1.032)/2 = 1.9908
torch.nn.MarginRankingLoss(margin=0.0, size_average=None, reduce=None, reduction='mean')
loss = criterion(x1, x2, y)
x1、x2、y都是一个长度为B的一维向量。
import torch
criterion = torch.nn.MarginRankingLoss(margin=0.3, reduction='mean')
x1 = torch.Tensor([3, 2])
x2 = torch.Tensor([1, 4])
y = torch.Tensor([1, 2])
loss = criterion(x1, x2, y)
print(loss)
输出:
对于第1个点,x1=3, x2 = 1, y=1。 max(-y*(x1-x2), 0) = max(-1*(3-1)+0.3, 0) = max(-1.7, 0) = 0
对于第2个点,x1=2, x2 = 4, y=2。 max(-y*(x1-x2), 0) = max(-2*(2-4), 0) = max(4.3, 0) = 4.3
求平均:2.15
nn.marginRankingLoss不是专门用来计算triplet loss的,但是也可以用来计算triplet loss。
当用来计算triplet loss时, 与TripletMarginLoss的不同是,它的输入是不再是三个原始的向量anchor, positive, negative,而是计算好dap和dan的值,再输入进去。
在nn.marginRankingLoss的公式中:
如果让,y=全1向量, x1 = dan, x2 = dap ,那么:
loss = max(0, -(dan - dap) + margin) = max(dap - dan + margin, 0)
就刚好是triplet loss的公式。
举例:
import torch
'''
anchor = torch.Tensor([[1, 0, 1], [1,1,1]]) #2*3的向量,表示batchsize=2,3维向量。
positive = torch.Tensor([[4, 1, 5], [2,2,2]])
negative = torch.Tensor([[3, 1, 2], [1,1,2]])
'''
criterion = torch.nn.MarginRankingLoss(margin=0.3, reduction='mean')
dap = torch.Tensor([5.099, 1.732]) #dap是一个B维的向量。如[a1,a2,a3],a1表示anchor1与positive1的距离,a2表示anchor2与positive2的距离,...
dan = torch.Tensor([2.4495, 1]) #dan是一个B维的向量。如[b1,b2,b3],b1表示anchor1与negative1的距离,b2表示anchor2与negative2的距离,...
y = torch.Tensor([1, 1])
loss = criterion(dan, dap, y) #注意dan在前,dap在后
print(loss) #1.99
结果:
与TripletMarginLoss中的例子的结果相同。
注意: 在计算triHard loss的时候,我们就用到了nn.marginRankingLoss来求,因为我们求困难样本的时候已经知道了距离矩阵,所以可以直接用nn.marginRankingLoss求更方便,二如果调用TripletMarginLoss的话会再求一遍距离。见实现:triHard loss的原理与实现