Foundation
Постановка проблемы
Давайте рассмотрим модель линейной регрессии для набора образцов X
, где каждый образец представлен одной функцией x
. В рамках обучения модели мы ищем строку w.x + b
такую, что ((w.x+b) -y )^2
(квадрат потерь) минимален. Для набора точек данных мы берем средний квадрат потерь для каждой выборки и так называемую среднеквадратичную ошибку (MSE). w
и b
, обозначающие вес и смещение, вместе называются весами.
Установка линии / Обучение модели
- У нас есть решение в закрытой форме для решая задачу линейной регрессии и составляет
(X^T.X)^-1.X^T.y
- . Мы также можем использовать метод градиентного приличия для поиска весов, которые минимизируют квадрат потерь. Фреймворки, такие как tenorflow, pytorch, используют градиентный приличный для поиска весов (называемый обучением).
Градиентный приличный
Градиентный приличный алгоритм для обучения регрессии выглядит как удар
w, b = some initial value
While model has not converged:
y_hat = w.X + b
error = MSE(y, y_hat)
back propagate (BPP) error and adjust weights
Каждый прогон приведенного выше l oop называется эпохой. Однако из-за ограничений ресурсов вычисление y_hat
, error
и BPP не выполняется для полного набора данных, вместо этого данные делятся на более мелкие пакеты, и указанные выше операции выполняются для одного пакета за раз. Также мы обычно фиксируем количество эпох и отслеживаем сходимость модели.
w, b = some initial value
for i in range(number_of_epochs)
for X_batch,y_batch in get_next_batch(X, y)
y_hat = w.X_batch + b
error = MSE(y_batch, y_hat)
back propagate (BPP) error and adjust weights
Реализация пакетов Keras
Допустим, мы хотели бы добавить root среднеквадратичную ошибку для трассировки производительность модели во время ее обучения. Keras реализует следующий способ:
w, b = some initial value
for i in range(number_of_epochs)
all_y_hats = []
all_ys = []
for X_batch,y_batch in get_next_batch(X, y)
y_hat = w.X_batch + b
error = MSE(y_batch, y_hat)
all_y_hats.extend(y_hat)
all_ys.extend(y_batch)
batch_rms_error = RMSE(all_ys, all_y_hats)
back propagate (BPP) error and adjust weights
Как вы можете видеть выше, прогнозы накапливаются, и RMSE рассчитывается на основе накопленных прогнозов, а не на основе среднего значения RMSE всех предыдущих пакетов.
Реализация в keras
Теперь, когда наша основа ясна, давайте посмотрим, как мы можем реализовать то же самое в keras. У keras есть обратные вызовы, поэтому мы можем подключиться к обратному вызову on_batch_begin
и накапливать all_y_hats
и all_ys
. На обратном вызове on_batch_end
keras дает нам рассчитанное RMSE
. Мы вручную вычислим RMSE
, используя наши накопленные all_y_hats
и all_ys
, и проверим, совпадает ли оно с тем, что было рассчитано keras. Мы также сохраним веса, чтобы мы могли позже построить линию, которая изучается.
import numpy as np
from sklearn.metrics import mean_squared_error
import keras
import matplotlib.pyplot as plt
# Some training data
X = np.arange(16)
y = 0.5*X +0.2
batch_size = 8
all_y_hats = []
learned_weights = []
class CustomCallback(keras.callbacks.Callback):
def on_batch_begin(self, batch, logs={}):
w = self.model.layers[0].weights[0].numpy()[0][0]
b = self.model.layers[0].weights[1].numpy()[0]
s = batch*batch_size
all_y_hats.extend(b + w*X[s:s+batch_size])
learned_weights.append([w,b])
def on_batch_end(self, batch, logs={}):
calculated_error = np.sqrt(mean_squared_error(all_y_hats, y[:len(all_y_hats)]))
print (f"\n Calculated: {calculated_error}, Actual: {logs['root_mean_squared_error']}")
assert np.isclose(calculated_error, logs['root_mean_squared_error'])
def on_epoch_end(self, batch, logs={}):
del all_y_hats[:]
model = keras.models.Sequential()
model.add(keras.layers.Dense(1, input_shape=(1,)))
model.compile(optimizer=keras.optimizers.RMSprop(lr=0.01), loss="mean_squared_error", metrics=[keras.metrics.RootMeanSquaredError()])
# We should set shuffle=False so that we know how baches are divided
history = model.fit(X,y, epochs=100, callbacks=[CustomCallback()], batch_size=batch_size, shuffle=False)
Вывод:
Epoch 1/100
8/16 [==============>...............] - ETA: 0s - loss: 16.5132 - root_mean_squared_error: 4.0636
Calculated: 4.063645694548688, Actual: 4.063645839691162
Calculated: 8.10112834945773, Actual: 8.101128578186035
16/16 [==============================] - 0s 3ms/step - loss: 65.6283 - root_mean_squared_error: 8.1011
Epoch 2/100
8/16 [==============>...............] - ETA: 0s - loss: 14.0454 - root_mean_squared_error: 3.7477
Calculated: 3.7477213352845675, Actual: 3.7477214336395264
-------------- truncated -----------------------
Ta-da! утверждение assert np.isclose(calculated_error, logs['root_mean_squared_error'])
ни разу не сработало, поэтому наши расчеты / понимание верны.
Линия
Наконец, построим линию, которая корректируется алгоритмом BPP на основе среднеквадратичной потери ошибок . Мы можем использовать приведенный ниже код для создания png-изображения линии, изучаемой в каждом пакете, вместе с данными поезда.
for i, (w,b) in enumerate(learned_weights):
plt.close()
plt.axis([-1, 18, -1, 10])
plt.scatter(X, y)
plt.plot([-1,17], [-1*w+b, 17*w+b], color='green')
plt.savefig(f'img{i+1}.png')
Ниже приведена анимация в формате gif для вышеуказанных изображений в порядке их изучения.
The hyperplane (line in this case) being learned when y = 0.5*X +5.2
введите описание изображения здесь