微信大数据挑战赛复盘:CTR预估与特征工程实战全程解析

发布时间:2026/10/20 7:19:33
微信大数据挑战赛复盘:CTR预估与特征工程实战全程解析 微信大数据挑战赛国内数据竞赛圈里关注度很高的一个场次。今年我和队友从初赛一路打到决赛最终拿到的名次不算靠前但整个过程下来踩过的坑、试过的方法、调过的参远比最后的奖项更有价值。这篇文章不打算写一段“获奖心得”而是把我们从赛题拆解、数据探索、特征工程、模型训练到最后做融合的全流程用复盘的方式记录下来。适合那些准备第一次参加数据竞赛、或者已经报了名但不知道从哪下手的同学也适合那些打过几次比赛但总觉得分数卡在平台期的朋友。我会尽量说人话把每一步“为什么这样做”讲清楚而不是只丢一份跑分报告。1. 赛前准备与赛题拆解1.1 先别急着跑模型把赛题读懂数据竞赛最常见的一个错误就是拿到数据立刻开始写代码恨不得当天晚上就把 baseline 跑出来。我第一次参赛的时候也是这样结果后来才发现对赛题的理解是错的大量时间花在了无用的方向上。微信大数据挑战赛这类的题目通常会给一张用户行为日志表里面包含用户ID、内容ID、行为类型、行为时间戳等字段目标是根据历史行为去预测用户在特定场景下会不会发生某种行为。更具体地说这是一个CTR/CVR类型的预估问题。第一步要做的事情不是建模而是把几个关键问题想清楚预测的目标到底是什么是二分类概率还是排序分数评估指标是什么AUC 和 GAUC 的差别很大会直接决定特征和模型怎么设计。训练的样本是什么粒度一条样本代表一次曝光还是一对用户-内容组合训练集和测试集的时间范围是怎样的这决定了验证集的划分方式。这些内容赛题说明里通常都有但很多人看一遍就过了。我的习惯是把赛题说明打印出来用笔把关键词圈出来尤其是“评估指标”和“训练/测试数据格式”这两段逐字逐句读三遍。遇到不清楚的地方再回到赛题页面上和主办方确认。宁可赛前多花半天也好过后面用一个月为错误理解买单。1.2 评估指标决定了解题方向我记得这一届微信大数据挑战赛的评估指标用的是 AUC 家族。很多人觉得 AUC 不就是 ROC 曲线下面面积嘛用 sklearn 算一下就出来了能有多大差别实际上差别大得很。如果是普通 AUC你把所有样本简单拼接在一起算就行这时候你关心的是全局排序能力。但如果是 GAUCGroup AUC那就要按照某个维度通常是用户分组先计算每个组内的 AUC再按组内样本数或曝光数加权平均。这意味着模型的重心不再是“全局把正样本排前面”而是“在每个用户自己的候选集里把正样本排前面”。这两个目标并不完全一致。举个例子全局 AUC 高可能是因为模型特别擅长区分活跃用户和非活跃用户把活跃用户的正样本都排前面了。但 GAUC 更看重同一个用户内部你对 TA 喜欢的内容和不喜欢的内容有没有区分度。所以做特征的时候需要在用户维度的个性化特征上花更多功夫而不是一味堆内容侧统计量。我们当时花了大概一周时间围绕指标设计了验证方式后面基本没有走偏。我的建议是参赛第一天就把评估指标吃透并且自己写一个复现评估指标的脚本用 baseline 的小结果跑通验证后面每一次提交之前都可以本地先算一遍。否则你根本不知道自己的每次改动到底有没有用。2. 数据分析与样本工程2.1 数据探索要回答的三个问题拿到数据以后我先做了比较系统的 EDA探索性数据分析。很多人做 EDA 就是画几张分布图看一眼就过去了。我的经验是要有目的地去看主要回答三个问题。第一数据规模有多大能不能放进内存微信大数据挑战赛这类比赛的数据量级通常在几千万到几亿行单机 Pandas 很容易内存爆掉。我一开始直接 pd.read_csv结果 16G 内存的机器直接卡死。后来改成用 pandas 的分块读取或者换成分块读入后抽样统计路径才顺畅起来。第二数据的时间分布是什么样的用户行为数据往往有明显的星期效应和小时效应。我画了每天的行为量、每个小时的活跃度曲线发现工作日的白天和晚上的行为模式完全不同。这些规律后面都做成了时间特征。第三缺失和脏数据多不多ID 类字段一般比较干净但数值字段经常有明显的异常值比如某条记录的停留时长为负数或者年龄字段出现几百这种离谱值。对待缺失值我采取的策略是“能不做填充就不做填充”因为很多模型对缺失值有自己的处理方式。LightGBM 可以直接把 NaN 当成一个分支方向强行填充反而会引入噪声。下面是我当时做数据概况统计时的核心代码压力不算大import pandas as pd # 分块读取先看整体行数和字段 chunk_size 500000 total_rows 0 dtypes_summary {} for chunk in pd.read_csv(train_log.csv, chunksizechunk_size): total_rows len(chunk) for col in chunk.columns: unique_est chunk[col].nunique(dropnaFalse) dtypes_summary[col] dtypes_summary.get(col, 0) unique_est print(总行数, total_rows) print(每列唯一值累加, dtypes_summary)这里用的是一个近似估计思路不需要精确统计每一列的唯一值因为大数据量下 nunique 非常耗时。先快速了解字段的基数再决定哪些字段适合做类别特征、哪些适合做数值特征。2.2 样本构建与正负样本处理赛题给的行为日志并不直接就是训练样本。需要自己决定一条样本怎么定义正样本是什么负样本从哪里来拿 CTR 预估场景来说通常一条样本就是一次“曝光”。曝光后用户点了就是正样本没点就是负样本。但微信大数据挑战赛里给定的是用户过去一段时间的日志没有显式的曝光记录这就需要推断。我们当时的做法是把用户发生过目标行为的时间点看作正样本再在附近的时间窗口里采样未发生该行为的时刻构造负样本。负采样的比例非常关键。如果正负样本比是 1:100 甚至更大模型会倾向于把所有样本都预测为负样本。我们的经验值是控制在 1:5 到 1:20 之间具体看模型。采样时还要注意“时间对齐”负样本的时间分布要和正样本保持一致不能只在半夜采样负样本那样就等于强行塞入了时间信息。我们第一版 baseline 用的就是 1:10 的负采样比例线下 GAUC 大概 0.62后面慢慢调到 0.70 以上。如果一开始就把负样本调到 1:50baseline 会非常难看甚至让你误判整个方案的方向。2.3 验证集划分按时间切不要随机切这条经验可以说是数据竞赛里最重要的一条没有之一。用户行为数据本身有时间结构线上测试集是未来一段时间的样本如果你在训练集里随机抽 20% 做验证那么验证集里既包含“过去”也包含“未来”模型在训练时已经见过和验证集同时期的数据分布离线分数会虚高而且这种虚高在不同特征组合之间并不稳定。正确做法是按照时间先后把训练集切成两部分比如前 7 天做训练最后 1 天做验证。这样能够模拟真实线上场景。我们当时用了“滑窗 留一天”的方式确保验证集的 GAUC 和最终线上结果的相关性高很多。注意如果赛题提供的训练数据本身就包含多个时间片一定要先看测试集对应的时间范围。训练集最后一天的分布可能和测试集相差很大那么验证集还要再往前放一点尤其要留出“评价期”和“冷启动”缓冲。另外构造样本时要注意不能 lookahead。也就是说某条样本在构造特征时只能使用该样本时间点之前的信息。这个点在特征工程部分还会反复踩到。3. 特征工程实战3.1 用户侧特征一切围绕历史行为特征工程是整个比赛里最花时间、也是提分最大的一块。在微信大数据挑战赛这种用户行为预测任务里用户侧特征的重要性大于内容侧特征因为 GAUC 本身就更看重用户内部的排序。用户侧特征主要包括几类历史行为统计过去 1 天 / 3 天 / 7 天用户的行为次数、目标行为次数、点击率CTR、行为多样性涉及多少个不同内容等。活跃度刻画用户每天活跃时段、最近一次活跃距今多久、活跃间隔的平均值和方差。兴趣向量对内容 ID 的偏好可以用用户最近行为的内容 ID 做聚合 embedding。这里要特别强调时间衰减。同一个用户在 7 天前的一次行为和昨天的一次行为信息量完全不同。所以做统计特征时不能简单把整个历史平均一下而是要做“时间衰减加权”。最常用的方法是指数衰减给距当前时间越远的行为乘以越小的权重。import numpy as np def decayed_count(df, time_col, span_days7, half_life_days2): # 构造指数衰减权重 delta_days (df[time_col] - df[time_col].max()) / (24 * 3600) weights np.power(0.5, -delta_days / half_life_days) df[decayed_cnt] df.groupby(user_id)[behavior_id].transform(count) * weights return df这段代码是我为了说明思路简化过的真实场景里要先把时间戳换算成相对时间然后对每个用户做聚合。重点不是代码本身而是这个“衰减窗口 半衰期”的思路。半衰期可以多试几个值比如 1 天、3 天、7 天放到模型里让特征重要性告诉你哪个更有效。3.2 内容侧与交叉特征内容侧特征也很重要。每个内容 ID 会有自己的历史热度、近 1 天 / 近 7 天被行为次数、正样本率等。这类特征的作用是捕捉“这个内容本身火不火”。但它有一个问题内容的热度是会衰减的有的内容上线第一天很火后面就淡了。所以内容侧特征同样需要加时间窗口不能直接全量统计。交叉特征是把用户和内容拼在一起。最简单的做法是统计“该用户过去对该内容的行为次数”如果有的话这个特征几乎会直接给出答案。但真实的推荐场景里绝大多数用户和内容之间没有历史交互大多数值都是 0稀疏度极高。这种情况下我一般会构造一个“用户-内容类别”的组合特征。比如说内容有频道或分类字段就可以统计“该用户在该频道上的历史行为数”、“该频道下内容的平均热度”这样的特征。它比单纯的用户侧特征更能反映个性化又比用户-内容的精确匹配稀疏度低很多。我在做交叉特征时的另一个心得是不要迷信 pairwise 交叉先做用户侧和内容侧的宽表然后让模型自己去学特征之间的非线性交互。LightGBM 这类 GBDT 模型本身就能做特征分裂组合很多时候你把交叉特征做好提分效果反而不如多加一个高质量的单侧特征。3.3 时间上下文特征与防穿越行为数据比赛还有一个很容易被忽视的点时间上下文特征。用户在周一早上看到的内容和周五晚上看到的内容行为概率可能差别很大。所以我在特征列表里加入了小时、星期、是否节假日这些字段。但它们不能直接作为原始特征喂进去因为测试集的时间范围可能不同直接把小时放进去会导致模型学到“测试集的某个小时概率低”这种错误的模式。更好的做法是把“用户在当前小时的活跃程度”或者“内容在最近一个小时内被行为的数量”作为动态特征。这种特征既携带了时间信息又不是单纯的日历时间。防穿越是特征工程里最需要小心的地方。最常见的坑是构造内容侧特征时用了全量数据统计导致训练集和验证集共享了同一份统计量验证集的信息被泄露到了训练集里。解决办法是特征统计必须按时间进行比如每个内容的“7 天热度”只能在当前样本时间点之前的 7 天窗口内计算。这需要写一个有序的滚动聚合不能简单地对整个表 groupby。我们后来用过一个简单的 debug 方法把训练集里“点击率特别高”的样本挑出来检查它们的特征值如果发现某些内容 ID 的热度特征高得离谱通常就是泄露了。发现之后不要心疼数据直接重算。4. 模型训练与调优4.1 第一个 BaselineLightGBM 必须跑通第一次做微信大数据挑战赛这种数据量级的比赛我不建议一上来就上深度模型。哪怕你的深度学习经验再丰富也先跑一个 LightGBM baseline。原因有三个第一baseline 的价值在于打通流程包括数据读取、特征拼接、训练验证、预测提交这一整套 pipeline。流程通了后续所有优化才有依托。第二GBDT 对特征工程的结果反馈非常直接特征做得好不好很快能从线下 GAUC 变化上看出来。第三LightGBM 处理几十个特征、千万级样本完全够用迭代速度快适合快速验证想法。我们当时的 baseline 只用了几十个特征线下 GAUC 大概 0.62。流程跑通之后后面所有的优化都在这个基础之上迭代。如果你连 baseline 都没有就想着去做模型融合那等于空中楼阁。4.2 LightGBM 参数调优的实用顺序LightGBM 参数很多但实际调参不需要一上来就网格搜索。我的调参顺序是固定的先固定一个较大的学习率 0.1把 num_leaves、min_data_in_leaf、feature_fraction 这些参数根据经验设一个合理初值。用 early stopping 确定迭代轮数。千万不要手工固定一个 500 轮那样容易过拟合或欠拟合。再缩小学习率比如 0.05、0.01重新跑一遍观察验证集 GAUC 的变化。最后针对 num_leaves 和 min_data_in_leaf 做小范围搜索。我给出一个我当时用的参数配置作为参考params { objective: binary, metric: auc, learning_rate: 0.05, num_leaves: 63, min_data_in_leaf: 100, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 1, verbose: -1, num_threads: 8, }其中 min_data_in_leaf 设大一点是为了在大数据集上抑制过拟合。数据量越大叶子节点越小模型越容易记住噪声。宁可让树的深度浅一点把泛化性能留给后面的融合。调参过程一定要做好实验记录。我们会在每轮实验后面记录特征版本、参数、线下 GAUC、线上 GAUC。没有记录你后面一定会忘掉之前调过什么然后浪费大量时间重复实验。4.3 深度学习模型值不值得上微信大数据挑战赛这类比赛很多冠军方案会用深度学习模型尤其是带 attention 机制的网络比如 DIN、DIEN 这些专门建模用户行为序列。但这些模型能不能帮你提升完全取决于你的数据量和数据形态。如果你的样本量在千万级以上用户行为序列足够长那么深度学习模型值得尝试。但如果只有几百万样本行为序列又短深度模型很可能打不过调参到位的 LightGBM而且训练成本高到难以快速迭代。我的建议是前期把精力放在特征和 LightGBM 上把框架跑通之后如果时间充裕再尝试一个深度学习版本。不要一上来就和深度学习较劲数据竞赛里绝大部分分数来自特征工程和集成深度学习是“锦上添花”不是“雪中送炭”。我们尝试过 DeepFM在相同特征下因为 embedding 学习的限制初期效果反而不如 LightGBM。后来改变策略把 LightGBM 的概率输出作为深度模型的一个特征才算有了正向收益。这个做法本质上是把两个模型的输出做了融合非常实用。5. 模型融合与线上提分5.1 融合为什么总是能提升单模型做到后期线下 GAUC 基本在一个平台期上不动了。这时候最有效的提分手段通常就是模型融合。不同模型学到的数据模式不一样LightGBM 擅长捕捉特征交叉深度模型擅长捕捉 embedding 表示融合之后可以取长补短。最常用的融合方式是加权平均。假设你有两个模型分别输出概率 p1 和 p2那么最终概率就是 alpha * p1 (1 - alpha) * p2。这个 alpha 需要在验证集上搜索一般从 0.5 开始向两边试。要注意的是两个模型如果相关性太高融合几乎没效果。所以我们一般会选择 LightGBM 深度模型或者 LightGBM XGBoost而不是两个结构完全一样的模型做融合。还有一种叫排序融合。因为评估指标是 GAUC本质上只关心排序不关心具体概率值所以可以把多个模型的预测结果按行分别排名再把排名取平均。这个方法在指标是 GAUC 的时候往往比概率加权更稳因为它不容易被某个模型的概率分布形态带偏。我当时的方法是这样先用 LightGBM 和 XGBoost 各出一个预测再对每行取两个模型的概率平均验证集 GAUC 从 0.685 提升到 0.692。然后又把深度模型的输出加了进来三个模型做简单平均线下又提升了 0.003。看起来幅度不大但在排名榜上就是几个名次的差距。5.2 离线高分线上掉分问题出在哪参赛过程中最让人崩溃的事就是本地验证集 GAUC 很高一提交线上分数就掉一截。这种情况在微信大数据挑战赛里非常常见原因可能有几个。第一验证集和线上测试集的时间分布不一致。前面说过如果验证集切得太靠近训练集而线上是更远的未来模型在验证集上看到的分布和历史更接近自然会虚高。第二特征统计量发生了穿越。某个内容的热度特征是在全量数据上统计的训练集和验证集共享了信息导致线下虚高但线上做得到做不到。第三线上测试集的样本构成不一样。比如线上正负样本比例和线下不同AUC 会直接受影响这是数据本身的问题不一定是模型过拟合。面对这种问题我的做法是先检查验证集时间切分是否合理再看特征是否穿越如果都没问题就干脆以线上为准调整策略。有些特征线下特别有用但线上没用说明是统计口径问题要果断删掉。注意提交结果之前一定要确认预测概率的列名、样本顺序和线上要求完全一致。看起来是小问题实际上每年都能淘汰不少人。6. 参赛心得与避坑清单6.1 时间管理与迭代节奏数据竞赛是一场马拉松不是冲刺。两个多月的赛程如果前期用力过猛后期很容易疲掉。我当时把整个赛程分成了三个阶段前两周做数据探索和 baseline中间三周集中迭代特征最后两周做模型融合和精调。每个阶段结束都留出缓冲时间防止意外情况影响进度。比赛的日常节奏也很关键。我固定每天早上跑一组实验下午分析结果晚上写代码构造下一天的特征。每天只做一到两个改动配合实验记录这样每一分提升都能定位到具体原因。千万不要一天同时改十个特征那样就算分数涨了你也不知道到底是哪个起作用。6.2 我踩过的五个坑和解决方案我把一次完整的比赛过程中遇到的典型问题整理成了一个表格方便大家对照排查。问题表现原因解决方案内存爆炸读取数据时直接被 kill数据量太大一次性 load分块读取用抽样统计验证集虚高线下 GAUC 0.75线上 0.65验证集随机划分特征穿越按时间划分验证集特征滚动计算负样本过多模型预测概率普遍极低正负比失衡负采样 1:10观察 GAUC 变化深度模型不涨分DeepFM 效果不如 GBDT数据量不足 / 行为序列短把深度模型输出作为融合特征提交 ID 错误提交结果得分为 0预测顺序和测试集不一致提交前校验行数和顺序写脚本自动检查这五个坑每一个我都不是从书上看来的而是实打实踩过。尤其是验证集虚高那个问题曾让我们整个团队一度怀疑特征方案错了后来花了两天排查才发现是特征穿越。希望后来者能少花这种冤枉时间。6.3 给新手的三个建议如果这篇文章对你有用最后我想给准备参加比赛的你说三句话。第一先跑通再优化。一个可以运行的 baseline比一万个停留在脑子里的完美方案都值钱。第二建立自己的实验记录体系。用简单的表格记录每次实验的改动和结果这会让你的工作效率至少翻一倍。第三多看看公开的思路分享。比赛最关键的是思路碰撞不要闷头做。我在比赛过程中就借鉴了很多前人的特征构造经验再用自己的方式落地。说说我个人这几个月下来最深的体会。参赛之前我以为数据竞赛拼的是模型复杂度谁的网络结构高级谁就赢。真正走完微信大数据挑战赛这一趟我才明白决定排名的往往是那些看起来不起眼的基础工作比如吃透指标、做对验证集、防住特征泄露、把实验记录做好。这些工作不性感甚至有点枯燥但每一步都在实打实地提高你的分数。如果你也想参加下一次比赛别纠结于那些花哨的技巧先把这几点做好成绩一定不会差。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询