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

Фон

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

Альтернативный подход — это решение в закрытой форме, которое не требует скорости обучения или эпох. Решение в закрытой форме для регрессии известно как нормальное уравнение. Его можно использовать для непосредственного определения веса линии наилучшего соответствия. В этой статье он будет получен, а затем реализован в Python.

Вывод нормального уравнения

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

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

Чтобы доказать, что это возвращает новые веса, как и ожидалось, можно проверить размер каждого компонента:

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

Реализация нормального уравнения в Python

Это уравнение можно реализовать на Python и использовать тот же пример из предыдущей статьи.

def NormalEquation(X, Y):
  """
    Inputs:
      X: array of input values | (n samples, num features)
      Y: array of expected outputs | (n samples, 1)
      
    Output:
      returns the optimized weights | (num features, 1)
  """
  
  return torch.inverse(X.T @ X) @ X.T @ Y

После создания функции все, что необходимо, — это некоторые входные данные, которые генерируются ниже:

import torch

torch.manual_seed(5)
torch.set_printoptions(precision=2)

# (n samples, features)
X = torch.randint(low=0, high=11, size=(20, 1))

# normal distribution with a mean of 0 and std of 1
normal = torch.distributions.Normal(loc=0, scale=1)

# generate output
Y = (1.5*X + 2) + normal.sample(X.shape)

# add bias column
X = torch.hstack((torch.ones(X.shape),X))

Их можно подключить к нормальному уравнению для создания оптимизированных весов:

w = NormalEquation(X, Y)
tensor([[1.97],
        [1.52]])

Эти веса почти идентичны функции чертежа. Вместо 2 и 1,5 уравнение выводит 1,97 и 1,52. Они не идеальны из-за случайности, добавленной к выходным данным. Кроме того, эти значения более точны, чем значения из предыдущей статьи, поскольку не нужно было выбирать скорость обучения и конкретное количество эпох.

Когда его использовать

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

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

В следующих двух статьях будут использоваться оба подхода. Следующая статья — Введение в машинное обучение в Python: множественная линейная регрессия.

Пожалуйста, не забудьте поставить лайк и подписаться! :)

Рекомендации

  1. Обзор нормального уравнения