原理
TF/IDF方法于1983年题出,它先计算每个单词出现的频率,然后适当归一化。利用TF-IDF方法将任意长度的文档缩减为固定长度的数字列表,然后对比文本相似度,gensim工具包提供该方法。
简单复习一下具体算法:
词频TF
其中n是句中词,i是词的索引号,j是文章索引号,k是文章中所有词,上式计算的是词i在本篇出现的比率。请注意:在短文本的情况下,绝大多数词只出现一次,tf就只和文章长短有关了。
逆向文档频率IDF
其中分母是文章总数,分子是包含词i的文章数。
TF/IDF
tfidf值反映的是每个词在文档中的重要程度。请注意:这是一种基于计数的方法,不直接使用词义。
该算法的优点在于算法简单,计算量小;而缺点在于无法处理对同一概念的不同描述,另外,它是词袋类模型,不考虑词的先后顺序和关系。
流程
计算文本相似度,指的是从多个文档中找到与句子相似度最高的文档,常用于实现搜索,匹配,文本标准化等功能。具体流程如下:
- 用待搜语料训练TFIDF
- 将待搜语料转成包含的关键字及关键字对应评分 M
- 将搜索文本转换成关键字和评分 K
- 逐条计算M中内容与K的相似度评分
- 选最相近的前N条
代码分析
from jieba import lcut
from gensim.similarities import SparseMatrixSimilarity
from gensim.corpora import Dictionary
from gensim.models import TfidfModel
data = ['古典生物型霍乱',
'霍乱,由于O1群霍乱弧菌,埃尔托生物型所致',
'埃尔托生物型霍乱',
'霍乱暴发型',
'伤寒']
texts = [lcut(s) for s in data]
'''
此时 texts中每个元素是一个句子,句子又由词组成
[['古典', '生物', '型', '霍乱'],
['霍乱', ',', '由于', 'O1', '群', '霍乱弧菌', ',', '埃尔托', '生物', '型', '所致'],
['埃尔托', '生物', '型', '霍乱'],
['霍乱', '暴发型'],
['伤寒']]
'''
dictionary = Dictionary(texts)
'''
dictionary 结构类似字典,包含索引号和词的关系
{0: '古典', 1: '型', 2: '生物', 3: '霍乱', 4: 'O1', 5: '埃尔托', 6: '所致',
7: '由于', 8: '群', 9: '霍乱弧菌', 10: ',', 11: '暴发型', 12: '伤寒'}
'''
corpus = [dictionary.doc2bow(text) for text in texts]
'''
corpus 语料库,结构同texts,但每个子元素不是字,而是id及其在本句中出现的次数
[[(0, 1), (1, 1), (2, 1), (3, 1)],
[(1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1), (8, 1), (9, 1), (10, 2)],
[(1, 1), (2, 1), (3, 1), (5, 1)],
[(3, 1), (11, 1)],
[(12, 1)]]
'''
tfidf = TfidfModel(corpus) # 用语料库训练模型
tf_texts = tfidf[corpus] # 将语料库作为被搜索数据
sparse_matrix = SparseMatrixSimilarity(tf_texts, len(dictionary)) # 构建工具类的实例
'''
sparse_matrix.index内容如下:
(0, 0) 0.9050976
(0, 1) 0.28727236
(0, 2) 0.28727236
(0, 3) 0.12548897
(1, 1) 0.10273404
(1, 2) 0.10273404
(1, 3) 0.044877227
...
此时sparse_matrix中存储的是各句中包含的关键字及其tfidf值
'''
keyword = '埃尔托生物霍乱'
kw_vector = dictionary.doc2bow(lcut(keyword))
'''
转换待搜索字符串,返回包含的关键字id及其出现次数
kw_vector: [(2, 1), (3, 1), (5, 1)]
'''
tf_kw = tfidf[kw_vector]
'''
获取关键词对应的的tfidf值
tf_kw: [(2, 0.476), (3, 0.208), (5, 0.854)]
'''
similarities = sparse_matrix.get_similarities(tf_kw)
for e, s in enumerate(similarities):
print('与 %d 相似度为:%.2f' % (e, s))
'''
计算搜索串与被搜索数据中每一项的相似度
与 0 相似度为:0.16
与 1 相似度为:0.22
与 2 相似度为:0.90
与 3 相似度为:0.03
与 4 相似度为:0.00
'''
完整代码
封装一下,以便拿来就用
from jieba import lcut
from gensim.similarities import SparseMatrixSimilarity
from gensim.corpora import Dictionary
from gensim.models import TfidfModel
import numpy as np
class TFIDFSimilarity:
def __init__(self):
pass
def train(self, data):
'''
训练模型,需转入待匹配列表
'''
texts = [lcut(s) for s in data]
self.dictionary = Dictionary(texts)
corpus = [self.dictionary.doc2bow(text) for text in texts]
self.tfidf = TfidfModel(corpus)
tf_texts = self.tfidf[corpus]
num_features = len(self.dictionary.token2id)
self.sparse_matrix = SparseMatrixSimilarity(tf_texts, num_features)
def get_similarities(self, string, topN = 3):
'''
从模型中找最相近的 topN 个匹配项,返回其索引号和近似度
'''
text = lcut(string)
kw_vector = self.dictionary.doc2bow(text)
tf_kw = self.tfidf[kw_vector]
similarities = self.sparse_matrix.get_similarities(tf_kw)
index = np.argsort(similarities)[:-topN-1:-1]
return [(i,s) for i,s in zip(index,similarities[index])]
data = ['霍乱,由于O1群霍乱弧菌,霍乱生物型所致',
'古典生物型霍乱',
'霍乱,由于O1群霍乱弧菌,埃尔托生物型所致']
s_tools = TFIDFSimilarity()
s_tools.train(data)
s_tools.get_similarities('埃尔托生物霍乱',3)
技巧
- 生成词典可以选择以词为单位,以字为单位,或拆分成所有可能的词...
- 可去掉停用词和与主题无关的词
- 可加入其它的语料训练TFIDF,以学习词的重要性(不加入候选集即可)
- 如果搜索句子中的词未出现在字典中,将自动忽略该词
- 如包含英文,请注意大小写
网友评论