尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
风控评分卡建模实战:逻辑回归WOE编码与分数转换全流程
2011年的Kaggle上有个叫Give_Me_Some_Credit的竞赛十多年过去了它依然是很多金融科技公司面试风控建模岗时的必考题目。原因很简单这个赛题几乎覆盖了评分卡模型从数据清洗、特征工程到模型训练、分数映射的完整链路数据量不大、字段干净但坑一点不少非常适合用来检验一个人是不是真的动手做过风控模型而不只是背过几道机器学习面试题。这篇文章我会完整复盘我用这个数据集做评分卡的全过程包括为什么选逻辑回归而不是XGBoost、WOE编码到底解决了什么问题、样本不平衡怎么处理、以及最终的Probability of Default怎么映射成一张可解释的分数卡。内容包括可以直接复用的Python代码、参数推导过程和我在实操中踩过的坑适合正在入门机器学习的同学也适合打算转行风控建模的朋友参考。1. 赛题与数据剖析为什么这个8年前的比赛至今仍是风控建模的试金石1.1 赛题背景与要解决的问题Give_Me_Some_Credit的建模目标非常直接根据借款人过去两年的信用行为数据预测他在未来两年内发生“严重逾期”的概率。这里的“严重逾期”在数据里被定义为SeriousDlqin2yrs字段取值为1即借款人至少经历过90天以上的逾期。这本质上是一个二分类问题但在金融场景里我们不只要一个分类结果更希望得到一个连续的风险概率然后再把概率映射成整数分数。这也是评分卡模型和普通机器学习模型最大的区别——银行信贷审批、信用卡额度调整、贷后预警这些业务场景都需要一个稳定、可解释、可监控的分数而不是一个黑盒的“逾期/不逾期”标签。1.2 数据字段全景与业务含义这个数据集一共有15万条样本11个字段。每一个字段在真实信贷业务中都能找到对应含义我把它们整理成了一张速查表字段名业务含义说明SeriousDlqin2yrs目标变量1表示两年内发生过严重逾期0表示未发生RevolvingUtilizationOfUnsecuredLines循环额度使用率信用卡等无担保贷款已用额度/总授信额度age借款人年龄数值型单位岁NumberOfTime30-59DaysPastDueNotWorse30-59天逾期次数过去两年内逾期30-59天的次数DebtRatio负债比率月还款额/月收入注意包含抚养支出等MonthlyIncome月收入数值型存在大量缺失NumberOfOpenCreditLinesAndLoans开放式信贷笔数当前在用的贷款和信用卡数量NumberOfTimes90DaysLate90天以上逾期次数严重逾期历史NumberRealEstateLoansOrLines不动产贷款笔数房贷或房屋净值贷款NumberOfTime60-89DaysPastDueNotWorse60-89天逾期次数中度逾期历史NumberOfDependents家属人数不包括本人1.3 这个数据集的“坑”在哪里这15万条数据表面看很规整但实际一跑你就会发现几个经典问题首先目标变量严重不平衡。严重逾期样本只有约1万条占总样本的6.6%左右。如果直接拿原始数据训练逻辑回归模型会倾向于把所有样本都预测成“好客户”因为即使什么都不做准确率也能到93%以上。其次MonthlyIncome缺失率接近20%。这非常贴近真实业务——很多申请人在填收入时要么不填要么填一个明显失真的数字。缺失值怎么补、补多少直接影响到DebtRatio这个特征的计算进而影响模型效果。第三部分字段存在不合常理的取值。比如NumberOfTime30-59DaysPastDueNotWorse这个字段最大值竟然是98这在信贷业务里几乎不可能出现。通常这类字段的取值区间是0到十几这种异常值很有可能是数据录入或者上游系统的问题。这些坑处理得好不好往往决定了模型在排行榜上的位置。我见过很多人直接删掉缺失值、保留异常值跑出来AUC也不差但做出来的分数卡在业务上完全没法解释。评分卡模型的价值不只是AUC高更要每个分数段的风险区分度都稳定、可解释。2. 数据清洗与特征工程先看分布再做WOE2.1 缺失值处理MonthlyIncome与NumberOfDependents的两种思路先说结论MonthlyIncome我用随机森林回归补全NumberOfDependents用中位数补全。这两个字段的处理思路完全不同。MonthlyIncome不是随机缺失的。我做了个简单的统计分析发现收入缺失的样本里坏客户占比明显高于收入完整的样本。也就是说缺失本身可能就带有风险信息。这种情况下如果直接用中位数或均值填充会抹掉这部分信息如果直接删掉样本又会损失近3万条数据。我的做法是把MonthlyIncome作为目标变量用其他特征age、NumberOfOpenCreditLinesAndLoans、DebtRatio、NumberOfDependents等训练一个随机森林回归模型然后对缺失值进行预测填充。这样填充出来的收入值跟该样本其他维度的特征保持了一致性。注意如果只是做简单的机器学习分类用中位数填充也勉强能用。但做评分卡时WOE编码对变量分布很敏感填充方式不当会导致分箱后的IV值失真。NumberOfDependents缺失率只有2.6%左右而且缺失样本的风险分布跟总体没有显著差异。这种字段直接填中位数就够了没必要引入额外模型。2.2 异常值不急着删先看看业务合理性我说一个很多人都会踩的坑拿到数据直接看describe发现某些字段的最大值很离谱就当成异常值删掉或者用百分位截断。这样做事后看AUC可能没太大影响但分箱的时候你会发现变量分布完全扭曲了。比如NumberOfTime30-59DaysPastDueNotWorse这个字段96%的样本取值是0剩下的集中在1到4之间突然冒出几个98、96的极端值。我当时的处理方式是分箱之后再合并把大于等于5的归为一箱而不是直接删除这些样本。理由很简单这些极端值虽然不符合常理但它们在业务上可能代表同一类人——已经严重逾期的老赖把他们单独挑出来反而会影响模型的稳定性。2.3 WOE编码的真正作用让特征与目标呈线性关系评分卡模型里几乎不用原始特征直接进逻辑回归而是先把连续变量分箱然后把每一箱替换成WOEWeight of Evidence值。这一步是评分卡的灵魂。WOE的计算公式是WOE_i ln(坏样本占比 / 好样本占比)其中坏样本占比 该箱中坏客户数 / 全部坏客户数好样本占比同理。注意这里用的是“占全部好坏客户的比例”而不是“该箱内的坏样本率”。为什么要做这个变换逻辑回归本身是线性模型输入的x和输出的log-odds被假设为线性关系。但实际业务中年龄和逾期率往往是U型关系——年轻人和老年人违约率都高中年人最低。直接用原始年龄进模型线性假设根本不成立。分箱后WOE编码相当于把非线性关系转换成了单调或接近线性的关系模型拟合能力会大幅提升。分箱的方法我推荐用等频分箱配合手动调整。等频分箱保证每箱样本量不要太少避免某一箱全是好客户导致WOE为负无穷。分箱时还要注意每箱的好客户数和坏客户数都不能为0否则ln计算会出问题。如果出现这种情况就把相邻箱合并。2.4 IV值筛选不要把相关性弱的变量硬塞进来IVInformation Value是WOE的加权求和用来衡量一个变量的预测能力。计算公式是IV Σ (坏样本占比 - 好样本占比) × WOEIV值越大变量区分好坏客户的能力越强。行业里常用的判断标准是IV值范围预测能力 0.02几乎无预测能力0.02 ~ 0.1弱预测能力0.1 ~ 0.3中等预测能力0.3 ~ 0.5强预测能力 0.5可疑需要检查是否过拟合我在这个赛题上算下来RevolvingUtilizationOfUnsecuredLines的IV值最高接近0.9这符合常识——信用卡额度使用率越高的人资金链越紧张违约风险越大。NumberOfTimes90DaysLate和NumberOfTime30-59DaysPastDueNotWorse的IV值也很高。而NumberOfOpenCreditLinesAndLoans、NumberRealEstateLoansOrLines这些字段IV值较低但超过0.02可以保留。注意IV值超过0.5的变量不一定就是“神变量”有时是因为分箱过细导致过拟合。对IV值异常高的变量我会单独检查它的分箱稳定性确保每一箱的样本量足够大。3. 逻辑回归与样本不平衡评分卡为什么还选LR3.1 为什么不直接上XGBoost很多初学者拿到分类问题第一反应就是上XGBoost、LightGBMAUC确实能刷得更高。但风控评分卡场景有个硬约束模型必须可解释、可监控、可审计。监管机构和业务部门会问“为什么这个客户是620分而不是650分”逻辑回归可以给出清晰的答案——每个变量的每个分箱对应一个固定的分数加加减减就出来了。XGBoost做不到这一点。另外一个原因是稳定性。逻辑回归的参数方差小对特征分布的轻微波动不敏感。而树模型很容易捕获训练集中的噪声在客群结构变化时分数波动较大这在信贷场景里是致命的。并不是说树模型没用。我遇到很多实际项目是树模型和评分卡并行的评分卡用于贷前审批树模型用于贷中预警和催收策略。两者服务不同的业务场景但在评分卡这个框架下逻辑回归依然是首选。3.2 样本不平衡处理的两个方向下采样与权重调整样本不平衡问题在评分卡建模中不能回避。我尝试了两种方案最后选择了权重调整方案一是对多数类样本进行下采样让好坏样本比例达到1:1或2:1。这样做的好处是模型能更充分地学习坏客户的特征坏客户召回率更高。缺点是损失了大量好客户样本模型对好客户的判断可能失真而且下采样的随机性会导致每次训练结果略有差异。方案二是在逻辑回归中设置class_weight参数给少数类更高的权重。这种做法不损失样本训练结果更稳定而且实际业务中好坏客户的成本本来就不对称——把一个坏客户放进来造成的损失远大于把一个好客户拒之门外的机会成本。我在这个赛题上最终选择了权重调整具体做法是在sklearn的LogisticRegression里设置class_weightbalanced或者自定义权重比例。如果你用的是statsmodels可以给样本加权后再训练两者效果差异不大。3.3 训练/验证切分与交叉验证的要点数据切分上我用的是70%训练集、30%验证集。但这个赛题有个特殊性数据本身是按时间收集的直接随机切分可能让训练集和验证集存在信息重叠。更严谨的做法是按时间排序后切分比如用前70%时间段的样本训练后30%时间段的样本验证这样能模拟模型上线后的真实表现。我两种方式都试过随机切分时验证集AUC大约在0.87左右按时间切分后降到0.85左右这是正常现象。时间切分的AUC更接近模型上线后的实际效果做风控模型时建议以这个数字为准。交叉验证方面我用5折CV来调正则化参数C。逻辑回归的正则化强度对结果影响不小C值太小模型欠拟合太大模型过拟合。我通过GridSearchCV搜索C在0.01到10之间取log均匀分布的50个值最终选择了C1.0左右这个参数下的模型在训练集和验证集上的表现最接近。4. 从概率到分数评分卡刻度转换的完整推导4.1 评分卡的核心公式训练完逻辑回归得到每个客户的违约概率p但这还不是评分卡。评分卡要把概率映射成一个整数分数让分数越高代表风险越低。标准映射公式是Score Offset - Factor × ln(p / (1 - p))其中p是模型预测的违约概率p/(1-p)就是oddsOffset和Factor是两个需要预先设定的刻度参数。这个公式直白解释就是违约概率每翻一倍odds翻倍分数减少一个固定值这个固定值就是Factor。4.2 Factor与Offset怎么定Factor和Offset的确定需要两个业务约定某个特定odds对应的基准分数。行业里常用的是odds1:1即违约概率50%时基准分数设为600分。odds翻倍时分数减少的值。行业里常用的是odds翻倍分数减少50分这个值叫PDOPoint of Double Odds。有了这两个约定Factor和Offset可以算出来Factor PDO / ln(2) 50 / 0.6931 72.13 Offset 基准分数 - Factor × ln(基准odds) 600 - 72.13 × ln(1) 600所以这个赛题的评分公式就是Score 600 - 72.13 × ln(p / (1 - p))我写了一段Python代码来计算每个样本的分数import numpy as np # 模型预测的违约概率 p model.predict_proba(X)[:, 1] # 计算odds odds p / (1 - p) # 刻度参数 PDO 50 base_score 600 base_odds 1 Factor PDO / np.log(2) Offset base_score - Factor * np.log(base_odds) # 计算分数 score Offset - Factor * np.log(odds)注意不同公司对基准分数和PDO的设定不同有的用odds1:1对应500分PDO30有的用odds1:19对应700分。参数怎么设不影响模型区分度但会影响业务部门对分数的直观理解需要跟业务方提前对齐。4.3 各变量得分明细表怎么生成前面算出来的是总分数但评分卡要能拆解到每个变量的每个分箱这样信贷审批人员才能知道“这个客户为什么被扣了30分”。拆解逻辑是这样的逻辑回归的线性部分可以写成ln(p / (1-p)) β0 β1×WOE_1 β2×WOE_2 ... βn×WOE_n代入评分公式后可以拆成Score Offset - Factor×(β0 β1×WOE_1 ... βn×WOE_n) (Offset - Factor×β0) - Factor×β1×WOE_1 - ... - Factor×βn×WOE_n所以每个变量的每个分箱的得分 -Factor × βi × WOE_ij可以提前算好存成一张映射表。比如age这个变量分成4箱每一箱都有一个固定的得分。线上使用时先看客户年龄落在哪一箱再查表拿到对应分数把所有变量的分数加起来再加上Offset-Factor×β0这个常数项就是最终分数。我在实际项目中会把这张映射表存到数据库或者配置中心业务系统只需要一个简单的lookup操作就能算出分数不需要实时调用模型服务。4.4 上线使用时的分数匹配逻辑评分卡上线后每个变量的分数映射关系是固定的。但这里有个细节训练时的分箱边界在线上必须保持一致。如果线上跑批时某个客户的一个特征值落在训练分箱的边界附近比如age刚好30岁而训练时25到30岁是一箱、30到35岁是另一箱上线代码必须严格判断“大于等于30归到哪一箱”否则会出现线下验证和线上分数不一致的问题。我踩过这个坑早期做评分卡时分箱边界写成了(25, 30]、(30, 35]但线上Java代码的区间判断是闭区间30岁的客户被归到了前一箱导致一整个客群的分数系统性地偏低。后来我在生成分箱映射表时会额外输出一份边界判断规则文档并且用一批已知分数的样本做线上线下比对测试确保完全一致。5. 模型评估AUC、KS之外风控还看什么5.1 AUC的局限与KS的使用场景AUC是衡量模型区分度的经典指标它的含义是随机抽取一个正样本和负样本模型给出的正样本风险得分高于负样本的概率。这个指标在评分卡评估中当然要看但不是唯一标准。风控行业更常用的是KSKolmogorov-Smirnov统计量。KS的计算方式是把样本按预测分数从低到高排序逐段计算累计坏样本占比和累计好样本占比两者的最大差值就是KS。KS值越大说明模型在某个分数段能把好坏客户区分得越开。我在这个赛题上算出的KS大约是0.48对应的AUC是0.87左右这个水平在信贷评分卡中属于良好。顺便说一句KS并不是越大越好超过0.7的KS要么是模型过拟合要么是特征里包含了未来信息比如直接用“发生过逾期”作为特征预测“未来是否逾期”这个在风控建模中是要坚决避免的。5.2 坏样本率分层校验Lift与洛伦兹曲线除了AUC和KS我还会做一套分层校验按预测分数从低到高分成10档计算每一档的实际坏样本率。理论上分数最低的一档坏样本率应该显著高于其他档而且从低分到高分坏样本率应该单调递减。如果出现某一档的坏样本率异常高或低说明模型在局部区间有偏。比如分数最高的那一档应该是风险最低的客户坏样本率反而上升了大概率是某些特征在高分段出现了过拟合。我再画洛伦兹曲线横轴是累计好样本占比纵轴是累计坏样本占比。曲线越靠左上角说明模型在很小比例的好客户覆盖下就能抓住大部分坏客户。这些图表在给业务方汇报时非常直观比单纯报一个AUC数字更说服力。5.3 过拟合控制与时间外验证评分卡最怕的不是线下AUC低而是上线后效果衰减快。我在这个赛题上做了一个关键验证把数据集按时间切成两段用前段训练后段验证。如果时间外验证的AUC比随机切分的AUC低很多说明模型过拟合了历史数据泛化能力不足。控制过拟合的操作主要有三个特征数量控制最终进入评分卡的特征数量控制在6到8个宁可牺牲一点训练集AUC也要保证特征的业务含义清晰、相关性稳定。正则化参数逻辑回归中C值不要设太大让参数估计更平滑。分箱稳定性每个分箱的样本量不低于总样本的2%避免在个别箱上过拟合。做完这些之后我的时间外验证AUC只比随机切分低0.02左右这个差距在可接受范围内。6. 踩坑复盘与工程化心得从模型到上线细节决定成败6.1 缺失值填充方式对WOE分箱的影响我在项目初期用中位数直接填充MonthlyIncome分箱后IV值只有0.08但改用随机森林预测填充后IV值提升到了0.12左右。原因很直接中位数填充会让大量样本堆积在同一个值附近分箱时这一箱的样本量特别大、好坏混杂区分度自然就差了。反过来如果某个特征的缺失率特别高比如超过40%我通常的做法是把“是否缺失”做成一个单独的分箱而不是填充后再分箱。因为缺失本身可能就有业务含义——有些客户不愿意填收入、有些渠道的手机号字段天然缺失这些在WOE编码中会体现为缺失箱与其他箱的风险水平不同。6.2 分箱后保险单调性检查与手动调整在真实业务中我强烈建议分箱后检查每个变量的WOE值是否单调或符合业务逻辑。比如年龄字段合理的WOE分布应该是年轻段WOE为正坏样本占比高中年龄段WOE为负老年段WOE又回升。如果分箱后WOE乱跳比如30-35岁WOE是-0.535-40岁突然变成0.340-45岁又是-0.2这种分箱不能直接进模型需要手动调整边界或者合并相邻箱。等频分箱出来的初始分箱经常出现这种问题我的处理流程是先等频分10箱看WOE趋势图把WOE值接近的相邻箱合并把趋势突变点作为新的边界重新分箱迭代2到3轮。6.3 从模型到分数映射表一次性做成可直接落地的配置训练完成后我会输出两个文件一个是模型参数文件包含每个特征的系数β另一个是分箱映射表包含每个特征的分箱边界、WOE值、单箱得分。分箱映射表长这样特征分箱区间WOE值单箱得分RevolvingUtilizationOfUnsecuredLines(-inf, 0.04]-1.1282RevolvingUtilizationOfUnsecuredLines(0.04, 0.15]-0.6851RevolvingUtilizationOfUnsecuredLines(0.15, 0.42]-0.119RevolvingUtilizationOfUnsecuredLines(0.42, 0.88]0.42-31RevolvingUtilizationOfUnsecuredLines(0.88, inf)1.23-89这张表可以直接交付给开发团队配置到规则引擎中不需要部署机器学习服务。评分卡模型最大的工程优势就在这里轻量、透明、低维护成本。6.4 关于“拒绝推断”的一点提醒这个赛题的训练数据只包含“已经获得信贷”的客户那些被历史审批流程拒绝的客户根本不在样本里。这就产生了一个叫做“拒绝推断”的问题训练样本存在选择性偏差模型对高风险客户的预测可能偏乐观。在真实业务中这个问题可以通过引入拒绝样本的推测标签来缓解或者直接使用“基于申请评分卡”的专门框架来建模。但在Kaggle赛题里没有拒绝样本数据所以我只是提一下。知道这个问题的存在比不知道好得多——至少你不会天真地认为线下AUC就是上线后的真实表现。写在最后很多人觉得评分卡模型技术含量不如深度学习但恰恰是这种“简单”模型对从业者的业务理解、数据处理能力和工程落地能力要求最高。Give_Me_Some_Credit这个赛题我用不同方法重做过好几遍每次都有新收获第一次是跑通了流程第二次开始关注分箱和WOE的细节第三次深入理解了样本偏差和模型稳定性问题。如果非要给一个建议顺序先在Kaggle上完整跑通一遍这个项目再找一个真实的信贷数据集做一次时间外验证最后把评分卡映射表做成一个可配置的规则引擎。这三步走完你对机器学习在风控领域的应用就算真正入门了。
RELATED

相关推荐

Easy-Vibe 实战解读:用 Vibe Coding 打造「编程陪学 AI 助教」——从 50 人课堂的崩溃现场到可落地的教学产品

Easy-Vibe 实战解读:用 Vibe Coding 打造「编程陪学 AI 助教」——从 50 人课堂的崩溃现场到可落地的教学产品

Easy-Vibe 实战解读:用 Vibe Coding 打造「编程陪学 AI 助教」——从 50 人课堂的崩溃现场到可落地的教学产品 【免费下载链接】easy-vibe 💻 vibe coding 101|The first course for AI-native product builders. 项目地址: https://gitcod…

📅 2026/9/15 16:20:18
Haystack 集成指南:使用 YouComWebSearch 组件接入 You.com 搜索 API

Haystack 集成指南:使用 YouComWebSearch 组件接入 You.com 搜索 API

Haystack 集成指南:使用 YouComWebSearch 组件接入 You.com 搜索 API 【免费下载链接】haystack Open-source AI orchestration framework for building context-engineered, production-ready LLM applications. Design modular pipelines and agent workflows wit…

📅 2026/9/15 16:20:18
Friend 项目内存权限管控:app/key 级内存授权(Memory App/Key Grants)架构与实践

Friend 项目内存权限管控:app/key 级内存授权(Memory App/Key Grants)架构与实践

Friend 项目内存权限管控:app/key 级内存授权(Memory App/Key Grants)架构与实践 【免费下载链接】Friend AI that sees your screen, listens to your conversations and tells you what to do 项目地址: https://gitcode.com/GitHub_Tren…

📅 2026/9/15 16:15:18
MORE NEWS

更多资讯

📰

如何用 taosgen 编排并运行 TDengine 写入基准测试作业

如何用 taosgen 编排并运行 TDengine 写入基准测试作业 【免费下载链接】TDengine High-performance, scalable time-series database designed for Industrial IoT (IIoT) scenarios 项目地址: https://gitcode.com/GitHub_Trending/tde/TDengine 在评估 TDengine 的写…

📰

LeGo-LOAM核心解析:mapOptimization.cpp四层架构与GTSAM因子图实战

1. 为什么必须从mapOptimization.cpp切入LeGo-LOAM源码核心LeGo-LOAM不是那种“跑通Demo就万事大吉”的算法框架。它表面看是激光SLAM流程的封装,实则是一套精密耦合的多线程状态估计系统——前端特征提取、中端运动补偿、后端图优化,三者像齿轮一样咬合…

📰

MOT Challenge评估指标与TrackEval实战全解析

做多目标跟踪(MOT)这几年,我几乎每天都要跟评估指标打交道。尤其是交论文或者跑比赛的时候,最后那道坎永远是同一个:怎么让官方评估代码老老实实跑通,并且跑出来的分数是别人能复现的。这个项目标题其实涵盖…

📰

**Description**

Description 【免费下载链接】metamask-extension :globe_with_meridians: :electric_plug: The MetaMask browser extension enables browsing Ethereum blockchain enabled websites 项目地址: https://gitcode.com/GitHub_Trending/me/metamask-extension [分析 git …

📰

RuboCop v1.25.1 版本修复详解:8 项 Bug 修复的技术内幕与回归测试指南

RuboCop v1.25.1 版本修复详解:8 项 Bug 修复的技术内幕与回归测试指南 【免费下载链接】rubocop A Ruby static code analyzer and formatter, based on the community Ruby style guide. 项目地址: https://gitcode.com/GitHub_Trending/rub/rubocop 导读 …

📰

Rnote:开源手写笔记与矢量绘图应用,压力感应笔触三分钟上手

Rnote:开源手写笔记与矢量绘图应用,压力感应笔触三分钟上手 【免费下载链接】rnote Sketch and take handwritten notes. 项目地址: https://gitcode.com/GitHub_Trending/rn/rnote 用数位板记课堂笔记,笔触总是又糊又飘?R…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬