11

У меня есть трехмерный тензор формы [batch, None, dim], где второе измерение, то есть временные метки, неизвестно. Я использую dynamic_rnn обрабатывать такой ввод, как в следующем фрагменте:Получить последний выход dynamic_rnn в TensorFlow

import numpy as np 
import tensorflow as tf 

batch = 2 
dim = 3 
hidden = 4 

lengths = tf.placeholder(dtype=tf.int32, shape=[batch]) 
inputs = tf.placeholder(dtype=tf.float32, shape=[batch, None, dim]) 
cell = tf.nn.rnn_cell.GRUCell(hidden) 
cell_state = cell.zero_state(batch, tf.float32) 
output, _ = tf.nn.dynamic_rnn(cell, inputs, lengths, initial_state=cell_state) 

На самом деле, работает это пропущено с некоторыми фактическими цифрами, у меня есть некоторые разумные результаты:

inputs_ = np.asarray([[[0, 0, 0], [1, 1, 1], [2, 2, 2], [3, 3, 3]], 
        [[6, 6, 6], [7, 7, 7], [8, 8, 8], [9, 9, 9]]], 
        dtype=np.int32) 
lengths_ = np.asarray([3, 1], dtype=np.int32) 

with tf.Session() as sess: 
    sess.run(tf.global_variables_initializer()) 
    output_ = sess.run(output, {inputs: inputs_, lengths: lengths_}) 
    print(output_) 

И выход:

[[[ 0.   0.   0.   0.  ] 
    [ 0.02188676 -0.01294564 0.05340237 -0.47148666] 
    [ 0.0343586 -0.02243731 0.0870839 -0.89869428] 
    [ 0.   0.   0.   0.  ]] 

[[ 0.00284752 -0.00315077 0.00108094 -0.99883419] 
    [ 0.   0.   0.   0.  ] 
    [ 0.   0.   0.   0.  ] 
    [ 0.   0.   0.   0.  ]]] 

есть ли способ, чтобы получить тензор 3-D в форме [batch, 1, hidden] с последнего соответствующего выхода динамического RNN? Благодаря!

+1

Мне нравится ваш стиль кода! Это отличный вопрос для изучения того, как работает nn. спасибо –

+0

Принимаемый ответ не является предпочтительным способом сделать это. Посмотрите на ответ @ ShaoTang в конце. – Rahul

+0

@Rahul спасибо, что указал. Во всяком случае, похоже, что ShaoTang указывает на состояния, в то время как вопрос конкретно касается результатов. Или я чего-то не хватает? – petrux

ответ

7

Это то, что 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 

В вашем случае:

output = extract_axis_1(output, lengths - 1) 

Теперь output есть тензор размерности [batch_size, num_cells].

+0

Отлично! Я попытался отредактировать ответ и добавить весь код, чтобы он был автономным, но у меня было странное сообщение о том, что «очередь редактирования заполнена». В любом случае, спасибо за ответ! – petrux

1

На самом деле, решение было не так сложно. Я осуществил следующий код:

slices = [] 
for index, l in enumerate(tf.unstack(lengths)): 
    slice = tf.slice(rnn_out, begin=[index, l - 1, 0], size=[1, 1, 3]) 
    slices.append(slice) 
last = tf.concat(0, slices) 

Таким образом, полный фрагмент кода будет следующее:

import numpy as np 
import tensorflow as tf 

batch = 2 
dim = 3 
hidden = 4 

lengths = tf.placeholder(dtype=tf.int32, shape=[batch]) 
inputs = tf.placeholder(dtype=tf.float32, shape=[batch, None, dim]) 
cell = tf.nn.rnn_cell.GRUCell(hidden) 
cell_state = cell.zero_state(batch, tf.float32) 
output, _ = tf.nn.dynamic_rnn(cell, inputs, lengths, initial_state=cell_state) 

inputs_ = np.asarray([[[0, 0, 0], [1, 1, 1], [2, 2, 2], [3, 3, 3]], 
        [[6, 6, 6], [7, 7, 7], [8, 8, 8], [9, 9, 9]]], 
        dtype=np.int32) 
lengths_ = np.asarray([3, 1], dtype=np.int32) 

slices = [] 
for index, l in enumerate(tf.unstack(lengths)): 
    slice = tf.slice(output, begin=[index, l - 1, 0], size=[1, 1, 3]) 
    slices.append(slice) 
last = tf.concat(0, slices) 

with tf.Session() as sess: 
    sess.run(tf.global_variables_initializer()) 
    outputs = sess.run([output, last], {inputs: inputs_, lengths: lengths_}) 
    print 'RNN output:' 
    print(outputs[0]) 
    print 
    print 'last relevant output:' 
    print(outputs[1]) 

И вывод:

RNN output: 
[[[ 0.   0.   0.   0.  ] 
[-0.06667092 -0.09284072 0.01098599 -0.03676109] 
[-0.09101103 -0.19828682 0.03546784 -0.08721405] 
[ 0.   0.   0.   0.  ]] 

[[-0.00025157 -0.05704876 0.05527233 -0.03741353] 
[ 0.   0.   0.   0.  ] 
[ 0.   0.   0.   0.  ] 
[ 0.   0.   0.   0.  ]]] 

last relevant output: 
[[[-0.09101103 -0.19828682 0.03546784]] 

[[-0.00025157 -0.05704876 0.05527233]]] 
+1

Я бы изменил tf.unpack() с помощью tf.unstacks(), поскольку tf.unpack устарел: https://www.tensorflow.org/api_docs/python/array_ops/slicing_and_joining # unpack – pabaldonedo

+0

вы также можете избежать цикла for, используя '' 'tf.gather''', поскольку он комментируется здесь http://stackoverflow.com/questions/36088277/how-to-select-rows-from-a -3-d-тензор-в-тензорном потоке и адаптирован для этой же проблемы здесь: http://stackoverflow.com/questions/41267829/retrieving-last-value-of-lstm-sequence-in-tensorflow/41273843# 41273843 – pabaldonedo

+0

Звучит здорово! Я обновлю ответ, как только смогу! – petrux

4

Из следующих двух источников,

http://www.wildml.com/2016/08/rnns-in-tensorflow-a-practical-guide-and-undocumented-features/

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

Или https://github.com/ageron/handson-ml/blob/master/14_recurrent_neural_networks.ipynb,

Очевидно, что last_states могут быть непосредственно извлечены из второго выхода вызова dynamic_rnn. Он даст вам last_states через все слоев (в LSTM он компилируется из LSTMStateTuple), а выходы содержат все состояния в последнем слое.

+3

, если документация была более понятной ... – Escachator

+0

бог я тоже досадуюсь до конца документацией tensorflow – Nitin

+0

Если вы используете выпад, он будет применен к FIRST OUTPUT, но не к SECOND OUTPUT динамического_rnn возврата. см. [здесь] (https://groups.google.com/a/tensorflow.org/forum/#!topic/discuss/EhZ6uWxpFzc) – Nitin

0

Хорошо, так выглядит, на самом деле является более простым решением. Как отмечали @Shao Tang и @Rahul, предпочтительным способом сделать это будет доступ к окончательному состоянию ячейки. Вот почему:

  • Если вы посмотрите на исходный код GRUCell (см. Ниже), вы увидите, что «состояние», которое поддерживает ячейка, на самом деле является скрытым весом. . Таким образом, когда tf.nn.dynamic_rnn возвращает конечное состояние, он на самом деле возвращения окончательных скрытые весов, что вы заинтересованы в Чтобы доказать это, я просто переделана вашу установку и получил результаты:

GRUCell Call (rnn_cell_impl.ру):

def call(self, inputs, state): 
"""Gated recurrent unit (GRU) with nunits cells.""" 
if self._gate_linear is None: 
     bias_ones = self._bias_initializer 
if self._bias_initializer is None: 
     bias_ones = init_ops.constant_initializer(1.0, dtype=inputs.dtype) 
with vs.variable_scope("gates"): # Reset gate and update gate. 
self._gate_linear = _Linear(
      [inputs, state], 
2 * self._num_units, 
True, 
bias_initializer=bias_ones, 
kernel_initializer=self._kernel_initializer) 
    value = math_ops.sigmoid(self._gate_linear([inputs, state])) 
    r, u = array_ops.split(value=value, num_or_size_splits=2, axis=1) 
    r_state = r * state 
if self._candidate_linear is None: 
with vs.variable_scope("candidate"): 
self._candidate_linear = _Linear(
      [inputs, r_state], 
self._num_units, 
True, 
bias_initializer=self._bias_initializer, 
kernel_initializer=self._kernel_initializer) 
    c = self._activation(self._candidate_linear([inputs, r_state])) 
    new_h = u * state + (1 - u) * c 
return new_h, new_h 

Решение:

import numpy as np 
import tensorflow as tf 

batch = 2 
dim = 3 
hidden = 4 

lengths = tf.placeholder(dtype=tf.int32, shape=[batch]) 
inputs = tf.placeholder(dtype=tf.float32, shape=[batch, None, dim]) 
cell = tf.nn.rnn_cell.GRUCell(hidden) 
cell_state = cell.zero_state(batch, tf.float32) 
output, state = tf.nn.dynamic_rnn(cell, inputs, lengths, initial_state=cell_state) 

inputs_ = np.asarray([[[0, 0, 0], [1, 1, 1], [2, 2, 2], [3, 3, 3]], 
        [[6, 6, 6], [7, 7, 7], [8, 8, 8], [9, 9, 9]]], 
        dtype=np.int32) 
lengths_ = np.asarray([3, 1], dtype=np.int32) 

with tf.Session() as sess: 
    sess.run(tf.global_variables_initializer()) 
    output_, state_ = sess.run([output, state], {inputs: inputs_, lengths: lengths_}) 
    print (output_) 
    print (state_) 

Выход:

[[[ 0.   0.   0.   0.  ] 
    [-0.24305521 -0.15512943 0.06614969 0.16873555] 
    [-0.62767833 -0.30741733 0.14819752 0.44313088] 
    [ 0.   0.   0.   0.  ]] 

[[-0.99152333 -0.1006391 0.28767768 0.76360202] 
    [ 0.   0.   0.   0.  ] 
    [ 0.   0.   0.   0.  ] 
    [ 0.   0.   0.   0.  ]]] 
[[-0.62767833 -0.30741733 0.14819752 0.44313088] 
[-0.99152333 -0.1006391 0.28767768 0.76360202]] 
  • Для других читателей, которые работают с LSTMCell (еще один популярный вариант), все работает немного иначе. LSTMCell поддерживает состояние по-другому - состояние ячеек является либо кортежем, либо конкатенированной версией фактического состояния ячейки и скрытого состояния. Таким образом, для доступа к конечным скрытым весам вы можете установить (is_state_tuple на True) во время инициализации ячейки, а конечным состоянием будет кортеж: (конечное состояние ячейки, окончательные скрытые веса). Таким образом, в этом случае,

    _ (_, ч) = tf.nn.dynamic_rnn (ячейка, входы, длины, initial_state = cell_state)

даст вам окончательные веса.

Ссылки: c_state and m_state in Tensorflow LSTM https://github.com/tensorflow/tensorflow/blob/438604fc885208ee05f9eef2d0f2c630e1360a83/tensorflow/python/ops/rnn_cell_impl.py#L308 https://github.com/tensorflow/tensorflow/blob/438604fc885208ee05f9eef2d0f2c630e1360a83/tensorflow/python/ops/rnn_cell_impl.py#L415

Смежные вопросы