资讯详情

Elo竞赛复盘:特征工程、目标编码与模型融合的实战指南

📅 2026/10/24 6:05:34 | 华诺云谱 👁 阅读
Elo竞赛复盘:特征工程、目标编码与模型融合的实战指南
最近整理自己打过的比赛时翻到Elo Merchant Category Recommendation这一场感触挺深。它是典型的“表格数据时序用户行为”回归赛涉及特征工程、目标编码、离线验证、模型融合这些常规招式但每一招都比一般的入门教学赛藏得更深。如果你现阶段正在学机器学习竞赛用这个题练手会非常值。这场竞赛的核心目标是预测用户对商户类别的偏好得分但是所有特征都不是现成的表格数据分布在交易流水里需要你自己动手聚合。当时我花在数据分析上的时间比建模多得多最后拿到了一个不错的排名。这篇文章不是复刻官方高手的方案而是我跑完整个流程后沉淀下来的完整思路包括数据表关系、验证切分、特征聚合、调参和踩坑记录希望能给准备入坑或正在做类似题目的朋友一些参考。1. 赛题拆解Elo这场回归赛真正在考什么1.1 任务定义与评估指标回归到交易行为里的偏好分数这个赛题给的训练数据是一组脱敏后的信用卡交易流水每个持卡人有若干条历史交易记录每条记录关联了商户ID、商户类别、城市、交易金额、分期笔数等字段。我们需要预测的是目标字段——一个代表用户对该商户类别偏好强度的连续型分数评测指标用的RMSE。可以这么理解业务场景平台要根据用户过去的刷卡习惯对某个商户或商户类别给出一个推荐分数分数越高说明这位用户越可能在下一个时间段里选择这类商户消费。模型输出的不是分类标签而是一个排序分最终用作推荐列表的排序依据。RMSE这个指标意味着我们不需要追求极端精确的绝对分数更重要的是整体预测误差的平稳性。它比较容易被少数差异很大的样本带偏所以在做特征和调参时对异常大或异常小的目标值要格外关注。当时我的经验是训练目标并不是完全平滑的分布有一小部分分数很低或很高的样本它们对RMSE的影响远超中间区域的样本。1.2 几类表之间的协作关系训练主表、交易明细表和商户属性表赛题数据并不是一张大宽表而是分成了多张表。主表每行是一个card_id带若干脱敏的用户级匿名特征交易明细表记录每条真实刷卡记录一个card_id对应多行交易交易时间、是否授权、金额、滞后月份都在这里商户表则提供了商户ID本身的一些属性比如类别、城市、活跃度等信息。表之间的关联关系主要有两条一是用card_id把主表跟历史交易明细关联得到“这个用户过去刷过什么”二是用merchant_id把交易明细跟商户表关联得到“这笔交易发生在什么样的商户身上”。这些关联都是在做特征工程之前需要彻底搞清楚的如果字段关系理解错了后面造出来的特征全是错的。我记得有相当一部分参赛者其实没有吃透这个关系直接把交易明细表当主表去训练导致一个card_id被重复输入到模型里结果一定是极差且没有意义。正确做法永远是先定好预测目标我们是站在card_id这一行去预测目标分数所以最终特征表一定是主表级别的宽表交易数据只是用来构造这个宽表的中间素材。1.3 为什么这个题适合反复复盘Elo赛题之所以经典不只是因为数据量大、特征组合多而是它几乎涵盖了表格类竞赛的所有关键环节多表关联、时序泄漏风险、类别特征编码、稀疏交易处理、内存优化、单模调参和多模型融合。每一个环节单独拿出来都可以作为一次专题练习。更难得的是它特别像真实业务里的用户画像和推荐系统构造过程。你在实际工作中会遇到的用户冷启动、交易记录不完整、特征出现缺失值这些问题这个赛题里全都有。所以我一直跟身边想学竞赛的朋友推荐说Elo这场赛可以作为“第一个认真刷的比赛”刷完它之后大部分表格类比赛的处理思路你都大概能摸到边了。2. 数据科学家的第一步不是建模是理解每一列的含义2.1 先回答三个关键问题谁在交易、什么时候交易、交易算不算数打开交易明细表之后我建议先不要急着写代码先回答几个业务问题这张表里的每一行代表什么一个用户的交易行为是按什么时间维度记录的所谓“授权”和“未授权”对统计口径有什么影响我在Elo赛题里遇到最重要的一个问题是authorized_flag。它标记了这笔交易是否被授权通过。被拒掉的交易记录其实仍然存在表里如果把它也当作正常消费去做统计金额均值和频次都会被拉偏。我当时先单独拉出授权交易做一套特征又拉出全部交易做第二套特征模型结果明显是第一套更稳定。这个字段本身也值得做一个统计某用户在过去一段时间的授权失败比例就隐含了他的消费质量和信用层面的信息。另外month_lag字段非常关键它表示这笔交易距离当前预测时间点的月数。你自己想一下如果一个用户在最近一个月频繁在某个品类消费和他在一年前频繁消费哪个对当下偏好的预测力更强显然是近期行为。所以后续时间窗口特征的基础就是先吃透这个字段的含义。2.2 内存优化把几百万行交易流水塞进内存的土办法Elo赛题的数据量不算恐怖但也不算太小尤其是交易明细表通常有几千万行。如果你一上来就用默认的pandas读取方式内存很容易直接爆炸导致后面所有操作都卡顿。我当时的做法很简单读取时指定每列类型把能转的整型列转成int32或int8浮点列转成float32ID类列转成category。全部处理完之后内存占用能比原始加载方式降一半以上。不要小看这个步骤在没有GPU内存资源紧张的环境里这一步省出来的内存直接决定了你后续能不能放肆尝试特征。另外对于多表关联后产生的超大中间表尽量不要反复保存成CSV再读取CSV的读写成本太高优先用parquet格式保存中间文件。我自己在调特征过程中保存过上百个特征中间表parquet格式让重跑特征的成本变得非常低。2.3 缺失值不是随机掉的而是带着业务信息在Elo赛题里有几列缺失率很高。很多新手第一反应是全用均值或中位数填充但如果你只这么做了其实丢掉了很多信息。缺失本身经常意味着“没有发生过”或者“该商户类型下没有这个属性”这种空值和0是完全不同的业务含义。比较实用的做法是把缺失值单独造一个布尔特征出来表示“是否缺失”再让模型自己去学习该特征和缺失填充值的组合关系。比如某个类别特征缺失时填充-1同时保留原缺失标志列LightGBM这类树模型就能依靠分裂逻辑找到缺失组合的规律。我在做实验时明显感觉到加上缺失标志列之后线下验证的RMSE会微降一些。2.4 匿名特征的作用比想象中大主表里有若干匿名特征列很多人会把大量精力放在交易明细的聚合上却忽视这些匿名列。实际上这些匿名特征往往是官方在构造这个挑战时就已经筛选过的强特征它们本身可能就带有部分目标信息。我的一位一起打比赛的朋友早期只做交易特征忽视匿名特征本地分数一直卡在一个瓶颈后来把匿名特征简单做了组合和分箱分数立刻提升了一个档次。对待匿名特征的策略先看它们与目标的单变量相关性或简单回归结果把作用大的列直接作为基础特征输入再用它们做交叉实验比如两两相加、相减、相乘观察是否有信息增益。通过这种简单方式我找到了一些非常有效的组合特征。3. 线下验证的陷阱为什么本地分数很高线上却不涨3.1 随机切分为什么会给出虚假信心很多新手入门时习惯用一个train_test_split随机切分来评估模型但这个做法在Elo赛题里是致命的。原因在于目标分数本质上是由用户历史消费行为决定的同一个用户出现在训练集和验证集里时模型已经在训练时把“专属记忆”学到了验证时自然表现得不差。也就是说如果不做分组切分验证分数反映的只是模型对这些用户历史的记忆能力而不是它对陌生新用户的真实泛化能力。这会导致两个后果一是你为了提升“新特征”做了很多无用功因为虚假分数怎么都会高二是真正有效的特征反而不容易被看到因为分数都被高估了。3.2 我的CV方案按card_id分组时间裁剪我当时采用的验证方案是两层逻辑分组以card_id为组划分折数保证同一张卡的所有数据不会同时出现在训练集和验证集里时间裁剪在构造特征时为了让每个用户都能有“历史”我把特征聚合区间限制在预测时刻之前绝不让验证集时间段里的交易数据被提前聚合进训练样本。具体实现可以是定义一个函数输入训练集的月份截止时间输出对应时间之前的历史统计特征然后在对验证集做同样操作时把截止时间往后平移。这样在离线阶段就能模拟线上预测时的特征分布降低时间穿越风险。我还比较了随机切分和分组切分下的分数差距结果非常吓人随机切分下的本地RMSE低了非常多但提交到线上之后完全对不上。后来我彻底放弃了随机切分只用分组CV和自定义时间切分。3.3 本地分数和线上排行只盯公共榜会害了你竞赛平台通常会把测试数据分成公开榜和私有榜两部分公共榜在提交后能看到分数私有榜只有比赛结束后才能看到。很多人会为了提升公共榜分数反复提交其实这是在把公共榜当作验证集用很容易过拟合到公共榜的随机噪声上。我在Elo赛题中期也犯过这个毛病公共榜分数提升了一点结果私有榜出来后反而更差。之后我给自己定了一个规则公共榜分数只走相对变化作为参考最重要的永远是线下CV分数的稳定性。如果某个操作让CV分数提升但公共榜没有同步提升我倾向于相信CV并且会更仔细检查特征是否有泄漏。这个原则在后来的很多比赛中都帮我避开了无效自嗨。3.4 记录每一次实验的完整日志这部分经验可能听起来没技术含量但非常重要。每次实验都要记录特征文件路径、模型参数、CV分数、公共榜分数、备注说明。我见过太多参赛者跑了很多轮实验最后找不到最优模型对应的特征版本只能从头再跑一遍非常浪费时间。我习惯用表格记录实验编号特征版本模型参数CV分数公共榜分数备注exp_001base_v1lgb_default3.62103.6312基线exp_002base_v1lgb_tuned3.61003.6250调参exp_003feat_v2lgb_tuned3.60953.6233新增商户特征这份日志在后期做特征删减和融合时帮了大忙。4. 特征工程实战把百万笔交易折叠成商户画像4.1 聚合特征的黄金三件套数量、金额、频率教学赛里最常用的一句话是“groupby之后做聚合”但具体聚合哪些函数、聚合到哪个层级这才是经验所在。在Elo场景里核心维度至少有三个card_id、merchant_id、category_id。我当时造了以下几类聚合特征交易次数一个用户在某个商户/类别下总共消费了几笔以及占该用户全部交易笔数的比例金额特征消费总金额、金额均值、最近N笔的金额均值、金额标准差、金额的最大和最小值频率特征平均多久消费一次可以通过交易记录数除以覆盖的月份跨度近似得到。有一个需要注意的地方金额原始值不能直接参与跨用户的比较。因为用户消费能力差异巨大一个高消费用户的金额均值天然就高。所以我在做金额特征时会对每个用户单独计算其历史平均消费金额然后用单次消费金额除以这个用户均值得到标准化后的相对金额。这样对模型来说特征表达的是“这个用户比平时花得多还是少”而不是“绝对数字大不大”。4.2 用户偏好怎么用一笔交易算出一个偏好分对推荐类任务来说金额绝对值不重要重要的是用户在不同类别之间的相对偏好。比如一位用户在“超市类”消费了20笔每笔平均80元同时在“服装类”消费了2笔每笔平均500元。单看次数用户更喜欢超市单看单笔金额服装类显然更吸引他。到底怎么判断真实偏好我用了一种比较朴素但有效的方法先按(card_id, category_id)聚合出每个用户在每个类别下的总次数与总额然后跟用户在所有类别下的总次数与总额做差分对比得到一个比率特征。具体公式是ratio 用户在该类别的平均消费金额 / (用户在所有类别下的平均消费金额 平滑项)这个ratio大于1说明用户在该类别花的钱高于个人平均水平。再加上次数占比、最近消费月份远近组合起来就是一个非常稳定的偏好描述。这类特征在树模型中的重要度排序里基本都排前列。4.3 目标编码与平滑参数把类别特征变成信息特征Elo赛题里有大量类别特征比如category_id、city_id、merchant_category等。如果直接用原始类别ID喂树模型往往会因为某些类别数量太少模型记不住泛化效果也不好。目标编码是常用解法就是用该类别的历史目标均值替代类别ID本身。使用目标编码时注意要做fold内编码在每一折的交叉验证里用该折训练集的目标均值编码这一折的验证集特征防止把未来信息泄漏进去。同时要加入平滑项。平滑项通常有两种直接给均值加上样本量加权总均值乘以一个权类别均值乘以另一个权权重取决于样本数量用“类别目标总和全局均值×平滑系数”除以“类别样本数平滑系数”。平滑系数很关键太小会导致稀有类别过拟合太大会让编码信息消失。我当时用网格搜索试了不同的平滑系数最终选了一个让CV分数最稳定的值并没有一味追求训练集得分。4.4 时间窗口特征近期偏好比历史习惯更重要用户偏好不是恒定不变的食品类消费可能每周都有旅游类消费可能一年也就一两次。如果只用用户全量的历史统计最近三个月的消费模式反而不突出。所以我额外构造了“最近1个月”“最近3个月”“最近6个月”几个时间窗口的聚合特征再跟全量聚合特征做比值。这个操作会让特征数量翻倍但模型收益非常显著。因为树模型可以在不同窗口特征之间做切分天然学习到“用户近期变化”的信号。比如某个用户过去半年都在超市消费最近一个月突然频繁在健身房消费这时候“最近1个月健身房消费次数/全量健身房消费次数”这个比率就能马上捕捉到变化的苗头。需要注意时间窗口太短会导致某些用户出现在窗口内的样本数为0这时候要填充为一个特殊值比如-100表示“该窗口没有记录”而不是简单地填0。5. 模型环节LightGBM为主力XGBoost当辅助5.1 为什么梯度提升树适合这种表格数据Elo赛题的特征有上千维其中大量特征是稀疏的、类别化的还有不少缺失值。LightGBM作为基于直方图的梯度提升树对这类数据的支持非常友好内建处理缺失值机制、天然支持类别特征、训练速度快、能捕捉特征间的非线性交互。相比深度学习模型它不需要做繁琐的特征缩放也不需要把类别转为嵌入更适合表格竞赛环境。XGBoost在Elo这种规模的数据上也有不错表现但训练速度会明显慢一些。我当时把XGBoost当作交叉验证模型和融合成员在特征筛选阶段只用LightGBM做快速实验到了融合阶段才让多个模型并行跑。5.2 调参经验先固定一块再去动另一块调参顺序很重要不要上来就同时调所有参数。我当时采用的策略是固定学习率为0.05用它来确定树的规模先调num_leaves和max_depth观察训练集和验证集分数的差距变化确定模型复杂度上限固定树规模后再调min_data_in_leaf和feature_fraction控制过拟合最后调bagging_fraction和正则化参数再降低学习率到0.02或0.01增大迭代次数。我用的是类似下面这组初始配置params { objective: regression, metric: rmse, learning_rate: 0.05, num_leaves: 63, max_depth: -1, min_data_in_leaf: 20, feature_fraction: 0.7, bagging_fraction: 0.8, bagging_freq: 1, lambda_l1: 0.1, lambda_l2: 0.1, verbose: -1 }每调一个参数我都在表格里记录CV分数变化。不要只盯着训练集表现因为训练集轻松逼近0.00不代表模型有价值。关键是在验证集上出现平台期时就要考虑收手了。5.3 模型融合为什么简单的加权平均效果很好到了后期单个LightGBM很难再提升我开始做模型融合。融合的基本逻辑很简单多个模型对同一批样本的误差模式不同把它们的结果加权相加噪声互相抵消信号会被加强。但融合之前要看相关性。我计算了我的LightGBM预测结果与XGBoost预测结果的相关系数也计算了不同随机种子下LightGBM自身的预测相关性。相关性太高的话融合收益趋近于零。我最终采用的融合方式用不同随机种子训练多个LightGBM用不同特征子集训练两个LightGBM增加一个XGBoost和CatBoost模型对所有人的预测做加权平均权重通过在验证集上网格搜索得到。权重搜索范围一般在0到1之间步长0.05目标是让验证集RMSE最低。最后得到的权重组合不会太极端因为如果某个模型权重为0说明它对整体反而是噪声可以考虑直接去掉。5.4 特征重要性的使用边界LightGBM自带feature_importance输出每个特征在分裂时带来的信息增益总和。这是一个有用的参考但注意不要完全按重要性排序去删特征。对于高相关的特征组多个重要特征之间会分摊重要性权重导致单个特征的重要性显得很低如果直接删掉其实丢掉了整组信息。我的做法是用模型验证“置换重要性”把某个特征的值随机打乱看验证集分数的下降幅度。如果打乱后分数变化很小说明该特征在当前模型里作用不大可以尝试删除。这个方法比原生特征重要性的稳定性好很多。6. 复盘时最容易翻车的三个细节6.1 训练时用了未来信息历史交易聚合不小心覆盖了验证期这是最隐蔽的泄漏问题。我早期在构造特征时直接对整张交易明细表做groupby聚合然后join到训练数据上这时候问题就出来了训练集里的某一个card_id在验证期对应的交易数据也被聚合进去等于模型在训练时看到了未来的交易信息线下CV自然虚高。解决办法是在聚合前先对交易明细表按month_lag做时间裁剪。训练集里用month_lag小于某个阈值的交易做特征验证集和测试集也要按照相应的时间线生成对应特征。这里不要偷懒时间不对齐会让一切特征工程白做。我在修复这个问题之后本地CV分数涨得很难看但线上分数反而更真实了。这也是一个反向信号如果本地分数高得离谱先别高兴先检查变量有没有时间层面上的穿越。6.2 新商户冷启动特征全空模型怎么猜Elo赛题还有一个现实问题测试阶段会有一批全新的商户他们没有历史交易记录所有由交易流水聚合出来的特征都是空值或者填充值。模型在训练时很少见到这种“全空特征”的样本预测时就会往均值方向收缩导致新商户的预测分数普遍偏低。针对冷启动我当时做了两件事对交易聚合特征用相同商户类别、相同城市的已有商户的均值去填充新商户的特征而不是用全局均值单独构造一个“历史交易次数是否为零”的冷启动标志特征让模型意识到这个样本属于冷启动场景。加了这个特征后线上分数确实有了小幅提升。原因是模型学会了在识别到冷启动样本时采用一组更合适的权重去使用商户属性特征。6.3 公共榜和私有榜的反复横跳这个坑很多人一辈子都踩不厌。比赛平台的公共榜只是一部分随机样本的结果你用它来筛选模型时实际上是在对这堆样本拟合。公共榜刷得越好并不意味着私有榜就一定更好反而往往因为过拟合到公共榜噪声导致私有榜翻车。我当时的处理方法是把线下CV当作最权威的参考指标公共榜分数只用来检查是否存在严重的时间/特征泄漏。如果线下提升但公共榜反而变差我不会刷掉这个特征而是仔细检查两者之间的数据分布差异。这种方式尽管偶尔会让公共榜排名看起来平庸但最终私有榜排名往往能往前大幅提升。6.4 如果重来一次我会如何调整这套流程如果现在让我重新打一场Elo赛题我会在第一天就建立分组CV框架把数据表关系和特征生成管线全部写成清晰可复用的函数而不是每次临时修改。快速跑出一个全流程基线哪怕只用最朴素的计数特征也能让后续每个实验都在相同基准上比较。我会花更多时间在数据分析上比如画出目标分数的分布、交易金额随month_lag的走势、不同类别下交易笔数的偏态。很多时候特征为什么有效、为什么无效光看数值看不出来数据可视化能直接告诉你答案。模型部分我会更早引入多模型交叉验证而不是等到LightGBM调不动了才考虑融合。机器学习和体育竞技很像稳定输出的最低分往往比偶尔爆发的最高分更重要。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑