6.2 分布式处理一个基于项目的推荐算法
对于如此规模的数据,我们希望使用分布式处理的方法。首先,我们会对基于项目的算法的分布式变异版本进行描述。它在某种程度上和以前非分布式的版本很相似。当然它看起来是完全不同的,因为以前的算法未曾被翻译到分布式的世界。接下来我们要用Hadoop来跑一跑。
6.2.1 构造一个协同矩阵
这个算法由一些简单的矩阵操作都成,它易于解释并且容易实现。如果你上次接触矩阵是好几年前了,不要担心,最棘手的操作只有矩阵乘法(如果您连矩阵乘法都不知道,拜托,那还搞啥数据挖掘...)。这里保证没有什么行列式、行分解、特征值等等。
回忆一下,基于项目的推荐,我们依赖于ItemSimilarity的实现。它可以提供两个项目间的相似程度。试想一下,我们会把计算完的相似度存放在一个很大的方阵里面,行和列和项目一一对应。每一行(或者每一列)代表了一个特定的项目与其他项目的相似度的一个向量。事实上它是个对称方阵。因为X与Y的相似度就是Y与X的相似度。
对与这个算法,我们需要一个叫做“协同矩阵(co-occurrence matrix)”的东西。我们用两个不同项目同时被喜欢的次数(协同因子)来代替之前相似度,这样可以构造出一个协同矩阵。比如,9个用户同时对X,Y感兴趣,那么他们的协同因子就是9,如果X,Y没有同时被任何用户喜欢那么协同因子就是0,对于项目对自己的协同因子我们不考虑,或者说我们不去计算它。
协同因子很像相似度;两个项目同时出现可以描述这两个项目的相似程度。所以,正如我们之前所看到的,协同矩阵扮演了ItemSimilarity所需要的数据的角色。
表 6.1 样本集中的协同矩阵。第一行和第一列分别是项目序号。
生成矩阵的过程就是简单的计数,这个矩阵和偏好值无关。一会我们会对这些值进行计算。表6.1 表示了我们通篇用到的一个小样例数据集所产生的协同矩阵。像上面所说,它是个对称方阵。有7个项目,矩阵有7行7列,对角线上的值将不会被使用,为了矩阵显示完整,我们暂且把数据写在上面。
6.2.2 计算用户向量
下一步就是我们把先前的推荐方法转化为一个基于矩阵的分布式模型,我们需要把用户对项目的偏好看作是一个向量。这一步你已经做到了。当我们使用欧几里德距离来度量用户相似度时,只需把用户看成是空间里的点,而相似度就是点之间的距离。
同样的,在模型中,用户的偏好就是一个n维的向量,每个维度代表一个项目。偏好值就是向量的每个分量。0代表用户对该项目无偏好。这个向量是稀疏的,很多分量都是0,因为实际中的用户仅对很少的项目有偏好。
例如,在上面的小样本数据集中,用户3的向量就是[2.0, 0.0, 0.0, 4.0, 4.5, 0.0, 5.0]。为了完成推荐,每个用户都需要这样一个向量。
6.2.3 产生推荐结果
为了给用户3产生推荐结果,只需要用矩阵去乘以他的用户向量,如6.2表中所示。
花一些时间回忆一下矩阵乘法把,如果需要来这里看看:(http://en.wikipedia.org/wiki/Matrix_multiplication )。协同矩阵和用户向量的乘积是一个向量,维度个数等于项目的个数。这个向量就是我们要的推荐结果。分量的值越大说明该项目的推荐排名越靠前。
表6.2 协同矩阵与用户3向量(U3)的乘积,最后用R来表示结果。
表6.2 展示了协同矩阵与U3的乘法运算过程,以及得到最终的推荐结果R。对于用斜体表示分数的项目101、104、105还有107,我们完全忽略他们的结果,因为我们事先知道用户3对这些项目产生了偏好。剩下的结果中,103的分数是最高的,用黑体表示,那么103就应该排在推荐结果中的首位。其他按照分数往下排即可。
6.2.4 分析结果
让我们停下来理解一下上面产生的结果,为何R中值较高的项目是较好的推荐呢?R中的没个分量是估计出来的偏好值,但是为何这个值就可以来表征用户对该项目的偏好呢?
回顾一下上面的计算过程,比如,R的第三个分量来自于矩阵的第三行与用户向量的点积。拿上面的数据就是:
4(2.0) + 3(0.0) + 4(0.0) + 3(4.0) + 1(4.5) + 2(0.0) + 0(5.0) = 24.5
矩阵的第三行包含了项目103与其他项目的协同因子。直觉告诉我们,如果103和该用户所偏好的其他项目比较相似,那么用户很可能会对103也有偏好。这个公式就是项目的协同因子与用户偏好乘积之和。如果103和该用户所偏好的很多项目同时出现时,那么103的分数就会越高。相应的项目偏好越大也会对分数有所贡献。这就是为何R可以作为预测结果。
需要说明的是,R不能代表用户对该物品的偏好值,因为他们对于偏好值来说太大了。我们可以通过归一化来将这些值变位符合要求的偏好值。如果你愿意的话,可以自己对结果处理一下。但是对于我们的目标来说,归一化是没必要的,因为我们关心的只是这些项目的得分排序,而不是具体的值。
6.2.5 向分布式计算迈进
这是个很有意思的事情,但是用户大规模处理的算法究竟是什么样子的呢?
算法的每个单元在任何时候都仅仅包含了数据的一个子集。例如,在创建用户向量时,每个计算单元都只为一个特定的用户去计算。计算协同矩阵时,计算单元仅为一个项目去计算它的协同因子向量。产生推荐结果时,计算单元只是用矩阵的一个行或者一列去乘以用户向量。另外,一些计算单元可以立足于收集这些相关数据。比如,用户向量的创建,它所需的每个偏好值可以各自单独去计算,然后收集。
MapReduce范式专门为此而生。
(翻译 By 花考拉)