使用Pipeline基于分区DataFrame创建许多Spark MLlib模型

时间:2018-04-12 18:54:56

标签: scala apache-spark spark-dataframe apache-spark-mllib

scala> spark.version res8: String = 2.2.0

我正在使用包含列locationID的spark Dataframe。我已经创建了一个MLlib管道来构建一个线性回归模型,当我为一个locationID提供数据时它就可以工作。我现在想为每个'locationID'创建许多模型(生产中可能有几千个locationID)。我想保存每个模型的模型系数。

我不确定在Scala中如何做到这一点。

我的管道定义如下:

import org.apache.spark.ml.feature.{OneHotEncoder, StringIndexer}
import org.apache.spark.ml.regression.LinearRegression
import org.apache.spark.ml.regression.LinearRegressionModel
import org.apache.spark.ml.feature.VectorAssembler
import org.apache.spark.ml.{Pipeline, PipelineModel}
import org.apache.spark.sql


// Load the regression input data
val mydata = spark.read.format("csv")
  .option("header", "true")
  .option("inferSchema", "true")
  .load("./inputdata.csv")

// Crate month one hot encoding
val monthIndexer = new StringIndexer()
  .setInputCol("month")
  .setOutputCol("monthIndex").fit(mydata)
val monthEncoder = new OneHotEncoder()
  .setInputCol(monthIndexer.getOutputCol)
  .setOutputCol("monthVec")
val assembler =  new VectorAssembler()
  .setInputCols(Array("monthVec","tran_adr"))
  .setOutputCol("features")
val lr = new LinearRegression()
  .setMaxIter(10)
  .setRegParam(0.3)
  .setElasticNetParam(0.8)
val pipeline = new Pipeline()
  .setStages(Array(monthIndexer, monthEncoder, assembler, lr))


// Fit using the model pipeline
val myPipelineModel = pipeline.fit(mydata)

然后我能够像这样拉出模型细节:

val modelExtract = myPipelineModel.stages(3).asInstanceOf[LinearRegressionModel]

println(s"Coefficients: ${modelExtract.coefficients} Intercept: ${modelExtract.intercept}")
// Summarize the model over the training set and print out some metrics
val trainingSummary = modelExtract.summary
println(s"numIterations: ${trainingSummary.totalIterations}")
println(s"objectiveHistory: [${trainingSummary.objectiveHistory.mkString(",")}]")
trainingSummary.residuals.show()
println(s"RMSE: ${trainingSummary.rootMeanSquaredError}")
println(s"r2: ${trainingSummary.r2}")

现在,我想对locationID中找到的列mydata进行分组,并在数据的每个分区上运行管道。

我尝试过使用groupby,但我只能聚合。

val grouped = mydata.groupBy("locationID")

我还试图将唯一的locationID作为一个列表并循环遍历它:

val locationList = mydata.select(mydata("prop_code")).distinct

locationList.foreach { printLn }

我知道spark不是创建许多小型模型的理想选择,最适合在大量数据上创建一个模型,但我的任务是将其作为概念验证。

在spark中做这样的事情的正确方法是什么?

2 个答案:

答案 0 :(得分:1)

  

在spark中做这样的事情的正确方法是什么?

我冒险声称没有好的方法。有许多高级工具可以处理核心内数据处理和许多任务调度库,可用于协调独立的学习任务。 Spark根本没有提供任何东西。

它的调度功能是平庸的,ML / MLlib工具也是如此,当每个任务都是独立的时,缩放和容错是没有用的。

你可以使用Spark进行通用目的的调度(如果你不介意使用Python,这个想法是用sklearn keyed models实现的),但那就是它。

答案 1 :(得分:0)

我遇到了同样的问题。我的数据在$“ description_pretty”上分区,这就是我的处理方式。我在分区上拆分数据框,将其输入管道,选择相关列,然后将其合并回去。

    val pipe = new Pipeline().setStages(Array(encoder, assembler, 
         multivariate_linear_model))

    val descriptions_pretty = training_df.select("description_pretty").
         distinct.
         as[String].
         rdd.
         collect

    val model_predictions_df = descriptions_pretty.par.
         map(x => pipe.fit(training_df.filter($"description_pretty" === x)).
              transform(prediction_df.filter($"description_pretty" === x)).
              select($"description", $"description_pretty", 
              $"standard_event_date".cast("String"), 
              $"prediction".as("daily_peak_bps"))).
         reduce( _ union _)

您可以在.transform之前停下来,而是获取系数