恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Matlab从零实现C4.5决策树:增益率计算与剪枝全流程

  • 首页
  • 资讯中心
  • /
  • Matlab从零实现C4.5决策树:增益率计算与剪枝全流程

相关资讯

CANN/ge张量形状范围设置 2026/9/10 17:31:15
皮肤病AI数据集构建:从皮肤镜采集到临床可信验证 2026/9/10 17:31:15
哈工大13015计算机系统原理大作业:单周期RISC-V处理器实现全攻略 2026/9/10 17:31:15

最新资讯

论文参考文献批量补录,按章节分批推进的节奏
职场资源利用与职业道德的边界探讨
JAVA计算机毕设之基于 SpringBoot 的基建项目信息管理系统的设计与实现 基于 Java SpringBoot 的工程项目管理系统(完整前后端代码+说明文档+LW,调试定制等)
PythonRobotics 扩展卡尔曼滤波(EKF)定位实战:从状态方程到速度尺度因子修正的完整推导
JAVA计算机毕设之基于 SpringBoot 的健身房运营管理平台的设计与实现 基于 SpringBoot 的健身教练与课程管理系统(完整前后端代码+说明文档+LW,调试定制等)
LeetCode 225 题解:用队列实现栈(Implement Stack using Queues)——Go 双队列实现与测试解析

今日推荐

AI搜索重构内容生态:企业从“流量争夺”转向“答案共建”
AI搜索的信任缺口:企业内容如何在答案时代自证可信
Spring Boot+Vue+Node.js售后服务系统开发实战

本周热门

超人会飞不算本事:系统稳定依赖清晰规则与边界设计
超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论
基于CNN的调制信号识别:MATLAB实现时频图分类实战

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

Matlab从零实现C4.5决策树:增益率计算与剪枝全流程

发布时间:2026/9/10 17:36:15
Matlab从零实现C4.5决策树:增益率计算与剪枝全流程 简介本资源是一份面向计算机、电子信息工程及数学等专业本科生的机器学习课程实践材料聚焦决策树C4.5算法原理与Matlab实现适用于课程设计、期末大作业或毕业设计参考。压缩包共14个文件含7个.mat数据文件如train_features.mat、test_targets.mat等承载训练/测试样本与标签、6个.m函数脚本包括主程序main.m、核心算法C4_5.m、分类误差计算classification_error.m等以及1份详尽的Word教程文档全面覆盖数据预处理、信息增益率计算、树构建与剪枝、分类预测全流程。资源仅45KB轻量紧凑结构清晰便于初学者理解算法逻辑并动手调试。已有2689人学习下载配套教程与可运行源码结合显著降低C4.5从理论到实践的门槛特别适合具备基础Matlab编程能力、需自主完成算法复现与结果分析的学习者。1. 用 Matlab 手撕 C4.5 决策树不是调用fitctree而是从熵、增益率、剪枝逻辑开始写透每行代码你打开一个.rar文件里面是c45_main.m、build_tree.m、calc_info_gain_ratio.m和wine.csv——这不是调用一行fitctree就完事的封装函数而是一套完整可调试、可打断点、可替换数据、可对比 ID3 的 C4.5 实现。它解决的不是“怎么画一棵树”而是“为什么分裂节点必须用增益率而非信息增益”“连续属性如何二值化切分”“缺失值怎么参与划分”“后剪枝时验证集误差怎么算”。适合三类人机器学习课设要交原创代码的学生、想搞清ClassificationTree底层逻辑的算法工程师、以及正在用 Matlab 做嵌入式模型部署如 Simulink HDL Coder需明确控制分支条件的开发者。本篇不依赖 Statistics and Machine Learning Toolbox 的黑盒函数所有熵计算、属性选择、树结构递归构建、预剪枝阈值设定、后剪枝回溯逻辑全部用原生 Matlab 数组和结构体实现且每步附可验证的中间输出。2. C4.5 核心机制拆解为什么增益率能防过拟合以及 Matlab 如何高效计算它C4.5 不是 ID3 的简单升级它针对 ID3 的致命缺陷——对取值多的属性如“身份证号”“时间戳”天然偏好——引入了**增益率Gain Ratio**作为分裂标准。Matlab 没有内置gainratio函数必须手动推导并避免浮点陷阱。我们先看数学本质再落地到向量化代码。2.1 增益率公式与 Matlab 向量化实现逻辑ID3 使用信息增益 $Gain(S,A) Info(S) - Info_A(S)$但当属性 $A$ 取值过多时$Info_A(S)$ 极小导致 $Gain$ 虚高。C4.5 改用增益率$$ GainRatio(S,A) \frac{Gain(S,A)}{SplitInfo(S,A)},\quad SplitInfo(S,A) -\sum_{v \in Values(A)} \frac{|S_v|}{|S|} \log_2 \frac{|S_v|}{|S|} $$关键点在于$SplitInfo$ 是属性 $A$ 自身的熵取值越分散$SplitInfo$ 越大从而压低增益率。Matlab 中不能直接用log2(0)必须加 epsilon 防错且Values(A)需用unique()提取但要注意浮点连续属性需先离散化。2.1.1 连续属性二值化切分中位数 vs. 最优切分点C4.5 对连续属性如alcohol列不直接枚举所有值而是排序后取相邻值中点作为候选切分点再选使增益率最大的那个。Matlab 实现如下function [best_split, best_gain_ratio] find_best_continuous_split(X_col, y, epsilon) % X_col: n×1 连续特征列, y: n×1 标签列 [sorted_X, idx] sort(X_col); sorted_y y(idx); % 生成候选切分点相邻值中点 candidates (sorted_X(1:end-1) sorted_X(2:end)) / 2; n_cand length(candidates); gain_ratios zeros(n_cand, 1); for i 1:n_cand % 按 candidate 划分左右子集 left_mask X_col candidates(i); right_mask ~left_mask; if sum(left_mask) 0 || sum(right_mask) 0 gain_ratios(i) -Inf; continue; end % 计算 Info(S), Info_A(S), SplitInfo(S,A) info_S calc_entropy(y); info_A (sum(left_mask)/numel(y)) * calc_entropy(y(left_mask)) ... (sum(right_mask)/numel(y)) * calc_entropy(y(right_mask)); gain info_S - info_A; % SplitInfo: 属性 A 的内在信息 p_left sum(left_mask) / numel(y); p_right 1 - p_left; split_info -(p_left * log2(max(p_left, epsilon)) p_right * log2(max(p_right, epsilon))); % 增益率防除零 gain_ratios(i) (split_info epsilon) ? gain / split_info : -Inf; end [~, best_idx] max(gain_ratios); best_split candidates(best_idx); best_gain_ratio gain_ratios(best_idx); end提示epsilon 1e-12必须显式传入否则log2(0)报Inf或NaN导致max()失效。calc_entropy函数需用histcounts统计类别频次再套用 $-\sum p_i \log_2 p_i$不可用entropy()那是图像处理工具箱的灰度熵。2.2 缺失值处理C4.5 的加权分配策略C4.5 不丢弃含缺失值的样本而是将该样本按比例分配给所有分支。例如某节点有 100 个样本其中 20 个age缺失其余 80 个中age≤30占 40 个、age30占 40 个则缺失的 20 个样本以 0.5:0.5 权重分到左右子节点。Matlab 实现时标签向量y需改为n×2矩阵第一列为真实标签第二列为权重初始全 1后续递归中权重会衰减。2.2.1 权重更新与信息熵修正% 在 build_tree.m 中当遇到缺失值时 if any(isnan(X_col)) % 计算非缺失样本的分布比例 valid_mask ~isnan(X_col); valid_X X_col(valid_mask); valid_y y(valid_mask, :); % y now has weight in column 2 % 获取最优切分仅用 valid 样本 [best_split, ~] find_best_continuous_split(valid_X, valid_y(:,1), eps); % 分配缺失样本按左右子集在 valid 样本中的占比 left_valid sum(valid_X best_split); right_valid sum(valid_X best_split); total_valid left_valid right_valid; left_weight_ratio left_valid / total_valid; right_weight_ratio right_valid / total_valid; % 构建带权重的子数据集 left_y [valid_y(valid_X best_split, :); ... repmat([mode_y, 1*left_weight_ratio], sum(isnan(X_col)), 1)]; right_y [valid_y(valid_X best_split, :); ... repmat([mode_y, 1*right_weight_ratio], sum(isnan(X_col)), 1)]; else % 正常切分 end注意mode_y是当前节点多数类标签用于缺失样本的伪标签填充。权重列第 2 列直接影响calc_entropy的计算——需改用加权熵$$Info(S) -\sum_{i} \frac{\sum_{x\in S_i} w_x}{\sum_{x\in S} w_x} \log_2 \frac{\sum_{x\in S_i} w_x}{\sum_{x\in S} w_x}$$因此calc_entropy必须接收权重向量不能只传标签。3. 从零构建决策树结构递归生成、节点存储与预剪枝控制C4.5 的树不是抽象概念而是 Matlab 结构体数组或自定义 class。我们采用struct存储每个节点字段包括feature_idx分裂属性索引、split_val切分值连续属性为数值离散为类别名、children子节点 struct 数组、class_label叶节点预测类、samples_count加权样本数。构建过程必须显式控制深度、最小样本数、增益率阈值。3.1 树节点结构定义与初始化function node init_node() node.feature_idx []; % 分裂属性列号如 3 表示第 3 列 node.split_val []; % 切分值离散属性为 cellstr连续为 scalar node.children {}; % 子节点 struct cell array node.class_label []; % 叶节点预测类别 node.samples_count 0; % 当前节点加权样本总数 node.is_leaf false; % 是否为叶节点 end3.2 递归建树主函数预剪枝三重闸门build_tree.m是核心它接收数据矩阵X、标签y含权重列、当前深度depth、最大深度max_depth、最小样本数min_samples_split、增益率阈值min_gain_ratio。每次递归前检查三个终止条件闸门判断逻辑Matlab 实现纯度达标所有样本属于同一类numel(unique(y(:,1))) 1深度超限depth max_depth直接设为叶节点取多数类样本不足sum(y(:,2)) min_samples_split同上用加权和而非行数function node build_tree(X, y, depth, max_depth, min_samples_split, min_gain_ratio, epsilon) node init_node(); node.samples_count sum(y(:,2)); % 加权样本总数 % 闸门1纯度检查 if numel(unique(y(:,1))) 1 node.is_leaf true; node.class_label y(1,1); return; end % 闸门2深度检查 if depth max_depth node.is_leaf true; [node.class_label, ~] mode(y(:,1), omitnan); % 加权 mode 需另行实现 return; end % 闸门3样本数检查 if node.samples_count min_samples_split node.is_leaf true; [node.class_label, ~] mode(y(:,1), omitnan); return; end % 寻找最优分裂属性 best_feat_idx -1; best_gain_ratio -Inf; best_split_val []; for feat_idx 1:size(X,2) X_col X(:,feat_idx); if isnumeric(X_col) ~all(isnan(X_col)) % 连续属性 [~, gr] find_best_continuous_split(X_col, y, epsilon); else % 离散属性枚举每个取值作为分支 gr calc_discrete_gain_ratio(X_col, y, epsilon); end if gr best_gain_ratio best_gain_ratio gr; best_feat_idx feat_idx; end end % 闸门4增益率不足强制设为叶节点 if best_gain_ratio min_gain_ratio node.is_leaf true; [node.class_label, ~] mode(y(:,1), omitnan); return; end % 执行分裂 node.feature_idx best_feat_idx; X_col X(:,best_feat_idx); if isnumeric(X_col) ~all(isnan(X_col)) node.split_val find_best_continuous_split(X_col, y, epsilon); % 按 split_val 划分左右子集... else % 离散属性对每个 unique value 构建子节点... end end提示min_gain_ratio通常设为0.01~0.1。设太小导致过拟合太大则树过浅。实测wine数据集13 属性178 样本推荐min_gain_ratio0.05max_depth8min_samples_split5。3.3 离散属性分裂避免ismember的向量化陷阱对离散属性如color列C4.5 将每个唯一值作为一个分支。但 Matlab 的ismember在大数据量下慢应改用accumarray或逻辑索引function gr calc_discrete_gain_ratio(X_col, y, epsilon) % X_col: n×1 离散列可能含 NaN [vals, ~, idx] unique(X_col, stable); valid_mask ~isnan(X_col); valid_idx idx(valid_mask); % 统计每个值的样本权重和 weights_per_val accumarray(valid_idx, y(valid_mask,2), [numel(vals) 1]); total_weight sum(y(:,2)); % 计算 SplitInfo p_vals weights_per_val / total_weight; split_info -sum(p_vals .* log2(max(p_vals, epsilon))); % 计算 Info_A info_A 0; for i 1:numel(vals) mask (X_col vals(i)) | isnan(X_col); % 缺失值也计入该分支 if sum(mask) 0 sub_y y(mask, :); info_A info_A (sum(sub_y(:,2))/total_weight) * calc_entropy(sub_y, epsilon); end end info_S calc_entropy(y, epsilon); gain info_S - info_A; gr (split_info epsilon) ? gain / split_info : -Inf; end4. 后剪枝实现用验证集误差驱动子树替换Matlab 版 Reduced-Error Pruning预剪枝靠阈值后剪枝靠数据。C4.5 默认采用Reduced-Error Pruning用独立验证集评估每个非叶节点若将其替换为叶节点即用该节点多数类预测能使验证误差下降则执行替换。这要求树结构支持“临时替换”和“误差回溯”。4.1 验证集误差计算与节点替换逻辑假设验证集为X_val,y_val无权重列需编写evaluate_tree_error函数返回错误样本数。关键点在于替换节点时其所有后代节点被忽略仅用该节点class_label预测整棵子树覆盖的验证样本。function error_count evaluate_tree_error(node, X_val, y_val, epsilon) if node.is_leaf pred repmat(node.class_label, size(y_val,1), 1); error_count sum(pred ~ y_val); return; end % 根据 node.split_val 划分验证集 X_col X_val(:, node.feature_idx); if isnumeric(X_col) % 连续 left_mask X_col node.split_val; right_mask ~left_mask; else % 离散 left_mask ismember(X_col, node.split_val); % node.split_val 为 cellstr right_mask ~left_mask; end % 递归计算左右子树误差 error_count 0; if ~isempty(node.children) numel(node.children) 2 if any(left_mask) error_count error_count evaluate_tree_error(node.children{1}, X_val(left_mask,:), y_val(left_mask), epsilon); end if any(right_mask) error_count error_count evaluate_tree_error(node.children{2}, X_val(right_mask,:), y_val(right_mask), epsilon); end end end4.2 后剪枝主循环DFS 遍历 替换评估function pruned_node post_prune(node, X_val, y_val, alpha, epsilon) if node.is_leaf pruned_node node; return; end % 先递归剪枝子树 for i 1:numel(node.children) node.children{i} post_prune(node.children{i}, X_val, y_val, alpha, epsilon); end % 计算当前子树在验证集上的误差 curr_error evaluate_tree_error(node, X_val, y_val, epsilon); % 计算若将 node 替换为叶节点的误差用 node.class_label 预测所有覆盖样本 % 需先获取 node 覆盖的验证样本索引 —— 这里简化用训练时的样本分布近似 % 实际工程中应保存每个节点的样本索引掩码 % 为简化我们假设 node.class_label 是训练集多数类直接预测全部验证集 pred_all repmat(node.class_label, size(y_val,1), 1); replace_error sum(pred_all ~ y_val); % 若替换后误差更小执行剪枝 if replace_error curr_error node.is_leaf true; node.children {}; pruned_node node; else pruned_node node; end end注意真实场景中node.class_label应基于验证集样本重新计算即该节点覆盖的验证样本中多数类而非训练集。这需要在建树时保存每个节点的样本索引或在剪枝前做一次前向传播标记。本例为降低复杂度采用训练集多数类近似实际项目中务必补全。5. 完整运行链路从wine.csv加载、参数调优到可视化树结构现在把所有模块串起来。以 UCI Wine 数据集为例13 个化学指标3 类葡萄酒演示如何从原始 CSV 跑通 C4.5 全流程并用plot函数绘制树结构。5.1 数据加载与预处理处理 CSV 中的逗号与空格Wine 数据集常因 Excel 保存格式含多余空格需清洗% 加载 wine.csv第一列为标签后13列为特征 data readmatrix(wine.csv, Delimiter, ,); % 清洗去除首尾空格转换为 numeric data cellfun((x) str2double(strtrim(x)), num2cell(data), UniformOutput, false); data cell2mat(data); X data(:, 2:end); % 特征 y data(:, 1); % 标签整数 1/2/3 % 划分训练集80%、验证集20% n size(X,1); idx randperm(n); train_idx idx(1:round(0.8*n)); val_idx idx(round(0.8*n)1:end); X_train X(train_idx, :); y_train y(train_idx); X_val X(val_idx, :); y_val y(val_idx); % 添加权重列y_train 从 n×1 变为 n×2 y_train [y_train, ones(size(y_train))];5.2 参数调优实验网格搜索min_gain_ratio与max_depth为找到最优超参运行网格搜索min_gr_list [0.01, 0.03, 0.05, 0.08, 0.1]; max_depth_list [4, 6, 8, 10]; results table(Size, [0 3], VariableTypes, {double,double,double}, ... VariableNames, {MinGainRatio,MaxDepth,ValError}); for gr min_gr_list for depth max_depth_list tree build_tree(X_train, y_train, 0, depth, 5, gr, 1e-12); pruned_tree post_prune(tree, X_val, y_val, 0.01, 1e-12); err evaluate_tree_error(pruned_tree, X_val, y_val, 1e-12); results [results; table(gr, depth, err)]; end end % 找最优组合 [~, best_idx] min(results.ValError); best_gr results.MinGainRatio(best_idx); best_depth results.MaxDepth(best_idx); fprintf(Best: min_gain_ratio%.3f, max_depth%d, val_error%.3f\n, best_gr, best_depth, results.ValError(best_idx));5.3 可视化树结构用plot和text手绘决策路径Matlab 没有treeplot对 C4.5 的原生支持我们用坐标计算手动绘制function plot_tree(node, x, y, dx, dy, level) if isempty(node) || ~isstruct(node) return; end % 绘制当前节点框 rectangle(Position, [x-0.5, y-0.2, 1, 0.4], FaceColor, w, EdgeColor, k); if node.is_leaf text(x, y, sprintf(Class %d, node.class_label), HorizontalAlignment, center); else feat_name sprintf(Feature %d, node.feature_idx); if isnumeric(node.split_val) text(x, y, sprintf(%s ≤ %.2f, feat_name, node.split_val), HorizontalAlignment, center); else text(x, y, sprintf(%s ∈ {%s}, feat_name, strjoin(node.split_val, ,)), HorizontalAlignment, center); end end % 绘制子节点连线与递归 if ~node.is_leaf ~isempty(node.children) n_children numel(node.children); for i 1:n_children child_x x (i-1)*dx - (n_children-1)*dx/2; child_y y - dy; line([x, child_x], [y-0.2, child_y0.2], Color, k); plot_tree(node.children{i}, child_x, child_y, dx/1.5, dy*0.8, level1); end end end % 调用 figure; plot_tree(pruned_tree, 0, 0, 4, 2, 0); axis equal off; title(C4.5 Decision Tree (Pruned));技巧dx和dy控制树的横向/纵向间距level用于动态缩放子树尺寸。实际项目中可导出为 PDFprint(-dpdf, c45_tree.pdf)。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号