Объясните градиентный спуск в машинном обучении

машинное обучение

Эта статья возникла из личного публичного аккаунта:TechFlow, оригинальность непроста, прошу внимания


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

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

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


Определение градиента


Давайте сначала посмотрим на определение в Википедии: градиент — это обобщение многомерных производных. Производная обычной унарной (одномерной) функции является скалярнозначной функцией, а градиент многомерной функции является векторнозначной функцией. многомерная дифференцируемая функция{\displaystyle f}в точку{\displaystyle P}Градиент на , есть{\displaystyle f}существует{\displaystyle P}Частные производные на являются векторами компонентов.

Это предложение очень краткое, но не обязательно легкое для понимания, давайте рассмотрим его по частям. Мы изучали производные в старших классах и раньше, но производные, вычисляемые в старших классах, часто относятся к унарным функциям. То есть имеется только одна переменная x, а результатом вывода является конкретное значение, которое является скаляром. Результатом вывода функции многих переменных в некоторой точке является вектор, а результирующей компонентой вывода функции от n переменных является n, а каждая компонента производной является частной производной соответствующей переменной в этой точке . Этот вектор частных производных является градиентом функции в этой точке.

Тогда, в соответствии с приведенным выше определением, мы можем прояснить два момента: во-первых, градиент — это вектор, который имеет как направление, так и размер.


Объяснение градиентов


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

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

Второй пример — модель комнатной температуры Предположим, мы установили систему координат для комнаты, тогда каждая точка в комнате может быть представлена ​​как{\displaystyle (x,y,z)}, температура в этой точке\phi(x,y,z). Если предположить, что температура в помещении не меняется со временем, то градиент в каждой точке помещения представляет собой направление, в котором температура становится выше быстрее всего, а величина градиента представляет собой скорость изменения температуры.

С этими двумя примерами должно быть легко понять концепции направления и величины градиента.


Пример


Если предположить, что f - функция, определенная в трехмерном пространстве, то градиент f в определенной точке можно записать как:

\nabla f=(\frac{\partial f}{\partial x}, \frac{\partial f}{\partial y}, \frac{\partial f}{\partial z})=\frac{\partial f}{\partial x}i+\frac{\partial f}{\partial y}j + \frac{\partial f}{\partial z}k

здесьi, j, k- стандартные единичные векторы, представляющие оси координатx, y, zнаправление.

Возьмем пример:

f=3x^2+4y-sinz

Вставьте формулу градиента прямо сейчас, вы можете получить:

\nabla f = 6x\cdot i + 4\cdot j - \cos z\cdot k

если мы знаемx, y, zКоординаты , и подставляя их в него, можно узнать соответствующий градиент.


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


После понимания концепции градиента давайте посмотрим, что метод градиентного спуска на самом деле является графом. Пожалуйста, смотрите рисунок ниже.

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

Мы, очевидно, не знаем, каково оптимальное x в начале (ерунда, о чем еще спрашивать, если мы знаем), поэтому мы предполагаем случайную позицию в начале. Предполагая, что это на картинкеx_1позиция. Тогда у нас естьx_1Найдите градиент. Как мы уже говорили, градиент — это направление, в котором точка спускается наиболее круто, а величина градиента — это ее крутизна. Теперь, когда мы знаем направление градиента, это на самом деле очень просто: все, что нам нужно сделать, это сделать небольшой шаг вперед в направлении градиентного спуска, который является самым крутым.

мы предполагаем, что,x_1Градиент вs_1, то имеемs_1Функция потерь оптимизируется итерационным методом. Звучит немного пусто, но я понимаю это, когда пишу.

\begin{aligned} x_2 &= x_1 + \eta \cdot s_1 \\ x_3 &= x_2 + \eta \cdot s_2 \\ \vdots \\ x_n &= x_{n-1} + \eta \cdot s_{n-1} \end{aligned}

Как видно из приведенной выше формулы, это итерационная формула. То есть мы оптимизируем параметры, непрерывно повторяя итерации. Теоретически такие итерации неисчерпаемы, и нам нужно вручную завершать итерации. Когда это может прекратиться? Мы можем судить о градиенте каждой итерации, Когда градиент достаточно мал, чтобы приблизиться к 0, это означает, что обучение модели сошлось, и обучение в это время можно остановить.

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

Так чем выше скорость обучения, тем лучше? Очевидно нет. Потому что, если скорость обучения слишком велика, это может привести к тому, что оптимальная точка будет пропущена в итеративном процессе. Это похоже на то, как если бы вы резко нажали на педаль газа и заехали слишком далеко, так что может возникнуть ситуация, когда она никогда не сойдется. Например, мы можем обратиться к следующему изображению:

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

Разве не было бы нормально, если бы скорость обучения была установлена ​​меньше? Нет, если скорость обучения слишком мала, количество итераций будет не только настолько велико, что обучение займет слишком много времени, но и может превысить диапазон точности чисел с плавающей запятой, поскольку дробная часть слишком велика. происходит, когда возникает недопустимое значение Nan. Опять же, мы можем обратиться к следующему рисунку:

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

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

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

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

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

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


Кодовый бой


Talk is cheap, show me the code.

Просто говорить, а не практиковать фальшивую ручку, теперь, когда мы узнали суть градиентного спуска, пришло время испытать его с кодом. Мы по-прежнему используем предыдущую задачу линейной регрессии. Если вы забыли учащихся, вы можете щелкнуть ссылку ниже, чтобы просмотреть предыдущее содержимое:

Документ о прозрачной модели линейной регрессии

Как и прежде, давайте сначала создадим набор точек:

import numpy as np
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)

Это по функцииy = 3x + 4Случайным образом мы будем делать линейную регрессию градиентным спуском. Сначала выведем формулу градиента:

При использовании алгоритма градиентного спуска мы фактически вычисляем текущую\thetaвниз по градиенту. Эта величина отражает, когда наша\thetaКогда произойдут изменения, насколько изменится вся функция потерь MSE (среднеквадратичная ошибка среднеквадратичной ошибки). Градиент можно получить, взяв частную производную переменной. написано как:\frac {\partial}{\partial \theta_j}MSE(\theta).

мы рассчитываем отдельно\theta_jЧастная производная функции потерь записывается как:\frac {\partial}{\partial \theta_j}MSE(\theta), приведя предыдущую формулу функции потерь, расчет можно упростить, чтобы получить:

\frac {\partial}{\partial \theta_j}MSE(\theta)=\frac {1}{m}\sum_{i=1}^m(\theta^T \cdot x_i-y_i)x_j

это только\theta_jЧастная производная , мы можем положить вектор\thetaЧастные производные каждой переменной вычисляются вместе. Отметить как:

\nabla_\theta MSE(\theta)=\begin{pmatrix} \frac {\partial}{\partial \theta_0}MSE(\theta) \\ \frac {\partial}{\partial \theta_1}MSE(\theta) \\ \vdots\\ \frac {\partial}{\partial \theta_n}MSE(\theta) \\ \end{pmatrix}=\frac {1}{m}X^T\cdot(X \cdot\theta-y)

Нетрудно заметить, что в этой формуле задействован весь объем обучающих выборок X. Поэтому этот метод называется пакетным градиентным спуском. Поэтому, когда наши обучающие выборки очень большие, это делает наш алгоритм очень медленным. Но при использовании алгоритма градиентного спуска, который пропорционален количеству признаков, когда число признаков велико, градиентный спуск выполняется намного быстрее, чем непосредственное решение уравнения.

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

\theta^{next step}=\theta-\eta\nabla_\theta MSE(\theta)

По формуле написать код не сложно:

eta = 0.1 # 学习率
n_iterations = 1000 # 迭代次数
m = 100

theta = np.random.randn(2,1) # 随机初始值
X = np.c_[np.ones(100).T, X]
for iteration in range(n_iterations):
    gradients = 1/m * X.T.dot(X.dot(theta) - y) # 根据梯度公式迭代
    theta = theta - eta * gradients

Давайте вызовем этот код, чтобы увидеть результат:

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

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

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

Это все для сегодняшней статьи. Если вы чувствуете, что я что-то приобрел, пожалуйста, подпишитесь на нее или перешлите ее. Ваша поддержка - моя самая большая мотивация.