Tính toán phân kỳ Jensen-Shannon cho 3 phân phối thăm dò: Điều này có ổn không?


12

Tôi muốn tính toán phân kỳ jensen-shannon cho anh ta sau 3 lần phân phối. Tính toán dưới đây có đúng không? (Tôi đã làm theo công thức JSD từ 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

Cảm ơn trước...

EDIT Đây là một số mã Python bẩn đơn giản cũng tính toán điều này:

    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
Nhân tiện mã Python đẹp!
gui11aume

Câu trả lời:


13

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

Tôi sẽ cung cấp chi tiết về một tính toán:

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

Theo cách tương tự, các điều khoản khác là 0.325083 và 1.098612. Vậy kết quả cuối cùng là 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
Không bẩn lắm ... ;-)
gui11aume

4
(1) Làm lại toán học. (2) Entropy có thể được đo bằng bất kỳ cơ sở logarit nào bạn thích, miễn là bạn nhất quán. Nhật ký tự nhiên, phổ biến và cơ sở-2 đều là thông thường. (3) Đó thực sự là một sự khác biệt trung bình giữa các bản phân phối và mức trung bình của chúng. Nếu bạn nghĩ mỗi phân phối là một điểm, chúng tạo thành một đám mây. Bạn đang nhìn vào "khoảng cách" trung bình giữa tâm của đám mây và các điểm của nó, giống như bán kính trung bình. Theo trực giác, nó đo kích thước của đám mây.
whuber

1
@Legend Tôi nghĩ bạn đúng. Tôi đã không kiểm tra đầy đủ sau khi nhận thấy rằng một kết quả đồng ý với câu trả lời tôi nhận được theo cách khác (với Mathematica ).
whuber

1
@dmck Thực sự có lỗi chính tả trong nhận xét của tôi: (1) cụm từ h <- function(x) {đã được dán hai lần. Chỉ cần xóa nó: mọi thứ khác hoạt động và tạo ra kết quả tôi trích dẫn. Sau đó sửa đổi apply(p, 2, h)thành apply(p, 1, h)như được chỉ ra trong bình luận của Legend .
whuber

6

Con trăn

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

Bạn đã đưa ra một tài liệu tham khảo Wikipedia. Ở đây tôi đưa ra biểu thức hoàn chỉnh cho phân kỳ Jensen-Shannon với nhiều phân phối xác suất:

JSmetrTôic(p1,...,pm)= =H(p1+...+pmm)-Σj= =1mH(pj)m

Câu hỏi ban đầu đã được đăng mà không có biểu thức toán học về phân kỳ JS đa phân phối dẫn đến sự nhầm lẫn trong việc hiểu tính toán được cung cấp. Ngoài ra, thuật ngữ weightđã được sử dụng một lần nữa gây nhầm lẫn rằng cách bạn chọn trọng số phù hợp để nhân. Biểu hiện trên làm rõ những nhầm lẫn này. Rõ ràng từ biểu thức trên, trọng số được tự động chọn tùy thuộc vào số lượng phân phối.


Điều này đang được tự động gắn cờ là chất lượng thấp, có lẽ vì nó quá ngắn. Hiện tại nó là một nhận xét nhiều hơn là một câu trả lời theo tiêu chuẩn của chúng tôi. Bạn có thể mở rộng về nó? Chúng tôi cũng có thể biến nó thành một bình luận.
gung - Phục hồi Monica

Nghe có vẻ như một bình luận làm rõ, hơn là một câu trả lời. Đây có nên là một chỉnh sửa vào câu hỏi?
gung - Phục hồi Monica

@ gung, sửa đổi câu trả lời của tôi. Hy vọng nó giúp.
Xin chào thế giới

0

Phiên bản Scala của phân kỳ JS của hai chuỗi độ dài tùy ý:

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

Kiểm tra chéo câu trả lời này với mã trong phần chỉnh sửa câu hỏi:

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

0

Một phiên bản chung, cho n phân phối xác suất, trong python dựa trên công thức Wikipedia và nhận xét trong bài đăng này với vectơ trọng số ( pi ) làm tham số và logbase tùy chỉnh :

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.