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

مقدمة في الرسوم البيانية ووظيفة 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 الرسوم البيانية كتنسيق لل نماذج المحفوظة عندما تصدر لهم من بيثون.

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

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

هناك نظام التحسين بأكمله، الطارح ، لأداء هذا وspeedups أخرى.

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

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

اقامة

import tensorflow as tf
import timeit
from datetime import datetime

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

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

# 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 الصورة وراء API واحد . هذه هي الطريقة Function قادرة على تعطيك فوائد تنفيذ الرسم البياني ، مثل السرعة وdeployability.

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 والمنطق بيثون، مثل if-then فقرات، حلقات، break ، return ، continue ، وأكثر من ذلك. في حين أن عمليات القبض TensorFlow بسهولة من قبل tf.Graph والاحتياجات منطق بيثون محددة للخضوع خطوة إضافية من أجل أن تصبح جزءا من الرسم البياني. tf.function يستخدم مكتبة يسمى توقيعه ( tf.autograph ) لتحويل كود بايثون إلى رمز لتوليد الرسم البياني.

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
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const"
      }
    }
    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
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const_1"
      }
    }
    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
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const_2"
      }
    }
    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
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const_3"
      }
    }
    node_def {
      name: "cond/Identity"
      op: "Identity"
      input: "cond/Const_3:output:0"
      attr {
        key: "T"
        value {
          type: DT_BOOL
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Identity"
      }
    }
    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
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const_4"
      }
    }
    node_def {
      name: "cond/Identity_1"
      op: "Identity"
      input: "cond/Const_4:output:0"
      attr {
        key: "T"
        value {
          type: DT_INT32
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Identity_1"
      }
    }
    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
          }
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Const"
      }
    }
    node_def {
      name: "cond/Identity"
      op: "Identity"
      input: "cond/Const:output:0"
      attr {
        key: "T"
        value {
          type: DT_BOOL
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Identity"
      }
    }
    node_def {
      name: "cond/Identity_1"
      op: "Identity"
      input: "cond_identity_1_x"
      attr {
        key: "T"
        value {
          type: DT_INT32
        }
      }
      experimental_debug_info {
        original_node_names: "cond/Identity_1"
      }
    }
    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: 808
  min_consumer: 12
}

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

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

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

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

و Function يخزن tf.Graph في المقابل الى ان التوقيع في ConcreteFunction . A 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

وحتى الآن، كنت قد تعلمت كيفية تحويل وظيفة بيثون إلى رسم بياني ببساطة عن طريق استخدام 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([6 4 3 1 5], shape=(5,), dtype=int32)
tf.Tensor([6 7 9 9 1], shape=(5,), dtype=int32)
get_MSE(y_true, y_pred)
<tf.Tensor: shape=(), dtype=int32, numpy=25>

للتحقق من أن لديك Function الصورة الرسم البياني يفعل نفس الحساب وظيفة بيثون ما يعادلها، يمكنك ان تجعل من تنفيذ بفارغ الصبر مع 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=25>
# Don't forget to set it back when you are done.
tf.config.run_functions_eagerly(False)

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

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

# 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 هو أحد الآثار الجانبية بيثون، وهناك اختلافات أخرى التي يجب أن تكون على علم عند تحويل وظيفة إلى 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 الصورة خارج الدالة بيثون وتعديلها في الداخل. الشيء نفسه ينطبق على الكائنات التي تستخدم tf.Variable ، مثل keras.layers ، keras.Model الصورة و tf.optimizers .
  • تجنب كتابة الوظائف التي تعتمد على المتغيرات الخارجية بيثون ، باستثناء tf.Variable الصورة والكائنات 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.286959676999686
power_as_graph = tf.function(power)
print("Graph execution:", timeit.timeit(lambda: power_as_graph(x, 100), number=1000))
Graph execution: 0.6817171659999985

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 دليل.