http://my.oschina.net/zhangjiawen/blog/185625
1基于用户的协同过滤算法:
基于用户的协同过滤算法是推荐系统中最古老的的算法,可以说是这个算法的诞生标志了推荐系统的诞生。该算法在1992年被提出,并应用于邮件过滤系统,1994年被GroupLens用于新闻过滤。
在一个在线个性化推荐系统中,当一个用户A需要个性化推荐时,可以先找到和他有相似兴趣的其他用户,然后把那些用户喜欢的而用户A没有接触过的物品推荐给A。这种方法称为基于用户的协同过滤算法。
给定用户u和用户v,令N(u)表示用户u曾经有过正反馈的物品集合,通过余弦相似度计算用户的相似度。由于很多用户相互之间并没有对同样的物品产生过行为,即
,因此可以先计算
的用户对(u,v)。为此,可以首先建立物品到用户的倒查表,对于每个物品保存对该物品产生过行为的用户列表。令稀疏矩阵
,假设用户u和用户v同时属于倒查表中K个物品对应的用户列表,就有
(即用户u和v对相同物品产生正反馈的物品数),从而可以扫描倒查表中每个物品对应的用户列表,将用户列表中的两两用户对应的
加1,最终就可以获得所有用户之间不为0的
(也就是余弦相似度的分子)。
得到用户之间的兴趣相似度之后,基于用户的协同过滤算法(User Based Collaborative Filering)会给用户推荐和他兴趣最相似的K个用户喜欢的物品。如下公式度量了UserCF算法中用户u对物品i的感兴趣程度:

其中,S(u,k)包含和用户u兴趣相似度最接近的k个用户集合,N(i)是对物品i有过行为的用户集合,
是用户u和用户v的兴趣相似度,
代表用户v对物品i的兴趣,因为使用的是单一行为的隐反馈数据,因此
为1。
根据以上思路,使用Python实现UserCF算法的代码如下:
004 |
def __init__(self,datafile = None): |
005 |
self.datafile = datafile |
009 |
def readData(self,datafile = None): |
011 |
read the data from the data file which is a data set |
013 |
self.datafile = datafile or self.datafile |
015 |
for line in open(self.datafile): |
016 |
userid,itemid,record,_ = line.split() |
017 |
self.data.append((userid,itemid,int(record))) |
019 |
def splitData(self,k,seed,data=None,M = 8): |
022 |
testdata is a test data set |
023 |
traindata is a train set |
024 |
test data set / train data set is 1:M-1 |
028 |
data = data or self.data |
030 |
for user,item, record in self.data: |
031 |
if random.randint(0,M) == k: |
032 |
self.testdata.setdefault(user,{}) |
033 |
self.testdata[user][item] = record |
035 |
self.traindata.setdefault(user,{}) |
036 |
self.traindata[user][item] = record |
038 |
def userSimilarityBest(self,train = None): |
040 |
the other method of getting user similarity which is better than above |
041 |
you can get the method on page 46 |
042 |
In this experiment,we use this method |
044 |
train = train or self.traindata |
045 |
self.userSimBest = dict() |
047 |
for u,item in train.items(): |
048 |
for i in item.keys(): |
049 |
item_users.setdefault(i,set()) |
051 |
user_item_count = dict() |
053 |
for item,users in item_users.items(): |
055 |
user_item_count.setdefault(u,0) |
056 |
user_item_count[u] += 1 |
059 |
count.setdefault(u,{}) |
060 |
count[u].setdefault(v,0) |
062 |
for u ,related_users in count.items(): |
063 |
self.userSimBest.setdefault(u,dict()) |
064 |
for v, cuv in related_users.items(): |
065 |
self.userSimBest[u][v] = cuv / math.sqrt(user_item_count[u] * user_item_count[v] * 1.0) |
067 |
def recommend(self,user,train = None,k = 8,nitem = 40): |
068 |
train = train or self.traindata |
070 |
interacted_items = train.get(user,{}) |
071 |
for v ,wuv in sorted(self.userSimBest[user].items(),key = lambda x : x[1],reverse = True)[0:k]: |
072 |
for i , rvi in train[v].items(): |
073 |
if i in interacted_items: |
077 |
return dict(sorted(rank.items(),key = lambda x :x[1],reverse = True)[0:nitem]) |
079 |
def recallAndPrecision(self,train = None,test = None,k = 8,nitem = 10): |
081 |
Get the recall and precision, the method you want to know is listed |
084 |
train = train or self.traindata |
085 |
test = test or self.testdata |
089 |
for user in train.keys(): |
090 |
tu = test.get(user,{}) |
091 |
rank = self.recommend(user, train = train,k = k,nitem = nitem) |
092 |
for item,_ in rank.items(): |
097 |
return (hit / (recall * 1.0),hit / (precision * 1.0)) |
099 |
def coverage(self,train = None,test = None,k = 8,nitem = 10): |
100 |
train = train or self.traindata |
101 |
test = test or self.testdata |
102 |
recommend_items = set() |
104 |
for user in train.keys(): |
105 |
for item in train[user].keys(): |
107 |
rank = self.recommend(user, train, k = k, nitem = nitem) |
108 |
for item,_ in rank.items(): |
109 |
recommend_items.add(item) |
110 |
return len(recommend_items) / (len(all_items) * 1.0) |
112 |
def popularity(self,train = None,test = None,k = 8,nitem = 10): |
115 |
the algorithm on page 44 |
117 |
train = train or self.traindata |
118 |
test = test or self.testdata |
119 |
item_popularity = dict() |
120 |
for user ,items in train.items(): |
121 |
for item in items.keys(): |
122 |
item_popularity.setdefault(item,0) |
123 |
item_popularity[item] += 1 |
126 |
for user in train.keys(): |
127 |
rank = self.recommend(user, train, k = k, nitem = nitem) |
128 |
for item ,_ in rank.items(): |
129 |
ret += math.log(1+item_popularity[item]) |
131 |
return ret / (n * 1.0) |
133 |
def testUserBasedCF(): |
134 |
cf = UserBasedCF('u.data') |
135 |
cf.userSimilarityBest() |
136 |
print "%3s%20s%20s%20s%20s" % ('K',"recall",'precision','coverage','popularity') |
137 |
for k in [5,10,20,40,80,160]: |
138 |
recall,precision = cf.recallAndPrecision( k = k) |
139 |
coverage = cf.coverage(k = k) |
140 |
popularity = cf.popularity(k = k) |
141 |
print "%3d%19.3f%%%19.3f%%%19.3f%%%20.3f" % (k,recall * 100,precision * 100,coverage * 100,popularity) |
143 |
if __name__ == "__main__": |
2 基于物品的协同过滤算法:
基于物品的协同过滤算法(Item-Based Collaborative Filtering)是目前业界应用最多的算法,亚马逊、Netflix、Hulu、YouTube都采用该算法作为其基础推荐算法。
基于用户的协同过滤算法有一些缺点:随着网站的用户数目越来越大,计算用户兴趣相似度矩阵将越来越困难,其运算时间复杂度和空间复杂度的增长和用户数的增长近似平方关心。并且,基于用户的协同过滤算法很难对推荐结果做出解释。因此亚马逊提出了基于物品的协同过滤算法。
基于物品的协同过滤算法给用户推荐那些和他们之前喜欢的物品相似的物品。不过ItemCF算法并不利用物品的内容属性计算物品之间的相似度,它主要通过分析用户的行为记录计算用户之间的相似度,也就是说物品A和物品B具有很大的相似度是因为喜欢物品A的用户大都也喜欢物品B(这一点也是基于物品的协同过滤算法和基于内容的推荐算法最主要的区别)。同时,基于物品的协同过滤算法可以利用用户的历史行为给推荐结果提供推荐解释,用于解释的物品都是用户之前喜欢的或者购买的物品。
“Customers Who Bought This Item Also Bought”(亚马逊显示相关物品推荐时的标题),从这句话的定义出发,利用以下公式定义物品之间的相似度:

其中N(i)是喜欢物品i的用户数,分子
是同时喜欢物品i和物品j的用户数。这个公式惩罚了物品j的权重,减轻了热门物品会和很多物品相似的可能性(这样wij的值会很大,接近于1)。这个公式说明两个物品产生相似度是因为它们共同被很多用户喜欢,也就是说每个用户都可以通过他们的历史兴趣列表给物品“贡献”相似度。
和UserCF算法类似,用ItemCF算法计算物品相似度时也可以首先建立用户-物品倒排表,即对每个用户建立一个包含他喜欢的物品的列表,然后对于每个用户,将他物品列表中的物品两两在共现矩阵C中加1,最终就可以得到所有物品之间不为0的
,也就是公式中的分子。
在得到物品之间的相似度后,ItemCF通过如下公式计算用户u对一个物品i的兴趣:

其中,N(u)是用户喜欢的物品集合,S(i,k)是和物品i最相似的k个物品的集合,
是物品j和物品i的相似度,
是用户u对物品i的兴趣,对于隐反馈数据集,如果用户u对物品i有过行为,即可令
为1。
根据以上思路,使用Python实现ItemCF算法的代码如下:
004 |
def __init__(self, datafile = None): |
005 |
self.datafile = datafile |
009 |
def readData(self,datafile = None): |
010 |
self.datafile = datafile or self.datafile |
012 |
file = open(self.datafile,'r') |
013 |
for line in file.readlines()[0:100*1000]: |
014 |
userid, itemid, record,_ = line.split() |
015 |
self.data.append((userid,itemid,int(record))) |
017 |
def splitData(self,k,seed,data=None,M = 8): |
020 |
data = data or self.data |
022 |
for user,item,record in self.data: |
023 |
if random.randint(0,7) == k: |
024 |
self.testdata.setdefault(item,{}) |
025 |
self.testdata[item][user] = record |
027 |
self.traindata.setdefault(item,{}) |
028 |
self.traindata[item][user] = record |
030 |
def ItemSimilarity(self, train = None): |
031 |
train = train or self.traindata |
032 |
self.itemSim = dict() |
034 |
item_user_count = dict() #item_user_count{item: likeCount} the number of users who like the item |
035 |
count = dict() #count{i:{j:value}} the number of users who both like item i and j |
036 |
for user, item in train.items(): #initialize the user_items{user: items} |
037 |
for i in item.keys(): |
038 |
item_user_count.setdefault(i,0) |
039 |
item_user_count[i] += 1 |
040 |
for j in item.keys(): |
043 |
count.setdefault(i,{}) |
044 |
count[i].setdefault(j,0) |
046 |
for i, related_items in count.items(): |
047 |
self.itemSim.setdefault(i,dict()) |
048 |
for j, cuv in related_items.items(): |
049 |
self.itemSim[i].setdefault(j,0) |
050 |
self.itemSim[i][j] = cuv / math.sqrt(item_user_count[i] * item_user_count[j] * 1.0) |
052 |
def recommend(self,user,train = None, k = 8, nitem = 40): |
053 |
train = train or self.traindata |
055 |
ru = train.get(user,{}) |
056 |
for i,pi in ru.items(): |
057 |
for j,wj in sorted(self.itemSim[i].items(), key = lambda x:x[1], reverse = True)[0:k]: |
062 |
#print dict(sorted(rank.items(), key = lambda x:x[1], reverse = True)[0:nitem]) |
063 |
return dict(sorted(rank.items(), key = lambda x:x[1], reverse = True)[0:nitem]) |
065 |
def recallAndPrecision(self,train = None,test = None,k = 8,nitem = 10): |
066 |
train = train or self.traindata |
067 |
test = test or self.testdata |
071 |
for user in train.keys(): |
072 |
tu = test.get(user,{}) |
073 |
rank = self.recommend(user,train = train,k = k,nitem = nitem) |
074 |
for item,_ in rank.items(): |
079 |
return (hit / (recall * 1.0),hit / (precision * 1.0)) |
081 |
def coverage(self,train = None,test = None,k = 8,nitem = 10): |
082 |
train = train or self.traindata |
083 |
test = test or self.testdata |
084 |
recommend_items = set() |
086 |
for user in train.keys(): |
087 |
for item in train[user].keys(): |
089 |
rank = self.recommend(user, train, k = k, nitem = nitem) |
090 |
for item,_ in rank.items(): |
091 |
recommend_items.add(item) |
092 |
return len(recommend_items) / (len(all_items) * 1.0) |
094 |
def popularity(self,train = None,test = None,k = 8,nitem = 10): |
097 |
the algorithm on page 44 |
099 |
train = train or self.traindata |
100 |
test = test or self.testdata |
101 |
item_popularity = dict() |
102 |
for user ,items in train.items(): |
103 |
for item in items.keys(): |
104 |
item_popularity.setdefault(item,0) |
105 |
item_popularity[item] += 1 |
108 |
for user in train.keys(): |
109 |
rank = self.recommend(user, train, k = k, nitem = nitem) |
110 |
for item ,_ in rank.items(): |
111 |
ret += math.log(1+item_popularity[item]) |
113 |
return ret / (n * 1.0) |
116 |
ubcf = ItemBasedCF('u.data') |
118 |
ubcf.splitData(4,100) |
119 |
ubcf.ItemSimilarity() |
121 |
rank = ubcf.recommend(user,k = 3) |
122 |
for i,rvi in rank.items(): |
123 |
items = ubcf.testdata.get(user,{}) |
124 |
record = items.get(i,0) |
125 |
print "%5s: %.4f--%.4f" %(i,rvi,record) |
127 |
def testItemBasedCF(): |
128 |
cf = ItemBasedCF('u.data') |
130 |
print "%3s%20s%20s%20s%20s" % ('K',"recall",'precision','coverage','popularity') |
131 |
for k in [5,10,20,40,80,160]: |
132 |
recall,precision = cf.recallAndPrecision( k = k) |
133 |
coverage = cf.coverage(k = k) |
134 |
popularity = cf.popularity(k = k) |
135 |
print "%3d%19.3f%%%19.3f%%%19.3f%%%20.3f" % (k,recall * 100,precision * 100,coverage * 100,popularity) |
137 |
if __name__ == "__main__": |
3 UserCF和ItemCF的综合比较:
UserCF给用户推荐那些和他有共同兴趣爱好的用户喜欢的物品,而ItemCF给用户推荐那些和他之前喜欢的物品类似的物品。从这个原理可以看到,UserCF的推荐结果着重于反映和用户兴趣相似的小群体的热点,而ItemCF的推荐结果着重于维系用户的历史兴趣。UserCF的推荐更社会化,反映了用户所在的小型兴趣群体中物品的热门程度,而ItemCF的推荐更加个性化,反映了用户自己的兴趣传承。同时,从技术上来说,UserCF需要维护一个用户相似度的矩阵,而ItemCF需要维护一个物品相似度矩阵。从存储的角度来说,如果用户很多,那么维护用户兴趣相似度矩阵需要很大的空间,同理,如果物品很多,维护物品相似度矩阵代价较大。
对于UserCF和ItemCF,我们采用http://www.grouplens.org/node/73 的数据集进行测试,使用准确率/召回率、覆盖率和流行度对实验结果进行评测。
对用户u推荐N个物品R(u),令用户u在测试集上喜欢的物品集合为T(u),则:


召回率描述有多少比例的用户-物品评分记录包含在最终的推荐列表中,而准确率描述最终的推荐列表中有多少比例是发生过的用户-物品评分记录。

覆盖率表示最终的推荐列表中包含多大比例的物品,如果所有的物品都被推荐给至少一个用户,那么覆盖率就是100%。
最后还需要评测推荐的新颖度,这里用推荐列表中物品的平均流行度度量推荐结果的新颖度,如果推荐出的物品都很热门,说明推荐的新颖度较低,否则说明推荐结果比较新颖。

图1 UserCF实验结果

图2 ItemCF实验结果
对于以上UserCF和ItemCF的实验结果可以看出,推荐系统的精度指标(准确率和召回率)并不和参数k成线性关系。推荐结果的精度对k也不是特别敏感,只要选在一定的区域内,就可以获得不错的精度。
对于覆盖率而言,k越大则推荐结果的覆盖率越低,覆盖率的降低是因为流行度的增加,随着流行度增加,推荐算法越来越倾向于推荐热门的物品,这是因为k决定了推荐算法在做推荐时参考多少和你兴趣相似的其他用户的兴趣,如果k越大,参考的人或者物品越多,结果就越来越趋近于全局热门的物品。
- 推荐系统第2周--itemCF和userCF
推荐系统分类 基于应用领域分类:电子商务推荐,社交好友推荐,搜索引擎推荐,信息内容推荐基于设计思想:基于协同过滤的推荐,基于内容的推荐,基于知识的推荐,混合推荐基于使用何种数据:基于用户行为数据的推荐 ...
- 推荐系统之隐语义模型(LFM)
LFM(latent factor model)隐语义模型,这也是在推荐系统中应用相当普遍的一种模型.那这种模型跟ItemCF或UserCF的不同在于: 对于UserCF,我们可以先计算和目标用户兴趣 ...
- 推荐系统之隐语义模型LFM
LFM(latent factor model)隐语义模型,这也是在推荐系统中应用相当普遍的一种模型.那这种模型跟ItemCF或UserCF的不同在于: 对于UserCF,我们可以先计算和目标用户兴趣 ...
- 推荐系统-协同过滤在Spark中的实现
作者:vivo 互联网服务器团队-Tang Shutao 现如今推荐无处不在,例如抖音.淘宝.京东App均能见到推荐系统的身影,其背后涉及许多的技术.本文以经典的协同过滤为切入点,重点介绍了被工业界广 ...
- 推荐系统——online(上)
框架介绍 上一篇从总体上介绍了推荐系统,推荐系统online和offline是两个组成部分,其中offline负责数据的收集,存储,统计,模型的训练等工作:online部分负责处理用户的请求,模型数据 ...
- Mahout介绍和简单应用
Mahout学习(主要学习内容是Mahout中推荐部分的ItemCF.UserCF.Hadoop集群部署运行) 1.Mahout是什么? Mahout是一个算法库,集成了很多算法. Apache Ma ...
- 推荐系统第5周--- 基于内容的推荐,隐语义模型LFM
基于内容的推荐
- 基于Spark机器学习和实时流计算的智能推荐系统
概要: 随着电子商务的高速发展和普及应用,个性化推荐的推荐系统已成为一个重要研究领域. 个性化推荐算法是推荐系统中最核心的技术,在很大程度上决定了电子商务推荐系统性能的优劣,决定着是否能够推荐用户真正 ...
- 大数据学习路线copy自淘宝
一.hadoop视频学习(入门到精通) 二.数据挖掘(入门到精通) 三.Hadoop学习路线 1.开发前期准备 首先,如果你没有Java和Linux基础,建议你先简单学一下这两门课程,此宝贝里面都为你 ...
随机推荐
- brew安装sshpass
有以下解决方法: # 1 brew install https://raw.githubusercontent.com/kadwanev/bigboybrew/master/Library/Formu ...
- PHPWord 打印 快递单/合同
打印快递单有个特点: 被打印纸的背景是固定的, 你只能 在合适的位置输入快递单的内容,操作步骤如下: 1.制作 word 模板 参考文章 “图解如何用打印机套打快递单” 2.在 模板 中放置“占位符” ...
- Why I Left the .NET Framework
The .NET Framework was good. Really good. Until it wasn't. Why did I leave .NET? In short, it constr ...
- 清华梦的粉碎—写给清华大学的退学申请(转自王垠Blog)
清华梦的诞生 小时候,妈妈给我一个梦.她指着一个大哥哥的照片对我说,这是爸爸的学生,他考上了清华大学,他是我们中学的骄傲.长大后,你也要进入清华大学读书,为我们家争光.我不知道清华是什么样子,但是我知 ...
- eclipse配置问题汇总
问题1:fatjar安装出现故障 问题描写叙述:由于要打包包括第三方jar包的工程,需下载eclipse插件.一般下载地址:http://sourceforge.net/projects/fjep/ ...
- 在ASP.NET MVC中使用Knockout实践06,自定义验证、异步验证
在上一篇中体验了Knockout.Validation的基本验证,本篇体验自定义验证和异步验证. 自定义验证规则 ko.validation有一个rules属性,专门用来存放验证规则,它是一个键值对集 ...
- ASP.NET Web API实践系列02,在MVC4下的一个实例, 包含EF Code First,依赖注入, Bootstrap等
本篇体验在MVC4下,实现一个对Book信息的管理,包括增删查等,用到了EF Code First, 使用Unity进行依赖注入,前端使用Bootstrap美化.先上最终效果: →创建一个MVC4项目 ...
- iOS非ARC内存管理摘要 - 实践型
关于ios内存管理.在开发过程中,内存管理很重要,我简单说明一下. 1.正确用法 UIView *v = [[UIView alloc] init]; //分配后引用计数为1 [self.view a ...
- Python index()方法
Python index()方法 Python 字符串 描述 Python index() 方法检测字符串中是否包含子字符串 str ,如果指定 beg(开始) 和 end(结束) 范围,则检查是否 ...
- android studio# jdk8# class file for java.lang.invoke.MethodType not found
https://github.com/evant/gradle-retrolambda/issues/23 class file for java.lang.invoke.MethodType not ...