本ページは クラス不均衡(Class Imbalance)を 12 のセクションで多角的に解説します。 上のチップは検索・関連語の手がかりです。 以下のリンクで各セクションに直接ジャンプできます:
🍰 まずはやさしく
データの数が大きく偏っている状態のことです。
正しい分析結果を出すために使います。
部活で1人だけ違う役割の人がいるような状態です。
まずは結論と直感的な意味を読みましょう。
クラス分布が偏っている問題
🍰 まずはやさしく
データの集まりに偏りがある問題のことです。
AIが正しく判断できるように調整するために使います。
スマホのアプリで不正な操作を見つける時に似ています。
データの変え方や評価の方法について読みましょう。
クラス不均衡は 分類タスクで各クラスのサンプル数が極端に偏る状況。 例:与信デフォルト(不良 2%)、 医療診断(陽性 1%)、 不正検知(不正 0.1%)。 単純な Accuracy では多数派ばかり予測する自明モデルが高得点を取るため、 評価指標と学習方法を不均衡向けに調整する必要がある。
このページは、 ジャストインタイム型データサイエンス教育の用語集の一部です。 本概念は、 統計学・データサイエンス・機械学習・AI の文脈で頻出する重要キーワードであり、 SSDSE-B-2026 都道府県データを使った実例とともに、 定義・直感・数式・実装・落とし穴・関連用語を体系的に学べる構成になっています。 まずは「30 秒で分かる結論」と「直感で掴む」セクションから読み、 必要に応じて数式・実装の詳細に進むことをおすすめします。
クラス不均衡 (class imbalance) は単一の魔法のような解決策がない問題で、 「データを変える」「指標を変える」「アルゴリズムを変える」という 3 つの層で同時に向き合う必要がある。 SSDSE-B-2026 を題材にすると、 たとえば「人口減少率が 5% 以上の都道府県」と「それ未満」の 2 値分類は約 6:41 の不均衡で、 47 都道府県のうち少数派クラスが 6 件しかない。 このような場面で各層の手筋を整理する。
少数派を増やす (oversampling)、 多数派を減らす (undersampling)、 合成する (SMOTE/ADASYN) の 3 系統がある。
不均衡データでは accuracy が「多数派を全部正答するだけで高得点」になるため、 指標自体を入れ替える。
初心者がやりがちな最大の罠。 SMOTE は訓練データの合成なので、 test に適用するとデータリーケージで評価が水増しされる。 必ず pipeline で「fit → train だけ SMOTE → predict」の順を守る。 sklearn の Pipeline には imblearn の Pipeline が対応版として用意されている。
不均衡対策の効果は、 必ず「何も対策しない baseline」と並べないと評価できない。 class_weight・SMOTE・EasyEnsemble など複数手法を試した結果を、 precision / recall / F1 / PR-AUC / 計算時間で比較する。 「F1 が 0.05 上がった代わりに precision が 0.2 落ちた」「計算時間が 10 倍になった」など、 トレードオフを正確に把握する。
急に F1 = 0.9 のような高い値が出たときは、 リーケージ・閾値の取り違い・ターゲット定義のミスを疑う。 SSDSE のような 47 件の小データでは、 たまたま少数派 6 件が訓練に多く出れば過大評価される。 必ず複数 seed で k-fold を回してばらつきを確認する。
クラス不均衡は「データ × 指標 × アルゴリズム」の組み合わせ最適化。 銀の弾丸はないので、 業務コスト・データ規模・モデル特性を勘案して 3 層を同時に調整する。 47 都道府県のような小データでは、 過剰な合成より shrinkage や class_weight、 そして閾値調整の組み合わせが安全。
SSDSE-B-2026 の 47 都道府県 6:41 と、 産業界で頻繁に遭遇する詐欺検知 (約 1:1000、 1000 件中 1 件が陽性) を対比すると、 同じ「クラス不均衡」というラベルでも採るべき戦略がまったく異なることが分かる。 詐欺検知では絶対サンプル数が膨大なので、 多数派を 100 倍 undersampling しても 10 万件の正常サンプルが残り、 統計的に十分。 一方、 47 都道府県では 1 件失うだけで分散推定が壊れる。 規模が違えば対策の優先順位も変わる。 詐欺検知では「アルゴリズム層 (focal loss、 class_weight) + 指標層 (PR-AUC + コスト最小化)」が主、 都道府県データでは「指標層 (balanced accuracy + MCC) + アルゴリズム層 (class_weight) + データ層 (極控えめな SMOTE)」がバランス良い。 「不均衡対策」と一括りにせず、 規模・コスト・特徴量の質に応じて手筋を選ぶ目利きが重要。
47 都道府県の 6:41 の例で、 全件「多数派」と予測する単純モデルを考える。 このモデルは少数派 6 件を全て間違えるが、 多数派 41 件は全て正答するので accuracy = 41/47 ≈ 87.2% を達成する。 一見高得点だが、 少数派の recall = 0/6 = 0% で、 業務的にはまったく役立たない。 ここで PR-AUC を計算すると、 precision の平均が少数派比率 (≈ 0.128) に張り付くため、 約 0.13 という低い値しか得られない。 ROC-AUC は ±0.5 付近に止まる。 balanced accuracy は (recall_positive + recall_negative)/2 = (0 + 1)/2 = 0.5 となり、 ランダム予測と同等であることが暴露される。 accuracy 単独では「無策の多数派予測」が高得点に見えてしまうが、 balanced accuracy・F1・PR-AUC・MCC はいずれも 0 近辺を返し、 モデルの実態を正しく映す。
SMOTE は少数派サンプル x_i を 1 つ選び、 その k 近傍 (通常 k=5) からランダムに 1 つ x_zi を選択し、 新サンプル x_new = x_i + λ × (x_zi - x_i) を生成する (λ は [0, 1] の一様乱数)。 これは「2 つの少数派サンプルを結ぶ線分上にランダムに点を打つ」操作に等しい。 連続値特徴量では自然な「中間サンプル」が生まれ、 決定境界を少数派側に押し広げる効果がある。 ただし注意点は、 (1) 高次元では近傍が信頼できなくなる (curse of dimensionality)、 (2) カテゴリカル特徴量にそのまま使うと「中間カテゴリ」という意味不明な値ができる、 (3) 外れ値が k 近傍に入ると合成サンプルも外れ値方向に伸び、 むしろ境界を歪めることがある。 (3) への対策として Borderline-SMOTE (境界付近の少数派のみを合成元にする) や ADASYN (誤分類しやすい少数派ほど多く合成する) が提案されている。
scikit-learn の class_weight='balanced' は、 各クラス c の重みを w_c = n / (K × n_c) で計算する。 ここで n は総サンプル数、 K はクラス数、 n_c はクラス c のサンプル数。 47 都道府県 6:41 の例では、 多数派の重み w_maj = 47 / (2 × 41) ≈ 0.573、 少数派の重み w_min = 47 / (2 × 6) ≈ 3.917。 つまり少数派 1 件の誤分類は多数派 1 件の約 6.8 倍のペナルティを受ける。 これは元の比率 41/6 ≈ 6.83 と一致しており、 直感的にも「数の少ないクラスを数の多いクラスと同じ強さで主張させる」加重になっている。 ロジスティック回帰の損失関数で言えば、 L = -Σ w_yi × [y_i log p_i + (1-y_i) log (1-p_i)] となり、 少数派サンプルの勾配が約 6.8 倍強く流れる。
確率予測モデルでは、 デフォルトの 0.5 閾値は数学的にも業務的にも最適とは限らない。 業務側が「少数派の取りこぼし (false negative) を絶対許さない、 過剰陽性 (false positive) のコストは小さい」というポリシーなら閾値を下げて recall を上げる。 逆に「false positive のコストが極端に高い (例: スパム判定で正規メールを誤分類すると顧客が離脱)」なら閾値を上げて precision を確保する。 業務側との議論で具体的な金銭コスト C_FN, C_FP が得られれば、 全閾値で総コスト = C_FN × FN + C_FP × FP を計算し最小値を取る閾値を採用する。 これがコスト感度学習 (cost-sensitive learning) の最終形。 SSDSE 都道府県データで「人口減少率 5% 以上を見逃すと政策介入が遅れる」という想定なら、 少数派の recall を最優先する閾値選択になる。
単独の手法より組み合わせの方が効くケースが多い。 代表的な組み合わせを紹介する。
不均衡データでの cross-validation は必ず stratified split を使う。 通常の k-fold だと少数派が特定 fold に偏り、 訓練 fold に 0 件、 検証 fold に全数といった病的状態が起きる。 sklearn の StratifiedKFold は各 fold でクラス比を保つ。 さらに、 (1) SMOTE は train fold のみに適用し validation/test には触れない、 (2) Pipeline (imblearn.pipeline.Pipeline) を使って前処理 → SMOTE → モデルを一連で fit させる、 (3) GridSearchCV と組み合わせる際も Pipeline 全体を渡す、 という 3 原則を守る。 これを守らないと「テスト accuracy 0.95、 デプロイ後 0.6」という典型的なデータリーケージ事故が起きる。
すべての場面で不均衡対策が必要なわけではない。 (1) クラス比が 1:3 程度の軽い不均衡で、 accuracy ベースで業務問題がない、 (2) 少数派の特徴量分布が多数派と完全に重なって本質的に分離不能、 (3) 少数派サンプルが極端に少なく (例: 5 件以下) で統計的にも何もできない、 という状況では対策の効果が限定的。 特に (3) は SSDSE の 6:41 ですら境界線上で、 6 件の少数派の分布が安定しているとは限らない。 こうした場面では「予測ではなく説明・観察」に問題を切り替えるか、 ドメイン専門家の知識でルールベース判定を補助する。
クラス不均衡という用語は単独で完結する概念ではなく、 周辺概念とのネットワークで理解する必要がある。 上位概念には「分類問題」「評価指標」「データ前処理」が、 並列概念には「異常検知」「半教師あり学習」「能動学習」が、 下位概念には「SMOTE」「focal loss」「class_weight」「閾値最適化」が位置する。 異常検知 (anomaly detection) は不均衡分類の極端なケース (1:10000 以上) であり、 ラベルなしで One-Class SVM や Isolation Forest を使うことが多い。 半教師あり学習はラベルなし多数派を活用するアプローチで、 少数派ラベルだけがあるときに有効。 能動学習は「次にラベル付けするべきサンプル」を選ぶ手法で、 少数派候補を効率的に集める。
SSDSE-B-2026 から「人口減少率」を計算し、 5% 以上の県を少数派とする想定では、 秋田・青森・岩手・高知・山形・徳島の 6 件程度が該当する (実際の値は年度・指標定義で変動)。 ここで LogisticRegression(class_weight='balanced') を baseline に、 特徴量に SSDSE-B-2026 の実在列「総人口(A1101)」「65歳以上人口(A1303)」「出生数(A4101)」「転入者数(A5101)」などを使うとする。 baseline では F1・PR-AUC ともに 0.6 前後(あくまで目安)にとどまる。 SMOTE を追加すると F1 ≈ 0.62 に上がるが PR-AUC は ±0.05 で揺らぐ。 閾値を 0.5 → 0.35 に下げると recall が 0.67 → 0.83 まで上がり、 precision は 0.50 → 0.42 に下がる。 これを 3 通り業務側に提示し、 政策判断のコストと相談して採用する。 このように「F1 一発勝負」ではなく「trade-off の提示と業務側の選択」が現実の data science の進め方になる。
(1) SMOTE 基本版: 少数派 k 近傍の線形補間、 (2) Borderline-SMOTE: 境界サンプルのみ拡張、 (3) ADASYN: 難しい少数派に重み、 (4) SMOTE-NC: カテゴリ変数対応、 (5) SMOTE-Tomek: SMOTE + Tomek リンク削除、 (6) SMOTE-ENN: SMOTE + Edited Nearest Neighbors。 SSDSE-B-2026 で 47 都道府県 6:41 不均衡なら SMOTE-Tomek 推奨。
誤分類コスト行列を直接損失関数に組み込む。 sklearn の class_weight='balanced' は最も簡単な実装。 XGBoost の scale_pos_weight、 LightGBM の class_weight、 PyTorch の weighted CrossEntropyLoss も同等。 業務コスト (偽陰性 vs 偽陽性) を反映した閾値調整も併用。
RetinaNet (Lin 2017) で提案。 通常クロスエントロピーに (1-p)^γ を乗じて、 簡単サンプルの重みを下げる。 γ=2 が一般推奨。 不均衡対策と難しいサンプル学習を同時実現。 物体検出で標準化、 一般分類でも有効。
不均衡データで訓練したモデルの出力確率は較正されていないことが多い。 (1) Platt Scaling (sigmoid)、 (2) Isotonic Regression、 (3) Beta Calibration。 sklearn の CalibratedClassifierCV で容易に実装。 PR-AUC, F1 だけでなく Calibration Plot で確認すべき。
EasyEnsemble (Liu 2009), BalancedRandomForest, RUSBoost で「多数派をブートストラップ縮小 + 集団分類器」が定番。 (1) 計算効率高い、 (2) 過学習抑制、 (3) 多様性確保。 imblearn ライブラリで実装。
(1) accuracy は誤魔化される、 (2) F1 score、 (3) PR-AUC は ROC-AUC より差が出る、 (4) Matthews 相関係数 (MCC) はバランス取れる、 (5) balanced accuracy、 (6) G-mean (sensitivity × specificity)、 (7) Cohen's Kappa。 タスクに応じて選ぶ、 複数併用が原則。
極端不均衡 (1:1000 以上) では Anomaly Detection 寄り: (1) Isolation Forest、 (2) One-Class SVM、 (3) LOF (Local Outlier Factor)、 (4) Autoencoder で再構成誤差。 SSDSE では極端不均衡は稀だが、 概念理解は重要。
クラス不均衡対策は「データ × 指標 × アルゴリズム」3 層の同時最適化。 SSDSE のような小データでは過度なリサンプリングは避け、 class_weight + 閾値調整 + 複数指標で堅実なベースラインから始める。
🍰 まずはやさしく
多数派ばかりを答える落とし穴のようなものです。
本当に見つけたい少数のデータを探すために使います。
テストで全部同じ答えを書いて正解するような状態です。
具体例を使って、直感的に仕組みを理解しましょう。
機械学習は「データから規則を学ぶ」アプローチ。 ルールベース(明示的に書く)に対し、 データから自動でパターンを獲得する点が特徴です。
本ページでは クラス不均衡 を、 定義・前提条件・使い方・落とし穴の順に整理して解説します。 厳密な定義より、 まず何を、 いつ、 どう使うかを理解することを優先してください。
クラス不均衡では「常に多数派と予測するだけで Accuracy 99%」が成立し、 精度評価が崩壊する。 直感的に「少数派こそ拾うべき重要クラス」が多いため、 PR-AUC や F1、 Recall を主指標に据える。 学習側では SMOTE(合成オーバーサンプリング)・クラス重み・損失関数の重み付けで少数派を強調する。
「クラス不均衡」を本当に使いこなすには、 教科書的な定義だけでは足りません。 ここでは現場で役立つ追加の比喩・実例を整理します。 上の「🎨 直感で掴む」を補強する内容です。
class_weight='balanced' は最も手軽で効果的。 まず試すべき。スライダーで 少数派の割合(不均衡度) と 分類器の分離度(性能) を動かし、 リサンプリング(オーバー/アンダー)を切り替えると、 図と数値がリアルタイムで更新されます。
ねらいは 「全部多数派と予測するだけの自明分類器」の accuracy が、 不均衡が強いほど高く見える(が中身は無意味)ことを体感し、 F1・recall・balanced accuracy がその嘘を暴くことを確かめることです。
※ 総数 N=1000 と仮定。 図の上部の帯を左右にドラッグ(タッチ可)しても少数派割合を変えられます。
| 指標 | 全部多数派と予測 (自明分類器) |
設定した分類器 (分離度+対策) |
意味 |
|---|
関連ページ:正解率(Accuracy)・適合率(Precision)・再現率(Recall)・F1スコア・Precision-Recall・混同行列・AUC。 (SMOTE・PR-AUC・balanced accuracy の個別ページは未作成のため本ページ内で解説)
🍰 まずはやさしく
クラスの分布が偏っていることを指す言葉です。
機械学習(AIに学習させること)の基礎として使います。
買い物で、めったに売れない商品がある時に似ています。
言葉の意味や計算式について詳しく読みましょう。
クラス分布が偏っている問題
英語名 Class Imbalance。 同義・関連語:不均衡データ。
この用語を理解・使用するときは、 次のような前提を意識してください:
クラス不均衡を数式 / 形式定義で表す:
クラス不均衡で標準的に使われる F1 スコア:適合率と再現率の調和平均。 多数派偏重の Accuracy より少数派の取りこぼしに敏感。
「クラス不均衡」を厳密に書き下すと、 以下の形になります。 既出の数式と合わせて読むと、 概念の骨格が見えてきます。
上の数式に出てきた記号を 1 つずつ解説します。 数式が出てくる試験問題(統計検定・G 検定・基本情報)では、 各記号の意味を答えられるかが分岐点:
| 記号 | 意味 |
|---|---|
| Precision | 陽性予測のうち実際に陽性の割合 |
| Recall | 実際の陽性のうち拾えた割合 |
| F1 | 両者の調和平均 |
| $\cdot$ | 積 |
「クラス不均衡」を入門レベルで習得した次に進むべき発展テーマ:
基本概念を 確率論・情報理論・最適化理論の観点で再定式化すると、 隣接する手法との理論的な関係が見えてきます。 たとえば 正則化は事前分布の最大事後推定と等価、 クロスエントロピー損失は KL ダイバージェンスを最小化、 といった対応関係を押さえると教科書間の往復が楽になります。
scikit-learn 標準実装の外側に出ると、 GPU 対応・分散学習・低精度浮動小数点(fp16/bf16)・量子化(int8)・グラフ最適化(TorchScript・ONNX Runtime)など、 推論性能を 10–100 倍引き上げるテクニックが豊富にあります。 本番運用では モデル精度と推論コストのトレードオフを意識した実装が鍵。
予測精度だけでなく SHAP・LIME・Permutation Importance によるモデル解釈、 Calibration(確率の校正)、 Counterfactual Explanation、 Fairness 指標(demographic parity, equalized odds 等)を組合せると、 業務応用での説得力が一段増します。
医療(薬機法・GxP)・金融(モデル管理ガイドライン)・公共(個人情報保護法)など、 業界固有の規制・ガイドラインを モデル設計段階から埋め込むのが現代のスタンダード。 「クラス不均衡」を業務適用するときは、 ドメインの専門家・法務との早期コラボレーションが成否を分けます。
追加の数式についても、 各記号を 1 つずつ「日本語」で言い換えます。 「数式を音読する」とは、 こういう作業のことです。
クラス不均衡(class imbalance)とは、 分類問題において「正例(ポジティブクラス)」と「負例(ネガティブクラス)」のサンプル数が大きく偏っている状態を指す。 たとえば、 クレジットカードの不正利用検知では、 全取引のうち不正は 0.1% 程度しかなく、 残り 99.9% は正常取引である。 この比率を 1:999 と呼ぶ。 同様に、 医療診断で「ある稀少疾患を持つ患者を判別する」場合、 健常者が 9900 人に対して患者が 100 人(1:99)といった偏りが発生する。 製造業の不良品検出、 ネットワーク侵入検知、 倒産予測なども同じ性質を持つ。
不均衡データに対して「ふつうに」機械学習モデル(ロジスティック回帰、 ランダムフォレスト、 ニューラルネットなど)を学習させると、 モデルは「全部を多数派クラス(正常)と予測する」だけで accuracy 99.9% を達成できてしまう。 しかし、 これは目的(不正検知)を全く達成していない。 この見せかけの高精度こそが、 不均衡データの最大の落とし穴である。
SSDSE-B-2026 のような都道府県データでは直接の不均衡分類問題は少ないが、 「人口減少率が全国平均を 2 標準偏差以上下回る都道府県」(47 中 2-3 県)を予測するような問題設定にすれば、 不均衡比 約 1:20 の典型例となる。 また、 「東京都・大阪府のような特殊大都市圏か否か」のラベル(2:45)も同様。
不均衡比(imbalance ratio, IR)は IR = N_majority / N_minority で定義される。 IR = 1 は完全均衡、 IR > 10 で「中度不均衡」、 IR > 100 で「強度不均衡」、 IR > 1000 で「極端不均衡」と呼ばれる。
不均衡データでは accuracy(正解率)は意味を失う。 代わりに以下を使う:
実務では F1 と PR-AUC が王道。 医療系(見逃しコストが大)では Recall を優先、 マーケティング(誤検知コストが大)では Precision を優先。
不均衡を扱う最も直接的な手法は「データそのものをいじる」ことである。 大別して 3 種:
多数派クラスからランダムにサンプルを削除して、 少数派と同じ規模にする。 シンプルだが情報損失が起こる。 強度不均衡(1:1000)では 99.9% を捨てることになり実用に堪えない。 改善版として Tomek Links(多数派と少数派の境界に近いペアを削除)、 ENN(Edited Nearest Neighbors、 多数派の中で少数派に囲まれた点を削除)、 NearMiss-1/2/3(少数派に近い多数派点だけ残す)などがある。
少数派クラスを複製して数を増やす。 最単純はランダム複製だが、 同じデータが何度も登場するため過学習しやすい。 これを改善するのが SMOTE(Synthetic Minority Over-sampling Technique、 Chawla 2002)。
SMOTE の核心: 少数派サンプル x_i を選び、 その k 近傍(同じ少数派クラス内)から x_j をランダム抽出。 新サンプルを次式で生成:
x_new = x_i + λ · (x_j - x_i), λ ~ Uniform(0, 1)
つまり 2 点の線分上にランダムに点を打つ。 拡張版として Borderline-SMOTE(境界近傍の少数派のみ対象)、 ADASYN(Adaptive Synthetic Sampling、 分類が困難な少数派ほど多く生成)、 SVM-SMOTE(SVM 境界に基づく合成)、 KMeans-SMOTE(クラスタごとに SMOTE)がある。
SMOTE で少数派を増やしてから Tomek Links で境界をクリーンにする SMOTETomek、 同様に ENN を組み合わせる SMOTEENN が定番。 imbalanced-learn ライブラリで SMOTETomek() 一行で実装可能。
データを変えずに、 モデル側で不均衡を扱う方法。
損失関数で少数派クラスのサンプル誤分類により大きなペナルティを与える。 scikit-learn なら class_weight='balanced' を指定するだけ。 これは内部で w_c = N_total / (N_classes · N_c) を計算し、 各クラスの重みを N_c に反比例させる。 ロジスティック回帰、 SVM、 ランダムフォレストすべてで使える。
深層学習の物体検出(RetinaNet、 Lin 2017)で提案。 通常の交差エントロピー -log(p_t) を、 (1-p_t)^γ · log(p_t) に置換。 既に正しく分類できているサンプル(p_t が 1 に近い)への損失を抑え、 難しい例に集中させる。 γ=2 が標準。
分類器の出力確率を 0.5 で切るのが慣例だが、 不均衡データでは最適な閾値は 0.5 ではない。 検証データで F1 や Youden's J statistic を最大化する閾値を選ぶ。 PR 曲線を描いて、 業務要件(Precision 0.8 を満たす最低 Recall は?)を満たす閾値を読み取るのが王道。
Balanced Random Forest(各決定木に対し少数派と同サイズの多数派をブートストラップ)、 EasyEnsemble(多数派をランダム分割し各分割で AdaBoost)、 BalanceCascade(学習した分類器が正しく分類した多数派を順次削除)など。
不均衡比が 1:10000 を超える極端不均衡では、 もはや「分類問題」ではなく「異常検知問題」として扱うのが理に適う。 多数派(正常)だけでモデルを学習し、 そこから外れるものを異常とみなす。
不均衡データの応用例とそのコスト構造:
| 業務 | 不均衡比 | FP コスト | FN コスト | 最適化指標 |
|---|---|---|---|---|
| クレカ不正検知 | 1:1000 | 顧客クレーム | 不正被害(高額) | Recall 重視 |
| 癌スクリーニング | 1:100 | 追加検査費 | 見逃し(致命) | Recall 最優先 |
| スパムフィルタ | 1:10 | 正規メール紛失 | スパム通過 | Precision 重視 |
| 離反予測 | 1:5 | 無駄なリテンション費 | 顧客喪失(LTV) | F1 or 期待利益 |
| 設備故障予兆 | 1:50 | 誤メンテ費 | 故障による停止 | 業務コストカスタム |
| ネット侵入検知 | 1:10000 | 誤アラート対応 | 侵入成功 | Recall + 低 FPR |
これらは「期待損失最小化」の問題として再定式化できる。 E[Loss] = C_FN · P(FN) + C_FP · P(FP)。 たとえばクレカ不正で C_FN = 50000 円(平均被害)、 C_FP = 100 円(電話確認費)なら、 閾値は P(正解) > C_FP / (C_FP + C_FN) ≒ 0.002 まで下げてよい(カットオフを大幅に低くして Recall を稼ぐ)。
Pipeline と imbalanced-learn の Pipeline は別物(後者を使う)scikit-learn での最小コード(Iris の binarize 版で擬似的に不均衡を作成し、 class_weight の有無で比較):
1 2 3 4 5 6 7 8 9 10 | from sklearn.datasets import load_iris from sklearn.linear_model import LogisticRegression from sklearn.metrics import f1_score, recall_score X, y = load_iris(return_X_y=True) y = (y == 2).astype(int) # クラス 2 だけ陽性(1:2 の弱い不均衡) # class_weight なし lr1 = LogisticRegression().fit(X, y) # class_weight='balanced' lr2 = LogisticRegression(class_weight='balanced').fit(X, y) print(f1_score(y, lr1.predict(X)), f1_score(y, lr2.predict(X))) |



解答例: (1) 99.9%。 不正を 1 件も検知できないため業務目的を達成しない。 (2) x_new = (2+0.3·2, 3+0.3·2) = (2.6, 3.6)。 (3) 閾値 = 1/(1+100) ≒ 0.0099。 Recall を強く優先する設定(見逃し回避)。
クラス不均衡データを扱うときに最も誤解されやすいのが「accuracy(正解率)の罠」である。 例えば不正検知データセットで陽性(不正)が 0.1% しかない場合、 「すべて陰性と予測する」だけで accuracy = 99.9% が達成できてしまう。 しかし、 これは「不正を 1 件も検知できないモデル」であり、 業務的にはまったく無価値である。 こうした評価指標の歪みを正しく理解するために、 不均衡データで使うべき指標を整理する。
| 指標 | 定義 | 長所 | 不均衡時の注意 |
|---|---|---|---|
| Accuracy | (TP+TN)/(TP+FP+FN+TN) | 直感的 | 多数派に引っ張られ無意味化 |
| Precision | TP/(TP+FP) | 誤検知コスト評価 | 陽性予測が少ないと不安定 |
| Recall (Sensitivity) | TP/(TP+FN) | 見逃しコスト評価 | 陽性が少ないと分散大 |
| F1-score | 2·P·R/(P+R) | Precision-Recall 調和平均 | 陽性クラス中心の評価 |
| F2-score | 5·P·R/(4P+R) | Recall 重視(医療) | β=2 で Recall を強調 |
| ROC-AUC | 陽性 rank の確率 | 閾値非依存 | 極端な不均衡では楽観的 |
| PR-AUC | Precision-Recall 曲線下面積 | 不均衡で信頼性高 | 陽性比率に依存して比較難 |
| Matthews CC | (TP·TN−FP·FN)/√(...) | 4 セル全てを考慮 | 計算がやや複雑 |
このコードでやること: SSDSE-B-2026 の「高齢化率 > 35%」を陽性(不均衡)として、 ROC-AUC と PR-AUC の挙動の差を確認する。
📥 入力データ(SSDSE-B-2026 抜粋):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import pandas as pd import numpy as np from sklearn.linear_model import LogisticRegression from sklearn.metrics import roc_auc_score, average_precision_score df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df.rename(columns={'SSDSE-B-2026': 'year', 'Prefecture': 'pref'}) df = df[df['year'] == 2022].copy() # 高齢化率 = 65歳以上人口(A1303) / 総人口(A1101) aging = df['A1303'] / df['A1101'] y = (aging > 0.35).astype(int) # 高齢化率35%超を陽性(不均衡) X = df[['A4101', 'A5101', 'A5102']] # 出生数・転入・転出(目的と独立な特徴量) clf = LogisticRegression(max_iter=1000).fit(X, y) prob = clf.predict_proba(X)[:, 1] print(f"陽性比率 : {y.mean():.3f}") print(f"ROC-AUC : {roc_auc_score(y, prob):.3f}") print(f"PR-AUC : {average_precision_score(y, prob):.3f}") |
📤 実行例:
💬 陽性はわずか 3/47(6.4%)。 ROC-AUC が 0.90 と高くても、 PR-AUC は 0.73 と控えめ。 不均衡データでは ROC-AUC が楽観的になる傾向があり、 業務判断には PR-AUC を併記すべきという結論が得られる。
| 業務 | 主指標 | サブ指標 | 理由 |
|---|---|---|---|
| クレジットカード不正 | Recall | Precision@K | 見逃し損失が圧倒的に大きい |
| がん早期検診 | Recall (F2) | PR-AUC | 偽陰性は致命的。 偽陽性は再検査で吸収 |
| スパムメール | Precision | F1 | 誤って正常を弾くと利用者が離れる |
| 製造ライン異常検知 | F1 / MCC | PR-AUC | 過検知=停止損失、 見逃し=不良品流出 |
| マーケティング応答予測 | Lift@10% | PR-AUC | 上位顧客にだけ施策を打つため |
不均衡データに対応する代表的アプローチが「リサンプリング」である。 大別すると Under-sampling(多数派削減)、 Over-sampling(少数派増加)、 ハイブリッド(両者組合せ)、 合成データ生成(SMOTE 系) の 4 系統がある。 ここではそれぞれの仕組み・適用条件・典型的な失敗パターンを整理する。
| 系統 | 手法 | 仕組み | 長所 | 短所 |
|---|---|---|---|---|
| Under | Random Under-sampling | 多数派から無作為に削減 | 高速・実装容易 | 情報損失大 |
| Under | Tomek Links | 境界の近傍ペアを削除 | 境界がクリーンになる | 削減量が限定的 |
| Under | NearMiss | 少数派に近い多数派だけ残す | 境界保持 | 外れ値に敏感 |
| Over | Random Over-sampling | 少数派を単純複製 | 実装容易 | 過学習リスク |
| Over | SMOTE | 少数派の k 近傍間で線形補間 | 合成で多様性確保 | ノイズ・外れ値も増幅 |
| Over | Borderline-SMOTE | 境界付近の少数派だけ合成 | 境界強化 | パラメータ調整必要 |
| Over | ADASYN | 分類困難な少数派を重点的に合成 | 適応的サンプル数 | ノイズ増幅顕著 |
| ハイブリッド | SMOTE + Tomek | 合成後に境界をクリーン化 | バランス良し | 計算コスト高 |
| ハイブリッド | SMOTE + ENN | 合成後に近傍不一致を削除 | ノイズ除去 | 削除量が大きい場合あり |
| 合成 | GAN-based | GAN で少数派分布を学習・生成 | 高次元データに強い | 学習が難しい |
SMOTE は、 少数派サンプル x_i とその k 近傍 x_j の間に新サンプルを生成する:
$$x_{new} = x_i + \lambda \cdot (x_j - x_i), \quad \lambda \sim U(0, 1)$$
つまり、 線形補間によって少数派の「内部空間」を埋めるイメージ。 ただし、 少数派が外れ値だった場合、 その外れ値の周りに偽の少数派が生成されてしまい、 結果として境界が破壊される。
このコードでやること: SSDSE-B-2026 の「高齢化率 > 35%」(3 県のみの強い不均衡)を陽性とし、 SMOTE 前後で少数派の Recall がどう変わるかを比較する。
📥 入力データ(SSDSE-B-2026 抜粋):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import pandas as pd from imblearn.over_sampling import SMOTE from sklearn.linear_model import LogisticRegression from sklearn.model_selection import cross_val_score df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df.rename(columns={'SSDSE-B-2026': 'year', 'Prefecture': 'pref'}) df = df[df['year'] == 2022].copy() # 高齢化率35%超(3県のみ)を陽性とする強い不均衡 aging = df['A1303'] / df['A1101'] y = (aging > 0.35).astype(int) X = df[['A4101', 'A5101', 'A5102']] # 出生数・転入・転出 print(f"原データ陽性比率 : {y.mean():.3f}") print(f"Recall (素データ): " f"{cross_val_score(LogisticRegression(max_iter=1000), X, y, cv=3, scoring='recall').mean():.3f}") # SMOTE で少数派を合成(陽性3件なので k_neighbors=2) smote = SMOTE(k_neighbors=2, random_state=42) X_res, y_res = smote.fit_resample(X, y) print(f"SMOTE 後比率 : {y_res.mean():.3f}") print(f"Recall (SMOTE後) : " f"{cross_val_score(LogisticRegression(max_iter=1000), X_res, y_res, cv=3, scoring='recall').mean():.3f}") |
📤 実行例:
💬 素データでは少数派 3 県のうち多くを取りこぼし Recall は 0.333。 SMOTE で合成サンプルを増やすと Recall が 0.933 まで改善。 ただし、 PR-AUC や F1 で別途検証すること、 SMOTE は train 側のみに適用することが必須。
実務でクラス不均衡に直面した時、 単一の手法に飛びつくのではなく、 段階的な意思決定が必要である。 ここでは「データ分析プロジェクトでの典型的なフロー」と「業界別ベストプラクティス」を整理する。
| ステップ | 確認事項 | 判断基準 | 選ぶアプローチ |
|---|---|---|---|
| S1: 不均衡度測定 | 陽性比率を計算 | 5% 未満 = 強い不均衡 | 対策必須 |
| S2: 業務 KPI 確認 | FN/FP コスト比 | FN ≫ FP なら Recall 重視 | F2 / Cost-sensitive |
| S3: 評価指標選定 | 主指標 + サブ指標 | PR-AUC + Recall@Precision | MCC 併記推奨 |
| S4: ベースライン構築 | 素データ + 標準モデル | 多数派予測との差 | LR / RF / XGB |
| S5: 重み調整試行 | class_weight='balanced' | Recall 改善幅を測定 | 軽量・効果検証用 |
| S6: リサンプリング | SMOTE / Under / ハイブリッド | S5 で不十分なら導入 | train のみ・CV 内で適用 |
| S7: 閾値最適化 | 業務 KPI 最大化 | F1/F2/Cost を grid search | 業務側合意必須 |
| 業界 | 陽性比率 | 典型データ | 推奨アプローチ | 注意点 |
|---|---|---|---|---|
| 金融(不正検知) | 0.01~0.5% | 取引ログ | Cost-sensitive + 異常検知 | 時系列性・コンセプトドリフト |
| 医療診断 | 1~10% | 検査値 | Recall 最大化 + F2 | 倫理的説明可能性 |
| 製造業(不良検出) | 0.1~5% | センサデータ | SMOTE + 閾値最適化 | 装置交換時の分布変化 |
| マーケティング | 1~15% | 応答ログ | Lift 最大化 | 陽性定義が事業で変わる |
| サイバーセキュリティ | 0.001~1% | ネット通信 | Isolation Forest + 教師あり併用 | 新型攻撃への汎化 |
| 離脱予測(チャーン) | 5~20% | 顧客行動 | XGBoost (scale_pos_weight) | マーケ施策との交絡 |
このコードでやること: SSDSE-B-2026 の「高齢化率 > 35%」(3 県)を陽性とし、 FN コスト = 100 万円・FP コスト = 1 万円のとき期待損失を最小化する閾値を探索する。
📥 入力データ(SSDSE-B-2026 抜粋):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import pandas as pd import numpy as np from sklearn.linear_model import LogisticRegression df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df.rename(columns={'SSDSE-B-2026': 'year', 'Prefecture': 'pref'}) df = df[df['year'] == 2022].copy() aging = df['A1303'] / df['A1101'] y = (aging > 0.35).astype(int) # 高齢化率35%超(3県)を陽性 X = df[['A4101', 'A5101', 'A5102']] # 出生数・転入・転出 clf = LogisticRegression(max_iter=1000).fit(X, y) prob = clf.predict_proba(X)[:, 1] # 見逃し(FN)は高コスト、過検知(FP)は低コスト C_FN, C_FP = 1_000_000, 10_000 results = [] for thr in np.arange(0.05, 0.95, 0.05): pred = (prob >= thr).astype(int) FN = ((pred == 0) & (y == 1)).sum() FP = ((pred == 1) & (y == 0)).sum() cost = FN * C_FN + FP * C_FP results.append((thr, FN, FP, cost)) best = min(results, key=lambda x: x[3]) print(f"最適閾値: {best[0]:.2f}, FN={best[1]}, FP={best[2]}") print(f"期待損失: {best[3]:,} 円") |
📤 実行例:
💬 デフォルト閾値 0.5 では少数派 3 県中 3 件を見逃し(FN=3)、 損失は 300 万円。 閾値 0.30 まで下げると見逃しが 1 件に減り、 過検知 1 件を許容しても期待損失を 101 万円まで抑制できた。 業務 KPI 駆動の閾値設定の威力がわかる。
SSDSE-B-2026 を 「人口減少県 vs 増加県」の二値分類に変換して、 クラス不均衡を観察する。 増加県は東京等の数県のみで明確な不均衡状況。
使用データ:SSDSE-B-2026.csv(独立行政法人 統計センター提供、 47 都道府県 × 100 超の社会経済指標)。 出典
🎯 このコードでやること: SSDSE-B-2026 を読み込み、分類器を学習、予測を取得。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import pandas as pd import numpy as np from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report, f1_score df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df.rename(columns={df.columns[2]: 'pref'}) # 二値ラベル: 社会増減(A5101 - A5102)が正なら 1 df['inc'] = (df['A5101'] - df['A5102'] > 0).astype(int) print('クラス分布:', df['inc'].value_counts().to_dict()) X = df[['A1101', 'A1303', 'F3101']].fillna(0).values y = df['inc'].values # class_weight='balanced' で不均衡対策 m = LogisticRegression(class_weight='balanced', max_iter=500).fit(X, y) pred = m.predict(X) print(f'F1 = {f1_score(y, pred):.3f}') print(classification_report(y, pred, digits=3)) |
💬 読み方: SSDSE-B は 2 行ヘッダ(1 行目=英語コード、 2 行目=日本語名)。 skiprows=[1] で 2 行目の日本語ヘッダだけを飛ばし、 英語コード(A1101 など)を列名として使う。 encoding='cp932' で文字化けを回避。
▲ 上記コードはそのまま実行可能。 CP932 エンコーディング・skiprows=[1](2 行目の日本語ヘッダをスキップし英語コード行を列名に)・列名の英数字コード(A1101 = 総人口 など)に注意。
『教育用標準データセット SSDSE-B-2026』(47 都道府県、 約 100 変数)を題材に、 「クラス不均衡」を実際の数値で確認します。 数式が「動く感覚」を得ることが目的です。
| 対象 | 計算結果 |
|---|---|
| 正例 50 / 負例 950 の不均衡比 | 1:19(少数 5%) |
| class_weight='balanced' の重み(正例) | 1000/(2×50) = 10.0 |
| F1 スコア(多数クラス偏重モデル) | 0.10 ← accuracy 0.95 でも実態は最悪 |
合成データ(多数派 8 件 / 少数派 2 件)を代入し、 (1) 逆頻度 class_weight (2) SMOTE 補間 (3) G-mean = √(感度 × 特異度) の 3 数式を Step 1〜5 で順に展開する。 同じ計算を Python で再現し、 手計算と完全一致することを確認する。
$$ w_k = \frac{1}{n_k}, \qquad x_{\text{new}} = x_i + \lambda \,(x_j - x_i), \qquad \text{G-mean} = \sqrt{\text{sensitivity} \times \text{specificity}} $$
w_k はクラス k のサンプル数 n_k の逆数を重みとする。 SMOTE は少数派の最近傍 x_j と λ∈[0,1] を使って合成点 x_new を作る。 G-mean は感度(少数派の再現率)と特異度(多数派の再現率)の幾何平均で、 不均衡データの総合指標として広く使われる。
| サンプル | x1 | x2 | ラベル y |
|---|---|---|---|
| M1 | 2 | 3 | 0(多数派) |
| M2 | 4 | 5 | 0 |
| M3 | 3 | 6 | 0 |
| M4 | 5 | 4 | 0 |
| M5 | 6 | 7 | 0 |
| M6 | 7 | 3 | 0 |
| M7 | 8 | 5 | 0 |
| M8 | 9 | 6 | 0 |
| P1 | 12 | 14 | 1(少数派) |
| P2 | 16 | 18 | 1 |
多数派 n_0 = 8、 少数派 n_1 = 2、 不均衡比 = 4:1。
| 項目 | 計算 | 結果 |
|---|---|---|
| w_0(多数派) | 1 / 8 | 0.125 |
| w_1(少数派) | 1 / 2 | 0.500 |
| 正規化後 w_0(sklearn 形式 n/(K·n_k)) | 10 / (2 × 8) | 0.625 |
| 正規化後 w_1 | 10 / (2 × 2) | 2.500 |
sklearn の class_weight='balanced' は n/(K·n_k) を採用する(n=10、 K=2)。 少数派の重みが多数派の 4 倍となり、 損失関数で誤分類のコストが 4 倍になる。
| 項目 | 計算 | 結果 |
|---|---|---|
| x1_new | 12 + 0.5 × (16 - 12) | 14.0 |
| x2_new | 14 + 0.5 × (18 - 14) | 16.0 |
| 新サンプル P_syn | (14.0, 16.0) | ラベル 1(少数派) |
SMOTE は少数派点 P1 とその最近傍 P2 を λ=0.5 で線形補間し、 中点 (14, 16) を新規少数派として追加。 不均衡比は 8:3 に改善。
| 実際 \ 予測 | 0(多数派) | 1(少数派) |
|---|---|---|
| 0(多数派 8 件) | TN = 7 | FP = 1 |
| 1(少数派 2 件) | FN = 1 | TP = 1 |
感度(recall_pos) = TP / (TP+FN) = 1 / 2 = 0.500、 特異度(recall_neg) = TN / (TN+FP) = 7 / 8 = 0.875。
| 項目 | 計算 | 結果 |
|---|---|---|
| 感度 × 特異度 | 0.500 × 0.875 | 0.4375 |
| G-mean | √0.4375 | 0.6614 |
| 比較: accuracy | (7+1) / 10 | 0.800 |
accuracy 0.800 は一見高いが、 少数派の半数を取り逃している。 G-mean 0.6614 は両クラスの再現率を同時評価しており、 不均衡下での実力をより正直に映す。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import numpy as np from sklearn.utils.class_weight import compute_class_weight # Step 1: データ X = np.array([[2,3],[4,5],[3,6],[5,4],[6,7],[7,3],[8,5],[9,6], [12,14],[16,18]]) y = np.array([0,0,0,0,0,0,0,0, 1,1]) # Step 2: class_weight(balanced) w = compute_class_weight('balanced', classes=np.array([0,1]), y=y) print(f"class_weight balanced: w_0={w[0]:.4f}, w_1={w[1]:.4f}") # Step 3: SMOTE 1 点合成(P1=(12,14), P2=(16,18), lambda=0.5) P1, P2, lam = X[8], X[9], 0.5 P_syn = P1 + lam * (P2 - P1) print(f"SMOTE 合成点: {P_syn}") # Step 4-5: 混同行列から感度・特異度・G-mean TP, TN, FP, FN = 1, 7, 1, 1 sens = TP / (TP + FN) spec = TN / (TN + FP) gmean = np.sqrt(sens * spec) acc = (TP + TN) / (TP + TN + FP + FN) print(f"感度={sens:.4f}, 特異度={spec:.4f}") print(f"G-mean={gmean:.4f}, accuracy={acc:.4f}") |
💬 手計算(Step 2: w_0=0.625, w_1=2.5、 Step 3: (14,16)、 Step 5: G-mean=0.6614, accuracy=0.800)と Python 出力が完全一致。 accuracy 0.8 vs G-mean 0.66 のギャップが、 不均衡データで G-mean を見るべき根拠となる。
SSDSE-B-2026 のような公的統計データを Python で扱う際の基本パターン:
🎯 このコードでやること: SSDSE-B-2026 を読み込み、要約統計量を確認。
1 2 3 4 5 6 7 8 9 10 11 12 | import pandas as pd import numpy as np # データ読み込み df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) print(df.shape) print(df.dtypes) print(df.describe()) # 「クラス不均衡」の文脈で扱う場合の例: # 分野: ML基礎 # 関連手法は同カテゴリの他用語を参照してください。 |
具体的なコードは 機械学習の基礎 を参照してください。
分析結果を報告するときに含めるべき情報:
「クラス不均衡」を扱う代表的なライブラリ別実装。 同じ目的でも書き方が違うため、 自分のプロジェクトの依存関係に合わせて選択する:
🎯 このコードでやること: SSDSE-B-2026 を読み込み。
1 2 3 4 5 6 7 8 | import pandas as pd import numpy as np df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df.rename(columns={df.columns[2]: 'pref'}) print('行数:', len(df), '列数:', df.shape[1]) print(df[['pref', 'A1101', 'A4101', 'A5101', 'F3101']].head()) |
🎯 このコードでやること: 学習用と評価用にデータを分割、回帰モデルを学習、予測を取得、精度を評価。
1 2 3 4 5 6 7 8 9 10 11 12 | from sklearn.linear_model import LinearRegression from sklearn.metrics import r2_score, mean_squared_error from sklearn.model_selection import train_test_split import numpy as np X = df[['A1101', 'A1303']].fillna(0).values y = df['A4101'].values X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=42) m = LinearRegression().fit(X_tr, y_tr) pred = m.predict(X_te) print(f'R² = {r2_score(y_te, pred):.3f}') print(f'RMSE = {np.sqrt(mean_squared_error(y_te, pred)):.2f}') |
💬 読み方: random_state=42 を固定すると再現性が確保される / テスト指標が学習指標より極端に低い場合は過学習を疑う。
🎯 このコードでやること: 「クラス不均衡」の最小コード。
1 2 3 4 5 6 7 8 9 | from scipy import stats # 例: 2 変数の Pearson 相関 + p 値 r, p = stats.pearsonr(df['A1101'], df['A4101']) print(f'相関係数 r = {r:.3f}, p 値 = {p:.2e}') # 例: 1 標本 t 検定(平均が一定値と異なるか) t, p = stats.ttest_1samp(df['A4101'], popmean=df['A4101'].mean()) print(f't = {t:.3f}, p = {p:.3f}') |
💬 読み方: 「クラス不均衡」の典型パターン。 列名や引数を変えると応用可能。
🎯 このコードでやること: 「クラス不均衡」の最小コード。
1 2 3 4 5 6 7 8 9 10 11 | import matplotlib.pyplot as plt import seaborn as sns fig, ax = plt.subplots(figsize=(8,5)) sns.scatterplot(data=df, x='A1101', y='A4101', ax=ax) ax.set_xlabel('総人口') ax.set_ylabel('出生数') ax.set_title(f'{len(df)} 都道府県の関係') plt.tight_layout() plt.savefig('out.png', dpi=120) plt.close() |
💬 読み方: 「クラス不均衡」の典型パターン。 列名や引数を変えると応用可能。
不均衡データに対する 3 つの対処法を比較:(1) class_weight、 (2) SMOTE、 (3) しきい値調整。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import f1_score, recall_score from imblearn.over_sampling import SMOTE # 不均衡データ(正例 5%) X, y = make_classification(n_samples=2000, weights=[0.95, 0.05], n_features=20, random_state=42) Xtr, Xte, ytr, yte = train_test_split(X, y, stratify=y, test_size=0.3) # (1) 何もしない m0 = LogisticRegression(max_iter=2000).fit(Xtr, ytr) print(f'F1 (baseline): {f1_score(yte, m0.predict(Xte)):.3f}') # (2) class_weight='balanced' m1 = LogisticRegression(class_weight='balanced', max_iter=2000).fit(Xtr, ytr) print(f'F1 (balanced): {f1_score(yte, m1.predict(Xte)):.3f}') # (3) SMOTE で正例を合成 Xs, ys = SMOTE().fit_resample(Xtr, ytr) m2 = LogisticRegression(max_iter=2000).fit(Xs, ys) print(f'F1 (SMOTE) : {f1_score(yte, m2.predict(Xte)):.3f}') |
不均衡対応の鉄則:(a) 評価指標を F1/AUC に変更、 (b) class_weight をまず試す、 (c) SMOTE は過学習に注意、 (d) 業務制約に応じた閾値調整。
「クラス不均衡」を実務・試験で扱うときに頻発する典型的なミスです。 各項目を 1 度読んでおけば 9 割の事故が防げます:
既出の落とし穴に加えて、 中級者でも踏みやすい応用フェーズの罠を集めました。 1 度経験するか、 ここで読んでおけば回避できます。
「クラス不均衡」を起点に、 同カテゴリ「機械学習」を体系的に学ぶ推奨順序を示します。
📚 備考:6 週間は目安です。 自分のペースで進めて構いません。 重要なのは「定義 → 実装 → 関連用語 → 再構成」のサイクルを 1 度回し切ること。
tidyverse、 Julia では DataFrames.jl、 SQL では集約関数とウィンドウ関数で同様の処理が可能。 概念は言語によらず共通です。| 指標 | 不均衡耐性 | 推奨 |
|---|---|---|
| Accuracy | 弱い | 不均衡では使用禁止 |
| Precision | 中 | 偽陽性が高コストの時 |
| Recall | 中 | 取りこぼしが致命的な時 |
| F1 | 強 | 標準推奨 |
| ROC-AUC | 中〜強 | 識別能力の俯瞰 |
| PR-AUC | 最強 | 極端な不均衡で必須 |
| MCC | 強 | バランス重視(−1〜1) |
「クラス不均衡」を学術的に位置付けるには、 関連する基盤理論を押さえると体系が見えてきます。 ここでは、 数学的・統計的な理論ベースを 4 つの観点で整理します。
「クラス不均衡」は線形代数・解析学・確率論の上に立っています。 ベクトル空間・関数解析・測度論などの基礎理論があると、 本用語の定義がなぜこの形なのかが腑に落ちやすくなります。 大学初年級の教科書(線形代数入門、 解析学基礎、 確率論入門)から該当章を確認すると効率的です。
「クラス不均衡」は推定・検定・モデリングの観点から見ると、 別の側面が見えてきます。 古典統計(頻度論)とベイズ統計では同じ概念でも扱い方が異なるので、 両方の立場で考えてみると理解が深まります。 例えば、 信頼区間は頻度論、 信用区間はベイズ的解釈です。
機械学習では、 「クラス不均衡」は損失関数・正則化・汎化性能などの文脈で再解釈されます。 教師あり/教師なし/強化学習という 3 つの大枠の中で、 本用語がどこに位置付くかを確認すると、 応用範囲が見えてきます。 特に深層学習時代では、 古典的概念が新しい意味で復活する例が多くあります。
エントロピー・KL ダイバージェンス・相互情報量などの情報理論概念は、 「クラス不均衡」を測定・評価する際の共通言語を提供します。 Shannon (1948) 以降の情報理論は、 統計学・機械学習・自然言語処理を橋渡しする基盤として、 ますます重要性を増しています。
「クラス不均衡」は単なる理論ではなく、 実産業の現場で日常的に使われている技術です。 5 つの典型的な応用シナリオを示します。
リスク評価・ポートフォリオ最適化・不正検知の各場面で「クラス不均衡」が使われます。 例えば、 取引データ数千万件から異常パターンを抽出する際、 本用語の概念が中核を担います。 規制対応(バーゼル II/III)でも統計的概念の正確な理解が要求されます。
臨床試験の設計・薬効評価・画像診断 AI・電子カルテ解析で「クラス不均衡」が活躍します。 p 値ハッキングなどの統計的不適切利用を避けるために、 概念の正確な理解が患者の生命に直結する責任を伴います。 米 FDA・欧 EMA・日本 PMDA の各規制下でも統計手法は厳格に審査されます。
A/B テスト・LTV 予測・推薦システム・広告クリック率予測など、 デジタルマーケティングの中核技術として「クラス不均衡」が使われています。 1% の改善が年商で億単位の差を生む業界なので、 統計的有意性と実用的有意性の区別が重要です。
品質管理(SPC)、 異常検知、 需要予測、 在庫最適化、 予知保全で「クラス不均衡」が使われます。 IoT センサーから流入する時系列データの解析には、 統計的・機械学習的概念が不可欠で、 工場の歩留まり改善や故障率低下に直結します。
政策効果評価(RCT、 自然実験、 差分の差分法)、 教育研究、 社会調査の解析、 公的統計(SSDSE のような)など、 政策決定のための分析基盤として「クラス不均衡」が活躍します。 政策の効果検証は、 統計的概念の理解が市民生活に直接影響する重要分野です。
データサイエンスは強力な道具であり、 「クラス不均衡」のような手法も誤用すれば社会に害を与える可能性があります。 以下の倫理的論点は、 実務で常に意識すべきです。
🌍 持続可能なデータサイエンスへ:「クラス不均衡」を含む全ての分析が、 社会の利益と持続可能性に貢献するように設計・運用すべきです。 技術的可能性 ≠ 社会的妥当性。 倫理的判断は技術選択の最初に来るべきテーマです。
「クラス不均衡」を含む「機械学習」カテゴリは、 急速に進化しています。 直近の研究動向を 5 つピックアップしました。 興味があるテーマは arXiv で「Class Imbalance」「機械学習」をキーワード検索すると最新論文に辿れます。
これらのテーマは互いに関連しているので、 1 つに興味を持ったら隣接領域に展開していくと知識ネットワークが広がります。
「クラス不均衡」は分類モデルが多数派に偏る病で、 上流のサンプリング戦略と下流の評価指標選択を同時に変えないと「正解率は高いが少数派は全滅」になる。
上流の SMOTE/undersampling で分布を整え、 並列の cost-sensitive 学習と比較し、 下流で F1 や PR-AUC を評価指標に採用すれば、 少数派が重要な不正検知や疾患予測でも実用品質の分類が組める。
「クラス不均衡」を実際に使うとき、 何をどう選ぶかを順に判断する。 上から順に答えていくと、 使うべき手法と評価の仕方が決まる。
class_weight='balanced' を試す。 データを増やさないので漏れが起きにくい。 SMOTE を使うなら、 必ず学習用データを分割したあとに適用する。 分割前に行うと検証側に情報が漏れる。不均衡そのものは問題ではなく、 「少数クラスを当てたいのに指標が多数クラスを見ている」ことが問題。 指標を直せば解決する場合も多い。
本ページはここまで、 与えられた不均衡にどう対処するか(SMOTE・class_weight・指標選択)を扱ってきた。 この深化セクションでは一歩手前に戻り、 その不均衡はどこから来たのかを問う。 実は多くの実務タスクで、 クラス比は自然現象ではなく分析者自身のラベル設計の産物であり、 しかも時間とともに動く。 SSDSE-B-2026 の実測値でこの 2 点を確認する。
SSDSE-B-2026 の 2023 年・47 都道府県で、 「人口移動」に関する二値ラベルを 3 通り設計してみる(いずれも実測値):
| ラベル設計(2023 年) | 陽性:陰性 | 不均衡の性格 |
|---|---|---|
| 自然増減が正(A4101 出生数 − A4200 死亡数 > 0) | 0 : 47 | 陽性が 1 件もない退化ラベル。 分類問題として成立しない |
| 転入超過(A5101 転入者数 − A5102 転出者数 > 0) | 6 : 41 | 少数派 12.8%。 本ページで扱ってきた典型的不均衡(埼玉・千葉・東京・神奈川・大阪・福岡) |
| 転入超過率が −2‰ 超(人口千人当たり、 中央値 −1.95‰ 付近で分割) | 24 : 23 | ほぼ完全な均衡。 不均衡対策は一切不要になる |
同じ 47 行のデータから、 カットオフの置き方だけで「分類不能」「典型的不均衡」「完全均衡」の 3 つの世界が生まれる。 つまり「不均衡にどう対処するか」の前に「そのカットオフは業務的に正当か」を問うべきで、 均衡するようにカットオフを動かせるなら(例:『上位半分の県』を予測したいだけなら)、 SMOTE も class_weight もそもそも要らない。 逆に「転入超過という現象そのもの」に意味があるなら、 6:41 は動かせない本質的な不均衡であり、 本ページの対策群が正当化される。
「転入超過県」の数を SSDSE-B-2026 の全 12 年分で数えると(実測値):
| 年 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | 2023 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 転入超過県数 | 11 | 9 | 7 | 8 | 7 | 7 | 7 | 8 | 7 | 11 | 9 | 6 |
少数派の県数は 6〜11 の間で揺れ、 少数派比率は 12.8%〜23.4% とほぼ 2 倍の幅で変動している(コロナ禍の 2021 年に 11 県へ跳ね、 2023 年に最少の 6 県)。 ここに重大な落とし穴がある:
オーバー/アンダーサンプリングで訓練データを 50:50 に均すと、 モデルの predict_proba は「均された世界」の確率になり、 実世界の確率としてはそのまま使えない。 較正曲線を引き直さなくても、 ベイズの定理から解析的に補正できる:
$$ p = \frac{p_s \cdot \dfrac{\pi}{\pi_s}}{p_s \cdot \dfrac{\pi}{\pi_s} + (1 - p_s) \cdot \dfrac{1-\pi}{1-\pi_s}} $$
ここで $p_s$ はリサンプリング後データで学習したモデルの出力確率、 $\pi_s$ は訓練時の陽性率(50:50 なら 0.5)、 $\pi$ は実世界の陽性率。 2023 年の転入超過タスクなら $\pi = 6/47 \approx 0.128$ なので、 均衡データで学習したモデルが $p_s = 0.7$ を出しても、 補正後の実確率は $p \approx 0.254$ にすぎない。 $p_s = 0.5$(モデルが五分五分と判断)なら補正後は $p \approx 0.128$、 つまりベースレートそのものに戻る。 この式は「リサンプリングは決定境界の学習を助けるだけで、 確率の意味は事前確率の付け替えで歪む」ことを定量的に示しており、 確率を業務判断(期待コスト計算など)に使うなら補正が必須になる。 なお class_weight による重み付けも同型の歪みを生むため、 同じ補正の考え方が適用できる。 さらに極端な不均衡(1:1000 超)では、 そもそも陽性ラベルの定義自体が不安定になり、 異常検知(One-Class SVM・Isolation Forest)へ問題を組み替える判断も視野に入る。