seq2seq理论研究和在量化上的延伸系列一(思路+代码)(2)
2017-05-13 编辑:
编码(降维)
encoder_inputs和decode_inputs是形状为[max_time,batch_size]的int32张量,而编码器和解码器RNN预期字的密集向量表示[max_time,batch_size,input_embedding_size]。 我们通过使用单词嵌入来将它们转换成另一个。
首先我们初始化嵌入矩阵。 初始化是随机的。 我们依靠端到端训练来与编码器和解码器共同学习矢量表示。
embeddings = tf.Variable(tf.random_uniform([vocab_size, input_embedding_size], - 1.0, 1.0), dtype=tf.float32)
我们使用tf.nn.embedding_lookup来索引嵌入矩阵:给定单词4,我们将其表示为第4列嵌入矩阵。 与单热编码字4的替代方法[0,0,0,1,0,0,0,0,0,0](vocab size 10)相比,该操作是轻量级的,然后通过嵌入矩阵。
另外,除了4th之外,我们不需要计算任何列的渐变。
编码器和解码器将共享嵌入。 实际的NLP应用中,嵌入矩阵可以获得非常大的,具有100k甚至1m的列。
encoder_inputs_embedded = tf.nn.embedding_lookup(embeddings, encoder_inputs)decoder_inputs_embedded = tf.nn.embedding_lookup(embeddings, decoder_inputs)
编码器
TensorFlow中所有RNN的核心是RNNCell类及其衍生(如LSTMCell)
encoder_cell = tf.contrib.rnn.LSTMCell(encoder_hidden_units)encoder_outputs, encoder_final_state = tf.nn.dynamic_rnn( encoder_cell, encoder_inputs_embedded, dtype=tf.float32, time_major= True,)
delencoder_outputs
我们丢弃encoder_outputs,因为我们在seq2seq框架内对它们不感兴趣。 我们实际想要的是encoder_final_state - 在编码器卷展栏最后一刻的LSTM隐藏单元格的状态。
encoder_final_state也称为“思维向量”。 我们将使用它作为解码器的初始状态。 在seq2seq没有注意的情况下,这是编码器将信息传递给解码器的唯一点。 我们希望通过时间的反向传播(BPTT)算法将调整模型以通过思想向量传递足够的信息以进行正确的序列输出解码。
encoder_final_state LSTMStateTuple(c=<tf.Tensor 'rnn/while/Exit_2:0' shape=(?, 20) dtype=float32>, h=<tf.Tensor 'rnn/while/Exit_3:0' shape=(?, 20) dtype=float32>)
TensorFlow LSTM实现将状态存储为张量的元组。
encoder_final_state.h是LSTM单元的隐藏层的激活
encoder_final_state.c是最终输出
解码器
decoder_cell = tf.contrib.rnn.LSTMCell(decoder_hidden_units)decoder_outputs, decoder_final_state = tf.nn.dynamic_rnn( decoder_cell, decoder_inputs_embedded, initial_state=encoder_final_state, dtype=tf.float32, time_major= True, scope= "plain_decoder",)
由于我们将encoder_final_state作为initial_state传递给解码器,所以它们应该是兼容的。 这意味着相同的单元格类型(在本例中为LSTMCell),相同数量的hidden_units和相同的图层数量(单层)。 我想这可以放宽,如果我们通过一个单层的MLP通过编码器_final_state。
使用编码器,我们对单元格输出不感兴趣。 但是解码器的输出是我们实际上的输出:我们使用它们来获得输出序列的单词的分布。
在这一点上,decode_cell输出是每个时间步长的一个hidden_units大小的向量。 然而,对于训练和预测,我们需要大小为vocab_size的logits。 合理的事情是将线性层(没有激活功能的完全连接层)放在LSTM输出的顶部,以获得非归一化的逻辑。 这个层被称为投影层。
decoder_logits = tf.contrib.layers.linear(decoder_outputs, vocab_size)decoder_prediction = tf.argmax(decoder_logits, 2)
优化
decoder_logits
<tf.Tensor 'fully_connected/Reshape_1:0' shape=(?, ?, 10) dtype=float32>
RNN输出形状为[max_time,batch_size,hidden_units]的张量,其中投影层映射到[max_time,batch_size,vocab_size]。 vocab_size部分是静态的,而max_time和batch_size是动态的。
stepwise_cross_entropy = tf.nn.softmax_cross_entropy_with_logits( labels=tf.one_hot(decoder_targets, depth=vocab_size, dtype=tf.float32), logits=decoder_logits,)loss = tf.reduce_mean(stepwise_cross_entropy)train_op = tf.train.AdamOptimizer().minimize(loss)sess.run(tf.global_variables_initializer())
测试
当构建图形时,当静态形状不匹配时,TF将抛出错误。 但是,当我们尝试通过图形运行某些东西时,往往只会发现动态形状之间的不匹配。
所以让我们尝试运行一些东西。 为此,我们需要准备一些数据,我们将会投入到占位符中。
- 编码器形状如何固定为最大
- 解码器形状如何由输入进行仲裁和确定,但编码器应该可能会更长
- 如何解码器输入值也是仲裁的,以及我们如何使用指令,以及这些0是什么,可以使用什么(移位Gold序列,波束搜索)