Регрессия
🎯 Цель
После прочтения этой главы:
- Знаете, что такое Regression и когда его использовать
- Понимаете разницу между Linear, Polynomial, Ridge, Lasso, ElasticNet
- Правильно интерпретируете метрики regression (RMSE, MAE, R²)
- Строите regression-модель на реальном dataset и выполняете serve в FastAPI
Что нужно изучить
- Linear Regression — самый базовый алгоритм, знает каждый ML-инженер
- Polynomial Regression — нелинейные изгибы
- Regularization — Ridge (L2), Lasso (L1), ElasticNet (L1+L2)
- Feature scaling и его влияние на regression
- Multicollinearity — когда features зависят друг от друга
- Assumptions — linearity, normality, homoscedasticity (на простом уровне)
- Метрики — MSE, RMSE, MAE, R², MAPE
- Robust regression — при наличии outliers
Библиотеки
pip install scikit-learn statsmodels
- scikit-learn — основное
- statsmodels — если нужны статистические детали (p-value, confidence interval)
Важные темы
Интуиция Linear Regression
Цель — найти прямую вида y = w₀ + w₁x₁ + w₂x₂ +... + wₙxₙ:
- Как можно ближе к фактам (
y_true) - Мера «близости» — обычно MSE(Mean Squared Error)
Оптимизация: **Ordinary Least Squares (OLS)**или Gradient Descent.
Зачем нужен Regularization?
Если features много (часто больше, чем наблюдений) или они зависят друг от друга, модель overfitting. Решение — regularization:
- Ridge (L2):
loss + λ * Σwᵢ²— приближает features к нулю - Lasso (L1):
loss + λ * Σ|wᵢ|— делает точно нулём некоторые features (feature selection) - **ElasticNet:**смесь обоих
λ kichik (0) λ o'rta λ katta
Overfitting Optimal Underfitting
(model murakkab) (model oddiy)
Linear assumptions
- Linearity — действительно ли связь между y и X линейная?
- Independence — наблюдения независимы (для time series это нарушается)
- Homoscedasticity — variance ошибок одинаковая (проверка через residual plot)
- Normality — ошибки в нормальном распределении (Q-Q plot)
- No multicollinearity — features не сильно связаны друг с другом (VIF)
**Совет backend dev:**Эти assumptions не всегда обязательны для бизнеса — random forest или XGBoost работают и без них. Но для красивого результата с Linear Regression полезно.
Метрики — когда какая?
| Метрика | Формула | Интерпретация | Когда |
|---|---|---|---|
| MAE | `mean( | y - ŷ | )` |
| MSE | mean((y - ŷ)²) | Квадратичная ошибка — штрафует большие ошибки | Для loss function |
| RMSE | sqrt(MSE) | В единицах измерения | Самая распространённая |
| R² | 1 - SSres/SStot | 0..1 (или отрицательное) — какой % данных объяснён | Оценка модели |
| MAPE | `mean( | y - ŷ | / |
Примеры кода
Linear Regression — California Housing
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error
import numpy as np
# 1. Data
data = fetch_california_housing(as_frame=True)
X, y = data.data, data.target # y = медианная цена дома ($100k)
# 2. Split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 3. Pipeline
pipeline = Pipeline([
("scaler", StandardScaler()),
("lr", LinearRegression()),
])
pipeline.fit(X_train, y_train)
# 4. Predict и метрики
y_pred = pipeline.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
mae = mean_absolute_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"RMSE: {rmse:.3f}") # 0.745
print(f"MAE: {mae:.3f}") # 0.533
print(f"R²: {r2:.3f}") # 0.576
# 5. Coefficients
coefs = dict(zip(X.columns, pipeline.named_steps["lr"].coef_))
for name, c in sorted(coefs.items(), key=lambda x: abs(x[1]), reverse=True):
print(f" {name}: {c:+.3f}")
Ridge, Lasso, ElasticNet
from sklearn.linear_model import Ridge, Lasso, ElasticNet
models = {
"LinearRegression": LinearRegression(),
"Ridge (L2)": Ridge(alpha=1.0, random_state=42),
"Lasso (L1)": Lasso(alpha=0.01, random_state=42),
"ElasticNet": ElasticNet(alpha=0.01, l1_ratio=0.5, random_state=42),
}
for name, model in models.items():
pipe = Pipeline([("scaler", StandardScaler()), ("model", model)])
pipe.fit(X_train, y_train)
score = pipe.score(X_test, y_test) # R²
print(f"{name:20s} R² = {score:.4f}")
Polynomial Regression
from sklearn.preprocessing import PolynomialFeatures
poly_pipeline = Pipeline([
("poly", PolynomialFeatures(degree=2, include_bias=False)),
("scaler", StandardScaler()),
("lr", LinearRegression()),
])
poly_pipeline.fit(X_train, y_train)
print(f"Polynomial R²: {poly_pipeline.score(X_test, y_test):.4f}")
Hyperparameter tuning — GridSearchCV
from sklearn.model_selection import GridSearchCV
ridge_pipe = Pipeline([("scaler", StandardScaler()), ("ridge", Ridge())])
param_grid = {"ridge__alpha": [0.01, 0.1, 1.0, 10.0, 100.0]}
gs = GridSearchCV(ridge_pipe, param_grid, cv=5, scoring="neg_root_mean_squared_error")
gs.fit(X_train, y_train)
print(f"Best alpha: {gs.best_params_['ridge__alpha']}")
print(f"Best CV RMSE: {-gs.best_score_:.3f}")
Интеграция с backend
Price prediction API (FastAPI)
from fastapi import FastAPI
from pydantic import BaseModel, Field
import joblib
import numpy as np
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app):
app.state.model = joblib.load("models/california_housing_v1.joblib")
yield
app = FastAPI(lifespan=lifespan, title="California Housing Price Predictor")
class HouseFeatures(BaseModel):
MedInc: float = Field(..., gt=0, description="Median income (10k USD)")
HouseAge: float = Field(..., ge=0, le=100)
AveRooms: float = Field(..., gt=0)
AveBedrms: float = Field(..., gt=0)
Population: float = Field(..., gt=0)
AveOccup: float = Field(..., gt=0)
Latitude: float
Longitude: float
class PricePrediction(BaseModel):
predicted_price_100k: float
predicted_price_usd: float
@app.post("/predict/", response_model=PricePrediction)
def predict_price(features: HouseFeatures):
X = np.array([[
features.MedInc, features.HouseAge, features.AveRooms,
features.AveBedrms, features.Population, features.AveOccup,
features.Latitude, features.Longitude,
]])
pred = float(app.state.model.predict(X)[0])
return PricePrediction(
predicted_price_100k=pred,
predicted_price_usd=pred * 100_000,
)
Logging и мониторинг (начальный уровень)
import logging
from datetime import datetime
logger = logging.getLogger("ml_service")
@app.post("/predict/", response_model=PricePrediction)
def predict_price(features: HouseFeatures):
start = datetime.now()
X = np.array([list(features.dict().values())])
pred = float(app.state.model.predict(X)[0])
duration_ms = (datetime.now() - start).total_seconds() * 1000
logger.info(
"prediction",
extra={
"input": features.dict(),
"prediction": pred,
"duration_ms": duration_ms,
"model_version": "v1",
},
)
return PricePrediction(predicted_price_100k=pred, predicted_price_usd=pred * 100_000)
Ресурсы
- Scikit-learn Regression — scikit-learn.org/stable/supervised_learning.html#regression
- StatQuest — Linear Regression(YouTube)
- StatQuest — Ridge, Lasso, ElasticNet(3 отдельных видео)
- “Introduction to Statistical Learning”(ISLR) — бесплатный PDF, глубокий regression
- Andrew Ng — ML Specialization Course 1(Linear Regression module)
🏋️ Упражнения
🟢 Easy
- На
sklearn.datasets.load_diabetes()обучите Linear Regression, выведите R². - Попробуйте
alpha = [0.001, 0.01, 0.1, 1, 10, 100]в Ridge, нарисуйте, как меняется R². - Сравните train и test R² — когда возникает overfitting?
🟡 Medium
- California Housing: сравните Linear, Ridge, Lasso, ElasticNet, Polynomial в виде таблицы.
- Manual gradient descent: напишите Linear Regression своими руками через numpy (без sklearn).
- Residual analysis: визуализируйте
y_test - y_pred. Есть ли паттерн? (homoscedasticity check)
🔴 Hard
- Production-сервис: модель California Housing в Docker + FastAPI + Postgres (для логирования предсказаний). Healthcheck, Prometheus metrics (
request_count,prediction_duration). - A/B test infra: одновременно serve две модели (
v1иv2), разделите трафик 50/50, собирайте метрики отдельно для каждой.
Capstone
notebooks/month-02/01_regression.ipynb:
- Kaggle — House Prices: Advanced Regression Techniques competition
- Сделайте первый submit
- Цель: top 50% (RMSE log <= 0.16)
- Шаги: EDA → preprocessing → baseline с Ridge → feature engineering → feature selection с Lasso → submission
✅ Чек-лист
- Понимаю математическую формулу Linear Regression (y = wx + b)
- Знаю разницу между Ridge и Lasso (L1 vs L2)
- Знаю, когда использовать RMSE и MAE
- Могу объяснить смысл R² бизнесу
- Умею создавать Pipeline (scaler + model)
- Делаю tune гиперпараметров через GridSearchCV
- Сделал serve regression-модели в FastAPI
- Сделал первый Kaggle submission
Переходим к Classification.