导包
import org.apache.spark.ml.{Model, Pipeline, PipelineModel, PipelineStage}
import org.apache.spark.ml.evaluation.{BinaryClassificationEvaluator, Evaluator}
import org.apache.spark.ml.tuning.ParamGridBuilder
import org.apache.spark.ml.param.ParamMap
import org.apache.spark.ml.tuning.{CrossValidator}
import org.apache.spark.ml.classification.LogisticRegression
import org.apache.spark.sql.functions._
import org.apache.spark.ml.feature.VectorAssembler
import org.apache.spark.ml.linalg.{Vector, Vectors}
import org.apache.spark.sql.{DataFrame, Row, SparkSession}
数据构造
var dfTrain = Seq(
(1, 5.1, 3.5, 1.4, 0.2, 0),
(2, 4.9, 3.0, 1.4, 0.2, 1),
(3, 4.7, 3.2, 1.3, 0.2, 0),
(4, 4.6, 3.1, 1.5, 0.2, 1),
(5, 5.0, 3.6, 1.4, 0.2, 0),
(56, 5.7, 2.8, 4.5, 1.3,1),
(57, 5.3, 3.3, 4.7, 1.6,0),
(58, 4.9, 2.4, 3.3, 1.0,1),
(59, 6.6, 3.9, 4.6, 1.3,1),
(60, 5.2, 2.7, 3.9, 1.4,0)
).toDF("id","x1","x2", "x3","x4","label")
val dfTest = dfTrain
dfTrain.show()
pipeline
省略复杂的数据预处理过程
// 向量化
val assemble = new VectorAssembler()
.setInputCols(Array("x1","x2","x3","x4"))
.setOutputCol("features")
// 逻辑回归
val lr = new LogisticRegression().
setMaxIter(10).
setRegParam(0.01)
// 模型
val pipeline = new Pipeline().setStages(Array(assemble, lr))
网格搜索
网格参数设置
val elasticNetParamArray =Array(0.2,0.3,0.5)
val regParamArray =Array(0.01, 0.1)
val paramGrid = new ParamGridBuilder().
addGrid(lr.elasticNetParam,elasticNetParamArray ).
addGrid(lr.regParam,regParamArray ).
build()
这里需要关注下paramGrid是一个Array,里面的元素是ParamMap
cv设置
CV设置主要参数为:
- K折交叉验证的K
- 评估指标
val evaluator = new BinaryClassificationEvaluator()
val evaluator1 =
new BinaryClassificationEvaluator()
.setLabelCol("label")
.setRawPredictionCol("rawPrediction")
.setMetricName("areaUnderROC") // (supports "areaUnderROC" (default), "areaUnderPR")
// 准确率,二分类多分类通用
// val evaluator1 = new MulticlassClassificationEvaluator().
// setLabelCol("label").
// setPredictionCol("prediction").
// setMetricName("accuracy")
// 多分类f1、precision、recall
// val evaluator1 = new MulticlassClassificationEvaluator().
// setLabelCol("label").
// setPredictionCol("prediction").
// setMetricName("f1")//(supports "f1" (default), "weightedPrecision", "weightedRecall", "accuracy")
//需要注意的是BinaryClassificationEvaluator只有AUC、AUPR,而MulticlassClassificationEvaluator的精确率、召回率、f1都是加了权重的,并不适用二分类问题。准确率多分类和二分类一样。
val cv = new CrossValidator().
setEstimator(pipeline).
setEvaluator(evaluator1). //评估指标
setEstimatorParamMaps(paramGrid). //网格搜索参数
setNumFolds(3) // 3折交叉验证
// 执行cv
val moedel = cv.fit(dfTrain)
cv结果
做完交叉验证,我们一般想知道:
- 每个参数组合的avgMetric是多少
- 最优参数组合是什么//某种意义上和前面一致
- 最优模型,及其参数
其中avgMetric是可以从cv中直接获取的,它的顺序和paramGrid是一样的。最优模型可以通过bestModel属性获得。
// zip之后生成Array,每个元素都是Tuple2
// Tuple2._1是网格搜索的维度组合,ParamMap类型;Tuple2._2是该参数组合cv之后avgMetric
// 按照avgMetric升序排序
val combined = paramGrid.zip(moedel.avgMetrics).sortBy(_._2)
// 输出交叉验证的结果
println(s"validation result:${combined.toList}")
// 输出metric的max和min值,不同的场景最优的avgMetric可能是最大值也可能是最小值
println("*=*=*=*=*=*=*=*=*=*=*=*CV:Max Min avgMetric=*=*=*=*=*=*=*=*=*=*=*=*=*")
println(combined.maxBy(_._2))
println(combined.minBy(_._2))
println("*=*=*=*=*=*=*=*=*=*=*=*CV:Max Min avgMetric=*=*=*=*=*=*=*=*=*=*=*=*=*")
最优模型
val bestModel = moedel.bestModel
输出模型参数
bestModel.asInstanceOf[PipelineModel].stages.last.extractParamMap
预测
bestModel.transform(dfTest).show()
疑问:
lr模型设定threshold备选参数并通过cv找到最优参数后在预测时为何依然选中默认的0.5?
参考:
模型调参