การคำนวณ KL Divergence ใน Python


22

ฉันค่อนข้างใหม่สำหรับสิ่งนี้และไม่สามารถพูดได้ว่าฉันมีความเข้าใจที่สมบูรณ์เกี่ยวกับแนวคิดทางทฤษฎีที่อยู่เบื้องหลังสิ่งนี้ ฉันกำลังพยายามคำนวณ KL Divergence ระหว่างรายการหลายจุดใน Python ฉันใช้http://scikit-learn.org/stable/modules/generated/sklearn.metrics.mutual_info_score.htmlเพื่อลองทำสิ่งนี้ ปัญหาที่ฉันพบคือค่าที่ส่งคืนนั้นเหมือนกันสำหรับ 2 หมายเลขรายการใด ๆ (1.3862943611198906) ฉันมีความรู้สึกว่าฉันทำผิดทางทฤษฎีบางอย่างที่นี่ แต่มองไม่เห็น

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

นี่คือตัวอย่างของสิ่งที่ฉันกำลังทำงาน - เพียงแค่ฉันได้รับผลลัพธ์เดียวกันสำหรับ 2 อินพุทใด ๆ คำแนะนำใด ๆ / ความช่วยเหลือจะได้รับการชื่นชม!


โดย KL คุณหมายถึง Kullback-Leibler divergence หรือเปล่า
Dawny33

ใช่แล้ว!
นันดา

โดยการเรียกใช้ผมได้รับค่าsklearn.metrics.mutual_info_score([1.346112,1.337432,1.246655], [1.033836,1.082015,1.117323]) 1.0986122886681096
Dawny33

ขออภัยฉันใช้ค่า 1 เป็น [1, 1.346112,1.337432,1.246655] และค่า 2 เป็นค่า 2 เป็น [1,1.033836,1.082015,1.117323] และดังนั้นจึงเป็นค่าความแตกต่าง
Nanda

คำตอบ:


18

ก่อนอื่นsklearn.metrics.mutual_info_scoreใช้ข้อมูลร่วมกันสำหรับการประเมินผลการจัดกลุ่มไม่ใช่การเบี่ยงเบน Kullback-Leibler ที่บริสุทธิ์ !

นี่เท่ากับ Kullback-Leibler divergence ของการกระจายแบบร่วมกับการกระจายผลิตภัณฑ์ของมาร์จิ้น

KL แตกต่าง (และมาตรการอื่น ๆ เช่น) คาดว่าข้อมูลของท่านจะมีผลรวมของ 1 มิฉะนั้นพวกเขาจะไม่เหมาะสมการแจกแจงความน่าจะเป็น หากข้อมูลของคุณไม่มีผลรวม 1 อาจเป็นไปได้ยากที่จะใช้ KL divergence! (ในบางกรณีอาจยอมรับได้ว่ามีผลรวมน้อยกว่า 1 เช่นในกรณีที่ข้อมูลหายไป)

โปรดทราบด้วยว่าการใช้ลอการิทึมฐาน 2 เป็นเรื่องปกติ สิ่งนี้ให้ผลการปรับสัดส่วนคงที่เท่านั้น แต่ลอการิทึมฐาน 2 ง่ายต่อการตีความและมีสเกลที่ใช้งานง่ายกว่า (0 ถึง 1 แทนที่จะเป็น 0 ถึง log2 = 0.69314 ... การวัดข้อมูลในบิตแทนที่จะเป็นนัต)

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

ดังที่เราเห็นได้อย่างชัดเจนว่าผลลัพธ์ MI ของการ sklearn ถูกปรับขนาดโดยใช้ลอการิทึมธรรมชาติแทน log2 นี่เป็นตัวเลือกที่โชคร้ายดังที่อธิบายไว้ข้างต้น

Kullback-Leibler divergence นั้นบอบบาง แต่น่าเสียดายที่ ในตัวอย่างข้างต้นมันไม่ได้กำหนดไว้อย่างดี: KL([0,1],[1,0])ทำให้เกิดการหารด้วยศูนย์และมีแนวโน้มที่จะไม่มีที่สิ้นสุด นอกจากนี้ยังเป็นที่ไม่สมมาตร


โปรดทราบว่าเมื่อscipy.stats.entropyมีการใช้งานมันจะทำให้ความน่าจะเป็นเป็นปกติ จาก docs ( scipy.github.io/devdocs/generated/scipy.stats.entropy.html ): "รูทีนนี้จะทำให้ปกติ pk และ qk หากพวกเขาไม่รวมถึง 1"
Itamar Mushkin

15

ฟังก์ชันเอนโทรปีของ Scipy จะคำนวณ KL divergence หากป้อนเวกเตอร์สองตัว p และ q แต่ละอันแทนการแจกแจงความน่าจะเป็น หากเวกเตอร์สองตัวไม่ใช่ไฟล์ PDF มันจะทำให้เป็นมาตรฐานก่อน

ข้อมูลร่วมกันเกี่ยวข้อง แต่ไม่เหมือนกับ KL Divergence

"ข้อมูลร่วมกันแบบถ่วงน้ำหนักนี้เป็นรูปแบบของ KL-Divergence แบบถ่วงน้ำหนักซึ่งเป็นที่ทราบกันว่าใช้ค่าลบสำหรับอินพุตบางตัวและมีตัวอย่างที่ข้อมูลร่วมกันแบบถ่วงน้ำหนักก็ใช้ค่าลบ"


6

ฉันไม่แน่ใจกับการใช้ ScikitLearn แต่นี่คือการดำเนินการอย่างรวดเร็วของ KL divergence ใน 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)

เอาท์พุท: 0.775279624079

อาจมีข้อขัดแย้งของการนำไปใช้ในบางไลบรารีดังนั้นโปรดอ่านเอกสารก่อนใช้


1
ฉันลองสิ่งนี้เช่นกัน แต่นี่เป็นการคืนค่าลบซึ่งฉันคิดว่าไม่ใช่ค่าที่ถูกต้อง การวิจัยเล็กน้อยจากนั้นพาฉันไปที่mathoverflow.net/questions/43849/ผลการวิจัยนี้ซึ่งพูดถึงว่าอินพุตมีการกระจายความน่าจะเป็นอย่างไร เดาว่าเป็นที่ที่ฉันทำผิดของฉัน
นันดา

@Nanda ขอบคุณสำหรับลิงค์ ผลตอบแทนของฉัน0.775279624079สำหรับปัจจัยการผลิตของคุณและผลตอบแทนที่1.3862943611198906ได้ ยังคงสับสน! แต่ดูเหมือนว่ารวมถึงการตรวจสอบค่าเหล่านั้นตาม qn ที่เป็นสคริปต์ที่ควรจะทำอย่างไร :)
Dawny33

1
ฉันรู้คุณหมายถึงอะไร! ฉันได้ลองใช้ฟังก์ชั่นที่แตกต่างกัน 3 แบบเพื่อให้ได้ 3 ค่าที่แตกต่างกันโดยมีสิ่งเดียวที่เหมือนกันระหว่างพวกเขาคือผลลัพธ์ไม่ได้ "รู้สึก" ค่าที่ป้อนเข้านั้นเป็นข้อผิดพลาดเชิงตรรกะดังนั้นการเปลี่ยนแนวทางของฉันโดยสิ้นเชิง
นันดา

@Nanda Ahh ชัดเจนตอนนี้ :) ขอบคุณสำหรับการอธิบาย
Dawny33

2

เคล็ดลับนี้หลีกเลี่ยงโค้ดที่มีเงื่อนไขและอาจให้ประสิทธิภาพที่ดีขึ้น

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

เคล็ดลับดี! ฉันสนใจที่จะดูว่าสิ่งนี้เปรียบเทียบกับโซลูชันอื่น ๆ ในเวลามาตรฐาน
ย่อมคุณ

0

พิจารณาสามตัวอย่างต่อไปนี้จากการแจกแจง

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

เห็นได้ชัดว่าค่า 1 และค่า 2 ใกล้เคียงกันดังนั้นเราคาดหวังว่าการวัดsurpriseหรือเอนโทรปีจะลดลงเมื่อเทียบกับค่า 3

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

เราเห็นผลลัพธ์ต่อไปนี้:

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

เราเห็นว่าสิ่งนี้สมเหตุสมผลเนื่องจากค่าระหว่างค่า 1 และค่า 3 กับค่า 2 และค่า 3 ค่อนข้างรุนแรงกว่าการเปลี่ยนแปลงค่ามากกว่าค่า 1 ถึงค่า 2 นี่คือการตรวจสอบความถูกต้องของฉันในการทำความเข้าใจ KL-D และแพ็คเกจที่สามารถใช้ประโยชน์ได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.