news 2026/9/26 16:39:17

逻辑回归实战:信用卡欺诈检测中的类别不平衡与阈值调优

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
逻辑回归实战:信用卡欺诈检测中的类别不平衡与阈值调优

简介:一份使用逻辑回归算法完成信用卡欺诈检测的机器学习项目,面向人工智能初学者和相关专业的高校学生,尤其适合正在准备课程设计、期末大作业的人群。项目压缩包共收录3个文件:Python源码、CSV信用卡交易数据集和Markdown说明文档,整体大小约63.29MB;其中源码包含数据读取、缺失值处理、特征标准化、模型训练及混淆矩阵评估等完整环节,每行关键代码均有中文注释,数据集提供真实欺诈标签样本,文档则给出环境配置与运行说明。目前已吸引四百零八人学习下载,代码结构清晰、可直接运行,能输出准确率、召回率等核心指标。这套被导师认可的高分项目,既可作为毕业设计或课程设计的现成方案,也可作为学习逻辑回归在金融风控领域实战应用的参考范例。

1. 逻辑回归做信用卡欺诈检测:为什么这个项目值得你拆一遍

做机器学习课程设计的人很多,但真正能在答辩现场把“为什么选逻辑回归”讲清楚的不多。这个信用卡欺诈检测项目最打动我的地方在于,它不是套一个现成模型跑个准确率就完事,而是把数据预处理、类别不平衡处理、模型训练、评估指标这一整条链路都走通了,代码里还带注释,非常适合当做期末大作业或者毕业设计的底子。数据集用的是经典的 creditcard.csv,包含约 28 万个样本,特征是 PCA 降维后的数值型变量,标签列 Class 中欺诈样本占比极低,这正是逻辑回归最容易翻车的场景,也是最能体现你水平的场景。如果你正愁课程设计没方向,或者想看看带完整文档的机器学习项目长什么样,这份资源值得下下来认真过一遍。

2. 欺诈检测的数据底子:先搞懂 creditcard.csv 里藏着什么

2.1 数据字段与匿名化设计的门道

creditcard.csv 是学术界和工业界做异常检测最常用的公开数据集之一。每一行代表一笔信用卡交易,Time 字段是距离第一笔交易经过的秒数,V1 到 V28 是经过 PCA 变换后的特征——原始特征被匿名化处理了,这样做的目的是保护用户隐私,但也意味着你无法从业务角度逐个解释这些特征的含义。最后两列是 Amount(交易金额)和 Class(标签),Class 为 1 表示这笔交易被确认为欺诈,0 表示正常交易。

这个数据集有一个非常关键的特点:极度不平衡。欺诈交易通常只占总样本的 0.17% 左右,也就是说 28 万笔交易里大约只有 492 笔是欺诈。如果你直接拿原始数据去训练一个逻辑回归模型,模型会把所有样本都预测成正常类,因为你只需要把准确率做到 99.8% 以上就“看起来很好”,但这种模型在实际场景里毫无用处。我处理这类数据的第一步永远是先统计类别分布,确认不平衡比例,再决定后续的采样策略。

数据读取和分布探查用 Pandas 就能完成,项目里 Creditcard_fraud_detection.py 脚本也包含这部分逻辑。我自己习惯先跑一个快速统计,确认数据规模、缺失值情况和类别分布,再进入建模环节:

import pandas as pd # 读取数据 df = pd.read_csv('creditcard.csv') # 查看前几行,确认字段结构 print(df.head()) # 检查是否存在缺失值 print(df.isnull().sum().sum()) # 统计类别分布 print(df['Class'].value_counts()) print(f"欺诈样本占比: {df['Class'].mean():.4f}")

这段代码做了三件事:读入 CSV、确认没有空值干扰建模、算出欺诈样本占比。绝大多数情况下这个数据集是干净的,不需要做缺失值填充,重点精力应该放在类别不平衡处理上。如果Class的占比和你预期差很多,先检查是不是数据文件没下完整。

2.2 为什么要对 Amount 和 Time 做标准化

原始数据里 V1 到 V28 已经是标准化后的特征,基本不需要再处理。但 Amount 和 Time 没有经过同样的变换,Amount 的取值范围可能从 0 到几千甚至上万,Time 从 0 到 17 万秒左右,如果直接把这些原始数值丢给逻辑回归,梯度下降的收敛速度会变慢,而且数值大的特征容易在损失函数里占据主导地位,让模型偏向于依赖交易金额来做判断,而不是综合所有特征。

标准化的常见做法是用 StandardScaler,把每个特征的均值归零、方差归 1。这里有一个很多新手容易忽略的细节:一定要先切分训练集和测试集,再在训练集上 fit 标准化器,最后用同一个标准化器去 transform 测试集。如果先对全量数据做标准化再切分,测试集的信息会泄露到训练过程中,导致评估结果虚高,答辩时被老师一问就露馅。

from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split # 切分特征和标签 X = df.drop('Class', axis=1) y = df['Class'] # 切分训练集和测试集,保持类别比例 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42, stratify=y ) # 对数值特征做标准化 scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test)

stratify=y这个参数非常关键,它保证训练集和测试集里的欺诈样本占比和原始数据一致。如果不加这个参数,随机切分时有可能把测试集里的欺诈样本分得特别少甚至为零,导致你根本没法评估模型对欺诈样本的识别能力。random_state=42是为了让结果可复现,交作业时别人重跑你的代码能得到一模一样的数字,这一点在课程设计评审里是加分项。

3. 类别不平衡问题:逻辑回归在这里最容易“假阳性”翻车

3.1 为什么准确率在欺诈检测里是无效指标

如果直接用原始不平衡数据训练逻辑回归,你会得到一个准确率 99.8% 的模型,然后误以为自己做得很好。但真正该看的指标是召回率——在所有真实欺诈交易里,模型成功抓出了多少。试想一下,如果 492 笔欺诈只被识别出 10 笔,召回率只有 2%,那剩下的 482 笔欺诈仍然会给银行造成巨大损失,99% 的准确率没有意义。

适合不平衡分类的评估指标是混淆矩阵、精确率、召回率和 F1 分数。精确率衡量模型预测为欺诈的样本中有多少是真的欺诈,召回率衡量真实欺诈中有多少被找出来了。这两个指标天然存在此消彼长的关系,F1 分数是它们的调和平均,能帮你在两者之间找一个平衡点。项目文档里推荐重点看召回率和 F1,因为欺诈检测宁可多报一些可疑交易让人工审核,也不能漏掉真实欺诈。

3.2 三种常用处理策略:下采样、上采样与 class_weight

处理不平衡数据有三个主流方向。下采样是从多数类里随机抽取一部分样本,让多数类和少数类数量接近,这样做训练速度快,但会丢弃大量数据,模型可能学不到足够的正常交易模式。上采样是用 SMOTE 之类的算法合成新的少数类样本,保留全部数据,但合成样本可能引入噪声,训练时间也更长。还有一个更简单的方法是把逻辑回归的class_weight参数设为'balanced',让 sklearn 根据类别频率自动调整惩罚权重,代价小的类别权重高,代价高的类别权重低,相当于给少数类“加钱”,让优化器更重视它们。

我一般先跑一个class_weight='balanced'的基线模型,看看召回率能到多少,再决定要不要做更复杂的采样。项目代码里也提供了类似的思路——先用基线模型建立评估基准,再通过调整阈值来优化最终效果。下面是带 class_weight 的逻辑回归训练代码:

from sklearn.linear_model import LogisticRegression from sklearn.metrics import confusion_matrix, classification_report # 训练带类别权重的逻辑回归 model = LogisticRegression(class_weight='balanced', max_iter=1000, random_state=42) model.fit(X_train_scaled, y_train) # 预测并输出评估指标 y_pred = model.predict(X_test_scaled) print(confusion_matrix(y_test, y_pred)) print(classification_report(y_test, y_pred))

class_weight='balanced'是处理这类问题投入产出比最高的单行代码。max_iter在逻辑回归里表示最大迭代次数,默认值是 100,但在标准化后的数据集上有时需要适当调大,否则会报“ConvergenceWarning”警告,看到这个警告不要慌,把max_iter调到 1000 基本就好了。random_state同样是固定随机种子,保证模型初始化的一致性。

3.3 模型阈值调整:别被默认的 0.5 绑住手脚

逻辑回归输出的其实是概率值,sklearn 的predict方法默认把概率大于等于 0.5 的样本判为正类。但在欺诈检测场景里,0.5 这个阈值往往不是最优的,因为欺诈样本太少,模型的预测概率普遍偏低,默认阈值会漏掉很多真实欺诈。这时候应该看predict_proba输出的概率值,然后手动调整阈值。

import numpy as np # 获取预测概率,第二列是正类(欺诈)的概率 y_proba = model.predict_proba(X_test_scaled)[:, 1] # 手动设置阈值,比如 0.3 threshold = 0.3 y_pred_custom = (y_proba >= threshold).astype(int) # 输出新的混淆矩阵 print(confusion_matrix(y_test, y_pred_custom)) print(classification_report(y_test, y_pred_custom))

把阈值从 0.5 降到 0.3 后,模型会把更多概率稍高的样本判为欺诈,召回率通常明显上升,代价是精确率略微下降。在欺诈检测这个场景里这个代价是值得的——多报几笔正常交易让银行风控人员核查一下,远好过漏掉一笔真欺诈。调阈值不是拍脑袋定的,你可以挨个试 0.1 到 0.5 之间的几个值,对比召回率和精确率的组合,然后按业务偏好选出最终阈值。

4. 代码执行全流程:从读数据到出评估报告

4.1 项目文件结构与运行顺序

压缩包里的核心文件有三个:creditcard.csv 是数据集,Creditcard_fraud_detection.py 是主脚本,README.md 是说明文档。如果你想做成一份可以答辩的课程设计,建议按“读数据 → 探索分布 → 标准化 → 切分数据集 → 训练模型 → 评估指标 → 阈值调优 → 可视化混淆矩阵”这个顺序来组织代码,项目主脚本基本也是按这个思路写的。

运行环境需要 Python 3.6 以上,依赖库包括 Pandas、NumPy、scikit-learn,做可视化的话还需要 Matplotlib。如果是在 Jupyter Notebook 里跑,建议把每个环节拆成独立 cell;如果直接运行.py脚本,确保目录下有creditcard.csv文件,脚本会用相对路径读取数据。我习惯在项目根目录下执行脚本,避免路径出错。

4.2 完整训练脚本的逐段拆解

下面是一段可以在课程设计中直接使用的完整流程代码,从读取数据到输出评估报告,一行一行都能讲清楚:

import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.metrics import confusion_matrix, classification_report # 1. 加载数据 df = pd.read_csv('creditcard.csv') # 2. 特征与标签分离 X = df.drop(columns=['Class']) y = df['Class'] # 3. 切分数据集,保持类别比例 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42, stratify=y ) # 4. 标准化特征 scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # 5. 训练逻辑回归模型 model = LogisticRegression(class_weight='balanced', max_iter=1000, random_state=42) model.fit(X_train_scaled, y_train) # 6. 预测与评估 y_pred = model.predict(X_test_scaled) print(classification_report(y_test, y_pred)) print('混淆矩阵:') print(confusion_matrix(y_test, y_pred))

这段代码的逻辑非常清晰:先做数据准备,再做特征工程,然后建模评估。train_test_split里的test_size=0.3表示用 30% 的数据做测试,你可以改成 0.2 或 0.25,但要注意测试集太小会导致评估结果波动,太大又会让训练数据不足。标准化要把fit和transform分开,这个前面强调过,答辩时这里是很常见的提问点——如果老师问你训练集和测试集为什么要分别处理,答案就是“防止测试集信息泄漏”。

4.3 训练时间与硬件要求

creditcard.csv 有 28 万行数据、31 个特征,规模在机器学习里属于中小型。逻辑回归本身是线性模型,计算开销很低,普通笔记本 CPU 训练时间通常在几秒到十几秒之间,完全不需要 GPU。如果你的电脑跑出几分钟甚至更久,大概率是max_iter设得过大或者循环里重复训练了多次模型,排查一下代码结构就行。

5. 避坑指南:逻辑回归做欺诈检测的五个常见翻车点

5.1 训练集和测试集共用了同一个标准化器

现象:训练出来的模型看评估指标非常漂亮,但在答辩现场用新数据测试时效果明显下滑。原因:先对全量数据做标准化再切分,标准化器已经“见过”测试集的信息,产生了数据泄漏。解决:严格按“先切分、后 fit_transform 训练集、再用同一个 scaler 只 transform 测试集”的顺序执行,这属于流程性错误,也是最容易被答辩老师抓到的硬伤。

5.2 只报 accuracy 不报召回率

现象:很多人写完代码只输出一个accuracy_score,看到 99% 就觉得大功告成。原因:没意识到类别不平衡场景下准确率没有参考价值,把模型训练成了“全预测为正常”。解决:至少要输出混淆矩阵和classification_report,重点关注 recall 和 f1-score,并在文档里解释为什么准确率不适用。

5.3 报 ConvergenceWarning 却不处理

现象:脚本运行过程中出现ConvergenceWarning: Maximum number of iterations reached,但模型还是输出了结果,于是直接忽略。原因:默认max_iter=100不够,逻辑回归的优化过程没有收敛。解决:把max_iter调大到 1000,同时确认特征已经标准化。这个警告在答辩时很显眼,不要让它在你的运行日志里出现。

5.4 忘了设置随机种子导致结果不可复现

现象:每次重新运行脚本,训练出的模型评估指标都不一样,记录实验结果时数值总是对不上。原因:训练集切分和模型初始化都有随机性,没固定随机种子。解决:在train_test_split和LogisticRegression里都加上random_state=42,让整套流程可复现,写实验报告时数据是稳定的。

5.5 测试集切分不做分层采样

现象:多跑几次切分,偶尔发现测试集里欺诈样本的数量为 0 或者特别少,评估结果忽高忽低。原因:默认切分是随机抽的,在不平衡数据里少数类样本可能全被分到训练集或全被分到测试集。解决:train_test_split中加stratify=y,确保切分后训练集和测试集的类别比例与原始数据一致。这是处理不平衡数据的基本常识,项目代码里也照这个做法写的。

6. 把结果做好看:混淆矩阵可视化与答辩口径

课程设计答辩时,一张清晰的可视化图表比十行文字描述更有说服力。项目里用了 Matplotlib 做可视化,我建议至少画两个图:一个是混淆矩阵热力图,一个是欺诈样本比例分布图。混淆矩阵能直观展示模型漏报和误报的数量,分布图能说明数据不平衡问题的严重程度,也解释了你为什么不做“全预测为正常”的无脑模型。

混淆矩阵可视化的代码很短,但很出效果:

import matplotlib.pyplot as plt import seaborn as sns # 构建混淆矩阵并可视化 cm = confusion_matrix(y_test, y_pred) plt.figure(figsize=(6, 5)) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=['Normal', 'Fraud'], yticklabels=['Normal', 'Fraud']) plt.xlabel('Predicted') plt.ylabel('Actual') plt.title('Confusion Matrix - Logistic Regression') plt.show()

annot=True会在每个格子里显示具体数字,fmt='d'表示按整数格式显示。如果你不想引入 seaborn,用plt.imshow(cm)也能做,但 seaborn 的热力图在答辩投影上更清晰。左下角的数值是“实际欺诈但被预测为正常”的数量,也就是漏报数,右下角是“实际欺诈且被预测为欺诈”的数量,也就是成功抓出的欺诈笔数。讲结果时先说漏报数,再说召回率,接着解释为了降低漏报把阈值调低了一些,但会带来多少误报成本——这个过程能让老师看到你不是只会敲代码,而是真在思考业务价值。

阈值调优那边还有一个可以加的验证方法:画出不同阈值下的召回率和精确率曲线,直观展示二者此消彼长的关系。做法是遍历 0.1 到 0.9 的所有阈值,计算每个阈值下的两个指标,然后绘图。这段代码不长,但对“阈值为什么不能拍脑袋定”这个问题是很好的可视化证明。我当时做期末大作业的时候,就是靠这张曲线图把阈值选择讲清楚的,老师听完没有再追问。从那以后我每次写不平衡分类的项目,都会强制走一遍“先看分布、再定基线、最后调阈值”的流程,评估指标也永远先看召回率。这份项目无论是做毕设底子还是期末大作业,都够你交出一份让导师认可的答案,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 16:38:36

JSP+MySQL个人记事系统源码部署实战:从JDBC配置到Tomcat war包发布

简介:基于JSP与MySQL实现的个人记事备忘系统完整源码包,面向正在学习Java Web开发的初学者、毕业设计学生以及需要快速搭建轻量级记事本应用的开发者。项目采用JSPServletJDBC经典技术栈,涵盖用户笔记增删改查、分类管理、登录验证等核心功能…

作者头像 李华
网站建设 2026/9/26 16:36:55

Claude CLI工具链:基于MCP协议的工程化AI编程实践

1. 项目概述:这不是一个“模板库”,而是一套面向 Claude 生态的 CLI 工具链设计范式 “claude-code-templates”这个标题乍看像一个 GitHub 上常见的静态代码片段合集——比如几十个 .js 或 .py 文件,按语言或场景分类,供人复…

作者头像 李华
网站建设 2026/9/26 16:35:48

Jobs Portal v3.5求职招聘系统源码二次开发全攻略:部署避坑与商用升级

简介:Jobs Portal求职招聘系统v3.5是一套面向求职者、企业HR及开发者的完整招聘平台源码,主要解决职位信息发布、简历上传与检索、候选人筛选、站内沟通与后台管理等环节的在线化问题,适用于企业招聘门户搭建、培训机构项目实训或开发者二次学…

作者头像 李华
网站建设 2026/9/26 16:35:48

AI Agent 2026完全指南:用TaoToken统一Key打通MCP与A2A的Agent架构配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华