在千里码上找到一题好玩的协同过滤。下面给各位朋友概述一下其原理以及实现
-
什么是协同过滤
协同过滤是利用集体智慧的一个典型方法。要理解什么是协同过滤 (Collaborative Filtering, 简称 CF),首先想一个简单的问题,如果你现在想看个电影,但你不知道具体看哪部,你会怎么做?大部分的人会问问周围的朋友,看看最近有什么好看的电影推荐,而我们一般更倾向于从口味比较类似的朋友那里得到推荐。这就是协同过滤的核心思想。
换句话说,就是借鉴和你相关人群的观点来进行推荐,很好理解。 -
协同过滤的实现
要实现协同过滤的推荐算法,要进行以下三个步骤:
收集数据——找到相似用户和物品——进行推荐
豆瓣电影数据建模
选用一个用来切割,计算,合并数据的包plyr对题目中的文本文件进行处理
(1) 找到与目标用户兴趣相似的用户集合
(2) 找到这个集合中用户喜欢的、并且目标用户没有听说过的物品推荐给目标用户。
- 1.数据清洗
文本文件部分数据如下图所示
Tips:读取文件trains.csv到它创建的数据帧称为text。header = TRUE指定此数据包含一个标题行,sep =“,”表示数据以逗号分隔
text = read.csv('train.csv',sep=',',header=TRUE)
- 2.分别提取用户列表以及电影列表
#提取文本对应列
user = text$userid
item = text$itemid
rate = text$rating
#提取排序用户列表
userUnique<-function(){
users<-unique(text$userid)
users[order(users)]
}
#提取排序商品列表
itemsUnique<-function(){
items<-unique(text$itemid)
items[order(items)]
}
#获取用户列表,商品列表,以及评分列表
users<-userUnique()
items<-itemsUnique()
#建立电影列表索引, 取电影列表中在x中的元素的下标
index<-function(x) which(items %in% x)
#拆分数据帧,应用功能,并将结果返回到数据帧中
data<-plyr::ddply(.data=text, as.quoted(user,item,rate), .fun=summarize, idx=index(text$itemid))
- 3.构建电影同现矩阵
基于物品的同现矩阵:
form.gif
同现矩阵是协同过滤中计算物品相似度最重要的一环,表征物品与物品之间的关联度, 这个关联度由所有用户对所有物品的评分决定
构建思路:基于用户对物品的偏好找到相似的物品,然后根据用户的历史偏好,推荐相似的物品给他。从计算的角度看,就是将所有用户对某个物品的偏好作为一个向量来计算物品之间的相似度,得到物品的相似物品后,根据用户历史的偏好预测当前用户还没有表示偏好的物品,计算得到一个排序的物品列表作为推荐。下图给出了一个例子,对于物品 A,根据所有用户的历史偏好,喜欢物品 A 的用户都喜欢物品 C,得出物品 A 和物品 C 比较相似,而用户 C 喜欢物品 A,那么可以推断出用户 C 可能也喜欢物品C
#电影同现矩阵
cooccurrence<-function(data){
n<-length(items)
#狗血商品协同矩阵
co<-matrix(rep(0,n*n),nrow = n)
#遍历用户列表,但凡电影条目中,存在用户名相同的记录,则标记索引
for(u in users){
idx <- index(data$item[which(data$user==u)])
#每个商品两两出现的集合
m <- merge(idx,idx)
for(i in 1:nrow(m)){
#根据商品的索引找到同现矩阵中的位置进行次数累加
co[m$x[i],m$y[i]] = co[m$x[i],m$y[i]]+1
}
}
return(co)
}
co<-cooccurrence(data)
#生成电影同现矩阵
Tips:windows上使用Rstudio加载plyr的ddply函数可能会出现无法加载的错误,可能是由于加载其他具有函数名称summarize(或summarise)的函数而导致与plyr包冲突。根据解决方法,修改
summarize
为plyr::summarize
- 4.基于物品的协同过滤算法,计算物品相似度
基于物品的协同过滤算法主要分为两步:
picture.png
(1) 计算物品之间的相似度
|N(i)|是喜欢物品i的用户数,|N(j)|是喜欢物品j的用户数,|N(i)&N(j)|是同时喜欢物品i和物品j的用户数。
从上面的定义看出,在协同过滤中两个物品产生相似度是因为它们共同被很多用户喜欢,两个物品相似度越高,说明这两个物品共同被很多人喜欢。
recommand<-function(udata=udata,co=coMatrix,num=0){
n<-length(items)
#统计所有在同现矩阵的索引中出现的用户评分
rates = rep(0,n)
rates[udata$idx]=udata$rating
#构建用户评分矩阵
userx<-matrix(rates,nrow = n)
#同现矩阵*评分矩阵
r<-co %*% userx
}
(2) 根据物品的相似度和用户的历史行为给用户生成推荐列表
simailar.png
如下图,ItemCF通过如下公式计算用户u对一个物品j的兴趣
Puj表示用户u对物品j的兴趣,N(u)表示用户喜欢的物品集合(i是该用户喜欢的某一个物品),S(i,k)表示和物品i最相似的K个物品集合(j是这个集合中的某一个物品),Wji表示物品j和物品i的相似度,Rui表示用户u对物品i的兴趣(这里简化Rui都等于1)。
该公式的含义是:和用户历史上感兴趣的物品越相似的物品,越有可能在用户的推荐列表中获得比较高的排名。
recommand<-function(udata=udata,co=coMatrix,num=0){
n<-length(items)
#统计所有在同现矩阵的索引中出现的用户评分
rates = rep(0,n)
rates[udata$idx]=udata$rating
#构建用户评分矩阵
userx<-matrix(rates,nrow = n)
#同现矩阵*评分矩阵
r<-co %*% userx
#推荐结果排序,并把用户评分过的商品设置为0
r[udata$idx]<-0
idx<-order(r,decreasing = TRUE)
topn<-data.frame(user=rep(udata$user[1],length(idx)),item=items[idx],val=r[idx])
#推荐结果取前num个
if(num>0){
topn<-head(topn,num)
}
return(topn)
}
- 5.计算推荐结果,使用rbind()构建分块矩阵,取N(u)与S(i,k)的交集
recommendation<-data.frame()
for(i in 1:length(users)){
udata<-data[which(data$user==users[i]),]
recommendation<-rbind(recommendation,recommand(udata,co,0))
}
recommendation<-recommendation[which(recommendation$val>0)]
代码清单Clear:
library(plyr)
text = read.csv('train.csv',sep=',',header=TRUE)
#提取文本对应列
user = text$userid
item = text$itemid
rate = text$rating
#train<-data.frame(user,item,rate)
#提取排序用户列表
userUnique<-function(){
users<-unique(text$userid)
users[order(users)]
}
#提取排序商品列表
itemsUnique<-function(){
items<-unique(text$itemid)
items[order(items)]
}
#获取用户列表,商品列表,以及评分列表
users<-userUnique()
items<-itemsUnique()
#建立电影列表索引, 取电影列表中在x中的元素的下标
index<-function(x) which(items %in% x)
#拆分数据帧,应用功能,并将结果返回到数据帧中
data<-plyr::ddply( .data=text, as.quoted(c(user,item,rate)), .fun=summarize, idx=index(text$itemid))
#电影同现矩阵
cooccurrence<-function(data){
n<-length(items)
#狗血商品协同矩阵
co<-matrix(rep(0,n*n),nrow = n)
#遍历用户列表,但凡电影条目中,存在用户名相同的记录,则标记索引
for(u in users){
idx <- index(data$item[which(data$user==u)])
#每个商品两两出现的集合
m <- merge(idx,idx)
for(i in 1:nrow(m)){
#根据商品的索引找到同现矩阵中的位置进行次数累加
co[m$x[i],m$y[i]] = co[m$x[i],m$y[i]]+1
}
}
return(co)
}
co<-cooccurrence(data)
#生成电影同现矩阵
recommand<-function(udata=udata,co=coMatrix,num=0){
n<-length(items)
#统计所有在同现矩阵的索引中出现的用户评分
rates = rep(0,n)
rates[udata$idx]=udata$rating
#构建用户评分矩阵
userx<-matrix(rates,nrow = n)
#同现矩阵*评分矩阵
r<-co %*% userx
#推荐结果排序,并把用户评分过的商品设置为0
r[udata$idx]<-0
idx<-order(r,decreasing = TRUE)
topn<-data.frame(user=rep(udata$user[1],length(idx)),item=items[idx],val=r[idx])
#推荐结果取前num个
if(num>0){
topn<-head(topn,num)
}
return(topn)
}
for(i in 1:length(users)){
udata<-data[which(data$user==users[i]),]
recommendation<-rbind(recommendation,recommand(udata,co,0))
}
recommendation<-recommendation[which(recommendation$val>0)]
网友评论