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

文本类似性计算(一) 文本类似性计算(二) 前面说了两篇了,分别介绍了TFIDF和向量空间的相关东西,而后介绍了主题模型,这一篇咱们就来试试这两个东西。词向量就不在这篇试了,词向量和这两个关系不大,很差对比,不过我最后也给出了代码。html

0. 工具准备

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

为何不本身写?这个问题....呵呵.....呵呵....我写不出来.....java

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

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

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

1. 数据准备

数据准备但是个技术活,个人职业操守很高,没有用公司的数据,那只能本身找数据了,若是直接找网上的语料,显得太Low了。因而我本身爬了一些数据。golang

首先,我瞄准了目前全国最火的全栈技术网站(就是SegmentFault啦),而后瞄准了一个汽车网站,因而开始爬数据,本身写了个爬虫开始爬数据,恩,个人爬虫我以为还能够,调度器+爬取器组成,爬取器插件化了,可使用任意语言作编写,甚至能够直接对接chrome爬取纯JS单页面网站爬取,也支持代理池,若是你们感兴趣我也能够说说爬虫相关的东西,分布式的哦,能够随便加机器增长爬取能力。算法

好了,八卦完了,爬了两个网站,能够开始干活了,爬两个类型的网站是为了说明后面LDA主题模型,你们就有个认识了。spring

2. 数据清理

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

拿到数据之后,写个脚本

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

    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百科,他们开放了他们的全部数据给全世界作语料分析,其中有中文的,地址是:dumps.wikimedia.org/zhwiki/late…,但维基上中文语料并很少,中文语料多的是百度百科,但看看百度百科,呵呵,不但不开放,防爬虫跟防贼同样,呵呵,不过我也给你们个地址,100G的百度百科原始页面:pan.baidu.com/s/1i3wvfil ,接头密码:neqs,由亚洲第二爬虫天王梁斌penny友情提供。

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


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

相关文章
相关标签/搜索