4. 标签定义与目标变量:如何定义“好客户”与“坏客户”,连续型额度定价目标变量的处理
在额度定价模型中,标签定义直接决定了模型学习的方向。错误的标签定义会导致模型“学偏”,即使特征工程再完美,也无法产出合理的额度策略。本章将系统讲解分类任务中的“好/坏客户”定义标准,以及回归任务中连续型目标变量的处理方法。
4.1 分类任务:好客户 vs 坏客户
额度定价模型通常首先判断客户的风险等级,因此需要将客户划分为“好”与“坏”两类(或多类)。定义标准必须兼顾业务容忍度与数据分布。
4.1.1 核心定义维度
| 维度 | 典型定义 | 适用场景 |
|---|---|---|
| 逾期天数 | 逾期 ≥ 30天 为坏客户 | 消费金融、信用卡 |
| 逾期金额 | 逾期金额 ≥ 本金 20% 为坏客户 | 大额分期、抵押贷 |
| 核销标记 | 已核销 / 委外催收为坏客户 | 银行、持牌消金 |
| 复合指标 | 逾期 ≥ 30天 且 金额 ≥ 500元 | 通用风控模型 |
实践建议:定义“坏客户”时,建议设置 观察期 + 表现期。例如:观察客户在放款后 6 个月内的表现,逾期 ≥ 30 天即标记为坏。表现期太短(如 1 个月)会导致坏样本不充分;太长(如 12 个月)则样本时效性差。
4.1.2 常见定义陷阱与避坑
- 样本不平衡陷阱:坏客户占比通常低于 5%,直接建模会导致模型偏向预测“好客户”。需采用下采样、SMOTE 或代价敏感学习。
- 时间窗口不一致:不同批次客户的观察期长度不同,需统一截断或使用生存分析。
- 多头借贷干扰:客户可能在多个平台同时逾期,单一平台标签可能失真,建议引入外部征信数据交叉验证。
案例:某消费金融平台最初将“逾期 ≥ 90 天”定义为坏客户,导致坏样本仅占 0.8%,模型 AUC 仅 0.62。后将定义调整为“逾期 ≥ 30 天”,坏样本占比升至 3.5%,AUC 提升至 0.78,额度定价的区分度显著改善。
4.2 连续型目标变量:额度定价的回归任务
当模型直接输出建议额度或利率时,目标变量是连续值。此时需要处理截断、偏态分布和业务约束问题。
4.2.1 目标变量类型与处理策略
| 目标变量类型 | 原始分布特点 | 推荐处理方式 |
|---|---|---|
| 授信额度 | 右偏、长尾、有上限 | 对数变换 (log1p) 或 Box-Cox |
| 利率 / 费率 | 集中在几个档位,离散化 | 分箱后做有序回归或分类 |
| 风险评分 | 近似正态,但有极端值 | Winsorize 截断 + 标准化 |
| 损失金额 (LGD) | 大量 0 值 + 右偏 | 两阶段模型:先分类是否损失,再回归损失金额 |
4.2.2 连续目标变量处理流程
- 缺失值处理:额度为 0 或空值需单独标记,不可直接填充。
- 异常值截断:使用百分位数(如 1% ~ 99%)或业务上限截断。
- 分布变换:优先尝试 log1p 变换,使分布接近正态。
- 分箱离散化(可选):当业务需要固定额度档位时,将连续值映射为等级。
- 目标编码对齐:确保训练集与测试集使用相同的变换参数。
注意:连续型目标变量在建模后,预测值需要做 逆变换 才能还原为实际额度。例如使用 log1p 变换,预测后需执行
expm1() 还原。逆变换参数必须与训练集一致,否则额度会系统性偏移。
4.3 标签定义与目标变量处理流程总览
下图展示了从原始数据到最终标签/目标变量的完整处理链路:
4.4 代码示例:连续目标变量处理
以下代码展示了从原始额度数据到建模目标变量的完整处理过程:
import numpy as np
import pandas as pd
from scipy.stats import boxcox
def process_continuous_target(df, target_col='credit_limit', method='log1p'):
"""
连续目标变量处理函数
:param df: 原始DataFrame
:param target_col: 目标变量列名
:param method: 变换方法 ('log1p', 'boxcox', 'winsorize')
:return: 处理后的DataFrame和变换参数
"""
data = df.copy()
# 1. 缺失值标记
data[target_col + '_missing'] = data[target_col].isnull().astype(int)
data[target_col].fillna(0, inplace=True)
# 2. 异常值截断 (Winsorize)
lower = data[target_col].quantile(0.01)
upper = data[target_col].quantile(0.99)
data[target_col] = data[target_col].clip(lower, upper)
# 3. 分布变换
if method == 'log1p':
data[target_col + '_trans'] = np.log1p(data[target_col])
transform_params = {'method': 'log1p'}
elif method == 'boxcox':
transformed, lambda_val = boxcox(data[target_col] + 1) # +1 避免0值
data[target_col + '_trans'] = transformed
transform_params = {'method': 'boxcox', 'lambda': lambda_val}
elif method == 'winsorize':
# 仅截断,不做变换
data[target_col + '_trans'] = data[target_col]
transform_params = {'method': 'winsorize', 'lower': lower, 'upper': upper}
return data, transform_params
# 使用示例
df = pd.DataFrame({'credit_limit': [0, 5000, 10000, 50000, 200000, None]})
df_processed, params = process_continuous_target(df, method='log1p')
print(df_processed[['credit_limit', 'credit_limit_trans']])
4.5 标签定义与目标变量选择决策矩阵
| 业务场景 | 推荐标签类型 | 目标变量 | 处理要点 |
|---|---|---|---|
| 新客额度审批 | 二分类(好/坏) | 是否违约 | 表现期 ≥ 6个月,逾期 ≥ 30天 |
| 存量客户提额 | 回归 | 建议额度 | log1p变换,截断极端值 |
| 利率差异化定价 | 有序分类 | 利率档位 | 分箱后做有序逻辑回归 |
| 损失准备金计提 | 两阶段 | 是否损失 + 损失金额 | 先分类再回归,处理零膨胀 |
核心原则:标签定义必须 可回溯、可验证、可解释。每次模型迭代时,标签定义逻辑应作为元数据与模型版本一同保存,避免因定义漂移导致模型效果评估失真。