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 не вернёт 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 обрабатываются следующим образом:
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 при обучении на GPU. Если флаг swap_memory имеет значение true, эти тензоры меняются с GPU на CPU. Это, например, позволяет обучать модели RNN с очень длинными последовательностями и большими партиями.
| Args | |
|---|---|
cond | Вызываемый объект, представляющий условие завершения цикла. |
body | Вызываемый объект, представляющий тело цикла. |
loop_vars | (Возможная вложенная) кортеж, именованная кортеж или список массивов NumPy, 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). |
| Returns | |
|---|---|
Выходные тензоры для переменных цикла после завершения цикла. Если return_same_structure имеет значение True, значение возврата имеет ту же структуру, что и loop_vars. Если return_same_structure имеет значение False, значение возврата — это тензор, TensorArray или IndexedSlice, если длина loop_vars равна 1, или список в противном случае. |
| 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/compat/v1/while_loop