关于Pytorch的softmax与Cross_entropy交叉熵的测试

主要是搞不清在使用CrossEntropyLoss是的输入大小size。于是分析了softmax,Nll_loss,crossentropyloss三个函数。因为 crossentropyloss = softmax + Nll_loss。
(这点参考@小风的文章[PyTorch的 nn.CrossEntropyLoss() 方法详解])(https://blog.csdn.net/weixin_44298740/article/details/104928756)

主要结论:
1、softmax是输入与输出一样size,但是对不同维度做,得到的结果tensor的shape要把那个dim去掉。
2、交叉熵的Target比输入少一维,少的是C分类数。target 的tensor的值也必须是分类数。shape和值不对,都不能运算。

1、softmax / log_softmax

先看官方文档

def log_softmax(input, dim=None, _stacklevel=3, dtype=None):
    # type: (Tensor, Optional[int], int, Optional[int]) -> Tensor

log_softmax 是输入为一个tensor和一个维度,这个维度是指对tensor中那个维度进行概率计算。softmax的计算结果,输出的size=输入的size,该维度的数值加起来=1。如果对该维度的数求和,则维度减去该维度。
例如:输入[2,3,4], dim=2, 则求和结果为[2,3]
dim=1, [2,4],
dim=0, [3,4]
log_softmax对softmax的结果求对数,和就不一定了。维度变化一致。

看例子:

a = list(range(0,24,1))
a[1]=9
a[15]=5    # 这里修改了2个数,以免激活后的位置结果一样
input = torch.Tensor(a).reshape(2,3,4)  # N,C,in_size
print()
print('Input:')
print(input.shape)   # [2,3,4]
print(input)

SF = nn.Softmax(dim=2)
out_softmax = SF(input)
print()
print('Result of softmax:')
print(out_softmax.shape)   # [2,3,4]  与 输入一样
print(out_softmax)
s_max = out_softmax.max(dim=2)[0]
s_loc = out_softmax.max(dim=2)[1]
print()
print('计算结果的最大值的大小/位置:')
print(s_max)
print(s_loc)

运行结果:

Input:
torch.Size([2, 3, 4])
tensor([[[ 0.,  9.,  2.,  3.],
         [ 4.,  5.,  6.,  7.],
         [ 8.,  9., 10., 11.]],

        [[12., 13., 14.,  5.],
         [16., 17., 18., 19.],
         [20., 21., 22., 23.]]])

Result of softmax:
torch.Size([2, 3, 4])
tensor([[[1.2298e-04, 9.9650e-01, 9.0869e-04, 2.4701e-03],
         [3.2059e-02, 8.7144e-02, 2.3688e-01, 6.4391e-01],
         [3.2059e-02, 8.7144e-02, 2.3688e-01, 6.4391e-01]],

        [[9.0023e-02, 2.4471e-01, 6.6519e-01, 8.2091e-05],
         [3.2059e-02, 8.7144e-02, 2.3688e-01, 6.4391e-01],
         [3.2059e-02, 8.7144e-02, 2.3688e-01, 6.4391e-01]]])
         
计算结果的最大值的大小/位置:
tensor([[0.9965, 0.6439, 0.6439],  # 可以理解为每页,每行中所有列的最大值,及其位置
        [0.6652, 0.6439, 0.6439]])
tensor([[1, 3, 3],
        [2, 3, 3]])```
注意,输出结果是降维了的,从2,3,4 变成 2,3。

求和结果看以下代码:

sum = out_softmax.sum(dim=2)
print()
print('求和结果:')
print(sum.shape)
print(sum)

结果:

求和结果:
torch.Size([2, 3])
tensor([[1., 1., 1.],
        [1., 1., 1.]])

手工测试一下:

a = [1.2298e-04, 9.9650e-01, 9.0869e-04, 2.4701e-03]
print(sum(a))   # 1.0000017700000001

如果把两处的dim都换为1,

Result of softmax:
torch.Size([2, 3, 4])
tensor([[[3.2932e-04, 4.9546e-01, 3.2932e-04, 3.2932e-04],
         [1.7980e-02, 9.0747e-03, 1.7980e-02, 1.7980e-02],
         [9.8169e-01, 4.9546e-01, 9.8169e-01, 9.8169e-01]],

        [[3.2932e-04, 3.2932e-04, 3.2932e-04, 1.4956e-08],
         [1.7980e-02, 1.7980e-02, 1.7980e-02, 1.7986e-02],
         [9.8169e-01, 9.8169e-01, 9.8169e-01, 9.8201e-01]]])

计算结果的最大值的大小/位置:
tensor([[4.9546e-01, 1.7980e-02, 9.8169e-01],
        [3.2932e-04, 1.7986e-02, 9.8201e-01]])
tensor([[1, 0, 0],
        [0, 3, 3]])

求和结果:
torch.Size([2, 4])
tensor([[1., 1., 1., 1.],
        [1., 1., 1., 1.]])   #注意,这里shape变了,因为是对行计算,所以每列都有值,共4列

求和测试:

b = [3.2932e-04, 1.7980e-02, 9.8169e-01]
print(sum(b))  # 0.9999993199999999

如果dim改为0

Result of softmax:
torch.Size([2, 3, 4])
tensor([[[6.1442e-06, 1.7986e-02, 6.1442e-06, 1.1920e-01],
         [6.1442e-06, 6.1442e-06, 6.1442e-06, 6.1442e-06],
         [6.1442e-06, 6.1442e-06, 6.1442e-06, 6.1442e-06]],

        [[9.9999e-01, 9.8201e-01, 9.9999e-01, 8.8080e-01],
         [9.9999e-01, 9.9999e-01, 9.9999e-01, 9.9999e-01],
         [9.9999e-01, 9.9999e-01, 9.9999e-01, 9.9999e-01]]])

计算结果的最大值的大小/位置:
tensor([[1.1920e-01, 6.1442e-06, 6.1442e-06],
        [9.9999e-01, 9.9999e-01, 9.9999e-01]])
tensor([[3, 0, 0],
        [0, 0, 0]])
求和结果:
torch.Size([3, 4])
tensor([[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]])

求和测试:

c = [6.1442e-06,9.9999e-01]
print(sum(c))   # 0.9999961442

2、NLL_Loss

def nll_loss(input, target, weight=None, size_average=None, ignore_index=-100,
reduce=None, reduction=‘mean’):
# type: (Tensor, Tensor, Optional[Tensor], Optional[bool], int, Optional[bool], str) -> Tensor
主要的是input和target,

Args:
        input: :math:`(N, C)` where `C = number of classes` or :math:`(N, C, H, W)`
            in case of 2D Loss, or :math:`(N, C, d_1, d_2, ..., d_K)` where :math:`K \geq 1`
            in the case of K-dimensional loss.
        target: :math:`(N)` where each value is :math:`0 \leq \text{targets}[i] \leq C-1`,
            or :math:`(N, d_1, d_2, ..., d_K)` where :math:`K \geq 1` for
            K-dimensional loss.

可见简单的情况,输入是一个二维矩阵,N代表要预测的项数,C代表分类数,目标是一个长度为N的向量。相当于,有N个东西要分为C类,每一个类由前面的softmax给出了一个概率,概率最大的那个分类数就是Target的值。下面是官方的示例:

import torch
import torch.nn.functional as F

input = torch.randn(3, 5, requires_grad=True)
print(input)
target = torch.randint(5, (3,), dtype=torch.int64)
 # 在 [0,5)中随机选3个数, 必须是Long整数,必须走分类数中,
 # 这是一个大坑哈。也就是说,结果必须代表分类的数。在class的定义中有说明:
 # each element in target has to have 0 <= value < C
print(target)
loss = F.cross_entropy(input, target)
print(loss)
loss.backward()
print(loss)

结果如下:

tensor([[ 0.1578,  0.2282, -0.6279, -1.0986,  1.5271],
        [ 0.7805, -0.4901, -1.2760, -0.7056, -0.0250],
        [ 0.1768, -0.7692,  0.7770,  1.9761,  0.5800]], requires_grad=True)
tensor([2, 0, 0])
tensor(1.9343, grad_fn=<NllLossBackward>)
tensor(1.9343, grad_fn=<NllLossBackward>)

注意:每次运行的结果不一样。
还有,F中的cross_entropy与nn中的class NLLLoss用法不完全一样哈:
以下例子是官方给的,可以自己试一下:

        >>> m = nn.LogSoftmax(dim=1)
        >>> loss = nn.NLLLoss()
        >>> # input is of size N x C = 3 x 5
        >>> input = torch.randn(3, 5, requires_grad=True)
        >>> # each element in target has to have 0 <= value < C
        >>> target = torch.tensor([1, 0, 4])
        >>> output = loss(m(input), target)
        >>> output.backward()
        >>>
        >>>
        >>> # 2D loss example (used, for example, with image inputs)
        >>> N, C = 5, 4
        >>> loss = nn.NLLLoss()
        >>> # input is of size N x C x height x width
        >>> data = torch.randn(N, 16, 10, 10)
        >>> conv = nn.Conv2d(16, C, (3, 3))   # C为通道数,3为核数,卷积10x10的结果为8x8
        >>> m = nn.LogSoftmax(dim=1)
        >>> # each element in target has to have 0 <= value < C
        >>> target = torch.empty(N, 8, 8, dtype=torch.long).random_(0, C)  #在0~C中取任意数
        >>> output = loss(m(conv(data)), target)
        >>> output.backward()

3、 class CrossEntropyLoss()

这个用起来就简单了:

        >>> loss = nn.CrossEntropyLoss()
        >>> input = torch.randn(3, 5, requires_grad=True)
        >>> target = torch.empty(3, dtype=torch.long).random_(5)
        # random_(5), 在0~5(不包括5)中选随机数
        >>> output = loss(input, target)
        >>> output.backward()

不需要做softmax了。

所以说,要想用交叉熵,target必须是分类结果。
我有个实验,直接给的数值,总是报错,现在看来显然是不行的。
可以考虑把数值转换为分类数。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值