参考了很多大神@DASOU_NLP从入门到放弃 @台大李宏毅 @霹雳吧啦Wz @跟李沐学AI的讲解,记录一下自己的学习历程。
目录
5.1 Masked muti-head attention
1 Transformer总体架构
Transformer是Google在2017年提出的一种全新的神经网络结构,主要用于序列学习任务,如机器翻译、文本摘要等。它通过自注意力机制来建模序列元素之间的依赖关系,实现序列到序列的建模。
《 Attention is all you need》本篇主要应用在NLP任务里,我们以机器翻译为例,Transformer做的事情就是,输入一句话,再把它的翻译结果输出出来。Transformer相当于黑箱子,它主要分为encoder和decoder两部分。论文里的encoder和decoder分别为6层,这里的编码层和解码层架构相同但是参数不一样!且并不是只训练一个encoder,剩下的5层都是复制来的,它是一起训练的
2 Encoder层
Encoder编码器,主要用于对输入序列进行特征提取与编码。包含多层Encoder Layer,多个Encoder Layer是同时训练的,它们的参数是独立的,但输出是通过残差连接和层归一化相加得到的,每个Encoder Layer又包含两个子层
- Self-Attention:自注意力子层,利用多头注意力机制来建模序列内部的依赖关系,获得序列的上下文表征。输出为Attention Outputs
- Feed Forward:前馈子层,包含两个线性变换与ReLU激活函数,用来增强Self-Attention的表征,输出为FFN Outputs
然后,这两个子层的输出通过残差连接和层归一化相加,作为Encoder Layer的最终输出:
EncoderLayer Output = LayerNorm(Attention Outputs + FFN Outputs)
所有的Encoder Layer输出也按照同样方式相加,作为Encoder的最终输出:
Encoder Output = EncoderLayer1 Output
+ EncoderLayer2 Output
+ ...
+ EncoderLayerN Output
在训练过程中,每个Encoder Layer都有自己的训练参数,但它们最终的输出是相加的。这样的设计有以下好处:
- 每个Encoder Layer可以建模输入序列的不同特征与模式。不同层的Self-Attention会聚焦在不同的依赖关系上。这增强了模型的表达能力。
- 通过残差连接,每个Encoder Layer只需要学习输入与其输出的差异部分。这缓解了网络加深时的退化问题,使得模型可以达到更深的层数。
- 层归一化可以更好地 propagating each Encoder Layer的梯度,使其可以并行优化。这提高了训练的效率。
- 在推理时,直接将所有Encoder Layer的输出相加可以得到序列的最后表征。这简化了模型的部署过程。
3 输入部分(Embedding层+位置编码)
Transformer输入的信息可以是一段音频、一个图网络、一句话,输出的信息也可以分为两类
- 输入的每个向量都有标签或者数值
- 输入的向量作为一个整体进行分析:
RNN是非常典型的循环神经网络,它在处理nlp任务时面对输入的句子信息,自然而然的包含时序信息,transformer抛弃了神经网络结构,改用self-attention,在可以并行同时也丢失了词序信息,我们就需要将包含位置编码的数据进行输入。
one-hot也是可以的,每一个维度代表一个数据,当数据量比较小的时候也可以用,但是数据量比较大的时候就不是很适合了,假如10w个数据,总不能给它一个10w的向量矩阵吧,且这个矩阵里根本看不出来语义信息,字向量之间是没有关联的。用Embedding可以将相似的点在空间里更聚集,语义相关的词拥有接近的向量表示。
输入部分整体就是对某个词向量都在512维的空间里进行拼接,使它作为整体的输入
input = input_embedding + positional_embedding
位置编码:位置编码(Position Encoding)是一种特殊的Embedding方式,它为序列中的每个元素赋予一个唯一的向量表示,用以表示该元素在序列中的位置信息,transformer论文里采用正余弦编码,这样的编码既可以蕴含绝对位置信息也包含相对位置信息。
- 用于提供绝对位置信息,表示每个token在整个序列中的相对顺序。这是Transformer缺失的位置敏感性。
- 通常是模型参数的函数,如正弦函数PE(pos,i)=sin(pos/10000^(2i/d_model))。因此不需要训练,并且对所有序列都适用。
- 随着维度的增加,每个位置的编码也在变化,这使得模型可以利用不同的维度对不同的位置进行编码,获知丰富的位置信息。
- 通过与Embedding的相加,将位置信息融入每个token的表达中。这使得模型既具有语义信息也具有位置信息,可以建立语义与位置的关系。
Embedding:Embedding是机器学习中的一个概念,指将高维稀疏的数据映射到低维空间的过程。它的主要目的是找到数据中潜在的低维结构,使得相似的数据点在低维空间中更加聚集。这可以用于数据的可视化、降维与特征学习
- 用于提供语义信息,将输入的one-hot词向量映射为语义丰富的dense词向量。这降低了输入的维度,并编码了词与词之间的语义依赖关系。
- 是模型训练的参数矩阵E,需要经过大量数据进行学习与优化。不同的矩阵会对不同的输入进行不同的映射。
- 仅根据输入字典的大小来确定行数,忽略了位置信息。这使得Embedding层本身是位置无关的。
- Embedding矩阵中的每一行都对应一个输入token的词向量。通过查表的方式,根据输入的one-hot ID查找对应的词向量。
- Embedding可以通过矩阵的学习,自动获得数据集的语义信息与规律,这减轻了模型的难度且增强了泛化性。但这也要求有足够的数据进行有效学习。
综上,位置编码为Transformer模型提供了位置信息,使其能够建模输入序列的顺序关系。Embedding为Transformer模型提供了语义信息,增强其对词与词之间关系的理解。
Transformer模型使用嵌入层(Embedding)将词转换为向量表示。其主要步骤如下:
- 构建词表:收集训练数据中的所有词,并按出现频率排序,选取排名较高的N个词构建词表。N的选择需要权衡模型的覆盖率与参数量。Transformer中常用的词表大小为30000~50000。
- 初始化词向量:为词表中的每个词随机初始化一个实值向量。通常初始化为[0,1]区间内的实数,使初始词向量接近于0均值。
- 加入位置编码:为输入序列中的每个词加入一个位置编码向量。这使模型对词的位置信息具有感知,可以进行序列建模。位置编码一般通过正弦函数与余弦函数进行映射。
- 输入Embedding:将输入序列中的词索引通过Embedding层转换为对应的词向量。同时加上位置编码向量,得到Embedding层的输出结果。
4 注意力机制
4.1 自注意力机制
针对一个sequence,我们不仅要看到当前词向量,也希望看到上下文的语义信息,怎么做呢?
可以把这个窗口拉大一点,如下:
但是输入的sequence很长呢?窗口无限增大参数量和计算量无疑也会变大
芜湖~自注意力机制来咯~
我们希望不仅可以看到上下文信息,但是不用那么大的参数量:
- self-attention:关注全局信息
- FC层:关注某个具体位置的信息
那么self-attention是怎么做的呢?
a1,a2,a3,a4是输入信息(事实上它们作为sequence里的数据,都是并行化处理的,并没有位置信息,通过上面的embedding加了位置编码之后才会有),b1,b2,b3,b4是考虑了整个所有a的sequence 才输出的结果,4个a所以会对应产生4个b,且b1,b2,b3,b4是同时得出的。
以b1为例,用α表示整个sequence中与a1关联程度,通常用Dot-product和Additive两种方法,如下图:Dot-product是输入两个向量,分别乘以两个不同的W矩阵,得到的结果相乘,即为α值,Transformer就是用的这个方法。
计算α值,并经过softmax做归一化处理,得到最终的结果。通过数值就可以看出向量是a1的相似度。(也可以不用softmax,也有人尝试relu6,得到了更好的效果,炼丹侠名不虚传~)
通过得到的α'找到最相似的a,在整个sequence里抽取相关信息,如何抽取呢?
v1,v2,v3,v4分别来自于a1,a2,a3,a4,将其乘以Wv矩阵,再乘以α'得到b值
so如果两个值关联性很强,比如a1与a2,那么他们的α'就会很大,得到的b值就会比较接近~
(我觉得:上面用soft-max就是要对结果进行归一化处理,好加权~)
(我还觉得:是因为qkv都是来自自己,所以叫自注意力)
下面就是矩阵角度再解释了一遍:
总结起来就是下面这张图片,整个训练过程中只有红框里的参数是需要学习的~
Encoder的训练步骤主要包括:
- 定义Encoder结构:Encoder由多个相同的Layer堆叠构成,每个Layer包含一个Multi-Head Attention层和一个Position-wise Feed-Forward层。
- 制作训练数据:源语言序列的词表编码作为Encoder的输入,每个位置的词ID构成一个向量作为该位置的表达。同时生成该序列的Position Encoding,为每个位置提供绝对位置信息。
- 迭代训练Encoder参数:(1)输入源语言序列的表达,并添加Position Encoding,得到Encoder的输入。 (2)Encoder的第一个Layer使用输入序列作为Query,Key和Value,计算Attention权重,得到Attention层的输出。(3)Attention层的输出传入Feed Forward层,进行非线性变换,得到Encoder第一层的输出。(4)Encoder第二层使用上一层的输出作为输入,重复(2)-(3)步骤,计算Attention与得到Feed Forward层输出。(5)Encoder重复(4)步骤,逐层堆叠直到最后一层。最后一层的输出作为Encoder的输出表达。(6)Encoder的输出传入Decoder,用于计算损失与更新参数。基于该损失,Encoder的各层参数通过反向传播进行更新。 (7)重复(1)-(6)步骤,迭代更新Encoder的各层参数直到损失收敛。
- Encoder表达的提取:在推理时,输入源语言序列到Encoder中,输出最后一层的输出作为序列的表达。这种表达可以用于各下游任务,如机器翻译、摘要、分类等。
综上,Encoder的训练主要是定义其结构,构造训练数据,并迭代更新其Attention层与Feed Forward层的参数。在推理时可以提取输入序列的表达,这段表达捕捉了序列的全局语义与语法,是完成各下游任务的关键信息。
4.2 多头注意力机制
多头实际上是希望捕获不同维度的特征信息
多头注意力机制在翻译和语音辨识等方面比较常用,因为对于一个输入向量,计算机需要注意不同的信息,多个q负责不同种类的相关性。计算方式和步骤和自注意力机制是一样的,需要注意的是如上图箭头标注的,a1产生的第一个q,k,v(第二层的那12个)只与a2产生的第一个q,k,v作相关运算。
4.3 Self-attention VS RNN
- Self-attention可以并行化处理,Rnn只能按顺序执行;
- 因此如果网络较深的话,前面的信息可能会丢失;
- Self-attention可以考虑全部的输入,而RNN似乎只能考虑之前的输入(左边)。(但是当使用双向RNN的时候可以避免这一问题)
4.4 Self-attention VS CNN
5 Decoder层
Decoder的训练步骤主要包括:
- 定义Decoder结构:Decoder由多个相同的Layer堆叠构成,每个Layer包含两个Multi-Head Attention层和一个Position-wise Feed-Forward层。
- 制作训练数据:源语言序列的编码通过Encoder处理得到的中间表达作为Decoder第一个Attention层的Key和Value。目标语言序列与源语言序列对齐,每个位置的目标词作为Decoder输出的教师强制标签。
- 计算Attention Mask:对Decoder的第一个Attention层构造掩码矩阵,屏蔽Encoder表达中后续位置的信息。这个掩码在训练与推理过程中保持不变。
- 迭代训练Decoder参数
- beam search解码
综上,Decoder的训练主要通过迭代更新其Attention层和Feed Forward层的参数,并在推理时采取beam search策略进行解码。Attention Mask确保其第一个Attention层不会考虑后续位置的信息。
5.1 Masked muti-head attention
因为多头注意力机制是并行处理的,就是所有的输入计算机都是可以看到了,如果训练的时候也采用这样子的方法,所有的输入信息都对当前节点的输出提供了信息,训练结果很好,但是预测的时候可能效果不佳(就相当于平时抄作业但是考试的时候啥也不会)
比如上图,我们希望you是它自己根据前面的信息自己训练出来的,不是看了全部的信息之后才得到的。解码端预测的时候是一个个解码,看不到当前单词之后的信息,所以在训练的时候需要把这个信息抹掉,保证一致性。
(就是训练的时候把“答案”遮住,不然知道答案训练,然后去预测结果会很差)
5.2 交互层
所有Encoder的输出和Decoder的每一层进行交互
迭代训练Decoder参数:
- Decoder的第一个Attention层使用Encoder的表达作为Key和Value,计算Attention权重。该层的输出作为第二个Attention层的Query输入。
- Decoder的第二个Attention层使用上一层Attention的输出作为Key和Value,计算Attention权重。该层的输出与目标输出词进行比较,计算交叉熵损失。
- 基于该损失, Decoder的参数通过反向传播进行更新。
- Decoder的Feed Forward层对Attention层的输出进行变换,其输出作为Decoder下一层的输入。
- 重复上述步骤,逐层训练Decoder的参数,直到损失收敛。
beam search解码:在推理时,Decoder逐层解码以生成最终的序列输出。具体步骤如下:
- Encoder的输出作为Decoder第一层Attention的Key和Value,计算Attention权重,得到输出。
- Decoder第一层的输出与所有可能的目标词比较,选取得分最高的前k个词作为第二层Attention的Query输入。
- Decoder第二层Attention使用上一层Attention的输出作为Key和Value,计算k个Query的Attention权重与输出。
- 重复上述步骤直到解码出完整的目标序列。
- 从k个解码序列中选择得分最高的作为最终输出。
6 一些补充知识:
6.1 残差结构
残差结构(Residual Connection)是Transformer模型中用于加速训练与提高模型深度的一种关键技术。它将深层网络的中间层输出与相对应的输入进行相加,然后将结果作为最终的输出。
梯度消失一般情况下是因为连乘(但是对于RNN有一点小小不一样的地方,RNN梯度消失是远距离梯度被近距离梯度主导,RNN中的总梯度不会消失,而是远距离输出对应的梯度更容易消失,从而导致模型难以学到远距离的依赖关系。)
但是对上面的输出进行链式求导,因为有1的存在,不管连乘再多,都不会发生梯度消失。
这也是为什么,NLP任务里,网络可以变得比较深的原因,缓解梯度消失
6.2 batch norm
对batch的样本,在同一维度做处理(针对同意特征)
为什么不用batch norm,而用layer norm?
bn优点:
- 可以解决内部协变量偏移
- 缓解了梯度饱和问题(如果使用sigmoid激活函数的话),加快收敛
bn缺点:
- batch_size较小的时候,效果差。(因为它是用batch样本模拟所有的样本的均值和方差,如果batch-size比较小的话,差异就会很大,影响结果)
- BN 在RNN中效果比较差(Rnn的输入是动态的,不能有效的得到整个样本的均值和方差)
比如上面的例子,10个样本,前9个样本都是5个单词长度,第10个单词长度为20,那么前5个单词的均值和方差容易得到,6-20的方差怎么得到呢,如果只取第10个样本,那么又回到了它的第一个缺点,batch_size较小
6.3 layer norm
LayerNorm单独对一个样本的所有单词做缩放可以起到效果。
参考链接:
1.【李宏毅机器学习2021】自注意力机制 (Self-attention) (下)_哔哩哔哩_bilibili
3.Transformer论文逐段精读【论文精读】_哔哩哔哩_bilibili
4.Transformer中Self-Attention以及Multi-Head Attention详解_哔哩哔哩_bilibili