交叉熵损失函数
交叉熵损失函数
1.基础知识
import torch import numpy as np # 自然对数底数e print("自然对数底数e",np.e) # 无穷大 print('无穷大',np.inf) # 无穷小 print('负无穷大',-np.inf)
输出:
# 自然对数底数e 2.718281828459045 # 无穷大 inf # 负无穷大 -inf
data = torch.tensor([-np.inf,-5,0,1,5,np.inf]) print(data) # tensor([-inf, -5., 0., 1., 5., inf]) # 指数运算,以e为底 print(torch.exp(data)) # tensor([0.0000e+00, 6.7379e-03, 1.0000e+00, 2.7183e+00, 1.4841e+02, inf])
2、softmax
定义
其中
通过
计算过程
代码
import torch # 定义一个一维张量 t_data = torch.tensor([0,1,2,3,4]) # 将张量指数化,以e为底 exp_data = torch.exp(t_data) # tensor([ 1.0000, 2.7183, 7.3891, 20.0855, 54.5981]) # 对张量求和 sum = torch.sum(exp_data) # tensor(85.7910) # 计算softmax概率 softmax = exp_data/sum print(softmax) # tensor([0.0117, 0.0317, 0.0861, 0.2341, 0.6364]) # 计算softmax之和 print(softmax.sum()) # tensor(1.)
import torch # 定义一个一维张量 t_data = torch.randn(4,4) print('t_data',t_data) # 将张量指数化,以e为底 exp_data = torch.exp(t_data) print('exp_data',exp_data) # 对张量求和 sum = torch.sum(exp_data,dim=1).reshape(-1,1) print(sum) # 计算softmax概率 softmax = exp_data/sum print('softmax',softmax) print('softmax.sum',softmax.sum()) # 计算softmax概率 softmax = exp_data/sum print('softmax',softmax) # 打印softmax张量总和 print('softmax.sum',softmax.sum()) # 计算softmax张量在行方向上每行之和 torch.sum(softmax,dim=1) # tensor([1.0000, 1.0000, 1.0000, 1.0000])
优缺点
优点:
- 输出是一个概率分布,所有元素的和为1,可以直接用于多类别分类问题。
- 可以将输入的实数值映射到0到1之间,使得输出更易于解释和理解。
- 具有平滑性,对输入的小变化相对较不敏感
缺点:
- 对于输入中的大值,Softmax函数的输出会趋向于1,而对于小值,输出会趋向于0,这可能导致梯度消失或梯度爆炸的问题。
- Softmax函数的计算相对复杂,当输入向量较大时,计算开销较大
2.log_softmax
定义
LogSoftmax函数是Softmax函数的一种变体,它将Softmax函数的输出取对数,以解决Softmax函数在数值计算上的一些问题。LogSoftmax函数常用于多类别分类问题中的损失函数计算。
代码
import torch import torch.nn.functional as F test = torch.tensor([[0,1,2], [1,4,5], [2,4,4]],dtype=float) # 使用softmax激活 softmax = F.softmax(test,dim=1) #tensor([[0.0900, 0.2447, 0.6652], # [0.0132, 0.2654, 0.7214], # [0.0634, 0.4683, 0.4683]], dtype=torch.float64) # 对softmax输出做log torch.log(softmax) # tensor([[-2.4076, -1.4076, -0.4076], # [-4.3266, -1.3266, -0.3266], # [-2.7586, -0.7586, -0.7586]], dtype=torch.float64) # 直接对数据集做log_softmax F.log_softmax(test,dim=1) # tensor([[-2.4076, -1.4076, -0.4076], # [-4.3266, -1.3266, -0.3266], # [-2.7586, -0.7586, -0.7586]], dtype=torch.float64)
如上结果发现:F.log_softmax(test,dim=1)结果与先F.softmax(test,dim=1)后torch.log结果一致。说明log_softmax就是对softmax结果再log化。
优缺点
优点:
- 避免了数值计算上的不稳定性:在Softmax函数中,当输入值较大时,指数运算可能导致数值溢出或计算不稳定。而LogSoftmax函数通过取对数的方式,将指数运算转换为加法运算,减少了数值计算上的不稳定性。
- 保留了相对概率关系:LogSoftmax函数的输出仍然保留了输入之间的相对概率关系,因为对数运算不会改变相对大小关系。
缺点:
- 输出不再是概率分布:与Softmax函数不同,LogSoftmax函数的输出不再是概率分布,因为取对数后的值不再满足概率的非负性和和为1的性质。因此,LogSoftmax函数的输出不能直接解释为概率。
总结来说,LogSoftmax函数通过取对数的方式解决了Softmax函数在数值计算上的不稳定性问题,但它的输出不再是概率分布。在实际应用中,LogSoftmax函数常用于损失函数的计算,而Softmax函数常用于预测和分类的输出。
3、CrossEntropyLoss与NLLLoss
CrossEntropyLoss: 交叉熵损失函数。
信息量
信息熵(information entropy)是信息论的基本概念。描述信息源各可能事件发生的不确定性。20世纪40年代,香农(C.E.Shannon)借鉴了热力学的概念,把信息中排除了冗余后的平均信息量称为“信息熵”,并给出了计算信息熵的数学表达式。 [1] 信息熵的提出解决了对信息的量化度量问题。
它是用来衡量一个事件的不确定性的;一个事件发生的概率越大,不确定性越小,则它所携带的信息量就越小。通常,一个信源发送出什么符号是不确定的,衡量它可以根据其出现的概率来度量。概率大,出现机会多,不确定性小;反之不确定性就大。
不确定性函数
- 不确定性函数
是概率 的减函数 - 可加性:两个独立符号所产生的不确定性应等于各自不确定性之和,即
- 不相关事件的概率独立:
为什么有一个负号?
其中,负号是为了确保信息一定是正数或者是0!概率
为什么底数为2?
这是因为,我们只需要信息量满足低概率事件x对应于高的信息量。那么对数的选择是任意的。我们只是遵循信息论的普遍传统,使用2作为对数的底!
假设
信息熵
信息量度量的是一个具体事件发生了所带来的信息,而熵则是在结果出来之前对可能产生的信息量的期望——考虑该随机变量的所有可能取值,即所有可能发生事件所带来的信息量的期望
在信源中,考虑的不是某一单个符号发生的不确定性,而是要考虑这个信源所有可能发生情况的平均不确定性
在信源中,考虑的不是某一单个符号发生的不确定性,而是要考虑这个信源所有可能发生情况的平均不确定性。若信源符号 有n种取值:
式中对数一般取 2 为底,单 位为比特。但是,也可以取其它对数底,采用其它相应的单位,它们间可用换底公式换算。
它是用来衡量一个系统的混乱程度的,代表一个系统中信息量的总和;信息量总和越大,表明这个系统不确定性就越大。
举个例子:假如小明和小王去打靶,那么打靶结果其实是一个0-1分布,
与之对应的,小王的熵(打靶的不确定度)为:
虽然小明打靶结果的不确定度较低,毕竟十次有9次都脱靶;但是小王打靶结果的不确定度更低,1000次射击只有1次脱靶,结果相当的确定。
交叉熵
它主要刻画的是实际输出(概率)与期望输出(概率)的距离,也就是交叉熵的值越小,两个概率分布就越接近。
假设现在有一个样本集中两个概率分布p,q,其中p为真实分布,q为非真实分布。假如,按照真实分布p来衡量识别一个样本所需要的编码长度的期望为:
上述公式其实就是信息熵公式
但是,如果采用错误的分布q来表示来自真实分布p的平均编码长度(p为期望输出,概率分布q为实际输出),则叫做交叉熵:
举个例子,假设
通过上面可以看出,q2与p更为接近,它的交叉熵也更小。
3、KL散度(相对熵)
信息熵和交叉熵的不同点,由公式可以看出,信息熵使用真实分布p计算信息量:
在机器学习的分类问题中,我们希望缩小模型预测和标签之间的差距,即KL散度越小越好,在这里由于KL散度中的
4、机器学习中多分类任务中的交叉熵损失函数
公式如下:
5、Pytorch nll_loss:负对数似然
概率是已知参数,推数据。似然是已知数据,推参数。
整个数据集的负对数似然损失就是对所有样本的损失进行求和或平均。N表示样本数量。使用概率模型来预测每个样本的类别概率分布,表示为
但是在pytorch中的F.nll_loss实现,是与上述公式不一样的!!!并没有取
代码
import torch def nll_loss(output, target): batch_size = output.size(0) return -output[range(batch_size), target].mean() # 示例 output = torch.tensor([[0.2, 0.3, 0.5], [0.1, 0.6, 0.3]]) target = torch.tensor([2, 0]) loss = nll_loss(torch.log(output), target) print(loss) # tensor(1.4979) F.nll_loss(torch.log(output),target) # # tensor(1.4979)
上述代码中可以看到,自定义的nll_loss函数,模拟公式写出,计算结果和torch自带的nll_loss计算结果一致
6、Pytorch CrossEntropyLoss:交叉熵
交叉熵定义公式:
其中
所以交叉熵损失函数可以直接简化为:
代码
import torch import torch.nn as nn input = torch.tensor([[ 0.8082, 1.3686, -0.6107], [ 1.2787, 0.1579, 0.6178], [-0.6033, -1.1306, 0.0672], [-0.7814, 0.1185, -0.2945]]) # softmax激活,转化为概率 softmax_output = nn.Softmax(dim=1)(input) print(softmax_output) #tensor([[0.3341, 0.5851, 0.0808], # [0.5428, 0.1770, 0.2803], # [0.2821, 0.1665, 0.5515], # [0.1966, 0.4835, 0.3199]]) # 标签采用onehot编码,y_target中的元素表示onehot中为1的元素位置,比如y_target = [1,0,2,1]表示的onehot为[[0,1,0],[1,0,0],[0,0,1],[0,1,0]] y_target = [1,0,2,1]
计算
log_softmax_output = torch.log(softmax_output) log_softmax_output #tensor([[-1.0964, -0.5360, -2.5153], # [-0.6111, -1.7319, -1.2720], # [-1.2657, -1.7930, -0.5952], # [-1.6266, -0.7267, -1.1397]])
手动从log_softmax_output输出中按照y_target所表示的onehot元素1位置,找到对应的预测概率,手动做
loss =-(-0.5360-0.6111-0.5952-0.7267) / 4 print(loss) # 0.61725
使用torch自带的CrossEntropyLoss计算:
import torch import torch.nn as nn input = torch.tensor([[ 0.8082, 1.3686, -0.6107], [ 1.2787, 0.1579, 0.6178], [-0.6033, -1.1306, 0.0672], [-0.7814, 0.1185, -0.2945]],requires_grad=True) target = torch.tensor([1,0,2,1]) loss = nn.CrossEntropyLoss() output = loss(input, target) output.backward() print(output)# 0.61725
发现两者结果一致
7、pytorch nll_loss 与 CrossEntropyLoss的关系
负对数似然函数公式:
N表示样本数量,
交叉熵损失函数公式:
上述为一个样本公式,如果是N个样本,则表示形式为如下:
在onehot标签编码形势下,由于只计算onehot编码中不为0的项,简化后的交叉熵编码和nll_loss公式是一致的。我们可以发现,Nll_loss的完整形式和交叉熵公式其实是一致的,但是在pytorch中的实现,并没有对输入的概率取
总的来说,onehot的标签编码形式,导致了这种统一性,一切建立在onehot标签编码基础之上。
代码
import torch import torch.nn.functional as F # 创建随机二维(3,3)结构的矩阵张量 x_input=torch.randn(3,3) # 定义一个目标张量 y_target=torch.tensor([1,2,0]) print('x_input:\n',x_input) #tensor([[ 0.6821, -1.3179, -1.0447], # [-0.2849, -0.4089, 1.2510], # [ 0.2702, 1.3541, 1.1645]]) # 直接使用交叉熵损失函数计算损失 crossentropyloss_output=F.cross_entropy(x_input,y_target) print(crossentropyloss_output) # tensor(1.4898)
F.cross_entropy计算结果为tensor(1.4898)
# 使用经过log_softmax之后的输入用nll_loss计算损失 log_softmax_output = F.log_softmax(x_input,dim=1) log_softmax_to_nlloss_output = F.nll_loss(log_softmax_output,y_target) print(log_softmax_to_nlloss_output) #tensor(1.4898)
输入经过og_softmax后的F.nll_loss计算结果为tensor(1.4898)
# 直接使用原来输入与目标张量计算nll_loss损失 nll_loss_output = F.nll_loss(x_input,y_target) print(nll_loss_output) # tensor(-0.0678)
直接使用F.nll_loss计算结果为tensor(-0.0678)
总结:通过上述实验代码可以看出,pytorch中自带的两种交叉熵分类损失函数,不同点和区别就在于,F.nll_loss直接使用交叉熵运算,而F.cross_entropy则是比F.nll_loss在输入张量上多了一个预处理:输入张量要首先经过log_softmax进行数据变换,然后再调用F.nll_loss。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)