2. 分群评分沙盘概念
2.1 什么是分群评分沙盘
分群评分沙盘(Segmentation & Score Sandbox)是授信决策引擎中用于离线模拟、验证和优化策略组合的隔离环境。它将客群按照风险特征划分为多个子群(Segment),并为每个子群分配独立的评分卡与决策规则,在沙盘环境中进行“假设推演”,从而在不影响生产流量的前提下完成策略调优。
核心定义: 分群评分沙盘 = 客群分群 × 评分卡矩阵 × 离线回测引擎 × 策略编排沙盒。
沙盘的核心组件包括:
- 客群分群器: 基于规则(如年龄、收入、征信分)或模型(如聚类、决策树)将申请客群划分为若干同质子群。
- 评分卡矩阵: 每个子群对应一张评分卡,评分卡可以是逻辑回归、XGBoost 或集成模型,输出违约概率或信用分数。
- 策略编排层: 在沙盘内定义每个子群的准入规则、额度矩阵、定价策略,支持 Python 脚本动态编排。
- 回测引擎: 使用历史样本数据模拟策略执行,输出通过率、坏账率、ROI 等关键指标。
2.2 沙盘在策略调优与回测中的作用
沙盘是策略调优的“安全实验室”,其核心价值体现在以下三个维度:
| 作用维度 | 具体说明 | 典型场景 |
|---|---|---|
| 策略假设验证 | 在沙盘中修改分群阈值、评分卡切分点、额度公式,观察通过率与坏账率的变化曲线 | 将优质客群准入分从 600 分提升至 650 分,模拟通过率下降 8% 但坏账率下降 15% |
| 多版本对比回测 | 同时运行多个策略版本(V1、V2、V3),使用同一历史样本集进行 A/B 回测 | 对比三种额度矩阵方案在 6 个月窗口期内的 ROI 差异 |
| 极端场景压力测试 | 在沙盘中注入模拟的宏观冲击(如失业率上升 5%),评估策略的鲁棒性 | 模拟疫情冲击下,高风险子群坏账率从 8% 升至 15% 时策略是否触发熔断 |
调优最佳实践: 每次只改变一个变量(如单一子群的评分卡阈值),保持其他条件不变,才能准确归因指标变化。沙盘支持 Git 版本管理,每次策略变更应记录 commit 信息。
回测流程通常包含以下步骤:
- 数据准备: 选取最近 6-12 个月的历史申请样本,包含特征、标签(逾期表现)、决策结果。
- 分群映射: 将样本按沙盘定义的分群规则分配到对应子群。
- 评分计算: 对每个子群使用对应的评分卡计算分数。
- 策略执行: 按沙盘策略规则(准入、额度、定价)生成模拟决策。
- 指标汇总: 计算通过率、坏账率、平均额度、风险调整后收益等。
- 差异分析: 对比沙盘结果与生产实际结果,校准策略参数。
2.3 沙盘与生产环境的差异
沙盘环境与生产环境在数据、时效、风险、迭代方式上存在本质区别,理解这些差异是正确使用沙盘的前提。
| 对比维度 | 沙盘环境 | 生产环境 |
|---|---|---|
| 数据来源 | 历史离线样本(已标注逾期标签) | 实时申请流量(无标签,需实时决策) |
| 时效要求 | 分钟级到小时级(批量回测) | 毫秒级到秒级(实时响应) |
| 风险暴露 | 零风险(不产生真实授信) | 真实风险(决策直接影响资金损失) |
| 迭代频率 | 每日可迭代数十次 | 策略变更需审批,通常周/月级 |
| 样本偏差 | 存在幸存者偏差(仅包含通过样本) | 包含全量申请(通过+拒绝) |
| 计算资源 | 可弹性扩展(Spark/Dask 集群) | 固定资源(需保证峰值吞吐) |
⚠️ 关键风险提示: 沙盘回测结果不能直接等同于生产效果。由于样本偏差(仅包含历史通过样本)和宏观环境变化(客群迁移),沙盘中的通过率通常偏高,坏账率通常偏低。建议在沙盘结果上叠加“悲观系数”(如坏账率上浮 20%)作为生产预估。
2.4 沙盘策略编排的 Python 实现思路
在分群评分沙盘中,Python 策略编排的核心是将分群逻辑、评分计算、规则决策封装为可复用的函数或类,并通过配置驱动的方式实现快速迭代。以下是一个简化的沙盘编排代码示例:
import pandas as pd
import numpy as np
from typing import Dict, List, Callable
class SegmentScoreSandbox:
"""分群评分沙盘策略编排器"""
def __init__(self, segment_rules: Dict[str, Callable],
scorecards: Dict[str, Callable],
policy_rules: Dict[str, Dict]):
self.segment_rules = segment_rules # 分群规则函数字典
self.scorecards = scorecards # 评分卡函数字典
self.policy_rules = policy_rules # 策略规则字典
def assign_segment(self, df: pd.DataFrame) -> pd.Series:
"""将样本分配到对应子群"""
segments = pd.Series(index=df.index, dtype='object')
for seg_name, rule_func in self.segment_rules.items():
mask = rule_func(df)
segments[mask] = seg_name
return segments
def compute_scores(self, df: pd.DataFrame,
segments: pd.Series) -> pd.Series:
"""按子群计算评分"""
scores = pd.Series(index=df.index, dtype='float64')
for seg_name in segments.unique():
mask = segments == seg_name
scores[mask] = self.scorecards[seg_name](df[mask])
return scores
def execute_policy(self, df: pd.DataFrame,
scores: pd.Series,
segments: pd.Series) -> pd.DataFrame:
"""执行策略规则,返回决策结果"""
decisions = []
for seg_name in segments.unique():
mask = segments == seg_name
seg_df = df[mask].copy()
seg_scores = scores[mask]
# 应用该子群的策略规则
seg_policy = self.policy_rules[seg_name]
seg_df['decision'] = np.where(
seg_scores >= seg_policy['approve_threshold'],
'approve', 'reject'
)
seg_df['score'] = seg_scores
decisions.append(seg_df)
return pd.concat(decisions)
def backtest(self, df: pd.DataFrame) -> Dict:
"""执行完整回测流程"""
segments = self.assign_segment(df)
scores = self.compute_scores(df, segments)
result = self.execute_policy(df, scores, segments)
# 汇总指标
summary = {
'total_count': len(result),
'approve_rate': (result['decision'] == 'approve').mean(),
'avg_score': result['score'].mean(),
'segment_distribution': segments.value_counts().to_dict()
}
return summary
编排技巧: 将分群规则、评分卡、策略规则全部外部化为 JSON/YAML 配置文件,沙盘启动时动态加载。这样策略调优只需修改配置文件,无需改动 Python 代码,大幅提升迭代效率。