Skip to content

Coding Logistic Regression

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

Run in Google Colab

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

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

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

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

Начнем с функции сигмойда, sigmoid функция:

\[h_{\theta}(x)=\frac{1}{1+\exp^{-\theta x}},\]
  • \(\theta\) — вектор параметров логистической регрессии
  • \(x\) - вектор признаков объекта из выборки

Функция ошибки для логистической регрессии в случае бинарной классификации называется бинарной кросс-энтропией (log loss) и записывается следующим образом:

\[L=-\frac{1}{n}(y_i \log h_{\theta}(x_i) + (1-y_i) \log(1-h_{\theta}(x_i)))\]

Для геализации градиентного спуска, нам нудем градиент функции потерь

Соответствующий градиент функции ошибки равен:

\[\nabla L=\frac{1}{n}\sum_{i=1}^{n}{(h_{\theta}(x_i)-y_i)x_i}\]

Реализуем функцию сигмойда

# Функция Сигмойда
def sigmoid(X, theta):
    return 1.0 / (1.0 + np.exp(-X.dot(theta)))

Функция ошибки logloss, разница с регрессии только в sigmoid(X,theta), в регресии используем X.theta

# Градиент бинарной кросс энтропии
# sigmoid(X,theta) - классификации
# X.theta - регрессии
def bin_crossentropy_grad(X, y, theta):
    n = X.shape[0]
    grad = (1.0/n) * X.T.dot(sigmoid(X,theta) - y )
    return grad

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

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

# Главная функция 
def optimize(X, y, grad_func, start_theta, alpha, n_iters):

    theta = start_theta.copy()

    for i in range(n_iters):
        theta_grad = grad_func(X, y, theta)
        theta = gradient_step(theta, theta_grad, alpha)

    return theta

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

Процесс идентичный, как и в линейной регрессии, мы добавляем дополнительный термин в функцию потерь, в этот раз это бинарная кросс энтропия (logloss)

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

\[L=-\frac{1}{n}(y_i \log h_{\theta}(x_i) + (1-y_i) \log(1-h_{\theta}(x_i)))+\frac{\lambda}{2m}\sum_{j}^{m}{\theta_j^2}\]
  • \(x_i\) — вектор признаков
  • \(i\)-го примера из обучающей выборки
  • \(y_i\) — истинный класс для соответствующего примера (0 или 1),- \(n\) — число примеров в обучающей выборке
  • \(m\) — количество нефиктивных признаков
  • \(\lambda\) — параметр регуляризации
  • \(h_{\theta}(x)\) — sigmoid функция, равная:
\[h_{\theta}(x)=\frac{1}{1+\exp^{-\theta x}}\]
  • \(\theta\) — вектор параметров логистической регрессии
  • \(x\) - вектор признаков объекта из выборки

Соответствующий градиент функции ошибки равен:

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

И ее реализация:

# градиент бинарной кросс энтропии
def logloss_reg(X,y,theta):
   n = X.shape[0]
   grad = (1.0/n) * X.T.dot(sigmoid(X,theta) - y)
   grad_term = lambd * np.mean(theta)
   return grad + grad_term

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

Найдя оптимальные гиперпараметры \(\theta\) , мы можем делать предсказания с помошью функции сигмойды. Она дает нам вероятности принадлежности к положительному классу в диапазоне [0,1]. Мы можем как и в sklearn анолагично именовать их predict & predict_proba

# Вероятность принадлежности класса (0/1)
def predict_proba(X,theta):
   return self.sigmoid(X,theta)

# Предсказание с порогом 0.5
def predict(X):
   y_pred = self.predict_proba(X) > 0.5

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

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

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

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