
简介一份关于小额贷款公司个人贷款信用风险评估的研究PDF文档聚焦Logistic与Probit组合模型在小额信贷场景中的应用面向金融风控从业者、信贷审核人员及学术研究者旨在解决优质客户被传统金融机构垄断背景下如何有效识别违约风险的问题。文档从信用风险评估的重要性切入系统梳理Logistic回归与Probit概率模型的适用性并详细展示组合模型的构建过程结合真实小额贷款公司案例验证组合模型识别准确率约70%可为贷款决策提供相对可靠的数据支撑同时强调提升信贷控制人员职业操守与判断能力的必要性。文件共1个PDF大小约1.88MB内容精炼完整适合快速获取模型原理与应用思路。目前已有156人学习适合作为个人信贷风控建模的参考材料。1. 小额贷款公司的个人信贷评分为什么绕不开Logistic与Probit组合小额贷款公司的个人贷款信用风险评估最容易被新框架带偏XGBoost、LightGBM、深度学习听上去一个比一个能打。但真动过小贷数据的建模师都知道坏样本常常只有几百条字段缺失率三成起审批评分卡还要求每个变量都讲得出业务逻辑。这种约束下最稳的基线一直是Logistic和Probit这一对经典模型以及它们的组合。组合模型不是为了炫技而是用两种不同的分布假设去交叉验证同一批客户在小样本下把排序稳定性和概率校准同时照顾好。下面把模型差异、组合权重、Python实现和边界踩坑串成一条可复现的路径适合正在搭评分卡、或者在优化存量授信模型的风控同学。2. Logistic与Probit的模型差异决定了组合权重怎么设2.1 连接函数差在哪Logistic的厚尾与Probit的薄尾Logistic模型写出来是 P(Y1|X)1/(1e^{-Xβ})两边取对数后就是 ln(P/(1-P))Xβ它假设“违约几率的对数”与自变量线性相关。Probit模型写成 P(Y1|X)Φ(Xβ)Φ是标准正态分布的累积分布函数它假设的是“违约倾向的潜变量”服从正态分布观察到的违约行为是这个潜变量跨过某个门槛的结果。这两个模型的差别不只是公式形态不同连接函数直接把同一个Xβ映射到了不同的概率形状上。在 Xβ0 的位置两个模型给出的违约概率都是 0.5越往两端走差异越明显。Logistic背后的逻辑分布尾部比正态分布厚概率在0和1附近变化得更慢Probit的正态尾部更薄线性预测值到±2左右概率就被推向极端。业务上的直接后果是同一个低分客户Probit给出的违约概率往往比Logistic更高更早触发拒绝同一个高分客户Probit给出的违约概率更低定价上就敢给更优惠的利率。Logistic相对温和会把更多客户留在0.3到0.7的中间区间特征变化在中间段更容易反映到评分上。我见过不少团队选Probit是因为老板希望“高风险客户拒绝得更果断”。这不算错但要明白这不是哪个模型更准而是分布假设刚好匹配了风险偏好。真正的坑在系数对比上同样数据跑出来Logistic系数几乎都大于Probit因为逻辑分布标准差约1.81标准正态是1两者线性预测值的刻度天生差了1.6到1.8倍经验上常见1.7左右。拿系数绝对值比变量重要性是外行做法要比就用边际效应Logistic的边际效应是 β_j·P(1-P)Probit是 β_j·φ(Xβ)都得在给定X的取值下才算得出来。最稳妥的是报告样本均值处的平均边际效应或者把所有连续变量标准化后再进模型。2.2 Probit更值钱的场景尾部定价与样本选择小贷风控里Logistic是行业默认原因很实在评分卡分数可以直接由log-odds映射系数可以换算成每个分箱的分数理解、上线、审计都方便。但Probit并不是多余选项它最值钱的两个场景反而被大多数团队忽略了。第一个场景是尾部客户集中。小贷客群和信用卡客群不同风险分布往往在低分段堆得很厚。Logistic尾部厚低分客户的违约概率上升得慢拒绝线附近的风险区分不够锐利Probit尾部薄同样一个Xβ变化低分段的违约概率被推得更极端拒绝线切下去更果断。如果公司风险偏好比较激进或者贷后催收资源有限Probit输出在低分段的排序稳定性和业务收益可能明显好于Logistic。我在实际项目里的做法是把两个模型的概率都算出来在低分段按每5个百分点分位数拉一张坏账率对比表比争论哪个模型理论更优有用得多。第二个场景是样本选择校正。小贷公司建模数据极少是纯随机进件的要么业务只给了经过人工预审的客户要么流量渠道只进了有意愿的客群。直接对这批样本跑Logistic参数会带上选择偏差。Probit的潜变量结构天然支持Heckman Probit这类两阶段扩展能在一个模型里同时处理“是否获批”和“是否违约”两道关口。所以我的组合方案从来不是二选一而是让Logistic做对外主评分卡Probit做样本选择校正、尾部概率校准和内部复核。2.3 组合权重的三种设法和Brier Score的优先地位组合模型绝不等于把两个模型概率做算术平均虽然我见过不少项目就是这么干的。等权平均的前提是两个模型精度接近且误差独立小贷数据里两个模型AUC差距通常在0.005以内等权不是最差选择但它浪费了Brier Score提供的信息。实际落地时三种权重方案方案权重公式适合场景等权平均w_L w_P 0.5快速基线、两模型验证集性能相当Brier加权w_L (1-B_L) / ((1-B_L) (1-B_P))以概率校准为目标的授信额度策略AIC加权w_i e^{-0.5ΔAIC_i} / Σ e^{-0.5ΔAIC_j}模型平均上会评审、强调统计依据三者里我更常默认Brier加权。Brier Score是概率预测的均方误差 (1/n)Σ(p_i - y_i)²直接衡量预测概率和实际结果的贴近程度。小贷审批线上真正参与额度决策的就是概率本身所以Brier和最接近业务目标。为什么不推荐AUC加权AUC只在乎排序不关心概率绝对值小样本下两个模型的AUC差本身标准差很大权重会跟着样本切分大幅抖动Brier对概率校准敏感样本间更稳定。我一般以Brier加权为主方案同时把等权和AIC权重的结果都算一遍如果三种权重在测试集上的排序没有实质差异说明模型关系稳定可以放心上线。2.4 组合模型为什么在小样本下更稳有人会质疑两个模型共享同一批特征和同一个坏样本标签预测误差高度相关组合大概率白忙。实际跑下来不完全是这样。因为连接函数不同它们对边界样本的预测偏差并不一致组合后的预测误差在Brier意义上大多会下降AUC则持平或微升不到0.005。从方差拆解看两个估计量等权平均的误差方差大致是 (σ₁²σ₂²2ρσ₁σ₂)/4只要相关系数ρ不逼近1平均就有收益。小贷数据里Logit和Probit的概率相关性通常落在0.97到0.99看着挺高但正是那1%到3%的不一致区域决定了极端客户的排序而这部分恰恰是利润和损失最集中的地方。所以评估组合模型不要只盯整体AUC要看两个尾巴段上的KS和坏账率变化。组合模型大多赢在尾巴。如果组合后在验证集上AUC明显低于单模型通常不是组合方式出了问题而是其中某个单模型在测试集上已经过拟合先回头检查特征分箱和样本切分别急着换权重公式。3. 跑通LogisticProbit组合风险评分Python操作流程与参数3.1 先定三个口径好/坏定义、观察期、表现期任何小贷评分项目动手第一件事不是选模型而是把口径锁死。假设原始表里有申请时间、贷后最大逾期天数以及一批申请时点已可获取的特征字段我通常这么开样本import pandas as pd import numpy as np import statsmodels.api as sm from sklearn.metrics import roc_auc_score, brier_score_loss # 假设表里有 apply_date 申请时间、overdue_days 贷后最大逾期天数 # 以及一堆申请时刻就可获取的特征dti、query_12m、card_util... df pd.read_csv(loan_app_data.csv, parse_dates[apply_date]) # 坏定义先用 90 天逾期作为 M3 口径具体后面再验证 df[bad] (df[overdue_days] 90).astype(int) # 观察期和表现期切分放款日在 2022-07-01 之前且已满 6 个月表现期 train df[(df[apply_date] 2022-07-01) (df[apply_date] 2020-01-01)].copy() test df[(df[apply_date] 2022-07-01) (df[apply_date] 2023-01-01)].copy() print(train bad rate:, train[bad].mean()) print(test bad rate:, test[bad].mean())这段代码把三个最关键的时点纪律写死了。bad用overdue_days 90只是起点很多小贷公司最后会改用M2因为产品期限短、M2之后催回率往往断崖下降。到底用哪一档要去看M0→M1→M2→M3的滚动率找“跨过这一档几乎回不来”的节点而不是拍脑袋。观察期和表现期之间必须留出干净边界放款日小于2022-07-01的样本表现期至少满6个月bad标签才没有被截断。test切在之后三个月模拟上线后的客群。如果train和test的坏率差超过30%先查渠道投放或进件政策变动不要急着进建模。3.2 特征分箱与WoE让变量先变成可解释的刻度小贷模型的解释性要求在WoE分箱这一步就开始了。连续变量直接进Logistic也能跑但分箱后每个区间对应一个度、一个坏账率后续做评分卡映射会方便得多。我常用的分箱逻辑是先看分布再决定用等频还是等宽def woe_binning(df, col, targetbad, bins10): tmp df[[col, target]].dropna(subset[col]) try: tmp[bin] pd.qcut(tmp[col], qbins, duplicatesdrop) except ValueError: tmp[bin] pd.cut(tmp[col], binsmin(bins, tmp[col].nunique())) grouped tmp.groupby(bin, observedTrue)[target].agg([count, sum]) grouped[bad_rate] grouped[sum] / grouped[count] grouped[good] grouped[count] - grouped[sum] total_bad, total_good grouped[sum].sum(), grouped[good].sum() # 真实项目必须先把 0 坏箱并入相邻箱否则这里会算出 inf grouped[woe] np.log((grouped[good] / total_good) / (grouped[bad] / total_bad)) grouped[iv] ((grouped[good] / total_good - grouped[bad] / total_bad) * grouped[woe]) return grouped woe_binning(train, dti, bins10)pd.qcut按分位数切分适合分布偏的变量当某个分位点的值重复太多会抛ValueErrorexcept里用pd.cut按等宽兜底。woe是“好客户占比除以坏客户占比再取对数”woe越大表示这个箱越偏向好客户iv是各箱信息量加权求和行业里一般IV小于0.02的变量直接放弃0.1到0.3算中等区分力超过0.3要警惕是不是有泄漏。代码里故意没做0坏箱合并这是小样本分箱最容易翻车的点某个箱里一个坏样本都没有时woe会算出inf后面模型直接被这个变量带飞。真实项目里要先用 grouped[grouped[sum] 0] 扫一遍再人工合并相邻箱。3.3 分别拟合Logistic与Probit模型参数与收敛细节分箱和变量筛选做完就到拟合环节。statsmodels把Logit和Probit接口做得完全对齐代码几乎一模一样但这恰恰让很多人忽略了一个事实两个模型的收敛风险来自完全相同的地方处理顺序也一样。cols [dti, query_12m, card_util, m1_count, age, amount, term] X_train sm.add_constant(train[cols]) X_test sm.add_constant(test[cols]) # Logistic 与 Probit 分别拟合dispFalse 只关迭代日志 logit_model sm.Logit(train[bad], X_train).fit(dispFalse) probit_model sm.Probit(train[bad], X_train).fit(dispFalse) # 输出测试集上的违约概率 pred_logit logit_model.predict(X_test) pred_probit probit_model.predict(X_test)如果日志里出现 “Maximum Likelihood optimization failed to converge”不要先去调method按顺序查三件事一是有没有0坏箱或0好箱导致WoE分箱出现极端值二是特征之间有没有强共线性三是某个变量是不是和坏标签高度线性可分。解决顺序是先回到3.2合并分箱再查相关性矩阵最后才考虑把连续变量标准化或者把method换为bfgs。fit(dispFalse)只是关掉迭代输出不影响估计结果。拟合完先别急着组合把 model.summary() 打开看几个数系数符号是否符合业务直觉Pseudo R-squared是不是高得离谱比如超过0.8大概率有泄漏特征系数的标准误是不是有几位数大。两个模型系数刻度不同logit大约是probit的1.6到1.8倍这个已经说过不要拿它们直接对比。3.4 计算组合权重、效果校验与评分卡分数映射模型拟合完就到了组合权重的核心计算# 两模型在测试集上的 Brier Score越小代表概率预测越准 brier_logit brier_score_loss(test[bad], pred_logit) brier_probit brier_score_loss(test[bad], pred_probit) # Brier 加权(1-brier) 作为质量分再归一化成权重 w_logit (1 - brier_logit) / ((1 - brier_logit) (1 - brier_probit)) w_probit 1 - w_logit pred_comb w_logit * pred_logit w_probit * pred_probit print(AUC logit/probit/comb:, roc_auc_score(test[bad], pred_logit), roc_auc_score(test[bad], pred_probit), roc_auc_score(test[bad], pred_comb)) print(Brier logit/probit/comb:, brier_logit, brier_probit, brier_score_loss(test[bad], pred_comb))Brier加权的逻辑很直接哪个模型的概率校准更准哪个模型在组合里话语权就更大。有一个纪律必须遵守权重必须在训练集或验证集上计算绝不能拿到测试集上算否则组合权重自己就在偷看未来。组合概率的AUC通常和单模型持平甚至略低但Brier一般会下降一点这是正常的组合模型换来的不是整体排序提升而是概率校准的稳定性。如果这家小贷公司最终要的是标准评分卡就把组合概率映射成分数Score offset factor * ln(p/(1-p))。factor决定odds翻多少倍时分数增加多少常见取20/ln(2)≈28.85也就是分数每20分违约几率翻一倍offset根据公司的基准违约率回推比如希望600分对应5%违约率就把基准odds代进去反解offset。这里要提醒一句评分映射的对齐对象是公司已有的额度策略和利率策略不是随便定一个分就能上线这一步没对齐组合模型做得再漂亮也只是学术结果。4. 边界在哪样本量、分离问题与权重漂移组合模型救不了什么4.1 EPV低于10的时候组合模型是杯水车薪小贷数据最常见的死法是坏样本压根不够。EPVEvents Per Variable是坏样本数与模型变量数的比值统计建模里一般要求不低于10。这里的变量数要按进模型的自变量个数算不要因为做了WoE分箱就把变量当成哑变量去数自由度还是原始特征的数量但分箱搜索本身也在消耗数据所以实际项目中我会把标准线再往上抬一点20个特征至少需要200个坏样本。如果打开数据发现坏样本只有80个组合模型救不了你因为它只是两个模型的加权平均不产生任何新信息。这种场景的正确顺序是先用单变量IV排序把特征压到8个以内再考虑对Logistic加L2惩罚或换Firth回归最后才谈得上Logistic和Probit的组合。4.2 完全分离WoE出现inf时模型系数会发散现象是拟合日志不收敛跑出来的系数动不动上千标准误也大得离谱。原因是某个分箱里坏样本数为0或者某个字段跟坏标签完全线性可分。这时Logistic和Probit都会发散但表现略有差异Logistic更容易出现迭代轮数超限Probit可能会出现矩阵分解失败的报错。处理办法就三招按顺序来把0坏箱并入相邻箱删掉这个变量或者对模型加惩罚。statsmodels的Logit没有内置正则化想加L2可以直接换sklearn的LogisticRegressionProbit的正则化实现很少所以大多数项目都在前面分箱环节把这个坑填掉。这也是为什么我在第3章的分箱代码里宁可把问题暴露出来也不做一个永远不出错的漂亮壳子。4.3 固定权重上线是隐雷Brier权重每季度重估一次很多团队在开发阶段算出来的权重挺漂亮上线后半年不更新最后组合模型的表现比单模型还差回过头怀疑组合策略。权重是一个时点估计小贷客群随渠道和投放策略一变两个模型各自的Brier Score就会动。比如近期切入了一批更下沉的客户Logistic因为厚尾特性在低分段捕捉得更充分Brier可能明显改善固定权重就会把过高的权重压在Probit上。我的习惯是组合模型上线后每季度重算一次Brier权重同时监控两个指标整个组合评分的PSI不超过0.1低分段的拒绝率相对训练期偏移不超过5个百分点。如果Probit的权重从0.4跳到0.8说明数据里极端违约的比例在涨要先回看拒绝线而不是默默接受新权重继续跑。4.4 可解释性约束下组合模型别直接对外小贷公司审批和资方审计基本都要求评分可解释至少评审会上能回答“为什么这个变量是负向的”“为什么这个客户会降额”。Logistic评分卡可以拿一套分数映射讲明白组合模型在这件事上天然吃亏因为对外解释时要讲两套分布假设、两个连接函数、还有那个加权公式。我的经验是对外审批用Logistic主评分卡Probit和组合概率一律作为内部输入用于利率定价、人工复核、额度微调。除非组合概率在跨时间验证集上把AUC比Logistic单模型拉高0.01以上这个提升足够覆盖解释成本才值得对外切换。这个判断要写进评审材料不然模型上线后审计一问整个项目容易翻车。5. 避坑清单好坏定义、变量泄漏与阈值错配的五个现场5.1 坏定义拖到上线前夜才暴露现象项目跑了两三个月模型上线前夜业务确认“我们催收其实50天就转法务了”整个训练集的坏标签口径跟贷后处置流程对不上。原因是建模和业务各说各话风险部用90天催收部实际动作在50天附近。解决办法是项目第一天就拉滚动率把M0转M1、M1转M2、M2转M3的比例拉出来找滚转率接近100%的那一档做坏定义。小贷产品期限短很多时候M2已经是不可挽回节点拖到M3会损失模型效率。这个口径一旦定下来监控阶段也不要随便变否则前后两个版本完全不可比。5.2 变量泄漏让训练AUC高到吓人现象训练集AUC 0.93OOT直接掉到0.62。原因十有八九是特征表里混进了贷后变量催收通话次数、还款日修改次数、担保人确认时间、放款后额度调整次数。这些变量在申请时点根本不存在训练集里的“未来信息”帮模型作弊上线后实时评分拿不到AUC自然崩盘。解决方法是做一张字段注册表每个特征标注“可获取时点”凡晚于申请时点的一律删掉不管IV有多高。我见过最隐蔽的泄漏是“客户录入联系人数”这个字段在申请时点有但线上版本把联系人的接通状态一起塞进来了从字段名上看不出来必须看数据字典的更新时间。5.3 渠道口径漂移线下训练、线上预测现象训练数据来自线下门店进件实时评分却是APP线上进件分数整体偏高PSI超过0.2。原因是两个渠道的字段质量差异极大线下客户有客户经理帮忙填写线上客户自己填运营商在网时长这类字段缺失率前后能差30个百分点。WoE分箱时缺失值被当成一个区间线上样本缺失率骤降落点当然漂移。解决路径有两条样本量够就分渠道建模样本量不足至少要在评分前用渠道做分桶每个渠道单独做概率校准。最忌讳的是在所有渠道上硬套同一个概率切分因为渠道本身已经是一个强风险分层因子。5.4 共线性让系数符号反直觉现象收入变量和负债率变量同时进模型收入的系数居然是负的越有钱越容易逾期业务一看就拒绝接受。原因就是两个强相关变量在Logit和Probit里竞争同一段信息导致其中一个系数被挤出正常方向。处理方法是在分箱之后、拟合之前做一次性检查相关性矩阵里相关系数大于0.7的两个变量只保留IV较高的那一个连续变量标准化后再看模型输出。这个坑在组合模型里会被放大因为Logistic和Probit各自都可能出现一个符号不合理的系数组合概率的权重计算却不会察觉最后形成一个解释不了的决策分数。5.5 阈值硬切0.5业务量直接崩现象上线时按p0.5判坏结果通过率只有20%放款量撑不住产品部门立刻要求撤模型。原因是样本坏率可能只有5%0.5这个阈值对应的是后验赔率1:1放在5%坏率的客群里相当于极端保守。解决方法是按收益矩阵或分数分位数来定cutoff先算清楚一笔坏账损失多少钱、一笔好客户利息赚多少钱然后用期望成本最小化去找切点没有收益数据时就按通过率60%、50%、40%拉三档分数分别对应不同的额度策略。组合模型的Brier加权概率比单模型更接近真实概率用这个概率做收益矩阵的切分会比直接在Logistic概率上切稳一些但前提还是先别碰0.5这个默认数。6. 用预测分歧抓人工复核名单组合模型上线后的一个低成本技巧6.1 分歧名单怎么生成组合模型上线后大多数团队只盯着AUC和PSI忽略了一个已经握在手里的信号Logistic和Probit对同一批客户预测概率的差值。两个模型对中间客群的判断往往一致最大的分歧集中在那些特征组合正好落在两种分布假设边界的人。这群人比例不高但对审批策略最有价值因为他们的风险状态最不确定也正是人工复核应该介入的地方。# 用测试集上两个模型的概率差拉出分歧最大的 5% 客户 diff np.abs(pred_logit - pred_probit) test_copy test.copy() test_copy[p_diff] diff review test_copy.nlargest(int(len(test_copy) * 0.05), p_diff) print(全体坏率: %.4f, 分歧组坏率: %.4f % ( test_copy[bad].mean(), review[bad].mean()))5%是常用起点具体取多少取决于复核人力。如果分歧组的坏率明显高于全体坏率这些客户值得进人工复核名单而且这张名单比单纯拉低分名单更有信息量因为低分客户本来就会被拒掉而分歧客户往往落在模棱两可的分数段可能被放过也可能被误杀。如果跑下来分歧组坏率和全体基本一样说明两个模型的差异来自分布假设而不是业务信号这时候要回头检查权重和新客群的Brier变化而不是急着把名单交给审核员。我自己的习惯是每次参数重估后先跑一遍这个diff花十分钟就能看到模型往哪个方向飘再决定要不要动权重或调复核名单。希望帮到你。本文还有配套的精品资源点击获取