余弦相似度,又称为余弦相似性,是通过计算两个向量的夹角余弦值来评估向量间的相似度。
计算两个n维向量夹角余弦值的计算公式为:

其中A*B是A和B向量的点积,|A|*|B|是向量A和B模长的积。
1、相似度分析程序编码
余弦相似度分析工作流程图如下:

多文档词袋数据已经建立,待分析的文档数据需要进行中文分词,形成单文档词袋数据。相似性分析程序对传入的单文档词袋数据,与多文档词袋数据进行逐一分析,并输出一个数组,数组的元素为单文档词袋数据与比对文档词袋数据的夹角余弦值。
在项目根目录下建立文件夹similarities,在similarities文件夹下建立similarity.py文件。代码如下:
""" 模块:相似性分析 功能: 使用余弦相似度分析文档间的相似度 """ import numpy as np class Similarity(): def __init__(self,corpus): # corpus为多文档词袋数据 self.corpus = corpus # 与多文档词袋数据逐一分析 # text 待分析的单文档词袋数据 def one_analysis(self,text): # 存储相似度 sim = [] v_text = self.bow2Vector(text) for item in self.corpus: v_item = self.bow2Vector(item) value = self.consine(np.array(v_text),np.array(v_item)) sim.append(value) return sim # 词袋模型抽取为向量 def bow2Vector(self,bow): vect = [] for m in bow: vect.append(m[1]) return vect # 计算两个向量间夹角的余弦值 def consine(self,v,mv): # 计算点积 dot = np.dot(v,mv) # 计算向量v的模长 ma = np.linalg.norm(v) # 计算向量mv的模长 mb = np.linalg.norm(mv) # 计算向量v和mv的相似度 return dot/(ma*mb)
Similarity类的corpus为多文档词袋数据,在类初始化时被赋值。one_analysis()方法采用逐一比对的方式计算向量间夹角的余弦值,该算法效率比较低下,为提高计算效率,应采用矩阵运算,后面会采用矩阵运算,进行计算效率的比对分析。
bow2Vector()方法是从词袋数据抽取向量,词袋数据的元素是一个二元组,分别存储单词的ID和词频,该方法抽取词频构成词频向量。
consine()方法计算两个向量间夹角的余弦值,该值作为两个文档之间的相似度。
若待分析的文档没有添加到字典,需要在字典中添加文档并更新字典数据。在Dictionary类添加update_doc2bow()方法,该方法添加新文档到字典,并返回词袋数据。方法代码如下:
# 更新字典并输出词袋数据 # text为待分析单文档 def update_doc2bow(self,text): texts =[text] # 更新字典 self.add_documents(texts) vect = [] # 遍历字典全部单词 for key in self.token2id.keys(): if key in text: ''' 若text包含字典单词,创建二元组(数字ID,词频) 添加二元组到词袋 ''' vect.append((self.token2id[key], text.count(key))) else: ''' 若text不包含字典单词,创建二元组(数字ID,0) 添加二元组到词袋 ''' vect.append((self.token2id[key], 0)) # 词袋数据按二元组的数字ID排序 vect = sorted(vect, key= lambda x: x[0]) return vect
2、验证相似度分析程序
编写一个测试程序,从数据库读取100条新闻条目,建立字典和多文档词袋数据,从中抽取任意1条新闻条目进行相似性分析,并输出整个分析过程耗费的时间。在项目的test目录下建立simtest.py文件。代码如下:
# 相似性分析测试程序
#导入db模块#
from db import readnews
#导入路径模块
from tool import participle as fc
# 导入csv模块
import csv
#导入路径模块
import tool.filepath as path
#导入字典模块
from corpora.dictionary import Dictionary
#导入分析模块
from similarities.similarity import Similarity
#导入时间模块
import datetime
# 计算时间间隔
def get_time_differ(t1,t2):
duringtime = t2 - t1
return duringtime.total_seconds()
# 程序入口
if __name__ == '__main__':
starttime = datetime.datetime.now()
# 读取前100条新闻条目
t1 = datetime.datetime.now()
data = readnews.query_database_record_limit(0,100)
t2 = datetime.datetime.now()
runsecond = get_time_differ(t1,t2)
print("从读取数据库100条新闻条目,耗时:%.3f秒" % (runsecond))
if data == None:
print("数据库读取发生错误")
else:
texts = []
t1 = datetime.datetime.now()
# 中文分词
for text in data:
words = fc.get_particlple(text[1])
texts.append(words)
t2 = datetime.datetime.now()
runsecond = get_time_differ(t1,t2)
print("中文分词,耗时:%.3f秒" % (runsecond))
# 创建字典
dictionary = Dictionary()
t1 = datetime.datetime.now()
# 添加文档到字典
dictionary.add_documents(texts)
t2 = datetime.datetime.now()
runsecond = get_time_differ(t1,t2)
print("建立字典,耗时:%.3f秒" % (runsecond))
# 提取第1条语料数据进行相似性分析
textwords = texts[0]
t1 = datetime.datetime.now()
simttext = dictionary.update_doc2bow(textwords)
# 从字典读取词袋数据
croups = dictionary.doc2bow(texts)
t2 = datetime.datetime.now()
runsecond = get_time_differ(t1,t2)
print("转换词袋数据,耗时:%.3f秒" % (runsecond))
# 初始化相似性分析类
docsim = Similarity(croups)
# 采用文档逐一分析方式
t1 = datetime.datetime.now()
sim = docsim.one_analysis(simttext)
t2 = datetime.datetime.now()
runsecond = get_time_differ(t1,t2)
print("相似性分析,耗时:%.3f秒" % (runsecond))
print(sim)
endtime = datetime.datetime.now()
runsecond = get_time_differ(starttime,endtime)
print("总耗时:%.3f秒" % (runsecond))测试程序从数据库读取100条新闻条目,并进行中文分词,分词完成后创建字典,调用字典对象的doc2bow()方法获取词袋数据。取第1条新闻条目作为待分析的新文档,调用字典对象的update_doc2bow()方法更新字典并获取新文档的词袋数据,使用Similarity类对新文档进行相似性分析,输出结果为新文档与原文当的相似度。