דיוק מעורב

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

סקירה כללית

דיוק מעורב הוא השימוש בשני סוגי נקודה צפה של 16 סיביות ו-32 סיביות במודל במהלך האימון כדי לגרום לו לרוץ מהר יותר ולהשתמש בפחות זיכרון. על ידי שמירת חלקים מסוימים של המודל בסוגי 32 סיביות ליציבות מספרית, למודל יהיה זמן צעדים נמוך יותר ויתאמן באותה מידה גם במונחים של מדדי הערכה כגון דיוק. מדריך זה מתאר כיצד להשתמש ב-Keras Mixed Precision API כדי להאיץ את הדגמים שלך. שימוש ב-API זה יכול לשפר את הביצועים ביותר מפי 3 ב-GPUs מודרניים ו-60% ב-TPUs.

כיום, רוב הדגמים משתמשים ב-float32 dtype, שלוקח 32 סיביות של זיכרון. עם זאת, ישנם שני סוגי dtypes בעלי דיוק נמוך יותר, float16 ו-bfloat16, כל אחד מהם לוקח 16 ביטים של זיכרון במקום זאת. מאיצים מודרניים יכולים להריץ פעולות מהר יותר ב-dtypes של 16 סיביות, מכיוון שיש להם חומרה מיוחדת להפעלת חישובי 16 סיביות וניתן לקרוא 16-bit dtypes מהזיכרון מהר יותר.

GPUs של NVIDIA יכולים להריץ פעולות ב-float16 מהר יותר מאשר ב-float32, ו-TPUs יכולים להריץ פעולות ב-bfloat16 מהר יותר מאשר float32. לכן, יש להשתמש ב-dtypes ברמת דיוק נמוכה יותר במידת האפשר במכשירים אלה. עם זאת, משתנים וכמה חישובים עדיין צריכים להיות ב-float32 מסיבות מספריות כך שהמודל מתאמן לאותה איכות. ה-API של Keras Mixed Precision מאפשר לך להשתמש בשילוב של float16 או bfloat16 עם float32, כדי לקבל את יתרונות הביצועים מ-float16/bfloat16 ואת יתרונות היציבות המספרית מ-float32.

להכין

import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras import mixed_precision

חומרה נתמכת

בעוד שדיוק מעורב יפעל ברוב החומרה, זה רק יאיץ דגמים ב-NVIDIA GPUs ו-Cloud TPUs האחרונים. גרפי NVIDIA תומכים בשילוב של float16 ו-float32, בעוד ש-TPUs תומכים בשילוב של bfloat16 ו-float32.

מבין GPUs של NVIDIA, אלה עם יכולת מחשוב 7.0 ומעלה יראו את הביצועים הגדולים ביותר מדיוק מעורב מכיוון שיש להם יחידות חומרה מיוחדות, הנקראות Tensor Cores, כדי להאיץ את ההכפלה והפיתולים של מטריצות float16. GPUs ישנים יותר אינם מציעים יתרון בביצועים מתמטיים לשימוש בדייקנות מעורבת, אולם חיסכון בזיכרון וברוחב הפס יכול לאפשר כמה מהירות. אתה יכול לחפש את יכולת החישוב של ה-GPU שלך בדף האינטרנט CUDA GPU של NVIDIA. דוגמאות למעבדי GPU שייהנו הכי הרבה מדיוק מעורב כוללות RTX GPUs, ה-V100 וה-A100.

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

nvidia-smi -L
GPU 0: Tesla V100-SXM2-16GB (UUID: GPU-99e10c4d-de77-42ee-4524-6c41c4e5e47d)

כל TPUs בענן תומכים ב-bfloat16.

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

הגדרת מדיניות dtype

כדי להשתמש בדיוק מעורב ב-Keras, עליך ליצור tf.keras.mixed_precision.Policy , המכונה בדרך כלל מדיניות dtype . מדיניות Dtype מציינת את שכבות ה-dtypes שיפעלו בהן. במדריך זה, תבנה מדיניות מהמחרוזת 'mixed_float16' ותגדיר אותה כמדיניות הגלובלית. זה יגרום לשכבות שנוצרו לאחר מכן להשתמש בדיוק מעורב עם תערובת של float16 ו-float32.

policy = mixed_precision.Policy('mixed_float16')
mixed_precision.set_global_policy(policy)
INFO:tensorflow:Mixed precision compatibility check (mixed_float16): OK
Your GPU will likely run quickly with dtype policy mixed_float16 as it has compute capability of at least 7.0. Your GPU: Tesla V100-SXM2-16GB, compute capability 7.0

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

# Equivalent to the two lines above
mixed_precision.set_global_policy('mixed_float16')

המדיניות מציינת שני היבטים חשובים של שכבה: ה-dtype שבו מתבצעים חישובי השכבה, וה-dtype של משתני השכבה. למעלה, יצרת מדיניות mixed_float16 (כלומר, mixed_precision.Policy שנוצרה על ידי העברת המחרוזת 'mixed_float16' שלה). עם מדיניות זו, שכבות משתמשות בחישובי float16 ובמשתני float32. החישובים נעשים ב-float16 לביצועים, אך יש לשמור משתנים ב-float32 ליציבות מספרית. אתה יכול לשאול ישירות את המאפיינים האלה של המדיניות.

print('Compute dtype: %s' % policy.compute_dtype)
print('Variable dtype: %s' % policy.variable_dtype)
Compute dtype: float16
Variable dtype: float32

כפי שהוזכר קודם לכן, מדיניות mixed_float16 תשפר באופן משמעותי ביותר את הביצועים ב-NVIDIA GPUs עם יכולת מחשוב של לפחות 7.0. המדיניות תפעל על GPUs ו-CPUs אחרים, אך עשויה שלא לשפר את הביצועים. עבור TPUs, יש להשתמש במדיניות mixed_bfloat16 במקום זאת.

בניית הדגם

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

inputs = keras.Input(shape=(784,), name='digits')
if tf.config.list_physical_devices('GPU'):
  print('The model will run with 4096 units on a GPU')
  num_units = 4096
else:
  # Use fewer units on CPUs so the model finishes in a reasonable amount of time
  print('The model will run with 64 units on a CPU')
  num_units = 64
dense1 = layers.Dense(num_units, activation='relu', name='dense_1')
x = dense1(inputs)
dense2 = layers.Dense(num_units, activation='relu', name='dense_2')
x = dense2(x)
The model will run with 4096 units on a GPU

לכל שכבה יש מדיניות והיא משתמשת במדיניות הגלובלית כברירת מחדל. לכן לכל אחת מהשכבות mixed_float16 Dense שהגדרת את המדיניות הגלובלית ל- mixed_float16 בעבר. זה יגרום לשכבות הצפופות לבצע חישובי float16 ולהיות בעלי משתני float32. הם משליכים את התשומות שלהם ל-float16 כדי לבצע חישובי float16, מה שגורם לפלטים שלהם להיות float16 כתוצאה מכך. המשתנים שלהם הם float32 ויוצקו ל-float16 כאשר השכבות ייקראו כדי למנוע שגיאות מחוסר התאמה של dtype.

print(dense1.dtype_policy)
print('x.dtype: %s' % x.dtype.name)
# 'kernel' is dense1's variable
print('dense1.kernel.dtype: %s' % dense1.kernel.dtype.name)
<Policy "mixed_float16">
x.dtype: float16
dense1.kernel.dtype: float32

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

# INCORRECT: softmax and model output will be float16, when it should be float32
outputs = layers.Dense(10, activation='softmax', name='predictions')(x)
print('Outputs dtype: %s' % outputs.dtype.name)
Outputs dtype: float16

הפעלת softmax בסוף הדגם צריכה להיות float32. מכיוון שמדיניות dtype היא mixed_float16 , להפעלת softmax תהיה בדרך כלל טנסורים float16 לחשב dtype ופלט float16.

ניתן לתקן זאת על ידי הפרדת שכבות Dense ו-softmax, ועל ידי העברת dtype='float32' לשכבת softmax:

# CORRECT: softmax and model output are float32
x = layers.Dense(10, name='dense_logits')(x)
outputs = layers.Activation('softmax', dtype='float32', name='predictions')(x)
print('Outputs dtype: %s' % outputs.dtype.name)
Outputs dtype: float32

העברת dtype='float32' לבנאי השכבות softmax עוקפת את מדיניות dtype של השכבה להיות מדיניות float32 , שעושה חישובים ושומרת משתנים ב-float32. באופן שווה, היית יכול לעבור במקום את dtype=mixed_precision.Policy('float32') ; שכבות תמיד ממירות את הארגומנט dtype למדיניות. מכיוון שלשכבת Activation אין משתנים, מתעלמים מהמשתנה dtype של המדיניות, אך ה-dtype של המדיניות של המדיניות של float32 גורם ל-softmax ולפלט המודל להיות float32.

הוספת float16 softmax באמצע דגם זה בסדר, אבל softmax בסוף הדגם צריך להיות ב-float32. הסיבה היא שאם טנזור הביניים הזורם מה-softmax לאובדן הוא float16 או bfloat16, עלולות להתרחש בעיות מספריות.

אתה יכול לעקוף את ה-dtype של כל שכבה להיות float32 על ידי העברת dtype='float32' אם אתה חושב שהיא לא תהיה יציבה מספרית עם חישובי float16. אבל בדרך כלל, זה נחוץ רק בשכבה האחרונה של המודל, מכיוון שלרוב השכבות יש דיוק מספיק עם mixed_float16 ו- mixed_bfloat16 .

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

# The linear activation is an identity function. So this simply casts 'outputs'
# to float32. In this particular case, 'outputs' is already float32 so this is a
# no-op.
outputs = layers.Activation('linear', dtype='float32')(outputs)

לאחר מכן, סיים והרכיב את המודל, והפק נתוני קלט:

model = keras.Model(inputs=inputs, outputs=outputs)
model.compile(loss='sparse_categorical_crossentropy',
              optimizer=keras.optimizers.RMSprop(),
              metrics=['accuracy'])

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255

דוגמה זו העבירה את נתוני הקלט מ-int8 ל-float32. אתה לא מטיל ל-float16 מכיוון שהחלוקה ב-255 היא על ה-CPU, שמריץ פעולות float16 לאט יותר מפעולות float32. במקרה זה, ההבדל בביצועים זניח, אבל באופן כללי אתה צריך להפעיל מתמטיקה של עיבוד קלט ב-float32 אם הוא פועל על ה-CPU. השכבה הראשונה של המודל תטיל את התשומות ל-float16, כאשר כל שכבה מטילה תשומות של נקודה צפה ל-dtype המחשוב שלה.

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

initial_weights = model.get_weights()

הכשרת הדוגמנית עם Model.fit

לאחר מכן, אמנו את הדגם:

history = model.fit(x_train, y_train,
                    batch_size=8192,
                    epochs=5,
                    validation_split=0.2)
test_scores = model.evaluate(x_test, y_test, verbose=2)
print('Test loss:', test_scores[0])
print('Test accuracy:', test_scores[1])
Epoch 1/5
6/6 [==============================] - 2s 78ms/step - loss: 4.9609 - accuracy: 0.4132 - val_loss: 0.6643 - val_accuracy: 0.8437
Epoch 2/5
6/6 [==============================] - 0s 34ms/step - loss: 0.7752 - accuracy: 0.7789 - val_loss: 0.3098 - val_accuracy: 0.9175
Epoch 3/5
6/6 [==============================] - 0s 34ms/step - loss: 0.3620 - accuracy: 0.8848 - val_loss: 0.3149 - val_accuracy: 0.8969
Epoch 4/5
6/6 [==============================] - 0s 34ms/step - loss: 0.2998 - accuracy: 0.9066 - val_loss: 0.2988 - val_accuracy: 0.9068
Epoch 5/5
6/6 [==============================] - 0s 33ms/step - loss: 0.2298 - accuracy: 0.9285 - val_loss: 0.5062 - val_accuracy: 0.8414
313/313 - 0s - loss: 0.5163 - accuracy: 0.8392
Test loss: 0.5163048505783081
Test accuracy: 0.8392000198364258

שימו לב שהדגם מדפיס את הזמן לכל שלב ביומנים: לדוגמה, "25ms/צעד". התקופה הראשונה עשויה להיות איטית יותר מכיוון ש-TensorFlow משקיעה זמן מה באופטימיזציה של המודל, אך לאחר מכן הזמן לכל שלב אמור להתייצב.

אם אתה מפעיל את המדריך הזה ב-Colab, אתה יכול להשוות את הביצועים של דיוק מעורב עם float32. כדי לעשות זאת, שנה את המדיניות מ- mixed_float16 ל- float32 בקטע "הגדרת מדיניות dtype", ולאחר מכן הרץ מחדש את כל התאים עד לנקודה זו. במעבדי GPU עם יכולת מחשוב 7.X, אתה אמור לראות את הזמן לכל שלב עולה באופן משמעותי, מה שמצביע על דיוק מעורב שהאיץ את המודל. הקפד לשנות את המדיניות בחזרה ל- mixed_float16 ולהפעיל מחדש את התאים לפני שתמשיך עם המדריך.

ב-GPUs עם יכולת מחשוב של לפחות 8.0 (GPUs אמפר ומעלה), סביר להניח שלא תראה שיפור בביצועים בדגם הצעצוע במדריך זה בעת שימוש בדיוק מעורב בהשוואה ל-float32. זה נובע מהשימוש ב- TensorFloat-32 , שמשתמש אוטומטית במתמטיקה עם דיוק נמוך יותר בפעולות מסוימות של float32 כגון tf.linalg.matmul . TensorFloat-32 נותן כמה מיתרונות הביצועים של דיוק מעורב בעת שימוש ב-float32. עם זאת, בדגמים בעולם האמיתי, עדיין תראה בדרך כלל שיפורים משמעותיים בביצועים מרמת דיוק מעורבת בשל חיסכון ברוחב פס זיכרון ופעולות בהן TensorFloat-32 אינו תומך.

אם מפעילים דיוק מעורב על TPU, לא תראה רווח גדול בביצועים בהשוואה להפעלת דיוק מעורב על GPUs, במיוחד GPUs pre-Ampere. הסיבה לכך היא ש-TPUs מבצעים פעולות מסוימות ב-bfloat16 מתחת למכסה המנוע אפילו עם מדיניות ברירת המחדל של dtype של float32. זה דומה לאופן שבו GPUs של אמפר משתמשים ב- TensorFloat-32 כברירת מחדל. בהשוואה למעבדי GPU של אמפר, TPUs בדרך כלל רואים פחות שיפורי ביצועים עם דיוק מעורב בדגמים מהעולם האמיתי.

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

קנה מידה של הפסד

קנה מידה של הפסד הוא טכניקה tf.keras.Model.fit מבצעת אוטומטית עם מדיניות mixed_float16 כדי למנוע זרימה מספרית. סעיף זה מתאר מהו קנה מידה הפסד והסעיף הבא מתאר כיצד להשתמש בו עם לולאת אימון מותאמת אישית.

תחתית וגלישה

לסוג הנתונים float16 יש טווח דינמי צר בהשוואה ל-float32. משמעות הדבר היא שערכים מעל \(65504\) יעלו על גדותיו לאינסוף וערכים מתחת \(6.0 \times 10^{-8}\) יזרמו לאפס. ל-float32 ול-bfloat16 טווח דינמי גבוה בהרבה, כך שגלישה ו-underflow אינם מהווים בעיה.

לדוגמה:

x = tf.constant(256, dtype='float16')
(x ** 2).numpy()  # Overflow
inf
x = tf.constant(1e-5, dtype='float16')
(x ** 2).numpy()  # Underflow
0.0

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

סקירת קנה המידה של הפסדים

הרעיון הבסיסי של קנה המידה של הפסד הוא פשוט: פשוט תכפיל את ההפסד במספר גדול כלשהו, ​​נניח \(1024\), ותקבל את ערך סולם ההפסד . זה יגרום להגדלת השיפועים גם לפי \(1024\) , ויקטין מאוד את הסיכוי לזרימה נמוכה. לאחר חישוב ההדרגות הסופיות, חלק אותם ב- \(1024\) כדי להחזיר אותם לערכים הנכונים שלהם.

הפסאודוקוד עבור תהליך זה הוא:

loss_scale = 1024
loss = model(inputs)
loss *= loss_scale
# Assume `grads` are float32. You do not want to divide float16 gradients.
grads = compute_gradient(loss, model.trainable_variables)
grads /= loss_scale

בחירת סולם הפסד יכולה להיות מסובכת. אם סולם ההפסדים נמוך מדי, שיפועים עשויים עדיין לאפס. אם גבוה מדי, הבעיה הפוכה מתרחשת: ההדרגות עלולות לעלות על גדותיהן עד אינסוף.

כדי לפתור זאת, TensorFlow קובע באופן דינמי את סולם ההפסדים כך שלא תצטרך לבחור אחד באופן ידני. אם אתה משתמש ב- tf.keras.Model.fit , קנה המידה של הפסד נעשה עבורך כך שלא תצטרך לעשות שום עבודה נוספת. אם אתה משתמש בלולאת אימון מותאמת אישית, עליך להשתמש במפורש במעטפת האופטימיזציה המיוחדת tf.keras.mixed_precision.LossScaleOptimizer כדי להשתמש בקנה מידה אובדן. זה מתואר בסעיף הבא.

אימון הדגם עם לולאת אימון מותאמת אישית

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

הפעלת לולאת אימון מותאמת אישית בדייקנות מעורבת דורשת שני שינויים על פני הפעלתה ב-float32:

  1. בנה את הדגם בדיוק מעורב (כבר עשית את זה)
  2. השתמש במפורש בקנה מידה הפסד אם נעשה שימוש mixed_float16 .

עבור שלב (2), תשתמש במחלקה tf.keras.mixed_precision.LossScaleOptimizer , אשר עוטפת אופטימיזציה ומחילה קנה מידה אובדן. כברירת מחדל, הוא קובע באופן דינמי את סולם ההפסדים כך שלא תצטרך לבחור אחד. בנה LossScaleOptimizer באופן הבא.

optimizer = keras.optimizers.RMSprop()
optimizer = mixed_precision.LossScaleOptimizer(optimizer)

אם תרצה, אפשר לבחור בסולם אובדן מפורש או להתאים באופן אחר את התנהגות קנה המידה של ההפסד, אך מומלץ מאוד לשמור על התנהגות קנה המידה של ההפסדים המוגדרים כברירת מחדל, שכן נמצא כי היא עובדת היטב בכל הדגמים המוכרים. עיין בתיעוד tf.keras.mixed_precision.LossScaleOptimizer אם ברצונך להתאים אישית את התנהגות קנה המידה של אובדן.

לאחר מכן, הגדר את אובייקט האובדן ואת s tf.data.Dataset :

loss_object = tf.keras.losses.SparseCategoricalCrossentropy()
train_dataset = (tf.data.Dataset.from_tensor_slices((x_train, y_train))
                 .shuffle(10000).batch(8192))
test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(8192)

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

  • get_scaled_loss(loss) : מכפיל את ההפסד בסולם ההפסדים
  • get_unscaled_gradients(gradients) : מקבל רשימה של מעברים מותאמים כקלט, ומחלק כל אחד בסולם האובדן כדי לבטל את קנה המידה שלהם

יש להשתמש בפונקציות אלו על מנת למנוע זרימה תת-קרקעית בשיפועים. LossScaleOptimizer.apply_gradients לאחר מכן מעברי צבע אם לאף אחד מהם אין s Inf s או NaN s. זה גם יעדכן את סולם ההפסדים, יקצר אותו בחצי אם לדרגות היו Inf s או NaN s וייתכן שיגדיל אותו אחרת.

@tf.function
def train_step(x, y):
  with tf.GradientTape() as tape:
    predictions = model(x)
    loss = loss_object(y, predictions)
    scaled_loss = optimizer.get_scaled_loss(loss)
  scaled_gradients = tape.gradient(scaled_loss, model.trainable_variables)
  gradients = optimizer.get_unscaled_gradients(scaled_gradients)
  optimizer.apply_gradients(zip(gradients, model.trainable_variables))
  return loss

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

כעת, הגדר את שלב הבדיקה:

@tf.function
def test_step(x):
  return model(x, training=False)

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

model.set_weights(initial_weights)

לבסוף, הפעל את לולאת האימון המותאמת אישית:

for epoch in range(5):
  epoch_loss_avg = tf.keras.metrics.Mean()
  test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(
      name='test_accuracy')
  for x, y in train_dataset:
    loss = train_step(x, y)
    epoch_loss_avg(loss)
  for x, y in test_dataset:
    predictions = test_step(x)
    test_accuracy.update_state(y, predictions)
  print('Epoch {}: loss={}, test accuracy={}'.format(epoch, epoch_loss_avg.result(), test_accuracy.result()))
Epoch 0: loss=4.869325160980225, test accuracy=0.7221999764442444
Epoch 1: loss=0.4893573224544525, test accuracy=0.878000020980835
Epoch 2: loss=0.36011582612991333, test accuracy=0.9440000057220459
Epoch 3: loss=0.27391332387924194, test accuracy=0.9318000078201294
Epoch 4: loss=0.247697651386261, test accuracy=0.933899998664856

טיפים לביצועי GPU

להלן כמה עצות לביצועים בעת שימוש בדייקנות מעורבת במעבדי GPU.

הגדלת גודל האצווה שלך

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

הקפדה על שימוש בליבות טנסור של GPU

כפי שהוזכר קודם לכן, GPUs מודרניים של NVIDIA משתמשים ביחידת חומרה מיוחדת בשם Tensor Cores שיכולה להכפיל מטריצות float16 במהירות רבה. עם זאת, ליבות Tensor דורשות מממדים מסוימים של טנסורים להיות כפולה של 8. בדוגמאות שלהלן, ארגומנט מודגש אם ורק אם הוא צריך להיות כפולה של 8 כדי להשתמש ב- Tensor Cores.

  • tf.keras.layers.Dense( units=64 )
  • tf.keras.layers.Conv2d( filters=48 , kernel_size=7, stride=3)
    • ובדומה לשכבות קונבולוציוניות אחרות, כגון tf.keras.layers.Conv3d
  • tf.keras.layers.LSTM( units=64 )
    • ודומה עבור RNNs אחרים, כגון tf.keras.layers.GRU
  • tf.keras.Model.fit(epochs=2, batch_size=128 )

כדאי לנסות להשתמש ב- Tensor Cores במידת האפשר. אם אתה רוצה ללמוד עוד, מדריך ביצועי למידה עמוקה של NVIDIA מתאר את הדרישות המדויקות לשימוש בליבות Tensor וכן מידע אחר על ביצועים הקשורים ל- Tensor Core.

XLA

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

טיפים לביצועי TPU בענן

כמו במעבדי GPU, עליך לנסות להכפיל את גודל האצווה שלך בעת שימוש ב-Cloud TPUs מכיוון שטנסור bfloat16 משתמש בחצי מהזיכרון. הכפלת גודל האצווה עשויה להגדיל את תפוקת האימון.

TPUs אינם דורשים כל כוונון מעורב אחר ספציפי לדיוק כדי לקבל ביצועים אופטימליים. הם כבר דורשים שימוש ב-XLA. TPUs נהנים מכך שמידות מסוימות הן כפולות של \(128\), אבל זה חל באותה מידה על סוג float32 כמו על דיוק מעורב. עיין במדריך הביצועים של Cloud TPU לקבלת טיפים כלליים לביצועי TPU, החלים על דיוק מעורב כמו גם על טנסור float32.

סיכום

  • אתה צריך להשתמש בדיוק מעורב אם אתה משתמש ב-TPUs או NVIDIA GPUs עם יכולת מחשוב לפחות 7.0, שכן זה ישפר את הביצועים עד פי 3.
  • אתה יכול להשתמש בדיוק מעורב עם השורות הבאות:

    # On TPUs, use 'mixed_bfloat16' instead
    mixed_precision.set_global_policy('mixed_float16')
    
  • אם הדגם שלך מסתיים ב-softmax, ודא שהוא float32. ובלי קשר למה שהדגם שלך מסתיים בו, ודא שהפלט הוא float32.

  • אם אתה משתמש בלולאת אימון מותאמת אישית עם mixed_float16 , בנוסף לשורות לעיל, עליך לעטוף את האופטימיזציה שלך עם tf.keras.mixed_precision.LossScaleOptimizer . לאחר מכן התקשר ל- optimizer.get_scaled_loss כדי לשנות את קנה המידה של ההפסד, ול- optimizer.get_unscaled_gradients כדי לבטל את קנה המידה של מעברי הצבע.

  • הכפיל את גודל אצווה האימון אם זה לא מפחית את דיוק ההערכה

  • במעבדי GPU, ודא שרוב מימדי הטנסור הם כפולה של \(8\) כדי למקסם את הביצועים

לדוגמאות נוספות של דיוק מעורב באמצעות tf.keras.mixed_precision API, בדוק את מאגר הדגמים הרשמי . רוב הדגמים הרשמיים, כגון ResNet ו- Transformer , יפעלו תוך שימוש בדיוק מעורב על ידי העברת --dtype=fp16 .