Применение градиентного спуска в машинном обучении

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

Применение градиентного спуска в машинном обучении

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

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

Итак, пусть у нас есть простая функция Применение градиентного спуска в машинном обученииМы знаем, что минимум функции при w=0, но, предположим, мы этого не знаем. Наш весовой коэффициент установим случайным образом. Предположим, w=20. Мы знаем, что производная dJ/dw равна 2w.

Установим коэффициент обучения равным 0,1. В первом приближении мы имеем:

w – 0,1*2w = 20 – 0,1*40 = 16.

Поэтому установим w=16. Во втором приближении

w – 0,1*2w = 16 – 0,1*2*16 = 12,8.

Это даёт нам новое значение w=12,8. В третьем приближении

w – 0,1*2w = 12,8 – 0,1*2*12,8 = 10,24.

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

Теперь давайте попробуем реализовать это в коде и посмотрим, сможем ли мы полностью дойти до нуля. Импортируем библиотеку NumPy, установим значение w=20 и будем печатать результат на каждом шаге итераций. Количество приближений установим равным 30.

import numpy as np

w = 20

для i in xrange(30):

w = w – 0.1*2*w

print w

Как видим, w достигает значения 0,02, так что, похоже, 30 приближений недостаточно. Попробуем 100 приближений.

w = 20

for i in xrange(100):

w = w – 0.1*2*w

print w

Теперь результат равен

 4,07*10^{{-9}}

– это очень близко к нулю.

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

Почему этот метод так важен?

По мере продвижения далее в глубокое обучение и машинное обучение функции будут становиться всё более сложными. Для нейронных сетей с softmax 

нахождение производной может занять у вас несколько часов или даже дней.

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

Тем более, что для вычисления градиентов можно использовать специальные библиотеки, такие как Theano и TensorFlow.

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

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

Применение градиентного спуска в машинном обучении

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

Теперь рассмотрим, как использовать градиентный спуск применительно к линейной регрессии. Что такое линейная регрессия в Python рассмотрели подробно в этой статье.

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

J=(Y-Xw)^T (Y-Xw).

Мы также знаем, что такое производная, так как уже имели с ней дело.

Но в случае градиентного спуска мы вместо приравнивания производной к нулю и решения относительно w просто делаем небольшие шаги в направлении градиента, пока w не сойдётся с решением. Стоит отметить, что поскольку двойка является константой, мы можем опустить её, поскольку она может быть поглощена коэффициентом обучения. Поэтому полный gradient descent algorithm имеет вид

w = draw sample from N(0, 1/D)

для t=1..T

w=w - learning\;rate*X^T(Y_{hat}-Y)

Сначала мы устанавливаем некоторое случайное значение для w.

Хорошим значением может быть значение по распределению Гаусса с центром в нуле и дисперсией от единицы до D, где D – размерность. Следующее – вход в цикл для указанного количества шагов с последующим обновлением значения только что полученного градиентного спуска. Как вариант – цикл можно заканчивать, когда изменение w становится меньше некоторой установленной величины.

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

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

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

Обход ловушки фиктивной переменной с помощью градиентного спуска

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

Если вы не хотите самостоятельно писать код, то соответствующий файл называется gradient_descent.pi.

Итак, начнём с импорта библиотек NumPy и Matplotlib.

import numpy as np

import matplotlib as plt

Установим количество опытов равным 10, а размерность – равную трём. У нас получается Х в виде матрицы размерностью NxD. Установим первый столбец равным единице; установим первые пять элементов второго столбца и последние пять элементов третьего столбца также равными единице.

X = np.zeros((N, D))

X[:, 0] = 1

X[:5, 1] = 1

X[:5, 2] = 1

Так что наша матрица Х будет иметь вид

array([[1., 1., 0.],

 [1., 1., 0.],

 [1., 1., 0.],

 [1., 1., 0.],

 [1., 1., 0.],

 [1., 0., 1.],

 [1., 0., 1.],

 [1., 0., 1.],

 [1., 0., 1.],

 [1., 0., 1.]])

Зададим Y так, чтобы для первой половины данных значение Y было равно нулю, а для второй половины – равным единице.

Y = np.array([0]*5 + [1]*5)

Таким образом, Y будет иметь вид

array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1])

Вы можете видеть, что обычное решение

w = np.linalg.solve(X.T.dot(X), X.T.dot(Y))

не работает, так как XTX является единичной матрицей.

Поэтому мы воспользуемся градиентным спуском. Сохраним прежнюю функцию затрат, а потом изобразим её графически, чтобы вы смогли убедиться, что она падает по мере выполнения градиентного спуска. Зададим случайные весовые коэффициенты таким образом, чтобы дисперсия была в диапазоне от 1 до D, установим коэффициент обучения равным 0,001 и запустим 1000 циклов. Мы можем также заодно вычислить среднеквадратическую ошибку.

costs = []

w = np.random.randn(D) / np.sqrt(D)

learning_rate = 0.001

для t in xrange(1000):

Yhat = X.dot(w)

delta = Yhat – Y

w = w – learning_rate*X.T.dot(delta)

mse = delta.dot(delta) / N

costs.append(mse)

Теперь изобразим это графически.

plt.plot(costs)

plt.show()

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

Давайте также отобразим окончательное значение w и изобразим график.

Применение градиентного спуска в машинном обучении

plt.plot(Yhat, label=’prediction’)

plt.plot(Y, label=’targey’)

plt.legend()

plt.show()

Мы видим, что предсказанные значения Y очень близко к целевым.

Понравилась статья? Поделить с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: