ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

信用风险等级评分全流程:基于逻辑回归的机器学习评分卡实现

信用风险等级评分全流程:基于逻辑回归的机器学习评分卡实现 简介这是一套面向金融风控与信贷审批场景的信用风险等级评分系统基于机器学习对信用卡申请人的历史数据进行建模帮助银行、消费金融等机构快速识别高风险客户提升审批效率与自动化水平。资源共4个文件包含源码、交互式Notebook、训练数据及说明文档压缩包整体18.19MB便于直接运行与二次开发。项目完整覆盖数据预处理、特征分箱、标签编码、探索性数据分析、多模型训练与评估等环节支持逻辑回归、随机森林、XGBoost等算法并输出混淆矩阵、ROC曲线、KS曲线及AUC等指标同时借助toad工具构建专业评分卡增强模型可解释性与监管合规性。目前已有149人学习适合具有一定Python基础、希望系统掌握信用评分卡开发流程的数据分析师或机器学习初学者入手实践。1. 机器学习做信用风险等级评分为什么黑盒模型在风控里走不通审批一笔信用贷系统三秒给出“建议拒绝”业务人员追问一句“为什么”模型答不上来——这是很多机器学习信用风险模型的通病准确率越漂亮解释性越差。而这套基于机器学习的信用风险等级评分系统就是冲着“回答上为什么”来的。它把从原始信贷数据到最终信用分的过程做成了完整可复现的链路好坏客户定义、WOE 分箱、逻辑回归训练、分数映射、上线监控每一步都有对应代码和配置。对想上手金融风控建模的从业者、做毕设或实训项目的学生它最大的价值不是“跑通一个模型”而是能照着它做出一张业务方真正能用的信用评分卡。下面按这套系统的实际结构从头到尾拆一遍。2. 从业务问题到评分卡先把风险定义和样本切分讲清楚做信用评分的第一步不是调参而是和业务方对齐一句话什么样的客户算“坏”。这个定义没定好后面所有 AUC 和 KS 都只是数字游戏。我之前见过一个项目业务说“逾期就算坏”结果模型训练出来把逾期 1 天的客户和逾期 90 天的客户混在一起模型完全学不出风险梯度。所以这一章先讲这套系统里最容易被跳过的两块好坏定义和时间切分它们是整个评分卡的地基。2.1 定义“坏客户”表现期与观察期怎么定信用评分里有三个时间概念要分清观察期、表现期、审批时点也常叫放款时点。观察期是提取特征的时间窗口比如客户申请前 12 个月表现期是看客户最终好坏的窗口比如放款后 12 个月审批时点就在二者中间。核心问题是“坏客户”用什么口径。常见做法是按逾期天数 DPD 分档DPD 30、DPD 60、DPD 90。这套系统默认用的是 D≥30 且表现期内未清偿表现期设为 12 个月。为什么是 12 个月因为现金贷产品 12 个月后风险基本暴露你把这个窗口压缩到 3 个月样本是多了但很多短账龄客户还来不及变坏标签会被打错模型区分度会被悄悄拉低。从还款表生成标签的逻辑是客户在表现期内任意一个月 DPD 达到 30 且后续没有还清就标成坏客户。写代码时注意别把同一个客户多个月的重复记录算成多个样本每个客户只保留一条样本、一个标签。如果切到 D≥90坏客户定义更严格但坏样本占比会暴跌后面调类别不平衡会很酸爽。我的建议是先用 D≥30 搭第一版基线跑通流程后再看要不要和业务口径对齐换成 D≥60 或 D≥90。提示第一版别在坏客户定义上反复纠结先让整个链路转起来再回来收紧口径。2.2 样本选择与时间切分拒绝推断的前置逻辑样本从哪来不是把全量申请记录都丢进去。已放贷且表现期结束的客户有确定好坏标签可以直接做训练样本被拒绝客户没有还款表现标签是缺失的。这部分客户如果完全扔掉模型只在“通过客群”上学习上线后遇到过去被拒、但风控策略已经变化的客户预测就会有偏差。这就是拒绝推断要解决的问题。这套系统的做法比较朴素对拒绝样本做简单扩充按已放贷样本的好坏比例回填标签然后放进训练集。业内更严格的方案有 Ai 等但对一个第一版评分系统来说回填法已经能把样本偏差压下去一大截。实操里要控制回填样本量别超过真实放贷样本的 30%不然训练集被“假标签”主导模型照样乱。回填的核心假设是拒绝客户的风险结构和通过客户相同这在短期策略不放宽的阶段是可接受的近似。时间切分是另一个大坑。不能把近几年的数据一次性随机切 8:2因为特征分布和客群结构一直在变。正宗做法是按时间顺序切拿 2022 年 1 月到 2023 年 6 月放款的样本训练拿 2023 年 7 月到 2023 年 12 月放款的样本做验证集。随机切分最直观的恶果是同一个客户的多个贷款记录可能同时出现在训练集和验证集里造成信息重叠线上表现被高估。这套系统的 README 里默认就是这个逻辑训练脚本会读取 config 里的 start_date 和 end_date保证验证集永远在训练集之后。2.3 运行前环境准备依赖包和配置文件改动环境建议 Python 3.9 以上核心依赖是 pandas、numpy、statsmodels、scikit-learn、pyyaml。拿到项目先按 requirements.txt 装依赖然后用编辑器打开 config 下的 base_config.yaml里面这几项是必改的observe_months: 12 # 观察期取特征的时间窗口 performance_months: 12 # 表现期看客户好坏的时间窗口 bad_definition: dpd_over: 30 # 逾期超过30天且未清偿视为坏客户 train_window: start: 2022-01-01 end: 2023-06-30 valid_window: start: 2023-07-01 end: 2023-12-31 output_score: base_score: 600 # 基准分 base_odds: 19 # 基准好坏比 pdo: 50 # 好坏比翻倍时增加的分数train_window 和 valid_window 的顺序在脚本里会被严格校验验证集开始日期必须晚于训练集结束日期改错会直接告警——这是故意设计的为了防止不小心样本穿越。output_score 里三个参数直接影响最终信用分的量纲先保持默认。2.4 系统目录结构模型文件、配置与特征清单这套项目的文件组织和真实风控工程基本一致拿到手先看一遍目录再动代码。主要模块如下路径作用data/apply_data.csv申请记录主表含特征字段与审批结果data/repay_data.csv还款表现表用于生成坏客户标签config/base_config.yaml好坏客户定义、观察期与表现期参数feature_pipeline/woe_iv.py分箱、WOE/IV 计算与特征筛选train/train_lr.py逻辑回归训练与系数、P 值输出score/score_mapping.py线性预测转信用分输出评分卡monitor/psi_report.py上线后的 PSI、KS 监控报表README.md环境依赖、运行顺序与参数说明先改 config 里的坏客户口径和日期范围再按 README 顺序跑 woe_iv.py、train_lr.py、score_mapping.py。注意 config 里每一行都留了注释这是我拆项目时最喜欢看到的习惯——参数不明就运行最后八成要翻车。3. 特征工程与 WOE 分箱把原始数据变成可信度高的风险信号机器学习项目里有一句话特征决定了模型的上限算法只决定逼近上限的速度。放到信用评分场景里尤其如此。信贷数据里真正有用的信号就那些能不能把它们变成“单调、可解释、稳定”的输入直接决定评分卡能不能上线。这一章按这套代码的实际处理顺序讲先洗数据再分箱最后筛特征。3.1 缺失率与异常值清洗先砍无效字段拿到原始表先做三件事看缺失率看单一取值占比看极端值分布。这套系统里默认两个硬规则缺失率超过 70% 的字段直接剔除单一取值占比超过 95% 的字段直接剔除。理由很简单那么多缺失的字段填充后带来的噪声远大于信号单一取值占比过高的字段在逻辑回归里就是一个常数没有区分度还容易引入共线性。连续变量的极端值处理我一般用分位截断。比如收入字段99 分位是 12 万那超过 12 万的全部按 12 万计1 分位如果是 0那 0 以下的异常值也截掉。这样做的目的不是“让数据更好看”而是避免极端值把分箱边界拉偏导致某个箱样本量极少、IV 虚高。需要特别注意清洗规则必须保存成 json 或 pickle上线推理时对同一特征用同一套截断值。很多项目训练集和线上代码各写一份结果训练时收入上限 12 万线上变成 20 万上线一周后 PSI 直接爆表。这个问题在第 5 章还会专门展开。3.2 分箱与 WOE/IV 计算逻辑回归前的标准动作为什么要分箱一个原因是信贷特征和坏率的关系基本不是线性的比如年龄和逾期率是 U 型30 岁和 55 岁风险偏高中间 35 到 45 岁最好。直接拿原始年龄进逻辑回归模型只能拟合一条直线效果自然差分箱后每个箱可以用一个 WOE 值代替就能把 U 型关系表达出来。另一个原因更现实分箱后的特征对异常值不敏感换了额度口径或者换了数据源箱边缘不容易跳变。分箱有三条路线等频分箱、等距分箱、业务分箱。等频分箱就是每个箱的样本量接近qcut 直接做等距分箱适合收入、额度这类跨度大的变量业务分箱则优先按业务规则切比如年龄按 18/22/30/40/50 切征信查询次数按 0/1/3/6 切。这套系统的默认实现是等频分箱但保留了手打分箱的接口。import numpy as np import pandas as pd def calc_woe_iv(df, feature, target, bins10): 对单个特征分箱并计算 WOE 与 IV 参数: df: 训练数据 feature: 待分箱特征名 target: 0好客户, 1坏客户 bins: 等频分箱的箱数 返回: woe_df: 每箱的样本量、好坏占比与 WOE 值 iv: 该特征整体的 IV 值 data df.copy() data[feature] data[feature].fillna(-999) # 缺失值单独落入边远箱 try: data[bin] pd.qcut(data[feature], qbins, duplicatesdrop) except ValueError: # 样本太少或取值太集中时qcut 会失败退化为等距分箱 data[bin] pd.cut(data[feature], binsbins) grouped data.groupby(bin, observedFalse).apply( lambda d: pd.Series({ total: len(d), good: (d[target] 0).sum(), bad: (d[target] 1).sum(), }) ).reset_index() total_good grouped[good].sum() total_bad grouped[bad].sum() grouped[good_pct] grouped[good] / total_good grouped[bad_pct] grouped[bad] / total_bad # WOE 衡量每个箱的好坏比例相对整体的差异 grouped[woe] np.log(grouped[bad_pct] / grouped[good_pct]) # IV 是各箱 (坏占比-好占比) * WOE 的累加 grouped[iv] (grouped[bad_pct] - grouped[good_pct]) * grouped[woe] return grouped, grouped[iv].sum()逻辑说明WOE 的计算公式里bad_pct / good_pct 大于 1 时 WOE 为正说明这个箱的坏客户占比高于好客户箱的风险偏高反过来 WOE 为负说明偏安全。IV 则是把每个箱的好坏差异按占比加权累加数值越大说明这个特征对坏客户的区分能力越强。这套代码的输出会同时打印每箱的 WOE 值和整体 IV我一般都会逐箱扫一眼走势确认是否符合业务直觉。参数说明q10 表示等频十箱适合大多数连续变量duplicatesdrop 处理连续变量取值重复太多导致的分位点重合缺失值填 -999 是为了让缺失单独落入最边远的箱避免和正常值混在一起打乱分箱边界。如果某个特征取值太少导致 qcut 抛错异常分支会自动退化成等距分箱保证脚本不会中途崩掉。注意如果某个箱里的坏样本为 0WOE 会算成无穷大。实际代码里我给 good 和 bad 各加 0.5 做平滑或者直接把 0 箱和相邻箱合并。平滑值会影响 IV 绝对值所以同一个项目里要全局统一。3.3 特征筛选脚本IV 阈值与相关性去重分箱和 WOE 计算完手上可能有几十个特征。不能全塞进逻辑回归一是维度高容易过拟合二是特征间相关性高会导致回归系数不稳定。这套系统给了一个贪心筛选思路先按 IV 排序保留 IV 不低于阈值的特征然后逐个检查与已保留特征的相关性相关系数过高就丢掉低 IV 的一方。def select_features(woe_iv_dict, corr_matrix, iv_threshold0.02, corr_threshold0.7): 按 IV 过滤并去重高相关特征 参数: woe_iv_dict: 特征名到 IV 值的映射 corr_matrix: 特征的 Pearson 相关系数矩阵 iv_threshold: IV 低于该值的特征直接剔除 corr_threshold: 与已选特征相关系数超过该值则丢弃 candidates {f: iv for f, iv in woe_iv_dict.items() if iv iv_threshold} keep [] for feature, value in sorted(candidates.items(), keylambda x: x[1], reverseTrue): drop False for kept in keep: if abs(corr_matrix.loc[feature, kept]) corr_threshold: drop True break if not drop: keep.append(feature) return keep逻辑说明先检查每个特征是否已经被更强的特征“覆盖”。比如“近 3 月查询次数”和“近 6 月查询次数”相关系数通常能到 0.8 以上这时 IV 高的那个保留另一个丢掉。这段代码返回的 keep 列表就是后续逻辑回归的入模特征。参数说明IV 阈值 0.02 是行业常见的弱特征下线低于这个值意味着区分度基本可以忽略相关阈值 0.7 属于中等偏严如果保留特征太少可以放松到 0.75。入模特征一般控制在 10 到 20 个之间太多会让评分卡的系数解释变成体力活——这一点在正规风控里尤其看重。4. 训练与评估逻辑回归、集成模型和分数映射模型选型这一块很多初学者会把 XGBoost 当成默认答案但信用评分卡场景里逻辑回归才是绝对主力。原因不是 XGBoost 效果差而是监管和业务要求“每个因素对分数的影响可解释、可复核”。逻辑回归的系数和 WOE 乘起来一眼就能看出“征信查询次数每增加一个档分数降低多少分”换成树模型就只能靠 SHAP 解释现场被审计问两句就招架不住。所以这套系统的主模型是逻辑回归XGBoost 只用来做区分度对标。4.1 逻辑回归训练基线P 值、系数方向与正则化训练逻辑回归我用 statsmodels 而不是 sklearn原因很直接statsmodels 自带 P 值和置信区间能直接看到每个特征的显著性。评分卡项目里入模特征必须在统计上显著这是硬要求。import statsmodels.api as sm def train_lr(woe_df, features, targetis_bad): 训练逻辑回归并输出统计摘要返回模型和不显著特征列表 参数: woe_df: 已经完成 WOE 映射的训练数据 features: 入模特征列表WOE 化之后 target: 标签列名 X woe_df[features].copy() X sm.add_constant(X) # 加截距项statsmodels 不会自动加 y woe_df[target] model sm.Logit(y, X).fit(disp0) print(model.summary()) pvalues model.pvalues.drop(const) bad_feats [f for f in pvalues.index if pvalues[f] 0.05] return model, bad_feats逻辑说明WOE 化的特征已经带了“风险方向”信息理论上高风险箱 WOE 较大逻辑回归系数也应该为正。训练完第一件事不是看 AUC而是看每个系数的正负号是否和业务直觉一致。比如“负债收入比”分箱后高负债箱 WOE 是正的系数却变成负的这大概率是共线性或分箱方向反了要回查数据而不是继续往下走。参数说明train_lr 返回的 bad_feats 是 P 值大于 0.05 的特征通常做法是剔除后再训一轮反复迭代两三次直到所有入模特征都显著。遇到样本量很小、P 值压不下去的情况可以放宽到 0.1但要在模型说明文档里写明否则后面审计复核对不上账。4.2 KS、AUC 与 PSI三个指标盯住区分度与稳定性信用评分模型上线前主要看三个指标。AUC 衡量模型把所有好客户排在坏客户前面的概率KS 衡量好坏两个群体分数分布的最大分离程度PSI 衡量新旧样本的分数分布是否一致。这里给一张表把三个指标的口径说清楚指标看什么合理区间翻车信号AUC排序能力0.72~0.82超过 0.95 优先怀疑样本穿越KS好坏样本累积分布最大差0.25~0.45KS 虚高但上线后频繁波动PSI分数分布稳定性小于 0.1超过 0.25 考虑重训训练阶段用前两个上线后用第三个。很多项目把 AUC 当成唯一 KPI结果上线一个月分数分布大幅偏移才发现客群结构已经变了。评分卡是长期在线的东西稳定性比区分度更值钱。KS 的快速计算做法是直接把好坏客户两组分数跑双样本检验或者自己算累积分布差的最大值def ks_score(score_series, bad_flag): 按分数排序后计算好坏客户累积占比的最大差值 df pd.DataFrame({score: score_series, bad: bad_flag}).sort_values(score) df[cum_bad] df[bad].cumsum() / df[bad].sum() df[cum_good] (1 - df[bad]).cumsum() / (1 - df[bad]).sum() return (df[cum_bad] - df[cum_good]).abs().max()逻辑说明分数从低到高排列低分段坏客户占比应该远高于高分段累积占比差异的最大值就是 KS。这个值 0.3 以上意味着模型有实用价值低于 0.2 时基本要和策略规则谈合作了。4.3 分数映射公式把模型输出转成 300~900 的信用分逻辑回归输出的是一组概率业务方不认这个。他们认的是“分数越高风险越低”最好还是 300 到 900 这种整型分。这就用到经典的线性分数映射分数等于 offset 加上 factor 乘上模型输出的线性预测值 ln(odds)。先约定两个参数base_score 是基准分对应约定的好坏比pdoPoint to Double Odds是“好坏比翻倍时增加的分数”。这套系统默认设置是 base_score600base_odds19pdo50也就是好坏比 19:1 的客户拿 600 分好坏比 38:1 的客户拿 650 分。def linear_to_score(linear_pred, base_score600, base_odds19, pdo50): 将逻辑回归的线性输出映射为 600 为基准的信用分 参数: linear_pred: 模型输出的 ln(odds) base_score: 基准分 base_odds: 基准好坏比好客户数为坏客户数的倍数 pdo: 好坏比翻倍时分数的增加值 factor pdo / np.log(2) offset base_score - factor * np.log(base_odds) score offset factor * linear_pred return np.round(score, 0).astype(int)逻辑说明factor 在这里约等于 72.13offset 约等于 387.7。把某个客户的 woe 值和系数代入 linear_pred加 offset 就得到分数。分数越高风险越低和业务直觉一致。这套映射逻辑不依赖具体模型但每次重训后系数变了、分数尺度会漂移所以每次重训都要重新生成映射。参数说明想让分数分布落在 300~900可以先训练完线性预测看它的 5% 和 95% 分位值再反推 base_score 和 base_odds保证两端分数正好落在区间边缘附近。默认的 600/19/50 是业界常见起点跑完第一版再微调。验证分数是否正确有个土办法挑两个客户好客户线性预测值是 1.5坏客户是 0.5二者分差应该约等于 factor。如果差出几十分多半是 offset 带入时 log 的底数写错或者 base_odds 传反了。5. 上线避坑指南样本穿越、共线性与 PSI 漂移的排查记录这一章全是血泪经验。我在拆这套系统和做其他风控项目时反复遇到的就是下面五个问题每个都曾经让我在深夜对着日志怀疑人生。这里按“现象、原因、解决”三条线写清楚方便你以后直接对照排查。5.1 样本穿越AUC 高到吓人的时候先查时间口径现象训练集 AUC 跑到 0.98验证集也漂亮得不像话结果线上试跑一周头部客户的分数分布明显异于训练集统计。原因这是典型的样本穿越也叫特征泄漏。回头看代码我在构造“历史逾期次数”特征时用了客户放款之后的表现数据来填特征也就是模型在“看答案做题”。另一个常见来源是用了全量数据做标准化时把验证集的信息也算进了均值方差。评分卡场景里训练集、验证集必须严格按时间顺序切分特征取值时间必须早于审批时点。解决把所有特征统一到“审批时点前的可获取信息”口径。对特征做一次时点审计对每个特征标注它的数据截止时间再对比审批时点确保所有特征都在审批时点之前。那一版模型全部重训之后AUC 从 0.98 回落到 0.79这才是一个可信的模型该有的样子。5.2 类别不平衡坏客户太少模型只管说“好”现象坏样本占比只有 2.5%模型训练完坏客户基本全被判成好客户。阈值调到 0.1 之后过件率掉了一大截业务直接找上门。原因逻辑回归默认在 0.5 阈值下决策但 2.5% 的坏样本占比意味着先验概率极低模型输出概率整体被压到很小的范围0.5 根本够不着。这不代表模型没学到东西只是阈值选错了。解决两个动作结合。第一训练时给坏客户样本加权权重按好坏比反比设置比如好坏比 1:40那坏样本权重设为 40 左右第二不要用 0.5 当阈值而是画出 KS 最大点对应的分数作为初始 cutoff再结合业务的通过率调整。这套系统里 KS 计算脚本输出的 max_ks_score就是默认的阈值参考点。5.3 特征共线性系数符号和业务直觉相反现象负债收入比这个特征业务上明显是越高风险越大分箱后高负债箱 WOE 为正逻辑回归系数却是负的。单独删掉这个特征后其他特征的系数又变正常了。原因WOE 化之后的特征之间仍然可能有强相关性尤其是多个变量来源于同一个底层字段时比如“近 3 月查询次数”“近 6 月查询次数”“近 12 月查询次数”三者相关系数能到 0.9。回归模型在强共线下会把系数分摊到多个变量上出现符号反转一点也不稀奇这叫系数不稳定不是模型“学错了”。解决入模前跑一遍相关系数矩阵和 VIF。VIF 大于 10 的特征先剔除或合并同一信息源只保留最强的一个特征。第 3.3 节那个 select_features 就是干这个用的。从那以后我入模特征始终控制在 15 个以内系数符号再没翻过车。5.4 PSI 漂移上线一个月分数分布开始翻车现象上线首月分数分布和训练集基本一致第二个月开始平均分明显向高处移动通过率变高业务说“最近客户质量变好了”但风控心里清楚这不太可能是好事。原因PSI 漂移通常不是模型坏了而是入模特征的分布变了。可能是获客渠道换了人群可能是某个外部数据源开始缺数也可能是客群干扰。特征层面的 PSI 比分数层面的 PSI 更有诊断价值。解决分数 PSI 超过 0.1 进入关注超过 0.25 必须回查特征。做法是逐特征算 PSI把贡献最大的那个特征拉出来看分箱占比变化和缺失率变化。这套系统的 monitor/psi_report.py 会输出特征级 PSI 排序重点盯排序前 5 的特征。找到漂移源之后要么修正数据管道要么把漂移特征下线并重训。5.5 分箱边界不固定训练和线上对不上箱现象同一个客户在离线回放时落在第 3 箱线上 scoring 却落到了第 4 箱分数差出三四十分问题出在分箱逻辑“训练一份、线上另一份”的双轨制。原因pd.qcut 在不指定分位点的情况下每次运行都会重新计算分箱边界。训练脚本跑的时候数据分布是那样线上脚本跑的时候数据分布已经变了边界自然对不上。哪怕数据没变qcut 对重复值的处理也可能让边界偏移一位。解决训练完成后把每个特征的分箱边界存成 json 或 pickle上线推理时用 pd.cut 传入固定的 bins而不是重新 qcut。每次重训前人工检查一遍边界文件是否和训练输出一致。我在项目里加了一个校验函数训练完自动比对“训练分箱结果”和“边界文件重放结果”不一致直接报错从此杜绝了这个低级但杀伤力极大的问题。6. 上线后的模型监控与版本迭代把评分系统当成一个需要运维的产品评分卡上线不是终点而是另一场长征的起点。这一章给一个我实际在用的监控与迭代节奏直接拿来就能搭。先给一张监控动作表按周、月、季度拆开频率动作阈值或触发条件每日实时分数分布直方图分数均值波动超过 5 分告警每周特征级 PSI 报告任一特征 PSI 超过 0.1 进入观察每月KS、AUC 回溯计算月度 KS 低于初始值的 80% 触发重新评估每季度完整重训候选评审客群结构变化或坏定义调整版本迭代的判断条件有三个特征 PSI 连续两月超 0.25KS 比上线时下滑超过 20%业务方调整了坏客户定义比如从 D≥30 改成 D≥60。满足任意一条就把当前数据按同样的流程重跑一遍生成新的训练集、新分数映射然后做一次历史样本回放对比。历史样本回放是我强烈建议加的一步把新模型应用到最近 6 个月的历史客户上对比新旧模型的分数分布和排序一致性确认没有“局部颠覆式”的变化。这一步能挡住绝大多数数据口径错位的问题。我现在的习惯是任何新模型版本都要先跑回放、再上灰度灰度比例从 10% 开始观察两周 PSI 无异常后才全量切换。还有一个小技巧分数 cutoff 不要只看 KS 最大点。要同时算两个指标一个是通过率一个是坏账率在业务可接受的坏账上限下找通过率最大化的分数阈值。这套系统里我把这两个指标做成了同一张图输出每次调阈值直接看图说话。整套代码按 README 顺序执行就能复现整条评分卡链路下载后重点看 feature_pipeline/woe_iv.py 和 monitor/psi_report.py 这两个文件。说回踩过的坑从那次 AUC 0.98 的样本穿越之后我现在每次交付评分系统都强制走一遍三件事特征时点审计、固定分箱边界、历史回放对比。宁可慢半天也不让模型裸奔上线。希望帮到你。本文还有配套的精品资源点击获取
返回列表