计算2篇文本的文本相似度(python实现)

 

引言

给定两个文本文件(比如2018年政府工作报告.txt与2019年政府工作报告.txt),怎么计算两者的相似度有多大?这是文本挖掘的一个任务,本篇将使用的方法思想如下:

  • 使用jieba包分别对两篇中文txt文件进行分词,得如['今天', '我', '遇到', '一只', '蹦蹦跳跳', '的', '兔子']的两个字符串数组;*
  • 对得到的分词后的数组通过进行词袋模型统计,得到他们每个词在文中出现的次数向量;
  • 对得到的两个次数的矩阵进行余弦相似度计算,得到余弦相似度作为它们的文本相似度。

理论知识

一、词袋模型

文档内容(以下均简称为文档)中出现频率越高的词项,越能描述该文档(不考虑停用词)。因此可以统计每个词项在每篇文档中出现的次数,即词项频率,记为

,其中t 为词项,d为文档。获得文档中每个词的 tf 权重,一篇文档则转换成了词-权重的集合,通常称为词袋模型(bag of words model)。我们用词袋模型来描述一篇文档 。词袋的含义就是说,像是把一篇文档拆分成一个一个的词条,然后将它们扔进一个袋子里。在袋子里的词与词之间是没有关系的。因此词袋模型中,词项在文档中出现的次序被忽略,出现的次数被统计。例如,“a good book” 和“book good a” 具有同样的意义。将词项在每篇文档中出现的次数保存在向量中,这就是这篇文档的文档向量。

二、余弦相似度计算

前面提出了文档向量的概念。其中每个分量代表词项在文档中的相对重要性。一系列文档在同一向量空间的表示称为 VSM(Vector Space Model)。VSM 是词袋模型。向量空间模型是信息检索、文本分析中基本的模型。通过该模型,可以进行有序文档检索、文档聚类、文档分类等。当然,现在的研究有新发展。出现了很多模型代替VSM。每篇文档在VSM中用向量表示,那么计算两篇文档的相似度自然的想到用两个向量的差值。但是,可能存在的情况是。如果两篇相似的文档,由于文档长度不一样。他们的向量的差值会很大。余弦相似度是使用的非常广泛的计算两个向量相似度的公式,它可以去除文档长度的影响。

公式的分母是两个向量的欧几里得长度之积,分子是两个向量的内积。这样计算得到的sim实际上就是两个欧式归一化的向量之间的夹角的余弦。如下图:

python实现及注释

 1 import jieba
 2 import math
 3 import re
 4 #读入两个txt文件存入s1,s2字符串中
 5 s1 = open('rdbg2018.txt','r').read()
 6 s2 = open('rdbg2019.txt','r').read()
 7 
 8 #利用jieba分词与停用词表,将词分好并保存到向量中
 9 stopwords=[]
10 fstop=open('stop_words.txt','r',encoding='utf-8-sig')
11 for eachWord in fstop:
12     eachWord = re.sub("\n", "", eachWord)
13     stopwords.append(eachWord)
14 fstop.close()
15 s1_cut = [i for i in jieba.cut(s1, cut_all=True) if (i not in stopwords) and i!='']
16 s2_cut = [i for i in jieba.cut(s2, cut_all=True) if (i not in stopwords) and i!='']
17 word_set = set(s1_cut).union(set(s2_cut))
18 
19 #用字典保存两篇文章中出现的所有词并编上号
20 word_dict = dict()
21 i = 0
22 for word in word_set:
23     word_dict[word] = i
24     i += 1
25 
26 
27 #根据词袋模型统计词在每篇文档中出现的次数,形成向量
28 s1_cut_code = [0]*len(word_dict)
29 
30 for word in s1_cut:
31     s1_cut_code[word_dict[word]]+=1
32 
33 s2_cut_code = [0]*len(word_dict)
34 for word in s2_cut:
35     s2_cut_code[word_dict[word]]+=1
36 
37 # 计算余弦相似度
38 sum = 0
39 sq1 = 0
40 sq2 = 0
41 for i in range(len(s1_cut_code)):
42     sum += s1_cut_code[i] * s2_cut_code[i]
43     sq1 += pow(s1_cut_code[i], 2)
44     sq2 += pow(s2_cut_code[i], 2)
45 
46 try:
47     result = round(float(sum) / (math.sqrt(sq1) * math.sqrt(sq2)), 3)
48 except ZeroDivisionError:
49     result = 0.0
50 print("\n余弦相似度为:%f"%result)

 

 

参考文章

 

词袋模型

余弦相似度

posted @ 2020-04-22 23:48  HankTown  阅读(7755)  评论(0编辑  收藏  举报