индексировать список внутри функции TensorFlow while_loop - PullRequest
0 голосов
/ 24 мая 2018

Здравствуйте.

У меня есть проблема.На самом деле у меня есть список ( реальный список Python ) заполнителей.Мой список имеет длину n (= T в приведенном ниже коде) и выглядит следующим образом:

my_list = [[D0, K], [D1, K], ... [Dn, K]]

Где Di не нужны одинакового размера,Вот почему я использовал список (потому что я не могу преобразовать этот список в тензор без заполнения)

Я хочу сделать следующее:

temp = []
for step in range(T):
    temp.append(tf.reduce_sum(x[step], axis=0))

sum_vn_t = tf.stack(temp)

Где x = my_list длины n , определенной ранее.Этот фрагмент кода просто преобразует мой список входных данных x , который выглядит следующим образом:

[[D0, K], [D1, K], ... [Dn, K]]

в

[n, K]

Где я фактически суммирую по каждой строке Di так, чтобы каждая j-ая строка моего нового тензора размера [n, K] содержит: sum ([Dj, K], axis = 0)

Проблема в том, чтоесли я использую python for ... loop Я не уверен, что обратное распространение действительно будет работать (я новичок в TensorFlow, но я думаю, что если я не использую while_loop function мои операции не будут добавлены в Graph, и поэтому делать нативный цикл python for не имеет смысла?).

Итак, я только что попытался перекодировать этот фрагмент кода, используя tenorflow while_loop.Код выглядит следующим образом:

def reduce_it(i, outputs):
    i_row = tf.reduce_sum(x[i], axis=0) # x[i] throw an error as i is a Tensor
    outputs = outputs.write(i, i_row)

    return i+1, outputs

temp = tf.TensorArray(dtype=tf.float32, infer_shape=False, size=1,
                  dynamic_size=True)
_, temp = tf.while_loop(lambda i, *args: tf.less(i, T),
                     reduce_it, [0, temp])
temp = temp.stack()

Я уже видел, как кто-то спрашивал об этом, но никто не мог дать ему обходной путь.Я попытался преобразовать Tensor i в целое число, передав массив массивов, к которому я добавляю элемент во время цикла while, чтобы получить форму этого массива:

def reduce_it(arr, outputs):
    idx = arr.shape[0] - 1 # use shape[0] of array as i
    i_row = tf.reduce_sum(x[idx], axis=0)
    outputs = outputs.write(tf.constant([idx]), i_row)
    arr = np.append(arr, 0)
    return arr, outputs

temp = tf.TensorArray(dtype=tf.float32, infer_shape=False, size=1,
                  dynamic_size=True)
_, temp = tf.while_loop(lambda arr, *args: tf.less(arr.shape[0], T),
                     reduce_it, [np.array([0]), temp])
temp = temp.stack()

, но это нене работает, потому что форма моего массива arr изменяется во время цикла, поэтому мне может понадобиться использовать параметр shape_invariants в while_loop, но мне не удалось получить рабочий код ...

Также я преобразовал свой список в Tensor, добавив отступ, так что мой тензор имеет размер: [T, max (Di), K] , но мне все еще нужно знать, на каком Dimension Di i 'Я работаю на каждой итерации моего цикла, это означает, что мне нужно создать тензор (1d-массив) размера n, имеющий Di в качестве числа на позиции i:

my_tensor = [D1, D2, ..., Dn]

, тогда мне нужно собрать Di в моем временицикл, но если я просто сделаю:

my_dim = tf.gather(my_tensor, i)

Я соберу только тензор и мне нужно целое число.

Не думаю, что смогу определить сеанс и восстановить my_dim.eval (), поскольку этот код является частью моего модуля, который затем вызывается во время обучения (и ясоздать сеанс в данный момент).

Некоторые эксперты TF могут подумать об обходном пути или взломе?

Заранее спасибо

Примечание Также заполнение - это решение, но на самом деле позже в моем коде мне нужно получить каждую исходную матрицу размера [Di, K], и поэтому, если я дополню свой [Di, K], чтобы я мог построить тензор формы:

[n, max(Dn), K]

тогда мне все еще нужно восстановить каждый [Di, K], чтобы иметь возможность использовать tf.matmul () (например, операции) с правильными размерами.Так что заполнение для меня на самом деле не решение.

Надеюсь, мой пост достаточно ясен.

1 Ответ

0 голосов
/ 24 мая 2018

Найдите ниже потенциальное решение, которое, однако, я бы не рекомендовал для больших значений T (этот метод создает столько же операций, сколько элементов в my_list).

Ваша идея дополнить тензорыс нулями кажется хорошим в противном случае.Эти дополнительные нули не должны влиять на вашу tf.reduce_sum(x[idx], axis=0), если я правильно понимаю вашу конечную цель (но, тем не менее, это решение не может быть рекомендовано для больших T по тем же причинам, что и раньше).

НаконецВы также можете попытаться преобразовать свой код, чтобы использовать tf.SparseTensor и tf.sparse_reduce_sum().


Решение с tf.case() и tf.while_loop()

import tensorflow as tf
import numpy as np

T = 10
my_list = [tf.ones((np.random.randint(2, 42))) for i in range(T)] # list of random size tensors

def reduce_it(i, outputs):
    get_lambda_for_list_element = lambda idx: lambda: my_list[idx]
    cases = {tf.equal(i, idx): get_lambda_for_list_element(idx) for idx in range(len(my_list))}
    x = tf.case(cases, exclusive=True)

    # It's not clear to me what my_list contains / what your loop is suppose to compute.
    # Here's a toy example supposing the loop computes:
    #       outputs[i] = tf.reduce_sum(my_list[i]) for i in range(T)
    i_row = tf.reduce_sum(x)
    indices = tf.range(0, T)
    outputs = tf.where(tf.equal(indices, i), tf.tile(tf.expand_dims(i_row, 0), [T]), outputs)

    return i+1, outputs

temp = tf.zeros((T))
_, temp = tf.while_loop(lambda i, *args: tf.less(i, T), reduce_it, [0, temp])

with tf.Session() as sess:
    res = sess.run(temp)
    print(res)
    # [37.  2. 22. 16. 37. 40. 10.  3. 12. 26.]

    # Checking if values are correct:
    print([sess.run(tf.reduce_sum(tensor)) for tensor in my_list])
    # [37.0, 2.0, 22.0, 16.0, 37.0, 40.0, 10.0, 3.0, 12.0, 26.0]

Раствор с tf.pad()

import tensorflow as tf
import numpy as np

T = 10
my_list = [tf.ones((np.random.randint(2, 42))) for i in range(T)]  # list of random size tensors

dims = [t.get_shape().as_list()[0] for t in my_list]
max_dims = max(dims)

my_padded_list = [tf.squeeze(
    # Padding with zeros:
    tf.pad(tf.expand_dims(t, 0),
           tf.constant([[0, 0], [int(np.floor((max_dims - t.get_shape().as_list()[0]) / 2)),
                                 int(np.ceil((max_dims - t.get_shape().as_list()[0]) / 2))]],
                       dtype=tf.int32),
           "CONSTANT"))
    for t in my_list]

my_padded_list = tf.stack(my_padded_list)
outputs_with_padding = tf.reduce_sum(my_padded_list, axis=1)

with tf.Session() as sess:
    # [13. 11. 24.  9. 16.  8. 24. 34. 35. 32.]
    res = sess.run(outputs_with_padding)
    print(res)

    # Checking if values are correct:
    print([sess.run(tf.reduce_sum(tensor)) for tensor in my_list])
    # [13.0, 11.0, 24.0, 9.0, 16.0, 8.0, 24.0, 34.0, 35.0, 32.0]

Раствор с tf.SparseTensor

import tensorflow as tf
import numpy as np

T = 4
K = 2
max_length = 42
my_list = [np.random.rand(np.random.randint(1, max_length + 1), K) for i in range(T)]  # list of random size tensors

x = tf.sparse_placeholder(tf.float32)
res = tf.sparse_reduce_sum(x, axis=1)

with tf.Session() as sess:
    # Preparing inputs for sparse placeholder:
    indices = np.array([ [t, i, k] for t in range(T) 
                         for i in range(my_list[t].shape[0]) 
                         for k in range(my_list[t].shape[1]) ], dtype=np.int64)
    values = np.concatenate([t.reshape((-1)) for t in my_list])
    dense_shape = np.array([T, max_length, K], dtype=np.int64)
    sparse_feed_dict = {x: tf.SparseTensorValue(indices, values, dense_shape)}
    # or implictely, sparse_feed_dict = {x: (indices, values, dense_shape)}
    print(sess.run(res, feed_dict=sparse_feed_dict))
    # [[2.160928   3.38365   ]
    #  [13.332438  14.3232155]
    #  [6.563875   6.540451  ]
    #  [3.3114233  2.138658  ]]

    # Checking if values are correct:
    print([sess.run(tf.reduce_sum(tensor, axis=0)) for tensor in my_list])
    # [array([2.16092795, 3.38364983  ]), 
    #  array([13.33243797, 14.32321563]), 
    #  array([6.56387488, 6.54045109  ]), 
    #  array([3.31142322, 2.13865792  ])]
...