Посмотреть на 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()
Тензоры обычно собираются вместе в пакеты или группы входных и выходных данных, сложенных вместе. Пакетная обработка может дать некоторые преимущества при обучении и хорошо работает с ускорителями и векторизованными вычислениями. Учитывая, насколько мал этот набор данных, вы можете рассматривать весь набор данных как один пакет.
Определить модель
Используйте 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())
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()
Визуализируйте, как работает обученная модель
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())
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 см. в этом руководстве . Для написания пользовательских распределенных циклов обучения см. это руководство .