0%

Seq2Seq

RNN Encoder-Decoder

序列

在介绍模型之前,先简单地解释一下什么是序列。
序列是一种数据形式,比如一个文本或是一段音频,它的一个很重要的特点就是在其内部有着先后关系
文本的前文和后文之间有着先后关系,音频的前一秒和后一秒有着先后关系,这些关系是不能乱的


如果明白了什么是序列,那么就可以开始接下来的学习了

模型介绍

为了解决如语言翻译这些问题,一种模型于《Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation》被提出
seq2seq

模型主要由两个部分组成

  • Encoder
  • Decoder

这两个部分通过一个语义向量C连接而成

Encoder

seq2seq

该部分使用RNN将原序列压缩成一个固定长度的语义向量C,每个$X_i$都代表着一个经过embedding(词嵌入)的词语,它们按顺序输入RNN,$X_T$对应的隐状态就是我们需要的语义向量C

Decoder

seq2seq
我们知道原本RNN隐状态的计算方式为
在Decoder部分,计算方式为
将语义向量作为影响因素加入了隐藏层的计算

seq2seq
或许这样会更加清楚,其中\是代表序列开始的占位符
值得注意的是,我们在训练阶段,输出的$yi$与生成$y{i+1}$时输入的$yi$并不相同,为了使生成的结果更为精确,输入的$y_i$取自我们想要得到的结果; 而在使用阶段,则将输出的$y_i$作为生成$y{i+1}$时输入的$y_i$


基础的Seq2Seq模型还有很多缺陷,之后会继续介绍有更佳性能的变体

仅供参考的Tensorflow实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def Encoder(input_data, rnn_size, num_layers):
'''
- input_data: 经过embedding的输入数据
- rnn_size: 每个rnn_cell中units的个数
- num_layers: rnn_cell的个数
'''

def get_lstm_cell(rnn_size):
return tf.nn.rnn_cell.LSTMCell(rnn_size)
stack_lstm = tf.nn.rnn_cell.MultiRNNCell([get_lstm_cell(rnn_size) for _ in range(num_layers)])
encoder_output, encoder_state = tf.nn.dynamic_rnn(stack_lstm, input_data, dtype=tf.float32)

# encoder_state即是我们要的语义向量
return encoder_output, encoder_state
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def Decoder(target_data, rnn_size, num_layers, c, start_token, end_token, length, decoder_embedding_table):
'''
- target_data: 经过embedding的目标数据
- rnn_size: 每个rnn_cell中units的个数
- num_layers: rnn_cell的个数
- c: 语义向量
- start_token: 开始标志占位符
- end_token: 结束标志占位符
- length: target数据中最大的长度
- decoder_embedding_table: 在Decoder端用来embedding的表
这里的Helper比较难理解
对Tensorflow中seq2seq使用方法的解释,对理解Helper有一定帮助
https://applenob.github.io/tf_dynamic_seq2seq.html
'''

def get_lstm_cell(rnn_size):
return tf.nn.rnn_cell.LSTMCell(rnn_size)
stack_lstm = tf.nn.rnn_cell.MultiRNNCell([get_lstm_cell(rnn_size) for _ in range(num_layers)])

# train_decoder
with tf.variable_scope('decode'):
train_help = tf.contrib.seq2seq.TrainingHelper(inputs = target_data, sequence_length= length)

train_decoder = tf.contrib.seq2seq.BasicDecoder(stack_lstm,train_help,c)

train_output, _ = tf.contrib.seq2seq.dynamic_decode(train_decoder, maximum_iterations=length)

# predict_decoder
with tf.variable_scope('decode', reuse=True):
# 将每次输出的logits中最大值的下标所对应的embedding向量作为下次输入
predict_help = tf.contrib.seq2seq.GreedyEmbeddingHelper(decoder_embedding_table, start_token, end_token)

predict_decoder = tf.contrib.seq2seq.BasicDecoder(stack_lstm, predict_help, c)

predict_output, _ =tf.contrib.seq2seq.dynamic_decode(predict_decoder, maximum_iterations=length)

return train_output, predict_output