Базовые тренировочные циклы

Посмотреть на TensorFlow.org Запустить в Google Colab Посмотреть исходный код на GitHub Скачать блокнот

В предыдущих руководствах вы узнали о тензорах , переменных , градиентной ленте и модулях . В этом руководстве вы соберете все это вместе для обучения моделей.

TensorFlow также включает в себя tf.Keras API , высокоуровневый API нейронной сети, который предоставляет полезные абстракции для сокращения шаблонов. Однако в этом руководстве вы будете использовать базовые классы.

Настраивать

import tensorflow as tf

import matplotlib.pyplot as plt

colors = plt.rcParams['axes.prop_cycle'].by_key()['color']

Решение проблем с машинным обучением

Решение задачи машинного обучения обычно состоит из следующих шагов:

  • Получите обучающие данные.
  • Определите модель.
  • Определите функцию потерь.
  • Пробегитесь по тренировочным данным, рассчитав потери от идеального значения
  • Рассчитайте градиенты для этой потери и используйте оптимизатор , чтобы настроить переменные в соответствии с данными.
  • Оцените свои результаты.

В целях иллюстрации в этом руководстве вы разработаете простую линейную модель \(f(x) = x * W + b\)с двумя переменными: \(W\) (веса) и \(b\) (смещение).

Это самая основная проблема машинного обучения: учитывая \(x\) и \(y\), попробуйте найти наклон и смещение линии с помощью простой линейной регрессии .

Данные

В контролируемом обучении используются входные данные (обычно обозначаемые как x ) и выходные данные (обозначаемые y , часто называемые метками ). Цель состоит в том, чтобы учиться на парных входных и выходных данных, чтобы вы могли предсказать значение выходных данных на основе входных данных.

Каждый ввод ваших данных в TensorFlow почти всегда представлен тензором и часто является вектором. При обучении с учителем вывод (или значение, которое вы хотите предсказать) также является тензором.

Вот некоторые данные, синтезированные путем добавления гауссовского (нормального) шума к точкам вдоль линии.

# The actual line
TRUE_W = 3.0
TRUE_B = 2.0

NUM_EXAMPLES = 201

# A vector of random x values
x = tf.linspace(-2,2, NUM_EXAMPLES)
x = tf.cast(x, tf.float32)

def f(x):
  return x * TRUE_W + TRUE_B

# Generate some noise
noise = tf.random.normal(shape=[NUM_EXAMPLES])

# Calculate y
y = f(x) + noise
# Plot all the data
plt.plot(x, y, '.')
plt.show()

png

Тензоры обычно собираются вместе в пакеты или группы входных и выходных данных, сложенных вместе. Пакетная обработка может дать некоторые преимущества при обучении и хорошо работает с ускорителями и векторизованными вычислениями. Учитывая, насколько мал этот набор данных, вы можете рассматривать весь набор данных как один пакет.

Определить модель

Используйте tf.Variable для представления всех весов в модели. tf.Variable хранит значение и предоставляет его в тензорной форме по мере необходимости. Подробнее см. в руководстве по переменным .

Используйте tf.Module для инкапсуляции переменных и вычислений. Вы можете использовать любой объект Python, но таким образом его можно легко сохранить.

Здесь вы определяете как w , так и b как переменные.

class MyModel(tf.Module):
  def __init__(self, **kwargs):
    super().__init__(**kwargs)
    # Initialize the weights to `5.0` and the bias to `0.0`
    # In practice, these should be randomly initialized
    self.w = tf.Variable(5.0)
    self.b = tf.Variable(0.0)

  def __call__(self, x):
    return self.w * x + self.b

model = MyModel()

# List the variables tf.modules's built-in variable aggregation.
print("Variables:", model.variables)

# Verify the model works
assert model(3.0).numpy() == 15.0
Variables: (<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=0.0>, <tf.Variable 'Variable:0' shape=() dtype=float32, numpy=5.0>)
2021-12-08 17:11:44.542944: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.

Начальные переменные задаются здесь фиксированным образом, но Keras поставляется с любым из ряда инициализаторов, которые вы можете использовать, с остальной частью Keras или без нее.

Определение функции потерь

Функция потерь измеряет, насколько хорошо результат модели для данного входа соответствует целевому результату. Цель состоит в том, чтобы свести к минимуму эту разницу во время тренировки. Определите стандартные потери L2, также известные как «среднеквадратическая» ошибка:

# This computes a single loss value for an entire batch
def loss(target_y, predicted_y):
  return tf.reduce_mean(tf.square(target_y - predicted_y))

Перед обучением модели вы можете визуализировать значение потерь, нарисовав прогнозы модели красным цветом, а данные обучения — синим:

plt.plot(x, y, '.', label="Data")
plt.plot(x, f(x), label="Ground truth")
plt.plot(x, model(x), label="Predictions")
plt.legend()
plt.show()

print("Current loss: %1.6f" % loss(y, model(x)).numpy())

png

Current loss: 10.301712

Определение цикла обучения

Цикл обучения состоит из многократного выполнения трех задач по порядку:

  • Отправка пакета входных данных через модель для генерации выходных данных
  • Расчет потерь путем сравнения выходов с выходом (или меткой)
  • Использование градиентной ленты для поиска градиентов
  • Оптимизация переменных с этими градиентами

Для этого примера вы можете обучить модель с помощью градиентного спуска .

Существует множество вариантов схемы градиентного спуска, которые зафиксированы в tf.keras.optimizers . Но в духе построения из первых принципов здесь вы сами реализуете базовую математику с помощью tf.GradientTape для автоматического дифференцирования и tf.assign_sub для уменьшения значения (который объединяет tf.assign и tf.sub ):

# Given a callable model, inputs, outputs, and a learning rate...
def train(model, x, y, learning_rate):

  with tf.GradientTape() as t:
    # Trainable variables are automatically tracked by GradientTape
    current_loss = loss(y, model(x))

  # Use GradientTape to calculate the gradients with respect to W and b
  dw, db = t.gradient(current_loss, [model.w, model.b])

  # Subtract the gradient scaled by the learning rate
  model.w.assign_sub(learning_rate * dw)
  model.b.assign_sub(learning_rate * db)

Чтобы посмотреть на обучение, вы можете отправить один и тот же пакет x и y через цикл обучения и посмотреть, как развиваются W и b .

model = MyModel()

# Collect the history of W-values and b-values to plot later
weights = []
biases = []
epochs = range(10)

# Define a training loop
def report(model, loss):
  return f"W = {model.w.numpy():1.2f}, b = {model.b.numpy():1.2f}, loss={current_loss:2.5f}"


def training_loop(model, x, y):

  for epoch in epochs:
    # Update the model with the single giant batch
    train(model, x, y, learning_rate=0.1)

    # Track this before I update
    weights.append(model.w.numpy())
    biases.append(model.b.numpy())
    current_loss = loss(y, model(x))

    print(f"Epoch {epoch:2d}:")
    print("    ", report(model, current_loss))

Пройти обучение

current_loss = loss(y, model(x))

print(f"Starting:")
print("    ", report(model, current_loss))

training_loop(model, x, y)
Starting:
     W = 5.00, b = 0.00, loss=10.30171
Epoch  0:
     W = 4.46, b = 0.40, loss=10.30171
Epoch  1:
     W = 4.06, b = 0.72, loss=10.30171
Epoch  2:
     W = 3.77, b = 0.97, loss=10.30171
Epoch  3:
     W = 3.56, b = 1.18, loss=10.30171
Epoch  4:
     W = 3.40, b = 1.34, loss=10.30171
Epoch  5:
     W = 3.29, b = 1.47, loss=10.30171
Epoch  6:
     W = 3.21, b = 1.58, loss=10.30171
Epoch  7:
     W = 3.15, b = 1.66, loss=10.30171
Epoch  8:
     W = 3.10, b = 1.73, loss=10.30171
Epoch  9:
     W = 3.07, b = 1.78, loss=10.30171

Постройте эволюцию весов с течением времени:

plt.plot(epochs, weights, label='Weights', color=colors[0])
plt.plot(epochs, [TRUE_W] * len(epochs), '--',
         label = "True weight", color=colors[0])

plt.plot(epochs, biases, label='bias', color=colors[1])
plt.plot(epochs, [TRUE_B] * len(epochs), "--",
         label="True bias", color=colors[1])

plt.legend()
plt.show()

png

Визуализируйте, как работает обученная модель

plt.plot(x, y, '.', label="Data")
plt.plot(x, f(x), label="Ground truth")
plt.plot(x, model(x), label="Predictions")
plt.legend()
plt.show()

print("Current loss: %1.6f" % loss(model(x), y).numpy())

png

Current loss: 0.897898

То же решение, но с Keras

Полезно сравнить приведенный выше код с его эквивалентом в Keras.

Определение модели выглядит точно так же, если вы создадите подкласс tf.keras.Model . Помните, что модели Keras в конечном итоге наследуются от модуля.

class MyModelKeras(tf.keras.Model):
  def __init__(self, **kwargs):
    super().__init__(**kwargs)
    # Initialize the weights to `5.0` and the bias to `0.0`
    # In practice, these should be randomly initialized
    self.w = tf.Variable(5.0)
    self.b = tf.Variable(0.0)

  def call(self, x):
    return self.w * x + self.b

keras_model = MyModelKeras()

# Reuse the training loop with a Keras model
training_loop(keras_model, x, y)

# You can also save a checkpoint using Keras's built-in support
keras_model.save_weights("my_checkpoint")
Epoch  0:
     W = 4.46, b = 0.40, loss=10.30171
Epoch  1:
     W = 4.06, b = 0.72, loss=10.30171
Epoch  2:
     W = 3.77, b = 0.97, loss=10.30171
Epoch  3:
     W = 3.56, b = 1.18, loss=10.30171
Epoch  4:
     W = 3.40, b = 1.34, loss=10.30171
Epoch  5:
     W = 3.29, b = 1.47, loss=10.30171
Epoch  6:
     W = 3.21, b = 1.58, loss=10.30171
Epoch  7:
     W = 3.15, b = 1.66, loss=10.30171
Epoch  8:
     W = 3.10, b = 1.73, loss=10.30171
Epoch  9:
     W = 3.07, b = 1.78, loss=10.30171

Вместо того, чтобы писать новые обучающие циклы каждый раз при создании модели, вы можете использовать встроенные функции Keras в качестве ярлыка. Это может быть полезно, если вы не хотите писать или отлаживать обучающие циклы Python.

Если вы это сделаете, вам нужно будет использовать model.compile() для установки параметров и model.fit() для обучения. Может быть меньше кода, чтобы использовать реализации Keras потерь L2 и градиентного спуска, опять же в качестве ярлыка. Потери и оптимизаторы Keras также могут использоваться вне этих удобных функций, и в предыдущем примере они могли использоваться.

keras_model = MyModelKeras()

# compile sets the training parameters
keras_model.compile(
    # By default, fit() uses tf.function().  You can
    # turn that off for debugging, but it is on now.
    run_eagerly=False,

    # Using a built-in optimizer, configuring as an object
    optimizer=tf.keras.optimizers.SGD(learning_rate=0.1),

    # Keras comes with built-in MSE error
    # However, you could use the loss function
    # defined above
    loss=tf.keras.losses.mean_squared_error,
)

Keras fit ожидает пакетные данные или полный набор данных в виде массива NumPy. Массивы NumPy разбиваются на пакеты и по умолчанию имеют размер пакета 32.

В этом случае, чтобы соответствовать поведению написанного от руки цикла, вы должны передать x как один пакет размером 1000.

print(x.shape[0])
keras_model.fit(x, y, epochs=10, batch_size=1000)
201
Epoch 1/10
1/1 [==============================] - 0s 242ms/step - loss: 10.3017
Epoch 2/10
1/1 [==============================] - 0s 3ms/step - loss: 6.3148
Epoch 3/10
1/1 [==============================] - 0s 3ms/step - loss: 4.0341
Epoch 4/10
1/1 [==============================] - 0s 3ms/step - loss: 2.7191
Epoch 5/10
1/1 [==============================] - 0s 3ms/step - loss: 1.9548
Epoch 6/10
1/1 [==============================] - 0s 2ms/step - loss: 1.5068
Epoch 7/10
1/1 [==============================] - 0s 3ms/step - loss: 1.2422
Epoch 8/10
1/1 [==============================] - 0s 2ms/step - loss: 1.0845
Epoch 9/10
1/1 [==============================] - 0s 2ms/step - loss: 0.9899
Epoch 10/10
1/1 [==============================] - 0s 3ms/step - loss: 0.9327
<keras.callbacks.History at 0x7f02ad940050>

Обратите внимание, что Keras распечатывает потери после тренировки, а не до нее, поэтому первая потеря выглядит ниже, но в остальном это показывает практически ту же производительность обучения.

Следующие шаги

В этом руководстве вы увидели, как использовать основные классы тензоров, переменных, модулей и градиентной ленты для построения и обучения модели, а также как эти идеи сопоставляются с Keras.

Однако это очень простая задача. Более практическое введение см. в разделе Пошаговое руководство по пользовательскому обучению .

Подробнее об использовании встроенных обучающих циклов Keras см. в этом руководстве . Подробнее о тренировочных циклах и Keras см. в этом руководстве . Для написания пользовательских распределенных циклов обучения см. это руководство .