pytorch | Softmax->Log->NLLLoss->CrossEntropyLoss

1.torch.nn.Softmax

做什么的:将Softmax函数应用于n维输入张量,重新缩放它们,以便n维输出张量的元素位于[0,1]范围内,并且总和为1。

注意事项:需要指定按照行和为1还是列和为1,参数设置dim=1表示行和为1,dim=0表示列和为1。

计算公式:

难点:看到公式,手动该如何计算?假设需要进行softmax操作的张量是:[[1,2,3],[1,2,4]]是一个2*3规模的张量,经过softmax后,希望行和为1,手动计算过程(把公式列举出来,计算可以使用百度帮忙计算):

根据计算公式,把算式列出来,通过百度计算出结果。

    

手动计算结果出来了,接着写段代码来验证一下,看看是否和计算结果一致。

代码:

import torch
import torch.nn as nn
m = nn.Softmax(dim=1)
input = torch.Tensor([[1,2,3],[1,2,4]])
print(input)
output = m(input)
print(output)

代码运行结果:

观察结果:结果都是小数点后保留4位小数,对比第一行手动计算结果,是一致的。接着按照同样的公式,把第二行的结果计算出来

       

对比结果中的第二行,确实是一致的。观察结果:每行的和为1,这是因为dim=1这个参数所控制的。

总结:该公式的意义有点像计算每个值占总和中的比例,总和为1,每个值的范围是[0,1]之间。可以理解为概率。

2.torch.nn.LogSoftmax

做什么的:通过softmax计算出来的占比,也可以理解为概率大小,因为都是0-1之间的数,概率很小的时候,还要进行连乘操作,会导致结果更小,为了避免下溢,将积运算转换为和运算,使用的公式是:

注意事项:公式中只写了一个log,底数没有给出,需要首先确定底数是多少,一般底数比较特殊的就是2和e以及10,e可以排除,如果是e,一般是写为ln,10为底一般可以写为lg,所以优先猜测是以2位底的,其次,计算机机器码就是0和1组成,所以是2的可能性很大。

计算公式:

观察公式,好像就是比Softmax的结果上加了一个log,也就是取对数。那我把上面计算结果去对数看看:

python中有数学公式,直接调用,这次就不要傻傻的去百度计算结果,因为输入的时候不好计算,对数m为底n的对数,输入不方便,所以调用现成的。

代码:

x00 = math.log(0.0900)
x01 = math.log(0.2447)
x02 = math.log(0.6652)
x10 = math.log(0.0420)
x11 = math.log(0.1142)
x12 = math.log(0.8438)
print(x00, x01, x02)
print(x10, x11, x12)

运行结果:

这个计算结果就理解为手动计算出来的结果,也就是将softmax计算出来的结果,取了对数,可能存在一些误差,softmax计算出来的结果是保留了4位小数的。

为了验证手动计算结果是否准确,上代码,看看运行结果

代码:

​​import torch
import torch.nn as nn
m = nn.LogSoftmax(dim=1)
input = torch.Tensor([[1,2,3],[1,2,4]])
print(input)
output = m(input)
print(output)

代码运行结果:

结果显示:和手动结算结果是一致的。说明我们对公式的理解没有问题。注意:举例都是在dim=1参数下做的,不要把条件搞忘了。

总结:在softmax上加了对数,可以理解为对数概率。

3.torch.nn.NLLLoss

做什么的:负对数似然损失,训练分类。

手动计算过程:首先需要明确的点是:NLLLoss函数输入input之前,需要对input进行log_softmax处理。

假设数据是:[[1,2,3],[1,2,4]],标签为:[1,2] dim设置为1,按行求和

首先计算softmax,计算结果是[[0.0900,0.2447,0.6652],[0.0420,0.1142,0.8438]]

再取对数,计算结果是:[[-2.4076,-1.4076,-0.4076],[-3.1698,-2.1698,-0.1698]]

计算负对数似然,首先取负数,再计算,默认是取均值。

根据标签,1表示第一行的索引为1的的数,即:-1.4076

2表示第二行的索引为2的数,即:-0.1698

取负数后,1.4076和0.1698

求均值:(1.4076+0.1698)/2 = 1.5774/2=0.7887

注意事项:标签中的索引号范围是:[0,C-1],比如分为3类,则最多出现:0,1,2,不可能出现比0小或者比2大的数成为标签中的元素。

写段代码验证一下手动计算结果。

代码:

m = nn.LogSoftmax(dim=1)  # 模型
loss = nn.NLLLoss()  # 计算损失的标准
input = torch.Tensor([[1,2,3],[1,2,4]])
print('input:',input)
target = torch.tensor([1, 2])#每一项的范围是[0,C-1]
print('target:', target)
output = loss(m(input), target)
print('output:', output)

代码运行结果:

4.torch.nn.CrossEntropyLoss

前面所说的一切,都是为了给交叉熵作铺垫,^_^,先看下官网怎么说的:

这句描述说的是:交叉熵是LogSoftmax()和NLLLoss()组合而成,而LogSoftmax()实际上是softmax+log,所以通俗的讲:交叉熵是softmax+log+NLLLoss组合而成。并没有什么新的内容。

变相的说,调用交叉熵的结果,和上面一步一步实现的结果是一样的。

代码:

input = torch.Tensor([[1,2,3],[1,2,4]])
target = torch.Tensor([1,2]).long()
criterion = nn.CrossEntropyLoss()
loss = criterion(input, target)
print(loss)

代码运行结果:

再单独一步一步执行测试一下:

代码:

import torch
import torch.nn as nn
input = torch.Tensor([[1, 2, 3], [1, 2, 4]])
target = torch.Tensor([1, 2]).long()
m1 = nn.Softmax(dim=1)
r1 = m1(input)
print('softmax结果:\n', r1)
r2 = torch.log(r1)
print('log结果:\n', r2)
m2 = nn.NLLLoss()
loss = m2(r2, target)
print('NLLLoss结果:\n',loss)
print(loss)

代码运行结果:

总结:我感觉最难的点是公式看不懂,不知道如何展开,其次,就是测试的时候最好是自己写数字,因为随机产生的,不好计算,自己写的,手动计算的时候稍微好计算一些。总之,无论多艰难,一步一步走,大不了多花点时间,总会弄明白。

猜你喜欢

转载自blog.csdn.net/songxiaolingbaobao/article/details/112304708