
2020年我以一个几乎零基础的身份参加了某金融机构办的金融科技训练营数据赛道现在回想起来整个过程就四个字可以概括小白打boss。拿到赛题的时候我盯着屏幕愣了很久训练集一百多万条样本、八十多个字段目标变量是信贷用户未来一段时间是否违约典型的二元分类问题。满脑子都是“我是谁、我在哪、这堆数据怎么处理”的空白。后来一步一步啃下来最终在主赛道拿到了一个成绩还不错的排名也顺利进了决赛答辩。这篇文章把我从赛题解读、特征工程、建模调参到最终答辩的完整过程和踩过的坑全部整理出来给同样打算硬着头皮挑战数据赛道的新手一个可复制的路线图也给自己留一份复盘记录。1. 摸清boss底细赛题解读与赛前准备1.1 先看懂赛题在问什么别急着动代码拿到赛题的第一步永远不是打开电脑敲代码而是把赛题说明和数据字典从头到尾读两遍。这次的赛题背景是一家消费金融公司的信贷风控场景要求参赛者根据用户授权的基本信息、历史借贷行为、还款记录等多维度数据预测该用户在未来六个月内是否会发生逾期违约。训练集大约120万条样本字段涵盖用户基础信息、授信额度、历史贷款笔数、每笔贷款的金额期限利率、还款行为统计、第三方征信衍生指标等80余个特征目标变量是二值的“是否违约”。我当时犯的第一个错误就是觉得字段多就代表复杂恨不得每个字段都拿来做可视化。实际上这80多个字段里存在大量的冗余和强相关性字段比如“历史借款总金额”和“历史平均借款金额”本质上表达的是同一类信息。数据字典里还标注了一部分字段的含义但那只是原始字段名真正有用的特征还需要自己从业务角度重新理解和加工。提示拿到数据后先做一张“字段类型分布表”把字段分为基础属性、行为统计、金额类、时间类、第三方衍生等几类标出缺失率。这样后面做特征工程时心里有数不会被字段数量吓到。我当时整理的字段分布大概是这样的字段类别字段数量典型字段示例特点用户基础属性12年龄、性别、学历、婚姻状况缺失率较低类别型为主授信与额度信息8授信额度、当前额度使用率数值型分布偏态明显历史借贷行为25贷款笔数、历史逾期次数、最大逾期天数存在大量0值需要区分真实0与缺失还款行为统计15平均还款金额、提前还款次数部分字段仅对已有贷款用户存在第三方征信衍生18征信查询次数、外部黑名单命中数缺失率较高含义相对抽象时间与行为序列6最近一次借款距今天数需要从时间维度构造相对特征从这个分布就能看出赛题考察的核心不是模型算法本身有多炫而是你能否从一堆杂乱无章的业务字段里提炼出真正影响违约风险的信息这其实就是风控场景最底层的能力。1.2 从评估指标反推整个建模策略训练营在赛题页面上明确写了官方评估指标是AUC这个信息极其重要因为它决定了你后面所有的建模策略。AUC的全称是ROC曲线下面积直观含义是随机抽一个正样本和一个负样本模型把正样本预测分值排到负样本前面的概率。这个指标最大的特点是只关心样本之间的相对排序不关心具体的预测概率是否校准对样本类别不平衡情况也不敏感。这次赛题里违约用户占比大约只有百分之二极端不平衡。如果用准确率来评估那模型什么都不学、全预测“不违约”准确率也有98%完全没意义。AUC则天然避免了这个问题它迫使模型去学习哪些特征能把极少数违约用户从海量正常用户里有效区分出来。从AUC这个指标我推导出三条建模策略建模的目标是“排序能力”不是“概率准确度”所以后期不需要花费太多精力去做概率校准阈值选择之类的工作。类别不平衡问题不需要通过过采样或欠采样强行平衡因为AUC对不平衡不敏感强行平衡反而可能引入噪声。交叉验证时一定要用分层采样保证每一折里正负样本比例与整体一致否则AUC的方差会很大。1.3 环境配置与baseline搭建工欲善其事必先利其器。这次比赛我是在一台8核CPU、32G内存的云服务器上完成的。配置并不算高但在当时处理120万条×80个字段的数据量已经是够用的水平了LightGBM在这种规模数据上的优势也体现得很明显。环境版本信息如下给后来的朋友一个参考Python 3.7、Pandas 0.25.3、NumPy 1.18.5、Scikit-learn 0.22.2、LightGBM 2.3.1、XGBoost 1.1.2。为什么我特别推崇LightGBM作为第一个上手的模型因为它用了基于直方图的决策树算法训练速度和内存占用都比传统GBDT有明显优势而且原生支持类别特征对新手极其友好。我的baseline搭建只用了三步。第一步写一个5折交叉验证的函数使用StratifiedKFold保证每一折正负样本比例一致每一折返回验证集AUC。第二步用最原始的80个字段不过做缺失值填充和简单的类型转换直接丢进LightGBM训练500棵树。第三步在验证集上计算AUC跑通整个流程。第一次跑通的时候5折平均AUC是0.724。看到这个数字我反而松了一口气因为这说明原始字段里已经包含了一定的区分能力模型能学到东西。接下来所有的工作都是在这个0.724的基准线上做加法。心得新手最容易犯的错就是一上来就想用复杂的深度学习模型。表格数据场景下树模型往往就是最优解先把一个完整的baseline跑通后面优化才有对照物。不要在没有基准线的情况下一通乱调。2. 特征工程看懂boss的攻击动作与弱点2.1 缺失值处理不是所有空值都要填特征工程阶段我理解为“看boss的攻击动作”目标是从每个原始字段里找出与违约风险相关的信号。第一步处理的是缺失值。很多人拿到数据直接对全表做fillna这是最粗糙的做法。正确的姿势是先看每列缺失率再结合业务含义决定处理方案。我的处理规则是缺失率超过70%的字段直接删掉。信息缺失太严重填了也是噪声。类别型字段用“-1”或“Unknown”填充作为一个独立类别因为对树模型来说是否会填缺失值本身可能就是一个有区分度的信息。数值型字段用中位数填充而不是均值。因为很多字段分布是严重右偏的均值会被极端值拉走中位数更稳健。还有一类字段缺失本身就代表业务含义。比如“最长逾期天数”这个字段对没有历史借贷记录的用户来说肯定是空的那么“缺失”意味着“没有历史逾期”反而是一个低风险信号。这种字段应该保留缺失标记并单独做一个“是否缺失”的指示特征。踩坑实录我最早一股脑把所有数值型字段的缺失值都用-1填充结果导致大量字段的分布变成“大部分正常值 一个小尖峰在-1”树模型确实会利用这个尖峰但特征的可解释性变得极差而且某些填充方式还会破坏字段本身的业务语义。适当混用“中位数填充”和“缺失指示特征”之后AUC提升到0.736。2.2 异常值处理与样本分布检查异常值处理这块我的经验是树模型对异常值相对稳健不需要花太多精力做极端的截断或替换但要做基本的合理性校验。比如年龄字段出现了500这种明显不合逻辑的值说明数据采集阶段存在脏数据直接删除或替换为缺失即可。更值得关注的是数值型字段的分布形状。我发现大部分金额类、天数类字段的分布都是严重右偏的长尾拖得很长。虽然LightGBM基于分裂点做切分对单调变换不敏感但右偏分布意味着在长尾区域很难找到有区分度的分裂点。所以我对一部分偏度大于2的字段做了log1p变换压缩长尾的影响。做完这一步加上前面的缺失处理AUC提升到了0.748。同时我强烈建议做一次“时间分布检查”。这次赛题没有直接给出“申请时间”字段而是有“最近一次借款距今天数”之类的相对时间字段。如果你发现训练集和测试集的这些时间字段分布差异很大就要警惕是否存在时间漂移问题。交叉验证时我的做法是按时间排序后的前几折作为训练、最后一折作为验证确保验证集是“未来的数据”这样评估出来的模型泛化能力更接近真实线上场景。2.3 关键特征衍生从原始字段里挖装备特征衍生是整个比赛中提升最大的一环我把这个过程比喻成制作打boss的专属装备。围绕信贷违约这个业务目标我从四个方向做了衍生第一个方向是比率类特征。信贷风控里单一绝对值往往没有相对值来得有信号。我构造了“当前授信额度使用比例”“平均还款金额与月收入的比例”“历史最大借款金额与授信额度比例”等特征。这类特征能刻画用户的杠杆水平和还款能力是非常经典的风险信号。第二个方向是时间跨度类特征。把与时间相关的字段转化为“距今多少天”“注册到首次借款的间隔天数”“最近两次借款的间隔天数”等。这类特征捕捉的是用户资金紧张程度和行为节奏比如短期频繁借款往往意味着流动性紧张。第三个方向是统计聚合类特征。对同一个用户的多笔历史贷款记录我做了一系列聚合操作平均借款金额、最大借款金额、借款金额标准差、逾期次数的占比、平均实际还款金额与应还款金额的差异等。这类特征是吃数据的细节能力把粒度的深化作为核心信息加强。第四个方向是波动类特征。比如用户在不同时期的额度使用率最大值与最小值之差还款金额的变异系数借款金额的时间趋势斜率。这类特征刻画的是行为的稳定性对风控模型非常有价值。用Python做这些衍生关键是要避免低效的循环写法尽量用向量化操作。一个典型的按用户分组的聚合特征做法是这样的import pandas as pd import numpy as np # 假设原始表是每笔贷款一行user_id是用户标识 loan_df pd.read_csv(loan_records.csv) # 按用户聚合借款金额的统计量 agg_dict { loan_amount: [mean, max, min, std, sum], loan_term: [mean, max], overdue_days: [max, mean, count] } user_agg loan_df.groupby(user_id).agg(agg_dict).reset_index() # 展开多级列名生成新特征名 user_agg.columns [_.join(col).strip(_) for col in user_agg.columns.values]到这里特征规模已经从原始的80个扩展到接近200个。靠前面的筛选逻辑保留有效部分避免噪声特征拖后腿。2.4 类别特征编码与特征筛选类别特征的处理我遵循的原则是“树模型能懂就行”。对于无序类别直接做标签编码给每个类别分配一个整数ID树模型在分裂时据此寻找最优切分点。对于有序类别如学历从小学到博士保留原始顺序映射成0到N-1的整数。对树模型来说做独热编码反而会制造稀疏特征、增加内存开销而且把类别间的隐含顺序关系抹掉了。这里分享一个树模型下很好用的小技巧频率编码。对类别特征计算每个类别在训练集中出现的次数然后用这个次数值代替类别ID。它的效果挺好原因在于出现频率高的类别往往对应着更大规模的样本量树模型在分裂时更容易从高频类别上获得统计稳定的增益。不过要注意有些业务场景中“出现频率低”本身就是风险信号所以频率编码和标签编码结合着用比只用一个更全面。特征筛选方面我用了两种手段相结合。第一种是看LightGBM的特征重要性把重要性为零或极低的特征直接剔除。第二种是算特征两两之间的相关系数对相关系数大于0.95的特征对手动检查一遍删除其中重要性较低的一个。这样处理后最终进入模型的特征保持在120个左右既保留了信息量又避免了高相关性带来的冗余。3. 建模调参正式输出伤害3.1 模型框架与交叉验证设置特征工程做完后模型就进入了一个稳定提升的阶段。我采用的交叉验证框架是StratifiedKFold 5折每一折的训练集大约96万条验证集大约24万条。为什么要用5折而不是3折或10折3折验证的方差太大一次调参的结论可能只是因为数据划分运气好10折的方差小但训练成本高在时间有限的比赛场景里不划算。5折是我在稳定性和效率之间找到的平衡。交叉验证的另一个关键作用是观察模型稳定性。如果5折AUC的标准差超过0.01说明模型对数据划分非常敏感可能存在问题。当时我检查过一次发现某一折AUC明显偏低排查后定位到该折中包含了一批特征极端值样本导致模型在验证时表现骤降处理方式是加入对极端值的裁剪逻辑而不是简单调参。3.2 单模型调参从粗调到细调LightGBM的参数太多新手容易一头扎进去乱调。我的策略是分阶段、以粗到细来推进。先固定学习率为0.1保持其他参数默认只调树的复杂度参数。重点关注max_depth和num_leaves这两个参数控制树的深度和叶子数量适合先粗调。我会查看模型在训练集和验证集上的AUC差值来判断是否过拟合如果差值持续扩大说明模型过于复杂了。确定了树的复杂度范围之后再做正则化参数的细调。min_child_samples控制叶子节点所需的最小样本数值越大模型越保守减少过拟合并提高稳定性。subsample和colsample_bytree控制行采样和列采样比例能有效降低方差。reg_alpha和reg_lambda是L1和L2正则系数对高维特征空间很有帮助。粗调阶段我用一个简易的网格搜索加手动判断下面是这部分的关键参数取值范围记录参数粗调范围最终取值作用说明learning_rate0.1固定0.02学习率越低单棵树贡献越小需要的树越多max_depth4, 6, 8, 10, 128限制树深度防止单棵树过深导致的过拟合num_leaves16, 31, 63, 12763控制树的复杂度越大模型越精细但越容易过拟合min_child_samples20, 50, 100, 20050叶子最小样本数值越大越保守subsample0.6, 0.8, 1.00.8训练时行采样比例colsample_bytree0.6, 0.8, 1.00.8训练时列采样比例reg_alpha0, 0.1, 1.00.1L1正则让部分特征权重为0reg_lambda0.1, 1.0, 5.01.0L2正则控制特征权重整体大小细调阶段的核心操作是把学习率降到0.02同时把n_estimators从500提高到3000用早停策略控制迭代轮数。早停轮数我设置为100轮也就是如果连续100轮验证集AUC不再提升就停止训练。这个做法节约了大量时间也帮我在同一套参数下快速确认了最优迭代次数。注意早停轮数不宜设得太大。我曾经把早停轮数设成200结果模型在验证集上出现了明显的峰值后回落现象早停机制迟迟没有触发白白浪费了大量时间。100轮是一个比较折中的选择。调参完成后单模型LightGBM在5折交叉验证上的平均AUC达到了0.796相比baseline的0.724已经提升了0.072。这个阶段要特别注意一个陷阱——调参过程中反复使用同一份验证集很容易把验证集信息泄漏到模型的参数选择里面导致验证集分数虚高。所以后期参数基本确定后我重新用全部数据训练了模型确保验证结果可信度没有严重偏离实际。3.3 多模型融合单挑变群殴单模型做到0.796之后我发现提升明显放缓陷入了连续几天都在0.797和0.798之间徘徊的瓶颈。后来想到的思路是既然单挑不过那就群殴。模型融合的逻辑是降低预测方差、综合不同模型的偏置但前提是模型之间的差异度足够大。我先后训练了XGBoost和逻辑回归。XGBoost作为另一种梯度提升树实现和LightGBM同属一类预测结果相关性较高按0.4的权重参与融合。真正给我带来惊喜的是逻辑回归它在同样的120个特征上效果只有0.736但和树模型的输出相关性只有0.88左右这意味着它能提供树模型看不到的线性视角的信息。树模型擅长捕捉非线性切分逻辑回归则对特征的线性加权组合更敏感两者融合让整体预测更加稳健。融合方式我试过两种。第一种是加权平均先对每个模型输出的预测概率做归一化再按权重加总。权重可以手动设置也可以通过一个小网格搜索确定。第二种是Stacking用训练集的预测结果OOF作为输入特征训练一个第二层模型这种方式理论上有更大提升空间但更容易过拟合需要格外小心。我最终用的是加权平均LightGBM占0.5、XGBoost占0.3、逻辑回归占0.2融合后的平均AUC达到了0.818突破了之前的瓶颈。心得很多新手把精力全部放在调参上却忽略了融合。实际上当单模型已经收敛时多模型融合带来的0.01到0.02的提升往往比继续调参更大。但有一句话要记住特征工程决定上限模型融合只是逼近上限的手段。如果特征不行融合也只是把几个烂预测拼在一起变得更烂而已。4. 实战心路纪录那些踩过的坑与通关建议4.1 时间管理与赛程节奏整个赛程大约持续六周我给自己设定的时间分配是第一周读题和搭基础框架第二到第四周做特征工程和建模调参第五周做模型融合和结果稳定性验证最后一周整理代码和准备答辩材料。但这个节奏在实际执行中是会发生偏移的我最大的教训是特征工程阶段严重超时原因是我一股脑地做了两百多个特征其中至少三分之一在后续重要性分析中被发现贡献极小。回头看更高效的做法是“先快速搭一个baseline再做小批量特征实验每次只验证一组特征增量式加入模型”。这样能及时知道哪些特征有效哪些无效而不是攒了一堆再一起验证出了问题都找不到源头。时间管理还有一个细节每天固定留出时间看讨论区和前排队伍的公开分享。这次训练营的氛围很好主办方会定期开放一些基线代码和思路指导同赛道选手也会在交流群里讨论对某些字段的理解。这些信息能给你提供完全不同的视角比如结构性的特征变换方法如果自己琢磨可能要很久。4.2 高频踩坑特征泄漏、内存爆炸与分布偏移多轮调试之后我总结了几类最典型的问题。特征泄漏是我认为最隐蔽的一类它指的是训练过程中用到了未来才会知道的信息线上预测获取不到对应的信息。我在早期的一个版本中犯了类似的错误做标准化和缺失值填充时使用了全量数据计算参数这其实让训练集在统计上包含了验证集和测试集的信息导致验证集AUC虚高。树模型对数值尺度不敏感所以标准化本身不必要但如果要做就一定只能在每一折训练集内部计算参数再应用到验证集上。内存和速度问题也很现实。120万条训练样本配合200个特征在Pandas里做复杂分组聚合时内存占用可以轻松逼近30G。我的解决方法是尽量优化字段类型把整数型字段转为int32、浮点型转为float32分类字符串转为category类型这样内存直接减少将近一半。另外避免在DataFrame上逐行apply自定义函数能用向量化写法就绝不写循环实测同样逻辑向量化写法速度可以提高几十倍。还有一个经典问题是线下验证AUC和线上提交分数的差距。我的最好版本线下验证分是0.827线上得分0.814差了0.013。这个差距主要来自样本分布漂移训练集和测试集因为不同的采集时间段或用户来源导致某些字段的分布不完全一致。应对方案是在验证集划分时模拟测试集的分布特征如果测试集的时间更靠后就应该按时间切分验证集而不是完全随机划分。下面是我整理的常见问题排查表症状可能原因解决方式验证分数极高但提交分极低特征泄漏或数据划分不合理检查是否在训练前使用了全量数据做任何变换按时间切分验证集5折AUC标准差大特征稳定性差或脏数据检查极端值、缺失字段做分位数截断训练长时间不结束参数过拟合或数据低效调小n_estimators使用早停压缩数据内存融合后分数不升反降模型间差异不足或权重不合理增加不同类型的模型用小网格搜索权重特征重要性普遍偏低特征噪声太大或目标分布有问题检查有无真实泄漏字段加强业务特征衍生方向4.3 答辩展示与小白进阶路线进入决赛答辩环节我和一位队友组队用大约三天时间整理了一份包含赛题理解、特征体系、模型方案、结果分析的展示材料。展示的时候要注意一件事评委看重的不是代码细节而是你对数据问题的理解深度和决策过程。比如我当时重点讲了一个故事通过构造“额度使用率的变化幅度”这个特征显著提升了对高杠杆用户群体的识别效果然后解释了为什么会想到这个特征、带来的量化提升是多少、在模型中的重要度排名如何。这样的叙述比贴一堆代码更有说服力。还有一个加分项是展示“失败实验”很多参赛者只讲成功路径忽略了那些被验证无效的尝试。主动坦诚地讲一两个踩坑经历比如我早期用了过多数值型字段的缺失值填充方式导致特征可解释性下降后来调整为缺失指示特征才解决问题反而能体现思维能力。训练营结束后很多新手朋友问我接下来该学什么。我的建议是如果只对表格数据感兴趣深度学习路线并不着急更有价值的是学业务知识比如知道风控场景中哪些字段是强信号、哪些业务逻辑会导致分布偏移。此外多尝试不同的数据竞赛体验不同领域的数据特点建立“看到字段就能猜出它适合构造什么特征”的直觉这个能力比会调参重要得多。最后再分享一个我后来一直用的小技巧每次跑完一版实验我都会把实验记录、参数、特征列表、验证分数、关键操作表格记录下来形成一份“每次技术决策的论证结构”文档。比赛走到第十天的时候回头查看这份文档你可以很清晰地看到每一步为什么有效、为什么无效这比在脑海中回忆清晰一万倍也减少了大量重复实验。希望这篇复盘能给同样从零开始的人一些笃定感boss再大一步一步磨总有通关的一天。