ARTICLE DETAIL

资讯详情

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

Python信贷决策模型实战:从数据清洗到评分卡

Python信贷决策模型实战:从数据清洗到评分卡 简介面向计算机相关专业正在准备毕业设计、课程设计或期末大作业的学生以及需要项目实战练习的学习者该下载内容聚焦中小微企业信贷决策模型与算法研究可支撑从课题立项到论文答辩的全流程。压缩包共23个文件、约2.21MB包含7个源码文件、5个Excel数据表、4个CSV数据集、2个MAT数据文件、论文PDF、中英文答辩备忘Markdown及答辩演示文稿源码与数据层次分明便于按模块对照学习附录对应关系能帮助快速定位实验章节。目前已有90人学习下载。资料中既有完整论文与演示文稿也涵盖信誉评级预测、突发制裁等场景的实验数据代码可运行、数据可替换适合作为高分毕设参考模板或在此基础上快速扩展为实际项目能够明显压缩算法设计和论文写作的前期耗时。1. Python毕业设计做中小微企业信贷决策比你想的更值钱每年毕业季都有人拿着“信贷决策模型”当题目但真正能做出高分项目的没几个。原因不是模型难而是大多数人都卡在了“业务理解”和“数据清洗”上最后只会调一个 sklearn 包就交差。这篇笔记要说的是用 Python 从零把中小微企业信贷决策模型做成一个可复现、可解释、答辩不怕问的毕业设计项目附源码和论文怎么写。它解决的是“银行该不该给一家小微企业放贷、放多少、利率怎么定”这个真实风控问题适合计算机、金融、大数据方向的学生也适合想快速搭一个风控 Demo 的从业者。一个反直觉的结论是答辩老师更看重你对算法取舍和业务边界的理解而不是模型是否用了深度学习。2. 信贷决策模型的算法选型为什么不是模型越新越好2.1 把业务问题翻译成二分类任务中小微企业信贷决策本质上是一个“是否违约”的二分类问题。你需要拿到一批企业的历史信贷记录包括企业的财务报表、贷款申请信息、经营状况、还款表现等然后定义一个标签在未来一定时间窗口内比如一年是否发生逾期或拖欠。这个标签的定义决定了整个项目的方向。常见做法是取某个月份作为观察点观察点之前的企业特征作为模型输入观察点之后 6~12 个月的逾期状态作为 y。比如一家企业在 2022 年 6 月申请贷款你用它在 2022 年 6 月及之前的经营数据来预测它在 2022 年 7 月到 2023 年 6 月会不会违约。注意这个时间窗口的划分不能乱来否则很容易引入“未来信息”也就是数据泄漏。建模时把有违约记录的企业标为 1正常还款标为 0。输出不是简单给一个 0 或 1而是给一个违约概率 p。银行根据 p 决定是否放贷或者放贷多少。这就是“决策”的含义。你论文里要写清楚目标变量是什么观察期是多长表现期是多长以及为什么这么切分。2.2 主流算法的对比逻辑回归、随机森林、XGBoost、LightGBM做这个题目算法不是越花哨越好。我把常见的选择整理成了一张对比表你可以直接抄到论文的算法选型章节里。算法可解释性非线性能力小样本表现调参难度论文友好度逻辑回归极强弱靠特征工程补好低非常高随机森林中强较好低高XGBoost弱强中高高LightGBM弱强中高高深度学习 / DNN极弱强差极高低逻辑回归是评分卡的基础每个系数可以直接换算成评分加减分银行风控最喜欢这种“白盒”模型。随机森林能处理非线性关系而且几乎不需要做特征缩放。XGBoost 和 LightGBM 在多数竞赛里刷分更猛但你在论文里要解释清楚超参数的意义否则答辩容易翻车。深度学习在这个任务里没有优势——小微企业信贷样本量通常只有几千到几万条企业财务特征多为表格型数据DNN 容易过拟合而且可解释性差答辩时很难讲明白。我的建议是用逻辑回归做基线XGBoost 做提升再用随机森林做对照。三个模型横向对比本身就构成论文里的实验亮点。2.3 别把不相关的算法硬塞进项目里我在评阅同学项目时经常看到有人把聚类算法、kmp算法、照片修复模型之类的东西往信贷决策里塞理由是“想体现算法多样性”。这属于典型的为了凑算法而堆技术。信贷决策是一个监督学习任务聚类是无监督kmp 是字符串匹配照片修复跟金融风控毫无关系。你硬塞进去答辩老师一问“这个模型解决了信贷决策里的什么问题”你很难自圆其说。如果你确实想增加算法维度可以加一个“基于聚类的小微企业客群分层”用 KMeans 把企业分成不同风险偏好群体再在每个群体内单独建模。这个思路是合理的因为它对应了银行精细化管理客群的实际需求。但注意聚类只能作为辅助不能替代主模型。至于深度学习算法可以在论文里用一小节对比说明“为什么不采用”这反而能体现你的思考深度。3. 数据准备与特征工程小微企业的数据比算法更难搞3.1 数据从哪里来公开数据集与自造数据这个题目的数据来源有三个常见路子一是公开数据集比如 UCI 的 German Credit、Kaggle 上的 Give Me Some Credit、Lending Club 的贷款数据二是和导师合作拿脱敏后的企业数据三是自己用 Python 生成模拟数据。对于毕业设计前两种更受认可因为有真实业务背景论文里的数据说明也更好写。我自己做的时候用的是 Kaggle 上的 Give Me Some Credit它带有企业主个人特征和信贷表现虽然偏个人信贷但特征工程的思路完全一致。如果你只能获得一个二分类数据集也很容易迁移到“小微企业”语境下只要把特征列名改成“企业成立年限”“月均营业收入”“资产负债率”“行业类别”“历史逾期次数”等就是一个小微企业信贷数据集了。注意数据集的划分要提前固定下来。不要用整个数据集做特征选择后再划分训练集和测试集那会造成数据泄漏。正确的顺序是先切分再做特征工程而且特征工程的统计量只能从训练集计算。3.2 缺失值清洗与异常值处理的工程步骤拿到数据第一步是清洗。我用一段最小代码带你跑通这个过程import pandas as pd import numpy as np # 读取数据low_memoryFalse 避免大文件推断类型导致警告 df pd.read_csv(credit_data.csv, low_memoryFalse) # 缺失率统计决定删除还是填充 missing_rate df.isnull().mean().sort_values(ascendingFalse) print(missing_rate[missing_rate 0.01]) # 删除缺失率超过 50% 的列 drop_cols missing_rate[missing_rate 0.5].index df.drop(columnsdrop_cols, inplaceTrue) # 对数值型特征用中位数填充对类别型特征用众数填充 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()) df[cat_cols] df[cat_cols].fillna(df[cat_cols].mode().iloc[0]) # 简单处理异常值把营收、资产等正向指标小于 0 的值替换为该指标的中位数 amount_cols [revenue, total_assets, loan_amount] for col in amount_cols: if col in df.columns: df.loc[df[col] 0, col] df[col].median() print(df.shape)这段代码做了三件事统计缺失率、清洗缺失值、处理明显异常。其中fillna(df[col].median())对偏态分布更稳健比均值填充好mode().iloc[0]取众数填充类别型变量。为什么用中位数而不是均值因为企业营收、资产这类指标通常长尾严重个别大企业会把均值拉高中位数能代表大多数中小企业的水平。如果你拿到的时间跨度数据想提取趋势特征可以考虑用滑动窗口滤波模型平滑营收曲线不过多数毕业设计用截面数据就够了不需要做时序。3.3 WOE编码与分箱让逻辑回归拥有非线性能力逻辑回归本身是线性模型但通过 WOEWeight of Evidence编码可以让它具备非线性表达能力这也是银行评分卡的标准做法。分箱的基本思路是把连续变量切成几段计算每一段的违约占比和正常占比再算出 WOE 值和 IVInformation Value值。IV 值越大说明该变量区分好坏客户的能力越强。下面是一个常用的分箱 WOE 编码函数你可以在项目里直接复用def woe_iv_calculation(df, feature, target): 对有监督的等频分箱计算 WOE 和 IV df: DataFramefeature: 特征列名target: 目标列名(0/1) 返回一个 DataFrame包含每个分箱的样本数、违约率、WOE、IV df df.copy() # 等频分箱把特征分成 5 箱 df[bin] pd.qcut(df[feature], 5, duplicatesdrop) grp df.groupby(bin)[target].agg([sum, count]) grp.columns [bad, total] grp[good] grp[total] - grp[bad] grp[bad_rate] grp[bad] / grp[bad].sum() grp[good_rate] grp[good] / grp[good].sum() # 避免除零给 good_rate 或 bad_rate 为 0 的箱一个极小值 grp[woe] np.log(grp[bad_rate] / grp[good_rate].replace(0, 0.0001)) # IV 计算求和 grp[iv] (grp[bad_rate] - grp[good_rate]) * grp[woe] total_iv grp[iv].sum() print(f特征 {feature} 的 IV {total_iv:.4f}) return grp, total_iv # 示例用法 # woe_df, iv woe_iv_calculation(df, revenue, default_flag)pd.qcut按分位数切分比pd.cut等宽分箱更稳健因为企业收入分布很偏等宽分箱会导致某些箱里样本极少。duplicatesdrop会自动合并重复的边界值避免报错。grp[bad_rate] / grp[good_rate]取对数得到 WOE正数表示该箱的违约占比高于正常占比负数则相反。IV 值大于 0.3 就认为是有力的预测变量小于 0.02 可以放弃。注意WOE 编码只能在训练集上计算分箱边界然后把这个映射关系应用到测试集否则测试集的分布会提前泄露到模型里。我一般会把分箱边界保存成一个 JSON 文件后续预测时加载同一个映射。4. 核心模型实现与调参从默认参数到交叉验证4.1 训练第一个逻辑回归基线模型当你把特征清洗完、做成了 WOE 数值化后就可以开始建模了。第一个模型永远建议用逻辑回归它是最稳定的基线。一段最小可跑的代码如下from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.metrics import roc_auc_score, classification_report # X 为特征矩阵y 为标签列已经做过数据清洗和 WOE 编码 X df_woe.drop(default_flag, axis1) y df_woe[default_flag] # stratify 保证训练/测试集正负样本比例一致 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) model LogisticRegression( C1.0, class_weightbalanced, max_iter1000, solverliblinear, random_state42 ) model.fit(X_train, y_train) y_pred_prob model.predict_proba(X_test)[:, 1] print(AUC:, roc_auc_score(y_test, y_pred_prob)) print(classification_report(y_test, model.predict(X_test)))class_weightbalanced是为了对抗正负样本不平衡权重自动按反比调整。solverliblinear适合中小样本也支持平衡权重。C1.0是正则化强度的倒数C 越小正则化越强默认 1 可以先跑通。max_iter1000是因为用 liblinear 时如果样本量稍大默认 100 次可能不收敛。输出里roc_auc_score是判断模型排序能力的最核心指标毕业论文里也主要用 AUC 和 KS。注意这时的特征已经是 WOE 编码后的数值逻辑回归的系数可以直接解释系数为正说明该特征 WOE 高时违约概率上升。这就是评分卡模型可解释性的来源。4.2 用 XGBoost 做额度调整与风险排序逻辑回归做基线后你可以用 XGBoost 进一步提升排名能力。XGBoost 对表格数据的效果通常优于逻辑回归但它像个黑匣子需要小心调参。一个可复现的训练模板如下import xgboost as xgb from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) model_xgb xgb.XGBClassifier( n_estimators200, max_depth3, learning_rate0.05, subsample0.8, colsample_bytree0.8, scale_pos_weight(y 0).sum() / (y 1).sum(), eval_metricauc, random_state42 ) model_xgb.fit( X_train, y_train, eval_set[(X_test, y_test)], early_stopping_rounds20, verboseFalse ) y_pred model_xgb.predict_proba(X_test)[:, 1] print(XGBoost AUC:, roc_auc_score(y_test, y_pred))关键参数说明n_estimators200是树的棵数配合learning_rate0.05小学习率效果更好不要一上来就拉满 1000 棵。max_depth3控制单棵树复杂度信贷表格数据树太深容易过拟合。scale_pos_weight用负样本数除以正样本数达到处理不平衡的效果这里我直接按比例算出来了。early_stopping_rounds20在测试集上早停防止训练过头但你论文里要注意用测试集做早停等于把测试集泄露给模型严格做法是再切一个验证集。不过毕业设计做到这个程度答辩老师通常认可。你可以在论文里写逻辑回归 AUC 0.78XGBoost AUC 0.82说明非线性模型在小微企业信贷变量间找到了更复杂的交互关系。这个对比就很有说服力。4.3 用交叉验证防止调参翻车我见过太多人直接在测试集上调参最后测试集分数很高但换了数据就废。正确姿势是用交叉验证。下面是一段用 GridSearchCV 搜索逻辑回归正则化强度的代码from sklearn.model_selection import GridSearchCV param_grid { C: [0.01, 0.1, 1, 10, 100], class_weight: [None, balanced] } gs GridSearchCV( LogisticRegression(max_iter1000, solverliblinear, random_state42), param_grid, cv5, scoringroc_auc, n_jobs-1, verbose1 ) gs.fit(X_train, y_train) print(best params:, gs.best_params_) print(best cv AUC:, gs.best_score_)cv5表示 5 折交叉验证scoringroc_auc是选择模型的标准因为准确率在类别不平衡时失效。n_jobs-1用所有 CPU 并行能省一半时间。你要注意交叉验证的分数会略低于你单独在测试集上测得的分数这是正常的因为测试集会参与早停或调参存在乐观偏差。论文里建议把“模型最终在测试集上的 AUC”放在表格中同时在脚注说明测试集只用于最终评估。4.4 环境与源码运行先保证能跑起来很多同学拿到源码后第一件事就是 pip install 一堆包然后报错。我的习惯是用一个requirements.txt锁住版本。信贷决策项目需要的核心包是 pandas、numpy、scikit-learn、xgboost如果是论文画图再加 matplotlib。没有特殊需求不要装最新版 XGBoost有时会和旧版 numpy 冲突。pip install -r requirements.txt之前先确认 Python 版本在 3.8~3.11 之间深度学习包在这里不是必需品能省则省。如果实在缺环境网上很多免费 python 源码大全里都有环境配置模板但注意不要照抄别人项目的文件结构最好自己按“data/”“features/”“models/”“notebooks/”这样的目录重新组织。这本身也是论文里“系统设计”的一部分。5. 信贷决策模型避坑指南数据泄漏、样本不平衡、论文查重5.1 数据泄漏看似很准的模型其实是纸糊的现象测试集 AUC 高达 0.98你觉得自己做出了一个完美模型但答辩时老师随口一问“你怎么处理企业未来一年的营收数据”你发现特征里包含了观察期之后的信息。现场直接沉默。原因数据泄漏的本质是特征里混进了“未来变量”或“标签本身的先验信息”。比如直接用“是否已逾期”作为特征或者用全量数据做统计编码再切分训练集都会导致模型“作弊”。解决严格按时间切分数据观察点之前的信息才能进特征。如果做 WOE 分箱或缺失值填充一律只允许在训练集上计算统计量再映射到测试集。我在 3.3 节里强调的分箱边界保存就是这么用的。论文里一定要画一张“数据划分时间轴”的图让老师一眼看到你没有偷看未来。5.2 样本不平衡违约样本只有5%现象训练出的模型把所有企业都预测为“不违约”准确率高达 95%但违约样本一个都没抓出来AUC 只有 0.5。答辩时老师一看分类报告就发现问题。原因信贷数据天然不平衡违约比例通常在 3%~10%。模型为了降低整体损失倾向于把样本归入多数类。准确率在此场景完全失去参考价值。解决第一评估指标改用 AUC、KS、召回率、F1-score不要只看 accuracy。第二在逻辑回归中设置class_weightbalanced在 XGBoost 中设置scale_pos_weight如 4.1 和 4.2 的代码所示。第三也可以尝试 SMOTE 过采样但要注意只在训练集上生成合成样本测试集保持原始分布。我在一个项目里试过 SMOTE随机森林AUC 从 0.72 提升到 0.76但调参不当会导致过采样后的模型在真实数据上表现变差所以论文里尽量强调“代价敏感学习”而非“盲目过采样”。5.3 特征选择不当导致模型“玄学”现象你往模型里加了 50 个特征结果 AUC 反而比只用 10 个特征时还低。有些特征单独看 IV 值很高但和其他特征强相关导致模型系数不稳定。原因一方面是小微企业数据里不少财务指标高度相关比如营收和成本多重共线性会影响逻辑回归稳定性另一方面是弱相关特征带来的噪声淹没了信号。解决先用 IV 值排序保留 IV 0.02 的特征。再用相关性矩阵去除相关性高于 0.75 的冗余特征。最后在 XGBoost 上观察特征重要性把重要性为 0 的特征删掉。这是一个简单的筛选代码# 计算特征间的相关系数矩阵 corr X_train.corr() # 找出相关性高于 0.75 的特征对 high_corr_pairs [ (col1, col2, abs(corr.loc[col1, col2])) for col1 in corr.columns for col2 in corr.columns if col1 col2 and abs(corr.loc[col1, col2]) 0.75 ] print(high correlation pairs:, high_corr_pairs[:10])注意这个筛选过程只能在训练集上进行否则又是一种数据泄漏。做完筛选后模型参数才稳定论文里也能给出“特征选择前/后 AUC 对比表”这个加分项。5.4 论文查重与代码评审怎么把“搬来的源码”变成自己的现象论文查重率超过 30%系统标红一片“信贷风险影响因素分析”。源码里核心函数和 GitHub 上某个开源项目一模一样老师一眼看出来直接判定不通过。原因很多毕业设计项目源码都是从免费 python 源码大全或其他开源仓库直接下载的论文也有大量参考他人文献。如果不加改造查重和人工检查都过不去。解决不是让你从零造轮子而是在理解的基础上重构。把数据清洗函数重写改变量名调整分箱策略把模型训练改成交叉验证网格搜索新增一个对比模型在论文里对所有参考的开源项目明确标注来源并用自己的实验数据重新验证。最稳妥的方式是把“重新实现并改进”写进论文的创新点比如“在原评分卡模型基础上引入 XGBoost 增量学习”这样即使是复现场景也是有意义的工程贡献。6. 从模型到论文验证指标、图表生成与答辩演示到这一步模型已经能跑坑也排得差不多剩下就是把它变成一篇高分论文。很多同学重代码轻论文最后被导师批“工作量不够”。其实信贷决策这个题目的工作量很好展示一张完整的特征工程流程图、一张数据划分时间轴、一张三个模型 AUC 对比表再加一张 KS 曲线图就能撑起核心篇幅。KS 曲线的生成建议用这一段代码它比单纯画 ROC 更贴合信贷场景import matplotlib.pyplot as plt from sklearn.metrics import roc_curve def plot_ks(y_true, y_pred_prob, model_name): # 计算累计好、坏样本分布 df pd.DataFrame({label: y_true, prob: y_pred_prob}) df df.sort_values(prob, ascendingFalse).reset_index(dropTrue) df[good_cum] (df[label] 0).cumsum() / (df[label] 0).sum() df[bad_cum] (df[label] 1).cumsum() / (df[label] 1).sum() ks abs(df[good_cum] - df[bad_cum]).max() plt.figure(figsize(6, 4)) plt.plot(df[bad_cum], labelGood cumulative) plt.plot(df[good_cum], labelBad cumulative) plt.axvline(df.index[abs(df[good_cum] - df[bad_cum]).idxmax()], colorred, linestyle--, labelfKS{ks:.2%}) plt.title(f{model_name} KS curve) plt.legend() plt.show() # 调用方式 # plot_ks(y_test, y_pred_prob, XGBoost)代码逻辑很简单把样本按预测概率降序排列随着阈值下移计算前 N% 样本中好坏客户的累计占比两曲线最大垂直距离就是 KS。KS 越高说明模型区分好坏客户的能力越强银行通常要求 KS 0.3。这段图放论文里比单纯写“AUC 0.82”要丰满得多。答辩演示时我有个血泪教训只贴了 AUC被老师追问“你的模型怎么支持银行定价”结果答不上来。后来我把输出解释成了“风险评分 基准分 各特征分项之和”并给出一段把违约概率映射为 300~900 分评分的函数。这个加分项其实就是用逻辑回归的系数做线性求和已包含在 WOE 编码后的逻辑回归模型里。你只需在论文的模型应用一节说明分数越高风险越低银行可以按分数区间设置差异化额度与利率。最后再提醒一句源码一定要整理成结构化目录主程序能一键运行生成所有结果图片。答辩前把数据泄漏、样本不平衡、模型可解释性三个问题提前自问一遍。我在第一次做这个方向时就是因为没注意数据切分被老师一句话问得翻车。后来重新画时间轴、严格按训练集统计量做特征工程才把项目救回来。希望这篇笔记能让你少走这段弯路顺顺利利毕业。本文还有配套的精品资源点击获取
返回列表