【Deep learning】——一文知torch分类任务的交叉熵Loss(多分类、多标签任务)
- 多分类任务
- 多标签任务
之前对torch的分类Loss还有好些疑惑,这次一网打进不留。文章包含多分类任务loss和多标签任务loss【softmax/NLLloss/CrossEntropyLoss/sigmoid/BCEloss/BCEWithLogitsLoss】
参考博客:
Pytorch入门笔记(多分类问题)Pytorch踩坑记之交叉熵(nn.CrossEntropy,nn.NLLLoss,nn.BCELoss的区别和使用)pytorch 多标签分类以及BCEloss
多分类任务
上面三个图胜千言,总结如下:
自己尝试用代码做一遍(三个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
对二分类问题,其Loss也叫交叉熵,但公式表示形式不同,与多分类的softmax后-y*logy^相比,给人的感觉就像考虑了yn=0的样本的损失。
而对于二分类任务和多标签任务,pytorch有BCEloss和BCEWithLogitsLoss供使用选择,与前面介绍的NLLloss和CrossEntropy的关系类似,BCEloss前需要做Sigmoid激活,BCEWithLogitsLoss自带了Sigmoid,直接用logits作输入即可。
下图也是一图胜前言
自己尝试用代码做一遍(三个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)