协同过滤和简单SVD优化
协同过滤(collaborative filtering)
推荐系统:
百度百科的定义是:它是利用电子商务网站向客户提供商品信息和建议,帮助用户决定应该购买什么产品,模拟销售人员帮助客户完成购买过程
主要有有以下几种推荐的方式:
- 基于内容的推荐
- 协同过滤
- 关联推荐
- 混合推荐
协同过滤
这里我们主要考虑的是协同过滤,这也是最经典的推荐算法。协同过滤的思想很简单,就是像我们平时需要找一部好看的电影最简单的方式就是找兴趣相同的人推荐。
相似度计算:
相似度的计算主要有以下几种方法:
- 基于欧氏距离 相似度=1/(1+欧式距离)
- 基于皮尔逊相关系数(Pearson correlation) 0.5+0.5*corrcoef()
- 余弦相似度 0.5+0.5*cos
item-based CF & user-based CF:
item-based CF
基于item的协同过滤,通过用户对不同item的评分来评测item之间的相似性,基于item之间的相似性做出推荐;
user-based CF
基于user的协同过滤,通过不同用户对item的评分来评测用户之间的相似性,基于用户之间的相似性做出推荐;
推荐系统的评价:
最小均方根误差(Root Mean Squared Error,RMSE):首先计算均方误差值,然后取其平方根。(如果用户的评价在一星到五星,而我们的RMSE=1,说明我们的预估和用户评价相差一个星级)
面临的挑战:
实例和SVD优化
背景:
构建一个餐馆食物推荐引擎,推荐给用户他没有尝试过的最适合他的选择。
做法:
我们这里采用的是item-based CF。就是推断出用户对那些没有尝试过的评分再根据评分推荐。另外由于现实中客户是远远不可能尝试所有的产品的,所以实际的矩阵中很多的值都是0,这时候我们可以采 用SVD进行降维,在小的多的数据情况下得到相似的结果。
函数:
ecludSim(inA, inB)
基于欧式距离的相似度计算pearsSim(inA, inB)
基于皮尔森距离的相似度计算cosSim(inA, inB)
余弦相似度的计算standEst(dataMat, user, simMeans, item)
求出用户对物品的估计分值,计算出相似度和评分成绩,最后相似度评分进行归一化使结果在0到5之间svdEst(dataMat, user, simMeas, item)
在估计分数的过程中使用svd降维,保留90%的能量值。def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst)
对给定的用户推荐给他N个产品。过程很简单,首先找出那些他没有评级的产品,然后调用estMethod给出评分,再选评分前面几个。
-
#coding=utf-8 from numpy import * def loadExData(): return[[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], [5, 5, 5, 0, 0], [1, 1, 1, 0, 0]] def loadExData2(): return[[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5], [0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 3], [0, 0, 0, 0, 4, 0, 0, 1, 0, 4, 0], [3, 3, 4, 0, 0, 0, 0, 2, 2, 0, 0], [5, 4, 5, 0, 0, 0, 0, 5, 5, 0, 0], [0, 0, 0, 0, 5, 0, 1, 0, 0, 5, 0], [4, 3, 4, 0, 0, 0, 0, 5, 5, 0, 1], [0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4], [0, 0, 0, 2, 0, 2, 5, 0, 0, 1, 2], [0, 0, 0, 0, 5, 0, 0, 0, 0, 4, 0], [1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0]] def ecludSim(inA, inB): return 1.0 / (1.0 + linalg.norm(inA - inB)) def pearsSim(inA, inB): if len(inA) < 3: return 1.0 return 0.5 + 0.5*corrcoef(inA , inB, rowVal = 0)[0][1] def cosSim(inA, inB): num = float(inA.T*inB) denom = linalg.norm(inA)*linalg.norm(inB) return 0.5 + 0.5 * (num/denom) def standEst(dataMat, user, simMeans, 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 = simMeans(dataMat[overLap,item], dataMat[overLap,j]) simTotal += similarity ratSimTotal += similarity * userRating if simTotal == 0: return 0 else: return ratSimTotal / simTotal def svdEst(dataMat, user, simMeas, item): n = shape(dataMat)[1] simTotal = 0.0; ratSimTotal = 0.0 U,Sigma,VT = linalg.svd(dataMat) sig2 = Sigma ** 2 cut = 0 for i in range(n): if sum(sig2[:i]) / sum(sig2) > 0.9: print i cut = i break Sig4 = mat(eye(cut)*Sigma[:cut]) #arrange Sig4 into a diagonal matrix xformedItems = dataMat.T * U[:,:cut] * Sig4.I #create transformed items for j in range(n): userRating = dataMat[user,j] if userRating == 0 or j==item: continue similarity = simMeas(xformedItems[item,:].T,\ xformedItems[j,:].T) 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 def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst): unratedItems = nonzero(dataMat[user,:].A==0)[1]#find unrated items if len(unratedItems) == 0: return 'you rated everything' 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] def main(): myMat = mat(loadExData2()) print recommend(myMat,2,estMethod=svdEst) if __name__ == '__main__': main()