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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章