二分类交叉熵,多分类交叉熵,focal loss
1:二分类交叉熵a) 公式:,其中表示网络预测结果,是一个属于(0到1)的值,我们当然希望它们的值很接近1。是真实标签,因为是二分类,所以,的值为0或者1。网络最后一层一般为sigmoid。比如,网络最后一层sigmoid之后,网络输出为0.8,若= 1,代入公式则loss = -1*log(0.8);若= 0,loss=(1-0)*log(1-0.8)。b) pytorch中的形式:criter
1:二分类交叉熵
a) 公式: ,其中表示网络预测结果,是一个属于(0到1)的值,我们当然希望它们的值很接近1。是真实标签,因为是二分类,所以,的值为0或者1。网络最后一层一般为sigmoid。比如,网络最后一层sigmoid之后,网络输出为0.8,若 = 1,代入公式则loss = -1*log(0.8);若= 0,loss=(1-0)*log(1-0.8)。
b) pytorch中的形式:
criterion1 = nn.BCEWithLogitsLoss()
criterion2 = nn.BCEWithLoss()
loss1 = criterion1(out,)
loss2 = criterion2(,)
c) 计算:
- 第一种情况:out为网络输出,取值不定,任意数都有可能,它只是一个数,也是一个数,期望取值为0或1。
1, = sigmoid(out)
2,根据公式,若取值为0,,若取值为1,,
例1:令out=1.2388,=0,
则,
loss=(1-0)*log(1-0.77535)=1.4932
例2:令out=1.2388,=1,
则,
loss=-1*log(0.77535)=0.2544
例3:令out=1.2388,=0.1,
则,
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个数,是一个tensor,shape=(batch),所有数取值为0或者1。相当于求一个平均,和上面大同小异。
1: = 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) 公式:,网络输出shape为(batch,n),y的shape为(batch)
b) pytorch中的形式:criterion = nn.CrossEntropyLoss()
loss = criterion(,)
c) 计算:out表示网络输出,shape=(batch,class),取值任意,的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) 公式:,和上面的公式一样,只不过,现在把维度扩大,上面只有2个维度,这次有3个,网络输出shape(batch,class,n),y的shape为(batch,n),y取值0到class
b) pytorch中的形式:criterion = nn.CrossEntropyLoss()
loss = criterion(,)
c) 计算:out表示网络输出,shape=(batch,class,n),取值任意,的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) 公式:,其中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为:
b) pytorch中的形式:criterion = nn.CrossEntropyLoss()
loss = criterion(,)
c) 计算:out表示网络输出,shape=(batch,class,rows,cols),取值任意,的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多分类)
公式: ,其中表示网络输出只经过softmax, ,表示的是网络输出经过softmax之后再取负数,再取log。即:交叉熵损失。Focal Loss还等价于。
其中: ,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;
- 计算: 令
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
若:, FLoss = -(1-0.09)^2 * 2.4079 = 1.9939
这种情况属于不好分类的,损失很大,但是FLoss不会把损失减少的很多。(2倍)
特别地,若,则损失基本没有变。
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相比交叉熵多了两个参数。组合成MF。即:
2:对于分类准确的样本 pt->1 ,MF趋近于0。
3:对于分类不准确的样本 pt->0 ,MF趋近于1。
4:即相比交叉熵损失,focal loss对于分类不准确的样本,损失没有改变,对于分类准确的样本,损失会变小。
5:整体而言,相当于增加了分类不准确样本在损失函数中的权重。结论:通过α可以抑制正负样本的数量失衡,通过γ可以控制简单/难区分样本数量失衡
更多推荐
所有评论(0)