深入解析CART决策树算法实现:从数学原理到Python实战

1次阅读
没有评论

共计 1934 个字符,预计需要花费 5 分钟才能阅读完成。

image.webp

决策树在特征工程中的核心价值

决策树通过递归分割数据空间,能自动识别最具区分度的特征组合。其产生的 if-else 规则天然可解释,特别适合需要特征重要性分析的场景。相比神经网络等黑盒模型,决策树在特征交互关系的可视化呈现上具有独特优势。

深入解析 CART 决策树算法实现:从数学原理到 Python 实战

算法数学原理对比

信息熵(ID3)

  • 熵衡量样本集合的混乱程度,计算公式:
    H(D) = -Σ(p_k * log2(p_k))
  • 信息增益 = 父节点熵 – 加权子节点熵

信息增益率(C4.5)

  • 解决 ID3 对多值特征的偏好问题:
    Gain_ratio(D,A) = Gain(D,A) / IV(A)
    IV(A) = -Σ(|D_v|/|D| * log2(|D_v|/|D|))

基尼系数(CART)

  • 衡量数据不纯度的替代指标:
    Gini(D) = 1 - Σ(p_k^2)
  • 计算效率比熵更高(省去对数运算)

CART 二叉树构建全流程

节点类定义

class TreeNode:
    def __init__(self, feat_idx=None, threshold=None,
                 left=None, right=None, value=None):
        self.feat_idx = feat_idx   # 分裂特征索引
        self.threshold = threshold # 分裂阈值
        self.left = left           # 左子树
        self.right = right         # 右子树
        self.value = value         # 叶节点预测值 

基尼系数计算实现

def gini_impurity(y):
    m = y.shape[0]
    return 1.0 - sum((np.sum(y == c) / m) ** 2 for c in np.unique(y))

递归分裂逻辑

  1. 寻找最佳分裂特征和阈值:

    def find_best_split(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 thresh in thresholds:
                left_idx = X[:, feat_idx] <= thresh
                gini = (len(y[left_idx]) * gini_impurity(y[left_idx]) + 
                        len(y[~left_idx]) * gini_impurity(y[~left_idx])) / len(y)
                if gini < best_gini:
                    best_gini = gini
                    best_idx = feat_idx
                    best_thresh = thresh
        return best_idx, best_thresh

  2. 递归构建树:

    def build_tree(X, y, max_depth=5, min_samples_split=2):
        if len(y) < min_samples_split or max_depth <= 0:
            return TreeNode(value=np.argmax(np.bincount(y)))
    
        feat_idx, thresh = find_best_split(X, y)
        left_idx = X[:, feat_idx] <= thresh
    
        left = build_tree(X[left_idx], y[left_idx], max_depth-1, min_samples_split)
        right = build_tree(X[~left_idx], y[~left_idx], max_depth-1, min_samples_split)
        return TreeNode(feat_idx, thresh, left, right)

剪枝策略实战分析

预剪枝方法

  • 最大深度限制
  • 节点最小样本数
  • 分裂增益阈值

后剪枝步骤

  1. 先构建完整树
  2. 自底向上计算剪枝前后的验证集准确率
  3. 保留提升准确率的剪枝操作

生产环境避坑指南

类别特征处理

  • 有序类别:转换为数值编码
  • 无序类别:执行 one-hot 编码

缺失值应对方案

  • 分裂时:将缺失样本分配到增益更大的分支
  • 预测时:同时遍历左右子树并加权平均结果

延伸思考方向

  1. 如何改造当前实现以支持连续特征动态分箱?
  2. 能否通过特征采样实现随机森林的并行化训练?
  3. 当类别极度不均衡时,基尼系数是否需要加权调整?

基尼系数 vs 交叉熵

  • 基尼系数:计算更高效,对类别分布变化更敏感
  • 交叉熵:对错误分类惩罚更重,理论上有更好泛化性

手动计算示例

假设某节点有 10 个样本:[A 类 6 个, B 类 4 个]
– 基尼系数 = 1 – (0.6² + 0.4²) = 0.48
– 熵 = -(0.6log2(0.6) + 0.4log2(0.4)) ≈ 0.971

树深度与模型偏差方差

  • 浅树:高偏差(欠拟合)
  • 适当深度:偏差方差平衡
  • 深树:高方差(过拟合)

通过控制 max_depth 参数,可以实现模型复杂度的精确调控。建议从 3 - 5 层开始,逐步增加深度观察验证集表现变化。

正文完
 0
评论(没有评论)