Skip to content

Coding Linear Regression

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

Run in Google Colab

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

Задача оптимизации гиперпараметров

Существует несколько вариантов для выбора гипераметров линейной регрессии

  • Через прямые матричные операции
  • Через градиентны спуск

Я думаю важно знать о обоих подходов и как они реализуются

Реализация Матричных Операции

Линейная регрессия выражается следующей зависимостью:

\[y=X\theta+\epsilon\]
  • \(X\) — матрица объекты-признаки
  • \(y\) — вектор целевых значений

Cоответствующих:

  • \(X\), \(\theta\) — параметр линейной регрессии, \(\epsilon\) — некоторый шум

Наша задача, минимизировать среднеквадратическую ошибку между \(y\) и \(X\theta\) (используюя Least Squares Method)

Используя матричную формулеровку, следует выражение для \(\theta\) как:

\[X^Ty=X^TX\theta \rightarrow \theta=(X^TX)^{-1}X^Ty\]

Который нам дает оптимальный \(\theta\) в этой постоновке задачи, решая \(\theta\) мы можем делать предсказание \(y=X\theta+\epsilon\)

def linreg(X,y):
    lsm = inv(np.dot(X.T,X))
    Xt = np.dot(X.T,y)
    theta = np.dot(lsm,Xt)
    return theta

Реализация Градиентного Спуска

Алтернативный подход для решения этой задачи (оптимизации гиперпараметров) является с методом градиентного спуска

Для реализации линейной регрессии с помощью методов оптимизации будем использовать функцию ошибки среднего квадратичного, которая является выпуклой функцией в n-мерном пространстве \(\mathbb{R}^n\) и в общем виде выглядит следующим образом:

\[MSE = \frac{1}{n} * \sum_{i=1}^{n}{(y_i - a(x_i))^2}\]
  • \(x_i\) — вектор-признак \(i\)-го объекта обучающей выборки
  • \(y_i\) — истинное значение для \(i\)-го объекта,
  • \(a(x)\) — алгоритм, предсказывающий для данного объекта \(x\) целевое значение
  • \(n\) — кол-во объектов в выборке

В случае линейной регрессии \(MSE\) представляется как:

\[MSE(X, y, \theta) = \frac{1}{2n} * \sum_{i=1}^{n}{(y_i - \theta^Tx_i)^2} = \frac{1}{2n} \lVert{y - X\theta}\rVert_{2}^{2}=\frac{1}{2n} (y - X\theta)^T(y - X\theta)\]
  • \(\theta\) — параметр модели линейной регрессии
  • \(X\) — матрица объекты-признаки
  • \(y\) - вектор истинных значений, соответствующих \(X\)

Возьмем первый вариант представления функции ошибки и посчитаем ее градиент по параметру \(\theta\), предварительно переименовав \(MSE\) в \(L\):

\[L=\frac{1}{2n} * \sum_{i=1}^{n}{(y_i - \theta^Tx_i)^2}\]
\[\nabla L = \frac{1}{n}\sum_{i=1}^{n}{(\theta^Tx_i - y_i) \cdot x_i} = \frac{1}{n}X^T(X\theta - y)\]
def mse_grad(X,theta):
    n = X.shape[0]
    grad = (1/n) * X.T.dot(X.dot(theta) - y)
    return grad

Нам еще нужны функции для шага градиентного спуска и оптимизационный цикл

# шаг градиентного спуска
def grad_step(theta,theta_grad,alpha):
    return theta - alpha*theta_grad

# оптимизационный цикл
def optimise(X,theta,n_iters):

    # theta0
    theta = start_theta.copy()

    # оптимизационный цикл
    for i in range(n_iters):
        theta_grad = mse_grad(X,theta)
        theta = grad_step(theta,theta_grad,alpha)

    return theta

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

Регуляризация

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

  • Для L1 нормалтзации: Регуляризация также штрафует большие коэффициенты, и может свести их к нулю!
  • Для L2 нормалтзации: Регуляризация штрафует большие коэффициенты, но не обнуляет их, а лишь делает их ближе к нулю. Это помогает уменьшить вариативность модели и сделать её более стабильной

Рассмотрим как это можно реализовать при использования градиентного спуска

После добавления регуляризации функция ошибки линейной регрессии будет выглядеть следующим образом:

\[L=\frac{1}{2n} * \sum_{i=1}^{n}{(y_i - \theta^Tx_i)^2} + \frac{\lambda}{2m}\sum_{j}^{m}{\theta_j^2}\]

А ее градиент по параметру \(\theta\):

\[\nabla L = \frac{1}{n}\sum_{i=1}^{n}{(\theta^Tx_i - y_i) \cdot x_i} + \frac{\lambda}{m}\sum_{j=1}^{m}{\theta_j} = \frac{1}{n}X^T(X\theta - y) + \frac{\lambda}{m}\sum_{j=1}^{m}{\theta_j}\]

Что мы делаем, мы добавляем дополнительный термин который пропорционален квадрату величины коэффициентов в квадратную функцию потерь (на подобие RidgeRegressor)

# градиент квадратной функции потерь
def mse_grad_reg(X,theta):
    n = X.shape[0]
    grad = (1/n) * X.T.dot(X.dot(theta) - y) # стандартный градиент функции потерь
    grad_temp = lambd * np.mean(theta)  # дополнительный термин
    return grad + grad_temp

Предсказание с LinearRegression

Имея оптимизированные параметры \(\theta\), мы можем делаить предсказания используя функцию

def predict(X,theta):
     y_pred = X.dot(self.theta)

Подведем Итоги

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

Для решения задачи выбора гиперпараметров линейной регресии с помошью градиентного спуска нам нужно знать градиент среднеквадратической ошибки, так же и для логистической регресиии нам нужно знать градиент бинарной кросс энтропии, это пожалуй остновной момент. Зная формулы для \(\frac{dL}{d\theta}\), мы можем воспользоваться градиентном спуском для оптимизации гиперпараметров.

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