web-dev-qa-db-ja.com

2つのテキストドキュメント間の類似性を計算する方法は?

私はあらゆるプログラミング言語でNLPプロジェクトに取り組んでいます(Pythonが私の好みですが)。

2つのドキュメントを取得し、それらの類似性を判断したいと思います。

168
Reily Bourne

これを行う一般的な方法は、ドキュメントをtf-idfベクトルに変換してから、それらの間のコサイン類似度を計算することです。情報検索(IR)に関する教科書はこれをカバーしています。特に参照してください。 Introduction to Information Retrieval 、これは無料でオンラインで入手できます。

Tf-idf(および同様のテキスト変換)は、Pythonパッケージ Gensim および scikit-learn に実装されています。後者のパッケージでは、コサインの類似性の計算は次のように簡単です

from sklearn.feature_extraction.text import TfidfVectorizer

documents = [open(f) for f in text_files]
tfidf = TfidfVectorizer().fit_transform(documents)
# no need to normalize, since Vectorizer will return normalized tf-idf
pairwise_similarity = tfidf * tfidf.T

または、ドキュメントがプレーン文字列の場合、

>>> vect = TfidfVectorizer(min_df=1)
>>> tfidf = vect.fit_transform(["I'd like an Apple",
...                             "An Apple a day keeps the doctor away",
...                             "Never compare an Apple to an orange",
...                             "I prefer scikit-learn to Orange"])
>>> (tfidf * tfidf.T).A
array([[ 1.        ,  0.25082859,  0.39482963,  0.        ],
       [ 0.25082859,  1.        ,  0.22057609,  0.        ],
       [ 0.39482963,  0.22057609,  1.        ,  0.26264139],
       [ 0.        ,  0.        ,  0.26264139,  1.        ]])

gensimにはこの種のタスクのためのオプションがさらにあります。

この質問 も参照してください。

[免責事項:scikit-learn tf-idfの実装に関与していました。]

244
Fred Foo

@larsmanと同じですが、前処理がいくつかあります

import nltk, string
from sklearn.feature_extraction.text import TfidfVectorizer

nltk.download('punkt') # if necessary...


stemmer = nltk.stem.porter.PorterStemmer()
remove_punctuation_map = dict((ord(char), None) for char in string.punctuation)

def stem_tokens(tokens):
    return [stemmer.stem(item) for item in tokens]

'''remove punctuation, lowercase, stem'''
def normalize(text):
    return stem_tokens(nltk.Word_tokenize(text.lower().translate(remove_punctuation_map)))

vectorizer = TfidfVectorizer(tokenizer=normalize, stop_words='english')

def cosine_sim(text1, text2):
    tfidf = vectorizer.fit_transform([text1, text2])
    return ((tfidf * tfidf.T).A)[0,1]


print cosine_sim('a little bird', 'a little bird')
print cosine_sim('a little bird', 'a little bird chirps')
print cosine_sim('a little bird', 'a big dog barks')
79
Renaud

これは古い質問ですが、これは Spacy で簡単にできることがわかりました。ドキュメントが読み込まれると、単純なAPI similarityを使用して、ドキュメントベクトル間のコサインの類似性を見つけることができます。

import spacy
nlp = spacy.load('en')
doc1 = nlp(u'Hello hi there!')
doc2 = nlp(u'Hello hi there!')
doc3 = nlp(u'Hey whatsup?')

print doc1.similarity(doc2) # 0.999999954642
print doc2.similarity(doc3) # 0.699032527716
print doc1.similarity(doc3) # 0.699032527716
30
Koustuv Sinha

一般に、2つのドキュメント間のコサイン類似度は、ドキュメントの類似度の尺度として使用されます。 Javaでは、 Lucene (コレクションがかなり大きい場合)または LingPipe を使用してこれを行うことができます。基本的な概念は、すべてのドキュメントの用語をカウントし、用語ベクトルの内積を計算することです。ライブラリは、この一般的なアプローチに対していくつかの改善を提供します。逆ドキュメント頻度を使用し、tf-idfベクトルを計算します。 copmlexを実行する場合、LingPipeは、コサイン類似度よりも優れた結果をもたらすドキュメント間のLSA類似度を計算するメソッドも提供します。 Pythonの場合、 NLTK を使用できます。

17
Pulkit Goyal

始めるための小さなアプリがあります...

import difflib as dl

a = file('file').read()
b = file('file1').read()

sim = dl.get_close_matches

s = 0
wa = a.split()
wb = b.split()

for i in wa:
    if sim(i, wb):
        s += 1

n = float(s) / float(len(wa))
print '%d%% similarity' % int(n * 100)
4
Dr. KingSchultz

コサインドキュメントの類似性についてこのオンラインサービスを試してみるとよいかもしれません http://www.scurtu.it/documentSimilarity.html

import urllib,urllib2
import json
API_URL="http://www.scurtu.it/apis/documentSimilarity"
inputDict={}
inputDict['doc1']='Document with some text'
inputDict['doc2']='Other document with some text'
params = urllib.urlencode(inputDict)    
f = urllib2.urlopen(API_URL, params)
response= f.read()
responseObject=json.loads(response)  
print responseObject

非常に正確なものを探している場合は、tf-idfよりも優れたツールを使用する必要があります。 niversal statement encoder は、2つのテキスト間の類似性を見つけるための最も正確なものの1つです。 Googleは、何もゼロからトレーニングする必要なく、独自のアプリケーションに使用できる事前トレーニング済みモデルを提供しました。まず、tensorflowとtensorflow-hubをインストールする必要があります。

    pip install tensorflow
    pip install tensorflow_hub

以下のコードでは、テキストを固定長のベクトル表現に変換し、ドット積を使用してそれらの間の類似性を見つけることができます

module_url = "https://tfhub.dev/google/universal-sentence-encoder/1?tf-hub-format=compressed"

# Import the Universal Sentence Encoder's TF Hub module
embed = hub.Module(module_url)

# sample text
messages = [
# Smartphones
"My phone is not good.",
"Your cellphone looks great.",

# Weather
"Will it snow tomorrow?",
"Recently a lot of hurricanes have hit the US",

# Food and health
"An Apple a day, keeps the doctors away",
"Eating strawberries is healthy",
]

similarity_input_placeholder = tf.placeholder(tf.string, shape=(None))
similarity_message_encodings = embed(similarity_input_placeholder)
with tf.Session() as session:
    session.run(tf.global_variables_initializer())
    session.run(tf.tables_initializer())
    message_embeddings_ = session.run(similarity_message_encodings, feed_dict={similarity_input_placeholder: messages})

    corr = np.inner(message_embeddings_, message_embeddings_)
    print(corr)
    heatmap(messages, messages, corr)

プロットのコード:

def heatmap(x_labels, y_labels, values):
    fig, ax = plt.subplots()
    im = ax.imshow(values)

    # We want to show all ticks...
    ax.set_xticks(np.arange(len(x_labels)))
    ax.set_yticks(np.arange(len(y_labels)))
    # ... and label them with the respective list entries
    ax.set_xticklabels(x_labels)
    ax.set_yticklabels(y_labels)

    # Rotate the tick labels and set their alignment.
    plt.setp(ax.get_xticklabels(), rotation=45, ha="right", fontsize=10,
         rotation_mode="anchor")

    # Loop over data dimensions and create text annotations.
    for i in range(len(y_labels)):
        for j in range(len(x_labels)):
            text = ax.text(j, i, "%.2f"%values[i, j],
                           ha="center", va="center", color="w", 
fontsize=6)

    fig.tight_layout()
    plt.show()

結果は次のようになります。 the similarity matrix between pairs of texts

あなたが見ることができるように、最も類似しているのは、テキスト自身とそれから意味の近いテキストの間です。

重要:モデルをダウンロードする必要があるため、コードを初めて実行するときは遅くなります。モデルを再度ダウンロードしないようにし、ローカルモデルを使用する場合は、キャッシュ用のフォルダーを作成して環境変数に追加し、最初の実行後にそのパスを使用する必要があります。

tf_hub_cache_dir = "universal_encoder_cached/"
os.environ["TFHUB_CACHE_DIR"] = tf_hub_cache_dir

# pointing to the folder inside cache dir, it will be unique on your system
module_url = tf_hub_cache_dir+"/d8fbeb5c580e50f975ef73e80bebba9654228449/"
embed = hub.Module(module_url)

詳細: https://tfhub.dev/google/universal-sentence-encoder/2

2
Rohola Zandie

2つのテキストのセマンティックな類似性の測定に興味がある場合は、 このgitlabプロジェクト をご覧になることをお勧めします。サーバーとして実行できます。また、2つのテキストの類似性を簡単に測定できる事前構築モデルもあります。ほとんどの場合、2つの文の類似性を測定するために訓練されていますが、それでもケースで使用できます。Javaで記述されていますが、RESTfulサービスとして実行できます。

別のオプションも DKPro Similarity です。これは、テキストの類似性を測定するためのさまざまなアルゴリズムを備えたライブラリです。ただし、Javaでも記述されています。

2
Mohammad-Ali