Машинное обучение: карта трека, типы задач и постановка
Эта статья — вход в трек. Она не пересказывает пятнадцать последующих материалов, а даёт систему координат, без которой они превращаются в список библиотечных вызовов. После неё вы сможете про любую задачу ответить на четыре вопроса: чему именно учится модель, на каких данных, что мы минимизируем и как узнаем, что не обманываем себя. Практически все катастрофы в ML-проектах — это неверный ответ на один из этих четырёх вопросов, а вовсе не «взяли не тот градиентный бустинг».
1. Почему вообще нужно учиться по данным
Начнём с честного вопроса: зачем ML, если есть обычный код? Возьмём задачу «отличить спам от не-спама». Первое решение очевидно и не требует никакой математики — правила:
def is_spam(text: str) -> bool:
# Ручные правила: работают ровно до тех пор, пока противник о них не узнал
lowered = text.lower()
if "виагра" in lowered:
return True
if lowered.count("!") > 5:
return True
return False
Такой код живёт неделю. Дальше происходит ровно три вещи. Спамеры пишут «в и а г р а», и правило рассыпается. Правил становится четыреста, они начинают противоречить друг другу, и никто не решается удалить ни одно из них. Появляется вторая страна с другим языком, и всю пирамиду надо строить заново.
Ключевое наблюдение: мы не умеем выписать правило, но умеем узнать ответ, когда видим пример. Разметить сто
тысяч писем «спам / не спам» скучно, но выполнимо; выписать функцию, отделяющую одно от другого, — нет. Машинное
обучение — это инженерный ответ именно на такую асимметрию: вместо программы мы пишем процедуру порождения
программы из примеров. Классическая формулировка Тома Митчелла: программа обучается на опыте E относительно
класса задач T и меры качества P, если её качество на задачах T, измеренное через P, растёт с накоплением
опыта E (Mitchell, Machine Learning, 1997).
Отсюда сразу следует критерий применимости, который стоит применять до любого кода:
| ML уместен, когда | ML не нужен (и вреден), когда |
|---|---|
| правило существует, но его сложно выписать явно | правило известно и стабильно — это if, а не модель |
| есть исторические данные с наблюдаемым исходом | исход не наблюдается или наблюдается через годы |
| ошибки допустимы и стоят конечных денег | цена единичной ошибки катастрофична и необъяснима |
| распределение данных меняется медленно | правила меняются приказом каждый месяц |
| решение принимается часто, выигрыш масштабируется | решение принимается пять раз в год — дешевле эксперт |
Отдельно: ML не создаёт информацию, которой нет в данных. Если по имеющимся признакам два объекта неотличимы, а
исходы у них разные, никакая модель их не разделит — это неустранимая (байесовская) ошибка. Половина проектов,
которые «не взлетели из-за слабой модели», на самом деле упёрлись в отсутствие сигнала, и правильный ход был —
идти собирать новые признаки, а не менять RandomForest на CatBoost.
2. Формальная постановка: риск, эмпирический риск, обобщение
Договоримся о языке — он единый для всего трека.
- Объект
x ∈ X— то, о чём принимаем решение (письмо, клиент, снимок). Обычно вектор признаковx ∈ ℝᵈ. - Ответ (таргет)
y ∈ Y— то, что предсказываем.Y = ℝ— регрессия,Y = {0,1}— бинарная классификация. - Распределение
P(x, y)— неизвестный источник данных. Мы видим лишь выборкуS = {(xᵢ, yᵢ)}ᵢ₌₁ⁿ, предположительно независимую и одинаково распределённую (i.i.d.) изP. - Гипотеза
h: X → Yиз семействаH(все линейные функции, все деревья глубины ≤ 6, все сети данной архитектуры).H— это и есть «что модель в принципе способна выразить». - Функция потерь
L(y, ŷ) ≥ 0— цена одной ошибки. Квадратичная(y − ŷ)², абсолютная|y − ŷ|, логистическая−[y·log p + (1−y)·log(1−p)].
Цель — минимизировать риск (ожидаемые потери на всём распределении, включая примеры, которых мы никогда не видели):
R(h) = E_{(x,y) ~ P} [ L(y, h(x)) ]
Беда в том, что P неизвестно, и R(h) невычислим. Всё, что доступно, — эмпирический риск на выборке:
R̂(h) = (1/n) · Σᵢ L(yᵢ, h(xᵢ))
Метод обучения, минимизирующий R̂ по h ∈ H, называется минимизацией эмпирического риска (ERM). Почти всё в
классическом ML — это ERM плюс регуляризатор: линейная регрессия, логистическая регрессия, SVM, бустинг, нейросети
отличаются выбором H, L и способом оптимизации. Держите эту рамку в голове, читая статьи трека, — она склеивает
их в одну картину.
Разложим ошибку на три слагаемых — это самая полезная схема для диагностики:
R(ĥ) − R* = [ R(h*) − R* ] + [ R(ĥ) − R(h*) ]
ошибка аппроксимации ошибка оценивания
где R* — байесовский риск (неустранимый шум), h* — лучшая гипотеза внутри H, ĥ — то, что реально нашёл
алгоритм на конечной выборке. Первое слагаемое падает, если расширять H (брать более гибкие модели); второе
слагаемое от этого растёт, потому что на конечной выборке легче случайно подогнаться. Это и есть компромисс
смещение — разброс (bias-variance), которому целиком посвящена статья
https://courses.digitable.life/post/machine-learning/11-overfitting-and-regularization/.
Из картинки следуют два практических правила, которые нарушают чаще всего:
- Ошибка на обучающей выборке ничего не говорит о качестве модели. Модель «запомнить всю таблицу» имеет нулевую ошибку на train и бесполезна. Любая цифра, полученная на тех же данных, на которых шло обучение, — не оценка, а самообман.
- Минимум справа существует не всегда. Для сильно переопределённых моделей (большие нейросети) кривая валидации после пика может снова пойти вниз — эффект double descent (Belkin et al., 2019, arxiv.org/abs/1812.11118). Классическая U-образная картинка — про модели умеренной ёмкости, то есть про почти весь табличный ML.
И фундаментальное ограничение, известное как теорема об отсутствии бесплатных завтраков (Wolpert, 1996): усреднённо по всем возможным задачам все алгоритмы одинаковы. Работают они только потому, что реальные задачи не случайны — в них есть гладкость, локальность, иерархия. Выбор алгоритма — это выбор предположения о структуре мира, а не поиск «объективно лучшего». Отсюда следует, что вопрос «какая модель лучше?» без указания задачи и данных некорректен, а единственный ответ — эксперимент на вашей выборке.
3. Таксономия: какие вообще бывают задачи
Разберём по существу, а не по названиям.
Обучение с учителем (supervised). Есть пары (x, y) — известен правильный ответ. Девяносто процентов
прикладных задач здесь. Внутри деление по типу Y:
- Регрессия:
Y = ℝ. Прогноз цены, времени доставки, потребления. Метрики — MAE, RMSE, MAPE. См. https://courses.digitable.life/post/machine-learning/03-linear-and-logistic-regression/. - Классификация:
Y— конечное множество. Отток клиента, дефект на снимке, тема письма. Важнейший нюанс: модель почти всегда выдаёт вероятность, а не класс; превращение вероятности в решение — отдельный шаг с порогом, который выбирается по цене ошибок (https://courses.digitable.life/post/machine-learning/10-model-evaluation/). - Ранжирование: нужно упорядочить кандидатов, а не оценить каждого в отдельности. Метрики попарные и списочные (NDCG, MAP). Базис рекомендаций и поиска (https://courses.digitable.life/post/machine-learning/12-recommender-systems/).
- Прогнозирование рядов: формально регрессия, но наблюдения зависимы во времени, и обычная кросс-валидация ломается (https://courses.digitable.life/post/machine-learning/13-time-series/).
Обучение без учителя (unsupervised). Есть только x. Модель ищет структуру: группы похожих объектов
(https://courses.digitable.life/post/machine-learning/08-clustering/), низкоразмерное представление (https://courses.digitable.life/post/machine-learning/09-dimensionality-reduction/),
редкие события. Главная сложность здесь не алгоритмическая, а оценочная: правильного ответа нет, поэтому
«хорошесть» кластеризации приходится определять через внешнюю задачу или экспертизу. Никогда не запускайте
кластеризацию, не сформулировав заранее, какое решение вы примете по её результату.
Self-supervised. Ответ конструируется из самих данных: предскажи следующее слово, закрытый кусок картинки, близость двух аугментаций одного объекта. Это то, на чём стоят современные языковые и визуальные модели — тема трека нейросетей (https://courses.digitable.life/post/neural-networks/00-overview/).
Обучение с подкреплением (RL). Нет фиксированной выборки: агент действует, среда отвечает наградой, а действия меняют будущие данные. Здесь возникают свои сущности — состояние, политика, отложенное вознаграждение (https://courses.digitable.life/post/machine-learning/14-reinforcement-learning/). Частный и очень практичный случай — многорукие бандиты для распределения трафика.
Причинность и uplift. Отдельный контур, который путают с классификацией чаще всего. Обычная модель отвечает на
вопрос «кто уйдёт?», а бизнесу почти всегда нужен ответ на вопрос «кого удержит скидка?». Это разные величины:
первая — P(y|x), вторая — разница P(y|x, воздействие) − P(y|x, без воздействия), которую нельзя получить из
наблюдательных данных без эксперимента или сильных допущений. Правильный ответ на «кто уйдёт» и неправильная
интерпретация стоили компаниям очень дорого; см. Pearl, The Book of Why и обзор uplift-моделирования в
causalml.
4. Постановка задачи: путь от вопроса бизнеса к обучающей выборке
Самая дорогая работа в ML делается до первой строки кода. Схема ниже — рабочий чек-лист.
что за решение принимается?"] --> B{"Решение зависит
от предсказания?"} B -- нет --> Z["ML не нужен:
отчёт, правило, эксперимент"] B -- да --> C["Определить объект:
что такое одна строка выборки"] C --> D["Определить таргет y:
наблюдаем ли исход и когда"] D --> E{"Исход наблюдается
для всех объектов?"} E -- нет --> F["Смещение отбора:
чинить дизайн сбора данных"] E -- да --> G["Зафиксировать момент t0:
какие признаки доступны в проде"] G --> H["Собрать выборку строго
по состоянию на t0"] H --> I["Выбрать функцию потерь
и метрику качества"] I --> J["Разбиение train/valid/test
по времени и группам"] J --> K["Baseline: константа,
текущее правило, простая модель"] K --> L{"Модель бьёт baseline
значимо?"} L -- нет --> M["Искать признаки/данные,
а не менять алгоритм"] L -- да --> N["Оценка выигрыша в деньгах,
затем интеграция"]
Четыре узла этой схемы разберём подробно, потому что именно они ломаются.
Что такое одна строка. Ответ неочевиден чаще, чем кажется. «Отток клиентов» — это строка на клиента, на
клиента-месяц или на клиента-день? От выбора зависит всё: размер выборки, определение таргета, зависимость строк
между собой и корректность разбиения. Строки «клиент-месяц» коррелируют внутри клиента, поэтому случайное
разбиение раскидает один и тот же клиент в train и test, и оценка будет завышенной. Лечится группировкой
(GroupKFold).
Момент t0 и горизонт. Модель в проде работает в момент t0 и имеет доступ ровно к тем данным, что
существовали к t0. Таргет наблюдается позже — через горизонт H (уйдёт ли клиент в течение 30 дней). Значит,
обучающая выборка должна собираться так же: признаки — срезом на t0, таргет — из окна (t0, t0+H]. Нарушение
этого правила — самая частая и самая незаметная утечка целевой переменной (data leakage). Каноничный пример:
признак «количество обращений в поддержку», посчитанный по всей истории, включая обращение, которым клиент
сообщил об уходе. Модель показывает AUC 0.98 на валидации и 0.55 в проде. Подробный разбор видов утечек —
https://courses.digitable.life/post/machine-learning/02-data-and-features/.
Метрика. Их всегда три уровня, и их нельзя смешивать:
| Уровень | Пример | Кто смотрит | Когда измеряется |
|---|---|---|---|
| Бизнес-метрика | удержанная выручка, отток в % | продукт, финансы | недели, A/B-тест |
| Прокси / офлайн-метрика решения | precision@k при бюджете 5000 звонков | аналитик, DS | сразу на holdout |
| Функция потерь при обучении | logloss, MSE, pairwise-loss | оптимизатор | каждый шаг |
Функция потерь оптимизируется, потому что она гладкая и дифференцируемая. Метрика решения — то, по чему выбирается модель и порог. Бизнес-метрика — то, ради чего всё затевалось. Классическая ошибка — выбирать модель по accuracy, когда решение принимается по топ-5% скоринга, или гнаться за AUC при том, что в проде важна калибровка вероятностей (если вероятность умножается на сумму убытка — она обязана быть настоящей вероятностью, см. https://courses.digitable.life/post/machine-learning/10-model-evaluation/).
Baseline. До любой модели зафиксируйте три числа: качество константы (среднее / самый частый класс), качество текущего правила, если оно есть, и качество простейшей модели (логистическая регрессия на пяти очевидных признаках). Без этого невозможно понять, много ли даёт «AUC 0.81». Половина запущенных в прод моделей проигрывала правилу «позвонить тем, у кого просрочка больше 30 дней», и никто не заметил, потому что правило не измеряли.
5. Данные и честная оценка
Обобщение проверяется только на данных, которые не участвовали ни в обучении, ни в выборе гиперпараметров, ни в отборе признаков. Отсюда трёхчастное разбиение и его вариации.
Ключевые правила:
- Test трогают один раз. Как только вы посмотрели на test и что-то поменяли — test стал валидацией, и оценка снова оптимистична. Это неформальный, но абсолютно реальный механизм переобучения «руками аналитика».
- Схема разбиения должна повторять способ применения. Прод предсказывает будущее по прошлому — валидация обязана делать то же. Прод видит новых клиентов — валидация должна разделять по клиентам, а не по строкам.
- Все преобразования учатся только на train. Среднее для заполнения пропусков, параметры масштабирования,
словарь для target-encoding — всё считается внутри фолда. Именно поэтому в scikit-learn существует
Pipeline: он гарантирует это структурно, а не силой воли. - Стратификация при дисбалансе. При 1% положительного класса случайное разбиение даёт фолды с сильно разной
долей класса, и метрики скачут.
StratifiedKFoldрешает вопрос.
Минимальный, но полностью корректный baseline на scikit-learn — шаблон, который стоит копировать в каждый новый проект:
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.dummy import DummyClassifier
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import average_precision_score, roc_auc_score
from sklearn.model_selection import StratifiedKFold, cross_val_score, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
df = pd.read_parquet("churn_snapshot.parquet") # признаки — срезом на t0
y = df.pop("churn_30d").to_numpy() # таргет — из окна (t0, t0+30 дней]
num_cols = df.select_dtypes(include="number").columns.tolist()
cat_cols = df.select_dtypes(include=["object", "category"]).columns.tolist()
# Все преобразования — ВНУТРИ пайплайна, иначе статистики протекут из валидации в обучение
prep = ColumnTransformer([
("num", Pipeline([
("impute", SimpleImputer(strategy="median")),
("scale", StandardScaler()),
]), num_cols),
("cat", Pipeline([
("impute", SimpleImputer(strategy="most_frequent")),
# handle_unknown="ignore" — в проде обязательно появятся невиданные категории
("ohe", OneHotEncoder(handle_unknown="ignore", min_frequency=20)),
]), cat_cols),
])
X_train, X_test, y_train, y_test = train_test_split(
df, y, test_size=0.2, stratify=y, random_state=42
)
models = {
"константа": DummyClassifier(strategy="prior"),
"логрегрессия": Pipeline([("prep", prep), ("clf", LogisticRegression(max_iter=1000, C=1.0))]),
# Бустинг по гистограммам сам работает с пропусками и не требует масштабирования
"бустинг": HistGradientBoostingClassifier(max_depth=6, learning_rate=0.06, max_iter=400),
}
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
for name, model in models.items():
# PR-AUC информативнее ROC-AUC при сильном дисбалансе классов
scores = cross_val_score(model, X_train, y_train, cv=cv, scoring="average_precision")
print(f"{name:14s} PR-AUC = {scores.mean():.3f} ± {scores.std():.3f}")
Замечание о дисперсии оценки: разброс ±0.02 при разнице моделей в 0.01 означает, что разницы нет. Сравнивать
модели нужно по одним и тем же фолдам (парный тест), а не по независимым запускам, — иначе шум разбиения
маскирует эффект.
6. ERM своими руками: что на самом деле делает fit
Чтобы формулы из раздела 2 перестали быть абстракцией, реализуем логистическую регрессию с нуля — это ровно ERM с логистической потерей и L2-регуляризатором, оптимизируемый градиентным спуском.
Псевдокод:
вход: X (n×d), y ∈ {0,1}^n, шаг η, регуляризация λ, число итераций T
w ← 0, b ← 0
повторить T раз:
z ← X·w + b # линейные оценки
p ← σ(z) = 1 / (1 + exp(−z)) # вероятности
g_w ← (1/n)·Xᵀ·(p − y) + λ·w # градиент logloss + L2
g_b ← (1/n)·Σ(p − y)
w ← w − η·g_w ; b ← b − η·g_b
вернуть w, b
import numpy as np
def sigmoid(z: np.ndarray) -> np.ndarray:
"""Численно устойчивая сигмоида: exp не переполняется ни при каком знаке z."""
out = np.empty_like(z, dtype=float)
pos, neg = z >= 0, z < 0
out[pos] = 1.0 / (1.0 + np.exp(-z[pos]))
ez = np.exp(z[neg])
out[neg] = ez / (1.0 + ez)
return out
def fit_logreg(X: np.ndarray, y: np.ndarray, lr: float = 0.5,
l2: float = 1e-3, epochs: int = 2000) -> tuple[np.ndarray, float]:
n, d = X.shape
w, b = np.zeros(d), 0.0
for step in range(epochs):
p = sigmoid(X @ w + b)
residual = p - y # (p − y) — градиент logloss по z
grad_w = X.T @ residual / n + l2 * w
grad_b = residual.mean()
w -= lr * grad_w
b -= lr * grad_b
if step % 500 == 0:
eps = 1e-12 # защита от log(0)
loss = -np.mean(y * np.log(p + eps) + (1 - y) * np.log(1 - p + eps))
print(f"эпоха {step:5d} logloss = {loss + 0.5 * l2 * w @ w:.4f}")
return w, b
rng = np.random.default_rng(0)
X = rng.normal(size=(4000, 3))
true_w = np.array([1.5, -2.0, 0.7])
y = (rng.uniform(size=4000) < sigmoid(X @ true_w - 0.3)).astype(float)
w, b = fit_logreg(X, y)
print("восстановленные веса:", np.round(w, 2), "смещение:", round(b, 2))
Сложность: одна итерация — O(n·d) по времени и O(d) дополнительной памяти, всего O(T·n·d). Отсюда сразу
понятно, почему стохастический градиентный спуск (мини-батчи) выигрывает на больших n: он делает n/B шагов за
ту же стоимость одного полного прохода и обычно сходится за 1–3 эпохи вместо тысяч итераций. Для сравнения:
точное решение линейной регрессии через нормальные уравнения стоит O(n·d² + d³) — при d ≈ 10⁴ это уже
неприемлемо, и итеративные методы становятся единственным вариантом.
Обратите внимание на две детали, которые в учебниках проскакивают мимо, а в проде стоят дней отладки: устойчивая
сигмоида (наивная 1/(1+exp(-z)) переполняется при z ≈ −800 и даёт NaN в градиенте) и eps под логарифмом.
Численная устойчивость — не педантизм, а причина, по которой существуют log_loss с клиппингом и
logsumexp. Подробнее о математической подложке — https://courses.digitable.life/post/machine-learning/01-math-foundations/.
7. Как выбирать семейство моделей
Правило, которое экономит месяцы: начинайте с самой простой модели, которая может сработать, и усложняйте только при доказанной нехватке. Для табличных данных практическая последовательность такова: константа → логистическая или линейная регрессия → градиентный бустинг на деревьях → и только если бустинг явно упирается, всё остальное. Нейросети на табличных данных систематически не выигрывают у бустинга — это подтверждено аккуратными сравнениями (Grinsztajn et al., 2022, arxiv.org/abs/2207.08815).
Что стоит за координатами:
- Логистическая регрессия остаётся стандартом там, где решение надо объяснять регулятору: коэффициенты имеют смысл, модель монотонна, калибровка хорошая из коробки (https://courses.digitable.life/post/machine-learning/03-linear-and-logistic-regression/).
- kNN и наивный Байес — быстрые, почти без обучения, отличные baseline; kNN страдает от проклятия размерности, Байес — от предположения независимости (https://courses.digitable.life/post/machine-learning/04-knn-and-naive-bayes/).
- SVM блистает на средних выборках с чётким зазором, но плохо масштабируется: ядровые методы требуют
O(n²)памяти (https://courses.digitable.life/post/machine-learning/05-svm/). - Деревья интерпретируемы и не требуют масштабирования, но нестабильны — одна строка меняет всё дерево (https://courses.digitable.life/post/machine-learning/06-decision-trees/). Ровно эта нестабильность и делает их идеальным строительным блоком для ансамблей (https://courses.digitable.life/post/machine-learning/07-ensembles-and-boosting/).
- Стекинг даёт последние доли процента ценой невозможности отладки и хрупкости в проде. На соревнованиях — всегда, в продакшене — почти никогда.
Приблизительные ориентиры по стоимости обучения (n — объекты, d — признаки, k — деревья/соседи/компоненты):
| Метод | Обучение | Предсказание одного объекта | Память |
|---|---|---|---|
| Линейная/логистическая регрессия (SGD) | O(T·n·d) |
O(d) |
O(d) |
| kNN | O(1) (хранение) |
O(n·d) или O(log n) с KD-деревом |
O(n·d) |
| Решающее дерево | O(n·d·log n) |
O(глубина) |
O(число узлов) |
| Случайный лес (k деревьев) | O(k·n·d·log n) |
O(k·глубина) |
O(k·узлов) |
| Бустинг по гистограммам | O(k·(n + бины·d)) |
O(k·глубина) |
O(n + k·узлов) |
| SVM с ядром | от O(n²·d) до O(n³·d) |
O(число опорных·d) |
O(n²) |
| k-means | O(T·n·k·d) |
O(k·d) |
O(n·d) |
| PCA (полное SVD) | O(n·d·min(n,d)) |
O(d·компонент) |
O(d²) |
8. Модель в проде: не событие, а жизненный цикл
Обученная модель — это не конец, а начало эксплуатации. Файл с весами протухает, потому что мир меняется: меняется
поведение пользователей (concept drift — меняется P(y|x)), меняется состав аудитории (covariate shift —
меняется P(x)), ломается поставщик признака, и колонка молча заполняется нулями.
Три обязательных для прода вещи, которые чаще всего забывают:
- Теневой запуск. Модель считает предсказания на живом трафике, но на решения не влияет. Ловит ровно тот класс
ошибок, который офлайн-оценка не видит принципиально: расхождение офлайн- и онлайн-признаков (
training-serving skew), задержки, отсутствующие в бою поля. - Мониторинг трёх слоёв. Распределения входных признаков (PSI, KS-статистика), распределение самих предсказаний (среднее предсказанной вероятности — самый ранний и дешёвый сигнал беды, доступный ещё до того, как станет известен факт) и бизнес-метрика с лагом.
- Воспроизводимость. Зафиксированы версия данных, версия кода, seed, версия признаков. Без этого фраза «перевыкатите модель прошлой недели» невыполнима. Всему этому посвящена https://courses.digitable.life/post/machine-learning/15-mlops/, а инфраструктуре данных под ней — трек https://courses.digitable.life/post/data-engineering/00-overview/.
Ещё одна особенность, отличающая ML-системы от обычного софта: обратная связь. Скоринг-модель решает, кому
выдать кредит; данные о невозвратах приходят только по выданным кредитам; следующая модель обучается на выборке,
которую сама же и отобрала. Это цензурирование данных, и без явных мер (случайный процент одобрений вне модели,
inverse propensity weighting) система постепенно сходится к собственным предубеждениям. Тот же механизм в
рекомендациях называется петлёй обратной связи и разбирается в https://courses.digitable.life/post/machine-learning/12-recommender-systems/.
Классическое чтение по накопленной сложности таких систем — «Hidden Technical Debt in Machine Learning Systems»
(Sculley et al., NeurIPS 2015,
papers.nips.cc).
9. Типичные ошибки — по убыванию частоты
- Утечка целевой переменной. Признак, который в момент
t0физически не существовал. Симптом — подозрительно высокое качество. Проверка: для каждого признака ответьте, откуда он берётся в проде и с какой задержкой. - Неправильная схема валидации. Случайное разбиение на временных данных или на данных с группами. Симптом — офлайн отлично, онлайн никак.
- Оценка на данных, использованных для отбора. Отобрали признаки по всей выборке, потом сделали CV. Отбор — часть обучения, он обязан быть внутри фолда.
- Accuracy при дисбалансе. При 1% положительных константа «всё отрицательно» даёт 99%. Смотрите PR-AUC, recall при фиксированном бюджете, матрицу ошибок.
- Оптимизация метрики вместо решения. Модель улучшила AUC на 0.01, но порог не пересчитали, и в проде ничего не изменилось.
- Отсутствие baseline. Невозможно понять, есть ли выигрыш вообще.
- Игнорирование дисперсии оценки. Выбор модели по разнице, лежащей внутри доверительного интервала, — выбор шума.
- Обучение на «почищенных» данных, применение на грязных. Выкинули строки с пропусками при обучении — в проде они никуда не делись.
- Дообучение препроцессинга на всей выборке. Классика:
StandardScaler().fit(X)до разбиения. - Модель без владельца. Через полгода никто не знает, кто её переобучает и что делать при деградации.
10. Как устроен трек
Порядок чтения и что забирать из каждой статьи:
| Статья | Зачем она вам |
|---|---|
| https://courses.digitable.life/post/machine-learning/01-math-foundations/ — Математика для ML | векторы, градиенты, вероятности — язык всех остальных статей |
| https://courses.digitable.life/post/machine-learning/02-data-and-features/ — Данные и признаки | 80% результата: кодирование, масштабирование, пропуски, утечки |
| https://courses.digitable.life/post/machine-learning/03-linear-and-logistic-regression/ — Регрессии | базовая модель и интуиция про веса, вероятности, регуляризацию |
| https://courses.digitable.life/post/machine-learning/04-knn-and-naive-bayes/ — kNN и Байес | метрики близости, проклятие размерности, генеративный подход |
| https://courses.digitable.life/post/machine-learning/05-svm/ — SVM | зазор, опорные векторы, ядерный трюк как общая идея |
| https://courses.digitable.life/post/machine-learning/06-decision-trees/ — Деревья | нелинейность без масштабирования, критерии разбиения |
| https://courses.digitable.life/post/machine-learning/07-ensembles-and-boosting/ — Ансамбли | рабочая лошадь табличного ML: бэггинг, RF, бустинг |
| https://courses.digitable.life/post/machine-learning/08-clustering/ — Кластеризация | структура без разметки и как её честно оценивать |
| https://courses.digitable.life/post/machine-learning/09-dimensionality-reduction/ — Снижение размерности | PCA/SVD/t-SNE/UMAP, сжатие и визуализация |
| https://courses.digitable.life/post/machine-learning/10-model-evaluation/ — Оценка | метрики, кросс-валидация, пороги, калибровка |
| https://courses.digitable.life/post/machine-learning/11-overfitting-and-regularization/ — Переобучение | bias-variance, L1/L2, ранняя остановка |
| https://courses.digitable.life/post/machine-learning/12-recommender-systems/ — Рекомендации | коллаборативная фильтрация, ранжирование, петли обратной связи |
| https://courses.digitable.life/post/machine-learning/13-time-series/ — Временные ряды | зависимость во времени, ARIMA, признаки для бустинга |
| https://courses.digitable.life/post/machine-learning/14-reinforcement-learning/ — RL | среда, политика, отложенная награда, бандиты |
| https://courses.digitable.life/post/machine-learning/15-mlops/ — MLOps | воспроизводимость, деплой, мониторинг, переобучение |
Если нужен только прикладной минимум для работы — читайте 02, 03, 07, 10, 11, 15. Это шесть статей, которые покрывают подавляющее большинство реальных табличных задач.
11. Источники, которым стоит доверять
Учебники (в порядке возрастания сложности):
- Hastie, Tibshirani, Friedman. The Elements of Statistical Learning — канон классического ML, доступен бесплатно: hastie.su.domains/ElemStatLearn.
- James et al. An Introduction to Statistical Learning — та же линия, но мягче и с кодом: statlearning.com.
- Bishop. Pattern Recognition and Machine Learning — вероятностный взгляд, байесовская линия.
- Shalev-Shwartz, Ben-David. Understanding Machine Learning: From Theory to Algorithms — теория обобщения и PAC-обучение строго: cs.huji.ac.il.
- Murphy. Probabilistic Machine Learning — современный и очень полный: probml.github.io.
Практика и продакшн:
- Документация scikit-learn — редкий случай, когда доки одновременно и учебник: scikit-learn.org/stable/user_guide.html.
- Chip Huyen. Designing Machine Learning Systems — про системы, а не про модели.
- Google. Rules of Machine Learning — сорок три правила из практики, читается за час и экономит месяцы: developers.google.com/machine-learning/guides/rules-of-ml.
- Kaufman et al. Leakage in Data Mining — систематика утечек: dl.acm.org/doi/10.1145/2382577.2382579.
Мини-итог
- ML нужен там, где правило существует, но не выписывается явно, а примеры доступны.
- Формально почти всё обучение — минимизация эмпирического риска в выбранном классе гипотез плюс регуляризатор.
- Ошибка раскладывается на неустранимый шум, ошибку аппроксимации и ошибку оценивания; борьба с одной обычно увеличивает другую.
- Тип задачи определяется тем, что такое
yи наблюдается ли он вообще, — и от этого зависит всё остальное. - Постановка (объект, таргет,
t0, горизонт, метрика, разбиение) важнее выбора алгоритма. - Честная оценка требует данных, не участвовавших ни в обучении, ни в настройке; схема разбиения обязана повторять способ применения модели.
- Модель в проде — жизненный цикл с дрейфом, мониторингом и переобучением, а не одноразовый артефакт.
Что дальше
Дальше — язык, на котором записано всё остальное: векторы и матрицы как способ говорить об объектах и признаках, производные и градиенты как механизм обучения, вероятности как способ выражать неуверенность.