【记录】文章相似度计算开发(附代码)
写在前边:目前已经通过爬虫等手段获取了千万级的文章类数据,但是目前这些数据是只是简单的基于表层的应用,相对粗粒度的统计,文本之间的很多信息并没有被良好的利用起来。为了提高数据的使用率并获取更多有用信息,尝试用常见的NLP来计算文章之间的相似度,探索文章与文章之间的关联关系,了解文章的传播范围,为后续灵活使用数据提供技术基础。
1、设计思路
1.1、 数据分析
- 数据样式上复杂。在数据探索阶段发现除了有汉语、英语,同时也有不少藏文、特殊符号构成的文章,样式上相对多样,对数据处理有更高的要求
- 数据长度相对较长,大部分都是长文本类数据,一篇文章长的有上万个词语,对数据处理时候的速度影响较大,且在词语数量过多的情况下,会有不少无效词语对整片文章造成“中庸”的影响,即钝化的文章的特征
- 文章内夹杂大量emoji符号,在对文章进行分析的时候,很难去定义符号的含义,如果不考虑这类数据的特征时,实际上有大量的信息被忽略了
- 语义分析复杂,在长文本的特征提取时,使用常规的句法分析工具,提取“主+谓+宾”之类的,作为文本的切分结果。但是句法分析的耗时比较高,适合数据量小的场景,在精度要求没那么高的时候不考虑句向量分析。
1.2、流程设计
存量数据相似度计算
新增数据相似度计算
2、前期准备
2.1、 表设计
word_vec
字段名称 字段英文名 字段类型 文章id id int 文章向量 vec varchar similarity_results
字段名称 字段英文名 字段类型 文章id id int 与id进行对比的文章id hit_id int 相似度 similarity float
2.2、 腾讯词向量包下载
考虑到有实时需求,不再通过tf-idf来提取特征。采用腾讯词向量包来计算向量
2.3、 用到的python包
gensim 提取词向量
jieba 分词
mysql.connector mysql连接器
numpy 计算包
json 解析json
time 获取时间
kafka 消费topic
sklearn 机器学习包
2.4、 MysqlBinlog实时读取
需要提前安装部署debezium+kafka,实时的获取mysql的数据
3、开发流程
3.1、导入所需的python包
from gensim.models import KeyedVectors
import jieba
import mysql.connector as mysql
import numpy as np,json
import time
from kafka import KafkaConsumer
3.2、将腾讯词向量包加载进内存,生成字典
def load_tencent_packet():starttime = time.time()print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + ":开始加载词向量包")wv_from_text = KeyedVectors.load_word2vec_format("./Tencent_AILab_ChineseEmbedding.txt", binary=False)#wv_from_text = KeyedVectors.load_word2vec_format(r"F:\5000-small.txt", binary=False)wv_from_text.init_sims(replace=True)print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + ":词向量包加载完毕")print("加载词向量包耗时:", time.time() - starttime, "秒")return wv_from_text
3.3、 创建mysql连接
def getDB():db = mysql.connect(host='192.168.5.219', user='root', passwd='123456', database='test', charset='utf8')return db
3.4、 进行中文分词:“我爱北京天安门” --> “我 爱 北京 天安门”,设置停用词并删除非中文字符
def cut_word(text,stop_list):word_list = []data_list = jieba.lcut(text.strip())for word in data_list:if word not in stop_list:if u'\u4e00' <= word <= u'\u9fff':word_list.append(word)return word_list
3.5、 在词典中找该词语,获取向量
def word2vec(word, wv_from_text, min_n=1, max_n=3):# 确认词向量维度word_size = wv_from_text.vector_size# 计算word的ngrams词组ngrams = compute_ngrams(word, min_n=min_n, max_n=max_n)# 如果在词典之中,直接返回词向量if word in wv_from_text.index_to_key:return wv_from_text[word]else:# 不在词典的情况下,计算与词相近的词向量word_vec = np.zeros(word_size, dtype=np.float32)ngrams_found = 0ngrams_single = [ng for ng in ngrams if len(ng) == 1]ngrams_more = [ng for ng in ngrams if len(ng) > 1]# 先只接受2个单词长度以上的词向量for ngram in ngrams_more:if ngram in wv_from_text.index_to_key:word_vec += wv_from_text[ngram]ngrams_found += 1# print(ngram)# 如果,没有匹配到,那么最后是考虑单个词向量if ngrams_found == 0:for ngram in ngrams_single:if ngram in wv_from_text.index_to_key:word_vec += wv_from_text[ngram]ngrams_found += 1if word_vec.any(): # 只要有一个不为0return word_vec / max(1, ngrams_found)else:#print('all ngrams for word %s absent from model' % word)return 0
3.6、 字典中不存在的词语就找对应的近义词
def compute_ngrams(word, min_n, max_n):extended_word = wordngrams = []for ngram_length in range(min_n, min(len(extended_word), max_n) + 1):for i in range(0, len(extended_word) - ngram_length + 1):ngrams.append(extended_word[i:i + ngram_length])return list(set(ngrams))
3.7、 余弦相似度计算
def cosVector(x,y):if(len(x)!=len(y)):print('error input,x and y is not in the same space')returnresult1=0.0result2=0.0result3=0.0for i in range(len(x)):result1+=x[i]*y[i] #sum(X*Y)result2+=x[i]**2 #sum(X*X)result3+=y[i]**2 #sum(Y*Y)resultnum=result1/((result2*result3)**0.5)return resultnum
3.8、 执行程序
db = getDB()
print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + ":连接数据库成功")
stop_list = get_stop_list("./stop_word.txt")
print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + ":获取停用词列表成功")
cursor = db.cursor()
count=0
insert_vec = "replace into word_vec (id,vec) Values ( %s, %s);"
consumer = KafkaConsumer('mysql.sentiment.stm_information',auto_offset_reset='earliest',bootstrap_servers=['risen-dn01:9092'])
for message in consumer:title=json.loads(message.value)["after"]["title"]message_id=json.loads(message.value)["after"]["id"]count +=1print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())," id:",message_id," 第",count,"行")word_list=cut_word(title,stop_list)vec=get_vec(word_list,wv_from_text)if vec.size < 200:content=json.loads(message.value)["after"]["content"]content_word_list=cut_word(content,stop_list)if len(content_word_list) >2000:continuevec=get_vec(content_word_list,wv_from_text)if content_vec.size < 200:continuem = [str(j) for j in vec]#将新的向量存储id_list.append(message_id)vec_list.append(vec)cursor.execute(insert_vec, [message_id, ",".join(m)])#开始计算余弦相似度allnum = 0cosnum=0insert_result="replace into similarity_results (id,hit_id,similarity) Values ( %s, %s,%s);"cos_reslut=cosine_similarity(vec_list[-30000:])re_id_list = id_list[-30000:]cos_list = cos_reslut[-1]for index, cosR in enumerate(cos_list):if 0.99 > cosR >0.85 :print(message_id,"-",re_id_list[index],"-",cosR)cursor.execute(insert_result, [message_id,re_id_list[index],float(cosR)])db.commit()#while(allnum<1000 and cosnum<2):# print(allnum)# allnum +=1# num = random.randint(0, len(vec_list)-1)# cosDU=cosVector(vec,vec_list[num])# print(message_id,id_list[num],":",cosDU,"=======",cosnum)# if 0.8<cosDU<0.99:# cosnum += 1# print("======================",cosnum,":",cosDU)
db.close
4、步骤说明
4.1、余弦相似度计算原理
在余弦定理中,两个向量间的余弦值可以通过使用欧几里得点积公式求出:
又因为余弦值的范围在[-1,1]之间,值越趋近于1,代表两个向量的方向越接近
所以将两个文本根据他们词,建立两个向量,计算这两个向量的余弦值,就可以知道两个文本在统计学方法中他们的相似度情况
4.2、 词语转化为向量
本质上就是将无法计算的文本量化成可以计算的向量,在这个过程需要有一套行之有效的计算规则。在设计之初考虑采用TF-IDF进行文本特征的抽取,但是由于可能会有实时的需求,当每来一条数据都会对向量维度造成影响的时候,就舍弃了这种方法,这里我们采用的是腾讯的词向量包。
腾讯词向量包本质上就是腾讯提供的一个 词语->向量 一一对应的数据包。
前边的汉字是词语,后边的数字即这个词语所对应的向量,该向量有200个维度,总共有8824330个词在这个包中有一一对应的向量。
在使用时只需要对文章进行分词,再把一个个词语在数据包中找到对应的向量即可实现文本的量化。
5、小结及后续优化思路
5.1、小结
- 目前的这种设计相对简陋,是因为在文本太长的情况下,分出来的词就会很多,相应的各类计算量就太大了,很多精细化的计算没办法进行,不然对效率的影响非常大,在精简了很多操作之后,现在的效率其实也不高,平均在1.5秒/条。与目前采集数据每秒数十条的速度相比还要进行调整才能达到实时要求。
- 精准度在之前的测试中,要在85%及以上的才可以看到明显的相似,因为为了提升效率,在精度上牺牲了很多,在正式进行运行的时候,因为数据量过大,没办法将每一条数据都去和所有的数据进行匹配,只能筛选一部分来计算,所以计算出来相似的这些文章不一定是最优解。
- 目前采用的是不再分析文章内容,而是从标题入手,当标题不满足计算条件的时候再考虑解析文章内容,可以明显提升效率,因为在很多时候,文章标题就是文章内容的高度总结
5.2、 后续优化思路
- 采用pyflink集群运行模式,提高并行度的情况下,满足我们目前采集速度应该是可以达到的。
- 建立近义词库,每篇文章取词频最高的几个词语,去词库中找相应的文章进行计算相似度,可以缩小匹配范围,既可以提高准确率也可以明显提升效率。
- 增加匹配不到的处理逻辑。当匹配不到相似文章的时候,推荐热度最高的文章
【记录】文章相似度计算开发(附代码)相关推荐
- 头条项目推荐的相关技术(四):离线文章画像的增量更新及离线文章相似度计算
1. 写在前面 这里是有关于一个头条推荐项目的学习笔记,主要是整理工业上的推荐系统用到的一些常用技术, 这是第四篇, 上一篇文章整理了离线文章画像的计算过程,主要包括TFIDF和TextRank两种技 ...
- 基于多重继承与信息内容的知网词语相似度计算 - 论文及代码讲解
文章目录 概念 example.py HybridSim.py howNet.py 论文:<基于多重继承与信息内容的知网词语相似度计算>-2017-张波,陈宏朝等 查看 代码:https: ...
- 基于信息内容的词林词语相似度计算 - 论文及代码讲解
文章目录 论文 同义词林简介 特点 代码 获取词的编码 求IC值 求相似度 选取相似度最大值 论文:<基于信息内容的词林词语相似度计算 >-2018-彭琦,朱新华等 查看 代码:https ...
- 绝对不容错过:最完整的检测模型评估指标mAP计算指南(附代码)在这里!
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 前言 本文翻译自Measuring Object Detectio ...
- word2vec的应用:gensim相似度检测(附代码)
上篇写的word2vec的相关算法,这篇附上代码.其中我们的语料是小说"人民的名义",百度云盘:https://pan.baidu.com/s/1ggA4QwN 首先是进行分词: ...
- php文章相似度计算,PHP相似度算法
写了很久忘保存了,囧没了,先放这里 catalogue 1. TF-IDF2. 基于空间向量的余弦算法3. 最长公共子序列4. 最小编辑距离算法5. similar_text 1. TF-IDF Re ...
- Word2Vec原理及应用与文章相似度(推荐系统方法)
Word2Vec与文章相似度(推荐系统方法) 学习目标 目标 知道文章向量计算方式 了解Word2Vec模型原理 知道文章相似度计算方式 应用 应用Spark完成文章相似度计算 1 文章相似度 在我们 ...
- python-文章相似度计算
python-文章相似度计算 编写一个程序,设计实现以下函数并实现整体功能(文章相似度计算): 1.0 word_input_file(file):输入文本文件路径(如input.txt),返回该文档 ...
- SAP ABAP 开发管理 代码内存标记 位置使用清单(Mark of memory id)
SAP ABAP 开发管理 代码内存标记 位置使用清单(Mark of memory id) 引言: 代码内存标记(Mark of memory id)是我开发中对 ABAP MEMORY ID 使用 ...
最新文章
- Mediasoup简介及其基本概念
- 个人项目的设计与分析——类饿了么、美团式订餐类校园食堂版App“加个蛋”。...
- STL源码剖析面试问题
- jps、jstack、jmap、jhat、jstat、hprof使用详解
- ECCV 2020 论文大盘点-视频目标分割篇
- sumk 2.0.0 发布,轻量级互联网框架
- 风压和功率计算公式轴流式_这是你见过最全的风机计算公式
- linux 所有文件大小排序,linux 根据文件大小排序
- 以太网驱动详解之 MAC、MII、PHY 详解
- matplotlib添加字体、字体格式自定义
- [转] 彼得林奇:最珍贵的成长股投资精髓
- excel学习-日期计算函数DATEDIF函数(计算相隔年数、月数、天数)
- Vue打开外部链接问题
- Unity3D 鼠标选择物体高光显示信息
- 数据库创建表以及增删改查
- 蓝牙资讯|苹果 AirPods Pro 2正式发布,有惊喜也有遗憾
- java/php/net/python“最终幻想14”游戏时装图鉴网站设计与制作
- 在群晖 DSM 7.0 系统上如何安装 Plex Media Server
- mac怎么压缩pd文件最小
- 对麦克斯韦方程组的理解(非常详细)