Пакетный градиентный спуск

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

Проще говоря, представьте, что вы находитесь на вершине горы и хотите добраться до подножия как можно быстрее. Вы можете начать спускаться по склону, делая шаги в том направлении, которое кажется вам самым крутым. Однако вы не всегда сможете определить, какое направление действительно самое крутое, особенно если местность сложная.

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

В частности, градиентный спуск работает путем вычисления градиента функции в текущей точке, которая представляет собой направление наибольшего подъема. Затем алгоритм делает небольшой шаг в противоположном направлении, к самому крутому спуску. Этот процесс повторяется много раз, при этом размер каждого шага определяется скоростью обучения, пока алгоритм не достигнет минимума, по мере того как мы приближаемся к минимуму, размер нашего шага (скорость обучения) уменьшается. изначально наш размер шага высок.

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

Вот пошаговая математическая формулировка и вывод для нахождения значения 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 не сходится к минимуму.

Вот пошаговый вывод:

  1. Инициализируйте b случайным значением.
  2. Рассчитайте градиент SSE относительно b:
  3. ∂SSE/∂b = Σ-2(yi — (mx_i + b))
  4. Обновите b, используя правило обновления градиентного спуска:
  5. b_new = b — альфа * ∂SSE/∂b
  6. Повторяйте шаги 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 не сойдутся к минимуму.

Вот пошаговый вывод:

  1. Инициализируйте m и b случайными значениями.
  2. Рассчитайте градиенты SSE относительно m и b:
  3. ∂SSE/∂m = Σ-2x_i(yi — (mx_i + b))
  4. ∂SSE/∂b = Σ-2(yi — (mx_i + b))
  5. Обновите m и b, используя правило обновления градиентного спуска:
  6. m_new = m — альфа * ∂SSE/∂m
  7. b_new = b — альфа * ∂SSE/∂b
  8. Повторяйте шаги 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, мы используем пакетный градиентный спуск, который включает следующие шаги:

Примечание. Пакетный градиентный спуск ничем не отличается от градиентного спуска.

  1. Инициализация: мы начинаем с некоторых начальных значений коэффициентов, скажем, β0 = 0, β1 = 0, β2 = 0, …, βn = 0.
  2. Вычислить функцию стоимости: мы вычисляем функцию стоимости J(β), которая измеряет, насколько хорошо модель соответствует данным. Функция стоимости обычно определяется как среднеквадратическая ошибка (MSE), которая представляет собой среднее значение квадратов разностей между прогнозируемыми значениями и фактическими значениями выходной переменной y.
  3. Вычислить градиент: мы вычисляем градиент функции стоимости относительно коэффициентов β0, β1, β2, …, βn. Градиент представляет собой вектор из n+1 элементов, где первый элемент соответствует β0, а остальные элементы соответствуют β1, β2, …, βn.
  4. Обновите коэффициенты: мы обновляем коэффициенты, используя правило обновления градиентного спуска:

βj := βj — α * ∂J(β) / ∂βj

где α — скорость обучения (гиперпараметр, управляющий размером обновлений), а j = 0, 1, 2,…, n.

  1. Повторяйте шаги 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.


сингапурский доллар:

  • Вычисляет градиент функции стоимости для одного обучающего примера.
  • Обновляет параметры модели один раз для каждого примера.
  • Сходится быстрее, но с большим количеством колебаний к минимуму функции стоимости.
  • Может быть более чувствительным к выбору скорости обучения и случайности данных.
  • Требует меньше памяти и вычислительных ресурсов, чем BGD.


МБГД:

  • Вычисляет градиент функции стоимости для небольших случайных подмножеств обучающих данных (мини-пакетов).
  • Обновляет параметры модели один раз за мини-пакет.
  • Предлагает компромисс между скоростью SGD и стабильностью BGD.
  • Сходится быстрее, чем BGD, и с меньшими колебаниями, чем SGD.
  • Требует меньше памяти и вычислительных ресурсов, чем BGD, но больше, чем SGD.