【问题标题】:feeding data into an LSTM - Tensorflow RNN PTB tutorial将数据输入 LSTM - Tensorflow RNN PTB 教程
【发布时间】:2018-03-06 23:31:48
【问题描述】:

我正在尝试将数据输入 LSTM。我正在审查来自Tensorflow's RNN tutorial here 的代码。

感兴趣的代码段来自本教程的 reader.py 文件,特别是 ptb_producer 函数,它输出 LSTM 使用的 XY

raw_data 是来自ptb.train.txt 文件的单词索引列表。 raw_data 的长度为 929,589。 batch_size 是 20,num_steps 是 35。batch_sizenum_steps 都基于将数据提供给 LSTM 的 LARGEconfig

我已经浏览了代码(并为我打印的内容添加了 cmets),直到 tf.strided_slice 为止我都理解它。通过重塑,我们得到了一个形状为(20, 46497) 的索引矩阵。

i 的第一次迭代中的跨步切片,尝试从[0, i * num_steps + 1](即[0,1*35+1])获取数据,直到[batch_size, (i + 1) * num_steps + 1](即[20, (1+1)*35+1])。

两个问题:

  1. 矩阵中的哪个位置是[0,1*35+1][20, (1+1)*35+1]? strided_slice 中的开头和结尾(20, 46497) 中的哪些位置试图访问?

  2. 似乎i 的每次迭代都会从 0 获取数据?数据矩阵的最开始(20, 46497)

我想我不明白的是,考虑到批量大小和num_steps(序列长度),您将如何将数据输入 LSTM。

我已阅读 colahs blog on LSTMKarpathy's blog on RNN,这对理解 LSTM 有很大帮助,但似乎没有解决将数据输入 LSTM 的确切机制。 (也许我错过了什么?)

def ptb_producer(raw_data, batch_size, num_steps, name=None):
  """Iterate on the raw PTB data.
  This chunks up raw_data into batches of examples and returns Tensors that
  are drawn from these batches.
  Args:
    raw_data: one of the raw data outputs from ptb_raw_data.
    batch_size: int, the batch size.
    num_steps: int, the number of unrolls.
    name: the name of this operation (optional).
  Returns:
    A pair of Tensors, each shaped [batch_size, num_steps]. The second 
    element of the tuple is the same data time-shifted to the right by one.
  Raises:
    tf.errors.InvalidArgumentError: if batch_size or num_steps are too high.
  """
  with tf.name_scope(name, "PTBProducer", [raw_data, batch_size, num_steps]):
    raw_data = tf.convert_to_tensor(raw_data, name="raw_data", dtype=tf.int32)
    data_len = tf.size(raw_data)   # prints 929,589
    batch_len = data_len // batch_size   # prints 46,497
    data = tf.reshape(raw_data[0 : batch_size * batch_len],
                      [batch_size, batch_len])      
    #this truncates raw data to a multiple of batch_size=20, 
    #then reshapes to [20, 46497].  prints (20,?)

    epoch_size = (batch_len - 1) // num_steps   #prints 1327 (number of epoches)
    assertion = tf.assert_positive(
        epoch_size,
        message="epoch_size == 0, decrease batch_size or num_steps")
    with tf.control_dependencies([assertion]):
      epoch_size = tf.identity(epoch_size, name="epoch_size")

    i = tf.train.range_input_producer(epoch_size, shuffle=False).dequeue()
    #for each of the 1327 epoches
    x = tf.strided_slice(data, [0, i * num_steps], [batch_size, (i + 1) * num_steps])   # prints (?, ?) 
    x.set_shape([batch_size, num_steps])  #prints (20,35) 
    y = tf.strided_slice(data, [0, i * num_steps + 1], [batch_size, (i + 1) * num_steps + 1])
    y.set_shape([batch_size, num_steps])
    return x, y

【问题讨论】:

  • 我认为 Oriol Vinyals 编写了这段代码并希望它直接加载 Mikolov 使用的预处理 penn 树库文件。在我看来,这种加载数据的方法对于 TensorFlow 初学者来说并不容易理解。

标签: tensorflow lstm rnn


【解决方案1】:
  1. 矩阵的哪里是[0,1*35+1]和[20, (1+1)*35+1]? strided_slice 中的开头和结尾 (20, 46497) 中的哪些点试图访问?

所以数据矩阵排列为20 x 46497。可以把它想象成 20 个句子,每个句子有 46497 个字符。您的具体示例中的跨步切片将从每行获取字符 36 到 70(不包括 71,典型的范围语义)。这相当于

strided = [data[i][36:71] for i in range(20)]
  1. 似乎 i 的每次迭代都会从 0 获取数据?数据矩阵的最开始 (20, 46497)?

这是正确的。对于每次迭代,我们需要第一维中的批量大小元素,即我们想要一个大小为batch size x num_steps 的矩阵。因此,第一个维度总是从 0 到 19,本质上是循环遍历每个句子。并且在num_steps 中增加第二个维度,从每个句子中获取词段。如果我们查看它在每个步骤中为x 从数据中提取的索引,我们会得到如下信息:

- data[0:20,0:35]
- data[0:20,35:70]
- data[0:20,70:105]
- ...

对于y,我们加1,因为我们希望预测下一个字符。 所以你的火车,标签元组将是这样的:

- (data[0:20,0:35], data[0:20, 1:36])
- (data[0:20,35:70], data[0:20,36:71])
- (data[0:20,70:105], data[0:20,71:106])
- ...

【讨论】: