1. 为什么决策树值得单独开一篇:从"可解释"说起
先抛一个观点:如果你是做业务的,或者未来要和业务方打交道,决策树很可能是你机器学习生涯里第一个能真正"讲得出口"的模型。神经网络再强,你跟运营说"这是个带注意力机制的多层感知机",对方大概只会礼貌地点点头,然后继续问你"那你说说它为什么判定这个用户会流失"。这时候决策树的价值就出来了——它能把判断逻辑变成一串"如果...那么..."的规则,每一层分裂都对应一个看得懂的条件,任何一个没有算法基础的人都能顺着树走一遍,走到哪个叶子就得到哪个结论。这种可解释性,在风控、医疗、信贷、故障诊断这些需要给结论背书的场景里,是硬需求。
作为机器学习系列教程的第五篇,这篇不再讲线性模型的那套梯度下降和损失函数了。决策树走的完全不是同一个路子,它的核心是"组织结构"而不是"数学拟合"。说得更直白一点:线性回归和逻辑回归在干的事,是找一条线或者一个面把数据分开;而决策树干的事,是把数据一层一层地切格子,切到最后每个格子里尽量只住着一类样本。它从头到尾没有"权重"这个概念,也没有梯度,用的是另一套数学工具——信息论里的熵、增益、基尼系数。这套工具不复杂,但很多人学完只会调sklearn的参,不知道树是怎么长出来的,也不知道为什么要剪枝,更不知道面对连续特征和缺失值时算法背后发生了什么。
这篇文章的目标读者,是已经看完本系列前面几篇、有Python基础、但还没系统学过树模型的同学。我会从"树是怎么长出来的"这个最底层的问题开始,把特征选择、三种主流算法、训练与预测的完整流程、剪枝的必要性,以及连续值和缺失值这些实际落地时才躲不开的问题,全部串起来讲一遍。每个环节不光讲"是什么",还会解释"为什么这么设计"和"实际用的时候要注意什么"。建议你打开一个Jupyter Notebook跟着后面那段代码跑一遍,跑了之后很多模糊的地方会自动变得清晰。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 特征选择的内功心法:信息熵、信息增益与基尼指数
2.1 信息熵:度量"一团乱麻"到底有多乱
决策树在每一个节点上都要回答一个问题:我应该用哪个特征来切分当前这批数据?这个选择不是拍脑袋定的,得有一个量化的标准,让它能看出"切完之后比切之前更纯了"还是"更乱了"。这个标准的基础就是信息熵。
信息熵这个概念,可以用一个猜球的游戏来理解。假设有一个箱子,里面有10个球,只有红球这一种颜色。你随手摸出一个球,不用猜就知道是红的,因为根本没有任何不确定性,这时的熵最低,等于0。再换一个箱子,5个红球5个蓝球,你摸之前要猜一下颜色,不确定程度就上来了,熵变大。如果箱子变成10种颜色各1个球,那猜中的难度直线上升,熵也更大。
信息熵的数学定义是:
[ H = -\sum_{i=1}^{n} p_i \log_2 p_i ]
其中 ( p_i ) 是第 ( i ) 类样本在数据集里的比例。这个式子不要被符号吓住,你只要抓住一个感觉:取值越多样、各类比例越接近,熵越大;样本越清一色,熵越小。比如一个二分类数据集,如果正负样本各占一半,那么 ( H = -0.5\log_2 0.5 - 0.5\log_2 0.5 = 1 ),这是二分类问题熵的上限。如果正样本占到90%,负样本只占10%,那么 ( H = -0.9\log_2 0.9 - 0.1\log_2 0.1 \approx 0.469 ),明显变小了。
提示:有些资料会把 ( \log_2 ) 换成自然对数 ( \ln ),这只是度量尺度不同,不影响特征选择时的相对比较。实际操作中我们关心的不是熵的绝对值,而是"分裂前和分裂后熵差了多少"。
我在带新人做项目时,发现很多人在这一步就卡住了,总觉得要"理解"熵的哲学含义才能继续往下学。其实不必,你就把它当成一把尺子,尺子上的读数代表"混乱程度",接下来要做的所有事情都是围绕"哪个特征能让混乱程度降得最快"展开的。
2.2 信息增益:选特征就像玩"排除法"猜人
有了熵这把尺子,现在可以来选特征了。决策树的核心逻辑是:选择一个特征,把当前数据集按这个特征的取值分成几份,然后分别计算每个子集的熵,再按样本量加权平均,得到"分裂后的总熵"。用分裂前的熵减去分裂后的熵,得到的就是这个特征带来的"信息增益"。增益越大,说明这个特征切完之后数据变纯的程度越高,它就应该被优先选为分裂特征。
用一个经典的"今天要不要出门打球"数据集来走一遍计算,数据长这样:
| 天气 | 温度 | 湿度 | 风 | 是否打球 |
|---|---|---|---|---|
| 晴 | 热 | 高 | 无 | 否 |
| 晴 | 热 | 高 | 有 | 否 |
| 多云 | 热 | 高 | 无 | 是 |
| 雨 | 温 | 高 | 无 | 是 |
| 雨 | 冷 | 正常 | 无 | 是 |
| 雨 | 冷 | 正常 | 有 | 否 |
| 多云 | 冷 | 正常 | 有 | 是 |
| 晴 | 温 | 高 | 无 | 否 |
| 晴 | 冷 | 正常 | 无 | 是 |
| 雨 | 温 | 正常 | 无 | 是 |
| 晴 | 温 | 正常 | 有 | 是 |
| 多云 | 温 | 高 | 有 | 是 |
| 多云 | 热 | 正常 | 无 | 是 |
| 雨 | 温 | 高 | 有 | 否 |
一共14条样本,打球的有9个,不打球的有5个,根节点的熵是:
[ H(\text{根}) = -\frac{9}{14}\log_2\frac{9}{14} - \frac{5}{14}\log_2\frac{5}{14} \approx 0.940 ]
现在看"天气"特征的划分。天气取"晴"时有5条样本,其中2个打球3个不打球,子集熵 ( H(\text{晴}) \approx 0.971 );取"多云"时有4条,全打球,子集熵 ( H(\text{多云}) = 0 );取"雨"时有5条,3个打球2个不打球,子集熵 ( H(\text{雨}) \approx 0.971 )。按样本量加权之后,天气特征分裂后的总熵是:
[ \frac{5}{14} \times 0.971 + \frac{4}{14} \times 0 + \frac{5}{14} \times 0.971 \approx 0.694 ]
信息增益:
[ \text{Gain}(\text{天气}) = 0.940 - 0.694 = 0.
