Руководство для лучшего понимания моделей линейной регрессии с помощью Python и SciKit-Learn

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

Линейные модели делают прогноз, используя линейную функцию входных функций. Здесь мы рассмотрим некоторые популярные линейные модели в Scikit-Learn.

Полную записную книжку Jupyter можно найти здесь.

Данные

Здесь мы воспользуемся набором данных SciKit-Learn о диабете для обзора некоторых популярных алгоритмов линейной регрессии.

Набор данных содержит 10 характеристик (которые уже были центрированы по среднему и масштабированы) и целевое значение: показатель прогрессирования заболевания через год после исходного уровня.

Импортируем данные и готовимся к моделированию:

from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
# load regression dataset
diabetes, target = load_diabetes(return_X_y=True)
diabetes = pd.DataFrame(diabetes)
# Prepare data for modeling
# Separate input features and target
y = target
X = diabetes
# setting up testing and training sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=27)

Оценочная метрика: R²

R-квадрат, или коэффициент детерминации, показывает, насколько велика дисперсия целевой переменной, которая объясняется нашей моделью.

Значения могут находиться в диапазоне от 0 до 1. Более высокие значения указывают на модель с высоким уровнем прогнозирования. Например, значение R² 0,80 означает, что на модель приходится 80% изменчивости данных.

Как правило, чем выше значение R², тем лучше. Низкие значения указывают на то, что наша модель не очень хорошо предсказывает цель. Однако есть одно предостережение: очень высокий R² может быть признаком переобучения.

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

from sklearn.model_selection import cross_val_score
# function to get cross validation scores
def get_cv_scores(model):
    scores = cross_val_score(model,
                             X_train,
                             y_train,
                             cv=5,
                             scoring='r2')
    
    print('CV Mean: ', np.mean(scores))
    print('STD: ', np.std(scores))
    print('\n')

Линейная регрессия (обыкновенные наименьшие квадраты)

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

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

Чтобы сгенерировать линейную регрессию, мы используем класс LinearRegression Scikit-Learn:

from sklearn.linear_model import LinearRegression
# Train model
lr = LinearRegression().fit(X_train, y_train)
# get cross val scores
get_cv_scores(lr)
[out]
### CV Mean:  0.4758231204137221
### STD:  0.1412116836029729

Мы получаем значение R² 0,48 и стандартное отклонение 0,14. Низкое значение R² указывает на то, что наша модель не очень точна. Значение стандартного отклонения указывает на то, что мы можем переоснастить обучающие данные.

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

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

Регуляризацию можно определить как явное ограничение модели для предотвращения переобучения.

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

Ридж-регрессия (регуляризация L2)

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

Мы контролируем сложность нашей модели с помощью параметра регуляризации ⍺.

Более высокие значения ⍺ заставляют коэффициенты двигаться к нулю и увеличивают ограничение на модель. Это снижает производительность обучения, но также увеличивает обобщаемость модели. Установка слишком большого значения ⍺ может привести к слишком простой модели, которая не соответствует данным.

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

Посмотрим, сможем ли мы повысить производительность с помощью класса Ridge Scikit-Learn:

from sklearn.linear_model import Ridge
# Train model with default alpha=1
ridge = Ridge(alpha=1).fit(X_train, y_train)
# get cross val scores
get_cv_scores(ridge)
[out]
### CV Mean:  0.3826248703036134
### STD:  0.09902564009167607

Средний балл R² 0,38 означает, что мы можем объяснить только 38% дисперсии с помощью нашей модели регрессии хребта - определенно не улучшение по сравнению с линейной регрессией, описанной выше. Однако наше стандартное отклонение уменьшилось, что говорит о меньшей вероятности переобучения.

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

Посмотрим, сможем ли мы улучшить показатель R², изменив значение альфа. Мы воспользуемся поиском по сетке, чтобы найти оптимальное значение альфа:

# find optimal alpha with grid search
alpha = [0.001, 0.01, 0.1, 1, 10, 100, 1000]
param_grid = dict(alpha=alpha)
grid = GridSearchCV(estimator=ridge, param_grid=param_grid, scoring='r2', verbose=1, n_jobs=-1)
grid_result = grid.fit(X_train, y_train)
print('Best Score: ', grid_result.best_score_)
print('Best Params: ', grid_result.best_params_)
[out]
### Best Score:  0.4883436188936269
### Best Params:  {'alpha': 0.01}

Наш рейтинг R² увеличился за счет оптимизации для альфа-версии! Однако рейтинг R² 0,48 все еще не очень хороший результат. Посмотрим, сможем ли мы улучшить это дальше, используя другие типы регуляризации.

Регрессия лассо (регуляризация L1)

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

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

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

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

Давайте попробуем регрессию Лассо на нашем наборе данных о диабете:

from sklearn.linear_model import Lasso
# Train model with default alpha=1
lasso = Lasso(alpha=1).fit(X_train, y_train)
# get cross val scores
get_cv_scores(lasso)
[out]
### CV Mean:  0.3510033961713952
### STD:  0.08727927390128883

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

Давайте посмотрим, как можно улучшить показатель R², изменив значение альфа. Мы воспользуемся поиском по сетке, чтобы найти оптимальное значение альфа:

# find optimal alpha with grid search
alpha = [0.001, 0.01, 0.1, 1, 10, 100, 1000]
param_grid = dict(alpha=alpha)
grid = GridSearchCV(estimator=lasso, param_grid=param_grid, scoring='r2', verbose=1, n_jobs=-1)
grid_result = grid.fit(X_train, y_train)
print('Best Score: ', grid_result.best_score_)
print('Best Params: ', grid_result.best_params_)
[out]
### Best Score:  0.48813139496070573
### Best Params:  {'alpha': 0.01}

Наш рейтинг улучшился за счет оптимизации альфа-версии!

Мы можем проверить коэффициенты, чтобы увидеть, были ли они установлены на ноль:

# match column names to coefficients
for coef, col in enumerate(X_train.columns):
    print(f'{col}:  {lasso.coef_[coef]}')
[out]
age:  20.499547879943435
sex:  -252.36006394772798
bmi:  592.1488111417586
average_bp:  289.434686266713
s1:  -195.9273869617746
s2:  0.0
s3:  -96.91157736328506
s4:  182.01914264519363
s5:  518.6445047270033
s6:  63.76955009503193

Коэффициент для s2 равен нулю. Модель полностью игнорирует это!

Мы попробуем последний тип регрессии, чтобы посмотреть, сможем ли мы еще больше улучшить показатель R².

Регрессия эластичной сети

Elastic-net - это модель линейной регрессии, которая сочетает в себе штрафы Лассо и Риджа.

Мы используем параметр l1_ratio для управления комбинацией регуляризации L1 и L2. Когда l1_ratio = 0 у нас есть регуляризация L2 (Ridge), а когда l1_ratio = 1, у нас есть регуляризация L1 (Lasso). Значения от нуля до единицы дают нам комбинацию регуляризации L1 и L2.

Сначала мы подбираем эластичную сетку с параметрами по умолчанию, а затем используем поиск по сетке, чтобы найти оптимальные значения для alpha и l1_ratio:

from sklearn.linear_model import ElasticNet
# Train model with default alpha=1 and l1_ratio=0.5
elastic_net = ElasticNet(alpha=1, l1_ratio=0.5).fit(X_train, y_train)
# get cross val scores
get_cv_scores(elastic_net)
[out]
### CV Mean:  -0.05139208284143739
### STD:  0.07297997198698156
# find optimal alpha with grid search
alpha = [0.001, 0.01, 0.1, 1, 10, 100, 1000]
l1_ratio = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
param_grid = dict(alpha=alpha, l1_ratio=l1_ratio)
grid = GridSearchCV(estimator=elastic_net, param_grid=param_grid, scoring='r2', verbose=1, n_jobs=-1)
grid_result = grid.fit(X_train, y_train)
print('Best Score: ', grid_result.best_score_)
print('Best Params: ', grid_result.best_params_)
[out]
### Best Score:  0.48993062619187755
### Best Params:  {'alpha': 0.001, 'l1_ratio': 0.8}

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

Заключение

Мы исследовали четыре различные линейные модели регрессии:

  • Линейная регрессия
  • Хребет
  • Лассо
  • Эластичная сетка

Мы упростили нашу модель с помощью регуляризации. К сожалению, наша оценка R² остается низкой.

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