KLダイバージェンスとJSダイバージェンス — 確率分布間の距離と機械学習への応用

「2つの確率分布がどれだけ違うか」を数値で表したい——機械学習や統計の現場でこの問いは頻繁に現れます。観測データの真の分布 $p$ に対してモデルが推定した $q$ がどれくらい外しているかを測りたい。変分推論で扱いやすい近似分布 $q_\phi$ を持ち出し、本当に欲しい事後分布 $p$ にどこまで近づけたかを定量化したい。GANでは生成器が作る画像分布が本物画像の分布にどれだけ寄り添えているかを学習中に評価したい——いずれも「分布と分布の比較」が中心の問いです。

これを素朴にユークリッド距離で測ろうとするとすぐに行き詰まります。確率分布は和や積分が1になる制約付きの関数で、サポート (値が非ゼロな領域) が違うと比較自体が定義しにくいからです。そこで情報理論の発想——「ある分布から生まれたデータを別の分布で説明しようとしたら、どれくらい余分な符号長が必要になるか」——をもとに設計されたのが、本記事の主役 KLダイバージェンス $D_{KL}(P\|Q)$ と、その対称化版 JSダイバージェンス $D_{JS}(P\|Q)$ です。

これらは現代の機械学習の至るところに登場します。VAEの損失関数に現れる潜在変数の正則化項、t-SNEが高次元と低次元の点の近さを揃える目的関数、PPOで方策更新を安定化させるKL制約、初期GAN損失と等価な式に現れるJSダイバージェンス——いずれも「分布を分布として比較する道具」がアルゴリズムの心臓部に座っています。

本記事の内容

  • KLダイバージェンスの直感的意味と厳密な定義、そしてギブスの不等式 ($D_{KL}\geq 0$) の証明
  • 非対称性の意味——forward KL と reverse KL で生まれる mode-covering / mode-seeking の挙動の違い
  • 対称化したJSダイバージェンスの定義と、平方根で本物の距離 (metric) になる事実
  • Wasserstein距離との比較とWGANが生まれた歴史的経緯
  • Pythonでの計算 (scipy / 自前実装) と、forward/reverse KLでガウス近似が変わる様子の可視化
  • VAEのELBO、GANのJSD最小化、t-SNE、PPOへの応用と実装スケッチ

KLダイバージェンスとJSダイバージェンスの概念模式図

左図は KL が「真の分布 P から見てモデル分布 Q の悪さを測る」非対称な操作であることを示しています。右図は JS が「中点分布 M = (P+Q)/2 を仲立ちにして両側から等しく比較する」対称な操作であることを示しています。この二つの測り方の違いが、機械学習における VAE と GAN で異なる挙動を生む根本原因となります。

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

なぜ「分布間の距離」が必要か

具体的な場面から始めましょう。コインを100回投げて表が60回出たとします。「表の確率は $0.5$」「$0.6$」というふたつの仮説 (分布) のどちらが「データから見て真の分布に近い」と言えるでしょうか。素朴には各 $x$ について $|p(x) – q(x)|$ を全部足す 全変動距離 (total variation) を使う手もありますが、これは尤度比 $p(x)/q(x)$ の情報を捨ててしまいます。尤度比は「あるデータ点 $x$ を観測したとき、$P$ と $Q$ のどちらが尤もらしいか」を直接表す量で、ベイズ推論や仮説検定の中核にあります。せっかくならこの尤度比をベースにした距離が欲しい。

もうひとつ、機械学習におけるモデル分布の評価を考えます。言語モデルは文 $x_1, x_2, \dots$ に対する確率 $q_\theta(x_1, x_2, \dots)$ を出力する装置で、真の分布を $p$ としたとき $q_\theta$ がよいモデルかどうかは「$p$ にどれくらい近いか」で決まります。$q_\theta(x)$ が大きいほどモデルは自信を持って予測できているわけで、この「自信」を平均化した量が負の対数尤度であり、これがKLダイバージェンスと直結します。

確率分布の差を測る道具に求められる性質は最低限以下です。

  1. 非負性 — 同じ分布同士で測れば $0$、違うほど大きくなる
  2. 尤度比を扱う — $p/q$ の対数を素直に使える形
  3. 加法性 — 独立な要素は独立に評価できる (符号化と相性がよい)

この3条件を満たす最も自然な定義として登場するのが、シャノンの情報量を起源にもつKLダイバージェンスです。

KLダイバージェンスの定義と性質

定義

離散確率分布 $P, Q$ (台 $\mathcal{X}$ 上で $p(x), q(x)$) に対する KLダイバージェンス は次で定義されます。

$$ D_{KL}(P \,\|\, Q) = \sum_{x \in \mathcal{X}} p(x) \log \frac{p(x)}{q(x)} $$

連続分布なら和を積分に置き換えて、

$$ D_{KL}(P \,\|\, Q) = \int p(x) \log \frac{p(x)}{q(x)}\, dx $$

です。記号は $D_{KL}(P\|Q)$ と縦棒2本で書き、「$P$ に対する $Q$ のKLダイバージェンス」あるいは「$P$ から $Q$ への」と読みます。順序が大事で、$P$ が「真の分布」、$Q$ が「比較先 (モデル分布)」というのが標準的な読みです。

このとき暗黙の約束として、$p(x) = 0$ ならその点は $0\log 0 = 0$ として除き、$p(x) > 0$ かつ $q(x) = 0$ となる点が一点でもあれば $D_{KL} = +\infty$ とします。つまり「$P$ がサポートする領域は $Q$ もサポートしなければならない」(絶対連続性 $P \ll Q$) が要請されます。これは後で見るVAEや変分推論で「事前分布のサポートを十分広く取る」べき理由とも結びつきます。

直感: 期待対数尤度比

定義式 $\sum_x p(x)\log\frac{p(x)}{q(x)} = \mathbb{E}_{x \sim P}\!\left[\log\frac{p(x)}{q(x)}\right]$ は、$P$ から $x$ をサンプリングしたときの対数尤度比の期待値です。$\log p(x)/q(x)$ は「観測 $x$ を $P$ で説明した尤度が $Q$ で説明した尤度の何倍か」を対数スケールで表したもの。$P = Q$ なら常に $0$、ずれるほど大きくなります。

符号理論の言葉だと符号長の差として読めます。$Q$ にもとづく最適符号で $x$ を表すと平均符号長は $\sum_x p(x)(-\log_2 q(x))$ ——これがクロスエントロピー $H(P, Q)$ です。一方 $P$ にもとづく最適符号なら $H(P)$ で済みます。KLダイバージェンスは

$$ D_{KL}(P\|Q) = H(P, Q) – H(P) $$

と書け、「真の分布 $P$ から出るデータを誤った $Q$ で符号化したときの余分なビット数の期待値」と解釈できます (相対エントロピーとも呼ばれます)。

ギブスの不等式: 非負性の証明

KLダイバージェンスは常に $D_{KL}(P\|Q) \geq 0$ であり、等号は $P = Q$ のときに限る——これが ギブスの不等式 です。この性質はEMアルゴリズムや変分推論の収束性を支える根幹なので、ここで証明しておきます。

証明の鍵は、対数関数の凹性から導かれる $\log x \leq x – 1$ (等号は $x = 1$) という不等式です。グラフで言うと、対数関数のグラフが直線 $y = x – 1$ を $x = 1$ で接した上で常に下にいる、という事実です。

ギブスの不等式 log x <= x-1 の図解

グラフを見ると、$\log x$ (青線) は常に直線 $x – 1$ (赤破線) 以下にあり、$x = 1$ でだけ接しています。オレンジの塗りつぶし領域が「余分さ」に対応し、この余分さが KL ダイバージェンスの非負性を保証します。等号条件 $P = Q$ はこの接点 $x = 1$ に対応します。これを符号反転すると

$$ -\log x \geq 1 – x \quad (x > 0) $$

になります。さて、$D_{KL}$ の定義式に $x = q(x)/p(x)$ を代入する形でこの不等式を使います。$p(x) > 0$ の点で

$$ \log \frac{p(x)}{q(x)} = -\log \frac{q(x)}{p(x)} \geq 1 – \frac{q(x)}{p(x)} $$

が成立します。両辺に $p(x) \geq 0$ を掛けて $x$ について総和すると、

$$ \sum_x p(x) \log \frac{p(x)}{q(x)} \geq \sum_x p(x) \left(1 – \frac{q(x)}{p(x)}\right) = \sum_x p(x) – \sum_x q(x) $$

となります。ここで $\sum_x p(x) = 1, \sum_x q(x) = 1$ (確率分布だから) を代入すると、右辺は $1 – 1 = 0$。したがって

$$ D_{KL}(P\|Q) = \sum_x p(x) \log \frac{p(x)}{q(x)} \geq 0 $$

が示せました。等号は $\log(p/q) = 1 – q/p$ がすべての $x$ で成立するとき、すなわち $q(x)/p(x) = 1$、つまり $P = Q$ のときに限られます。これでギブスの不等式が証明できました。

非対称性と「距離」と呼べない事情

定義式を見ればすぐ気づくとおり、KLダイバージェンスは

$$ D_{KL}(P \,\|\, Q) \neq D_{KL}(Q \,\|\, P) $$

と一般に非対称です。期待値を取る分布が違えば値も違うのは当然で、たとえば $P$ が広く、$Q$ が狭い場合、$P$ で平均する $D_{KL}(P\|Q)$ では「$P$ が値を持つのに $Q$ が薄い領域」が大きく罰されますが、逆向きでは別の領域が罰されます。距離 (metric) の公理 (対称性と三角不等式) を満たさないため、本来は「距離 (distance)」ではなく「ダイバージェンス (divergence)」と呼びます。

しかし、この非対称性こそが応用上で本質的な役割を果たします——分布を片方から見るか、もう片方から見るかで「何を許して何を許さないか」がガラッと変わる。次節で詳しく見ていきましょう。

KLダイバージェンスの非対称性: forwardとreverseの値の違い

左図では Q の平均 $\mu_Q$ を動かしたとき、forward KL (青) と reverse KL (赤破線) の値の推移が異なることがわかります。右図では Q の分散 $\sigma_Q$ を変化させた場合を示していて、forward KL は Q が狭くなる (小さい $\sigma$) 方向で急激に増加し、reverse KL は Q が広くなる (大きい $\sigma$) 方向で急激に増加します。これは後述する mode-covering / mode-seeking という挙動の違いに直結します。

ここまででKLの定義と非負性が押さえられました。次に、forward KLとreverse KLという「向き」の違いが、機械学習の挙動にどう効くかを見ていきます。

非対称性 — forward vs reverse

用語の整理

真の分布 (target) を $P$、近似分布 (model) を $Q$ とするとき、慣例的に次の二つを区別します。

  • Forward KL: $D_{KL}(P \,\|\, Q)$ — $P$ から見て $Q$ の悪さを測る。最尤推定はこれを最小化することと等価。
  • Reverse KL: $D_{KL}(Q \,\|\, P)$ — $Q$ から見て $P$ との違いを測る。変分推論 (ELBO最大化) はこれを最小化することと等価。

「forward」「reverse」の呼称は、$P$ を変えずに固定して $Q$ を動かす最適化を想定したときの引数の順序を基準にしています。どちらの最適化問題かによって、得られる $Q$ の形がガラリと変わります。

Mode-covering (zero-avoiding): forward KL の挙動

Forward KLの被積分項は $p(x) \log p(x)/q(x)$ で、$p(x) > 0$ の点で $q(x) \to 0$ になると $\log p/q \to +\infty$ と罰則が発散します。よって最適化は$P$ が値を持つ場所では $Q$ もちゃんと値を持たせる方向に動きます。

この性質は mode-covering または zero-avoiding と呼ばれます。$P$ が二峰性で $Q$ をひとつのガウスで近似する場合、forward KLは両方の山をカバーする幅広い分布を選びます。$P$ の細かい構造を表現できる柔軟なモデルクラスがあれば有用ですが、表現力が低いと「どちらの山にも属さない平均」を返してしまう恐れがあります。

最尤推定との関係も確かめておきます。サンプル $\{x_i\}_{i=1}^N$ が $P$ から得られていれば、

$$ D_{KL}(P\|Q_\theta) = \mathbb{E}_{x \sim P}[\log p(x)] – \mathbb{E}_{x \sim P}[\log q_\theta(x)] $$

の第一項は $\theta$ に依らない定数なので、$D_{KL}$ の最小化は $\mathbb{E}_{x \sim P}[\log q_\theta(x)]$ の最大化、すなわち対数尤度の最大化と同値です。サンプル平均で置き換えれば、最尤推定 (MLE) はforward KLを最小化していることになります。

Mode-seeking (zero-forcing): reverse KL の挙動

Reverse KLの被積分項は $q(x) \log q(x)/p(x)$ で、期待値は $Q$ について取ります。$p(x) = 0$ の領域でも $q(x) > 0$ だと $\log q/p \to +\infty$ になり罰則が無限大です。これを避けるには「$P$ がゼロの領域では $Q$ もゼロにする」必要があります。これは mode-seeking または zero-forcing と呼ばれます。

二峰性の $P$ をひとつのガウスで近似する場合、reverse KLは「どちらか一方の山にぴったり寄り添う」狭い分布を選びます。両方の山にまたがると、山と山の間の谷 (= $p(x)$ が小さい領域) に $q(x)$ がはみ出して大きなペナルティを受けるからです。

VAEや変分推論で使われるのは reverse KL で、これは計算上の理由 (後述のELBOで $Q$ について期待値が取れる) と、扱いやすい近似分布族 (例えばガウス) しか使えないという制約から、mode-seeking で「ひとつのモードを丁寧に近似する」方向に倒したいケースが多いからです。

forward KL (mode-covering) vs reverse KL (mode-seeking): 二峰性分布へのガウス近似

左図の forward KL 最小化では、平均が二つの山の中間付近に置かれ、分散が大きく取られることで「両方の山をカバー」しています。一方、右図の reverse KL 最小化 (左の山から初期化) では、左の山だけにぴったり寄り添う狭いガウスが得られています。この違いが VAE 生成モデルの「多様性 vs 鮮明さ」のトレードオフの理論的背景となります。

自然な疑問: 両方の良いところを取れないか

mode-covering は欲張りすぎ、mode-seeking は片側だけ。両方の良いところを取れる対称な分割は作れないか——これが次節の主役、JSダイバージェンスの問題意識です。

ここまでで「非対称性は欠点でも長所でもある」ことが理解できました。次に、対称化した上で本物の距離になる工夫を見ていきます。

JSダイバージェンスと真の距離化

定義

JSダイバージェンス (Jensen-Shannon divergence) は、$P$ と $Q$ の中点分布

$$ M = \tfrac{1}{2}(P + Q), \quad m(x) = \tfrac{1}{2}\big(p(x) + q(x)\big) $$

を仲立ちにして、両側からのKLを平均した量です。

$$ D_{JS}(P \,\|\, Q) = \tfrac{1}{2} D_{KL}(P \,\|\, M) + \tfrac{1}{2} D_{KL}(Q \,\|\, M) $$

定義から明らかに $D_{JS}(P\|Q) = D_{JS}(Q\|P)$ で対称です。また両方のKLが有限になるためのサポート条件はゆるく、$P$ と $Q$ が同じサポートを共有していなくても、中点 $M$ が両者を含むため $D_{KL}(P\|M), D_{KL}(Q\|M)$ はいずれも有限値で定義されます (具体的には $0 \leq D_{JS}(P\|Q) \leq \log 2$ の範囲に収まる)。

JSダイバージェンスの有界性 [0, log2] と中点分布の役割

左図は二つのガウス分布のピーク間距離が広がるにつれて JS ダイバージェンスが増加し、$\log 2 \approx 0.693$ に飽和していく様子を示しています。右図は「ほぼ同じ分布」「中程度の差」「完全分離」の三段階で中点分布 M がどのような形を取るかを示しています。M が二つの山の間で「橋渡し」の役割を果たすため、P と Q のサポートが重なっていなくても M を通じた KL が有限値に留まる仕組みが視覚的に理解できます。

エントロピーによる別表現

JSダイバージェンスは中点分布のエントロピーと個別エントロピーの差として、以下のようにも書けます。

$$ D_{JS}(P\|Q) = H(M) – \tfrac{1}{2}\big(H(P) + H(Q)\big) $$

ここで $H(P) = -\sum_x p(x)\log p(x)$ はシャノンエントロピーです。これは中点分布の不確実性 $H(M)$ から、個別分布の不確実性の平均を引いた量で、$P, Q$ が大きく違うほど中点が「ぼやけて」エントロピーが上がるので大きくなる、という直感的な意味を持ちます。

この表現は、JSダイバージェンスを相互情報量として書くこともできることを示しています。$P$ と $Q$ の混合に対し「どちらから来たか」を表すラベル $Y \in \{0, 1\}$ を等確率で振ると、$X | Y=0 \sim P$, $X | Y=1 \sim Q$ となり、

$$ I(X; Y) = H(X) – H(X|Y) = H(M) – \tfrac{1}{2}(H(P) + H(Q)) = D_{JS}(P\|Q) $$

が成り立ちます。つまり「$X$ を観測したとき、それが $P$ から来たか $Q$ から来たかをどれだけ判別できるか」がJSダイバージェンスの正体です。値が小さいほど判別不能で、$0$ ならば完全に区別できない (= $P = Q$)、最大値 $\log 2$ ならば完全に分離できる、というスケールになります。

平方根で metric になる

KLダイバージェンスは三角不等式を満たさず metric ではありませんでしたが、JSダイバージェンスの平方根 $d_{JS}(P, Q) = \sqrt{D_{JS}(P\|Q)}$ は metric になります。すなわち以下の3条件を満たします。

  1. 非負性と同一性: $d_{JS}(P, Q) \geq 0$、等号は $P = Q$ のときに限る
  2. 対称性: $d_{JS}(P, Q) = d_{JS}(Q, P)$
  3. 三角不等式: $d_{JS}(P, R) \leq d_{JS}(P, Q) + d_{JS}(Q, R)$

三角不等式の証明はEndresとSchindelin (2003)らによる非自明な結果で、JS距離がヒルベルト空間に等長埋め込み可能であることを使って示されます。実用上は「3つの分布の遠近関係でちゃんと三角不等式が成り立つ距離が欲しい」場面で有用で、クラスタリングや埋め込み空間構築での解析が楽になります。

JS距離 (平方根) が満たす三角不等式の確認

左図は三つの分布 A, B, C を可視化したもので、それぞれ異なる平均・分散を持ちます。右図の棒グラフでは $d_{JS}(A,C)$ (赤棒) が $d_{JS}(A,B) + d_{JS}(B,C)$ (オレンジ棒) より小さく、三角不等式が数値的に成立していることが確認できます。これが JS「ダイバージェンス」の平方根を取ることで初めて「距離 (metric)」として使える理由です。

GAN損失との関係

オリジナルGAN (Goodfellow et al. 2014) では、識別器 $D$ を最適化した後の生成器の目的関数が、データ分布 $P_{\text{data}}$ と生成分布 $P_g$ の間のJSダイバージェンス最小化と等価になります。最適識別器 $D^*(x) = p_{\text{data}}(x) / (p_{\text{data}}(x) + p_g(x))$ を代入すると、生成器の目的関数は定数を除いて $-2 D_{JS}(P_{\text{data}}\|P_g) + 2\log 2$ となるためです。

しかし、$P_{\text{data}}$ と $P_g$ のサポートが共通部分を持たない (高次元データではしばしば起こる) と $D_{JS}$ は定数 $\log 2$ で飽和し、生成器に勾配が伝わりません。これがGANの勾配消失問題の理論的根拠であり、次節のWasserstein距離 (WGAN) が提案された動機です。

Wasserstein距離との関係

定義と直感

Wasserstein距離 (Earth Mover’s distance) は、確率分布を「土の山」に見立て、$P$ という形の山を $Q$ という形の山に変形するのに必要な「最小の土の移動量 × 距離」として定義されます。1次元の場合、累積分布関数 $F_P, F_Q$ を使って簡潔に

$$ W_1(P, Q) = \int_{-\infty}^{\infty} \big| F_P(x) – F_Q(x) \big|\, dx $$

と書けます (1-Wasserstein距離)。より一般には、$P, Q$ の同時分布 (結合測度) $\gamma$ のすべてに対して

$$ W_p(P, Q) = \left( \inf_{\gamma \in \Pi(P, Q)} \int \|x – y\|^p\, d\gamma(x, y) \right)^{1/p} $$

で定義されます。ここで $\Pi(P, Q)$ は周辺分布が $P, Q$ となる結合測度の集合です。

KL/JSと違う本質的な性質

Wasserstein距離が機械学習で重宝される理由は、サポートが重ならない場合でも有限の差を返すことです。たとえば、$P$ が点 $x=0$ に集中したデルタ分布、$Q$ が点 $x=\theta$ に集中したデルタ分布だとすると、$\theta \neq 0$ である限り $P$ と $Q$ のサポートは交わりません。このとき

  • $D_{KL}(P\|Q) = \infty$ ($Q$ の台に $P$ の質量がない)
  • $D_{JS}(P\|Q) = \log 2$ ($\theta$ に依らない定数)
  • $W_1(P, Q) = |\theta|$ ($\theta$ について連続で微分可能)

となります。$\theta$ を変えてみると、KLは「無限大か0か」のステップ関数、JSは「定数か0か」のステップ関数なのに対して、Wassersteinだけが $\theta$ に連動する滑らかな関数です。サポートが共通しないことが当たり前の高次元データ (画像など) で生成モデルを学習するとき、この性質が決定的に効いてきます。

WGANという解決策

GANのJSD最小化の勾配消失問題を解くため、ArjovskyらはWasserstein距離を目的関数に使う WGAN を提案しました (2017)。直接の最適化は難しいので、双対表現 (Kantorovich-Rubinstein双対)

$$ W_1(P, Q) = \sup_{\|f\|_L \leq 1} \big( \mathbb{E}_{x \sim P}[f(x)] – \mathbb{E}_{x \sim Q}[f(x)] \big) $$

によりリプシッツ制約 $\|f\|_L \leq 1$ を満たす関数 $f$ (実装上はクリッピングや勾配ペナルティで実現) を識別器として最大化する形に落とします。詳細は WGAN — ワッサースタイン距離による安定学習 を参照してください。

KL/JS/Wasserstein距離: サポートが重ならない場合の挙動比較

左図では、二つのガウス分布の中心間距離 $\theta$ を増やしていくと、KL (青) は急激に発散し、JS (緑破線) は $\log 2$ の水平線に飽和していく様子が一目瞭然です。右図では JS と Wasserstein を比較しており、JS が飽和して勾配情報を失う一方で $W_1$ は $\theta$ に線形比例し続けます。この「勾配が途切れない」性質が WGAN の直接の動機です。

「KL → JS → Wasserstein」という進化は、サポートが交わらない場合でも勾配を伝えたいという生成モデル学習の現実的要請が距離設計を駆動した好例です。次節では、Pythonでforward/reverse KLの挙動の違いを目で確かめます。

Python実装 — 計算と可視化

離散分布での基本計算

まず離散分布での KL/JS の基本計算を、自前実装と scipy の両方で確かめます。

import numpy as np
from scipy.special import rel_entr, kl_div
from scipy.spatial.distance import jensenshannon

def kl_divergence_manual(p, q, eps=1e-12):
    """KL(p || q) の素朴実装 (離散)"""
    p = np.asarray(p, dtype=float)
    q = np.asarray(q, dtype=float)
    # 数値安定化のため微小値を足す (本来は p>0 の点でだけ和を取る)
    mask = p > 0
    return float(np.sum(p[mask] * np.log(p[mask] / (q[mask] + eps))))

def js_divergence_manual(p, q):
    """JS(p, q) の素朴実装"""
    p, q = np.asarray(p, dtype=float), np.asarray(q, dtype=float)
    m = 0.5 * (p + q)
    return 0.5 * kl_divergence_manual(p, m) + 0.5 * kl_divergence_manual(q, m)

# テスト用の2つのカテゴリカル分布
p = np.array([0.1, 0.4, 0.5])
q = np.array([0.2, 0.3, 0.5])

# scipy.special.rel_entr は要素ごとに p*log(p/q) を返す
kl_scipy = float(np.sum(rel_entr(p, q)))

# scipy.spatial.distance.jensenshannon は JS距離 (= sqrt(JS divergence)) を返す
# 底が e なので log2 とのスケール差に注意
js_dist_scipy = jensenshannon(p, q, base=np.e)

print(f"KL(P||Q) manual: {kl_divergence_manual(p, q):.6f}")
print(f"KL(P||Q) scipy : {kl_scipy:.6f}")
print(f"KL(Q||P) manual: {kl_divergence_manual(q, p):.6f}  (非対称!)")
print(f"JS divergence  : {js_divergence_manual(p, q):.6f}")
print(f"JS distance (scipy, =sqrt(JS)): {js_dist_scipy:.6f}")
print(f"  -> JS divergence from scipy : {js_dist_scipy**2:.6f}")

出力から、KLの自前実装と scipy の rel_entr の総和が浮動小数点誤差レベルで一致することがわかります。$D_{KL}(P\|Q)$ と $D_{KL}(Q\|P)$ は明らかに異なる値で、非対称性が数値でも体感できます。scipy.spatial.distance.jensenshannon が返すのはJS距離 (平方根を取った metric バージョン) で、JSダイバージェンスを得るには2乗する必要がある点に注意してください。

連続分布での解析的計算: 正規分布同士のKL

2つの一次元正規分布 $\mathcal{N}(\mu_1, \sigma_1^2)$ と $\mathcal{N}(\mu_2, \sigma_2^2)$ のKLダイバージェンスは解析的に書け、

$$ D_{KL}\big(\mathcal{N}(\mu_1, \sigma_1^2) \,\big\|\, \mathcal{N}(\mu_2, \sigma_2^2)\big) = \log\frac{\sigma_2}{\sigma_1} + \frac{\sigma_1^2 + (\mu_1 – \mu_2)^2}{2\sigma_2^2} – \frac{1}{2} $$

となります (詳細な導出は KLダイバージェンス(正規分布の場合) を参照)。この公式は VAE のELBOで「事後近似分布 $q(z|x) = \mathcal{N}(\mu_\phi, \sigma_\phi^2)$ と標準正規事前分布 $p(z) = \mathcal{N}(0, 1)$ のKL」を解析的に計算するために頻繁に使われる、超重要な式です。

import numpy as np

def kl_normal_analytic(mu1, sigma1, mu2, sigma2):
    """N(mu1, sigma1^2) と N(mu2, sigma2^2) の KLダイバージェンス"""
    return (np.log(sigma2 / sigma1)
            + (sigma1**2 + (mu1 - mu2)**2) / (2 * sigma2**2)
            - 0.5)

def kl_normal_numeric(mu1, sigma1, mu2, sigma2, x_range=(-15, 15), n=10000):
    """同じKLを数値積分でも計算 (検算用)"""
    from scipy.stats import norm
    xs = np.linspace(*x_range, n)
    p = norm.pdf(xs, mu1, sigma1)
    q = norm.pdf(xs, mu2, sigma2)
    mask = p > 1e-15
    return float(np.trapz(p[mask] * np.log(p[mask] / q[mask]), xs[mask]))

# サンプル: N(0, 1) と N(1, 2)
kl_a = kl_normal_analytic(0, 1, 1, 2)
kl_n = kl_normal_numeric(0, 1, 1, 2)
print(f"KL analytic: {kl_a:.6f}")
print(f"KL numeric : {kl_n:.6f}")
print(f"Diff       : {abs(kl_a - kl_n):.2e}")

# 標準正規同士は0
print(f"KL(N(0,1) || N(0,1)) = {kl_normal_analytic(0, 1, 0, 1):.6e}")

解析式と数値積分の結果が小数点以下数桁で一致することから、公式が正しく実装できていることが確認できます。また、同じ分布同士なら KLがほぼ厳密に $0$ になることもチェックできました。これでギブスの不等式が数値レベルでも確認できたことになります。

二峰性分布へのガウス近似: forward vs reverse

ここからが本記事の山場のひとつ、非対称性の可視化です。二峰性分布 $P$ をひとつのガウス $Q_\theta = \mathcal{N}(\mu, \sigma^2)$ で近似することを考え、forward KL と reverse KL のそれぞれを最小化したときに得られる $Q$ がどう違うかを見ます。

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize
from scipy.stats import norm

# 真の分布: 二峰性 (2つのガウスの混合)
def p_target(x):
    return 0.5 * norm.pdf(x, -3, 0.8) + 0.5 * norm.pdf(x, 3, 0.8)

xs = np.linspace(-8, 8, 2000)
dx = xs[1] - xs[0]
p_vals = p_target(xs)

# Forward KL: KL(P || Q) — Pで平均
def forward_kl(params):
    mu, log_sigma = params
    sigma = np.exp(log_sigma)
    q_vals = norm.pdf(xs, mu, sigma)
    mask = p_vals > 1e-12
    return np.sum(p_vals[mask] * np.log(p_vals[mask] / (q_vals[mask] + 1e-12))) * dx

# Reverse KL: KL(Q || P) — Qで平均
def reverse_kl(params):
    mu, log_sigma = params
    sigma = np.exp(log_sigma)
    q_vals = norm.pdf(xs, mu, sigma)
    mask = q_vals > 1e-12
    return np.sum(q_vals[mask] * np.log(q_vals[mask] / (p_vals[mask] + 1e-12))) * dx

# それぞれを最小化
res_fwd = minimize(forward_kl, x0=[0.0, 0.0], method='Nelder-Mead')
res_rev_left = minimize(reverse_kl, x0=[-3.0, 0.0], method='Nelder-Mead')
res_rev_right = minimize(reverse_kl, x0=[3.0, 0.0], method='Nelder-Mead')

mu_f, sig_f = res_fwd.x[0], np.exp(res_fwd.x[1])
mu_rL, sig_rL = res_rev_left.x[0], np.exp(res_rev_left.x[1])
mu_rR, sig_rR = res_rev_right.x[0], np.exp(res_rev_right.x[1])

print(f"Forward KL optimum  : mu={mu_f:.3f}, sigma={sig_f:.3f}")
print(f"Reverse KL (左初期値): mu={mu_rL:.3f}, sigma={sig_rL:.3f}")
print(f"Reverse KL (右初期値): mu={mu_rR:.3f}, sigma={sig_rR:.3f}")

# 可視化
fig, axes = plt.subplots(1, 2, figsize=(12, 5), sharey=True)
for ax, (mu, sig, title) in zip(
    axes,
    [(mu_f, sig_f, f"Forward KL  (mode-covering)\nmu={mu_f:.2f}, sigma={sig_f:.2f}"),
     (mu_rL, sig_rL, f"Reverse KL  (mode-seeking)\nmu={mu_rL:.2f}, sigma={sig_rL:.2f}")]):
    ax.plot(xs, p_vals, 'k-', lw=2, label='True P (bimodal)')
    ax.plot(xs, norm.pdf(xs, mu, sig), 'r--', lw=2, label='Approx Q (Gaussian)')
    ax.fill_between(xs, 0, norm.pdf(xs, mu, sig), color='red', alpha=0.15)
    ax.set_title(title)
    ax.set_xlabel('x'); ax.legend(); ax.grid(alpha=0.3)
axes[0].set_ylabel('density')
plt.tight_layout()
plt.savefig('forward_vs_reverse_kl.png', dpi=140, bbox_inches='tight')
plt.show()

この実験から、forward / reverse KLの根本的な違いがひと目でわかります。Forward KLを最小化したガウスは、平均が二峰の中央 (おそらく $0$ 付近) に、分散が大きく取られ、両方の山を「カバー」する形になります。一方、reverse KLを左の山近くから最適化すると左の山だけにぴったり寄り添う狭いガウスが、右から始めれば右の山に寄り添うガウスが得られます。初期値によって到達する局所最適が違う ことも、reverse KLの典型的な振る舞いです。$P$ が値を持たない谷に $Q$ がはみ出すと罰せられる、というzero-forcingの性質が、Qを片側の山に押し込めているわけです。

VAEのELBOで使われるのは reverse KL なので、学習結果として「事後分布の山のひとつに寄り添う」mode-seekingな振る舞いが出ます。これがVAEで生成画像が「ぼやけずに鮮明だが多様性が乏しい」傾向が出る一因とも言われます。

KL/JSのサーフェスプロット

パラメータを動かしたときのKL/JSの形をサーフェスで可視化します。

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm

# 固定: P = N(0, 1)
# 可変: Q = N(mu, sigma), mu と sigma を動かす
mus = np.linspace(-3, 3, 60)
sigmas = np.linspace(0.3, 3.0, 60)
MU, SG = np.meshgrid(mus, sigmas)

# 数値積分用の x グリッド
xs = np.linspace(-12, 12, 2000)
dx = xs[1] - xs[0]
p_vals = norm.pdf(xs, 0, 1)

KL_PQ = np.zeros_like(MU)
KL_QP = np.zeros_like(MU)
JS = np.zeros_like(MU)
for i in range(MU.shape[0]):
    for j in range(MU.shape[1]):
        mu, sg = MU[i, j], SG[i, j]
        q_vals = norm.pdf(xs, mu, sg)
        m_vals = 0.5 * (p_vals + q_vals)
        # KL(P||Q)
        mask = p_vals > 1e-12
        KL_PQ[i, j] = np.sum(p_vals[mask] * np.log(p_vals[mask] / (q_vals[mask] + 1e-12))) * dx
        # KL(Q||P)
        mask = q_vals > 1e-12
        KL_QP[i, j] = np.sum(q_vals[mask] * np.log(q_vals[mask] / (p_vals[mask] + 1e-12))) * dx
        # JS
        kl_pm = np.sum(p_vals * np.log((p_vals + 1e-12) / (m_vals + 1e-12))) * dx
        kl_qm = np.sum(q_vals * np.log((q_vals + 1e-12) / (m_vals + 1e-12))) * dx
        JS[i, j] = 0.5 * (kl_pm + kl_qm)

fig, axes = plt.subplots(1, 3, figsize=(15, 4.5))
titles = ['KL(P||Q)', 'KL(Q||P)', 'JS divergence']
for ax, Z, title in zip(axes, [KL_PQ, KL_QP, JS], titles):
    cs = ax.contourf(MU, SG, Z, levels=20, cmap='viridis')
    plt.colorbar(cs, ax=ax)
    ax.set_xlabel('mu (Q)')
    ax.set_ylabel('sigma (Q)')
    ax.set_title(title + '   (P = N(0,1) fixed)')
    # 真の値 (mu=0, sigma=1) をマーク
    ax.scatter([0], [1], color='red', s=80, marker='*', edgecolor='white', label='P=Q')
    ax.legend(loc='upper right')
plt.tight_layout()
plt.savefig('kl_js_surface.png', dpi=140, bbox_inches='tight')
plt.show()

3枚のコンター図を比較すると、いくつかの重要な特徴が読み取れます。第一に、すべて $(\mu, \sigma) = (0, 1)$ で最小値 $0$ を取っています (赤い星印)。これはギブスの不等式 $D_{KL} \geq 0$ と $D_{JS} \geq 0$ が等号となる点で、$P = Q$ の条件と一致します。

第二に、$D_{KL}(P\|Q)$ と $D_{KL}(Q\|P)$ では値の分布形状が違います。Forward $D_{KL}(P\|Q)$ は $\sigma$ が小さくなる方向 ($Q$ が鋭くなる方向) で急激に発散します——$P$ の裾が広いのに $Q$ がそこに密度をほとんど割り当てないと、強く罰せられるためです。Reverse $D_{KL}(Q\|P)$ は逆に $\sigma$ が大きくなる方向で罰則が増えます——$Q$ が広く取ると $P$ の裾の外に質量を持ち、$P=0$ の領域で $\log(q/p) \to \infty$ の罰を受けるためです。

第三に、$D_{JS}$ は対称な分布形をしており、$0 \leq D_{JS} \leq \log 2 \approx 0.693$ の有界範囲に収まっています。両方向のKLの中庸を取ったような滑らかな形をしているのが見て取れます。

P=N(0,1)固定でQのパラメータを変化させたKL/JSの等高線図

三枚の等高線図を比較すると、$(\mu_Q, \sigma_Q) = (0, 1)$ の赤い星印 ($P = Q$) がすべての図で最小値 $0$ を取ることが確認できます。左図 (forward KL) は $\sigma_Q$ が小さい方向 (Q が鋭くなる方向) で急峻に値が増加し、中図 (reverse KL) は $\sigma_Q$ が大きい方向で急峻に増加します。右図 (JS) は左右対称で、原点付近の滑らかな「お椀型」になっており、前述した有界性と対称性の両方を視覚的に確認できます。

簡易ELBO最大化のシミュレーション

VAE の心臓部にある ELBO (Evidence Lower BOund) を、シンプルな1次元の例で動かしてみます。観測モデル $p(x|z) = \mathcal{N}(z, 1)$、事前分布 $p(z) = \mathcal{N}(0, 1)$ として、観測 $x = 2$ が得られたときの事後分布を、ガウス近似 $q_\phi(z) = \mathcal{N}(\mu_\phi, \sigma_\phi^2)$ で求めることを考えます。

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
from scipy.optimize import minimize

# 観測モデル: p(x|z) = N(z, 1), 事前: p(z) = N(0, 1)
# 観測: x = 2 が得られた
x_obs = 2.0

# 真の事後 (今回は解析的に求まる): p(z|x) ∝ p(x|z)p(z)
# 両方ガウスの積なので、事後もガウス: 平均 x/2, 分散 1/2
mu_true, sigma_true = x_obs / 2, np.sqrt(0.5)
print(f"True posterior: N({mu_true:.3f}, {sigma_true**2:.3f})")

# ELBO = E_q[log p(x|z)] - KL(q || p(z))
# 第1項は再構成項, 第2項はKL正則化項 (reverse KL!)
def neg_elbo(params):
    mu_q, log_sigma_q = params
    sigma_q = np.exp(log_sigma_q)
    # E_q[log p(x|z)] = E_q[-0.5 (x - z)^2] + const
    #                 = -0.5 ((x - mu_q)^2 + sigma_q^2) + const
    recon = -0.5 * ((x_obs - mu_q)**2 + sigma_q**2)
    # KL(N(mu_q, sigma_q^2) || N(0, 1)) の解析式
    kl = 0.5 * (mu_q**2 + sigma_q**2 - 1 - 2 * log_sigma_q)
    return -(recon - kl)  # negative ELBO を最小化

res = minimize(neg_elbo, x0=[0.0, 0.0], method='Nelder-Mead')
mu_q, sigma_q = res.x[0], np.exp(res.x[1])
print(f"VI posterior  : N({mu_q:.3f}, {sigma_q**2:.3f})")
print(f"ELBO at opt   : {-res.fun:.4f}")

# 可視化
zs = np.linspace(-2, 4, 500)
plt.figure(figsize=(8, 5))
plt.plot(zs, norm.pdf(zs, 0, 1), 'g--', alpha=0.6, label='Prior p(z) = N(0,1)')
plt.plot(zs, norm.pdf(zs, mu_true, sigma_true), 'k-', lw=2, label=f'True posterior')
plt.plot(zs, norm.pdf(zs, mu_q, sigma_q), 'r:', lw=2.5,
         label=f'VI approx q(z)')
plt.axvline(x_obs, color='blue', alpha=0.4, ls='--', label=f'Observation x={x_obs}')
plt.xlabel('z'); plt.ylabel('density')
plt.title('Variational Inference (1D Gaussian example)')
plt.legend(); plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('elbo_vi.png', dpi=140, bbox_inches='tight')
plt.show()

この簡易VAEの結果から、変分推論が真の事後分布を綺麗に再現していることが確認できます。観測 $x=2$ によって事後分布の平均が事前 ($0$) と観測 ($2$) の中間 ($1$) にシフトし、観測情報により分散も狭く ($0.5$ に) なっています。コード中で計算しているKLの解析式 $\frac{1}{2}(\mu^2 + \sigma^2 – 1 – 2\log\sigma)$ は、VAE論文でKL正則化項として頻繁に出てくる式そのものです。これがreverse KL であることに注意してください——$q_\phi$ について期待値を取ることで、サンプリングが可能になり勾配計算が成立する、というのが変分推論で reverse KLを使う実装上の理由です。

VAEのELBO最大化による変分推論の収束

左図では VI 近似 (赤点線) が真の事後分布 (青実線) にほぼ重なり、変分推論がこの単純な設定では非常に精度良く動作することがわかります。右図の ELBO の収束曲線では、最適化の初期から急速に上昇し、数十ステップで安定することが確認できます。最終 ELBO 値と KL の寄与もグラフ内に表示されており、再構成項と正則化項のバランスが自動的に調整された結果です。

GAN風の収束シミュレーション

JSダイバージェンスでGAN損失をシミュレートし、生成分布が真の分布に収束する様子を確かめます。識別器/生成器のNN部分は解析的に置き換え、生成分布のパラメータを直接JSDの勾配で動かします。

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm

# 真のデータ分布: N(2, 0.5)
mu_data, sigma_data = 2.0, 0.5

# 生成分布: N(mu_g, sigma_g)
mu_g, sigma_g = -1.0, 1.5  # 適当な初期値

# JSダイバージェンスの数値計算
xs = np.linspace(-6, 8, 1500)
dx = xs[1] - xs[0]
p_data = norm.pdf(xs, mu_data, sigma_data)

def js_value(mu_g, sigma_g):
    p_g = norm.pdf(xs, mu_g, sigma_g)
    m = 0.5 * (p_data + p_g)
    kl_pm = np.sum(p_data * np.log((p_data + 1e-12) / (m + 1e-12))) * dx
    kl_qm = np.sum(p_g * np.log((p_g + 1e-12) / (m + 1e-12))) * dx
    return 0.5 * (kl_pm + kl_qm)

# 有限差分で勾配を計算しつつ GAN学習をシミュレーション
lr = 0.3
n_iter = 100
eps = 1e-3
history = []
for it in range(n_iter):
    js = js_value(mu_g, sigma_g)
    history.append((mu_g, sigma_g, js))
    # 勾配計算 (中心差分)
    g_mu = (js_value(mu_g + eps, sigma_g) - js_value(mu_g - eps, sigma_g)) / (2 * eps)
    g_sg = (js_value(mu_g, sigma_g + eps) - js_value(mu_g, sigma_g - eps)) / (2 * eps)
    # 更新 (sigma > 0 を保持)
    mu_g -= lr * g_mu
    sigma_g = max(0.05, sigma_g - lr * g_sg)

mus = [h[0] for h in history]
sgs = [h[1] for h in history]
jss = [h[2] for h in history]

fig, axes = plt.subplots(1, 2, figsize=(13, 5))
ax = axes[0]
ax.plot(jss, 'b-', lw=2)
ax.set_xlabel('iteration'); ax.set_ylabel('JS divergence')
ax.set_title('Convergence of generator (JSD minimization)')
ax.axhline(0, color='k', ls='--', alpha=0.3)
ax.grid(alpha=0.3)

ax = axes[1]
ax.plot(xs, p_data, 'k-', lw=2, label=f'Data N({mu_data},{sigma_data**2:.2f})')
for it_to_show, color in zip([0, 10, 30, n_iter - 1], ['red', 'orange', 'gold', 'green']):
    mu_v, sg_v, _ = history[it_to_show]
    ax.plot(xs, norm.pdf(xs, mu_v, sg_v), color=color, alpha=0.7,
            label=f'iter {it_to_show}: N({mu_v:.2f},{sg_v**2:.2f})')
ax.set_xlabel('x'); ax.set_ylabel('density')
ax.set_title('Generator distribution over iterations')
ax.legend(fontsize=9); ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('gan_jsd_convergence.png', dpi=140, bbox_inches='tight')
plt.show()
print(f"Final  : mu_g={mus[-1]:.3f}, sigma_g={sgs[-1]:.3f}, JS={jss[-1]:.4e}")
print(f"Target : mu  ={mu_data:.3f}, sigma  ={sigma_data:.3f}")

このシミュレーションから、GANの理想化された挙動 (JSDの勾配が正しく伝わる場合) が見て取れます。左図 (JSDの推移) では、JSダイバージェンスが単調に減少して最終的にゼロ近くに収束しています。右図 (生成分布の推移) では、初期はデータから大きく外れていた生成分布 $N(-1, 1.5^2)$ が、徐々にデータ分布 $N(2, 0.5^2)$ に重なっていく様子が観察できます。実際のGANではこの「JSDの勾配」を識別器ニューラルネットが暗黙に提供しますが、サポートが重ならない初期では勾配がほぼゼロになり、これが学習不安定性の原因になります。WGANはここをWasserstein距離で置き換えて解決した、というのが先ほどの歴史的経緯です。

JSD最小化によるGAN生成器の収束シミュレーション

左図の収束曲線では JS ダイバージェンスが急速に減少し、最終的に $10^{-5}$ 以下に達しています。右図では赤 (初期) → オレンジ → 黄 → 緑 (最終) の順に生成分布がデータ分布に近づく軌跡が追えます。この「支持が重なる状況では JSD 最小化がうまく機能する」という事実が、逆に「重ならない高次元画像では機能しない」というWGAN 提案の背景を浮き彫りにします。

ここまでで道具と直感が揃いました。最後に、KL/JSがどんな応用先で具体的に登場するかをまとめます。

応用 — VAE/GAN/t-SNE/PPO

VAE: ELBOとKL正則化

変分オートエンコーダ (VAE) は潜在変数モデル $p(x, z) = p(x|z)p(z)$ の対数尤度の下界 (ELBO) を最大化します。エンコーダ $q_\phi(z|x)$ がデコーダ $p_\theta(x|z)$ と事前 $p(z) = \mathcal{N}(0, I)$ に対して

$$ \mathcal{L}_{\text{ELBO}}(x; \theta, \phi) = \underbrace{\mathbb{E}_{q_\phi(z|x)}\big[\log p_\theta(x|z)\big]}_{\text{再構成項}} – \underbrace{D_{KL}\big(q_\phi(z|x) \,\|\, p(z)\big)}_{\text{KL正則化項}} $$

を最大化します。第二項のKLは reverse KLで、$q_\phi$ と $p(z)$ がともにガウスなら本記事の解析式で勾配が書けます。Mode-seekingな振る舞いを内包するため、VAE生成画像が「鮮明だが多様性が乏しい」傾向の理論的背景にもなります。詳細は VAEの実装チュートリアル を参照してください。

GAN: JSDの最小化と勾配消失

生成的敵対ネットワーク (GAN) の元祖はJSダイバージェンスの最小化を行う生成器学習で、識別器 $D$ が出す確率 $D(x) = p_{\text{data}}/(p_{\text{data}} + p_g)$ を最適化したとき、生成器の損失は $-2 D_{JS} + 2\log 2$ になります。理論的にはこれで $P_g \to P_{\text{data}}$ ですが、サポートが重ならない高次元画像の場合に $D_{JS}$ が定数 $\log 2$ で飽和して勾配が消える問題が常につきまといます。詳細は GANの理論WGAN を参照してください。

t-SNE: 高次元 → 低次元のKL最小化

t-SNE は高次元データを低次元に埋め込む際、点ペアの近さを表す確率分布を高次元と低次元で揃えるアプローチを取ります。高次元はガウス $p_{j|i} \propto \exp(-\|x_i – x_j\|^2 / 2\sigma_i^2)$、低次元は裾の重いStudent’s t $q_{ij} \propto (1 + \|y_i – y_j\|^2)^{-1}$ で表し、目的関数は対称化した

$$ C = D_{KL}(P \,\|\, Q) = \sum_{i \neq j} p_{ij} \log \frac{p_{ij}}{q_{ij}} $$

の最小化です。これも forward KL なので「$P$ で近い点ペアは $Q$ でも近くする (mode-covering的)」性質があり、t-SNEが局所構造を保つことに長ける理由になっています。

強化学習PPO: KL制約による安定化

PPO (Proximal Policy Optimization) は方策勾配法の中でも実用的に最強と言われるアルゴリズムで、新しい方策 $\pi_\theta$ が古い方策 $\pi_{\theta_{\text{old}}}$ から大きく離れすぎないように、損失関数にKLペナルティまたは確率比のクリッピングを入れます。KLペナルティ版では

$$ \mathcal{L}(\theta) = \mathbb{E}_t \left[ \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{\text{old}}}(a_t|s_t)} A_t \right] – \beta \, D_{KL}\big(\pi_{\theta_{\text{old}}} \,\|\, \pi_\theta\big) $$

のような形で、KL を「方策の変化の大きさ」のセーフティガードとして使います。これは「ステップサイズが大きすぎて学習が崩壊する」古典的な方策勾配法の弱点を克服した重要なアイデアで、PPOは現在の大規模言語モデルのRLHF (Reinforcement Learning from Human Feedback) でも事実上の標準アルゴリズムになっています。詳細は PPOアルゴリズム を参照してください。

これらの応用に共通するのは、「サンプリングできる分布から 扱いやすい分布族 (パラメトリック) への近似」あるいは「現状から大きく変えない安定化」のために、確率分布間の差を測る道具としてKLが選ばれているという点です。VAE/PPOではreverse KL、t-SNEではforward KL、GANではJSDが使われており、選択の背景には本記事で見た非対称性の意味と数値的扱いやすさの双方が効いています。

まとめ

本記事では、確率分布の差を測る2つの中心的な道具 KLダイバージェンス と JSダイバージェンス について、定義・性質・非対称性の意味・他の距離との関係・主要な応用までを解説しました。

  • KLダイバージェンス: $D_{KL}(P\|Q) = \sum_x p(x)\log\frac{p(x)}{q(x)}$ は「真の分布 $P$ で平均した対数尤度比」または「$Q$ で符号化したときの余分なビット数」。クロスエントロピー $H(P, Q)$ とエントロピー $H(P)$ の差 $H(P, Q) – H(P)$ に一致する
  • ギブスの不等式: $\log x \leq x – 1$ を $x = q/p$ に代入して総和することで $D_{KL} \geq 0$ (等号は $P = Q$) を証明
  • 非対称性: forward KL は mode-covering で最尤推定と等価。reverse KL は mode-seeking で変分推論で使う
  • JSダイバージェンス: 中点 $M = (P+Q)/2$ を介した両側KLの平均で対称、$[0, \log 2]$ に有界。平方根を取れば metric になる
  • Wasserstein距離との関係: サポートが重ならない分布同士で KL/JS が発散・飽和する問題に対し、Wasserstein距離はパラメータに対して連続。WGANはこの性質でGAN学習を安定化させた
  • 応用: VAE (reverse KL正則化)、GAN (JSD最小化)、t-SNE (forward KL最小化)、PPO (方策更新時のKL制約)

Python実装では、KL/JSの基本計算、正規分布での解析式と数値積分の一致、二峰性分布へのガウス近似でのforward/reverse KLの挙動の違い、サーフェスプロット、ELBOによる変分推論、JSDによるGAN収束シミュレーションまで一通り体験しました。とくに「同じ真の分布に対してforward/reverseで異なる近似が得られる」可視化は、変分推論やVAEを使うすべての人が押さえておきたい事実です。

次の一歩としては、Wasserstein距離 (最適輸送理論) を深く理解する、あるいは f-ダイバージェンスという KL/JS を統一的に扱う一般化を学ぶと、視野がさらに広がります。

次のステップとして、以下の記事も参考にしてください。

KLダイバージェンスを解説
KLダイバージェンス(カルバック・ライブラー情報量)の定義・性質・計算方法を基礎から解説します。本記事のKL比較・JS対称化の前提知識として。
画像なし
交差エントロピーとKLダイバージェンスの情報理論的意味
クロスエントロピーとKLダイバージェンスの関係を情報理論の観点から解説。H(P,Q) = H(P) + KL(P||Q) という接続式が機械学習の損失設計に与える意味を理解できます。