Pytorch中的分类损失函数比较NLLLoss与CrossEntropyLoss

参考来源,仅作为学习笔记


二分类

对于一个二分类问题,比如我们有一个样本,有两个不同的模型对他进行分类,那么它们的输出都应该是一个二维向量,比如:

模型一的输出为:pred_y1=[0.8,0.2]
模型二的输出为:pred_y2=[0.6,0.4]

需要注意的是,这里的数值已经经过了sigmoid激活函数,所以0.8+0.2=1,比如样本的真实标签是:true_y=[1,0]

现在我们来求这两个模型对于这一个类别的分类损失,怎么求?先给出二分类损失函数,表达式如下:
L = − y l o g ( y ′ ) + ( 1 − y ) l o g ( 1 − y ′ ) L = -ylog(y')+(1-y)log(1-y') L=ylog(y)+(1y)log(1y)
这里的y表示的是真实地标签,y’表示模型的输出标签,我们带入进去计算得到:

cost1 = -[1log0.8+(1-1)log0.2] = 0.22314
cost2 = -[1log0.6+(1-1)log0.4] = 0.51083

可以看出,第一个模型的损失更小,即模型更好,而且直观来看,第一个模型觉得正确的概率是0.8,自然比0.6要好。


多分类

交叉熵是一个信息论中的概念,它原来是用来估算平均编码长度的。给定两个概率分布p和q,通过q来表示p的交叉熵为上式,交叉熵刻画的是两个概率分布之间的距离,或可以说它刻画的是通过概率分布q来表达概率分布p的困难程度,p代表正确答案,q代表的是预测值,交叉熵越小,两个概率的分布约接近。
H ( p , q ) = − ∑ x ( p ( x ) ∗ l o g ( q ( x ) ) ) H(p,q) = -\sum_x(p(x) * log(q(x))) H(p,q)=x(p(x)log(q(x)))


过程总结

不管是二分类,还是多分类问题,其实在计算损失函数的过程都经历了三个步骤:

(1) 激活函数。通过激活函数sigmoid或者是softmax将输出值缩放到[0,1]之间,

(2) 求对数。计算缩放之后的向量的对数值,即所谓的logy的值,求对数之后的值在[-infinite,0]之间

(3) 累加求和。根据损失函数的定义,将标签和输出值逐元素相乘再求和,最后再添加一个负号求相反数,得到一个正数损失。


Pytorch中损失函数的实现

求多分类交叉熵损失有三种途径可以实现,分别是:

(1) 三步实现:softmax+log+nll_loss
(2) 两步实现:log_softmax+nll_loss
(3) 一步实现:crossEntropyLoss

代码实现

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

# 比如这是一个模型的输出,本案例为一个三类别的分类,共有四组样本,如下:
pred_y = np.array([[0.30722019, -0.8358033, -1.24752918],
                   [0.72186664, 0.58657704, -0.25026393],
                   [0.16449865, -0.44255082, 0.68046693],
                   [-0.52082402, 1.71407838, -1.36618063]])
pred_y = torch.from_numpy(pred_y)

# 真实的标签如下所示
true_y = np.array([[1, 0, 0],
                   [0, 1, 0],
                   [0, 1, 0],
                   [0, 0, 1]])
true_y = torch.from_numpy(true_y)
target = np.argmax(true_y, axis=1)  # (4,) #其实就是获得每一给类别的整数值,这个和tensorflow里面不一样哦 内部会自动转换为one-hot形式
target = torch.LongTensor(target)  # (4,)

print(target)  # tensor([0,1,1,2])
print("-----------------------------------------------------------")


'''
三步实现:softmax + log + nll_loss如下:
'''
# 第一步:使用激活函数softmax进行缩放
pred_y = F.softmax(pred_y, dim=1)
print(pred_y)
print('-----------------------------------------------------------')

# 第二步:对每一个缩放之后的值求对数log
pred_y = torch.log(pred_y)
print(pred_y)
print('-----------------------------------------------------------')

# 第三步:求交叉熵损失
loss = F.nll_loss(pred_y, target)
print(loss)  # 最终的损失为:tensor(1.5929, dtype=torch.float64)


'''
两步实现:log_softmax+nll_loss
'''


# 第一步:直接使用log_softmax,相当于softmax+log
pred_y=F.log_softmax(pred_y,dim=1)
print(pred_y)
print('-----------------------------------------------------------')

# 第二步:求交叉熵损失
loss=F.nll_loss(pred_y,target)
print(loss) # tensor(1.5929, dtype=torch.float64)


'''
一步实现
'''
loss=F.cross_entropy(pred_y,target)
print(loss) # tensor(1.5929, dtype=torch.float64)

注意

在求交叉熵损失的时候,需要注意的是,不管是使用 nll_loss函数,还是直接使用cross_entropy函数,都需要传递一个target参数,这个参数表示的是真实的类别,对应于一个列表的形式而不是一个二维数组,这个和tensorflow是不一样的哦!(Pytorch分类损失函数内部会自动把列表形式(一维数组形式)的整数索引转换为one-hot表示)

猜你喜欢

转载自blog.csdn.net/Jeremy_lf/article/details/114978684