Сумма python на массив тензоров vs tf.add_n

user2458126 спросил: 12 мая 2018 в 03:51 в: python

Итак, у меня есть код

tensors = [] //its filled with 3D float tensors
total = sum(tensors)

, если я изменю последнюю строку на

total = tf.add_n(tensors)

, тогда код создает тот же выход, но работает намного медленнее и скоро вызывает исключение из памяти. Что тут происходит? Может ли кто-нибудь объяснить, как питоны, построенные в функции sum, и tf.add_n взаимодействуют с массивом тензоров соответственно и почему сумма питонов, по-видимому, будет просто лучшей версией?

2 ответа

Ron Lawhorn ответил: 12 мая 2018 в 04:09

Встроенная функция sum () принимает только итерации и, следовательно, имеет преимущество использования генераторов в отношении профиля памяти.

Функция add_n () для тензора принимает список тензоров и похоже, сохраняют эту структуру данных во время обработки, основываясь на ее требовании для сравнения формы.

In [29]: y = [1,2,3,4,5,6,7,8,9,10]  In [30]: y.__sizeof__()
Out[30]: 120In [31]: x = iter(y)In [32]: x.__sizeof__()
Out[32]: 32
P-Gn ответил: 16 мая 2018 в 04:42

Когда вы используете sum, вы вызываете стандартный алгоритм python, который вызывает __add__ рекурсивно на элементах массива. Поскольку __add__ (или +) действительно перегружен тензорами тензорного потока, он работает так, как ожидалось: он создает график, который может быть выполнен во время сеанса. Однако это не является оптимальным, поскольку вы добавляете столько операций, сколько есть элементов в вашем списке; Кроме того, вы выполняете порядок операции (добавьте первые два элемента, затем третий к результату и т. д.), что также не является оптимальным.

В отличие от этого add_n - это специализированная операция для этого. Глядя на график, я действительно думаю:

import tensorflow as tfwith tf.variable_scope('sum'):
  xs = [tf.zeros(()) for _ in range(10)]
  sum(xs)with tf.variable_scope('add_n'):
  xs = [tf.zeros(()) for _ in range(10)]
  tf.add_n(xs)

Однако, вопреки тому, что я думал ранее, add_n занимает больше памяти, потому что он ждет - и сохраняет - для всех входящих входов перед их сохранением. Если количество входов велико, то разница может быть существенной.

Поведение, которое я ожидал от add_n, т. Е. Суммирование входов по мере их доступности, фактически достигнуто по tf.accumulate_n. Это должно быть превосходной альтернативой, поскольку она занимает меньше памяти, чем add_n, но не обеспечивает соблюдение порядка суммирования, например sum.

Почему авторы тензорного потока -wavenet используется sum вместо tf.accumulate_n? Конечно, поскольку до того, как эта функция не дифференцируема на TF < 1.7. Поэтому, если вам нужно поддерживать TF < 1.7 и - эффективная память, старый добрый sum на самом деле неплохой вариант.

user2458126 ответил: 16 мая 2018 в 03:49
Ну это не мой код. Если вам действительно интересно, это репо. github.com/ibab/tensorflow-wavenet Простое изменение строки в функции model.py _create_network приводит к тому, что она очень быстро достигает исключения OOM на моем графическом процессоре