tf.compat.v2.while_loop
Повторять body пока условие cond истинно.
tf.compat.v2.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 обрабатываются специально следующим образом:
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 для этого цикла. |
maximum_iterations |
Необязательное максимальное количество итераций цикла while для выполнения. Если указано, результат cond умножается по логическому И с дополнительным условием, гарантирующим, что количество выполненных итераций не превышает 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)
© 2020 The TensorFlow Authors. All rights reserved.
Licensed under the Creative Commons Attribution License 3.0.
Code samples licensed under the Apache 2.0 License.
https://www.tensorflow.org/versions/r1.15/api_docs/python/tf/compat/v2/while_loop