3 probluk dağılımlar için Jensen-Shannon sapma hesaplaması: Bu iyi mi?


12

3 dağılımı takip eden jensen-shannon ıraksamasını hesaplamak istiyorum. Aşağıdaki hesaplama doğru mu? ( JSD formülünü wikipedia'dan takip ettim ):

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

Şimdiden teşekkürler...

DÜZENLEME Bunu da hesaplayan bazı basit kirli Python kodu:

    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]])

2
Bu arada güzel Python kodu!
gui11aume

Yanıtlar:


13

(5/18,28/90,37/90)(1/6,1/5,9/30)

Bir hesaplamanın detayını vereceğim:

H(1/2,1/2,0)=1/2log(1/2)1/2log(1/2)+0=0.6931472

Benzer şekilde, diğer terimler 0.325083 ve 1.098612'dir. Böylece nihai sonuç 1.084503 - (0.6931472 + 0.325083 + 1.098612) / 3 = 0.378889


3
h <- function(x) {h <- function(x) {y <- x[x > 0]; -sum(y * log(y))}; jsd <- function(p,q) {h(q %*% p) - q %*% apply(p, 2, h)}pqp <- matrix(c(1/2,1/2,0, 0,1/10,9/10, 1/3,1/3,1/3), ncol=3, byrow=TRUE); q <- c(1/3,1/3,1/3); jsd(p,q)0.378889334/1551/9213/45714/453737/90

1
Çok kirli değil ... ;-)
gui11aume

4
(1) Matematiği yeniden yapın. (2) Entropi, tutarlı olduğunuz sürece, istediğiniz herhangi bir logaritma tabanı kullanılarak ölçülebilir. Doğal, ortak ve baz-2 kütükleri gelenekseldir. (3) Gerçekten dağılımlar ve ortalamaları arasında ortalama bir tutarsızlık var. Her dağılımı bir nokta olarak düşünüyorsanız, bir bulut oluştururlar. Bulutun merkezi ile noktaları arasındaki ortalama "mesafeye" bakıyorsunuz, tıpkı ortalama bir yarıçap gibi. Sezgisel olarak, bulutun boyutunu ölçer.
whuber

1
@Legend Sanırım haklısın. Bir sonucun başka bir şekilde ( Mathematica ile ) aldığım cevapla hemfikir olduğunu bulduktan sonra yeterince test yapmadım .
whuber

1
@dmck Benim yorumda gerçekten yazım hataları var: (1) ifade h <- function(x) {iki kez yapıştırıldı. Silin: diğer her şey teklif ettiğim sonuçları üretir ve üretir. Sonra değiştirmek apply(p, 2, h)için apply(p, 1, h)belirttiği gibi Legend tarafından yorumunda .
whuber

6

Python:

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]))

Java:

/**
 * 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
}

0

Wikipedia referansı verdiniz. Burada Jensen-Shannon ıraksaması için çoklu olasılık dağılımları ile tam bir ifade veriyorum:

JSmetric(p1,...,pm)=H(p1+...+pmm)j=1mH(pj)m

Orijinal soru, sağlanan hesaplamanın anlaşılmasında karışıklığa yol açan çoklu dağıtım JS sapmasının matematiksel ifadesi olmadan gönderilmiştir. Ayrıca, weighttekrar çarpma için uygun ağırlıkları nasıl seçtiğinizde karışıklığa neden olan terim kullanıldı. Yukarıdaki ifade bu karışıklıkları açıklığa kavuşturmaktadır. Yukarıdaki ifadeden anlaşılacağı üzere, ağırlıklar dağıtım sayısına bağlı olarak otomatik olarak seçilir.


Bu, muhtemelen çok kısa olduğu için otomatik olarak düşük kalite olarak işaretleniyor. Şu anda standartlarımızın cevabından çok bir yorumdur. Üzerine genişleyebilir misin? Ayrıca bir yoruma dönüştürebiliriz.
gung - Monica'yı eski durumuna getirin

Bu bir cevaptan ziyade açıklayıcı bir yorum gibi geliyor. Bu soruda bir düzenleme olmalı mı?
gung - Monica'yı eski durumuna getirin

@ gung, cevabımı değiştirdi. Umarım yardımcı olur.
Merhaba Dünya

0

İki keyfi uzunluk dizisinin JS diverjansının Scala versiyonu:

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

Bu cevabı soru düzenleme bölümünde kodla birlikte kontrol edin:

jsd([np.array([0.5,0.5,0]), np.array([0,0.1,0.9])])
0.55797881790005399

0

Vikipedi formülüne dayalı python'da n olasılık dağılımları için genel bir versiyon ve parametre ve özel logbase olarak ağırlık vektörü ( pi ) içeren bu yazıdaki yorumlar :

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))

,546621319446

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.