news 2026/10/16 1:10:29

金融数据竞赛小白实战复盘:从逻辑回归到LightGBM的提分之路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
金融数据竞赛小白实战复盘:从逻辑回归到LightGBM的提分之路

2020年我干过最犹豫也最值的一件事,就是顶着“小白”的头衔报了某平台金融科技训练营的数据赛道。当时满屏都是大神复盘帖,什么GBDT、深度特征、自动调参,看得我血压直接飙到一百八。但回头想想,数据这行光靠啃课本永远学不会,必须有一场比赛把你扔到真实问题面前逼你动手。如果你也属于那种学过Python基础、看过几篇机器学习教程、但还没正经打完一场数据比赛的人,这篇复盘应该对你有用。我尽量把从报名、读题、建模到踩坑的完整过程讲透,争取让你少走点弯路。

1. 赛前准备:数据赛道到底比什么

1.1 小白也能报名的底气来源

先说一个反直觉的事实:数据赛道并不是“大神的专属竞技场”。训练营的数据赛道通常分两派,一类是偏算法效果的“分数竞赛”,另一类是偏业务落地的“方案竞赛”。2020年那届的数据赛道属于前者,核心就是给你一批已经脱敏的结构化数据,让你预测某个金融场景下的目标标签,线上评测、实时排名。好处是门槛极低,只要能跑通一次提交就能看到自己的分数轨迹。

我当时的底气主要来自三样东西:第一,懂一点pandas,会做简单的数据读取、筛选、分组统计;第二,看过几篇树模型的科普,知道XGBoost和LightGBM大概是干什么的;第三,也是最重要的一点,我能接受自己前几次提交分数垫底。比赛不是考试,排名是动态的,今天垫底不代表两周后垫底,这种反馈机制对小白的心理压力反而比考试小得多。

提示:如果你连pandas的groupby都没用过,建议先花一周时间跟一遍入门教程再参赛。比赛会倒逼你学东西,但完全零基础去参赛,挫败感会淹没收获感。

1.2 读懂boss的设定:赛题、数据与评估指标

报名之后的第一步不是写代码,而是当一回“阅读理解选手”。2020 fintech训练营数据赛道的赛题背景设定是:某金融机构提供了一批模拟的消费金融业务数据,目标是预测借款人是否会发生逾期,也就是经典的二分类问题。训练集大概有三万多条样本,测试集一万条左右,特征有几十列,做了脱敏和编码处理,字段名看起来像是某类系统生成的代号。

这条信息量很大,我拆给你看。消费金融场景意味着样本大概率不平衡,逾期用户通常是少数,如果直接拿准确率当指标,你全预测“不逾期”也能拿99分,但这个模型毫无意义。所以赛方选用了AUC作为评估指标。AUC不关心预测分数的绝对大小,只看正样本和负样本排序的准确性,能抵抗样本不平衡带来的误导。这直接决定了后面所有特征工程和模型调参的导向,不是把预测值压低,而是把排序能力拉高。

关于赛制,初赛提供固定训练集和测试集,要求提交测试集预测结果文件;复赛会更换一批新数据,测试模型泛化能力。也就是说,你在初赛里做的一切都可能是“过拟合表演”,真正考验的是你这套方案换个批次还能不能打。

1.3 环境与工具箱:不需要显卡也能打

数据赛道的另一个友好之处在于,绝大部分赛题用单机CPU就能解决。我当时用的是一台普通笔记本,16GB内存,没有独立显卡,照样跑完了整场比赛。

具体工具链如下:

  • Python 3.7以上环境,Anaconda直接装好;
  • pandas、numpy、scikit-learn用于数据清洗和模型评估;
  • LightGBM作为主力模型,XGBoost用来做融合对比;
  • matplotlib、seaborn画图辅助理解分布;
  • Jupyter Notebook做探索性分析,正式训练时改成.py脚本跑。

这套组合到今天依然很能打。原因在于,表格数据的比赛里,树模型长期是王者,深度学习只有在极大规模数据和特殊结构(如序列、文本、图像)下才有优势。金融风控类场景的特征大多是数值型加类别型,树模型对特征尺度不敏感,也不需要做标准化,迭代速度快,对新手极其友好。

注意:比赛前先把环境装好,别在赛题公布当天开始配环境。你不是在给自己找事,你是在给对手送分。

2. 核心技巧:数据特征才是真正的胜负手

2.1 动手建模前的数据解剖

我见过不少新人拿了数据就套模型,结果分数上不去,还以为是自己调参不行。其实绝大多数情况下,问题出在你根本不了解自己手里的数据。我给自己定过一个规矩:建模前必须完成四件事,否则不动模型。

第一,看形状和类型。df.shape、df.dtypes这两行代码能告诉你有多少行、多少列、哪些是数值哪些是类别。这一步能快速发现“特征全是数值型”和“特征里有大量类别型”这两种极端情况,处理策略完全不同。第二,看缺失率。缺失率超过60%的列基本可以直接扔掉,但要不要扔不是绝对的,需要结合业务含义判断。第三,看目标分布。训练集里正样本占比到底是多少,决定了后面要不要做采样处理。第四,看特征的取值个数。取值个数特别少的特征可能是类别,取值分布畸形的特征可能有异常值。

我当时在数据探索阶段用了一个很笨但很有效的方法:把每一列单独打印出来,统计它的缺失率、唯一值数量、均值、分位数。虽然会花掉大半天时间,但效果是后面三天几乎没有走过弯路。

2.2 缺失值、类别特征和时间特征的处置逻辑

关于缺失值,我必须先泼一盆冷水:不要一股脑全用中位数填充。缺失本身就是一种信息,缺失可能意味着“这个客户没有这项数据”而“没有”本身可能和违约相关。我当时的处理策略是:把缺失率低的列用中位数或众数填充;缺失率高的列保留一个“是否缺失”的0/1标记,再做填充。这个操作在本次比赛中效果明显,单独加缺失标记的AUC涨了差不多0.003,不要小看这一丁点,在高手云集的榜单上,千分之三就能拉开几十个名次。

类别特征的处理要慎重。最简单的做法是LabelEncoder,把类别转成0、1、2、3这样的整数。但这样做有个隐患:树模型会把这些整数当成有序数值,强行给类别排序,如果类别本身没有顺序关系,就可能误导分裂。更好的选择是使用LightGBM原生支持的类别特征,直接传特征名列,它内部会做最优划分。如果模型不支持原生类别特征,可以用One-Hot编码,但当类别取值特别多时维度会爆炸,这时候就要考虑目标编码了。

时间特征在风控场景里经常被忽略。如果数据里有申请时间、逾期时间这类字段,不要只把它当字符串丢掉。我通常从时间字段里提取年、月、日、星期几、季度等,再计算“这条记录距离数据集基准日期的天数差”。在2020年的赛题里,“时间天数差”这一列特征的重要性排进了前五,说明它能有效捕捉业务的时间效应。

2.3 高阶特征工程:目标编码与交叉特征

目标编码(Target Encoding)是我在整个比赛里学到的最值钱的一招。它的思路很简单:用类别特征对应的目标变量均值来代替原始类别值。比如“职业”这一列中,“程序员”这个类别的违约率是0.12,那就可以把这个类别的所有样本的这个特征填成0.12。

但这里有一个大坑:直接在全量训练集上做目标编码会引入标签泄漏,因为你在用当前样本的目标值去预测当前样本,模型会“记住”训练集而崩溃。正确做法是使用K折交叉验证式的目标编码,把训练集分成K份,每一份用其余K-1份计算目标均值,测试集用全部训练集计算。这样编码过程是“干净”的。

交叉特征则是把两个或多个特征组合成新特征。比如“年龄”和“收入”组合成交叉特征,能比单独两个特征捕捉到更细的模式。不过交叉特征不是越多越好,每造一个特征都会增加内存消耗和过拟合风险,我的经验是只对特征重要性靠前的类别特征做交叉,其他一律不做。

2.4 特征泄漏:小白最容易踩的暗坑

特征泄漏这个词,新手容易理解成“数据里写了答案”,但它比那更隐蔽。常见的泄漏有两种。第一种是时间泄漏,比如用包含未来信息的统计量构造特征,典型的操作是:计算某个客户在测试期内的“平均还款金额”填到训练集特征里,这等于把未来的结果泄露给了模型,线上一测试就露馅。第二种是分布泄漏,比如用了全量数据的统计值做标准化或目标编码,导致训练集和测试集的分布信息混在一起。

我自查泄漏的方法很简单:每次构造完新特征,就在心中问一句“如果这条样本是测试集,我能不能在只有训练集的情况下算出它的这个特征值”。如果答案是“需要用到全量数据或未来数据”,那就一定会泄漏。比赛中我见到有人因为一个统计特征线上分数暴跌到不如随机,就是踩了这类坑。

提示:特征工程做完之后,务必重新跑模型做特征重要性排序。重要性靠前的特征逐个人工审查,尤其是原始数据里不存在的衍生特征,排查泄漏最有效。

3. 实操全流程:从baseline到一路推到决赛

3.1 用逻辑回归快速建立baseline

在没跑任何模型之前,先定一个“及格线”非常关键。这个及格线用来衡量你后续所有工作到底有没有进步,没有它你会在迷茫中反复调参。

第一步,数据清洗。去掉全缺失的列,填充低缺失列,把类别特征做简单编码。

第二步,建立一个简单的逻辑回归模型。代码极短,大概长这样:

from sklearn.linear_model import LogisticRegression from sklearn.model_selection import StratifiedKFold from sklearn.metrics import roc_auc_score # X_train, y_train 为清洗后的训练数据 skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) scores = [] for train_idx, valid_idx in skf.split(X_train, y_train): X_t, X_v = X_train.iloc[train_idx], X_train.iloc[valid_idx] y_t, y_v = y_train.iloc[train_idx], y_train.iloc[valid_idx] model = LogisticRegression(max_iter=1000) model.fit(X_t, y_t) y_pred = model.predict_proba(X_v)[:, 1] scores.append(roc_auc_score(y_v, y_pred)) print('AUC:', sum(scores) / len(scores))

这里的StratifiedKFold是分层折交叉验证,能保证每一折的正负样本比例和整体一致,在样本不平衡时尤其重要。我当时跑出来baseline的AUC是0.712,不高,但它确立了一个坐标系:之后我做的任何一个特征、任何一次调参,都拿它当对照物。

第三步,用交叉验证分数指导决策。baseline最大的作用不是拿出去提交,而是让你知道“我现在这套原始流程值多少分”。如果后续特征工程折腾了两天分数还在0.71附近徘徊,说明要么特征构造方向错了,要么模型能力不够,该换模型了。

3.2 切换LightGBM:速度与精度的双赢

逻辑回归的baseline打完之后,我直接切到了LightGBM。为什么不是XGBoost?两个原因。第一,LightGBM用基于直方图的算法,训练速度比XGBoost快好几倍,在同样数据量下能让我更快迭代试错;第二,LightGBM原生支持类别特征,省去了大量编码工作。但如果你是第一次用,建议先把XGBoost也跑一遍,两个模型的融合通常能比单个模型再提升一截。

核心训练代码如下:

import lightgbm as lgb params = { 'objective': 'binary', 'metric': 'auc', 'learning_rate': 0.03, 'num_leaves': 31, 'feature_fraction': 0.8, 'bagging_fraction': 0.8, 'bagging_freq': 1, 'verbose': -1, } lgb_train = lgb.Dataset(X_train, y_train) lgb_valid = lgb.Dataset(X_valid, y_valid) model = lgb.train( params, lgb_train, num_boost_round=5000, valid_sets=[lgb_valid], callbacks=[lgb.early_stopping(200), lgb.log_evaluation(100)] )

这里的early_stopping是防止过拟合的关键武器。它会在验证集的AUC连续200轮不再上升时自动停止训练,省去手动挑选迭代次数的麻烦。learning_rate设成0.03是折中方案,太高容易过拟合,太低训练时间成倍增加。

换成LightGBM之后,五折AUC从0.712涨到了0.765,这是一个跨越式的提升。也侧面说明baseline的瓶颈主要不是特征而是模型本身。

3.3 调参路线与模型融合的正确姿势

调参是很多新手最容易迷失的环节,我见过有人拿网格搜索把所有参数排列组合跑了一整夜,结果还没默认参数好。我的调参思路是“分批来、先粗后细”,不要一上来全参数一起调。

第一批调节的是树结构参数:num_leaves和min_data_in_leaf。num_leaves控制树的复杂度,值越大模型越容易过拟合;min_data_in_leaf控制叶子节点最少样本数,值越大模型越保守。我的参考范围是num_leaves从15到127之间试,配合min_data_in_leaf从20到200之间试,优先看交叉验证分数的变化趋势。

第二批调节的是采样参数:feature_fraction和bagging_fraction。这两个参数分别控制每次迭代时用的特征比例和数据比例,默认1.0代表全部使用,调成0.8左右可以增强泛化能力。需要注意的是,开启bagging时必须指定bagging_freq为1,否则采样不会生效,这个细节很多人会漏。

模型融合是冲分的关键一步。最朴素也最实用的方式是加权平均:把LightGBM、XGBoost、逻辑回归三个模型的预测概率按0.5、0.3、0.2加权求和。为什么权重这样分配?因为LightGBM的单模型分数最高,应该给更大权重。这个融合操作让我线上AUC又涨了0.004左右。更高级的Stacking需要额外训练一个元模型,新手前期可以先不碰,容易在交叉验证的实现上出bug。

3.4 提交波形分析:从0.72到0.79的踩坑记录

我把自己的提交记录拉出来看,基本就是一部“小白翻车史”。第一次提交,baseline直接上线,分数0.7153,排在榜单中后段。第二次提交,做了缺失标记和简单目标编码,分数0.7247,涨了接近0.01,当时挺开心。第三次提交,做了一个自以为很厉害的组合统计特征,分数反而掉到0.7198,这是第一次遇到“负优化”,原因就是那个特征强烈依赖测试集分布,产生了分布泄漏。

第四次提交开始切换LightGBM并加上五折交叉验证,分数跳到0.7638。之后几天用特征重要性筛选去掉无用特征,把噪声降下来,分数稳定到0.7725。倒数第三轮,我开始做模型融合,又涨了0.004。最后两天专门检查特征泄漏,砍掉两个可疑特征,线上分数不降反升,最终定在0.79附近。这段经历让我明白一个道理:涨分靠特征,暴涨靠换模型,阴沟翻船全靠泄漏。

4. 问题排查:翻车实录与避坑手册

4.1 高频报错与解法速查表

比赛期间我遇到的报错五花八门,我把典型的几类整理成了一张速查表,供你直接抄作业。

现象原因解决方案
训练时内存暴涨然后崩溃特征过多或数据类型为高精度浮点用pd.to_numeric降精度,删除重要性低的特征
LightGBM报错“categorical feature”类别特征编码格式不对将类别列转为pandas的category类型
五折验证分数高但线上分数低特征泄漏或训练与测试分布不一致检查衍生特征是否使用了全量数据计算
提交文件格式错误索引未重置或样本顺序被改动提交前确认submit文件行数与测试集一致
predict_proba返回了单列分类器被自动识别为二分类检查目标列是否是0/1整数
模型训练速度极慢learning_rate太低且early_stopping轮次太大先调大learning_rate到0.1跑通,再降回来

最容易忽视的是第一种和第五种。内存爆炸往往发生在特征工程后期,当你生成了几百列特征之后,16GB内存就捉襟见肘了。解决办法是对特征做dtype压缩:pandas默认会用int64和float64存储,其实很多特征用float32就够了,内存立省一半。

4.2 一个让我彻夜未眠的泄漏案例

比赛中期我构造了一个看起来很聪明的特征:计算每个用户在历史记录中的“申请次数”。想法是申请次数越多的人可能越缺钱,逾期风险越高。训练集上这个特征的重要性排名第一,五折AUC直接涨到0.79,我当时兴奋得差点跳起来。但提交之后线上分数只有0.73,比之前还低。那天晚上我反复看这个特征的构造代码,最后发现:测试集里有些用户是“只有一次申请”的新客,而训练集里同一个用户的申请次数分布差别巨大,导致这个特征在不同数据集上含义完全不同,本质上是把训练集分布特征泄漏出去了。

排查泄漏的最佳方式是做“时间切片验证”:把训练数据按时间切成前80%和后20%,用前80%训练、后20%验证,如果验证分数远低于随机五折分数,说明特征里可能混入了未来信息。这也是为什么金融风控比赛里时间特征如此重要,它不只是信息,更是校验泄漏的标尺。

4.3 样本不平衡的三条实用经验

消费金融场景的逾期率通常很低,本次赛题正样本占比在5%左右,属于典型的类别不平衡。我试过三类方案,说下效果和原因。

第一,不做任何处理,直接训练。树模型在AUC指标下往往能扛住不平衡,因为AUC只看排序。我实测结果五折AUC是0.765,已经不错。

第二,负样本下采样,让正负比例变成1比1后再训练。AUC反而降到了0.750,原因很简单:丢弃了大量负样本等于丢弃了信息量。

第三,SMOTE过采样,用插值生成新的正样本。AUC稍有提升到0.772,但训练时间变长,而且生成的样本可能偏离真实分布。

结论是:在AUC指标下,树模型加上适当调参就能处理好不平衡,通常不需要大动干戈做采样。但如果赛题指标换成F1或KS,不平衡的处理就变成必需项,那时候可以去了解下带权损失函数,核心逻辑就是给少数类更高的惩罚权重,强制模型更关注它们。

5. 赛道收尾:复盘与后面的路

5.1 比赛结束后我保留了哪些习惯

训练营结束不代表学习结束,我觉得这场比赛留给我的最大财富不是最终排名,而是一套可以复用的做事方法。第一,我养成了“先定baseline再优化”的习惯,任何新项目拿到手,先跑通一条最简单的路径,再逐步迭代,绝不空想方案。第二,我把“五折交叉验证”变成了肌肉记忆,每做一个特征都要过一遍交叉验证,分数不涨就回滚,不靠感觉做决策。第三,我开始认真写实验日志,每天记录做了什么操作、涨了多少分、为什么涨,两天后回头看这份日志,能避免大量重复劳动。

我还建议你比赛之后做一个动作:把代码从头整理一遍,删掉调试用的残留代码,做成一个能直接复跑的项目结构。这份代码未来就是你的“比赛模板”,下次参赛可以直接复用数据清洗、交叉验证、特征工程、模型训练、融合、提交这六件套。

提示:比赛结束后复盘时,一定要记录“负优化”的尝试。知道什么不work,往往比知道什么work更值钱,因为你能省下大量重复试错时间。

5.2 下一步可以往哪走

如果你打完了2020届这类入门级数据赛道,下一步有两个方向值得考虑。第一个方向是继续刷比赛,从入门赛道过渡到更高难度的公开赛,逐步接触CV、NLP、时序预测等不同数据类型,拓宽技术面。第二个方向是选择一个行业深耕,比如金融风控、电商推荐、营销响应这些场景,把特征工程和业务理解结合起来深挖。在我个人体验里,数据竞赛的指标只是表面功夫,真正让一个数据从业者和别人拉开差距的,是你对“数据为什么长这样”的理解深度。

另有一个小技巧,打完比赛之后把排名靠前的大神分享帖找出来对比学习。不要只看他们最终用了什么模型,重点分析他们的特征工程思路和验证方案,那才是拉开差距的核心。

这场比赛对我来说是一次很典型的小白成长样本:从只会跑通逻辑回归,到最终玩转LightGBM、目标编码、模型融合,前后不到三周时间。数据赛道最大的魅力就在这里,它用一个具体的问题逼着你把所有知识点串起来,告诉你机器学习不是看会的东西,是练会的东西。希望这篇复盘能给你一点参考,也期待你在自己的第一次比赛里,完成从“小白”到“能打”的转变。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 1:09:52

Qoder Skill功能实战:从配置到进阶,打造AI编程上下文管理利器

1. 为什么我会花一整周去啃Qoder的Skill功能先说结论:Qoder的Skill功能是我最近半年里遇到的、少数让我觉得"这工具设计者是真的自己写代码"的功能模块。这东西表面上是一个"给AI预处理指令"的配置面板,但实际用下来,它解…

作者头像 李华
网站建设 2026/10/16 1:09:52

HarmonyOS 6.0企业安全办公App实战:窗口增强与数据防泄露指南

企业安全办公App这东西,真不是做个登录页面、存个密码就算“安全”了。我见过太多团队,移动端跑得挺好,一上PC就翻车:没有多窗口思维,窗口一最大化就把整个屏幕霸占;没有防截屏意识,核心数据被人…

作者头像 李华
网站建设 2026/10/16 1:09:47

Android自绘滚轮实现省市区三级联动城市选择器

简介:面向Android应用开发者的城市选择器组件,适合在电商、旅游、生活服务等场景中快速接入城市选择功能。该组件界面模仿美团,并借助高德地图API完成定位与城市数据展示,兼具美观度和实用性。压缩包内共285个文件,大小…

作者头像 李华
网站建设 2026/10/16 1:09:43

2S电池组被动均衡方案:从电压失配到MP2642与PIC24F实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 1:09:10

无监督单目深度估计:左右一致性如何用双目几何约束训练模型

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 1:09:10

Qt从零开始入门指南:环境搭建、信号槽与实战项目

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华