基于邻域的协同过滤算法(一)

来源:互联网 发布:淘宝证书错误 编辑:程序博客网 时间:2024/04/29 14:15

  两个月前我开始学习机器学习,这两个月期间学习了一些基础的机器学习算法及其Python实现。这周我刚开始学习《推荐系统实践》这本书,并打算以后定期将自己的学习情况做个简单的总结。这份总结是我这个博客上的第一篇文章。这周的学习的主要内容是基于邻域的协同过滤算法。基于用户行为分析的推荐算法是个性化推荐系统的重要算法,学术界一般将这种类型的算法称为协同过滤算法。

  协同过滤,从名字就可以看出这种方法的本质是先有协同(用户齐心协力)再有过滤(每个用户的推荐列表能够过滤掉他不喜欢的物品)。对协同过滤算法,学术届提出了了很多种方法,比如基于邻域的方法(neighborhood-based)、隐语义模型(laten factor model)、基于图的随机游走算法(random walk on graph)等。

  基于邻域的方法主要包括两种算法:基于用户的协同过滤算法(UserCF)和基于物品的协同过滤算法(ItemCF)。

基于用户的协同过滤算法(UserCF):

  UserCF的本质就是我要给你推荐物品,我先找到与你兴趣最相似的K个用户,然后把他们喜欢的而你却没产生过行为的物品推荐给你。当然不是把所有的他们喜欢的且你没产生过行为的都推荐给你,而是推荐你最感兴趣的 N个产品。根据上面这些描述,我们看到使用 UserCF需要两步:

  1. 找到与你兴趣相似的K个用户。
  2. 确定你最感兴趣的N个物品?

  在第一步中,我们需要去计算你与每个用户的兴趣相似度,然后按相似度降序排列,所对应的前K个用户就是我们要找的。

  对于用户u和v我们可以通过如下的Jaccard公式来计算:
wuv=N(u)N(v)N(u)N(v),

或者通过余弦相似度计算:
wuv=N(u)N(v)N(u)N(v),
计算相似度的时候,为了减少时间复杂度,需要建立一个物品-用户倒排表。因为当用户数很大时,直接从数据集很费时间,很多时间浪费在了计算 N(u)N(v)=0上。另外,两个用户对冷门物品采取过同样的行为更能说明他们兴趣的相似度。所以书中指出 John S. Breese 在论文中提出如下计算相似度的公式:

wuv=iN(u)N(v)1ln(1+N(i))N(u)N(v).

这个公式通过1ln(1+N(i)) 惩罚了用户u和用户v共同兴趣列表中热门物品对他们相似度的影响。
这里用字典格式来储存数据集和相似度,如:

train={'user1':['a', 'b', 'c'], 'user2':['a','b'],....,'userN':[...]}]W={('user1', 'user2'): w12, ('user1', 'user3'): w13,.....}

  下面是计算相似度的代码

def UserSimilarity2(train, flag=1):    item_users = dict()      for u, item in train.items():        for i in item:            if i not in item_users:                item_users[i] = set()  # 生成一个集合            item_users[i].add(u)    C = dict()  # C为分子    N = dict()  # N[u]表示用户u喜欢的物品个数    ""    #用户数很大时计算C耗时    for u in train.keys():        for v in train.keys():            if u==v:                continue            else:                if len(set(train[u] ) & set(train[v]))!=0:                    C[(u, v)]=len(set(train[u] ) & set(train[v]))    for u in train.keys():        N[u]=len(train[u])    print C    print N    ""    for item, users in item_users.items():        for u in users:            if u not in N:                N[u] = 1  # 如果用户u不在字典N里面,先创建            else:                N[u] += 1            for v in users:                if u != v:                    if flag == 0:  # 修正前                        if (u, v) not in C:                            C[(u, v)] = 1                        else:                            C[(u, v)] += 1                    elif flag == 1: # 修正后                        if (u, v) not in C:                            C[(u, v)] = 1 / log(1 + len(users))                        else:                            C[(u, v)] += 1 / log(1 + len(users))    W = dict()    for uv in C.keys():        # pdb.set_trace()        u = uv[0]        v = uv[1]        if u not in W:            W[u] = set()        # 添加与用户u相关的用户v,第二个意思是他们的权重Wuv        W[u].add((v, C[uv] / sqrt(N[u] * N[v])))    return W

这个代码中 flag=0对应的是第一个没有修正的相似度计算公式,flag=1对应的就是修正后的相似度计算公式。
计算出相似度后,下来要做的就是找出与你相似度高的前K个用户,然后去把他们喜欢的你没发生过行为的物品(记为Itemj)却很感兴趣的物品推荐给你。

  第二步和第一步的思路差不多,定性问题定量化。计算你对Itemj的感兴趣程度,然后降序排列,选出感兴趣程度最高的前N个推荐给你。
这里,感兴趣程度是用下面的公式来度量的:

p(u,i)=vS(u,K)N(i)wuvrvi

S(u,K)就是那K个用户的集合,N(i)是对物品i有过行为的用户集合,wuv就不说了,rvi 是用户v对i物品的兴趣。简单的说,v对i产生了行为rvi就是1,没产生就是0,当然这里的rvi只有这两种值。

  下面是实现推荐的代码:

def Recommend(user, train, W, N, K=10):    rank = dict()    interacted_items = train[user]    for v, wuv in sorted(W[user], key=lambda x: x[1], reverse=True)[0:K]:  # 相似度最大的K个用户        for i in train[v]:  # v产生过行为的物品            if i not in interacted_items:                  if i not in rank:                    rank[i] = wuv * 1                else:                    rank[i] += wuv * 1    rank = sorted(rank.items(), key=lambda x: x[1], reverse=True)    rank = rank[:N]    return rank

这里的K是一个很关键的参数,K的选取会影响最后的推荐结果。这本书中K对TopN推荐的好坏,是用这几个指标来衡量的:准确率,召回率,覆盖率 和 流行度。
记U为系统的用户集合,R(u)为训练集上得出的推荐列表, T(u)为测试集上的行为列表。
推荐结果的准确率可定义为:

Precision=uU|R(u)T(u)|uU|R(u)|

召回率被定义为
Recall=uU|R(u)T(u)|uU|T(u)|

def PrecisionRecall(train, test, N):    hit = 0 #两者的分子    n_precision=0 # 准确率的分母    n_recall = 0 #召回率的分母    W = UserSimilarity2(train)    for user in train.keys():        try:  # 计算分子分母            te_user_items = test[user]            recomRank = Recommend(user, train, W, N)            for recom_item, w in recomRank:                if recom_item in te_user_items:                    hit += 1            n_precision += N            n_recall+=len(te_user_items)        except:            pass    return [hit / (1.0*n_precision), hit/(1.0*n_recall)]

覆盖率的计算公式为:

Coverage=uUR(u)I

这里I所有物品的集合。那么覆盖率就是推荐出去的物品占总物品的比率,商家们会关心的指标。下面是计算覆盖率的代码:

def Coverage(train, N):    recommend_items = set() #分子    all_items = set() #分母    W = UserSimilarity2(train)    for user in train.keys():        for item in train[user]:            all_items.add(item)        rank = Recommend(user, train, W, N)        for item in rank[0]:            recommend_items.add(item)    return len(recommend_items) / (len(all_items) * 1.0)

这本书上,目前看的这一章,我没有看到流行度的具体定义,但从给的代码看,似乎平均流行度是这么定义的:

p=uUln(1+R(u))U

具体代码是这样的:

 def Popularity(train,N):    item_popularity=dict()    for user, items in train.items():        for item in items:            if item not in item_popularity:                item_popularity[item]=0            item_popularity[item]+=1    ret=0    n=0    W = UserSimilarity2(train, flag=1)    for user in train.keys():        rank=Recommend(user, train, W, N, K=10)        for item, pui in rank:            ret +=log(1+ item_popularity[item])            n +=1    ret /=n*1.0    return ret
1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 苹果手机微信语音是耳机模式怎么办 平果手机没声音出现耳机模式怎么办 苹果6s进水了一直耳机模式怎么办 华为手机没声音显示耳机模式怎么办 华为手机没声音出现耳机模式怎么办 安卓手机全民k歌耳返延迟怎么办 华为手机双清后手机开不了机怎么办 荣耀9上面有个耳机标志怎么办 华为p9耳机有一个没有声音怎么办 小米5c手机gps信号弱怎么办 华为8手机时常听常音乐声怎么办 手机用久了变慢了怎么办 免税店买的皮带太短了怎么办 小米手机自拍照片是反的怎么办 华为p20旅行助手被删了怎么办 假如手机点击一个链接是病毒怎么办 华为手机自带铃声没了怎么办 华为手机升级后铃声没了怎么办 华为平板电脑激活锁忘记了怎么办 华为手机不小心删了系统应用怎么办 华为畅想6s开机键坏了怎么办 浏览网页是进入有病毒的网页怎么办 华为荣耀7清理加速那么慢怎么办 华为手机自带天气卸载了怎么办 手机被病毒感染了开不了机了怎么办 小米手机系统桌面已停止运行怎么办 三星手机应用锁密码忘了怎么办 手机管家应用加密密码忘记了怎么办 下载东西呗安全管家制止了该怎么办 手机百度时不小心中毒扣话费怎么办 手机扫二维码中了木马病毒要怎么办 电脑管家微信扫描语音打不开怎么办 遇到花心老公又爱玩没有担当怎么办 软件全闪退返回键不管用了怎么办 为什么下载了我的世界打不开怎么办 问道手游安全锁忘记了怎么办 电脑显示网络电缆没有插好怎么办 手机扣扣的密码忘记了怎么办 扣扣忘记密码和密保怎么办 以前用的扣扣密码忘记了怎么办 我忘记扣扣支付密码了怎么办