美文网首页
基于协同过滤实现个性化推荐

基于协同过滤实现个性化推荐

作者: 会跳的八爪鱼 | 来源:发表于2024-03-30 19:26 被阅读0次

    个性化推荐分为两种方式,一种是利用神经网络实现个性化推荐,另一种是使用协同过滤算法实现个性化推荐。协同过滤算法主要分为两步,第一步协同,比较两者的相似度,然后找出相似度较近的两者,第二步是过滤,过滤出相似两者相同的物品,其他的物品就是用户可能感兴趣的东西。

    相似度计算公式

    1)余弦相似度:结果 = 向量A · 向量B / (向量A的模长 * 向量B的模长)

    余弦相似度公式
    它计算的是两个向量在空间中的夹角大小, 值域为[-1, 1]: 1代表夹角为0°, 完全重叠/完全相似; -1代表夹角为180°, 完全相反方向/毫不相似

    余弦相似度的问题是: 其计算严格要求"两个向量必须所有维度上都有数值",不能存在null的情况。

    2)皮尔逊相关系数
    皮尔逊相关系数的思路是, 我把这些null的维度都填上0,所以它可以看成数据标准化处理之后的余弦相似度
    皮尔逊相关系数具有以下特点:

    (1)、当X、Y相关系数结果为0时,X和Y两变量无关系。
    (2)、当X的值增大(减小),Y值增大(减小),两个变量为正相关,相关系数在0.00与1.00之间。
    (3)、当X的值增大(减小),Y值减小(增大),两个变量为负相关,相关系数在-1.00与0.00之间。
    相关系数的绝对值越大,相关性越强,相关系数越接近于1或-1,相关度越强,相关系数越接近于0,相关度越弱。

    通常情况下通过以下取值范围判断变量的相关强度:
    0.8-1.0 极强相关
    0.6-0.8 强相关
    0.4-0.6 中等程度相关
    0.2-0.4 弱相关
    0.0-0.2 极弱相关或无相关

    皮尔逊相关算法以及python实现

    皮尔逊相关系数计算公式
    ##普通计算
    def test(x, y):
       sum_xy = (x*y).mean()
       sum_x = x.mean()
       sum_y = y.mean()
       sum_x2 = (x*x).mean()
       sum_y2 = (y*y).mean()
       pc = (sum_xy-sum_x*sum_y)/np.sqrt((sum_x2-sum_x*sum_x)*(sum_y2-sum_y*sum_y))
       print(pc)
       
    ##pandas方式计算
    def test2(x, y):
       data=pd.DataFrame({"x":x,"y":y})
       print(data.corr())
    
    if __name__ == '__main__':
       x=np.array([1,3,5])
       y=np.array([1,2,4])
       test(x, y);
       test2(x, y);
    
    基于用户的协同过滤算法(UserCF)

    基本思路:
    ①首先整理用户A的感兴趣物品列表,例如物品A,物品B等
    ②计算计算用户A与其他用户之间的相似度。计算用户相似度我们可以使用余弦相似度皮尔逊相关系数
    ③计算得到与用户A相似度最近的K个用户,最后通过用户相似度叠加该用户感兴趣物品的程序,得出相似度最近的物品。
    例如,下表是 用户-物品 评分表,0表示没有对物品进行过操作,根据此表数据列表用户A的推荐物品清单:

    用户\物品 a b c d e
    A 1 0 0 4 0
    B 2 0 3 1 1
    C 1 3 3 0 4
    D 2 1 2 3 0

    计算过程:
    ①首先计算各用户与A用户的相似度分别为:B:-0.12,C:-0.87,D:0.75
    ②然后相似度叠加到物品评分上,得出剩余物品的评分:b:-1.86, c:-1.47, e:-3.6
    ③综合以上评分,推荐的物品排序是 c > d > e

    完整代码: 代码逻辑主要参照协同过滤和算法推荐,并作了进一步处理

    import operator
    from math import sqrt, pow
    
    
    class UserCf():
       # 获得初始化数据
       def __init__(self, data):
           self.data = data;
    
       # 计算两个用户的皮尔逊相关系数
       def pearson(self, user1, user2):
           sumXY = 0.0;
           n = 0;
           sumX = 0.0;
           sumY = 0.0;
           sumX2 = 0.0;
           sumY2 = 0.0;
           try:
               for movie1, score1 in user1.items():
                   # 此处选择的是两个用户之间相同的部门,为了解决空数据的问题,与上面的计算公式略有不同
                   if movie1 in user2.keys():
                       n += 1;
                       sumXY += score1 * user2[movie1]
                       sumX += score1;
                       sumY += user2[movie1]
                       sumX2 += pow(score1, 2)
                       sumY2 += pow(user2[movie1], 2)
    
               ## 利用上图中第三个公式求解
               ##协方差:cov(X,Y) = E(XY)-E(X)E(Y) = SUM(XY)/n-[SUM(X)/n*SUM(Y)/n]
               molecule = sumXY - (sumX * sumY) / n;
               ##方差:sqrt(SUM(X2)/n-[SUM(X)/n]2)
               denominator = sqrt((sumX2 - pow(sumX, 2) / n) * (sumY2 - pow(sumY, 2) / n))
               r = molecule / denominator
           except Exception as e:
               print("异常信息:", e.message)
               return None
           return r
    
       # 计算与当前用户的距离,获得最临近的用户
       def nearstUser(self, username, n=1):
           distances = {};  # 用户,相似度
           for otherUser, items in self.data.items():  # 遍历整个数据集
               if otherUser not in username:  # 非当前的用户
                   distance = self.pearson(self.data[username], self.data[otherUser])  # 计算两个用户的相似度
                   distances[otherUser] = distance
           sortedDistance = sorted(distances.items(), key=operator.itemgetter(1), reverse=True);  # 最相似的N个用户
           print("排序后的用户相似度为:", sortedDistance)
           return sortedDistance[:n]
    
       # l利用用户相似度和评分给电影排序
       def recomand(self, username, n=1):
           recommand = {};  # 待推荐的电影
           for user, score in dict(self.nearstUser(username, n)).items():  # 最相近的n个用户
               for movies, scores in self.data[user].items():
                   if movies not in self.data[username].keys():  # 当前用户没有看过
                       ## 此处添加了用户相似度与评分之间的叠加关系
                       if movies not in recommand.keys():
                           recommand[movies] = scores * score
                       else:
                           recommand[movies] += scores * score
    
           return sorted(recommand.items(), key=operator.itemgetter(1), reverse=True);  # 对推荐的结果按照电影评分排序
    
    
    if __name__ == '__main__':
       users = {'Lisa Rose': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.5,
                              'Just My Luck': 3.0, 'Superman Returns': 3.5, 'You, Me and Dupree': 2.5,
                              'The Night Listener': 3.0},
    
                'Gene Seymour': {'Lady in the Water': 3.0, 'Snakes on a Plane': 3.5,
                                 'Just My Luck': 1.5, 'Superman Returns': 5.0, 'The Night Listener': 3.0,
                                 'You, Me and Dupree': 3.5},
    
                'Michael Phillips': {'Lady in the Water': 2.5, 'Snakes on a Plane': 3.0,
                                     'Superman Returns': 3.5, 'The Night Listener': 4.0},
    
                'Claudia Puig': {'Snakes on a Plane': 3.5, 'Just My Luck': 3.0,
                                 'The Night Listener': 4.5, 'Superman Returns': 4.0,
                                 'You, Me and Dupree': 2.5},
    
                'Mick LaSalle': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0,
                                 'Just My Luck': 2.0, 'Superman Returns': 3.0, 'The Night Listener': 3.0,
                                 'You, Me and Dupree': 2.0},
    
                'Jack Matthews': {'Lady in the Water': 3.0, 'Snakes on a Plane': 4.0,
                                  'The Night Listener': 3.0, 'Superman Returns': 5.0, 'You, Me and Dupree': 3.5},
    
                'Toby': {'Snakes on a Plane': 4.5, 'You, Me and Dupree': 1.0, 'Superman Returns': 4.0}
                }
    
       userCf = UserCf(data=users)
       recommandList = userCf.recomand('Toby', 10)
       print("最终推荐:%s" % recommandList)
    
    
    基于物品实现协同过滤

    基本思路:
    ①首先整理出用户物品的关系表。即用户-物品列表,记录了每个用户喜爱的物品
    ②统计各物品的喜欢人数,整理出物品-物品共现矩阵,即共同喜欢两个物品的人数
    ③利用余弦相似度计算物品-物品之间的相似度,并且通过叠加用户对该物品的感兴趣程度,得出相似度最近的物品。

    NOTE:对于异常值的处理:限制"用户-物品倒排表"中物品数量
    比如,商城网站,假定一般用户一天内只会购买10件商品,如果存在用户一天内购买了超过100件商品,说明这个用户可能是批发商,此类用户数据需要排除或者添加惩罚参数。因为此类用户的行为数据不仅增加计算量,而且会干扰其他用户的推荐数据,比如相似度计算时,热门物品与所有人都有关联,那么这些物品与其他物品的相似度都会比较大,那么计算出来的推荐结果总是热门。

    IUF公式

    IUF(Inverse User Frequence),即用户活跃度对数的倒数的参数,John S. Breese认为活跃用户对物品相似度的贡献应该小于不活跃的用户,他提出应该增加IUF参数来修正物品相似度的计算公式。为了避免相似度矩阵过于稠密,我们在实际计算中一般直接忽略他的兴趣列表,而不将其纳入到相似度计算的数据集中

    表格1-1,用户-物品列表

    用户 喜爱的物品
    A {a,b,d}
    B {b,c,e, f}
    C {c,d}
    D {b,c,d}
    E {a,d, f}
    F {a,b, c, d, e, f}(非正常用户,不参与计算)

    表格1-2,物品的共现矩阵

    物品 a b c d e f
    a 0 1 0 2 0 1
    b 1 0 2 2 1 1
    c 0 2 0 2 1 1
    d 2 2 2 0 0 1
    e 0 1 1 0 0 1
    f 1 1 1 1 1 0

    表格1-3,物品的感兴趣人数列表

    物品 感兴趣人数
    a 2
    b 3
    c 3
    d 4
    e 1
    f 2

    表格1-4,物品相似度,利用余弦相似度公式 W=N(ab)/√(N(a)*N(b))

    物品 a b c d e f
    a 0 1/√6 0 2/√8 0 1/2
    b 1/√6 0 2/3 2/√12 1/√3 1/√6
    c 0 2/3 0 2/√12 1/√3 1/√6
    d 2/√8 2/√12 2/√12 0 0 1/√8
    e 0 1/√3 1/√3 0 0 1/√2
    f 1/2 1/√6 1/√6 1/√8 1/√2 0

    表格1-5,计算用户A之外的c,e,f的分数

    物品 分数
    c 1*0+1*2/3+1*2/√12 = 1.24
    e 1*0+1*1/√3+1*0 = 0.577
    f 1*1/2+1*1/√6+1*1/√8 = 1.26

    所以推荐的顺序是:f > c > e,但是f属于是热门产品,因为它和所有的物品都有关联关系,所以在分数时,只选择与该物品最相似的物品,防止分数叠加造成的影响。

    完整代码: 代码逻辑主要参照协同过滤和算法推荐,并作了进一步处理

    # -*- coding: UTF-8 -*-
    from math import sqrt
    import operator
    
    #1.构建用户-->物品的倒排
    def loadData(files):
       data ={}
       for line in files:
           user,score,item=line.split(",")
           data.setdefault(user,{})
           data[user][item]=score
       print("----1.1.用户:物品的倒排----")
       print(data)
    
       # 排除非正常用户的干扰
       for user in list(data.keys()):
           if len(data[user]) > 5:
               del data[user]
    
       print("----1.2.用户:物品的倒排----")
       print(data)
       return data
    
    def similarity(data):
       # 构造物品的共现矩阵
       N={} #喜欢物品i的总人数
       C={} #喜欢物品i也喜欢物品j的人数
       for user,item in data.items():
           for i,score in item.items():
               N.setdefault(i,0)
               N[i]+=1
               C.setdefault(i,{})
               for j,scores in item.items():
                   if j not in i:
                       C[i].setdefault(j,0)
                       C[i][j]+=1
    
       print("---2.构造的共现矩阵---")
       print ('N:',N)
       print ('C:',C)
    
       #2.2 计算物品与物品的相似矩阵
       W={}
       for i,item in C.items():
           W.setdefault(i,{})
           for j,item2 in item.items():
               W[i].setdefault(j,0)
               W[i][j]=C[i][j]/sqrt(N[i]*N[j])
    
       print("---3.构造的相似矩阵---")
       print(W)
       return W
    
    #3.根据用户的历史记录,给用户推荐物品
    def recommandList(data,W,user,k=3,N=10):
       rank={}
       for i,score in data[user].items(): #获得用户user历史记录,如A用户的历史记录为{'a': '1', 'b': '1', 'd': '1'}
    >     # 此处k的作用是只选择与该物品最相似的产品,防止热门数据的干扰
           for j,w in sorted(W[i].items(),key=operator.itemgetter(1),reverse=True)[0:k]:  #获得与物品i相似的k个物品
               if j not in data[user].keys(): #该相似的物品不在用户user的记录里
                   rank.setdefault(j,0)
                   rank[j]+=float(score) * w
    
       print("---4.推荐----")
       print(sorted(rank.items(),key=operator.itemgetter(1),reverse=True)[0:N])
       return sorted(rank.items(),key=operator.itemgetter(1),reverse=True)[0:N]
    
    if __name__=='__main__':
       # 用户,兴趣度,物品
       uid_score_bid = ['A,1,a', 'A,1,b', 'A,1,d', 'B,1,b', 'B,1,c', 'B,1,e', 'B,1,f', 'C,1,c', 'C,1,d', 'D,1,b', 'D,1,c', 'D,1,d',
                        'E,1,a', 'E,1,d', 'E,1,f', 'F,1,a', 'F,1,b', 'F,1,c', 'F,1,d', 'F,1,e', 'F,1,f']
       data=loadData(uid_score_bid)   #获得数据
       W=similarity(data) #计算物品相似矩阵
       recommandList(data,W,'A',3,10) #推荐
    

    基于用户的协同过滤推荐算法与基于项目的协同过滤推荐算法比较

    基于用户的协同过滤推荐算法:可以帮助用户发现新的商品,适用于用户比较少的场景。
    基于物品的协同过滤推荐算法:准确性好,便于离线计算,但推荐结果一般是用户喜欢的商品,不会带给用户惊喜性,适用于物品不太多的场景。

    一般新闻,博客网站的用户可能不太关心自己比较感兴趣的区域,而是热点数据。而且这种网站数据更新十分频繁,物品数据量大。所以这种场景比较适合使用基于用户的过滤算法。
    而在商城或者评分网站,物品数量更新没这么频繁,而且此类网站用户热衷于寻找自己感兴趣的物品,所以更适合基于物品的过滤算法。

    参照:物品协同过滤算法(ItemCF)原理以及案例实战(附完整 Python 代码)
    皮尔逊相关系数python实现
    推荐系统一基本流程讲解
    java开发实现协同过滤算法 协同过滤和算法推荐
    基于用户的协同过滤算法(java)
    ItemCF - 限制"用户-物品倒排表"中物品数量

    相关文章

      网友评论

          本文标题:基于协同过滤实现个性化推荐

          本文链接:https://www.haomeiwen.com/subject/awlttjtx.html