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稍长。
- Hadoop与Spark算法分析(三)——矩阵乘法
- Hadoop与Spark算法分析(一)——WordCount
- Hadoop与Spark算法分析(二)——排序算法
- Hadoop与Spark算法分析(四)——PageRank算法
- Spark中的矩阵乘法源码分析
- 算法导论——矩阵链乘法
- 算法导论——矩阵链乘法
- 蓝桥杯算法训练——矩阵乘法
- 矩阵乘法——strassen算法
- 分而治之——strassen矩阵乘法算法
- strassen算法(矩阵乘法)
- 基于hadoop与spark的大数据分析实战——第一章 Hadoop部署与实践
- 《算法设计与分析》学习笔记_第六章_矩阵链乘法问题
- 算法设计与分析:第三章 分治 3.5Strassen矩阵乘法
- 《数据结构与算法分析》动态规划--矩阵乘法最优顺序、最优二叉查找树详解
- 009-矩阵乘法-分治法-《算法设计技巧与分析》M.H.A学习笔记
- Python算法 Hadoop实例-单轮MapReduce的矩阵乘法
- 《算法导论》学习心得(二)—— 矩阵乘法之Strassen算法
- 94. Binary Tree Inorder Traversal
- 基础算法 之 BFS & DFS
- ios-__autoreleasing修饰符
- 【其他】我整理了一个书单
- PHP操作mongo数据库
- Hadoop与Spark算法分析(三)——矩阵乘法
- 《零基础入门学习Python》学习笔记1
- Consul 是什么
- Zookeeper_神奇的动物在哪里?
- C# Image与Base64编码互转函数
- 深度学习框架TensorFlow学习与应用(三)——使用交叉熵作为代价函数
- 自定义
- jdbc与odbc
- 从尾到头打印链表