推荐引擎对因特网用户而言已经不再是什么新鲜事。Amazon会根据顾客的购买历史向他们推荐物品,Netflix会向其用户推荐电影,新闻网站会对用户推荐新闻报道…..当然,有很多方法可以实现推荐功能,这里我们只使用一种称为协同过滤(collaborative filtering)的方法。协同过滤是通过将用户和其他用户的数据进行对比来实现推荐的。
1.1 相似度计算
计算物品之间的相似度,一般来说,我们有3种方法:
1. 欧氏距离:我们希望相似度值在0到1之间变化,并且物品对越相似,它们的相似度值也就越大。可以用“相似度=1/(1+距离)”这样的算式来计算相似度。当距离为0时,相似度值为1;如果距离很大,相似度也就趋近于0。
def eulidSim(inA, inB):
return 1.0/(1.0+linalg.norm(inA-inB)) #norm求范数
2.皮尔逊相关系数: 它度量的是两个向量之间的相似度,在Numpy中,皮尔逊相关系数的计算是由函数corrcoef()进行的。皮尔逊相关系数的取值范围从-1到1,通过0.5+0.5*corrcoef()这个函数计算,把取值范围归一化到0到1之间。
def pearsSim(inA,inB):
if len(inA)<3 :return 1.0
return 0.5+0.5*corrcoef(inA,inB,rowvar = 0)[0][1]
3. 余弦相似度: 计算的是两个向量夹角的余弦值。夹角为90度,则相似度为0;如果两个向量的方向相同,则相似度为1.0。
def cosSim(inA,inB):
num = float(inA.T*inB)
denom = linalg.norm(inA)*linalg.norm(inB)
return 0.5+0.5*(num/denom)
1.2 示例:餐馆菜肴推荐引擎(推荐未尝过的菜肴)
推荐系统的工作过程是:给定一个用户,系统会为此用户返回N个最好的推荐菜。为了实现这个,我们需要做到:
1. 寻找用户没有评级的菜肴,即在用户-物品矩阵中的0值;
2. 在用户没有评级的所有物品中,对每个物品预计一个可能的评级分数。这就是,我们认为用户可能会对物品的打分(这就是相似度计算的初衷)
3. 对这些物品的评分从高到低进行排序,返回前N个物品
程序清单: 基于物品相似度的推荐引擎
def standEst(dataMat,user,simMeas,item):
n = shape(dataMat)[1]
simTotal = 0.0; ratSimTotal = 0.0
for j in range(n):
userRating = dataMat[user,j]
if userRating == 0: continue #终止循环
overLap = nonzero(logical_and(dataMat[:,item].A>0,dataMat[:,j].A>0))[0]
if len(overLap) == 0:similarity = 0
else:similarity = simMeas(dataMat[overLap,item],dataMat[overLap,j])
#print 'the %d and %d similarity is:%f' %(item,j,similarity)
simTotal += similarity
ratSimTotal +=similarity*userRating
if simTotal == 0:return 0
else:return ratSimTotal/simTotal
函数standEst用来计算给定相似度计算方法的条件下,用户对物品的估计评分值。参数包括数据矩阵,用户编号,物品编号和相似度计算方法。数据矩阵的形式为:行对应用户,列对应物品。接着,我们遍历每一个物品,如果某个物品评分值为0,就意味着用户没有对该物品评分,跳过了这个物品。循环大体上是对用户评过分的每个物品进行遍历,并将它和没有评分的物品进行相似度计算。overLap给出的是两个物品都被评分的值对应的行数。如果两者没有任何重合元素,则相似度为0且终止本次循环。但是如果存在重合评分值的物品,则基于这些重合物品计算相似度。随后,相似度会不断累加,每次计算还考虑相似度和当前用户评分的乘积。最后,通过所有的评分总和除以总的相似度,使得最后的评分值在0-5之间,这些评分值则用于对预测值进行排序。
def recommend(dataMat,user,N=3,simMeas=cosSim,estMethod=standEst):
unratedItems = nonzero(dataMat[user,:].A==0)[1]
if len(unratedItems) == 0:return 'you rated everthing'
itemScores =[]
for item in unratedItems:
estimatedScore = estMethod(dataMat,user,simMeas,item)
itemScores.append((item,estimatedScore))
return sorted(itemScores,key=lambda jj:jj[1],reverse=True)[:N]
recommend函数产生评分最高的N个推荐结果。改函数先对给定用户建立一个未评分的物品列表。如果不存在未评分物品,那么就退出函数;否则在所有未评分物品上进行循环。对每个未评分物品,则调用standEst函数产生该物品的预测得分。该物品的编号和估计得分值会放在一个元素列表itemScores中。
(利用SVD可以提高推荐的效果,后续添加这个功能)