2013-04-08 6 views
8

Ich habe eine andere Frage, die ich mir erhofft hatte, dass jemand mir helfen könnte.Jensen-Shannon Divergenz

Ich benutze die Jensen-Shannon-Divergence, um die Ähnlichkeit zwischen zwei Wahrscheinlichkeitsverteilungen zu messen. Die Ähnlichkeitswerte scheinen in dem Sinne korrekt zu sein, dass sie zwischen 1 und 0 liegen, wobei 1 bedeutet, dass die Verteilungen gleich sind.

Ich bin mir jedoch nicht sicher, ob es tatsächlich irgendwo einen Fehler gibt und fragte mich, ob jemand vielleicht sagen könnte "Ja, es ist richtig" oder "Nein, du hast etwas falsch gemacht". Hier

ist der Code:

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, 1.0/10, 0] 
    q = [0, 1.0/10, 9.0/10] 
    print J.Jensen_Shannon_divergence(p, q) 

Das Problem ist, dass ich das Gefühl, dass die Werte nicht hoch genug sind, wenn zwei Textdokumente zu vergleichen, zum Beispiel. Dies ist jedoch ein rein subjektives Gefühl.

Jede Hilfe wird, wie immer, geschätzt.

+1

Vielleicht versuchen Ausgabe zu vergleichen (https://www.mathworks.com/matlabcentral/fileexchange/20689-jensen-shannon-divergenz) Oder lauf es in Oktave. –

+0

Das 'if p [x]! = 0.0 oder p [x]! = 0 'sieht seltsam aus. –

+0

Wenn p [x]! = 0.0 oder p [x]! = 0 verwendet wird, um sicherzustellen, dass wir Einträge, die Null sind, nicht berücksichtigen, ob sie Floats oder Ganzzahlen sind, ist das, worauf Sie sich beziehen? Oder meintest du, dass diese Linie komisch ist, Punkt? Danke vielmals. – Martyn

Antwort

6

Erhalten Sie einige Daten für Verteilungen mit bekannter Divergenz, und vergleichen Sie Ihre Ergebnisse mit diesen bekannten Werten.

BTW: die Summe in KL_divergence kann die zip built-in function wie dies mit neu geschrieben werden:

sum(_p * log(_p/_q) for _p, _q in zip(p, q) if _p != 0) 

Damit entfällt mit vielen „Lärm“ und ist auch viel mehr „pythonic“. Der Doppelvergleich mit 0.0 und 0 ist nicht notwendig.

14

Beachten Sie, dass der Scipy-Entropie-Aufruf unten die Kullback-Leibler-Divergenz ist.

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

Beachten Sie auch, dass der Testfall in der Frage irrt aussieht ?? Die Summe der p-Verteilung addiert sich nicht zu 1.0.

See: http://www.itl.nist.gov/div898/handbook/eda/section3/eda361.htm

+1

Das Importieren und Verwenden von 'norm' ist nicht erforderlich, da' entropy' die Verteilungen normalisiert, wenn sie nicht zu 1 addiert werden (siehe http://docs.scipy.org/doc/scipy-dev/reference/generated/scipy) .stats.entropy.html). Um "_M" so zu berechnen, müssen '_P' und' _Q' jedoch 'numpy.darray' Objekte sein. – Tur1ng

+4

@ Tur1ng Beachten Sie, dass eine Norm erforderlich ist, da die Berechnung von '_M' erfordert, dass '_P' und' _Q' Wahrscheinlichkeitsverteilungen sind (bereits normalisiert). Beachten Sie auch, dass Listen als numplige Arrays erzwungen werden, so dass dies in Ordnung ist: '[2, 4]/np.array ([1, 2])' –

+0

Rechts. Notiert, danke – Tur1ng

0

Explizit nach der Mathematik in den Wikipedia article: [? Diesem Matlab-Skript]

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