MapReduce 的局限性
根据定义,成对用户相似性在 MapReduce 中是不可表示的。
如果您还记得是什么map
,它会读取一个键值对。它没有读取两个键值对。
根据 的定义map
,您无法计算成对比较。
如果您意识到map和reduce是线性过程,这应该是显而易见的。成对相似性是一个二次任务。它不能工作。
也就是说,除非你滥用mapreduce。您可以将数据空间分解为二次大小。如果您首先生成所有 n*n 对,那么您可以再次使用 MapReduce 处理这些对。但这是对它的严重滥用(尽管有些人似乎正是这样做的)。
MapReduce 的替代品(滥用)
首先,有时您可以将问题重写为实际上是线性的,例如通过反转数据。或者通过一些巧妙的剪枝,虽然实际上它是二次的,但在实际数据中它通常保持线性(因为您可以在生成期间或之前丢弃大量理论上的二次数据)。
其次,请注意 Mahout 构建在 Hadoop 平台上,但仅使用 MapReduce 并不能解决所有问题。很多 Hadoop 的东西不只是“map+reduce”。例如 TeraSort - 由于排序不是线性问题(它还涉及比较元素!),它无法通过 MapReduce 解决。但是您可以将 Hadoop 用于 TeraSort,通过编写一个分布式排序,该排序使用广义中位数方法来估计分位数,根据这些分位数分布数据,然后对O(n log n)
各个节点上的各个存储桶 (in) 进行排序。复杂性仍然是超线性的,但运行时间远低于单个节点。
我很确定 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 个相似度,然后输出它们。
繁荣,你的地图减少了问题。这是愚蠢和低效的,但它有效并且正在完成。
使用辅助数据进行更明智的滥用
更智能的版本将直接将数据集作为所谓的“辅助数据”加载到系统中。所以它:
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 分钟的处理时间。