Что такое регрессионный анализ и как он работает?

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

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

Формулы и математические основы регрессии

В самом общем виде задача регрессии формулируется следующим образом:

имея набор наблюдений (x₁, y₁), (x₂, y₂), …, (xₙ, yₙ),

мы хотим найти функцию f, такую что y ≈ f(x) для новых наблюдений x.

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

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

f(x) = β₀ + β₁x₁ + β₂x₂ + … + βₚxₚ

где β₀, β₁, β₂, …, βₚ — параметры модели (коэффициенты регрессии), которые нужно оценить по имеющимся данным.

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

min Σ(y_i — f(x_i))²

Этот метод математически обоснован и имеет ряд оптимальных статистических свойств при определенных предположениях.

Терминология регрессионного анализа

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

  • Зависимая переменная (target, outcome, response) — это переменная, значение которой мы хотим предсказать или объяснить. Обычно обозначается как y;
  • Независимые переменные (features, predictors, explanatory variables) — это переменные, которые используются для предсказания зависимой переменной. Обозначаются как x₁, x₂, …, xₚ;
  • Коэффициенты регрессии — параметры модели, которые показывают, как изменяется зависимая переменная при изменении соответствующей независимой переменной на единицу (при условии, что остальные независимые переменные остаются неизменными);
  • Свободный член (intercept) — значение, которое принимает функция регрессии, когда все независимые переменные равны нулю;
  • Остатки (residuals) — разницы между фактическими и предсказанными значениями зависимой переменной;
  • Коэффициент детерминации (R²) — статистический показатель, характеризующий долю дисперсии зависимой переменной, объясненную моделью.

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

Виды регрессионного анализа

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

Линейная регрессия

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

Простая линейная регрессия работает с одной независимой переменной и имеет вид:

y = β₀ + β₁x + ε

где

  • β₀ — свободный член;
  • β₁ — коэффициент наклона;
  • ε — случайная ошибка.

Множественная линейная регрессия включает несколько независимых переменных:

y = β₀ + β₁x₁ + β₂x₂ + … + βₚxₚ + ε

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

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

Рассмотрим пример реализации линейной регрессии на Python с использованием библиотеки scikit-learn:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score

# Создаем синтетические данные
np.random.seed(42)
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)

# Разделяем данные на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Обучаем модель
model = LinearRegression()
model.fit(X_train, y_train)

# Получаем предсказания на тестовой выборке
y_pred = model.predict(X_test)

# Оцениваем качество модели
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)

print(f"Коэффициент: {model.coef_[0][0]:.4f}")
print(f"Свободный член: {model.intercept_[0]:.4f}")
print(f"Среднеквадратичная ошибка: {mse:.4f}")
print(f"Коэффициент детерминации (R²): {r2:.4f}")

# Визуализируем результаты
plt.figure(figsize=(10, 6))
plt.scatter(X_test, y_test, color='blue', label='Фактические значения')
plt.plot(X_test, y_pred, color='red', linewidth=2, label='Предсказания')
plt.title('Линейная регрессия')
plt.xlabel('X')
plt.ylabel('y')
plt.legend()
plt.grid(True)
plt.show()

Коэффициент: 2.7993
Свободный член: 4.1429
Среднеквадратичная ошибка: 0.6537
Коэффициент детерминации (R²): 0.8072

График линейной регрессии

Рис. 1: График линейной регрессии

Этот код демонстрирует полный цикл работы с линейной регрессией: от создания данных до оценки качества модели и визуализации результатов.

Читайте также:  Прогнозирование временных рядов с помощью ARIMA, SARIMA, ARFIMA

Полиномиальная регрессия

Когда зависимость между переменными не является линейной, на помощь приходит полиномиальная регрессия. Она позволяет моделировать нелинейные отношения, используя полиномы различных степеней:

y = β₀ + β₁x + β₂x² + … + βₚxᵖ + ε

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

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

Рассмотрим пример полиномиальной регрессии на Python:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score

# Создаем нелинейные синтетические данные
np.random.seed(42)
X = 6 * np.random.rand(200, 1) - 3
y = 0.5 * X**3 - X**2 + 2 * X + 2 + np.random.randn(200, 1) * 2

# Разделяем данные на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Создаем модели с разными степенями полинома
degrees = [1, 3, 5, 7]
plt.figure(figsize=(14, 10))

for i, degree in enumerate(degrees):
    # Создаем пайплайн: полиномиальное преобразование + линейная регрессия
    model = Pipeline([
        ('poly', PolynomialFeatures(degree=degree, include_bias=False)),
        ('linear', LinearRegression())
    ])
    
    # Обучаем модель
    model.fit(X_train, y_train)
    
    # Получаем предсказания
    y_pred = model.predict(X_test)
    
    # Оцениваем качество
    mse = mean_squared_error(y_test, y_pred)
    r2 = r2_score(y_test, y_pred)
    
    # Сортируем данные для плавной визуализации
    X_sorted = np.sort(X_test, axis=0)
    y_sorted_pred = model.predict(X_sorted)
    
    # Визуализируем результаты
    plt.subplot(2, 2, i+1)
    plt.scatter(X_test, y_test, color='blue', s=20, alpha=0.6, label='Фактические значения')
    plt.plot(X_sorted, y_sorted_pred, color='red', linewidth=2, label=f'Полином степени {degree}')
    plt.title(f'Полиномиальная регрессия (степень {degree})')
    plt.text(X_test.min(), y_test.max() * 0.9, f'MSE: {mse:.2f}\nR²: {r2:.2f}')
    plt.xlabel('X')
    plt.ylabel('y')
    plt.legend()
    plt.grid(True)

plt.tight_layout()
plt.show()

Графики полиномиальной регрессии с разной степенью полинома

Рис. 2: Графики полиномиальной регрессии с разной степенью полинома

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

Регрессия через гребневую регуляризацию (Ridge Regression)

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

min (Σ(y_i — (β₀ + β₁x₁_i + … + βₚxₚ_i))² + λΣβ_j²)

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

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

Рассмотрим пример использования Ridge регрессии с подбором оптимального параметра регуляризации:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import Ridge, LinearRegression
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.datasets import make_regression

# Создаем данные с мультиколлинеарностью
np.random.seed(42)
X, y = make_regression(n_samples=200, n_features=20, n_informative=10, 
                       noise=50, random_state=42)

# Создаем мультиколлинеарность, делая некоторые признаки линейными комбинациями других
X[:, 5] = X[:, 0] * 0.5 + X[:, 1] * 0.5 + np.random.normal(0, 0.01, size=X.shape[0])
X[:, 15] = X[:, 10] * 0.3 + X[:, 11] * 0.7 + np.random.normal(0, 0.01, size=X.shape[0])

# Разделяем данные на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Создаем пайплайны для обычной линейной регрессии и Ridge
pipe_lr = Pipeline([
    ('scaler', StandardScaler()),
    ('model', LinearRegression())
])

pipe_ridge = Pipeline([
    ('scaler', StandardScaler()),
    ('model', Ridge())
])

# Настраиваем сетку параметров для Ridge
param_grid = {
    'model__alpha': np.logspace(-3, 3, 7)  # Проверяем значения от 0.001 до 1000
}

# Выполняем поиск по сетке с кросс-валидацией
grid_search = GridSearchCV(pipe_ridge, param_grid, cv=5, scoring='neg_mean_squared_error')
grid_search.fit(X_train, y_train)

# Получаем лучшую модель и параметры
best_ridge = grid_search.best_estimator_
best_alpha = grid_search.best_params_['model__alpha']

# Обучаем обычную линейную регрессию
pipe_lr.fit(X_train, y_train)

# Получаем предсказания
y_pred_lr = pipe_lr.predict(X_test)
y_pred_ridge = best_ridge.predict(X_test)

# Оцениваем качество
mse_lr = mean_squared_error(y_test, y_pred_lr)
r2_lr = r2_score(y_test, y_pred_lr)
mse_ridge = mean_squared_error(y_test, y_pred_ridge)
r2_ridge = r2_score(y_test, y_pred_ridge)

print(f"Линейная регрессия - MSE: {mse_lr:.2f}, R²: {r2_lr:.4f}")
print(f"Ridge регрессия (alpha={best_alpha:.4f}) - MSE: {mse_ridge:.2f}, R²: {r2_ridge:.4f}")

# Извлекаем коэффициенты
coef_lr = pipe_lr.named_steps['model'].coef_
coef_ridge = best_ridge.named_steps['model'].coef_

# Визуализируем коэффициенты
plt.figure(figsize=(12, 6))
plt.bar(np.arange(len(coef_lr)) - 0.2, coef_lr, width=0.4, label='Линейная регрессия')
plt.bar(np.arange(len(coef_ridge)) + 0.2, coef_ridge, width=0.4, label='Ridge регрессия')
plt.axhline(y=0, color='k', linestyle='-', alpha=0.3)
plt.xlabel('Признаки')
plt.ylabel('Коэффициенты')
plt.title('Сравнение коэффициентов линейной и Ridge регрессий')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

Линейная регрессия — MSE: 2356.75, R²: 0.9078
Ridge регрессия (alpha=1.0000) — MSE: 2453.20, R²: 0.9040

Сравнение коэффициентов линейной и Ridge регрессий

Рис. 3: Сравнение коэффициентов линейной и Ridge регрессий

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

Лассо-регрессия (Lasso Regression)

Лассо-регрессия (Least Absolute Shrinkage and Selection Operator) — это еще один метод регуляризации, который использует L1-норму вместо L2:

Читайте также:  Оптимизация цепочек поставок с помощью машинного обучения

min (Σ(y_i — (β₀ + β₁x₁_i + … + βₚxₚ_i))² + λΣ|β_j|)

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

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

Рассмотрим пример реализации Lasso на Python:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import Lasso, LinearRegression
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.datasets import make_regression

# Создаем данные с большим количеством признаков, но малым числом информативных
np.random.seed(42)
X, y = make_regression(n_samples=200, n_features=50, n_informative=10, 
                       noise=50, random_state=42)

# Разделяем данные на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Создаем пайплайны для обычной линейной регрессии и Lasso
pipe_lr = Pipeline([
    ('scaler', StandardScaler()),
    ('model', LinearRegression())
])

pipe_lasso = Pipeline([
    ('scaler', StandardScaler()),
    ('model', Lasso(max_iter=10000))
])

# Настраиваем сетку параметров для Lasso
param_grid = {
    'model__alpha': np.logspace(-4, 1, 6)  # Проверяем значения от 0.0001 до 10
}

# Выполняем поиск по сетке с кросс-валидацией
grid_search = GridSearchCV(pipe_lasso, param_grid, cv=5, scoring='neg_mean_squared_error')
grid_search.fit(X_train, y_train)

# Получаем лучшую модель и параметры
best_lasso = grid_search.best_estimator_
best_alpha = grid_search.best_params_['model__alpha']

# Обучаем обычную линейную регрессию
pipe_lr.fit(X_train, y_train)

# Получаем предсказания
y_pred_lr = pipe_lr.predict(X_test)
y_pred_lasso = best_lasso.predict(X_test)

# Оцениваем качество
mse_lr = mean_squared_error(y_test, y_pred_lr)
r2_lr = r2_score(y_test, y_pred_lr)
mse_lasso = mean_squared_error(y_test, y_pred_lasso)
r2_lasso = r2_score(y_test, y_pred_lasso)

print(f"Линейная регрессия - MSE: {mse_lr:.2f}, R²: {r2_lr:.4f}")
print(f"Lasso регрессия (alpha={best_alpha:.6f}) - MSE: {mse_lasso:.2f}, R²: {r2_lasso:.4f}")

# Извлекаем коэффициенты
coef_lr = pipe_lr.named_steps['model'].coef_
coef_lasso = best_lasso.named_steps['model'].coef_

# Подсчитываем ненулевые коэффициенты
non_zero_lasso = np.sum(coef_lasso != 0)
print(f"Количество ненулевых коэффициентов в Lasso: {non_zero_lasso} из {len(coef_lasso)}")

# Визуализируем коэффициенты
plt.figure(figsize=(15, 6))
plt.subplot(1, 2, 1)
plt.stem(coef_lr, markerfmt='ro', linefmt='r-', basefmt='k-')
plt.title('Коэффициенты линейной регрессии')
plt.xlabel('Признаки')
plt.ylabel('Значение коэффициента')
plt.grid(True, alpha=0.3)

plt.subplot(1, 2, 2)
plt.stem(coef_lasso, markerfmt='bo', linefmt='b-', basefmt='k-')
plt.title(f'Коэффициенты Lasso (alpha={best_alpha:.6f})')
plt.xlabel('Признаки')
plt.ylabel('Значение коэффициента')
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

Линейная регрессия — MSE: 3980.56, R²: 0.6940
Lasso регрессия (alpha=1.000000) — MSE: 3407.17, R²: 0.7381
Количество ненулевых коэффициентов в Lasso: 39 из 50

Сравнение коэффициентов линейной и Lasso регрессии после обучения моделей

Рис. 4: Сравнение коэффициентов линейной и Lasso регрессии после обучения моделей

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

Эластичная сеть (Elastic Net)

Elastic Net представляет собой комбинацию Ridge и Lasso регрессий, используя и L1, и L2 регуляризацию одновременно:

min (Σ(y_i — (β₀ + β₁x₁_i + … + βₚxₚ_i))² + λ₁Σ|β_j| + λ₂Σβ_j²)

Это позволяет объединить преимущества обоих методов: способность Lasso к отбору признаков и способность Ridge справляться с группами коррелированных переменных.

Эластичная сеть особенно полезна, когда в данных присутствует большое количество коррелированных признаков. В этом случае Lasso имеет тенденцию произвольно выбирать один признак из группы коррелированных, а Ridge сохраняет их все. Elastic Net находит баланс между этими подходами.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import ElasticNet, Lasso, Ridge
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.datasets import make_regression
import warnings
from sklearn.exceptions import ConvergenceWarning
warnings.filterwarnings("ignore", category=ConvergenceWarning)

# Создаем данные с коррелированными признаками
np.random.seed(42)
X, y = make_regression(n_samples=200, n_features=30, n_informative=10, 
                       noise=50, random_state=42)

# Создаем группы коррелированных признаков
for i in range(5):
    X[:, i*2+10] = X[:, i] * 0.8 + X[:, i+1] * 0.2 + np.random.normal(0, 0.01, size=X.shape[0])
    X[:, i*2+11] = X[:, i] * 0.2 + X[:, i+1] * 0.8 + np.random.normal(0, 0.01, size=X.shape[0])

# Разделяем данные на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Создаем пайплайны для Ridge, Lasso и ElasticNet
pipe_ridge = Pipeline([
    ('scaler', StandardScaler()),
    ('model', Ridge())
])

pipe_lasso = Pipeline([
    ('scaler', StandardScaler()),
    ('model', Lasso(max_iter=10000))
])

pipe_elastic = Pipeline([
    ('scaler', StandardScaler()),
    ('model', ElasticNet(max_iter=10000))
])

# Настраиваем сетки параметров
param_grid_ridge = {
    'model__alpha': np.logspace(-3, 2, 6)
}

param_grid_lasso = {
    'model__alpha': np.logspace(-3, 2, 6)
}

param_grid_elastic = {
    'model__alpha': np.logspace(-3, 2, 6),
    'model__l1_ratio': [0.1, 0.3, 0.5, 0.7, 0.9]
}

# Выполняем поиск по сеткам с кросс-валидацией
grid_ridge = GridSearchCV(pipe_ridge, param_grid_ridge, cv=5, scoring='neg_mean_squared_error')
grid_ridge.fit(X_train, y_train)

grid_lasso = GridSearchCV(pipe_lasso, param_grid_lasso, cv=5, scoring='neg_mean_squared_error')
grid_lasso.fit(X_train, y_train)

grid_elastic = GridSearchCV(pipe_elastic, param_grid_elastic, cv=5, scoring='neg_mean_squared_error')
grid_elastic.fit(X_train, y_train)

# Получаем лучшие модели и параметры
best_ridge = grid_ridge.best_estimator_
best_alpha_ridge = grid_ridge.best_params_['model__alpha']

best_lasso = grid_lasso.best_estimator_
best_alpha_lasso = grid_lasso.best_params_['model__alpha']

best_elastic = grid_elastic.best_estimator_
best_alpha_elastic = grid_elastic.best_params_['model__alpha']
best_l1_ratio = grid_elastic.best_params_['model__l1_ratio']

# Получаем предсказания
y_pred_ridge = best_ridge.predict(X_test)
y_pred_lasso = best_lasso.predict(X_test)
y_pred_elastic = best_elastic.predict(X_test)

# Оцениваем качество
mse_ridge = mean_squared_error(y_test, y_pred_ridge)
r2_ridge = r2_score(y_test, y_pred_ridge)

mse_lasso = mean_squared_error(y_test, y_pred_lasso)
r2_lasso = r2_score(y_test, y_pred_lasso)

mse_elastic = mean_squared_error(y_test, y_pred_elastic)
r2_elastic = r2_score(y_test, y_pred_elastic)

print(f"Ridge (alpha={best_alpha_ridge:.4f}) - MSE: {mse_ridge:.2f}, R²: {r2_ridge:.4f}")
print(f"Lasso (alpha={best_alpha_lasso:.4f}) - MSE: {mse_lasso:.2f}, R²: {r2_lasso:.4f}")
print(f"ElasticNet (alpha={best_alpha_elastic:.4f}, l1_ratio={best_l1_ratio:.1f}) - MSE: {mse_elastic:.2f}, R²: {r2_elastic:.4f}")

# Извлекаем коэффициенты
coef_ridge = best_ridge.named_steps['model'].coef_
coef_lasso = best_lasso.named_steps['model'].coef_
coef_elastic = best_elastic.named_steps['model'].coef_

# Подсчитываем ненулевые коэффициенты
non_zero_ridge = np.sum(np.abs(coef_ridge) > 1e-5)
non_zero_lasso = np.sum(np.abs(coef_lasso) > 1e-5)
non_zero_elastic = np.sum(np.abs(coef_elastic) > 1e-5)

print(f"Ненулевые коэффициенты - Ridge: {non_zero_ridge}, Lasso: {non_zero_lasso}, ElasticNet: {non_zero_elastic}")

# Визуализируем коэффициенты
plt.figure(figsize=(15, 10))
plt.subplot(3, 1, 1)
plt.stem(coef_ridge, markerfmt='ro', linefmt='r-', basefmt='k-')
plt.title(f'Коэффициенты Ridge (alpha={best_alpha_ridge:.4f})')
plt.grid(True, alpha=0.3)

plt.subplot(3, 1, 2)
plt.stem(coef_lasso, markerfmt='go', linefmt='g-', basefmt='k-')
plt.title(f'Коэффициенты Lasso (alpha={best_alpha_lasso:.4f})')
plt.grid(True, alpha=0.3)

plt.subplot(3, 1, 3)
plt.stem(coef_elastic, markerfmt='bo', linefmt='b-', basefmt='k-')
plt.title(f'Коэффициенты ElasticNet (alpha={best_alpha_elastic:.4f}, l1_ratio={best_l1_ratio:.1f})')
plt.xlabel('Признаки')
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()
Ridge (alpha=0.0100) - MSE: 10366.79, R²: 0.5794
Lasso (alpha=1.0000) - MSE: 9755.48, R²: 0.6042
ElasticNet (alpha=0.0010, l1_ratio=0.9) - MSE: 10233.01, R²: 0.5849
Ненулевые коэффициенты - Ridge: 30, Lasso: 19, ElasticNet: 30

Сравнение коэффициентов регрессий типа Ridge, Lasso, ElasticNet

Рис. 5: Сравнение коэффициентов регрессий типа Ridge, Lasso, ElasticNet

Данный график наглядно показывает, как три различных подхода к регуляризации влияют на коэффициенты модели. ElasticNet часто находит компромисс между Ridge и Lasso, сохраняя группы коррелированных признаков и при этом обнуляя совсем незначимые.

Читайте также:  Создание ML-модели прогноза действий пользователей интернет-магазина и рекомендательной системы

Логистическая регрессия

Хотя название включает слово «регрессия», логистическая регрессия на самом деле является методом классификации. Тем не менее, она тесно связана с линейной регрессией и часто рассматривается в рамках регрессионного анализа.

Логистическая регрессия моделирует вероятность того, что зависимая переменная принимает определенное значение (обычно бинарное: 0 или 1), исходя из значений независимых переменных.

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

P(y=1|x) = 1 / (1 + e^-(β₀ + β₁x₁ + β₂x₂ + … + βₚxₚ))

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

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix, roc_curve, roc_auc_score
from sklearn.datasets import make_classification

# Создаем синтетические данные для бинарной классификации
np.random.seed(42)
X, y = make_classification(n_samples=1000, n_features=5, n_informative=3, 
                          n_redundant=1, n_classes=2, weights=[0.7, 0.3],
                          random_state=42)

# Разделяем данные на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=42)

# Стандартизируем признаки
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# Создаем и обучаем модель логистической регрессии
model = LogisticRegression(random_state=42, max_iter=1000)
model.fit(X_train_scaled, y_train)

# Получаем предсказания (как классы, так и вероятности)
y_pred = model.predict(X_test_scaled)
y_prob = model.predict_proba(X_test_scaled)[:, 1]

# Оцениваем качество модели
accuracy = accuracy_score(y_test, y_pred)
class_report = classification_report(y_test, y_pred)
conf_matrix = confusion_matrix(y_test, y_pred)
roc_auc = roc_auc_score(y_test, y_prob)

print(f"Точность: {accuracy:.4f}")
print(f"ROC AUC: {roc_auc:.4f}")
print("\nОтчет о классификации:")
print(class_report)
print("\nМатрица ошибок:")
print(conf_matrix)

# Получаем коэффициенты модели
coef = model.coef_[0]
intercept = model.intercept_[0]
feature_names = [f'Признак {i+1}' for i in range(X.shape[1])]

# Визуализируем коэффициенты
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.barh(feature_names, coef)
plt.title('Коэффициенты логистической регрессии')
plt.xlabel('Значение коэффициента')
plt.axvline(x=0, color='k', linestyle='--', alpha=0.3)
plt.grid(True, alpha=0.3)

# Строим ROC-кривую
plt.subplot(1, 2, 2)
fpr, tpr, _ = roc_curve(y_test, y_prob)
plt.plot(fpr, tpr, color='blue', lw=2, label=f'ROC кривая (AUC = {roc_auc:.3f})')
plt.plot([0, 1], [0, 1], color='gray', linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('Частота ложноположительных результатов')
plt.ylabel('Частота истинно положительных результатов')
plt.title('ROC-кривая')
plt.legend(loc='lower right')
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# Визуализируем решающую границу (для двух наиболее значимых признаков)
if X.shape[1] >= 2:
    # Находим два наиболее важных признака
    top_features = np.argsort(np.abs(coef))[-2:]
    
    plt.figure(figsize=(10, 8))
    
    # Создаем сетку для построения границы решения
    h = 0.02  # шаг сетки
    x_min, x_max = X_test_scaled[:, top_features[0]].min() - 1, X_test_scaled[:, top_features[0]].max() + 1
    y_min, y_max = X_test_scaled[:, top_features[1]].min() - 1, X_test_scaled[:, top_features[1]].max() + 1
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    
    # Подготавливаем данные для предсказания
    Z_test = np.zeros((xx.shape[0], xx.shape[1], X.shape[1]))
    Z_test[:, :, top_features[0]] = xx
    Z_test[:, :, top_features[1]] = yy
    Z_test = Z_test.reshape(-1, X.shape[1])
    
    # Заполняем остальные признаки средними значениями
    for i in range(X.shape[1]):
        if i not in top_features:
            Z_test[:, i] = np.mean(X_test_scaled[:, i])
    
    # Получаем предсказания
    Z = model.predict_proba(Z_test)[:, 1]
    Z = Z.reshape(xx.shape)
    
    # Строим контурный график
    plt.contourf(xx, yy, Z, cmap=plt.cm.RdBu, alpha=0.7)
    
    # Наносим точки обучающей выборки
    scatter = plt.scatter(X_test_scaled[:, top_features[0]], X_test_scaled[:, top_features[1]], 
                c=y_test, cmap=plt.cm.RdBu_r, edgecolors='k', alpha=0.9)
    
    plt.xlim(xx.min(), xx.max())
    plt.ylim(yy.min(), yy.max())
    plt.title(f'Граница решения логистической регрессии\nПризнаки {top_features[0]+1} и {top_features[1]+1}')
    plt.xlabel(f'Признак {top_features[0]+1}')
    plt.ylabel(f'Признак {top_features[1]+1}')
    plt.colorbar(scatter)
    plt.grid(True, alpha=0.3)
    plt.show()
Точность: 0.9250
ROC AUC: 0.9557

Отчет о классификации:
              precision    recall  f1-score   support

           0       0.93      0.96      0.95       140
           1       0.91      0.83      0.87        60

    accuracy                           0.93       200
   macro avg       0.92      0.90      0.91       200
weighted avg       0.92      0.93      0.92       200


Матрица ошибок:
[[135   5]
 [ 10  50]]

Коэффициенты логистической регрессии по признакам и ROC-кривая

Рис. 6: Коэффициенты логистической регрессии по признакам и ROC-кривая

Граница решения логистической регрессии

Рис. 7: Граница решения логистической регрессии

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

Заключение

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

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

Кроме того, мы изучили Elastic Net, который объединяет преимущества Ridge и Lasso, и логистическую регрессию, которая используется для задач классификации. Каждый из этих методов имеет свои особенности и области применения, и выбор подходящего метода зависит от конкретной задачи и характера данных.