Spark Mllib框架1
1. 概述
1.1 功能
MLlib是Spark的机器学习(machine learing)库,其目标是使得机器学习的使用更加方便和简单,其具有如下功能:
- ML算法:常用的学习算法,包括分类、回归、聚类和过滤;
- 特征:特征萃取、转换、降维和选取;
- Pipelines:其是一个工具,目标是用于构建、测量和调节;
- 使用工具:包括线性代数、统计学习和数据操作等等。
1.2 API架包
MLlib有两个API架包:
2. Pipelines Components
MLlib标准化机器学习算法的API,使得更容易将多个算法组合成到单个管道(工作流)。其设计思想是受到Scikit-learn项目的启发。
- DataFrame:MLlib的数据使用Spark SQL中的DataFrame结构来存储,即用户的数据集和模型的输出标签都是以此结构存储,包括Pipeline内部数据的传输都是以此结构存储;
- Transformer:MLlib将算法模型用Transformer结构来表示,其以一个DataFrame数据作为输入,通过模型计算后转换为一个DataFrame数据;
- Estimator:Estimator结构也表示一种算法,但其以一个DataFrame数据作为输入,通过模型计算后转换为一个Transformer对象,而不是DataFrame数据;
- Pipeline:MLlib使用Pipeline来组织多个ML模型,即其内部有多个Transformer和Estimator对象,从而组成一个算法工作流;
- Parameter:MLlib使用Parameter结构来存储参数,用户通过这些参数来配置和调节模型。即在一个Pipeline对象内的所有Transformer和Estimator对象都共享一个Parameter对象。
2.1 DataFrame
机器学习中数据集是由一个个样本组成,而每个样本其实是一条有多个特征组成的记录,从而数据集其实是一个矩阵结构。而Spark SQL中的DataFrame结构也拥有类似的结构,DataFrame内部有一行行的数据Row组成,每个Row对象内部也可以由多个属性组成。从而MLlib使用DataFrame来描述机器学习中的数据集正好不过了。
Spark SQL的DataFrame其实一种Dataset类型,只是存储的是Row元素,如下Spark源码所示:
Package object sql{ …… type DataFrame = Dataset[Row] } |
2.2 Pipeline
MLlib使用Pipeline来组织多个ML模型,其内部有多个Transformer和Estimator对象,从而组成一个算法工作流。在Spark ML中与Pipeline相关联的类如图 1所示。从图中可明显看出Transformer和Estimator都是PipelineStage抽象类的子类;并且Pipeline类内部有一个stages数组来存储PipelineStage对象,即存放Transformer和Estimator对象;当用户调用Pipeline的fit()方法时,将产生一个PipelineModel对象;PipelineModel类有一个transform()方法能返回一个DataFrame对象。
图 1
3. 工作机制
Pipeline是由一系列stage组成,这些stage有两种类型:Transformer和Estimator。Stage在Pipeline的运行是有序的,而且输入的DataFrame会在stage中被转换和传递。若stage是Transformer类型,则对条用Transformer对象的transform()方法将输入的DataFrame转换为另一种DataFrame;若stage是Estimator类型,则会调用Estimator对象的fit()方法产生Transformer对象,调用该Transformer对象的transform()方法一样会产生一个DataFrame。
可以将上述这一段,详细解释为两个过程:模型训练和模型预测,如下所示:
3.1 模型训练
Pipeline对象内部有一个stages容器,存放多个Transformer对象和一个Estimator对象。当用户调用Pipeline对象的fit()方法时,会接收输入的DataFrame,然后在这些stage中被转换和传递。当传递到最后一个stage(Estimator对象)时,将生成一个PipelineModel对象(Transformer子类),如图 2所示。
图 2
用户调用上图中Pipeline的fit()时,会将stages容器存放的所有Transformer对象和Estimator对象生成的Transformer对象都添加到PipelineModel对象中,该对象有一个stages容器(Array[Transformer]类型),其能够存放Transformer对象。
通过Spark源码,可以查看Pipeline类中的fit()内容如下所示:
override def fit(dataset: Dataset[_]): PipelineModel = { transformSchema(dataset.schema, logging = true) val theStages = $(stages) … var curDataset = dataset val transformers = ListBuffer.empty[Transformer] theStages.view.zipWithIndex.foreach { case (stage, index) => if (index <= indexOfLastEstimator) { val transformer = stage match { case estimator: Estimator[_] =>//若是Estimator对象,则调用fit()方法生成一个Transformer estimator.fit(curDataset) case t: Transformer =>//若是Transformer对象,则直接返回 t case _ => throw new IllegalArgumentException( s"Does not support stage $stage of type ${stage.getClass}") } if (index < indexOfLastEstimator) { curDataset = transformer.transform(curDataset)//如果不是最后的对象,则调用transformer对象的transform方法,生成一个DataFrame } transformers += transformer //将生成的所有Transformer对象都添加到一个list中 } else { transformers += stage.asInstanceOf[Transformer] } } new PipelineModel(uid, transformers.toArray).setParent(this) //最后创建PipelineModel对象,并传递上述的Transformer列表。 } |
3.2 模型预测
在模型训练阶段会通过向Pipeline的fit()方法传递DataFrame数据来训练模型,从而生成一个PipelineModel对象(Transformer子类),该对象内部有一个stages容器,存放了所有Transformer对象。
当进行模型预测时,即通过向PipelineModel对象的transform传递一个DataFrame数据来预测时,会依序调用其stages容器中的Transformer对象,每个Transformer对象都有一个DataFrame输入和一个DataFrame的输出,最后生成一个DataFrame作为用户的输出,如图 3所示。
图 3
类似,可以查看PipelineModel对象的transform()方法,如下所示:
override def transform(dataset: Dataset[_]): DataFrame = { transformSchema(dataset.schema, logging = true) stages.foldLeft(dataset.toDF)((cur, transformer) => transformer.transform(cur)) } |
stages.foldLeft(dataset.toDF)((cur, transformer) => transformer.transform(cur))语句正是图 3的实现,即第一次输入数据是dataset.toDF,然后每次调用transformer.transform(cur))方法,产生的DataFrame输出作为下一次的输入。
3.3 关系总结
通过上述Pipeline工作机制的分析,现在从机器学习的角度总结一下Pipeline、Transformer和Estimator三者之间的关系,如图 4所示。
图 4
- Transformer:是对数据进行预处理,如特征向量萃取、向量转换或降维;
- Estimator:机器学习的某种算法,如线性回归、贝叶斯或支持向量机;
- Pipeline:是一种算法组织者,将Transformer和Estimator组织成有序的执行过程。
4. Examples
4.1 Estimator、Transformer和Param
本节以Estimator类为例,没有使用Pipeline结构来组织Estimator和Transformer对象。Estimator类可以单独使用,不需要Pipeline结构也能工作,此时Estimator类似Scikit-learn框架。首先,用户直接调用Estimator对象的fit()方法来训练数据;然后,根据fit()方法返回的Transformer对象,用户接着调用Transformer的transform()方法来预测或测试;
如下所示的完整程序:
// scalastyle:off println package org.apache.spark.examples.ml // $example on$ import org.apache.spark.ml.classification.LogisticRegression import org.apache.spark.ml.linalg.{Vector, Vectors} import org.apache.spark.ml.param.ParamMap import org.apache.spark.sql.Row // $example off$ import org.apache.spark.sql.SparkSession object EstimatorTransformerParamExample { def main(args: Array[String]): Unit = { val spark = SparkSession .builder .appName("EstimatorTransformerParamExample") .getOrCreate() // $example on$ // Prepare training data from a list of (label, features) tuples. val training = spark.createDataFrame(Seq( (1.0, Vectors.dense(0.0, 1.1, 0.1)), (0.0, Vectors.dense(2.0, 1.0, -1.0)), (0.0, Vectors.dense(2.0, 1.3, 1.0)), (1.0, Vectors.dense(0.0, 1.2, -0.5)) )).toDF("label", "features") // Create a LogisticRegression instance. This instance is an Estimator. val lr = new LogisticRegression() // Print out the parameters, documentation, and any default values. println("LogisticRegression parameters:\n" + lr.explainParams() + "\n") // We may set parameters using setter methods. lr.setMaxIter(10) .setRegParam(0.01) // Learn a LogisticRegression model. This uses the parameters stored in lr. val model1 = lr.fit(training) // Since model1 is a Model (i.e., a Transformer produced by an Estimator), // we can view the parameters it used during fit(). // This prints the parameter (name: value) pairs, where names are unique IDs for this // LogisticRegression instance. println("Model 1 was fit using parameters: " + model1.parent.extractParamMap) // We may alternatively specify parameters using a ParamMap, // which supports several methods for specifying parameters. val paramMap = ParamMap(lr.maxIter -> 20) .put(lr.maxIter, 30) // Specify 1 Param. This overwrites the original maxIter. .put(lr.regParam -> 0.1, lr.threshold -> 0.55) // Specify multiple Params. // One can also combine ParamMaps. val paramMap2 = ParamMap(lr.probabilityCol -> "myProbability") // Change output column name. val paramMapCombined = paramMap ++ paramMap2 // Now learn a new model using the paramMapCombined parameters. // paramMapCombined overrides all parameters set earlier via lr.set* methods. val model2 = lr.fit(training, paramMapCombined) println("Model 2 was fit using parameters: " + model2.parent.extractParamMap) // Prepare test data. val test = spark.createDataFrame(Seq( (1.0, Vectors.dense(-1.0, 1.5, 1.3)), (0.0, Vectors.dense(3.0, 2.0, -0.1)), (1.0, Vectors.dense(0.0, 2.2, -1.5)) )).toDF("label", "features") // Make predictions on test data using the Transformer.transform() method. // LogisticRegression.transform will only use the 'features' column. // Note that model2.transform() outputs a 'myProbability' column instead of the usual // 'probability' column since we renamed the lr.probabilityCol parameter previously. model2.transform(test) .select("features", "label", "myProbability", "prediction") .collect() .foreach { case Row(features: Vector, label: Double, prob: Vector, prediction: Double) => println(s"($features, $label) -> prob=$prob, prediction=$prediction") } // $example off$ spark.stop() } } |
其实Estimator类的单独使用,也可以理解为Pipeline对象只有一个Estimator对象。上述的程序来自:\src\main\scala\org\apache\spark\examples\ml\ ElementwiseProductExample.scala
4.2 Pipeline
输入的Dataframe经过PipelineStage对象处理后悔输出新的DataFrame,此时输出的DataFrame会增加一些列,即增加了一些特征,而具体增加什么列,需要看具体是什么PipelineStage对象。
如下所示,输入DataFrame只有三列"id"、"text"、"label",但输出DataFrame不仅保存了输入列,同时增加了一些列。
package org.apache.spark.examples.ml // $example on$ import org.apache.spark.ml.{Pipeline, PipelineModel} import org.apache.spark.ml.classification.LogisticRegression import org.apache.spark.ml.feature.{HashingTF, Tokenizer} import org.apache.spark.ml.linalg.Vector import org.apache.spark.sql.Row // $example off$ import org.apache.spark.sql.SparkSession object PipelineExample { def main(args: Array[String]): Unit = { val spark = SparkSession .builder .appName("PipelineExample") .getOrCreate() // $example on$ // Prepare training documents from a list of (id, text, label) tuples. val training = spark.createDataFrame(Seq( (0L, "a b c d e spark", 1.0), (1L, "b d", 0.0), (2L, "spark f g h", 1.0), (3L, "hadoop mapreduce", 0.0) )).toDF("id", "text", "label") // Configure an ML pipeline, which consists of three stages: tokenizer, hashingTF, and lr. //Tokenizer功能是对输入的DataFrame某一列进行分割,分割后将数据添加到DataFrame的新列种 val tokenizer = new Tokenizer() .setInputCol("text") //设置输入DataFrame中要处理的列名字 .setOutputCol("words") //设置输出的DataFrame中增加列的名字 val hashingTF = new HashingTF() .setNumFeatures(1000) .setInputCol(tokenizer.getOutputCol) .setOutputCol("features") val lr = new LogisticRegression() .setMaxIter(10) .setRegParam(0.001) val pipeline = new Pipeline() .setStages(Array(tokenizer, hashingTF, lr)) // Fit the pipeline to training documents. val model = pipeline.fit(training) // Now we can optionally save the fitted pipeline to disk model.write.overwrite().save("/tmp/spark-logistic-regression-model") // We can also save this unfit pipeline to disk pipeline.write.overwrite().save("/tmp/unfit-lr-model") // And load it back in during production val sameModel = PipelineModel.load("/tmp/spark-logistic-regression-model") // Prepare test documents, which are unlabeled (id, text) tuples. val test = spark.createDataFrame(Seq( (4L, "spark i j k"), (5L, "l m n"), (6L, "spark hadoop spark"), (7L, "apache hadoop") )).toDF("id", "text") // Make predictions on test documents. model.transform(test) .select("id", "text", "probability", "prediction") .collect() .foreach { case Row(id: Long, text: String, prob: Vector, prediction: Double) => println(s"($id, $text) --> prob=$prob, prediction=$prediction") } // $example off$ spark.stop() } } |
5. 参考文献
Spark Mllib框架1的更多相关文章
- Spark MLlib框架详解
1. 概述 1.1 功能 MLlib是Spark的机器学习(machine learing)库,其目标是使得机器学习的使用更加方便和简单,其具有如下功能: ML算法:常用的学习算法,包括分类.回归.聚 ...
- 《Spark MLlib机器学习实践》内容简介、目录
http://product.dangdang.com/23829918.html Spark作为新兴的.应用范围最为广泛的大数据处理开源框架引起了广泛的关注,它吸引了大量程序设计和开发人员进行相 ...
- Spark入门实战系列--8.Spark MLlib(上)--机器学习及SparkMLlib简介
[注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .机器学习概念 1.1 机器学习的定义 在维基百科上对机器学习提出以下几种定义: l“机器学 ...
- Spark MLlib 之 Vector向量深入浅出
Spark MLlib里面提供了几种基本的数据类型,虽然大部分在调包的时候用不到,但是在自己写算法的时候,还是很需要了解的.MLlib支持单机版本的local vectors向量和martix矩阵,也 ...
- Spark MLlib介绍
Spark MLlib介绍 Spark之所以在机器学习方面具有得天独厚的优势,有以下几点原因: (1)机器学习算法一般都有很多个步骤迭代计算的过程,机器学习的计算需要在多次迭代后获得足够小的误差或者足 ...
- spark mllib lda 中文分词、主题聚合基本样例
github https://github.com/cclient/spark-lda-example spark mllib lda example 官方示例较为精简 在官方lda示例的基础上,给合 ...
- Spark MLlib - LFW
val path = "/usr/data/lfw-a/*" val rdd = sc.wholeTextFiles(path) val first = rdd.first pri ...
- Spark学习(二) -- Spark整体框架
标签(空格分隔): Spark 还记得上次的wordCount程序嘛?通过这个小程序,我们来一窥Spark的框架是什么样子的. sc.textFile("/usr/local/Cellar/ ...
- Spark MLlib 之 Basic Statistics
Spark MLlib提供了一些基本的统计学的算法,下面主要说明一下: 1.Summary statistics 对于RDD[Vector]类型,Spark MLlib提供了colStats的统计方法 ...
随机推荐
- Oracle 12C 新特性 - “可插拔数据库”功能
Oracle 12C加入了一个非常有新意的功能"可插拔数据库"特性,实现了数据库(PDB)在"容器"(CDB)上的拔功能,既能提高系统资源的利用率,也简化大面积 ...
- Node.js 入门:Express + Mongoose 基础使用
前言 Express 是基于 Node.js 平台的 web 应用开发框架,在学习了 Node.js 的基础知识后,可以使用 Express 框架来搭建一个 web 应用,实现对数据库的增删查改. 数 ...
- iOS 配置
1.git的配置 使用Github,也许大家觉得比较麻烦的就是在每次push的时候,都需要输入用户名和密码.如果使用SSH,就可以记住用户名,并创建属于自己的密码来保证安全操作,还有神奇的一招可以“不 ...
- 【原创】-- C# 点滴积累 -- String
一.string.Format() 将[数字字符串]转为两位小数显示的字符串: dt = dtResult.Select(it => new CommodityPriceLimitEntity ...
- 30. leetcode 121. Best Time to Buy and Sell Stock
121. Best Time to Buy and Sell Stock Say you have an array for which the ith element is the price of ...
- hdu--1077--Catching Fish
思路: 1.枚举两点确定圆心,大于2不用考虑 2.逐个判断判断距圆心的距离小于1.00001符合题意 这个题,主要在求圆心上废了不少功夫,但是仍存在问题 #include<iostrea ...
- 转载的log4cplus使用指南
以下转载的log4cplus使用指南的版本可能不是最新,仅作参考了解.应以最新安装包中的示例代码为准. 目 录1 Log4cplus简介 52 安装方法 53 主要类说明 64 ...
- MySQL的MySQL 的JDBC的安装与使用
1.配置好环境,主要是安装mysql和下载连接需要的包(我用的是mysql-connector-java-5.1.3-rc-bin.jar) 2.注册驱动 Class.forName("c ...
- C#快速入门
一.简介 1.C#是由Anders Hejlsberg和他的团队在.Net框架开发期间开发的:是.Net框架的一部分. C#是专为公共语言基础结构(CLI)设计的,CLI由可执行代码和运行时环境组成, ...
- vue2.0 新手教程
想想自己写vue的项目也写了一年了,从vue1.0到2.0,走过不少路,填过不少坑, 下面记录一下新手从0到1的过程,本文“应该”会持续更新 首先安装vue的运行环境node 1.下载Nodejs并安 ...