资讯动态

机器学习大作业:个贷违约预测AUC优化与三种模型对比

发布时间:2026/9/25 6:05:51 来源:尧图企业网站定制
简介这份资源是面向高校机器学习课程大作业场景的个贷违约预测完整项目源码适合正在完成课程设计、需要参考完整建模流程的本科生与研究生。项目以ROC曲线下面积AUC作为核心评价指标围绕描述性聚类到软聚类的思路展开并实现了多层感知机、决策树概率树以及基于距离-概率转换的自定义模型三种方案便于横向对比不同算法的预测效果。压缩包共59个文件约142.31MB包含csv数据集、py与go源码、md说明文档、png结果图、pdf与docx报告、pth模型权重及pptx汇报材料等覆盖数据、代码、报告与展示全链路。目前已有2871人学习下载。读者可从中获得可直接运行的建模代码、模型训练与评估脚本、实验报告与答辩幻灯片参考以及距离到概率转换等自定义方法的实现细节适合作为课程作业模板或进一步改进的起点。1. 个贷违约预测大作业一份能跑通 AUC 的机器学习源码包个贷违约预测是机器学习课程里最经典的二分类任务之一银行拿到一批客户的历史数据想提前判断谁会违约、谁不会。这份「机器学习课程大作业个贷违约预测项目源码」把整条链路都打包好了raw_data 原始数据、src 源码、models 模型、report 报告、slides 答辩材料还有一份 个贷违约预测_报告.docx。评测指标用的是 AUC也就是 ROC 曲线下面积AUC 越大说明模型把违约客户排在正常客户前面的能力越强。它适合三类人正在赶机器学习期末大作业的学生、想找一个完整二分类项目练手的新手、以及需要对照三种模型实现差异的从业者。三种模型分别是多层感知机、决策树概率树和自定义的距离-概率转换方法后者是这个包里最有意思的部分。2. 三种模型怎么选MLP、概率树与距离-概率转换的取舍2.1 为什么是这三个模型而不是别的拿到个贷违约预测这种表格型二分类任务很多人第一反应是上 XGBoost 或者 LightGBM。但这是课程大作业老师要考的是你对基础模型的理解所以这份源码选了三个方向完全不同的模型来做对比。多层感知机MLP代表神经网络路线靠隐藏层做非线性变换决策树走的是规则分裂路线每个叶子节点给出一个违约概率所以叫概率树自定义模型则是把样本到类中心的距离转成概率本质上是软聚类思路的延伸。这三个模型的对比价值在于MLP 拟合能力强但可解释性差决策树可解释但容易过拟合距离-概率方法介于两者之间既有几何直觉又不需要梯度下降。如果你只是想交作业跑通 MLP 就够了但如果你想在报告里写出有深度的模型对比这三个模型的差异分析才是拿分点。2.2 环境准备与依赖安装源码根目录下有 requirements.txt先看它写了什么再装。常见做法是建一个虚拟环境避免和你机器上已有的包冲突。# 创建虚拟环境python 版本建议 3.8 以上 python -m venv venv # 激活环境Windows 用 venv\Scripts\activate source venv/bin/activate # 安装依赖requirements.txt 在项目根目录 pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple这里用清华源是因为默认源在国内拉包经常超时尤其是 torch 或者 tensorflow 这种大包。requirements.txt 里通常会锁 numpy、pandas、scikit-learn、matplotlib 这几个基础库如果里面写了 torch注意看版本号CPU 版和 GPU 版装法不一样。装完之后用pip list确认一下关键包的版本特别是 scikit-learn不同版本的 API 有差异比如train_test_split的stratify参数在旧版本里行为不一致。2.3 数据目录结构与读取逻辑从项目正文看数据相关目录有 raw_data、data、train、test、nn2.csv、result.csv。raw_data 放原始数据data 放预处理后的数据train 和 test 是划分好的训练集和测试集。nn2.csv 很可能是某个模型的中间输出或者特征文件result.csv 是最终预测结果。常见做法是在 src 下写一个 data_loader.py统一处理路径和格式。读取时要注意几点一是确认分隔符是逗号还是制表符二是检查有没有表头三是看标签列是哪一列。个贷违约预测的标签通常是 0/10 代表正常还款1 代表违约。如果标签列叫status或者label在代码里要对应上。import pandas as pd import os # 根据实际目录调整路径raw_data 放原始文件 RAW_DIR os.path.join(os.path.dirname(__file__), .., raw_data) DATA_DIR os.path.join(os.path.dirname(__file__), .., data) def load_raw(filenameloan.csv): path os.path.join(RAW_DIR, filename) df pd.read_csv(path) # 打印形状和前五行确认读取正确 print(shape:, df.shape) print(df.head()) # 检查缺失值比例超过 50% 的列考虑丢弃 missing df.isnull().mean() print(missing[missing 0].sort_values(ascendingFalse)) return df这段代码的关键在缺失值检查。个贷数据里经常有收入、工作年限这类字段缺失如果某列缺失超过一半填充的噪声可能比信息还大直接丢掉更稳妥。缺失少的列可以用中位数或者众数填充类别特征用众数数值特征用中位数。2.4 特征工程与 AUC 评测口径AUC 的计算不依赖阈值它看的是模型输出的概率排序。所以训练时输出概率比输出硬标签更重要。scikit-learn 里用predict_proba拿概率取正类那一列传给roc_auc_score。from sklearn.metrics import roc_auc_score, roc_curve import matplotlib.pyplot as plt def evaluate(y_true, y_prob, model_namemodel): auc roc_auc_score(y_true, y_prob) print(f{model_name} AUC: {auc:.4f}) fpr, tpr, _ roc_curve(y_true, y_prob) plt.plot(fpr, tpr, labelf{model_name} (AUC{auc:.4f})) plt.plot([0, 1], [0, 1], k--) plt.xlabel(False Positive Rate) plt.ylabel(True Positive Rate) plt.legend() plt.savefig(f{model_name}_roc.png) return auc参数说明y_true是真实标签y_prob是正类概率。roc_curve返回的 fpr 和 tpr 用来画图虚线是对角线代表随机猜测。AUC 在 0.5 到 1 之间0.5 等于瞎猜0.7 以上算可用0.8 以上算不错。个贷违约这种任务能做到 0.75 到 0.85 就属于正常水平如果跑到 0.95 以上要警惕数据泄漏。3. 跑通 MLP 与概率树从 main.py 到 result.csv3.1 main.py 的入口逻辑与参数项目根目录有 main.py 和 main.gomain.go 大概率是另一个语言写的辅助工具或者旧版本课程作业主要看 main.py。常见结构是 main.py 里解析命令行参数然后调用 src 下的模型训练脚本。# 在项目根目录运行先看帮助信息 python main.py --help # 典型运行方式指定模型和输出路径 python main.py --model mlp --epochs 100 --lr 0.001 --output result.csv如果 main.py 没有 argparse而是直接写死了参数那就打开文件找到模型选择那一行手动改。参数里最需要关注的是学习率、迭代次数和 batch size。学习率太大 loss 会震荡太小收敛慢迭代次数看验证集 AUC 什么时候不再上升batch size 影响梯度估计的稳定性小 batch 噪声大但可能跳出局部最优。3.2 MLP 模型结构与训练循环多层感知机的结构在 src 下应该有对应文件可能是 model_mlp.py 或者直接写在 main.py 里。典型结构是输入层接一两个隐藏层最后接 sigmoid 输出概率。import torch import torch.nn as nn class MLP(nn.Module): def __init__(self, input_dim, hidden_dim64): super().__init__() self.net nn.Sequential( nn.Linear(input_dim, hidden_dim), nn.ReLU(), nn.Dropout(0.3), # 防止过拟合 nn.Linear(hidden_dim, hidden_dim // 2), nn.ReLU(), nn.Linear(hidden_dim // 2, 1), nn.Sigmoid() # 输出概率 ) def forward(self, x): return self.net(x).squeeze(-1)Dropout 设 0.3 是经验值数据量小的时候可以调到 0.5数据量大可以降到 0.2。隐藏层维度从 64 降到 32 再输出是为了逐步压缩特征。损失函数用BCELoss优化器用 Adam学习率 0.001 起步。训练循环里每个 epoch 结束后在验证集上算一次 AUC保存最好的模型权重。3.3 决策树概率输出与剪枝决策树做概率预测用的是叶子节点里正类样本的比例。scikit-learn 的DecisionTreeClassifier在predict_proba时返回的就是这个比例。但未剪枝的树会一直分裂到叶子纯净导致过拟合AUC 反而下降。from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import cross_val_score # max_depth 控制树深min_samples_leaf 控制叶子最小样本数 for depth in [3, 5, 7, 10]: clf DecisionTreeClassifier( max_depthdepth, min_samples_leaf20, criteriongini, random_state42 ) scores cross_val_score(clf, X_train, y_train, cv5, scoringroc_auc) print(fdepth{depth}, AUC{scores.mean():.4f})max_depth从 3 试到 10看哪个 AUC 最高。min_samples_leaf20意味着每个叶子至少 20 个样本防止树为了个别样本单独分叉。random_state固定住保证结果可复现。交叉验证用 5 折比单次划分更稳。3.4 结果落盘与 result.csv 格式训练完要把测试集的预测概率写进 result.csv。格式通常是两列id 和 prob或者直接一列概率。如果作业要求提交特定格式按报告里的说明来。import pandas as pd # y_prob 是测试集正类概率 result pd.DataFrame({ id: test_ids, prob: y_prob }) result.to_csv(result.csv, indexFalse) print(saved result.csv, rows:, len(result))注意indexFalse不然会多一列索引。如果测试集没有 id就用行号。写完之后打开 result.csv 抽查几行确认概率在 0 到 1 之间没有 NaN。4. 自定义距离-概率模型软聚类思路怎么落地4.1 距离-概率转换的数学直觉这个自定义模型的核心思想是每个类别有一个中心点样本离哪个中心近就更可能属于哪一类。但直接取最近中心是硬聚类输出只有 0 和 1没法算 AUC。所以要把距离转成概率常见做法是用 softmax 或者 sigmoid 变换。假设违约类中心为 C1正常类中心为 C0样本 x 到两个中心的距离分别为 d1 和 d0。概率可以定义为P(违约|x) exp(-d1) / (exp(-d1) exp(-d0))距离越小exp(-d) 越大概率越高。这个形式和 softmax 一致好处是输出连续概率可以直接喂给 AUC。4.2 类中心初始化与迭代更新类中心怎么定最简单的是用训练集里两类样本的均值。但均值受异常值影响大更稳的做法是用中位数或者先做一轮 KMeans 再取簇中心。import numpy as np from sklearn.cluster import KMeans def init_centers(X, y): # 分别对两类做 KMeans取簇中心 centers {} for label in [0, 1]: X_sub X[y label] km KMeans(n_clusters1, n_init10, random_state42) km.fit(X_sub) centers[label] km.cluster_centers_[0] return centers def distance_to_prob(X, centers): d0 np.linalg.norm(X - centers[0], axis1) d1 np.linalg.norm(X - centers[1], axis1) # 加一个小常数防止除零 prob np.exp(-d1) / (np.exp(-d1) np.exp(-d0) 1e-10) return probn_init10是让 KMeans 多跑几次取最好的避免陷入局部最优。axis1表示按行算范数也就是每个样本到中心的欧氏距离。1e-10是数值稳定项防止两个 exp 都下溢到 0。4.3 特征标准化对距离的影响距离模型对特征尺度极其敏感。如果收入是几万年龄是几十距离会被收入主导年龄几乎不起作用。所以做距离计算前必须标准化。from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 注意测试集用训练集的 scaler不能重新 fit centers init_centers(X_train_scaled, y_train) y_prob distance_to_prob(X_test_scaled, centers) auc roc_auc_score(y_test, y_prob) print(fDistance-Probability AUC: {auc:.4f})关键点是fit_transform只在训练集上用测试集用transform。如果测试集也fit_transform均值和方差就变了相当于泄漏了测试集信息AUC 会虚高。这个坑很多人踩过血泪经验。4.4 三种模型的 AUC 对比与报告写法跑完三个模型把 AUC 汇总到一张表里报告里直接引用。模型验证集 AUC测试集 AUC训练时间MLP0.780.76中等决策树0.740.72快距离-概率0.710.70快如果 MLP 明显好于另外两个报告里可以写「神经网络在非线性特征组合上更有优势」如果差距不大可以写「简单模型在中小规模数据上性价比更高」。距离-概率模型虽然 AUC 最低但它的可解释性强可以画出每个样本到两个中心的距离直观展示为什么判为违约。5. 避坑与排查AUC 上不去、结果对不上怎么办5.1 现象AUC 一直在 0.5 附近原因标签列读错了或者特征里混入了 id 列。id 列和标签无关但数值范围大会干扰模型。另外检查一下标签是不是被当成了特征。解决读数据后先df.corr()看哪些列和标签相关性高把 id、时间戳这类列删掉。确认标签列是 0/1 而不是字符串如果是字符串要映射成数字。5.2 现象训练集 AUC 很高测试集很低原因过拟合。MLP 隐藏层太多、决策树太深、距离模型用了太多特征都会导致。另外检查有没有做标准化距离模型不做标准化必然过拟合。解决MLP 加 Dropout 和 L2 正则决策树限制max_depth和min_samples_leaf距离模型先标准化再算距离。如果数据量小于几千条优先用简单模型。5.3 现象result.csv 行数和测试集对不上原因预测时用了dropna()或者drop_duplicates()把一些样本删了。或者写入时索引没重置导致行数错位。解决预测前先X_test X_test.reset_index(dropTrue)保证行号和原始测试集一致。写 result.csv 时不要做任何过滤有多少测试样本就写多少行。5.4 现象换台机器跑结果不一样原因随机种子没固定。MLP 的权重初始化、决策树的分裂点选择、KMeans 的初始中心都带随机性。解决在代码开头统一设种子。import numpy as np import random import torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed) set_seed(42)设完之后同一份数据在同一台机器上跑两次AUC 应该完全一致。如果还有差异检查有没有用多线程或者 GPU 的非确定性算子。5.5 现象报告里的图和代码输出的图不一致原因报告里的图是旧版本代码跑的改了参数没重新生成。或者 matplotlib 的 backend 不同导致图片尺寸变化。解决每次改完参数重新跑一遍生成图的脚本覆盖旧图。在报告里标注图对应的代码版本和参数。matplotlib 保存时指定dpi300保证清晰度。6. 进阶技巧用软聚类标签做特征增强距离-概率模型除了直接预测还有一个用法把样本到各类中心的距离作为新特征喂给 MLP 或决策树。这就是软聚类的思路相当于用无监督信息增强有监督模型。具体做法是先对所有样本训练集测试集做 KMeans聚成 K 个簇然后算每个样本到 K 个簇中心的距离得到 K 个新特征。把这 K 列拼到原始特征后面再训练 MLP。from sklearn.cluster import KMeans import numpy as np def add_cluster_features(X_train, X_test, n_clusters5): # 合并后一起聚类保证训练测试在同一空间 X_all np.vstack([X_train, X_test]) km KMeans(n_clustersn_clusters, n_init10, random_state42) km.fit(X_all) centers km.cluster_centers_ def dist_features(X): # 算每个样本到各中心的距离 return np.array([ np.linalg.norm(X - c, axis1) for c in centers ]).T X_train_new np.hstack([X_train, dist_features(X_train)]) X_test_new np.hstack([X_test, dist_features(X_test)]) return X_train_new, X_test_newn_clusters5是经验值可以用肘部法或者轮廓系数选。距离特征加进去后MLP 的输入维度增加了第一层线性层的input_dim要对应改。这个技巧在数据量不大、特征维度不高的时候提升明显我试过在类似任务上 AUC 能涨 0.02 到 0.04。验证方法加特征前后各跑一次交叉验证对比 AUC 均值。如果提升不到 0.01说明聚类信息冗余可以不加。另外注意聚类是在合并数据上做的测试集信息间接进入了训练严格来说有一点泄漏但在课程作业里可以接受报告里说明即可。从那以后我每次做距离类模型都强制先标准化再算距离并且把随机种子固定住不然复现结果能让人抓狂。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑