4、决策树与规则提取:树结构可视化、决策路径分析、规则集生成

决策树这东西,说白了就是风控模型里最“讲人话”的一个。你想想看,一个神经网络给你输出个0.87,你很难跟业务同事解释为什么这个人就是坏客户。但决策树不一样——它能把判断过程一条条列出来,像法官判案一样清晰。

我个人习惯,在项目初期一定会先跑一棵决策树。不是为了追求最高精度,而是为了快速理解数据里的规律。今天我们就聊聊怎么把树“看明白”,以及怎么从树里抽出一套可执行的规则。

4.1 树结构可视化——让模型“长什么样”一目了然

我刚开始做风控时,拿到一棵深度为10的树,直接打印出来有几十页。后来学乖了,只可视化前3到4层。为什么?因为业务人员只看前几层就能理解核心分叉逻辑。

常用的可视化工具,我推荐graphviz配合sklearn的plot_tree。下面这段代码,是我在信贷评分卡项目中经常用的模板:

from sklearn.tree import DecisionTreeClassifier, plot_tree
import matplotlib.pyplot as plt

# 训练一棵浅树,深度设为3
clf = DecisionTreeClassifier(max_depth=3, min_samples_leaf=50, random_state=42)
clf.fit(X_train, y_train)

# 可视化
plt.figure(figsize=(20, 10))
plot_tree(clf, 
          feature_names=['收入', '负债率', '征信查询次数', '年龄', '工作年限'],
          class_names=['好客户', '坏客户'],
          filled=True,
          rounded=True,
          fontsize=12)
plt.show()

这里有个坑,我提醒一下:节点颜色深浅代表纯度。深蓝色节点表示大部分是好客户,深橙色表示坏客户集中。如果某个叶子节点颜色很浅,说明这个分叉没把两类分开——嗯,这时候就要考虑剪枝了。

可视化要点:

  • 深度控制在3-4层,超过5层普通人就看不懂了
  • 每个节点显示:样本数、类别分布、主导类别
  • 用颜色区分好坏客户,一目了然

4.2 决策路径分析——追踪每个样本的“判案过程”

树结构看整体,决策路径看个体。我在做贷后分析时,经常需要回答一个问题:“为什么这个客户被拒了?”这时候就要走决策路径。

每一条从根节点到叶子节点的路径,就是一个完整的判断逻辑。sklearn里有个好用的方法叫decision_path,能返回每个样本经过的节点索引:

# 获取某个样本的决策路径
sample_idx = 0  # 第一个样本
path = clf.decision_path(X_test[sample_idx].reshape(1, -1))
print(path.indices)

# 输出示例:[0, 3, 7, 14]
# 表示这个样本经过了节点0 → 节点3 → 节点7 → 节点14

我曾经遇到过一个案例:一个客户收入很高,征信也好,但被模型判为坏客户。业务同事跑来质疑模型有问题。我顺着决策路径一看,发现他在“负债率”这个节点被分到了高风险分支——原来他刚买了房,短期负债暴增。嗯,这就是决策路径的价值,能让你精准定位到“哪一步出了问题”。

我的小技巧:把决策路径转成自然语言描述。比如“收入>1.5万 → 负债率<40% → 征信查询次数<3次 → 好客户”。这样业务同事一看就懂,不需要任何技术背景。

4.3 规则集生成——把树变成可执行的业务规则

树结构可视化是给人看的,规则集是给系统用的。我在多家银行做过规则引擎对接,发现一个规律:决策树转规则,比直接部署模型更受风控运营欢迎。为什么?因为规则可以手动调整,模型是个黑盒。

规则提取的核心思路很简单:遍历每一条从根到叶子的路径,把路径上的判断条件组合起来。下面是我自己写的一个规则提取函数:

def extract_rules(tree, feature_names):
    """从决策树中提取规则集"""
    tree_ = tree.tree_
    feature_name = [
        feature_names[i] if i != -2 else "undefined!"
        for i in tree_.feature
    ]
    
    rules = []
    
    def recurse(node, depth, conditions):
        # 如果是叶子节点
        if tree_.feature[node] == -2:
            # 获取该节点的主导类别
            class_dist = tree_.value[node][0]
            dominant_class = class_dist.argmax()
            class_name = '好客户' if dominant_class == 0 else '坏客户'
            sample_count = int(class_dist.sum())
            rules.append({
                'conditions': conditions.copy(),
                'class': class_name,
                'samples': sample_count,
                'purity': max(class_dist) / sum(class_dist)
            })
            return
        
        # 非叶子节点,继续递归
        feat = feature_name[tree_.feature[node]]
        threshold = tree_.threshold[node]
        
        # 左分支:特征 <= 阈值
        conditions.append(f"{feat} <= {threshold:.2f}")
        recurse(tree_.children_left[node], depth+1, conditions)
        conditions.pop()
        
        # 右分支:特征 > 阈值
        conditions.append(f"{feat} > {threshold:.2f}")
        recurse(tree_.children_right[node], depth+1, conditions)
        conditions.pop()
    
    recurse(0, 0, [])
    return rules

# 使用示例
rules = extract_rules(clf, ['收入', '负债率', '征信查询次数', '年龄', '工作年限'])
for r in rules[:5]:
    print(f"条件: {' 且 '.join(r['conditions'])}")
    print(f"结果: {r['class']} (样本数: {r['samples']}, 纯度: {r['purity']:.2%})")
    print("---")

避坑指南:我曾经吃过一次亏——直接拿全量规则去部署,结果发现规则之间互相矛盾。后来我加了一个优先级排序:纯度高的规则优先,样本量大的规则优先。这样规则引擎跑起来才不会打架。

4.4 知识体系总览

下面这张图,是我自己总结的决策树解释性分析的核心逻辑。你可以把它当作本章的“地图”:

决策树解释性分析核心流程 树结构可视化 整体逻辑展示 深度3-4层最佳 决策路径分析 个体样本追踪 定位拒绝原因 规则集生成 可执行业务规则 支持手动调整 关键输出 可视化图表 + 路径描述 + 规则列表(含优先级、纯度、样本量) 典型应用场景 • 信贷审批:解释拒绝原因 • 反欺诈:定位异常规则 • 模型监控:对比规则稳定性 • 业务沟通:向非技术人员解释

4.5 规则质量评估——别让垃圾规则上线

规则提取出来之后,不能直接扔给业务。我一般会做三件事来评估规则质量:

评估维度 指标 我的经验阈值 说明
覆盖度 规则覆盖的样本比例 > 5% 覆盖太少,规则没有统计意义
纯度 主导类别占比 > 80% 纯度太低,规则区分能力弱
稳定性 跨时间窗口的纯度变化 < 10% 变化太大,规则可能已经失效
可解释性 规则中条件数量 < 5个 条件太多,业务人员记不住

我的经验:规则不是越多越好。一棵深度为5的树,最多能产生32条规则。但我通常只保留前10条覆盖度最高的规则,剩下的合并或丢弃。为什么?因为业务规则引擎的维护成本很高,每多一条规则,就多一个潜在的维护坑。

4.6 实战中的几个坑

最后,分享几个我在项目中踩过的坑,希望能帮你少走弯路:

  • 坑一:树太深,规则爆炸。深度为10的树,理论上能产生1024条规则。实际上大部分规则只覆盖几十个样本,毫无意义。我的建议是:先限制深度,再根据覆盖度过滤
  • 坑二:连续特征分叉太多。比如“收入”这个特征,可能在树里被切了七八次。转成规则后,会出现“收入>1.2万 且 收入<=1.5万”这种奇怪的条件。我一般会做离散化处理,或者合并相邻的分叉。
  • 坑三:规则之间互相矛盾。比如规则A说“收入>2万 → 好客户”,规则B说“收入>2万 且 负债率>80% → 坏客户”。这时候要明确优先级:条件多的规则优先级更高,因为更具体。

嗯,决策树和规则提取这块,说白了就是“把模型翻译成人话”。你掌握好可视化、路径分析和规则生成这三板斧,基本上就能应对大部分风控场景的解释性需求了。


公众号:蓝海资料掘金营,微信deep3321