Kaggle信用卡欺诈检测实战:5种采样方法对比与XGBoost调参全流程

Kaggle信用卡欺诈检测实战:5种采样方法对比与XGBoost调参全流程

金融风控领域有个经典的“大海捞针”难题:如何在每天数百万笔看似正常的信用卡交易中,精准识别出那0.17%的欺诈行为?这不仅仅是技术挑战,更是商业与安全的平衡艺术。我处理过不少类似的项目,最深的体会是,数据不平衡问题处理得好坏,直接决定了整个模型的生死。很多工程师拿到Kaggle上那个著名的信用卡欺诈数据集,第一反应是直接套用XGBoost或者随机森林,结果模型准确率高达99.9%,却对欺诈交易一个都抓不到——这种“虚假繁荣”在实际业务中毫无价值。

真正有价值的方案,必须直面数据不平衡这个核心痛点。今天我们就以Kaggle信用卡欺诈检测项目为战场,深入对比5种主流采样策略的实际效果,并结合XGBoost的scale_pos_weight参数调优,构建一套可落地的技术方案。无论你是金融科技公司的算法工程师,还是正在准备数据科学面试的求职者,这篇文章都能给你带来实实在在的实操经验。

1. 数据不平衡的本质与业务影响

在深入技术细节之前,我们必须先理解为什么信用卡欺诈检测如此特殊。Kaggle提供的这个数据集包含284,807笔交易,其中欺诈交易仅492笔,占比0.172%。这种极端不平衡不是偶然现象,而是金融风控领域的常态——如果欺诈交易占比过高,整个支付系统早就崩溃了。

1.1 不平衡数据的三大陷阱

我在实际项目中踩过不少坑,总结下来主要有三个陷阱:

陷阱一:准确率幻觉

# 一个极端的例子
from sklearn.metrics import accuracy_score
import numpy as np

# 假设我们有一个“愚蠢”的模型,永远预测为0(非欺诈)
y_true = np.array([0]*1000 + [1]*2)  # 1000个正常,2个欺诈
y_pred = np.array([0]*1002)  # 全部预测为正常

accuracy = accuracy_score(y_true, y_pred)
print(f"准确率: {accuracy:.4f}")  # 输出: 0.9980

这个模型准确率高达99.8%,但在业务上完全失败——它漏掉了所有欺诈交易。这就是为什么在金融风控中,召回率(Recall)通常比准确率更重要

陷阱二:模型偏向多数类 大多数机器学习算法默认假设类别分布均衡,优化目标是最小化整体错误率。在极端不平衡的情况下,模型会“偷懒”地偏向多数类,因为这样就能获得很高的准确率。

陷阱三:评估指标选择不当 不同的评估指标关注不同的方面:

评估指标 计算公式 业务意义 适用场景
准确率 (TP+TN)/(TP+TN+FP+FN) 整体预测正确率 类别均衡的场景
召回率 TP/(TP+FN) 识别欺诈交易的能力 欺诈检测优先
精确率 TP/(TP+FP) 预测为欺诈的可信度 误报成本高的场景
F1-Score 2×(精确率×召回率)/(精确率+召回率) 平衡精确率和召回率 需要综合评估
AUC-ROC ROC曲线下面积 分类器整体性能 类别不平衡时更稳定

关键提示:在信用卡欺诈检测中,业务方通常更关心“有多少欺诈交易被成功拦截”(高召回率),而不是“我们预测为欺诈的交易中有多少是真的”(高精确率)。因为漏掉一个欺诈交易的实际损失,远高于误拦一个正常交易带来的客户体验下降。

1.2 数据探索与可视化

让我们先看看数据的真实面貌。Kaggle数据集已经对原始特征进行了PCA处理,得到了V1-V28这28个主成分,保留了时间和金额两个原始特征。

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import StandardScaler
import warnings
warnings.filterwarnings('ignore')

# 加载数据
df = pd.read_csv('creditcard.csv')

# 查看类别分布
print("交易类别分布:")
print(df['Class'].value_counts())
print(f"\n欺诈交易占比: {df['Class'].mean()*100:.3f}%")

# 可视化
fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# 类别分布柱状图
df['Class'].value_counts().plot(kind='bar', ax=axes[0], color=['skyblue', 'coral'])
axes[0].set_title('交易类别分布 (0: 正常, 1: 欺诈)')
axes[0].set_xlabel('类别')
axes[0].set_ylabel('数量')
axes[0].set_xticklabels(['正常', '欺诈'], rotation=0)

# 欺诈交易金额分布
fraud_amounts = df[df['Class'] == 1]['Amount']
normal_amounts = df[df['Class'] == 0]['Amount'].sample(n=1000)  # 抽样显示

axes[1].hist([normal_amounts, fraud_amounts], 
             bins=50, 
             label=['正常交易', '欺诈交易'],
             alpha=0.7,
             color=['skyblue', 'coral'])
axes[1].set_title('交易金额分布对比')
axes[1].set_xlabel('交易金额')
axes[1].set_ylabel('频次')
axes[1].legend()
axes[1].set_xlim([0, 1000])  # 聚焦小额交易

plt.tight_layout()
plt.show()

运行这段代码,你会看到两个关键现象:

  1. 类别分布极度不平衡,欺诈交易仅占0.172%
  2. 欺诈交易的金额普遍较小,这符合现实——欺诈者通常从小额测试开始

2. 5种采样策略深度对比

处理不平衡数据,采样是最直接有效的方法。但不同采样策略各有优劣,需要根据具体场景选择。下面我们详细对比5种主流方法。

2.1 随机欠采样(Random Under-Sampling)

原理:从多数类中随机删除样本,使两类样本数量相等。

from imblearn.under_sampling import RandomUnderSampler
import numpy as np

# 准备数据
X = df.drop('Class', axis=1)
y = df['Class']

# 应用随机欠采样
rus = RandomUnderSampler(sampling_strategy=1.0, random_state=42)
X_resampled, y_resampled = rus.fit_resample(X, y)

print(f"原始数据形状: {X.shape}")
print(f"欠采样后形状: {X_resampled.shape}")
print(f"欠采样后类别分布:\n{pd.Series(y_resampled).value_counts()}")

优点

  • 计算效率高,训练速度快
  • 避免过拟合风险(相对于过采样)

缺点

  • 丢失大量有价值信息
  • 可能删除重要的多数类样本
  • 在实际测试时性能可能下降

我在一个项目中用过随机欠采样,训练时召回率达到95%,但在真实生产环境测试时,误杀率(将正常交易判为欺诈)高达15%,导致大量客户投诉。根本原因是欠采样后的数据分布与真实分布差异太大。

2.2 随机过采样(Random Over-Sampling)

原理:随机复制少数类样本,增加其数量。

from imblearn.over_sampling import RandomOverSampler

ros = RandomOverSampler(sampling_strategy=0.5, random_state=42)  # 使少数类达到多数类的一半
X_ros, y_ros = ros.fit_resample(X, y)

print(f"过采样后形状: {X_ros.shape}")
print(f"少数类样本数量从 {sum(y==1)} 增加到 {sum(y_ros==1)}")

优点

  • 不丢失任何信息
  • 简单易实现

缺点

  • 容易导致过拟合
  • 没有增加新信息,只是重复现有样本

2.3 SMOTE(Synthetic Minority Over-sampling Technique)

这是目前最流行的过采样方法之一,通过插值生成新的合成样本。

from imblearn.over_sampling import SMOTE

# 注意:SMOTE对高维数据效果更好
smote = SMOTE(
    sampling_strategy=0.5,  # 使少数类达到多数类的一半
    random_state=42,
    k_neighbors=5,  # 默认值,对于小样本可以减小
    n_jobs=-1  # 使用所有CPU核心
)

# 先标准化连续特征
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

X_smote, y_smote = smote.fit_resample(X_scaled, y)

print("SMOTE过采样完成")
print(f"生成 {sum(y_smote==1) - sum(y==1)} 个合成欺诈样本")

SMOTE的工作原理

  1. 对每个少数类样本,找到其k个最近邻(通常是5个)
  2. 随机选择一个最近邻
  3. 在两者连线上随机选择一个点作为新样本

参数调优建议

  • k_neighbors:对于极少数类(如<100),设置为3可能更好
  • sampling_strategy:通常设为0.5-1.0,根据计算资源调整

2.4 ADASYN(Adaptive Synthetic Sampling)

SMOTE的改进版,根据样本密度自适应生成合成样本。

from imblearn.over_sampling import ADASYN

adasyn = ADASYN(
    sampling_strategy=0.5,
    random_state=42,
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值