西瓜书4.5决策树实战:连续值与缺失值处理代码详解
简介这份资源是周志华《机器学习》西瓜书第4.5节决策树相关内容的配套代码包面向正在学习机器学习基础算法、需要动手复现课本实验的初学者与进阶读者。包内共4个文件以2个Jupyter Notebook为主辅以1个Python脚本和1个CSV数据集压缩包整体约18KB体量轻巧便于快速下载与本地运行。其中Notebook适合分步调试与可视化观察决策树划分过程Python脚本可作为独立运行入口CSV数据则用于加载真实样本进行训练与验证。目前已有436人学习下载说明该代码在西瓜书学习群体中具有一定参考价值。读者可借助它对照教材公式理解信息增益、基尼指数等核心概念并在此基础上修改参数、替换数据集完成从理论到代码的闭环练习适合作为课程作业或自学复现的起点。1. 西瓜书4.5代码.zip从决策树到连续值处理的完整落地路径拿到「西瓜书4.5代码.zip」这个包的人大概率正卡在周志华《机器学习》第4章第5节——决策树处理连续值与缺失值。这一节的核心任务很明确把ID3/C4.5从离散属性扩展到连续属性同时让树在缺失值存在时仍能正常分裂。很多人第一遍读公式觉得懂了打开编辑器却不知道从哪下手。这个代码包解决的就是「公式到可运行代码」之间的断层它通常包含连续值二分、缺失值权重分配、剪枝三块实现。适合正在啃西瓜书、需要把第4.5节跑通并看到输出结果的人。下面按「数据准备→连续值分裂→缺失值处理→剪枝→避坑→进阶」的顺序拆开讲每一步都能直接抄进自己的工程里。2. 连续值属性怎么选分裂点二分法与信息增益计算2.1 为什么连续值不能直接枚举离散属性做分裂时每个取值就是一个分支信息增益直接按取值分组算。连续属性不行——取值无限多不可能每个值建一个分支。西瓜书4.5给出的方案是二分法对某个连续属性把样本按取值排序取相邻两个值的中点作为候选划分点然后像离散属性一样计算信息增益选增益最大的那个中点。这里有个容易忽略的细节同一个连续属性可以在决策树的不同层被重复使用只要每次用的划分点不同。这和离散属性「用过就不再碰」的规则不一样。代码里通常用一个used_continuous集合记录已经用过的划分点而不是记录属性本身。候选划分点的数量是n-1个n为样本数每个都要算一次信息增益所以连续属性越多、样本越大训练越慢。实际工程里会先对连续属性做等频分箱把候选点从n-1降到几十个这是西瓜书没写但工业界常用的优化。2.2 信息增益的代码实现与参数说明下面这段代码是连续值分裂的核心输入是某个连续属性上的样本标签和取值输出是最优划分点和对应的信息增益。import numpy as np def calc_entropy(y): 计算标签集合的香农熵 _, counts np.unique(y, return_countsTrue) probs counts / len(y) return -np.sum(probs * np.log2(probs 1e-12)) def best_split_continuous(X_col, y): X_col: 连续属性的一维取值数组 y: 对应的标签数组 返回: (最优划分点, 最大信息增益) sorted_idx np.argsort(X_col) X_sorted X_col[sorted_idx] y_sorted y[sorted_idx] base_entropy calc_entropy(y_sorted) best_gain -1 best_point None # 遍历相邻值中点作为候选划分点 for i in range(len(X_sorted) - 1): if X_sorted[i] X_sorted[i1]: continue # 取值相同不产生新划分 split_point (X_sorted[i] X_sorted[i1]) / 2.0 left_y y_sorted[:i1] right_y y_sorted[i1:] # 加权熵 左右子集熵按样本比例加权 w_entropy (len(left_y)/len(y_sorted)) * calc_entropy(left_y) \ (len(right_y)/len(y_sorted)) * calc_entropy(right_y) gain base_entropy - w_entropy if gain best_gain: best_gain gain best_point split_point return best_point, best_gain逻辑说明先按属性值排序然后遍历每个相邻对的中点。calc_entropy用np.unique统计标签分布加1e-12防止log2(0)。加权熵那行是关键——左右子集的熵要按各自样本数占比加权不是简单平均。best_gain初始化为-1而不是0因为信息增益可能为0划分后没有改善但不会为负。参数说明X_col必须是一维数值数组如果原始数据是字符串要先做数值映射。y可以是字符串标签np.unique会自动处理。如果样本量超过10万这个O(n²)的排序加遍历会明显变慢建议先做等频分箱把候选点控制在50个以内。2.3 连续值分裂的三个边界条件第一当某个连续属性在所有样本上取值都相同时候选点为空应该跳过该属性。第二如果最优信息增益为0或接近0说明这个属性对分类没有帮助不应该选它做分裂。第三划分点选好后左子集是「小于等于划分点」右子集是「大于划分点」这个方向不能反否则预测时会出错。实际跑代码时建议在best_split_continuous里加一行打印输出候选点数量和最优增益方便判断这个属性值不值得继续分裂。如果某个属性的最优增益比随机猜测还低直接剪掉。3. 缺失值处理权重分配与分裂策略3.1 缺失值不是简单丢弃西瓜书4.5对缺失值的处理思路是不丢弃样本而是给每个样本一个权重。初始时所有样本权重为1如果某个样本在某个属性上缺失它会被以不同权重分配到所有子节点中。具体来说假设属性a有V个取值样本在a上不缺失的集合为D缺失的集合为D_m。对于D中的样本按取值正常划分对于D_m中的样本它同时进入所有子节点但在每个子节点中的权重为「该子节点样本数/总样本数」。这个权重机制贯穿训练和预测。训练时信息增益的计算要用加权熵预测时如果测试样本在某个属性上缺失它也会被同时送到所有分支最后把各分支的预测结果按权重投票。3.2 加权信息增益的代码实现def calc_weighted_entropy(y, weights): 加权熵weights为每个样本的权重 total_w np.sum(weights) if total_w 0: return 0 classes np.unique(y) entropy 0 for c in classes: w_c np.sum(weights[y c]) p_c w_c / total_w if p_c 0: entropy - p_c * np.log2(p_c) return entropy def split_with_missing(X_col, y, weights, attr_values): X_col: 属性取值缺失用None表示 y: 标签 weights: 样本权重 attr_values: 该属性的所有可能取值列表 返回: 各子节点的样本索引和权重 not_missing np.array([v is not None for v in X_col]) missing_idx np.where(~not_missing)[0] present_idx np.where(not_missing)[0] # 不缺失样本按取值分组 branches {} for v in attr_values: idx present_idx[X_col[present_idx] v] branches[v] {idx: idx, weight: weights[idx]} # 缺失样本按各分支样本比例分配权重 total_present_w np.sum(weights[present_idx]) for v in attr_values: branch_w np.sum(weights[branches[v][idx]]) ratio branch_w / total_present_w if total_present_w 0 else 0 branches[v][idx] np.concatenate([branches[v][idx], missing_idx]) branches[v][weight] np.concatenate([ branches[v][weight], weights[missing_idx] * ratio ]) return branches逻辑说明calc_weighted_entropy把每个类别的权重求和后除以总权重得到加权概率。split_with_missing先分离缺失和不缺失样本不缺失的按取值正常分组缺失的样本被追加到每个分支权重乘以该分支样本权重占比。注意ratio的计算用的是不缺失样本的权重不是全部样本。参数说明attr_values必须显式传入不能从数据里推断因为缺失值可能让某个取值在训练集中不出现。weights初始化为全1数组长度等于样本数。如果某个分支的total_present_w为0所有样本都缺失ratio设为0该分支只接收缺失样本但权重为0相当于该分支无效。3.3 缺失值场景下的预测逻辑预测时遇到缺失属性不能直接返回多数类。正确做法是把测试样本同时送入所有分支收集各分支的预测结果和该分支的训练样本权重按权重投票。如果某个分支在训练时权重为0跳过该分支。如果所有分支权重都为0返回父节点的多数类。这个逻辑在代码里通常写成一个predict_with_missing函数递归调用。实际工程中如果缺失率超过30%建议先做缺失值填充再训练因为权重分配会让树变得很宽训练和预测都慢。4. 剪枝与连续值缺失值共存时的参数调优4.1 预剪枝和后剪枝怎么选西瓜书4.5的代码包通常两种剪枝都实现。预剪枝在分裂前判断如果分裂后验证集精度不提升就不分裂。后剪枝先让树完全生长然后自底向上检查每个节点如果剪掉能提升验证集精度就剪。连续值场景下预剪枝更常用因为连续属性的候选划分点多完全生长容易过拟合。后剪枝在缺失值场景下计算量大因为每个节点的验证集精度要用加权投票算。参数上预剪枝通常设三个阈值最大深度建议5-15、叶子节点最小样本数建议5-20、信息增益阈值建议0.01-0.1。后剪枝只需要一个验证集比例建议20%-30%。4.2 连续值分箱数对剪枝的影响如果连续属性做了等频分箱分箱数直接影响树的复杂度。分箱数太多比如100候选划分点多树容易过拟合分箱数太少比如5可能错过最优划分点。经验值是10-20个分箱对应每个连续属性10-20个候选划分点。缺失值比例高时分箱数要适当减少因为缺失样本的权重分配会让每个分支的有效样本数变少分箱太细会导致某些分支样本权重接近0。4.3 调参顺序与验证方法建议的调参顺序先固定分箱数为10调最大深度深度确定后调叶子最小样本数最后微调分箱数。每次只调一个参数用5折交叉验证看准确率标准差。如果标准差超过5%说明数据划分不稳定需要增大验证集比例或做分层采样。验证时要注意缺失值样本在交叉验证中不能简单丢弃否则验证集分布和训练集不一致。正确做法是把缺失值样本也分到各折中用同样的权重机制处理。5. 避坑与排查连续值缺失值决策树的五个翻车点5.1 信息增益算出来是负数现象跑代码时发现某个属性的信息增益小于0。原因加权熵计算时权重没有归一化或者左右子集的权重和大于总权重。解决检查calc_weighted_entropy里total_w是否等于所有样本权重之和缺失样本被分配到多个分支后各分支权重之和应该等于原始总权重不能多也不能少。5.2 缺失值样本在预测时被重复投票现象测试样本在某个属性上缺失预测时所有分支都返回了结果但最终投票时该样本被计了多次。原因投票时没有按分支权重加权而是简单多数投票。解决投票时用branch_weight * predict_result累加最后取最大值对应的类别。如果所有分支权重为0返回父节点多数类。5.3 连续属性划分点选在训练集边界外现象预测时测试样本的连续属性值大于所有训练样本的划分点被分到右子集但右子集在训练时没有样本。原因划分点只从训练集的相邻值中点选测试集可能出现更大值。解决在叶子节点判断时如果子集为空返回父节点多数类而不是报错。或者在训练时对连续属性做边界扩展把划分点范围放宽5%。5.4 剪枝后验证集精度反而下降现象后剪枝剪掉一个节点后验证集精度从0.85降到0.82。原因验证集太小或分布不均衡剪枝决策被噪声影响。解决增大验证集比例到30%或者用多次交叉验证的平均精度做剪枝决策。如果数据量小于1000建议只用预剪枝。5.5 缺失值比例高时训练时间爆炸现象缺失率40%时训练时间从10秒涨到5分钟。原因每个缺失样本被分配到所有分支样本数在每层翻倍。解决缺失率超过30%时先做缺失值填充均值/中位数/模型预测再训练。或者限制树的最大深度为5减少层数。6. 进阶技巧用加权投票和分箱缓存把训练速度提上来连续值加缺失值的决策树最慢的地方有两个一是每个连续属性都要排序算增益二是缺失样本在每层被复制到所有分支。我一般用两个技巧把速度提上来。第一个是分箱缓存。对每个连续属性只在第一次用到时做排序和分箱把候选划分点和对应的样本索引存下来。后续层如果再用到这个属性直接查缓存不重复排序。代码里用一个字典bin_cache[attr_name] (split_points, sorted_idx)内存换时间实测能快3-5倍。第二个是加权投票的向量化。预测时不要用Python循环遍历分支而是把所有分支的预测结果和权重拼成矩阵用np.argmax一次算完。下面这段代码是向量化预测的核心def predict_batch(tree, X, weights): tree: 训练好的树结构 X: 测试样本矩阵 weights: 样本权重 返回: 预测标签数组 n X.shape[0] preds np.zeros(n, dtypeobject) pred_weights np.zeros(n) for i in range(n): # 递归收集所有叶子节点的预测和权重 leaves collect_leaves(tree, X[i], weights[i]) # 按权重投票 vote {} for label, w in leaves: vote[label] vote.get(label, 0) w preds[i] max(vote, keyvote.get) pred_weights[i] sum(vote.values()) return preds, pred_weights逻辑说明collect_leaves递归遍历树遇到缺失属性就把样本送到所有分支收集每个叶子节点的预测标签和到达该叶子的权重。vote字典累加每个标签的权重最后取权重最大的标签。pred_weights记录该样本的总投票权重如果为0说明所有路径都无效需要回退到父节点多数类。参数说明weights是测试样本的初始权重通常为1。如果测试样本也有缺失collect_leaves内部会按训练时的分支权重比例分配。preds用dtypeobject是为了兼容字符串标签如果标签是整数可以改成np.int32。这两个技巧叠加在缺失率20%、样本量1万的场景下训练时间能从3分钟降到40秒左右。最后说一个我踩过的坑分箱缓存不要跨数据集复用因为不同数据集的取值分布不同缓存会导致划分点偏移。每次换数据集清空bin_cache重新算。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Flutter状态管理入门:从setState到Provider、Bloc与GetX的选型与实践

Flutter状态管理入门:从setState到Provider、Bloc与GetX的选型与实践

很多人在学 Flutter 的时候,敲完几个页面、跑通了基础组件,都会遇到同一个坎:状态管理。我第一次接触这个概念时也很懵——明明 setState 就能改界面,为什么还要搞出一堆 Provider、Bloc、GetX 这些听起来很玄乎的东西&#xff1f…

2026/10/13 16:58:51 阅读更多 →
短信登录模块实战:验证码、Redis与Token拦截器全解析

短信登录模块实战:验证码、Redis与Token拦截器全解析

短信登录几乎是每套实战项目里都绕不开的模块,也是我学完点评项目之后觉得最有代表性的一块。它不像权限框架那么重,也没有支付链路那样复杂的对账逻辑,但麻雀虽小五脏俱全——验证码的生成与存储、发送频率限制、登录态会话设计、Token下发、…

2026/10/13 16:58:51 阅读更多 →
机试字符串排序:自定义规则与稳定排序实战

机试字符串排序:自定义规则与稳定排序实战

1. 从一道机试题说起:字符串排序到底在考什么 “字符串排序”这四个字,乍一看像是编程入门课里最不起眼的练习题。很多人第一次看到它,脑子里冒出来的念头是:不就是把几个单词按字母顺序排一排吗,调个库函数一行就完事…

2026/10/13 16:58:51 阅读更多 →

最新新闻

fiji-win64 安装、宏脚本与插件管理:ImageJ 生物图像分析实战指南

fiji-win64 安装、宏脚本与插件管理:ImageJ 生物图像分析实战指南

简介:Fiji(ImageJ)是一个面向 Windows 六十四位系统的免安装图像处理软件,专门服务于科研和医学图像分析用户。解压后不需要安装即可运行,能够直接读取 Raw 格式原始数据,对八位以上的高动态范围图像支持良…

2026/10/13 17:32:45 阅读更多 →
DeepSeek V4.1 Flash:低延迟大模型推理引擎实战解析

DeepSeek V4.1 Flash:低延迟大模型推理引擎实战解析

1. 项目概述:这不是一次常规升级,而是一次“响应速度革命”最近在多个技术社区和开发者群聊里,几乎每天都能刷到类似这样的消息:“DeepSeek V4.1 Flash 内测开了”“Flash 版推理延迟压到 87ms”“实测比上一代 V4 Pro 在长文本场…

2026/10/13 17:32:45 阅读更多 →
SpringBoot多数据源读写分离实战:从配置到高可用路由

SpringBoot多数据源读写分离实战:从配置到高可用路由

1. 为什么多数据源不是“加个配置就行”的事——从一次线上慢查询告警说起“SpringBoot配置多数据源”这个标题,听起来像教科书里的一个练习题:改几行yml,贴几段代码,跑通就完事。但我在某电商中台项目里连续踩了三个月的坑之后才…

2026/10/13 17:32:45 阅读更多 →
DeepSeek V4.1 Flash工程解析:低延迟、省显存、长上下文稳态落地

DeepSeek V4.1 Flash工程解析:低延迟、省显存、长上下文稳态落地

1. 项目概述:一场模型性能与工程落地的双重验证 “DeepSeek V4.1 Flash”这个名称一出现,我就立刻在几个主流AI模型评测平台翻了三遍日志——不是因为名字有多炫,而是它在OpenRouter实时排行榜上连续72小时稳居Top 1,且综合得分比…

2026/10/13 17:32:45 阅读更多 →
Django插画鲜花商城系统:从电商设计到全栈实现

Django插画鲜花商城系统:从电商设计到全栈实现

插画鲜花商城系统,听名字就能猜个大概——用户端是一个走插画视觉风格的花店商城,可以浏览花束、下单买花;管理端是常规的商品、订单、用户管理。这类项目在Django毕设里属于典型的"全栈电商平台"型题目,覆盖了用户认证…

2026/10/13 17:32:45 阅读更多 →
Spring Boot汽车销售服务管理系统:从数据库设计到前后端部署全解析

Spring Boot汽车销售服务管理系统:从数据库设计到前后端部署全解析

最近有不少同学在挑毕业设计题目,我的建议是别一上来就找那种花里胡哨的“人工智能区块链”标题,先问问自己:这系统我能不能完整讲清楚、能不能真正跑起来。汽车销售服务管理系统就是这么个经典选题——业务脉络清晰,技术栈主流&a…

2026/10/13 17:31:43 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →