Menghitung KL Divergence dengan Python

22

Saya agak baru dalam hal ini dan tidak bisa mengatakan saya memiliki pemahaman yang lengkap tentang konsep-konsep teoritis di balik ini. Saya mencoba untuk menghitung KL Divergence antara beberapa daftar poin dengan Python. Saya menggunakan http://scikit-learn.org/stable/modules/generated/sklearn.metrics.mutual_info_score.html untuk mencoba dan melakukan ini. Masalah yang saya hadapi adalah bahwa nilai yang dikembalikan sama untuk setiap 2 daftar angka (1,3862943611198906). Saya merasa bahwa saya membuat semacam kesalahan teoretis di sini tetapi tidak dapat menemukannya.

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

Itu adalah contoh dari apa yang saya jalankan - hanya saja saya mendapatkan output yang sama untuk 2 input. Setiap saran / bantuan akan dihargai!

Nanda
sumber
Maksud Anda KL, maksud Anda perbedaan Kullback-Leibler?
Dawny33
Ya persis seperti itu!
Nanda
Dengan berlari sklearn.metrics.mutual_info_score([1.346112,1.337432,1.246655], [1.033836,1.082015,1.117323]), saya mendapatkan nilai 1.0986122886681096.
Dawny33
Maaf, saya menggunakan values1 sebagai [1, 1.346112,1.337432,1.246655] dan values2 sebagai values2 sebagai [1,1.033836,1.082015,1.117323] dan karenanya merupakan nilai selisih.
Nanda

Jawaban:

18

Pertama-tama, sklearn.metrics.mutual_info_scoremengimplementasikan informasi timbal balik untuk mengevaluasi hasil pengelompokan, bukan perbedaan murni Kullback-Leibler!

Ini sama dengan divergensi Kullback-Leibler dari distribusi bersama dengan distribusi produk dari marjinal.

Divergensi KL (dan ukuran lain apa pun) mengharapkan data input memiliki jumlah 1 . Kalau tidak, mereka bukan distribusi probabilitas yang tepat . Jika data Anda tidak memiliki jumlah 1, kemungkinan besar biasanya tidak tepat untuk menggunakan perbedaan KL! (Dalam beberapa kasus, dapat diterima untuk memiliki jumlah kurang dari 1, misalnya dalam kasus data yang hilang.)

Perhatikan juga bahwa umum untuk menggunakan logaritma basis 2. Ini hanya menghasilkan faktor penskalaan konstan dalam perbedaan, tetapi logaritma basis 2 lebih mudah untuk diinterpretasikan dan memiliki skala yang lebih intuitif (0 ke 1 bukannya 0 ke log2 = 0,69314 ..., mengukur informasi dalam bit alih-alih nats).

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

seperti yang bisa kita lihat dengan jelas, hasil MI dari sklearn diskalakan menggunakan logaritma natural, bukan log2. Ini adalah pilihan yang tidak menguntungkan, seperti dijelaskan di atas.

Sayangnya, perbedaan Kullback-Leibler rapuh. Pada contoh di atas tidak terdefinisi dengan baik: KL([0,1],[1,0])menyebabkan pembagian dengan nol, dan cenderung tak hingga. Ini juga asimetris .

Anony-Mousse
sumber
Perhatikan bahwa ketika scipy.stats.entropydigunakan, itu akan menormalkan probabilitas menjadi satu. Dari dokumen ( scipy.github.io/devdocs/generated/scipy.stats.entropy.html ): "Rutin ini akan menormalkan pk dan qk jika mereka tidak berjumlah 1."
Itamar Mushkin
15

Fungsi entropi Scipy akan menghitung divergensi KL jika memberi makan dua vektor p dan q, masing-masing mewakili distribusi probabilitas. Jika kedua vektor bukan pdf, maka akan dinormalisasi terlebih dahulu.

Informasi timbal balik terkait, tetapi tidak sama dengan KL Divergence.

"Informasi timbal balik tertimbang ini adalah bentuk KL-Divergence tertimbang, yang diketahui mengambil nilai negatif untuk beberapa input, dan ada contoh di mana informasi timbal balik tertimbang juga mengambil nilai negatif"

jamesmf
sumber
6

Saya tidak yakin dengan implementasi ScikitLearn, tetapi berikut ini adalah implementasi cepat dari divergence KL dengan 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)

Keluaran: 0.775279624079

Mungkin ada konflik implementasi di beberapa perpustakaan, jadi pastikan Anda membaca dokumen mereka sebelum menggunakan.

Dawny33
sumber
1
Saya mencoba ini juga tetapi ini mengembalikan nilai negatif yang, saya pikir, bukan nilai yang valid. Sedikit riset kemudian membawa saya ke hasil ini mathoverflow.net/questions/43849/... yang berbicara tentang bagaimana input harus menjadi distribusi probabilitas. Tebak di situlah saya membuat kesalahan.
Nanda
@Nanda Terima kasih atas tautannya. Tambang kembali 0.775279624079untuk input Anda dan metrik sklearn kembali 1.3862943611198906. Masih bingung! Tapi, sepertinya memasukkan cek nilai tersebut sesuai dengan qn, ke dalam skrip harus dilakukan :)
Dawny33
1
Saya mengerti maksud Anda! Saya telah mencoba 3 fungsi berbeda untuk mendapatkan 3 nilai berbeda dengan satu-satunya hal yang umum di antara mereka adalah bahwa hasilnya tidak "terasa" benar. Nilai input jelas merupakan kesalahan logis sehingga mengubah pendekatan saya sama sekali!
Nanda
@Nanda Ahh, sudah jelas sekarang :) Terima kasih telah menjelaskan
Dawny33
2

Trik ini menghindari kode kondisional dan karenanya dapat memberikan kinerja yang lebih baik.

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
sumber
Trik yang bagus! Saya akan tertarik untuk melihat bagaimana ini membandingkan dengan solusi lain pada benchmark waktu.
pastinya kita bercanda
0

Pertimbangkan tiga sampel berikut dari suatu distribusi.

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

Jelas, nilai1 dan nilai2 lebih dekat, jadi kami berharap ukuran surpriseatau entropi, lebih rendah jika dibandingkan dengan nilai3.

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

Kami melihat output berikut:

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

Kami melihat ini masuk akal karena nilai antara nilai1 dan nilai3 dan nilai 2 dan nilai 3 hanya lebih drastis dalam perubahan daripada nilai1 ke nilai 2. Ini adalah validasi saya untuk memahami KL-D dan paket yang dapat dimanfaatkan untuk itu.

bmc
sumber