简介:这份资源是面向计算机相关专业学生与项目实战学习者的贷中风险预测完整项目包,以Python机器学习为核心,围绕金融大数据建模赛题展开,适合用作毕业设计、期末大作业或技能进阶练习。包内共50个文件,涵盖19个py脚本、11个csv数据集、5个ipynb笔记本、4个docx文档、3个xlsx表格及2个pptx答辩演示等,压缩包约10.83MB,源码均经本地编译调试可运行。项目包含赛题方案文档、数据字段说明、特征工程与多模型对比代码,涉及逻辑回归、XGBoost、LightGBM、随机森林、朴素贝叶斯等多种算法实现,并配有答辩PPT与结果输出文件,便于理解完整建模流程。已有62人学习,评审分98分,适合需要参考高分项目结构、学习特征提取与模型调优思路的读者下载使用。
1. 贷中风险预测这套源码,到底能帮你省下多少造轮子的时间
做过信贷风控建模的人都知道,真正难的不是把模型跑起来,而是把散落在申请、客户信息、贷款流水、征信几张表里的字段,拼成一张能进模型的宽表。这套「python基于机器学习的贷中风险预测模型」源码包,解决的正是这个环节——它来自 2020「江苏银行杯」金融大数据建模挑战赛的初赛作品,把贷中风险预测的完整链路拆成了可运行的 Python 脚本和 Notebook。适合两类人:一是正在做机器学习、金融风控方向毕业设计或期末大作业的学生,需要一份结构完整、能跑通、有答辩 PPT 的参考项目;二是想练手特征工程和集成模型的从业者,想看看别人怎么把多表关联、特征衍生、多模型对比串成一条流水线。包里 src 目录下既有 handle_apply.py、handle_credit.py 这类数据清洗脚本,也有 feature_engineering.ipynb 和 models 目录下 lgb.py、_xgb.py、randomforest.py 等一堆模型实现,还有两份答辩 PPT 和赛题文档,属于少见的「代码 + 文档 + 汇报材料」三件套齐全的资源。
2. 先看懂数据结构和建模链路:贷中预测和贷前到底差在哪
2.1 贷中风险预测的业务定位
贷前预测回答的是「这个人该不该放款」,贷中预测回答的是「已经放出去的这笔贷款,未来会不会逾期」。这个区别直接决定了标签和特征窗口的构造方式。贷中场景下,你手里已经有客户的放款记录、还款流水、历史征信查询,特征更多来自「行为」而不是「资质」。这套源码里 label.csv 存标签,apply.txt、customer_information.txt、credit.txt、flow_chart.txt 分别对应申请信息、客户信息、征信、流水四类原始数据,handle_*.py 系列脚本就是把这些 txt/csv 清洗成可用的结构化表。理解这个业务定位,你才知道为什么特征工程里会有那么多基于时间窗口的统计量,而不是简单地把静态属性拼一起。
2.2 从原始表到宽表的处理链路
整个链路可以拆成四步:原始数据读取 → 单表清洗 → 多表关联 → 特征衍生。merge.py、merge_test.py、merge_task.ipynb 负责关联,handle_apply.py、handle_customer_information.py、handle_credit.py、handle_flow_chart.py 负责单表清洗,feature_engineering.ipynb 负责衍生。常见做法是先用 pandas 把每张表按主键去重、缺失填充、类型转换,再以申请表为主表 left join 其余表。下面这段是我按这套脚本的套路还原的单表清洗骨架,你可以对照 handle_apply.py 看它的真实写法:
import pandas as pd import numpy as np # 读取申请信息表,原始为制表符分隔 apply = pd.read_csv('apply.txt', sep='\t', encoding='utf-8') # 主键去重:同一客户多次申请只保留最新一条 apply = apply.sort_values('apply_time').drop_duplicates('cust_id', keep='last') # 缺失值处理:数值型填中位数,类别型填众数 num_cols = apply.select_dtypes(include=[np.number]).columns cat_cols = apply.select_dtypes(include=['object']).columns apply[num_cols] = apply[num_cols].fillna(apply[num_cols].median()) for c in cat_cols: apply[c] = apply[c].fillna(apply[c].mode()[0]) # 类型转换:把申请时间转成标准 datetime,方便后续算账龄 apply['apply_time'] = pd.to_datetime(apply['apply_time'], errors='coerce') print(apply.shape, apply['cust_id'].nunique())逻辑说明:先去重保证一个客户一行,避免关联后行数爆炸;缺失填充用中位数和众数是最稳的兜底策略,比直接 dropna 保留更多样本。参数上,keep='last'表示保留时间最新的一条,如果你的业务是保留首次申请,改成keep='first'。errors='coerce'让非法时间变成 NaT 而不是直接报错,方便后面统一处理。跑完这一步,你应该能看到清洗后的行数和唯一客户数,两者差距大就说明去重逻辑有问题。
2.3 多表关联的主键与粒度对齐
merge.py 的核心是确定关联主键和粒度。申请表、客户信息表一般以 cust_id 为主键,征信和流水表可能一个客户多条记录,直接 join 会导致行数膨胀。正确做法是先对多记录表做聚合(比如按客户算流水笔数、征信查询次数),再关联。这一步是整条链路最容易翻车的地方,粒度没对齐,后面模型指标全是假的。建议在 merge 之后立刻打印df.shape和df['cust_id'].nunique(),确认没有意外膨胀。
3. 特征工程怎么落地:从流水和征信里榨出可用变量
3.1 时间窗口类特征的构造思路
贷中场景最有价值的特征往往带时间属性:近 3 个月查询次数、近 6 个月最大逾期天数、放款至今的账龄。feature_engineering.ipynb 里大量用到这类窗口统计。构造方法通常是先按客户分组,再用 rolling 或条件筛选算聚合值。下面这段演示如何从贷款流水里算「近 N 期还款表现」:
import pandas as pd flow = pd.read_csv('handle_flow_chart.csv') # 统一时间字段 flow['repay_time'] = pd.to_datetime(flow['repay_time'], errors='coerce') # 按客户分组,算历史逾期次数和最大逾期天数 flow_agg = flow.groupby('cust_id').agg( overdue_cnt=('overdue_days', lambda x: (x > 0).sum()), max_overdue=('overdue_days', 'max'), repay_cnt=('repay_time', 'count'), last_repay=('repay_time', 'max') ).reset_index() # 账龄:最近一次还款距今天数 flow_agg['days_since_last'] = (pd.Timestamp('2020-06-30') - flow_agg['last_repay']).dt.days print(flow_agg.describe())逻辑说明:groupby('cust_id')把流水粒度压到客户粒度,这是关联前必须做的。lambda x: (x > 0).sum()统计逾期次数,比直接 sum 更稳,因为逾期天数是连续值。days_since_last用固定基准日算账龄,基准日要选在训练集时间范围内,否则会引入未来信息。参数上,overdue_days字段名要和你实际数据对齐,如果原始字段叫别的名字,改这里即可。跑完看 describe,如果 max_overdue 出现异常大值,先查是不是有脏数据没清。
3.2 类别特征的编码与分箱
apply 和 customer_information 里有大量类别字段,比如职业、学历、婚姻状态。直接 one-hot 维度会炸,常见做法是先用频次编码或目标编码压维度,再对高基数类别做分箱。这套源码里没有用太复杂的编码器,主要是 map 和 cut。我的习惯是:低基数(少于 10 类)用 one-hot,高基数用频次编码,连续变量用等频分箱。分箱时注意边界要基于训练集确定,再应用到测试集,否则测试集的箱边界会泄露信息。
3.3 特征筛选与多重共线性处理
特征不是越多越好。feature_engineering.ipynb 里应该有一版基于 IV 值或树模型重要性的筛选。实操中我会先跑一遍 LightGBM,看 feature_importance,把重要性接近 0 的删掉,再检查两两相关系数超过 0.9 的变量,保留业务含义更清晰的那个。这一步能明显降低过拟合风险,也能让答辩时讲特征逻辑更清楚。注意:筛选要在训练集上做,别拿全量数据筛完再切分,那是典型的泄露。
4. 多模型对比与调参:为什么包里塞了七八个模型脚本
4.1 models 目录下的模型清单与选型逻辑
models 目录里有 dtc.py(决策树)、lgb.py 和 _lgb.py(LightGBM)、_xgb.py(XGBoost)、randomforest.py(随机森林)、AdaBoost.py、_GBDT.py、MNBC.py 和 _GaussianNB.py(朴素贝叶斯)。塞这么多模型不是为了炫技,而是为了在答辩时展示「多模型对比 + 择优」的完整思路。金融风控表格数据里,LightGBM 和 XGBoost 通常是效果最好的,随机森林和 GBDT 作为 baseline,朴素贝叶斯用来做快速验证。选型逻辑是:先用朴素贝叶斯跑通流程验证特征有效性,再用树模型调参冲指标,最后用集成或 stacking 看能不能再涨一点。
4.2 LightGBM 训练脚本的关键参数
以 lgb.py 为例,核心是构造 Dataset、设参数、交叉验证。下面是我按这套脚本风格整理的训练骨架:
import lightgbm as lgb from sklearn.model_selection import StratifiedKFold import pandas as pd import numpy as np train = pd.read_csv('train.csv') test = pd.read_csv('test.csv') features = [c for c in train.columns if c not in ['cust_id', 'label']] # 五折分层交叉验证,保证每折正负样本比例一致 skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) oof = np.zeros(len(train)) pred = np.zeros(len(test)) params = { 'objective': 'binary', # 二分类任务 'metric': 'auc', # 风控常用 AUC 评估 'learning_rate': 0.05, # 学习率,调小更稳但更慢 'num_leaves': 31, # 叶子数,控制模型复杂度 'max_depth': -1, # 不限制深度,靠 num_leaves 控 'feature_fraction': 0.8, # 特征采样,防过拟合 'bagging_fraction': 0.8, 'min_data_in_leaf': 50, # 叶子最小样本,防过拟合关键参数 'verbose': -1 } for fold, (tr_idx, va_idx) in enumerate(skf.split(train[features], train['label'])): tr_x, va_x = train.iloc[tr_idx][features], train.iloc[va_idx][features] tr_y, va_y = train.iloc[tr_idx]['label'], train.iloc[va_idx]['label'] dtrain = lgb.Dataset(tr_x, tr_y) dvalid = lgb.Dataset(va_x, va_y) model = lgb.train(params, dtrain, num_boost_round=1000, valid_sets=[dvalid], callbacks=[lgb.early_stopping(50)]) oof[va_idx] = model.predict(va_x, num_iteration=model.best_iteration) pred += model.predict(test[features], num_iteration=model.best_iteration) / 5 print('OOF AUC:', roc_auc_score(train['label'], oof))逻辑说明:分层交叉验证保证每折正负样本比例和整体一致,风控数据正样本少,不分层会导致某折正样本极少。early_stopping(50)表示验证集指标 50 轮不提升就停,防止过拟合。min_data_in_leaf=50是防过拟合的关键,样本少的时候可以调到 20 到 30。feature_fraction和bagging_fraction都设 0.8,是常用的折中值。跑完看 OOF AUC,如果低于 0.7,先回去查特征工程,别急着调参。
4.3 模型评估指标与阈值选择
风控模型不能只看 AUC。AUC 衡量排序能力,但实际业务要定一个阈值把客户分成通过和拒绝。常见做法是看 KS 值,或者按通过率反推阈值。这套源码的 result.csv 和 result.xlsx 应该存了预测结果。实操中我会同时输出 AUC、KS 和按 5% 通过率下的坏账捕获率,答辩时这三个指标一起讲,比单说 AUC 有说服力。阈值选择要基于业务成本,不是越高越好。
5. 避坑与常见问题排查:跑不通多半是这几个原因
5.1 中文路径和编码导致读取失败
现象:运行 handle_apply.py 时报 UnicodeDecodeError 或 FileNotFoundError。原因:原始 txt 文件可能是 GBK 编码,或者路径里有中文,Windows 下 pandas 读取容易出问题。解决:读取时显式指定encoding='gbk'或encoding='utf-8-sig',路径尽量改成纯英文,或者用os.path.join拼接避免转义。
5.2 多表关联后行数暴涨
现象:merge 之后 df.shape 从几万变成几十万。原因:被关联表里一个客户有多条记录,没做聚合直接 join。解决:关联前先对多记录表按 cust_id 做 groupby 聚合,或者用drop_duplicates保留一条,关联后立刻检查nunique是否和主表一致。
5.3 训练集测试集特征分布不一致
现象:本地交叉验证 AUC 很高,但测试集预测结果全是一个值。原因:特征工程里用了全量数据算统计量(比如均值、分箱边界),导致测试集信息泄露到训练集。解决:所有统计量只在训练集上 fit,再 transform 到测试集,用 sklearn 的 Pipeline 能强制这个顺序。
5.4 正负样本极度不平衡导致模型摆烂
现象:模型把所有样本都预测为负类,AUC 接近 0.5。原因:风控数据正样本通常只有几个百分点,模型学不到正类模式。解决:LightGBM 里设is_unbalance=True或scale_pos_weight,或者用欠采样/过采样。注意别在交叉验证前做采样,要在每折训练集内部做。
5.5 Notebook 里的执行顺序依赖
现象:单独跑 feature_engineering.ipynb 报变量未定义。原因:Notebook 依赖前面 cell 的中间变量,跳着跑就断。解决:从上到下 Restart & Run All 跑一遍,或者把关键中间结果落盘成 csv,脚本之间用文件传递而不是内存变量。
6. 答辩 PPT 和文档怎么用:把代码讲成故事的两个技巧
答辩 PPT 有两份,一份是「大佬的ppt,特征提取十分值得学习.pptx」,一份是「风险模型预测-最终版.pptx」。前者重点看特征提取的讲解逻辑,后者看整体结构。我的经验是,答辩时评委最想听的不是你用了多复杂的模型,而是「你为什么这么选」。第一个技巧:把特征工程讲成业务故事。比如「近 3 个月征信查询次数多,说明客户资金紧张,违约概率高」,比干讲 IV 值有说服力。第二个技巧:准备一页模型对比表,把每个模型的 AUC、KS 列出来,说明为什么最终选 LightGBM,而不是只放一个最终结果。
| 模型 | 脚本 | 适用场景 | 调参重点 |
|---|---|---|---|
| 朴素贝叶斯 | MNBC.py / _GaussianNB.py | 快速验证特征有效性 | 几乎不用调 |
| 决策树 | dtc.py | 可解释性要求高 | max_depth、min_samples_leaf |
| 随机森林 | randomforest.py | baseline 对比 | n_estimators、max_features |
| GBDT | _GBDT.py | 中小规模数据 | learning_rate、n_estimators |
| XGBoost | _xgb.py | 冲指标 | max_depth、subsample |
| LightGBM | lgb.py / _lgb.py | 大规模表格数据首选 | num_leaves、min_data_in_leaf |
验证方法上,我一般会做两件事:一是把 OOF 预测结果按分数分十档,看每档的实际坏账率是否单调,单调性好的模型业务上更可信;二是拿测试集跑一遍,确认预测分布和训练集没有明显偏移。如果十档坏账率不单调,说明模型过拟合或者特征有问题,得回去查。
从那以后我每次拿到这种多表关联的风控项目,都强制先跑一遍「关联后行数检查 + 训练测试分布对比」,这两个检查能挡掉八成翻车。希望帮到你。
本文还有配套的精品资源,点击获取