Линейная регрессия
Узнайте, как работает линейная регрессия, разберитесь с математикой и реализуйте простую и множественную линейную регрессию в Python с scikit-learn.
Линейная регрессия — один из самых фундаментальных алгоритмов машинного обучения. Она моделирует зависимость между зависимой переменной (тем, что нужно предсказать) и одной или несколькими независимыми переменными (входными данными), подбирая прямую линию — или гиперплоскость — через данные.
На этой странице рассматривается:
- Как работает простая и множественная линейная регрессия с математической точки зрения
- Метод наименьших квадратов (МНК) для подбора линии
- Ключевые допущения, которые необходимо проверить перед использованием модели
- Полное пошаговое руководство по scikit-learn: загрузка данных, обучение, оценка и интерпретация результатов
- Как читать коэффициенты модели и избежать распространённых ошибок
Как работает линейная регрессия
Уравнение
Простая линейная регрессия (один входной признак) строит прямую:
y = β₀ + β₁x + εy— зависимая переменная (целевая)x— независимая переменная (признак)β₀— свободный член (значение y при x = 0)β₁— наклон (изменение y при увеличении x на одну единицу)ε— член ошибки (шум, который модель не может объяснить)
Множественная линейная регрессия расширяет это на n признаков:
y = β₀ + β₁x₁ + β₂x₂ + ... + βₙxₙ + εКаждый коэффициент βᵢ показывает, насколько изменяется y при увеличении xᵢ на одну единицу при фиксированных остальных признаках.
Метод наименьших квадратов (МНК)
Модель обучается, минимизируя сумму квадратов остатков — разность между каждым фактическим значением yᵢ и предсказанием модели ŷᵢ:
SSR = Σ(yᵢ - ŷᵢ)²Возведение остатков в квадрат штрафует большие ошибки сильнее, чем малые, и гарантирует, что положительные и отрицательные ошибки не компенсируют друг друга. Этот критерий имеет точное аналитическое решение, поэтому линейная регрессия обучается почти мгновенно даже на больших наборах данных.
Ключевые допущения
Линейная регрессия даёт надёжные предсказания только при соблюдении следующих условий:
| Допущение | Что проверять |
|---|---|
| Линейность | Зависимость между признаками и целевой переменной приблизительно линейная |
| Независимость | Наблюдения независимы друг от друга |
| Гомоскедастичность | Дисперсия остатков примерно постоянна для всех предсказаний |
| Нормальность остатков | Остатки приблизительно нормально распределены |
| Отсутствие мультиколлинеарности | Независимые переменные не имеют высокой корреляции между собой |
При нарушении этих допущений оценки коэффициентов могут оказаться смещёнными, а модель — плохо работать на новых данных.
Пример простой линейной регрессии
Прежде чем переходить к множеству признаков, рассмотрим, как алгоритм подбирает прямую по одному признаку. Это позволяет легко визуализировать геометрию.
import numpy as np
import matplotlib
matplotlib.use('Agg') # non-interactive backend for scripts
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
# Simulate: house size (sq ft) vs price ($1000s)
rng = np.random.default_rng(42)
X_simple = rng.uniform(500, 3000, 50).reshape(-1, 1)
y_simple = 50 + 0.1 * X_simple.ravel() + rng.normal(0, 15, 50)
model = LinearRegression()
model.fit(X_simple, y_simple)
print(f"Intercept (β₀): {model.intercept_:.2f}")
print(f"Slope (β₁): {model.coef_[0]:.4f}")
print(f"Interpretation: each extra sq ft adds ${model.coef_[0]*1000:.0f} to the predicted price")Ожидаемый вывод:
Intercept (β₀): 46.17
Slope (β₁): 0.1007
Interpretation: each extra sq ft adds $101 to the predicted priceЗначения свободного члена и наклона вычисляются МНК автоматически — вам не нужно делать никаких алгебраических вычислений самостоятельно.
Множественная линейная регрессия с scikit-learn
В реальных наборах данных много признаков. В этом разделе рассматривается полный конвейер на наборе данных California Housing, который содержит жилищную статистику на уровне переписных блоков для Калифорнии 1990 года.
Шаг 1: Импорт библиотек
import pandas as pd
import numpy as np
from sklearn.datasets import fetch_california_housing
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_scoreШаг 2: Загрузка и исследование набора данных
california = fetch_california_housing()
df = pd.DataFrame(california.data, columns=california.feature_names)
df['MedHouseVal'] = california.target # median house value in $100,000s
print(df.shape) # (20640, 9)
print(df.head())
print(df.describe())Набор данных содержит 20 640 строк и 8 входных признаков:
| Признак | Описание |
|---|---|
MedInc | Медианный доход в блоке (в десятках тысяч долларов) |
HouseAge | Медианный возраст домов в блоке |
AveRooms | Среднее количество комнат на домохозяйство |
AveBedrms | Среднее количество спален на домохозяйство |
Population | Население блока |
AveOccup | Среднее количество жильцов на домохозяйство |
Latitude | Широта блока |
Longitude | Долгота блока |
Целевая переменная MedHouseVal — медианная стоимость дома в единицах $100 000.
Шаг 3: Выбор признаков и разбивка данных
Для простоты демонстрации используются все 8 признаков. Подробное объяснение того, зачем разбивать данные, см. в разделе Train/Test Split.
X = df[california.feature_names] # all 8 features
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Параметр random_state=42 гарантирует воспроизводимость разбивки при каждом запуске скрипта.
Шаг 4: Обучение модели
model = LinearRegression()
model.fit(X_train, y_train)Это всё, что требуется. Метод fit() аналитически решает задачу МНК с помощью матричной алгебры — итерационный градиентный спуск по умолчанию не используется.
Шаг 5: Изучение обученных коэффициентов
Понимание того, что модель выучила, не менее важно, чем её точность:
coef_df = pd.DataFrame({
'Feature': california.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
AveBedrms 0.7831
MedInc 0.4487
Longitude -0.4337
Latitude -0.4198
AveRooms -0.1233
HouseAge 0.0097
AveOccup -0.0035
Population -0.0000
Intercept: -37.0233Интерпретация коэффициентов:
AveBedrms = 0.783: увеличение среднего количества спален на одну единицу предсказывает рост стоимости дома на $78 300 — однако это значение перепутано сAveRooms(они коррелируют). Когда коррелирующие признаки присутствуют одновременно, отдельные коэффициенты могут стать большими, нестабильными или даже нелогичными. Это и есть мультиколлинеарность.MedInc = 0.449: увеличение медианного дохода на одну единицу (примерно $10 000) предсказывает рост стоимости дома на $44 900 при неизменных остальных переменных.Longitude = -0.434иLatitude = -0.420: чисто географические контрольные переменные; модель использует их для учёта локационного эффекта, хотя не способна хорошо моделировать нелинейную географию.
Шаг 6: Оценка модели
y_pred = model.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
r2 = r2_score(y_test, y_pred)
print(f"RMSE: {rmse:.4f} (in $100,000s, so ±${rmse*100_000:,.0f})")
print(f"R²: {r2:.4f}")Ожидаемый вывод:
RMSE: 0.7456 (in $100,000s, so ±$74,560)
R²: 0.5758Интерпретация метрик:
- RMSE (среднеквадратичная ошибка) — средняя ошибка предсказания в тех же единицах, что и целевая переменная. Чем меньше, тем лучше.
- R² (коэффициент детерминации) — доля дисперсии
y, объяснённая моделью. R² = 0,58 означает, что модель объясняет около 58% дисперсии цен на жильё. Значения ближе к 1,0 лучше; значения около 0 означают, что модель едва превосходит предсказание среднего.
R² ≈ 0,58 типичен для этого набора данных при линейной регрессии. Зависимость между ценами на жильё и этими признаками частично нелинейна, поэтому методы, такие как полиномиальная регрессия или градиентный бустинг, часто показывают более высокие результаты.
Шаг 7: Визуализация предсказанных и фактических значений
Наиболее наглядный диагностический график для регрессионной модели — предсказанные против фактических — он работает независимо от количества признаков:
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
plt.figure(figsize=(7, 5))
plt.scatter(y_test, y_pred, alpha=0.3, s=10, color='steelblue')
plt.plot([y_test.min(), y_test.max()],
[y_test.min(), y_test.max()],
'r--', linewidth=1.5, label='Perfect prediction')
plt.xlabel('Actual Median House Value ($100,000s)')
plt.ylabel('Predicted Median House Value ($100,000s)')
plt.title('Linear Regression: Predicted vs Actual')
plt.legend()
plt.tight_layout()
plt.savefig('lr_predicted_vs_actual.png', dpi=120)
print("Plot saved.")Точки, лежащие на красной пунктирной линии, — идеальные предсказания. Разброс вокруг линии показывает ошибку. Веерообразная форма (более широкий разброс при высоких значениях) сигнализирует о гетероскедастичности — нарушении одного из ключевых допущений.
Полный конвейер (все шаги вместе)
import pandas as pd
import numpy as np
from sklearn.datasets import fetch_california_housing
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
# Load data
california = fetch_california_housing()
df = pd.DataFrame(california.data, columns=california.feature_names)
df['MedHouseVal'] = california.target
# Split
X = df[california.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)
# Train
model = LinearRegression()
model.fit(X_train, y_train)
# Evaluate
y_pred = model.predict(X_test)
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}")Когда использовать линейную регрессию
Линейная регрессия — хороший первый выбор, когда:
- Зависимость между входными данными и выходом приблизительно линейная
- Важна интерпретируемость — нужно объяснять предсказания заинтересованным сторонам
- Набор данных небольшой или средний, и важна скорость обучения
- Нужен быстрый базовый уровень перед испытанием более сложных моделей
Следует рассмотреть альтернативы, когда:
- Признаки и целевая переменная имеют сильную нелинейную зависимость → попробуйте полиномиальную регрессию или деревья решений
- У вас много признаков, часть из которых может быть нерелевантной → регуляризованные варианты (Ridge, Lasso) предотвращают переобучение, сжимая коэффициенты
- Целевая переменная — категория, а не число → используйте логистическую регрессию
Распространённые ошибки
Забывают масштабировать признаки. Коэффициенты линейной регрессии отражают единицы измерения каждого признака. Если один признак измеряется в тысячах, а другой — в долях, размеры необработанных коэффициентов несопоставимы. Используйте StandardScaler перед сравнением важности признаков. Подробнее см. в разделе Feature Scaling.
Мультиколлинеарность. Высококоррелированные признаки делают отдельные коэффициенты ненадёжными — они могут даже менять знак. Проверьте матрицу корреляций с помощью df.corr() и удалите или объедините коррелирующие признаки.
Экстраполяция. Линейная модель, обученная на данных в определённом диапазоне, может давать грубо неверные предсказания за пределами этого диапазона. Всегда проверяйте, что новые входные данные попадают в диапазон обучающей выборки.
Игнорирование графиков остатков. Всегда стройте графики остатков после обучения. Паттерны в остатках (кривые, веерообразность, выбросы) указывают на нарушение допущений модели, и предсказаниям не следует доверять без дополнительного изучения.
Следующие шаги
Получив работающую базовую модель линейной регрессии, изучите следующие связанные темы:
- Multiple Regression — подробнее об использовании нескольких признаков и интерпретации каждого коэффициента
- Polynomial Regression — подбор кривых вместо прямых с помощью полиномиальных признаков
- Train/Test Split — почему и как правильно оценивать производительность модели
- Feature Scaling — стандартизация входных данных для корректной работы коэффициентов и градиентных методов
- Logistic Regression — предсказание категорий (да/нет, спам/не спам) вместо непрерывных значений