机器学习3:交叉验证、下采样、过采样(银行贷款案例)
·
一、前言
今天系统学习了逻辑回归在金融风控中的实战应用,核心解决银行贷款预测中最经典的问题:数据集类别不平衡 + 模型评估不准。
在银行贷款数据中:
- 绝大多数用户 正常还款(负样本/多数类)
- 极少数用户 违约坏账(正样本/少数类)
如果直接训练模型,逻辑回归会“摆烂”,直接预测全部为不违约,准确率很高、但是完全没有业务价值。
因此今天重点掌握了三个核心工具:
- 下采样:对多数样本做减法
- 过采样(SMOTE):对少数样本做加法
- 交叉验证:稳定评估模型、选择最优参数
二、业务场景:为什么贷款数据必须做采样?
1. 类别不平衡问题
银行贷款是极度不平衡二分类任务。
逻辑回归的损失函数是全局平均损失,样本数量多的类别会主导训练过程,导致模型:
- 严重偏向多数类(预测不违约)
- 违约样本识别率极低(召回率差)
- 看似准确率很高,实际风控完全失效
2. 普通划分数据集的缺陷
单纯 train_test_split 一次划分,结果随机性大,无法代表模型真实水平,所以必须引入K 折交叉验证。
三、三大核心技术原理
1. 交叉验证(K 折 CV)
核心作用
- 避免单次数据集划分的偶然性
- 稳定评估模型泛化能力
- 自动筛选逻辑回归最优正则参数 C
- 有效检测过拟合
2. 下采样(欠采样)—— 减少多数类样本
原理:随机抽取部分正常用户样本,让正负样本比例 1:1。
优点:训练速度快、彻底平衡样本、解决模型偏置。
缺点:丢失大量正常客户信息,容易欠拟合。
适用:大数据量场景。
3. 过采样(SMOTE)—— 扩增少数类样本
原理:不是简单复制违约样本,而是根据近邻特征合成全新违约样本。
优点:不丢失任何原始数据、缓解过拟合、提升少数类识别能力。
缺点:极少数样本时容易生成虚假数据。
适用:金融风控主流首选方案。
四、注意
严禁全局采样!!!
正确流程:
- 先划分训练集、测试集
- 只对训练集做下采样/过采样
- 测试集保持原始真实分布
原因:测试集必须模拟真实业务场景,不能人为平衡,否则评估结果虚高、上线翻车。
五、完整代码(下采样 + 交叉验证)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix, classification_report
绘制混淆矩阵
def cm_plot(y, yp):
cm = confusion_matrix(y, yp)
plt.matshow(cm, cmap=plt.cm.Blues)
plt.colorbar()
for x in range(len(cm)):
for y in range(len(cm)):
plt.annotate(cm[x, y], xy=(y, x), ha='center', va='center')
plt.ylabel('True label')
plt.xlabel('Predicted label')
return plt
1. 读取数据与预处理
data = pd.read_csv(r"./creditcard.csv")
scaler = StandardScaler()
data['Amount'] = scaler.fit_transform(data[['Amount']])
data = data.drop(['Time'], axis=1)
2. 划分训练集、测试集
X = data.drop('Class', axis=1)
y = data['Class']
x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
3. 下采样平衡训练集
train_data = x_train.copy()
train_data['Class'] = y_train
major = train_data[train_data['Class'] == 0]
minor = train_data[train_data['Class'] == 1]
major_down = major.sample(len(minor), random_state=0)
train_bal = pd.concat([major_down, minor])
x_train_bal = train_bal.drop('Class', axis=1)
y_train_bal = train_bal['Class']
4. 交叉验证选最优 C
c_list = [0.01, 0.1, 1, 10, 100]
scores = []
for c in c_list:
lr = LogisticRegression(C=c, max_iter=1000)
res = cross_val_score(lr, x_train_bal, y_train_bal, cv=8, scoring='recall')
scores.append(res.mean())
print(f"C={c}, 召回率均值: {res.mean():.4f}")
best_c = c_list[np.argmax(scores)]
print("最优惩罚因子 C:", best_c)
5. 训练模型
lr_final = LogisticRegression(C=best_c, max_iter=1000)
lr_final.fit(x_train_bal, y_train_bal)
评估
train_pred = lr_final.predict(x_train_bal)
print("=====训练集报告=====")
print(classification_report(y_train_bal, train_pred))
cm_plot(y_train_bal, train_pred).show()
test_pred = lr_final.predict(x_test)
print("=====测试集报告=====")
print(classification_report(y_test, test_pred, digits=6))
cm_plot(y_test, test_pred).show()
六、完整代码(过采样 SMOTE + 交叉验证)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix, classification_report
from imblearn.over_sampling import SMOTE
def cm_plot(y, yp):
cm = confusion_matrix(y, yp)
plt.matshow(cm, cmap=plt.cm.Blues)
plt.colorbar()
for x in range(len(cm)):
for y in range(len(cm)):
plt.annotate(cm[x, y], xy=(y, x), ha='center', va='center')
plt.ylabel('True label')
plt.xlabel('Predicted label')
return plt
数据预处理
data = pd.read_csv(r"./creditcard.csv")
scaler = StandardScaler()
data['Amount'] = scaler.fit_transform(data[['Amount']])
data = data.drop(['Time'], axis=1)
X = data.drop('Class', axis=1)
y = data['Class']
x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
SMOTE 过采样
smote = SMOTE(random_state=0)
os_x_train, os_y_train = smote.fit_resample(x_train, y_train)
交叉验证调参
c_list = [0.01, 0.1, 1, 10, 100]
scores = []
for c in c_list:
lr = LogisticRegression(C=c, max_iter=1000)
res = cross_val_score(lr, os_x_train, os_y_train, cv=8, scoring='recall')
scores.append(res.mean())
print(f"C={c}, 召回率均值: {res.mean():.4f}")
best_c = c_list[np.argmax(scores)]
print("最优惩罚因子 C:", best_c)
训练评估
lr_final = LogisticRegression(C=best_c, max_iter=1000)
lr_final.fit(os_x_train, os_y_train)
train_pred = lr_final.predict(os_x_train)
print("=====过采样训练集报告=====")
print(classification_report(os_y_train, train_pred))
cm_plot(os_y_train, train_pred).show()
test_pred = lr_final.predict(x_test)
print("=====原始测试集报告=====")
print(classification_report(y_test, test_pred, digits=6))
cm_plot(y_test, test_pred).show()
七、下采样 VS 过采样 对比
| 方法 | 核心思路 | 优点 | 缺点 | 贷款场景推荐 |
|---|---|---|---|---|
| 下采样 | 删减多数类 | 训练快、分布均衡 | 丢失样本信息、易欠拟合 | 大数据量可用 |
| SMOTE 过采样 | 合成少数类 | 不丢数据、效果更好 | 少量样本易造伪数据 | 金融场景首选 |
| 交叉验证 | 多折训练取平均 | 评估稳定、精准调参 | 计算量增大 | 必用 |
八、今日学习总结
- 银行贷款预测是典型的不平衡二分类问题,直接建模只会追求高准确率,毫无业务意义。
- 下采样通过删减多数样本平衡数据,过采样 SMOTE通过合成少数样本优化模型。
- 交叉验证解决模型评估不稳定、参数选择盲目问题,是建模的标准流程。
- 工业级最重要准则:先划分数据集、再训练集采样、测试集绝对保真,杜绝数据泄露。
更多推荐

所有评论(0)