一.TF-IDF算法概述
什么是TF-IDF?
词频-逆文档频率(Term Frequency-Inverse Document Frequency,TF-IDF)是一种常用于文本处理的统计方法,可以评估一个单词在一份文档中的重要程度。简单来说就是可以用于文档关键词的提取。
TF-IDF的基本思想:
看到下面这段文本,我们应该很容易就能看出“梅西”应该是一个关键词,但是我们如何通过算法的形式让计算机也能够辨别呢?
五届世界最佳球员莱昂内尔·梅西与阿根廷一起遭遇了更多的心碎——在世界杯1/8淘汰赛上,阿根廷3-4输给了法国队。 梅西在俄罗斯只进了一球,在世界杯淘汰赛阶段还没有进球。尽管被广泛认为是史上最伟大的球员之一,巴塞罗那球星在他的祖国阿根廷却仍然受到许多人的质疑,特别是与1986年夺得世界杯的球王马拉多纳相比。曾经的 “球王接班人”如今已年满31岁,他可能已经失去了为祖国争夺荣誉的最后机会。
脑海中想到的第一个方法就是对单词出现的次数进行统计,也就是词频。如果一个单词在文中出现的频率很高,那我们是否可以认为这个单词就是文章的关键词呢?
其实不一定,词频很高的单词往往更有可能是一些没有意义的停用词(stopword),例如“我”,“的”,“了”等等。
与此同时,在文章中出现次数很少的单词也不一定是不重要的单词。因此,TF-IDF的基本思想是:如果某个单词在一篇文章的出现的频率很高,同时在其他文章中很少出现,则认为该单词大概率是一个关键词。
词频统计的思路:单词w在文档d中出现的频率。
逆文档频率(Inverse Document Frequency,IDF):
逆文档频率的思路:如果一个单词在很多的文档中出现,则意味着该单词的的重要性不高;反之则意味着该单词的重要性很高。主要是考虑了单词的重要性。
文档数量越大,同时单词出现在越少的文档中,IDF值就越大,则说明单词越重要。
上面IDF公式已经可以使用了,但是在一些特殊情况下可能会有一些小问题,比如某一个生僻词在我们的语料库中没有出现过,那么分母N(w)=0,IDF就没有意义了。
所以常用的IDF需要做平滑处理,使得没有在语料库中出现的单词也可以得到一个合适的IDF值。
二.代码实现
# 0. 引入依赖
import numpy as np
import pandas as pd
# 1. 定义数据和预处理
docA = "The cat sat on my bed"
docB = "The dog sat on my knees"
bowA = docA.split(" ")
bowB = docB.split(" ")
# 构建词库
wordSet = set(bowA).union(set(bowB))
# print(wordSet)
# 2. 进行词数统计
# 用统计字典来保存词出现的次数
wordDictA = dict.fromkeys(wordSet, 0)
wordDictB = dict.fromkeys(wordSet, 0)
# 遍历文档,统计词数
for word in bowA:
wordDictA[word] += 1
for word in bowB:
wordDictB[word] += 1
# pd.DataFrame([wordDictA, wordDictB])
# print(wordDictA)
# print(wordDictB)
# 3. 计算词频TF
def computeTF(wordDict, bow):
# 用一个字典对象记录tf,把所有的词对应在bow文档里的tf都算出来
tfDict = {}
nbowCount = len(bow)
# 取出key与value
for word, count in wordDict.items():
tfDict[word] = count / nbowCount
return tfDict
tfA = computeTF(wordDictA, bowA)
tfB = computeTF(wordDictB, bowB)
# print(tfA)
# print(tfB)
# 4. 计算逆文档频率idf
def computeIDF(wordDictList):
# 用一个字典对象保存idf结果,每个词作为key,初始值为0
idfDict = dict.fromkeys(wordDictList[0], 0)
N = len(wordDictList)
import math
# 遍历字典序列中的每一本字典
for wordDict in wordDictList:
# 遍历字典中的每个词汇,统计Ni
for word, count in wordDict.items():
if count > 0:
# 先把Ni增加1,存入到idfDict
idfDict[word] += 1
# 已经得到所有词汇i对应的Ni,现在根据公式把它替换成为idf值。Ni:表示文档集中包含了词汇i的文档数
for word, Ni in idfDict.items():
# 若一个词汇每个文档均出现则Ni=N,则log10(1)=0
idfDict[word] = math.log10((N + 1) / (Ni + 1))
return idfDict
idfs = computeIDF([wordDictA, wordDictB])
# print(idfs)
# 5. 计算TF-IDF
def computeTFIDF( tf, idfs ):
tfidf = {}
for word, tfval in tf.items():
tfidf[word] = tfval * idfs[word]
return tfidf
tfidfA = computeTFIDF( tfA, idfs )
tfidfB = computeTFIDF( tfB, idfs )
# pd.DataFrame( [tfidfA, tfidfB] )
# print(tfidfA)
# print(tfidfB)