PythonでのKL発散の計算


22

私はこれにかなり慣れていないため、この背後にある理論的概念を完全に理解しているとは言えません。Pythonの複数のポイントリスト間のKL Divergenceを計算しようとしています。私はhttp://scikit-learn.org/stable/modules/generated/sklearn.metrics.mutual_info_score.htmlを使用してこれを試みています。私が遭遇している問題は、返される値が2つの数値リスト(その1.3862943611198906)で同じであることです。私はここで何らかの理論上の間違いを犯していると感じていますが、それを見つけることはできません。

values1 = [1.346112,1.337432,1.246655]
values2 = [1.033836,1.082015,1.117323]
metrics.mutual_info_score(values1,values2)

それは私が実行しているものの例です-2つの入力に対して同じ出力を取得しているだけです。何かアドバイス/助けをいただければ幸いです!


KLすることで、カルバック・ライブラー情報量を意味するのですか?
Dawny33

はい、まさにそれ!
ナンダ

実行することによりsklearn.metrics.mutual_info_score([1.346112,1.337432,1.246655], [1.033836,1.082015,1.117323])、値を取得します1.0986122886681096
Dawny33

申し訳ありませんが、私は[1、1.346112,1.337432,1.246655]及び[1,1.033836,1.082015,1.117323]としてvalues2ひいては差分値としてvalues2としてvalues1を用いました。
ナンダ

回答:


18

まず第一に、純粋なカルバック・ライブラーの発散ではなく、クラスタリング結果を評価するための相互情報sklearn.metrics.mutual_info_score実装します!

これは、周辺分布の積分布と共同分布のカルバック・ライブラー発散に等しい。

KLの発散(およびその他のそのような尺度)は、入力データの合計が1であると想定しています。そうでなければ、それらは適切な確率分布ではありません。データの合計が1でない場合、通常はKL発散を使用するのが適切ではない可能性があります!(場合によっては、データが欠落している場合など、合計が1未満であることが許容される場合があります。)

また、底2の対数を使用するのが一般的であることに注意してください。これは一定のスケーリング係数の差をもたらすだけですが、2を底とする対数は解釈が容易で、より直感的なスケール(0からlog2 = 0.69314ではなく0から1 ...、natの代わりにビットで情報を測定)を持ちます。

> sklearn.metrics.mutual_info_score([0,1],[1,0])
0.69314718055994529

明らかなように、sklearnのMI結果はlog2ではなく自然対数を使用してスケーリングされます。上記で説明したように、これは残念な選択です。

残念ながら、Kullback-Leiblerの発散は脆弱です。上記の例ではKL([0,1],[1,0])、明確に定義されていません。ゼロによる除算を引き起こし、無限になりがちです。また、非対称です。


scipy.stats.entropy使用すると、確率が1に正規化されることに注意してください。ドキュメント(scipy.github.io/devdocs/generated/scipy.stats.entropy.html)から:「このルーチンは、合計が1にならない場合、pkとqkを正規化します。」
イタマルマシュキン

15

Scipyのエントロピー関数は、それぞれ確率分布を表す2つのベクトルpおよびqを供給する場合、KL発散を計算します。2つのベクトルがpdfでない場合、最初に正規化されます。

相互情報はKL Divergenceに関連していますが、同じではありません

「この加重相互情報は、一部の入力に対して負の値をとることが知られている加重KL-ダイバージェンスの形式であり、加重相互情報も負の値をとる例があります。」


6

ScikitLearnの実装には確信がありませんが、PythonでのKLダイバージェンスの簡単な実装を次に示します。

import numpy as np

def KL(a, b):
    a = np.asarray(a, dtype=np.float)
    b = np.asarray(b, dtype=np.float)

    return np.sum(np.where(a != 0, a * np.log(a / b), 0))


values1 = [1.346112,1.337432,1.246655]
values2 = [1.033836,1.082015,1.117323]

print KL(values1, values2)

出力: 0.775279624079

一部のライブラリでは実装の競合が発生する可能性があるため、使用する前に必ずドキュメントを読んでください。


1
私もこれを試しましたが、これは負の値を返していましたが、有効な値ではないと思います。その後、少しの研究でこの結果に至りましたmathoverflow.net/questions/43849/…これは、入力がどのように確率分布でなければならないかについて語っています。それが私が間違いを犯した場所だと思います。
ナンダ

@Nandaリンクをありがとう。0.775279624079入力に対して鉱山が戻り、sklearnメトリックが戻り1.3862943611198906ます。まだ混乱しています!しかし、qnに従ってこれらの値のチェックをスクリプトに含めるようにしてください:)
Dawny33

1
あなたが言っていることがわかります!私は3つの異なる値を取得するために3つの異なる関数を試しましたが、それらの間で共通しているのは、結果が正しく「感じられない」ことだけです。入力値は間違いなく論理エラーなので、私のアプローチを完全に変更します!
ナンダ

今明らかだ@Nandaああ、:)説明するためのおかげで
Dawny33

2

このトリックにより条件付きコードが回避されるため、パフォーマンスが向上する場合があります。

import numpy as np

def KL(P,Q):
""" Epsilon is used here to avoid conditional code for
checking that neither P nor Q is equal to 0. """
     epsilon = 0.00001

     # You may want to instead make copies to avoid changing the np arrays.
     P = P+epsilon
     Q = Q+epsilon

     divergence = np.sum(P*np.log(P/Q))
     return divergence

# Should be normalized though
values1 = np.asarray([1.346112,1.337432,1.246655])
values2 = np.asarray([1.033836,1.082015,1.117323])

# Note slight difference in the final result compared to Dawny33
print KL(values1, values2) # 0.775278939433

ニースのトリック!時間ベンチマークでこれが他のソリューションとどのように比較されるかを見てみたいと思います。
確かに

0

分布(S)より3つの以下のサンプルを考えます。

values1 = np.asarray([1.3,1.3,1.2])
values2 = np.asarray([1.0,1.1,1.1])
values3 = np.array([1.8,0.7,1.7])

明らかに、values1とvalues2はより近いため、values3とsurprise比較した場合、エントロピーの測定値はより低いと予想されます。

from scipy.stats import entropy
print("\nIndividual Entropy\n")
print(entropy(values1))
print(entropy(values2))
print(entropy(values3))

print("\nPairwise Kullback Leibler divergence\n")
print(entropy(values1, qk=values2))
print(entropy(values1, qk=values3))
print(entropy(values2, qk=values3))

私たちは、次の出力を参照してください。

Individual Entropy

1.097913446793334
1.0976250611902076
1.0278436769863724 #<--- this one had the lowest, but doesn't mean much.

Pairwise Kullback Leibler divergence

0.002533297351606588
0.09053972625203921 #<-- makes sense
0.09397968199352116 #<-- makes sense

これは、values1とvalues3、values2とvalues3の間の値がvalues1からvalues2への変化よりも劇的に変化するため、理にかなっています。これはKL-Dとそのために利用できるパッケージを理解するための検証です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.