2. 分群评分沙盘概念

2.1 什么是分群评分沙盘

分群评分沙盘(Segmentation & Score Sandbox)是授信决策引擎中用于离线模拟、验证和优化策略组合的隔离环境。它将客群按照风险特征划分为多个子群(Segment),并为每个子群分配独立的评分卡与决策规则,在沙盘环境中进行“假设推演”,从而在不影响生产流量的前提下完成策略调优。

核心定义: 分群评分沙盘 = 客群分群 × 评分卡矩阵 × 离线回测引擎 × 策略编排沙盒。

沙盘的核心组件包括:

  • 客群分群器: 基于规则(如年龄、收入、征信分)或模型(如聚类、决策树)将申请客群划分为若干同质子群。
  • 评分卡矩阵: 每个子群对应一张评分卡,评分卡可以是逻辑回归、XGBoost 或集成模型,输出违约概率或信用分数。
  • 策略编排层: 在沙盘内定义每个子群的准入规则、额度矩阵、定价策略,支持 Python 脚本动态编排。
  • 回测引擎: 使用历史样本数据模拟策略执行,输出通过率、坏账率、ROI 等关键指标。
分群评分沙盘架构图 分群评分沙盘核心架构 历史样本数据 客群分群器(规则/聚类) 子群 A(优质) 子群 B(标准) 子群 C(高风险) 评分卡 A 评分卡 B 评分卡 C 策略编排层(Python 沙盒) 沙盘内所有组件均使用历史数据离线运行,不产生真实授信决策

2.2 沙盘在策略调优与回测中的作用

沙盘是策略调优的“安全实验室”,其核心价值体现在以下三个维度:

作用维度 具体说明 典型场景
策略假设验证 在沙盘中修改分群阈值、评分卡切分点、额度公式,观察通过率与坏账率的变化曲线 将优质客群准入分从 600 分提升至 650 分,模拟通过率下降 8% 但坏账率下降 15%
多版本对比回测 同时运行多个策略版本(V1、V2、V3),使用同一历史样本集进行 A/B 回测 对比三种额度矩阵方案在 6 个月窗口期内的 ROI 差异
极端场景压力测试 在沙盘中注入模拟的宏观冲击(如失业率上升 5%),评估策略的鲁棒性 模拟疫情冲击下,高风险子群坏账率从 8% 升至 15% 时策略是否触发熔断
调优最佳实践: 每次只改变一个变量(如单一子群的评分卡阈值),保持其他条件不变,才能准确归因指标变化。沙盘支持 Git 版本管理,每次策略变更应记录 commit 信息。

回测流程通常包含以下步骤:

  1. 数据准备: 选取最近 6-12 个月的历史申请样本,包含特征、标签(逾期表现)、决策结果。
  2. 分群映射: 将样本按沙盘定义的分群规则分配到对应子群。
  3. 评分计算: 对每个子群使用对应的评分卡计算分数。
  4. 策略执行: 按沙盘策略规则(准入、额度、定价)生成模拟决策。
  5. 指标汇总: 计算通过率、坏账率、平均额度、风险调整后收益等。
  6. 差异分析: 对比沙盘结果与生产实际结果,校准策略参数。

2.3 沙盘与生产环境的差异

沙盘环境与生产环境在数据、时效、风险、迭代方式上存在本质区别,理解这些差异是正确使用沙盘的前提。

对比维度 沙盘环境 生产环境
数据来源 历史离线样本(已标注逾期标签) 实时申请流量(无标签,需实时决策)
时效要求 分钟级到小时级(批量回测) 毫秒级到秒级(实时响应)
风险暴露 零风险(不产生真实授信) 真实风险(决策直接影响资金损失)
迭代频率 每日可迭代数十次 策略变更需审批,通常周/月级
样本偏差 存在幸存者偏差(仅包含通过样本) 包含全量申请(通过+拒绝)
计算资源 可弹性扩展(Spark/Dask 集群) 固定资源(需保证峰值吞吐)
⚠️ 关键风险提示: 沙盘回测结果不能直接等同于生产效果。由于样本偏差(仅包含历史通过样本)和宏观环境变化(客群迁移),沙盘中的通过率通常偏高,坏账率通常偏低。建议在沙盘结果上叠加“悲观系数”(如坏账率上浮 20%)作为生产预估。
沙盘环境与生产环境对比 沙盘环境 vs 生产环境 沙盘环境(离线) 历史样本(含标签) 批量回测(分钟级) 零风险迭代 输出:回测指标报告 生产环境(在线) 实时申请流量(无标签) 实时决策(毫秒级) 真实风险暴露 输出:授信决策结果 差异校准 沙盘结果需经过偏差校准(悲观系数)后才能作为生产策略的参考依据

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 代码,大幅提升迭代效率。