Python
機械学習
MachineLearning

生成モデルで語られる Kullback-Leibler を理解する

More than 1 year has passed since last update.

はじめに

最近,VAE(Variational Autoencoder)やGAN(Generative Adversarial Networks)等のモデルを学んでいますが,これら生成モデルの話で必ず登場するのが Kullback-Leibler divergence という用語です.機械学習の用語において,「クロス・エントロピー」や「ドロップアウト」でしたら,英単語と物理,数学の知識でなんとなく想像がつきますが,「カルバック・ライブラー」という言葉を初めてみたとき,「???」となる方も多いのではないでしょうか? 特にボルツマンマシンから生成モデルに入ると,計算手法のCD(コントラスティブダイバージェンス)も新しい用語として登場して来るので,「生成モデルって...難しい」となりがちです.本記事では,図を見ながら "Kullback-Leibler" を理解していきたいと思います.

まず,日本語Wikipediaから引用させていただきます.

カルバック・ライブラー情報量(英語: Kullback–Leibler divergence、カルバック・ライブラー・ダイバージェンス)とは、確率論と情報理論における2つの確率分布の差異を計る尺度である。情報ダイバージェンス(Information divergence)、情報利得(Information gain)、相対エントロピー(Relative entropy)とも呼ばれる。

これだけで大体理解される方も多いのではと思います.カルバックライブラー情報量 = エントロピー となればコンセプトはさほど難しくはありません.また,こちらは雑学的な知識になりますが,

The Kullback–Leibler divergence was originally introduced by Solomon Kullback and Richard Leibler in 1951 as the directed divergence between two distributions; Kullback himself preferred the name discrimination information. (from en.wikipedia)

とのことで,これは,KullbackさんとLeiblerさんが考案したもののようです.
(統計モデリングで使われる,"AIC" も 赤池さん's Information Criterionですので,命名の経緯は似ていますね.)

定義としては,以下の式になります.

D_{KL} (P || Q) = \sum_i {P(i)\ log \frac{P(i)}{Q(i)}}

上式において,P(i)Q(i) は,2つの確率分布になります.式としては,それほど複雑のものではなく,また,"log" と分数式が出てくるところは,分類問題のクロス・エントロピーにかなり似ています.一方,「距離」の公理に照らし合わせてみると,「非負性」は満たしますが,「対称性」 $$ D_{KL}(P,Q)\ =\ D_{KL}(Q,P)$$ は満たしませんので,「距離」のコンセプトからは外れるようです.

簡単な分布でKL divergenceを求めてみる

簡単な分布,まず初めに平均の異なる2つの正規分布で Kullback-Leibler divergence(以下,DKL)を求めてみたいと思います.

Pythonで確率分布を計算するのに,scipy.stats の関数を用いました.また DKL の計算には scipy.stats.entropy() が使えることが分かりました.(参考,Scipyドキュメント:https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.entropy.html
前記のDKL定義式においてlog(自然対数)が入るので,log引数が0となるケースは注意が必要ですが,以下の例では特に問題はありませんでした.)

作成したコードが以下になります.

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

ndiv = 100
px =  np.linspace(norm.ppf(0.001), (norm.ppf(0.999) * 1.1), ndiv)

# Shifted normal distributions
fig, axes = plt.subplots(3, 3, figsize=(8, 8), sharey=True)
loc_opts = np.array([i * 0.1 for i in range(9)])
loc_opts = loc_opts.reshape([3, 3])

pdf_std = norm.pdf(px, loc=0., scale=1.)    # reference distribution
py_zero = np.zeros_like(px)                 # y=0.0 line

for i in range(3):
    for j in range(3):
        norm_ij = norm(loc=loc_opts[i, j], scale=1.)
        pdf_ij = norm_ij.pdf(px)

        ax_ij = axes[i, j]
        ax_ij.fill_between(px, pdf_std, py_zero, color='b', alpha=0.3)

        ax_ij.fill_between(px, pdf_ij, py_zero, color='r', alpha=0.3)
        ax_ij.set_xticklabels([])

        dkl_ij = entropy(pdf_std, pdf_ij)
        title_str = 'DKL={:>.3f}'.format(dkl_ij)
        ax_ij.set_title(title_str)

正規分布の平均値(中央値)を,[0.0, 0.1, ..., 0.8] と設定してDKLを計算しました.

Fig.1 Normal Distributions (shifted)
dkl_fig_1.png

基準となる「青」の分布とシフトした「赤」の分布の間の DKL を算出し,タイトルに示しています.シフト量=0 で DKL=0 となることが確認できました.また,2つの分布が重ならない部分(紫になっていない,青と赤のエリア)面積と DKL 値が正の相関を持っているのがよく分かるかと思います.2つの各分布は,プログラム内では離散値を持っているので,面積を求めてみましたが,線形の関係にはなりませんでした.

 s = 0.0000, DKL = 0.0000
 s = 1.2105, DKL = 0.0049
 s = 2.4190, DKL = 0.0197
 s = 3.6210, DKL = 0.0444
 s = 4.8121, DKL = 0.0790
 s = 5.9891, DKL = 0.1234
 s = 7.1529, DKL = 0.1776
 s = 8.2951, DKL = 0.2416
 s = 9.4114, DKL = 0.3154

式を見れば,両者が線形にならないのは当然といえます.(以下,擬似コードを示します.)

重ならないエリアの面積が,

S = sum(abs(pk - qk), axis=0) * x_width
#   pk - probability distribution 1
#   qk - probability distribution 2
#   x_width - width of discreted x points

であるのに対し,

DKLは,

DKL = sum(pk * log(pk / qk), axis=0)
#   pk - probability distribution 1
#   qk - probability distribution 2

ですので,SとDKLは線形の関係になりません.

次の例は,正規分布に skew をかけていった時の,DKLの変化を見たものです.

# Skewed normal distributions
fig, axes = plt.subplots(3, 3, figsize=(8, 8), sharey=True)
skew_opts = np.array([i * 0.1 for i in range(9)])
skew_opts = skew_opts.reshape([3, 3])

print('\n')
for i in range(3):
    for j in range(3):
        ax_ij = axes[i, j]
        ax_ij.fill_between(px, pdf_std, py_zero, color='b', alpha=0.3)

        pdf_ij = skewnorm.pdf(px, skew_opts[i, j], scale=1.)

        ax_ij.fill_between(px, pdf_ij, py_zero, color='r', alpha=0.3)
        ax_ij.set_xticklabels([])

        dkl_ij = entropy(pdf_std, pdf_ij)
        title_str = 'DKL={:>.3f}'.format(dkl_ij)
        ax_ij.set_title(title_str)

        # calculate area out-of-union
        s = sum([abs(y1 - y2) for y1, y2 in zip(pdf_std, pdf_ij)])
        print(' s = {:>.4f}, DKL = {:>.4f}'.format(s, dkl_ij))

Fig.2 Normal Distributions (skew added)
dkl_fig_2.png

こちらも,重ならないエリアが大きくなるにつれ,DKLの値が大きくなることが確認できました.

いかがでしょうか? このように見てみると Kullback Leibler divergenceが大分理解できた気がするのではないでしょうか? 要は,生成したモデルと入力データから導出されたモデルの「近さ」を測るための指標です.

「GANって学習が難しいから,Kullback Leiblerをよくモニターする必要があるよね?」とか,
「Kullback Leiblerが振動的になるから,Learning Rateを調整しようかな?」などと
つぶやくことで,「こいつ,生成モデルに詳しい」感を,かもしだすことができるかも知れません.

(プログラミング環境は次の通りです:Python 3.5.2, Scipy 0.19.0, Matplotlib 2.0.0 )

参考文献, web site