推荐领域的人一般都会听说过十年前 Netflix Prize 的比赛,随着Netflix Prize推荐比赛的成功举办,近年来隐语义模型(Latent Factor MOdel,LFM)受到越来越多的关注。隐语义模型最早在文本挖掘领域被提出,用于寻找文本的隐含语义,相关的模型常见的有潜在语义分析(Latent Semantic Analysis,LSA)、LDA(Latent Dirichlet Allocation)的主题模型(Topic Model)、矩阵分解(Matrix Factorization)等等。
其中矩阵分解技术是实现隐语义模型使用最广泛的一种方法,其思想也正来源于此,Yehuda Koren凭借矩阵分解模型勇夺Netflix Prize推荐比赛冠军,以矩阵分解为基础,Yehuda Koren在数据挖掘和机器学习相关的国际顶级会议(SIGIR,SIGKDD,RecSys等)发表了很多文章,将矩阵分解模型的优势发挥得淋漓尽致。实验结果表明,在个性化推荐中使用矩阵分解模型要明显优于传统的基于邻域的协同过滤(又称基于领域的协同过滤)方法,如UserCF、ItemCF等,这也使得矩阵分解成为了目前个性化推荐研究领域中的主流模型。
需要说明的是,协同过滤方法分为两大类,一类为上述基于领域的方法,第二类为基于模型的方法,即隐语义模型,矩阵分解模型是隐语义模型最为成功的一种实现,不作特别说明的情况下,本文将隐语义模型和矩阵分解看做同一概念,User-Item矩阵和User-Item评分矩阵为同一概念。
矩阵分解几个明显的特点,它具有协同过滤的 “集体智慧”,隐语义的 “深层关系”,以及机器学习的 “以目标为导向的有监督学习”。在了解了基于邻域的协同过滤算法后,集体智慧自不必多说,我们依次从 “隐因子” 和 “有监督学习” 的角度来了解矩阵分解的基本思路。
推荐算法中的矩阵分解最初的想法是从奇异值分解(Singular Value Decomposition,SVD)借鉴来的,也仅仅是借鉴,并非是标准的奇异值分解,勉强算是一个伪奇异值分解。具体的区别留在相关算法这一小节详说。
以 Netflix 用户对电影的评分矩阵为例,矩阵分解,直观上来说就是把原来的大矩阵,近似分解成两个小矩阵的乘积,在实际推荐计算时不再使用大矩阵,而是使用分解得到的两个小矩阵。按照矩阵分解的原理,我们会发现原来 的大矩阵会分解成 和 的两个小矩阵,这里多出来一个 k 维向量,就是隐因子向量(Latent Factor Vector),类似的表达还有隐因子、隐向量、隐含特征、隐语义、隐变量等。
基于矩阵分解的推荐算法的核心假设是用隐语义(隐变量)来表达用户和物品,他们的乘积关系就成为了原始的元素。这种假设之所以成立,是因为我们认为实际的交互数据是由一系列的隐变量的影响下产生的(通常隐变量带有统计分布的假设,就是隐变量之间,或者隐变量和显式变量之间的关系,我们往往认为是由某种分布产生的。),这些隐变量代表了用户和物品一部分共有的特征,在物品身上表现为属性特征,在用户身上表现为偏好特征,只不过这些因子并不具有实际意义,也不一定具有非常好的可解释性,每一个维度也没有确定的标签名字,所以才会叫做 “隐变量”。而矩阵分解后得到的两个包含隐变量的小矩阵,一个代表用户的隐含特征,一个代表物品的隐含特征,矩阵的元素值代表着相应用户或物品对各项隐因子的符合程度,有正面的也有负面的。
依然以电影为例,电影可能具有一些隐藏因子:演员、题材、主题、年代…,而用户针对这些隐因子有偏好特征属性,为了便于理解,我们假设隐因子数量 k 是 2,分别代表着喜剧片和动作片两种题材,矩阵分解后的两个小矩阵,分布代表着电影对这两种题材的符合程度以及用户对这两种题材的偏好程度,如下图:
通常情况下,隐因子数量 k 的选取要远远低于用户和电影的数量,大矩阵分解成两个小矩阵实际上是用户和电影在 k 维隐因子空间上的映射,这个方法其实是也是一种 “降维”(Dimension Reduction)过程,同时将用户和电影的表示转化为在这个 k 维空间上的分布位置,电影和用户的距离越接近表示用户越有可能喜欢这部电影,表现在数值上则是各项隐因子符合程度的正负性越一致。
我们再从机器学习的角度来了解矩阵分解,我们已经知道电影评分预测实际上是一个矩阵补全的过程,在矩阵分解的时候原来的大矩阵必然是稀疏的,即有一部分有评分,有一部分是没有评过分的,不然也就没必要预测和推荐了,所以整个预测模型的最终目的是得到两个小矩阵,通过这两个小矩阵的乘积来补全大矩阵中没有评分的位置。所以对于机器学习模型来说,问题转化成了如何获得两个最优的小矩阵。因为大矩阵有一部分是有评分的,那么只要保证大矩阵有评分的位置(实际值)与两个小矩阵相乘得到的相应位置的评分(预测值)之间的误差最小即可,其实就是一个均方误差损失,这便是模型的目标函数,具体的公式可参考相关算法这一小节。
这种带有隐因子的机器学习模型通常称为 “隐语义模型”(Latent Factor Model,LFM),因为隐因子的概念最早在文本领域被提出,用于找到文本的隐含语义,所以隐因子有时也称隐语义。而矩阵分解是隐语义模型的代表,在很多地方,会直接使用隐语义模型代表矩阵分解的这一类模型。隐语义模型的在推荐算法中的优势是对用户和物品信息中的隐含结构进行建模,从而能够挖掘更加深层次的用户和物品关系。
矩阵分解方法将高维User-Item评分矩阵映射为两个低维用户和物品矩阵,解决了数据稀疏性问题。
使用矩阵分解具有以下优点:
矩阵分解的不足主要有:
说到矩阵分解,我们首先想到的就是 奇异值分解 SVD。理论上,SVD 也可以直接用于推荐,我们可以把 User-Item 评分矩阵 A 进行 SVD 分解,并通过选择部分较大的一些奇异值来同时进行降维,也就是说矩阵 A 此时分解为:
其中 k 是矩阵 A 中较大的部分奇异值的个数,一般会远远的小于用户数和物品数。
如果我们要预测第i个用户对第j个物品的评分 , 则只需要计算 即可。通过这种方法,我们可以将评分表里面所有没有评分的位置得到一个预测评分,通过找到最高的若干个评分对应的物品推荐给用户。
思路很简单,但是奇异值分解在直接用于推荐的使用过程中存在很多问题:
传统的 SVD 无法直接应用于推荐系统,因此需要进行简化,接下来我们看看实际可以用于推荐系统的矩阵分解以及各种优化变种。
基本思想
Funk-SVD的核心思想认为用户的兴趣只受少数几个因素的影响,因此将稀疏且高维的User-Item评分矩阵分解为两个低维矩阵,即通过User、Item评分信息来学习到的用户特征矩阵P和物品特征矩阵Q,通过重构的低维矩阵预测用户对产品的评分。由于用户和物品的特征向量维度比较低,因而可以通过梯度下降(Gradient Descend)的方法高效地求解,分解示意图如下所示:
数学原理
Simon Funk 在博客上公开发表了一个只考虑已有评分记录的矩阵分解方法,称为 Funk-SVD,也就是被 Yehuda Koren 称为隐语义模型的矩阵分解方法。它的出发点为,既然将一个矩阵做 SVD 分解成 3 个矩阵很耗时,同时还面临稀疏的问题,那么我们能不能避开稀疏问题,同时只分解成两个矩阵呢?也就是说,现在期望我们的矩阵M这样进行分解:
这种简化的矩阵分解不再是分解为三个矩阵,而是分解为两个低秩的用户和物品矩阵,其实就是把用户和物品都映射到一个 k 维空间中,这个 k 维空间对应着 k 个隐因子,我们认为用户对物品的评分主要是由这些隐因子影响的,所以这些隐因子代表了用户和物品一部分共有的特征,在物品身上表现为属性特征,在用户身上表现为偏好特征。只不过这些隐因子并不具有实际意义,也不一定具有非常好的可解释性,每一个维度也没有确定的标签名字,所以才会叫做 “隐因子”。
我们知道SVD分解已经很成熟了,但是Funk-SVD如何将矩阵M分解成为P和Q呢?这里采用了线性回归的思想。我们的目标是让用户的评分和用矩阵乘积得到的评分残差尽可能的小,也就是说,可以用均方差作为损失函数,来寻找最终的P和Q。即通过 User-Item 评分信息来学习到的用户特征矩阵 P 和物品特征矩阵 Q,通过重构的低维矩阵预测用户对物品的评分。
对于某一个用户评分 如果用Funk-SVD进行矩阵分解,则对应的表示为 ,采用均方差作为损失函数,则我们期望 尽可能的小,如果考虑所有的物品和样本的组合,则我们期望最小化下式:
只要我们能够最小化上面的式子,并求出极值所对应的 ,则我们最终可以得到矩阵P和Q,那么对于任意矩阵M任意一个空白评分的位置,我们可以通过 计算预测评分,很漂亮的方法!
当然,在实际应用中,为了防止过拟合,会加入一个L2的正则化项,因此正是的Funk-SVD的优化目标函数 是这样的:
其中K为已有评分记录的 对集合, 为用户i对物品 的真实评分, 是正则化系数,需要调参。假设输入评分矩阵为M,它是 维矩阵,通过直接优化以上损失函数得到用户特征矩阵 和物品特征矩阵 ,其中 。对于这个优化问题,一般通过梯度下降法来进行优化得到结果。
将上式分别对pi,qj求导我们得到:
则在梯度下降法迭代时, 的迭代公式为:
通过迭代我们最终可以得到P和Q,进而用于推荐。FunkSVD算法虽然思想很简单,但是在实际应用中效果非常好,这真是验证了大道至简。
在Funk-SVD算法火爆之后,出现了很多Funk-SVD的改进版算法。其中Bias算是改进的比较成功的一种算法。其在 Funk-SVD 的基础上加了偏置项特征。
Funk-SVD方法通过学习用户和物品的特征向量进行预测,即用户和物品的交互信息。用户的特征向量代表了用户的兴趣,物品的特征向量代表了物品的特点,且每一个维度相互对应,两个向量的内积表示用户对该物品的喜好程度。但是我们观测到的评分数据大部分都是都是和用户或物品无关的因素产生的效果,即有很大一部分因素是和用户对物品的喜好无关而只取决于用户或物品本身特性的。例如,对于乐观的用户来说,它的评分行为普遍偏高,而对批判性用户来说,他的评分记录普遍偏低,即使他们对同一物品的评分相同,但是他们对该物品的喜好程度却并不一样。同理,对物品来说,以电影为例,受大众欢迎的电影得到的评分普遍偏高,而一些烂片的评分普遍偏低,这些因素都是独立于用户或产品的因素,而和用户对产品的的喜好无关。
我们把这些独立于用户或独立于物品的因素称为偏置(Bias)部分,将用户和物品的交互即用户对物品的喜好部分称为个性化部分。事实上,在矩阵分解模型中偏好部分对提高评分预测准确率起的作用要大大高于个性化部分所起的作用,以Netflix Prize推荐比赛数据集为例为例,Yehuda Koren仅使用偏置部分可以将评分误差降低32%,而加入个性化部分能降低42%,也就是说只有10%是个性化部分起到的作用,这也充分说明了偏置部分所起的重要性,剩下的58%的误差Yehuda Koren将称之为模型不可解释部分,包括数据噪音等因素。
偏置部分主要由三个子部分组成,分别是
则偏置部分表示为: ,则加入了偏置项以后的优化目标函数 是这样:
这个优化目标也可以采用梯度下降法求解。和Funk-SVD不同的是,此时我们多了两个偏执项 , 的迭代公式和Funk-SVD类似,只是每一步的梯度导数稍有不同而已,这里就不给出了。而 一般可以初始设置为0,然后参与迭代。这里给出 的迭代方法:
通过迭代我们最终可以得到P和Q,进而用于推荐。BiasSVD增加了一些额外因素的考虑,因此在某些场景会比Funk-SVD表现好。
Bias-SVD的理解:
SVD++是对 BiasSVD 的进一步改进,引入了隐式反馈和用户属性的信息,相当于引入了额外的信息源,这样可以从侧面反映用户的偏好,而且能够解决因显式评分行为较少导致的冷启动问题。
先说隐式反馈怎么加入,方法是:除了假设评分矩阵中的物品有一个隐因子向量外,用户有过行为的物品集合也都有一个隐因子向量,维度是一样的。把用户操作过的物品隐因子向量加起来,用来表达用户的兴趣偏好。
类似的,用户属性如社会学统计信息,全都转换成 0-1 型的特征后,对每一个特征也假设都存在一个同样维度的隐因子向量,一个用户的所有属性对应的隐因子向量相加,也代表了他的一些偏好。
综合两者,SVD++ 的目标函数中,只需要把推荐分数预测部分稍作修改,原来的用户向量那部分增加了隐式反馈向量和用户属性向量:
对于某一个用户 ,它提供了隐式反馈的物品集合定义为 , 这个用户对某个物品 对应的隐式反馈修正的评分值为 , 那么该用户所有的评分修正值为 。一般我们将它表示为用 形式,则加入了隐式反馈项以后的优化目标函数 是这样的:
其中,引入 是为了消除不同 个数引起的差异。式子够长的,不过需要考虑用户的隐式反馈时,使用SVD++还是不错的选择。
考虑隐式反馈。隐式反馈的形式很多。一般而言,会选择数据比较稠密且容易收集的特征作为隐式反馈的特征,比如浏览行为、时间咨询,或者其它一些看似无关的特征。
使用方式:
举例说明:
上述模型都是静态的,但是现实中,物品的流行度会随时间变化,用户的品味也可能随着时间而改变。因此,可以采用 TimeSVD++ 模型基于这种假设建模。该模型的核心思想是:为每个时间段学习一个参数,某个时间段参数使用该时间段数据进行学习。通过建模海量数据,自动发现潜在的时间模式。考虑到数据可能不足的问题,我们想要使用所有数据一起学习,那么可以添加时间距离作为权重。
上述 、 分别是用户和物品偏置随着时间变化的函数, 是用户隐因子随时间变化的函数。对于这些随时间变化的函数,一种处理是将时间离散化,可以将整个时间窗按照一定粒度进行划分,粒度越小代表随时间变化较大,粒度越大则代表变化较慢。例如处理物品 ,若假定物品的 bias 在短期内不会很大的改变,那么可以以较大的粒度划分,假设我们选取 3 个月的交互数据进行建模,整个时间窗口为3个月,以2周为一片,共划分为6片,每个时间 t,赋予一个 (即1到6之间的整数),这样就可以把 分为静态以及动态两部分: ,建模时,相当于需要额外对每个时间片求一个参数 ,以建模物品流行度随时间变化的特性。
如果某个时间t数据较少,不能学习到关于该时间t的偏置变化参数,那么就不能添加这种和时间相关的参数。一种解决方法是,可以定义关于时间的连续函数,例如处理用户偏置,定义时间偏移量函数: , 其中 代表该用户所有交互数据的平均时间(是指用户交互的时刻)。 代表时间距离。 是超参数,衡量时间变化影响程度,越大代表受时间影响程度大。则: 。 是模型需要为每个用户学习的参数,也就是说参数 和时间无关,关于该用户的所有数据都能够进行学习,时间因素主要体现在 上。
另一种时间函数用到高斯核来衡量时间的相似性。首先获取用户所有交互时间集合, 个时间点,即 。 然后使用公式:
需要为该用户所有交互时间点学习一个参数 , 共 个参数。预测时,使用高斯核计算时间相似性,在所有的时间上计算时间相似性,使用时间相似性对参数 进行加权。
另外考虑到周期性影响(periodic effects),例如工作日,周末等,可以添加 periodic effects 参数。
对特殊的期间,如节日、活动时期等也可训练对应的隐因子向量。
基本思想:
从某种程度上来说,PCA和SVD是一对表亲,PCA对特征的协方差矩阵进行分解,找到一堆特征的线性组合,尽可能多的表示出原始特征中成分,SVD则对原始数据直接进行奇异值分解,找到原始数据中尽可能大的特征值,以这些特征值多对应的特征向量作为新的特征。
对于病态矩阵,目前主要的处理办法有预调节矩阵方法、区域分解法、正则化方法等,截断奇异值分解技术TSVD(Truncated SVD,截断SVD)就是一种正则化方法,它牺牲部分精度换去解的稳定性,使得结果具有更高的泛化能力。如果奇异值矩阵的奇异值发生严重倾斜:TOP N之后的奇异值全部趋于0。这种情况称之为病态矩阵。病态程度越高,无用的特征越多,通常会截取前p个最大的奇异值;截断参数p的选取是TSVD方法的一个难点。TSVD对于稀疏性比较严重的数据集,效果较好。
著名的科学杂志《Nature》于1999年刊登了两位科学家D.D.Lee和H.S.Seung对数学中非负矩阵研究的突出成果。该文提出了一种新的矩阵分解思想——非负矩阵分解(Non-negative Matrix Factorization,NMF)算法,即NMF是在矩阵中所有元素均为非负数约束条件之下的矩阵分解方法。
通常的矩阵分解会把一个大的矩阵分解为多个小的矩阵,但是这些矩阵的元素有正有负。而在现实世界中,比如图像,文本等形成的矩阵中负数的存在是没有意义的,所以如果能把一个矩阵分解成全是非负元素是很有意义的。在
NMF中要求原始的矩阵V的所有元素的均是非负的,那么矩阵V可以分解为两个更小的非负矩阵的乘积,这个矩阵V有且仅有一个这样的分解,即满足存在性和唯一性。
数学原理
非负矩阵分解 (Nonnegative Matrix Factorization, NMF)是机器学习中重要的非监督学习算法,其广泛应用于话题发现和推荐系统等应用。给定一个非负样本矩阵(如m个用户对n个商品的打分矩阵) ,NMF的目标为求解两个非负低秩矩阵 ,最小化如下目标函数如下:
其中|.|表示矩阵的F范数(Frobenius范数:矩阵元素绝对值的平方和再开平方)。上述目标函数也可以表示为:
其中 和 均为行向量,其分别对应矩阵W的第i个行向量和H的第j个行向量。可计算W和H的梯度如下:
其中,
采用梯度下降的参数优化方式, 可得W以及H的更新方式见下式:
其中 定义为 。
由于 NMF 是一个非凸问题,所以只能找到一个 local optimal。参数初始值的选取对结果的影响比较大。参数初始值最好离预期解比较近,所以其重构后应尽可能和原样本值接近: 。
一种简单的策略是让参数随机初始值重构后的值等于 R 的均值 :
在优化的过程中,如果在迭代过程一直保持一个学习率会导致loss震荡,在优化的过程中步长宜逐渐减小。
在矩阵分解基础上,加入了隐向量的非负限制。然后使用非负矩阵分解的优化算法求解。
其中 既可以使用 FunkSVD 求法 ,也可以使用 BiasSVD 求法 ,当然也可以改进成使用 SVD++的求法。
我们前面讲的算法都是针对显式反馈的评分矩阵的,因此当数据集只有隐式反馈时,应用上述矩阵分解直接建模会存在问题。主要有两方面的原因,首先,隐式反馈数据集中只存在正样本,即 rij=1,∀rij∈R。此时,不能够只使用正样本进行优化,而忽略了未观测样本,否则会造成 trivial 但是无用的解,例如把所有的隐向量都预测成向量空间中的同一个点上。其次,不能够把所有的未观测样本都当做是负样本,因为这些未观测的样本既可能是用户不喜欢,也有可能是用户未曾看到但是实际上是喜欢的。虽然可以把预测用户行为看成一个二分类问题,猜用户会不会做某件事,但实际上收集到的数据只有明确的一类:而用户明确 “不干” 某件事的数据却没有明确表达。这类问题在业内称为 One-Class,One-Class 数据也是隐式反馈的通常特点。
为了解决该问题,引入 WRMF (weighted regularized matrix factorization)。该方法对每个训练样本都加一个权重,来表征用户对物品偏好的置信度。这个权重通常使用隐式反馈行为的次数或者一些量化指标的转换,比如浏览次数或观看时间等。权重可以减少未知样本的影响力,这些未知样本的权重一般的比观测样本的权重小的多。
WRMF 的目标函数:
其中, 代表观测样本,都为正样本,即 , 代表未观测样本,都为负样本,即 。 是样本 的权重,也就是置信度,在计算误差时考虑反馈次数,次数越多,就越可信。置信度一般也不是直接等于反馈次数,根据一些经验,置信度 可这样计算:
其中 是一个超参数,需要调教,默认值取 40 可以得到差不多的效果, 就是次数了。
此外,还可以多了解几点:
One-Class 的负样本如何采样?
因此,One-Class 这种问题不能讲所有的缺失值作为负类别,矩阵分解的初心就是要填充这些值,如果都假设他们为 0 了,那就忘记初心了。应对这个问题的做法就是负样本采样,即挑一部分缺失值作为负类别样本即可。常见的有两个方法:
按照经验,第一种不是很靠谱,第二种在实践中经过了检验。想一想也不难理解,在理想情况下,什么样的样本最适合做负样本?就是展示给用户了,他也知道这个物品的存在了,但就是没有对其作出任何反馈。问题就是很多时候不知道到底是用户没有意识到物品的存在呢,还是知道物品的存在而不感兴趣呢?因此按照物品热门程度采样的思想就是:一个越热门的物品,用户越可能知道它的存在。那这种情况下,用户还没对它有反馈就表明:这很可能就是真正的负样本。按照热门程度采样来构建负样本,在实际中是一个很常用的技巧,其实 Word2Vec 学习过程,也用到了类似的负样本采样技巧。
PMF,即概率矩阵分解(Probabilistic Matrix Factorization)。设有 N 个用户和 M 个商品,评分系统用 的矩阵 R 表示。基本思路:仍然是矩阵的低秩近似。即 ,用户和商品之间的关系(即用户对商品的偏好)可以由较少的几个因素的线性组合决定。这也是 MF 的基本思想。PMF在MF 的基础上加上了贝叶斯观点,即认为R是观测到的值,U、V 描述了系统的内部特征,是需要估计的。
实际上,由于系统噪声存在,不可能做出这样的完美分解,另外R包含很多未知元素。所以问题转化为:
基本假设:
根据第一条假设,有:
通过平移得到, ,根据 i.i.d,可以得到评分矩阵的条件概率密度:
这里 U 和 V 是参数,其余看作超参数。 是指示函数,如果用户i对商品j评分过,则为 1,否则为 0。
根据第二条假设,可以写出用户、商品属性的概率密度函数。其中 、 是先验噪声的方差,人工设定。
那么根据贝叶斯法则,可以得到用户和商品属性的后验概率:
采用对数似然估计,代入高斯密度函数可得代价函数如下:
其中,D 为隐因子的数量,C 为不受参数影响的常数。当固定超参数后,最大化上述似然等价于最小下述二次优化目标:
该模型可以看做是 SVD 模型的概率化拓展。
最后,为了限制评分的范围,对高斯函数的均值施加logistic函数 ,其取值在 (0,1)之间。最终的能量函数是:
上述所述模型如左图所示。对于右图,是论文提出的受约束的 PMF(Constrained PMF)。
简要说明一下 Constrained-PMF:用户是否给某个商品“打过分”这个信息本身就能一定程度上说明用户的属性。Constrained PMF 尝试把I_{ij}引入到模型中去。
用户属性 U 由两部分组成:和之前相同的高斯部分Y,以及W用 “看过” 矩阵I加权的结果。
其中W服从方差为 , 均值为 0 的高斯分布。
上述算法在确定了目标函数后,就要用到优化算法找到能使它最小的参数。优化方法常用的选择有两个,一个是比较熟悉的随机梯度下降(SGD),另一个则是 交替最小二乘(Alternating Least Squares,ALS)。
在实际应用中,交替最小二乘更常用一些,这也是社交巨头 Facebook 在他们的推荐系统中选择的主要矩阵分解方法。
交替最小二乘的核心是 “交替”,接下来看看 ALS 是如何 “交替”。
矩阵分解的最终任务是找到两个矩阵 P 和 Q,让它们相乘后约等于原矩阵 R:
难就难在,P 和 Q 两个都是未知的,如果知道其中一个的话,就可以按照线性代数标准解法求得,比如如果知道了 Q,那么 P 就可以这样算:
也就是 R 矩阵乘以 Q 矩阵的逆矩阵就得到了结果。
反之知道了 P 再求 Q 也一样。交替最小二乘通过迭代的方式解决了这个鸡生蛋蛋生鸡的难题:
这便是机器学习的一大优势,先给一个假的结果,让整个模型运转起来,然后不断迭代最终得到想要的结果。另外,WRMF 这种带权重的 ALS 优化算法叫做加权交替最小二乘:Weighted-ALS。
基于内容的推荐算法、基于近邻的协同过滤以及基于矩阵分解的协同过滤算法可以说是三大基础推荐算法,它们的算法思想构建了整个推荐算法大厦的基石,后面高级算法的不断演化,底层的基本假设和原理大都基于这三大基础推荐算法。
本小节我们来看看矩阵分解有哪些高级演化,了解推荐算法的演化路径和优化思路。
在推荐或搜索领域,排序学习(Leaning To Rank)占有非常重要的地位,因为对于推荐或者搜索,本质上我们还是会更加关注相关内容的排序,越相关就希望它的排序越靠前。其实矩阵分解也属于排序学习的一种,也就是最为常见的 Pointwise 类型,而基于矩阵分解构建的 贝叶斯个性化排序(Bayesian personalized ranking,BPR)则是一种 Pairwise 类型的排序学习算法,确切来讲,BPR 实际上是一个基于 Pointwise 之上的算法框架,但最常见的就是基于矩阵分解的构建形式,借助于矩阵分解得到排序学习的损失函数,然后完成排序学习的任务。
矩阵分解的一大特点便是隐语义的学习,这使得该算法能够挖掘出更深的用户和物品之间的联系,从而提高预测准确率。而恰好词嵌入(Word Embedding)也可以通过浅层神经网络学到这样的隐语义,而且这种嵌入式表达往往更加简单、快速、有效,因此很快被人们应用到推荐系统中。
Embedding 在推荐中的一些用法:
Embedding 本质上也是体现物品关联,但是比协同过滤的覆盖度高。由此,Embedding 几乎成为高级推荐算法的必用技术,Embedding 的特征预处理可以带来更好的效果,又能降维又能挖掘深层关联信息,何乐而不为呢?与矩阵分解类似,其核心思想就是同时构建用户和物品的嵌入式表示,使得多种实体的嵌入式表示存在于同一个隐含空间内, 进而挖掘实体之间的关联信息 (非端到端),以及实体和最终目标任务之间的关联信息(端到端)。
分解机FM(Factorization Machine)是由Konstanz大学Steffen Rendle于2010年最早提出的,旨在解决稀疏数据下的特征组合问题。FM设计灵感来源于广义线性模型和矩阵分解。
在线性模型中,我们会单独考察每个特征对Label的影响,一种策略是使用One-hot编码每个特征,然后使用线性模型来进行回归,但是one-hot编码后,一者,数据会变得稀疏,二者,很多时候,单个特征和Label相关性不够高。最终导致模型性能不好。为了引入关联特征,可以引入二阶项到线性模型中进行建模:
、 是经过One-hot编码后的特征,取 0 或 1。只有当二者都为1时, 权重才能得以学习。然后由于稀疏性的存在,满足 , 都非零的样本很少,导致组合特征权重参数缺乏足够多的样本进行学习。
FM模型公式
矩阵分解思想此时发挥作用。借鉴协同过滤中,评分矩阵的分解思想,我们也可以对 组成的二阶项参数矩阵进行分解,所有二次项参数 可以组成一个对称阵W,那么这个矩阵就可以分解为 ,V的第j列便是第j维特征的隐向量。换句话说,每个参数 ,这就是 FM 模型的核心思想。因此,FM 的模型方程为(常用的是二阶,本文不讨论 FM 的高阶形式):
其中, 是第i维特征的隐向量, 代表向量点积,计算公式为
隐向量的长度为 ,包含k个隐因子。具体解读一下这个公式:
根据上式,二次项的参数数量减少为 个,远少于多项式模型的参数数量。
此外,参数因子化表示后,使得 的参数与 的参数不再相互独立。这样我们就可以在样本系数的情况下相对合理地估计 FM 模型交叉项的参数。具体地:
与 的系数分别为 和 ,它们之间有共同项 ,也就是说,除了 自身,所有包含 的非零组合特征(存在某个 , 使得 )的样本也都可以用来学习隐向量 ,这在很大程度上避免了数据稀疏性造成参数估计不准确的影响。而在多项式模型中, 和 是相互独立的。
FM 模型复杂度
FM 模型公式直观来看,时间复杂度为 ,n是特征的数量,但是,通过下面的等价转换,可以将 FM 的二次项化简,其复杂度可以优化到 ,即:
FM 模型的优化
如果用随机梯度下降(SGD)法学习模型参数。那么模型各个参数的梯度如下:
其中, 是隐向量 的第 个元素。由于 只与f有关,在参数迭代过程中,只需要计算第一次所有f的 ,就能够方便地得到所有 的梯度。显然,计算所有f的复杂度是 ;已知 时,计算每个参数梯度的复杂度是 ;得到梯度后,更新每个参数的复杂度是 ;模型参数一共有 个。因此,FM 参数训练的时间复杂度为 。综上可知,FM 可以在线性时间训练和预测,是一种非常简单高效的模型,在一些工业级的应用中被大量使用。
FM 的模型可用的损失函数
另外,上述 FM 模型方程是一个通用的拟合方程,可以采用不同的损失函数用于解决回归、二元分类等问题,比如可以采用 MSE(Mean Square Error)损失函数来求解回归问题,也可以采用 Hinge、Cross-Entropy 损失来求解分类问题。当然,在进行二元分类时,FM 的输出需要经过 Sigmoid 变换,这与 Logistic 回归是一样的。
FM 应用场景 | 损失函数 | 说明 |
回归 | 均方误差(MSE)损失 | Mean Square Error,与平方误差类似 |
二类分类 | Hinge/Cross-Entopy 损失 | 分类时,结果需要做 sigmoid 变换 |
排序 |
由上可知,FM 是一种比较灵活的模型,通过合适的特征变换方式,FM 可以模拟二阶多项式核的 SVM 模型、MF 模型、SVD++模型等,可以说单纯基于评分矩阵的矩阵分解推荐算法是 FM 模型的特例,当我们的显式特性仅仅是用户 ID 和物品 ID 的时候,分解机的表达退回了最原始的矩阵分解。例如,相比 MF 矩阵分解中的 BiasSVD 而言,我们把 BiasSVD 中每一项的 rating 分改写为 ,从公式中可以看出,这相当于只有两类特征u和i的 FM 模型。对于 FM 而言,我们可以加任意多的特征,比如 user 的历史购买平均值,item 的历史购买平均值等,但是 MF 只能局限在两类特征。SVD++与 BiasSVD 类似,在特征的扩展性上都不如 FM。
FM 模型对稀疏数据有更好的学习能力,通过交互项可以学习特征之间的关联关系,并且保证了学习效率和预估能力,在工业界使用非常多。
场感知分解机 FFM(Field-aware Factorization Machine)最初的概念来自 Yu-Chin Juan与其比赛队员,是他们借鉴了来自 Michael Jahrer 的论文中的 field 概念提出了 FM 的升级版模型。通过引入 field 的概念,FFM 把相同性质的特征归于同一个 field。特征分 “域” 非常适合类别型特征经过 One-Hot 编码后再通过 “域” 来区分,比如某一原始特征是 “地点”,有三个类别的值:北京、天津、上海;经过 One-Hot 编码后成为 “地点=北京”、“地点=天津”、“地点=上海” 这三个特征,此时就可以将这三个特征放到同一个 “域” 中。简单来说,同一个类别型特征经过 One-Hot 编码生成的数值特征都可以放到同一个域中,包括用户性别、职业、品类偏好等。
在 FFM 中,每一维特征 ,针对其它特征的每一个域 ,都会学习一个隐向量 。因此,隐向量不仅与特征相关,也与域相关。也就是说,不同域下的特征进行关联的时候使用不同的隐向量,也是 FFM 中 “field-aware” 的由来。假设样本的n个特征属于f个 field,那么 FFM 的二次项有nf个隐向量。而在 FM 模型中,每一维特征的隐向量只有一个。FM 可以看作 FFM 的特例,是把所有特征都归属到一个 field 时的 FFM 模型。根据 FFM 的 field 敏感特性,可以导出其模型方程:
也就是和不同 field 的特征计算参数 时,使用不同的隐向量 。其中, 是第j个特征所属的 field。如果隐向量的长度为 k,那么 FFM 的二次参数有nfk个,远多于 FM 模型的nk个。此外,由于隐向量与 field 相关,FFM 二次项并不能够化简,其复杂度为 。
Yu-Chin Juan 实现了一个C++版的FFM 模型,即比较出名的 libffm,也有人在此之上开发出了 Python 接口。这个版本的 FFM 省略了常数项和一次项,模型方程如下:
其中,C2是非零特征的二元组合, 是特征,属于 field , 是特征 对field 的隐向量。此 FFM 模型采用 logistic loss 作为损失函数,和 L2 惩罚项,因此只能用于二元分类问题。
其中, 是第i个样本的 label,L是训练样本数量, 是惩罚项系数,模型采用可 SGD 进行优化。
目前 FFM 在点击率预测、转化率预测方法取得了很大的成果。不过在实际应用中,FM 比 FFM 更广泛一些,是对性能和效果的一些折中。
矩阵分解还有一个演化算法是张量分解(Tensor Factorization)。矩阵分解的核心思想,是用矩阵这种数据结构来表达用户和物品的相互关系。这里,我们一般谈论的都是一些最简单的关系,例如评分、点击、购买等(本小节我们只是讨论评分)。在这种二元的模式下,矩阵就是最好的表达用户和物品之间关系的数据结构。然而,在真实的场景中,用户和物品的关系以及产生这种关系的周围环境是复杂的。一个矩阵并不能完全描述所有的变量。例如,用户对于某个物品的评分是发生在某个地点、某个时间段内的。这种所谓的 “上下文关系”(Context)往往会对评分产生很大影响。遗憾的是,一个矩阵无法捕捉这样的上下文关系。
之前讨论过的 “SVD++”和“FM”,本质上都是在某种程度上绕开这个问题。采用的方法就是,依然用矩阵来表达二元关系,但是把其他信息放进隐变量中,或者是采用基于信息的推荐系统的思路来得到相关信息的建模。除了这种思路,还有没有别的方法,可以把上下文关系融入到对用户和物品的建模中去呢?这时 “张量” 就该上场了。从本质上来说,张量就是矩阵的推广。我们可以这么理解,矩阵是对二维关系建模的一种工具;而张量,就是对 N 维关系的一种建模。在二维关系中,用户和物品的评分是唯一能够被建模的变量;而到了 N 维关系中,理论上,我们可以对任意多种上下文关系进行建模。比如,我们刚才提到的时间,就可以组成一个三维的张量,分别为用户、物品和时间。然后,在这个三维的张量中,每一个单元代表着某一个用户对于某一个物品在某一个时间段的评分。那么,如何使用张量来进行推荐模型的建模呢?我们还是用刚才所说的 “用户、物品和时间” 作为例子。在这个三维的张量里,每一个元素代表的是用户对于物品在某个时间段上的评分。那么,根据矩阵分解的思路,我们怎么来对这个张量进行分解呢?遗憾的是,张量的分解要比矩阵分解更为复杂。与矩阵分解不同,张量分解至少有两种不同的形式,这两种形式会引导出不同的分解模型和算法。
第一种分解模式:CP 分解(CANDECOMP/PARAFAC)
拿我们刚才所说的三维张量来讲,CP 分解是把一个三维张量分解为三个矩阵。具体来说,比如我们的三维张量是 N 维用户乘以 M 维的物品乘以 R 维的时间段。那么,分解出来的三个矩阵就分别为 N 维乘以 K 维的用户矩阵,M 维乘以 K 维的物品矩阵,以及 R 维乘以 K 维的时间矩阵。这三个矩阵中的每一个向量都代表某一个用户、某一个物品和某一个时间段。K 在这里是一个参数,类似于矩阵分解中的隐变量的维度,我们也就可以把这个 K 理解成为隐变量的维度。那么在原始的三维张量中,某一个元素就是这三个矩阵的某三个向量对应元素乘积相加的结果。CP 分解的一大好处就是,分解出来的三个矩阵的隐变量维度是一样的,这也就减少了需要调整的参数的个数。
第二种分解模式: HOSVD 分解(High Order Singular Value decomposition)。
这种分解和 CP 分解最大的不同就是分解出来的三个矩阵的维度不再一样。也就是说,在我们之前的例子中,用户矩阵的维度可以是 N 乘以 A,物品矩阵的维度是 M 乘以 B,时间段矩阵的维度是 R 乘以 C。当然,这样就无法还原之前的 N 乘以 M 乘以 R 的三维张量了。于是在技术上,还需要乘以一个 A 乘以 B 乘以 C 的小张量才能对原始数据进行复原。所以,通俗地讲,HOSVD 分解就是把一个三维的张量,分解成为三个矩阵和一个更小的张量的乘积。那 HOSVD 相比 CP 分解有什么好处呢?好处自然就是给不同的数据以不同的自由度,因为不再限制用户、物品和时间段都必须有一样的维度。但同时也带来了难点,那就是有了更多的 “超参数” 需要调整。值得一提的是,我们仅仅是讲了讲张量分解的一个基本思路。在实际的运作中,还有一个重要的步骤,那就是设计目标函数,或者说是定义损失函数。在一般的分解过程中,我们可以定义 “平方差”(Squared Loss),也就是原始数值和预测数值之间的平方差来作为损失函数,当然也可以使用其他的损失函数,这一点和矩阵分解是一样的。关于张量分解的参数求解,和常规方法一样,一种是随机梯度下降,一种是交替最小二乘法,只不过这里的交替最小二乘法,每次更新其中一个参数,要固定另外两个参数不动。
虽然张量是对矩阵的自然延伸,张量分解是矩阵分解的某种延伸,但其在现实建模的过程中有很大难度,最大的问题就是张量分解的求解过程相对比较复杂,因此,工业界似乎很少有人用张量分解方法。
协同矩阵分解(Collective Matrix Factorization)与张量分解的目的一样,也是用来解决融合多元信息的问题。在有一些应用中,除了用户和物品这样很明显的二元关系以外,还有其他也很明显的二元关系,如何把这些二元关系有效地组织起来,就变成了一个有挑战的任务。比如一个社交媒体网站,既有用户对于物品(比如帖子)的交互信息,又有用户之间的互相连接信息(谁与谁是好友等)。那么,如何来显式地表达这两类不同的二元关系呢?协同矩阵分解就是为了解决这类问题的。
协同矩阵分解的基本思路其实非常直观,那就是有多少种二元关系,就用多少个矩阵分解去建模这些关系,并且假定这些不同类型的关系都是在同一组隐变量的影响下起作用。用我们刚才所说的社交媒体的例子。如果我们有用户与用户的关系,用户与物品的关系,那我们就组织两个矩阵分解,分别来对这两种关系进行建模。如果有一个用户与用户的矩阵需要分解,然后有一个用户与物品的矩阵需要分解。那从这两个矩阵分解中,我们分别可以得到至少两组不同的用户隐变量。一组是从用户与用户的关系而来,一组是从用户与物品的关系而来。这两组用户的隐变量是不一样的。同时,因为两个矩阵没有关联,所以无法达到我们希望这两种关系互相影响的效果。要想在两个矩阵分解之间建立联系,我们必须有其他的假设。这里的其他假设就是,两组不同的用户隐变量其实是一样的。也就是说,我们假设,或者认定,用户隐变量在用户与用户的关系中,以及在用户与物品的关系中,是同一组用户隐变量在起作用。这样,虽然表面上还是两个矩阵分解,但其实我们限定了其中某一部分参数的取值范围。说得直白一些,我们认定从两个矩阵分解出来的两组来自同一个因素(这里是用户)的隐变量是完全一样的。用更加学术的语言来说,这就是将两组矩阵分别投影到了相同的用户空间和物品空间。这样做的好处,自然就是对于多种不同的关系来说,我们使用 “相同隐变量” 这样的假设,可以把这些关系都串联起来,然后减少了总的变量数目,同时也让各种关系互相影响。那么,这样的假设有没有潜在的问题呢?一个比较大的潜在问题就是,使用同样的一组隐变量去表达所有的同类关系,这样的假设存在一定的局限性。比如上面的例子,用同样一组用户隐变量去解释用户和用户之间的关系,同时也需要去解释用户和物品之间的关系,能够找到这样一组用户隐变量其实是有一定难度的。而在实际应用中,不同关系的数据量会有很大的差距。比如,用户和物品关系的数据总量可能要比用户与用户的多。所以,由于用户和物品关系的数据多,两个矩阵分解用的同一组用户隐变量,很可能会更多地解释用户和物品的部分,从而造成了学到的隐变量未必能够真正表达所有的关系。对于这样的情况,自然已经有一些学者想到了对策,我们今天就不在这里展开了。
最后,需要提一下,在协同矩阵分解的场景中,学习各个隐变量的参数的过程,和一般的单个矩阵分解相比,没有太多本质性的变化。最简单的学习过程,依然是利用随机梯度下降法(SGD, Stochastic Gradient Descent)去学习。只不过,每一个隐变量会存在于多个矩阵分解中,这在更新变量时增加了所需的计算量。
参考链接:
Related posts: