目录
1. BCELoss
2. CELoss
3. MSELoss
4. FocalLoss
5. DiceLoss
1. BCELoss
用于二分类任务,二值交叉熵(Binary Cross Entropy)。
公式如下,其中y是真实值,
是预测值:
注意:
- 真实值需要经过onehot编码成0,1值;
- 预测值可以通过sigmoid成为(0,1)区间;
- 真实值和预测值的维度一样。
使用方式如下:
class torch.nn.BCELoss
Examples::
>>> loss = nn.BCELoss()
>>> activation = nn.Sigmoid() # to [0, 1]
>>> input = torch.randn(3, requires_grad=True) # 生成3个随机数,shape = torch.Size([3])
>>> target = torch.empty(3).random_(2) # 生成3个数,值为0 或者 1,shape = torch.Size([3])
>>> output = loss(activation(input), target) # m(input)激活,生成概率p,区间为(0, 1)
>>> output.backward()
2. CELoss
用于多分类任务,交叉熵(Cross Entropy)。
公式如下,其中y是真实值,
是预测值:
使用方式如下:
class torch.nn.CrossEntropyLoss
It is useful when training a classification problem with `C` classes.
The `input` is expected to contain scores for each class.
Examples::
>>> loss = nn.CrossEntropyLoss()
>>> input = torch.randn(3, 5, requires_grad=True) # 3行5列,每一行代码当前数据特征
>>> target = torch.empty(3, dtype=torch.long).random_(5) # 3个数,值范围是0,1...,4
>>> output = loss(input, target)
>>> output.backward()
支持多目标分割、多分类
def forward(self, predictive, target):
"""
:param predictive: 分类任务:(b,c), 语义分割任务: (b,c,h,w)
:param target: 分类任务:(b,), 语义分割任务: (b,c,h,w)
:return:
"""
# seg: predictive.shape [b,c,h,w] -> [n,c]. target.shape [b,c,h,w] -> [n, ]
# cls: predictive.shape [b,c] -> [n,c]. target.shape [b,]-> [n, ]
c = predictive.size()[1]
if len(predictive.shape) == 4: # seg, [b,c,h,w] -> [b*h*w, c]
predictive = predictive.transpose(1, 2).transpose(2, 3).contiguous().view(-1, c)
target = torch.argmax(target, dim=1) # 输入是one hot. [b,c,h,w] -> [b, h, w]
target = target.view(-1) # [b, h, w] -> (n, )
# predictive.shape == [n, c], target.shape == [n,]
# cross_entropy will take one-hot operation and change target.shape to [n, c]
return F.cross_entropy(predictive, target.long(), weight=self.weight, reduction=self.reduction)
3. MSELoss
计算均方误差 Mean Squared Error (squared L2 Norm)。
公式如下,其中y是真实值,
是预测值:
class torch.nn.MSELoss
Creates a criterion that measures the mean squared error (squared L2 norm) between
each element in the input `x` and target `y`.
Examples::
>>> loss = nn.MSELoss()
>>> input = torch.randn(3, 5, requires_grad=True)
>>> target = torch.randn(3, 5)
>>> output = loss(input, target)
>>> output.backward()
4. FocalLoss
https://arxiv.org/pdf/1708.02002.pdf
二分类任务,Focal loss主要是为了解决one-stage目标检测中正负样本比例严重失衡的问题。该损失函数降低了大量简单负样本在训练中所占的权重,也可理解为一种困难样本挖掘。
Focal loss 修改自 cross entropy loss for binary classfication (严格来讲是BCE,而不是CE),二值交叉熵:
可见普通的交叉熵对于正样本而言,输出概率越大损失越小。对于负样本而言,输出概率越小则损失越小。
公式4是y = 1时的Focal loss和预测概率p之间关系。其中,gamma=0时,即为二值交叉熵函数loss=-log
, y = 1。Focal loss只是在原有函数上加个权重,我们可以单独画出y = 1的权重函数图像:
原始的BCE Loss函数如下左图,加上上图紫色线的权重后,Focal Loss如下右图:
画图所用到的代码:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 1)
y1, y2, y3, y4, y5 = (1-x)**0, (1-x)**0.5, (1-x)**1, (1-x)**2, (1-x)**5
plt.plot(x, y1, 'red')
plt.plot(x, y2, 'green')
plt.plot(x, y3, 'blue')
plt.plot(x, y4, 'yellow')
plt.plot(x, y5, 'purple')
# plt.title('line chart')
plt.xlabel('probability of ground truth class')
plt.ylabel('Weight Value')
plt.show()
注意:用权重函数,加权BCELoss,则生成论文中的函数图像。由上图可知,其中gamma=2时,权重函数是个单调下降函数,预测的概率值较小时(即为难样本),Focal Loss所加的权重较大,使得整体loss变大,突出难样本;预测值概率值较大时(即为易样本),Focal Loss所加的权重较小,使得整体loss变小,这不要紧,因为预测值概率值较大(注意,此时y=1),我们就是希望loss较小。
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 1)
y1, y2, y3, y4, y5 = (1-x)**0 * (-np.log(x)), (1-x)**0.5 * (-np.log(x)), (1-x)**1 * (-np.log(x)), (1-x)**2 * (-np.log(x)), (1-x)**5 * (-np.log(x))
plt.plot(x, y1, 'red')
plt.plot(x, y2, 'green')
plt.plot(x, y3, 'blue')
plt.plot(x, y4, 'yellow')
plt.plot(x, y5, 'purple')
# plt.title('line chart')
plt.xlabel('probability of ground truth class')
plt.ylabel('Focal loss')
plt.show()
备注:论文中的图像如下
此外,加入平衡因子alpha,用来平衡正负样本本身的比例不均:文中alpha取0.25。用到缺陷检测时,有缺陷的样本非常少,同样可以加入平衡因子alpha。 In practice α may be set by inverse class fre-quency or treated as a hyperparameter to set by cross validation. 即alpha可以设置与样本数成反比,也可以视为一个超参数。
作为正类的有缺陷样本数较少,按照论文的意思应该是alpha大于0.5才对,那是没有
情况下才是这样的,因为Focal Loss受两个调节因子影响,不能但看一个,所以这个参数只是试出来的。
从论文中的实验部分可以得到验证,左边是只有alpha, 没有
加权的,所以apha=0.75时最好。右图可知gamma=2, alpha=0.25最好,正好验证了如下公式,因为难样本,即正样本往往远少于负样本,y=1时,focal loss是递减的,加上一个小点的权重0.25,loss会变大,则会突出正样本的学习。
import torch
import torch.nn as nn
#二分类
class FocalLoss(nn.Module):
def __init__(self, gamma=2,alpha=0.25):
super(FocalLoss, self).__init__()
self.gamma = gamma
self.alpha=alpha
def forward(self, input, target):
# input:size is M*2. M is the batch number
# target:size is M.
pt=torch.softmax(input,dim=1)
p=pt[:,1]
loss = -self.alpha*(1-p)**self.gamma*(target*torch.log(p))-\
(1-self.alpha)*p**self.gamma*((1-target)*torch.log(1-p))
return loss.mean()
5. DiceLoss
较适用于样本极度不均的情况,公式如下。
后面部分是Dice系数,是集合A和B相似度度量函数。
(1) 分子是矩阵A和B交集,逐个元素相乘(点乘),再求和。
(2) 分母是矩阵分别求和(矩阵内所有元素加起来),再相加。
对于二分类问题,GT 掩码图只有 0, 1 两个值,G为0,无论预测为多少,loss一直是1,不会改变,则梯度值为0,则不会更新权重,所以不会关注背景区域。
而对于G为1的区域,预测值越大,损失越小。所以会关注前景。
import torch.nn as nn
import torch.nn.functional as F
class SoftDiceLoss(nn.Module):
def __init__(self, weight=None, size_average=True):
super(SoftDiceLoss, self).__init__()
def forward(self, logits, targets):
num = targets.size(0)
smooth = 1
probs = F.sigmoid(logits) # to (0, 1)
m1 = probs.view(num, -1) # (b,c,h,w) -- (n, )
m2 = targets.view(num, -1) # (b,c,h,w) -- (n, )
intersection = (m1 * m2) # A * B
score = 2. * (intersection.sum(1) + smooth) / (m1.sum(1) + m2.sum(1) + smooth)
score = 1 - score.sum() / num
return score