本章主要回顾RNN、LSTM技术原理,并基于RNN/LSTM技术训练语言模型。也就是给定一个单词序列,预测最有可能出现的下一个单词。例如,给定[had, a, general] 3个单词的LSTM输入序列,预测下一个单词是什么?如下图所示:
(点击放大图像)
RNN技术原理循环神经网络(Recurrent Neural Network, RNN)是一类用于处理序列数据的神经网络。和卷积神经网络的区别在于,卷积网络是适用于处理网格化数据(如图像数据)的神经网络,而循环神经网络是适用于处理序列化数据的神经网络。例如,你要预测句子的下一个单词是什么,一般需要用到前面的单词,因为一个句子中前后单词并不是独立的。RNN之所以称为循环神经网路,即一个序列当前的输出与前面的输出也有关。具体的表现形式为网络会对前面的信息进行记忆并应用于当前输出的计算中,即隐藏层之间的节点不再无连接而是有连接的,并且隐藏层的输入不仅包括输入层的输出还包括上一时刻隐藏层的输出。如下图所示:
(点击放大图像)
LSTM技术原理RNN有一问题,反向传播时,梯度也会呈指数倍数的衰减,导致经过许多阶段传播后的梯度倾向于消失,不能处理长期依赖的问题。虽然RNN理论上可以处理任意长度的序列,但实习应用中,RNN很难处理长度超过10的序列。为了解决RNN梯度消失的问题,提出了Long Short-Term Memory模块,通过门的开关实现序列上的记忆功能,当误差从输出层反向传播回来时,可以使用模块的记忆元记下来。所以 LSTM 可以记住比较长时间内的信息。常见的LSTM模块如下图所示:
(点击放大图像)
(点击放大图像)
output gate类似于input gate同样会产生一个0-1向量来控制Memory Cell到输出层的输出,如下公式所示:
(点击放大图像)
三个门协作使得 LSTM 存储块可以存取长期信息,比如说只要输入门保持关闭,记忆单元的信息就不会被后面时刻的输入所覆盖。
使用TensorFlow构建单词预测模型首先下载PTB的模型数据,该数据集大概包含10000个不同的单词,并对不常用的单词进行了标注。
首先需要对样本数据集进行预处理,把每个单词用整数标注,即构建词典索引,如下所示:
读取训练数据 data = _read_words(filename) #按照单词出现频率,进行排序 counter = collections.Counter(data) count_pairs = sorted(counter.items(), key=lambda x: (-x1, x[0])) #构建词典及词典索引 words, _ = list(zip(*count_pairs)) word_to_id = dict(zip(words, range(len(words))))接着读取训练数据文本,把单词序列转换为单词索引序列,生成训练数据,如下所示:
读取训练数据单词,并转换为单词索引序列data = _read_words(filename) data = [word_to_id[word] for word in data if word in word_to_id]
生成训练数据的data和label,其中epoch_size为该epoch的训练迭代次数,num_steps为LSTM的序列长度 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]) 构建LSTM Cell,其中size为隐藏神经元的数量 lstm_cell = tf.contrib.rnn.BasicLSTMCell(size, forget_bias=0.0, state_is_tuple=True) 如果为训练模式,为保证训练鲁棒性,定义dropout操作 attn_cell = tf.contrib.rnn.DropoutWrapper(lstm_cell, output_keep_prob=config.keep_prob) 根据层数配置,定义多层RNN神经网络 cell = tf.contrib.rnn.MultiRNNCell( [ attn_cell for _ in range(config.num_layers)], state_is_tuple=True) 根据词典大小,定义词向量 embedding = tf.get_variable("embedding", [vocab_size, size], dtype=data_type()) 根据单词索引,查找词向量,如下图所示。从单词索引找到对应的One-hot encoding,然后红色的weight就直接对应了输出节点的值,也就是对应的embedding向量。 inputs = tf.nn.embedding_lookup(embedding, input_.input_data)(点击放大图像)