共计 2167 个字符,预计需要花费 6 分钟才能阅读完成。
背景与核心概念
在分类问题中,线性模型和树模型各有优劣。线性模型(如逻辑回归)适合处理特征间存在线性关系的数据,但当数据存在复杂非线性关系时,决策树的优势就显现出来了。决策树通过递归分割数据空间,能够捕捉复杂的特征交互作用,且模型结果易于解释。

然而,初学者在使用决策树时常会遇到两个典型问题:
- 模型过拟合:决策树会不断生长直到完美拟合训练数据,导致在测试集上表现不佳
- 特征重要性误判:由于算法贪婪特性,可能高估某些偶然性特征的贡献度
算法原理与对比
关键数学概念
CART(Classification And Regression Trees) 使用基尼系数作为分割标准,其定义为:
$$Gini(p) = 1 – \sum_{k=1}^K p_k^2$$
其中 $p_k$ 是第 k 类样本在节点中的比例。基尼系数越小,表示节点纯度越高。
相比 ID3 和 C4.5 算法:
- ID3 使用信息增益,倾向于选择取值多的特征
- C4.5 使用信息增益比,解决了 ID3 的偏好问题
- CART 使用基尼系数,计算效率更高且可处理连续特征
参数影响可视化
通过调整 max_depth 参数,可以直观看到模型复杂度的变化:
from sklearn.tree import DecisionTreeClassifier, plot_tree
import matplotlib.pyplot as plt
# 生成不同深度的树
for depth in [2, 4, 6]:
clf = DecisionTreeClassifier(max_depth=depth)
clf.fit(X_train, y_train)
plt.figure(figsize=(12, 8))
plot_tree(clf, filled=True)
plt.title(f'Max Depth = {depth}')
plt.show()
核心代码实现
基尼系数计算
使用 numpy 实现向量化计算,提升性能:
import numpy as np
def gini_impurity(y: np.ndarray) -> float:
"""计算基尼不纯度"""
if len(y) == 0:
return 0
p = np.bincount(y) / len(y)
return 1 - np.sum(p**2)
树节点结构设计
定义二叉树节点类,包含关键分裂信息:
class TreeNode:
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 # 叶节点预测值
生产环境优化
sklearn 参数调优
关键参数说明:
max_depth: 控制树的最大深度min_samples_split: 节点最小样本数min_impurity_decrease: 分裂最小纯度提升
使用网格搜索寻找最优参数组合:
from sklearn.model_selection import GridSearchCV
param_grid = {'max_depth': [3, 5, 7],
'min_samples_split': [2, 5, 10],
'min_impurity_decrease': [0, 0.01, 0.1]
}
grid_search = GridSearchCV(DecisionTreeClassifier(),
param_grid,
cv=5,
scoring='accuracy'
)
grid_search.fit(X_train, y_train)
print("Best parameters:", grid_search.best_params_)
常见问题解决方案
类别不平衡处理
设置 class_weight 参数自动调整类别权重:
# 自动根据类别频率调整权重
clf = DecisionTreeClassifier(class_weight='balanced')
# 手动指定类别权重
clf = DecisionTreeClassifier(class_weight={0:1, 1:5})
特征重要性分析
通过 feature_importances_属性识别无效特征:
import pandas as pd
clf.fit(X_train, y_train)
feat_importances = pd.Series(
clf.feature_importances_,
index=feature_names
).sort_values(ascending=False)
# 可视化特征重要性
feat_importances.plot(kind='barh')
plt.title('Feature Importances')
plt.show()
延伸思考与改进方向
- 如何处理连续特征缺失值?可以考虑用特征分布的中位数填充
- 如何提升模型对噪声数据的鲁棒性?尝试实现代价敏感学习
- 当特征维度很高时,如何加速训练过程?研究特征预筛选策略
通过本文的实践,你应该已经掌握了 CART 决策树的核心原理和实现方法。建议读者在理解这些基础后,可以进一步探索随机森林、GBDT 等集成树模型,它们能有效改善单棵决策树的局限性。
正文完
