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

Множественная линейная регрессия – это естественное расширение простой линейной регрессии на случай с несколькими независимыми переменными (предикторами), и она позволяет:

  1. Учитывать комплекс факторов – строить прогнозы на основе множества признаков одновременно,

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

  3. Повышать точность прогнозов по сравнению с одномерными моделями.

В этой статье мы разберем математическую основу метода и реализуем его с помощью Python и библиотеки scikit-learn.

Математическая основа

Если простая линейная регрессия описывается уравнением прямой, то множественная регрессия стремится описать гиперплоскость в многомерном пространстве.

Начнем с уравнения, модель множественной линейной регрессии с n предикторами записывается следующим образом:

Y=β0+β1X1+β2X2+⋯+βnXn+ϵ

Где:
Y — зависимая переменная (то, что мы предсказываем)
X1, X2, X_n — независимые переменные (те самые предикторы)
β0 — интерсепт (свободный член), это, по сути, значение Y, когда все предикторы равны нулю
βi — коэффициенты регрессии, он нтерпретируются как ожидаемое изменение Y при увеличении Xi на одну единицу, но, при условии, что все остальные предикторы остаются неизменными
ϵ — случайная ошибка (шум), которую модель не может объяснить

Как и в случае с простой регрессией, для нахождения оптимальных коэффициентов β используется метод наименьших квадратов, он минимизирует сумму квадратов вертикальных расстояний (остатков ϵ) между фактическими значениями Y и предсказанными моделью значениями. Решение этой задачи в матричном виде выглядит так:

β^=(XTX)−1XTyβ^​

Где:
X — матрица признаков (с добавленным столбцом единиц для интерсепта)
y — вектор целевых значений

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

Расщепление данных

Критически важный этап в машинном обучении – это разделение данных на обучающую и тестовую выборки. Модель обучается на одной части данных, а проверяется на другой, которую она "не видела", это помогает оценить, насколько хорошо модель будет работать с новыми данными, и избежать переобучения (overfitting), когда модель "запоминает" шум в обучающих данных вместо выявления общей закономерности.

Категориальные переменные

Линейная регрессия работает с числами, если у вас есть категориальные признаки (например, город или тип недвижимости), их необходимо преобразовать, самый распространенный способ – это One-Hot Encoding (создание фиктивных переменных), когда каждая категория превращается в отдельную колонку из 0 и 1.

Масштабирование признаков

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

Практическая реализация на Python

Перейдем к практике, рассмотрим построение модели на классическом датасете Advertising, он содержит данные о продажах (Sales) в зависимости от бюджетов на рекламу в трёх каналах: TV, Radio и Newspaper.

Техническое задание: Предсказать объем продаж на основе затрат на рекламу в различных медиа.

Импорт библиотек и загрузка данных

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error
from sklearn.preprocessing import PolynomialFeatures

# Настройка визуализации
plt.style.use('seaborn-v0_8-whitegrid')
sns.set_palette("husl")

# Загрузка полного датасета Advertising (200 наблюдений)
url = "https://www.tu-chemnitz.de/mathematik/numa/lehre/ds-2018/exercises/Advertising.csv"
df = pd.read_csv(url)

# Проверяем и удаляем лишний столбец индекса, если он есть
if 'Unnamed: 0' in df.columns:
    df = df.drop('Unnamed: 0', axis=1)

# Проверяем названия столбцов и приводим к стандартному виду
df.columns = [col.strip().lower() for col in df.columns]
if 'tv' in df.columns and 'radio' in df.columns and 'newspaper' in df.columns and 'sales' in df.columns:
    df.columns = ['TV', 'Radio', 'Newspaper', 'Sales']

print("Первые 5 строк данных:")
print(df.head())
print(f"\nРазмер данных: {df.shape}")
print(f"\nНазвания столбцов: {df.columns.tolist()}")

Результат выполнения:

Первые 5 строк данных:
      TV  Radio  Newspaper  Sales
0  230.1   37.8       69.2   22.1
1   44.5   39.3       45.1   10.4
2   17.2   45.9       69.3    9.3
3  151.5   41.3       58.5   18.5
4  180.8   10.8       58.4   12.9

Размер данных: (200, 4)

Разведочный анализ данных (EDA)

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

# Статистика данных
print("Статистика данных:")
print(df.describe())

# Корреляционная матрица
correlation_matrix = df.corr()
print("\nКорреляция с целевой переменной Sales:")
print(correlation_matrix['Sales'].sort_values(ascending=False))

# Визуализация корреляционной матрицы
plt.figure(figsize=(8, 6))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0, fmt='.3f')
plt.title('Корреляционная матрица признаков', fontsize=14)
plt.tight_layout()
plt.show()

# Парные графики зависимостей
fig, axes = plt.subplots(1, 3, figsize=(15, 4))

for idx, feature in enumerate(['TV', 'Radio', 'Newspaper']):
    axes[idx].scatter(df[feature], df['Sales'], alpha=0.6, color='steelblue')
    axes[idx].set_xlabel(feature, fontsize=12)
    axes[idx].set_ylabel('Sales', fontsize=12)
    axes[idx].set_title(f'{feature} vs Sales', fontsize=14)

plt.tight_layout()
plt.show()
Статистика данных:
              TV       Radio   Newspaper       Sales
count  200.00000  200.000000  200.000000  200.000000
mean   147.04250   23.264000   30.554000   14.022500
std     85.85426   14.846809   21.778621    5.217457
min      0.70000    0.000000    0.300000    1.600000
25%     74.37500    9.975000   12.750000   10.375000
50%    149.75000   22.900000   25.750000   12.900000
75%    218.82500   36.525000   45.100000   17.400000
max    296.40000   49.600000  114.000000   27.000000

Корреляция с целевой переменной Sales:
Sales        1.000000
TV           0.782224
Radio        0.576223
Newspaper    0.228299
Name: Sales, dtype: float64

Подготовка данных для обучения

# Определяем признаки (X) и целевую переменную (y)
X = df[['TV', 'Radio', 'Newspaper']]
y = df['Sales']

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

print(f"Размер обучающей выборки: {X_train.shape[0]} samples")
print(f"Размер тестовой выборки: {X_test.shape[0]} samples")

Результат выполнения:

Размер обучающей выборки: 160 samples
Размер тестовой выборки: 40 samples

Обучение модели множественной регрессии

# Создание и обучение модели
model = LinearRegression()
model.fit(X_train, y_train)

# Вывод коэффициентов модели
print("=" * 50)
print("Коэффициенты модели множественной регрессии:")
print("=" * 50)
print(f"Интерсепт (Intercept): {model.intercept_:.4f}")

print("\nКоэффициенты при признаках:")
for name, coef in zip(X.columns, model.coef_):
    print(f"  {name:10} : {coef:.4f}")

Результат выполнения:

==================================================
Коэффициенты модели множественной регрессии:
==================================================
Интерсепт (Intercept): 2.9791

Коэффициенты при признаках:
  TV         : 0.0447
  Radio      : 0.1892
  Newspaper  : 0.0028

Коэффициенты модели дают важную информацию о влиянии каждого фактора:

TV (0.0447): При увеличении бюджета на TV-рекламу на 1 тысячу долларов, продажи вырастут в среднем на 44.7 единиц, при неизменных бюджетах Radio и Newspaper.

Radio (0.1892): При увеличении бюджета на радио-рекламу на 1 тысячу долларов, продажи вырастут в среднем на 189.2 единиц это наибольший эффект среди всех каналов.

Newspaper (0.0028): Коэффициент близок к нулю и статистически незначим. Это означает, что при наличии TV и Radio в модели, реклама в газетах практически не влияет на продажи.

Оценка качества модели

# Прогноз на тестовой выборке
y_pred = model.predict(X_test)

# Метрики качества
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
mae = mean_absolute_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)

print("\n" + "=" * 50)
print("Оценка качества модели:")
print("=" * 50)
print(f"Среднеквадратичная ошибка (MSE): {mse:.2f}")
print(f"Корень из MSE (RMSE): {rmse:.2f}")
print(f"Средняя абсолютная ошибка (MAE): {mae:.2f}")
print(f"Коэффициент детерминации (R²): {r2:.4f}")

Результат выполнения:

==================================================
Оценка качества модели:
==================================================
Среднеквадратичная ошибка (MSE): 3.17
Корень из MSE (RMSE): 1.78
Средняя абсолютная ошибка (MAE): 1.46
Коэффициент детерминации (R²): 0.8994

Интерпретация метрик:

Метрика

Значение

Интерпретация

0.8994

Модель объясняет 89.94% дисперсии продаж — это отличный результат

RMSE

1.78

В среднем модель ошибается на 1.78 тысяч единицпродаж

MAE

1.46

Средняя абсолютная ошибка составляет 1.46 тысяч единиц продаж

Что означают эти цифры на практике:

Если предсказанный объем продаж составляет, например, 15 тысяч единиц, то реальное значение будет находиться в интервале 15 ± 1.78 тысяч единиц (если ориентироваться на RMSE) или 15 ± 1.46 тысяч единиц (если ориентироваться на MAE).

Разница между RMSE (1.78) и MAE (1.46) указывает на наличие небольшого количества ошибок с большим отклонением, которые RMSE "штрафует" сильнее из-за возведения в квадрат.

Визуализация результатов

# Сравнение реальных и предсказанных значений
plt.figure(figsize=(8, 6))
plt.scatter(y_test, y_pred, alpha=0.7)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
plt.xlabel('Фактические значения Sales')
plt.ylabel('Предсказанные значения Sales')
plt.title('Сравнение фактических и предсказанных значений')
plt.tight_layout()
plt.show()

# Анализ остатков
residuals = y_test - y_pred

fig, axes = plt.subplots(1, 2, figsize=(12, 4))

# Гистограмма остатков
axes[0].hist(residuals, bins=10, edgecolor='black', alpha=0.7)
axes[0].axvline(x=0, color='red', linestyle='--')
axes[0].set_xlabel('Остатки')
axes[0].set_ylabel('Частота')
axes[0].set_title('Распределение остатков')

# Остатки vs предсказанные значения
axes[1].scatter(y_pred, residuals, alpha=0.7)
axes[1].axhline(y=0, color='red', linestyle='--')
axes[1].set_xlabel('Предсказанные значения')
axes[1].set_ylabel('Остатки')
axes[1].set_title('Остатки vs предсказанные значения')

plt.tight_layout()
plt.show()

Результат выполнения:

График 1: Сравнение фактических и предсказанных значений

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

График 2: Распределение остатков (гистограмма)

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

График 3: Остатки vs предсказанные значения

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

Сравнение с простой линейной регрессией

Давайте теперь еще сравним, насколько множественная регрессия лучше, чем одномерные модели:

# Простая регрессия только на TV
model_tv = LinearRegression()
model_tv.fit(X_train[['TV']], y_train)
y_pred_tv = model_tv.predict(X_test[['TV']])
r2_tv = r2_score(y_test, y_pred_tv)

# Простая регрессия только на Radio
model_radio = LinearRegression()
model_radio.fit(X_train[['Radio']], y_train)
y_pred_radio = model_radio.predict(X_test[['Radio']])
r2_radio = r2_score(y_test, y_pred_radio)

# Простая регрессия только на Newspaper
model_newspaper = LinearRegression()
model_newspaper.fit(X_train[['Newspaper']], y_train)
y_pred_newspaper = model_newspaper.predict(X_test[['Newspaper']])
r2_newspaper = r2_score(y_test, y_pred_newspaper)

print("=" * 50)
print("Сравнение моделей:")
print("=" * 50)
print(f"R² только TV:           {r2_tv:.4f}")
print(f"R² только Radio:        {r2_radio:.4f}")
print(f"R² только Newspaper:    {r2_newspaper:.4f}")
print(f"R² множественная регрессия: {r2:.4f}")

Результат выполнения:

==================================================
Сравнение моделей:
==================================================
R² только TV:           0.6767
R² только Radio:        0.2634
R² только Newspaper:    0.0299
R² множественная регрессия: 0.8994

Анализ результатов:
Модель только с TV-рекламой объясняет 67.67% дисперсии продаж
Модель только с Radio-рекламой объясняет 26.34% дисперсии
Модель только с Newspaper-рекламой объясняет лишь 2.29% дисперсии
Множественная регрессия, учитывающая TV, Radio и Newspaper одновременно, объясняет 89.94% дисперсии

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

Расширение модели: Полиномиальная регрессия

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

from sklearn.preprocessing import PolynomialFeatures

# Создаем полиномиальные признаки степени 2
poly = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly.fit_transform(X)

# Разделяем данные с полиномиальными признаками
X_train_poly, X_test_poly, y_train, y_test = train_test_split(
    X_poly, y, test_size=0.2, random_state=42
)

# Обучаем модель на полиномиальных признаках
model_poly = LinearRegression()
model_poly.fit(X_train_poly, y_train)

# Оценка качества
y_pred_poly = model_poly.predict(X_test_poly)
r2_poly = r2_score(y_test, y_pred_poly)

print("=" * 50)
print("Полиномиальная регрессия (degree=2):")
print("=" * 50)
print(f"Количество признаков после преобразования: {X_poly.shape[1]}")
print(f"R² полиномиальной модели: {r2_poly:.4f}")
print(f"R² линейной модели: {r2:.4f}")

Результат выполнения:

==================================================
Полиномиальная регрессия (degree=2):
==================================================
Количество признаков после преобразования: 9
R² полиномиальной модели: 0.9869
R² линейной модели: 0.8994

Анализ результатов:
- Полиномиальная модель (со степенью 2) использует 9 признаков вместо исходных 3;
- R² увеличился с 0.8994 до 0.9869  – модель объясняет уже 98.69% дисперсии;
- Однако на малых выборках (всего 10 наблюдений) есть риск переобучения, так как на каждый признак приходится всего 1 наблюдение.

Важно: Добавление слишком большого количества степеней может привести к переобучению. Контролировать сложность модели помогают кросс-валидация и метрика Adjusted R².

Заключение

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

  1. Интерпретируемость – благодаря ей мы можем сказать: "при увеличении X₁ на единицу, Y изменится на β₁, при неизменных остальных факторах"

  2. Учет комплексных зависимостей – возможность моделировать влияние множества факторов одновременно

  3. Фундамент для сложных методов – понимание принципов множественной регрессии необходимо для освоения Ridge, LASSO и нейронных сетей

Ключевые моменты, которые нужно запомнить:

Аспект

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

Мультиколлинеарность

Сильная корреляция между предикторами может делать оценки коэффициентов ненадежными

Качество данных

Модель чувствительна к выбросам — их нужно выявлять и обрабатывать

Размер выборки

Чем больше признаков, тем больше данных нужно для надежной оценки

Сложность модели

Больше признаков ≠ лучше. Используйте тестовую выборку для проверки

Освоив множественную регрессию, вы получите фундамент для понимания более сложных алгоритмов машинного обучения. В следующих статьях мы рассмотрим методы регуляризации (Ridge и LASSO), которые помогают бороться с переобучением и отбирать наиболее важные признаки.

Датасет Advertising на Kaggle

✔️Больше про будни и задачи аналитика данных в моем тг канале 🌸Таня и Данные📊