推荐系统协同过滤在Spark中的实现向量iu算法余弦

作者:vivo互联网服务器团队-TangShutao

现如今推荐无处不在,例如抖音、淘宝、京东App均能见到推荐系统的身影,其背后涉及许多的技术。

本文以经典的协同过滤为切入点,重点介绍了被工业界广泛使用的矩阵分解算法,从理论与实践两个维度介绍了该算法的原理,通俗易懂,希望能够给大家带来一些启发。

笔者认为要彻底搞懂一篇论文,最好的方式就是动手复现它,复现的过程你会遇到各种各样的疑惑、理论细节。

一、背景

1.1引言

在信息爆炸的二十一世纪,人们很容易淹没在知识的海洋中,在该场景下搜索引擎可以帮助我们迅速找到我们想要查找的内容。

在电商场景,如今的社会物质极大丰富,商品琳琅满目,种类繁多。消费者很容易挑花眼,即用户将会面临信息过载的问题。

为了解决该问题,推荐引擎应运而生。例如我们打开淘宝App,JDapp,B站视频app,每一个场景下都有推荐的模块。

那么此时有一个幼儿园小朋友突然问你,为什么JD给你推荐这本《程序员颈椎康复指南》?你可能会回答,因为我的职业是程序员。

接着小朋友又问,为什么《Spark大数据分析》这本书排在第6个推荐位,而《Scala编程》排在第2位?这时你可能无法回答这个问题。

为了回答该问题,我们设想下面的场景:

在JD的电商系统中,存在着用户和商品两种角色,并且我们假设用户都会对自己购买的商品打一个0-5之间的分数,分数越高代表越喜欢该商品。

基于此假设,我们将上面的问题转化为用户对《程序员颈椎康复指南》,《Spark大数据分析》,《Scala编程》这三本书打分的话,用户会打多少分(用户之前未购买过这3本书)。因此物品在页面的先后顺序就等价于预测用户对这些物品的评分,并且根据这些评分进行排序的问题。

为了便于预测用户对物品的评分问题,我们将所有三元组(User,Item,Rating),即用户User给自己购买的商品Item的评分为Rating,组织为如下的矩阵形式:

其中,表格包含m个用户和n个物品,将表格定义为评分矩阵Rm×n"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">Rm×nRm×n,其中的元素ru,i"role="presentation">ru,iru,i表示第u个用户对第i个物品的评分。

例如,在上面的表格中,用户user-1购买了物品item-1,item-3,item-4,并且分别给出了4,2,5的评分。最终,我们将原问题转化为预测白色空格处的数值。

1.2协同过滤

协同过滤,简单来说是利用与用户兴趣相投、拥有共同经验之群体的喜好来推荐给用户感兴趣的物品。兴趣相投使用数学语言来表达就是相似度(人与人,物与物)。因此,根据相似度的对象,协同过滤可以分为基于用户的协同过滤和基于物品的协同过滤。

以评分矩阵为例,以行方向观测评分矩阵,每一行代表每个用户的向量表示,例如用户user-1的向量为[4,0,2,5,0,0]。以列方向观测评分矩阵,每一列表示每个物品的向量表示,例如物品item-1的向量为[4,3,0,0,5]。

基于向量表示,相似度的计算有多种公式,例如余弦相似度,欧氏距离,皮尔森。这里我们以余弦相似度为例,它是我们中学学过的向量夹角(中学只涉及2维和3维)的高维推广,余弦相似度公式很容易理解和使用。给定两个向量A={a1,,an}"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">A={a1,,an}A={a1,,an}和B={b1,,bn}"role="presentation">B={b1,,bn}B={b1,,bn},其夹角定义如下:

例如,我们计算user-3和user-4的余弦相似度,二者对应的向量分别为[0,2,0,3,0,4],[0,3,3,5,4,0]

向量夹角的余弦值越接近1代表两个物品方向越接近平行,也就是越相似,反之越接近-1代表两个物品方向越接近反向,表示两个物品相似度接近相反,接近0,表示向量接近垂直/正交,两个物品几乎无关联。显然,这和人的直觉完全一致。

我们用《血族第一部》在向量库(存储向量的数据库,该系统能够根据输入向量,用相似度公式在库中进行检索,找出TopN的候选向量)里面进行相似度检索,找到了前7部高相似度的影片,值得注意的是第一部是自己本身,相似度为1.0,其他三部是《血族》的其他3部同系列作品。

1.2.1基于用户的协同过滤(UserCF)

基于用户的协同过滤分为两步

找出用户相似度TopN的若干用户。

根据TopN用户评分的物品,形成候选物品集合,利用加权平均预估用户u对每个候选物品的评分。

例如,由用户u的相似用户{u1,u3,u5,u9}可得候选物品为

我们现在预测用户u对物品i1的评分,由于物品在两个用户{u1,u5}的购买记录里,因此用户u对物品i1的预测评分为:

其中sim(u,u1)"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">sim(u,u1)sim(u,u1)表示用户u与用户u1"role="presentation">u1u1的相似度。

在推荐时,根据用户u对所有候选物品的预测分进行排序,取TopM的候选物品推荐给用户u即可。

1.2.2基于物品的协同过滤(ItemCF)

基于物品的协同过滤分为两步

在用户u购买的物品集合中,选取与每一个物品TopN相似的物品。

TopN相似物品形成候选物品集合,利用加权平均预估用户u对每个候选物品的评分。

例如,我们预测用户u对物品i3的评分,由于物品i3与物品{i6,i1,i9}均相似,因此用户u对物品i3的预测评分为:

1.2.3UserCF与ItemCF的比较

我们对ItemCF和UserCF做如下总结:

ItemCF给用户推荐那些和他之前喜欢的物品类似的物品,即ItemCF的推荐结果着重于维系用户的历史兴趣,推荐更加个性化,反应用户自己的兴趣。在实际应用中,图书、电影平台使用ItemCF,比如豆瓣、亚马逊、Netflix等。

除了基于用户和基于物品的协同过滤,还有一类基于模型的协同过滤算法,如上图所示。此外基于用户和基于物品的协同过滤又可以归类为基于邻域(K-NearestNeighbor,KNN)的算法,本质都是在找"TopN邻居",然后利用邻居和相似度进行预测。

二、矩阵分解

经典的协同过滤算法本身存在一些缺点,其中最明显的就是稀疏性问题。我们知道评分矩阵是一个大型稀疏矩阵,导致在计算相似度时,两个向量的点积等于0(以余弦相似度为例)。为了更直观的理解这一点,我们举例如下:

romsklearn.metrics.pairwiseimportcosine_similaritya=[[0,0,0,3,2,0,3.5,0,1],[0,1,0,0,0,0,0,0,0],[0,0,1,0,0,0,0,0,0],[4.1,3.8,4.6,3.8,4.4,3,4,0,3.6]cosine_similarity(a)#array([[1.,0.,0.,0.66209271],#[0.,1.,0.,0.34101639],#[0.,0.,1.,0.41280932],#[0.66209271,0.34101639,0.41280932,1.]])

我们从评分矩阵中抽取item1-item4的向量,并且利用余弦相似度计算它们之间的相似度。

通过相似度矩阵,我们可以看到物品item-1,item-2,item-3的之间的相似度均为0,而且与item-1,item-2,item-3最相似的物品都是item-4,因此在以ItemCF为基础的推荐场景中item-4将会被推荐给用户。

综上,我们可以看到经典的基于用户/物品的协同过滤算法有天然的缺陷,无法处理稀疏场景。为了解决该问题,矩阵分解被提出。

2.1显示反馈

我们将用户对物品的评分行为定义为显示反馈。基于显示反馈的矩阵分解是将评分矩阵Rm×n用两个矩阵Xm×k"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">Xm×k和Yn×k的乘积近似表示,其数学表示如下:

为了求解矩阵Xm×k"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">Xm×kXm×k和Yn×k,需要最小化平方误差损失函数,来尽可能地使得两个矩阵的乘积逼近评分矩阵Rm×n"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">Rm×nRm×n,即

其中,λ(∑uxuTxu+∑iyiTyi)"role="presentation">λ(∑uxTuxu+∑iyTiyi)λ(∑uxuTxu+∑iyiTyi)为惩罚项,λ为惩罚系数/正则化系数,xu表示第u个用户的k维特征向量,yi"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">yiyi表示第i个物品的k维特征向量。

全体用户的特征向量构成了用户矩阵Xm×k"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">Xm×kXm×k,全体物品的特征向量构成了物品矩阵Yn×k。

我们训练模型的时候,就只需要训练用户矩阵中的m×k个参数和物品矩阵中的n×k个参数。因此,协同过滤就成功转化成了一个优化问题。

2.2预测评分

通过模型训练(即求解模型系数的过程),我们得到用户矩阵Xm×k和物品矩阵Yn×k"role="presentation">Yn×kYn×k,全部用户对全部物品的评分预测可以通过Xm×k(Yn×k)T"role="presentation">Xm×k(Yn×k)TXm×k(Yn×k)T获得。如下图所示。

得到全部的评分预测后,我们就可以对每个物品进行择优推荐。需要注意的是,用户矩阵和物品矩阵的乘积,得到的评分预估值,与用户的实际评分不是全等关系,而是近似相等的关系。如上图中两个矩阵粉色部分,用户实际评分和预估评分都是近似的,有一定的误差。

2.3理论推导

矩阵分解ALS的理论推导网上也有不少,但是很多推导不是那么严谨,在操作向量导数时有的步骤甚至是错误的。有的博主对损失函数的求和项理解解出现错误,例如

但是评分矩阵是稀疏的,求和并不会贯穿整个用户集和物品集。正确的写法应该是

其中,(u,i)isknown"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">(u,i)isknown(u,i)isknown表示已知的评分项。

我们在本节给出详细的、正确的推导过程,一是当做数学小练习,其次也是对算法有更深层的理解,便于阅读SparkALS的源码。

将(u,i)isknown"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">(u,i)isknown(u,i)isknown使用数学语言描述,矩阵分解的损失函数定义如下:

其中K为评分矩阵中已知的(u,i)集合。例如下面的评分矩阵对应的K为

求解上述损失函数存在两种典型的优化方法,分别为

交替最小二乘(AlternatingLeastSquares,ALS)

随机梯度下降(StochasticGradientDescent,SGD)

交替最小二乘,指的是固定其中一个变量,利用最小二乘求解另一个变量,以此交替进行,直至收敛或者到达最大迭代次数,这也是“交替”一词的由来。

随机梯度下降,是优化理论中最常用的一种方式,通过计算梯度,然后更新待求的变量。

在矩阵分解算法中,Spark最终选择了ALS作为官方的唯一实现,原因是ALS很容易实现并行化,任务之间没有依赖。

下面我们动手推导一下整个计算过程,在机器学习理论中,微分的单位一般在向量维度,很少去对向量的分量为偏微分推导。

首先我们固定物品矩阵Y,将物品矩阵Y看成常量。不失一般性,我们定义用户u评分过的物品集合为Iu"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">IuIu,利用损失函数对向量xu"role="presentation">xuxu求偏导,并且令导数等于0可得:

等式两边取转置,我们有

为了化简∑i∈IuyiyiT"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">∑i∈IuyiyTi∑i∈IuyiyiT与

∑i∈Iuru,iyi"role="presentation">∑i∈Iuru,iyi∑i∈Iuru,iyi,我们将Iu"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">Iu"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">Iu展开。

假设Iu={ic1,,icN},其中N表示用户u评分过的物品数量,ici"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">iciici表示第ci"role="presentation">cici个物品对应的索引/序号,借助于Iu"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;background-color:rgb(255,255,255);">Iu,我们有

其中,

YIu"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">YIuYIu为以Iu={ic1,icN}"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">Iu={ic1,icN}Iu={ic1,icN}为行号在物品矩阵Y中选取的N个行向量形成的子矩阵

Ru,Iu为以Iu={ic1,icN}为索引,在评分矩阵R的第u行的行向量中选取的N个元素,形成的子行向量

因此,我们有

网上的博客,许多博主给出类似下面形式的结论不是很严谨,主要是损失函数的理解不到位导致的。

同理,我们定义物品i被评分的用户集合为Ui={ud1,udM}"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">Ui={ud1,udM}Ui={ud1,udM}

根据对称性可得

XUi"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">XUiXUi为以Ui={ud1,,udM}"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">Ui={ud1,,udM}Ui={ud1,,udM}为行号在用户矩阵X中选取的M个行向量形成的子矩阵

Ri,Ui"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">Ri,UiRi,Ui为以Ui={ud1,,udM}"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">Ui={ud1,,udM}Ui={ud1,,udM}为索引,在评分矩阵R的第i列的列向量中选取的M个元素,形成的子列向量

此外,Ik"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">IkIk为单位矩阵

如果读者感觉上述的推导还是很抽象,我们也给一个具体实例来体会一下中间过程

注意到损失函数是一个标量,这里我们只展开涉及到x1,1,x1,2"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">x1,1,x1,2x1,1,x1,2的项,如下所示

让损失函数对x1,1,x1,2"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">x1,1,x1,2x1,1,x1,2分别求偏导数可以得到

写成矩阵形式可得

利用我们上述的规则,很容易检验我们导出的结论。

总结来说,ALS的整个算法过程只有两步,涉及2个循环,如下图所示:

算法使用RMSE(root-mean-squareerror)评估误差。

当RMSE值变化很小时或者到达最大迭代步骤时,满足收敛条件,停止迭代。

“Talkischeap.Showmethecode.”作为小练习,我们给出上述伪代码的Python实现。

#迭代10次foriterinrange(10):foruinrange(1,m+1):Iu=np.array(X_idx_dict[u])YIu=Y[Iu-1]YIuT=YIu.TRuIu=R[u-1,Iu-1]xu=linear_solve(YIuT.dot(YIu)+_lambda*np.eye(k),YIuT.dot(RuIu))X[u-1]=xuforiinrange(1,n+1):Ui=np.array(Y_idx_dict[i])XUi=X[Ui-1]XUiT=XUi.TRiUi=R.T[i-1,Ui-1]yi=linear_solve(XUiT.dot(XUi)+_lambda*np.eye(k),XUiT.dot(RiUi))Y[i-1]=yi

最终,我们打印用户矩阵,物品矩阵,预测的评分矩阵如下,可以看到预测的评分矩阵非常逼近原始评分矩阵。

#Xarray([[1.30678487,2.03300876,3.70447639],[4.96150381,1.03500693,1.62261161],[6.37691007,2.4290095,1.03465981],[0.41680155,3.31805612,3.24755801],[1.26803845,3.57580564,2.08450113]])#Yarray([[0.24891282,1.07434519,0.40258993],[0.12832662,0.17923216,0.72376732],[-0.00149517,0.77412863,0.12191856],[0.12398438,0.46163336,1.05188691],[0.07668894,0.61050204,0.59753081],[0.53437855,0.20862131,0.08185176]])#X.dot(Y.T)预测评分array([[4.00081359,3.2132548,2.02350084,4.9972158,3.55491072,1.42566466],[3.00018371,1.99659282,0.99163666,2.79974661,1.98192672,3.00005934],[4.61343295,2.00253692,1.99697545,3.00029418,2.59019481,3.99911584],[4.97591903,2.99866546,2.96391664,4.99946603,3.99816006,1.18076534],[4.99647978,2.31231627,3.02037696,4.0005876,3.5258348,1.59422188]])#原始评分矩阵array([[4,0,2,5,0,0],[3,2,1,0,0,3],[0,2,0,3,0,4],[0,3,3,5,4,0],[5,0,3,4,0,0]])

三、SparkALS应用

Spark的内部实现并不是我们上面所列的算法,但是核心原理是完全一样的,Spark实现的是上述伪代码的分布式版本,具体算法参考Large-scaleParallelCollaborativeFilteringfortheNetflixPrize。其次,查阅Spark的官方文档,我们也注意到,Spark使用的惩罚函数与我们上文的有细微的差别。

其中nu,ni"role="presentation"SegoeUI",Roboto,Oxygen,Ubuntu,"FiraSans","DroidSans","HelveticaNeue",sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;orphans:2;widows:2;-webkit-text-stroke-width:0px;background-color:rgb(255,255,255);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;">nu,ninu,ni分别表示用户u打分的物品数量和物品i被打分的用户数量。即

本小节通过两个案例来了解SparkALS的具体使用,以及在面对互联网实际工程场景下的应用。

3.1Demo案例

以第一节给出的数据为例,将三元组(User,Item,Rating)组织为als-demo-data.csv,该demo数据集涉及5个用户和6个物品。

userId,itemId,rating1,1,41,3,21,4,52,1,32,2,22,3,12,6,33,2,23,4,33,6,44,2,34,3,34,4,54,5,45,1,55,3,35,4,4

使用Spark的ALS类使用非常简单,只需将三元组(User,Item,Rating)数据输入模型进行训练。

importorg.apache.spark.sql.SparkSessionimportorg.apache.spark.ml.recommendation.ALSvalspark=SparkSession.builder().appName("als-demo").master("local[*]").getOrCreate()valrating=spark.read.options(Map("inferSchema"->"true","delimiter"->",","header"->"true")).csv("./data/als-demo-data.csv")//展示前5条评分记录rating.show(5)valals=newALS().setMaxIter(10)//迭代次数,用于最小二乘交替迭代的次数.setRank(3)//隐向量的维度.setRegParam(0.01)//惩罚系数.setUserCol("userId")//user_id.setItemCol("itemId")//item_id.setRatingCol("rating")//评分列valmodel=als.fit(rating)//训练模型//打印用户向量和物品向量model.userFactors.show(truncate=false)model.itemFactors.show(truncate=false)//给所有用户推荐2个物品model.recommendForAllUsers(2).show()

上述代码在控制台输出结果如下:

|userId|itemId|rating||1|1|4||1|3|2||1|4|5||2|1|3||2|2|2|onlyshowingtop5rows|id|features||1|[-0.17339179,1.3144133,0.04453602]||2|[-0.3189066,1.0291641,0.12700711]||3|[-0.6425665,1.2283803,0.26179287]||4|[0.5160747,0.81320006,-0.57953185]||5|[0.645193,0.26639006,0.68648624]||id|features||1|[2.609607,3.2668495,3.554771]||2|[0.85432494,2.3137972,-1.1198239]||3|[3.280517,1.9563107,0.51483333]||4|[3.7446978,4.259611,0.6640027]||5|[1.6036265,2.5602736,-1.8897828]||6|[-1.2651576,2.4723763,0.51556784]||userId|recommendations||1|[[4,4.9791617],[1,3.9998217]]|//对应物品的序号和预测评分|2|[[4,3.273963],[6,3.0134287]]||3|[[6,3.9849386],[1,3.2667015]]||4|[[4,5.011649],[5,4.004795]]||5|[[1,4.994258],[4,4.0065994]]|

我们使用numpy来验证Spark的结果,并且用Excel可视化评分矩阵。

importnumpyasnpX=np.array([[-0.17339179,1.3144133,0.04453602],[-0.3189066,1.0291641,0.12700711],[-0.6425665,1.2283803,0.26179287],[0.5160747,0.81320006,-0.57953185],[0.645193,0.26639006,0.68648624]])Y=np.array([[2.609607,3.2668495,3.554771],[0.85432494,2.3137972,-1.1198239],[3.280517,1.9563107,0.51483333],[3.7446978,4.259611,0.6640027],[1.6036265,2.5602736,-1.8897828],[-1.2651576,2.4723...

THE END
1.基于协同过滤算法的推荐系统推荐系统有着广泛的应用,电影推荐,商品推荐等都用到推荐系统。本文介绍协同过滤算法的基本原理,进而理解推荐系统的实现原理。 推荐系统的描述 我们以电影推荐系统来看一下怎么样以机器学习的角度来描述推荐系统。我们记 $n_u$ 为用户的数量,$n_m$ 为电影的数量,$r(i,j) = 1$ 表示用户 j 对电影 i 进行过...https://www.jianshu.com/p/9b06ef8c79fa
2.基于协同过滤算法图书推荐系统的设计与实现.pdf论文题目:基于协同过滤算法的图书推荐系统的 设计与实现 摘要 随着网络和信息技术的飞速发展,电子图书资源的数量也在以惊人的速度增 长,越来越多的用户通过图书购买网站购买图书或在图书阅读网站上阅读电子书, 不管是网上购书还是网上读书都会面临一个相同的问题,如何从海量的图书资源 ...https://max.book118.com/html/2024/0217/7053136045006042.shtm
3.基于内容与PTUI协同过滤算法的个性化学习系统基于内容与PTUI协同过滤算法的个性化学习系统 项目类型: 创新训练项目 所属学校: 南昌工程学院 项目期限: 一年期(2022-05 至 2023-05) 所属一级学科: 工学 所属二级学科: 计算机类 立项时间: 2022-06-30 结题时间: 2023-11-16 项目成员: 姓名专业班级所在学院项目中的分工成员类型 ...http://jxdc.jxedu.gov.cn/cxcypt/Index/ItemDetail/e6051e10-240f-4ab7-992d-269eb49cb410
4.基于流形近邻的协同过滤算法AET关键词: 流形近邻;距离空间;协同过滤;视觉距离;最小最大距离;推荐系统 0引言 协同过滤是Web 3.0时代一个新颖的技术,被广泛应用于各类电子商务网站。通常协同过滤算法分为两大类:基于内存的协同过滤算法和基于模型的协同过滤算法[1]。基于内存的算法[2]首先找到k个近邻,然后根据近邻进行推荐。基于模型的算法[3 5]...http://www.chinaaet.com/article/3000016485
1.基于协同过滤算法的绿色食品推荐系统(10075)Java毕业设计基于ssm协同过滤算法的绿色食品推荐系统 q_1262330535的博客 1087 系统的开发离不开前期的需求分析,这个阶段就是让程序员知道自己该做什么事情,在进行需求分析的时候,着重点就是用户对系统的功能要求,这个阶段要是分析得很到位,系统开发出来投入使用时,用户就会发现系统的功能跟用户需求保持一致,程序稳定...https://blog.csdn.net/m0_72438098/article/details/143813340
2.基于协同过滤算法的论文推荐系统研究与设计基于上述问题,本文设计了一个论文推送系统。改进了传统基于用户的协同过滤算法,在计算用户与用户之间的相似度时加权融合了用户点击和搜索词的相似度,并且计算点击得分的时候会对点击文章的时间做衰减处理,进而更加精准地召回近邻用户。通过实验,本文选取多样性和准确率这两个指标来对本文所提出的论文推荐算法进行评价。https://cdmd.cnki.com.cn/Article/CDMD-10488-1018203115.htm
3.基于协同过滤算法的安规考核系统试题推荐方法研究本文主要完成了以下内容:1.研究了基于用户的协同过滤算法和基于物品的协同过滤算法,比较两者在不同推荐系统中的应用情况,比较其优缺点。结合安规考核系统的实际情况采用基于物品的协同过滤算法实现本课题的研究,根据需求在数据库中设计用于保存用户-物品评分矩阵和物品-物品相似度矩阵的数据表。2.学习中文分词技术,收集...https://wap.cnki.net/touch/web/Dissertation/Article/10079-1019233359.nh.html
4.推荐系统算法实战协同过滤CF算法(CollaborativeFiltering...协同过滤推荐(Collaborative Filtering Recommendation)。 仅仅基于用户行为数据设计的推荐算法一般称为协同过滤算法。学术界对协同过滤算法 进行了深入研究,提出了很多方法,比如基于邻域的方法(neighborhood-based)、隐语义模型 (latent factor model)、基于图的随机游走算法(random walk on graph)等。在这些方法中, 最著名...https://blog.51cto.com/u_15236724/5968286
5.从零开始学推荐系统一:基于邻域的算法腾讯云开发者社区本系列文章会从最简单的推荐系统到目前主流的推荐系统解决方案做总结。 1. 基于邻域的算法 基于邻域的算法是推荐系统中最基本的算法,在业界得到了广泛应用。基于邻域的算法分为两大类,一类是基于用户的协同过滤算法,另一类是基于物品的协同过滤算法。 1.1 基于用户的协同过滤算法(UserCF) ...https://cloud.tencent.com/developer/article/1694686
6.相似度算法(精选十篇)1.1基于语义资源的词语相似度算法 近年来, 一些诸如同义词词林、WordNet、知网这种大规模可量化的语言本体的诞生与发展, 为进行真实文本的语义分析和理解提供了强有力的资源支持。特别是最近几年“知网”等语义资源不断丰富发展, 中文语义研究方向逐渐增多。知网作为一个知识系统, 是一个网而不是树, 它主要反映概念...https://www.360wenmi.com/f/cnkeyytvb26n.html
7.基于时间和关系感知的图协同过滤跨域序列推荐受协同过滤思想启发,针对上述问题,提出基于时间和关系感知的图协同过滤跨域序列推荐(time and relation-aware graph collaborative filtering for cross-domain sequential recommendation,TRaGCF)算法.该算法主要包括3个模块:1)为获得用户行为序列中项目间高阶复杂的时序依赖关系,提出时间感知图注意力(time-aware graph atte...https://crad.ict.ac.cn/cn/article/doi/10.7544/issn1000-1239.202110545?viewType=HTML
8.希冀基于Apriori算法的投票模式挖掘 基于Apriori算法发现毒蘑菇相似特征 FP-Growth算法 基于FP-Growth算法Twiter数据挖掘 基于FP-Growth算法新闻网站点击流挖掘 数据降维 PCA算法 基于PCA算法的半导体制造数据降维 SVD算法 基于SVD的图像压缩 推荐系统 协同过滤算法 https://www.educg.com/ai.html