基于LendingClub数据的信贷风控建模与评分卡开发实践
发布时间:2026/9/16 1:39:21 锦皓数字建站

做了几年信贷风控和数据建模说实话最常被同行问到的就是“有没有一份干净、公开、字段又全的信贷数据可以用来练手或者做方案验证”。我一般都会直接推荐LendingClub的公开贷款数据。这份数据在风控圈子里算是非常经典的教学级数据量大、字段丰富、带真实的违约标签既有财务特征又有行为特征拿来练特征工程、模型训练、评分卡开发都特别合适。这篇文章我会用比较口语化的方式把基于LendingClub数据做信贷分析和建模的完整思路拆开讲一遍包括我是怎么理解字段、怎么定义坏客户、怎么做特征衍生、怎么建模评估以及在实操里踩过的好几个坑。不管你是刚入门想做信贷风控项目还是工作里需要快速搭一个可解释性强的信用评分模型这篇内容应该都能给你一些能直接落地的参考。1. 先搞清数据和业务场景再动手建模1.1 这份数据到底是什么LendingClub是一家美国的P2P贷款平台公开的贷款数据是从平台撮合成功的个人贷款记录里抽样脱敏后的结果其中包含了贷款申请时填写的资质信息、平台审批时用到的信用特征、贷后还款表现等。我用的版本大概是2007年到2018年之间的数据总记录数接近三百万条字段数量接近150个单条样本涵盖申请、授信、还款、违约整个链路的信息。很多刚接触的人一上来就写代码跑模型结果发现字段名一堆看不懂比如fico_range_low、dti、mths_since_last_delinq也不知道哪些该用哪些不该用。我的建议是先按三段去理解这份数据借款人的信用画像、贷款产品的合同信息、贷后的还款表现。这三类信息在建模时扮演的角色完全不同。1.2 目标变量是怎么定义出来的信贷建模最大的前置问题是“好账和坏账怎么定义”。LendingClub数据里最直接的字段是loan_status它的取值有很多种Fully Paid、Charged Off、Current、Default、Late (31-120 days)等等。这里有个非常关键的实操细节不能简单地把Charged Off定义为坏样本把Fully Paid定义好样本就完事。我在做这个项目时用的定义是这样的好客户Fully Paid也就是贷款已经全部结清。坏客户Charged Off也就是平台已经认定这笔贷款无法收回并做了坏账核销。删除样本还在还款中的Current、宽限期内的Grace Period、逾期时间较短且尚未核销的Late这些样本的最终结局还没落定直接放进模型里会给标签带来噪声。这样做完之后数据大概剩下不到两百万条坏样本占比约在20%左右具体比例会因为时间窗口选择不同略有差异。这个比例对于二分类风控模型来说是比较理想的既不是极度不均衡也不会因为正负样本差距过大导致模型学不到东西。2. 数据清洗和探索性分析的正确姿势2.1 缺失值不能一把梭哈删除LendingClub数据里缺失值问题非常常见而且不同字段的缺失逻辑完全不同。比如mths_since_last_delinq这个字段是“距离上次逾期过去的月份数”如果借款人过去从来没有逾期记录这个字段就会是空值这不是数据缺失而是“无此事件”的语义缺失。如果直接把这个字段删掉或者填空等于扔掉了“这个人历史很干净”这个强信号。我的处理方式是分成三类业务语义缺失比如上述的“无逾期记录”用-1或0单独填充表示“无事件发生”模型能自动学出这个分箱的含义。真正意义上的散乱缺失比如某些不太重要的外部征信字段如果缺失率超过60%我会直接删除。缺失率适中、且有业务含义的字段我会结合其他字段做填补比如annual_inc年收入偶尔缺失可以用同emp_length工作年限分组的均值做填充。这里建议把每个字段的缺失率都输出一张表自己先过一遍再决定处理方式。千万别一上来就用dropna()把所有带空值的行删掉尤其mths_since_last_delinq这类字段删除会让你的好样本大量流失而且把最有区分度的信息白白扔掉。2.2 异常值处理要结合业务逻辑dti债务收入比这个字段最典型。正常情况下dti的分布范围会在0到50之间但是数据里偶尔会出现几百甚至几千的极值这种通常是申请时填写错误或者数据录入异常。还有annual_inc有些人年收入填了几百万美元明显不符合数据主体人群的分布。我是怎么处理的呢第一种情况业务上不合逻辑的极值比如dti大于100的直接剔除第二种情况虽然数值很大但不一定错比如年收入50万美元以上的我会做截断处理把超过99.5%分位数的值替换成该分位数对应的数值这样即保留了排序信息又不会被极端值拉偏模型。年收入还有个细节LendingClub公布的部分数据里有些年收入是0或者特别低但这类人居然通过了审批。这种情况我一般会去看他的home_ownership和emp_length如果确实是个有房有稳定工作的人年收入字段可能是脱敏问题我会用分箱后的收入等级去替代原始值。说白了做异常值处理最怕的不是极值本身而是你不清楚这个极值背后的业务故事。2.3 探索性分析要看哪些维度做EDA不是简单画图而是要回答三个问题这个特征和违约是否相关、特征之间是否存在强烈的共线性、时间维度上有没有显著的不稳定。拿利率int_rate来说LendingClub里头利率是平台根据借款人风险定价的结果我跑下来单变量分析里这个字段和违约的相关性非常显著利率越高坏账率越高。这是业务逻辑一致的高风险的人拿到的贷款价格本来就贵。但注意正因为利率是风险定价的产物它和很多风险特征比如grade、sub_grade是高度共线的建模时如果不加处理逻辑回归的系数会很不稳定。我习惯用分箱后的坏账率曲线做单变量分析就是按特征的十分位数去分组看每组坏账率的单调性。如果一条特征分十组之后坏账率完全是乱序跳动的那这个字段的建模价值就比较存疑。反过来如果坏账率从低到高呈单调递增那就是个值得进模型的货真价实的风险信号。3. 特征工程决定模型上限的关键环节3.1 原始字段并不适合直接进模型很多入门玩家拿到数据之后直接把所有数值型字段丢给机器学习模型训练结果线上效果远不如预期然后就说是模型问题。实际上大多数情况是特征工程没做扎实。LendingClub数据里fico_range_low和fico_range_high是两个分数段字段直接丢进去等于给了模型两个高度相关的变量而且FICO分数本身和违约是非线性关系比如740分以上坏账率差别不大640分到680分区间坏账率急剧攀升。我处理时会把两个字段取中间值再按业务经验做分箱比如600、600-640、640-680、680-720、720-760、760把连续值变成有序类别。这样逻辑回归这类线性模型也能学到非线性关系。再比如addr_state借款人所在州这种高基数类别特征直接做one-hot编码会产生五十多个稀疏列而且很多州样本量很小统计意义不足。我的做法是把这个字段转换成“该州的平均坏账率”用历史数据算一个州级风险指数代替原始类别。这样既保留了地域层面的结构性风险差异又把高基数特征压缩成一个连续字段。3.2 衍生特征怎么构造才有业务含义LendingClub数据最有意思的地方在于它不仅是申请时的截面数据还包含了部分贷中行为数据比如delinq_2yrs两年内逾期次数、inq_last_6mths近6个月查询次数、open_acc未结清账户数、total_acc总信用账户数。这些字段单独看有意义组合起来更有意义。我做了一组衍生特征这里列几个效果比较好的信用账户使用率revol_bal / revol_lim反映借款人当前额度使用情况一般使用率越高风险越大。查询密度inq_last_6mths / 6近半年平均每月被查询次数多头借贷风险的重要信号。逾期严重度delinq_2yrs / open_acc两年内逾期次数占未结清账户的比例比单独看逾期次数更能反映真实还款能力。收入负债压力annual_inc / (12 * dti) * 100输出一个综合收入负债比虽然计算上不一定严谨但在模型里作为非线性交互特征确实有效。等额本息还款压力也是一个有价值的衍生特征就是用loan_amnt、int_rate、term计算每个月要还多少钱然后和annual_inc做比值。这个特征比单纯看贷款金额要合理因为同样贷三万三年期和五年期的月供压力完全不同收入两万和收入二十万的人承受能力也完全不同。3.3 时间变量是信贷建模最容易翻车的地方申请贷款的年份、月份这个信息很多人不重视处理方式就是直接扔掉或者当成普通数值特征丢进模型。这里头其实藏着很大的坑。LendingClub覆盖了十多年的时间跨度中间经历过完整的经济周期2008年金融危机之后坏账率有明显变化2015年之后平台的风控策略也做过调整。同一个特征在不同年份的分布和风险含义并不稳定。如果直接把年份当数值特征用等于要求模型自己去学周期性规律样本量分配不平的时候很容易学出一堆伪规律。正确的做法是先把时间信息做成年份分箱、季度分箱、月份分箱这几个维度一方面用于做时间维度的交叉验证另一方面可以用来观察标签分布是否随时间漂移。我实际跑下来发现样本的时间窗口对模型影响非常大训练集用早期数据、测试集用后期数据AUC会比随机切分低不少。这一点后面模型部分我会再展开。4. 建模过程与评估细节4.1 训练集和测试集到底应该怎么切分信贷建模里的数据集划分和普通机器学习竞赛有本质区别。普通竞赛经常用随机切分只要保证训练集和测试集分布一致就行。但是信贷场景是从过去的数据学习来预测未来的违约风险所以必须按时间顺序切分。我是这样做的按贷款发放月份排序比如说用2015年到2017年上半年的数据做训练集2017年下半年和2018年的数据做测试集。这样做更接近实际应用场景我们手上只有历史数据要预测的是未来发放贷款的客户表现。时间切分会带来一个显著的体验模型性能通常会比随机切分差一些AUC可能低个0.02到0.05。这是正常的至少说明你做的不是“作弊版”的模型。如果只看随机切分的结果你可能会高估模型在真实场景里的表现。做信贷风控的人必须习惯这一点真实世界的分布一直在变模型不会永远保持实验室里的效果。4.2 逻辑回归和树模型怎么选信贷建模和很多纯算法竞赛有个很大不同风控模型往往需要可解释性监管或者业务方会问“为什么给这个人拒绝”“为什么给这个人提额”。这时候逻辑回归的优势就非常明显。逻辑回归的优势在于每个特征的系数直接反映了该特征对违约概率的正负影响方向。可以非常方便地把系数转化成评分卡分值比如基础分加各项特征得分的形式。训练速度快部署简单上线之后排查问题也容易。我一般会用带L1和L2混合正则化的逻辑回归作为基线模型先跑出一版结果确认特征方向是否符合业务常识。如果某个特征的系数方向明显反了比如收入越高风险越大那就要回头检查是不是特征共线性或者数据泄漏的问题。树模型这边我会用XGBoost或者LightGBM做对比。树模型的优势是能自动学习特征交互和非线性关系通常AUC和KS会明显高于逻辑回归。但它的问题是可解释性差而且在小样本或者标签噪声比较大的时候容易过拟合。我在项目里的做法是两个模型都跑一个用于评分卡开发和业务沟通一个用于预测性能提升和策略制定的参考。4.3 关键指标不能只盯着准确率信贷数据里即使坏样本占比在20%左右用准确率看模型也会很失真。比如全部预测为好客户准确率也有八成但这个模型毫无意义。核心要看的指标是这几个AUC综合反映模型对好坏客户的排序能力0.5等于瞎猜0.7以上算有可用价值0.8以上在信贷场景里已经算不错的单模型。KS最大区分度计算每个分数段的好坏样本累计占比差值的最大绝对值。信贷场景里KS在0.3左右就算可用的模型超过0.5要小心过拟合。召回率坏客户召回设定一个拒绝阈值后模型能抓住多少比例的坏客户。这个指标比准确率更贴近风控业务目标。稳定度PSI这个指标是监控模型上线后效果衰减用的建模阶段主要关注特征的PSI防止换了时间段后特征分布不稳。我在报告里习惯做一张“不同拒绝率下的模型表现”表意思是如果拒绝掉30%的申请件预计能拦截掉多少比例的坏账这样业务方一眼就能明白模型的价值。4.4 调参和特征筛选的实操顺序调参这件事特别容易被新手做反。很多人一上来就GridSearch跑一大堆参数组合结果调了几天模型性能提升还不到一个点。我的建议是先定特征再定参数特征没理清之前疯狂调参基本是浪费时间。特征筛选我这边顺序是这样的先用IV值信息价值粗筛一遍删除IV小于0.02的特征这些特征的区分能力太弱留着只增加噪声。然后在逻辑回归里用L1正则化做第二轮筛选把系数被压缩到零的特征去掉。最后结合业务经验做人工确认比如某个特征虽然统计上区分度一般但在业务上是核心风险指标比如收入负债比我会保留。参数层面逻辑回归需要关注的其实是正则化系数C和正则化方式。C值越小正则化越强一般通过交叉验证去选没有必要上很复杂的搜索方法。XGBoost的话主要关注学习率、树的深度和最小叶子样本数学习率低一些配合足够的迭代次数训练时间长一点但效果更稳定。LightGBM还多一个叶子节点数和特征采样的参数需要关注。5. 实操中踩过的坑与排查方法5.1 数据泄漏最隐蔽也最致命的错误这是我在做这个项目时吃过最大的亏。LendingClub数据里很多字段其实是贷款发放之后才能确定的比如total_pymnt实际收到的总还款额、total_rec_prncp实际收回的本金、total_rec_int实际收回的利息、recoveries催收回款。这些字段在申请阶段根本不存在如果你把它们放进了模型那等于用“未来的结果”去预测“未来的结果”训练时AUC能干到0.99以上一到真实应用就是废品。判断一个字段是不是“未来变量”有一个很实用的方法问自己“在贷款申请的那一刻我能不能知道这个值”。如果答案是不能那这个字段就必须剔除。我当时是把所有带total_pymnt、total_rec_、recoveries、collection_recovery_fee开头的字段全部从特征列表里删掉然后再重新跑基线模型AUC瞬间从0.98回到了0.72这才是真实水平。还有一类容易被忽略的泄漏就是issue_d之后才产生的行为数据比如某些还款表现类的字段。处理方式同样简单只要字段的生成时间晚于申请时点全部不要。5.2 样本不平衡要不要做采样LendingClub数据坏样本占比20%左右严格说算不上极度不平衡但如果你只关心坏客户召回率20%的占比仍然会让模型偏向多数类。这时候可以做两件事而不是盲目采样一个是调整模型权重逻辑回归里设置class_weightbalancedXGBoost里设置scale_pos_weight等于负样本数除以正样本数另一个是在评估阶段更关注KS和坏客户召回率用拒绝率场景倒推阈值。我给个数值上的参考如果正负样本比大约4:1那么scale_pos_weight可以从4开始试然后观察验证集的KS和召回率变化。并不是权重设得越高越好设太高模型会把大量好客户误判为坏客户虽然坏客户召回率上去了但误杀率也会高得没法接受。这个平衡要根据业务成本去定不是纯技术问题。5.3 测试时特征分布漂移怎么定位时间序列切分之后你会发现某些特征在训练集和测试集上的分布差异不小比如收入中位数上升了、信用账户数减少了。这时候要区分两种情况一种是业务数据本身的自然波动比如平台用户结构变化这个属于长期存在的现象模型需要接受另一种是特征口径发生了变化比如某段时间内某个字段的缺失率突然变高这往往是上游数据记录方式调整了。我排查分布漂移时用的是一个很简单的方法对每个特征计算训练集和测试集的PSIPSI大于0.1的拿出来单独看分箱分布图确认是整体偏移还是个别分箱变化。如果是整体偏移说明用户群体确实变了可以考虑是否要在模型里加入时间类特征如果是个别分箱异常大概率是数据质量问题需要回头检查清洗逻辑。这里推荐大家在特征筛选时尽量保留业务含义明确、分布稳定的特征像FICO分数分箱、收入负债比这类变量在长周期里相对稳定而那些纯统计挖掘出来的交互特征虽然当时效果很好换个时间段可能衰减得特别快。5.4 复现别人的结果但分数对不上怎么办这个问题我被问过无数次我按网上教程跑了同一份数据为什么AUC和别人不一样原因通常不在模型代码而在几个看起来不起眼的环节。最典型的就是样本筛选规则别的人删不删Current、删不删Late、保留哪些时间区间都会影响最终结果。标签口径一变模型学的东西就不一样。还有一种情况是缺失值处理方法不同比如连续变量填空用0还是用中位数类别变量是单独成箱还是直接删除都会导致结果差异。再就是随机种子XGBoost和LightGBM如果不固定随机种子每次跑的AUC在第二三位小数上就是会浮动。我现在的习惯是把所有数据处理步骤、样本筛选条件、随机种子全部写进配置文件里这样出任何结果都能回溯原因。如果你复现不出别人的分数先别怀疑代码写错了先看数据预处理和样本定义是不是完全一致。做项目记录时也建议把每一步都留下截图或者结果表格方便后续回溯。6. 一些深入使用的扩展思路6.1 从分类模型升级到评分卡如果你把逻辑回归跑通了下一步可以尝试把它转成标准评分卡。评分卡的本质是把逻辑回归的线性输出映射到一个整数分数区间分数越高代表风险越低。具体做法是设定基准分和基准Odds然后让每个特征分箱对应一个偏移分值最后累加得到总分。举个例子假设你设定基准分600分对应Odds为1:32即好客户概率是坏客户的32倍每提高20分对应于Odds翻倍。通过这两个参数可以换算出每个特征的每个分箱的分值。这种方法在传统银行风控里用了很多年优点是透明度极高业务人员拿手指头就能算清楚一个客户的得分来源。LendingClub数据做出来的评分卡效果不一定比XGBoost高但胜在可解释和可监管。如果你工作里需要对接传统金融客户这个能力还是很吃香的。6.2 从单模型到模型融合不少项目在单模型性能到顶后会发现卡在0.76左右不动了这时候可以试试模型融合。我常用的是把逻辑回归的输出概率和XGBoost的输出概率做加权平均权重可以通过验证集搜索确定。这个方法在LendingClub数据上通常能让AUC再提升零点几个百分点。需要注意的是融合模型的复杂度管理信贷场景不是比赛不是模型越复杂越好。如果融合后性能提升很小而解释成本和运维成本大幅增加那我宁可牺牲一点性能保持单模型的简洁。一般来说提升小于0.5个百分点我就不会上线融合模型否则出了问题排查起来非常痛苦。6.3 自动化报告生成思路如果你是拿这个项目做课程设计或者工作汇报每次重新跑数据、整理图表、写结论确实很费时间。我给一个比较偷懒但高效的做法把整个分析流程封装成一个脚本从数据读取、清洗、特征工程、建模、评估到图表输出跑完之后自动生成一份带表格和描述性结论的报告。描述性结论部分不需要强求自然语言生成直接用固定模板填数字就行比如“模型的AUC为0.78验证集KS为0.35”。我自己的经验是建模项目里真正耗时间的不是代码本身而是反复调整后需要重新生成所有图表和结论。搭好这套自动化流程之后后续换参数、换样本窗口都只需要改配置文件重新跑一遍工作效率会明显提升。做信贷数据分析核心要义其实不是把模型分数堆到多高而是理解每一份数据背后的人、业务逻辑和风险本质。LendingClub这份数据之所以经典就是因为它把信贷业务里最核心的变量都放到了你面前逼着你想清楚每一个字段的真实含义。希望这篇内容能帮你少走一点弯路。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。