在文本处理中,比如商品评论挖掘,有时需要了解每个评论分别和商品的描述之间的相似度,以此衡量评论的客观性。
评论和商品描述的相似度越高,说明评论的用语比较官方,不带太多感情色彩,比较注重描述商品的属性和特性,角度更客观。
再比如知乎、贴吧等问答社区内问题下面有很多回复者,如何快速过滤掉与问题无关的回答或者垃圾广告??
那么Python 里面有计算文本相似度的程序包吗,恭喜你,不仅有,而且很好很强大。
使用gensim进行文本相似度计算
原理
1、文本相似度计算的需求始于搜索引擎。
搜索引擎需要计算“用户查询”和爬下来的众多”网页“之间的相似度,从而把最相似的排在最前返回给用户。
2、主要使用的算法是tf-idf
tf:term frequency 词频
idf:inverse document frequency 倒文档频率
主要思想是:如果某个词或短语在一篇文章中出现的频率高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
第一步:把每个网页文本分词,成为词包(bag of words)。
第三步:统计网页(文档)总数M。
第三步:统计第一个网页词数N,计算第一个网页第一个词在该网页中出现的次数n,再找出该词在所有文档中出现的次数m。则该词的tf-idf 为:n/N * 1/(m/M) (还有其它的归一化公式,这里是最基本最直观的公式)
第四步:重复第三步,计算出一个网页所有词的tf-idf 值。
第五步:重复第四步,计算出所有网页每个词的tf-idf 值。
3、处理用户查询
第一步:对用户查询进行分词。
第二步:根据网页库(文档)的数据,计算用户查询中每个词的tf-idf 值。
4、相似度的计算
使用余弦相似度来计算用户查询和每个网页之间的夹角。夹角越小,越相似。
学习目标:
- 利用gensim包分析文档相似度
- 使用jieba进行中文分词
- 了解TF-IDF模型
注:为了简化问题,本文没有剔除停用词“stop-word”。实际应用中应该要剔除停用词。
安装相关包
pip install jieba
pip install gensim
关于结巴分词,这里推荐
https://github.com/WenDesi/zhcnSegment
已经将主要功能封装好,包括添加自定义语料,添加停用词等,简单、易调用
首先引入分词API库jieba、文本相似度库gensim
代码语言:javascript复制
以下doc0-doc7是几个最简单的文档,我们可以称之为目标文档,本文就是分析doc_test(测试文档)与以上8个文档的相似度。
代码语言:javascript复制
分词
首先,为了简化操作,把目标文档放到一个列表all_doc中。
代码语言:javascript复制
以下对目标文档进行分词,并且保存在列表all_doc_list中
代码语言:javascript复制
把分词后形成的列表显示出来:
代码语言:javascript复制
[[‘我’, ‘不’, ‘喜欢’, ‘上海’], [‘上海’, ‘是’, ‘一个’, ‘好’, ‘地方’], [‘北京’, ‘是’, ‘一个’, ‘好’, ‘地方’], [‘上海’, ‘好吃’, ‘的’, ‘在’, ‘哪里’], [‘上海’, ‘好玩’, ‘的’, ‘在’, ‘哪里’], [‘上海’, ‘是’, ‘好’, ‘地方’], [‘上海’, ‘路’, ‘和’, ‘上海’, ‘人’], [‘喜欢’, ‘小吃’]]
以下把测试文档也进行分词,并保存在列表doc_test_list中
代码语言:javascript复制
[‘我’, ‘喜欢’, ‘上海’, ‘的’, ‘小吃’]
制作语料库
首先用dictionary方法获取词袋(bag-of-words)
代码语言:javascript复制
词袋中用数字对所有词进行了编号
代码语言:javascript复制
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
编号与词之间的对应关系
代码语言:javascript复制
{‘一个’: 4, ‘上海’: 0, ‘不’: 1, ‘人’: 14, ‘北京’: 8, ‘和’: 15, ‘哪里’: 9, ‘喜欢’: 2, ‘在’: 10, ‘地方’: 5, ‘好’: 6, ‘好吃’: 11, ‘好玩’: 13, ‘小吃’: 17, ‘我’: 3, ‘是’: 7, ‘的’: 12, ‘路’: 16}
以下使用doc2bow制作语料库
代码语言:javascript复制corpus = [dictionary.doc2bow(doc)
for doc in all_doc_list]
语料库如下。语料库是一组向量,向量中的元素是一个二元组(编号、频次数),对应分词后的文档中的每一个词。
[[(0, 1), (1, 1), (2, 1), (3, 1)], [(0, 1), (4, 1), (5, 1), (6, 1), (7, 1)], [(4, 1), (5, 1), (6, 1), (7, 1), (8, 1)], [(0, 1), (9, 1), (10, 1), (11, 1), (12, 1)], [(0, 1), (9, 1), (10, 1), (12, 1), (13, 1)], [(0, 1), (5, 1), (6, 1), (7, 1)], [(0, 2), (14, 1), (15, 1), (16, 1)], [(2, 1), (17, 1)]]
以下用同样的方法,把测试文档也转换为二元组的向量
代码语言:javascript复制
[(0, 1), (2, 1), (3, 1), (12, 1), (17, 1)]
相似度分析
使用TF-IDF模型对语料库建模。
gensim包提供了这几个模型: TF-IDF、LSI 、LDA
因此我们直接拿来用就好
代码语言:javascript复制#models.LsiModel()
获取测试文档中,每个词的TF-IDF值
代码语言:javascript复制
[(0, 0.08112725037593049), (2, 0.3909393754390612), (3, 0.5864090631585919), (12, 0.3909393754390612), (17, 0.5864090631585919)]
对每个目标文档,分析测试文档的相似度
代码语言:javascript复制
array([ 0.54680777, 0.01055349, 0. , 0.17724207, 0.17724207, 0.01354522, 0.01279765, 0.70477605], dtype=float32)
根据相似度排序
代码语言:javascript复制
[(7, 0.70477605), (0, 0.54680777), (3, 0.17724207), (4, 0.17724207), (5, 0.013545224), (6, 0.01279765), (1, 0.010553493), (2, 0.0)]
从分析结果来看,测试文档与doc7相似度最高,其次是doc0,与doc2的相似度为零。大家可以根据TF-IDF的原理,看看是否符合预期。