1:二分类交叉熵

a) 公式-y*log(\widetilde{y}) - (1-y)*log(1-\widetilde{y})  ,其中\small \widetilde{y}表示网络预测结果,是一个属于(0到1)的值,我们当然希望它们的值很接近1。\small y是真实标签,因为是二分类,所以,\small y的值为0或者1。网络最后一层一般为sigmoid。比如,网络最后一层sigmoid之后,网络输出为0.8,若 \small y= 1,代入公式则loss = -1*log(0.8);若\small y= 0,loss=(1-0)*log(1-0.8)。


b) pytorch中的形式:

                                        criterion1 =   nn.BCEWithLogitsLoss() 

                                        criterion2 =   nn.BCEWithLoss() 

                                        loss1        =   criterion1(out,\small y)

                                        loss2        =   criterion2(\small \widetilde{y},\small y)


c) 计算:

  •      第一种情况:out为网络输出,取值不定,任意数都有可能,\widetilde{y}只是一个数,\tiny y也是一个数,期望\tiny y取值为0或1。

             1, \widetilde{y} = sigmoid(out)

             2,根据公式,若\tiny y取值为0,loss=\tiny -(1-y)*log(1-\widetilde{y}),若\tiny y取值为1,loss=\tiny -(y)*log(\widetilde{y})

                例1:令out=1.2388,\tiny y=0

                                则\widetilde{y}=\tiny \tfrac{1}{1+e^{-1.2388}}=0.77535,      

                                loss=(1-0)*log(1-0.77535)=1.4932

                 例2:令out=1.2388,\tiny y=1

                                 则\widetilde{y}=\tiny \tfrac{1}{1+e^{-1.2388}}=0.77535,      

                                 loss=-1*log(0.77535)=0.2544

                 例3:令out=1.2388,\tiny y=0.1

                                 则\widetilde{y}=\tiny \tfrac{1}{1+e^{-1.2388}}=0.77535,

                                 loss=-(1-0.1)*log(1-0.77535)-0.1*log(0.77535) = 1.3694

                3,代码实现

import torch.nn as nn
import math
import torch
criterion1 =   nn.BCEWithLogitsLoss()
criterion2 =   nn.BCELoss()
def softmax(a):
    return 1/(1+1/math.pow(math.e,a))

out=1.2388
y = 0 #
y_ = softmax(out) # 也可用y_= torch.sigmoid(out)代替
print(y_,y)
# 0.7753550678315562, 0
out = torch.Tensor([out])
y_ = torch.Tensor([y_])
y = torch.Tensor([y])
print(out,y_,y)                    # tensor([1.2388]) tensor([0.7754]) tensor([0.])
print(out.shape,y_.shape,y.shape)  # torch.Size([1]) torch.Size([1]) torch.Size([1])
print(out.dtype,y_.dtype,y.dtype)  # torch.float32 torch.float32,torch.float32
loss1 = criterion1(out,y)   # tensor(1.4932),没经过softmax
loss2 = criterion2(y_,y)    # tensor(1.4932),经过softmax
# 若y=1
loss1 = criterion1(out,y)   # tensor(0.2544)
loss2 = criterion2(y_,y)    # tensor(0.2544)
# # 若y=0.1
loss1 = criterion1(out,y)   # tensor(1.3694)
loss2 = criterion2(y_,y)    # tensor(1.3694)
  •      第二种情况:out是一个tensor,shap=(batch),batch个数,\tiny y是一个tensorshape=(batch),所有数取值为0或者1。相当于求一个平均,和上面大同小异。

               1:\tiny \widetilde{y} = Sigmoid(out)

                2:对tensor里每个数都求一次交叉熵。和上面步骤2一样。

                 3:对所有的loss求平均。

criterion1 =   nn.BCEWithLogitsLoss()
criterion2 =   nn.BCELoss()
out=[1.2388,3.8811,5.1488] 
y = [1,0,1]
out= torch.Tensor(out)  #tensor([0.7754, 0.9798, 0.9942])
y_ = torch.sigmoid(out)
y = torch.Tensor(y)
print(out,y)         #tensor([1.2388, 3.8811, 5.1488]), tensor([1., 0., 1.])
criterion1(out,y)    #tensor(1.3872)
criterion2(y_,y)     #tensor(1.3872)
'''
    #手动计算方法
    1:out=[0.7754, 0.9798, 0.9942],y=[1,0,1]
    2:交叉熵的公式:
        第1个值:-log(0.7754) = 0.25437
        第2个值:-(1-0)*log(1-0.9798)=3.90207
        第3个值:-log(0.9942)=0.00581
    3:求均值:(0.25437+3.90207+0.00581)/3=1.3874
        
'''
#####################################################################
# 增加维度
out=[[1.2388,3.8811,5.1488],
     [2.2388,4.8811,6.1488],
     [7.2388,8.8811,9.1488]]
y = [[1,0,1],[0,0,1],[1,1,0]]
out= torch.Tensor(out)
y = torch.Tensor(y)
y_ = torch.sigmoid(out)
#tensor([[0.7754, 0.9798, 0.9942],[0.9037, 0.9925, 0.9979],[0.9993, 0.9999, 0.9999]])
criterion1(out,y)    #tensor(2.2825)
criterion2(y_,y)     #tensor(2.2825)
'''
    #手动计算方法
    1:
        out = [[0.7754, 0.9798, 0.9942],[0.9037, 0.9925, 0.9979],[0.9993, 0.9999, 0.9999]]
        y = [[1,0,1],[0,0,1],[1,1,0]]
    2:
        由交叉熵的公式:
        第1行:-log(0.7754)-log(1-0.9798)-log(0.9942) = 4.16226
        第2行:-log(1-0.9037)-log(1-0.9925)-log(0.9979) = 7.23524
        第3行:-log(0.9993)-log(0.9999)-log(1-0.9999)=9.2111
    3:均值
        (4.16226+7.23524+9.2111)/9 = 2.2898(稍微有点误差,误差来源out精度被裁)
    
'''

2:多分类交叉熵1

 a) 公式:-\frac{1}{n}\sum_{i=1}^{n}\sum_{j=1}^{k} y_{_{_{ij}}}*log(\widetilde{y_{_{_{ij}}}}),网络输出shape为(batch,n),y的shape为(batch)

b) pytorch中的形式:criterion = nn.CrossEntropyLoss()

                                        loss = criterion(\small \widetilde{y},\small y)

c) 计算:out表示网络输出,shape=(batch,class),取值任意,\small y的shape=(batch),取值为[0,class]的整数。

例子:out.shape=(1,7)

                out = tensor([[0.4100,0.2784,0.8864,0.2563,0.6145,0.1003,0.2250]])

                 y   =  tensor([1.4966]) shape为(1)

      1:out进行softmax,再去对数log。具体操作是,对每一列的3个数字,进行softmax,使其3个数的概率加起来等于1,然后对这3个数取对数log。得到如下的结果(pytorch中代码是out.log_softmax(1))。

                out_ls = tensor([[-1.9652, -2.0968, -1.4888, -2.1189, -1.7607, -2.2749, -2.1502]])

      2:将y变成y.long() 则y=tensor([1])。y的取值范围是【0-6】,不然报错。

      3:y的取值,解释一下,1表示第要从out_ls的第1=(0+1)个元素中取。取到的值为:-2.0968。

在pytorch中的代码是:loss_tmp = nll_loss(log_softmax(out,1))

      4:将上面得到的数字取反。最终结果为: 2.0968.

criterion = nn.CrossEntropyLoss()
out = [[0.4100, 0.2784, 0.8864, 0.2563, 0.6145, 0.1003, 0.2250]]
y = [1.4966]
out = torch.Tensor(out)
y = torch.Tensor(y)
y = y.long() # tensor([1])
print(y.shape,out.shape,y.dtype,out.dtype)
# torch.Size([1, 7]) torch.Size([1, 3, 7]) torch.float32 torch.float32
out_ls = out.log_softmax(1)
# tensor([[-1.9652, -2.0968, -1.4888, -2.1189, -1.7607, -2.2749, -2.1502]])
criterion(out,y)    #tensor(2.0968)

总结:因为out的shape(batch,c)这里是(1,7),可以理解为,这属于1个样本,这个样本会输出一个7维的向量。

1:对这7个数字进行softmax,通过预测值可以判断这个样本属于哪个类别。

2:与真值y做交叉熵,得到loss

3:多分类交叉熵2

a) 公式:-\frac{1}{n}\sum_{i=1}^{n}\sum_{j=1}^{k} y_{_{_{ij}}}*log(\widetilde{y_{_{_{ij}}}}),和上面的公式一样,只不过,现在把维度扩大,上面只有2个维度,这次有3个,网络输出shape(batch,class,n),y的shape为(batch,n),y取值0到class


b) pytorch中的形式:criterion = nn.CrossEntropyLoss()

                                        loss = criterion(\small \widetilde{y},\small y)


c) 计算:out表示网络输出,shape=(batch,class,n),取值任意,\small y的shape=(batch,n),取值为[0,class]的整数。

例子:out.shape=(1,3,7)

                out = tensor([[[0.4100,0.2784,0.8864,0.2563,0.6145,0.1003,0.2250],

                                       [0.8236,0.3911,0.7626,0.4091,0.5717,0.1733,0.7634],

                                       [0.6580,0.9722,0.0596,0.5479,0.9591,0.5731,0.7304]]])

                   y   =  tensor([[0.4966,2.4566,0.1086, 1.6627,0.3579,0.6607,0.3494]]) shape为(1,7)

      1:out进行softmax,再去对数log。具体操作是,对每一列的3个数字,进行softmax,使其3个数的概率加起来等于1,然后对这3个数取对数log。得到如下的结果(pytorch中代码是out.log_softmax(1))。

                out_ls = tensor([[[-1.3333,-1,4160,-0.8420,-1.2538,-1.2148,-1.3030,-1.4750],

                                          [-0.9197,-1.3033,-0.9658,-1.1010,-1.2575,-1.2300,-0.9366],

                                          [-1.0854,-0.7222,-1.6688,-0.9622,-0.8702,-0.8302,-0.9696]]])

      2:将y变成y.long() 则y=tensor([[0,2,0,1,0,0,0]])

      3:y的取值,解释一下,0,2,0,1,0,0,0表示第1个元素要从第1=(0+1)行取,第2个元素要从第3=(2+1)行取,第3个元素要从第1=(0+1)行取,第4个元素要从第2=(1+1)行取,第5个元素要从第1=(0+1)行取,以此类推。最终的结果                                       [-1.3333,-0.7222,-0.8420,-1.1010,-1.2148,-1.3030,-1.4750]

在pytorch中的代码是:loss_tmp = nll_loss(log_softmax(out,1))

      4:将上面得到的数字取反,再求平均值。最终结果为:1.1416

criterion = nn.CrossEntropyLoss()
out = [[[0.4100, 0.2784, 0.8864, 0.2563, 0.6145, 0.1003, 0.2250],
        [0.8236, 0.3911, 0.7626, 0.4091, 0.5717, 0.1733, 0.7634],
        [0.6580, 0.9722, 0.0596, 0.5479, 0.9591, 0.5731, 0.7304]]]
y = [0.4966,2.4566,0.1086, 1.6627,0.3579,0.6607,0.3494]
out = torch.Tensor(out)
y = torch.Tensor(y)
y = y.long() # tensor([[0, 2, 0, 1, 0, 0, 0]])
print(y.shape,out.shape,y.dtype,out.dtype)
# torch.Size([1, 7]) torch.Size([1, 3, 7]) torch.float32 torch.float32
criterion(out,y)    #tensor(1.1416)



总结:因为out的shape(batch,c,n)这里是(1,3,7),可以理解为,这属于1个样本,这个样本会输出一个3*7维的向量,因为标签shape是(1,7)。可以知道,有3个类别。

  • 扩展一下啊out的shape可以是(batch,nclass,d1,d2,d3,....),y对应的shape是(batch,d1,d2,d3,....),y比out的shape少一个维度!!!

4:多分类交叉熵3

a) 公式:-\frac{1}{n}\sum_{i=1}^{n}\sum_{j=1}^{k} y_{_{_{ij}}}*log(\widetilde{y_{_{_{ij}}}}),其中n表示一个batch中有n个样本,k表示任务是k分类的,如果手写数字mnist分类,k就等于10。比如以5分类为例,网络的最后一层softmax输出是(0.1,0.3,0.4,0.1,0.1),标签是(0,1,0,0,0),则最后的损失loss为:

       \small loss=-[0*log(0.1)+1*log(0.3)+0*log(0.4)+0*log(0.1)+0*log(0.1)]=0.9163


b) pytorch中的形式:criterion  = nn.CrossEntropyLoss()

                                        loss  =  criterion(\small \widetilde{y},\small y)


c) 计算:out表示网络输出,shape=(batch,class,rows,cols),取值任意,\small y的shape=(batch,rows,cols),取值为[0,class]的整数。y比out的shape少一个维度!!!

     例子:out.shape=(1,3,7,7),现在模拟的是一张图片,宽高为7*7,深度为3,batch为1

                   y   =  tensor([[[ 1.1550,1.5795,1.2852,1.9990,1.8652,1.7475,1.0594],
                                           [ 2.0696,2.2433,2.5151,2.8715,2.6185,2.4253,2.9496],
                                           [ 1.8982,1.9049,1.8766,1.4609,1.4692,1.9599,1.9277],
                                           [ 0.6416,0.7500,0.5921,0.1870,0.7028,0.8485,0.8178],
                                           [ 0.5703,0.2703,0.1175,0.0287,0.0178,0.7828,0.3572],
                                           [ 0.2374,0.0806,0.8449,0.9463,0.8729,0.3430,0.3805],
                                           [ 0.2683,0.4941,0.0417,0.1650,0.7390,0.5697,0.1700]]])

      1:out进行softmax,再去对数log。具体操作是,(类似上图标红的3个数),进行softmax,使其3个数的概率加起来等于1,得到如下结果,可以看到下图圈出的3个数,加起来概率为1。

               然后对这3个数取对数log。得到如下的结果(pytorch中代码是out.log_softmax(1))。

      2:将y变成y.long() 则y=tensor([1,1,1,1,1,1,1]
                                                           [2,2,2,2,2,2,2]
                                                           [1,1,1,1,1,1,1]
                                                           [0,0,0,0,0,0,0]
                                                           [0,0,0,0,0,0,0]
                                                           [0,0,0,0,0,0,0]
                                                           [0,0,0,0,0,0,0]),

         y的值肯定是0,1,2,不会出现其他的数,不然就会报错。

      3:y的取值,解释一下,我为了好计算,将y取值第一行全部为1,第二行全部为2,第三行全部为1 ,剩下的行全部为0,实际情况有可能是0,1,2,这三个数字随机出现,肯定不会像上面那样这么有规律,但是这不影响计算。我们现在要构造一个矩阵,shape是(1*7*7)。全部元素都从out矩阵中去取,而out的shape是(1*3*7*7),直白一点,说明y只有1个7*7的矩阵,但是out有3个7*7的矩阵,这就说明,只需要取其中的三分之一。我们现在开始取元素,因为y的第一行全是1,表示第一行的元素要从out的第2(1+1)个矩阵中取,同样第二行全是2,说明第二行的元素要从out的第3(2+1)个矩阵中取,第三行全是1,说明第三行的元素要从out的第2(1+1)个矩阵中取。第四到七行的元素为0,说明它们的元素要从第1(0+1)个矩阵中取。

实际情况可能第一行不是(1,1,1,1,1,1,1),这么有规律。举个例子假如是(2,0,1,1,0,0,2),那最终取的元素为(-1.3215,-0.9459,-1.0012,-1.0004,-0.6489,-1.5278,-1.5021),可以发现,这七个元素都在out的三个矩阵的第一行。

          在pytorch中的代码是:loss_tmp = nll_loss(log_softmax(out,1)),示意图如下。

         最后得到的矩阵是:

      4:将上面得到的数字取反,再求平均值。最终结果为:1.1080。

总计:对于多分类交叉熵来说,一般out有三种形式(batch,class),(batch,class,n),(batch,class,h,w)。对于的标签分别为(batch),(batch,n),(batch,h,w)。本文中的三个例子分别是,out是(1,7)(1,3,7)和(1,3,7,7),可以发现,标签和out的区别就是维度少了1个,标签分别是(1),(1,7)和(1,7,7)。这三种形式分布对应于基本元素是点,线,面。点就是说一个数字代表一个基本运算单元,线表示一个行向量代表一个基本运算单元,面表示一个矩阵代表一个基本运算单元,比如7*7,可以表示一个图像,这样就可以计算图像和图像之间的交叉熵,多用于像素分类。

5:Focal Loss多分类)

公式: \small FL(pt) = \alpha (1-pt)^{\gamma }*CELoss,其中\small pt表示网络输出只经过softmax, \small CELoss=-log(pt),表示的是网络输出经过softmax之后再取负数,再取log。即:交叉熵损失。Focal Loss还等价于\small FL(pt) = -\alpha (1-pt)^{\gamma }*log(pt)

其中:   pt = \left\{\begin{matrix} p,(y=1) & \\ 1-p ,(y=0) & \end{matrix}\right.,p为预测值,经过softmax的输出值。

pt反映了与ground truth(即类别y)的接近程度,

1:若y为1,p为0.91,则pt为0.91;

2:若y为1,p为0.09,则pt为0.09;

3:若y为0,p为0.91,则pt为0.09;

4:若y为0,p为0.11,则pt为0.91;

  • 计算: 令\small \alpha=0.5 , \gamma =2

1: ​​​​​​​CELoss = -log(pt) = 0.0943,   FLoss = -0.5*(1-0.91)^2 * 0.0943 = 0.0003819

       这种情况属于好分类的,所以会把类别损失从0.0943降低到0.0003819。(100多倍)

2:​​​​​​​CELoss = -log(pt) = 2.4079   FLoss = -0.5*(1-0.09)^2 * 2.4079 = 0.9969

        若:\alpha =1,\gamma =2,   FLoss​​​​​​​ = -(1-0.09)^2 * 2.4079 = 1.9939

       这种情况属于不好分类的,损失很大,但是FLoss不会把损失减少的很多。(2倍)

       特别地,若\alpha=1,则损失基本没有变。

3:CELoss = -log(pt) = 2.4079   FLoss = -0.5*(1-0.09)^2 * 2.4079 = 0.9969,(同情况2)

4:CELoss = -log(pt) = 0.0943,   FLoss = -0.5*(1-0.91)^2 * 0.0943 = 0.0003819,(同情况2)

  • 说明一下:为什么引入pt。

原本网络输出为p,交叉熵是由网络输出p和真值y来决定的。

CELoss = -(1-y)*log(1-p)-y*log(p),这个公式可以转化成下面的公式。因为另外一项就是0了,可以代入算一下。

1:当y=0的时候,CELoss = -(1-y)*log(1-p)

2:当y=1的时候,CELoss = -y*log(p)

则此时引入一个变量pt,当y=0时,pt=1-p;当y=1时,pt=p,pt有两种解释。

第一种就是替换上面的两个公式,这样就可以统一交叉熵的公式:CELoss = -log(pt)

第二种就是,pt可以表示与y的接近程度。p表示正例的概率,当y为0时,和正例接近程度是(1-p),当y为1时,接近程度就是p。

FocalLoss总结:

1:focal loss相比交叉熵多了两个参数\alpha,\beta。组合成MF。即:\alpha *(1-pt)^{\gamma }
2:对于分类准确的样本 pt->1 ,MF趋近于0。
3:对于分类不准确的样本 pt->0 ,MF趋近于1。
4:即相比交叉熵损失,focal loss对于分类不准确的样本,损失没有改变,对于分类准确的样本,损失会变小。 
5:整体而言,相当于增加了分类不准确样本在损失函数中的权重。

结论:通过α可以抑制正负样本的数量失衡,通过γ可以控制简单/难区分样本数量失衡

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐