当前位置: 首页 > news >正文

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)}}

相关文章:

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

Spark MLlib机器学习库(一)决策树和随机森林案例详解 1 决策树预测森林植被 1.1 Covtype数据集 数据集的下载地址&#xff1a; https://www.kaggle.com/datasets/uciml/forest-cover-type-dataset 该数据集记录了美国科罗拉多州不同地块的森林植被类型&#xff0c;每个样本…...

CI/CD入门(二)

CI/CD入门(二) 目录 CI/CD入门(二) 1、代码上线方案 1.1 早期手动部署代码1.2 合理化上线方案1.3 大型企业上线制度和流程1.4 php程序代码上线的具体方案1.5 Java程序代码上线的具体方案1.6 代码上线解决方案注意事项2、理解持续集成、持续交付、持续部署 2.1 持续集成2.2 持续…...

【BASH】回顾与知识点梳理(三十五)

【BASH】回顾与知识点梳理 三十五 三十五. 二十七至三十四章知识点总结及练习35.1 总结35.2 练习RAIDLVMsystemd 35.3 简答题 该系列目录 --> 【BASH】回顾与知识点梳理&#xff08;目录&#xff09; 三十五. 二十七至三十四章知识点总结及练习 35.1 总结 Quota 可公平的分…...

excel逻辑函数篇2

1、IF(logical_test,[value_if_true],[value_if_false])&#xff1a;判断是否满足某个条件&#xff0c;如果满足返回一个值&#xff0c;如果不满足则返回另一个值 if(条件,条件成立返回的值,条件不成立返回的值) 2、IFS(logical_test1,value_if_true1,…)&#xff1a;检查是否…...

设计模式详解-解释器模式

类型&#xff1a;行为型模式 实现原理&#xff1a;实现了一个表达式接口&#xff0c;该接口使用标识来解释语言中的句子 作用&#xff1a;给定一个语言&#xff0c;定义它的文法表示&#xff0c;并定义一个解释器&#xff0c;这个解释器来解释。 主要解决&#xff1a;一些重…...

如何在React项目中动态插入HTML内容

React是一种流行的JavaScript库&#xff0c;用于构建用户界面。它提供了一种声明式的方法来创建可复用的组件&#xff0c;使得开发者能够更轻松地构建交互性的Web应用程序。在React中&#xff0c;我们通常使用JSX语法来描述组件的结构和行为。 在某些情况下&#xff0c;我们可…...

十六、Spring Cloud Sleuth 分布式请求链路追踪

目录 一、概述1、为什么出出现这个技术&#xff1f;需要解决哪些问题2、是什么&#xff1f;3、解决 二、搭建链路监控步骤1、下载运行zipkin2、服务提供者3、服务调用者4、测试 一、概述 1、为什么出出现这个技术&#xff1f;需要解决哪些问题 2、是什么&#xff1f; 官网&am…...

ElasticSearch DSL语句(bool查询、算分控制、地理查询、排序、分页、高亮等)

文章目录 DSL 查询种类DSL query 基本语法1、全文检索2、精确查询3、地理查询4、function score &#xff08;算分控制&#xff09;5、bool 查询 搜索结果处理1、排序2、分页3、高亮 RestClient操作 DSL 查询种类 查询所有&#xff1a;查询所有数据&#xff0c;一般在测试时使…...

【考研数学】概率论与数理统计 | 第一章——随机事件与概率(2,概率基本公式与事件独立)

文章目录 引言四、概率基本公式4.1 减法公式4.2 加法公式4.3 条件概率公式4.4 乘法公式 五、事件的独立性5.1 事件独立的定义5.1.1 两个事件的独立5.1.2 三个事件的独立 5.2 事件独立的性质 写在最后 引言 承接上文&#xff0c;继续介绍概率论与数理统计第一章的内容。 四、概…...

SpringBoot整合RabbitMQ,笔记整理

1创建生产者工程springboot-rabbitmq-produce 2.修改pom.xml文件 <!--父工程--> <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.0</version><r…...

搜狗拼音暂用了VSCode及微信小程序开发者工具快捷键Ctrl + Shit + K 搜狗拼音截图快捷键

修改搜狗拼音的快捷键 右键--更多设置--属性设置--按键--系统功能快捷键--系统功能快捷键设置--取消Ctrl Shit K的勾选--勾选截屏并设置为Ctrl Shit A 微信开发者工具设置快捷键 右键--Command Palette--删除行 微信开发者工具快捷键 删除行&#xff1a;Ctrl Shit K 或…...

Python包sklearn画ROC曲线和PR曲线

前言 关于ROC和PR曲线的介绍请参考&#xff1a; 机器学习&#xff1a;准确率(Precision)、召回率(Recall)、F值(F-Measure)、ROC曲线、PR曲线 参考&#xff1a; Python下使用sklearn绘制ROC曲线&#xff08;超详细&#xff09; Python绘图|Python绘制ROC曲线和PR曲线 源码 …...

snpEff变异注释的一点感想

snpEff变异注释整成人生思考 1.介绍2.安装过程以及构建物种参考数据库3.坑货来了4.结果文件判读5.小tips 1.介绍 &nbsp SnpEff&#xff08;Snp Effect&#xff09;是一个用于预测基因组变异&#xff08;例如单核苷酸变异、插入、缺失等&#xff09;对基因功能的影响的生物…...

“保姆级”考研下半年备考时间表

7月-8月 确定考研目标与备考计划 暑假期间是考研复习的关键时期&#xff0c;需要复习的主要内容有&#xff1a;重点关注重要的学科和专业课程&#xff0c;复习相关基础知识和核心概念。制定详细的复习计划并合理安排每天的学习时间&#xff0c;增加真题练习熟悉考试题型和答题技…...

具有弱监督学习的精确3D人脸重建:从单幅图像到图像集的Python实现详解

随着深度学习和计算机视觉技术的飞速发展&#xff0c;3D人脸重建技术在多个领域获得了广泛应用&#xff0c;例如虚拟现实、电影特效、生物识别等。但是&#xff0c;由单幅图像实现高精度的3D人脸重建仍然是一个巨大的挑战。在本文中&#xff0c;我们将探讨如何利用弱监督学习进…...

查询投稿会议的好用网址

会议伴侣 https://www.myhuiban.com/ 艾思科蓝 https://www.ais.cn/...

一元三次方程的解

一元三次方程的解法&#xff0c;点击跳转知乎原文地址 &#xff08;一&#xff09;一元三次方程降阶 一元三次方程原型&#xff1a; a x 3 b x 2 c x d 0 a x^3 b x^2 cx d 0 ax3bx2cxd0 代换削元。最简单的方法是线性变化削元。假设x my n, 带入后可以削去未知数…...

aardio开发语言Excel数据表读取修改保存实例练习

import win.ui; /*DSG{{*/ var winform win.form(text"aardio form";right759;bottom479) winform.add( buttonEnd{cls"button";text"末页";left572;top442;right643;bottom473;z6}; buttonExcelRead{cls"button";text"读取Exce…...

webshell绕过

文章目录 webshell前置知识进阶绕过 webshell 前置知识 <?phpecho "A"^""; ?>运行结果 可以看到出来的结果是字符“&#xff01;”。 为什么会得到这个结果&#xff1f;是因为代码的“A”字符与“”字符产生了异或。 php中&#xff0c;两个变…...

Spring Boot 统一功能处理

目录 1.用户登录权限效验 1.1 Spring AOP 用户统一登录验证的问题 1.2 Spring 拦截器 1.2.1 自定义拦截器 1.2.2 将自定义拦截器加入到系统配置 1.3 拦截器实现原理 1.3.1 实现原理源码分析 2. 统一异常处理 2.1 创建一个异常处理类 2.2 创建异常检测的类和处理业务方法 3. 统一…...

图像处理常见的两种拉流方式

传统算法或者深度学习在进行图像处理之前&#xff0c;总是会首先进行图像的采集&#xff0c;也就是所谓的拉流。解决拉流的方式有两种&#xff0c;一个是直接使用opencv进行取流&#xff0c;另一个是使用ffmpeg进行取流&#xff0c;如下分别介绍这两种方式进行拉流处理。 1、o…...

数据可视化数据调用浅析

数据可视化是现代数据分析和决策支持中不可或缺的一环。它将数据转化为图形、图表和可视化工具&#xff0c;以便更直观地理解和解释数据。在数据可视化的过程中&#xff0c;数据的调用和准备是关键的一步。本文将探讨数据可视化中的数据调用过程&#xff0c;并介绍一些常用的数…...

恒运资本:CPO概念发力走高,兆龙互联涨超10%,华是科技再创新高

CPO概念15日盘中发力走高&#xff0c;截至发稿&#xff0c;华是科技涨超15%再创新高&#xff0c;兆龙互联涨逾11%&#xff0c;中贝通讯涨停&#xff0c;永鼎股份、太辰光涨超5%&#xff0c;天孚通讯涨逾4%。 消息面上&#xff0c;光通讯闻名咨询机构LightCounting近日发布的202…...

【蓝桥杯】[递归]母牛的故事

原题链接&#xff1a;https://www.dotcpp.com/oj/problem1004.html 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 我们列一个年份和母牛数量的表格&#xff1a; 通过观察&#xff0c;找规律&#xff0c;我们发现&#xff1a; 当年份小于等于4时&…...

使用RDP可视化远程桌面连接Linux系统

使用RDP可视化远程桌面连接Linux系统 远程桌面连接Linux安装安装包准备服务器安装xrdp远程连接 远程桌面连接Linux 通常使用SSH来连接服务器&#xff0c;进行命令行操作&#xff0c;但是这次需要远程调试生产环境的内网服务器&#xff0c;进行浏览器访问内网网站&#xff0c;至…...

数据可视化diff工具jsondiffpatch使用学习

1.jsondiffpatch 简介 jsondiffpatch 是一个用于比较和生成 JSON 数据差异的 JavaScript 库。它可以将两个 JSON 对象进行比较&#xff0c;并生成一个描述它们之间差异的 JSON 对象。这个差异对象可以用于多种用途&#xff0c;例如&#xff1a; 生成可视化的差异报告应用差异…...

pdf 转 word

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

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

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

Java 中创建对象有哪些方式?

目录 面试回答 使用 new 关键字 使用反射机制 使用 Class 类的 newInstance() 方法 使用 Constructor 类的 newInstance 方法 使用 clone 方法 使用反序列化 使用方法句柄 使用 Unsafe 分配内存 面试回答 使用 new 关键字 这是我们最常用的、也是最简单的创建对象的方…...

Kafka 消息发送和消费流程

发送消息 流程如下&#xff1a; Producer 端直接将消息发送到 Broker 中的 Leader 分区中Broker 对应的 Leader 分区收到消息会先写入 Page Cache&#xff0c;定时刷盘进行持久化&#xff08;顺序写入磁盘&#xff09;Follower 分区拉取 Leader 分区的消息&#xff0c;并保持…...

UVa10048 Audiophobia(floyd)

题意 给出一个图&#xff0c;图中的边表示从点u到点v路径上的噪音。给出q个查询&#xff0c;问从u到v所经路径上的最小噪音 思路 在使用floyd计算点对之间的路径时&#xff0c; D u , v k m i n { D u , v k − 1 , m a x { D u , k k − 1 , D k , v k − 1 } } D_{u, v}^…...

​Redis概述

目录 Redis - 概述 使用场景 如何安装 Window 下安装 Linux 下安装 docker直接进行安装 下载Redis镜像 Redis启动检查常用命令 Redis - 概述 redis是一款高性能的开源NOSQL系列的非关系型数据库,Redis是用C语言开发的一个开源的高键值对(key value)数据库,官方提供测试…...

MsrayPlus多功能搜索引擎采集软件

MsrayPlus多功能搜索引擎采集软件 摘要&#xff1a; 本文介绍了一款多功能搜索引擎软件-MsrayPlus&#xff0c;该软件能够根据关键词从搜索引擎中检索相关数据&#xff0c;并提供搜索引擎任务、爬虫引擎任务和联系信息采集三大功能。我们将分析该软件在不同领域的应用&#xf…...

机器学习之概率论

最近&#xff0c;在了解机器学习相关的数学知识&#xff0c;包括线性代数和概率论的知识&#xff0c;今天&#xff0c;回顾了概率论的知识&#xff0c;贴上几张其他博客的关于概率论的图片&#xff0c;记录学习过程。...

【深度学习 | 数据可视化】 视觉展示分类边界: Perceptron模型可视化iris数据集的决策边界

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…...

【计算机视觉】相机基本知识(还在更新)

1.面阵工业相机与线阵工业相机 1.1 基本概念区别 面阵相机则主要采用的连续的、面状扫描光线来实现产品的检测&#xff1b; 线阵相机即利用单束扫描光来进行物体扫描的工作的。 1.2 优缺点 &#xff08;1&#xff09;面阵CCD工业相机&#xff1a; 优点&#xff1a;应用面…...

C++ (友元)(类嵌套时,成员函数以及类声明定义的顺序)小demo

#include<iostream> using namespace std; class Building; //1.因为Goodgay类需要声明Building类变量&#xff0c; //所以Building类必须Goodgay类之前声明&#xff08;前向声明&#xff09;&#xff1b; class GoodGay { public:GoodGay();void visit(); private:Build…...

前端实习第五周周记

前言 每一天做了什么还是要记录一下&#xff0c;不然过两天后就会发现&#xff0c;慢慢遗忘自己的收获与做过的东西。 这周做的是医学检验系统的样本库部分。由于是公司的代码所以不能交代具体&#xff0c;那么久聊一下每天具体做了些什么以及我的一些收获。 周一 周一上午…...

【图论】Floyd算法

一.简介 Floyd算法&#xff0c;也称为Floyd-Warshall算法&#xff0c;是一种用于解决所有节点对最短路径问题的动态规划算法。它可以在有向图或带权图中找到任意两个节点之间的最短路径。 Floyd算法的基本思想是通过中间节点逐步优化路径长度。它使用一个二维数组来存储任意两…...

ceph数据分布

ceph的存储是无主结构&#xff0c;数据分布依赖client来计算&#xff0c;有两个条主要路径。 1、数据到PG 2、PG 到OSD 有两个假设&#xff1a; 第一&#xff0c;pg的数量稳定&#xff0c;可以认为保持不变&#xff1b; 第二&#xff0c; OSD的数量可以增减&#xff0c;OSD的…...

mysql的两张表left join 进行关联后,索引进行优化案例

一 mysql的案例 1.1 不加索引情况 1.表1没加索引 2.表2没加索引 3.查看索引 1.2 添加索引 1.表1添加索引 2.表2添加索引 3.查看...

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

2018年3月全国计算机等级考试真题&#xff08;语言二级C&#xff09; 第1题 设有定义&#xff1a;char s[81]&#xff1b;int i0&#xff1b;以下不能将一行带有空格的字符串正确读入的语句或语句组是 A. while((s[i]getchar())!\n);s[i]\0; B. scanf("%s",s); C.…...

java.util.Timer简介以及简单使用示例

一、简介 定时器&#xff08;Timer&#xff09;是一个工具类&#xff0c;用于安排任务&#xff08;java.util.TimerTask&#xff09;在指定时间后执行或以指定的时间间隔重复执行。它可以用于执行定时任务、定时调度和时间延迟等操作。 定时器&#xff08;Timer&#xff09;可以…...

C语言笔试训练【第12天】

文章目录 1、请阅读以下程序&#xff0c;其运行结果是&#xff08; &#xff09;2、假设编译器规定 int 和 short 类型长度分别为32位和16位&#xff0c;若有下列C语言语句&#xff0c;则 y 的机器数为&#xff08; &#xff09;3、下列程序的输出结果是什么&#xff08; &…...

外网连接局域网的几种方式?快解析内网穿透安全便利吗?

外网连接局域网是一项网络连接中的关键技术&#xff0c;它能够让远程用户通过互联网访问内部局域网中的资源和服务。外网连接局域网为企业提供了更大的灵活性和便捷性&#xff0c;但也需要严格的安全措施来防止未经授权的访问。 外网连接局域网的几种方式 在将外网连接到局域…...

基于互斥锁的生产者消费者模型

文章目录 生产者消费者 定义代码实现 / 思路完整代码执行逻辑 / 思路 局部具体分析model.ccfunc&#xff08;消费者线程&#xff09; 执行结果 生产者消费者 定义 生产者消费者模型 是一种常用的 并发编程模型 &#xff0c;用于解决多线程或多进程环境下的协作问题。该模型包含…...

USB隔离器电路分析,SA8338矽塔sytatek电机驱动,源特科技VPS8701,开关电源,电源 大师

一、 USB隔离器电路分析 进行usb隔离可以使用USB隔离模块 ADUM3160 ADUM4160 注意&#xff1a;B0505S 最大带载0.16A&#xff0c;副边需要带载能力需要改变方案 比如移动硬盘至少需要0.5A 用充电宝、18650、设计5V1A输出电源 二、 1A隔离电压方案...

TPC-DS 测试是否支持 Glue Data Catalog?

在上一篇文章《在Hive/Spark上执行TPC-DS基准测试 (PARQUET格式)》中,我们详细介绍了具体的操作方法,当时的集群使用的是Hive Metastore,所有操作均可成功执行。当集群启用 Glue Data Catalog 时,在执行add_constraints.sql时会报错: Optimizing table date_dim (1/24).…...

网络编程(8.14)TCP并发服务器模型

作业&#xff1a; 1. 多线程中的newfd&#xff0c;能否修改成全局&#xff0c;不行&#xff0c;为什么&#xff1f; 2. 多线程中分支线程的newfd能否不另存&#xff0c;直接用指针间接访问主线程中的newfd,不行&#xff0c;为什么&#xff1f; 多线程并发服务器模型原代码&…...

认识负载均衡||WEBSHELL

目录 一、负载均衡 1.nginx负载均衡算法 2.nginx反向代理-负载均衡 二、webshell 1.构造不含数字和字母的webshell 2.如何绕过 一、负载均衡 1.nginx负载均衡算法 &#xff08;1&#xff09;轮询&#xff08;默认&#xff09;每个请求按时间顺序逐一分配到不同的后端服务&…...