一次结束,总归要写个回忆
一是以后写进简历的项目经历可以复习
二是对过去一个多月的时间的交代
(可能会较啰嗦吧, 穿插着回忆)
比赛前
一个多月前,组内大佬问我要不要参加这次CCF大赛,我确实不想参加,因为当时的我,申请没搞定,文书没搞定,套磁一个offer都没有,回信都没有。
都要失学了,做什么比赛啊。可是组内成员几乎都参赛了,组会都已经变成竞赛讨论的主题。
我也在想,我进实验室这一年,除了写写爬虫,混混经历,还收获了什么…
没做过比赛,没出过论文,以后想读博,现在做比赛有啥用,即便被大佬带着拿奖,能给cv上增添什么,来得及么?
但是总还是萌生了一个念头,是啊,以后真读博也没什么机会做比赛了。
当时进实验室的原因不还是听说实验室有大牛,做天池特别厉害,想有些漂亮的竞赛经历,然后好找工作么…
既然曾经憧憬过,既然以后没有机会再做,那就趁着这如火如荼的两个月,过一次瘾吧。
涛哥当时给我们开会,一个比赛一个比赛的分析,说到商铺预测的时候,说这个有点难,但是还是鼓励新人来做这个,我就信了这句话……就只报了这一个比赛。
baseline
当时我还在JIE, 完全远离数据挖掘组的所有人,开始的前两天,马璐李友出了一个baseline, 一个很基础的baseline, 准确率只有69,我收到代码之后仔细读了两天,尝试理解每个函数的思路
大概思路就是:
- 二分类
- 用经纬度距离特征K个最近的shop构建负样本(负样本的其他信息与正样本相同)
- 主要特征是shop内的wifi信息和user的wifi信息的交互特征
前面基本没做什么事情,除了加了一些无关紧要的wifi特征,增加训练轮数等等,做到了75,但是听说203这边海城他们已经做到了87,用的是基于bayes的生成模型,主要是用规则做。(说实话,我当时对规则和模型的理解都很模糊,后来才慢慢了解到,相比于说规则就是类似一条条的if语句,更明显的区别在于,规则没有明显的训练过程,没有说拿着label来修正模型的过程)
理解leakage
插一个话题,当时的我还一直不明白一个事情:leakage
怎么说,有那么一点意思,但是又说不清楚。
我始终不太理解为什么给了8月一个月的训练数据,baseline要用前两周的历史记录做特征,后两周来做训练集,我总觉得这样是浪费,这个问题结合滑窗问题一起形成的困扰,我的理解大概经历了如下几个重要的时刻:
- 我当时问垚明,因为只有他在406,他就告诉我:你构造测试集的特征需要用到训练集的历史数据吗?如果需要,为了模拟这个过程,你构造训练集特征,也要留一部分数据单纯做特征。
- 有天晚上,wepon大神在Tianchi直播,讲的是o2o优惠券那一题他们夺冠的思路和答疑,我们组大部分人都去看了,他在那里提及了一个当时他们用到的leakage的特征。主要思路就是“穿越”,就是用到未来数据体现出来的一些(基于时序的)历史规律构造特征。
- 到复赛的时候重回二分类,我和金杨就这个问题讨论了很久,又加上涛哥比赛结束前那次大组会上的分析,我们决定,也认可我们之前的思路:
初赛:
数据:8月数据+9月前两周数据(Online 测试,不开放)
训练集历史数据feature: 8月前两周
训练集:8月后两周
测试集历史数据feature:8月后两周
测试集:9月前两周
复赛:
数据:7,8月数据+9月前两周(Online 测试,不开放)
训练集历史数据feature: 7月数据
训练集:8月数据
测试集历史数据feature:8月数据
测试集:9月前两周
这样的原因是:
等时间段划分,数据体量同步,历史特征如 xxx出现次数 等这些可以直接用。(当然我觉得如果处理一下类似归一化的操作,应该也可以做不等长的划窗,但是我还没有尝试过)
这里回头看了一下其他队分的数据,回头一看,区间都划分错了,应该是:
这里确实太不应该了,太粗心了!
然而关于leakage, 我暂时觉得垚明的那句话已经很有用了。
海城的Bayes
(其实也可能是我自己理解的Bayes,没有海城那么透彻)
组会的时候,海城给我们讲了一下大概的思路,说实话当时我还是似懂非懂。
只是当他提到觉得这个问题有点像生成模型的时候,我有点捶胸顿足:哎呦我操,前两天我不也刚刚看到过Andrew Ng讲的朴素贝叶斯么,不也刚刚看到生成模型和判别模型的区别么,我咋就没想到…
一条记录出现了一些wifi, 我要判定他在哪个shop里,还真的有点像垃圾邮件分类
简化的思路就如下:
加上强度分级,平滑等一些处理,当时他能够达到不错的结果。
麦芽的开源
正当我们一股脑的想如何通过Bayes达到海城的效果时,比赛的qq群惊现一份简洁的代码,有大神开源了,最少也能9072,结果第二天看榜,清一色的9072,当然也有很多苦苦挣扎进排行榜的人一下子被这份开源代码冲了下去。这件事还引起了一个小波动,但是不是重点。重点是这份代码
无比简介而直接的思路,七八十行代码,秒杀绝大多数苦苦做二分类,用规则提特征的参赛者。思路如下:
- 分mall训练, 训练多个多分类模型。(这一步一下子缩小了数据集的体量,也为后面的简单粗暴的特征工程做了铺垫)
- wifi one-hot。(可能每个参赛者都知道wifi是最重要的特征了,如何完整的表征这个信息,可能是特征工程的最重要的工作。然而由于麦芽大神之前分mall的结果,每个mall中所出现的wifi的id个数多了也就上千个,直接one-hot,再用强度来作为one-hot值,wifi信息可以说表征的十分完备,当然麦芽也做了一些wifi的筛选,即出现次数小于20的wifi直接忽略)
两步思路,多分类,如此优秀的效果
对于我一个萌新来说,简直惊呆了,实在是佩服简洁的思路
(后来复赛的时候想到如何离散化特征的时候,回想起这个粗暴的“离散化”,佩服之情又是增添一分)
而且这个baseline,没有用到先验知识(历史特征),所以不用考虑leakage
的问题。
候选集
开源代码出来之后,我们都有些迷茫,之前一直在做二分类呀,这突然出了一个多分类,还比我们的二分结果都要好;
群里也有人说,马璐大佬也说到,可以将他的结果做候选集,然后再每个候选样本标0,1的label,然后做二分。
(说来惭愧,我当时也不会算覆盖率)
所以初步思路就是,取多分类每条记录的softmax概率的top_10的shop作为该记录的候选集,为真实值的label为1,其他的为负样本,label为0
那么既然是候选集,就要提高覆盖率,覆盖率就是之后二分的上限;因为你的候选集里至少要有正确的答案吧,如果没有,怎么分都分不到的。
(当然这一点,我们后来做复赛的时候,也讨论过,要不要把所有的正确答案union到候选集里去,这样训练集效果可能会很好,但是测试集就难说了)
所以马璐大佬给我的工作就是,想方设法加特征,提高多分类的效果,提高覆盖率。
我继续做,做到了916,提不上去了,初赛也快结束了。
组队
这可能是个长长的话题。
比赛开始前,一个本科同学(现在研一)找我组队,我当时还没决定要做,就推了,然后他们几个(都是我的本科同学)自己组了一支队 (中东F4)参赛。后来我还是参赛了。
不同于他们一开始就组队,我们实验室都是先单干,然后只是组队的时候融合一些思路和结果。当然对于我们这种新人渣渣来说,就没什么显著的贡献了…
组队的过程,就不细说了…
反正最后,实验室的几个大佬(涛哥,马璐,海城,李友,还有一个北邮大神)组到了一队,我们几个(我,锦进,金杨,郭晓,还有已经毕业的大佬春振)组到了一队。
其实那晚讨论组队的时候,无论是组对结果,还是涛哥说以后尽量小组内部讨论,可能我们几个新人心里都有点不舒服吧…
因为之前总想着要被大佬带嘛,这只剩下我们几个新人了,春振是大佬,但是毕竟能投入的时间和精力太有限了(复赛也确实,都是我们把问题抽象出一个很通用的模型去问他,他给我们解答,不过有一些解答确实很关键,也很感谢春振大佬)。
剩下我们4个,都是第一次打比赛。
当时郭晓要准备雅思,金杨还不怎么熟悉,锦进也有另一个比赛要忙,我确实很难受。
有天晚上跟郭晓聊天,她说锦进的主要精力应该要放在另一个比赛上了…
我又想到那几天, 天天用cpu跑一次多分类(加了一堆特征之后)要跑24h以上,而大佬们可以有gpu轻松完成的时候,说实话,很不开心,真的很不开心…
可是没办法呀,也不能怪谁吧…CCF这次对实验室也很重要,如果有更好的资源,在这种需要出成绩的时候,当然是要给更优秀的队伍来用呀。
我当时就安慰自己,有问题还是可以问大佬们的呀,马璐大佬去年的时候不也是自己做,而且做到了很好的成绩吗?提起马璐大佬,真的很感谢…初赛帮了我太多,而且复赛的时候,一队中我也只问过他问题,也总是很热心的解答,虽然中间我有次不小心八卦出了他的恋情…
但是总有一种被所有人都抛弃的感觉,我当时就跟郭晓说,就算感觉被所有人抛弃,还是有一种要证明给别人看的冲动。
进入复赛
进了复赛,就要用数加平台了,完全没接触过呀。听说要写sql?还说要用java的udf? 哇真的一脸懵逼,什么鬼,前几天都是在打酱油中度过。
我们最先的思路就是复现麦芽的多分类。但是遇到的第一个问题就是sql比起python太不方便了,用sql去构造wifi的onehot特征太麻烦了…
而且平台上的sql(订制版本)上不支持循环语句,udf也不能做sql语句的循环(也有可能可以,只是我们不会?),复赛有487个mall,也就是我们要在线下生成487段代码进去,然后再融合结果。
而由于当时没有更好的思路,我们只能硬着头皮这样做,但是开始限制资源的时候,我们发现多分类可能不是一个好的思路,复现难度太大。(当时麦芽初赛开源的时候,就有人调侃说,这是把我们往歪路上引,看来有点道理啊哈哈),所以还是传统的方法,候选集,二分类。
三个人的努力
郭晓强势回归后不久,我们确立了二分类的思路,我,金杨,郭晓三个人就成了我们队里做主要工作的三个人。
郭晓代码写的很快,而且总是:要不我来做这个;我现在又闲着没事做了;…
金杨的思路很清晰,代码实现能力也很强,一些udf, 也有很多sql的操作都是他完成的
我…就划划水,,想一些做一些二分特征,然后和金杨讨论一下模型(主要是怎么用数据更好这些偏理论的东西),然后如果有什么错的时候,debug一下…(btw, 我真的是发现coding能力下降了好多…果然还是要多写码)
前期候选集主要是两位大佬在做,我一直在想有没有更好的二分特征做进去;后期大家都做好的时候,资源也比较紧缺,都是商量着来了,三个人的工作就比较均匀…
话说有次通知203要停电,我们仨还一起抱着电脑跑到406做了一天~
总之跟这两个人一起做比赛,总觉得心里有种劲儿在暗暗的push吧,每个人都希望自己能做的事情多一点,每个人都不希望拖后腿,有任务都是抢着做(郭晓都跟我抢过很多次),这是这次比赛最愉悦的体验了。
当然在复赛过程中,春振大佬的理论指导,锦进的两次“送温暖”都起到的至关重要的作用,不然光靠我们仨瞎折腾,,,复赛可能现在还在72呢。
模型的思考
中间又一次,我和金杨讨论到,我问模型到底代表了什么。
对我来说,模型还是一个很模糊的概念
对于这次比赛,
**无论是二分,还是多分,xgb都是一个集成树模型,本质上还是决策树
那么我想,对于决策树来说,模型就在于这颗决策树有几棵,有几层,在什么地方分裂
那么我们训练的过程,也就是调整最优分裂点的过程(因为树的个数,层数这些应该是模型参数给定了)
那么有label的数据教会我们的,就是这列特征,我这样分得到的效果好不好(同一label的样本数据是否最大程度分到一起去)**
离散化
复赛的最后阶段,我提出一个一直很困扰我的问题(因为我之前想要用wifi的bssid做几列特征(算是不能one hot的一个补偿),但是bssid种类太多,这样效果肯定不好,就顺势在思考队友在用bayes的概率做特征的效果)我当时觉得,树模型处理类别特征应该效果会更好,这种概率数值特征,会不会让树模型分类很困难呢?
答案是不会。。。
后来了解到离散化不是我这样用的,,,
https://www.zhihu.com/question/31989952
看了上面的链接,和大佬的讲解,还是用自己的话说一遍吧
**1. 离散化是为了防止过拟合,分成几个区间,相当于缩短了这列特征之间的相对距离,一些离群点就不会被过拟合
2. 如果不是类似get_dummys这种one hot 的离散化,一定会损失一些信息
3. 常见的两种情况: 简单模型+海量离散特征,复杂模型+少量连续特征**
既然xgboost会对特征预排序,所以概率特征直接输进去,是没有什么问题的。
所以最后阶段我们对所有特征的离散化,其实是几乎没有用的操作
那么为什么wifi分强度区间的效果会比把强度值直接做权重效果好呢?
这应该是我们应用的方法不对…
贝叶斯的再理解
中间还有一个问题,我当时在考虑,bayes的概率做特征,左边是一堆wifi概率的连乘再乘先验概率得到一个数值,这里如果这列特征中,完全不同的wifi列表,各自连乘的结果处理之后可能得到相同的概率特征,这,模型该怎么区分呢?
问了一下马璐大佬,自己反思了一下,得出如下的结论:
1. 这种情况确实很少
2. 如果单纯用这列特征,可能效果确实不好
3. 不妨想想,这概率是怎么得到的?既然前期是在用生成模型,这个概率得到的就是一条记录分到某个shop的概率。或者说,这个操作,是先得到在某个shop的概率,再来做特征,而不是通过这个概率做特征,得到了这条记录属于哪个shop。类似于是把生成模型当作判别模型来做?这样一想,单纯用这个概率做特征,确实不是一个好的选择。
wepon的参数
复赛末期,我们也想到,如何调参,因为几十维特征,几千万条数据,但是500轮左右就收敛,但是收敛的效果不好。
金杨索性直接拿wepon在o2o的源码的参数过来试下(因为他也是几十维特征),效果确实好点。
话说刚刚看到参数,我们都吓到了,3800轮,但是样本采样率0.4
想想这样也不失为一个好的选择。样本很多,或者特征很多的时候
其实可以降低样本采样率或者特征采样率,增大轮数来训练,也是一个调参的思路。
平台的折磨 & 最后的挣扎
我们最初以为,数加平台的最大制约是不能用python,只能用sql和PAI命令,然而没想到,限制资源才是最大的制约。想想我们浪费了前期那么多天,资源一点都没用,后期却一直在排队,真的很可惜,很可惜。
最后一天的时候,我们把三个人的所有特征加进去训练,希望能够提升效果,但是,训练完之后,没资源了
也就是没法预测测试集的结果了…
心灰意冷,垂头丧气,互相安慰,准备结束
然而感觉命运对自己还是挺好的,真的会柳暗花明,中午突然通知会补发一些资源(因为大部分人都是这样,没能量预测,可能是平台出了什么问题),我们死灰一样的心又振奋起来,于是我们三个又赶紧预测结果,(但是因为中间有个小插曲,由于郭总的一个失误,测试集的候选集构建错了,于是我们又匆忙的重新构建候选集,重新插特征,不过还好不用重新训练)终于赶在13:59:42得到预测表,赶上了14:00的评测,比原来提高了2个百分点。
那天下午,240w的线上测试集,我们中午交的那一版还有7w多的空值,我们急忙商量如何填充,又是一个紧张的下午,我们最终决定要用每条有空值记录最近的5个shop中,候选集概率最大的那个shop填充。
填充完毕之后,提升了3个千分点,比赛结束。
对自己的反思
自己的感受,代码能力确实弱了很多
我们一起讨论的时候,郭晓说:我们总是讨论的太多,做的太少
我回来反思自己,太注重通用的理论讨论了,而忽视了实践,或者是懒于去实践…
中间我还想过一个问题:可解释性和效果,到底应该哪个在前?
尽快有点不情愿接受,现在的趋势就是试出好的效果,再去寻找可解释性,数据挖掘,更应该是挖掘可解释性,
这也算是自己想法的一点点改变吧。
至于以后会不会再做比赛?
这次没拿奖,肯定是不甘心的;但是马上的实习生活也还是会有很重要的科研任务…
未来的事,就交给未来吧。
不过说实话,真的想挣点天池粮票,换件天池的衣服呀~~~