Penghitungan divergensi Jensen-Shannon untuk 3 distribusi prob: Apakah ini oke?


12

Saya ingin menghitung divergensi jensen-shannon untuknya mengikuti 3 distribusi. Apakah perhitungan di bawah ini benar? (Saya mengikuti rumus JSD dari wikipedia ):

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

Terima kasih sebelumnya...

Sunting Berikut adalah beberapa kode Python kotor sederhana yang menghitung ini juga:

    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
Ngomong-ngomong, kode Python!
gui11aume

Jawaban:


13

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

Saya akan memberikan detail dari satu perhitungan:

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

Dengan cara yang sama, ketentuan lainnya adalah 0,325083 dan 1.098612. Jadi hasil akhirnya adalah 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
Tidak begitu kotor ... ;-)
gui11aume

4
(1) Mengulang matematika. (2) Entropi dapat diukur menggunakan basis logaritma yang Anda suka, selama Anda konsisten. Log natural, umum, dan base-2 semuanya konvensional. (3) Ini benar-benar perbedaan yang berarti antara distribusi dan rata-rata mereka. Jika Anda menganggap setiap distribusi sebagai suatu titik, mereka membentuk awan. Anda sedang melihat "jarak" rata-rata antara pusat awan dan titik-titiknya, seperti jari-jari rata-rata. Secara intuitif, ini mengukur ukuran awan.
Whuber

1
@ Legenda, saya pikir Anda benar. Saya tidak menguji cukup setelah menemukan bahwa satu hasil setuju dengan jawaban yang saya peroleh dengan cara lain (dengan Mathematica ).
whuber

1
@ Dmck Memang ada kesalahan ketik dalam komentar saya: (1) frasa h <- function(x) {itu disisipkan dua kali. Hapus saja: semuanya berfungsi dan menghasilkan hasil yang saya kutip. Kemudian modifikasi apply(p, 2, h)ke apply(p, 1, h)seperti yang ditunjukkan dalam komentar oleh Legend .
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]))

Jawa:

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

Anda memberi referensi Wikipedia. Di sini saya memberikan ekspresi lengkap untuk divergensi Jensen-Shannon dengan distribusi probabilitas berganda:

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

Pertanyaan asli diposting tanpa ekspresi matematika divergensi JS multi-distribusi yang mengarah pada kebingungan dalam memahami perhitungan yang diberikan. Juga, istilah weightitu digunakan yang lagi-lagi menyebabkan kebingungan bahwa bagaimana Anda memilih bobot yang sesuai untuk perkalian. Ekspresi di atas menjelaskan kebingungan ini. Seperti yang jelas dari ungkapan di atas, bobot dipilih secara otomatis tergantung pada jumlah distribusi.


Ini secara otomatis ditandai sebagai kualitas rendah, mungkin karena sangat pendek. Saat ini lebih merupakan komentar daripada jawaban oleh standar kami. Bisakah Anda mengembangkannya? Kami juga dapat mengubahnya menjadi komentar.
gung - Reinstate Monica

Itu terdengar seperti komentar yang mengklarifikasi, bukan jawaban. Apakah ini harus diedit ke dalam pertanyaan?
gung - Reinstate Monica

@ung, dimodifikasi jawaban saya. Semoga ini bisa membantu.
Hello World

0

Versi scala dari divergensi JS dari dua urutan panjang arbitrer:

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

Periksa silang jawaban ini dengan kode di bagian edit pertanyaan:

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

0

Versi umum, untuk distribusi probabilitas n , dalam python berdasarkan rumus Wikipedia dan komentar dalam posting ini dengan vektor bobot ( pi ) sebagai parameter dan logbase khusus :

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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.