דף זה תורגם על ידי Cloud Translation API.
Switch to English

מבוא לתרשימים וגרפים

צפה ב- TensorFlow.org הפעל בגוגל קולאב צפה במקור ב- GitHub הורד מחברת

סקירה כללית

מדריך זה נכנס מתחת לפני השטח של TensorFlow ו- Keras כדי לראות כיצד TensorFlow עובד. אם אתה רוצה להתחיל מיד עם Keras, אנא עיין באוסף המדריכים שלנו .

במדריך זה תראה את הליבה של האופן שבו TensorFlow מאפשר לך לבצע שינויים פשוטים בקוד שלך כדי לקבל גרפים וכיצד הם מאוחסנים ומיוצגים וכיצד תוכל להשתמש בהם כדי להאיץ ולייצא את המודלים שלך.

זו הקדמה לטופס קצר; לקבלת מבוא מלא למושגים אלה, עיין במדריך tf.function .

מהם גרפים?

בשלושת המדריכים הקודמים ראיתם את TensorFlow פועל בשקיקה . המשמעות היא שפעולות TensorFlow מבוצעות על ידי פייתון, פעולה על ידי פעולה, ומחזירות תוצאות חזרה לפייתון. להוט TensorFlow מנצל את GPUs, ומאפשר לך למקם משתנים, טנזורים ואפילו פעולות על GPUs ו- TPU. קל גם לבצע ניפוי באגים.

עבור משתמשים מסוימים, לעולם לא תזדקק או תרצה לעזוב את Python.

עם זאת, הפעלת TensorFlow op-by-op ב- Python מונעת שלל תאוצות שקיימות אחרת. אם אתה יכול לחלץ חישובי טנסור מפייתון, אתה יכול להפוך אותם לגרף .

גרפים הם מבני נתונים המכילים קבוצה של אובייקטים tf.Operation , המייצגים יחידות חישוב; ו tf.Tensor אובייקטים, המייצגים את יחידות הנתונים tf.Tensor בין פעולות. הם מוגדרים בהקשר tf.Graph . מכיוון שגרפים אלה הם מבני נתונים, ניתן לשמור, להריץ ולשחזר אותם ללא קוד הפייתון המקורי.

כך נראה גרף דו-שכבתי פשוט כאשר מדמיינים אותו ב- TensorBoard.

גרף זרימת טנסור דו שכבתי

היתרונות של גרפים

בעזרת גרף יש לך מידה רבה של גמישות. אתה יכול להשתמש בתרשים TensorFlow שלך בסביבות שאין להם מתורגמן של Python, כמו יישומים ניידים, התקנים מוטבעים ושרתי backend. TensorFlow משתמש בגרפים כפורמט עבור דגמים שמורים כאשר הוא מייצא אותם מפייתון.

ניתן לבצע אופטימיזציה של גרפים בקלות, מה שמאפשר למהדר לבצע טרנספורמציות כמו:

  • מסיקים סטטית את ערך הטנזורים על ידי קיפול צמתים קבועים בחישוב שלכם ("קיפול קבוע") .
  • הפרד חלקי משנה של חישוב שאינם תלויים וחלק אותם בין שרשור או התקן.
  • לפשט פעולות חשבון באמצעות ביטול ביטויי משנה נפוצים.

יש מערכת אופטימיזציה שלמה, Grappler , שתבצע מהירויות זה ואחרות.

בקיצור, גרפים שימושיים ביותר ומאפשרים ל- TensorFlow לרוץ מהר , לרוץ במקביל ולהפעיל ביעילות על מספר מכשירים .

עם זאת, אתה עדיין רוצה להגדיר את מודלי הלמידה החישובית שלנו (או חישובים אחרים) בפייתון מטעמי נוחות, ואז לבנות אוטומטית גרפים כשתזדקק להם.

מעקב אחר גרפים

הדרך בה אתה יוצר גרף ב- TensorFlow היא להשתמש ב- tf.function , tf.function ישירה או כמעצב.

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

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

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

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

tf.function -ized פונקציות הם callables Python שפועלים זהה ושווי Python שלהם. יש להם מחלקה מסוימת ( python.eager.def_function.Function ), אבל מבחינתך הם מתנהגים בדיוק כמו הגרסה שאינה python.eager.def_function.Function .

tf.function עוקבת אחר כל פונקציית פייתון שהיא מכנה.

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

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

  return inner_function(x, y, b)

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

אם השתמשת ב- TensorFlow 1.x, תבחין שבשום פנים ואופן לא היית צריך להגדיר Placeholder או tf.Sesssion .

בקרת זרימה ותופעות לוואי

בקרת זרימה ולולאות מומרות כברירת מחדל ל- TensorFlow באמצעות tf.autograph . אוטוגרף משתמש בשילוב של שיטות, כולל סטנדרטיזציה של מבני לולאה, התגלגלות ומניפולציה של AST .

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

a_function = tf.function(my_function)

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

אתה יכול להתקשר ישירות להמרת חתימה כדי לראות כיצד פיתון הופך ל- TensorFlow ops. זה, לרוב, לא קריא, אבל אתה יכול לראות את השינוי.

# Don't read the output too carefully.
print(tf.autograph.to_code(my_function))
def tf__my_function(x):
    with ag__.FunctionScope('my_function', 'fscope', ag__.ConversionOptions(recursive=True, user_requested=True, optional_features=(), internal_convert_user_code=True)) as fscope:
        do_return = False
        retval_ = ag__.UndefinedReturnValue()

        def get_state():
            return (retval_, do_return)

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

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

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


חתימה אוטומטית ממירה if-then סעיפים, לולאות, break , return , continue ועוד.

לרוב, חתימה תעבוד ללא שיקולים מיוחדים. עם זאת, ישנם כמה אזהרות, ומדריך הפונקציה tf. יכול לעזור כאן, כמו גם הפניה החתימה המלאה

לראות את המהירות

tf.function גלישת פונקציה המשתמשת tf.function בפונקציה tf.function אינה tf.function אוטומטית את הקוד שלך. עבור פונקציות קטנות הנקראות מספר פעמים במכונה אחת, התקורה של קריאת גרף או קטע גרף עשויה לשלוט בזמן הריצה. כמו כן, אם רוב החישוב כבר היה קורה במאיץ, כמו ערימות של עיקולים כבדים ב- GPU, מהירות הגרף לא תהיה גדולה.

עבור חישובים מסובכים, גרפים יכולים לספק מהירות משמעותית. הסיבה לכך היא שגרפים מפחיתים את תקשורת פייתון למכשיר ומבצעים כמה מהירויות.

קוד זה כפול כמה ריצות על כמה שכבות צפופות קטנות.

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

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

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

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

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

פונקציות פולימורפיות

כאשר אתה עוקב אחר פונקציה, אתה יוצר אובייקט Function שהוא פולימורפי . פונקציה פולימורפית היא קריאה לפיתון המכיל כמה גרפים של פונקציות קונקרטיות מאחורי API אחד.

אתה יכול להשתמש בזה Function על כל סוגים שונים של dtypes וצורות. בכל פעם שאתה מפעיל את זה עם חתימת טיעון חדשה, הפונקציה המקורית מתחקה אחר הארגומנטים החדשים. לאחר מכן, Function מאחסן את tf.Graph המתאים tf.Graph עקב tf.Graph concrete_function . אם הפונקציה כבר עוקבה אחר סוג זה של טיעון, אתה פשוט מקבל את הגרף שלך.

מבחינה רעיונית, אז:

  • tf.Graph הוא מבנה הנתונים הגולמי tf.Graph המתאר חישוב
  • Function היא מטמון, מעקב, שולח על פני Function בטון
  • ConcreteFunction הוא עטיפה תואמת להוט סביב גרף המאפשר לבצע את הגרף מ- Python

בדיקת פונקציות פולימורפיות

אתה יכול לבדוק a_function , שהיא תוצאה של קריאה ל- tf.function בפונקציית Python my_function . בדוגמה זו, קריאת a_function עם שלושה סוגים של ארגומנטים מביאה לשלוש פונקציות קונקרטיות שונות.

print(a_function)

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

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

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

בדוגמה זו אתה רואה די הרבה בערימה. אלא אם כן אתה מנהל את המעקב באופן ספציפי, בדרך כלל לא תצטרך להתקשר ישירות לפונקציות קונקרטיות כפי שמוצג כאן.

חוזרים לביצוע להוט

אתה עלול למצוא את עצמך מסתכל על עקבות מחסנית ארוכים, במיוחד כאלה שמתייחסים ל tf.Graph או with tf.Graph().as_default() . זה אומר שאתה צפוי לרוץ בהקשר גרפי. פונקציות הליבה ב- TensorFlow משתמשות בהקשרים גרפיים, כמו למשל model.fit של model.fit() .

לעתים קרובות קל הרבה יותר לפתור באגים לביצוע להוט. עקבות הערימה צריכות להיות קצרות יחסית וקלות להבנה.

במצבים שבהם הגרף הופך את הבעיות לניפוי מסובך, אתה יכול לחזור ולהשתמש בביצוע להוט לניפוי באגים.

להלן דרכים בהן תוכלו לוודא שאתם רצים בשקיקה:

  • התקשר למודלים ולשכבות ישירות כניתנות להתקשרות

  • כאשר אתה משתמש ב- Keras קומפילציה / התאמה, בזמן הידור השתמש ב- model.compile(run_eagerly=True)

  • הגדר מצב ביצוע גלובלי באמצעות tf.config.run_functions_eagerly(True)

באמצעות run_eagerly=True

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

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

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

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

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

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

ראשית, הידר את המודל מבלי להוט. שים לב שהדגם לא נמצא במעקב; למרות שמו, compile מגדיר רק פונקציות אובדן, אופטימיזציה ופרמטרים אחרים של אימונים.

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

עכשיו, התקשרו fit ובדקו שהמעקב אחר הפונקציה (פעמיים) ואז האפקט הלהוט לעולם לא יופעל שוב.

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

Currently running eagerly 2020-11-04 02:22:28.114630

Currently running eagerly 2020-11-04 02:22:28.233822
2/2 [==============================] - 0s 2ms/step - loss: 0.9890
Epoch 2/3
2/2 [==============================] - 0s 1ms/step - loss: 0.0017
Epoch 3/3
2/2 [==============================] - 0s 1ms/step - loss: 5.4478e-04

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

אם אתה מנהל אפילו תקופה אחת בלהיטות, עם זאת, אתה יכול לראות את תופעת הלוואי הלהוטה פעמיים.

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

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

Currently running eagerly 2020-11-04 02:22:28.449835
1/2 [==============>...............] - ETA: 0s - loss: 7.8712e-04
Currently running eagerly 2020-11-04 02:22:28.472645
2/2 [==============================] - 0s 6ms/step - loss: 4.1988e-04

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

באמצעות run_functions_eagerly

אתה יכול גם להגדיר שהכל יפעל בשקיקה. זהו מתג העוקף את הפונקציות העקבות של הפונקציה הפולימורפית וקורא ישירות לפונקציה המקורית. אתה יכול להשתמש בזה לצורך איתור באגים.

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

# Create a polymorphic function
polymorphic_function = tf.function(model)

print("Tracing")
# This does, in fact, trace the function
print(polymorphic_function.get_concrete_function(input_data))

print("\nCalling twice eagerly")
# When you run the function again, you will see the side effect
# twice, as the function is running eagerly.
result = polymorphic_function(input_data)
result = polymorphic_function(input_data)
Run all functions eagerly.
Tracing

Currently running eagerly 2020-11-04 02:22:28.502694
ConcreteFunction function(self)
  Args:
    self: float32 Tensor, shape=(60, 28, 28)
  Returns:
    float32 Tensor, shape=(60, 10)

Calling twice eagerly

Currently running eagerly 2020-11-04 02:22:28.507036

Currently running eagerly 2020-11-04 02:22:28.508331

# Don't forget to set it back when you are done
tf.config.experimental_run_functions_eagerly(False)
WARNING:tensorflow:From <ipython-input-1-782fe9ce7b18>:2: experimental_run_functions_eagerly (from tensorflow.python.eager.def_function) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.config.run_functions_eagerly` instead of the experimental version.

מעקב וביצועים

מעקב עולה תקורה. למרות שמעקב אחר פונקציות קטנות הוא מהיר, דגמים גדולים יכולים לקחת זמן שעון קיר מורגש. השקעה זו מוחזרת בדרך כלל במהירות עם שיפור ביצועים, אך חשוב להיות מודעים לכך שהתקופות הראשונות של כל אימון מודלים גדול יכולות להיות איטיות יותר עקב מעקב.

לא משנה כמה גדול המודל שלך, אתה רוצה להימנע ממעקב לעתים קרובות. סעיף זה במדריך פונקציות tf.com דן כיצד להגדיר מפרטי קלט ולהשתמש בארגומנטים של טנסור כדי למנוע חזרה על עקבותיהם. אם אתה מוצא שאתה מקבל ביצועים גרועים במיוחד, כדאי לבדוק אם אתה חוזר בטעות.

באפשרותך להוסיף תופעת לוואי להוטה בלבד (כגון הדפסת ארגומנט של פייתון) כדי שתוכל לראות מתי מתחקה אחר הפונקציה. כאן תראה עקירה נוספת מכיוון שארגומנטים חדשים של Python מפעילים תמיד את ההחזרה.

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

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

# This retraces each time the Python argument 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)
tf.Tensor(11, shape=(), dtype=int32)
Tracing!
tf.Tensor(6, shape=(), dtype=int32)
Tracing!
tf.Tensor(11, shape=(), dtype=int32)

הצעדים הבאים

תוכלו לקרוא דיון מעמיק יותר גם בעמוד הפניה ל- API של tf.function וגם במדריך .