加载中...

信用评分卡(Scorecard)入门:银行到底是怎么给你打分的?

最近接手了新项目,未来很长一段时间估计都要跟**信用评分卡(Credit Scorecard)**死磕了。

趁着刚开始梳理,赶紧把这套逻辑整理出来。虽然现在深度学习、XGBoost 满天飞,但在金融风控领域,逻辑回归(Logistic Regression)+ 评分卡依然是绝对的王者。

为啥?因为可解释性。银行拒了你的贷款,监管机构问起来,银行得能说清楚:“因为他收入太低”或者“因为他最近逾期太多”。如果是神经网络,总不能说:“因为我的几百万个参数算出来他是坏人”吧?

这篇文章就用大白话,把评分卡这套东西从头到尾盘一遍。

什么是评分卡?

简单说,就是把客户的各种属性(年龄、收入、职业等)转化成一个分数。分数越高,信用越好(通常是这样,也有反过来的)。

在业务上,通常分为三种卡:

  • A卡 (Application score card):申请评分卡。你申请贷款时用,决定批不批、批多少。
  • B卡 (Behavior score card):行为评分卡。贷后管理用,看你最近还款习不习惯,要不要提额或风控。
  • C卡 (Collection score card):催收评分卡。逾期了用,看你还钱的概率大不大,决定催收策略。

我们今天主要聊最经典的 A 卡开发流程。

核心流程:从数据到分数

开发一张评分卡,大概分这么几步:

  1. 数据准备:找好人(按时还钱)和坏人(逾期超过一定天数)。
  2. 分箱 (Binning):把连续变量变成一个个区间。
  3. WOE 编码:把箱子变成数字。
  4. 特征筛选 (IV值):挑出最有用的特征。
  5. 逻辑回归建模:算概率。
  6. 分数转换:把概率变成 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 之后,就可以丢进逻辑回归模型了。

P(Y=1X)=11+e(w0+w1x1+...+wnxn)P(Y=1|X) = \frac{1}{1 + e^{-(w_0 + w_1x_1 + ... + w_nx_n)}}

模型输出的是一个 0 到 1 之间的违约概率 (PD)

5. 分数转换 (Scaling)

业务人员看不懂“违约概率 0.023”,他们喜欢看“信用分 750”。所以需要一个公式把概率转换成整数分。

通常用这个公式:

Score=AB×ln(Odds1)Score = A - B \times \ln(\frac{Odds}{1})

其中 Odds=P1POdds = \frac{P}{1-P} (坏好比)。

这里有两个关键参数:

  • 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}")

模型评估

做完模型,怎么知道好不好?看两个指标:

  1. AUC (Area Under Curve): 衡量排序能力。0.5 是瞎猜,1 是完美。一般 0.7 以上就算能用了。
  2. KS (Kolmogorov-Smirnov): 衡量区分能力。就是好人累积分布和坏人累积分布之间的最大差距。一般要求 KS > 0.3。

总结

信用评分卡看着简单,其实坑很多:

  • 拒绝推断 (Reject Inference):你建模的数据都是通过的人,那些被拒的人(可能更坏)没在模型里,这会导致偏差。
  • 稳定性 (PSI):模型上线后,如果客群变了(比如突然来了一波薅羊毛的),模型分数分布会不会剧烈波动?

接下来的工作就要跟这些细节搏斗了。希望能搞出一张稳健的 A 卡!

Author
Tong
© 2025 by Tong 本文基于 CC BY-NC-SA 4.0 许可 CC 协议 必须注明创作者 仅允许将作品用于非商业用途 改编作品必须遵循相同条款进行共享 最后更新:2025/12/15