基于weka手工实现ID3决策树

一、决策树ID3算法

相比于logistic回归、BP网络、支持向量机等基于超平面的方法,决策树更像一种算法,里面的数学原理并不是很多,较好理解。

决策树就是一个不断地属性选择、属性划分地过程,直到满足某一情况就停止划分。

  1. 当前样本全部属于同一类别了(信息增益为0);
  2. 已经是空叶子了(没有样本了);
  3. 当前叶子节点所有样本所有属性上取值相同,无法划分了(信息增益为0)。

信息增益如何计算?根据信息熵地变化量,信息熵减少最大地属性就是我们要选择地属性。

信息熵定义:

E n t ( D ) = − ∑ k = 1 ∣ y ∣ p k l o g 2 p k Ent(D)=-\sum_{k=1}^{|y|}p_klog_2p_k Ent(D)=k=1ypklog2pk

信息增益定义:

G a i n ( D , a ) = E n t ( D ) − ∑ v = 1 v ∣ D v ∣ ∣ D ∣ E n t ( D v ) Gain(D,a)=Ent(D)-\sum_{v=1}^v\frac{|D^v|}{|D|}Ent(D^v) Gain(D,a)=Ent(D)v=1vDDvEnt(Dv)

信息增益越大,则意味着属性a来划分所获得的“纯度提升”越大。

ID3就是以信息增益作为属性选择和划分的标准的。有了决策树生长和停止生长的条件,剩下的其实就是一些编程技巧了,我们就可以进行编码了。

除此之外,决策树还有C4.5等其它实现的算法,包括基尼系数、增益率、剪枝、预剪枝等防止过拟合的方法,但决策树最本质、朴素的思想还是在ID3中体现的最好。

具体可以参考这篇博客:机器学习06:决策树学习.

二、基于weka平台实现ID3决策树

package weka.classifiers.myf;import weka.classifiers.Classifier;
import weka.core.*;/*** @author YFMan* @Description 自定义的 ID3 分类器* @Date 2023/5/25 18:07*/
public class myId3 extends Classifier {// 当前节点 的 后续节点private myId3[] m_Successors;// 当前节点的划分属性 (如果为空,说明当前节点是叶子节点;否则,说明当前节点是中间节点)private Attribute m_Attribute;// 当前节点的类别分布 (如果为中间节点,全为 0;为叶子节点,为类别分布)private double[] m_Distribution;// 当前节点的类别 (如果为中间节点,为 0;为叶子节点,为类别分布)// (用于获取类别的索引,对于算法本身没用,但对于可视化 决策树有用)private double m_ClassValue;// 当前节点的类别属性 (如果为中间节点,为 null;为叶子节点,为类别属性)// (用于获取类别的名称,对于算法本身没用,但对于可视化 决策树有用)private Attribute m_ClassAttribute;/** @Author YFMan* @Description 根据训练数据 建立 决策树* @Date 2023/5/25 18:43* @Param [data]* @return void**/public void buildClassifier(Instances data) throws Exception {// 建树makeTree(data);}/** @Author YFMan* @Description 根据训练数据 建立 决策树* @Date 2023/5/25 18:43* @Param [data] 训练数据* @return void**/private void makeTree(Instances data) throws Exception {// 如果是空叶子,拒绝建树 (拒判)if (data.numInstances() == 0) {m_Attribute = null;m_ClassValue = Instance.missingValue();m_Distribution = new double[data.numClasses()];return;}// 计算 所有属性的 信息增益double[] infoGains = new double[data.numAttributes()];// 遍历所有属性for(int i = 0; i < data.numAttributes(); i++) {// 如果是类别属性,跳过if (i == data.classIndex()) {infoGains[i] = 0;} else {// 计算信息增益infoGains[i] = computeInfoGain(data, data.attribute(i));}}// 选择信息增益最大的属性m_Attribute = data.attribute(Utils.maxIndex(infoGains));// 如果信息增益为 0,说明当前节点包含的样例都属于同一类别,直接设置为叶子节点if (Utils.eq(infoGains[m_Attribute.index()], 0)) {// 设置为叶子节点m_Attribute = null;m_Distribution = new double[data.numClasses()];// 遍历所有样例for (int i = 0; i < data.numInstances(); i++) {// 获取当前样例的类别Instance inst = data.instance(i);// 统计类别分布m_Distribution[(int) inst.classValue()]++;}// 归一化Utils.normalize(m_Distribution);// 设置类别m_ClassValue = Utils.maxIndex(m_Distribution);m_ClassAttribute = data.classAttribute();} else { // 否则,递归建树// 划分数据集Instances[] splitData = splitData(data, m_Attribute);// 创建叶子m_Successors = new myId3[m_Attribute.numValues()];// 叶子再去长叶子,递归调用for (int j = 0; j < m_Attribute.numValues(); j++) {m_Successors[j] = new myId3();m_Successors[j].makeTree(splitData[j]);}}}/** @Author YFMan* @Description 根据 instance 进行分类* @Date 2023/5/25 18:33* @Param [instance] 待分类的实例* @return double[] 类别分布**/public double[] distributionForInstance(Instance instance)throws NoSupportForMissingValuesException {// 如果到达叶子节点,返回类别分布if (m_Attribute == null) {// 如果 m_Distribution 全为 0(是空叶子),随机返回一个类别分布if (Utils.eq(Utils.sum(m_Distribution), 0)) {// 在 0~类别数-1 之间随机选择一个类别m_Distribution = new double[m_ClassAttribute.numValues()];m_Distribution[(int) Math.round(Math.random() * m_ClassAttribute.numValues())] = 1.0;}return m_Distribution;} else {// 否则,递归调用return m_Successors[(int) instance.value(m_Attribute)].distributionForInstance(instance);}}/** @Author YFMan* @Description 计算当前数据集 选择某个属性的 信息增益* @Date 2023/5/25 18:29* @Param [data, att] 当前数据集,选择的属性* @return double 信息增益**/private double computeInfoGain(Instances data, Attribute att)throws Exception {// 计算 data 的信息熵double infoGain = computeEntropy(data);// 计算 data 按照 att 属性进行划分的信息熵// 划分数据集Instances[] splitData = splitData(data, att);// 遍历划分后的数据集for (Instances instances : splitData) {// 计算概率double probability = (double) instances.numInstances() / data.numInstances();// 计算信息熵infoGain -= probability * computeEntropy(instances);}// 返回信息增益return infoGain;}/** @Author YFMan* @Description 计算信息熵* @Date 2023/5/25 18:18* @Param [data] 计算的数据集* @return double 信息熵**/private double computeEntropy(Instances data) throws Exception {// 计不同类别的数量double[] classCounts = new double[data.numClasses()];// 遍历数据集for(int i=0;i<data.numInstances();i++){// 获取类别int classIndex = (int) data.instance(i).classValue();// 数量加一classCounts[classIndex]++;}// 计算信息熵double entropy = 0;// 遍历类别for (double classCount : classCounts) {// 注意:这里是大于 0,因为 log2(0) = -Infinity;// 如果是等于 0,那么计算结果就是 NaN,熵就出错了if(classCount > 0){// 计算概率double probability = classCount / data.numInstances();// 计算信息熵entropy -= probability * Utils.log2(probability);}}// 返回信息熵return entropy;}/** @Author YFMan* @Description 根据属性划分数据集* @Date 2023/5/25 18:23* @Param [data, att] 数据集,属性* @return weka.core.Instances[] 划分后的数据集**/private Instances[] splitData(Instances data, Attribute att) {// 定义划分后的数据集Instances[] splitData = new Instances[att.numValues()];// 遍历划分后的数据集for(int i=0;i<splitData.length;i++){// 创建数据集 (这里主要是为了初始化 数据集 header)// Constructor copying all instances and references to the header// information from the given set of instances.splitData[i] = new Instances(data,0);}// 遍历数据集for(int i=0;i<data.numInstances();i++){// 获取实例Instance instance = data.instance(i);// 获取实例的属性值double value = instance.value(att);// 将实例添加到对应的数据集中splitData[(int) value].add(instance);}// 返回划分后的数据集return splitData;}private String toString(int level) {StringBuffer text = new StringBuffer();if (m_Attribute == null) {if (Instance.isMissingValue(m_ClassValue)) {text.append(": null");} else {text.append(": " + m_ClassAttribute.value((int) m_ClassValue));}} else {for (int j = 0; j < m_Attribute.numValues(); j++) {text.append("\n");for (int i = 0; i < level; i++) {text.append("|  ");}text.append(m_Attribute.name() + " = " + m_Attribute.value(j));text.append(m_Successors[j].toString(level + 1));}}return text.toString();}public String toString() {if ((m_Distribution == null) && (m_Successors == null)) {return "Id3: No model built yet.";}return "Id3\n\n" + toString(0);}/*** Main method.** @param args the options for the classifier*/public static void main(String[] args) {runClassifier(new myId3(), args);}
}

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

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

相关文章

【TypeScript】类型断言的基本使用

类型断言的概念 有些时候开发者比TS本身更清楚当前的类型是什么&#xff0c;可以使用断言&#xff08;as&#xff09;让类型更加精确和具体。 类型断言&#xff08;Type Assertion&#xff09;表示可以用来手动指定一个值的类型。 类型断言语法&#xff1a; 值 as 类型 或 <…

Springboot整合activiti5,达梦数据库,mybatis中间件

Springboot整合activiti5&#xff0c;达梦数据库&#xff0c;mybatis中间件 问题现象解决方案 问题现象 由于工作流引擎不支持达梦数据库以及国产中间件&#xff0c;所以我们引入的时候会报错&#xff0c;这个时候就需要去改造代码和配置文件。各种文档和资料查找一天&#xf…

4.2的幂次方表示

【题目】 任何一个正整数都可以用2进制表示&#xff0c;例如&#xff1a;137的2进制表示为10001001。 将这种2进制表示写成2的次幂的和的形式&#xff0c;令次幂高的排在前面&#xff0c;可得到如下表达式&#xff1a;137 2^7 2^3 2^0 现在约定幂次用括号来表示&#xff0…

Kafka3.0.0版本——生产者如何提高吞吐量

目录 一、生产者提高吞吐量参数设置二、产者提高吞吐量代码示例 一、生产者提高吞吐量参数设置 batch.size&#xff1a;设置批次大小&#xff0c;默认16klinger.ms&#xff1a;设置等待时间&#xff0c;修改为5-100msbuffer.memory&#xff1a;设置缓冲区大小&#xff0c; 默认…

Node.js-http模块服务端请求与响应操作,请求报文与响应报文

简单案例创建HTTP服务端&#xff1a; // 导入 http 模块 const http require("http"); // 创建服务对象 const server http.createServer((request, response) > {// 设置编码格式&#xff0c;解决中文乱码问题response.setHeader("content-type", &…

升级mybatis-plus到3.5.3.1和JSQLParser 从4.3升级到4.6版本引起的插入问题解决

由于项目组件升级&#xff0c;所以需要升级mybatis-plus到3.5.3.1和JSQLParser 从4.3升级到4.6版本&#xff0c;但发现用标准的插入也会报错&#xff0c;如下&#xff1a; ### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Failed to process, Erro…

tomcat限制IP访问

tomcat可以通过增加配置&#xff0c;来对来源ip进行限制&#xff0c;即只允许某些ip访问或禁止某些来源ip访问。 配置路径&#xff1a;server.xml 文件下 标签下。与同级 <Valve className"org.apache.catalina.valves.RemoteAddrValve" allow"192.168.x.x&…

python练习10-8,10-9

10-8 def count_words(filename):try:with open(filename) as f:cf.read()except FileNotFoundError:print(f"Sorry,the file {filename} did not exist.")else:print(c)filenames[cats.txt,dogs.txt] for filename in filenames:filename.hanshu() #记错了在类中…

特斯拉墨西哥工厂风波:2.5万美金的车型何时开造?

作者 | Amy 编辑 | 德新 去年10月&#xff0c;马斯克闪现墨西哥新莱昂州&#xff0c;会见了当地官员。考虑到新莱昂州是通用和现代工厂所在地&#xff0c;特斯拉第五大工厂花落墨西哥的消息不胫而走。 今年3月&#xff0c;特斯拉正式宣布&#xff0c;将在墨西哥北部新莱昂州的…

C++(15):面向对象程序设计

面向对象程序设计概述 面向对象程序设计&#xff08;object-oriented programming&#xff09;的核心思想是数据抽象、继承和动态绑定。 1.使用数据抽象&#xff0c;可以将类的接口与实现分离&#xff1b; 2.使用继承&#xff0c;可以定义相似的类型并对其相似关系建模&#x…

嵌入式:C高级 Day2

一、递归实现&#xff0c;输入一个数&#xff0c;输出这个数的每一位 二、递归实现&#xff0c;输入一个数字&#xff0c;输出这个数的二进制 三、写一个脚本&#xff0c;包含以下内容 1.显示/etc/group文件中第五行的内容 2.创建目录/home/ubuntu/copy 3.切换工作路径到此目录…

多雷达探测论文阅读笔记:雷达学报 2023, 多雷达协同探测技术研究进展:认知跟踪与资源调度算法

多雷达协同探测技术 原始笔记链接:https://mp.weixin.qq.com/s?__biz=Mzg4MjgxMjgyMg==&mid=2247486627&idx=1&sn=f32c31bfea98b85f2105254a4e64d210&chksm=cf51be5af826374c706f3c9dcd5392e0ed2a5fb31ab20924b7dd38e1b1ae32abe9a48afa8174#rd ↑ \uparrow …

【蓝图】p46角色上下车功能

这里写目录标题 p46角色上下车功能上车&#xff08;控制权切换&#xff09;让角色和汽车一起移动GetWorldTransform&#xff08;获取场景变换&#xff09;break&#xff08;拆分变换&#xff09;AttachActorToComponent&#xff08;附加Actor到组件&#xff09; 下车 p46角色上…

记录一次stopwatchThreadLocal为空的问题及解法

Component Slf4j public class ApiTimeMonitorInterceptor extends HandlerInterceptorAdapter {private ThreadLocal<Stopwatch> stopwatchThreadLocal new NamedThreadLocal<>("api_time_monitor");//外部使用public long getElapsedMs() {Stopwatch …

2023华数杯数学建模C题思路 - 母亲身心健康对婴儿成长的影响

# 1 赛题 C 题 母亲身心健康对婴儿成长的影响 母亲是婴儿生命中最重要的人之一&#xff0c;她不仅为婴儿提供营养物质和身体保护&#xff0c; 还为婴儿提供情感支持和安全感。母亲心理健康状态的不良状况&#xff0c;如抑郁、焦虑、 压力等&#xff0c;可能会对婴儿的认知、情…

【雕爷学编程】MicroPython动手做(28)——物联网之Yeelight 5

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

43. 字符串相乘(leetcode刷题记录)

字符串相乘 给定两个以字符串形式表示的非负整数 num1 和 num2&#xff0c;返回 num1 和 num2 的乘积&#xff0c;它们的乘积也表示为字符串形式。 注意&#xff1a;不能使用任何内置的 BigInteger 库或直接将输入转换为整数。 class Solution { public:string multiply(stri…

UniPro助力金融企业数字化转型 强化项目协作与跟踪

根据一份来自Standish Group的研究报告&#xff08;"CHAOS Report"&#xff09;&#xff0c;该报告对美国各行业的项目进行了调查&#xff0c;结果显示仅有不到一半&#xff08;约44%&#xff09;的项目能够成功按时完成&#xff0c;并达到预期的业务目标。其中&…

计算两条直线夹角(C++)

计算两条直线的锐角可以使用向量的知识来实现。在C中&#xff0c;我们可以定义一个函数来计算两个向量的夹角&#xff0c;并根据夹角的余弦值来判断角度的大小。以下是一个用C编写的示例代码&#xff1a; #include <iostream> #include <cmath>using namespace st…

c++调用ffmpeg api录屏 并进行rtmp推流

代码及工程见https://download.csdn.net/download/daqinzl/88156528 开发工具&#xff1a;visual studio 2019 记得启动rtmp流媒体服务 nginx的rtmp服务见https://download.csdn.net/download/daqinzl/20478812 播放&#xff0c;采用ffmpeg工具集里的ffplay.exe, 执行命令 f…