W3docs

Множественная регрессия

Как работает множественная линейная регрессия: интерпретация коэффициентов, мультиколлинеарность и построение модели в Python с scikit-learn.

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

Эта страница охватывает:

  • Уравнение множественной регрессии и значение каждого коэффициента
  • Как построить полный конвейер scikit-learn: загрузка, предобработка, обучение, оценка
  • Почему важно масштабирование признаков и как выполнять его правильно
  • Как интерпретировать и сравнивать масштабированные и немасштабированные коэффициенты
  • Диагностика мультиколлинеарности — наиболее распространённая ловушка в множественной регрессии
  • Анализ остатков для проверки допущений модели
  • Когда выбирать множественную регрессию и что пробовать, если она не справляется

Уравнение множественной регрессии

Множественная линейная регрессия моделирует целевую переменную y как линейную комбинацию n входных признаков:

y = β₀ + β₁x₁ + β₂x₂ + ... + βₙxₙ + ε
  • β₀свободный член (intercept): предсказанное значение y, когда все признаки равны нулю
  • β₁ … βₙкоэффициенты: насколько изменяется y при увеличении каждого xᵢ на одну единицу при постоянстве всех остальных признаков
  • εошибка: та часть y, которую модель не может объяснить

Алгоритм находит коэффициенты, минимизируя сумму квадратов остатков (метод наименьших квадратов):

SSR = Σ(yᵢ - ŷᵢ)²

Это задача имеет точное замкнутое аналитическое решение, поэтому LinearRegression из scikit-learn не требует итерационного градиентного спуска — обучение происходит практически мгновенно даже на наборах данных с сотнями тысяч строк.

Отличие от простой линейной регрессии

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

Набор данных

В примерах ниже используется набор данных California Housing, встроенный в scikit-learn. Он содержит статистику жилья на уровне переписных блоков Калифорнии за 1990 год и включает 20 640 образцов с 8 признаками.

import pandas as pd
from sklearn.datasets import fetch_california_housing

housing = fetch_california_housing()
df = pd.DataFrame(housing.data, columns=housing.feature_names)
df['MedHouseVal'] = housing.target  # median house value in $100,000s

print(df.shape)      # (20640, 9)
print(df.head())

8 входных признаков:

ПризнакОписание
MedIncМедианный доход в блоке (в десятках тысяч долларов)
HouseAgeМедианный возраст домов в блоке
AveRoomsСреднее количество комнат на домохозяйство
AveBedrmsСреднее количество спален на домохозяйство
PopulationНаселение блока
AveOccupСреднее количество жильцов на домохозяйство
LatitudeШирота блока
LongitudeДолгота блока

Целевая переменная MedHouseVal — медианная стоимость дома в единицах $100 000, то есть значение 2.0 соответствует $200 000.

Построение модели шаг за шагом

Шаг 1 — Разделение данных

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

from sklearn.model_selection import train_test_split

X = df[housing.feature_names]
y = df['MedHouseVal']

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

print(f"Training samples: {len(X_train)}")  # 16512
print(f"Test samples:     {len(X_test)}")   # 4128

Шаг 2 — Масштабирование признаков

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

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

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)   # fit ONLY on training data
X_test_scaled  = scaler.transform(X_test)         # apply the same transformation

Ключевое правило: вызывайте fit_transform на обучающих данных и transform (без подгонки) на тестовых. Подгонка на тестовых данных загрязнит оценку.

Шаг 3 — Обучение модели

from sklearn.linear_model import LinearRegression

model = LinearRegression()
model.fit(X_train_scaled, y_train)

LinearRegression.fit() решает задачу МНК аналитически. Для классической множественной регрессии нет гиперпараметров для настройки — вы выбираете только набор признаков.

Шаг 4 — Оценка модели

import numpy as np
from sklearn.metrics import mean_squared_error, r2_score

y_pred = model.predict(X_test_scaled)

mse  = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
r2   = r2_score(y_test, y_pred)

print(f"Mean Squared Error:      {mse:.4f}")
print(f"Root Mean Squared Error: {rmse:.4f}  (±${rmse * 100_000:,.0f})")
print(f"R-squared:               {r2:.4f}")

Ожидаемый вывод:

Mean Squared Error:      0.5559
Root Mean Squared Error: 0.7456  (±$74,558)
R-squared:               0.5758

Что означают метрики:

  • MSE (средняя квадратичная ошибка) — среднее квадратов разностей между предсказаниями и реальными значениями. Возведение в квадрат штрафует большие ошибки сильнее, чем малые. Единицы измерения — квадрат единиц целевой переменной ($100 000²), поэтому прямая интерпретация затруднена.
  • RMSE (среднеквадратичное отклонение) — квадратный корень из MSE, возвращающий результат в единицы целевой переменной. RMSE равный 0.75 означает, что предсказания модели в среднем отличаются примерно на $75 000.
  • R² (коэффициент детерминации) — доля дисперсии целевой переменной, объясняемой моделью. R² = 0.58 означает, что модель объясняет 58% вариации цен на жильё. Значения варьируются от 0 (не лучше предсказания среднего) до 1 (идеальные предсказания). Отрицательный R² возможен, если модель хуже среднего — это явный признак проблемы.

R² ≈ 0.58 типичен для этого набора данных при использовании классической линейной регрессии. Связь между ценами на жильё и этими признаками частично нелинейна и включает географическую кластеризацию, которую гиперплоскость не может хорошо уловить. Алгоритмы на основе градиентного бустинга стабильно достигают 0.80+ на этом наборе данных.

Шаг 5 — Анализ коэффициентов

После масштабирования величины коэффициентов непосредственно сопоставимы — они показывают, какие признаки наиболее сильно влияют на предсказание:

coef_df = pd.DataFrame({
    'Feature':     housing.feature_names,
    'Coefficient': model.coef_
}).sort_values('Coefficient', key=abs, ascending=False)

print(coef_df.to_string(index=False))
print(f"\nIntercept: {model.intercept_:.4f}")

Ожидаемый вывод:

   Feature  Coefficient
  Latitude    -0.8969
 Longitude    -0.8698
    MedInc     0.8544
 AveBedrms     0.3393
  AveRooms    -0.2944
  HouseAge     0.1225
  AveOccup    -0.0408
Population    -0.0023

Intercept: 2.0719

Чтение коэффициентов после стандартного масштабирования:

  • MedInc = 0.854 — единственный наиболее сильный предиктор. Увеличение медианного дохода на одно стандартное отклонение предсказывает рост стоимости дома на $85 400 при прочих равных условиях.
  • Latitude = -0.897 и Longitude = -0.869 — модель обнаружила, что более северные и более восточные переписные блоки, как правило, дешевле. Однако эти два географических признака сильно коррелированы (r = -0.93), что может дестабилизировать их индивидуальные коэффициенты (см. Диагностика мультиколлинеарности).
  • AveBedrms = +0.339 против AveRooms = -0.294 — эти признаки имеют противоположные знаки, хотя большее количество комнат и спален в целом означает более крупный и дорогой дом. Это классический признак мультиколлинеарности: AveRooms и AveBedrms коррелированы (r = 0.85), поэтому их коэффициенты компенсируют друг друга. Не интерпретируйте их по отдельности.
  • Population = -0.002 — очень близко к нулю после масштабирования. Население блока почти не влияет на предсказание при наличии остальных признаков.

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

Шаг 6 — Предсказания для новых данных

# A new census block: high income, older house, San Francisco Bay Area
new_block = pd.DataFrame([[8.0, 41.0, 6.0, 1.0, 322, 2.5, 37.88, -122.23]],
                         columns=housing.feature_names)

new_block_scaled = scaler.transform(new_block)
prediction = model.predict(new_block_scaled)

print(f"Predicted median house value: ${prediction[0] * 100_000:,.0f}")

Ожидаемый вывод:

Predicted median house value: $410,895

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

Диагностика мультиколлинеарности

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

Проверка с помощью матрицы корреляций

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

corr = df[housing.feature_names].corr()
print(corr.round(2))

Ключевые пары для внимания:

Пара признаковКорреляцияПроблема
AveRooms / AveBedrms0.85Высокая — коэффициенты компенсируют друг друга
Latitude / Longitude-0.93Очень высокая — географическое совместное движение
MedInc / MedHouseVal (цель)0.69Хороший предиктор, не является проблемой коллинеарности

Корреляция выше 0.80 между двумя признаками — предупреждающий сигнал. Когда вы её обнаруживаете, рассмотрите следующее:

  1. Удалите один из коррелированных признаков. Если в модели есть и AveRooms, и AveBedrms, попробуйте убрать AveBedrms и проверьте, сильно ли изменится предсказательная способность модели.
  2. Объедините их. Создайте производный признак (например, rooms_per_bedroom = AveRooms / AveBedrms), который отражает отношение без избыточности.
  3. Используйте регуляризованную модель. Ridge-регрессия добавляет L2-штраф, который сжимает коррелированные коэффициенты друг к другу, стабилизируя их. Lasso (L1) может полностью обнулить избыточные признаки.

Анализ остатков

Остаток — это разность между фактическим значением и предсказанием модели: residual = y_actual - y_predicted. Построение графика остатков позволяет выяснить, выполняются ли допущения модели.

residuals = y_test - y_pred

print(f"Mean of residuals: {residuals.mean():.4f}")  # should be close to 0
print(f"Std of residuals:  {residuals.std():.4f}")

Ожидаемый вывод:

Mean of residuals: 0.0035
Std of residuals:  0.7457

Среднее близко к нулю — признак того, что модель в среднем несмещённая. Однако стандартное отклонение 0.75 (±$75 000) свидетельствует о значительном разбросе.

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

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

# Predicted vs Actual
axes[0].scatter(y_test, y_pred, alpha=0.2, s=8)
axes[0].plot([0, 5], [0, 5], 'r--')
axes[0].set_xlabel('Actual')
axes[0].set_ylabel('Predicted')
axes[0].set_title('Predicted vs Actual')

# Residuals vs Predicted
axes[1].scatter(y_pred, residuals, alpha=0.2, s=8)
axes[1].axhline(0, color='r', linestyle='--')
axes[1].set_xlabel('Predicted')
axes[1].set_ylabel('Residual')
axes[1].set_title('Residuals vs Predicted')

plt.tight_layout()
plt.savefig('residuals.png', dpi=120)
print("Saved residuals.png")

На что обращать внимание:

  • Predicted vs Actual (предсказанное vs фактическое) — в идеале точки лежат вдоль диагонали. Систематическое отклонение от диагонали (изгиб или уплощение при высоких значениях) означает, что линейное допущение нарушается в части диапазона.
  • Residuals vs Predicted (остатки vs предсказанные) — в идеале остатки случайно рассеяны вокруг нуля при любых уровнях предсказания. Форма воронки (более широкий разброс при высоких предсказаниях) указывает на гетероскедастичность — ошибка модели непостоянна, что может делать интервальные оценки ненадёжными.
  • Кластеры — отдельные группы на графике остатков могут указывать на то, что набор данных содержит подпопуляции (например, городские vs сельские районы), для которых требуются отдельные модели или дополнительные признаки.

Полный конвейер

Вот всё вышесказанное в виде единого запускаемого скрипта:

import numpy as np
import pandas as pd
from sklearn.datasets import fetch_california_housing
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error, r2_score

# 1. Load data
housing = fetch_california_housing()
df = pd.DataFrame(housing.data, columns=housing.feature_names)
df['MedHouseVal'] = housing.target

# 2. Split — before any preprocessing
X = df[housing.feature_names]
y = df['MedHouseVal']
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# 3. Scale
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled  = scaler.transform(X_test)

# 4. Train
model = LinearRegression()
model.fit(X_train_scaled, y_train)

# 5. Evaluate
y_pred = model.predict(X_test_scaled)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
r2   = r2_score(y_test, y_pred)
print(f"RMSE: {rmse:.4f}")
print(f"R²:   {r2:.4f}")

# 6. Coefficients (most important first)
coef_df = pd.DataFrame({
    'Feature':     housing.feature_names,
    'Coefficient': model.coef_
}).sort_values('Coefficient', key=abs, ascending=False)
print(coef_df.to_string(index=False))

Когда использовать множественную регрессию

Множественная регрессия — сильный первый выбор, когда:

  • Связь между каждым признаком и целевой переменной приблизительно линейна
  • Важна интерпретируемость — каждый коэффициент имеет понятный смысл
  • Нужен быстрый базовый уровень перед попыткой сложных моделей
  • У вас достаточно образцов относительно числа признаков (грубое правило: не менее 10–20 наблюдений на признак)

Рассмотрите альтернативы, когда:

СитуацияАльтернатива
Нелинейная зависимость признака и целиПолиномиальная регрессия
Много признаков, риск переобученияRidge или Lasso (регуляризованные линейные модели)
Целевая переменная — категория, а не числоЛогистическая регрессия
Сложные взаимодействия и нелинейностьГрадиентный бустинг или случайные леса

Распространённые ошибки

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

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

Доверие коэффициентам при коррелированных признаках. Когда в модели есть и AveRooms, и AveBedrms, ни один из коэффициентов не отражает достоверно истинный эффект этой переменной. Проверяйте матрицу корреляций перед интерпретацией отдельных коэффициентов.

Игнорирование графиков остатков. R² = 0.58 выглядит приемлемым на бумаге, но паттерны остатков могут показать, что модель систематически ошибается для дорогостоящей недвижимости или определённых географических регионов.

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

Следующие шаги

Was this page helpful?