ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

信用卡数据集实战:审批模型与欺诈识别为何不能共用一套代码

信用卡数据集实战:审批模型与欺诈识别为何不能共用一套代码 简介面向机器学习与数据分析初学者的信用卡申请数据实战项目包含34.81 KB完整数据集和五个精心整理的Python脚本覆盖数据清洗、可视化、逻辑回归建模、PCA降维、K均值聚类与Isolation Forest异常检测等关键环节。压缩包共7个文件含5个.py源代码、1个readme.txt和1个csv数据文件整体仅18KB轻量易获取代码全部可运行依赖pandas、numpy、plotly.express、seaborn、sklearn等标准库分别演示信用卡申请审批预测、欺诈检测、可疑客户识别与模型快速对比等任务。目前已有96人学习/浏览适合希望用真实业务数据快速上手机器学习流程的读者。通过对照源码与数据集可直观理解从特征工程到模型训练评估的完整路径还能将同样的分析思路迁移至信用风险控制、反欺诈等实际项目中无论是课程设计、毕业设计还是个人学习都能从中获得完整实践样例。1. 信用卡数据集分析预测实战审批模型与欺诈识别为什么不能共用一套代码拿到这份信用卡数据集之后我先干的事不是写模型而是把五个脚本和 readme 串起来看了一遍。它的思路很清楚34.81 KB 的 CSV 数据同时喂给两条技术线——一条用逻辑回归和 LazyPredict 做信用卡申请审批预测另一条用 PCA、KMeans、Isolation Forest 做异常客户识别。两套逻辑从数据预处理开始就分叉了审批要的是特征可解释性欺诈要的是离群点敏感性。这份资源适合两种人一是刚开始接触 sklearn想用真实 CSV 把完整流程跑通的人二是想在自己数据集上快速复现分类 异常检测双方案的从业者。下面我把五个脚本逐个拆开讲清楚每一步在做什么、参数为什么这么设、以及哪些位置按默认值跑会翻车。2. 数据清洗与基线建模先用 LazyPredict 把全模型跑一遍再谈调优2.1 先读数据问号占位、缺失值分布和标签分布一起看经典的 UCI 信用卡审批数据集特征列一般是脱敏编号 A1~A15最后一列是 approval 标签。这份 CSV 只有 34.81 KB行数在几百条级别跑起来非常快但字段类型很杂数值列、类别列、缺失值标记混在一起。第一步不是直接扔给模型而是先确认缺失值长什么样再确认标签分布是否均衡。这两个信息决定后面所有预处理策略。import pandas as pd df pd.read_csv(data/Credit_Card_Applications.csv, headerNone) print(df.shape) print(df.head()) # 经典版本里缺失值用 ? 占位读进来是字符串而不是 NaN for col in df.columns: q_count (df[col] ?).sum() if q_count 0: print(fcolumn {col}: {q_count} missing values) # 标签列分布 print(df.iloc[:, -1].value_counts())逻辑说明先用headerNone读因为原文件没有表头逐列统计?的数量是为了确认缺失值用什么符号占位最后看标签列分布判断类别是否平衡。这里最容易犯的错是直接df.isnull().sum()发现全是 0 就以为数据干净了——实际上缺失值全藏在?里。参数说明read_csv的headerNone是必须的不然第一行会被当成列名后面建模时可以做映射把?统一替换成np.nan但我更推荐用na_values?在读入阶段就转换少写一行替换逻辑后续所有列都是干净的数值类型。替换完成后做一次简单的数据类型审计数值列转 float类别列转 category。这一步看起来基础但对后面的 PCA 和 KMeans 影响很大——这两类算法全是距离计算类别列不编码、缺失值不填充距离矩阵就是错的聚类结果直接没法看。2.2 LazyPredict 跑基线十几个分类器一次出结果在深入逻辑回归之前先用1-LazyPredict to Classify Credit Card Applications.py跑一遍基线。LazyPredict 是第三方 AutoML 库作用是自动拟合十几个常见分类器并打印准确率、AUC、训练时间方便在五分钟内判断这个数据集的分类天花板大概在哪。这一步的价值是建立参照系避免后面调逻辑回归时不知道好坏边界。# pip install lazypredict from lazypredict.Supervised import LazyClassifier from sklearn.model_selection import train_test_split X df.iloc[:, :-1].copy() y df.iloc[:, -1].copy() # 先做特征编码与缺失值填充LazyPredict 不接受 NaN X X.replace(?, pd.NA) X X.apply(pd.to_numeric, errorscoerce) X X.fillna(X.median()) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) clf LazyClassifier(verbose0, ignore_warningsTrue, predictionsTrue) models, predictions clf.fit(X_train, X_test, y_train, y_test) print(models)逻辑说明LazyPredict 的fit接口和 sklearn 不一样它直接接收训练集和测试集内部循环遍历分类器并打分返回一个 DataFrame 和全部分类器的预测结果。我们用它判断哪些模型值得继续调而不是直接拿结果上线。参数说明test_size0.2是常规划分样本量只有几百条时不要超过 0.3否则测试集太小、评估波动大stratifyy保证划分前后标签比例一致尤其在类别不平衡时这个参数直接决定测试集是否失真random_state42是固定随机种子让每次跑出的结果可复现别人拿到脚本也能复现你的实验。跑完的基线表大致会长这样模型AccuracyROC AUCTime TakenLogisticRegression0.860.880.01RandomForestClassifier0.840.850.03KNeighborsClassifier0.810.790.01SVC0.800.820.02基线表的意义不是找冠军模型而是观察逻辑回归在这个脱敏数据集上不输复杂集成模型——这决定了后面审批预测主脚本用逻辑回归是合理选择而不是拍脑袋。如果你跑出来某个树模型明显碾压逻辑回归那后面调参方向就要换。如果 LazyPredict 安装失败常见原因是 Python 版本太高3.11 以上部分依赖包没跟上我一般降到 3.9 或 3.10 的虚拟环境再装。注意 LazyPredict 只是辅助工具生产代码里不会依赖它基线的目的就是快速摸底。2.3 数据划分的细节先清洗再切分顺序错一步就是数据泄漏train_test_split在模块清单里被单独列出来确实值得单独讲。常见误用是先对整个数据集标准化或填充再切分这在生产上是数据泄漏——测试集的信息提前进了训练过程评估结果虚高上线后真实表现会打回原形。# 正确顺序先切分再做填充和标准化 X_train, X_test, y_train, y_test train_test_split( X_raw, y, test_size0.2, random_state42, stratifyy ) # 填充用训练集的统计量不能让测试集参与计算 median_vals X_train.median() X_train X_train.fillna(median_vals) X_test X_test.fillna(median_vals) # 标准化同样只用训练集的均值和方差 scaler StandardScaler().fit(X_train) X_train_scaled scaler.transform(X_train) X_test_scaled scaler.transform(X_test)逻辑说明fillna和StandardScaler都是用训练集统计量去变换测试集测试集在这里只负责被评估不贡献任何统计信息。这样做的评估结果才是真实线上水平而不是实验室里的幻觉。参数说明median比mean抗离群点数据里如果有极端值中位数更稳StandardScaler默认把数据变成均值 0、方差 1逻辑回归、KMeans、PCA 都需要这一步树模型则完全不需要。所以当你看到脚本里同时出现 StandardScaler 和随机森林时心里要打个问号——树模型不依赖尺度标准化对它是多余操作。3. 审批通过率预测逻辑回归系数就是审批秘密最直接的答案3.1 主脚本的四步管道编码、填充、标准化、回归4-Credit Cards Applications.py是资源里的主干脚本处理的是该不该批这张卡的问题。审批决策本质上是一个二分类标签就是 approved 或 rejected特征就是申请人的各项资料。脱敏后的特征没有真实业务含义所以这里追求的不是业务上可解释的规则而是一个规范、可复现、能交差的分类流程。import numpy as np import pandas as pd from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler X X.replace(?, pd.NA).apply(pd.to_numeric, errorscoerce) X X.fillna(X.median()) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) scaler StandardScaler().fit(X_train) X_train_s scaler.transform(X_train) X_test_s scaler.transform(X_test) model LogisticRegression(C1.0, max_iter1000, solverlbfgs, random_state42) model.fit(X_train_s, y_train) print(train acc:, model.score(X_train_s, y_train)) print(test acc:, model.score(X_test_s, y_test)) print(test AUC:, roc_auc_score(y_test, model.predict_proba(X_test_s)[:, 1]))逻辑说明数据清洗和划分顺序与第 2 章完全一致强调先切分再变换。逻辑回归在标准化之后的特征上训练系数大小才能横向比较未标准化的特征直接按系数绝对值排序没有意义因为量纲差异会把排序带偏。参数说明C1.0是正则化强度的倒数C 越小正则越强样本量小时 C 太大容易过拟合表现为训练集精度高、测试集掉下来。max_iter1000是为了防止 lbfgs 在默认 100 次迭代下没收敛就报警告solverlbfgs是中小数据集上逻辑回归的默认首选适合 L2 正则。predict_proba拿的是概率而不是硬分类AUC 必须要用概率计算直接拿model.predict的结果去算会丢失排序信息AUC 值会偏低。3.2 审批洞察脚本系数排序和概率直方图是两道验证工序2-Credit Card Application Secrets Approval Insi_8639.py名字里带 Secrets其实就是把哪些特征在影响审批这件事量化了。逻辑回归在标准化之后系数可以直接解释为该特征每变动一个标准差对数几率的变化量。所以按系数绝对值排序就是特征重要性的粗糙排序这也是审批秘密最直接的答案。coef_df pd.DataFrame({ feature: [fA{i} for i in range(1, X.shape[1] 1)], coef: model.coef_[0], }) coef_df[abs_coef] coef_df[coef].abs() coef_df coef_df.sort_values(abs_coef, ascendingFalse) print(coef_df.head(10)) # 预测概率分布直方图看模型是否把大部分样本压在一个置信区间 proba model.predict_proba(X_test_s)[:, 1] import plotly.express as px px.histogram(proba, nbins20).show()逻辑说明系数表回答哪些特征在驱动审批决策概率直方图回答这个模型的输出有没有区分度。如果直方图集中在 0.4~0.6说明模型实际上分不开两类样本这时候再看 AUC 大概率也不好看。参数说明model.coef_[0]在二分类中直接取第一行即可多分类场景才需要遍历nbins20控制直方图分箱样本量几百条时分到 20 箱足够看出分布形状。特征编号 A1~A15 因为脱敏已经不可考但不妨碍判断这条审批模型线是否可用。到这里审批预测这条线基本闭环了。如果你发现在系数排序里某个特征绝对值特别大但业务上完全说不过去先别急着给结论——检查一下这个特征是不是有极端离群值离群值会拉高系数但它的预测力可能是虚假的。3.3 类别不平衡的加权处理class_weight 该什么时候用审批数据里拒绝样本占大头是很常见的事。如果负样本占到 80% 以上模型学到的捷径是全部输出拒绝准确率照样 0.8 以上但通过的那几笔一个都没预测出来。这时候光靠调阈值不够得在模型层面加权。model LogisticRegression( C1.0, max_iter1000, solverlbfgs, class_weightbalanced, random_state42, ) model.fit(X_train_s, y_train) from sklearn.metrics import classification_report print(classification_report(y_test, model.predict(X_test_s)))逻辑说明class_weightbalanced会让 sklearn 自动按类别频率反比加权少数类样本的错分代价被放大模型被迫去关注那些占比少但重要的样本。配合classification_report看每个类的 precision、recall、f1能立刻发现全拒答问题是否被缓解。参数说明加了class_weight之后整体准确率通常会小幅下降但少数类的 recall 会明显上升。如果你做的是审批场景漏掉一个优质客户的代价可能比误拒一个劣质客户更高优先保 recall 是合理策略如果是风控场景误杀太多会导致客诉优先保 precision。这个取舍没有标准答案取决于业务不是代码问题。4. 避坑这套代码在真实运行中踩过的五个位置4.1 缺失值用?占位isnull 统计永远是 0现象跑df.isnull().sum()输出全是 0但模型一训练就报 Input contains NaN。原因经典 UCI 信用卡数据集用?字符串表示缺失pandas 不会自动识别成 NaN。跳过缺失值处理直接建模数值列被当成字符串要么报错要么精度全崩。解决pd.read_csv(..., na_values?)读入时统一转换或者读完后df.replace(?, pd.NA)再fillna。我在处理这类老数据集时都用na_values在读取阶段解决后面所有列都是干净的数值类型排查问题少一层。这个坑藏得深因为它不报错只会在模型精度上做文章。4.2 先标准化再切分导致的假高分现象测试集准确率 0.92看起来很漂亮换一批真实数据掉到 0.78。原因标准化和缺失值填充用了全量数据的统计量测试集信息泄露进训练流程。这类问题在几百条的小数据集上特别隐蔽——样本少测试集对统计量的影响更明显虚高幅度更大。解决严格按切分 → 训练集 fit → 训练集 transform 测试集 transform的顺序执行。发现脚本是先填充再切分时重排流程准确率掉 5 到 8 个百分点是正常的那个偏低的值才是真实水平。我那一次排查浪费了半天最后把两步换了个顺序精度从 0.92 变成 0.85才意识到前面是数据泄漏。4.3 Isolation Forest 的 contamination 参数拍脑袋设异常比例完全跑偏现象5-Suspicious client IDs PCA dh Kmean dh IsoForest.py跑出来十几条可疑客户人工复核发现里面有大量正常客户或者反过来一个异常都没抓到。原因contamination 默认值是 0.1意思是我预期数据里 10% 是异常。而信用卡申请欺诈的实际比例远低于 1%0.1 会强行把一堆正常样本标成离群点。这个问题在绝大多数异常检测脚本里都存在因为 contamination 是唯一告诉模型数据脏到什么程度的入口。解决先算一下数据里已知欺诈比例如果有标签就直接统计没标签就先用 KMeans 或直方图看一眼数据分布把值压到 0.01~0.05 之间再跑。宁可少抓也不要误伤一大片——误报带来的业务成本比漏报更高因为人工复核会淹没在假警报里。4.4 类别不平衡时只看准确率模型实际是全拒答现象准确率 0.86精细看预测结果全是拒绝通过的那几笔一笔都没预测出来。原因数据里拒绝样本占大头时模型学会的捷径是全部输出多数类。逻辑回归默认分类阈值 0.5 在这种分布下也会加剧问题——少数类的预测概率很难超过 0.5永远被分到拒绝侧。解决输出混淆矩阵和分类报告重点看少数类的 recall 和 f1-score而不是 accuracy。必要时用 3.3 节的class_weightbalanced加权或者把判定阈值从 0.5 下调到 0.3 再验证。调阈值本身不需要重训模型直接用predict_proba的概率做比较即可这个操作在真实项目里非常常用。4.5 plotly 在无图形界面环境下黑屏不出图现象脚本在服务器上跑px.histogram(...).show()直接不显示也不报错进程就挂在那里。原因plotly 的show()默认调用浏览器渲染服务器没有图形界面就没有出口。这不是代码逻辑问题是运行环境问题很多人在远程 Linux 上跑这类脚本时都会遇到。解决改用fig.write_html(distribution.html)把图写入文件本地下载后用浏览器看或者直接用 matplotlib 的Agg后端保存 PNG。我在服务器上处理这类项目时会把所有出图点都改成写文件省得来回折腾。这个习惯后来帮我避开了很多次代码在本地好使、一上服务器就哑火的尴尬。5. 进阶用 Top-K 命中率验证异常检测再把五条脚本串成一条可复查的流水线5.1 异常检测的验证跟分类不一样不能只看混淆矩阵欺诈检测脚本输出的是可疑客户 ID 列表它没有硬标签所以不能像分类任务那样直接套用混淆矩阵。常见做法是把模型输出的异常得分降序排列取前 K 个然后人工复核这 K 个客户里有多少是真的问题账户这个比例就是 Top-K 命中率。# 异常得分降序取前 20 个客户供人工复核 from sklearn.ensemble import IsolationForest iso IsolationForest(contamination0.03, random_state42) iso.fit(X_scaled) scores iso.score_samples(X_scaled) # 越高越正常越低越可疑 anomaly_rank np.argsort(scores)[:20] # 得分最低的 20 个客户 print(top 20 suspicious indices:, anomaly_rank) print(their scores:, scores[anomaly_rank])逻辑说明score_samples返回每个样本的异常得分值越低越可疑取前 20 是为了给业务方做人工复核而不是直接当成定罪证据。Top-K 命中率在真实欺诈场景里比 AUC 更贴近使用方式——业务只关心你给的名单准不准不关心概率曲线下面积有多大。参数说明contamination0.03表示预期 3% 的异常比例这个值必须按业务调原理见 4.3 节random_state42固定结果方便下次复跑对比名单是否变化。如果两次跑出的名单差异很大说明数据或参数不稳优先检查特征编码和缺失值填充是否一致。5.2 把五个脚本串成一条流水线先立项再动手结果才可复查这个包的五个脚本其实是三个独立任务基线对比、审批预测、异常识别。我一般拿到这类项目会先给数据做一次全面体检然后定一个主指标——审批线看测试集 AUC欺诈线看 Top-K 命中率。接下来所有调参都围绕主指标进行不被训练集准确率带偏。每个脚本开头固定random_state结尾输出可保存的结果文件预测结果、系数表、可疑客户名单各存一份这样第二天回来能清楚知道上一次跑出了什么、改了什么参数。这是我反复踩坑换来的习惯。早先我拿到类似数据集上来就调参调完发现不知道哪个版本的结果是准的只能全部重跑。从那以后我每次做这类项目都强制走一遍流程先读数据看缺失值和分布再切分固定种子然后标准化最后才进模型。KMeans 聚类数和 Isolation Forest 的 contamination 是两个最需要人工判断的口子别的参数用默认值先跑等主指标不达标再动。这套流程帮我在几个项目里省掉了大量返工时间也希望帮到你。本文还有配套的精品资源点击获取
返回列表