共计 3642 个字符,预计需要花费 10 分钟才能阅读完成。
一. 什么是 TF-IDF
TF-IDF(Term Frequency-Inverse Document Frequency, 词频 - 逆文件频率).
是一种用于资讯检索与资讯探勘的罕用加权技术。TF-IDF 是一种统计办法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要水平。字词的重要性随着它在文件中呈现的次数成正比减少,但同时会随着它在语料库中呈现的频率成反比降落。
上述援用总结就是, 一个词语在一篇文章中呈现次数越多, 同时在所有文档中呈现次数越少, 越可能代表该文章. 这也就是 TF-IDF 的含意.
词频 (term frequency, TF) 指的是某一个给定的词语在该文件中呈现的次数。这个数字通常会被归一化(个别是词频除以文章总词数), 以避免它偏差长的文件。(同一个词语在长文件里可能会比短文件有更高的词频,而不论该词语重要与否。)
TF 公式:TF = 在某一文档中词条 w 呈现的次数 / 该文档中所有的词条数目
然而, 须要留神, 一些通用的词语对于主题并没有太大的作用, 反倒是一些呈现频率较少的词才可能表白文章的主题, 所以单纯应用是 TF 不适合的。权重的设计必须满足:一个词预测主题的能力越强,权重越大,反之,权重越小。所有统计的文章中,一些词只是在其中很少几篇文章中呈现,那么这样的词对文章的主题的作用很大,这些词的权重应该设计的较大。IDF 就是在实现这样的工作.
逆向文件频率 (inverse document frequency, IDF) IDF 的次要思维是:如果蕴含词条 t 的文档越少, IDF 越大,则阐明词条具备很好的类别辨别能力。某一特定词语的 IDF,能够由总文件数目除以蕴含该词语之文件的数目,再将失去的商取对数失去。
IDF 公式:IDF = log(语料库的文档总数 / (蕴含词条 w 的文档数 + 1))
分母之所以要加 1,是为了防止分母为 0
某一特定文件内的高词语频率,以及该词语在整个文件汇合中的低文件频率,能够产生出高权重的 TF-IDF。因而,TF-IDF 偏向于过滤掉常见的词语,保留重要的词语。
IF-IDF 公式:IF-IDF = IF * IDF
最终公式:IF-IDF = (在某一文档中词条 w 呈现的次数 / 该文档中所有的词条数目) * log(语料库的文档总数 / (蕴含词条 w 的文档数 + 1))
二. 案例
三. Spark 中 TF-IDF 的实现
1. 基于 spark1.4.1 ml 算法包的 TF-IDF 算法
import org.apache.spark.{SparkContext, SparkConf}
import org.apache.spark.ml.feature.{Tokenizer,HashingTF, IDF}
import org.apache.spark.mllib.linalg.{Vectors, Vector}
// 创立实例数据
val sentenceData = sqlContext.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")
// scala> sentenceData.show
// +-----+--------------------+
// |label| sentence|
// +-----+--------------------+
// | 0|Hi I heard about ...|
// | 0|I wish Java could...|
// | 1|Logistic regressi...|
// +-----+--------------------+
// 句子转化成单词数组
val tokenizer = new Tokenizer().setInputCol("sentence").setOutputCol("words")
val wordsData = tokenizer.transform(sentenceData)
// scala> wordsData.show
// +-----+--------------------+--------------------+
// |label| sentence| words|
// +-----+--------------------+--------------------+
// | 0|Hi I heard about ...|ArrayBuffer(hi, i...|
// | 0|I wish Java could...|ArrayBuffer(i, wi...|
// | 1|Logistic regressi...|ArrayBuffer(logis...|
// +-----+--------------------+--------------------+
// hashing 计算 TF 值, 同时还把停用词 (stop words) 过滤掉了. setNumFeatures(20)表最多 20 个词
val hashingTF = new HashingTF().setInputCol("words").setOutputCol("rawFeatures").setNumFeatures(20)
val featurizedData = hashingTF.transform(wordsData)
// scala> featurizedData.show
// +-----+--------------------+--------------------+--------------------+
// |label| sentence| words| rawFeatures|
// +-----+--------------------+--------------------+--------------------+
// | 0|Hi I heard about ...|ArrayBuffer(hi, i...|(20,[5,6,9],[2.0,...|
// | 0|I wish Java could...|ArrayBuffer(i, wi...|(20,[3,5,12,14,18...|
// | 1|Logistic regressi...|ArrayBuffer(logis...|(20,[5,12,14,18],...|
// +-----+--------------------+--------------------+--------------------+
val idf = new IDF().setInputCol("rawFeatures").setOutputCol("features")
val idfModel = idf.fit(featurizedData)
val rescaledData = idfModel.transform(featurizedData)
// 提取该数据中稠密向量的数据, 稠密向量:SparseVector(size,indices,values)
// rescaledData.select("features").rdd.map(row => row.getAs[linalg.Vector](0)).map(x => x.toSparse.indices).collect
rescaledData.select("features", "label").take(3).foreach(println)
// 其中,20 是标签总数, 下一项是单词对应的 hashing ID. 最初是 TF-IDF 后果
// [(20,[5,6,9],[0.0,0.6931471805599453,1.3862943611198906]),0]
// [(20,[3,5,12,14,18],[1.3862943611198906,0.0,0.28768207245178085,0.28768207245178085,0.28768207245178085]),0]
// [(20,[5,12,14,18],[0.0,0.5753641449035617,0.28768207245178085,0.28768207245178085]),1]
2. 基于 RDD 的 MLlib 包中的 TF_IDF 算法
import org.apache.spark.mllib.linalg.Vector
val sc: SparkContext = ...
val documents: RDD[Seq[String]] = sc.textFile("...").map(_.split(" ").toSeq)
val hashingTF = new HashingTF()
val tf: RDD[Vector] = hashingTF.transform(documents)
tf.cache()
val idf = new IDF().fit(tf)
val tfidf: RDD[Vector] = idf.transform(tf)