在金融风控领域,信用卡信誉检测是一个典型的类别不平衡问题:正常交易样本占绝对多数,而欺诈交易样本极少。我们的核心目标是尽可能多地识别出欺诈交易,这就对模型的召回率(Recall)提出了极高要求。
最近我在做信用卡信誉检测项目时,从模型训练的坑点(欠拟合、过拟合)出发,通过交叉验证和正则化优化,最终有效提升了模型性能,下面就把整个过程分享给大家。
一、问题背景与数据初探
信用卡失信检测数据集(creditcard.csv)包含28个匿名特征、Time(交易时间)、Amount(交易金额)和Class(标签,0为正常交易,1为欺诈交易)。首先,我们观察数据分布:
python
import pandas as pd
import matplotlib.pyplot as plt
from pylab import mpl
# 解决中文显示问题
mpl.rcParams['font.sans-serif'] = ['Microsoft YaHei']
mpl.rcParams['axes.unicode_minus'] = False
data = pd.read_csv("creditcard.csv")
labels_count = data['Class'].value_counts()
print(labels_count)
plt.title("正负例样本数")
plt.xlabel("类别")
plt.ylabel("频率")
labels_count.plot(kind='bar')
plt.show()

运行后可以发现,正常交易(0)的数量远多于欺诈交易(1),这是典型的类别不平衡场景。在这种场景下,单纯追求准确率毫无意义------即使模型把所有样本都预测为0,准确率也会极高,但完全无法识别欺诈交易,因此召回率(Recall)才是我们的核心指标。
二、初始模型:从欠拟合到过拟合的困境
首先,我们对数据进行预处理:
• 对Amount特征进行标准化
• 移除无关特征Time
• 划分训练集和测试集(测试集占比30%)然后训练一个基础的逻辑回归模型:
python
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn import metrics
from sklearn.preprocessing import StandardScaler
# 对金额进行标准化
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.3, random_state=1000)
# 初始化并训练模型
lr = LogisticRegression(C=0.01)
lr.fit(x_train, y_train)
# 模型预测与评估
test_predicted = lr.predict(x_test)
print(metrics.classification_report(y_test, test_predicted))

输出的分类报告显示,模型对欺诈样本(1)的召回率非常低,这意味着大量欺诈交易被漏判,完全无法满足风控需求。
问题分析
-
欠拟合:初始模型正则化强度过高(C=0.01),模型过于简单,无法捕捉数据中的规律,导致在训练集和测试集上表现都差。
-
过拟合风险:如果我们过度降低正则化强度,模型又会在训练集上表现极好,但在测试集上泛化能力不足,学到了训练数据中的噪声。
-
超参数未优化:初始模型使用了默认的正则化强度,可能没有适配数据特性。
-
类别不平衡:正常样本过多,模型倾向于预测为多数类,导致少数类召回率低。
三、核心优化:交叉验证+正则化提升召回率
为了解决上述问题,我们引入K折交叉验证来选择最优的正则化惩罚因子,同时使用L2正则化来防止过拟合。
1. 交叉验证选择最优惩罚因子
python
from sklearn.model_selection import cross_val_score
import numpy as np
# 定义候选的惩罚因子C
C_range = [0.01, 0.1, 1, 10, 100]
scores = []
# 遍历每个C值,用8折交叉验证计算平均召回率
for i in C_range:
lr = LogisticRegression(C=i, penalty='l2', solver='lbfgs', max_iter=1000)
score = cross_val_score(lr, x_train, y_train, cv=8, scoring='recall')
score_mean = np.mean(score)
scores.append(score_mean)
print(f"C={i} 时,平均召回率:{score_mean}")
# 选择召回率最高的C值
best_C = C_range[np.argmax(scores)]
print(f"最优惩罚因子为:{best_C}")

通过交叉验证,我们可以找到在训练集上表现最稳定、召回率最高的正则化强度,避免了单次划分数据带来的偶然性。
2. 用最优参数重建模型
python
# 使用最优C值训练最终模型
lr = LogisticRegression(C=best_C, penalty='l2', solver='lbfgs', max_iter=1000)
lr.fit(x_train, y_train)
# 在训练集上评估
train_predicted = lr.predict(x_train)
print("训练集评估报告:")
print(metrics.classification_report(y_train, train_predicted))
# 在测试集上评估
test_predicted = lr.predict(x_test)
print("测试集评估报告:")
print(metrics.classification_report(y_test, test_predicted, digits=6))

同时,我们还可以绘制混淆矩阵来直观地查看模型的预测效果:
python
def cm_plot(y, yp):
from sklearn.metrics import confusion_matrix
import matplotlib.pyplot as plt
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), horizontalalignment='center', verticalalignment='center')
plt.ylabel('True label')
plt.xlabel('Predicted label')
return plt
cm_plot(y_test, test_predicted).show()
四、核心知识点:欠拟合、过拟合与正则化
1. 欠拟合与过拟合
• 欠拟合:模型过于简单,在训练集和测试集上表现都差,无法捕捉数据中的规律。
• 过拟合:模型在训练集上表现极好,但在测试集上泛化能力差,学到了训练数据中的噪声和偶然特征。
2. 正则化惩罚
正则化的目的是防止过拟合,通过在损失函数中加入惩罚项,约束模型参数的大小,避免模型过度复杂。
• 原始损失函数(以线性回归为例):
• 加入L2正则化后的损失函数:
其中,就是正则化惩罚项,它会惩罚模型参数的过大值,从而防止过拟合。
3. 交叉验证
交叉验证是一种评估模型性能的方法,它将数据划分为多个子集,轮流用不同子集训练和验证,提升模型评估的可靠性。其中,K折交叉验证是最常用的方法,它将数据划分为K个折叠,每次用K-1个折叠作为训练集,1个折叠作为验证集,最终取K次验证的平均结果作为模型性能的评估。
五、优化效果与关键结论
通过交叉验证和正则化优化,我们得到了显著的效果提升:
-
召回率显著提升:欺诈样本的召回率从初始的极低水平提升到了可接受的范围,有效减少了欺诈漏判。
-
模型稳定性增强:交叉验证避免了单次数据划分的偶然性,让模型在不同数据子集上的表现更稳定。
-
过拟合得到抑制:L2正则化惩罚了模型参数的过大值,提升了模型在测试集上的泛化能力。
六、总结与展望
在信用卡欺诈检测这类类别不平衡的任务中,召回率是比准确率更重要的指标。通过交叉验证选择最优正则化参数,我们可以有效提升模型对少数类的识别能力,同时保证模型的泛化能力。
未来,我们还可以尝试以下方法进一步优化:
• 使用SMOTE等过采样技术处理类别不平衡
• 尝试集成学习方法(如随机森林、XGBoost)
• 调整决策阈值,在精确率和召回率之间做更灵活的权衡
如果你也在处理类似的风控或异常检测问题,不妨试试这些方法,相信会有不错的效果。
