
Python 数据分析入门树的第一步先看什么一文讲清 ID3、C4.5、CART 与决策树剪枝适合人群Python 初学者 / 数据分析入门 / 机器学习入门 / 教学案例分享本章有点难的上一篇文章里我们已经认识了决策树知道它很像一张流程图先问一个问题再根据答案走不同分支最后得到分类结果但认识决策树只是第一步。真正开始往下学的时候一个问题会马上冒出来这棵树到底该怎么长出来比如在“学生能否通过技能考核”这个案例里树的第一步到底应该先看什么平时成绩实训成绩出勤率还是是否参加培训这件事看起来像是在“选一个条件”实际上它决定了整棵树后面的结构。第一步选得好后面就容易分第一步选得不好整棵树可能越长越别扭。所以这一篇文章重点就讲两件事决策树第一步为什么不能随便选树是不是长得越复杂越好顺着这个思路我们就会自然碰到几个经典概念ID3C4.5CART信息增益增益率基尼指数剪枝过拟合这些名字第一次看会有点乱但别急。这篇文章我还是继续用“学生职业技能考核通过预测”这个统一案例把它们尽量讲得直白一点。一、先把问题说透决策树为什么不能“随便分”上一篇我们说过决策树的核心思想其实很朴素把分类问题变成一连串条件判断。但是真正开始构造一棵树时最关键的不是“要不要分”而是先按哪个属性分还是拿“学生能否通过技能考核”来举例。现在手里有这些特征平时成绩实训成绩出勤率是否参加培训如果一开始先按“是否参加培训”分也能分。如果一开始先按“实训成绩”分也能分。甚至先按“平时成绩”分也不是不行。那问题就来了既然都能分为什么还要研究“先分谁”因为第一步的分法会直接影响后面的整棵树。你可以把它想象成走迷宫如果第一步选对方向后面很快就接近出口如果第一步选偏了后面虽然还能走但会绕很多弯所以决策树构建里有一个特别核心的问题怎么选出当前最适合拿来分裂的属性这就是后面 ID3、C4.5、CART 要解决的事情。二、决策树到底是怎么“长出来”的决策树的构建过程本质上就是一个不断拆分数据的过程。它通常采用自顶向下、递归划分的方式来构造也就是从根结点开始一层一层往下长 [1]。这个过程可以理解成下面这样先拿全部样本作为起点选一个最合适的属性进行第一次划分划分之后得到几个子集对每个子集再继续选属性往下分一直分到满足停止条件为止所以决策树并不是“凭空生成”的它更像是在做一件事不断把原来混在一起的数据拆成越来越清楚的小块。那接下来的关键自然就是到底怎样才算“分得更好”不同算法对这个问题的回答不一样。三、先别急着记名字先记住这三种算法都在做同一件事很多人第一次看到这几个名字就开始头大ID3C4.5CART其实先别把它们当成三个完全不同的东西。更好的理解方式是它们都是用来长决策树的只是判断“该先分哪个属性”的标准不同。也就是说这三种算法都在做同一件事帮我们选属性帮我们决定当前这一步怎么分帮我们尽量把样本分得更容易分类差别不在“是不是决策树”而在于它们衡量“这个属性值不值得先分”的方式不同。为了把这件事讲清楚下面还是只用一个案例来说明不换别的场景。四、还是这个案例预测学生能否通过技能考核假设现在有 10 个学生结果如下6 个“通过”4 个“未通过”我们准备从下面几个属性里选一个作为决策树的第一层实训成绩是否参加培训学号看起来这三个属性都能拿来分但它们“分出来的效果”其实完全不一样。1按“实训成绩”分假设按“实训成绩”分成两组实训成绩高5 人其中 4 个通过1 个未通过实训成绩低5 人其中 2 个通过3 个未通过你会发现这样一分之后数据开始有方向了实训成绩高这一组明显偏向“通过”实训成绩低这一组未通过开始变多虽然还没彻底分干净但已经比原来“6 通过、4 未通过混在一起”更好判断了。也就是说这个属性一分下去让数据变得更容易区分。2按“是否参加培训”分再看另一个属性。假设按“是否参加培训”分成两组参加培训7 人其中 4 个通过3 个未通过没参加培训3 人其中 2 个通过1 个未通过这时候很多人会觉得“也分成两组了啊为什么说它没那么好”关键就在这里虽然分了但分完以后数据还是比较混。第一组是 4 比 3第二组是 2 比 1也就是说两边都还是“通过”和“未通过”掺在一起。和原来相比整体并没有明显变得更容易判断。所谓“没有变得更清楚”说白了就是你分是分了但分完以后还是没有出现哪一边明显更偏向某一类。所以这个属性不是不能用而是它的区分能力没有那么强。3按“学号”分“学号”这个例子特别有代表性因为它最容易让人一开始觉得绕。假设 10 个学生的学号都不同。如果按“学号”来分会发生什么很可能变成这样学号001 → 单独一组学号002 → 单独一组学号003 → 单独一组……表面上看这种分法简直太厉害了一下子把每个人都分开了。从训练数据的角度看它确实“切得很细”。每个分支都很纯因为一个分支里可能就只有一个人。但问题也恰恰在这里它不是在学规律而是在记人。学号当然能区分每个学生但学号本身和“能不能通过考核”没有真正的分类规律关系。它不能帮助我们判断一个新学生。所以“学号”这个属性特别适合说明一个问题有些属性看起来特别会分但它只是把训练样本拆散了不代表它真的有预测价值。这一点正是 ID3 和 C4.5 差别会出现的地方。五、ID3谁能让数据变清楚我就先选谁ID3 是决策树里非常经典的算法它使用的是信息增益作为属性选择标准 [1]。第一次学这个概念时真的没必要一上来就背公式。先抓住最朴素的意思就够了信息增益就是看这个属性分完之后数据比原来清楚了多少。什么叫“更清楚”就是看分完以后各个小组里的类别是不是更集中。比如刚才那个例子原来是 6 个通过、4 个未通过混在一起按“实训成绩”分完之后一组 4 通过 1 未通过一组 2 通过 3 未通过这说明分类方向开始显现出来了所以这个属性有价值。而“是否参加培训”那个例子里一组 4 通过 3 未通过一组 2 通过 1 未通过分完以后还是差不多混着说明它没有让数据明显变得更好判断。所以 ID3 的思路可以直接记成哪个属性一分下去能让数据更清楚我就先选哪个。这就是信息增益最大优先。在更正式一点的表述里信息增益表示通过某个属性划分后获得了多少“额外信息”选择信息增益最高的属性就等价于选择当前“最能做出最佳分类”的属性 [1]。ID3 的特点ID3 的好处很明显思路经典非常适合作为入门理解很容易把“选属性”这件事讲明白但它也有明显问题容易偏向取值很多的属性 [1]不支持连续值 [1]不支持缺失值不支持剪枝 [1]换句话说ID3 更像一个“非常经典的第一版”。六、C4.5不能只看分得开还得看你是不是在“钻空子”C4.5 可以理解成 ID3 的升级版。它最核心的改动就是不用信息增益了改用增益率[1]。为什么要改因为 ID3 有一个典型问题它容易偏爱像“学号”这种取值很多的属性。原因很简单。像“学号”这种属性一分就能把人切得很散表面上看数据一下子变得特别清楚。所以从信息增益的角度看它可能得分很高。但这种“高分”其实有点投机。因为它不是在学“哪些学生更容易通过”而是在记“每个人是谁”。所以增益率可以怎么理解可以这样记信息增益看你分开了多少增益率看你分开了多少同时防止你靠“取值太多”占便宜也就是说C4.5 更像是在说我不只看你会不会分还要看你这种分法是不是靠谱。为什么这个改动很重要因为它能避免“学号”这种看起来很厉害、实际上没什么预测意义的属性被优先选中。所以同样面对下面这几个属性实训成绩是否参加培训学号ID3 可能会被“学号”这种属性吸引而 C4.5 会更谨慎更倾向于选“实训成绩”这种真正有业务意义的属性。C4.5 还有哪些改进除了属性选择更合理它还支持连续属性处理 [1]缺失值处理 [1]剪枝 [1]比如“实训成绩 82” 这种连续值C4.5 可以处理学生信息里某一项缺失也能继续工作而且对连续属性C4.5 会先对属性值排序再考察相邻值中点作为候选分裂点选出信息增益最大的分裂点 [1]。所以如果从实际使用角度看C4.5 明显比 ID3 更成熟。七、CART我不管信息增益这些词我只看“纯不纯”如果说 ID3 和 C4.5 更像是在讨论“分完之后信息是不是更清楚”那 CART 的风格就完全不一样了。CART 用的是基尼指数[1]第一次接触时也不用急着记公式。你先把它理解成一句话我只看分完以后每一边是不是更纯。什么叫“纯”纯的意思就是一个结点里的样本尽量大多数都属于同一类。比如9 个通过1 个未通过 → 很纯5 个通过5 个未通过 → 不纯所以基尼指数本质上是在衡量一个结点里的样本混得厉不厉害。越混越不纯越偏向某一类越纯。CART 的想法是什么很简单每次划分都尽量让左右两个子结点更纯。还是拿学生案例来理解。如果按“实训成绩 80”来切左边大多数是通过右边未通过明显变多那 CART 就会觉得这一刀切得不错。八、CART 和前两个还有一个特别重要的区别它是二叉树这是一个很容易被忽略、但特别值得强调的点。ID3 和 C4.5 更像是“多分支”结构。比如一个属性可以直接分成高中低也就是说一次划分可能分出多个分支。但 CART 不一样。CART 每次通常只做一个二选一判断。[1]比如实训成绩 80实训成绩 ≤ 80或者出勤率 90出勤率 ≤ 90所以如果用更通俗的话说ID3 / C4.5 更像多分支的 if 结构CART 更像每次只做一个二选一判断这个理解其实非常重要因为它直接决定了树的形态。所以 CART 可以一句话概括成每次都尽量一分为二而且希望分完之后每一边都更纯。另外在实际编程里Python 的scikit-learn中常用的决策树实现主要就是基于 CART 思路 [1]。九、这里再补一个很容易问到的问题第一步用什么标准后面每一步也还是这个标准吗这个问题其实特别好而且很多人第一次学决策树时都会想到。答案是是的同一种决策树算法在整棵树往下构建的过程中通常都会一直使用同一个属性选择标准。也就是说如果用的是ID3第一层按信息增益选属性后面每一层继续往下分时也还是按信息增益来选 [1]如果用的是C4.5第一层按增益率选属性后面每一层也还是按增益率来选 [1]如果用的是CART第一层按基尼指数来决定怎么分后面每一层也还是按基尼指数来分 [1]这件事很好理解。因为决策树本来就是一个“自顶向下、递归构造”的过程先在全部样本上选一个最合适的属性分裂分完以后每个子结点上又形成新的样本子集再在这个子集上继续按照同样的规则选下一步怎么分一层一层往下长 [1]。不过这里还有一个特别容易误解的地方标准虽然不变但每一层最后选出来的属性不一定一样。为什么因为每一层面对的数据已经不是同一批了。比如在“学生能否通过技能考核”这个案例里第一层面对的是全部学生数据这时候可能“实训成绩”最有区分度但当树往下走到“实训成绩已经比较高”这一支时这个分支里的学生情况已经变了这时候再比较剩下的属性可能“出勤率”就比“是否参加培训”更适合继续分再往下一层可能又会变成别的属性更有用所以更准确地说应该这样理解同一种算法整棵树用的是同一套“选属性标准”但由于每一层面对的是不同的数据子集所以每一层选出来的具体属性可能不同。如果再说得更直白一点就是算法的“打分规则”不变但是每一轮参加比较的数据变了所以最终胜出的属性也可能跟着变这样理解后面看决策树一层层往下长的过程就不会觉得奇怪了。十、三种算法怎么一句话记住学到这里其实可以直接用三句话来区分ID3谁让数据更清楚我先选谁。C4.5谁让数据更清楚而且这种分法更公平我先选谁。CART谁让分完之后每一边更纯我先选谁。如果你更喜欢术语版也可以记成ID3 看信息增益C4.5 看增益率CART 看基尼指数[1]十一、除了选属性标准不同它们还有哪些区别除了分裂标准不同这三种算法还有一些常见区别。ID3多叉树使用信息增益不支持连续值不支持缺失值不支持剪枝 [1]C4.5多叉树使用增益率支持连续值支持缺失值支持剪枝 [1]CART二叉树使用基尼指数支持连续值支持缺失值支持剪枝支持属性多次使用 [1]所以如果整体去看ID3 更像经典入门版C4.5 更像增强版CART 更像工程里最常见的实战版十二、接下来就是另一个关键问题树是不是越深越好很多人学决策树时直觉上会觉得树分得越细应该越准吧如果只看训练集很多时候确实是这样。树越深说明规则越多样本切得越细。这样一来模型会越来越擅长“解释训练数据”。但问题也正出在这里它可能不只是学到了规律还顺手把噪声和偶然情况也记住了。这就是决策树特别容易遇到的问题过拟合可以把过拟合理解成一句特别生活化的话模型把训练数据学得太死了连不该记住的细节也记住了。结果就是训练集效果很好新数据效果反而变差所以树并不是越复杂越好。十三、为什么决策树特别容易过拟合因为决策树本质上就是一个不断划分数据的过程。如果你不限制它它会一直问下去这里还能不能分能分那继续那边还能不能分还能分那继续最后就可能出现一种情况某个叶子结点里只剩下极少数样本甚至只剩 1 个样本这时候模型学到的往往已经不是普遍规律而是训练数据里的个别情况。再加上数据里本来就可能有噪声孤立点偶然异常所以决策树特别容易长着长着就“长歪”。这时候就必须做一件事剪枝十四、什么是剪枝为什么一定要剪枝“剪枝”这个词其实很好理解。你可以把决策树想象成一棵枝叶过于茂盛的树。枝条太多、太细看起来很复杂但不一定真的更好。所以需要做的事情就是把那些没必要、没价值、容易导致过拟合的枝条剪掉。这就是决策树里的剪枝。它的核心目的不是“让树更短”而是让模型对新数据更稳。常见剪枝方式有两种预剪枝后剪枝十五、预剪枝和后剪枝怎么理解最顺手1预剪枝边长边控制预剪枝的思路是还没往下分之前先判断值不值得继续分。如果继续划分带来的提升已经很小甚至低于设定阈值那就不再继续分了 [1]。也就是说树还没完全长出来就先把它控制住所以预剪枝可以理解成边长边刹车。它的优点是效率高。但问题也很明显阈值不太好设。设得太严格树可能学得不够设得太宽松又可能控制不住2后剪枝先长完再回头修后剪枝的思路正好相反先让树长出来再从下往上慢慢修。它的做法通常是先生成完整树再从叶子结点往上回缩比较剪枝前后的效果决定要不要剪掉这一部分 [1]所以后剪枝更像是先长完再回头修枝。这种方式通常更稳因为它是在看完整棵树之后再决定怎么修。十六、实际写代码时最常见的“剪枝替代手段”是什么在真正写代码时我们很多时候不会自己手工实现完整的剪枝算法而是直接通过模型参数去限制树的复杂度。在DecisionTreeClassifier里最常见的几个参数就是criterion划分标准可选gini或entropy[1]max_depth树的最大深度 [1]min_samples_leaf叶子结点最少样本数 [1]其中最常用、也最好理解的就是max_depth它的作用就是不让树无限往下长到了指定深度就停下来所以在很多实际任务里max_depth就是最常见的“防止树长歪”的参数。十七、先看一个简单实操比较不同划分标准的树下面先用sklearn自带的鸢尾花数据集简单演示一下不同划分标准下的决策树效果。fromsklearn.datasetsimportload_irisfromsklearn.model_selectionimporttrain_test_splitfromsklearn.treeimportDecisionTreeClassifier irisload_iris()X_train,X_test,y_train,y_testtrain_test_split(iris.data,iris.target,test_size0.2,random_state42)clf_giniDecisionTreeClassifier(criteriongini,max_depth3,random_state42)clf_gini.fit(X_train,y_train)clf_entropyDecisionTreeClassifier(criterionentropy,max_depth3,random_state42)clf_entropy.fit(X_train,y_train)print(Gini准确率,clf_gini.score(X_test,y_test))print(Entropy准确率,clf_entropy.score(X_test,y_test))这段代码最重要的地方在于它把前面讲过的概念真正对应到了参数上gini对应基尼指数entropy对应信息熵max_depth对应控制树深度概念和代码到这里就真正连上了。十八、再把学生案例完整跑一遍前面一直在讲“学生技能考核预测”下面就直接用这组模拟数据把它跑通。importpandasaspdfromsklearn.model_selectionimporttrain_test_splitfromsklearn.treeimportDecisionTreeClassifier# 1. 构造学生数据data{平时成绩:[82,76,90,60,71,88,67,95,78,85,69,92],实训成绩:[85,70,92,58,75,90,65,96,80,87,68,94],出勤率:[96,85,98,70,80,97,75,99,90,93,78,98],是否通过:[通过,未通过,通过,未通过,通过,通过,未通过,通过,通过,通过,未通过,通过]}dfpd.DataFrame(data)print(原始数据)print(df)# 2. 提取特征和标签Xdf[[平时成绩,实训成绩,出勤率]]ydf[是否通过]# 3. 划分训练集和测试集X_train,X_test,y_train,y_testtrain_test_split(X,y,test_size0.25,random_state42)# 4. 使用基尼指数构建决策树clf_giniDecisionTreeClassifier(criteriongini,max_depth3,random_state42)clf_gini.fit(X_train,y_train)# 5. 使用信息熵构建决策树clf_entropyDecisionTreeClassifier(criterionentropy,max_depth3,random_state42)clf_entropy.fit(X_train,y_train)# 6. 模型评估print(\nGini准确率,clf_gini.score(X_test,y_test))print(Entropy准确率,clf_entropy.score(X_test,y_test))# 7. 预测新学生new_student[[79,82,91]]pred_giniclf_gini.predict(new_student)pred_entropyclf_entropy.predict(new_student)print(\n新学生Gini预测结果,pred_gini[0])print(新学生Entropy预测结果,pred_entropy[0])这段代码的意义很直接用学生数据构造一个分类任务分别用两种标准训练树比较模型效果再预测一个新学生这时候前面讲的那些概念就都不再是空的了。十九、这一课最值得记住的几个点学完这一篇建议把下面这些点真正记住。1决策树为什么不能随便分因为第一步分错了后面整棵树都可能长得不理想。2ID3、C4.5、CART 的根本区别是什么ID3看信息增益C4.5看增益率CART看基尼指数3为什么“学号”这种属性会误导算法因为它能把训练样本切得很散但这种分法没有真正的规律价值。4为什么树太深会有问题因为它可能把训练数据里的噪声和特殊情况也一起记住。5剪枝是干什么的控制树的复杂度防止过拟合。6代码里最常用来控树的参数是什么max_depth二十、结尾总结这一篇文章重点其实就解决了两个问题决策树的第一步到底先看哪个条件树到底是不是越复杂越好顺着这两个问题就把整件事串起来了决策树构建时属性选择非常重要ID3、C4.5、CART 都是在解决“先选哪个属性”的问题它们的区别主要在于判断标准不同 [1]ID3、C4.5 更像多分支结构CART 更像二叉判断结构 [1]树太深容易过拟合所以需要剪枝在实际编程里可以通过max_depth等参数来控制树的复杂度 [1]如果说上一篇文章解决的是决策树是什么那么这一篇真正解决的就是决策树到底怎么长出来又怎么防止它长歪。把这一步想明白后面再学随机森林就会顺很多。因为随机森林本质上就是“一棵树不够稳那就很多棵树一起判断”。二十一、课后思考可以试着回答下面几个问题为什么决策树第一步不能随便选一个属性来分ID3、C4.5、CART 的核心区别到底是什么为什么“学号”这种属性看起来很会分却不一定有用为什么树太深反而可能效果变差在代码里max_depth为什么这么重要如果这些问题都能比较顺畅地说清楚那这一块就算真正入门了。写在最后决策树真正有意思的地方不只是它“长得像树”而是它背后那套“怎么选、怎么分、怎么控”的思路。从“认识决策树”到“理解决策树怎么构建”这一步其实非常关键。接下来最自然的下一站就是随机森林一棵树不够稳那就很多棵树一起投票模型评估模型到底好不好不能只靠感觉