1. 决策树基础与核心原理
决策树作为机器学习中最直观的算法之一,其核心思想源于人类日常的决策过程。想象一位经验丰富的医生在诊断感冒时,会先询问"是否发烧",再根据回答决定下一步询问"是否有咳嗽"或"是否头痛",这种层层递进的判断逻辑正是决策树的本质。
1.1 决策树的解剖结构
一棵完整的决策树包含以下关键组成部分:
- 根节点:代表整个数据集的起点,是所有判断的源头。例如在医疗诊断中可能是"患者体温是否超过38°"。
- 内部节点:每个内部节点都是一个特征判断点,包含一个判断条件和两个或多个分支。例如"年龄>30岁"就是一个二元判断节点。
- 分支:表示特征判断的结果路径。对于分类问题通常是离散值(是/否),对于连续特征可能需要离散化处理。
- 叶节点:决策树的最终输出,包含类别标签(分类)或具体数值(回归)。在医疗场景中可能是"流感阳性"或"建议服用抗生素"。
1.2 决策树的工作机制
决策树的构建和预测遵循明确的流程:
-
训练阶段 - 递归拆分:
- 从根节点开始,选择当前最优的特征进行数据划分
- 对每个子节点重复选择最优特征的过程
- 直到满足停止条件:节点样本全属同一类、无更多特征可用、节点样本数低于阈值等
-
预测阶段 - 树遍历:
- 新样本从根节点进入
- 根据特征值选择对应分支向下移动
- 最终到达的叶节点即为预测结果
python复制# 决策树预测的伪代码示例
def predict(sample, node):
if node.is_leaf:
return node.label
if sample[node.feature] <= node.threshold:
return predict(sample, node.left_child)
else:
return predict(sample, node.right_child)
1.3 决策树的优势与局限
核心优势:
- 模型可解释性强,决策过程可视化
- 对数据预处理要求低,能处理混合类型特征
- 不需要特征缩放,对异常值不敏感
主要局限:
- 容易过拟合,需要剪枝等正则化手段
- 对数据微小变化敏感,可能导致树结构剧烈变化
- 倾向于选择具有更多取值的特征,可能忽视重要但取值少的特征
提示:在实际应用中,决策树的深度通常控制在3-5层以保证可解释性。当特征数量超过20个时,建议先进行特征选择再构建决策树。
2. 决策树生成算法详解
决策树算法的核心差异在于特征选择的标准和树的构建方式。主流算法包括ID3、C4.5和CART,每种算法都有其独特的数学基础和适用场景。
2.1 ID3算法:信息增益最大化
ID3算法是最早的决策树算法之一,由Ross Quinlan于1986年提出。它采用自顶向下的贪婪搜索策略,每次选择信息增益最大的特征进行分裂。
2.1.1 信息论基础
信息增益建立在信息熵的概念上。熵是信息论中衡量系统不确定性的指标:
-
对于二分类问题:
code复制H(D) = -p log₂p - (1-p) log₂(1-p)其中p是正类样本比例
-
对于多分类问题:
code复制H(D) = -Σ(p_k log₂p_k)p_k是第k类样本的比例
熵的特性:
- 当所有样本属于同一类时,熵为0(完全确定)
- 当样本均匀分布在所有类别时,熵最大(最不确定)
2.1.2 信息增益计算实例
假设我们有一个14天的天气数据集,预测是否适合打高尔夫:
| 天气 | 温度 | 湿度 | 风速 | 打球 |
|---|---|---|---|---|
| 晴 | 热 | 高 | 弱 | 否 |
| 晴 | 热 | 高 | 强 | 否 |
| ... | ... | ... | ... | ... |
计算"天气"特征的信息增益:
-
计算原始熵H(D):
- 打球"是":9天
- 打球"否":5天
- H(D) = -(9/14)log₂(9/14) - (5/14)log₂(5/14) ≈ 0.940
-
计算按"天气"分裂后的条件熵:
- 晴天(5天):打球2/5 → H=0.971
- 多云(4天):打球4/4 → H=0
- 雨天(5天):打球3/5 → H=0.971
- H(D|天气) = (5/14)*0.971 + (4/14)*0 + (5/14)*0.971 ≈ 0.693
-
信息增益:
- IG(天气) = H(D) - H(D|天气) ≈ 0.940 - 0.693 = 0.247
同理计算其他特征的信息增益,选择最大的作为分裂特征。
2.1.3 ID3的局限性
-
偏向多值特征:当某个特征取值很多时,其信息增益会被人为放大。例如"ID"字段的信息增益总是最大,但毫无预测意义。
-
无法处理连续特征:ID3只能处理离散型特征,对于温度、年龄等连续值需要先离散化。
-
缺失剪枝机制:容易生成过深的树,导致过拟合。
2.2 C4.5算法:信息增益比改进
C4.5是ID3的改进版本,通过引入信息增益比来解决ID3的缺陷。
2.2.1 信息增益比的计算
信息增益比通过引入分裂信息(Split Information)来惩罚多值特征:
code复制IV(A) = -Σ(|D_v|/|D| * log₂(|D_v|/|D|))
IGR(A) = IG(A) / IV(A)
其中IV(A)称为特征的固有值(Intrinsic Value),衡量特征取值的分布情况。
继续之前的天气例子:
计算"天气"的IV值:
- IV(天气) = -[(5/14)log₂(5/14) + (4/14)log₂(4/14) + (5/14)log₂(5/14)] ≈ 1.577
- IGR(天气) = 0.247 / 1.577 ≈ 0.157
2.2.2 C4.5的其他改进
-
处理连续特征:通过寻找最佳分割点将连续特征离散化。例如对温度特征,尝试所有可能的分割点,选择信息增益比最大的。
-
处理缺失值:采用概率权重的方式分配样本到各分支。
-
剪枝策略:采用悲观剪枝(Pessimistic Pruning),基于统计显著性检验决定是否剪枝。
2.3 CART算法:基尼不纯度与二叉树
CART(Classification and Regression Trees)算法由Breiman等人于1984年提出,既可以用于分类也可以用于回归。
2.3.1 基尼不纯度
基尼不纯度是CART用于分类问题的分裂标准:
code复制Gini(D) = 1 - Σ(p_k²)
基尼不纯度反映了从数据集中随机抽取两个样本,其类别不一致的概率。
对于二分类问题:
- 当所有样本属于同一类时,基尼指数为0
- 当样本均匀分布时,基尼指数为0.5
2.3.2 CART的特点
-
二叉树结构:无论特征有多少取值,都只生成两个分支。对于多值离散特征,需要寻找最优二分方式。
-
回归树支持:对于回归问题,使用最小二乘偏差作为分裂标准,叶节点输出子集样本的均值。
-
剪枝策略:采用代价复杂度剪枝(Cost-Complexity Pruning),通过交叉验证选择最优树大小。
2.3.3 CART与ID3/C4.5的对比
| 特性 | ID3 | C4.5 | CART |
|---|---|---|---|
| 分裂标准 | 信息增益 | 信息增益比 | 基尼指数/均方误差 |
| 树结构 | 多叉树 | 多叉树 | 二叉树 |
| 任务类型 | 分类 | 分类 | 分类+回归 |
| 连续特征 | 不支持 | 支持 | 支持 |
| 缺失值处理 | 不支持 | 支持 | 支持 |
| 剪枝方式 | 无 | 悲观剪枝 | 代价复杂度剪枝 |
3. 决策树的优化与剪枝
未经优化的决策树容易产生过拟合问题,需要通过剪枝等技术来提高泛化能力。同时,参数调优也是提升模型性能的关键。
3.1 预剪枝与后剪枝
3.1.1 预剪枝(Pre-pruning)
在树构建过程中提前停止生长,常用停止条件包括:
- 节点样本数小于阈值(如5%总样本)
- 树的深度达到限制
- 分裂带来的性能提升不显著(如信息增益<0.01)
优点:计算成本低,训练速度快
缺点:可能过早停止,欠拟合风险
3.1.2 后剪枝(Post-pruning)
先构建完整的树,再自底向上剪枝:
- 计算每个节点的验证集误差
- 尝试剪枝该节点(变为叶节点)
- 如果验证误差不增加或增加可接受,则保留剪枝
- 重复直到无法进一步改善
常用方法:
- 代价复杂度剪枝(Cost-Complexity Pruning)
- 最小误差剪枝(Minimum Error Pruning)
- 悲观剪枝(Pessimistic Pruning)
python复制# 决策树剪枝的Python实现示例
from sklearn.tree import DecisionTreeClassifier
# 预剪枝参数设置
clf = DecisionTreeClassifier(
max_depth=5, # 最大深度
min_samples_split=10, # 节点最小样本数
min_impurity_decrease=0.01 # 分裂最小增益
)
# 后剪枝需要通过cost_complexity_pruning_path获取alpha参数
path = clf.cost_complexity_pruning_path(X_train, y_train)
alphas = path.ccp_alphas
# 为每个alpha训练一个剪枝模型
pruned_clfs = []
for alpha in alphas:
pruned_clf = DecisionTreeClassifier(ccp_alpha=alpha)
pruned_clf.fit(X_train, y_train)
pruned_clfs.append(pruned_clf)
3.2 决策树参数调优
关键参数及其影响:
| 参数 | 作用 | 典型值范围 | 影响方向 |
|---|---|---|---|
| max_depth | 树的最大深度 | 3-10 | 越大越容易过拟合 |
| min_samples_split | 节点分裂所需最小样本数 | 2-20 | 越大越保守 |
| min_samples_leaf | 叶节点最少样本数 | 1-10 | 防止异常值影响 |
| max_features | 考虑的特征数量 | 'sqrt'或1.0-0.1 | 控制随机性 |
| ccp_alpha | 剪枝强度参数 | 0-0.1 | 越大剪枝越强 |
经验法则:对于中小型数据集(10^3-10^4样本),max_depth设为5-8通常效果较好。min_samples_leaf建议设为1-5%的总样本数。
3.3 处理类别不平衡
当类别分布不均衡时,决策树可能偏向多数类。解决方法包括:
-
类别权重:
python复制class_weight='balanced' # 自动调整权重 -
采样方法:
- 过采样少数类(SMOTE)
- 欠采样多数类
-
损失函数调整:
- 使用F1-score等不敏感指标优化
4. 集成学习基础与Bagging方法
单一决策树往往存在高方差问题,集成学习通过组合多个模型来提高预测稳定性和准确性。根据集成方式不同,主要分为Bagging和Boosting两大类。
4.1 偏差-方差分解
理解集成学习的价值需要先明确偏差(Bias)和方差(Variance)的概念:
- 偏差:模型预测值与真实值的系统性差异,反映模型拟合能力
- 方差:模型对训练数据微小变化的敏感程度,反映模型稳定性
- 噪声:数据本身的不可约误差
三者关系可表示为:
code复制总误差 = 偏差² + 方差 + 噪声
决策树的误差特点:
- 浅树:高偏差(欠拟合),低方差
- 深树:低偏差,高方差(过拟合)
4.2 Bagging原理
Bagging(Bootstrap Aggregating)通过并行训练多个模型并聚合结果来降低方差。
4.2.1 算法步骤
-
自助采样(Bootstrap):
- 从原始训练集(大小N)中有放回地随机采样N个样本
- 约63.2%的原始样本会被选中,36.8%未被选中(称为OOB样本)
-
基模型训练:
- 对每个采样集训练一个基模型(如决策树)
- 各模型独立并行训练
-
结果聚合:
- 分类问题:多数投票
- 回归问题:平均预测
python复制# Bagging的Python实现示例
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
bagging = BaggingClassifier(
base_estimator=DecisionTreeClassifier(),
n_estimators=100, # 基模型数量
max_samples=0.8, # 每个模型的样本比例
max_features=0.5, # 每个模型的特征比例
oob_score=True # 使用OOB样本评估
)
bagging.fit(X_train, y_train)
print("OOB score:", bagging.oob_score_)
4.2.2 为什么Bagging有效
-
方差减少:通过平均多个高方差模型的预测,降低整体方差
code复制Var(1/m Σ f_i) = 1/m Var(f) + (1-1/m)Cov(f_i,f_j)当模型相关性低时,协方差项小,整体方差显著降低
-
OOB估计:未被采样的样本可用来评估模型性能,无需额外验证集
-
并行化:各基模型独立训练,适合分布式计算
4.3 随机森林
随机森林(Random Forest)是Bagging的扩展,在决策树的训练过程中引入额外的随机性。
4.3.1 算法增强
-
特征随机性:
- 每个节点分裂时,仅考虑随机子集的特征(通常√p或log₂p,p为总特征数)
- 进一步降低模型间的相关性
-
其他增强:
- 可使用随机阈值而非最优阈值
- 可结合随机子空间等方法
4.3.2 随机森林的优势
- 抗过拟合:双重随机性(数据+特征)使模型更鲁棒
- 处理高维数据:自动特征选择,对冗余特征不敏感
- 内置特征重要性:基于特征在树中的分裂贡献度计算
- 开箱即用:默认参数通常表现良好
python复制# 随机森林的Python实现
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(
n_estimators=200,
max_features='sqrt', # 每棵树考虑√p个特征
max_depth=8,
min_samples_leaf=5,
n_jobs=-1 # 使用所有CPU核心
)
rf.fit(X_train, y_train)
# 特征重要性可视化
importances = rf.feature_importances_
4.3.3 随机森林调优要点
- n_estimators:树的数量,越大越好但计算成本增加,通常100-500
- max_features:控制随机性强度,常用'sqrt'或0.3-0.5
- max_depth:通常不限制或设为较大值(如20),让树充分生长
- min_samples_leaf:控制叶节点最小样本数,防止过拟合
实际经验:随机森林对max_depth等参数不敏感,重点调整n_estimators和max_features即可获得不错效果。
5. Boosting方法与GBDT详解
与Bagging并行集成不同,Boosting采用串行方式逐步提升模型性能,通过重点关注先前模型预测错误的样本来迭代改进。
5.1 Boosting核心思想
Boosting的工作机制可以概括为:
- 顺序训练:基模型按顺序而非并行训练
- 错误关注:后续模型更关注前序模型预测错误的样本
- 加权组合:最终预测是所有基模型的加权和
数学表达:
code复制F(x) = Σ α_t * h_t(x)
其中α_t是第t个模型的权重,h_t是第t个基模型
5.2 梯度提升决策树(GBDT)
GBDT是Boosting的杰出代表,通过梯度下降的思想来优化任意可微损失函数。
5.2.1 算法推导
GBDT最小化以下损失函数:
code复制L = Σ l(y_i, F(x_i))
通过梯度下降逐步更新模型:
code复制F_{t}(x) = F_{t-1}(x) + α * h_t(x)
其中h_t(x)拟合负梯度(伪残差):
code复制r_{ti} = -∂l(y_i, F(x_i))/∂F(x_i) | F=F_{t-1}
常见损失函数:
- 回归:均方误差 → 残差r=y-F(x)
- 分类:对数损失 → 残差r=y-p(y=1|x)
5.2.2 GBDT实现步骤
-
初始化常数模型:
code复制F_0(x) = argmin_γ Σ L(y_i, γ) -
对于t=1到T:
a. 计算伪残差:code复制r_{ti} = -[∂L(y_i, F(x_i))/∂F(x_i)]_{F=F_{t-1}}b. 训练决策树h_t(x)拟合伪残差{r_{ti}}
c. 通过线搜索确定步长:code复制α_t = argmin_α Σ L(y_i, F_{t-1}(x_i)+α*h_t(x_i))d. 更新模型:
code复制F_t(x) = F_{t-1}(x) + ν*α_t*h_t(x)(ν是学习率,通常0.01-0.1)
-
输出最终模型F_T(x)
python复制# GBDT的Python实现示例
from sklearn.ensemble import GradientBoostingClassifier
gbdt = GradientBoostingClassifier(
n_estimators=200,
learning_rate=0.05,
max_depth=3,
min_samples_leaf=5,
subsample=0.8 # 随机采样比例
)
gbdt.fit(X_train, y_train)
5.2.3 GBDT关键参数
- n_estimators:基模型数量,需与learning_rate平衡
- learning_rate:收缩系数,越小需要更多基模型
- max_depth:每棵树的深度,通常3-8
- subsample:行采样比例,引入随机性
- min_samples_leaf:控制树生长
调优技巧:先设较大n_estimators(如500)和较小learning_rate(如0.05),通过early_stopping确定最佳树数量。
5.3 XGBoost与LightGBM
XGBoost和LightGBM是GBDT的高效实现,加入了多项优化:
5.3.1 XGBoost的创新
- 正则化项:目标函数中加入L1/L2正则
- 二阶泰勒展开:使用损失函数的一阶和二阶导数
- 加权分位数草图:高效寻找最佳分割点
- 稀疏感知:自动处理缺失值
- 并行化:特征并行和数据并行
python复制# XGBoost示例
import xgboost as xgb
dtrain = xgb.DMatrix(X_train, label=y_train)
params = {
'max_depth': 6,
'eta': 0.3,
'objective': 'binary:logistic',
'subsample': 0.8
}
model = xgb.train(params, dtrain, num_boost_round=100)
5.3.2 LightGBM的创新
- 直方图算法:将连续特征离散化为直方图
- 单边梯度采样(GOSS):保留大梯度样本,随机采样小梯度样本
- 互斥特征捆绑(EFB):合并稀疏特征减少维度
- Leaf-wise生长:相比level-wise更高效
python复制# LightGBM示例
import lightgbm as lgb
train_data = lgb.Dataset(X_train, label=y_train)
params = {
'max_depth': -1, # 无限制
'learning_rate': 0.1,
'num_leaves': 31,
'feature_fraction': 0.8
}
model = lgb.train(params, train_data, num_boost_round=100)
5.3.3 算法选择建议
| 场景 | 推荐算法 | 理由 |
|---|---|---|
| 中小数据集 | XGBoost | 精度高,功能完善 |
| 大数据集 | LightGBM | 训练速度快,内存占用低 |
| 需要模型解释 | 随机森林 | 特征重要性直观 |
| 类别不平衡 | LightGBM | 内置处理方式完善 |
| 低延迟要求 | LightGBM | 预测速度快 |
6. 决策树与集成学习的实践应用
理论知识的最终价值在于实践应用。本节将探讨决策树和集成学习在实际项目中的最佳实践,包括特征工程、模型解释和部署考量。
6.1 特征工程特别考量
虽然决策树对数据预处理要求较低,但适当的特征工程仍能显著提升性能:
6.1.1 特征编码策略
-
类别特征:
- 有序类别:直接映射为数值(如"低=0,中=1,高=2")
- 无序类别:避免独热编码(可能导致分裂不平衡),推荐:
- 目标编码(Target Encoding)
- 计数编码(Count Encoding)
- 留一法编码(Leave-One-Out)
-
连续特征:
- 决策树本身能处理连续特征,但有时离散化有帮助:
- 等宽/等频分箱
- 基于决策树的最优分箱
- 决策树本身能处理连续特征,但有时离散化有帮助:
python复制# 目标编码示例
from category_encoders import TargetEncoder
encoder = TargetEncoder()
X_train_encoded = encoder.fit_transform(X_train[categorical_cols], y_train)
X_test_encoded = encoder.transform(X_test[categorical_cols])
6.1.2 特征交互与组合
决策树能自动发现一些特征交互,但显式创建交互特征仍有价值:
-
数值特征组合:
- 加减乘除等算术组合
- 多项式特征
-
类别特征交叉:
- 笛卡尔积生成新类别
- 基于领域知识的组合
注意:随机森林等集成方法对冗余特征具有鲁棒性,但过多无关特征仍会影响训练速度和特征重要性评估。
6.2 模型解释与可视化
决策树系列模型的可解释性是其主要优势之一,常用解释方法包括:
6.2.1 决策树可视化
python复制# 使用graphviz可视化决策树
from sklearn.tree import export_graphviz
import graphviz
dot_data = export_graphviz(
decision_tree,
out_file=None,
feature_names=feature_names,
class_names=class_names,
filled=True,
rounded=True
)
graph = graphviz.Source(dot_data)
graph.render("decision_tree") # 保存为PDF
6.2.2 特征重要性
-
基于分裂:
- 计算特征在所有树中带来的不纯度减少总量
- 归一化为重要性分数
-
基于排列:
- 随机打乱特征值,观察模型性能下降程度
- 更可靠但计算成本高
python复制# 特征重要性分析
importances = model.feature_importances_
indices = np.argsort(importances)[::-1]
plt.figure(figsize=(10, 6))
plt.title("Feature Importances")
plt.bar(range(X.shape[1]), importances[indices], align="center")
plt.xticks(range(X.shape[1]), feature_names[indices], rotation=90)
plt.show()
6.2.3 SHAP值解释
SHAP(SHapley Additive exPlanations)提供更精细的特征贡献分析:
python复制import shap
# 创建解释器
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_test)
# 可视化单个预测
shap.force_plot(explainer.expected_value, shap_values[0,:], X_test.iloc[0,:])
# 汇总图
shap.summary_plot(shap_values, X_test)
6.3 部署与生产化考量
将决策树模型投入生产环境时需考虑以下因素:
6.3.1 模型优化
-
剪枝与量化:
- 后剪枝减小模型体积
- 将浮点参数转换为定点数
-
模型蒸馏:
- 用复杂集成模型训练小型决策树
- 保持大部分性能的同时大幅减小模型大小
6.3.2 部署方式
-
嵌入式部署:
- 将模型直接编译到应用程序中
- 适合移动端和IoT设备
-
服务化部署:
- 通过REST/gRPC接口提供服务
- 使用Flask/FastAPI等框架封装
python复制# 使用ONNX格式导出模型
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
initial_type = [('float_input', FloatTensorType([None, X_train.shape[1]]))]
onx = convert_sklearn(model, initial_types=initial_type)
with open("model.onnx", "wb") as f:
f.write(onx.SerializeToString())
6.3.3 监控与维护
-
性能监控:
- 记录预测延迟、吞吐量
- 监控输入特征分布变化
-
概念漂移检测:
- 定期检查模型在最新数据上的表现
- 设置自动重训练机制
-
版本控制:
- 对模型和预处理管道进行版本化
- 实现A/B测试和灰度发布
7. 实战案例:信用风险评估系统
为了综合展示决策树和集成学习的应用,我们通过一个信用风险评估的完整案例,演示从数据准备到模型部署的全流程。
7.1 业务理解与数据准备
信用风险评估的核心是预测借款人违约的可能性。我们使用公开的German Credit数据集,包含1000个样本和20个特征。
7.1.1 数据概览
python复制import pandas as pd
from sklearn.datasets import fetch_openml
# 加载数据
data = fetch_openml('credit-g', as_frame=True)
df = data.frame
target = 'class' # 'good' or 'bad'
# 基本统计
print(df.shape)
print(df[target].value_counts(normalize=True))
7.1.2 数据预处理
-
缺失值处理:
- 数值特征:中位数填充
- 类别特征:新增"missing"类别
-
特征编码:
- 有序类别:OrdinalEncoder
- 无序类别:TargetEncoder
-
特征选择:
- 基于IV值(Information Value)筛选
- 移除高相关性特征
python复制# 预处理管道示例
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OrdinalEncoder
# 定义特征类型
numeric_features = ['age', 'credit_amount', 'duration']
ordinal_features = ['checking_status'] # 有序类别
categorical_features = ['purpose', 'savings_status'] # 无序类别
# 构建预处理管道
preprocessor = ColumnTransformer(
transformers=[
('num', 'passthrough', numeric_features),
('ord', OrdinalEncoder(), ordinal_features),
('cat', TargetEncoder(), categorical_features)
])
7.2 模型训练与评估
我们比较单一决策树、随机森林和GBDT三种模型。
7.2.1 基准模型 - 决策树
python复制from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
tree = Pipeline([
('preprocessor', preprocessor),
('classifier', DecisionTreeClassifier(max_depth=5))
])
scores = cross_val_score(tree, df, df[target], cv=5, scoring='roc_auc')
print(f"DecisionTree AUC: {scores.mean():.3f} ± {scores.std():.3f}")
7.2.2 随机森林
python复制from sklearn.ensemble import RandomForestClassifier
rf = Pipeline([
('preprocessor', preprocessor),
('classifier', RandomForestClassifier(
n_estimators=200,
max_features='sqrt',
max_depth=7,
class_weight='balanced'
))
])
scores = cross_val_score(rf, df, df[target], cv=5, scoring='roc_auc')
print(f"RandomForest AUC: {scores.mean():.3f} ± {scores.std():.3f}")
7.2.3 GBDT
python复制from sklearn.ensemble import GradientBoostingClassifier
gbdt = Pipeline([
('preprocessor', preprocessor),
('classifier', GradientBoostingClassifier(
n_estimators=300,
learning_rate=0.05,
max_depth=3,
subsample=0.8
))
])
scores = cross_val_score(gbdt, df, df[target], cv=5, scoring='roc_auc')
print(f"GBDT AUC: {scores.mean():.3f} ± {scores.std():.3f}")
7.2.4 模型对比
| 模型 | AUC均值 | AUC标准差 | 训练时间 | 可解释性 |
|---|---|---|---|---|
| 决策树 | 0.742 | 0.032 | 0.1s | ★★★★★ |
| 随机森林 | 0.813 | 0.028 | 2.3s | ★★★☆☆ |
| GBDT | 0.826 | 0.025 | 4.1s | ★★★☆☆ |
7.3 模型解释与部署
选择随机森林作为最终模型,进行详细解释和部署准备。
7.3.1 全局解释
python复制# 训练最终模型
rf.fit(df, df[target])
# 特征重要性
importances = rf.named_steps['classifier'].feature_importances_
features = numeric_features + ordinal_features + categorical_features
pd.DataFrame({
'feature': features,
'importance': importances
}).sort_values('importance', ascending=False).head(10)
7.3.2 局部解释
python复制# 单个样本预测解释
sample_idx = 10
sample = df.iloc[sample_idx:sample_idx+1]
# SHAP解释
explainer = shap.TreeExplainer(rf.named_steps['classifier'])
shap_values = explainer.shap_values(preprocessor.transform(sample))
shap.force_plot(
explainer.expected_value[1],
shap_values[1][0],
sample[numeric_features + ordinal_features + categorical_features]
)
7.3.3 部署准备
python复制import joblib
# 保存整个管道
joblib.dump(rf, 'credit_risk_model.pkl')
# 加载使用示例
loaded_model = joblib.load('credit_risk_model.pkl')
prob = loaded_model.predict_proba(new_data)[:, 1]
7.4 业务应用与监控
-
决策阈值选择:
- 基于业务成本矩阵选择最佳阈值
- 实现ROC曲线分析
-
监控仪表板:
- 特征分布漂移检测
- 模型性能衰减预警
-
反馈循环:
- 收集实际违约数据
- 定期模型重训练
python复制# 阈值选择示例
from sklearn.metrics import roc_curve
probs = rf.predict_proba(df)[:, 1]
fpr, tpr, thresholds = roc_curve(df[target], probs, pos_label='bad')
# 找到最佳阈值(可根据业务成本调整)
optimal_idx = np.argmax(tpr - fpr)
optimal_threshold = thresholds[optimal_idx]
print(f"Optimal threshold: {optimal_threshold:.2f}")
8. 决策树与集成学习的进阶话题
在掌握了基础应用后,我们探讨一些前沿发展和高级技巧,帮助读者在复杂场景中更好地应用这些算法。
8.1 处理非结构化数据
传统决策树主要处理结构化数据,但通过特征工程也能应用于非结构化数据:
8.1.1 文本数据
-
词袋模型:
- TF-IDF特征
- 主题模型特征(LDA)
-
深度学习特征:
- 使用BERT等模型提取嵌入
- 作为决策树的输入特征
python复制# 文本特征+随机森林示例
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.pipeline import make_pipeline
text_clf = make_pipeline(
TfidfVectorizer(max_features=1000),
RandomForestClassifier(n_estimators=100)
)
text_clf.fit(text_train, y_train)
8.1.2 图像数据
-
传统特征:
- HOG(方向梯度直方图)
- 颜色直方图
-
CNN特征:
- 使用预训练CNN提取特征
- 决策树作为顶层分类器
python复制# 图像特征提取示例
from keras.applications.vgg16 import VGG16, preprocess_input
from keras.preprocessing import image
base_model = VGG16(weights='imagenet', include_top=False)
def extract_features(img_path):
img = image.load_img