4. 简单拒绝推断方法:随机抽样法、拒绝样本直接赋值法、拒绝样本加权法
好,咱们进入正题。拒绝推断这个事儿,说白了就是跟「未知」较劲。你想想看,银行只给通过的人放贷,那被拒的那些人,到底会不会违约?没人知道。但模型又必须学这个信息,怎么办?
我刚开始做评分卡那会儿,也天真地以为「拒绝样本扔了就行」。后来被老板骂了一顿才明白——拒绝样本里藏着巨大的选择偏差。你不处理它,模型上线后一跑,坏账率直接翻倍。嗯,这都是血泪教训。
今天咱们先聊三种最简单的拒绝推断方法。它们不复杂,但很实用。我个人习惯在项目初期先用它们探探路。
4.1 随机抽样法
这个方法最直白。你从拒绝样本里随机抽一部分,直接扔进训练集。然后给它们打上标签——通常是「好人」或者「未知」。具体怎么打,看你的业务场景。
我见过不少团队这么干。为什么?因为快。你想想看,一个模型迭代周期就两周,哪有时间搞复杂的半监督学习?随机抽样,简单粗暴,先跑起来再说。
但这里有个坑。我曾经在一个现金贷项目里试过随机抽样,结果模型上线后,通过率下降了5个点。为什么?因为拒绝样本里其实有很多「硬拒绝」——那些征信黑名单的人。你把他们抽进来当好人,模型就学歪了。
具体操作步骤很简单:
- 统计拒绝样本总量,比如有10万条
- 设定抽样比例,比如10%
- 随机抽取1万条
- 给这1万条打上「好人」标签(或者保留为空,看算法)
- 合并到训练集里重新训练
代码实现也不复杂:
import pandas as pd
import numpy as np
# 假设 reject_df 是拒绝样本
sample_rate = 0.1
sample_size = int(len(reject_df) * sample_rate)
# 随机抽样
sampled_reject = reject_df.sample(n=sample_size, random_state=42)
# 打标签:假设0是好人
sampled_reject['label'] = 0
# 合并到训练集
train_augmented = pd.concat([train_df, sampled_reject], ignore_index=True)
4.2 拒绝样本直接赋值法
这个方法比随机抽样「聪明」一点点。它不随机抽,而是给每个拒绝样本直接赋一个标签。怎么赋?用你已有的评分卡去打分。
具体来说:
- 先用通过样本训练一个初始模型
- 用这个模型去预测拒绝样本的违约概率
- 设定一个阈值,比如概率大于0.7的,标记为「坏人」
- 概率小于0.3的,标记为「好人」
- 中间的,扔掉或者保留为不确定
我当年在一个信用卡项目里用过这招。当时拒绝样本有20万条,直接赋值后,模型AUC提升了0.03。效果还不错。
但这里有个明显的悖论——你用一个有偏的模型去预测有偏的样本,结果能准吗?说白了,这就是「用错误去纠正错误」。所以这个方法只适合做快速验证,不能作为最终方案。
代码示例:
from sklearn.ensemble import RandomForestClassifier
# 训练初始模型
model = RandomForestClassifier()
model.fit(X_train, y_train)
# 预测拒绝样本
reject_proba = model.predict_proba(X_reject)[:, 1]
# 直接赋值
reject_label = np.where(reject_proba > 0.7, 1,
np.where(reject_proba < 0.3, 0, np.nan))
# 只保留有明确标签的样本
valid_idx = ~np.isnan(reject_label)
X_reject_valid = X_reject[valid_idx]
y_reject_valid = reject_label[valid_idx]
4.3 拒绝样本加权法
这个方法我最常用。它不改变标签,而是给样本加权重。核心思想是:拒绝样本的信息量不如通过样本大,所以给它更低的权重。
怎么算权重?常见做法有两种:
| 方法 | 权重计算 | 适用场景 |
|---|---|---|
| 逆概率加权 | 权重 = 1 / 通过概率 | 拒绝机制已知 |
| 启发式加权 | 权重 = 0.3 ~ 0.5 | 拒绝机制未知 |
我个人习惯用启发式加权。为什么?因为大多数银行的拒绝机制都是黑箱——你根本不知道审批经理到底看了什么。逆概率加权虽然理论漂亮,但实际用起来一堆假设,很容易翻车。
具体操作:
- 通过样本权重设为1.0
- 拒绝样本权重设为0.3 ~ 0.5
- 训练时,模型会更关注通过样本,但也不会完全忽略拒绝样本
代码实现:
# 构建权重数组
weights = np.ones(len(X_train))
reject_weight = 0.3 # 拒绝样本权重
# 假设 reject_idx 是拒绝样本在训练集中的索引
weights[reject_idx] = reject_weight
# 训练时传入权重
model.fit(X_train, y_train, sample_weight=weights)
4.4 三种方法对比
咱们用一张表总结一下:
| 方法 | 优点 | 缺点 | 我推荐的使用场景 |
|---|---|---|---|
| 随机抽样法 | 简单、快、容易实现 | 可能引入噪声,硬拒绝场景失效 | 项目初期快速验证 |
| 直接赋值法 | 有依据,比随机抽样更合理 | 存在循环论证,模型偏差会放大 | 拒绝样本量大的时候做初步筛选 |
| 加权法 | 不改变标签,灵活可控 | 权重需要手动调,没有理论最优解 | 大多数实际项目,我最常用 |
4.5 核心逻辑流程图
下面这张图展示了三种方法的整体流程。你可以看到,它们都是从「拒绝样本池」出发,但处理方式完全不同。
你看,三种方法最终都指向同一个目标——更新评分卡模型。但路径不同,代价也不同。随机抽样法最快但最粗糙,直接赋值法有依据但容易过拟合,加权法最灵活但需要调参。
好了,这三种简单方法就聊到这儿。它们虽然「简单」,但绝不是「简陋」。在实际项目中,我见过不少团队用加权法就搞定了80%的问题。别小看它们。