如何衡量词的重要性
在文本处理过程中总会遇到这样一个问题,如何衡量一个词在一篇文档中的重要性?就像我们在写论文的时候,会有一个摘要和关键词,这个关键词便是我们认为的对于这篇文章来说最重要的几个词,那我们怎么得到这样的关键词呢?
假设我们现在有这么一段文本:
我最喜欢度假了,度假的时候我的心情会变得十分美丽,因为我可以去游山玩水,我还可以去吃好多好吃的,我还可以认识好多新的朋友,经历不一样的人生。。。。。
那么从我们来看,这一段话中明显“度假”的重要性要高于其他的词,那么我们怎么才能让计算机把它找出来呢,因为我们知道,计算机最擅长的就是计算,它几乎就是只在跟数字打交道,那么我们能不能通过数学的方式,将这个关键词计算出来呢?
我们第一个想到的是某个词出现的次数,也就是词频(Term Frequency,即TF),我们认为出现次数多的一般都比较重要,但这样就会出现一个问题,有些无用词会来干扰我们的判断,比如上面文本中,最重要的词“度假”出现的词频是2,而主语词“我”出现的词频是5,而单单一个“我”,却带来不了什么实际的含义。所以说我们肯定是得做去停用词的操作的。但是仅仅去掉停用词,似乎还是不能完全解决我们的问题,假设现在有两个词在同一篇文本中的词频一样,但是当我们把这篇文本划分为不同主题时,可能这两个词的重要程度又是不一样的。所以简单的词频,并不能很好的满足我们的需求,我们需要更好的词语重要性表征工具。
于是一种加了权重的词频表达方式就来了:TF-IDF 。
TF-IDF的引出
TF-IDF是TF(词频,Term Frequency)和IDF(逆文档频率,Inverse Document Frequency)的乘积。我们先来看他们分别是怎么计算的:
TF的计算有多种方式,常见的是:
考虑到文章有长短之分,为了便于不同文章的比较,进行"词频"标准化:
或者是:
再来看IDF的计算:
逆文档频率需要站在语料库的角度看:
如果一个词越常见,那么分母就越大,逆文档频率就越小越接近0。分母之所以要加1,是为了避免分母为0(即所有文档都不包含该词)。log表示对得到的值取对数,求log是为了归一化,保证IDF不会过大。
所以TF-IDF 的计算就是:
以下有几个细节点的理解:
- IDF表征的是区分度、稀缺性,用以评估一个单词在语料库中的重要程度,一个词在少数几篇文档中出现的次数越多,它的IDF值越高,如果这个词在大多数文档中都出现了,这个值就不大了。从公式也可以看出来,由于log函数是单增函数,当文档总数固定时,包含该词的文档数越少,IDF值越大,稀缺性越强。背后的思想是某个词或者短语在一篇文章中出现的频率高(TF大),并且在其他文档中很少出现(IDF也大),则认为该词或短语具备很好的类别区分能力(TF-IDF就越大)。
- TF刻画了词语w对某篇文档的重要性,IDF刻画了w对整个文档集的重要性。TF与IDF没有必然联系,TF低并不一定伴随着IDF高。实际上我们可以看出来,IDF其实是给TF加了一个权重。
应用场景
- 找关键词:TF-IDF值排序,取前top-k。
- 文本相似度:找出两个待比较文档中TF-IDF排名前top-k的关键词,利用余弦相似度来计算两个文本的相似度。
- 自动文本摘要:拟定一个簇长度n,其实就是一个窗口长度,在这个窗口长度下,某个句子含有的关键词越多说明这个句子就越重要,就可以作为摘要的一句,可能还要加上重要的文头文尾,就可以简单的生成一个摘要。
利用代码理解
利用sklearn中既有的包来简单看一下TF-IDF的计算:
from sklearn.feature_extraction.text import CountVectorizer,TfidfTransformer
texts=["dog cat fish","dog cat cat","fish bird", 'bird'] # “dog cat fish” 为输入列表元素,假设代表一篇文章的所有字符串
cv = CountVectorizer()#创建词袋数据结构
cv_fit=cv.fit_transform(texts)
print(cv.get_feature_names()) #['bird', 'cat', 'dog', 'fish'] 列表形式呈现文章生成的词典,以字母顺序进行排列
print(cv.vocabulary_ ) # {‘dog’:2,'cat':1,'fish':3,'bird':0} 字典形式呈现,key:词,value:在词袋列表中的index
print(cv_fit)
'''
(0, 2) 1 #第0个列表元素"dog cat fish",词典中索引为2的元素:‘dog’, 词频为1
(0, 1) 1
(0, 3) 1
(1, 2) 1
(1, 1) 2
(2, 3) 1
(2, 0) 1
(3, 0) 1
'''
print(cv_fit.toarray()) #.toarray() 是将结果转化为稀疏矩阵矩阵的表示方式;
'''
[[0 1 1 1]#"dog cat fish"
[0 2 1 0]#"dog cat cat"
[1 0 0 1]#"fish bird"
[1 0 0 0]]#'bird'
'''
print(cv_fit.toarray().sum(axis=0)) #每个词在所有文档中的词频
#[2 3 2 2]#dog:2,cat:3,fish:3,bird:2
print(cv_fit.shape)#(4, 4)
tfidf = TfidfTransformer()
tfidf_fit = tfidf.fit_transform(cv_fit)
print(tfidf_fit)
'''
(0, 3) 0.5773502691896257 #dog cat fish中dog对应的tfidf值
(0, 2) 0.5773502691896257
(0, 1) 0.5773502691896257
(1, 2) 0.4472135954999579
(1, 1) 0.8944271909999159
(2, 3) 0.7071067811865475
(2, 0) 0.7071067811865475
(3, 0) 1.0
'''
print(tfidf_fit.toarray())
'''#对应的稀疏矩阵
[[0. 0.57735027 0.57735027 0.57735027]
[0. 0.89442719 0.4472136 0. ]
[0.70710678 0. 0. 0.70710678]
[1. 0. 0. 0. ]]
'''
print(tfidf_fit.shape)#(4, 4)
优点与不足
TF-IDF算法的优点是简单快速,结果比较符合实际情况。缺点是,单纯以"词频"衡量一个词的重要性,不够全面,有时重要的词可能出现次数并不多。这会导致TF-IDF法的精度并不是很高。而且,这种算法无法体现词的位置信息,出现位置靠前的词与出现位置靠后的词,都被视为重要性相同,这是不正确的。(常用的一种解决方法是,对全文的第一段和每一段的第一句话,给予较大的权重。)
总结
我们通过寻找关键词入手,想要寻找一种带有权重的词频统计方法,从而引入了TF-IDF方法,并且探寻了这种方法可能应用的场景已经该方法的优点与不足。学习简单传统方法的意义就在于掌握思想、打好基础,为后面的进阶做好准备,加油。
参考文章: