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

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

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

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

Настраивать

import tensorflow as tf

Решение проблем машинного обучения

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

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

В целях иллюстрации в этом руководстве вы разработаете простую линейную модель $ 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 = 1000

# A vector of random x values
x = tf.random.normal(shape=[NUM_EXAMPLES])

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

# Calculate y
y = x * TRUE_W + TRUE_B + noise
# Plot all the data
import matplotlib.pyplot as plt

plt.scatter(x, y, c="b")
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>)

Начальные переменные устанавливаются здесь фиксированным образом, но 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.scatter(x, y, c="b")
plt.scatter(x, model(x), c="r")
plt.show()

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

PNG

Current loss: 9.402307

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

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

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

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

Существует множество вариантов схемы градиентного спуска, которые фиксируются в 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
Ws, bs = [], []
epochs = range(10)

# Define a training loop
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
    Ws.append(model.w.numpy())
    bs.append(model.b.numpy())
    current_loss = loss(y, model(x))

    print("Epoch %2d: W=%1.2f b=%1.2f, loss=%2.5f" %
          (epoch, Ws[-1], bs[-1], current_loss))
print("Starting: W=%1.2f b=%1.2f, loss=%2.5f" %
      (model.w, model.b, loss(y, model(x))))

# Do the training
training_loop(model, x, y)

# Plot it
plt.plot(epochs, Ws, "r",
         epochs, bs, "b")

plt.plot([TRUE_W] * len(epochs), "r--",
         [TRUE_B] * len(epochs), "b--")

plt.legend(["W", "b", "True W", "True b"])
plt.show()
Starting: W=5.00 b=0.00, loss=9.40231
Epoch  0: W=4.58 b=0.41, loss=6.29136
Epoch  1: W=4.25 b=0.74, loss=4.32347
Epoch  2: W=3.98 b=1.00, loss=3.07861
Epoch  3: W=3.77 b=1.21, loss=2.29113
Epoch  4: W=3.61 b=1.38, loss=1.79297
Epoch  5: W=3.47 b=1.51, loss=1.47783
Epoch  6: W=3.37 b=1.62, loss=1.27846
Epoch  7: W=3.29 b=1.70, loss=1.15233
Epoch  8: W=3.22 b=1.77, loss=1.07254
Epoch  9: W=3.17 b=1.82, loss=1.02206

PNG

# Visualize how the trained model performs
plt.scatter(x, y, c="b")
plt.scatter(x, model(x), c="r")
plt.show()

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

PNG

Current loss: 1.022059

То же решение, но с Керасом

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

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

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.58 b=0.41, loss=6.29136
Epoch  1: W=4.25 b=0.74, loss=4.32347
Epoch  2: W=3.98 b=1.00, loss=3.07861
Epoch  3: W=3.77 b=1.21, loss=2.29113
Epoch  4: W=3.61 b=1.38, loss=1.79297
Epoch  5: W=3.47 b=1.51, loss=1.47783
Epoch  6: W=3.37 b=1.62, loss=1.27846
Epoch  7: W=3.29 b=1.70, loss=1.15233
Epoch  8: W=3.22 b=1.77, loss=1.07254
Epoch  9: W=3.17 b=1.82, loss=1.02206

Вместо того, чтобы писать новые циклы обучения каждый раз, когда вы создаете модель, вы можете использовать встроенные функции 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)
1000
Epoch 1/10
1/1 [==============================] - 0s 204ms/step - loss: 9.4023
Epoch 2/10
1/1 [==============================] - 0s 2ms/step - loss: 6.2914
Epoch 3/10
1/1 [==============================] - 0s 2ms/step - loss: 4.3235
Epoch 4/10
1/1 [==============================] - 0s 2ms/step - loss: 3.0786
Epoch 5/10
1/1 [==============================] - 0s 2ms/step - loss: 2.2911
Epoch 6/10
1/1 [==============================] - 0s 2ms/step - loss: 1.7930
Epoch 7/10
1/1 [==============================] - 0s 2ms/step - loss: 1.4778
Epoch 8/10
1/1 [==============================] - 0s 2ms/step - loss: 1.2785
Epoch 9/10
1/1 [==============================] - 0s 2ms/step - loss: 1.1523
Epoch 10/10
1/1 [==============================] - 0s 2ms/step - loss: 1.0725
<tensorflow.python.keras.callbacks.History at 0x7f85ec096bd0>

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

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

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

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

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