TD-IDF

什么是 TF-IDF 算法?

简单来说,向量空间模型就是希望把查询关键字和文档都表达成向量,然后利用向量之间的运算来进一步表达向量间的关系。比如,一个比较常用的运算就是计算查询关键字所对应的向量和文档所对应的向量之间的 “相关度”。

在这里插入图片描述

TF (Term Frequency)—— “单词频率”

意思就是说,我们计算一个查询关键字中某一个单词在目标文档中出现的次数。举例说来,如果我们要查询 “Car Insurance”,那么对于每一个文档,我们都计算“Car” 这个单词在其中出现了多少次,“Insurance”这个单词在其中出现了多少次。这个就是 TF 的计算方法。

TF 背后的隐含的假设是,查询关键字中的单词应该相对于其他单词更加重要,而文档的重要程度,也就是相关度,与单词在文档中出现的次数成正比。比如,“Car” 这个单词在文档 A 里出现了 5 次,而在文档 B 里出现了 20 次,那么 TF 计算就认为文档 B 可能更相关。

然而,信息检索工作者很快就发现,仅有 TF 不能比较完整地描述文档的相关度。因为语言的因素,有一些单词可能会比较自然地在很多文档中反复出现,比如英语中的 “The”、“An”、“But” 等等。这些词大多起到了链接语句的作用,是保持语言连贯不可或缺的部分。然而,如果我们要搜索 “How to Build A Car” 这个关键词,其中的 “How”、“To” 以及 “A” 都极可能在绝大多数的文档中出现,这个时候 TF 就无法帮助我们区分文档的相关度了。

IDF(Inverse Document Frequency)—— “逆文档频率”

就在这样的情况下应运而生。这里面的思路其实很简单,那就是我们需要去 “惩罚”(Penalize)那些出现在太多文档中的单词。

也就是说,真正携带 “相关” 信息的单词仅仅出现在相对比较少,有时候可能是极少数的文档里。这个信息,很容易用 “文档频率” 来计算,也就是,有多少文档涵盖了这个单词。很明显,如果有太多文档都涵盖了某个单词,这个单词也就越不重要,或者说是这个单词就越没有信息量。因此,我们需要对 TF 的值进行修正,而 IDF 的想法是用 DF 的倒数来进行修正。倒数的应用正好表达了这样的思想,DF 值越大越不重要。

TF-IDF 算法主要适用于英文,中文首先要分词,分词后要解决多词一义,以及一词多义问题,这两个问题通过简单的tf-idf方法不能很好的解决。于是就有了后来的词嵌入方法,用向量来表征一个词。

TF-IDF实际上是:TF * IDF

​ 某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。

TF-IDF计算方式

在这里插入图片描述
在这里插入图片描述

TF-IDF 的4个变种

在这里插入图片描述
TF-IDF常见的4个变种

变种1:通过对数函数避免 TF 线性增长

很多人注意到 TF 的值在原始的定义中没有任何上限。虽然我们一般认为一个文档包含查询关键词多次相对来说表达了某种相关度,但这样的关系很难说是线性的。拿我们刚才举过的关于 “Car Insurance” 的例子来说,文档 A 可能包含 “Car” 这个词 100 次,而文档 B 可能包含 200 次,是不是说文档 B 的相关度就是文档 A 的 2 倍呢?其实,很多人意识到,超过了某个阈值之后,这个 TF 也就没那么有区分度了。

用 Log,也就是对数函数,对 TF 进行变换,就是一个不让 TF 线性增长的技巧。具体来说,人们常常用 1+Log(TF) 这个值来代替原来的 TF 取值。在这样新的计算下,假设 “Car” 出现一次,新的值是 1,出现 100 次,新的值是 5.6,而出现 200 次,新的值是 6.3。很明显,这样的计算保持了一个平衡,既有区分度,但也不至于完全线性增长。

变种2:标准化解决长文档、短文档问题

经典的计算并没有考虑 “长文档” 和“短文档”的区别。一个文档 A 有 3,000 个单词,一个文档 B 有 250 个单词,很明显,即便 “Car” 在这两个文档中都同样出现过 20 次,也不能说这两个文档都同等相关。对 TF 进行 “标准化”(Normalization),特别是根据文档的最大 TF 值进行的标准化,成了另外一个比较常用的技巧

变种3:对数函数处理 IDF

第三个常用的技巧,也是利用了对数函数进行变换的,是对 IDF 进行处理。相对于直接使用 IDF 来作为 “惩罚因素”,我们可以使用 N+1 然后除以 DF 作为一个新的 DF 的倒数,并且再在这个基础上通过一个对数变化。这里的 N 是所有文档的总数。这样做的好处就是,第一,使用了文档总数来做标准化,很类似上面提到的标准化的思路;第二,利用对数来达到非线性增长的目的。

变种4:查询词及文档向量标准化

还有一个重要的 TF-IDF 变种,则是对查询关键字向量,以及文档向量进行标准化,使得这些向量能够不受向量里有效元素多少的影响,也就是不同的文档可能有不同的长度。在线性代数里,可以把向量都标准化为一个单位向量的长度。这个时候再进行点积运算,就相当于在原来的向量上进行余弦相似度的运算。所以,另外一个角度利用这个规则就是直接在多数时候进行余弦相似度运算,以代替点积运算。

基于gensim TFIDF模型的文章推荐算法

https://tedboy.github.io/nlps/generated/generated/gensim.similarities.SparseMatrixSimilarity.html#gensim.similarities.SparseMatrixSimilarity
https://blog.csdn.net/ld326/article/details/78441773
https://blog.csdn.net/qq_34333481/article/details/85014010
一 训练阶段 输入数据格式:一个列表,列表中的每个元素(也是列表)代表一个文本。每个文本分词后的词语组成的一个列表代表该文本。 生成的模型、tfidf矩阵、文章item_id列表,字典,语料分别保存。

gensim的TFIDF模型使用步骤

gensim版本的TFIDF模型的创建分为一下5步:

  1. 将待对比文本集合预处理成[[vec1],[vec2]] ,一个列表,列表中的每个元素[vec1] (也是列表)代表一个文本。每个文本分词后的词语组成的一个列表代表该文本。这里可以使用jieba分词来进行文本得的分词。

  2. 生成字典 dictionary = corpora.Dictionary(train)
    Dictionary encapsulates the mapping between normalized words and their integer ids.
    字典封装了规范化单词及其整数ID之间的映射。

  3. 生成语料 corpus = [dictionary.doc2bow(text) for text in train]
    Convert document into the bag-of-words (BoW) format = list of (token_id, token_count) tuples.
    将每一个文本转化为[(单词所在字典位置,单词数量)]

  4. 定义TFIDF模型 tfidf_model = models.TfidfModel(corpus, dictionary=dictionary)
    Objects of this class realize the transformation between word-document co-occurrence matrix (int) into a locally/globally weighted TF-IDF matrix (positive floats).

  5. 用语料训练模型并生成TFIDF矩阵 corpus_tfidf = tfidf_model[corpus]
    详细计算方式见上TF-IDF计算方式

  6. 生成余弦相似度索引 index = similarities.SparseMatrixSimilarity(corpus_tfidf, num_features=featurenum) 使用SparseMatrixSimilarity(),可以占用更少的内存和磁盘空间。
    Compute cosine similarity against a corpus of documents by storing the index matrix in memory.

  7. 测试阶段,模型对测试集进行operation;求余弦相似度。对于给定的新文本,找到训练集中最相似的五篇文章作为推荐。

余弦相似度索引的三种计算方法

1 Similarity、MatrixSimilarity和SparseMatrixSimilarity
Similarity 、MatrixSimilarity、SparseMatrixSimilarity 都是 gensim.similarities.docsim 下的类。其对象为相似索引index,都使用cosine相似度算法。

区别:
Similarity将索引缓存于硬盘(文件形式),MatrixSimilarity/SparseMatrixSimilarity 将索引数据置于内存。
Similarity支持迭代形式的数据输入,MatrixSimilarity/SparseMatrixSimilarity要求输入矩阵完全存在于内存中。
Similarity/SparseMatrixSimilarity生成的index计算最多利用单核,MatrixSimilarity则使用全部核心,cpu利用率基本为100%。
SparseMatrixSimilarity相较于MatrixSimilarity,专门用于接收稀疏向量输入。
(当输入确实为稀疏表示时,应优先使用SparseMatrixSimilarity,运算效率更高,内存消耗更小)
如何选择
当输入数据量较为有限时,使用MatrixSimilarity/SparseMatrixSimilarity;数据量很大不能全部储存在内存时,使用Similarity。
当输入数据量较为有限,且输入为稀疏表示时,使用SparseMatrixSimilarity。为稠密表示则使用MatrixSimilarity。

代码说明

1 import warnings   warnings.filterwarnings(action='ignore',category=UserWarning,module='gensim') 为了不报警告。
2 pickle.dump() 报错,需要有wirite属性。改为 from sklearn.externals import joblib。其dump 和load 方式和pickle一致。
3 index.get_similarities(test_vec) 返回test_vec 和训练语料中所有文本的余弦相似度。返回结果是个numpy数组 
4 related_doc_indices = sim.argsort()[:-6:-1] 完成对numpy数组的排序并获取其top5最大值。
mport jieba
from gensim import corpora, models, similarities
# gensim的模型model模块,可以对corpus进行进一步的处理,比如tf-idf模型,lsi模型,lda模型等
f = open('nbs_2017_data_35.txt','r',encoding='utf-8')
wordstest_model = f.read()
# wordstest_model = ["我去玉龙雪山并且喜欢玉龙雪山玉龙雪山","我在玉龙雪山并且喜欢玉龙雪山","我喜欢九寨沟","我在九寨沟,不知道你说啥",'我在九寨沟,很喜欢']
test_model = [[word for word in jieba.cut(words)] for words in wordstest_model]
dictionary = corpora.Dictionary(test_model,prune_at=2000000)
# for key in dictionary.iterkeys():
#     print key,dictionary.get(key),dictionary.dfs[key]
corpus_model= [dictionary.doc2bow(test) for test in test_model]
print(corpus_model)
# [[(0, 1), (1, 3), (2, 1), (3, 1), (4, 1)], [(0, 1), (1, 2), (3, 1), (4, 1), (5, 1)], [(0, 1), (5, 1), (6, 1)]]

# 目前只是生成了一个模型,并不是将对应的corpus转化后的结果,里面存储有各个单词的词频,文频等信息
tfidf_model = models.TfidfModel(corpus_model)
# 对语料生成tfidf
corpus_tfidf = tfidf_model[corpus_model]


#使用测试文本来测试模型,提取关键词,test_bow提供当前文本词频,tfidf_model提供idf计算
testword = "福建省仙游县盖尾镇石马村"
test_bow = dictionary.doc2bow([word for word in jieba.cut(testword)])
# print(test_bow)
test_tfidf = tfidf_model[test_bow]


# 计算相似度
index = similarities.MatrixSimilarity(corpus_tfidf) #把所有评论做成索引
sims = index[test_tfidf]  #利用索引计算每一条评论和商品描述之间的相似度
maxs_index = sims.index(max(sims))
print(wordstest_model[maxs_index])

模型保存和加载

def tfidf_modle_save(dictionary, corpus_model, tfidf_model, corpus_tfidf):
    # model save
    dictionary.save(model_path + 'train_dictionary.dict')  # 保存生成的词典
    tfidf_model.save(model_path + 'train_tfidf.model')
    corpora.MmCorpus.serialize(model_path + 'train_corpuse.mm', corpus_model)
    featurenum = len(dictionary.token2id.keys())  # 通过token2id得到特征数
    # 稀疏矩阵相似度,从而建立索引,我们用待检索的文档向量初始化一个相似度计算的对象
    index = similarities.SparseMatrixSimilarity(corpus_tfidf, num_features=featurenum)
    index.save(model_path + 'train_index.index')
    
    

def tfidf_model_load():
    # model load
    dictionary = corpora.Dictionary.load(model_path + "train_dictionary.dict")
    tfidf_model = models.TfidfModel.load(model_path + "train_tfidf.model")
    index = similarities.SparseMatrixSimilarity.load(model_path + 'train_index.index')
    corpus_model = corpora.MmCorpus(model_path + 'train_corpuse.mm')
    print('模型加载完成')
    return dictionary, tfidf_model, index, corpus_model

参考文献

https://easyai.tech/ai-definition/tf-idf/

https://blog.csdn.net/asialee_bird/article/details/81486700#2%E3%80%81TF-IDF%E5%BA%94%E7%94%A8

https://blog.csdn.net/qq_34333481/article/details/85327090

https://blog.csdn.net/hao5335156/article/details/87835851

https://www.jianshu.com/p/6e07729c6c5b

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章