一、简介
- 损失函数的作用: 主要用于深度学习中predict与True label “距离”度量或者“相似度度量”,并通过反向传播求梯度,进而通过梯度下降算法更新网络参数,周而复始,通过损失值和评估值反映模型的好坏。
- 损失函数的分类: 主要分为回归损失函数和分类损失函数。
- 回归损失函数:
reg_loss(回归预测一个具体的数值,真实的一个具体值)
,比如我要预测一个矩形框的宽高,一般来说可以使任意值。一般的回归会将预测的值设计到一个较小的范围比如0~1范围内,这样可以加速模型收敛,要不然模型前期预测的数值“乱跳”,出现波动的情况。一般有L1 Loss
、L2 Loss
、Smooth L1 Loss
,以及在一些任务如目标检测中根据场景设置的loss如,IOU Loss
、GIOU Loss
、DIOU Loss
、CIOU Loss
。 - 分类损失函数:
reg_loss(回归预测一个具体的数值,真实的一个具体值)
,比如我要预测一个矩形框的宽高,一般来说可以使任意值。一般的回归会将预测的值设计到一个较小的范围比如0~1范围内,这样可以加速模型收敛,要不然模型前期预测的数值“乱跳”,出现波动的情况。一般有CrossEntropy Loss
、NLL Loss
、KLDiv Loss
、BCE Loss
、BCE With Logits Loss
、Margin Ranking Loss
、Hinge Embedding Loss
、Huber Loss
等。
需要注意的:
pytorch损失函数中一般有size_average
、size_average
、reduction
这三个参数。
这三个参数决定损失函数是1.只求是loss; 2. 对loss求和;3. 对loss求平均。
(deprecated
表示此方法已废弃、暂时可用,但以后该方法不会再更新,建议后来人不要调用此方法。)
size_average
和reduce
是deprecated,两个之间是有关系的。目前使用的reduction
。
Reduce | size_average | result |
---|---|---|
False | - | 1. 只是求loss(忽略size_average参数) |
True | False | 2. 对loss求和 |
True | True | 3. 对loss求平均值 |
Reduce | result |
---|---|
‘None’ | 1. 只是求loss(忽略size_average参数) |
‘sum’ | 2. 对loss求和 |
‘mean’ | 3. 对loss求平均值 |
补充知识:torch.nn.Xxx和torch.nn.function.xxx异同
1. 实现的功能和效率是相同的。
2. functional.xxx
是函数接口, nn.Xxx
是前者的类封装, nn.Xxx
都继承 nn.Module
,除了具备前者函数的功能之外,内部还有 nn.Module
类的属性,例如train(), eval(), load_state_dict, state_dict
等方法
3. 类的需要先实例化并传入参数conv = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, padding=1)
,然后再以函数调用的方式实例化对象并传入数据out = conv(inputs)
。
4. 由于类是继承于nn.Module
,能与nn.Sequention
结合使用,而函数的实现无法与其结合使用。
5. 类的不需要自己定义和管理weight,而函数的需要自己定义,每次调用时候需要手动传入weight,不利于代码复用。
6. 具有学习参数的建议使用类的实现方式,无学习参数的如非特殊需要,也建议使用类的实现方式。
7. 一般情况下都是用类的方式,函数的方式更接近于底层,使用起来更加的灵活,更加的能自定义的实现自己想要的功能。
二、分类损失函数
补充知识:softmax, logsoftmax和sigmoid
- softmax:
torch.nn.Softmax
, torch.nn.functional.softmax
输出范围:0-1。一般用于多分类问题,交叉熵损失里的激活用的就是softmax。
s
o
f
t
m
a
x
(
z
i
)
=
e
z
i
∑
e
z
i
softmax(z_i) = \frac{e^{z_i}}{\sum{e^{z_i}}}
softmax(zi)=∑eziezi
- logsoftmax:
torch.nn.LogSoftmax
, torch.nn.functional.log_softmax
输出范围:<0
L
o
g
s
o
f
t
m
a
x
(
z
i
)
=
l
o
g
(
e
z
i
∑
e
z
i
Logsoftmax(z_i) =log( \frac{e^{z_i}}{\sum{e^{z_i}}}
Logsoftmax(zi)=log(∑eziezi)
- sigmoid:
torch.nn.Sigmoid
, torch.nn.functional.sigmoid
输出范围:0-1。一般用于二分类问题,二分类交叉熵损失(BCE)用的激活是sigmoid。
s
i
g
m
o
i
d
(
z
i
)
=
1
1
+
e
−
z
i
sigmoid(z_i) = \frac{1}{1+e^{-z_i}}
sigmoid(zi)=1+e−zi1
1.NLL Loss(Negative Log Likelihood Loss, 负对数似然函数)
介绍
公式为:
l
(
x
,
y
)
=
1
N
∑
i
=
1
N
−
y
i
(
l
o
g
(
s
o
f
t
m
a
x
(
x
)
)
)
l(x,y) = \frac{1}{N}\sum_{i=1}^{N}-y_i(log(softmax(x)))
l(x,y)=N1i=1∑N−yi(log(softmax(x)))
- 先对输入的x(N*C, N为样本个数,C为类别总个数)求softmax,此时范围是0-1;
- 然后求对数log,此时范围是(-∞, 0),然后加个负号,最终得出的损失值的范围为(0, +∞ );
- 通过labels(targets)将(N*C)中对应的C取出来得到(N,1),最后求和取均值。
- 其含义为对预测正确但是预测概率不高的情况进行惩罚(这种情况的损失值更大)。
一般先对输入x直接使用nn.LogSoftmax(dim=1)
, 然后再将其输入到torch.nn.NLLLoss()
损失函数中。
代码
torch.nn.NLLLoss(weight=None, size_average=None, ignore_index=- 100, reduce=None, reduction='mean')
weight:用于数据集类别不均衡问题。
import torch
from torch import nn
# 1.定义logsoftmax
m = nn.LogSoftmax(dim=1)
# 2.定义NLL loss
loss = nn.NLLLoss()
# 3. 定义输入 input is of size N x C = 3 x 5
input = torch.randn(3, 5, requires_grad=True)
# 4.定义标签 each element in target has to have 0 <= value < C
target = torch.tensor([1, 0, 4])
output = loss(m(input), target)
output.backward()
2. Cross Entropy Loss(交叉熵损失)
介绍
- 用于分类问题,二分类和多分类。
- 学习过程:
- 神经网络输出最后一层,shape=(N, C),即每个样本对应预测每个类别的得分scores(logits);
- 然后将该得分经过sigmoid或softmax输出概率值,shape=(N, C);
- 最后,将预测的概率值(shape=(N,C))与真实类别标签的one-hot的形式(shape=(N,C))进行交叉熵计算。
nn.CrossEntropyLoss (input, target)
, input.shape=(N, C),target.shape为(N)或者概率形式的target(N,C)。
- 二分类
l ( x , y ^ ) = y ^ ⋅ l o g ( x ) + ( 1 − y ^ ) ⋅ l o g ( 1 − x ) l(x, \hat{y}) = \hat{y}·log(x) + (1-\hat{y})·log(1-x) l(x,y^)=y^⋅log(x)+(1−y^)⋅log(1−x)
x为预测当前标签的概率值。
- 多分类
l ( y , y ^ ) = − y ^ ⋅ l o g ( s o f t m a x ( y ) ) l(y, \hat{y}) = -\hat{y}·log(softmax(y)) l(y,y^)=−y^⋅log(softmax(y))
此处的softmax(y)指的就是预测标签的概率。
n n . C r o s s E n t r o p y L o s s = n n . N L L L o s s ( n n . L o g S o f t m a x ( y ^ ) , y ) nn.CrossEntropyLoss = nn.NLLLoss(nn.LogSoftmax(\hat{y}), y) nn.CrossEntropyLoss=nn.NLLLoss(nn.LogSoftmax(y^),y)
代码
torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=- 100, reduce=None, reduction='mean', label_smoothing=0.0)
from torch import nn
import torch
loss = nn.CrossEntropyLoss()
# 2. 输入,shape=(N, C)
input = torch.randn(3, 5, requires_grad=True)
# 一、 标签为概率,如0,1,2,3
input = torch.randn(3, 5, requires_grad=True)
target = torch.randn(3, 5).softmax(dim=1)
print(target)
output = loss(input, target)
output.backward()
# 二、 标签为概率,shape=input.shape
input = torch.randn(3, 5, requires_grad=True)
target = torch.randn(3, 5).softmax(dim=1)
output = loss(input, target)
output.backward()
3. BCE Loss(二分类交叉熵损失,Binary Cross Entropy Loss)
介绍
可用于多标签分类,即判断每一个标签是否是前景、背景。
nn.BCELoss(input, target)
, input和target的shape一致。
l ( y , y ^ ) = − ( y ⋅ l o g ( y ^ ) + ( 1 − y ) ⋅ l o g ( 1 − y ^ ) ) l(y, \hat{y}) = -(y·log(\hat{y}) + (1-y)·log(1-\hat{y})) l(y,y^)=−(y⋅log(y^)+(1−y)⋅log(1−y^))
y ^ \hat{y} y^为预测当前标签的概率值。在使用BCE前,需要通过sigmoid将score转换为预测的概率。
代码
torch.nn.BCELoss(weight=None, size_average=None, reduce=None, reduction='mean')
# 1. 在放入BCE Loss前需要对输入使用sigmoid转换为概率,
m = nn.Sigmoid()
loss = nn.BCELoss()
input = torch.randn(3, requires_grad=True)
target = torch.empty(3).random_(2)
output = loss(m(input), target)
output.backward()
4. BCE With Logits Loss
介绍
可用于多标签分类,即判断每一个标签是否是前景、背景。
nn.BCEWithLogitsLoss(input, target)
, input和target的shape一致。
B C E W i t h L o g i t s L o s s ( i n p u t s , t a r g e t s ) = B C E L o s s ( s i g m o i d ( i n p u t s ) , t a r g e t s ) BCEWithLogitsLoss(inputs, targets) = BCELoss(sigmoid(inputs), targets) BCEWithLogitsLoss(inputs,targets)=BCELoss(sigmoid(inputs),targets)
代码
torch.nn.BCEWithLogitsLoss(weight=None, size_average=None, reduce=None, reduction='mean', pos_weight=None)
loss = nn.BCEWithLogitsLoss()
input = torch.randn(3, requires_grad=True)
target = torch.empty(3).random_(2)
output = loss(input, target)
output.backward()
5. Focal Loss
介绍
- 用于分类问题(多标签多分类,即多个二分类),在BCEWithLogitsLoss上进行改进。
- 解决正负分别不平衡、困难容易样本不均衡的问题。在一阶段的目标检测中,背景框类别的数量远远大于前景框的数量,使得占大多数量的无用的背景框主导损失函数、主导训练,导致训练结果差。
- 使用类别权重处理,平衡正负样本权重
- 使用难度权重处理,平衡容易样本和困难样本的权重
公式:
B
C
E
W
i
t
h
L
o
g
i
t
s
L
o
s
s
(
y
,
p
)
=
−
y
⋅
l
o
g
(
p
)
−
(
1
−
y
)
⋅
l
o
g
(
1
−
p
)
(
1
)
BCEWithLogitsLoss (y, p) = -y·log(p) - (1-y)·log(1-p) (1)
BCEWithLogitsLoss(y,p)=−y⋅log(p)−(1−y)⋅log(1−p)(1)
由(1)可推出:
B
C
E
W
i
t
h
L
o
g
i
t
s
L
o
s
s
(
y
,
p
)
=
{
−
l
o
g
(
p
)
,
y
=
1
−
l
o
g
(
1
−
p
)
,
其
他
BCEWithLogitsLoss (y, p) = \begin{cases} -log(p), y=1\\ -log(1-p), 其他\\ \end{cases}
BCEWithLogitsLoss(y,p)={−log(p),y=1−log(1−p),其他
继续可变形为:
令
p
t
=
{
p
,
y
=
1
1
−
p
,
其
他
令 p_t = \begin{cases} p, y=1\\ 1-p, 其他\\ \end{cases}
令pt={p,y=11−p,其他
则
B
C
E
W
i
t
h
L
o
g
i
t
s
L
o
s
s
(
y
,
p
)
=
B
C
E
W
i
t
h
L
o
g
i
t
s
L
o
s
s
(
p
t
)
=
−
l
o
g
(
p
t
)
则 BCEWithLogitsLoss (y, p) =BCEWithLogitsLoss (p_t) = -log(p_t)
则BCEWithLogitsLoss(y,p)=BCEWithLogitsLoss(pt)=−log(pt)
α t = i n p u t s {\alpha}_t = inputs αt=inputs
F o c a l L o s s ( p t ) = − α t ⋅ ( 1 − p t ) γ ⋅ l o g ( p t ) Focal Loss(p_t) = -{\alpha}_t·(1-p_t)^\gamma·log(p_t) FocalLoss(pt)=−αt⋅(1−pt)γ⋅log(pt)
- α t {\alpha}_t αt为类别权重, α t = y ∗ α + ( 1 − y ) ∗ ( 1 − α ) {\alpha}_t = y * \alpha + (1 - y) * (1 - \alpha) αt=y∗α+(1−y)∗(1−α), α \alpha α为常数,一般为0.25.
- ( 1 − p t ) γ (1-p_t)^\gamma (1−pt)γ为难度权重, γ \gamma γ为常数,一般为1.5.
代码
import torch
from torch import nn
class FocalLoss(nn.Module):
def __init__(self, loss_fcn, gamma=1.5, alpha=0.25):
super(FocalLoss, self).__init__()
self.loss_fcn = loss_fcn # must be nn.BCEWithLogitsLoss()=Sigmoid+BCELoss 定义为多分类交叉熵损失函数
self.gamma = gamma # 参数gamma 用于削弱简单样本对loss的贡献程度
self.alpha = alpha # 参数alpha 用于平衡正负样本个数不均衡的问题
# self.reduction: 控制FocalLoss损失输出模式 sum/mean/none 默认是Mean
self.reduction = loss_fcn.reduction
# focalloss中的BCE函数的reduction='None' BCE不使用Sum或者Mean
self.loss_fcn.reduction = 'none' # 需要将Focal loss应用于每一个样本之中
def forward(self, pred, true):
loss = self.loss_fcn(pred, true) # 正常BCE的loss: loss = -log(p_t)
# p_t = torch.exp(-loss)
# loss *= self.alpha * (1.000001 - p_t) ** self.gamma # non-zero power for gradient stability
pred_prob = torch.sigmoid(pred) # prob from logits
# true=1 p_t=pred_prob true=0 p_t=1-pred_prob
# 计算p_t
p_t = true * pred_prob + (1 - true) * (1 - pred_prob) # p_t
# 计算 alpha_t
# true=1 alpha_factor=self.alpha true=0 alpha_factor=1-self.alpha
alpha_factor = true * self.alpha + (1 - true) * (1 - self.alpha) # alpha_t
# 计算(1.0 - p_t) ** gamma
modulating_factor = (1.0 - p_t) ** self.gamma # 这里代表Focal loss中的指数项
# 返回最终的loss=BCE * 两个参数 (看看公式就行了 和公式一模一样)
loss *= alpha_factor * modulating_factor
# 最后选择focalloss返回的类型 默认是mean
if self.reduction == 'mean':
return loss.mean()
elif self.reduction == 'sum':
return loss.sum()
else: # 'none'
return loss
6. QFocal Loss(Generalized Focal Loss)
介绍
- 将Focal Loss中的 1 − p t γ {1-p_t}^{\gamma} 1−ptγ改为 ∣ t r u e − s i g m o i d ( p r e d ) ∣ γ |true - sigmoid(pred)|^{\gamma} ∣true−sigmoid(pred)∣γ
总结
- NLL和CE:
NLL=-targets* log(inputs)
CE = NLL(LogSoftmax(inputs), targets)
此处inputs未经LogSoftmax、softmax、sigmoid转换。
-
CE、BCE (BCEWithLogits):
CE用于多分类,激活函数为softmax
BCE用于二分类(可用于多分类多标签,看成多个二分类),激活函数为sigmoid
BCEWithLogits = BCE + sigmoid
-
Focal Loss和QFocal Loss:
Focal Loss 在BCEWithLogits上增加了正负样本权重和难易样本权重。
QFocal Loss 在Focal Loss上对难易样本权重进行了更改。
ps:本博客仅供自己复习理解,不具其他人可参考,本博客参考了大量的优质资源,侵删。