【机器学习】信用卡欺诈检测实战:逻辑回归 + 下采样

文章目录


前言

信用卡欺诈是典型的类别极不平衡的分类问题:正常交易(负样本)远多于欺诈交易(正样本)。如果直接用原始数据训练模型,模型会倾向于把所有样本都预测为"正常",导致召回率极低。本文使用下采样(Undersampling)平衡正负样本,并用逻辑回归建模,最后通过调整分类阈值提升欺诈交易的召回率。


一、运行环境

需要安装以下 Python 库:

c 复制代码
pip install pandas matplotlib numpy scikit-learn

pandas:用于读取、处理表格数据

matplotlib :本文未绘图,但保留通用导入格式

numpy :用于数组、数学计算

pylab:导入绘图中文支持库

数据集:creditcard.csv(可从 Kaggle 获取,包含时间、金额、V1~V28 PCA 特征以及类别标签 Class)

二、完整代码

c 复制代码
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split,cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn import metrics
from sklearn.preprocessing import StandardScaler

data = pd.read_csv(r'./creditcard.csv')

scaler = StandardScaler()
a = data[['Amount']]
data['Amount'] = scaler.fit_transform(data[['Amount']])

data = data.drop(['Time'],axis=1)

X_whole = data.drop('Class',axis=1)
y_whole = data.Class
x_train_w ,x_text_w , y_train_w , y_text_w = \
    train_test_split(X_whole,y_whole,test_size=0.2,random_state=0)

x_train_w['Class'] = y_train_w
data_train = x_train_w

# 进行下采样
pg = data_train[data_train['Class'] == 0]
ng = data_train[data_train['Class'] == 1]
pg = pg.sample(len(ng))

data_c = pd.concat([ pg, ng])

X = data_c.drop('Class',axis=1)
y = data_c.Class

scores = []
c_param_range = [0.01,0.1,1,10,100]
for i in c_param_range:
    lr = LogisticRegression(C = i,solver='lbfgs',max_iter=1000)
    score = cross_val_score(lr, X, y, cv=5,scoring='recall')
    score_mean = sum(score)/len(score)
    scores.append(score_mean)
    print(score_mean)

best_c = c_param_range[np.argmax(scores)]

lr = LogisticRegression(C= best_c,max_iter=1000)
lr.fit(X,y)

train_predicted = lr.predict(X)
print(metrics.classification_report(y,train_predicted))

test_predicted_big = lr.predict(x_text_w)
print(metrics.classification_report(y_text_w,test_predicted_big))

thresholds = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9]
recalls = []
for i in thresholds:
    y_predict_proba = lr.predict_proba(x_text_w)
    y_predict_proba = pd.DataFrame(y_predict_proba)
    y_predict_proba = y_predict_proba.drop([0],axis=1)
    y_predict_proba[y_predict_proba[[1]] > i] = 1
    y_predict_proba[y_predict_proba[[1]] <= i] = 0
    recall = metrics.recall_score(y_text_w,y_predict_proba[1])
    recalls.append(recall)
    print("{} Recall metric in the testing dataset:{:.3f}".format(i,recall))

三、代码逐行解释

  1. 导入所需库
c 复制代码
import pandas as pd               # 数据处理
import numpy as np                # 数组计算
from sklearn.model_selection import train_test_split, cross_val_score  # 划分数据集 + 交叉验证
from sklearn.linear_model import LogisticRegression   # 逻辑回归模型
from sklearn import metrics       # 模型评估指标
from sklearn.preprocessing import StandardScaler      # 标准化
  1. 加载数据
c 复制代码
data = pd.read_csv(r'./creditcard.csv')   # 读取 CSV 文件到 DataFrame
  1. 特征工程:标准化 Amount 列
c 复制代码
scaler = StandardScaler()                     # 创建标准化器对象
a = data[['Amount']]                          # 取出 Amount 列(注意双括号保持二维形状)
data['Amount'] = scaler.fit_transform(data[['Amount']])   # 拟合 + 转换,将 Amount 变为均值为0、方差为1的分布

为什么标准化?

因为Amount 和其他特征(V1~V28)尺度差异大,影响梯度下降收敛速度和正则化效果。

  1. 删除不需要的特征
c 复制代码
data = data.drop(['Time'], axis=1)   

删除 Time 列(axis=1 表示列),因为时间与欺诈相关性弱且噪声大

  1. 划分特征矩阵 X 和标签 y
c 复制代码
X_whole = data.drop('Class', axis=1)   # 删除 Class 列,剩下的都是特征
y_whole = data.Class                   # Class 列为标签(1-欺诈,0-正常)
  1. 划分训练集和测试集(原始不平衡数据)
c 复制代码
x_train_w, x_text_w, y_train_w, y_text_w = train_test_split(
    X_whole, y_whole, test_size=0.2, random_state=0
)
# 训练集占80%,测试集占20%;random_state 保证每次运行划分相同
  1. 构造带标签的训练集,用于下采样
c 复制代码
x_train_w['Class'] = y_train_w   # 将标签列添加到训练特征中,方便按类别筛选
data_train = x_train_w           # 重命名为 data_train
  1. 下采样(Undersampling)------平衡正负样本
c 复制代码
pg = data_train[data_train['Class'] == 0]   # 筛选出正常交易(多数类)
ng = data_train[data_train['Class'] == 1]   # 筛选出欺诈交易(少数类)
pg = pg.sample(len(ng))                     # 从正常交易中随机抽取与欺诈交易相同数量的样本
data_c = pd.concat([pg, ng])                # 将抽取的正常样本和全部欺诈样本合并成平衡数据集

下采样原理:让正负样本数量相等,避免模型偏向多数类。

  1. 提取下采样后的特征和标签
c 复制代码
X = data_c.drop('Class', axis=1)   # 平衡数据集的特征
y = data_c.Class                   # 平衡数据集的标签
  1. 交叉验证选择最佳正则化参数 C
c 复制代码
scores = []                         # 存储每个 C 对应的平均召回率
c_param_range = [0.01, 0.1, 1, 10, 100]   # 待测试的 C 值
for i in c_param_range:
    lr = LogisticRegression(C=i, solver='lbfgs', max_iter=1000)   # 创建逻辑回归模型,C 为正则化强度的倒数
    score = cross_val_score(lr, X, y, cv=5, scoring='recall')     # 5折交叉验证,评估指标为召回率
    score_mean = sum(score) / len(score)                          # 计算平均召回率
    scores.append(score_mean)
    print(score_mean)

C 越大,正则化越弱,模型可能过拟合;C 越小,正则化越强,可能欠拟合。

scoring='recall' 因为欺诈检测更关注"抓住多少真正的欺诈"(召回率)。

  1. 选出最佳 C 值并用全部平衡数据训练最终模型
c 复制代码
best_c = c_param_range[np.argmax(scores)]   # 找到平均召回率最高的 C
lr = LogisticRegression(C=best_c, max_iter=1000)   # 创建最终模型
lr.fit(X, y)                                # 用全部平衡训练集拟合模型
  1. 在训练集(平衡数据)上评估模型
c 复制代码
train_predicted = lr.predict(X)                                # 预测训练集
print(metrics.classification_report(y, train_predicted))       # 输出精确率、召回率、F1 等
  1. 在原始测试集(不平衡数据)上评估模型
c 复制代码
test_predicted_big = lr.predict(x_text_w)                      # 预测原始测试集
print(metrics.classification_report(y_text_w, test_predicted_big))

原始测试集仍然是不平衡的,模型直接预测时可能召回率偏低。

  1. 调整分类阈值提升召回率
c 复制代码
thresholds = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]   # 尝试不同的概率阈值
recalls = []
for i in thresholds:
    y_predict_proba = lr.predict_proba(x_text_w)               # 获取每个样本属于类别0和1的概率
    y_predict_proba = pd.DataFrame(y_predict_proba)            # 转为 DataFrame
    y_predict_proba = y_predict_proba.drop([0], axis=1)        # 只保留类别1(欺诈)的概率列
    # 根据阈值 i 将概率转换为类别标签
    y_predict_proba[y_predict_proba[[1]] > i] = 1
    y_predict_proba[y_predict_proba[[1]] <= i] = 0
    recall = metrics.recall_score(y_text_w, y_predict_proba[1])   # 计算召回率
    recalls.append(recall)
    print("{} Recall metric in the testing dataset:{:.3f}".format(i, recall))

为什么调阈值?

默认阈值 0.5 时,模型只有非常确信时才判断为欺诈。降低阈值(如 0.3)会让更多样本被判为正类,从而提高召回率(但可能误判正常交易)。

四、知识点解读

1.为什么要做下采样?

信用卡欺诈数据是典型的不平衡数据:

正常交易:99.8%

欺诈交易:0.2%

模型会直接全部预测为正常交易,准确率极高,但完全无法检测欺诈。

下采样:减少多数类(正常交易)数量,让两类样本数量相等,让模型学会识别欺诈特征。

  1. 为什么用召回率 (recall) 评估模型?

欺诈检测的核心要求:宁可误判,不可漏判

召回率:所有真实欺诈中,被模型检测出来的比例

召回率越高,漏判的欺诈交易越少


总结

本文通过一个完整的代码实例,展示了信用卡欺诈检测的完整流程:

数据标准化与清洗--->

下采样处理类别不平衡--->

交叉验证选择逻辑回归的正则化参数--->

模型训练与评估--->

后处理调整阈值优化召回率

每一行代码都配有详细注释,非常适合新手理解机器学习项目的工程实现。你可以在此基础上尝试上采样(SMOTE)、集成方法(XGBoost)或其它阈值调优技巧

相关推荐
AskHarries1 小时前
Gateway:OpenClaw 的入口层和调度中心
人工智能
OPMR1 小时前
【初步成功】RTX4090微调Qwen3-TTS模型及相关问题
人工智能·深度学习
yzx9910131 小时前
超能模式全领域解说
人工智能
Black蜡笔小新1 小时前
自动化AI算法训练服务器DLTM零代码私有化部署筑牢企业AI落地根基
人工智能·算法·自动化
带娃的IT创业者1 小时前
深度解析:YouTube 自动标注 AI 生成内容背后的技术博弈与架构演进
大数据·人工智能·架构·youtube·数字水印·技术架构·ai生成内容
好好学仿真1 小时前
机器学习预测聚合物拉伸强度:五种回归算法对比(附Kaggle数据集 + 五折交叉验证)
python·机器学习·xgboost·梯度提升·材料性能预测·随机森林回归
X54先生(人文科技)1 小时前
《元创力》纪实录·卷宗2.1 关联观察孤岛的回归:当一座“反AI叙事飞地”成为最后的堡垒
人工智能·架构·开源·ai写作·零知识证明
小糖学代码1 小时前
机器学习:4.人工神经网络
人工智能·深度学习·神经网络·机器学习
Y学院1 小时前
PyTorch深度学习框架核心概念精讲
人工智能·pytorch·深度学习