news 2026/4/3 3:39:39

【决策树深度探索(三)】树的骨架:节点、分支与叶子,构建你的第一个分类器!

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【决策树深度探索(三)】树的骨架:节点、分支与叶子,构建你的第一个分类器!

  • 个人首页: 永远都不秃头的程序员(互关)
  • C语言专栏:从零开始学习C语言
  • C++专栏:C++的学习之路
  • K-Means专栏:K-Means深度探索系列
  • 本章所属专栏:决策树深度探索系列

文章目录

      • 决策树的DNA:基本结构单元
        • 1. 根节点 (Root Node):决策的起点
        • 2. 内部节点 (Internal Node):提出问题与分叉口
        • 3. 分支 (Branch):决策的路径与条件
        • 4. 叶子节点 (Leaf Node):最终的结论与答案
      • 手撕代码:构建一个最简单的分类器骨架!
        • 代码解读:
      • 结语与展望

亲爱的AI探索者们,大家好!🎉 欢迎来到“决策树深度探索”系列的第三站。如果说第一站是决策树的“大脑”——信息增益,第二站是决策树的“思维模式”——决策流程,那么今天,我们要深入探究的,就是这棵“智慧之树”的**“骨架”**!我们将像生物学家解剖植物一样,详细拆解决策树的各个结构单元:根节点、内部节点、分支和叶子节点,并亲手组装它们,构建一个能够清晰分类的决策器。

决策树的DNA:基本结构单元

想象一下一棵真实的树:它有一个主干,主干上分出枝桠,枝桠上再长出叶子。决策树的结构与此异曲同工,每一个部分都有其特定的功能和意义。理解这些基本单元,是掌握决策树的基石。

1. 根节点 (Root Node):决策的起点

决策树的根节点是整个树的起始点,也是我们进行决策的第一个关卡。它代表了整个数据集,所有的样本数据都从这里开始进入决策流程。

  • 功能:在根节点,算法会选择第一个、也是最重要的一个特征来进行划分。这个特征的选择基于它能够带来最大的信息增益(或最小的Gini不纯度),从而将最复杂的初始数据集进行最有效的第一次分离。
  • 类比:就像你早晨醒来,做的第一个决定(例如“今天穿什么衣服?”)会影响接下来一系列的子决定。
2. 内部节点 (Internal Node):提出问题与分叉口

除了根节点和叶节点,树中所有的中间节点都称为内部节点。它们是决策树的核心决策点。

  • 功能:每个内部节点都代表了对某个特征的测试。例如,“天气是否晴朗?”、“学生成绩是否大于60?”。根据这个特征的取值,样本会被引导到不同的分支。
  • 如何选择测试:算法会在当前节点包含的子数据集中,再次寻找能够最大化信息增益的特征来作为新的划分标准。
  • 类比:想象一个选择题,每个内部节点就是一个问题,你根据问题的答案选择走向不同的选项。
3. 分支 (Branch):决策的路径与条件

从内部节点延伸出去的每一条连线,就是分支

  • 功能:每个分支都代表了内部节点测试结果的一个特定取值或条件。例如,如果内部节点测试的是“天气”,那么一个分支可能代表“晴朗”,另一个分支代表“阴天”,还有一个分支代表“下雨”。样本会沿着与其特征值匹配的分支向下移动。
  • 类比:在迷宫中,每当你遇到一个岔路口,你都需要根据某个条件(例如“这条路通向出口吗?”)选择一条路径。
4. 叶子节点 (Leaf Node):最终的结论与答案

当一个分支无法再进行进一步划分时(因为它满足了停止条件),它就会终止于一个叶子节点

  • 功能:叶子节点是决策树的终点,它不再包含任何测试,而是直接给出一个分类结果(在分类树中)或预测值(在回归树中)。这个结果通常是该叶节点所包含的所有样本中数量最多的类别。
  • 纯度:理想情况下,一个叶子节点应该包含尽可能“纯净”的数据,即所有样本都属于同一类别。
  • 类比:当你沿着决策路径一路走下来,最终到达一个房间,这个房间就是你的最终目的地和结论。

手撕代码:构建一个最简单的分类器骨架!

为了更直观地理解这些结构,我们将再次回到“手撕代码”的模式,用最朴素的方式来模拟构建决策树的骨架。这次,我们将使用一个更简单、更“玩具”的数据集,专注于如何将数据递归地分解,直到形成叶节点。

我们将尝试识别一种水果是“苹果”还是“橙子”,只根据两个特征:“颜色”和“形状”。

importcollections# 示例数据集:[颜色, 形状, 标签(目标变量)]# 0: 红色, 1: 圆形, 2: 苹果# 1: 橙色, 2: 椭圆, 3: 橙子toy_data=[['Red','Round','Apple'],['Red','Round','Apple'],['Red','Round','Apple'],['Orange','Round','Orange'],['Orange','Oval','Orange'],['Orange','Oval','Orange'],['Red','Oval','Apple'],]feature_names=['Color','Shape']target_index=2# 目标变量在数据集中的索引# 1. 定义节点结构 (这是树的骨架!)classSimpleDecisionTreeNode:def__init__(self,feature_name=None,predicted_class=None):self.feature_name=feature_name# 如果是内部节点,表示用于划分的特征名称self.predicted_class=predicted_class# 如果是叶节点,表示最终预测的类别self.children={}# 字典 {特征值: 子节点}defadd_child(self,feature_value,child_node):"""为当前节点添加一个子节点,通过特定的特征值连接。"""self.children[feature_value]=child_nodedef__repr__(self):ifself.predicted_classisnotNone:returnf"Leaf({self.predicted_class})"else:returnf"Node({self.feature_name}, branches={list(self.children.keys())})"# 2. 辅助函数:计算当前数据集中出现最多的类别 (用于叶节点)defget_majority_class(data):""" 计算数据集中目标变量出现次数最多的类别。 """ifnotdata:returnNonelabels=[sample[target_index]forsampleindata]returncollections.Counter(labels).most_common(1)[0][0]# 3. 核心构建函数:递归地创建树的骨架defbuild_simple_tree(data,available_feature_indices):""" 递归构建一个简化的决策树。 这个版本不计算信息增益,只是演示节点的递归构建和停止条件。 """# 停止条件1: 数据集为空ifnotdata:returnSimpleDecisionTreeNode(predicted_class="Unknown")# 停止条件2: 所有样本都属于同一类别 (纯净)labels_in_data=[sample[target_index]forsampleindata]iflen(set(labels_in_data))==1:returnSimpleDecisionTreeNode(predicted_class=labels_in_data[0])# 停止条件3: 没有更多特征可以用来划分ifnotavailable_feature_indices:returnSimpleDecisionTreeNode(predicted_class=get_majority_class(data))# --- 简化处理:这里我们不计算信息增益,而是简单地按顺序选择下一个可用特征 ---# 在实际决策树中,这里会选择信息增益最大的特征current_feature_idx=available_feature_indices[0]current_feature_name=feature_names[current_feature_idx]print(f" 🌳 当前节点根据特征 '{current_feature_name}' 进行划分...")root_node=SimpleDecisionTreeNode(feature_name=current_feature_name)# 按照当前特征的不同取值进行分组feature_value_groups=collections.defaultdict(list)forsampleindata:feature_value=sample[current_feature_idx]feature_value_groups[feature_value].append(sample)# 递归构建子树new_available_feature_indices=available_feature_indices[1:]# 移除已用特征forvalue,group_datainfeature_value_groups.items():print(f" ➡️ 分支: '{current_feature_name}' = '{value}'")child_node=build_simple_tree(group_data,new_available_feature_indices)root_node.add_child(value,child_node)returnroot_node# 4. 可视化树的结构 (简单打印)defprint_simple_tree(node,indent=""):ifnode.predicted_classisnotNone:print(f"{indent}└── Predict:{node.predicted_class}")else:forvalue,childinnode.children.items():print(f"{indent}├── IF{node.feature_name}is '{value}':")print_simple_tree(child,indent+"│ ")# --- 运行你的骨架分类器! ---print("🚀 开始构建决策树骨架...")all_features_indices=list(range(len(feature_names)))my_tree_skeleton=build_simple_tree(toy_data,all_features_indices)print("\n🌳 构建完成的决策树骨架:")print_simple_tree(my_tree_skeleton)# 预测功能 (与前面类似)defsimple_predict(tree,sample):iftree.predicted_classisnotNone:returntree.predicted_class feature_idx=feature_names.index(tree.feature_name)feature_value=sample[feature_idx]iffeature_valueintree.children:returnsimple_predict(tree.children[feature_value],sample)else:# 如果遇到训练集中没有出现过的特征值,返回默认多数类别return"Unknown"# 或者可以返回树的多数类别print("\n🔮 进行预测:")test_fruit1=['Red','Round']prediction1=simple_predict(my_tree_skeleton,test_fruit1)print(f"Sample:{test_fruit1}-> Prediction:{prediction1}")test_fruit2=['Orange','Oval']prediction2=simple_predict(my_tree_skeleton,test_fruit2)print(f"Sample:{test_fruit2}-> Prediction:{prediction2}")test_fruit3=['Green','Round']# 训练集中未出现的颜色prediction3=simple_predict(my_tree_skeleton,test_fruit3)print(f"Sample:{test_fruit3}-> Prediction:{prediction3}")
代码解读:
  1. SimpleDecisionTreeNode

    • 这是我们定义树的骨架的核心。feature_name存储当前节点用于划分的特征名称(对于内部节点)。predicted_class存储这个节点最终预测的类别(对于叶节点)。children是一个字典,通过特征值将当前节点与它的子节点连接起来,完美体现了分支的概念。
  2. get_majority_class(data)

    • 一个辅助函数,用于在达到停止条件时,确定叶节点应该预测的类别——通常是当前数据集中出现次数最多的类别。
  3. build_simple_tree(data, available_feature_indices)

    • 这是一个递归函数,负责树的生长。
    • 它首先检查一系列停止条件:数据集是否为空?是否所有样本都属于同一类别(纯净的叶节点)?是否已经没有更多特征可以用来划分?这些条件确保了树不会无限生长,最终能够形成叶节点。
    • 在本次简化实现中,我们省略了信息增益的计算,而是简单地按available_feature_indices中的顺序选择下一个特征进行划分。这突出了“节点-分支-子节点”的递归构建过程,而不是特征选择的复杂性。
    • 它创建当前节点(root_node),根据所选特征的值将数据分组,然后对每个分组递归调用build_simple_tree来构建子节点,并将子节点通过add_child方法连接到当前节点,形成分支。
  4. print_simple_treesimple_predict

    • 这两个函数帮助我们可视化和使用构建好的骨架树。print_simple_tree可以让你清晰地看到树的层次结构,而simple_predict则模拟了样本如何在树中沿着分支移动,最终得到预测结果。

通过这个亲手构建的“骨架”分类器,你是不是对决策树的结构有了更深层次的理解?你不仅看到了这些抽象的概念,更是亲手将它们代码化,组成了这棵“智慧之树”!💡

结语与展望

在未来的“深度探索”系列中,我们将继续升级我们的知识:

  • 连续型特征的处理:如何为数值型数据找到最佳的划分点?
  • 决策树的剪枝:如何让树变得更“健壮”,避免过拟合?
  • 多分类问题处理:决策树如何优雅地处理多个类别?
  • 回归决策树:如何用决策树来预测连续值?

机器学习的道路充满了无限可能,每一次探索都是一次成长。保持这份热情,我们下篇再见!如果你有任何疑问或想分享你的见解,欢迎在评论区留言哦!💬


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/12 13:13:54

Glyph法律场景应用:合同文本快速解析系统搭建教程

Glyph法律场景应用:合同文本快速解析系统搭建教程 1. 为什么法律人需要Glyph这样的工具? 你有没有遇到过这些情况: 一份50页的并购合同,光通读就要两小时,关键条款还容易漏看;客户临时发来三份不同版本的…

作者头像 李华
网站建设 2026/3/12 15:27:24

智能家居设备集成:从碎片化控制到互联互通的技术探索

智能家居设备集成:从碎片化控制到互联互通的技术探索 【免费下载链接】hass-xiaomi-miot Automatic integrate all Xiaomi devices to HomeAssistant via miot-spec, support Wi-Fi, BLE, ZigBee devices. 小米米家智能家居设备接入Hass集成 项目地址: https://gi…

作者头像 李华
网站建设 2026/4/2 0:10:55

Qwen3-Embedding-0.6B经济部署:低功耗GPU运行可行性案例

Qwen3-Embedding-0.6B经济部署:低功耗GPU运行可行性案例 你是不是也遇到过这样的问题:想在业务中用上高质量的文本嵌入能力,但一看到动辄需要A100或H100的模型就打退堂鼓?显存不够、电费太贵、运维复杂……这些现实约束让很多团队…

作者头像 李华
网站建设 2026/3/31 11:50:47

升级Z-Image-Turbo后,图像生成速度提升3倍

升级Z-Image-Turbo后,图像生成速度提升3倍 你是否经历过这样的等待:输入一段提示词,点击生成,然后盯着进度条数秒、十几秒,甚至更久?在AI图像创作中,生成速度往往直接决定工作流的流畅度——一…

作者头像 李华
网站建设 2026/3/16 9:12:09

解锁流媒体解析全攻略:N_m3u8DL-RE视频下载工具深度指南

解锁流媒体解析全攻略:N_m3u8DL-RE视频下载工具深度指南 【免费下载链接】N_m3u8DL-RE 跨平台、现代且功能强大的流媒体下载器,支持MPD/M3U8/ISM格式。支持英语、简体中文和繁体中文。 项目地址: https://gitcode.com/GitHub_Trending/nm3/N_m3u8DL-RE…

作者头像 李华
网站建设 2026/3/27 16:20:13

解锁文档扫描新姿势:开源工具NAPS2全场景应用秘诀

解锁文档扫描新姿势:开源工具NAPS2全场景应用秘诀 【免费下载链接】naps2 Scan documents to PDF and more, as simply as possible. 项目地址: https://gitcode.com/gh_mirrors/na/naps2 纸质文档堆积正成为现代办公的隐形效率杀手——占用宝贵物理空间、检…

作者头像 李华