@袁全V 的如下评价是个很好的建议。
—–
@袁全V:如果喜欢数据占大多数,只选”喜欢”数据,用recall或ndcg当metric也可以,没必要去套rmse. ID没有匿名化是硬伤
—–
我也收到了今晚看啥的来信。
—–
@汪冠春:看了你的分析和建议,很细致。我们在出题准备数据的时候有了一些不科学之处,望理解。明天我会和百度学院的朋友确认是否会对比赛有影响并确认处理方法。
—–
从公开数据集、办比赛来讲,这次这种形式我是非常支持的。也期待有更多的数据能够公开出来方便大家学习。办比赛确实有难度,要考虑的很多。虽然这次有很多问题,但以后总会做的更好。也期待百度今后继续办这样的活动,给我们提供从实践中学习的机会。
百度已经调整了比赛规则和数据,新的数据集简要分析请见 http://diaorui.net/?p=510
摘要
本文主要通过一系列数据上的探索,论证百度电影推荐系统算法大赛的数据集并非真实。旨在让做相关研究的人不要直接去使用这组不真实的数据去检验自己的算法,以免误导。本文并非只是为了批评,文中也指出了若干条建议,希望此次比赛主办方以及希望举办类似比赛的人参考。
文中纯属个人观点,如有错误,请不吝赐教。
相关链接
大赛活动主页:http://openresearch.baidu.com/topic/40.jspx
任务描述(摘自大赛网站)
本次创新大赛的任务是基于喜好和好友的电影推荐引擎,根据提供的包括关于用户的观看和评分记录数据,以及用户的社交关系数据,利用这些数据实现一个完整的个性化推荐算法,帮助用户发现可能喜欢的电影。
本次大赛数据集包含了14,000部电影对应的标签信息, 用户观看评分记录(User_id, Movie_id, Ratings)以及14,0000用户的社交关系,引入的社交元素,主要是微博好友关系,以及名人推荐;这些数据在保护用户隐私前提下,大部分都是明文,方便参赛者理解,更好的提升算法效果。Rating打分的范围是0-5分。分数越高,视为用户越喜欢这个电影。选手利用给定的数据来预测用户对电影的打分情况,帮助用户发现可能会喜欢的电影。
产生怀疑
我对数据的质疑萌生于SVD模型的“失效”。分数范围为0-5分,几个简单的预测可以得到如下分数:
策略 | RMSE |
---|---|
全部预测4分 | 约0.57 |
使用用户平均分和电影平均分加权 | 约0.53 |
使用最基本的SVD模型 | 约0.52 |
这个和我预期非常不符。全部预测4分误差过小,而SVD模型只有非常微弱的改进。所以我开始怀疑数据。
但是这个毕竟只是怀疑,然后我又通过绘制log-log图来进一步观察数据分布。
这里所谓log-log图是指,横轴为电影个数取对数,纵轴为给这么多电影打过分数的人有多少个取对数。
使用2011和2012年的KDD Cup数据集,绘制log-log图都会得到一条直线。这是所谓齐夫定律(Zipf’s law, 参考维基百科 http://en.wikipedia.org/wiki/Zipf%27s_law )。log-log曲线为一条直线目前仍是一个经验定律。我还不能从理论上解释原因,所以只能通过举例说明。
下图是根据KDD Cup 2011 Track 2 (雅虎音乐推荐)的数据绘制,每个点表示给x首歌打分的人有y个(给20首歌打分的人约有6000个)。坐标轴上的数值标的是取log前的数值。
下图是根据KDD Cup 2012 Track 2 (广告点击率预测)的数据绘制,每个点表示搜了x次的人有y个(搜了1次的人约有1000万个)。坐标轴上的数值标的是取log前的数值。
然后我们再看百度电影推荐系统算法大赛的数据。下面先列出电影推荐系统大赛训练集的一部分统计数据。
打过多少个分数 | 打过这么多分数的有多少人 |
---|---|
1 | 245 |
2 | 175 |
3 | 173 |
4 | 168 |
5 | 158 |
6 | 381 |
7 | 323 |
8 | 156 |
9 | 151 |
10 | 110 |
11 | 99 |
12 | 206 |
13 | 107 |
14 | 96 |
15 | 85 |
16 | 96 |
17 | 91 |
18 | 190 |
19 | 77 |
20 | 97 |
21 | 89 |
22 | 76 |
23 | 78 |
24 | 150 |
25 | 73 |
26 | 55 |
27 | 58 |
28 | 66 |
29 | 71 |
30 | 129 |
可以看到一个很奇怪的现象,打分个数为6的倍数的人非常多,这两列取log绘图如下:
可以明显的看到是两条线,非常奇怪。考虑到测试集实际上也是用户打分的一部分,所以如果放到一起,重新画图,就变成了这样:
正常了很多,但这个图明显不是直线,更进一步让人怀疑数据的真实性。
至于训练集中为什么会出现打分数量为6的倍数的人非常多? 这个问题后来找到了答案,下面会解答。但从这里已经能看出,即使数据是真实的,选取测试集的方式似乎破坏了什么。导致训练集和完整数据集相比失去了某种一致性。
寻找数据源
数据源应该是来自百度旗下,某个有电影打分的网站。于是联想到了不久前被百度收购的“今晚看啥”。之前有了解过这个网站,刚推出不久的时候一些媒体有报道。
今晚看啥原域名为:http://jinwankansha.com
被收购后的域名为:http://kansha.baidu.com
有这么几个网址可能有用:
查看用户编号15所打过的分数:http://kansha.baidu.com/user/15
查看电影编号6294的电影信息:http://kansha.baidu.com/movie/6294
类似的查看其它用户、电影的地址可以根据上述网址特点自行发现。
然后可以看到和比赛数据中的用户ID,电影ID似乎是可以匹配上的,初步断定数据来源为“今晚看啥”网站。
在用户页面中,列出的每个电影右上角就是该用户所打的分数,可以发现这个分数是10分制的。于是很容易猜测到,比赛数据很可能是对他除以2。
但是为了证明以上猜测,需要拿到整个网站的数据验证。
爬取网站数据
根据比赛数据中提供的distinct_userId.txt,可以爬取网站中所有涉及到的用户打分。因为打分数量非常大,这个任务持续了好几天。
基本上是在两个机器上爬,一个机器从前往后,一个机器从后往前遍历用户ID。然后再拼接。
此外还爬了distinct_movieid.txt中涉及到的所有电影主页,这是为了分析电影标签用到。
网站数据和比赛数据的对比
对照网站和训练集,观察第一个用户(User ID为15的用户)所打过的分数。很快能看出,该用户的每七个打分中,前六个在训练集中,第七个在测试集中。伪代码类似这样:
1 2 3 | 对每个用户 对这个用户的第i个评分 如果i是7的倍数,放入测试集,否则放入训练集 |
这就解释了为什么给6的倍数个电影打分的人数非常多。假设有一个用户总共给7个电影打过分,那么只有前6个会在训练集中;如果一个用户总共给6个电影打过分,那么也是6个打分在训练集中。这就导致了在训练集中,给6个电影打过分的人数比例变多了。重申一下,我认为这种划分训练集和测试集的方式不够好。
因为已经得到了顺序信息,在爬完所有用户打分之后,我采用了比较严格的对照策略:只有当网站数据和比赛数据的打分电影以及打分顺序完全对照上的时候,我才认为两组数据对照好了。否则的话我按照顺序不能乱的原则,能匹配上多少匹配上多少。比赛数据一共涉及到149768个用户,最终有3586个用户的打分信息没有完全对照上,约占2.39%;训练集中包含8533166个打分,其中100390个打分没有对照上,约占1.18%;测试集中包含1348391组数据,其中16633组没有对照上,约占1.23%。因此没对照上的数据影响不大。这部分可能是因为有用户后来更改过或者删除过自己以前的打分。至此我们论证了,比赛数据来源于“今晚看啥”网站数据。
于是,我们可以对比一下网站数据和比赛数据的基本统计信息了。由于网站数据中打分分值是比赛数据中打分分值的2倍,所以以后对比的时候一律将网站数据打分除以2。实际上比赛数据应该就是这么生成的。
打分所占比例:
分数 | 比赛数据中所占比例 | 网站数据中所占比例 |
---|---|---|
0.5 | 0.025% | 0.025% |
1.0 | 0.156% | 0.156% |
1.5 | 0.045% | 0.045% |
2.0 | 0.548% | 0.548% |
2.5 | 0.268% | 0.268% |
3.0 | 10.594% | 3.872% |
3.5 | 14.566% | 1.123% |
4.0 | 50.401% | 90.753% |
4.5 | 14.328% | 0.881% |
5.0 | 9.069% | 2.330% |
可以清晰的看到,3-5分这个范围内的分数比例出现了很大变动,而其余比例竟毫无变化。下面这个表一共10行10列,第i行j列的数表示有多少个打分从j/2.0分变成了i/2.0分
2077 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
0 | 13114 | 0 | 4 | 0 | 1 | 0 | 0 | 0 | 0 |
0 | 0 | 3834 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 46198 | 3 | 2 | 1 | 0 | 0 | 0 |
0 | 0 | 1 | 3 | 22583 | 7 | 1 | 4 | 0 | 0 |
0 | 0 | 0 | 3 | 2 | 326318 | 13 | 567010 | 10 | 17 |
0 | 0 | 0 | 0 | 3 | 56 | 94613 | 1133621 | 20 | 26 |
0 | 1 | 0 | 6 | 6 | 60 | 42 | 4249919 | 86 | 109 |
0 | 0 | 0 | 0 | 0 | 18 | 10 | 1134025 | 74120 | 54 |
0 | 0 | 0 | 1 | 2 | 17 | 9 | 568422 | 24 | 196285 |
可以看出,有很多4.0分在比赛数据中变成了3.0,3.5,4.5,5.0这四个分数。而其他分数,几乎没有改变。有少量的改变可能是因为有用户后来更改过自己的打分。
从下面表格中,你可以看到网站数据中的4.0分,很可能是怎样被变成其他分数的。这里我们忽略掉由4.0分变成0.5-2.5的分数,因为那部分人数过少。
这里我们猜测:4.0分以2/27的概率变动到3.0,以4/27的概率变动到3.5,以4/27的概率变动到4.5,以2/27的概率变动到5.0
由4.0分变成多少分 | 所占比例 | 猜测的比例 |
---|---|---|
3.0 | 567010/7652997=0.074089928 | 2/27=0.074074074 |
3.5 | 1133621/7652997=0.14812772 | 4/27=0.148148148 |
4.0 | 4249919/7652997=0.55532741 | 15/27=0.555555556 |
4.5 | 1134025/7652997=0.14818051 | 4/27=0.148148148 |
5.0 | 568422/7652997=0.074274431 | 2/27=0.074074074 |
因此,猜测数据是这样被扰动的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | if 原打分是4分 then r = 随机生成一个[0,26]中的整数 if r < 2 then 打分改为 3分 else if r < 6 then 打分改为 3.5分 else if r < 10 then 打分改为 4.5分 else if r < 12 then 打分改为 5分 else then 打分保持4分不变 end if end if |
当然,我无法论证数据就是依靠这个伪代码生成的,但是从上述统计数据上看,至少他一定是人工做了调整的,而且极有可能是以类似上述代码的方式做的调整。
但是这种调整导致了数据的不真实!
为什么会故意改变真实数据?我现在只能猜测一个可能性:网站原始数据分布非常不好,有90.753%的打分都是4.0分,出题人也许认为这样的数据不适合用于比赛。
但是使用不真实的数据来检测模型,设计算法,还有意义吗?何况是这么大比例的不真实。
这相当于我们要预测的数据集里面,九成以上都是随机数。
如果直接用网站数据作为预测结果提交,根据上述分布估算,大约能获得0.46左右的RMSE(截至我写博文的时候尚未提交测试)。请注意,即使你知道了真实数据,也仅仅能得到0.46左右的RMSE,而提交全4分的预测都能得到0.57左右的RMSE。何况我们不可能用算法完美“预测”到网站数据。
如果一定要继续比赛,那也可以使用网站数据把模型训练的极度过拟合。但这样没什么太大意义了。即使没有使用网站数据,达到了好成绩,也很可能只能说明对随机扰动恢复的比较好。
一些其他的发现
1)网站数据中为什么有那么多人给电影8分(也即换算成比赛数据集中的5分制,对应4分)
中国人都喜欢8分?不是。如果你未登录的情况下访问今晚看啥,他会让你选择5个喜欢的电影,给你推荐相似电影。如果选完之后,你再登录网站(注意必须先选完再登录),你会发现今晚看啥把你刚才登录前选的电影认为是你打了8分。
这个设置合理吗?我个人觉得不合理。
2)关于比赛数据中出现了8分(难道网站上有人打16分?)
比赛明确说明了打分范围是0到5分,而比赛数据中却出现了有人打8分。另外实际上在正常情况下,网站上0分也是打不了的。但比赛数据中也有0分出现。这是怎么回事?
请浏览这个用户的打分吧 http://kansha.baidu.com/user/198?order=rating
也许页面内容以后随着用户修改自己打分会被改变,我截图如下:
他还真的给电影打过16分。这可能是网站早期的一个bug:后台没有验证分数范围。查看这个帐号所对应的微博 http://weibo.com/u/2487127822 他早在网站上线的6月份之前就开始发“今晚看啥”的微博,而且一直在发,所以他可能是网站内部人员,以前测试的时候打的16分遗留了下来。那么除以2换算成5分制,刚好8分。
但经我测试,现在还是可以想办法给电影打0分。至少在我写这篇博文的时候。
3)标签格式不规范
有的标签后面有多余空格,例如: http://kansha.baidu.com/movie/38 里面有两个标签看上去挨着太紧了吧,那是因为前面一个标签多了个空格。
截图如下:
4)被删除的电影占据幽灵位置?
访问 http://kansha.baidu.com/user/398?p=4 注意这不是最后一页,但是这页却少写了一个电影。 初步推测是被删除的电影依然占了一个位置。而左侧栏这个用户打分总数(982)也包括了这个空白位。
截图如下:
对推荐系统以及推荐系统比赛的个人建议
提出对数据质疑的目的不是批评,一是希望大家不要用这组数据检验自己的模型,二是提出一些针对性的建议,希望能有用。
1)务必使用真实数据
为了隐私等因素,可以做匿名化处理,但是尽可能保持数据真实性。因为这类数据集不多,我想今后也会有做相关研究的人找来做实验。
2)不要混淆“喜欢”和打分
根据本文前面所述,“今晚看啥”将所有的”喜欢“认定为8分,和用户打分混合记录。“喜欢”并不能对应到一个具体的分值,首先网站在记录上,应当把“喜欢”和评分分开记录(因我不能看到“今晚看啥”后台数据库,这点我不能确定是否已经这么做了)。其次,在发布数据的时候,应该将这两部分分开,因为无法确定做模型和算法的人是否把“喜欢”对应到具体分值。个人认为不对应到具体分数效果会更好,因为“喜欢”更多的是一种隐反馈。
3)注册前和注册后的行为区分开
记录用户行为的时候,在注册前的行为最好分开记录(因我不能看到“今晚看啥”后台数据库,这点我不能确定是否已经这么做了)。
因为用户注册前的行为有可能乱试试的心理更重,数据参考价值更低。“今晚看啥”把注册/登录前点击的“喜欢”也放入了比赛数据集,我想这可能是导致log-log曲线不正常的主要原因。因为如果这么记录,那还有很多点击了”喜欢“但没有注册/登录的用户没有被统计。
4)除了RMSE外考虑其他评价准则
RMSE是推荐系统的一个评价方式,但不是唯一方式。由于“今晚看啥”里的“喜欢”数量远大于具体评分,所以这时候使用RMSE作为评价标准显得非常不合理。
一个可能更加合理的方式请参考KDD CUP 2011年Track 2中的题目。按某种比例,随机选择一些热门电影作为负样本,再随机从用户喜欢的电影里抽取同样数量作为正样本,混合在一起。比赛任务是尽可能准确的区分出正样本和负样本。 评分很高的一部分电影也可以作为正样本。
这不一定是最好的办法,但看上去会比RMSE更合理。
5)生成测试集的策略问题
如本文所述,本次比赛数据的生成策略是每7个电影中选择一个出来放入测试集。这样做使得训练集和测试集在某种程度上分布不一致。
建议不如用最简单的方式随机选择测试集更合适。注意避开最不活跃的用户即可。
6)网站评分的UI设计
可以看到网站打分里面,整数分远多于非整数分。这是因为UI设计上是五颗星,用鼠标点击到半颗星的位置上时,才对应非整数分。所以大多数人容易点到整数分。
因此建议不如干脆取消非整数分数。(我对此条建议没有足够说服力,不保证靠谱。)
7)排行榜仅显示部分数据的测试结果
排行榜应当只显示对至多一半提交数据的评测信息,而剩下的数据用做比赛结束后公布最终排行。因为如果对所有提交数据全部评测,最终会导致排名最靠前的人过拟合,而真正防过拟合的提交缺不能排到前面。根据比赛网站所述,这次比赛应该是对整个提交数据进行了评测并发布到排行榜上,于是这次比赛也就直接选择排行榜上的最好提交作为最终提交。而一般这类比赛都是选择最后一次提交作为最终提交,因为排行榜看似分高的提交可能是过拟合的。
电影标签的分析
这一节内容和本文主题无关,只是顺带说一下。因为有人觉得比赛数据中有的电影标签过多,是不是有什么问题。
比赛数据给出的电影标签是数字,而网站中看不到数字ID,而且在网站上管标签叫做“电影基因”。
一个奇怪的事情是比赛数据中有的电影有好几十个标签,但是在今晚看啥网站上没有看到这个情况,似乎标签都不多。于是我尝试将数字标签和网站的电影基因进行对照。这样可以知道原因。
定义标签和基因的相似度,假设集合A包含出现过某个标签的所有电影,集合B包含出现过某个基因的所有电影,则可以定义 $| A \cap B | / | A \cup B|$ 为标签和基因的相似度。于是可以计算得到每个标签和每个基因的相似度。然后再通过二分图最大权匹配来得到标签和基因的对应。(实际上我偷懒没有用二分图最大权匹配,直接找了每个标签最相似的基因,这么做并不准确但结果大致还可以。)由于信息量不足,有的标签或者基因出现次数过于少,所以无法确定对应关系,只能确定其中一部分标签和基因的对应。以数据集中的第一个电影(电影ID为31)为例,他的标签为148,149,160,372,138,969,68,42,556,567,790,592,644,484,43,508,492,467,890,908,但是网站中这个电影只有剧情,犯罪,警匪,悬疑,粗犷,凄凉,吸毒,上瘾这么几个标签,比数字要少。我找到的和这个电影相关的对应关系包括:
数字标签 | 电影基因 |
---|---|
148 | 贩毒 |
149 | 吸毒 |
160 | 卧底 |
372 | 伸张正义 |
68 | 凄凉 |
42 | 粗犷 |
484 | 惊悚 |
43 | 悬疑 |
508 | 犯罪 |
492 | 警匪 |
467 | 剧情 |
890 | 黑帮 |
从表中可以看到,确实这些标签看上去都和那个电影相关,因此可以确定网站上没有显示所有的标签,而实际上他有更多的标签,所以电影标签这部分数据是真实的。
致谢
感谢kirstein最先发现平均分和全4分就能达到很好的RMSE,引起我对数据的怀疑。
感谢licstar提供KDD Cup数据集的log-log图以及帮助我观察随机扰动的规律。他也对本文提出了若干建议。