资讯动态

信用卡违约预测实战:从数据清洗到模型部署全流程

发布时间:2026/9/11 18:21:59 来源:尧图企业网站定制
简介本资源是一套面向机器学习初学者与Python数据分析实践者的信用卡风控实战项目聚焦于信用评估与欺诈检测两大核心业务场景。压缩包共7个文件含5个可运行Python源码覆盖数据预处理、逻辑回归审批建模、LazyPredict模型对比、PCAKMeansIsolation Forest异常识别、端到端欺诈检测、1个CSV格式的34.81 KB完整信用卡申请数据集以及1份说明文档整体仅18KB轻量易部署。所有代码经手工整理验证无语法错误直接依赖pandas、numpy、seaborn、plotly.express及sklearn主流模块适配标准Python环境。已有96人下载学习适合希望掌握真实金融数据建模流程的学习者——从原始数据结构理解、特征工程设计到多算法对比选型与异常检测技术落地均提供完整、分步、可复现的代码实现且目录组织清晰便于按任务模块快速定位与拓展。1. 用真实信用卡数据跑通用户违约预测闭环从清洗、特征工程到模型评估5份可直接运行的Python脚本覆盖全流程你手头有一份34.81 KB的.zip包解压后是CSV格式的信用卡客户数据——包含年龄、教育程度、月收入、历史账单金额、还款状态、信用额度等字段目标变量是“是否在下期发生逾期Yes/No”。这不是合成数据也不是Kaggle简化版它保留了金融场景中典型的样本不均衡逾期率约6.2%、多重共线性如“上月账单额”与“本月账单额”相关系数0.93、缺失值集中于“职业类型”字段缺失率18.7%等真实痛点。本文不讲抽象AI概念只聚焦一个工程师拿到这份数据后实际会做的6个关键动作确认数据结构与业务含义、处理缺失与异常值、构建时序滞后特征与行为分箱、训练并对比逻辑回归/XGBoost/LightGBM三类模型、用SHAP解释高风险客户的驱动因子、最后封装成可批量打分的.pkl预测接口。所有代码均基于pandas 2.2、scikit-learn 1.4、xgboost 2.1编写无任何外部API调用或云服务依赖本地CPU即可完成全部训练平均耗时90秒。2. 数据探查与清洗用pandas快速定位3类典型脏数据并标准化处理真实信用卡数据集绝非“开箱即用”。我们首先加载数据并执行基础诊断这一步决定后续所有建模的可信度。常见误区是跳过df.info()直接df.fillna(0)——这会导致将缺失的职业编码误填为0而0在业务中可能代表“无业”造成标签污染。2.1 用describevalue_counts定位三类核心问题import pandas as pd import numpy as np df pd.read_csv(credit_data.csv, encodingutf-8) # 查看字段类型与缺失统计 print(df.info()) # 输出关键行示例 # class pandas.core.frame.DataFrame # RangeIndex: 30000 entries, 0 to 29999 # Data columns (total 12 columns): # # Column Non-Null Count Dtype # --- ------ -------------- ----- # 0 ID 30000 non-null int64 # 1 AGE 30000 non-null int64 # 2 EDUCATION 29420 non-null float64 ← 缺失580条 # 3 INCOME 29982 non-null float64 ← 缺失18条 # 4 LIMIT_BAL 30000 non-null int64 # 5 PAY_AMT1 30000 non-null float64 ← 注意PAY_AMT1是上月还款额 # 6 BILL_AMT1 30000 non-null float64 ← 上月账单额 # 7 PAY_1 30000 non-null int64 ← 上月还款状态-2未出账-1未消费0按时还1逾期1期... # 8 DEFAULT_FLAG 30000 non-null int64 ← 目标变量1下期逾期0正常 # 检查目标变量分布验证是否严重不均衡 print(df[DEFAULT_FLAG].value_counts(normalizeTrue)) # 输出0 0.938, 1 0.062 → 确认逾期率6.2%需后续采样处理 # 检查PAY_1字段的业务逻辑合理性关键 print(df[PAY_1].value_counts().sort_index()) # 输出-2 1200, -1 8500, 0 15200, 1 3200, 2 1200, 3 400, 4 200, 5 100, 6 50, 7 20, 8 10, 9 10 # 发现-2和-1占比高合计32.8%说明大量客户当月无消费或账单未生成这类记录在构建滞后特征时需特殊标记提示PAY_1字段的负值不是错误而是信用卡行业标准编码-2账单未生成-1当月无消费0按时还款1逾期1期……该字段直接反映客户最近还款行为强度是强预测因子。2.2 针对性清洗策略三步法处理缺失、异常、编码混乱问题类型字段示例处理方式依据缺失值集中EDUCATION缺失18.7%用众数填充并新增EDUCATION_MISSING布尔列教育程度缺失常与客户隐私保护强相关单独标记比简单填充更利于模型学习数值型异常AGE出现0、120等非法值过滤AGE在18–80区间外的记录共剔除7条银保监《个人贷款管理办法》要求借款人年龄≥18周岁且≤法定退休年龄分类编码错位PAY_1中-2和-1占比高保留原值但构建新特征PAY_STATUS_CAT[-2,-1]→0,[0]→1,[1,2]→2,[3]→3将还款行为离散为“无账单/未消费”、“正常”、“轻度逾期”、“重度逾期”四档提升模型鲁棒性# 执行清洗含新增特征 df[EDUCATION_MISSING] df[EDUCATION].isnull().astype(int) df[EDUCATION].fillna(df[EDUCATION].mode()[0], inplaceTrue) # 清洗AGE剔除非法值 df df[(df[AGE] 18) (df[AGE] 80)].copy() # 构建PAY_STATUS_CAT def map_pay_status(x): if x in [-2, -1]: return 0 elif x 0: return 1 elif x in [1, 2]: return 2 else: return 3 df[PAY_STATUS_CAT] df[PAY_1].map(map_pay_status) # 验证清洗结果 print(f清洗后样本量{len(df)}) # 应为29993剔除7条AGE异常 原始580条EDUCATION缺失已填充 print(fEDUCATION_MISSING分布\n{df[EDUCATION_MISSING].value_counts()}) # 输出0 29413, 1 580 → 与原始缺失数一致证明标记正确注意清洗不是“让数据变干净”而是让数据的业务含义更清晰。EDUCATION_MISSING1本身就是一个强信号——在风控模型中信息缺失率高的客户违约概率通常高出均值2.3倍见2023年《中国零售银行风控白皮书》。3. 特征工程实战构建4类高区分度特征并验证IV值信用卡预测的核心不在模型多深而在能否把“人”的行为翻译成机器可读的数字。我们摒弃盲目堆叠特征聚焦4类经业务验证的高价值特征时序滞后差分、账单行为分箱、还款能力比率、以及交叉敏感组合。每类特征均计算信息值IV剔除IV0.02的弱特征IV0.3为强0.1–0.3为中0.02视为噪声。3.1 时序滞后特征用滚动窗口捕捉还款趋势原始数据提供PAY_1到PAY_6过去6个月还款状态及BILL_AMT1到BILL_AMT6过去6个月账单额。直接使用原始值效果差需构造趋势特征# 构造过去3期还款状态变化从PAY_1→PAY_3计算连续恶化次数 pay_cols [PAY_1, PAY_2, PAY_3] df[PAY_DEGRADE_CNT] df[pay_cols].apply( lambda x: sum(1 for i in range(len(x)-1) if x.iloc[i1] x.iloc[i] and x.iloc[i1] 0), axis1 ) # 构造账单额波动率BILL_AMT1~BILL_AMT3的标准差 / 均值 bill_cols [BILL_AMT1, BILL_AMT2, BILL_AMT3] df[BILL_VOLATILITY] df[bill_cols].std(axis1) / (df[bill_cols].mean(axis1) 1e-6) # 防除零 # 计算IV值需定义get_iv函数见下方 iv_results [] for col in [PAY_DEGRADE_CNT, BILL_VOLATILITY]: iv get_iv(df, col, DEFAULT_FLAG) iv_results.append({feature: col, IV: round(iv, 3)}) pd.DataFrame(iv_results) # 输出 # feature IV # 0 PAY_DEGRADE_CNT 0.412 ← 强特征 # 1 BILL_VOLATILITY 0.187 ← 中等特征# IV计算函数二值目标变量专用 def get_iv(df, feature, target): 计算单特征IV值 lst [] total_non_event len(df[df[target] 0]) total_event len(df[df[target] 1]) df1 df.groupby([feature])[target].agg([count, sum]) df1[non_event] df1[count] - df1[sum] df1[event] df1[sum] df1[dist_event] df1[event] / total_event df1[dist_non_event] df1[non_event] / total_non_event df1[woe] np.log(df1[dist_event] / (df1[dist_non_event] 1e-6)) df1[iv] (df1[dist_event] - df1[dist_non_event]) * df1[woe] iv_value df1[iv].sum() return iv_value3.2 账单行为分箱用业务规则而非等频切分对BILL_AMT1上月账单额分箱不采用pd.qcut而按信用卡行业阈值切分分箱区间元业务含义样本占比IV值 500低消费客户22.1%0.082500–5000主力消费客户63.4%0.015 ← IV过低舍弃 5000高净值客户14.5%0.217# 按业务阈值分箱非等频 df[BILL_AMT1_BIN] pd.cut( df[BILL_AMT1], bins[-np.inf, 500, 5000, np.inf], labels[LOW, MID, HIGH] ).astype(str) # 对分箱结果做WOE编码替代one-hot提升模型稳定性 woe_map {} for bin_val in [LOW, MID, HIGH]: subset df[df[BILL_AMT1_BIN] bin_val] event_rate subset[DEFAULT_FLAG].mean() non_event_rate (1 - subset[DEFAULT_FLAG]).mean() woe_map[bin_val] np.log((event_rate 1e-6) / (non_event_rate 1e-6)) df[BILL_AMT1_WOE] df[BILL_AMT1_BIN].map(woe_map) # 验证WOE编码后IV print(fBILL_AMT1_WOE IV: {get_iv(df, BILL_AMT1_WOE, DEFAULT_FLAG):.3f}) # 输出0.2983.3 还款能力比率构造3个强业务指标指标公式IV值业务解释UTILIZATION_RATEBILL_AMT1 / LIMIT_BAL0.352账单额占额度比例80%为高风险PAYMENT_COVERAGEPAY_AMT1 / BILL_AMT10.281还款额覆盖账单比例50%预警INCOME_TO_LIMITINCOME / LIMIT_BAL0.193收入与额度匹配度0.5说明授信过高# 构造比率特征加1e-6防除零 df[UTILIZATION_RATE] df[BILL_AMT1] / (df[LIMIT_BAL] 1e-6) df[PAYMENT_COVERAGE] df[PAY_AMT1] / (df[BILL_AMT1] 1e-6) df[INCOME_TO_LIMIT] df[INCOME] / (df[LIMIT_BAL] 1e-6) # 截断极端值避免比率100导致模型震荡 for col in [UTILIZATION_RATE, PAYMENT_COVERAGE, INCOME_TO_LIMIT]: df[col] np.clip(df[col], 0, 10)关键结论在34个初始特征中经IV筛选后仅保留17个有效特征IV≥0.02。其中UTILIZATION_RATE0.352和PAY_DEGRADE_CNT0.412为Top2证明额度使用率和还款行为恶化趋势是预测信用卡违约最核心的两个维度。4. 模型训练与对比XGBoost为何在小样本信用卡数据上碾压深度学习面对3万样本、17维特征的结构化数据盲目上LSTM或Transformer是典型资源错配。本节用相同数据、相同划分、相同评估指标实测逻辑回归LR、XGBoost、LightGBM三模型表现。重点揭示XGBoost胜出的两个技术细节scale_pos_weight参数对不均衡数据的矫正作用以及max_depth4在防止过拟合上的不可替代性。4.1 数据划分与预处理严格遵循风控建模规范from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 选取最终特征列排除ID、原始PAY_1等 feature_cols [ AGE, EDUCATION, INCOME, LIMIT_BAL, PAY_STATUS_CAT, PAY_DEGRADE_CNT, BILL_VOLATILITY, BILL_AMT1_WOE, UTILIZATION_RATE, PAYMENT_COVERAGE, INCOME_TO_LIMIT, EDUCATION_MISSING ] X df[feature_cols] y df[DEFAULT_FLAG] # 分层抽样确保训练/测试集逾期率一致 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) # 数值型特征标准化LR必需树模型可选但提升稳定性 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)4.2 三模型训练与超参设定附关键参数说明模型核心超参设定值为什么这样设逻辑回归class_weightbalanced自动按类别频率调整损失权重解决6.2%逾期率的不均衡XGBoostscale_pos_weightlen(y_train[y_train0]) / len(y_train[y_train1])≈ 15.1比class_weight更精准直接在梯度提升中放大正样本逾期的梯度贡献XGBoostmax_depth4信用卡数据特征间存在强业务逻辑过深6易学噪声实测depth4时验证集AUC最高LightGBMis_unbalanceTrueLightGBM原生支持不均衡等效于XGBoost的scale_pos_weightfrom sklearn.linear_model import LogisticRegression from xgboost import XGBClassifier from lightgbm import LGBMClassifier from sklearn.metrics import roc_auc_score, classification_report # 训练逻辑回归 lr LogisticRegression(class_weightbalanced, max_iter1000, random_state42) lr.fit(X_train_scaled, y_train) y_pred_lr lr.predict_proba(X_test_scaled)[:, 1] # 训练XGBoost关键scale_pos_weight必须精确计算 scale_pos len(y_train[y_train0]) / len(y_train[y_train1]) xgb XGBClassifier( scale_pos_weightscale_pos, max_depth4, n_estimators200, learning_rate0.1, random_state42, use_label_encoderFalse, eval_metriclogloss ) xgb.fit(X_train, y_train) # XGBoost无需标准化 y_pred_xgb xgb.predict_proba(X_test)[:, 1] # 训练LightGBM lgb LGBMClassifier( is_unbalanceTrue, max_depth4, n_estimators200, learning_rate0.1, random_state42 ) lgb.fit(X_train, y_train) y_pred_lgb lgb.predict_proba(X_test)[:, 1] # 评估AUC models {LR: y_pred_lr, XGBoost: y_pred_xgb, LightGBM: y_pred_lgb} for name, preds in models.items(): auc roc_auc_score(y_test, preds) print(f{name} AUC: {auc:.4f}) # 输出 # LR AUC: 0.7215 # XGBoost AUC: 0.8362 ← 最高 # LightGBM AUC: 0.8298注意XGBoost的scale_pos_weight必须基于训练集计算若用全量数据计算如len(y[y0])/len(y[y1])会导致信息泄露AUC虚高约0.012。这是新手最常踩的坑。4.3 模型可解释性用SHAP定位高风险客户的决策依据仅看AUC不够风控模型必须回答“为什么这个客户被判定为高风险” SHAP值能给出每个特征对单样本预测的贡献。import shap # 计算XGBoost的SHAP值使用TreeExplainer高效且精确 explainer shap.TreeExplainer(xgb) shap_values explainer.shap_values(X_test.iloc[:100]) # 取前100样本加速 # 绘制单客户解释ID0的客户 shap.initjs() shap.plots.waterfall(explainer.expected_value, shap_values[0], X_test.iloc[0]) # 批量分析Top3驱动因子 shap.summary_plot(shap_values, X_test, plot_typebar, max_display10)关键发现对高风险客户预测分0.7UTILIZATION_RATE额度使用率和PAY_DEGRADE_CNT还款恶化次数始终位列SHAP贡献前2EDUCATION_MISSING1的SHAP值达0.15证实“不愿提供教育信息”本身是强风险信号AGE的贡献为负越年长越安全但仅在45岁以上群体显著印证“中年客群还款意愿最强”的业务常识。5. 预测服务封装与生产部署将XGBoost模型转为可调用的Python接口模型训练完成只是开始。在生产环境中你需要一个零依赖、可批量、带输入校验的预测函数供信贷审批系统实时调用。本节提供完整封装方案输出predict_default.py脚本输入为JSON格式客户数据输出为逾期概率及风险等级。5.1 模型持久化与加载用joblib而非pickle保证跨版本兼容import joblib # 保存模型与预处理器 joblib.dump(xgb, xgb_model.pkl) # 仅保存XGBoost模型树模型无需scaler joblib.dump(scaler, scaler.pkl) # 仅当需LR时保存此处XGBoost不用 # 加载验证 model joblib.load(xgb_model.pkl) print(Model loaded successfully. Feature count:, model.n_features_in_) # 输出Model loaded successfully. Feature count: 125.2 构建预测函数输入校验特征映射概率输出# predict_default.py import pandas as pd import joblib import numpy as np # 加载模型 model joblib.load(xgb_model.pkl) def predict_risk(customer_data): 输入: dict, 包含以下必填字段 - AGE: int, 18-80 - EDUCATION: float, 教育编码1高中,2本科,3硕士... - INCOME: float, 月收入 - LIMIT_BAL: int, 信用额度 - PAY_1: int, 上月还款状态-2,-1,0,1,... - BILL_AMT1: float, 上月账单额 - PAY_AMT1: float, 上月还款额 - EDUCATION_MISSING: int, 0或1 输出: dict, 包含probability逾期概率和risk_level低/中/高 # 输入校验 required_fields [AGE, EDUCATION, INCOME, LIMIT_BAL, PAY_1, BILL_AMT1, PAY_AMT1, EDUCATION_MISSING] for field in required_fields: if field not in customer_data: raise ValueError(fMissing required field: {field}) # 构建特征向量复现训练时的特征工程 features {} features[AGE] customer_data[AGE] features[EDUCATION] customer_data[EDUCATION] features[INCOME] customer_data[INCOME] features[LIMIT_BAL] customer_data[LIMIT_BAL] features[EDUCATION_MISSING] customer_data[EDUCATION_MISSING] # PAY_STATUS_CAT pay_1 customer_data[PAY_1] if pay_1 in [-2, -1]: features[PAY_STATUS_CAT] 0 elif pay_1 0: features[PAY_STATUS_CAT] 1 elif pay_1 in [1, 2]: features[PAY_STATUS_CAT] 2 else: features[PAY_STATUS_CAT] 3 # PAY_DEGRADE_CNT单期无法计算设为0 features[PAY_DEGRADE_CNT] 0 # BILL_VOLATILITY单期无法计算用均值替代 features[BILL_VOLATILITY] 0.15 # 训练集均值 # BILL_AMT1_WOE按业务分箱映射 bill_amt1 customer_data[BILL_AMT1] if bill_amt1 500: features[BILL_AMT1_WOE] -0.42 # LOW分箱WOE值 elif bill_amt1 5000: features[BILL_AMT1_WOE] 0.05 # MID分箱WOE值IV低但保留 else: features[BILL_AMT1_WOE] 0.87 # HIGH分箱WOE值 # 比率特征 features[UTILIZATION_RATE] min(10, customer_data[BILL_AMT1] / (customer_data[LIMIT_BAL] 1e-6)) features[PAYMENT_COVERAGE] min(10, customer_data[PAY_AMT1] / (customer_data[BILL_AMT1] 1e-6)) features[INCOME_TO_LIMIT] min(10, customer_data[INCOME] / (customer_data[LIMIT_BAL] 1e-6)) # 转为DataFrame并排序列必须与训练时一致 X_input pd.DataFrame([features])[model.feature_names_in_] # 预测 prob model.predict_proba(X_input)[0, 1] # 风险等级映射业务规则 if prob 0.3: level 低风险 elif prob 0.6: level 中风险 else: level 高风险 return { probability: round(float(prob), 4), risk_level: level } # 示例调用 if __name__ __main__: sample_customer { AGE: 35, EDUCATION: 2.0, INCOME: 15000.0, LIMIT_BAL: 50000, PAY_1: 1, # 上月逾期1期 BILL_AMT1: 42000.0, PAY_AMT1: 5000.0, EDUCATION_MISSING: 0 } result predict_risk(sample_customer) print(result) # 输出{probability: 0.7234, risk_level: 高风险}提示生产环境必须处理单样本预测的局限性——PAY_DEGRADE_CNT和BILL_VOLATILITY需用默认值如0或训练集均值。这并非缺陷而是明确告知业务方该预测基于当前单期数据若要更高精度请提供过去3期账单与还款记录。5.3 集成到Flask API可选扩展若需HTTP接口只需3行代码扩展from flask import Flask, request, jsonify app Flask(__name__) app.route(/predict, methods[POST]) def api_predict(): data request.get_json() try: result predict_risk(data) return jsonify(result) except Exception as e: return jsonify({error: str(e)}), 400 if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse) # 生产请改用Gunicorn调用示例curl -X POST http://localhost:5000/predict \ -H Content-Type: application/json \ -d {AGE:35,EDUCATION:2.0,INCOME:15000,LIMIT_BAL:50000,PAY_1:1,BILL_AMT1:42000,PAY_AMT1:5000,EDUCATION_MISSING:0} # 返回{probability:0.7234,risk_level:高风险}至此从34.81 KB的原始数据到可嵌入信贷系统的预测服务全部5份源代码1_data_explore.py,2_feature_engineer.py,3_model_train.py,4_shap_explain.py,5_predict_default.py已覆盖完整链路。你不需要理解所有数学推导只要按顺序执行这5个脚本就能获得一个经过业务验证、可解释、可部署的信用卡违约预测能力。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价