python机器学习案例系列教程——推荐系统

版权声明:本文为博主原创文章,转载请注明来源。开发合作联系82548597@qq.com https://blog.csdn.net/luanpeng825485697/article/details/78756993

全栈工程师开发手册 (作者:栾鹏)

python数据挖掘系列教程

主流的推荐系统算法大致分为两类:

  1. 基于用户行为数据的协同过滤算法
  2. 基于内容数据的过滤算法

大致而言,基于内容数据的算法适用于cold start,即用户和项目都比较少的时候,而基于用户行为数据的协同过滤算法在用户和项目较多,数据比较丰富的情况下有较高的准确率。

除此之外,还包括基于社会网络数据的推荐,基于语境(上下文)感知数据的推荐,基于心理学数据的推荐等等。

基于用户行为数据的算法

1.1 基于用户的协同过滤算法(user-based CF)

一个用户喜欢和他具有相似喜好的用户喜欢的项目, 两个用户喜欢的项目交集越大, 这两个用户越相似。

两个用户兴趣相似度的计算可以有多种方法,常见的如 Pearson相关(皮尔逊相似性)和余弦相似度计算。

适用场景和优缺点:这个是cf中的一种,它的主要特色是可以发现和用户具有同样taste的人,有句俗话叫做观其友知其人,大概也是这个道理吧。找到用户的相似用户,通过相似用户喜欢的item推荐给该用户。因为用户的相似用户群还是比较敏感的,所以要频繁地计算出用户的相似用户矩阵,这样的话运算量会非常大。而且这个算法往往推荐出来的item很多都是大家都喜欢的比较hot的item,有的时候它提供的结果并不是个性化,反而成了大众化的推荐了。用这种算法的web应用一般都是item更新频繁,比如提供资讯类服务的应用(以“指阅”为代表的),或者笑话类推荐(以“冷笑话精选”为代表的)。当然这种算法的一个中间产物—–用户相似度矩阵是一个很有用的东西,社交类的网站可以利用这个中间产物来为用户提供相同品位的好友推荐

1.2 基于商品的协同过滤

基于商品的协同过滤推荐(item-based CF)基于这样的假设: 一个用户会喜欢与他之前喜欢的商品相似的商品。因此,基于商品的协同过滤推荐关键在于计算物品之间的相似度。

适用场景和优缺点:这个算法是cf中的一种,也是当今很多大型网站都在采用的核心算法之一。对于商城网站(以Amazon为代表,当然也包括京东那种具有搞笑特色的推荐系统在内),影视类推荐,图书类推荐,音乐类推荐系统来说,item的增长速度远不如user的增长速度,而且item之间的相似性远不如user之间的相似性那么敏感,所以可以在离线系统中将item的相似度矩阵计算好,以供线上可以近乎即时地进行推荐。因为这种方法靠的是item之间的相关性进行推荐,所以推荐的item一般都和喜欢的item内容或者特性高度相似,很难推荐出用户潜在喜欢的item,多样性也比较差

基于用户的协同过滤和基于商品的协同过滤统称为基于邻域的推荐 (nearest neighbor recommendation),也称作基于记忆的推荐算法(memory-based recommendation)。

基于邻域的推荐算法需要维护一个用户相似度矩阵或商品相似度矩阵, 因此对于商品的数目更新速度远远小于用户数目的增长速度的情况,宜采用基于商品的推荐算法, 如 Amazon 建立的推荐系统正是基于商品的协同过滤推荐算法, 还有移动应用产品的推荐。另外, 有研究表明, 基于商品的算法一般在性能上要优于基于用户的算法。

注意:协同过滤不提取用户特征,也不提取商品特征,只是计算向相似度,当有新商品出来时,没有任何用户购买过时,是不会推荐的。

1.3 基于模型的协同过滤

基于模型的协同过滤推荐 (model-based CF) 是采用机器学习或数据挖掘等算法, 用训练数据来学习识别复杂模式, 从而得到学习模型, 然后基于学习模型在数据集上进行智能预测。后面我们了解的矩阵分解就是其中的一种。

隐语义模型 (latent semantic CF models)/矩阵分解模型(matrix factorization)

贝叶斯信念网协同过滤模型(Bayesian belief nets CF models

聚类协同过滤模型 (clustering CF models)

概率因素模型(probabilistic factor models)

基于内容数据的推荐

对一个给定的用户, 推荐与他之前喜欢的商品在内容上有相似性的其他商品。这有点类似于广告点击率预测。需要用户特征和内容特征。进行分类预测。

这种推荐仅需要得到两类信息: 商品特征的描述和用户过去的喜好信息。

  • 利用领域专家给项目打标签的方法 , 也即传统的分类系统(Taxonomy),
  • 另一种是用户给项目打标签, 也即大众分类系统 (Folksolomy)。

基于内容的推荐,很大程度上是在进行文本挖掘。web应用提供的内容或者爬取的内容在推给用户之前可以做一些挖掘,比如资讯类的应用,将抓取到的资讯,通过文本分析那一套算法提取出每篇资讯的关键词,以及统计频次和逆向文档频率来聚类或者笨一点地话计算出资讯的相似度矩阵,即共同的key words越多,两篇资讯的相似度越高。当你的用户很少很少,你的显式反馈数据非常非常少的时候,你可以根据用户的浏览或者搜索等等各种行为,来给用户进行推荐。再猥琐一点的话,你可以在用户刚刚注册好你的应用的时候,给他一些提问,比如让他输入一些感兴趣的话题啊,或者对以前看过的电影打分什么的。(当然这些电影都是你从各个簇中随机选取的,要足够多样性)这个算法它好就好在,不需要拿到用户–项目的评分矩阵,只需要知道用户喜欢什么,就可以很快速地推荐给用户十分相关的item。这个算法需要每天都要根据你抓取的资讯,不断地计算item之间的相似性。这个算法有个好处在于可以从容应对上面的两个算法其实都很难应对的问题,就是如果你想推出一个新的item,因为没有一个人有对这个new item的评分,所以上述的两个算法不可能推荐新的东西给你,但你可以用基于内容的算法将新的item计算出它属于哪个类,然后时不时地推出你的新item,这点对于商城尤其重要。因为新商品也是有特征的,而这个特征跟其他商品的特征是一样的。

Knn(邻近算法)

K最近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。该方法的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。 KNN方法虽然从原理上也依赖于极限定理,但在类别决策时,只与极少量的相邻样本有关。由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合

基于矩阵分解的推荐算法

矩阵分解的一般形式

矩阵分解是指将一个矩阵分解成两个或者多个矩阵的乘积。对于上述的用户-商品矩阵(评分矩阵),记为Rm×n。可以将其分解成两个或者多个矩阵的乘积,假设分解成两个矩阵Pm×kQk×n,我们要使得矩阵Pm×kQk×n的乘积能够还原原始的矩阵Rm×n

Rm×nPm×k×Qk×n=R^m×n

其中,矩阵Pm×k表示的是m个用户与k个主题之间的关系,而矩阵Qk×n表示的是k个主题与n个商品之间的关系。

利用矩阵分解进行预测

在上述的矩阵分解的过程中,将原始的评分矩阵Rm×n分解成两个矩阵Pm×kQk×n的乘积:

损失函数

可以使用原始的评分矩阵Rm×n与重新构建的评分矩阵R^m×n之间的误差的平方作为损失函数,即:

ei,j2=(ri,jr^i,j)2=(ri,jk=1Kpi,kqk,j)2

最终,需要求解所有的非“-”项的损失之和的最小值:

minloss=ri,jei,j2

损失函数的求解

对于上述的平方损失函数,可以通过梯度下降法求解,梯度下降法的核心步骤是

求解损失函数的负梯度:

pi,kei,j2=2(ri,jk=1Kpi,kqk,j)qk,j=2ei,jqk,j

qk,jei,j2=2(ri,jk=1Kpi,kqk,j)pi,k=2ei,jpi,k

根据负梯度的方向更新变量:

pi,k=pi,kαpi,kei,j2=pi,k+2αei,jqk,j

qk,j=qk,jαqk,jei,j2=qk,j+2αei,jpi,k

通过迭代,直到算法最终收敛。

加入正则项的损失函数即求解方法

通常在求解的过程中,为了能够有较好的泛化能力,会在损失函数中加入正则项,以对参数进行约束,加入L2正则的损失函数为:

Ei,j2=(ri,jk=1Kpi,kqk,j)2+β2k=1K(pi,k2+qk,j2)

利用梯度下降法的求解过程为:

求解损失函数的负梯度:

pi,kEi,j2=2(ri,jk=1Kpi,kqk,j)qk,j+βpi,k=2ei,jqk,j+βpi,k

qk,jEi,j2=2(ri,jk=1Kpi,kqk,j)pi,k+βqk,j=2ei,jpi,k+βqk,j

根据负梯度的方向更新变量:
pi,k=pi,kα(pi,kei,j2+βpi,k)=pi,k+α(2ei,jqk,jβpi,k)

qk,j=qk,jα(qk,jei,j2+βqk,j)=qk,j+α(2ei,jpi,kβqk,j)

通过迭代,直到算法最终收敛。

预测
利用上述的过程,我们可以得到矩阵Pm×kQk×n,这样便可以为用户i对商品j进行打分:

k=1Kpi,kqk,j

Svd(奇异值分解)

svd的全称是:Singular Value Decomposition,翻译过来是奇异值分解,是一种矩阵分解的方法。其实,这个方法是提取一般实矩阵“特征值”的算法,(这里特征值加引号是因为,特征值是针对方阵来定义的,而一般的m*n的实矩阵是没有特征值的。)其实,矩阵就是一个线性变换的表示方法,因为一个向量乘一个矩阵的结果是一个向量,第一个向量通过线性变换来变成第二个向量。线性变换有许多变换方向,比如你可以对一个图像矩阵做伸缩同时也做平移。那么特征值和特征向量又是什么?一个特征向量就是表示其中的一个变换方向,而对应的特征值则表示这个变换方向对于整个线性变换有多么重要。书归正传,那么奇异值又是什么?我觉得奇异值就是特征值从方阵往一般实矩阵的一个推广。你将一个m*n的实矩阵和它的转置相乘,就会得到一个方阵,然后对这个方阵做特征值分解,得到的特征值就是所谓的奇异值的平方。我的意思是说,某种意义上,可以讲奇异值和特征值理解为一回事。那么拿到奇异值又会有什么用呢?拿到奇异值后,我们就可以抓到主要的成分,丢掉次要和非常次要的成分进行分析。也就是说,我们可以对原来的庞大的常常又非常稀疏的矩阵进行降维和分解,而分解后得到的矩阵都是稠密矩阵。最终我们会得到一个表示user特性的矩阵和一个表示item特性的矩阵。拿到这些数据之后,我们就可以进行推荐了,而且也可以很容易地进行聚类分析。这个算法的好处在于,可以解决rating矩阵的稀疏性问题,同时可以降低矩阵的维度,提高运算速度。但它的缺点是付出的空间代价太大。在做svd分解时,你需要先把一个大的rating矩阵分解成三个大的矩阵,这三个矩阵需要存在计算机内存中,然后才能进行降维。其实,svd这个方法的思路和PCA(主成分分析法)很像,抓住主要矛盾,忽略次要矛盾。分解降维后的矩阵非常约等于原来的矩阵。

聚类算法

这里用到的聚类算法,是用来降低维度以及为并行计算作准备的。拿到rating矩阵之后,可以通过这些评分将用户自然地聚成几簇,然后用上述的算法对各个簇做推荐算法并行计算,充分地利用好所有计算资源。当然你也可以在svd分解之后,拿到user和item矩阵之后,对这两个矩阵分别作聚类分析,你可以得到user的簇以及item的簇。这样的结果会非常有意义,你可以作好友推荐,相似item推荐等等。在基于内容的算法中,因为很多资讯之间并不是那么的相关,把他们都相互计算相似度,会得到很多的0,所以没有必要。因此可以在计算之前,对整个item做个聚类,然后分别对各簇来做相似度计算。聚类算法中,我用过性能最好的也是最简单的就是k-means。

基于社会网络数据的推荐

在社会网络中, 与其他用户进行链接的用户, 表示愿意分享一定共同的兴趣爱好, 或者他们之间有相似的品味(同质性原理),
这里的相似性被期望来帮助改进推荐准确率和质量

基于社会网络数据的推荐(social network-based recommendation)早先大部分是基于领域的方法。首先探索打分者的社会网络, 聚集打分者的打分来计算预测打分; 然后找到打分者的邻居。除了简单的基于邻域的方法, 还有许多是基于模型的推荐方法,如采用矩阵分解方法、 图模型, 将用户的社会网络和用户物品的喜好关系建模到一张图中, 联合基于项目的推荐和基于信任(trust-based)的推荐, 然后利用随机游走算法给用户做推荐。采用异构数据源对用户兴趣进行建模, 从异构社会网络中学习相关性。

这里, 基于信任的推荐是将信任度引入推荐系统, 用信任度代替相似度。有许多方法可以计算用户的 top-N信任邻居。用一个信任矩阵 (如 Eigentrust)来计算 top-N 被信任的用户, 或者激活扩散模型(spreading activation model)通过他们接收到的能量来给结点排序。

4. 基于语境(上下文)感知数据的推荐(context aware-based recommendation)

语境信息类型包括时间、 信息、 外界物理环境 (如天气、温度等)、 设备类型、 周围人员、 活动状态、 目的/意图等。
还有些系统考虑了情绪、 计算平台、 网络条件、社会网络等更为广泛的语境。

(1)、通过语境驱动的查询和搜索推荐
典型的是使用语境信息来查询或搜索一个特 定的资源应答(如餐馆), 并给出最好的匹配资源推荐给用户(如离用户最近的当前营业着的餐馆)
(2)、通过语境喜好的启发和评估推荐
使用多维方法来整合语境信息到推荐系统, 这里传统的二维用户/项目范例被延伸来支持额外的语境维度, 如时间、 位置和公司。

5. 基于人口统计学数据的推荐

基于人口统计学数据的推荐 (demographic-based recommendation)是根据人口统计学数据对每个用户建立一个用户剖面(user profile), 系统根据用户的剖面图, 计算用户间相似度, 得到当前用户的最近邻集, 最后系统会把基于 “邻居” 用户群喜好的项目推荐给当前用户。

6. 基于心理学数据的推荐

人的心理特征和情感因素在用户做决策时非常重要。人类情感、 个性及其模型已经被广泛地在计算机上实施。基于心理学数据的推荐(psychology-based recommendation)方法有情感智能 (emotional intelligence)、 满意度 (satisfaction) 、 心理作用 (psychological effects)、 个性特质 (personalitybased)。
值得一提的是, Neflix竞赛获奖者之一就是学心理学的。

7. 基于大数据的推荐(big data-based recommendation)

目前, 大数据通常被认为是当前传统技术难以处理的, 这里的传统技术也包括传统推荐技术。

基于大数据的推荐(big data-based recommendation)有两个内涵: 一个是大数据使得传统推荐技术更加准确; 另一个是传统的技术已经不能满足需求, 大数据需要新的推荐技术。

大数据的来源多样, 对推荐系统而言,“大数据”包含系统可触及到的数据, 如用户行为数据、 社会网络数据、 人口统计学数据、 语境感知数据等。

大数据环境下的推荐是至少基于两种类型的数据而进行的推荐。研究表明, 基于大数据的各种混合推荐算法的推荐效果要优于单纯的基于一种数据的推荐, 如将基于社会网络的推荐和协同过滤推荐结合, 将基于内容的推荐和协同过滤方法结合, 跨领域推荐。

案例

在样本数据集中,数据存储一般以矩阵的形式,每一行代表一个对象,每一列代表一个特征属性。而文本介绍一种稀疏高维的特征数据集。例如在文本分类中,文章的特征用文章包含的单词表示,在客户购物中,客户的特征用所购买的物品表示。这种对象的特征维度往往都是很大的,并且是稀疏的。对于这种数据集进行计算,算法往往比较复杂。

一个普通的特征数据集如下表:

人        身高    年龄     体重
小明1     182     23      67
小明2     181     24      65
小明3     184     24      65
小明4     183     21      68
小明5     185     22      66      

一个稀疏高维特征数据集如下:0表示没买,1表示买

客户     宝贝1   宝贝2    宝贝3   宝贝4   宝贝5   宝贝6  宝贝7    ...   
客户1     1       0       0       0      0      1      0      ...
客户2     0       0       1       0      1      0      0      ...
客户3     0       1       0       0      0      0      1      ...
客户4     0       0       1       0      0      0      0      ...
客户5     1       0       0       0      1      0      0      ...
客户6     0       0       0       1      0      0      0      ...
客户7     0       0       0       0      1      0      0      ...
客户8     0       1       0       0      0      1      0      ...
客户9     0       0       0       0      1      0      0      ...

高维稀疏特征数据集是经常存在的,例如:

影评人给电影打分。 向影评人推荐相似影评人,向影评人推荐电影。

买家给宝贝评分。向淘宝买家推荐宝贝等。

读者停留在某类文章上的时间长度。向读者推荐类似文件的强度。

上网用户对某博客进行了点击。向用户推荐感性的博主。

有多的应用需要在工作中学会洞察。

对这种数据集进行聚类或者分类往往比较困难,而且尤其当我们关系的特征出现的数量很少还要主要注意进行样本均衡。这些读者可以在以后的学习中注意。

本文仅以电影推荐这个例子来讲述匹配与推荐的算法(协作性过滤算法)。

构造数据集

我们先使用如下简单的数据集。

注意:实际中特征数据集可能是稀疏的,因为本文重点是为了了解匹配和推荐机制。

# 偏好数据集(人-电影-评分)
prefs={
    'name1': {'movie1': 2.5, 'movie2': 3.5,'movie3': 3.0, 'movie4': 3.5, 'movie5': 2.5, 'movie6': 3.0},
    'name2': {'movie1': 3.0, 'movie2': 3.5,'movie3': 1.5, 'movie4': 5.0, 'movie6': 3.0,'movie5': 3.5},
    'name3': {'movie1': 2.5, 'movie2': 3.0,'movie4': 3.5, 'movie6': 4.0},
    'name4': {'movie2': 3.5, 'movie3': 3.0, 'movie6': 4.5, 'movie4': 4.0, 'movie5': 2.5},
    'name5': {'movie1': 3.0, 'movie2': 4.0, 'movie3': 2.0, 'movie4': 3.0, 'movie6': 3.0,'movie5': 2.0},
    'name6': {'movie1': 3.0, 'movie2': 4.0, 'movie6': 3.0, 'movie4': 5.0, 'movie5': 3.5},
    'name7': {'movie2':4.5,'movie5':1.0,'movie4':4.0}
}

相似度计算方法

我们可以对行或对列进行相似度计算。这里先了解如何对计算两个行的相似程度。我们采用欧几里得距离和皮尔逊相似度。将计算的值映射的0-1范围上来代表相似程度,1代表完全相同,0代表完全不同。

注意:读者可以思考,如何计算高维稀疏特征数据集中两行之间的相似度

欧几里得距离计算

公式:|x| = √( x[1]2 + x[2]2 + … + x[n]2 ) 欧式距离百科

获取两个行(对象)的相同列,生成一个局部特征数据集。再根据这个局部特征数据集计算欧式距离(这是因为属性数据集太系数,直接计算相似度会计算出来的值特别小)。

from math import sqrt
# 计算两行之间的欧几里得距离,以此来代表相似度。prefs表示偏好数据集
def sim_distance(prefs,row1_name,row2_name):
    # 首先计算是否有共同列(都看过的电影)
    si={}
    for item in prefs[row1_name]:
        if item in prefs[row2_name]: si[item]=1

    # 如果没有共同列,则两行之间相似度为0
    if len(si)==0: return 0

    # 根据共同列计算两行的欧几里得距离,并将距离映射到0-1上。0表示完全不相似,1表示完全相似
    sum_of_squares=sum([pow(prefs[row1_name][item]-prefs[row2_name][item],2) for item in prefs[row1_name] if item in prefs[row2_name]])
    return 1/(1+sum_of_squares)

皮尔逊相似度计算

假设有两个向量X、Y,那么两向量间的皮尔逊相关系数可通过以下公式计算:

公式一:

这里写图片描述

可以将公式进行多种演变,其中一种转换为下面的公式。

公式二:

这里写图片描述

其中E是数学期望,cov表示协方差,N表示变量取值的个数。

我们就根据公式二进行计算。

获取两个行(对象)的相同列,生成一个局部特征数据集。再根据这个局部特征数据集计算皮尔逊相似度(这是因为属性数据集太系数,直接计算相似度会计算出来的值特别小)。

虽然皮尔逊相似度的计算过程更复杂一些,但它在数据不是很规范的时候(例如:影评者对影片的评价总是相对于平均水平偏离很大时),会倾向于给出很更好的结果。

# 计算两行的皮尔逊相似度,以此来代表相似度。prefs表示数据集
def sim_pearson(prefs,row1_name,row2_name):
    # 首先计算是否有共同列(都看过的电影)
    si={}
    for item in prefs[row1_name]:
        if item in prefs[row2_name]: si[item]=1

    # 如果没有共同列,两行之间相似度为0
    if len(si)==0: return 0

    # 得到列表元素个数
    n=len(si)

    # 对两行的共同列求和
    sum1=sum([prefs[row1_name][it] for it in si])
    sum2=sum([prefs[row2_name][it] for it in si])

    # 对两行的共同列求平方和
    sum1Sq=sum([pow(prefs[row1_name][it],2) for it in si])
    sum2Sq=sum([pow(prefs[row2_name][it],2) for it in si])

    # 对两行的共同列求乘积之和
    pSum=sum([prefs[row1_name][it]*prefs[row2_name][it] for it in si])

    # 计算皮尔逊评价值
    num=pSum-(sum1*sum2/n)
    den=sqrt((sum1Sq-pow(sum1,2)/n)*(sum2Sq-pow(sum2,2)/n))
    if den==0: return 0

    r=num/den

    return r

匹配相似行

有了每两行之间的相似度计算方法,就可以计算每行的相似行了。在计算相似行时,对于两个特征属性的数量不同的行计算相似度时,只用计算公共特征属性即可。

# 匹配相似行
# 根据偏好数据集,返回与某个行最匹配的n行。person表示要匹配的行(人),similarity表示相似度计算函数
def topMatches(prefs,row_name,n=5,similarity=sim_pearson):
    scores=[(similarity(prefs,row_name,other),other) for other in prefs if other!=row_name]
    scores.sort()
    scores.reverse()
    num = n
    if n>len(scores):num= len(scores)
    return scores[0:num]

利用相似行估计列的值,并排名

有了相似行,某一行就可以根据相似行对各列的评值,来估计当前行各列存在的空白值。为了避免算法的不精准,所以相似行不止一个,每个相似行根据相似度确定权重。最后估值采用加权平均的方式。

对当前行各列存在的空白值进行估计以后,将估计的值进行排名推荐。

# 利用相似行,估计某行所有列存在的空白值,并排名(估计影片评分,并排名推荐)
# 利用所有其他行的各列取值的加权平均(相似度为权值),为某行各列提供估值
def getRecommendations(prefs,row_name,similarity=sim_pearson):
    totals={}
    simSums={}
    for other in prefs:
        # 不和自己做比较
        if other==row_name: continue
        sim=similarity(prefs,row_name,other)

        # 忽略评价值为0或为负的情况
        if sim<=0: continue
        for item in prefs[other]:
            # 只对自己还未有的列进行临时估值
            if item not in prefs[row_name] or prefs[row_name][item]==0:
                # 相似度*临时估值
                totals.setdefault(item,0)
                totals[item]+=prefs[other][item]*sim
                # 相似度之和
                simSums.setdefault(item,0)
                simSums[item]+=sim

    # 建立归一化列表
    rankings=[(total/simSums[item],item) for item,total in totals.items()]

    # 返回最终估值经过排序的列表
    rankings.sort()
    rankings.reverse()
    return rankings

匹配相似列

对于数据来说他无法识别每行的含义,只是在做行间运算。如果我们把数据矩阵进行转置,再做行间运算。那就是进行的列匹配。

数据集转置

def transformPrefs(prefs):
    result={}
    for row_name in prefs:
        for item in prefs[row_name]:
            result.setdefault(item,{})

            # 将行与列对调
            result[item][row_name]=prefs[row_name][item]
    return result

匹配相似列

# 匹配相似列,返回各列的匹配集合(因为各列的匹配可提前在用户登陆前完成),
# 根据转置后的偏好数据集,获取每列相似的n个其他列
def calculateSimilarItems(prefs,n=10):
    # 建立字典,以给出与这些列最为相近的所有其他列
    itemMatch={}
    # 以列为中心对偏好矩阵实施转置处理
    itemPrefs=transformPrefs(prefs)
    c=0
    for item in itemPrefs:
        # 针对大数据集更新状态变量
        c+=1
        if c%100==0: print("%d / %d" % (c,len(itemPrefs)))
        # 寻找最为相近的列
        scores=topMatches(itemPrefs,item,n=n,similarity=sim_distance)
        itemMatch[item]=scores

    return itemMatch    #返回每列匹配的其他列

利用相似列,对某一行的各列空白处进行估值

与根据相似行对各列的空白值进行估计类似。

# 利用相似列,对某一行的各列进行估值,(估计影片评分,并排名推荐):根据偏好数据集和提前构造好的物品匹配库,向用户推荐物品
def getRecommendedItems(prefs,itemMatch,row_name):
    onerow=prefs[row_name]  #获取当前行所拥有的列
    scores={}
    totalSim={}
    # 循环遍历由当前行所拥有的列
    for (item,rating) in onerow.items( ):

        # 循环遍历与当前列相似的列
        for (similarity,item2) in itemMatch[item]:

            # 忽略行已经拥有的列
            if item2 in onerow: continue
            # 估值与相似度的加权之和
            scores.setdefault(item2,0)
            scores[item2]+=similarity*rating
            # 全部相似度之和
            totalSim.setdefault(item2,0)
            totalSim[item2]+=similarity

    # 将每个合计值除以加权和,求出平均值
    rankings=[(score/totalSim[item],item) for item,score in scores.items( )]

    # 按最高值到最低值的顺序,返回估值排行
    rankings.sort( )
    rankings.reverse( )
    return rankings

运行试验

有了上面的算法,我们就可以来尝试效果了。

if __name__=="__main__":     #只有在执行当前模块时才会运行此函数
    #利用相似人推荐相似物品
    rankings = getRecommendations(prefs,'name7')
    print(rankings)   #打印推荐排名
    #利用相似物品推荐相似物品
    itemMatch = calculateSimilarItems(prefs)  # 提前计算所有物品的相似物品
    rankings = getRecommendedItems(prefs,itemMatch,'name7')
    print(rankings)  #打印推荐排名

应该选用哪一种相似性度量方式

我们在此处已经介绍了两种不同的度量方式,但实际上,还有许多其他的衡量两个数组相似度的方式。例如jaccard系数和曼哈顿距离算法。使用哪种方式最优取决于具体的应用。如果你想看看哪种方式最优建议你都尝试一下。

距离度量总结参考:https://blog.csdn.net/luanpeng825485697/article/details/80032013

矩阵分解的代码

from numpy import *


def load_data(path):
    f = open(path)
    data = []
    for line in f.readlines():
        arr = []
        lines = line.strip().split("\t")
        for x in lines:
            if x != "-":
                arr.append(float(x))
            else:
                arr.append(float(0))
        # print arr
        data.append(arr)
    # print data
    return data


def gradAscent(data, K):
    dataMat = mat(data)
    print(dataMat)
    m, n = shape(dataMat)
    p = mat(random.random((m, K)))
    q = mat(random.random((K, n)))

    alpha = 0.0002
    beta = 0.02
    maxCycles = 10000

    for step in range(maxCycles):
        for i in range(m):
            for j in range(n):
                if dataMat[i, j] > 0:
                    # print dataMat[i,j]
                    error = dataMat[i, j]
                    for k in range(K):
                        error = error - p[i, k] * q[k, j]
                    for k in range(K):
                        p[i, k] = p[i, k] + alpha * (2 * error * q[k, j] - beta * p[i, k])
                        q[k, j] = q[k, j] + alpha * (2 * error * p[i, k] - beta * q[k, j])

        loss = 0.0
        for i in range(m):
            for j in range(n):
                if dataMat[i, j] > 0:
                    error = 0.0
                    for k in range(K):
                        error = error + p[i, k] * q[k, j]
                    loss = (dataMat[i, j] - error) * (dataMat[i, j] - error)
                    for k in range(K):
                        loss = loss + beta * (p[i, k] * p[i, k] + q[k, j] * q[k, j]) / 2

        if loss < 0.001:
            break
        # print step
        if step % 1000 == 0:
            print(loss)

    return p, q


if __name__ == "__main__":
    dataMatrix = load_data("./data")

    p, q = gradAscent(dataMatrix, 5)
    '''
    p = mat(ones((4,10)))
    print p
    q = mat(ones((10,5)))
    '''
    result = p * q
    print(p)
    print(q)

    print(result)

没有更多推荐了,返回首页