اشکال زدایی TF2 Migred Training Pipeline

مشاهده در TensorFlow.org در Google Colab اجرا شود مشاهده منبع در GitHub دانلود دفترچه یادداشت

این نوت بوک نحوه اشکال زدایی خط لوله آموزشی را هنگام مهاجرت به TF2 نشان می دهد. از اجزای زیر تشکیل شده است:

  1. مراحل پیشنهادی و نمونه کد برای خط لوله آموزشی اشکال زدایی
  2. ابزارهایی برای رفع اشکال
  3. سایر منابع مرتبط

یک فرض این است که شما کد TF1.x و مدل های آموزش دیده برای مقایسه دارید، و می خواهید یک مدل TF2 بسازید که به دقت اعتبارسنجی مشابهی دست یابد.

این نوت بوک مسائل مربوط به عملکرد اشکال زدایی را برای سرعت آموزش/استنتاج یا استفاده از حافظه پوشش نمی دهد.

اشکال زدایی گردش کار

در زیر یک گردش کار کلی برای اشکال زدایی خطوط لوله آموزشی TF2 ارائه شده است. توجه داشته باشید که لازم نیست این مراحل را به ترتیب انجام دهید. همچنین می‌توانید از یک رویکرد جستجوی دودویی استفاده کنید که در آن مدل را در یک مرحله میانی آزمایش می‌کنید و دامنه اشکال‌زدایی را محدود می‌کنید.

  1. رفع خطاهای کامپایل و زمان اجرا

  2. اعتبار سنجی یک پاس رو به جلو (در یک راهنمای جداگانه)

    آ. بر روی یک دستگاه CPU

    • تأیید کنید که متغیرها فقط یک بار ایجاد می شوند
    • تعداد متغیرها، نام ها و اشکال مطابقت را بررسی کنید
    • همه متغیرها را بازنشانی کنید، معادل عددی را با غیرفعال بودن همه تصادفی بررسی کنید
    • ایجاد اعداد تصادفی را تراز کنید، هم ارزی عددی را در استنتاج بررسی کنید
    • (اختیاری) چک پوینت ها به درستی بارگذاری شده اند و مدل های TF1.x/TF2 خروجی یکسان تولید می کنند

    ب روی یک دستگاه GPU/TPU

    ج با استراتژی های چند دستگاهی

  3. آموزش مدل اعتبار سنجی معادل سازی عددی برای چند مرحله (نمونه کد موجود در زیر)

    آ. اعتبارسنجی مرحله آموزشی تک مرحله ای با استفاده از داده های کوچک و ثابت در یک دستگاه واحد CPU. به طور خاص، معادل عددی را برای اجزای زیر بررسی کنید

    • محاسبه تلفات
    • معیارهای
    • میزان یادگیری
    • محاسبه گرادیان و به روز رسانی

    ب بررسی آمار پس از آموزش 3 مرحله یا بیشتر برای تأیید رفتارهای بهینه‌ساز مانند تکانه، همچنان با داده‌های ثابت در یک دستگاه CPU

    ج روی یک دستگاه 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() مدیر زمینه ارائه می دهد که می تواند عملیات تصادفی حالت دار را از همان seed در هر دو نمودار/جلسات 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 را در حالت نمودار اجرا کنید. جمع آوری آمار برای 3 مرحله آموزش اول برای مقایسه هم ارزی عددی.

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 را در حالت مشتاق اجرا کنید. جمع آوری آمار برای 3 مرحله آموزش اول برای مقایسه هم ارزی عددی.

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 برای آزمایش مدل هایی با پیکربندی های مختلف استفاده کنید. جزئیات با نمونه کد .

توجه داشته باشید که اجرای APIهای جلسه و اجرای مشتاقانه در همان مورد آزمایشی امکان پذیر است. تکه کدهای زیر نشان می دهد که چگونه.

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 و logging.info فقط در زمان ردیابی ثبت می‌شوند، که اغلب آن چیزی نیست که شما می‌خواهید.
  • tf.print همچنین از چاپ تانسورهای ترکیبی مانند tf.RaggedTensor و tf.sparse.SparseTensor می کند.
  • همچنین می‌توانید از تماس برگشتی برای نظارت بر معیارها و متغیرها استفاده کنید. لطفاً نحوه استفاده از تماس‌های سفارشی با ویژگی dict و self.model را بررسی کنید.

tf.print در مقابل چاپ داخل tf.function

# `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.strategy

  • اگر tf.function حاوی tf.print روی کارگران اجرا شود، به عنوان مثال هنگام استفاده از TPUStrategy یا ParameterServerStrategy ، باید گزارش‌های کارگر/پارامتر سرور را بررسی کنید تا مقادیر چاپ شده را بیابید.
  • برای print یا logging.info ، هنگام استفاده از ParameterServerStrategy ، گزارش‌ها روی هماهنگ‌کننده چاپ می‌شوند، و هنگام استفاده از TPU، گزارش‌ها روی STDOUT روی worker0 چاپ می‌شوند.

tf.keras.Model

  • هنگام استفاده از مدل‌های API متوالی و عملکردی، اگر می‌خواهید مقادیری را چاپ کنید، به عنوان مثال، ورودی‌های مدل یا ویژگی‌های میانی پس از چند لایه، گزینه‌های زیر را دارید.
    1. یک لایه سفارشی بنویسید که ورودی ها را tf.print کند.
    2. خروجی های میانی را که می خواهید بررسی کنید در خروجی های مدل قرار دهید.
  • لایه‌های tf.keras.layers.Lambda دارای محدودیت‌های (عدم) سریال‌سازی هستند. برای جلوگیری از مشکلات بارگیری ایست بازرسی، به جای آن یک لایه زیر کلاس سفارشی بنویسید. برای جزئیات بیشتر، اسناد API را بررسی کنید.
  • اگر به مقادیر واقعی دسترسی ندارید، نمی‌توانید خروجی‌های میانی را در tf.print tf.keras.callbacks.LambdaCallback کنید، بلکه فقط به اشیاء تانسور نمادین 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 هم در ترمینال و هم در Colab برای بررسی مقادیر میانی برای اشکال زدایی استفاده کنید.

نمودار را با TensorBoard تجسم کنید

می توانید نمودار TensorFlow را با TensorBoard بررسی کنید. TensorBoard نیز در colab پشتیبانی می شود. TensorBoard یک ابزار عالی برای تجسم خلاصه ها است. می‌توانید از آن برای مقایسه میزان یادگیری، وزن مدل، مقیاس گرادیان، معیارهای آموزشی/اعتبارسنجی، یا حتی مدل‌سازی خروجی‌های میانی بین مدل TF1.x و مدل TF2 مهاجرت‌شده از طریق فرآیند آموزش استفاده کنید و ببینید آیا مقادیر مطابق انتظار هستند یا خیر.

پروفایل TensorFlow

TensorFlow Profiler می تواند به شما در تجسم جدول زمانی اجرا در GPU/TPU کمک کند. شما می توانید این نسخه ی نمایشی Colab را برای استفاده اصلی آن بررسی کنید.

MultiProcessRunner

MultiProcessRunner یک ابزار مفید هنگام اشکال زدایی با MultiWorkerMirroredStrategy و ParameterServerStrategy است. برای کاربرد آن می توانید به این مثال ملموس نگاهی بیندازید.

به طور خاص برای موارد این دو استراتژی، به شما توصیه می شود: 1) نه تنها تست های واحد را برای پوشش جریان آنها داشته باشید، 2) بلکه سعی کنید شکست ها را با استفاده از آن در تست واحد بازتولید کنید تا هر بار که تلاش می کنند کار توزیع شده واقعی را راه اندازی نکنید. یک تعمیر