个性化推荐分为两种方式,一种是利用神经网络实现个性化推荐,另一种是使用协同过滤算法实现个性化推荐。协同过滤算法主要分为两步,第一步协同,比较两者的相似度,然后找出相似度较近的两者,第二步是过滤,过滤出相似两者相同的物品,其他的物品就是用户可能感兴趣的东西。
相似度计算公式
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(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 - 限制"用户-物品倒排表"中物品数量
网友评论