最近想写一个map-reduce版的userbased,于是先研究mahout中已实现的itembased算法。itembased看起来简单,但是深入到实现细节还是有点复杂的,用map-reduce实现就更复杂了。
itembased的本质:
预测某用户user对某物品item的打分,
看看该用户对其他item的打分,如果其他item跟该item越相似,则权重越高。
最后加权平均。
itembased核心步骤:
1 计算item相似度矩阵(利用两个矩阵相乘)
2 user打分矩阵 乘以 item相似度矩阵 = user评分预测矩阵
当然,这里所谓的矩阵相乘,并不是数学意义上的相乘。数学意义上的相乘,前面矩阵的行向量和后面的列向量是算内积。而这里,往往不仅仅是内积,有可能做了normalize,有可能做了downsample等等。
输入文件数据格式: userID,itemID,pref
user1,item1,pref
user2,item1,pref
user2,item2,pref
user3,item1,pref
USER_VECTORS: userID,vector[(itemID,pref)]
user1,vector[(item1,pref)]
user2,vector[(item1,pref),(item2,pref)]
user3,vector[(item1,pref)]
RATING_MATRIX: itemID,vector[(userID,pref)]
item1,vector[(user1,pref),(user2,pref),(user3,pref)]
item2,vector[(user2,pref)]
RATING_MATRIX-> similarityMatrix
通过计算RATING_MATRIX行与行之间的相似性,得出itemsimilarity
|
Item1 |
Item2 |
Item1 |
|
|
Item2 |
|
|
MAPPER:
similarityMatrix-> itemID,vector[(itemID,sim)]
item1,vector[(item2,sim)]
item2,vector[(item1,sim)]
USER_VECTORS-> itemID,(userID,pref)
item1,(user1,pref)
item1,(user2,pref)
item2,(user2,pref)
item1,(user3,pref)
(格式跟输入文件一直,但是存储的数据结构不同)
REDUCER:itemID,(vector[(itemID,sim)],(vector[userID],vector[pref]))
当前ITEM,与当前ITEM相似的item的列表(取topK),对当前ITEM打过分的user列表及其打分。
item1,(vector[(item2,sim)],(vector[user1,user2,user3],vector[pref,pref,pref]))
item2,(vector[(item1,sim)],(vector[user2],vector[pref]))
MAPPER:userID,(pref(cur_item),vector[(itemID,sim)])
表示userID对cur_item的打分是pref,与cur_item相似的item列表及其相似度是vector。
user1,(pref(item1),vector[(item2,sim)])
user2,(pref(item1),vector[(item2,sim)])
user3,(pref(item1),vector[(item2,sim)])
user2,(pref(item2),vector[(item1,sim)])
比如第一行表示的意思,到时要预测user1对未评分item的打分,因为item1与她相似,所以要考虑user1对item1的评分。
REDUCE:userID,itemID,pref
通过上面的mapper,同一user的数据都落在一个reducer中,
就可以得到一个用户所有的打分item,以及这些item跟其他item的相似度。
UserID |
Item1 |
Item2 |
Item1,pref |
NULL |
sim |
Item2,pref |
Sim |
NULL |
User1 |
Item1 |
Item2 |
Item1,pref |
NULL |
sim |
Item2,unkownPref |
Sim |
NULL |
User2 |
Item1 |
Item2 |
Item1,pref |
NULL |
sim |
Item2,pref |
Sim |
NULL |
User3 |
Item1 |
Item2 |
Item1,pref |
NULL |
sim |
Item2,unkownPref |
Sim |
NULL |
根据这个(user,item),item的矩阵,就可以预测出该user对未打分的item的打分了。
p(u,n)=sum(pref(u,i)*sim(n,i))/sum(sim(n,i))
表达的意思是,想预测u对n的偏好,
遍历u之前看过的i,
参考对这些i的偏好,
和这些n跟i的相似度,
加权平均。
值得一提的是,mahout为了考虑性能,并没有真正做了一个完整的矩阵乘法。
比如就itemsimilarity,只保留了topK,其他都没有存(其实就是相似度太小,可以忽略不计)。
因此,对于某个user,要预测的item集合,并不是item全集减去该user已评分的item集合。而是该user已评分的item各自最为相似topK的item集合。对于不在这集合里面的item,表明与当前user已评分的item相似性太小,直接表明该user不感兴趣,于是预测的分就是0了,就不用计算了。
至于这个最为相似的topK怎么取,我没有仔细研究,可能是约定K为一个常数,可能是定一个阈值,相似度小于这个阈值的就不要了。对于这两者,后者更靠谱一些,具有对称性。
本文链接:http://blog.csdn.net/lingerlanlan/article/details/42656161 本文作者:linger