CrossEntropyLoss() 函数联合调用了 nn.LogSoftmax() 和 nn.NLLLoss()。
关于交叉熵函数的公式详见:
交叉熵损失函数原理详解
CrossEntropyLoss() 函数的计算过程可以拆解为如下四个步骤:
1、对输出的结果进行softmax操作,因为softmax操作可以将所有输入值都归为[0,1]之间,且所有值之和为1,符合概率分布的特性。
2、对softmax结果进行log运算,求出都是小于0的值
3、对真实概率值进行one-hot编码
4、利用下面的公式求出最终的loss值
C
r
o
s
s
E
n
t
r
o
p
y
L
o
s
s
(
x
)
=
−
∑
i
=
1
n
O
n
e
H
o
t
(
t
a
r
g
e
t
i
)
∗
l
o
g
s
o
f
t
m
a
x
(
i
n
p
u
t
)
i
CrossEntropyLoss(x) = - \sum_{i=1}^{n} OneHot(target_i) * log^{softmax(input)_i}
CrossEntropyLoss(x)=−i=1∑nOneHot(targeti)∗logsoftmax(input)i
不难看出NLLloss+log+softmax就是CrossEntropyLoss(softmax版的交叉熵损失函数),而其中的NLLloss就是在做交叉熵损失函数的最后一步:预测结果的取负求和。
一段代码带你上高速:
原生手写CrossEntropyLoss()函数与PyTorch里的CrossEntropyLoss():
import torch
import torch.nn as nn
import torch.nn.functional as F
def softmax(x):
r"""
这里的公式是:
.. math::
softmax(x_i) = \frac{\exp^{x_i}}{\sum_{i=0}^N (\exp^{x_i})}
:param x:
:return:
"""
# return torch.exp(x) / torch.unsqueeze(torch.sum(torch.exp(x), dim=1), dim=1)
t = torch.zeros_like(x)
sum_array = []
for idx in range(len(x)):
t[idx] = torch.exp(x[idx]) / torch.sum(torch.exp(x[idx]))
sum_array.append(torch.sum(torch.exp(x[idx])))
print(sum_array)
print(torch.unsqueeze(torch.sum(torch.exp(x), dim=1), dim=1))
return t
def manual_cross_entropy_loss(inputs, target):
one_hot_target = F.one_hot(target, 10)
print(one_hot_target)
# print(softmax_)
# print(F.softmax(inputs, dim=-1))
# cross_entropy_loss = -torch.sum(one_hot_target * torch.log(F.softmax(inputs, dim=-1))) / len(inputs)
cross_entropy_loss = -torch.sum(one_hot_target * torch.log(softmax(inputs))) / len(inputs)
print(cross_entropy_loss)
def call_cross_entropy_loss(inputs, target):
"""
调用真实的CrossEntropyLoss()
:return:
"""
loss = nn.CrossEntropyLoss()
loss = loss(inputs, target)
print(loss)
if __name__ == '__main__':
torch.random.manual_seed(0)
inputs = torch.randn(3, 10)
# 获取inputs的最大值的索引
target = torch.argmax(inputs, dim=-1)
print(target)
manual_cross_entropy_loss(inputs=inputs, target=target)
call_cross_entropy_loss(inputs=inputs, target=target)