共计 2648 个字符,预计需要花费 7 分钟才能阅读完成。
引言
决策树是机器学习中最直观且易于解释的算法之一,广泛应用于分类和回归任务。从信用卡审批到医疗诊断,决策树因其规则明确、可解释性强而备受青睐。本文将从 CART 算法的基础理论出发,逐步构建完整的决策树实现,并分享实战中的调优技巧。

数学原理:基尼系数与信息增益
基尼系数
基尼系数(Gini Index)是 CART 算法用于分类问题的纯度衡量标准,计算公式为:
$$ Gini(p) = 1 – \sum_{k=1}^{K} p_k^2 $$
其中 $p_k$ 是样本中第 $k$ 类所占的比例。基尼系数越小,数据纯度越高。
信息增益
信息增益(Information Gain)基于信息熵的概念,计算公式为:
$$ Entropy(D) = -\sum_{k=1}^{K} p_k \log_2 p_k $$
信息增益表示特征 A 对训练集 D 的信息增益:
$$ Gain(D,A) = Entropy(D) – \sum_{v=1}^{V} \frac{|D^v|}{|D|} Entropy(D^v) $$
两者对比
- 基尼系数计算更快(无对数运算)
- 信息增益对不纯度更敏感
- 实际应用中两者结果通常相近
算法实现:递归构建决策树
- 从根节点开始,计算所有特征的信息增益 / 基尼系数
- 选择最佳划分特征
- 对每个特征值创建子节点
- 递归处理子节点,直到:
- 节点样本全属同一类
- 无更多特征可用
- 达到预设终止条件
伪代码实现:
function build_tree(data):
if 终止条件满足:
return 叶子节点
选择最佳划分特征
根据特征值划分数据集
for 每个子集:
build_tree(子集)
Python 完整实现
import numpy as np
from collections import Counter
class DecisionTree:
def __init__(self, max_depth=5, min_samples_split=2):
self.max_depth = max_depth
self.min_samples_split = min_samples_split
def gini(self, y):
counts = Counter(y)
return 1 - sum((count/len(y))**2 for count in counts.values())
def best_split(self, X, y):
best_gini = float('inf')
best_feature, best_value = None, None
for feature in range(X.shape[1]):
values = np.unique(X[:, feature])
for value in values:
left_idx = X[:, feature] <= value
left_gini = self.gini(y[left_idx])
right_gini = self.gini(y[~left_idx])
weighted_gini = (left_gini * sum(left_idx) +
right_gini * sum(~left_idx)) / len(y)
if weighted_gini < best_gini:
best_gini = weighted_gini
best_feature = feature
best_value = value
return best_feature, best_value
def fit(self, X, y, depth=0):
if (depth >= self.max_depth or
len(y) < self.min_samples_split or
len(set(y)) == 1):
self.label = Counter(y).most_common(1)[0][0]
return
self.feature, self.value = self.best_split(X, y)
left_idx = X[:, self.feature] <= self.value
self.left = DecisionTree(self.max_depth, self.min_samples_split)
self.right = DecisionTree(self.max_depth, self.min_samples_split)
self.left.fit(X[left_idx], y[left_idx], depth+1)
self.right.fit(X[~left_idx], y[~left_idx], depth+1)
def predict(self, X):
if hasattr(self, 'label'):
return self.label
if X[self.feature] <= self.value:
return self.left.predict(X)
else:
return self.right.predict(X)
模型调优与评估
防止过拟合的方法
- 预剪枝(Pre-pruning):
- 限制树的最大深度
- 设置节点最小样本数
-
设定信息增益阈值
-
后剪枝(Post-pruning):
- 用验证集评估剪枝效果
- 递归地从叶子节点向上剪枝
交叉验证技巧
from sklearn.model_selection import GridSearchCV
params = {'max_depth': [3, 5, 7],
'min_samples_split': [2, 5, 10]
}
grid_search = GridSearchCV(DecisionTree(), params, cv=5)
grid_search.fit(X_train, y_train)
生产环境最佳实践
处理类别不平衡
- 使用 class_weight 参数调整类别权重
- 对少数类样本过采样
- 采用 AUC-ROC 作为评估指标
特征工程建议
- 离散型特征建议 one-hot 编码
- 连续型特征考虑分箱处理
- 缺失值处理:
- 数值特征用中位数填充
- 类别特征用众数填充
常见问题解答
Q:为什么我的决策树在测试集表现很差?
A:可能原因包括:
1. 过拟合(尝试剪枝)
2. 特征相关性低(检查特征重要性)
3. 数据量不足(增加样本或使用集成方法)
Q:如何解释决策树的预测结果?
A:可以可视化决策路径:
from sklearn.tree import export_text
print(export_text(tree_model, feature_names=feature_names))
结语
通过本文的详细讲解和代码实现,相信您已经掌握了 CART 决策树的核心要点。建议在实践中多尝试不同的参数组合,并关注模型的可解释性。决策树作为基础算法,也是随机森林、GBDT 等强大模型的基础,值得深入学习。
正文完
