![]() | ![]() | ![]() | ![]() |
ב- TensorFlow 2, ביצוע להוט מופעל כברירת מחדל. ממשק המשתמש הוא אינטואיטיבי וגמיש (הפעלת פעולות חד פעמיות קלה ומהירה בהרבה), אך הדבר יכול לבוא על חשבון ביצועים ופריסה.
אתה יכול להשתמש ב- tf.function
כדי ליצור גרפים מהתוכניות שלך. זהו כלי טרנספורמציה שיוצר גרפי זרימת נתונים שאינם תלויים בפייתון מתוך קוד הפייתון שלך. זה יעזור לך ליצור מודלים ביצועים וניידים, וזה נדרש להשתמש ב- SavedModel
.
מדריך זה יעזור לך tf.function
כיצד tf.function
עובדת מתחת למכסה המנוע, כך שתוכל להשתמש בה בצורה יעילה.
ההמלצות העיקריות וההמלצות הן:
- ניפוי שגיאות במצב להוט, ואז קישוט עם
@tf.function
. - אל תסמוך על תופעות לוואי של פייתון כמו מוטציה של אובייקטים או רשימות.
-
tf.function
עובד הכי טוב עם TensorFlow ops; שיחות 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
שאתה מגדיר היא בדיוק כמו פעולת ליבה של TensorFlow: אתה יכול לבצע אותה בשקיקה; אתה יכול לחשב שיפועים; וכן הלאה.
@tf.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
s יכולות להיות מהירות יותר מקוד להוט, במיוחד עבור גרפים עם אופים קטנים רבים. אך עבור גרפים עם כמה אופציות יקרות (כמו התפתלות), ייתכן שלא תראו מהירות רבה.
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.003903477999756433 Function conv: 0.004678294000314054 Note how there's not much difference in performance for convolutions
מַעֲקָב
הקלדה דינמית של פייתון פירושה שתוכל להתקשר לפונקציות עם מגוון סוגי ארגומנטים, ופייתון יכול לעשות משהו אחר בכל תרחיש.
עם זאת, כדי ליצור גרף TensorFlow, dtypes
סטטיים dtypes
צורה. tf.function
מגשרת על פער זה על ידי גלישת פונקציית פייתון ליצירת אובייקט Function
. בהתבסס על הקלטים הנתונים, Function
בוחרת את הגרף המתאים עבור הקלטים הנתונים, ומשחזרת את פונקציית הפייתון לפי הצורך. ברגע שאתה מבין מדוע ומתי מתרחש מעקב, הרבה יותר קל להשתמש ב- tf.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: string Tensor, shape=() Returns: string Tensor, shape=() double(a) Args: a: int32 Tensor, shape=() Returns: int32 Tensor, shape=() double(a) Args: a: float32 Tensor, shape=() Returns: float32 Tensor, shape=()
עד כה ראית ש- tf.function
יוצר שכבת משלוח דינמית במטמון מעל לוגיקת האיתור הגרפי של TensorFlow. ליתר דיוק לגבי המינוח:
-
tf.Graph
הואtf.Graph
גלם, אגנוסטי לשפה, ונייד של החישוב שלך. -
ConcreteFunction
הוא עטיפהtf.Graph
בשקיקה סביבtf.Graph
. -
Function
מנהלת מטמון שלConcreteFunction
s ובוחרת את המתאים עבור הקלטים שלך. -
tf.function
עוטף פונקציית פייתון, ומחזיר אובייקטFunction
.
השגת פונקציות קונקרטיות
בכל פעם שמעקב אחר פונקציה נוצר פונקציה קונקרטית חדשה. באפשרותך להשיג ישירות פונקציית בטון באמצעות 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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises yield File "<ipython-input-1-e4e2860a4364>", 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]
יתכן שתבחין כי לטיעוני Python ניתן טיפול מיוחד בחתימת הקלט של פונקציה קונקרטית. לפני 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.6/site-packages/tensorflow/python/eager/function.py", line 1683, in _call_impl cancellation_manager) File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/eager/function.py", line 1728, 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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises yield File "<ipython-input-1-d163f3d206cb>", 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
. למרות 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
ישנות של פייתוןprint
מתבצעות רק במהלך האיתור, ועוזרות לך להתחקות אחר המעקב אחר הפונקציה שלך. - שיחות
tf.print
יבוצעו בכל פעם ויכולות לעזור לך לאתר ערכי ביניים במהלך הביצוע. -
tf.debugging.enable_check_numerics
היא דרך קלה לאתר היכן נוצרים NaNs ו- Inf. -
pdb
יכול לעזור לך להבין מה קורה במהלך האיתור. (אזהרה: ה- PDB יפיל אותך לקוד מקור שהופך באמצעות AutoGraph.)
התחקות אחר סמנטיקה
כללי מפתח מטמון
Function
קובעת אם לעשות שימוש חוזר בפונקציית בטון שעוקבה על ידי חישוב מפתח מטמון מתוך הטענות והקווארוגים של הקלט.
- המפתח שנוצר עבור ארגומנט
tf.Tensor
הוא צורתו וסוג ה- dt. - החל מ- TensorFlow 2.3, המפתח שנוצר עבור ארגומנט
tf.Variable
הוא ה-id()
שלוid()
. - המפתח שנוצר לפרימיטיבי של פייתון הוא הערך שלו. המפתח שנוצר עבור
dict
מקוננים,list
s,tuple
s,namedtuple
tuple
s ו-attr
s הוא tuple שטוח. (כתוצאה מהשטחה זו, קריאה לפונקציית בטון עם מבנה קינון שונה מזה המשמש במהלך העקיבה תביא ל- TypeError). - עבור כל שאר סוגי הפיתון, המפתחות מבוססים על
id()
האובייקטid()
כך שניתן לעקוב אחר שיטות באופן עצמאי לכל מופע של מחלקה.
שליטה חוזרת
גיוס חוזר מסייע להבטיח ש- TensorFlow יוצר גרפים נכונים עבור כל קבוצת תשומות. עם זאת, מעקב הוא פעולה יקרה! אם Function
שלך משחזרת גרף חדש לכל שיחה, תגלה שהקוד שלך פועל לאט יותר מאשר אם לא tf.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])))
# We 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]]))
# We 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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises yield File "<ipython-input-1-20f544b8adbf>", 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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises yield File "<ipython-input-1-20f544b8adbf>", 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
s לעשות שימוש חוזר במעקב לצורך קלט בגודל משתנה. קלט בגודל משתנה יכול להתרחש אם יש לך רצפים באורך שונה, או תמונות בגדלים שונים עבור כל אצווה (ראה למשל מדריכי רובוטריק ו Deep Dream ).
@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 כדי להפחית את ההחזרה.
לעיתים קרובות, ארגומנטים של פייתון משמשים לשליטה
num_layers=10
ובבניית גרפים - למשל,num_layers=10
אוtraining=True
אוnonlinearity='relu'
. אז אם הטיעון של פייתון ישתנה, הגיוני שתצטרך לעקוב אחר הגרף מחדש.עם זאת, יתכן שלא משתמשים בטיעון של פייתון לבקרת בניית גרפים. במקרים אלה, שינוי בערך הפיתון יכול לגרום למשיכה מיותרת. קחו, למשל, את לולאת האימון הזו, ש- 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
תופעות לוואי של פייתון
תופעות לוואי של פיתון כמו הדפסה, הוספה לרשימות ומוטב גלובליות מתרחשות רק בפעם הראשונה שאתה קורא Function
עם סט כניסות. לאחר מכן, ה- tf.Graph
מבוצע מחדש, מבלי לבצע את קוד הפייתון.
כלל האצבע הוא להשתמש רק בתופעות לוואי של Python לצורך ניפוי העקבות שלך. אחרת, אופציות TensorFlow כמו tf.Variable.assign
, tf.print
ו- tf.summary
הן הדרך הטובה ביותר להבטיח שהמעקב אחר קודך יבוצע על ידי זמן הריצה של 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
.
כדי לתת דוגמה אחת, התקדמות מצב האיטרציה היא תופעת לוואי של פייתון ולכן מתרחשת רק במהלך העקיבה.
external_var = tf.Variable(0)
@tf.function
def buggy_consume_next(iterator):
external_var.assign_add(next(iterator))
tf.print("Value of external_var:", external_var)
iterator = iter([0, 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 of external_var: 0 Value of external_var: 0 Value of external_var: 0
מבני איטרציה מסוימים נתמכים באמצעות AutoGraph. ראה סעיף על התמונות האוטוגרפיות לקבלת סקירה כללית.
אם ברצונך לבצע קוד פייתון במהלך כל הפעלת Function
, tf.py_function
הוא פתח יציאה. החיסרון של tf.py_function
הוא שהוא לא נייד או ביצועי במיוחד, וגם לא עובד טוב tf.py_function
מבוזרות (מרובות GPU, TPU). כמו כן, מכיוון שיש לחבר את tf.py_function
הוא tf.py_function
את כל הקלטים / הפלטים לטנזורים.
ממשקי API כמו tf.gather
, tf.stack
ו- tf.TensorArray
יכולים לעזור לך ליישם דפוסי לולאה נפוצים ב- TensorFlow.
external_list = []
def side_effect(x):
print('Python side effect')
external_list.append(x)
@tf.function
def f(x):
tf.py_function(side_effect, inp=[x], Tout=[])
f(1)
f(1)
f(1)
# The list append happens all three times!
assert len(external_list) == 3
# The list contains tf.constant(1), not 1, because py_function casts everything to tensors.
assert external_list[0].numpy() == 1
Python side effect Python side effect Python side effect
משתנים
אתה עלול להיתקל בשגיאה בעת יצירת tf.Variable
. tf.Variable
חדש בפונקציה. שומרי שגיאה זו נגד הבדלי התנהגות על קריאות חוזרות ונשנות: במצב להוט, פונקצית היוצרת משתנה חדש עם כל שיחה, אבל בתוך Function
, משתנה חדש לא יכול להיוצר עקב שימוש חוזר עקבות.
@tf.function
def f(x):
v = tf.Variable(1.0)
v.assign_add(x)
return v
with assert_raises(ValueError):
f(1.0)
Caught expected exception <class 'ValueError'>: Traceback (most recent call last): File "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises yield File "<ipython-input-1-73e410646579>", line 8, in <module> f(1.0) ValueError: in user code: <ipython-input-1-73e410646579>:3 f * v = tf.Variable(1.0) /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:262 __call__ ** return cls._variable_v2_call(*args, **kwargs) /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:256 _variable_v2_call shape=shape) /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:67 getter return captured_getter(captured_previous, **kwargs) /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/eager/def_function.py:731 invalid_creator_scope "tf.function-decorated function tried to create " ValueError: tf.function-decorated function tried to create variables on non-first call.
ניתן ליצור משתנים בתוך Function
כל עוד משתנים אלה נוצרים רק בפעם הראשונה שהפונקציה מבוצעת.
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)
שגיאה נוספת שתיתקל בה היא משתנה שנאסף אשפה. שלא כמו פונקציות פייתון רגילות, פונקציות קונקרטיות שומרות רק על 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))
del external_var
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 "<ipython-input-1-73d0ca52e838>", line 8, in assert_raises yield File "<ipython-input-1-304a18524b57>", line 14, in <module> traced_f(4) tensorflow.python.framework.errors_impl.FailedPreconditionError: 2 root error(s) found. (0) Failed precondition: Error while reading resource variable _AnonymousVar6 from Container: localhost. This could mean that the variable was uninitialized. Not found: Resource localhost/_AnonymousVar6/N10tensorflow3VarE does not exist. [[node ReadVariableOp (defined at <ipython-input-1-304a18524b57>:4) ]] (1) Failed precondition: Error while reading resource variable _AnonymousVar6 from Container: localhost. This could mean that the variable was uninitialized. Not found: Resource localhost/_AnonymousVar6/N10tensorflow3VarE does not exist. [[node ReadVariableOp (defined at <ipython-input-1-304a18524b57>:4) ]] [[ReadVariableOp/_2]] 0 successful operations. 0 derived errors ignored. [Op:__inference_f_473] Function call stack: f -> f
שינויים באוטוגרף
AutoGraph היא ספרייה tf.function
כברירת מחדל tf.function
, והופכת תת-קבוצה של קוד להוט של פייתון לפונקציות TensorFlow תואמות גרף. זה כולל זרימת בקרה if
, for
while
.
אופציות של TensorFlow כמו tf.cond
ו- tf.cond
. tf.while_loop
ממשיכות לעבוד, אך לעתים קרובות קל יותר לכתוב ולהבין זרימת שליטה tf.while_loop
בפייתון.
# 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.82800746 0.953753114 0.439338565 0.432760358 0.699063778] [0.679404736 0.741477489 0.413096 0.407625586 0.603773236] [0.591132283 0.630037069 0.391098291 0.386454701 0.539729118] [0.530709505 0.558077753 0.372306645 0.368299842 0.492782891] [0.48592326 0.506549835 0.356007636 0.352503687 0.456422389] [0.450974762 0.467252612 0.341692597 0.338594049 0.427163869] [0.422699928 0.435976833 0.328987628 0.326221645 0.402948409] [0.399202496 0.41030407 0.3176108 0.315121651 0.382468879] [0.379266351 0.388730794 0.307344973 0.305089235 0.364849627] [0.362070143 0.370265543 0.298019737 0.295963019 0.349478543] [0.347036153 0.354223937 0.289499342 0.287613869 0.335912973] [0.333744407 0.340116203 0.281673938 0.279937118 0.32382378] [0.321881264 0.327581108 0.27445364 0.272846848 0.312960267] [0.311206967 0.316345602 0.267764032 0.26627183 0.303127766] [0.301534683 0.306198835 0.261543036 0.26015237 0.294172347] [0.292716414 0.296975076 0.255738169 0.254438043 0.285970479] [0.28463307 0.288541943 0.250304967 0.249085933 0.278421789] [0.277187616 0.280792207 0.245205313 0.244059205 0.271443784] [0.27030015 0.273638099 0.240406319 0.239326134 0.26496774] [0.263904095 0.267006844 0.235879496 0.234859139 0.258936107] [0.257943511 0.260837495 0.231599987 0.230634138 0.25330016] [0.252371043 0.255078703 0.227546036 0.226629987 0.248018324] [0.247146174 0.249686733 0.223698452 0.22282806 0.243054956] [0.242234126 0.244624153 0.220040262 0.219211861 0.23837918] [0.237604842 0.239858657 0.216556415 0.215766713 0.23396422] [0.233232126 0.235362232 0.213233441 0.212479532 0.229786649] [0.22909309 0.231110409 0.210059345 0.209338576 0.225825876] [0.225167572 0.227081761 0.207023308 0.206333309 0.22206375] [0.221437797 0.223257363 0.204115555 0.203454211 0.218484148] [0.217888013 0.219620526 0.201327294 0.200692669 0.215072796] [0.214504138 0.216156334 0.198650569 0.198040903 0.211816847] [0.21127364 0.212851539 0.196078092 0.195491791 0.208704829] [0.208185241 0.209694266 0.193603292 0.193038911 0.205726475] <tf.Tensor: shape=(5,), dtype=float32, numpy= array([0.20522884, 0.20667385, 0.19122012, 0.19067629, 0.20287241], 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)
תנאים
AutoGraph ימיר כמה הצהרות if <condition>
לשיחות tf.cond
המקבילות. החלפה זו נעשית אם <condition>
הוא טנסור. אחרת, if
בהצהרה מבוצעת בתור מותנית Python.
תנאי פייתון מבוצע במהלך המעקב, כך שענף אחד של התנאי יתווסף לגרף. ללא AutoGraph, גרף מעקב זה לא יוכל לקחת את הענף החלופי אם יש זרימת בקרה תלויה בנתונים.
tf.cond
עוקב ומוסיף את שני ענפי התנאי לגרף, תוך בחירה דינמית של ענף בזמן הביצוע. למעקב יכולות להיות תופעות לוואי לא מכוונות; ראה אפקטים למעקב אחר AutoGraph לקבלת מידע נוסף.
@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
עיין בתיעוד ההפניה להגבלות נוספות על הצהרות המרות AutoGraph אם.
לולאות
חתימה תמיר חלק for
ו while
הצהרות לתוך TensorFlow המקבילה looping ops, כמו tf.while_loop
. אם לא מומרים, הלולאה for
או while
מבוצעת כלולאת פייתון.
החלפה זו נעשית במצבים הבאים:
-
for x in y
: אםy
הוא טנסור, המירו ל-tf.while_loop
.tf.while_loop
. במקרה המיוחד שבוy
הואtf.data.Dataset
, נוצר שילוב שלtf.data.Dataset
ops. -
while <condition>
: אם<condition>
הוא טנסור, המירו ל-tf.while_loop
.tf.while_loop
.
לולאת פייתון tf.Graph
במהלך המעקב, ומוסיפה אופס נוספים ל- tf.Graph
עבור כל איטרציה של הלולאה.
לולאת TensorFlow עוקבת אחר גוף הלולאה, ובוחרת באופן דינמי כמה איטרציות לרוץ בזמן הביצוע. גוף הלולאה מופיע רק פעם אחת ב- tf.Graph
שנוצר.
עיין בתיעוד ההפניה למגבלות נוספות בהמרת AutoGraph for
הצהרות while
.
מדלג על נתוני פייתון
מלכודת נפוצה היא לולאה על נתונים של פייתון / Numpy בתוך tf.function
. לולאה זו תבוצע במהלך תהליך האיתור, tf.Graph
עותק של המודל שלך ל- 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 10 nodes in its graph train(<FlatMapDataset shapes: (<unknown>, <unknown>), types: (tf.int32, tf.int32)>) contains 10 nodes in its graph
כאשר עוטפים נתונים של Python / Numpy במערך נתונים, יש לשים לב ל tf.data.Dataset.from_generator
לעומת tf.data.Dataset.from_tensors
. הראשון ישמור את הנתונים בפייתון tf.py_function
אותם באמצעות tf.py_function
שיכולה להיות השלכות ביצועים, ואילו האחרון tf.constant()
עותק של הנתונים tf.constant()
אחד גדול בגרף, שיכול להיות בעל השלכות זיכרון.
קריאת נתונים מקבצים באמצעות TFRecordDataset / CsvDataset / וכו '. היא הדרך היעילה ביותר לצרוך נתונים, שכן אז TensorFlow עצמה יכולה לנהל את הטעינה וההעברה האסינכרונית של נתונים, מבלי לערב את Python. למידע נוסף, עיין במדריך tf.data .
צבירת ערכים בלולאה
דפוס נפוץ הוא צבירת ערכי ביניים מלולאה. בדרך כלל, הדבר נעשה על ידי הצטרפות לרשימת פייתון או הוספת ערכים למילון פייתון. עם זאת, מכיוון שמדובר בתופעות לוואי של פייתון, הן לא יעבדו כצפוי בלולאה הנגללת באופן דינמי. השתמש ב- tf.TensorArray
כדי לצבור תוצאות 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.8876684 , 0.51591396, 0.906677 , 0.9903525 ], [1.06799 , 1.2731225 , 1.0150459 , 1.4682751 ], [1.117998 , 1.4896113 , 1.8637874 , 1.9430903 ]], [[0.9585289 , 0.6935735 , 0.76666355, 0.1607722 ], [1.1963592 , 1.0271138 , 1.5978618 , 0.61320186], [1.5212903 , 1.8943032 , 1.6955764 , 1.2236354 ]]], dtype=float32)>
לקריאה נוספת
למידע על אופן הייצוא והטעינה של Function
, עיין במדריך SavedModel . למידע נוסף על אופטימיזציות גרפים המבוצעות לאחר מעקב, עיין במדריך Grappler . כדי ללמוד כיצד לבצע אופטימיזציה של צינור הנתונים שלך ולפרופיל את המודל שלך, עיין במדריך Profiler .