1. 相对频率的计算
在我们使用应用程序来分析文章时,一个重要的使用就是文章主题分类。就是依据文章所要表达的主题进行分类。而一般的程序化分类 (非人工分类)所使用的方法是TF-IDF。这种方法依据字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。比如本文中多次出现MapReduce这一词,若是被搜索引擎分析时,它会计算出本文中MapReduce 的TF-IDF。依据本文MapReduce的TF-IDF值很大进而将本文主题归类为MapReduce。而和MapReduce主题密切相关的一般是“并行执行”,“分布式”,“Hadoop”,从而在用户搜索这方面的关键词的时候,将本文加入候选区进行排序。
上面所讲用Paris 和Stripes 来实现单词的共现矩阵。这里我们直接使用文集来计算出共现矩阵,并没有使用一些“净化”工具(例如lucene)来进行预处理。这样的问题就是:比如本文中一些语气助词或修订词会多次出现,在英文中”The”这个限定词几乎随处可见,那么在没有“净化”的情况下这些没有的词在计算TF-IDF会有很大的值。这里我们将讨论相对频率f(wj|wi)的计算。
首先f(wj|wi)的定义如下:
这个定义和概率统计里的条件概率的定义相似,该定义的解释是:
单词对(wj,wi) 出现的次数与单词 wi 的数量比。
假设我们计算出的共现矩阵如下图:
我们计算f(wj|wi)的值也是很简单的 将(wj,wi)出现的次数除以 wi所在行的所有数据之和即可。
我们对这个相对频率f(wj|wi)最简单的计算方法是:通过 三次MapReduce即可。第一次即计算上面的共现矩阵,第二次计算每个单词出现的次数(即数单词)。第三次就直接求比值。
但在这里我要实现的是在上篇Paris 和Stripes 算法的基础上稍作改进。下面具体分析:
这里所要解决的问题是:我们在Reduce阶段首先就要计算出每个单词出现的总次数,然后对让每个(wj,wi)出现的次数除以wi 出现的次数就可以计算出来了。
对于Stripes 算法来说,这是很容易实现的,从上篇介绍Stripes 算法可知Stripes Mapper 产生的中间结果为: (wi, H=[(w1,c1),(w2,c2)…(wn,cn)])。这样在Reduce阶段我首先就直接遍历这个关联数组H,将c1, c2 …cn 相加即可得到这个wi 单词的总数目。然后在遍历一遍即可得到结果。这样的实现很简单,但是在拓展性上有很大的限制(详见Paris和Stripes 算法)。
对于Paris 算法来说就不是那么容易了,我们需要使用MapReduce 算法的一些设计技巧来完成了。Paris 算法将所有(wj,wi)相同的键值对汇集到一个Reducer上来处理。我们这里改变以下汇集到Reducer的规则(这个实现很简单只要重写下Partitioner 即可)。将(wj,wi)只要wi 相同的就聚集到一个Reducer上,如(wj,wi)和(wk,wi)将会被汇集到一个Reducer上。然后先计算遍历wi 单词的总数目,然后然后在组织一个关联数组遍历一遍即可得到结果。这样的实现和上面的Stripes 实际上是一样的。这样也会存在拓展性问题。
那么有没有一个没有拓展性问题的算法实现呢?答案是肯定的。上面的Paris 的算法若能在Map阶段顺带计算出wi 的数量即可以了。我们输出的键值对是这样的<(wj,wi);1>,我们可以输出这样一个键值对<(*,wi),1>,表示这是单词wi 出现的次数1。
这样解决是可以实现的,因为使用Mapper输出一个<(*,wi),1>是很简单。但是还有两个问题要解决:第一,键值对的key为(*,wi)需要第一个被Reducer处理。第二,对于键值对的key为(wj,wi)和(*,wi) 要汇集到一个reducer 上。
在之前简述Combiner 和In-Mapper Combining 时,我们说到MapReduce 的可控和不可控的方面。对于可控方面有两点这里可以用到:
对中间结果Key 排序的控制,这样程序员就有能力控制Reducer 处理Key的顺序。
对中间结果Key 分区的控制,这样程序员就有能力控制Reducer 处理Key的集合。
我们可以定义Key (wj,wi)对象的排序方法,比如JAVA中实现Comparable接口,使得(*,wi)对象类型的对象排在前面,从而首先被Reducer处理。
将键值对的key为(wj,wi)和(*,wi) 汇集到一个reducer 上,我们则只要重新实现一个Partitioner即可。