深入理解Spark ML:基于ALS矩阵分解的协同过滤算法与源码分析

Posted 小爷毛毛(卓寿杰)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解Spark ML:基于ALS矩阵分解的协同过滤算法与源码分析相关的知识,希望对你有一定的参考价值。

http://blog.csdn.net/u011239443/article/details/51752904

1. 引言

随着互联网的迅猛发展,为了满足人们在繁多的信息中获取自己需要内容的需求,个性化推荐应用而生。协同过滤推荐是其中运用最为成功的技术之一。其中,基于用户的最近邻法根据相似用户的评分来预测当前用户的评分。然而,在用户数量以及用户评分不足的情况下,该方法存在冷启动和数据稀疏的问题。为了解决这两个问题,业界提出了提出了基于项的最近邻法,利用项之间相似性稳定的特点可以离线计算相似性,降低了在线计算量,提高了推荐效率,但同样存在冷启动和数据稀疏问题。若使用 矩 阵 分 解 中 的 奇 异 值 分 解 ( Singular Value Decomposition,SVD) 减少评分矩阵的维数,之后应用最近邻法预测评分,一定程度上解决了同义词问题,但由于评分矩阵中大部分的评分是分解之前填充的,所以得到的特征矩阵不能直接用于评分。业界还提出了一种基于矩阵分解和用户近邻模型的算法,解决了数据稀疏的问题,但存在模型过拟合的问题。而协同过滤提出了一种支持不完整评分矩阵的矩阵分解方法,不用对评分矩阵进行估值填充,有很好的推荐精度。在 Netflix推荐系统竞赛中的应用表明,该矩阵分解相对于其他的推荐算法能产生更精确的推荐。 [ 1   2 ] ^[1~2] [1 2]

在矩阵分解推荐算法中,每项评分预测都需要整合现有评分集的信息。随着用户数与项目数的增长,算法的计算量也会随着增长,单机模式的推荐算法逐渐难以满足算法的计算以及推送的即时性需求,因此分布式推荐算法成为推荐算法中研究的一个新的方向。业界提出了分布式的矩阵分解算法,为矩阵分解的并行计算提供了一种可行的解决方案,但其使用的MapReduce框架在各计算节点的迭代计算中会产生过多的磁盘文件读写操作,影响了算法的执行效率。

本文旨在深入与Spark并行计算框架结合,探索协同过滤算法原理与在Spark上的实现,来解决大数据情况下矩阵分解推荐算法时间代价过高的问题。

2. 基于ALS矩阵分解协同过滤算法

如上述提及的,协同过滤提出了一种支持不完整评分矩阵的矩阵分解方法,不用对评分矩阵进行估值填充,有很好的推荐精度。Spark MLlib中实现的基于ALS矩阵分解协同过滤算法。下面我们来介绍下ALS矩阵分解

2.1 矩阵分解模型

用户对物品的打分行为可以表示成一个评分矩阵A(m*n),表示m个用户对n各物品的打分情况。如下表所示:

U\\Vv1v2v3v4
u143?5
u2?545
u3??33
u45533
u5215?

其中, A ( i , j ) A(i,j) A(i,j)表示用户 u s e r i user_i useri对物品 i t e m j item_j itemj的打分。但是,用户不会对所以物品打分,表中"?"表示用户没有打分的情况,所以这个矩阵A很多元素都是空的,我们称其为“缺失值(missing value)”。协同过滤提出了一种支持不完整评分矩阵的矩阵分解方法,不用对评分矩阵进行估值填充。在推荐系统中,我们希望得到用户对所有物品的打分情况,如果用户没有对一个物品打分,那么就需要预测用户是否会对该物品打分,以及会打多少分。这就是所谓的“矩阵补全(填空)”。

ALS 的核心假设是:打分矩阵A是近似低秩的,即一个 m ∗ n m*n mn的打分矩阵 A A A 可以用两个小矩阵 U ( m ∗ k ) U(m*k) U(mk) V ( n ∗ k ) V(n*k) V(nk)的乘积来近似:

A ≈ U V T , k < < m , n \\large A \\approx UV^T , k << m,n AUVT,k<<m,n

我们把打分理解成相似度,那么“打分矩阵 A ( m ∗ n ) A(m*n) A(mn)”就可以由“用户喜好特征矩阵 U ( m ∗ k ) U(m*k) U(mk)”和“产品特征矩阵 V ( n ∗ k ) V(n*k) V(nk)”的乘积。

2.2 交替最小二乘法(ALS)

我们使用用户喜好特征矩阵 U ( m ∗ k ) U(m*k) U(mk)中的第i个用户的特征向量 u i u_i ui,和产品特征矩阵 V ( n ∗ k ) V(n*k) V(nk)第j个产品的特征向量 v j v_j vj来预测打分矩阵 A ( m ∗ n ) A(m*n) A(mn)中的 a i j a_ij aij。我们可以得出一下的矩阵分解模型的损失函数为:

C = ∑ ( i , j ) ∈ R [ ( a i j − u i v j T ) 2 + λ ( u i 2 + v j 2 ) ] \\large C = \\sum\\limits_(i,j)\\in R[(a_ij - u_iv_j^T)^2+\\lambda(u_i^2+v_j^2)] C=(i,j)R[(aijuivjT)2+λ(ui2+vj2)]

有了损失函数之后,下面就开始介绍优化方法。通常的优化方法分为两种:交叉最小二乘法(alternative least squares)和随机梯度下降法(stochastic gradient descent)。Spark使用的是交叉最小二乘法(ALS)来最优化损失函数。算法的思想就是:我们先随机生成然后固定它求解,再固定求解,这样交替进行下去,直到取得最优解 m i n ( C ) min(C) min(C)。因为每步迭代都会降低误差,并且误差是有下界的,所以 ALS 一定会收敛。但由于问题是非凸的,ALS 并不保证会收敛到全局最优解。但在实际应用中,ALS 对初始点不是很敏感,是否全局最优解造成的影响并不大。

算法的执行步骤:

  • 先随机生成一个。一般可以取0值或者全局均值。

  • 固定,即认为是已知的常量,来求解:
    C = ∑ ( i , j ) ∈ R [ ( a i j − u i ( 0 ) v j T ) 2 + λ ( ( u i 2 ) ( 0 ) + v j 2 ) ] \\large C = \\sum\\limits_(i,j)\\in R[(a_ij - u_i^(0)v_j^T)^2+\\lambda((u_i^2)^(0)+v_j^2)] C=(i,j)R[(aijui(0)vjT)2+λ((ui2)(0)+vj2)]
    由于上式中只有 v j v_j vj一个未知变量,因此C的最优化问题转化为最小二乘问题,用最小二乘法求解 v j v_j vj的最优解:
    固定$ j , j\\in (1,2,…,n) ,则:等式两边关于为 ,则:等式两边关于为 ,则:等式两边关于为v_j$求导得:

    $\\large \\fracd(c)d(v_j) $
    $\\large= \\fracdd(v_j)(\\sum\\limits_i=1^m[(a_ij - u_i^(0)v_j^T)^2+\\lambda((u_i^2)^(0)+v_j^2)])$ 
    $\\large= \\sum\\limits_i=1^m[2(a_ij - u_i^(0)v_j^T)(- (u_i^T)^(0))+2\\lambda v_j]$
    $\\large= 2\\sum\\limits_i=1^m[( u_i^(0)(u_i^T)^(0)+\\lambda)v_j-a_ij(u_i^T)^(0)]$
    

d ( c ) d ( v j ) = 0 \\large \\fracd(c)d(v_j) =0 d(vj)d(c)=0,可得:
$\\large\\sum\\limits_i=1^m[( u_i(0)(u_iT)(0)+\\lambda)v_j]=\\sum\\limits_i=1m a_ij(u_iT)(0) $
= > ( U ( 0 ) ( U T ) ( 0 ) + λ E ) v j = a j T U ( 0 ) \\large => (U^(0)(U^T)^(0) + \\lambda E)v_j = a_j^TU^(0) =>(U(0)(UT)(0)+λE)vj=ajTU(0)

M 1 = U ( 0 ) ( U T ) ( 0 ) + λ E , M 2 = a j T U ( 0 ) M_1 = U^(0)(U^T)^(0) + \\lambda E , M_2 = a_j^TU^(0) M1=U(0)(UT)(0)+λE,M2=ajTU(0),则 v j = M 1 − 1 M 2 v_j = M_1^-1M_2 vj=M11M以上是关于深入理解Spark ML:基于ALS矩阵分解的协同过滤算法与源码分析的主要内容,如果未能解决你的问题,请参考以下文章

spark实现ALS矩阵分解-附scala代码

基于Spark的Als算法+自迭代+Spark2.0新写法

通过 pyspark.ml CrossValidator 调整隐式 pyspark.ml ALS 矩阵分解模型的参数

大数据Spark MLlib基于模型的协同过滤

推荐算法协同过滤算法代码(pyspark | ALS)

Spark2.0 协同过滤推荐