推荐系统在现实生活中应用甚广,电商、社区、社交平台,无不充斥这各种各样的推荐。其实,推荐的原理很简单,无论是给人推荐物,还是给人推荐人,都是无外乎找到和被推荐者具有相同特质的其他用户,看看那些用户结交/买了/浏览了哪些被推荐者没有光顾过的人/商品/书籍/电影/音乐/etc.,将其推荐给被推荐者。
下面我们假设下列文件是一个电影网站的内部记录:
uid,m1, m2, m3, m4, m5, m6, m7, m8, m9, m10
1,5,3,0,4,0,0,1,2,4,4
2,3,1,2,0,0,2,0,0,1,2
3,4,0,1,2,1,0,1,2,4,0
4,0,2,3,0,0,4,2,1,1,2
5,1,3,4,1,2,0,0,0,0,2
6,2,0,0,0,2,3,5,1,4,0
7,4,1,1,0,2,0,1,0,0,3
8,1,2,0,3,0,0,2,1,0,3
9,3,1,0,3,0,4,0,0,1,0
10,2,1,3,1,0,1,0,0,2,1
这个网站总共有10个用户,文件第一列是这些用户的id。该网站提供给用户一共10部电影,供用户打分。文件中第2到11列每列对应一部电影,i行j列就是用户i给电影j打的分。分数限制在1-5区间,如果为0,则说明i并没有给j打过分。
我们现在要做的是,写一个程序,判断某个用户没有打分的那些电影中,有哪些可能是TA感兴趣的。
对于用户i,在推荐给TA电影之前,我们首先要判断,TA和其他用户之间的共性有多强。也就是对所有这10个用户,两两计算相似度。
相似度的计算方法有很多,常用的分为3类:欧氏距离,余弦相似度,皮尔逊相关系数。这里我们采用余弦距离——所谓余弦距离就是利用两个向量之间夹角的余弦值来衡量两个向量之间的相似度,显然夹角越小,余弦值就越大(取值在0-1之间),两个向量就越靠近,即二者越相似。
首先,我们打开R,读入该文件:
> mydata = read.csv(".\\rec_1.csv", header =TRUE)
> rownum = nrow(mydata);rownum
[1] 10
> colnum = ncol(mydata);colnum
[1] 11
要计算余弦距离,就要首先安装一个新的包:lsa。
> install.packages("lsa")
> library(lsa)
然后我们开始计算用户两两之间的余弦距离:
> cossimilar<-function(ui,uj) {
+ indexlist <-list()
+ for (i in 2:colnum){
+ if (mydata[ui,i]> 0 && mydata[uj,i] > 0) {
+ indexlist <-c(indexlist,i)
+ }
+ }
+ indexlen =length(indexlist)
+ if (indexlen < (colnum - 1) * 0.3) {
+ 0
+ } else {
+ tmpui <-vector(length = indexlen)
+ tmpuj <-vector(length = indexlen)
+ for (i in 1:indexlen) {
+ tmpui[i] = mydata[ui,indexlist[[i]]]
+ tmpuj[i] =mydata[uj,indexlist[[i]]]
+ }
+ cosine(tmpui,tmpuj)
+ }
+ }
上面函数用来计算用户i和j之间的余弦距离。首先注意,我们不是把用户i和j对所有商品的评分直接拿来作为计算余弦距离的vector,而是只选择他们共同做了评价那部分电影(把一方或双方未评价的电影排除在外)。
NOTE:此处还有一个小tricky:如果两人共同评价过的电影少于总电影数的30%,则认为两人毫无共同点,余弦距离为0。这个30%是我们认为设定的阈值,可以根据具体数据进行调整。
两两计算相似度:
> cosdata <- matrix(nrow = rownum, ncol = rownum)
> for (i in 1:(rownum-1)) {
+ for (j in (i+1):rownum) {
+ cosdata[i,j] <-cossimilar(i,j)
+ }
+ }
> cosdata
然后依据上面的相似度矩阵,对于一个用户未作评价的电影进行评分预测,具体预测算法是所有和该用户有相似性且评价了该电影的用户对于该电影评分的加权平均。
例如:user1对m3的评分就是:(cos_u1_u2 * score_u2_m3 + cos_u1_u3 * score_u3_m3 + cos_u1_u4 * score_u4_m3 + cos_u1_u5* score_u5_m3 + cos_u1_u7 * score_u7_m3 + cos_u1_u10 * score_u10_m3) / 6。
代码如下:
> rdata <- matrix(nrow = rownum, ncol = (colnum -1))
> for (i in 1:rownum) {
+ for (j in2:colnum) {
+ if (mydata[i,j] ==0) {
+ predictedV = 0
+ simnum = 0
+ if (i > 1) {
+ for (k in 1:(i-1)) {
+ predictedV =predictedV + cosdata[k,i] * mydata[k,j]
+ if (mydata[k,j]> 0) {
+ simnum = simnum + 1
+ }
+ }
+ }
+ if (i <rownum) {
+ for (k in(i+1):rownum) {
+ predictedV =predictedV + cosdata[i,k] * mydata[k,j]
+ if (mydata[k,j]> 0) {
+ simnum = simnum + 1
+ }
+ }
+ }
+ rdata[i,j-1] =predictedV / simnum
+ }
+ }
+ }
> rdata
我们已经预测出了每个用户对于他们没打过分的电影的评分情况。如果预测得分>2分,我们就向该用户推荐该电影:
> for (i in 1:rownum) {
+ cat("Recommendation to User", i, " products: ")
+ for (j in 1: (colnum- 1)) {
+ if(!is.na(rdata[i,j])) {
+ if (rdata[i,j]> 2) {
+ cat( j, ", ")
+ }
+ }
+ }
+ cat("\n")
+ }
Recommendation to User 1 products : 3 , 6 ,
Recommendation to User 2 products : 4 ,
Recommendation to User 3 products : 6 ,
Recommendation to User 4 products : 1 , 4 ,
Recommendation to User 5 products :
Recommendation to User 6 products :
Recommendation to User 7 products :
Recommendation to User 8 products : 6 , 9 ,
Recommendation to User 9 products :
Recommendation to User 10 products :
用户1,2,3,4,8得到了推荐。