news 2026/4/18 8:01:48

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

作者头像

张小明

前端开发工程师

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/4/18 7:56:56

ESP32开发环境零失败配置:从入门到专家的系统方案

ESP32开发环境零失败配置:从入门到专家的系统方案 【免费下载链接】arduino-esp32 Arduino core for the ESP32 项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32 在物联网开发领域,ESP32开发板以其强大的性能和丰富的功能成为开…

作者头像 李华
网站建设 2026/4/15 4:09:04

5个场景带你解锁PDF Arranger:开源工具如何重构文档管理效率

5个场景带你解锁PDF Arranger:开源工具如何重构文档管理效率 【免费下载链接】pdfarranger Small python-gtk application, which helps the user to merge or split PDF documents and rotate, crop and rearrange their pages using an interactive and intuitive…

作者头像 李华
网站建设 2026/3/26 22:59:34

如何用AI测试生成提升80%开发效率?从0到1构建智能测试体系

如何用AI测试生成提升80%开发效率?从0到1构建智能测试体系 【免费下载链接】claude-code Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining com…

作者头像 李华
网站建设 2026/4/18 5:42:33

是否需要重训练GPEN?迁移学习适用场景判断教程

是否需要重训练GPEN?迁移学习适用场景判断教程 你刚拿到一个GPEN人像修复增强模型镜像,打开终端运行了几行命令,一张模糊的老照片瞬间变得清晰自然——这时候你可能会想:这个效果已经很好了,我是不是还得花几天时间准…

作者头像 李华
网站建设 2026/4/17 7:55:45

Z-Image-Turbo提示词怎么写?prompt参数优化实战指南

Z-Image-Turbo提示词怎么写?prompt参数优化实战指南 1. 开箱即用:30G权重预置的文生图高性能环境 Z-Image-Turbo不是又一个需要折腾下载、编译、调试的模型,它是一套真正“开箱即用”的文生图解决方案。镜像中已完整集成阿里ModelScope开源…

作者头像 李华
网站建设 2026/4/11 3:27:28

3步驯服失控快捷键:OpenArk工具的系统级解决方案

3步驯服失控快捷键:OpenArk工具的系统级解决方案 【免费下载链接】OpenArk The Next Generation of Anti-Rookit(ARK) tool for Windows. 项目地址: https://gitcode.com/GitHub_Trending/op/OpenArk 当你在 deadline 前遭遇快捷键失效,精心编排的…

作者头像 李华