TF-IDF模型详解
1. 理论基础
由于数据挖掘所有数据都要以数字形式存在,而文本是以字符串形式存在。所以进行文本挖掘时需要先对字符串进行数字化,从而能够进行计算。TF-IDF就是这样一种技术,能够将字符串转换为数字,从而能够进行数据计算。
TF-IDF(term frequency–inverse document frequency)是一种用于资讯检索与资讯探勘的常用加权技术。TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一份文件对于所在的一个语料库中的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。
TF意思是词频(Term Frequency),IDF意思是逆向文件频率(Inverse Document Frequency)。
1.1 简介
FIDF的主要思想是:如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,即IDF低,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
可以将一个词对的TF-IDF值表示为公式-1,该值明确定义了对于分类的重要性,值越大,说明越有益于分类;值越小,说明越无益于分类。
1.2 TF(词频)
在一份给定的文件里,词频(term frequency,TF)指的是某一个给定的词语在该文件中出现的频率。这个数字是对词数(term count)的归一化,以防止它偏向长的文件。(同一个词语在长文件里可能会比短文件有更高的词数,而不管该词语重要与否。)对于在某一特定文件里的词语ti来说,它的重要性可表示为:
以上式子中ni,j是该词在文件dj中的出现次数,而分母则是在文件dj中所有字词的出现次数之和。
1.3 IDF(逆向文件频率)
逆向文件频率 (inverse document frequency, IDF) 是一个词语普遍重要性的度量。某一特定词语的IDF,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取对数得到。
其中:
-
|D|:语料库中的文件总数;
-
|{j:ti∈dj}|:包含词语ti的文件数目,如果该词语不在语料库中,就会导致被除数为零,因此一般情况下使用1+|{j:ti∈dj}|
所以公式-1可以表示为:
某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF。因此,TF-IDF倾向于过滤掉常见的词语,保留重要的词语。
1.4示例
TFIDF实际上是:TF * IDF,TF词频(Term Frequency),IDF逆向文件频率(Inverse Document Frequency)。TF表示词条在文档d中出现的频率。IDF的主要思想是:如果包含词条t的文档越少,也就是n越小,IDF越大,则说明词条t具有很好的类别区分能力。
如果某一类文档C中包含词条t的文档数为m,而其它类包含t的文档总数为k,显然所有包含t的文档数n=m+k,当m大的时候,n也大,按照IDF公式得到的IDF的值会小,就说明该词条t类别区分能力不强。但是实际上,如果一个词条在一个类的文档中频繁出现,则说明该词条能够很好代表这个类的文本的特征,这样的词条应该给它们赋予较高的权重,并选来作为该类文本的特征词以区别与其它类文档。
有很多不同的数学公式可以用来计算TF-IDF。这边的例子以上述的数学公式来计算。词频 (TF) 是一词语出现的次数除以该文件的总词语数。假如一篇文件的总词语数是100个,而词语"母牛"出现了3次,那么"母牛"一词在该文件中的词频就是3/100=0.03。一个计算文件频率 (DF) 的方法是测定有多少份文件出现过"母牛"一词,然后除以文件集里包含的文件总数。所以,如果"母牛"一词在1,000份文件出现过,而文件总数是10,000,000份的话,其逆向文件频率就是 log(10,000,000 / 1,000)=4。最后的TF-IDF的分数为0.03 * 4=0.12。
根据关键字k1,k2,k3进行搜索结果的相关性就变成TF1*IDF1 + TF2*IDF2 + TF3*IDF3。比如document1的term总量为1000,k1,k2,k3在document1出现的次数是100,200,50。包含了 k1, k2, k3的docuement总量分别是 1000, 10000,5000。document set的总量为10000。 TF1 = 100/1000 = 0.1 TF2 = 200/1000 = 0.2 TF3 = 50/1000 = 0.05 IDF1 = log(10000/1000) = log(10) = 2.3 IDF2 = log(10000/100000) = log(1) = 0; IDF3 = log(10000/5000) = log(2) = 0.69 这样关键字k1,k2,k3与docuement1的相关性= 0.1*2.3 + 0.2*0 + 0.05*0.69 = 0.2645 其中k1比k3的比重在document1要大,k2的比重是0.
2. Spark应用
Spark有两个机器学习的算法库:mlLib和ml,这两个库的最大区别是依赖的容器不同。mlLib将数据存储在DataFrame中,而ml是将数据存储在RDD中。其中mlLib较新,所以本文只对mlLib的TF-IDF进行介绍。其中对于Spark mlLib框架和使用方式不熟悉可以参考另外一篇文章。
2.1 依赖组件
TF操作依赖两个类:HashingTF和CountVectorizer,它们都可以用来生成频率向量。其中HashingTF是一个Transformer子类,它接受一组数据,并将这些数据转换为固定长度的特征向量。CountVectorizer将文本文档转换为术语计数的向量。更多的细节请参考CountVectorizer。
IDF是一个Estimator子类,其会训练数据集并生成一个IDFModel对象。IDFModel采用特征向量(通常是从HashingTF或CountVectorizer创建的),并缩放每一列。直观地说,它降低了在语料库中频繁出现的列。
2.2 示例
如下示例通过Tokenizer类,将一组句子分离为一个个单词。对于每一个句子(单词袋),我们使用HashingTF把这个句子变成一个特征向量。我们使用IDF来重新缩放特征向量;当使用文本作为特性时,这通常会提高性能。我们的特征向量可以被传递给一个学习算法。
-
import org.apache.spark.ml.feature.{HashingTF, IDF, Tokenizer}
-
val sentenceData = spark.createDataFrame(Seq(
-
(0.0, "Hi I heard about Spark"),
-
(0.0, "I wish Java could use case classes"),
-
(1.0, "Logistic regression models are neat")
-
)).toDF("label", "sentence")
-
-
val tokenizer = new Tokenizer().setInputCol("sentence").setOutputCol("words")
-
val wordsData = tokenizer.transform(sentenceData)
-
-
val hashingTF = new HashingTF()
-
.setInputCol("words").setOutputCol("rawFeatures").setNumFeatures(20)
-
-
val featurizedData = hashingTF.transform(wordsData)
-
// alternatively, CountVectorizer can also be used to get term frequency vectors
-
-
val idf = new IDF().setInputCol("rawFeatures").setOutputCol("features")
-
val idfModel = idf.fit(featurizedData)
-
-
val rescaledData = idfModel.transform(featurizedData)
-
rescaledData.select("label", "features").show()
3. Scikit-learn应用
3.1 依赖组件
-
sudo pip install scikit-learn
-
-
sudo pip install jieba
-
3.2 Jieba使用
关于jieba分词的使用非常简单,参考这里,关键的语句就是(这里简单试水,不追求效果 )
-
import jieba.posseg as pseg
-
words=pseg.cut("对这句话进行分词")
-
for key in words:
-
print key.word,key.flag
输出: 对 p 这 r 句 q 话 n 进行 v 分词 n |
3.3 示例
采用scikit-learn包进行tf-idf分词权重计算关键用到了两个类:CountVectorizer和TfidfTransformer,具体参见这里.
一个简单的代码如下:
-
# coding:utf-8
-
__author__ = "liuxuejiang"
-
import jieba
-
import jieba.posseg as pseg
-
import os
-
import sys
-
from sklearn import feature_extraction
-
from sklearn.feature_extraction.text import TfidfTransformer
-
from sklearn.feature_extraction.text import CountVectorizer
-
-
if __name__ == "__main__":
-
corpus=["我 来到 北京 清华大学",#第一类文本切词后的结果,词之间以空格隔开
-
"他 来到 了 网易 杭研 大厦",#第二类文本的切词结果
-
"小明 硕士 毕业 与 中国 科学院",#第三类文本的切词结果
-
"我 爱 北京 天安门"]#第四类文本的切词结果
-
vectorizer=CountVectorizer()#该类会将文本中的词语转换为词频矩阵,矩阵元素a[i][j] 表示j词在i类文本下的词频
-
transformer=TfidfTransformer()#该类会统计每个词语的tf-idf权值
-
tfidf=transformer.fit_transform(vectorizer.fit_transform(corpus))#第一个fit_transform是计算tf-idf,第二个fit_transform是将文本转为词频矩阵
-
word=vectorizer.get_feature_names()#获取词袋模型中的所有词语
-
weight=tfidf.toarray()#将tf-idf矩阵抽取出来,元素a[i][j]表示j词在i类文本中的tf-idf权重
-
for i in range(len(weight)):#打印每类文本的tf-idf词语权重,第一个for遍历所有文本,第二个for便利某一类文本下的词语权重
-
print u"-------这里输出第",i,u"类文本的词语tf-idf权重------"
-
for j in range(len(word)):
-
print word[j],weight[i][j]
程序输出:每行格式为:词语 tf-idf权重
-------这里输出第 0 类文本的词语tf-idf权重------ #该类对应的原文本是:"我来到北京清华大学" 中国 0.0 北京 0.52640543361 大厦 0.0 天安门 0.0 小明 0.0 来到 0.52640543361 杭研 0.0 毕业 0.0 清华大学 0.66767854461 硕士 0.0 科学院 0.0 网易 0.0 -------这里输出第 1 类文本的词语tf-idf权重------ #该类对应的原文本是: "他来到了网易杭研大厦" 中国 0.0 北京 0.0 大厦 0.525472749264 天安门 0.0 小明 0.0 来到 0.414288751166 杭研 0.525472749264 毕业 0.0 清华大学 0.0 硕士 0.0 科学院 0.0 网易 0.525472749264 -------这里输出第 2 类文本的词语tf-idf权重------ #该类对应的原文本是: "小明硕士毕业于中国科学院" 中国 0.4472135955 北京 0.0 大厦 0.0 天安门 0.0 小明 0.4472135955 来到 0.0 杭研 0.0 毕业 0.4472135955 清华大学 0.0 硕士 0.4472135955 科学院 0.4472135955 网易 0.0 -------这里输出第 3 类文本的词语tf-idf权重------ #该类对应的原文本是: "我爱北京天安门" 中国 0.0 北京 0.61913029649 大厦 0.0 天安门 0.78528827571 小明 0.0 来到 0.0 杭研 0.0 毕业 0.0 清华大学 0.0 硕士 0.0 科学院 0.0 网易 0.0 |
4. 参考文献
[1].tf-idf.
[2].TF-IDF原理及使用.
[3].python scikit-learn计算tf-idf词语权重.
[4].Python TF-IDF计算100份文档关键词权重.
[5].Spark mllib官网.