spark机器学习从0到1特征提取 TF-IDF(十二)

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

此处|D| 是语料库中总的文档数。公式中使用log函数,当词出现在所有文档中时,它的IDF值变为0。加1是为了避免分母为0的情况。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(十二)的更多相关文章
- spark机器学习从0到1特征抽取–Word2Vec(十四)
一.概念 Word2vec是一个Estimator,它采用一系列代表文档的词语来训练word2vecmodel.该模型将每个词语映射到一个固定大小的向量.word2vecmodel使用文档中每个词 ...
- spark机器学习从0到1介绍入门之(一)
一.什么是机器学习 机器学习(Machine Learning, ML)是一门多领域交叉学科,涉及概率论.统计学.逼近论.凸分析.算法复杂度理论等多门学科.专门研究计算机怎样模拟或实现人类的学习行 ...
- spark机器学习从0到1特征选择-卡方选择器(十五)
一.公式 卡方检验的基本公式,也就是χ2的计算公式,即观察值和理论值之间的偏差 卡方检验公式 其中:A 为观察值,E为理论值,k为观察值的个数,最后一个式子实际上就是具体计算的方法了 n 为总 ...
- spark机器学习从0到1特征变换-标签和索引的转化(十六)
一.原理 在机器学习处理过程中,为了方便相关算法的实现,经常需要把标签数据(一般是字符串)转化成整数索引,或是在计算结束后将整数索引还原为相应的标签. Spark ML 包中提供了几个相关的转换器 ...
- spark机器学习从0到1机器学习工作流 (十一)
一.概念 一个典型的机器学习过程从数据收集开始,要经历多个步骤,才能得到需要的输出.这非常类似于流水线式工作,即通常会包含源数据ETL(抽取.转化.加载),数据预处理,指标提取,模型训练与交叉 ...
- spark机器学习从0到1奇异值分解-SVD (七)
降维(Dimensionality Reduction) 是机器学习中的一种重要的特征处理手段,它可以减少计算过程中考虑到的随机变量(即特征)的个数,其被广泛应用于各种机器学习问题中,用于消除噪声 ...
- spark机器学习从0到1决策树(六)
一.概念 决策树及其集合是分类和回归的机器学习任务的流行方法. 决策树被广泛使用,因为它们易于解释,处理分类特征,扩展到多类分类设置,不需要特征缩放,并且能够捕获非线性和特征交互. 诸如随机森林和 ...
- spark机器学习从0到1基本数据类型之(二)
MLlib支持存储在单个机器上的局部向量和矩阵,以及由一个或多个RDD支持的分布式矩阵. 局部向量和局部矩阵是用作公共接口的简单数据模型. 底层线性代数操作由Breeze提供. 在监督学习中使 ...
- spark机器学习从0到1特征抽取–CountVectorizer(十三)
一.概念 CountVectorizer 旨在通过计数来将一个文档转换为向量.当不存在先验字典时,Countvectorizer作为Estimator提取词汇进行训练,并生成一个CountVe ...
随机推荐
- 反向代理负载均衡之haproxy
在上篇安装的nginx的机器环境上将nginx停掉 /usr/local/nginx/sbin/nginx -s stop 在linux-node2上编译安装haproxy作为反向代理服务器 [roo ...
- 推荐3个Python初学者学习Python案例
回复资料,获取最新的Python的资料.想学习Python可以加微信回复报名. 希望今天的分享3个小案例,对你学习Python有帮助 Python 九九乘法表 以下实例演示了如何实现九九乘法表: 实例 ...
- CloudCC CRM探讨:精细流程管理与员工悟性培养
很多企业主招聘时更喜欢专业的销售,来给他们创造价值.老板不愿意花时间在"磨刀上",而喜欢员工一来就"砍柴".即使是建立培训机制,仍然很大程度依赖于员工自己的悟性 ...
- Leo2DNT(雷傲论坛转DiscuzNT)1.0转换程序发布
数据转换程序 雷傲论坛(Leobbs4.x) -> Discuz!NT V1.0 本转换程序基于Leobbs4.x设计 声明: 1.本程序只对数据作转换,不会对原来的雷傲论坛(数据 ...
- windows下遍历文件夹
Github地址 函数: HANDLE WINAPI FindFirstFile( _In_ LPCTSTR lpFileName, _Out_ LPWIN32_FIND_DATA lpFindFil ...
- 重新认识Java注解
重新认识Java注解 今天Debug看源码的时候,无意间看到这么个东西 首先承认我的无知,看到这个我很惊诧. 也勾起了我的好奇心,于是有了这篇认知记录. 下面就来重新认识下注解吧! 注解的本质 关于运 ...
- golang之channel
Buffered Channels package main import "fmt" func main() { ch := make(chan int, 2) ch <- ...
- Q - Play With Sequence HDU - 3971 线段树 重新排序建树
Q - Play With Sequence HDU - 3971 这个题目是一个线段树,比较特别的线段树,就是c询问一定次数之后重新排序建树来优化减低复杂度. 第一次碰到这种题目有点迷. 这个题目写 ...
- 前端【JS】,深入理解原型和原型链
对于原型和原型链,相信有很多伙伴都说的上来一些,但有具体讲不清楚.但面试的时候又经常会碰到面试官的死亡的追问,我们慢慢来梳理这方面的知识! 要理解原型和原型链的关系,我们首先需要了解几个概念:1.什么 ...
- VSCode最强助攻
VSCode最强助攻 VS Code是前端界必备的开发工具.页面仔小杨简单介绍几款高效.好用的插件,让原本单薄的VS Code如虎添翼,开发效率倍增. vscode-icons vscode-icon ...