異常検知の評価は他の タスクと違い、異常率が極めて低い(1% 以下)ため、Accuracy では計れません。
Imbalanced data の罠
「Accuracy 99%!」のウソ
異常率 1% のデータで「全部正常」と予測しても Accuracy 99%。Precision・Recall・F1で見ること、特に PR-AUC が異常検知の標準。
教師あり評価
| 指標 | 意味 | 重視するケース |
|---|---|---|
| Precision | 異常と判定した中で本当に異常な割合 | False positive が高コスト |
| Recall | 本当の異常のうち検出できた割合 | 見逃しが致命的(医療等) |
| F1 | Precision と Recall の調和平均 | バランス型 |
| PR-AUC | PR 曲線の下面積 | 異常検知の標準指標 |
| ROC-AUC | ROC 曲線の下面積 | imbalanced で過大評価ぎみ |
ラベルなし評価
- Silhouette score:クラスタの分離度
- Calinski-Harabasz index:クラスタの分散比
- Davies-Bouldin index:クラスタ内/間距離比
- A/B 運用:本番でチャンピオン vs チャレンジャー比較
Python 実装:教師あり評価指標
Precision / Recall / F1 / PR-AUC の計算
Python
import numpy as npfrom sklearn.metrics import ( precision_score, recall_score, f1_score, average_precision_score, roc_auc_score, precision_recall_curve, classification_report,)
# y_true: 真のラベル(0=正常、1=異常)# y_pred: 予測ラベル# scores: 異常度スコア(連続値)
# 1. Confusion Matrix ベースの指標print(f"Precision: {precision_score(y_true, y_pred):.3f}")print(f"Recall: {recall_score(y_true, y_pred):.3f}")print(f"F1: {f1_score(y_true, y_pred):.3f}")print(classification_report(y_true, y_pred, target_names=["正常", "異常"]))
# 2. AUC 系(連続スコアから)print(f"PR-AUC: {average_precision_score(y_true, scores):.3f}") # 異常検知の標準print(f"ROC-AUC: {roc_auc_score(y_true, scores):.3f}") # imbalanced で過大評価ぎみPR 曲線の可視化
Python
import matplotlib.pyplot as pltimport japanize_matplotlib # noqa: F401
precision, recall, thresholds = precision_recall_curve(y_true, scores)ap = average_precision_score(y_true, scores)
fig, ax = plt.subplots(figsize=(8, 6))ax.plot(recall, precision, label=f"PR-AUC = {ap:.3f}")ax.set_xlabel("Recall")ax.set_ylabel("Precision")ax.set_title("Precision-Recall 曲線")ax.legend(); ax.grid(alpha=0.3)plt.tight_layout(); plt.show()Imbalanced data の罠を体感
Python
import numpy as npfrom sklearn.metrics import accuracy_score
# 異常率 1% のデータを作るnp.random.seed(42)y_true = np.zeros(10000, dtype=int)y_true[:100] = 1 # 100/10000 = 1%
# 「全部正常」と予測y_pred_dummy = np.zeros(10000, dtype=int)
print(f"Accuracy: {accuracy_score(y_true, y_pred_dummy):.3f}") # 0.990 → 一見高得点print(f"Recall: {recall_score(y_true, y_pred_dummy):.3f}") # 0.000 → 異常を全く検出してないprint(f"F1: {f1_score(y_true, y_pred_dummy, zero_division=0):.3f}") # 0.000ラベルなし評価(Silhouette score)
Python
from sklearn.metrics import silhouette_scorefrom sklearn.cluster import KMeans
# 2 クラスタに分けて分離度を測るkmeans = KMeans(n_clusters=2, random_state=42)labels = kmeans.fit_predict(X)score = silhouette_score(X, labels)print(f"Silhouette score: {score:.3f}")# > 0.5: クラスタが明確に分離(=異常検知うまく機能)# 0.0〜0.5: 一部重なり# < 0: 異常クラスタリング失敗ふくふくの進め方
異常検知モデルの評価設計、ベンチマークデータ整備からご支援します。
次回予告
EP.09 はビジネス活用:不正検知・故障予測・。
この記事の感想を教えてください
あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。