功能介绍
文本生成模型是NLP中基础的语言模型,它的任务是在给定的Word Sequence下输出下一个Word的分布。以下对Tensorflow例程代码进行解读,代码地址为源代码地址
数据预处理
训练数据集来源于Penn Tree Bank数据集。数据预处理代码为reader.py文件,预处理流程如下:
-
读取文件
-
分割预料成训练集、验证集、测试集
-
字母小写
-
数字用 “N” 替换,换行 “\n” 用,合并成一行。
-
建立word –> id的映射字典并转换为tensor
-
将数据 reshape 成 [batch_size, batch_len]形状,对数据做切分,切分成一个个batch。
-
构建模型输入输出,此模型是N到N模型,输出数据和输入数据一致,输出单词为输入单词的右移一位。代码里巧妙的使用 queue 和 slice 完成输入、输出数据的制作:
i = tf.train.range_input_producer(epoch_size, shuffle=False).dequeue() x = tf.strided_slice(data, [0, i * num_steps], [batch_size, (i + 1) * num_steps]) x.set_shape([batch_size, num_steps]) y = tf.strided_slice(data, [0, i * num_steps + 1], [batch_size, (i + 1) * num_steps + 1]) y.set_shape([batch_size, num_steps])
其中 i 为循环队列,存储元素为0到epoch_size-1,Graph运行时,从队列去除元素 i ,然后从data中切割得到输入数据x和输出数据y。
模型构建
模型构建过程如下:
- 对输入的数据进行word embedding,embedding 矩阵跟随模型被训练,这里也可使用预训练的embedding词向量。
- 构建多层LSTM网络,这里提供了3中不同网络规格的配置:
- 小型网络,2个LSTM层,展开大小是20,每层包含了200个隐藏节点.,dropout为0。
- 中型网络,2个LSTM层,展开大小是35,每层包含了650个隐藏节点,dropout为0.5。
- 大型网络,2个LSTM层,展开大小是35,每层包含了1500个隐藏节点,dropout为0.65。
可以发现,模型中隐藏节点越多,越容易过拟合,需要更大的dropout。通过参数配置可以选择生成不同类型的LSTM的Cell,并使用tf.contrib.rnn.MultiRNNCell生成LSTM模型。
- 构建softmax层
训练方法
-
首先定义了损失函数,这里使用的是sequence_loss_by_example函数计算损失,这本质是交叉熵。
-
使用梯度下降的优化方法
-
为了防止梯度爆炸,当梯度平方和大于max_grad_norm对梯度缩放缩放。
# 做梯度缩放防止梯度爆炸 grads, _ = tf.clip_by_global_norm(tf.gradients(self._cost, tvars), config.max_grad_norm) # 优化 optimizer = tf.train.GradientDescentOptimizer(self._lr) self._train_op = optimizer.apply_gradients( zip(grads, tvars), global_step=tf.train.get_or_create_global_step())
-
学习率缩放,为了更快手链,学习率设置成指数衰减
lr_decay = config.lr_decay ** max(i + 1 - config.max_epoch, 0.0) m.assign_lr(session, config.learning_rate * lr_decay)
-
为了便于理解RNN,示例代码显示的一个个step得到output的过程。
# The alternative version of the code below is: # # inputs = tf.unstack(inputs, num=self.num_steps, axis=1) # outputs, state = tf.nn.static_rnn(cell, inputs, # initial_state=self._initial_state) outputs = [] with tf.variable_scope("RNN"): for time_step in range(self.num_steps): if time_step > 0: tf.get_variable_scope().reuse_variables() (cell_output, state) = cell(inputs[:, time_step, :], state) outputs.append(cell_output) output = tf.reshape(tf.concat(outputs, 1), [-1, config.hidden_size])
训练结果
训练结果如下图所示:
其中preplexity为loss值,可以看出训练过程可以完全收敛,但是验证集和测试集的loss值较大,存在一定的过拟合。
学习之处
样例有几点值得学习的地方:
- 数据预处理方法值得学习。
- 设置多个网络类型,可通过参数配置。
- 采用多个正则化方法,比如梯度缩放,增加dropout,不同网络设置不同dropout。
不足之处:
- 没有文本生成代码,不能查看文本生成效果。