Spark 3.0 - 8.ML Pipeline 之决策树原理与实战

news2024/11/23 15:04:18

目录

一.引言

二.决策树基础-信息熵

三.决策树的算法基础 - ID3 算法

四.ML 中决策树的构建

1.信息增益计算

2.连续属性划分

五.ML 决策树实战

1.Libsvm 数据与加载

2.StringIndexer

3.VectorIndexer

4.构建决策树与 Pipeline

5.测试与评估

6.获取决策树

六.总结


一.引言

决策树是在已知各种情况发生概率的基础上,通过构成决策树来求取净现值的期望值大于等于零的概率,评估项目风险,判断其可行性的决策分析方法,是直观运用概率分析的一种图解法。由于其决策分支画成图像很像一颗树的枝干,故称之为决策树。

二.决策树基础-信息熵

信息熵指的是对事件中不确定的信息度量。一个事件或属性中,其信息熵越大代表其不确定因素越大,对数据分析的计算更有益。因为熵其实用来描述一个物体或事件内部的混乱程度。在一个事件中,需要计算各个属性的不同信息熵。如果事件中包含 n 个属性,且个属性事件彼此独立、无相关性,此时可以将信息熵定义为单个属性的对数平均值:

 举个栗子 🌰:

上述描述了 14 天中不同的天气属性以及是否出门打网球:

E(tennis) = -\sum p_ilog_p_i = -(\frac{5}{14}log_2\frac{5}{14})- -(\frac{9}{14}log_2\frac{9}{14})\approx 0.94

即是否出门打网球这个属性的信息熵 Entropy 为 0.918,同理以 Humidity 湿度为属性计算 Tennis 的信息熵:

其中 High 情况下 Tennis Yes 3 次,No 4 次,Normal 情况下 Tennis Yes 6 次,No 1 次:

E (Hmd) = \frac{7}{14} \cdot (-\frac{3}{7}*1og\frac{3}{7}-\frac{4}{7}log\frac{4}{7}) + \frac{7}{14} \cdot (-\frac{1}{7}*1og\frac{1}{7}-\frac{6}{7}log\frac{6}{7}) \approx 0.788

通过计算对数平均值可以获得条件概率下不同属性的信息熵。使用下述方法可以轻松计算一个属性的信息熵:

  def calcEntropy(pArr: Array[Double]): Double = {
    var sum = 0D
    pArr.foreach(p => {
      sum += -1.0 * p * log2(p)
    })
    sum
  }

  def log2(p: Double): Double = {
    Math.log(p) / Math.log(2) // Math.log的底为e
  }

三.决策树的算法基础 - ID3 算法

上面介绍了信息熵,下面基于这个概念介绍如何尽可能的建立一颗最短的、最小的且最有效的决策树。ID3 算法是基于信息熵的一种经典决策树构建方法,其以信息熵的下降速度为选取测试属性的标准,即在每个节点选取还尚未用来划分的、具有最高信息增益的属性作为划分标准并不断重复这个过程,直到生成的决策树可以完美分类训练样例。其核心为信息增益:

信息增益,指的是一个时间前后发生的不同信息之间的差值,即在决策树生成过程前后不同的信息熵差值,公式可以表达为:

Gain(P_1,P_2) = E(P_1) - E(P_2)

以上面的 Tessins 与 Humidity 为例:

然后,重复上表中每个属性的信息增益计算,并选择信息增益最高的属性作为决策树中的第一个分割点。在这种情况下,outlook 产生了最高的信息增益。然后,对每个子树重复该过程。 

四.ML 中决策树的构建

1.信息增益计算

Spark ML 实现了支持使用连续和离散特征的二元和多类分类以及回归的决策树。该实现按行对数据进行分区,允许使用数百万甚至数十亿个实例进行分布式训练。决策树构建采用递归二分法方式。不断从根节点进行生成,直到决策树的需求信息增益满足一定条件为止:

Gain(P_1,P_2) = E(P_1) - \frac{P_{left}}{P_1}E(P_{left}) - \frac{P_{right}}{P_1}E(P_{right})

Left、right 为待计算属性,每增加一个分类节点,待计算属性便减少一个。

2.连续属性划分

上面介绍的 Tennis,其属性均为离散属性,实际应用中会有大量的连续性特征,解决办法就是在计算时根据需要将数据划分为若干个部分进行处理。这些被划分的若干部分在 ML 中称为 bin,即分箱的意思。每个作为分割点的节点被称为 split。决策树是一种贪婪算法,其通过从每一组可能的分割中选择最佳分割来贪婪的选择每个分割。例如一个连续特征为 {1,2,3,4,5},实际中采用二分法,此时 split 为3,划分得到 {1,2,3},{4,5} 两个 bin。

五.ML 决策树实战

1.Libsvm 数据与加载

实战前首先熟悉一种数据格式-Libsvm:

数据的第一列为标签,以上面 Tennis 为例,1 代表出去,0 代表不出去,后面的 key 代表属性的序号,value 代表该属性的具体值。下面读取实战的数据:

    val spark = SparkSession
      .builder                //创建spark会话
      .master("local")        //设置本地模式
      .appName("DecisionTreeClassificationExample")   //设置名称
      .getOrCreate()          //创建会话变量

    spark.sparkContext.setLogLevel("error")

    // 读取文件,装载数据到spark dataframe 格式中
    val data = spark.read.format("libsvm").load("./sample_libsvm_data.txt")

通过 .format 指定 livsvm 格式即可读取对应格式文件,解析后获得一个两列的 DataFrame,一列为 label,另一列为 features。

Tips:

细心的同学可能会发现,原始的数据为 128:x、129:y、130:z,为什么 features 里变成了 127、128、129 所有的索引的减了1,这是因为 Libsvm 数据格式从 1 开始,而我们的 features 的 Vector 内索引从 0 开始,所以需要将 libsvm 数据中的 key 都减去1,而 value 则不动。

2.StringIndexer

    val labelIndexer = new StringIndexer()
      .setInputCol("label")
      .setOutputCol("indexedLabel")
      .fit(data)

字符索引器,通过遍历标签添加元数据到标签列,实现【标签 -> 序号】的映射,如果是数值型 Label,会先将 Label 转化为字符串,然后再进行索引化,如果 label 已经是字符串,则直接进行索引化。其中标签索引的顺序按照标签出现的频率来排序,出现最多的 Label 索引即为0,依次逆序排列。

上面说的可能比较绕,简单解释下该函数的意义就是将不规则的 label 处理为有序的数字序号,例如原始标签有 A、C、E 三种类型,通过 StringIndexer 会变成 0、1、2,而对应的映射关系取决于 A、C、E 的出现次数,次数最多的索引为 0。E 出现最多,所以 E 的索引为0,以此类推。

    +---+--------+-------------+
    | id|category|categoryIndex|
    +---+--------+-------------+
    | 0| A| 1.0|
    | 1| C| 2.0|
    | 2| E| 0.0|
    | 3| E| 0.0|
    | 4| A| 1.0|
    | 5| E| 0.0|
    +---+--------+-------------+

上面的实战数据我们转换看一下:

 看到 label 0.0 变为 1.0, 1.0 变为 0.0,我们再用 Spark Sql 看下标签数据的分布:

     val labelIndexDF = labelIndexer.transform(data)
     labelIndexDF.show(5)
     labelIndexDF.createOrReplaceTempView("LabelIndex")
     spark.sql("select label,count(*) from LabelIndex group by label").collect().foreach(println)

没毛病,label=1.0 的标签多,所以 1.0 被映射为 0.0。 

[0.0,43]
[1.0,57]

3.VectorIndexer

    // 自动识别分类特征,并对其进行索引
    val featureIndexer = new VectorIndexer()
      .setInputCol("features") // 设置输入输出参数
      .setOutputCol("indexedFeatures")
      .setMaxCategories(5) // 具有多于5个不同值的特性被视为连续特征
      .fit(data)

该方法主要用于自动识别离散与分类特征,提高决策树 ML 方法的分类效果。其中 MaxCategories 参数设置一个数值,如果某个特征的取值类型多于该参数,则该参数会被认定为连续特征,不作处理,反之会被认定为离散特征,并被重新编号为 0-K (K < MaxCategories)。


+-------------------------+-------------------------+
|features                 |indexedFeatures          |
+-------------------------+-------------------------+
|(3,[0,1,2],[2.0,5.0,7.0])|(3,[0,1,2],[2.0,1.0,1.0])|
|(3,[0,1,2],[3.0,5.0,9.0])|(3,[0,1,2],[3.0,1.0,2.0])|
|(3,[0,1,2],[4.0,7.0,9.0])|(3,[0,1,2],[4.0,3.0,2.0])|
|(3,[0,1,2],[2.0,4.0,9.0])|(3,[0,1,2],[2.0,0.0,2.0])|
|(3,[0,1,2],[9.0,5.0,7.0])|(3,[0,1,2],[9.0,1.0,1.0])|
|(3,[0,1,2],[2.0,5.0,9.0])|(3,[0,1,2],[2.0,1.0,2.0])|
|(3,[0,1,2],[3.0,4.0,9.0])|(3,[0,1,2],[3.0,0.0,2.0])|
|(3,[0,1,2],[8.0,4.0,9.0])|(3,[0,1,2],[8.0,0.0,2.0])|
|(3,[0,1,2],[3.0,6.0,2.0])|(3,[0,1,2],[3.0,2.0,0.0])|
|(3,[0,1,2],[5.0,9.0,2.0])|(3,[0,1,2],[5.0,4.0,0.0])|
+-------------------------+-------------------------+

上面示例中共有三个特征:

0 - [2,3,4,5,8,9] - 类别数为6,大于 MaxCategories,不执行划分

1 - [4,5,6,7,9],小于 MaxCategories,执行划分  [4,5,6,7,9] -> [0,1,2,3,4]

2 - [2,7,9],小于 MaxCategories,执行划分 [2,7,9] -> [0,1,2]

该方法主要用于离散特征与连续特征的区分,对于连续型特征,决策树的划分点划分多为 Feat >= threshold 的形式,而离散型的特征则多为 Feat in {value}。

4.构建决策树与 Pipeline

    // 按照7:3的比例进行拆分数据,70%作为训练集,30%作为测试集。
    val Array(trainingData, testData) = data.randomSplit(Array(0.7, 0.3))

    // 建立一个决策树分类器
    val dt = new DecisionTreeClassifier()
      .setLabelCol("indexedLabel")
      .setFeaturesCol("indexedFeatures")
      .setMaxDepth(2)

    // 将索引标签转换回原始标签
    val labelConverter = new IndexToString()
      .setInputCol("prediction")
      .setOutputCol("predictedLabel")
      .setLabels(labelIndexer.labelsArray(0))


    // 把索引和决策树链接(组合)到一个管道(工作流)之中
    val pipeline = new Pipeline()
      .setStages(Array(labelIndexer, featureIndexer, dt, labelConverter))

    // 载入训练集数据正式训练模型
    val model = pipeline.fit(trainingData)

- DecisionTreeClassifier 主要属性有

Impuriry (String) : 计算信息增益的方式

maxDepth(Int) :  树的深度

maxBins(Int) : 能够分裂的数据集合数量

可以通过 dt.extractParamMap 方法获取当前模型的自定义参数与默认参数。

- IndexToString

该方法主要用于将转换后的 label 再映射回去,例如前面将 1->0 0->1 再重新反向映射回去:

    // 按顺序来,相当于映射 0->1 1->0
    println("=========Label Index=========")
    println(labelIndexer.labelsArray(0).mkString(","))

 labelsArray 为如下形式,将其 zipWithIndex 再反转即可实现 label 的反向映射。

=========Label Index=========
1.0,0.0

- pipeline

构建 Pipeline 之后即可实现 PipelineModel.fit 训练训练数据。

5.测试与评估

    // 使用测试集作预测
    val predictions = model.transform(testData)

    // 选择一些样例进行显示
    predictions.select("predictedLabel", "label", "features").show(5)

    // 计算测试误差
    val evaluator = new MulticlassClassificationEvaluator()
      .setLabelCol("indexedLabel")
      .setPredictionCol("prediction")
      .setMetricName("accuracy")
    val accuracy = evaluator.evaluate(predictions)
    println(s"Test Error = ${(1.0 - accuracy)}")

 使用 PipelineModel.transform 进行测试,使用 label 与 predict 进行 accuracy 的指标评估:

 

6.获取决策树

    val treeModel = model.stages(2).asInstanceOf[DecisionTreeClassificationModel]
    println(s"Learned classification tree model:\n ${treeModel.toDebugString}")

使用 AsInstanceOf 将 Stage(2) 转化为 Dt 并调用 toDebugString 获取树的结构:

通过两层 If-Else 嵌套形式展示了一棵树,这样再来一个样本,我们可以轻易地判断其所属类别。

 

六.总结

本文根据样例数据进行了 Spark ML 决策树的 Demo 讲解,其中涉及到很多特征处理与转化的组件,可以通过样例进行熟悉,后续也会基于真实数据进行随机森林与梯度提升树的案例,加深对树的理解。

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

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

相关文章

【学习笔记67】JavaScript中的闭包

一、认识函数的过程 1. 定义 在堆内存中开辟一段内存空间(XF001)把函数体的内容&#xff0c;完全百分百的照抄一份&#xff0c;存放在内存空间中(XF001)把内存空间的地址(XF001) 赋值给函数名2. 调用 根据函数名内存储的地址 (XF001) &#xff0c;去堆内存中找到对应函数会去…

Nginx安装Openresty加载Lua代码

1、下载 VM环境&#xff1a;ubuntu 16 http://openresty.org/cn/download.html 我选择的是截图红框的那个版本&#xff0c;其他高级的版本&#xff0c;我编译的时候都会报错&#xff0c;所以选择了这个版本&#xff0c;大家编译失败的时候不要放弃&#xff0c;继续选择其他版…

【PS-8】选区

目录 矩形选框工具 先选区再按【shift】&#xff0c;正方形选区 选区的同时按【ALT】&#xff0c;从中心点选区 选区时按【shiftalt】&#xff0c;从中心点建立正方形选区 模式1&#xff1a;添加到选区 模式2&#xff1a;新选区 模式3&#xff1a; 从选区减去 模式4&am…

《C++Primer》-1-前序与基础第I部分重点

文章目录第一章 开始1. c之与其他语言的优点&#xff1f;2. c语言的组成3. 标准输入输出cin、cout4. include格式第二章 变量和基本类型1. 无符号数的使用注意2. 初始化注意事项3. 声明与定义4. 标识符的下划线规则5. &引用、取地址&、指针的区别6. 如何理解“因为引用…

Rainbow Brackets的配色修改和使用

修改配色&#xff1a;&#xff09; 敲好看内&#xff01;&#xff01; 记得每个都要改噢&#xff01; 5分别对应的是&#xff1a; 圆括号 方括号 波形括号 尖括号 6分别对应的是&#xff1a; ECB1E9 F6F0A9 78B8EF F3BBA2 A9D57E 使用方式 Ctrl 鼠标右键&#xff1a;高亮{}…

C++ Reference: Standard C++ Library reference: Containers: list: list: end

C官网参考链接&#xff1a;https://cplusplus.com/reference/list/list/end/ 公有成员函数 <list> std::list::end C98 iterator end(); const_iterator end() const; C11 iterator end() noexcept; const_iterator end() const noexcept; 返回结束迭代器 返回一个指向…

企业日常公关如何抵御负面信息的入侵?

如今&#xff0c;互联网时代信息传播速度极快&#xff0c;这使得宣传工作效率倍增&#xff0c;也给企业舆情管理带来一定的挑战。舆情优化搞得好&#xff0c;企业宣传工作事半功倍&#xff0c;网络舆论走向负面的话&#xff0c;则对宣传工作非常不利&#xff0c;会导致推广效果…

Echart 柱状图,X轴斜着展示

option { color: [‘#3398DB’], tooltip: { trigger: ‘axis’, axisPointer: { // 坐标轴指示器&#xff0c;坐标轴触发有效 type: ‘shadow’ // 默认为直线&#xff0c;可选为&#xff1a;‘line’ | ‘shadow’ } }, grid: { left: ‘3%’, right: ‘4%’, bottom: ‘3%’…

go-zero服务自动收集线上问题线上实战

前言 ​ 对于pprof&#xff0c;相信熟悉go语言的程序员基本都不陌生&#xff0c;一般线上的问题都是靠它可以快速定位。但是实际项目中&#xff0c;很多时候我们为了性能都不会开启它&#xff0c;但是出了问题又要靠它来分析。好在go-zero已经帮我们很好的集成进来了&#xff…

Leu-Trp-Leu-COOH,42293-99-2

编号: 122381中文名称: 三肽Leu-Trp-Leu英文名: Leu-Trp-LeuCAS号: 42293-99-2单字母: H2N-LWL-OH三字母: H2N-Leu-Trp-Leu-COOH氨基酸个数: 3分子式: C23H34N4O4平均分子量: 430.54精确分子量: 430.26等电点(PI): 6.11pH7.0时的净电荷数: -0.02平均亲水性: -2.3333333333333疏…

[附源码]计算机毕业设计springboot环境保护宣传网站

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Windows中睡眠和休眠的区别

休眠一般是笔记本电脑才有的功能&#xff0c;睡眠是几乎所有电脑都有 一、系统层面的理解 1.1 睡眠 睡眠状态下&#xff0c;电脑只会消耗很少的电量&#xff0c;电脑可更快启动&#xff0c;并且可以立即返回到离开的位置。因此&#xff0c;使用者不必担心由于电池电量耗尽而丢…

国外Essay写作怎么让内容丰富起来?

在国外大学&#xff0c;写Essay是很多课程常用的考核方式&#xff0c;这种形式也非常考察同学们的综合能力。但对于本身英语就是第二语言的我们&#xff0c;有时候会觉得困难&#xff0c;有时候拖到最后匆忙交上去一篇&#xff0c;当然没有办法得到好分数。今天我们就来看一看如…

后端存储实战课总结(上)

创建和更新订单 表设计 最少应该有以下几张表&#xff1a; 订单主表&#xff1a;保存订单基本信息订单商品表&#xff1a;保存订单中的商品信息订单支付表&#xff1a;保存订单支付和退款信息订单优惠表&#xff1a;保存订单的优惠信息 订单主表和字表是一对多关系&#xf…

android Framework 中用到了哪些跨进程通信方式?

文章目录Linux 有哪些跨进程的通信方式&#xff1f;管道本地 Socket共享内存信号Linux 有哪些跨进程的通信方式&#xff1f; Binder 机制是Android基于Linux的一种独特的IPC机制。我们常用的AMS&#xff0c;PMS 等都是通过Binder机制来完成跨进程通信的&#xff0c;那么除了Bin…

维格云连接功能日志入门教程

目录 维格云连接功能简介 维格云连接功能效果 涉及功能范围 注意事项 维格云连接功能简介 应用内「数据管理——概览」界面,新增了连接功能日志模块,便于查看连接功能的执行结果、排查问题。 维格云连接功能效果 鼠标移动至目标日志,点击“查看数据”可以跳转至对应数据…

求树的直径算法以及证明

以下为两次dfs&#xff08;bfs&#xff09;的做法以及正确性证明。 算法步骤 &#xff08;1&#xff09;任取树上一点S&#xff0c;以S为源点BFS得S到各个顶点的d值&#xff1b; &#xff08;2&#xff09;取d值最大者之一为P&#xff0c;再以P为源点BFS得P到各个顶点的d值&am…

Metabase学习教程:仪表盘-7

使用Metabase构建记录查找工具 如何使用Metabase构建内部查找工具来快速查找有关客户、订单或其他数据的详细信息。 我们写过人们使用Metabase的一些有趣的方式其中之一是使用Metabase作为内部或后台应用程序的解决方案&#xff0c;例如客户查找工具。您不必构建定制页面来查…

惠普笔记本重装系统后没有声音如何解决

​最近很多小伙伴又开始了网上冲浪的生活,但是电脑用久了难免会发生一些故障的问题,最近就有些小伙伴重装系统之后问小编自己的电脑没有声音的问题?不要慌?接下来小编教你惠普笔记本重装系统后没有声音的解决方法. 工具/原料&#xff1a; 系统版本&#xff1a;win7系统 型…

基于单片机技术的自动停车器的设计

目 录 摘 要 I Abstract II 1绪论 1 1.1课题研究背景 1 1.2国内外发展现状 1 1.3汽车自动停车器的研究目的 2 1.4课题研究的意义 2 2汽车停车器的功能设计 3 2.1汽车自动停车器的设计要求 3 2.2停车器的主要功能 3 3汽车自动停车器的硬件设计 5 3.1汽车自动停车器的硬件组成 5 …