简介:面向高校计算机相关专业毕业设计与大作业场景,这份基于机器学习的贷中风险预测项目包含完整建模流程、源码、数据文件、配套文档与答辩演示,适合需要完成同类课题或进行实战练习的学生。资源共50个文件,压缩包约11MB,核心包括19个Python脚本、5个Notebook分析笔记、11个CSV数据文件、4份Word文档、3个Excel结果表及2套PPT,覆盖数据预处理、特征工程、模型训练与评估、结果导出等完整环节。已有62人学习使用。项目经导师指导并获高分,参考金融大数据建模挑战赛赛题整理,源码经本地编译调试可运行,内容涵盖数据清洗、特征衍生、主流模型比对与结果分析,并提供答辩展示材料,便于快速理解贷中风险预测的实现路径和项目组织方法,作为课程设计或毕业设计参考可显著节省从零搭建的时间。
1. 贷中风险预测模型到底在预测什么:为什么回测很好看的模型一上线就衰减
贷中风险预测,是信贷场景里比贷前评分卡更吃数据功底的建模任务:贷前你只能拿到申请资料和外部征信,贷中手里却握着客户过去半年到一年的真实还款行为。信息多了,陷阱也多了。我见过不少同学用 python 基于机器学习的贷中风险预测模型,回测 KS 做到 0.4 以上,上线三个月 KS 直接跌到 0.2,问题往往不是模型选得不好,而是标签定义穿越、样本切分不干净。这里把这类项目从样本窗口、标签、特征、训练、评估到上线的完整链路捋清楚,适合正在做贷中行为评分卡建模的从业者,也适合拿这个题目做毕业设计或答辩项目的同学。希望在动手之前,你能先避开那些最贵的坑。
2. 先定样本窗口和标签:观察点、表现期与坏客户定义
2.1 三个时间窗口先对齐:观察点、观察期、表现期
贷中风险预测最常犯的错,是还没把时间窗口画清楚就急着跑模型。所谓贷中,是客户已经在贷、额度已经放出去之后,每个月跑批一次,判断这个客户在未来一段时间内会不会变坏。我们不是拿"历史所有流水"直接建模,而是把账务历史切成三块:观察点、观察期、表现期。
观察点(T)是每个样本的评价时刻,通常取每个自然月的月底;观察期是 T 之前的一段时间,比如 12 个月,这里取特征;表现期是 T 之后的 3 到 6 个月,这里打标签。为了增加样本量,常见做法是把每个账户的每个月度快照都当成一个样本,一个用款两年的客户最多贡献二十多个样本。核心原则只有一条:特征只能来自观察点之前,标签只能来自观察点之后,中间不允许交叉。
先确认 python 环境里 pandas、numpy 这些基础包是齐的,没装好的先把 python 开发环境搭出来再往下走。下面这个函数生成最基础的样本表,注意 obs_months 传入你选定的那一批观察点月份。
import pandas as pd def build_sample_base(acct_month, obs_months, perf_months=3): """ acct_month: 账户月度快照,字段至少包含 account_id, month obs_months: 观察点列表,例如 2022-01 ~ 2022-10 各月 perf_months: 表现期月数,贷中一般取 3 """ df = acct_month[acct_month['month'].isin(obs_months)].copy() df['obs_month'] = df['month'] # 观察期起点:观察点往前推 12 个月 df['feat_start'] = pd.to_datetime(df['obs_month']) - pd.DateOffset(months=11) # 表现期终点:观察点往后推 perf_months 个月 df['perf_end'] = pd.to_datetime(df['obs_month']) + pd.DateOffset(months=perf_months) return dfobs_month、feat_start、perf_end 三个字段就是后面所有特征和标签的"时间围栏"。观察期取 12 个月是贷中建模的经验值:太短特征不稳定,太长早期行为对当前风险的解释力已经衰减,还会混入大量政策变化前的旧行为。如果手上数据只有半年,退而求其次用 6 个月也行,但特征含义要相应调整,"近12月最大逾期天数"这类特征就别硬造了。
2.2 好坏样本的判定规则:别用"是否逾期过"当标签
很多项目直接把"客户历史上有没有逾期"当 y,这是非常危险的标签定义。一个账户用了三年,第三年才逾期,如果观察点在第一年,这个样本其实是个好样本。所以贷中标签必须绑定表现期:观察点之后、表现期内,客户是否发生过指定程度的逾期。
逾期程度用 M 几来描述:M0 是未逾期,M1 是逾期 1 到 30 天,M2 是逾期 31 到 60 天,M3 是逾期 61 到 90 天。贷中行为评分卡一般把"表现期内最大逾期天数 >= 30 天"即 M2+ 作为坏样本。原因有两个:M1 噪声太大,很多人因为忘还、扣款失败晚了一两天,不等于真实风险;M2+ 已经能反映还款意愿或还款能力的实质性恶化。如果样本量足够,可以再尝试 M3+ 做稳健性测试,对比标签口径对模型排序能力的影响。
还需要做样本排除:观察点当天已经处于 M2+ 的账户不进样本;表现期内已经结清、核销、转人工催收的账户,要么排除,要么单独打标。这些"已经在坏状态"的账户混进来,等于把答案写进了训练集。下面这段代码用逾期流水表生成标签。
def assign_label(sample_base, overdue_flow, perf_months=3): """ overdue_flow: 账户-字段,至少包含 account_id, month, max_overdue_days 返回在表现期内是否发生 M2+(>=30天) """ start = pd.to_datetime(sample_base['obs_month']) end = start + pd.DateOffset(months=perf_months) # 只保留 (obs_month, perf_end] 区间的逾期记录 flow = overdue_flow[(overdue_flow['month'] > start) & (overdue_flow['month'] <= end)] worst = flow.groupby('account_id')['max_overdue_days'].max() sample_base['y'] = sample_base['account_id'].map(worst).fillna(0) sample_base['y'] = (sample_base['y'] >= 30).astype(int) return sample_base两个容易忽略的点。第一,month 要统一存成日期类型,不要用 int 类型的 202201,否则区间比较会出乱子。第二,worst 取最大值而不是平均值,贷中风险是非对称的,客户只要坏一次就该被识别。标签定完之后务必看坏样本率:整体坏样本率低于 2%,后面要处理样本不均衡;高于 15%,大概率是观察点之前已经坏掉的客户混进来了,回去查排除逻辑。
2.3 特征工程:先复现这几组被验证过的行为特征
特征工程最容易写长也最容易骗自己。我的做法是先做一组被验证过有效的行为特征,再根据业务理解增量,而不是一上来堆几百个特征。贷中特征一般分四类:
| 特征组 | 特征示例 | 业务含义 | 稳定性注意 |
|---|---|---|---|
| 还款行为 | 近3月M1次数、近6月最大逾期天数、近6月还款率 | 直接刻画还款意愿和能力,排序最强 | M1次数容易受扣款日影响,分箱时单独处理0箱 |
| 额度使用 | 当前额度使用率、近6月平均使用率、使用率波动系数 | 使用率突然飙升往往是资金链紧张信号 | 节假日前后波动大,注意看PSI |
| 负债与多头 | 近1月贷款审批查询次数、近3月新增大额负债 | 反映外部负债压力 | 依赖征信数据源,缺失率要排查 |
| 行为稳定性 | 还款金额变异系数、还款日偏移天数均值 | 行为紊乱度,辅助信号 | 新客户数据短,缺失用中位数填充 |
下面计算两个最基础也最常用的特征,后续所有特征都可以按同样套路叠加。关键点:所有计算严格约束在 feat_start 到 obs_month 之间。
def gen_repay_feats(sample_base, repay_flow): """ repay_flow: 账户-月还款流水,需要 account_id, month, due_amt, repay_amt, overdue_days 生成近3月M1次数、近6月还款率 """ res = sample_base.copy() s = pd.to_datetime(res['obs_month']) - pd.DateOffset(months=2) m3_m1 = (repay_flow[(repay_flow['month'] >= s) & (repay_flow['overdue_days'].between(1, 30))] .groupby('account_id').size()) res['m1_cnt_3m'] = res['account_id'].map(m3_m1).fillna(0) s6 = pd.to_datetime(res['obs_month']) - pd.DateOffset(months=5) f6 = repay_flow[(repay_flow['month'] >= s6)] f6 = f6.groupby('account_id').agg( due_sum=('due_amt', 'sum'), repay_sum=('repay_amt', 'sum') ) res['repay_rate_6m'] = res['account_id'].map( (f6['repay_sum'] / f6['due_sum'].replace(0, np.nan)) ).fillna(0) return res参数说明:近 3 月 M1 次数在实操里通常非常稀疏,大量客户取 0,分箱时要让第 0 箱单独存在;还款率分母为 0 用 NaN 填充再补 0,避免出现无穷值。这类特征可解释性强,贷中策略端非常看重这一点。特征做完后先跑一遍缺失率和取值分布,缺失率高于 80% 的直接丢掉,后续建模会省事很多。
3. 从逻辑回归到 LightGBM:用 python 机器学习常用包跑通建模流程
3.1 选型逻辑:为什么贷中主线用逻辑回归,LightGBM 做交叉验证
贷中模型的主流选择是逻辑回归,不是因为它效果最好,而是因为稳定、可解释、好上线。监管和内部审计要求你能说清楚每个特征的系数方向,逻辑回归天然满足这个要求;贷中数据量通常几十万样本起步,特征经过 WOE 编码后线性可分性也不错,逻辑回归的 KS 并不比树模型差太多。
用 python 机器学习常用包跑这套流程,核心是 sklearn 加 LightGBM 两类:sklearn 提供逻辑回归、KS 计算和网格搜索,LightGBM 负责验证"非线性关系还能不能再挖一点"。我一般把 LightGBM 当影子模型:如果它的验证集 AUC 比逻辑回归高很多,说明还有重要的非线性特征没挖出来;如果只高 0.01,那逻辑回归已经够用,直接上线更省心。
3.2 数据切分:按客户和时间切,不是随机切
贷中样本里同一个客户会出现很多次,如果随机切分,同一客户的不同月份样本会同时出现在训练集和验证集里,验证集 AUC 会虚高 0.05 到 0.1。正确做法是按客户分组切分,并尽量让验证集在时间上晚于训练集,模拟"用上个月学到的规律预测下个月"的真实场景。
实操上我推荐两段式切分:先按时间切出最后 20% 月份作为测试集,剩余样本按客户分组做 5 折交叉验证调参。测试集最后只评估一次,所有调参决策都基于验证集。
from sklearn.model_selection import GroupKFold from sklearn.linear_model import LogisticRegression # 先按时间切测试集 train_df = df[df['obs_month'] <= '2022-08-31'] test_df = df[df['obs_month'] > '2022-08-31'] X_cols = [c for c in train_df.columns if c.startswith('feat_')] y_col = 'y' # 按客户分组做交叉验证,避免同一客户泄露 gkf = GroupKFold(n_splits=5) for fold, (train_idx, val_idx) in enumerate( gkf.split(train_df, train_df[y_col], groups=train_df['account_id']) ): Xtr, Xval = train_df.iloc[train_idx][X_cols], train_df.iloc[val_idx][X_cols] ytr, yval = train_df.iloc[train_idx][y_col], train_df.iloc[val_idx][y_col] model = LogisticRegression(max_iter=500) model.fit(Xtr, ytr) # 这里记录 val_auc,后续用均值对比不同特征组合注意两个细节。第一,GroupKFold 要求每个组里样本数不要差异过大,某几个客户样本太少的话,可以先按"客户-表现期"聚合后再切。第二,切分后要检查训练集和验证集的坏样本率是否接近,如果验证集坏样本率只剩 1%,说明时间切点选得不对,或者该时段贷后政策发生了大变化。
3.3 逻辑回归的 WOE 分箱与训练:让回归结果可解释
直接拿原始特征喂逻辑回归也可以,但效果和稳定性都不如分箱后做 WOE 编码。分箱让特征与坏样本率的关系变得单调、平滑,减小极端值影响;WOE 编码把每一箱映射成"该箱客户相对总体是更优还是更坏"的对数优势比。
分箱常用两种方式:等频分箱和决策树分箱。等频分箱简单,但遇到取值高度集中的特征会分出很多空箱;决策树分箱更贴近风险切点,但容易过拟合。我一般先用等频分箱看分布,再对业务关键特征(比如额度使用率)手工调整切点,保证每箱坏样本率单调。
import numpy as np def woe_encode(df, feature, target='y', bins=10): tmp = df[[feature, target]].copy() tmp['bin'] = pd.qcut(tmp[feature], q=bins, duplicates='drop') g = tmp.groupby('bin', observed=False)[target].agg(['sum', 'count']) g['bad'] = g['sum'] g['good'] = g['count'] - g['sum'] g['bad_pct'] = g['bad'] / g['bad'].sum() g['good_pct'] = g['good'] / g['good'].sum() # 避免除零 g['bad_pct'] = g['bad_pct'].replace(0, 1e-6) g['good_pct'] = g['good_pct'].replace(0, 1e-6) g['woe'] = np.log(g['good_pct'] / g['bad_pct']) g['iv'] = (g['good_pct'] - g['bad_pct']) * g['woe'] return g, g['iv'].sum()WOE 的符号要重点检查:额度使用率越高,WOE 应该越低,也就是越坏。如果某个特征的 WOE 方向和业务直觉相反,且不是特征定义写反了,这个特征不能直接进模型,否则每次评审都要解释一遍"为什么使用率更高的客户反而更安全"。IV 值参考线:小于 0.02 区分度很弱,0.02 到 0.1 中等,0.1 以上算强。贷中场景有还款行为类强特征,IV 超过 0.3 并不罕见。
WOE 编码做完后训练逻辑回归,要注意多重共线性。我习惯先算 WOE 特征相关性矩阵,绝对值超过 0.7 的两个特征只保留 IV 更大的那个;训练完再打印每个特征的系数和标准误,系数正负号异常的优先排查。
3.4 LightGBM 参数设置与早停:别一上来就 1000 棵树
LightGBM 在贷中场景里通常用来做基准对比或最终提效。参数设置给一组常用起点值,不是最优,但是能稳定出结果的范围。
import lightgbm as lgb params = { 'objective': 'binary', 'metric': 'auc', 'learning_rate': 0.03, 'num_leaves': 63, 'max_depth': 6, 'min_child_samples': 200, 'subsample': 0.8, 'subsample_freq': 1, 'colsample_bytree': 0.8, 'reg_alpha': 0.1, 'reg_lambda': 1.0, 'verbose': -1, 'seed': 42 } dtrain = lgb.Dataset(Xtr, label=ytr) dvalid = lgb.Dataset(Xval, label=yval, reference=dtrain) model = lgb.train( params, dtrain, num_boost_round=2000, valid_sets=[dvalid], callbacks=[lgb.early_stopping(100), lgb.log_evaluation(50)] )| 参数 | 常用范围 | 作用与注意点 |
|---|---|---|
| learning_rate | 0.01~0.05 | 调低通常涨点,但训练轮数要增加数倍 |
| num_leaves | 31~127 | 叶子数太大容易过拟合,用 max_depth 配合限制 |
| max_depth | 4~8 | 不是越大越好,小样本特别容易崩 |
| min_child_samples | 100~500 | 贷中样本量大,设 100 以下容易学到极少数客户的噪声 |
| subsample / colsample_bytree | 0.7~0.9 | 行采样和列采样,双 0.8 起步 |
| reg_alpha / reg_lambda | 0.1~10 | 特征多时加大正则,特征少时作用不明显 |
early_stopping 轮数我习惯设 100,别设 500,否则浪费训练时间还容易过拟合。LightGBM 结果还要和逻辑回归对比:只高一点点的话,上线成本高、可解释性差,不值得换;AUC 高出 0.03 以上,就可以考虑双模型并行,线上用逻辑回归做基础决策,LightGBM 做辅助排序。
4. 模型评估与上线前检查:KS、PSI、过拟合三个关口
4.1 KS 和 AUC 怎么算,贷中阈值怎么定
贷中模型评估不看准确率,看排序和区分度。最常用的两个指标是 KS 和 AUC。KS 是累计坏样本率与累计好样本率差值的最大值,刻画"按分数排序后,好坏客户被分开的最大程度";AUC 本质是随机抽一个好客户和一个坏客户,模型把坏客户排在好客户前面的概率。
贷中的经验线是:验证集 KS 大于 0.3、AUC 大于 0.75,模型有可用价值;KS 低于 0.25,要么特征没做透,要么好坏标签定义有问题。如果训练集 KS 0.45、验证集 KS 0.3,过拟合已经很明显了。
from sklearn.metrics import roc_auc_score def ks_score(y_true, y_prob): df = pd.DataFrame({'y': y_true, 'p': y_prob}) df = df.sort_values('p', ascending=False).reset_index(drop=True) df['cum_bad'] = df['y'].cumsum() / df['y'].sum() df['cum_good'] = (1 - df['y']).cumsum() / (1 - df['y']).sum() return (df['cum_bad'] - df['cum_good']).max() ks = ks_score(y_val, y_prob) auc = roc_auc_score(y_val, y_prob) print(f"KS: {ks:.4f}, AUC: {auc:.4f}")阈值怎么定?贷中输出预测概率后,把验证集按概率从高到低排序,画出"通过率-坏账率"曲线。业务方会给出目标坏账率,比如月度贷中坏账率控制在 1.5%,你反查对应的通过率和分数切点。一般决策不是"概率大于 0.5 就拒绝",而是"拒绝预测风险最高的一批客户",比如取分数最低的 5% 或 10% 客户。这样做业务量可控,模型影响容易被观察。
4.2 PSI:模型上线前的特征稳定性测试
KS 好不等于能上线。上线后面对的客户分布和训练时不一样,贷中客户会随准入政策、额度策略变化。PSI 用来度量两个分布偏移程度。常用线:小于 0.1 稳定,0.1 到 0.25 有偏移需要关注,大于 0.25 分布发生重大变化,基本需要重建或重训。
def psi_score(expected, actual, bins=10): expected = pd.Series(expected).dropna() actual = pd.Series(actual).dropna() # 以 expected 的分位数为箱边界,固定切分规则 edges = np.quantile(expected, np.linspace(0, 1, bins + 1)) edges[0], edges[-1] = -np.inf, np.inf e_cut = pd.cut(expected, bins=edges, include_lowest=True) a_cut = pd.cut(actual, bins=edges, include_lowest=True) e_cnt = e_cut.value_counts().sort_index() a_cnt = a_cut.value_counts().sort_index() e_pct = e_cnt / e_cnt.sum() a_pct = a_cnt / a_cnt.sum() a_pct = a_pct.reindex(e_pct.index, fill_value=1e-6) psi = ((e_pct - a_pct) * np.log(e_pct / a_pct)).sum() return psi这里最容易出错的是分箱方式:一定先用训练集分布定箱边界,再用同一批边界去切线上分布,否则两边的箱边界不一致,PSI 会被高估。如果报 "Bin edges must be unique",说明变量大量重复值,先对 expected 做轻微扰动或改用 rank 分位。在特征工程阶段就可以用 PSI 做特征筛选,剔除掉"近1天还款次数"这种预测能力尚可但天然不稳定的特征,否则模型上线后每周都在报警。
4.3 过拟合诊断:训练集和验证集差距多大要警惕
过拟合的常规判断是训练集和验证集指标差距。AUC 或 KS 差距在 0.05 以内属于正常泛化损耗,0.05 到 0.1 要警惕,超过 0.1 基本就是模型记住了训练数据里的噪声。出现这种情况,优先调低模型复杂度,而不是继续加特征或加大训练轮数。
实操上我会把训练集和验证集的 KS 画在同一张图上,一眼就能看出差距。LightGBM 的 feature_importance 也要检查:如果排名最靠前的特征里有明显的时间属性,比如"最近5分钟还款次数"这种快到分钟级的变量,赶紧拿掉。贷中模型的特征必须经得起跨时间的稳定性考验,时间粒度太细的特征天然不稳。
# 输出 LightGBM 特征重要性,按 gain 排序 imp = pd.Series( model.feature_importance(importance_type='gain'), index=X_cols ).sort_values(ascending=False) print(imp.head(10))如果逻辑回归模型,则直接看系数绝对值排名,并核对前 5 个特征的方向是否符合业务直觉。贷中模型不需要上百个特征,20 到 40 个经过筛选的、方向明确的特征,往往比 300 个特征堆出来的模型更耐打。
5. 避坑:贷中建模最容易被坑的 5 个位置
5.1 特征穿越:回测 KS 很高,上线直接失效
现象:回测 KS 做到 0.5 以上,上线一个多月 KS 直接跌到 0.2 以下。
原因:特征或标签里混入了观察点之后的信息,也就是特征穿越。最常见的是用逾期流水生成特征时过滤条件写错月份,把表现期的数据也统计进去了;还有一种是把"客户当前是否 M2+"当特征,这在某个时间点是已知状态,但在观察点当天其实是未来信息。
解决:把观察期和表现期的日期围栏写成全局变量,任何特征代码都从 sample_base 的 feat_start、obs_month、perf_end 三个字段取时间,不手写月份。建模前做一次"答案注入检测":把表现期内的标签信息,比如表现期内最大逾期天数,临时伪装成特征放进模型,如果这个伪特征重要性排第一,说明时间围栏有漏洞,回去查数据链路。
5.2 同客户多月份样本串组,验证集虚高
现象:随机切分后验证集 AUC 比按时间切高出 0.08,看起来模型很棒,上线后打五折。
原因:同一个客户在训练集和验证集里都有样本,模型实际上在用该客户前几个月的行为预测后几个月的标签,客户个体信息泄漏到验证集里。
解决:所有交叉验证都用 GroupKFold 按 account_id 分组;测试集必须晚于训练集,且测试集客户不得出现在训练集里。代码上只需要把 random split 全部替换成 group-aware split,方案在 3.2 节已经给了。很多开源源码包默认用的是随机切分,拿到别人代码第一步就是改这个。
5.3 已逾期客户进样本,等于把答案写进卷子
现象:标签坏样本率高达 25%,模型 KS 很高,但策略人员一看就说模型不能用。
原因:没有剔除观察点当天已经在逾期的账户。这些账户在表现期内大概率继续坏,而且坏的状态在观察点就已知,预测它们没有意义,只是放大了模型的区分度。
解决:观察点当天逾期天数大于等于 30 的样本直接剔除,只留"当时还是好的、后来变坏"的样本。贷中模型的目标永远是识别从好变坏的趋势,而不是识别已经变坏的客户。识别后者是催收分群要解决的事,不是风险预测。
5.4 准确率陷阱:99% 的准确率照样没用
现象:项目汇报时展示准确率 98%,评审问 KS 和 AUC,答不上来。
原因:贷中正样本率通常只有 3% 到 5%,全预测"不会逾期"准确率也有 95% 以上,准确率在这个场景里没有区分能力。
解决:汇报指标统一用 KS、AUC、Recall、Precision@Top5% 这类。Top5% 查全率的含义是"按预测风险从高到低拒绝最危险的 5% 客户,能覆盖多少真实坏客户",这个指标比准确率直观得多,业务和评审都能听懂。源码包里如果只算了 accuracy,记得自己补上这批指标。
5.5 上线后不监控,模型是被策略"用废"的
现象:模型上线半年后回测 PSI 0.35,重新训练收益也不明显,有人开始怀疑模型本身不行。
原因:线上策略在模型分数之外还叠加了人工规则和额度调整,实际流入模型的客户分布早就不是训练时那个分布;长期不重训,排序能力自然会退化。
解决:上线前把分数分布存档,上线后每周跑一次分数 PSI 和特征 PSI,大于 0.25 就发警告;同时记录"模型命中率"和"策略实际拒绝率"两条指标。分数分布漂移往往先于坏账率恶化出现,监控脚本应当和模型部署一起交付,不能等出了事再写。
6. 从影子运行到答辩 PPT:把模型讲清楚比调参数更值钱
6.1 影子运行:先双跑一个月再说
新模型上线前,我习惯让它跟老模型并行跑三十天,只记录分数和决策结果,不实际干预业务。这段时间用来观察新模型的分数分布是否稳定,以及它挑出的高风险客户是不是业务想拒的那批。影子运行期不需要复杂平台,一份脚本每天把新老模型分数拉出来做 diff 就够。
def shadow_check(old_score, new_score, top=0.05): old_top = set(old_score.nsmallest(int(len(old_score) * top)).index) new_top = set(new_score.nsmallest(int(len(new_score) * top)).index) print('top5%变化率:', len(old_top ^ new_top) / len(old_top))影子运行不只是技术验证,也是给业务方的信心证据。连续 30 天风险人群重叠率稳定,才敢切实际策略。
6.2 答辩 PPT 和高分项目文档怎么讲
拿现成源码和文档做答辩的同学,最容易把 PPT 写成 API 文档。评审更想听的是判断过程:为什么坏样本定义为 M2+?为什么观察期取 12 个月?为什么不用随机切分?每一个选择看一眼源码都知道,但"为什么这么选"才是项目含金量。
我的建议是 PPT 按四页讲主线:第一页,样本定义和时间窗口图;第二页,坏样本率、样本量、特征 IV 分布表;第三页,模型评估,训练集验证集测试集的 KS/AUC 对比和阈值切分依据;第四页,上线后的稳定性监控计划。文档里把第 5 章那类踩坑记录写进去,比漂亮的正样本率更让评审信服——真正的风控项目,都是在坑里爬出来的。
这套贷中风险预测模型的完整链路,从时间窗口到上线监控,每一步都比调参更值得花时间。做完之后最大的收获,是你会对"哪些信息在观察点当天真的已知"产生肌肉记忆,这个判断能力比任何模型里的超参数都值钱。希望帮到你。
本文还有配套的精品资源,点击获取