深度学习之全连接神经网络(三)

部署运行你感兴趣的模型镜像

目录

四、参数初始化

1. 固定值初始化

1.1 全零初始化

1.2 全1初始化

1.3 任意常数初始化

2. 随机初始化

3. Xavier 初始化

4. He初始化

5. 总结

五、损失函数

1. 线性回归损失函数

1.1 MAE损失

1.2 MSE损失

2. CrossEntropyLoss

2.1 信息量

2.2 信息熵

2.3 KL散度

2.4 交叉熵

3. BCELoss

4. 总结


四、参数初始化

神经网络的参数初始化是训练深度学习模型的关键步骤之一。初始化参数(通常是权重和偏置)会对模型的训练速度、收敛性以及最终的性能产生重要影响。下面是关于神经网络参数初始化的一些常见方法及其相关知识点。

官方文档参考:torch.nn.init — PyTorch 2.8 documentation

1. 固定值初始化

固定值初始化是指在神经网络训练开始时,将所有权重或偏置初始化为一个特定的常数值。这种初始化方法虽然简单,但在实际深度学习应用中通常并不推荐。

1.1 全零初始化

将神经网络中的所有权重参数初始化为0。

方法:将所有权重初始化为零。

缺点:导致对称性破坏,每个神经元在每一层中都会执行相同的计算,模型无法学习。

应用场景:通常不用来初始化权重,但可以用来初始化偏置。

对称性问题

  • 现象:同一层的所有神经元具有完全相同的初始权重和偏置。

  • 后果:

    • 在反向传播时,所有神经元会收到相同的梯度,导致权重更新完全一致。

    • 无论训练多久,同一层的神经元本质上会保持相同的功能(相当于“一个神经元”的多个副本),极大降低模型的表达能力。

代码演示:

 import torch
 import torch.nn as nn
 ​
 def test004():
     # 3. 全0参数初始化
     linear = nn.Linear(in_features=6, out_features=4)
     # 初始化权重参数
     nn.init.zeros_(linear.weight)
     # 打印权重参数
     print(linear.weight)
 ​
 ​
 if __name__ == "__main__":
     test004()
 ​

打印结果:

 Parameter containing:
 tensor([[0., 0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0., 0.],
         [0., 0., 0., 0., 0., 0.]], requires_grad=True)

1.2 全1初始化

全1初始化会导致网络中每个神经元接收到相同的输入信号,进而输出相同的值,这就无法进行学习和收敛。所以全1初始化只是一个理论上的初始化方法,但在实际神经网络的训练中并不适用。

代码演示:

 import torch
 import torch.nn as nn
 ​
 ​
 def test003():
     # 3. 全1参数初始化
     linear = nn.Linear(in_features=6, out_features=4)
     # 初始化权重参数
     nn.init.ones_(linear.weight)
     # 打印权重参数
     print(linear.weight)
 ​
 ​
 if __name__ == "__main__":
     test003()
 ​

输出结果:

 Parameter containing:
 tensor([[1., 1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1., 1.]], requires_grad=True)

1.3 任意常数初始化

将所有参数初始化为某个非零的常数(如 0.1,-1 等)。虽然不同于全0和全1,但这种方法依然不能避免对称性破坏的问题。

 import torch
 import torch.nn as nn
 ​
 ​
 def test002():
     # 2. 固定值参数初始化
     linear = nn.Linear(in_features=6, out_features=4)
     # 初始化权重参数
     nn.init.constant_(linear.weight, 0.63)
     # 打印权重参数
     print(linear.weight)
     pass
 ​
 ​
 if __name__ == "__main__":
     test002()
 ​

输出结果:

 Parameter containing:
 tensor([[0.6300, 0.6300, 0.6300, 0.6300, 0.6300, 0.6300],
         [0.6300, 0.6300, 0.6300, 0.6300, 0.6300, 0.6300],
         [0.6300, 0.6300, 0.6300, 0.6300, 0.6300, 0.6300],
         [0.6300, 0.6300, 0.6300, 0.6300, 0.6300, 0.6300]], requires_grad=True)

参考2:

 import torch
 import torch.nn as nn
 ​
 ​
 def test002():
     net = nn.Linear(2, 2, bias=True)
     # 假设一个数值
     x = torch.tensor([[0.1, 0.95]])
 ​
     # 初始化权重参数
     net.weight.data = torch.tensor([
         [0.1, 0.2], 
         [0.3, 0.4]
     ])
 ​
     # 输出什么:权重参数会转置
     output = net(x)
     print(output, net.bias)
     pass
 ​
 ​
 if __name__ == "__main__":
     test002()
 ​
  • 所有输入特征被同等对待

  • 无法学习特征间的不同重要性

2. 随机初始化

方法:将权重初始化为随机的小值,通常从正态分布或均匀分布中采样。

应用场景:这是最基本的初始化方法,通过随机初始化避免对称性破坏。

代码演示:随机分布之均匀初始化

 import torch
 import torch.nn as nn
 ​
 ​
 def test001():
     # 1. 均匀分布随机初始化
     linear = nn.Linear(in_features=6, out_features=4)
     # 初始化权重参数
     nn.init.uniform_(linear.weight)
     # 打印权重参数
     print(linear.weight)
 ​
 ​
 if __name__ == "__main__":
     test001()
 ​

打印结果:

 Parameter containing:
 tensor([[0.4080, 0.7444, 0.7616, 0.0565, 0.2589, 0.0562],
         [0.1485, 0.9544, 0.3323, 0.9802, 0.1847, 0.6254],
         [0.6256, 0.2047, 0.5049, 0.3547, 0.9279, 0.8045],
         [0.1994, 0.7670, 0.8306, 0.1364, 0.4395, 0.0412]], requires_grad=True)

代码演示:正态分布初始化

 import torch
 import torch.nn as nn
 ​
 ​
 def test005():
     # 5. 正太分布初始化
     linear = nn.Linear(in_features=6, out_features=4)
     # 初始化权重参数
     nn.init.normal_(linear.weight, mean=0, std=1)
     # 打印权重参数
     print(linear.weight)
 ​
 ​
 if __name__ == "__main__":
     test005()
 ​

打印结果:

 Parameter containing:
 tensor([[ 1.5321,  0.2394,  0.0622,  0.4482,  0.0757, -0.6056],
         [ 1.0632,  1.8069,  1.1189,  0.2448,  0.8095, -0.3486],
         [-0.8975,  1.8253, -0.9931,  0.7488,  0.2736, -1.3892],
         [-0.3752,  0.0500, -0.1723, -0.4370, -1.5334, -0.5393]],
        requires_grad=True)

3. Xavier 初始化

前置知识

均匀分布:

均匀分布的概率密度函数(PDF):

$$
p(x)=\begin{cases}\frac{1}{b−a} & 如果 a≤x≤b\\ 0 & 其他情况 \end{cases}
$$

计算期望值(均值):

$$
E[X]=∫_a^bx⋅p(x) dx=∫_a^bx⋅\frac{1}{b−a} dx=\frac{a+b}{2}
$$

计算方差(二阶矩减去均值的平方):

$$
Var(X)=E(X-E(X))^2=E[X^2]−(E[X])^2
$$

  • 先计算 E[X^2]

    $$
    E[X^2]=∫_a^bx^2⋅\frac{1}{b−a} dx=\frac{b^3−a^3}{3(b−a)}=\frac{a^2+ab+b^2}{3}
    $$

  • 代入方差公式:

    $$
    Var(X)=\frac{a^2+ab+b^2}{3}−(\frac{a+b}{2})^2=\frac{(b−a)^2}{12}
    $$

Xavier 初始化(由 Xavier Glorot 在 2010 年提出)是一种自适应权重初始化方法,专门为解决神经网络训练初期的梯度消失或爆炸问题而设计。Xavier 初始化也叫做Glorot初始化。Xavier 初始化的核心思想是根据输入和输出的维度来初始化权重,使得每一层的输出的方差保持一致。具体来说,权重的初始化范围取决于前一层的神经元数量(输入维度)和当前层的神经元数量(输出维度)。

方法:根据输入和输出神经元的数量来选择权重的初始值。

数学原理:

(1) 前向传播的方差一致性

假设输入 x 的均值为 0,方差为 \sigma _x^2,权重 W的均值为 0,方差为 \sigma _W^2,则输出 z=Wx的方差为:

$$
Var(z)=n_{in}⋅Var(W)⋅Var(x)
$$

为了使 Var(z)=Var(x),需要:

$$
n_{in}⋅Var(W)=1  ⟹  Var(W)=\frac{1}{n_{in}}
$$

其中 n_{in}是输入维度(fan_in)。这里乘以 nin 的原因是,输出 z 是由 nin 个输入 x 的线性组合得到的,每个输入 x 都与一个权重 W 相乘。因此,输出 z 的方差是 nin 个独立的 Wx 项的方差之和。

(2) 反向传播的梯度方差一致性

在反向传播过程中,梯度 \frac{\partial L}{\partial x}是通过链式法则计算得到的,其中 L 是损失函数,x 是输入,z 是输出。梯度\frac{\partial L}{\partial x}可以表示为:

$$
\frac{∂L}{∂x}=\frac{∂L}{∂z}.\frac{∂z}{∂x}
$$

假设 z=Wx,其中 W 是权重矩阵,那么 \frac{\partial z}{\partial x}=W。因此,梯度 \frac{\partial L}{\partial x}可以写为: \frac{\partial L}{\partial x}=\frac{\partial L}{\partial z}W

反向传播时梯度 \frac{\partial L}{\partial x} 的方差应与 \frac{\partial L}{\partial z} 相同,因此:

$$
n_{out}⋅Var(W)=1  ⟹  Var(W)=\frac{1}{n_{out}}
$$

其中 n_{out}是输出维度(fan_out)。为了保持梯度的方差一致性,我们需要确保每个输入维度 nin 的梯度方差与输出维度 nout 的梯度方差相同。因此,我们需要将 W 的方差乘以 nout,以确保梯度的方差在反向传播过程中保持一致。

(3) 综合考虑

为了同时平衡前向传播和反向传播,Xavier 采用:

$$
Var(W)=\frac{2}{n_{in}+n_{out}}
$$

权重从以下分布中采样:

均匀分布

$$
W\sim\mathrm{U}\left(-\frac{\sqrt{6}}{\sqrt{n_\mathrm{in}+n_\mathrm{out}}},\frac{\sqrt{6}}{\sqrt{n_\mathrm{in}+n_\mathrm{out}}}\right)
$$

在Xavier初始化中,我们选择 a = - \sqrt{\frac{6}{n_{in}+n_{out}}}b=\sqrt{\frac{6}{n_{in}+n_{out}}},这样方差为:

$$
Var(W)=\frac{(b−a)^2}{12}=\frac{(2\sqrt{\frac{6}{n_{in}+n_{out}}})^2}{12}=\frac{4⋅\frac{6}{nin+nout}}{12}=\frac{2}{n_{in}+n_{out}}
$$

正态分布

$$
W\sim\mathrm{N}\left(0,\frac{2}{n_\mathrm{in}+n_\mathrm{out}}\right)
$$

$$
\mathcal{N}(0, \text{std}^2)
$$

其中n_{\text{in}} 是当前层的输入神经元数量,n_{\text{out}}是输出神经元数量。

在前向传播中,输出的方差受 n_{in} 影响。在反向传播中,梯度的方差受 n_{out} 影响。

优点:平衡了输入和输出的方差,适合Sigmoid 和 Tanh 激活函数。

应用场景:常用于浅层网络或使用Sigmoid 、Tanh 激活函数的网络。

代码演示:

 import torch
 import torch.nn as nn
 ​
 ​
 def test007():
     # Xavier初始化:正态分布
     linear = nn.Linear(in_features=6, out_features=4)
     nn.init.xavier_normal_(linear.weight)
     print(linear.weight)
 ​
     # Xavier初始化:均匀分布
     linear = nn.Linear(in_features=6, out_features=4)
     nn.init.xavier_uniform_(linear.weight)
     print(linear.weight)
 ​
 ​
 if __name__ == "__main__":
     test007()
 ​

打印结果:

 Parameter containing:
 tensor([[-0.4838,  0.4121, -0.3171, -0.2214, -0.8666, -0.4340],
         [ 0.1059,  0.6740, -0.1025, -0.1006,  0.5757, -0.1117],
         [ 0.7467, -0.0554, -0.5593, -0.1513, -0.5867, -0.1564],
         [-0.1058,  0.5266,  0.0243, -0.5646, -0.4982, -0.1844]],
        requires_grad=True)
 Parameter containing:
 tensor([[-0.5263,  0.3455,  0.6449,  0.2807, -0.3698, -0.6890],
         [ 0.1578, -0.3161, -0.1910, -0.4318, -0.5760,  0.3746],
         [ 0.2017, -0.6320, -0.4060,  0.3903,  0.3103, -0.5881],
         [ 0.6212,  0.3077,  0.0783, -0.6187,  0.3109, -0.6060]],
        requires_grad=True)

4. He初始化

也叫kaiming 初始化。He 初始化的核心思想是调整权重的初始化范围,使得每一层的输出的方差保持一致。与 Xavier 初始化不同,He 初始化专门针对 ReLU 激活函数的特性进行了优化。

数学推导

(1) 前向传播的方差一致性

对于 ReLU 激活函数,输出的方差为:

$$
Var(z)=\frac{1}{2}n_{in}⋅Var(W)⋅Var(x)
$$

(因为 ReLU 使一半神经元输出为 0,方差减半) 为使 Var(z)=Var(x),需:

$$
\frac{1}{2}n_{in}⋅Var(W)=1  ⟹  Var(W)=\frac{2}{n_{in}}
$$

(2) 反向传播的梯度一致性

类似地,反向传播时梯度方差需满足:

$$
Var(\frac{∂L}{∂x})=\frac{1}{2}n_{out}⋅Var(W)⋅Var(\frac{∂L}{∂z})
$$

因此:

$$
Var(W)=\frac{2}{n_{out}}
$$

(3) 两种模式

  • fan_in 模式(默认):优先保证前向传播稳定,方差\frac{2}{n_{in}}

  • fan_out 模式:优先保证反向传播稳定,方差\frac{2}{n_{out}}

方法:专门为 ReLU 激活函数设计。权重从以下分布中采样:

均匀分布:

$$
W\sim\mathrm{U}\left(-\frac{\sqrt{6}}{\sqrt{n_\mathrm{in}}},\frac{\sqrt{6}}{\sqrt{n_\mathrm{in}}}\right)
$$

正态分布:

$$
W\sim\mathrm{N}\left(0,\frac{2}{n_\mathrm{in}}\right)
$$

其中 n_{\text{in}} 是当前层的输入神经元数量。

优点:适用于ReLU 和 Leaky ReLU 激活函数。

应用场景:深度网络,尤其是使用 ReLU 激活函数时。

代码演示:

 import torch
 import torch.nn as nn
 ​
 ​
 def test006():
     # He初始化:正态分布
     linear = nn.Linear(in_features=6, out_features=4)
     nn.init.kaiming_normal_(linear.weight, nonlinearity="relu", mode='fan_in')
     print(linear.weight)
 ​
     # He初始化:均匀分布
     linear = nn.Linear(in_features=6, out_features=4)
     nn.init.kaiming_uniform_(linear.weight, nonlinearity="relu", mode='fan_out')
     print(linear.weight)
 ​
 ​
 if __name__ == "__main__":
     test006()
 ​

输出结果:

 Parameter containing:
 tensor([[ 1.4020,  0.2030,  0.3585, -0.7419,  0.6077,  0.0178],
         [-0.2860, -1.2135,  0.0773, -0.3750, -0.5725,  0.9756],
         [ 0.2938, -0.6159, -1.1721,  0.2093,  0.4212,  0.9079],
         [ 0.2050,  0.3866, -0.3129, -0.3009, -0.6659, -0.2261]],
        requires_grad=True)
 ​
 Parameter containing:
 tensor([[-0.1924, -0.6155, -0.7438, -0.2796, -0.1671, -0.2979],
         [ 0.7609,  0.9836, -0.0961,  0.7139, -0.8044, -0.3827],
         [ 0.1416,  0.6636,  0.9539,  0.4735, -0.2384, -0.1330],
         [ 0.7254, -0.4056, -0.7621, -0.6139, -0.6093, -0.2577]],
        requires_grad=True)

5. 总结

在使用Torch构建网络模型时,每个网络层的参数都有默认的初始化方法,同时还可以通过以上方法来对网络参数进行初始化。

五、损失函数

1. 线性回归损失函数

1.1 MAE损失

MAE(Mean Absolute Error,平均绝对误差)通常也被称为 L1-Loss,通过对预测值和真实值之间的绝对差取平均值来衡量他们之间的差异。

MAE的公式如下:

$$
\text{MAE} = \frac{1}{n} \sum_{i=1}^{n} \left| y_i - \hat{y}_i \right|
$$

其中:

  • n 是样本的总数。

  • y_i 是第 i 个样本的真实值。

  • \hat{y}_i 是第 i 个样本的预测值。

  • \left| y_i - \hat{y}_i \right|是真实值和预测值之间的绝对误差。

特点

  1. 鲁棒性:与均方误差(MSE)相比,MAE对异常值(outliers)更为鲁棒,因为它不会像MSE那样对较大误差平方敏感。

  2. 物理意义直观:MAE以与原始数据相同的单位度量误差,使其易于解释。

应用场景: MAE通常用于需要对误差进行线性度量的情况,尤其是当数据中可能存在异常值时,MAE可以避免对异常值的过度惩罚。

使用torch.nn.L1Loss即可计算MAE:

 import torch
 import torch.nn as nn
 ​
 # 初始化MAE损失函数
 mae_loss = nn.L1Loss()
 ​
 # 假设 y_true 是真实值, y_pred 是预测值
 y_true = torch.tensor([3.0, 5.0, 2.5])
 y_pred = torch.tensor([2.5, 5.0, 3.0])
 ​
 # 计算MAE
 loss = mae_loss(y_pred, y_true)
 print(f'MAE Loss: {loss.item()}')

1.2 MSE损失

均方差损失,也叫L2Loss。

MSE(Mean Squared Error,均方误差)通过对预测值和真实值之间的误差平方取平均值,来衡量预测值与真实值之间的差异。

MSE的公式如下:

$$
\text{MSE} = \frac{1}{n} \sum_{i=1}^{n} \left( y_i - \hat{y}_i \right)^2
$$

其中:

  • n 是样本的总数。

  • y_i是第 i 个样本的真实值。

  • \hat{y}_i 是第 i 个样本的预测值。

  • \left( y_i - \hat{y}_i \right)^2 是真实值和预测值之间的误差平方。

特点

  1. 平方惩罚:因为误差平方,MSE 对较大误差施加更大惩罚,所以 MSE 对异常值更为敏感。

  2. 凸性:MSE 是一个凸函数(国际的叫法,国内叫凹函数),这意味着它具有一个唯一的全局最小值,有助于优化问题的求解。

应用场景

MSE被广泛应用在神经网络中。

使用 torch.nn.MSELoss 可以实现:

 import torch
 import torch.nn as nn
 ​
 # 初始化MSE损失函数
 mse_loss = nn.MSELoss()
 ​
 # 假设 y_true 是真实值, y_pred 是预测值
 y_true = torch.tensor([3.0, 5.0, 2.5])
 y_pred = torch.tensor([2.5, 5.0, 3.0])
 ​
 # 计算MSE
 loss = mse_loss(y_pred, y_true)
 print(f'MSE Loss: {loss.item()}')

2. CrossEntropyLoss

2.1 信息量

信息量用于衡量一个事件所包含的信息的多少。信息量的定义基于事件发生的概率:事件发生的概率越低,其信息量越大。其量化公式:

对于一个事件x,其发生的概率为 P(x),信息量I(x) 定义为:

$$
I(x)=−logP(x)
$$

性质

  1. 非负性:I(x)≥0。

  2. 单调性:P(x)越小,I(x)越大。

2.2 信息熵

信息熵是信息量的期望值。熵越高,表示随机变量的不确定性越大;熵越低,表示随机变量的不确定性越小。

公式由数学中的期望推导而来:

$$
H(X)=−∑_{i=1}^n P(x_i)logP(x_i)
$$

其中:

-logP(x_i)是信息量,P(x_i)是信息量对应的概率

2.3 KL散度

KL散度用于衡量两个概率分布之间的差异。它描述的是用一个分布 Q来近似另一个分布 P时,所损失的信息量。KL散度越小,表示两个分布越接近。

对于两个离散概率分布 P和 Q,KL散度定义为:

$$
D_{KL}(P||Q)=∑_iP(x_i)log\frac{⁡P(x_i)}{Q(x_i)}
$$

其中:P 是真实分布,Q是近似分布。

2.4 交叉熵

对KL散度公式展开:

$$
D_{KL}(P||Q)=∑_iP(x_i)log\frac{⁡P(x_i)}{Q(x_i)}=∑_iP(x_i)[log{⁡P(x_i)}-log{Q(x_i)}]\\ =∑_iP(x_i)log{⁡P(x_i)}-∑_iP(x_i)log{Q(x_i)}=-(-∑_iP(x_i)log{⁡P(x_i)})+(-∑_iP(x_i)log{Q(x_i)})\\ =-H(P)+(-∑_iP(x_i)log{Q(x_i)})\\ =H(P,Q)-H(P)
$$

由上述公式可知,P是真实分布,H(P)是常数,所以KL散度可以用H(P,Q)来表示;H(P,Q)叫做交叉熵。

如果将P换成y,Q换成\hat{y},则交叉熵公式为:

$$
\text{CrossEntropyLoss}(y, \hat{y}) = - \sum_{i=1}^{C} y_i \log(\hat{y}_i)
$$

其中:

  • C 是类别的总数。

  • y 是真实标签的one-hot编码向量,表示真实类别。

  • \hat{y} 是模型的输出(经过 softmax 后的概率分布)。

  • y_i是真实类别的第 i 个元素(0 或 1)。

  • \hat{y}_i 是预测的类别概率分布中对应类别 i 的概率。

函数曲线图:

特点:

  1. 概率输出:CrossEntropyLoss 通常与 softmax 函数一起使用,使得模型的输出表示为一个概率分布(即所有类别的概率和为 1)。PyTorch 的 nn.CrossEntropyLoss 已经内置了 Softmax 操作。如果我们在输出层显式地添加 Softmax,会导致重复应用 Softmax,从而影响模型的训练效果。

  2. 惩罚错误分类:该损失函数在真实类别的预测概率较低时,会施加较大的惩罚,这样模型在训练时更注重提升正确类别的预测概率。

  3. 多分类问题中的标准选择:在大多数多分类问题中,CrossEntropyLoss 是首选的损失函数。

应用场景:

CrossEntropyLoss 广泛应用于各种分类任务,包括图像分类、文本分类等,尤其是在神经网络模型中。

nn.CrossEntropyLoss基本原理:

由交叉熵公式可知:

$$
\text{Loss}(y, \hat{y}) = - \sum_{i=1}^{C} y_i \log(\hat{y}_i)
$$

因为y_i是one-hot编码,其值不是1便是0,又是乘法,所以只要知道1对应的index就可以了,展开后:

$$
\text{Loss}(y, \hat{y}) = - \log(\hat{y}_m)
$$

其中,m表示真实类别。

因为神经网络最后一层分类总是接softmax,所以可以把\hat{y}_m直接看为是softmax后的结果。

$$
\text{Loss}(i) = - \log(softmax(x_i))
$$

所以,CrossEntropyLoss 实质上是两步的组合:Cross Entropy = Log-Softmax + NLLLoss

  • Log-Softmax:对输入 logits 先计算对数 softmax:log(softmax(x))

  • NLLLoss(Negative Log-Likelihood):对 log-softmax 的结果计算负对数似然损失。简单理解就是求负数。原因是概率值通常在 0 到 1 之间,取对数后会变成负数。为了使损失值为正数,需要取负数。

对于softmax(x_i),在softmax介绍中了解到,需要减去最大值以确保数值稳定。

$$
\mathrm{Softmax}(x_i)=\frac{e^{x_i-\max(x)}}{\sum_{j=1}^ne^{x_j-\max(x)}}
$$

则:

$$
LogSoftmax(x_i) =log(\frac{e^{x_i-\max(x)}}{\sum_{j=1}^ne^{x_j-\max(x)}})\\ =x_i-\max(x)-log(\sum_{j=1}^ne^{x_j-\max(x)})
$$

所以:

$$
\text{Loss}(i) = - (x_i-\max(x)-log(\sum_{j=1}^ne^{x_j-\max(x)}))
$$

总的交叉熵损失函数是所有样本的平均值:

$$
\ell(x, y) = \begin{cases} \frac{\sum_{n=1}^N l_n}{N}, & \text{if reduction} = \text{`mean';}\\ \sum_{n=1}^N l_n, & \text{if reduction} = \text{`sum'.} \end{cases}
$$

示例代码如下:

 import torch
 import torch.nn as nn
 ​
 # 假设有三个类别,模型输出是未经softmax的logits
 logits = torch.tensor([[1.5, 2.0, 0.5], [0.5, 1.0, 1.5]])
 ​
 # 真实的标签
 labels = torch.tensor([1, 2])  # 第一个样本的真实类别为1,第二个样本的真实类别为2
 ​
 # 初始化CrossEntropyLoss
 # 参数:reduction:mean-平均值,sum-总和
 criterion = nn.CrossEntropyLoss()
 ​
 # 计算损失
 loss = criterion(logits, labels)
 print(f'Cross Entropy Loss: {loss.item()}')

在这个例子中,CrossEntropyLoss 直接作用于未经 softmax 处理的 logits 输出和真实标签,PyTorch 内部会自动应用 softmax 激活函数,并计算交叉熵损失。

分析示例中的代码:

 logits = torch.tensor([[1.5, 2.0, 0.5], [0.5, 1.0, 1.5]])

第一个样本的得分是 [1.5, 2.0, 0.5],分别对应类别 0、1 和 2 的得分。

第二个样本的得分是 [0.5, 1.0, 1.5],分别对应类别 0、1 和 2 的得分

 labels = torch.tensor([1, 2])

第一个样本的真实类别是 1。

第二个样本的真实类别是 2。

CrossEntropyLoss 的计算过程可以分为以下几个步骤:

(1) LogSoftmax 操作

首先,对每个样本的 logits 应用 LogSoftmax 函数,将 logits 转换为概率分布。LogSoftmax 函数的公式是: LogSoftmax(x_i) =x_i-\max(x)-log(\sum_{j=1}^ne^{x_j-\max(x)})

对于第一个样本 [1.5, 2.0, 0.5]

减去最大值:

x_i-\max(x)=[1.5-2.0,2.0-2.0,0.5-2.0]=[-0.5,0,-1.5]

计算e^{x_i-\max(x)}

$$
e^{x_i-\max(x)}=[e^{-0.5},e^{-0},e^{-1.5}]≈[0.6065, 1.0, 0.2231]
$$

求和并取对数:

$$
log(\sum_{j=1}^ne^{x_j-\max(x)})=log(0.6065 + 1.0 + 0.2231)=log(1.8296) ≈ 0.6041
$$

计算 log_softmax

$$
LogSoftmax(x_i) =x_i-\max(x)-log(\sum_{j=1}^ne^{x_j-\max(x)})= [-0.5-0.6041, 0.0-0.6041, -1.5-0.6041]\\ ≈ [-1.1041, -0.6041, -2.1041]
$$

对于第二个样本 [0.5, 1.0, 1.5]

减去最大值:

x_i-\max(x)=[0.5-1.5,1.0-1.5,1.5-1.5]=[-1.0,-0.5,0]

计算e^{x_i-\max(x)}

$$
e^{x_i-\max(x)}=[e^{-1.0},e^{-0.5},e^{0}]≈[0.3679, 0.6065, 1.0]
$$

求和并取对数:

$$
log(\sum_{j=1}^ne^{x_j-\max(x)})=log(0.3679+0.6065+1.0) =log(1.9744)≈ 0.6803
$$

计算 log_softmax

$$
LogSoftmax(x_i) =x_i-\max(x)-log(\sum_{j=1}^ne^{x_j-\max(x)})= [-1.0-0.6803,-0.5-0.6803,0-0.6803]\\ ≈ [-1.6803, -1.1803, -0.6803]
$$

(2) 计算每个样本的损失

接下来,根据真实标签 z_t 计算每个样本的交叉熵损失。交叉熵损失的公式是:   
$$L_i=−log(softmax(x_i))$$

对于第一个样本:

  • 真实类别是 1,对应的 softmax 值是 -0.6041。

对于第二个样本:

  • 真实类别是 2,对应的 softmax 值是 -0.6803。

(3) 计算平均损失

最后,计算所有样本的平均损失: 平均损失=-\frac{L_1+L_2}{2}=\frac{0.6041 + 0.6803}{2}=0.6422

3. BCELoss

二分类交叉熵损失函数,使用在输出层使用sigmoid激活函数进行二分类时。

由交叉熵公式:

$$
\text{CELoss}(y, \hat{y}) = - \sum_{i=1}^{C} y_i \log(\hat{y}_i)
$$

对于二分类问题,真实标签 y的值为(0 或 1),假设模型预测为正类的概率为 \hat{y},则:

$$
\begin{cases} {y} & 如果 y=1\\ 1−{y} & 如果 y=0 \end{cases}
$$

所以:

$$
\text{CELoss}(y, \hat{y}) = -[ylog(\hat{y}) + (1-y)log(1-\hat{y})]
$$

示例:

 import torch
 import torch.nn as nn
 ​
 # y 是模型的输出,已经被sigmoid处理过,确保其值域在(0,1)
 y = torch.tensor([[0.7], [0.2], [0.9], [0.7]])
 # targets 是真实的标签,0或1
 t = torch.tensor([[1], [0], [1], [0]], dtype=torch.float)
 ​
 # 计算损失方式一:
 bceLoss = nn.BCELoss()
 loss1 = bceLoss(y, t)
 ​
 #计算损失方式二: 两种方式结果相同
 loss2 = nn.functional.binary_cross_entropy(y, t)
 ​
 print(loss1, loss2)

逐样本计算

样本y_it_i计算项 t_i * log(y_i) + (1-t_i) * log(1-y_i)
10.711*log(0.7) + 0*log(0.3) ≈ -0.3567
20.200*log(0.2) + 1*log(0.8) ≈ -0.2231
30.911*log(0.9) + 0*log(0.1) ≈ -0.1054
40.700*log(0.7) + 1*log(0.3) ≈ -1.2040

计算最终损失

$$
BCE=−\frac{(−0.3567)+(−0.2231)+(−0.1054)+(−1.2040)}4=\frac{0.3567+0.2231+0.1054+1.2040}4≈0.4723
$$

4. 总结

  • 当输出层使用softmax多分类时,使用交叉熵损失函数;

  • 当输出层使用sigmoid二分类时,使用二分类交叉熵损失函数, 比如在逻辑回归中使用;

  • 当功能为线性回归时,使用均方差损失-L2 loss;
     


技术分享是一个相互学习的过程。关于本文的主题,如果你有不同的见解、发现了文中的错误,或者有任何不清楚的地方,都请毫不犹豫地在评论区留言。我很期待能和大家一起讨论,共同补充更多细节。

您可能感兴趣的与本文相关的镜像

GPT-oss:20b

GPT-oss:20b

图文对话
Gpt-oss

GPT OSS 是OpenAI 推出的重量级开放模型,面向强推理、智能体任务以及多样化开发场景

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值