最近在看arcFace當中pytorch代碼的實現,先把InsightFace中ArcFace代碼貼出來:
class Arcface(Module):
# implementation of additive margin softmax loss in https://arxiv.org/abs/1801.05599
def __init__(self, embedding_size=512, classnum=51332, s=64., m = 0.5):
super(Arcface, self).__init__()
self.classnum = classnum
self.kernel = Parameter(torch.Tensor(embedding_size, classnum))
# initial kernel
self.kernel.data.uniform_(-1, 1).renorm_(2, 1, 1e-5).mul_(1e5) #uniform_(-1, 1)服從均勻分佈,mul_對應點相乘
self.m = m # the margin value, default is 0.5
self.s = s # scalar value default is 64, see normface https://arxiv.org/abs/1704.06369
self.cos_m = math.cos(m)
self.sin_m = math.sin(m)
self.mm = self.sin_m * m # issue 1
self.threshold = math.cos(math.pi - m)
def forward(self, embbedings, label):
# weights norm
nB = len(embbedings)
kernel_norm = l2_norm(self.kernel, axis=0)
# cos(theta+m)
cos_theta = torch.mm(embbedings, kernel_norm)#進行矩陣乘法
# output = torch.mm(embbedings,kernel_norm)
cos_theta = cos_theta.clamp(-1,1) # for numerical stability
cos_theta_2 = torch.pow(cos_theta, 2)
sin_theta_2 = 1 - cos_theta_2
sin_theta = torch.sqrt(sin_theta_2)
cos_theta_m = (cos_theta * self.cos_m - sin_theta * self.sin_m)
# this condition controls the theta+m should in range [0, pi]
# 0<=theta+m<=pi
# -m<=theta<=pi-m
cond_v = cos_theta - self.threshold
cond_mask = cond_v <= 0
keep_val = (cos_theta - self.mm) # when theta not in [0,pi], use cosface instead
cos_theta_m[cond_mask] = keep_val[cond_mask]
output = cos_theta * 1.0 # a little bit hacky way to prevent in_place operation on cos_theta
idx_ = torch.arange(0, nB, dtype=torch.long)
output[idx_, label] = cos_theta_m[idx_, label]
output *= self.s # scale up in order to make softmax work, first introduced in normface
return output
對於torch中一些函數的理解
1)對於self.kernel = Parameter(torch.Tensor(embedding_size, classnum))中,Parameter的作用:
首先可以把Parameter理解爲類型轉換函數,將一個不可訓練的類型Tensor轉換成可以訓練的類型parameter並將這個parameter綁定到這個module裏面(net.parameter()中就有這個綁定的parameter,所以在參數優化的時候可以進行優化的),所以經過類型轉換這個self.kernel變成了模型的一部分,成爲了模型中根據訓練可以改動的參數了。使用這個函數的目的也是想讓某些變量在學習的過程中不斷的修改其值以達到最優化。(摘自:原文鏈接:https://blog.csdn.net/qq_36955294/article/details/88117170)
看了torch官網的解釋:
"Variable
的一種,常被用於模塊參數(module parameter
)。
Parameters
是 Variable
的子類。Paramenters
和Modules
一起使用的時候會有一些特殊的屬性,即:當Paramenters
賦值給Module
的屬性的時候,他會自動的被加到 Module
的 參數列表中(即:會出現在 parameters() 迭代器中
)。將Varibale
賦值給Module
屬性則不會有這樣的影響。 這樣做的原因是:我們有時候會需要緩存一些臨時的狀態(state
)"
這句話中,embedding_size = 512,classnum是人臉識別的ID數,先使用orch.Tensor,生成一個512×classnum的張量,然後通過Parameter將這個張量轉化爲可以訓練的模型;
2)對於self.kernel.data.uniform_(-1, 1).renorm_(2, 1, 1e-5).mul_(1e5) 的理解:
#uniform_(from=-1, to=1) → Tensor 將tensor用從均勻分佈中抽樣得到的值填充。
# renorm_返回一個張量,包含規範化後的各個子張量,使得沿着2維劃分的各子張量的1範數小於1e-5
# mul_用標量值1e5
乘以輸入input
的每個元素,並返回一個新的結果張量;
以上是對pytorch中一些函數的理解;
對於arcface公式的代碼實現
對於arcFace的實現實際應該是包括兩部分,第一部分是cosin函數部分;第二部分就是常規的softmax部分;
在pytorch代碼中,第二部分直接有函數實現,是可以直接使用的;所以重點是cosin函數部分的實現;
下面就重點講解記錄一下怎樣一步步的實現第一部分代碼:
1)對Feature進行了l2 norm,對參數也進行了l2 norm.所以權值參數×feature = cos theta
2)將cos theta夾逼到【-1, 1】之間,因爲cos theta的定義域在【0,pi】值域實在【-1,1】之間;
3)計算cos(theta + m)使用到餘弦定理;
4)計算完成後,要判斷theta是否超出範圍,進行數據調整,這一塊的判讀原理在下圖:
(不知道這樣理解是否有錯?望大佬賜教)
判斷後得出一個值爲0或1的mask,通過使用cos_theta_m[cond_mask] = keep_val[cond_mask],將超出範圍的值使用keep_val表示,加入[cond_mask],是將mask爲1(True)位置的元素取出,進行修改;