Spark Mlib TFIDF源码详读 笔记

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

在提取文本特征时,经常用到TF-IDF算法。Spark Mlib实现了该算法。下面是Spark Mlib中,TF_IDF算法调用的一个实例:

def main(args:Array[String]){val sc: SparkContext = null                         // Load documents (one per line).val documents: RDD[Seq[String]] = sc.textFile("...").map(_.split(" ").toSeq)val hashingTF = new HashingTF()//计算tf val tf: RDD[Vector] = hashingTF.transform(documents)tf.cache()//得到idfModel对象 val idf = new IDF().fit(tf)//得到tf-idf值val tfidf: RDD[Vector] = idf.transform(tf)

要求输入数据  必须是一行一篇文章(切过词的),Spark Mlib中没有提供切词的工具,但给出了建议使用的切词工具 Stanford NLP Group and scalanlp/chalk

1、TF源码详读

在调用的代码中,我们找到

val hashingTF = new HashingTF()
//计算tf 
val tf: RDD[Vector] = hashingTF.transform(documents)

  获取TF,主要是通过HashingTF类的 transform方法,跟踪该方法

 

  /*** Transforms the input document to term frequency vectors.*/@Since("1.1.0")def transform[D <: Iterable[_]](dataset: RDD[D]): RDD[Vector] = {dataset.map(this.transform)}

 

SparkMlib是基于RDD的,所以在看源码前,必须要对RDD熟悉。再看 dataset.map(this.transform)中的transform方法:

 

 /*** Transforms the input document into a sparse term frequency vector.*/@Since("1.1.0")def transform(document: Iterable[_]): Vector = {//定义词频的mapval termFrequencies = mutable.HashMap.empty[Int, Double]//循环每篇文章里的每个词document.foreach { term =>//获取词项term对应的向量位置val i = indexOf(term)//i即代表这个词,统计次数放入termFrequenciestermFrequencies.put(i, termFrequencies.getOrElse(i, 0.0) + 1.0)}//将词特征映射到一个很大维度的向量中去 稀疏向量 numFeatures是类HashingTF的成员变量 可以在调用HashingTF传入,如果没有传入,默认为2的20次方Vectors.sparse(numFeatures, termFrequencies.toSeq)}

 

transform方法对每一行(即每篇文章)都会执行一次,主要是计算每篇文章里的词的词频,转存入一个维度很大的稀疏向量中,每个词在该向量中对应的位置就是:

 @Since("1.1.0")def indexOf(term: Any): Int = Utils.nonNegativeMod(term.##, numFeatures)

term.##相当于hashcode(),得到每个词的hash值,然后对numFeatures 取模,是个Int型的值

到此为止,TF就计算完了,最终的结果是一个存放词的位置,以及该词对应词频的 向量,即SparseVector(size, indices, values)

2、IDF源码详读     

      //得到idfModel对象 输入的tf类型是SparseVector(size, indices, values)val idf = new IDF().fit(tf)//得到tf-idf值val tfidf: RDD[Vector] = idf.transform(tf)

IDF实现主要通过两步:

第一步: val idf = new IDF().fit(tf)

 /*** Computes the inverse document frequency.* @param dataset an RDD of term frequency vectors*/@Since("1.1.0")def fit(dataset: RDD[Vector]): IDFModel = {//返回 IDF向量 类型是DenseVector(values)val idf = dataset.treeAggregate(new IDF.DocumentFrequencyAggregator(minDocFreq = minDocFreq))(///minDocFreq是词最小出现频率,不填是默认0seqOp = (df,v) => df.add(v),//计算combOp = (df1, df2) => df1.merge(df2)//合并).idf()new IDFModel(idf)}

上面treeAggregate方法原型是def treeAggregate[U: ClassTag](zeroValue: U)( seqOp: (U, T) => U, combOp: (U, U) =>U, depth: Int = 2): U    

treeAggregate是使用mapPartition进行计算的,需定义两个操作符,一个用来计算,一个用来合并结果

 seqOp 用来计算分区结果的操作符 (an operator used to accumulate results within a partition)

combOp 用来组合来自不同分区结果的关联操作符( an associative operator used to combine results from different partitions)

该方法的调用返回new IDF.DocumentFrequencyAggregator对象,接着又调用DocumentFrequencyAggregator的idf方法,返回idf向量,然后又通过new IDFModel(idf)返回IDFModel对象

下面是 DocumentFrequencyAggregator 类的方法,即一个add(seqOp)一个merge(combOp

 

private object IDF {/** Document frequency aggregator. */class DocumentFrequencyAggregator(val minDocFreq: Int) extends Serializable {/** number of documents 文档总数量*/ private var m = 0L/** document frequency vector df向量,词在出现过的文档个数*/private var df: BDV[Long] = _def this() = this(0) //构造方法,如果minDocFreq没有传入的话,默认值为0/** Adds a new document. 这个地方就是执行的每个分区里的计算操作 ,输入是tf向量*/def add(doc: Vector): this.type = {if (isEmpty) {df = BDV.zeros(doc.size)}doc match {//tf向量是 SparseVector 所以会走这个casecase SparseVector(size, indices, values) =>val nnz = indices.sizevar k = 0while (k < nnz) {if (values(k) > 0) {df(indices(k)) += 1L //如果词在文章中出的频率大于0,则该词的df+1}k += 1}case DenseVector(values) =>val n = values.sizevar j = 0while (j < n) {if (values(j) > 0.0) {df(j) += 1L}j += 1}case other =>throw new UnsupportedOperationException(s"Only sparse and dense vectors are supported but got ${other.getClass}.")}m += 1Lthis}/** Merges another. 这个地方就是执行所有分区的合并操作*/def merge(other: DocumentFrequencyAggregator): this.type = {if (!other.isEmpty) {m += other.m //总文档数合并if (df == null) {df = other.df.copy} else {df += other.df //df向量合并}}this}private def isEmpty: Boolean = m == 0L/** Returns the current IDF vector. 计算idf向量的方法 */def idf(): Vector = {if (isEmpty) {throw new IllegalStateException("Haven't seen any document yet.")}val n = df.lengthval inv = new Array[Double](n)var j = 0while (j < n) {/** If the term is not present in the minimum* number of documents, set IDF to 0. This* will cause multiplication in IDFModel to* set TF-IDF to 0.** Since arrays are initialized to 0 by default,* we just omit changing those entries.*/if (df(j) >= minDocFreq) { //如果df大于设定的值,就计算idf的值,如果不大于的话,就直接设置为0inv(j) = math.log((m + 1.0) / (df(j) + 1.0))}j += 1}Vectors.dense(inv) //返回idf 密集向量}}
}

第二步:通过上面的计算得到idf向量,剩下的工作就是计算 tf*idf了,会用到IDFMode类中的transform方法 val tfidf: RDD[Vector] = idf.transform(tf)

private object IDFModel {/*** Transforms a term frequency (TF) vector to a TF-IDF vector with a IDF vector** @param idf an IDF vector* @param v a term frequence vector* @return a TF-IDF vector*/def transform(idf: Vector, v: Vector): Vector = {val n = v.sizev match {//会进入这个casecase SparseVector(size, indices, values) =>val nnz = indices.sizeval newValues = new Array[Double](nnz)var k = 0while (k < nnz) {newValues(k) = values(k) * idf(indices(k)) //计算tf*idfk += 1}Vectors.sparse(n, indices, newValues) //TFIDF向量case DenseVector(values) =>val newValues = new Array[Double](n)var j = 0while (j < n) {newValues(j) = values(j) * idf(j)j += 1}Vectors.dense(newValues)case other =>throw new UnsupportedOperationException(s"Only sparse and dense vectors are supported but got ${other.getClass}.")}}
}

以上就是整个TFIDF的计算过程,用到Spark Mlib 的密集向量(DenseVector)和稀疏向量(SparseVector) 、RDD的聚合操作

主要相关的类有三个:HashingTF 、IDF、IDFModel 

还有就是利用spark Mlib 的TFIDF生成的TFIDF向量,位置信息存是词hash后和向量维度取模后的值,而不是该词,在后面做一些分类,或者文本推荐的时候,如果需要用到词本身,还需要做调整

 

转载于:https://my.oschina.net/xiaoluobutou/blog/670367

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/543296.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

焦作的计算机三级考试考点,3月河南计算机等级考试考点分布情况

摘要&#xff1a; 3月河南计算机等级考试考点分布情况为你介绍河南计算机等级考试时间为2020年3月28日-30日&#xff0c;为了方便河南考生。下面是小编整理的2020年3月河南计算机等级考试报...河南计算机等级考试时间为2020年3月28日-30日&#xff0c;为了方便河南考生。下面是…

js客户端存储之Web存储

WEB存储 客户端存储有几种方式&#xff0c;WEB存储就是其中一种。最初作为H5的一部分被定义成API形式&#xff0c;后来被剥离出来作为独立的标准。所描述的API包含localStorage对象和sessionStorage对象&#xff0c;这两个对象实际都代表同一个Storage对象&#xff0c;是持久化…

直播软件自动化测试,基于SRS-Bench工具的直播平台性能测试

摘要&#xff1a;性能测试通过自动化的测试工具模拟正常、异常场景来对系统的各项性能指标进行测试。通过性能测试可以分析一个系统能力、瓶颈、关键问题等。本文结合直播平台的部分场景&#xff0c;使用开源SRS-Bench工具对直播并的4个场景进行压力测试&#xff0c;测试直播平…

pythontype函数使用_Python astype(np.float)函数使用方法解析

Python astype(np.float)函数使用方法解析 我的数据库如图结构我取了其中的name age nr&#xff0c;做成array&#xff0c;只要所取数据存在str型&#xff0c;那么取出的数据&#xff0c;全部转化为str型&#xff0c;也就是array阵列的元素全是str&#xff0c;不管数据库定义的…

计算机硬件加速怎么开,显卡硬件加速,小编教你电脑怎么开启显卡硬件加速

小编之前是不知道怎么开启显卡硬件加速的&#xff0c;后来是朋友教我&#xff0c;才知道原来开启显卡硬件加速并不难操作。所以今天小编也将者开启的方法分享给小伙伴们。让你们也知道怎么开启硬件加速。显卡硬件加速有什么作用呢&#xff1f;其实开启了显卡硬件加速&#xff0…

um是代表什么意思_女生约会心里都想什么?女生约会举动代表什么意思

在和女生约会的全过程中&#xff0c;女生的思绪通常都较为细致&#xff0c;1个目光、1个行为、1个中停……都将会蕴含了無限的含意&#xff0c;男生们假如愿意取得成功将女生追到手&#xff0c;很必须剖析一下下女生约会心里都想干什么&#xff0c;女生约会举动代表什么意思&am…

ubuntu mysql开发_ubuntu linux mysql 开发模式与连接编译

【源码 测试】#include #include #include int main(void){MYSQL_RES *result;MYSQL_ROW row;MYSQL *connection, mysql;int state;mysql_init(&mysql);connection mysql_real_connect(&mysql,"localhost","root","951241","mysql…

浅谈浏览器http的缓存机制

针对浏览器的http缓存的分析也算是老生常谈了&#xff0c;每隔一段时间就会冒出一篇不错的文章&#xff0c;其原理也是各大公司面试时几乎必考的问题。 之所以还写一篇这样的文章&#xff0c;是因为近期都在搞新技术&#xff0c;想“回归”下基础&#xff0c;也希望尽量总结的更…

计算机故障检修课过时,第三场公开课|电脑故障维修以及笔记本知识科普

原标题&#xff1a;第三场公开课|电脑故障维修以及笔记本知识科普等待了漫长的一个星期&#xff0c; R&D又带着满满的干货和大家见面啦~你没看错&#xff01;&#xff01;这次R&D带来了两份大大的干货给大家分别是大家关心的 电脑故障维修问题以及大家想要了解的 笔记本…

为什么说python是计算机语言的未来_Python这么火,为什么说它不是未来的编程语言?...

本文转载自公众号“读芯术”(ID&#xff1a;AI_Discovery) Python这两年非常火&#xff0c;随处可见的广告让它逐渐变成了一种老少皆知的存在。 虽然问世几十年后&#xff0c;Python才得到编程社区的重视。 但是&#xff0c;其发展的势头似乎无人能及。自2010年年初以来&#x…

win 杀掉占用的端口_Windows netstat 查看端口、进程占用、杀掉进程

转载&#xff1a;http://ywsm.iteye.com/blog/510670http://58582786.blog.51cto.com/1550000/671487目标&#xff1a;在Windows环境下&#xff0c;用netstat命令查看某个端口号是否占用&#xff0c;为哪个进程所占用.操作&#xff1a;操作分为两步&#xff1a;(1)查看该端口被…

C++课程上 有关“指针” 的小结

上完了C的第二节课以后&#xff0c;觉得应该对这个内容进行一个小结&#xff0c;巩固知识点&#xff0c;并对我的心情进行了一个侧面烘托... 开始上课的老师&#xff1a; 正在上课的我&#xff1a; 上去敲代码的我&#xff1a; 过程是这样的&#xff1a; 下来的我&#xff1a; …

python安装路径查看_查看python安装路径及pip安装的包列表及路径

一、Linux系统 查看Python路径 whereis python 此命令将会列出系统所安装的所有版本的Python的路径效果如下&#xff1a;使用以下命令可分别查看Python2&#xff0c;Python3的安装路径 whereis python2 whereis python3 查看使用pip安装的软件包 默认Python3 pip list Python2查…

python循环10次_开发一个循环 5 次计算的小游戏, 设置随机种子为10,每次随机产生两个 1~10的数字以及随机选择...

开发一个循环 5 次计算的小游戏&#xff0c;设置随机种子为10&#xff0c;每次随机产生两个 1~10的数字以及随机选择“、-、*”运算符&#xff0c;构成一个表达式&#xff0c;让用户计算式子结果并输入结果&#xff0c;如果计算结果正确则加一分&#xff0c;如果计算结果错误不…

配置java编译环境

2019独角兽企业重金招聘Python工程师标准>>> (1)我的电脑属性->高级系统配置->环境变量(2)点击第二个"新建(W)..."->输入变量名"JAVA_PATH",变量值"C:\Program Files\Java\jdk1.8.0_91"->确定(3)找系统变量中的Path并双击…

spring aop 必须的包 及里面用到的东西_Spring 原理初探——IoC、AOP

前言众所周知&#xff0c; 现在的 Spring 框架已经成为构建企业级 Java 应用事实上的标准了&#xff0c;众多的企业项目都构建在 Spring 项目及其子项目之上&#xff0c;特别是 Java Web 项目。Spring 的两个核心概念是 IoC(控制反转)和 AOP(面向切面编程)。想了解 Spring 的工…

Android平台和java平台 DES加密解密互通程序及其不能互通的原因

为什么80%的码农都做不了架构师&#xff1f;>>> 网上的demo一搜一大堆&#xff0c;但是&#xff0c;基本上都是一知半解&#xff08;包括我&#xff09;。为什么呢&#xff1f;我在尝试分别在两个平台加密的时候&#xff0c;竟然发现Android DES 加密和Java DES加密…

PDM系统服务器管理,基于PDM的异地协同设计系统

基于PDM的异地协同设计系统随着经济全球化的不断发展&#xff0c;产品设计分工越来越细&#xff0c;产品协同设计团队越来越分散。由于产品设计的需要&#xff0c;分布在不同地方的设计人员和其他相关人员都要参与产品的开发过程&#xff0c;各自承担相应的设计任务&#xff0c…

flex 下对齐_Flex 布局示例

感谢阮一峰老师的教程http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html本示例将教程上所有的布局都简单的实现了一遍&#xff0c;并保存GitHub上面https://github.com/JailBreakC/flex-box-demo​github.com容器的属性1、flex-direction属性flex-direction属性决定主…

python读csv最快方法_使用Python读写csv文件的三种方法

Python读写csv文件觉得有用的话,欢迎一起讨论相互学习~Follow Me前言逗号分隔值(Comma-Separated Values&#xff0c;CSV&#xff0c;有时也称为字符分隔值&#xff0c;因为分隔字符也可以不是逗号)&#xff0c;其文件以纯文本形式存储表格数据(数字和文本)。纯文本意味着该文件…