# transformer
# 原理
Transformer 从零详细解读 (可能是你见过最通俗易懂的讲解)_哔哩哔哩_bilibili
6 个结构完全相同的 encoder,6 个结构完全相同的 decoder,数量可以自定义。结构上相同,但是参数上是不同的,训练是各自训练自己的参数,不是只训练一个然后 copy。后面 Albert 通过共享 transformer 某些层的参数达到减少 bert 参数量的目的。
在上图中,左边是 encoder,右边是 decoder。encoder 和 decoder 的结构不同。同时注意到有两个输入,下边左右分别是编码段和解码段的输入,最上面有一个输出
encoder 的组成部分
使用 word2vec
RNN 的参数:
- u:输入参数
- w:隐层参数
- v:输出参数
对于 RNN 的所有 timestep,都共享上面的一套参数。从上右图也可以看出,V、W、U 是没有下标的。
pos 是单词或字的位置、在偶数(2i)的位置使用 sin、在奇数(2i+1)的位置使用 cos
下面的 embedding 和位置编码进行相加,得到最终的输入
softmax 之后得到的是一个向量,乘以 V 这个矩阵后得到的是加权的和(结果仍为向量吧)
Q 是单词对应的某种向量、四个区域对应四个 K 向量、四个 V 也是四个区域对应的值向量。Q 分别和四个 K 进行点乘(点乘结果越大,说明越靠近,相似度越大,越关注)
QK 相乘的值很大,softmax 在反向传播的时候梯度很小,容易造成梯度的消失。除以根号 dk 是为了控制方差为 1,
旁边的 W 矩阵应该是模型训练出来的超参数。
多头注意力机制即使用了多套参数
在上图中,连乘和一个 1 相加,可以避免了梯度消失
BN 针对整个 batch 中的样本在同一维度的特征做处理
下面的图中,每一竖(条)代表一个样本,每一行代表一个特征,如身高、体重等
上面是因为 BN 是使用整个 batch 中的样本的均值和方差来模拟全部数据的均值和方差
比如下面的样本,batch_size 为 10。9 个样本,句子长度为 5 个单词;1 个样本,句子长度为 10 个单词。前 5 个单词的均值和方差可以用 10 个样本算出来;但是第 6 到第 20 个单词就没法了。 ==》BN 效果不好的原因:RNN 的输入是动态的
如果没有 mask,就像下图那样
因为在预测阶段,是没有后面的信息的,模型看不到未来的单词
最后的 encoder 的输出和每一个 decoder 做交互
encoder 生成 K、V 矩阵,decoder 生成 Q 矩阵
下图中,虚线代表 KV 矩阵的输出
# 代码
右上角绿色的框框不是解码端的输出,而是解码端的真实标签。在 decoder 输出之后,会去和真实标签计算损失
解码端是不能并行的(看上图右边的输入和输出),当输入为 S 的时候,输出为 I,然后将 I 作为下一刻的输入。不能并行的原因是因为下一时刻的输入依赖于上一时刻的输出。但为了加快训练速度和收敛速度,进行 teacher force 操作,把真实标签作为一种输入,一起输入进去,但这样会看到所有的信息,所以要使用 mask 标志,把当前单词后面的单词给 mask 住。
训练和预测的时候形式是一致的。
在模型的运行中,是先运行完解码器,然后将解码器的输出作为编码器的输入,再运行编码器的。
S 为 start,E 为 end,P 为 pad(填充)
batch_size 为 4 时,下面的每一行句子表示每一组句子中的第一条
大于 8 的部分截断,小于 8 的部分使用特殊字符 PAD 来填充
在后续的注意力层中,P 置为负无穷,让它不能对其他的字符产生效果。
在 Q K 相乘除以根号 dk 后,在 softmax 之前得到的矩阵,可以大致理解成每个单词对其他单词的相似性
每一横行做 softmax,得到这 4 个字符对竖行这个字符(如” 卷 “)哪个更重要
将符号矩阵传到后面的层或模型中,知道了 pad 的位置后,就可以消掉 pad 的影响了。
词嵌入后 batch_size=1,len=1,d_model=4,然后生成三个参数矩阵 WQ、WK、WV,三个参数矩阵和两个输入分别做计算,生成对应的 q、k、v 向量,
在做自注意力层的时候要做两个 mask,一个是 pad 符号,另一个是当前单词之后看不到的单词的 mask
在做交互注意力机制的时候,只需要对编码 pad 做 mask,
# 外加的自己的理解
多头注意力机制层的输入是 Q、K、V,
# Encoder 部分
EncoderLayer 包含两个部分:多头注意力机制和前馈神经网络,而且,主要的输入和输出都是 [batch_size, src_len, d_model]
,
在 MultiHeadAttention 里,输入三个相同的 [batch_size, src_len, d_model]
,就是上一步的输出或者第一次加上位置编码之后的词向量,再用线性层进行分头产生 Q、K、V,大小分别是 Q: [batch_size, n_heads, len_q, d_k]、K: [batch_size, n_heads, len_k, d_k]、V: [batch_size, n_heads, len_v (=len_k), d_v],然后利用公式对每个头进行计算,返回的结果是那个公式的计算结果,和没有乘以 V 的中间结果,进行一层全连接层将多头变为一个,输出结果为 output:[batch_size x len_q x d_model]
上面是以一个 batch_size 成块进行处理的,而在图示中是一个词向量的形式进行处理的,Wq、Wk、Wv 矩阵应该是全连接层中的那些参数
# Decoder 部分
在 mask 的时候使用的是上三角矩阵,说明主要的注意点是竖排的这些,
在 Q 和 K 矩阵相乘之后再根据符号矩阵将相应的值置为无穷小,
并行化是通过矩阵同时处理来实现的,
Q 和 K 矩阵相乘后得到的矩阵维度为 [batch_size, n_heads, len_q, len_k],关注后面两个,根据 mask 矩阵,对其的理解是竖的考虑了横的的哪些信息,如果横的中的哪些单元格被 mask 掉了,则说明不考虑这些信息。
在交互注意力层,Q 来自解码端,K 来自编码端,V 来自编码端。此处的 mask 也是只需要考虑 K 的 pad 符号的影响。
在编码端的自注意层的才需要考虑自我 mask,