3 가지 분포를 따르는 젠슨-섀넌 분산을 계산하고 싶습니다. 아래 계산이 정확합니까? ( 위키피디아의 JSD 공식을 따랐 습니다 ) :
P1 a:1/2 b:1/2 c:0
P2 a:0 b:1/10 c:9/10
P3 a:1/3 b:1/3 c:1/3
All distributions have equal weights, ie 1/3.
JSD(P1, P2, P3) = H[(1/6, 1/6, 0) + (0, 1/30, 9/30) + (1/9,1/9,1/9)] -
[1/3*H[(1/2,1/2,0)] + 1/3*H[(0,1/10,9/10)] + 1/3*H[(1/3,1/3,1/3)]]
JSD(P1, P2, P3) = H[(1/6, 1/5, 9/30)] - [0 + 1/3*0.693 + 0] = 1.098-0.693 = 0.867
미리 감사드립니다 …
편집 여기에 이것을 계산하는 간단한 더러운 파이썬 코드가 있습니다.
def entropy(prob_dist, base=math.e):
return -sum([p * math.log(p,base) for p in prob_dist if p != 0])
def jsd(prob_dists, base=math.e):
weight = 1/len(prob_dists) #all same weight
js_left = [0,0,0]
js_right = 0
for pd in prob_dists:
js_left[0] += pd[0]*weight
js_left[1] += pd[1]*weight
js_left[2] += pd[2]*weight
js_right += weight*entropy(pd,base)
return entropy(js_left)-js_right
usage: jsd([[1/2,1/2,0],[0,1/10,9/10],[1/3,1/3,1/3]])
답변
혼합물 분포에 실수가 있습니다. 합계가 1이
아닌 대신 이어야합니다 . 엔트로피 (자연 로그 포함)는 1.084503입니다. . 다른 엔트로피 용어가 잘못되었습니다.
하나의 계산에 대해 자세히 설명하겠습니다.
비슷한 방식으로 다른 용어는 0.325083 및 1.098612입니다. 따라서 최종 결과는 1.084503-(0.6931472 + 0.325083 + 1.098612) / 3 = 0.378889입니다.
답변
파이썬 :
import numpy as np
# @author: jonathanfriedman
def jsd(x,y): #Jensen-shannon divergence
import warnings
warnings.filterwarnings("ignore", category = RuntimeWarning)
x = np.array(x)
y = np.array(y)
d1 = x*np.log2(2*x/(x+y))
d2 = y*np.log2(2*y/(x+y))
d1[np.isnan(d1)] = 0
d2[np.isnan(d2)] = 0
d = 0.5*np.sum(d1+d2)
return d
jsd(np.array([0.5,0.5,0]),np.array([0,0.1,0.9]))
자바:
/**
* Returns the Jensen-Shannon divergence.
*/
public static double jensenShannonDivergence(final double[] p1,
final double[] p2) {
assert (p1.length == p2.length);
double[] average = new double[p1.length];
for (int i = 0; i < p1.length; ++i) {
average[i] += (p1[i] + p2[i]) / 2;
}
return (klDivergence(p1, average) + klDivergence(p2, average)) / 2;
}
public static final double log2 = Math.log(2);
/**
* Returns the KL divergence, K(p1 || p2).
*
* The log is w.r.t. base 2.
* <p>
* *Note*: If any value in <tt>p2</tt> is <tt>0.0</tt> then the
* KL-divergence is <tt>infinite</tt>. Limin changes it to zero instead of
* infinite.
*/
public static double klDivergence(final double[] p1, final double[] p2) {
double klDiv = 0.0;
for (int i = 0; i < p1.length; ++i) {
if (p1[i] == 0) {
continue;
}
if (p2[i] == 0.0) {
continue;
} // Limin
klDiv += p1[i] * Math.log(p1[i] / p2[i]);
}
return klDiv / log2; // moved this division out of the loop -DM
}
답변
위키 백과 참조를하셨습니다. 다음은 확률 분포가 여러 개인 Jensen-Shannon 분기에 대한 완벽한 표현입니다.
다중 분포 JS 분기의 수학적 표현없이 원래의 질문이 게시되어 제공된 계산을 이해하는 데 혼란을 초래했습니다. 또한 weight
곱셈에 적합한 가중치를 선택하는 방법에 혼동을 일으키는 용어 가 사용되었습니다. 위의 표현은 이러한 혼란을 명확하게합니다. 위의 표현에서 알 수 있듯이 가중치는 분포 수에 따라 자동으로 선택됩니다.
답변
두 개의 임의 길이 시퀀스의 JS 발산의 스칼라 버전 :
def entropy(dist: WrappedArray[Double]) = -(dist.filter(_ != 0.0).map(i => i * Math.log(i)).sum)
val jsDivergence = (dist1: WrappedArray[Double], dist2: WrappedArray[Double]) => {
val weights = 0.5 //since we are considering inly two sequences
val left = dist1.zip(dist2).map(x => x._1 * weights + x._2 * weights)
// println(left)
// println(entropy(left))
val right = (entropy(dist1) * weights) + (entropy(dist2) * weights)
// println(right)
entropy(left) - right
}
jsDivergence(Array(0.5,0.5,0), Array(0,0.1,0.9))
res0: Double = 0.557978817900054
질문 수정 섹션의 코드로이 답변을 확인하십시오.
jsd([np.array([0.5,0.5,0]), np.array([0,0.1,0.9])])
0.55797881790005399
답변
Wikipedia 공식을 기반으로 한 파이썬의 n 확률 분포에 대한 일반 버전 과 매개 변수 및 사용자 정의 로그베이스 로 가중치 벡터 ( pi )가 있는이 게시물의 주석 :
import numpy as np
from scipy.stats import entropy as H
def JSD(prob_distributions, weights, logbase=2):
# left term: entropy of mixture
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
# wentropies = np.dot(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