Mengdong的技术博客

学习,记录,分享

导航

《Data-intensive Text Processing with MapReduce》读书笔记第3章:MapReduce算法设计(2)

本读书笔记的目录地址:http://www.cnblogs.com/mdyang/archive/2011/06/29/data-intensive-text-prcessing-with-mapreduce-contents.html

3.2 对(pairs)与带(stripes)

本节用一个自然语言处理(Natural Language Processing, NLP)中的常见示例来说明pair和stripe两种数据组织模式的区别。这个示例是共现矩阵(Co-occurance Matrix)的计算。

共现矩阵

在NLP中,共现矩阵是一个n×n的方阵,n是所需处理的语料中的单词数(不同单词的数量)。矩阵元素mij的值代表单词wi, wj的共现(共同出现)次数。wi, wj共现定义为wi, wj在指定的上下文范围中同时出现。上下文的范围可以有各种定义,例如同一个句子、同一个段落、同一个文档或是同一个由连续k个单词构成的序列(k的值也是可以定义的)。由于共现关系是对称关系,因此m的上三角阵与下三角镇是相同的(划分线:左下→右上)。

共现矩阵的计算在NLP中很普遍。除此之外,还有很多其他应用:

  1. 文本挖掘
  2. 伴随事件组合挖掘(例如超市购物,买了a的客人通常都会买b,则可以将商品a和b靠近摆放)
  3. 可疑行为探测(发现某种事件组合模式与反常事件之间的联系)

……

共现矩阵的空间开销显然是O(n2),其中n为词汇表(vocabulary)的大小(即不同单词的数量)。根据数据集的不同,词汇表的大小也会有差距:英语语料库中可能包含几万个词汇,而对于web规模的语料库则可能有几十亿大的词汇表。如果词汇表不大,共现矩阵能够放入单机内存中处理当然是最好不过的。但是大的语料数据往往有很大的词汇表,以至于共现矩阵大到放不进内存。虚存的使用将会降低算法性能,而即使压缩能够将共现矩阵的空间开销降低一些,这种基于单机、主存的处理模式终归是有上限的。下面我们即将介绍两种基于MapReduce的算法,它们具有良好的可扩展性,能够处理大规模语料数据。

基于MapReduce的共现矩阵计算

图3.8展示了第一种基于pair的MapReduce共现矩阵计算算法(以下简称为pair算法)。

图3.8 基于pair的MapReduce共现矩阵计算算法

mapper接受(docid,doc)对作为输入,对于每个共现对(w,u)都产生一个输出key-value对(见图3.8 mapper第5行)。这里采用的是很直观的二层循环的方式完成的:第一层循环遍历所有单词,第二层循环遍历所有与当前单词相邻(共现)的单词。如果将共现矩阵看做一张图(graph),这相当于每次输出图中的一条边上的一个计数。

reducer将所有具有相同key值的((w,u),1)相加,得出最终的((w,u),s)集合,集合中的每个元素对应共现矩阵中的一个元素,因此这个集合等价于共现矩阵。

图3.9展示了一种基于stripe的MapReduce共现矩阵计算算法(以下简称为stripe算法)。

图3.9 基于stripe的MapReduce共现矩阵计算算法

mapper构造当前文档的共现对的过程与图3.8所示算法类似,也是通过二层循环,但mapper记录与输出数据的方式有了一些变化,对于当前文档的每个单词/术语(term)w,维护一个关联数组H,使得H{u}记录的是共现对(w,u)出现的次数。reduce操作则是对分配入当前reducer的由mapper产生的(w, H)对进行加和合并,最后得到一个(w, H)的集合,对于其中每个元素,w是一个单词/术语,H中存储的是所有与w共现的单词u以及共现对(w,u)出现的次数。这个集合同样等价于共现矩阵。

pair算法与stripe算法的比较

直观比较下,基于stripe的算法数据表示更为紧凑,而基于pair的算法会产生比基于stripe的算法多得多的中间结果key-value对。因此执行时基于pair的算法需要排序的元素数更多。

这两个算法都可以应用combiner. 对于stripe算法,应用combiner算法很简单,效率也很高,只需要合并关联数组即可,且合并的步数不会超过语料数据词汇表的尺寸(因为需要合并的关联数组元素数不会超过语料数据词汇表的尺寸)。而pair算法的合并工作量就大很多,它只能合并那些具有相同(w,u)值的((w,u),1)对,而(w,u)的可能取值通常很多。

这两个算法也都可以应用mapper内合并,具体做法在此留给读者思考。无论具体做法是什么,有一点与使用combiner是一样的:pair算法产生的中间结果key-value对在key的取值范围上呈现稀疏分布的特征,因此即使对其应用局部合并,能够真正合并的项也是很少的。而且既然(w,u)的可能取值非常大,也使得对pair应用mapper内合并时很有可能遇到内存不足的问题。

这两个算法的扩展性同样重要。stripe算法能够工作的前提是对于每个mapper输入((docid, doc)对),其对应产生的关联数组都能够放入内存之中,否则虚存的引入将会大大降低性能。因此stripe对于词汇表很大的情况难以胜任(一般几个GB的语料数据没问题,而TB甚至PB级的数据就不好说了)。而pair算法不存在这个问题。

那么,哪个算法的性能更好呢?对此,我们给出过去的一些实验结果进行说明。我们在Hadoop上实现了以上两个算法,并用它们处理227万个来自APW(Associated Press Worldstream)的文档(共5.7GB)。使用Hadoop处理之前先进行了一些预处理:移除XML标签,使用Lucene对单词进行编号(tokenization),如此将文档数据转换为纯数字集合(这么做是为了处理方便)。所有这些实验在一个拥有19台slave的Hadoop集群上完成,每台slave配有两个单核CPU与两块磁盘。图3.10分别展示了pair算法与stripe算法的运行时间。

图3.10 pair算法与stripe算法的运行时间

(原文中用语言描述的对比数据,我转成了一张表)

T

IP

IPV

PCIP

FP

stripe

666sec (~11min)

653m

48.1GB

28.3m

1.69m

pair

3758sec (~62min)

(5.7× slower)

2.6b

31.2GB

1.1b

142m

列说明

  1. T=Time 算法消耗时间
  2. IP=Intermediate key-value Pairs  中间结果key-value对数(mapper输出)
  3. IPV=Intermediate key-value Pairs Volume 中间结果key-value对所占空间
  4. PCIP=Post-Combiner Intermediate key-value Pairs 经过combiner合并后剩下的中间结果key-value对数(combiner输出)
  5. FP=Final key-value Pairs  最终的key-value对数(reducer输出)

数/量词说明

  1. sec=second 秒
  2. min=minute 分钟
  3. b=billion 十亿
  4. m=million  百万
  5. GB=?你懂的

从对比数据可以看出,stripe算法在实验数据集上的性能优于pair算法。两个算法都具有理想的算法复杂度(线性):使用线性回归计算出的R2值均接近1.

另一个比较指标是算法的可扩展性。我们测定了stripe算法在不同规模集群下的计算性能。实验采用Amazon EC2服务进行。实验数据如图3.11所示。

图3.11 针对stripe算法在集群规模变化下的扩展性测试

(左为计算时间数据图,右为加速比数据图)

左图是stripe算法在集群尺寸位于20-80之间(每次增10)的计算时间,右图为根据时间数据计算的加速比数据。可以看出,stripe算法具有良好的可扩展性(线性),根据加速比数据进行线性回归计算出的R2值接近1.

总结

pair算法与stripe算法代表了两种从观察集中发现、计数共现事件(在本例中即为从语料库中构造共现矩阵)的方法。这两种算法的思路在很多类问题中都能得到有效应用(例如文本处理、数据挖掘、生物信息计算等)。

再深入一步看,pair算法与stripe算法是统一的。只是他们记录的粒度不同:pair算法单独记录每一次共现,而stripe算法将满足某种条件的共现记录在一起(这个条件就是拥有同样的key值)。在stripe算法中,我们可以将语料数据的词汇表划分为b个桶(比如通过哈希),这样原先的stripe就会被划分为b个“子stripe(sub-stripe)”. 这种方法可以解决词汇表很大时stripe算法内存不足的问题。注意,当b=1时,即为标准的stripe算法;而当b=|V|(其中|V|是词汇表中的词汇数)时,stripe算法即等价于pair算法。

posted on 2011-07-14 10:49  mdyang  阅读(2174)  评论(2编辑  收藏  举报