利用SVD-推荐未尝过的菜肴

推荐未尝过的菜肴

推荐系统的工作过程:给定一个用户,系统会为此用户返回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)]

猜你喜欢

转载自www.cnblogs.com/gezhuangzhuang/p/10204999.html
SVD