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

Что будет с ROC-AUC, если умножить все предсказанные вероятности на 2?

2.3 Middle🔥 121 комментариев
#Метрики и оценка моделей#Статистика и A/B тестирование

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

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

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

Отличный вопрос, который проверяет понимание ROC-AUC! Коротко: ROC-AUC останется неизменной. Давайте разберёмся почему и докажем это математически.

Что такое ROC-AUC

ROC-AUC (Area Under the Receiver Operating Characteristic Curve) — это площадь под кривой, которая показывает зависимость True Positive Rate от False Positive Rate при разных пороговых значениях.

Ключевое свойство: ROC-AUC инвариантна к монотонным преобразованиям

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

# Исходные вероятности
y_test = np.array([0, 0, 0, 1, 1, 1])
y_proba = np.array([0.1, 0.2, 0.3, 0.6, 0.7, 0.9])

# Оригинальный ROC-AUC
roc_auc_original = roc_auc_score(y_test, y_proba)
print(f"ROC-AUC (оригинальные вероятности): {roc_auc_original:.4f}")

# Умножим на 2 (будут > 1)
y_proba_scaled = y_proba * 2
roc_auc_scaled = roc_auc_score(y_test, y_proba_scaled)
print(f"ROC-AUC (умноженные на 2): {roc_auc_scaled:.4f}")

# Проверка: они равны
print(f"\nРавны ли ROC-AUC? {np.isclose(roc_auc_original, roc_auc_scaled)}")

# Можно умножить на любое число > 0
for multiplier in [0.5, 1.5, 3, 10, 100]:
    y_proba_mult = y_proba * multiplier
    roc_auc_mult = roc_auc_score(y_test, y_proba_mult)
    print(f"Множитель {multiplier:3}: ROC-AUC = {roc_auc_mult:.4f}")

Почему ROC-AUC не изменяется

ROC-AUC зависит только от ранжирования (ranking) предсказаний, а не от их абсолютных значений.

Визуализация

# Построим ROC-кривые
fpr_orig, tpr_orig, thresholds_orig = roc_curve(y_test, y_proba)
fpr_scaled, tpr_scaled, thresholds_scaled = roc_curve(y_test, y_proba_scaled)

plt.figure(figsize=(10, 6))
plt.plot(fpr_orig, tpr_orig, label=f"Original (AUC={roc_auc_original:.4f})", linewidth=2)
plt.plot(fpr_scaled, tpr_scaled, label=f"Scaled x2 (AUC={roc_auc_scaled:.4f})", 
         linestyle="--", linewidth=2, alpha=0.7)
plt.plot([0, 1], [0, 1], "k--", alpha=0.3, label="Random")
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC Curves: Original vs Scaled x2")
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

# Кривые совпадают!

Математическое доказательство

ROC-AUC как вероятность

ROC-AUC можно интерпретировать как вероятность того, что модель правильно ранжирует случайный пример положительного класса выше случайного примера отрицательного класса:

ROC-AUC = P(score_positive > score_negative)

Если умножить все оценки на константу k > 0:

ROC-AUC' = P(k * score_positive > k * score_negative)
          = P(score_positive > score_negative)  [т.к. k > 0]
          = ROC-AUC

Пороговые значения

РОК-кривая создаётся по разным пороговым значениям:

# Для каждого порога считаем TPR и FPR
for threshold in [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]:
    pred_orig = (y_proba >= threshold).astype(int)
    tp = np.sum((pred_orig == 1) & (y_test == 1))
    fp = np.sum((pred_orig == 1) & (y_test == 0))
    fn = np.sum((pred_orig == 0) & (y_test == 1))
    tn = np.sum((pred_orig == 0) & (y_test == 0))
    
    tpr = tp / (tp + fn) if (tp + fn) > 0 else 0
    fpr = fp / (fp + tn) if (fp + tn) > 0 else 0
    
    # Для масштабированных вероятностей порог тоже масштабируется
    threshold_scaled = threshold * 2
    pred_scaled = (y_proba_scaled >= threshold_scaled).astype(int)
    
    # Предсказания одинаковые!
    print(f"Порог {threshold}: одинаковые предсказания? {np.array_equal(pred_orig, pred_scaled)}")

Что ИЗМЕНИТСЯ при умножении?

1. Пороги (thresholds)

Пороги в ROC-кривой будут другими:

fpr, tpr, thresholds = roc_curve(y_test, y_proba)
print(f"Оригинальные пороги: {thresholds}")
print(f"Минимальный: {thresholds.min():.4f}")
print(f"Максимальный: {thresholds.max():.4f}")

fpr_s, tpr_s, thresholds_s = roc_curve(y_test, y_proba_scaled)
print(f"\nМасштабированные пороги: {thresholds_s}")
print(f"Минимальный: {thresholds_s.min():.4f}")
print(f"Максимальный: {thresholds_s.max():.4f}")

# Пороги масштабируются, но ROC-кривая остаётся той же!

2. Если вероятности > 1

В примере выше y_proba * 2 даст значения > 1, что нарушает интерпретацию как вероятности:

print(f"Масштабированные вероятности: {y_proba_scaled}")
# [0.2, 0.4, 0.6, 1.2, 1.4, 1.8] — это не валидные вероятности!

# Но ROC-AUC всё равно работает корректно

Другие метрики, которые ИЗМЕНЯТСЯ

Вот здесь важно быть осторожным:

Log Loss (cross-entropy) — ИЗМЕНИТСЯ

from sklearn.metrics import log_loss

log_loss_original = log_loss(y_test, y_proba)
log_loss_scaled = log_loss(y_test, np.clip(y_proba_scaled, 1e-10, 1-1e-10))

print(f"Log Loss (оригинал): {log_loss_original:.4f}")
print(f"Log Loss (масштабированный): {log_loss_scaled:.4f}")
print(f"Равны? {np.isclose(log_loss_original, log_loss_scaled)}")
# Нет! Log Loss изменится

Brier Score — ИЗМЕНИТСЯ

from sklearn.metrics import brier_score_loss

brier_original = brier_score_loss(y_test, y_proba)
brier_scaled = brier_score_loss(y_test, np.clip(y_proba_scaled, 0, 1))

print(f"Brier Score (оригинал): {brier_original:.4f}")
print(f"Brier Score (масштабированный): {brier_scaled:.4f}")
print(f"Равны? {np.isclose(brier_original, brier_scaled)}")
# Нет!

Практический пример

# Реальный сценарий: модель выдаёт логиты вместо вероятностей
logits = np.array([-2.0, -1.0, 0.0, 1.0, 2.0, 3.0])
y_test = np.array([0, 0, 0, 1, 1, 1])

# Вероятности
y_proba = 1 / (1 + np.exp(-logits))  # sigmoid

roc_auc_proba = roc_auc_score(y_test, y_proba)
roc_auc_logits = roc_auc_score(y_test, logits)  # Можно передать логиты!

print(f"ROC-AUC (вероятности): {roc_auc_proba:.4f}")
print(f"ROC-AUC (логиты): {roc_auc_logits:.4f}")
print(f"Равны? {np.isclose(roc_auc_proba, roc_auc_logits)}")
# Да! Потому что ROC-AUC зависит только от ранжирования

Ключевые выводы

  1. ROC-AUC инвариантна к положительным монотонным преобразованиям (умножение на любое число > 0, логарифм, возведение в степень)

  2. Это верно для всех метрик на основе ранжирования: Precision@K, MRR, NDCG и т.д.

  3. Другие метрики чувствительны к масштабированию: Log Loss, Brier Score, Calibration error

  4. Пороги изменятся, но ROC-кривая останется той же

  5. Практический совет: если хотите калиброванные вероятности (чтобы 0.7 действительно означало 70% уверенность), умножение разрушит калибровку. Но для ранжирования (выдачи топ-N) это не важно.

Это демонстрирует, почему ROC-AUC часто предпочитается для задач ранжирования, даже если вероятности не совсем валидны.