忙活了一个月的数据挖掘邀请赛上周终于结束了,今天终于得闲,从参赛者的角度,写篇总结,也算是梳理下一个月来的思路,好放进记忆中保存。
先自我介绍吧,我是本科生组的OnlyMe,今年6月份就毕业了。咋知道这个比赛的呢?大概去年下半年吧,订阅了xlvector,不周山,yihui,cloud_wei,等几位的博客,自己也常用豆瓣,就知道了“推荐系统”这样东西,有一点儿兴趣也就有些关注。最早看见比赛的消息是豆瓣上的友邻发布的,那时数据还没开放下载。
为啥决定参加呢?最主要应该是因为比赛的数据有意思,问题很有趣,加之我的毕业设计很无趣...
看我的ID应该可以猜到我是自己一个人一队。因为之后要读研,我挪到了新导师这里来毕设,所以一直没在自己学校。新环境里还不认识什么人,大多数还都是生物背景的研究僧,而我还是很想呆着本科生组的额...之前也不想“单刀”考虑过和学校之前团队老搭档们合作,但杯了个催的我工位在个事业单位晚上没处给我上网!而且远程合作沟通成本很高(不光是钱),各种效率低。考虑到我能管理好的也就是我自己了,而且之前有过独自参赛的经历,比较擅长控制自己的情绪起伏,就决定自己先试一试(而且一直木有让导师知道...嘿嘿)
正式开始像回事儿弄是从3月29号(虽然22号就有数据了可我毕竟还是得弄弄毕设不是?),那会排行榜上已经有0.22+了,之前没参加过这样动态排名的比赛,看着榜感觉鸭梨好大的...我没有统计学的基础(本科专业是BME),机器学习的各种算法也是只闻其名。基础很差的直接表现就是自己都深知面对数据自己缺乏基本的条件反射。所以在对数据动手之前,先翻了翻书和文献(带过来了一本《数据挖掘》和《集体智慧编程》,又拿Collaborative Filtering,Ranking Function,NDCG,statistic model,recommend system,online dating等几个关键字搜了些被引用颇多的论文和wiki),给自己简单入了个门,好歹知道了item-based/user-based filtering是在说啥,KNN,SVM,决策树这些方法是想干啥。这期间看的很多东西我知道自己可能比赛用不上,但我想它们至少可以帮我打开思路。因为毕设的缘故装了weka,看以上论文期间我用它观察了下提供的数据,隐隐感觉到“在没有弄清楚数据本身的含义及如何转换成更易于理解的形式之前,轻率地使用提供给我们的数据是错误的”(出现在《集体智慧编程》第201页)。受之前推荐系统论坛上一句“算法只占10%”(当然我知道这里面还有上下文)的影响,我从一开始到结束都没有把算法放在最关键的那一环(不知是明智还是败笔)。特别是这种对人的兴趣进行建模的问题,我感觉:如何去理解行为数据背后的驱动力,你所做的尝试基于怎样的假设,怎样一步步细化定义这个问题,是在选择任何算法前应该明确的。算法是为需求服务的。我不可能把整个数据直接丢给一个看似很玄妙的算法期望它完美解决问题,毕竟,计算机不会帮我们思考...
数据清理花了一周(尽情来BS我吧,说了我基础很差...),语言全程用的都是Matlab(跟R和python都还不熟,不过姐接下来会学的...),程序全是用我自己笔记本跑的(散热极差,稍微热一点儿就自动断电,所以外加的散热器呼呼呼呼转了一个月,我的本,你辛苦了...有好机器或者用服务器的童鞋,求你们不要来得瑟)。说实话,Matlab确实有些慢的,所以事先可以考虑到会耗时的问题都做了些提速的优化(比如用profiler,尽量使用Matlab本身提供的向量化函数,把会员的id转换为我自己保存的数据矩阵的索引等等)。没有把数据导入任何数据库(也是因为不熟),数据观察用过weka,不过大部分时候我都是盯着文本文件看,然后琢磨...(囧)然后我记得是在4月6号,我初步构造好了数据集和一些文件,清楚了哪些数据大概用得上大概用不上,写出了可以产生个随机排序结果的程序了。
这期间有两个插曲。一个是Matlab自带的sort函数,可以输出原序列排序后的index,但是做个简单的测试你就会发现它不对,你要真用,坑死你~。二一个是在我第一次尝试提交结果的时候(开始没用train在本地测,兴奋地...),系统说我文件里“没整数”!姐全篇都是整数你说“没整数”!适逢那天实验室网络瘫痪,我找不出为什么郁闷地暴走网吧还连吃了5个大桔子...不过好在当天下午就自己发现了,是因为每行末都有个空格,删掉就没事了(看到当天论坛里也有发问的童鞋遇到过这个问题的)。因为是第一次提交,我盯着那个0.08数量级的结果,都呵呵呵傻笑了半天。这是一次印象比较深刻的情绪波动,我做比赛还有个目的就是想治治自己心理素质差的毛病~那天还看了一句话:女人的一生,无非是和自己的情绪搏斗,打赢了就赢一生,打输了就苦一生。
关于具体的解决方案,我开始是这么想的:对用户的性质进行分类,分出来哪些是真想找的,哪些只是围观的哪些是刚注册冷启动的,对不同类的人给予不同推荐策略(基于popularity还是基于match的程度),后来发现train和test里的人基本都是真想找的(因为有click和msg行为而且岁数好多不小了呀),而且user_B里的人基本上也都满足择偶条件,我之前的盘算就不靠谱了。恰逢windwail在论坛里说折腾profile没多大意思,所以将注意力转移到行为数据上。当时我给自己比赛的目标定在0.2+,又恰逢论坛里有人说了那个(click+msg)/rec_time的公式,我就“可耻地”试了下,这样轻轻松松站上了0.21,心里自然不会满足。然后就思忖如何将popularity反演得更加精确。把rec_time替换为use_B总共被推荐给过少人(因为click次数和msg次数是按人头计算的),这样达到了0.22。考虑到用户收到推荐列表后看到头像同时会看到年龄,所以针对train中年龄分布做了统计(分开男女),统计每个年龄的用户所点击用户的年龄分布,制成两张80*80的年龄点击频度表作为概率,结合popularity对user_B进行排序,调好参数结果可以到0.229(如果问我的整个“算法”是什么,可能也就这里有一点儿协作过滤的思想了)。
这期间的插曲也有两个:站上0.228后排名到了16,心里超级开心就给老妈打了个电话告知了她比赛一事。她老人家说那你发链接来吧我看看,我就发了。她参与进来的结果就是杯了个催的第二天一早,我刚踏入食堂,短信响,俺娘说:你掉到第25了(那天是4月17,很多人爆发的一天)。上午我改了下到了0.229,中午也是刚踏进食堂,又是短信响,俺娘说:你又变了...介不四给俺鸭梨么!我感觉短时间不易突破,就强迫自己不再去看那个排行榜了(只会退瞅它干毛...),天知道10天内俺娘刷新那个网页几百回...还一个插曲是早一些时候,我感觉实在没有“算法”参与太不像回事,就用对男女用户分别挑了几个特征(女:年龄,学历,收入,头像,pop值;男:年龄,学历,头像,房子,车子,收入,pop值)进行了聚类(用的EM算法),分别聚了10类,对每个人做了类号标记,然后对发生点击行为的双方类号进行分布统计(即比如女1类点击男2类的概率为0.2,男5类的概率为0.4等等这个意思),结果发现,还确实是有规律的!那天因为是第一次用聚类,还发现真的有规律,感慨算法的神奇,兴奋得半宿没睡着...脑子不停地转,想下一步怎么办。这一个月,确实是我脑子不停地噗噗冒想法的一个月,我很陶醉于这种感觉,有时疲劳,却很兴奋,一直在想如果我试一下某某方法,会有规律没,起床都有劲儿了。我的新专业生物信息学现在是一个由假设驱动的科学,我还没体验过,而这个比赛很给我一种“假设驱动”的感觉。而因为只是我一个人,我所有的假设都需要自己去准备数据集,自己去写些脚本验证假设。我之前是一个行动力比较差的人(可能因为天秤座吧...),这个过程让我一定程度上突破了自我,我发现逼着自己去面对问题,踏踏实实写程序,有的假设我自己居然一个小时或者一个上午就可以把数据做出来验证,是件非常开心的事情!
之后很多的尝试在结果上都没有提高,还有可能是ensemble做得不好,也许会有效但我没有深入尝试就放弃转到新想法上了。比如我做了很多正负样本的比较(我不知道是不是可以这样叫,我把click双方看做positive pair,而被rec却没有进一步行为的叫negative pair,排除掉完全没有click的round,因为那些round很可能A就没有看推荐列表,所以不认为那些B是真的negative...),比如我同样对negative pair做了类分布统计,被positive pair得到的table除,因为聚类过程得到的各类分布不均(即比方说因为1类占的百分比大,被点的概率就大),所以这样相除,相当于做了normalization。尝试还有,比方说:如果大规模人群的择偶偏好不易捕捉,那么特殊人群会不会容易呢?我就将离异和丧偶的人单独挑出来(他们在test里还是占一定比例的),看他们的行为数据是否特别,结果花现其实跟普通大众差不多,sigh...还有那些没有头像却被点击很多次的有神马特点啦(经猜测,应该是取了诱人的昵称),记得那段时间,我说的最多的一句话就是:没有规律...那段时间养成了一个坏毛病,心情低落了,就去买好吃的...心情明媚了,就去买好吃的...曾经连续四天每天一包泡椒鸡爪...比赛完了之后第三天就看见校内有人分享《泡椒鸡爪吃不得...》,我倒...
接下来要说说让我最伤心的一点。我把train上每个pair里user_B给user_A的推荐次数统计出来了(就是对应test里的第三列),观察发现推荐次数为1的不太容易获得click,这一点非常明显。所以我才用了自己命名的“滤1法”(让他们怎样都进不了前20),那天晚上在train上,我看到了NDCG@10=0.5+,之前说了晚上没有网,所以没传上去在test里测。虽然我知道这肯定出现过度拟合了,但我觉着怎么着在测试集上也会有提升(bootstrap说了,test是从整个数据里切出来的嘛),心里特高兴,连着看了7集《尼基塔》...怀揣着第二天能进前10的梦想幸福地睡去...结果第二天,不升反降!心里那叫一个不甘哇,相信试图在推荐次数上做文章的童鞋应该都见过忽悠人的0.5+吧,sigh...这件事对俺杀伤力之大,让我最终都不甘心放弃对推荐次数做手脚...
之后没有什么价值很大的尝试了,最终的版本也就是popularity、age distribution、rec_time三者的组合,调参数。继续播放插曲:因为这次比赛,特意注册了世纪佳缘,以一名用户的身份体验推荐,发现里面有些男人,确实还蛮不错嗒,灭哈哈。截止前一天(也就是26,正好我排26)晚,俺娘call来问我:还在忙?我:嗯。我娘说俺爹觉得,我也就这样了,差不多了。让我很不爱听...第二天很幸运,调参数出现了0.239,呀呼嘿嘿咿呼呀嘿,我爹诧异了。长时间没有起色的时候心里总是很后悔,排16的时候没有像bootstrap说的一样,“高处留名,截图纪念”,可能再也见不到了,所以在最后一天0.238的时候赶紧截了个15,存了下来,因为我隐隐感觉到,算上过度拟合的因素,我最终很可能就是这个成绩了。
想说的还有,一个人做比赛,真的有点孤单,因为有的时候确实很想有个人说说话。我不知道是啥鼓励着我坚持到最后一天,坚持到截止前最后一分钟。很多个傍晚,我漫无目的地走了很远很远的路,心里在想:要不就这样了,算了吧。但没到走回寝室,我心里又会多出两个想要试一试的想法,就一直没丢,现在想想居然有些感动。记得前几天《程序员》上讨论API的专题上有过这样一句话:需要不断改进的事情算不上什么坏事,只是对现实的一种坦诚。比赛期间还读到过萨缪尔-贝克特的一句诗:没事。再试。再输。输好看点。(No matter. Try again. Fail again. Fail better.)。这两句,让我基本保持了淡定的情绪,也给了我很大的动力。
最后一天看到很多人(特别是研究生组)上演了最后的疯狂,甚至在最后几分钟做出了自己最好的结果。我很欣慰自己和大家一起,经历了这最后激动人心的战斗,很为你们感到高兴。那个18号就做出0.25+却不公布的队,你们真是淡定真是有强大内心。每次看到排行榜末尾专心致力于做出更小结果的队伍,我都会笑得很开心,咱web2.0时代每个人的口味真是不一样哦。是不是也正是由于他们的存在,我们的推荐系统要面临更大的挑战呐?
刚得知最后排名的数字时,我是比较平静的,比预期还好了那么一点点。但是看到主办方把学校列出来后,我发现我居然是我们学校唯一冒泡的一个。没有得意,心里很凄凉。太多人跟我说:你呀,可以了,不错了。但我一下子觉得很后悔很自责,我在想如果自己再给力一点,是不是结果会好一点儿给自己母校争个脸(有些想法确实在最后几天觉得时间不够驾驭不了放弃了,比如论坛上有人提过的SVD),眼泪刷刷流了一脸。毕业季有些地方总会一碰就疼,每个人都觉得自己学校挺好的吧,四年了,自己都没能做一件让学校因为我骄傲的事情,个人确实比较伤心,因为马上没机会了...
最后,真的要狠狠感谢主办方,感谢你们组织了这么有意思的一次比赛,让我拥有了这一段难忘的心路历程,很开心。我知道在整个比赛期间,很多人付出了无比艰辛的劳动,比如热心的bootstrap,cloud_wei,还有我不知名的工作人员们,帮着摇旗呐喊的童鞋们,感谢你们!
听说最后获奖的论文会公布,我现在无比期待中~~
有三点我很好奇,一个是大家是如何做模型混合的?具体来说是如何调节每个模型输出的数量级,又是如何决定模型间的主次优先级的?
还一个是:有没有对于profile比较简洁合理的编码方式,能较容易计算(比如位运算,同或异或啥的)每个指标落在给定区间与否或偏移距离,这样查找近邻或match making都比较快一点?想法来自于能把一幅图像编成一个数字的算术编码,我在想对profile属性是不是也有?
再一个,对于推荐列表很长的(即一个user_A对应大几百个user_B),有没有人试过对前几十进行精排?分类面又是怎么找的?想法来自google对第一页结果的优化...
希望能看到其他人的分享帖,吐水也好,菜鸟抛块砖先。耐心看完的童鞋,辛苦啦,笑过没?[s:11]