新闻详情

新闻详情

首页 / 资讯中心 / 详情

个人贷款违约预测算法实战:从数据清洗到模型部署

发布时间:2026/10/1 6:06:27来源:尧图网络
个人贷款违约预测算法实战:从数据清洗到模型部署
简介面向金融风控学习者的个人贷款违约预测完整项目包涵盖算法源码、说明文档、部署文件与清洗后的真实案例数据适合金融科技方向学生、数据分析人员及风控从业者用于理解并落地信贷违约预测模型。压缩包共11个文件以5份docx文档为主体依次交代模型与数据说明、程序说明、结果分析、阈值取值依据和部署方案3份csv提供训练集、测试集及提交结果另有1个py主代码、1个md说明和gitignore等配套文件整体仅2.17MB结构清晰便于按文档顺序学习。目前已有69人学习下载适合作为教学示例或个人提升数据分析与机器学习实战能力的参考资料。通过源码与文档可掌握逻辑回归、决策树等算法在个人信贷场景中的调优思路以及阈值选择、结果评估和模型部署为可执行文件或API的方法能够帮助读者走通从数据预处理到模型上线的完整流程。1. 个人贷款违约预测算法把“还不还得上”提前算出来在信贷风控里个人贷款违约预测算法是最经典也最常用的二分类任务给定申请人的年龄、收入、负债、历史信用记录判断未来一段时间内会不会逾期。这个压缩包给了一套完整的python源码、说明文档、部署文件和数据可以直接从数据清洗一路跑到接口部署。它能帮你在银行、消金公司或小贷平台的审批流程里做初筛也能当机器学习课程设计或内部风控Demo的完整交付。适合刚学完机器学习、想知道一个项目从数据到上线长什么样的人也适合已经在做业务、需要快速起一个可解释风控基线的工程师。2. 数据清洗与特征工程从压缩包里的原始数据到可用特征模型效果的天花板不在算法而在特征。同一个LightGBM用清洗干净的字段和用原始表格直接喂AUC能差出0.05以上。这一章先把压缩包里的数据理顺把后续建模要用的特征准备出来。2.1 打开压缩包先做什么说明文档与文件清单打开zip之后先别急着跑代码把说明文档读完。说明文档一般会写清数据来源、字段字典、Python版本和依赖库以及src下面每个脚本的执行顺序。我见过不少人跳过了说明文档结果把训练集和测试集字段理解反了白白浪费两天。常见文件组织方式就是下面这样拿到手先按这个结构对一遍。路径/文件作用data/train.csv训练集包含目标字段 default_flagdata/test.csv测试集不含目标字段用于生成预测结果src/data_preprocess.py数据清洗与特征工程src/train.py模型训练、交叉验证与评估src/predict.py读取新样本并输出违约概率deploy/app.pyFastAPI接口服务deploy/model.pkl训练完成后保存的模型产物requirements.txtPython依赖清单第一件必做的事是检查数据规模和字段类型。用pandas加载训练集看看shape、dtypes和空值分布对数据先有个整体感觉。import pandas as pd import numpy as np train pd.read_csv(data/train.csv) print(训练集规模:, train.shape) print(\n字段类型:\n, train.dtypes) print(\n空值统计:\n, train.isnull().sum().sort_values(ascendingFalse))shape返回行数和列数dtypes能看到每个字段是数值还是对象isnull().sum()按列统计空值数量。这一步做完基本能判断清洗的重点空值多的字段要靠填充还是直接删除字符串字段要不要做编码。提示缺失率超过30%的字段除非有明确的业务依据否则优先考虑删掉不要为了保字段做大量脑补填充。2.2 贷款数据字段缺失值、异常值与分布歪斜个人贷款数据通常包含以下字段不同平台的命名会有差异但核心字段基本逃不开这一组。字段含义常见坑age年龄存在空值或身份证号解析出的异常值income年收入右偏严重少数高收入把均值拉得很高loan_amount贷款金额不同产品量纲差异大interest_rate贷款利率缺失率可能较高credit_score外部信用分部分客群没有信用记录缺失率高debt_to_income_ratio负债收入比极端值需要截断default_flag是否违约0/1二值通常违约样本占5%左右处理缺失值时不要所有列统一填均值。income这种重度右偏字段少数高收入者会把均值拽上去填均值等于给大多数低收入的申请人虚构了一笔收入。我一般对连续型字段用中位数填充对离散型字段填众数或者单独填一个unknown类目。num_cols train.select_dtypes(include[np.number]).columns.tolist() for col in num_cols: if train[col].isnull().sum() 0: train[col] train[col].fillna(train[col].median()) print(train.isnull().sum().sum(), 个缺失值已完成中位数填充)select_dtypes只挑数值列避免对loan_id这类标识字段做无意义填充。fillna传入median()返回一个标量同一列的空值都用这个值。数值列的缺失值处理完再看连续特征的长尾收入和贷款金额这类字段我会先截断再取对数。income_upper train[income].quantile(0.99) train[income_log] np.log1p(train[income].clip(upperincome_upper)) print(收入99分位:, income_upper) print(取对数后分布:\n, train[income_log].describe())clip把超过99分位的极端值压到分位点log1p对截断后的值做对数变换拉近右偏分布。要注意的是income_upper这个截断值必须从训练集算出来之后测试集和线上数据要用同一个值去截断不能各自重新算分位数否则训练和预测的数据口径就分叉了。2.3 特征工程分箱、WOE编码与趋势检验逻辑回归和评分卡对连续变量的处理标准做法是先分箱再算WOE。分箱的好处是能缓解异常值的影响同时把收入和年龄这种非线性字段变成分段特征。train[age_bin] pd.qcut(train[age], q5, labelsFalse, duplicatesdrop) print(train.groupby(age_bin)[default_flag].mean())qcut按分位数等频分箱q5表示分成5箱labelsFalse只输出箱编号。age这种离散程度较高的字段分位数边界可能有重复duplicatesdrop会合并重复箱。分组看每个箱的违约率这一步是为了验证单调性——好的分箱结果是违约率随箱号递增或递减如果出现中间高两头低说明边界不合适需要手动合并箱。接下来做WOE编码替换分箱结果后放进模型。def woe_encoding(df, feature, targetdefault_flag): grouped df.groupby(feature)[target].agg(badsum, countcount) grouped[good] grouped[count] - grouped[bad] grouped[bad_rate] (grouped[bad] 0.5) / (grouped[bad].sum() 1) grouped[good_rate] (grouped[good] 0.5) / (grouped[good].sum() 1) grouped[woe] np.log(grouped[bad_rate] / grouped[good_rate]) return grouped[woe].to_dict() woe_map woe_encoding(train, age_bin) train[age_woe] train[age_bin].map(woe_map)groupby聚合出每个箱的坏样本数和总数good用总数减坏样本得到。bad_rate和good_rate都加了0.5平滑避免某个箱坏样本为0时取对数算出负无穷。woe为正数表示这个箱比总体更容易违约负数表示更安全。逻辑回归拿到WOE值后系数方向会更稳定解释起来也清晰。特征工程做完把衍生字段和原始字段一起组成建模用的特征集保存成新文件后面训练脚本直接读。feature_columns [age_woe, income_log, loan_amount, interest_rate, credit_score, debt_to_income_ratio] X train[feature_columns].copy() X.to_csv(data/train_featurized.csv, indexFalse)这里只列了示范字段真实项目里还会有employment_length、house_ownership、loan_purpose等。特征列表要单独存好因为部署阶段模型必须按同一列顺序输入少一个字段或者顺序对不上预测结果就是错的。3. 训练违约预测模型算法选型、参数设置与评估数据准备到位后进入建模。这章的思路是先用逻辑回归出一版基线再用LightGBM追指标。训练脚本train.py的骨架、每个参数的含义以及评估指标怎么读都在这里展开。3.1 算法选型逻辑回归打基线LightGBM追上限信贷风控里模型的首要任务不是把准确率刷到99%而是让排序能力稳定、可解释性在线。逻辑回归的预测公式是P1/(1e^-z)每个特征的系数直接对应风险方向业务和监管都能看懂。我一般的做法是先跑一版逻辑回归把AUC和KS量出来如果效果不够再上LightGBM或XGBoost。表格类数据上的经验是树模型通常能把AUC再往上推几个点但代价是解释性变差线上特征分布一旦漂移树模型飘得比逻辑回归更快。如果两个模型的KS差距在0.03以内我倾向选逻辑回归因为后期维护成本低出问题也容易定位。深度学习算法在这种结构化表格数据上不一定占优势。信贷特征大多是离散且含义明确的字段树模型对特征交互的建模已经足够稳深度学习在表格数据上反而容易过拟合而且在风控场景解释成本很高。所以这个项目主线的算法组合逻辑回归加LightGBM是合理且常见的搭配。3.2 训练脚本与核心参数交叉验证、样本权重与早停train.py的核心是分层交叉验证。样本不均衡时普通KFold可能让某一折全抽到好客户导致验证分数忽高忽低。StratifiedKFold按类别比例分层保证每折的正样本比例和整体一致。import lightgbm as lgb import joblib import numpy as np from sklearn.model_selection import StratifiedKFold from sklearn.metrics import roc_auc_score train_feat pd.read_csv(data/train_featurized.csv) y pd.read_csv(data/train.csv)[default_flag] X train_feat[feature_columns].copy() print(正样本占比:, y.mean()) model lgb.LGBMClassifier( objectivebinary, learning_rate0.05, num_leaves31, max_depth5, min_child_samples20, scale_pos_weight3, n_estimators500, verbosity-1, random_state42, ) skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) auc_scores [] for fold, (tr_idx, va_idx) in enumerate(skf.split(X, y)): tr_X, va_X X.iloc[tr_idx], X.iloc[va_idx] tr_y, va_y y.iloc[tr_idx], y.iloc[va_idx] model.fit( tr_X, tr_y, eval_set[(va_X, va_y)], callbacks[lgb.early_stopping(50), lgb.log_evaluation(50)] ) pred model.predict_proba(va_X)[:, 1] auc_scores.append(roc_auc_score(va_y, pred)) print(ffold {fold}, auc{auc_scores[-1]:.4f}, best_iter{model.best_iteration_}) print(fCV AUC: {np.mean(auc_scores):.4f} ± {np.std(auc_scores):.4f})参数说明里learning_rate0.05是步长调大收敛快但容易过拟合num_leaves31控制树的复杂度LightGBM的关键参数就是它而不是max_depthmin_child_samples20限制叶子节点最少样本数防止树把单个异常样本学进去scale_pos_weight是对正样本的权重放大倍数先跑一下y.mean()如果违约率是25%负样本数是正样本的3倍这里就设3左右如果违约率只有5%这个值要调到接近19。n_estimators500是最大迭代数配合early_stopping(50)表示验证集AUC连续50轮不提升就停最后best_iteration_记录的是最佳迭代步数。eval_set里的验证集专门用来做早停判断不会参与梯度更新。训练完成后把模型和特征信息一起保存部署阶段直接加载。bundle { model: model, features: feature_columns, income_upper: income_upper, } joblib.dump(bundle, deploy/model.pkl)model.pkl里装的其实是一个字典除了模型本身还有特征列表和训练时用到的截断值。没有这些元信息部署时根本不知道线上数据该怎么预处理。3.3 评估指标AUC、KS与混淆矩阵在风控里的读法因为违约样本占比通常只有3%到8%准确率这个指标在风控里基本没有参考价值。全都预测“不违约”准确率也能到90%以上但坏人一个都没抓住。AUC衡量的是排序能力随机抽一个违约样本和一个正常样本模型把违约样本排在前面的概率。KS是累计坏样本率减累计好样本率的最大值表示模型对好坏客户的最大区分度。KS大于0.3算可用0.4以上不错超过0.6就要警惕过拟合。def ks_score(y_true, y_pred): df pd.DataFrame({y: y_true, p: y_pred}) df df.sort_values(p, ascendingFalse).reset_index(dropTrue) df[cum_bad] (df[y] 1).cumsum() df[cum_good] (df[y] 0).cumsum() total_bad df[cum_bad].max() total_good df[cum_good].max() return max(df[cum_bad] / total_bad - df[cum_good] / total_good)把预测概率按从高到低排序依次累加坏样本和好样本的占比两者的差最大值就是KS。这个值对应风控里的一个直观场景拒绝概率最高的20%客户能抓回多少坏账。混淆矩阵的作用是定阈值。模型输出的是一个概率线上系统需要一个明确的“通过还是拒绝”的结论。常见做法是先按模型分从低到高排序结合业务要求的通过率倒推阈值再计算这个阈值下的精确率和召回率而不是死守0.5这个默认值。4. 模型部署把训练好的违约预测模型变成可调用的接口模型训练完不算完能被人调用才算落地。部署文件是压缩包里最容易被忽略的部分很多人训练完就交差了但实际项目里后端要拿这个模型给每个申请实时打分。4.1 部署文件有什么模型产物、接口服务和依赖清单部署目录deploy下一般有app.py、model.pkl和requirements.txt。requirements.txt不需要手工写版本号本地环境装完依赖后执行pip freeze就能固下来。fastapi uvicorn joblib pandas numpy lightgbm安装依赖用pip install -r requirements.txt版本以实际冻结的为准。模型产物建议用一个字典打包保存这样特征列表、截断值、WOE映射表都跟着模型走避免部署时到处找训练脚本。age_bins [18, 25, 35, 45, 60, 80] bundle { model: model, features: feature_columns, income_upper: income_upper, age_bins: age_bins, woe_map: woe_map, } joblib.dump(bundle, deploy/model.pkl)age_bins是训练时定的分箱边界woe_map是每个箱对应的WOE值。部署脚本加载bundle后用同一套边界和映射表处理新数据保证线上口径和训练一致。4.2 用FastAPI暴露预测接口加载模型、做特征工程、返回概率接口服务用FastAPI写是常见做法pydantic负责请求体校验字段缺失时直接返回422不会让脏数据进到模型里炸掉。import joblib import numpy as np import pandas as pd from fastapi import FastAPI from pydantic import BaseModel bundle joblib.load(deploy/model.pkl) model bundle[model] feature_columns bundle[features] income_upper bundle[income_upper] age_bins bundle[age_bins] woe_map bundle[woe_map] app FastAPI() class LoanApplication(BaseModel): age: float income: float loan_amount: float interest_rate: float credit_score: float debt_to_income_ratio: float def build_features(raw: dict) - pd.DataFrame: x pd.DataFrame([raw]) x[income_log] np.log1p(x[income].clip(upperincome_upper)) x[age_bin] pd.cut(x[age], binsage_bins, labelsFalse) x[age_woe] x[age_bin].astype(float).map(woe_map) return x[feature_columns] app.post(/predict) def predict(data: LoanApplication): x build_features(data.model_dump()) prob model.predict_proba(x)[0, 1] return { probability: round(prob, 6), risk_level: high if prob 0.5 else low }build_features函数负责把请求里的原始字段先转成训练时的特征形态。income_log用的是截断加对数age_bin和age_woe用的是分箱和WOE映射。如果特征列表里有字段没生成最后x[feature_columns]会抛KeyError这反而是好事能提前暴露字段缺失。pydantic v2里取请求数据用model_dump如果项目还在用pydantic v1把model_dump换成dict就行。接口启动命令行如下uvicorn deploy.app:app --host 0.0.0.0 --port 8000host设成0.0.0.0是为了让容器或局域网内其他服务能访问到接口port按需调整。如果部署到服务器上常见做法是把deploy目录打进Docker镜像再用docker run映射端口。4.3 上线校验一致性测试与压测接口写完后上线前必须做一致性校验。拿训练集里几条样本先本地用predict.py跑一遍概率再调接口拿概率两者必须完全一致。任何微小差异都说明请求处理和训练时的特征工程有出入。import requests payload { age: 35, income: 18000, loan_amount: 120000, interest_rate: 7.5, credit_score: 720, debt_to_income_ratio: 0.3, } resp requests.post(http://127.0.0.1:8000/predict, jsonpayload) print(resp.status_code, resp.json())如果返回200且概率和本地脚本一致说明接口链路通了。压测一般用locust模拟并发达标观察p99延迟和错误率。风控实时调用通常要求几百毫秒内返回如果模型大导致单次推理太慢常见做法是把模型特征裁剪或改成批量打分而不是硬扛。5. 个人贷款违约预测实战避坑最容易翻车的5个地方这部分是我做风控项目几年下来最想提前说的。模型失败绝大多数时候不是因为算法选错而是掉进下面这些坑里。5.1 数据泄露AUC 0.99的模型上线就翻车现象训练和验证AUC都到0.98团队觉得捡到宝了结果一上线预测完全乱套。原因特征里混进了目标变量的“泄密字段”常见的像“当前逾期次数”“最近一次还款是否成功”这类信息在申请时点根本拿不到或者是结果本身的一部分。模型学的不是规律而是答案。解决把每个字段的产生时点写出来凡是样本申请之后才会生成的字段一律剔除。我有一个很实用的验证方法逐个删除字段看AUC波动如果删掉某个字段后AUC大幅下跌就把它单独拎出来确认业务上是否真的能在申请时点取到。5.2 特征分布漂移上线三周模型越跑越差现象上线前两周KS正常第三周开始明显下滑坏客户的预测分数整体偏低。原因客群结构变了。比如获客渠道从高薪白领换成了大学生收入、负债、学历分布全部偏移模型还在用旧分布预测新客群。解决上线时把训练集每个特征的均值、分位数保存成json每周对新样本算一遍同样的统计量计算PSI。PSI大于0.1要关注超过0.25就要准备重训。没有一劳永逸的办法只能监控加定期重训。5.3 无脑过采样SMOTE把好客户误杀成坏客户现象用SMOTE后训练集AUC涨了0.05上线后正常客户被拒率明显上升。原因SMOTE在好客户和坏客户的连线上合成新样本造出来的边界不是真实业务边界。信贷数据噪声本来就大合成样本越多模型越容易把边缘好客户推成高风险。解决优先用scale_pos_weight或class_weight这是不改变样本分布的轻量方案。如果一定要过采样把SMOTE放在交叉验证的每一折内部做只对训练折合成验证折保持真实分布否则验证分数会被污染。5.4 拿准确率汇报95%准确率的模型在风控里等于废了现象项目周报写“模型准确率95%”业务认可上线后坏账没降。原因违约率只有4%全部预测“不违约”准确率就是96%。准确率被多数类绑架了模型实际一个坏客户都没抓到。解决AUC和KS看排序能力精确率和召回率看不同阈值下的审批效果。汇报口径应该写成“通过率70%时能抓回多少坏客户整体违约率是多少”这才是业务听得懂也经得起追问的指标。5.5 同一个人的多笔贷款被切进训练和验证集现象交叉验证AUC高线上效果对不上排查发现训练集和验证集里出现了同一个客户ID。原因贷款数据里一个人可能有多笔借款如果只按行随机切分同一个客户的样本会同时落到训练集和验证集。验证集等于偷看了训练集里的个人特征分数虚高。解决先按客户ID去重再切分客户集合最后用客户ID取样本。from sklearn.model_selection import train_test_split customers data[customer_id].unique() tr_cust, va_cust train_test_split(customers, test_size0.2, random_state42) tr_data data[data[customer_id].isin(tr_cust)] va_data data[data[customer_id].isin(va_cust)]这样能保证同一个客户的所有样本完全落在同一侧验证集的结果才有参考价值。6. 进阶用SHAP把“为什么拒绝”解释给业务听6.1 从黑匣子到可解释SHAP特征归因的落地用法模型上线后业务问得最多的一句话是“这个客户为什么被拒”。人工复核时如果没有可解释的信息业务同学不敢闭环处理合规上也过不去。SHAP能算出每条预测里每个特征贡献了多少把所有贡献加总等于模型的预测分数是给树模型做解释最常用的工具。import shap explainer shap.TreeExplainer(model) shap_values explainer.shap_values(va_X.head(100)) shap.summary_plot(shap_values, va_X.head(100))summary_plot的横轴是SHAP值颜色深浅代表特征值高低。一眼看过去哪个字段把客户推向高风险、方向是否符合业务直觉都清清楚楚。如果收入越高反而SHAP值推高违约概率先别急着怀疑模型回头查收入字段是不是在清洗时填错了方向。这个动作要把特征的业务方向一条条过一遍全过完才能安心上线。如果用的是逻辑回归解释其实更简单直接看系数表就行。如果用的是LightGBM我给每条被拒绝的样本输出概率最高的top3贡献特征打到审批备注里。人工复核的人看到的是“负债收入比过高、信用分偏低、近期查询次数多”这样可读的信息而不是一个冰冷的概率数字。我现在的习惯是任何模型上线前先把SHAP跑一遍把特征方向对着业务同学一条一条解释过去。被问住的地方十有八九就是上线后会出问题的地方。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

基于WebSocket的跨平台私人远程桌面工具:毕业设计实战与避坑指南 2026/10/1 11:14:12

基于WebSocket的跨平台私人远程桌面工具:毕业设计实战与避坑指南

简介:这是一套面向计算机相关专业毕业设计场景的跨平台私人远程桌面工具完整源码,基于Java AWT、SpringBoot与websocket技术构建,适合正在准备毕设或希望深入理解远程控制原理的开发者参考。项目实现了鼠标键盘模拟、远程执行DOS命令、远程关…

阅读更多 →
Vscode Remote-SSH下Jupyter内核CUDA失效的完整修复指南 2026/10/1 11:14:12

Vscode Remote-SSH下Jupyter内核CUDA失效的完整修复指南

你有没有过这种经历:明明在命令行里用CUDA跑模型一切正常,一换到 Vscode Remote-SSH 打开的 Jupyter Notebook 里,同一段代码的torch.cuda.is_available()就稳稳当当返回False?我当时遇到这个问题,第一反应是怀疑 SSH …

阅读更多 →
前缀和算法全攻略:从一维到二维、差分与哈希表组合技巧 2026/10/1 11:14:12

前缀和算法全攻略:从一维到二维、差分与哈希表组合技巧

提到区间查询、子数组统计这类问题,很多算法题解里都会带一笔“可以用前缀和优化”,但到底为什么能优化、优化在哪一步,不少人其实是一知半解的。我自己刚开始刷题那阵子也这样——看答案觉得前缀和特别巧妙,真到自己写的时候又总…

阅读更多 →
掌握AI大模型,开启高薪职业新篇章!收藏必备的技术风向标 2026/10/1 11:14:12

掌握AI大模型,开启高薪职业新篇章!收藏必备的技术风向标

AI大模型正成为未来5年程序员的最佳技术发展方向。华为、美团、阿里等大厂纷纷布局,相关岗位需求激增,年薪突破百万。想在大模型领域立足,不仅需要懂prompt调用,更要掌握Fine-tuning、Agent、RAG等技术,并结合业务场景…

阅读更多 →
Spring Boot+Vue美食网站开发:从数据库设计到前后端分离部署实战 2026/10/1 11:13:58

Spring Boot+Vue美食网站开发:从数据库设计到前后端分离部署实战

1. 项目概述与核心功能拆解看到“Java基于Spring BootVue的美食网站的设计与实现”这个题目,我第一反应是:这应该是课程设计或毕业设计级别的项目,但它又跟那些换壳的“某管理系统”不太一样。美食网站本质上是电商系统的迷你版,用…

阅读更多 →
从单体事务到TCC、Saga与最终一致性:分布式事务选型指南 2026/10/1 11:13:58

从单体事务到TCC、Saga与最终一致性:分布式事务选型指南

很多年过去,我依然记得那个深夜:订单服务创建订单成功,库存服务扣减库存失败,但订单已经写入了数据库。用户在页面上看到“下单成功”,仓库里却永远找不到这件货。那一晚之后,我开始认真研究分布式事务&…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉