
简介这份资源面向金融风控方向的数据科学从业者与机器学习学习者聚焦信用卡违约预测这一典型分类任务提供一套可运行的完整实现方案。压缩包内共1个文件为单个py脚本整体约4KB体量轻便便于快速阅读与二次修改。脚本预计覆盖数据加载与清洗、特征工程、多模型训练与k折交叉验证并落地bagging、boosting、stacking等模型融合策略最后通过AUC-ROC、精确率、召回率、F1分数等指标评估效果形成从预处理到预测输出的完整链路。已有591人学习下载说明该方案在同类任务中具备一定参考价值。读者可借此理解逻辑回归、随机森林、XGBoost等算法的选型思路掌握融合模型降低过拟合、提升稳定性的实践方法并对照代码梳理金融风控项目的标准流程适合作为课程作业、竞赛基线或业务原型的技术参考。1. 拆开 predict.rar一份能跑通的信用卡违约预测与模型融合实战包信贷风控里有个反直觉的现象单模型 AUC 冲到 0.78 就沾沾自喜上线后逾期召回却一塌糊涂真正卡住业务脖子的往往不是算法不够新而是没把多模型的预测结果用好。这次拆的predict.rar里就一个predict.py配合数据集能完整跑通「数据清洗 → 特征工程 → 多模型训练 → 交叉验证 → 融合输出」这条链路覆盖逻辑回归、随机森林、XGBoost、LightGBM 到 stacking 元学习。它适合两类人刚转金融风控、想找一个能照着复现的端到端脚本的工程师以及手头有信贷数据、想快速验证融合策略是否比单模型更稳的从业者。下面按我实际跑一遍的顺序把参数、坑和调优边界都摊开讲。2. 数据预处理与特征工程predict.py 里最容易被跳过的硬骨头2.1 先看清数据长什么样再动手信用卡违约数据集通常是「一张宽表 一个二分类标签」标签列常见命名是default.payment.next.month或is_default0 代表正常还款1 代表违约。这类数据有几个固定特征样本量几万条、特征二三十列、正负样本比例悬殊违约率常在 20% 上下极端情况到 5%。predict.py开头一般用 pandas 读入我习惯先做一次结构体检再进流程import pandas as pd import numpy as np # 读入原始数据注意编码和分隔符信贷数据常见 csv 或 xls df pd.read_csv(credit_default.csv, encodingutf-8) # 体检形状、标签分布、缺失、类型 print(shape:, df.shape) print(label ratio:\n, df[is_default].value_counts(normalizeTrue)) print(missing:\n, df.isnull().sum()[df.isnull().sum() 0]) print(dtypes:\n, df.dtypes.value_counts()) # 标签列单独拎出来避免后续特征工程误伤 y df[is_default].astype(int) X df.drop(columns[is_default])逻辑说明value_counts(normalizeTrue)直接给出违约占比这个数字决定后面要不要做重采样isnull().sum()只打印有缺失的列避免刷屏。参数上encoding要按实际文件改遇到中文列名乱码就换gbk如果数据是 Excel用pd.read_excel并指定sheet_name。2.2 缺失值、异常值与重复值的三板斧信贷数据的缺失往往有业务含义——比如「近半年查询次数」为空可能代表该客户从未被查询过直接填 0 比填均值更合理。predict.py里常见的处理顺序是先去重再处理异常值最后填缺失。# 1. 去重完全重复的行直接删 df df.drop_duplicates() # 2. 异常值账龄、年龄这类字段用分位数截断避免极端值带偏标准化 for col in [age, credit_limit, bill_amount]: low, high df[col].quantile([0.01, 0.99]) df[col] df[col].clip(low, high) # 3. 缺失值数值列填中位数类别列填众数或单独一档 num_cols df.select_dtypes(include[np.number]).columns cat_cols df.select_dtypes(include[object]).columns df[num_cols] df[num_cols].fillna(df[num_cols].median()) for c in cat_cols: df[c] df[c].fillna(Unknown)逻辑说明quantile([0.01, 0.99])取 1% 和 99% 分位做截断比直接删行温和保留样本量中位数填充对偏态分布比均值稳。参数上分位点可以按业务调整风控里常用 0.005/0.995 更保守。注意如果缺失比例超过 40%填任何值都是噪声我一般直接删列。2.3 特征工程分箱、交互与降维的取舍原始特征直接喂模型不是不行但风控场景里「年龄 25 和 26 的违约风险几乎一样35 和 55 却差很多」所以连续变量分箱WOE 或等频往往比原始值更有效。predict.py里如果没做 WOE至少会做标准化和独热编码。from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline # 数值列标准化类别列独热注意 OneHotEncoder 的 handle_unknown preprocess ColumnTransformer( transformers[ (num, StandardScaler(), num_cols.tolist()), (cat, OneHotEncoder(handle_unknownignore, sparse_outputFalse), cat_cols.tolist()), ], remainderdrop ) # 包成 Pipeline保证交叉验证时预处理只在训练折上 fit避免数据泄漏 pipe Pipeline([(prep, preprocess)]) X_processed pipe.fit_transform(df.drop(columns[is_default]))逻辑说明ColumnTransformer把数值和类别分开处理handle_unknownignore保证测试集出现训练时没见过的类别不会报错。关键点是预处理必须放进 Pipeline否则先对全量数据标准化再交叉验证验证集信息泄漏进训练AUC 会虚高——这是新手最常翻车的地方。降维PCA在这个数据规模上收益有限除非特征上百列否则我一般跳过。3. 多模型训练与交叉验证从逻辑回归到 LightGBM 的参数落点3.1 基模型选型为什么不是越复杂越好predict.py里同时出现逻辑回归、随机森林、XGBoost、LightGBM不是堆砌而是各有分工。逻辑回归可解释性强、训练快适合做基线随机森林对缺失和异常不敏感方差低XGBoost 和 LightGBM 在表格数据上通常精度最高但参数敏感。选型逻辑是先用逻辑回归跑通全流程拿到基线 AUC再用树模型冲精度最后用融合把两者的优势合起来。from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier from xgboost import XGBClassifier from lightgbm import LGBMClassifier models { lr: LogisticRegression(max_iter1000, C1.0, class_weightbalanced), rf: RandomForestClassifier(n_estimators300, max_depth8, class_weightbalanced, n_jobs-1, random_state42), xgb: XGBClassifier(n_estimators400, learning_rate0.05, max_depth5, subsample0.8, colsample_bytree0.8, eval_metricauc, random_state42), lgb: LGBMClassifier(n_estimators400, learning_rate0.05, num_leaves31, subsample0.8, colsample_bytree0.8, random_state42), }参数说明class_weightbalanced让逻辑回归和随机森林自动按类别频率加权缓解样本不均衡XGBoost 的subsample和colsample_bytree控制行、列采样比例0.8 是抗过拟合的常用起点LightGBM 的num_leaves比max_depth更影响复杂度31 是默认值数据量大可调到 63。random_state固定保证结果可复现调参时别改。3.2 K 折交叉验证别只看平均 AUC交叉验证不只是算个平均分更要看每折的方差。如果 5 折 AUC 从 0.72 跳到 0.81说明模型对数据划分敏感融合时权重会不稳。from sklearn.model_selection import StratifiedKFold, cross_val_score skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) for name, model in models.items(): scores cross_val_score(model, X_processed, y, cvskf, scoringroc_auc, n_jobs-1) print(f{name}: AUC{scores.mean():.4f} (/- {scores.std():.4f}))逻辑说明StratifiedKFold保证每折里违约样本比例一致比普通 KFold 更适合不均衡数据scoringroc_auc直接输出 AUC。参数上n_splits5是精度和耗时的平衡点样本少于 1 万可以上 10 折。看结果时标准差超过 0.02 就要警惕可能需要检查是否有泄漏特征或样本量太小。3.3 模型融合Bagging、Boosting 与 Stacking 的实操差别Bagging 和 Boosting 在predict.py里通常体现为随机森林和 XGBoost 本身真正需要手写的是 Stacking——用基模型的输出当新特征再训一个元模型。from sklearn.ensemble import StackingClassifier # 基模型列表元模型用逻辑回归防止元层过拟合 estimators [(name, model) for name, model in models.items()] stack StackingClassifier( estimatorsestimators, final_estimatorLogisticRegression(max_iter1000), cv5, passthroughFalse, # 是否把原始特征也传给元模型 n_jobs-1 ) stack.fit(X_processed, y)逻辑说明StackingClassifier内部对每个基模型做 5 折交叉预测生成 out-of-fold 预测作为元模型输入避免基模型在训练集上的过拟合被元模型学走。passthroughFalse表示元模型只看基模型输出设为 True 会把原始特征拼进去数据量大时容易过拟合我一般保持 False。元模型选逻辑回归而非树模型是因为元层输入维度低等于基模型数量线性模型更稳。4. 避坑与排查跑 predict.py 时最常撞上的五个问题4.1 现象AUC 高得离谱接近 0.99原因预处理在交叉验证之前对全量数据做了 fit验证集信息泄漏或者数据里有「未来信息」特征比如用还款后的状态预测是否违约。解决把所有预处理塞进 Pipeline确保fit只在训练折发生逐列检查特征含义任何在预测时点拿不到的字段一律删掉。4.2 现象LightGBM 报错「Do not support special JSON characters in feature name」原因原始列名带中文、空格或特殊符号LightGBM 对特征名有字符限制。解决在读入后统一重命名用df.columns [ff{i} for i in range(df.shape[1])]或正则替换掉非字母数字字符再进模型。4.3 现象Stacking 训练时间暴涨内存吃满原因基模型数量多、每个都做 5 折交叉预测计算量是单模型的 N×5 倍n_jobs-1又让所有核同时抢内存。解决先单独评估每个基模型的 AUC把明显拖后腿的比如 AUC 低于最好模型 0.03 以上剔出融合列表n_jobs改成 2 或 4给内存留余量。4.4 现象交叉验证每折 AUC 波动大融合后反而比单模型差原因样本量小或正负比例极端某些折里违约样本太少基模型学不稳元模型学到的是噪声。解决改用StratifiedKFold并增加折数到 10或者对训练折做 SMOTE 过采样但注意过采样只能在训练折内做不能碰验证折。4.5 现象预测输出全是 0 或全是 1原因用默认 0.5 阈值切分而违约样本少、模型输出概率整体偏低。解决不要用固定阈值改用验证集上的 F1 或 KS 统计量找最优切点predict_proba拿概率后用np.quantile按业务要求的通过率反推阈值。5. 进阶技巧用 OOF 预测做加权融合与阈值校准Stacking 不是唯一出路实际业务里我更常用「OOF 预测 权重搜索」这套轻量方案因为它可控、可解释出问题能定位到具体基模型。核心思路是先拿到每个基模型在训练集上的 out-of-fold 预测概率再用这些概率去搜一组权重让加权后的 AUC 最大。from sklearn.model_selection import cross_val_predict from scipy.optimize import minimize from sklearn.metrics import roc_auc_score # 1. 收集每个基模型的 OOF 预测概率 oof_preds {} for name, model in models.items(): oof_preds[name] cross_val_predict( model, X_processed, y, cvskf, methodpredict_proba, n_jobs-1 )[:, 1] # 2. 定义目标函数权重非负且和为 1最大化 AUC def neg_auc(weights): weights np.abs(weights) / np.abs(weights).sum() # 归一化 blend sum(w * oof_preds[n] for w, n in zip(weights, oof_preds)) return -roc_auc_score(y, blend) # 3. 从均等权重出发做优化 init np.ones(len(oof_preds)) / len(oof_preds) res minimize(neg_auc, init, methodNelder-Mead) best_w np.abs(res.x) / np.abs(res.x).sum() print(best weights:, dict(zip(oof_preds.keys(), best_w.round(3))))逻辑说明cross_val_predict返回的是每个样本在「没被该折训练过」的模型上的预测天然避免泄漏minimize用 Nelder-Mead 是因为目标函数不可导且维度低基模型数量比梯度法稳。参数上初始权重设均等优化后如果某个模型权重接近 0说明它对融合没贡献可以直接剔除再跑一轮。拿到融合概率后阈值校准是最后一步。风控业务通常关心「在通过率 70% 的前提下能抓住多少违约」所以阈值不是拍脑袋定的# 按业务通过率反推阈值假设只拒绝 30% 的高风险客户 threshold np.quantile(blend_prob, 0.70) pred_label (blend_prob threshold).astype(int) print(threshold:, round(threshold, 4)) print(recall on default:, recall_score(y, pred_label))这里np.quantile(blend_prob, 0.70)表示把预测概率从低到高排取 70% 分位作为切点高于它的 30% 判为违约。参数 0.70 要按实际审批通过率改不是固定值。我一般会画一条 KS 曲线取 KS 最大点作为参考阈值再结合业务通过率微调。从那以后我每次跑融合模型都强制先看 OOF 预测的相关系数矩阵——如果两个基模型相关性超过 0.95留一个就够多留只会增加计算量不涨分。这套流程不复杂但能把「模型融合」从玄学变成可复现的工程步骤。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。