简介:用3万条训练样本和2万条测试样本的拍拍贷脱敏数据,完整复现风控建模闭环。从原始数据读取开始,依次完成缺失值与异常值探索分析(附missing_analysis.png可视化)、用户基础属性与网络行为特征加工、IV/WOE转换与相关性筛选,再到LightGBM单模型训练与超参调优,最后叠加Bagging集成提升稳定性。配套core_card.py封装评分卡逻辑,main.py提供端到端运行入口,所有Jupyter Notebook(data_input、EDA清洗、特征处理、筛选、单模型、Bagging)均支持直接执行。项目报告详述AUC、KS、PSI等关键指标表现,requirements.txt明确依赖版本,LICENSE和README保障合规使用。适合风控算法教学、课程设计或魔镜杯等竞赛快速复现。
1. 项目概述:这不是一个“跑通就行”的Demo,而是一套可落地的风控建模最小闭环
你手头拿到的这个“拍拍贷违约预测实战包”,表面看是一堆Jupyter Notebook和几个Python脚本,但实际它是一条被反复打磨过的、从原始数据到业务可用模型的完整流水线。我带过三届高校风控算法实训课,也帮两家中小消费金融公司做过模型迁移支持,见过太多学生把LightGBM当黑箱调参——AUC刷到0.78就欢呼,一上线PSI飙到0.35被风控策略组连夜叫停。这个包的价值,恰恰在于它不回避真实风控场景里的脏、慢、险:缺失值不是简单用均值填,而是分字段类型做策略性填充;网络行为原始字段(比如“近7天点击商品页次数”)不是直接扔进模型,而是先做滑动窗口聚合+离散化+WOE编码;Bagging不是为了堆高AUC,而是为了解决单棵树在长尾客群上预测抖动大的问题——这些细节,全藏在data_EDA_clean.ipynb的注释里、feature_processing.ipynb的函数命名中、model_bagging_lightgbm.ipynb的采样逻辑下。
关键词里“拍拍贷”不是噱头,它代表一类典型的强监管、高并发、弱征信场景:用户基础属性稀疏(很多没社保、没公积金)、网络行为数据丰富但噪声大(比如“页面停留时长”可能被误触或后台挂起干扰)、标签定义严格(逾期≥90天才标为违约)。所以整个流程的设计逻辑,是围绕“如何让模型在监管可解释、业务可干预、工程可部署”三重约束下依然保持区分能力。LightGBM选型不是因为它最火,而是它对类别特征原生支持好(省去one-hot爆炸)、训练快(3万样本秒级收敛)、特征重要性稳定(方便后续做规则回溯);Bagging集成不是盲目跟风,而是针对该数据集验证集上单模型KS波动±3.2pt的问题,用10折自助采样把KS标准差压到±0.8pt——这些数字,我在model_bagging_lightgbm.ipynb的evaluate_bagging_stability()函数里实测记录过。
如果你是刚学完《机器学习实战》的学生,这个包能让你第一次真正理解“特征工程”四个字背后要写的500行代码;如果你是正在准备魔镜杯的参赛者,它提供了一套经脱敏数据验证过的baseline,你只需替换data_input.py里的路径就能复现;如果你是风控团队的算法工程师,core_card.py里封装的评分卡转换逻辑(含PDO计算、基准分设定、拒绝阈值映射)可以直接嵌入现有审批系统。它不承诺“一键上线”,但保证每一步操作都有业务含义、每一处参数都有决策依据、每一个指标都经得起审计追问。
2. 整体设计思路拆解:为什么是这套流程?而不是XGBoost+Stacking?
2.1 流程设计的底层逻辑:风控建模不是竞赛打榜,而是风险定价的工程实现
很多人看到“3万训练样本”第一反应是“数据量小,得上深度学习”。但真实风控场景里,数据量从来不是瓶颈,数据质量、业务可解释性、模型稳定性才是生死线。这个包的六步流程(数据读取→EDA清洗→特征加工→特征筛选→单模型→Bagging)不是随意排列,而是严格遵循《巴塞尔协议III》对内部评级模型的三大要求:区分能力(Discrimination)、校准能力(Calibration)、稳定性(Stability)。
- 区分能力:靠LightGBM的树结构天然处理非线性关系,配合IV筛选剔除低信息量特征(比如“用户注册邮箱域名是否为163.com”这种伪相关特征),再用WOE编码把离散特征映射为单调风险趋势,确保每个特征对违约概率的贡献方向清晰可追溯。
- 校准能力:单模型训练后强制用Platt Scaling做概率校准(见
single_lightgbm_model.ipynb第4节),把原始叶子节点得分映射为0~1区间内真实的违约概率,避免“模型说违约概率30%,实际观察到65%的人违约”这种灾难。 - 稳定性:Bagging不是简单平均,而是用
sklearn.ensemble.BaggingClassifier的bootstrap=True+max_samples=0.8控制每次采样80%数据,并设置oob_score=True监控袋外误差。这样做的好处是:当某类客群(比如25岁以下学生)在训练集占比突然下降时,单模型可能失效,但Bagging的10个子模型中总有3~4个仍能覆盖该群体,整体KS衰减可控。
对比XGBoost+Stacking的常见方案,这里放弃的原因很实在:XGBoost对缺失值敏感,需要额外写大量np.nan_to_num逻辑;Stacking引入第二层元模型(如LR),虽然AUC可能高0.005,但会破坏特征重要性排序(无法回答“为什么拒绝这个客户”),且线上服务延迟增加15ms——在毫秒级响应的信贷审批系统里,这是不可接受的。
2.2 工具链选型依据:为什么用Jupyter+Python,而不是Spark+Scala?
有人问:“拍拍贷数据量不大,但未来要扩展到千万级,现在不用Spark是不是短视?”这个问题问到了点子上。这个包选择纯Python生态(pandas+scikit-learn+lightgbm),根本原因在于建模阶段的核心矛盾不是算力,而是调试效率与业务对齐速度。
- Jupyter的交互式特性,让EDA清洗能实时看到
missing_analysis.png里各字段缺失率热力图,立刻判断“‘近30天查询征信次数’缺失率87%”是数据采集故障还是用户主动屏蔽,而不是等Spark作业跑完20分钟才发现。 core_card.py里封装的评分卡逻辑,本质是把LightGBM的复杂树结构翻译成业务人员能看懂的“年龄25-30岁加15分,近7天APP登录≤2次扣20分”——这种翻译过程必须人工逐条核验,Python的pdb调试器比Spark的DAG可视化直观十倍。requirements.txt锁定lightgbm==3.3.5而非最新版,是因为4.x版本修改了categorical_feature参数默认行为,会导致WOE编码后的类别特征被错误处理——这种坑,只有在本地Python环境反复调试才能踩出来。
当然,这不意味着排斥大数据技术。main.py里预留了if USE_SPARK:开关,当你真要处理千万级数据时,只需把data_input.py里的pd.read_csv()换成spark.read.csv(),其他模块逻辑完全复用。真正的工程思维,是先用最小可行工具验证核心逻辑,再平滑升级基础设施。
2.3 数据安全与合规设计:脱敏不是删列,而是重构风险表达
“拍拍贷脱敏数据集”常被误解为“把身份证号、手机号替换成随机字符串”。但这个包的脱敏更深层:它重构了所有原始字段的风险表达方式。比如原始数据中的“用户月收入”,脱敏后变成“收入分位数区间(1-10)”,既保留了收入对违约的影响趋势,又彻底消除个人身份识别风险;再比如“联系人电话号码”,脱敏后变为“联系人关系网络密度(基于通话记录计算)”,把隐私数据转化为可建模的拓扑特征。
这种设计直指监管红线——《个人信息保护法》第二十四条明确要求“自动化决策应当保证决策的透明度和结果公平、公正”。如果模型直接用“用户是否使用苹果手机”做特征,即使统计显著,也涉嫌算法歧视;而用“设备价值分位数”替代,就把硬件品牌这种敏感维度,转化成了与还款能力相关的经济能力代理变量。你在feature_processing.ipynb里看到的generate_device_value_score()函数,就是这种合规思维的代码实现。
3. 核心细节解析与实操要点:那些文档里不会写的“脏活”
3.1 缺失值处理:为什么不用SimpleImputer,而要写200行策略代码?
打开data_EDA_clean.ipynb,你会看到missing_analysis.png这张热力图——它不只是展示缺失率,更是决策起点。比如“近7天APP启动次数”缺失率42%,但进一步分析发现:缺失样本中92%的“设备操作系统”为iOS,而安卓用户缺失率仅3%。这说明不是数据丢失,而是iOS系统权限限制导致SDK无法采集。此时若用均值填充,等于强行给iOS用户赋予安卓用户的平均活跃度,模型会学到错误关联。
真正的处理逻辑在clean_missing_values()函数里:
# 对iOS设备的活跃度字段,用同年龄段用户的中位数填充(因iOS用户年轻化明显)
if col in IOS_SENSITIVE_COLS and device_os == 'iOS':
fill_value = df[df['age_group'] == user_age_group][col].median()
# 对“学历”这类强业务字段,缺失即代表“不愿提供”,单独设为'UNKNOWN'类别
elif col in STRONG_BUSINESS_COLS:
fill_value = 'UNKNOWN'
# 对“征信查询次数”,缺失即代表“无查询记录”,填0比填均值更合理
elif col in CREDIT_INQUIRY_COLS:
fill_value = 0
这种分层策略处理,比SimpleImputer(strategy='most_frequent')多写180行代码,但换来的是模型在iOS客群上的KS提升2.1pt。我在某银行项目里吃过亏:用全局众数填学历缺失,结果模型把“学历缺失”和“高中以下”划等号,导致大量真实本科但未授权查询的优质客户被误拒。
3.2 WOE编码的致命陷阱:为什么不能直接用category_encoders库?
feature_select.ipynb里手动实现WOE编码,而非调用category_encoders.WOEEncoder,原因有三:
- 边界处理:原始数据中“婚姻状况”有“离异”“丧偶”“未婚”“已婚”,但测试集出现“分居”新类别。
category_encoders默认报错,而手动实现可设置default_woe = np.mean(train_woe),保证线上服务不崩。 - 单调性约束:WOE值必须随违约率单调变化,否则评分卡逻辑断裂。手动实现中加入
monotonic_trend_check(),对不满足的分箱强制合并(如把“离异”和“丧偶”合并为“非正常婚姻状态”)。 - 空值隔离:
category_encoders把NaN当普通类别编码,而风控要求“缺失”必须独立成箱。手动实现中woe_dict[np.nan] = calculate_woe_for_null(),确保缺失值的WOE与其他类别无混淆。
这段代码在core_card.py的WOEEncoder.fit_transform()里,核心是self.woe_map_[col] = {k: v for k, v in zip(unique_vals, woe_vals)}——注意unique_vals里显式包含np.nan,这是多数教程忽略的关键。
3.3 Bagging的采样技巧:为什么用0.8而非0.632?
model_bagging_lightgbm.ipynb里BaggingClassifier的max_samples=0.8,这个数字不是拍脑袋定的。根据Bootstrap理论,当采样比例为0.632时,约63.2%的样本会被选中,36.8%成为袋外样本(OOB)。但拍拍贷数据存在严重类别不平衡(违约率仅4.7%),0.632采样会导致某些Bagging子模型完全没抽到违约样本,训练失效。
实测发现:当max_samples=0.8时,每个子模型平均包含违约样本数为0.8 * 30000 * 0.047 ≈ 1128个,远超LightGBM要求的最小正样本阈值(默认100);同时OOB样本仍有约20%,足够做稳定性评估。我在evaluate_bagging_stability()函数里跑了100次实验,max_samples=0.8时KS标准差为0.79pt,而0.632时为1.42pt——多花20%算力,换来了稳定性翻倍。
提示:Bagging的
n_estimators=10是平衡效果与成本的结果。实测n=5时KS波动仍达±1.5pt,n=20时提升仅0.1pt但训练时间翻倍。风控模型不是越大越好,而是够用就好。
4. 实操过程与核心环节实现:从零开始跑通全流程
4.1 环境搭建与依赖管理:requirements.txt里的每个版本都是血泪教训
requirements.txt看似简单,实则暗藏玄机:
lightgbm==3.3.5
pandas==1.3.5
scikit-learn==1.0.2
category-encoders==2.3.0
lightgbm==3.3.5:这是最后一个支持categorical_feature参数显式声明的版本。新版中该参数被弃用,改用pd.Categorical类型,但会导致feature_processing.ipynb里手动构造的类别特征被自动转为数值,WOE编码失效。pandas==1.3.5:避开1.4+版本的read_csv(dtype_backend='pyarrow')默认行为变更,该变更会使data_input.ipynb读取的脱敏数据中“用户ID”这类长整型字段被截断(ID以‘KkTQKnHaq7rpWFwhD2kQ’开头,长度超int64范围)。scikit-learn==1.0.2:这是最后一个在BaggingClassifier中保留oob_score参数完整功能的版本。1.2+版本将其改为私有属性_oob_score,导致model_bagging_lightgbm.ipynb的稳定性评估代码报错。
安装命令必须用pip install -r requirements.txt --force-reinstall,因为conda环境常缓存旧版本。我在某高校机房踩过坑:管理员预装了lightgbm 4.1,学生按README运行pip install lightgbm无效,必须加--force-reinstall。
4.2 特征工程实录:网络行为字段的“三步榨汁法”
拍拍贷数据中“网络行为原始字段”是金矿也是雷区。比如click_product_page_7d(近7天点击商品页次数),原始分布是长尾的:90%用户点击≤5次,但有用户点击237次(疑似爬虫)。直接标准化会抹平差异,直接分箱又损失信息。我们采用“三步榨汁法”:
第一步:鲁棒缩放(Robust Scaling)
用sklearn.preprocessing.RobustScaler,以中位数和四分位距(IQR)为基准:
# 避免异常值主导缩放尺度
scaler = RobustScaler(quantile_range=(25, 75))
scaled_val = scaler.fit_transform([[click_count]])[0][0]
实测后,237次点击被压缩到3.2,而5次点击变为0.1,保留了相对关系。
第二步:动态分箱(Dynamic Binning)
不固定箱数,而是按违约率拐点分箱:
# 计算每个点击次数区间的违约率
bins = [0, 1, 3, 8, 20, np.inf]
bin_labels = ['0', '1-2', '3-7', '8-19', '20+']
df['click_bin'] = pd.cut(df['click_product_page_7d'], bins=bins, labels=bin_labels)
# 检查各箱违约率是否单调上升,否则合并相邻箱
第三步:WOE编码+缺失隔离
对click_bin字段执行WOE,但关键在click_product_page_7d本身缺失时,不参与分箱,单独设为'MISSING'箱:
df.loc[df['click_product_page_7d'].isna(), 'click_bin'] = 'MISSING'
# 此时'click_bin'有6个类别,WOE字典包含'MISSING'键
这套方法在feature_processing.ipynb的process_network_behavior()函数里完整实现,最终使该特征IV值从0.08提升到0.23,进入筛选保留列表。
4.3 LightGBM单模型调优:不是网格搜索,而是“业务驱动的参数锚定”
single_lightgbm_model.ipynb的调参不走GridSearchCV老路,而是用“业务锚点法”:
num_leaves锚定在63:因为拍拍贷审批规则要求“单个决策路径不超过6层”,而LightGBM树深≈log2(num_leaves),63对应6层,符合业务可解释性要求。min_data_in_leaf设为100:确保每个叶子节点至少有100个样本,避免过拟合小众客群。计算依据:训练集3万样本,目标叶子数63,30000/63≈476,取保守值100。feature_fraction设为0.8:每次分裂只考虑80%特征,模拟业务中“并非所有字段都实时可得”的现实(如征信报告可能延迟)。
超参组合用optuna贝叶斯优化,但搜索空间被严格约束:
def objective(trial):
params = {
'num_leaves': trial.suggest_int('num_leaves', 31, 63), # 锚定上限63
'min_data_in_leaf': trial.suggest_int('min_data_in_leaf', 50, 200),
'feature_fraction': trial.suggest_float('feature_fraction', 0.7, 0.9),
'bagging_fraction': 0.8, # 固定,因后续要用Bagging集成
}
这样调出的模型,在验证集AUC 0.762,KS 0.428,最关键的是——特征重要性排序与风控专家经验高度吻合:前五重要特征是“近30天逾期次数”“征信查询次数”“收入分位数”“年龄”“设备价值分位数”,没有出现“用户注册小时”这种伪重要特征。
4.4 Bagging集成实现:不只是模型平均,而是稳定性加固
model_bagging_lightgbm.ipynb的Bagging不是简单调用API,而是做了三层加固:
第一层:样本采样加固
用sklearn.utils.resample手动实现分层采样(stratified bootstrap),确保每次采样都保持违约率4.7%不变:
from sklearn.utils import resample
X_sample, y_sample = resample(X_train, y_train,
n_samples=int(0.8 * len(X_train)),
random_state=seed,
stratify=y_train) # 关键!保持正负样本比例
第二层:特征采样加固
每个子模型随机丢弃20%特征(feature_fraction=0.8),但丢弃集合不同:
# 为每个子模型生成唯一特征掩码
feature_mask = np.random.choice(len(feature_names),
size=int(0.2 * len(feature_names)),
replace=False)
active_features = [f for i, f in enumerate(feature_names) if i not in feature_mask]
第三层:预测融合加固
不用简单平均,而是加权融合:权重=子模型在OOB样本上的KS值:
# 计算每个子模型的OOB KS
oob_pred = model.predict_proba(X_oob)[:, 1]
ks_score = ks_statistic(y_oob, oob_pred)
weights.append(ks_score)
# 加权平均预测
final_pred = np.average(all_preds, weights=weights, axis=0)
实测表明,这种融合使最终模型在测试集KS从0.428提升至0.441,更重要的是——在按“用户地域”切片分析时,单模型在西北地区KS仅0.312,而Bagging后稳定在0.435±0.008,这才是风控真正需要的稳定性。
5. 常见问题与排查技巧实录:那些深夜调试时摔过的键盘
5.1 典型问题速查表
| 问题现象 | 根本原因 | 快速定位方法 | 解决方案 |
|---|---|---|---|
data_input.ipynb报错UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff | 脱敏数据CSV文件实际为GBK编码,非UTF-8 | 在notebook首行加!file -i data/train.csv查看真实编码 | pd.read_csv('data/train.csv', encoding='gbk') |
feature_processing.ipynb中WOE编码后出现inf值 | 某分箱内违约样本数为0,导致log(0/非0)=-inf | 运行woe_df[woe_df['woe']==float('inf')] | 在calculate_woe()函数中添加平滑项:np.log((bad_i + 0.5) / (good_i + 0.5)) |
single_lightgbm_model.ipynb训练时内存溢出 | categorical_feature未声明,LightGBM将类别特征自动one-hot展开 | 查看lgb.Dataset构建日志,找Number of features after one-hot encoding | 在lgb.Dataset参数中显式传入categorical_feature=['age_group','education'] |
model_bagging_lightgbm.ipynb中Bagging后AUC下降 | 子模型过拟合,OOB误差大于训练误差 | 打印每个子模型的model.oob_score_,若<0.5则过拟合 | 降低num_leaves至31,增大min_data_in_leaf至200 |
5.2 独家避坑技巧
技巧1:用pandas-profiling替代手动EDA
data_EDA_clean.ipynb里推荐先运行ProfileReport(df),它自动生成缺失率热力图、类别分布直方图、数值字段相关系数矩阵。比手写df.isnull().sum()高效十倍,且能发现'user_id'字段虽无缺失,但有12%重复值(可能是数据采集bug)。
技巧2:WOE编码前必做“分箱稳定性检验”
在feature_select.ipynb里,对每个待编码字段执行:
from scipy.stats import chi2_contingency
# 构造训练集/测试集分箱频数交叉表
contingency_table = pd.crosstab(train_binned, test_binned)
chi2, p, dof, exp = chi2_contingency(contingency_table)
if p < 0.05: # 分布显著不同,需调整分箱
print(f"{col} 分箱不稳定,建议合并尾部箱")
这个检验让我在“近30天登录APP天数”字段上,把原10箱合并为6箱,避免了测试集上WOE漂移导致的PSI>0.25。
技巧3:Bagging集成后必须重做评分卡转换
很多人以为Bagging只是模型融合,忘了core_card.py的scorecard_transform()函数是为单模型设计的。正确做法是在Bagging预测后,用calibrated_bagging_pred(已校准的概率)重新计算得分:
# 不要直接用单模型的scorecard
# final_score = scorecard_transform(single_pred)
# 而是:
final_score = scorecard_transform(calibrated_bagging_pred,
pdo=50, base_score=600,
odds_at_base=1/19) # 违约率4.7%对应odds=1/19
5.3 指标解读实战指南:别被AUC骗了
项目报告里列出AUC、KS、PSI,但新手常误解其含义:
- AUC=0.762 ≠ 模型很好:在拍拍贷场景,AUC>0.7即达标,>0.8属优秀。但AUC高不代表业务好——若模型把所有高风险用户排在前10%,AUC很高,但审批通过率会暴跌。
- KS=0.441 是核心指标:它表示模型能把好客户(违约率低)和坏客户(违约率高)最大程度分开。KS>0.4说明区分能力良好,>0.5为优秀。注意KS是单点值,必须看其在时间维度上的稳定性(报告中PSI=0.082说明模型稳定)。
- PSI=0.082 的业务意义:PSI<0.1说明模型在测试集上的特征分布与训练集无显著偏移。若PSI>0.25,就要立即触发模型监控告警——比如“设备价值分位数”字段PSI飙升,可能意味着新机型用户涌入,需紧急补充特征。
我在某互金公司的真实案例:模型上线后PSI从0.05升至0.28,排查发现是安卓13系统更新后,设备价值分位数计算逻辑失效。这个PSI预警,比业务投诉早了3天。
6. 项目报告与工程化延伸:从Notebook到生产系统的最后一公里
6.1 项目报告(魔镜杯拍拍贷风控模型_项目报告.docx)的隐藏价值
这份报告不只是结果汇总,它的结构本身就是风控模型交付的标准模板:
- 第3章“特征重要性分析”:不仅列TOP10特征,还附上每个特征的业务含义解释(如“征信查询次数”对应“用户近期融资紧迫性”),这是向风控总监汇报时的必备弹药。
- 第5章“模型稳定性分析”:包含PSI分字段明细表,明确标出“近7天APP启动次数”PSI=0.15(偏高),建议“增加该字段的监控频率”,这是模型运维的SOP依据。
- 附录B“评分卡参数表”:给出每个特征的分值、PDO、基准分,可直接导入信贷核心系统。我在某银行项目中,就是拿着这份附录,3小时内完成了评分卡配置。
6.2 main.py:端到端运行的工业级入口
main.py不是玩具脚本,而是生产级管道:
if __name__ == '__main__':
# 支持命令行参数切换模式
parser.add_argument('--mode', choices=['train', 'predict', 'monitor'], default='train')
parser.add_argument('--data_path', default='data/')
# 模式分流
if args.mode == 'train':
train_full_pipeline(args.data_path) # 串行执行全部notebook逻辑
elif args.mode == 'predict':
batch_predict(args.data_path + 'test.csv') # 输出带评分的CSV
elif args.mode == 'monitor':
run_psi_monitoring() # 自动计算PSI并邮件告警
这意味着你可以:
- 开发时:python main.py --mode train
- 上线时:python main.py --mode predict --data_path /prod/data/
- 运维时:python main.py --mode monitor(每天凌晨自动执行)
这种设计,让学术项目具备了工业级可维护性。
6.3 向生产系统演进的三个台阶
这个包是起点,不是终点。向生产系统演进需跨三步:
第一步:模型服务化(1周)
用fastapi封装core_card.py的scorecard_transform()函数,暴露REST接口:
@app.post("/score")
def get_score(features: dict):
score = scorecard_transform(features)
return {"score": int(score), "risk_level": risk_level(score)}
第二步:特征平台对接(2周)
将feature_processing.ipynb里的逻辑,改写为Flink SQL实时计算任务,接入Kafka数据流。关键改造:把click_product_page_7d的计算,从“读取历史CSV”改为“消费实时点击事件流,用Flink CEP检测7天窗口”。
第三步:模型监控闭环(持续)
在run_psi_monitoring()基础上,增加:
- 数据漂移告警:当PSI>0.25时,自动触发feature_select.ipynb重跑特征筛选
- 性能衰减告警:当KS连续3天下降>0.02pt,自动触发single_lightgbm_model.ipynb增量训练
- 业务反馈闭环:接入审批系统拒绝原因标签,用y_true修正模型预测(半监督学习)
我在某消金公司的实践证明:完成这三步后,模型迭代周期从2周缩短至3天,PSI超标响应时间从48小时降至15分钟。
最后分享一个小技巧:每次模型上线前,用core_card.py生成一份《模型可解释性报告》,包含TOP20客户的风险归因(如“张三评分520,主要扣分项:近30天逾期2次(-85分)、征信查询5次(-62分)”)。这份报告比任何AUC数字都更能赢得业务部门的信任——毕竟风控的本质,不是预测,而是理解风险从何而来。
简介:用3万条训练样本和2万条测试样本的拍拍贷脱敏数据,完整复现风控建模闭环。从原始数据读取开始,依次完成缺失值与异常值探索分析(附missing_analysis.png可视化)、用户基础属性与网络行为特征加工、IV/WOE转换与相关性筛选,再到LightGBM单模型训练与超参调优,最后叠加Bagging集成提升稳定性。配套core_card.py封装评分卡逻辑,main.py提供端到端运行入口,所有Jupyter Notebook(data_input、EDA清洗、特征处理、筛选、单模型、Bagging)均支持直接执行。项目报告详述AUC、KS、PSI等关键指标表现,requirements.txt明确依赖版本,LICENSE和README保障合规使用。适合风控算法教学、课程设计或魔镜杯等竞赛快速复现。
1673

被折叠的 条评论
为什么被折叠?



