ترجمت واجهة Cloud Translation API‏ هذه الصفحة.
Switch to English

تدريب مخصص مع tf.distribute.Strategy

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

يوضح هذا البرنامج التعليمي كيفية استخدام tf.distribute.Strategy مع حلقات تدريب مخصصة. سنقوم بتدريب نموذج بسيط لشبكة CNN على مجموعة بيانات الموضة MNIST. تحتوي مجموعة بيانات Fashion MNIST على 60000 صورة قطار بحجم 28 × 28 و 10000 صورة اختبار بحجم 28 × 28.

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

# Import TensorFlow
import tensorflow as tf

# Helper libraries
import numpy as np
import os

print(tf.__version__)
2.3.0

قم بتنزيل مجموعة بيانات الموضة MNIST

fashion_mnist = tf.keras.datasets.fashion_mnist

(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()

# Adding a dimension to the array -> new shape == (28, 28, 1)
# We are doing this because the first layer in our model is a convolutional
# layer and it requires a 4D input (batch_size, height, width, channels).
# batch_size dimension will be added later on.
train_images = train_images[..., None]
test_images = test_images[..., None]

# Getting the images in [0, 1] range.
train_images = train_images / np.float32(255)
test_images = test_images / np.float32(255)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
32768/29515 [=================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
26427392/26421880 [==============================] - 1s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
8192/5148 [===============================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
4423680/4422102 [==============================] - 1s 0us/step

إنشاء استراتيجية لتوزيع المتغيرات والرسم البياني

كيف تعمل استراتيجية tf.distribute.MirroredStrategy ؟

  • يتم تكرار جميع المتغيرات والرسم البياني للنموذج على النسخ المتماثلة.
  • يتم توزيع المدخلات بالتساوي عبر النسخ المتماثلة.
  • تحسب كل نسخة متماثلة الخسارة والتدرجات للإدخال الذي تلقته.
  • تتم مزامنة التدرجات عبر جميع النسخ المتماثلة عن طريق جمعها.
  • بعد المزامنة ، يتم إجراء نفس التحديث على نسخ المتغيرات في كل نسخة متماثلة.
# If the list of devices is not specified in the
# `tf.distribute.MirroredStrategy` constructor, it will be auto-detected.
strategy = tf.distribute.MirroredStrategy()
INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:GPU:0',)

print ('Number of devices: {}'.format(strategy.num_replicas_in_sync))
Number of devices: 1

إعداد خط أنابيب الإدخال

تصدير الرسم البياني والمتغيرات إلى تنسيق SavedModel الحيادي للنظام الأساسي. بعد حفظ النموذج الخاص بك ، يمكنك تحميله بالنطاق أو بدونه.

BUFFER_SIZE = len(train_images)

BATCH_SIZE_PER_REPLICA = 64
GLOBAL_BATCH_SIZE = BATCH_SIZE_PER_REPLICA * strategy.num_replicas_in_sync

EPOCHS = 10

قم بإنشاء مجموعات البيانات وتوزيعها:

train_dataset = tf.data.Dataset.from_tensor_slices((train_images, train_labels)).shuffle(BUFFER_SIZE).batch(GLOBAL_BATCH_SIZE) 
test_dataset = tf.data.Dataset.from_tensor_slices((test_images, test_labels)).batch(GLOBAL_BATCH_SIZE) 

train_dist_dataset = strategy.experimental_distribute_dataset(train_dataset)
test_dist_dataset = strategy.experimental_distribute_dataset(test_dataset)

قم بإنشاء النموذج

قم بإنشاء نموذج باستخدام tf.keras.Sequential . يمكنك أيضًا استخدام Model Subclassing API للقيام بذلك.

def create_model():
  model = tf.keras.Sequential([
      tf.keras.layers.Conv2D(32, 3, activation='relu'),
      tf.keras.layers.MaxPooling2D(),
      tf.keras.layers.Conv2D(64, 3, activation='relu'),
      tf.keras.layers.MaxPooling2D(),
      tf.keras.layers.Flatten(),
      tf.keras.layers.Dense(64, activation='relu'),
      tf.keras.layers.Dense(10)
    ])

  return model
# Create a checkpoint directory to store the checkpoints.
checkpoint_dir = './training_checkpoints'
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")

حدد وظيفة الخسارة

عادةً ، في جهاز واحد مع 1 GPU / CPU ، يتم تقسيم الخسارة على عدد الأمثلة في دفعة الإدخال.

لذا ، كيف يجب حساب الخسارة عند استخدام tf.distribute.Strategy ؟

  • على سبيل المثال ، لنفترض أن لديك 4 وحدات معالجة رسومات وحجم دُفعة 64. تم توزيع دفعة واحدة من الإدخال عبر النسخ المتماثلة (4 وحدات معالجة رسومات) ، تحصل كل نسخة متماثلة على إدخال بحجم 16.

  • يقوم النموذج الموجود في كل نسخة متماثلة بتمرير للأمام مع المدخلات الخاصة به ويحسب الخسارة. الآن ، بدلاً من قسمة الخسارة على عدد الأمثلة في المدخلات الخاصة بها (BATCH_SIZE_PER_REPLICA = 16) ، يجب تقسيم الخسارة على GLOBAL_BATCH_SIZE (64).

لماذا فعل هذا؟

  • يجب القيام بذلك لأنه بعد حساب التدرجات اللونية في كل نسخة متماثلة ، تتم مزامنتها عبر النسخ المتماثلة عن طريق جمعها .

كيف يتم القيام بذلك في TensorFlow؟

  • إذا كنت تكتب حلقة تدريب مخصصة ، كما في هذا البرنامج التعليمي ، فيجب عليك جمع الخسائر لكل مثال وتقسيم المبلغ على GLOBAL_BATCH_SIZE: scale_loss = tf.reduce_sum(loss) * (1. / GLOBAL_BATCH_SIZE) أو يمكنك استخدام tf.nn.compute_average_loss الذي يأخذ الخسارة لكل مثال وأوزان العينة الاختيارية و GLOBAL_BATCH_SIZE كوسيطات ويعيد الخسارة المقاسة.

  • إذا كنت تستخدم خسائر التنظيم في نموذجك ، فأنت بحاجة إلى قياس قيمة الخسارة حسب عدد النسخ المتماثلة. يمكنك القيام بذلك باستخدام وظيفة tf.nn.scale_regularization_loss .

  • لا يوصى باستخدام tf.reduce_mean . يؤدي القيام بذلك إلى تقسيم الخسارة على الحجم الفعلي لكل نسخة متماثلة والذي قد يختلف من خطوة إلى أخرى.

  • يتم إجراء هذا التخفيض والتحجيم تلقائيًا في model.compile و model.fit

  • في حالة استخدام فئاتtf.keras.losses (كما في المثال أدناه) ، يجب تحديد تقليل الخسارة بشكل صريح ليكون واحدًا من NONE أو SUM . AUTO و SUM_OVER_BATCH_SIZE غير مسموح SUM_OVER_BATCH_SIZE عند الاستخدام مع tf.distribute.Strategy . لا يُسمح بـ AUTO لأن المستخدم يجب أن يفكر صراحة في التخفيض الذي يريده للتأكد من صحته في الحالة الموزعة. SUM_OVER_BATCH_SIZE غير مسموح به لأنه لن يقسم حاليًا إلا على حجم دفعة النسخة المتماثلة ، ويترك القسمة على عدد النسخ المتماثلة للمستخدم ، وهو الأمر الذي قد يكون من السهل تفويته. لذا بدلاً من ذلك ، نطلب من المستخدم إجراء التخفيض بنفسه صراحةً.

  • إذا كانت labels متعددة الأبعاد ، per_example_loss عبر عدد العناصر في كل عينة. على سبيل المثال ، إذا كان شكل predictions هو (batch_size, H, W, n_classes) وكانت labels (batch_size, H, W) ، فستحتاج إلى تحديث per_example_loss مثل: per_example_loss /= tf.cast(tf.reduce_prod(tf.shape(labels)[1:]), tf.float32)

with strategy.scope():
  # Set reduction to `none` so we can do the reduction afterwards and divide by
  # global batch size.
  loss_object = tf.keras.losses.SparseCategoricalCrossentropy(
      from_logits=True,
      reduction=tf.keras.losses.Reduction.NONE)
  def compute_loss(labels, predictions):
    per_example_loss = loss_object(labels, predictions)
    return tf.nn.compute_average_loss(per_example_loss, global_batch_size=GLOBAL_BATCH_SIZE)

حدد المقاييس لتتبع الخسارة والدقة

تتعقب هذه المقاييس فقدان الاختبار والتدريب ودقة الاختبار. يمكنك استخدام .result() للحصول على الإحصائيات المتراكمة في أي وقت.

with strategy.scope():
  test_loss = tf.keras.metrics.Mean(name='test_loss')

  train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(
      name='train_accuracy')
  test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(
      name='test_accuracy')

حلقة التدريب

# model, optimizer, and checkpoint must be created under `strategy.scope`.
with strategy.scope():
  model = create_model()

  optimizer = tf.keras.optimizers.Adam()

  checkpoint = tf.train.Checkpoint(optimizer=optimizer, model=model)
def train_step(inputs):
  images, labels = inputs

  with tf.GradientTape() as tape:
    predictions = model(images, training=True)
    loss = compute_loss(labels, predictions)

  gradients = tape.gradient(loss, model.trainable_variables)
  optimizer.apply_gradients(zip(gradients, model.trainable_variables))

  train_accuracy.update_state(labels, predictions)
  return loss 

def test_step(inputs):
  images, labels = inputs

  predictions = model(images, training=False)
  t_loss = loss_object(labels, predictions)

  test_loss.update_state(t_loss)
  test_accuracy.update_state(labels, predictions)
# `run` replicates the provided computation and runs it
# with the distributed input.
@tf.function
def distributed_train_step(dataset_inputs):
  per_replica_losses = strategy.run(train_step, args=(dataset_inputs,))
  return strategy.reduce(tf.distribute.ReduceOp.SUM, per_replica_losses,
                         axis=None)

@tf.function
def distributed_test_step(dataset_inputs):
  return strategy.run(test_step, args=(dataset_inputs,))

for epoch in range(EPOCHS):
  # TRAIN LOOP
  total_loss = 0.0
  num_batches = 0
  for x in train_dist_dataset:
    total_loss += distributed_train_step(x)
    num_batches += 1
  train_loss = total_loss / num_batches

  # TEST LOOP
  for x in test_dist_dataset:
    distributed_test_step(x)

  if epoch % 2 == 0:
    checkpoint.save(checkpoint_prefix)

  template = ("Epoch {}, Loss: {}, Accuracy: {}, Test Loss: {}, "
              "Test Accuracy: {}")
  print (template.format(epoch+1, train_loss,
                         train_accuracy.result()*100, test_loss.result(),
                         test_accuracy.result()*100))

  test_loss.reset_states()
  train_accuracy.reset_states()
  test_accuracy.reset_states()
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/data/ops/multi_device_iterator_ops.py:601: get_next_as_optional (from tensorflow.python.data.ops.iterator_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.data.Iterator.get_next_as_optional()` instead.
INFO:tensorflow:Reduce to /job:localhost/replica:0/task:0/device:CPU:0 then broadcast to ('/job:localhost/replica:0/task:0/device:CPU:0',).
INFO:tensorflow:Reduce to /job:localhost/replica:0/task:0/device:CPU:0 then broadcast to ('/job:localhost/replica:0/task:0/device:CPU:0',).
INFO:tensorflow:Reduce to /job:localhost/replica:0/task:0/device:CPU:0 then broadcast to ('/job:localhost/replica:0/task:0/device:CPU:0',).
INFO:tensorflow:Reduce to /job:localhost/replica:0/task:0/device:CPU:0 then broadcast to ('/job:localhost/replica:0/task:0/device:CPU:0',).
INFO:tensorflow:Reduce to /job:localhost/replica:0/task:0/device:CPU:0 then broadcast to ('/job:localhost/replica:0/task:0/device:CPU:0',).
INFO:tensorflow:Reduce to /job:localhost/replica:0/task:0/device:CPU:0 then broadcast to ('/job:localhost/replica:0/task:0/device:CPU:0',).
Epoch 1, Loss: 0.50295090675354, Accuracy: 82.1116714477539, Test Loss: 0.3852590322494507, Test Accuracy: 86.5999984741211
INFO:tensorflow:Reduce to /job:localhost/replica:0/task:0/device:CPU:0 then broadcast to ('/job:localhost/replica:0/task:0/device:CPU:0',).
INFO:tensorflow:Reduce to /job:localhost/replica:0/task:0/device:CPU:0 then broadcast to ('/job:localhost/replica:0/task:0/device:CPU:0',).
INFO:tensorflow:Reduce to /job:localhost/replica:0/task:0/device:CPU:0 then broadcast to ('/job:localhost/replica:0/task:0/device:CPU:0',).
INFO:tensorflow:Reduce to /job:localhost/replica:0/task:0/device:CPU:0 then broadcast to ('/job:localhost/replica:0/task:0/device:CPU:0',).
Epoch 2, Loss: 0.32958829402923584, Accuracy: 88.20333862304688, Test Loss: 0.3391425311565399, Test Accuracy: 87.6500015258789
Epoch 3, Loss: 0.2872008979320526, Accuracy: 89.57167053222656, Test Loss: 0.2974696457386017, Test Accuracy: 89.31000518798828
Epoch 4, Loss: 0.255713552236557, Accuracy: 90.58499908447266, Test Loss: 0.2988712787628174, Test Accuracy: 89.31999969482422
Epoch 5, Loss: 0.23122134804725647, Accuracy: 91.41667175292969, Test Loss: 0.27742496132850647, Test Accuracy: 89.99000549316406
Epoch 6, Loss: 0.212575763463974, Accuracy: 92.17333221435547, Test Loss: 0.2573488652706146, Test Accuracy: 90.75
Epoch 7, Loss: 0.1963273137807846, Accuracy: 92.77166748046875, Test Loss: 0.2587501108646393, Test Accuracy: 90.66000366210938
Epoch 8, Loss: 0.1779220998287201, Accuracy: 93.46666717529297, Test Loss: 0.267805814743042, Test Accuracy: 90.55999755859375
Epoch 9, Loss: 0.16410504281520844, Accuracy: 93.91333770751953, Test Loss: 0.25632956624031067, Test Accuracy: 91.00999450683594
Epoch 10, Loss: 0.14829590916633606, Accuracy: 94.47833251953125, Test Loss: 0.25820475816726685, Test Accuracy: 91.00999450683594

أشياء يجب ملاحظتها في المثال أعلاه:

  • نحن train_dist_dataset على مجموعة بيانات train_dist_dataset و test_dist_dataset باستخدام a for x in ... test_dist_dataset .
  • الخسارة المقاسة هي القيمة المرجعة للخطوة distributed_train_step . يتم تجميع هذه القيمة عبر النسخ المتماثلة باستخدام الاستدعاء tf.distribute.Strategy.reduce ثم عبر الدُفعات عن طريق جمع قيمة tf.distribute.Strategy.reduce للمكالمات tf.distribute.Strategy.reduce .
  • tf.keras.Metrics ينبغي تحديث داخل train_step و test_step أن يعدم قبل tf.distribute.Strategy.run . * يُرجع tf.distribute.Strategy.run النتائج من كل نسخة متماثلة محلية في الإستراتيجية ، وهناك طرق متعددة لاستهلاك هذه النتيجة. يمكنك عمل tf.distribute.Strategy.reduce للحصول على قيمة مجمعة. يمكنك أيضًا إجراء tf.distribute.Strategy.experimental_local_results للحصول على قائمة القيم الموجودة في النتيجة ، واحدة لكل نسخة محلية.

قم باستعادة أحدث نقطة تفتيش واختبار

نموذج تم tf.distribute.Strategy باستخدام tf.distribute.Strategy يمكن استعادة الإستراتيجية مع أو بدون إستراتيجية.

eval_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(
      name='eval_accuracy')

new_model = create_model()
new_optimizer = tf.keras.optimizers.Adam()

test_dataset = tf.data.Dataset.from_tensor_slices((test_images, test_labels)).batch(GLOBAL_BATCH_SIZE)
@tf.function
def eval_step(images, labels):
  predictions = new_model(images, training=False)
  eval_accuracy(labels, predictions)
checkpoint = tf.train.Checkpoint(optimizer=new_optimizer, model=new_model)
checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))

for images, labels in test_dataset:
  eval_step(images, labels)

print ('Accuracy after restoring the saved model without strategy: {}'.format(
    eval_accuracy.result()*100))
Accuracy after restoring the saved model without strategy: 91.00999450683594

طرق بديلة للتكرار على مجموعة بيانات

باستخدام التكرارات

إذا كنت ترغب في تكرار عدد معين من الخطوات وليس من خلال مجموعة البيانات بأكملها ، يمكنك إنشاء مكرر باستخدام استدعاء iter واستدعاء الوضوح next في المكرر. يمكنك اختيار التكرار على مجموعة البيانات داخل وخارج وظيفة tf. إليك مقتطف صغير يوضح تكرار مجموعة البيانات خارج دالة tf باستخدام مكرر.

for _ in range(EPOCHS):
  total_loss = 0.0
  num_batches = 0
  train_iter = iter(train_dist_dataset)

  for _ in range(10):
    total_loss += distributed_train_step(next(train_iter))
    num_batches += 1
  average_train_loss = total_loss / num_batches

  template = ("Epoch {}, Loss: {}, Accuracy: {}")
  print (template.format(epoch+1, average_train_loss, train_accuracy.result()*100))
  train_accuracy.reset_states()
Epoch 10, Loss: 0.12157603353261948, Accuracy: 95.0
Epoch 10, Loss: 0.1367541253566742, Accuracy: 94.6875
Epoch 10, Loss: 0.14902949333190918, Accuracy: 93.90625
Epoch 10, Loss: 0.12149540334939957, Accuracy: 95.625
Epoch 10, Loss: 0.13160167634487152, Accuracy: 94.6875
Epoch 10, Loss: 0.13297739624977112, Accuracy: 95.3125
Epoch 10, Loss: 0.16038034856319427, Accuracy: 94.53125
Epoch 10, Loss: 0.1035340279340744, Accuracy: 96.40625
Epoch 10, Loss: 0.11846740543842316, Accuracy: 95.625
Epoch 10, Loss: 0.09006750583648682, Accuracy: 96.71875

التكرار داخل دالة tf

يمكنك أيضًا التكرار عبر مجموعة train_dist_dataset الإدخال بأكملها داخل دالة tf باستخدام for x in ... build أو عن طريق إنشاء مكررات كما فعلنا أعلاه. يوضح المثال أدناه التفاف حقبة واحدة من التدريب في دالة tf والتكرار عبر مجموعة train_dist_dataset داخل الوظيفة.

@tf.function
def distributed_train_epoch(dataset):
  total_loss = 0.0
  num_batches = 0
  for x in dataset:
    per_replica_losses = strategy.run(train_step, args=(x,))
    total_loss += strategy.reduce(
      tf.distribute.ReduceOp.SUM, per_replica_losses, axis=None)
    num_batches += 1
  return total_loss / tf.cast(num_batches, dtype=tf.float32)

for epoch in range(EPOCHS):
  train_loss = distributed_train_epoch(train_dist_dataset)

  template = ("Epoch {}, Loss: {}, Accuracy: {}")
  print (template.format(epoch+1, train_loss, train_accuracy.result()*100))

  train_accuracy.reset_states()
Epoch 1, Loss: 0.13680464029312134, Accuracy: 94.90499877929688
Epoch 2, Loss: 0.12503673136234283, Accuracy: 95.33499908447266
Epoch 3, Loss: 0.11472766101360321, Accuracy: 95.71333312988281
Epoch 4, Loss: 0.10419528931379318, Accuracy: 96.13500213623047
Epoch 5, Loss: 0.09566374123096466, Accuracy: 96.44833374023438
Epoch 6, Loss: 0.08704081922769547, Accuracy: 96.82499694824219
Epoch 7, Loss: 0.08157625794410706, Accuracy: 96.96333312988281
Epoch 8, Loss: 0.07562965154647827, Accuracy: 97.11000061035156
Epoch 9, Loss: 0.0676642507314682, Accuracy: 97.47999572753906
Epoch 10, Loss: 0.06430575996637344, Accuracy: 97.58333587646484

تتبع فقدان التدريب عبر النسخ المتماثلة

لا نوصي باستخدام tf.metrics.Mean لتتبع فقدان التدريب عبر النسخ المتماثلة المختلفة ، بسبب حساب قياس الخسارة الذي يتم تنفيذه.

على سبيل المثال ، إذا كنت تدير وظيفة تدريبية بالخصائص التالية:

  • نسختان متماثلتان
  • تتم معالجة عينتين على كل نسخة طبق الأصل
  • قيم الخسارة الناتجة: [2 ، 3] و [4 ، 5] على كل نسخة متماثلة
  • حجم الدفعة العالمية = 4

باستخدام مقياس الفقد ، يمكنك حساب قيمة الخسارة لكل عينة في كل نسخة متماثلة عن طريق إضافة قيم الخسارة ، ثم القسمة على حجم الدُفعة العام. في هذه الحالة: (2 + 3) / 4 = 1.25 و (4 + 5) / 4 = 2.25 .

إذا كنت تستخدم tf.metrics.Mean لتعقب الخسارة عبر نسختين متماثلتين ، فإن النتيجة مختلفة. في هذا المثال ، ينتهي بك الأمر total 3.50 count 2 ، مما ينتج عنه total / count = 1.75 عندما يتم استدعاء result() في المقياس. يتم احتساب الخسارة باستخدام tf.keras.Metrics بواسطة عامل إضافي يساوي عدد النسخ المتماثلة المتزامنة.

دليل وأمثلة

فيما يلي بعض الأمثلة لاستخدام إستراتيجية التوزيع مع حلقات تدريب مخصصة:

  1. دليل التدريب الموزع
  2. مثال على DenseNet باستخدام MirroredStrategy .
  3. تم تدريب مثال BERT باستخدام MirroredStrategy و TPUStrategy . هذا المثال مفيد بشكل خاص لفهم كيفية التحميل من نقطة تفتيش وإنشاء نقاط تفتيش دورية أثناء التدريب الموزع وما إلى ذلك.
  4. مثال NCF تم تدريبه باستخدام MirroredStrategy يمكن تمكينه باستخدام علامة keras_use_ctl .
  5. تم تدريب مثال NMT باستخدام MirroredStrategy .

المزيد من الأمثلة المدرجة في دليل استراتيجية التوزيع .

الخطوات التالية