简介:面向机器学习竞赛选手与金融风控从业者,这份压缩包完整收录“马上AI全球挑战赛-违约用户风险预测”赛道亚军方案。方案基于Anaconda3中的Python 3.6环境,融合scikit-learn、pandas、numpy、xgboost等常用工具库,围绕信贷违约预测提供两套可执行思路:一是通过XGB1.py直接生成result_xgb.csv,快速见效;二是以feature_selection.py封装特征选择与XGBoost、Logistic Regression训练函数,配合feature_engine.py进行特征提取,最后由construct_module.py调度主流程输出sample.csv,覆盖从特征筛选、模型训练到结果落地的完整链路,便于对照学习与二次开发。全包仅7个文件、733KB,包含4个Python脚本、说明文档PDF、README与LICENSE,整体目录精简、可读性好,解压后即可按需查看,其中PDF对方案设计进行了展开说明,README则提供运行指引。目前已有297人学习浏览,适合希望复现亚军特征工程思路、比较单模型与集成模型效果,或快速搭建违约预测基线的读者。 做金融风控类数据竞赛,尤其是“违约用户风险预测”这种题,最大的感受就是:它不是纯拼模型,而是拼谁对业务的理解更深,谁对数据细节更敏感。AI Challenger这个系列赛事的规格和含金量都不低,能在其中拿到违约风险预测任务的前二,这套方案背后确实有不少值得掰开揉碎讲的东西。
这篇文章我尽量不写虚的,把当时解决问题的完整链路、关键决策点以及那些让排名产生质变的细节,一次性说清楚。
1. 赛题本质:这不是一个普通的二分类问题
拿到“违约用户风险预测”这个题目,第一反应可能都是“这不就是个二分类吗,LightGBM上就完事了”。但真正做进去会发现,远没那么简单。金融违约预测和一般的点击率预估、商品推荐分类有本质区别,它的核心难点在于数据的不平衡性和特征的稀疏性。
多数情况下,违约用户占总样本的比例非常低,可能只有个位数百分比,甚至更低。这种分布会带来两个直接问题:第一,模型很容易学成“全都预测为不违约”的懒惰模式,因为这样准确率也能到90%以上;第二,少数类样本的噪声会被放大,模型很难学到真正有区分度的模式。
当时我做的第一件事不是建模,而是花了两天时间仔细看了数据的分布形态。我强烈建议所有做这类题目的人,第一步永远是画分布图,包括违约标签随时间的分布、用户基本属性的分布、缺失值在各个特征上的分布。这一步能帮你规避掉很多后面会踩的坑,比如某个时间段的样本质量明显不同、某个特征缺失率过高导致后续要专门处理等等。
另外需要明确一点:这类赛题的评估指标往往不是准确率,而是AUC或者KS值。这意味着模型输出的概率值本身要有很好的排序能力,而不是非黑即白的判断。很多新手会在阈值选择上纠结半天,但在以AUC为指标的赛题里,阈值几乎不影响最终排名,真正影响排名的是模型对每个样本违约风险的精细刻画能力。
第二个容易被忽略的点是时间穿越问题。金融数据天生带有时间顺序,训练集和测试集往往是按时间切分的。如果特征构建过程中不小心用到了未来信息,比如用某个月的标签去构造前一个月的特征,那线上成绩会直接崩掉。这个问题在后面细说。
2. 数据清洗与底层特征构造:决定上限的脏活累活
2.1 缺失值处理不是填空,而是造特征
很多人在做缺失值处理时直接用均值、中位数去填,或者用简单插值。但在我这套方案里,“是否缺失”本身就是一个极强的一阶特征。原因很简单:在信贷场景中,某字段缺失往往代表着特定的业务含义。比如用户的工作单位信息缺失,可能意味着他是自由职业或无固定收入;某联系人信息为空,可能意味着社交关系薄弱。
具体操作上,我把每个字段的缺失指示单独拆出来作为一列,然后再用业务上合理的方式进行填充。比如收入字段,我按用户所在城市等级进行分组中位数填充,而不是全局填充。这样做的好处是保留了局部差异性,避免把所有用户拉到同一个基准线上。
数值型特征的处理也需要注意极值。信贷场景下的收入、负债、额度这类字段,往往存在长尾分布。如果不处理,树模型虽然对单调变换不敏感,但极值会影响分裂点的选择质量。我当时对这类字段做了分位数截断,把99%分位以上的值统一设为99%分位点的值,效果提升非常明显。
2.2 特征工程的核心:把静态数据展开成行为序列
金融场景的数据表往往包含好几类:用户静态属性表、借款行为流水表、还款记录表、催收记录表等等。原始的字段数量可能只有几十个,但是一旦我们把“用户在不同时间点的行为”展开来看,可挖的东西就多了。
我在这套方案里的特征工程框架,可以总结为四个字:聚合、衍生、交叉、时序。
聚合特征是最基本的路数:对借款历史做count、sum、mean、std、max、min,得到类似“历史借款次数”“平均借款金额”“最大单笔借款”“金额波动性”这类特征。这些特征刻画用户的负债习惯和资金需求特征。
衍生特征的核心在于构造比率。比率特征和原始数值特征最大的不同是,它天然去掉了量纲差异,更能反映用户的真实经济状况。举例来说,“月还款额/月收入”就是负债收入比,这是信贷审批里最核心的指标之一;“借款金额/授信额度”则反映了用户的资金饥渴程度,这个比例越高,风险往往越大。
交叉特征方面,我重点关注了“时间”和其他字段的交叉。比如借款金额随时间的变化趋势:把用户的历史借款按时间排序,计算后一半借款的平均金额相比前一半的变化率。这个特征能够捕捉用户是额度越借越大(可能陷入债务螺旋)还是越借越小(可能资金压力在缓解)。
时序特征这块,我把它拆成三层。第一层是相隔时间:比如最近一次借款距离现在多少天、平均借款间隔是多少。第二层是频率变化:近30天借款次数占历史总次数的比例、近90天借款金额占历史总金额的比例。第三层是周期规律:用户是否习惯在特定时间段借款,比如月底借款的用户,资金链往往更紧张。
最终我从原始的几十个字段构建出了400多个特征。这里有个经验供参考:特征数量并不是越多越好,但在这个量级下,树模型配合合理的特征筛选,基本能稳定带来AUC的提升。特征过多时可以用LightGBM的特征重要性进行初筛,去掉那些importance一直为0的特征。
3. 模型选型与融合:树模型是基石,但关键在于差异化
3.1 单模型怎么选:不同算法看到的“世界”不一样
很多top方案都会用 LightGBM、XGBoost、CatBoost 三个模型做融合。但真正让融合有效的前提是——这三个模型要有足够的差异性,否则融合只是求了个平均,收益非常有限。
LightGBM 我用得最多,主要看中它的训练速度和直方图算法在大样本下的稳定性。它的leaf-wise增长策略容易过拟合,因此我对num_leaves控制得比较谨慎,常用范围在 16~31 之间,配合min_data_in_leaf防止过细的分裂。
XGBoost 我保留了它做二阶导近似的特性。在某些非线性关系比较强的特征组合上,XGBoost 的拟合精度会比 LightGBM 略好一点,代价是训练速度慢很多。我通常会对 XGBoost 做更重的正则化,lambda和alpha都会调大。
CatBoost 的优势在于它对类别特征的原生支持和有序提升机制。考虑到赛事中确实存在一些类别字段,如行业类别、地区编码等,直接用 CatBoost 原生处理往往比自己手动做 target encoding 更稳,因为它的 Ordered Target Statistics 从机制上避免了目标泄漏。
三个模型的参数我都分别做了贝叶斯调参,而不是简单地套默认参数。这里有个建议:调参要分步走,先把树的结构参数(深度、叶子数)确定,再调采样比例和正则化,最后微调学习率。一次全调容易陷入局部最优,而且很难定位是哪个参数带来的提升。
3.2 训练策略的细节:验证集怎么切很关键
因为数据具备时间顺序,我在切分验证集时没有用随机的 KFold,而是用了TimeSeriesSplit,按时间顺序切分为多个训练集和验证集组合。
这样做的好处有两个:一是模拟线上真实的数据分布,线上测试集一定是训练集之后的时间段;二是可以观察模型在不同时间段上表现的稳定性——如果一个模型在最近的验证集上AUC明显下滑,说明模型学到了已经失效的模式,这个信号非常重要。
此外,我也专门留了一个最终验证集,它是训练数据中时间最靠后的10%,在调参和特征筛选阶段一直不使用,只在最后评估融合模型时拿出来看一次。这样可以避免在验证集上反复迭代导致的隐式过拟合。
3.3 融合与Stacking:权重不是拍脑袋定的
单模型都跑完之后,融合策略我用了两层。
第一层是加权概率平均。我根据三个模型在验证集上的AUC表现和它们之间的相关性,手动调了一个初始权重。初始值大致参考验证集AUC的比例,然后在这个附近做网格搜索,以验证集AUC最大化为目标找到最优权重。
第二层是简单的Stacking。用三个模型的预测概率作为新的特征,再加上几个我认为比较重要的原始特征,喂给一个逻辑回归做最终预测。逻辑回归在这里很合适,因为它简单、稳定,不容易在只有几个特征的情况下过拟合。而且它的可解释性好,可以通过系数看到每个模型在融合中所占的贡献。
这里有一个非常关键的细节:Stacking训练时需要做OOF(Out-Of-Fold)预测。如果用模型在训练集上的预测结果去训练第二层,几乎一定会过拟合。我用的是5折交叉验证产生OOF预测,确保第二层输入的是模型“没见过”的样本的预测结果。
最终融合模型在验证集上的AUC,比最强的单模型大概提升了0.003到0.005。这个幅度在AUC赛道上已经算非常可观的差异了,名次往往就在这个区间拉开。
4. 防泄漏与防过拟合:排名靠前和靠后的分水岭
4.1 时间穿越是最隐蔽的杀手
做金融风控类赛题,我最怕的问题不是模型效果不够好,而是模型“好得离谱”。如果你的验证集AUC达到了0.99甚至更高,不要高兴太早——大概率是特征泄漏了。
我在这套方案里专门做了一遍防泄漏审查,重点检查了三类位置:
第一类是直接使用未来信息。比如某个特征是“用户最终是否还清贷款”,如果这个结果本身就包含了考核期之后的信息,用它训练就是开卷考试。这类特征在原始数据里不一定显眼,可能藏在某个看起来无关的统计字段里。
第二类是target encoding操作不当。如果对类别特征做目标编码时用了全量数据的统计值,比如直接计算全量数据中某个类别的违约率,那这个特征就已经包含了目标信息。正确做法是在每一折内部,只利用训练部分的标签统计,再映射到验证部分。这个操作叫“Fold-wise Target Encoding”。
第三类是归一化或填充时跨越了时间边界。比如用全量数据的均值去做缺失值填充,这也算一种轻微的泄漏,虽然在树模型里影响不大,但在逻辑回归这类模型里会有影响。
4.2 样本权重:让模型更关注“难分”的样本
不平衡问题我正在用的另一个有效手段是样本权重。不是简单地把少数类样本复制几份,而是给不同样本分配不同的训练权重。
具体思路是:先训练一个基线模型,得到每个样本的预测概率。对于预测概率在0.3到0.7这个模糊区间的样本,在下一轮训练中把权重调高;而对于预测概率接近0.9以上的显著违约样本,反而不用给太高权重,因为模型已经“学会”它们了。
这种做法相当于让模型把重点关注在决策边界附近的样本上,和Focal Loss的思路有异曲同工之处。实测下来,这个技巧在AUC上的提升大约是0.001到0.002。幅度不大,但在竞争激烈的赛场上,每0.001都值得争取。
4.3 早停与模型快照:别让最后一步毁了全部
训练LightGBM时开启早停,这个大家都懂。但我想单独强调的是,早停的轮数设置要配合学习率一起看。
学习率设置得比较大(比如0.05以上)时,早停轮数不能太大,否则模型在验证集上已经开始回升后还会继续跑好几个轮次,浪费时间且可能保存到次优模型。学习率比较小(比如0.01以下)时,早停轮数要给足,因为小学习率下模型的提升非常缓慢,早停太早反而欠拟合。
我的做法是先用一个中等学习率(0.03)把最优迭代轮数摸出来,知道了大概范围之后,再降低学习率并相应扩大迭代轮数,做一次精细训练。这种做法能确保每个模型都在它能力范围内训练到最优状态,而不是靠运气停在某个局部好位置。
5. 排名提升最关键的三次实验迭代
这部分讲三个让我在排行榜上实现关键跳跃的实验迭代,每一次背后都是对业务的进一步理解。
第一次关键提升来自还款行为序列特征的加入。最初的版本只用了用户的基本属性和借款汇总统计,AUC大概在0.75左右。我把还款记录按时序展开,计算了诸如“历史逾期次数占比”“最长连续按时还款月数”“最近一次逾期距离今天的天数”等30多个特征后,AUC提升到了0.772。这一步最有用的特征排序前三名分别是:最长连续按时还款月数、历史逾期次数占比、最近一次提前还款距今天数。
第二次关键提升是负债收入比相关特征的精修。原本我直接用了现成的负债字段和收入字段相除,但发现噪声太大。后来我把字段做了一层层拆分:先把收入拆为稳定收入和额外收入,把负债拆为固定负债和临时负债,再分别构造“固定负债/稳定收入”和“总负债/总收入”,同时配合“收入波动率”和“负债波动率”做交叉。这一步让我从0.772提升到了0.783。
第三次提升来自多头借贷特征的构造。在信贷业务里,“多头借贷”指的是用户在多个平台同时借款的行为,这是风控关注的高风险信号。虽然原始数据里没有直接给出“借款平台数量”,但可以通过借款记录中的机构编号去统计。我构建的特征包括近30天/90天/180天的不同机构借款数量、同一天在不同机构申请借款的次数等。模型的效果从0.783跳到了0.791。这一步的关键在于把业务常识翻译成了可计算的特征,这比任何调参带来的收益都大。
最终线上AUC在0.79左右,这个成绩在当时的赛场上帮助我锁定了第二名的位置。
6. 竞赛之外:这套方案能直接用在生产环境吗
这是很多读者会关心的问题。坦白说,竞赛方案和工业级风控方案之间,还有不小的距离,但差异主要在工程和制度层面,方法论层面很多是可以复用的。
竞赛方案里哪些可以直接落地:特征工程框架,尤其是聚合+衍生+交叉+时序的四层结构,在任何信贷风险管理项目里都适用,无非是SQL和Spark的改写问题。多模型融合的思路也能直接迁移到生产的评分卡或机器学习风控模型中。OOF框架下的Stacking,在生产环境中可以用模型上线后的分时段预测替代。
竞赛方案里哪些不能直接落地:首先是特征穿越问题,生产环境里存在更严格的变量可用性约束,比如“建模时点的数据必须是在这个时点之前已经产生的”,这要求特征依赖的表必须做严格的时间快照。其次是模型可解释性,竞赛只要AUC,生产环境里银行、持牌金融机构的模型需要过审,监管要求模型决策逻辑必须能被解释。LightGBM这类黑盒模型的解释成本很高,很多机构仍然会选择逻辑回归或者带有单调约束的树模型。
所以我的建议是:如果是想通过这类竞赛入门金融风控,重点学特征工程的思路和数据集的洞察方式,这些是通用的能力。如果本身已经在风控行业,参加这类赛事可以帮你跳出业务惯性,用更灵活的视角重新审视原有的风控框架。
7. 一些琐碎但重要的工程经验
最后整理几条做这类赛事的工程细节,都是踩过坑之后换来的经验。
词的参数备份要按实验记录。我吃过最大的亏是跑了一组好结果,但忘了记参数组合,后面再也没复现出来。后来我用了一份简单的实验记录表,每一组实验记录下参数、特征列表、验证集AUC、训练时间。别小看这个习惯,它能让你在最后做融合时省下大把时间。
离散特征的基数要控制。如果某个类别特征的取值数量特别多,比如几万个(类似用户ID),直接用LabelEncoder喂给LightGBM很容易过拟合。一种做法是做一个统计映射:把出现次数少的类别归并为“其他”。另一种做法是用计数特征替换,直接用这个类别在历史数据中出现的次数作为特征。
对数据做多次抽样验证稳定性。同一组特征和参数,固定随机种子跑出的结果没有代表性。我当时用了不同的随机种子重复训练5次,观察AUC的均值和标准差。如果标准差偏大,说明这个方案不稳定,即便平均AUC高点也会在线上翻车。
不要迷信公开的baseline代码。每年赛后都会有人开源高分方案,但直接套用效果往往不好,因为不同赛题数据的分布差异太大。更有效的做法是读懂别人的特征思路,然后自己重新实现、改造。理解比复现重要得多。
写到这里,这套违约风险预测方案的整个框架就基本梳理完了。从数据理解到特征工程,从单模型训练到融合策略,从防泄漏检查到多次实验迭代,每个环节都很常规,但把这些常规动作做到位,组合起来的效果就足够支撑一个靠前的名次。希望这篇文章能给正在做信贷风控或者准备参加同类竞赛的朋友一些实在的参考。如果后续有时间,我再单独写一写特征工程中那些具体特征的构造细节和代码实现。
本文还有配套的精品资源,点击获取