
精度が上がらない原因の多くは「評価設計の誤り」にあります。評価とは「何点取れたか」ではなく、意思決定に耐えるかを測る営み。この記事では、回帰/分類/ランキングの指標の選び方、交差検証の正しい使い分け、閾値最適化/キャリブレーション、不均衡データ、時系列CV、統計的有意性(ブートストラップ/検定)、そしてビジネスKPIとの接続までを、現場でそのまま使えるコード+チェックリストで解説します。
この記事で身に付く力
- タスク/データ状況に合った指標選定(AUC・PR-AUC・MAEなど)
- 交差検証の設計とリーク対策(StratifiedKFold/TimeSeriesSplit)
- 閾値最適化とコスト最小化(Youden J・Top-k・損失関数)
- 確率の“当たり”を良くするキャリブレーション
- ブートストラップで安定性(CI)の提示
- ビジネスKPIへの橋渡し(Precision@k・Recall@k・コスト)
モデル評価でやりがちな落とし穴
まずは“事故”の典型パターンを押さえておきましょう。ここを外すと、どれだけ学習を回しても実務で使えません。
- 指標ミスマッチ:不均衡なのにAccuracy、コスト非対称なのにF1だけを見る
- 前処理リーク:標準化やエンコードを全体でfit→CVが過大評価
- テスト濫用:ハイパラ探索にテストを使い回して選択バイアス
- ビジネス非接続:AUC↑≠利益↑。意思決定に繋がらない
解決策は「指標の設計 → CV → 閾値/コスト → KPI接続」を型として固定すること。以下で実装と一緒に落とし込みます。
ふみとの現場メモ
役員会で「その差は偶然では?」と聞かれるのは日常。平均値だけでなくCIやTop-kの変化を示せると、会議が一気に前に進みます。
用語メモ(最短で読み解くために)
- Positive/Negative:関心事象(例:離反=1)
- TP/FP/FN/TN:混同行列の4要素
- Precision/Recall/F1:適合率/再現率/調和平均
- ROC-AUC / PR-AUC:しきい値に依存しないランキング力
指標の選び方:タスク別の“最短表”
“何で勝負するか”を最初に決めます。指標はビジネスKPIの代替物。迷ったら下表で決め打ちし、あとで補助指標で補うのが実務的です。
タスク | データ状況 | 推奨主要指標 | 補助指標/補足 |
---|---|---|---|
回帰 | 外れ値あり | MAE | RMSE(外れ値に敏感)、R²(参考) |
二値分類(均衡) | バランス良い | AUC + F1 | Accuracyは補助、閾値最適化で業務接続 |
二値分類(不均衡) | Positive稀少 | PR-AUC + Recall@k/Precision@k | ROCは楽観的になることあり |
多クラス | — | macro-F1 | 混同行列・class-weight |
ランキング/推薦 | Top-k重要 | MAP@k / nDCG@k | HitRate@k、Coverage |
時系列 | 漏洩注意 | MAE/RMSE + TimeSeriesCV | 期間固定の外挿に注意 |
判断基準:ビジネスKPIとの橋渡し(例:Recall@k=架電件数上限に一致、MAE=平均誤差コスト)。
データ分割と交差検証:正しい比較の土台
評価は分割設計から始まります。とくにshuffleと層化、そして時系列で未来を覗かないことは鉄則です。
from sklearn.model_selection import train_test_split, KFold, StratifiedKFold, TimeSeriesSplit
# ホールドアウト(最終報告用のテストは最後に1回)
X\_tr, X\_te, y\_tr, y\_te = train\_test\_split(X, y, test\_size=0.2, stratify=y, random\_state=42)
# KFold(回帰)/ StratifiedKFold(分類)
cv\_cls = StratifiedKFold(n\_splits=5, shuffle=True, random\_state=42)
cv\_reg = KFold(n\_splits=5, shuffle=True, random\_state=42)
# 時系列CV
cv\_ts = TimeSeriesSplit(n\_splits=5)
判断基準
shuffle=True
で分割の偶然差を均す- 分類はStratifiedKFoldで比率維持
- 時系列はTimeSeriesSplitで未来を覗かない
基本評価(分類):混同行列・ROC/PR・レポート
分類は確率出力を基準に評価します。固定閾値0.5に縛られないのがコツ。
import numpy as np, pandas as pd
from sklearn.metrics import (confusion_matrix, classification_report,
roc_auc_score, roc_curve, precision_recall_curve,
average_precision_score)
proba = model.predict\_proba(X\_te)\[:,1]
pred = (proba >= 0.5).astype(int)
cm = confusion\_matrix(y\_te, pred)
print(pd.DataFrame(cm, index=\["TrueNeg","TruePos"], columns=\["PredNeg","PredPos"]))
print(classification\_report(y\_te, pred, digits=3))
print("ROC-AUC=", roc\_auc\_score(y\_te, proba))
fpr, tpr, thr = roc\_curve(y\_te, proba)
prec, rec, thr2 = precision\_recall\_curve(y\_te, proba)
print("PR-AUC=", average\_precision\_score(y\_te, proba))
判断基準
確率出力を使い、ROC/PRと混同行列の両面で評価。
閾値最適化:Youden J・コスト最小・Top-k
実務では「何件まで架電できるか」「FNは極力避けたい」など制約と損失が非対称。閾値は業務前提から決めます。
from sklearn.metrics import roc_curve, confusion_matrix
import numpy as np
proba = model.predict\_proba(X\_te)\[:,1]
fpr, tpr, thr = roc\_curve(y\_te, proba)
# Youden J(tpr - fpr)最大
j = tpr - fpr
thr\_opt = thr\[j.argmax()]
print("Youden best threshold=", float(thr\_opt))
# コスト最適化(例:FPコスト=1, FNコスト=10)
cost\_fp, cost\_fn = 1.0, 10.0
best = (np.inf, 0.5)
for t in np.linspace(0,1,101):
p = (proba >= t).astype(int)
tn, fp, fn, tp = confusion\_matrix(y\_te, p).ravel()
cost = cost\_fp*fp + cost\_fn*fn
best = min(best, (cost, t))
print("Cost-min threshold=", best\[1])
判断基準:損失関数を閾値に反映。架電上限k件のような制約がある場合は、Precision@k/Recall@k
で選定します。
キャリブレーション:確率の“当たり”を良くする
ツリーモデルの確率の歪みは運用で効きます(在庫/リスク)。Brierや信頼度曲線で確認し、必要なら補正。
from sklearn.calibration import CalibratedClassifierCV
from sklearn.metrics import brier_score_loss
# 例:確率が偏りがちなツリーモデルをPlattスケーリング
cal\_model = CalibratedClassifierCV(model, method="sigmoid", cv=5).fit(X\_tr, y\_tr)
proba\_cal = cal\_model.predict\_proba(X\_te)\[:,1]
print("Brier(before)", brier\_score\_loss(y\_te, model.predict\_proba(X\_te)\[:,1]))
print("Brier(after) ", brier\_score\_loss(y\_te, proba\_cal))
不均衡データ:PR-AUCとTop-k評価
資源制約下では「上位何%に当てるか」が要諦。PR-AUCとTop-kで意思決定に直結させましょう。
from sklearn.metrics import average_precision_score
import numpy as np
proba = model.predict\_proba(X\_te)\[:,1]
print("PR-AUC=", average\_precision\_score(y\_te, proba))
# Top-k(例:上位5%を営業対象にする)
k = int(len(proba) \* 0.05)
idx = np.argsort(-proba)\[:k]
recall\_at\_k = y\_te.iloc\[idx].mean() # Positive率
print("Recall\@5%:", float(recall\_at\_k))
回帰評価:MAE/RMSE/R²とビジネス解釈
回帰はコスト直結のMAEを主役に据え、RMSEは外れ値感度、R²は参考扱いで。
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
pred = reg\_model.predict(X\_te)
mae = mean\_absolute\_error(y\_te, pred)
rmse = mean\_squared\_error(y\_te, pred, squared=False)
r2 = r2\_score(y\_te, pred)
print({"MAE": mae, "RMSE": rmse, "R2": r2})
統計的安定性:ブートストラップで信頼区間
「その差は偶然?」を可視化する最短の方法がブートストラップ。平均だけでなく±の幅を示しましょう。
import numpy as np
from sklearn.utils import resample
from sklearn.metrics import roc_auc_score
proba = model.predict\_proba(X\_te)\[:,1]
auc = roc\_auc\_score(y\_te, proba)
aucs = \[]
for \_ in range(500):
idx = resample(np.arange(len(y\_te)), replace=True, random\_state=\_)
aucs.append(roc\_auc\_score(y\_te.iloc\[idx], proba\[idx]))
lo, hi = np.percentile(aucs, \[2.5, 97.5])
print(f"AUC={auc:.3f} (95% CI: {lo:.3f}-{hi:.3f})")
モデル比較の検定(考え方)
- McNemar検定:2分類器の誤分類の非対称性を検定(同一サンプルの0/1予測)
- DeLong検定:AUCの差の有意性を検定
実装はライブラリ依存のため詳細は割愛。重要なのは、差が偶然かを“言葉で”添えることです。
ビジネス接続:KPI・コスト・意思決定
- KPI接続:AUC↑でもトップ案件のPrecisionが下がれば営業効率は悪化。
Precision@k/Recall@k
で検証 - コスト関数:FP/FNの非対称コストを定義し、最小コスト閾値で運用
- 運用設計:再学習頻度と監視指標(データドリフト/閾値劣化)を決める
評価設計から“面接に耐える”ポートフォリオへ
評価が語れるポートフォリオは説得力が段違い。交差検証→閾値→Top-k→CIまで組み込んだ例を、10分LTテンプレで話せるように仕上げましょう。独学で難しい場合はレビュー/質問対応のあるスクール活用も近道です。
・株式会社キカガク:実務再現型の課題設計と出口支援が強い。転職直結に◎
・Tech Academy:質問の速さ×短時間運用で継続しやすい。副業/在宅に◎
TechAcademy データサイエンスコース(受講料:174,600円~ ※更に割引あり)

株式会社キカガク AI人材長期育成コース(受講料:237,600円~)

ミニプロジェクト(提出推奨)
課題:「不均衡データの二値分類で、PR-AUCとRecall@5%を改善し、最小コスト閾値で運用案を提示する」
- CVでベースライン(ロジスティック/ツリーモデル)を比較
- PR-AUC/ROC-AUC、Top-k(5%)を計測
- 閾値最適化(Youden/コスト最小)を実施し、混同行列とコストを報告
- ブートストラップCIで安定性を提示
- READMEにKPI接続(例:架電当たり成約率/人件費)を3行で記載
レビュー観点チェックリスト(コピペ可)
- 指標がタスク/データ状況に合っている
- CVでモデル選択、テスト1回の原則を守っている
- 閾値/コストの設計が明示されている
- CIで安定性を示している
- KPI接続(意思決定への示唆)が書かれている
付録A:ランキング評価(MAP@k / nDCG@k)最短コード
import numpy as np
def apk(actual, pred, k=10):
pred = pred\[:k]
score, hits = 0.0, 0
for i, p in enumerate(pred, start=1):
if p in actual and p not in pred\[:i-1]:
hits += 1
score += hits / i
return score / min(len(actual), k) if actual else 0.0
def mapk(actual\_list, pred\_list, k=10):
return np.mean(\[apk(a, p, k) for a, p in zip(actual\_list, pred\_list)])
付録B:評価レポート1枚の型(テンプレ)
- 目的と指標(PR-AUC / Recall@5% / コスト)
- CV結果(平均±std)
- テスト結果(混同行列/閾値/コスト)
- 安定性(CI)
- 意思決定案(運用・再学習・監視)
FAQ:よくある質問
Q1. Accuracyはいつ使う?
A. 均衡データかつミスのコストが対称なとき。多くの現場では補助的に扱います。
Q2. どの閾値が正解?
A. ユースケース依存。Youdenはクラス重みが同等の仮定。コスト最小/Top-kが実務向き。
Q3. AUCが上がったのに業務が改善しない
A. Top-kや閾値後のPrecision/Recallを確認。営業枠/在庫制約と整合させる。
Q4. テストのCIは必要?
A. 意思決定の確からしさを示すために有用。ブートストラップやDeLongで提示。
この記事から次に読むべきもの(内部リンク)
-
-
【保存版】scikit-learn基礎:回帰・分類・前処理・パイプライン・交差検証を“実務の型”で習得
機械学習で迷子になる最大の理由は、前処理→学習→評価→改善の順番が曖昧なまま個々のアルゴリズムに飛びつくこと。本記事は、未経験〜初学者が週10時間×2〜3週で到達できるscikit-learnの最短ル ...
-
-
【保存版】ハイパーパラメータ入門:Grid/Random/Optunaの実務チューニング完全ガイド
チューニングのゴールは「スコアの数字遊び」ではありません。意思決定に耐える安定した最適化を短時間で作ること。本記事は未経験〜初学者が週10時間×2週間で、GridSearchCV / Randomiz ...
-
-
【実務で差がつく】pandas実践:欠損処理・結合・ウィンドウ関数・時系列・品質保証まで“読みやすく速い”型を習得
リード(結論)基礎を終えたら次は実務の現場で頻出する処理を“型”で覚える段階です。本記事は、pandas 2.x を前提に、欠損・外れ値・結合・ウィンドウ関数・時系列・カテゴリ処理・集計の自動化・大規 ...
-
-
【保存版】可視化入門:Matplotlib/Plotlyの使い分けと“伝わるグラフ設計”10ステップ
結論:可視化は「きれいに描く」ことではなく、意思決定を動かすための設計です。本稿では、未経験〜初学者が 週10時間×1〜2週 で、Matplotlib/Plotlyを軸に “伝わるグラフ”の設計と実装 ...
-
-
【保存版】データ職のポートフォリオ完全ガイド|再現性・評価・LTまで
ポートフォリオって「作ったものの置き場」でしょ? いいえ。採用側が見たいのは「意思決定に効いた証拠」と「再現性」です。 本ガイドは、未経験〜初学者が週10時間×4〜6週で、テーマ選定→要件定義→データ ...
最近のコメント