ترجمت واجهة Cloud Translation API‏ هذه الصفحة.
Switch to English

مقدمة في الرسوم البيانية والوظائف

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

مقدمة في الرسوم البيانية tf.function

يذهب هذا الدليل تحت سطح TensorFlow و Keras لمعرفة كيفية عمل TensorFlow. إذا كنت ترغب بدلاً من ذلك في البدء على الفور مع Keras ، يرجى الاطلاع على مجموعتنا من أدلة Keras .

في هذا الدليل ، سترى جوهر كيف يسمح لك TensorFlow بإجراء تغييرات بسيطة على التعليمات البرمجية الخاصة بك للحصول على الرسوم البيانية ، وكيف يتم تخزينها وتمثيلها ، وكيف يمكنك استخدامها لتسريع وتصدير النماذج الخاصة بك.

هذه مقدمة قصيرة. للحصول على مقدمة كاملة لهذه المفاهيم ، انظر دليل tf.function .

ما هي الرسوم البيانية؟

في الأدلة الثلاثة السابقة ، شاهدت TensorFlow يعمل بلهفة . هذا يعني أن عمليات TensorFlow يتم تنفيذها بواسطة Python ، والتشغيل عن طريق العملية ، وإرجاع النتائج إلى Python. تستفيد Eager TensorFlow من GPUs ، مما يسمح لك بوضع المتغيرات ، والموترات ، وحتى العمليات على GPUs و TPU. من السهل أيضًا تصحيحه.

بالنسبة لبعض المستخدمين ، قد لا تحتاج أبدًا إلى مغادرة Python أو ترغب في ذلك.

ومع ذلك ، فإن تشغيل TensorFlow op-by-op في Python يمنع مجموعة من التسارع المتاحة بخلاف ذلك. إذا كان يمكنك استخراج حسابات الموتر من Python ، فيمكنك تحويلها إلى رسم بياني .

الرسوم البيانية هي هياكل البيانات التي تحتوي على مجموعة من كائنات tf.Operation ، والتي تمثل وحدات الحساب ؛ و tf.Tensor كائنات tf.Tensor ، والتي تمثل وحدات البيانات التي تتدفق بين العمليات. يتم تعريفها في سياق tf.Graph . نظرًا لأن هذه الرسوم البيانية هي هياكل بيانات ، يمكن حفظها وتشغيلها واستعادتها كلها بدون رمز Python الأصلي.

هذا هو شكل الرسم البياني البسيط المكون من طبقتين عندما يتم تخيله في TensorBoard.

رسم بياني tensorflow من طبقتين

فوائد الرسوم البيانية

مع الرسم البياني ، لديك قدر كبير من المرونة. يمكنك استخدام الرسم البياني لـ TensorFlow في البيئات التي لا تحتوي على مترجم Python ، مثل تطبيقات الهاتف المحمول والأجهزة المضمنة وخوادم الواجهة الخلفية. يستخدم TensorFlow الرسوم البيانية كتنسيق للنماذج المحفوظة عند تصديرها من Python.

يتم أيضًا تحسين الرسوم البيانية بسهولة ، مما يسمح للمترجم بإجراء تحويلات مثل:

  • يستنتج بشكل ثابت قيمة الموتر عن طريق طي العقد الثابتة في حسابك ("الطي المستمر") .
  • افصل الأجزاء الفرعية للحسابات المستقلة وقسمتها بين سلاسل المحادثات أو الأجهزة.
  • تبسيط العمليات الحسابية عن طريق القضاء على التعبيرات الفرعية الشائعة.

هناك نظام تحسين كامل ، Grappler ، لأداء هذا وتسريع آخر.

وباختصار ، فإن الرسوم البيانية مفيدة للغاية وتسمح لـ TensorFlow الخاص بك بالعمل بسرعة ، والتوازي ، والتشغيل بكفاءة على أجهزة متعددة .

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

تتبع الرسوم البيانية

الطريقة التي تقوم بها بإنشاء رسم بياني في TensorFlow هي استخدام tf.function ، إما tf.function مباشرة أو كديكور.

 import tensorflow as tf
import timeit
from datetime import datetime
 
 # Define a Python function
def function_to_get_faster(x, y, b):
  x = tf.matmul(x, y)
  x = x + b
  return x

# Create a `Function` object that contains a graph
a_function_that_uses_a_graph = tf.function(function_to_get_faster)

# Make some tensors
x1 = tf.constant([[1.0, 2.0]])
y1 = tf.constant([[2.0], [3.0]])
b1 = tf.constant(4.0)

# It just works!
a_function_that_uses_a_graph(x1, y1, b1).numpy()
 
array([[12.]], dtype=float32)

tf.function tf.function ذات الوظائف هي الاستدعاءات Python التي تعمل نفس معادلاتها Python. لديهم فئة معينة ( python.eager.def_function.Function ) ، لكن بالنسبة لك فهم يعملون فقط python.eager.def_function.Function .

يتتبع tf.function بشكل متكرر أي وظيفة Python يسميها.

 def inner_function(x, y, b):
  x = tf.matmul(x, y)
  x = x + b
  return x

# Use the decorator
@tf.function
def outer_function(x):
  y = tf.constant([[2.0], [3.0]])
  b = tf.constant(4.0)

  return inner_function(x, y, b)

# Note that the callable will create a graph that
# includes inner_function() as well as outer_function()
outer_function(tf.constant([[1.0, 2.0]])).numpy()
 
array([[12.]], dtype=float32)

إذا كنت قد استخدمت TensorFlow 1.x ، فستلاحظ أنه لم تكن بحاجة في أي وقت إلى تحديد Placeholder أو tf.Sesssion .

التحكم في التدفق والآثار الجانبية

يتم تحويل التحكم في التدفق والحلقات إلى TensorFlow عبر tf.autograph بشكل افتراضي. يستخدم Autograph مجموعة من الأساليب ، بما في ذلك توحيد تركيبات الحلقات ، والانفتاح ، ومعالجة AST .

 def my_function(x):
  if tf.reduce_sum(x) <= 1:
    return x * x
  else:
    return x-1

a_function = tf.function(my_function)

print("First branch, with graph:", a_function(tf.constant(1.0)).numpy())
print("Second branch, with graph:", a_function(tf.constant([5.0, 5.0])).numpy())
 
First branch, with graph: 1.0
Second branch, with graph: [4. 4.]

يمكنك استدعاء تحويل Autograph مباشرة لمعرفة كيفية تحويل Python إلى عمليات TensorFlow. هذا ، في الغالب ، غير قابل للقراءة ، ولكن يمكنك رؤية التحول.

 # Don't read the output too carefully.
print(tf.autograph.to_code(my_function))
 
def tf__my_function(x):
    with ag__.FunctionScope('my_function', '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 (retval_, do_return)

        def set_state(vars_):
            nonlocal retval_, do_return
            (retval_, do_return) = vars_

        def if_body():
            nonlocal retval_, do_return
            try:
                do_return = True
                retval_ = (ag__.ld(x) * ag__.ld(x))
            except:
                do_return = False
                raise

        def else_body():
            nonlocal retval_, do_return
            try:
                do_return = True
                retval_ = (ag__.ld(x) - 1)
            except:
                do_return = False
                raise
        ag__.if_stmt((ag__.converted_call(ag__.ld(tf).reduce_sum, (ag__.ld(x),), None, fscope) <= 1), if_body, else_body, get_state, set_state, ('retval_', 'do_return'), 2)
        return fscope.ret(retval_, do_return)


توقيعه تلقائيا المتحولين if-then فقرات، حلقات، break ، return ، continue ، وأكثر من ذلك.

في معظم الأحيان ، ستعمل Autograph دون اعتبارات خاصة. ومع ذلك ، هناك بعض التحذيرات ، ويمكن أن يساعد دليل tf.function هنا ، بالإضافة إلى مرجع التوقيع الكامل

رؤية تسريع

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

بالنسبة للحسابات المعقدة ، يمكن أن توفر الرسوم البيانية تسريعًا ملحوظًا. وذلك لأن الرسوم البيانية تقلل من اتصال Python إلى الجهاز ، وأداء بعض التسريع.

يقوم هذا الرمز بضع مرات بتشغيل بعض الطبقات الكثيفة الصغيرة.

 # Create an oveerride model to classify pictures
class SequentialModel(tf.keras.Model):
  def __init__(self, **kwargs):
    super(SequentialModel, self).__init__(**kwargs)
    self.flatten = tf.keras.layers.Flatten(input_shape=(28, 28))
    self.dense_1 = tf.keras.layers.Dense(128, activation="relu")
    self.dropout = tf.keras.layers.Dropout(0.2)
    self.dense_2 = tf.keras.layers.Dense(10)

  def call(self, x):
    x = self.flatten(x)
    x = self.dense_1(x)
    x = self.dropout(x)
    x = self.dense_2(x)
    return x

input_data = tf.random.uniform([60, 28, 28])

eager_model = SequentialModel()
graph_model = tf.function(eager_model)

print("Eager time:", timeit.timeit(lambda: eager_model(input_data), number=10000))
print("Graph time:", timeit.timeit(lambda: graph_model(input_data), number=10000))

 
Eager time: 4.9249971129993355
Graph time: 2.026840765000088

وظائف متعددة الأشكال

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

يمكنك استخدام هذه Function على جميع أنواع dtypes والأشكال المختلفة. في كل مرة تستدعيها بتوقيع وسيطة جديد ، يتم إعادة تتبع الوظيفة الأصلية مع الوسيطات الجديدة. تقوم Function بعد ذلك بتخزين tf.Graph المقابل لذلك التتبع في وظيفة concrete_function . إذا كانت الوظيفة قد تم تتبعها بالفعل مع هذا النوع من الحجة ، فما عليك سوى الحصول على الرسم البياني الذي تم تتبعه مسبقًا.

من الناحية النظرية ، إذن:

  • إن tf.Graph هو هيكل البيانات الخام والمحمول الذي يصف الحساب
  • Function هي التخزين المؤقت ، والتتبع ، والمرسل فوق الخرسانة
  • إن ConcreteFunction عبارة عن غلاف متوافق مع حريصة حول رسم بياني يتيح لك تنفيذ الرسم البياني من Python

فحص الوظائف المتعددة الأشكال

يمكنك فحص a_function ، والتي هي نتيجة استدعاء tf.function على دالة Python my_function . في هذا المثال ، a_function استدعاء a_function بثلاثة أنواع من الوسيطات ثلاث وظائف ملموسة مختلفة.

 print(a_function)

print("Calling a `Function`:")
print("Int:", a_function(tf.constant(2)))
print("Float:", a_function(tf.constant(2.0)))
print("Rank-1 tensor of floats", a_function(tf.constant([2.0, 2.0, 2.0])))
 
<tensorflow.python.eager.def_function.Function object at 0x7f466417bf60>
Calling a `Function`:
Int: tf.Tensor(1, shape=(), dtype=int32)
Float: tf.Tensor(1.0, shape=(), dtype=float32)
Rank-1 tensor of floats tf.Tensor([1. 1. 1.], shape=(3,), dtype=float32)

 # Get the concrete function that works on floats
print("Inspecting concrete functions")
print("Concrete function for float:")
print(a_function.get_concrete_function(tf.TensorSpec(shape=[], dtype=tf.float32)))
print("Concrete function for tensor of floats:")
print(a_function.get_concrete_function(tf.constant([2.0, 2.0, 2.0])))

 
Inspecting concrete functions
Concrete function for float:
ConcreteFunction my_function(x)
  Args:
    x: float32 Tensor, shape=()
  Returns:
    float32 Tensor, shape=()
Concrete function for tensor of floats:
ConcreteFunction my_function(x)
  Args:
    x: float32 Tensor, shape=(3,)
  Returns:
    float32 Tensor, shape=(3,)

 # Concrete functions are callable
# Note: You won't normally do this, but instead just call the containing `Function`
cf = a_function.get_concrete_function(tf.constant(2))
print("Directly calling a concrete function:", cf(tf.constant(2)))
 
Directly calling a concrete function: tf.Tensor(1, shape=(), dtype=int32)

في هذا المثال ، ترى الكثير في المكدس. ما لم تكن تدير عملية التتبع تحديدًا ، فلن تحتاج عادةً إلى استدعاء وظائف محددة مباشرةً كما هو موضح هنا.

العودة إلى الإعدام المتلهف

قد تجد نفسك تبحث في آثار المكدس الطويلة ، خاصة تلك التي تشير إلى tf.Graph أو with tf.Graph().as_default() . هذا يعني أنك على الأرجح تعمل في سياق الرسم البياني. تستخدم الوظائف الأساسية في TensorFlow سياقات الرسم البياني ، مثل model.fit() من model.fit() .

غالبًا ما يكون من الأسهل جدًا تصحيح التنفيذ المتلهف. يجب أن تكون آثار المكدس قصيرة نسبيًا وسهلة الفهم.

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

إليك بعض الطرق التي يمكنك من خلالها التأكد من أنك تسير بشغف:

  • استدعاء النماذج والطبقات مباشرة كمواد قابلة للاستدعاء

  • عند استخدام Keras compile / fit ، استخدم model.compile(run_eagerly=True) في وقت الترجمة model.compile(run_eagerly=True)

  • تعيين وضع التنفيذ العام عبر tf.config.run_functions_eagerly(True)

باستخدام run_eagerly=True

 # Define an identity layer with an eager side effect
class EagerLayer(tf.keras.layers.Layer):
  def __init__(self, **kwargs):
    super(EagerLayer, self).__init__(**kwargs)
    # Do some kind of initialization here

  def call(self, inputs):
    print("\nCurrently running eagerly", str(datetime.now()))
    return inputs
 
 # Create an override model to classify pictures, adding the custom layer
class SequentialModel(tf.keras.Model):
  def __init__(self):
    super(SequentialModel, self).__init__()
    self.flatten = tf.keras.layers.Flatten(input_shape=(28, 28))
    self.dense_1 = tf.keras.layers.Dense(128, activation="relu")
    self.dropout = tf.keras.layers.Dropout(0.2)
    self.dense_2 = tf.keras.layers.Dense(10)
    self.eager = EagerLayer()

  def call(self, x):
    x = self.flatten(x)
    x = self.dense_1(x)
    x = self.dropout(x)
    x = self.dense_2(x)
    return self.eager(x)

# Create an instance of this model
model = SequentialModel()

# Generate some nonsense pictures and labels
input_data = tf.random.uniform([60, 28, 28])
labels = tf.random.uniform([60])

loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
 

أولاً ، قم بتجميع النموذج دون حريصة. لاحظ أنه لا يتم تتبع النموذج ؛ على الرغم من اسمها ، فإن compile فقط بإعداد وظائف الخسارة ، والتحسين ، ومعلمات التدريب الأخرى.

 model.compile(run_eagerly=False, loss=loss_fn)
 

الآن ، قم باستدعاء fit وشاهد أن الوظيفة يتم تتبعها (مرتين) ثم لا يعمل التأثير المتلهف مرة أخرى أبدًا.

 model.fit(input_data, labels, epochs=3)
 
Epoch 1/3

Currently running eagerly 2020-08-04 01:22:21.848492

Currently running eagerly 2020-08-04 01:22:21.955102
2/2 [==============================] - 0s 1ms/step - loss: 1.4056
Epoch 2/3
2/2 [==============================] - 0s 1ms/step - loss: 0.0037
Epoch 3/3
2/2 [==============================] - 0s 1ms/step - loss: 0.0019

<tensorflow.python.keras.callbacks.History at 0x7f45f03c5630>

ومع ذلك ، إذا قمت بتشغيل حقبة واحدة في شغف ، يمكنك رؤية التأثير الجانبي الحريص مرتين.

 print("Running eagerly")
# When compiling the model, set it to run eagerly
model.compile(run_eagerly=True, loss=loss_fn)

model.fit(input_data, labels, epochs=1)

 
Running eagerly

Currently running eagerly 2020-08-04 01:22:22.152979
1/2 [==============>...............] - ETA: 0s - loss: 8.7806e-04
Currently running eagerly 2020-08-04 01:22:22.173295
2/2 [==============================] - 0s 5ms/step - loss: 4.6877e-04

<tensorflow.python.keras.callbacks.History at 0x7f45f0312940>

باستخدام run_functions_eagerly

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

 # Now, globally set everything to run eagerly
tf.config.run_functions_eagerly(True)
print("Run all functions eagerly.")

# First, trace the model, triggering the side effect
polymorphic_function = tf.function(model)

# It was traced...
print(polymorphic_function.get_concrete_function(input_data))

# But when you run the function again, the side effect happens (both times).
result = polymorphic_function(input_data)
result = polymorphic_function(input_data)
 
Run all functions eagerly.

Currently running eagerly 2020-08-04 01:22:22.202726
ConcreteFunction function(self)
  Args:
    self: float32 Tensor, shape=(60, 28, 28)
  Returns:
    float32 Tensor, shape=(60, 10)

Currently running eagerly 2020-08-04 01:22:22.206521

Currently running eagerly 2020-08-04 01:22:22.207818

 # Don't forget to set it back when you are done
tf.config.experimental_run_functions_eagerly(False)

 
WARNING:tensorflow:From <ipython-input-17-782fe9ce7b18>:2: experimental_run_functions_eagerly (from tensorflow.python.eager.def_function) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.config.run_functions_eagerly` instead of the experimental version.

التتبع والأداء

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

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

يمكنك إضافة تأثير جانبي حريص فقط (مثل طباعة وسيطة Python) حتى تتمكن من معرفة متى يتم تتبع الوظيفة. هنا ، يمكنك رؤية تتبع إضافي لأن حجج Python الجديدة دائمًا ما تؤدي إلى التراجع.

 # Use @tf.function decorator
@tf.function
def a_function_with_python_side_effect(x):
  print("Tracing!")  # This eager
  return x * x + tf.constant(2)

# This is traced the first time
print(a_function_with_python_side_effect(tf.constant(2)))
# The second time through, you won't see the side effect
print(a_function_with_python_side_effect(tf.constant(3)))

# This retraces each time the Python argument chances
# as a Python argument could be an epoch count or other
# hyperparameter
print(a_function_with_python_side_effect(2))
print(a_function_with_python_side_effect(3))

 
Tracing!
tf.Tensor(6, shape=(), dtype=int32)
tf.Tensor(11, shape=(), dtype=int32)
Tracing!
tf.Tensor(6, shape=(), dtype=int32)
Tracing!
tf.Tensor(11, shape=(), dtype=int32)

الخطوات التالية

يمكنك قراءة مناقشة أكثر تعمقًا في كل من الصفحة المرجعية لواجهة برمجة التطبيقات tf.function وفي الدليل .