一、概念

“词频-逆向文件频率”(TF-IDF)是一种在文本挖掘中广泛使用的特征向量化方法,它可以体现一个文档中词语在语料库中的重要程度。
词语由t表示,文档由d表示,语料库由D表示。词频TF(t,d)是词语t在文档d中出现的次数。文件频率DF(t,D)是包含词语的文档的个数。如果我们只使用词频来衡量重要性,很容易过度强调在文档中经常出现,却没有太多实际信息的词语,比如“a”,“the”以及“of”。如果一个词语经常出现在语料库中,意味着它并不能很好的对文档进行区分。TF-IDF就是在数值化文档信息,衡量词语能提供多少信息以区分文档。其定义如下:

 
IDF

此处|D| 是语料库中总的文档数。公式中使用log函数,当词出现在所有文档中时,它的IDF值变为0。加1是为了避免分母为0的情况。TF-IDF 度量值表示如下:

 
TF-IDF

在Spark ML库中,TF-IDF被分成两部分:TF (+hashing) 和 IDF。

TF: HashingTF 是一个Transformer,在文本处理中,接收词条的集合然后把这些集合转化成固定长度的特征向量。这个算法在哈希的同时会统计各个词条的词频。

IDF: IDF是一个Estimator,在一个数据集上应用它的fit()方法,产生一个IDFModel。 该IDFModel 接收特征向量(由HashingTF产生),然后计算每一个词在文档中出现的频次。IDF会减少那些在语料库中出现频率较高的词的权重。

Spark.mllib 中实现词频率统计使用特征hash的方式,原始特征通过hash函数,映射到一个索引值。后面只需要统计这些索引值的频率,就可以知道对应词的频率。这种方式避免设计一个全局1对1的词到索引的映射,这个映射在映射大量语料库时需要花费更长的时间。但需要注意,通过hash的方式可能会映射到同一个值的情况,即不同的原始特征通过Hash映射后是同一个值。为了降低这种情况出现的概率,我们只能对特征向量升维。i.e., 提高hash表的桶数,默认特征维度是 2^20 = 1,048,576.

在下面的代码段中,我们以一组句子开始。首先使用分解器Tokenizer把句子划分为单个词语。对每一个句子(词袋),我们使用HashingTF将句子转换为特征向量,最后使用IDF重新调整特征向量。这种转换通常可以提高使用文本特征的性能。

二、代码实现

2.1、构造文档集合

导入TFIDF所需要的包,创建一个简单的DataFrame,每一个句子代表一个文档。

import java.util.Arrays;
import java.util.List;
import org.apache.spark.ml.feature.HashingTF;
import org.apache.spark.ml.feature.IDF;
import org.apache.spark.ml.feature.IDFModel;
import org.apache.spark.ml.feature.Tokenizer;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.RowFactory;
import org.apache.spark.sql.SparkSession;
import org.apache.spark.sql.types.DataTypes;
import org.apache.spark.sql.types.Metadata;
import org.apache.spark.sql.types.StructField;
import org.apache.spark.sql.types.StructType; //获取spark
SparkSession spark = SparkSession.builder().appName("FeatureExtractors").master("local").getOrCreate(); //构造数据
List<Row> rawData = Arrays.asList(RowFactory.create(0, "I heard about Spark and I love Spark"),
RowFactory.create(0, "I wish Java could use case classes"),
RowFactory.create(1, "Logistic regression models are neat")
); StructType schema = new StructType(new StructField[] {
new StructField("label",DataTypes.IntegerType,false,Metadata.empty()),
new StructField("sentence",DataTypes.StringType,false,Metadata.empty())
});
Dataset<Row> sentenceData = spark.createDataFrame(rawData,schema);
sentenceData.show(false);

输出结果:

+-----+-------------------------------------+
|label|sentence |
+-----+-------------------------------------+
|0 |I heard about Spark and I love Spark|
|0 |I wish Java could use case classes |
|1 |Logistic regression models are neat |
+-----+-------------------------------------+
2.2、tokenizer对句子进行分词

得到文档集合后,即可用tokenizer对句子进行分词。

Tokenizer tokenizer = new Tokenizer().setInputCol("sentence").setOutputCol("words");
Dataset<Row> wordsData = tokenizer.transform(sentenceData);
wordsData.show(false);

输出结果:

+-----+------------------------------------+---------------------------------------------+
|label|sentence |words |
+-----+------------------------------------+---------------------------------------------+
|0 |I heard about Spark and I love Spark|[i, heard, about, spark, and, i, love, spark]|
|0 |I wish Java could use case classes |[i, wish, java, could, use, case, classes] |
|1 |Logistic regression models are neat |[logistic, regression, models, are, neat] |
+-----+------------------------------------+---------------------------------------------+
2.3、TF把句子哈希成特征向量

得到分词后的文档序列后,即可使用HashingTF的transform()方法把句子哈希成特征向量,这里设置哈希表的桶数为2000。

HashingTF hashingTF = new HashingTF().setInputCol("words").setOutputCol("rawFeatures").setNumFeatures(2000);
Dataset<Row> featurizedData = hashingTF.transform(wordsData);
featurizedData.show(false);

输出结果:

+-----+------------------------------------+---------------------------------------------+---------------------------------------------------------------------+
|label|sentence |words |rawFeatures |
+-----+------------------------------------+---------------------------------------------+---------------------------------------------------------------------+
|0 |I heard about Spark and I love Spark|[i, heard, about, spark, and, i, love, spark]|(2000,[240,333,1105,1329,1357,1777],[1.0,1.0,2.0,2.0,1.0,1.0]) |
|0 |I wish Java could use case classes |[i, wish, java, could, use, case, classes] |(2000,[213,342,489,495,1329,1809,1967],[1.0,1.0,1.0,1.0,1.0,1.0,1.0])|
|1 |Logistic regression models are neat |[logistic, regression, models, are, neat] |(2000,[286,695,1138,1193,1604],[1.0,1.0,1.0,1.0,1.0]) |
+-----+------------------------------------+---------------------------------------------+---------------------------------------------------------------------+

每一个单词被哈希成了一个不同的索引值。以”I heard about Spark and I love Spark”为例,输出结果中2000代表哈希表的桶数,“[240,333,1105,1329,1357,1777]”分别代表着“heard, about, i, spark, and, love”的哈希值,“[1.0,1.0,2.0,2.0,1.0,1.0]”为对应单词的出现次数,无序。

2.4、IDF修正词频特征向量

可以看到,分词序列被变换成一个稀疏特征向量,其中每个单词都被散列成了一个不同的索引值,特征向量在某一维度上的值即该词汇在文档中出现的次数。
最后,使用IDF来对单纯的词频特征向量进行修正,使其更能体现不同词汇对文本的区别能力,IDF是一个Estimator,调用fit()方法并将词频向量传入,即产生一个IDFModel。

IDF idf = new IDF().setInputCol("rawFeatures").setOutputCol("features");
IDFModel idfModel = idf.fit(featurizedData);
2.5、得到单词对应的TF-IDF度量值

很显然,IDFModel是一个Transformer,调用它的transform()方法,即可得到每一个单词对应的TF-IDF度量值。

Dataset<Row> rescaledData = idfModel.transform(featurizedData);
rescaledData.show(false);

输出结果:

+-----+------------------------------------+---------------------------------------------+---------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|label|sentence |words |rawFeatures |features |
+-----+------------------------------------+---------------------------------------------+---------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|0 |I heard about Spark and I love Spark|[i, heard, about, spark, and, i, love, spark]|(2000,[240,333,1105,1329,1357,1777],[1.0,1.0,2.0,2.0,1.0,1.0]) |(2000,[240,333,1105,1329,1357,1777],[0.6931471805599453,0.6931471805599453,1.3862943611198906,0.5753641449035617,0.6931471805599453,0.6931471805599453]) |
|0 |I wish Java could use case classes |[i, wish, java, could, use, case, classes] |(2000,[213,342,489,495,1329,1809,1967],[1.0,1.0,1.0,1.0,1.0,1.0,1.0])|(2000,[213,342,489,495,1329,1809,1967],[0.6931471805599453,0.6931471805599453,0.6931471805599453,0.6931471805599453,0.28768207245178085,0.6931471805599453,0.6931471805599453])|
|1 |Logistic regression models are neat |[logistic, regression, models, are, neat] |(2000,[286,695,1138,1193,1604],[1.0,1.0,1.0,1.0,1.0]) |(2000,[286,695,1138,1193,1604],[0.6931471805599453,0.6931471805599453,0.6931471805599453,0.6931471805599453,0.6931471805599453]) |
+-----+------------------------------------+---------------------------------------------+---------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Dataset<Row> data = rescaledData.select("features","label");
data.show(false);

输出结果:

+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----+
|features |label|
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----+
|(2000,[240,333,1105,1329,1357,1777],[0.6931471805599453,0.6931471805599453,1.3862943611198906,0.5753641449035617,0.6931471805599453,0.6931471805599453]) |0 |
|(2000,[213,342,489,495,1329,1809,1967],[0.6931471805599453,0.6931471805599453,0.6931471805599453,0.6931471805599453,0.28768207245178085,0.6931471805599453,0.6931471805599453]) |0 |
|(2000,[286,695,1138,1193,1604],[0.6931471805599453,0.6931471805599453,0.6931471805599453,0.6931471805599453,0.6931471805599453]) |1 |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----+

可以看到,特征向量已经被其在语料库中出现的总次数进行了修正,通过TF-IDF得到的特征向量,在接下来可以被应用到相关的机器学习方法中。

spark机器学习从0到1特征提取 TF-IDF(十二)的更多相关文章

  1. spark机器学习从0到1特征抽取–Word2Vec(十四)

      一.概念 Word2vec是一个Estimator,它采用一系列代表文档的词语来训练word2vecmodel.该模型将每个词语映射到一个固定大小的向量.word2vecmodel使用文档中每个词 ...

  2. spark机器学习从0到1介绍入门之(一)

      一.什么是机器学习 机器学习(Machine Learning, ML)是一门多领域交叉学科,涉及概率论.统计学.逼近论.凸分析.算法复杂度理论等多门学科.专门研究计算机怎样模拟或实现人类的学习行 ...

  3. spark机器学习从0到1特征选择-卡方选择器(十五)

      一.公式 卡方检验的基本公式,也就是χ2的计算公式,即观察值和理论值之间的偏差   卡方检验公式 其中:A 为观察值,E为理论值,k为观察值的个数,最后一个式子实际上就是具体计算的方法了 n 为总 ...

  4. spark机器学习从0到1特征变换-标签和索引的转化(十六)

      一.原理 在机器学习处理过程中,为了方便相关算法的实现,经常需要把标签数据(一般是字符串)转化成整数索引,或是在计算结束后将整数索引还原为相应的标签. Spark ML 包中提供了几个相关的转换器 ...

  5. spark机器学习从0到1机器学习工作流 (十一)

        一.概念 一个典型的机器学习过程从数据收集开始,要经历多个步骤,才能得到需要的输出.这非常类似于流水线式工作,即通常会包含源数据ETL(抽取.转化.加载),数据预处理,指标提取,模型训练与交叉 ...

  6. spark机器学习从0到1奇异值分解-SVD (七)

      降维(Dimensionality Reduction) 是机器学习中的一种重要的特征处理手段,它可以减少计算过程中考虑到的随机变量(即特征)的个数,其被广泛应用于各种机器学习问题中,用于消除噪声 ...

  7. spark机器学习从0到1决策树(六)

      一.概念 决策树及其集合是分类和回归的机器学习任务的流行方法. 决策树被广泛使用,因为它们易于解释,处理分类特征,扩展到多类分类设置,不需要特征缩放,并且能够捕获非线性和特征交互. 诸如随机森林和 ...

  8. spark机器学习从0到1基本数据类型之(二)

        MLlib支持存储在单个机器上的局部向量和矩阵,以及由一个或多个RDD支持的分布式矩阵. 局部向量和局部矩阵是用作公共接口的简单数据模型. 底层线性代数操作由Breeze提供. 在监督学习中使 ...

  9. spark机器学习从0到1特征抽取–CountVectorizer(十三)

        一.概念 CountVectorizer 旨在通过计数来将一个文档转换为向量.当不存在先验字典时,Countvectorizer作为Estimator提取词汇进行训练,并生成一个CountVe ...

随机推荐

  1. Struts2漏洞利用

    Struts漏洞合集 Struts-S2-013漏洞利用 受影响版本 Struts 2.0.0 - Struts 2.3.14.1 漏洞利用 任意命令执行POC: ${(#_memberAccess[ ...

  2. windows服务程序的编写

    服务编写https://blog.csdn.net/lanuage/article/details/77937407 #include <windows.h> #include <s ...

  3. 如何设计高并发web应用

      所谓高并发,就是同一时间有很多流量(通常指用户)访问程序的接口.页面及其他资源,解决高并发就是当流量峰值到来时保证程序的稳定性. 我们一般用QPS(每秒查询数,又叫每秒请求数)来衡量程序的综合性能 ...

  4. 使用CXF开发Web Service服务

    1.使用CXF开发Web Service服务端 1.1 开发一个Web Service业务接口,该接口要用@WebService修饰 (1)创建一个Java项目MyServer (2)在MyServe ...

  5. Java 线程池(ThreadPoolExecutor)原理分析与实际运用

    在我们的开发中"池"的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 有关java线程技术文章还可以推荐阅读:< ...

  6. C语言基础知识总结

    知识点的回忆与巩固 一. 条件分支结构 1.if分支语句 2.switch语句 二.循环体部分知识点整理 1.for循环 2.while循环-适合不确定循环次数时使用 三.字符串与数组 数组的操作 1 ...

  7. 小白,你要的Java抽象类,操碎了心!

    自从给小白写了两篇科普性质的文章后,我就有点一发不可收拾,觉得很有必要继续写下去.因为有读者留言"鼓励"我说,"二哥,你真的是为小白操碎了心啊!"我容易吗?我. ...

  8. Codeforces Round #575 (Div. 3) 昨天的div3 补题

    Codeforces Round #575 (Div. 3) 这个div3打的太差了,心态都崩了. B. Odd Sum Segments B 题我就想了很久,这个题目我是找的奇数的个数,因为奇数想分 ...

  9. while(scanf("%d",&n)!=EOF) / while(cin>>n)终止问题

    问题的发现:(想要看干货可以直接跳过这段) 我最近刚了解到关于栈的用法,于是按照参考书寻找代码,并把它敲到电脑上.编译运行代码后发现无法终止,在网上查找各种资料,总结如下. 因为我的电脑是Window ...

  10. 【Scala】关于集合的各种知识点

    目录 映射Map 不可变Map 概述 操作实例 可变Map 概述 操作实例 Map的遍历 for循环遍历 格式 操作实例 模式匹配遍历 格式 操作实例 Tuple 元祖 概述 定义格式 获取元素方法 ...