مشاهده در TensorFlow.org | در Google Colab اجرا شود | مشاهده منبع در GitHub | دانلود دفترچه یادداشت |
بررسی اجمالی
این راهنما به زیر سطح TensorFlow و Keras می رود تا نحوه عملکرد TensorFlow را نشان دهد. اگر میخواهید فوراً با Keras شروع کنید، مجموعه راهنمای Keras را بررسی کنید.
در این راهنما، یاد خواهید گرفت که چگونه TensorFlow به شما اجازه می دهد تا تغییرات ساده ای در کد خود ایجاد کنید تا نمودارها را دریافت کنید، نمودارها چگونه ذخیره و نمایش داده می شوند و چگونه می توانید از آنها برای سرعت بخشیدن به مدل های خود استفاده کنید.
این یک نمای کلی با تصویر بزرگ است که نحوه تغییر عملکرد tf.function
را به شما امکان می دهد از اجرای مشتاقانه به اجرای گراف تغییر دهید. برای مشخصات tf.function
به راهنمای tf.function
.
نمودارها چیست؟
در سه راهنما قبلی، TensorFlow را مشتاقانه اجرا کردید. این بدان معناست که عملیات TensorFlow توسط پایتون اجرا می شود، عملیات به عملیات، و نتایج را به پایتون برمی گرداند.
در حالی که اجرای مشتاق چندین مزیت منحصر به فرد دارد، اجرای گراف قابلیت حمل خارج از پایتون را ممکن می کند و تمایل به ارائه عملکرد بهتر دارد. اجرای نمودار به این معنی است که محاسبات تانسور به عنوان یک گراف TensorFlow اجرا می شود که گاهی اوقات به عنوان tf.Graph
یا به سادگی یک "گراف" نامیده می شود.
نمودارها ساختارهای داده ای هستند که شامل مجموعه ای از اشیاء tf.Operation
هستند که واحدهای محاسباتی را نشان می دهند. و اشیاء tf.Tensor
که واحدهای داده ای را نشان می دهند که بین عملیات جریان دارند. آنها در یک زمینه tf.Graph
تعریف شده اند. از آنجایی که این نمودارها ساختارهای داده ای هستند، می توان آنها را بدون کد اصلی پایتون ذخیره، اجرا و بازیابی کرد.
این همان چیزی است که یک نمودار TensorFlow که یک شبکه عصبی دو لایه را نشان می دهد، هنگامی که در TensorBoard تجسم می شود، به نظر می رسد.
مزایای نمودارها
با یک نمودار، انعطاف پذیری زیادی دارید. میتوانید از نمودار TensorFlow خود در محیطهایی که مفسر پایتون ندارند، مانند برنامههای کاربردی تلفن همراه، دستگاههای جاسازی شده و سرورهای پشتیبان استفاده کنید. TensorFlow از نمودارها به عنوان قالبی برای مدل های ذخیره شده استفاده می کند که آنها را از پایتون صادر می کند.
نمودارها نیز به راحتی بهینه می شوند و به کامپایلر اجازه می دهد تا تبدیل هایی مانند:
- مقدار تانسورها را با تا کردن گره های ثابت در محاسبات خود به صورت ایستا استنتاج کنید ("تاشوی ثابت") .
- بخش های فرعی یک محاسبات را که مستقل هستند از هم جدا کنید و آنها را بین رشته ها یا دستگاه ها تقسیم کنید.
- با حذف عبارات فرعی رایج، عملیات حسابی را ساده کنید.
یک سیستم بهینهسازی کامل، Grappler ، برای انجام این افزایش سرعت و سایر افزایشها وجود دارد.
به طور خلاصه، نمودارها بسیار مفید هستند و به TensorFlow شما اجازه می دهند سریع اجرا شود، به صورت موازی اجرا شود و به طور موثر در چندین دستگاه اجرا شود.
با این حال، هنوز هم میخواهید مدلهای یادگیری ماشین (یا سایر محاسبات) خود را برای راحتی در پایتون تعریف کنید و سپس بهطور خودکار در صورت نیاز نمودارها را بسازید.
برپایی
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
می تواند مزایای اجرای نمودار مانند سرعت و قابلیت استقرار را به شما بدهد.
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
از کتابخانه ای به نام AutoGraph ( 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 } } } } 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.function می تواند در اینجا کمک کند، و همچنین مرجع کامل AutoGraph
چند شکلی: یک Function
، نمودارهای متعدد
یک tf.Graph
به نوع خاصی از ورودی ها اختصاص دارد (مثلاً تانسورها با نوع dtype
خاص یا اشیاء با همان id()
).
هر بار که Function
را با dtypes
و اشکال جدید در آرگومان های آن فراخوانی می کنید، Function
یک tf.Graph
جدید برای آرگومان های جدید ایجاد می کند. dtypes
و اشکال ورودی های tf.Graph
به عنوان یک امضای ورودی یا فقط یک امضا شناخته می شود.
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
تا کنون، شما یاد گرفته اید که چگونه یک تابع پایتون را به سادگی با استفاده از 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
شما همان محاسبه تابع پایتون را انجام می دهد، می توانید آن را با 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
پایتون نمونه ای از تفاوت این دو حالت است. بیایید بررسی کنیم وقتی یک دستور 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
کد اصلی را اجرا می کند تا نمودار را در فرآیندی به نام "ردیابی" ایجاد کند. Tracing عملیات 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.function بیشتر بیاموزید .
اجرای غیر دقیق
اجرای گراف فقط عملیات لازم برای ایجاد اثرات قابل مشاهده را اجرا می کند که شامل:
- مقدار بازگشتی تابع
- عوارض جانبی شناخته شده مستند مانند:
- عملیات ورودی/خروجی، مانند
tf.print
- عملیات اشکال زدایی، مانند توابع assert در
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
s را خارج از تابع پایتون ایجاد کنید و آنها را در داخل تغییر دهید. همین امر در مورد اشیایی که ازtf.Variable
استفاده می کنند، مانندkeras.layers
،keras.Model
و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)
آرگومانهای جدید پایتون همیشه باعث ایجاد یک گراف جدید میشوند، بنابراین ردیابی اضافی.
مراحل بعدی
میتوانید در صفحه مرجع API و با دنبال کردن راهنمای عملکرد بهتر با tf.function
، درباره tf.function
اطلاعات بیشتری کسب کنید.