web-dev-qa-db-fra.com

Calcul de la distance de Kullback-Leibler (KL) entre des documents texte à l'aide de numpy

Mon objectif est de calculer la distance KL entre les documents texte suivants:

1)The boy is having a lad relationship
2)The boy is having a boy relationship
3)It is a lovely day in NY

J'ai tout d'abord vectorisé les documents pour pouvoir appliquer facilement numpy

1)[1,1,1,1,1,1,1]
2)[1,2,1,1,1,2,1]
3)[1,1,1,1,1,1,1]

J'ai ensuite appliqué le code suivant pour calculer la distance KL entre les textes:

import numpy as np
import math
from math import log

v=[[1,1,1,1,1,1,1],[1,2,1,1,1,2,1],[1,1,1,1,1,1,1]]
c=v[0]
def kl(p, q):
    p = np.asarray(p, dtype=np.float)
    q = np.asarray(q, dtype=np.float)
    return np.sum(np.where(p != 0,(p-q) * np.log10(p / q), 0))
for x in v:
    KL=kl(x,c)
    print KL

Voici le résultat du code ci-dessus: [0.0, 0.602059991328, 0.0]. Les textes 1 et 3 sont complètement différents, mais la distance les séparant est 0, tandis que les textes 1 et 2, qui sont très liés, ont une distance de 0.602059991328. Ce n'est pas exact.

Quelqu'un a-t-il une idée de ce que je ne fais pas bien en ce qui concerne KL? Merci beaucoup pour vos suggestions.

11
Tiger1

Bien que je déteste ajouter une autre réponse, il y a deux points ici. Tout d’abord, comme Jaime l’a souligné dans les commentaires, la divergence de KL (ou la distance - ils sont, selon la documentation suivante, identiques) est conçue pour mesurer la différence entre les distributions de probabilité. Cela signifie fondamentalement que ce que vous transmettez à la fonction doit être de type tableau-like, les éléments de chacun d'eux totalisant 1.

Deuxièmement, scipy semble implémenter cela, avec un schéma de nommage plus lié au domaine de la théorie de l'information. La fonction est "entropie":

scipy.stats.entropy(pk, qk=None, base=None)

http://docs.scipy.org/doc/scipy-dev/reference/generated/scipy.stats.entropy.html

De la docs:

Si qk n'est pas Aucun, calculez une entropie relative (également appelée divergence de Kullback-Leibler ou distance de Kullback-Leibler) S = somme (pk * log (pk/qk), axe = 0).

L'avantage de cette fonction est également qu'elle normalisera les vecteurs que vous transmettez s'ils ne totalisent pas 1 (bien que cela signifie que vous devez faire attention aux tableaux que vous passez - c'est-à-dire, comment ils sont construits à partir de données).

J'espère que cela vous aidera, et au moins une bibliothèque le fournira, donc n'ayez pas à coder votre propre code.

30
dpb

Après un peu de google pour comprendre le concept de KL, je pense que votre problème est dû à la vectorisation: vous comparez le nombre d'apparition de différents mots. Vous devez soit associer votre indice de colonne à un mot, soit utiliser un dictionnaire:

#  The boy is having a lad relationship It lovely day in NY
1)[1   1   1  1      1 1   1            0  0      0   0  0]
2)[1   2   1  1      1 0   1            0  0      0   0  0]
3)[0   0   1  0      1 0   0            1  1      1   1  1]

Ensuite, vous pouvez utiliser votre fonction kl.

Pour vectoriser automatiquement dans un dictionnaire, voir Comment compter la fréquence des éléments dans une liste? (collections.Counter est exactement ce dont vous avez besoin). Ensuite, vous pouvez faire une boucle sur l'union des clés des dictionnaires pour calculer la distance KL.

1
J. Martinot-Lagarde

Un problème potentiel pourrait être lié à votre NP définition de KL. Lisez la page wikipedia pour la formule: http://fr.wikipedia.org/wiki/Kullback%E2%80%93Leibler_divergence

Notez que vous multipliez (p-q) par le résultat du journal. Conformément à la formule KL, cela ne devrait être que p:

 return np.sum(np.where(p != 0,(p) * np.log10(p / q), 0))

Cela peut aider ...

0
dpb