作業報告
( 2019學年春季學期 )
課程名稱 |
大數據與深度學習 |
作業名稱 |
大數據矩陣乘法 |
組長 |
LFY |
學號 |
2016 |
組員 |
|
學號 |
|
組員 |
|
學號 |
|
組員 |
|
學號 |
|
組員 |
|
學號 |
|
專業 |
|
教師 |
|
1、問題描述
利用擬分佈式系統,利用map/reduce編寫程序計數大數據的矩陣乘法,大型矩陣一般採用稀疏矩陣存儲,只存儲非0數據。
注意: 由於矩陣過大,所以合理的存儲是分佈式的稀疏矩陣存儲,稀疏矩陣不懂的請先修《數據結構》
2、實現描述
按照老師提醒的思路,在map階段把兩個輸入的矩陣的值進行劃分成對應的key,value值進行輸出,裏面存放計數算法所必須的內容,這樣就能對應這是用於計算哪個結果位置的哪個矩陣的什麼值,reduce階段,同一個key表示都是用於計算該位置的值,對應相同的行列序號進行相乘對應相加即得結果,若某數據不存在即根據稀疏矩陣的存儲,該值爲0。
3、運行效果
1、輸入文本
矩陣一:
矩陣二:
2、輸出文本
3、輸出日誌
4、源碼
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 liufangyu
* @version 1.0 Created on 2019年3月14日
*/
public class SparseMatrixMultiply {
private final static int ax = 4;
private final static int ay = 3;
private final static int bx = 3;
private final static int by = 2;
public static class SMMapper extends Mapper<LongWritable, Text, Text, Text>{
private Text keys = new Text();
private Text values = new Text();
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String fileName=getFileName(((FileSplit)context.getInputSplit()).getPath().toString());
String[] fields = value.toString().split(",");
String k="",val="";
if(fileName.equals("matrix1.txt")) {
k=fields[0]+",";
val="a,"+fields[1]+","+fields[2];
values.set(val);
for(int i=1;i<=by;i++) {
keys.set(k+String.valueOf(i));
context.write(keys, values);
}
}else if(fileName.equals("matrix2.txt")) {
k=","+fields[1];
val="b,"+fields[0]+","+fields[2];
values.set(val);
for(int i=1;i<=ax;i++) {
keys.set(String.valueOf(i)+k);
context.write(keys, values);
}
}
}
private String getFileName(String path) {
int index = path.lastIndexOf("/");//最後一個斜線的位置
String fileName = path.substring(index+1);//得到輸入文件的文件名
return fileName;
}
}
public static class SMReducer extends Reducer<Text, Text, Text, IntWritable> {
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));
}
String s=key.toString();
Text t=new Text();
t.set("("+s+")\t");
IntWritable ans=new IntWritable();
ans.set(result);
context.write(t,ans);
}
}
public static void main(String[] args) throws IOException,ClassNotFoundException, InterruptedException {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "SparseMatrixMultiply");
job.setJarByClass(SparseMatrixMultiply.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
job.setMapperClass(SMMapper.class);
job.setReducerClass(SMReducer.class);
job.setInputFormatClass(TextInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class);
FileInputFormat.setInputPaths(job, new Path(args[0]), new Path(args[1]));// 加載2個輸入數據集
Path outputPath = new Path(args[2]);
outputPath.getFileSystem(conf).delete(outputPath, true);
FileOutputFormat.setOutputPath(job, outputPath);
System.exit(job.waitForCompletion(true)?0 : 1);
}
}
5、總結
通過該次作業,我發現雖說map/reduce提供的接口雖然有點死板,但是靈活運用的話可以解決其他很多問題。