Hadoop与Spark算法分析(三)——矩阵乘法

来源:互联网 发布:销售pipeline数据 编辑:程序博客网 时间:2024/05/22 12:48

矩阵乘法的并行计算最早被用于Google提出的PageRank算法中包含的迭代乘法运算,也可高效执行大矩阵间的相乘运算。

1. 实验准备

由于Hadoop与Spark对于矩阵乘法的实现过程不同,这里分别使用Linux Shell生成随机的对应于Hadoop与Spark算法的输入矩阵文件,其中输入文件名均以“矩阵名_行值_列值”格式命名,执行shell脚本所需要的参数有矩阵的行值,列值以及矩阵的列值。
对于Hadoop输入矩阵,文件中每行数据包含矩阵元素的行列信息及具体值。对应shell脚本如下:

#! /bin/bashfor i in `seq 1 $1`do    for j in `seq 1 $2`    do        s=$((RANDOM % 100))        echo -e "$i,$j\t$s" >> M_$1_$2    donedonefor j in `seq 1 $2`do    for k in `seq 1 $3`    do        s=$((RANDOM % 100))        echo -e "$j,$k\t$s" >> N_$2_$3    donedone

对于Spark输入矩阵,输入文件命名与Hadoop一致,其中矩阵这里写图片描述以原始行列格式保存在文件中,矩阵这里写图片描述以列为单位,将所有元素以空格分开作为一行保存在文件中。对应shell脚本如下:

#! /bin/bashfor i in `seq 1 $1`do    for j in `seq 1 $2`    do        s[$j]=$((RANDOM % 100))    done    echo "${s[*]}" >> M_$1_$2donefor j in `seq 1 $2`do    for k in `seq 1 $3`    do        s[$k]=$((RANDOM % 100))    done    echo -e "${s[*]} \c" >> N_$2_$3done

2. Hadoop实现

回顾矩阵乘法的过程,假设有矩阵这里写图片描述与矩阵这里写图片描述相乘,则矩阵这里写图片描述,可以看出矩阵这里写图片描述的包含信息有矩阵这里写图片描述的行值与矩阵这里写图片描述的列值以及对应两矩阵计算后的结果元素值。基于这种思想,程序最终的输出结果,需要将矩阵这里写图片描述的行列值作为键值对中的key,运算后元素的值作为键值对中的value。另外,程序运算过程中,需要将涉及的3个关键参数:矩阵这里写图片描述的行值rowM,矩阵这里写图片描述的列值columnM和矩阵 的列值columnN,作为全局共享变量,用于map和reduce过程。
map过程通过获取文件名来判断输入矩阵,对文件中的每行数据进行解析,其中对这里写图片描述矩阵解析为((i,k),(M,j,这里写图片描述)),其中k从1递增到矩阵这里写图片描述的列值,对这里写图片描述矩阵解析为((i,k),(N,j,这里写图片描述)),其中i从1递增到矩阵这里写图片描述的行值。
reduce过程根据key进行值的合并运算,将两个矩阵经过map过程后,列值j相同的元素值这里写图片描述这里写图片描述相乘,最后将各相乘的结果相加即可得到最终矩阵的元素值。
具体代码实现如下:

package org.hadoop.test;import java.io.IOException;import org.apache.hadoop.conf.Configuration;import org.apache.hadoop.fs.Path;import org.apache.hadoop.io.Text;import org.apache.hadoop.mapreduce.Job;import org.apache.hadoop.mapreduce.Mapper;import org.apache.hadoop.mapreduce.Reducer;import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;import org.apache.hadoop.mapreduce.lib.input.FileSplit;import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;public class MatrixMultiply {    //全局共享变量    public static int rowM = 0;    public static int columnM = 0;    public static int columnN = 0;    public static class MatrixMapper         extends Mapper<Object, Text, Text, Text>{            private Text map_key = new Text();            private Text map_value = new Text();            public void setup(Context context){                Configuration conf = context.getConfiguration();                rowM = Integer.parseInt(conf.get("rowM"));                columnN = Integer.parseInt(conf.get("columnN"));            }            public void map(Object key, Text value, Context context)                     throws IOException, InterruptedException{                        FileSplit fileSplit = (FileSplit) context.getInputSplit();                        String fileName = fileSplit.getPath().getName();                        if(fileName.contains("M")){                            String[] element = value.toString().split(",");                            int i = Integer.parseInt(element[0]);                            String[] element1 = element[1].split("\t");                            int j = Integer.parseInt(element1[0]);                            int Mij = Integer.parseInt(element1[1]);                            for(int k=1;k<columnN+1;k++){                                map_key.set(i+","+k);                                map_value.set("M"+","+j+","+Mij);                                context.write(map_key, map_value);                            }                        }else{                            String[] element = value.toString().split(",");                            int j = Integer.parseInt(element[0]);                            String[] element1 = element[1].split("\t");                            int k = Integer.parseInt(element1[0]);                            int Njk = Integer.parseInt(element1[1]);                            for(int i=1;i<rowM+1;i++){                                map_key.set(i+","+k);                                map_value.set("N"+","+j+","+Njk);                                context.write(map_key, map_value);                            }                        }            }    }    public static class MatrixReducer         extends Reducer<Text, Text, Text, Text>{            private Text result = new Text();            public void setup(Context context){                Configuration conf = context.getConfiguration();                columnM = Integer.parseInt(conf.get("columnM"));            }            public void reduce(Text key, Iterable<Text> values, Context context)                     throws IOException, InterruptedException{                        int[] M = new int[columnM+1];                        int[] N = new int[columnM+1];                        int sum = 0;                        for(Text val:values){                            String[] element = val.toString().split(",");                            if(element[0].equals("M")){                                M[Integer.parseInt(element[1])] = Integer.parseInt(element[2]);                            }else{                                N[Integer.parseInt(element[1])] = Integer.parseInt(element[2]);                            }                        }                        for(int p=1;p<columnM+1;p++){                            sum += M[p]*N[p];                        }                        result.set(Integer.toString(sum));                        context.write(key, result);            }    }    public static void main(String[] args)             throws Exception {                if(args.length != 3){                    System.err.println("Usage: MatrixMultiply <MatrixM> <MatrixN> <out>");                    System.exit(2);                }else{                    String[] element_M = args[0].split("_");                    rowM = Integer.parseInt(element_M[1]);                    columnM = Integer.parseInt(element_M[2]);                    String[] element_N = args[1].split("_");                    columnN = Integer.parseInt(element_N[2]);                }                Configuration conf = new Configuration();                conf.setInt("rowM", rowM);                conf.setInt("columnM", columnM);                conf.setInt("columnN", columnN);                Job job = new Job(conf, "MatrixMultiply");                job.setJarByClass(MatrixMultiply.class);                job.setMapperClass(MatrixMapper.class);                job.setReducerClass(MatrixReducer.class);                job.setOutputKeyClass(Text.class);                job.setOutputValueClass(Text.class);                FileInputFormat.setInputPaths(job, new Path(args[0]), new Path(args[1]));                FileOutputFormat.setOutputPath(job, new Path(args[2]));                System.exit(job.waitForCompletion(true)?0:1);    }}

3. Spark实现

Spark算法执行过程中,将矩阵这里写图片描述输入文件中的每行作为一个RDD,并以本地向量形式将矩阵这里写图片描述转换为行矩阵RowMatrix,实现矩阵的分布式存储。最后通过调用RowMatrix的multiply方法实现与矩阵这里写图片描述的相乘。
具体代码实现如下:

import org.apache.spark.mllib.linalg.{Matrices, Vectors}import org.apache.spark.mllib.linalg.distributed.RowMatriximport org.apache.spark.{SparkConf, SparkContext}/**  * Created by rose on 16-4-25.  */object MatrixMultiply {  def main(args:Array[String]): Unit = {    if (args.length != 3) {      println("Usage:<MatrixA> <MatrixB> <out>")      return    }    val conf = new SparkConf().setAppName("MatrixMultiply")    val sc = new SparkContext(conf)    val rdd_A = sc.textFile(args(0)).map(_.split(" ").map(_.toDouble)).map(line=>Vectors.dense(line))    val matrixA = new RowMatrix(rdd_A)    val rdd_B = sc.textFile(args(1)).map(_.split(" ").map(_.toDouble)).toArray.flatten    val element = args(1).split("_")    val rowB = Integer.parseInt(element(1))    val columnB = Integer.parseInt(element(2))    val matrixB = Matrices.dense(rowB, columnB, rdd_B)    val resMatrix = matrixA.multiply(matrixB)    resMatrix.rows.saveAsTextFile(args(2))  }}

4. 运行过程

1)上传本地文件到HDFS目录下
在HDFS上创建输入文件夹

$hadoop fs -mkdir -p matrixMultiply/input

上传本地文件到集群的input目录下

$hadoop fs -put ~/M_* ~/N_* matrixMultiply/input

查看集群文件目录

$hadoop fs -ls matrixMultiply/input

2)运行程序
将矩阵乘法算法程序MatrixMultiply打包为后缀名为jar的压缩文件MatrixMultiply.jar,进入到压缩文件所在文件夹(这里以一对输入文件和一个output输出文件夹为例说明)。
Hadoop程序运行如下命令执行

$hadoop jar ~/hadoop/MatrixMultiply.jar org.hadoop.test.MatrixMultiply matrixMultiply/input/M_* matrixMultiply/input/N_* matrixMultiply/hadoop/output

Spark程序运行如下命令执行

$spark-submit --master yanr-client --class MatrixMultiply ~/spark/MatrixMultiply.jar hdfs://master:9000/matrixMultiply/input/M_* hdfs://master:9000/matrixMultiply/input/N_* hdfs://master:9000/matrixMultiply/spark/output

3)查看运行结果
查看Hadoop执行结果

$hadoop fs -ls matrixMultiply/hadoop/output

查看Spark执行结果

$hadoop fs -ls matrixMultiply/spark/output

5. 测试对比

这里写图片描述

如图所示为矩阵乘法测试对比图,可以看出,对于两个大矩阵的相乘运算来说,由于Spark算法在执行过程中需要将RDD转为本地的行矩阵进行乘法运算,造成了运行时间总体上要比Hadoop稍长。

阅读全文
1 0
原创粉丝点击