Spark mllib的Pipeline

Spark Pipeline API的灵感来自scikit-learn,旨在简化机器学习流程的创建,调优和检验。
ML Pipeline通常由一下几个阶段构成:

  1. 数据预处理
  2. 特征提取
  3. 算法模型的创建和模型参数的拟合
  4. 验证

ML Pipeline的各阶段是通过一系列转换器和评估器来实现的。

1.转换器(transformer)

abstract class Transformer extends PipelineStage {
    ...
    def transform(dataset: Dataset[_]):DataFrame
}

转换器抽象类Transformer 定义了transform方法,用来将一个DataFrame转换为另一个DataFrame,spark中定义type DataFrame = org.apache.spark.sql.Dataset[org.apache.spark.sql.Row]

使用转换器时,一般需要指定inputColoutputCol,即指定一个输入列,进行转换操作,得到一个新的列(原来的列任然存在)。

  /** @group setParam */
  def setInputCol(value: String): T = set(inputCol, value).asInstanceOf[T]
  /** @group setParam */
  def setOutputCol(value: String): T = set(outputCol, value).asInstanceOf[T]

2.评估器(estimator)

评估器是对学习算法的抽象。评估器抽象类Estimator定义了fit()方法,该方法以DataFrame为输入,返回一个算法模型。
在许多Estimator中同样会有setInputCol()方法和setOutputCol()方法,实际上这里指定的输入列和输出列是为Estimator.fit()返回的Transformer而准备的。

abstract class Estimator[M <: Model[M]] extends PipelineStage {
     def fit(dataset: Dataset[_]): M
}

3.Pipeline

转换器Transform和评估器Estimator都继承自PipelineStage,而Pipeline可以理解为是从数据预处理,特征提取到模型拟合和验证的工作流程,是由一系列按特定顺序运行的PipelineStage构成的,每一个PipelineStage对应一个转换器或评估器。创建一个Pipeline对象后,可以通过setStages(value: Array[_ <: PipelineStage])方法指定工作流程中使用到的PipelineStage以及它们之间的先后顺序。

class Pipeline @Since("1.4.0") (
  @Since("1.4.0") override val uid: String) extends Estimator[PipelineModel] with MLWritable {
      def setStages(value: Array[_ <: PipelineStage]): this.type = {
        set(stages, value.asInstanceOf[Array[PipelineStage]])
        this
      }
      override def fit(dataset: Dataset[_]): PipelineModel = {}
  }

注意到Pipeline本身就是一个Estimator,可以通过调用fit()方法,返回一个PipelineModel,而PipelineModel是一个Transformer。

class PipelineModel private[ml] (
    @Since("1.4.0") override val uid: String,
    @Since("1.4.0") val stages: Array[Transformer])
  extends Model[PipelineModel] with MLWritable with Logging {}
  
abstract class Model[M <: Model[M]] extends Transformer {}

4.案例

4.1.One Hot Encoding

    val spark: SparkSession = SparkSession.builder().appName("OneHotEncoderExample").master("local[*]").getOrCreate()
    val df: DataFrame = spark.createDataFrame(Seq((0, 3), (1, 2), (2, 4), (3, 3), (4, 3), (5, 4))).toDF("id", "category")
    val indexer: StringIndexerModel = new StringIndexer().setInputCol("category").setOutputCol("categoryIndex").fit(df)
    val indexed: DataFrame = indexer.transform(df)
    indexed.show()
    val encoder: OneHotEncoder = new OneHotEncoder().setInputCol("categoryIndex").setOutputCol("categoryVec").setDropLast(false)
    val encoded: DataFrame = encoder.transform(indexed)
    encoded.show()

output

+---+--------+-------------+
| id|category|categoryIndex|
+---+--------+-------------+
|  0|       3|          0.0|
|  1|       2|          2.0|
|  2|       4|          1.0|
|  3|       3|          0.0|
|  4|       3|          0.0|
|  5|       4|          1.0|
+---+--------+-------------+

+---+--------+-------------+-------------+
| id|category|categoryIndex|  categoryVec|
+---+--------+-------------+-------------+
|  0|       3|          0.0|(3,[0],[1.0])|
|  1|       2|          2.0|(3,[2],[1.0])|
|  2|       4|          1.0|(3,[1],[1.0])|
|  3|       3|          0.0|(3,[0],[1.0])|
|  4|       3|          0.0|(3,[0],[1.0])|
|  5|       4|          1.0|(3,[1],[1.0])|
+---+--------+-------------+-------------+

StringIndexer是一个Estimator,它的功能是maps a string column of labels to an ML column of label indices。即对输入的标签列进行编号(string类型),且出现频率越高的标签,编号越靠前,如果一个标签对应的编号为0,表示该标签出现的最多。
OneHotEncoder是一个Tramsformer。

4.2.VectorAssembler

    val spark: SparkSession = SparkSession.builder().appName("test_VectorAssembler").master("local[*]").getOrCreate()
    val df: DataFrame = spark.createDataFrame(Seq((0.1, 0.3, 0.2), (1.0, 1.2, 0.5), (2.1, 0.4, 0.2), (1.1, 0.1, 0.3))).toDF("f1", "f2", "f3")
    val assembler: VectorAssembler = new VectorAssembler().setInputCols(Array("f1", "f2", "f3")).setOutputCol("features")
    val df1: DataFrame = assembler.transform(df)
    df1.show()
+---+---+---+-------------+
| f1| f2| f3|     features|
+---+---+---+-------------+
|0.1|0.3|0.2|[0.1,0.3,0.2]|
|1.0|1.2|0.5|[1.0,1.2,0.5]|
|2.1|0.4|0.2|[2.1,0.4,0.2]|
|1.1|0.1|0.3|[1.1,0.1,0.3]|
+---+---+---+-------------+

VectorAssembler是一个Transformer,它的作用是将多个列合并为一个。

4.3.决策树Pipeline

def decisionTreePipeline(vectorAssembler: VectorAssembler, dataFrame: DataFrame) = {
    val Array(training, test): Array[Dataset[Row]] = dataFrame.randomSplit(Array(0.8, 0.2), seed = 12345)
    val stages = new mutable.ArrayBuffer[PipelineStage]()
    val labelIndexer = new StringIndexer().setInputCol("label").setOutputCol("indexedLabel")
    stages += labelIndexer
    val dt: DecisionTreeClassifier = new DecisionTreeClassifier().setFeaturesCol(vectorAssembler.getOutputCol)
      .setLabelCol("indexedLabel").setMaxDepth(5).setMaxBins(32).setMinInstancesPerNode(1)
      .setMinInfoGain(0.0).setCacheNodeIds(false).setCheckpointInterval(10)
    stages += vectorAssembler
    stages += dt
    val pipeline: Pipeline = new Pipeline().setStages(stages.toArray)
    val model: PipelineModel = pipeline.fit(training)
    val holdout: DataFrame = model.transform(test).select("prediction", "label")
    val evaluator: MulticlassClassificationEvaluator = new MulticlassClassificationEvaluator().setPredictionCol("prediction").setLabelCol("label").setMetricName("accuracy")
    val acc: Double = evaluator.evaluate(holdout)
    println(acc)
  }

该案例中的决策树Pipeline由3个PipelineStage构成:StringIndexer、VectorAssembler和DecisionTreeClassfier。分别为Estimator、Transformer和Estimator。

参考

[1] Machine Learning with Spark (Second Edition), Rajdeep Dua.

你可能感兴趣的:(spark,机器学习)