☰
金融反欺诈机器学习实战:不平衡分类与模型评估全流程
2026/10/1 23:18:26 网站建设 项目流程

简介:这是一套基于Python与机器学习的金融反欺诈检测完整资源包,包含源码、开发文档说明与数据报告,面向期末作业、课程设计或项目开发场景,适合需要上手完整建模流程的初学者与进阶者。项目以金融交易数据为对象,覆盖缺失值处理、特征筛选与标准化等预处理环节,引入SMOTE采样缓解不平衡数据问题,并通过stacking集成学习完成欺诈识别建模,代码经严格测试,可放心参考并二次扩展。压缩包共17个文件,大小约66.7MB,主体为2个ipynb模型代码与1个md开发说明,另附项目报告pptx、模型可视化html、原始数据rar及多张实验图表,便于对照复现与成果展示。已有192人浏览学习。借助配套可视化图表、报告模板与README说明,能快速理清数据预处理、不平衡采样、集成建模等模块的衔接,是期末答辩、课程汇报或简历项目的落地参考。目录层次清晰,从数据导入到模型评估均有对应文件,适合快速搭建演示环境。

1. 金融反欺诈检测这门课设,到底在训练什么

答辩现场最容易出现的尴尬不是模型跑不出来,而是老师问了一句:“你的准确率 99.7%,为什么还要做反欺诈?” 你要是答不上来,前面熬的夜全白费。基于 python+机器学习做的金融反欺诈检测,本质上是训练一个二分类模型去识别交易数据里的欺诈样本,但它最大的特点不是“准确”,而是数据极度不平衡——一万笔交易里可能只有几笔是欺诈。这门课设的完整交付物包括可运行的源码、开发文档说明和数据报告,多数是期末大作业或课程设计的形式。它适合机器学习入门者练手,也适合拿来做毕业设计的预研:数据量不大、模型选型空间充足、评估指标有讲头,从数据处理到答辩演示能形成完整闭环。这篇笔记按我实际做过的方案,从数据探索、模型训练、评估到写文档,一条线走完。

2. 拿到信用卡欺诈数据先别急着跑模型:数据探索与特征准备的完整流程

2.1 数据长什么样:从读入到 EDA 的最少代码

金融反欺诈检测最常用的公开数据集是信用卡交易记录,特征是经过 PCA 降维后的数值型变量,标签只有 0 和 1。这类数据集的优点是干净,缺点是太干净——你不用做太多特征工程,反而更容易把精力放在建模和评估上。

拿到数据第一步不是建模,而是搞清楚三件事:样本量多大、正负样本比例多少、有没有缺失值。用 pandas 几行代码就能看明白:

import pandas as pd df = pd.read_csv("creditcard.csv") print(df.shape) # (284807, 31) print(df.isnull().sum().sum()) # 缺失值总数 print(df["Class"].value_counts(normalize=True))

从输出能直接看到负样本占比往往在 0.1% 到 1% 之间,这就是金融反欺诈检测和普通分类任务最本质的区别。这一步的产出是一个数字:欺诈样本占多少。后面所有模型选型、评估指标、阈值调整,都围绕这个比例展开。

2.2 为什么金融反欺诈必须做标准化:Amount 与 PCA 特征的尺度差异

这个数据集的绝大多数字段 V1 到 V28 是 PCA 变换后的结果,均值在 0 附近、方差接近 1,但 Amount(交易金额)字段是原始数值,范围可能从 0.01 到几万。如果不做标准化,基于距离和梯度的模型会天然偏向金额大的样本,逻辑回归的系数也会被尺度带偏。

from sklearn.preprocessing import StandardScaler scaler = StandardScaler() df["Amount_scaled"] = scaler.fit_transform(df["Amount"].values.reshape(-1, 1)) df = df.drop(["Amount", "Time"], axis=1) # Time 对欺诈检测帮助有限,直接删掉

这里有两个细节。第一,fit_transform是对 Amount 单独做的,因为 V1 到 V28 已经是标准化后的结果,不需要再动。第二,Time 字段表示交易时间间隔,在大多数课设场景下对分类没有直接贡献,删掉可以降低噪声;如果你有兴趣,也可以把它拆成“小时”或“星期”这种周期性特征做尝试。特征的尺度问题直接影响模型收敛速度,特别是用逻辑回归或神经网络的时候,不标准化的后果是 loss 下降慢、系数解释性差。

2.3 训练集 / 验证集划分:时间顺序比随机划分更接近真实场景

很多教程直接train_test_split(df, test_size=0.2)随机切分,这在金融反欺诈场景里是错的。原因是交易数据天然带时间属性,随机划分会让模型“看到未来”——训练集里混入了时间上更晚的样本,验证集里却出现了更早的数据,这相当于开卷考试。

df = df.sort_values("Time").reset_index(drop=True) split_idx = int(len(df) * 0.8) train = df.iloc[:split_idx] test = df.iloc[split_idx:] X_train = train.drop("Class", axis=1) y_train = train["Class"] X_test = test.drop("Class", axis=1) y_test = test["Class"]

我一般会先按Time排序,再取前 80% 做训练、后 20% 做验证。这样模拟的是“用过去的数据预测未来的交易”,和真实风控系统的上线流程一致。答辩时如果你能主动说出这个理由,老师会认为你不是在照搬代码,而是理解了这个场景的业务逻辑。另外,这里排序用的Time是原始字段,所以上一步先别急着删,等切分完再删也不迟。

3. 用逻辑回归和 XGBoost 搭出第一个可运行的反欺诈模型

3.1 逻辑回归做基线:金融风控开场为什么多是它

金融反欺诈检测的入门模型,我几乎总是先跑逻辑回归。原因有三:训练快、可解释性强、对不平衡数据有天然的应对手段。逻辑回归输出的是概率而不是硬分类,这在风控场景里非常有用——你可以拿概率去卡阈值,而不是被迫接受 0/1 的判决。另一个重要原因是class_weight参数,它能在不改变数据分布的前提下,让模型更关注少数类。

from sklearn.linear_model import LogisticRegression from sklearn.metrics import roc_auc_score lr = LogisticRegression(class_weight="balanced", max_iter=500) lr.fit(X_train, y_train) y_prob = lr.predict_proba(X_test)[:, 1] print("AUC:", roc_auc_score(y_test, y_prob))

class_weight="balanced"会自动按类别频率反比调整权重:少数类样本的损失被放大,模型不再“躺平”把所有样本都预测成正常交易。max_iter=500是为了给收敛留足迭代空间,因为标准化后的高维特征虽然收敛快,但默认的 100 次有时不够。这一步跑完,AUC 通常能到 0.95 以上,说明逻辑回归在这个任务上已经有不错的区分度了。

3.2 给 XGBoost 调三个参数:从过拟合拉回泛化

逻辑回归做基线没问题,但要想在课设里体现“我做了模型对比”,XGBoost 是第二梯队的最佳选择。它训练快、对特征交互敏感、自带正则化,缺点是需要调的参数比逻辑回归多。我一般只动三个参数,其他保持默认:

from xgboost import XGBClassifier xgb = XGBClassifier( n_estimators=200, max_depth=5, scale_pos_weight=99, # 负样本≈99倍于正样本时使用 learning_rate=0.1, eval_metric="auc", use_label_encoder=False ) xgb.fit(X_train, y_train) y_prob_xgb = xgb.predict_proba(X_test)[:, 1] print("XGB AUC:", roc_auc_score(y_test, y_prob_xgb))

scale_pos_weight的作用和逻辑回归的class_weight类似,它让 XGBoost 在计算梯度时给少数类更大的权重。这个值一般取负样本数除以正样本数,你可以自己算完填进去。max_depth=5控制树的深度,太深容易过拟合;learning_rate=0.1配合n_estimators=200是折中方案。跑完后看 AUC 是否比逻辑回归有提升——有提升就把它当最终模型,没提升就用逻辑回归当最终模型,毕竟答辩时“能解释清楚”比“分数高一点”更重要。

3.3 类别不平衡的两种处理:class_weight 与 SMOTE 的取舍

遇到类别不平衡,很多人第一时间想到 SMOTE(合成少数类过采样)。但我要泼一盆冷水:SMOTE 在这类 PCA 特征数据集上的收益可能不大,而且用它之前必须想清楚评估方式会不会泄漏。

from imblearn.over_sampling import SMOTE from sklearn.model_selection import train_test_split # 只对训练集做过采样,测试集保持原样 X_train_sm, y_train_sm = SMOTE(random_state=42).fit_resample(X_train, y_train) print(y_train_sm.value_counts())

这里最容易犯的错是对全量数据做过采样再切分,这属于典型的评估泄漏,后面避坑章节会专门讲。SMOTE 的核心逻辑是在少数类样本之间插值生成新样本,让正负样本比例接近 1:1,但它生成的是“假数据”,不一定符合真实欺诈分布。我的做法是:先跑一个class_weight或scale_pos_weight的版本,再跑一个 SMOTE 版本,对比 AUC 和召回率,用结果说话。如果两者差不多,优先选择前者,因为代码更简洁、答辩时更好解释。

4. 反欺诈模型的真正考题:召回率、精准率与 AUC 的读法

4.1 accuracy 是陷阱:0.17% 的欺诈率下读准确率没有意义

金融反欺诈检测的评估环节,是区分“真懂”和“照葫芦画瓢”的分水岭。假设你的模型把所有交易都预测为正常,准确率是 99.83%——听起来很优秀,但这个模型毫无价值,因为它漏掉了所有欺诈。所以我从来不在答辩里说“准确率有多高”,而是用 AUC、召回率和精准率说话。

from sklearn.metrics import classification_report, confusion_matrix y_pred = (y_prob_xgb > 0.5).astype(int) print(classification_report(y_test, y_pred, target_names=["正常", "欺诈"])) print(confusion_matrix(y_test, y_pred))

classification_report会输出每个类别的 precision、recall 和 f1-score,其中欺诈类的召回率是最重要的指标——它代表“100 笔欺诈里能抓出多少”。如果召回率低于 0.5,说明模型在漏案子,这在风控场景里是不可接受的。阈值 0.5 是 scikit-learn 的默认值,但在极度不平衡数据上它往往不是最优选择,所以下一步要调阈值。

4.2 用混淆矩阵和 PR 曲线挑阈值:让模型按业务要求“偏科”

贷款风控关心的是误伤率——把好客户拦下来是损失;但反欺诈更关心的是漏检率——放过一个坏人可能造成更大损失。这两个诉求在阈值上是互斥的,你只能在“多抓”和“少错杀”之间取一个平衡点。

import numpy as np from sklearn.metrics import precision_recall_curve precisions, recalls, thresholds = precision_recall_curve(y_test, y_prob_xgb) best_f1 = 0 best_threshold = 0.5 for t, p, r in zip(thresholds, precisions[:-1], recalls[:-1]): f1 = 2 * p * r / (p + r) if (p + r) > 0 else 0 if f1 > best_f1: best_f1 = f1 best_threshold = t print("Best F1:", best_f1, "Threshold:", format(best_threshold, ".4f"))

这段代码遍历所有可能阈值,找出 F1 分数最高对应的截断点。在实际课设里,你可以把“业务规则”写得更具体:比如“召回率不低于 0.8 的前提下,尽量提高精准率”,然后据此选阈值。调阈值这件事本身不需要重新训练模型,它只改变判决边界,所以你可以公开这样做——答辩时讲清楚“我为了业务目标选择了 0.3 而不是 0.5”,比单贴一个模型结果更有说服力。

4.3 导出模型文件与预测结果:给答辩留好“后悔药”

课设通常要现场演示,最尴尬的情况是环境崩了、模型丢了。把训练好的模型保存到本地文件是个好习惯,答辩前哪怕代码重跑,也能直接加载模型文件输出预测结果。

import joblib joblib.dump(xgb, "fraud_model.pkl") # 加载模型对单笔交易做预测 # loaded_model = joblib.load("fraud_model.pkl") # sample = X_test.iloc[[0]] # prob = loaded_model.predict_proba(sample)[:, 1] # print("欺诈概率:", prob[0])

保存模型之后,再做一步“预测结果落地”——把测试集的预测概率和原始标签拼在一起,导出成 CSV。这样做有两个好处:一是答辩时可以直接展示“模型在这 5000 条未来数据上抓出了多少欺诈”,二是如果答辩时有人质疑结果,你有数据文件可以现场复盘,不至于翻车。

5. 金融反欺诈开发中的 5 个高频踩坑:从数据泄漏到过拟合

5.1 坑一:SMOTE 过采样后直接切测试集——训练泄漏

现象:用 SMOTE 处理完整数据集后,再随机切分训练集和测试集,AUC 高达 0.999,答辩时被老师一问就露馅。

原因:SMOTE 生成的合成样本同时进入了训练集和测试集,模型在“看到过”的样本上做评估,分数虚高。

解决:先切分,再对训练集单独过采样;测试集必须保持原始分布。这是我反复强调的一点,也是最容易忽略的评估泄漏。

5.2 坑二:StandardScaler 在全量数据上 fit——标准化泄漏

现象:训练时 AUC 正常,但对新数据做预测时效果暴跌。

原因:scaler.fit_transform(df["Amount"])使用了全量数据的均值和标准差,相当于测试集的信息被训练阶段“偷看”了。这比 SMOTE 泄漏更隐蔽,因为很多人根本想不到标准化也算泄漏。

解决:先切分数据,再对训练集的Amount字段fit,然后用训练集的均值和标准差transform测试集。代码就三行,但顺序不能错:

scaler = StandardScaler().fit(X_train["Amount"].values.reshape(-1, 1)) X_train["Amount_scaled"] = scaler.transform(X_train["Amount"].values.reshape(-1, 1))

5.3 坑三:无视类别不平衡直接调默认参数——模型“躺平”

现象:模型跑完,AUC 挺高,但看混淆矩阵发现欺诈类召回率是 0,模型把所有样本都预测为正常。

原因:默认参数下模型优化的是整体准确率,欺诈样本占比太低,模型发现“全部预测为正常”损失最小,于是躺平。

解决:逻辑回归用class_weight="balanced",XGBoost 用scale_pos_weight=99,或者在训练前确认正负样本比例。不要迷信 AUC 高就是好模型——AUC 只看排序不看过拟合程度,和实际判决行为是两回事。

5.4 坑四:随机划分训练集——时间维度“剧透”

现象:模型在验证集上召回率达到 0.9,但放到真实场景里效果大幅下降。

原因:随机切分让训练集和测试集来自同一时间段,模型学到了“时间段特征”而非“交易特征”。真实场景要用历史数据预测未来,时间错位后就失灵了。

解决:严格按照Time排序后前 80% 训练、后 20% 验证。如果数据集没有时间字段,则不要使用该字段做随机划分以外的任何处理,并在文档里说明这一假设。

5.5 坑五:阈值调到召回率等于 1——误伤率爆表

现象:为了让召回率变成 1,把阈值降到 0.1,结果正常交易也被大量拦下,业务上不可用。

原因:调阈值一味追求单指标,忽视了风控的真实约束。真实场景里误伤过多会导致用户流失和投诉,甚至超过欺诈造成的损失。

解决:调阈值时参考 PR 曲线,设定“误伤率上限”或“召回率下限”,在这两个约束内取最优。比如“召回率不低于 0.8 时,精准率最高”的阈值就是合理选择。把这条逻辑写进报告,答辩反而加分。

6. 把课设做成能答辩的交付物:开发文档、数据报告与演示路径

6.1 开发文档怎么写:从环境到复现结果一节不落

很多人的开发文档是答辩前草草补的,这很吃亏。真正加分的是“环境能复现、结果能验证”的说明,即使代码写得不那么完美,文档完整也能弥补印象分。

# 金融反欺诈检测系统 ## 运行环境 - Python 3.8+ - pandas==1.5.3 / numpy==1.24.3 / scikit-learn==1.2.2 - xgboost==1.7.5 / imblearn==0.10.1 ## 文件结构 - src/data_processing.py # 数据读取、清洗、切分 - src/train.py # 模型训练与保存 - src/evaluate.py # 评估指标计算与阈值调整 - data/creditcard.csv # 原始数据集 ## 运行步骤 1. python src/data_processing.py # 生成 X_train, X_test 2. python src/train.py # 训练并保存 fraud_model.pkl 3. python src/evaluate.py # 输出评估结果与阈值 ## 结果复现 - AUC: 0.97(XGBoost) - 召回率(欺诈): 0.82 @ threshold=0.3

这份文档的核心价值是第三人称“可验证性”——任何人按这个步骤跑一遍,能得出和你报告中一致的数字。写文档时把每条结论都挂上对应脚本名,比大段理论描述有用得多。

6.2 数据报告的核心图表:从数据分布到模型结论

数据报告不是代码日志的堆砌,而是回答四个问题:数据长什么样、模型怎么选的、效果如何、业务上意味着什么。以下五张图表足以撑起一份课设报告:

图表解决的问题生成工具
类别分布柱状图直观展示不平衡程度matplotlib / seaborn
特征相关性热力图说明 PCA 特征之间独立性seaborn
逻辑回归与 XGBoost 的 ROC 曲线对比支撑模型选型结论matplotlib
PR 曲线展示不同阈值下的精准率 / 召回率权衡matplotlib
混淆矩阵热力图展示最终模型在测试集上的表现seaborn

每张图配一段两三句话的解读,重点不是“它是什么图”,而是“从图里看出了什么,所以接下来怎么做”。比如“类别分布显示欺诈占比 0.17%,因此评估指标放弃 accuracy 改用 AUC 和召回率”——把图和决策串起来,报告就有血有肉了。

6.3 答辩演示的 20 分钟路径:从数据到结论一气呵成

答辩演示的时间分配很重要。我建议按“2 分钟背景— 5 分钟数据 — 5 分钟模型 — 5 分钟评估 — 3 分钟总结与展望”来安排,这样既覆盖了项目的完整闭环,又给每部分留了讲清楚的时间。演示时手里要准备一页“模型好坏怎么界定”的说明——这往往是答辩老师最关注的:你的模型到底好在哪,是比逻辑回归好了多少,还是比随机猜测好了多少?用数字回答,不要用形容词。

我的习惯是开场直接说:“这个项目解决的是信用卡交易中的欺诈识别问题,我选择了 XGBoost 模型,在测试集上 AUC 0.97,欺诈类召回率 0.82。下面看数据是怎么支撑这个结论的。” 然后按数据分布、基线模型、调阈值、最终结论走一遍,全程不超过 20 分钟。这套路的终点是“可复现的交付物”——代码、文档、报告三件套齐全,实验数字经得起验证,你就不怕任何追问。

做这种机器学习课设,最怕的不是模型跑不出来,而是不知道好模型长什么样、文档和报告该怎么组织。把重心放在数据切分、评估指标和文档可复现性上,这个项目的底线就守住了。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询