经典决策树分类实战:从特征选择到模型调优全流程解析

1次阅读
没有评论

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

image.webp

背景痛点:业务场景中的决策树困境

决策树分类器在实际业务中常遇到三个典型问题:

经典决策树分类实战:从特征选择到模型调优全流程解析

  • 金融风控场景下的过拟合:当用户行为特征维度高、样本量少时,决策树容易生成过于复杂的规则,导致在测试集上表现急剧下降。例如某借贷平台使用用户手机型号(200+ 类别)作为特征时,AUC 从训练集的 0.92 跌至测试集的 0.68

  • 医疗数据中的特征重要性误判:当特征间存在多重共线性时(如血压测量值与心血管疾病指标),基尼重要性评分会出现偏差。某三甲医院的糖尿病预测模型中,误将无关的体检编号字段识别为 top3 重要特征

  • 工业级应用的性能瓶颈:当类别特征基数超过 1000 时(如电商用户 ID),传统 one-hot 编码会导致内存爆炸。某推荐系统的决策树在特征预处理阶段就消耗了 32GB 内存

技术对比:三大算法原理差异

1. 信息增益(ID3)

数学表达式:
$$IG(S,A) = H(S) – \sum_{v\in Values(A)} \frac{|S_v|}{|S|}H(S_v)$$

适用场景:
– 特征取值较少的离散变量
– 对缺失值敏感(需提前处理)

2. 信息增益率(C4.5)

改进公式:
$$IGR(S,A) = \frac{IG(S,A)}{IV(A)}, \quad IV(A)=-\sum_{v\in Values(A)} \frac{|S_v|}{|S|} \log_2 \frac{|S_v|}{|S|}$$

优势:
– 克服了 ID3 对多值特征的偏好
– 支持连续特征自动分箱

3. 基尼系数(CART)

分裂标准:
$$Gini(D) = 1 – \sum_{k=1}^K p_k^2$$

与信息熵的对比:
– 计算速度更快(无对数运算)
– 对类别分布不均匀的数据更鲁棒
– 倾向于选择具有更多取值的特征

核心实现:Python 全流程示例

数据准备与特征工程

import pandas as pd
from sklearn.preprocessing import LabelEncoder

# 处理高基数类别特征
class HighCardinalityProcessor:
    def __init__(self, threshold=10):
        self.threshold = threshold

    def transform(self, X, col):
        freq = X[col].value_counts()
        mask = freq >= self.threshold
        return X[col].where(X[col].isin(freq[mask].index), 'Other')

# 示例:电商用户行为数据
user_df = pd.read_csv('user_behavior.csv')
processor = HighCardinalityProcessor(threshold=50)
user_df['user_region'] = processor.transform(user_df, 'region_id')

模型训练与调参

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import GridSearchCV

# 关键参数网格
param_grid = {'max_depth': [3, 5, 7, None],
    'min_samples_split': [2, 5, 10],
    'criterion': ['gini', 'entropy'],
    'class_weight': [None, 'balanced']
}

# 网格搜索示例
dtree = DecisionTreeClassifier(random_state=42)
grid_search = GridSearchCV(dtree, param_grid, cv=5, scoring='roc_auc')
grid_search.fit(X_train, y_train)

print(f"最优参数:{grid_search.best_params_}")
print(f"验证集 AUC:{grid_search.best_score_:.4f}")

决策树可视化

import graphviz
from sklearn.tree import export_graphviz

# 导出 dot 文件
dot_data = export_graphviz(
    grid_search.best_estimator_,
    out_file=None,
    feature_names=feature_names,
    class_names=['负样本', '正样本'],
    filled=True,
    rounded=True
)

# 生成可视化图形
graph = graphviz.Source(dot_data)
graph.render('decision_tree')  # 保存为 PDF

生产环境避坑指南

1. 高基数类别特征处理

  • 分箱法:对出现频率低于阈值的类别合并为 ”Other”
  • 目标编码:用该类别下目标变量的均值进行编码
  • 避免直接 one-hot:当类别数 >50 时考虑其他编码方式

2. 随机森林的替代时机

当出现以下情况时建议切换:
– 单棵树的测试集表现波动大于 5%
– 特征重要性排名在多次训练中不一致
– 业务要求模型具备可解释性(随机森林的黑箱程度更高)

3. 模型持久化优化

  • 使用 pickle 的 protocol= 4 版本
  • 剪枝后模型体积可减小 60% 以上
  • 对于深度 >20 的树,建议转换为规则引擎代码

性能验证:乳腺癌数据集实验

from sklearn.datasets import load_breast_cancer
from sklearn.metrics import classification_report

# 加载数据
data = load_breast_cancer()
X, y = data.data, data.target

# 最佳模型训练
best_dtree = grid_search.best_estimator_.fit(X_train, y_train)

# 评估报告
print(classification_report(y_test, best_dtree.predict(X_test)))

# 特征重要性
importance = pd.DataFrame({
    'feature': data.feature_names,
    'importance': best_dtree.feature_importances_
}).sort_values('importance', ascending=False)

典型输出结果:

              precision    recall  f1-score   support

           0       0.97      0.91      0.94        53
           1       0.95      0.98      0.97        90

    accuracy                           0.96       143
   macro avg       0.96      0.95      0.95       143
weighted avg       0.96      0.96      0.96       143

Top5 重要特征:1. worst radius (0.72)
2. worst perimeter (0.12)
3. mean concave points (0.08)
4. worst area (0.04)
5. mean radius (0.02)

延伸思考:时序数据特征构造

当处理时间序列数据时,可以尝试以下特征构造方法:

  1. 滑动统计量
  2. 过去 7 天的最大值 / 标准差
  3. 环比变化率:(当前值 – 前一天值) / 前一天值

  4. 事件标记

  5. 是否达到月累计阈值
  6. 连续上涨 / 下跌天数

  7. 周期特征

  8. 星期几(weekday)的 one-hot 编码
  9. 是否为月末 / 季末

示例代码:

def create_time_features(df, time_col='date'):
    df['day_of_week'] = df[time_col].dt.dayofweek
    df['is_month_end'] = df[time_col].dt.is_month_end.astype(int)

    # 7 日滑动平均
    df['7d_avg'] = df['value'].rolling(7).mean()
    return df

决策树在处理这类特征时,建议先进行标准化处理并限制树深度(通常 3 - 5 层足够),避免时间维度上的过拟合。

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