spark厦大---机器学习工作流(ML Pipelines)—— spark.ml包

来源:http://mocom.xmu.edu.cn/article/show/5858a6062b2730e00d70fa06/0/1


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

在介绍工作流之前,我们先来了解几个重要概念:

  • DataFrame:使用Spark SQL中的DataFrame作为数据集,它可以容纳各种数据类型。 较之 RDD,包含了 schema 信息,更类似传统数据库中的二维表格。它被 ML Pipeline 用来存储源数据。例如,DataFrame中的列可以是存储的文本,特征向量,真实标签和预测的标签等。

  • Transformer:翻译成转换器,是一种可以将一个DataFrame转换为另一个DataFrame的算法。比如一个模型就是一个 Transformer。它可以把 一个不包含预测标签的测试数据集 DataFrame 打上标签,转化成另一个包含预测标签的 DataFrame。技术上,Transformer实现了一个方法transform(),它通过附加一个或多个列将一个DataFrame转换为另一个DataFrame。

  • Estimator:翻译成评估器,它是学习算法或在训练数据上的训练方法的概念抽象。在 Pipeline 里通常是被用来操作 DataFrame 数据并生产一个 Transformer。从技术上讲,Estimator实现了一个方法fit(),它接受一个DataFrame并产生一个转换器。如一个随机森林算法就是一个 Estimator,它可以调用fit(),通过训练特征数据而得到一个随机森林模型。

  • Parameter:Parameter 被用来设置 Transformer 或者 Estimator 的参数。现在,所有转换器和估计器可共享用于指定参数的公共API。ParamMap是一组(参数,值)对。

  • PipeLine:翻译为工作流或者管道。工作流将多个工作流阶段(转换器和估计器)连接在一起,形成机器学习的工作流,并获得结果输出。

工作流如何工作

要构建一个 Pipeline工作流,首先需要定义 Pipeline 中的各个工作流阶段PipelineStage,(包括转换器和评估器),比如指标提取和转换模型训练等。有了这些处理特定问题的转换器和 评估器,就可以按照具体的处理逻辑有序的组织PipelineStages 并创建一个Pipeline。比如:

val pipeline = new Pipeline().setStages(Array(stage1,stage2,stage3,…))

然后就可以把训练数据集作为输入参数,调用 Pipeline 实例的 fit 方法来开始以流的方式来处理源训练数据。这个调用会返回一个 PipelineModel 类实例,进而被用来预测测试数据的标签。更具体的说,工作流的各个阶段按顺序运行,输入的DataFrame在它通过每个阶段时被转换。 对于Transformer阶段,在DataFrame上调用transform()方法。 对于估计器阶段,调用fit()方法来生成一个转换器(它成为PipelineModel的一部分或拟合的Pipeline),并且在DataFrame上调用该转换器的transform()方法。

上面,顶行表示具有三个阶段的流水线。 前两个(Tokenizer和HashingTF)是Transformers(蓝色),第三个(LogisticRegression)是Estimator(红色)。 底行表示流经管线的数据,其中圆柱表示DataFrames。 在原始DataFrame上调用Pipeline.fit()方法,它具有原始文本文档和标签。 Tokenizer.transform()方法将原始文本文档拆分为单词,向DataFrame添加一个带有单词的新列。 HashingTF.transform()方法将字列转换为特征向量,向这些向量添加一个新列到DataFrame。 现在,由于LogisticRegression是一个Estimator,Pipeline首先调用LogisticRegression.fit()产生一个LogisticRegressionModel。 如果流水线有更多的阶段,则在将DataFrame传递到下一个阶段之前,将在DataFrame上调用LogisticRegressionModel的transform()方法。

值得注意的是,工作流本身也可以看做是一个估计器。在工作流的fit()方法运行之后,它产生一个PipelineModel,它是一个Transformer。 这个管道模型将在测试数据的时候使用。 下图说明了这种用法。

在上图中,PipelineModel具有与原始流水线相同的级数,但是原始流水线中的所有估计器都变为变换器。 当在测试数据集上调用PipelineModel的transform()方法时,数据按顺序通过拟合的工作流。 每个阶段的transform()方法更新数据集并将其传递到下一个阶段。工作流和工作流模型有助于确保培训和测试数据通过相同的特征处理步骤。 



工作流(ML Pipelines)例子

本节以逻辑斯蒂回归为例,构建一个典型的机器学习过程,来具体介绍一下工作流是如何应用的。我们的目的是查找出所有包含"spark"的句子,即将包含"spark"的句子的标签设为1,没有"spark"的句子的标签设为0。

首先,导入工作流和逻辑斯蒂回归所需要的包。

import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.sql.SQLContext
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.mllib.linalg.Vector
import org.apache.spark.sql.Row

接下来,根据SparkContext来创建一个SQLContext,其中sc是一个已经存在的SparkContext;然后导入sqlContext.implicits._来实现RDD到Dataframe的隐式转换。

scala> val sqlContext = new SQLContext(sc)
sqlContext: org.apache.spark.sql.SQLContext = org.apache.spark.sql.SQLContext@129bfa97

scala> import sqlContext.implicits._
import sqlContext.implicits._

然后,我们构建训练数据集。

scala> val training = sqlContext.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")
training: org.apache.spark.sql.DataFrame = [id: bigint, text: string, label: double]

在这一步中我们要定义 Pipeline 中的各个工作流阶段PipelineStage,包括转换器和评估器,具体的,包含tokenizer, hashingTF和lr三个步骤。

scala> val tokenizer = new Tokenizer().
     |       setInputCol("text").
     |       setOutputCol("words")
tokenizer: org.apache.spark.ml.feature.Tokenizer = tok_5151ed4fa43e

scala> val hashingTF = new HashingTF().
     |       setNumFeatures(1000).
     |       setInputCol(tokenizer.getOutputCol).
     |       setOutputCol("features")
hashingTF: org.apache.spark.ml.feature.HashingTF = hashingTF_332f74b21ecb

scala> val lr = new LogisticRegression().
     |       setMaxIter(10).
     |       setRegParam(0.01)
lr: org.apache.spark.ml.classification.LogisticRegression = logreg_28a670ae952f

有了这些处理特定问题的转换器和评估器,接下来就可以按照具体的处理逻辑有序的组织PipelineStages 并创建一个Pipeline。

scala> val pipeline = new Pipeline().
     |       setStages(Array(tokenizer, hashingTF, lr))
pipeline: org.apache.spark.ml.Pipeline = pipeline_4dabd24db001

现在构建的Pipeline本质上是一个Estimator,在它的fit()方法运行之后,它将产生一个PipelineModel,它是一个Transformer。

scala> val model = pipeline.fit(training)
model: org.apache.spark.ml.PipelineModel = pipeline_4dabd24db001

我们可以看到,model的类型是一个PipelineModel,这个管道模型将在测试数据的时候使用。所以接下来,我们先构建测试数据。

 scala> val test = sqlContext.createDataFrame(Seq(
     |       (4L, "spark i j k"),
     |       (5L, "l m n"),
     |       (6L, "spark a"),
     |       (7L, "apache hadoop")
     |     )).toDF("id", "text")
test: org.apache.spark.sql.DataFrame = [id: bigint, text: string]

然后,我们调用我们训练好的PipelineModel的transform()方法,让测试数据按顺序通过拟合的工作流,生成我们所需要的预测结果。

scala> 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")
     |       }
(4, spark i j k) --> prob=[0.5406433544851421,0.45935664551485783], prediction=0.0
(5, l m n) --> prob=[0.9334382627383259,0.06656173726167405], prediction=0.0
(6, spark a) --> prob=[0.15041430048068286,0.8495856995193171], prediction=1.0
(7, apache hadoop) --> prob=[0.9768636139518304,0.023136386048169585], prediction=0.0

通过上述结果,我们可以看到,第4句和第6句中都包含"spark",其中第六句的预测是1,与我们希望的相符;而第4句虽然预测的依然是0,但是通过概率我们可以看到,第4句有46%的概率预测是1,而第5句、第7句分别只有7%和2%的概率预测为1,这是由于训练数据集较少,如果有更多的测试数据进行学习,预测的准确率将会有显著提升。 


你可能感兴趣的:(spark厦大---机器学习工作流(ML Pipelines)—— spark.ml包)