【问题标题】:Tensorflow dynamic_rnn propagates nans for batch size greater than 1Tensorflow dynamic_rnn 传播大于 1 的批大小的 nans
【发布时间】:2018-10-11 11:16:56
【问题描述】:

希望有人能帮助我理解我在 Tensorflow 中使用带有 dynamic_rnn 的 LSTM 时遇到的问题。根据这个MWE,当我的批量大小为1且序列不完整时(我用nan填充短张量而不是零以突出显示)一切正常运行,短序列中的nan按预期被忽略.. .

import tensorflow as tf
import numpy as np

batch_1 = np.random.randn(1, 10, 8)
batch_2 = np.random.randn(1, 10, 8)

batch_1[6:] = np.nan # lets make a short batch in batch 1 second sample of length 6 by padding with nans

seq_lengths_batch_1 = [6]
seq_lengths_batch_2 = [10]

tf.reset_default_graph()

input_vals = tf.placeholder(shape=[1, 10, 8], dtype=tf.float32)
lengths = tf.placeholder(shape=[1], dtype=tf.int32)

cell = tf.nn.rnn_cell.LSTMCell(num_units=5)
outputs, states  = tf.nn.dynamic_rnn(cell=cell, dtype=tf.float32, sequence_length=lengths, inputs=input_vals)
last_relevant_value = states.h
fake_loss = tf.reduce_mean(last_relevant_value)
optimizer = tf.train.AdamOptimizer(learning_rate=0.001).minimize(fake_loss)

sess = tf.InteractiveSession()
sess.run(tf.global_variables_initializer())
_, fl, lrv = sess.run([optimizer, fake_loss, last_relevant_value], feed_dict={input_vals: batch_1, lengths: seq_lengths_batch_1})
print(fl, lrv)
_, fl, lrv = sess.run([optimizer, fake_loss, last_relevant_value], feed_dict={input_vals: batch_2, lengths: seq_lengths_batch_2})
print(fl, lrv)

sess.close()

输出正确填充的同类值......

0.00659429 [[ 0.11608966  0.08498846 -0.02892204 -0.01945034 -0.1197343 ]]
-0.080244 [[-0.03018401 -0.18946587 -0.19128899 -0.10388547  0.11360413]]

但是,例如,当我将批量大小增加到 3 时,第一批正确执行,但不知何故,第二批导致 nans 开始传播

import tensorflow as tf
import numpy as np

batch_1 = np.random.randn(3, 10, 8)
batch_2 = np.random.randn(3, 10, 8)

batch_1[1, 6:] = np.nan 
batch_2[0, 8:] = np.nan 

seq_lengths_batch_1 = [10, 6, 10]
seq_lengths_batch_2 = [8, 10, 10]

tf.reset_default_graph()

input_vals = tf.placeholder(shape=[3, 10, 8], dtype=tf.float32)
lengths = tf.placeholder(shape=[3], dtype=tf.int32)

cell = tf.nn.rnn_cell.LSTMCell(num_units=5)
outputs, states  = tf.nn.dynamic_rnn(cell=cell, dtype=tf.float32, sequence_length=lengths, inputs=input_vals)
last_relevant_value = states.h
fake_loss = tf.reduce_mean(last_relevant_value)
optimizer = tf.train.AdamOptimizer(learning_rate=0.001).minimize(fake_loss)

sess = tf.InteractiveSession()
sess.run(tf.global_variables_initializer())
_, fl, lrv = sess.run([optimizer, fake_loss, last_relevant_value], feed_dict={input_vals: batch_1, lengths: seq_lengths_batch_1})
print(fl, lrv)
_, fl, lrv = sess.run([optimizer, fake_loss, last_relevant_value], feed_dict={input_vals: batch_2, lengths: seq_lengths_batch_2})
print(fl, lrv)

sess.close()

给予

0.0533635 [[ 0.33622459 -0.0284576   0.11914439  0.14402215 -0.20783389]
 [ 0.20805927  0.17591488 -0.24977767 -0.03432769  0.2944448 ]
 [-0.04508523  0.11878576  0.07287208  0.14114542 -0.24467923]]
nan [[ nan  nan  nan  nan  nan]
 [ nan  nan  nan  nan  nan]
 [ nan  nan  nan  nan  nan]]

我发现这种行为很奇怪,因为我预计序列长度之后的所有值都会被忽略,就像批量大小为 1 时发生的那样,但不适用于批量大小为 2 或更大的情况。

显然,如果我使用 0 作为我的填充值,nans 不会被传播,但这并不能激发我对 dynamic_rnn 按我期望的那样运行的信心。

另外我应该提到,如果我删除优化步骤,问题就不会发生,所以现在我很困惑,经过一天尝试许多不同的排列,我不明白为什么批量大小会在这里产生任何影响

【问题讨论】:

    标签: tensorflow lstm recurrent-neural-network


    【解决方案1】:

    我没有追查到确切的操作,但这是我认为的情况。

    为什么sequence_length 以外的值不被忽略? 在执行某些操作时,它们被乘以0(它们被屏蔽)的意义上被忽略。从数学上讲,结果始终为零,因此它们应该没有效果。不幸的是,nan * 0 = nan。因此,如果您在示例中提供 nan 值,它们就会传播。您可能想知道为什么 TensorFlow 没有完全忽略它们,而只是掩盖了它们。原因是现代硬件的性能。对带有一堆零的大型规则形状进行操作比对几个小形状(通过分解不规则形状获得)进行操作要容易得多。

    为什么它只发生在第二批?在第一批中,损失和最后隐藏状态是使用原始变量值计算的。他们很好。因为您还在sess.run() 中执行优化器更新,所以变量会在第一次调用中更新并变为nan。在第二次调用中,来自变量的nans 传播到损失和隐藏状态。

    我如何确信 sequence_length 之外的值真的被掩盖了?我修改了您的示例以重现该问题,但也使其具有确定性。

    import tensorflow as tf
    import numpy as np
    
    batch_1 = np.ones((3, 10, 2))
    
    batch_1[1, 7:] = np.nan
    
    seq_lengths_batch_1 = [10, 7, 10]
    
    tf.reset_default_graph()
    
    input_vals = tf.placeholder(shape=[3, 10, 2], dtype=tf.float32)
    lengths = tf.placeholder(shape=[3], dtype=tf.int32)
    
    cell = tf.nn.rnn_cell.LSTMCell(num_units=3, initializer=tf.constant_initializer(1.0))
    init_state = tf.nn.rnn_cell.LSTMStateTuple(*[tf.ones([3, c]) for c in cell.state_size])
    outputs, states  = tf.nn.dynamic_rnn(cell=cell, dtype=tf.float32, sequence_length=lengths, inputs=input_vals,
            initial_state=init_state)
    last_relevant_value = states.h
    fake_loss = tf.reduce_mean(last_relevant_value)
    optimizer = tf.train.AdamOptimizer(learning_rate=0.1).minimize(fake_loss)
    
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        for _ in range(1):
            _, fl, lrv = sess.run([optimizer, fake_loss, last_relevant_value],
                    feed_dict={input_vals: batch_1, lengths: seq_lengths_batch_1})
            print "VARIABLES:", sess.run(tf.trainable_variables())
            print "LOSS and LAST HIDDEN:", fl, lrv
    

    如果您将batch_1[1, 7:] = np.nan 中的np.nan 替换为任意数字(例如尝试-1M、1M、0),您将看到您得到的值是相同的。您还可以运行循环进行更多迭代。作为进一步的健全性检查,如果您将 seq_lengths_batch_1 设置为“错误”,例如[10, 8, 10],您可以看到现在您在batch_1[1, 7:] = np.nan 中使用的值会影响输出。

    【讨论】:

    • 非常感谢您抽出宝贵时间审阅并提供如此详细的解释。现在对我来说更有意义了。特别是您使用 LSTMStateTuples 的确定性示例非常有用。
    猜你喜欢
    • 2017-08-05
    • 1970-01-01
    • 2018-02-21
    • 2018-12-24
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    • 2018-06-27
    • 1970-01-01
    相关资源
    最近更新 更多