一、前言

今天系统学习了逻辑回归在金融风控中的实战应用,核心解决银行贷款预测中最经典的问题:数据集类别不平衡 + 模型评估不准。

在银行贷款数据中:

  • 绝大多数用户 正常还款(负样本/多数类)
  • 极少数用户 违约坏账(正样本/少数类)

如果直接训练模型,逻辑回归会“摆烂”,直接预测全部为不违约,准确率很高、但是完全没有业务价值。

因此今天重点掌握了三个核心工具:

  • 下采样:对多数样本做减法
  • 过采样(SMOTE):对少数样本做加法
  • 交叉验证:稳定评估模型、选择最优参数

二、业务场景:为什么贷款数据必须做采样?

1. 类别不平衡问题

银行贷款是极度不平衡二分类任务。

逻辑回归的损失函数是全局平均损失,样本数量多的类别会主导训练过程,导致模型:

  • 严重偏向多数类(预测不违约)
  • 违约样本识别率极低(召回率差)
  • 看似准确率很高,实际风控完全失效

2. 普通划分数据集的缺陷

单纯 train_test_split 一次划分,结果随机性大,无法代表模型真实水平,所以必须引入K 折交叉验证。

三、三大核心技术原理

1. 交叉验证(K 折 CV)

核心作用

  • 避免单次数据集划分的偶然性
  • 稳定评估模型泛化能力
  • 自动筛选逻辑回归最优正则参数 C
  • 有效检测过拟合

2. 下采样(欠采样)—— 减少多数类样本

原理:随机抽取部分正常用户样本,让正负样本比例 1:1。

优点:训练速度快、彻底平衡样本、解决模型偏置。

缺点:丢失大量正常客户信息,容易欠拟合。

适用:大数据量场景。

3. 过采样(SMOTE)—— 扩增少数类样本

原理:不是简单复制违约样本,而是根据近邻特征合成全新违约样本。

优点:不丢失任何原始数据、缓解过拟合、提升少数类识别能力。

缺点:极少数样本时容易生成虚假数据。

适用:金融风控主流首选方案。

四、注意

严禁全局采样!!!

正确流程:

  1. 先划分训练集、测试集
  2. 只对训练集做下采样/过采样
  3. 测试集保持原始真实分布

原因:测试集必须模拟真实业务场景,不能人为平衡,否则评估结果虚高、上线翻车。

五、完整代码(下采样 + 交叉验证)

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 过采样合成少数类不丢数据、效果更好少量样本易造伪数据金融场景首选
交叉验证多折训练取平均评估稳定、精准调参计算量增大必用

八、今日学习总结

  1. 银行贷款预测是典型的不平衡二分类问题,直接建模只会追求高准确率,毫无业务意义。
  2. 下采样通过删减多数样本平衡数据,过采样 SMOTE通过合成少数样本优化模型。
  3. 交叉验证解决模型评估不稳定、参数选择盲目问题,是建模的标准流程。
  4. 工业级最重要准则:先划分数据集、再训练集采样、测试集绝对保真,杜绝数据泄露。

更多推荐