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

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

Мы начинаем наш пример с расширения нашего линейного уравнения от 2 до n переменных. Вместо того, чтобы выглядеть так:

const h = x => thetaZero + thetaOne * x;

Теперь это выглядит так:

Говоря очень конкретно, это означает, что, например, вместо того, чтобы предсказывать цену дома как зависящую от города (постоянная) плюс район * квадратные футы, она может зависеть от любого количества факторов, таких как город плюс тета_1 * квадратные футы. плюс theta_2 * количество комнат плюс theta_3 * качество обзора и т. д.

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

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

Функция стоимости может быть реализована следующим образом:

const cost = () => {
  let sum = 0;

  for (let i = 0; i < n; i++) {
    sum += (h(x[i]) - y[i])**2;
  }

  return sum / (2 * M);
}

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

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

Во-первых, вы ощупываете самое крутое направление, в котором можете ступить.

Затем вы делаете шаг определенного размера в этом направлении (размер этого шага называется «скоростью обучения»).

Затем вы повторяете этот процесс, пока не достигнете дна долины.

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

const alpha = 0.0003;

Если перевести это в математику, то мы берем нашу функцию h(θ_i) и берем ее производную в i-м направлении. Формально говоря, мы можем написать наш итерационный алгоритм, где α — скорость обучения («размер» ваших шагов).

Более явно, мы можем изолировать каждую из наших n «функций» (например, количество комнат, квадратных футов, качество обзора, местоположение) и минимизировать функцию стоимости для каждой из них. Ниже приведено приведенное выше уравнение с производной, явно выполненной с использованием цепного правила, которое вы помните из школьного исчисления.

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

const gradientDescent = (alpha) => {
  let thetaZeroFinal = 0;
  let thetaOneFinal = 0;

  for (let i = 0; i < n; i++) {
    thetaZeroFinal += h(x[i]) - y[i];
    thetaOneFinal += (h(x[i]) - y[i]) * x[i];
  }

  thetaZero = thetaZero - (alpha / n) * thetaZeroFinal;
  thetaOne = thetaOne - (alpha / n) * thetaOneFinal;
}

Вот визуальное представление того, как выглядит этот процесс (поверхность — это функция стоимости).

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