1 交叉熵损失函数

  在之前的损失函数介绍中已经解释过,什么是交叉熵损失函数以及其数学原理(【Pytorch基础】torch.nn.CrossEntropyLoss损失函数介绍)。在多分类问题中输出层的函数是 S o f t m a x Softmax Softmax函数, 在二分类问题中输出层的函数是 S i g m o d Sigmod Sigmod函数。

  而CrossEntropy损失函数适用于总共有N个类别的分类。当N=2时,即二分类任务,只需要判断是还是否的情况,就可以使用二分类交叉熵损失,本小节介绍二分类交叉熵损失函数。

2 二元交叉熵损失函数

2.1 torch.nn.BCELoss()

  BCELoss的全称是Binary Cross Entropy, 即二分类交叉熵损失。如下公式 (y是真实标签,x是预测值):
L o s s = − 1 N ∑ i = 1 N [ y i log ⁡ ( p i ) + ( 1 − y i ) log ⁡ ( 1 − p i ) ] Loss=-\frac{1}{N} \sum_{i=1}^{N}\left[y_{i} \log \left(p_{i}\right)+\left(1-y_{i}\right) \log \left(1-p_{i}\right)\right] Loss=N1i=1N[yilog(pi)+(1yi)log(1pi)]
其实这个函数就是CrossEntropyLoss的当类别数N=2时候的特例。因为类别数为2,属于第一类的概率为y,那么属于第二类的概率自然就是(1-y)。因此套用与CrossEntropy损失的计算方法,用对应的标签乘以对应的预测值再求和,就得到了最终的损失。参考文献【3】
在我自己的问题中二元交叉熵损失函数针对的是单标签二分类问题)关于单标签二分类和多标签二分类对于torch.nn.BCELoss()的使用见参考文献【4】【5】。
其中, N N N是总样本数, y i y_{i} yi是第 i i i个样本的所属类别, p i p_{i} pi是第 i i i个样本的预测值,一般来说,它是一个概率值。
举个例子: y i = [ 1 , 0 , 0 ] , p i = [ 0.8 , 0.2 , 0.4 ] y_{i}=[1, 0, 0], p_{i}=[0.8, 0.2, 0.4] yi=[1,0,0],pi=[0.8,0.2,0.4]例子来着参考文献【5】
L = − 1 3 [ ( 1 ∗ log ⁡ 0.8 + ( 1 − 1 ) ∗ log ⁡ ( 1 − 0.8 ) ) + ( 0 ∗ log ⁡ 0.2 + ( 1 − 0 ) ∗ log ⁡ ( 1 − 0.2 ) ) + ( 0 ∗ log ⁡ 0.4 + ( 1 − 0 ) ∗ log ⁡ ( 1 − 0.4 ) ) ] = 0.319 L=-\frac{1}{3}[(1 * \log 0.8+(1-1) * \log (1-0.8))+(0 * \log 0.2+(1-0) * \log (1-0.2))+(0 * \log 0.4+(1-0) * \log (1-0.4))]=0.319 L=31[(1log0.8+(11)log(10.8))+(0log0.2+(10)log(10.2))+(0log0.4+(10)log(10.4))]=0.319

from math import log
loss = ((1*log(0.8) + (1-1)*log(1-0.8)) + (0*log(0.2)+(1-0)*log(1-0.2)) + (0*log(0.4)+(1-0)*log(1-0.4))) / -3
print(loss)
0.3190375754648034

其实,在PyTorch中已经内置了BCELoss,它的主要用途是计算二分类问题的交叉熵,我们可以调用该方法,并将结果与上面手动计算的结果做个比较:

import torch
import torch.nn as nn

bce_loss = nn.BCELoss()
pred_pro = torch.tensor([0.8, 0.2, 0.4], dtype=torch.float)
label = torch.tensor([1, 0, 0], dtype=torch.float)

print(bce_loss(pred_pro, label))

tensor(0.3190)

需要注意的是,输入BCELoss中的预测值应该是个概率 p i p_{i} pi

上面的栗子直接给出了预测的 p i p_{i} pi,这是符合要求的。但在更一般的二分类问题中,网络的输出取值是整个实数域(可正可负可为0)
  为了由这种输出值得到对应的 p i p_{i} pi,你可以在网络的输出层之后新加一个Sigmoid层,这样便可以将输出值的取值规范到0和1之间,这就是交叉熵公式中的 p i p_{i} pi
当然,可以不更改网络输出,而是在将输出值送入交叉熵公式进行性计算之前,手动用Simgmoid函数做一个映射。
  在PyTorch中,提供了BCEWithLogitsLoss方法,它可以直接将输入的值规范到0和1 之间,相当于将Sigmoid和BCELoss集成在了一个方法中。

2.2 torch.nn.BCEWithLogitsLoss()

举个例子来具体进行说明:假设pred是shape为[4,1]的tensor,其中4代表样本个数,2代表该样本分别属于两个类别的概率(前提是规范到了0和1之间,否则就是两个实数域上的值,记住,现在我们讨论的是二分类);target是shape为[4]的tensor,4即样本数。

import torch
import torch.nn as nn

bce_loss = nn.BCELoss()

pred = torch.randn(4, 1)  # 预测值
print("pred", pred)
target = torch.rand(4).random_(0, 2)  # 真实类别标签
print("target", target)
# 将target进行独热编码
onehot_target = torch.eye(2)[target.long(), :]
print("onehot_target", onehot_target)

sigmoid = nn.Sigmoid()
sigmoid_pred = sigmoid(pred)
print("sigmoid_pred", sigmoid_pred)

loss1 = bce_loss(sigmoid_pred, target.view(4, -1))
print(loss1)

bce_loss2 = nn.BCEWithLogitsLoss()
loss2 = bce_loss2(pred, target.view(4, -1))
print(loss2)
“”“
pred tensor([[ 0.8269],
        [-0.7197],
        [-0.6237],
        [ 0.1657]])
target tensor([0., 0., 0., 1.])
onehot_target tensor([[1., 0.],
        [1., 0.],
        [1., 0.],
        [0., 1.]])
sigmoid_pred tensor([[0.6957],
        [0.3275],
        [0.3489],
        [0.5413]])
tensor(0.6573)
tensor(0.6573)

”“”

3 参考文献

[1]pytorch nn.BCELoss()详解
[2]pytorch二分类损失函数BCEWithLogitsLoss
[3]Pytorch常用损失函数拆解
[4]loss函数之BCELoss
[5](七)详解pytorch中的交叉熵损失函数nn.BCELoss()、nn.BCELossWithLogits(),二分类任务如何定义损失函数,如何计算准确率、如何预测

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐