信用卡 A 卡建模实战:从 WOE 评分卡到授信审批决策
一、信用卡 A 卡究竟是什么?
银行信用卡风险管理中,经常会出现三个名称:
| 模型 | 英文名称 | 主要用途 |
|---|---|---|
| A 卡 | Application Scorecard | 申请阶段的信用风险评估 |
| B 卡 | Behavior Scorecard | 存量客户的行为风险评估 |
| C 卡 | Collection Scorecard | 逾期客户的催收风险评估 |
三者的区别主要体现在客户状态和预测目标上。
A 卡关注申请时点。
例如,一位从未持有该银行信用卡的客户提出申请。银行希望根据其申请资料和外部征信信息,判断未来是否可能违约。
B 卡关注持卡之后。
客户已经使用信用卡一段时间。银行可以根据账单、还款、额度使用和交易等行为预测未来风险。
C 卡关注逾期之后。
客户已经出现逾期。银行需要判断其进一步恶化、恢复还款或产生回收的可能性,以辅助催收策略。
本文重点研究 A 卡。
1. A 卡预测什么?
最常见的做法是定义一个明确的表现期,例如:
在信用卡获批后的未来 12 个月内,客户是否发生 90 天及以上逾期,或符合银行既定标准的违约事件?
于是,A 卡可以转化成一个二分类问题:
[
Y=
\begin{cases}
1, & \text{表现期内发生违约}\
0, & \text{表现期内未发生违约}
\end{cases}
]
模型输入申请时点的客户信息,输出违约风险概率:
[
PD=P(Y=1\mid X)
]
这里的违约定义可以根据业务目标调整,例如 60+ DPD、90+ DPD 或其他约定的不良事件。
但在模型开发、验证和使用过程中,应当保持标签定义、表现期和风险客群的一致性。
需要注意,申请评分模型估计的通常是特定表现期内的风险事件概率。若最终用于监管资本或减值参数计量,还需要按照相应要求确定违约口径和校准方法。
2. A 卡通常使用哪些变量?
| 变量类别 | 典型特征 |
|---|---|
| 征信历史 | 历史逾期次数、最长逾期天数、信用账户数量 |
| 用信情况 | 当前贷款余额、信用额度使用率、未结清账户数 |
| 信用查询 | 最近 1、3、6 个月贷款审批查询次数 |
| 偿债能力 | 收入、负债收入比、每月还款负担 |
| 申请信息 | 本次申请额度、产品类型、申请渠道 |
| 历史关系 | 是否为本行存量客户、历史贷款表现 |
实际使用的变量还需要符合适用的法律法规、数据授权和公平信贷要求。
A 卡有一个重要限制:所有预测变量都必须在申请决策时点已经可获得。
客户获批后的消费金额、首次账单、未来逾期记录和催收行为,都属于后续发生的信息。
二、传统 A 卡为什么喜欢用 WOE + Logistic?
尽管如今已有大量复杂机器学习模型,但 WOE + Logistic Regression 依然是非常典型的传统评分卡开发路线。
原因主要有三个。
第一,逻辑回归结构简单,便于解释不同风险变量如何影响违约概率。
第二,WOE 分箱可以将复杂的原始变量转换为相对稳定的风险特征,并允许对缺失值、异常值和非线性关系进行处理。
第三,模型最终可以转换为标准分数表,适合在审批系统中部署。
例如:
客户提交申请
|
v
获取征信与申请资料
|
v
计算申请评分卡变量
|
v
变量分箱 + WOE 转换
|
v
Logistic Regression
|
v
计算违约风险概率 PD
|
v
转换成 A 卡评分
|
v
结合准入规则和额度策略
|
v
批准 / 进一步审核 / 拒绝
其中最有特点的部分就是 WOE。
1. WOE 是什么?
WOE 全称 Weight of Evidence,通常翻译为证据权重。
它主要用于衡量某个特征分组相对于总体的好坏客户分布差异。
假设有一个变量:最近六个月的征信查询次数。
将其分为四组:
| 征信查询次数 | 好客户数 | 坏客户数 | 坏样本率 |
|---|---|---|---|
| 0 次 | 4,000 | 200 | 4.8% |
| 1~2 次 | 3,000 | 300 | 9.1% |
| 3~5 次 | 2,000 | 500 | 20.0% |
| 6 次及以上 | 1,000 | 1,000 | 50.0% |
这是示意数据。
可以看到,查询次数越多,坏样本率越高。
但是传统评分卡通常会进一步将坏样本率转换为 WOE。
采用坏客户占比除以好客户占比的定义:
[
WOE_i=\ln\left(
\frac{Bad_i/Bad_{total}}
{Good_i/Good_{total}}
\right)
]
其中:
- (Bad_i):第 (i) 个分箱中的坏客户数量。
- (Good_i):第 (i) 个分箱中的好客户数量。
- (Bad_{total}):总体坏客户数量。
- (Good_{total}):总体好客户数量。
按照上面的数据:
总好客户为 10,000 人,总坏客户为 2,000 人。
对于查询 6 次及以上的客户:
[
WOE=\ln\left(\frac{1000/2000}{1000/10000}\right)
=\ln(5)\approx1.609
]
这个结果表示,该组客户在坏样本中的相对占比显著高于其在好样本中的相对占比。
在上述 WOE 定义下:
- WOE 为正:该组相对偏向坏客户。
- WOE 为负:该组相对偏向好客户。
- WOE 接近 0:该组的好坏分布与总体接近。
部分工具采用相反的 WOE 定义,因此阅读代码或模型结果时,应当先确认其符号约定。
2. IV 又是什么?
IV(Information Value)经常与 WOE 一起使用。
它用于衡量某个变量整体对好坏客户的区分程度。
[
IV=\sum_i
(BadDist_i-GoodDist_i)\times WOE_i
]
其中:
[
BadDist_i=\frac{Bad_i}{Bad_{total}}
]
[
GoodDist_i=\frac{Good_i}{Good_{total}}
]
IV 越高,通常说明该变量在当前建模样本上的区分信息越强。
一些传统评分卡开发经验会采用以下参考范围:
| IV | 常见经验解释 |
|---|---|
| 小于 0.02 | 区分信息较弱 |
| 0.02~0.10 | 有一定预测信息 |
| 0.10~0.30 | 预测信息较明显 |
| 0.30~0.50 | 预测信息较强 |
| 大于 0.50 | 需要重点检查变量来源与数据泄漏 |
这些是历史经验阈值,并非统一的统计检验标准。
尤其需要注意,IV 很高的变量可能确实非常有预测能力,也可能来自目标泄漏、过度细分或特殊样本结构。
建模时不能仅凭 IV 决定变量是否入模。 还需要考虑变量稳定性、业务逻辑、共线性和可解释性。
三、案例一:利用德国信用风险数据开发传统 A 卡
1. 业务背景
第一个案例使用 UCI 的经典 Statlog German Credit Data。
数据集包含:
- 1,000 条消费信贷记录。
- 20 个预测特征。
- 700 条良好信用记录。
- 300 条不良信用记录。
主要变量包括贷款金额、贷款期限、储蓄账户状态、历史信用记录、现有分期负担等。
这是一个适合演示申请评分卡开发过程的公开数据集。
需要说明,该数据集属于历史消费信贷资料,包含的贷款产品并非全部为信用卡。由于缺少真正的信用卡完整申请与表现期数据,这里将其用作传统 A 卡开发方法的教学样本。
原始数据年代较早,而且好坏样本比例经过特定抽样处理,因此最终模型分数只能用于演示,不能直接用于当前银行信用卡申请审批。
2. 使用 scorecardpy
这里使用专门面向评分卡开发的 Python 开源库 scorecardpy。
GitHub 项目:
https://github.com/ShichenXie/scorecardpy
它提供完整的评分卡开发组件:
woebin:变量分箱。woebin_ply:WOE 转换。scorecard:评分卡转换。scorecard_ply:计算客户信用分数。perf_eva:模型性能评价。perf_psi:分布稳定性分析。
安装依赖:
pip install scorecardpy scikit-learn pandas numpy
3. 完整 Python 实践
import numpy as np
import pandas as pd
import scorecardpy as sc
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score, roc_curve
# 1. 加载德国信用风险数据
df = sc.germancredit()
# 2. 选择若干申请阶段可用的变量
features = [
"status.of.existing.checking.account",
"duration.in.month",
"credit.history",
"credit.amount",
"savings.account.and.bonds",
"installment.rate.in.percentage.of.disposable.income",
"other.installment.plans",
"number.of.existing.credits.at.this.bank"
]
target = "creditability"
df = df[features + [target]].copy()
# 3. 划分训练集和测试集
train, test = train_test_split(
df,
test_size=0.3,
stratify=df[target],
random_state=42
)
# 4. 只在训练集上计算 WOE 分箱
bins = sc.woebin(
train,
y=target,
x=features,
positive="bad|1",
bin_num_limit=5
)
# 5. 将原始变量转换为 WOE
train_woe = sc.woebin_ply(train, bins)
test_woe = sc.woebin_ply(test, bins)
woe_cols = [
c for c in train_woe.columns
if c.endswith("_woe")
]
X_train = train_woe[woe_cols]
X_test = test_woe[woe_cols]
y_train = (train[target] == "bad").astype(int)
y_test = (test[target] == "bad").astype(int)
# 6. 训练 Logistic Regression
model = LogisticRegression(
C=0.6,
max_iter=2000,
random_state=42
)
model.fit(X_train, y_train)
# 7. 预测坏客户概率
pred_pd = model.predict_proba(X_test)[:, 1]
# 8. 评价模型
auc = roc_auc_score(y_test, pred_pd)
fpr, tpr, thresholds = roc_curve(y_test, pred_pd)
ks = np.max(tpr - fpr)
print("AUC:", round(auc, 4))
print("KS:", round(ks, 4))
# 9. 将概率模型转换为评分卡
card = sc.scorecard(
bins,
model,
woe_cols,
points0=600,
odds0=1/19,
pdo=50,
digits=2
)
# 10. 计算测试集客户信用分数
score_df = sc.scorecard_ply(
test,
card,
print_step=0
)
result = pd.DataFrame({
"score": score_df["score"].to_numpy(),
"pd": pred_pd,
"actual_bad": y_test.to_numpy()
})
print(result.head(10))
# 11. 按分数分组,检查实际坏样本率
result["score_group"] = pd.qcut(
result["score"].rank(method="first"),
q=5,
labels=False
) + 1
summary = result.groupby("score_group").agg(
count=("actual_bad", "size"),
avg_score=("score", "mean"),
avg_pd=("pd", "mean"),
actual_bad_rate=("actual_bad", "mean")
)
print(summary)
这段代码完成了传统评分卡的主要技术流程:
原始变量 → 分箱 → WOE → Logistic → 预测 PD → 信用评分。
其中最重要的处理是:分箱规则仅使用训练集建立,然后应用到测试集。
如果在全量数据上先计算 WOE,再划分训练集和测试集,就会让测试集标签信息提前进入分箱过程,使模型评估偏乐观。
4. 如何把 PD 转换成信用分数?
这是传统 A 卡最有代表性的设计。
逻辑回归输出的原始形式为:
[
PD=\frac{1}{1+e^{-z}}
]
其中:
[
z=\beta_0+\sum_i\beta_i WOE_i
]
将其转换成违约赔率:
[
Odds=\frac{PD}{1-PD}
]
然后使用:
[
\boxed{Score=A-B\ln(Odds)}
]
其中:
[
B=\frac{PDO}{\ln(2)}
]
PDO(Points to Double the Odds)表示坏客户与好客户的赔率每翻倍一次,信用评分下降多少分。
在上面的代码中,我们设定:
points0 = 600
odds0 = 1/19
pdo = 50
含义是:
- 基准分数为 600 分。
- 600 分对应坏好赔率 1:19,即 PD 为 5%。
- 坏好赔率每翻倍,分数下降 50 分。
因此,按这一规则:
| 评分 | 坏好赔率 | 对应 PD |
|---|---|---|
| 500 | 4:19 | 17.39% |
| 550 | 2:19 | 9.52% |
| 600 | 1:19 | 5.00% |
| 650 | 1:38 | 2.56% |
| 700 | 1:76 | 1.30% |
这张表说明了评分与风险的数学关系。分数越高,违约概率越低。
但这里的 600 分、50 PDO、5% 基准 PD 都是人为设定的评分刻度,并不代表银行必须采用相同标准。
而且,评分转换本身不会让原始概率变得更加准确。只有原始模型经过适当校准,分数与 PD 之间的对应关系才具有实际风险意义。
5. 如何评价 A 卡效果?
代码输出两个经典指标。
AUC
用于衡量模型区分好客户与坏客户的能力。
AUC 为 0.5 接近随机排序;越接近 1,通常说明模型的风险区分能力越好。
KS
KS 用于衡量不同风险阈值下,坏客户累计分布与好客户累计分布之间的最大差异。
[
KS=\max_t(TPR_t-FPR_t)
]
AUC 和 KS 都能评价风险排序能力。
但它们不能直接证明 PD 校准准确,也无法独立判断审批策略是否能够盈利。
四、案例二:使用 Home Credit 数据模拟信用卡申请准入
1. 业务背景
第二个案例使用 Kaggle 的 Home Credit Default Risk 竞赛数据。
该数据集的申请主表 application_train.csv 包含约 30.75 万条贷款申请记录,并提供申请时点的收入、授信金额、就业情况和外部风险评分等信息。
其中一个重要变量是:
NAME_CONTRACT_TYPE
它区分两种贷款类型:
Cash loans
Revolving loans
我们可以选择 Revolving loans,研究循环授信产品的申请风险预测。
循环授信与信用卡有一定相似性:客户获得授信额度后,可以根据规则反复使用额度。
不过,公开数据中的循环贷款未必对应标准信用卡产品,而且只提供特定的还款困难标签。
其 TARGET 表示客户在样本贷款最初若干期还款中是否发生符合数据集定义的严重付款困难。公开字段说明使用了未公开的 X 天、Y 期标准。
所以,这个案例用于演示申请风险评分与准入决策方法,并不直接对应严格的未来 12 个月 90+ DPD 违约标签。
2. 获取数据
前往 Kaggle 的 Home Credit Default Risk 竞赛页面,下载:
application_train.csv
本案例先只使用申请主表。
完整的 Home Credit 数据还包括征信历史、历史申请记录、此前贷款还款记录等表。正式开发 A 卡时,这些数据往往具有较高价值,但需要根据申请时点正确提取历史信息。
3. Python 实践:申请风险评分与准入策略
这次使用梯度提升树,重点演示如何将机器学习模型输出转化为审批决策。
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.metrics import roc_auc_score, brier_score_loss
# 1. 加载申请数据
df = pd.read_csv(
"application_train.csv",
usecols=[
"TARGET",
"NAME_CONTRACT_TYPE",
"AMT_INCOME_TOTAL",
"AMT_CREDIT",
"AMT_ANNUITY",
"DAYS_EMPLOYED",
"DAYS_LAST_PHONE_CHANGE",
"EXT_SOURCE_2",
"EXT_SOURCE_3"
]
)
# 2. 选择循环授信样本
df = df[
df["NAME_CONTRACT_TYPE"] == "Revolving loans"
].copy()
# 3. 构造申请阶段特征
X = df[[
"AMT_INCOME_TOTAL",
"AMT_CREDIT",
"AMT_ANNUITY",
"DAYS_EMPLOYED",
"DAYS_LAST_PHONE_CHANGE",
"EXT_SOURCE_2",
"EXT_SOURCE_3"
]].copy()
# 处理就业天数特殊编码
X.loc[
X["DAYS_EMPLOYED"] == 365243,
"DAYS_EMPLOYED"
] = np.nan
# 转为就业年限
X["employment_years"] = (
-X["DAYS_EMPLOYED"] / 365
).clip(lower=0)
X = X.drop(columns=["DAYS_EMPLOYED"])
# 构造额度收入比
X["credit_income_ratio"] = (
X["AMT_CREDIT"]
/ X["AMT_INCOME_TOTAL"].replace(0, np.nan)
)
X = X.replace([np.inf, -np.inf], np.nan)
y = df["TARGET"].astype(int)
# 4. 划分训练、验证、测试集
X_train, X_temp, y_train, y_temp = train_test_split(
X, y,
test_size=0.4,
stratify=y,
random_state=42
)
X_valid, X_test, y_valid, y_test = train_test_split(
X_temp, y_temp,
test_size=0.5,
stratify=y_temp,
random_state=42
)
# 5. 训练梯度提升树
model = HistGradientBoostingClassifier(
max_iter=150,
max_leaf_nodes=15,
learning_rate=0.05,
min_samples_leaf=100,
l2_regularization=1.0,
random_state=42
)
model.fit(X_train, y_train)
# 6. 预测风险
valid_prob = model.predict_proba(X_valid)[:, 1]
test_prob = model.predict_proba(X_test)[:, 1]
print(
"测试集 AUC:",
roc_auc_score(y_test, test_prob)
)
print(
"测试集 Brier:",
brier_score_loss(y_test, test_prob)
)
# 7. 在验证集上制定一个模拟审批策略
# 假设希望对风险最低的60%申请予以批准
threshold = np.quantile(valid_prob, 0.60)
# 8. 在独立测试集上执行策略
accepted = test_prob <= threshold
print("风险阈值:", threshold)
print("模拟通过率:", accepted.mean())
print(
"模拟通过客群的实际坏样本率:",
y_test.to_numpy()[accepted].mean()
)
# 9. 按风险分数进行十等分
result = pd.DataFrame({
"risk": test_prob,
"actual_bad": y_test.to_numpy()
})
result["risk_decile"] = pd.qcut(
result["risk"].rank(method="first"),
q=10,
labels=False
) + 1
summary = result.groupby("risk_decile").agg(
count=("actual_bad", "size"),
avg_risk=("risk", "mean"),
bad_rate=("actual_bad", "mean")
)
print(summary)
4. 为什么需要将模型与审批策略分开?
这是信用卡 A 卡实际落地中非常重要的一点。
模型输出的是风险,而银行最终需要作出业务决策。
假设一批客户的风险预测结果如下:
| 客户 | 预测坏账概率 | 风险排序 |
|---|---|---|
| A | 1.2% | 很低 |
| B | 2.8% | 较低 |
| C | 5.5% | 中等 |
| D | 9.3% | 较高 |
| E | 18.0% | 很高 |
银行可以根据预期损失、资金成本、风险偏好和业务收益来设定审批策略。
例如:
预测 PD
|
+-- PD ≤ 3% ------> 候选自动通过
|
+-- 3% < PD ≤ 8% -> 进一步审核
|
+-- PD > 8% ------> 候选拒绝
以上阈值仅为教学示例。
实际审批还需要综合身份核验、反欺诈、准入政策、偿债能力和授信额度等条件。
模型分数高,也可能因为触发强制准入规则而被拒绝;模型分数较低的客户,也可能通过额外材料审核获得批准。
5. 从通过率到坏账率
假设银行希望提高信用卡申请通过率。
最直接的方法之一就是放宽模型分数阈值。
但是,通过率提高以后,新进入的客户通常具有更高的边际信用风险。
例如:
| 模拟通过率 | 通过客群平均坏样本率 |
|---|---|
| 40% | 2.0% |
| 50% | 2.6% |
| 60% | 3.4% |
| 70% | 4.5% |
| 80% | 6.2% |
这些是示意数据,并非 Home Credit 的实测结果。
从表中可以发现,通过率越高,准入客群的平均坏样本率可能越高。
但这并不意味着应该一味降低审批通过率。
银行还需要考虑客户带来的利息收入、手续费收入、资金成本、运营成本和信用损失。
在一个简化的决策模型中,可以估算:
[
ExpectedProfit=
ExpectedRevenue-ExpectedCost-ExpectedLoss
]
其中信用损失可以进一步近似为:
[
ExpectedLoss=PD\times LGD\times EAD
]
真实信用卡产品中,还要考虑客户是否激活卡片、实际用信行为、额度使用率以及不同产品的收入结构。
A 卡的最终业务价值,在于帮助银行选择风险收益更合理的客户组合。
五、A 卡最棘手的问题:拒绝推断
前面的公开数据案例有一个共同问题。
数据主要来自已经获得贷款的客户。
但真实信用卡申请流程中,还有一批客户在审批阶段就被银行拒绝。
对于这些客户,银行通常无法直接观察他们在本行获批后的还款表现。
这会产生一个典型问题:
Reject Inference(拒绝推断)。
1. 为什么拒绝推断会影响模型?
假设银行收到 100,000 份信用卡申请。
其中:
- 60,000 人获批。
- 40,000 人被拒绝。
银行可以观察获批客户后续 12 个月的信用卡还款表现。
但是,另外 40,000 名被拒绝的申请人并没有获得本行信用卡,银行无法直接知道如果批准他们,实际违约结果会怎样。
如果只使用 60,000 名获批客户训练模型,模型学到的风险关系就可能受到历史审批策略影响。
例如,历史策略几乎从不批准某类高风险客户,训练数据中就很少出现这类客户。
此时即使模型在获批客户测试集上的 AUC 很高,也不意味着它能够准确预测所有申请人的风险。
这涉及选择偏差和样本支持范围的问题。
2. 常见的拒绝推断方法
| 方法 | 主要思路 |
|---|---|
| Parceling | 根据已知样本风险分布,对拒绝样本赋予推断标签 |
| Fuzzy Augmentation | 对拒绝样本构建带概率权重的好坏标签 |
| Reweighting | 根据样本获批概率,对训练样本进行加权 |
| 外部信用表现补充 | 在合法授权和适当观察期下,利用外部表现补充信息 |
| 随机化探索样本 | 在明确风险控制下,通过部分探索性审批获取代表性表现数据 |
其中 Reweighting 可以利用获批概率模型估计选择机制。
但这类方法依赖一定假设,例如获批机制能否由已观测变量充分解释,以及不同申请客群是否具备足够的获批样本。
因此,拒绝推断没有适用于所有银行的统一解决方案。
对于真实 A 卡,必须结合历史审批策略、样本覆盖情况和实际业务约束选择合适方法。
六、如何从模型开发走向银行生产环境?
前面的案例展示了模型算法,但正式开发 A 卡还需要解决几个工程问题。
1. 样本定义必须清晰
一个典型的建模样本设计如下:
申请时点 T
|
|-- 获取申请资料与征信信息
|
|-- 根据审批策略作出决策
|
v
申请获批并开立账户
|
|-- 观察未来12个月
|
v
确定是否发生定义的违约事件
其中需要明确:
- 客群范围:信用卡新客户、存量客户新申请等。
- 观察时点:申请提交、审批完成或授信生效。
- 表现期:例如 12 个月。
- 坏样本:符合既定违约标准的客户。
- 好样本:完整表现期内未发生该事件的客户。
- 不确定样本:观察期不足、表现缺失或需要特殊处理的记录。
对于尚未走完整个表现期的客户,不能简单地将其标记为好样本。
还需要明确申请维度与客户维度的关系,避免同一客户重复申请导致训练集和测试集之间出现信息交叉。
2. 建模时需要比较哪些指标?
| 指标 | 主要作用 |
|---|---|
| AUC | 判断好坏客户的区分能力 |
| KS | 衡量好坏样本累计分布差异 |
| Brier Score | 衡量概率预测误差 |
| 分数分组坏样本率 | 检查风险排序和校准 |
| PSI | 检查评分或变量分布变化 |
| OOT AUC / KS | 检查跨时间稳定性 |
| O/E | 比较实际与预测坏样本数量 |
| 审批通过率 | 衡量准入策略覆盖程度 |
| 通过客群坏样本率 | 衡量审批策略下的客群质量 |
其中 PSI 是常见的模型稳定性指标。
其定义为:
[
PSI=\sum_i
(P_i-Q_i)\ln\left(\frac{P_i}{Q_i}\right)
]
这里的 (P_i)、(Q_i) 分别表示基准样本与比较样本在第 (i) 个分组中的比例。
PSI 越大,通常意味着两个样本的分布差异越明显。
但 PSI 并不直接衡量预测准确性。某个变量的 PSI 较高,可能来自正常的营销活动、申请渠道变化或季节性因素,也可能提示模型适用性发生变化。
因此,需要结合业务变化与模型性能共同判断。
3. 评分卡为什么需要定期更新?
假设一个信用卡 A 卡在 2024 年开发,并且当时的申请客群具有以下特点:
- 平均收入相对稳定。
- 历史负债水平较低。
- 信用查询次数较少。
- 获批客户的坏样本率处于较低水平。
到了 2026 年,经济环境、信用卡申请渠道和客群结构可能已经发生变化。
即使模型结构没有改变,风险评分与实际坏样本率之间的关系也可能发生漂移。
因此,银行需要定期检查:
- 新申请客群的风险特征是否变化。
- 评分分布是否发生显著偏移。
- 不同评分段的实际坏样本率是否稳定。
- 模型是否仍然能够有效区分风险。
- 预测 PD 与实际违约频率是否存在系统性偏差。
发现明显变化后,可以考虑重新校准概率、调整审批阈值,或者重新开发模型。
具体选择取决于模型变化的原因。
如果仅仅是整体坏样本率发生变化,模型排序能力依然稳定,概率再校准可能已经足够。
如果客户风险特征与违约之间的关系也发生明显变化,就需要重新评估模型结构与变量。
七、传统 WOE 评分卡与 XGBoost A 卡怎么选?
随着机器学习在银行业普及,A 卡已经不局限于 WOE + Logistic。
很多机构也会使用 XGBoost、LightGBM 等方法开发申请评分模型。
| 维度 | WOE + Logistic | XGBoost / LightGBM |
|---|---|---|
| 特征处理 | 通常需要分箱和 WOE 转换 | 可以直接处理大量数值特征 |
| 非线性关系 | 通过分箱、变换和交互项处理 | 可以自动学习复杂非线性关系 |
| 模型结构 | 简单 | 较复杂 |
| 变量解释 | 系数及分箱分数较直观 | 通常需要 SHAP 等解释工具 |
| 评分卡转换 | 容易形成标准分数表 | 需要额外设计分数映射 |
| 模型调参 | 相对简单 | 通常需要更多实验 |
| 适用场景 | 强调透明性、稳定性和可复核性 | 特征丰富、复杂关系明显的场景 |
实际开发中,可以采用一种比较稳妥的方式。
首先使用 WOE + Logistic 构建传统 A 卡基线,形成完整的风险分层、分数表和解释体系。
随后使用相同的训练集、验证集与测试集训练 XGBoost 或 LightGBM。
重点比较 AUC、KS、概率校准、跨时间表现,以及各审批阈值下的风险收益。
如果复杂模型的性能提升有限,传统评分卡可能仍然是更合适的选择。
如果复杂模型能够稳定改善风险识别,也可以研究将其作为主模型、挑战者模型,或者与传统评分卡进行组合。
八、总结
信用卡 A 卡的本质,是在客户申请授信时,将有限的征信信息、申请资料与历史风险经验转化为可量化的信用风险评分。
本文两个案例展示了两种实践路径。
第一个案例使用德国信用风险数据,完整演示了 WOE 分箱、Logistic Regression 和标准评分转换。这是理解传统银行信用评分卡的基础。
第二个案例使用 Home Credit 循环授信申请数据,展示了如何利用机器学习模型预测申请风险,并进一步模拟审批通过率与坏样本率之间的关系。
但真实 A 卡建模的困难,往往还包括数据选择偏差、拒绝推断、跨时间表现、概率校准和审批策略优化。
因此,一张成熟的 A 卡需要同时回答三个问题:
- 风险识别: 能否有效识别未来可能违约的申请人?
- 风险量化: 能否将不同客户的信用风险转换为稳定、可靠的分数和违约概率?
- 业务决策: 能否在风险可控的条件下,帮助银行确定合理的申请准入与授信策略?
最终,A 卡的价值不仅体现为 AUC 或 KS 提高了多少,也体现为银行能否在相同风险约束下批准更多合适的客户,并在长期经营中获得更好的风险调整后收益。
理解这一点,就理解了银行信用评分卡为什么能够从传统的统计建模方法,发展为今天连接机器学习、风险策略与授信决策的核心工具。
- 点赞
- 收藏
- 关注作者
评论(0)