دقة مختلطة

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

ملخص

الدقة المختلطة هي استخدام نوعي الفاصلة العائمة 16 بت و 32 بت في نموذج أثناء التدريب لجعله يعمل بشكل أسرع واستخدام ذاكرة أقل. من خلال الاحتفاظ بأجزاء معينة من النموذج في أنواع 32 بت للاستقرار الرقمي ، سيكون للنموذج وقت خطوة أقل ويتدرب بشكل متساوٍ من حيث مقاييس التقييم مثل الدقة. يصف هذا الدليل كيفية استخدام واجهة برمجة تطبيقات الدقة المختلطة Keras لتسريع النماذج الخاصة بك. يمكن أن يؤدي استخدام واجهة برمجة التطبيقات هذه إلى تحسين الأداء بأكثر من 3 مرات على وحدات معالجة الرسومات الحديثة و 60٪ على وحدات المعالجة المركزية (TPU).

اليوم ، تستخدم معظم الطرز float32 dtype ، والذي يأخذ 32 بتًا من الذاكرة. ومع ذلك ، هناك نوعان من أنواع dtype منخفضة الدقة ، float16 و bfloat16 ، يستهلك كل منهما 16 بتًا من الذاكرة بدلاً من ذلك. يمكن للمسرعات الحديثة تشغيل العمليات بشكل أسرع في أنواع dtypes ذات 16 بت ، حيث تحتوي على أجهزة متخصصة لتشغيل حسابات 16 بت ويمكن قراءة أنواع dtypes ذات 16 بت من الذاكرة بشكل أسرع.

يمكن لوحدات معالجة الرسومات NVIDIA تشغيل العمليات في float16 بشكل أسرع من float32 ، ويمكن أن تقوم وحدات المعالجة المركزية (TPU) بتشغيل العمليات في bfloat16 بشكل أسرع من float32. لذلك ، يجب استخدام هذه الأنواع منخفضة الدقة كلما أمكن ذلك على تلك الأجهزة. ومع ذلك ، يجب أن تظل المتغيرات وبعض العمليات الحسابية في float32 لأسباب رقمية بحيث يتدرب النموذج على نفس الجودة. تتيح لك واجهة برمجة تطبيقات Keras المختلطة الدقة استخدام مزيج من 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 و Cloud TPU. تدعم وحدات معالجة الرسومات NVIDIA استخدام مزيج من float16 و float32 ، بينما تدعم TPUs مزيجًا من bfloat16 و float32.

من بين وحدات معالجة الرسومات NVIDIA ، فإن أولئك الذين لديهم قدرة حسابية 7.0 أو أعلى سيشهدون الاستفادة القصوى من الأداء من الدقة المختلطة لأن لديهم وحدات أجهزة خاصة ، تسمى Tensor Cores ، لتسريع مضاعفات وتلافيف مصفوفة float16. لا تقدم وحدات معالجة الرسومات القديمة أي فائدة في الأداء الرياضي لاستخدام الدقة المختلطة ، ولكن توفير الذاكرة وعرض النطاق الترددي يمكن أن يتيح بعض التسريع. يمكنك البحث عن إمكانية الحوسبة لوحدة معالجة الرسومات الخاصة بك على صفحة ويب CUDA GPU الخاصة بـ NVIDIA. تتضمن أمثلة وحدات معالجة الرسومات التي ستستفيد أكثر من الدقة المختلطة وحدات معالجة الرسومات RTX و V100 و A100.

يمكنك التحقق من نوع GPU الخاص بك من خلال ما يلي. يوجد الأمر فقط إذا تم تثبيت برامج تشغيل NVIDIA ، لذا فإن ما يلي سيؤدي إلى حدوث خطأ بخلاف ذلك.

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

تدعم جميع وحدات TPU السحابية bfloat16.

حتى في وحدات المعالجة المركزية ووحدات معالجة الرسومات الأقدم ، حيث لا يُتوقع حدوث تسريع ، لا يزال من الممكن استخدام واجهات برمجة التطبيقات ذات الدقة المختلطة لاختبار الوحدة أو تصحيح الأخطاء أو لمجرد تجربة واجهة برمجة التطبيقات. على وحدات المعالجة المركزية (CPU) ، ستعمل الدقة المختلطة بشكل أبطأ بشكل ملحوظ.

تحديد سياسة النوع

لاستخدام الدقة المختلطة في Keras ، تحتاج إلى إنشاء tf.keras.mixed_precision.Policy ، يشار إليها عادةً باسم سياسة dtype . تحدد سياسات Dtype الطبقات التي سيتم تشغيلها فيها. في هذا الدليل ، ستقوم بإنشاء سياسة من السلسلة '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_float16' mixed_precision.Policy مُنشئها). باستخدام هذه السياسة ، تستخدم الطبقات حسابات 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 مع قدرة حسابية لا تقل عن 7.0. سيتم تشغيل السياسة على وحدات معالجة الرسومات ووحدات المعالجة المركزية الأخرى ولكنها قد لا تؤدي إلى تحسين الأداء. بالنسبة إلى TPU ، يجب استخدام سياسة mixed_bfloat16 بدلاً من ذلك.

بناء النموذج

بعد ذلك ، لنبدأ في بناء نموذج بسيط. لا تستفيد نماذج الألعاب الصغيرة جدًا عادةً من الدقة المختلطة ، لأن الحمل الزائد من وقت تشغيل TensorFlow يهيمن عادةً على وقت التنفيذ ، مما يجعل أي تحسين في الأداء على وحدة معالجة الرسومات ضئيلاً. لذلك ، دعونا نبني طبقتين Dense كبيرتين مع 4096 وحدة لكل منهما في حالة استخدام وحدة معالجة الرسومات.

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

كل طبقة لها سياسة وتستخدم السياسة العامة بشكل افتراضي. وبالتالي ، فإن كل طبقة من الطبقات Dense لها سياسة mixed_float16 لأنك قمت بتعيين السياسة العامة على mixed_float16 سابقًا. سيؤدي هذا إلى قيام الطبقات الكثيفة بحسابات float16 ولها متغيرات float32. قاموا بإلقاء مدخلاتهم على float16 من أجل إجراء حسابات float16 ، مما يؤدي إلى تعويم مخرجاتهم نتيجة لذلك. المتغيرات الخاصة بهم هي float32 وسيتم تحويلها إلى float16 عندما يتم استدعاء الطبقات لتجنب الأخطاء من عدم تطابق النوع.

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. نظرًا لأن سياسة mixed_float16 ، فإن تنشيط softmax عادةً ما يكون له نوع dtype لحساب float16 وموترات إخراج 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 على متغيرات ، يتم تجاهل النوع المتغير الخاص بالسياسة ، ولكن نوع حساب السياسة الخاص بالسياسة لـ float32 يتسبب في أن يكون softmax وإخراج النموذج float32.

من الجيد إضافة float16 softmax في منتصف النموذج ، ولكن يجب أن يكون softmax في نهاية النموذج في float32. والسبب هو أنه إذا كان الموتر الوسيط المتدفق من softmax إلى الخسارة هو float16 أو bfloat16 ، فقد تحدث مشكلات رقمية.

يمكنك تجاوز نوع dtype لأي طبقة لتكون float32 من خلال تمرير dtype='float32' إذا كنت تعتقد أنها لن تكون مستقرة عدديًا باستخدام حسابات float16. ولكن عادةً ما يكون هذا ضروريًا فقط في الطبقة الأخيرة من النموذج ، حيث أن معظم الطبقات تتمتع بدقة كافية مع mixed_float16 mixed_bfloat16 .

حتى إذا لم ينتهي النموذج بـ softmax ، فيجب أن تظل المخرجات عائمة. في حين أنه غير ضروري لهذا النموذج المحدد ، يمكن تحويل مخرجات النموذج إلى 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 يتم إجراؤها على وحدة المعالجة المركزية ، والتي تقوم بتشغيل عمليات float16 أبطأ من عمليات float32. في هذه الحالة ، يكون الاختلاف في الأداء ضئيلًا ، ولكن بشكل عام يجب تشغيل معالجة الرياضيات في float32 إذا كان يعمل على وحدة المعالجة المركزية. ستعمل الطبقة الأولى من النموذج على تحويل المدخلات إلى 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

لاحظ أن النموذج يطبع الوقت لكل خطوة في السجلات: على سبيل المثال ، "25 مللي ثانية / خطوة". قد تكون الفترة الأولى أبطأ حيث يقضي TensorFlow بعض الوقت في تحسين النموذج ، ولكن بعد ذلك يجب أن يستقر الوقت لكل خطوة.

إذا كنت تقوم بتشغيل هذا الدليل في Colab ، فيمكنك مقارنة أداء الدقة المختلطة مع float32. للقيام بذلك ، قم بتغيير السياسة من mixed_float16 إلى float32 في قسم "إعداد سياسة نوع dtype" ، ثم أعد تشغيل جميع الخلايا حتى هذه النقطة. في وحدات معالجة الرسومات ذات القدرة الحسابية 7.X ، يجب أن ترى الوقت لكل خطوة يزداد بشكل ملحوظ ، مما يشير إلى أن الدقة المختلطة تسرع النموذج. تأكد من تغيير السياسة مرة أخرى إلى mixed_float16 وإعادة تشغيل الخلايا قبل متابعة الدليل.

في وحدات معالجة الرسومات ذات القدرة الحسابية على الأقل 8.0 (Ampere GPUs وما فوق) ، من المحتمل ألا ترى أي تحسن في الأداء في نموذج اللعبة في هذا الدليل عند استخدام الدقة المختلطة مقارنةً بـ float. ويرجع ذلك إلى استخدام TensorFloat-32 ، والذي يستخدم تلقائيًا رياضيات أقل دقة في بعض عمليات float32 مثل tf.linalg.matmul . يعطي TensorFloat-32 بعض مزايا أداء الدقة المختلطة عند استخدام float. ومع ذلك ، في نماذج العالم الحقيقي ، ستظل ترى عادةً تحسينات كبيرة في الأداء من الدقة المختلطة بسبب توفير عرض النطاق الترددي للذاكرة وعمليات التشغيل التي لا يدعمها TensorFloat-32.

إذا قمت بتشغيل دقة مختلطة على TPU ، فلن ترى قدرًا كبيرًا من مكاسب الأداء مقارنة بتشغيل الدقة المختلطة على وحدات معالجة الرسومات ، خاصة وحدات معالجة الرسومات السابقة Ampere. هذا لأن TPUs تقوم بعمليات معينة في bfloat16 تحت الغطاء حتى مع سياسة النوع الافتراضي لـ float32. هذا مشابه لكيفية استخدام وحدات معالجة الرسومات Ampere TensorFloat-32 افتراضيًا. مقارنةً بوحدات معالجة الرسوميات Ampere ، عادةً ما تشهد TPUs مكاسب أقل في الأداء مع دقة مختلطة في نماذج العالم الحقيقي.

بالنسبة للعديد من طرز العالم الحقيقي ، تتيح لك الدقة المختلطة أيضًا مضاعفة حجم الدُفعة دون نفاد الذاكرة ، حيث تأخذ موترات float16 نصف الذاكرة. ومع ذلك ، لا ينطبق هذا على نموذج اللعبة هذا ، حيث يمكنك على الأرجح تشغيل النموذج في أي نوع dtype حيث تتكون كل دفعة من مجموعة بيانات MNIST الكاملة المكونة من 60.000 صورة.

تحجيم الخسارة

مقياس الخسارة هو أسلوب يتم تنفيذه تلقائيًا بواسطة tf.keras.Model.fit مع سياسة mixed_float16 لتجنب التدفق الرقمي. يصف هذا القسم ما هو مقياس الخسارة ويصف القسم التالي كيفية استخدامه مع حلقة تدريب مخصصة.

تحت التدفق والفائض

نوع البيانات float16 له نطاق ديناميكي ضيق مقارنةً بـ float32. هذا يعني أن القيم التي تزيد عن \(65504\) سوف تتدفق إلى ما لا نهاية والقيم التي تقل عن \(6.0 \times 10^{-8}\) سوف تتدفق إلى الصفر. يحتوي كل من float32 و bfloat16 على نطاق ديناميكي أعلى بكثير بحيث لا يمثل الفائض والتدفق السفلي مشكلة.

فمثلا:

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

في الممارسة العملية ، نادرًا ما يحدث تجاوز مع float16. بالإضافة إلى ذلك ، نادرًا ما يحدث التدفق السفلي أثناء التمريرة الأمامية. ومع ذلك ، أثناء التمرير الخلفي ، يمكن أن تتدفق التدرجات إلى الصفر. مقياس الفقد هو تقنية لمنع هذا التدفق.

نظرة عامة على مقياس الخسارة

المفهوم الأساسي لتحجيم الخسارة بسيط: ببساطة اضرب الخسارة بعدد كبير ، لنقل \(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 إذا كنت تريد تخصيص سلوك قياس الخسارة.

بعد ذلك ، حدد كائن الخسارة و tf.data.Dataset s:

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 التدرجات اللونية إذا لم يكن أي منها يحتوي على Inf s أو NaN s. سيقوم أيضًا بتحديث مقياس الخسارة ، مما يؤدي إلى خفضه إلى النصف إذا كانت التدرجات تحتوي على Inf s أو NaN ومن المحتمل زيادتها بخلاف ذلك.

@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

نصائح حول أداء وحدة معالجة الرسومات

فيما يلي بعض نصائح الأداء عند استخدام الدقة المختلطة في وحدات معالجة الرسومات.

زيادة حجم الدفعة الخاصة بك

إذا لم يؤثر ذلك على جودة النموذج ، فحاول تشغيل مضاعفة حجم الدُفعة عند استخدام الدقة المختلطة. نظرًا لأن موترات float16 تستخدم نصف الذاكرة ، فإن هذا غالبًا ما يسمح لك بمضاعفة حجم الدفعة دون نفاد الذاكرة. عادةً ما تؤدي زيادة حجم الدُفعة إلى زيادة إنتاجية التدريب ، أي عناصر التدريب في الثانية التي يمكن للنموذج تشغيلها.

ضمان استخدام نوى GPU Tensor

كما ذكرنا سابقًا ، تستخدم وحدات معالجة الرسومات NVIDIA الحديثة وحدة أجهزة خاصة تسمى Tensor Cores يمكنها مضاعفة مصفوفات float16 بسرعة كبيرة. ومع ذلك ، تتطلب Tensor Cores أبعادًا معينة من الموترات لتكون مضاعفًا للعدد 8. في الأمثلة أدناه ، تكون الحجة غامقة إذا وفقط إذا كانت تحتاج إلى مضاعف 8 لكي يتم استخدام Tensor Cores.

  • tf.keras.layers.Dense ( وحدة = 64 )
  • tf.keras.layers.Conv2d ( المرشحات = 48 ، kernel_size = 7 ، الخطوة = 3)
    • وبالمثل بالنسبة للطبقات التلافيفية الأخرى ، مثل tf.keras.layers.Conv3d
  • tf.keras.layers.LSTM ( الوحدات = 64 )
    • ومماثلة لـ RNNs الأخرى ، مثل tf.keras.layers.GRU
  • tf.keras.Model.fit (فترات = 2 ، حجم_دفعة = 128 )

يجب أن تحاول استخدام Tensor Cores عندما يكون ذلك ممكنًا. إذا كنت تريد معرفة المزيد ، فإن دليل أداء التعلم العميق NVIDIA يصف المتطلبات الدقيقة لاستخدام Tensor Cores بالإضافة إلى معلومات الأداء الأخرى المتعلقة بـ Tensor Core.

XLA

XLA هو مترجم يمكنه زيادة أداء الدقة المختلطة ، بالإضافة إلى أداء float32 إلى حد أقل. راجع دليل XLA للحصول على التفاصيل.

نصائح أداء Cloud TPU

كما هو الحال مع وحدات معالجة الرسومات ، يجب أن تحاول مضاعفة حجم الدفعة عند استخدام Cloud TPUs لأن موتر bfloat16 يستخدم نصف الذاكرة. قد تؤدي مضاعفة حجم الدُفعة إلى زيادة إنتاجية التدريب.

لا تتطلب TPUs أي ضبط مختلط محدد الدقة للحصول على الأداء الأمثل. أنها تتطلب بالفعل استخدام XLA. تستفيد TPU من وجود أبعاد معينة باعتبارها مضاعفات \(128\)، ولكن هذا ينطبق بشكل متساوٍ على نوع float32 كما هو الحال مع الدقة المختلطة. تحقق من دليل أداء Cloud TPU للحصول على نصائح أداء TPU العامة ، والتي تنطبق على الدقة المختلطة بالإضافة إلى موترات float32.

ملخص

  • يجب عليك استخدام الدقة المختلطة إذا كنت تستخدم وحدات معالجة الرسومات (TPU) أو NVIDIA مع قدرة حسابية على الأقل 7.0 ، حيث ستعمل على تحسين الأداء بما يصل إلى 3x.
  • يمكنك استخدام الدقة المختلطة مع الأسطر التالية:

    # 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 لفك مقياس التدرجات.

  • ضاعف حجم دفعة التدريب إذا لم تقلل من دقة التقييم

  • في وحدات معالجة الرسومات ، تأكد من أن معظم أبعاد الموتر هي مضاعفات \(8\) لتحقيق أقصى قدر من الأداء

لمزيد من الأمثلة على الدقة المختلطة باستخدام tf.keras.mixed_precision API ، تحقق من مستودع النماذج الرسمي . سيتم تشغيل معظم الطرز الرسمية ، مثل ResNet و Transformer ، باستخدام دقة مختلطة عن طريق تمرير --dtype=fp16 .