1. 先从直觉说起:决策树到底在做什么
很多人一听到“决策树”三个字,第一反应是“又是机器学习里一个高大上的算法”,第二反应是“肯定要推一堆数学公式”。说实话,我刚开始学的时候也是这样想的,直到我真正把它用到业务里之后,才发现一个真相:决策树可能是所有机器学习算法里最贴近人类思考方式的一个,它本质上就是“一连串 if-else 规则的集合”。
什么叫 if-else 规则?举个例子,你在网上买衣服,判断一件衣服值不值得买,你可能会先看价格,超过预算直接放弃;价格合适再看尺码,没有自己的码数也放弃;尺码合适再看颜色和款式,都满意就下单。这个过程就是一个决策树:每一步都问一个问题,根据答案走向不同的分支,最终到达一个结论。决策树做的,就是把这个人类纯直觉的判断过程,转化为一套可以自动化执行的规则体系。
从算法层面来说,决策树的训练过程就是回答三个问题:
第一,每一步应该用哪个特征来划分数据?第二,这个特征应该按什么阈值来切?第三,什么时候停下来,生成一个叶子节点?
这三个问题看起来简单,但背后涉及信息熵、信息增益、基尼系数这些概念。而这些概念指向的终极目标只有一个:找到一种划分方式,让数据被切分之后,每个子集内部的样本类别越来越纯。纯的意思就是,同一堆数据里尽量都是同一个种类的东西。
谁适合把决策树学透?我觉得所有刚开始接触机器学习的人,都应该先把它作为第一个真正动手去拆解的算法。原因也很直接:它不像神经网络那样是端到端的黑盒,它的每一步决策你都能解释清楚,每个节点的含义你都能说出来。而且从实操角度来看,决策树是后面很多高级算法的地基,像随机森林、GBDT、XGBoost、LightGBM,内部核心组件都是一棵棵决策树。搞不懂单棵树,后面玩集成学习基本等于空中楼台。
这篇文章,我打算从决策树的原理推导、特征选择逻辑、剪枝策略、回归场景、与随机森林的关系、以及一份可以直接跑的 Python 代码案例,完整地拆一遍。你不需要有很高深的数学基础,能用 python 写基本循环就行,跟着一步步来,一定会比你在书上看十遍公式管用得多。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 特征选择的核心逻辑:从信息熵到信息增益
2.1 信息熵:衡量数据混乱程度的尺子
要理解决策树怎么选特征,得先理解一个概念:信息熵。
信息熵这个概念来自信息论,天才人物香农在1948年提出了它,用来量化一份数据的“不确定性”。打个比方,一个袋子里全部装的是红球,你伸手进去摸一个球,不用看就知道是红球,没有任何不确定性,这时候熵是0。如果袋子里红球和白球各占一半,你猜对颜色就没那么容易了,这时候不确定性就高,熵也就大。
信息熵的计算公式是:
code复制H(D) = -∑(pk * log2(pk))
其中 pk 表示第 k 类样本在数据集 D 中占的比例。我们通常用 log2 来计算,单位是比特(bit)。熵值越小,说明数据越纯;熵值越大,说明数据越混乱。
举个例子。一个数据集里有 10 个样本,其中 5 个是猫,5 个是狗。那么:
code复制H(D) = -(5/10 * log2(5/10) + 5/10 * log2(5/10))
= -(0.5 * (-1) + 0.5 * (-1))
= 1
这个数据集的熵就是 1 bit。如果 10 个样本里 9 个是猫、1 个是狗,那么:
code复制H(D) = -(9/10 * log2(9/10) + 1/10 * log2(1/10))
≈ -(0.9 * (-0.152) + 0.1 * (-3.322))
≈ 0.469
可以看到,数据越“偏”,熵越小,不确定性越低。这就是熵的直观含义。
2.2 信息增益:选择特征的评判标准
现在我们知道了怎么度量一份数据的混乱程度,下一步就是怎么评判一个特征好不好。
理想的特征是:我用了这个特征把数据划分之后,各个子集的熵之和(按样本量加权平均)显著变小了。这个“熵的减少量”就叫信息增益。
信息增益的计算公式是:
code复制Gain(D, A) = H(D) - ∑( |Dv| / |D| * H(Dv) )
其中 A 是我们要评估的特征,v 表示特征 A 的每一个取值,Dv 是按照特征 A=v 划分出来的子集。
我拿一个经典场景来说:根据天气条件判断要不要出门打球。训练数据有14个样本,特征包括天气(晴、阴、雨)、温度(高、中、低)、湿度(高、正常)、风(有、无),标签是“打球”和“不打球”。
数据是这样的:
| 序号 | 天气 | 温度 | 湿度 | 风 | 打球 |
|---|---|---|---|---|---|
| 1 | 晴 | 高 | 高 | 无 | 否 |
| 2 | 晴 | 高 | 高 | 有 | 否 |
| 3 | 阴 | 高 | 高 | 无 | 是 |
| 4 | 雨 | 中 | 高 | 无 | 是 |
| 5 | 雨 | 低 | 正常 | 无 | 是 |
| 6 | 雨 | 低 | 正常 | 有 | 否 |
| 7 | 阴 | 低 | 正常 | 有 | 是 |
| 8 | 晴 | 中 | 高 | 无 | 否 |
| 9 | 晴 | 低 | 正常 | 无 | 是 |
| 10 | 雨 | 中 | 正常 | 无 | 是 |
| 11 | 晴 | 中 | 正常 | 有 | 是 |
| 12 | 阴 | 中 | 高 | 有 | 是 |
| 13 | 阴 | 高 | 正常 | 无 | 是 |
| 14 | 雨 | 中 | 高 | 有 | 否 |
14个样本里,打球的有9个,不打球的有5个。总体熵为:
code复制H(D) = -(9/14 * log2(9/14) + 5/14 * log2(5/14))
≈ -(0.643 * (-0.637) + 0.357 * (-1.485))
≈ 0.940
好,现在我们计算一下“天气”这个特征的信息增益。天气有晴、阴、雨三种取值:
-
晴:5个样本(编号1、2、8、9、11),其中打球2个,不打球3个。这个子集的熵是:
code复制H(晴) = -(2/5 * log2(2/5) + 3/5 * log2(3/5)) ≈ 0.971 -
阴:4个样本(编号3、7、12、13),全部打球。熵为 0。
-
雨:5个样本(编号4、
