深度学习 —参数初始化以及损失函数
文章目录
一,参数初始化
神经网络的参数初始化是训练深度学习模型的关键步骤之一。初始化参数(通常是权重和偏置)会对模型的训练速度、收敛性以及最终的性能产生重要影响。
1.1 固定值初始化
固定值初始化是指在神经网络训练开始时,将所有权重或偏置初始化为一个特定的常数值。这种初始化方法虽然简单,但在实际深度学习应用中通常并不推荐。
1.1.1 全0初始化
方面 | 内容 |
---|---|
方法 | 将所有权重初始化为零。 |
缺点 | 1. 对称性破坏:每个神经元在每一层中都会执行相同的计算,模型无法学习。 2. 权重更新一致:在反向传播时,所有神经元会收到相同的梯度,导致权重更新完全一致。 3. 降低表达能力:同一层的神经元会保持相同的功能,极大降低模型的表达能力。 |
应用场景 | 通常不用来初始化权重,但可以用来初始化偏置。 |
对称性问题 | - 现象:同一层的所有神经元具有完全相同的初始权重和偏置。 - 后果:在反向传播时,所有神经元会收到相同的梯度,导致权重更新完全一致;无论训练多久,同一层的神经元本质上会保持相同的功能(相当于“一个神经元”的多个副本),极大降低模型的表达能力。 |
import torch
import torch.nn as nn
def test004():
linear = nn.Linear(in_features=6, out_features=4)
nn.init.zeros_(linear.weight)
print(linear.weight)
if __name__ == "__main__":
test004()
1.1.2 全1初始化
全1初始化会导致网络中每个神经元接收到相同的输入信号,进而输出相同的值,这就无法进行学习和收敛。所以全1初始化只是一个理论上的初始化方法,但在实际神经网络的训练中并不适用
import torch
import torch.nn as nn
def test003():
linear = nn.Linear(in_features=6, out_features=4)
nn.init.ones_(linear.weight)
print(linear.weight)
if __name__ == "__main__":
test003()
1.3 任意常数初始化
将所有参数初始化为某个非零的常数(如 0.1,-1 等)。虽然不同于全0和全1,但这种方法依然不能避免对称性破坏的问题
import torch
import torch.nn as nn
def test002():
linear = nn.Linear(in_features=6, out_features=4)
nn.init.constant_(linear.weight, 0.63)
print(linear.weight)
pass
if __name__ == "__main__":
test002()
1.2 随机初始化
方面 | 内容 |
---|---|
方法 | 使用随机数生成器为每个权重分配一个随机值。通常从某个分布(如正态分布或均匀分布)中采样。例如: - 均匀分布:从 [ − a , a ] [-a, a] [−a,a] 中随机采样,其中 a a a 是一个小的常数。 - 正态分布:从均值为0、标准差为 σ \sigma σ 的正态分布中采样。 |
优点 | 1. 打破对称性:每个神经元的初始权重不同,避免了所有神经元执行相同计算的问题。 2. 加速收敛:随机初始化可以避免梯度消失或梯度爆炸的问题,使训练过程更加稳定。 3. 提高模型表达能力:不同的初始权重使得每个神经元能够学习不同的特征,增强了模型的表达能力。 |
缺点 | 1. 选择合适的范围:如果随机初始化的范围过大或过小,可能会导致训练不稳定或收敛缓慢。例如,范围过大可能导致梯度爆炸,范围过小可能导致梯度消失。 2. 需要调整超参数:需要选择合适的分布和范围,这可能需要一些实验和调优。 |
应用场景 | 适用于大多数神经网络的权重初始化,尤其是深层网络。正态分布和均匀分布是最常用的初始化方法。 |
注意事项 | 1. 小范围初始化:通常选择较小的随机范围,例如
[
−
0.01
,
0.01
]
[-0.01, 0.01]
[−0.01,0.01] 或标准差为0.01的正态分布。 2. 根据网络深度调整:对于较深的网络,可能需要更小的初始化范围,以避免梯度消失或爆炸。 3. 结合激活函数:不同的激活函数可能需要不同的初始化策略。例如,ReLU 激活函数通常需要使用 He 初始化(正态分布,标准差为 2 / n \sqrt{2/n} 2/n),而 Sigmoid 或 Tanh 激活函数通常使用 Xavier 初始化(均匀分布或正态分布,范围根据输入和输出维度调整)。 |
def test01():
model =nn.Linear(8,1)
print(model.weight)
#均匀分步初始化
nn.init.normal_(model.weight,mean=0,std=0.01)
print(model.weight)
#正态分布初始化
# 均值为0,标准差为0.01
nn.init.normal_(model.weight,mean=0,std=0.01)
print(model.weight)
if __name__ == '__main__':
test01()
1.3 Xavier 初始化
维度 | 内容 |
---|---|
提出者 | Xavier Glorot(2010) |
目的 | 缓解神经网络训练初期的梯度消失/爆炸问题,保持每层的输出/梯度方差一致 |
适用激活函数 | Sigmoid、Tanh(适用于对称、零中心的激活函数) |
核心思想 | 根据输入( n i n n_{in} nin)和输出( n o u t n_{out} nout)维度自适应初始化权重方差 |
方差公式 | 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 U\left(-\sqrt{\frac{6}{n_{in}+n_{out}}}, \sqrt{\frac{6}{n_{in}+n_{out}}}\right) W∼U(−nin+nout6,nin+nout6) |
正态分布初始化 | W ∼ N ( 0 , 2 n i n + n o u t ) W \sim N\left(0, \frac{2}{n_{in}+n_{out}}\right) W∼N(0,nin+nout2) |
前向传播约束 | V a r ( z ) = V a r ( x ) ⇒ V a r ( W ) = 1 n i n Var(z) = Var(x) \Rightarrow Var(W) = \frac{1}{n_{in}} Var(z)=Var(x)⇒Var(W)=nin1 |
反向传播约束 | V a r ( ∂ L ∂ x ) = V a r ( ∂ L ∂ z ) ⇒ V a r ( W ) = 1 n o u t Var\left(\frac{\partial L}{\partial x}\right) = Var\left(\frac{\partial L}{\partial z}\right) \Rightarrow Var(W) = \frac{1}{n_{out}} Var(∂x∂L)=Var(∂z∂L)⇒Var(W)=nout1 |
综合平衡 | 取前向和反向传播约束的平均值: V a r ( W ) = 2 n i n + n o u t Var(W) = \frac{2}{n_{in} + n_{out}} Var(W)=nin+nout2 |
优点 | 1. 保持前向/反向传播方差稳定 2. 适用于浅层网络和对称激活函数(Sigmoid/Tanh) |
局限性 | 不适用于ReLU等非对称激活函数(需改用He初始化) |
def test04():
model=MyNet1(10,2)
nn.init.xavier_uniform_(model.fc1.weight)
print(model)
if __name__ == '__main__':
test04()
1.4 He初始化
维度 | He 初始化(Kaiming 初始化) |
---|---|
提出者 | Kaiming He(2015) |
目的 | 解决 ReLU 激活函数在深度网络中的梯度消失/爆炸问题,保持前向/反向传播方差稳定 |
适用激活函数 | ReLU、Leaky ReLU(非对称、非零中心激活函数) |
核心思想 | 针对 ReLU 的“一半神经元输出为 0”特性,调整权重方差(比 Xavier 大 2 倍) |
方差公式 | - fan_in 模式:
V
a
r
(
W
)
=
2
n
i
n
Var(W) = \frac{2}{n_{in}}
Var(W)=nin2(默认,前向传播稳定) - fan_out 模式: V a r ( W ) = 2 n o u t Var(W) = \frac{2}{n_{out}} Var(W)=nout2(反向传播稳定) |
均匀分布初始化 | W ∼ U ( − 6 n i n , 6 n i n ) W \sim U\left(-\sqrt{\frac{6}{n_{in}}}, \sqrt{\frac{6}{n_{in}}}\right) W∼U(−nin6,nin6)(fan_in 模式) |
正态分布初始化 | W ∼ N ( 0 , 2 n i n ) W \sim N\left(0, \frac{2}{n_{in}}\right) W∼N(0,nin2)(fan_in 模式) |
前向传播约束 | V a r ( z ) = V a r ( x ) ⇒ 1 2 n i n V a r ( W ) = 1 Var(z) = Var(x) \Rightarrow \frac{1}{2} n_{in} Var(W) = 1 Var(z)=Var(x)⇒21ninVar(W)=1(ReLU 导致方差减半) |
反向传播约束 | $Var\left(\frac{\partial L}{\partial x}\right) = Var\left(\frac{\partial L}{\partial z}\right) |
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()
二,损失函数
2.1 线性回归损失函数
2.1.1 MAE损失
维度 | 说明 | ||
---|---|---|---|
全称 | Mean Absolute Error | ||
别名 | L1-Loss、L1 范数误差 | ||
公式 | ![]() | ||
符号 | •
n
n
n:样本总数 • y i y_i yi:第 i i i 个真实值 • y ^ i \hat{y}_i y^i:第 i i i 个预测值 | ||
梯度 | ∂ MAE ∂ y ^ i = { + 1 , y ^ i > y i − 1 , y ^ i < y i 不可导 , y ^ i = y i \displaystyle\frac{\partial\,\text{MAE}}{\partial\hat{y}_i}= \begin{cases}+1,&\hat{y}_i>y_i\\-1,&\hat{y}_i<y_i\\\text{不可导},&\hat{y}_i=y_i\end{cases} ∂y^i∂MAE=⎩ ⎨ ⎧+1,−1,不可导,y^i>yiy^i<yiy^i=yi(常用次梯度 [ − 1 , 1 ] [-1,1] [−1,1] 处理) | ||
特点 | 1. 鲁棒:对异常值不敏感(相比 MSE 无平方放大效应) 2. 直观:误差单位与原数据一致 | ||
优点 | • 抗离群点能力强 • 易于业务解释 | ||
缺点 | • 在
y
i
=
y
^
i
y_i=\hat{y}_i
yi=y^i 处不可导(需次梯度) • 对极小误差“不敏感”,可能导致模型收敛到次优 | ||
适用场景 | • 数据含潜在异常值 • 需线性、可解释误差度量(如金融风控、房价预测) | ||
对比 MSE | MAE 更鲁棒,MSE 更平滑且对异常值敏感;二者均为凸损失函数 |
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()}')
2.1.2 MSE损失
名称 | 均方差损失 / L2Loss / MSE(Mean Squared Error,均方误差) |
---|---|
定义 | 对预测值与真实值之间的误差平方取平均,衡量二者差异 |
公式 | MSE = 1 n ∑ i = 1 n ( y i − y ^ i ) 2 \displaystyle \text{MSE}=\frac{1}{n}\sum_{i=1}^{n}(y_i-\hat{y}_i)^2 MSE=n1i=1∑n(yi−y^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 (y_i-\hat{y}_i)^2 (yi−y^i)2:单个样本的误差平方 |
特点 | 1. 平方惩罚:对较大误差施加重罚,因而对异常值敏感 2. 凸性:函数为凸,具有唯一全局最小值,便于优化 |
应用场景 | 广泛用于神经网络及其它回归问题的损失函数 |
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.2 CrossEntropyLoss
2.1 信息量
信息量用于衡量一个事件所包含的信息的多少。信息量的定义基于事件发生的概率:事件发生的概率越低,其信息量越大。其量化公式:
对于一个事件x,其发生的概率为 P(x),信息量I(x) 定义为:
I
(
x
)
=
−
l
o
g
P
(
x
)
I(x)=−logP(x)
I(x)=−logP(x)
性质
- 非负性:I(x)≥0。
- 单调性:P(x)越小,I(x)越大。
2.2 信息熵
信息熵是信息量的期望值。熵越高,表示随机变量的不确定性越大;熵越低,表示随机变量的不确定性越小。
公式由数学中的期望推导而来:
H
(
X
)
=
−
∑
i
=
1
n
P
(
x
i
)
l
o
g
P
(
x
i
)
H(X)=−∑_{i=1}^n P(x_i)logP(x_i)
H(X)=−i=1∑nP(xi)logP(xi)
其中:
− l o g P ( x i ) -logP(x_i) −logP(xi)是信息量, P ( x i ) P(x_i) P(xi)是信息量对应的概率
2.3 KL散度
KL散度用于衡量两个概率分布之间的差异。它描述的是用一个分布 Q来近似另一个分布 P时,所损失的信息量。KL散度越小,表示两个分布越接近。
对于两个离散概率分布 P和 Q,KL散度定义为:
D
K
L
(
P
∣
∣
Q
)
=
∑
i
P
(
x
i
)
l
o
g
P
(
x
i
)
Q
(
x
i
)
D_{KL}(P||Q)=∑_iP(x_i)log\frac{P(x_i)}{Q(x_i)}
DKL(P∣∣Q)=i∑P(xi)logQ(xi)P(xi)
其中:P 是真实分布,Q是近似分布。
2.4 交叉熵
对KL散度公式展开:
D
K
L
(
P
∣
∣
Q
)
=
∑
i
P
(
x
i
)
l
o
g
P
(
x
i
)
Q
(
x
i
)
=
∑
i
P
(
x
i
)
[
l
o
g
P
(
x
i
)
−
l
o
g
Q
(
x
i
)
]
=
∑
i
P
(
x
i
)
l
o
g
P
(
x
i
)
−
∑
i
P
(
x
i
)
l
o
g
Q
(
x
i
)
=
−
(
−
∑
i
P
(
x
i
)
l
o
g
P
(
x
i
)
)
+
(
−
∑
i
P
(
x
i
)
l
o
g
Q
(
x
i
)
)
=
−
H
(
P
)
+
(
−
∑
i
P
(
x
i
)
l
o
g
Q
(
x
i
)
)
=
H
(
P
,
Q
)
−
H
(
P
)
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)
DKL(P∣∣Q)=i∑P(xi)logQ(xi)P(xi)=i∑P(xi)[logP(xi)−logQ(xi)]=i∑P(xi)logP(xi)−i∑P(xi)logQ(xi)=−(−i∑P(xi)logP(xi))+(−i∑P(xi)logQ(xi))=−H(P)+(−i∑P(xi)logQ(xi))=H(P,Q)−H(P)
由上述公式可知,P是真实分布,H§是常数,所以KL散度可以用H(P,Q)来表示;H(P,Q)叫做交叉熵。
如果将P换成y,Q换成
y
^
\hat{y}
y^,则交叉熵公式为:
CrossEntropyLoss
(
y
,
y
^
)
=
−
∑
i
=
1
C
y
i
log
(
y
^
i
)
\text{CrossEntropyLoss}(y, \hat{y}) = - \sum_{i=1}^{C} y_i \log(\hat{y}_i)
CrossEntropyLoss(y,y^)=−i=1∑Cyilog(y^i)
- 函数曲线图
特点:
-
概率输出:CrossEntropyLoss 通常与 softmax 函数一起使用,使得模型的输出表示为一个概率分布(即所有类别的概率和为 1)。PyTorch 的 nn.CrossEntropyLoss 已经内置了 Softmax 操作。如果我们在输出层显式地添加 Softmax,会导致重复应用 Softmax,从而影响模型的训练效果。
-
惩罚错误分类:该损失函数在真实类别的预测概率较低时,会施加较大的惩罚,这样模型在训练时更注重提升正确类别的预测概率。
-
多分类问题中的标准选择:在大多数多分类问题中,CrossEntropyLoss 是首选的损失函数。
应用场景:
CrossEntropyLoss 广泛应用于各种分类任务,包括图像分类、文本分类等,尤其是在神经网络模型中。
import torch
import torch.nn as nn
# 假设有三个类别
logits = torch.tensor([[1.5, 2.0, 0.5], [0.5, 1.0, 1.5]])
# 真实的标签
labels = torch.tensor([1, 2])
criterion = nn.CrossEntropyLoss()
# 计算损失
loss = criterion(logits, labels)
print(f'Cross Entropy Loss: {loss.item()}')
2.3 BCELoss
名称 | 二分类交叉熵损失(Binary Cross-Entropy Loss,BCE) | ||||
---|---|---|---|---|---|
适用场景 | 输出层使用 Sigmoid 激活函数的二分类任务 | ||||
通用交叉熵公式 | CELoss ( y , y ^ ) = − ∑ i = 1 C y i log ( y ^ i ) \displaystyle \text{CELoss}(y,\hat{y})=-\sum_{i=1}^{C} y_i\log(\hat{y}_i) CELoss(y,y^)=−i=1∑Cyilog(y^i) | ||||
二分类简化 | 类别数 C = 2 C=2 C=2,真实标签 y ∈ { 0 , 1 } y\in\{0,1\} y∈{0,1} | ||||
简化后公式 | BCE ( y , y ^ ) = − [ y log ( y ^ ) + ( 1 − y ) log ( 1 − y ^ ) ] \displaystyle \text{BCE}(y,\hat{y})=-\left[y\log(\hat{y})+(1-y)\log(1-\hat{y})\right] BCE(y,y^)=−[ylog(y^)+(1−y)log(1−y^)] | ||||
符号说明 | •
y
y
y:真实标签(0 或 1) • y ^ \hat{y} y^:模型预测为正类的概率(Sigmoid 输出) | ||||
示例 | 真实标签 y y y | 预测概率 y ^ \hat{y} y^ | 损失计算 | 结果 | |
— | — | — | — | — | |
1 | 0.9 | − [ 1 ⋅ log ( 0.9 ) + ( 1 − 1 ) ⋅ log ( 1 − 0.9 ) ] -[\,1\cdot\log(0.9)+(1-1)\cdot\log(1-0.9)\,] −[1⋅log(0.9)+(1−1)⋅log(1−0.9)] | ≈ 0.105 | ||
0 | 0.2 | − [ 0 ⋅ log ( 0.2 ) + ( 1 − 0 ) ⋅ log ( 1 − 0.2 ) ] -[\,0\cdot\log(0.2)+(1-0)\cdot\log(1-0.2)\,] −[0⋅log(0.2)+(1−0)⋅log(1−0.2)] | ≈ 0.223 |
import torch
import torch.nn as nn
y = torch.tensor([[0.7], [0.2], [0.9], [0.7]])
#真实的标签
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)