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

2025-01-21 09:01:00
admin
原创
82
摘要:问题描述:我正在考虑从事 NLP 项目,可以使用任何编程语言(但我更喜欢 Python)。我想要获取两份文档并确定它们有多相似。解决方案 1:常见的做法是将文档转换为 TF-IDF 向量,然后计算它们之间的余弦相似度。任何关于信息检索 (IR) 的教科书都会介绍这一点。请参阅《信息检索简介》 ,它是免费的,可...

问题描述:

我正在考虑从事 NLP 项目,可以使用任何编程语言(但我更喜欢 Python)。

我想要获取两份文档并确定它们有多相似。


解决方案 1:

常见的做法是将文档转换为 TF-IDF 向量,然后计算它们之间的余弦相似度。任何关于信息检索 (IR) 的教科书都会介绍这一点。请参阅《信息检索简介》 ,它是免费的,可在线获取。

计算成对相似度

TF-IDF(以及类似的文本转换)在 Python 包Gensim和scikit-learn中实现。在后者包中,计算余弦相似度非常简单

from sklearn.feature_extraction.text import TfidfVectorizer

documents = [open(f).read() 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

或者,如果文档是纯字符串,

>>> corpus = ["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", 
...           "The scikit-learn docs are Orange and Blue"]                                                                                                                                                                                                   
>>> vect = TfidfVectorizer(min_df=1, stop_words="english")                                                                                                                                                                                                   
>>> tfidf = vect.fit_transform(corpus)                                                                                                                                                                                                                       
>>> pairwise_similarity = tfidf * tfidf.T 

但 Gensim 可能对这类任务有更多的选择。

另请参阅此问题。

[免责声明:我参与了 scikit-learn TF-IDF 的实施。]

解释结果

从上图可以看出,是一个方形的pairwise_similarityScipy稀疏矩阵,其行数和列数等于语料库中的文档数。

>>> pairwise_similarity                                                                                                                                                                                                                                      
<5x5 sparse matrix of type '<class 'numpy.float64'>'
    with 17 stored elements in Compressed Sparse Row format>

.toarray()您可以通过或将稀疏数组转换为 NumPy 数组.A

>>> pairwise_similarity.toarray()                                                                                                                                                                                                                            
array([[1.        , 0.17668795, 0.27056873, 0.        , 0.        ],
       [0.17668795, 1.        , 0.15439436, 0.        , 0.        ],
       [0.27056873, 0.15439436, 1.        , 0.19635649, 0.16815247],
       [0.        , 0.        , 0.19635649, 1.        , 0.54499756],
       [0.        , 0.        , 0.16815247, 0.54499756, 1.        ]])

假设我们想要找到与最终文档“scikit-learn 文档为橙色和蓝色”最相似的文档。此文档在 中的索引为 4。corpus您可以通过获取该行的 argmax 来找到最相似文档的索引,但首先您需要屏蔽 1,这表示每个文档与自身的相似性。您可以通过 执行后者np.fill_diagonal(),通过 执行前者np.nanargmax()

>>> import numpy as np     
                                                                                                                                                                                                                                  
>>> arr = pairwise_similarity.toarray()     
>>> np.fill_diagonal(arr, np.nan)                                                                                                                                                                                                                            
                                                                                                                                                                                                                 
>>> input_doc = "The scikit-learn docs are Orange and Blue"                                                                                                                                                                                                  
>>> input_idx = corpus.index(input_doc)                                                                                                                                                                                                                      
>>> input_idx                                                                                                                                                                                                                                                
4

>>> result_idx = np.nanargmax(arr[input_idx])                                                                                                                                                                                                                
>>> corpus[result_idx]                                                                                                                                                                                                                                       
'I prefer scikit-learn to Orange'

注意:使用稀疏矩阵的目的是为大型语料库和词汇表节省(大量空间)。您可以执行以下操作,而不是转换为 NumPy 数组:

>>> n, _ = pairwise_similarity.shape                                                                                                                                                                                                                         
>>> pairwise_similarity[np.arange(n), np.arange(n)] = -1.0
>>> pairwise_similarity[input_idx].argmax()                                                                                                                                                                                                                  
3

解决方案 2:

与@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')

解决方案 3:

这是一个老问题,但我发现使用Spacy可以轻松完成此操作。读取文档后,similarity可以使用一个简单的 api 来查找文档向量之间的余弦相似度。

首先安装包并下载模型:

pip install spacy
python -m spacy download en_core_web_sm

然后像这样使用:

import spacy
nlp = spacy.load('en_core_web_sm')
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

解决方案 4:

如果您正在寻找非常准确的东西,您需要使用比 tf-idf 更好的工具。通用句子编码器是查找任何两段文本之间相似性的最准确工具之一。Google 提供了预训练模型,您可以将其用于自己的应用程序,而无需从头开始训练任何东西。首先,您必须安装 tensorflow 和 tensorflow-hub:

    pip install tensorflow
    pip install tensorflow_hub

下面的代码可以将任何文本转换为固定长度的向量表示,然后可以使用点积来找出它们之间的相似性

import tensorflow_hub as 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()

结果是:
文本对之间的相似度矩阵

正如你所看到的,最相似的是文本本身之间的相似性,其次是文本与意义相近的文本之间的相似性。

重要提示:第​​一次运行代码时速度会很慢,因为它需要下载模型。如果您想防止它再次下载模型并使用本地模型,您必须创建一个缓存文件夹并将其添加到环境变量中,然后在第一次运行后使用该路径:

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

解决方案 5:

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

解决方案 6:

对于句法相似性,有 3 种简单的方法来检测相似性。

  • Word2Vec

  • 手套

  • Tfidf 或 countvectorizer

对于语义相似性,可以使用 BERT 嵌入并尝试不同的词池策略来获取文档嵌入,然后在文档嵌入上应用余弦相似性。

一种先进的方法可以使用 BERT SCORE 来获得相似度。
BERT 分数

研究论文链接:https ://arxiv.org/abs/1904.09675

解决方案 7:

为了用很少的数据集找到句子相似度并获得高精度,你可以使用下面的python包,该包使用了预先训练的BERT模型,

pip install similar-sentences

解决方案 8:

这是一个可以帮助您入门的小应用程序...

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)

解决方案 9:

Simphile NLP文本相似性 Python 包的创建者。Simphile 包含几种文本相似性方法,这些方法与语言无关,并且比语言嵌入占用更少的 CPU 空间。

安装:

pip install simphile

选择您喜欢的方法。此示例显示了三种方法:

from simphile import jaccard_similarity, euclidian_similarity, compression_similarity

text_a = "I love dogs"
text_b = "I love cats"

print(f"Jaccard Similarity: {jaccard_similarity(text_a, text_b)}")
print(f"Euclidian Similarity: {euclidian_similarity(text_a, text_b)}")
print(f"Compression Similarity: {compression_similarity(text_a, text_b)}")
  • 压缩相似性——利用压缩算法的模式识别

  • 欧几里得相似度——将文本视为多维空间中的点,并计算它们的接近度

  • Jaccard Similairy – 文本中的单词重叠越多,文本就越相似

解决方案 10:

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

另一个选择是DKPro Similarity,它是一个具有多种算法的库,用于测量文本的相似度。但是,它也是用 Java 编写的。

代码示例:

// this similarity measure is defined in the dkpro.similarity.algorithms.lexical-asl package
// you need to add that to your .pom to make that example work
// there are some examples that should work out of the box in dkpro.similarity.example-gpl 
TextSimilarityMeasure measure = new WordNGramJaccardMeasure(3);    // Use word trigrams

String[] tokens1 = "This is a short example text .".split(" ");   
String[] tokens2 = "A short example text could look like that .".split(" ");

double score = measure.getSimilarity(tokens1, tokens2);

System.out.println("Similarity: " + score);

解决方案 11:

我们可以使用 sentencetransformer 来完成这个
任务

以下是来自 sbert 的一个简单示例:

from sentence_transformers import SentenceTransformer, util
model = SentenceTransformer('all-MiniLM-L6-v2')
# Two lists of sentences
sentences1 = ['The cat sits outside']
sentences2 = ['The dog plays in the garden']
#Compute embedding for both lists
embeddings1 = model.encode(sentences1, convert_to_tensor=True)
embeddings2 = model.encode(sentences2, convert_to_tensor=True)
#Compute cosine-similarities
cosine_scores = util.cos_sim(embeddings1, embeddings2)
#Output the pairs with their score
for i in range(len(sentences1)):
   print("{}          {}          Score: {:.4f}".format(sentences1[i], 
         sentences2[i], cosine_scores[i][i]))

解决方案 12:

您可能想尝试一下余弦文档相似度的在线服务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

解决方案 13:

我正在结合@FredFoo 和@Renaud 的答案。我的解决方案是能够将@Renaud 的预处理应用于@FredFoo 的文本语料库,然后显示相似度大于 0 的成对相似度。我首先在 Windows 上安装 python 和 pip,然后运行此代码。pip 是作为 python 的一部分安装的,但您可能必须通过重新运行安装包、选择修改然后选择 pip 来明确执行此操作。我使用命令行执行保存在文件“similarity.py”中的 python 代码。我必须执行以下命令:

>set PYTHONPATH=%PYTHONPATH%;C:_location_of_python_lib_
>python -m pip install sklearn
>python -m pip install nltk
>py similarity.py

similarity.py的代码如下:

from sklearn.feature_extraction.text import TfidfVectorizer
import nltk, string
import numpy as np
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]

def normalize(text):
    return stem_tokens(nltk.word_tokenize(text.lower().translate(remove_punctuation_map)))

corpus = ["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", 
           "The scikit-learn docs are Orange and Blue"]  

vect = TfidfVectorizer(tokenizer=normalize, stop_words='english')
tfidf = vect.fit_transform(corpus)   
                                                                                                                                                                                                                    
pairwise_similarity = tfidf * tfidf.T

#view the pairwise similarities 
print(pairwise_similarity)

#check how a string is normalized
print(normalize("The scikit-learn docs are Orange and Blue"))

解决方案 14:

要计算两个文本文档之间的相似度,您可以使用 Gensim 库中的 Word2Vec 模型。此模型捕获单词之间的语义关系,并可用于计算句子之间的相似度。以下是您的代码以及一些说明:

from gensim.models import Word2Vec
from gensim.utils import simple_preprocess
import numpy as np

# Define the calculate_similarity function
def calculate_similarity(sentence1, sentence2):
    # Tokenize the sentences
    tokens1 = simple_preprocess(sentence1)
    tokens2 = simple_preprocess(sentence2)

    # Load or train a Word2Vec model
    # Here, we'll create a simple model for demonstration purposes
    sentences = [tokens1, tokens2]
    model = Word2Vec(sentences, vector_size=100, window=5, min_count=1, sg=0)

    # Calculate the vector representation for each sentence
    vector1 = np.mean([model.wv[token] for token in tokens1], axis=0)
    vector2 = np.mean([model.wv[token] for token in tokens2], axis=0)

    # Calculate cosine similarity
    similarity = np.dot(vector1, vector2) / (np.linalg.norm(vector1) * np.linalg.norm(vector2))
    return similarity

# Example usage
sentence1 = "This is the first document."
sentence2 = "This document is the second document."
similarity_score = calculate_similarity(sentence1, sentence2)
print("Similarity score:", similarity_score)
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1590  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1361  
  信创产品在政府采购中的占比分析随着信息技术的飞速发展以及国家对信息安全重视程度的不断提高,信创产业应运而生并迅速崛起。信创,即信息技术应用创新,旨在实现信息技术领域的自主可控,减少对国外技术的依赖,保障国家信息安全。政府采购作为推动信创产业发展的重要力量,其对信创产品的采购占比情况备受关注。这不仅关系到信创产业的发展前...
信创和国产化的区别   18  
  信创,即信息技术应用创新产业,旨在实现信息技术领域的自主可控,摆脱对国外技术的依赖。近年来,国货国用信创发展势头迅猛,在诸多领域取得了显著成果。这一发展趋势对科技创新产生了深远的推动作用,不仅提升了我国在信息技术领域的自主创新能力,还为经济社会的数字化转型提供了坚实支撑。信创推动核心技术突破信创产业的发展促使企业和科研...
信创工作   18  
  信创技术,即信息技术应用创新产业,旨在实现信息技术领域的自主可控与安全可靠。近年来,信创技术发展迅猛,对中小企业产生了深远的影响,带来了诸多不可忽视的价值。在数字化转型的浪潮中,中小企业面临着激烈的市场竞争和复杂多变的环境,信创技术的出现为它们提供了新的发展机遇和支撑。信创技术对中小企业的影响技术架构变革信创技术促使中...
信创国产化   19  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用