文档章节

推荐系统技术之文本相似性计算(三)

wyh817
 wyh817
发布于 2017/01/16 11:45
字数 4642
阅读 319
收藏 1

前面说了两篇了,分别介绍了TFIDF和向量空间的相关东西,然后介绍了主题模型,这一篇我们就来试试这两个东西。词向量就不在这篇试了,词向量和这两个关系不大,不好对比,不过我最后也给出了代码。

0. 工具准备

工欲善其事,必先利其器,那么我们先来利其器,这里我们使用的是python的gensim工具包,地址是:https://radimrehurek.com/gensim/index.html,这个工具包很强大,我就不一一介绍了,反正我们需要的功能都有,而且我们用得很简单,它还可以分布式部署,感兴趣可以去官网看具体介绍。

为什么不自己写?这个问题....呵呵.....呵呵....我写不出来.....

至于安装,需要先安装python 2.6以上(废话),NumPy 1.3以上,SciPy 0.7以上,后两个是python的科学计算的包。

easy_install很容易搞定,这里就不废话了,windows上安装可能有点困难,但我很久没用过windows了,我电脑上安装很轻松,三四个命令搞定,可以去看gensim的官方文档,上面也有怎么安装,如果你装都装不上,那就google,百度,总有解决办法。

除了gensim,还有个分词的包需要装一下,就是jieba分词,这个也很容易装。

1. 数据准备

数据准备可是个技术活,我的职业操守很高,没有用公司的数据,那只能自己找数据了,如果直接找网上的语料,显得太Low了。于是我自己爬了一些数据。

首先,我瞄准了目前全国最火的全栈技术网站(就是SegmentFault啦),然后瞄准了一个汽车网站,于是开始爬数据,自己写了个爬虫开始爬数据,恩,我的爬虫我觉得还可以,调度器+爬取器组成,爬取器插件化了,可以使用任意语言做编写,甚至可以直接对接chrome爬取纯JS单页面网站爬取,也支持代理池,如果大家感兴趣我也可以说说爬虫相关的东西,分布式的哦,可以随便加机器增加爬取能力。

好了,八卦完了,爬了两个网站,可以开始干活了,爬两个类型的网站是为了说明后面LDA主题模型,大家就有个认识了。

2. 数据清理

数据爬下来后,要做的就是数据清洗工作了,我之前有一篇搞机器学习的技能说了,数据的清理是一个算法工程师的必备技能,如果没有好的数据,算法怎么好都没用。

拿到数据以后,写个脚本

  • 首先把标题,作者,时间之类的提取出来,通过正则也好,xpath也好,都很容易把这些东西提取出来。
  • 然后把html标签干掉,一堆正则就行了,剩下的基本上就是正文了,另外,SF站的东西还特殊处理了一下,把<code></code>中的内容干掉了,一堆代码对我来说没什么用。
  • 最后,把标点符号干掉,把特殊符号干掉,调整一下格式,最后的每一篇文章都变成下面的样子

ID(实际上是url)[TAB]TITLE(标题)[TAB]CONTENT(文章详情)

一共有11628篇文章,其中汽车类大约6000,技术类(SF站)大约6000,好了,数据也基本上清洗好了。

4. 训练数据

都觉得这一节才是重点,其实有jieba分词和gensim以后,代码非常简单,不超过50行,我们来一步一步玩。

4.1 分词--建立词典--准备数字语料

分词是基础,首先进行分词

from gensim import corpora,models,similarities,utils
import jieba
import jieba.posseg as pseg
jieba.load_userdict( "user_dic.txt" ) #载入自定义的词典,主要有一些计算机词汇和汽车型号的词汇
#定义原始语料集合
train_set=[]
f=open("./data/all.txt")
lines=f.readlines()
for line in lines:
    content = (line.lower()).split("\t")[2] + (line.lower()).split("\t")[1]
    #切词,etl函数用于去掉无用的符号,cut_all表示非全切分
    word_list = filter(lambda x: len(x)>0,map(etl,jieba.cut(content,cut_all=False)))
    train_set.append(word_list)
f.close()

得到的tain_set就是原始语料了,然后对这些语料导入到词典中,建立一个词典。

#生成字典
dictionary = corpora.Dictionary(train_set)
#去除极低频的杂质词
dictionary.filter_extremes(no_below=1,no_above=1,keep_n=None)
#将词典保存下来,方便后续使用
dictionary.save(output + "all.dic")

将语料导入词典后,每个词实际上就已经被编号成1,2,3....这种编号了,这是向量化的第一步,然后把词典保存下来。 然后生成数字语料

corpus = [dictionary.doc2bow(text) for text in train_set]

这一句表示把每一条原始数据向量化成编号,这样以后,corpus这个变量是个二维数据,每一行表示一个文档的每个词的编号和词频,每一行像这样

[(1,2),(2,4),(5,2)....] 表示编号为1的词出现了2次,编号为2的词出现了4次....

OK,前期准备OK了,原始文章通过切词-->建立词典-->生成语料后已经被我们数字化了,后面就简单了。

4.1 TFIDF模型

有了数字语料以后,我们可以生成一个TFIDF模型

#使用数字语料生成TFIDF模型
tfidfModel = models.TfidfModel(corpus)
#存储tfidfModel
tfidfModel.save(output + "allTFIDF.mdl")

这一句是关键,我们用了原始的数字语料,生成了一个TFIDF模型,这个模型能干什么呢?gensim重载了[]操作符,我们可以用类似[(1,2),(2,4),(5,2)....]的原始向量传进去,变成一个tfidf的向量,像这样[(1,0.98),(2,0.23),(5,0.56)....],这样就说明编号1的词的重要性比后面两个词都要大,这个向量可以作为后面LDA的原始向量输入。

然后我们把所有的语料都TFIDF向量化,并作为一个索引数据存起来方便以后查找的时候使用

#把全部语料向量化成TFIDF模式,这个tfidfModel可以传入二维数组
tfidfVectors = tfidfModel[corpus]
#建立索引并保存
indexTfidf = similarities.MatrixSimilarity(tfidfVectors)
indexTfidf.save(output + "allTFIDF.idx")

好了,TFIDF部分完了,先记下来,我们生成了一个模型数据(allTFIDF.mdl),生成了一份全部语料的TFIDF向量的索引数据(allTFIDF.idx),加上上面的词典数据(all.dic),我们现在有三份数据了,后面再说怎么用,现在先继续LDA部分。

4.2 LDA模型

LDA上一篇讲了那么多,在gensim看来就是下面几行代码,而且使用了传说中的机器学习哦。只能说gensim的代码封装得太简洁了。

#通过TFIDF向量生成LDA模型,id2word表示编号的对应词典,num_topics表示主题数,我们这里设定的50,主题太多时间受不了。
lda = models.LdaModel(tfidfVectors, id2word=dictionary, num_topics=50)
#把模型保存下来
lda.save(output + "allLDA50Topic.mdl")
#把所有TFIDF向量变成LDA的向量
corpus_lda = lda[tfidfVectors]
#建立索引,把LDA数据保存下来
indexLDA = similarities.MatrixSimilarity(corpus_lda)
indexLDA.save(output + "allLDA50Topic.idx")

虽然只有这三步,但是还是挺耗时的,在log打开的情况下可以看到处理过程,我随便截取了几个,像下面一样,很明显,前面几个主题都和汽车相关,后面几个主题都和技术相关,看样子还算比较靠谱的。

#38 (0.020): 0.003*新奇 + 0.003*骏 + 0.002*途安 + 0.002*配备 + 0.002*都市 + 0.001*除 + 0.001*昂科威
#27 (0.020): 0.003*配置 + 0.003*内饰 + 0.003*车型 + 0.002*气囊 + 0.002*瑞风 + 0.002*万元 + 0.002*逸致 +
#0 (0.020): 0.004*奔腾 + 0.003*加速 + 0.003*嘉年华 + 0.002*油门 + 0.002*爱丽舍 + 0.002*秒
#49 (0.020): 0.004*瑞虎 + 0.004*绅宝 + 0.004*欧诺 + 0.002*雷克萨斯 + 0.002*车型 + 0.002*乐途 
#26 (0.020): 0.011*列表 + 0.009*流 + 0.007*快捷键 + 0.006*崩溃 + 0.002*大神 + 0.002*混淆 + 0.002*邮箱
#21 (0.020): 0.035*命令 + 0.018*浏览器 + 0.007*第三方 + 0.007*安装 + 0.006*控制台 
topic #25 (0.020): 0.064*文件 + 0.004*约束 + 0.004*练习 + 0.003*复制到 + 0.003*就行了 + 0.003*反编译

好了,LDA部分也完了,又多了两个文件allLDA50Topic.mdlallLDA50Topic.idx,加上前面的3个,一共5个文件了,OK,休息一下,喝杯可乐,继续下一步。

5. 验证结果

好了,第四部分中不知不觉我们已经使用机器学习这么高端的东西了,那现在要验证一下这么高端的东西到底效果如何了。

前面的TFIDF和LDA我们都保存了模型和向量数据,那么我们就用两篇新的文章,来看看和这篇文章最相似的文章都有哪些来验证这两个模型靠谱不靠谱吧。

我随便打开一个汽车网站,选了一篇汽车的文章(宝马的评测),再找了我之前的一篇技术的文章(讲搜索引擎的),并且只随机截取了文章的一段进行测试。

看开头这应该是一篇为全新宝马X1 Li(下文简称新X1)洗地的文章,我想很多宝马死忠、车神也已经准备移步评论........

一般情况下,搜索引擎默认会认为索引是不会有太大的变化的,所以把索引分为全量索引和增量索引两部分,全量索引一般是以天.......

好,文章选好了,先载入之前保存的数据文件

#载入字典
dictionary = corpora.Dictionary.load(output + "all.dic")
#载入TFIDF模型和索引
tfidfModel = models.TfidfModel.load(output+"allTFIDF.mdl")
indexTfidf = similarities.MatrixSimilarity.load(output + "allTFIDF.idx")
#载入LDA模型和索引
ldaModel = models.LdaModel.load(output + "allLDA50Topic.mdl")
indexLDA = similarities.MatrixSimilarity.load(output + "allLDA50Topic.idx")

然后把测试数据进行切词TFIDF向量化找相似LDA向量化找相似

#query就是测试数据,先切词
query_bow = dictionary.doc2bow(filter(lambda x: len(x)>0,map(etl,jieba.cut(query,cut_all=False))))
#使用TFIDF模型向量化
tfidfvect = tfidfModel[query_bow]
#然后LDA向量化,因为我们训练时的LDA是在TFIDF基础上做的,所以用itidfvect再向量化一次
ldavec = ldaModel[tfidfvect]
#TFIDF相似性
simstfidf = indexTfidf[tfidfvect]
#LDA相似性
simlda = indexLDA[ldavec]

好了,结束,所有代码就这么些了。太简单了。。。。我们来看看效果。

6 输出结果

我们先看TFIDF的结果

  • 汽车的测试文章TFIDF结果(前10结果中随机选3个)

优惠购车推荐宝马x3优惠3.5-7万元 保时捷macan竞争力分析宝马x3 宝马2014年新车展望多达十余款新车

  • 技术的测试文章TFIDF结果(前10结果中随机选3个)

用golang写一个搜索引擎(0x06) 索引那点事 [搜索引擎] sphinx 的介绍和原理探索

很明显,结果基本都比较靠谱,第一个基本是说宝马车的,第二个基本都在说搜索引擎和索引。 我们再看看LDA的结果,LDA的主要功能是文本分类而不是关键词的匹配,就是看测试文章分类分得对不对,我们这里基本上是两类文章,一类技术文章,一类汽车文章,所以我们通过找和测试文章最相似的文章,然后看看找出来最相似的文章是不是正好都是技术类的或者汽车类的,如果是,表示模型比较好。

  • 汽车的测试文章LDA结果(前10结果中随机选3个)

编辑心中最美中级车一汽-大众新cc 25万时尚品质4款豪华紧凑车之奔驰a级 iphone手机html5上传图片方向问题解决

  • 技术的测试文章LDA结果(前10结果中随机选3个)

java 多线程核心技术梳理(附源码) springsession原理解析 并发中的锁文件模式

从结果看,基本比较靠谱,但汽车那个出现了一个badcaseiphone手机html5上传图片方向问题解决,这是篇技术文章,但是出现在了汽车类上面。

7. 结果分析

我们来分析一下这个结果,对于TFIDF模型,在现有数据集(12000篇文章)的情况下,推荐结果强相关,让人觉得推荐结果很靠谱,这也是TFIDF这种算法简单有效的地方,他把文章中的关键词很好的提取出来了,所以推荐的结果让人觉得强相关,但是他也有自己的问题。

  • 对于比较短的文章(比如微博这类的),由于文本太短了,TFIDF比较难提取出重要的关键词或者提取得不对,导致推荐结果不靠谱。
  • 单纯以词频来说明这个词的重要性感觉不全面,比如这篇文章,人类来看的话应该是文本相似性最重要,但有可能按TFIDF算出来是模型这个词最重要。 对于纯文本的推荐系统来说,这种文本相关性的推荐可能比较适合垂直类的网站,比如像SegmentFault这种,看某篇文章的人很可能希望看到类似的文章,更深入的了解这个领域,这种算法比较靠谱,不过据我观察,SegmentFault是使用的标签推荐,这种推荐效果更好,但人为因素更多点,要是我写文章的时候随便打标签就比较麻烦了。

再来看看LDA模型,LDA主要用在文本聚类上,而且他的基础是主题,如果把他作为推荐系统的算法来使用,要看具体场景,他的推荐结果在数据样本不太够的情况下,可能看上去不太靠谱(即便样本大可能也看上去不太好),显得粒度很粗,但正因为很粗,所以比较适合做内容发现,比如我对数码新闻感兴趣,这种感兴趣不仅仅是只对iphone感兴趣,只要是数码这个主题的我都感兴趣,所以用LDA可以很好的给我推荐数码这个主题下的东西,这比正在看iphone的文章,下面全是iphone的文章要靠谱多了。

LDA出现上一节的哪种badcase的时候怎么办呢?因为基本不太可能改模型,那么只能从几个方面入手。

  • 如果只是偶尔的一两个,可以选择忍了。
  • 如果多的话,那只能先调一调主题个数,然后LDA里面有些个参数可以调调(算法工程师的价值所在啊)
  • 还有一条路子就是把输入的数据尽可能的清洗干净,把无用的杂质去掉(算法工程师必备技能耐心和细心) 所以,不同的模型对于不同的场景是很重要的,根据你的场景选择合适的模型才能达到合适的效果。

8. 写在后面的话

这篇文章只是一个文本相似性的最最基本的文章,可以最直观的了解一下TFIDF模型和LDA模型,同时,也使用了目前最热的机器学习技术哦。

其实,像LDA,以及word2vec这种模型,已经是被数学抽象得很强的模型了,和实际场景基本上已经脱离了,已经完全数学化了,所以其实不一定要用在文本处理上,在流量分析,用户行为分析上一样有用,这就是算法工程师要想的事情,一个好的算法如何用在现有的场景中。

试想一下,如果我们想给我们的用户分个类,看看哪些用户兴趣比较相似。我们其实可以这么来做:

  • 首先,如果我们有一堆用户的浏览行为数据,每一条数据记录了用户点击某个链接,或者点击了某个按钮。
  • 把这些浏览行为按照用户维度进行合并,那么新数据中每一条数据就是一个用户的操作记录,按顺序就是他几点几分有什么行为。类似于用户A :[浏览了a页面,点击了b按钮,浏览了c页面....]
  • 好,如果我们发挥算法工程师的必备技能之一----想象力,那么我们把每个用户的行为当成一篇文章,每个行为数据当成一个词语,然后使用LDA.....呵呵 这样算出来的主题,是不是就是用户的类别呢?有相似行为数据的用户会出现在相同的主题下,那么这样就把这些用户分类了,那么是不是可以理解为同样类别的下的用户有着相似的爱好呢?如果你觉得可行,可以拿你公司的用户数据试试,看看效果好不好:)

9. 后面的后面

最后,所有代码在github上,点击这里可以看得到,代码相当简单,整个不超过200行,核心的就是我上面列的那些,代码中也有word2vec的代码和使用,这篇文章就没提了,另外,爬取的文章就不放上来了,太大了。

如果大家想要语料自己玩,可以上wiki百科,他们开放了他们的所有数据给全世界做语料分析,其中有中文的,地址是:https://dumps.wikimedia.org/zhwiki/latest/zhwiki-latest-pages-articles.xml.bz2,但维基上中文语料并不多,中文语料多的是百度百科,但看看百度百科,呵呵,不但不开放,防爬虫跟防贼一样,呵呵,不过我也给大家个地址,100G的百度百科原始页面:http://pan.baidu.com/s/1i3wvfil ,接头密码:neqs,由亚洲第二爬虫天王梁斌penny友情提供。

好了,今天的文章有点长,就到这了,后面会把算法部分放一放,最近工作太忙,等这一段结束了,我会再说说算法部分,因为现在工作中会有一些比较好玩的算法要用,接下来的文章会主要写写系统架构方面的东西了,另外我自己的那个搜索引擎目前太忙没时间整,也要等一小段时间了,不好意思:)但放心,不会有头无尾啦。


欢迎关注我的公众号,主要聊聊搜索,推荐,广告技术,还有瞎扯。。文章会在这里首先发出来:)扫描或者搜索微信号XJJ267或者搜索西加加语言就行

© 著作权归作者所有

wyh817

wyh817

粉丝 13
博文 20
码字总数 63957
作品 1
海淀
其他
私信 提问
推荐算法概述:基于内容的推荐算法、协同过滤推荐算法和基于知识的推荐算法

所谓推荐算法就是利用用户的一些行为,通过一些数学算法,推测出用户可能喜欢的东西。推荐算法主要分为两种 1. 基于内容的推荐 基于内容的信息推荐方法的理论依据主要来自于信息检索和信息过...

xiaomin0322
2018/06/20
269
0
余弦定理的应用:基于文字的文本相似度计算

最近由于工作项目,需要判断两个txt文本是否相似,于是开始在网上找资料研究,因为在程序中会把文本转换成String再做比较,所以最开始找到了这篇关于 距离编辑算法 Blog写的非常好,受益匪浅...

大数据之路
2013/03/24
4.1K
5
让你"又爱又恨"的推荐系统--程序猿篇

推荐系统 0、又爱又恨的推荐系统 作为一名程序猿,一直对推荐系统比较感兴趣,最近看到一个用户的吐槽: 又爱又恨 推荐系统的应用场景,我相信在日常生活中大家基本都会接触到。例如,作为一...

流川枫AI
2018/01/06
0
0
工程实践也能拿KDD最佳论文?解读Embeddings at Airbnb

作者 | Mihajlo Grbovic,Airbnb 资深机器学习科学家 译者 | Lang Yang,Airbnb 工程经理 【导读】本文最早于 2018 年 5 月 13 日发表,主要介绍了机器学习的嵌入技术在 Airbnb 爱彼迎房源搜...

AI科技大本营
2018/11/23
0
0
Python文本挖掘-PDF和脚本见附件

课程要点 •分词以及词权重 •文本分类算法 •文本检索和LDA 常国珍《Python数据科学:全栈技术详解》 3 课程大纲 1)文本挖掘介绍 2)中文分词 3)文本特征提取与相关性的度量 4)文本分类 5)主...

Ben_Chang
2018/05/01
0
0

没有更多内容

加载失败,请刷新页面

加载更多

JS基础-该如何理解原型、原型链?

JS的原型、原型链一直是比较难理解的内容,不少初学者甚至有一定经验的老鸟都不一定能完全说清楚,更多的"很可能"是一知半解,而这部分内容又是JS的核心内容,想要技术进阶的话肯定不能对这个...

OBKoro1
今天
6
0
高防CDN的出现是为了解决网站的哪些问题?

高防CDN是为了更好的服务网络而出现的,是通过高防DNS来实现的。高防CDN是通过智能化的系统判断来路,再反馈给用户,可以减轻用户使用过程的复杂程度。通过智能DNS解析,能让网站访问者连接到...

云漫网络Ruan
今天
14
0
OSChina 周一乱弹 —— 熟悉的味道,难道这就是恋爱的感觉

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @xiaoshiyue :好久没分享歌了分享张碧晨的单曲《今后我与自己流浪》 《今后我与自己流浪》- 张碧晨 手机党少年们想听歌,请使劲儿戳(这里)...

小小编辑
今天
2.9K
24
SpringBoot中 集成 redisTemplate 对 Redis 的操作(二)

SpringBoot中 集成 redisTemplate 对 Redis 的操作(二) List 类型的操作 1、 向列表左侧添加数据 Long leftPush = redisTemplate.opsForList().leftPush("name", name); 2、 向列表右......

TcWong
今天
46
0
排序––快速排序(二)

根据排序––快速排序(一)的描述,现准备写一个快速排序的主体框架: 1、首先需要设置一个枢轴元素即setPivot(int i); 2、然后需要与枢轴元素进行比较即int comparePivot(int j); 3、最后...

FAT_mt
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部