1、定义
在信息检索中,tf-idf(词频-逆文档频率)是一种统计方法,用以评估一个单词在一个文档集合或语料库中的重要程度。经常被用作信息检索、文本挖掘以及用户模型的权重因素。tf-idf的值会随着单词在文档中出现的次数的增加而增大,也会随着单词在语料库中出现的次数的增多而减小。tf-idf是如今最流行的词频加权方案之一。
tf-idf的各种改进版本经常被搜索引擎用作在给定用户查询时对文档的相关性进行评分和排序的主要工具。tf-idf可以成功地用于各种主题字段的停用词过滤,包括文本摘要和分类。
2、原理
TF-IDF实际上是:TF * IDF。主要思想是:如果某个词或短语在一篇文章中出现的频率高(即TF高),并且在其他文章中很少出现(即IDF高),则认为此词或者短语具有很好的类别区分能力,适合用来分类。
通俗理解TF-IDF就是:TF刻画了词语t对某篇文档的重要性,IDF刻画了词语t对整个文档集的重要性。
3、词频-逆向文件频率(TF-IDF)
词频-逆向文件频率(TF-IDF)是一种在文本挖掘中广泛使用的特征向量化方法,它可以体现一个文档中词语在语料库中的重要程度。词语由 t 表示,文档由 d 表示,语料库由 D 表示。词频 TF(t,,d)是词语 t 在文档 d 中出现的次数。文件频率 DF(t,D)是包含词语的文档的个数。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL5gTO0QTMwAjM0ETMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
如果我们只使用词频来衡量重要性,很容易过度强调在文档中经常出现而并没有包含太多与文档有关的信息的词语,比如“a”,“the”以及“of”。如果一个词语经常出现在语料库中,它意味着它并没有携带特定的文档的特殊信息。逆向文档频率数值化衡量词语提供多少信息:
其中,|D|表示是语料库中的文档总数。由于采用了对数,如果一个词出现在所有的文件,其 IDF 值变为 0。
某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的 TF-IDF。因此,TF-IDF 倾向于过滤掉常见的词语,保留重要的词语。
可以看到,TF-IDF 与一个词在文档中的出现次数成正比,与该词在整个语言中的出现次数成反比。所以,自动提取关键词的算法就很清楚了,就是计算出文档的每个词的 TF-IDF值,然后按降序排列,取排在最前面的几个词。接下来看一个例子,假定该文长度为 1000 个词,"中国"、"蜜蜂"、"养殖"各出现 20 次,则这三个词的"词频"(TF)都为 0.02。然后,搜索 Google 发现,包含"的"字的网页共有 250亿张,假定这就是中文网页总数。包含"中国"的网页共有 62.3 亿张,包含"蜜蜂"的网页为0.484 亿张,包含"养殖"的网页为 0.973 亿张。则它们的逆文档频率(IDF)和 TF-IDF 如下:
从上表可见,"蜜蜂"的 TF-IDF 值最高,"养殖"其次,"中国"最低。(如果还计算"的"字的 TF-IDF,那将是一个极其接近 0 的值。)所以,如果只选择一个词,"蜜蜂"就是这篇文章的关键词。除了自动提取关键词,TF-IDF 算法还可以用于许多别的地方。比如,信息检索时,对于每个文档,都可以分别计算一组搜索词("中国"、"蜜蜂"、"养殖")的 TF-IDF,将它们相加,就可以得到整个文档的 TF-IDF。这个值最高的文档就是与搜索词最相关的文档。接下来我们就通过 SparkMllib 实现 TF-IDF
4、spark代码
4.1、添加依赖包
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-mllib_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
4.2、业务逻辑实现
object CalcCorrelation {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder().appName("CalcCorrelation").master("local[4]").getOrCreate()
val sc: SparkContext = spark.sparkContext
sc.setLogLevel("WARN")
tfAndIDF(spark)
}
private def tfAndIDF(spark: SparkSession): Unit = {
val sentenceData = spark.createDataFrame(Seq(
(0, "Hi I heard about Spark"),
(0, "I wish Java could use case classes"),
(1, "Logistic regression models are neat")
)).toDF("label", "sentence")
val tokenizer: Tokenizer = new Tokenizer().setInputCol("sentence").setOutputCol("words")
val wordsData: DataFrame = tokenizer.transform(sentenceData)
val hashingTF: HashingTF = new HashingTF().setInputCol("words").setOutputCol("rawFeatures").setNumFeatures(20)
val featurizedData: DataFrame = hashingTF.transform(wordsData)
featurizedData.show()
val idf: IDF = new IDF().setInputCol("rawFeatures").setOutputCol("features")
val idfModel: IDFModel = idf.fit(featurizedData)
val rescaledData: DataFrame = idfModel.transform(featurizedData)
rescaledData.show()
rescaledData.select("features", "label").take(5).foreach(println)
}
}