
クレジットカード詐欺を予測する機械学習モデルが99.9%の精度を報告しました。経営陣は大喜びしましたが、取引の0.1%が不正であり、モデルがすべてのリクエストに対して単にis_fraud = Falseと予測していたことを知って愕然とします。ほぼ完璧な精度を誇りながら、詐欺を0%しか検出していなかったのです。
これが精度パラドックスです。現実世界の機械学習では、データセットが不均衡で、偽陽性(false positive)のコストが偽陰性(false negative)のコストと大きく異なる場合、生の精度は無意味です。
このガイドでは、分類モデルを評価し、ビジネス上の問題に適した指標を選択し、Pythonで決定閾値(decision threshold)を調整するための、本番環境レベルのフレームワークを提供します。
┌─────────────────────────────────────────┐
│ Actual Reality │
│ Positive (1) │ Negative (0) │
┌─────────┬──────────┼───────────────────┼─────────────────────┤
│ Model │ Pos (1) │ True Pos (TP) │ False Pos (FP) │
│ Predict ├──────────┼───────────────────┼─────────────────────┤
│ │ Neg (0) │ False Neg (FN) │ True Neg (TN) │
└─────────┴──────────┴───────────────────┴─────────────────────┘
これら4つの値から、すべての主要な評価指標が導き出されます。
\text{Precision} = \frac{\text{TP}}{\text{TP} + \text{FP}} \quad \text{(モデルが陽性と予測したとき、それが正しい頻度は?)}
\text{Recall (Sensitivity)} = \frac{\text{TP}}{\text{TP} + \text{FN}} \quad \text{(すべての真の陽性のうち、どれだけを検出できたか?)}
\text{Specificity} = \frac{\text{TN}}{\text{TN} + \text{FP}} \quad \text{(すべての真の陰性のうち、どれだけを正しく陰性と判断できたか?)}
すべての分類モデルは、連続的な確率 p \in [0, 1] を出力します。決定閾値(例えば0.5から0.2へ)をシフトすると、Precisionを犠牲にしてRecallが向上します。
Low Threshold (e.g. 0.1) ───────────► High Recall, Low Precision (Catches everything, high noise)
High Threshold (e.g. 0.8) ──────────► High Precision, Low Recall (Only fires on sure things)
F_1 vs F_\beta Score標準的なF_1スコアは、PrecisionとRecallの調和平均であり、両方を均等にバランスさせます。
F_1 = 2 \cdot \frac{\text{Precision} \cdot \text{Recall}}{\text{Precision} + \text{Recall}}
ビジネスでどちらか一方を優先する場合は、F_\betaスコアを使用します。
F_\beta = (1 + \beta^2) \cdot \frac{\text{Precision} \cdot \text{Recall}}{(\beta^2 \cdot \text{Precision}) + \text{Recall}}
\beta = 2.0 (F_2スコア): RecallをPrecisionの2倍重視します(詐欺/医療診断に最適)。\beta = 0.5 (F_{0.5}スコア): PrecisionをRecallの2倍重視します(検索&スパムフィルタリングに最適)。どちらの指標も、すべての可能な決定閾値(0.0から1.0まで)でモデルを評価しますが、不均衡なデータではその振る舞いが大きく異なります。
ROC Curve: Plot of True Positive Rate (Recall) vs False Positive Rate (FPR)
PR Curve: Plot of Precision vs Recall
from sklearn.metrics import roc_auc_score, average_precision_score
# ROC-AUC is misleadingly high on imbalanced datasets!
roc = roc_auc_score(y_true, y_pred_prob) # e.g., 0.985 (looks amazing)
# PR-AUC (Average Precision) reflects real-world rare class performance
pr_auc = average_precision_score(y_true, y_pred_prob) # e.g., 0.620 (reveals true difficulty)
経験則:
多くの最新の分類器(特にXGBoost、LightGBM、ディープニューラルネットワーク)は、キャリブレーションされていない確率を出力します。モデルが100人のユーザーに0.80の確率を割り当てた場合、そのうち正確に80人が陽性であるべきです。もし40人しか陽性でなければ、モデルは過信していることになります。
from sklearn.calibration import CalibratedClassifierCV, calibration_curve
from sklearn.metrics import brier_score_loss
import lightgbm as lgb
# Train raw classifier
base_model = lgb.LGBMClassifier()
base_model.fit(X_train, y_train)
# Calculate Brier Score (lower is better; 0 = perfect calibration)
raw_brier = brier_score_loss(y_test, base_model.predict_proba(X_test)[:, 1])
# Calibrate using Isotonic Regression or Platt Scaling (Sigmoid)
calibrated_model = CalibratedClassifierCV(
estimator=base_model,
method='isotonic', # or 'sigmoid' for smaller datasets (< 1000 samples)
cv='prefit'
)
calibrated_model.fit(X_val, y_val)
calibrated_brier = brier_score_loss(y_test, calibrated_model.predict_proba(X_test)[:, 1])
print(f"Brier score improved from {raw_brier:.4f} to {calibrated_brier:.4f}")
ここでは、不均衡なデータで分類器を訓練し、ビジネスコストに基づいて最適な決定閾値を見つける方法を示します(Cost(FN) = $100、Cost(FP) = $5)。
import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.metrics import precision_recall_curve
# Generate synthetic imbalanced dataset (1% positive class)
X, y = make_classification(
n_samples=50_000, n_features=20, weights=[0.99, 0.01], random_state=42
)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, stratify=y)
# Fit model
clf = HistGradientBoostingClassifier(class_weight='balanced', random_state=42)
clf.fit(X_train, y_train)
# Predict continuous probabilities
y_probs = clf.predict_proba(X_test)[:, 1]
# Calculate Precision-Recall curve
precisions, recalls, thresholds = precision_recall_curve(y_test, y_probs)
# Define Business Cost Function
COST_FALSE_NEGATIVE = 100.0 # Missed fraud cost
COST_FALSE_POSITIVE = 5.0 # User friction cost
total_costs = []
for t in thresholds:
y_pred = (y_probs >= t).astype(int)
fn = np.sum((y_test == 1) & (y_pred == 0))
fp = np.sum((y_test == 0) & (y_pred == 1))
cost = (fn * COST_FALSE_NEGATIVE) + (fp * COST_FALSE_POSITIVE)
total_costs.append(cost)
# Find optimal threshold minimizing business loss
best_idx = np.argmin(total_costs)
optimal_threshold = thresholds[best_idx]
print(f"Default 0.5 Threshold Cost: ${total_costs[np.abs(thresholds - 0.5).argmin()]:,.2f}")
print(f"Optimized Threshold ({optimal_threshold:.3f}) Cost: ${total_costs[best_idx]:,.2f}")
| シナリオ | 主要指標 | 副次指標 |
|---|---|---|
| バランスの取れた二値分類 | ROC-AUC | 精度 / F_1 |
| 詐欺 / 異常検出 (陽性クラス < 2%) | PR-AUC (平均Precision) | F_2スコア & コスト曲線 |
| 検索 & コンテンツ取得 | Precision@K / 平均Precision (MAP) | NDCG@K |
| 確率に敏感なシステム (入札 / リスク) | Brierスコア & 期待キャリブレーション誤差 (ECE) | Log-Loss |
大規模なコードベースにおける重大なセキュリティ脆弱性を特定するための機械学習モデルが開発されています。偽陰性(重大な脆弱性を見逃すこと)は大規模なデータ侵害につながる可能性があり、偽陽性(存在しない脆弱性を誤ってフラグ付けすること)は開発者が無関係な問題の調査に時間を費やすことになります。これらのリスクを考慮すると、モデル開発と閾値調整においてどの評価指標を優先すべきでしょうか?
AI CLIログの整形、Cron式の作成、JWTの解析、chmod権限の計算をオフラインで実行。

PolarsとPandasのメモリ使用量、Apache Arrow統合、遅延クエリ最適化、マルチコアCPUスケーリングに関する包括的なベンチマーク。
続きを読む

Pythonにおける非同期プログラミングの決定版リファレンス。イベントループの内部構造、TaskGroupによる構造化並行処理、フリーストレッド版CPython 3.13、コネクションプーリング、イベントループ枯渇のプロファイリングを網羅する。
続きを読む