《Seq2Seq中的Attention》

  Sequence to Sequence的结构在整个深度学习的进程中占有重要的角色,我在2017年做OCR的时候用这个,当时语音组做语音识别的同事也是用这个,而nlp组的做机器翻译的同事更是利用这个取得了不错的效果,尤其是Attention的引入让Sequence to Sequence的表现更加惊艳,所以这一经典的结构是值得被反复揣摩的,在此记录一下我对Seq2Seq的理解尤其是其中Attention机制,本文介绍Seq2Seq中一种计算Attention的方式。本文的Attention区别于self-Attention。

Key Words:Seq2Seq、RNN、Attention


Beijing, 2020

作者:RaySue

Code:https://github.com/bentrevett/pytorch-seq2seq

Agile Pioneer  


前言

  Attention 被广泛用于序列到序列(seq2seq)模型,这是一种深度学习模型,在很多任务上都取得了成功,如:机器翻译、文本摘要、图像描述生成。谷歌翻译在 2016 年年末开始使用这种模型。有 2 篇开创性的论文对这些模型进行了解释,论文连接如下:

  无论是语音识别或是机器翻译等任务都存在序列间的对齐问题,和同为解决对齐问题的 CTC Loss 相媲美的就是本文所说的 Attention 机制,Attention模型虽然好,但是还是有自身的问题:1.适合短语识别,对长句子识别比较差;2.noisy data的时候训练不稳定,因此比较好的方案是使得Attention与CTC进行结合,实验证实对比Attention模型还有CTC模型,Attention+CTC模型更快的收敛了,这得益于初始阶段CTC的阶段对齐更准确,使得Attention模型训练收敛更快,本文旨在讲清楚嵌入在seq2seq中的 Attention 机制。

  • seq2seq模型一般形式,以机器问答为例,在Encoder阶段,每个时刻RNN的输入 = 每个时刻的输入 + 上一时刻的 hidden state;而在Decoder阶段,每个时刻RNN的输入 = 前一时刻的输出+前一时刻的hidden state。

在这里插入图片描述


RNN 简介

  • RNN 的运行机制

  RNN 在每个时间步,采用上一个时间步的 hidden state(隐藏层状态) 和当前时间步的输入向量,来得到输出。白话说就是利用相同的权重在序列数据上滚动,和普通的ANN相比,引入了time step概念,这样就可以学习到特征在时间线上的关系了,RNN不同time step的权值是共享的。

  RNN的第0时刻的hidden state是初始化的,RNN具体运算看下图,
在这里插入图片描述

  • 搞清楚三个变量 batch_size、input_size、time_steps,两个函数输出outputs、state(hidden state)

  • 以文字数据为例:
      如果数据有1000段时序的句子,每句话有25个字,对每个字进行向量化,每个字的向量维度为300,那么batch_size=1000,time_steps=25,input_size=300。解析:time_steps一般情况下就是等于句子的长度,input_size等于字量化后向量的长度。

  • 以图片数据为例:
      拿 MNIST 手写数字集来说,训练数据有 6000 个手写数字图像,每个数字图像大小为28*28,batch_size=6000没的说,time_steps=28,input_size=28,我们可以理解为把图片图片分成28份,每份shape=(1, 28),然后利用最后一个时刻的状态来解码分类即可。

  • outputs:time_steps步里所有输出,shape=(batch_size, time_steps, cell.output_size)

  • state:最后一步的隐状态,它的形状为(batch_size, cell.state_size)

RNN的缺点: RNN机制实际中存在长程梯度消失的问题,对于较长的句子,我们很难寄希望于将输入的序列转化为定长的向量而保存所有的有效信息,所以随着所需翻译句子的长度的增加,这种结构的效果会显著下降。


Encoder - Decoder

  Encoder-Decoder是个非常通用的计算框架,至于Encoder和Decoder具体使用什么模型都是由研究者自己定的,常见的比如CNN/RNN/BiRNN/GRU/LSTM/Deep LSTM等,可以做机器翻译,也可以做图像语义分割。

  数据先在Encoder部分将输入数据通过非线性变换转化为中间语义 C,然后传入到解码器,在解码器的每个time step的输入是前一时刻的预测结果和hidden state一起作为输入的,这种经典的seq2seq的模型在解码部分的输入只是编码器最后一个时刻的hidden state,然后和历史预测的结果进行逐步解码,实际上无论第几个time step的解码都是依赖同一个Encoder的结果,即 Y 1 = F ( C , < E O S > ) ;    Y_1 = F(C, <EOS>);\space\space Y1=F(C,<EOS>);   Y 2 = F ( C , Y 1 ) ;    Y_2 = F(C, Y_1);\space\space Y2=F(C,Y1);   Y 3 = F ( C , Y 1 , Y 2 ) ; Y_3 = F(C, Y_1, Y_2); Y3=F(C,Y1,Y2);,这就导致了模型注意力不集中。

在这里插入图片描述


Attention

   Attention模型是对Encoder的所有hidden state与Decoder的每个 time step 的hidden state 做了一个加权融合,这样在不同的Decoder的time step具有不同的中间语义,比如在机器翻译中本身就应该是对齐的,我们在解码的过程中并不需要整个句子得到的中间语义,而是需要特定单词占权重很高的中间语义来解码,Attention 通过计算解码器每个time step的hidden state与所有Encoder的中间语义的“相似度”得到了一组权重,用于求加权平均即Attention的结果,用于解码。


Attention 数学公式

  下面公式中的 z t z_t zt 要和Encoder的 n n n h i h_i hi 做相似度运算,然后得到 n n n 个权重系数 a i t a_i^t ait, 然后对Encoder的 n n n h i h_i hi 做weighted sum就得到了 c t c_t ct

u i t = v T t a n h ( W 1 h i + W 2 z t )      ( 1 ) u_i^t=v^Ttanh(W_1h_i + W_2z_t) \space\space\space\space (1) uit=vTtanh(W1hi+W2zt)    (1)
a i t = s o f t m a x ( u i t )      ( 2 ) a_i^t=softmax(u_i^t) \space\space\space\space (2) ait=softmax(uit)    (2)
c t = ∑ i = 1 T A a i t h i      ( 3 ) c_t=\sum_{i=1}^{T_A}a_i^th_i \space\space\space\space (3) ct=i=1TAaithi    (3)

  • h i h_i hi 表示Encoder的第 i i i 个 time step 的 hidden state
  • z t z_t zt 表示Decoder的第 t t t 个 time step 的 hidden state
  • W 1 、 W 2 、 v T W_1、W_2、v^T W1W2vT 是需要学习的参数,通过 v T v^T vT将结果映射到与Encoder的time step一致
  • T A T_A TA 是 Encoder 的 time steps
  • c t c_t ct 表示第 t t t 个 time step 的 Attention 结果
Attention代码
# https://github.com/bentrevett/pytorch-seq2seq/blob/master/3%20-%20Neural%20Machine%20Translation%20by%20Jointly%20Learning%20to%20Align%20and%20Translate.ipynb
class Attention(nn.Module):
    def __init__(self, enc_hid_dim, dec_hid_dim):
        super().__init__()
        # enc_dim * 2 if encoder_is_bidirectional else enc_dim
        self.attn = nn.Linear((enc_hid_dim * 2) + dec_hid_dim, dec_hid_dim)
        self.v = nn.Linear(dec_hid_dim, 1, bias = False)
        
    def forward(self, hidden, encoder_outputs):
        
        #hidden = [batch size, dec hid dim]
        # src len 表示 Encoder 有多少个hi
        #encoder_outputs = [src len, batch size, enc hid dim * 2]
        
        batch_size = encoder_outputs.shape[1]
        src_len = encoder_outputs.shape[0]
        
        #repeat decoder hidden state src_len times
        hidden = hidden.unsqueeze(1).repeat(1, src_len, 1)
        
        encoder_outputs = encoder_outputs.permute(1, 0, 2)
        
        #hidden = [batch size, src len, dec hid dim]
        #encoder_outputs = [batch size, src len, enc hid dim * 2]
        
        energy = torch.tanh(self.attn(torch.cat((hidden, encoder_outputs), dim = 2))) 
        
        #energy = [batch size, src len, dec hid dim]

        attention = self.v(energy).squeeze(2)
        
        #attention= [batch size, src len]
        
        return F.softmax(attention, dim=1)

Attention 计算示意图

在这里插入图片描述


Hard Attention

  Hard attention is a stochastic process: instead of using all the hidden states as an input for the decoding, the system samples a hidden state h i h_i hi with the probabilities s i s_i si. In order to propagate a gradient through this process, we estimate the gradient by Monte Carlo sampling.

  Hard attention 是一个随机过程:不使用Encoder全部的hidden state作为解码的输入,取而代之的是对每个 h i h_i hi 依概率 s i s_i si 进行采样。为了能让梯度在这个过程传播,我们通过蒙特卡洛随机抽样来估计梯度。


Soft Attention

  我们常用的Attention就是Soft Attention,这种思路在图像分割网络 SE-Net 中也有很好的应用,对每个channel依其重要性进行加权平均得到更好的特征。


Attention 是如何解码的

  不同的论文的做法不一样我们这里说的流程是参考Neural Machine Translation by Jointly Learning to Align and Translate论文的做法

  • Step 1: Decoder包含attention层,attention层的输入是前一时刻的hidden state s t − 1 s_{t-1} st1 和所有的Encoder的hidden states H H H 一起计算得到的attention向量,记为 a t a_t at。然后我们利用 a t a_t at与所有的Encoder的hidden states加权相加得到一个加权向量 w t w_t wt,表示attention的输出。

a 1 = s o f t m a x ( v T t a n h ( W 1 s 0 , W 2 H ) ) ; . . . ; a t = s o f t m a x ( v T t a n h ( W 1 s t − 1 , W 2 H ) ) a_1=softmax(v^Ttanh(W_1s_{0}, W_2H));...;a_t=softmax(v^Ttanh(W_1s_{t-1}, W_2H)) a1=softmax(vTtanh(W1s0,W2H));...;at=softmax(vTtanh(W1st1,W2H))

w t = a t H w_t = a_t H wt=atH

  • Step 2: 计算Decoder hidden state,输入:输入词嵌入向量 d ( y t ) d(y_t) d(yt) 和attention向量 w t w_t wt的concat结果,和前一时刻的 Decoder 的 hidden state s t − 1 s_{t-1} st1,得到 s t s_t st

s t = D e c o d e r ( d ( y t ) , w t , s t − 1 ) s_t = Decoder(d(y_t),w_t,s_{t-1}) st=Decoder(d(yt),wt,st1)

  • Step 3: 预测 y ^ t + 1 \hat{y}_{t+1} y^t+1,我们把 d ( y t ) d(y_t) d(yt), w t w_t wt s t s_t st concat到一起之后经过一个全连接层 f f f, 来预测目标句子中的下一个单词 y ^ t + 1 \hat{y}_{t+1} y^t+1

y t + 1 ^ = f ( d ( y t ) , w t , s t ) \hat{y_{t+1}}=f(d(y_t), w_t, s_t) yt+1^=f(d(yt),wt,st)

示意图:

在这里插入图片描述


Attention应用

Attention的应用比较多,这里列举几个图例:

语音识别

在这里插入图片描述


OCR

在这里插入图片描述


Image captioning

在这里插入图片描述


参考

RNN

RNN

Neural Machine Translation by Jointly Learning to Align and Translate

Neural Machine Translation by Jointly Learning to Align and Translate - Code

https://www.cnblogs.com/mdumpling/p/8657070.html

https://mp.weixin.qq.com/s/SlO-onoAmsea64HtcGzVfQ

https://blog.csdn.net/u010159842/article/details/80473462

https://zhuanlan.zhihu.com/p/47063917

https://github.com/keon/seq2seq


Q&A

Q1: seq2seq 损失函数用什么?

A1: 交叉熵,nn.CrossEntropyLoss


Q2: 在不加入Attention的decoder部分,是如何使用Encoder最后一个时刻的hidden state的?

A2:一般的做法是把Encoder最后一个时刻的hidden state直接作为Decoder的hidden state传入,每步解码用 h t − 1 h_{t-1} ht1 y t ^ \hat{y_t} yt^一起来预测 y t + 1 ^ \hat{y_{t+1}} yt+1^,也有的文章的做法是在Decoder的每个时间步都输入Encoder最后一个时刻的hidden state来一起解码,也是work的。


Q3: Attention 解决什么问题?

A3: 解决长程梯度消失的问题,以及对齐问题。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐