共计 2974 个字符,预计需要花费 8 分钟才能阅读完成。
核心概念
决策树是机器学习中最直观的分类工具之一,它通过一系列简单的 if-else 规则模拟人类决策过程,特别适合处理结构化数据和非线性关系。对于初学者来说,理解其核心分裂指标和构建流程是掌握该算法的关键。

基尼系数 vs 信息增益
-
基尼系数 (Gini Index):衡量数据集不纯度的指标,值越小表示纯度越高。计算公式为:
def gini_index(groups, classes): n_instances = sum(len(group) for group in groups) gini = 0.0 for group in groups: size = len(group) if size == 0: continue score = 0.0 for class_val in classes: p = [row[-1] for row in group].count(class_val) / size score += p * p gini += (1.0 - score) * (size / n_instances) return gini -
信息增益 (Information Gain):基于信息熵的指标,计算复杂度略高于基尼系数
实际应用中,基尼系数通常作为默认选择,因为:
1. 计算速度更快(无需对数运算)
2. 对类别分布差异更敏感
3. 在 sklearn 等库中默认使用
过拟合的典型症状
- 训练集准确率接近 100% 但测试集表现骤降
- 决策路径出现明显不合理的长链条
- 对微小数据变化极度敏感
算法实现
CART 二叉树构建流程
- 从根节点开始,计算当前节点的基尼不纯度
- 遍历所有特征及其可能的分裂点
- 选择使子节点加权不纯度下降最多的分裂方案
- 递归处理子节点直到满足停止条件
图示说明:
[根节点]
/ \
[特征 A≤0.5] [特征 A >0.5]
/ \ / \
[类 1] [类 2] [类 3] [继续分裂]
完整 Python 实现
class DecisionNode:
def __init__(self, feature_idx=None, threshold=None,
left=None, right=None, value=None):
self.feature_idx = feature_idx # 分裂特征索引
self.threshold = threshold # 分裂阈值
self.left = left # 左子树
self.right = right # 右子树
self.value = value # 叶节点预测值
class CARTClassifier:
def __init__(self, max_depth=5):
self.max_depth = max_depth
def _gini(self, y):
m = y.size
return 1.0 - sum((np.sum(y == c) / m) ** 2 for c in np.unique(y))
def _best_split(self, X, y):
best_gini = float('inf')
best_idx, best_thr = None, None
for idx in range(X.shape[1]):
thresholds = np.unique(X[:, idx])
for thr in thresholds:
left_mask = X[:, idx] <= thr
gini = (left_mask.sum() * self._gini(y[left_mask]) +
(~left_mask).sum() * self._gini(y[~left_mask])) / y.size
if gini < best_gini:
best_gini = gini
best_idx = idx
best_thr = thr
return best_idx, best_thr
def fit(self, X, y, node=None, depth=0):
if node is None:
self.tree = DecisionNode()
node = self.tree
if depth >= self.max_depth or len(np.unique(y)) == 1:
node.value = np.argmax(np.bincount(y))
return
idx, thr = self._best_split(X, y)
if idx is None:
node.value = np.argmax(np.bincount(y))
return
left_mask = X[:, idx] <= thr
node.feature_idx = idx
node.threshold = thr
node.left = DecisionNode()
node.right = DecisionNode()
self.fit(X[left_mask], y[left_mask], node.left, depth+1)
self.fit(X[~left_mask], y[~left_mask], node.right, depth+1)
优化策略
剪枝技术对比
预剪枝 (Pre-pruning):
# sklearn 实现示例
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier(
max_depth=3,
min_samples_leaf=5,
min_impurity_decrease=0.01
)
后剪枝 (Post-pruning):
def prune(node, X_val, y_val):
if node.value is not None:
return node.value == y_val.mean()
left_mask = X_val[:, node.feature_idx] <= node.threshold
# 剪枝判断条件
if (prune(node.left, X_val[left_mask], y_val[left_mask]) and
prune(node.right, X_val[~left_mask], y_val[~left_mask])):
node.value = np.argmax(np.bincount(y_val))
return True
return False
连续特征处理
- 等宽分箱:
pd.cut(feature, bins=10) - 等频分箱:
pd.qcut(feature, q=10) - 基于分裂点动态离散化(CART 原生支持)
类别不平衡处理
# sklearn 中的类别权重设置
clf = DecisionTreeClassifier(class_weight={0:1, 1:5} # 少数类样本权重放大
)
生产实践
特征重要性计算
基尼重要性公式:
特征 j 的重要性 = ∑(节点 k 的样本占比 × ΔGini_k)
sklearn 实现:
importance = clf.feature_importances_
部署注意事项
- 对连续特征进行标准化处理(0- 1 归一化)
- 限制树深度防止 API 响应超时
- 添加特征缺失值的默认处理分支
延伸思考
-
特征重要性解释 :基尼系数下降越多的特征,对分类决策贡献越大。但需要注意高基数类别特征可能带来虚假重要性
-
决策树劣势场景 :
- 特征间存在强线性关系
- 数据维度极高且样本稀疏
-
需要概率输出时(相比逻辑回归)
-
进阶方向 :
- 尝试用 Graphviz 可视化决策路径
- 研究 GBDT/XGBoost 等集成方法如何提升单棵树的表现
决策树就像数据科学的瑞士军刀——简单但功能强大。理解其核心机制后,你可以更自信地调整参数和应对各种数据挑战。试着用本文代码在 Kaggle 数据集上实践,观察不同参数如何影响模型表现吧!
正文完
