← Назад к вопросам

Какую метрику выбрать для оценки работы бинарного классификатора с распределением 95% положительных и 5% отрицательных классов?

2.0 Middle🔥 131 комментариев
#Метрики и оценка моделей

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Метрики для дисбалансированной классификации (95%-5%)

Это классическая проблема при работе с редкими событиями: fraud detection, редкие заболевания, техническая неисправность оборудования. Accuracy здесь абсолютно неподходящая метрика, и вот почему.

Почему Accuracy неправильна

from sklearn.metrics import accuracy_score

# Наивный классификатор: всегда предсказывает 1 (положительный класс)
y_true = [1]*95 + [0]*5  # 95% положительных, 5% отрицательных
y_pred = [1]*100  # Всегда 1

accuracy = accuracy_score(y_true, y_pred)  # 95%!
print(f'Accuracy наивного классификатора: {accuracy:.1%}')

# Но на самом деле модель бесполезна!

Accuracy оценивает долю правильных предсказаний, но не показывает, что модель совсем не определяет редкий класс. При дисбалансе это критично.

Лучшие метрики для дисбалансированных данных

1. AUC-ROC (Receiver Operating Characteristic) — ОСНОВНАЯ метрика

Это мой первый выбор для дисбалансированных данных. AUC показывает, насколько хорошо модель разделяет классы, независимо от порога решения.

from sklearn.metrics import roc_auc_score, roc_curve
import matplotlib.pyplot as plt

y_proba = model.predict_proba(X_test)[:, 1]  # Вероятности положительного класса
auc = roc_auc_score(y_true, y_proba)
print(f'AUC-ROC: {auc:.4f}')  # 0.5 = случайный классификатор, 1.0 = идеальный

# ROC кривая показывает trade-off между TPR и FPR
fpr, tpr, thresholds = roc_curve(y_true, y_proba)
plt.plot(fpr, tpr, label=f'AUC = {auc:.3f}')
plt.plot([0, 1], [0, 1], 'k--', label='Random')
plt.xlabel('False Positive Rate (1 - Specificity)')
plt.ylabel('True Positive Rate (Recall)')
plt.legend()
plt.show()

Плюсы:

  • Не зависит от порога решения
  • Инвариантна к дисбалансу классов
  • Интерпретируемая: вероятность того, что модель правильно рангирует случайно выбранный положительный пример выше, чем отрицательный

Минусы:

  • Не показывает абсолютные значения ошибок
  • Менее интуитивна, чем точность

2. Precision-Recall кривая и F1-score — когда фокус на редком классе

Если мы особенно заботимся о положительном (редком) классе:

from sklearn.metrics import precision_recall_curve, f1_score

# Precision: из предсказанных положительных, сколько правильных
# Recall (Sensitivity): из всех положительных, сколько мы поймали

precision, recall, thresholds = precision_recall_curve(y_true, y_proba)

plt.plot(recall, precision, label='PR curve')
plt.xlabel('Recall (Sensitivity)')
plt.ylabel('Precision')
plt.legend()
plt.show()

# F1-score: гармоническое среднее Precision и Recall
optimal_threshold = 0.3  # Выбираем порог максимизирующий F1
y_pred = (y_proba >= optimal_threshold).astype(int)
f1 = f1_score(y_true, y_pred)
print(f'F1-score: {f1:.4f}')

Когда использовать:

  • Fraud detection: неправильный положительный (False Positive) дорогой — берём высокую Precision
  • Medical diagnosis: пропустить больного (False Negative) опасно — берём высокий Recall
  • Обычно ищем баланс через F1-score

3. PR-AUC (Area Under Precision-Recall Curve) — для экстремального дисбаланса

Если дисбаланс очень сильный (как в вашем случае 95%-5%), PR-AUC предпочтительнее ROC-AUC.

from sklearn.metrics import auc

pr_auc = auc(recall, precision)
print(f'PR-AUC: {pr_auc:.4f}')

# PR-AUC более чувствительна к производительности на меньшинстве

4. Confusion Matrix и производные метрики

Для полного понимания::

from sklearn.metrics import confusion_matrix, classification_report

y_pred = (y_proba >= 0.5).astype(int)
cm = confusion_matrix(y_true, y_pred)

print(cm)
# [[TN, FP],
#  [FN, TP]]

TN, FP, FN, TP = cm.ravel()

print(f'True Positive Rate (Recall, Sensitivity): {TP / (TP + FN):.4f}')
print(f'False Positive Rate: {FP / (FP + TN):.4f}')
print(f'Precision (PPV): {TP / (TP + FP):.4f}')
print(f'Specificity: {TN / (TN + FP):.4f}')

print(classification_report(y_true, y_pred))

5. Matthews Correlation Coefficient (MCC) — игнорирует дисбаланс

Одна из лучших метрик для несбалансированных данных:

from sklearn.metrics import matthews_corrcoef

mcc = matthews_corrcoef(y_true, y_pred)
print(f'MCC: {mcc:.4f}')  # От -1 до 1, 0 = случайный

# MCC = (TP*TN - FP*FN) / sqrt((TP+FP)(TP+FN)(TN+FP)(TN+FN))

MCC учитывает все четыре компонента матрицы ошибок и не зависит от дисбаланса. Очень хорошая метрика!

6. Cohen's Kappa — согласованность с учётом случайности

from sklearn.metrics import cohen_kappa_score

kappa = cohen_kappa_score(y_true, y_pred)
print(f'Cohen Kappa: {kappa:.4f}')

Практический пример для вашего случая (95%-5%)

import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import (
    roc_auc_score, pr_auc, matthews_corrcoef, f1_score, 
    precision_score, recall_score, confusion_matrix
)

# Создаём дисбалансированный датасет
X, y = make_classification(
    n_samples=10000,
    n_features=20,
    n_informative=10,
    weights=[0.95, 0.05],  # 95%-5% дисбаланс
    random_state=42
)

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42, stratify=y
)

# Обучаем модель с балансировкой
model = RandomForestClassifier(
    n_estimators=100,
    class_weight='balanced',  # ВАЖНО для дисбаланса!
    random_state=42
)
model.fit(X_train, y_train)

y_proba = model.predict_proba(X_test)[:, 1]
y_pred = model.predict(X_test)

# МЕТРИКИ ДЛЯ ОТЧЁТА
print('=' * 60)
print('МЕТРИКИ ДИСБАЛАНСИРОВАННОЙ КЛАССИФИКАЦИИ')
print('=' * 60)

print(f'\n1. AUC-ROC: {roc_auc_score(y_test, y_proba):.4f}')  # ОСНОВНАЯ
print(f'2. PR-AUC: {auc(recall, precision):.4f}')  # ДЛЯ ЭКСТРЕМАЛЬНОГО ДИСБАЛАНСА
print(f'3. F1-score: {f1_score(y_test, y_pred):.4f}')  # БАЛАНС PRECISION-RECALL
print(f'4. MCC (Matthews): {matthews_corrcoef(y_test, y_pred):.4f}')  # УНИВЕРСАЛЬНАЯ')
print(f'5. Precision: {precision_score(y_test, y_pred):.4f}')  # ИЗ ПРЕДСК. ПОЛОЖИТ. СКОЛЬКО ПРАВИЛЬНО')
print(f'6. Recall: {recall_score(y_test, y_pred):.4f}')  # ИЗ ИСТИННО ПОЛОЖИТ. СКОЛЬКО ПОЙМАЛИ')

print(f'\nMatrix ошибок:')
print(confusion_matrix(y_test, y_pred))

print(f'\nAccuracy (НЕ ИСПОЛЬЗУЙ!): {(y_pred == y_test).mean():.4f}')

Рекомендация для вашей задачи

При распределении 95%-5% я бы использовал:

  1. AUC-ROC ≥ 0.85 — основная метрика (базовая, хорошая)
  2. PR-AUC ≥ 0.60 — вторичная (более чувствительна к редкому классу)
  3. F1-score ≥ 0.60 — для практического применения (выбирайте порог, максимизирующий F1)
  4. MCC ≥ 0.50 — как дополнительная проверка

Чего НЕ делать:

  • Accuracy — совершенно неинформативна при таком дисбалансе
  • Полагаться только на одну метрику
  • Забывать про порог решения (по умолчанию 0.5 редко оптимален)

Оптимизация порога решения

from sklearn.metrics import f1_score

# Находим оптимальный порог
thresholds = np.arange(0.1, 0.9, 0.01)
f1_scores = []

for threshold in thresholds:
    y_pred_thresh = (y_proba >= threshold).astype(int)
    f1 = f1_score(y_test, y_pred_thresh)
    f1_scores.append(f1)

optimal_threshold = thresholds[np.argmax(f1_scores)]
print(f'Оптимальный порог: {optimal_threshold:.2f}')

y_pred_optimal = (y_proba >= optimal_threshold).astype(int)
print(f'F1-score с оптимальным порогом: {f1_score(y_test, y_pred_optimal):.4f}')

Итоговый совет

Для задачи с дисбалансом 95%-5% думайте так: модель оценивается тем, насколько хорошо она различает редкий класс, а не тем, что она говорит "положительный" в 95% случаев. AUC-ROC + PR-AUC + F1-score — магическая тройка для дисбалансированных задач.

Какую метрику выбрать для оценки работы бинарного классификатора с распределением 95% положительных и 5% отрицательных классов? | PrepBro