时间真快,遥想刚开始搞这个比赛的时候,我还是刚从字节离职。一晃几个月就过去了,我的研一上学期也接近了尾声。
这个比赛奖金贼多,总奖金18w,但是根据我从小到大围棋比赛的经验来看,有奖金特别是奖金丰厚的比赛往往能卷出新高度,一般都非常的难打。所以,我就一直觉得重在参与就好,但即使这样我中后期还是心态崩了。在组队方面,虽然师姐和同学是我的精神支柱,但是我没能找到一起写代码的队友,只好全程自己一个人肝。此外,研一上的课不是一般的多,感觉天天都是上课、写作业、考试,卑微的打工人只能抽出所剩无几的课余时间推进一下比赛的进度(虽然我觉得我全身心投入也干不过前排大佬,哈哈哈哈)。
虽然最后只是rank8(最后4, 5不知道咋回事,好像是一个人,所以变成rank6了,哈哈哈哈),但好歹也有1w奖金,也算是几个月的努力没有白费,今后还要继续努力啊。
代码:https://github.com/ytchx1999/MAXP_DGL_Graph
不得不说,这比赛初赛的时间真的长(10月~12月),分三个阶段总结一下。
我开始的比较早,基本就是国庆之后数据集和baseline就放出来了。有一点值得一提,这个比赛只能用DGL,其他的不能用(PyG、PGL等)。我之前没怎么用过DGL,就只能现学现卖,好在官方给了baseline。
首先肯定是跑baseline,看看啥情况。这个赛题我再熟悉不过了,几乎是和本科毕设一模一样(引文网络的节点分类任务),只不过数据集大了好多。有了一些先验知识的指导,再看代码感觉也比较好理解了。前期主要是在学习DGL的一些使用,仔细去看看了文档,慢慢也就熟悉了,甚至我觉得DGL比PyG更香一点。
baseline主要是GraphSAGE和GAT,没调参没加trick,GraphSAGE的acc大约在48左右,GAT高一点差不多在49左右。后面主要是根据在baseline的基础上做了一些改动(吐槽,baseline代码写的太烂),对GAT加了一些之前刷榜用过的trick:
一波操作,分数就来到了54.24。由于我开始的比较早,还没有多少人来提交这个比赛,所以我当时还能在rank8左右徘徊,当时还挺乐观的。但万万没想到,还有双周赛这个东西,第一个双周赛就被卷没了,记得好像是掉到了16名,让我放弃了幻想。但这还只是噩梦的开始,后面只会越来越卷,一段时间我直接不看排行榜了,因为每次一刷新排名就又掉一名。
我甚至冒出了一些非常离谱的想法——在ogbn-papers100M上进行训练!但是失败了,我分析原因可能在于:
阶段性放弃了,只能用研一上的课程大作业来麻痹自己,哈哈哈。
我和室友(也是此次队伍的一员)说,我卷不动了,他却说,你最后应该能是第7。我当时觉得他这是在损我,没想到竟是一语成谶。。。
中间我参加了DataFun的一个线上分享会,主要是一些学术界和工业界的GNN大佬们出来做分享。由于我之前看到OGB节点分类任务被GAMLP刷榜了,正好论文的一作也要分享,于是就关注了一下。但是,GAMLP毕竟是和SIGN、SAGN类似,都是Scalable GNN,我一开始理解他们放弃了一些精度,来做到Scalable。原始GAMLP根本也达不到刷榜的程度,之所以能刷榜是因为加上了类似伪标签/标签蒸馏等类似的trick。我并没有想用Scalable GNN来做比赛,毕竟比赛要的是精度,只要能跑起来,跑得快不快其实是次要的因素。
但是,我还是选择了“病急乱投医”,准备试试Scalable系列模型,就算不成功,也算是学习了一些新的方法。就这样,我又开始了一波自认为没有希望的探索:
但是,尽管如此,前排的大佬还是太强了。太卷了太卷了,每次到双周赛,我都不敢看排行榜,又开始往下掉。我再一次放弃了。有个小插曲,比赛举办方亚马逊DGL在群里打广告招实习生,日常和暑期实习都可以投递。我一想,既然我比赛拿不到奖金,那我何不再面一下试试,至少又可以积累一波经验了。我暑假7月份面过DGL,毫无悬念的被拒了,这次算是“二战”吧,哈哈哈哈。没想到,不知道是这次的题简单还是这几个月来我有所进步,居然过了。。那天上选修课,亚马逊的hr给我打来了电话,问我啥时候能去实习,这个以后再说吧,不是本次复盘的重点。
大佬们逐渐发力,越来越多的人也加入了这个比赛(钱多,宣传力度大。。)。前排先是分数逐渐上到了56,后来又上到了57,逐渐卷出了新高度。我一度快被卷出30名,卷不动了,真的躺平了。初赛还有一个月结束的时候,我彻底放弃了。分数也一直停滞不前,没有正反馈,比赛真的很难打下去了。而且这一阶段很多结课作业和考试,确实也顾不上了。之前我还加了前排大佬的微信,卑微的寻求组队,但是无奈自己太菜,大佬还是婉拒了我。
被这个比赛伤透了心的我,去看了一下同样是亚马逊举办的WSDM Cup,这次是动态连接预测任务,于是就做了一段时间的数据处理工作,提交了中期榜,这个也等有时间再说吧。至少换个比赛(没有实时榜),我就没有之前那么难受了。
但是,我的运气似乎是一下子就来了。临近初赛结束的某一天,我发现复赛的规则居然改了!!!原来只有前20名能参加复赛,现在是全体参赛者都可以了!我在往下看,更是抑制不住激动地心情——复赛时间只有48h!这样的话,如果有新的节点和边的加入,可能需要重新训练,模型是否Scalable非常重要,太复杂的模型未必能在48h内跑完。(虽然这个时候主办方还没有说是否要加入新的节点和边)我觉得,机会来了。
也正因为规则的改变,前排大佬开始疯狂的组队。我的排名竟然一天一天的提升了!直到复赛开始之前,我居然到了rank14,简直是不敢相信我居然是以这种方式前进。我突然对复赛重新燃起了希望,这时候,室友又跟我说,“我早都说了复赛能拿第7,我说第7就第7”。虽然有希望,但我还是半信半疑。总之,到了这一步,把自己全部发挥出来不留遗憾就好了。
复赛规则对我来说完全是有利的:每天能交2次,没有实时榜,也看不到自己的分数,比赛结束放榜。复赛新加入了160w+节点和2000w+的边,不重训是不行了。这样的话,GAMLP的优势就完全体现出来了——快。我甚至跑完“老牛拉破车”般的node2vec后,还能跑8-fold,集成20个模型。。48h绰绰有余,几乎没有“丢盔弃甲”的现象出现。
早上8点,复赛开始。我借了是有的百度网盘vip,开始有条不紊的下载数据集。在下载数据集和上传服务器的同时,我开始重新code review并修改原来的代码,适配新的test数据。9左右我开始运行jupyter的数据处理代码,虽然比较数据比较大,但是服务器还是相当的给力,没有和群里的大佬那样出现训不动的情况,都顺利地完成了。9点半左右正式开始执行了不带node2vec的GAMLP训练,看了一下loss和valid acc以及显存(毕竟V100 32GB)都没什么问题,放心的让他去后台跑了。
来到下午,我居然还要去上模式识别课!!!1点半到4点半,听一会看一会,后面老师讲的都懂了索性不听了,就修改了一下ensemble的代码,适配了test submission。
晚上回来,开始跑C&S和模型集成。保险起见,我没有选择更快的方式,而是选择了最稳妥的方式进行test submission文件的生成,7点左右提交了一下8 ensemble的版本,系统显示“success”,分数暂时看不到,这也代表代码应该没什么致命的非逻辑bug。然后整晚上就在等node2vec跑完。一边等一边看“孤独的美食家”,哈哈哈哈。大约到了11点,node2vec跑完了,虽然效果并不尽如人意,但也只能死马当活马医了。在另一块V100上开了GAMLP+node2vec的实验,观察了一会发现没什么问题,就上床睡觉了。
没课的一天,早上起来,提交了不加node2vec的20 ensemble版本,提交成功了。
下午3点左右,GAMLP+node2vec也跑完了,然后就开始20 ensemble,也提交成功了。
晚上洗了个澡平复了一下心情,但还是非常的紧张。我不知道为什么会这样,虽然我一直告诉自己平常心,但是到这了一步,努力了这么久,总是想要一个好的结果。室友说,你最后一定是第7。于是,我怀着惴惴不安的心情睡了一晚上。
早上起来,精神焕发。主办方说8点整放榜,但是那边好像又出了一些问题吧,总之,8点没能放榜。我来图书馆本来是要复习模式识别的,迟迟不放榜,我直接去复习模式识别了。快9点的时候,我看了一下群里,有人截图了,看来是出来了。我点开截图一看,居然是第8!要不是在图书馆,我可能都会叫出来。我把喜讯发给了队友们,队友们直呼过瘾。前排有两个号的结果一模一样,如果是小号的话,那我还真是rank7了,室友yyds。我从没想到过我能在奖金如此之多,如此卷的比赛中拿到奖金。感觉是实力+运气+各方面其他因素共同作用的结果,运气是还很重要的啊。整个上午都沉浸在这种喜悦当中,当然是无心复习了。
下午整理了一下文档和模型文件,由于我一直都维护着一个私有的仓库,所以只要把private变成public就好了,剩下的整理也不怎么花费时间了。无数次想要放弃,但都是由于某种机缘巧合坚持了下来,这对我来说也是一种莫大的鼓励。
主要是总结一些失败的尝试,为以后积累经验。
2021 MAXP 命题赛 任务一:基于DGL的图机器学习任务
整体是Scalable GNN,内核是GAMLP。对于大规模图,可扩展性较好,训练的速度比较快,显存占用很低。
baseline给的jupyter文件,主要是构造DGL格式的图(节点列表、边列表以及节点特征)。
GAMLP(Graph Attention Multi-Layer Perceptron):https://arxiv.org/abs/2108.10097
每一折的输出都进行C&S:https://arxiv.org/abs/2010.13993
总共集成了不同初始化的20个模型,最后进行Soft-Voting。
Method | Test Score |
---|---|
GAMLP (leaky-relu, 9 hops, 8-fold) + node2vec + C&S (DAD, AD) + Model Merge (+GAMLP_8fold_seed_{0-2}) | 49.7822086481499 |
GAMLP (leaky-relu, 9 hops, 8-fold) + C&S (DAD, AD) + Model Merge (+GAMLP_8fold_seed_{0-2}) | 49.7923833548815 |
GAMLP (leaky-relu, 9 hops, 8-fold) + C&S (DAD, AD) + Model Merge (+GAMLP_8fold_seed_{0}) | 49.7767704428278 |
Date | Method | Valid Score |
---|---|---|
2021-11-17 | GAMLP (leaky-relu, 9 hops, 8-fold) + node2vec + C&S (DAD, AD) + Model Merge (+GAMLP_8fold_seed_{0-1}) | 55.53829808307 |
2021-11-13 | GAMLP (leaky-relu, 9 hops) + node2vec + C&S (DAD, AD) + Model Merge (+GAMLP_seed_{0-9}) + lr adjustment | 55.5081350224647 |
2021-11-08 | GAMLP (leaky-relu) + node2vec + C&S + Model Merge (+GAMLP_seed_{0-9}) | 55.3825306357653 |
2021-11-06 | GAMLP + node2vec + C&S + Model Merge (+GAMLP_seed_{0-9}) | 55.3694749826675 |
2021-11-05 | GAMLP + node2vec + C&S + Model Merge (+GAMLP_seed_1, +SAGN_SE_stage_0, +SAGN_SE_stage_2, +SAGE, +GAT) | 55.0993580220235 |
2021-11-04 | GAMLP + node2vec + C&S + Model Merge (+SAGN-SE, +SAGE, +GAT) | 55.0070680604702 |
2021-10-31 | SAGN + node2vec + SE + Model Merge (+GAT, +SAGE) + C&S | 54.5420166932282 |
2021-10-24 | GAT + node2vec + FLAG + C&S + Model Merge (+SAGE, +GCN) | 54.2394856973069 |
2021-10-22 | GAT + node2vec + FLAG + C&S | 53.9846753644328 |
… | … | … |
2021-10-18 | GAT+res+bn+dropout+train_label的特征增强+FLAG +增加训练epoch(5–10) | 52.95 |
2021-10-17 | GAT+res+bn+dropout+train_label的特征增强+FLAG +inception(已注释) | 小于52.53 |
2021-10-15 | GAT+res+bn+dropout+train_label的特征增强 +FLAG | 52.53 |
2021-10-15 | GAT+res+bn+dropout +train_label的特征增强 | 51.27 |
2021-10-14 | GAT+ res+bn+dropout,调整了采样策略 | 50.79 |
2021-10-13 | GraphSAGE +一些tricks | 48.48 |
2021-10-13 | GraphSAGE(baseline) | 48.14 |
方法 | 效果 |
---|---|
node2vec embedding | 一般来说是有用的 |
GAT embedding | 试过了,没什么用 |
SGC embedding | 试过了,没什么用 |
FLAG | 模型效果差的时候有一点效果,大约有5k,但是一个epoch的时间会变慢 |
残差连接,batch norm,dropout | 几乎是必须加的 |
inception | papers100M上好像有点用,这边没什么用 |
label usage | 有点用,但是用C&S就不要用这个了 |
C&S | 有用,传播残差,不过超参数要好好调调 |
norm adj. of GAT | 没什么用 |
self-kd | 前排大佬说有点用 |
SAGN里面的SLE | 没用,因为这里面test没给标签,标签太少,label model聚合不到什么有用的信息 |
GAMLP里面的RLU | 没用,因为这里面test没给标签,标签太少,label model聚合不到什么有用的信息 |
ogbn-papers100M预训练 | 没用(没用什么其他策略,只是finetune),主要是因为特征的生成方式、输入和输出维度不同,而且两个数据集的训练节点规模差不多 |
模型融合 | 不同随机种子的模型融合是有用的 |
k折交叉验证 | 配合模型融合是有用的 |
伪标签 | 自己没试过,前排大佬说有点用 |
self-train | 有点用,但不大 |
… | … |
有个大佬对前排的方案进行了汇总:https://github.com/CYBruce/MAXP-DGL-solutions
比赛答辩视频:https://www.bilibili.com/video/BV1fr4y1v737?p=2
rank2大佬的代码:https://github.com/langgege-cqu/maxp_dgl