אופטימיזציה של גרף TensorFlow עם Grappler

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

סקירה כללית

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

גראפלר היא מערכת ברירת המחדל לאופטימיזציה של גרפים בזמן הריצה של TensorFlow. Grappler מחיל אופטימיזציות במצב גרף (בתוך tf.function ) כדי לשפר את הביצועים של חישובי TensorFlow שלך באמצעות הפשטות גרפים ואופטימיזציות אחרות ברמה גבוהה, כגון גופי פונקציה מוטבעים כדי לאפשר אופטימיזציות בין-פרוצדורות. אופטימיזציה של tf.Graph גם מפחיתה את השימוש בשיא הזיכרון במכשיר ומשפרת את ניצול החומרה על ידי אופטימיזציה של המיפוי של צמתים גרפים למשאבי מחשוב.

השתמש tf.config.optimizer.set_experimental_options() לשליטה עדינה יותר באופטימיזציות tf.Graph .

כלי אופטימיזציה של גרפים זמינים

גראפלר מבצע אופטימיזציות של גרפים באמצעות מנהל התקן ברמה עליונה בשם MetaOptimizer . כלי האופטימיזציה של הגרפים הבאים זמינים עם TensorFlow:

  • אופטימיזציית קיפול מתמיד - מסיק באופן סטטי את ערכם של הטנזורים במידת האפשר על ידי קיפול צמתים קבועים בגרף ומממש את התוצאה באמצעות קבועים.
  • מיטוב אריתמטי - מפשט פעולות אריתמטיות על ידי ביטול ביטויי משנה נפוצים ופישוט הצהרות אריתמטיות.
  • אופטימיזציית פריסה - מייעל פריסות טנזור לביצוע פעולות תלויות פורמט נתונים כגון פיתולים בצורה יעילה יותר.
  • אופטימיזציית Remapper - ממפה מחדש תת-גרפים למימושים יעילים יותר על-ידי החלפת תת-גרפים שכיחים עם גרעינים מונוליטיים מותכים.
  • אופטימיזציית זיכרון - מנתח את הגרף כדי לבדוק את שיא השימוש בזיכרון עבור כל פעולה ומכניס פעולות העתקת זיכרון CPU-GPU להחלפת זיכרון GPU למעבד כדי להפחית את שיא השימוש בזיכרון.
  • אופטימיזציית תלות - מסיר או מסדר מחדש תלות בקרה כדי לקצר את הנתיב הקריטי לשלב מודל או לאפשר אופטימיזציות אחרות. מסיר גם צמתים שהם למעשה ללא פעולות כגון Identity.
  • מיטוב גיזום - גוזם צמתים שאין להם השפעה על הפלט מהגרף. בדרך כלל היא מופעלת תחילה כדי להקטין את גודל הגרף ולהאיץ את העיבוד במעברי גרפלר אחרים.
  • אופטימיזציית פונקציות - מייעל את ספריית הפונקציות של תוכנית TensorFlow ושוליים גופי פונקציות כדי לאפשר אופטימיזציות בין-פרוצדורות אחרות.
  • מייעל צורות - מייעל תת-גרפים הפועלים על מידע הקשור לצורה וצורה.
  • אופטימיזציה אוטומטית במקביל - מקבילה גרפים באופן אוטומטי על ידי פיצול לאורך ממד האצווה. כלי האופטימיזציה הזה כבוי כברירת מחדל.
  • אופטימיזציית לולאות - מייעל את זרימת בקרת הגרף על ידי הנפת תת-גרפים ללא לולאות מתוך לולאות ועל ידי הסרת פעולות מחסנית מיותרות בלולאות. גם מייעל לולאות עם ספירת נסיעות ידועה סטטית ומסיר ענפים מתים ידועים סטטית בתנאים.
  • כלי אופטימיזציה של מקצים בהיקף - מציג מקצים בטווח כדי לצמצם את תנועת הנתונים ולאחד פעולות מסוימות.
  • הצמד לאופטימיזציית מארח - מחליף פעולות קטנות למעבד. כלי האופטימיזציה הזה כבוי כברירת מחדל.
  • אופטימיזציית דיוק מעורבת אוטומטית - ממיר סוגי נתונים ל-float16 היכן שניתן לשיפור הביצועים. נכון לעכשיו, חל רק על GPUs.
  • Debug stripper - מסיר צמתים הקשורים לפעולות איתור באגים כגון tf.debugging.Assert , tf.debugging.check_numerics ו- tf.print מהגרף. כלי האופטימיזציה הזה כבוי כברירת מחדל.

להכין

import numpy as np
import timeit
import traceback
import contextlib


import tensorflow as tf

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

@contextlib.contextmanager
def options(options):
  old_opts = tf.config.optimizer.get_experimental_options()
  tf.config.optimizer.set_experimental_options(options)
  try:
    yield
  finally:
    tf.config.optimizer.set_experimental_options(old_opts)

השווה ביצועי ביצוע עם גראפלר ובלי

TensorFlow 2 ואילך פועל בשקיקה כברירת מחדל. השתמש tf.function כדי להעביר את ברירת המחדל למצב גרף. גראפלר פועל אוטומטית ברקע כדי להחיל את האופטימיזציות של הגרפים לעיל ולשפר את ביצועי הביצוע.

אופטימיזציית קיפול מתמיד

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

def test_function_1():
  @tf.function
  def simple_function(input_arg):
    print('Tracing!')
    a = tf.constant(np.random.randn(2000,2000), dtype = tf.float32)
    c = a
    for n in range(50):
      c = c@a
    return tf.reduce_mean(c+input_arg)

  return simple_function

כבה את אופטימיזציית הקיפול הקבוע ובצע את הפונקציה:

with options({'constant_folding': False}):
  print(tf.config.optimizer.get_experimental_options())
  simple_function = test_function_1()
  # Trace once
  x = tf.constant(2.2)
  simple_function(x)
  print("Vanilla execution:", timeit.timeit(lambda: simple_function(x), number = 1), "s")
{'constant_folding': False, 'disable_model_pruning': False, 'disable_meta_optimizer': False}
Tracing!
Vanilla execution: 0.0018392090000816097 s

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

with options({'constant_folding': True}):
  print(tf.config.optimizer.get_experimental_options())
  simple_function = test_function_1()
  # Trace once
  x = tf.constant(2.2)
  simple_function(x)
  print("Constant folded execution:", timeit.timeit(lambda: simple_function(x), number = 1), "s")
{'constant_folding': True, 'disable_model_pruning': False, 'disable_meta_optimizer': False}
Tracing!
Constant folded execution: 0.0006749789999958011 s

ניפוי באגים אופטימיזציה של סטריפר

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

def test_function_2():
  @tf.function
  def simple_func(input_arg):
    output = input_arg
    tf.debugging.check_numerics(output, "Bad!")
    return output
  return simple_func

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

test_func = test_function_2()
p1 = tf.constant(float('inf'))
try:
  test_func(p1)
except tf.errors.InvalidArgumentError as e:
  traceback.print_exc(limit=2)
2021-09-22 20:34:55.871238: E tensorflow/core/kernels/check_numerics_op.cc:292] abnormal_detected_host @0x7f4878e00100 = {0, 1} Bad!
Traceback (most recent call last):
  File "/tmp/ipykernel_22954/3616845043.py", line 4, in <module>
    test_func(p1)
  File "/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/eager/def_function.py", line 885, in __call__
    result = self._call(*args, **kwds)
tensorflow.python.framework.errors_impl.InvalidArgumentError:  Bad! : Tensor had Inf values
     [[node CheckNumerics (defined at tmp/ipykernel_22954/2241890286.py:5) ]] [Op:__inference_simple_func_131]

Errors may have originated from an input operation.
Input Source operations connected to node CheckNumerics:
 input_arg (defined at tmp/ipykernel_22954/3616845043.py:4)

Function call stack:
simple_func

tf.debugging.check_numerics מעלה שגיאת ארגומנט לא חוקי בגלל הארגומנט Inf ל- test_func .

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

with options({'debug_stripper': True}):
  test_func2 = test_function_2()
  p1 = tf.constant(float('inf'))
  try:
    test_func2(p1)
  except tf.errors.InvalidArgumentError as e:
    traceback.print_exc(limit=2)

כלי אופטימיזציית ה-debug stripper מסיר את הצומת tf.debug.check_numerics מהגרף ומבצע את הפונקציה מבלי להעלות שגיאות כלשהן.

סיכום

זמן הריצה של TensorFlow משתמש ב-Grappler כדי לבצע אופטימיזציה אוטומטית של גרפים לפני ביצוע. השתמש tf.config.optimizer.set_experimental_options כדי להפעיל או להשבית את מיטובי הגרפים השונים.

למידע נוסף על גראפלר, ראה אופטימיזציות גרפי TensorFlow .