Hadoop矩阵乘法

时间:2012-03-14 19:02:02

标签: java hadoop cloud mapreduce

我正在运行http://www.norstad.org/matrix-multiply/index.html处的MapReduce矩阵乘法程序。 我发现当输入矩阵中有0时,此实现无法正常工作。但是我不明白为什么,以及如何修改程序以使其工作? MapReduce操作完成,但输出始终是包含所有元素0的矩阵。

我的输入矩阵A& B是:

Matrix A     Matrix B
0 0 0        6 7 4 
0 1 6        9 1 3 
7 8 9        7 6 2  

输出矩阵:

0 0 0
0 0 0
0 0 0

以下内容取自作业的日志文件:

matrixB的地图输出:

##### Map setup: matrixA = false for hdfs://localhost/user/hadoop-user/B
strategy = 4
R1 = 4
I = 3
K = 3
J = 3
IB = 3
KB = 3
JB = 3
##### Map input: (0,0) 6
##### Map output: (0,0,0,1) (0,0,6) 
##### Map input: (0,1) 7
##### Map output: (0,0,0,1) (0,1,7) 
##### Map input: (0,2) 4
##### Map output: (0,0,0,1) (0,2,4) 
##### Map input: (1,0) 9
##### Map output: (0,0,0,1) (1,0,9) 
##### Map input: (1,1) 1
##### Map output: (0,0,0,1) (1,1,1) 
##### Map input: (1,2) 3
##### Map output: (0,0,0,1) (1,2,3) 
##### Map input: (2,0) 7
##### Map output: (0,0,0,1) (2,0,7) 
##### Map input: (2,1) 6
##### Map output: (0,0,0,1) (2,1,6) 
##### Map input: (2,2) 2
##### Map output: (0,0,0,1) (2,2,2) 

Matrix A的地图输出:

##### Map setup: matrixA = true for hdfs://localhost/user/hadoop-user/A
strategy = 4
R1 = 4
I = 3
K = 3
J = 3
IB = 3
KB = 3
JB = 3
##### Map input: (1,1) 1
##### Map output: (0,0,0,0) (1,1,1) 
##### Map input: (1,2) 6
##### Map output: (0,0,0,0) (1,2,6) 
##### Map input: (2,0) 7
##### Map output: (0,0,0,0) (2,0,7) 
##### Map input: (2,1) 8
##### Map output: (0,0,0,0) (2,1,8) 
##### Map input: (2,2) 9
##### Map output: (0,0,0,0) (2,2,9) 

请注意,矩阵A的Map不会从输入文件中读取零。 注意:两个输入文件都作为序列文件存储在HDFS中,输出也是一个序列文件。有人可以解释问题是什么吗?谢谢!

Mapper类的代码如下:

/** The job 1 mapper class. */

private static class Job1Mapper 
    extends Mapper<IndexPair, IntWritable, Key, Value>
{
    private Path path;
    private boolean matrixA;
    private Key key = new Key();
    private Value value = new Value();

    public void setup (Context context) {
        init(context);
        FileSplit split = (FileSplit)context.getInputSplit();
        path = split.getPath();
        matrixA = path.toString().startsWith(inputPathA);
        if (DEBUG) {
            System.out.println("##### Map setup: matrixA = " + matrixA + " for " + path);
            System.out.println("   strategy = " + strategy);
            System.out.println("   R1 = " + R1);
            System.out.println("   I = " + I);
            System.out.println("   K = " + K);
            System.out.println("   J = " + J);
            System.out.println("   IB = " + IB);
            System.out.println("   KB = " + KB);
            System.out.println("   JB = " + JB);
        }
    }

    private void printMapInput (IndexPair indexPair, IntWritable el) {
        System.out.println("##### Map input: (" + indexPair.index1 + "," + 
            indexPair.index2 + ") " + el.get());
    }

    private void printMapOutput (Key key, Value value) {
        System.out.println("##### Map output: (" + key.index1 + "," + 
            key.index2 + "," + key.index3 + "," + key.m + ") (" + 
            value.index1 + "," + value.index2 + "," + value.v + ") ");
    }

    private void badIndex (int index, int dim, String msg) {
        System.err.println("Invalid " + msg + " in " + path + ": " + index + " " + dim);
        System.exit(1);
    }

    public void map (IndexPair indexPair, IntWritable el, Context context)
        throws IOException, InterruptedException 
    {
        if (DEBUG) printMapInput(indexPair, el);
        int i = 0;
        int k = 0;
        int j = 0;
        if (matrixA) {
            i = indexPair.index1;
            if (i < 0 || i >= I) badIndex(i, I, "A row index");
            k = indexPair.index2;
            if (k < 0 || k >= K) badIndex(k, K, "A column index");
        } else {
            k = indexPair.index1;
            if (k < 0 || k >= K) badIndex(k, K, "B row index");
            j = indexPair.index2;
            if (j < 0 || j >= J) badIndex(j, J, "B column index");
        }
        value.v = el.get();
                if (matrixA) {
                    key.index1 = i/IB;
                    key.index3 = k/KB;
                    key.m = 0;
                    value.index1 = i % IB;
                    value.index2 = k % KB;
                    for (int jb = 0; jb < NJB; jb++) {
                        key.index2 = jb;
                        context.write(key, value);
                        if (DEBUG) printMapOutput(key, value);
                    }
                } else {
                    key.index2 = j/JB;
                    key.index3 = k/KB;
                    key.m = 1;
                    value.index1 = k % KB;
                    value.index2 = j % JB;
                    for (int ib = 0; ib < NIB; ib++) {
                        key.index1 = ib;
                        context.write(key, value);
                        if (DEBUG) printMapOutput(key, value);
                    }
        }
    }
}

如果有任何语法错误,那只是因为我复制粘贴代码并在此修改它,所以我可能忽略了一些东西。

我需要帮助Map函数的逻辑,它不会从输入文件中读取0,有人可以告诉我为什么吗?

1 个答案:

答案 0 :(得分:3)

在TestMatrixMultiply.java中,来自您链接的网站(可能包含您用于将矩阵编码为预期的IndexPair支持的文件格式的代码),有函数writeMatrix:

public static void writeMatrix (int[][] matrix, int rowDim, int colDim, String pathStr)
    throws IOException
{
    Path path = new Path(pathStr);
    SequenceFile.Writer writer = SequenceFile.createWriter(fs, conf, path, 
        MatrixMultiply.IndexPair.class, IntWritable.class, 
        SequenceFile.CompressionType.NONE);
    MatrixMultiply.IndexPair indexPair = new MatrixMultiply.IndexPair();
    IntWritable el = new IntWritable();
    for (int i = 0; i < rowDim; i++) {
        for (int j = 0; j < colDim; j++) {
            int v = matrix[i][j];
            if (v != 0) { // !!! well, that would be why we aren't writing 0s
                indexPair.index1 = i;
                indexPair.index2 = j;
                el.set(v);
                writer.append(indexPair, el);
            }
        }
    }
    writer.close();
}

注释插入内部for循环的第二行。

您的映射器读数为0,因为您的输入文件不包含0。

代码经过精心设计,假设所有缺失值都为0,并执行额外的检查以避免发出0,以尝试最小化网络流量。

这里的一切都是错误的,因为我误解了算法
(但上面的部分仍然有用)

从链接页面,您正在使用策略3.策略3依赖于分区行为和排序顺序。不幸的是,分区错了!这与0没有打印出来是分开的。这里的分区器只是直接错误,你得到的矩阵都是0,因为它乘以先前初始化为0的数据,并且永远不会被块的正确数据覆盖。这在单机操作中是隐藏的,因为分区程序是空操作,但在大多数集群中都会中断。

  

分区程序将中间密钥(kb,jb,ib)映射到reducer r,如下所示:

     

r = (jb*KB + kb) mod R

需要保证同一块的所有键都分配给同一个reducer。不幸的是,它保证除非KB % numReducers == 0

,否则不会发生这种情况
map (key, value)
   if from matrix A with key=(i,k) and value=a(i,k)
      for 0 <= jb < NJB
         emit (k/KB, jb, i/IB), (i mod IB, k mod KB, a(k,j)) // compare this...
   if from matrix B with key=(k,j) and value=b(k,j)
       emit (k/KB, j/JB, -1), (k mod KB, j mod KB, b(k,j))  // ...to this

对于矩阵A,正在迭代密钥jb。对于矩阵B,正在计算密钥jb。由于对减速器的赋值是循环法,因此保证将A矩阵键分配给与B矩阵键相同的缩减器。因此,该算法失败,因为它假设关于密钥的分配和排序,这是可证明不正确的。关键字顺序是正确的,当且仅当有一个减速器分配了所有键时,但分区器是错误的!

必须修改分区程序以对策略3使用kb % numReducers。这不是一个非常好的分区程序,但它是唯一可行的分区程序,因为需要将不相同的键分类到同一个reducer按特定顺序。

您实际放在问题中的代码与错误实际存在的位置无关。