赞
踩
这篇文章是关于《如何从浅入深理解transformer》 的一个阅读笔记。
因为是第一次接触 transformer,找了半天,感觉这篇文章作为入门来说还不错,可以将整个发展的历程串联起来讲。
但是这毕竟是一篇阅读笔记,我只会对个人不太清楚的概念详细了解。
n-Gram 模型的主体思想就是:下一个词出现的概率只依赖于它前面 n-1 个词,也被称为 n 阶马尔科夫链。
文中出现的公式: P ( w t ∣ w 1 , w 2 , . . . , w t − 1 ) = C ( w 1 , w 2 , . . . , w t ) C ( w 1 , w 2 , . . . , w t − 1 ) P(w_t|w_1, w_2,...,w_{t-1}) = \frac{C(w_1, w_2,...,w_{t})}{C(w_1, w_2,...,w_{t-1})} P(wt∣w1,w2,...,wt−1)=C(w1,w2,...,wt−1)C(w1,w2,...,wt) 其中的参数 C ( w 1 , w 2 , . . . , w t ) C(w_1, w_2,...,w_{t}) C(w1,w2,...,wt) 表示词序列 w 1 , w 2 , . . . , w t w_1, w_2,...,w_{t} w1,w2,...,wt 在训练语料中出现的次数(count)。 C ( w 1 , w 2 , . . . , w t − 1 ) C(w_1, w_2,...,w_{t-1}) C(w1,w2,...,wt−1) 也是一样的含义。但是根据条件概率的定义的,本来的公式应该是: P ( w t ∣ w 1 , w 2 , . . . , w t − 1 ) = P ( w 1 , w 2 , . . . , w t ) P ( w 1 , w 2 , . . . , w t − 1 ) P(w_t|w_1, w_2,...,w_{t-1}) = \frac{P(w_1, w_2,...,w_{t})}{P(w_1, w_2,...,w_{t-1})} P(wt∣w1,w2,...,wt−1)=P(w1,w2,...,wt−1)P(w1,w2,...,wt)也就是应该使用词序列在训练语料里出现的概率来进行计算的。
为什么第一个公式使用了次数(count)来代替概率(probability)来计算条件概率呢?
由于在实际应用中,直接计算词序列的联合概率 P ( w 1 , w 2 , . . . , w t ) P(w_1, w_2,...,w_{t}) P(w1,w2,...,wt) 通常不可行,因为它们涉及到整个词序列的概率计算,而这些概率往往非常小,容易导致数值下溢。因此,通常使用词频来近似这些概率。
根据大数定律,当语料库足够大时,词频可以近似为概率。因此,可以将上述公式中的概率用词频来替代:
接着,我们就可以对词序列
w
1
,
w
2
,
.
.
.
,
w
t
w_1, w_2,...,w_{t}
w1,w2,...,wt 的概率
P
(
w
1
,
w
2
,
.
.
.
,
w
t
)
P(w_1, w_2,...,w_{t})
P(w1,w2,...,wt) 进行计算了:
P
(
w
1
,
w
2
,
.
.
.
,
w
t
)
=
P
(
w
t
∣
w
1
,
w
2
,
.
.
.
,
w
t
−
1
)
⋅
P
(
w
t
−
1
∣
w
1
,
w
2
,
.
.
.
,
w
t
−
2
)
…
P
(
w
1
)
=
∏
t
−
1
i
=
1
P
(
w
i
∣
w
1
:
i
−
1
)
如果是 n = 2 的情况,也就是二元文法 bi-gram 模型的情况呢,第 n 个词只依赖于它前面的第 n-1 个词,也就是当前词语出现的概率只由上一个词决定。我们可以稍微简化一下上面的公式,就可以得到: P ( w 1 , w 2 , . . . , w t ) = ∏ i = 1 t − 1 P ( w i ∣ w i − 1 ) = ∏ i = 1 t − 1 C ( w i − 1 , w i ) C ( w i − 1 ) P(w_1, w_2,...,w_{t}) = \prod_{i=1}^{t-1} P(w_i|w_{i-1}) = \prod_{i=1}^{t-1}\frac{C(w_{i-1}, w_i)}{C(w_{i-1})} P(w1,w2,...,wt)=i=1∏t−1P(wi∣wi−1)=i=1∏t−1C(wi−1)C(wi−1,wi) 这就是二元文法 bi-gram 模型计算词序列出现概率的公式。
- 如果有词出现次数为了 0,这一串乘出来就是 0 了,咋办?
- 因为基于马尔科夫假设,所以 N 固定窗口取值,对长距离词依赖的情况会表现很差。
- 如果把 N 值取很大来解决长距离词依赖,则会导致严重的数据稀疏(零频太多了),参数规模也会急速爆炸(高维张量计算)。
文中提出了上面 3 个问题,这个三个问题其实是有某种递进关系的,归纳起来就是
首先我们可以观察上面的 n-gram 公式,如果连乘的概率中出现一个词序列的概率为 0 的话,就会导致整个结果都变为 0。这个问题要怎么尽可能避免了呢,通过减少 N 的取值?尽可能杜绝零频率词序列的出现?但是这会导致长距离词依赖(Long-Range Dependencies)变现变差的问题,如果为了解决长距离词依赖的问题,将 N 值取得很大呢,这又会导致参数爆炸和数据稀疏的问题(也就是回到了问题一,出现词序列零频的问题)。所以这三个问题就像是一个恶性循环,如果尝试解决一个问题,就会引入另一个问题。
所以我们要另辟蹊径,通过外部的手段解决零频的问题,而不是简单的通过调整 N 值的大小。
词序列零频的问题是普遍存在的:
为了解决零频的问题,我们引入是平滑(smoothing)的概念。
简单来说就是
bi-gram 的加 1 平滑公式: P ( w i ∣ w i − 1 ) = C ( w i − 1 , w i ) + 1 ∑ j = 1 V ( C ( w i − 1 , w j ) + 1 ) = C ( w i − 1 , w i ) + 1 C ( w i − 1 ) + V P(w_i|w_{i-1}) = \frac{C(w_{i-1}, w_i) +1}{ {\textstyle \sum_{j=1}^{V}} (C(w_{i-1}, w_j) + 1)} = \frac{C(w_{i-1}, w_i)+1}{C(w_{i-1}) + V} P(wi∣wi−1)=∑j=1V(C(wi−1,wj)+1)C(wi−1,wi)+1=C(wi−1)+VC(wi−1,wi)+1 其中:
其中 N N N 表示所有词的词频之和, V V V 表示词汇表的大小。
原文中提到上面这句话,这个 N N N 可能是指原文公式的求和的上标 n n n,表示 token 的数量,但是我觉得公式里求和的上标应该是词汇表(vocabulary)的大小。
uni-gram 的加 1 平滑公式是: P ( w i ) = C ( w i ) + 1 N + V P(w_i) = \frac{C(w_i) + 1}{N+V} P(wi)=N+VC(wi)+1这里涉及的 N N N 就是训练的 token 总数,即便是有重复的词语,都会被计算在内, V V V 是词汇表的大小,表示在训练数据中唯一的存在,相当于 set 处理之后的 token。
这是对加 1 平滑的优化,对于某些
bi-gram 的加 δ \delta δ 平滑公式: P ( w i ∣ w i − 1 ) = C ( w i − 1 , w i ) + δ ∑ j = 1 V ( C ( w i − 1 , w j ) + δ ) = C ( w i − 1 , w i ) + δ C ( w i − 1 ) + δ V P(w_i|w_{i-1}) = \frac{C(w_{i-1}, w_i) +\delta}{ {\textstyle \sum_{j=1}^{V}} (C(w_{i-1}, w_j) + \delta)} = \frac{C(w_{i-1}, w_i)+\delta}{C(w_{i-1}) + \delta V} P(wi∣wi−1)=∑j=1V(C(wi−1,wj)+δ)C(wi−1,wi)+δ=C(wi−1)+δVC(wi−1,wi)+δ 和加 1 平滑基本一样,就是将 1 换成了 δ \delta δ。
与加 1 平滑相比,加 δ \delta δ 平滑允许对于低频词的平滑程度进行更灵活的控制。当 δ \delta δ 较小时,对低频事件的平滑效果较小,而 δ \delta δ 较大时,平滑效果更加显著。这使得模型在平滑概率时能够更好地适应不同的数据特点。
δ \delta δ 是一个超参数,如果可以寻找到最优的值,就可以使得模型的效果更好,原文中说确定这个 δ \delta δ 值需要用到困惑度(Perplexity),但是其实个人觉得,只是一种比较间接的影响关系。
首先加 δ \delta δ 平滑操作的对象都是训练集,然后训练出一个语言模型,然后困惑度是用来评价语言模型在测试集上的性能的。通过调整 δ \delta δ 值,进行多次训练,使得语言模型在特定测试集的困惑度最小,这样便能得到最优的模型。
(在这里突然插入一个评价指标有点奇怪)
困惑度:评价一个概率模型预测测试集的能力,困惑度越低,表示模型越好。
下面是针对单一词序列 w 1 , w 2 , … , w N w_1, w_2, \dots ,w_N w1,w2,…,wN 的困惑度计算公式,并不是整个测试集的: Perplexity ( W ) = P ( w 1 , w 2 , … , w N ) − 1 N = 1 P ( w 1 , w 2 , … , w N ) N \text{Perplexity}(W) =P(w_1, w_2, \dots ,w_N)^{-\frac{1}{N} }= \sqrt[N]{\frac{1}{P(w_1, w_2, \dots, w_N)}} Perplexity(W)=P(w1,w2,…,wN)−N1=NP(w1,w2,…,wN)1 我们可以看到,公式里是将这段词序列的出现概率开了 N 次根,然后再求了倒数。
为什么要进行开 N 次方根的操作?
为了将不同长度的词序列归一化到一个统一的尺度来进行比较。我们将联合概率标准化为每个词的平均概率,即平均每个词的预测概率。这样,我们可以更好地比较模型在不同长度序列上的性能,而不受序列长度的影响。
我们将前面的联合概率的公式代入进去,就可以得到: Perplexity ( W ) = ∏ i = 1 N 1 P ( w i ∣ w 1 , w 2 , … , w i − 1 ) N \text{Perplexity}(W) = \sqrt[N]{\prod_{i=1}^{N} \frac{1}{P(w_i|w_1,w_2,\dots ,w_{i-1})} } Perplexity(W)=Ni=1∏NP(wi∣w1,w2,…,wi−1)1 也可以得到 bi-gram 的单一词序列困惑度计算公式: Perplexity ( W ) = ∏ i = 1 N 1 P ( w i ∣ w i − 1 ) N \text{Perplexity}(W) = \sqrt[N]{\prod_{i=1}^{N} \frac{1}{P(w_i| w_{i-1})} } Perplexity(W)=Ni=1∏NP(wi∣wi−1)1
在回退模型中,计算一个3-gram的概率时,如果这个3-gram在训练数据中存在,则直接使用其概率;如果不存在,则回退到2-gram,然后再回退到1-gram,依此类推。这样,即使某个组合在3-gram中没有观察到,仍然可以通过回退到2-gram等较低阶的模型来估计其概率。
这个感觉还是挺好理解的,一句话来说就是:退而求其次。
公式也挺好理解的:
P
(
w
i
∣
w
i
−
2
,
w
i
−
1
)
=
{
P
(
w
i
∣
w
i
−
2
,
w
i
−
1
)
C
(
w
i
−
2
,
w
i
−
1
,
w
i
)
>
0
P
(
w
i
∣
w
i
−
1
)
C
(
w
i
−
2
,
w
i
−
1
,
w
i
)
=
0
and
C
(
w
i
−
1
,
w
i
)
>
0
P(w_i|w_{i-2},w_{i-1}) =
在计算 n-gram 的概率的时候,也考虑 n-1-gram,甚至是 n-2-gram 的概率:
P
(
w
i
∣
w
i
−
2
,
w
i
−
1
)
=
λ
1
P
(
w
i
∣
w
i
−
2
,
w
i
−
1
)
+
λ
2
P
(
w
i
∣
w
i
−
1
)
+
λ
3
P
(
w
i
)
P(w_i|w_{i-2},w_{i-1}) = \lambda_1P(w_i|w_{i-2},w_{i-1}) + \lambda_2P(w_i|w_{i-1}) + \lambda_3P(w_i)
P(wi∣wi−2,wi−1)=λ1P(wi∣wi−2,wi−1)+λ2P(wi∣wi−1)+λ3P(wi)
感知器一般用于处理二分类问题:
y
=
{
1
,
if
w
x
+
b
>
0
0
,
othrewise
y =
上面的公式中 w x + b wx+b wx+b 的计算结果是需要映射到 0,1 两个结果中,是离散值,解决的是分类问题。但是如果我们直接将 w x + b wx+b wx+b 的计算结果拿过来使用,而不进行映射的话,结果将会是一个连续值,问题也会变成线性回归问题。
原本中有一个使用 pytorch 库中 nn.Linear 进行代码实操的代码片段,这里就不多说了。
如果说线性回归的结果是没有限制范围的,那么逻辑回归就会将计算结果限制在一个特定范围之内。
在线性回归的公式为: y = w x + b y = wx + b y=wx+b
那么在逻辑回归中: y = σ ( w x + b ) y = \sigma (wx + b) y=σ(wx+b)
激活函数会将线性回归 w x + b wx+b wx+b 的计算结果,再限制在一个确定的范围了。
上面这张图展示了逻辑回归的流程,已经有点神经网络的神经元的雏形了。
sigmoid 函数是我们在神经网络中常见到的激活函数,简单来说,当逻辑回归中的
σ
(
z
)
\sigma(z)
σ(z) 是 sigmoid 函数的时候,这个逻辑回归也叫做 sigmoid 回归。
y
=
Sigmoid
(
w
x
+
b
)
=
1
1
+
e
−
(
w
x
+
b
)
y = \text{Sigmoid} (wx + b) = \frac{1}{1+e^{-(wx+b)}}
y=Sigmoid(wx+b)=1+e−(wx+b)1 其中
Sigmoid
(
z
)
=
1
1
+
e
−
z
\text{Sigmoid}(z) = \frac{1}{1+e^{-z}}
Sigmoid(z)=1+e−z1,这个 sigmoid 函数会将结果值限制在
[
0
,
1
]
[0, 1]
[0,1] 之间。
一般是用在二分类问题中预测概率。
上面说到 sigmoid 回归主要是针对二分类的问题,现在要介绍的 softmax 回归主要就是针对多分类的问题。
如上图所示,一个输入 x x x,如果要进行 softmax 回归,进行类别为 K K K 的 多元分类。首先和 sigmoid 一样,需要经过各自类别的权重 w K w_K wK 和偏置 b K b_K bK 处理之后,得到每个类别的原始得分 z K z_K zK。然后一起送入到 softmax 函数内计算对应类别的概率。
这个公式就是计算输入在类别
i
i
i 的概率:
y
i
=
σ
(
z
)
i
=
e
z
i
∑
j
=
1
K
e
z
j
=
e
z
i
e
z
1
+
e
z
2
+
⋯
+
e
z
K
y_i = \sigma (\bold{z})_i = \frac{e^{z_i}}{\sum_{j=1}^{K} e^{z_j}} = \frac{e^{z_i}}{ e^{z_1} + e^{z_2} + \dots + e^{z_K} }
yi=σ(z)i=∑j=1Kezjezi=ez1+ez2+⋯+ezKezi其中有
z
K
=
w
K
x
+
b
K
z_K = w_K x + b_K
zK=wKx+bK。
一般来说,输入 x x x 其实也是一个维度为 K K K 的向量,如果不足的可以进行补零,超出的话就进行保留最主要的 K K K 个特征。这里我假设这个输入向量 x \bold{x} x 的所有维度的值都是 x x x。
MLP 是一种全连接的前馈神经网络,每一层的每个神经元都与上一层的所有神经元相连,有点全连接层的意思。
而且,MLP 中的激活函数必须得是非线性激活函数,如果是线性,不管是多少层,其实都只是一个简单的线性模型,只能解决简单的线性问题,没有非线性的学习能力。
因为全连接的关系,所以一个比较大的缺点就是:计算量非常大。
MLP 主要有输入层,隐藏层和输出层三个部分组成。
其中最重要的是隐藏层,加权求和,还有激活函数的操作都是在隐藏层进行的。
为了加深对 MLP 的理解,我自己画了一下 MLP 的一个简单的网络结构图。输入层和输出层没有像大多数图那样画成节点的形式,也将权重偏置显式地标注了出来。
图中的 w 0 , 00 w_{0, 00} w0,00 的下标逗号之前的 0 0 0 表示这个参数属于第 0 0 0 层隐藏层的。下标逗号后面的 00 00 00 表示这是输入参数 x 0 x_0 x0 与隐藏单元 h 0 , 0 h_{0, 0} h0,0 之间的参数。
如果将这个网络结构用向量公式表现出来就是下面的式子:
A
0
=
f
(
W
0
⋅
X
+
b
0
)
Y
=
f
(
W
1
⋅
A
0
+
b
1
)
最后得出来的结果也是我们非常熟悉的形式。然后和之前的逻辑回归类似,将这个加权求和得到的结果,送到激活函数中,进行非线性的变换,得到我们所需的下一个隐藏层的输入项 A 0 \mathbf{A_0} A0。
合并偏置常数项之后,我们的 MLP 结构图也可以画成上图的形式。
第二隐藏层的具体式子在这里就不列出来了。
这个原文中也没做过多的展开,我这里也留着
MLP 里每一层的每个元素,都要乘以一个独立参数的权重 W,再加上一个偏置 b,这样的神经网络层常被我们叫做「全连接层(Fully Connected Layer)或稠密层(Dence Layer)。但是这样有个显著问题:如果输入内容的局部重要信息只是发生轻微移动并没有丢失,在全连接层处理后,整个输出结果都会发生很大变化 —— 这不合理
即全连接层可能在处理输入时过于强调整体特征,而不足够关注输入中的局部结构信息。
所以为了让全连接层也能关注局部信息,是否可以设置一个滑动窗口来对局部输入进行处理。这就会 n-gram 设置 N 值的大小思路一样。这就引出了我们熟悉的 CNN 卷积神经网络,使用卷积核在原图上的滑动来提取图像的局部特征。
上面我画的示意图中,MLP 的输入数据是一维的,所以所有输入 MLP 进行全连接处理的数据都得先展平成一维的数据,而 CNN 的输入数据则是二维的,也就是可以。
其实某种意义上,可以将 MLP 理解成一个 1 × 1 1\times1 1×1 的卷积,但是和真正的 1 × 1 1\times1 1×1 卷积又有一些微秒的区别,在 CNN 中我们也常使用 1 × 1 1\times1 1×1 卷积来实现全连接结构,但是 1 × 1 1\times1 1×1 卷积的输入可以是二维的数据,而 MLP 还是只能输入一维的数据。
让我们修改一下上面的图,将输入数据变成我们熟悉的二维图像数据。
也就是输入和输出的维度是一样的。
LSTM 因为在图像处理中基本用不上,所以我比较陌生。是一种特殊的 RNN 网络。
我们通过 RNN 的结构就可以看出来,后向传播的时候,连续的乘法结构很容易造成梯度消失或者梯度爆炸的问题。在 CNN 的时候,我们会通过 dropout 对某些神经元进行舍弃,来进行正则化,缓解梯度消失和爆炸的问题。而 LSTM 中呢,我们也会设计一个模块对过往的信息进行舍弃,来缓解 RNN 中出现的梯度消失或者爆炸问题。
LSTM将长短期记忆文本管理问题分为两个子问题:
LSTM 引入了 “门(gate)” 的概念来控制信息的流向,可以分成 3 个门:
其实 LSTM 的结构和公式都不难理解,但是我希望在这里一步步从 RNN 推导到 LSTM,就有很多细节不清楚了,这里暂时也不过多的纠结了。
在经典的循环神经网络中,状态的传输是从前往后单向的。然而,在有些问题中,当前时刻的输出不仅和之前的状态有关系,也和之后的状态相关。这时就需要双向RNN(BiRNN)来解决这类问题。例如预测一个语句中缺失的单词不仅需要根据前文来判断,也需要根据后面的内容,这时双向RNN就可以发挥它的作用。
原文中给了一张简单的 bi-RNN 示意图,但是我感觉有点容易误解的地方。它只是对前向和反向的隐藏层权重
W
W
W 和
W
′
W'
W′ 进行了区分,但是没有区分
V
V
V 和
V
′
V'
V′ ,
U
U
U 和
U
′
U'
U′ 的权重。
一般计算的步骤是从前向后算一次,然后再从后向前计算一次。
原文虽然小标题中提到了双向 LSTM,但是实际没有具体的提到双向 LSTM 的,所以我这里也暂时略过。
就是一层的输出作为下一层的输入,多层堆叠
上图是一个抽象化的 Encoder-Decoder 模型示意图,相当于一个 N vs 1 和一个 1 vs M 的 RNN 网络的串联。一般我们也称之为 Seq2Seq 模型。
Encoder-Decoder 的结构有多种,但是大体的原理都是一样的。
仅管 Encoder-Decoder 模型可以实现序列到序列的任务,对于语音识别,翻译任务,问答任务的效果很不错,但是也存在一些问题:
其实从 4.7 就可以看出来,传统的 Encoder-Decoder 对于长序列的处理能力是很差的,因为不管序列多长,在经过编码器之后,都会压缩成固定尺寸的上下文向量(context vector),在这个压缩编码的过程中,很多长序列的信息就会丢失,导致后续解码部分也没办法获得全部的长序列信息。
举个简单的例子,如果长序列信息类似于山洞里的宝库,这时候一个盗贼进入了山洞,他只有一个箱子可用于搬运且只能搬运一次的话,可能是没办法将山洞内的财宝全部取走。想要实现利益最大化的话,自然是聚焦于高价值的宝物,也就是尽可能的用高价值的宝物填满箱子。
这就引出我们久有耳闻的 attention 机制。将注意力放到有价值的信息上面。
在传统的 Encoder-Decoder 模型里引入 attention 模块。
从Encoder-Decoder(Seq2Seq)理解Attention的本质 :这篇文章挺不错的。
从上面的 Encoder-Decoder 示意图,我们可以知道上下文向量(context vector)可以通过下式:
C
=
f
(
x
1
,
x
2
,
…
,
x
n
)
C = f(x_1, x_2, \dots ,x_n)
C=f(x1,x2,…,xn) 计算得到,而想要对于最后的输出结果
y
i
y_i
yi,我们可以通过:
y
k
=
g
(
C
,
y
1
,
y
2
,
…
,
y
k
−
1
)
y_k = g(C, y_1, y_2, \dots, y_{k-1})
yk=g(C,y1,y2,…,yk−1)但是我们也可以看出来,不管是计算
y
1
y_1
y1,
y
2
y_2
y2 还是
y
3
y_3
y3,我们都是通过
C
C
C 来计算的,也就是说 Encoder 中任意单词对于 Decoder 的输出的影响都是一样的,不存在注意力机制的。
如果加入了 attention 模块,我们就可以看出来,对于不同的输出
y
i
y_i
yi,我们有着不同的
C
i
C_i
Ci,则对应的输出:
y
k
=
g
(
C
k
,
y
1
,
y
2
,
…
,
y
k
−
1
)
y_k = g(C_k, y_1, y_2, \dots, y_{k-1})
yk=g(Ck,y1,y2,…,yk−1)
但是问题来了,这个
C
k
C_k
Ck 是怎么计算出来的?
也就是说,对于不同的 C k C_k Ck,attention 模块会给出不同的注意力向量 a k \mathbf{a^k} ak 来进行计算,上面的式子中, n n n 为输入序列的长度,而 k k k 为输出序列的长度。
a i k a_{ik} aik 就是 attention 注意力权重,表示第 i i i 个输入在第 k k k 个输出上分配的注意力。数值越高表示第 j j j 个输出受到第 i i i 个输入的影响越大。
如果将他们看成一个矩阵运算的话,则可以写成下面的形式:
[
h
1
h
2
⋯
h
i
h
1
h
2
⋯
h
i
⋮
⋮
⋱
⋮
h
1
h
2
⋯
h
i
]
⋅
[
a
1
,
1
a
1
,
2
⋯
a
1
,
k
a
2
,
1
a
1
,
2
⋯
a
2
,
k
⋮
⋮
⋱
⋮
a
i
,
1
a
i
,
2
⋯
a
i
,
k
]
=
[
C
1
,
C
2
,
⋯
,
C
k
]
但是问题又来了,
a
i
k
a_{ik}
aik 是怎么计算获得的?
在 attention 模块中,注意力评价值
e
i
k
e_{ik}
eik 是由两部分组成的:
也就是说这个注意力的值其实既和当前输入有关,也和前一时刻的输出相关。
公式可以写成: e i k = α ( h i , s k − 1 ) e_{ik} = \alpha(h_i, s_{k-1}) eik=α(hi,sk−1)我们这里的 attention 机制是 soft attention,所以和 softmax 类似,我们要对上面这个注意力评价值进行加权平均: a i k = exp ( e i k ) ∑ i = 1 n exp ( e i k ) a_{ik} = \frac{\exp(e_{ik})}{\sum_{i=1}^n \exp(e_{ik})} aik=∑i=1nexp(eik)exp(eik)其中:
感觉有种当前时刻输入,在总输入中的贡献占比的感觉。
为什么要使用 exp 来计算 attention 权重呢?
最后我们列出计算输出 y i y_i yi 的完整流程:
下面的章节就正式的进入到 transformer 的邻域了。
参考文章:The Illustrated Transformer
这是我参考文章内的图片自己做的一个示意图。通过这个示意图,我来大概地讲解一下 self attention 的计算流程:
为什么要除以 d k \sqrt{d_k} dk ?
d k d_k dk 表示的是 key 矩阵的中 k i k_i ki 的维度,除以 d k \sqrt{d_k} dk 是为了:
- 防止 Q ⋅ K T \mathbf{Q \cdot K^T} Q⋅KT 的值过大,导致 softmax 计算的时候出现上溢出(overflow)。
- 使得 Q ⋅ K T \mathbf{Q \cdot K^T} Q⋅KT 的结果满足期望为 0,方差为 1 的分布。
为什么要计算 Q ⋅ K T \mathbf{Q \cdot K^T} Q⋅KT ?
计算 Q ⋅ K T \mathbf{Q \cdot K^T} Q⋅KT 时,从几何的角度来说,相当于对内部的向量 q \mathbf{q} q 和 k \mathbf{k} k 进行点积操作,来计算两个向量的相似度。
q ⋅ k = ∥ q ∥ ∥ k ∥ ⋅ cos θ \mathbf{q \cdot k} =\left \| \mathbf{q} \right \| \left \| \mathbf{k} \right \| \cdot \cos \theta q⋅k=∥q∥∥k∥⋅cosθ
- 当两个向量垂直的时候, θ = 9 0 ∘ \theta = 90^{\circ} θ=90∘,就是点积为 0,表示这两个向量没有相似的地方。
- 当两个向量平行的时候,两个向量的点积即为 ∥ q ∥ ∥ k ∥ \left \| \mathbf{q} \right \| \left \| \mathbf{k} \right \| ∥q∥∥k∥
- 当两个向量存在夹角 θ \theta θ,两个向量的点积就如上面公式一样。点积就是 q \mathbf{q} q 在 k \mathbf{k} k 上投影的模。
这里 self attention 的 Z \mathbf{Z} Z 和 soft attention 中的 C \mathbf{C} C 是等效的。
多头注意力,其实相当于多次求 self attention,上面的 self attention 的产物是一个 Z \mathbf{Z} Z 矩阵,在多头注意力里,我们通过多组的 W Q \mathbf{W^Q} WQ, W K \mathbf{W^K} WK 和 W V \mathbf{W^V} WV,来获得多组的 Z \mathbf{Z} Z。
我们将上面 self attention 中计算 Z \mathbf{Z} Z 的过程简化一下,直接得到对应的 Z 0 , Z 1 , ⋯ , Z n \mathbf{Z_0, Z_1, \cdots, Z_n} Z0,Z1,⋯,Zn。然后将其组合起来,与一个新设置的权重矩阵 W o \mathbf{W^o} Wo 相乘,获得最终的 Z \mathbf{Z} Z。
多头注意力中的每个头都能关注不同的信息。
ResNet 残差网络在 CV 中也经常用到,所及就不详细解说了。
在计算 attention 的时候,我们只是关注词与词之间的相似关系,但是却忽略了位置关系。
在 Transformer 中,为了解决这个问题,新增了位置编码(Positional Encoding)。
大致的流程如上图所示,就是在 embedding 和计算
Q
\mathbf{Q}
Q,
K
\mathbf{K}
K 和
V
\mathbf{V}
V 之间加入一个位置编码的流程,来将每个词或者说 token 的位置信息加入到输入向量
x
\mathbf{x}
x 中。
矩阵
P
E
\mathbf{PE}
PE 中的向量
p
e
\mathbf{pe}
pe 内的元素具体是怎么计算的呢?
p
e
t
,
i
=
{
sin
(
1
1000
0
2
i
d
x
⋅
t
)
if
i
=
2
k
cos
(
1
1000
0
2
i
d
x
⋅
t
)
if
i
=
2
k
+
1
for
k
=
0
,
1
,
2
,
⋯
,
d
x
2
−
1
pe_{t,i} =
如果
i
i
i 是偶数的话,就是计算
sin
\sin
sin,如果
i
i
i 是奇数的话就是计算
cos
\cos
cos。一个完整的
p
e
\mathbf{pe}
pe 向量可以写成下面形式:
p
e
t
=
[
p
e
t
,
0
,
p
e
t
,
1
,
⋯
,
p
e
t
,
d
x
]
\mathbf{pe}_t = [pe_{t,0}, pe_{t,1}, \cdots, pe_{t,d_x}]
pet=[pet,0,pet,1,⋯,pet,dx]而一个完整的位置编码矩阵可以写成下面形式:
P
E
=
[
p
e
0
p
e
1
⋮
p
e
t
]
=
[
p
e
0
,
0
p
e
0
,
1
⋯
p
e
0
,
d
x
p
e
1
,
0
p
e
1
,
1
⋯
p
e
1
,
d
x
⋮
⋮
⋱
⋮
p
e
t
,
0
p
e
t
,
1
⋯
p
e
t
,
d
x
]
\mathbf{PE}=
上面介绍的这种,是绝对编码中的三角式位置编码(Sinusoidal Positional Encoding),除此之外还有相对位置编码。
除了上面提到的三角式位置编码,还有下面几种绝对位置编码:
感觉暂时也不需要详细展开了,后续有时间的话再写吧。
沿用之前的参数符号,上图是 Transformer 的 Encoder 示意图,可以看出来:
其中可以看到 Encoder 中存在两条 short-cut。
接下来看看 Decoder 的结构,除了一个新的 masked multi-head attention 模块,其他模块都和 Encoder 的类似,所以具体的计算公式就不标注出来了。
我们说一下 masked multi-head attention 模块,这个 masked 的对象是什么,具体有什么用?
我们这里以单个注意力模块,也就是 self attention 来举例子,其实 masked 的操作主要是加在注意力权值矩阵进行 softmax 操作之前。
这是我从 self attention 示意图中抽取的注意力权值矩阵计算部分,左边是普通的计算注意力权值矩阵,在求解
A
′
\mathbf{A'}
A′ 之后,直接对其进行 softmax 操作就行了。
而右边则是包含 mask 操作的,在进行 softmax 操作之前,先进行一个 mask 操作,防止后续标签进行对前面的预测进行影响,因为我们知道 Transformer 中 Decoder 也会输入一次输入序列,但是和 Encoder 部分不同的是,会对输入数列进行一个右移(shifted right)的操作。类似 Encoder 如果输入 [i am a student],那么 Decoder 部分,则会输入 [ I am a student],多了一个起始符,这个起始符就是用于预测 “I” 的。
这个注意力模块也可以叫做 cross attention 模块,或者是 encoder-decoder attention 模块
第二个多头注意力模块,其实我们注意到,有两条线是来自于 Encoder,我们知道在计算 self attention 的时候,首先我们要计算 Q \mathbf{Q} Q, K \mathbf{K} K 和 V \mathbf{V} V 三个矩阵,一般来说,这三个矩阵都是一个输入和对应的权重矩阵计算获得的。
但是在 Decoder 的第二个多头注意力模块中,其中 K \mathbf{K} K 和 V \mathbf{V} V 是由 Encoder 的输出 X out 2 \mathbf{X}_{\text{out}2} Xout2 计算获得的,而 Q \mathbf{Q} Q 是由 Decoder 的第一个 masked multi-head attention 模块的输出计算获得的。
我理解为 Transformer 是由很多个 Decoder block 组成的,但是第一个 Decoder block 和后续的 Decoder block 有一点细微的差别
假设 Transformer 一共有
N
N
N 个 Decoder block,第一个 Decoder block 是不含 masked multi-head attention 模块的。后续的
N
−
1
N-1
N−1 个 Decoder block 才是我们上面分析的正常的 Decoder block,这是为什么?
为什么 transformer的第一个 decoder blcok 不需要经过 masked multi-head attention 模块?
在 Transformer 模型中,第一个 Decoder block 的出结果是基于 Encoder 的输出和 Decoder 的初始输入(通常是起始符号,如)生成的。这个过程不需要经过 Masked Multi-Head Attention Layer,因为即便是经过了多头注意力模块,
没看懂
这个部分其实和 cv 中的分类最后是一样的,只是 transformer 中的类别是词汇表。
- 首先输入数据生成词的嵌入式向量表示(Embedding),生成位置编码(Positional Encoding,简称 PE)。进入
- Encoders 部分。先进入多头注意力层(Multi-Head Attention),是自注意力处理,然后进入全连接层(又叫前馈神经网络层),每层都有 ResNet、Add & Norm。
- 每一个Encoder 的输入,都来自前一个 Encoder 的输出,但是第一个 Encoder 的输入就是 Embedding + PE。
- 进入 Decoders 部分。先进入第一个多头注意力层(是 Masked 自注意力层),再进入第二个多头注意力层(是 Encoder-Decoder 注意力层),每层都有 ResNet、Add & Norm。
- 每一个 Decoder 都有两部分输入。
- Decoder 的第一层(Maksed 多头自注意力层)的输入,都来自前一个 Decoder 的输出,但是第一个 Decoder 是不经过第一层的(因为经过算出来也是 0)
- Decoder 的第二层(Encoder-Decoder 注意力层)的输入,Q 都来自该 Decoder 的第一层,且每个 Decoder 的这一层的 K、V 都是一样的,均来自最后一个 Encoder。* 最后经过 Linear、Softmax 归一化。
后面的部分基本都是实际训练部分了。
参考文章:举个例子讲下transformer的输入输出细节及其他
但是感觉 transformer decoder 的输入输出维度还是不是很清晰
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。