2026/2/21 0:41:06
网站建设
项目流程
网站建设学校培训学校,读取别人网站代码自己做,罗马尼亚网站后缀,企业网站建设三网合一个人首页#xff1a; 永远都不秃头的程序员(互关)C语言专栏:从零开始学习C语言C专栏:C的学习之路K-Means专栏:K-Means深度探索系列本章所属专栏:决策树深度探索系列 文章目录 决策树的DNA#xff1a;基本结构单元1. 根节点 (Root Node)#xff1a;决策的起点2. 内部节点 (In…个人首页 永远都不秃头的程序员(互关)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_index2# 目标变量在数据集中的索引# 1. 定义节点结构 (这是树的骨架)classSimpleDecisionTreeNode:def__init__(self,feature_nameNone,predicted_classNone):self.feature_namefeature_name# 如果是内部节点表示用于划分的特征名称self.predicted_classpredicted_class# 如果是叶节点表示最终预测的类别self.children{}# 字典 {特征值: 子节点}defadd_child(self,feature_value,child_node):为当前节点添加一个子节点通过特定的特征值连接。self.children[feature_value]child_nodedef__repr__(self):ifself.predicted_classisnotNone:returnfLeaf({self.predicted_class})else:returnfNode({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_classUnknown)# 停止条件2: 所有样本都属于同一类别 (纯净)labels_in_data[sample[target_index]forsampleindata]iflen(set(labels_in_data))1:returnSimpleDecisionTreeNode(predicted_classlabels_in_data[0])# 停止条件3: 没有更多特征可以用来划分ifnotavailable_feature_indices:returnSimpleDecisionTreeNode(predicted_classget_majority_class(data))# --- 简化处理这里我们不计算信息增益而是简单地按顺序选择下一个可用特征 ---# 在实际决策树中这里会选择信息增益最大的特征current_feature_idxavailable_feature_indices[0]current_feature_namefeature_names[current_feature_idx]print(f 当前节点根据特征 {current_feature_name} 进行划分...)root_nodeSimpleDecisionTreeNode(feature_namecurrent_feature_name)# 按照当前特征的不同取值进行分组feature_value_groupscollections.defaultdict(list)forsampleindata:feature_valuesample[current_feature_idx]feature_value_groups[feature_value].append(sample)# 递归构建子树new_available_feature_indicesavailable_feature_indices[1:]# 移除已用特征forvalue,group_datainfeature_value_groups.items():print(f ➡️ 分支: {current_feature_name} {value})child_nodebuild_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_indiceslist(range(len(feature_names)))my_tree_skeletonbuild_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_idxfeature_names.index(tree.feature_name)feature_valuesample[feature_idx]iffeature_valueintree.children:returnsimple_predict(tree.children[feature_value],sample)else:# 如果遇到训练集中没有出现过的特征值返回默认多数类别returnUnknown# 或者可以返回树的多数类别print(\n 进行预测)test_fruit1[Red,Round]prediction1simple_predict(my_tree_skeleton,test_fruit1)print(fSample:{test_fruit1}- Prediction:{prediction1})test_fruit2[Orange,Oval]prediction2simple_predict(my_tree_skeleton,test_fruit2)print(fSample:{test_fruit2}- Prediction:{prediction2})test_fruit3[Green,Round]# 训练集中未出现的颜色prediction3simple_predict(my_tree_skeleton,test_fruit3)print(fSample:{test_fruit3}- Prediction:{prediction3})代码解读SimpleDecisionTreeNode类这是我们定义树的骨架的核心。feature_name存储当前节点用于划分的特征名称对于内部节点。predicted_class存储这个节点最终预测的类别对于叶节点。children是一个字典通过特征值将当前节点与它的子节点连接起来完美体现了分支的概念。get_majority_class(data)一个辅助函数用于在达到停止条件时确定叶节点应该预测的类别——通常是当前数据集中出现次数最多的类别。build_simple_tree(data, available_feature_indices)这是一个递归函数负责树的生长。它首先检查一系列停止条件数据集是否为空是否所有样本都属于同一类别纯净的叶节点是否已经没有更多特征可以用来划分这些条件确保了树不会无限生长最终能够形成叶节点。在本次简化实现中我们省略了信息增益的计算而是简单地按available_feature_indices中的顺序选择下一个特征进行划分。这突出了“节点-分支-子节点”的递归构建过程而不是特征选择的复杂性。它创建当前节点root_node根据所选特征的值将数据分组然后对每个分组递归调用build_simple_tree来构建子节点并将子节点通过add_child方法连接到当前节点形成分支。print_simple_tree和simple_predict这两个函数帮助我们可视化和使用构建好的骨架树。print_simple_tree可以让你清晰地看到树的层次结构而simple_predict则模拟了样本如何在树中沿着分支移动最终得到预测结果。通过这个亲手构建的“骨架”分类器你是不是对决策树的结构有了更深层次的理解你不仅看到了这些抽象的概念更是亲手将它们代码化组成了这棵“智慧之树”结语与展望在未来的“深度探索”系列中我们将继续升级我们的知识连续型特征的处理如何为数值型数据找到最佳的划分点决策树的剪枝如何让树变得更“健壮”避免过拟合多分类问题处理决策树如何优雅地处理多个类别回归决策树如何用决策树来预测连续值机器学习的道路充满了无限可能每一次探索都是一次成长。保持这份热情我们下篇再见如果你有任何疑问或想分享你的见解欢迎在评论区留言哦