您的位置:首页 > 其它

基于改进的协同过滤算法的用户评分预测模型

2015-05-09 14:01 666 查看
目标:精确预测某目标用户对目标电影M的评分值。

步骤:1得到与目标电影M最相似的K个电影集合ci。

2.基于ci,计算评论过m电影且与目标用户最相似的k2个用户

3.将与目标用户最相似的k2个用户对目标电影M的评分值加权平均值作为目标用户对目标电影评分预测值。

4测试不同k值,不同目标用户对电影M的预测值减目标用户对电影M的实际评分值,结果显示评分误差在0.6左右,说明算法预测精度非常高。

相关公式模型:

1电影相似度计算公式(修正的余弦相似度计算公式)



其中:Uij为同时评论电影ij的所有用户集合,ru,i为用户u对电影i的评分值,/ru为用户u评论的所有电影的评分平均值。

2.用户相似度计算公式



ci为1中计算的与电影i最相似的k个电影集合,u,v代表两个用户,ru,i为用户u对电影i的评分值。可以看到,根据此公式,用户间相似度为0等价于两用户对同一电影评分的值相差1分。

3.评分预测模型



4模型评估方法



实现代码及注释:

#-*- coding: utf-8 -*-
import math
class UserBasedCF:
def __init__(self,datafile = None,k1=300,k2=2000):#构造函数初始化
self.readData(datafile=datafile,k1=k1,k2=k2)#载入数据
self.cacheData()
def readData(self,datafile=None,k1=300,k2=2000):
data = []
for line in open(datafile):
userid,itemid,record,_ = line.strip('\n').split("::")
if (int(userid)<k1)&(int(itemid)<k2):#设置训练集的大小
data.append((userid,itemid,int(record)))#data列表存储用户id,电影id,得分
self.data=data
return  data
def cacheData(self):
self.userItemScore=dict()#{用户:{电影1:评分,电影2:评分2}}
self.user_items = dict()#{用户:set(看过的电影集合)}
self.item_users = dict()#{电影:set(看过该电影的用户集合)}
#仅仅遍历一次数据,形成三个中间结果查询字典
for user,item,rate in self.data:
if user not in self.userItemScore:
self.userItemScore.setdefault(user,{})
self.userItemScore[user][item]=int(rate)
if item not in self.item_users:
self.item_users.setdefault(item,set())
self.item_users[item].add(user)
if user not in self.user_items:
self.user_items.setdefault(user,set())
self.user_items[user].add(item)
'''
#寻找hotmovie
hotmovie={}
for i in self.item_users.keys():
hotmovie.setdefault(i,len(self.item_users[i]))
self.hotmovie=sorted(hotmovie.items(),key = lambda x : x[1],reverse = True)[0:20]
print self.hotmovie
'''
def itemSimBest(self,targetMovie='1210',k=[190,200]):#设置目标电影,与返回的最相似电影个数
self.targetMovie=targetMovie
#计算每个targetmovie以外的电影与movie的相似度
simmj=dict()#存电影m与j的相似度
for j in self.item_users.keys():#j为所有电影
if j==targetMovie:continue
umj=self.item_users[targetMovie]&self.item_users[j]#同时评论过电影mj的用户集合
if umj==set([]):continue#如果没有用户共同评论电影mj则跳出本次循环
#中间结果计算,计算umj中每个用户给出评分的平均值
umjj=dict()#umjj存对电影mj同时进行过评论的用户的平均分
for u in umj:
ucum=0
for i in self.userItemScore[u].items():
ucum+=float(i[1])#所有电影得分相加
umean=ucum/len(self.userItemScore[u])#该用户评分总分/评论的电影数
umjj[u]=umean#用户评分平均分,加入字典
#print(umjj)#与u共同评论了mj电影的用户v有····他们各自的平均评分为   {'75': 3.92, '92': 2.8545454545454545}
#对相似度计算的分子
fenzi=fenmu1=fenmu2=0
for v in umj:#i为用户
fenzi+=(self.userItemScore[v][targetMovie]-umjj[v])*(self.userItemScore[v][j]-umjj[v])
fenmu1+=pow(self.userItemScore[v][targetMovie]-umjj[v],2)
fenmu2+=pow(self.userItemScore[v][j]-umjj[v],2)
if fenmu1*fenmu2==0:simmj[j]=0
else:
sim=fenzi/(math.sqrt(fenmu1)*math.sqrt(fenmu2))
simmj[j]=sim  #m与j项目相似度为sim
#字典排序,取出与m最相似的k个电影
simmj=sorted(simmj.items(),key = lambda x : x[1],reverse = True)
if len(simmj)<max(k):print "K值不能大于simmmj集合的元素个数————simmj集合的个数为"+str((len(simmj)))
simmjkDict={}#存放不同k值,对应的最相似电影集合
for i in k:
simmjkDict[i]=dict(simmj[0:i])#i为k
return simmjkDict

def userSimBest(self,simmjkDict,simk=0.00001):
self.rightSimTargetuuDict={}#存放不同K值下,符合条件的目标用户与其多个相似用户的相似值
for key in simmjkDict.keys():#key就是topk这个列表
simmjk=simmjkDict[key]
simmjk=set(simmjk.keys())#准备ci
#print 'simmjk'
#print len(simmjk)
userm=list(self.item_users[self.targetMovie])#获取评论过目标电影的用户集合
rightSimTargetuu={}#有的tagetu的ci为空集,所以需要找到ci不为空集的targetu结果如图
#print 'len user m'
#print len(userm)
for targetu in userm:#找到符合条件的targetu
simtargetuu={}#找到与tagetu最相似的几个用户
for u in userm:#评论过目标电影的集合
if targetu==u:continue
ci=self.user_items[targetu]&self.user_items[u]&simmjk
if len(ci)>5:
cum=0.0
for a in ci:
cum+=pow((self.userItemScore[targetu][a]-self.userItemScore[u][a]),2)
simtargetuu[u]=1.0-(cum*1.0)/len(ci)
if len(simtargetuu)!=0:rightSimTargetuu[targetu]=simtargetuu
#print rightSimTargetuu
#因为ci设置过小,会使得有的用户相似度计算结果为负或者0这种相似度值当然不适合预测,所以需要去除奇异值
for u in rightSimTargetuu.keys():
for v in rightSimTargetuu[u].keys():
if rightSimTargetuu[u][v]<simk:del rightSimTargetuu[u][v]#设定相似度阈值
for u in rightSimTargetuu.keys():#删除{‘151’:{}}这样的空值
if rightSimTargetuu[u]=={}:  del rightSimTargetuu[u]
#print 'del sim dic'
#print rightSimTargetuu
#self.rightSimTargetuu=rightSimTargetuu
if len(rightSimTargetuu)==0:print "对k="+str(key)+"  没有匹配的相似用户,需要降低相似度阈值or增加K or 改变目标电影"
self.rightSimTargetuuDict[key]=rightSimTargetuu
#print self.rightSimTargetuuDict.keys()
#print self.rightSimTargetuuDict[200]

def predictAndEvaluation(self):
m=self.targetMovie
K_MAE_Dict={}
for key in self.rightSimTargetuuDict.keys():
rightSimTargetuu=self.rightSimTargetuuDict[key]
predictTargetScore={}
for u in rightSimTargetuu.keys():
fenzi=fenmu=0.0
for v in rightSimTargetuu[u].keys():
fenzi+=rightSimTargetuu[u][v]*self.userItemScore[v][m]
fenmu+=rightSimTargetuu[u][v]

predictTargetScore[u]=fenzi*1.0/fenmu
#test result
cum=0.0
for i in predictTargetScore.keys():
cum+=abs(predictTargetScore[i]-self.userItemScore[i][m])
if len(predictTargetScore.keys())==0:break
MAE=math.sqrt(cum/len(predictTargetScore.keys()))
K_MAE_Dict[key]=MAE
K_MAE_Dict=sorted(K_MAE_Dict.items(),key = lambda x : x[0],reverse = False)
print "对目标电影  "+str(self.targetMovie)+"  不同k值对应的MAE值"
print K_MAE_Dict

def testModel():
cf=UserBasedCF('ratings.dat',k1=300,k2=2000)#载入数据文件。由于原始数据集是由6040个用户对3900个电影评论产生的逾100万条电影评论数据
#原始数据较大,故可以使用k1,k2参数获取原始数据子集进行模型效果初步检验
#k1代表用户id范围,k2代表电影id范围(0<k1<6040,0<k2<3900)
print "数据样例:"
print cf.data[0:6]#打印部分数据样例
print "载入的数据条目数量"+str(len(cf.data))
k=[190,200,205,210,]#设定与目标电影最相似的k个电影数值。当k<<number(共同评论目标电影m与电影j的人数数量) Top-k个电影
simmjkDict=cf.itemSimBest(targetMovie='260',k=k)#求目标电影的K-Top个最相似电影
#最好选热门电影,可以的热门电影有:1196、1210、260、1198、593、110、1265、589、318、1197、608、296、527、1617、1
cf.userSimBest(simmjkDict,simk=0.00001)#simk为用户相似度的阈值。根据你的相似度计算公式,用户相似度值大于0,即相似度很高,不建议修改该默认阈值
cf.predictAndEvaluation()

if __name__ == "__main__":
testModel()


实现结果:



合作者:湖南大学 李剑锋
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: