Zobacz na TensorFlow.org | Uruchom w Google Colab | Wyświetl źródło na GitHub | Pobierz notatnik |
W tym notatniku pokazano, jak debugować potok uczenia podczas migracji do TF2. Składa się z następujących elementów:
- Sugerowane kroki i próbki kodu do debugowania potoku szkoleniowego
- Narzędzia do debugowania
- Inne powiązane zasoby
Jedno założenie jest takie, że masz kod TF1.x i wytrenowane modele do porównania i chcesz zbudować model TF2, który osiągnie podobną dokładność walidacji.
Ten notebook NIE obejmuje problemów z wydajnością debugowania w zakresie szybkości uczenia/wnioskowania lub wykorzystania pamięci.
Debugowanie przepływu pracy
Poniżej znajduje się ogólny przepływ pracy dotyczący debugowania potoków szkoleniowych TF2. Pamiętaj, że nie musisz wykonywać tych kroków w kolejności. Możesz również użyć podejścia wyszukiwania binarnego, w którym testujesz model w kroku pośrednim i zawężasz zakres debugowania.
Napraw błędy kompilacji i działania
Walidacja pojedynczego przejścia do przodu (w osobnym przewodniku )
a. Na urządzeniu z jednym procesorem
- Sprawdź, czy zmienne są tworzone tylko raz
- Sprawdź, czy liczby zmiennych, nazwy i kształty pasują do siebie
- Zresetuj wszystkie zmienne, sprawdź równoważność liczbową z wyłączoną losowością
- Wyrównaj generowanie liczb losowych, sprawdź równoważność liczbową w wnioskowaniu
- (Opcjonalnie) Sprawdź, czy punkty kontrolne są prawidłowo załadowane, a modele TF1.x/TF2 generują identyczne dane wyjściowe
b. Na jednym urządzeniu GPU/TPU
C. Dzięki strategiom na wiele urządzeń
Walidacja równoważności numerycznej szkolenia modelowego w kilku krokach (próbki kodu dostępne poniżej)
a. Walidacja pojedynczego etapu szkolenia przy użyciu małych i stałych danych na jednym urządzeniu z procesorem. W szczególności sprawdź równoważność liczbową następujących składników
- obliczanie strat
- metryka
- szybkość uczenia się
- obliczanie i aktualizacja gradientu
b. Sprawdź statystyki po treningu 3 lub więcej kroków, aby zweryfikować zachowania optymalizatora, takie jak pęd, nadal ze stałymi danymi na jednym urządzeniu z procesorem
C. Na jednym urządzeniu GPU/TPU
D. Ze strategiami wielu urządzeń (sprawdź intro do MultiProcessRunner na dole)
Kompleksowe testowanie pokrycia na rzeczywistym zbiorze danych
a. Sprawdź zachowania treningowe z TensorBoard
- użyj najpierw prostych optymalizatorów, np. SGD i prostych strategii dystrybucji, np
tf.distribute.OneDeviceStrategy
- metryki treningowe
- metryki oceny
- dowiedzieć się, jaka jest rozsądna tolerancja dla wrodzonej przypadkowości
b. Sprawdź równoważność za pomocą zaawansowanego optymalizatora/harmonogramu szybkości uczenia się/strategii dystrybucji
C. Sprawdź równoważność podczas korzystania z precyzji mieszanej
- użyj najpierw prostych optymalizatorów, np. SGD i prostych strategii dystrybucji, np
Dodatkowe testy porównawcze produktów
Ustawiać
pip uninstall -y -q tensorflow
# Install tf-nightly as the DeterministicRandomTestTool is only available in
# Tensorflow 2.8
pip install -q tf-nightly
Weryfikacja pojedynczego przejścia do przodu
Weryfikacja pojedynczego przejścia do przodu, w tym ładowanie punktów kontrolnych, jest omówiona w innym colab .
import sys
import unittest
import numpy as np
import tensorflow as tf
import tensorflow.compat.v1 as v1
Uczenie modelu walidacja równoważności numerycznej w kilku krokach
Ustaw konfigurację modelu i przygotuj fałszywy zbiór danych.
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
Zdefiniuj 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])
Poniższa klasa v1.keras.utils.DeterministicRandomTestTool
udostępnia scope()
menedżera kontekstu, który może sprawić, że stanowe operacje losowe będą używać tego samego źródła zarówno na wykresach/sesjach TF1, jak i na przyspieszonym wykonywaniu,
Narzędzie udostępnia dwa tryby testowania:
-
constant
, która używa tego samego ziarna dla każdej pojedynczej operacji, bez względu na to, ile razy została wywołana i, -
num_random_ops
, który wykorzystuje liczbę wcześniej zaobserwowanych losowych operacji stanowych jako ziarno operacji.
Dotyczy to zarówno stanowych operacji losowych używanych do tworzenia i inicjowania zmiennych, jak i stanowych operacji losowych używanych w obliczeniach (takich jak warstwy usuwania).
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.
Uruchom model TF1.x w trybie wykresu. Zbierz statystyki dla pierwszych 3 kroków szkolenia w celu porównania równoważności liczbowej.
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()
Zdefiniuj 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
Uruchom model TF2 w trybie przyspieszonym. Zbierz statystyki dla pierwszych 3 kroków szkolenia w celu porównania równoważności liczbowej.
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])
Porównaj równoważność liczbową dla kilku pierwszych kroków szkoleniowych.
Możesz również sprawdzić notatnik Walidacja poprawności i równoważności liczbowej, aby uzyskać dodatkowe porady dotyczące równoważności liczbowej.
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])
Testy jednostkowe
Istnieje kilka typów testów jednostkowych, które mogą pomóc w debugowaniu kodu migracji.
- Weryfikacja pojedynczego przejścia do przodu
- Uczenie modelu walidacja równoważności numerycznej w kilku krokach
- Wydajność wnioskowania porównawczego
- Wytrenowany model dokonuje prawidłowych prognoz na stałych i prostych punktach danych
Możesz użyć @parameterized.parameters
do testowania modeli o różnych konfiguracjach. Szczegóły z próbką kodu .
Zwróć uwagę, że możliwe jest uruchomienie interfejsów API sesji i przyspieszonego wykonania w tym samym przypadku testowym. Poniższe fragmenty kodu pokazują, jak to zrobić.
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)
Narzędzia do debugowania
tf.print
tf.print a print/logging.info
- Dzięki konfigurowalnym argumentom,
tf.print
może rekursywnie wyświetlać pierwsze i ostatnie kilka elementów każdego wymiaru dla drukowanych tensorów. Szczegóły znajdziesz w dokumentacji API . - W celu szybkiego wykonania zarówno
print
, jak itf.print
wypisują wartość tensora. Jednakprint
może wiązać się z kopiowaniem z urządzenia do hosta, co może potencjalnie spowolnić kod. - W przypadku trybu wykresu zawierającego użycie wewnątrz
tf.function
, musisz użyćtf.print
do wydrukowania rzeczywistej wartości tensora.tf.print
jest kompilowany jako operacja na wykresie, podczas gdyprint
ilogging.info
logują się tylko w czasie śledzenia, co często nie jest tym, czego chcesz. -
tf.print
obsługuje również drukowanie złożonych tensorów, takich jaktf.RaggedTensor
itf.sparse.SparseTensor
. - Możesz również użyć wywołania zwrotnego do monitorowania metryk i zmiennych. Sprawdź, jak używać niestandardowych wywołań zwrotnych z logami dict i atrybutem self.model .
tf.print vs print wewnątrz 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
- Jeśli funkcja
tf.print
tf.function
wykonywana na pracownikach, na przykład podczas korzystania zTPUStrategy
lubParameterServerStrategy
, należy sprawdzić logi pracownika/serwera parametrów, aby znaleźć drukowane wartości. - W przypadku
print
lublogging.info
, logi będą drukowane na koordynatorze podczas korzystania zParameterServerStrategy
, a logi będą drukowane na STDOUT na worker0 podczas korzystania z TPU.
tf.keras.Model
- W przypadku korzystania z modeli sekwencyjnego i funkcjonalnego interfejsu API, jeśli chcesz wydrukować wartości, np. dane wejściowe modelu lub elementy pośrednie po niektórych warstwach, dostępne są następujące opcje.
- Napisz niestandardową warstwę , która
tf.print
dane wejściowe. - Uwzględnij wyniki pośrednie, które chcesz sprawdzić, w wynikach modelu.
- Napisz niestandardową warstwę , która
- Warstwy
tf.keras.layers.Lambda
mają ograniczenia (de)serializacji. Aby uniknąć problemów z ładowaniem punktów kontrolnych, zamiast tego napisz niestandardową warstwę podklasy. Więcej informacji znajdziesz w dokumentacji API . - Nie możesz
tf.print
wyników pośrednich wtf.keras.callbacks.LambdaCallback
, jeśli nie masz dostępu do rzeczywistych wartości, ale zamiast tego tylko do symbolicznych obiektów tensora Keras.
Opcja 1: napisz własną warstwę
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>
Opcja 2: uwzględnij wyjścia pośrednie, które chcesz skontrolować, w wyjściach modelu.
Pamiętaj, że w takim przypadku możesz potrzebować pewnych dostosowań , aby użyć 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
Możesz użyć pdb zarówno w terminalu, jak i Colab, aby sprawdzić wartości pośrednie do debugowania.
Wizualizuj wykres za pomocą TensorBoard
Możesz zbadać wykres TensorFlow za pomocą TensorBoard . TensorBoard jest również obsługiwany w colab . TensorBoard to świetne narzędzie do wizualizacji podsumowań. Można go użyć do porównania szybkości uczenia się, wag modelu, skali gradientu, metryk uczenia/walidacji, a nawet pośrednich wyników modelowania między modelem TF1.x a zmigrowanym modelem TF2 w procesie uczenia i sprawdzania, czy wartości wyglądają zgodnie z oczekiwaniami.
TensorFlow Profiler
TensorFlow Profiler może pomóc w wizualizacji osi czasu wykonania na GPU/TPU. Możesz sprawdzić to demo Colab , aby zapoznać się z jego podstawowymi zastosowaniami.
MultiProcessRunner
MultiProcessRunner to przydatne narzędzie podczas debugowania za pomocą MultiWorkerMirroredStrategy i ParameterServerStrategy. Możesz spojrzeć na ten konkretny przykład pod kątem jego zastosowania.
Szczególnie w przypadku tych dwóch strategii zaleca się 1) nie tylko przeprowadzanie testów jednostkowych, aby pokryć ich przebieg, 2) ale także próbę odtworzenia awarii przy użyciu ich w teście jednostkowym, aby uniknąć uruchamiania rzeczywistego zadania rozproszonego za każdym razem, gdy próbują. naprawa.