tf.while_loop
| Просмотреть исходный код на GitHub |
Повторять body пока условие cond истинно. (устаревшие значения аргументов)
tf.while_loop(
cond,
body,
loop_vars,
shape_invariants=None,
parallel_iterations=10,
back_prop=True,
swap_memory=False,
maximum_iterations=None,
name=None
)
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 не вернёт false.
Для корректности tf.while_loop() строго применяет инварианты формы для переменных цикла. Инвариант формы — это (возможно, частичная) форма, которая не изменяется во время итераций цикла. Если форма переменной цикла после итерации окажется более общей или несовместимой с её инвариантом формы, будет выброшено исключение. Например, форма [11, None] более общая, чем форма [11, 17], а [11, 21] несовместима с [11, 17]. По умолчанию (если аргумент shape_invariants не указан), предполагается, что начальная форма каждого тензора в loop_vars одинакова на каждой итерации. Аргумент shape_invariants позволяет вызывающему объекту указать менее специфичный инвариант формы для каждой переменной цикла, что необходимо, если форма изменяется между итерациями. Функция tf.Tensor.set_shape также может быть использована в функции body для указания конкретной формы выходной переменной цикла. Инварианты формы для SparseTensor и IndexedSlices обрабатываются следующим образом:
а) Если переменная цикла является SparseTensor, инвариант формы должен быть TensorShape([r]), где r — ранг плотного тензора, представленного разреженным тензором. Это означает, что формы трёх тензоров SparseTensor составляют ([None], [None, r], [r]). ПРИМЕЧАНИЕ: Инвариант формы здесь — это форма свойства SparseTensor.dense_shape. Она должна быть формой вектора.
б) Если переменная цикла является IndexedSlices, инвариант формы должен быть инвариантом формы тензора значений IndexedSlices. Это означает, что формы трёх тензоров IndexedSlices составляют (форма, [форма[0]], [форма.ndims]).
while_loop реализует нестрогую семантику, позволяющую запускать несколько итераций параллельно. Максимальное количество параллельных итераций может быть контролировано параметром parallel_iterations, что даёт пользователям некоторый контроль над потреблением памяти и порядком выполнения. Для корректных программ while_loop должен возвращать тот же результат для любого значения parallel_iterations > 0.
Для обучения TensorFlow хранит тензоры, которые производятся при прямом выводе и необходимы при обратном распространении. Эти тензоры являются основным источником потребления памяти и часто вызывают ошибки OOM при обучении на графических процессорах. При значении флага swap_memory в true, эти тензоры меняются с графического процессора на центральный процессор. Это, например, позволяет обучать модели RNN с очень длинными последовательностями и большими партиями.
| Args | |
|---|---|
cond | Вызываемый объект, представляющий условие завершения цикла. |
body | Вызываемый объект, представляющий тело цикла. |
loop_vars | (Возможно, вложенная) кортеж, именованный кортеж или список объектов массива numpy, Tensor, и TensorArray . |
shape_invariants | Инварианты формы для переменных цикла. |
parallel_iterations | Количество итераций, разрешённых для параллельного выполнения. Должно быть положительным целым числом. |
back_prop | (необязательно) Устарел. False отключает поддержку обратного распространения. Вместо этого предпочитайте использование tf.stop_gradient. |
swap_memory | Включена ли перемена памяти GPU-CPU для этого цикла. |
maximum_iterations | Необязательное максимальное число итераций цикла while, которые необходимо выполнить. Если указано, выходной cond AND-ится с дополнительным условием, гарантирующим, что число выполненных итераций не превышает maximum_iterations. |
name | Необязательное префиксное имя для возвращаемых тензоров. |
| Returns | |
|---|---|
Тензоры вывода для переменных цикла после цикла. Результат имеет ту же структуру, что и loop_vars. |
| Raises | |
|---|---|
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/versions/r2.9/api_docs/python/tf/while_loop