使用 mahout mapreduce 计算用户相似度
Posted
技术标签:
【中文标题】使用 mahout mapreduce 计算用户相似度【英文标题】:Computing user similarity using mahout mapreduce 【发布时间】:2012-09-01 10:00:23 【问题描述】:我正在使用 Mahout 集群,我有大型集群,每个集群有大约 10 万用户,每个用户有 5 个功能。 在下一步中,我需要计算 pearson 相关性以找到集群用户之间的相似性。
目前我有一个 python 脚本,它对我做同样的事情,但正如预期的那样,计算需要很长时间并且不再是一个可行的选择
我查看了 Mahout,因为它提供了使用 Pearson、Tanimoto、对数似然度量来查找 UserSimilarity 的功能,但我无法找到开发这些相似度量的 Mapreduce 版本的方法。
是否有任何资源可以举例说明如何开发 UserSimilarity 的 mapreduce 版本,或者使用 hadoop 流和相同的 java 类是否有意义。
编辑
即使我在一个集群中有 100k 用户,我也很少需要计算 100k* 100k 矩阵。大多数时候它将是一个 10k*100k 矩阵。但是我有大约 500 个这样的集群 所以计算 500 个 10k * 100k 的集群所花费的时间相当长,这就是我寻找更好的方法并引发讨论的原因
【问题讨论】:
请分享更多关于你打算做什么的信息。你真的需要所有成对的相似性(O(n^2)
数据集!)
@Anony-Mousse :编辑了问题并提供了更多详细信息
那么,你感兴趣的 10k*100k 矩阵应该是什么?仅计算该部分应该快 10 倍...
其实可以用MinHash算法解决大量用户的两两比较,时间复杂度可以降低到O(n)。
【参考方案1】:
MapReduce 的局限性
成对用户相似度不能在 MapReduce 中表示定义。
如果您还记得 map
所做的事情,那就是它读取 one 键值对。 不是读取两个键值对。
根据map
的定义,您无法计算成对 比较。
如果您意识到 map 和 reduce 是线性过程,这应该是显而易见的。成对相似性是一个二次任务。它不能工作。
也就是说,除非你滥用 mapreduce。您可以将数据空间分解到二次大小。如果您首先生成所有 n*n 对,那么您可以再次使用 MapReduce 处理这些对。但这是对它的严重滥用(尽管有些人似乎正是这样做的)。
替代(滥用)MapReduce
首先,有时您可以将问题重写为实际上是线性的,例如通过反转数据。或者通过一些巧妙的修剪,虽然实际上它是二次的,但在实际数据中它通常保持线性(因为您可以在生成期间或之前删除大量理论上的二次数据)。
其次,请注意 Mahout 构建在 Hadoop 平台上,但仅使用 MapReduce 并不能解决所有问题。很多 Hadoop 的东西不只是“map+reduce”。例如 TeraSort - 由于排序不是线性问题(它还涉及比较元素!),它无法通过 MapReduce 解决。但是您可以将 Hadoop 用于 TeraSort,通过编写一个分布式排序,该排序使用广义中位数方法来估计分位数,根据这些分位数分配数据,然后对各个节点上的各个桶(O(n log n)
)进行排序.复杂度仍然是超线性的,但运行时间远低于单个节点。
我很确定 Hadoop 为您提供了 MapReduce 之外的几个选项。 您可能需要更仔细地了解 Mahout 为解决此类非线性问题所做的工作。它不会尝试或假装 MapReduce 一切。还有 Apache Hama,它也超越了 MapReduce,使用了一种称为“批量同步处理”的概括。 Hadoop(和 Yarn)一般做允许这样的事情。但该 API 显然比经典的 Mapper 和 Reducer API 难多了。
对 MapReduce 的幼稚滥用
或者你采取滥用的方式(这可能不是 Mahout 所做的)。滥用 map reduce 实际上相当简单。因为对输出大小没有限制。所以如果你不关心内存,你可以这样做:
假设您知道所有键,例如因为它们的编号是 1 到 n。然后就可以使用了
map(K, V) -> [ (1, (K, V)), (2, (K, V)), (3, (K, V)), ..., (n, (K, V)) ]
(这显然会创建一个二次大小的数据集!)在 reducer 中,每个键现在都有一个数据集的完整副本和一个需要关心的 ID。
因此,reducers 为每个对象再次读取 完整 数据集,计算 n 个相似度,并输出它们。
Boom,您的地图减少了问题。这是愚蠢和低效的,但它有效并且正在完成。
对辅助数据的更明智的滥用
更智能的版本会将数据集作为所谓的“辅助数据”直接加载到系统中。确实如此:
map (K,V) -> [ (K_1, sim(V, aux_obj1)), (K_2, sim(V, aux_obj2)),
(K_3, sim(V, aux_obj3)), ... (K_n, sim(V, aux_objn)) ]
这并不是滥用 MapReduce(它只是并行计算二次结果矩阵的规范方法),因为它至少诚实地说明了它的作用:使用大量辅助数据。它也可以工作 - 只要辅助数据适合您的工作人员记忆。而且它不再是一个合适的 mapreduce,因为它使用了不能真正被视为函数参数化的辅助数据。
鉴于您显然可以将数据加载到内存中(即使在 python 中),最后一个选项可能是您最简单的方法。您甚至可以将辅助内存分成块,并将这些数据库切片作为单独的作业进行计算。
不过,它不是 MapReduce。这是一个二次问题,根据定义不能是 MapReduce。这些问题可以在 Hadoop 上解决(请参阅 Mahout),但您需要的不仅仅是 MapReduce。
对您的实际任务的最后评论
首先,请分享更多关于您真正计划做的事情。 变得更快的关键是少做。如果你可以节省计算,你总是更快。
10 万用户和 5 个属性(双值?)不是很多。所以也许你的python实现效率太低了。编译和优化的语言可能会使您的速度提高 1-2 个数量级。我在 10 分钟内完成了 110k 个具有 8 个属性的对象的成对相似性;所以你的问题应该在这个时候也可以解决。 10 万用户和 5 个属性还不是真正的“hadoop 大小的大数据”。与快速的低级实现相比,您最终可能会为 Hadoop 开销支付更多费用。
优化 Pearson 相关性:您可以在这里做几件事。请注意您最终是如何重新计算标准偏差之类的?如果您预处理您的数据并将每条记录标准化为均值 0 和标准差 1,则 Pearson 相关简化为协方差(因为 stddev 为 1)。因为均值是 0,协方差就变成了 E(x*y) = 1/5 \sum x_i * y_i
。如果您有兴趣,可以通过使用空间索引来加速此功能,例如仅在前 10 个相似对象中。我认为您可以轻松地将其添加到 ELKI 并在那里使用空间索引结构。这通常会减少另一个数量级的运行时间,这意味着您应该在单个 CPU 上的处理时间约为 1 分钟。
【讨论】:
您提出了一种计算用户-用户相似度的方法,并认为这在 M/R 中不可行。但是您建议的方法甚至不是 Mahout 是如何做到的。计算用户-用户相似度涉及对每个item 及其用户关联列表的操作。在单个项目级别,您输出和汇总有关用户与用户交互的信息,这些信息可能非常庞大。智能修剪在实践中解决了这个问题。对 M/R 的幼稚适应可能不起作用,但 Mahout 是(某种程度上)更智能可能性的存在证明,是 OP 正在寻找的形式。 他写道用户有 5 个特征并且他使用 Pearson 相关性,所以我假设他通过计算这些向量上的成对 pearson 相关性来计算用户相似度。这听起来绝对不像他在使用图形数据(= 他没有关联?)。我从来没有说过 Mahout 是按照我的草图来做的。我说这就是 MapReduce 被滥用 来解决非线性问题的方式。我明确建议研究 Mahout 而不是尝试手动使用 MapReduce。 用户-项目关联是指评级或偏好——没有图表。您不必从字面上收集两个用户的数据来计算所有对的相似性,而 Mahout 则不需要。您实际上需要按项目而不是用户进行映射。您的帖子表明这在 M/R 中无法表示,但是 Mahout 是存在证明。您建议 Mahout 可能不会为此使用 M/R,但确实如此。您建议 Mahout 爆炸数据的完整笛卡尔连接,但事实并非如此。给出一种方法效果不佳的原因(是的,那不行)并不意味着没有可行的方法,而这就是 OP 的目的? 让我再次重申 Pearson 5 个特征的相关性...这不是像用户-项目关联那样的二分图。您不能仅将其拆分为这 5 个功能并独立处理它们(除此之外仅给您 5 个分区)。 您当然可以将其视为一个 n x 5“用户项”矩阵并运行全对用户相似性作业。然后看看org.apache.mahout.math.hadoop.similarity.cooccurrence.measures.PearsonCorrelationSimilarity
。 (将数据居中是作弊,这意味着这会降低余弦相似度。)那时您只需要点积和规范。规范很容易——没有成对的。您可以通过一次检查一个特征并输出元素的成对积,然后求和来获得点积。这比预先加入笛卡尔坐标要好得多。以上是关于使用 mahout mapreduce 计算用户相似度的主要内容,如果未能解决你的问题,请参考以下文章