1. 项目概述:一场关于信贷决策的“数据马拉松”
2020年的全国大学生数学建模竞赛C题,题目是“中小微企业的信贷决策”。这个题目一出来,当时我们团队就意识到,这绝对是一场硬仗。它不像一些纯算法优化的题目,给你一堆数据让你去拟合、去预测就完事了。这道题的核心,是把我们这些“象牙塔”里的学生,直接推到了银行信贷经理的位置上,要求我们基于真实、复杂且充满不确定性的企业数据,去构建一套完整的信贷风险评估与决策模型。说白了,就是让我们用数学模型,去回答一个现实世界里银行每天都在面对的核心问题:这笔钱,该不该贷?该贷多少?该收多少利息?
这道题的价值,远远超出了竞赛本身。它精准地切入了当时(乃至现在)金融科技领域最核心的议题之一——如何利用数据驱动的方法,解决中小微企业“融资难、融资贵”的痛点。对于参赛者而言,这不仅是一次数学和编程能力的考验,更是一次对金融逻辑、商业理解、数据处理和系统化思维的综合锤炼。你需要懂一点财务分析,知道哪些指标能反映企业的健康状况;你需要懂一点统计和机器学习,能从海量数据中挖掘出风险信号;你还需要懂一点优化理论,能把风险、收益、政策约束变成一个可求解的数学问题。
整个解题过程,就像完成一个微缩版的金融科技产品开发项目:从业务理解、数据清洗、特征工程,到模型构建、策略制定、方案评估,最后形成一份逻辑严谨、可落地的分析报告。接下来,我就结合我们当时的实战经验,把这道题的解题思路、核心方法、踩过的坑以及一些独家的建模心得,完整地拆解一遍。
2. 赛题核心需求与业务逻辑拆解
拿到题目后,切忌一头扎进数据里。第一步,也是最重要的一步,是彻底读懂题目在问什么,背后的业务逻辑是什么。2020年C题提供了123家有信贷记录的企业和302家无信贷记录企业的数据,包括进销项发票信息、信誉评级、是否违约等。题目要求我们解决四个核心问题:
2.1 问题一:信贷风险量化与评级
题目要求根据123家企业的发票数据、信誉评级和是否违约记录,建立信贷风险模型,并对这123家企业进行风险评估。这里的关键在于“量化”。信誉评级(A, B, C, D)是一个离散的、主观的标签,而“是否违约”是一个二分类结果。我们需要做的,是构建一个模型,能够输出一个连续的、更精细的风险分数,或者对原有评级进行验证和细化。
注意:这里最容易犯的错误是直接拿“是否违约”作为标签去训练一个分类模型(比如逻辑回归、随机森林),然后直接用模型预测的概率作为风险分数。这忽略了题目中“信誉评级”这个重要先验信息。更合理的思路是,将“信誉评级”作为模型的一个重要特征,或者将其与违约记录结合,构造一个更稳健的监督信号。
2.2 问题二与问题三:未知企业的信贷策略制定
这是题目的核心和难点。对于302家无信贷记录的企业,我们需要制定具体的信贷策略,包括:
- 是否放贷:一个二分类决策。
- 信贷额度:一个回归或优化问题,贷多少钱。
- 贷款利率:一个定价问题,风险和收益如何权衡。
题目还给出了一个非常关键的约束:银行的年度信贷总额固定(比如1亿元)。这意味着我们的决策不是孤立的,而是一个资源分配优化问题。你不能给所有低风险企业都批很高的额度,因为钱就那么多。必须在所有申请企业中,找到一个最优的分配方案,使得在总额度限制下,银行的整体预期收益最大(或风险损失最小)。
2.3 问题四:突发情况(疫情)下的策略调整
题目假设突发情况(如新冠疫情)对所有企业产生影响,但影响程度不同。要求我们分析突发情况对模型和策略的影响,并给出调整方案。这考察的是模型的稳健性和可解释性。你不能说“模型失效了,重做吧”,而必须能说清楚:哪些特征可能变得不可靠?风险传导的路径是什么?如何基于有限的假设进行快速调整?
2.4 业务逻辑闭环
综合来看,整个解题过程需要构建一个逻辑闭环:数据(发票流) -> 特征(经营指标) -> 模型(风险分数) -> 策略(额度/利率) -> 优化(总额度约束) -> 评估与调整(突发情况)。 每一步都需要有坚实的数学或统计方法作为支撑,并且每一步的选择都要有明确的业务解释。
3. 数据预处理与特征工程实战
题目提供的核心数据是企业的进项和销项发票信息。这是典型的交易流水数据,原始数据非常“碎”,直接建模几乎不可能。特征工程是这里成败的关键,工作量可能占整个项目的60%以上。
3.1 发票数据的关键信息提取
一张发票通常包含:时间、金额、对方企业名称、商品名称等。我们需要从这些流水记录中,提炼出能反映企业“经营健康度”和“稳定性”的指标。
核心特征构建思路:
经营规模与活跃度特征:
- 交易总额/均值:进项总额、销项总额。直接反映业务量。
- 交易频次:月度/季度发票张数。反映业务活跃度和稳定性。
- 交易对手数量:进项/销项中不同的合作企业数量。反映客户/供应商集中度,集中度过高可能有风险。
盈利能力与现金流特征(这是重点):
- 毛利率估算:这是一个关键衍生特征。虽然发票没有直接成本,但我们可以用
(销项总额 - 进项总额) / 销项总额来近似估算毛利率。当然,这非常粗糙,因为进项和销项在时间上不匹配,且包含税费。但作为相对比较指标,在大量企业中依然有效。 - 进销项匹配度:计算每个时间窗口(如月度)的“销项/进项”比率。长期远大于1可能虚开发票,长期远小于1可能经营萎缩。
- 现金流波动性:计算月度净现金流(销项-进项)的标准差、变异系数。波动越大,经营风险可能越高。
- 毛利率估算:这是一个关键衍生特征。虽然发票没有直接成本,但我们可以用
稳定性与趋势特征:
- 季节性/趋势分解:对企业的月度交易额进行时间序列分解,查看其是否存在稳定的增长趋势、季节性波动,或是无规律的随机波动。稳定的增长趋势是加分项。
- 断流检测:企业是否存在长时间(如超过2个月)无任何交易记录的情况,这可能是经营中断的危险信号。
行业与上下游特征(如果数据允许):
- 通过对商品名称进行简单的文本分类或关键词匹配,可以大致划分企业所属行业(如“电子元件”、“纺织服装”)。
- 分析其上下游企业(交易对手)本身的风险特征(如果对手也在样本池中)。例如,一家企业的核心供应商风险很高,那么它的风险也会被传导。
3.2 数据处理中的“坑”与技巧
- 时间窗口对齐:发票日期是精确到日的,但我们需要聚合到月或季度。务必注意窗口的起止时间,确保每个企业都有可比的时间段。对于新成立或数据缺失的企业,要谨慎处理。
- 异常值处理:发票数据中可能存在错漏(如金额极大或极小的发票)。我们当时采用的方法是,对于单张发票金额,计算其与企业历史交易额均值的差距,超过3倍标准差的需要人工复核(在比赛中就是根据业务逻辑判断是否剔除)。不要盲目删除,一笔巨大的进项可能是购买了设备,一笔巨大的销项可能是接到了大订单。
- 缺失值处理:对于302家无标签企业,特征也可能缺失。我们采用了基于同类企业(行业、规模相近)的均值填充,并在特征中增加了一个“是否填充”的布尔标记,让模型知道这个信息是估算的。
- 特征标准化:由于后续模型涉及距离计算或梯度下降(如神经网络),必须对连续特征进行标准化(Z-score)或归一化(Min-Max)。树模型(如随机森林、XGBoost)对尺度不敏感,但标准化有助于我们理解特征重要性。
实操心得:特征工程不是一蹴而就的。我们采用的是“构建-评估-筛选”的迭代方式。先基于业务理解构建一大批特征(我们当时构建了超过50个),然后使用问题一中有标签的数据,通过计算特征与目标(风险评级/违约)的相关性,以及用简单的模型(如逻辑回归)查看系数显著性,进行初步筛选。最终保留15-20个解释性强、相关性高的特征进入主模型。
4. 核心模型构建:从风险预测到策略优化
这是整个解题的“发动机”。我们将其拆解为两个核心模型:风险预测模型和信贷策略优化模型。
4.1 风险预测模型选型与实践
目标:输入企业的特征向量,输出一个连续的风险评分Risk_Score(例如0-100分,分数越高风险越大)。
可选模型对比:
| 模型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 逻辑回归 | 可解释性极强,系数代表特征影响,稳定。 | 线性假设,难以捕捉复杂非线性关系。 | 基线模型,用于特征初筛和理解。 |
| 随机森林 | 能处理非线性,抗过拟合能力强,可输出特征重要性。 | 黑盒模型,可解释性差,可能忽略特征间细微的线性关系。 | 作为主力预测模型,效果通常不错。 |
| XGBoost | 预测精度高,能自动处理缺失值,有正则化防过拟合。 | 比随机森林更黑盒,调参稍复杂。 | 追求预测精度时的首选。 |
| 神经网络 | 理论上拟合能力最强,能学习复杂模式。 | 需要大量数据,训练不稳定,可解释性最差,易过拟合。 | 数据量极大时考虑,本题不推荐。 |
我们的方案:融合模型我们最终没有只用一个模型。而是采用了一个两阶段融合的方案:
- 第一阶段 - 有监督预测:使用XGBoost模型,以123家企业的“是否违约”为主要标签,同时将“信誉评级”作为有序变量(A=1, B=2, C=3, D=4)融入损失函数进行训练,得到一个初始风险概率
P_default。 - 第二阶段 - 无监督校准:考虑到违约样本较少,直接用有监督模型预测302家无标签企业可能不稳定。我们引入了无监督的聚类分析(如K-Means或高斯混合模型GMM),基于特征空间将所有企业(包括123家和302家)进行聚类。然后观察每个簇中有标签企业的风险分布。如果一个簇里已知的高风险企业多,那么这个簇的整体风险权重就调高。最终风险分由
P_default和聚类调整因子加权得到。
为什么这么做?这相当于结合了“经验”(有标签数据)和“相似度”(无监督聚类)。即使一个新企业没有任何历史信贷记录,只要它的经营模式(特征)和历史上那些“坏企业”很像,它的风险分也会被拉高。这增强了模型的泛化能力和稳健性。
4.2 信贷策略优化模型构建
有了风险分数,接下来就是制定策略。这本质上是一个带约束的优化问题。
决策变量:对于每个企业i,我们需要决定:
x_i:是否放贷(0或1)a_i:信贷额度(连续变量)r_i:贷款利率(连续变量,通常在一个基准利率上浮动)
目标函数:银行希望最大化总预期收益。
Maximize: Σ [ x_i * a_i * (r_i - Cost_of_Capital) * (1 - P_default_i) - x_i * a_i * P_default_i ]解释:收益部分 = 额度 * 利差 * 不违约的概率;损失部分 = 额度 * 违约的概率。P_default_i就是我们从风险模型得到的企业i的违约概率。
约束条件:
- 总额度约束:
Σ (x_i * a_i) <= Total_Credit_Limit(题目给出的总额度,如1亿)。 - 额度上下限:
Min_Amount <= a_i <= Max_Amount, 对于不同规模企业,额度应有合理范围。 - 利率风险定价:
r_i = Base_Rate + Risk_Premium_i。风险溢价Risk_Premium_i应与P_default_i正相关,例如Risk_Premium_i = k * P_default_i。这保证了高风险高收益。 - 逻辑约束:如果
x_i = 0(不放贷),则a_i = 0。
求解方法: 这是一个混合整数非线性规划问题,直接求解较难。我们做了合理简化:
- 将是否放贷
x_i和额度a_i的决策分开。先根据风险分数和预期收益,用一个排序规则初筛出“值得放贷”的企业池(x_i=1)。 - 在放贷池中,将利率
r_i表示为风险分数s_i的线性函数,代入目标函数。 - 此时,问题简化为在总额度约束下,对放贷池中的企业分配额度
a_i,以最大化一个关于a_i的线性或二次目标函数。这可以用经典的线性规划或二次规划求解器(如Python的PuLP,CVXOPT)高效求解。
策略输出:求解后,我们得到一张清晰的信贷策略表:
| 企业ID | 是否放贷 | 建议额度(万元) | 建议利率 | 风险等级 |
|---|---|---|---|---|
| E001 | 是 | 100 | 5.8% | 低 |
| E002 | 否 | 0 | - | 高 |
| ... | ... | ... | ... | ... |
5. 突发情况影响分析与模型调整
题目第四问模拟新冠疫情这类系统性冲击。我们的分析框架如下:
5.1 影响识别
系统性冲击不会同等地影响所有企业和所有特征。
- 对特征的影响:
- 直接冲击特征:如“最近一季度交易额”、“交易频次”可能断崖式下跌。这类特征的当期数据会瞬间“失真”。
- 间接冲击特征:如“毛利率”、“现金流波动性”,其计算依赖于多期数据,影响会滞后并持续一段时间。
- 相对稳定特征:如企业长期的“交易对手集中度”、“历史平均规模”,可能变化不大。
- 对模型的影响:
- 特征分布漂移:冲击后,数据的统计分布(均值、方差)与模型训练时所用数据的分布发生显著变化,导致模型预测失效。
- 风险关联性变化:原本不重要的特征(如“是否依赖线下”)可能突然成为强风险因子。
5.2 调整策略
我们不能等有新违约数据了再重新训练模型,必须基于假设快速调整。
特征重加权:这是最快速有效的方法。在原有模型中,手动调高那些对冲击敏感的特征的权重。例如,在风险评分公式中,增加“近期交易活跃度下降比例”这个新特征的权重,同时降低“历史年均交易额”的权重。
Adjusted_Score = α * Original_Score + β * Impact_Indicator- 其中
Impact_Indicator可以是行业受冲击系数(假设旅游、餐饮行业系数高)与近期经营下滑系数的乘积。
引入先验规则:在模型决策层之上,加入规则引擎。例如:“如果企业所属行业为受冲击严重行业,且近期交易额下降超过50%,则无论原模型评分如何,将其风险等级临时上调一级,并大幅降低授信额度。”
压力测试模拟:利用历史数据(如果有类似冲击时期)或蒙特卡洛模拟,模拟冲击下企业现金流断裂的概率,并将其作为额外的风险因子加入评估。
策略的弹性调整:信贷策略优化模型中的约束条件需要动态调整。例如,总额度不变,但可以临时增设“对特定行业的集中度限制”,或提高所有企业的利率风险溢价基础值
k,以覆盖更高的系统性风险。
注意事项:在论文中阐述这部分时,重点不在于你调整得多么精确(因为无法验证),而在于你的分析逻辑的完整性和合理性。你需要清晰地展示:a) 识别了哪些可能受影响的环节;b) 提出了哪些具体的、可操作的调整方法;c) 解释了这些方法如何缓解冲击带来的问题。
6. 建模全流程中的常见陷阱与应对技巧
回顾整个解题过程,有几个地方特别容易出错,这里集中分享一下:
6.1 数据理解与清洗陷阱
陷阱:忽视发票数据中的“负数”金额。在实际中,负数可能代表红字发票(退货、折让)。如果直接求和,会低估交易额。我们一开始就漏掉了,导致一些企业的特征计算错误。
应对:在数据清洗的第一步,就单独统计正负金额,分析负数发票的比例和原因。在计算交易总额时,使用绝对值求和或净额计算,需根据业务含义决定,并在论文中说明。
陷阱:简单按企业ID分组计算特征,忽略了集团企业的关联交易。如果两家企业是母子关系或关联公司,它们之间的交易可能虚增经营规模。
应对:这是一个高级技巧。如果时间允许,可以对交易对手名称进行模糊匹配或网络分析,识别出频繁交易且名称相似的企业群,将它们视为一个整体进行风险评估。
6.2 特征工程与模型过拟合
- 陷阱:构建了大量复杂、高维的特征(如各种交互项、多项式特征),直接在123条小样本数据上训练复杂模型(如深度网络),导致严重的过拟合。模型在训练集上表现完美,但泛化能力为零。
- 应对:
- 特征筛选是必须的:使用方差阈值、相关性分析、基于模型的特征重要性(如XGBoost的
feature_importances_)进行筛选。 - 坚决使用交叉验证:在123条数据上,采用留一法或5折交叉验证来评估模型性能,而不是看训练集准确率。
- 优先选择简单模型:在数据量小时,逻辑回归加精心构建的特征,其效果和可解释性往往优于一个过拟合的黑盒模型。
- 特征筛选是必须的:使用方差阈值、相关性分析、基于模型的特征重要性(如XGBoost的
6.3 优化模型求解的可行性
- 陷阱:设计的优化模型过于复杂(如非线性、非凸),自己无法求解,或者求解时间过长。
- 应对:
- 一定要做简化:像我们前面做的那样,将是否放贷和额度分配分步进行。先用一个清晰的规则(如风险分数低于阈值且预期收益为正)筛选出候选集,大大减少决策变量。
- 验证解的质量:对于求出的解,要检查其是否满足所有约束条件。可以设计一个简单的贪婪算法作为基线(例如,按“单位风险收益比”从高到低分配额度),对比优化解的效果,证明优化是有效的。
- 利用成熟求解器:Python的
PuLP(对接多种开源/商业求解器)或SciPy.optimize对于线性/二次规划问题非常可靠,代码也简洁。
6.4 论文写作与结果展示
- 陷阱:论文通篇是模型公式和代码,没有业务解释;或者结果只有一堆数字表格,没有可视化。
- 应对:
- 每一部分都要有“为什么”:为什么选这个特征?为什么用这个模型?为什么这样设定约束?这是评委最看重的逻辑链。
- 可视化是关键:企业风险得分的分布直方图、特征重要性条形图、额度分配前后的对比饼图、聚类结果散点图……一图胜千言。使用
Matplotlib或Seaborn制作清晰、专业的图表。 - 结果要可解释:对于最终的信货策略表,可以附上一些典型企业的案例分析。例如:“企业E123,因其毛利率持续下滑且交易波动大,被模型评为高风险,故不予授信。”这能让你的模型结果落地。
最后想说的是,数模竞赛和真实的数据科学项目一样,没有唯一的标准答案。我们的这套方案——融合模型、两阶段优化、系统性风险分析——只是众多可行路径中的一种。它的优势在于逻辑层次清晰,兼顾了监督与非监督学习,并且将业务约束很好地融入了数学建模。在实际比赛中,更重要的是你思考的深度、解决问题的完整度,以及将复杂问题清晰呈现的能力。这道题就像一个微缩的金融科技沙盘,走完一遍,你对如何用数据驱动商业决策,会有完全不一样的理解。