tf.compat.v1.while_loop
Повторять body, пока условие cond истинно.
tf.compat.v1.while_loop(
cond,
body,
loop_vars,
shape_invariants=None,
parallel_iterations=10,
back_prop=True,
swap_memory=False,
name=None,
maximum_iterations=None,
return_same_structure=False
)
cond — это вызываемый объект, возвращающий булеву скалярную тензор. body — это вызываемый объект, возвращающий (возможно, вложенную) кортеж, именованную кортеж или список тензоров той же арности (длины и структуры) и типов, что и loop_vars. loop_vars — это (возможно, вложенная) кортеж, именованная кортеж или список тензоров, передаваемых как в cond, так и в body. cond и body оба принимают столько аргументов, сколько loop_vars.
Помимо обычных тензоров или IndexedSlices, тело может принимать и возвращать объекты TensorArray. Потоки объектов TensorArray будут должным образом передаваться между циклами и во время вычисления градиентов.
Обратите внимание, что while_loop вызывает cond и body ровно один раз (внутри вызова while_loop, и совсем не во время Session.run()). while_loop соединяет фрагменты графа, созданные во время вызовов cond и body, с некоторыми дополнительными узлами графа, чтобы создать поток графа, который повторяет body, пока cond не вернёт ложь.
Для корректности tf.while_loop() строго накладывает ограничения на инварианты формы для переменных цикла. Инвариант формы — это (возможно, частичная) форма, которая не изменяется во всех итерациях цикла. Если форма переменной цикла после итерации окажется более общей или несовместимой с её инвариантом формы, будет выброшено исключение. Например, форма [11, None] более общая, чем форма [11, 17], а [11, 21] несовместима с [11, 17]. По умолчанию (если аргумент shape_invariants не указан), предполагается, что начальная форма каждого тензора в loop_vars одинакова во всех итерациях. Аргумент shape_invariants позволяет вызывающей стороне указать менее специфический инвариант формы для каждой переменной цикла, что необходимо, если форма изменяется между итерациями. Функция tf.Tensor.set_shape также может использоваться в функции body, чтобы указать, что выходная переменная цикла имеет определённую форму. Инварианты формы для SparseTensor и IndexedSlices обрабатываются особо следующим образом:
a) Если переменная цикла — SparseTensor, инвариант формы должен быть TensorShape([r]), где r — ранг плотного тензора, представленного разреженным тензором. Это означает, что формы трёх тензоров SparseTensor равны ([None], [None, r], [r]). ПРИМЕЧАНИЕ: Здесь инвариант формы — это форма свойства SparseTensor.dense_shape. Это должна быть форма вектора.
b) Если переменная цикла — IndexedSlices, инвариант формы должен быть инвариантом формы тензора значений IndexedSlices. Это означает, что формы трёх тензоров IndexedSlices равны (форма, [форма[0]], [форма.ndims]).
while_loop реализует нестрогую семантику, позволяя запускать несколько итераций параллельно. Максимальное количество параллельных итераций можно контролировать с помощью parallel_iterations, что даёт пользователям некоторый контроль над потреблением памяти и порядком выполнения. Для корректных программ while_loop должна возвращать один и тот же результат для любого значения parallel_iterations > 0.
Для обучения TensorFlow сохраняет тензоры, которые производятся в прямом выводе и необходимы для обратного распространения. Эти тензоры — основной источник потребления памяти и часто вызывают ошибки OOM при обучении на графических процессорах. Если флаг swap_memory имеет значение true, эти тензоры перемещаются из графического процессора в процессор. Это, например, позволяет нам обучать модели RNN с очень длинными последовательностями и большими пакетами.
| Аргументы | |
|---|---|
cond | Вызываемый объект, представляющий условие завершения цикла. |
body | Вызываемый объект, представляющий тело цикла. |
loop_vars | (Возможно, вложенная) кортеж, именованная кортеж или список объектов numpy array, Tensor и TensorArray. |
shape_invariants | Инварианты формы для переменных цикла. |
parallel_iterations | Количество итераций, которые разрешено запускать параллельно. Должно быть положительным целым числом. |
back_prop | Включено ли обратное распространение для этого цикла while. |
swap_memory | Включен ли обмен памятью между GPU и CPU для этого цикла. |
name | Необязательное префиксное имя для возвращаемых тензоров. |
maximum_iterations | Необязательное максимальное количество итераций цикла while для запуска. Если указано, результат cond соединяется с дополнительным условием, гарантирующим, что количество выполненных итераций не превышает maximum_iterations. |
return_same_structure | Если True, вывод имеет ту же структуру, что и loop_vars. Если включено выполнение в режиме eager, это игнорируется (и всегда обрабатывается как True). |
| Возвращаемое значение | |
|---|---|
Выходные тензоры для переменных цикла после цикла. Если return_same_structure имеет значение True, возвращаемое значение имеет ту же структуру, что и loop_vars. Если return_same_structure имеет значение False, возвращаемое значение является тензором, TensorArray или IndexedSlice, если длина loop_vars равна 1, или списком в противном случае. |
| Исключения | |
|---|---|
TypeError | если cond или body не вызываемый объект. |
ValueError | если loop_vars пусто. |
Пример:
i = tf.constant(0) c = lambda i: tf.less(i, 10) b = lambda i: tf.add(i, 1) r = tf.while_loop(c, b, [i])
Пример со вложением и именованной кортежем:
import collections
Pair = collections.namedtuple('Pair', 'j, k')
ijk_0 = (tf.constant(0), Pair(tf.constant(1), tf.constant(2)))
c = lambda i, p: i < 10
b = lambda i, p: (i + 1, Pair((p.j + p.k), (p.j - p.k)))
ijk_final = tf.while_loop(c, b, ijk_0)
Пример использования shape_invariants:
i0 = tf.constant(0)
m0 = tf.ones([2, 2])
c = lambda i, m: i < 10
b = lambda i, m: [i+1, tf.concat([m, m], axis=0)]
tf.while_loop(
c, b, loop_vars=[i0, m0],
shape_invariants=[i0.get_shape(), tf.TensorShape([None, 2])])
Пример, демонстрирующий нестрогую семантику: В следующем примере конечное значение счётчика i не зависит от x. Поэтому while_loop может увеличивать счётчик параллельно обновлениям x. Однако, поскольку значение счётчика цикла в одной итерации зависит от значения в предыдущей итерации, сам счётчик цикла не может увеличиваться параллельно. Следовательно, если нам нужно только конечное значение счётчика (которое мы выводим на строке print(sess.run(i))), то x никогда не будет увеличиваться, а счётчик будет обновляться на одном потоке. И наоборот, если нам нужно значение вывода (которое мы выводим на строке print(sess.run(out).shape)), то счётчик может быть увеличен в своём собственном потоке, в то время как x может увеличиваться параллельно в отдельном потоке. В крайнем случае можно представить, что поток, увеличивающий счётчик, выполняется до завершения перед тем, как x будет увеличен хотя бы один раз. Единственное, что никогда не произойдёт, это то, что поток, обновляющий x, никогда не опередит поток счётчика, потому что поток, увеличивающий x, зависит от значения счётчика.
import tensorflow as tf
n = 10000
x = tf.constant(list(range(n)))
c = lambda i, x: i < n
b = lambda i, x: (tf.compat.v1.Print(i + 1, [i]), tf.compat.v1.Print(x + 1,
[i], "x:"))
i, out = tf.while_loop(c, b, (0, x))
with tf.compat.v1.Session() as sess:
print(sess.run(i)) # prints [0] ... [9999]
# The following line may increment the counter and x in parallel.
# The counter thread may get ahead of the other thread, but not the
# other way around. So you may see things like
# [9996] x:[9987]
# meaning that the counter thread is on iteration 9996,
# while the other thread is on iteration 9987
print(sess.run(out).shape)
© 2022 The TensorFlow Authors. All rights reserved.
Licensed under the Creative Commons Attribution License 4.0.
Code samples licensed under the Apache 2.0 License.
https://www.tensorflow.org/api_docs/python/tf/compat/v1/while_loop