Berechnung der KL-Divergenz in Python

22

Ich bin ziemlich neu in diesem Bereich und kann nicht sagen, dass ich die theoretischen Konzepte dahinter vollständig verstanden habe. Ich versuche, die KL-Divergenz zwischen mehreren Punktelisten in Python zu berechnen. Ich benutze http://scikit-learn.org/stable/modules/generated/sklearn.metrics.mutual_info_score.html , um dies zu versuchen. Das Problem, auf das ich stoße, ist, dass der zurückgegebene Wert für 2 beliebige Zahlenlisten gleich ist (sein 1.3862943611198906). Ich habe das Gefühl, dass ich hier einen theoretischen Fehler mache, kann ihn aber nicht erkennen.

values1 = [1.346112,1.337432,1.246655]
values2 = [1.033836,1.082015,1.117323]
metrics.mutual_info_score(values1,values2)

Das ist ein Beispiel für das, was ich laufe - nur, dass ich für 2 Eingänge die gleiche Ausgabe erhalte. Jeder Rat / Hilfe wäre dankbar!

Nanda
quelle
Meinen Sie mit KL die Kullback-Leibler-Divergenz?
Dawny33
Ja genau das!
Nanda
Beim Laufen sklearn.metrics.mutual_info_score([1.346112,1.337432,1.246655], [1.033836,1.082015,1.117323])bekomme ich den Wert 1.0986122886681096.
Dawny33
Entschuldigung, ich habe Werte1 als [1, 1.346112, 1.337432, 1.246655] und Werte2 als Werte2 als [1.1.033836, 1.082015, 1.117323] und damit den Differenzwert verwendet.
Nanda

Antworten:

18

Vor allem sklearn.metrics.mutual_info_scoreGeräte zur gegenseitigen Information für Clustering Bewertung der Ergebnisse, nicht rein Kullback-Leibler - Divergenz!

Dies entspricht der Kullback-Leibler-Divergenz der gemeinsamen Verteilung mit der Produktverteilung der Ränder.

KL-Divergenz (und jedes andere derartige Maß) erwartet, dass die Eingabedaten eine Summe von 1 haben . Andernfalls handelt es sich nicht um richtige Wahrscheinlichkeitsverteilungen . Wenn Ihre Daten keine Summe von 1 haben, ist es höchstwahrscheinlich nicht richtig, KL-Divergenz zu verwenden! (In einigen Fällen kann es zulässig sein, eine Summe von weniger als 1 zu haben, z. B. bei fehlenden Daten.)

Beachten Sie auch, dass es üblich ist, Logarithmen zur Basis 2 zu verwenden. Dies ergibt nur einen konstanten Skalierungsfaktor für die Differenz, aber Logarithmen zur Basis 2 sind einfacher zu interpretieren und haben eine intuitivere Skalierung (0 zu 1 anstelle von 0 zu log2 = 0,69314 ..., wobei die Informationen in Bits anstelle von Nats gemessen werden).

> sklearn.metrics.mutual_info_score([0,1],[1,0])
0.69314718055994529

Wie wir deutlich sehen können, wird das MI-Ergebnis von sklearn unter Verwendung natürlicher Logarithmen anstelle von log2 skaliert. Dies ist eine unglückliche Entscheidung, wie oben erläutert.

Die Kullback-Leibler-Divergenz ist leider zerbrechlich. In dem obigen Beispiel ist es nicht genau definiert: Es KL([0,1],[1,0])bewirkt eine Division durch Null und tendiert zur Unendlichkeit. Es ist auch asymmetrisch .

Anony-Mousse
quelle
Beachten Sie, dass bei scipy.stats.entropyVerwendung die Wahrscheinlichkeiten auf eins normalisiert werden. Aus den Dokumenten ( scipy.github.io/devdocs/generated/scipy.stats.entropy.html ): "Diese Routine normalisiert pk und qk, wenn sie nicht 1 ergeben."
Itamar Mushkin
15

Die Entropiefunktion von Scipy berechnet die KL-Divergenz, wenn zwei Vektoren p und q zugeführt werden, die jeweils eine Wahrscheinlichkeitsverteilung darstellen. Wenn die beiden Vektoren keine pdfs sind, wird sie zuerst normalisiert.

Gegenseitige Informationen beziehen sich auf KL Divergence , sind jedoch nicht mit diesen identisch .

"Diese gewichtete gegenseitige Information ist eine Form der gewichteten KL-Divergenz, von der bekannt ist, dass sie für einige Eingaben negative Werte annimmt, und es gibt Beispiele, bei denen die gewichtete gegenseitige Information auch negative Werte annimmt."

jamesmf
quelle
6

Ich bin mir bei der Implementierung von ScikitLearn nicht sicher, aber hier ist eine kurze Implementierung der KL-Divergenz in Python:

import numpy as np

def KL(a, b):
    a = np.asarray(a, dtype=np.float)
    b = np.asarray(b, dtype=np.float)

    return np.sum(np.where(a != 0, a * np.log(a / b), 0))


values1 = [1.346112,1.337432,1.246655]
values2 = [1.033836,1.082015,1.117323]

print KL(values1, values2)

Ausgabe: 0.775279624079

Es könnte sein , Konflikt der Umsetzung in einigen Bibliotheken, so stellen Sie sicher , dass Sie ihre Dokumente zu lesen , bevor Sie.

Dawny33
quelle
1
Ich habe es auch versucht, aber es wurden negative Werte zurückgegeben, was meiner Meinung nach kein gültiger Wert ist. Ein bisschen Recherche brachte mich dann zu diesem Ergebnis mathoverflow.net/questions/43849/…, das darüber spricht, wie die Eingabe eine Wahrscheinlichkeitsverteilung sein muss. Ich schätze, hier habe ich meinen Fehler gemacht.
Nanda
@ Nanda Danke für den Link. Meine Renditen 0.775279624079für Ihre Eingaben und die sklearn-Metriken kehren zurück 1.3862943611198906. Immer noch verwirrt! Aber es scheint so, als würde das Einbeziehen dieser
Wertprüfungen
1
Ich weiß, was du meinst! Ich habe 3 verschiedene Funktionen ausprobiert, um 3 verschiedene Werte zu erhalten. Das einzige, was sie gemeinsam haben, ist, dass sich das Ergebnis nicht richtig anfühlt. Die Eingabewerte sind definitiv ein logischer Fehler, also ändere ich meinen Ansatz insgesamt!
Nanda
@ Nanda Ahh, das ist jetzt klar :) Danke für die Erklärung
Dawny33
2

Dieser Trick vermeidet bedingten Code und bietet möglicherweise eine bessere Leistung.

import numpy as np

def KL(P,Q):
""" Epsilon is used here to avoid conditional code for
checking that neither P nor Q is equal to 0. """
     epsilon = 0.00001

     # You may want to instead make copies to avoid changing the np arrays.
     P = P+epsilon
     Q = Q+epsilon

     divergence = np.sum(P*np.log(P/Q))
     return divergence

# Should be normalized though
values1 = np.asarray([1.346112,1.337432,1.246655])
values2 = np.asarray([1.033836,1.082015,1.117323])

# Note slight difference in the final result compared to Dawny33
print KL(values1, values2) # 0.775278939433
Johann
quelle
Guter Trick! Mich würde interessieren, wie sich dies mit der anderen Lösung auf einem Zeit-Benchmark vergleicht.
sicherlich Spaß
0

Betrachten Sie die drei folgenden Beispiele aus einer Distribution.

values1 = np.asarray([1.3,1.3,1.2])
values2 = np.asarray([1.0,1.1,1.1])
values3 = np.array([1.8,0.7,1.7])

Es ist klar, dass Werte1 und Werte2 näher beieinander liegen, sodass wir davon ausgehen, dass das Maß für surpriseoder die Entropie im Vergleich zu Werten3 niedriger ist.

from scipy.stats import entropy
print("\nIndividual Entropy\n")
print(entropy(values1))
print(entropy(values2))
print(entropy(values3))

print("\nPairwise Kullback Leibler divergence\n")
print(entropy(values1, qk=values2))
print(entropy(values1, qk=values3))
print(entropy(values2, qk=values3))

Wir sehen die folgende Ausgabe:

Individual Entropy

1.097913446793334
1.0976250611902076
1.0278436769863724 #<--- this one had the lowest, but doesn't mean much.

Pairwise Kullback Leibler divergence

0.002533297351606588
0.09053972625203921 #<-- makes sense
0.09397968199352116 #<-- makes sense

Wir sehen, dass dies sinnvoll ist, weil die Werte zwischen Werten1 und Werten3 und den Werten 2 und 3 sich drastischer ändern als die Werte1 bis 2. Dies ist meine Bestätigung für das Verständnis von KL-D und den Paketen, die dafür eingesetzt werden können.

bmc
quelle