简介:这份资源面向Python机器学习初学者与课程设计实践者,围绕个人信用评估与贷款违约预测任务,提供一套可复现的完整项目方案。数据集选自阿里天池贷款违约预测比赛,原始数据超120万条、含47列变量,其中15列为匿名变量,并已对employmentTitle、purpose、postCode和title等字段做脱敏处理,适合练习特征工程、类别编码、缺失值填充与模型调参。压缩包共82个文件,约5.16MB,包含13个Python脚本、2个CSV数据文件、1份Word设计报告、1份README说明及62张可视化PNG图,脚本覆盖数据校验、编码、填充、PCA降维、网格搜索与训练流程,图片则呈现相关性热力图、分布图与特征关系。目前已有740人学习下载。读者可据此获得从数据探索到建模评估的完整赛题思路,并借助报告与图表理解各阶段结论,适合作为课程设计或入门实战的参考模板。
1. 从一份 120 万条信贷记录说起:这套 Python 信用评估源码到底能跑出什么
信贷风控里有个反直觉的现象:决定一笔贷款会不会违约的,往往不是收入高低,而是几个看起来不起眼的字段组合——比如grade和subGrade的错配、dti的异常区间、revolUtil的循环授信使用率。这套「Python 通过机器学习实现对个人信用评估」的资源,正是围绕这类判断展开的。它基于阿里天池贷款违约预测数据集,原始数据超过 120 万条、47 列变量,其中 15 列是匿名变量,训练集 80 万、测试集 A/B 各 20 万,employmentTitle、purpose、postCode、title做了脱敏处理。资源里给的是完整可复现的工程:设计报告 Word、src下的数据处理与训练脚本、scripts下的可视化脚本、imgs里几十张分布与相关性图、submit.csv提交样例。适合正在做课程设计、想跑通一套完整风控建模流程的人,也适合已经会调 sklearn 但没做过百万级真实信贷数据的从业者。
2. 数据管道拆解:从 47 列原始字段到模型可吃的特征矩阵
拿到这份源码,第一件要搞清楚的事不是模型,而是数据怎么从 47 列原始字段变成模型能吃的矩阵。信贷数据的坑几乎全在管道里,模型本身反而是最标准化的一环。
2.1 先看 dataCheck 和 nullInfo:缺失不是删掉就完事
src/dataCheck.py和nullInfo/nullInfo.png是入口。信贷数据的缺失有强业务含义:employmentLength缺失可能代表无业或自由职业,dti缺失可能是负债信息未采集,直接dropna会把高风险样本整批删掉,模型学到的分布就偏了。
常见做法是分三类处理:数值型缺失用中位数或分组中位数填充,类别型缺失单独设一个Unknown类别,匿名变量n0~n14缺失率高的直接看是否整列丢弃。下面是我一般会先跑一遍的缺失体检脚本:
import pandas as pd import numpy as np df = pd.read_csv('data/train.csv') # 缺失率排序,先看哪些列值得救 null_rate = df.isnull().mean().sort_values(ascending=False) print(null_rate[null_rate > 0]) # 数值列和类别列分开处理 num_cols = df.select_dtypes(include=[np.number]).columns.tolist() cat_cols = df.select_dtypes(include=['object']).columns.tolist() # 数值列:按目标分组中位数填充,比全局中位数更贴近业务 for col in num_cols: if df[col].isnull().any(): df[col] = df.groupby('isDefault')[col].transform( lambda x: x.fillna(x.median()) ) # 类别列:缺失单独成类,保留信息 for col in cat_cols: df[col] = df[col].fillna('Unknown')逻辑说明:groupby('isDefault')是关键,违约组和非违约组的收入、负债分布差异很大,用全局中位数会把两拨人混在一起。参数上,isDefault是标签列,如果你的数据里标签叫别的名字,改这一处即可。类别列填Unknown而不是众数,是因为缺失本身可能就是信号。
2.2 dataFill 与 dataEncode:匿名变量和脱敏字段怎么进模型
src/dataFill.py和src/dataEncode.py负责填充和编码。这里有个容易翻车的地方:employmentTitle、purpose、postCode、title是脱敏字段,取值多且稀疏,直接 One-Hot 会让特征维度爆炸到几万维。
我一般会按基数分档处理:
| 字段类型 | 基数范围 | 处理方式 |
|---|---|---|
| 低基数类别 | < 20 | One-Hot 或 Ordinal |
| 中基数类别 | 20 ~ 200 | 目标编码或频率编码 |
| 高基数脱敏字段 | > 200 | 频率编码 + 分箱 |
| 匿名变量 n0~n14 | 不定 | 保留原值,树模型可处理 |
grade和subGrade是有序的,grade从 A 到 G 风险递增,直接映射成 0~6 的整数比 One-Hot 更合理,树模型能直接利用这个序关系。interestRate、ficoRangeLow、ficoRangeHigh这些数值列不用动,但要注意ficoRangeLow和ficoRangeHigh高度相关,corr/heatMap.png里能看到这一点,后面 PCA 会处理。
# 有序类别映射,保留风险序关系 grade_map = {g: i for i, g in enumerate('ABCDEFG')} df['grade'] = df['grade'].map(grade_map) df['subGrade'] = df['subGrade'].apply(lambda x: (ord(x[0]) - 65) * 5 + int(x[1])) # 高基数脱敏字段用频率编码 for col in ['employmentTitle', 'purpose', 'postCode', 'title']: freq = df[col].value_counts(normalize=True) df[col + '_freq'] = df[col].map(freq) df.drop(columns=[col], inplace=True)参数说明:subGrade的映射把A1~G5压成 0~34 的连续整数,比字符串编码省内存也保留序。频率编码后原列可以删掉,避免树模型对高基数类别过拟合。
2.3 pca_dimensionReduce:匿名变量降维的取舍
src/pca_dimensionReduce.py和pca/pca.png处理匿名变量。n0~n14这 15 列没有业务含义,但彼此之间以及和数值特征之间有相关性,corr/high_corr_features.csv` 里列了高相关特征对。
PCA 降维的坑在于:树模型对线性降维不敏感,PCA 之后反而损失了可解释性。我一般会先跑一版不降维的基线,如果训练时间可接受、AUC 没明显下降,就不做 PCA。只有在用逻辑回归或需要压缩特征时才启用。pca.png里的碎石图能帮你判断保留几个主成分,通常累计方差到 85%~90% 就够。
3. 训练与调参:GridSearchCV 在百万级数据上的现实用法
模型训练部分集中在src/train.py和src/GridSearchCV.py。百万级数据上直接上网格搜索是血泪经验级的慢,得先想清楚搜索空间和验证策略。
3.1 train.py 的主流程与标签定义
train.py的主流程是:读数据 → 调dataHandler→ 切分 → 训练 → 输出submit.csv。标签是isDefault,1 表示违约。信贷数据极度不平衡,违约率通常在个位数到十几个百分点,所以评估指标不能只看准确率。
from sklearn.model_selection import train_test_split from sklearn.metrics import roc_auc_score import lightgbm as lgb X_train, X_val, y_train, y_val = train_test_split( X, y, test_size=0.2, stratify=y, random_state=42 ) model = lgb.LGBMClassifier( n_estimators=1000, learning_rate=0.05, num_leaves=31, scale_pos_weight=(y_train == 0).sum() / (y_train == 1).sum(), random_state=42 ) model.fit( X_train, y_train, eval_set=[(X_val, y_val)], eval_metric='auc', callbacks=[lgb.early_stopping(50)] ) print('Val AUC:', roc_auc_score(y_val, model.predict_proba(X_val)[:, 1]))逻辑说明:stratify=y保证切分后正负样本比例一致,否则验证集可能偏差很大。scale_pos_weight处理不平衡,值等于负样本数除以正样本数。early_stopping(50)在验证 AUC 50 轮不升就停,省时间也防过拟合。参数上num_leaves=31是 LightGBM 的保守起点,数据量大可以往上调,但超过 127 容易过拟合。
3.2 GridSearchCV 的搜索空间怎么设才不浪费时间
GridSearchCV.py里如果直接对n_estimators、learning_rate、num_leaves、max_depth全排列,组合数轻松上百,百万级数据跑一轮就是几小时。我的做法是分两阶段:先粗搜学习率和叶子数,再细搜正则参数。
from sklearn.model_selection import GridSearchCV param_grid = { 'learning_rate': [0.03, 0.05, 0.1], 'num_leaves': [31, 63, 127], 'min_child_samples': [20, 50] } # 用 3 折而不是 5 折,百万级数据下省一半时间 grid = GridSearchCV( lgb.LGBMClassifier(n_estimators=500, random_state=42), param_grid, cv=3, scoring='roc_auc', n_jobs=-1, verbose=1 ) grid.fit(X_train, y_train) print(grid.best_params_)参数说明:cv=3是权衡,数据量大时 5 折的边际收益不高。n_jobs=-1用满 CPU。scoring='roc_auc'而不是 accuracy,因为不平衡数据下 accuracy 会骗人。搜索完拿到最优参数后,再把n_estimators调大配合 early stopping 跑最终模型。
3.3 特征重要性验证:别只看 AUC
训练完一定要看特征重要性,visualize/plotCorr.py和plotDistribution.py能帮你把关键特征的分布画出来。信贷场景里,如果grade、interestRate、dti、revolUtil没排在前列,说明编码或填充环节出了问题。我见过有人把grade做了 One-Hot 之后重要性被稀释,换成有序编码就正常了。
4. 可视化与结果校验:那些图不是装饰,是排查工具
imgs目录下几十张图不是拿来凑报告页数的,每一张对应一个排查点。scripts下的plotCorr.py、plotCorrPair.py、plotDistribution.py是生成这些图的脚本。
4.1 分布图怎么读:从 interestRate-grade 看业务逻辑
interestRate-grade.png和interestRate-subGrade.png展示利率随信用等级的分布。正常情况是等级越差利率越高,如果图里出现交叉或异常,说明数据有问题或者等级映射反了。ficoRangeHigh-ficoRangeLow.png看 FICO 分数区间,loanAmnt-installment.png看贷款金额和分期金额的关系,openAcc-totalAcc.png看开户数和总账户数。
import matplotlib.pyplot as plt import seaborn as sns # 利率与等级的关系,验证业务逻辑 plt.figure(figsize=(10, 6)) sns.boxplot(x='grade', y='interestRate', data=df) plt.title('Interest Rate by Grade') plt.savefig('imgs/interestRate-grade-check.png', dpi=150)逻辑说明:箱线图比散点图更适合看分布,能直接看出中位数和离群点。如果某个等级的利率分布和其他等级重叠严重,要么是数据采集问题,要么是这个等级样本太少。
4.2 相关性热力图与高相关特征处理
corr/heatMap.png和corr/high_corr_features.csv是特征筛选的依据。信贷数据里ficoRangeLow和ficoRangeHigh、loanAmnt和installment通常高度相关。树模型对共线性不敏感,但逻辑回归会受影响,而且共线特征会让重要性解释变得模糊。
我一般会设一个阈值,比如相关系数绝对值大于 0.9 就保留业务含义更强的那一个。high_corr_features.csv里已经列好了,直接按它筛就行。注意别把grade和subGrade一起删,这两个虽然相关但粒度不同,subGrade更细。
4.3 submit.csv 的格式校验
submit.csv是提交样例,格式通常是id,isDefault两列,isDefault是 0/1 或概率值。生成提交文件前一定要检查行数和测试集一致、id 顺序对得上、没有缺失值。我踩过的坑是 pandas 写 csv 时默认带索引,多出一列导致提交失败,加index=False就好。
# 生成提交文件,注意 index=False submission = pd.DataFrame({'id': test_ids, 'isDefault': preds}) submission.to_csv('submit.csv', index=False) print(submission.shape, submission['isDefault'].isnull().sum())5. 避坑与常见问题:这套源码跑起来最容易翻车的五个地方
5.1 内存爆掉:120 万条 47 列直接读进来就卡死
现象:pd.read_csv读训练集时内存飙升,16G 机器直接卡住或报 MemoryError。 原因:120 万行 47 列,如果全是 object 类型,内存占用能到几个 G,加上后续 One-Hot 编码会翻好几倍。 解决:读数据时指定dtype,数值列用float32或int32,类别列用category类型。分块读或者先抽样跑通流程再上全量。
dtypes = {col: 'float32' for col in num_cols} dtypes.update({col: 'category' for col in cat_cols}) df = pd.read_csv('data/train.csv', dtype=dtypes)5.2 标签泄漏:用未来信息预测过去
现象:验证 AUC 高得离谱,线上或测试集表现断崖式下跌。 原因:某些特征在业务上只有放款后才知道,比如totalAcc如果包含了这笔贷款本身,就是泄漏。或者填充缺失时用了全量数据的中位数,验证集信息渗进了训练集。 解决:填充、编码、PCA 这些步骤全部在训练集上 fit,再 transform 验证集。用 sklearn 的 Pipeline 能强制这个顺序。
5.3 类别编码顺序错乱:grade 映射反了
现象:模型 AUC 正常但特征重要性里grade排最后,或者预测结果和业务直觉相反。 原因:grade映射时把 A 映射成 6、G 映射成 0,序关系反了,树模型学到的分裂方向就反了。 解决:映射后打印一下df.groupby('grade')['isDefault'].mean(),正常应该是等级越高违约率越高。如果反了,检查映射字典。
5.4 匿名变量 n0~n14 的缺失处理不当
现象:PCA 降维后模型效果反而下降。 原因:匿名变量缺失率高,如果填充时用了不合适的值,PCA 会把噪声放大。或者 PCA 在白化时对量纲敏感,没标准化就降维。 解决:PCA 前必须StandardScaler。匿名变量缺失率超过 50% 的列考虑直接丢弃,别硬填。
5.5 提交文件 id 对不上
现象:提交后平台报格式错误或分数为 0。 原因:测试集读取时顺序变了,或者train_test_split打乱了 id 顺序,生成提交时没对齐。 解决:测试集的 id 单独存一份,预测结果按原顺序拼回去。生成后merge校验一遍行数和 id 集合。
6. 进阶技巧:把 baseline 推到可用的几个实操手段
跑通 baseline 之后,想再往上提一截,我一般会从三个方向入手。第一是特征交叉,信贷场景里grade和dti的组合、interestRate和term的组合往往比单特征更有区分度,树模型虽然能自动学,但显式交叉能让它学得更快。第二是分箱,annualIncome、loanAmnt这些连续变量做等频分箱后,模型的稳定性会好一些,尤其是线上评分卡场景。第三是模型融合,LightGBM 加一个逻辑回归做 stacking,AUC 通常能再涨千分之几。
验证方法上,别只看一次切分的 AUC。我习惯跑 5 折交叉验证,看 AUC 的均值和方差,方差大说明模型不稳定,可能是某些特征在特定折里分布差异大。GridSearchCV.py里改成cv=5就能拿到每折分数。另外,visualize/plotDistribution.py可以改一下,把预测概率的分布画出来,正常应该是双峰或者偏态,如果集中在 0.5 附近,说明模型没学到东西。
from sklearn.model_selection import cross_val_score scores = cross_val_score( model, X, y, cv=5, scoring='roc_auc', n_jobs=-1 ) print('AUC mean: %.4f, std: %.4f' % (scores.mean(), scores.std()))参数说明:cv=5比单次切分可靠,n_jobs=-1并行。如果 std 超过 0.01,就得回头查数据管道,而不是继续调参。
从那以后我每次跑信贷模型,都强制先跑一遍缺失体检和分布校验,再动模型。这套源码的价值不在于模型多复杂,而在于它把从原始数据到提交文件的每一步都摆出来了,你可以照着改、照着踩坑、照着修。希望帮到你。
本文还有配套的精品资源,点击获取