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

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

ملخص

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

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

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

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

في الأدلة الثلاثة السابقة ، قمت بتشغيل TensorFlow بلهفة . هذا يعني أن عمليات TensorFlow يتم تنفيذها بواسطة Python ، والتشغيل عن طريق العملية ، وإرجاع النتائج إلى Python.

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

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

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

رسم بياني بسيط لـ TensorFlow

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

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

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

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

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

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

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

يثبت

import tensorflow as tf
import timeit
from datetime import datetime

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

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

# Define a Python function.
def a_regular_function(x, y, b):
  x = tf.matmul(x, y)
  x = x + b
  return x

# `a_function_that_uses_a_graph` is a TensorFlow `Function`.
a_function_that_uses_a_graph = tf.function(a_regular_function)

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

orig_value = a_regular_function(x1, y1, b1).numpy()
# Call a `Function` like a Python function.
tf_function_value = a_function_that_uses_a_graph(x1, y1, b1).numpy()
assert(orig_value == tf_function_value)

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

tf.function تنطبق على وظيفة وجميع الوظائف الأخرى التي تستدعيها:

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

# Use the decorator to make `outer_function` a `Function`.
@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.Session .

تحويل دوال بايثون إلى رسوم بيانية

ستحتوي أي وظيفة تكتبها باستخدام TensorFlow على مزيج من عمليات TF المضمنة ومنطق Python ، مثل جمل if-then ، والحلقات ، break ، return ، continue ، والمزيد. بينما يتم التقاط عمليات TensorFlow بسهولة بواسطة tf.Graph ، فإن المنطق الخاص ببايثون يحتاج إلى الخضوع لخطوة إضافية ليصبح جزءًا من الرسم البياني. تستخدم tf.function مكتبة تسمى AutoGraph ( tf.autograph ) لتحويل كود Python إلى كود لإنشاء الرسم البياني.

def simple_relu(x):
  if tf.greater(x, 0):
    return x
  else:
    return 0

# `tf_simple_relu` is a TensorFlow `Function` that wraps `simple_relu`.
tf_simple_relu = tf.function(simple_relu)

print("First branch, with graph:", tf_simple_relu(tf.constant(1)).numpy())
print("Second branch, with graph:", tf_simple_relu(tf.constant(-1)).numpy())
First branch, with graph: 1
Second branch, with graph: 0

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

# This is the graph-generating output of AutoGraph.
print(tf.autograph.to_code(simple_relu))
def tf__simple_relu(x):
    with ag__.FunctionScope('simple_relu', '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 (do_return, retval_)

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

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

        def else_body():
            nonlocal retval_, do_return
            try:
                do_return = True
                retval_ = 0
            except:
                do_return = False
                raise
        ag__.if_stmt(ag__.converted_call(ag__.ld(tf).greater, (ag__.ld(x), 0), None, fscope), if_body, else_body, get_state, set_state, ('do_return', 'retval_'), 2)
        return fscope.ret(retval_, do_return)
# This is the graph itself.
print(tf_simple_relu.get_concrete_function(tf.constant(1)).graph.as_graph_def())
node {
  name: "x"
  op: "Placeholder"
  attr {
    key: "_user_specified_name"
    value {
      s: "x"
    }
  }
  attr {
    key: "dtype"
    value {
      type: DT_INT32
    }
  }
  attr {
    key: "shape"
    value {
      shape {
      }
    }
  }
}
node {
  name: "Greater/y"
  op: "Const"
  attr {
    key: "dtype"
    value {
      type: DT_INT32
    }
  }
  attr {
    key: "value"
    value {
      tensor {
        dtype: DT_INT32
        tensor_shape {
        }
        int_val: 0
      }
    }
  }
}
node {
  name: "Greater"
  op: "Greater"
  input: "x"
  input: "Greater/y"
  attr {
    key: "T"
    value {
      type: DT_INT32
    }
  }
}
node {
  name: "cond"
  op: "StatelessIf"
  input: "Greater"
  input: "x"
  attr {
    key: "Tcond"
    value {
      type: DT_BOOL
    }
  }
  attr {
    key: "Tin"
    value {
      list {
        type: DT_INT32
      }
    }
  }
  attr {
    key: "Tout"
    value {
      list {
        type: DT_BOOL
        type: DT_INT32
      }
    }
  }
  attr {
    key: "_lower_using_switch_merge"
    value {
      b: true
    }
  }
  attr {
    key: "_read_only_resource_inputs"
    value {
      list {
      }
    }
  }
  attr {
    key: "else_branch"
    value {
      func {
        name: "cond_false_34"
      }
    }
  }
  attr {
    key: "output_shapes"
    value {
      list {
        shape {
        }
        shape {
        }
      }
    }
  }
  attr {
    key: "then_branch"
    value {
      func {
        name: "cond_true_33"
      }
    }
  }
}
node {
  name: "cond/Identity"
  op: "Identity"
  input: "cond"
  attr {
    key: "T"
    value {
      type: DT_BOOL
    }
  }
}
node {
  name: "cond/Identity_1"
  op: "Identity"
  input: "cond:1"
  attr {
    key: "T"
    value {
      type: DT_INT32
    }
  }
}
node {
  name: "Identity"
  op: "Identity"
  input: "cond/Identity_1"
  attr {
    key: "T"
    value {
      type: DT_INT32
    }
  }
}
library {
  function {
    signature {
      name: "cond_false_34"
      input_arg {
        name: "cond_placeholder"
        type: DT_INT32
      }
      output_arg {
        name: "cond_identity"
        type: DT_BOOL
      }
      output_arg {
        name: "cond_identity_1"
        type: DT_INT32
      }
    }
    node_def {
      name: "cond/Const"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
    }
    node_def {
      name: "cond/Const_1"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
    }
    node_def {
      name: "cond/Const_2"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_INT32
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_INT32
            tensor_shape {
            }
            int_val: 0
          }
        }
      }
    }
    node_def {
      name: "cond/Const_3"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
    }
    node_def {
      name: "cond/Identity"
      op: "Identity"
      input: "cond/Const_3:output:0"
      attr {
        key: "T"
        value {
          type: DT_BOOL
        }
      }
    }
    node_def {
      name: "cond/Const_4"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_INT32
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_INT32
            tensor_shape {
            }
            int_val: 0
          }
        }
      }
    }
    node_def {
      name: "cond/Identity_1"
      op: "Identity"
      input: "cond/Const_4:output:0"
      attr {
        key: "T"
        value {
          type: DT_INT32
        }
      }
    }
    ret {
      key: "cond_identity"
      value: "cond/Identity:output:0"
    }
    ret {
      key: "cond_identity_1"
      value: "cond/Identity_1:output:0"
    }
    attr {
      key: "_construction_context"
      value {
        s: "kEagerRuntime"
      }
    }
    arg_attr {
      key: 0
      value {
        attr {
          key: "_output_shapes"
          value {
            list {
              shape {
              }
            }
          }
        }
      }
    }
  }
  function {
    signature {
      name: "cond_true_33"
      input_arg {
        name: "cond_identity_1_x"
        type: DT_INT32
      }
      output_arg {
        name: "cond_identity"
        type: DT_BOOL
      }
      output_arg {
        name: "cond_identity_1"
        type: DT_INT32
      }
    }
    node_def {
      name: "cond/Const"
      op: "Const"
      attr {
        key: "dtype"
        value {
          type: DT_BOOL
        }
      }
      attr {
        key: "value"
        value {
          tensor {
            dtype: DT_BOOL
            tensor_shape {
            }
            bool_val: true
          }
        }
      }
    }
    node_def {
      name: "cond/Identity"
      op: "Identity"
      input: "cond/Const:output:0"
      attr {
        key: "T"
        value {
          type: DT_BOOL
        }
      }
    }
    node_def {
      name: "cond/Identity_1"
      op: "Identity"
      input: "cond_identity_1_x"
      attr {
        key: "T"
        value {
          type: DT_INT32
        }
      }
    }
    ret {
      key: "cond_identity"
      value: "cond/Identity:output:0"
    }
    ret {
      key: "cond_identity_1"
      value: "cond/Identity_1:output:0"
    }
    attr {
      key: "_construction_context"
      value {
        s: "kEagerRuntime"
      }
    }
    arg_attr {
      key: 0
      value {
        attr {
          key: "_output_shapes"
          value {
            list {
              shape {
              }
            }
          }
        }
      }
    }
  }
}
versions {
  producer: 898
  min_consumer: 12
}

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

تعدد الأشكال: Function واحدة ، العديد من الرسوم البيانية

tf.Graph لنوع معين من المدخلات (على سبيل المثال ، dtype محدد أو كائنات بنفس id() ).

في كل مرة تقوم فيها باستدعاء Function مع أنواع وأشكال جديدة في dtypes ، تقوم Function بإنشاء tf.Graph جديد للوسيطات الجديدة. تُعرف أنواع وأشكال tf.Graph dtypes توقيع الإدخال أو مجرد توقيع .

تخزن Function tf.Graph المناظر لذلك التوقيع في وظيفة ConcreteFunction . الوظيفة ConcreteFunction عبارة عن غلاف حول رسم tf.Graph .

@tf.function
def my_relu(x):
  return tf.maximum(0., x)

# `my_relu` creates new graphs as it observes more signatures.
print(my_relu(tf.constant(5.5)))
print(my_relu([1, -1]))
print(my_relu(tf.constant([3., -3.])))
tf.Tensor(5.5, shape=(), dtype=float32)
tf.Tensor([1. 0.], shape=(2,), dtype=float32)
tf.Tensor([3. 0.], shape=(2,), dtype=float32)

إذا تم استدعاء Function بالفعل بهذا التوقيع ، فإن Function لا تنشئ رسمًا tf.Graph جديدًا.

# These two calls do *not* create new graphs.
print(my_relu(tf.constant(-2.5))) # Signature matches `tf.constant(5.5)`.
print(my_relu(tf.constant([-1., 1.]))) # Signature matches `tf.constant([3., -3.])`.
tf.Tensor(0.0, shape=(), dtype=float32)
tf.Tensor([0. 1.], shape=(2,), dtype=float32)

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

# There are three `ConcreteFunction`s (one for each graph) in `my_relu`.
# The `ConcreteFunction` also knows the return type and shape!
print(my_relu.pretty_printed_concrete_signatures())
my_relu(x)
  Args:
    x: float32 Tensor, shape=()
  Returns:
    float32 Tensor, shape=()

my_relu(x=[1, -1])
  Returns:
    float32 Tensor, shape=(2,)

my_relu(x)
  Args:
    x: float32 Tensor, shape=(2,)
  Returns:
    float32 Tensor, shape=(2,)

باستخدام tf.function

لقد تعلمت حتى الآن كيفية تحويل دالة Python إلى رسم بياني ببساطة باستخدام tf.function كديكور أو غلاف. ولكن من الناحية العملية ، قد يكون tf.function تعمل بشكل صحيح أمرًا صعبًا! في الأقسام التالية ، ستتعلم كيف يمكنك جعل الكود الخاص بك يعمل كما هو متوقع مع tf.function .

تنفيذ الرسم البياني مقابل التنفيذ الشغوف

يمكن تنفيذ الكود في Function بشغف وكرسوم بيانية. بشكل افتراضي ، تنفذ Function كودها كرسم بياني:

@tf.function
def get_MSE(y_true, y_pred):
  sq_diff = tf.pow(y_true - y_pred, 2)
  return tf.reduce_mean(sq_diff)
y_true = tf.random.uniform([5], maxval=10, dtype=tf.int32)
y_pred = tf.random.uniform([5], maxval=10, dtype=tf.int32)
print(y_true)
print(y_pred)
tf.Tensor([1 0 4 4 7], shape=(5,), dtype=int32)
tf.Tensor([3 6 3 0 6], shape=(5,), dtype=int32)
get_MSE(y_true, y_pred)
<tf.Tensor: shape=(), dtype=int32, numpy=11>

للتحقق من أن الرسم البياني Function يقوم بنفس العمليات الحسابية لوظيفة Python المكافئة ، يمكنك جعله ينفذ بفارغ الصبر باستخدام tf.config.run_functions_eagerly(True) . هذا مفتاح يعمل على إيقاف تشغيل قدرة Function على إنشاء الرسوم البيانية وتشغيلها ، بدلاً من تنفيذ التعليمات البرمجية بشكل طبيعي.

tf.config.run_functions_eagerly(True)
get_MSE(y_true, y_pred)
<tf.Tensor: shape=(), dtype=int32, numpy=11>
# Don't forget to set it back when you are done.
tf.config.run_functions_eagerly(False)

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

@tf.function
def get_MSE(y_true, y_pred):
  print("Calculating MSE!")
  sq_diff = tf.pow(y_true - y_pred, 2)
  return tf.reduce_mean(sq_diff)

لاحظ ما هو مطبوع:

error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
Calculating MSE!

هل الإخراج مفاجئ؟ get_MSE تتم طباعته مرة واحدة فقط بالرغم من أنه تم استدعاؤه ثلاث مرات.

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

للتحقق من سلامة البيانات ، دعنا نوقف تنفيذ الرسم البياني للمقارنة:

# Now, globally set everything to run eagerly to force eager execution.
tf.config.run_functions_eagerly(True)
# Observe what is printed below.
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
error = get_MSE(y_true, y_pred)
Calculating MSE!
Calculating MSE!
Calculating MSE!
tf.config.run_functions_eagerly(False)

يعد print أحد الآثار الجانبية لـ Python ، وهناك اختلافات أخرى يجب أن تكون على دراية بها عند تحويل دالة إلى Function . تعرف على المزيد في قسم القيود من دليل الأداء الأفضل باستخدام tf.function .

التنفيذ غير الصارم

ينفذ تنفيذ الرسم البياني فقط العمليات الضرورية لإنتاج التأثيرات الملحوظة ، والتي تشمل:

  • القيمة المرجعة للدالة
  • الآثار الجانبية الموثقة والمعروفة مثل:
    • عمليات الإدخال / الإخراج ، مثل tf.print
    • عمليات التصحيح ، مثل وظائف التأكيد في tf.debugging
    • طفرات tf.Variable

يُعرف هذا السلوك عادةً باسم "التنفيذ غير الصارم" ، ويختلف عن التنفيذ الحثيث ، والذي يمر عبر جميع عمليات البرنامج ، سواء أكان مطلوبًا أم لا.

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

في المثال التالي ، تم تخطي العملية "غير الضرورية" tf.gather أثناء تنفيذ الرسم البياني ، لذلك لا يظهر خطأ وقت التشغيل InvalidArgumentError لأنه سيكون في حالة تنفيذ حثيثة. لا تعتمد على ظهور خطأ أثناء تنفيذ الرسم البياني.

def unused_return_eager(x):
  # Get index 1 will fail when `len(x) == 1`
  tf.gather(x, [1]) # unused 
  return x

try:
  print(unused_return_eager(tf.constant([0.0])))
except tf.errors.InvalidArgumentError as e:
  # All operations are run during eager execution so an error is raised.
  print(f'{type(e).__name__}: {e}')
tf.Tensor([0.], shape=(1,), dtype=float32)
@tf.function
def unused_return_graph(x):
  tf.gather(x, [1]) # unused
  return x

# Only needed operations are run during graph exection. The error is not raised.
print(unused_return_graph(tf.constant([0.0])))
tf.Tensor([0.], shape=(1,), dtype=float32)

أفضل ممارسات tf.function

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

قد يكون التصميم tf.function هو أفضل رهان لك لكتابة برامج TensorFlow المتوافقة مع الرسم البياني. هنا بعض النصائح:

  • قم بالتبديل بين التنفيذ الشغوف والرسم البياني مبكرًا وغالبًا باستخدام tf.config.run_functions_eagerly لتحديد ما إذا / عندما يتباعد الوضعان.
  • قم tf.Variable خارج دالة Python وقم بتعديلها من الداخل. الشيء نفسه ينطبق على الكائنات التي تستخدم tf.Variable ، مثل keras.layers و keras.Model s و tf.optimizers .
  • تجنب كتابة الدوال التي تعتمد على متغيرات بايثون الخارجية ، باستثناء tf.Variable s و Keras.
  • يفضل كتابة الدوال التي تأخذ الموترات وأنواع TensorFlow الأخرى كمدخلات. يمكنك تمرير أنواع أخرى من الكائنات ولكن كن حذرًا !
  • قم بتضمين أكبر قدر ممكن من العمليات الحسابية ضمن tf.function لتعظيم مكاسب الأداء. على سبيل المثال ، قم بتزيين خطوة تدريب كاملة أو حلقة التدريب بأكملها.

رؤية التسريع

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

x = tf.random.uniform(shape=[10, 10], minval=-1, maxval=2, dtype=tf.dtypes.int32)

def power(x, y):
  result = tf.eye(10, dtype=tf.dtypes.int32)
  for _ in range(y):
    result = tf.matmul(x, result)
  return result
print("Eager execution:", timeit.timeit(lambda: power(x, 100), number=1000))
Eager execution: 2.5637862179974036
power_as_graph = tf.function(power)
print("Graph execution:", timeit.timeit(lambda: power_as_graph(x, 100), number=1000))
Graph execution: 0.6832536700021592

تُستخدم tf.function بشكل شائع لتسريع حلقات التدريب ، ويمكنك معرفة المزيد عنها في كتابة حلقة تدريب من البداية باستخدام Keras.

الأداء والمفاضلات

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

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

ما هي Function تتبع؟

لمعرفة متى يتم تتبع Function ، أضف عبارة print إلى كودها. كقاعدة عامة ، ستقوم Function بتنفيذ جملة print في كل مرة تتعقب فيها.

@tf.function
def a_function_with_python_side_effect(x):
  print("Tracing!") # An eager-only side effect.
  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)))
Tracing!
tf.Tensor(6, shape=(), dtype=int32)
tf.Tensor(11, shape=(), dtype=int32)
# This retraces each time the Python argument changes,
# 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)
Tracing!
tf.Tensor(11, shape=(), dtype=int32)

تؤدي دائمًا وسيطات Python الجديدة إلى إنشاء رسم بياني جديد ، ومن ثم التتبع الإضافي.

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

يمكنك معرفة المزيد حول tf.function على صفحة مرجع API ومن خلال اتباع دليل أداء أفضل مع tf.function .