Spark Pipeline Stage模型自定义(一)

前言

Spark的Mllib机器学习工具包括两个扩展,一是Mllib,其算法都是围绕RDD这个数据结构来实现的;二是ML,其基于Pipeline提供了一整套建立在DataFrame上的高级API,将每一个操作定义为一个Stage,能够帮助用户创建和优化机器学习流程。本文关注ML扩展中的Pipeline,并就如何自定义Stage模型进行讨论。

一、 Pipeline介绍

Pipeline直译过来就是管道、工作流。在Spark中,它的目的是将数据挖掘、机器学习的各个阶段封装成一个工作流模型。我们知道一个完整的数据挖掘流程大致包括数据清洗、特征工程、算法模型建立、算法模型评估等几个步骤,如果我们要针对每个步骤单独开发模型,并手动将他们串接,无疑是十分低效的。特别是在Spark中,基本的数据结构RDD太过抽象,要基于RDD来做数据挖掘的整套工作非常的不便。

而通过使用Pipeline,我们能够很好地完成这个任务。这是因为基于Pipeline的机器学习工作是围绕DataFrame来开展的,这是一种我们能够更加直观感受的数据结构。其次,它定义机器学习的每个阶段Stage,并抽象成Transformer和Estimator两类,我们能够基于这两个类抽样封装自己算法模型,并通过Pipeline自动串联的能力,将每一步连成自己的工作流。

二、自定义Transformer模型

Transformer模型仅仅对数据做转换操作,不涉及到模型训练。现在假设我们有一份人群身高数据,需要自己定义一个Transformer模型,实现一个简单的数据转换功能。期望的效果是给数据新增一列“h170”表示该人身高是否大于170,是的话赋值1,否则赋值0,具体操作如下:

1. 构造基本的transformer类

这里我们创建Mytransformer类,并继承Transformer,同时定义两个参数inputCol和outputCol分别代表Transformer所操作DateFrame的输入列和输出列。

class Mytransformer (override val uid: String) extends Transformer {
final val inputCol= new Param[String](this, "inputCol", "The input column")
final val outputCol = new Param[String](this, "outputCol", "The output column")
def setInputCol(value: String): this.type = set(inputCol, value)
def setOutputCol(value: String): this.type = set(outputCol, value)

  def this() = this(Identifiable.randomUID("Mytransformer "))
  def copy(extra: ParamMap): Mytransformer  = {
    defaultCopy(extra)
  }
}

2. 重写transformSchema方法

TransformSchema方法确定Transformer操作中,所操作的DateFrame对象schema的变化。因为我们要会增加一列“h170”表示身高是否大于170,所以这里我们首先要判断待计算列的类型是否是Double,不是的话报错。其次是要返回变化后的表结构,即增加了一列“h170”的StructType。

override def transformSchema(schema: StructType): StructType = {
    val idx = schema.fieldIndex("height")
    val field = schema.fields(idx)
    if (field.dataType != DoubleType) {
        throw new Exception(s"Input type ${field.dataType} did not match input type DoubleType")
    }
    schema.add(StructField($(outputCol), DoubleType, false))
}

3. 实现transform方法

transform方法实现Transformer对DataFrame所做的具体操作

class MyEstimator(override val uid: String) extends Estimator[MyEstimatorModel] with MyEstimatorParams with DefaultParamsWritable {
    def setInputCol(value: String) = set(inputCol, value)
    def setOutputCol(value: String) = set(outputCol, value)
    def this() = this(Identifiable.randomUID("MyEstimatorParams"))
    override def copy(extra: ParamMap): MyEstimator = {
        defaultCopy(extra)
    }
}

4. 实现可读写

1)继承DefaultParamsWritable

class Mytransformer(override val uid: String) extends Transformer  with DefaultParamsWritable

2)实现Transformer的伴生对象

这里有两步

  • 实现伴生对象,并继承DefaultParamsReadable
  • 伴生对象重写load方法,实现读Transformer实例的功能
object Mytransformer  extends DefaultParamsReadable[Mytransformer] {
  override def load(path: String): Mytransformer = super.load(path)
}

三、测试Transformer模型

通过前面的步骤,Transformer的基本功能就已经实现了,这里我们写个程序测试一下:

val dataset = spark.createDataFrame(Seq( ("mike", 166.0), ("tom", 175.0), ("wade", 163.0))).toDF("name", "height")
val mytransformer = new Mytransformer
mytransformer.setInputCol("height").setOutputCol("h170")
val pipeline = new Pipeline().setStages(Array(mytransformer)).fit(dataset)
val r = pipeline.transform(dataset)
r.show

结果如下:

Spark Pipeline Stage模型自定义(一)_第1张图片

这样,我们自定义Pipeline Transformer模型的工作就全部做完了。

转载于:https://my.oschina.net/weekn/blog/1975783

你可能感兴趣的:(Spark Pipeline Stage模型自定义(一))