一些算法的MapReduce版本总结

K-Means

算法

K-Means的算法还是比较容易理解的,也很直观。有一批数据,需要把它聚类成K个类别。首先随机获得K个初始样本点作为聚类中心(这里就使用最简单的获得初始点的方法),然后在每一次迭代中,对于每个样本点计算离哪个聚类中心更近,就把它归到这个类别中,这样讲所有的样本点都归类到某一个类别中,接着计算每一个类别的新的聚类中心,对这K个新的聚类中心再一次进行迭代计算。收敛的条件聚类中心不再变动得很多,或者人为地设置一个时间截止。

MapReduce算法

聚类中心的存储

因为我们每一次迭代,都需要对每一个样本关于聚类中心进行距离度量,所以需要将聚类中心存储起来,共享给所有的Mapper。共享的方法有很多,例如使用HDFS的文件,Redis,或者加入到MapReduce的分布共享缓存中(Distributed Cache)中作为全局共享数据。

Map

  1. setup函数读取聚类中心的数据。
  2. 对每一个记录(Object,样本点向量),计算与各个聚类中心的距离,得到最近的距离中心,发射中间的键值对(聚类类别,样本点向量)。

Combine

对于属于每一个聚类中心的样本点计算它们的和,输入键值对(聚类类别,样本点集合),输出键值对(聚类类别,样本点的和以及样本个数)。

Reduce

对于属于每一个聚类中心的做Combine操作计算均值。

数据类型可以定义一个类:

class Cluster implements Writable {
    private int clusterID;    // 属于的类别
    private long numOfPoints;    // 个数,单个样本就是1
    private Instance;    // 坐标
}

自然连接

我们需要在属性X上做关系R和S的自然连接。对于属性X上相同的值,R和S上需要做笛卡尔乘积,所以联想到可以用属性X的值作为键。

Map

输入时每条记录,输出以属性X的值作为键,其余属性加上R和S的标志作为值。

Reduce

输入时属性X的值为键,对于每一个值将它分为R和S的集合,然后对这两个集合做笛卡尔乘积,添加X的值作为新的记录发射出去。

矩阵乘法

公式为:\(p_{ik}=(MN)_{ik}=\sum_jm_{ij}n_{jk}\)

那么我们可以用\((i, k)\)作为键。

Map

对于M中的每个元素\(m_{ij}\),产生一系列的键值对\(<(i, k), (M, j, m_{ij})>\),对于N中的每个元素\(n_{jk}\),产生一系列的键值对\(<(i, k), (N, j, n_{jk})>\)

Reduce

将属于相同\((i, k)\)的值,根据\(M\)\(N\)分成两拨,对具有相同\(j\)的值,抽取\(m_{ij}\)\(n_{jk}\),相乘并累加。

倒排索引

倒排索引是目前几乎所有支持全文检索的搜索引擎都需要依赖的一个数据结构,即提供了一种内容来查找文档的方式。

简单倒排索引

Mapper

对于输入的每一行,输出<word, filename#offset>

Reducer

对于同一个key,即同一个单词进行值的累加拼接,输出<fish, doc1#0;doc1#8;doc2#3>

带词频的倒排索引

通常的倒排索引的键是word,值是posting的集合,每个posting第一个是文档序号,后面是属性,我们这里就取一个词频的属性,所以输出的形式为<word, ((doc1, p1), (doc2, p2))>

如果我们还是按照简单倒排索引的思路来组键值对,那么Reducer从Mapper处获得的posting将会按文档次序进行一次排序,如果数据集很大,而且属性较多,那么就会造成内存溢出。所以我们需要采用复合键。这里的复合键可以是(word, docid),由于Mapper会按照键来自动排序,那么就会将会把文档排序。

Mapper

输入为文档的每一行,输出键值对<(word, docid), p>

Combiner

对于是相同的的键来进行词频的累加。

Partitioner

由于现在采用的是(word, docid)的复合键,那么相同的单词会被分配到不同的Reducer当中,所以我们需要自定义一个Partitioner,将只使用word来分配Reducer。

Reducer

对每个键提取word和docid,将(docid, p)存储到posting的列表当中,然后输出<word, posting>。

posted @ 2017-03-14 16:28  传奇魔法师  阅读(866)  评论(0编辑  收藏  举报