16. 机器学习 - 决策树

在这里插入图片描述

Hi,你好。我是茶桁。

在上一节课讲SVM之后,再给大家将一个新的分类模型「决策树」。我们直接开始正题。

决策树

我们从一个例子开始,来看下面这张图:

Alt text

假设我们的x1 ~ x4是特征,y是最终的决定,打比方说是买东西和不买东西,0为不买,1为买东西,假设现在y是[0,0,1,0,1]

那么,我们应该以哪个特征为准去判断到底y是0还是1呢?

如果关注x3,那么x3为A的时候,即有0也有1,我们先放一边找找看有没有更合适的。

如果是x4的话,肉眼可见的,区分度是最准确的对吧?B的都是都是0,C的时候都是1,那么x4也就是区分度最大的。

我们现在换成人的思维过程来说,肯定是期望先找到那个最能区分它的,就是最能识别的特征。这个最能识别的特征在数学里面有一个专门的定义:Salient feature, 就是显著特征。

如果我们更改一下x4的值,变成[B,C,C,B,B], 那x4也就不那么显著了。这个时候最能区分的就是x2了,只在x2[1]的位置上判断错了一个。

这个时候,我们就需要客观的衡量一下,什么叫做最能区分,或者说是最能分割,最显著的区别。这就需要一个专门的数值来计算。

那我们就来看看,到底怎么样来做区分。我们现在根据一个值,将我们的数据分成了两堆,那咱们的期望就是这两堆数据尽可能是一样的。比如极端情况下,一堆全是0, 一堆全是1,当然,中间混进去一个不一样的也行,但是尽可能的「纯」:

Alt text

好,那我们该怎么定义这个「纯」呢?

我们大家应该都知道物理里有一个定义:「熵」,那熵在物理里是衡量物体的混乱程度的。

比如说一个组织、一个单位、公司或者个人,其内部的熵都是在呈现越来越混乱,而且熵的混乱具有不可逆性。这个呢,就是熵增定律,也叫「热力学第二定律」。是德国人克劳修斯提出的理论,最初用于揭示事物总是向无序的方向的发展、以及“孤立系统下热量从高温物体流向低温不可逆”的热力学定律(要不说看我的文章涨学问呢是吧)。

信息熵

好,说回咱们的机器学习。后来有一个叫「香农」的人要衡量一堆新息的混乱程度,就起了一个名字「信息熵」,也就成了衡量信息的复杂程度。

那么信息熵怎么求呢?

E n t r o p y = ∑ i = 1 n − p ( c i ) l o g 2 ( p ( c i ) ) \begin{align*} Entropy = \sum_{i=1}^n-p(c_i)log_2(p(c_i)) \end{align*} Entropy=i=1np(ci)log2(p(ci))

这个值越大,就说明了这个新息越混乱,相反的,越小就说明越有秩序。来,我们看一下代码演示,定义一个熵的方法:

import numpy as np
from icecream import ic
from collections import Counter

def pr(e, elements):
    counter = Counter(elements)
    return counter[e] / len(elements)

# 信息熵
def entropy(elements):
    return -np.sum(pr(e, element) * np.log(pr(e, elements)) for e in elements)

然后我们具体的来看几组数据:

ic(entropy([1,1,1,1,1,0]))
ic(entropy([1,1,1,1,1,1]))
ic(entropy([1,2,3,4,5,8]))
ic(entropy([1,2,3,4,5,9]))
ic(entropy(['a','b','c','c','c','c','c']))
ic(entropy(['a','b','c','c','c','c','d']))

---
ic| entropy([1,1,1,1,1,0]): 1.05829973151282
ic| entropy([1,1,1,1,1,1]): -0.0
ic| entropy([1,2,3,4,5,8]): 1.7917594692280547
ic| entropy([1,2,3,4,5,9]): 1.7917594692280547
ic| entropy(['a','b','c','c','c','c','c']): 1.7576608876629927
ic| entropy(['a','b','c','c','c','c','d']): 2.1130832934475294

我们可以看到,最「纯」的是第二行数据,然后是第一行,第三行和第四行是一样的。5,6行就更混乱一些。

那接下来的知识点是只关于Python的,我们看上面的代码是不是有点小问题?这个代码里有很多的冗余。一般情况下,会将counter改成全局变量,但是一般如果想要代码质量好一些,尽量不要轻易定义全局变量。我们来简单的修改一下:

def entropy(elements):
    # 信息熵
    def pr(es):
        counter = Counter(es)

        def _wrap(e):
            return counter[e] / len(elements)
        
        return _wrap
    
    p = pr(elements)
    return -np.sum(p(e) * np.log(p(e)) for e in elements)

这样写之后,我们再用刚才的数据来进行调用会看到结果完全一样。不过如果这样写之后,如果我们数据量很大的情况下,会发现会快很多。

Gini系数

除了上面这个信息熵之外,还有一个叫Gini系数,和信息熵很类似:

G i n i = 1 − ∑ i = 1 n p 2 ( C i ) \begin{align*} Gini = 1 - \sum_{i=1}^np^2(C_i) \\ \end{align*} Gini=1i=1np2(Ci)

假如说probability都是1,也就是最纯的情况,那么1减去1就等于0。如果它特别长,特别混乱,都很分散,那probability就会越接近于0,那么1减去0,那结果也就是越接近于1。

那么代码实现一下就是这样:

def geni(elements):
    return 1-np.sum(pr(e) ** 2 for e in set(elements))

好吧,这个时候我发现一个问题,之前我们将probability函数定义到熵函数内部了,为了让Gini函数能够调用,我们还得拿出来。在我们之前修改的初衷不变的情况下,我们来这样进行修改:

def pr(es):
    counter = Counter(es)

    def _wrap(e):
        return counter[e] / len(es)
    return _wrap

def entropy(elements):
    # 信息熵
    p = pr(elements)
    return -np.sum(p(e) * np.log(p(e)) for e in elements)

哎,这样就对了。优雅…

然后我们来修改Gini函数,让其调用pr函数:

def gini(elements):
    p = pr(elements)
    return 1 - np.sum(p(e) ** 2 for e in set(elements))

然后我们写一个衡量的方法:

pure_func = gini

然后我们将之前的调用都修改一下:

ic(pure_func([1,1,1,1,1,0]))
ic(pure_func([1,1,1,1,1,1]))
ic(pure_func([1,2,3,4,5,8]))
ic(pure_func([1,2,3,4,5,9]))
ic(pure_func(['a','b','c','c','c','c','c']))
ic(pure_func(['a','b','c','c','c','c','d']))

---
ic| pure_func([1,1,1,1,1,0]): 0.2777777777777777
ic| pure_func([1,1,1,1,1,1]): 0.0
ic| pure_func([1,2,3,4,5,8]): 0.8333333333333333
ic| pure_func([1,2,3,4,5,9]): 0.8333333333333333
ic| pure_func(['a','b','c','c','c','c','c']): 0.44897959183673464
ic| pure_func(['a','b','c','c','c','c','d']): 0.6122448979591837

我们可以看到,Gini系数是把整个纯度压缩到了0~1之间,越接近于1就是越混乱,越接近0呢就是越有秩序。

其实除了数组之外,字符串也是一样可以衡量的:

ic(pure_func("probability"))
ic(pure_func("apple"))
ic(pure_func("boom"))

---
ic| pure_func("probability"): 0.8760330578512396
ic| pure_func("apple"): 0.72
ic| pure_func("boom"): 0.625

在能够定义纯度之后,现在如果我们有很多数据,就比方说是我们最之前定义数据再增多一些:[x1, x2, x3, ..., xn],那么我们决策树会做什么呢?

根据x1对y做了分类,根据x2对y做了分类,做了分类之后,通过x1把y分成了两堆,一堆我们称呼其为m_left, 另外一堆我们称呼其为m_right,然后我们来定义一个loss函数:

l o s s = m l e f t n ⋅ G l e f t + m r i g h t m ⋅ G r i g h t \begin{align*} loss = \frac{m_{left}}{n} \cdot G_{left} + \frac{m_{right}}{m} \cdot G_{right} \end{align*} loss=nmleftGleft+mmrightGright

现在要让这个loss函数的值最小。在整个式子中,G代表的是纯度函数,这个纯度函数可以是Entropy,也可以是Gini。

loss函数的值最小的时候,就可以实现左右两边分的很均匀。我们把这个算法就叫做CART。

Alt text

CART算法其实就是classification and regression tree Algorithm, 也称为「分类和回归树算法」。

上面我们讲的所有内容可以实现分类问题,那么回归问题怎么解决呢?CART里可是包含了Regression的。

好,还是我们最之前给的数据,我们现在的y不是[0,0,1,0,1]了,我们将其更改为[1.3,1.4,0.5,0.8,1.9]。我们人类大脑中的直觉会怎么分类?会将[1.3,1.4,1.9]分为一类,而[0.5,0.8]分为一类对吧?(我说的是大部分人,少部分「天才」忽略)。

那么为了完成这样的一个分类,我们将之前公式里的纯度函数替换成MSE,那么函数就会变成如下这个样子:

J ( k , t k ) = m l e f t m ⋅ M S E l e f t + m r i g h t m M S E r i g h t J(k, t_k) = \frac{m_{left}}{m} \cdot MSE_{left} + \frac{m_{right}}{m}MSE_{right} J(k,tk)=mmleftMSEleft+mmrightMSEright

MSE是什么呢?其实就是均方误差。

M S E n o d e = ∑ i ∈ n o d e ( y ^ n o d e − y ( i ) ) 2 y ^ n o d e = 1 m n o d e ∑ i ∈ n o d e y ( i ) \begin{align*} MSE_{node} & = \sum_{i \in node}(\hat y_{node} - y^{(i)})^2 \\ \hat y_{node} & = \frac{1}{m_{node}}\sum_{i\in node}y^{(i)} \end{align*} MSEnodey^node=inode(y^nodey(i))2=mnode1inodey(i)

我们要取的,就是最小的那一个MSE。

决策树最大的优点就是在决策上我们需要更大的解释性的时候很直观,决策树可以将其分析过程以树的形式展现出来。一般在商业、金融上进行决策的时候我们都需要很高的解释性。就比如下面这个例子:

Alt text

第二呢,它可以来提取重要特征。决策术可能某一类问题上效果假设最多只能做到85%的准确度,我们期望换一种模型,希望用到的维度少一点。

比方说要做逻辑回归,我们期望的w.x+b乘以一个Sigmoid,原来的x是10维的,我们期望把它降到5维。那这个时候决策树的构建过程就从最显著的特征开始逐渐构建,我们就可以把它前五个特征给他保留下来,前五个特征就是最salience的feature。我们假如把它用到逻辑回归上,直接用这五个维度就可以了。

接着我们来个问题:本来是10维的我们期望把它变成5维,为什么我们希望降维呢?

还记得我们「拟合」这一节么?我们说过,过拟合最主要的原因是数据量过少或者模型过于复杂,那为什么数据量过少呢?不知道是否还记得我讲过的维度灾难。

多个维度就需要多个数量级的数据。在仅有这么多数据的情况下,维度越多需要更多数据来拟合,但是大部分时候我们并没有那么多数据。

这个问题其实是一个很经典的问题,为什么我们做各种机器学习的时候期望降维。如果能把这个仔仔细细的想清楚,其实机器学习原理基本上已经能够掌握清楚了。

接下来我们再说说它的缺点,其实也很明显,它的分类规则太过于简单。所以它最大的缺点就是因为简单,所以好解释,但正是因为简单,所以解决不了复杂问题。

和SVM一样,也可以给决策树加核函数,曾经有一段时间这也是很重要的一个研究领域。

好,在结束之前我们预告一下下一节课内容,我们还是讲决策树,讲讲决策树中的Adaboost等,以及决策树的升级版:随机森林。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

合肥中科深谷嵌入式项目实战——人工智能与机械臂(六)

订阅:新手可以订阅我的其他专栏。免费阶段订阅量1000 python项目实战 Python编程基础教程系列(零基础小白搬砖逆袭) 说明:本专栏持续更新中,订阅本专栏前必读关于专栏〖Python网络爬虫实战〗转为付费专栏的订阅说明作者&#xff1…

Python最强自动化神器Playwright!再也不用为爬虫逆向担忧了!

版权说明:本文禁止抄袭、转载,侵权必究! 目录 一、简介+使用场景二、环境部署(准备)三、代码生成器(优势)四、元素定位器(核心)五、追踪查看器(辅助)六、权限控制与认证(高级)七、其他重要功能(进阶)八、作者Info一、简介+使用场景 Playwright是什么?来自Chat…

0001Java安卓程序设计-基于Android多餐厅点餐桌号后厨前台服务设计与开发

文章目录 **摘** **要****目** **录**系统设计开发环境 编程技术交流、源码分享、模板分享、网课教程 🐧裙:776871563 摘 要 移动互联网时代的到来,给人们的生活带来了许多便捷和乐趣。随着用户的不断增多,其规模越来越大&#…

linux环境下编译,安卓平台使用的luajit库

一、下载luajit源码 1、linux下直接下载: a、使用curl下载:https://luajit.org/download/LuaJIT-2.1.0-beta3.tar.gz b、git下载地址;https://github.com/LuaJIT/LuaJIT.git 2、Windows下载好zip文件,下载地址:https…

【四、http】go的http的文件下载

一、日常下载图片到本地 //下载文件func downloadfile(url, filename string) {r, err : http.Get(url)if err ! nil {fmt.Println("err", err.Error())}defer r.Body.Close()f, err : os.Create(filename)if err ! nil {fmt.Println("err", err.Error())…

一文详解:传统企业如何把进销存管理流程搬到线上?

进销存管理是企业管理的核心流程之一,它有助于提高效率、降低成本、增加盈利,同时确保客户满意度,这对于企业的长期成功和竞争力至关重要。但在信息化转型的浪潮下,很多企业的传统进销存流程却遇到不少问题。 如果你也在考虑把进…

Navicat连接mysql 8.0.35 2059错误解决办法

这2天在家重装电脑,顺便把mysql升级8.0,安装完成后,用Navicat连接,报错2059,如下 网上查了一下, 【报错原因】mysql8.0 之前的版本中加密规则是 mysql_native_password,而 mysql8.0 之后的版本…

随机微分方程的分数扩散模型 (score-based diffusion model) 代码示例

随机微分方程的分数扩散模型(Score-Based Generative Modeling through Stochastic Differential Equations) 基于分数的扩散模型,是估计数据分布梯度的方法,可以在不需要对抗训练的基础上,生成与GAN一样高质量的图片。…

【Kotlin精简】第7章 泛型

1 泛型 泛型即 “参数化类型”,将类型参数化,可以用在类,接口,函数上。与 Java 一样,Kotlin 也提供泛型,为类型安全提供保证,消除类型强转的烦恼。 1.1 泛型优点 类型安全:通用允许…

CoDeSys系列-4、基于Ubuntu的codesys运行时扩展包搭建Profinet主从环境

CoDeSys系列-4、基于Ubuntu的codesys运行时扩展包搭建Profinet主从环境 文章目录 CoDeSys系列-4、基于Ubuntu的codesys运行时扩展包搭建Profinet主从环境一、前言二、资料收集三、Ubuntu18.04从安装到更换实时内核1、下载安装Ubuntu18.042、下载安装实时内核,解决编…

如何将PDF文件转换成翻页电子书?这个网站告诉你

​随着电子书的普及,越来越多的人开始将PDF文件转换成翻页电子书。翻页电子书不仅方便阅读,而且还可以在手机上轻松翻页。那么如何将PDF文件转换成翻页电子书呢?今天就为大家介绍一个网站,可以帮助你轻松完成这个任务。 1.首先&am…

Proteus仿真--12864LCD显示计算器键盘按键实验(仿真文件+程序)

本文主要介绍基于51单片机的12864LCD液晶显示电话拨号键盘按键实验(完整仿真源文件及代码见文末链接) 仿真图如下 本设计主要介绍计算器键盘仿真,按键按下后在12864液晶上显示对应按键键值 仿真运行视频 Proteus仿真--12864LCD显示计算器…

【漏洞复现】IIS_7.o7.5解析漏洞

感谢互联网提供分享知识与智慧,在法治的社会里,请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描3、漏洞验证 1.5、修复建议 1.1、漏洞描述 漏洞原理: cgi.fix_path1 1.png/.php该…

第九章《搞懂算法:决策树是怎么回事》笔记

决策树算法是机器学习中很经典的一个算法,它既可以作为分类算法,也可以作为回归算法。 9.1 典型的决策树是什么样的 决策树算法是依据“分而治之”的思想,每次根据某属性的值对样本进行分类,然后传递给下个属性继续进行分类判断…

项目实战:新增@Controller和@Service@Repository@Autowire四个注解

1、Controller package com.csdn.mymvc.annotation; import java.lang.annotation.*; Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Inherited public interface Controller { }2、Service package com.csdn.mymvc.annotation; import java.lang.annotation.*…

zookeeper节点类型

节点类型 持久节点(Persistent Nodes) 这些是Zookeeper中最常见的一种节点类型,当创建一个持久类型节点时,该值会一直存在zookeeper中,直到被显式删除或被新值覆盖。 临时节点(Ephemeral Nodes&#xff…

【漏洞复现】Apache_Tomcat_PUT方法任意写文件(CVE-2017-12615)

感谢互联网提供分享知识与智慧,在法治的社会里,请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描3、漏洞验证工具扫描验证POC 1.6、修复建议 说明内容漏洞编号CVE-2017-12615漏洞名称Tomcat_PU…

【python】路径管理+路径拼接问题

路径管理 问题相对路径问题绝对路径问题 解决os库pathlib库最终解决 问题 环境:python3.7.16 win10 相对路径问题 因为python的执行特殊性,使用相对路径时,在不同路径下用python指令会有不同的索引效果(python的项目根目录根据执…

服务器搭建:从零开始创建自己的Spring Boot应用【含登录、注册功能】

当然,你可以先按照IDEA搭建SSM框架【配置类、新手向】完成基础框架的搭建 步骤 1:设计并实现服务器端的用户数据库 在这个示例中,我们将使用MySQL数据库。首先,你需要安装MySQL并创建一个数据库以存储用户信息。以下是一些基本步…

5.3有效的括号(LC20-E)

算法: 题目中:左括号必须以正确的顺序闭合。意思是,最后出现的左括号(对应着栈中的最后一个元素),应该先找到对应的闭合符号(右括号) 比如:s"( [ ) ]"就是False&#xf…