如何在tensorflow中获取dynamic_rnn的最后一次输出?

17

我正在使用dynamic_rnn来处理MNIST数据:

# LSTM Cell
lstm = rnn_cell.LSTMCell(num_units=200,
                         forget_bias=1.0,
                         initializer=tf.random_normal)

# Initial state
istate = lstm.zero_state(batch_size, "float")

# Get lstm cell output
output, states = rnn.dynamic_rnn(lstm, X, initial_state=istate)

# Output at last time point T
output_at_T = output[:, 27, :]

完整代码:http://pastebin.com/bhf9MgMe

LSTM的输入是(batch_size, sequence_length, input_size)

因此,output_at_T 的维度为 (batch_size, sequence_length, num_units),其中 num_units=200

我需要获取沿着 sequence_length 维度的最后一个输出。在上面的代码中,这是硬编码为 27。然而,在我的应用程序中,我不知道 sequence_length 的先验知识,因为它可以从批次到批次改变。

我尝试了:

output_at_T = output[:, -1, :]

但是它说负索引尚未实现,我尝试使用占位符变量以及常量(可以理想地将sequence_length馈送到特定批次中);但是两者都没有起作用。

有没有办法在tensorflow中实现这样的功能?


1
你的序列长度相等吗? - danijar
可能是 在 TensorFlow 中获取 dynamic_rnn 的最后输出 的重复问题。 - Alex
7个回答

13

你是否注意到dynamic_rnn有两个输出?

  1. 第一个输出,我们称之为h,它包含每个时间步的所有输出(如h_1,h_2等)。
  2. 第二个输出是final_state,包括两个元素:cell_state和每个批次元素的最后一个输出(只要您将序列长度输入到dynamic_rnn中)。

因此:

h, final_state= tf.dynamic_rnn( ..., sequence_length=[batch_size_vector], ... )

每个批次中的元素的最后状态为:

final_state.h
请注意,这包括批处理中每个元素的序列长度不同的情况,因为我们使用了sequence_length参数。

1
final_state.h返回的是最后一个输出还是最后一个隐藏状态 - bluesummers
1
@bluesummers final_state.h 是批处理中每个元素的最终激活状态(即最终输出),final_state.c 是单元格状态。 - Escachator
请问 final_state.h 的维度是 [batch_size, lstm_units] 吗? - bluesummers

5
这就是gather_nd的用途!
def extract_axis_1(data, ind):
    """
    Get specified elements along the first axis of tensor.
    :param data: Tensorflow tensor that will be subsetted.
    :param ind: Indices to take (one for each element along axis 0 of data).
    :return: Subsetted tensor.
    """

    batch_range = tf.range(tf.shape(data)[0])
    indices = tf.stack([batch_range, ind], axis=1)
    res = tf.gather_nd(data, indices)

    return res

在您的情况下(假设sequence_length是一个1-D张量,其长度为每个轴0元素的长度):
output = extract_axis_1(output, sequence_length - 1)

现在输出是一个维度为[batch_size, num_cells]的张量。

1
不需要这样做,dynamic_rnn的输出已经包含了最后一个状态。请查看我的回答。 - Escachator
如果你知道(或获取)'ind',你就知道了批处理中每个元素的序列长度。然后,您可以将其输入到dynamic_rnn的'sequence_length'参数中,并且它将在批处理的每个元素上到达那里时停止计算。然后,您只需取final_state.h即可。我可能应该将其添加到我的答案中。 - Escachator
你可以开发一个函数,给定一个批次,返回每个元素的序列长度。我假设你正在进行填充。即使使用新的tf.data.Dataset API和map函数,你也可以这样做。然后将其输入到tf.dynamic_rnn的sequence_length中,就完成了。它甚至可能更快,因为dynamic_rnn一旦到达元素结尾就会停止计算。 - Escachator
我们正在讨论如何获得一批数据内不同时间步发生的输出的符号表示。sequence_length不能帮助实现此操作。请参考我之前发布的链接the link,并尝试自己运行问题中的示例。sequence_length只会将给定长度之外的输出归零。 - Alex
我看到了你的回答价值,实际上我用了一段时间。然而问题是关于对于每个批次中的每个元素取最后一个激活状态,其中每个元素具有任意大小。问题不是要取激活向量的某个任意元素。因此,最简单的方法是使用sequence_length并取final_state.h。 - Escachator
显示剩余4条评论

3
output[:, -1, :]

现在可以与Tensorflow 1.x一起使用了!


1
只有当output中的每个元素的sequence_length相同时,此方法才有效。 - Alex
这个问题特别涉及到动态长度,而这种方法不适用。 - Jules G.M.

2

大多数答案已经详细解释了,但是这段代码可能有助于理解dynamic_rnn层实际返回的内容:

=> 元组(outputs, final_output_state)

因此,对于长度为T的时间步的输入,当time_major=False(默认值)时,outputs的形状为[Batch_size, T, num_inputs],其中包含每个时间步骤h1、h2、.....、hT的输出状态。

final_output_state的形状为[Batch_size,num_inputs],对于每个批次序列,它都具有最终的单元格状态cT和输出状态hT

但是由于正在使用dynamic_rnn,因此我猜测每个批次的序列长度会有所不同。

    import tensorflow as tf
    import numpy as np
    from tensorflow.contrib import rnn
    tf.reset_default_graph()

    # Create input data
    X = np.random.randn(2, 10, 8)

    # The second example is of length 6 
    X[1,6:] = 0
    X_lengths = [10, 6]

    cell = tf.nn.rnn_cell.LSTMCell(num_units=64, state_is_tuple=True)

    outputs, states  = tf.nn.dynamic_rnn(cell=cell,
                                         dtype=tf.float64,
                                         sequence_length=X_lengths,
                                         inputs=X)

    result = tf.contrib.learn.run_n({"outputs": outputs, "states":states},
                                    n=1,
                                    feed_dict=None)
    assert result[0]["outputs"].shape == (2, 10, 64)
    print result[0]["outputs"].shape
    print result[0]["states"].h.shape
    # the final outputs state and states returned must be equal for each      
    # sequence
    assert(result[0]["outputs"][0][-1]==result[0]["states"].h[0]).all()
    assert(result[0]["outputs"][-1][5]==result[0]["states"].h[-1]).all()
    assert(result[0]["outputs"][-1][-1]==result[0]["states"].h[-1]).all()

由于第二个序列的最终状态在第6个时间步骤即索引5处,而来自[6:9]的其余输出在第二个时间步骤中均为0,因此最终的断言将失败。


1

我是Stackoverflow的新手,还不能评论,所以我写了这个新答案。@VM_AI,最后一个索引是tf.shape(output)[1] - 1。因此,重新使用你的答案:

# Let's first fetch the last index of seq length
# last_index would have a scalar value
last_index = tf.shape(output)[1] - 1
# Then let's reshape the output to [sequence_length,batch_size,num_units]
# for convenience
output_rs = tf.transpose(output,[1,0,2])
# Last state of all batches
last_state = tf.nn.embedding_lookup(output_rs,last_index)

这对我来说有效。

问题要求的是最后一个动态状态,而不是数组中的最后一个状态。 - Jules G.M.

0

您可以使用tf.shape(output)访问您的output张量的形状。 tf.shape()函数将返回一个包含output张量大小的1d张量。在您的示例中,这将是(batch_size, sequence_length, num_units)

然后,您应该能够提取output_at_T的值,如下所示:output[:, tf.shape(output)[1], :]


0
在TensorFlow中有一个函数tf.shape,它允许您获取形状的符号解释,而不是通过output._shape[1]返回None。在获取最后一个索引后,您可以使用tf.nn.embedding_lookup进行查找,特别是当要获取的数据很多时,这是推荐的方法,因为它可以并行查找,默认情况下是32
# Let's first fetch the last index of seq length
# last_index would have a scalar value
last_index = tf.shape(output)[1] 
# Then let's reshape the output to [sequence_length,batch_size,num_units]
# for convenience
output_rs = tf.transpose(output,[1,0,2])
# Last state of all batches
last_state = tf.nn.embedding_lookup(output_rs,last_index)

这应该可以工作。

只是为了澄清@Benoit Steiner所说的。他的解决方案不起作用,因为tf.shape将返回形状值的符号解释,这样不能用于切片张量,即直接索引。


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