参数初始化与损失函数

一、参数初始化

在神经网络中,参数初始化对于模型的训练效果至关重要。合理的初始化可以加速训练,避免梯度消失或梯度爆炸问题,甚至影响最终模型的性能。常见的初始化方法如下:
官方文档参考:Pytorch

1. 固定值初始化

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

1.1 全零初始化
import torch
import torch.nn as nn

# 创建全零初始化的权重
layer = nn.Linear(in_features=10, out_features=5)
torch.zeros_like(layer.weight)
  • 问题:全零初始化可能会导致所有神经元的输出相同,无法产生有效的特征学习,从而无法打破对称性,阻碍学习过程。
  • 总结:全零初始化不推荐用于深度神经网络,因为会导致训练停滞。
1.2 全1初始化

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

layer.weight.data.fill_(1)
  • 问题:全1初始化同样存在对称性问题,导致每个神经元的梯度更新相同,无法有效学习不同的特征。
  • 总结:虽然不如全零初始化明显问题大,但依然不可取。
1.3 任意常数初始化

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

layer.weight.data.fill_(0.01)
  • 问题:虽然这能避免全零初始化的问题,但常数初始化可能无法适应不同规模的模型,且并未针对性地利用权重的分布。
  • 总结:常数初始化可能对简单模型有效,但对于深度学习任务,无法保证稳定的训练效果。

2. 随机初始化

随机初始化能够帮助打破对称性,使得每个神经元在训练时能够有独立的学习能力。

2.1 均匀分布初始化

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

layer.weight.data.uniform_(-0.1, 0.1)
  • 问题:虽然能够打破对称性,但可能存在权重值过大或过小导致梯度消失或爆炸的问题。
  • 总结:一般用于较简单模型,对于复杂模型不推荐使用。
2.2 正态分布初始化
layer.weight.data.normal_(mean=0.0, std=0.01)
  • 问题:尽管能打破对称性,过大的方差可能导致梯度爆炸或消失问题。
  • 总结:适用于较浅的网络,仍需调整方差。

3. Xavier初始化

前置知识

均匀分布:

均匀分布的概率密度函数(PDF):
p ( x ) = { 1 b − a 如果 a ≤ x ≤ b 0 其他情况 p(x)=\begin{cases}\frac{1}{b−a} & 如果 a≤x≤b\\ 0 & 其他情况 \end{cases} p(x)={ba10如果axb其他情况
计算期望值(均值):
E [ X ] = ∫ a b x ⋅ p ( x )   d x = ∫ a b x ⋅ 1 b − a   d x = a + b 2 E[X]=∫_a^bx⋅p(x) dx=∫_a^bx⋅\frac{1}{b−a} dx=\frac{a+b}{2} E[X]=abxp(x)dx=abxba1dx=2a+b
计算方差(二阶矩减去均值的平方):
V a r ( X ) = E ( X − E ( X ) ) 2 = E [ X 2 ] − ( E [ X ] ) 2 Var(X)=E(X-E(X))^2=E[X^2]−(E[X])^2 Var(X)=E(XE(X))2=E[X2](E[X])2

  • 先计算 E [ X 2 ] E[X^2] E[X2]
    E [ X 2 ] = ∫ a b x 2 ⋅ 1 b − a   d x = b 3 − a 3 3 ( b − a ) = a 2 + a b + b 2 3 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} E[X2]=abx2ba1dx=3(ba)b3a3=3a2+ab+b2

  • 代入方差公式:
    V a r ( X ) = a 2 + a b + b 2 3 − ( a + b 2 ) 2 = ( b − a ) 2 12 Var(X)=\frac{a^2+ab+b^2}{3}−(\frac{a+b}{2})^2=\frac{(b−a)^2}{12} Var(X)=3a2+ab+b2(2a+b)2=12(ba)2

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

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

数学原理:

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

假设输入 x 的均值为 0,方差为 σ x 2 σ_x^2 σx2,权重 W的均值为 0,方差为 σ W 2 σ_W^2 σW2,则输出 z = W x z=Wx z=Wx的方差为:
V a r ( z ) = n i n ⋅ V a r ( W ) ⋅ V a r ( x ) Var(z)=n_{in}⋅Var(W)⋅Var(x) Var(z)=ninVar(W)Var(x)
为了使 Var(z)=Var(x),需要:
n i n ⋅ V a r ( W ) = 1    ⟹    V a r ( W ) = 1 n i n n_{in}⋅Var(W)=1  ⟹  Var(W)=\frac{1}{n_{in}} ninVar(W)=1    Var(W)=nin1
其中 n i n n_{in} nin是输入维度(fan_in)。这里乘以 nin 的原因是,输出 z 是由 nin 个输入 x 的线性组合得到的,每个输入 x 都与一个权重 W 相乘。因此,输出 z 的方差是 nin 个独立的 Wx 项的方差之和。

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

在反向传播过程中,梯度 ∂ L ∂ x \frac{∂L}{∂x} xL 是通过链式法则计算得到的,其中 L 是损失函数,x 是输入,z 是输出。梯度 ∂ L ∂ x \frac{∂L}{∂x} xL可以表示为:
∂ L ∂ x = ∂ L ∂ z . ∂ z ∂ x \frac{∂L}{∂x}=\frac{∂L}{∂z}.\frac{∂z}{∂x} xL=zL.xz
假设 z=Wx,其中 W 是权重矩阵,那么 ∂ z ∂ x = W \frac{∂z}{∂x}=W xz=W。因此,梯度 ∂ L ∂ x \frac{∂L}{∂x} xL可以写为: ∂ L ∂ x = ∂ L ∂ z W \frac{∂L}{∂x}=\frac{∂L}{∂z}W xL=zLW

反向传播时梯度 ∂ L ∂ x \frac{∂L}{∂x} xL 的方差应与 ∂ L ∂ z \frac{∂L}{∂z} zL 相同,因此:
n o u t ⋅ V a r ( W ) = 1    ⟹    V a r ( W ) = 1 n o u t n_{out}⋅Var(W)=1  ⟹  Var(W)=\frac{1}{n_{out}} noutVar(W)=1    Var(W)=nout1
其中 n o u t n_{out} nout是输出维度(fan_out)。为了保持梯度的方差一致性,我们需要确保每个输入维度 nin 的梯度方差与输出维度 nout 的梯度方差相同。因此,我们需要将 W 的方差乘以 nout,以确保梯度的方差在反向传播过程中保持一致。

(3) 综合考虑

为了同时平衡前向传播和反向传播,Xavier 采用:
V a r ( W ) = 2 n i n + n o u t Var(W)=\frac{2}{n_{in}+n_{out}} Var(W)=nin+nout2
权重从以下分布中采样:

均匀分布
W ∼ U ( − 6 n i n + n o u t , 6 n i n + n o u t ) 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) WU(nin+nout 6 ,nin+nout 6 )

在Xavier初始化中,我们选择 a = − 6 n i n + n o u t a=−\sqrt{\frac{6}{n_{in}+n_{out}}} a=nin+nout6 b = 6 n i n + n o u t b=\sqrt{\frac{6}{n_{in}+n_{out}}} b=nin+nout6 ,这样方差为:
V a r ( W ) = ( b − a ) 2 12 = ( 2 6 n i n + n o u t ) 2 12 = 4 ⋅ 6 n i n + n o u t 12 = 2 n i n + n o u t 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}} Var(W)=12(ba)2=12(2nin+nout6 )2=124nin+nout6=nin+nout2
正态分布
W ∼ N ( 0 , 2 n i n + n o u t ) W\sim\mathrm{N}\left(0,\frac{2}{n_\mathrm{in}+n_\mathrm{out}}\right) WN(0,nin+nout2)

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

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

在前向传播中,输出的方差受 n i n n_{in} nin 影响。在反向传播中,梯度的方差受 n o u t n_{out} nout 影响。

优点:平衡了输入和输出的方差,适合 S i g m o i d Sigmoid Sigmoid T a n h Tanh Tanh 激活函数。

应用场景:常用于浅层网络或使用 S i g m o i d Sigmoid Sigmoid T a n h Tanh 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 激活函数,输出的方差为:
V a r ( z ) = 1 2 n i n ⋅ V a r ( W ) ⋅ V a r ( x ) Var(z)=\frac{1}{2}n_{in}⋅Var(W)⋅Var(x) Var(z)=21ninVar(W)Var(x)
(因为 ReLU 使一半神经元输出为 0,方差减半)
为使 Var(z)=Var(x),需:
1 2 n i n ⋅ V a r ( W ) = 1    ⟹    V a r ( W ) = 2 n i n \frac{1}{2}n_{in}⋅Var(W)=1  ⟹  Var(W)=\frac{2}{n_{in}} 21ninVar(W)=1    Var(W)=nin2
(2) 反向传播的梯度一致性

类似地,反向传播时梯度方差需满足:
V a r ( ∂ L ∂ x ) = 1 2 n o u t ⋅ V a r ( W ) ⋅ V a r ( ∂ L ∂ z ) Var(\frac{∂L}{∂x})=\frac{1}{2}n_{out}⋅Var(W)⋅Var(\frac{∂L}{∂z}) Var(xL)=21noutVar(W)Var(zL)
因此:
V a r ( W ) = 2 n o u t Var(W)=\frac{2}{n_{out}} Var(W)=nout2
(3) 两种模式

  • fan_in 模式(默认):优先保证前向传播稳定,方差 2 n i n \frac{2}{n_{in}} nin2
  • fan_out 模式:优先保证反向传播稳定,方差 2 n o u t \frac{2}{n_{out}} nout2

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

均匀分布:
W ∼ U ( − 6 n i n , 6 n i n ) W\sim\mathrm{U}\left(-\frac{\sqrt{6}}{\sqrt{n_\mathrm{in}}},\frac{\sqrt{6}}{\sqrt{n_\mathrm{in}}}\right) WU(nin 6 ,nin 6 )
正态分布:
W ∼ N ( 0 , 2 n i n ) W\sim\mathrm{N}\left(0,\frac{2}{n_\mathrm{in}}\right) WN(0,nin2)
其中 n in n_{\text{in}} nin 是当前层的输入神经元数量。

优点:适用于 R e L U ReLU ReLU L e a k y R e L U Leaky ReLU LeakyReLU 激活函数。

应用场景:深度网络,尤其是使用 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. 总结

合理的参数初始化有助于网络的训练稳定性。对于较浅的网络,随机初始化或Xavier初始化即可。而对于深度网络,推荐使用He初始化,以避免梯度消失或爆炸。


二、损失函数

损失函数是机器学习中优化目标的关键,选择合适的损失函数可以帮助模型快速有效地学习任务。

1. 线性回归损失函数

1.1 MAE(Mean Absolute Error)损失

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

MAE的公式如下:
MAE = 1 n ∑ i = 1 n ∣ y i − y ^ i ∣ \text{MAE} = \frac{1}{n} \sum_{i=1}^{n} \left| y_i - \hat{y}_i \right| MAE=n1i=1nyiy^i
其中:

  • n n n 是样本的总数。
  • y i y_i yi 是第 i i i 个样本的真实值。
  • y ^ i \hat{y}_i y^i 是第 i i i 个样本的预测值。
  • ∣ y i − y ^ i ∣ \left| y_i - \hat{y}_i \right| yiy^i 是真实值和预测值之间的绝对误差。

特点

  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(Mean Squared Error)损失

均方差损失,也叫L2Loss。

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

MSE的公式如下:
MSE = 1 n ∑ i = 1 n ( y i − y ^ i ) 2 \text{MSE} = \frac{1}{n} \sum_{i=1}^{n} \left( y_i - \hat{y}_i \right)^2 MSE=n1i=1n(yiy^i)2
其中:

  • n n n 是样本的总数。
  • y i y_i yi 是第 i i i 个样本的真实值。
  • y ^ i \hat{y}_i y^i 是第 i i i 个样本的预测值。
  • ( y i − y ^ i ) 2 \left( y_i - \hat{y}_i \right)^2 (yiy^i)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 信息量、信息熵、KL散度
  • 信息量:反映一个事件不确定性的大小,常用对数表示。时间发生的概率越低,其信息量越大
  • 信息熵:是信息量的期望值,表示系统的平均不确定性。
  • KL散度:衡量两个概率分布的相似度,常用于评估模型的输出分布与目标分布的差异。KL散度越小,表示两个分布越接近
2.2 交叉熵损失(CrossEntropyLoss)
loss = F.cross_entropy(pred, target)
  • 解释:交叉熵损失函数用于分类任务,衡量两个概率分布之间的差异,能够有效训练分类模型。
  • 总结:交叉熵损失函数常用于多分类任务中,是分类问题的标准损失函数。

3. BCELoss(二元交叉熵损失)

loss = F.binary_cross_entropy(pred, target)
  • 解释:BCELoss用于二分类任务,衡量目标概率分布与预测概率分布之间的差异。
  • 总结:当模型处理二分类任务时,BCELoss是最常见的损失函数。

4. 总结

选择损失函数时,应该根据任务的类型(回归、分类等)以及对异常值的敏感度来选择。MAE适合异常值少的任务,MSE适合精度要求高的任务,而交叉熵和BCELoss分别适用于多分类和二分类问题。


Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐