Scikit-Learn

Scikit-Learn — библиотека классического машинного обучения: регрессия, классификация, кластеризация, метрики и пайплайны. Единый интерфейс fit/transform/predict.

Уровень: средний 18 разделов ⏱ 4 часа

1. Что такое Scikit-Learn

Scikit-Learn строит ML-пайплайны: модели обучаются методом fit(X, y), предсказывают predict(X), оцениваются метриками. Вход — матрица признаков (NumPy/DataFrame), выход — вектор целевой переменной.

Подходит для табулярных данных; для картинок и текстов чаще используют глубокие фреймворки TensorFlow/PyTorch.

2. Установка

pip install scikit-learn
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

data = load_iris()
X, y = data.data, data.target

3. Тренировочные и тестовые данные

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y)
Никогда не оценивай модель на тех же данных, на которых обучал: завышенное качество — классическая ошибка. Разделяй выборку до обучения.

4. Линейная регрессия

from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

model = LinearRegression()
model.fit(X_train, y_train)
pred = model.predict(X_test)

print(model.coef_)
print(mean_squared_error(y_test, pred))

5. Логистическая регрессия (классификация)

from sklearn.linear_model import LogisticRegression

clf = LogisticRegression(max_iter=1000)
clf.fit(X_train, y_train)
print(clf.score(X_test, y_test))

Логистическая регрессия — модель вероятности класса; отлично работает на табулярных признаках.

6. Деревья и случайные леса

from sklearn.ensemble import RandomForestClassifier

rf = RandomForestClassifier(n_estimators=200, random_state=42)
rf.fit(X_train, y_train)

# важность признаков
print(rf.feature_importances_)
import pandas as pd
print(pd.Series(rf.feature_importances_, index=data.feature_names))

Случайные леса хорошо работают без масштабирования и устойчивы к выбросам.

7. Метрики качества

from sklearn.metrics import accuracy_score, precision_score, \
    recall_score, f1_score, confusion_matrix

acc  = accuracy_score(y_test, pred)
prec = precision_score(y_test, pred, average='macro')
rec  = recall_score(y_test, pred, average='macro')
f1   = f1_score(y_test, pred, average='macro')
print(confusion_matrix(y_test, pred))

Для несбалансированных классов точность (accuracy) обманчива — смотри precision/recall/F1.

8. Кросс-валидация

from sklearn.model_selection import cross_val_score

scores = cross_val_score(rf, X, y, cv=5)
print(scores.mean(), scores.std())

cv=5 разбивает данные на 5 частей и обучает 5 моделей — честная оценка обобщения.

9. Масштабирование признаков

from sklearn.preprocessing import StandardScaler

# подгоняем масштаб ТОЛЬКО по train, применяем к обоим
scaler = StandardScaler()
X_train_s = scaler.fit_transform(X_train)
X_test_s = scaler.transform(X_test)
Модели на расстояниях (SVM, kNN, логистика) требуют масштабирования; деревья — нет.

10. Пайплайны (Pipeline)

from sklearn.pipeline import Pipeline
from sklearn.svm import SVC

pipe = Pipeline([
    ('scaler', StandardScaler()),
    ('svc', SVC(kernel='rbf'))
])
pipe.fit(X_train, y_train)
print(pipe.score(X_test, y_test))

Pipeline связывает трансформации и модель: весь процесс переносится одним объектом, включая в кросс-валидацию.

11. Подбор параметров (GridSearch)

from sklearn.model_selection import GridSearchCV

grid = {'svc__C': [0.1, 1, 10], 'svc__kernel': ['rbf', 'linear']}
gs = GridSearchCV(pipe, grid, cv=3)
gs.fit(X_train, y_train)
print(gs.best_params_)
print(gs.best_score_)

Имя параметра внутри pipeline — через имя_шага__имя_атрибута.

12. Кластеризация: KMeans и DBSCAN

Кластеризация — задача обучения без учителя: разбить объекты на группы без заранее известных ответов. Она удобна для сегментации клиентов, поиска аномалий и подготовки признаков.

from sklearn.cluster import KMeans, DBSCAN

kmeans = KMeans(n_clusters=3, random_state=42)
kmeans.fit(X)
labels = kmeans.labels_

dbscan = DBSCAN(eps=0.5, min_samples=5)
db_labels = dbscan.fit_predict(X)
АлгоритмКогда использовать
KMeansровные компактные кластеры, известное число групп
DBSCANнеизвестное число кластеров, выбросы, произвольная форма
Agglomerativeиерархия кластеров, дендрограмма
Число кластеров для KMeans подбирают методом «локтя» или силуэтом silhouette_score. Нормализуйте признаки перед кластеризацией — см. раздел 9.
Метки кластеров — отличный готовый признак для надзирательной модели: «клиент из сегмента A» часто предсказывает поведение лучше сырых цифр.

13. Понижение размерности: PCA

Когда признаков сотни, модели учатся медленно и переобучаются. PCA (метод главных компонент) сжимает данные до нескольких ортогональных осей, сохраняя максимум дисперсии.

from sklearn.decomposition import PCA

pca = PCA(n_components=2)
X_2d = pca.fit_transform(X_scaled)

print(pca.explained_variance_ratio_)
print(pca.explained_variance_ratio_.cumsum())
// типичный набор: 5 компонент объясняют 90% дисперсии
[0.42 0.37 0.10 0.06 0.03]
Компоненты PCA — комбинации исходных признаков, интерпретировать их как «признак №1» нельзя. Для объяснимости линейных моделей используйте коэффициенты до PCA.

14. Сохранение и объяснимость модели

Обученную модель нужно сериализовать, чтобы использовать в сервисе, и уметь объяснить её решения заказчику. Стандарт сериализации — joblib.

import joblib

joblib.dump(pipe, "model_pipeline.joblib")
loaded = joblib.load("model_pipeline.joblib")
print(loaded.predict(X_new))
from sklearn.inspection import permutation_importance

importance = permutation_importance(loaded, X_test, y_test,
                                   n_repeats=10, random_state=42)
print(dict(zip(feature_names, importance.importances_mean)))
МетодЧто объясняет
feature_importances_вклад признаков в ансамбль деревьев
permutation_importanceпадение качества при перемешивании признака
SHAPвклад каждого признака в каждое предсказание
В продакшене модели часто оборачивают в REST API на FastAPI/Flask. Храните версию датасета вместе с моделью — иначе её не воспроизвести.

15. Категориальные признаки и ColumnTransformer

Модели понимают только числа. Поэтому строковые категории кодируют: OrdinalEncoder превращает значения в номера, OneHotEncoder — в столбцы 0/1. Для смеси числовых и категориальных признаков предусмотрен ColumnTransformer.

from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder, StandardScaler

prep = ColumnTransformer([
    ("num", StandardScaler(), ["age", "income"]),
    ("cat", OneHotEncoder(),  ["city", "plan"])
])

pipe = Pipeline([("prep", prep), ("clf", LogisticRegression())])
pipe.fit(X_train, y_train)

Работает внутри того же Pipeline: каждая колонка получает свой трансформер, и весь процесс целиком попадает в кросс-валидацию и GridSearch из раздела 11.

OneHotEncoder с множеством значений создаст тысячи столбцов. Для кардинальных категорий (город, браузер) используйте min_frequency — редкие значения соберутся в одну группу.
Не кодируйте категорию как «числа на глаз» 0,1,2: линейные модели прочитают в этом порядок, которого в данных нет.

16. Несбалансированные классы

Когда одного класса 2%, модель «хитрит» и предсказывает большинство — accuracy при этом высоки. Лечится тремя способами: весами, ресемплингом выборки и правильными метриками.

model = RandomForestClassifier(class_weight="balanced")

# или ресемплинг: дублируем редкий класс
from imblearn.over_sampling import SMOTE
X_res, y_res = SMOTE(random_state=42).fit_resample(X_train, y_train)

Оценивать такие модели нужно не accuracy, а precision/recall/F1 (раздел 7), и обязательно через StratifiedKFold, чтобы редкий класс попадал в каждую фолду.

from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import f1_score

scores = [f1_score(y[val], model.fit(X[t], y[t]).predict(X[val]))
          for t, val in StratifiedKFold(n_splits=5).split(X, y)]
print(sum(scores) / len(scores))
МетодСутьКогда
class_weightштраф за ошибки редкого классабыстро, без новых данных
oversamplingкопии редких объектов (SMOTE)мало данных редкого класса
undersamplingвыкинуть часть большинстваогромный датасет
Ресемплинг выполняйте только внутри train-части, до разделения; иначе тест «подсмотрит» синтетические объекты и оценка станет завышенной.

17. Бустинг: HistGradientBoosting и друзья

Бустинг обучает слабые деревья последовательно: каждое следующее исправляет ошибки предыдущего. Случайный лес из раздела 6 — это бэггинг, деревья там обучаются параллельно; бустинг обычно точнее, но чувствительнее к переобучению.

from sklearn.ensemble import HistGradientBoostingClassifier

hgb = HistGradientBoostingClassifier(
    learning_rate=0.1, max_iter=300, random_state=42)
hgb.fit(X_train, y_train)
print(hgb.score(X_test, y_test))

learning_rate и число деревьев связаны обратно: маленький шаг требует больше итераций, но меньше переобучает. Промышленный стандарт — XGBoost, LightGBM и CatBoost: у них GPU-ускорение и честная работа с категориями.

МашинаСильная сторона
sklearn HistGradientBoostingбез лишних зависимостей, API sklearn
XGBoostзрелость, экосистема, ранняя остановка
LightGBMскорость, категориальные признаки
CatBoostмало данных, много категорий
Если бустинг настойчиво переобучается — уменьшите max_depth и добавьте регуляризацию вместо бесконечного роста итераций.

18. MLflow и мониторинг модели

Эксперименты без фиксации бесполезны: невозможно вспомнить, какие признаки и гиперпараметры дали лучшее F1. MLflow логирует параметры, метрики и сам артефакт модели в единую базу.

import mlflow

mlflow.set_experiment("churn-v2")
with mlflow.start_run():
    mlflow.log_params(grid)
    mlflow.log_metric("f1", f1_score(y_test, pred))
    mlflow.sklearn.log_model(model, "model")

После деплоя модель живёт своей жизнью: распределение признаков «уплывает» (data drift), и качество тихо деградирует. Минимальный контроль — считать те же метрики на новых данных и сравнивать с порогом, а также следить, что не пропадают важные признаки.

# простой watchdog в сервисе
def check_drift(real_pred, y_real):
    fresh = f1_score(y_real, real_pred)
    return fresh > best_f1 - 0.05
Данные для обучения и для инференса должны собираться одинаково: любое отличие трансформации — первый источник «странного» падения качества в проде.

19. Feature engineering: из сырых данных в признаки

Качество модели чаще решают данные, а не алгоритм. Feature engineering — превращение сырых колонок в признаки, с которыми модель работает лучше: полиномы, бины, взаимодействия.

from sklearn.preprocessing import PolynomialFeatures, Binarizer
from sklearn.compose import ColumnTransformer

ct = ColumnTransformer([
    ('poly', PolynomialFeatures(degree=2,
             interaction_only=True), ['price', 'rooms']),
    ('bin',  Binarizer(threshold=200), ['area']),
])

Осторожно с утечкой данных: все преобразования, вычисляющие статистику (StandardScaler, PolynomialFeatures без параметров), учите только на train — через Pipeline это гарантируется автоматически.

ПризнакКогда работает
полиномиальныйнелинейная зависимость
биновыйпороговые правила, категории интервалов
взаимодействиесовместный эффект двух переменных
Проверьте после добавления признаков качество через cross_val_score (раздел 8): прирост 1–2% — сигнал, что фичи тащут.

20. Валидация для временных рядов

Простое train_test_split (со случайным перемешиванием) ломает временные ряды: модель видит «будущее» в обучении. Для дат применяем последовательные срезы.

from sklearn.model_selection import TimeSeriesSplit

tscv = TimeSeriesSplit(n_splits=5)
for train_idx, val_idx in tscv.split(X):
    X_train, X_val = X[train_idx], X[val_idx]

Принципы walk-forward валидации:

fold 1: [train 01-03] [val 04]
fold 2: [train 01-04] [val 05]
fold 3: [train 01-05] [val 06]
Кросс-валидация с перемешиванием по времени даст завышенную точность: в проде будущего в обучении не будет. Для ряда используйте TimeSeriesSplit, даже если качество упадёт.

21. Обработка пропусков: imputation

Пропущенные значения нельзя тупо удалить: строк может быть мало, а в X теряется связь. Imputation — заполнение пропусков оценкой: константой, медианой или моделью.

from sklearn.impute import SimpleImputer, IterativeImputer

SimpleImputer(strategy='median')        # константа/медиана
IterativeImputer(max_iter=10)           # заполнение моделью по остальным фичам
СтратегияПлюсыМинусы
константа/медианапросто, стабильносдвигает распределение
imputation по KNNучитывает похожие объектыдорого на больших данных
IterativeImputerиспользует взаимосвязисложный, медленный

Важно: импьютеры должны учиться на трейне, поэтому вставляйте их в Pipeline (раздел 9). Не забывайте про признак «был ли пропуск» — MissingIndicator часто улучшает модель.

Сравните метрику модели до и после импутации на одной валидации — иногда простая медиана бьёт сложный IterativeImputer.

22. Ансамбли: стекинг и голосование

Разные модели ошибаются по-разному. Ансамбли объединяют предсказания: голосование (voting) без создания новых моделей и стекинг, где мета-модель учится на выходах базовых.

from sklearn.ensemble import VotingClassifier, StackingClassifier
from sklearn.linear_model import LogisticRegression

stack = StackingClassifier(
    estimators=[('tree', DecisionTreeClassifier(max_depth=4)),
                ('nb',   GaussianNB())],
    final_estimator=LogisticRegression())
stack.fit(X_train, y_train)
ПодходКак объединяет
Voting (hard)голосование классов поровну
Voting (soft)усреднение вероятностей
Stackingмета-модель учится на выходах базовых
Не добавляйте в ансамбль две одинаковые модели (два RandomForest с разными seed) — они предскажут одно и то же, вес ансамбля не изменится, а время обучения удвоится.
Стекинг легко включить в GridSearch из раздела 10 — перебирайте гиперпараметры базовых моделей, тогда эффект объединения виден на валидации честно.

Что дальше

Для глубокого обучения переходи на Keras/PyTorch. Данные готовь в Pandas, а работу веди в Jupyter.

← Вернуться к курсам