Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Классификация

🎯 Цель

После прочтения этой главы:

  • Сможете отличить задачу Classification от Regression
  • Знаете алгоритмы Logistic Regression, KNN, SVM, Decision Tree
  • Распознаёте проблему imbalanced data и знаете её решения
  • Правильно интерпретируете Confusion matrix, Precision, Recall, F1, ROC-AUC
  • Понимаете разницу между Binary и multi-class classification

Что нужно изучить

  • Logistic Regression — название «regression», но для classification
  • K-Nearest Neighbors (KNN) — lazy learning
  • Support Vector Machines (SVM) — kernel trick
  • Decision Trees — дерево правил
  • Naive Bayes — классика для text classification
  • Imbalanced classes — SMOTE, class_weight, undersampling
  • Multi-class strategies — OvR (One-vs-Rest), OvO (One-vs-One)
  • Probability calibration — чтобы predict_proba был надёжным
  • Threshold tuning0.5 не всегда оптимален

Библиотеки

pip install scikit-learn imbalanced-learn
  • scikit-learn — основные модели
  • imbalanced-learn — SMOTE и другие стратегии imbalance

Важные темы

Документ выбора алгоритма

АлгоритмСкоростьInterpretabilityУстойчивость к ImbalancedКогда использовать
Logistic RegressionОчень быстро⭐⭐⭐СредняяBaseline, линейные features
KNNМедленно⭐⭐НизкаяМаленький dataset, интуиция
SVM (linear)Быстро⭐⭐Хорошая (class_weight)Средний dataset
SVM (RBF)МедленноХорошаяСложные паттерны, маленький dataset
Decision TreeОчень быстро⭐⭐⭐⭐ХорошаяДля начала, interpretability
Naive BayesОчень быстро⭐⭐⭐СредняяText classification, baseline

Logistic Regression — как работает?

  1. Линейная комбинация: z = w₀ + w₁x₁ +... + wₙxₙ
  2. Sigmoid функция: p = 1 / (1 + e^(-z)) → результат в диапазоне (0, 1)
  3. Threshold: если p > 0.5 — class 1, иначе class 0
sigmoid(z):
   1 |        ___________
     |       /
   0.5|------/
     |     /
   0 |____/_____________
       -∞    0    +∞

Confusion Matrix

                 Predicted
                  0     1
Actual    0     [TN]  [FP]
          1     [FN]  [TP]
  • **TP (True Positive):**правильно определили как 1
  • **TN (True Negative):**правильно определили как 0
  • **FP (False Positive):**ошибочно сказали 1 (Type I error)
  • **FN (False Negative):**ошибочно сказали 0 (Type II error)

Метрики — когда какая?

МетрикаФормулаКогда важна
Accuracy(TP+TN)/NКогда классы сбалансированы
PrecisionTP/(TP+FP)False Positive опасен (spam → не теряете важные email)
RecallTP/(TP+FN)False Negative опасен (распознавание болезни — не пропустить больного)
F12*P*R/(P+R)Баланс P и R
ROC-AUCcurve areaThreshold-independent, сбалансированная оценка
PR-AUCprecision-recall areaЛучше для imbalanced data

Реальный пример — Precision vs Recall tradeoff

Модель Cancer detection:

  • Recall = 99% → находятся 99% больных
  • Precision = 60% → 60% из тех, кого назвали «больным», действительно больны
  • Это приемлемо — важнее не пропустить больного

Spam filter:

  • Precision = 99% → 99% помеченных как spam действительно spam
  • Recall = 80% → 20% spam проходит
  • Это приемлемо — нельзя терять важные email

Проблема Imbalanced data

Если 95% data — class 0, 5% — class 1, и модель всегда предсказывает 0 — accuracy 95%! Но это бесполезно.

Решения:

  1. class_weight='balanced'(в sklearn-моделях)
  2. SMOTE — синтетические minority samples (imbalanced-learn)
  3. Undersampling — убрать некоторые из majority class
  4. Stratified sampling — пропорция сохраняется при train/test split
  5. Threshold tuning — threshold ниже 0.5 (увеличивает recall)
  6. Другие метрики — вместо accuracy F1, PR-AUC

Примеры кода

Logistic Regression — Breast Cancer

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.metrics import (
    accuracy_score, precision_score, recall_score, f1_score,
    roc_auc_score, confusion_matrix, classification_report,
)

# 1. Data
data = load_breast_cancer(as_frame=True)
X, y = data.data, data.target  # 0 = malignant, 1 = benign

# 2. Split (stratify ВАЖНО!)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

# 3. Pipeline
pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("clf", LogisticRegression(max_iter=1000, random_state=42)),
])
pipe.fit(X_train, y_train)

# 4. Evaluation
y_pred = pipe.predict(X_test)
y_proba = pipe.predict_proba(X_test)[:, 1]

print(classification_report(y_test, y_pred, target_names=["malignant", "benign"]))
print(f"\nROC-AUC: {roc_auc_score(y_test, y_proba):.4f}")
print(f"Confusion Matrix:\n{confusion_matrix(y_test, y_pred)}")

Imbalanced data + class_weight

import numpy as np
from sklearn.linear_model import LogisticRegression

# Искусственный imbalanced data
from sklearn.datasets import make_classification
X, y = make_classification(
    n_samples=10_000, n_features=20, n_informative=10,
    weights=[0.95, 0.05], random_state=42,
)
# 95% class 0, 5% class 1

# Вариант 1: default (accuracy = высокая, recall = низкая)
m1 = LogisticRegression(max_iter=1000).fit(X, y)

# Вариант 2: class_weight balanced
m2 = LogisticRegression(max_iter=1000, class_weight="balanced").fit(X, y)

# Вариант 3: manual weights
m3 = LogisticRegression(max_iter=1000, class_weight={0: 1, 1: 19}).fit(X, y)

Oversampling через SMOTE

from imblearn.over_sampling import SMOTE
from imblearn.pipeline import Pipeline as ImbPipeline

# imblearn Pipeline (SMOTE не работает внутри sklearn Pipeline!)
pipe = ImbPipeline([
    ("scaler", StandardScaler()),
    ("smote", SMOTE(random_state=42)),
    ("clf", LogisticRegression(max_iter=1000)),
])

pipe.fit(X_train, y_train)

Threshold tuning

import numpy as np

y_proba = pipe.predict_proba(X_test)[:, 1]

# Default threshold 0.5
y_pred_default = (y_proba >= 0.5).astype(int)

# Custom threshold for higher recall
y_pred_recall = (y_proba >= 0.3).astype(int)

# Optimal threshold (F1 maximizing)
from sklearn.metrics import precision_recall_curve
precisions, recalls, thresholds = precision_recall_curve(y_test, y_proba)
f1_scores = 2 * precisions * recalls / (precisions + recalls + 1e-9)
best_threshold = thresholds[np.argmax(f1_scores)]
print(f"Best threshold for F1: {best_threshold:.3f}")

Multi-class classification

from sklearn.datasets import load_digits
from sklearn.svm import SVC

X, y = load_digits(return_X_y=True)  # 10 classes (0..9)

pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("svm", SVC(kernel="rbf", probability=True, random_state=42)),
])
pipe.fit(X_train, y_train)

# Multi-class metric
from sklearn.metrics import classification_report
print(classification_report(y_test, pipe.predict(X_test)))

Интеграция с backend

Churn prediction API

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
import joblib
import numpy as np

app = FastAPI(title="Customer Churn Predictor")
model = joblib.load("models/churn_v1.joblib")

class CustomerFeatures(BaseModel):
    tenure_months: int = Field(..., ge=0)
    monthly_charges: float = Field(..., gt=0)
    total_charges: float = Field(..., ge=0)
    contract_type: int = Field(..., ge=0, le=2)  # 0=monthly, 1=1yr, 2=2yr
    has_internet: bool
    payment_method: int = Field(..., ge=0, le=3)

class ChurnPrediction(BaseModel):
    will_churn: bool
    churn_probability: float
    risk_level: str  # low / medium / high
    recommended_action: str

@app.post("/predict/churn", response_model=ChurnPrediction)
def predict_churn(customer: CustomerFeatures):
    X = np.array([list(customer.dict().values())])
    proba = float(model.predict_proba(X)[0, 1])
    
    # Custom business threshold
    if proba > 0.7:
        risk, action = "high", "immediate_retention_call"
    elif proba > 0.4:
        risk, action = "medium", "send_discount_offer"
    else:
        risk, action = "low", "monitor"
    
    return ChurnPrediction(
        will_churn=proba > 0.5,
        churn_probability=proba,
        risk_level=risk,
        recommended_action=action,
    )

Batch prediction endpoint

class BatchInput(BaseModel):
    customers: list[CustomerFeatures]

@app.post("/predict/churn/batch")
def predict_batch(payload: BatchInput):
    X = np.array([list(c.dict().values()) for c in payload.customers])
    probas = model.predict_proba(X)[:, 1]
    return {
        "predictions": [
            {"index": i, "churn_proba": float(p), "will_churn": bool(p > 0.5)}
            for i, p in enumerate(probas)
        ],
        "summary": {
            "total": len(probas),
            "at_risk": int((probas > 0.5).sum()),
            "high_risk": int((probas > 0.7).sum()),
        },
    }

Ресурсы

🏋️ Упражнения

🟢 Easy

  1. На load_iris() сравните 4 classifier (LogReg, KNN, SVM, Tree).
  2. На breast cancer dataset нарисуйте Confusion Matrix (ConfusionMatrixDisplay).
  3. В KNN попробуйте k со значениями [1, 3, 5, 10, 50].

🟡 Medium

  1. Imbalanced demo: создайте 95/5 imbalanced data через make_classification. Default vs class_weight='balanced' vs SMOTE — сравните precision/recall каждого.
  2. ROC curve: нарисуйте ROC curve 3 моделей на одном chart.
  3. Threshold tuning: найдите F1-maximizing threshold на Telco Churn dataset.

🔴 Hard

  1. Production churn-сервис: полный churn prediction сервис в Docker + FastAPI + Postgres. Endpoints /predict, /feedback (для возврата реального результата), /metrics (Prometheus).
  2. Online learning: используйте SGDClassifier и partial_fit модели при каждом новом feedback — адаптация к drift.

Capstone

notebooks/month-02/02_classification_models.ipynb:

  • Kaggle — Telco Customer Churn
  • EDA → preprocessing → сравнение 5 classifier
  • Работа с class imbalance
  • Построение ROC, PR curve
  • Deploy лучшей модели в Docker

✅ Чек-лист

  • Знаю разницу между Classification и Regression
  • Умею читать Confusion Matrix
  • Могу объяснить Precision, Recall, F1 бизнесу
  • Знаю разницу между ROC-AUC и PR-AUC
  • Знаю 3 стратегии для imbalanced data
  • Могу отличить predict_proba от predict
  • Могу настроить результат через custom threshold
  • Сделал serve classification-модели в FastAPI

Переходим к Clustering.