最近接手了新项目,未来很长一段时间估计都要跟**信用评分卡(Credit Scorecard)**死磕了。
趁着刚开始梳理,赶紧把这套逻辑整理出来。虽然现在深度学习、XGBoost 满天飞,但在金融风控领域,逻辑回归(Logistic Regression)+ 评分卡依然是绝对的王者。
为啥?因为可解释性。银行拒了你的贷款,监管机构问起来,银行得能说清楚:“因为他收入太低”或者“因为他最近逾期太多”。如果是神经网络,总不能说:“因为我的几百万个参数算出来他是坏人”吧?
这篇文章就用大白话,把评分卡这套东西从头到尾盘一遍。
什么是评分卡?
简单说,就是把客户的各种属性(年龄、收入、职业等)转化成一个分数。分数越高,信用越好(通常是这样,也有反过来的)。
在业务上,通常分为三种卡:
- A卡 (Application score card):申请评分卡。你申请贷款时用,决定批不批、批多少。
- B卡 (Behavior score card):行为评分卡。贷后管理用,看你最近还款习不习惯,要不要提额或风控。
- C卡 (Collection score card):催收评分卡。逾期了用,看你还钱的概率大不大,决定催收策略。
我们今天主要聊最经典的 A 卡开发流程。
核心流程:从数据到分数
开发一张评分卡,大概分这么几步:
- 数据准备:找好人(按时还钱)和坏人(逾期超过一定天数)。
- 分箱 (Binning):把连续变量变成一个个区间。
- WOE 编码:把箱子变成数字。
- 特征筛选 (IV值):挑出最有用的特征。
- 逻辑回归建模:算概率。
- 分数转换:把概率变成 300-900 这样的整数分。
1. 分箱 (Binning)
比如“年龄”这个字段,从 18 岁到 80 岁都有。直接丢进模型不太好,因为年龄和违约率往往不是线性的(太年轻没钱,太老了也没钱,中间最稳)。
所以我们要分箱,比如:
- [18, 25)
- [25, 35)
- [35, 50)
- [50, +∞)
分箱的好处是稳定,哪怕你年龄从 26 变到 29,只要还在同一个箱子里,分数就不变。
2. WOE (Weight of Evidence)
分完箱之后,每个箱子怎么赋值?用 WOE。
公式不写了,直接说人话:WOE 就是衡量这个箱子里的人,相比整体,是更容易变坏还是更容易变好。
- WOE > 0:这个箱子里好人多(比如收入高的人群)。
- WOE < 0:这个箱子里坏人多(比如无业游民)。
- WOE = 0:跟整体平均水平一样。
把原始数据(比如“30岁”)替换成对应的 WOE 值(比如 0.5),这就是 WOE 编码。
3. IV (Information Value)
特征那么多,哪个有用?看 IV 值。
IV 其实就是 WOE 的加权和。它衡量的是这个特征区分好人和坏人的能力。
- IV < 0.02:没啥用,扔了。
- 0.02 < IV < 0.1:有点用。
- 0.1 < IV < 0.3:挺好用。
- IV > 0.5:太好用了,好得有点假,要检查是不是用到未来数据了。
4. 逻辑回归 (Logistic Regression)
数据都变成 WOE 之后,就可以丢进逻辑回归模型了。
模型输出的是一个 0 到 1 之间的违约概率 (PD)。
5. 分数转换 (Scaling)
业务人员看不懂“违约概率 0.023”,他们喜欢看“信用分 750”。所以需要一个公式把概率转换成整数分。
通常用这个公式:
其中 (坏好比)。
这里有两个关键参数:
- Base Point: 比如设定 Odds=1:1 时,分数为 600 分。
- PDO (Points to Double the Odds): 比如设定 PDO=20,意味着 Odds 每翻一倍,分数减少 20 分。
代码实战 (Python)
虽然现在有很多现成的包(比如 toad, scorecardpy),但理解原理最好还是手写一部分。
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
# 1. 计算 WOE 和 IV 的伪代码逻辑
def calculate_woe_iv(df, feature, target):
# 统计每个箱子里的好人数和坏人数
lst = []
for i in range(df[feature].nunique()):
val = list(df[feature].unique())[i]
lst.append({
'Value': val,
'All': df[df[feature] == val].count()[feature],
'Bad': df[(df[feature] == val) & (df[target] == 1)].count()[feature]
})
data = pd.DataFrame(lst)
data['Good'] = data['All'] - data['Bad']
# 计算占比
total_bad = data['Bad'].sum()
total_good = data['Good'].sum()
data['Bad Rate'] = data['Bad'] / total_bad
data['Good Rate'] = data['Good'] / total_good
# 计算 WOE
# 加上一个极小值防止分母为0
data['WOE'] = np.log((data['Good Rate'] + 1e-5) / (data['Bad Rate'] + 1e-5))
# 计算 IV
data['IV'] = (data['Good Rate'] - data['Bad Rate']) * data['WOE']
return data
# 2. 训练模型
# 假设 X_woe 是已经转换成 WOE 值的特征矩阵
clf = LogisticRegression()
clf.fit(X_woe, y)
# 3. 得到系数
coefficients = clf.coef_
intercept = clf.intercept_
print(f"截距: {intercept}")
print(f"特征权重: {coefficients}")
模型评估
做完模型,怎么知道好不好?看两个指标:
- AUC (Area Under Curve): 衡量排序能力。0.5 是瞎猜,1 是完美。一般 0.7 以上就算能用了。
- KS (Kolmogorov-Smirnov): 衡量区分能力。就是好人累积分布和坏人累积分布之间的最大差距。一般要求 KS > 0.3。
总结
信用评分卡看着简单,其实坑很多:
- 拒绝推断 (Reject Inference):你建模的数据都是通过的人,那些被拒的人(可能更坏)没在模型里,这会导致偏差。
- 稳定性 (PSI):模型上线后,如果客群变了(比如突然来了一波薅羊毛的),模型分数分布会不会剧烈波动?
接下来的工作就要跟这些细节搏斗了。希望能搞出一张稳健的 A 卡!