MapReduce实现矩阵乘法
简单回想一下矩阵乘法:
矩阵乘法要求左矩阵的列数与右矩阵的行数相等。m×n的矩阵A,与n×p的矩阵B相乘,结果为m×p的矩阵C。具体内容能够查看:矩阵乘法。
为了方便描写叙述,先进行如果:
- 矩阵A的行数为m,列数为n,aij为矩阵A第i行j列的元素。
- 矩阵B的行数为n。列数为p。bij为矩阵B第i行j列的元素。
分析
由于分布式计算的特点,须要找到相互独立的计算过程,以便能够在不同的节点上进行计算而不会彼此影响。依据矩阵乘法的公式,C中各个元素的计算都是相互独立的,即各个cij在计算过程中彼此不影响。这种话,在Map阶段能够把计算所须要的元素都集中到同一个key中,然后,在Reduce阶段就能够从中解析出各个元素来计算cij。
另外,以a11为例,它将会在c11、c12……c1p的计算中使用。也就是说。在Map阶段,当我们从HDFS取出一行记录时,假设该记录是A的元素。则须要存储成p个<key, value>对。而且这p个key互不同样。假设该记录是B的元素,则须要存储成m个<key, value>对,同样的,m个key也应互不同样;但同一时候。用于存放计算cij的ai1、ai2……ain和b1j、b2j……bnj的<key,
value>对的key应该都是同样的,这样才干被传递到同一个Reduce中。
设计
普遍有一个共识是:数据结构+算法=程序,所以在编写代码之前须要先理清数据存储结构和处理数据的算法。
算法
map阶段
在map阶段。须要做的是进行数据准备。把来自矩阵A的元素aij,标识成p条<key, value>的形式,key="i,k",(当中k=1,2,...,p)。value="a:j,aij";把来自矩阵B的元素bij,标识成m条<key, value>形式,key="k,j"(当中k=1,2,...,m),value="b:i,bij"。
经过处理,用于计算cij须要的a、b就转变为有同样key("i,j")的数据对,通过value中"a:"、"b:"能区分元素是来自矩阵A还是矩阵B。以及详细的位置(在矩阵A的第几列。在矩阵B的第几行)。
shuffle阶段
这个阶段是Hadoop自己主动完毕的阶段,具有同样key的value被分到同一个Iterable中,形成<key,Iterable(value)>对,再传递给reduce。
reduce阶段
通过map数据预处理和shuffle数据分组两个阶段,reduce阶段仅仅须要知道两件事即可:
- <key,Iterable(value)>对经过计算得到的是矩阵C的哪个元素?由于map阶段对数据的处理。key(i,j)中的数据对。就是其在矩阵C中的位置,第i行j列。
- Iterable中的每一个value来自于矩阵A和矩阵B的哪个位置?这个也在map阶段进行了标记。对于value(x:y,z),仅仅须要找到y同样的来自不同矩阵(即x分别为a和b)的两个元素,取z相乘,然后加和就可以。
数据结构
计算过程已经设计清楚了,就须要对数据结构进行设计。大体有两种设计方案:
第一种:使用最原始的表示方式,同样行内不同列数据通过","切割。不同行通过换行切割。
另外一种:通过行列表示法,即文件里的每行数据有三个元素通过分隔符切割,第一个元素表示行,第二个元素表示列,第三个元素表示数据。这样的方式对于能够不列出为0的元素,即能够降低稀疏矩阵的数据量。
在上图中,第一种方式存储的数据量小于另外一种,但这仅仅是由于样例中的数据设计成这样。在现实中,使用分布式计算矩阵乘法的环境中,大部分矩阵是稀疏矩阵。且数据量极大,在这样的情况下,另外一种数据结构的优势就显现了出来。并且,由于使用分布式计算,假设数据大于64m,在map阶段将不可以逐行处理,将不能确定数据来自于哪一行。只是,由于现实中对于大矩阵的乘法,考虑到存储空间和内存的情况,须要特殊的处理方式,有一种是将矩阵进行行列转换然后计算。这个时候第一种还是挺有用的。
编写代码
第一种数据结构
代码为:
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
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.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat; /**
* @author liuxinghao
* @version 1.0 Created on 2014年10月9日
*/
public class MatrixMultiply {
public static class MatrixMapper extends
Mapper<LongWritable, Text, Text, Text> {
private String flag = null;// 数据集名称
private int rowNum = 4;// 矩阵A的行数
private int colNum = 2;// 矩阵B的列数
private int rowIndexA = 1; // 矩阵A,当前在第几行
private int rowIndexB = 1; // 矩阵B。当前在第几行 @Override
protected void setup(Context context) throws IOException,
InterruptedException {
flag = ((FileSplit) context.getInputSplit()).getPath().getName();// 获取文件名
} @Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String[] tokens = value.toString().split(",");
if ("ma".equals(flag)) {
for (int i = 1; i <= colNum; i++) {
Text k = new Text(rowIndexA + "," + i);
for (int j = 0; j < tokens.length; j++) {
Text v = new Text("a," + (j + 1) + "," + tokens[j]);
context.write(k, v);
}
}
rowIndexA++;// 每运行一次map方法。矩阵向下移动一行
} else if ("mb".equals(flag)) {
for (int i = 1; i <= rowNum; i++) {
for (int j = 0; j < tokens.length; j++) {
Text k = new Text(i + "," + (j + 1));
Text v = new Text("b," + rowIndexB + "," + tokens[j]);
context.write(k, v);
}
}
rowIndexB++;// 每运行一次map方法。矩阵向下移动一行
}
}
} public static class MatrixReducer extends
Reducer<Text, Text, Text, IntWritable> {
@Override
protected void reduce(Text key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
Map<String, String> mapA = new HashMap<String, String>();
Map<String, String> mapB = new HashMap<String, String>(); for (Text value : values) {
String[] val = value.toString().split(",");
if ("a".equals(val[0])) {
mapA.put(val[1], val[2]);
} else if ("b".equals(val[0])) {
mapB.put(val[1], val[2]);
}
} int result = 0;
Iterator<String> mKeys = mapA.keySet().iterator();
while (mKeys.hasNext()) {
String mkey = mKeys.next();
if (mapB.get(mkey) == null) {// 由于mkey取的是mapA的key集合。所以仅仅须要推断mapB是否存在就可以。
continue;
}
result += Integer.parseInt(mapA.get(mkey))
* Integer.parseInt(mapB.get(mkey));
}
context.write(key, new IntWritable(result));
}
} public static void main(String[] args) throws IOException,
ClassNotFoundException, InterruptedException {
String input1 = "hdfs://192.168.1.128:9000/user/lxh/matrix/ma";
String input2 = "hdfs://192.168.1.128:9000/user/lxh/matrix/mb";
String output = "hdfs://192.168.1.128:9000/user/lxh/matrix/out"; Configuration conf = new Configuration();
conf.addResource("classpath:/hadoop/core-site.xml");
conf.addResource("classpath:/hadoop/hdfs-site.xml");
conf.addResource("classpath:/hadoop/mapred-site.xml");
conf.addResource("classpath:/hadoop/yarn-site.xml"); Job job = Job.getInstance(conf, "MatrixMultiply");
job.setJarByClass(MatrixMultiply.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class); job.setMapperClass(MatrixMapper.class);
job.setReducerClass(MatrixReducer.class); job.setInputFormatClass(TextInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class); FileInputFormat.setInputPaths(job, new Path(input1), new Path(input2));// 载入2个输入数据集
Path outputPath = new Path(output);
outputPath.getFileSystem(conf).delete(outputPath, true);
FileOutputFormat.setOutputPath(job, outputPath); System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
画图演示效果:
另外一种数据结构
代码为:
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
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.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat; /**
* @author liuxinghao
* @version 1.0 Created on 2014年10月10日
*/
public class SparseMatrixMultiply {
public static class SMMapper extends Mapper<LongWritable, Text, Text, Text> {
private String flag = null;
private int m = 4;// 矩阵A的行数
private int p = 2;// 矩阵B的列数 @Override
protected void setup(Context context) throws IOException,
InterruptedException {
FileSplit split = (FileSplit) context.getInputSplit();
flag = split.getPath().getName();
} @Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String[] val = value.toString().split(",");
if ("t1".equals(flag)) {
for (int i = 1; i <= p; i++) {
context.write(new Text(val[0] + "," + i), new Text("a,"
+ val[1] + "," + val[2]));
}
} else if ("t2".equals(flag)) {
for (int i = 1; i <= m; i++) {
context.write(new Text(i + "," + val[1]), new Text("b,"
+ val[0] + "," + val[2]));
}
}
}
} public static class SMReducer extends
Reducer<Text, Text, Text, IntWritable> {
@Override
protected void reduce(Text key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
Map<String, String> mapA = new HashMap<String, String>();
Map<String, String> mapB = new HashMap<String, String>(); for (Text value : values) {
String[] val = value.toString().split(",");
if ("a".equals(val[0])) {
mapA.put(val[1], val[2]);
} else if ("b".equals(val[0])) {
mapB.put(val[1], val[2]);
}
} int result = 0;
// 可能在mapA中存在在mapB中不存在的key,或相反情况
// 由于,数据定义的时候使用的是稀疏矩阵的定义
// 所以,这样的仅仅存在于一个map中的key。说明其相应元素为0。不影响结果
Iterator<String> mKeys = mapA.keySet().iterator();
while (mKeys.hasNext()) {
String mkey = mKeys.next();
if (mapB.get(mkey) == null) {// 由于mkey取的是mapA的key集合。所以仅仅须要推断mapB是否存在就可以。
continue;
}
result += Integer.parseInt(mapA.get(mkey))
* Integer.parseInt(mapB.get(mkey));
}
context.write(key, new IntWritable(result));
}
} public static void main(String[] args) throws IOException,
ClassNotFoundException, InterruptedException {
String input1 = "hdfs://192.168.1.128:9000/user/lxh/matrix/t1";
String input2 = "hdfs://192.168.1.128:9000/user/lxh/matrix/t2";
String output = "hdfs://192.168.1.128:9000/user/lxh/matrix/out"; Configuration conf = new Configuration();
conf.addResource("classpath:/hadoop/core-site.xml");
conf.addResource("classpath:/hadoop/hdfs-site.xml");
conf.addResource("classpath:/hadoop/mapred-site.xml");
conf.addResource("classpath:/hadoop/yarn-site.xml"); Job job = Job.getInstance(conf, "SparseMatrixMultiply");
job.setJarByClass(SparseMatrixMultiply.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class); job.setMapperClass(SMMapper.class);
job.setReducerClass(SMReducer.class); job.setInputFormatClass(TextInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class); FileInputFormat.setInputPaths(job, new Path(input1), new Path(input2));// 载入2个输入数据集
Path outputPath = new Path(output);
outputPath.getFileSystem(conf).delete(outputPath, true);
FileOutputFormat.setOutputPath(job, outputPath); System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
画图演示效果:
代码分析
比較两种代码,能够非常清楚的看出,两种实现仅仅是在map阶段有些差别,reduce阶段基本同样。对于当中关于行i、列j定义不是从0计数(尽管我倾向于从0開始计数。不用写等号。简单),是为了更直观的观察数据处理过程是否符合设计。
在第一种实现中,须要记录当前是读取的哪一行数据,所以。这样的仅适用于不须要分块的小文件里进行的矩阵乘法运算。
另外一种实现中,每行数据记录了所在行所在列,不会有这方面的限制。
在另外一种实现中,遍历两个HashMap时。取mapA的key作为循环标准。是由于在普通情况下,mapA和mapB的key是同样的(如第一种实现)。由于使用稀疏矩阵,两个不同样的key说明是0,能够舍弃不參与计算。所以仅仅使用mapA的key。并推断mapB是否存在该key相应的值。
两种实现的reduce阶段。计算最后结果时。都是直接使用内存存储数据、计算结果。所以当数据量非常大的时候(通常都会非常大,否则不会用分布式处理),极易造成内存溢出,所以,对于大矩阵的运算,还须要其它的转换方式,比方行列相乘运算、分块矩阵运算、基于最小粒度相乘的算法等方式。
另外,由于这两份代码都是demo,所以代码中缺少过滤错误数据的部分。
MapReduce实现矩阵乘法的更多相关文章
- 【甘道夫】MapReduce实现矩阵乘法--实现代码
之前写了一篇分析MapReduce实现矩阵乘法算法的文章: [甘道夫]Mapreduce实现矩阵乘法的算法思路 为了让大家更直观的了解程序运行,今天编写了实现代码供大家參考. 编程环境: java v ...
- mapreduce 实现矩阵乘法
import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs ...
- 基于MapReduce的矩阵乘法
参考:http://blog.csdn.net/xyilu/article/details/9066973文章 文字未得及得总结,明天再写文字,先贴代码 package matrix; import ...
- Python+MapReduce实现矩阵相乘
算法原理 map阶段 在map阶段,需要做的是进行数据准备.把来自矩阵A的元素aij,标识成p条<key, value>的形式,key="i,k",(其中k=1,2,. ...
- 矩阵乘法的MapReduce实现
对于任意矩阵M和N,若矩阵M的列数等于矩阵N的行数,则记M和N的乘积为P=M*N,其中mik 记做矩阵M的第i行和第k列,nkj记做矩阵N的第k行和第j列,则矩阵P中,第i行第j列的元素可表示为公式( ...
- MapReduce实现大矩阵乘法
来自:http://blog.csdn.net/xyilu/article/details/9066973 引言 何 为大矩阵?Excel.SPSS,甚至SAS处理不了或者处理起来非常困难,需要设计巧 ...
- MapReduce的矩阵相乘
一.单个mapreduce的实现 转自:http://blog.sina.com.cn/s/blog_62186b460101ai1x.html 王斌_ICTIR老师的<大数据:互联网大规模数据 ...
- *HDU2254 矩阵乘法
奥运 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submissi ...
- *HDU 1757 矩阵乘法
A Simple Math Problem Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Ot ...
随机推荐
- [九省联考2018]一双木棋chess
题解: 水题吧 首先很显然的是状压或者搜索 考虑一下能不能状压吧 这个东西一定是长成三角形的样子的 所以是可以状压的 相邻两位之间有几个0代表他们差几 这样最多会有2n 然后就可以转移了 由于之前对博 ...
- python全栈开发day13-迭代器、生成器、列表推导式等
昨日内容:函数的有用信息.带参数的装饰器.多个装饰器修饰一个函数 迭代器 可迭代对象:内部含有__iter__方法 迭代器 定义:可迭代对象.__iter__()就是迭代器,含有__iter__且__ ...
- 057 Hive项目案例过程
1.说明 这里只粘贴一张,图,主要针对的hive的项目的实践过程. 2.图 3.需求 统计PV 统计注册人数 => 这个是一个公司会关注的,每天的注册率. 统计ip 统计跳出率 => ip ...
- 064 UDF
一:UDF 1.自定义UDF 二:UDAF 2.UDAF 3.介绍AbstractGenericUDAFResolver 4.介绍GenericUDAFEvaluator 5.程序 package o ...
- maven环境的配置,如果jar包下载不下来,其他配置无错误的话,极有可能是网速的缘故
1首先下载apach maven 2配置maven环境变量 m2_home maven的源文件的路径 path变量后跟 %m2_home%\bin 3cmd 控制台运行mvn -version 查看 ...
- django csrf_protect及浏览器同源策略
1.django在检测post行为时会有诸多的限制. 为了防止跨域请求伪造安全 参考:http://www.qttc.net/201209211.html https://www.cnblogs. ...
- moodle 笔记
默认版块的设置: //这些变量为新课程定义了DEFAULT块变量如果设置了这个变量,它将覆盖所有其他变量,并且是唯一使用的变量s $CFG->defaultblocks_override = ' ...
- js获取按键
event.altKey.event.ctrlKey.event.shiftKey 属性 属性为true表示事件发生时Alt.Ctrl.Shift键被按下并保持,为false则Alt.Ctrl.Shi ...
- centos7.2下安装Mysql笔记
centos7.2下安装Mysql笔记 安装 MySQL 适用于 CentOS 7.0 或以后版本: yum install mariadb mariadb-server 适用于 CentOS 6.8 ...
- 使用metasploit做SNMP扫描和利用
使用MSF用于SNMP扫描 auxiliary/scanner/snmp/snmp_login 介绍 补充知识: 在执行SNMP扫描之前,需要了解几件事情.首先,“只读”和“读写”团体名(commun ...