pytorch模型构建(四)——常用的分类损失函数

一、简介

  • 损失函数的作用: 主要用于深度学习中predict与True label “距离”度量或者“相似度度量”,并通过反向传播求梯度,进而通过梯度下降算法更新网络参数,周而复始,通过损失值和评估值反映模型的好坏。
  • 损失函数的分类: 主要分为回归损失函数和分类损失函数。
  • 回归损失函数: reg_loss(回归预测一个具体的数值,真实的一个具体值),比如我要预测一个矩形框的宽高,一般来说可以使任意值。一般的回归会将预测的值设计到一个较小的范围比如0~1范围内,这样可以加速模型收敛,要不然模型前期预测的数值“乱跳”,出现波动的情况。一般有L1 LossL2 LossSmooth L1 Loss,以及在一些任务如目标检测中根据场景设置的loss如,IOU LossGIOU LossDIOU LossCIOU Loss
  • 分类损失函数: reg_loss(回归预测一个具体的数值,真实的一个具体值),比如我要预测一个矩形框的宽高,一般来说可以使任意值。一般的回归会将预测的值设计到一个较小的范围比如0~1范围内,这样可以加速模型收敛,要不然模型前期预测的数值“乱跳”,出现波动的情况。一般有CrossEntropy LossNLL LossKLDiv LossBCE LossBCE With Logits LossMargin Ranking LossHinge Embedding LossHuber Loss等。

需要注意的:
pytorch损失函数中一般有size_averagesize_averagereduction这三个参数。
这三个参数决定损失函数是1.只求是loss; 2. 对loss求和;3. 对loss求平均。
deprecated表示此方法已废弃、暂时可用,但以后该方法不会再更新,建议后来人不要调用此方法。)

size_averagereduce 是deprecated,两个之间是有关系的。目前使用的reduction

Reducesize_averageresult
False-1. 只是求loss(忽略size_average参数)
TrueFalse2. 对loss求和
TrueTrue3. 对loss求平均值
Reduceresult
‘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+ezi1


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=1Nyi(log(softmax(x)))

  1. 先对输入的x(N*C, N为样本个数,C为类别总个数)求softmax,此时范围是0-1;
  2. 然后求对数log,此时范围是(-∞, 0),然后加个负号,最终得出的损失值的范围为(0, +∞ );
  3. 通过labels(targets)将(N*C)中对应的C取出来得到(N,1),最后求和取均值。
  4. 其含义为对预测正确但是预测概率不高的情况进行惩罚(这种情况的损失值更大)。
    一般先对输入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(交叉熵损失)

介绍

  • 用于分类问题,二分类和多分类。
  • 学习过程:
  1. 神经网络输出最后一层,shape=(N, C),即每个样本对应预测每个类别的得分scores(logits);
  2. 然后将该得分经过sigmoid或softmax输出概率值,shape=(N, C);
  3. 最后,将预测的概率值(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)+(1y^)log(1x)

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^)=(ylog(y^)+(1y)log(1y^))

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)=ylog(p)(1y)log(1p)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=1log(1p),
继续可变形为:
令 p t = { p , y = 1 1 − p , 其 他 令 p_t = \begin{cases} p, y=1\\ 1-p, 其他\\ \end{cases} pt={p,y=11p,
则 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(1pt)γlog(pt)

  • α t {\alpha}_t αt为类别权重, α t = y ∗ α + ( 1 − y ) ∗ ( 1 − α ) {\alpha}_t = y * \alpha + (1 - y) * (1 - \alpha) αt=yα+(1y)(1α), α \alpha α为常数,一般为0.25.
  • ( 1 − p t ) γ (1-p_t)^\gamma (1pt)γ为难度权重, γ \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} 1ptγ改为 ∣ t r u e − s i g m o i d ( p r e d ) ∣ γ |true - sigmoid(pred)|^{\gamma} truesigmoid(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:本博客仅供自己复习理解,不具其他人可参考,本博客参考了大量的优质资源,侵删。

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值