يوم مجتمع ML هو 9 نوفمبر! الانضمام إلينا للحصول على التحديثات من TensorFlow، JAX، وأكثر معرفة المزيد

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

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

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

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

سوف يساعدك هذا الدليل تصور كيف 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 تقوم بتعريف (على سبيل المثال عن طريق تطبيق @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.004223347999982252
Function conv: 0.004738496000015857
Note how there's not much difference in performance for convolutions

اقتفاء أثر

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

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

و Function يعمل البرنامج في TensorFlow الرسم البياني . ومع ذلك، tf.Graph لا يمكن أن تمثل كل الأشياء التي كنت أكتب في برنامج TensorFlow متحمسين. على سبيل المثال، بيثون يدعم تعدد الأشكال، ولكن 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: 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 لتتبع المنطق. لتكون أكثر تحديدًا حول المصطلحات:

  • A tf.Graph هو الخام، والملحد اللغة والتمثيل المحمولة من حساب TensorFlow.
  • A ConcreteFunction يلتف على 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 الصورة هي الصفوف (tuple) بالارض من أوراق مفاتيح (انظر nest.flatten ). (نتيجة لهذا التسطيح ، فإن استدعاء وظيفة ملموسة بهيكل متداخل مختلف عن تلك المستخدمة أثناء التتبع سيؤدي إلى خطأ في النوع).
  • بالنسبة لجميع أنواع Python الأخرى ، يكون المفتاح فريدًا للكائن. بهذه الطريقة يتم تتبع وظيفة أو طريقة بشكل مستقل لكل حالة يتم استدعاؤها معها.

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

متتبعا، وهو عندما الخاص بك 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])))
# 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_31121/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_31121/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_31121/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_31121/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' . لذلك ، إذا تغيرت حجة بايثون ، فمن المنطقي أن تضطر إلى إعادة تتبع الرسم البياني.

    ومع ذلك ، من الممكن ألا يتم استخدام وسيطة بايثون للتحكم في إنشاء الرسم البياني. في هذه الحالات ، يمكن للتغيير في قيمة 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 "/tmp/ipykernel_31121/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_31121/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 ، تمت إزالة حجج 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.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_31121/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_31121/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 ستنخفض كنت في شفرة مصدر-تحولت توقيعه).

تحويلات AutoGraph

التوقيع هو المكتبة التي هي على افتراضيا في 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.440381289 0.90519166 0.611484885 0.657032371 0.357666135]
[0.413960457 0.7188164 0.545171499 0.57638514 0.343156636]
[0.391830236 0.616175652 0.496892601 0.520033121 0.330292553]
[0.372936964 0.548459709 0.459669828 0.477725565 0.318783611]
[0.356557965 0.499364883 0.429815084 0.444420278 0.308406651]
[0.342178583 0.4616175 0.405166715 0.417301714 0.298986852]
[0.329420924 0.43140161 0.384361088 0.394654781 0.290385127]
[0.318000346 0.406491935 0.366488785 0.375366092 0.28248921]
[0.307697684 0.385489941 0.350916684 0.358676434 0.275207311]
[0.298341125 0.367465615 0.337188244 0.344047606 0.268463552]
[0.28979376 0.351772934 0.324964851 0.331086099 0.262194574]
[0.28194496 0.337946951 0.313989192 0.319496334 0.25634703]
[0.274704218 0.325643241 0.30406186 0.309051424 0.250875592]
[0.267996669 0.314600587 0.295025319 0.299573869 0.245741531]
[0.261759728 0.30461663 0.286753505 0.290922552 0.240911454]
[0.255940735 0.29553175 0.279144 0.282983691 0.236356467]
[0.250494868 0.287218243 0.272112608 0.2756643 0.232051343]
[0.245383754 0.279572427 0.265589505 0.26888752 0.227973983]
[0.240574405 0.272509277 0.25951609 0.262589365 0.224104956]
[0.236038208 0.26595816 0.253842831 0.256715834 0.220427066]
[0.23175019 0.25985986 0.248527542 0.25122118 0.21692507]
[0.227688447 0.254164428 0.243534029 0.246066257 0.213585317]
[0.223833725 0.24882926 0.238830984 0.2412173 0.210395679]
[0.220169008 0.243817821 0.234391257 0.236645207 0.207345188]
[0.216679126 0.239098579 0.230191082 0.232324496 0.204424]
[0.213350594 0.2346441 0.226209641 0.228232905 0.201623216]
[0.210171312 0.230430529 0.222428545 0.22435087 0.198934779]
[0.207130447 0.226436824 0.218831509 0.220661014 0.196351364]
[0.204218224 0.222644478 0.215404049 0.217147961 0.193866298]
[0.201425806 0.219037116 0.212133229 0.213798046 0.191473484]
[0.198745191 0.215600118 0.209007442 0.210598975 0.189167336]
[0.196169078 0.212320447 0.206016243 0.207539737 0.186942741]
<tf.Tensor: shape=(5,), dtype=float32, numpy=
array([0.19369087, 0.20918646, 0.20315024, 0.20461042, 0.184795  ],
      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 يتم تنفيذ البيان بوصفه ومشروطة بيثون.

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

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

A شرك المشترك هو حلقة على البيانات بيثون / نمباي ضمن 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 مدخلات الدليل.

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

النمط الشائع هو تجميع القيم الوسيطة من حلقة. عادة ، يتم تحقيق ذلك عن طريق إلحاق قائمة Python أو إضافة إدخالات إلى قاموس Python. ومع ذلك ، نظرًا لأن هذه آثار جانبية لـ Python ، فلن تعمل كما هو متوقع في حلقة غير متحكم فيها ديناميكيًا. استخدام 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.26657474, 0.64122915, 0.43046582, 0.21260166],
        [1.2160734 , 1.5840842 , 0.8233242 , 0.6383432 ],
        [1.9039613 , 2.1369288 , 0.99450636, 1.2205092 ]],

       [[0.89379036, 0.76746535, 0.87074137, 0.29888546],
        [1.6800594 , 1.6103547 , 1.2287309 , 1.1181811 ],
        [1.7122784 , 2.1740289 , 1.715089  , 1.6384293 ]]], dtype=float32)>

محددات

TensorFlow Function لديها عدد قليل من القيود حسب التصميم التي يجب أن تكون على علم عند تحويل وظيفة بيثون إلى 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

إذا كنت ترغب في تنفيذ التعليمات البرمجية بايثون خلال كل استدعاء و Function ، tf.py_function هو فتحة خروج. العيب من tf.py_function هو أنه لا المحمولة أو performant للبشكل خاص، لا يمكن حفظ مع SavedModel، ولا يعمل بشكل جيد في توزيع (متعدد GPU، 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 .

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

تعتمد العديد من ميزات 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

تماما مثل كيف 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_31121/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_31121/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_31121/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_31121/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_31121/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_31121/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)

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

with assert_raises(TypeError):
  captures_leaked_tensor(tf.constant(2))
2
Caught expected exception 
  <class 'AttributeError'>:
Tensor("add:0", shape=(), dtype=int32)
Caught expected exception 
  <class 'TypeError'>:
Traceback (most recent call last):
  File "/tmp/ipykernel_31121/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_31121/3734761697.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'
Traceback (most recent call last):
  File "/tmp/ipykernel_31121/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_31121/3734761697.py", line 20, 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 أو هياكل البيانات. بالإضافة إلى تسريب الموترات التي يتعذر الوصول إليها ، من المحتمل أيضًا أن تكون هذه العبارات خاطئة لأنها تعتبر آثارًا جانبية لـ Python ، ولا يتم ضمان تنفيذها عند كل استدعاء للوظيفة.

تتضمن الطرق الشائعة لتسريب الموترات المحلية أيضًا تغيير مجموعة 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 لا تقييم بشكل صحيح، ويمكن تفسير هذا الخطأ هذه المشكلات المعروفة التي من المقرر أن تكون ثابتة في المستقبل.

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

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

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 الصورة كما سمات كائن، والتي يمكن أن تحور (ولكن لم يتغير،! دقيق) للحصول على تأثير مشابه دون الحاجة إلى تقفي أثر.

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_31121/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_31121/3018268426.py", line 7, in <module>
    f(1.0)
ValueError: in user code:

    /tmp/ipykernel_31121/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_31121/3551158538.py", line 8, in assert_raises
    yield
  File "/tmp/ipykernel_31121/3167358578.py", line 18, in <module>
    train_step(w, x, y, opt2)
ValueError: in user code:

    /tmp/ipykernel_31121/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 . لمعرفة المزيد عن تحسينات الرسم البياني التي يتم تنفيذها بعد تتبع، راجع دليل الطارح . لمعرفة كيفية تحسين خط أنابيب البيانات الخاصة بك والبيانات الشخصية النموذج الخاص بك، راجع دليل التعريف .