尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

信用评分卡实战:逻辑回归从数据到评分的完整工程链路

发布时间:2026/9/28 22:31:19

资讯中心
01
ARTICLE

信用评分卡实战:逻辑回归从数据到评分的完整工程链路

信用评分卡实战:逻辑回归从数据到评分的完整工程链路
简介这份资源围绕逻辑回归算法构建信用评分模型面向具备一定Python基础、希望入门风控评分卡的学习者可用于毕业设计、课程实践或实训项目。内容覆盖数据预处理与特征处理、变量WOE转换、信息值计算与变量筛选、选定特征WOE化以及根据输入特征自动生成评分结果的完整建模流程帮助读者理解评分卡从原始数据到评分输出的关键环节。资源包共10个文件以csv数据文件、py脚本、xls数据字典及md说明文档为主另含zbak备份文件压缩包约7.68MB结构紧凑便于按模块查阅。目前已有41人学习下载。通过数据字典与脚本配合读者可复现特征筛选与评分计算逻辑掌握WOE与IV在变量选择中的实际用法并借鉴建模思路完成自己的评分体系搭建。1. 信用评分卡不是调包从 score_logistic-master 看逻辑回归怎么落成生产工具很多人第一次接触逻辑回归是在头歌平台上做逻辑回归损失函数那道题手推梯度、算交叉熵跑通一个二分类就算完事。但真到金融风控场景里逻辑回归模型和评分卡之间还隔着一条完整的工程链路。score_logistic-master.zip这个包解决的就是这条链路它把原始信贷数据经过缺失值处理、变量分箱、WOE 转换、IV 筛选最终用逻辑回归拟合出一套能直接输出信用评分的模型。包里带了cs-training.csv和cs-test.csv两份数据、一份Data-Dictionary.xls字段字典、sampleEntry.csv提交样例以及核心脚本score.py。适合正在做课程设计、毕业设计或者想理解评分卡从数据到分数完整流程的人。它不教你逻辑回归的数学推导它教你一个模型从 CSV 到可解释评分结果中间要过几道工序。2. 拆开 score_logistic-master数据字典、训练集与脚本的分工2.1 包内文件各管什么拿到一个压缩包先别急着跑score.py。我一般会先把目录结构过一遍搞清楚每个文件在流程里的位置。这个包的结构不复杂但每个文件都有明确职责文件作用使用阶段cs-training.csv带标签的训练数据含目标列建模与WOE计算cs-test.csv无标签或留出验证数据评分与提交Data-Dictionary.xls字段含义、类型说明特征理解sampleEntry.csv提交格式样例结果输出对齐score.py主流程脚本全流程执行README.md运行说明与依赖环境准备Data-Dictionary.xls是最容易被忽略但最不该跳过的文件。信用评分数据里大量字段是脱敏的比如RevolvingUtilizationOfUnsecuredLines、NumberOfTime30-59DaysPastDueNotWorse这种长名字不看字典你根本不知道它是额度使用率还是逾期次数。字段理解错了后面分箱和WOE方向就会反模型输出完全不可用。2.2 环境准备与依赖确认score.py是纯 Python 脚本依赖集中在数据处理和建模两块。常见做法是建一个干净虚拟环境避免和系统里已有的包版本打架python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install pandas numpy scikit-learn scipy xlrd openpyxl这里xlrd和openpyxl是为了读Data-Dictionary.xls。注意.xls是老格式新版xlrd只支持.xls.xlsx要用openpyxl两个都装上省得来回换。scikit-learn提供逻辑回归实现scipy在分箱和统计检验里会用到。装完后先跑一句python -c import pandas, sklearn; print(pandas.__version__, sklearn.__version__)确认没有报错再往下走。2.3 数据加载与字段初筛真正开始跑之前先单独把数据读进来看一眼形状和缺失情况。不要直接执行score.py然后对着报错猜。我习惯先写一段探查代码import pandas as pd import numpy as np train pd.read_csv(cs-training.csv, index_col0) test pd.read_csv(cs-test.csv, index_col0) print(train shape:, train.shape) print(test shape:, test.shape) print(缺失值 top10:) print(train.isnull().sum().sort_values(ascendingFalse).head(10)) print(目标列分布:) print(train.iloc[:, 0].value_counts(normalizeTrue))index_col0是因为这类数据集第一列通常是无意义行号不设的话会多出一个Unnamed: 0列干扰后续特征处理。目标列一般是第一列value_counts(normalizeTrue)看正负样本比例。信用评分场景里坏样本通常很少如果坏样本占比低于 5%后面分箱和WOE计算要特别注意每箱样本量否则IV值会失真。这一步不产出任何模型结果但它决定了你后面要不要做采样、分箱粒度设多粗。3. 从原始变量到WOE分箱、IV筛选与逻辑回归拟合的完整链路3.1 为什么评分卡必须走WOE而不是直接塞原始值逻辑回归本身能处理连续变量但信用评分卡场景里直接把原始值丢进去有几个硬伤。第一连续变量和违约概率之间往往不是线性关系额度使用率从 0.1 到 0.2 和从 0.8 到 0.9 对风险的影响完全不同。第二原始值里可能有极端离群点一个异常大值就能把系数拉偏。第三业务方要的是可解释的评分卡每个变量每个分档对应多少分原始连续值给不出这种粒度。WOE 转换解决的就是这几个问题。它先把连续变量离散化成若干箱再对每箱计算$$WOE_i \ln\left(\frac{Good_i / Good_{total}}{Bad_i / Bad_{total}}\right)$$其中Good_i是第 i 箱里好样本数Bad_i是坏样本数。WOE 把每个箱映射成一个对数值这个值天然和违约风险单调相关而且量纲统一不同变量之间可比。IV 值则是在 WOE 基础上算信息量$$IV \sum_i (Good_i/Good_{total} - Bad_i/Bad_{total}) \times WOE_i$$IV 用来做变量筛选一般 IV 小于 0.02 的变量直接丢0.02 到 0.1 弱预测力0.1 到 0.3 中等0.3 以上强。但 IV 过高也要警惕超过 0.5 往往意味着变量有泄漏或者分箱过细。3.2 分箱的实操等频、等距还是决策树分箱方法直接决定WOE质量。score.py里常见做法是等频分箱打底再根据单调性微调。等频保证每箱样本量接近避免某箱只有几个样本导致WOE剧烈波动。但等频不保证WOE单调可能出现中间某箱WOE反向。这时候要么合并相邻箱要么换决策树分箱。下面是一段可复用的等频分箱加WOE计算代码def woe_binning(df, feature, target, bins5): # 等频分箱duplicatesdrop 处理大量重复值 df[bin] pd.qcut(df[feature], qbins, duplicatesdrop) grouped df.groupby(bin)[target].agg([count, sum]) grouped.columns [total, bad] grouped[good] grouped[total] - grouped[bad] # 防止除零加极小值 grouped[bad_rate] grouped[bad] / grouped[bad].sum() grouped[good_rate] grouped[good] / grouped[good].sum() grouped[woe] np.log((grouped[good_rate] 1e-10) / (grouped[bad_rate] 1e-10)) grouped[iv] (grouped[good_rate] - grouped[bad_rate]) * grouped[woe] return grouped, grouped[iv].sum()pd.qcut的duplicatesdrop很关键。信用数据里像逾期次数这种变量大量样本集中在 0等频切分时边界会重复不 drop 直接报错。1e-10是防止某箱坏样本为 0 时取对数出错。返回的grouped里每箱的woe就是后续替换原始值的依据iv求和就是该变量的总信息量。实际跑的时候我会对每个候选变量调一次这个函数把 IV 汇总成一张表再决定留哪些。3.3 用IV筛选变量并替换为WOE拿到每个变量的 IV 后筛选逻辑很直接candidate_features [c for c in train.columns if c ! target_col] iv_summary {} for feat in candidate_features: try: _, iv woe_binning(train, feat, target_col, bins5) iv_summary[feat] iv except Exception as e: print(f{feat} 分箱失败: {e}) iv_df pd.DataFrame.from_dict(iv_summary, orientindex, columns[IV]) iv_df iv_df.sort_values(IV, ascendingFalse) selected iv_df[(iv_df[IV] 0.02) (iv_df[IV] 0.5)].index.tolist() print(入选变量:, selected)上限设 0.5 是我自己的习惯IV 太高的变量往往有问题宁可先排除再人工检查。try-except是因为有些变量可能全是缺失或者单一值分箱会直接抛异常不捕获的话整个循环断掉。入选变量确定后把训练集和测试集里这些变量的原始值替换成对应箱的 WOE 值得到纯数值矩阵再送进逻辑回归。3.4 逻辑回归拟合与评分转换WOE 替换完成后逻辑回归这一步反而简单from sklearn.linear_model import LogisticRegression from sklearn.metrics import roc_auc_score X_train train_woe[selected] y_train train[target_col] X_test test_woe[selected] y_test test[target_col] model LogisticRegression(C1.0, max_iter1000, solverlbfgs) model.fit(X_train, y_train) prob model.predict_proba(X_test)[:, 1] print(AUC:, roc_auc_score(y_test, prob))C1.0是默认正则强度信用评分场景样本量通常够大不太需要强正则。max_iter1000是因为 WOE 特征虽然量纲统一但维度可能十几二十个默认 100 次迭代有时不收敛。AUC 是评分卡最常用的评估指标一般 0.7 以上算可用0.75 以上算不错。但 AUC 只是排序能力真正上线还要看 KS 和分数分布。评分转换用标准公式$$Score A - B \times \ln(Odds)$$其中Odds p / (1-p)p是模型输出的违约概率。A 和 B 是尺度参数通常设定基准分和基准 Odds 反解出来。比如设定 600 分对应 Odds 为 1:1每增加 20 分 Odds 翻倍就能解出 A 和 B。这一步score.py里应该有对应实现核心就是把model.predict_proba的输出转成整数分数。4. 避坑与排查score.py 跑不通时先看这几处4.1 分箱报错 Bin edges must be unique现象执行到pd.qcut时抛ValueError: Bin edges must be unique。原因某个变量大量重复值等频切分时边界重叠。解决加duplicatesdrop或者先对变量做rank(methodfirst)再分箱。如果 drop 后箱数太少说明这个变量本身区分度不够考虑直接排除。4.2 WOE 出现 inf 或极端值现象某箱 WOE 算出inf或绝对值超过 10。原因该箱坏样本数为 0 或好样本数为 0取对数发散。解决分子分母加极小值1e-10只能防报错不能解决业务问题。更稳的做法是合并该箱到相邻箱或者对该变量改用更粗的分箱粒度。WOE 绝对值超过 3 就要警惕超过 5 基本说明分箱有问题。4.3 训练集 AUC 高但测试集崩现象训练集 AUC 0.85测试集只有 0.6。原因分箱边界是在全量数据上算的WOE 映射用了测试集信息造成泄漏。解决分箱和 WOE 计算只在训练集上做测试集用训练集的边界做映射。score.py如果是在全量数据上 fit 分箱再拆这就是典型翻车点。我一般会把分箱边界存成字典训练和测试各查各的表。4.4 逻辑回归系数符号与业务直觉相反现象逾期次数越多模型给的分数反而越高。原因WOE 计算时好坏样本定义反了或者分箱后 WOE 单调性没检查。解决WOE 公式里好样本在分子坏样本在分母算出来 WOE 越大代表越好。如果某变量 WOE 随原始值增大而增大但业务上应该是风险升高说明好坏定义反了。拟合前把每个变量的 WOE 和原始值关系画出来看一眼不费事但能省很多后悔药。4.5 缺失值处理不一致现象训练时能跑测试时某变量全空导致 WOE 映射失败。原因训练集缺失值被分到某一箱测试集该变量整列缺失映射不到任何箱。解决分箱时把缺失单独作为一箱或者用训练集中位数填充后再分箱。信用数据里缺失本身可能携带信息单独成箱有时比填充效果更好但要在 IV 计算时确认这一箱的区分度。5. 把评分卡跑成可复现流程从脚本到验证的进阶习惯score.py能跑通只是起点。真正要拿这套东西做课程设计或者往生产靠我建议在它基础上加三件事。第一把分箱边界和 WOE 映射表持久化。不要每次跑都重新算训练完把每个变量的箱边界、每箱 WOE 值存成 JSON 或 pickle。这样测试集映射、后续新样本打分都用同一套表避免每次结果不一致。我一般会在score.py同级建一个woe_mapping.json结构是{feature: {bin_label: woe_value}}加载时直接查表替换。第二加一段分数分布验证。评分卡输出不是看 AUC 就完事要看分数是否呈合理分布、好坏样本分数是否分离。简单做法是画两张直方图叠加import matplotlib.pyplot as plt scores A - B * np.log(prob / (1 - prob)) plt.hist(scores[y_test 0], bins50, alpha0.5, labelGood) plt.hist(scores[y_test 1], bins50, alpha0.5, labelBad) plt.legend() plt.xlabel(Score) plt.ylabel(Count) plt.title(Score Distribution by Target) plt.show()好样本分数应该整体右偏坏样本左偏重叠区域越小越好。如果两条分布几乎重合AUC 再高也没用说明分数没有区分度。这个图比 AUC 直观得多答辩或者汇报时放这张图比放混淆矩阵管用。第三做一次跨时间验证。如果数据有时间字段按时间切训练和测试而不是随机切。信用评分模型最怕时间穿越随机切分会让模型看到未来信息AUC 虚高。按时间切虽然 AUC 会降一些但那个数字才接近真实表现。我吃过这个亏随机切分 AUC 0.78按时间切只有 0.69差距全在泄漏上。还有一个习惯每次改完分箱或变量筛选把 IV 表、AUC、KS 三个数记下来。不用多复杂一个 CSV 追加一行就行。跑多了你会发现哪些变量稳定、哪些变量一换分箱就跳这种直觉比任何教程都值钱。从那以后我每次动分箱参数前都强制走一遍 IV 对比确认不是靠某个变量硬撑。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。