推荐未尝过的菜肴-基于物品相似度的推荐
推荐系统的工作过程:给定一个用户,系统会为此用户返回N个最好的推荐菜
1. 寻找用户没有评级的菜肴,即在用户-物品矩阵中的0值
2. 在用户没有评级的所有物品中,对每个物品预计一个可能的评级分数(利用相似度计算)。这就是说,我们预测用户对每个物品的打分
3. 对这些物品的评分从高到低进行排序,返回前N个物品
from numpy import * from numpy import linalg as la
一、相似度计算(欧式距离、皮尔逊相关系数、余弦相似度)
# 相似度计算 # 计算欧式距离 def ecludSim(inA, inB): return 1.0 / (1.0 + la.norm(inA - inB)) # pearsim()函数会检查是否存在3个或更多的点 # corrcoef直接计算皮尔逊相关系数,范围[-1, 1],归一化后[0, 1] def pearsSim(inA, inB): # 如果不存在,该函数返回1.0,此时两个向量完全相关 if len(inA) < 3: return 1.0 return 0.5 + 0.5 * corrcoef(inA, inB, rowvar=0)[0][1] # 计算余弦相似度,如果夹角为90度,相似度为0;如果两个向量的方向相同,相似度为1.0 def cosSim(inA, inB): num = float(inA.T * inB) denom = la.norm(inA) * la.norm(inB) return 0.5 + 0.5 * (num / denom)
二、基于物品相似度的推荐
计算未评分物品的分值:用已评分物品的分值以及与未评分物品的相似度进行评分
# 基于物品相似度的推荐引擎 def standEst(dataMat, user, simMeas, item): """standEst(计算用户未评分物品中,以对该物品和其他物品评分的用户的物品相似度,然后进行综合评分) Args: dataMat 训练数据集 user 用户编号 simMeas 相似度计算方法 item 未评分的物品编号 Return: ratSimTotal / simTotal 评分(0~5之间的值) """ # 得到数据集中物品数目 n = shape(dataMat)[1] # 初始化两个评分值 simTotal = 0.0 ratSimTotal = 0.0 # 遍历行中的每个物品(对用户评过分的物品进行遍历,并将它与其他物品进行比较) for j in range(n): userRating = dataMat[user, j] # 如果某个物品的评分值为0,则跳过这个物品 if userRating == 0: continue # 寻找两个用户都评级的物品 # 变量overlap给出的是两个物品当中已经被评分的那个元素的索引ID # logical_and 计算x1和x2元素的真值 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]) # 相似度会不断累加,每次计算时还考虑相似度和当前用户评分的乘积 # similarity 用户相似度 userRating 用户评分 simTotal += similarity ratSimTotal += similarity * userRating if simTotal == 0: return 0 # 通过除以所有的评分总合,对上述相似度评分的乘积进行归一化,使得最后评分在0~5之间,这些评分用来对预测进行排序 else: return ratSimTotal / simTotal
三、排序获取最后的推荐结果
# recommend()函数,就是推荐引擎,它默认调用 standEst()函数,产生了最高的N个推荐结果 # 如果不指定N的大小,则默认值为3,该函数另外的参数该包括相似度计算方法和估计方法 def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst): """recommend() Args: dataMat 训练数据集 user 用户编号 simMeas 相似度计算方法 estMethod 使用的推荐算法 Returns: 返回最终N个推荐结果 """ # 寻找未评级的物品 # 对给定用户建立一个未评分的物品列表 unratedItems = nonzero(dataMat[user, :].A == 0)[1] # 如果不存在未评分物品,那么就退出函数 if len(unratedItems) == 0: return 'you rated everything' # 物品的编号和评分值 itemScores = [] for item in unratedItems: # 获取 item 该物品的评分 estimatedScore = estMethod(dataMat, user, simMeas, item) itemScores.append((item, estimatedScore)) # 按照评分得分,进行逆排序,获取前N个未评级物品进行推荐 return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[: N]
四、矩阵测试
myMat = mat([[4, 4, 0, 2, 2], [4, 0, 0, 3, 3], [4, 0, 0, 1, 1], [1, 1, 1, 2, 0], [2, 2, 2, 0, 0], [1, 1, 1, 0, 0], [5, 5, 5, 0, 0]])
recommend(myMat, 2)
[(2, 2.5), (1, 2.0243290220056256)]
这表明用户2(从0开始计数,对应是矩阵第三行),对物品2的预测评分为2.5,对物品1预测评分为2.02
使用其他方法进行推荐:
recommend(myMat, 2, simMeas=ecludSim)
[(2, 3.0), (1, 2.8266504712098603)]
recommend(myMat, 2, simMeas=pearsSim)
[(2, 2.5), (1, 2.0)]