先看代码:
class Embeddings(nn.Module):
def __init__(self, d_model, vocab):
super(Embeddings, self).__init__()
self.lut = nn.Embedding(vocab, d_model)
self.d_model = d_model
def forward(self, x):
return self.lut(x) * math.sqrt(self.d_model)
在得到嵌入矩阵后,主动乘以了一个 d m o d e l \sqrt {{d}_{model}} dmodel这里 d m o d e l = 512 {d}_{model}=512 dmodel=512那么这里为什么要乘以一个 d m o d e l \sqrt {{d}_{model}} dmodel呢?
首先我们对其效果做一个结论:这是由于后续要和positional encodeing部分的输出相加,所以一定要保持一个差不多的scale(来自李沐的答案)。
以此来防止梯度消失和梯度爆炸(每一层的输出的方差接近于其输入的方差,从而避免梯度消失或梯度爆炸的问题),并且可以加快收敛速度(每一层输出的方差都接近输入的方差,使得每一层的梯度方差接近于1,这样每一层的参数更新幅度不会相差太大,从而加快收敛速度)
下面进行定性分析,为什么要乘以
d
m
o
d
e
l
\sqrt{{d}_{model}}
dmodel?
首先经过初始化(Xavier初始化)的nn.Embedding.weight矩阵即
W
W
W参数矩阵满足如下分布:
W
∼
(
0
,
1
n
)
W\sim (0,\, \frac {1} {n})
W∼(0,n1)
其中
n
n
n表示
d
m
o
d
e
l
{d}_{model}
dmodel。具体理论可以参考:链接
其中我们拿到的Embedding矩阵
E
m
b
e
d
d
i
n
g
=
(
O
n
e
−
H
o
t
)
W
Embedding = (One-Hot)W
Embedding=(One−Hot)W
其相当于是从W里抽出来的
d
m
o
d
e
l
d_model
dmodel个样本。
那么Elements of Embedding
就是正态总体Elements of W
中的子样。
正态总体子样的均值
X
‾
=
∑
i
=
1
n
X
i
\overline{X}=\sum ^{n}_{i=1} {{X}_{i}}
X=∑i=1nXi和方差满足:
X
‾
∼
N
(
μ
,
σ
2
/
n
)
\overline{X}\sim N(\mu,{\sigma}^{2}/n)
X∼N(μ,σ2/n)
(
n
−
1
)
S
2
σ
2
∼
X
2
(
n
−
1
)
\frac {(n-1){S}^{2}} {{\sigma }^{2}}\sim {\mathcal{X}}^{2}(n-1)
σ2(n−1)S2∼X2(n−1)
推导过程
其中
μ
=
0
,
σ
2
=
1
n
\mu=0, {\sigma}^{2}=\frac{1}{n}
μ=0,σ2=n1,卡方分布
X
2
(
n
−
1
)
{\mathcal{X}}^{2}(n-1)
X2(n−1)的均值和方差分别是
n
−
1
n-1
n−1和
2
(
n
−
1
)
2(n-1)
2(n−1)所以有
E
(
X
‾
)
=
0
E(\overline{X})=0
E(X)=0.
E
(
(
n
−
1
)
S
2
σ
2
)
=
n
−
1
E(\frac {(n-1){S}^{2}} {{\sigma }^{2}})=n-1
E(σ2(n−1)S2)=n−1
可解得
E
(
S
2
)
=
1
n
E({S}^{2})=\frac{1}{n}
E(S2)=n1
综上所述:
E
m
b
e
d
d
i
n
g
∼
N
(
0
,
1
d
m
o
d
e
l
)
Embedding\sim N(0, \frac{1}{{d}_{model}})
Embedding∼N(0,dmodel1)
所以我们需要乘以一个
d
m
o
d
e
l
\sqrt{{d}_{model}}
dmodel把
E
m
b
d
e
d
i
n
g
Embdeding
Embdeding调整到
N
(
0
,
1
)
N(0,1)
N(0,1)
参考文章:
https://www.zhihu.com/question/415263284/answer/2010360549