机器学习——编程实现从零构造训练集的决策树

自己搭建一棵决策树【长文预警】

忙了一个周末就写到了“构建决策树”这一步,还没有考虑划分测试集、验证集、“缺失值、连续值”,预剪枝、后剪枝的部分,后面再补吧(挖坑)

第二节内容:验证集划分\k折交叉检验 机器学习——编程从零实现决策树【二】-CSDN博客

目录

1、信息

1)基本算法过程

2)信息熵和信息增益的计算方式

2、做点假设,简化运算

3、拆解算法过程

0)结点类

1)同类样本判断

2)数据集能否再拆解

3)选取最优属性

步骤1

步骤2:

步骤3:

步骤4: 

 步骤5:

4)构造新结点

4、完整的结点类代码

5、完整的构造树的过程

6、建树

1)准备数据集

2)建树

7、绘图查看树的结构

1)绘图代码

2)结果

3)预测


完整的代码指路

DrawPixel/decisionTree.ipynb at main · ndsoi/DrawPixel (github.com)

1、信息

1)基本算法过程

2)信息熵和信息增益的计算方式

2、做点假设,简化运算

 ① 为了选择最优的属性进行划分,我们需要计算信息增益,而计算信息增益需要用到信息:

        1、选取的属性attr有多少种取值?

                (用西瓜分类的例子,考虑属性”纹理“,就有3种取值——”清晰“、”稍糊“和”模糊“)

        2、每种取值有哪些数据?这些数据中有多少是A类别的,又有多少是B类别的..?

                比如对原始数据集考虑”纹理=清晰“的数据,那么有7个是好瓜,有2个是坏瓜

② 计算完信息增益之后,我们选信息增益最大的属性,按照这个属性划分数据集,生成子结点

注意这里的划分数据集,事实上我们在完成①.2问题的时候就已经“划分了”一次数据集,只是我们没有记录下来,类似这样的“冗余”计算有很多,为了尽量减少“重复”计算,我重规划算法的步骤如下:

1、设总共有class_num个类别,假设我们初始化结点node的时候就知道了这个数据集的如下信息:

数据集        self.data

属性集        self.attr

该数据集内样本数量最多的类别        self.max

该数据集内每个类别的样本数量        self.cal_class 是一个列表,每一个元素是|Dv|

2、基于假设1: 

计算Ent(D):

def Ent(D,cal_class):sum = len(D) # 样本总数# 求占比re = 0for k in cal_class:pk_class = k/sumif pk_class != 0:re -= pk_class* math.log(pk_class,2)return re

3、拆解算法过程

0)结点类

class Node():def __init__(self,D,A,max,cal_class,class_num):self.data = Dself.attr = Aself.class_num = class_numself.cal_class = cal_classself.max = maxself.label = 0 # 0表示非叶结点 1表示叶结点self.Class = 0 # 默认一个self.flag = "init"

1)同类样本判断

若要判断D中的样本是否同属于一个类别:只需要判断self.max的数量是否等于class_num

  def isSameClass(self):if self.cal_class[self.max] == len(self.data):return Truereturn False

2)数据集能否再拆解

若D中样本不属于同一类,那么接下来要看D中的样本是否还能再分解:

def isNoAttr(self):# 属性集为空if self.attr == None or self.attr==[]:return True,[]# 存储取值不同的属性self.Attr_Div = []for a in self.attr:a1 = self.data[0][a]for d in self.data:if d[a] != a1:self.Attr_Div.append(a)break# 无可分的属性if self.Attr_Div == []:return True,[]return False,self.Attr_Div

3)选取最优属性

从2)中获取了当前node数据集进一步可以分解的属性范围(self.Attr_Div),对于self.Attr_Div中的每一个attr,我们需要做的事情还有:

1. 找出属性attr的所有取值

2. 按照attr的不同取值将self.data划分成互斥的子集Dv_{attr=a1},Dv_{attr_{a_2}}...Dv_{a_n} 简称为Dv

3. 计算|Dv|和 Ent(Dv) 

4. 计算出attr的Gain

5. 重复步骤2-4 计算出所有attr的Gain, 选出Gain最大的attr

步骤1

# 属性attr的取值大全
def attrAllvalue(D,attr):Allvalue = {}for d in D:Allvalue[d[attr]] = 0return Allvalue

步骤2:

def divDataByattr(D,attr):# 建立一个字典,key是attr的取值,已初始化数值为0re = attrAllvalue(D,attr)n = len(re) # 要划分出n个子数据集SubDataSets = {}for key,value in re.items():SubDataSets[key] = []for d in D:SubDataSets[d[attr]].append(d)return SubDataSets

divDataByattr获得形如: {'清晰':[数据1,数据2],'模糊':[数据3],'稍糊':[数据4]} 的字典

步骤3:

为了计算Ent(Dv)我们需要获得Dv的cal_class,下列函数计算了数据集子集Dv的max和cal_class

# 获取maxnumClass
def calMaxClass(D,class_num):# 统计数据集D中各类样本的数目cal_class = [0 for i in range(class_num)]max = 0for d in D:cal_class[d['Class']]+=1if cal_class[d['Class']] > cal_class[max]:max = d['Class']return max,cal_class

步骤4: 

确定一个attr,划分出子集的集合,遍历子集集合,然后调用Ent函数,组合计算(加粗部分就是Gain函数所做的事情)

# 信息增益
def Gain(D,attr,class_info):max,cal_class = calMaxClass(D,class_num)EntD = Ent(D,cal_class)SubDataSets = divDataByattr(D,attr)EntDv = 0for value,Dv in SubDataSets.items():# cal_classmax,cal_class=calMaxClass(Dv,class_num)class_info.append([max,cal_class])EntDv +=len(Dv)/len(D)*Ent(Dv,cal_class)Gain_D_attr = EntD-EntDvreturn Gain_D_attr

补充:这里的class_info就是记录下每一个Dv的max和cal_class,用于后续传参给node 初始化

 步骤5:

def choseAttr(D,attrSet):compar = 0Gain_D = {}for attr in attrSet:SubDataSets = divDataByattr(D,attr)EntDv = 0# 补充上Dv额外的参数class_info = []Gain_D_attr = Gain(D,attr,class_info)# 记录数据集D用属性attr做划分时所有的已知信息,包括gain,数据子集,数据子集的class_num和max类Gain_D[attr] = {'gain':Gain_D_attr,'Dv':SubDataSets,'Dv_info':class_info}# 找gain最高的attrtarget = attrSet[0]for attr in attrSet:if Gain_D[attr]['gain'] > compar:compar = Gain_D[attr]['gain']target = attrreturn target,Gain_D

4)构造新结点

在完成3)的步骤5后,应该为选定的attr划分的子集生成新结点,新结点

# 选取最优属性attr,info = node.bestAttr()# 获取划分好的数据集SubDataSets = info[attr]['Dv']SubInfo = info[attr]['Dv_info']# 生成子nodeAttr = copy.deepcopy(Attr_Div)Attr.remove(attr)st = 0for value,subds in SubDataSets.items():# 因为假设是离散属性,所以新的self.attr必然要去掉已经选出的attrsubnodeAttr = copy.deepcopy(Attr)# 获取已经算好的Dv的max和cal_classsubmax = SubInfo[st][0]subcal_class = SubInfo[st][1]st+=1# 生成新结点subnode = Node(subds,subnodeAttr,submax,subcal_class,class_num)subnode.setflag(attr)# 新结点还要继续加入tree进行讨论tree.put(subnode)# 父结点记录子结点的指引node.addsubDs(subnode,value)

4、完整的结点类代码

# 说明:
# 设数据集是[{},{},{},...,{}]的格式
# {}的格式是{'attr1':'value1,'attr2':'value2',..,'label':'class'}
# label是结点node:表明其为叶节点还是非叶节点
# Class 是当node为叶结点时,该集合的类别
#
# 类别的数量
class_num = 2
class Node():def __init__(self,D,A,max,cal_class,class_num):self.data = Dself.attr = Aself.class_num = class_numself.cal_class = cal_classself.max = maxself.label = 0 # 0表示非叶结点 1表示叶结点self.Class = 0 # 默认一个self.flag = "init"def isSameClass(self):if self.cal_class[self.max] == len(self.data):return Truereturn Falsedef isNoAttr(self):# 属性集为空if self.attr == None or self.attr==[]:return True,[]# 存储取值不同的属性self.Attr_Div = []for a in self.attr:a1 = self.data[0][a]for d in self.data:if d[a] != a1:self.Attr_Div.append(a)break# 无可分的属性if self.Attr_Div == []:return True,[]return False,self.Attr_Div# 计算选取最优划分属性def bestAttr(self):# 指向划分的子结点self.subDs = {}self.bestattr,self.Gain_D = choseAttr(self.data,self.Attr_Div)return self.bestattr,self.Gain_Ddef setflag(self,attr):self.flag = attr# 设置subDsdef addsubDs(self,node,value):self.subDs[value] = node

5、完整的构造树的过程

import copy
import queuedef do_tree(tree):node = tree.get()print(node.data)# 判断D中的类别是不是都是一类re = node.isSameClass()if re:print("当前node都属于同一类别")# 如果D中的数据都属于同一个类别node.Class = node.maxnode.label = 1 # 标记为叶子结点return# D中的数据并不属于同一个类别# 判断属性是否可分boolre,Attr_Div = node.isNoAttr()print(f"Attr_Div={Attr_Div}")# D中的属性不可再分if boolre == True:print("当前类别属性不可再分")node.label = 1node.Class = node.maxreturn# 选取最优属性attr,info = node.bestAttr()# 获取划分好的数据集SubDataSets = info[attr]['Dv']SubInfo = info[attr]['Dv_info']# 生成子nodeAttr = copy.deepcopy(Attr_Div)Attr.remove(attr)st = 0for value,subds in SubDataSets.items():# 因为假设是离散属性,所以新的self.attr必然要去掉已经选出的attrsubnodeAttr = copy.deepcopy(Attr)# 获取已经算好的Dv的max和cal_classsubmax = SubInfo[st][0]subcal_class = SubInfo[st][1]st+=1# 生成新结点subnode = Node(subds,subnodeAttr,submax,subcal_class,class_num)subnode.setflag(attr)# 新结点还要继续加入tree进行讨论tree.put(subnode)# 父结点记录子结点的指引node.addsubDs(subnode,value)def TreeGenerate(D,A):# 计算初始数据集的max和cal_classmax,cal_class = calMaxClass(D,class_num)# 生成根结点node = Node(D,A,max,cal_class,class_num)tree = queue.Queue()tree.put(node)while tree.empty() == False:do_tree(tree)return node

6、建树

1)准备数据集


dataSet = [# 1['青绿', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'],# 2['乌黑', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '好瓜'],# 3['乌黑', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'],# 4['青绿', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '好瓜'],# 5['浅白', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'],# 6['青绿', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '好瓜'],# 7['乌黑', '稍蜷', '浊响', '稍糊', '稍凹', '软粘', '好瓜'],# 8['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '硬滑', '好瓜'],# ----------------------------------------------------# 9['乌黑', '稍蜷', '沉闷', '稍糊', '稍凹', '硬滑', '坏瓜'],# 10['青绿', '硬挺', '清脆', '清晰', '平坦', '软粘', '坏瓜'],# 11['浅白', '硬挺', '清脆', '模糊', '平坦', '硬滑', '坏瓜'],# 12['浅白', '蜷缩', '浊响', '模糊', '平坦', '软粘', '坏瓜'],# 13['青绿', '稍蜷', '浊响', '稍糊', '凹陷', '硬滑', '坏瓜'],# 14['浅白', '稍蜷', '沉闷', '稍糊', '凹陷', '硬滑', '坏瓜'],# 15['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '坏瓜'],# 16['浅白', '蜷缩', '浊响', '模糊', '平坦', '硬滑', '坏瓜'],# 17['青绿', '蜷缩', '沉闷', '稍糊', '稍凹', '硬滑', '坏瓜']]
Attr = ['色泽', '根蒂', '敲击', '纹理', '脐部', '触感']# 硬编码类别
class_dict = {'坏瓜':0,'好瓜':1}# 将数据合并格式
D = []
for i in range(len(dataSet)):d = {}for j in range(len(Attr)):d[Attr[j]] = dataSet[i][j]d['Class'] = class_dict[dataSet[i][-1]]D.append(d)print(D)

2)建树

root = TreeGenerate(D,Attr)

7、绘图查看树的结构

1)绘图代码

只是打印每层的结点,通过分支数目得知父子结点的关系

cur = root
# 表示区分的属性q = queue.Queue()
q.put(cur)
while q.empty()==False:# 这层的宽度width = q.qsize()for i in range(width):# 用/**/包住一个nodeprint(" /*",end="")cur = q.get()if cur.label == 1:# 叶子结点print(f"叶子:{cur.Class,cur.flag,cur.data[0][cur.flag]}",end="")else:l = len(cur.subDs)print(f"被分类依据:{cur.flag}",end="")if cur.flag != "init":print(f"值:{cur.data[0][cur.flag]}",end="  ")print(f",分支:{l}个",end="")for key,nod in cur.subDs.items():q.put(nod)print("*/ ",end="")print("")

2)结果

手绘还原:

3)预测

投入一个样本,返回好瓜/坏瓜判断

def predict(data,root):cur = rootwhile cur.label != 1:attr = cur.bestattrcur = cur.subDs[data[attr]]return cur.Classfor d in D:pd_label = predict(d,root)if pd_label == 0:print("坏瓜")else:print("好瓜")

结果打印8行好瓜,9行坏瓜

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

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

相关文章

python爬虫之xpath入门

文章目录 一、前言参考文档: 二、xpath语法-基础语法常用路径表达式举例说明 三、xpath语法-谓语表达式举例注意 四、xpath语法-通配符语法实例 五、选取多个路径实例 六、Xpath Helper安装使用说明例子: 七、python中 xpath 的使用安装xpath 的依赖包xm…

基于yolov2深度学习网络的人脸检测matlab仿真,图像来自UMass数据集

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 网络架构与特征提取 4.2 输出表示 4.3损失函数设计 4.4预测阶段 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 load yolov2.mat% 加载…

爱注讲台三尺案不辞长作“育花”人

—记邵阳市优秀班主任、新宁县优秀教师周芳平 教育是人与人心灵上最美妙的接触,只要用心体察,用情关注,每一位学生都会走向金光大道。 ---题记 “亲爱的妈妈,祝您节日快乐!”2024年3月8日,一条从深圳华为…

28-3 文件上传漏洞 -白盒审计绕过

环境准备:构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客 一、upload-labs 靶场的第7关 先进行代码审计 $is_upload = false; $msg = null; if (isset($_POST[submit])) {if (file_exists($UPLOAD_ADDR)) {$deny_ext = array(".php&…

Spring Boot:筑基

Spring Boot 前言概述使用 Intellij idea 快速创建 Spring Boot 项目注意事项 前言 在学习 Spring 、SpringMVC 、MyBatis 和 JPA 框架的过程中,了解到 SSM 框架为 Java Web 开发提供了强大的后端支持,JPA 框架则简化了数据库的操作。然而,S…

Cesium:按行列绘制3DTiles的等分线

作者:CSDN @ _乐多_ 本文将介绍如何使用 Cesium 引擎根据模型的中心坐标,半轴信息,绘制 3DTiles 对象的外包盒等分线。 外包盒是一个定向包围盒(Oriented Bounding Box),它由一个中心点(center)和一个包含半轴(halfAxes)组成。半轴由一个3x3的矩阵表示,这个矩阵是…

算法第三十一天-区域和检索【数组不可变】

区域和检索-数组不可变 题目要求 解题思路 为方便描述,把 n u m s nums nums 记作 a a a。 对于数组 a a a,定义它的前缀和 s [ 0 ] 0 s [ 1 ] a [ 0 ] s [ 2 ] a [ 0 ] a [ 1 ] ⋮ s [ i ] a [ 0 ] a [ 1 ] ⋯ a [ i − 1 ] ∑ j 0 i −…

x86 32 64 Arm这些听过但不懂,都是什么?是架构还是系统?一文梳理

x86 听过吗?64位操作系统知道吧 和x86什么关系32和64都是什么东西?曾经的我也一头雾水,今天我才来整理一下,惭愧惭愧!今天带着沉重的心情来梳理一下学习内容吧 如果你很熟悉很了解计算机的话,应该知道&…

深度分析:社科赛斯——穿越市场周期二十二年的考研机构

近日,一份由有关部门发布的统计数据引发了广泛关注:在中国,中小企业的平均寿命仅有3.7年,而小微企业更是不到3年。这一数字凸显了中小企业所面临的挑战与困境。然而,在这个充满风险与变化的商业环境中,社科…

中霖教育:二级建造师证书好考吗?

在建筑行业,二级建造师资格认证相较于一级建造师资格,难度会低一些。考试科目共有三科,考生需要在连续两个年度内通过所有科目的考试才为通过。 对于具备建筑相关基础和实践经验的考生来说,二级建造师的考试难度会低一些。根据往…

30天拿下Rust之错误处理

概述 在软件开发领域,对错误的妥善处理是保证程序稳定性和健壮性的重要环节。Rust作为一种系统级编程语言,以其对内存安全和所有权的独特设计而著称,其错误处理机制同样体现了Rust的严谨与实用。在Rust中,错误处理通常分为两大类&…

KUKA机器人自动回原点程序

一、创建全局变量点 创建两个全局变量分别用于储存机器人的笛卡尔姿态与关节角姿态。 打开System文件夹中的config文件创建全局变量的点位。 在USER GROBALS用户自定义变量Userdefined variables下创建一个E6POS类型的点位,一个E6AXIS类型的点位。 二、创建回原点…

基于SpringBoot+Vue交通管理在线服务系统的开发(源码+部署说明+演示视频+源码介绍)

您好,我是码农飞哥(wei158556),感谢您阅读本文,欢迎一键三连哦。💪🏻 1. Python基础专栏,基础知识一网打尽,9.9元买不了吃亏,买不了上当。 Python从入门到精通…

React状态管理Mobx

1 https://zh.mobx.js.org/README.html 2 https://juejin.cn/post/7046710251382374413 3 https://cn.mobx.js.org/refguide/observable.html ​​mobx入门基础教程-慕课网​​ ​​Mobx学习 - 掘金​​ 十分钟入门 MobX & React ​​十分钟入门 MobX & React​​…

警惕!On Hold被踢,2本1区,5本Springer旗下,共8本SCI/SSCI被剔除!

毕业推荐 SSCI(ABS一星) • 社科类,3.0-4.0,JCR2区,中科院3区 • 13天录用,28天见刊,13天检索 SCIE: • 计算机类,6.5-7.0,JCR1区,中科院2区…

农业气象站在农业生产中的应用—气象科普

农业气象站在农业生产中发挥着至关重要的作用。它能够有效监测和记录农田环境中的各类气象要素,为农民提供科学、准确的气象数据,帮助他们更好地掌握天气变化规律,从而合理安排农业生产活动。 首先,农业气象站能够实时提供温度、…

使用 Clojure 进行 OpenCV 开发简介

返回:OpenCV系列文章目录(持续更新中......) 上一篇:如何将OpenCV Java 与Eclipse结合使用 下一篇: OpenCV4.9.0在Android 开发简介 ​警告 本教程可以包含过时的信息。 从 OpenCV 2.4.4 开始,OpenCV 支持…

挑战设计极限!电路仿真软件成功案例大揭秘,助您圆梦创新之路

在电子设计领域,电路仿真软件扮演着至关重要的角色。它们不仅能够帮助工程师们模拟和分析电路的性能,还能够加速设计过程,降低成本,提高产品的质量和可靠性。今天,让我们一起挑战设计极限,揭秘电路仿真软件…

Java基础---反射

什么是反射? 反射允许对成员变量,成员方法和构造方法的信息进行编程访问。 这么说可能比较抽象,可以简单理解为:反射就是一个人,可以把类里面的成员变量,成员方法,构造方法都获取出来。 并且可…

Springcloud智慧工地APP云综合平台源码 SaaS服务

目录 智慧工地功能介绍 一、项目人员 二、视频监控 三、危大工程 四、绿色施工 五、安全隐患 具体功能介绍: 1.劳务管理: 2.施工安全管理: 3.视频监控管理: 4.机械安全管理: 5.危大工程监管: …