В предыдущей статье разобрали простую линейную регрессию, где целевая переменная зависела от одного фактора, но в реальной жизни всё сложнее. Представьте, что мы прогнозируем стоимость квартиры: она зависит не только от площади, но и от количества комнат, этажа, района, года постройки, наличия парковки и десятков других важных характеристик.
Множественная линейная регрессия – это естественное расширение простой линейной регрессии на случай с несколькими независимыми переменными (предикторами), и она позволяет:
-
Учитывать комплекс факторов – строить прогнозы на основе множества признаков одновременно,
-
Оценивать изолированное влияние каждого предиктора, контролируя эффект остальных переменных,
-
Повышать точность прогнозов по сравнению с одномерными моделями.
В этой статье мы разберем математическую основу метода и реализуем его с помощью 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
Интерпретация метрик:
|
Метрика |
Значение |
Интерпретация |
|---|---|---|
|
R² |
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².
Заключение
Множественная линейная регрессия – это мощный и интерпретируемый инструмент анализа данных и ее ключевые преимущества:
-
Интерпретируемость – благодаря ей мы можем сказать: “при увеличении X₁ на единицу, Y изменится на β₁, при неизменных остальных факторах”
-
Учет комплексных зависимостей – возможность моделировать влияние множества факторов одновременно
-
Фундамент для сложных методов – понимание принципов множественной регрессии необходимо для освоения Ridge, LASSO и нейронных сетей
Ключевые моменты, которые нужно запомнить:
|
Аспект |
Рекомендация |
|---|---|
|
Мультиколлинеарность |
Сильная корреляция между предикторами может делать оценки коэффициентов ненадежными |
|
Качество данных |
Модель чувствительна к выбросам — их нужно выявлять и обрабатывать |
|
Размер выборки |
Чем больше признаков, тем больше данных нужно для надежной оценки |
|
Сложность модели |
Больше признаков ≠ лучше. Используйте тестовую выборку для проверки |
Освоив множественную регрессию, вы получите фундамент для понимания более сложных алгоритмов машинного обучения. В следующих статьях мы рассмотрим методы регуляризации (Ridge и LASSO), которые помогают бороться с переобучением и отбирать наиболее важные признаки.
✔️Больше про будни и задачи аналитика данных в моем тг канале 🌸Таня и Данные📊
Автор: TanyaVSdannye


