【机器学习实战系列】读书笔记之利用SVD简化数据(一)

本文详细说明机器学习实战这本书中的示例:餐厅菜肴推荐引擎的算法

5.1 推荐未尝过的菜肴

推荐系统的工作过程是:给定一个用户,系统会为此用户返回N个最好的推荐菜。为了实现这一点,则需要做到:

  1. 寻找用户没有评级的菜肴,即在用户-物品矩阵中的0值;
  2. 在用户没有评级的所有物品中,对每个物品预计一个可能的评级分数。这就是说,我们认为用户可能对物品的打分(这就是相似度计算的初衷);
  3. 对这些物品的评分从高到底进行排序,返回前N个物品。

基于物品相似度的推荐引擎代码如下:

# 用来计算在给定相似度计算方法的条件下,用户对物品的估计评分值
# 参数:数据矩阵、用户编号、物品编号、相似度计算方法,矩阵采用图1和图2的形式
# 即行对应用户、列对应物品
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]
        # 如果某个物品评分值为0,意味着用户没有对该物品评分,跳过
        if userRating == 0 : continue
        # 寻找两个用户都评级的物品,变量overLap给出的是两个物品当中已经被评分的那个元素
        overLap = nonzero(logical_and(dataMat[:, item].A>0, dataMat[:, j].A>0))[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
    # 通过除以所有的评分总和,对上述相似度评分的乘积进行归一化。这使得评分值在0-5之间,
    # 而这些评分值则用于对预测值进行排序
    else : return ratSimTotal/simTotal

# 推荐引擎,会调用standEst()函数,产生最高的N个推荐结果。
# simMeas:相似度计算方法
# estMethod:估计方法
def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst) :
    # 寻找未评级的物品,对给定用户建立一个未评分的物品列表
    unratedItems = nonzero(dataMat[user, :].A==0)[1]
    # 如果不存在未评分物品,退出函数,否则在所有未评分物品上进行循环
    if len(unratedItems) == 0 : return 'you rated everything'
    itemScores = []
    for item in unratedItems :
        # 对于每个未评分物品,通过调用standEst()来产生该物品的预测评分。
        estimatedScore = estMethod(dataMat, user, simMeas, item)
        # 该物品的编号和估计得分值会放在一个元素列表itemScores
        itemScores.append((item, estimatedScore))
    # 寻找前N个未评级物品
    return  sorted(itemScores, key=lambda jj : jj[1], reverse=True)[:N] 

我们以一个更大的用户-菜肴矩阵来详细的说明算法的流程,用户-菜肴矩阵如下图:


模拟算法过程如下:

1.进入recommend函数,设置user=Brett=0,dataMat为用户-菜肴矩阵


2.计算unratedItems,先看dataMat[user, :].A==0得出的是布尔值,所以我们观察矩阵,
dataMat[0, :].A 是 [2,0,0,4,4,0,0,0,0,0,0]
dataMat[0, :].A==0 得出的列表是 [F,T,T,F,F,T,T,T,T,T,T],F表示false,t表示true
nonzero(dataMat[user, :].A==0)得出的数组是array([1,2,5,6,7,8,9,10],dtype=int64)
 unratedItems =nonzero(dataMat[user, :].A==0)[0]得出的数组是[1,2,5,6,7,8,9,10]
发现 len(unratedItems) != 0
执行 itemScores = []
item为[1,2,5,6,7,8,9,10]中的其中一个,首先取item为1
执行estimatedScore = estMethod(dataMat, Brett, simMeas,1)


3.进入standEst函数,设置user=Brett,dataMat为用户-菜肴矩阵,item为1
执行 n = shape(dataMat)[1],n=11
simTotal = 0.0; ratSimTotal = 0.0
执行for j in range(n) :取j为0
执行userRating = dataMat[user,j],可以看出userRating = dataMat[0, 0]=2 
执行overLap = nonzero(logical_and(dataMat[:, item].A>0, dataMat[:, j].A>0))[0]
dataMat[:, item].A >0= dataMat[:, 1].A>0=[ F,F,F,T,T,F,F,F,F,F,T ]
dataMat[:, j].A >0= dataMat[: , 0].A>0  = [ T.F.F.T.T.F.T.F.F.F.T ]
logical_and(dataMat[:, item].A>0, dataMat[:, j].A>0)=[F,F,F,T,T,F,F,F,F,F,T],这样就使得两个评分为0的用户的值为False
overLap = nonzero(logical_and(dataMat[:, item].A>0, dataMat[:, j].A>0))[0] = [3,4,10]
可以看出 len(overLap) != 0
执行similarity = simMeas(dataMat[overLap, 1], dataMat[overLap, 0]),

即将(3,5,1),(3,5,1)这两个向量余弦化,得:

 similarity = (3*3+5*5+1*1)/[sqrt(3^2+5^2+1^2)*sqrt(3^2+5^2+1^2)]=1

 simTotal = 0+1=1,  ratSimTotal = 0+1*2=2
 
 4.然后再次执行for j in range(n) :取j为1
 执行userRating = dataMat[user,j],可以看出userRating = dataMat[0, 1]=0
 执行if userRating == 0 : continue ,这里就规避了未被评分的物品的影响
 
  5.依次类推
 得出 ratSimTotal/simTotal
 
  6.再返回recommend函数,执行itemScores.append((item, estimatedScore))=itemScores.append((1, estimatedScore))等,
 计算出每个未评级物品的相应得分
 执行并返回 sorted(itemScores, key=lambda jj : jj[1], reverse=True)[:N] ,寻找前N个未评级物品
 
  7.结果分析,得分越高,说明给用户推荐该物品,用户喜欢的可能性就越大。 整个算法其实就是在做一件事情,计算所有未评级物品与已评级物品的相似度,并打分。

猜你喜欢

转载自blog.csdn.net/qq_38150441/article/details/79883353