资讯详情

资讯详情

逻辑回归信用评分卡实战:从数据清洗到模型监控全流程

简介这份资源围绕逻辑回归算法构建信用评分模型面向对Python数据分析与风控建模感兴趣的新手及有一定基础的学习者可用于毕业设计、课程实践、作业任务或实训项目。内容覆盖数据预处理与特征处理、变量WOE转换、信息值计算与变量筛选、选定特征WOE格式化以及根据输入特征数值自动生成评分结果的评分模型搭建帮助读者理解评分卡从原始数据到最终评分的完整链路。资源包共10个文件以csv数据文件、py脚本、xls数据字典及md说明文档为主另含zbak备份文件压缩包约7.68MB结构紧凑便于按模块查阅。目前已有41人学习下载。通过这份资料读者可获取一套可运行的评分体系实现脚本、配套训练与测试数据、字段说明文档并借助备份文件对比不同版本快速复现逻辑回归评分卡流程掌握WOE与IV在变量筛选中的实际用法。1. 信用评分模型为什么逻辑回归依然是那条最稳的基线如果你在消费金融、小贷或助贷机构做风控建模大概率绕不开一个场景业务方丢过来一份几万行的历史放款数据要求你在一周内给出一个能上线、能解释、能过监管的评分模型。这时候你翻遍工具箱XGBoost、LightGBM 甚至深度学习都能跑出更高的 AUC但最后真正敢往生产环境推的往往还是逻辑回归。原因不复杂——信用评分卡要的不只是排序能力还要每个特征对分数的贡献方向明确、分值可追溯、拒绝原因能落到具体变量上。逻辑回归配合 WOE 编码恰好把这几点都满足了。这个标题讲的就是从原始信贷数据到一张可解释评分卡的完整链路数据清洗、特征分箱、WOE/IV 计算、逻辑回归训练、评分刻度转换、模型验证。它适合风控建模新人建立第一套完整方法论也适合有经验但一直用树模型“黑匣子”交付的工程师回头补上可解释性这一课。下面按实际项目顺序拆开讲每一步都给可复现的代码和参数。2. 从原始数据到建模宽表清洗、分箱与WOE编码2.1 信用评分模型的数据准备与标签定义信用评分模型的原料是一张“人-时间-表现”的宽表。常见做法是从申请信息、征信变量、行为埋点里拉出候选特征再定义一个二分类标签通常用“未来 6 到 12 个月内是否逾期 90 天以上”作为坏样本定义记为 1好样本记为 0。这里有两个容易翻车的地方一是观察期和表现期必须错开否则会引入标签泄漏二是样本要排除“灰样本”比如只逾期 30 天的客户他们既不算好也不算坏强行归类会污染模型。我一般会先做一轮基础清洗缺失率超过 80% 的字段直接丢单值占比超过 95% 的字段也丢因为方差太小对模型没有贡献。剩下的缺失值不要急着填均值信用场景里“缺失”本身可能就是信息比如“无征信记录”往往对应薄档案人群可以单独设一个缺失标志位。下面这段代码演示从 CSV 读入到基础清洗的最小流程。import pandas as pd import numpy as np # 读入原始宽表假设已按客户维度聚合 df pd.read_csv(credit_raw.csv) # 标签列1 为坏样本0 为好样本 label_col is_bad # 1. 丢弃缺失率过高的列 missing_rate df.isnull().mean() drop_cols missing_rate[missing_rate 0.8].index.tolist() df df.drop(columnsdrop_cols) # 2. 丢弃单值占比过高的列 nunique_ratio df.nunique() / len(df) drop_cols2 nunique_ratio[nunique_ratio 0.95].index.tolist() df df.drop(columnsdrop_cols2) # 3. 对数值型缺失加标志位再填中位数 num_cols df.select_dtypes(include[np.number]).columns.tolist() num_cols [c for c in num_cols if c ! label_col] for col in num_cols: if df[col].isnull().any(): df[col _missing] df[col].isnull().astype(int) df[col] df[col].fillna(df[col].median()) print(清洗后形状:, df.shape) print(坏样本占比:, df[label_col].mean().round(4))这段代码的逻辑是先做列级过滤再做行级缺失处理。参数上80% 和 95% 是两个经验阈值不是硬标准如果某个字段业务上极其重要即使缺失率高也建议保留并单独分析。坏样本占比打印出来是为了后续判断样本是否均衡如果坏样本低于 5%要考虑过采样或调整权重但不要直接上 SMOTE 把数据搞失真。2.2 卡方分箱与WOE、IV的计算细节逻辑回归对连续变量的非线性关系不敏感所以信用评分里几乎都会先把连续变量离散化成分箱再计算 WOEWeight of Evidence做编码。分箱方法有等频、等距、卡方分箱和决策树分箱实操中最稳的是卡方分箱因为它会自底向上合并类别保证每个箱内坏样本率单调或至少差异显著。下面用optbinning库演示一个变量的分箱和 WOE 计算。from optbinning import OptimalBinning # 以某个连续变量为例 x df[age].values y df[label_col].values optb OptimalBinning( nameage, dtypenumerical, solvercp, # 约束规划求解结果稳定 min_bin_size0.05, # 每箱至少占 5% 样本 max_n_bins6, # 最多 6 箱便于评分卡展示 monotonic_trendauto ) optb.fit(x, y) # 输出分箱表和 WOE binning_table optb.binning_table.build() print(binning_table[[Bin, Count, Event_rate, WoE, IV]]) # 对训练集做 WOE 转换 df[age_woe] optb.transform(x, metricwoe)分箱的核心参数是min_bin_size和max_n_bins。min_bin_size0.05保证每箱样本量足够避免某个箱只有几十个样本导致 WOE 波动巨大max_n_bins6是评分卡展示的常见上限箱太多业务方看不懂。monotonic_trendauto会让算法自动判断坏样本率是否单调如果不单调会尝试合并。WOE 的计算公式是ln(坏样本占比 / 好样本占比)IV 是各箱 WOE 加权求和一般 IV 大于 0.02 才有预测力大于 0.5 要警惕过拟合。注意WOE 编码必须用训练集的箱边界去转换验证集和测试集不能重新分箱否则会引入未来信息。这是新手最容易踩的坑之一。2.3 多变量筛选与相关性处理单变量分箱做完后你会得到几十甚至上百个候选变量的 IV 值。接下来要按 IV 排序保留 IV 在 0.02 到 0.5 之间的变量然后做相关性筛查。信用场景里很多变量高度相关比如“近 6 个月查询次数”和“近 3 个月查询次数”同时放进逻辑回归会导致系数符号反转解释性直接崩掉。常见做法是计算变量间的 Pearson 或 Spearman 相关系数超过 0.7 的成对变量只保留 IV 更高的那个。# 假设已经算好每个变量的 IV iv_dict {age: 0.12, income: 0.25, query_3m: 0.31, query_6m: 0.29} selected [k for k, v in iv_dict.items() if 0.02 v 0.5] # 相关性矩阵 corr_matrix df[[c _woe for c in selected]].corr().abs() # 找出高相关对保留 IV 高的 to_drop set() for i in range(len(selected)): for j in range(i 1, len(selected)): if corr_matrix.iloc[i, j] 0.7: drop selected[j] if iv_dict[selected[i]] iv_dict[selected[j]] else selected[i] to_drop.add(drop) final_features [c for c in selected if c not in to_drop] print(最终入模变量:, final_features)这段代码的输出就是进入逻辑回归的变量清单。参数 0.7 是相关性阈值如果业务上两个变量都想保留可以放宽到 0.8但要做好系数不显著的心理准备。到这一步建模宽表基本成型每个变量都已经转成 WOE 值接下来就是逻辑回归训练。3. 逻辑回归训练与评分刻度转换从系数到分数3.1 逻辑回归损失函数与正则化选择逻辑回归的损失函数是对数似然损失的负数加上正则项。很多人只记得公式却忽略了正则化在信用评分里的实际作用。信用评分模型天然面临多重共线性即使做了相关性筛查变量之间仍有弱相关L2 正则可以把系数压缩得更平滑避免某个变量权重过大导致分数抖动。L1 正则则适合做进一步稀疏化但会让部分系数归零解释时要小心。from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import roc_auc_score # 准备入模数据 X df[[c _woe for c in final_features]] y df[label_col] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy ) # L2 正则逻辑回归 lr LogisticRegression( penaltyl2, C1.0, # 正则强度倒数越小正则越强 solverlbfgs, max_iter1000, class_weightbalanced # 样本不均衡时自动调权 ) lr.fit(X_train, y_train) # 评估 y_pred_prob lr.predict_proba(X_test)[:, 1] auc roc_auc_score(y_test, y_pred_prob) print(测试集 AUC:, round(auc, 4)) # 输出系数 coef_df pd.DataFrame({ feature: final_features, coef: lr.coef_[0] }).sort_values(coef, ascendingFalse) print(coef_df)参数C1.0是默认值实际项目里我会在 0.01 到 10 之间做网格搜索看验证集 AUC 和系数稳定性。class_weightbalanced在坏样本占比低于 10% 时很有用它会让模型更关注坏样本但会改变预测概率的绝对值如果后续要做概率校准记得关掉。系数输出后要检查符号WOE 编码下系数应该全为正如果出现负系数说明该变量和标签的关系与预期相反要么是分箱有问题要么是变量本身有噪声建议直接剔除。3.2 评分刻度转换把概率变成 300 到 850 的分数逻辑回归输出的是概率业务方要的是分数。信用评分卡的标准转换公式是score A - B * ln(odds)其中odds p / (1 - p)A 和 B 是刻度参数。通常设定某个基准分数对应的 odds比如 600 分对应 odds 为 50每增加 20 分 odds 翻倍这样就能解出 A 和 B。import math # 设定基准分和 odds base_score 600 base_odds 50 pdo 20 # 每 20 分 odds 翻倍 B pdo / math.log(2) A base_score B * math.log(base_odds) def prob_to_score(prob): odds prob / (1 - prob) return A - B * math.log(odds) # 对测试集打分 scores [prob_to_score(p) for p in y_pred_prob] print(分数范围:, round(min(scores), 1), 到, round(max(scores), 1))这段代码把概率映射到分数区间。参数base_score600、base_odds50、pdo20是行业常见设定但不同机构有自己的刻度标准改这三个参数就能适配。转换后要检查分数分布是否合理如果大量样本挤在某个区间说明模型区分度不够需要回头检查特征或分箱。3.3 评分卡表生成与业务交付模型训练完最终交付物是一张评分卡表每个变量的每个箱对应一个分数业务方拿到客户数据后逐项查表加总再加上基础分就是最终信用分。这张表要能直接嵌入决策引擎所以格式必须清晰。# 生成评分卡表 scorecard [] for feature in final_features: optb binning_models[feature] # 假设每个变量的分箱对象已保存 table optb.binning_table.build() coef lr.coef_[0][final_features.index(feature)] for _, row in table.iterrows(): if row[Bin] Special or row[Bin] Missing: continue woe row[WoE] score -B * coef * woe scorecard.append({ feature: feature, bin: row[Bin], woe: round(woe, 4), score: round(score, 2) }) scorecard_df pd.DataFrame(scorecard) print(scorecard_df.head(20))评分卡表的每一行是一个变量的一个分箱分数由-B * coef * woe计算得到。基础分是A - B * 截距所有变量分数加总再加基础分就是最终分数。这张表要保存成 CSV 或数据库表方便决策引擎调用。到这一步一个可上线的逻辑回归信用评分模型就完成了。4. 避坑与排查信用评分模型上线前必须检查的5件事4.1 现象验证集 AUC 比训练集低很多原因与解决现象是训练集 AUC 0.85验证集只有 0.68差距超过 0.1。原因通常是过拟合可能是入模变量太多、分箱过细或者 WOE 编码时用了全量数据导致信息泄漏。解决方法是先检查分箱是否在训练集上独立完成再减少变量数量把 IV 低于 0.05 的变量剔除同时增大正则强度把C从 1.0 降到 0.1 试试。如果还不行考虑用交叉验证的方式重新分箱。4.2 现象某个变量系数为负原因与解决现象是 WOE 编码后逻辑回归系数出现负值和业务直觉相反。原因可能是该变量和另一个变量高度相关模型把解释力分给了另一个变量也可能是分箱时没有保证坏样本率单调导致 WOE 和标签的关系扭曲。解决方法是先查相关性矩阵把相关变量剔除一个再检查分箱表如果坏样本率不单调强制设置monotonic_trendascending或descending重新分箱。4.3 现象分数分布过于集中原因与解决现象是上线后大量客户分数集中在 580 到 620 之间区分度很差。原因可能是模型区分能力不足也可能是刻度参数设置不合理导致分数被压缩。解决方法是先看测试集 AUC如果低于 0.7说明模型本身不行要加特征或换算法如果 AUC 正常调整pdo参数把 20 改成 40 或 50让分数区间拉开。4.4 现象缺失值填充后模型效果反而下降原因与解决现象是填充缺失值后 AUC 掉了 0.03。原因是缺失值本身有信息填了中位数反而抹掉了这个信号。解决方法是不要直接填中位数而是先加缺失标志位再把缺失值单独作为一个分箱让 WOE 编码去捕捉缺失的预测力。如果缺失标志位 IV 很低再考虑填充。4.5 现象上线后分数和离线不一致原因与解决现象是离线算好的分数和决策引擎跑出来的分数对不上。原因通常是 WOE 转换时用了不同的箱边界或者评分卡表里的分数精度不够。解决方法是把训练时的分箱边界和 WOE 映射表完整保存下来决策引擎严格按同一张表转换分数保留两位小数不要中途四舍五入。上线前用一批样本做端到端比对确保离线在线分数完全一致。5. 模型监控与迭代让评分卡活得久一点模型上线不是终点信用评分模型会随着客群变化和时间推移衰减。我一般会做三件事月度监控分数分布、季度监控变量 PSI、半年做一次模型重训。PSI 是群体稳定性指标衡量当前样本和训练样本在分数或变量上的分布差异PSI 小于 0.1 算稳定0.1 到 0.25 要警惕超过 0.25 就要考虑重训。def calculate_psi(expected, actual, bins10): # 按期望分布分箱 breakpoints np.percentile(expected, np.linspace(0, 100, bins 1)) breakpoints[0] -np.inf breakpoints[-1] np.inf expected_perc np.histogram(expected, breakpoints)[0] / len(expected) actual_perc np.histogram(actual, breakpoints)[0] / len(actual) # 避免除零 expected_perc np.where(expected_perc 0, 0.0001, expected_perc) actual_perc np.where(actual_perc 0, 0.0001, actual_perc) psi np.sum((expected_perc - actual_perc) * np.log(expected_perc / actual_perc)) return psi # 假设 train_scores 和 oot_scores 是训练集和跨时间样本的分数 psi_value calculate_psi(train_scores, oot_scores) print(分数 PSI:, round(psi_value, 4))这段代码计算分数分布的 PSI。参数bins10是分箱数样本量大时可以加到 20。PSI 超过阈值后不要急着全量重训先逐变量排查是哪个变量漂移最严重有时候只需要调整那个变量的分箱边界就能恢复。另外监控坏样本率的变化也很重要如果实际坏样本率远高于训练时的假设说明客群风险在上升这时候要同步调整通过率和额度策略。我自己的习惯是每次重训都保留旧版本评分卡新模型上线后做一段时间的影子运行对比新旧分数的排序差异和实际坏账表现确认新模型确实更好再切换。这个习惯帮我避免过好几次“新模型离线指标好看、上线后坏账反而升高”的翻车。信用评分模型不是一锤子买卖它更像一个需要持续照看的系统逻辑回归的可解释性在这里反而是优势——每次分数漂移你都能顺着系数和分箱找到原因。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →