Attention

Encoder-decoder

seq2seq模型跟RNN(包括LSTM,GRU)的区别是什么?

RNN能用来做词性标注,输入是一个长度为a的序列,输出也是一个长度为a的序列;RNN能用来分类,输出是一个长度固定或者不固定的序列,输出为一个label/class。

encoder_decoder_architecture

decoder过程中,每一个hidden state,都是根据上一个hidden state,上一个输出$\hat y_{t-1}$,和encoder的context vector来计算的。那么,context vector是如何利用的呢?在传统的RNN中,都是根据上一个hidden state和当前的input(在这里是上一个输出)决定当前的hidden state,没有利用context vector这么一说。在LSTM中虽然引入了context,但是并不是一成不变的context,而是随着时间推移在不断学习变化的context。因此,encoder-decoder到底是如何将context vector纳入的,细节需要搞清楚!

encoder

$$
h_i = (1- z_i) \odot h_{i-1} + z_i \odot \tilde h_i \\
\tilde h_i = \tanh (Wx_i + Ur_i \odot h_{i-1}) \\
z_i = \sigma(W_z x_i + U_z h_{i-1}) \\
r_i = \sigma(W_r x_i + U_r h_{i-1})
$$

encoder由forward RNN和backward RNN组合而成,每一个时刻的hidden state由两个结果拼接而成。

encoder_gru

decoder

$$
s_i = (1-z_i) \odot s_i + z_i \odot \tilde s_i \\
\tilde s_i = \tanh (W y_{i-1} + U r_i \odot s_{i-1} + C c_i) \\
z_i = \sigma(W_z y_{i-1} + U_z s_{i-1} + C_z c_i) \\
r_i = \sigma(W_r y_{i-1} + U_r s_{i-1} + C_r c_i) \\
$$

这里跟之前的版本相比,有一个很细微的差异:reset gate是直接跟前一个hidden state进行element-wise乘法,之前的是跟$U s_{i-1} + C c$相乘

decoder_gru

训练

训练的过程中,每次我们在考虑当前时刻的word来计算loss的时候,前一时刻的target word已经在训练数据中给出了(即不需要预测),我们直接根据交叉熵计算loss即可。

预测

编码解码模型求解的是$p(y_1^m \vert x_1^n)$ ,即给定source sequence,计算target sequence的条件概率。它的最简单形式:

  1. 首先,encoder按顺序处理source,最后生成一个定长的context vector。这个表示向量的实现方式是开放的,可以直接是encoder的最后的hidden state,也可以是最后的hidden state的函数,唯一确定的是,该向量的长度是固定的。
  2. decoder在定长的context vector的基础上生成target sequence。在用RNN语言模型随机生成文本的时候,hidden state $h_t$是 $h_{t-1}$和上一个预测$\hat y_{t-1}$的函数,$\hat y_t$是$h_t$的函数;在这里,$h_t$和$\hat y_t$都跟上一个预测和context vector有关:

$$
h_t = f(h_{t-1}, y_{t-1}, c) \\
y_t = g(h_t, y_{t-1}, c)
$$

上式中,如果$f$是LSTM,那么context vector该如何纳入到LSTM中呢?同样,$y_{t-1}$和$c$该如何应用在激活函数g中呢?

在得到decoder层的hidden state之后,如何根据它来计算生成每一个word的概率呢?

其实就是加了一个feedforward层,最后采用softmax进行归一化。
$$
p(y_j| y_1^{t-1}, x_1^n) = \frac{\exp(y_j s_t)} {\sum_{j=1}^K \exp(y_js_t)} \\
s_t = O_h h_t + O_y y_{t-1} + O_c c
$$
其中,$x_1^n$是给定的模型的input,$h_t$是t时刻的hidden state,$s_t$是t时刻根据hidden state生成的前馈神经网络的输出,$y_j$是第j个单词的词向量,K是词汇表的大小,$O$是参数,其不同的角标分别对应不同的参数。当然,我们注意到,$s_t$在feedforward最后,没有用激活函数(其实这里没用激活函数很正常,softmax一般就是在线性变换的基础上完成的),这是因为这里用了maxout激活函数,所以简写成上式。在Cho,2014中,maxout的k设置为2。

模型训练好之后,我们可以根据给定的source sequence来预测(计算概率最大的序列)target sequence。另外,我们也可以根据给定的source, target,计算该pair发生的概率(score):
$$
\frac{1}{m} \sum_{i=1}^m \log p(y_i|y_1^{i-1}, x_1^n)
$$

在feed forward结构中,都是$wx+b$,但是为了画图和公式的简洁性,我们一遍都忽略bias,但是这并不表示没有bias。

在预测过程中,生成最佳的target sequence需不需要用beam search呢?

Attention

首次提出attention

在neural machine translation模型中提出attention机制,是(Bahdanau,2015)在(Cho, 2014)基础上,摒弃了用一个定长的向量来表征source sequence,而是在解码过程中,根据当前的hidden state动态生成一个向量作为context vector。具体来说就是,之前是用encoder在处理input过程中最后输出的hidden state来表示input(可以在这基础上做一些变换),但为了克服RNN不能很好地编码长序列的信息(hidden state的维度有限,在处理长序列的时候,早期的信息容易丢失或者占比不够),我们将用上encoder输出的所有hidden state。但这个问题在于,对于这样一个不定长的hidden state序列,我们需要将其转换成一个定长的向量才能使用(不然对于不同长度的input,encoder的输出不一致,decoder没法儿统一使用)。因此,推出了attention机制:对于encoder生成的hidden state序列,我们对每一个元素采用不同的weight,进行加权。这样输出的向量便和最终的hidden state等长。之后的演化都是围绕着如何优化attention机制展开的。

attention-nmt

同Cho, 2014中提出的结构,attention版本的很多地方都类似。几个不一样的细节:

  1. RNN结构同样用的是GRU,但是encoder用的是Bi-RNN,这样的优势是能获得前向和后向依赖信息
  2. decoder用到的context vector不再是一个固定的定长向量,而是动态生成的向量

attention

注意力机制主要体现在$c_i$,$c_i = f(s_{i-1}, h)$,其中$h$是source的sequential representation,$s_{i-1}$是decoding过程中前一时刻的hidden state。
$$
c_i = \sum_{j=1}^{T_x} a_{ij}h_j
$$
其中$a_{ij}$是对齐函数,简单的版本可以是直接计算$h_j$和$s_{i-1}$的相似度,然后利用softmax进行normalization(使其符合概率分布)。这里采用的是简单的Feed forward网络结构来计算相似度,网络参数也是trainable。
$$
a_{ij} = \frac {\exp (e_{ij})} {\sum_{k=1}^{T_x} e_{ik}} \\
e_{ij} = v_a^T \tanh (W_a s_{i-1} + U_a h_j)
$$

也有资料将上式写成:$score(s_t, h_j) = v_a^T \tanh (W_a[s_t; h_j])$,分号表示concatenation。其实这两种表达方式是一样的,后面这种表述中,将两个参数合并成一个参数了。推广开来,上面提到的encoder和decoder图中,很多地方也可以看成是先将多个输入作一个concatenation,然后再作一个线性转换。

假设encoder层hidden state的维度为m,decoder层hidden state的维度为n,则$v_a$的维度为$d$,$W_a$的维度为$d \times n$,$U_a$的维度为$d \times m$。

引入attention之后,decoder在生成词的时候,也类似于之前的方式,采用一个feedforward网络外加一个maxout激活函数,唯一不同的地方就是context vector不再是固定的representation,而是在使用注意力机制之后的$c_i$。

Attention分类

类别 公式 备注
content-based $\cos(s_t, h_j)$ 跟隐状态和$h_j$的内容有关,取二者的余弦相似度
additive/concatenation $v_a^T \tanh(W_a\cdot [s_t:h_j])$ 将二者拼接,然后通过一个feedword网络
location-based $softmax (W_a\cdot s_t)$ 只跟当前隐状态有关,隐状态中包含位置信息
General $s_t^TW_ah_j$ (之所以叫general,是因为能模拟大部分的关系?)
dot-product $s_t^Th_j$
scaled dot-product $\frac{s_t^Th_j}{\sqrt n}$

Inspired by reference[1]

各种Attention

首先,我们先来理解一下soft和hard这两个词。hard,指的是非0即1;soft,是我们选择一个weight,然后根据这个权重来接纳。对应到attention机制,最早提出的attention机制,其实就是soft attention,对每一个$h_j$求得一个权重,然后按照权重将所有的$h_j$相加,得到$c_i$。

而global/local则是指attending的过程中,是否对所有encoder representation做对齐。global attention很明显,就是对所有的进行attention,进行的是soft attention;local attention的话,则是只对一部分encoder representation进行attend,这里面也分为两类。两类local attention的共同点都是以一个定长的window内的sequence进行attention机制,只不过中心点一个是固定的,一个是可学习的。

Transformer

  1. key, value, query分别该如何解读,代表什么呢?(已解答)
  2. 具体的网络结构是咋样的?如何用encoder-decoder的思路来分析它?
  3. 为什么只用self-attention就可以完成所有工作呢?
  4. 为什么multi-head attention可以并行呢?具体实现矩阵乘法的细节需要梳理一下
  5. dot-product求得的attention的weights,好像没有经过softmax归一化?(有!!!不瞎的话你应该早就看到了!!!)
  6. 多头self-attention如果都是同样的query, key, value的话,那么经过attention得到的应该都是同样的sequence of representation啊?如果都是一样的话,那还怎么学习不同的subspace的表示呢?难道只靠后面的feedword吗(feedforward不一样,它的weights的初始化的随机性,能保证学习到不一样的weights)?大概是因为在由embedding+position到进行attention的过程中,有一个线性变换的过程,即乘以一个参数矩阵,使得输出self-attention的dimension匹配一致。
  7. 在decoder过程中,为避免leftward的影响,即当前word之后的words对预测的影响,使用了mask attention,具体是怎么实现的呢?(将当前单词后面的embedding设置为$- \infty$,如此一来,用当前word的embedding去跟$-\infty$进行attention的时候,内积为负无穷,经softmax的幂指数处理为0,即不参与attention。)
  8. positional encoding是怎么做的?(目前还没看明白那两个公式是怎么应用的!!!
  9. 边界情况:decoder的第一个单词的预测是怎么进行的?是否需要给每句话加上开始和结束的token呢?

在NMT中,attention机制是通过$s_t$和$h_j$求权重,然后用求得的权重,加权$h_j$。用query,key,value来对应的话,$s_t$是query,key和value都是$h_1, h_2, \cdots, h_n$。因此,我们可以理解为:对齐函数计算query和key生成的权重之后,将对应的权重按照对应的位置分配到value上,对value进行加权。其中,query是一个向量,key和value都是向量序列。

在encoder层作self-attention的时候,每一个位置的word都会跟所有input words进行一次attention,生成一个vector,因此,self-attention结束,生成的是个vector的序列,序列的长度是input的长度。

网络结构-encoder

首先我们看encoder部分。

给定input之后,我们将每一个word都转换成embedding,再将每一个word的position转化成位置的embedding,加到word embedding之后。这时候,我们得到input对应的embedding。

input2embedding

首先,为确保每次attention都能获得不一样的输出,我们先对embedding进行线性变换(线性变换就是乘以一个矩阵,将向量转换成一个不同维度的向量,将向量序列转换成不同维度的向量序列,这些向量序列都是通过同一个线性变化作用的),生成三个要素:query,key,value。需要注意的是,转换过后,需要保证query和key的维度相同,之后才能用scaled dot product来进行attention。value的维度可以随意。

query_key_value

接下来就是self-attention阶段了。

self-attention

每一个word(query)都对所有的word(key)进行attention,得到权重之后,加权value,最终得到一个长度为input length的sequence representation。
$$
a(Q,K, V) = softmax(\frac{QK^T}{\sqrt d_k})V
$$
$d_k$是query的维度(Q为$l\times d_k$,$l$是input length),左边进过softmax得到的就是上图中中间部分的matrix,然后乘以$V$,便得到了最终的序列表征。

上面的attention进行多次,也称multi-head attention,得到的序列表示按照position对应关系进行拼接,得到一个新的序列表示。需要注意的是,重复h次的不仅仅是self-attention,从mix embedding到query,key,value的线性变换也同样需要重复h次。

multi-head-attention

到这里,encoder只是完成了multi-head attention部分。在mix embedding到multi-head attetion之间,加了一个residual connection,然后应用layer normalization。如下图所示。至此,完成了encoder中的第一个sublayer。第二个sublayer是一个feed-forword网络,激活函数是ReLU。但需要注意的是,第一个sublayer输出的不是一个向量,而是一个向量序列,序列对应于不同的position。因此,feed-forward网络是对每一个position的向量表征进行非线性变换,因此,这个前馈网络是position-wise的。网络参数在各个position之间是不共享的。同样,在前馈神经网络前后也加上了一个直连,然后应用layer normalization。至此,input words就转换成了sequence representation,供decoder解码的时候使用。

transformer_encoder

文章中multi-head的h取了8,即采用了8个attention,然后拼接每个的输出;$d_k$和$d_v$为64,query的dimension为64,每个attention的输出的维度为$l \times 64$。$d_model$为$64\times 8$,表明embedding的维度为512.

网络结构-decoder

decoder总体可以分为三个sublayer,第一个是self-attention,第二个是encoder-decoder attention,第三个是position-wise前馈神经网络。

联系前面提到的encoder-decoder,那里面的解码过程是:由$h_{t-1}$和encoder产生的序列表征生成当前的context vector,然后根据当前的$c_{t}$,$h_{t-1}$,$\hat y_{t-1}$求得当前的$h_t$。现在我们有了encoder产生的序列表征,该如何求$

  1. decoder的self-attention也可以通过矩阵乘法一次完成,但是在softmax之前,需要将当前词和之后的所有词都对应的值设置为$-\infty$,避免还没预测到的词对当前的预测产生影响。

在decoder过程中,有两个attention过程,第一个是self-attention,目的是将当前的word的embedding(word+position)对所有该单词之前的word进行self-attention,得到一个当前word的表示。然后用该表示作为第二个attention的query。第二个attention的key和value是encoder输出的representation sequence。

内积一定程度上也能反映两个向量的相似程度。相较于余弦距离,内积只是没有归一化而已

Reference

https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html#whats-wrong-with-seq2seq-model