Шпаргалка: метрики ML¶
~6 минут чтения
Предварительно: Выбор модели | Подготовка к интервью
Выбор метрики -- это не техническое решение, а бизнес-решение. Метрика определяет, что модель оптимизирует, и неправильная метрика может сделать "хорошую" модель бесполезной. Эта шпаргалка -- decision framework: от типа задачи к конкретной метрике с обоснованием.
Дерево выбора метрики¶
graph TD
START["Тип задачи?"] --> CLS["КЛАССИФИКАЦИЯ"]
START --> REG["РЕГРЕССИЯ"]
START --> RANK["РАНЖИРОВАНИЕ"]
START --> CLUST["КЛАСТЕРИЗАЦИЯ"]
CLS --> BIN["Бинарная?"]
CLS --> MULTI["Многоклассовая?"]
BIN --> BAL["Баланс классов<br/>Accuracy, F1"]
BIN --> IMB["Дисбаланс<br/>F1, PR-AUC"]
BIN --> FN_COST["FN дорого<br/>Recall, F2"]
BIN --> FP_COST["FP дорого<br/>Precision, F0.5"]
MULTI --> MACRO["Классы равны<br/>Macro F1"]
MULTI --> WEIGHTED["Частые важнее<br/>Weighted F1"]
REG --> BIG_ERR["Большие ошибки<br/>MSE, RMSE"]
REG --> OUTLIER["Выбросы<br/>MAE, MedAE"]
REG --> INTERP["Интерпретация<br/>MAPE, R2"]
RANK --> ORDER["Порядок<br/>NDCG, MAP"]
RANK --> FIRST["Первый результат<br/>MRR"]
RANK --> TOPK["Top-K<br/>P@K, R@K"]
CLUST --> LABELS_Y["Есть метки<br/>ARI, NMI"]
CLUST --> LABELS_N["Нет меток<br/>Silhouette, DB"]
style START fill:#e8eaf6,stroke:#3f51b5
style CLS fill:#e8f5e9,stroke:#4caf50
style REG fill:#fff3e0,stroke:#ef6c00
style RANK fill:#f3e5f5,stroke:#9c27b0
style CLUST fill:#fce4ec,stroke:#c62828
Classification Metrics¶
Confusion Matrix¶
Predicted
Negative Positive
Actual Negative TN FP ← Type I Error (ложная тревога)
Positive FN TP ← Type II Error (пропуск)
↑
Пропустили
Основные метрики¶
| Метрика | Формула | Когда использовать |
|---|---|---|
| Accuracy | (TP+TN)/(TP+TN+FP+FN) | Сбалансированные классы |
| Precision | TP/(TP+FP) | Дорогие FP (спам, реклама) |
| Recall (Sensitivity) | TP/(TP+FN) | Дорогие FN (рак, фрод) |
| Specificity | TN/(TN+FP) | Важно не путать здоровых с больными |
| F1-Score | 2·P·R/(P+R) | Баланс Precision и Recall |
| F-beta | (1+β²)·P·R/(β²·P+R) | Настраиваемый баланс |
Примеры бизнес-задач¶
МЕДИЦИНА (рак)
├── Recall важен: не пропустить больного
├── FN = пропущенный рак = ОЧЕНЬ ПЛОХО
└── Метрика: Recall, F2 (больше вес Recall)
СПАМ-ФИЛЬТР
├── Precision важен: не потерять важное письмо
├── FP = важное письмо в спаме = ПЛОХО
└── Метрика: Precision, F0.5 (больше вес Precision)
ФРОД-ДЕТЕКЦИЯ
├── Баланс: и пропустить плохо, и заблокировать хорошего плохо
├── Сильный дисбаланс классов
└── Метрика: PR-AUC, F1
РЕКОМЕНДАЦИИ
├── Precision@K: качество топ-K рекомендаций
└── Recall@K: покрытие интересов пользователя
Пороговые метрики¶
# ROC-AUC
from sklearn.metrics import roc_auc_score, roc_curve
y_prob = model.predict_proba(X_test)[:, 1]
roc_auc = roc_auc_score(y_test, y_prob)
fpr, tpr, thresholds = roc_curve(y_test, y_prob)
# PR-AUC (лучше для дисбаланса)
from sklearn.metrics import average_precision_score, precision_recall_curve
pr_auc = average_precision_score(y_test, y_prob)
precision, recall, thresholds = precision_recall_curve(y_test, y_prob)
Когда что использовать¶
| Ситуация | Метрика | Почему |
|---|---|---|
| Сбалансированные классы | Accuracy, F1 | Работают хорошо |
| Дисбаланс 1:10 | F1, ROC-AUC | Accuracy врёт |
| Дисбаланс 1:100+ | PR-AUC | ROC-AUC оптимистичен |
| Нужен оптимальный порог | ROC/PR кривые | Видно trade-off |
| Сравнение моделей | ROC-AUC, PR-AUC | Не зависят от порога |
Multiclass¶
from sklearn.metrics import f1_score
# Macro: среднее по классам (все классы равны)
f1_macro = f1_score(y_test, y_pred, average='macro')
# Weighted: взвешенное по размеру класса
f1_weighted = f1_score(y_test, y_pred, average='weighted')
# Micro: глобальный TP/FP/FN
f1_micro = f1_score(y_test, y_pred, average='micro')
Regression Metrics¶
| Метрика | Формула | Интерпретация |
|---|---|---|
| MSE | Σ(y-ŷ)²/n | Средний квадрат ошибки |
| RMSE | √MSE | В единицах y |
| MAE | Σ|y-ŷ|/n | Средняя абсолютная ошибка |
| MAPE | Σ|y-ŷ|/y·100% | Процент ошибки |
| R² | 1 - SS_res/SS_tot | Доля объяснённой дисперсии |
Сравнение¶
MSE vs MAE:
├── MSE: сильнее штрафует большие ошибки
├── MAE: устойчивее к выбросам
└── Выбор зависит от задачи
RMSE vs MAE:
├── RMSE ≥ MAE всегда
├── Если RMSE >> MAE → много больших ошибок
└── Если RMSE ≈ MAE → ошибки равномерные
R² интерпретация:
├── R² = 1: идеальная модель
├── R² = 0: модель = среднее
├── R² < 0: модель хуже среднего
└── R² = 0.8: "модель объясняет 80% дисперсии"
Когда что использовать¶
| Ситуация | Метрика | Почему |
|---|---|---|
| Нужна интерпретируемость | MAE, MAPE | Легко объяснить |
| Большие ошибки критичны | MSE, RMSE | Сильный штраф |
| Много выбросов | MAE, MedAE | Устойчивость |
| Сравнение разных таргетов | R², MAPE | Нормализованы |
| y близко к 0 | MAE, RMSE | MAPE взрывается |
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
mse = mean_squared_error(y_test, y_pred)
rmse = mean_squared_error(y_test, y_pred, squared=False)
mae = mean_absolute_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
Ranking Metrics¶
| Метрика | Формула | Когда использовать |
|---|---|---|
| Precision@K | Relevant in top K / K | Топ-K рекомендаций |
| Recall@K | Relevant in top K / Total relevant | Покрытие интересов |
| MAP | Mean of AP across queries | Общее качество поиска |
| MRR | 1/rank первого релевантного | Важен первый результат |
| NDCG | DCG/IDCG | Градуированная релевантность |
NDCG подробнее¶
DCG = Σ (2^relevance - 1) / log2(position + 1)
Пример:
relevance = [3, 2, 3, 0, 1]
positions = [1, 2, 3, 4, 5]
DCG = (2³-1)/log2(2) + (2²-1)/log2(3) + (2³-1)/log2(4) + ...
= 7/1 + 3/1.58 + 7/2 + 0 + 1/2.32
= 7 + 1.89 + 3.5 + 0 + 0.43
= 12.82
IDCG = DCG для идеального порядка
NDCG = DCG / IDCG
Clustering Metrics¶
Без истинных меток¶
| Метрика | Диапазон | Лучше |
|---|---|---|
| Silhouette | [-1, 1] | Больше |
| Calinski-Harabasz | [0, ∞) | Больше |
| Davies-Bouldin | [0, ∞) | Меньше |
С истинными метками¶
| Метрика | Диапазон | Что измеряет |
|---|---|---|
| ARI | [-1, 1] | Согласованность с учётом случайности |
| NMI | [0, 1] | Mutual information, нормализованная |
| Homogeneity | [0, 1] | Кластеры содержат один класс |
| Completeness | [0, 1] | Класс в одном кластере |
from sklearn.metrics import silhouette_score, adjusted_rand_score
# Без меток
silhouette = silhouette_score(X, labels)
# С метками
ari = adjusted_rand_score(y_true, labels)
NLP Metrics¶
| Метрика | Задача | Что измеряет |
|---|---|---|
| Perplexity | Language Model | 2^(cross-entropy) |
| BLEU | Translation | n-gram совпадение с reference |
| ROUGE | Summarization | Recall n-gram |
| BERTScore | Any generation | Семантическое сходство |
| Exact Match | QA | Точное совпадение ответа |
| F1 (token) | QA | Overlap токенов |
Computer Vision Metrics¶
| Метрика | Задача | Формула |
|---|---|---|
| IoU | Detection/Segmentation | Intersection / Union |
| mAP | Detection | Mean AP at IoU thresholds |
| Dice | Segmentation | 2·|A∩B| / (|A|+|B|) |
| SSIM | Image Quality | Structural similarity |
| FID | Generation | Distance in feature space |
Calibration Metrics¶
Модель калибрована, если P(y=1|p=0.8) ≈ 0.8
Метрики:
├── Brier Score = MSE для вероятностей (меньше лучше)
├── ECE = Expected Calibration Error
└── Reliability Diagram = визуальная проверка
from sklearn.calibration import calibration_curve
import matplotlib.pyplot as plt
fraction_of_positives, mean_predicted_value = calibration_curve(
y_test, y_prob, n_bins=10
)
plt.plot(mean_predicted_value, fraction_of_positives, 's-')
plt.plot([0, 1], [0, 1], '--') # Идеальная калибровка
plt.xlabel('Mean Predicted Probability')
plt.ylabel('Fraction of Positives')
Шпаргалка по sklearn¶
from sklearn.metrics import (
# Classification
accuracy_score,
precision_score, recall_score, f1_score,
roc_auc_score, average_precision_score,
confusion_matrix, classification_report,
log_loss,
# Regression
mean_squared_error,
mean_absolute_error,
r2_score,
mean_absolute_percentage_error,
# Clustering
silhouette_score,
adjusted_rand_score,
normalized_mutual_info_score
)
# Classification
print(classification_report(y_test, y_pred))
print(f"ROC-AUC: {roc_auc_score(y_test, y_prob):.4f}")
# Regression
print(f"RMSE: {mean_squared_error(y_test, y_pred, squared=False):.4f}")
print(f"R²: {r2_score(y_test, y_pred):.4f}")
# Clustering
print(f"Silhouette: {silhouette_score(X, labels):.4f}")
Частые ошибки¶
Accuracy Paradox на дисбалансе
На датасете с 95% класса 0 и 5% класса 1, модель "всегда предсказывай 0" получит accuracy = 95%. Это не значит, что модель хорошая. Используй F1, PR-AUC или balanced accuracy вместо accuracy на несбалансированных данных.
ROC-AUC обманчив при сильном дисбалансе
ROC-AUC использует FPR = FP / (FP + TN). При огромном TN (99% негативных) даже большое количество FP дает маленький FPR. Используй PR-AUC — он не зависит от TN.
❌ R² для сравнения разных датасетов
R² зависит от дисперсии таргета
→ Используй RMSE, MAE
❌ MAPE когда y близко к 0
Деление на маленькие числа → взрыв
→ Используй sMAPE или MAE
❌ Оптимизация одной метрики
Можно переоптимизировать в ущерб другим
→ Смотри на несколько метрик
Самопроверка: какую метрику выбрать?
Вы строите модель для банка, которая решает выдавать ли кредит. Одобрение мошенника = потеря $50K. Отказ хорошему клиенту = потеря $500 дохода. Fraud rate = 2%. (1) Какую метрику оптимизировать? (2) Почему accuracy не подойдёт? (3) Как выбрать порог?
Подсказки: (1) FN стоит 100x дороже FP, (2) accuracy при 2% fraud = 98% "always approve", (3) cost-sensitive threshold: порог где expected cost минимален.
Вопросы для собеседования¶
Датасет с 1% fraud. Модель дает accuracy 99%. Хорошая ли модель? Какую метрику выбрать?
«99% accuracy = отличная модель» -- модель может просто предсказывать "не фрод" для всех. accuracy = 99% при 1% fraud rate означает zero recall.
Нет -- модель-константа ("всегда не фрод") даст accuracy 99%. Нужно: (1) Recall -- сколько фродов поймали (при 0% recall модель бесполезна), (2) PR-AUC -- не зависит от TN (которых 99%), (3) F1 или F2 -- если пропуск фрода дороже false alarm. ROC-AUC тоже будет завышен из-за огромного TN в знаменателе FPR. На практике: бизнес задает cost matrix (FN = потеря $50K, FP = звонок клиенту = $5), по ней считается optimal threshold.
В чем разница между ROC-AUC и PR-AUC? Когда каждый лучше?
«ROC-AUC всегда лучше, потому что он стандартный» -- не учитывает дисбаланс.
ROC: ось X = FPR = FP/(FP+TN), ось Y = TPR = TP/(TP+FN). При дисбалансе 1:1000 огромный TN делает FPR почти нулевым даже при большом FP -- кривая выглядит хорошо. PR-AUC: ось X = Recall = TP/(TP+FN), ось Y = Precision = TP/(TP+FP). Precision не использует TN, поэтому дисбаланс не маскирует ошибки. Правило: ROC-AUC при дисбалансе до 1:10, PR-AUC при 1:100+. Для сравнения моделей -- оба, но PR-AUC более информативен на реальных данных с дисбалансом.
NDCG = 0.85. Как интерпретировать? Какие ограничения у этой метрики?
«0.85 из 1.0, значит 85% правильно» -- упрощение, не учитывает нюансы.
NDCG = DCG/IDCG = 85% от идеального ранжирования. DCG = \(\sum_i \frac{2^{rel_i} - 1}{\log_2(i+1)}\) -- позиция и релевантность взвешены логарифмически, ошибки вверху списка штрафуются сильнее. Ограничения: (1) Не различает "плохой топ + хороший хвост" от "хороший топ + плохой хвост" при одинаковом NDCG. Для поиска первый результат критичен -- используй NDCG@K с малым K. (2) Требует градуированную релевантность (0-3), а не бинарную. (3) Не учитывает позицию просмотра пользователем (cascade model). Дополняй MRR (первый релевантный) и MAP (средняя precision).
Источники¶
- sklearn.metrics -- Classification metrics
- sklearn.metrics -- Regression metrics
- Jesse Davis, Mark Goadrich -- "The Relationship Between Precision-Recall and ROC Curves" (ICML 2006) -- обоснование PR-AUC при дисбалансе
- Kalervo Jarvelin, Jaana Kekalainen -- "Cumulated Gain-Based Evaluation of IR Techniques" (ACM TOIS 2002) -- оригинал NDCG
See Also¶
- Classical ML Interview Q&A — вопросы про метрики на собеседованиях
- Model Selection Cheatsheet — какую модель выбрать для задачи
- Hyperparameters Cheatsheet — тюнинг параметров моделей
- System Design Interview Q&A — метрики в production ML
- Loss Functions — математика функций потерь