1. 요약
이진 분류 모델을 사용하여 결과를 예측할 때, 모델은 주로 확률값을 출력합니다.
이 확률값을 바탕으로 예측을 양성(Positive) 또는 음성(Negative)으로 변환할 때,
임계값(threshold)을 선택해야 합니다.
기본적으로 0.5를 많이 사용하지만, 데이터의 불균형이나 문제의 특성에 따라
0.5는 최적의 임계값이 아닐 수 있습니다.
따라서 임계값을 올바르게 선택하면 모델의 성능을 크게 향상시킬 수 있습니다.
2. 방법론
최적의 임계값을 선택하는 방법은 다양하며,
각각의 방법은 모델이 달성하려는 목표에 따라 다르게 적용됩니다.
1) ROC Curve와 Youden's J Statistic
ROC (Receiver Operating Characteristic) 곡선은
TPR(진양성률, True Positive Rate)과 FPR(위양성률, False Positive Rate) 간의 관계를
다양한 임계값에서 시각화한 것입니다.
Youden's J Statistic은 다음과 같이 정의됩니다:
J=TPR+(1−FPR)−1=TPR−FPR
이 값이 가장 클 때의 임계값이 최적의 임계값으로 간주됩니다.
2) Precision-Recall Curve
Precision과 Recall 간의 트레이드오프를 시각화한 곡선입니다.
불균형한 데이터에서 특히 유용합니다.
Precision-Recall Curve에서 가장 높은 F1 Score를 얻는 임계값이 최적의 값으로 사용될 수 있습니다.
3) F1 Score 최적화
F1 Score는 Precision과 Recall의 조화 평균으로,
양성 예측의 정확성과 회수율 간의 균형을 평가합니다.
F1 Score를 최대화하는 임계값을 찾는 방법입니다.
4) Cost-Sensitive Analysis
이 방법은 양성 예측 오류와 음성 예측 오류 간의 비용을 고려하는 방법입니다.
예를 들어, 양성 예측 오류가 더 중요한 경우(예: 질병 진단에서 false negative가 중요한 경우)
이 방법을 사용할 수 있습니다.이 경우 특정 비용 함수에 따라 임계값을 조정합니다.
5) Grid Search
다양한 임계값을 시도하여 원하는 성능 지표(예: Accuracy, F1 Score)를
최대화하는 임계값을 찾는 방법입니다.
3. 실습 코드
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve, precision_recall_curve, f1_score, classification_report
from sklearn.linear_model import LogisticRegression
# 데이터 로드 및 분할
data = pd.read_csv('data.csv')
X = data.drop('target', axis=1)
y = data['target']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 모델 훈련
model = LogisticRegression()
model.fit(X_train, y_train)
# 예측 확률 값 계산
y_prob = model.predict_proba(X_test)[:, 1]
### 1. ROC Curve와 Youden's J Statistic
fpr, tpr, thresholds_roc = roc_curve(y_test, y_prob)
# Youden's J Statistic 계산
j_scores = tpr - fpr
best_threshold_j = thresholds_roc[np.argmax(j_scores)]
print(f'Best Threshold using Youden\'s J Statistic: {best_threshold_j}')
# Youden's J Statistic에 따른 예측 및 평가
y_pred_j = (y_prob >= best_threshold_j).astype(int)
print("Classification report for Youden's J Statistic threshold:")
print(classification_report(y_test, y_pred_j))
### 2. Precision-Recall Curve에서 최적 F1 Score 찾기
precisions, recalls, thresholds_pr = precision_recall_curve(y_test, y_prob)
# F1 Score 계산
f1_scores = 2 * (precisions * recalls) / (precisions + recalls)
best_threshold_f1 = thresholds_pr[np.argmax(f1_scores)]
print(f'Best Threshold for Maximum F1 Score: {best_threshold_f1}')
# 최적 F1 Score에 따른 예측 및 평가
y_pred_f1 = (y_prob >= best_threshold_f1).astype(int)
print("Classification report for F1 Score optimal threshold:")
print(classification_report(y_test, y_pred_f1))
### 3. 단순 Grid Search를 통한 최적 임계값 찾기
# 다양한 임계값에 대해 F1 Score 계산
thresholds_grid = np.linspace(0.0, 1.0, num=100)
f1_grid_scores = [f1_score(y_test, (y_prob >= t).astype(int)) for t in thresholds_grid]
best_threshold_grid = thresholds_grid[np.argmax(f1_grid_scores)]
print(f'Best Threshold using Grid Search: {best_threshold_grid}')
# Grid Search 임계값에 따른 예측 및 평가
y_pred_grid = (y_prob >= best_threshold_grid).astype(int)
print("Classification report for Grid Search threshold:")
print(classification_report(y_test, y_pred_grid))