この連載 (Vol.1、Vol.2) を通じて、K-Fold CVから Stratified K-Fold CVへとステップアップし、不均衡データという最大の難敵に対する「信ぴょう性の高い検証の土台」を確立しました。
その成果として、信頼できる平均 F1スコア (0.4000) を手に入れています。
この数値を見て、「何を、どう改善すればこの数値は上がるのだろう?」という疑問が生じるのはごく自然な流れでしょう。
残念ながら、F1スコア単体では、AIモデルが「重要な陽性データを見逃したのか(偽陰性)」、それとも「誤報を出しすぎたのか(偽陽性)」というミスの内訳までは教えてくれません。
この内訳が分からなければ、モデルを改善するための具体的な行動指針を立てることは不可能です。
本記事 Vol.3 では、「混同行列(Confusion Matrix)」というツールを導入することで前述の課題を解決し、連載 (「交差検証」三部作) の最終回とします。
第1章:分析の羅針盤「混同行列(Confusion Matrix)」の理解
1.1. 混同行列が必要な理由
前回記事 (Vol.2) では、不均衡データ下での検証の信頼性を確立し、平均 F1スコア \(0.4000\) を得ました。
この数値は「モデルの検出能力は低い」ことを示していますが、具体的に「どう低いのか」を教えてくれません。
F1スコアの一般的な評価基準
F1スコアは \(0\) から \(1\) の値を取り、\(1\) に近いほど理想的(完全な検出能力)であることを示します。
- 1.0000(理想): 完璧なモデル。
- 0.8000 〜 0.9000以上: 実用レベルとして非常に優秀。
- 0.6000 〜 0.7000: 許容範囲、または改善の余地があるレベル。
- 0.4000以下: 一般的な二値分類タスクにおいて、明らかに性能が低いと見なされる水準です。
今回のケースでは、「陽性(異常データ)」を検出したいという目的があり、F1スコア \(0.4000\) は半分にも満たないため、「このままでは実戦で使えず、改善が必要な水準である」と判断します。
では、ここで質問です。
「F1スコアが低い原因は、次の選択肢の内どちらでしょうか?」
- 見逃し(偽陰性)が多い: 危険な陽性サンプルを「安全(陰性)」だと誤って判断している。
- 誤報(偽陽性)が多い: 安全な陰性サンプルを「危険(陽性)」だと誤って判断している。
はい、現時点では上記を判断する基準がありません。
しかし、内訳が分からなければ、モデル調整の方向性が定まりません。
この問題を解決するのが、混同行列(Confusion Matrix)です。
1.2. 混同行列の構造と4つの要素の定義
混同行列は、AIが出した予測(Predict)と、真実の答え(Actual/True)を組み合わせて分類した4つの数値から構成されます。
| 略称 | 英語 | 意味 |
|---|---|---|
| TP | True Positive | 真陽性。正解:陽性を陽性と正しく予測した数 |
| TN | True Negative | 真陰性。正解:陰性を陰性と正しく予測した数 |
| FP | False Positive | 偽陽性。ミス(誤報):陰性を陽性と誤って予測した数 |
| FN | False Negative | 偽陰性。ミス(見逃し):陽性を陰性と誤って予測した数 |
混合行列は、行(通常:真実のクラス)と列(通常:予測したクラス)で構成され、上表の4つの要素でモデルの性能を完全に分解します。
1.3. ビジネスにおけるミスの重み:FP vs. FN
この4要素のうち、モデルの性能を改善する上で重要なのは、ミスである FP(偽陽性)と FN(偽陰性)のバランスです。
- FN(見逃し)が多い場合: モデルが慎重すぎて、本当に重要なサインを見逃している可能性があります。金融不正検出や医療診断など、見逃しが致命的なケースで最も危険なミスです。
- FP(誤報)が多い場合: モデルが過剰に反応しすぎて、頻繁に誤報を出している可能性があります。通知が多すぎてユーザーをうんざりさせてしまう(通知疲れ)、リソースの無駄遣い(誤報検証)につながるなど、コストや信頼性に関わるミスです。
F1スコア \(0.4000\) という値は、この FPとFNのいずれか、または両方が非常に高いことを意味します。
次章では、この4つの要素を用いて、ついに F1スコアの内訳を分析するための指標を導出します。
第2章:再現率と適合率の計算と実装
第1章で学習した混同行列の4つの要素 (TP, FP, FN, TN) は、モデルのミスを分析するための「原材料」です。
この原材料を組み合わせて計算されるのが、F1スコアの構成要素である再現率(Recall)と適合率(Precision)です。
これらの指標は、モデルの性能を「見逃しの少なさ」と「誤報の少なさ」という二つの軸で捉えることを可能にします。
2.1. 再現率(Recall):見逃しの少なさ
再現率は、真実が陽性である全データ(実際に危険なもの全て)のうち、モデルがどれだけ正しく陽性と検出できたかを示す割合です。
別名として真陽性率(True Positive Rate: TPR)、または感度(Sensitivity)とも呼ばれます。
再現率は、真陽性(TP)を、真の陽性データ(TP + FN)の合計で割ることで求められます。
\(\displaystyle \text{再現率 (Recall)} = \frac{\text{TP}}{\text{TP} + \text{FN}}\)
- FN(偽陰性/見逃し)が少なければ少ないほど、再現率は1に近づき、モデルの見逃しが少ないことを意味します。
2.2. 適合率(Precision):誤報の少なさ
適合率は、モデルが陽性だと予測した全データのうち、実際にどれだけ正しく陽性であったかを示す割合です。
別名として陽性適中率(Positive Predictive Value: PPV)とも呼ばれます。
適合率は、真陽性(TP)を、モデルが予測した陽性データ(TP + FP)の合計で割ることで求められます。
\(\displaystyle \text{適合率 (Precision)} = \frac{\text{TP}}{\text{TP} + \text{FP}}\)
- FP(偽陽性/誤報)が少なければ少ないほど、適合率は1に近づき、モデルの誤報が少ないことを意味します。
2.3. 再現率と適合率のトレードオフ
再現率と適合率は、多くの場合、トレードオフ(二律背反)の関係にあります。
- 再現率を上げる(見逃しを減らす)ために、モデルを「少しでも怪しかったら陽性」と判断させると、その分誤報(FP)が増え、適合率が下がります。
- 適合率を上げる(誤報を減らす)ために、モデルを「確実な証拠がなければ陰性」と判断させると、真の陽性まで見逃してしまい(FNが増え)、再現率が下がります。
F1スコアは、この再現率と適合率の調和平均であり、このトレードオフをバランス良く評価するための単一の指標なのです。
2.4. 実装:交差検証への指標の組み込みとデータの準備
ここでは、Vol.2で定義したデータ生成、モデル設定、Stratified K-Fold CVのプログラムに、新たな指標(再現率と適合率)を組み込みます。
新規ファイル ai_cv_pr.py を作成し、次のプログラムをコーディングしてください。
# --- 1. 必要なライブラリのインポート ---
import numpy as np
# cross_val_scoreからcross_validateに、KFoldからStratifiedKFoldに変更
from sklearn.model_selection import cross_validate, StratifiedKFold
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification
# 1. 不均衡データセットの作成 (Vol.2のパラメーターを保持)
X, y = make_classification(
n_samples=1000,
n_features=20,
n_informative=10,
n_redundant=10,
n_classes=2,
n_clusters_per_class=1,
weights=[0.99, 0.01], # クラスの比率: 陰性(99%), 陽性(1%)
flip_y=0,
random_state=42
)
# 2. モデルの準備
model = RandomForestClassifier(random_state=42)
# 3. Stratified K-Fold分割器を準備(Vol.2の最終的な設定を保持)
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# 4. F1、再現率、適合率を計測するように cross_validate を実行
scoring = ['f1', 'recall', 'precision']
results = cross_validate(
model,
X,
y,
cv=skf,
scoring=scoring,
return_train_score=False
)
# --- 5. 結果表示関数の定義と実行 ---
def print_cv_results(results):
"""交差検証の結果を平均値と標準偏差で整形して表示する"""
print("--- 交差検証 (Stratified K-Fold) 結果 ---")
for key in results.keys():
if key.startswith('test_'):
metric_name = key[5:].upper()
mean = np.mean(results[key])
std = np.std(results[key])
print(f"- 平均 {metric_name} スコア: {mean:.4f} (±{std:.4f})")
print_cv_results(results)2.5. 実行結果の中間分析
--- 交差検証 (Stratified K-Fold) 結果 ---
- 平均 F1 スコア: 0.4000 (±0.3266)
- 平均 RECALL スコア: 0.3000 (±0.2449)
- 平均 PRECISION スコア: 0.6000 (±0.4899)実行結果を分析することで、F1スコア 約 \(0.4000\) の謎が解明されます。
- 再現率 (Recall)の平均スコア:\(0.3000\)
- 真の陽性データ(見つけるべきもの)の7割を見逃している(FNが多い)。モデルは検出に慎重すぎる。
- 適合率 (Precision)の平均スコア:\(0.60000\)
- 陽性と予測したうち、4割は誤報(FPが多い)である。モデルは誤報もそこそこ出している。
この結果から、モデルは再現率(\(0.3000\))が極端に低く、これが F1スコアを押し下げている主要因であることが特定されました。モデルは「見逃し(偽陰性)」を最も多く犯している状態です。
したがって、第3章で取り組むべき具体的な改善目標は、この再現率 \(0.3000\) を向上させることに定まります。
第3章:最終課題:モデル改善への応用
3.1. 分析結果が導く改善目標
第2章の実装と分析により、Vol.2から追ってきた F1スコア \(0.4000\)(今回の実行結果では \(0.3973\))の具体的な内訳が判明し、再現率(Recall)の平均スコア \(0.3000\) が性能を押し下げている真犯人であることが特定されました。
したがって、改善の最優先事項は「機能していない部分」である再現率の底上げに絞られます。
\(\text{目標:見逃し(FN)を減らし、再現率 } 0.3000 \text{ を向上させる}\)
3.2. モデル改善のための羅針盤:閾値調整の導入
分類モデルは、内部で予測確率(確信度)を計算しており、デフォルトではこの確率が \(0.5\) 以上であれば「陽性」と判断します。
この判断基準が「閾値(Threshold)」です。
現在の低すぎる再現率を改善するためには、モデルの判断基準を甘くする必要があります。
- 閾値を下げる(例: \(0.5 \to 0.3\)) \(\to\) 陽性判定の範囲を広げる。
- 結果 \(\to\) FN(見逃し)が減り、TP(真陽性)が増える \(\to\) 再現率 (Recall) 向上。
- 代償 \(\to\) FP(誤報)が増える \(\to\) 適合率 (Precision) 低下。
閾値の操作はモデルをビジネスに適合させるための強力な手法ですが、再現率と適合率のトレードオフを意図的に制御する行為であり、この手法ひとつですべての問題を解決するわけではないことには注意が必要です。
3.3. 実践:閾値調整によるモデル改善のデモ
ここでは、課題である再現率 \(0.3000\) の向上を目標に、モデルの閾値を \(0.5\) から \(0.3\) へと引き下げ、その効果を数値で確認するデモを実行します。
ただし、「データが変われば結果が変わるのは当然」です。
過学習を防ぎ、閾値調整の効果を公正に測定するため、実行済のプログラムとは完全に独立したデータ分割が必要です。
よって、既にCV評価を行った ai_cv_pr.py とは別に、同じデータ生成ロジックとモデルを使用し、シンプルな訓練・テスト分割で閾値調整のデモを行う独立したファイル ai_threshold_demo.py を作成します。
新規ファイル を作成し、次のプログラムをコーディングしてください。ai_threshold_demo.py
# ai_threshold_demo.py
# --- 1. 必要なライブラリのインポート ---
import numpy as np
# CV評価に使われた StratifiedKFold を使用
from sklearn.model_selection import StratifiedKFold
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification
from sklearn.metrics import confusion_matrix, recall_score, precision_score, f1_score
# --- 2. 不均衡データセットの作成 (ai_cv_pr.py と全く同じ) ---
X, y = make_classification(
n_samples=1000,
n_features=20,
n_informative=10,
n_redundant=10,
n_classes=2,
n_clusters_per_class=1,
weights=[0.99, 0.01], # クラスの比率: 陰性(99%), 陽性(1%)
flip_y=0,
random_state=42 # 同じ乱数シードで、ai_cv_pr.py と全く同じデータが生成される
)
# --- 3. モデルの準備 ---
model = RandomForestClassifier(random_state=42)
# --- 4. CVで使われた最後のFoldをテストデータとして採用 ---
# ai_cv_pr.py と同じ設定で StratifiedKFold を初期化
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
# 最終のFoldの分割インデックスを取得し、テストセットとして利用
for train_index, test_index in skf.split(X, y):
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
# --- 5. モデルの学習と予測確率の取得 ---
model.fit(X_train, y_train)
y_proba = model.predict_proba(X_test)[:, 1]
# --- 6. 閾値評価関数の定義 ---
def evaluate_threshold(y_true, y_proba, threshold):
"""閾値に基づいて予測を行い、評価指標と混同行列を表示する"""
y_pred = (y_proba >= threshold).astype(int)
cm = confusion_matrix(y_true, y_pred)
recall = recall_score(y_true, y_pred)
precision = precision_score(y_true, y_pred, zero_division=0)
f1 = f1_score(y_true, y_pred)
print(f"\n--- 閾値 {threshold} の性能 ---")
print(f"混同行列:\n{cm}")
print(f"再現率 (Recall): {np.round(recall, 4)}")
print(f"適合率 (Precision): {np.round(precision, 4)}")
print(f"F1 Score: {np.round(f1, 4)}")
# --- 7. 関数の呼び出しによる閾値の比較実行 ---
# ai_cv_pr.py の結果(再現率 0.3000)と比較するため、閾値 0.3 のみ評価する
evaluate_threshold(y_test, y_proba, 0.3)3.4. 実行結果の分析
実行結果を第2章のクロスバリデーション(CV)の結果と比較し、閾値調整の成果の確認と最終的な分析を行います。
--- 閾値 0.3 の性能 ---
混同行列:
[[197 1]
[ 1 1]]
再現率 (Recall): 0.5
適合率 (Precision): 0.5
F1 Score: 0.53.4.1. 混合行列
混合行列は、次の順に表示されます。(実行結果と照合)
\(\begin{pmatrix} TN=197 & FP=1 \\ FN=1 & TP=1 \end{pmatrix}\)
それぞれの数値の意味を再確認できるよう、1.2. の表を再掲しておきます。
| 略称 | 英語 | 意味 |
|---|---|---|
| TP | True Positive | 真陽性。正解:陽性を陽性と正しく予測した数 |
| TN | True Negative | 真陰性。正解:陰性を陰性と正しく予測した数 |
| FP | False Positive | 偽陽性。ミス(誤報):陰性を陽性と誤って予測した数 |
| FN | False Negative | 偽陰性。ミス(見逃し):陽性を陰性と誤って予測した数 |
3.4.2. 再現率 (Recall)
閾値を \(0.5\) から \(0.3\) に下げたことにより、モデルの見逃し(FN)が減り、再現率は \(0.3000\) から \(0.5000\) へと明確に向上しました。
3.4.3. 適合率 (Precision)
再現率向上の代償として、適合率は \(0.6000\) から \(0.5000\) へと低下しました。
これは、閾値を下げたことで、陽性と誤って判断するケース(FP)が増加したことを意味します。
これは、閾値調整におけるトレードオフの法則が、実データを用いて検証できたことにもなります。
3.4.4. 総合性能 F1スコア
F1スコアは \(0.4000\) から \(0.5000\) へと改善し、閾値調整がモデルの総合性能を向上させる有効な手段であることが示されました。
第4章:連載の総括と次のステップ
4.1. 本連載で確立した堅牢なAI評価基盤
本連載(Vol.1~Vol.3)の主眼は、目先の高いスコアを出すことではなく、不確実なデータ環境下で、AIモデルの性能を公正かつ安定的に評価し、改善する土台を確立することにありました。
中でも今回の記事を通じて、実践で使える交差検証技術の3つの重要な柱を構築しました。
- 公正な評価基盤の確立(第2章)
- 課題の特定: データセットの極端な不均衡性に対しCVの平均を用いることで、モデルの真の能力(F1スコア \(0.4000\))を安定して推定しました。
- 厳密性の保証:
StratifiedKFoldを採用することで、不均衡な陽性データが評価のたびに均等に分けられるようにし、評価の統計的な信頼性を担保しました。
- 課題の構造的な理解と改善の計画(第3章)
- 根本原因の分析: CVの結果から、性能のボトルネックが再現率の低さ(見逃しが多いこと)にあると構造的に理解しました。
- 改善の計画: この課題に対し、最も基本的かつ効果的な手法である閾値調整を改善のアクションとして決定しました。
- 改善アクションの厳密な検証(第3章)
- 検証の厳密性: デモコード
ai_threshold_demo.pyを用いることで、第2章で評価したモデルと全く同じ構成を維持したまま、公正なテストデータを用いて改善アクション(閾値 \(0.3\))の効果を検証しました。 - 基本原則の体感: 再現率が \(0.3\) から \(0.5\) へ向上したことと引き換えに、適合率が \(0.6\) から \(0.5\) へ低下するという、トレードオフの基本原則を数値で確認しました。
- 検証の厳密性: デモコード
4.2. 次のステップ:さらなる性能向上を目指して
本連載で確立した評価基盤は、今後行う改善行動の結果に対する統計的な信頼性を大きく高めます。
これにより、試行錯誤のプロセスを効率化し、プロジェクトの成功確率を向上させます。
しかし、閾値調整だけですべてが上手くいくほど、AI開発の世界は甘いものではありません。
この先に、まだまだ未知の手法や評価指標が控えています。
- データの再設計: 特徴量エンジニアリング、データの収集。
- より複雑なモデルの採用: ランダムフォレストの上位互換であるXGBoostやLightGBM。
- 不均衡データ処理の専門技術: SMOTEなどのオーバーサンプリング技術の適用。
ただ、今回の閾値調整が性能改善の重要な土台知識であることは間違いありません。
上記の高度な施策を実行する際も、第2章で確立したCV評価基盤に戻り、性能が真に向上したかどうかを公正に検証することが、AIプロジェクトを成功に導く鍵となることを忘れてはなりません。


コメント