预备知识
- 假设x是一个随机变量,它的均值和方差的定义为:
- 假设x,y是两个随机变量,记z=xy,则随机变量z的均值和方差为:
- 特别地,当x,y独立的时候,则有:
- 均匀分布的期望、方差:
为什么要初始化weights?
权值初始化的目的是防止层激活输出在深度神经网络的正向传递过程中爆炸或消失。如果发生任何一种情况,损失梯度要么太大,要么太小,无法有利地向后流动,如果网络能够这样做,则需要更长的时间才能收敛。
仿真实验
- case - 1
x = torch.randn(512, 1) # N(0,1)
for i in range(100):
A = torch.randn(512, 512) # 用 N(0,1) 来初始化weights
x = torch.mm(A, x)
x.mean(), x.std() # (tensor(nan), tensor(nan))
- case - 2
x = torch.randn(512, 1) # N(0,1)
for i in range(100):
A = torch.randn(512, 512) * 0.01 # 用 N(0,1e-4) 来初始化weights
x = torch.mm(A, x)
x.mean(), x.std() # (tensor(0.), tensor(0.))
- case - 3
-
num_iter = 10000 mean, var = 0.0, 0.0 for i in range(num_iter): x = torch.randn(512, 1) A = torch.randn(512, 512) # 用 N(0,1) 来初始化weights y = torch.mm(A, x) # 考虑y的每个维度,计算公式参考预备知识 mean += y.mean().item() # E(xy) = E(x)E(y) var += y.var().item() # Var(xy) = Var(x)Var(y) mean/num_iter, var/num_iter # (-0.004299676742777228, 512.4343985168457) ~ (0, 512) # Remark : 这也解释了为什么case - 1会出现nan!
-
num_iter = 10000 mean, var = 0.0, 0.0 for i in range(num_iter): x = torch.randn(512, 1) A = torch.randn(512, 512) /math.sqrt(512) # 核心操作 y = torch.mm(A, x) # 考虑y的每个维度,计算公式参考预备知识 mean += y.mean().item() # E(xy) = E(x)E(y) var += y.var().item() # Var(xy) = Var(x)Var(y) mean/num_iter, var/num_iter # (-0.0001050436558551155, 0.9986829454898835) ~ (0, 1)
Remark : 通过仿真实验,如果采用Naive的初始化方式[case-1或者case-2],可能会导致模型在前向传播时出现信号过大或者过小,这非常不利于模型训练、收敛!接下来介绍的Xavier_normal、Kaiming_normal的motivation就基于此,它们可以使得信号强度(体现在Variance)在网络的训练过程中保持不变,从而加速网络的收敛速度!
这里参考了[2]中的分析,以网络的FC层为例[Conv层同理] : Y = WX + B,这里,
,
均为随机变量。若想在前向、后向传播时保持信号的强度不变,则有:Var(Y) = Var(X), Var(delta_X) = Var(delta_Y).
参考文献
[2]. https://www.zhihu.com/search?type=content&q=xavier_normal