← 機械学習テキスト 一覧

🎓 レベル:発展 | 重要度:B(標準)

📎 前提:汎化と過学習・バイアスバリアンス分解 | 関連:訓練・検証・テストと交差検証

要点(BLUF)

1. i.i.d. 仮定の破綻 — なぜ汎化の保証が消えるか

機械学習の汎化(汎化と過学習・バイアスバリアンス分解汎化理論)は、ある暗黙の前提の上に立っています。それは 訓練データもテストデータも、同じ1つの分布 P(x,y)P(x,y) から独立同分布(i.i.d.)で取られている というものです。

私たちが最小化しているのは訓練データ上の経験リスク R^(f)=1ni(f(xi),yi)\hat{R}(f)=\frac1n\sum_i \ell(f(x_i),y_i) ですが、本当に小さくしたいのは未知データ上の期待リスク

R(f)=E(x,y)P[(f(x),y)]R(f)=\mathbb{E}_{(x,y)\sim P}\big[\ell(f(x),y)\big]

です。汎化バウンドが「訓練誤差が小さければテスト誤差も小さい」と言えるのは、R^\hat RRR が同じ PP の上で定義されているからです。

ところが本番運用では、データを生む分布が訓練時の PtrainP_{\text{train}} から運用時の PtestP_{\text{test}} へずれます。すると最小化していた R^\hat RPtrainP_{\text{train}} 上)と、本当に効く RRPtestP_{\text{test}} 上)が別物になります。

R^train(f) を小さくしてもRtest(f)=E(x,y)Ptest[(f(x),y)] は小さいと限らない\hat{R}_{\text{train}}(f)\ \text{を小さくしても}\quad R_{\text{test}}(f)=\mathbb{E}_{(x,y)\sim P_{\text{test}}}[\ell(f(x),y)]\ \text{は小さいと限らない}

要するに、汎化の保証は「同じ世界でテストする」という約束込みのもので、本番で世界が変われば保証ごと無効になります。これが分布シフトで本番モデルが劣化する根本理由です。

2. 分布シフトの3類型 — 同時分布のどこが変わるか

同時分布を2通りに分解します。

P(x,y)=P(yx)P(x)またはP(x,y)=P(xy)P(y)P(x,y)=P(y\mid x)\,P(x)\qquad\text{または}\qquad P(x,y)=P(x\mid y)\,P(y)

「どの因子が変わり、どの因子が不変か」で類型が決まります。

graph TD
  ROOT["分布シフト P(x,y) が train と test で異なる"] --> COV["共変量シフト covariate shift"]
  ROOT --> LBL["ラベルシフト label shift"]
  ROOT --> CON["概念ドリフト concept drift"]
  COV --> COVD["P(x) が変化 ・ P(y|x) は不変"]
  LBL --> LBLD["P(y) が変化 ・ P(x|y) は不変"]
  CON --> COND["P(y|x) 自体が変化 (関係が変わる)"]
  COVD --> COVF["対策:重要度重み付け w(x) で補正可"]
  LBLD --> LBLF["対策:w(y) で補正可(未ラベルでも推定可)"]
  COND --> CONF["対策:重み付けでは不可 → 再学習が必須"]

2-1. 共変量シフト(covariate shift)

入力の分布 P(x)P(x) だけが変わり、入力から正解への関係 P(yx)P(y\mid x) は不変 な場合です。

Ptrain(x)Ptest(x),Ptrain(yx)=Ptest(yx)P_{\text{train}}(x)\neq P_{\text{test}}(x),\qquad P_{\text{train}}(y\mid x)=P_{\text{test}}(y\mid x)

「入力ドリフト」「データドリフト」とも呼ばれます。たとえば、ある地域の患者で訓練した診断モデルを別の年齢構成の地域に出す場合、特徴量の分布は変わっても「この特徴ならこの病気」という関係自体は同じ、というイメージです。

2-2. ラベルシフト(label shift)

正解(クラス)の分布 P(y)P(y) が変わり、各クラスがどんな入力を生むか P(xy)P(x\mid y) は不変 な場合です。

Ptrain(y)Ptest(y),Ptrain(xy)=Ptest(xy)P_{\text{train}}(y)\neq P_{\text{test}}(y),\qquad P_{\text{train}}(x\mid y)=P_{\text{test}}(x\mid y)

「事前確率シフト」とも呼ばれます。原因(病気 yy)が結果(症状 xx)を生むような因果構造で自然に起きます。たとえば流行で病気の有病率 P(y)P(y) が跳ね上がっても、「その病気が出す症状の出方 P(xy)P(x\mid y)」は変わらない、という状況です。

2-3. 概念ドリフト(concept drift)

入力と正解の関係 P(yx)P(y\mid x) そのものが変わる 場合です。これが一番厄介です。

Ptrain(yx)Ptest(yx)P_{\text{train}}(y\mid x)\neq P_{\text{test}}(y\mid x)

「同じ入力に対する正解が、時間とともに変わってしまう」状態です。スパム判定で、スパマーが手口を変えれば「同じ文面」が以前は正常・今はスパム、と正解ラベルが反転します。需要予測で消費者の嗜好が変われば、同じ条件でも売れ行きが変わります。入力 xx の見た目は変わらないのに正解が変わるため、入力を観察するだけの監視では気づきにくいのが特徴です。

用語の注意:実務記事では「データドリフト=P(x)P(x) の変化(共変量シフト)」「概念ドリフト=P(yx)P(y\mid x) の変化」と対比されることが多いです。一方で学術的には「concept drift」を同時分布 P(x,y)P(x,y) の任意の変化を指す広義語として使う流派もあります。文脈で広狭が変わる点に注意してください。本ノートでは上の狭義(P(yx)P(y\mid x) の変化)で使います。

3. なぜ劣化するか — 重み付けの理論的裏付け

3-1. 期待リスクのずれと重要度重み付け

共変量シフトを例に、なぜ素朴な訓練がバイアスを持つのか、そしてどう直すのかを数式で見ます。本当に小さくしたいのはテスト分布上の期待リスクです。

Rtest(f)=Exptest[(f(x),y)]=(f(x),y)ptest(x)dxR_{\text{test}}(f)=\mathbb{E}_{x\sim p_{\text{test}}}\big[\ell(f(x),y)\big]=\int \ell(f(x),y)\,p_{\text{test}}(x)\,dx

ここで ptest(x)/ptrain(x)p_{\text{test}}(x)/p_{\text{train}}(x) を掛けて割る、重要度サンプリングの恒等式を使います。

Rtest(f)=(f(x),y)ptest(x)ptrain(x)ptrain(x)dx=Exptrain ⁣[ptest(x)ptrain(x)=w(x)(f(x),y)]R_{\text{test}}(f)=\int \ell(f(x),y)\,\frac{p_{\text{test}}(x)}{p_{\text{train}}(x)}\,p_{\text{train}}(x)\,dx =\mathbb{E}_{x\sim p_{\text{train}}}\!\left[\,\underbrace{\frac{p_{\text{test}}(x)}{p_{\text{train}}(x)}}_{=\,w(x)}\,\ell(f(x),y)\right]

つまり、訓練分布から取った各サンプルに重み w(x)=ptest(x)/ptrain(x)w(x)=p_{\text{test}}(x)/p_{\text{train}}(x) を掛けて平均すれば、テスト分布上のリスクを訓練データだけで不偏に推定できるということです(この比は測度論的には Radon–Nikodym 微分にあたります)。これが 重要度重み付け(importance weighting) の出発点で、Shimodaira (2000) が共変量シフト補正として導入しました。実際の重み付き経験リスク最小化はこうなります。

f^=argminf 1ni=1nw(xi)(f(xi),yi)\hat f=\arg\min_f\ \frac1n\sum_{i=1}^n w(x_i)\,\ell(f(x_i),y_i)

要するに、テスト側で多く出る領域のサンプルを重く、ほとんど出ない領域を軽く扱い直すことで、訓練データだけで「テスト世界での平均誤差」を最小化します。

3-2. 重要な但し書き:モデルが正しければ重み付けは要らない

ここが発展トピックの肝です。共変量シフトがあっても、モデル族が真の P(yx)P(y\mid x) を表現できる(well-specified)なら、重み無しの素朴な最尤/ERM でも漸近的に正しい推定が得られますP(yx)P(y\mid x) は不変なので、xx をどこでサンプリングしようと、十分なデータがあれば正しい条件付き分布に収束するからです。

重要度重み付けが効くのは、モデルが誤特定(misspecified)されているときです。表現力が足りないと、モデルは「データが密に来る領域」を優先して当てにいきます。訓練とテストで密な領域がずれていると、訓練で重視した領域とテストで効く領域が食い違い、バイアスが生じます。重みは「テストで効く領域を重視し直す」ことでこのバイアスを補正します。

flowchart TD
  Q["共変量シフトがある"] --> SPEC{"モデルは真の P(y|x) を表現できるか"}
  SPEC -->|"できる(well-specified)"| OK["素朴な学習でも漸近的に正しい → 重み付け不要"]
  SPEC -->|"できない(misspecified)"| WT["どの領域を優先するかでバイアス → 重要度重み付けが有効"]

要するに、「分布がずれたら必ず重み付けすべき」ではありません。重み付けは万能薬ではなく、有限データ・有限表現力という現実の不完全さを補正する道具です(しかも重みの分散が大きいと推定が不安定になる副作用もあります)。

3-3. ラベルシフトの補正と検知

ラベルシフトでは、重みは入力ではなく ラベル に付きます。

w(y)=ptest(y)ptrain(y)w(y)=\frac{p_{\text{test}}(y)}{p_{\text{train}}(y)}

問題は、運用時にはラベルが手に入らないことです。ラベル無しのテスト入力だけから ptest(y)p_{\text{test}}(y) をどう推定するか が鍵になります。これを解くのが BBSE(Black Box Shift Estimation, Lipton et al. 2018) です。

考え方はシンプルです。既存分類器 f^\hat f の予測 y^\hat y について、訓練データで作った混同行列 Cy^,y=ptrain(f^(x)=y^y)C_{\hat y,y}=p_{\text{train}}(\hat f(x)=\hat y\mid y) は、ラベルシフト下では運用時にも不変です(P(xy)P(x\mid y) が不変だから予測の出方も不変)。すると運用時の予測分布は

ptest(y^)=yptrain(f^(x)=y^y)混同行列ptest(y)p_{\text{test}}(\hat y)=\sum_y \underbrace{p_{\text{train}}(\hat f(x)=\hat y\mid y)}_{\text{混同行列}}\,p_{\text{test}}(y)

という線形関係になります。左辺(運用時の予測分布)は未ラベルでも数えられ、混同行列は訓練データから作れます。よって 混同行列が可逆なら、この連立方程式を解いて ptest(y)p_{\text{test}}(y) を逆算できる わけです。分類器が多少不正確・未校正でも、混同行列が可逆でありさえすれば一貫性が保証される、という点が実務的に強力です。

4. 検知 — シフトをどう見つけるか

シフト対策の前段は「気づくこと」です。手段は大きく2つです(MLOpsと実験管理 の監視の一部)。

監視対象何を見るか長所 / 短所
性能監視本番の予測精度・損失(正解ラベルが取れる場合)最も直接的。だが正解ラベルが遅延 or 入手不能だと使えない
入力分布の監視特徴量分布 P(x)P(x) の訓練 vs 本番の距離ラベル不要で常時可能。だが概念ドリフトは P(x)P(x) 不変なので検知できない

入力分布の距離としてよく使うのは、

flowchart LR
  IN["本番データが届く"] --> M1{"正解ラベルは取れるか"}
  M1 -->|"取れる"| PERF["性能監視(精度・損失の劣化を検知)"]
  M1 -->|"取れない / 遅延"| DIST["入力分布の距離を監視(KS ・ PSI ・ KL)"]
  PERF --> ALERT{"閾値を超えたか"}
  DIST --> ALERT
  ALERT -->|"超えた"| ACT["対策:重み付け / 再学習 / ドメイン適応"]
  ALERT -->|"範囲内"| KEEP["現行モデルを継続"]

要するに、ラベルが取れるなら性能を直接見るのが一番確実で、取れないなら入力分布の距離で代理監視します。ただし入力分布の監視だけでは概念ドリフトを取りこぼすため、可能なら遅延ラベルでの性能再評価を組み合わせます。

5. 対策 — 補正・再学習・ドメイン適応

シフトの型に応じて手段が変わります。型を取り違えると対策が空振りします。

何が変わった主な対策
共変量シフトP(x)P(x)重要度重み付け w(x)=ptest(x)/ptrain(x)w(x)=p_{\text{test}}(x)/p_{\text{train}}(x) で再重み付け / 再学習
ラベルシフトP(y)P(y)w(y)w(y) による再重み付け(BBSEで ptest(y)p_{\text{test}}(y) を推定)・事後確率の補正
概念ドリフトP(yx)P(y\mid x)再学習が必須(新しい正解ラベル付きデータで学び直す)

主要な打ち手は次の通りです。

⚠️ よくある誤解・落とし穴

対応するシミュレーション

simulations/distribution_shift.py:入力分布が訓練(x1x\approx1)と運用(x3x\approx3)でずれた共変量シフトを作り、曲線の関係を直線で近似(モデル誤設定)した場合を見ます。重みなしの直線は訓練が密な領域の傾きに合わせてテスト領域で外れる一方、「テスト密度/訓練密度」を重要度重みにして当てはめ直すとテスト領域での誤差が大きく下がる(テストMSE 4.4→0.85)ことを可視化します。関係 p(yx)p(y|x) 自体が変わる概念ドリフトでは再学習が要る点にも触れます。

共変量シフトと重要度重み付け

関連ノート