深入理解Transformer的笔记记录(精简版本)----Seq2Seq → Seq2Seq with Attention
只要是符合类似的框架,都可以统称为 Encoder-Decoder 模型。
1、RNN
RNN引入了隐状态h(hidden state)的概念,隐状态h可以对序列形的数据提取特征,接着再转换为输出。
x1,x2,x3,x4如:
- 自然语言处理问题。x1可以看做是第一个单词,x2可以看做是第二个单词,依次类推
- 语音处理。此时,x1、x2、x3……是每帧的声音信号。
- 时间序列问题。例如每天的股票价格等等
基于上一个隐藏层的状态和当前的输入计算得来,泛化到任一时刻,便是,而这里的一般是tanh、sigmoid、ReLU等非线性的激活函数
且在实践中,一般只包含前面若干步而非之前所有步的隐藏状态
的计算和类似。在计算时,每一步使用的参数U、W、b都是一样的,也就是说每个步骤的参数都是共享的,这是RNN的重要特点,因为RNN的权值是在同一个向量中,只是不同时刻而已。
依次计算剩下来的(使用相同的参数U、W、b):
一个箭头就表示对对应的向量做一次类似于f(Wx+b)的变换,这里的这个箭头就表示对h1进行一次变换,得到输出y1,剩下的输出类似进行:
缺点:
RNN 会受到短时记忆的影响。如果一条序列足够长,那它们将很难将信息从较早的时间步传送到后面的时间步。因此,如果你正在尝试处理一段文本进行预测,RNN 可能从一开始就会遗漏重要信息。在反向传播期间,RNN 会面临梯度消失的问题。因为梯度是用于更新神经网络的权重值(新的权值 = 旧权值 - 学习率*梯度),梯度会随着时间的推移不断下降减少,而当梯度值变得非常小时,就不会继续学习。由于这些层不学习,RNN会忘记它在较长序列中以前看到的内容,因此RNN只具有短时记忆。
2、LSTM网络
LSTM和基线RNN并没有特别大的结构不同,但是它们用了不同的函数来计算隐状态。LSTM的“记忆”我们叫做细胞/cells,你可以直接把它们想做黑盒,这个黑盒的输入为前状态和当前输入。
RNN是重复单一的神经网络层,LSTM中的重复模块则包含四个交互的层,三个Sigmoid 和一个tanh层,并以一种非常特殊的方式进行交互。
LSTM拥有三种类型的门结构:遗忘门/忘记门、输入门和输出门,来保护和控制细胞状态。 LSTM有通过精心设计的称作为“门”的结构来去除或者增加信息到细胞状态的能力。门是一种让信息选择式通过的方法。
2.1忘记门
LSTM中的第一步是决定我们会从细胞状态中丢弃什么信息,该忘记门会读取上一个输出和当前输入,做一个Sigmoid 的非线性映射,然后输出一个向量(该向量每一个维度的值都在0到1之间,1表示完全保留,0表示完全舍弃,相当于记住了重要的,忘记了无关紧要的),最后与细胞状态相乘。 权值不共享
2.2 输入门
下一步是确定什么样的新信息被存放在细胞状态中。这里包含两个部分:
第一,sigmoid层,称“输入门层”决定什么值我们将要更新;
第二,一个tanh层创建一个新的候选值向量,会被加入到状态中。
权值不共享
2.3 细胞状态
更新旧细胞状态,更新为。前面的步骤已经决定了将会做什么(遗忘一些信息+添加新的信息),现在把旧状态与相乘,丢弃掉我们确定需要丢弃的信息,接着加。这就是新的候选值,根据我们决定更新每个状态的程度进行变化。这就是我们实际根据前面确定的目标,丢弃旧的信息并添加新的信息的地方,类似更新细胞状态。
2.4 输出门
这个输出将会基于我们的细胞状态,也是一个过滤后的版本。
首先,运行一个sigmoid层来确定细胞状态的哪个部分将输出出去。
接着,我们把细胞状态通过tanh进行处理(得到一个在-1到1之间的值)并将它和sigmoid门的输出相乘,最终我们仅仅会输出我们确定输出的那部分。(最开始的状态*(遗忘一些信息+添加新的信息))
3、GRU
一个改动较大的变体是GatedRecurrentUnit(GRU),这是由Cho,etal.(2014)提出。它将忘记门和输入门合成了一个单一的更新门。同样还混合了细胞状态和隐藏状态,和其他一些改动。最终的模型比标准的LSTM模型要简单,也是非常流行的变体。
和都是对、做的Sigmoid非线性映射,那区别在哪呢?原因在于GRU把忘记门和输入门合二为一了,而是属于要记住的,反过来则是属于忘记的,相当于对输入、做了一些更改/变化,而则相当于先见之明的把输入、在/对其做更改/变化之前,先事先存一份原始的,最终在那做一个tanh变化。
4、从Seq2Seq到Seq2Seq with Attention
Encoder(编码器)和 Decoder(解码器)之间只有一个「向量C」来传递信息,且C的长度固定。其缺陷在于:当输入信息太长时,所有语义完全转换为一个中间语义向量C来表示,原始的信息已经消失,会丢失掉一些信息。而为了解决「信息过长,信息丢失」的问题,Attention 机制就应运而生了。
Attention 模型的特点是 Eecoder 不再将整个输入序列编码为固定长度的「中间向量C」,而是编码成一个向量的序列(包含多个向量)。引入了Attention的Encoder-Decoder 模型如下图:
从输出的角度讲
每个输出的词Y会受到每个输入X_{1}、X_{2}、X_{3}、X_{4}的整体影响,不是只受某一个词的影响,毕竟整个输入语句是整体而连贯的,但同时每个输入词对每个输出的影响又是不一样的,即每个输出Y受输入X_{1}、X_{2}、X_{3}、X_{4}的影响权重不一样,而这个权重便是由Attention计算,也就是所谓的注意力分配系数,计算每一项输入对输出权重的影响大小
从编码的角度讲
在根据给到的信息进行编码时(或称特征提取),不同信息的重要程度是不一样的(可用权重表示),即有些信息是无关紧要的,比如一些语气助词之类的,所以这个时候在编码时,就可以有的放矢,根据不同的重要程度针对性汲取相关信息
例如机器翻译的例子,目标句子中的每个单词都应该学会其对应的源语句子中单词的注意力分配概率信息。这意味着在生成每个单词y_{i}的时候,原先都是相同的中间语义表示C会被替换成根据当前生成单词而不断变化的C_{i}(注:这里就是Attention模型的关键,即由固定的中间语义表示C换成了根据当前输出单词来调整成加入注意力模型的变化的C_{i})。
生成目标句子单词的过程成了下面的形式:
一般的做法中,C的生成函数就是对构成元素加权求和,即下列公式:
其中,代表输入句子Source的长度,代表在Target输出第i个单词时Source输入句子中第j个单词的注意力分配系数,而则是Source输入句子中第j个单词的语义编码。
生成目标句子某个单词,如何知道Attention模型所需要的输入句子单词注意力分配概率分布值呢?下图便可以较为便捷地说明注意力分配概率分布值的通用计算过程。
计算生成y_{i}时输入句子中的单词“Tom”、“Chase”、“Jerry”对y_{i}来说的注意力分配概率分布,那么可以用Target输出句子i-1时刻的隐层节点状态H_{i-1}去一一和输入句子Source中每个单词对应的RNN隐层节点状态h_{j}进行对比,即通过函数F(h_{j},H_{i-1})来获得目标单词y_{i}和每个输入单词对应的对齐可能性,这个F函数在不同论文里可能会采取不同的方法,然后函数F的输出经过Softmax进行归一化就得到了符合概率分布取值区间的注意力分配概率分布数值。
这里举的例子是由 Tom chase Jerrry 得到 汤姆追逐杰瑞,现在我们假设要预测杰瑞(已经预测出来汤姆追逐),那么这个时候,i 就表示的是杰瑞这个时刻,i-1时刻的hidden就包含了汤姆追逐的信息,就是想计算i-1时刻的hidden和Tom、chase、Jerry的各自不同的Attention数值,进而更好地预测杰瑞这个词。
5、 Attention的算法流程总结:通过计算相似性得出权重最后加权求和
比如,图书馆(source)里有很多书(value),为了方便查找,我们给书做了编号(key)。当我们想要了解漫威(query)的时候,我们就可以看看那些动漫、电影、甚至二战(美国队长)相关的书籍。
可以看到,将Source中的构成元素想象成是由一系列的<Key,Value>数据对构成,此时给定Target中的某个元素Query,通过计算Query和各个Key的相似性或者相关性,得到每个Key对应Value的权重系数,然后对Value进行加权求和,即得到了最终的Attention数值。
所以本质上Attention机制是对Source中元素的Value值进行加权求和,而Query和Key用来计算对应Value的权重系数。即可以将其本质思想改写为如下公式:
归纳整理一下,则为
第一步:代表漫威漫画的query 和 代表某本书的key 进行相似度计算(常见的方法包括:求两者的向量点积、求两者的向量Cosine相似性等),得到权值
第二步:将权值进行归一化(将原始计算分值整理成所有元素权重之和为1的概率分布,或者说通过SoftMax的内在机制更加突出重要元素的权重),得到直接可用的权重
第三步:将权重和 value 进行加权求和
所谓的Self-Attention,指的不是Target和Source之间的Attention机制,而是Source内部元素之间或者Target内部元素之间发生的Attention机制,也可以理解为Target=Source这种特殊情况下的注意力计算机制。
参考文章:如何从RNN起步,一步一步通俗理解LSTM_rnn lstm-CSDN博客
Transformer通俗笔记:从Word2Vec、Seq2Seq逐步理解到GPT、BERT-CSDN博客