Пакетный градиентный спуск
Градиентный спуск — это популярный алгоритм оптимизации, используемый в машинном обучении для минимизации функции путем итеративной настройки ее параметров. Основная идея состоит в том, чтобы переместить параметры функции в направлении наискорейшего спуска градиента функции.
Проще говоря, представьте, что вы находитесь на вершине горы и хотите добраться до подножия как можно быстрее. Вы можете начать спускаться по склону, делая шаги в том направлении, которое кажется вам самым крутым. Однако вы не всегда сможете определить, какое направление действительно самое крутое, особенно если местность сложная.
Вместо этого вы можете использовать инструмент, который измеряет крутизну местности во всех направлениях, например, компас, указывающий вниз по склону. С помощью этого инструмента вы можете делать небольшие шаги в направлении, которое по компасу кажется самым крутым, пока не достигнете дна. Градиентный спуск подобен компасу для моделей машинного обучения: он сообщает модели, как настроить свои параметры, чтобы минимизировать функцию, которую она пытается оптимизировать.
В частности, градиентный спуск работает путем вычисления градиента функции в текущей точке, которая представляет собой направление наибольшего подъема. Затем алгоритм делает небольшой шаг в противоположном направлении, к самому крутому спуску. Этот процесс повторяется много раз, при этом размер каждого шага определяется скоростью обучения, пока алгоритм не достигнет минимума, по мере того как мы приближаемся к минимуму, размер нашего шага (скорость обучения) уменьшается. изначально наш размер шага высок.
В машинном обучении оптимизируемая функция обычно представляет собой функцию стоимости, которая измеряет, насколько хорошо модель выполняет данную задачу. Используя градиентный спуск для минимизации этой функции стоимости, модель может со временем улучшать свою производительность, учась делать более качественные прогнозы или классификации.
Вот пошаговая математическая формулировка и вывод для нахождения значения b в простой линейной регрессии с использованием градиентного спуска, предполагая, что у нас уже есть значение m:
Уравнение простой линейной регрессии: y = mx + b, где y — зависимая переменная, x — независимая переменная, m — наклон линии, а b — точка пересечения с осью y.
Допустим, у нас есть набор из n точек данных (x1, y1), (x2, y2),…, (xn, yn), и мы хотим найти значения m и b, которые минимизируют сумму квадратов ошибок (SSE) :
SSE = Σ(yi — (mx_i + b))², i = от 1 до n
Чтобы найти значение b с помощью градиентного спуска, нам сначала нужно вычислить частную производную SSE по b:
∂SSE/∂b = Σ-2(yi — (mx_i + b))
Затем мы обновляем значение b, используя правило обновления градиентного спуска:
b_new = b — альфа * ∂SSE/∂b
где альфа — скорость обучения. Мы повторяем этот процесс, пока значение b не сходится к минимуму.
Вот пошаговый вывод:
- Инициализируйте b случайным значением.
- Рассчитайте градиент SSE относительно b:
- ∂SSE/∂b = Σ-2(yi — (mx_i + b))
- Обновите b, используя правило обновления градиентного спуска:
- b_new = b — альфа * ∂SSE/∂b
- Повторяйте шаги 2 и 3, пока значение b не станет минимальным.
Вот пример кода Python для нахождения значения b с использованием градиентного спуска:
from sklearn.datasets import make_regression import numpy as np X,y = make_regression(n_samples=4, n_features=1, n_informative=1, n_targets=1,noise=80,random_state=13) import matplotlib.pyplot as plt plt.scatter(X,y)
# Lets apply OLS from sklearn.linear_model import LinearRegression reg = LinearRegression() reg.fit(X,y) LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False) reg.coef_ >>>>>> array([78.35063668]) reg.intercept_ >>>>> 26.15963284313262 plt.scatter(X,y) plt.plot(X,reg.predict(X),color='red')reg = LinearRegression() reg.fit(X,y) LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False) reg.coef_ >>>>>> array([78.35063668]) reg.intercept_ >>>>> 26.15963284313262 plt.scatter(X,y) plt.plot(X,reg.predict(X),color='red')
# Lets apply Gradient Descent assuming slope is constant m = 78.35 # and let's assume the starting value for intercept b = 0 y_pred = ((78.35 * X) + 100).reshape(4) plt.scatter(X,y) plt.plot(X,reg.predict(X),color='red',label='OLS') plt.plot(X,y_pred,color='#00a65a',label='b = 0') plt.legend() plt.show()
m = 78.35 b = 100 loss_slope = -2 * np.sum(y - m*X.ravel() - b) loss_slope >>>> 590.7223659179078 # Lets take learning rate = 0.1 lr = 0.1 step_size = loss_slope*lr step_size >>>>> 59.072236591790784 # Calculating the new intercept b = b - step_size b >>>> 40.927763408209216 y_pred1 = ((78.35 * X) + b).reshape(4) plt.scatter(X,y) plt.plot(X,reg.predict(X),color='red',label='OLS') plt.plot(X,y_pred1,color='#00a65a',label='b = {}'.format(b)) plt.plot(X,y_pred,color='#A3E4D7',label='b = 0') plt.legend() plt.show()
# Iteration 2 loss_slope = -2 * np.sum(y - m*X.ravel() - b) loss_slope >>>>> 118.14447318358157 step_size = loss_slope*lr step_size >>>>> 11.814447318358157 b = b - step_size b >>>>> 29.11331608985106 y_pred2 = ((78.35 * X) + b).reshape(4) plt.scatter(X,y) plt.plot(X,reg.predict(X),color='red',label='OLS') plt.plot(X,y_pred2,color='#00a65a',label='b = {}'.format(b)) plt.plot(X,y_pred1,color='#A3E4D7',label='b = {}'.format(b)) plt.plot(X,y_pred,color='#A3E4D7',label='b = 0') plt.legend() plt.show()
loss_slope = -2 * np.sum(y - m*X.ravel() - b) loss_slope >>>> 23.62889463671634 step_size = loss_slope*lr step_size >>>> 2.362889463671634 b = b - step_size b >>>> 26.750426626179426 y_pred3 = ((78.35 * X) + b).reshape(4) plt.figure(figsize=(15,15)) plt.scatter(X,y) plt.plot(X,reg.predict(X),color='red',label='OLS') plt.plot(X,y_pred3,color='#00a65a',label='b = {}'.format(b)) plt.plot(X,y_pred2,color='#A3E4D7',label='b = {}'.format(b)) plt.plot(X,y_pred1,color='#A3E4D7',label='b = {}'.format(b)) plt.plot(X,y_pred,color='#A3E4D7',label='b = 0') plt.legend() plt.show()
b = -100 m = 78.35 lr = 0.01 epochs = 100 for i in range(epochs): loss_slope = -2 * np.sum(y - m*X.ravel() - b) b = b - (lr * loss_slope) y_pred = m * X + b plt.plot(X,y_pred) plt.scatter(X,y)
«Визуализация градиентного спуска: пошаговый пример поиска y-перехвата».
Линия наилучшего соответствия:
График иллюстрирует взаимосвязь между количеством эпох и потерями.
График иллюстрирует взаимосвязь между количеством эпох и потерями, а также указывает скорость обучения, используемую в алгоритме оптимизации. По мере увеличения количества эпох потери уменьшаются, что указывает на то, что модель обучается и улучшает свою производительность при выполнении задачи. Однако важно отметить, что скорость обучения не является постоянной на протяжении процесса обучения.
В начале обучения скорость обучения высока, что позволяет модели делать большие обновления своих параметров и быстро сходиться к минимуму функции потерь. По мере приближения модели к минимуму скорость обучения снижается, чтобы избежать превышения минимума и обеспечить более стабильную сходимость.
Скорость обучения.
Скорость обучения — это гиперпараметр, который контролирует размер шагов, предпринимаемых на каждой итерации алгоритма градиентного спуска. Если скорость обучения слишком низкая, алгоритм может медленно сходиться, а если она слишком высока, алгоритм может выйти за пределы минимума и расходиться.
Вот схематическое представление того, как разные скорости обучения влияют на градиентный спуск:
На диаграмме у нас есть простая двумерная функция стоимости с одним минимумом. Синие точки представляют собой точки данных, а синяя линия — это наши наиболее подходящие линии алгоритма градиентного спуска с разной скоростью обучения.
Слева у нас низкая скорость обучения. Алгоритм делает небольшие шаги в направлении градиента и медленно сходится к минимуму.
В середине у нас умеренная скорость обучения. Алгоритм использует более крупные шаги и быстрее сходится к минимуму.
Справа у нас высокая скорость обучения. Алгоритм делает очень большие шаги и выходит за пределы минимума, в результате чего алгоритм расходится и колеблется вокруг минимума.
Важно выбрать подходящую скорость обучения для конкретной задачи, поскольку разные задачи могут иметь разные оптимальные скорости обучения. Распространенный подход заключается в том, чтобы попробовать разные скорости обучения и понаблюдать за сходимостью алгоритма.
Уменьшение потерь: оптимизация скорости обучения | Машинное обучение | Разработчики Google
пошаговая математическая формулировка и вывод для нахождения значений m и b в простой линейной регрессии с использованием градиентного спуска:
Уравнение простой линейной регрессии: y = mx + b, где y — зависимая переменная, x — независимая переменная, m — наклон линии, а b — точка пересечения с осью y.
Допустим, у нас есть набор из n точек данных (x1, y1), (x2, y2),…, (xn, yn), и мы хотим найти значения m и b, которые минимизируют сумму квадратов ошибок (SSE) :
SSE = Σ(yi — (mx_i + b))², i = от 1 до n
Чтобы найти значения m и b с помощью градиентного спуска, нам нужно вычислить частные производные SSE по m и b:
∂SSE/∂m = Σ-2x_i(yi — (mx_i + b))
∂SSE/∂b = Σ-2(yi — (mx_i + b))
Затем мы обновляем значения m и b, используя правило обновления градиентного спуска:
m_new = m — альфа * ∂SSE/∂m
b_new = b — альфа * ∂SSE/∂b
где альфа — скорость обучения. Мы повторяем этот процесс до тех пор, пока значения m и b не сойдутся к минимуму.
Вот пошаговый вывод:
- Инициализируйте m и b случайными значениями.
- Рассчитайте градиенты SSE относительно m и b:
- ∂SSE/∂m = Σ-2x_i(yi — (mx_i + b))
- ∂SSE/∂b = Σ-2(yi — (mx_i + b))
- Обновите m и b, используя правило обновления градиентного спуска:
- m_new = m — альфа * ∂SSE/∂m
- b_new = b — альфа * ∂SSE/∂b
- Повторяйте шаги 2 и 3, пока значения m и b не сойдутся к минимуму.
Вот пример кода Python для поиска значений m и b с использованием градиентного спуска:
from sklearn.datasets import make_regression import matplotlib.pyplot as plt import numpy as np from sklearn.model_selection import cross_val_score X,y = make_regression(n_samples=100, n_features=1, n_informative=1, n_targets=1,noise=20,random_state=13) plt.scatter(X,y)
from sklearn.model_selection import train_test_split X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=2) from sklearn.linear_model import LinearRegression lr = LinearRegression() lr.fit(X_train,y_train) print(lr.coef_) print(lr.intercept_) >>>> [28.12597332] >>>>-2.271014426178382 y_pred = lr.predict(X_test) from sklearn.metrics import r2_score r2_score(y_test,y_pred) >>>> 0.6345158782661013 class GDRegressor: def __init__(self,learning_rate,epochs): self.m = 100 self.b = -120 self.lr = learning_rate self.epochs = epochs def fit(self,X,y): # calcualte the b using GD for i in range(self.epochs): loss_slope_b = -2 * np.sum(y - self.m*X.ravel() - self.b) loss_slope_m = -2 * np.sum((y - self.m*X.ravel() - self.b)*X.ravel()) self.b = self.b - (self.lr * loss_slope_b) self.m = self.m - (self.lr * loss_slope_m) print(self.m,self.b) def predict(self,X): return self.m * X + self.b gd = GDRegressor(0.001,50) gd.fit(X_train,y_train) >>>> 28.159367347119066 -2.3004574196824854 y_pred = gd.predict(X_test) from sklearn.metrics import r2_score r2_score(y_test,y_pred) >>>>> 0.6343842836315579
«Конвергенция и визуализация: использование градиентного спуска для поиска наилучшей линии»
В этой визуализации «темная сторона» долины представляет собой точку минимума функции потерь, где параметры модели оптимизированы для получения наилучших прогнозов. Форма контурного графика может варьироваться в зависимости от сложности модели и количества оптимизируемых параметров.
3d представление
Здесь мы пытаемся сформулировать с помощью множественной линейной регрессии.
Модель множественной линейной регрессии определяется как:
у = β0 + β1x1 + β2x2 + … + βnxn
где β0, β1, β2, …, βn — коэффициенты (также известные как веса или параметры) модели, которую мы хотим оптимизировать.
Чтобы найти оптимальные значения β0, β1, β2, …, βn, мы используем пакетный градиентный спуск, который включает следующие шаги:
Примечание. Пакетный градиентный спуск ничем не отличается от градиентного спуска.
- Инициализация: мы начинаем с некоторых начальных значений коэффициентов, скажем, β0 = 0, β1 = 0, β2 = 0, …, βn = 0.
- Вычислить функцию стоимости: мы вычисляем функцию стоимости J(β), которая измеряет, насколько хорошо модель соответствует данным. Функция стоимости обычно определяется как среднеквадратическая ошибка (MSE), которая представляет собой среднее значение квадратов разностей между прогнозируемыми значениями и фактическими значениями выходной переменной y.
- Вычислить градиент: мы вычисляем градиент функции стоимости относительно коэффициентов β0, β1, β2, …, βn. Градиент представляет собой вектор из n+1 элементов, где первый элемент соответствует β0, а остальные элементы соответствуют β1, β2, …, βn.
- Обновите коэффициенты: мы обновляем коэффициенты, используя правило обновления градиентного спуска:
βj := βj — α * ∂J(β) / ∂βj
где α — скорость обучения (гиперпараметр, управляющий размером обновлений), а j = 0, 1, 2,…, n.
- Повторяйте шаги 2–4 до сходимости: мы повторяем шаги 2–4 до тех пор, пока функция стоимости не сойдется (т. Е. Перестанет значительно уменьшаться).
Вот пример того, как пакетный градиентный спуск можно применить к многомерной задаче множественной линейной регрессии:
Предположим, у нас есть набор данных с n = 10 признаков и m = 1000 наблюдений. Цель состоит в том, чтобы предсказать цену дома на основе таких характеристик, как площадь, количество спален, количество ванных комнат и т. д.
Мы можем представить набор данных в виде m x (n + 1) матрицы X, где первый столбец X представляет собой вектор из 1 (для члена пересечения), а остальные столбцы представляют n признаков. У нас также есть вектор y из m элементов, который представляет фактические цены на дома.
Мы можем инициализировать коэффициенты β0, β1, β2,…, βn равными нулю и установить скорость обучения α (например, 0,01). Затем мы можем вычислить функцию стоимости J(β) как:
J(β) = 1/2m * сумма((Xβ — y)²)
где Xβ — прогнозируемые цены домов на основе текущих значений коэффициентов β, а сумма берется по всем m наблюдениям.
Затем мы можем вычислить градиент функции стоимости как:
∂J(β) / ∂βj = 1/m * sum((Xβ — y) * Xj)
где Xj — j-й столбец матрицы X, а сумма берется по
продолжать выше
все m наблюдений.
Используя правило обновления градиентного спуска, мы можем обновить коэффициенты следующим образом:
βj := βj — α * ∂J(β) / ∂βj
для j = 0, 1, 2, …, n. Это правило обновления по существу перемещает коэффициенты в направлении наискорейшего спуска функции стоимости, пока мы не достигнем минимума.
Мы можем повторять вычисление функции стоимости и градиента, а также обновление коэффициентов до тех пор, пока функция стоимости не сойдется (т. е. перестанет значительно уменьшаться). Как только коэффициенты сойдутся, мы получим оптимальные значения β0, β1, β2, …, βn, которые лучше всего соответствуют данным.
Пакетный градиентный спуск имеет то преимущество, что он может сходиться к глобальному минимуму функции стоимости, но для больших наборов данных он может быть дорогостоящим в вычислительном отношении. Существуют и другие алгоритмы оптимизации, такие как стохастический градиентный спуск и мини-пакетный градиентный спуск, которые могут быть более эффективными для больших наборов данных.
Код Python
from sklearn.datasets import load_diabetes import numpy as np from sklearn.linear_model import LinearRegression from sklearn.metrics import r2_score from sklearn.model_selection import train_test_split X,y = load_diabetes(return_X_y=True) print(X.shape) print(y.shape) >>>> (442, 10) >>>>> (442,) X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=2) reg = LinearRegression() reg.fit(X_train,y_train) LinearRegression() print(reg.coef_) print(reg.intercept_) >>>> [ -9.16088483 -205.46225988 516.68462383 340.62734108 -895.54360867 561.21453306 153.88478595 126.73431596 861.12139955 52.41982836] >>>> 151.88334520854633 y_pred = reg.predict(X_test) r2_score(y_test,y_pred) >>>>> 0.4399387660024645 X_train.shape >>>>>> (353, 10) class GDRegressor: def __init__(self,learning_rate=0.01,epochs=100): self.coef_ = None self.intercept_ = None self.lr = learning_rate self.epochs = epochs def fit(self,X_train,y_train): # init your coefs self.intercept_ = 0 self.coef_ = np.ones(X_train.shape[1]) for i in range(self.epochs): # update all the coef and the intercept y_hat = np.dot(X_train,self.coef_) + self.intercept_ #print("Shape of y_hat",y_hat.shape) intercept_der = -2 * np.mean(y_train - y_hat) self.intercept_ = self.intercept_ - (self.lr * intercept_der) coef_der = -2 * np.dot((y_train - y_hat),X_train)/X_train.shape[0] self.coef_ = self.coef_ - (self.lr * coef_der) print(self.intercept_,self.coef_) def predict(self,X_test): return np.dot(X_test,self.coef_) + self.intercept_ gdr = GDRegressor(epochs=1000,learning_rate=0.5) gdr.fit(X_train,y_train) >>>>> 152.0135263267291 [ 14.38915082 -173.72674118 491.54504015 323.91983579 -39.32680194 >>>>> -116.01099114 -194.04229501 103.38216641 451.63385893 97.57119174] y_pred = gdr.predict(X_test) r2_score(y_test,y_pred) >>>>> 0.4534524671450598
Градиентный спуск, также известный как пакетный градиентный спуск.
вот более подробное сравнение сходимости пакетного градиентного спуска (BGD), стохастического градиентного спуска (SGD) и мини-пакетного градиентного спуска (MBGD):
БГД:
- Вычисляет градиент функции стоимости по всему обучающему набору.
- Обновляет параметры модели один раз за итерацию.
- Сходится медленнее, но неуклонно к минимуму функции стоимости.
- Может быть более стабильным, чем SGD и MBGD, но для сходимости может потребоваться больше времени.
- Требует больше памяти и вычислительных ресурсов, чем SGD и MBGD.
«Понимание градиентного спуска: интуиция, математическая формулировка и вывод
Batch-gradient-descentmedium.com»
сингапурский доллар:
- Вычисляет градиент функции стоимости для одного обучающего примера.
- Обновляет параметры модели один раз для каждого примера.
- Сходится быстрее, но с большим количеством колебаний к минимуму функции стоимости.
- Может быть более чувствительным к выбору скорости обучения и случайности данных.
- Требует меньше памяти и вычислительных ресурсов, чем BGD.
МБГД:
- Вычисляет градиент функции стоимости для небольших случайных подмножеств обучающих данных (мини-пакетов).
- Обновляет параметры модели один раз за мини-пакет.
- Предлагает компромисс между скоростью SGD и стабильностью BGD.
- Сходится быстрее, чем BGD, и с меньшими колебаниями, чем SGD.
- Требует меньше памяти и вычислительных ресурсов, чем BGD, но больше, чем SGD.