使用 seq2seq API(ver 1.1 及更高版本)的 Tensorflow 序列到序列模型

Posted

技术标签:

【中文标题】使用 seq2seq API(ver 1.1 及更高版本)的 Tensorflow 序列到序列模型【英文标题】:Tensorflow Sequence to sequence model using the seq2seq API ( ver 1.1 and above) 【发布时间】:2017-09-23 04:22:56 【问题描述】:

我正在使用 TensorFlow v:1.1,我想使用 tf.contrib.seq2seq api 实现 sequence to sequence 模型。 但是,我很难理解如何使用提供的所有功能(BasicDecoder、Dynamic_decode、Helper、Training Helper ...)来构建我的模型。 这是我的设置:我想“翻译”一个特征向量序列: (batch_size, encoder_max_seq_len, feature_dim) 成不同长度的序列 (batch_size, decoder_max_len, 1)。

我已经有了 encoder,它是一个带有 LSTM 单元的 RNN,我得到了它的 final state,我想将它作为初始输入提供给解码器。 我已经有了解码器 MultiRNNCell LSM 的单元。 你能帮我使用 tf.contrib.seq2seq2 和 dynamic_decode 的功能构建最后一部分吗(示例代码解释将不胜感激) ?

这是我的代码:

import tensorflow as tf
from tensorflow.contrib import seq2seq
from tensorflow.contrib import rnn
import math

from data import gen_sum_2b2

class Seq2SeqModel:
def __init__(self,
             in_size,
             out_size,
             embed_size,
             n_symbols,
             cell_type,
             n_units,
             n_layers):
    self.in_size = in_size
    self.out_size = out_size
    self.embed_size = embed_size
    self.n_symbols = n_symbols
    self.cell_type = cell_type
    self.n_units = n_units
    self.n_layers = n_layers

    self.build_graph()

def build_graph(self):
    self.init_placeholders()
    self.init_cells()
    self.encoder()
    self.decoder_train()
    self.loss()
    self.training()

def init_placeholders(self):
    with tf.name_scope('Placeholders'):
        self.encoder_inputs = tf.placeholder(shape=(None, None, self.in_size), 
                                             dtype=tf.float32, name='encoder_inputs')
        self.decoder_targets = tf.placeholder(shape=(None, None),
                                              dtype=tf.int32, name='decoder_targets')
        self.seqs_len = tf.placeholder(dtype=tf.int32)
        self.batch_size = tf.placeholder(tf.int32, name='dynamic_batch_size')
        self.max_len = tf.placeholder(tf.int32, name='dynamic_seq_len')
        decoder_inputs = tf.reshape(self.decoder_targets, shape=(self.batch_size,
                                    self.max_len, self.out_size))
        self.decoder_inputs = tf.cast(decoder_inputs, tf.float32)
        self.eos_step = tf.ones([self.batch_size, 1], dtype=tf.float32, name='EOS')
        self.pad_step = tf.zeros([self.batch_size, 1], dtype=tf.float32, name='PAD')

def RNNCell(self):
    c = self.cell_type(self.n_units, reuse=None)
    c = rnn.MultiRNNCell([self.cell_type(self.n_units) for i in range(self.n_layers)])
    return c

def init_cells(self):
    with tf.variable_scope('RNN_enc_cell'):
        self.encoder_cell = self.RNNCell()  
    with tf.variable_scope('RNN_dec_cell'):
        self.decoder_cell = rnn.OutputProjectionWrapper(self.RNNCell(), self.n_symbols)

def encoder(self):
    with tf.variable_scope('Encoder'):
        self.init_state = self.encoder_cell.zero_state(self.batch_size, tf.float32) 
        _, self.encoder_final_state = tf.nn.dynamic_rnn(self.encoder_cell, self.encoder_inputs,
                                                        initial_state=self.init_state) 

【问题讨论】:

你通过seq2seq tutorial了吗?它有一个端到端的编码和解码示例。 嗨,你搞清楚了吗? @AllenLavoie 我认为问题在于 API 已针对 v1.1 进行了更改,但示例尚未更新? @NedRuggeri 您是否遇到了特定错误,还是该教程中尚未包含新操作?如果是前者,Github 问题将是有序的,因此我们可以跟踪修复它。 @AllenLavoie 我认为教程代码没有给出任何错误:它只是使用了已被新类替换的遗留函数。如果您是新手,很难弄清楚,但是当我全部了解时,也许我可以提出一些建议:-) 【参考方案1】:

解码层:

由于traininginference期间的区别,解码由两部分组成:

解码器在特定时间步的输入总是来自输出 上一个时间步长。但是在训练期间,输出是固定的 到实际目标(实际目标作为输入反馈),这已证明可以提高性能。

这两个都是使用来自tf.contrib.seq2seq 的方法处理的。

    decoder的主要功能是:seq2seq.dynamic decoder()进行动态解码:

    tf.contrib.seq2seq.dynamic_decode(decoder,maximum_iterations)

    这需要Decoder 实例和maximum_iterations=maximum seq length 作为输入。

    1.1 Decoder 实例来自:

    seq2seq.BasicDecoder(cell, helper, initial_state,output_layer)

    输入是:cell(RNNCell 实例)、helper(辅助实例)、initial_state(解码器的初始状态,应该是编码器的输出状态)和output_layer(可选密集层作为输出进行预测)

    1.2 RNNCell 实例可以是rnn.MultiRNNCell()

    1.3 helper 实例与traininginference 不同。在training 期间,我们希望将输入馈送到解码器,而在inference 期间,我们希望将time-step (t) 中解码器的输出作为输入传递给time step (t+1) 中的解码器。

    对于训练:我们使用辅助函数: seq2seq.TrainingHelper(inputs, sequence_length),它只是读取输入。

    推理:我们调用辅助函数: seq2seq.GreedyEmbeddingHelper() or seqseq.SampleEmbeddingHelper(),不同的是它是否使用输出的argmax() or sampling(from a distribution) 并将结果通过嵌入层以获得下一个输入。

整合:Seq2Seq 模型

    encoder layer 获取编码器状态并将其作为initial_state 传递给解码器。 使用seq2seq.dynamic_decoder() 获取decoder traindecoder inference 的输出。当您调用这两种方法时,请确保共享权重。 (使用variable_scope 重复使用权重) 然后使用损失函数seq2seq.sequence_loss训练网络。

给出了示例代码here 和here。

【讨论】:

感谢您的回答。一件事还不清楚。在训练期间,我们是否真的将“真实”输出传递给解码器,就像我们从数据集中获得它们一样,而不是提供序列的先前输出(如在无法确定序列的真实情况下)? 换句话说,我们是使用真实的目标/解码器输入进行训练,然后在没有解码器输入的情况下测试看不见的数据(架构中强制要求的第一个除外)还是让我们的模型成为在不提供任何解码器输入的情况下进行训练和测试。前一种感觉像是作弊,但它可能会导致我现在无法猜测的良好行为 这样想:解码器的输入总是来自之前的输出。所以解码器总是得到一些输入。但在训练期间,实际目标的输出为fixed,这已证明可以提高性能。你可以看看 tensorflow 的 seq-2-seq 教程:github.com/google/seq2seq/blob/master/seq2seq/models/… 感谢@vijaym,关于 1.1 中的 output_layer,如果我没记错的话,它将把解码器隐藏层投影到目标。如何基于 output_layer 构建序列损失? dynamic_decode 不只返回隐藏状态吗? sequence_loss 需要解码器 logits,你是怎么得到的?

以上是关于使用 seq2seq API(ver 1.1 及更高版本)的 Tensorflow 序列到序列模型的主要内容,如果未能解决你的问题,请参考以下文章

无法理解 tf.contrib.seq2seq.TrainingHelper

在 API 15 及更高版本中使用 PreferenceActivity

IndexedDB - iPad Safari(版本:10 及更高版本)jquery.bind 不起作用

使用 API 8 及更高版本获取用户/所有者个人资料联系人 URI 和用户图像

Android Edittext 不能专注于 API 22(代码在 API 23 及更高版本上运行良好)

Android drawable 对于 API <23 和 API 23 及更高版本显示为拉伸