
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的客戶全部通過”。清晰地展示你的優化策略相比基準策略在利潤、風險等指標上提升了多少百分比用數據說話。信用評分卡優化是一個完美的交叉領域課題它要求你既懂金融風控的業務邏輯又能熟練運用運籌優化的數學工具。通過線性規劃建模我們可以將復雜的業務決策問題轉化為清晰的計算問題從而找到在既定規則下的“最優解”。這個過程本身就是數據驅動決策的核心體現。希望這份從實戰中總結的指南能幫你下次面對類似問題時思路更清晰下手更有力。記住建模的關鍵不在于用了多復雜的算法而在于你是否準確地把業務問題“翻譯”成了數學語言。