场景&模型选择:
根据 花的几个特征, 预测花的种类, 属于多元分类问题
花有哪几个分类?
— Iris Setosa
— Iris Versicolour
— Iris Virginica
需要哪几个特征参与模型学习?
1. sepal length in cm
2. sepal width in cm
3. petal length in cm
4. petal width in cm
分类问题常见的算法有决策树,随机森林,线性回归等, 这里使用随机森林模型RandomForestClassifier
;
模型评估使用ML库里的MulticlassClassificationEvaluator:
这个类位于org.apache.spark.ml.evaluation包下面,包含以下四种评估指标,我们选择使用accuracy这个指标:
* f1
* weightedPrecision
* weightedRecall
* accuracy
1.了解数据
数据, http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data
数据文件有下面信息构成: 特征集合+类型标签,下面是部分数据:
5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
7.0,3.2,4.7,1.4,Iris-versicolor
6.4,3.2,4.5,1.5,Iris-versicolor
6.3,3.3,6.0,2.5,Iris-virginica
5.8,2.7,5.1,1.9,Iris-virginica
2.数据预处理
先把文件中的字符串型做数值转换为Float型, 可以定义一个简单的Spark任务实现,在保存在文件Iris_data.txt
或其他存储介质中
5.1,3.5,1.4,0.2,0.0
4.9,3.0,1.4,0.2,0.0
7.0,3.2,4.7,1.4,1.0
6.4,3.2,4.5,1.5,1.0
6.3,3.3,6.0,2.5,2.0
5.8,2.7,5.1,1.9,2.0
Iris_data.txt
中文件加载数据转换成DF格式
val df = parseRDD(sc.textFile("/Iris_data.txt")).map(parseIris).toDF().cache()
val featureCols = Array("sepal_length", "sepal_width", "petal_length", "petal_width", "Iris_class")
相关结构和子函数定义:
case class Iris(
sepal_length: Float,
sepal_width: Float, petal_length: Float, petal_width: Float, Iris_class: Float
)
def parseIris(line: Array[Float]): Iris = {
Iris(
line(0),
line(1) - 1, line(2), line(3), line(4)
)
}
def parseRDD(rdd: RDD[String]): RDD[Array[Float]] = {
rdd.map(_.split(",")).map(_.map(_.toFloat))
}
特征列向量集featureCols
生成相关特征索引列features
, 标签列Iris_class
生成相关索引列label
, 再把数据集 分为训练集,验证集
VectorAssembler是一个transformer,将多列数据转化为单列的向量列
val assembler = new VectorAssembler().setInputCols(featureCols).setOutputCol("features")
val df2 = assembler.transform(df)
val labelIndexer = new StringIndexer().setInputCol("Iris_class").setOutputCol("label")
val df3 = labelIndexer.fit(df2).transform(df2)
val Array(trainingData, testData) = df3.randomSplit(Array(0.7, 0.3), 5000)
2.模型pipeline定义
分类问题可以使用的算法很多, 个人比较常用随机森林; 分类器使用多元分类器: MulticlassClassificationEvaluator
配置paramGrid
进行自动调试模型参数, 运行生成模型
val classifier = new RandomForestClassifier()
val model = classifier.fit(trainingData)
val evaluator = new MulticlassClassificationEvaluator().setLabelCol("label").setPredictionCol("prediction")
val paramGrid = new ParamGridBuilder()
.addGrid(classifier.maxBins, Array(25, 31))
.addGrid(classifier.maxDepth, Array(5, 10))
.addGrid(classifier.numTrees, Array(20, 60))
.addGrid(classifier.impurity, Array("entropy", "gini"))
.build()
val steps: Array[PipelineStage] = Array(classifier)
val pipeline = new Pipeline().setStages(steps)
val cv = new CrossValidator()
.setEstimator(pipeline)
.setEvaluator(evaluator)
.setEstimatorParamMaps(paramGrid)
.setNumFolds(10)
val pipelineFittedModel = cv.fit(trainingData)
模型预测与评估
val predictions = pipelineFittedModel.transform(testData)
predictions.show()
模型评估时主要使用label 和prediction 2列的差异
+————+———–+————+———–+———-+——————–+—–+————-+———–+———-+
|sepal_length|sepal_width|petal_length|petal_width|Iris_class| features|label|rawPrediction|probability|prediction|
+————+———–+————+———–+———-+——————–+—–+————-+———–+———-+
| 4.3| 2.0| 1.1| 0.1| 0.0|[4.30000019073486…| 1.0| [0.0,20.0]| [0.0,1.0]| 1.0|
| 4.4| 2.2| 1.3| 0.2| 0.0|[4.40000009536743…| 1.0| [0.0,20.0]| [0.0,1.0]| 1.0|
| 4.7| 2.2| 1.3| 0.2| 0.0|[4.69999980926513…| 1.0| [0.0,20.0]| [0.0,1.0]| 1.0|
| 4.7| 2.2| 1.6| 0.2| 0.0|[4.69999980926513…| 1.0| [0.0,20.0]| [0.0,1.0]| 1.0|
| 4.8| 2.4| 1.9| 0.2| 0.0|[4.80000019073486…| 1.0| [2.0,18.0]| [0.1,0.9]| 1.0|
| 5.0| 2.2| 1.2| 0.2| 0.0|[5.0,2.2000000476…| 1.0| [0.0,20.0]| [0.0,1.0]| 1.0|
| 5.0| 2.4| 1.6| 0.4| 0.0|[5.0,2.4000000953…| 1.0| [0.0,20.0]| [0.0,1.0]| 1.0|
| 5.0| 2.5| 1.3| 0.3| 0.0|[5.0,2.5,1.299999…| 1.0| [0.0,20.0]| [0.0,1.0]| 1.0|
| 5.0| 2.6| 1.4| 0.2| 0.0|[5.0,2.5999999046…| 1.0| [0.0,20.0]| [0.0,1.0]| 1.0|
| 5.2| 2.4| 1.4| 0.2| 0.0|[5.19999980926513…| 1.0| [0.0,20.0]| [0.0,1.0]| 1.0|
| 5.4| 2.4| 1.7| 0.2| 0.0|[5.40000009536743…| 1.0| [0.0,20.0]| [0.0,1.0]| 1.0|
| 5.4| 2.9| 1.3| 0.4| 0.0|[5.40000009536743…| 1.0| [0.0,20.0]| [0.0,1.0]| 1.0|
| 5.5| 1.4000001| 3.7| 1.0| 1.0|[5.5,1.4000000953…| 0.0| [20.0,0.0]| [1.0,0.0]| 0.0|
| 5.5| 1.5999999| 4.4| 1.2| 1.0|[5.5,1.5999999046…| 0.0| [20.0,0.0]| [1.0,0.0]| 0.0|
| 5.5| 3.1999998| 1.4| 0.2| 0.0|[5.5,3.1999998092…| 1.0| [0.0,20.0]| [0.0,1.0]| 1.0|
| 5.6| 1.7| 4.2| 1.3| 1.0|[5.59999990463256…| 0.0| [20.0,0.0]| [1.0,0.0]| 0.0|
| 5.6| 1.9000001| 3.6| 1.3| 1.0|[5.59999990463256…| 0.0| [20.0,0.0]| [1.0,0.0]| 0.0|
| 5.6| 2.0| 4.1| 1.3| 1.0|[5.59999990463256…| 0.0| [20.0,0.0]| [1.0,0.0]| 0.0|
| 5.6| 2.0| 4.5| 1.5| 1.0|[5.59999990463256…| 0.0| [20.0,0.0]| [1.0,0.0]| 0.0|
| 5.7| 1.5| 5.0| 2.0| 1.0|[5.69999980926513…| 0.0| [20.0,0.0]| [1.0,0.0]| 0.0|
val accuracy = evaluator.evaluate(predictions)
println("accuracy before pipeline fitting" + accuracy)
println(pipelineFittedModel.bestModel.asInstanceOf[org.apache.spark.ml.PipelineModel].stages(0))
评估结果: 生成的20颗树的随机森林模型, 预测精准度如下:
accuracy fitting 0.97
RandomForestClassificationModel (uid=rfc_d9c752e11bd5) with 20 trees
没有评论