
Двойной срок службы функции тензора
4 июня 2025 г.Обзор контента
В TensorFlow 2,Жесткое исполнениевключен по умолчанию. Пользовательский интерфейс является интуитивно понятным и гибким (выполнять одноразовые операции намного проще и быстрее), но это может произойти за счет производительности и развертываемости.
Вы можете использоватьtf.function
сделать графики из ваших программ. Это инструмент преобразования, который создает независимые от Python графики DataFlow из вашего кода Python. Это поможет вам создать производительные и портативные модели, и это требуется для использованияSavedModel
Полем
Это руководство поможет вам осмыслить, какtf.function
Работает под капотом, так что вы можете эффективно использовать его.
Основные выводы и рекомендации:
Отладка в нетерпеливом режиме, затем украсьте с помощью
@tf.function
ПолемНе полагайтесь на побочные эффекты Python, такие как мутация объекта или приложения списка.
tf.function
лучше всего работает с Tensorflow Ops; Numpy и Python Calls преобразуются в константы.
Настраивать
import tensorflow as tf
2024-08-15 02:57:28.958444: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:485] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered
2024-08-15 02:57:28.979712: E external/local_xla/xla/stream_executor/cuda/cuda_dnn.cc:8454] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered
2024-08-15 02:57:28.986177: E external/local_xla/xla/stream_executor/cuda/cuda_blas.cc:1452] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered
Определите вспомогательную функцию, чтобы продемонстрировать виды ошибок, с которыми вы можете столкнуться:
import traceback
import contextlib
# Some helper code to demonstrate the kinds of errors you might encounter.
@contextlib.contextmanager
def assert_raises(error_class):
try:
yield
except error_class as e:
print('Caught expected exception \n {}:'.format(error_class))
traceback.print_exc(limit=2)
except Exception as e:
raise e
else:
raise Exception('Expected {} to be raised but no error was raised!'.format(
error_class))
Основы
Использование
Аtf.function
что вы определяете (например, путем применения@tf.function
Декоратор) похож на операцию с основной тензором: вы можете выполнить его с нетерпением; Вы можете вычислить градиенты; и так далее.
@tf.function # The decorator converts `add` into a `PolymorphicFunction`.
def add(a, b):
return a + b
add(tf.ones([2, 2]), tf.ones([2, 2])) # [[2., 2.], [2., 2.]]
tf.ones([2, 2]), tf.ones([2, 2])) # [[2., 2.], [2., 2.]]
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
I0000 00:00:1723690651.607368 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690651.611235 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690651.614398 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690651.618234 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690651.629890 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690651.633433 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690651.636337 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690651.639748 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690651.643233 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690651.646588 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690651.649526 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690651.652949 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.865955 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.868101 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.870112 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.872121 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.874165 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.876153 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.878068 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.879960 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.881883 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.883841 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.885768 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.887660 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.926250 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.928321 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.930298 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.932288 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.934241 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.936253 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.938172 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.940080 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.942041 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.944593 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.946947 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723690652.949245 167534 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
array([[2., 2.],
[2., 2.]], dtype=float32)>
v = tf.Variable(1.0)
with tf.GradientTape() as tape:
result = add(v, 1.0)
tape.gradient(result, v)
<tf.Tensor: shape=(), dtype=float32, numpy=1.0>
Вы можете использоватьtf.function
S внутри другихtf.function
с
@tf.function
def dense_layer(x, w, b):
return add(tf.matmul(x, w), b)
dense_layer(tf.ones([3, 2]), tf.ones([2, 2]), tf.ones([2]))
<tf.Tensor: shape=(3, 2), dtype=float32, numpy=
array([[3., 3.],
[3., 3.],
[3., 3.]], dtype=float32)>
tf.function
S может быть быстрее, чем нетерпеливый код, особенно для графиков со многими небольшими операциями. Но для графиков с несколькими дорогими операциями (например, свертыми) вы можете не увидеть много ускорения.
import timeit
conv_layer = tf.keras.layers.Conv2D(100, 3)
@tf.function
def conv_fn(image):
return conv_layer(image)
image = tf.zeros([1, 200, 200, 100])
# Warm up
conv_layer(image); conv_fn(image)
print("Eager conv:", timeit.timeit(lambda: conv_layer(image), number=10))
print("Function conv:", timeit.timeit(lambda: conv_fn(image), number=10))
print("Note how there's not much difference in performance for convolutions")
W0000 00:00:1723690654.228267 167534 gpu_timer.cc:114] Skipping the delay kernel, measurement accuracy will be reduced
W0000 00:00:1723690654.285525 167534 gpu_timer.cc:114] Skipping the delay kernel, measurement accuracy will be reduced
W0000 00:00:1723690654.290477 167534 gpu_timer.cc:114] Skipping the delay kernel, measurement accuracy will be reduced
W0000 00:00:1723690654.295072 167534 gpu_timer.cc:114] Skipping the delay kernel, measurement accuracy will be reduced
W0000 00:00:1723690654.299820 167534 gpu_timer.cc:114] Skipping the delay kernel, measurement accuracy will be reduced
W0000 00:00:1723690654.304580 167534 gpu_timer.cc:114] Skipping the delay kernel, measurement accuracy will be reduced
W0000 00:00:1723690654.322737 167534 gpu_timer.cc:114] Skipping the delay kernel, measurement accuracy will be reduced
W0000 00:00:1723690654.327483 167534 gpu_timer.cc:114] Skipping the delay kernel, measurement accuracy will be reduced
W0000 00:00:1723690654.332646 167534 gpu_timer.cc:114] Skipping the delay kernel, measurement accuracy will be reduced
W0000 00:00:1723690654.337747 167534 gpu_timer.cc:114] Skipping the delay kernel, measurement accuracy will be reduced
W0000 00:00:1723690654.343046 167534 gpu_timer.cc:114] Skipping the delay kernel, measurement accuracy will be reduced
W0000 00:00:1723690654.347480 167534 gpu_timer.cc:114] Skipping the delay kernel, measurement accuracy will be reduced
W0000 00:00:1723690654.361780 167534 gpu_timer.cc:114] Skipping the delay kernel, measurement accuracy will be reduced
W0000 00:00:1723690654.370325 167534 gpu_timer.cc:114] Skipping the delay kernel, measurement accuracy will be reduced
W0000 00:00:1723690654.381185 167534 gpu_timer.cc:114] Skipping the delay kernel, measurement accuracy will be reduced
W0000 00:00:1723690654.405763 167534 gpu_timer.cc:114] Skipping the delay kernel, measurement accuracy will be reduced
Eager conv: 0.011224052000216034
Function conv: 0.005400947000453016
Note how there's not much difference in performance for convolutions
Трассировка
В этом разделе раскрывается, как TF.Function работает под капотом, включая детали реализации, которые могут измениться в будущем. Однако, как только вы поймете, почему и когда происходит отслеживание, намного проще использовать TF.Function эффективно!
Что такое «трассировка»?
Функция TF. Запускает вашу программу на графике TensorFlow. Тем не менее, TF.Graph не может представлять все вещи, которые вы пишете в нетерпеливой программе TensorFlow. Например, Python поддерживает полиморфизм, но TF.Graph требует, чтобы его входы имели указанный тип данных и измерение. Или вы можете выполнять побочные задачи, такие как чтение аргументов командной строки, повышение ошибки или работа с более сложным объектом Python; Ни одна из этих вещей не может работать в TF.Graph.
TF.Function Brids этот пробел, разделяя ваш код на два этапа:
- На первом этапе, называемом «трассировкой», tf.function создает новый TF.Graph. Код Python работает нормально, но все операции TensorFlow (например, добавление двух тензоров) отложены: они захватываются TF.Graph и не запускаются.
- На втором этапе TF.Graph, который содержит все, что было отложено на первом этапе, запускается. Этот этап намного быстрее, чем этап трассировки.
В зависимости от его входов, tf.function не всегда будет работать на первом этапе, когда она будет вызвана. См. «Правила отслеживания» ниже, чтобы лучше понять, как это делает это решимость. Пропустить первый этап и только выполнение второго этапа - это то, что дает вам высокую производительность Tensorflow.
Когда tf.function действительно решает отслеживать, за этапом трассировки сразу же следует второй этап, поэтому вызывая функцию TF. Оба создает и запускает TF.Graph. Позже вы увидите, как вы можете запустить только этап трассировки с помощью get_concrete_function.
Когда вы передаете аргументы разных типов в функцию TF, оба этапа запускаются:
@tf.function
def double(a):
print("Tracing with", a)
return a + a
print(double(tf.constant(1)))
print()
print(double(tf.constant(1.1)))
print()
print(double(tf.constant("a")))
print()
Tracing with Tensor("a:0", shape=(), dtype=int32)
tf.Tensor(2, shape=(), dtype=int32)
Tracing with Tensor("a:0", shape=(), dtype=float32)
tf.Tensor(2.2, shape=(), dtype=float32)
Tracing with Tensor("a:0", shape=(), dtype=string)
tf.Tensor(b'aa', shape=(), dtype=string)
Обратите внимание, что если вы неоднократно вызываетеtf.function
С тем же типом аргумента, Tensorflow пропустит стадию трассировки и повторно использует ранее прослеженный график, поскольку сгенерированный график будет идентичным.
# This doesn't print 'Tracing with ...'
print(double(tf.constant("b")))
tf.Tensor(b'bb', shape=(), dtype=string)
Вы можете использоватьpretty_printed_concrete_signatures()
Чтобы увидеть все доступные следы:
print(double.pretty_printed_concrete_signatures())
Input Parameters:
a (POSITIONAL_OR_KEYWORD): TensorSpec(shape=(), dtype=tf.int32, name=None)
Output Type:
TensorSpec(shape=(), dtype=tf.int32, name=None)
Captures:
None
Input Parameters:
a (POSITIONAL_OR_KEYWORD): TensorSpec(shape=(), dtype=tf.float32, name=None)
Output Type:
TensorSpec(shape=(), dtype=tf.float32, name=None)
Captures:
None
Пока вы видели этоtf.function
Создает кэшированный, динамический рассеянный слой над логикой трассировки графика Tensorflow. Быть более конкретным в терминологии:
- А
tf.Graph
является необработанным, агрессивным языком, портативным представлением вычисления с тензорфлоу. - Трассировка - это процесс, посредством которого новый
tf.Graph
S генерируется из кода Python. - Экземпляр
tf.Graph
специализируется на конкретных типах ввода, которыми он был прослежен. Различные типы требуют обращения. - Каждый прослеживается
tf.Graph
имеет соответствующийConcreteFunction
Полем - А
tf.function
управляет кэшемConcreteFunction
S и выбирает правильный для ваших входов. tf.function
Окутает функцию Python, которая будет прослежена, возвращаяtf.types.experimental.PolymorphicFunction
объект.
Правила трассировки
Когда называется, аtf.function
сначала оценивает тип каждого входного аргумента, используяtf.types.experimental.TraceType
каждого аргумента. Это используется для построенияtf.types.experimental.FunctionType
описывая подпись желаемогоConcreteFunction
Полем Мы сравниваем этоFunctionType
вFunctionType
S существующихConcreteFunction
с Если совпадениеConcreteFunction
найдено, звонок отправляется на него. Если совпадение не найдено, новыйConcreteFunction
прослежены на желаемоеFunctionType
Полем
Если найдено несколько совпадений, выбрана самая специфическая подпись. Сопоставление осуществляетсяподтинг, например, как обычные вызовы функций в C ++ или Java. Например,TensorShape([1, 2])
является подтипомTensorShape([None, None])
и так звонок к функции TF.TensorShape([1, 2])
может быть отправлен вConcreteFunction
производится сTensorShape([None, None])
но если аConcreteFunction
сTensorShape([1, None])
Также существует, тогда это будет приоритет, так как это более конкретно.
АTraceType
определяется из входных аргументов следующим образом:
Для
Tensor
, тип параметризованTensor
S.dtype
иshape
; Ранные формы представляют собой подтип небегальных фигур; Фиксированные измерения являются подтипом неизвестных измеренийДля
Variable
, тип похож наTensor
, но также включает в себя уникальный идентификатор ресурса переменной, необходимый для правильного управления проводом зависимостейДля примитивных значений Python тип соответствуетценитьсам Например,
TraceType
значения3
являетсяLiteralTraceType<3>
, нетint
ПолемДля Python упорядочил контейнеры, такие как
list
иtuple
и т. д. тип параметризован типами их элементов; Например, тип[1, 2]
являетсяListTraceType<LiteralTraceType<1>, LiteralTraceType<2>>
и тип для[2, 1]
являетсяListTraceType<LiteralTraceType<2>, LiteralTraceType<1>>
который отличается.Для отображений Python, таких как
dict
Тип также является отображением из тех же клавиш, но по типам значений вместо фактических значений. Например, тип{1: 2, 3: 4}
, являетсяMappingTraceType<<KeyValue<1, LiteralTraceType<2>>>, <KeyValue<3, LiteralTraceType<4>>>>
Полем Однако, в отличие от упорядоченных контейнеров,{1: 2, 3: 4}
и{3: 4, 1: 2}
имеют эквивалентные типы.Для объектов Python, которые реализуют
__tf_tracing_type__
Метод, тип - это то, что этот метод возвращает.Для любых других объектов Python тип является общим
TraceType
и соответствующий предшествующий- Сначала он проверяет, является ли объект тем же объектом, который используется в предыдущем трассировке (с помощью Python
id()
илиis
) Обратите внимание, что это все равно будет соответствовать, если объект изменился, поэтому, если вы используете объекты Python какtf.function
аргументы лучше всего использоватьнеизменени - Далее он проверяет, равен ли объект объекту, используемому в предыдущем трассировке (с помощью Python
==
)
Примечаниечто эта процедура только сохраняетSleedRefдля объекта и, следовательно, работает только до тех пор, пока объект находится в области объема/не удален
- Сначала он проверяет, является ли объект тем же объектом, который используется в предыдущем трассировке (с помощью Python
Примечание:TraceType
основан наtf.function
входные параметры, поэтому изменения в глобальном и
Контроль обращения
Поездка, когда вашtf.function
Создает более одного трассировки, помогает гарантировать, что TensorFlow генерирует правильные графики для каждого набора входов. Однако трассировка - это дорогая операция! Если тыtf.function
Посложняет новый график для каждого вызова, вы обнаружите, что ваш код выполняется медленнее, чем если бы вы не использовалиtf.function
Полем
Чтобы контролировать поведение трассировки, вы можете использовать следующие методы:
Пройти фиксированнуюinput_signature
кtf.function
Это силыtf.function
ограничить себя только однимtf.types.experimental.FunctionType
состоит из типов, перечисленныхinput_signature
Полем Звонки, которые не могут быть отправлены на этоFunctionType
выбросит ошибку.
@tf.function(input_signature=(tf.TensorSpec(shape=[None], dtype=tf.int32),))
def next_collatz(x):
print("Tracing with", x)
return tf.where(x % 2 == 0, x // 2, 3 * x + 1)
print(next_collatz(tf.constant([1, 2])))
# You specified a 1-D tensor in the input signature, so this should fail.
with assert_raises(TypeError):
next_collatz(tf.constant([[1, 2], [3, 4]]))
# You specified an int32 dtype in the input signature, so this should fail.
with assert_raises(TypeError):
next_collatz(tf.constant([1.0, 2.0]))
Tracing with Tensor("x:0", shape=(None,), dtype=int32)
tf.Tensor([4 1], shape=(2,), dtype=int32)
Caught expected exception
<class 'TypeError'>:
Caught expected exception
<class 'TypeError'>:
Traceback (most recent call last):
File "/tmpfs/tmp/ipykernel_167534/3551158538.py", line 8, in assert_raises
yield
File "/tmpfs/tmp/ipykernel_167534/3657259638.py", line 9, in <module>
next_collatz(tf.constant([[1, 2], [3, 4]]))
TypeError: Binding inputs to tf.function failed due to `Can not cast TensorSpec(shape=(2, 2), dtype=tf.int32, name=None) to TensorSpec(shape=(None,), dtype=tf.int32, name=None)`. Received args: (<tf.Tensor: shape=(2, 2), dtype=int32, numpy=
array([[1, 2],
[3, 4]], dtype=int32)>,) and kwargs: {} for signature: (x: TensorSpec(shape=(None,), dtype=tf.int32, name=None)).
Traceback (most recent call last):
File "/tmpfs/tmp/ipykernel_167534/3551158538.py", line 8, in assert_raises
yield
File "/tmpfs/tmp/ipykernel_167534/3657259638.py", line 13, in <module>
next_collatz(tf.constant([1.0, 2.0]))
TypeError: Binding inputs to tf.function failed due to `Can not cast TensorSpec(shape=(2,), dtype=tf.float32, name=None) to TensorSpec(shape=(None,), dtype=tf.int32, name=None)`. Received args: (<tf.Tensor: shape=(2,), dtype=float32, numpy=array([1., 2.], dtype=float32)>,) and kwargs: {} for signature: (x: TensorSpec(shape=(None,), dtype=tf.int32, name=None)).
Используйте неизвестные измерения для гибкости
Поскольку TensorFlow соответствует тензорам в зависимости от их формы, используяNone
размер как подстановочный знак позволитtf.function
S для повторного использования трассов для ввода переменного размера. Вход переменного размера может произойти, если у вас есть последовательности разной длины или изображения разных размеров для каждой партии.
@tf.function(input_signature=(tf.TensorSpec(shape=[None], dtype=tf.int32),))
def g(x):
print('Tracing with', x)
return x
# No retrace!
print(g(tf.constant([1, 2, 3])))
print(g(tf.constant([1, 2, 3, 4, 5])))
Tracing with Tensor("x:0", shape=(None,), dtype=int32)
tf.Tensor([1 2 3], shape=(3,), dtype=int32)
tf.Tensor([1 2 3 4 5], shape=(5,), dtype=int32)
Использоватьreduce_retracing
для автоматической гибкости
Когдаreduce_retracing
включено,tf.function
Автоматически идентифицирует супертипы типов входов, которые он наблюдает и выбирает автоматически отслеживать более обобщенные графики. Это менее эффективно, чем установлениеinput_signature
непосредственно, но полезно, когда необходимо поддерживать многие типы.
@tf.function(reduce_retracing=True)
def g(x):
print('Tracing with', x)
return x
# Traces once.
print(g(tf.constant([1, 2, 3])))
# Traces again, but more generalized this time.
print(g(tf.constant([1, 2, 3, 4, 5])))
# No more tracing!
print(g(tf.constant([1, 2, 3, 4, 5, 6, 7])))
print(g(tf.constant([1, 2, 3, 4, 5, 6, 7, 8, 9])))
Tracing with Tensor("x:0", shape=(3,), dtype=int32)
tf.Tensor([1 2 3], shape=(3,), dtype=int32)
Tracing with Tensor("x:0", shape=(None,), dtype=int32)
tf.Tensor([1 2 3 4 5], shape=(5,), dtype=int32)
tf.Tensor([1 2 3 4 5 6 7], shape=(7,), dtype=int32)
tf.Tensor([1 2 3 4 5 6 7 8 9], shape=(9,), dtype=int32)
Пройти тензоры вместо литералов Python
Часто аргументы Python используются для контроля гиперпараметров и конструкций графика - например,, например,num_layers=10
илиtraining=True
илиnonlinearity='relu'
Полем Таким образом, если аргумент Python изменится, имеет смысл, что вам придется просматривать график.
Тем не менее, возможно, что аргумент Python не используется для управления конструкцией графика. В этих случаях изменение значения Python может вызвать ненужные повреждения. Возьмите, к примеру, этот учебный цикл, который автограф динамически развернется. Несмотря на множественные следы, сгенерированный график на самом деле идентичен, поэтому обращение не нужно.
def train_one_step():
pass
@tf.function
def train(num_steps):
print("Tracing with num_steps = ", num_steps)
tf.print("Executing with num_steps = ", num_steps)
for _ in tf.range(num_steps):
train_one_step()
print("Retracing occurs for different Python arguments.")
train(num_steps=10)
train(num_steps=20)
print()
print("Traces are reused for Tensor arguments.")
train(num_steps=tf.constant(10))
train(num_steps=tf.constant(20))
Retracing occurs for different Python arguments.
Tracing with num_steps = 10
Executing with num_steps = 10
Tracing with num_steps = 20
Executing with num_steps = 20
Traces are reused for Tensor arguments.
Tracing with num_steps = Tensor("num_steps:0", shape=(), dtype=int32)
Executing with num_steps = 10
Executing with num_steps = 20
Если вам нужно затопить обработку, создайте новыйtf.function
Полем Отдельныйtf.function
Объекты гарантированы, чтобы не делиться следами.
def f():
print('Tracing!')
tf.print('Executing')
tf.function(f)()
tf.function(f)()
Tracing!
Executing
Tracing!
Executing
Используйте протокол трассировки
Там, где это возможно, вы должны предпочесть преобразовать тип Python вtf.experimental.ExtensionType
вместо. Более того,TraceType
изExtensionType
являетсяtf.TypeSpec
связан с этим. Поэтому, если необходимо, вы можете просто переопределить по умолчаниюtf.TypeSpec
Чтобы взять под контрольExtensionType
S.Tracing Protocol
Полем
В противном случае для прямого контроля над тем, когдаtf.function
должен просматривать в отношении конкретного типа Python, вы можете реализоватьTracing Protocol
для этого самостоятельно.
@tf.function
def get_mixed_flavor(fruit_a, fruit_b):
return fruit_a.flavor + fruit_b.flavor
class Fruit:
flavor = tf.constant([0, 0])
class Apple(Fruit):
flavor = tf.constant([1, 2])
class Mango(Fruit):
flavor = tf.constant([3, 4])
# As described in the above rules, a generic TraceType for `Apple` and `Mango`
# is generated (and a corresponding ConcreteFunction is traced) but it fails to
# match the second function call since the first pair of Apple() and Mango()
# have gone out out of scope by then and deleted.
get_mixed_flavor(Apple(), Mango()) # Traces a new concrete function
get_mixed_flavor(Apple(), Mango()) # Traces a new concrete function again
# However, each subclass of the `Fruit` class has a fixed flavor, and you
# can reuse an existing traced concrete function if it was the same
# subclass. Avoiding such unnecessary tracing of concrete functions
# can have significant performance benefits.
class FruitTraceType(tf.types.experimental.TraceType):
def __init__(self, fruit):
self.fruit_type = type(fruit)
self.fruit_value = fruit
def is_subtype_of(self, other):
# True if self subtypes `other` and `other`'s type matches FruitTraceType.
return (type(other) is FruitTraceType and
self.fruit_type is other.fruit_type)
def most_specific_common_supertype(self, others):
# `self` is the specific common supertype if all input types match it.
return self if all(self == other for other in others) else None
def placeholder_value(self, placeholder_context=None):
# Use the fruit itself instead of the type for correct tracing.
return self.fruit_value
def __eq__(self, other):
return type(other) is FruitTraceType and self.fruit_type == other.fruit_type
def __hash__(self):
return hash(self.fruit_type)
class FruitWithTraceType:
def __tf_tracing_type__(self, context):
return FruitTraceType(self)
class AppleWithTraceType(FruitWithTraceType):
flavor = tf.constant([1, 2])
class MangoWithTraceType(FruitWithTraceType):
flavor = tf.constant([3, 4])
# Now if you try calling it again:
get_mixed_flavor(AppleWithTraceType(), MangoWithTraceType()) # Traces a new concrete function
get_mixed_flavor(AppleWithTraceType(), MangoWithTraceType()) # Re-uses the traced concrete function
<tf.Tensor: shape=(2,), dtype=int32, numpy=array([4, 6], dtype=int32)>
Получение конкретных функций
Каждый раз, когда функция прослеживается, создается новая конкретная функция. Вы можете напрямую получить конкретную функцию, используяget_concrete_function
Полем
print("Obtaining concrete trace")
double_strings = double.get_concrete_function(tf.constant("a"))
print("Executing traced function")
print(double_strings(tf.constant("a")))
print(double_strings(a=tf.constant("b")))
Obtaining concrete trace
Executing traced function
tf.Tensor(b'aa', shape=(), dtype=string)
tf.Tensor(b'bb', shape=(), dtype=string)
# You can also call get_concrete_function on an InputSpec
double_strings_from_inputspec = double.get_concrete_function(tf.TensorSpec(shape=[], dtype=tf.string))
print(double_strings_from_inputspec(tf.constant("c")))
tf.Tensor(b'cc', shape=(), dtype=string)
ПечатьConcreteFunction
Отображает резюме его входных аргументов (с типами) и его выходного типа.
print(double_strings)
ConcreteFunction Input Parameters:
a (POSITIONAL_OR_KEYWORD): TensorSpec(shape=(), dtype=tf.string, name=None)
Output Type:
TensorSpec(shape=(), dtype=tf.string, name=None)
Captures:
None
Вы также можете напрямую получить подпись конкретной функции.
print(double_strings.function_type)
(a: TensorSpec(shape=(), dtype=tf.string, name=None)) -> TensorSpec(shape=(), dtype=tf.string, name=None)
Использование конкретного трассировки с несовместимыми типами вынесет ошибку
with assert_raises(tf.errors.InvalidArgumentError):
double_strings(tf.constant(1))
Caught expected exception
<class 'tensorflow.python.framework.errors_impl.InvalidArgumentError'>:
Traceback (most recent call last):
File "/tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tensorflow/python/eager/polymorphic_function/function_type_utils.py", line 442, in bind_function_inputs
bound_arguments = function_type.bind_with_defaults(
File "/tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tensorflow/core/function/polymorphism/function_type.py", line 277, in bind_with_defaults
with_default_args[arg_name] = constraint.cast(
TypeError: Can not cast TensorSpec(shape=(), dtype=tf.int32, name=None) to TensorSpec(shape=(), dtype=tf.string, name=None)
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tensorflow/python/eager/polymorphic_function/concrete_function.py", line 1179, in _call_impl
return self._call_with_structured_signature(args, kwargs)
File "/tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tensorflow/python/eager/polymorphic_function/concrete_function.py", line 1259, in _call_with_structured_signature
function_type_utils.canonicalize_function_inputs(
TypeError: Binding inputs to tf.function failed due to `Can not cast TensorSpec(shape=(), dtype=tf.int32, name=None) to TensorSpec(shape=(), dtype=tf.string, name=None)`. Received args: (<tf.Tensor: shape=(), dtype=int32, numpy=1>,) and kwargs: {} for signature: (a: TensorSpec(shape=(), dtype=tf.string, name=None)) -> TensorSpec(shape=(), dtype=tf.string, name=None).
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/tmpfs/tmp/ipykernel_167534/3551158538.py", line 8, in assert_raises
yield
File "/tmpfs/tmp/ipykernel_167534/3196284684.py", line 2, in <module>
double_strings(tf.constant(1))
tensorflow.python.framework.errors_impl.InvalidArgumentError: cannot compute __inference_double_189 as input #0(zero-based) was expected to be a string tensor but is a int32 tensor [Op:__inference_double_189]
Вы можете заметить, что аргументы Python получают особую обработку при входной подписи конкретной функции. До TensorFlow 2.3 аргументы Python были просто удалены из подписи конкретной функции. Начиная с TensorFlow 2.3, аргументы Python остаются в подписи, но они ограничены, чтобы принимать значение, установленное во время трассировки.
@tf.function
def pow(a, b):
return a ** b
square = pow.get_concrete_function(a=tf.TensorSpec(None, tf.float32), b=2)
print(square)
ConcreteFunction Input Parameters:
a (POSITIONAL_OR_KEYWORD): TensorSpec(shape=<unknown>, dtype=tf.float32, name=None)
b (POSITIONAL_OR_KEYWORD): Literal[2]
Output Type:
TensorSpec(shape=<unknown>, dtype=tf.float32, name=None)
Captures:
None
assert square(tf.constant(10.0)) == 100
with assert_raises(TypeError):
square(tf.constant(10.0), b=3)
Caught expected exception
<class 'TypeError'>:
Traceback (most recent call last):
File "/tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tensorflow/python/eager/polymorphic_function/function_type_utils.py", line 442, in bind_function_inputs
bound_arguments = function_type.bind_with_defaults(
File "/tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tensorflow/core/function/polymorphism/function_type.py", line 277, in bind_with_defaults
with_default_args[arg_name] = constraint.cast(
ValueError: Can not cast 3 to Literal[2]
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tensorflow/python/eager/polymorphic_function/concrete_function.py", line 1179, in _call_impl
return self._call_with_structured_signature(args, kwargs)
File "/tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tensorflow/python/eager/polymorphic_function/concrete_function.py", line 1259, in _call_with_structured_signature
function_type_utils.canonicalize_function_inputs(
TypeError: Binding inputs to tf.function failed due to `Can not cast 3 to Literal[2]`. Received args: (<tf.Tensor: shape=(), dtype=float32, numpy=10.0>,) and kwargs: {'b': 3} for signature: (a: TensorSpec(shape=<unknown>, dtype=tf.float32, name=None), b: Literal[2]) -> TensorSpec(shape=<unknown>, dtype=tf.float32, name=None).
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tensorflow/python/eager/polymorphic_function/concrete_function.py", line 1182, in _call_impl
return self._call_with_flat_signature(args, kwargs)
File "/tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tensorflow/python/eager/polymorphic_function/concrete_function.py", line 1233, in _call_with_flat_signature
raise TypeError(f"{self._flat_signature_summary()} got unexpected "
TypeError: pow(a) got unexpected keyword arguments: b.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/tmpfs/tmp/ipykernel_167534/3551158538.py", line 8, in assert_raises
yield
File "/tmpfs/tmp/ipykernel_167534/2310937119.py", line 4, in <module>
square(tf.constant(10.0), b=3)
TypeError: Binding inputs to tf.function failed due to `Can not cast 3 to Literal[2]`. Received args: (<tf.Tensor: shape=(), dtype=float32, numpy=10.0>,) and kwargs: {'b': 3} for signature: (a: TensorSpec(shape=<unknown>, dtype=tf.float32, name=None), b: Literal[2]) -> TensorSpec(shape=<unknown>, dtype=tf.float32, name=None).
Fallback to flat signature also failed due to: pow(a) got unexpected keyword arguments: b.
Получение графиков
Хотя получение фактическогоtf.Graph
Объект - это не то, что вам обычно нужно делать, вы можете легко получить его из любой конкретной функции.
graph = double_strings.graph
for node in graph.as_graph_def().node:
print(f'{node.input} -> {node.name}')
[] -> a
['a', 'a'] -> add
['add'] -> Identity
На самом деле,tf.Graph
S не подлежат напрямую. Мы на самом деле используемtf.types.experimental.AtomicFunction
Чтобы выполнить вычисления, описанныеtf.Graph
Полем Вы можете получить доступ кAtomicFunction
описывая отслеживаниеtf.Graph
и назовите это напрямую вместоConcreteFunction
:
atomic_fn = double_strings.inference_fn
atomic_fn(tf.constant("a"))
<tf.Tensor: shape=(), dtype=string, numpy=b'aa'>
Это имеет преимущество в том, что они имеют более низкие накладные расходы на высокопроизводительные сценарии. Но его следует использовать только для прямого вывода (без градиентного поддержки), и захваченные тензорные значения (если таковые имеются) должны быть явно поставлены.
Отладка
В общем, код отладки проще в нетерпеливом режиме, чем внутриtf.function
Полем Вы должны убедиться, что ваш код выполняет без ошибок в нетерпеливом режиме перед украшением с помощьюtf.function
Полем Чтобы помочь в процессе отладки, вы можете позвонитьtf.config.run_functions_eagerly(True)
чтобы глобально отключить и реконструироватьtf.function
Полем
При отслеживании проблем, которые появляются только внутриtf.function
, вот несколько советов:
- Простой старый питон
print
Вызовы выполняются только во время отслеживания, помогая вам отслеживать, когда ваша функция прослеживается. tf.print
Вызовы будут выполняться каждый раз и могут помочь вам отслеживать промежуточные значения во время выполнения.tf.debugging.enable_check_numerics
это простой способ отслеживать, где создаются NAN и Inf.pdb
(Отладчик Python) может помочь вам понять, что происходит во время трассировки. (Предостережение:pdb
Вернут вас в исходный код, преобразованный автографами.)
Первоначально опубликовано на
Оригинал