Pytorch常用的函数(十)交叉熵损失函数nn.BCELoss()、nn.BCELossWithLogits()、nn.CrossEntropyLoss()详解

Pytorch常用的函数(九)交叉熵损失函数nn.BCELoss()、nn.BCELossWithLogits()、nn.CrossEntropyLoss()详解

关于交叉熵公式推导以及理解,可以参考:

信息量、熵、KL散度、交叉熵概念理解

通过上面链接中的推导,二分类交叉熵损失函数:
l o s s = − 1 n ∑ i = 1 n ( y i l o g y i ^ + ( 1 − y i ) l o g ( 1 − y i ^ ) ) n 为批量样本 loss=-\frac{1}{n}\sum\limits_{i=1}^n(y_{i}log\hat{y_{i}}+(1-y_{i})log(1-\hat{y_i}))\\ n为批量样本 loss=n1i=1n(yilogyi^+(1yi)log(1yi^))n为批量样本

多分类的交叉熵损失函数:
l o s s = − 1 n ∑ i = 1 n ∑ c = 1 m y i c l o g y ^ i c n 为批量样本, m 为分类数 loss=-\frac{1}{n}\sum\limits_{i=1}^n\sum\limits_{c=1}^my_{ic}log\hat{y}_{ic} \\ n为批量样本,m为分类数 loss=n1i=1nc=1myiclogy^icn为批量样本,m为分类数
我们上面公式继续化简:
l o s s = − 1 n ∑ i = 1 n ∑ c = 1 m y i c l o g y ^ i c 我们现在只看一个样本: l o s s ( x , c l a s s ) = − ∑ c = 1 m y x c l o g y ^ x c = − [ y x 1 l o g ( y ^ x 1 ) + y x 2 l o g ( y ^ x 2 ) + . . . + y x m l o g ( y ^ x m ) ] 这个样本,只有 c l a s s 处 y x [ c l a s s ] = 1 ,其他地方都为 0 ,因此 l o s s ( x , c l a s s ) = − l o g ( y ^ x [ c l a s s ] ) 需要注意的是,在 p y t o r c h 中 x 需要先进行 s o f t m a x , 因此 l o s s ( x , c l a s s ) = − l o g ( y ^ x [ c l a s s ] ) = − l o g ( e x [ c l a s s ] ∑ j e x [ j ] ) = − x [ c l a s s ] + l o g ( ∑ j e x [ j ] ) 我们举个例子,假设预测三个类别的概率为 [ 0.1 , 0.2 , 0.3 ] ,标签 c l a s s = 1 l o s s ( x , c l a s s ) = − x [ c l a s s ] + l o g ( ∑ j e x [ j ] ) = − 0.2 + l o g ( e x [ 0 ] + e x [ 1 ] + e x [ 2 ] ) = − 0.2 + l o g ( e 0.1 + e 0.2 + e 0.3 ) loss=-\frac{1}{n}\sum\limits_{i=1}^n\sum\limits_{c=1}^my_{ic}log\hat{y}_{ic} \\ 我们现在只看一个样本:\\ loss(x,class)=-\sum\limits_{c=1}^my_{xc}log\hat{y}_{xc}\\ =-[y_{x1}log(\hat{y}_{x1})+ y_{x2}log(\hat{y}_{x2}) + ... + y_{xm}log(\hat{y}_{xm})] \\ 这个样本,只有class处y_{x[class]}=1,其他地方都为0,因此\\ loss(x,class)=-log(\hat{y}_{x[class]}) \\ 需要注意的是,在pytorch中x需要先进行softmax,因此\\ loss(x,class)=-log(\hat{y}_{x[class]})\\ =-log(\frac{e^{x[class]}}{\sum\limits_{j}e^{x[j]}}) \\ =-x[class]+log(\sum\limits_{j}e^{x[j]}) \\ 我们举个例子,假设预测三个类别的概率为[0.1, 0.2, 0.3],标签class=1\\ loss(x,class)=-x[class]+log(\sum\limits_{j}e^{x[j]})\\ =-0.2+log(e^{x[0]}+e^{x[1]}+e^{x[2]})\\ =-0.2 + log(e^{0.1}+e^{0.2}+e^{0.3}) loss=n1i=1nc=1myiclogy^ic我们现在只看一个样本:loss(x,class)=c=1myxclogy^xc=[yx1log(y^x1)+yx2log(y^x2)+...+yxmlog(y^xm)]这个样本,只有classyx[class]=1,其他地方都为0,因此loss(x,class)=log(y^x[class])需要注意的是,在pytorchx需要先进行softmax,因此loss(x,class)=log(y^x[class])=log(jex[j]ex[class])=x[class]+log(jex[j])我们举个例子,假设预测三个类别的概率为[0.1,0.2,0.3],标签class=1loss(x,class)=x[class]+log(jex[j])=0.2+log(ex[0]+ex[1]+ex[2])=0.2+log(e0.1+e0.2+e0.3)
现在得到了化简后的多分类交叉熵损失函数:
对于单个样本 x : l o s s ( x , c l a s s ) = − l o g ( e x [ c l a s s ] ∑ j e x [ j ] ) = − x [ c l a s s ] + l o g ( ∑ j e x [ j ] ) 对于单个样本x:\\ loss(x,class)=-log(\frac{e^{x[class]}}{\sum\limits_{j}e^{x[j]}}) =-x[class]+log(\sum\limits_{j}e^{x[j]}) 对于单个样本xloss(x,class)=log(jex[j]ex[class])=x[class]+log(jex[j])

(1)二分类损失函数nn.BCELoss()、nn.BCELossWithLogits()

l o s s = − 1 n ∑ i = 1 n ( y i l o g y i ^ + ( 1 − y i ) l o g ( 1 − y i ^ ) ) n 为批量样本 loss=-\frac{1}{n}\sum\limits_{i=1}^n(y_{i}log\hat{y_{i}}+(1-y_{i})log(1-\hat{y_i}))\\ n为批量样本 loss=n1i=1n(yilogyi^+(1yi)log(1yi^))n为批量样本

Pytorch链接:BCEWithLogitsLoss

torch.nn.BCEWithLogitsLoss(
    weight=None, 
    size_average=None, 
    reduce=None, 
    reduction='mean', # 默认计算的是批量样本损失的平均值,还可以为'sum'或者'none'
    pos_weight=None
)
# 可以输入参数的shape可以为任意维度,只不过Target要和Input一致
Input: (*), where *∗ means any number of dimensions.
Target: (*), same shape as the input.
# 如果reduction='mean',输出标量,
# reduction='none',输出Output的shape和input一致
Output: scalar. If reduction is 'none', then (*), same shape as input.

Pytorch链接:BCELoss

torch.nn.BCELoss(
    weight=None, 
    size_average=None, 
    reduce=None, 
    reduction='mean' # 默认计算的是批量样本损失的平均值,还可以为'sum'或者'none'
)
# 可以输入参数的shape可以为任意维度,只不过Target要和Input一致
Input: (*), where *∗ means any number of dimensions.
Target: (*), same shape as the input.
# 如果reduction='mean',输出标量,
# reduction='none',输出Output的shape和input一致
Output: scalar. If reduction is 'none', then (*), same shape as input.
  • 在PyTorch中,提供了nn.BCELoss()、nn.BCELossWithLogits()作为二分类的损失函数;

  • 其中BCEWithLogitsLoss方法,它可以直接将输入的值规范到0和1之间,相当于将SigmoidBCELoss集成在了一个方法中;

  • 我们用代码了解下这两个二分类损失函数的区别和联系。

import numpy as np
import torch
from torch import nn
import torch.nn.functional as F

y = torch.tensor([1, 0, 1], dtype=torch.float)
y_hat = torch.tensor([0.8, 0.2, 0.4], dtype=torch.float)


bce_loss = nn.BCELoss()

# nn.BCELoss()需要先对输入数据进行sigmod
print("官方BCELoss = ", bce_loss(torch.sigmoid(y_hat), y))

# nn.BCEWithLogitsLoss()不需要自己sigmod
bcelogits_loss = nn.BCEWithLogitsLoss()
print("官方BCEWithLogitsLoss = ", bcelogits_loss(y_hat, y))

# 我们根据二分类交叉熵损失函数实现:
def loss(y_hat, y):
    y_hat = torch.sigmoid(y_hat)
    l = -(y * np.log(y_hat) + (1 - y) * np.log(1 - y_hat))
    l = sum(l) / len(l)
    return l

print('自己实现Loss = ', loss(y_hat, y))
# 可以看到结果值相同
官方BCELoss           =  tensor(0.5608)
官方BCEWithLogitsLoss =  tensor(0.5608)
自己实现Loss           =  tensor(0.5608)

(2) nn.CrossEntropyLoss()

化简后的多分类交叉熵损失函数:
对于单个样本 x : l o s s ( x , c l a s s ) = − l o g ( e x [ c l a s s ] ∑ j e x [ j ] ) = − x [ c l a s s ] + l o g ( ∑ j e x [ j ] ) 对于单个样本x:\\ loss(x,class)=-log(\frac{e^{x[class]}}{\sum\limits_{j}e^{x[j]}}) =-x[class]+log(\sum\limits_{j}e^{x[j]}) 对于单个样本xloss(x,class)=log(jex[j]ex[class])=x[class]+log(jex[j])
Pytorch链接:CrossEntropyLoss

torch.nn.CrossEntropyLoss(
    weight=None, 
    size_average=None, 
    ignore_index=-100, 
    reduce=None, 
    reduction='mean', 
    label_smoothing=0.0 # 同样,默认计算的是批量样本损失的平均值,还可以为'sum'或者'none'
)

shape如下所示:

  • 可以看到Input的shape相对于Target的shape多了C
  • Target的shape和Output相等(当reduction=‘none’)

在这里插入图片描述

  • 在关于二分类的问题中,输入交叉熵公式的网络预测值必须经过Sigmoid进行映射
  • 而在多分类问题中,需要将Sigmoid替换成Softmax,这是两者的一个重要区别
  • CrossEntropyLoss = softmax+log+nll_loss的集成
cross_loss = nn.CrossEntropyLoss(reduction="none") # 设置为none,这里输入每个样本的loss值,不计算平均值
target = torch.tensor([0, 1, 2])

predict = torch.tensor([[0.9, 0.2, 0.8],
                        [0.5, 0.2, 0.4],
                        [0.4, 0.2, 0.9]]) # 未经过softmax
print('官方实现CrossEntropyLoss: ', cross_loss(predict, target))


# 自己实现方便理解版本的CrossEntropyLoss
def cross_loss(predict, target, reduction=None):
    all_loss = []
    for index, value in enumerate(target):
        # 利用多分类简化后的公式,对每一个样本求loss值
        loss = -predict[index][value] + torch.log(sum(torch.exp(predict[index])))
        all_loss.append(loss)
    all_loss = torch.stack(all_loss)
    if reduction == 'none':
        return all_loss
    else:
        return torch.mean(all_loss)

print('实现方便理解的CrossEntropyLoss: ', cross_loss(predict, target, reduction='none'))

# 利用F.nll_loss实现的CrossEntropyLoss
def cross_loss2(predict, target, reduction=None):
    # Softmax的缺点:
    # 1、如果有得分值特别大的情况,会出现上溢情况;
    # 2、如果很小的负值很多,会出现下溢情况(超出精度范围会向下取0),分母为0,导致计算错误。
    # 引入log_softmax可以解决上溢和下溢问题
    logsoftmax = F.log_softmax(predict)
    print('target = ', target)
    print('logsoftmax:\n', logsoftmax)
    # nll_loss不是将标签值转换为one-hot编码,而是根据target的值,索引到对应元素,然后取相反数。
    loss = F.nll_loss(logsoftmax, target, reduction=reduction)
    return loss

print('F.nll_loss实现的CrossEntropyLoss: ', cross_loss2(predict, target, reduction='none'))
官方实现CrossEntropyLoss:          tensor([0.8761, 1.2729, 0.7434])
实现方便理解的CrossEntropyLoss:     tensor([0.8761, 1.2729, 0.7434])

target =  tensor([0, 1, 2])
logsoftmax:
tensor([[-0.8761, -1.5761, -0.9761],
        [-0.9729, -1.2729, -1.0729],
        [-1.2434, -1.4434, -0.7434]])
F.nll_loss实现的CrossEntropyLoss:  tensor([0.8761, 1.2729, 0.7434])

最后提出一个问题,二分类问题,应该选择sigmoid还是softmax?

可以参考: 二分类问题,应该选择sigmoid还是softmax?

  • 17
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: nn.BCELoss()和nn.CrossEntropyLoss()是PyTorch库中常用的损失函数nn.BCELoss()是二分类问题中使用的二元交叉熵损失函数。这个函数适用于输出数据为概率值的情况,如sigmoid函数输出的概率值。它计算每个样本的损失然后对它们取平均值。BCE代表二元交叉熵(binary cross entropy)。这个损失函数可以反映模型预测结果与真实标签之间的差距,并帮助模型通过梯度下降来更新网络参数,使得预测结果与真实标签尽可能接近。 nn.CrossEntropyLoss()是多分类问题中使用的交叉熵损失函数。这个函数适用于输出数据为类别索引的情况,如使用softmax函数进行多分类任务的结果。它将模型预测的概率分布与真实标签之间的差异转化为一个标量损失值。CrossEntropy代表交叉熵损失,它将每个样本的预测概率与真实标签对应位置的真实概率进行比较,并对它们进行求和然后取平均。这个损失函数可以帮助模型学习到正确的类别预测,使得预测的概率分布与真实标签的概率分布尽可能接近。 综上所述,nn.BCELoss()适用于二分类问题,计算模型输出概率值与真实标签之间的差异;而nn.CrossEntropyLoss()适用于多分类问题,计算模型输出概率分布与真实标签之间的差异。 ### 回答2: nn.BCELoss()和nn.CrossEntropyLoss()都是PyTorch中用于计算损失函数的类。 nn.BCELoss()是二分类问题中使用的损失函数,用于计算二分类交叉熵损失。它将模型的输出与目标标签进行比较,并计算每个样本的二进制交叉熵损失。如果目标标签是正类,则损失函数会惩罚模型输出为负类的程度,反之亦然。nn.BCELoss()常用于二分类任务中,例如图像分类中的两类分割、异常检测等。 nn.CrossEntropyLoss()则用于多分类问题中,计算多分类交叉熵损失。它接受模型的输出和目标标签,并计算每个样本的交叉熵损失。在多分类问题中,目标标签需用One-Hot编码或整数类型表示。nn.CrossEntropyLoss()会将模型输出进行softmax处理,再与目标标签进行比较,计算交叉熵损失。它对于多分类问题是常用的损失函数。 总结: 1. nn.BCELoss()适用于二分类问题,计算二分类交叉熵损失。 2. nn.CrossEntropyLoss()适用于多分类问题,计算多分类交叉熵损失。 两者主要区别在于输入形式和模型输出的处理方式。 ### 回答3: nn.BCELoss()和nn.CrossEntropyLoss()都是PyTorch框架中常用的损失函数。它们的差异在于应用的场景和处理方式不同。 nn.BCELoss()是二分类问题的损失函数,适用于输出为概率值的情况。它的全称是Binary Cross Entropy Loss,用于衡量模型输出的概率与真实标签之间的差异。该损失函数采用式子:Loss = -[y*log(x) + (1-y)*log(1-x)],其中y是真实标签,x是模型输出的概率。nn.BCELoss()常用于输出值为0或1的二分类任务,比如判断图像中是否包含某个物体。 nn.CrossEntropyLoss()是多分类问题的损失函数,适用于输出为类别的情况。它的全称是Cross Entropy Loss,用于衡量模型输出的类别预测值与真实标签之间的差异。该损失函数采用式子:Loss = -[y*log(x)],其中y是真实标签的类别索引,x是模型输出的类别概率分布。nn.CrossEntropyLoss()将分类问题转化为一个概率分布的差异度量,常用于多分类任务,比如图像分类、文本分类等。 两者之间的区别主要在于输入形式和问题类型不同。nn.BCELoss()适用于二分类问题,输出为概率值;nn.CrossEntropyLoss()适用于多分类问题,输出为类别。同时需要注意的是,对于二分类问题,可以使用nn.CrossEntropyLoss()代替nn.BCELoss(),只是需要设置输出类别为2。而对于多分类问题,如果输出为概率值,则可以使用nn.BCELoss(),但需要将输出进行二值化处理。 综上所述,nn.BCELoss()和nn.CrossEntropyLoss()是PyTorch框架中常用的损失函数,用于不同类型的分类问题。根据具体的问题和输出类型,选择合适的损失函数可以提高模型训练的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值