简介面向金融风控建模学习者这份源码包聚焦大数据风控全流程适合有志于信用评分、欺诈检测与风险预测的数据分析师或建模工程师。资源共一百零六个文件包含四十个脚本、十九个模型、十六个样本数据、二十个可视化图表及三个示例压缩包约十九点六七兆字节脚本、数据、模型与说明文档层级分明。目前已有一千零一人下载学习。源码覆盖数据清洗与特征工程、逻辑回归与随机森林建模、异常检测与极端梯度提升欺诈识别、均值聚类客户分群以及模型验证与调参等关键环节并配有可视化与实时风控接口示例读者可结合数据直接运行快速掌握从数据预处理到模型落地的完整路径。该资源不仅便于理解各类算法在金融场景中的应用也为构建可上线风控系统提供了可参考的代码基线。1. Python金融大数据风控建模实战这个方向到底要解决什么问题“Python金融大数据风控建模实战”这个技术方向切入的是一类很具体的生产问题信贷业务每天涌入几万条申请每条申请带几百个原始字段人工审批和固定规则在这个量级上要么太慢、要么坏账率失控。用Python把数据清洗、特征工程、模型训练、评分卡转换和上线监控串成一条流水线就是这套实战方法的核心目标。它以数据量大、维度高、坏样本稀疏的信贷场景为对象产出一个可解释、可迭代的信用评分模型而不是一个只看AUC的黑匣子。这类项目适合消费金融、信用卡、供应链金融等场景的算法工程师和策略分析师也适合想从规则审批转模型审批的业务同学。对照“实战.zip”这个名字你拿到的往往是数据、代码、文档三件套但真正值钱的不是代码而是代码背后那套数据口径和建模纪律。下面我按过去做这类项目的落地顺序把宽度拼接、好坏定义、分箱编码、模型选型和最容易翻车的细节完整拆一遍。2. 建模前的三道关宽度拼接、好坏定义和时间切分2.1 从原始表到建模宽表先想清楚“一行代表什么”拿到这类实战包我习惯先不急着看代码而是把数据摸一遍。第一步永远是环境确认包里的代码大概率依赖pandas、numpy、scikit-learn和lightgbm先一次性检查是为了避免跑到半路才发现版本不对。unzip Python金融大数据风控建模实战.zip -d risk_workspace cd risk_workspace python -c import pandas, numpy, sklearn, lightgbm; print(deps ok)这里的逻辑是先解压到独立目录再验证依赖。lightgbm装不上时可以用sklearn的GradientBoostingClassifier先顶着数据量不大时效果差距不明显。接下来是数据形态。信贷原始数据通常分散在几张表里申请信息表、征信查询表、历史还款表。建模宽表的原则是“一行代表一个申请”同一个客户不同时间的申请要拆成多行。代码里最常见的翻车点是join时键不唯一一对多膨胀让行数暴涨模型训练到一半内存爆掉。所以我每次合并明细表之前都会先做一次基数检查。import pandas as pd app pd.read_csv(application.csv, parse_dates[apply_date]) repay pd.read_csv(repay_history.csv) n_before len(app) app_repay app.merge( repay.groupby(apply_id).agg( repay_count(repay_date, count), repay_late_days(late_days, max), ).reset_index(), onapply_id, howleft ) print(frows before {n_before}, rows after {len(app_repay)})这段代码的关键是先对明细表做groupby聚合把多种还款记录收敛到“申请”粒度再按apply_id左连接。join键用apply_id而不用customer_id是因为同一个客户历史上可能有多次申请用客户维度合并会把上一次申请的表现带到本次申请造成标签泄漏。repay_count是还款次数repay_late_days是历史最大逾期天数这两个字段就是最初级的申请行为特征。如果前后行数变化超过可忽略范围基本可以断定键没选对。2.2 好坏客户定义观察期与表现期分离建模宽表就绪后要做的第一件事不是写特征而是定义坏客户。坏客户不是数据里天然的字段需要业务口径换算。申请评分卡里最常见的定义是申请通过后进入表现期在表现期内出现任意一笔逾期超过30天的贷款标记为坏客户其余为好客户。表现期长度直接影响标签质量太短坏样本还没暴露完整太长样本时效性差模型服务的是三个月前的客群。常见选择是12个月业务周期短的场景用6个月。这里藏着风控建模最核心的一条纪律观察期和表现期必须严格错开。特征只允许使用表现期开始之前的信息标签来自表现期内的表现。如果特征里混进了表现期之后才产生的数据模型在训练集上会非常漂亮上线后直接崩盘这一类坑后面专门展开。构造标签的示意代码如下app[bad] 0 expr_end app[apply_date] pd.DateOffset(months12) for i, row in app.iterrows(): overdue repay[ (repay[apply_id] row[apply_id]) (repay[repay_date] row[apply_date]) (repay[repay_date] expr_end[i]) (repay[late_days] 30) ] if not overdue.empty: app.at[i, bad] 1这段循环写法是为了把口径讲清楚真实工程里要向量化否则几十万样本能跑很久。逻辑是取申请日到申请日加12个月内的还款记录只要有一条逾期天数达到30天该申请就被标记为坏客户。注意判断条件里repay_date大于apply_date这保证了还款行为发生在申请之后expr_end限制表现期终点超过12个月的逾期不参与本期建模。表现期不满12个月的样本要单独标记为floating不进建模集但进监控集样本量小时不要直接剔除保留floating可以在上线后跟踪回填标签持续扩充训练数据。2.3 时间切分不许随机shuffle风控数据天然带时间属性训练集、验证集、测试集的划分方式和普通机器学习不一样。默认的train_test_split自带shuffleTrue在风控场景里必须禁用。原因很直接模型上线后服务的是未来客群如果用随机切分等于让模型提前见过未来评估结果乐观得没有参考价值。我一般按申请日期升序排列后直接切片app app.sort_values(apply_date).reset_index(dropTrue) cut1 int(len(app) * 0.70) cut2 int(len(app) * 0.85) train app.iloc[:cut1] val app.iloc[cut1:cut2] test app.iloc[cut2:]这个切分的含义是最早70%的样本训练中间15%做验证调参最后15%做最终测试。验证集只用来选参数和早停测试集在整个建模周期里只允许碰一次多碰几次测试集就变成了训练集的一部分。切片后还要检查好坏比例在三段里是否接近如果时间窗口内有客群结构变化比如最近几个月明显收紧审批导致坏率下降那三段的比例差异是正常的不需要强行对齐。注意时间切分后重新检查验证集的坏样本占比如果验证集坏率明显偏低模型早停会提前需要整体重排切分点而不是靠调参硬拉回来。3. 特征工程与WOE编码风控建模和普通ML建模的分水岭3.1 为什么金融风控偏好WOE而不是直接喂原始值普通机器学习项目拿到数值特征标准化就可以进模型风控建模不行。原因有三个第一监管和业务部门要求模型可解释评分卡里每一个变量都要能讲清楚“为什么这一箱分数高、那一箱分数低”原始数值喂进逻辑回归只能告诉你系数正负解释不了非线性关系第二信贷特征里缺失值占比高、长尾重直接用原始值会把缺失变成一种隐性编码业务上说不通第三风控主模型常用逻辑回归逻辑回归在线性空间拟合log(odds)而WOE编码天然把特征转换成了对数尺度的“好坏比偏离度”与逻辑回归的假设契合。WOE的计算口径是对每个分箱计算好客户占比和坏客户占比的比值取对数。某个箱的好客户占比显著高于平均水平WOE就是正数反之是负数。IV值则是每个箱的“好客户占比减坏客户占比”乘以WOE再加总用来衡量变量整体的预测力。风控里对IV值有一套约定俗成的解读IV区间预测力判断处理建议0.02基本无预测力直接删除0.02~0.10弱预测力保留但组合使用0.10~0.30中等预测力主力变量区间0.30~0.50强预测力注意业务合理性0.50可疑优先排查时间穿越和数据错误这条线不是金科玉律但足够用来做第一轮筛选。我见过不少项目把IV大于0.5的变量当成宝贝结果一查是时间穿越或者字段口径错误所以IV值过高反而要先警惕。另外要记住IV筛选是单变量层面的初筛不替代多变量模型有些单变量IV低的特征组合起来对模型帮助很大0.02只是参考线不是硬阈值。3.2 特征分箱等频只是起点单调性和箱内占比才是目标WOE编码的前置步骤是分箱。最省事的做法是等频分箱按分位数把连续变量切成样本量相等的若干箱。等频分箱在数值分布均匀的字段上表现还可以但信贷特征普遍长尾收入、负债率这类变量大部分客户挤在低位少数客户拖出长尾等频切出来会看到某几个箱的坏率忽高忽低没有单调性。而评分卡要求连续变量的坏率尽量单调至少要在业务逻辑上说得通。我常用的替代方案是决策树分箱用单个变量和坏样本标签训练一棵浅层决策树叶子节点作为分箱边界。这样分箱会主动去拟合坏率变化比等频更贴合目标同时比卡方分箱更容易复现。示意代码from sklearn.tree import DecisionTreeClassifier def tree_bin(x, y, max_leaf5): x2 x.fillna(-999).values.reshape(-1, 1) clf DecisionTreeClassifier( max_leaf_nodesmax_leaf, min_samples_leaf0.02, criterionentropy ).fit(x2, y) return clf.apply(x2) train[age_bin] tree_bin(train[age], train[bad]) tmp train.groupby(age_bin).agg( cnt(age, count), bad_cnt(bad, sum) ) tmp[bad_rate] tmp[bad_cnt] / tmp[cnt] print(tmp)分箱边界由决策树自动决定但使用前必须做两件事检查箱内样本占比和坏率单调性。max_leaf_nodes限制节点数防止树枝太细min_samples_leaf0.02要求每个叶子至少覆盖2%样本保证箱子统计稳定。fillna(-999)只是让缺失值能进树因为sklearn的决策树不接受NaN如果原始数据没有-999这个合法值缺失样本会聚到同一个节点分箱完成后缺失自然独立成箱。分箱结束后检查bad_rate如果不单调先尝试合并相邻箱合并后仍不单调这个变量基本可以放弃不要为了凑变量数硬留。3.3 WOE计算与IV筛选公式、平滑、剔除高相关分箱完成后进入WOE计算。这里有个细节每个箱的好客户数或坏客户数可能为0直接取对数会产生inf所以要加一个很小的平滑项。计算IV的公式是每个箱的好客户占比减坏客户占比乘以WOE后再求和。代码里要注意好和坏的定义跟业务约定一致import numpy as np def calc_woe_iv(df, feature, targetbad): total_good (df[target] 0).sum() total_bad (df[target] 1).sum() tmp df.groupby(feature, dropnaFalse).agg( cnt(target, count), bad_cnt(target, sum) ) tmp[good_cnt] tmp[cnt] - tmp[bad_cnt] tmp[good_dist] tmp[good_cnt] / total_good tmp[bad_dist] tmp[bad_cnt] / total_bad eps 0.0001 tmp[woe] np.log((tmp[good_dist] eps) / (tmp[bad_dist] eps)) tmp[iv] (tmp[good_dist] - tmp[bad_dist]) * tmp[woe] return tmp[woe], tmp[iv].sum()groupby(dropnaFalse)可以把缺失值单独分成一组这一箱的WOE如果绝对值很大说明“是否缺失”本身有预测力业务上通常解释为渠道来源差异或信息完整度差异。计算完所有变量的IV后按IV降序保留大于0.02的变量同时计算相关系数矩阵把相关系数超过0.7的高相关特征成对删掉。逻辑回归对共线性很敏感两个强相关变量同时进模型系数符号可能互相抵消评分卡就没法解释。这一步做完特征集合一般能从几百个压到三四十个。4. 模型训练与评估从逻辑回归到LightGBM的选型路4.1 逻辑回归基线合规性优先特征工程完成后我先跑逻辑回归而不是一上来就上LightGBM。理由很简单风控模型要过审计逻辑回归的权重就是变量贡献度评分卡转换也依赖log(odds)的线性展开。用WOE化后的特征训练逻辑回归特征本身就是有序分箱系数方向能直接解释业务方向。训练代码from sklearn.linear_model import LogisticRegression X_train train_woe[feature_cols] y_train train_woe[bad] lr LogisticRegression( penaltyl2, C1.0, class_weightbalanced, max_iter200 ) lr.fit(X_train, y_train) coef_df pd.DataFrame({ feature: feature_cols, coef: lr.coef_[0] }).sort_values(coef, ascendingFalse) print(coef_df)C是正则化强度的倒数C越小正则越强默认1.0后续可以用验证集上的KS做网格搜索。class_weightbalanced自动按样本比例放大少数类权重信贷场景坏样本占比通常在3%到8%这个参数比手工下采样省事也更安全。系数方向检查是这步最容易忽略的把每个变量按业务常识判断预测方向比如收入水平越高、坏率越低在WOE编码下系数通常为负逾期次数越多、坏率越高系数通常为正。如果整体方向和业务常识相反别怀疑常识回去查分箱方向和WOE里好坏占比是不是写反了。4.2 LightGBM大数据量下的性能补充逻辑回归的缺点是只能捕捉线性关系。信贷数据里交叉特征很多年龄和负债率、收入和工作稳定性等组合往往比单变量更有效LightGBM这类梯度提升树天然擅长找交叉。在几十万样本、几百维特征上LightGBM的训练速度比XGBoost快一个数量级内存占用也低所以大数据风控里更常见。训练参数的设置有风控特点import lightgbm as lgb model lgb.LGBMClassifier( num_leaves31, learning_rate0.05, min_data_in_leaf200, feature_fraction0.8, bagging_fraction0.8, bagging_freq1, scale_pos_weight20, n_estimators500, verbose-1 ) model.fit( X_train, y_train, eval_set[(X_val, y_val)], callbacks[lgb.early_stopping(50), lgb.log_evaluation(0)] )min_data_in_leaf200是这里最重要的防过拟合参数。信贷数据噪声大叶子太小会把个别异常客群记住线上表现差的往往就是这批叶子。scale_pos_weight20表示把坏样本权重放大20倍近似平衡类别这个值通常取“好客户数除以坏客户数”的比值。feature_fraction和bagging_fraction分别是每棵树的列采样和行采样比例0.8是常见起点。早停基于验证集回调里的50表示连续50轮验证集指标不提升就停止训练。LightGBM模型一般不会直接上线当评分卡而是用来做两件事一是跟LR做特征交叉常见做法是让LightGBM输出叶子编码向量拼进LR特征既保留树的交叉能力又维持LR的可解释主结构二是通过特征重要性帮助筛选和验证LR的特征集合如果LightGBM的重要性排名和LR的系数方向明显冲突优先查特征口径而不是模型问题。4.3 评估模型KS、AUC和Gini别只看一个数风控模型有自己偏好的评估指标。AUC是全局区分能力KS刻画好坏客户累计分布的最大距离Gini是AUC的线性变换。三个指标从不同角度看同一个模型评估时都要看。常规参考线指标常见参考值说明KS0.3以上好坏分布分离度上线门槛常用0.3AUC0.75以上随机好坏样本排序正确的概率Gini2*AUC-1业务沟通时使用较多KS的计算和绘图是风控日常最高频的操作。一个计算KS的稳定实现如下from sklearn.metrics import roc_auc_score def calc_ks(y_true, y_score): df pd.DataFrame({y: y_true, s: y_score}).sort_values(s).reset_index(dropTrue) total_bad df[y].sum() total_good len(df) - total_bad df[cum_bad] df[y].cumsum() / total_bad df[cum_good] (1 - df[y]).cumsum() / total_good return (df[cum_bad] - df[cum_good]).abs().max() train_woe[score] lr.decision_function(X_train) test_woe[score] lr.decision_function(X_test) print(train ks:, calc_ks(train_woe[bad], train_woe[score])) print(test ks:, calc_ks(test_woe[bad], test_woe[score]))实现思路把样本按模型分数排序然后逐行累加坏客户占比和好客户占比两者差值的最大绝对值就是KS。这个函数比封装好的方法更可控方便你排序后在某个分数区间截取看分布。使用时有两点提醒第一验证集和测试集相差不要太大测试KS比训练高0.15以上基本可以认定有泄漏第二同一个模型在不同客群切片上的KS差异很大比如有房和无房客群区分度不一样是常态不用强迫完全一致。5. 风控建模避坑指南5个容易翻车的环节5.1 风控模型的失败模式大多数坑不在算法在数据风控建模跟普通ML项目最大的区别是数据口径几乎决定模型生死。第2章铺垫的观察期、表现期、时间切分没有哪一条是复杂算法但每一条都足以让模型上线后崩盘。下面这5条是这类项目里反复出现的血泪经验每一条都至少对应过一次返工建议动手前把对照清单过一遍。5.2 时间穿越特征里混入了未来信息现象是训练集AUC冲到0.95以上LightGBM的特征重要性榜首出现“当前逾期天数”这类字段模型上线后对新申请客群的区分度直接断崖式下跌。原因不是模型玄学而是特征表里有一列叫“截至提取日期的逾期状态”提取日期晚于表现期结束等于在用未来回答过去的问题。解决方法是构造特征时强制设置特征截止日只取申请日之前的数据上线前用历史截断数据做一次回测新客审批场景尤其要把“是否用到审批时点之后才产生的字段”作为代码评审的必查项。5.3 样本不平衡被过度矫正分数分布跟着偏现象是下采样之后模型在验证集上KS从0.42涨到0.48但回到原始样本分布里看分数整体分数偏高低分段几乎没有客户。原因是下采样把坏样本比例从5%强行提到50%模型学到的先验概率完全失真分数整体被推高切分阈值自然不准。解决方法是优先用class_weight或scale_pos_weight做类别平衡不要物理删样本如果出于性能必须下采样训练完成后要按原始好坏比例重新校准截距或分数映射参数再做KS和分数分布验证。5.4 缺失值一填了之“缺失”本身就是信号现象是一个IV排名前五的变量人工经验却觉得这个字段不该有这么大区分度查下来发现40%的客户在该字段上是空值。原因是建模时直接fillna(0)或fillna(均值)把“空值”和“真实值0”混为一谈缺失本身携带的渠道差异和信息完整度信号全部消失。解决办法是WOE分箱时把缺失单独成箱不做填充同时看IV高的变量要盯每一箱的样本占比缺失箱占比过高的变量要小心它反映的更可能是数据质量差异而不是客户信用差异上线后一旦数据收集策略变化这个变量会最先失效。5.5 随机切分训练验证集动态回溯就翻车现象是模型在测试集上AUC和KS都达标按月份滚动回测时最近三个月的KS连续下滑。原因很直接train_test_split默认随机抽样把未来月份的样本混进了训练集模型见过未来评估指标自然乐观。解决方法是回到第2章的时间切分方案按申请日期升序切片同时上线前的动态回溯必须纳入发布检查清单用最近三个月样本单独测KS如果比训练集KS衰减超过20%先排查是客群漂移还是模型失效。5.6 分数当概率用风险汇报差两个数量级现象是业务团队把评分卡分数除以某个常数直接当成逾期概率汇报结果汇报数字比实际坏账率低两个数量级。原因是评分卡分数是Odds的对数线性变换只保序不保数值分数高低能反映相对风险但不是概率本身。解决办法是交给业务前必须先通过第6章的公式把分数换算成Odds再换算成概率对外统一口径为分数段和对应坏账率文档里不要出现“概率”两个字以免误导。6. 把模型变成业务能用的评分卡分数映射与上线验证6.1 评分卡分数映射PDO和Offset怎么定评分卡的本质是把逻辑回归输出的log(odds)做一个线性变换变换到业务习惯的分数区间。两个自由参数是PDO即每个Odds翻倍对应的分数增量和基准分即某个指定Odds对应的分数。业界常见设定是Odds1:20时分数600Odds翻倍时分数加20。factor 20 / np.log(2) offset 600 - factor * np.log(1 / 20) # score offset factor * log(odds) # odds exp(b0 sum(bi * woe_i)) X_woe test_woe[feature_cols] score offset factor * (lr.intercept_[0] X_woe.dot(lr.coef_[0]))这里的factor对应PDO20除以ln2offset由基准分600和基准Odds1/20反推。两个参数可由风险策略自由设定修改它们只改变分数刻度不改变排序但会影响后续审批阈值。转换完成后每个WOE箱可以按系数拆出分数贡献形成一份业务能读懂的评分卡明细表。6.2 上线前两个验证PSI与滚动回测分数映射完不能直接上线至少要过两道验证。第一道是PSI群体稳定性指数衡量模型分数在训练样本和当前新样本上的分布偏移。第二道是滚动回测把最近三个月的数据按审批通过后的表现重新打标签用KS衰减判断模型是否还适用。PSI的计算很简单def calc_psi(expected, actual, bins10): exp, _ np.histogram(expected, binsbins) act, _ np.histogram(actual, binsbins) exp_pct exp / exp.sum() act_pct act / act.sum() eps 1e-6 return ((act_pct - exp_pct) * np.log((act_pct eps) / (exp_pct eps))).sum()PSI区间稳定性判断0.1稳定可正常使用0.1~0.25有漂移需关注变量变化0.25显著恶化建议排查重训我自己现在的习惯是不管项目多急评分卡上线前必跑一遍PSI和滚动回测。早期在模拟项目X上吃过一次亏训练时KS稳定在0.43上线后第二个月PSI直接到0.31查下来是某第三方数据源缺失率突变缺失箱样本占比从5%跳到35%评分卡在低分段挤了一大堆本不该拒绝的客户。后来我把所有高IV字段的缺失率也纳入每周监控评分卡加一个简单规则缺失率周环比超过50%触发告警。这条经验帮我少返了好几次工希望帮到你。本文还有配套的精品资源点击获取