使用TensorFlow dynamic_rnn时如何处理填充(padding)与sequence_length参数相关的问题

4
我正在尝试使用Tensorflow中的dynamic_rnn函数来加速训练。经过一些阅读,我了解到加速训练的一种方法是在此函数中明确地传递一个值给sequence_length参数。在进一步阅读并找到这个SO解释后,似乎我需要传递的是一个向量(可能由tf.placeholder定义),其中包含批次内每个序列的长度。
以下是我困惑的地方:为了利用这一点,我是否应将每个批次都填充到批次内最长序列的长度而不是训练集中最长序列的长度?Tensorflow如何处理任何较短序列中的剩余零/pad-token?此处的主要优势真正是速度,还是额外保证我们在训练期间屏蔽pad-token?如果有帮助或背景,请告诉我。
1个回答

8
我应该将每个批次的填充长度设为批次中最长序列的长度,而不是训练集中最长序列的长度吗?
一个批次内的序列必须对齐,也就是说它们必须具有相同的长度。所以你的问题的一般答案是“是”。但是不同的批次不必具有相同的长度,因此您可以将输入序列分成具有大致相同大小的组,并进行填充。这个技巧被称为,您可以在本教程中了解更多信息。
Tensorflow如何处理任何较短序列中的剩余零/填充标记?
非常直观。tf.nn.dynamic_rnn返回两个张量:outputstates。 假设实际序列长度为t,填充序列长度为T。
那么output将在t < i ≤ T时包含零,忽略尾部单元格的状态,states将包含第t个单元格状态。
以下是一个例子:
import numpy as np
import tensorflow as tf

n_steps = 2
n_inputs = 3
n_neurons = 5

X = tf.placeholder(dtype=tf.float32, shape=[None, n_steps, n_inputs])
seq_length = tf.placeholder(tf.int32, [None])

basic_cell = tf.nn.rnn_cell.BasicRNNCell(num_units=n_neurons)
outputs, states = tf.nn.dynamic_rnn(basic_cell, X, 
                                    sequence_length=seq_length, dtype=tf.float32)

X_batch = np.array([
  # t = 0      t = 1
  [[0, 1, 2], [9, 8, 7]], # instance 0
  [[3, 4, 5], [0, 0, 0]], # instance 1
  [[6, 7, 8], [6, 5, 4]], # instance 2
])
seq_length_batch = np.array([2, 1, 2])

with tf.Session() as sess:
  sess.run(tf.global_variables_initializer())
  outputs_val, states_val = sess.run([outputs, states], feed_dict={
    X: X_batch, 
    seq_length: seq_length_batch
  })
  print(outputs_val)
  print()
  print(states_val)

请注意,实例1被填充了,因此outputs_val[1,1]是一个零向量,且states_val[1] == outputs_val[1,0]
[[[ 0.76686853  0.8707901  -0.79509073  0.7430128   0.63775384]
  [ 1.          0.7427926  -0.9452815  -0.93113345 -0.94975543]]

 [[ 0.9998851   0.98436266 -0.9620067   0.61259484  0.43135557]
  [ 0.          0.          0.          0.          0.        ]]

 [[ 0.99999994  0.9982034  -0.9934515   0.43735617  0.1671598 ]
  [ 0.99999785 -0.5612586  -0.57177305 -0.9255771  -0.83750355]]]

[[ 1.          0.7427926  -0.9452815  -0.93113345 -0.94975543]
 [ 0.9998851   0.98436266 -0.9620067   0.61259484  0.43135557]
 [ 0.99999785 -0.5612586  -0.57177305 -0.9255771  -0.83750355]]

此外,这里的主要优势真的是速度吗,还是只是在训练过程中额外保证了我们掩盖填充令牌?

当然,批处理比逐个输入序列更有效率。但指定长度的主要优势在于您可以从RNN中获得合理的状态,即填充项不会影响结果张量。如果您不设置长度而手动选择正确的状态,则可以获得完全相同的结果(和相同的速度)。


嗨,@Maxim,我尝试了你的示例代码,但是将seq_length_batch分配值时触发了一些错误,即** ValueError: setting an array element with a sequence **。我在想我们是否应该使用数组np.array [1,2,1]来分配seq_length_batch的值。 - Yongfeng

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接