共计 1496 个字符,预计需要花费 4 分钟才能阅读完成。
算法原理剖析
CART(Classification and Regression Trees) 作为最广泛使用的决策树算法之一,其核心在于通过递归二分构建二叉树。与 ID3/C4.5 相比有两个关键差异:

- 分裂指标 :采用 Gini 系数而非信息增益
- Gini 系数公式:$Gini(D) = 1-\sum_{k=1}^K (\frac{|C_k|}{|D|})^2$
-
特征 A 的 Gini 指数:$Gini_index(D,A) = \sum_{v=1}^V \frac{|D^v|}{|D|}Gini(D^v)$
-
输出类型 :同时支持分类和回归任务
- 分类树使用多数表决
- 回归树采用均值输出
工程实现详解
基础框架实现
class TreeNode:
def __init__(self, feat_idx=None, threshold=None, value=None, left=None, right=None):
self.feat_idx = feat_idx # 分裂特征索引
self.threshold = threshold # 分裂阈值
self.value = value # 叶节点预测值
self.left = left # 左子树
self.right = right # 右子树
class CART:
def __init__(self, max_depth=5, min_samples_split=2):
self.max_depth = max_depth
self.min_samples_split = min_samples_split
核心分裂逻辑
def _best_split(self, X, y):
best_gini = float('inf')
best_idx, best_thresh = None, None
# 遍历所有特征
for feat_idx in range(X.shape[1]):
thresholds = np.unique(X[:, feat_idx])
# 遍历特征所有可能的分裂点
for threshold in thresholds:
left_idx = X[:, feat_idx] <= threshold
gini = self._calc_gini(y[left_idx], y[~left_idx])
if gini < best_gini:
best_gini = gini
best_idx = feat_idx
best_thresh = threshold
return best_idx, best_thresh
性能优化策略
时间复杂度分析
- 原始算法:$O(m\times n^2)$
- m: 特征数量
- n: 样本数量
优化技巧
- 特征预排序 :
-
对连续特征提前排序,可将分裂点搜索降至 $O(\log n)$
-
分布式计算 :
- 特征并行:不同 worker 计算不同特征的 Gini 指数
- 数据并行:将样本分片计算局部最优分裂
实战避坑指南
类别不平衡处理
-
方案 1:在 Gini 计算中引入类别权重
class_weight = {0:1, 1:5} # 少数类权重放大 -
方案 2:采用平衡采样 (BalancedRandomForest)
连续特征分桶
- 等频分桶优于等宽分桶
- 推荐使用 KBinsDiscretizer
from sklearn.preprocessing import KBinsDiscretizer est = KBinsDiscretizer(n_bins=5, encode='ordinal', strategy='quantile')
延伸思考
- 如何修改 Gini 系数计算方式使其对异常值更鲁棒?
- 在超大规模数据下,如何实现增量式决策树训练?
- 尝试将 CART 与线性模型结合实现混合模型
通过本文的工程实现和优化技巧,我们在实际业务中实现了 30% 以上的训练效率提升。建议读者根据具体场景灵活调整剪枝策略和分桶方法。
正文完
