Spark MLlib机器学习库(一)决策树和随机森林案例详解

Spark MLlib机器学习库(一)决策树和随机森林案例详解

1 决策树预测森林植被

1.1 Covtype数据集

数据集的下载地址: https://www.kaggle.com/datasets/uciml/forest-cover-type-dataset

该数据集记录了美国科罗拉多州不同地块的森林植被类型,每个样本包含了描述每块土地的若干特征,包括海拔、坡度、到水源的距离、遮阳情况和土壤类型,并且给出了地块对应的已知森林植被类型。

很自然地,我们把该数据解析成 DataFrame,因为 DataFrame 就是 Spark 针对表格数据的抽象,它有定义好的模式,包括列名和列类型。

package com.yydsimport org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.{DataFrame, SparkSession}
import org.apache.spark.ml.feature.VectorAssembler
import org.apache.spark.sql.functions._
import org.apache.spark.ml.classification.{DecisionTreeClassificationModel, DecisionTreeClassifier}
import org.apache.spark.ml.evaluation.MulticlassClassificationEvaluator
import org.apache.spark.mllib.evaluation.MulticlassMetrics
import org.apache.spark.ml.{Model, Pipeline, PipelineModel, Transformer}
import org.apache.spark.ml.tuning.{ParamGridBuilder, TrainValidationSplit, TrainValidationSplitModel}object DecisionTreeTest {Logger.getLogger("org.apache.spark").setLevel(Level.ERROR)def main(args: Array[String]): Unit = {// 构建SparkSession实例对象,通过建造者模式创建val spark: SparkSession = {SparkSession.builder().appName(this.getClass.getSimpleName.stripSuffix("$")).master("local[1]").config("spark.sql.shuffle.partitions", "3").getOrCreate()}// 利用Spark内置的读取CSV数据功能val dataWithHeader = spark.read.option("inferSchema", "true") // 数据类型推断.option("header", "true") // 表头解析.csv("D:\\kaggle\\covtype\\covtype.csv")// 检查一下列名,可以清楚地看到,有些特征确实是数值型。// 但是“荒野区域(Wilderness_Area)”有些不同,因为它横跨4列,每列要么为 0,要么为 1。// 实际上荒野区域是一个类别型特征,而非数值型。采用了one-hot编码// 同样,Soil_Type(40列)也是one-hot编码。dataWithHeader.printSchema()dataWithHeader.show(10)}}

解释一下one-hot编码

one-hot编码:一个有N个不同取值的类别型特征可以变成 N 个数值型特征,变换后的每个数值型特征的取值为 01。在这 N 个特征中,有且只有一个取值为 1,其他特征取值都为 0。比如,类别型特征“天气”可能的取值有“多云”“有雨”或“晴朗”。
在one-hot 编码中,它就变成了 3 个数值型特征:
多云用 1,0,0 表示,
有雨用 0,1,0 表示,
晴朗用 0,0,1 表示。不过,这并不是将分类特性编码为数值的唯一方法。
另一种可能的编码方式是为类别型特征的每个可能取值分配一个不同数值,比如多云 1.0、有雨 2.0 等。目标“Cover_Type”本身也是
类别型值,用 1~7 编码。在编码过程中,将类别型特征当成数值型特征时要小心。类别型特征值原本是没有大小顺序可言的,但被编码为数值之后,它们就“显得”有大小顺序
了。被编码后的特征若被视为数值,算法在一定程度上会假定有雨比多云大,而且大两倍,这样就可能导致不合理的结果。

1.2 建立决策树模型

1.2.1 原始特征组合为特征向量

    // 划分训练集和测试集val Array(trainData, testData) = dataWithHeader.randomSplit(Array(0.9, 0.1))trainData.cache()testData.cache()    // 输入的DataFrame包含许多列,每列对应一个特征,可以用来预测目标列。// Spark MLlib 要求将所有输入合并成一列,该列的值是一个向量。// 我们可以利用VectorAssembler将特征转换为向量val inputCols: Array[String] = trainData.columns.filter(_ != "Cover_Type")val assembler: VectorAssembler = new VectorAssembler().setInputCols(inputCols) // 除了目标列以外,所有其他列都作为输入特征,因此产生的DataFrame有一个新的“featureVector”.setOutputCol("featureVector")val assembledTrainData: DataFrame = assembler.transform(trainData)assembledTrainData.select(col("featureVector")).show(10,truncate = false)
+----------------------------------------------------------------------------------------------------+
|featureVector                                                                                       |
+----------------------------------------------------------------------------------------------------+
|(54,[0,1,2,3,4,5,6,7,8,9,13,15],[1859.0,18.0,12.0,67.0,11.0,90.0,211.0,215.0,139.0,792.0,1.0,1.0])  |
|(54,[0,1,2,3,4,5,6,7,8,9,13,15],[1860.0,18.0,13.0,95.0,15.0,90.0,210.0,213.0,138.0,780.0,1.0,1.0])  |
|(54,[0,1,2,3,4,5,6,7,8,9,13,15],[1861.0,35.0,14.0,60.0,11.0,85.0,218.0,209.0,124.0,832.0,1.0,1.0])  |
|(54,[0,1,2,3,4,5,6,7,8,9,13,15],[1866.0,23.0,14.0,85.0,16.0,108.0,212.0,210.0,133.0,819.0,1.0,1.0]) |
|(54,[0,1,2,3,4,5,6,7,8,9,13,15],[1867.0,20.0,15.0,108.0,19.0,120.0,208.0,206.0,132.0,808.0,1.0,1.0])|
|(54,[0,1,2,3,4,5,6,7,8,9,13,15],[1868.0,27.0,16.0,67.0,17.0,95.0,212.0,204.0,125.0,859.0,1.0,1.0])  |
|(54,[0,1,2,3,4,5,6,7,8,9,13,18],[1871.0,22.0,22.0,60.0,12.0,85.0,200.0,187.0,115.0,792.0,1.0,1.0])  |
|(54,[0,1,2,3,4,5,6,7,8,9,13,15],[1871.0,36.0,19.0,134.0,26.0,120.0,215.0,194.0,107.0,797.0,1.0,1.0])|
|(54,[0,1,2,3,4,5,6,7,8,9,13,15],[1871.0,37.0,19.0,120.0,29.0,90.0,216.0,195.0,107.0,759.0,1.0,1.0]) |
|(54,[0,1,2,3,4,5,6,7,8,9,13,18],[1872.0,12.0,27.0,85.0,25.0,60.0,182.0,174.0,118.0,577.0,1.0,1.0])  |
+----------------------------------------------------------------------------------------------------+
  • 输出看起来不是很像一串数字,这是因为它显示的是向量的原始表示,也就是 Sparse Vector(稀疏向量) 的实例,这样做可以节省存储空间。由于这 54 个值中的大多数值都是 0,它仅存储非零值及其索引。

  • VectorAssembler是当前 Spark MLlib管道(Pipeline) API 中的一个 Transformer 示例。 VectorAssembler可以将一个 DataFrame 转换成另外一个 DataFrame,并且可以和其他 Transformer 组合成一个管道。在后面,我们将这些转换操作将连接成一个真正的管道。

1.2.2 构建决策树

  val classifier = new DecisionTreeClassifier().setSeed(Random.nextLong())  // 随机数种子.setLabelCol("Cover_Type")  // 目标列.setFeaturesCol("featureVector") // 准换后的特征列.setPredictionCol("prediction") // 预测列的名称// DecisionTreeClassificationModel本身就是一个转换器// 它可以将一个包含特征向量的 DataFrame 转换成一个包含特征向量及其预测结果的 DataFrameval model: DecisionTreeClassificationModel = classifier.fit(assembledTrainData)println(model.toDebugString)
DecisionTreeClassificationModel: uid=dtc_54cb31909b32, depth=5, numNodes=51, numClasses=8, numFeatures=54If (feature 0 <= 3048.5)If (feature 0 <= 2559.5)If (feature 10 <= 0.5)If (feature 0 <= 2459.5)If (feature 3 <= 15.0)Predict: 4.0Else (feature 3 > 15.0)Predict: 3.0Else (feature 0 > 2459.5)If (feature 17 <= 0.5)Predict: 2.0Else (feature 17 > 0.5)Predict: 3.0Else (feature 10 > 0.5)If (feature 9 <= 5129.0)Predict: 2.0Else (feature 9 > 5129.0)If (feature 5 <= 569.5)Predict: 2.0Else (feature 5 > 569.5)Predict: 5.0......

依据上面模型表示方式的输出信息,我们可以发现模型的一些树结构。它由一系列针对特征的嵌套决策组成,这些决策将特征值与阈值相比较。

构建决策树的过程中,决策树能够评估输入特征的重要性。也就是说,它们可以评估每个输入特征对做出正确预测的贡献值。从模型中很容易获得这个信息。

// 把列名及其重要性(越高越好)关联成二元组,并按照重要性从高到低排列输出。 
// Elevation 似乎是绝对重要的特征;其他的大多数特征在预测植被类型时几乎没有任何作用!
model.featureImportances.toArray.zip(inputCols).sorted.reverse.foreach(println)
(0.8066003452907752,Elevation)
(0.04178573786315329,Horizontal_Distance_To_Hydrology)
(0.03280245080822316,Wilderness_Area1)
(0.030257284101934206,Soil_Type4)
(0.02562302586398405,Hillshade_Noon)
(0.023493741973492223,Soil_Type2)
(0.016910986928613186,Soil_Type32)
(0.011741228151910562,Wilderness_Area3)
(0.005884894981433861,Soil_Type23)
(0.0027811902118641293,Hillshade_9am)
(0.0021191138246161745,Horizontal_Distance_To_Roadways)
(0.0,Wilderness_Area4)
(0.0,Wilderness_Area2)
(0.0,Vertical_Distance_To_Hydrology)
(0.0,Soil_Type9)
......

1.2.3 预测

    // 比较一下模型预测值与正确的覆盖类型val predictions = model.transform(assembledTrainData)predictions.select("Cover_Type", "prediction", "probability").show(10, truncate = false)

在这里插入图片描述

  • 输出还包含了一个probability列,它给出了模型对每个可能的输出的准确率的估计。

  • 尽管只有 7 种可能的结果,而概率向量实际上有 8 个值。向量中索引 1~7 的值分别表示结果为 1~7 的概率。然而,索引 0 也有一个值,它总是显示概率为“0.0”。我们可以忽略它,因为 0 根本就不是一个有效的结果。

  • 决策树分类器的实现有几个超参数需要调整,这段代码中使用的都是默认值。

1.2.4 评估模型训练

     // 评估训练质量val evaluator = new MulticlassClassificationEvaluator().setLabelCol("Cover_Type").setPredictionCol("prediction")println("准确率:" + evaluator.setMetricName("accuracy").evaluate(predictions))println("f1值:" + evaluator.setMetricName("f1").evaluate(predictions))
准确率:0.7007016671765066
f1值:0.6810479157002327

混淆矩阵

单个的准确率可以很好地概括分类器输出的好坏,然而有时候混淆矩阵(confusion matrix)会更有效。

混淆矩阵是一个 N× N 的表, N 代表可能的目标值的个数。因为我们的目标值有 7 个分类,所以是一个 7× 7 的矩阵,每一行代表数据的真实归属类别,每一列按顺序依次代表预测值。第 i 行和第 j 列的条目表示数据中真正归属第 i 个类别却被预测为第 j 个类别的数据总量。因此,正确的预测是沿着对角线计算的,而非对角线元素代表错误预测。

   // 混淆矩阵,Spark 提供了用于计算混淆矩阵的代码;不幸的是,这个代码是基于操作 RDD的旧版 MLlib API 实现的val predictionRDD = predictions.select("prediction", "Cover_Type").as[(Double,Double)] // 转换成 Dataset,需要导入隐式准换 import spark.implicits._.rdd // 准换为rddval multiclassMetrics = new MulticlassMetrics(predictionRDD)println("混淆矩阵:")println(multiclassMetrics.confusionMatrix)
混淆矩阵:
130028.0  55161.0   187.0    0.0    0.0  0.0  5175.0   
50732.0   196315.0  7163.0   53.0   0.0  0.0  762.0    
0.0       2600.0    29030.0  600.0  0.0  0.0  0.0      
0.0       0.0       1487.0   967.0  0.0  0.0  0.0      
12.0      7743.0    755.0    0.0    0.0  0.0  0.0      
0.0       3478.0    11812.0  387.0  0.0  0.0  0.0      
7923.0    193.0     60.0     0.0    0.0  0.0  10275.0  

对角线上的次数多是好的。但也确实出现了一些分类错误的情况,比如分类器甚至没有将任何一个样本类别预测为 5。

     //  当然,计算混淆矩阵之类,也可以直接使用 DataFrame API 中一些通用的操作,而不再需要依赖专门的方法。//  透视Pivot//  透视操作简单直接,逻辑如下//  1、按照不需要转换的字段分组,本例中是Cover_Type;//  2、使用pivot函数进行透视,透视过程中可以提供第二个参数来明确指定使用哪些数据项;//  3、汇总数字字段val confusionMatrix = predictions.groupBy("Cover_Type").pivot("prediction", (1 to 7)) //透视可以视为一个聚合操作,通过该操作可以将一个(实际当中也可能是多个)具有不同值的分组列转置为各个独立的列.count().na.fill(0.0)   // 用 0 替换 null.orderBy("Cover_Type")confusionMatrix.show()
+----------+------+------+-----+---+---+---+-----+
|Cover_Type|     1|     2|    3|  4|  5|  6|    7|
+----------+------+------+-----+---+---+---+-----+
|         1|130028| 55161|  187|  0|  0|  0| 5175|
|         2| 50732|196315| 7163| 53|  0|  0|  762|
|         3|     0|  2600|29030|600|  0|  0|    0|
|         4|     0|     0| 1487|967|  0|  0|    0|
|         5|    12|  7743|  755|  0|  0|  0|    0|
|         6|     0|  3478|11812|387|  0|  0|    0|
|         7|  7923|   193|   60|  0|  0|  0|10275|
+----------+------+------+-----+---+---+---+-----+

70% 的准确率是用默认超参数取得的。如果在决策树构建过程中试试超参数的其他值,准确率还可以提高。

1.3 决策树的超参数

决策树的重要的超参数如下:

  • 最大深度

    • 最大深度只是对决策树的层数做出限制,它是分类器为了对样本进行分类所做的一连串判
      断的最大次数。限制判断次数有利于避免对训练数据产生过拟合
  • 最大桶数

    • 决策树算法负责为每层生成可能的决策规则,这些决策规则类似“重量≥ 100”或者“重量≥ 500”。

    • 决策总是采用相同形式:对数值型特征, 决策采用特征≥值的形式;对类别型特征,决策采用特征在(值 1, 值 2,…)中的形式。因此,要尝试的决策规则集合实际上是可以嵌入决策规则中的一系列值。

    • Spark MLlib 的实现把决策规则集合称为“桶”(bin)。桶的数目越多,需要的处理时间越多,但找到的决策规则可能更优

  • 不纯性度量

    • 好规则把训练集数据的目标值分为相对是同类或“纯”(pure)的子集。

    • 选择最好的规则也就意味着最小化规则对应的两个子集的不纯性(impurity)。

    • 不纯性有两种常用的度量方式: Gini不纯度(spark默认参数)或熵

  • 最小信息增益

    • 利于避免过拟合

1.4 决策树超参数调优

采用哪个不纯性度量所得到的决策树的准确率更高,或者最大深度或桶数取多少合适,我们可以让 Spark 来尝试这些值的许多组合。

首先,有必要构建一个管道,用于封装与上面相同的两个步骤。创建 VectorAssembler 和DecisionTreeClassifier,然后将这两个 Transformer 串起来,我们就可以得到一个单独的Pipeline 对象,这个 Pipeline 对象可以将前面的两个操作表示成一个 。

   val newAssembler = new VectorAssembler().setInputCols(inputCols).setOutputCol("featureVector")// 在这里我们先不设置超参数val newClassifier = new DecisionTreeClassifier().setSeed(Random.nextLong()).setLabelCol("Cover_Type").setFeaturesCol("featureVector").setPredictionCol("prediction")// 组合为Pipelineval pipeline = new Pipeline().setStages(Array(newAssembler, newClassifier))// 使用 SparkML API 内建支持的 ParamGridBuilder 来测试超参数的组合val paramGrid = new ParamGridBuilder() // 4个超参数来说,每个超参数的两个值都要构建和评估一个模型,共计16种超参数组合,会训练出16个模型.addGrid(newClassifier.impurity, Seq("gini", "entropy")).addGrid(newClassifier.maxDepth, Seq(1, 20)).addGrid(newClassifier.maxBins, Seq(40, 300)).addGrid(newClassifier.minInfoGain, Seq(0.0, 0.05)).build()// 设定评估指标  准确率val multiclassEval = new MulticlassClassificationEvaluator().setLabelCol("Cover_Type").setPredictionCol("prediction").setMetricName("accuracy")// 这里也可以用 CrossValidator 执行完整的 k 路交叉验证,但是要额外付出 k 倍的代价,并且在大数据的情况下意义不大。// 所以在这里 TrainValidationSplit 就够用了val validator = new TrainValidationSplit().setSeed(Random.nextLong()).setEstimator(pipeline)           // 管道.setEvaluator(multiclassEval)     // 评估器.setEstimatorParamMaps(paramGrid) // 超参数组合.setTrainRatio(0.9)               // 训练数据实际上被TrainValidationSplit 划分成90%与10%的两个子集val validatorModel: TrainValidationSplitModel = validator.fit(trainData)// validator 的结果包含它找到的最优模型。val bestModel = validatorModel.bestModel// 打印最优模型参数// 手动从结果 PipelineModel 中提取 DecisionTreeClassificationModel 的实例,然后提取参数println(bestModel.asInstanceOf[PipelineModel].stages.last.extractParamMap)
{dtc_1d4212c56614-cacheNodeIds: false,dtc_1d4212c56614-checkpointInterval: 10,dtc_1d4212c56614-featuresCol: featureVector,dtc_1d4212c56614-impurity: entropy,dtc_1d4212c56614-labelCol: Cover_Type,dtc_1d4212c56614-leafCol: ,dtc_1d4212c56614-maxBins: 40,dtc_1d4212c56614-maxDepth: 20,dtc_1d4212c56614-maxMemoryInMB: 256,dtc_1d4212c56614-minInfoGain: 0.0,dtc_1d4212c56614-minInstancesPerNode: 1,dtc_1d4212c56614-minWeightFractionPerNode: 0.0,dtc_1d4212c56614-predictionCol: prediction,dtc_1d4212c56614-probabilityCol: probability,dtc_1d4212c56614-rawPredictionCol: rawPrediction,dtc_1d4212c56614-seed: 2458929424685097192
}

这包含了很多拟合模型的信息:

  • “熵”作为不纯度的度量是最有效的

  • 最大深度 20 比 1 好,也在我们意料之中。

  • 最好的模型仅拟合到 40 个桶(bin),这一点倒可能让人有些意外,但这也可能意味着 40 个桶已经“足够好了”,而不是说拟合到 40 个桶比300 个桶“更好”。

  • 最后, minInfoGain 的值为 0,这比不为零的最小值要更好,因为这可能意味着模型更容易欠拟(underfit),而不是过拟合(overfit)

超参数和评估结果分别用 getEstimatorParamMaps 和 validationMetrics获得

    // 超参数和评估结果分别用 getEstimatorParamMaps 和 validationMetrics 获得。// 我们可以获取每一组超参数和其评估结果val paramsAndMetrics = validatorModel.validationMetrics.zip(validatorModel.getEstimatorParamMaps).sortBy(-_._1)paramsAndMetrics.foreach { case (metric, params) =>println(metric)println(params)println()}
0.9083158925519863
{dtc_5c5081d572b6-impurity: entropy,dtc_5c5081d572b6-maxBins: 40,dtc_5c5081d572b6-maxDepth: 20,dtc_5c5081d572b6-minInfoGain: 0.0
}
......
    // 这个模型在交叉验证集中达到的准确率是多少?最后,在测试集中能达到什么样的准确率?println("交叉验证集上最大准确率:" + validatorModel.validationMetrics.max)println("测试集上的准确率:" + multiclassEval.evaluate(bestModel.transform(testData)))
交叉验证集上最大准确率:0.9083158925519863
测试集上的准确率:0.9134603776838838

2 随机森林

  • 在决策树的每层,算法并不会考虑所有可能的决策规则。如果在每层上都要考虑所有可能的决策规则,算法的运行时间将无法想象。对一个有 N 个取值的类别型特征,总共有 2^N –2 个可能的决策规则(除空集和全集以外的所有子集)。即使对于一个一般大的 N,这也将创建数十亿候选决策规则。

  • 决策树在选择规则的过程中也涉及一些随机性;每次只考虑随机选择少数特征,而且只考虑训练数据中一个
    随机子集。在牺牲一些准确率的同时换回了速度的大幅提升,但也意味着每次决策树算法构造的树都不相同。

  • 但是树应该不止有一棵,而是有很多棵,每一棵都能对正确目标值给出合理、独立且互不相同的估计。这些树的集体平均预测应该比任一个体预测更接近正确答案。正是由于决策树构建过程中的随机性,才有了这种独立性,这就是随机决策森林的关键所在。

  • 随机决策森林的预测只是所有决策树预测的加权平均。

    • 对于类别型目标,这就是得票最多的类别,或有决策树概率平均后的最大可能值。
    • 随机决策森林和决策树一样也支持回归问题,这时森林做出的预测就是每棵树预测值的平均。
package com.yydsimport org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.{DataFrame, SparkSession}
import org.apache.spark.ml.feature.VectorAssembler
import org.apache.spark.ml.evaluation.MulticlassClassificationEvaluator
import org.apache.spark.ml.{Model, Pipeline, PipelineModel, Transformer}
import org.apache.spark.ml.tuning.{ParamGridBuilder, TrainValidationSplit, TrainValidationSplitModel}
import org.apache.spark.ml.classification.RandomForestClassifier
import org.apache.spark.ml.classification.RandomForestClassificationModel
import scala.util.Randomobject ForestModelTest {Logger.getLogger("org.apache.spark").setLevel(Level.ERROR)def main(args: Array[String]): Unit = {// 构建SparkSession实例对象,通过建造者模式创建val spark: SparkSession = {SparkSession.builder().appName(this.getClass.getSimpleName.stripSuffix("$")).master("local[1]").config("spark.sql.shuffle.partitions", "3").getOrCreate()}// 利用Spark内置的读取CSV数据功能val dataWithHeader: DataFrame = spark.read.option("inferSchema", "true") // 数据类型推断.option("header", "true") // 表头解析.csv("D:\\kaggle\\covtype\\covtype.csv")// 划分训练集和测试集val Array(trainData, testData) = dataWithHeader.randomSplit(Array(0.9, 0.1))trainData.cache()testData.cache()// 输入的特征列val inputCols: Array[String] = trainData.columns.filter(_ != "Cover_Type")val newAssembler = new VectorAssembler().setInputCols(inputCols).setOutputCol("featureVector")// 随机森林分类器val newClassifier = new RandomForestClassifier().setSeed(Random.nextLong()).setLabelCol("Cover_Type").setFeaturesCol("featureVector").setPredictionCol("prediction")// 组合为Pipelineval pipeline = new Pipeline().setStages(Array(newAssembler, newClassifier))// 使用 SparkML API 内建支持的 ParamGridBuilder 来测试超参数的组合val paramGrid = new ParamGridBuilder() // 4个超参数来说,每个超参数的两个值都要构建和评估一个模型,共计16种超参数组合,会训练出16个模型.addGrid(newClassifier.impurity, Seq("gini", "entropy")).addGrid(newClassifier.maxDepth, Seq(1, 20)).addGrid(newClassifier.maxBins, Seq(40, 300)).addGrid(newClassifier.numTrees, Seq(10, 20)) // 要构建的决策树的个数.build()// 设定评估指标  准确率val multiclassEval = new MulticlassClassificationEvaluator().setLabelCol("Cover_Type").setPredictionCol("prediction").setMetricName("accuracy")// 这里也可以用 CrossValidator 执行完整的 k 路交叉验证,但是要额外付出 k 倍的代价,并且在大数据的情况下意义不大。// 所以在这里 TrainValidationSplit 就够用了val validator = new TrainValidationSplit().setSeed(Random.nextLong()).setEstimator(pipeline)           // 管道.setEvaluator(multiclassEval)     // 评估器.setEstimatorParamMaps(paramGrid) // 超参数组合.setTrainRatio(0.9)               // 训练数据实际上被TrainValidationSplit 划分成90%与10%的两个子集val validatorModel: TrainValidationSplitModel = validator.fit(trainData)// validator 的结果包含它找到的最优模型。val bestModel = validatorModel.bestModel// 打印最优模型参数// 手动从结果 PipelineModel 中提取 DecisionTreeClassificationModel 的实例,然后提取参数println(bestModel.asInstanceOf[PipelineModel].stages.last.extractParamMap)// 随机森林分类器有另外一个超参数:要构建的决策树的个数。// 与超参数 maxBins 一样,在某个临界点之前,该值越大应该就能获得越好的效果。然而,代价是构造多棵决策树的时间比建造一棵的时间要长很多倍。val forestModel = bestModel.asInstanceOf[PipelineModel].stages.last.asInstanceOf[RandomForestClassificationModel]// 我们对于特征的理解更加准确了println("特征重要性:")println(forestModel.featureImportances.toArray.zip(inputCols).sorted.reverse.foreach(println))// 这个模型在交叉验证集中达到的准确率是多少?最后,在测试集中能达到什么样的准确率?println("交叉验证集上最大准确率:" + validatorModel.validationMetrics.max)println("测试集上的准确率:" + multiclassEval.evaluate(bestModel.transform(testData)))// 预测// 得到的“最优模型”实际上是包含所有操作的整个管道,其中包括如何对输入进行转换以适于模型处理,以及用于预测的模型本身。// 它可以接受新的 DataFrame 作为输入。它与我们刚开始时使用的 DataFrame 数据的唯一区别就是缺少“Cover_Type”列bestModel.transform(testData.drop("Cover_Type")).select("prediction").show(10)}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/44102.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2021年12月 C/C++(二级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题:统计指定范围里的数 给定一个数的序列S,以及一个区间[L, R], 求序列中介于该区间的数的个数,即序列中大于等于L且小于等于R的数的个数。 时间限制:1000 内存限制:65536 输入 第一行1个整数n、,分别表示序列的长度。(0 < n ≤ 10000) 第二行n个正整数,表示序列里…

Qt 编译使用Bit7z库接口调用7z.dll、7-Zip.dll解压压缩常用Zip、ISO9660、Wim、Esd、7z等格式文件(一)

bit7z一个c静态库&#xff0c;为7-zip共享库提供了一个干净简单的接口 使用CMAKE重新编译github上的bit7z库&#xff0c;用来解压/预览iso9660&#xff0c;WIm&#xff0c;Zip,Rar等常用的压缩文件格式。z-zip库支持大多数压缩文件格式 导读 编译bit7z(C版本)使用mscv 2017编译…

Verilog同步FIFO设计

同步FIFO(synchronous)的写时钟和读时钟为同一个时钟&#xff0c;FIFO内部所有逻辑都是同步逻辑&#xff0c;常常用于交互数据缓冲。 异步FIFO&#xff1a;数据写入FIFO的时钟和数据读出FIFO的时钟是异步的(asynchronous) 典型同步FIFO有三部分组成: &#xff08;1&#xff0…

TiDB 源码编译之 TiFlash 篇

作者&#xff1a; ShawnYan 原文来源&#xff1a; https://tidb.net/blog/5f3fe44d 导言 TiFlash 从去年四月一日开源至今已经过去将近一年半&#xff0c;这段时间里 TiFlash 从 v6.0.0-DMR 升级到了 v7.3.0-DMR &#xff0c;并增加了若干新特性&#xff0c;比如支持 …

Qt5开发环境-银河麒麟V10ARM平台

目录 前言1.源码下载2.编译安装2.1 安装依赖2.2 编译2.3 遇到的问题2.4 安装 3.编译qtwebengine3.1 安装依赖库3.2 编译3.3 遇到的问题3.4 安装 4.配置开发环境5.测试6.程序无法输入中文的问题总结 前言 近期因参与开发的某个软件需要适配银河麒麟v10arm 平台&#xff0c;于是…

LLaMA-7B微调记录

Alpaca&#xff08;https://github.com/tatsu-lab/stanford_alpaca&#xff09;在70亿参数的LLaMA-7B上进行微调&#xff0c;通过52k指令数据&#xff08;https://github.com/tatsu-lab/stanford_alpaca/blob/main/alpaca_data.json&#xff09;&#xff0c;在8个80GB A100上训…

webshell实践,在nginx上实现负载均衡

1、配置多台虚拟机&#xff0c;用作服务器 在不同的虚拟机上安装httpd服务 我采用了三台虚拟机进行服务器设置&#xff1a;192.168.240.11、192.168.240.12、192.168.240.13 [rootnode0-8 /]# yum install httpd -y #使用yum安装httpd服务#开启httpd服务 [rootnode0-8 /]# …

Android Alarm闹钟API使用心得

前言 有什么办法可以在不打开App的时候&#xff0c;也能够触发一些操作呢&#xff1f;比如说发送通知&#xff0c;解决这个需求的办法有很多种选择&#xff0c;比如说官方推荐的WorkManager API&#xff0c;可以在后台执行一次性、耗时、定时的任务&#xff0c;但WorkManager是…

基于Three.js的WebXR渲染入门

1、Three.js 渲染管线快速概览 我不会花太多时间讨论 Three.JS 渲染管道的工作原理,因为它在互联网上有详细记录(例如,此链接)。 我将在下图中列出基础知识,以便更容易理解各个部分的去向。 2、WebXR 设备 API 入门 在我们深入了解 WebXR API 本身之前,您应该知道 WebX…

【数据结构OJ题】设计循环队列

原题链接&#xff1a;https://leetcode.cn/problems/design-circular-queue/ 1. 题目描述 2. 循环队列的概念和结构 为充分利用向量空间&#xff0c;克服"假溢出"现象的方法是&#xff1a;将向量空间想象为一个首尾相接的圆环&#xff0c;并称这种向量为循环向量。…

redis十种数据类型及底层原理

概述 Redis 是一个开源的高性能键值数据库&#xff0c;它支持多种数据类型&#xff0c;可以满足不同的业务需求。本文将介绍 Redis 的10种数据类型&#xff0c;分别是 string&#xff08;字符串&#xff09; hash&#xff08;哈希&#xff09; list&#xff08;列表&#xf…

嵌入式设计中对于只有两种状态的变量存储设计,如何高效的对循迹小车进行偏差量化

前言 &#xff08;1&#xff09;在嵌入式程序设计中&#xff0c;我们常常会要对各类传感器进行数据存储。大多时候的传感器&#xff0c;例如红外光传感器&#xff0c;返回的数据要么是0&#xff0c;要么是1。因此&#xff0c;只需要一bit就能够存储。而很多人却常常使用char型数…

对象内存布局与对象头

对象内存布局 在Hotspot虚拟机里&#xff0c;对象在堆内存中的存储布局可以划分为三个部分&#xff1a;对象头、实例数据、对齐填充。 对象头 在64为系统中&#xff0c;Mark word 占了8个字节&#xff0c;cla ss Pointer 占了8个字节。从jdk1.8开始&#xff0c;存在指针压缩&am…

【探索Linux】—— 强大的命令行工具 P.6(调试器-gdb、项目自动化构建工具-make/Makefile)

阅读导航 前言一、什么是调试器二、详解 GDB - 调试器1.使用前提2.经常使用的命令3.使用小技巧 三、项目自动化构建工具 - make/Makefile1. make命令⭕语法⭕常用选项⭕常用操作⭕make命令的工作原理⭕make命令的优势&#xff1a; 2.Makefile文件⭕Makefile的基本结构⭕Makefil…

使用pnpm workspace管理Monorepo架构

在开发项目的过程中&#xff0c;我们需要在一个仓库中管理多个项目&#xff0c;每个项目有独立的依赖、脚手架&#xff0c;这种形式的项目结构我们称之为Monorepo&#xff0c;pnpm workspace就是管理这类项目的方案之一。 一、pnpm简介 1、pnpm概述 pnpm代表performance npm…

C++系列-引用

引用 引用的基本使用引用的起源引用的语法引用的本质引用的注意事项引用和指针 引用作为函数参数引用作为函数的返回值常量引用其它用返回值方式调用函数&#xff08;case 1&#xff09;用函数的返回值初始化引用的方式调用函数&#xff08;case 2&#xff09;用返回引用的方式…

数据库--SQL关键字的执行顺序

数据库相关链接&#xff1a; 数据库--数据类型&#xff1a;http://t.csdn.cn/RtqMD 数据库--三大范式、多表查询、函数sql&#xff1a;http://t.csdn.cn/udJSG 数据库--MySQL增删改查&#xff1a;http://t.csdn.cn/xkiti 一、一条sql语句通常包括&#xff1a; select fro…

make系列之入门

一.欢迎来到我的酒馆 在本章节介绍make工具。 目录 一.欢迎来到我的酒馆二.什么是make三.make与Makefile四.如何写Makefile 二.什么是make 你可能会遇到一些名词&#xff1a;GNU&#xff0c;Linux&#xff0c;make。它们是什么&#xff0c;又有什么样的联系&#xff1f; …

正则表达式:贪婪与非贪婪模式

正则中的三种模式&#xff0c;贪婪匹配、非贪婪匹配和独占模式。 在这 6 种元字符中&#xff0c;我们可以用 {m,n} 来表示 &#xff08;*&#xff09;&#xff08;&#xff09;&#xff08;?&#xff09; 这 3 种元字符&#xff1a; 贪婪模式&#xff0c;简单说就是尽可能进行…

安卓框架中的常见问题汇总

目录 1.安卓操作系统的组件结构图如下 2.问题汇总 1.安卓操作系统的组件结构图如下 2.问题汇总 问题1&#xff1a;安卓框架中的库和应用程序框架之间什么关系&#xff1f; 在安卓系统中&#xff0c;应用程序框架层&#xff08;Application Framework&#xff09;是核心应用程…