تصحيح خطأ TF2 خط أنابيب التدريب المُرحل

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

يوضح هذا الكمبيوتر الدفتري كيفية تصحيح أخطاء خط أنابيب التدريب عند الترحيل إلى TF2. يتكون من المكونات التالية:

  1. الخطوات المقترحة وعينات التعليمات البرمجية لتصحيح أخطاء خط أنابيب التدريب
  2. أدوات التصحيح
  3. الموارد الأخرى ذات الصلة

أحد الافتراضات هو أن لديك رمز TF1.x ونماذج مدربة للمقارنة ، وتريد إنشاء نموذج TF2 يحقق دقة تحقق مماثلة.

لا يغطي هذا الكمبيوتر الدفتري مشكلات تصحيح الأخطاء المتعلقة بسرعة التدريب / الاستدلال أو استخدام الذاكرة.

تصحيح سير العمل

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

  1. إصلاح أخطاء الترجمة ووقت التشغيل

  2. التحقق من صحة المرور إلى الأمام (في دليل منفصل)

    أ. على جهاز CPU واحد

    • تحقق من أن المتغيرات تم إنشاؤها مرة واحدة فقط
    • تحقق من تطابق الأعداد والأسماء والأشكال المتغيرة
    • إعادة تعيين جميع المتغيرات ، والتحقق من التكافؤ العددي مع تعطيل جميع العشوائية
    • قم بمحاذاة توليد الأرقام العشوائية ، وتحقق من التكافؤ العددي في الاستدلال
    • (اختياري) يتم تحميل نقاط الفحص بشكل صحيح وتولد نماذج TF1.x / TF2 مخرجات تعريفية

    ب. على جهاز GPU / TPU واحد

    ج. مع استراتيجيات متعددة الأجهزة

  3. نموذج التحقق من صحة التكافؤ العددي التدريبي لبضع خطوات (تتوفر عينات التعليمات البرمجية أدناه)

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

    • حساب الخسائر
    • المقاييس
    • معدل التعليم
    • حساب التدرج والتحديث

    ب. تحقق من الإحصائيات بعد التدريب 3 خطوات أو أكثر للتحقق من سلوكيات المحسن مثل القوة الدافعة ، مع استمرار البيانات الثابتة على جهاز وحدة المعالجة المركزية الواحدة

    ج. على جهاز GPU / TPU واحد

    د. باستخدام استراتيجيات الأجهزة المتعددة (تحقق من مقدمة MultiProcessRunner في الجزء السفلي)

  4. اختبار الغلاف الشامل على مجموعة بيانات حقيقية

    أ. تحقق من سلوكيات التدريب باستخدام TensorBoard

    • استخدم أدوات تحسين بسيطة مثل SGD واستراتيجيات التوزيع البسيطة مثل tf.distribute.OneDeviceStrategy أولاً
    • مقاييس التدريب
    • مقاييس التقييم
    • اكتشف ما هو التسامح المعقول للعشوائية المتأصلة

    ب. تحقق من التكافؤ مع المحسن المتقدم / جدولة معدل التعلم / استراتيجيات التوزيع

    ج. تحقق من التكافؤ عند استخدام الدقة المختلطة

  5. معايير المنتج الإضافية

يثبت

pip uninstall -y -q tensorflow
# Install tf-nightly as the DeterministicRandomTestTool is only available in
# Tensorflow 2.8
pip install -q tf-nightly

التحقق من صحة تمريرة واحدة للأمام

يتم تغطية التحقق من صحة المرور إلى الأمام الفردي ، بما في ذلك تحميل نقطة التفتيش ، في colab مختلفة.

import sys
import unittest
import numpy as np

import tensorflow as tf
import tensorflow.compat.v1 as v1

نموذج التحقق من صحة التكافؤ العددي للتدريب لبضع خطوات

قم بإعداد تكوين النموذج وإعداد مجموعة بيانات وهمية.

params = {
    'input_size': 3,
    'num_classes': 3,
    'layer_1_size': 2,
    'layer_2_size': 2,
    'num_train_steps': 100,
    'init_lr': 1e-3,
    'end_lr': 0.0,
    'decay_steps': 1000,
    'lr_power': 1.0,
}

# make a small fixed dataset
fake_x = np.ones((2, params['input_size']), dtype=np.float32)
fake_y = np.zeros((2, params['num_classes']), dtype=np.int32)
fake_y[0][0] = 1
fake_y[1][1] = 1

step_num = 3

حدد نموذج TF1.x.

# Assume there is an existing TF1.x model using estimator API
# Wrap the model_fn to log necessary tensors for result comparison
class SimpleModelWrapper():
  def __init__(self):
    self.logged_ops = {}
    self.logs = {
        'step': [],
        'lr': [],
        'loss': [],
        'grads_and_vars': [],
        'layer_out': []}

  def model_fn(self, features, labels, mode, params):
      out_1 = tf.compat.v1.layers.dense(features, units=params['layer_1_size'])
      out_2 = tf.compat.v1.layers.dense(out_1, units=params['layer_2_size'])
      logits = tf.compat.v1.layers.dense(out_2, units=params['num_classes'])
      loss = tf.compat.v1.losses.softmax_cross_entropy(labels, logits)

      # skip EstimatorSpec details for prediction and evaluation 
      if mode == tf.estimator.ModeKeys.PREDICT:
          pass
      if mode == tf.estimator.ModeKeys.EVAL:
          pass
      assert mode == tf.estimator.ModeKeys.TRAIN

      global_step = tf.compat.v1.train.get_or_create_global_step()
      lr = tf.compat.v1.train.polynomial_decay(
        learning_rate=params['init_lr'],
        global_step=global_step,
        decay_steps=params['decay_steps'],
        end_learning_rate=params['end_lr'],
        power=params['lr_power'])

      optmizer = tf.compat.v1.train.GradientDescentOptimizer(lr)
      grads_and_vars = optmizer.compute_gradients(
          loss=loss,
          var_list=graph.get_collection(
              tf.compat.v1.GraphKeys.TRAINABLE_VARIABLES))
      train_op = optmizer.apply_gradients(
          grads_and_vars,
          global_step=global_step)

      # log tensors
      self.logged_ops['step'] = global_step
      self.logged_ops['lr'] = lr
      self.logged_ops['loss'] = loss
      self.logged_ops['grads_and_vars'] = grads_and_vars
      self.logged_ops['layer_out'] = {
          'layer_1': out_1,
          'layer_2': out_2,
          'logits': logits}

      return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)

  def update_logs(self, logs):
    for key in logs.keys():
      model_tf1.logs[key].append(logs[key])

توفر فئة v1.keras.utils.DeterministicRandomTestTool التالية scope() الذي يمكن أن يجعل العمليات العشوائية ذات الحالة تستخدم نفس المصدر عبر كل من الرسوم البيانية / الجلسات TF1 والتنفيذ الحثيث ،

توفر الأداة وضعين للاختبار:

  1. constant يستخدم نفس البذور لكل عملية بغض النظر عن عدد المرات التي تم استدعاؤها و ،
  2. num_random_ops الذي يستخدم عدد العمليات العشوائية ذات الحالة التي تمت ملاحظتها سابقًا كبداية للعملية.

ينطبق هذا على كل من العمليات العشوائية ذات الحالة المستخدمة لإنشاء المتغيرات وتهيئتها ، والعمليات العشوائية ذات الحالة المستخدمة في الحساب (مثل طبقات التسرب).

random_tool = v1.keras.utils.DeterministicRandomTestTool(mode='num_random_ops')
WARNING:tensorflow:From /tmp/ipykernel_26769/2689227634.py:1: The name tf.keras.utils.DeterministicRandomTestTool is deprecated. Please use tf.compat.v1.keras.utils.DeterministicRandomTestTool instead.

قم بتشغيل نموذج TF1.x في وضع الرسم البياني. اجمع الإحصائيات لخطوات التدريب الثلاث الأولى لمقارنة التكافؤ العددي.

with random_tool.scope():
  graph = tf.Graph()
  with graph.as_default(), tf.compat.v1.Session(graph=graph) as sess:
    model_tf1 = SimpleModelWrapper()
    # build the model
    inputs = tf.compat.v1.placeholder(tf.float32, shape=(None, params['input_size']))
    labels = tf.compat.v1.placeholder(tf.float32, shape=(None, params['num_classes']))
    spec = model_tf1.model_fn(inputs, labels, tf.estimator.ModeKeys.TRAIN, params)
    train_op = spec.train_op

    sess.run(tf.compat.v1.global_variables_initializer())
    for step in range(step_num):
      # log everything and update the model for one step
      logs, _ = sess.run(
          [model_tf1.logged_ops, train_op],
          feed_dict={inputs: fake_x, labels: fake_y})
      model_tf1.update_logs(logs)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:14: UserWarning: `tf.layers.dense` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Dense` instead.
  
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/legacy_tf_layers/core.py:261: UserWarning: `layer.apply` is deprecated and will be removed in a future version. Please use `layer.__call__` method instead.
  return layer.apply(inputs)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:15: UserWarning: `tf.layers.dense` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Dense` instead.
  from ipykernel import kernelapp as app
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:16: UserWarning: `tf.layers.dense` is deprecated and will be removed in a future version. Please use `tf.keras.layers.Dense` instead.
  app.launch_new_instance()

حدد نموذج TF2.

class SimpleModel(tf.keras.Model):
  def __init__(self, params, *args, **kwargs):
    super(SimpleModel, self).__init__(*args, **kwargs)
    # define the model
    self.dense_1 = tf.keras.layers.Dense(params['layer_1_size'])
    self.dense_2 = tf.keras.layers.Dense(params['layer_2_size'])
    self.out = tf.keras.layers.Dense(params['num_classes'])
    learning_rate_fn = tf.keras.optimizers.schedules.PolynomialDecay(
      initial_learning_rate=params['init_lr'],
      decay_steps=params['decay_steps'],
      end_learning_rate=params['end_lr'],
      power=params['lr_power'])  
    self.optimizer = tf.keras.optimizers.SGD(learning_rate_fn)
    self.compiled_loss = tf.keras.losses.CategoricalCrossentropy(from_logits=True)
    self.logs = {
        'lr': [],
        'loss': [],
        'grads': [],
        'weights': [],
        'layer_out': []}

  def call(self, inputs):
    out_1 = self.dense_1(inputs)
    out_2 = self.dense_2(out_1)
    logits = self.out(out_2)
    # log output features for every layer for comparison
    layer_wise_out = {
        'layer_1': out_1,
        'layer_2': out_2,
        'logits': logits}
    self.logs['layer_out'].append(layer_wise_out)
    return logits

  def train_step(self, data):
    x, y = data
    with tf.GradientTape() as tape:
      logits = self(x)
      loss = self.compiled_loss(y, logits)
    grads = tape.gradient(loss, self.trainable_weights)
    # log training statistics
    step = self.optimizer.iterations.numpy()
    self.logs['lr'].append(self.optimizer.learning_rate(step).numpy())
    self.logs['loss'].append(loss.numpy())
    self.logs['grads'].append(grads)
    self.logs['weights'].append(self.trainable_weights)
    # update model
    self.optimizer.apply_gradients(zip(grads, self.trainable_weights))
    return

قم بتشغيل نموذج TF2 في الوضع الحثيث. اجمع الإحصائيات لخطوات التدريب الثلاث الأولى لمقارنة التكافؤ العددي.

random_tool = v1.keras.utils.DeterministicRandomTestTool(mode='num_random_ops')
with random_tool.scope():
  model_tf2 = SimpleModel(params)
  for step in range(step_num):
    model_tf2.train_step([fake_x, fake_y])

قارن التكافؤ العددي لخطوات التدريب القليلة الأولى.

يمكنك أيضًا التحقق من صحة دفتر التحقق من الصحة والتكافؤ العددي للحصول على مشورة إضافية بشأن التكافؤ العددي.

np.testing.assert_allclose(model_tf1.logs['lr'], model_tf2.logs['lr'])
np.testing.assert_allclose(model_tf1.logs['loss'], model_tf2.logs['loss'])
for step in range(step_num):
  for name in model_tf1.logs['layer_out'][step]:
    np.testing.assert_allclose(
        model_tf1.logs['layer_out'][step][name],
        model_tf2.logs['layer_out'][step][name])

اختبارات الوحدة

هناك أنواع قليلة من اختبار الوحدة التي يمكن أن تساعد في تصحيح رمز الترحيل الخاص بك.

  1. التحقق من صحة تمريرة واحدة للأمام
  2. نموذج التحقق من صحة التكافؤ العددي للتدريب لبضع خطوات
  3. أداء الاستدلال المعياري
  4. يقوم النموذج المدرب بعمل تنبؤات صحيحة على نقاط البيانات الثابتة والبسيطة

يمكنك استخدام @parameterized.parameters لاختبار النماذج ذات التكوينات المختلفة. التفاصيل مع نموذج التعليمات البرمجية .

لاحظ أنه من الممكن تشغيل واجهات برمجة التطبيقات للجلسة والتنفيذ الحثيث في نفس حالة الاختبار. توضح مقتطفات التعليمات البرمجية أدناه كيف.

import unittest

class TestNumericalEquivalence(unittest.TestCase):

  # copied from code samples above
  def setup(self):
    # record statistics for 100 training steps
    step_num = 100

    # setup TF 1 model
    random_tool = v1.keras.utils.DeterministicRandomTestTool(mode='num_random_ops')
    with random_tool.scope():
      # run TF1.x code in graph mode with context management
      graph = tf.Graph()
      with graph.as_default(), tf.compat.v1.Session(graph=graph) as sess:
        self.model_tf1 = SimpleModelWrapper()
        # build the model
        inputs = tf.compat.v1.placeholder(tf.float32, shape=(None, params['input_size']))
        labels = tf.compat.v1.placeholder(tf.float32, shape=(None, params['num_classes']))
        spec = self.model_tf1.model_fn(inputs, labels, tf.estimator.ModeKeys.TRAIN, params)
        train_op = spec.train_op

        sess.run(tf.compat.v1.global_variables_initializer())
        for step in range(step_num):
          # log everything and update the model for one step
          logs, _ = sess.run(
              [self.model_tf1.logged_ops, train_op],
              feed_dict={inputs: fake_x, labels: fake_y})
          self.model_tf1.update_logs(logs)

    # setup TF2 model
    random_tool = v1.keras.utils.DeterministicRandomTestTool(mode='num_random_ops')
    with random_tool.scope():
      self.model_tf2 = SimpleModel(params)
      for step in range(step_num):
        self.model_tf2.train_step([fake_x, fake_y])

  def test_learning_rate(self):
    np.testing.assert_allclose(
        self.model_tf1.logs['lr'],
        self.model_tf2.logs['lr'])

  def test_training_loss(self):
    # adopt different tolerance strategies before and after 10 steps
    first_n_step = 10

    # abosolute difference is limited below 1e-5
    # set `equal_nan` to be False to detect potential NaN loss issues
    abosolute_tolerance = 1e-5
    np.testing.assert_allclose(
        actual=self.model_tf1.logs['loss'][:first_n_step],
        desired=self.model_tf2.logs['loss'][:first_n_step],
        atol=abosolute_tolerance,
        equal_nan=False)

    # relative difference is limited below 5%
    relative_tolerance = 0.05
    np.testing.assert_allclose(self.model_tf1.logs['loss'][first_n_step:],
                               self.model_tf2.logs['loss'][first_n_step:],
                               rtol=relative_tolerance,
                               equal_nan=False)

أدوات التصحيح

tf.print

tf.print مقابل print / logging.info

  • باستخدام الوسائط القابلة للتكوين ، يمكن tf.print عرض العناصر القليلة الأولى والأخيرة من كل بُعد للموترات المطبوعة بشكل متكرر. تحقق من مستندات API للحصول على التفاصيل.
  • للتنفيذ الجاد ، يطبع كل من print و tf.print قيمة الموتر. لكن قد تتضمن print نسخًا من جهاز إلى مضيف ، مما قد يؤدي إلى إبطاء شفرتك.
  • بالنسبة لوضع الرسم البياني بما في ذلك الاستخدام داخل tf.function ، تحتاج إلى استخدام tf.print لطباعة قيمة الموتر الفعلية. يتم تجميع tf.print في مرجع في الرسم البياني ، بينما يسجل print and logging.info فقط في وقت التتبع ، وهو غالبًا ما لا تريده.
  • يدعم tf.print أيضًا طباعة الموترات المركبة مثل tf.RaggedTensor و tf.sparse.SparseTensor .
  • يمكنك أيضًا استخدام رد الاتصال لمراقبة المقاييس والمتغيرات. يرجى التحقق من كيفية استخدام عمليات الاسترجاعات المخصصة مع سمة logs و self.model .

tf.print مقابل وظيفة الطباعة داخل tf

# `print` prints info of tensor object
# `tf.print` prints the tensor value
@tf.function
def dummy_func(num):
  num += 1
  print(num)
  tf.print(num)
  return num

_ = dummy_func(tf.constant([1.0]))

# Output:
# Tensor("add:0", shape=(1,), dtype=float32)
# [2]
Tensor("add:0", shape=(1,), dtype=float32)
[2]

tf.distribute.Sstartibility

  • إذا تم تنفيذ دالة tf.print tf.function العمال ، على سبيل المثال عند استخدام TPUStrategy أو ParameterServerStrategy ، فأنت بحاجة إلى التحقق من سجلات خادم العامل / المعلمة للعثور على القيم المطبوعة.
  • print أو logging.info ، ستتم طباعة السجلات على المنسق عند استخدام ParameterServerStrategy ، وستتم طباعة السجلات على STDOUT على worker0 عند استخدام TPU.

tf.keras.Model

  • عند استخدام نماذج API التسلسلية والوظيفية ، إذا كنت ترغب في طباعة القيم ، على سبيل المثال ، مدخلات النموذج أو الميزات الوسيطة بعد بعض الطبقات ، فلديك الخيارات التالية.
    1. اكتب طبقة مخصصة tf.print المدخلات.
    2. قم بتضمين المخرجات الوسيطة التي تريد فحصها في مخرجات النموذج.
  • طبقات tf.keras.layers.Lambda لها (دي) قيود التسلسل. لتجنب مشاكل تحميل نقاط التحقق ، اكتب طبقة فرعية مخصصة بدلاً من ذلك. تحقق من مستندات API لمزيد من التفاصيل.
  • لا يمكنك tf.keras.callbacks.LambdaCallback tf.print لم يكن لديك وصول إلى القيم الفعلية ، ولكن بدلاً من ذلك فقط إلى كائنات Keras الرمزية.

الخيار 1: اكتب طبقة مخصصة

class PrintLayer(tf.keras.layers.Layer):
  def call(self, inputs):
    tf.print(inputs)
    return inputs

def get_model():
  inputs = tf.keras.layers.Input(shape=(1,))
  out_1 = tf.keras.layers.Dense(4)(inputs)
  out_2 = tf.keras.layers.Dense(1)(out_1)
  # use custom layer to tf.print intermediate features
  out_3 = PrintLayer()(out_2)
  model = tf.keras.Model(inputs=inputs, outputs=out_3)
  return model

model = get_model()
model.compile(optimizer="adam", loss="mse")
model.fit([1, 2, 3], [0.0, 0.0, 1.0])
[[-0.327884018]
 [-0.109294668]
 [-0.218589336]]
1/1 [==============================] - 0s 280ms/step - loss: 0.6077
<keras.callbacks.History at 0x7f63d46bf190>

الخيار 2: قم بتضمين المخرجات الوسيطة التي تريد فحصها في مخرجات النموذج.

لاحظ أنه في مثل هذه الحالة ، قد تحتاج إلى بعض التخصيصات لاستخدام Model.fit .

def get_model():
  inputs = tf.keras.layers.Input(shape=(1,))
  out_1 = tf.keras.layers.Dense(4)(inputs)
  out_2 = tf.keras.layers.Dense(1)(out_1)
  # include intermediate values in model outputs
  model = tf.keras.Model(
      inputs=inputs,
      outputs={
          'inputs': inputs,
          'out_1': out_1,
          'out_2': out_2})
  return model

PDB

يمكنك استخدام pdb في كل من Terminal و Colab لفحص القيم الوسيطة لتصحيح الأخطاء.

تصور الرسم البياني باستخدام TensorBoard

يمكنك فحص الرسم البياني TensorFlow باستخدام TensorBoard . TensorBoard مدعوم أيضًا على colab . TensorBoard هي أداة رائعة لتصور الملخصات. يمكنك استخدامه لمقارنة معدل التعلم ، وأوزان النموذج ، ومقياس التدرج ، ومقاييس التدريب / التحقق من الصحة ، أو حتى نموذج المخرجات الوسيطة بين نموذج TF1.x ونموذج TF2 المُرحل من خلال عملية التدريب ومعرفة ما إذا كانت القيم تبدو كما هو متوقع.

ملف تعريف TensorFlow

يمكن أن يساعدك TensorFlow Profiler في تصور الجدول الزمني للتنفيذ على وحدات معالجة الرسومات / وحدات المعالجة المركزية (TPU). يمكنك التحقق من هذا العرض التوضيحي Colab لاستخدامه الأساسي.

متعدد العمليات عداء

تعد MultiProcessRunner أداة مفيدة عند تصحيح الأخطاء باستخدام MultiWorkerMirroredStrategy و ParameterServerStrategy. يمكنك إلقاء نظرة على هذا المثال الملموس لاستخدامه.

على وجه التحديد لحالات هاتين الاستراتيجيتين ، يوصى بـ 1) ليس فقط لديك اختبارات وحدة لتغطية تدفقها ، 2) ولكن أيضًا لمحاولة إعادة إنتاج الفشل باستخدامه في اختبار الوحدة لتجنب إطلاق وظيفة حقيقية موزعة في كل مرة عندما يحاولون إصلاح.