最近一直在尝试基于这篇文章实现RNNLM 。有一个带有一些 LSTM 分解技巧的实现,但类似于作者的原始实现。
序言
1)数据集被分割成文件,然后文件的行在训练时被打乱,在测试时被顺序输入。(链接):
# deterministic at test time, non deterministic at train time
if not self._deterministic:
random.shuffle(lines)
2)批次连续成型
* 符号代表句子的开始\结尾。每个矩阵代表一个巴赫。代码链接。所以:
如果句子长于 num_steps,则在同一行的下一批继续。
如果句子较短,则批处理行正在填充另一个句子。
3)他们计算批量平均损失。num_steps - LSTM 的内存。代码。
# loss_shape = [batch_size * num_steps]
# 1D tensor, reshaped 2d tensor with dims of [batch_size, num_steps]
loss = tf.reduce_mean(loss)
4) LSTM 单元在每次训练迭代后更新,并在评估时归零。
他们将其声明为局部变量声明。
5)在评估时间,作者以这种方式计算困惑度(链接):
for i, (x, y) in enumerate(data_iterator):
# get a batch
loss = sess.run(model.loss, {model.x: x, model.y: y})
loss_nom += loss
loss_den += 1
loss = loss_nom / loss_den
sys.stdout.write("%d: %.3f (%.3f) ... " % (i, loss, np.exp(loss)))
sys.stdout.flush()
sys.stdout.write("\n")
这意味着他们测量批量平均困惑度。
话虽如此,我有两个主要问题。
问题
- 考虑序言 1)、2) 和 4)。
为什么批次是这样形成的?
LSTM 单元不会在每个句子之后归零,因此它会保留前一个句子的记忆。
在顶部的示例中,当神经网络处理第 №1 批次时,第 2 行表示单词“Half”,它会记住单词 Music 和 start\end 标记的上下文。如果句子没有被打乱并且是真实的文本,这可能是有意义的,但是它们被打乱并且没有相互连接。
我实现了这两种方法,无限批次提供了更好的性能。
- 考虑序言 3) 和 5)。
为什么我们要估计批平均困惑度?
考虑到第一个问题,我不清楚当我们以这种方式测量困惑度时,我们真的可以估计我们的模型有多好。但是句子平均困惑似乎更有效。
如果我的逻辑有缺陷,如果您指出这一点,我将不胜感激。