誰かが私を助けてくれることを望んでいた別の質問があります。
Jensen-Shannon-Divergenceを使用して、2つの確率分布間の類似性を測定しています。 2を底とする対数を使用する場合、類似性スコアは1から0の間にあるという意味で正しいように見えます。0は、分布が等しいことを意味します。
しかし、実際にどこかにエラーがあるかどうかはわかりません。誰かが「はい、それは正しい」または「いいえ、あなたは何か間違ったことをしました」と言うことができるのではないかと考えていました。
コードは次のとおりです。
from numpy import zeros, array
from math import sqrt, log
class JSD(object):
def __init__(self):
self.log2 = log(2)
def KL_divergence(self, p, q):
""" Compute KL divergence of two vectors, K(p || q)."""
return sum(p[x] * log((p[x]) / (q[x])) for x in range(len(p)) if p[x] != 0.0 or p[x] != 0)
def Jensen_Shannon_divergence(self, p, q):
""" Returns the Jensen-Shannon divergence. """
self.JSD = 0.0
weight = 0.5
average = zeros(len(p)) #Average
for x in range(len(p)):
average[x] = weight * p[x] + (1 - weight) * q[x]
self.JSD = (weight * self.KL_divergence(array(p), average)) + ((1 - weight) * self.KL_divergence(array(q), average))
return 1-(self.JSD/sqrt(2 * self.log2))
if __name__ == '__main__':
J = JSD()
p = [1.0/10, 9.0/10, 0]
q = [0, 1.0/10, 9.0/10]
print J.Jensen_Shannon_divergence(p, q)
問題は、たとえば2つのテキストドキュメントを比較したときにスコアが十分に高くないと感じていることです。しかし、これは純粋に主観的な感覚です。
いつものように、どんな助けでもありがたいです。
以下のscipyエントロピー呼び出しは、カルバック・ライブラー発散であることに注意してください。
参照: http://en.wikipedia.org/wiki/Jensen%E2%80%93Shannon_divergence
#!/usr/bin/env python
from scipy.stats import entropy
from numpy.linalg import norm
import numpy as np
def JSD(P, Q):
_P = P / norm(P, ord=1)
_Q = Q / norm(Q, ord=1)
_M = 0.5 * (_P + _Q)
return 0.5 * (entropy(_P, _M) + entropy(_Q, _M))
また、質問のテストケースが間違っているように見えることにも注意してください。 p分布の合計は1.0に加算されません。
参照: http://www.itl.nist.gov/div898/handbook/eda/section3/eda361.htm
既知の発散を持つ分布のデータを取得し、結果をそれらの既知の値と比較します。
ところで:KL_divergenceの合計は、次のように Zip組み込み関数 を使用して書き換えることができます。
sum(_p * log(_p / _q) for _p, _q in Zip(p, q) if _p != 0)
これにより、多くの「ノイズ」がなくなり、さらに「Pythonic」になります。 0.0
および0
との二重比較は必要ありません。
Jensen-Shannondistance( distance.jensenshannon
) Scipy 1.2
に含まれている場合、Jensen-Shannondivergenceは次のように取得できます。イェンセン・シャノン距離の二乗:
from scipy.spatial import distance
distance.jensenshannon([1.0/10, 9.0/10, 0], [0, 1.0/10, 9.0/10]) ** 2
# 0.5306056938642212
Pythonでのn個の確率分布の一般的なバージョン
import numpy as np
from scipy.stats import entropy as H
def JSD(prob_distributions, weights, logbase=2):
# left term: entropy of misture
wprobs = weights * prob_distributions
mixture = wprobs.sum(axis=0)
entropy_of_mixture = H(mixture, base=logbase)
# right term: sum of entropies
entropies = np.array([H(P_i, base=logbase) for P_i in prob_distributions])
wentropies = weights * entropies
sum_of_entropies = wentropies.sum()
divergence = entropy_of_mixture - sum_of_entropies
return(divergence)
# From the original example with three distributions:
P_1 = np.array([1/2, 1/2, 0])
P_2 = np.array([0, 1/10, 9/10])
P_3 = np.array([1/3, 1/3, 1/3])
prob_distributions = np.array([P_1, P_2, P_3])
n = len(prob_distributions)
weights = np.empty(n)
weights.fill(1/n)
print(JSD(prob_distributions, weights))
#0.546621319446
ウィキペディアの記事 の数学に明示的に従う:
def jsdiv(P, Q):
"""Compute the Jensen-Shannon divergence between two probability distributions.
Input
-----
P, Q : array-like
Probability distributions of equal length that sum to 1
"""
def _kldiv(A, B):
return np.sum([v for v in A * np.log2(A/B) if not np.isnan(v)])
P = np.array(P)
Q = np.array(Q)
M = 0.5 * (P + Q)
return 0.5 * (_kldiv(P, M) +_kldiv(Q, M))