共计 1715 个字符,预计需要花费 5 分钟才能阅读完成。
背景痛点
决策树在业务场景中广泛应用,如金融风控、医疗诊断等,因其可解释性强、对数据分布要求低而备受青睐。然而,在实际使用 CART(Classification and Regression Trees)算法时,开发者常遇到以下典型问题:

- 连续值处理 :如何高效找到最佳分割点?
- 剪枝策略选择 :预剪枝容易欠拟合,后剪枝计算成本高
- 过拟合风险 :树深度控制与样本量不匹配导致泛化性差
算法解析
Gini 系数计算
Gini 系数衡量节点纯度,计算公式为:
$$ Gini(p) = 1 – \sum_{k=1}^{K}p_k^2 $$
其中 $p_k$ 是第 k 类样本的占比。分裂时选择使 Gini 下降最大的特征:
$$ \Delta Gini = Gini(D) – \sum_{v=1}^{V}\frac{|D^v|}{|D|}Gini(D^v) $$
与 ID3/C4.5 的差异
- 分裂方式 :CART 强制生成二叉树,ID3/C4.5 允许多叉树
- 特征标准 :CART 使用 Gini 系数,ID3 用信息增益,C4.5 用增益率
- 输出类型 :CART 可同时处理分类和回归任务
剪枝策略
- 预剪枝 :在分裂前通过参数控制(如 max_depth)
- 后剪枝 :生成完整树后,用验证集自底向上剪枝
代码实现
核心分裂函数
def find_best_split(X, y):
best_gini = float('inf')
best_feature, best_threshold = None, None
for feature_idx in range(X.shape[1]):
# 对连续值特征排序
thresholds = np.unique(X[:, feature_idx])
for threshold in thresholds:
left_mask = X[:, feature_idx] <= threshold
gini = weighted_gini(y[left_mask], y[~left_mask])
if gini < best_gini:
best_gini = gini
best_feature = feature_idx
best_threshold = threshold
return best_feature, best_threshold
可视化决策边界
from sklearn.tree import plot_tree
import matplotlib.pyplot as plt
plt.figure(figsize=(12,8))
plot_tree(model, filled=True, feature_names=feature_names)
plt.show()
实战例题(乳腺癌数据集)
数据预处理
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
data = load_breast_cancer()
X, y = data.data, data.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
模型训练与评估
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import classification_report
model = DecisionTreeClassifier(criterion='gini', max_depth=3)
model.fit(X_train, y_train)
print(classification_report(y_test, model.predict(X_test)))
生产建议
超参数调优指南
- max_depth:通常 3 -10 层,通过交叉验证选择
- min_samples_split:建议≥20 防止过拟合
- class_weight:处理不平衡数据时设为 ’balanced’
进阶技巧
- 特征工程 :对连续特征做分箱处理
- 集成学习 :作为随机森林的基学习器
- 部署优化 :转换为 if-else 规则提升推理速度
思考题
- 当特征同时包含连续值和类别值时,应如何修改分裂逻辑?
- 如何证明后剪枝比预剪枝更能提升模型泛化能力?
正文完
