协同过滤(Collaborative Filtering,简写CF)简单来说就是根据已有数据来推测未知的数据的一种算法。在推荐系统中协同过滤算法一般是指在海量用户中发掘一小部分和你品味比较类似的,在协同过滤中,这些用户成为你的邻居,从而基于这些邻居的信息为你推荐商品。基于启发式的协同过滤算法可以分为基于用户的协同过滤算法(User-Based)和基于物品的协同过滤算法(Item-Based)。
基于用户的协同过滤算法(User-Based)
'''
基于用户的推荐算法
'''
from math import sqrt,pow
import operator
class UserCf():
#获得初始化数据,计算每个用户的评分的平均值
def __init__(self,data):
self.data=data
self.ave = {}
self.max = 0
for key,value in self.data.items():
sum1 = 0.0
for item,score in value.items():
if(int(item)>self.max):
self.max = int(item)
sum1 += score
self.ave[key] = sum1/len(data[key])
#1.获取用户待预测分数的相关用户,如找到用户C中待评分商品4的预测分数相关用户A,D
def find_user(self,item_goal):
user_goal = []
for key,values in self.data.items():
for key1,value1 in values.items():
if(key1 == item_goal):
user_goal.append(key)
break
return user_goal
#2.计算待预测分数中相关用户中两个用户之间的皮尔逊相关系数
def pearson(self,user1,item_goal):#数据格式为:商品,评分 A:{'a': 4.0, 'c': 3.0, 'd': 5.0
user_goal = self.find_user(item_goal)
denominator1 = 0.0 #分母1--待预测用户的分母1
denominator2 = 0.0 #分母2--相关用户的分母2
molecule = 0.0 #分子
r = {} #皮尔逊系数字典
try:
for user2 in user_goal:
for item_, score_ in self.data[user2].items():
for item1,score1 in self.data[user1].items():
if(item_ == item1):
molecule += (float(score_)-self.ave[user2])*(float(score1)-self.ave[user1])
denominator1 += pow(float(score1)-self.ave[user1],2)
denominator2 += pow(float(score_)-self.ave[user2],2)
r.setdefault(user1, {})
r[user1].setdefault(user2,0)
r[user1][user2] = (molecule)/sqrt(denominator1*denominator2)
molecule = 0.0
denominator1 = 0.0
denominator2 = 0.0
except e:
print("异常信息:",e.message)
return None
return r#返回相关用户的皮尔逊系数
#3.根据皮尔逊系数预测评分
def prediction(self,user1, item_goal):
ave1 = self.ave[user1]
r = self.pearson(user1,item_goal)
user_goal = self.find_user(item_goal)
anw1 = 0.0
anw2 = 0.0
for user in user_goal:
anw1 += r[user1][user]*((self.data[user][item_goal])-(self.ave[user]))
anw2 += abs(r[user1][user])
predict = ave1 + anw1/anw2
self.data[user1][item_goal] = round(predict,2)
#扫描数据集,收集未填充数据
def scan(self):
item_map = [str(i) for i in range(1,self.max+1)]
for user,value in self.data.items():
item_list = []
for m,n in value.items():
item_list.append(m)
for item in item_map:
if(item not in item_list):
self.prediction(user,item)
for user, value in self.data.items():
self.data[user] = sorted(self.data[user].items(),key = lambda d:d[0])
return self.data
if __name__=='__main__':
users = {'A': {'1': 4.0, '3': 3.0, '4': 5.0},
'B': {'2': 5.0, '3': 4.0},
'C': {'1': 5.0, '2': 4.0,'3': 2.0},
'D': {'1': 2.0, '2': 4.0,'4': 3.0},
'E': {'1': 3.0, '2': 4.0,'3': 5.0},
}
userCf=UserCf(data=users)
recommandList=userCf.scan()
print("协同过滤后的评分矩阵")
for key,value in recommandList.items():
print(key,value)
本算法采用简单的皮尔逊相关系数实现协同过滤
#!/usr/bin/python
#基于用户的推荐算法
from math import sqrt,pow
import operator
class UserCf():
#获得初始化数据
def __init__(self,data):
self.data=data;
#通过用户名获得电影列表,仅调试使用
def getItems(self,username1,username2):
return self.data[username1],self.data[username2]
#计算两个用户的皮尔逊相关系数
def pearson(self,user1,user2):#数据格式为:电影,评分 {'Snakes on a Plane': 4.5, 'You, Me and Dupree': 1.0, 'Superman Returns': 4.0}
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)
molecule=sumXY-(sumX*sumY)/n;
denominator=sqrt((sumX2-pow(sumX,2)/n)*(sumY2-pow(sumY,2)/n))
r=molecule/denominator
except Exception,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]
#给用户推荐电影
def recomand(self,username,n=1):
recommand={};#待推荐的电影
for user,score in dict(self.nearstUser(username,n)).items():#最相近的n个用户
print "推荐的用户:",(user,score)
for movies,scores in self.data[user].items():#推荐的用户的电影列表
if movies not in self.data[username].keys():#当前username没有看过
print "%s为该用户推荐的电影:%s"%(user,movies)
if movies not in recommand.keys():#添加到推荐列表中
recommand[movies]=scores
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', 2)
print("最终推荐:%s"%recommandList)
基于物品的协同过滤算法(Item-Based)
```python
#/python/Item_CF.py
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.用户:物品的倒排----")
print(data)
return data
def loadData2(files):
data={}
for line in files:
user,item,score,timestamp=line.split(",")
data.setdefault(user,{})
data[user][item]=score
print("----1.用户:物品的倒排----")
print(data)
return data
#2.计算
#2.1 构造物品-->物品的共现矩阵
#2.2 计算物品与物品的相似矩阵
#(这里采用的是余弦相似度算法计算的物品间的相似度)
def similarity(data):
# 2.1 构造物品:物品的共现矩阵
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'}
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__':
# 用户,兴趣度,物品
# 实例1
uid_score_bid = ['A,1,a', 'A,1,b', 'A,1,d', 'B,1,b', 'B,1,c', 'B,1,e', 'C,1,c', 'C,1,d', 'D,1,b', 'D,1,c', 'D,1,d',
'E,1,a', 'E,1,d']
data=loadData(uid_score_bid) #获得数据
W=similarity(data) #计算物品相似矩阵
recommandList(data,W,'A',3,10) #推荐
# 实例2
users2 = []
fp_2 = open("u.data", "r", encoding='utf-8')
for line2 in fp_2.readlines():
line_2=line2.replace("\t",",")
lines2 = line_2.strip().split("\n")
users2+=lines2
data2 = loadData2(users2) # 获得数据
W2 = similarity(data2) # 计算物品相似矩阵
recommandList(data2, W3, '160', 3, 20) # 推荐
总结
CF算法分为两大类,一类为基于memory的(Memory-based),另一类为基于Model的(Model-based),User-based和Item-based算法均属于Memory-based类型,具体细分类可以参考wikipedia的说明。
User-based的基本思想是如果用户A喜欢物品a,用户B喜欢物品a、b、c,用户C喜欢a和c,那么认为用户A与用户B和C相似,因为他们都喜欢a,而喜欢a的用户同时也喜欢c,所以把c推荐给用户A。该算法用最近邻居(nearest-neighbor)算法找出一个用户的邻居集合,该集合的用户和该用户有相似的喜好,算法根据邻居的偏好对该用户进行预测。
User-based算法存在两个重大问题:
数据稀疏性。一个大型的电子商务推荐系统一般有非常多的物品,用户可能买的其中不到1%的物品,不同用户之间买的物品重叠性较低,导致算法无法找到一个用户的邻居,即偏好相似的用户。
算法扩展性。最近邻居算法的计算量随着用户和物品数量的增加而增加,不适合数据量大的情况使用。
Iterm-based的基本思想是预先根据所有用户的历史偏好数据计算物品之间的相似性,然后把与用户喜欢的物品相类似的物品推荐给用户。还是以之前的例子为例,可以知道物品a和c非常相似,因为喜欢a的用户同时也喜欢c,而用户A喜欢a,所以把c推荐给用户A。
因为物品直接的相似性相对比较固定,所以可以预先在线下计算好不同物品之间的相似度,把结果存在表中,当推荐时进行查表,计算用户可能的打分值,可以同时解决上面两个问题。
参考
https://blog.csdn.net/qq_26274961/article/details/117881061