Расчет расхождения 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)

Это пример того, что я запускаю - просто я получаю одинаковый вывод для любых двух входов. Любой совет / помощь будет принята с благодарностью!

Нанда
источник
Под KL вы имеете в виду расхождение Кульбака-Лейблера?
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,1177323] и, следовательно, значение разности.
Нанда

Ответы:

18

Прежде всего, sklearn.metrics.mutual_info_scoreреализует взаимную информацию для оценки результатов кластеризации, а не чистую дивергенцию Кульбака-Лейблера!

Это равно расхождению Кульбака-Лейблера совместного распределения с распределением произведений маргиналов.

Расхождение KL (и любая другая такая мера) ожидает, что входные данные будут иметь сумму 1 . В противном случае они не являются правильными распределениями вероятностей . Если ваши данные не имеют суммы 1, то, скорее всего, использование дивергенции KL обычно некорректно! (В некоторых случаях может быть допустимо иметь сумму менее 1, например, в случае отсутствия данных.)

Также обратите внимание, что обычно используют основание 2 логарифмов. Это дает только постоянный коэффициент масштабирования в разнице, но логарифмы с основанием 2 легче интерпретировать и имеют более интуитивную шкалу (от 0 до 1 вместо 0 до log2 = 0.69314 ..., измеряя информацию в битах вместо nat).

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

как мы можем ясно видеть, MI-результат sklearn масштабируется с использованием натуральных логарифмов вместо log2. Это неудачный выбор, как объяснено выше.

Расхождение Кульбака-Лейблера, к сожалению, хрупкое. В приведенном выше примере это не является четко определенным: KL([0,1],[1,0])вызывает деление на ноль и стремится к бесконечности. Это также асимметрично .

Аноним-Мусс-Восстановить Монику
источник
Обратите внимание, что при scipy.stats.entropyиспользовании он нормализует вероятности до единицы. Из документов ( scipy.github.io/devdocs/generated/scipy.stats.entropy.html ): «Эта процедура нормализует pk и qk, если они не суммируются с 1».
Итамар Мушкин
15

Функция энтропии Сципи вычислит дивергенцию KL, если подать два вектора p и q, каждый из которых представляет распределение вероятности. Если два вектора не являются PDF-файлами, то сначала нормализуется.

Взаимная информация относится, но не совпадает с KL Divergence.

«Эта взвешенная взаимная информация является формой взвешенной дивергенции KL, которая, как известно, принимает отрицательные значения для некоторых входных данных, и есть примеры, когда взвешенная взаимная информация также принимает отрицательные значения»

jamesmf
источник
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

В некоторых библиотеках может возникнуть конфликт реализации , поэтому обязательно прочитайте их документы перед использованием.

Dawny33
источник
1
Я тоже пытался, но это возвращало отрицательные значения, что, я думаю, не является допустимым значением. Небольшое исследование привело меня к этому результату mathoverflow.net/questions/43849/…, который говорит о том, как входные данные должны быть распределением вероятности. Думаю, именно здесь я допустил ошибку.
Нанда,
@ Нанда Спасибо за ссылку. Мой доход 0.775279624079за ваши входы и возвращение метрики склеарна 1.3862943611198906. Смущен до сих пор! Но, похоже, что включение этих проверок значений в соответствии с qn в скрипт должно сделать :)
Dawny33
1
Я знаю, что Вы имеете ввиду! Я пробовал 3 разные функции, чтобы получить 3 разных значения, единственное, что между ними было общим, это то, что результат «не чувствовался» правильным. Входные значения, безусловно, являются логической ошибкой, поэтому мой подход полностью изменился!
Нанда,
@ Нанда Ах, теперь все понятно :) Спасибо за объяснение
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
Johann
источник
Хороший трюк! Мне было бы интересно посмотреть, как это соотносится с другим решением в тесте времени.
несомненно, вы шутите
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 и пакетов, которые можно использовать для него.

BMC
источник