一、Triplet結構:
triplet loss是一種比較好理解的loss,triplet是指的是三元組:Anchor、Positive、Negative:
整個訓練過程是:
- 首先從訓練集中隨機選一個樣本,稱爲Anchor(記爲x_a)。
- 然後再隨機選取一個和Anchor屬於同一類的樣本,稱爲Positive (記爲x_p)
- 最後再隨機選取一個和Anchor屬於不同類的樣本,稱爲Negative (記爲x_n)
由此構成一個(Anchor,Positive,Negative)三元組。
二、Triplet Loss:
在上一篇講了Center Loss的原理和實現,會發現現在loss的優化的方向是比較清晰好理解的。在基於能夠正確分類的同時,我們更希望模型能夠:1、把不同類之間分得很開,也就是更不容易混淆;2、同類之間靠得比較緊密,這個對於模型的魯棒性的提高也是比較有幫助的(基於此想到Hinton的Distillation中給softmax加的一個T就是人爲的對訓練過程中加上干擾,讓distribution變得更加soft從而去把錯誤信息放大,這樣模型能夠不光知道什麼是正確還知道什麼是錯誤。即:模型可以從僅僅判斷這個最可能是7,變爲可以知道這個最可能是7、一定不是8、和2比較相近,論文講解可以參看Hinton-Distillation)。
迴歸正題,三元組的三個樣本最終得到的特徵表達計爲:
triplet loss的目的就是讓Anchor這個樣本的feature和positive的feature直接的距離比和negative的小,即:
除了讓x_a和x_p特徵表達之間的距離儘可能小,而x_a和x_n的特徵表達之間的距離儘可能大之外還要讓x_a與x_n之間的距離和x_a與x_p之間的距離之間有一個最小的間隔α,於是修改loss爲:
於是目標函數爲:
距離用歐式距離度量,+表示[ *** ]內的值大於零的時候,取該值爲損失,小於零的時候,損失爲零。
故也可以理解爲:
L = max([ ] , 0)
在code中就是這樣實現的,利用marginloss,詳見下節。
三、Code實現:
筆者使用pytorch:
from torch import nn
from torch.autograd import Variable
class TripletLoss(object):
def __init__(self, margin=None):
self.margin = margin
if margin is not None:
self.ranking_loss = nn.MarginRankingLoss(margin=margin)
else:
self.ranking_loss = nn.SoftMarginLoss()
def __call__(self, dist_ap, dist_an):
"""
Args:
dist_ap: pytorch Variable, distance between anchor and positive sample,
shape [N]
dist_an: pytorch Variable, distance between anchor and negative sample,
shape [N]
Returns:
loss: pytorch Variable, with shape [1]
"""
y = Variable(dist_an.data.new().resize_as_(dist_an.data).fill_(1))
if self.margin is not None:
loss = self.ranking_loss(dist_an, dist_ap, y)
else:
loss = self.ranking_loss(dist_an - dist_ap, y)
return loss
理解起來非常簡單,margin這是上面說的和正樣本以及負樣本直接的距離a,margin不爲空時,使用SoftMarginLoss:
與我們要得到的loss類似:當與正例距離+固定distance大於負例距離時爲正值,則懲罰,否則不懲罰。