ROC(Receiver Operating Characteristic) 곡선은 임계값을 움직이며 두 축을 그린다.
세로축 TPR (재현율) = TP / (TP + FN) ← 양성을 얼마나 잡나가로축 FPR (거짓양성률) = FP / (FP + TN) ← 음성을 얼마나 잘못 경보하나(0,0) 모든 것을 Negative로 분류 (임계값=1)(1,1) 모든 것을 Positive로 분류 (임계값=0)좌상단 (0,1)에 가까울수록 좋은 모델대각선 y=x : 무작위 추측 수준
TPR1.0 ┤ ╭──────────────●(1,1) t=0 │ ╭─╯ ╱ │ ╭─╯ ╱0.8 ┤ ╱ ╱ ← 대각선(y=x): AUC 0.5, 무작위 │ ╱ ╱ │ ╱ ╱ ← 굵은 곡선: 좋은 모델 (좌상단으로 솟음)0.4 ┤╱ ╱ ││ ╱ ││ ╱ 좌상단 (0,1) = 완벽: FP 없이 모든 양성 탐지0.0 ●────╱────────────────→ FPR (0,0) 0.0 1.0 t=1
좋은 모델일수록 곡선이 좌상단 모서리로 솟구친다. FPR을 거의 늘리지 않으면서 TPR을 빠르게 끌어올리는 모양이다.
임계값을 낮추면 곡선 위를 (0,0)에서 (1,1) 방향으로 이동한다. 곡선 위 한 점 = 임계값 하나.
AUC가 임계값과 무관하다는 점이 양날의 검이다. 여러 모델의 변별력을 한 번에 비교할 때는 편리하지만, 실제 운영에서는 결국 임계값 하나를 골라야 한다. 그래서 시험에서 "모델들을 비교"하면 AUC, "운영 임계값을 정하라"면 ROC/PR 곡선 위의 특정 지점을 고르는 문제로 갈린다.
"불균형이면 PR 곡선"이라는 규칙의 근거는 분모에 있다. ROC의 FPR은 분모에 다수 클래스(TN)를 포함해 FP 증가를 희석하지만, PR의 정밀도는 분모가 (TP+FP)뿐이라 소수 클래스에서의 오탐을 가리지 않는다. 분모에 무엇이 들어가는지를 보면 어떤 곡선이 함정에 강한지 판단할 수 있다.
⚠️ 함정: PR AUC는 ROC-AUC와 달리 기준선이 0.5가 아니다. 양성 비율이 1%면 무작위 모델의 AP도 약 0.01이므로, AP 0.30은 "낮다"가 아니라 무작위 대비 30배다. 절대값만 보고 판단하면 안 된다.
import numpy as npfrom sklearn.metrics import precision_recall_curveprecision, recall, thresholds = precision_recall_curve(y_true, y_scores)# 예: 재현율 90% 이상을 의무화하는 규제 환경# → 그 제약을 만족하는 임계값 중 정밀도가 가장 높은 점 선택mask = recall[:-1] >= 0.90best_idx = np.argmax(precision[:-1][mask])chosen_threshold = thresholds[mask][best_idx]
대표적 선택 전략:
상황
임계값 전략
FN 비용 큼 (질병/사기)
재현율 하한 보장 → 임계값 낮춤
FP 비용 큼 (스팸/추천)
정밀도 하한 보장 → 임계값 높임
FP·FN 균형
F1 최대화 지점 선택
비용을 금액으로 환산 가능
기대비용 cost_FP×FP + cost_FN×FN 최소화 지점
기대비용 최소화는 가장 원칙적인 방법이다. FP 한 건과 FN 한 건의 달러 비용을 알면, 곡선 위 각 임계값의 총비용을 계산해 가장 싼 지점을 고른다.
[사기 탐지 / 양성 100건, 음성 9,900건][가정: FP 1건 = 조사비 $10, FN 1건 = 손실 $500]임계값 TP FP FN 비용 = 10×FP + 500×FN 0.2 95 1,200 5 12,000 + 2,500 = $14,500 0.3 88 500 12 5,000 + 6,000 = $11,000 0.5 60 100 40 1,000 + 20,000 = $21,000 0.7 35 20 65 200 + 32,500 = $32,700→ 총비용이 최소인 t = 0.3 을 운영 임계값으로 채택 (F1이 최대인 지점과 일치하지 않을 수 있다 — 비용이 비대칭이므로)
FN 비용이 FP 비용보다 훨씬 크면 최적 임계값은 0.5보다 낮은 쪽으로 밀린다.
반대로 FP가 비싸면(자동 계정 정지, 광고 차단) 최적점은 0.5보다 높아진다.
기본값 0.5는 "FP와 FN 비용이 같다"는 암묵적 가정일 뿐, 어떤 근거도 없는 숫자다.
성능을 평가하고 임계값을 정해야 한다
├─ 목적이 "여러 모델 중 무엇이 나은가"인가?
│ ├─ 예, 클래스 균형 → ROC-AUC
│ └─ 예, 극단 불균형 → PR AUC (Average Precision)
├─ 목적이 "운영 임계값 하나를 고르는 것"인가?
│ ├─ FP/FN 금액을 안다 → 기대비용 최소화 지점
│ ├─ 규제로 재현율 하한 → 제약 만족 중 정밀도 최대 지점
│ ├─ 규제로 정밀도 하한 → 제약 만족 중 재현율 최대 지점
│ └─ 비용 정보 없음 → F1 최대화 지점
└─ 비용 구조가 나중에 바뀔 수 있다
└─ 모델은 AUC로 고정하고 임계값만 재조정 (재학습 불필요)
빌트인 이진 분류기는 validation:auc를 학습 지표로 내보내므로 Automatic Model Tuning의 목적 지표로 쓸 수 있다. 또한 SageMaker 엔드포인트는 확률 점수를 반환하므로, 임계값 컷은 추론 후처리 단계에서 적용한다.
tuner = HyperparameterTuner( estimator=xgb_estimator, objective_metric_name="validation:auc", # 변별력 기준 튜닝 objective_type="Maximize", hyperparameter_ranges=ranges,)# 엔드포인트는 0.0~1.0 점수를 반환 → 후처리에서 비즈니스 임계값 적용score = float(predictor.predict(payload))label = 1 if score >= chosen_threshold else 0
AUC로 좋은 모델을 고른 뒤, 임계값은 별도로 비즈니스 제약에 맞춰 정하는 2단계 분리가 실무 패턴이다.
모델 학습/튜닝과 임계값 결정은 서로 다른 단계의 의사결정이다. AUC를 목적 지표로 튜닝하면 임계값에 휘둘리지 않는 "잘 순위 매기는 모델"을 얻고, 그 모델 위에서 비용 구조에 맞는 임계값을 사후에 고른다. 이 분리 덕분에 비용 구조가 바뀌어도 모델을 다시 학습하지 않고 임계값만 조정할 수 있다.