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 при обучении на GPU. Когда флаг swap_memory равен true, мы перемещаем эти тензоры с GPU на CPU. Это, например, позволяет обучать модели RNN с очень длинными последовательностями и большими партиями.
| Аргументы | |
|---|---|
cond | Вызываемый объект, представляющий условие завершения цикла. |
body | Вызываемый объект, представляющий тело цикла. |
loop_vars | (Возможно, вложенная) кортеж, именованная кортеж или список массивов numpy, Tensor, и TensorArray объектов. |
shape_invariants | Инварианты формы для переменных цикла. |
parallel_iterations | Количество итераций, которые разрешается выполнять параллельно. Должно быть положительным целым числом. |
back_prop | Включено ли обратное распространение для этого цикла while. |
swap_memory | Включен ли обмен памятью GPU-CPU для этого цикла. |
name | Необязательное префиксное имя для возвращаемых тензоров. |
maximum_iterations | Необязательное максимальное число итераций цикла while для выполнения. Если указано, результат cond AND'ится с дополнительным условием, гарантирующим, что число выполненных итераций не больше 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)
© 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/r2.4/api_docs/python/tf/compat/v1/while_loop