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.rhkb.cn/news/97024.html

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

相关文章

2021年3月全国计算机等级考试真题(C语言二级)

2021年3月全国计算机等级考试真题&#xff08;C语言二级&#xff09; 第1题 算法空间复杂度的度量方法是&#xff08;&#xff09; A. 算法程序的长度 B. 算法所处理的数据量 C. 执行算法所需要的工作单元 D. 执行算法所需要的存储空间 正确答案&#xff1a;D 第2题 下列叙…

回归预测 | MATLAB实现SA-SVM模拟退火算法优化支持向量机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现SA-SVM模拟退火算法优化支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现SA-SVM模拟退火算法优化支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基本…

IDEA中导出Javadoc遇到的GBK编码错误的解决思路和应用

IDEA中导出Javadoc遇到的GBK编码错误的解决思路和应用 ​ 当我们在导出自己写的项目的api文档的时候呢&#xff0c;有的时候会出现以下问题&#xff1a;也就是GBK编码错误不可导出 错误描述&#xff1a;编码GBK的不可映射字符无法导出&#xff0c;可以看出这是我们自己写的中文…

使用IText导出复杂pdf

1、问题描述 需要将发票导出成pdf&#xff0c;要求每页都必须包含发票信息和表头行。 2、解决方法 使用IText工具实现PDF导出 IText8文档&#xff1a;Examples (itextpdf.com) 3、我的代码 引入Itext依赖&#xff0c;我这里用的是8.0.1版本 <dependency><groupId>…

Win11中zookeeper的下载与安装

下载步骤 打开浏览器&#xff0c;前往 Apache ZooKeeper 的官方网站&#xff1a;zookeeper官方。在主页上点击"Project"选项&#xff0c;并点击"Release" 点击Download按钮&#xff0c;跳转到下载目录 在下载页面中&#xff0c;选择版本号&#xff0c;并点…

Ctfshow web入门 权限维持篇 web670-web679 详细题解 全

CTFshow 权限维持 web670【】 补充一下PHP中单双引号的区别&#xff1a; 单引号和双引号之间最显着的区别在于我们插入字符串和变量时。单引号不插入字符串和变量。**单引号内的内容会按原样打印出来。**在大多数情况下&#xff0c;单引号内没有任何变量或转义序列的编译。 …

04_15页表缓存(TLB)和巨型页

前言 linux里面每个物理内存(RAM)页的一般大小都是4kb(32位就是4kb),为了使管理虚拟地址数变少 加快从虚拟地址到物理地址的映射 建议配值并使用HugePage巨型页特性 cpu和mmu和页表缓存(TLB)和cache和ram的关系 CPU看到的都是虚拟地址&#xff0c;需要经过MMU的转化&#xf…

机器学习深度学习——BERT(来自transformer的双向编码器表示)

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——transformer&#xff08;机器翻译的再实现&#xff09; &#x1f4da;订阅专栏&#xff1a;机器学习&am…

houdini xyzdist primuv 实现按路径走

2. meause distance v 0; add popforce

关于Linux Docker springboot jar 日志时间不正确 问题解决

使用Springboot项目的jar&#xff0c;制作了一个Docker镜像&#xff0c;启动该镜像后发现容器和容器中的Springboot 项目的日志时间不正确。 解决 查看容器时间命令为&#xff1a; docker exec 容器id date 1. 容器与宿主机同步时间 在启动镜像时候把操作系统的时间通过&q…

iShot Pro for Mac 2.3.9最新中文版

iShot Pro是一款非常优秀的Mac截图软件&#xff0c;软件非常易于操作&#xff0c;主页面还设置了学习教程&#xff0c;可以轻松玩转软件所有功能&#xff0c;并且功能非常强大&#xff0c;不仅可以实现多种截图方式&#xff0c;还可以进行标注、贴图、取色、录屏、录音、OCR识别…

【二叉树前沿篇】树

【二叉树前沿篇】树 1 树的概念2. 树的相关概念3. 树的表示4. 树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09; 1 树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是…

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

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

IDEA:Error running,Command line is too long. 解决方法

报错如下&#xff1a; Error running SendSmsUtil. Command line is too long. Shorten the command line via JAR manifest or via a classpath file and rerun.原因是启动命令过长。 解决方法&#xff1a; 1、打开Edit Configurations 2、点击Modify options设置&#x…

Linux命令200例:nc非常有用的网络工具(常用)

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &…

根据源码,模拟实现 RabbitMQ - 实现消息持久化,统一硬盘操作(3)

目录 一、实现消息持久化 1.1、消息的存储设定 1.1.1、存储方式 1.1.2、存储格式约定 1.1.3、queue_data.txt 文件内容 1.1.4、queue_stat.txt 文件内容 1.2、实现 MessageFileManager 类 1.2.1、设计目录结构和文件格式 1.2.2、实现消息的写入 1.2.3、实现消息的删除…

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

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

【BUG】Docker启动MySQL报错

个人主页&#xff1a;金鳞踏雨 个人简介&#xff1a;大家好&#xff0c;我是金鳞&#xff0c;一个初出茅庐的Java小白 目前状况&#xff1a;22届普通本科毕业生&#xff0c;几经波折了&#xff0c;现在任职于一家国内大型知名日化公司&#xff0c;从事Java开发工作 我的博客&am…

Spring事务和事务传播机制(1)

前言&#x1f36d; ❤️❤️❤️SSM专栏更新中&#xff0c;各位大佬觉得写得不错&#xff0c;支持一下&#xff0c;感谢了&#xff01;❤️❤️❤️ Spring Spring MVC MyBatis_冷兮雪的博客-CSDN博客 在Spring框架中&#xff0c;事务管理是一种用于维护数据库操作的一致性和…

pdf 转 word

pdf 转 word 一、思路 直接调用LibreOffice 命令进行文档转换的命令行工具 使用的前系统中必须已经安装了 libreofficelibreoffice已翻译的用户界面语言包: 中文 (简体)libreoffice离线帮助文档: 中文 (简体)上传字体 重点&#xff1a;重点&#xff1a;重点&#xff1a; 亲…