如何计算两个文本文档之间的相似度?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何计算两个文本文档之间的相似度?相关的知识,希望对你有一定的参考价值。

我正在寻找一个NLP项目,使用任何编程语言(虽然Python将是我的偏好)。

我想拿两份文件,确定它们有多相似。

答案

这样做的常用方法是将文档转换为tf-idf向量,然后计算它们之间的余弦相似度。任何有关信息检索(IR)的教科书都涵盖了这一点。尤其是Introduction to Information Retrieval,免费在线提供。

Tf-idf(和类似的文本转换)在Python包Gensimscikit-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可能有更多选择来完成这类任务。

另见this question

[免责声明:我参与了scikit-learn tf-idf实现。]

另一答案

与@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')
另一答案

这是一个老问题,但我发现这可以通过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
另一答案

通常,两个文档之间的余弦相似性被用作文档的相似性度量。在Java中,您可以使用Lucene(如果您的集合非常大)或LingPipe来执行此操作。基本概念是计算每个文档中的术语并计算术语向量的点积。这些库确实提供了对这种通用方法的若干改进,例如,使用逆文档频率并计算tf-idf向量。如果你想做一些copmlex,LingPipe还提供了计算文档之间LSA相似性的方法,它提供了比余弦相似性更好的结果。对于Python,您可以使用NLTK

另一答案

这是一个让你入门的小应用程序......

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)
另一答案

你可能想尝试这个在线服务的余弦文档相似性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
另一答案

如果你对测量两段文字的语义相似性更感兴趣,我建议你看看this gitlab project。您可以将其作为服务器运行,还有一个预先构建的模型,您可以轻松地使用它来测量两个文本的相似性;即使它主要用于测量两个句子的相似性,你仍然可以在你的case中使用它。它是用java编写的,但你可以将它作为RESTful服务运行。

另一种选择是DKPro Similarity,它是一个具有各种算法来测量文本相似性的库。但是,它也是用java编写的。

另一答案

如果您正在寻找非常准确的东西,您需要使用比tf-idf更好的工具。 Universal sentence encoder是找到任何两段文本之间相似性的最准确的一种。 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/idf 和余弦相似度更好的文本文档聚类?

文本相似度算法

如何从TfidfVectorizer计算余弦相似度?

如何计算两个向量的余弦相似度?

如何计算 C# 中两个字符串之间的相似度?

如何计算两个文档的相似度