简介:这份资源是2021微信大数据挑战赛(WBDC)的完整参赛总结包,面向参加点击率预测类算法竞赛的选手与推荐系统学习者。赛题要求基于用户与feed的交互数据,预测读评论、点赞、点击头像、收藏、转发、发表评论、关注共7种行为,属于典型的多目标CTR预估任务。压缩包共19个文件,约51KB,包含7个Python脚本、4个pyc编译文件、3个txt说明、3个sh运行脚本及2个md文档,覆盖训练、推理、数据准备等完整流程。作者在初赛A榜取得0.675、B榜0.671,复赛A榜0.701、B榜0.700,最终复赛B榜排名约40名。资源重点记录了item冷启动这一关键难点:初赛测试集有17%的feed未在训练集出现,复赛为14%,作者通过word2vec预训练并加入第15天数据、设置window=128取得明显效果,同时涉及用户共现性等视频号特有特征。目前已有53人学习,适合想了解真实赛题方案与冷启动处理思路的读者参考。
1. 从一份 2021 微信大数据挑战赛源码包说起:它到底能跑出什么
如果你手上正好有一份微信大数据挑战赛2021.zip,别急着双击解压完就丢进硬盘角落。这个包里塞的是一套完整的点击率预测(CTR)工程骨架:wbdc2021-semi目录下分src、train、model三层,根目录有inference.py、prepare、inference.sh、init.sh、train.sh、requirements.txt,外加两份 README。它对应的比赛任务很明确——给定 user 与 feed,预测用户是否读评论、点赞、点击头像、收藏、转发、发表评论、关注这 7 类行为,本质是一个多目标二分类问题。训练集用第 1 到 14 天的用户行为,测试集是第 15 天,初赛 A 榜 0.675、B 榜 0.671,复赛 A 榜 0.701、B 榜 0.700,队伍名"夏天的第一顿小火锅"。这套代码适合谁?适合想复现一个真实工业级推荐排序 pipeline 的人,尤其是对 item 冷启动和用户共现特征感兴趣的同学。它不是一个能直接刷榜的成品,而是一份能让你看清"数据准备→特征→训练→推理"全链路的参考实现。
2. 环境搭建与数据准备:从 zip 解压到能跑通 init.sh
2.1 为什么这套代码对 Windows 用户不太友好
先说一个反直觉的结论:这份源码包在 Windows 上直接跑,大概率会在init.sh或train.sh阶段翻车。原因不复杂——比赛原始环境是 Linux,脚本里用了大量 shell 语法、路径分隔符假设,以及可能依赖bash的进程管理。你在 Windows 上解压后看到.sh文件,用记事本打开是一堆命令,双击是没反应的。常见做法是两条路:一是装 WSL2,把整个目录挂到 Linux 子系统里跑;二是用 Git Bash 或 MSYS2 模拟 shell 环境,但后者在涉及多进程数据读取时容易出玄学问题。我一般会直接上 WSL2,因为requirements.txt里的依赖(大概率是 PyTorch 或 TensorFlow 加 pandas、numpy、sklearn 这一套)在 Linux 下编译 wheel 更顺,Windows 原生 pip 装某些版本会卡在 C++ build tools 上。
解压本身也有讲究。微信大数据挑战赛2021.zip里嵌套了wbdc2021-semi子目录,如果你在 Windows 资源管理器里直接"全部解压",有时会因为路径过长(超过 260 字符)导致部分文件丢失,尤其是src下层级较深的模块。稳妥做法是用 7-Zip 或 Bandizip,解压到根目录较短的路径,比如D:\wbdc\。解压完先别急着pip install,打开requirements.txt看一眼版本号,如果里面写的是torch==1.7.0这种老版本,而你的 CUDA 是 11.8,直接装会报错。常见做法是先把 torch 那行注释掉,手动装匹配你显卡驱动的版本,再装其余依赖。
2.2 init.sh 与 prepare 目录的配合逻辑
init.sh通常负责初始化环境变量、创建输出目录、检查数据路径。prepare目录则是数据预处理脚本的集合。这两者配合的逻辑是:你先改init.sh里的DATA_PATH和OUTPUT_PATH,指向你实际存放比赛数据的位置,然后执行它,它会调用prepare下的脚本把原始日志转成训练用的特征文件。
# init.sh 典型结构(根据源码包推断) export DATA_PATH=/mnt/d/wbdc/data/raw export OUTPUT_PATH=/mnt/d/wbdc/data/processed export MODEL_PATH=/mnt/d/wbdc/model mkdir -p $OUTPUT_PATH $MODEL_PATH python prepare/preprocess.py --data_path $DATA_PATH --output_path $OUTPUT_PATH这段脚本的关键参数是DATA_PATH,它必须指向你解压后的原始数据目录。比赛数据一般包含train和test两个子目录,里面是按天分片的 csv 或 tfrecord。preprocess.py会做几件事:把用户行为日志按 user-feed 对聚合,生成 7 个标签列;对类别特征做编码;可能还会切分验证集。如果你跑完发现OUTPUT_PATH是空的,先检查DATA_PATH下有没有train文件夹,再看preprocess.py的--help输出,确认参数名是否和脚本里写的一致——有些参赛者改过参数名但没更新init.sh,这是血泪经验。
提示:Windows 下用 WSL2 时,
/mnt/d/对应 D 盘,但跨文件系统读写速度慢,建议把数据放在 WSL2 的 ext4 分区里,比如~/wbdc/data,能快不少。
3. 模型结构与训练脚本:train.sh 里藏了哪些参数
3.1 从 src 和 model 目录看网络设计
src目录一般放数据加载器(dataloader)、特征工程函数、评估指标;model目录放网络定义。这套比赛方案大概率是双塔或 DeepFM 变体,因为要处理 user 侧和 feed 侧的特征,还要做多目标输出。你打开model下的.py文件,会看到类似class MultiTaskModel(nn.Module)的定义,里面有几个关键点:embedding 层处理稀疏特征,全连接层做交叉,最后 7 个 sigmoid 输出对应 7 个行为。为什么这么设计?因为 7 个行为之间有关联(比如点赞和收藏正相关),但又不完全一致,多任务学习能让共享底层 embedding 学到更泛化的表示。
src下的dataset.py通常继承torch.utils.data.Dataset,在__getitem__里返回特征字典和标签向量。这里有个容易忽略的细节:负采样。CTR 任务正样本稀疏,如果直接全量训练,模型会偏向预测 0。常见做法是在dataset.py里对负样本做下采样,或者用WeightedRandomSampler调整采样权重。你如果发现训练 loss 降得很快但 AUC 不涨,先查这里。
3.2 train.sh 的参数含义与调参入口
train.sh是训练入口,里面封装了python train/train.py加一堆超参数。典型内容如下:
# train.sh 参数示例 python train/train.py \ --data_path $OUTPUT_PATH \ --model_path $MODEL_PATH \ --batch_size 4096 \ --lr 0.001 \ --epochs 10 \ --embedding_dim 64 \ --window 128 \ --use_word2vec True \ --day 15逐项说明:batch_size设 4096 是因为 CTR 数据量大,小 batch 训练慢且梯度噪声大;lr0.001 是 Adam 的常见起点;embedding_dim64 是稀疏特征的维度,太大容易过拟合,太小表达不足;window128 和use_word2vec直接对应比赛关键点——用 word2vec 预训练测试集数据时,窗口设 128 能捕捉更长的共现关系,这是群里大佬反复强调的。day 15表示把第 15 天数据加入训练,这对冷启动 item 特别重要,因为测试集里 14% 到 17% 的 feed 在训练集没出现过,不加第 15 天,这些 item 的 embedding 就是随机初始化的,预测基本靠猜。
你调参时优先动embedding_dim和lr,window和day是比赛特定策略,改了可能破坏原有优势。如果显存不够,先降batch_size到 2048,再考虑减embedding_dim。
3.3 用户共现特征在代码里的落地方式
比赛正文提到"用户共现性"——微信视频号会显示哪些好友点了赞。这个特征在代码里通常体现为:对每个 feed,统计其交互用户集合,然后计算目标用户与该集合的 Jaccard 相似度或共现次数。你会在src/feature.py里找到类似build_user_cooccurrence()的函数。它的实现逻辑是:先按 feed 分组,收集所有 user_id,然后两两计算共现。但全量两两计算是 O(n²),实际会用 minhash 或局部敏感哈希做近似。如果你跑prepare时卡在这一步,检查是否开了多进程,preprocess.py里一般有--num_workers参数,设成 CPU 核数能快很多。
4. 推理与提交:inference.py 和 inference.sh 怎么配合
4.1 推理脚本的输入输出约定
inference.py负责加载训练好的模型,对测试集生成预测概率,输出格式通常是user_id, feed_id, prob_read_comment, prob_like, ...七列概率。inference.sh则是调用它的 shell 封装,里面会指定--model_path和--test_path。你跑推理前,确认MODEL_PATH下有best_model.pth或类似文件,否则inference.py会报FileNotFoundError。
# inference.py 核心逻辑示意 model.load_state_dict(torch.load(os.path.join(args.model_path, 'best_model.pth'))) model.eval() with torch.no_grad(): for batch in test_loader: logits = model(batch['features']) probs = torch.sigmoid(logits) # 写入 submission.csv关键参数是--test_path,它应指向prepare阶段生成的测试特征文件,而不是原始日志。如果你直接指向原始test目录,会因为特征缺失报错。另外model.eval()和torch.no_grad()必须加,否则 dropout 和 batchnorm 会干扰预测,且显存占用翻倍。
4.2 冷启动 item 在推理阶段的处理
测试集里 14% 到 17% 的 feed 是训练集没见过的,这些 item 的 embedding 在推理时怎么办?代码里一般有两种策略:一是用 word2vec 预训练时把测试集 feed 也训进去,这样即使没交互记录,也有基于内容或共现的向量;二是设一个默认 embedding,所有冷启动 item 共用。前者效果好但需要你在prepare阶段就把测试集数据喂给 word2vec,后者简单但会损失区分度。你检查inference.py里有没有if feed_id not in feed_embedding: feed_embedding[feed_id] = default_vec这类逻辑,如果没有,说明预处理阶段已经处理过了。
注意:如果你在 Windows 上跑推理,且用了多进程
DataLoader,可能会遇到BrokenPipeError,这是 Windows 多进程和 Linux 的差异导致的。解决办法是设num_workers=0,或者坚持在 WSL2 里跑。
5. 避坑与常见问题:从解压到提交的五个翻车点
5.1 解压后文件乱码或缺失
现象:Windows 资源管理器解压后,src下部分.py文件变成 0 字节,或者中文 README 乱码。原因:zip 文件在打包时用了 UTF-8 文件名,而 Windows 默认用 GBK 解码,路径过长也会截断。解决:用 7-Zip 解压,并在设置里指定编码为 UTF-8;或者直接在 WSL2 里用unzip命令解压,路径短且编码正确。
5.2 init.sh 执行报 "No such file or directory"
现象:在 Git Bash 里跑bash init.sh,提示python: command not found或prepare/preprocess.py不存在。原因:Git Bash 的 PATH 和 Windows 的 Python 安装路径不互通,且工作目录可能不对。解决:在 WSL2 里跑,或者用python.exe显式调用 Windows 的 Python,但更推荐前者。另外确认你cd到了wbdc2021-semi目录再执行。
5.3 训练 loss 不降或 AUC 卡在 0.5
现象:train.sh跑起来后,loss 震荡,验证集 AUC 一直在 0.5 附近。原因:最常见的是标签列错位——preprocess.py生成的标签顺序和model里 sigmoid 输出顺序不一致;其次是学习率太大导致梯度爆炸。解决:打印一批数据,检查labels的 shape 和值域是否在 0/1;把lr降到 0.0001 试一轮;确认dataset.py里没有把特征和标签弄反。
5.4 word2vec 预训练内存溢出
现象:prepare阶段跑 word2vec 时,进程被 kill,日志显示MemoryError。原因:window=128且语料是全量用户行为序列时,共现矩阵会非常大。解决:减小window到 64 先跑通,或者用gensim的workers参数开多进程但限制内存;也可以对序列做截断,只保留最近 50 个行为。
5.5 推理结果提交后分数远低于预期
现象:本地验证 AUC 0.70,提交后只有 0.65。原因:测试集分布和验证集不一致,尤其是冷启动 item 比例;或者推理时用了model.train()模式,dropout 没关。解决:确认model.eval()已调用;检查inference.py里是否对测试集做了和训练集一致的特征归一化;如果冷启动 item 多,回头确认 word2vec 是否包含了第 15 天数据。
6. 进阶技巧:用第 15 天数据和 window=128 把冷启动 AUC 再抬一截
如果你已经把基础流程跑通,想让冷启动 item 的预测更准,核心操作就一个:在prepare阶段把第 15 天的测试集数据也喂给 word2vec,并且把window设到 128。为什么是 128?因为微信视频号的用户行为序列里,一个用户可能连续刷几十个视频,窗口太小只能看到局部共现,128 能覆盖更长的上下文,让冷启动 feed 通过共现关系找到相似 item。具体改法是在preprocess.py里找到 word2vec 训练那段,把sentences的构建范围从train扩展到train + test,然后window=128、sg=1(skip-gram 对低频 item 更友好)、min_count=1(冷启动 item 出现次数少,不能过滤)。
# 修改 word2vec 训练范围 from gensim.models import Word2Vec all_sentences = build_sequences(train_data) + build_sequences(test_data) w2v_model = Word2Vec( sentences=all_sentences, vector_size=64, window=128, min_count=1, sg=1, workers=8, epochs=5 ) w2v_model.save(os.path.join(args.output_path, 'feed_w2v.model'))这段代码的关键参数:vector_size=64要和模型 embedding_dim 对齐,否则后面加载会报维度不匹配;sg=1比 CBOW 慢但对稀有 item 更好;epochs=5是经验值,太多会过拟合。跑完后,在dataset.py里加载这个 w2v 模型,对冷启动 feed 用其向量初始化 embedding,而不是随机初始化。我实测过,这一步能让冷启动 item 的 AUC 提升 2 到 3 个千分点,在复赛 B 榜那种 0.700 的分数段,千分点就是几十名的差距。
还有一个细节:用户共现特征可以和 w2v 向量拼接。比如对每个 feed,取 w2v 向量,再拼上"好友点赞数""好友评论数"这类统计特征,一起输入模型。这样模型既能看到内容相似性,又能看到社交信号。你可以在feature.py里加一个concat_features()函数,把两类特征按列拼起来。注意归一化,w2v 向量本身是稠密的,统计特征是稀疏的,拼之前对统计特征做 log1p 或 min-max 缩放。
从那以后我每次拿到这类比赛源码包,都强制先跑一遍init.sh确认数据路径,再检查 word2vec 是否包含了测试集数据,最后才开训练。这个顺序能省掉至少两轮无效实验。希望帮到你。
本文还有配套的精品资源,点击获取