Seq2Seq的简单实现

写在前面

Tensorflow基础知识---Senquence-to-Senquence详解一文中详细讲述了sequence-to-sequence的原理,本文主要是基于前文中的讲述内容,使用tensorflow实现出来,便于对原理的理解。
本文主要参考ematvey在github上的代码。

simple seq2seq model with dynamic unrolling

本文实现的是一个最简单的seq2seq模型,即只包含前向传播的编码过程,和没有注意力机制的解码过程。并且没有使用tf.contrib.seq2seq接口。本代码主要的结构是参考的是 Sutskever, Vinyals and Le (2014)中提到的encoder-decoder结构。

具体描述参见Tensorflow基础知识---Senquence-to-Senquence详解图一的详细解释。

代码详解

import numpy as np
import tensorflow as tf

数据预处理

定义参数

# 填充标记
PAD = 0
# 结束标记
EOS = 1
# 词汇量的大小
vocab_size = 10
# 输入数据的词嵌套的大小
input_embedding_size = 20
# 编码中RNN的隐藏单元的个数
encoder_hidden_units = 25
# 解码中RNN的隐藏单元的个数
decoder_hidden_units = encoder_hidden_units

定义编码输入,解码输入和输出

# encoder_inputs:[max_time, batch_size]
encoder_inputs = tf.placeholder(shape=(None, None), dtype=tf.int32, name='encoder_inputs')
# decoder_targets: [max_time, batch_size]
decoder_targets = tf.placeholder(shape=(None, None), dtype=tf.int32, name='decoder_targets')
# decoder_inputs: [max_time, batch_size]
# 实际上是不用手动feeddecoder_inputs的,
decoder_inputs = tf.placeholder(shape=(None, None), dtype=tf.int32, name='decoder_inputs')

对输入数据进行embedding操作

embeddings = tf.Variable(tf.random_uniform([vocab_size, input_embedding_size], -1.0, 1.0), dtype=tf.float32)
# encoder_inputs_embeded: [max_time, batch_size, input_embedding_size]
encoder_inputs_embeded = tf.nn.embedding_lookup(embeddings, encoder_inputs)
# decoder_inputs_embeded: [max_time, batch_size, input_embedding_size]
decoder_inputs_embeded = tf.nn.embedding_lookup(embeddings, decoder_inputs)
decoder_inputs_embeded

Encoder过程

# 定义编码RNNCell
encoder_cell = tf.contrib.rnn.LSTMCell(encoder_hidden_units)
# dynamic RNN在改变batch_size或者sequence_length的时候是不需要重新训练的,但是如果改变了vocabulary size的时候是需要重新训练模型的
# time_major=True表示batch_size在第二列,即encoder_inputs_embeded的结构为[max_time, batch_size, embedding_dim]
# 如果time_major=True则表示batch_size在第二列,即encoder_inputs_embeded的机构为[batch_size, max_time, embedding]
encoder_outputs, encoder_final_state = tf.nn.dynamic_rnn(encoder_cell,
                                                         encoder_inputs_embeded,
                                                         dtype=tf.float32, time_major=True)
# decoder过程中不需要encoder的输出,所以将其删除,仅保留运行输出的隐藏状态
del encoder_outputs

Decoder过程

# 定义解码RNNCell
decoder_cell = tf.contrib.rnn.LSTMCell(decoder_hidden_units)
# 
decoder_outputs, decoder_final_state = tf.nn.dynamic_rnn(decoder_cell, 
                                                         decoder_inputs_embeded,
                                                         initial_state=encoder_final_state,
                                                         dtype=tf.float32, time_major=True,scope='plain_decoder')

参考文献

基于注意力机制的seq2seq网络