ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

线性规划与分支定界算法在金融风控信用评分卡优化中的应用

线性规划与分支定界算法在金融风控信用评分卡优化中的应用 1. 项目概述当数学建模遇上金融风控去年带学生打MathorCupA题“信用评分卡优化”一出来我们团队就意识到这绝不是一个简单的套模型题。它本质上是一个典型的、在强约束下的资源分配与决策优化问题。题目要求我们基于给定的客户数据构建并优化信用评分卡核心目标是最大化银行的利润或最小化风险损失同时必须满足一系列现实业务规则比如通过率、坏账率、风险敞口等限制。这听起来像是机器学习问题但当你真正开始构建目标函数和约束条件时你会发现它的最优解往往藏在线性规划Linear Programming, LP及其扩展形式如混合整数线性规划MILP的求解器里。为什么是线性规划因为评分卡的“优化”动作无论是调整评分阈值、决定授信额度还是组合不同的风控策略其决策变量如是否给某个分数段的客户放款与最终的目标利润和约束通过率之间在题目设定的框架下通常可以表达为线性关系。你的目标函数总利润是各客户群利润的线性加权和你的约束总通过人数、总坏账金额也是线性不等式。这就把一个复杂的金融风控问题转化成了一个标准的、可求解的数学优化问题。网络上热词里提到的“分支定界/分支切割算法求解MILP”正是我们当时求解核心模型的关键。因为有些决策变量必须是整数比如选择哪几张评分卡或者某个策略是否启用这就构成了混合整数线性规划。直接求解MILP是NP-Hard问题而分支定界Branch-and-Bound框架是求解它的主流精确算法。简单来说它通过不断“分支”将问题分解为更小的子问题并“定界”快速排除那些不可能包含最优解的分支从而在可接受的时间内找到全局最优解或近似最优解。所以这个项目的核心路径非常清晰将信用评分卡的业务优化问题严谨地建模成一个线性规划或混合整数线性规划模型然后利用高效的优化求解器如Gurobi, CPLEX或开源的OR-Tools、SCIP进行计算最终得到一整套可执行的、最优的信贷决策方案。下面我就把这个从业务理解到模型求解的完整链条拆开结合我们踩过的坑和实战心得给大家捋清楚。2. 问题拆解从风控业务到数学模型拿到题目和数据千万别急着套代码。第一步也是最关键的一步是把模糊的业务需求翻译成精确的数学语言。这决定了你的模型是否合理求解是否可行。2.1 核心决策变量定义决策变量是你的“操作手柄”。在信用评分卡优化中通常有两种定义方式对应不同的优化粒度方式一基于客户分箱的决策这是最直观的方式。信用评分卡通常会将客户根据分数划分为若干个风险等级例如A、B、C、D、F五个等级。那么你的决策变量可以是x_A, x_B, ..., x_F连续变量表示对每个分数段客户的通过率0到1之间。例如x_A 0.95表示A级客户95%通过审批。或者y_A, y_B, ..., y_F整数变量0或1表示是否对某个分数段采取“通过”策略。这常用于策略组合优化。方式二基于评分卡阈值的决策更精细的优化是直接调整评分卡的审批阈值Cut-off Score。例如原阈值是600分高于600的通过。优化后可能变为610分。这时决策变量可以是阈值S本身。但这样目标函数和约束关于S通常是非线性的因为客户分布是离散的需要做一些线性化处理或将其转化为第一种方式。在我们的解题中采用了第一种方式因为它能更自然地与线性规划结合。我们将客户按初始评分分成了20个组bin决策变量x_i表示第i组客户的放款比例。2.2 目标函数构建利润最大化银行的最终目的是盈利。因此目标函数通常是期望利润最大化。对于每一组客户i我们需要计算单客期望收入贷款利息收入。假设贷款额度为L_i利率为r则收入为L_i * r。单客期望损失即坏账损失。这需要用到题目提供的违约概率PD和违约损失率LGD。期望损失 L_i * PD_i * LGD_i。单客期望利润利润_i L_i * r - L_i * PD_i * LGD_i - 运营成本。其中运营成本可能是一个固定值或比例。那么对于该组所有客户总期望利润就是总利润 Σ (客户数_i * x_i * 利润_i)。 这个Σ (客户数_i * x_i * 利润_i)就是一个关于决策变量x_i的线性函数完美符合线性规划的要求。注意这里的PD_i和LGD_i需要从题目数据中估计。通常同一分数段内的客户具有相似的PD。我们可以用该分数段历史违约客户的占比来估算PDLGD有时题目会直接给出或假设一个固定值如45%。这是建模的第一个关键假设需要明确说明。2.3 约束条件梳理业务的紧箍咒光追求利润不行银行经营有严格的风险控制和合规要求。这些就构成了模型的约束条件同样是线性的。总体通过率约束银行可能希望控制整体业务规模或通过率在一个范围内。总体通过客户数 Σ (客户数_i * x_i)约束可能为总体通过率下限 (总体通过客户数 / 总客户数) 总体通过率上限坏账率约束控制整体风险水平。总坏账金额 Σ (客户数_i * x_i * L_i * PD_i * LGD_i)总放贷金额 Σ (客户数_i * x_i * L_i)约束为(总坏账金额 / 总放贷金额) 坏账率上限。注意这是一个比值约束但可以通过变形转化为线性约束总坏账金额 坏账率上限 * 总放贷金额。高风险客户限制对评分最低的几组客户如F级可能严格限制其通过率甚至禁止通过。x_F 0.05或x_F 0预算或资本约束总放贷金额不能超过可用资金总额B。Σ (客户数_i * x_i * L_i) B决策变量自身约束0 x_i 1对于某些需要整数决策的则y_i ∈ {0, 1}。将这些目标函数和约束条件用数学公式写出来一个完整的信用评分卡优化线性规划模型就诞生了。它看起来就像这样Maximize: Σ_i (N_i * x_i * P_i) Subject to: Σ_i (N_i * x_i) / N_total P_min Σ_i (N_i * x_i * L_i * PD_i * LGD_i) LR_max * Σ_i (N_i * x_i * L_i) Σ_i (N_i * x_i * L_i) B 0 x_i 1, for all i (可能还有 x_j C_j 对于特定高风险组j)其中N_i是i组客户数P_i是单客利润。3. 模型求解算法选择与实现细节模型建好了怎么解对于纯线性规划LP有成熟的单纯形法Simplex和内点法Interior-Point可以高效求解全局最优解。但我们的模型往往包含整数变量比如必须选择3张评分卡中的2张这就变成了MILP需要更专门的算法。3.1 求解器你的计算引擎不要试图自己从头实现单纯形法或分支定界算法那是科研人员的工作。我们应该站在巨人的肩膀上使用成熟的优化求解器商业求解器性能最强Gurobi学术界和工业界公认的标杆对学术免费速度和稳定性极佳。MathorCup这类比赛通常允许使用。CPLEXIBM的老牌产品同样非常强大。它们都提供了Python、Java、C等接口集成非常方便。开源求解器免费可选OR-Tools (Google)谷歌推出的优化工具套件内置了多个求解器对MILP支持良好文档丰富是比赛中的热门选择。SCIP目前最强大的非商业开源混合整数规划求解器之一。PuLP (Python)一个建模库可以调用多种后端求解器包括CBC、GLPK等。我们的选择是Python PuLP Gurobi。PuLP提供了非常直观的建模语法而Gurobi作为后端求解器保证了求解效率。对于无法获得Gurobi许可的情况PuLP默认的CBC求解器也能应付中小规模问题。3.2 求解过程与代码框架下面是一个高度简化的、基于PuLP的核心代码框架展示了如何将上述数学模型“翻译”成代码import pulp import pandas as pd # 1. 读取和处理数据 data pd.read_csv(customer_data.csv) # 假设数据已按评分分组并计算好所需字段 # data 中包含列bin, customer_count, avg_loan, pd, lgd, interest_rate, operation_cost # 2. 创建问题实例 # 最大化问题 prob pulp.LpProblem(Credit_Scorecard_Optimization, pulp.LpMaximize) # 3. 定义决策变量 # 为每个分箱创建一个连续变量代表放款比例范围在[0, 1] x pulp.LpVariable.dicts(x, data[bin].tolist(), lowBound0, upBound1) # 4. 构建目标函数 # 计算每个分箱的单客期望利润 data[profit_per_customer] data[avg_loan] * data[interest_rate] - \ data[avg_loan] * data[pd] * data[lgd] - \ data[operation_cost] # 总利润 sum(客户数 * 放款比例 * 单客利润) prob pulp.lpSum([data.loc[i, customer_count] * x[data.loc[i, bin]] * data.loc[i, profit_per_customer] for i in data.index]) # 5. 添加约束条件 # 5.1 总体通过率约束 (例如不低于30%) total_customers data[customer_count].sum() prob pulp.lpSum([data.loc[i, customer_count] * x[data.loc[i, bin]] for i in data.index]) 0.3 * total_customers # 5.2 坏账率约束 (例如不超过5%) # 总坏账金额 total_bad_debt pulp.lpSum([data.loc[i, customer_count] * x[data.loc[i, bin]] * \ data.loc[i, avg_loan] * data.loc[i, pd] * data.loc[i, lgd] for i in data.index]) # 总放贷金额 total_loan pulp.lpSum([data.loc[i, customer_count] * x[data.loc[i, bin]] * data.loc[i, avg_loan] for i in data.index]) prob total_bad_debt 0.05 * total_loan # 5.3 高风险组约束 (例如最低分箱通过率不超过1%) lowest_bin data[bin].min() # 假设bin是数值或可排序 prob x[lowest_bin] 0.01 # 6. 求解问题 # 使用CBC求解器开源如果安装了Gurobi可以替换为 pulp.GUROBI() prob.solve(pulp.PULP_CBC_CMD(msgFalse)) # 7. 输出结果 print(f求解状态: {pulp.LpStatus[prob.status]}) print(f最大化总利润: {pulp.value(prob.objective):.2f}) for b in data[bin]: if x[b].varValue 1e-5: # 忽略接近零的值 print(f分箱 {b}: 建议放款比例 {x[b].varValue:.3f})这段代码清晰地展示了从建模到求解的流水线。关键在于第4步和第5步如何正确地将业务逻辑转化为线性表达式。3.3 关于“分支定界”算法当我们引入整数变量时例如y_i ∈ {0,1}表示是否对第i组客户采用高利率策略PuLP和Gurobi在调用prob.solve()时会自动启用分支定界算法来求解这个MILP问题。你不需要手动实现它但理解其原理对调试和解释结果很有帮助松弛首先忽略整数约束求解对应的线性规划松弛问题LP Relaxation。这会得到一个目标值上界对于最大化问题。分支如果松弛解中某个整数变量y_i的值是小数比如0.7就创建两个新的子问题一个强制y_i 0另一个强制y_i 1。这就像一棵树的分叉。定界与剪枝求解每个子问题的松弛解。如果某个子问题的解比当前已知的整数解还差或者它的松弛解都无法超过当前最优整数解那么这个分支就可以被“剪掉”不再探索因为它不可能产生更好的整数解。迭代不断分支、求解、定界、剪枝直到找到满足整数要求的、且被证明是最优或接近最优的解。在求解日志中你可能会看到“Gap”在逐渐缩小这个Gap就是当前最优整数解与全局上界之间的差距是衡量求解进度和精度的关键指标。4. 方案优化与策略分析得到一组最优的x_i放款比例只是第一步。更重要的是如何解读这个结果并形成可执行的业务策略。4.1 结果解读与策略生成求解器输出的x_i可能是一些小数如x_A0.95, x_B0.8, x_C0.3, x_D0.01, x_F0.0。这直接翻译成业务策略A级客户优质客户给予95%的通过率几乎全部放行。B级客户次优客户通过率80%可以适当收紧。C级客户中等风险客户通过率30%需要严格筛选。D级客户高风险客户仅1%通过近乎拒绝。F级客户极高风险客户完全拒绝。基于此可以制定差异化审批策略自动通过评分高于A阈值的系统自动通过。人工复审评分在B和C区间的转入人工审批流程结合其他信息综合判断。自动拒绝评分低于D阈值的系统自动拒绝。4.2 敏感性分析与“What-If”场景线性规划的一个巨大优势是便于进行敏感性分析Sensitivity Analysis。这能回答业务非常关心的问题“如果某个条件变化了我的最优策略和最大利润会怎样变”约束右端值变化的影响例如如果监管要求的坏账率上限从5%收紧到4%我的最大利润会下降多少这个信息在求解报告中称为“影子价格Shadow Price”或“对偶价格Dual Price”。它量化了放松或收紧一单位约束所带来的边际利润变化。这对于资源分配和谈判极具价值。目标函数系数变化的影响如果某个客户群的利率r或违约率PD预估发生了变化当前的最优解是否依然最优求解器提供的“目标系数允许增减范围”可以告诉你答案。在比赛中进行深入的敏感性分析并据此提出动态策略调整建议是论文的重要加分项。例如你可以指出“在当前市场环境下坏账率约束是限制利润的主要瓶颈其影子价格为X。这意味着如果银行能通过提升催收能力将坏账损失降低1个百分点理论上可增加Y百万元利润。”4.3 模型扩展多评分卡与策略组合原题可能更复杂比如提供多张基础评分卡如“收益优先型”、“风险规避型”要求你从中选择几张进行组合并对不同客户群体应用不同的评分卡。这就引入了0-1整数变量。定义变量z_k ∈ {0,1}是否选用第k张评分卡。定义变量y_{i,k} ∈ {0,1}是否对第i组客户使用第k张评分卡。需要添加约束Σ_k z_k K最多选K张卡且y_{i,k} z_k只有被选中的卡才能被使用。目标函数变为Max Σ_i Σ_k (客户数_i * y_{i,k} * 利润_{i,k})。这个模型明显更复杂变量更多求解时间更长。但建模思路一脉相承只是约束条件更丰富了。这时求解器的性能差异就会体现出来。5. 实战心得与避坑指南最后分享一些在实战中总结出来的、教科书上不会写的经验。5.1 数据预处理是地基模型再漂亮数据不准全白搭。缺失值处理对于关键的PD、LGD字段如果缺失不能简单删除或填0。需要根据业务逻辑用同组均值、中位数或通过简单模型如基于其他特征的回归进行插补并说明处理方法。异常值处理对于明显不符合逻辑的极端值如贷款额度为负要查明原因是数据错误还是特殊业务如冲正交易。通常需要与业务方确认或采用盖帽法Capping进行处理。变量转换线性规划要求线性关系。如果原始变量与目标之间可能存在非线性可以考虑分段线性化或引入辅助变量。但在信用评分中经过分箱处理后组内用线性近似通常是可接受的。5.2 模型假设必须清晰所有模型都是对现实的简化必须明确你的假设。独立性假设我们假设不同客户之间的违约是独立的。现实中可能存在系统性风险导致违约相关但题目数据通常不包含这类信息。参数稳定性假设我们使用历史数据估计的PD、LGD来预测未来假设这些参数在未来一段时间内是稳定的。明确写下这些假设并在论文的“模型评价与推广”部分讨论这些假设不成立时的影响这体现了建模的严谨性。5.3 求解效率与规模平衡问题规模客户分箱数决策变量数不宜过多也不宜过少。过多如超过1000个可能导致求解变慢过少如少于10个则策略过于粗糙失去优化意义。通常20-50个分箱是一个合理的范围。整数变量带来的计算挑战MILP的求解时间随整数变量数量指数级增长。如果模型中有大量0-1变量求解可能非常耗时。可以尝试先求解LP松弛问题观察哪些变量在松弛解中已经是0或1将其固定。设置合理的求解时间限制Time Limit和最优间隙MIP Gap。比如设置“在1小时内找到Gap小于1%的解即可”这在商业应用中很常见。使用启发式方法如贪心算法先找到一个较好的可行解作为求解器的初始解Warm Start可以大幅加速求解过程。5.4 结果验证与业务合理性检查求解器给出的“数学最优解”未必是“业务可行解”。检查极端值是否出现了某个分箱通过率为99.9%而相邻分箱为0%这种跳跃过大的情况这可能在数学上最优但业务上难以解释风险是连续的。可以考虑添加平滑性约束如|x_i - x_{i1}| δ。进行压力测试用另一份验证集Out-of-Sample数据按照优化后的策略模拟运行计算实际的关键指标利润、坏账率看是否与模型预测相符。这是检验模型泛化能力的金标准。与基准策略对比一定要设置一个基准策略例如“所有分数高于600的客户全部通过”。清晰地展示你的优化策略相比基准策略在利润、风险等指标上提升了多少百分比用数据说话。信用评分卡优化是一个完美的交叉领域课题它要求你既懂金融风控的业务逻辑又能熟练运用运筹优化的数学工具。通过线性规划建模我们可以将复杂的业务决策问题转化为清晰的计算问题从而找到在既定规则下的“最优解”。这个过程本身就是数据驱动决策的核心体现。希望这份从实战中总结的指南能帮你下次面对类似问题时思路更清晰下手更有力。记住建模的关键不在于用了多复杂的算法而在于你是否准确地把业务问题“翻译”成了数学语言。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表