深度学习 Pytorch 神经网络的损失函数

news2025/1/31 2:22:54

本节开始将以分类神经网络为例,展示神经网络的学习和训练过程。在介绍PyTorch的基本工具AutoGrad库时,我们系统地介绍过数学中的优化问题和优化思想,我们介绍了最小二乘法以及梯度下降法这两个入门级优化算法的具体操作,并使用AutoGrad库实现了他们。接下来,我们将从梯度下降法向外拓展,介绍神经网络的损失函数、常用优化算法等信息,实现神经网络的学习和迭代。本节主要讲解神经网络常用的损失函数,并在PyTorch中实现这些函数。


37 机器学习中的优化思想

在之前的学习中,我们建立神经网络时总是先设定好 w w w b b b的值(或者由我们调用的Pytorch类帮助我们随机生成权重向量 w w w),接着通过加和求出 z z z,再在 z z z上嵌套sigmoid或者softmax函数,最终获得神经网络的输出。

在这里插入图片描述

我们的代码及计算流程总是从神经网络的左侧向右侧进行计算,这是神经网络的 正向传播过程 。但很明显这不是神经网络算法的全流程,这个流程虽然可以输出预测结果,但无法保证神经网络的输出结果与真实值接近。

在之前的学习中,我们了解到线性回归的任务就是构造一个预测函数来映射输入的特征矩阵 X X X和标签值 y y y的线性关系。构造预测函数核心就是 找出模型的权重向量 w w w,并令线性回归的输出结果与真实值相近 ,也就是求解线性方程组中的 w w w b b b

对神经网络而言也是如此,我们的核心任务是 求解一组最适合的 w w w b b b,令神经网络的输出结果和真实值相近 。找寻这个 w w w b b b的过程就是 “学习” ,也叫做 “训练” 或者 “建模”

那我们如何评价 w w w b b b是否合适呢?又如何衡量输出结果与真实值之间的差异大小呢?

此时就需要使用机器学习中通用的优化流程了。在讲解autograd时其实已经提过这个优化流程:

在这里插入图片描述

  1. 提出基本模型,明确目标

    基本模型就是自建的神经网络架构,我们需要求解的就是神经网络架构中的权重向量 w w w

  2. 确定损失函数/目标函数

    我们需要定义某个评估指标,用以衡量模型权重为 w w w的情况下,预测结果与真实结果的差异。当真实值与预测值差异越大时,我们就认为神经网络学习过程中丢失了许多信息,丢失的这部分被形象地称为“损失”,因此评估真实值与预测值差异的函数我们称为“损失函数”。

  3. 确定适合的优化算法

  4. 利用优化算法,最小化损失函数,求解最佳权重 w w w(训练)

对线性回归,我们的损失函数是SSE,优化算法是最小二乘法和梯度下降法,两者都是对机器学习来说非常重要的优化算法。但遗憾的是,最小二乘法作为入门级优化算法,有较多的假设和先决条件,不足以应对神经网络需要被应用的各种复杂环境。梯度下降法应用广泛,不过也有很多问题需要改进。

接下来将以分类深层神经网络为例介绍神经网络中所使用的入门级损失函数及优化算法。


38 回归:误差平方和SSE

对于回归类神经网络而言,最常见的损失函数是SSESum of the Squared Errors):
S S E = ∑ i = 1 m ( z i − z ^ i ) 2 SSE = \sum_{i=1}^{m} (z_i - \hat{z}_i)^2 SSE=i=1m(ziz^i)2
对于全部样本的 平均 损失,则可以写作:
M S E = 1 m ∑ i = 1 m ( z i − z ^ i ) 2 MSE = \frac{1}{m} \sum_{i=1}^{m} (z_i - \hat{z}_i)^2 MSE=m1i=1m(ziz^i)2
Pytorch中,我们可以通过以下代码调用MSE

from torch.nn import MSELoss # 类
yhat = torch.randn(size = (50, ), dtype = torch.float32)
y = torch.randn(size = (50, ), dtype = torch.float32)
criterion = MSELoss()	# 实例化
loss = criterion(yhat, y)
# 没有设置随机数种子,所以每次运行的数字都会不一致
loss
# output :
tensor(1.9213)

MSELoss中有重要的参数,reduction

reduction = "mean"(默认也是mean),则输出MSE

reduction = "sum",则输出SSE

criterion = MSELoss(reduction = 'mean')	# 实例化
criterion(yhat, y)
# output :
tensor(2.0026)

criterion = MSELoss(reduction = 'sum')
criterion(yhat, y)
# output :
tensor(128.8871)

39 二分类交叉熵损失函数

在这一节将介绍二分类神经网络的损失函数:二分类交叉熵损失函数(Binary Cross Entropy Loss),也叫做对数损失(log loss)。这个损失函数被广泛地使用在任何输出结果是二分类的神经网络中,不止限于单层神经网络,还可拓展到多分类中。大多数时候,除非特殊声明为二分类,否则提到交叉熵损失,我们会默认算法的分类目标是多分类。

二分类交叉熵损失函数是由极大似然估计推导出来的,对于有 m m m个样本的数据集而言,在全部样本上的平均损失写作:
L ( w ) = − ∑ i = 1 m ( y i ⋅ ln ⁡ ( σ i ) + ( 1 − y i ) ⋅ ln ⁡ ( 1 − σ i ) ) L(w) = -\sum_{i=1}^{m} \left( y_i \cdot \ln(\sigma_i) + (1 - y_i) \cdot \ln(1 - \sigma_i) \right) L(w)=i=1m(yiln(σi)+(1yi)ln(1σi))
在单个样本的损失写作:
L ( w ) i = − ( y i ⋅ ln ⁡ ( σ i ) + ( 1 − y i ) ⋅ ln ⁡ ( 1 − σ i ) ) L(w)_i = -\left( y_i \cdot \ln(\sigma_i) + (1 - y_i) \cdot \ln(1 - \sigma_i) \right) L(w)i=(yiln(σi)+(1yi)ln(1σi))

其中, l n ln ln是以自然底数 e e e为底的对数函数, w w w表示求解出来的一组权重(在等号的右侧, w w w σ \sigma σ里), m m m是样本的个数, y i y_i yi是样本 i i i上真实的标签, σ i \sigma_i σi是样本 i i i上基于参数 w w w计算出来的sigmoid函数的返回值, x i x_i xi是样本 i i i各个特征的取值。我们的目标就是求解出使 L ( w ) L(w) L(w)最小的 w w w取值。

注意,在神经网络中,特征张量 X X X是自变量,权重是 w w w。但在损失函数中,权重 w w w是损失函数的自变量,特征 x x x和真实标签 y y y都是已知的,相当于是常数。


39.1 极大似然估计求解二分类交叉熵损失

二分类交叉熵损失函数是怎么来的呢?为什么这个函数能够代表二分类时,真实值与预测值的差异呢?


极大似然估计(Maximum Likelihood Estimate,MLE)的感性认识

如果一个事件的发生概率很大,那这个事件应该很容易发生。相应的,如果依赖于权重 w w w的任意事件的发生就是我们的目标,那我们只要寻找令其发生概率最大化的权重 w w w就可以了。

寻找相应的权重 w w w,使得目标事件的发生概率最大,就是极大似然估计的基本方法。

其步骤如下:

  1. 构筑似然函数 P ( w ) P(w) P(w),用于评估目标事件发生的概率,该函数被设计成目标事件发生时,概率最大。
  2. 对整体似然函数取对数,构成对数似然函数 l n P ( w ) lnP(w) lnP(w)
  3. 在对数似然函数上对权重 w w w求导,并使导数为 0 0 0,对权重进行求解。

在二分类的例子中,我们的“任意事件”就是每个样本的分类都正确 ,对数似然函数的负数就是我们的损失函数。


在这里,我们基于极大似然估计法来推导交叉熵损失,这个推导过程能够帮助我们充分了解交叉熵损失的含义,以及为什么 L ( w ) L(w) L(w)的最小化能够实现模型在数据集上的拟合最好。

我们来看看逻辑回归的对数似然函数是怎样构筑的。


构筑对数似然函数

二分类神经网络的标签是[0, 1],此标签服从伯努利分布(即0-1分布),因此可得:

样本 i i i在由特征向量 x i x_i xi和权重向量 w w w组成的预测函数中,样本标签被预测为1的概率为:
P 1 = P ( y ^ i = 1 ∣ x i , w ) = σ P_1 = P(\hat{y}_i = 1 | x_i, w) = \sigma P1=P(y^i=1∣xi,w)=σ
对二分类而言, σ \sigma σ就是sigmoid函数的结果。

样本 i i i在由特征向量 x i x_i xi和权重向量 w w w组成的预测函数中,样本标签被预测为0的概率为:
P 0 = P ( y ^ i = 0 ∣ x i , w ) = 1 − σ P_0 = P(\hat{y}_i = 0 | x_i, w) = 1-\sigma P0=P(y^i=0∣xi,w)=1σ
P 1 P_1 P1的值为1的时候,代表样本 i i i的标签被预测为1;当 P 0 P_0 P0的值为1的时候,代表样本 i i i的标签被预测为0。 P 1 P_1 P1 P 0 P_0 P0相加一定是等于1的。

假设样本 i i i的真实标签 y i y_i yi为1,并且 P 1 P_1 P1也为1的话,那就说明我们将 i i i的标签预测为1的概率很大,与真实值一致,那模型的预测就是准确的,拟合程度很高,信息损失很少。相反,如果 P 1 P_1 P1接近0,就说明信息损失很多。当 y i y_i yi为0时也是同样的道理。

所以,当 y i y_i yi为1的时候,我们希望 P 1 P_1 P1非常接近1,当 y i y_i yi为0的时候,我们希望 P 0 P_0 P0非常接近1,这样,模型的效果就很好,信息损失就很少。

真实标签 y i y_i yi被预测为1的概率 P 1 P_1 P1被预测为0的概率 P 0 P_0 P0样本被预测为?与真实值一致吗?拟合状况信息损失
1010
1101
0010
0101

将两种取值的概率整合,我们可以定义如下等式:
P ( y ^ i ∣ x i , w ) = P 1 y i ∗ P 0 1 − y i P(\hat{y}_i | x_i, w) = P_1^{y_i} * P_0^{1-y_i} P(y^ixi,w)=P1yiP01yi
这个等式同时代表了 P 1 P_1 P1 P 0 P_0 P0,在数学上,它被叫做逻辑回归的假设函数。

当样本 i i i的真实标签 y i y_i yi为1的时候, 1 − y i 1-y_i 1yi就等于0, P 0 P_0 P0的0次方就是1,所以 P ( y ^ i ∣ x i , w ) P(\hat{y}_i | x_i, w) P(y^ixi,w)就等于 P 1 P_1 P1,这个时候,如果 P 1 P_1 P1为1,模型的效果就很好,损失就很小。

同理,当样本 i i i的真实标签 y i y_i yi为0的时候, P ( y ^ i ∣ x i , w ) P(\hat{y}_i | x_i, w) P(y^ixi,w)就等于 P 0 P_0 P0,此时如果 P 0 P_0 P0非常接近1,模型的效果就很好,损失就很小。

所以,为了达成让模型拟合好,损失小的目的,我们每时每刻都希望 P ( y ^ i ∣ x i , w ) P(\hat{y}_i | x_i, w) P(y^ixi,w)的值等于1。而 P ( y ^ i ∣ x i , w ) P(\hat{y}_i | x_i, w) P(y^ixi,w)的本质是样本 i i i由特征向量 x i x_i xi和权重 w w w组成的预测函数中,预测出所有可能的 y ^ i \hat{y}_i y^i的概率,因此1是它的最大值。 也就是说,每时每刻,我们都在追求 P ( y ^ i ∣ x i , w ) P(\hat{y}_i | x_i, w) P(y^ixi,w)的最大值。 而寻找相应的参数 w w w,使得每次得到的预测概率最大,正是极大似然估计的基本方法,不过 P ( y ^ i ∣ x i , w ) P(\hat{y}_i | x_i, w) P(y^ixi,w)是对单个样本而言的,因此我们还需要将其拓展到多个样本上。

P ( y ^ i ∣ x i , w ) P(\hat{y}_i | x_i, w) P(y^ixi,w)是对单个样本 i i i而言的函数,对一个训练集 m m m个样本来说,我们可以定义如下等式来表达所有样本在特征张量 X X X和权重向量 w w w组成的预测函数中,预测出所有可能的 y ^ \hat{y} y^的概率 P P P为:
P = ∏ i = 1 m P ( y ^ i ∣ x i , w ) = ∏ i = 1 m ( P 1 y i ∗ P 0 1 − y i ) = ∏ i = 1 m ( σ i y i ∗ ( 1 − σ i ) 1 − y i ) \begin{align*} P &= \prod_{i=1}^m P(\hat{y}_i | x_i, w) \\ &= \prod_{i=1}^m (P_1^{y_i} * P_0^{1-y_i}) \\ &= \prod_{i=1}^m (\sigma_i^{y_i} * (1 - \sigma_i)^{1-y_i}) \end{align*} P=i=1mP(y^ixi,w)=i=1m(P1yiP01yi)=i=1m(σiyi(1σi)1yi)
这个函数就是逻辑回归的似然函数 。对该概率 P P P取以 e e e为底的对数,再由 l o g ( A ∗ B ) = l o g A + l o g B log(A*B)=logA+logB log(AB)=logA+logB l o g A B = B l o g A logA^B=BlogA logAB=BlogA可得到逻辑回归的对数似然函数:
ln ⁡ P = ln ⁡ ∏ i = 1 m ( σ i y i ∗ ( 1 − σ i ) 1 − y i ) = ∑ i = 1 m ln ⁡ ( σ i y i ∗ ( 1 − σ i ) 1 − y i ) = ∑ i = 1 m ( ln ⁡ σ i y i + ln ⁡ ( 1 − σ i ) 1 − y i ) = ∑ i = 1 m ( y i ∗ ln ⁡ ( σ i ) + ( 1 − y i ) ∗ ln ⁡ ( 1 − σ i ) ) \begin{align*} \ln P &= \ln \prod_{i=1}^m (\sigma_i^{y_i} * (1 - \sigma_i)^{1-y_i}) \\ &= \sum_{i=1}^m \ln (\sigma_i^{y_i} * (1 - \sigma_i)^{1-y_i}) \\ &= \sum_{i=1}^m (\ln \sigma_i^{y_i} + \ln (1 - \sigma_i)^{1-y_i}) \\ &= \sum_{i=1}^m (y_i * \ln(\sigma_i) + (1 - y_i) * \ln(1 - \sigma_i)) \end{align*} lnP=lni=1m(σiyi(1σi)1yi)=i=1mln(σiyi(1σi)1yi)=i=1m(lnσiyi+ln(1σi)1yi)=i=1m(yiln(σi)+(1yi)ln(1σi))
这就是我们的二分类交叉熵函数。为了数学上的便利以及更好地定义“损失”的含义,我们希望将极大值问题转换成极小值问题,因此我们对 l n P lnP lnP取负,并且让权重 w w w作为函数的自变量,就得到了我们的损失函数 L ( w ) L(w) L(w)
L ( w ) = − ∑ i = 1 m ( y i ⋅ ln ⁡ ( σ i ) + ( 1 − y i ) ⋅ ln ⁡ ( 1 − σ i ) ) L(w) = -\sum_{i=1}^{m} (y_i \cdot \ln(\sigma_i) + (1 - y_i) \cdot \ln(1 - \sigma_i)) L(w)=i=1m(yiln(σi)+(1yi)ln(1σi))

L ( w ) L(w) L(w)为0时, l n P lnP lnP就等于0, P P P就得到最大概率1。

现在,我们已经将模型拟合中的“最小化损失”问题,转换成了对函数求解极值的问题。这就是一个,基于逻辑回归的返回值 σ \sigma σ的概率性质以及极大似然估计得出的损失函数。在这个函数上,我们只要追求最小值,就能让模型在训练数据上的拟合效果最好,损失最低。


39.2 用tensor实现二分类交叉熵损失

现在,让我们在PyTorch中来实现二分类交叉熵损失函数。首先使用基本的tensor方法来试试看,以加深我们对二分类交叉熵损失的印象:

import torch
import time
N = 3 * pow(10, 3)	# 样本数3000
torch.random.manual_seed(420)	# 设置随机数种子
X = torch.random((N, 4), dtype = torch.float32)
w = torch.rand((4, 1), dtype = torch.float32, requires_grad = True)
y = torch.randint(low = 0, high = 2, size = (N, 1), dtype = torch.float32)

zhat = torch.mm(X, w)
sigma = torch.sigmoid(zhat)

Loss = -(1/N) * torch.sum((1-y) * torch.log(1 - sigma) + y * torch.log(sigma))

注意,在写损失函数这样的复杂函数时,除了普通的加减乘除以外的全部计算,都要使用torch中的函数,因为tensor的运算速度是远远超过普通Python代码,甚至是NumPy的。

N = 3 * pow(10,6)
torch.random.manual_seed(420)
X = torch.rand((N,4),dtype=torch.float32)
w = torch.rand((4,1),dtype=torch.float32,requires_grad=True)
# 左闭右开,只会取0和1
y = torch.randint(low=0,high=2,size=(N,1),dtype=torch.float32)

zhat = torch.mm(X,w)
sigma = torch.sigmoid(zhat)

# 使用torch
start = time.time()
L1 = -(1/N)*torch.sum((1-y)*torch.log(1-sigma)+y*torch.log(sigma))
now = time.time() #seconds
print(now - start)
# output :
0.013980865478515625

# 使用python
start = time.time()
L2 = -(1/N)*sum((1-y)*torch.log(1-sigma)+y*torch.log(sigma))
now = time.time() #seconds
print(now - start)
# output :
16.02258539199829

从运行结果来看,除了加减乘除,我们应该尽量避免使用任何Python原生的计算方法。如果可能的话,让PyTorch处理一切。


39.3 用Pytorch中的类实现二分类交叉熵损失

PyTorch当中,我们有多种方式可以调用二分类交叉熵损失函数。

方法1:nn模块中的类

对于二分类交叉熵损失,nn提供了两个类:BCEWithLogitsLoss以及BCELoss。虽然pytorch官方没有直接明确,但实际上两个函数所需要输入的参数不同。

BCEWithLogitsLoss内置了sigmoid函数与交叉熵函数,它会自动计算输入值的sigmoid值,因此需要输入zhat与真实标签,且顺序不能变化,zhat必须在前。

相对的,BCELoss中只有交叉熵函数,没有sigmoid层,因此需要输入sigmoid与真实标签,且顺序不能变化。

同时,这两个函数都要求预测值与真实标签的 数据类型以及结构(shape)必须相同 ,否则运行就会报错。

接下来,我们来看看这两个类是如何使用的:

import torch.nn as nn

# 调用nn模块下的类
criterion = nn.BCELoss()	# 实例化
loss = criterion(sigma, y)

criterion2 = torch.nn.BCEWithLogitsLoss()	# 实例化
loss = criterion2(zhat, y)

根据pytorch官方的公告,他们更推荐使用BCEWithLogitsLoss这个内置了sigmoid函数的类。内置的sigmoid可以让精度问题被缩小(因为将指数运算包含在了内部),以维持算法运行时的稳定性。所以,当我们的输出层使用sigmoid函数时,我们就可以使用BCEWithLogitsLoss作为损失函数。

MSELoss相同,二分类交叉熵的类也有参数reduction,默认是"mean",表示求解所有样本平均的损失,也可换为"sum",要求输出整体的损失。以及,还可以使用选项"none",表示不对损失结果做任何聚合运算,直接输出每个样本对应的损失矩阵。

criterion2 = torch.nn.BCEWithLogitsLoss(reduction = 'mean')	
loss = criterion2(zhat, y)
loss
# output :
tensor(0.8951)

criterion2 = torch.nn.BCEWithLogitsLoss(reduction = 'sum')	
loss = criterion2(zhat, y)
loss
# output :
tensor(2685.3774)

criterion2 = torch.nn.BCEWithLogitsLoss(reduction = 'none')	
loss = criterion2(zhat, y)
loss.shape
# output :
torch.Size([3000, 1])

第二种方法很少用,我们了解一下即可:

方法2:functional库中的计算函数

function F.binary_cross_entropy_with_logits
function F.binary_cross_entropy

nn中的类们类似,名称中带有Logits的是内置了sigmoid功能的函数,没有带Logits的,是只包含交叉熵损失的函数。

from torch.nn import functional as F

# 直接调用functional库中的计算函数
F.binary_cross_entropy_with_logits(zhat, y)

F.binary_cross_entropy(sigma, y)

在这里两个函数的运行结果是一致的。同样的,pytorch官方推荐的是内置sigmoid功能的函数binary_cross_entropy_with_logits。通常来说,我们都使用类,不使用函数。虽然代码会因此变得稍稍有点复杂,但为了代码的稳定性与日后维护,使用类是更好的选择。


40 多分类交叉熵损失函数

40.1 由二分类推广到多分类

二分类交叉熵损失可以被推广到多分类上,但在实际处理时,二分类与多分类却有一些关键的区别。依然使用极大似然估计的推导流程,首先我们来确定单一样本概率最大化后的似然函数。

对于多分类的状况而言,标签不再服从伯努利分布(0-1分布),因此我们可以定义,样本 i i i在由特征向量 x i x_i xi和权重向量 w w w组成的预测函数中,样本标签被预测为类别 k k k的概率为:
P k = P ( y ^ i = k ∣ x i , w ) = σ P_k = P(\hat{y}_i = k | x_i, w) = \sigma Pk=P(y^i=kxi,w)=σ
对于多分类算法而言, σ \sigma σ就是softmax函数返回的对于类别的值。

假设一种最简单的情况:我们现在有三分类[1, 2, 3],则样本 i i i被预测为三个类别的概率分别为:
P 1 = P ( y ^ i = 1 ∣ x i , w ) = σ 1 P 2 = P ( y ^ i = 2 ∣ x i , w ) = σ 2 P 3 = P ( y ^ i = 3 ∣ x i , w ) = σ 3 \begin{align*} P_1 &= P(\hat{y}_i = 1 | x_i, w) = \sigma_1 \\ P_2 &= P(\hat{y}_i = 2 | x_i, w) = \sigma_2 \\ P_3 &= P(\hat{y}_i = 3 | x_i, w) = \sigma_3 \end{align*} P1P2P3=P(y^i=1∣xi,w)=σ1=P(y^i=2∣xi,w)=σ2=P(y^i=3∣xi,w)=σ3
假设样本的真实标签为1,我们就希望 P 1 P_1 P1最大,同理,如果样本的真实标签为其他值,我们就希望其他值所对应的概率最大。

在二分类中,我们将 y y y ( 1 − y ) (1-y) (1y)作为概率 P P P的指数,以此来融合真实标签0和为1的两种状况。但在多分类中,我们的真实标签可能是任意整数,无法使用 y y y ( 1 − y ) (1-y) (1y)这样的结构来构建似然函数。所以我们认为,如果多分类的标签也可以使用01来表示就好了,这样我们就可以继续使用真实标签作为指数的方式。

因此,我们对多分类的标签做出了如下变化:

在这里插入图片描述

原本的真实标签y是含有[1, 2, 3]三个分类的列向量,现在我们把它变成了标签矩阵,每个样本对应一个向量(这正是机器学习中的独热编码one-hot)。在矩阵中,每一行依旧对应样本,但却由三分类衍生出了三个新的列,分别代表:真实标签是否等于1、等于2以及等于3

在矩阵中,我们使用“1”标注出样本的真实标签的位置,使用0表示样本的真实标签不是这个标签。不难注意到,这个标签矩阵的结构其实是和softmax函数输出的概率矩阵的结构一致,并且一一对应的。

回顾二分类的似然函数:
P ( y ^ i ∣ x i , w ) = P 1 y i ∗ P 0 1 − y i P(\hat{y}_i | x_i, w) = P_1^{y_i} * P_0^{1-y_i} P(y^ixi,w)=P1yiP01yi
当我们把标签整合为标签矩阵后,我们就可以将单个样本在总共 k k k个分类情况整合为以下的似然函数:
P ( y ^ i ∣ x i , w ) = P 1 y i ( k = 1 ) ∗ P 2 y i ( k = 2 ) ∗ P 3 y i ( k = 3 ) ∗ … ∗ P K y i ( k = K ) P(\hat{y}_i | x_i, w) = P_1^{y_i(k=1)} * P_2^{y_i(k=2)} * P_3^{y_i(k=3)} * \ldots * P_K^{y_i(k=K)} P(y^ixi,w)=P1yi(k=1)P2yi(k=2)P3yi(k=3)PKyi(k=K)
其中 P P P就是样本标签被预测为某个具体值的概率,而右上角的指数就是标签矩阵中对应的值,即这个样本的真实标签是否为当前标签的判断(是就是1,否就是0)。

也可将式子简写为:
P ( y ^ i ∣ x i , w ) = P j y i ( k = j ) , j 为样本 i 所对应的真实标签的编号 P(\hat{y}_i | x_i, w) = P_j^{y_{i(k=j)}}, \quad j \text{为样本} i \text{所对应的真实标签的编号} P(y^ixi,w)=Pjyi(k=j),j为样本i所对应的真实标签的编号
对一个训练集的 m m m个样本来说,我们可以定义如下等式来表达所有样本在特征张量 X X X和权重向量 w w w组成的预测函数中,预测出所有可能的 y ^ \hat{y} y^的概率 P P P为:
P = ∏ i = 1 m P ( y ^ i ∣ x i , w ) = ∏ i = 1 m P j y i ( k = j ) = ∏ i = 1 m σ j y i ( k = j ) \begin{align*} P &= \prod_{i=1}^m P(\hat{y}_i | x_i, w) \\ &= \prod_{i=1}^m P_j^{y_{i(k=j)}} \\ &= \prod_{i=1}^m \sigma_j^{y_{i(k=j)}} \end{align*} P=i=1mP(y^ixi,w)=i=1mPjyi(k=j)=i=1mσjyi(k=j)
这是多分类状况下的似然函数。与二分类一致,似然函数解出来后,我们需要对似然函数求对数:
ln ⁡ P = ln ⁡ ∏ i = 1 m σ j y i ( k = j ) = ∑ i = 1 m ln ⁡ ( σ j y i ( k = j ) ) = ∑ i = 1 m y i ( k = j ) ln ⁡ σ i \begin{align*} \ln P &= \ln \prod_{i=1}^m \sigma_j^{y_{i(k=j)}} \\ &= \sum_{i=1}^m \ln(\sigma_j^{y_{i(k=j)}}) \\ &= \sum_{i=1}^m y_{i(k=j)} \ln \sigma_i \end{align*} lnP=lni=1mσjyi(k=j)=i=1mln(σjyi(k=j))=i=1myi(k=j)lnσi
其中 σ \sigma σ就是softmax函数返回的对应类别的值。在对整个公式取负,就得到了多分类状况下的损失函数:
L ( w ) = − ∑ i = 1 m y i ( k = j ) ln ⁡ σ i L(w) = -\sum_{i=1}^{m} y_{i(k=j)} \ln \sigma_i L(w)=i=1myi(k=j)lnσi
这个函数就是我们之前提到过的交叉熵函数 。不难看出,二分类的交叉熵函数其实是多分类的一种特殊情况。

在这里插入图片描述

交叉熵函数十分特殊,虽然我们求解过程中,取对数的操作是在确定了似然函数后才进行的,但从计算结果看,对数操作其实只对softmax函数的结果 σ \sigma σ起效。因此在实际操作中,我们把 l n ( s o f t m a x ( z ) ) ln(softmax(z)) ln(softmax(z))这样的函数单独定义了一个功能叫做logsoftmaxpytorch可以直接通过nn.logsoftmax类直接调用这个功能。

同时,我们把对数之外的。乘以标签、加和、取负等等过程打包起来,称之为负对数似然函数(Negative Log Likelihood function),在pytorch中可以使用nn.NLLLoss来调用。也就是说,在计算损失函数时,我们不再需要使用单独的softmax函数了。


40.2 用pytorch实现多分类交叉熵损失

PyTorch中实现交叉熵函数的时候,有两种办法:

调用logsoftmax和NLLLoss实现

import torch
import torch.nn as nn

N = 3 * pow(10, 2)
torch.random.manual_seed(420)
X = torch.rand((N, 4), dtype = torch.float32)
w = torch.rand((4, 3), dtype = torch.float32, requires_grad = True)
y = torch.randint(low=0,high=3,size=(m,),dtype=torch.float32)

zhat = torch.mm(X,w)
# 从这里开始调用softmax和NLLLoss
# dim = 1表示要对矩阵的每一行标签都进行softmax计算
logsm = nn.LogSoftmax(dim=1) # 实例化
logsigma = logsm(zhat)

criterion = nn.NLLLoss() #实例化
# 由于交叉熵损失需要将标签转化为独热形式,因此不接受浮点数作为标签的输入
# 对NLLLoss而言,需要输入logsigma
criterion(logsigma,y.long())

更加简便的方法是:

直接调用CrossEntropyLoss

criterion = nn.CrossEntropyLoss()
# 对打包好的CorssEnrtopyLoss而言,只需要输入zhat
criterion(zhat,y.long())

两种输出方法得到的损失函数结果是一致的。与其他损失函数一致,CrossEntropyLoss也有参数reduction,可以设置为meansum以及None

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2286704.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++ 中用于控制输出格式的操纵符——setw 、setfill、setprecision、fixed

目录 四种操纵符简要介绍 setprecision基本用法 setfill的基本用法 fixed的基本用法 setw基本用法 以下是一些常见的用法和示例: 1. 设置字段宽度和填充字符 2. 设置字段宽度和对齐方式 3. 设置字段宽度和精度 4. 设置字段宽度和填充字符,结合…

996引擎 - NPC-添加NPC引擎自带形象

996引擎 - NPC-添加NPC引擎自带形象 截图参考添加NPC参考资料截图参考 添加NPC 编辑NPC表:Envir\DATA\cfg_npclist.xls 1.1. 需要临时隐藏NPC时可以在id前加 // 1.2. 如果NPC朝向不对,可以调整dir 列。(按8方向,上是0顺时针数。我这里给的4) 1.3. 形象代码:NPC代码、怪物…

深度研究新范式:通过Ollama和DeepSeek R1实现自动化研究

引言 在信息时代,海量数据的产生与传播速度前所未有地加快,这既为研究者提供了丰富的资源,也带来了信息筛选与处理的巨大挑战。 传统研究方法往往依赖于研究者的个人知识库、文献检索技能以及时间投入,但面对指数级增长的数据量…

Golang 并发机制-1:Golang并发特性概述

并发是现代软件开发中的一个基本概念,它使程序能够同时执行多个任务,从而提高效率和响应能力。在本文中,我们将探讨并发性在现代软件开发中的重要性,并深入研究Go处理并发任务的独特方法。 并发的重要性 增强性能 并发在提高软…

(一)QT的简介与环境配置WIN11

目录 一、QT的概述 二、QT的下载 三、简单编程 常用快捷键 一、QT的概述 简介 Qt(发音:[kjuːt],类似“cute”)是一个跨平台的开发库,主要用于开发图形用户界面(GUI)应用程序,…

OpenEuler学习笔记(十四):在OpenEuler上搭建.NET运行环境

一、在OpenEuler上搭建.NET运行环境 基于包管理器安装 添加Microsoft软件源:运行命令sudo rpm -Uvh https://packages.microsoft.com/config/centos/8/packages-microsoft-prod.rpm,将Microsoft软件源添加到系统中,以便后续能够从该源安装.…

高级编码参数

1.跳帧机制 参考资料:frameskipping-hotedgevideo 跳帧机制用于优化视频质量和编码效率。它通过选择性地跳过某些帧并使用参考帧来预测和重建视频内容,从而减少编码所需的比特率,同时保持较高的视频质量。在视频编码过程中,如果…

gradio 合集

知识点 1:基本 Chatbot 创建 import gradio as gr 定义历史记录 history [gr.ChatMessage(role“assistant”, content“How can I help you?”), gr.ChatMessage(role“user”, content“What is the weather today?”)] 使用历史记录创建 Chatbot 组件 ch…

Python NumPy(5):广播、迭代

1 NumPy 广播(Broadcast) 广播(Broadcast)是 numpy 对不同形状(shape)的数组进行数值计算的方式, 对数组的算术运算通常在相应的元素上进行。如果两个数组 a 和 b 形状相同,即满足 a.shape b.shape,那么 a*b 的结果就是 a 与 b 数组对应位相…

基于 AWS SageMaker 对 DeepSeek-R1-Distilled-Llama-8B 模型的精调与实践

在当今人工智能蓬勃发展的时代,语言模型的性能优化和定制化成为研究与应用的关键方向。本文聚焦于 AWS SageMaker 平台上对 DeepSeek-R1-Distilled-Llama-8B 模型的精调实践,详细探讨这一过程中的技术细节、操作步骤以及实践价值。 一、实验背景与目标 …

【Rust自学】15.1. 使用Box<T>智能指针来指向堆内存上的数据

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 15.1.1. Box<T> box<T>可以被简单地理解为装箱&#xff0c;它是最简单的智能指针&#xff0c;允许你在堆内存上存储数据&am…

Hive:复杂数据类型之Map函数

Map函数 是Hive里面的一种复杂数据类型, 用于存储键值对集合。Map中的键和值可以是基础类型或复合类型&#xff0c;这使得Map在处理需要关联存储信息的数据时非常有用。 定义map时,需声明2个属性: key 和 value , map中是 key value 组成一个元素 key-value, key必须为原始类…

Matlab实现TCN-BiLSTM时间卷积神经网络结合双向长短期记忆神经网络多特征分类预测(附模型研究报告)

Matlab实现TCN-BiLSTM时间卷积神经网络结合双向长短期记忆神经网络多特征分类预测&#xff08;附模型研究报告&#xff09; 目录 Matlab实现TCN-BiLSTM时间卷积神经网络结合双向长短期记忆神经网络多特征分类预测&#xff08;附模型研究报告&#xff09;分类效果基本描述程序设…

Midjourney中的强变化、弱变化、局部重绘的本质区别以及其有多逆天的功能

开篇 Midjourney中有3个图片“微调”&#xff0c;它们分别为&#xff1a; 强变化&#xff1b;弱变化&#xff1b;局部重绘&#xff1b; 在Discord里分别都是用命令唤出的&#xff0c;但如今随着AI技术的发达在类似AI可人一类的纯图形化界面中&#xff0c;我们发觉这样的逆天…

NLP深度学习 DAY4:Word2Vec详解:两种模式(CBOW与Skip-gram)

用稀疏向量表示文本&#xff0c;即所谓的词袋模型在 NLP 有着悠久的历史。正如上文中介绍的&#xff0c;早在 2001年就开始使用密集向量表示词或词嵌入。Mikolov等人在2013年提出的创新技术是通过去除隐藏层&#xff0c;逼近目标&#xff0c;进而使这些单词嵌入的训练更加高效。…

【Linux】 冯诺依曼体系与计算机系统架构全解

Linux相关知识点可以通过点击以下链接进行学习一起加油&#xff01;初识指令指令进阶权限管理yum包管理与vim编辑器GCC/G编译器make与Makefile自动化构建GDB调试器与Git版本控制工具Linux下进度条 冯诺依曼体系是现代计算机设计的基石&#xff0c;其统一存储和顺序执行理念推动…

【最后203篇系列】005 -QTV200 Online

说明 借着春节休假&#xff0c;把这部分完工&#xff0c;然后2025年将正式的把量化研究的成果进行产品化输出。 首先&#xff0c;我会将策略的执行从脚本挪到服务。做法是将策略的逻辑放在微服务里&#xff0c;作为一个接口&#xff0c;而由sniffer来触发策略执行。我想这样策…

Midjourney基础-常用修饰词+权重的用法大全

用好修饰词很关键 Midjourney要用除了掌握好提示词的写法&#xff0c;按照上一篇《做Midjourney最好图文教程-提示词公式以及高级参数讲解》画面主体 场景氛围 主体行为 构图方式 艺术风格 图像质量。 要画出有质感的内容我们必须要掌握好“修饰词”&#xff0c;这些修饰…

Deepseek的RL算法GRPO解读

在本文中&#xff0c;我们将深入探讨Deepseek采用的策略优化方法GRPO&#xff0c;并顺带介绍一些强化学习&#xff08;Reinforcement Learning, RL&#xff09;的基础知识&#xff0c;包括PPO等关键概念。 策略函数&#xff08;policy&#xff09; 在强化学习中&#xff0c; a…

神经网络和深度学习

应用 类型 为什么近几年飞速发展 数据增长&#xff0c;算力增长&#xff0c;算法革新 逻辑回归 向量化 浅层神经网络(Shallow neural network) 单条训练数据前向传播计算表达式 batch训练数据前向传播计算表达式 反向传播计算表达式 参数随机初始化 不能全部设为0 原因是同一…