Розрахунок дивергенції KL в Python


22

Я досить новачок у цьому і не можу сказати, що я маю повне розуміння теоретичних концепцій, що стоять за цим. Я намагаюся обчислити розбіжність KL між декількома списками точок у 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 вхід. Будь-яка порада / допомога буде вдячна!


Під КЛ, ви маєте на увазі розбіжність Кульбека-Лейблера?
Світанок33

Так, саме так!
Нанда

Забігаючи sklearn.metrics.mutual_info_score([1.346112,1.337432,1.246655], [1.033836,1.082015,1.117323]), я отримую значення 1.0986122886681096.
Світанок33

Вибачте, я використовував значення1 як [1, 1.346112,1.337432,1.246655], а значення2 як значення2 як [1,1.033836,1.082015,1.117323], а отже, і значення різниці.
Нанда

Відповіді:


18

Перш за все, sklearn.metrics.mutual_info_scoreвпроваджує взаємну інформацію для оцінки результатів кластеризації, а не чистого розбіжності Kullback-Leibler!

Це дорівнює розбіжності Куллбека-Лейблера спільного розподілу з розподілом продуктів на границі.

Дивергенція KL (і будь-який інший такий захід) очікує, що вхідні дані матимуть суму 1 . В іншому випадку вони не є правильним розподілом ймовірностей . Якщо у ваших даних немає суми 1, швидше за все, неправильно використовувати розбіжність KL! (У деяких випадках може бути допустимо мати суму менше 1, наприклад, у випадку відсутніх даних.)

Також зауважте, що звичайно використовувати логарифми базових 2. Це дає лише постійний коефіцієнт масштабування різниці, але логарифми бази 2 легше інтерпретувати та мають більш інтуїтивну шкалу (від 0 до 1 замість 0 до log2 = 0,69314 ..., вимірюючи інформацію в бітах замість нац).

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

як ми добре бачимо, результат МК склеарну масштабується за допомогою природних логарифмів замість log2. Це невдалий вибір, як пояснено вище.

На жаль, розбіжність Кульбека-Лейблера є крихкою. На наведеному вище прикладі він не чітко визначений: KL([0,1],[1,0])викликає поділ на нуль і прагне до нескінченності. Він також несиметричний .


Зауважте, що при scipy.stats.entropyвикористанні це нормалізує ймовірності до одиниці. З документа ( scipy.github.io/devdocs/generated/scipy.stats.entropy.html ): "Цей звичайний спосіб нормалізує pk і qk, якщо вони не дорівнюватимуть 1."
Ітамар Мушкін

15

Ентропійна функція Scipy обчислить дивергенцію KL, якщо подавати два вектори p і q, кожен з яких представляє розподіл ймовірності. Якщо два вектори не є pdfs, він спочатку нормалізується.

Взаємна інформація пов'язана з, але не такою ж, як KL Divergence.

"Ця зважена взаємна інформація є формою зваженої KL-дивергенції, яка, як відомо, приймає негативні значення для деяких входів. Є приклади, коли зважена взаємна інформація також приймає негативні значення"


6

Я не впевнений у реалізації ScikitLearn, але ось швидка реалізація розбіжності KL у 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ваші входи, а показники sklearn повертаються 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 та пакетів, які можна використовувати для цього.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.