🔧

【AWS-ML】クラス不均衡とは:機械学習における不均衡データ対策入門

に公開

この記事で学べること

  • クラス不均衡とは何か、なぜ難しいのか
  • データレベルとモデルレベルの具体的対処法(SMOTE/アンダー・オーバーサンプリング/クラス重み/閾値最適化)
  • Accuracy に頼らない評価設計(Precision/Recall/F1/ROC-AUC/PR-AUC)
  • マルチクラス分類での集計方法(micro/macro/weighted)
  • scikit-learn + imbalanced-learn による再現可能な実装パターン

はじめに:クラス不均衡とは?

「クラス不均衡(class imbalance)」とは、分類問題においてクラスの出現頻度に大きな偏りがある状態のことです。
例)不正検知で「不正=1」が1%しかない、障害検知で「障害=1」が0.5%しかない等。


主な対処方法

  • データレベルの対処
    • オーバーサンプリング(少数クラスを増やす)
    • アンダーサンプリング(多数クラスを減らす)
    • 合成サンプリング(SMOTE など)
  • モデルレベルの対処
    • クラス重み(class_weight)
    • 閾値(しきい値)最適化(コスト最小化/F1 最大化 等)
  • 評価設計
    • Confusion Matrix に基づく Precision/Recall/F1
    • ROC-AUC と PR-AUC(陽性が希少なときは特に PR-AUC を重視)

データレベルの対処

オーバーサンプリング

  • 少数クラスのサンプルを複製して増やす。実装容易だが過学習を招きやすい。

アンダーサンプリング

  • 多数クラスを間引く。学習は安定しやすいが情報を捨てることになる。

SMOTE(Synthetic Minority Over-sampling Technique)

  • 少数クラス近傍のサンプル間を内挿して“合成データ”を生成。
  • 単純複製より汎化しやすい一方、ノイズの増幅やクラス境界のにじみには注意。
タイプ別の使い分け(目安)
  • 少数クラスが極端(<1%)かつデータ量が潤沢でない: アンダー+SMOTE の併用を検討
  • 特徴量が高次元・疎: 単純オーバーサンプリングは過学習しやすい。SMOTE かクラス重みを優先
  • 学習コストを最小化したい: ライトに「クラス重み」から試す

モデルレベルの対処

クラス重み(class_weight)

  • 少数クラスの誤分類をより重く評価する設定。class_weight='balanced' は頻度の逆数で自動調整。
  • ツリー系・線形モデル・一部の勾配ブースティング等で広くサポート。

閾値(しきい値)最適化

  • 多くの分類器は確率を出力。デフォルトの 0.5 をそのまま使わず、評価軸に合わせて最適化。
    • 例)F1 最大化、Recall 下限つきで Precision 最大化、期待コスト最小化 など。

評価指標の使い分け

  • Precision(適合率): 予測1のうち正解の割合
  • Recall(再現率): 実際の1をどれだけ拾えたか
  • F1 スコア: Precision と Recall の調和平均(バランスをとりたいとき)
  • ROC-AUC: 全閾値にわたる TPR と FPR の関係(陽性が希少でも一応安定)
  • PR-AUC: Precision-Recall 曲線の面積(陽性が希少なら ROC よりこちらが有用)

マルチクラス分類の集計(micro/macro/weighted)

  • micro: 全サンプルを一括でカウント(頻度の高いクラスの影響が大きい)
  • macro: クラスごとに算出して単純平均(クラス数に均等重み)
  • weighted: クラスごとに算出してサポート数で重みづけ平均(頻度差を反映)

実装例(scikit-learn + imbalanced-learn)

目的
  • Stratified で学習/評価を分離(リーク防止)
  • SMOTE を学習パイプラインに組み込む
  • F1 と PR-AUC を主指標に
  • 閾値を F1 最大化で最適化
# pip install scikit-learn imbalanced-learn
from imblearn.pipeline import Pipeline
from imblearn.over_sampling import SMOTE
from sklearn.model_selection import StratifiedKFold, cross_val_predict, train_test_split
from sklearn.metrics import (classification_report, f1_score, precision_recall_curve,
                             average_precision_score, roc_auc_score)
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
import numpy as np

# X: 特徴量 (ndarray / DataFrame), y: ラベル (0/1)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=42
)

pipe = Pipeline(steps=[
    ("scaler", StandardScaler(with_mean=False) if hasattr(X_train, "sparse") else StandardScaler()),
    ("smote", SMOTE(random_state=42)),
    ("clf", LogisticRegression(max_iter=1000, class_weight="balanced"))
])

# 予測確率を Stratified CV で推定(学習時のみ SMOTE を適用)
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
proba_cv = cross_val_predict(pipe, X_train, y_train, cv=cv, method="predict_proba")[:, 1]

# 閾値最適化(F1 最大)
prec, rec, thr = precision_recall_curve(y_train, proba_cv)
f1 = 2 * (prec * rec) / (prec + rec + 1e-12)
best_idx = np.nanargmax(f1)
best_thr = thr[best_idx] if best_idx < len(thr) else 0.5
print(f"Best F1 on CV={f1[best_idx]:.3f} at threshold={best_thr:.3f}")

# 最終学習→テスト評価
pipe.fit(X_train, y_train)
proba_test = pipe.predict_proba(X_test)[:, 1]
y_pred_test = (proba_test >= best_thr).astype(int)

print(classification_report(y_test, y_pred_test, digits=3))
print("F1:", f1_score(y_test, y_pred_test))
print("ROC-AUC:", roc_auc_score(y_test, proba_test))
print("PR-AUC (AP):", average_precision_score(y_test, proba_test))

ありがちな落とし穴

  • Accuracy に高得点でも、正例をほとんど拾えていない
  • CV で SMOTE を事前にかけてしまい、リークしている
  • マルチクラスで平均方法(micro/macro/weighted)を明示していない
  • ビジネス上のミスコスト(FP vs FN)を考えずに閾値 0.5 を固定

試験対策チェックリスト(AWS 文脈)

  • 評価指標の選択理由を説明できる(F1/PR-AUC 重視の根拠)
  • 対処法(SMOTE/アンダー・オーバー/クラス重み/閾値最適化)の長短を言語化できる
  • Stratified 分割や Pipeline によるリーク防止の理解
  • マルチクラスでの micro/macro/weighted の違いを説明できる
  • (補足)XGBoost など一部モデルでは「クラス重み・不均衡対応パラメータ(例:scale_pos_weight)」がある

ChatGPT とのやりとり(要約)

  • Q: クラス不均衡とは?なぜ Accuracy が危険?
    A: 少数クラスが希少だと「全部0」でも Accuracy が高くなる。Precision/Recall/F1/PR-AUC を確認すべき。

  • Q: どんな対処法がある?
    A: データ側(オーバー/アンダー/SMOTE)+モデル側(クラス重み、閾値最適化)。リーク防止のため学習系処理は CV 内で。

  • Q: どう評価する?
    A: Confusion Matrix を基礎に、F1 と PR-AUC を主柱に。ROC-AUC も併記。多クラスでは weighted を併用。

  • Q: 実装の定石は?
    A: Pipeline に SMOTE を組み込み、StratifiedKFold で CV。確率出力から閾値を最適化し、汎化性能を確認。


まとめ

  • 不均衡データでは「正しく測る」「正しく学習する」が最重要
  • データ生成(SMOTE)、サンプリング、クラス重み、閾値最適化を組み合わせて最適解を探す
  • 指標は F1/PR-AUC を中心に、ROC-AUC も添える
  • 実装はリーク防止を最優先に Pipeline + Stratified CV で

Discussion