分治法解决MapReduce stripe模式内存瓶颈问题
本文内容基于之前的一篇博文《Data-intensive Text Processing with MapReduce》读书笔记第3章:MapReduce算法设计(2):pair模式与stripe模式。这一节的末尾提出一个问题:
stripe模式因为需要在mapper内维护一个关联数组,因此无法处理语料的词汇表非常大的情况。
随后文章给出了一个解决思路:划分词汇表。但没有给出具体解决方法。
本文针对这个思路,谈一谈如何通过划分词汇表的方式解决stripe模式存在的内存瓶颈。本文仅从抽象算法的层面对这个思路进行细化,不涉及具体的程序设计。
因为条件所限,我目前尚无hadoop编程经验。空谈难免会有错误之处,还希望广大朋友不吝指正。
共现矩阵计算
关于共现矩阵的定义在此不再赘述,可参见本文开始给出的链接或是Wikipedia中的定义.
简单地说,计算共现矩阵的算法输入一组单词共现对,输出共现矩阵。
例如对于如下的共现对输入(简单起见单词均用斜体数字代替):
文档1 (1,2) (1,2)
(1,3) (2,3)
文档2 (2,4) (1,4) (3,4)
输出共现矩阵(共现对计数):
(1,2):2 (1,3):1
(2,3):1 (2,4):1 (1,4):1 (3,4):1
回顾stripe模式
首先来回顾一下应用stripe模式计算单词共现矩阵的过程:
stripe模式在mapper内对于当前单词wi维护一个关联数组(可以是哈希表,treemap等k-v结构)H. 在H中,k是与wi存在共现关系的单词wj,v是共现对(wi,wj)的计数,例如对于上面给出的实例,当处理文档1时,mapper有如下处理过程:
wi |
wj |
H |
操作 |
|
|
{} |
H初始化 |
1 |
2 |
{2:1} |
H[2] ++ |
|
2 |
{2:2} |
H[2] ++ |
|
3 |
{2:2,3:1} |
H[3] ++ |
|
|
|
输出(1,H) |
|
|
{} |
H初始化 |
2 |
3 |
{3:1} |
H[3] ++ |
|
|
|
输出(2,H) |
随后,reducer接受(w, [H1,H2,H3...]),合并H1,H2,H3...后输出结果,共现矩阵计算完毕。
内存瓶颈从何而来?
单词表很大的情况下,每个文档可能包含的单词量非常多。此时对于每个wi,可能都有非常多的单词与之共现。假如对于每个单词wi,与之共现的不同的wj单词数平均是一个相对于词汇表规模的固定比例r(例如1%),那么对于规模为n的单词表,维护对于一个特定单词wi的H平均需要存储r×n个k-v对,如果H的空间利用率是常数的话(例如50%或100%),那么维护H的空间开销是O(n).
简而言之,词汇表变得很大,从而导致与每个wi共现的不同wj很多,最终导致H变得很大。考虑stripe的应用情境:mapper内维护H,可以得出stripe受限于单机内存容量的结论(一个mapper任务在一台机器上运行)。因此stripe虽然高效(较之pair),却不能直接应用于词汇表很大的语料数据。
分治解决内存瓶颈
那么有没有办法解决这个问题呢?原书中小节末尾给出了一个划分词汇表的解决方法。
如何划分呢?内存瓶颈问题的本质是每个wi需要维护的wj太多了,导致H过大,如下图所示:
为了简单的考虑,我们先假设这r×n个wj均匀分布在整个词汇表区间里,如果能对这个词汇表做划分,那么就可以减少一次需要统计的wj的数量,从而减小H.
划分如下图所示:
将词汇表划分为b个桶后,对于每个桶单独统计,就可以减小H.
而b的值是可变的,这样即使是再大的词汇表,也可以通过增大b缩小需要一次处理的词汇表大小,使得stripe模式能够应用至任意大小的词汇表。
算法实现
分治可以通过在原有的stripe算法上增加一次预处理实现,预处理的功能即为划分词汇表:
class Mapper
method Map(docid a, doc d)
for all term w in doc d
do
for all term u in
Neighbors(w) do
barrel←hash(u) mod b
// 划为b个子区间
Emit(barrel, (a, (w, u)))
class
Reducer
method Reducer(barrel b, (docid
a, (term w, term u)))
Emit(a, (w, u))
经过预处理后,数据集被划分为b个子数据集,其中第i个数据集中只包含(wi,wj),其中hash(wj) mod b = i. 然后对这b个数据集分别使用stripe算法计算共现矩阵。由于数据集根据wj划分,可知每个数据集的计算结果都是最终全局共现矩阵的其中几列,而数据集之间的计算结果无交集。因此只需要将各数据集的结果简单合并即可得到全局共现矩阵,如下图所示。