网站整合营销,网站建设推广多少钱,昆明学校网站设计公司,大型医院设计网站建设首发于机器不学习关注专栏写文章从Encoder到Decoder实现Seq2Seq模型天雨粟模型师傅 / 果粉关注他300 人赞同了该文章更新#xff1a;感谢Gang He指出的代码错误。get_batches函数中第15行与第19行#xff0c;代码已经重新修改#xff0c;GitHub已更新。前言好久没有更新专…首发于机器不学习关注专栏写文章从Encoder到Decoder实现Seq2Seq模型天雨粟模型师傅 / 果粉关注他300 人赞同了该文章更新感谢Gang He指出的代码错误。get_batches函数中第15行与第19行代码已经重新修改GitHub已更新。前言好久没有更新专栏今天我们来看一个简单的Seq2Seq实现我们将使用TensorFlow来实现一个基础版本的Seq2Seq主要帮助理解Seq2Seq中的基础架构。最基础的Seq2Seq模型包含了三个部分即Encoder、Decoder以及连接两者的中间状态向量Encoder通过学习输入将其编码成一个固定大小的状态向量S继而将S传给DecoderDecoder再通过对状态向量S的学习来进行输出。图中每一个box代表了一个RNN单元通常是LSTM或者GRU。其实基础的Seq2Seq是有很多弊端的首先Encoder将输入编码为固定大小状态向量的过程实际上是一个信息“信息有损压缩”的过程如果信息量越大那么这个转化向量的过程对信息的损失就越大同时随着sequence length的增加意味着时间维度上的序列很长RNN模型也会出现梯度弥散。最后基础的模型连接Encoder和Decoder模块的组件仅仅是一个固定大小的状态向量这使得Decoder无法直接去关注到输入信息的更多细节。由于基础Seq2Seq的种种缺陷随后引入了Attention的概念以及Bi-directional encoder layer等由于本篇文章主要是构建一个基础的Seq2Seq模型对其他改进tricks先不做介绍。总结起来说基础的Seq2Seq主要包括EncoderDecoder以及连接两者的固定大小的State Vector。实战代码下面我们就将利用TensorFlow来构建一个基础的Seq2Seq模型通过向我们的模型输入一个单词字母序列例如hello模型将按照字母顺序排序输出即输出ehllo。版本信息Python 3 / TensorFlow 1.11. 数据集数据集包括source与target- source_data: 每一行是一个单词- target_data: 每一行是经过字母排序后的“单词”它的每一行与source_data中每一行一一对应例如source_data的第一行是hello第二行是what那么target_data中对应的第一行是ehllo第二行是ahtw。2. 数据预览我们先把source和target数据加载进来可以看一下前10行target的每一行是对source源数据中的单词进行了排序。下面我们就将基于这些数据来训练一个Seq2Seq模型来帮助大家理解基础架构。3. 数据预处理在神经网络中对于文本的数据预处理无非是将文本转化为模型可理解的数字这里都比较熟悉不作过多解释。但在这里我们需要加入以下四种字符PAD主要用来进行字符补全EOS和GO都是用在Decoder端的序列中告诉解码器句子的起始与结束UNK则用来替代一些未出现过的词或者低频词。 PAD: 补全字符。 EOS: 解码器端的句子结束标识符。 UNK: 低频词或者一些未遇到过的词等。 GO: 解码器端的句子起始标识符。通过上面步骤我们可以得到转换为数字后的源数据与目标数据。4. 模型构建Encoder模型构建主要包括Encoder层与Decoder层。在Encoder层我们首先需要对定义输入的tensor同时要对字母进行Embedding再输入到RNN层。在这里我们使用TensorFlow中的tf.contrib.layers.embed_sequence来对输入进行embedding。我们来看一个栗子假如我们有一个batch2sequence_length5的样本features [[1,2,3,4,5],[6,7,8,9,10]]使用tf.contrib.layers.embed_sequence(features,vocab_sizen_words, embed_dim10)那么我们会得到一个2 x 5 x 10的输出其中features中的每个数字都被embed成了一个10维向量。官方关于tf.contrib.layers.embed_sequence()的解释如下Maps a sequence of symbols to a sequence of embeddings.Typical use case would be reusing embeddings between an encoder and decoder.Decoder在Decoder端我们主要要完成以下几件事情对target数据进行处理构造DecoderEmbedding构造Decoder层构造输出层输出层会告诉我们每个时间序列的RNN输出结果Training DecoderPredicting Decoder下面我们会对这每个部分进行一一介绍。1. target数据处理我们的target数据有两个作用在训练过程中我们需要将我们的target序列作为输入传给Decoder端RNN的每个阶段而不是使用前一阶段预测输出这样会使得模型更加准确。这就是为什么我们会构建Training和Predicting两个Decoder的原因下面还会有对这部分的解释。需要用target数据来计算模型的loss。我们首先需要对target端的数据进行一步预处理。在我们将target中的序列作为输入给Decoder端的RNN时序列中的最后一个字母或单词其实是没有用的。我们来用下图解释我们此时只看右边的Decoder端可以看到我们的target序列是[go, W, X, Y, Z, eos]其中goWXYZ是每个时间序列上输入给RNN的内容我们发现eos并没有作为输入传递给RNN。因此我们需要将target中的最后一个字符去掉同时还需要在前面添加go标识告诉模型这代表一个句子的开始。如上图所示红色和橙色为我们最终的保留区域灰色是序列中的最后一个字符我们把它删掉即可。我们使用tf.strided_slice()来进行这一步处理。其中tf.fill(dims, value)参数会生成一个dims形状并用value填充的tensor。举个栗子tf.fill([2,2], 7) [[7,7], [7,7]]。tf.concat()会按照某个维度将两个tensor拼接起来。2. 构造Decoder对target数据进行embedding。构造Decoder端的RNN单元。构造输出层从而得到每个时间序列上的预测结果。构造training decoder。构造predicting decoder。注意我们这里将decoder分为了training和predicting这两个encoder实际上是共享参数的也就是通过training decoder学得的参数predicting会拿来进行预测。那么为什么我们要分两个呢这里主要考虑模型的robust。在training阶段为了能够让模型更加准确我们并不会把t-1的预测输出作为t阶段的输入而是直接使用target data中序列的元素输入到Encoder中。而在predict阶段我们没有target data有的只是t-1阶段的输出和隐层状态。上面的图中代表的是training过程。在training过程中我们并不会把每个阶段的预测输出作为下一阶段的输入下一阶段的输入我们会直接使用target data这样能够保证模型更加准确。这个图代表我们的predict阶段在这个阶段我们没有target data这个时候前一阶段的预测结果就会作为下一阶段的输入。当然predicting虽然与training是分开的但他们是会共享参数的training训练好的参数会供predicting使用。decoder层的代码如下构建好了Encoder层与Decoder以后我们需要将它们连接起来build我们的Seq2Seq模型。定义超参数# 超参数
# Number of Epochs
epochs 60
# Batch Size
batch_size 128
# RNN Size
rnn_size 50
# Number of Layers
num_layers 2
# Embedding Size
encoding_embedding_size 15
decoding_embedding_size 15
# Learning Rate
learning_rate 0.001定义loss function、optimizer以及gradient clipping目前为止我们已经完成了整个模型的构建但还没有构造batch函数batch函数用来每次获取一个batch的训练样本对模型进行训练。在这里我们还需要定义另一个函数对batch中的序列进行补全操作。这是啥意思呢我们来看个例子假如我们定义了batch2里面的序列分别是[[h, e, l, l, o],[w, h, a, t]]那么这两个序列的长度一个是5一个是4变长的序列对于RNN来说是没办法训练的所以我们这个时候要对短序列进行补全补全以后两个序列会变成下面的样子[[h, e, l, l, o],[w, h, a, t, PAD]]这样就保证了我们每个batch中的序列长度是固定的。感谢Gang He提出的错误。此处代码已修正。修改部分为get_batches中的两个for循环for target in targets_batch和for source in sources_batch之前的代码是for target in pad_targets_batch和for source in pad_sources_batch因为我们用sequence_mask计算了每个句子的权重该权重作为参数传入loss函数主要用来忽略句子中pad部分的loss。如果是对pad以后的句子进行loop那么输出权重都是1不符合我们的要求。在这里做出修正。GitHub上代码也已修改。至此我们完成了整个模型的构建与数据的处理。接下来我们对模型进行训练我定义了batch_size128epochs60。训练loss如下模型预测 我们通过实际的例子来进行验证。输入“hello”输入“machine”输入“common” 总结至此我们实现了一个基本的序列到序列模型Encoder通过对输入序列的学习将学习到的信息转化为一个状态向量传递给DecoderDecoder再基于这个输入得到输出。除此之外我们还知道要对batch中的单词进行补全保证一个batch内的样本具有相同的序列长度。我们可以看到最终模型的训练loss相对已经比较低了并且从例子看其对短序列的输出还是比较准确的但一旦我们的输入序列过长比如15甚至20个字母的单词其Decoder端的输出就非常的差。完整代码已上传至GitHub。转载请联系作者获得授权。编辑于 2018-03-19「真诚赞赏手留余香」赞赏4 人已赞赏深度学习Deep Learning文本处理神经网络赞同 300149 条评论分享收藏