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

基于物品的协同过滤算法(ItemCF)

  ItemCF更加看重与维系用户的历史兴趣,会给用户推荐与那些他们之前喜欢的物品相似的物品。也就是说ItemCF需要计算物品之间的相似度,而不像UserCF去计算用户的相似度。比如,你购买过物品A,通过计算物品B与物品A有很大的相似度,那么就会给你推荐物品A. 而这里计算物品之间的相似度,也不是利用物品的内容属性,而是通过分析用户的行为记录来计算的。该算法认为,之所以A与B有很大的相似性,是因为喜欢A的用户也喜欢B.

  ItemCF主要分两步:

  1. 计算物品之间的相似度。
  2. 根据物品的相似度和用户的历史行为给用户生成推荐列表。

   计算两个物品之间的相似度可以用下面的公式:

wij=N(i)N(j)N(i)N(j)

这里,我们看到,共同喜欢这两个物品的用户数目越多,就代表这两个物品的相似度越大。这个应该容易理解些。 计算时,需要先建立用户-物品倒排表,是为了降低时间复杂度,直接对那些同时出现在用户行为列表中的物品进行计数,而不去考虑 N(i)N(j)0 的情形。
相似度计算的python代码是:

from math import *
def ItemSimilarity(train):
    C=dict()#分子
    N=dict() #分母
    for u, items in train.items():
        for i in items:
           if i not in N:
               N[i] = 1
           else:
               N[i] += 1
           for j in items:
               if i != j:
                   if (i, j) not in C:
                       C[(i, j)] = 1
                   else:
                       C[(i, j)] += 1
    W = dict()
    for (i,j), val in C.items():
        if i not in W:
            W[i] = {}
        W[i][j]= val / sqrt(N[i] * N[j])
    return W

   从上面我们知道,每个用户都会为物品相似度做出贡献。考虑到活跃用户对物品相似度的贡献要小于要不活跃用户的贡献,本书中给出了修正的相似度计算公式,对活跃的用户做了一种软性的惩罚,

wij=uN(i)N(j)1ln(1+N(u))N(i)N(j).

这里说活跃用户对物品相似度的贡献小于不活跃的用户。我是这么理解的,一个活跃的用户买了很多物品,会涉及到很多类型,那么这里面任意两个物品属于同一个类型的概率就比较小,这是假相似。而两个物品被很多不活跃的用户买了,才说明它们是真的相似。另外,对于过于活跃的用户,为了避免相似度矩阵过于稠密,在实际计算中一般直接忽略他的兴趣列表。
下面是修正后的物品相似度计算代码:

from math import *
def ItemSimilarity2(train):
    C=dict() #分子
    N=dict() #分母
    for u, items in train.items():
        for i in items:
            if i not in N:
                N[i]=1
            else:
                N[i]+=1
            for j in items:
                if i !=j:
                    if (i,j) not in C:
                        C[(i,j)]=1/log(1+len(items))
                    else:
                        C[(i,j)]+=1/log(1+len(items))
    W=dict()
    for (i,j), val in C.items():
        if i not in W:
            W[i]={}
        W[i][j]=val/sqrt(N[i]*N[j])
    return W

  对于相似度,文章又指出如果可以把得出来的相似度按照最大值归一化,可以提高推荐的准确率,覆盖率和多样性。即

wij=wijmax wij

归一化这块理解的还不好,需要再查查资料
  相似度计算完后,就是要给用户生成推荐列表了。这里类似于UserCF, 计算用户对物品的兴趣度,然后推荐给用户兴趣度高的。通过下面的公式计算用户u对物品j的兴趣:
puj=iN(u)S(j,K)wjirui

这里, N(u) 是u喜欢的物品的集合, S(j,k) 是和物品j最形似的K个物品的集合, wji j i 的相似度, rui 为u对i的兴趣(对于隐反馈数据集,u对i有过行为,可令 rui=1 )。下面的程序封装了计算兴趣度和推荐:

def ItemRecommendation(user, train, W, N, K=10):
    rank = dict()
    user_items = train[user]
    for i in user_items:
        for j, wij in sorted(W[i].items(), key=lambda x: x[1],reverse=True)[0:K]:
            if j not in user_items:
                if j not in rank:
                    rank[j] = wij * 1
                else:
                    rank[j] += wij * 1
    rank = sorted(rank.items(), key=lambda x: x[1], reverse=True)

    rank = rank[:N]
    return rank

   接下来就是通过计算准确率,召回率,覆盖率来研究 K对系统的影响。计算公式已经在上一篇文章中给出,这里给出python代码

#准确率
def ItemPrecision(train, test, N, K):
    hit = 0
    alls = 0
    W = ItemSimilarity(train)
    for user in train.keys():
        te_user_item = test[user]
        recomRank = ItemRecommendation(user, train, W, N, K)
        for recom_item, w in recomRank:
            if recom_item in te_user_item:
                hit += 1
        alls += N
    return hit * 1.0 / alls
 #召回率
def ItemRecall(train, test, N):
    hit = 0
    alls = 0
    W = ItemSimilarity(train)
    for user in train.keys():
        te_user_item = test[user]
        recomRank = ItemRecommendation(user, train, W, N)
        for recom_item, w in recomRank:
            if recom_item in te_user_item:
                hit += 1
        alls += len(te_user_item)
    return hit * 1.0 / alls
 #覆盖率
def ItemCoverage(train, N):
    recommend_items = set()
    all_items = set()
    W = ItemSimilarity(train)
    for user in train.keys():
        for item in train[user]:
            all_items.add(item)
        rank = ItemRecommendation(user, train, W, N)
        for item in rank:
            recommend_items.add(item[0])
    return len(recommend_items) / (len(all_items) * 1.0)

ItemCF的大概思路应该就是这样。等看了ItemCF 这块的论文了,再补些想法。

你可能感兴趣的:(推荐系统)