Двойной срок службы функции тензора

Двойной срок службы функции тензора

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.functionS внутри других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.functionS может быть быстрее, чем нетерпеливый код, особенно для графиков со многими небольшими операциями. Но для графиков с несколькими дорогими операциями (например, свертыми) вы можете не увидеть много ускорения.

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 этот пробел, разделяя ваш код на два этапа:

  1. На первом этапе, называемом «трассировкой», tf.function создает новый TF.Graph. Код Python работает нормально, но все операции TensorFlow (например, добавление двух тензоров) отложены: они захватываются TF.Graph и не запускаются.
  2. На втором этапе 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.GraphS генерируется из кода Python.
  • Экземплярtf.Graphспециализируется на конкретных типах ввода, которыми он был прослежен. Различные типы требуют обращения.
  • Каждый прослеживаетсяtf.Graphимеет соответствующийConcreteFunctionПолем
  • Аtf.functionуправляет кэшемConcreteFunctionS и выбирает правильный для ваших входов.
  • tf.functionОкутает функцию Python, которая будет прослежена, возвращаяtf.types.experimental.PolymorphicFunctionобъект.

Правила трассировки

Когда называется, аtf.functionсначала оценивает тип каждого входного аргумента, используяtf.types.experimental.TraceTypeкаждого аргумента. Это используется для построенияtf.types.experimental.FunctionTypeописывая подпись желаемогоConcreteFunctionПолем Мы сравниваем этоFunctionTypeвFunctionTypeS существующих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, тип параметризованTensorS.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и соответствующий предшествующий

    • Сначала он проверяет, является ли объект тем же объектом, который используется в предыдущем трассировке (с помощью Pythonid()илиis) Обратите внимание, что это все равно будет соответствовать, если объект изменился, поэтому, если вы используете объекты Python какtf.functionаргументы лучше всего использоватьнеизменени
    • Далее он проверяет, равен ли объект объекту, используемому в предыдущем трассировке (с помощью Python==)

    Примечаниечто эта процедура только сохраняетSleedRefдля объекта и, следовательно, работает только до тех пор, пока объект находится в области объема/не удален

Примечание:TraceTypeоснован наtf.functionвходные параметры, поэтому изменения в глобальном исвободные переменныеОдно не создаст нового следа. Видетьэтот разделдля рекомендуемых практик при работе с Python Global и Free переменными.

Контроль обращения

Поездка, когда ваш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.functionS для повторного использования трассов для ввода переменного размера. Вход переменного размера может произойти, если у вас есть последовательности разной длины или изображения разных размеров для каждой партии.

@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Чтобы взять под контрольExtensionTypeS.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.GraphS не подлежат напрямую. Мы на самом деле используем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Вернут вас в исходный код, преобразованный автографами.)

Первоначально опубликовано наTensorflow сайтВЭта статья появляется здесь под новым заголовком и лицензирована подCC на 4,0.Образцы кода, разделенные подApache 2.0 Лицензия.


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE