【Deep learning】——一文知torch分类任务的交叉熵Loss(多分类、多标签任务)

  • 多分类任务
  • 多标签任务


之前对torch的分类Loss还有好些疑惑,这次一网打进不留。文章包含多分类任务loss和多标签任务loss【softmax/NLLloss/CrossEntropyLoss/sigmoid/BCEloss/BCEWithLogitsLoss】
参考博客:
Pytorch入门笔记(多分类问题)Pytorch踩坑记之交叉熵(nn.CrossEntropy,nn.NLLLoss,nn.BCELoss的区别和使用)pytorch 多标签分类以及BCEloss

多分类任务

pytorch推荐项目多任务51 pytorch多任务多loss_深度学习


pytorch推荐项目多任务51 pytorch多任务多loss_深度学习_02


pytorch推荐项目多任务51 pytorch多任务多loss_git_03


上面三个图胜千言,总结如下:

pytorch推荐项目多任务51 pytorch多任务多loss_pytorch_04


自己尝试用代码做一遍(三个loss算出来相等,推荐直接CrossEntropyLoss):

import torch
import torch.nn as nn
import torch.nn.functional as F

y = torch.Tensor([[1, 0, 0], [0, 1, 0]]) # one-hot vector
z = torch.Tensor([[0.2, 0.1, -0.1], [0.3, 0.5, -0.2]]) # logits

# 最原始的公式计算法
# y_pred = torch.exp(z) / torch.exp(z).sum(1, keepdim=True) # softmax,注意按行求和,而不是整个求和
y_pred = F.softmax(z, dim=1)
loss = (- y * torch.log(y_pred)).sum()
print('directly compute', loss)

# Nll_loss 公式
# y_pred = torch.exp(z) / torch.exp(z).sum(1, keepdim=True)
# y_pred_log = torch.log(y_pred)
y_pred_log = F.log_softmax(z, dim=1)
criterion = nn.NLLLoss(reduction='sum') # 默认为mean,总Loss/样本数
                                        # 若为none,则返回向量每个样本对应的loss
                                        # 若为sum,则返回总loss
loss = criterion(y_pred_log, torch.LongTensor([0, 1])) # 直接使用标签(需要是LongTensor类型),不用one-hot向量
print('NLL loss', loss)

# cross_entroy 公式
criterion = nn.CrossEntropyLoss(reduction='sum')
loss = criterion(z, torch.LongTensor([0, 1]))
print('cross entroy loss', loss)

多标签任务

首先要弄清楚什么是多标签分类:比如判断一幅图是否有猫或者狗,如果都有标签则为[1, 1];

——【可以看做多个二分类】,而对于二分类任务,通常采用BCEloss即Binary CrossEntropy loss。对于一个样本来说,它的各个label的分数加起来不一定等于1,不同的是,此时激活函数通常使用Sigmoid而不是Softmax

pytorch推荐项目多任务51 pytorch多任务多loss_深度学习_05


对二分类问题,其Loss也叫交叉熵,但公式表示形式不同,与多分类的softmax后-y*logy^相比,给人的感觉就像考虑了yn=0的样本的损失。

而对于二分类任务和多标签任务,pytorch有BCEloss和BCEWithLogitsLoss供使用选择,与前面介绍的NLLloss和CrossEntropy的关系类似,BCEloss前需要做Sigmoid激活,BCEWithLogitsLoss自带了Sigmoid,直接用logits作输入即可。

下图也是一图胜前言

pytorch推荐项目多任务51 pytorch多任务多loss_pytorch推荐项目多任务51_06


自己尝试用代码做一遍(三个loss算出来相等,推荐直接BCEWithLogitsLoss):

# 最原始计算
y = torch.Tensor([[1, 1, 0], [1, 0, 1]]) # 多标签one-hot
y_sigmoid = 1 / (1 + torch.exp(-z))
r11 = 1 * math.log(y_sigmoid[0, 0]) + 0 * math.log(1 - y_sigmoid[0, 0])
r12 = 1 * math.log(y_sigmoid[0, 1]) + 0 * math.log(1 - y_sigmoid[0, 1])
r13 = 0 * math.log(y_sigmoid[0, 2]) + 1 * math.log(1 - y_sigmoid[0, 2])

r21 = 1 * math.log(y_sigmoid[1, 0]) + 0 * math.log(1 - y_sigmoid[1, 0])
r22 = 0 * math.log(y_sigmoid[1, 1]) + 1 * math.log(1 - y_sigmoid[1, 1])
r23 = 1 * math.log(y_sigmoid[1, 2]) + 0 * math.log(1 - y_sigmoid[1, 2])

r1 = - (r11 + r12 + r13) / 3
r2 = - (r21 + r22 + r23) / 3
loss = (r1 + r2) / 2
print('directly compute', loss)


# BCE 公式
y = torch.Tensor([[1, 1, 0], [1, 0, 1]]) # 多标签one-hot
y_sigmoid = torch.sigmoid(z)
criterion = nn.BCELoss(reduction='mean')
loss = criterion(y_sigmoid, y)
print('BCE loss', loss)

# BCEwithLogits 公式
criterion = nn.BCEWithLogitsLoss(reduction='mean')
loss = criterion(z, y)
print('BCEwithLogits loss', loss)