推荐算法(基于内容CB和协同过滤CF)

引入Item属性的Content Based推荐

引入Item属性的CB算法是利用item的特征找到与之相关的其他item列表,这个过程是由正排表 --> 倒排表完成,排序后取TopN推荐给用户。打个比方,例如用户浏览一个音乐网站,该网站具有以下音乐item:itemA,itemB,itemC,itemD,itemE。

建立item属性索引表
事先对所有item建立属性索引表。首先对各item的名称进行分词,形成item --> token1, token2 ,…的正排表,其中token表示一个单词,score表示该token在该item名称里的权重值(TFIDF),score越大,说明该token越重要,即:
itemA --> token1 : score, token2 : score , token3 : score
itemB --> token1 : score, token3 : score , token4 : score
itemC --> token5 : score, token8 : score
itemD --> token3 : score, token1 : score
itemE --> token6 : score

然后利用上述正排表,形成每个token对应的倒排表,并且按照score逆序排列,即:
token1 --> itemA : score, itemB : score, itemD : score
token2 --> itemA : score,
token3 --> itemA : score, itemB : score,
token4 --> itemB : score,
token5 --> itemC : score,
token6 --> itemE
这些倒排表按照key - value的形式存入数据库中,key为token,value为该token对应的item列表。

相关性计算(itemH的token权重与倒排表的item权重的相乘)
当用户点击某一音乐itemH时,对itemH进行分词,形成
itemH --> token1: score1, token3 : score3,
拿分出的token去遍历数据库中的倒排索引表,查询token1和token3所对应的倒排列表
token1 --> itemA : score, itemB : score, itemD : score
token3 --> itemA : score, itemB : score。

(一个token倒排表里每个item的score就是该item正排表里分词出的相应token的score)

假设先遍历token1的倒排表,有itemA、itemB和itemD,此时计算每个item的推荐分数,则对itemA的推荐分数是
itemA.score= score1 * token1 -> itemA.score

对itemB的推荐分数
itemB.score = score1 * token1 -> itemB.score

对itemD的推荐分数
itemD.score = score1 * token1 -> itemD.score

接着遍历token3的倒排表,有itemA和itemB,则最终对itemA的推荐分数是

itemA.score= itemA.score + score3 * token3 -> itemA.score

对itemB的推荐分数是
itemB.score = itemB.score + score3 * token3 -> itemB.score

对itemD的推荐分数还是itemD.score

排序
最后按照itemA,itemB和itemD的score排序输出推荐item的顺序,排在前面的item自然就是与itemH相关度高的。


在这里插入图片描述

引入User属性的Content Based推荐

引入User属性的CB算法是在前面内容分析的基础上再结合了用户的行为(比如浏览记录、收藏记录等),也就是将已有的item数据和用户行为里的item数据放在一起,作分词产生正排表,再产生倒排表,结合用户当前浏览的item,利用其token遍历倒排表,进行相关性计算并排序,产生推荐列表。


在这里插入图片描述

协同过滤的CF(Collaborative Filterin)算法

在这里插入图片描述
item:用户行为日志,用户点击、浏览或观看某一个item的记录,并记录score。从行为日志中生成user-item即UI矩阵,表示某个user对某个item的喜好,这个喜好程度就用score来衡量。比如用户浏览某个新闻或观看某个视频的时间越长,score值越大。

微博推荐好友:一个user登录微博,将此user作为索引查询UU矩阵,找到其对应的user列表,完成好友推荐。

UU矩阵:反映出user和一组user之间的相似程度
II矩阵:反映出item和一组item之间的相似程度

充分利用群体智慧:预测user对某个item的喜爱程度是根据与该user相似的用户和这些用户对该item的score来判定的。这个过程没有像CB那样利用item的文本信息来计算相关性,不需要用分词来提取关键词,而是基于用户的行为,推荐的过程跟其他人有关,客观直接,CB的推荐过程与其他人无关,只根据item的内容相关性。

精度高于CB,CB只是依据文本内容(分词信息)进行相关性分析,但用户对某个item的喜好不仅仅是因为文本内容,可能会因为颜色、形状、甚至自己的感觉或周边人都喜欢等各种因素。CF中利用到user对item的score,而这个score正是反映出用户基于各种因素的考虑后对item的喜爱程度

推荐结果解释性较差:用户并不知道为什么要给自己推荐某个item,因为不像CB那样基于文本内容的相关性,而是基于其他用户的行为来推荐,很可能这些用户你并不认识,但你们在某种程度上存在相关性(比如有相同的爱好,都喜欢观看某一类型的视频),在于CF挖掘了某些隐含的相关性

对时效性强的item不适用:不管是利用UU还是II矩阵,前提都是需要整理出UI矩阵,如果这时新上架了一个item,没有任何人对此产生行为(比如点击、购买、浏览),那么这个新的item就不会存在于UI矩阵。只有当该item上架一段时间,收集到了大量的用户行为信息,那它才能进入UI矩阵,计算得出好的推荐结果。

冷启动问题:物品冷启动和用户冷启动,物品冷启动就是对新上架的item没有推荐作用;用户冷启动就是新注册了一个用户,该用户对物品没有产生任何的行为,同样对该user没有推荐作用。

CF要比CB有更好的推荐效果,一个网站新上架,此时没有任何的行为数据,那么先通过CB来将物品推荐出去,在推荐的过程中收集到大量的用户行为数据,一旦行为数据收集到一定程度,就可切换到CF产生推荐。也就是说CF是建立在CB的前提之上,因为没有行为的话CF无法作推荐,而CB是先帮你收集行为,有了行为就可以作CF,所以CF的效果要比CB好很多。

User-Based CF

假设:
–用户喜欢那些跟他有相似爱好的用户喜欢的东西
–具有相似兴趣的用户在未来也具有相似兴趣

方法:
–给定用户u,找到一个用户的集合N(u),他们和u具有相似的兴趣——UU矩阵
–将N(u)喜欢的物品推荐给用户,基于其他用户的行为


在这里插入图片描述

在这里插入图片描述

Item-Based CF

假设:
–用户喜欢跟他过去喜欢的物品相似的物品
–历史上相似的物品在未来也相似

方法:
–给定用户u,找到他过去喜欢的物品的集合R(u)——II矩阵
–把和R(u)相似的物品推荐给u,基于用户的历史行为


在这里插入图片描述
在这里插入图片描述

上图中的U和I表示所有用户和物品,用矩阵的形式求物品-物品相似度。利用余弦公式计算物品相似度时,可将其拆分为归一化后的两个物品向量对应纬度值相乘然后再累加,比如归一化后的两个物品向量Ii = (r1, r2, r3), Ij = (m1, m2, m3), 那么sim(Ii, Ij) = r1 * m1 + r2 * m2 + r3 * m3,这样就可以进行分布式计算了。

MapReduce计算item相似度:利用3次MapReduce。

分布式计算II相似度先将UI矩阵展开成三列userid, itemid, score,score就表示一个user对item的打分。这里的数据就不包括UI矩阵中的稀疏数据了,每一行的score都是非零的,在计算物品的模长时(用于归一化)只要找到一个itemid对应的所有(userid,score)即可,对于归一化后物品向量各分量的相乘,由于有userid,以userid为key就可获得所有item对应的gen_score,将其两两组合相乘就相当于物品向量各维度分量的乘积了

输入数据UI如下:每一行代表一个用户对某一个物品的评分

3666038384,1000139509,0.037646	
807800026,1000411209,0.514877	
3464005685,1000411209,0.487496	
1012011302,1000411209,0.343973	
4093957926,1000411209,0.047148	
3497056829,1000411209,0.514877	
1219587977,1000456309,0.449973	
..............................

第一个MapReduce:
map阶段,就是将原始数据格式的user,item,score转换成item,user,score,因为接下来对item归一化要以item为key,获取item所有的向量分量score值

#!/usr/local/bin/python

import sys

for line in sys.stdin:
    ss=line.strip().split(',')
    if len(ss) != 3:
        continue
    u, i, s = ss
    print "%s\t%s\t%s" % (i, u, s)

reduce阶段,因为map输出的结果是以key即itemid排好序的,所以接下来就相当于做一个wordcount。每遍历到相同的itemid,就将其对应的(userid,score)追加到user_score_list列表里,当遍历到不同itemid时,就把列表里所有tuple的score求平方和后再开根号,即得到itemi对应的模长,将各分量值归一化后以userid, itemid, gen_score的形式输出。

以该形式输出的目的是为了接下来以userid为key,获得每个userid对应的(itemid, score)集合,这样才能将每一行里(上图红框处)的各分量两两相乘组合

#!/usr/local/bin/python

import sys
import math

cur_item = None
user_score_list = [] # 保存一个item对应的各user对其的score

for line in sys.stdin:
    item, user, score = line.strip().split("\t")
    if not cur_item:
        cur_item = item
    if item != cur_item:
        sum = 0.0
        for tuple in user_score_list:
            (u, s) = tuple
            sum += pow(s, 2)
        sum = math.sqrt(sum)  #该物品item向量的模长,各维度是各用户对该item的score
        for tuple in user_score_list:
            (u, s) = tuple
            print "%s\t%s\t%s" % (u, cur_item, float(s / sum))

        user_score_list = []
        cur_item = item

    user_score_list.append((user, float(score)))

sum = 0.0
for tuple in user_score_list:
    (u, s) = tuple
    sum += pow(s, 2)
sum = math.sqrt(sum)
for tuple in user_score_list:
    (u, s) = tuple
    print "%s\t%s\t%s" % (u, cur_item, float(s / sum))

第二个MapReduce:
map阶段,只是输出了数据,userid, itemid, gen_score

#!/usr/local/bin/python

import sys

for line in sys.stdin:
    u, i, s =line.strip().split('\t')
    print "%s\t%s\t%s" % (u, i, s)

reduce阶段,就是把map输出的数据,按行遍历,把相同userid对应的所有(itemid,gen_score)都添加进item_score_list列表里,然后对其两两取对(正向取反向取,但不取自身),对应score相乘,以(itemid itemid, score)的形式输出(itemid包括正向反向),这样输出的每一行只是两个item的一对分量相乘,还需要把所有分量的乘积相累加才能得到两个item最终的相似度

#!/usr/local/bin/python

import sys

cur_user = None
item_score_list = [] # 保存用户user对应的item和score列表

for line in sys.stdin:
    user, item, score = line.strip().split("\t")
    if not cur_user:
        cur_user = user
    if user != cur_user:
        for i in range(0, len(item_score_list) - 1):
            for j in range(i + 1, len(item_score_list)):
                item_a, score_a = item_score_list[i]
                item_b, score_b = item_score_list[j]
                print "%s\t%s\t%s" % (item_a, item_b, score_a * score_b)
                print "%s\t%s\t%s" % (item_b, item_a, score_a * score_b)

        item_score_list = []
        cur_user = user

    item_score_list.append((item, float(score)))

for i in range(0, len(item_score_list) - 1):
    for j in range(i + 1, len(item_score_list)):
        item_a, score_a = item_score_list[i]
        item_b, score_b = item_score_list[j]
        print "%s\t%s\t%s" % (item_a, item_b, score_a * score_b)
        print "%s\t%s\t%s" % (item_b, item_a, score_a * score_b)

第三个MapReduce:
map阶段,将上一reduce输出中的item,item合并为item-item作为key

#!/usr/local/bin/python

import sys

for line in sys.stdin:
    i_a, i_b, s = line.strip().split('\t')
    print "%s\t%s" % (i_a + "" + i_b, s)

reduce阶段,作累加即可求得两两物品之间的相似度

#!/usr/local/bin/python

import sys

cur_ii_pair = None
score = 0.0

for line in sys.stdin:
    ii_pair, s = line.strip().split("\t")
    if not cur_ii_pair:
        cur_ii_pair = ii_pair
    if ii_pair != cur_ii_pair:
        #item_a, item_b = cur_ii_pair.split('')
        ss = cur_ii_pair.split('')
        if len(ss) != 2:
            continue
        item_a, item_b = ss
        print "%s\t%s\t%s" % (item_a, item_b, score)
        cur_ii_pair = ii_pair
        score = 0.0

    score += float(s)

ss = cur_ii_pair.split('')
if len(ss) != 2:
    sys.exit()
item_a, item_b = ss
print "%s\t%s\t%s" % (item_a, item_b, score)

在这里插入图片描述

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值