jlearning.cn

TensorFlow-touorials-rnn

语言模型

在这个教程中,我们将会展示如何在一个具有挑战的语言模型上训练循环神经网络。这个问题的目的是为了拟合一个给句子分配概率的概率模型。它通过根据前面的历史单词预测下一个单词来坐到这一点。为此,我们使用Penn Tree Bank数据集,该数据集是衡量这个模型质量的基准,同时小巧方便训练。

语言模型时很多有趣问题的关键,比如说语音识别、机器翻译、图片字幕。参考这里

为了达到目的,我们将重现Zaremba et al., 2014pdf)中的结果,他们在PTB数据集上取得了很好的效果。

教程文件

这个教程依赖TensorFlow models repomodels/tutorials/rnn/ptb下的文件:

文件 作用
ptb_word_lm.py 在PTB数据集上训练语言模型的代码
reader.py 读取数据集的代码

下载以及准备数据

这个教程需要的数据在PTB数据集的data/文件夹里面,来自Toms Mikolov’s的页面:http://www.fit.vutbr.cz/~imikolov/rnnlm/simple-examples.tgz

这个数据集已经预处理并包含了超过10000个不同的单词,包括句尾标记符号和稀有单词的特殊字符。在reader.py中,我们将每个单词转换为一个唯一的整数标记,为了让他在神经网络中处理起来更容易。

模型

LSTM

模型的核心包括一次处理一个单词的LSTM单元,并计算句子中下一个单词的可能值的概率。网络中的存储状态被初始化为零向量,在读取每一个单词后更新。为了计算,我们将以batch_size的小批进行处理。

伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
lstm = tf.contrib.rnn.BasicLSTMCell(lstm_size)
# Initial state of the LSTM memory.
state = tf.zeros([batch_size, lstm.state_size])
probabilities = []
loss = 0.0
for current_batch_of_words in words_in_dataset:
# The value of state is updated after processing each batch of words.
output, state = lstm(current_batch_of_words, state)
# The LSTM output can be used to make next word predictions
logits = tf.matmul(output, softmax_w) + softmax_b
probabilities.append(tf.nn.softmax(logits))
loss += loss_function(probabilities, target_words)

截断反向传播

依据设计,RNN的输出取决于不同的输入。不幸的是,这让反向传播算法计算困难。为了让学习过程易于控制,通常创建一个“unrolled”版本的网络,其包含了固定数量(num_steps)的LSTM输入和输出。然后对RNN进行有限的近似运算。这可以通过一次喂长度为num_steps的输入,在每次这样输入后执行反向过程来实现。

这是一个同于创建执行截断反向传播的图的简略代码块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Placeholder for the inputs in a given iteration.
words = tf.placeholder(tf.int32, [batch_size, num_steps])
lstm = tf.contrib.rnn.BasicLSTMCell(lstm_size)
# Initial state of the LSTM memory.
initial_state = state = tf.zeros([batch_size, lstm.state_size])
for i in range(num_steps):
# The value of state is updated after processing each batch of words.
output, state = lstm(words[:, i], state)
# The rest of the code.
# ...
final_state = state

如何实现在整个数据集上面的迭代:

1
2
3
4
5
6
7
8
# A numpy array holding the state of LSTM after each batch of words.
numpy_state = initial_state.eval()
total_loss = 0.0
for current_batch_of_words in words_in_dataset:
numpy_state, current_loss = session.run([final_state, loss],
# Initialize the LSTM state from the previous iteration.
feed_dict={initial_state: numpy_state, words: current_batch_of_words})
total_loss += current_loss

输入

词汇ID会被嵌入到密度表示,在喂给LSTM之前。这允许模型有效的表示某一单词的知识。实现起来也很简单:

1
2
# embedding_matrix is a tensor of shape [vocabulary_size, embedding size]
word_embeddings = tf.nn.embedding_lookup(embedding_matrix, word_ids)

这个嵌入矩阵将会被随机初始化,模型将会学习区分这些单词的意义通过观察数据。

损失函数

我们想最小化目标词汇的平均负log概率。

$\text{loss} = -\frac{1}{N}\sum{i=1}^{N} \ln p{\text{target}_i}$

实现并不困难,但是函数sequence_loss_by_example已经被提供,所以我们直接在这里使用就可以。

论文中提到的的典型方法是平均每个词的复杂度:

$e^{-\frac{1}{N}\sum{i=1}^{N} \ln p{\text{target}_i}} = e^{\text{loss}}$

我们会在训练过程中见识这一个值。

叠加多个LSTM

提供的模型很强大,我们可以增加LSTM层数去处理数据。第一层的输出将变成第二层的输入。

我们有一个叫做MultiRNNCell的类,无缝实现这些。

1
2
3
4
5
6
7
8
9
10
11
12
13
lstm = tf.contrib.rnn.BasicLSTMCell(lstm_size, state_is_tuple=False)
stacked_lstm = tf.contrib.rnn.MultiRNNCell([lstm] * number_of_layers,
state_is_tuple=False)
initial_state = state = stacked_lstm.zero_state(batch_size, tf.float32)
for i in range(num_steps):
# The value of state is updated after processing each batch of words.
output, state = stacked_lstm(words[:, i], state)
# The rest of the code.
# ...
final_state = state

运行代码

教程代码中支持三种模型,”small”,”medium”和”large”,他们的区别是LSTM的大小,和训练使用的超参数集的大小。

模型越大,结果越好。small模型应该可以达到120以下的复杂度在测试集中,large80以下。可能需要几个小时去训练。