交叉熵的数学原理—pytorch中的CrossEntropyLoss()函数
对于二分类来说,我们使用的是Binary Cross Entropy Loss,公式如下:
因为ln后的数为负数,所以加负号,保证我们是优化最小(不然就要往最大值优化了);
如当l=1时(标签为正样本),此时Loss = -logp, 我们要使p的值尽量大,loss才会尽量小,最好能到1,就代表分类正确了
多分类
这里的label会被展开为one-hot编码
主要讨论图像像素级的分类
# -*- coding: utf-8 -*-
'''
@File : 多分类.py
@Author: Moule Lin
@Date : 2020/11/1 17:53
@Github: https://github.com/moulelin
'''
'''
CrossEntropy 的原理
Pytorch中CrossEntropyLoss()函数的主要是将softmax-log-NLLLoss合并到一块得到的结果。
1、Softmax后的数值都在0~1之间,所以ln之后值域是负无穷到0。
2、然后将Softmax之后的结果取log,将乘法改成加法减少计算量,同时保障函数的单调性 。
3、NLLLoss的结果就是把上面的输出与Label对应的那个值拿出来(下面例子中就是:
将log_output\logsoftmax_output中与y_target对应的值拿出来),去掉负号,再求均值。
Tip: CrossEntropyLoss() 会把Label变成ont-hot形式
'''
import torch
import torch.nn as nn
loss = nn.CrossEntropyLoss()# 使用默认参数
input_data = torch.randn(1,3,2,2,requires_grad=True) # 第一维度对应channel,CrossEntropy是针对channel进行的
target = torch.tensor([[0, 1],[2, 0]],dtype=torch.long).unsqueeze(0) # 对应2*2大小的矩阵,每个点都存在一个类别
print(input_data)
print(target.shape)
print(input_data.shape)
loss_item = loss(input_data,target)
print(loss_item)
# 上面是直接通过Cross Entropy,下面一步步实现
softmax_func=nn.Softmax(dim=1) # 将channel维度进行压缩到0-1
softmax_out = softmax_func(input_data)
print("经过softmax如下所示:")
print(softmax_out)
print("经过log如下所示:")
log_output=torch.log(softmax_out) # 取对数
print(log_output)
print("经过NLLLoss如下所示:")
nllloss_func=nn.NLLLoss()
nlloss_output=nllloss_func(log_output,target)
print(nlloss_output)
# import math
# loss = nn.CrossEntropyLoss()
# data = torch.randn(1,3,1,5,requires_grad=True)
# label = torch.empty((1,1,5),dtype=torch.long).random_(5)
# out = loss(data,label)
# print(data.shape)
# print(label.shape)
# print(out)
输出如下:
tensor([[[[-0.2493, -0.9333],
[ 0.0676, 1.2035]],
[[ 0.2987, -0.6044],
[-0.1341, 0.0938]],
[[-1.3529, -0.2693],
[-0.3259, -0.7915]]]], requires_grad=True)
torch.Size([1, 2, 2])
torch.Size([1, 3, 2, 2])
tensor(0.9862, grad_fn=<NllLoss2DBackward>)
经过softmax如下所示:
tensor([[[[0.3266, 0.2308],
[0.4013, 0.6823]],
[[0.5650, 0.3207],
[0.3280, 0.2249]],
[[0.1083, 0.4484],
[0.2707, 0.0928]]]], grad_fn=<SoftmaxBackward>)
经过log如下所示:
tensor([[[[-1.1189, -1.4660],
[-0.9131, -0.3823]],
[[-0.5709, -1.1371],
[-1.1148, -1.4921]],
[[-2.2225, -0.8020],
[-1.3066, -2.3773]]]], grad_fn=<LogBackward>)
经过NLLLoss如下所示:
tensor(0.9862, grad_fn=<NllLoss2DBackward>)
这里一共有四个像素点,分别是[[0, 1],[2, 0]],对于第一个像素点来说,它的分类结果是0,输出为3类,即分为三类,所以会被展开为[1,0,0]
代表第一个元素的第0个通道上的数值是我想取出来的,这里就是-1.1189,同理,对于第二个像素点来说展开为[0,1,0],取得-1.1371,第三个[0,0,1],取得-1.3066,第四个[1,0,0],取得-0.3823, 将这四个数取绝对值相加再除4,(1.1189+1.1371+1.3066+0.3823)/4 = 0.9862
所以说,对于一个图像来说,输入维度为B,C,W,H,label对应的维度就是B,1,W,H就可以