focal loss

Focal Loss for Dense Object Detection

首先,需要了解交叉熵是怎麼工作的:  https://blog.csdn.net/tsyccnh/article/details/79163834

本文的核心公式如下:

借用上面博客中的概率表:
*     貓     青蛙     老鼠
Label     0     1     0
Pred     0.3     0.6     0.1

本文將該論文應用在多分類任務中的類別不平衡問題上.

其中alpha是控制類別不平衡問題的超參數,每個類別對應相應的alpha值,樣本多的對應的alpha小,樣本少的對應alpha大.

gamma是控制難易樣本的超參數,當這個是難樣本時,預測的pt會很低,(1-pt)值就會變高,使得這個損失加大,反之,樣本爲簡單樣本時,pt會很大,而(1-pt)會很小,使得這個損失變得更小.這樣就能使得模型更加關注與難樣本.由於類別不平衡時,少量樣本的類別預測結果更差,因此也能獲得更多的關注.這樣也能起到控制類別不平衡的效果.

例如上面計算結果爲-0.4log(0.6)*alpha.

當青蛙的樣本爲難樣本時,假設預測概率爲0.2.

則計算結果就爲-0.8*log(0.2)*alpha

很明顯結果比普通的交叉熵大.

該篇論文推薦的alpha爲0.25, gamma爲2

 

代碼如下,input爲預測結果,如果不是softmax的預測結果則使用第二個,否則使用第一行代碼.

target爲標籤

    logpt = torch.log(input)
    #logpt = F.log_softmax(input)
    #pt = nn.Softmax()(input)  # N*H*W,C
    pt = pt.gather(1, target).view(-1)
    logpt = logpt.gather(1, target)
    logpt = logpt * alpha
    loss = -1 * (1 - pt)**self.gamma * logpt
---------------------  
作者:一條不愛讀書的鹹魚  
來源:CSDN  
原文:https://blog.csdn.net/a362682954/article/details/85207937  
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

 

https://www.aiuai.cn/aifarm636.html

題目: Focal Loss for Dense Object Detection - ICCV2017

作者: Tsung-Yi, Lin, Priya Goyal, Ross Girshick, Kaiming He, Piotr Dollar

團隊: FAIR

<Detectron>

精度最高的目標檢測器往往基於 RCNN 的 two-stage 方法,對候選目標位置再採用分類器處理. 而,one-stage 目標檢測器是對所有可能的目標位置進行規則的(regular)、密集採樣,更快速簡單,但是精度還在追趕 two-stage 檢測器. <論文所關注的問題於此.>

論文發現,密集檢測器訓練過程中,所遇到的極端前景背景類別不均衡(extreme foreground-background class imbalance)是核心原因.

對此,提出了 Focal Loss,通過修改標準的交叉熵損失函數,降低對能夠很好分類樣本的權重(down-weights the loss assigned to well-classified examples),解決類別不均衡問題.

Focal Loss 關注於在 hard samples 的稀疏子集進行訓練,並避免在訓練過程中大量的簡單負樣本淹沒檢測器. <RetinaNet>

Focal Loss 是動態縮放的交叉熵損失函數,隨着對正確分類的置信增加,縮放因子(scaling factor) 衰退到 0. 如圖:

Focal Loss 的縮放因子能夠動態的調整訓練過程中簡單樣本的權重,並讓模型快速關注於困難樣本(hard samples).

基於 Focal Loss 的 RetinaNet 的目標檢測器表現.

1. Focal Loss

Focal Loss 旨在解決 one-stage 目標檢測器在訓練過程中出現的極端前景背景類不均衡的問題(如,前景:背景 = 1:1000).

首先基於二值分類的交叉熵(cross entropy, CE) 引入 Focal Loss:

 

 

其中,

爲 groundtruth 類別; 是模型對於類別

所得到的預測概率.

符號簡介起見,定義

 

 

則,

.

CE Loss 如圖 Figure 1 中的上面的藍色曲線所示. 其一個顯著特點是,對於簡單易分的樣本(

),其 loss 也是一致對待. 當累加了大量簡單樣本的 loss 後,具有很小 loss 值的可能淹沒稀少的類(rare class).

1.1 均衡交叉熵 Blanced CE

解決類別不均衡的一種常用方法是,對類別 +1 引入權重因子

,對於類別 -1 引入權重

.

符號簡介起見,定義

 

 

則,

-balanced CE loss 爲:

 

 

1.2 Focal Loss 定義

雖然

能夠平衡 positive/negative 樣本的重要性,但不能區分 easy/had 樣本.

對此,Focal Loss 提出將損失函數降低 easy 樣本的權重,並關注於對 hard negatives 樣本的訓練.

添加調製因子(modulating factor)

到 CE loss,其中

爲可調的 focusing 參數.

Focal Loss 定義爲:

 

 

如圖 Figure 1,給出了

中幾個值的可視化.

Focal Loss 的兩個屬性:

  • [1] - 當樣本被誤分,且

值很小時,調製因子接近於 1,loss 不受影響. 隨着

  • ,則調製因子接近於 0,則容易分類的樣本的損失函數被降低權重.
  • [2] - focusing 參數
  • 平滑地調整哪些 easy 樣本會被降低權重的比率(rate). 當 ,FL=CE;隨着 增加,調製因子的影響也會隨之增加(實驗中發現
    • 效果最佳.)

    直觀上,調製因子能夠減少 easy 樣本對於損失函數的貢獻,並延伸了loss 值比較地的樣本範圍.

    例如,

    時,被分類爲 的樣本,與 CE 相比,會減少 100x 倍;而且,被分類爲 的樣本,與 CE 相比,會有少於 1000x 倍的 loss 值. 這就自然增加了將難分類樣本的重要性(如 且

    時,難分類樣本的 loss 值會增加 4x 倍.)

    實際上,論文采用了 Focal Loss 的

    -balanced 變形:

     

     

    1.3. Focal Loss 例示

    Focal Loss 並不侷限於具體的形式. 這裏給出另一種例示.

    假設

    定義

    爲(類似於前面對於

    的定義):

     

     

    定義:

    ,其中,

    是 groundtruth 類別.

    則:

     

    時,樣本被正確分類,此時

    .

    有:

     

     

    對於交叉熵損失函數

    ,由

     

     

    對於 Focal Loss

    ,其中

    爲常數.

     

     

     

     

     

     

     

     

     

     

    再者,假設

    ,則 ,其中

    爲常數.

     

     

     

     

    則,

    包含兩個參數 和

    ,控制着 loss 曲線的陡度(steepness) 和移動(shift). 如 Figure 5.

    1.4. Focal Loss 求導

     

    關於

    的求導:

     

     

     

    關於

    的求導:

     

     

     

    關於

    的求導:

     

     

    如圖 Figure 6. 三種 loss 函數,對於high-confidence 的預測結果,其導數都趨近於 -1 或 0.

    但,與

    不同的是, 和 的有效設置時,只要

    ,二者的導數都是很小的.

    2. SoftmaxFocalLoss 求導

    Focal Loss 損失函數:

     

     

    其中:

     

     

    Softmax 函數:

     

     

    其中,

    爲類別數, 是網絡全連接層等的輸出向量, 是向量的第

    個元素值.

    關於

    求導:

     

     

    而,

     

     

     

     

    Softmax 函數關於 x 的求導爲:

     

     

     

     

    時,

     

     

     

     

     

     

    時,

     

     

     

     

     

     

    Softmax 的函數求導即爲:

     

     

    故:

     

     

     

     

    3. Pytorch 實現

    FocalLoss-PyTorch

    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    
    class FocalLoss(nn.Module):
        def __init__(self, alpha=0.25, gamma=2, size_average=True):
            super(FocalLoss, self).__init__()
            self.alpha = alpha
            self.gamma = torch.Tensor([gamma])
            self.size_average = size_average
            if isinstance(alpha, (float, int, long)):
                if self.alpha > 1:
                    raise ValueError('Not supported value, alpha should be small than 1.0')
                else:
                    self.alpha = torch.Tensor([alpha, 1.0 - alpha])
            if isinstance(alpha, list): self.alpha = torch.Tensor(alpha)
            self.alpha /= torch.sum(self.alpha)
    
        def forward(self, input, target):
            if input.dim() > 2:
                input = input.view(input.size(0), input.size(1), -1)  # [N,C,H,W]->[N,C,H*W] ([N,C,D,H,W]->[N,C,D*H*W])
            # target
            # [N,1,D,H,W] ->[N*D*H*W,1]
            if self.alpha.device != input.device:
                self.alpha = torch.tensor(self.alpha, device=input.device)
            target = target.view(-1, 1)
            logpt = torch.log(input + 1e-10)
            logpt = logpt.gather(1, target)
            logpt = logpt.view(-1, 1)
            pt = torch.exp(logpt)
            alpha = self.alpha.gather(0, target.view(-1))
    
            gamma = self.gamma
    
            if not self.gamma.device == input.device:
                gamma = torch.tensor(self.gamma, device=input.device)
    
            loss = -1 * alpha * torch.pow((1 - pt), gamma) * logpt
            if self.size_average:
                loss = loss.mean()
            else:
                loss = loss.sum()
            return loss

 

(原)SphereFace及其pytorch代碼

轉載請註明出處:

http://www.cnblogs.com/darkknightzh/p/8524937.html

論文:

SphereFace: Deep Hypersphere Embedding for Face Recognition

https://arxiv.org/abs/1704.08063

http://wyliu.com/papers/LiuCVPR17v3.pdf

官方代碼:

https://github.com/wy1iu/sphereface

pytorch代碼:

https://github.com/clcarwin/sphereface_pytorch

 

說明:沒用過mxnet,下面的代碼註釋只是純粹從代碼的角度來分析並進行註釋,如有錯誤之處,敬請諒解,並歡迎指出。

 

傳統的交叉熵公式如下:

Li=−logeWTyixi+byi∑jeWTjxi+bj=−loge‖‖Wyi‖‖‖‖xi‖‖cos(θyi,i)+byi∑je‖‖Wj‖‖‖‖xi‖‖cos(θj,i)+bj

 

將W歸一化到1,且不考慮偏置項,即bj=0

,則上式變成:

Lmodified=1N∑i−log(e‖‖xi‖‖cos(θyi,i)∑je‖‖xi‖‖cos(θj,i))

 

其中θ爲w和x的夾角。

爲了進一步限制夾角的範圍,使用mθ,上式變成

Lang=1N∑i−log(e‖‖xi‖‖cos(mθyi,i)e‖‖xi‖‖cos(mθyi,i)+∑j≠yie‖‖xi‖‖cos(θj,i))

 

其中θ範圍爲[0,πm]

爲了使得上式單調,引入ψ(θyi,i)

Lang=1N∑i−log(e‖‖xi‖‖ψ(θyi,i)e‖‖xi‖‖ψ(θyi,i)+∑j≠yie‖‖xi‖‖cos(θj,i))

 

其中

ψ(θyi,i)=(−1)kcos(mθyi,i)−2k

,θyi,i∈[kπm,(k+1)πm],k∈[0,m−1],m≥1

 

代碼中引入了超參數λ,爲

λ=max(λmin,λmax1+0.1×iterator)

 

其中,λmin=5

,λmax=1500

爲程序中預先設定的值。

實際的ψ(θ)

ψ(θyi)=(−1)kcos(mθyi)−2k+λcos(θyi)1+λ

 

對應下面代碼爲:

output = cos_theta * 1.0
output[index] -= cos_theta[index]*(1.0+0)/(1+self.lamb)
output[index] += phi_theta[index]*(1.0+0)/(1+self.lamb)

對於yi處的計算,

output(yi)=cos(θyi)−cos(θyi)1+λ+ψ(θyi)1+λ=ψ(θyi)+λcos(θyi)1+λ=(−1)kcos(mθyi)−2k+λcos(θyi)1+λ

 

和上面的公式對應。

具體的代碼如下(完整的代碼見參考網址):

複製代碼

 1 class AngleLinear(nn.Module):
 2     def __init__(self, in_features, out_features, m = 4, phiflag=True):
 3         super(AngleLinear, self).__init__()
 4         self.in_features = in_features
 5         self.out_features = out_features
 6         self.weight = Parameter(torch.Tensor(in_features,out_features))
 7         self.weight.data.uniform_(-1, 1).renorm_(2,1,1e-5).mul_(1e5)
 8         self.phiflag = phiflag
 9         self.m = m
10         self.mlambda = [
11             lambda x: x**0,  # cos(0*theta)=1
12             lambda x: x**1,  # cos(1*theta)=cos(theta)
13             lambda x: 2*x**2-1, # cos(2*theta)=2*cos(theta)**2-1
14             lambda x: 4*x**3-3*x,
15             lambda x: 8*x**4-8*x**2+1,
16             lambda x: 16*x**5-20*x**3+5*x
17         ]
18 
19     def forward(self, input):  # input爲輸入的特徵,(B, C),B爲batchsize,C爲圖像的類別總數
20         x = input   # size=(B,F),F爲特徵長度,如512
21         w = self.weight # size=(F,C)
22 
23         ww = w.renorm(2,1,1e-5).mul(1e5) #對w進行歸一化,renorm使用L2範數對第1維度進行歸一化,將大於1e-5的截斷,乘以1e5,使得最終歸一化到1.如果1e-5設置的過大,裁剪時某些很小的值最終可能小於1。注意,第0維度只對每一行進行歸一化(每行平方和爲1),第1維度指對每一列進行歸一化。由於w的每一列爲x的權重,因而此處需要對每一列進行歸一化。如果要對x歸一化,需要對每一行進行歸一化,此時第二個參數應爲0
24         xlen = x.pow(2).sum(1).pow(0.5) # 對輸入x求平方,而後對不同列求和,再開方,得到每行的模,最終大小爲第0維的,即B(由於對x不歸一化,但是計算餘弦時需要歸一化,因而可以先計算模。但是對於w,不太懂爲何不直接使用這種方式,而是使用renorm函數?)
25         wlen = ww.pow(2).sum(0).pow(0.5) # 對權重w求平方,而後對不同行求和,再開方,得到每列的模(理論上之前已經歸一化,此處應該是1,但第一次運行到此處時,並不是1,不太懂),最終大小爲第1維的,即C
26 
27         cos_theta = x.mm(ww) # 矩陣相乘(B,F)*(F,C)=(B,C),得到cos值,由於此處只是乘加,故未歸一化
28         cos_theta = cos_theta / xlen.view(-1,1) / wlen.view(1,-1) # 對每個cos值均除以B和C,得到歸一化後的cos值
29         cos_theta = cos_theta.clamp(-1,1) #將cos值截斷到[-1,1]之間,理論上不截斷應該也沒有問題,畢竟w和x都歸一化後,cos值不可能超出該範圍
30 
31         if self.phiflag:
32             cos_m_theta = self.mlambda[self.m](cos_theta) # 通過cos_theta計算cos_m_theta,mlambda爲cos_m_theta展開的結果
33             theta = Variable(cos_theta.data.acos()) # 通過反餘弦,計算角度theta,(B,C)
34             k = (self.m*theta/3.14159265).floor() # 通過公式,計算k,(B,C)。此處爲了保證theta大於k*pi/m,轉換過來就是m*theta/pi,再向上取整
35             n_one = k*0.0 - 1 # 通過k的大小,得到同樣大小的-1矩陣,(B,C)
36             phi_theta = (n_one**k) * cos_m_theta - 2*k # 通過論文中公式,得到phi_theta。(B,C)
37         else:
38             theta = cos_theta.acos() # 得到角度theta,(B, C),每一行爲當前特徵和w的每一列的夾角
39             phi_theta = myphi(theta,self.m) # 
40             phi_theta = phi_theta.clamp(-1*self.m,1)
41 
42         cos_theta = cos_theta * xlen.view(-1,1)  # 由於實際上不對x進行歸一化,此處cos_theta需要乘以B。(B,C)
43         phi_theta = phi_theta * xlen.view(-1,1)  # 由於實際上不對x進行歸一化,此處phi_theta需要乘以B。(B,C)
44         output = (cos_theta,phi_theta)
45         return output # size=(B,C,2)
46 
47 
48 class AngleLoss(nn.Module):
49     def __init__(self, gamma=0):
50         super(AngleLoss, self).__init__()
51         self.gamma   = gamma
52         self.it = 0
53         self.LambdaMin = 5.0
54         self.LambdaMax = 1500.0
55         self.lamb = 1500.0
56 
57     def forward(self, input, target):
58         self.it += 1
59         cos_theta,phi_theta = input # cos_theta,(B,C)。 phi_theta,(B,C)
60         target = target.view(-1,1) #size=(B,1)
61 
62         index = cos_theta.data * 0.0 #得到和cos_theta相同大小的全0矩陣。(B,C)
63         index.scatter_(1,target.data.view(-1,1),1) # 得到一個one-hot矩陣,第i行只有target[i]的值爲1,其他均爲0
64         index = index.byte() # index爲float的,轉換成byte類型
65         index = Variable(index)
66 
67         self.lamb = max(self.LambdaMin,self.LambdaMax/(1+0.1*self.it))  # 得到lamb
68         output = cos_theta * 1.0 #size=(B,C)  # 如果直接使用output=cos_theta,可能不收斂(未測試,但其他程序中碰到過直接對輸入使用[index]無法收斂,加上*1.0可以收斂的情況)
69         output[index] -= cos_theta[index]*(1.0+0)/(1+self.lamb) # 此行及下一行將target[i]的值通過公式得到最終輸出
70         output[index] += phi_theta[index]*(1.0+0)/(1+self.lamb)
71 
72         logpt = F.log_softmax(output) # 得到概率
73         logpt = logpt.gather(1,target) # 下面爲交叉熵的計算(和focal loss的計算有點類似,當gamma爲0時,爲交叉熵)。
74         logpt = logpt.view(-1)
75         pt = Variable(logpt.data.exp())
76 
77         loss = -1 * (1-pt)**self.gamma * logpt
78         loss = loss.mean()
79         
80         # target = target.view(-1)  # 若要簡化,理論上可直接使用這兩行計算交叉熵(此處未測試,在其他程序中使用後可以正常訓練)
81         # loss = F.cross_entropy(cos_theta, target)
82 
83         return loss
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章