
简介这份资源是面向计算机、人工智能、自动化等专业学生与从业者的深度学习实战项目包以个人贷款违约预测为主题可用于课程设计、大作业或毕业设计参考。项目代码经过调试测试注释较为完整并配有详细运行教程适合零基础入门者学习数据读取、预处理、可视化与特征工程等环节也便于进阶者在此基础上修改调整以实现不同功能。压缩包共28个文件约16.37MB包含csv数据集、py源码脚本、ipynb实验笔记、xml配置、xlsx商业取数逻辑表及md运行说明等覆盖数据清洗、独热编码、金额字段处理与多维度图表展示等模块。目前已有673人学习下载可帮助读者快速理解贷款违约预测的完整流程掌握从原始数据到模型输入的构建思路并借鉴项目目录组织与排错方法。1. 从一份 95 分毕设拆开看个人贷款违约预测系统到底能跑出什么答辩评审 95 分、代码注释拉满、附带详细运行教程——这套基于深度学习的个人贷款违约预测系统本质是一个把金融风控场景完整走通的 Python 工程包。它用真实结构的贷款业务数据账户、交易、放贷、信用卡、地区五张表从数据清洗一路做到特征工程、可视化分析和违约预测建模。适合三类人正在找课程设计或毕业设计选题的计算机/人工智能专业学生、想补一个端到端风控项目经验的转行者、以及需要一份可运行基线代码来改造成自己课题的从业者。它不是玩具 demo数据表之间的关联逻辑、金额字段的清洗方式、独热编码的处理细节都是真实业务里会碰到的。下面按「资源是什么 → 怎么跑起来 → 坑在哪 → 怎么改」的顺序拆。2. 数据层拆解五张 CSV 表的关联逻辑与预处理脚本拿到压缩包先别急着跑 main.ipynb。这个项目的核心价值在数据层——五张 CSV 不是随便凑的它们之间有明确的业务关联。先搞清楚表结构和预处理链路后面建模才不会翻车。2.1 五张核心表的字段含义与关联关系解压后 data 目录下是 order.csv、disp.csv、accounts.csv、clients.csv、card.csv、loans.csv、district.csv、trans.csv 这八张表。其中和违约预测直接相关的是下面五张文件名业务含义关键字段关联键accounts.csv账户信息account_id, district_id, frequency, dateaccount_idclients.csv客户信息client_id, birth_number, district_idclient_idloans.csv放贷记录loan_id, account_id, date, amount, duration, statusaccount_idtrans.csv交易流水trans_id, account_id, date, type, amountaccount_idcard.csv信用卡card_id, disp_id, type, issueddisp_id关联链路是clients → disp客户与账户的持有关系→ accounts → loans/trans。loans 表里的 status 字段就是预测目标——A 代表正常还款B 代表违约C 代表正常结清D 代表违约结清。做二分类时通常把 B 和 D 归为违约类。注意birth_number 字段不是标准日期格式捷克数据集里女性会加 50 到月份上解析时需要判断月份是否大于 50 来还原性别和真实出生月。2.2 read_file.py 与 fix_money.py 的清洗逻辑read_file.py 定义了两个函数read_file 负责批量读取指定目录下的 CSV 并返回 DataFrameone_hot 负责对类别字段做独热编码。fix_money.py 则处理金额字段里的$和,符号。先看 fix_money 的核心逻辑# fix_money.py def fix_money(s): 去除字符串中的 $ 和 , 后转为数值类型 if isinstance(s, str): s s.replace($, ).replace(,, ) return float(s) if __name__ __main__: print(fix_money($4,100)) # 输出 4100.0这个函数看着简单但它是整个数据清洗链路的第一环。trans.csv 和 loans.csv 里的 amount 字段原始格式带货币符号和千分位逗号不处理的话后面做聚合统计直接报类型错误。参数说明传入 str 返回 float传入非字符串则直接尝试 float 转换——这里有个隐患如果传入 None 会抛 TypeError实际使用时建议加一层空值判断。read_file.py 的批量读取逻辑# read_file.py import os import pandas as pd def read_file(path, filename): 读取指定目录下的单个 CSV 文件 filepath os.path.join(path, filename) return pd.read_csv(filepath, encodingutf-8) def one_hot(df, columns): 对指定列做独热编码返回编码后的 DataFrame return pd.get_dummies(df, columnscolumns) if __name__ __main__: data_path ./data accounts read_file(data_path, accounts.csv) print(accounts.head())read_file 的 encoding 参数默认 utf-8如果 CSV 是 GBK 编码会报 UnicodeDecodeError改成encodinggbk即可。one_hot 用的是 pandas 的 get_dummies好处是自动识别类别列坏处是如果训练集和测试集分开编码会导致列数不一致——常见做法是先合并再编码或者用 sklearn 的 OneHotEncoder 固定 categories。2.3 exercise_20190910.py 的可视化分析流程这个脚本是数据探索的主力跑完会生成多张图表。执行命令python exercise_20190910.py脚本内部流程是先用 read_file 读取 data 目录下的 CSV然后对 accounts、clients、card 等表做分组统计最后用 matplotlib 出图。重点看几个分析维度——不同卡类型的持卡人性别分布、年龄分布、账户余额分布。这些图表不是装饰它们直接告诉你哪些特征对违约预测有区分度。比如如果发现违约用户的年龄集中在某个区间那 age 就是一个强特征如果某类卡的违约率明显偏高card_type 就该进模型。脚本里用到的 groupby agg 组合是 pandas 做探索性分析的标配参数上注意 agg 里传字典时 key 是列名、value 是聚合函数。3. 建模链路从特征工程到深度学习分类器数据清洗完只是半成品真正决定预测效果的是特征工程和模型选型。这一章把从原始表到模型输入的完整链路拆开讲。3.1 特征工程把交易流水聚合成用户画像原始 trans 表是逐笔交易记录一个账户可能有几百条不能直接喂给模型。常见做法是按 account_id 聚合生成统计特征import pandas as pd trans pd.read_csv(./data/trans.csv) # 按账户聚合交易金额的统计量 trans_agg trans.groupby(account_id)[amount].agg( trans_countcount, trans_meanmean, trans_stdstd, trans_maxmax, trans_minmin ).reset_index() # 填充标准差缺失值只有一笔交易的账户 std 为 NaN trans_agg[trans_std] trans_agg[trans_std].fillna(0)这段代码的逻辑是count 反映账户活跃度mean 反映平均交易规模std 反映交易波动性——波动大的账户往往风险更高。参数上fillna(0) 处理的是只有单笔交易的账户它们的标准差在统计学上无定义填 0 表示「无波动」。除了金额统计还可以按交易类型type 字段做透视表生成每种交易类型的笔数和金额占比。loans 表的 duration 和 amount 也是重要特征但要注意量纲差异——amount 可能是几十万duration 是几十个月直接入模会被大数值主导。标准化处理from sklearn.preprocessing import StandardScaler scaler StandardScaler() loans[[amount, duration]] scaler.fit_transform(loans[[amount, duration]])StandardScaler 做的是 z-score 标准化让每个特征均值为 0、方差为 1。注意 fit_transform 只能在训练集上做测试集要用 transform否则会数据泄露。3.2 类别不平衡处理与模型输入构造loans 表里违约样本通常远少于正常样本这是风控场景的常态。直接训练会导致模型偏向多数类违约召回率极低。两种常见处理方式一是用 imblearn 的 SMOTE 做过采样from imblearn.over_sampling import SMOTE smote SMOTE(random_state42) X_resampled, y_resampled smote.fit_resample(X_train, y_train)SMOTE 的原理是在少数类样本之间做线性插值生成新样本random_state 固定保证可复现。注意 SMOTE 只能在训练集上做测试集保持原始分布。二是用 class_weight 参数让模型自动加权from sklearn.ensemble import RandomForestClassifier rf RandomForestClassifier( n_estimators200, max_depth8, class_weightbalanced, random_state42 )class_weightbalanced 会让模型按类别频率反比加权少数类的权重更高。n_estimators200 是树的数量max_depth8 控制树的深度防止过拟合——这两个参数是随机森林最常调的树太少欠拟合、太多过拟合深度同理。3.3 深度学习分类器的搭建与训练项目定位是「基于深度学习」所以除了传统机器学习基线还需要一个神经网络模型。用 PyTorch 搭一个简单的全连接网络import torch import torch.nn as nn class LoanDefaultNet(nn.Module): def __init__(self, input_dim): super().__init__() self.net nn.Sequential( nn.Linear(input_dim, 64), nn.ReLU(), nn.Dropout(0.3), nn.Linear(64, 32), nn.ReLU(), nn.Dropout(0.3), nn.Linear(32, 1), nn.Sigmoid() ) def forward(self, x): return self.net(x) model LoanDefaultNet(input_dimX_train.shape[1]) criterion nn.BCELoss() optimizer torch.optim.Adam(model.parameters(), lr1e-3)网络结构是 输入层 → 64 → 32 → 1每层之间用 ReLU 激活Dropout(0.3) 防止过拟合最后一层 Sigmoid 把输出压到 0-1 之间作为违约概率。BCELoss 是二分类交叉熵损失Adam 优化器学习率设 1e-3 是常规起点。训练循环里注意要把数据转成 tensor每轮做 forward → loss → backward → step 四步。提示如果数据量不大几千条全连接网络可能跑不过 XGBoost。这不是模型的问题是小样本下深度学习的通病。建议两个都跑用 AUC 对比。4. 避坑与排查跑这套代码最容易翻车的五个地方这套代码注释确实多但环境配置和数据路径的坑不会因为注释多就消失。下面五条是我实际跑下来最容易卡住的点。4.1 路径问题相对路径在 IDE 和命令行下行为不一致现象在 PyCharm 里跑 main.ipynb 正常命令行python exercise_20190910.py报 FileNotFoundError。原因脚本里用的是相对路径./data/xxx.csv相对路径的基准是当前工作目录。IDE 默认把项目根目录设为工作目录命令行的基准是你执行命令时所在的目录。解决要么 cd 到项目根目录再执行要么在脚本开头加os.chdir(os.path.dirname(os.path.abspath(__file__)))强制切换工作目录。4.2 编码问题CSV 文件不是 UTF-8现象pd.read_csv报 UnicodeDecodeError: utf-8 codec cant decode byte...原因原始数据文件可能是 GBK 或 latin-1 编码read_file.py 默认用 utf-8 读取。解决先试encodinggbk不行再试encodinglatin-1。更稳妥的做法是用 chardet 检测编码后传入。4.3 依赖版本冲突pandas 和 numpy 版本不匹配现象import pandas 时报ImportError: cannot import name NaN from numpy或类似错误。原因pandas 2.x 要求 numpy 1.22 以上旧环境里 numpy 版本过低。解决pip install --upgrade pandas numpy一起升级别单独升一个。如果项目有 requirements.txt 就按文件装。4.4 独热编码后列数爆炸现象做完 one_hot 后特征数从几十变成几千模型训练极慢甚至内存溢出。原因district.csv 里的地区编码有几十个类别one_hot 后每个类别一列。如果还有日期字段被误当类别处理列数会失控。解决高基数类别列不要用 one_hot改用目标编码或频率编码。日期字段先解析成数值特征年、月、日再入模。4.5 可视化中文乱码现象matplotlib 出的图里中文全变成方框。原因matplotlib 默认字体不支持中文。解决在绘图脚本开头加两行import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] FalseSimHei 是 Windows 自带黑体Mac 下换成[Arial Unicode MS]Linux 下需要先装中文字体。5. 进阶改造把基线模型换成 XGBoost 并做特征重要性分析跑通基线之后最有价值的改造方向是换模型 看特征重要性。随机森林和全连接网络能给你一个 AUC但不会告诉你「哪个特征在驱动预测」。XGBoost 在这方面更直接。先装依赖pip install xgboost然后替换模型部分import xgboost as xgb from sklearn.model_selection import StratifiedKFold from sklearn.metrics import roc_auc_score # 用分层K折保证每折的违约比例一致 skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) auc_scores [] for train_idx, val_idx in skf.split(X, y): X_train, X_val X.iloc[train_idx], X.iloc[val_idx] y_train, y_val y.iloc[train_idx], y.iloc[val_idx] model xgb.XGBClassifier( n_estimators300, max_depth5, learning_rate0.05, subsample0.8, colsample_bytree0.8, scale_pos_weightlen(y_train[y_train0]) / len(y_train[y_train1]), eval_metricauc, random_state42 ) model.fit(X_train, y_train) y_pred_proba model.predict_proba(X_val)[:, 1] auc_scores.append(roc_auc_score(y_val, y_pred_proba)) print(f5折AUC均值: {sum(auc_scores)/len(auc_scores):.4f})参数说明n_estimators300 是树的数量max_depth5 比随机森林的 8 更浅因为 XGBoost 是加法模型会累积深度learning_rate0.05 配合 300 棵树是经典的慢学习率多树组合subsample 和 colsample_bytree 都设 0.8 做行采样和列采样来防过拟合。scale_pos_weight 是 XGBoost 处理类别不平衡的内置参数值设为负样本数除以正样本数效果类似 class_weightbalanced。跑完 5 折后看特征重要性import matplotlib.pyplot as plt xgb.plot_importance(model, max_num_features15, importance_typegain) plt.title(Feature Importance (Gain)) plt.tight_layout() plt.show()importance_typegain 表示用信息增益衡量重要性比默认的 weight分裂次数更能反映特征的真实贡献。通常跑下来trans_agg 里的交易统计特征和 loans 的 duration、amount 会排在前列district 相关的独热特征往往排在后面——这说明地区信息在这个数据集里区分度有限可以考虑砍掉减少维度。我自己的习惯是每次换模型或加特征后先跑一遍 5 折交叉验证看 AUC 稳不稳定再跑特征重要性看有没有意外特征混进来。有一次发现某个 ID 类字段排到了重要性前三查了半天才意识到是数据泄露——那个字段在训练集和测试集之间的分布不一致。从那以后我每次做完特征工程都强制走一遍「特征重要性 分布对比」的检查再急也不跳过。希望这套代码能帮你少走点弯路把精力花在真正值得调的地方。本文还有配套的精品资源点击获取