Кладовочка

О разном, чтобы было под рукой онлайн

Scikit-learn: шпаргалка по машинному обучению на Python

О чём эта статья

Машинное обучение (Machine Learning, ML) — это подход, при котором программа не выполняет жёстко заданные инструкции, а учится находить закономерности в данных. Вы даёте алгоритму примеры (что было на входе → какой должен быть ответ), он строит модель, и затем эта модель способна предсказывать ответ для новых, незнакомых данных.

Scikit-learn (sklearn) — главная Python-библиотека для машинного обучения. Все её алгоритмы работают через три универсальных метода:

Метод Что делает Аналогия
.fit(X, y) Обучает модель на примерах «Посмотри на эти задачи с ответами и найди закономерность»
.predict(X) Выдаёт предсказание для новых данных «Какой ответ будет для этого нового случая?»
.transform(X) Преобразует данные (нормализация, кодирование, понижение размерности) «Приведи данные к удобному для анализа виду»

Благодаря единому API вы можете заменить RandomForestClassifier на LogisticRegression — и код .fit()/.predict() останется тем же. Все модели sklearn «говорят на одном языке».

Для кого эта статья: Python-разработчики, которые уже знакомы с NumPy и Pandas и хотят начать применять машинное обучение на практике.

Что вы узнаете: как создавать модели классификации и регрессии, оценивать их качество, строить пайплайны обработки данных и подбирать гиперпараметры. Каждый раздел содержит полный работающий пример с выводом результатов — код можно копировать и запускать.

Установка и импорт

pip install scikit-learn
import numpy as np
from sklearn import datasets, model_selection, metrics, preprocessing
from sklearn import linear_model, ensemble, cluster, decomposition
from sklearn import pipeline, compose

Ключевые модули — под каждую задачу свой:

Модуль Назначение Примеры классов
sklearn.linear_model Линейные модели LinearRegression, LogisticRegression, Ridge, Lasso
sklearn.ensemble Ансамбли RandomForestClassifier, GradientBoostingRegressor
sklearn.cluster Кластеризация KMeans, DBSCAN, AgglomerativeClustering
sklearn.decomposition Снижение размерности PCA, TruncatedSVD, NMF
sklearn.preprocessing Предобработка StandardScaler, OneHotEncoder, LabelEncoder
sklearn.model_selection Подбор, валидация train_test_split, GridSearchCV, cross_val_score
sklearn.metrics Метрики accuracy_score, r2_score, classification_report
sklearn.pipeline Пайплайны Pipeline, make_pipeline
sklearn.compose Композиция трансформеров ColumnTransformer, make_column_transformer

API Scikit-learn: fit, predict, transform

Главная сила sklearn — единый интерфейс. Все модели реализуют три метода, семантика которых не меняется от алгоритма к алгоритму:

Тип объекта fit(X, y) predict(X) transform(X) fit_transform(X, y)
Estimator (модель) Обучает модель Делает предсказание — —
Transformer (преобразователь) Вычисляет параметры (среднее, std) — Применяет преобразование fit + transform за один проход
Pipeline Обучает цепочку Предсказывает итоговой моделью — —
# Estimator (классификатор): fit → predict
from sklearn.linear_model import LogisticRegression

model = LogisticRegression(random_state=42)
model.fit(X_train, y_train)        # обучение
y_pred = model.predict(X_test)     # предсказание классов
y_proba = model.predict_proba(X_test)  # вероятности классов

# Transformer (масштабирование): fit → transform
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
scaler.fit(X_train)                     # вычислил mean и std
X_train_scaled = scaler.transform(X_train)  # применил к train
X_test_scaled = scaler.transform(X_test)    # применил к test (те же mean/std!)

# fit_transform — shortcut для train (НЕ для test!)
X_train_scaled = scaler.fit_transform(X_train)

Золотое правило: fit (или fit_transform) только на train. На test — только transform. Иначе утечка данных (data leakage): модель «подсмотрит» статистики test-выборки.

Линейные модели

LinearRegression — регрессия

Обычный метод наименьших квадратов. Минимизирует ∑(y_true - y_pred)². Идеален как baseline: быстро, интерпретируемо, но чувствителен к выбросам и мультиколлинеарности.

from sklearn.datasets import load_diabetes
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score, mean_squared_error

# Данные: diabetes — 442 пациента, 10 признаков, целевая переменная (прогресс болезни)
X, y = load_diabetes(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Модель
lr = LinearRegression()
lr.fit(X_train, y_train)
y_pred = lr.predict(X_test)

# Коэффициенты
print(f"Коэффициенты: {lr.coef_}")
print(f"Intercept: {lr.intercept_:.2f}")
# Коэффициенты: [  37.9  -241.9   542.4   347.2  -792.9   471.9   103.5
#                  -32.8   779.8   119.2]
# Intercept: 151.35

# Оценка качества
r2 = r2_score(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
print(f"R² = {r2:.4f}")   # R² = 0.4526
print(f"MSE = {mse:.2f}") # MSE = 2900.19

LogisticRegression — классификация

Несмотря на название, это классификатор (не регрессия). Предсказывает вероятность принадлежности к классу через сигмоиду: P(y=1) = 1/(1 + e^(-w·x)). По умолчанию — L2-регуляризация.

from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix

# Данные: iris — 150 образцов, 3 класса ирисов, 4 признака
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42, stratify=y
)

# Модель
logreg = LogisticRegression(max_iter=200, random_state=42)
logreg.fit(X_train, y_train)
y_pred = logreg.predict(X_test)

# Оценка
print(f"Accuracy = {accuracy_score(y_test, y_pred):.4f}")
# Accuracy = 0.9556

print("\nClassification report:")
print(classification_report(y_test, y_pred, target_names=load_iris().target_names))
#               precision    recall  f1-score   support
#       setosa       1.00      1.00      1.00        15
#   versicolor       0.94      0.94      0.94        16
#    virginica       0.93      0.93      0.93        14
#     accuracy                           0.96        45

print("Confusion matrix:")
print(confusion_matrix(y_test, y_pred))
# [[15  0  0]
#  [ 0 15  1]
#  [ 0  1 13]]

Деревья и ансамбли

RandomForestClassifier

Ансамбль решающих деревьев: каждое дерево обучается на случайной подвыборке данных и признаков (bagging). Итоговый ответ — голосование большинством. Устойчив к переобучению, не требует масштабирования.

from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification

# Синтетические данные: 1000 образцов, 10 признаков, 2 класса
X, y = make_classification(
    n_samples=1000, n_features=10, n_informative=5,
    n_redundant=2, random_state=42
)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

rf = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=42)
rf.fit(X_train, y_train)
y_pred = rf.predict(X_test)

print(f"RandomForest accuracy = {accuracy_score(y_test, y_pred):.4f}")
# RandomForest accuracy = 0.9200

# Важность признаков
for i, importance in enumerate(rf.feature_importances_):
    print(f"  Признак {i}: {importance:.4f}")
#   Признак 0: 0.0985
#   Признак 1: 0.0836
#   Признак 2: 0.1578
#   Признак 3: 0.0867
#   ...

Сравнение классификаторов на одном датасете

Сравним LogisticRegression и RandomForestClassifier на одних и тех же данных — это стандартный приём при выборе модели:

from sklearn.linear_model import LogisticRegression

models = {
    "LogisticRegression": LogisticRegression(max_iter=1000, random_state=42),
    "RandomForest":        RandomForestClassifier(n_estimators=100, random_state=42),
}

print(f"{'Модель':<25} {'Accuracy':>10} {'Время (мс)':>12}")
print("-" * 47)
for name, model in models.items():
    import time
    t0 = time.time()
    model.fit(X_train, y_train)
    acc = model.score(X_test, y_test)
    elapsed = (time.time() - t0) * 1000
    print(f"{name:<25} {acc:>10.4f} {elapsed:>12.1f}")
# Модель                     Accuracy   Время (мс)
# -----------------------------------------------
# LogisticRegression            0.8600        7.2
# RandomForest                 0.9200      158.3

RandomForestRegressor

Тот же принцип ансамбля, но для регрессии: усреднение предсказаний деревьев. Хорошо работает «из коробки» без настройки гиперпараметров.

from sklearn.ensemble import RandomForestRegressor
from sklearn.datasets import make_regression
from sklearn.linear_model import LinearRegression

# Данные для регрессии: 500 образцов, 8 признаков
X, y = make_regression(n_samples=500, n_features=8, noise=20, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Сравнение LinearRegression vs RandomForestRegressor
models = {
    "LinearRegression": LinearRegression(),
    "RandomForest":     RandomForestRegressor(n_estimators=100, random_state=42),
}

print(f"{'Модель':<25} {'R²':>8} {'MSE':>12}")
print("-" * 47)
for name, model in models.items():
    model.fit(X_train, y_train)
    y_pred = model.predict(X_test)
    r2 = r2_score(y_test, y_pred)
    mse = mean_squared_error(y_test, y_pred)
    print(f"{name:<25} {r2:>8.4f} {mse:>12.1f}")
# Модель                        R²          MSE
# -----------------------------------------------
# LinearRegression            0.9982        356.2
# RandomForest                0.9188      16120.0

На синтетических данных линейная модель выигрывает — данные сгенерированы линейно. На реальных данных RandomForest обычно показывает лучший R².

GradientBoostingClassifier

Последовательное построение деревьев: каждое новое дерево исправляет ошибки предыдущих. Медленнее Random Forest, но часто точнее. Ключевой гиперпараметр — learning_rate (темп обучения).

from sklearn.ensemble import GradientBoostingClassifier

gb = GradientBoostingClassifier(
    n_estimators=100, learning_rate=0.1, max_depth=3, random_state=42
)
gb.fit(X_train, y_train)
print(f"GradientBoosting accuracy = {gb.score(X_test, y_test):.4f}")
# GradientBoosting accuracy = 0.9250

RandomForest vs GradientBoosting:

Характеристика RandomForest GradientBoosting
Построение Параллельное (деревья независимы) Последовательное (каждое исправляет ошибки)
Скорость Быстрее Медленнее
Переобучение Устойчив (bagging) Склонен при большом n_estimators
Точность Хорошая Часто выше
Настройка Мало гиперпараметров Критичен learning_rate и max_depth

Кластеризация

KMeans

Разбивает данные на K кластеров, минимизируя сумму квадратов расстояний до центроидов. Требует задания числа кластеров заранее. Ключевая метрика — inertia (сумма квадратов расстояний до центров).

from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
from sklearn.metrics import silhouette_score

# Генерация: 300 точек, 3 кластера
X, y_true = make_blobs(n_samples=300, centers=3, cluster_std=0.8, random_state=42)

# KMeans
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
labels = kmeans.fit_predict(X)

print(f"Inertia = {kmeans.inertia_:.2f}")
# Inertia = 549.33
print(f"Silhouette score = {silhouette_score(X, labels):.4f}")
# Silhouette score = 0.7513
print(f"Центроиды:\n{kmeans.cluster_centers_}")
# Центроиды:
# [[-9.04 -3.80]
#  [-3.70  7.00]
#  [-6.84 -8.12]]

Подбор числа кластеров: метод локтя (elbow)

inertias = []
K_range = range(1, 10)
for k in K_range:
    km = KMeans(n_clusters=k, random_state=42, n_init=10)
    km.fit(X)
    inertias.append(km.inertia_)

for k, inertia in zip(K_range, inertias):
    marker = " ◀ изгиб" if k == 3 else ""
    print(f"  k={k}: inertia={inertia:.1f}{marker}")
#   k=1: inertia=6967.5
#   k=2: inertia=1994.1
#   k=3: inertia=549.3 ◀ изгиб
#   k=4: inertia=393.4
#   k=5: inertia=290.2
#   ...

«Локоть» — точка, после которой уменьшение inertia замедляется. Здесь это k=3, что совпадает с истинным числом кластеров.

Уменьшение размерности

PCA (Principal Component Analysis)

Находит направления максимальной дисперсии в данных и проецирует на них. Позволяет сжать данные, убрать шум, визуализировать многомерные данные в 2D.

from sklearn.decomposition import PCA

# Данные: iris — 4 признака → 2 главные компоненты
X, y = load_iris(return_X_y=True)

pca = PCA(n_components=2, random_state=42)
X_pca = pca.fit_transform(X)

print(f"Исходная размерность: {X.shape[1]} признаков")
print(f"После PCA:           {X_pca.shape[1]} компоненты")
print(f"Explained variance ratio: {pca.explained_variance_ratio_}")
# Explained variance ratio: [0.9246 0.0530]
print(f"Суммарно объяснено дисперсии: {pca.explained_variance_ratio_.sum():.2%}")
# Суммарно объяснено дисперсии: 97.76%

Две первые компоненты сохранили 97.8% информации — можно смело визуализировать в 2D вместо 4D.

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

Пайплайн — цепочка шагов предобработки и модели. Гарантирует, что fit/transform вызываются в правильном порядке, исключает утечку данных при кросс-валидации.

make_pipeline — простой пайплайн

from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression

# Пайплайн: масштабирование → классификатор
pipe = make_pipeline(StandardScaler(), LogisticRegression(random_state=42))
pipe.fit(X_train, y_train)
print(f"Pipeline accuracy = {pipe.score(X_test, y_test):.4f}")
# Pipeline accuracy = 0.9556

# Доступ к шагам по имени
print(f"Коэффициенты: {pipe.named_steps['logisticregression'].coef_}")

ColumnTransformer — разные преобразования для разных колонок

import pandas as pd
from sklearn.compose import make_column_transformer
from sklearn.preprocessing import OneHotEncoder

# Данные с числовыми и категориальными признаками
df = pd.DataFrame({
    'age':    [25, 32, 28, 45, 33],
    'city':   ['Moscow', 'SPb', 'Kazan', 'Moscow', 'SPb'],
    'salary': [80_000, 120_000, 90_000, 150_000, 110_000],
    'target': [0, 1, 0, 1, 0],
})

X = df[['age', 'city', 'salary']]
y = df['target']

# ColumnTransformer: числовые → масштабирование, категориальные → OneHot
preprocessor = make_column_transformer(
    (StandardScaler(), ['age', 'salary']),
    (OneHotEncoder(drop='first'), ['city']),
)

# Полный пайплайн
pipe = make_pipeline(preprocessor, LogisticRegression(random_state=42))
pipe.fit(X, y)
print(f"ColumnTransformer pipeline accuracy = {pipe.score(X, y):.4f}")
# ColumnTransformer pipeline accuracy = 1.0000

На реальных данных вместо имён колонок можно использовать make_column_selector(dtype_include='object') для авто-выбора категориальных колонок.

Подбор гиперпараметров

GridSearchCV — полный перебор

Перебирает ВСЕ комбинации из сетки параметров. Подходит, когда параметров мало и сетка невелика.

from sklearn.model_selection import GridSearchCV

param_grid = {
    'n_estimators': [50, 100, 200],
    'max_depth':    [3, 5, 10, None],
}

rf = RandomForestClassifier(random_state=42)
grid = GridSearchCV(rf, param_grid, cv=5, scoring='accuracy', n_jobs=-1)
grid.fit(X_train, y_train)

print(f"Best params: {grid.best_params_}")
# Best params: {'max_depth': 5, 'n_estimators': 50}
print(f"Best CV score: {grid.best_score_:.4f}")
# Best CV score: 0.9200
print(f"Test score:    {grid.score(X_test, y_test):.4f}")
# Test score:    0.9200

RandomizedSearchCV — случайный поиск

Случайно выбирает N комбинаций из распределений. Быстрее GridSearchCV при большом пространстве параметров.

from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint, uniform

param_dist = {
    'n_estimators':     randint(50, 300),
    'max_depth':        randint(3, 20),
    'learning_rate':    uniform(0.01, 0.3),
    'subsample':        uniform(0.6, 0.4),
}

gb = GradientBoostingClassifier(random_state=42)
random_search = RandomizedSearchCV(
    gb, param_dist, n_iter=20, cv=3, scoring='accuracy',
    random_state=42, n_jobs=-1
)
random_search.fit(X_train, y_train)

print(f"Best params: {random_search.best_params_}")
# Best params: {'learning_rate': 0.22, 'max_depth': 7,
#               'n_estimators': 161, 'subsample': 0.98}
print(f"Best score:  {random_search.best_score_:.4f}")
# Best score:  0.9175

cross_val_score — быстрая кросс-валидация

Оценка модели без подбора параметров — просто понять, насколько стабильно качество:

from sklearn.model_selection import cross_val_score

rf = RandomForestClassifier(n_estimators=100, random_state=42)
scores = cross_val_score(rf, X, y, cv=5, scoring='accuracy')

print(f"Scores per fold: {scores}")
# Scores per fold: [0.95 0.95 0.90 0.95 0.90]
print(f"Mean ± std:      {scores.mean():.4f} ± {scores.std():.4f}")
# Mean ± std:      0.9300 ± 0.0245

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

Сводная таблица: какую метрику использовать для какой задачи.

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

Метрика Функция sklearn Что измеряет Когда использовать
Accuracy accuracy_score Доля правильных ответов Сбалансированные классы
Precision precision_score Точность положительных предсказаний Важно не допустить ложных срабатываний (спам-фильтр)
Recall recall_score Полнота — сколько positives найдено Важно не пропустить positives (поиск болезней)
F1-score f1_score Гармоническое среднее precision и recall Несбалансированные классы
ROC-AUC roc_auc_score Качество разделения классов Бинарная классификация, нужны вероятности
Confusion matrix confusion_matrix Полная картина ошибок Детальный анализ ошибок
Classification report classification_report Precision/Recall/F1 по классам Отчёт «всё сразу»
from sklearn.metrics import (
    accuracy_score, precision_score, recall_score, f1_score,
    roc_auc_score, confusion_matrix, classification_report
)

# Бинарная классификация
y_true = [0, 1, 0, 1, 0, 1, 0, 0, 1, 1]
y_pred = [0, 0, 0, 1, 0, 1, 1, 0, 1, 1]

print(f"Accuracy:  {accuracy_score(y_true, y_pred):.3f}")   # 0.800
print(f"Precision: {precision_score(y_true, y_pred):.3f}")  # 0.800
print(f"Recall:    {recall_score(y_true, y_pred):.3f}")     # 0.800
print(f"F1:        {f1_score(y_true, y_pred):.3f}")         # 0.800

Регрессия

Метрика Функция sklearn Что измеряет Примечание
R² r2_score Доля объяснённой дисперсии (0..1, 1 = идеально) Основная метрика
MSE mean_squared_error Средний квадрат ошибки Штрафует выбросы квадратично
MAE mean_absolute_error Средний модуль ошибки Устойчив к выбросам
RMSE root_mean_squared_error Корень из MSE (в единицах y) Интерпретируема: «в среднем ошибаемся на N единиц»
from sklearn.metrics import mean_absolute_error, root_mean_squared_error

y_true = [100, 200, 300, 400, 500]
y_pred = [110, 190, 310, 380, 520]

print(f"R²:    {r2_score(y_true, y_pred):.4f}")             # 0.9880
print(f"MSE:   {mean_squared_error(y_true, y_pred):.1f}")   # 240.0
print(f"MAE:   {mean_absolute_error(y_true, y_pred):.1f}")  # 14.0
print(f"RMSE:  {root_mean_squared_error(y_true, y_pred):.1f}") # 15.5

Кластеризация

Метрика Функция sklearn Что измеряет
Silhouette silhouette_score Качество разделения кластеров (-1..1, чем ближе к 1, тем лучше)
Inertia kmeans.inertia_ Сумма квадратов расстояний до центроидов (чем меньше, тем плотнее кластеры)

Предобработка данных

Масштабирование (scaling)

Многие модели (SVM, логистическая регрессия, нейросети, KMeans, PCA) требуют масштабирования. Деревья и случайный лес — не требуют.

from sklearn.preprocessing import StandardScaler, MinMaxScaler

X = np.array([[1, -1], [2, 0], [3, 1], [4, 2]], dtype=float)

# StandardScaler: z-нормализация → mean=0, std=1
scaler = StandardScaler()
X_std = scaler.fit_transform(X)
print(f"StandardScaler:\n{X_std}")
# [[-1.34 -1.34]
#  [-0.45 -0.45]
#  [ 0.45  0.45]
#  [ 1.34  1.34]]

# MinMaxScaler: в диапазон [0, 1]
mms = MinMaxScaler()
X_mms = mms.fit_transform(X)
print(f"MinMaxScaler:\n{X_mms}")
# [[0.   0.  ]
#  [0.33 0.33]
#  [0.67 0.67]
#  [1.   1.  ]]

Кодирование категориальных признаков

from sklearn.preprocessing import LabelEncoder, OneHotEncoder

# LabelEncoder: строки → числа (для целевой переменной!)
le = LabelEncoder()
cities = ['Moscow', 'SPb', 'Kazan', 'Moscow', 'SPb']
print(f"LabelEncoder: {le.fit_transform(cities)}")
# LabelEncoder: [1 2 0 1 2]

# OneHotEncoder: строки → бинарные столбцы (для признаков!)
ohe = OneHotEncoder(sparse_output=False)
cities_2d = np.array(cities).reshape(-1, 1)
print(f"OneHotEncoder:\n{ohe.fit_transform(cities_2d)}")
# [[0. 1. 0.]
#  [0. 0. 1.]
#  [1. 0. 0.]
#  [0. 1. 0.]
#  [0. 0. 1.]]

Разделение на train/test

from sklearn.model_selection import train_test_split

# Базовое: 80% train, 20% test
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Со стратификацией (сохраняет пропорции классов — важно для несбалансированных!)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

Частые ошибки

Ошибка ❌ Неправильно ✅ Правильно
Утечка данных: fit на всём датасете scaler.fit(X) → X_train, X_test = ... scaler.fit(X_train) → scaler.transform(X_test)
fit_transform на test X_test_scaled = scaler.fit_transform(X_test) X_test_scaled = scaler.transform(X_test)
Нет random_state train_test_split(X, y) train_test_split(X, y, random_state=42)
Несбалансированные классы без class_weight LogisticRegression() LogisticRegression(class_weight='balanced')
Масштабирование после train_test_split см. пункт 1 Масштабировать train и test одним scaler'ом
OneHotEncoder для новых категорий Падает ValueError на test OneHotEncoder(handle_unknown='ignore')
Перекрёстная проверка без пайплайна cross_val_score(model, scaler.fit_transform(X), y) cross_val_score(make_pipeline(scaler, model), X, y)
Метрики регрессии для классификации r2_score(y_true, y_pred_classes) accuracy_score / f1_score
KMeans без n_init KMeans(n_clusters=3) — предупреждение KMeans(n_clusters=3, n_init=10, random_state=42)

Ресурсы

Что дальше


Связанные шпаргалки: Pandas, NumPy, Matplotlib, Seaborn