عملکرد بهتر با عملکرد tf

مشاهده در TensorFlow.org در Google Colab اجرا کنید مشاهده منبع در GitHub دانلود دفترچه یادداشت

در TensorFlow 2، اعدام مشتاق به طور پیش فرض تبدیل شده است. رابط کاربری بصری و انعطاف پذیر است (اجرای یکبار کار بسیار آسان تر و سریعتر است) ، اما این می تواند به هزینه عملکرد و قابلیت استفاده برسد.

شما می توانید با استفاده از tf.function به نمودار از برنامه های خود را. این یک ابزار تبدیل است که نمودارهای جریان داده مستقل از پایتون را از کد پایتون شما ایجاد می کند. این کمک خواهد کرد که شما ایجاد مدل های سازگار و قابل حمل، و آن را مورد نیاز برای استفاده SavedModel .

این راهنما به شما کمک خواهد مفهوم چگونه tf.function در زیر سرپوش، بنابراین شما می توانید آن را به طور موثر استفاده کنید.

موارد اصلی و توصیه ها عبارتند از:

  • اشکال زدایی در حالت مشتاق، و سپس با تزئین @tf.function .
  • به عوارض جانبی پایتون مانند جهش شی یا ضمایم لیست تکیه نکنید.
  • tf.function بهترین کار با عملیات TensorFlow؛ تماس های NumPy و Python به ثابت تبدیل می شوند.

برپایی

import tensorflow as tf

برای نشان دادن انواع خطاهایی که ممکن است با آنها مواجه شوید ، یک تابع کمکی تعریف کنید:

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))

اصول اولیه

استفاده

Function تعریف می کنید (برای مثال با استفاده از @tf.function دکوراتور) است که درست مثل یک عملیات هسته TensorFlow: شما می توانید آن را با اشتیاق اجرا؛ می توانید شیب ها را محاسبه کنید ؛ و غیره

@tf.function  # The decorator converts `add` into a `Function`.
def add(a, b):
  return a + b

add(tf.ones([2, 2]), tf.ones([2, 2]))  #  [[2., 2.], [2., 2.]]
<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>

شما می توانید با استفاده از Function ها در داخل یکدیگر 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)>

Function بازدید کنندگان می تواند سریع تر کد مشتاق، به ویژه برای نمودار با بسیاری از عملیات کوچک است. اما برای نمودارهایی که چند گزینه گران قیمت دارند (مانند پیچش ها) ، ممکن است سرعت زیادی مشاهده نکنید.

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")
Eager conv: 0.00443888600000264
Function conv: 0.004833394000002045
Note how there's not much difference in performance for convolutions

ردیابی

این بخش در معرض چگونه Function در زیر سرپوش، از جمله جزئیات پیاده سازی که ممکن است در آینده تغییر دهید. با این حال، هنگامی که شما را درک چرا و چه زمانی اتفاق می افتد ردیابی، آن را بسیار آسان تر به استفاده tf.function به طور موثر!

"ردیابی" چیست؟

Function برنامه خود را در یک اجرا می شود نمودار TensorFlow . با این حال، tf.Graph می توانید تمام چیزهایی که شما می خواهم در برنامه TensorFlow مشتاق ارسال نمایندگی نمی کند. به عنوان مثال، پایتون پشتیبانی از پلی مورفیسم، اما tf.Graph نیاز به ورودی های خود را به یک نوع داده مشخص و ابعاد. یا ممکن است کارهای جانبی مانند خواندن آرگومان های خط فرمان ، افزایش خطا یا کار با یک شیء پیچیده تر پایتون را انجام دهید. هیچ یک از این چیزها می تواند در یک اجرا tf.Graph .

Function پل این شکاف با جدا کد خود را در دو مرحله است:

1) در مرحله اول، به عنوان "ردیابی" با اشاره به، Function جدید ایجاد tf.Graph . کد پایتون اجرا می شود به طور معمول، اما تمام عملیات TensorFlow (مانند اضافه کردن دو تنسور) به تعویق افتاده است: آنها توسط اسیر tf.Graph و اجرا کنید.

2) در مرحله دوم، یک tf.Graph که شامل همه چیز است که در مرحله اول به تعویق افتاده بود اجرا شده است. این مرحله بسیار سریعتر از مرحله ردیابی است.

بسته به ورودی های آن، Function همیشه اولین مرحله هنگامی که آن را به نام اجرا کنید. مشاهده "قوانین ردیابی" زیر را برای دریافت یک حس بهتری از چگونگی آن را می سازد که تعیین. رد شدن از مرحله اول و تنها اجرای مرحله دوم چیزی است که عملکرد بالای TensorFlow را به شما ارائه می دهد.

هنگامی که Function می کند تصمیم به ردیابی، مرحله ردیابی است، بلافاصله توسط مرحله دوم به دنبال، به طوری که فراخوانی Function هر دو ایجاد و اجرا می شود tf.Graph . در ادامه خواهید دید که چگونه می توانید تنها مرحله ردیابی با اجرا get_concrete_function .

که استدلالهای از انواع مختلف عبور را به یک Function ، هر دو مرحله اجرا می شوند:

@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)

توجه داشته باشید که اگر شما بارها و بارها یک 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())
double(a)
  Args:
    a: int32 Tensor, shape=()
  Returns:
    int32 Tensor, shape=()

double(a)
  Args:
    a: float32 Tensor, shape=()
  Returns:
    float32 Tensor, shape=()

double(a)
  Args:
    a: string Tensor, shape=()
  Returns:
    string Tensor, shape=()

تا کنون، شما را دیده ام که tf.function ذخیره سازی، لایه پویا اعزام بیش از نمودار TensorFlow را ردیابی منطق ایجاد می کند. برای بیان دقیق تر اصطلاحات:

  • tf.Graph خام، زبان اگنوستیک، نمایندگی های قابل حمل از یک محاسبه TensorFlow است.
  • ConcreteFunction Wraps یک tf.Graph .
  • Function مدیریت یک انبار ConcreteFunction و میدارد یک حق برای ورودی های خود را.
  • tf.function کاری ادامه داده اند یک تابع پایتون، بازگشت Function شی.
  • ردیابی ایجاد یک tf.Graph و آن را کاری ادامه داده اند در یک ConcreteFunction ، همچنین به عنوان اثری شناخته شده است.

قوانین ردیابی

Function تعیین اینکه آیا برای استفاده مجدد از یک ترسیم ConcreteFunction با محاسبه کلید کش از استدلال و kwargs یک ورودی است. کلید کش یک کلید است که برای شناسایی یک ConcreteFunction بر اساس استدلال ورودی و kwargs از Function پاسخ، با توجه به قوانین زیر (که ممکن است تغییر):

  • کلید تولید شده برای یک tf.Tensor شکل و dtype آن است.
  • کلید تولید شده برای یک tf.Variable یک شناسه منحصر به فرد متغیر است.
  • کلید تولید شده برای پایتون ابتدایی (مانند int ، float ، str ) مقدار آن است.
  • کلید تولید شده برای تو در تو dict ها، list ها، tuple ها، namedtuple ، و attr بازدید کنندگان تاپل پهن از برگ کلید (است nest.flatten ). (در نتیجه این مسطح شدن ، فراخوانی یک تابع بتنی با ساختار تودرتو متفاوت از آنچه در حین ردیابی استفاده می شود منجر به TypeError می شود).
  • برای همه انواع دیگر پایتون ، کلید منحصر به فرد شی است. به این ترتیب یک تابع یا روش به طور مستقل برای هر نمونه ای که با آن فراخوانی می شود ، ردیابی می شود.

کنترل بازآموزی

Retracing است که زمانی که خود را Function ایجاد بیش از یک اثری، تضمین کمک می کند که TensorFlow تولید نمودار صحیح برای هر مجموعه ای از ورودی. با این حال ، ردیابی یک عملیات گران است! اگر خود را Function retraces یک نمودار جدید برای هر تماس، پیدا خواهید کرد که اجرا کد خود را آهسته تر از اگر شما از آن استفاده نکرد tf.function .

برای کنترل رفتار ردیابی ، می توانید از تکنیک های زیر استفاده کنید:

  • مشخص input_signature در tf.function به حد ردیابی است.
@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(ValueError):
  next_collatz(tf.constant([[1, 2], [3, 4]]))

# You specified an int32 dtype in the input signature, so this should fail.
with assert_raises(ValueError):
  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 'ValueError'>:
Caught expected exception 
  <class 'ValueError'>:
Traceback (most recent call last):
  File "/tmp/ipykernel_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/1851403433.py", line 9, in <module>
    next_collatz(tf.constant([[1, 2], [3, 4]]))
ValueError: Python inputs incompatible with input_signature:
  inputs: (
    tf.Tensor(
[[1 2]
 [3 4]], shape=(2, 2), dtype=int32))
  input_signature: (
    TensorSpec(shape=(None,), dtype=tf.int32, name=None))
Traceback (most recent call last):
  File "/tmp/ipykernel_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/1851403433.py", line 13, in <module>
    next_collatz(tf.constant([1.0, 2.0]))
ValueError: Python inputs incompatible with input_signature:
  inputs: (
    tf.Tensor([1. 2.], shape=(2,), dtype=float32))
  input_signature: (
    TensorSpec(shape=(None,), dtype=tf.int32, name=None))
  • مشخص کردن یک [هیچ] بعد در tf.TensorSpec برای انعطاف پذیری در استفاده مجدد اثری اجازه می دهد.

    از آنجا که TensorFlow مسابقات تانسورها بر اساس شکل آنها، با استفاده از یک None بعد به عنوان کلمات اجازه خواهد داد که Function به آثار استفاده مجدد برای ورودی متغیر اندازه. ورودی متغیر اندازه می تواند رخ دهد اگر شما توالی از طول های مختلف، و یا تصاویر با اندازه های مختلف برای هر دسته ای (مراجعه کنید ترانسفورماتور و عمیق رویای آموزش به عنوان مثال).

@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)
  • استدلال های پایتون را برای کاهش بازآموزی به Tensors ارسال کنید.

    اغلب، استدلال پایتون به hyperparameters کنترل و ساخت و سازه نمودار استفاده می شود - به عنوان مثال، num_layers=10 و یا training=True یا nonlinearity='relu' . بنابراین ، اگر استدلال پایتون تغییر کند ، منطقی است که مجبور باشید نمودار را دوباره تنظیم کنید.

    با این حال ، ممکن است از آرگومان پایتون برای کنترل ساخت نمودار استفاده نشود. در این موارد ، تغییر در مقدار پایتون می تواند باعث بازآموزی غیر ضروری شود. به عنوان مثال ، این حلقه آموزشی را در نظر بگیرید ، که AutoGraph به صورت پویا باز می شود. با وجود آثار متعدد ، نمودار ایجاد شده در واقع یکسان است ، بنابراین بازآرایی لازم نیست.

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

اگر شما به retracing نیروی نیاز دارید، ایجاد یک جدید Function . جدا Function اشیاء به آثار سهم تضمین شده است.

def f():
  print('Tracing!')
  tf.print('Executing')

tf.function(f)()
tf.function(f)()
Tracing!
Executing
Tracing!
Executing

بدست آوردن توابع بتنی

هر بار که یک تابع ردیابی می شود ، یک تابع ملموس جدید ایجاد می شود. شما می توانید به طور مستقیم دست آوردن یک تابع بتن، با استفاده از 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")))
Tracing with Tensor("a:0", shape=(), dtype=string)
tf.Tensor(b'cc', shape=(), dtype=string)

چاپ ConcreteFunction نمایش خلاصه ای از آرگومان ورودی آن (با انواع) و نوع خروجی آن است.

print(double_strings)
ConcreteFunction double(a)
  Args:
    a: string Tensor, shape=()
  Returns:
    string Tensor, shape=()

همچنین می توانید مستقیماً امضای یک تابع مشخص را بازیابی کنید.

print(double_strings.structured_input_signature)
print(double_strings.structured_outputs)
((TensorSpec(shape=(), dtype=tf.string, name='a'),), {})
Tensor("Identity:0", shape=(), dtype=string)

استفاده از یک اثر بتنی با انواع ناسازگار خطایی را ایجاد می کند

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 "/tmp/ipykernel_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/3196284684.py", line 2, in <module>
    double_strings(tf.constant(1))
tensorflow.python.framework.errors_impl.InvalidArgumentError: cannot compute __inference_double_162 as input #0(zero-based) was expected to be a string tensor but is a int32 tensor [Op:__inference_double_162]

ممکن است توجه داشته باشید که به استدلال های پایتون در امضای ورودی یک تابع خاص رفتار خاصی می شود. قبل از TensorFlow 2.3 ، استدلال های پایتون به سادگی از امضای تابع مشخص حذف شدند. با استفاده از TensorFlow 2.3 ، آرگومان های پایتون در امضا باقی می مانند ، اما محدودیتی برای تعیین مقدار تعیین شده در طول ردیابی وجود دارد.

@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 pow(a, b=2)
  Args:
    a: float32 Tensor, shape=<unknown>
  Returns:
    float32 Tensor, shape=<unknown>
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.7/site-packages/tensorflow/python/eager/function.py", line 1721, in _call_impl
    cancellation_manager)
  File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/function.py", line 1766, in _call_with_flat_signature
    self._flat_signature_summary(), ", ".join(sorted(kwargs))))
TypeError: pow(a) got unexpected keyword arguments: b.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/tmp/ipykernel_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/2310937119.py", line 4, in <module>
    square(tf.constant(10.0), b=3)
TypeError: ConcreteFunction pow(a, b) was constructed with int value 2 in b, but was called with int value 3

به دست آوردن نمودارها

هر تابع بتن لفاف بسته بندی قابل فراخوانی است در اطراف یک tf.Graph . اگرچه بازیابی واقعی 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.function . شما باید اطمینان حاصل شود که کد خود را اجرا در حالت مشتاق بدون خطا قبل از تزئین با tf.function . برای کمک به فرایند اشکال زدایی، شما می توانید پاسخ tf.config.run_functions_eagerly(True) در سطح جهان غیر فعال کردن و فعال کردن مجدد tf.function .

هنگامی که ردیابی کردن مسائلی که به نظر می رسد تنها در tf.function ، در اینجا برخی راهنمایی:

  • ساده و قدیمی پایتون print تماس فقط در طول ردیابی اجرا، کمک به شما پیگیری کردن هنگامی که عملکرد شما می شود (دوباره) پیش بینی کرد.
  • tf.print تماس خواهد هر بار اجرا، و می تواند به شما کمک کند پیگیری کردن نتایج متوسط در طول اجرای.
  • tf.debugging.enable_check_numerics یک راه آسان برای پیگیری کردن که در آن Nans به و جبهه ملی را پدید آورده است.
  • pdb (به دیباگر پایتون ) می تواند کمک به شما در درک چه خبر است در طول ردیابی. (اخطار: pdb شما را به کد منبع، دستخط تبدیل رها کنید.)

تحولات اتوگراف

دستخط یک کتابخانه در این است که به طور پیش فرض در است tf.function ، و تبدیل یک زیر مجموعه از پایتون کد مشتاق به نمودار های سازگار با عملیات TensorFlow. این شامل کنترل جریان مانند if ، for ، while .

عملیات TensorFlow مانند tf.cond و tf.while_loop همچنان به کار است، اما کنترل جریان اغلب ساده تر برای نوشتن و درک زمانی که در پایتون نوشته شده است.

# A simple loop

@tf.function
def f(x):
  while tf.reduce_sum(x) > 1:
    tf.print(x)
    x = tf.tanh(x)
  return x

f(tf.random.uniform([5]))
[0.134340048 0.12084043 0.899008512 0.317973018 0.984562874]
[0.133537665 0.120255642 0.715814829 0.307672977 0.755034447]
[0.132749513 0.119679272 0.614310205 0.298318624 0.638142884]
[0.131975174 0.119111128 0.547154069 0.289773166 0.56363374]
[0.131214246 0.118551008 0.498384237 0.281926 0.510668516]
[0.130466327 0.117998719 0.46084547 0.274686694 0.470465899]
[0.129731074 0.117454082 0.43077302 0.267980397 0.438575685]
[0.129008129 0.116916925 0.405967116 0.261744559 0.412463129]
[0.12829715 0.116387077 0.385043025 0.255926549 0.390562057]
[0.127597824 0.115864381 0.367079 0.250481546 0.371844649]
[0.126909807 0.115348652 0.351434082 0.245371237 0.355604142]
[0.126232818 0.11483977 0.337646723 0.240562648 0.341336131]
[0.125566557 0.114337578 0.325374842 0.236027122 0.328669697]
[0.124910742 0.113841921 0.314358741 0.231739685 0.317324936]
[0.124265119 0.113352649 0.304397196 0.227678493 0.30708608]
[0.123629414 0.112869643 0.295331478 0.223824292 0.297783881]
[0.123003371 0.112392753 0.287034482 0.220160022 0.289283246]
[0.122386754 0.111921862 0.279403061 0.216670573 0.281474978]
<tf.Tensor: shape=(5,), dtype=float32, numpy=
array([0.12177932, 0.11145685, 0.2723525 , 0.21334243, 0.27426964],
      dtype=float32)>

اگر کنجکاو هستید می توانید کد تولید شده را امتحان کنید.

print(tf.autograph.to_code(f.python_function))
def tf__f(x):
    with ag__.FunctionScope('f', 'fscope', ag__.ConversionOptions(recursive=True, user_requested=True, optional_features=(), internal_convert_user_code=True)) as fscope:
        do_return = False
        retval_ = ag__.UndefinedReturnValue()

        def get_state():
            return (x,)

        def set_state(vars_):
            nonlocal x
            (x,) = vars_

        def loop_body():
            nonlocal x
            ag__.converted_call(ag__.ld(tf).print, (ag__.ld(x),), None, fscope)
            x = ag__.converted_call(ag__.ld(tf).tanh, (ag__.ld(x),), None, fscope)

        def loop_test():
            return (ag__.converted_call(ag__.ld(tf).reduce_sum, (ag__.ld(x),), None, fscope) > 1)
        ag__.while_stmt(loop_test, loop_body, get_state, set_state, ('x',), {})
        try:
            do_return = True
            retval_ = ag__.ld(x)
        except:
            do_return = False
            raise
        return fscope.ret(retval_, do_return)

مشروط

دستخط برخی از تبدیل if <condition> اظهارات به معادل tf.cond تماس. این تعویض ساخته شده است اگر <condition> یک تانسور است. غیر این صورت، if بیانیه به عنوان یک شرطی پایتون اجرا می شود.

شرطی پایتون در طول ردیابی اجرا می شود ، بنابراین دقیقاً یک شاخه از شرطی به نمودار اضافه می شود. در صورت وجود جریان کنترل وابسته به داده ، بدون وجود AutoGraph ، این نمودار ردیابی شده نمی تواند شاخه جایگزین را بگیرد.

tf.cond آثار و می افزاید: هر دو شاخه از شرطی به نمودار، به صورت پویا انتخاب یک شاخه در زمان اجرا. ردیابی می تواند عوارض جانبی ناخواسته ای داشته باشد. چک کردن اثرات دستخط ردیابی برای اطلاعات بیشتر.

@tf.function
def fizzbuzz(n):
  for i in tf.range(1, n + 1):
    print('Tracing for loop')
    if i % 15 == 0:
      print('Tracing fizzbuzz branch')
      tf.print('fizzbuzz')
    elif i % 3 == 0:
      print('Tracing fizz branch')
      tf.print('fizz')
    elif i % 5 == 0:
      print('Tracing buzz branch')
      tf.print('buzz')
    else:
      print('Tracing default branch')
      tf.print(i)

fizzbuzz(tf.constant(5))
fizzbuzz(tf.constant(20))
Tracing for loop
Tracing fizzbuzz branch
Tracing fizz branch
Tracing buzz branch
Tracing default branch
1
2
fizz
4
buzz
1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz
16
17
fizz
19
buzz

مشاهده اسناد مرجع برای محدودیت های بیشتر در دستخط-تبدیل اگر اظهارات.

حلقه ها

دستخط برخی از تبدیل for و while اظهارات به TensorFlow معادل حلقه OPS، مانند tf.while_loop . اگر تبدیل نیست، for یا while حلقه به عنوان یک حلقه پایتون اجرا می شود.

این جایگزینی در موارد زیر انجام می شود:

  • for x in y : اگر y یک تانسور است، تبدیل به tf.while_loop . در موارد خاص که در آن y است tf.data.Dataset ، ترکیبی از tf.data.Dataset ارگانوفسفره ها تولید می شود.
  • while <condition> : اگر <condition> یک تانسور است، تبدیل به tf.while_loop .

پایتون اجرا حلقه در طول ردیابی، با اضافه کردن عملیات اضافی را به tf.Graph برای هر تکرار از حلقه است.

یک حلقه TensorFlow بدن حلقه را ردیابی می کند و به صورت پویا تعداد تکرارها را برای اجرا در زمان اجرا انتخاب می کند. بدن حلقه تنها یک بار در تولید به نظر می رسد tf.Graph .

را ببینید اسناد مرجع برای محدودیت های بیشتر در دستخط-تبدیل for و while اظهارات.

حلقه کردن اطلاعات پایتون

یک دام است به حلقه بیش از داده پایتون / نامپای در یک tf.function . این حلقه در طول فرآیند ردیابی اجرا، اضافه کردن یک کپی از مدل خود را به tf.Graph برای هر تکرار از حلقه است.

اگر شما می خواهید برای قرار دادن تمام حلقه های آموزشی در tf.function ، امن ترین راه برای انجام این کار است به بسته بندی کردن داده های خود را به عنوان یک tf.data.Dataset به طوری که دستخط به صورت پویا باز شدن حلقه آموزش خواهد شد.

def measure_graph_size(f, *args):
  g = f.get_concrete_function(*args).graph
  print("{}({}) contains {} nodes in its graph".format(
      f.__name__, ', '.join(map(str, args)), len(g.as_graph_def().node)))

@tf.function
def train(dataset):
  loss = tf.constant(0)
  for x, y in dataset:
    loss += tf.abs(y - x) # Some dummy computation.
  return loss

small_data = [(1, 1)] * 3
big_data = [(1, 1)] * 10
measure_graph_size(train, small_data)
measure_graph_size(train, big_data)

measure_graph_size(train, tf.data.Dataset.from_generator(
    lambda: small_data, (tf.int32, tf.int32)))
measure_graph_size(train, tf.data.Dataset.from_generator(
    lambda: big_data, (tf.int32, tf.int32)))
train([(1, 1), (1, 1), (1, 1)]) contains 11 nodes in its graph
train([(1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1), (1, 1)]) contains 32 nodes in its graph
train(<FlatMapDataset shapes: (<unknown>, <unknown>), types: (tf.int32, tf.int32)>) contains 6 nodes in its graph
train(<FlatMapDataset shapes: (<unknown>, <unknown>), types: (tf.int32, tf.int32)>) contains 6 nodes in its graph

زمانی که بسته بندی داده پایتون / نامپای در یک مجموعه، مراقب tf.data.Dataset.from_generator مقابل tf.data.Dataset.from_tensors . سابق داده ها در پایتون را حفظ خواهد کرد و واکشی آن را از طریق tf.py_function که می تواند پیامدهای عملکرد داشته، در حالی که دومی یک کپی از داده ها به عنوان یکی از بزرگ بسته نرم افزاری tf.constant() گره در گراف، که می تواند پیامدهای حافظه داشته باشد.

خواندن داده ها از فایل ها از طریق TFRecordDataset ، CsvDataset ، و غیره ترین راه موثر برای مصرف داده، به عنوان پس TensorFlow خودی خود می تواند بارگذاری ناهمگام و واکشی اولیه از داده ها بدون نیاز به درگیر پایتون مدیریت، است. برای کسب اطلاعات بیشتر، نگاه کنید به tf.data : خطوط لوله ورودی ساخت TensorFlow هدایت کند.

تجمع مقادیر در یک حلقه

یک الگوی رایج جمع آوری مقادیر متوسط ​​از یک حلقه است. به طور معمول ، این کار با افزودن به لیست پایتون یا افزودن مدخل به فرهنگ لغت پایتون انجام می شود. با این حال ، از آنجا که اینها عوارض جانبی پایتون هستند ، در یک حلقه باز نشده پویا آنطور که انتظار می رود کار نمی کنند. استفاده از tf.TensorArray به جمع آوری نتایج حاصل از یک حلقه به صورت پویا نعوظ.

batch_size = 2
seq_len = 3
feature_size = 4

def rnn_step(inp, state):
  return inp + state

@tf.function
def dynamic_rnn(rnn_step, input_data, initial_state):
  # [batch, time, features] -> [time, batch, features]
  input_data = tf.transpose(input_data, [1, 0, 2])
  max_seq_len = input_data.shape[0]

  states = tf.TensorArray(tf.float32, size=max_seq_len)
  state = initial_state
  for i in tf.range(max_seq_len):
    state = rnn_step(input_data[i], state)
    states = states.write(i, state)
  return tf.transpose(states.stack(), [1, 0, 2])

dynamic_rnn(rnn_step,
            tf.random.uniform([batch_size, seq_len, feature_size]),
            tf.zeros([batch_size, feature_size]))
<tf.Tensor: shape=(2, 3, 4), dtype=float32, numpy=
array([[[0.50723314, 0.05389965, 0.6574272 , 0.7971941 ],
        [0.76342154, 0.67873085, 0.6722325 , 0.80833924],
        [1.4959786 , 0.92963636, 1.181517  , 0.82988703]],

       [[0.05639362, 0.30149877, 0.5931294 , 0.58158445],
        [0.7223501 , 0.7357049 , 1.5900992 , 1.5196564 ],
        [1.2542304 , 1.443726  , 1.6683336 , 2.2088552 ]]], dtype=float32)>

محدودیت ها

TensorFlow Function دارای چند محدودیت های طراحی است که شما باید در هنگام تبدیل از یک تابع پایتون به یک آگاه باشند Function .

اجرای عوارض جانبی پایتون

عوارض جانبی مانند چاپ، اضافه به لیست ها، و جهش global دربرمیگیرد، می توانید به طور غیر منتظره در داخل یک رفتار Function ، گاهی اوقات دو بار یا نه همه اجرا. آنها فقط برای اولین بار اتفاق می افتد که شما یک تماس Function را با مجموعه ای از ورودی. پس از آن، ترسیم tf.Graph reexecuted است، بدون اجرای کد پایتون.

قاعده کلی این است که در منطق خود از اتکا به عوارض جانبی پایتون اجتناب کنید و فقط از آنها برای اشکال زدایی آثار خود استفاده کنید. در غیر این صورت، رابط های برنامه کاربردی TensorFlow مانند tf.data ، tf.print ، tf.summary ، tf.Variable.assign و tf.TensorArray هستند بهترین راه برای اطمینان از کد خود را توسط زمان اجرا TensorFlow با هر تماس را اعدام کرد.

@tf.function
def f(x):
  print("Traced with", x)
  tf.print("Executed with", x)

f(1)
f(1)
f(2)
Traced with 1
Executed with 1
Executed with 1
Traced with 2
Executed with 2

اگر شما می خواهم برای اجرای کد پایتون در طول هر فراخوانی یک Function ، tf.py_function یک دریچه خروج است. نقطه ضعف tf.py_function است که این قابل حمل نیست و یا به خصوص سازگار، می تواند با SavedModel ذخیره نمی شود، می کند و به خوبی در (چند پردازنده گرافیکی، TPU) تنظیم توزیع کار نمی کند. همچنین، از آنجایی tf.py_function باید به نمودار سیمی، آن کست تمام ورودی ها / خروجی به تانسورها.

تغییر متغیرهای جهانی و رایگان پایتون

تغییر پایتون جهانی و متغیرهای آزاد شمارش به عنوان یک عارضه جانبی پایتون، پس از آن تنها در طول ردیابی اتفاق می افتد.

external_list = []

@tf.function
def side_effect(x):
  print('Python side effect')
  external_list.append(x)

side_effect(1)
side_effect(1)
side_effect(1)
# The list append only happened once!
assert len(external_list) == 1
Python side effect

شما باید به جلوگیری جهش ظروف مانند لیست ها، dicts، اشیاء دیگر که در خارج زندگی می کنند Function . در عوض ، از آرگومان ها و اشیاء TF استفاده کنید. به عنوان مثال، بخش "جمع آوری مقادیر در یک حلقه" یک مثال از اینکه چگونه لیست مانند عملیات را می تواند اجرا شود.

شما می توانید، در برخی موارد، ضبط و دستکاری دولت اگر آن را یک است tf.Variable . این است که چگونه وزن از مدل های Keras با تماس های مکرر به همان به روز ConcreteFunction .

استفاده از تکرارکنندگان و ژنراتورهای پایتون

بسیاری از ویژگی های پایتون ، مانند ژنراتورها و تکرارکنندگان ، برای پیگیری وضعیت ، به زمان اجرا پایتون متکی هستند. به طور کلی ، در حالی که این سازه ها در حالت مشتاق کار می کنند ، نمونه هایی از عوارض جانبی پایتون هستند و بنابراین فقط در هنگام ردیابی اتفاق می افتند.

@tf.function
def buggy_consume_next(iterator):
  tf.print("Value:", next(iterator))

iterator = iter([1, 2, 3])
buggy_consume_next(iterator)
# This reuses the first value from the iterator, rather than consuming the next value.
buggy_consume_next(iterator)
buggy_consume_next(iterator)
Value: 1
Value: 1
Value: 1

درست مانند TensorFlow است تخصصی tf.TensorArray برای لیست سازه، آن را به یک متخصص tf.data.Iterator برای تکرار سازه. بخش مربوط به مشاهده تحولات دستخط برای یک مرور کلی. همچنین، tf.data API می تواند کمک به پیاده سازی الگوهای ژنراتور:

@tf.function
def good_consume_next(iterator):
  # This is ok, iterator is a tf.data.Iterator
  tf.print("Value:", next(iterator))

ds = tf.data.Dataset.from_tensor_slices([1, 2, 3])
iterator = iter(ds)
good_consume_next(iterator)
good_consume_next(iterator)
good_consume_next(iterator)
Value: 1
Value: 2
Value: 3

حذف tf.Variables بین Function تماس

خطای دیگری که ممکن است با آن روبرو شوید یک متغیر جمع آوری زباله است. ConcreteFunction فقط حفظ WeakRefs به متغیرهای آنها نزدیک بیش از، بنابراین شما باید یک مرجع به هر متغیر را حفظ کنید.

external_var = tf.Variable(3)
@tf.function
def f(x):
  return x * external_var

traced_f = f.get_concrete_function(4)
print("Calling concrete function...")
print(traced_f(4))

# The original variable object gets garbage collected, since there are no more
# references to it.
external_var = tf.Variable(4)
print()
print("Calling concrete function after garbage collecting its closed Variable...")
with assert_raises(tf.errors.FailedPreconditionError):
  traced_f(4)
Calling concrete function...
tf.Tensor(12, shape=(), dtype=int32)

Calling concrete function after garbage collecting its closed Variable...
Caught expected exception 
  <class 'tensorflow.python.framework.errors_impl.FailedPreconditionError'>:
Traceback (most recent call last):
  File "/tmp/ipykernel_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/3862898592.py", line 16, in <module>
    traced_f(4)
tensorflow.python.framework.errors_impl.FailedPreconditionError: 2 root error(s) found.
  (0) Failed precondition:  Could not find variable _AnonymousVar3. This could mean that the variable has been deleted. In TF1, it can also mean the variable is uninitialized. Debug info: container=localhost, status=Not found: Resource localhost/_AnonymousVar3/N10tensorflow3VarE does not exist.
     [[node ReadVariableOp (defined at tmp/ipykernel_8296/3862898592.py:4) ]]
  (1) Failed precondition:  Could not find variable _AnonymousVar3. This could mean that the variable has been deleted. In TF1, it can also mean the variable is uninitialized. Debug info: container=localhost, status=Not found: Resource localhost/_AnonymousVar3/N10tensorflow3VarE does not exist.
     [[node ReadVariableOp (defined at tmp/ipykernel_8296/3862898592.py:4) ]]
     [[ReadVariableOp/_2]]
0 successful operations.
0 derived errors ignored. [Op:__inference_f_772]

Function call stack:
f -> f

همه خروجی های یک تابع tf. باید مقادیر بازگشتی داشته باشند

به استثنای tf.Variable ، یک tf.function باید تمام خروجی خود بازگشت. تلاش برای دسترسی مستقیم به هر تنور از یک تابع بدون گذراندن مقادیر برگشتی باعث "نشت" می شود.

به عنوان مثال، تابع زیر "نشت" تانسور از طریق جهانی پایتون a x :

x = None

@tf.function
def leaky_function(a):
  global x
  x = a + 1  # Bad - leaks local tensor
  return a + 2

correct_a = leaky_function(tf.constant(1))

print(correct_a.numpy())  # Good - value obtained from function's returns
with assert_raises(AttributeError):
  x.numpy()  # Bad - tensor leaked from inside the function, cannot be used here
print(x)
3
Caught expected exception 
  <class 'AttributeError'>:
Tensor("add:0", shape=(), dtype=int32)
Traceback (most recent call last):
  File "/tmp/ipykernel_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/1216349822.py", line 13, in <module>
    x.numpy()  # Bad - tensor leaked from inside the function, cannot be used here
AttributeError: 'Tensor' object has no attribute 'numpy'

این حتی اگر مقدار نشتی نیز برگردانده شود صادق است:

@tf.function
def leaky_function(a):
  global x
  x = a + 1  # Bad - leaks local tensor
  return x  # Good - uses local tensor

correct_a = leaky_function(tf.constant(1))

print(correct_a.numpy())  # Good - value obtained from function's returns
with assert_raises(AttributeError):
  x.numpy()  # Bad - tensor leaked from inside the function, cannot be used here
print(x)
2
Caught expected exception 
  <class 'AttributeError'>:
Tensor("add:0", shape=(), dtype=int32)
Traceback (most recent call last):
  File "/tmp/ipykernel_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/3201904755.py", line 11, in <module>
    x.numpy()  # Bad - tensor leaked from inside the function, cannot be used here
AttributeError: 'Tensor' object has no attribute 'numpy'

شما ممکن است روبرو TypeError: tf.Graph captured an external symbolic tensor. هنگامی که از تانسور نمادین نشت شده در tf.Graph دیگر استفاده می کنید. مثلا،

@tf.function
def captures_leaked_tensor(b):
  b += x  # Bad - `x` is leaked from `leaky_function`
  return b

# `x` leaked from `leaky_function` is captured by `captures_leaked_tensor`.
with assert_raises(TypeError):
  captures_leaked_tensor(tf.constant(2))

concrete_func = captures_leaked_tensor.get_concrete_function(tf.constant(2))
print(concrete_func.captured_inputs)
assert x is concrete_func.captured_inputs[0]
Caught expected exception 
  <class 'TypeError'>:
[<tf.Tensor 'add:0' shape=() dtype=int32>]
Traceback (most recent call last):
  File "/tmp/ipykernel_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/2761600771.py", line 8, in <module>
    captures_leaked_tensor(tf.constant(2))
TypeError: An op outside of the function building code is being passed
a "Graph" tensor. It is possible to have Graph tensors
leak out of the function building context by including a
tf.init_scope in your function building code.
For example, the following function will fail:
  @tf.function
  def has_init_scope():
    my_constant = tf.constant(1.)
    with tf.init_scope():
      added = my_constant * 2
The graph tensor has name: add:0

معمولاً هنگام استفاده از دستورات Python یا ساختار داده ها ، نشتی از این قبیل رخ می دهد. این عبارات علاوه بر افشای تنسورهای غیرقابل دسترسی ، احتمالاً اشتباه هستند زیرا عوارض جانبی پایتون محسوب می شوند و تضمین نمی شود که در هر فراخوانی عملکرد اجرا شوند.

روشهای متداول برای نشت تنسورهای محلی شامل تغییر یک مجموعه خارجی پایتون یا یک شیء است:

class MyClass:

  def __init__(self):
    self.field = None

external_list = []
external_object = MyClass()

def leaky_function():
  a = tf.constant(1)
  external_list.append(a)  # Bad - leaks tensor
  external_object.field = a  # Bad - leaks tensor

مشکلات شناخته شده

اگر خود را Function به درستی ارزیابی نیست، خطای ممکن است توسط این مسائل شناخته شده است که طبق برنامه در آینده ثابت می شود توضیح داد.

بسته به متغیرهای جهانی و رایگان پایتون

Function جدید ایجاد ConcreteFunction که با یک مقدار جدید از یک استدلال پایتون به نام. با این حال، برای بسته شدن پایتون، global دربرمیگیرد، و یا nonlocals که انجام این کار نیست Function . اگر ارزش خود را تغییر در بین تماس به Function از Function هنوز هم ارزش آنها به حال زمانی که آن را ترسیم شده است استفاده کنید. این با نحوه عملکرد عملکردهای معمولی پایتون متفاوت است.

به همین دلیل ، ما یک سبک برنامه نویسی کاربردی را توصیه می کنیم که به جای بستن نام های خارجی از آرگومان ها استفاده می کند.

@tf.function
def buggy_add():
  return 1 + foo

@tf.function
def recommended_add(foo):
  return 1 + foo

foo = 1
print("Buggy:", buggy_add())
print("Correct:", recommended_add(foo))
Buggy: tf.Tensor(2, shape=(), dtype=int32)
Correct: tf.Tensor(2, shape=(), dtype=int32)
print("Updating the value of `foo` to 100!")
foo = 100
print("Buggy:", buggy_add())  # Did not change!
print("Correct:", recommended_add(foo))
Updating the value of `foo` to 100!
Buggy: tf.Tensor(2, shape=(), dtype=int32)
Correct: tf.Tensor(101, shape=(), dtype=int32)

تا زمانی که مقادیر آنها را به روز نکنید ، می توانید نامهای خارجی را ببندید.

بسته به اشیاء پایتون

توصیه به تصویب اشیاء پایتون به عنوان آرگومان در tf.function است تعدادی از مسائل شناخته شده است، که انتظار می رود در آینده ثابت می شود. به طور کلی، شما می توانید در ردیابی سازگار تکیه اگر شما استفاده از بدوی یا پایتون tf.nest ساختار سازگار به عنوان آرگومان و یا عبور در یک نمونه مختلف از یک شی را به یک Function . با این حال، Function خواهد اثری جدید ایجاد کنید زمانی که شما در همان جسم عبور و تنها ویژگی های آن را تغییر دهید.

class SimpleModel(tf.Module):
  def __init__(self):
    # These values are *not* tf.Variables.
    self.bias = 0.
    self.weight = 2.

@tf.function
def evaluate(model, x):
  return model.weight * x + model.bias

simple_model = SimpleModel()
x = tf.constant(10.)
print(evaluate(simple_model, x))
tf.Tensor(20.0, shape=(), dtype=float32)
print("Adding bias!")
simple_model.bias += 5.0
print(evaluate(simple_model, x))  # Didn't change :(
Adding bias!
tf.Tensor(20.0, shape=(), dtype=float32)

با استفاده از همان Function برای ارزیابی به عنوان مثال به روز شده از مدل خواهد شد حشره دار از مدل به روز شده است کلید کش همان به عنوان مدل اصلی است.

به همین دلیل، توصیه می کنیم که شما ارسال خود را Function به بسته به ویژگی های شی تغییرپذیر و یا اجتناب از ایجاد اشیاء جدید.

در صورتی که امکان پذیر نیست، یک راه حل این است که جدید Function ثانیه هر زمانی که شما جسم خود را به retracing نیروی تغییر دهید:

def evaluate(model, x):
  return model.weight * x + model.bias

new_model = SimpleModel()
evaluate_no_bias = tf.function(evaluate).get_concrete_function(new_model, x)
# Don't pass in `new_model`, `Function` already captured its state during tracing.
print(evaluate_no_bias(x))
tf.Tensor(20.0, shape=(), dtype=float32)
print("Adding bias!")
new_model.bias += 5.0
# Create new Function and ConcreteFunction since you modified new_model.
evaluate_with_bias = tf.function(evaluate).get_concrete_function(new_model, x)
print(evaluate_with_bias(x)) # Don't pass in `new_model`.
Adding bias!
tf.Tensor(25.0, shape=(), dtype=float32)

به عنوان retracing را می توان گران ، شما می توانید با استفاده از tf.Variable به عنوان ویژگی های شی، که می تواند جهش یافته (اما تغییر نکرده است، مراقب باشید!) برای یک اثر مشابه بدون نیاز به جابجایی.

class BetterModel:

  def __init__(self):
    self.bias = tf.Variable(0.)
    self.weight = tf.Variable(2.)

@tf.function
def evaluate(model, x):
  return model.weight * x + model.bias

better_model = BetterModel()
print(evaluate(better_model, x))
tf.Tensor(20.0, shape=(), dtype=float32)
print("Adding bias!")
better_model.bias.assign_add(5.0)  # Note: instead of better_model.bias += 5
print(evaluate(better_model, x))  # This works!
Adding bias!
tf.Tensor(25.0, shape=(), dtype=float32)

ایجاد tf. متغیرها

Function تنها پشتیبانی از تک قلو tf.Variable بازدید کنندگان یک بار در اولین تماس ایجاد شده، و مورد استفاده مجدد قرار در سراسر فراخوانی تابع پس از آن. تکه کد زیر یک جدید ایجاد tf.Variable در هر فراخوانی تابع، که نتایج در یک ValueError استثنا.

مثال:

@tf.function
def f(x):
  v = tf.Variable(1.0)
  return v

with assert_raises(ValueError):
  f(1.0)
Caught expected exception 
  <class 'ValueError'>:
Traceback (most recent call last):
  File "/tmp/ipykernel_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/3018268426.py", line 7, in <module>
    f(1.0)
ValueError: in user code:

    /tmp/ipykernel_8296/3018268426.py:3 f  *
        v = tf.Variable(1.0)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/variables.py:268 __call__  **
        return cls._variable_v2_call(*args, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/variables.py:262 _variable_v2_call
        shape=shape)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/variables.py:67 getter
        return captured_getter(captured_previous, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/def_function.py:765 invalid_creator_scope
        "tf.function-decorated function tried to create "

    ValueError: tf.function-decorated function tried to create variables on non-first call.

الگوی رایج این محدودیت به کار استفاده می شود این است که با یک مقدار هیچ پایتون شروع، و سپس مشروط ایجاد tf.Variable اگر مقدار است هیچ:

class Count(tf.Module):
  def __init__(self):
    self.count = None

  @tf.function
  def __call__(self):
    if self.count is None:
      self.count = tf.Variable(0)
    return self.count.assign_add(1)

c = Count()
print(c())
print(c())
tf.Tensor(1, shape=(), dtype=int32)
tf.Tensor(2, shape=(), dtype=int32)

استفاده از چندین بهینه ساز Keras

شما ممکن است روبرو ValueError: tf.function only supports singleton tf.Variables created on the first call. در هنگام استفاده از بیش از یک بهینه ساز Keras با tf.function . این خطا رخ می دهد به دلیل بهینه داخلی ایجاد tf.Variables هنگامی که آنها اعمال شیب برای اولین بار.

opt1 = tf.keras.optimizers.Adam(learning_rate = 1e-2)
opt2 = tf.keras.optimizers.Adam(learning_rate = 1e-3)

@tf.function
def train_step(w, x, y, optimizer):
   with tf.GradientTape() as tape:
       L = tf.reduce_sum(tf.square(w*x - y))
   gradients = tape.gradient(L, [w])
   optimizer.apply_gradients(zip(gradients, [w]))

w = tf.Variable(2.)
x = tf.constant([-1.])
y = tf.constant([2.])

train_step(w, x, y, opt1)
print("Calling `train_step` with different optimizer...")
with assert_raises(ValueError):
  train_step(w, x, y, opt2)
Calling `train_step` with different optimizer...
Caught expected exception 
  <class 'ValueError'>:
Traceback (most recent call last):
  File "/tmp/ipykernel_8296/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_8296/3167358578.py", line 18, in <module>
    train_step(w, x, y, opt2)
ValueError: in user code:

    /tmp/ipykernel_8296/3167358578.py:9 train_step  *
        optimizer.apply_gradients(zip(gradients, [w]))
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py:628 apply_gradients  **
        self._create_all_weights(var_list)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py:813 _create_all_weights
        _ = self.iterations
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py:820 __getattribute__
        return super(OptimizerV2, self).__getattribute__(name)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py:980 iterations
        aggregation=tf.VariableAggregation.ONLY_FIRST_REPLICA)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/optimizer_v2.py:1186 add_weight
        aggregation=aggregation)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/training/tracking/base.py:818 _add_variable_with_custom_getter
        **kwargs_for_getter)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/engine/base_layer_utils.py:129 make_variable
        shape=variable_shape if variable_shape else None)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/variables.py:266 __call__
        return cls._variable_v1_call(*args, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/variables.py:227 _variable_v1_call
        shape=shape)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/variables.py:67 getter
        return captured_getter(captured_previous, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/def_function.py:765 invalid_creator_scope
        "tf.function-decorated function tried to create "

    ValueError: tf.function-decorated function tried to create variables on non-first call.

اگر شما نیاز به تغییر بهینه ساز در طول آموزش، یک راه حل است برای ایجاد یک جدید Function برای هر بهینه ساز، خواستار ConcreteFunction به طور مستقیم.

opt1 = tf.keras.optimizers.Adam(learning_rate = 1e-2)
opt2 = tf.keras.optimizers.Adam(learning_rate = 1e-3)

# Not a tf.function.
def train_step(w, x, y, optimizer):
   with tf.GradientTape() as tape:
       L = tf.reduce_sum(tf.square(w*x - y))
   gradients = tape.gradient(L, [w])
   optimizer.apply_gradients(zip(gradients, [w]))

w = tf.Variable(2.)
x = tf.constant([-1.])
y = tf.constant([2.])

# Make a new Function and ConcreteFunction for each optimizer.
train_step_1 = tf.function(train_step).get_concrete_function(w, x, y, opt1)
train_step_2 = tf.function(train_step).get_concrete_function(w, x, y, opt2)
for i in range(10):
  if i % 2 == 0:
    train_step_1(w, x, y) # `opt1` is not used as a parameter. 
  else:
    train_step_2(w, x, y) # `opt2` is not used as a parameter.

با چندین مدل Keras استفاده می شود

شما همچنین ممکن است روبرو می شوند ValueError: tf.function only supports singleton tf.Variables created on the first call. در هنگام عبور از موارد مدل های مختلف به همان Function .

این خطا رخ می دهد چرا که مدل های Keras (که لازم نیست شکل ورودی خود را تعریف ) و لایه های Keras ایجاد tf.Variables بازدید کنندگان زمانی که آنها برای اولین بار به نام. شما ممکن است تلاش برای مقداردهی اولیه آن متغیرها در داخل یک Function ، که در حال حاضر به نام شده است. برای جلوگیری از این خطا، سعی کنید از تماس model.build(input_shape) به مقداردهی اولیه تمام وزن قبل از آموزش مدل.

خواندن بیشتر

برای یادگیری در مورد چگونگی صادرات و بار یک Function ، نگاه کنید به راهنمای SavedModel . برای کسب اطلاعات بیشتر در مورد بهینه سازی نمودار هایی که پس از ردیابی انجام شده، نگاه کنید به راهنمای Grappler . برای آشنایی با نحوه بهینه سازی خط لوله داده خود و مشخصات مدل خود، را ببینید راهنمای نیمرخ .