相似
测量两个txt文件之间的相似度(Python)
入门
Resemblance 适用于Python 3+和 Django 2+。
安装依赖项:
python3 -m pip3 install -r requirements.txt
然后运行以下命令:
python3 manage.py makemigrations sim
python3 manage.py migrate
python3 manage.py runserver
这篇文章与OnePublish交叉发布
在本文中,我们将构建一个 Web 应用程序,用于比较两个文档的相似度。我们将学习自然语言处理 (NLP) 的基础知识,它是人工智能的一个分支,致力于利用自然语言实现计算机与人类之间的交互。
这篇文章最初发表在我的实验室Reverse Python中。
让我们先从程序的基本结构开始,然后添加图形界面,使程序更易于使用。欢迎在我的 GitHub 上贡献此项目。
自然语言工具包 (NLTK)是最流行的自然语言处理 (NLP) 库,它用 Python 编写,并拥有庞大的社区支持。NLTK 也非常容易学习,实际上,它是我们将要使用的最简单的自然语言处理 (NLP) 库。它包含用于标记化、解析、分类、词干提取、标记和语义推理的文本处理库。
Gensim被标榜为一款“为人类进行主题建模”的自然语言处理软件包。但实际上,它的功能远不止于此。它是一个领先且最先进的文本处理软件包,能够与词向量模型(例如 Word2Vec、FastText 等)协同工作。
主题模型和词向量在 scikit、R 等其他软件包中也可用。但 gensim 构建和评估主题模型的功能广度和范围是无与伦比的,此外还有许多更便捷的文本处理功能。gensim 的另一个重要优势是,它允许您管理大型文本文件,而无需将整个文件加载到内存中。
首先,让我们通过以下命令安装 nltk 和 gensim:
pip install nltk
pip install gensim
我们使用 word_tokenize() 方法将句子拆分成单词。请看下面的示例
from nltk.tokenize import word_tokenize
data = "Mars is approximately half the diameter of Earth."
print(word_tokenize(data))
输出:
['Mars', 'is', 'approximately', 'half', 'the', 'diameter', 'of', 'Earth']
你可能会问,既然有单词分词,为什么还需要句子分词呢?我们需要计算每个句子的平均单词数,所以为了完成这项任务,我们除了单词分词外,还会使用句子分词来计算比例。
from nltk.tokenize import sent_tokenize
data = "Mars is a cold desert world. It is half the size of Earth. "
print(sent_tokenize(data))
输出:
['Mars is a cold desert world', 'It is half the size of Earth ']
现在,您知道这些方法在处理文本分类时非常有用。让我们在相似度算法中实现它。
创建一个 .txt 文件,并在其中写入 4-5 个句子。将该文件放在与 Python 程序相同的目录中。现在,我们将使用 Python 打开此文件并拆分句子。
import nltk
from nltk.tokenize import word_tokenize, sent_tokenize
file_docs = []
with open ('demofile.txt') as f:
tokens = sent_tokenize(f.read())
for line in tokens:
file_docs.append(line)
print("Number of documents:",len(file_docs))
程序将打开文件并读取其内容。然后将已标记的句子添加到数组中,以进行单词标记。
一旦我们在数组中添加了标记化的句子,就该为每个句子标记化单词了。
gen_docs = [[w.lower() for w in word_tokenize(text)]
for text in file_docs]
输出:
[['mars', 'is', 'a', 'cold', 'desert', 'world', '.'],
['it', 'is', 'half', 'the', 'size', 'of', 'earth', '.']]
为了处理文本文档,Gensim 要求将单词(也称为 token)转换为唯一的 ID。因此,Gensim 允许您创建一个 Dictionary 对象,将每个单词映射到一个唯一的 ID。让我们将句子转换为 [单词列表],并将其传递给 corpora.Dictionary() 对象。
dictionary = gensim.corpora.Dictionary(gen_docs)
print(dictionary.token2id)
输出:
{'.': 0, 'a': 1, 'cold': 2, 'desert': 3, 'is': 4, 'mars': 5,
'world': 6, 'earth': 7, 'half': 8, 'it': 9, 'of': 10, 'size': 11, 'the': 12}
字典将每个单词映射到一个数字。Gensim 允许您逐行读取文本并更新字典,而无需将整个文本文件加载到系统内存中。
为了在 gensim 中工作,你需要熟悉的下一个重要对象是语料库(Corpus,一个词袋)。它是一个包含单词 ID 及其在每个文档中的频率(仅列出每个单词在句子中出现的次数)的基本对象。
请注意,“token”通常表示“单词”。“document”通常指“句子”或“段落”,而“corpus”通常是“作为词袋的文档集合”。
现在,创建一个词袋语料库,并将标记化的单词列表传递给 Dictionary.doc2bow()
假设我们的文档是:
Mars is a cold desert world. It is half the size of the Earth.
corpus = [dictionary.doc2bow(gen_doc) for gen_doc in gen_docs]
输出:
{'.': 0, 'a': 1, 'cold': 2, 'desert': 3, 'is': 4,
'mars': 5, 'world': 6, 'earth': 7, 'half': 8, 'it': 9,
'of': 10, 'size': 11,'the': 12}
[[(0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1)],
[(0, 1), (4, 1), (7, 1), (8, 1), (9, 1), (10, 1), (11, 1), (12, 2)]]
如您所见,我们在第二句中使用了两次“the”,如果您查看 id=12 的单词(the),您会发现它的频率是 2(在句子中出现 2 次)
词频-逆文档频率 (TF-IDF) 也是一个词袋模型,但与常规语料库不同,TFIDF 会降低在文档中频繁出现的标记(单词)的权重。
TF-IDF 的计算方法是将局部分量 (TF) 与全局分量 (IDF) 相乘,并根据需要将结果归一化为单位长度。词频是指单词在文档中出现的频率,而逆文档频率则根据单词在语料库中的稀缺程度来调整其值。简而言之,在文档中出现频率较高的单词获得的权重较小。
This is the space. This is our planet. This is the Mars.
tf_idf = gensim.models.TfidfModel(corpus)
for doc in tfidf[corpus]:
print([[dictionary[id], np.around(freq, decimals=2)] for id, freq in doc])
输出:
[['space', 0.94], ['the', 0.35]]
[['our', 0.71], ['planet', 0.71]]
[['the', 0.35], ['mars', 0.94]]
“the”一词出现在两份文件中,因此权重降低。“this”和“is”一词出现在三份文件中,因此被全部删除。
现在,我们将创建相似度对象。其主要类是 Similarity,它为给定的一组文档构建索引。Comparisonity 类将索引拆分为几个较小的子索引,这些子索引基于磁盘。我们先创建相似度对象,然后您就会明白如何使用它进行比较。
# building the index
sims = gensim.similarities.Similarity('workdir/',tf_idf[corpus],
num_features=len(dictionary))
我们将索引矩阵存储在“workdir”目录中,但您可以随意命名它,当然您必须使用程序的同一目录来创建它。
索引建立后,我们将计算此查询文档与索引中每个文档的相似度。因此,创建第二个 .txt 文件,其中包含查询文档或句子,并像之前一样对其进行分词。
file2_docs = []
with open ('demofile2.txt') as f:
tokens = sent_tokenize(f.read())
for line in tokens:
file2_docs.append(line)
print("Number of documents:",len(file2_docs))
for line in file2_docs:
query_doc = [w.lower() for w in word_tokenize(line)]
query_doc_bow = dictionary.doc2bow(query_doc) #update an existing dictionary and
create bag of words
我们获得了新的文档(查询文档或句子),因此可以更新现有的词典以包含新词。
在此阶段,您将看到查询与所有索引文档之间的相似性。要获取查询文档与索引文档的相似性,请执行以下操作:
# perform a similarity query against the corpus
query_doc_tf_idf = tf_idf[query_doc_bow]
# print(document_number, document_similarity)
print('Comparing Result:', sims[query_doc_tf_idf])
余弦度量返回范围 <-1, 1> 内的相似度(越大,越相似)。
假设我们的文件是:
Mars is the fourth planet in our solar system.
It is second-smallest planet in the Solar System after Mercury.
Saturn is yellow planet.
查询文档为:
Saturn is the sixth planet from the Sun.
输出:
[0.11641413 0.10281226 0.56890744]
结果,我们可以看到第三个文档最相似
下一步是什么?我认为最好计算查询文档的平均相似度。现在,我们将导入 numpy 来计算这些相似度输出的总和。
import numpy as np
sum_of_sims =(np.sum(sims[query_doc_tf_idf], dtype=np.float32))
print(sum_of_sims)
Numpy 将帮助我们计算这些浮点数的总和,输出为:
# [0.11641413 0.10281226 0.56890744]
0.78813386
为了计算平均相似度,我们必须用文档数量除以该值
percentage_of_similarity = round(float((sum_of_sims / len(file_docs)) * 100))
print(f'Average similarity float: {float(sum_of_sims / len(file_docs))}')
print(f'Average similarity percentage: {float(sum_of_sims / len(file_docs)) * 100}')
print(f'Average similarity rounded percentage: {percentage_of_similarity}')
输出:
Average similarity float: 0.2627112865447998
Average similarity percentage: 26.27112865447998
Average similarity rounded percentage: 26
现在,我们可以说查询文档(demofile2.txt)与主文档(demofile.txt)有 26%的相似度
如果我们有多个查询文档怎么办?
作为解决方案,我们可以计算每个查询文档的平均值总和,它将为我们提供总体相似度百分比。
假设我们的主要文档是:
Malls are great places to shop, I can find everything I need under one roof.
I love eating toasted cheese and tuna sandwiches.
Should we start class now, or should we wait for everyone to get here?
顺便说一下,我使用随机词生成器工具来创建这些文档。我们的查询文档如下:
Malls are goog for shopping. What kind of bread is used for sandwiches? Do we have to start class now, or should we wait for
everyone to come here?
我们来看看代码:
avg_sims = [] # array of averages
# for line in query documents
for line in file2_docs:
# tokenize words
query_doc = [w.lower() for w in word_tokenize(line)]
# create bag of words
query_doc_bow = dictionary.doc2bow(query_doc)
# find similarity for each document
query_doc_tf_idf = tf_idf[query_doc_bow]
# print (document_number, document_similarity)
print('Comparing Result:', sims[query_doc_tf_idf])
# calculate sum of similarities for each query doc
sum_of_sims =(np.sum(sims[query_doc_tf_idf], dtype=np.float32))
# calculate average of similarity for each query doc
avg = sum_of_sims / len(file_docs)
# print average of similarity for each query doc
print(f'avg: {sum_of_sims / len(file_docs)}')
# add average values into array
avg_sims.append(avg)
# calculate total average
total_avg = np.sum(avg_sims, dtype=np.float)
# round the value and multiply by 100 to format it as percentage
percentage_of_similarity = round(float(total_avg) * 100)
# if percentage is greater than 100
# that means documents are almost same
if percentage_of_similarity >= 100:
percentage_of_similarity = 100
输出:
Comparing Result: [0.33515707 0.02852172 0.13209888]
avg: 0.16525922218958536
Comparing Result: [0. 0.21409164 0.27012902]
avg: 0.16140689452489218
Comparing Result: [0.02963242 0. 0.9407785 ]
avg: 0.3234703143437703
我们有3个查询文档,程序计算了每个文档的平均相似度。如果我们计算这些值,结果将是:
0.6501364310582478
我们将该值乘以 100,然后进行四舍五入,将其格式化为百分比,以使值更简单。Django 的最终结果如下:
太棒了!希望你从这个项目中学到了一些 NLP 的基础知识。此外,我用 Django 实现了这个算法,用于创建图形界面。欢迎在我的 GitHub 上贡献项目。
测量两个txt文件之间的相似度(Python)
Resemblance 适用于Python 3+和 Django 2+。
安装依赖项:
python3 -m pip3 install -r requirements.txt
然后运行以下命令:
python3 manage.py makemigrations sim
python3 manage.py migrate
python3 manage.py runserver
希望你从这次实验中有所收获😃。如果你觉得有用,请分享并在社交媒体上关注我!一如既往,保持联系!🚀
另请参阅反向 Python
参考:
文章来源:https://dev.to/thedevtimeline/compare-documents-similarity-using-python-nlp-4odp