决策树算法精讲:从CART理论到Python实战

1次阅读
没有评论

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

image.webp

引言

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

决策树算法精讲:从 CART 理论到 Python 实战

数学原理:基尼系数与信息增益

基尼系数

基尼系数(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) $$

两者对比

  • 基尼系数计算更快(无对数运算)
  • 信息增益对不纯度更敏感
  • 实际应用中两者结果通常相近

算法实现:递归构建决策树

  1. 从根节点开始,计算所有特征的信息增益 / 基尼系数
  2. 选择最佳划分特征
  3. 对每个特征值创建子节点
  4. 递归处理子节点,直到:
  5. 节点样本全属同一类
  6. 无更多特征可用
  7. 达到预设终止条件

伪代码实现:

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)

模型调优与评估

防止过拟合的方法

  1. 预剪枝(Pre-pruning):
  2. 限制树的最大深度
  3. 设置节点最小样本数
  4. 设定信息增益阈值

  5. 后剪枝(Post-pruning):

  6. 用验证集评估剪枝效果
  7. 递归地从叶子节点向上剪枝

交叉验证技巧

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)

生产环境最佳实践

处理类别不平衡

  1. 使用 class_weight 参数调整类别权重
  2. 对少数类样本过采样
  3. 采用 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 等强大模型的基础,值得深入学习。

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