web-dev-qa-db-ja.com

numpyを使用したテキストドキュメント間のカルバックライブラー(KL)距離の計算

私の目標は、次のテキストドキュメント間のKL距離を計算することです。

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

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]

次に、テキスト間のKL距離を計算するために次のコードを適用しました。

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

上記のコードの結果は次のとおりです:[0.0, 0.602059991328, 0.0]。テキスト1と3は完全に異なりますが、それらの間の距離は0ですが、関連性の高いテキスト1と2の距離は0.602059991328です。これは正確ではありません。

KLに関して私が正しくやっていないことを誰かが知っていますか?あなたの提案に感謝します。

11
Tiger1

別の答えを追加するのは嫌ですが、ここには2つのポイントがあります。まず、Jaimeがコメントで指摘したように、KL発散(または距離-以下のドキュメントによると、同じです)は、確率分布間の差を測定するように設計されています。これは基本的に、関数に渡すものは2つの配列のようなものであり、それぞれの要素の合計が1になることを意味します。

第二に、scipyは明らかにこれを実装しており、情報理論の分野により関連した命名スキームを使用しています。関数は「エントロピー」です。

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

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

ドキュメントから:

QkがNoneでない場合は、相対エントロピー(Kullback-Leibler発散またはKullback-Leibler距離とも呼ばれます)S = sum(pk * log(pk/qk)、axis = 0)を計算します。

この関数の利点は、合計が1にならない場合に、渡すベクトルを正規化することです(ただし、渡す配列、つまりデータからの構築方法に注意する必要があります)。

これがお役に立てば幸いです。少なくともライブラリが提供しているので、独自にコーディングする必要はありません。

30
dpb

KLの概念を理解するために少しグーグルした後、あなたの問題はベクトル化によるものだと思います。異なる単語の出現数を比較しているのです。列インデックスを1つの単語にリンクするか、辞書を使用する必要があります。

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

次に、kl関数を使用できます。

自動的に辞書にベクトル化するには、 リスト内の要素の頻度をカウントする方法collections.Counterはまさにあなたが必要とするものです)。次に、辞書のキーの和集合をループして、KL距離を計算できます。

1

潜在的な問題は、NP KLの定義にある可能性があります。式についてはウィキペディアのページをお読みください: http://en.wikipedia.org/wiki/Kullback%E2%80% 93Leibler_divergence

(p-q)にログ結果を掛けることに注意してください。 KLの式によれば、これはp:のみである必要があります。

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

それは役立つかもしれません...

0
dpb