Debug TF2 Migrated Training Pipeline

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub Unduh buku catatan

Notebook ini mendemonstrasikan cara men-debug pipeline pelatihan saat bermigrasi ke TF2. Ini terdiri dari komponen berikut:

  1. Langkah-langkah yang disarankan dan contoh kode untuk men-debug saluran pelatihan
  2. Alat untuk debugging
  3. Sumber daya terkait lainnya

Satu asumsi adalah Anda memiliki kode TF1.x dan model terlatih untuk perbandingan, dan Anda ingin membuat model TF2 yang mencapai akurasi validasi serupa.

Notebook ini TIDAK mencakup masalah kinerja debug untuk pelatihan/kecepatan inferensi atau penggunaan memori.

Alur kerja debug

Di bawah ini adalah alur kerja umum untuk men-debug saluran pelatihan TF2 Anda. Perhatikan bahwa Anda tidak perlu mengikuti langkah-langkah ini secara berurutan. Anda juga dapat menggunakan pendekatan pencarian biner di mana Anda menguji model dalam langkah menengah dan mempersempit ruang lingkup debugging.

  1. Perbaiki kesalahan kompilasi dan runtime

  2. Validasi forward pass tunggal (dalam panduan terpisah)

    sebuah. Pada perangkat CPU tunggal

    • Verifikasi variabel dibuat hanya sekali
    • Periksa jumlah variabel, nama, dan bentuk yang cocok
    • Setel ulang semua variabel, periksa kesetaraan numerik dengan semua keacakan dinonaktifkan
    • Sejajarkan pembuatan angka acak, periksa kesetaraan numerik dalam inferensi
    • (Opsional) Periksa pos pemeriksaan dimuat dengan benar dan model TF1.x/TF2 menghasilkan keluaran yang identik

    B. Pada perangkat GPU/TPU tunggal

    C. Dengan strategi multi-perangkat

  3. Model pelatihan validasi kesetaraan numerik untuk beberapa langkah (contoh kode tersedia di bawah)

    sebuah. Validasi langkah pelatihan tunggal menggunakan data kecil dan tetap pada perangkat CPU tunggal. Secara khusus, periksa kesetaraan numerik untuk komponen berikut:

    • perhitungan kerugian
    • metrik
    • kecepatan belajar
    • perhitungan dan pembaruan gradien

    B. Periksa statistik setelah melatih 3 langkah atau lebih untuk memverifikasi perilaku pengoptimal seperti momentum, masih dengan data tetap pada perangkat CPU tunggal

    C. Pada perangkat GPU/TPU tunggal

    D. Dengan strategi multi-perangkat (periksa intro untuk MultiProcessRunner di bagian bawah)

  4. Pengujian cakupan ujung-ke-ujung pada dataset nyata

    sebuah. Periksa perilaku pelatihan dengan TensorBoard

    • gunakan pengoptimal sederhana misalnya SGD dan strategi distribusi sederhana misalnya tf.distribute.OneDeviceStrategy terlebih dahulu
    • metrik pelatihan
    • metrik evaluasi
    • cari tahu apa toleransi yang masuk akal untuk keacakan bawaan

    B. Periksa kesetaraan dengan pengoptimal tingkat lanjut/penjadwal tingkat pembelajaran/strategi distribusi

    C. Periksa kesetaraan saat menggunakan presisi campuran

  5. Tolok ukur produk tambahan

Mempersiapkan

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

Validasi umpan maju tunggal

Validasi forward pass tunggal, termasuk pemuatan pos pemeriksaan, tercakup dalam colab yang berbeda.

import sys
import unittest
import numpy as np

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

Model pelatihan validasi kesetaraan numerik untuk beberapa langkah

Siapkan konfigurasi model dan siapkan dataset palsu.

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

Tentukan model 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])

Kelas v1.keras.utils.DeterministicRandomTestTool berikut menyediakan scope() yang dapat membuat operasi acak stateful menggunakan benih yang sama di kedua grafik/sesi TF1 dan eksekusi bersemangat,

Alat ini menyediakan dua mode pengujian:

  1. constant yang menggunakan benih yang sama untuk setiap operasi tidak peduli berapa kali dipanggil dan,
  2. num_random_ops yang menggunakan jumlah operasi acak stateful yang diamati sebelumnya sebagai benih operasi.

Ini berlaku baik untuk operasi acak stateful yang digunakan untuk membuat dan menginisialisasi variabel, dan untuk operasi acak stateful yang digunakan dalam komputasi (seperti untuk lapisan putus sekolah).

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.

Jalankan model TF1.x dalam mode grafik. Kumpulkan statistik untuk 3 langkah pelatihan pertama untuk perbandingan kesetaraan numerik.

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()

Tentukan model 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

Jalankan model TF2 dalam mode bersemangat. Kumpulkan statistik untuk 3 langkah pelatihan pertama untuk perbandingan kesetaraan numerik.

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])

Bandingkan kesetaraan numerik untuk beberapa langkah pelatihan pertama.

Anda juga dapat memeriksa buku catatan Validasi kebenaran & kesetaraan numerik untuk saran tambahan untuk kesetaraan numerik.

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])

Tes unit

Ada beberapa jenis pengujian unit yang dapat membantu men-debug kode migrasi Anda.

  1. Validasi umpan maju tunggal
  2. Model pelatihan validasi kesetaraan numerik untuk beberapa langkah
  3. Kinerja inferensi tolok ukur
  4. Model yang terlatih membuat prediksi yang benar pada titik data yang tetap dan sederhana

Anda dapat menggunakan @parameterized.parameters untuk menguji model dengan konfigurasi yang berbeda. Detail dengan contoh kode .

Perhatikan bahwa dimungkinkan untuk menjalankan API sesi dan eksekusi bersemangat dalam kasus pengujian yang sama. Cuplikan kode di bawah ini menunjukkan caranya.

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)

Alat debug

tf.print

tf.print vs print/logging.info

  • Dengan argumen yang dapat dikonfigurasi, tf.print dapat secara rekursif menampilkan beberapa elemen pertama dan terakhir dari setiap dimensi untuk tensor tercetak. Periksa dokumen API untuk detailnya.
  • Untuk eksekusi yang bersemangat, print dan tf.print mencetak nilai tensor. Tetapi print mungkin melibatkan salinan perangkat-ke-host, yang berpotensi memperlambat kode Anda.
  • Untuk mode grafik termasuk penggunaan di dalam tf.function , Anda perlu menggunakan tf.print untuk mencetak nilai tensor yang sebenarnya. tf.print dikompilasi menjadi sebuah op dalam grafik, sedangkan print dan logging.info hanya masuk pada waktu penelusuran, yang seringkali bukan yang Anda inginkan.
  • tf.print juga mendukung pencetakan komposit tensor seperti tf.RaggedTensor dan tf.sparse.SparseTensor .
  • Anda juga dapat menggunakan panggilan balik untuk memantau metrik dan variabel. Silakan periksa cara menggunakan panggilan balik khusus dengan dict log dan atribut self.model .

tf.print vs cetak di dalam 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

  • Jika tf.function yang berisi tf.print dijalankan pada pekerja, misalnya saat menggunakan TPUStrategy atau ParameterServerStrategy , Anda perlu memeriksa log server pekerja/parameter untuk menemukan nilai yang dicetak.
  • Untuk print atau logging.info , log akan dicetak pada koordinator saat menggunakan ParameterServerStrategy , dan log akan dicetak pada STDOUT pada worker0 saat menggunakan TPU.

tf.keras.Model

  • Saat menggunakan model API Sequential dan Fungsional, jika Anda ingin mencetak nilai, misalnya input model atau fitur perantara setelah beberapa lapisan, Anda memiliki opsi berikut.
    1. Tulis lapisan khusus yang tf.print inputnya.
    2. Sertakan keluaran antara yang ingin Anda periksa dalam keluaran model.
  • Lapisan tf.keras.layers.Lambda memiliki batasan (de)serialization. Untuk menghindari masalah pemuatan pos pemeriksaan, tulislah lapisan subkelas khusus sebagai gantinya. Periksa dokumen API untuk detail selengkapnya.
  • Anda tidak dapat tf.print output perantara di tf.keras.callbacks.LambdaCallback jika Anda tidak memiliki akses ke nilai sebenarnya, tetapi hanya ke objek tensor Keras simbolis.

Opsi 1: tulis lapisan khusus

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>

Opsi 2: sertakan keluaran antara yang ingin Anda periksa dalam keluaran model.

Perhatikan bahwa dalam kasus seperti itu, Anda mungkin memerlukan beberapa penyesuaian untuk menggunakan 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

Anda dapat menggunakan pdb di terminal dan Colab untuk memeriksa nilai antara untuk proses debug.

Visualisasikan grafik dengan TensorBoard

Anda dapat memeriksa grafik TensorFlow dengan TensorBoard . TensorBoard juga didukung di colab . TensorBoard adalah alat yang hebat untuk memvisualisasikan ringkasan. Anda dapat menggunakannya untuk membandingkan kecepatan pembelajaran, bobot model, skala gradien, metrik pelatihan/validasi, atau bahkan memodelkan keluaran antara antara model TF1.x dan model TF2 yang dimigrasikan melalui proses pelatihan dan melihat apakah nilainya terlihat seperti yang diharapkan.

Profiler TensorFlow

TensorFlow Profiler dapat membantu Anda memvisualisasikan timeline eksekusi pada GPU/TPU. Anda dapat melihat Demo Colab ini untuk penggunaan dasarnya.

MultiProcessRunner

MultiProcessRunner adalah alat yang berguna saat debugging dengan MultiWorkerMirroredStrategy dan ParameterServerStrategy. Anda dapat melihat contoh konkret ini untuk penggunaannya.

Khusus untuk kasus kedua strategi ini, Anda disarankan untuk 1) tidak hanya memiliki pengujian unit untuk menutupi alirannya, 2) tetapi juga mencoba mereproduksi kegagalan menggunakannya dalam pengujian unit untuk menghindari peluncuran tugas terdistribusi nyata setiap kali mereka mencoba perbaikan.