الرد على دعوة الحضور لحضور حدث TensorFlow Everywhere المحلي اليوم!
ترجمت واجهة Cloud Translation API‏ هذه الصفحة.
Switch to English

أداء أفضل مع وظيفة tf

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

في TensorFlow 2 ، يتم تشغيل التنفيذ الحثيث افتراضيًا. واجهة المستخدم سهلة الاستخدام ومرنة (تشغيل العمليات لمرة واحدة أسهل بكثير وأسرع) ، ولكن يمكن أن يأتي ذلك على حساب الأداء وقابلية النشر.

يمكنك استخدام tf.function لعمل رسوم بيانية من برامجك. إنها أداة تحويل تقوم بإنشاء رسوم بيانية لتدفق البيانات مستقلة عن Python من كود Python الخاص بك. سيساعدك هذا في إنشاء نماذج عالية الأداء ومحمولة ، ويلزم استخدام SavedModel .

سيساعدك هذا الدليل على تصور كيفية عمل tf.function تحت الغطاء حتى تتمكن من استخدامها بفعالية.

النقاط الرئيسية والتوصيات هي:

  • تصحيح الأخطاء في الوضع @tf.function ، ثم تزيينها باستخدام @tf.function .
  • لا تعتمد على تأثيرات Python الجانبية مثل تحور الكائن أو إلحاق القائمة.
  • تعمل 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 التي تحددها (على سبيل المثال من خلال تطبيق decorator @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.003833446999806256
Function conv: 0.004606625999940661
Note how there's not much difference in performance for convolutions

اقتفاء أثر

يشرح هذا القسم كيفية Function تحت الغطاء ، بما في ذلك تفاصيل التنفيذ التي قد تتغير في المستقبل . ومع ذلك ، بمجرد أن تفهم لماذا ومتى يحدث التتبع ، يصبح استخدام tf.function بشكل فعال أسهل بكثير!

ما هو "التتبع"؟

تقوم إحدى Function بتشغيل برنامجك في رسم بياني TensorFlow . ومع ذلك ، لا يمكن tf.Graph أن يمثل كل الأشياء التي تكتبها في برنامج TensorFlow الشغوف. على سبيل المثال ، تدعم Python تعدد الأشكال ، لكن tf.Graph يتطلب أن يكون لمدخلاتها نوع وأبعاد بيانات محددة. أو يمكنك أداء مهام جانبية مثل قراءة وسيطات سطر الأوامر ، أو إثارة خطأ ، أو العمل باستخدام كائن Python أكثر تعقيدًا ؛ لا يمكن تشغيل أي من هذه الأشياء في رسم 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: float32 Tensor, shape=()
  Returns:
    float32 Tensor, shape=()

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

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

حتى الآن ، رأيت أن tf.function تنشئ طبقة إرسال ديناميكية مخزنة مؤقتًا عبر منطق تتبع الرسم البياني في TensorFlow. لتكون أكثر تحديدًا حول المصطلحات:

  • tf.Graph هو tf.Graph للغة والمحمول لحساب TensorFlow.
  • تقوم tf.Graph ConcreteFunction بلف رسم tf.Graph .
  • تقوم إحدى Function بإدارة ذاكرة التخزين المؤقت Function ConcreteFunction وتختار العنصر المناسب لمدخلاتك.
  • تلف دالة tf.function دالة بايثون ، tf.function كائن Function .
  • ينشئ التتبع رسمًا tf.Graph في tf.Graph ConcreteFunction ، تُعرف أيضًا باسم التتبع.

قواعد التعقب

تحدد Function ما إذا كان سيتم إعادة استخدام Function ConcreteFunction التي تم تتبعها عن طريق حساب مفتاح ذاكرة التخزين المؤقت من args و kwargs للإدخال. مفتاح التخزين المؤقت هو مفتاح يحدد وظيفة ConcreteFunction بناءً على args and kwargs لاستدعاء Function ، وفقًا للقواعد التالية (والتي قد تتغير):

  • المفتاح الذي يتم إنشاؤه لوسيطة tf.Tensor هو شكله ونوعه.
  • بدءًا من TensorFlow 2.3 ، يكون المفتاح الذي تم إنشاؤه tf.Variable هو tf.Variable id() .
  • المفتاح الذي تم إنشاؤه لبايثون البدائي هو قيمته. المفتاح إنشاؤها من أجل متداخلة dict الصورة، list الصورة، tuple ق، namedtuple الصورة، و attr الصورة هي الصفوف (tuple) بالارض. (نتيجة لهذا التسطيح ، فإن استدعاء دالة محددة بهيكل متداخل مختلف عن تلك المستخدمة أثناء التتبع سيؤدي إلى خطأ في النوع).
  • بالنسبة لجميع أنواع Python الأخرى ، تعتمد المفاتيح على id() الكائن id() بحيث يتم تتبع الطرق بشكل مستقل لكل مثيل للفئة.

السيطرة على الاسترداد

متتبعا، وهو عندما الخاص بك Function يخلق أثر أكثر من واحد، يساعد يضمن أن TensorFlow يولد الرسوم البيانية الصحيحة لكل مجموعة من المدخلات. ومع ذلك ، فإن البحث عن المفقودين عملية مكلفة! إذا كان لديك Function يتتبع رسم بياني جديد لكل مكالمة، وستجد أن تنفيذ التعليمات البرمجية ببطء أكثر مما لو كنت لم تستخدم 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])))
# We 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]]))

# We 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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-20f544b8adbf>", 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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-20f544b8adbf>", 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 s بإعادة استخدام الآثار لمدخلات متغيرة الحجم. يمكن أن يحدث إدخال متغير الحجم إذا كان لديك تسلسلات ذات أطوال مختلفة ، أو صور بأحجام مختلفة لكل دفعة (انظر دروس Transformer و Deep Dream على سبيل المثال).

@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 لتقليل الاسترداد.

    في كثير من الأحيان ، يتم استخدام وسيطات Python للتحكم في num_layers=10 الرسم البياني - على سبيل المثال ، عدد num_layers=10 أو training=True أو nonlinearity='relu' . لذلك إذا تغيرت حجة بايثون ، فمن المنطقي أن تضطر إلى إعادة تتبع الرسم البياني.

    ومع ذلك ، من المحتمل ألا يتم استخدام وسيطة Python للتحكم في إنشاء الرسم البياني. في هذه الحالات ، يمكن للتغيير في قيمة Python أن يؤدي إلى ارتداد غير ضروري. خذ ، على سبيل المثال ، حلقة التدريب هذه ، والتي سيقوم 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

إذا كنت بحاجة إلى فرض الاسترداد ، فقم بإنشاء 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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-e4e2860a4364>", 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 ، تمت إزالة حجج 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 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.6/site-packages/tensorflow/python/eager/function.py", line 1683, in _call_impl
    cancellation_manager)
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/eager/function.py", line 1728, 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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-d163f3d206cb>", 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.function قبل التزيين tf.function . للمساعدة في عملية التصحيح ، يمكنك استدعاء tf.config.run_functions_eagerly(True) لتعطيل tf.function وإعادة تمكينها.

عند تعقب المشكلات التي تظهر فقط داخل tf.function ، إليك بعض النصائح:

  • لا يتم تنفيذ مكالمات print Python القديمة البسيطة إلا أثناء التتبع ، مما يساعدك على تعقب (إعادة) تتبع وظيفتك.
  • سيتم تنفيذ مكالمات tf.print كل مرة ، ويمكن أن تساعدك على تعقب القيم الوسيطة أثناء التنفيذ.
  • tf.debugging.enable_check_numerics هي طريقة سهلة لتعقب مكان إنشاء NaNs و Inf.
  • يمكن أن يساعدك pdb على فهم ما يحدث أثناء التتبع. (تحذير: سوف ينقلك PDB إلى كود المصدر المحول AutoGraph.)

تحويلات AutoGraph

AutoGraph هي مكتبة تعمل بشكل افتراضي في tf.function ، وتحول مجموعة فرعية من كود Python المتهور إلى عمليات TensorFlow المتوافقة مع الرسم البياني. يتضمن ذلك التحكم في التدفق مثل if ، for ، while .

تستمر عمليات TensorFlow مثل tf.cond و tf.while_loop في العمل ، ولكن غالبًا ما يكون التحكم في التدفق أسهل في الكتابة والفهم عند كتابته بلغة Python.

# 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.418865323 0.911027312 0.382812262 0.722406387 0.160078526]
[0.39597407 0.721625 0.365147263 0.618397713 0.158725053]
[0.376498967 0.617914855 0.34973979 0.550011516 0.157405376]
[0.359663159 0.549674571 0.336144745 0.500528812 0.156118125]
[0.344917297 0.500276268 0.324031234 0.462533 0.154862]
[0.331860244 0.462334394 0.313147396 0.43214643 0.15363577]
[0.320191294 0.431984901 0.303297669 0.407113552 0.152438238]
[0.309679836 0.406978786 0.294327527 0.386019081 0.151268333]
[0.300145775 0.385904342 0.286112964 0.36792323 0.150125]
[0.29144603 0.367824018 0.278553277 0.352173865 0.149007246]
[0.283465207 0.352086931 0.271565557 0.338302 0.147914127]
[0.276109159 0.338225 0.265080959 0.325960636 0.146844745]
[0.269300193 0.325891823 0.259041727 0.31488657 0.145798281]
[0.262973547 0.314824551 0.253399 0.304876 0.144773886]
[0.257074684 0.304819793 0.248111084 0.29576847 0.143770814]
[0.25155738 0.29571715 0.243142202 0.287435412 0.142788336]
[0.246382073 0.287388295 0.238461465 0.279772669 0.14182575]
[0.241514727 0.279729217 0.234042 0.272694677 0.140882403]
[0.23692593 0.272654444 0.229860321 0.266130418 0.139957651]
[0.232590035 0.266093045 0.225895762 0.260020494 0.139050901]
[0.228484616 0.259985656 0.222130179 0.254314691 0.13816157]
[0.224589869 0.254282087 0.218547404 0.248970196 0.137289107]
[0.220888361 0.248939633 0.215133116 0.243950352 0.13643299]
[0.217364609 0.243921608 0.211874455 0.23922351 0.135592714]
[0.214004785 0.239196405 0.208759964 0.234762147 0.134767786]
[0.210796505 0.234736577 0.20577924 0.230542287 0.133957744]
[0.207728744 0.230518073 0.202923 0.226542845 0.133162171]

<tf.Tensor: shape=(5,), dtype=float32, numpy=
array([0.20479149, 0.22651988, 0.20018281, 0.22274524, 0.13238062],
      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)


الشرطية

سوف يقوم AutoGraph بتحويل بعض عبارات if <condition> إلى مكالمات tf.cond المكافئة. يتم إجراء هذا الاستبدال إذا كان <condition> هو موتر. وبخلاف ذلك ، يتم تنفيذ عبارة if كشرط لبايثون.

يتم تنفيذ شرط Python أثناء التتبع ، لذلك سيتم إضافة فرع واحد بالضبط من الشرط إلى الرسم البياني. بدون الرسم البياني التلقائي ، لن يتمكن هذا الرسم البياني المتعقب من أخذ الفرع البديل إذا كان هناك تدفق تحكم يعتمد على البيانات.

يتتبع 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

راجع الوثائق المرجعية للحصول على قيود إضافية على عبارات if المحولة تلقائيًا.

الحلقات

سوف يقوم AutoGraph بتحويل بعض عبارات for and while إلى عمليات تكرار TensorFlow المكافئة ، مثل tf.while_loop . tf.while_loop . إذا لم يتم تحويلها ، يتم تنفيذ حلقة for أو while كحلقة Python.

يتم هذا الاستبدال في الحالات التالية:

  • for x in y : إذا كانت y هي Tensor ، tf.while_loop إلى tf.while_loop . في الحالة الخاصة حيث y هيtf.data.Dataset ، يتم إنشاء مجموعة منtf.data.Dataset ops.
  • while <condition> : إذا كان <condition> هو Tensor ، tf.while_loop إلى tf.while_loop . tf.while_loop .

يتم تنفيذ حلقة Python أثناء التتبع ، مما يضيف عمليات إضافية إلى tf.Graph لكل تكرار للحلقة.

تتعقب حلقة TensorFlow جسم الحلقة وتختار ديناميكيًا عدد التكرارات التي سيتم تشغيلها في وقت التنفيذ. يظهر جسم الحلقة مرة واحدة فقط في tf.Graph إنشاؤه.

راجع الوثائق المرجعية للحصول على قيود إضافية على كشوفات الرسم البياني AutoGraph المحولة for and while .

التكرار على بيانات بايثون

تتمثل إحدى tf.function الشائعة في إجراء حلقة حول بيانات Python / tf.function داخل tf.function . سيتم تنفيذ هذه الحلقة أثناء عملية التتبع ، بإضافة نسخة من نموذجك إلى tf.Graph لكل تكرار للحلقة.

إذا كنت ترغب في التفاف حلقة التدريب بأكملها في tf.function ، فإن الطريقة الأكثر أمانًا للقيام بذلك هي لف بياناتك على هيئةtf.data.Dataset بحيث يقوم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 10 nodes in its graph
train(<FlatMapDataset shapes: (<unknown>, <unknown>), types: (tf.int32, tf.int32)>) contains 10 nodes in its graph

عند تغليف بيانات Python / Numpy في مجموعة بيانات ، tf.data.Dataset.from_generator في tf.data.Dataset.from_generator مقابل tf.data.Dataset.from_tensors . سيحتفظ الأول بالبيانات في Python tf.py_function عبر tf.py_function والتي يمكن أن يكون لها آثار على الأداء ، في حين أن الثانية ستجمع نسخة من البيانات tf.constant() كبيرة في الرسم البياني ، والتي يمكن أن يكون لها آثار على الذاكرة.

قراءة البيانات من الملفات عبر TFRecordDataset / CsvDataset / إلخ. هي الطريقة الأكثر فاعلية لاستهلاك البيانات ، حيث يمكن لـ TensorFlow نفسها إدارة التحميل غير المتزامن والجلب المسبق للبيانات ، دون الحاجة إلى إشراك Python. لمعرفة المزيد ، راجع دليل tf.data .

تراكم القيم في حلقة

النمط الشائع هو تجميع القيم الوسيطة من حلقة. عادة ، يتم تحقيق ذلك عن طريق إلحاق قائمة Python أو إضافة إدخالات إلى قاموس Python. ومع ذلك ، نظرًا لأن هذه آثار جانبية لـ Python ، فإنها لن تعمل كما هو متوقع في حلقة غير متحكم فيها ديناميكيًا. استخدم tf.TensorArray لتجميع النتائج من الحلقة غير 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.04024231, 0.8750231 , 0.7828673 , 0.95796347],
        [0.6933379 , 1.4302992 , 1.0286844 , 1.2873636 ],
        [1.0946463 , 2.3919935 , 1.383383  , 1.6184174 ]],

       [[0.35952663, 0.8509462 , 0.04545867, 0.40333533],
        [0.43437874, 1.1436893 , 0.31203532, 0.4216336 ],
        [0.46808207, 1.8400187 , 0.3120687 , 1.1190954 ]]], dtype=float32)>

محددات

تحتوي Function TensorFlow على بعض القيود حسب التصميم التي يجب أن تكون على دراية بها عند تحويل دالة Python إلى Function .

تنفيذ الآثار الجانبية للبايثون

يمكن أن تتصرف التأثيرات الجانبية ، مثل الطباعة ، والإلحاق بالقوائم ، وتحوير الكرة الأرضية ، بشكل غير متوقع داخل إحدى Function ، وأحيانًا يتم تنفيذها مرتين أو لا تنفذ جميعها. تحدث فقط في المرة الأولى التي تقوم فيها باستدعاء Function مع مجموعة من المدخلات. بعد ذلك ، يتم إعادة تنفيذ tf.Graph ، دون تنفيذ كود بايثون.

القاعدة العامة هي تجنب الاعتماد على تأثيرات Python الجانبية في منطقك واستخدامهم فقط لتصحيح آثارك. خلاف ذلك، واجهات برمجة التطبيقات 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

إذا كنت ترغب في تنفيذ كود Python أثناء كل استدعاء Function ، فإن tf.py_function عبارة عن فتحة خروج. عيب tf.py_function هو أنها ليست محمولة أو tf.py_function بشكل خاص ، ولا يمكن حفظها مع SavedModel ، ولا تعمل بشكل جيد في الإعدادات الموزعة (متعددة GPU ، TPU). أيضًا ، نظرًا tf.py_function يجب tf.py_function في الرسم البياني ، فإنه يلقي جميع المدخلات / المخرجات إلى الموترات.

تغيير متغيرات بايثون العالمية والحرة

يعد تغيير متغيرات Python العامة والحرة أحد الآثار الجانبية لـ Python ، لذلك يحدث فقط أثناء التتبع.

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

يجب تجنب تحويل الحاويات مثل القوائم والإملاءات والأشياء الأخرى التي تعيش خارج Function . بدلاً من ذلك ، استخدم الحجج وكائنات TF. على سبيل المثال ، يحتوي قسم "تجميع القيم في حلقة" على مثال واحد لكيفية تنفيذ عمليات تشبه القائمة.

يمكنك ، في بعض الحالات ، التقاط الحالة ومعالجتها إذا كانت tf.Variable . هذه هي الطريقة التي يتم بها تحديث أوزان نماذج Keras باستدعاءات متكررة لنفس وظيفة ConcreteFunction .

استخدام مكررات ومولدات بايثون

تعتمد العديد من ميزات Python ، مثل المولدات والمكررات ، على وقت تشغيل Python لتتبع الحالة. بشكل عام ، بينما تعمل هذه التركيبات كما هو متوقع في الوضع الشغوف ، فهي أمثلة على الآثار الجانبية للبايثون وبالتالي تحدث فقط أثناء التتبع.

@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

تمامًا مثل كيفية امتلاك tf.TensorArray لـ tf.TensorArray المتخصصة tf.TensorArray القائمة ، فإنه يحتوي على tf.data.Iterator متخصص tf.data.Iterator التكرار. راجع قسم تحويلات الرسم البياني التلقائي للحصول على نظرة عامة. أيضًا ، يمكن أن تساعد واجهة برمجة تطبيقات tf.data تنفيذ أنماط المولد:

@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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-9a93d2e07632>", line 16, in <module>
    traced_f(4)
tensorflow.python.framework.errors_impl.FailedPreconditionError: 2 root error(s) found.
  (0) Failed precondition:  Error while reading resource variable _AnonymousVar3 from Container: localhost. This could mean that the variable was uninitialized. Not found: Resource localhost/_AnonymousVar3/N10tensorflow3VarE does not exist.
     [[node ReadVariableOp (defined at <ipython-input-1-9a93d2e07632>:4) ]]
  (1) Failed precondition:  Error while reading resource variable _AnonymousVar3 from Container: localhost. This could mean that the variable was uninitialized. Not found: Resource localhost/_AnonymousVar3/N10tensorflow3VarE does not exist.
     [[node ReadVariableOp (defined at <ipython-input-1-9a93d2e07632>:4) ]]
     [[ReadVariableOp/_2]]
0 successful operations.
0 derived errors ignored. [Op:__inference_f_782]

Function call stack:
f -> f


مشاكل معروفة

إذا كان لديك Function لا تقييم بشكل صحيح، ويمكن تفسير هذا الخطأ هذه المشكلات المعروفة التي من المقرر أن تكون ثابتة في المستقبل.

اعتمادًا على متغيرات Python العامة والحرة

Function بإنشاء Function ConcreteFunction جديدة عند استدعائها بقيمة جديدة من وسيطة Python. ومع ذلك ، فإنه لا يفعل ذلك لإغلاق Python أو globals أو 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)

يمكنك الإغلاق على الأسماء الخارجية ، طالما لم تقم بتحديث قيمها.

بالاعتماد على كائنات بايثون

التوصية لتمرير كائنات Python tf.function إلى tf.function لديها عدد من المشكلات المعروفة ، والتي من المتوقع إصلاحها في المستقبل. بشكل عام ، يمكنك الاعتماد على التتبع المتسق إذا كنت تستخدم بنية Python بدائية أو بنية متوافقة مع 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 جديدة في كل مرة تقوم فيها بتعديل كائنك لفرض الاستعادة:

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)

نظرًا لأن الاسترداد يمكن أن يكون مكلفًا ، يمكنك استخدام tf.Variable s 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.Variables في تتبعات جديدة. لا يُسمح حاليًا بإنشاء متغيرات جديدة في المكالمات اللاحقة ، ولكن سيكون في المستقبل.

مثال:

@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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-8a0913e250e0>", line 7, in <module>
    f(1.0)
ValueError: in user code:

    <ipython-input-1-8a0913e250e0>:3 f  *
        v = tf.Variable(1.0)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:262 __call__  **
        return cls._variable_v2_call(*args, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:256 _variable_v2_call
        shape=shape)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:67 getter
        return captured_getter(captured_previous, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/eager/def_function.py:731 invalid_creator_scope
        "tf.function-decorated function tried to create "

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


يمكنك إنشاء متغيرات داخل Function طالما أن هذه المتغيرات يتم إنشاؤها فقط في المرة الأولى التي يتم فيها تنفيذ الوظيفة.

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-decorated function tried to create variables on non-first call. عند استخدام أكثر من مُحسِّن Keras مع وظيفة tf.function . يحدث هذا الخطأ لأن tf.Variables داخليًا 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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises
    yield
  File "<ipython-input-1-d3d3937dbf1a>", line 18, in <module>
    train_step(w, x, y, opt2)
ValueError: in user code:

    <ipython-input-1-d3d3937dbf1a>:9 train_step  *
        optimizer.apply_gradients(zip(gradients, [w]))
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py:604 apply_gradients  **
        self._create_all_weights(var_list)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py:781 _create_all_weights
        _ = self.iterations
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py:788 __getattribute__
        return super(OptimizerV2, self).__getattribute__(name)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py:926 iterations
        aggregation=tf_variables.VariableAggregation.ONLY_FIRST_REPLICA)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py:1132 add_weight
        aggregation=aggregation)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/base.py:810 _add_variable_with_custom_getter
        **kwargs_for_getter)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/engine/base_layer_utils.py:142 make_variable
        shape=variable_shape if variable_shape else None)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:260 __call__
        return cls._variable_v1_call(*args, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:221 _variable_v1_call
        shape=shape)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:67 getter
        return captured_getter(captured_previous, **kwargs)
    /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/eager/def_function.py:731 invalid_creator_scope
        "tf.function-decorated function tried to create "

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


إذا كنت بحاجة إلى تغيير المُحسِّن أثناء التدريب ، فإن الحل البديل هو إنشاء Function جديدة لكل مُحسِّن ، واستدعاء 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-decorated function tried to create variables on non-first call. عند تمرير مثيلات نموذج مختلفة لنفس Function .

يحدث هذا الخطأ لأن نماذج Keras (التي لم يتم تحديد شكل إدخالها ) وطبقات tf.Variables تُنشئ tf.Variables s عندما يتم استدعاؤها لأول مرة. ربما تحاول تهيئة هذه المتغيرات داخل Function تم استدعاؤها بالفعل. لتجنب هذا الخطأ ، حاول استدعاء model.build(input_shape) لتهيئة كل الأوزان قبل تدريب النموذج.

قراءة متعمقة

للتعرف على كيفية تصدير Function وتحميلها ، انظر دليل SavedModel . لمعرفة المزيد حول تحسينات الرسم البياني التي يتم إجراؤها بعد التتبع ، راجع دليل Grappler . لمعرفة كيفية تحسين مسار البيانات الخاص بك وملف تعريف النموذج الخاص بك ، راجع دليل منشئ ملفات التعريف .