b) Kiến trúc của Hadoop
4.5. Thực nghiệm và đánh giá kết quả
4.5.1. Thiết lập môi trƣờng thực nghiệm
Các cài đặt sử dụng cho thực nghiệm các thuật toán trong đề tài này nhƣ sau:
Môi trƣờng thực nghiệm:
Hệ thống Hadoop dùng để thực nghiệm trong đề tài bao gồm 17 máy tính để bàn: một máy đƣợc dùng làm master node, 16 máy slaver node dùng cho việc tính toán tìm ra kết quả của bài toán. Mỗi máy tính có 2 CPUs và bộ nhớ RAM là 2 GB. Tất cả các thuật toán trong đề tài đƣợc cài đặt bằng Java.
Dữ liệu thực nghiệm:
Đề tài sử dụng một dữ liệu mạng đƣờng đi của thành phố New York [35] với kích thƣớc 264.346 đỉnh và 733.846 cạnh, trong đó mỗi đỉnh là định danh một địa
55
điểm của thành phố và mỗi cạnh chỉ đƣờng đi từ một địa điểm đến một địa điểm khác. Mỗi cạnh có trọng số là khoảng cánh giữa hai địa điểm.
Để tạo ra các phần đồ thị con khác nhau mà vẫn đảm bảo tính đúng đắn của dữ liệu, đề tài đã sử dụng công cụ GraphLab [36] cho việc phân mảnh đồ thị thành nhiều đồ thị con. Tác giả đã chuẩn bị 4 bộ dữ liệu với số phần đồ thị con lần lƣợt là 4, 8, 16 và 32.
Câu truy vấn:
Tác giả đã chọn ra ngẫu nhiên một câu truy vấn: Tìm đƣờng đi ngắn nhất từ đỉnh có mã là “1” đến đỉnh có mã là “14” trên đồ thị. Câu truy vấn này tồn tại kết quả một đƣờng đi ngắn nhất với tổng độ dài = 111751, nó đƣợc đƣa ra từ kết quả chạy thuật toán Dijkstra trên đồ thị ban đầu (chƣa phân mảnh). Tác giả dùng kết quả này để đối chiếu với kết quả sinh ra từ thuật toán đề xuất.
4.5.2. Kết quả thực nghiệm
Trong phần này, tác giả trình bày kết quả của việc thực hiện câu truy vấn nêu trên và đo lƣợng thời gian trả lời câu truy vấn trên môi trƣờng phân tán với các bộ dữ liệu có kích thƣớc khác nhau. Hình 4.5 cho thấy kết quả khi chia đồ thị ra thành càng nhiều phần đồ thị con và thực hiện thuật toán song song trên từng đồ thị con đó thì thời gian thực hiện thuật toán sẽ giảm dần. Cụ thể khi dữ liệu đƣợc chia thành 4 phần đồ thị con thì thời gian thực hiện của thuật toán là 200s, khi chia thành 8 phần đồ thị con thì thời gian thực hiện giảm còn 120s và khi số đồ thị con là 32 thì thời gian giảm chỉ còn 52s.
56
Hình 4.5: Thời gian thực thi câu truy vấn với số lượng đồ thị con khác nhau
Phân tích:
Hình 4.5 chỉ ra rằng thời gian trả lời câu truy vấn sẽ tỉ lệ nghịch với số phần đồ thị con. Thời gian giảm nhanh khi tăng số lƣợng hàm Map dùng cho việc tìm kiếm các kết quả từng phần. Ở đây, mỗi phần đồ thị con đƣợc thực hiện bởi một hàm Map. Thời gian thực hiện ở đây bao gồm cả thời gian khởi tạo 1 Job trong Hadoop, ghi/đọc dữ liệu từ HDFS và thời gian chạy thuật toán trả lời câu truy vấn. Chính vì vậy, ở đây chúng ta không so sánh về mặt thời gian thực hiện giữa thuật toán song song phân tán với thời gian thuật toán chạy tuần tự trên một máy. Đề xuất này của tác giả có ý nghĩa lớn trong việc xử lý phân tán với dữ liệu lớn, cái mà một máy tính đơn nhất có thể không giải quyết đƣợc cả về mặt lƣu trữ và việc xử lý tính toán.
200 120 67 52 0 50 100 150 200 250 4 8 16 32 Th ờ i gi an th ự c h iệ n ( gi ây ) Số phần đồ thị con
57
KẾT LUẬN
1. Kết quả đạt đƣợc của đề tài
Sau thời gian nghiên cứu thực hiện, đề tài đã hoàn thành đƣợc các nhiệm vụ cơ bản ban đầu đặt ra, với các kết quả đạt đƣợc nhƣ sau:
Nghiên cứu đƣợc một số vấn đề trong xử lý đồ thị lớn.
- Tổng quan về đồ thị, đồ thị lớn và các ứng dụng của bài toán xử lý đồ thị lớn.
- Một số thách thức, kỹ thuật và công cụ trong xử lý đồ thị lớn. Nghiên cứu về Hadoop/MapReduce
Phân tích bài toán đồ thị lớn với MapReduce - Khai phá đồ thị
- So khớp đồ thị - Truy vấn đồ thị
Giải quyết bài toán tìm đƣờng đi ngắn nhất trên đồ thị phân tán sử dụng MapReduce.
- Đề xuất thuật toán tìm đƣờng đi ngắn nhất trên đồ thị phân tán. - Thực nghiệm cài đặt bài toán sử dụng MapReduce trên Hadoop - Đánh giá kết quả đạt đƣợc.
Xuất bản 1 bài báo : “Một cách tiếp cận mới cho bài toán tìm đƣờng đi ngắn nhất trên đồ thị phân tán” Tạp trí Khoa học và Công nghệ Trƣờng Đại học Sƣ phạm Kỹ thuật Hƣng Yên số 9 tháng 3/2016 Tác giả : Nguyễn Thị Huyền – Phạm Đăng Hải.
Kết quả thực nghiệm trên một dữ liệu đồ thị thực tế đã chỉ ra tính hiệu quả trong cách tiếp cận của tác giả cho việc xử lý đồ thị lớn trên môi trƣờng
58
phân tán. Do đó, đề xuất này có thể áp dụng cho các ứng dụng khác trong thực tế nhƣ phân tích mạng xã hội, mạng internet, mạng giao thông, v.v…
2. Hạn chế của đề tài
Bên cạnh các kết quả đạt đƣợc, trong khuôn khổ của đề tài này vẫn còn một số việc chƣa làm đƣợc, cụ thể nhƣ:
- Tác giả dừng lại ở việc đề xuất và cài đặt thuật toán cho bài toán tìm đƣờng đi ngắn nhất trên đồ thị phân tán sử dụng MapReduce, chƣa so sánh thuật toán đề xuất với các thuật toán khác về mặt thực nghiệm.
- Quá trình thực nghiệm gặp phải vấn đề khó khăn trong tiền xử lý dữ liệu, đó là việc phân mảnh đồ thị lớn với số đỉnh trên 10 triệu.
3. Hƣớng phát triển của đề tài
Việc giải quyết những hạn chế nêu trên cũng là hƣớng phát triển của đề tài. Trong tƣơng lai tác giả sẽ nghiên cứu thêm các giải thuật tối ƣu cho truy vấn trên đồ thị phân tán và nghiên cứu các loại câu truy vấn khác nhau nhƣ truy vấn có dàng buộc điều kiện, truy vấn dựa vào biểu thức chính quy.
59
TÀI LIỆU THAM KHẢO
[1]. Lê Minh Hoàng, “Chuyên đề Lý thuyết đồ thị.” Đại Học Sƣ Phạm Hà Nội, 1999-2002
[2]. Zaharia, Matei, et al. "Spark: Cluster Computing with Working Sets." HotCloud10 (2010): 10-10
[3]. Leskovec, Jure, et al. "Statistical properties of community structure in large social and information networks." Proceedings of the 17th international conference on World Wide Web. ACM, 2008.
[4]. Bastian, Mathieu, Sebastien Heymann, and Mathieu Jacomy. "Gephi: an open source software for exploring and manipulating networks." ICWSM 8 (2009): 361- 362.
[5]. Dean, Jeffrey, and Sanjay Ghemawat. "MapReduce: simplified data processing on large clusters." Communications of the ACM 51.1 (2008): 107-113.
[6]. Holder, L.B., Cook, D.J., Djoko, S.: Substucture Discovery in the SUBDUE System. In: Knowledge Discovery and Data Mining, pp. 169–180 (1994)
[7]. Inokuchi, Akihiro, Takashi Washio, and Hiroshi Motoda. "An apriori-based algorithm for mining frequent substructures from graph data." Principles of Data Mining and Knowledge Discovery. Springer Berlin Heidelberg, 2000. 13-23.
[8]. Deshpande, Mukund, et al. "Frequent substructure-based approaches for classifying chemical compounds." Knowledge and Data Engineering, IEEE Transactions on 17.8 (2005): 1036-1050.
[9]. Alexaki, Sofia, et al. "On Storing Voluminous RDF Descriptions: The Case of Web Portal Catalogs." WebDB. 2001.
[10]. Bunke, Horst, and Kim Shearer. "A graph distance metric based on the maximal common subgraph." Pattern recognition letters 19.3 (1998): 255-259. [11]. Pei, Jian, et al. "Prefixspan: Mining sequential patterns efficiently by prefix- projected pattern growth." icccn. IEEE, 2001.
60
[12]. Yan, Xifeng, Philip S. Yu, and Jiawei Han. "Graph indexing: a frequent structure-based approach." Proceedings of the 2004 ACM SIGMOD international conference on Management of data. ACM, 2004.
[13]. Jin, Changjiu, et al. "GBLENDER: towards blending visual query formulation and query processing in graph databases." Proceedings of the 2010 ACM SIGMOD International Conference on Management of data. ACM, 2010.
[14]. Padmanabhan, S., Chakravarthy, S.: HDB-Subdue: A Scalable Approach to Graph Mining. In: Pedersen, T.B., Mohania, M.K., Tjoa, A.M. (eds.) DaWaK 2009. LNCS, vol. 5691, pp. 325–338. Springer, Heidelberg (2009)
[15]. Pelillo, Marcello. "Replicator equations, maximal cliques, and graph isomorphism." Neural Computation 11.8 (1999): 1933-1955.
[16]. Bunke, Horst, and Gudrun Allermann. "Inexact graph matching for structural pattern recognition." Pattern Recognition Letters 1.4 (1983): 245-253.
[17]. Bollobás, Bela, and Fan R. K. Chung. "The diameter of a cycle plus a random matching." SIAM Journal on discrete mathematics 1.3 (1988): 328-333.
[18]. Umeyama, Shinji. "An eigendecomposition approach to weighted graph matching problems." Pattern Analysis and Machine Intelligence, IEEE Transactions on 10.5 (1988): 695-703.
[19]. Melnik, Sergey, Hector Garcia-Molina, and Erhard Rahm. "Similarity flooding: A versatile graph matching algorithm and its application to schema matching."Data Engineering, 2002. Proceedings. 18th International Conference on. IEEE, 2002.
[20]. Zager, Laura A., and George C. Verghese. "Graph similarity scoring and matching." Applied mathematics letters 21.1 (2008): 86-94.
[21]. Malewicz, Grzegorz, et al. "Pregel: a system for large-scale graph processing." Proceedings of the 2010 ACM SIGMOD International Conference on Management of data. ACM, 2010.
61
[22]. Atre, Medha, et al. "Matrix Bit loaded: a scalable lightweight join query processor for RDF data." Proceedings of the 19th international conference on World wide web. ACM, 2010.
[23]. Neumann, Thomas, and Gerhard Weikum. "The RDF-3X engine for scalable management of RDF data." The VLDB Journal 19.1 (2010): 91-113.
[24]. Sun, Zhao, et al. "Efficient subgraph matching on billion node graphs." Proceedings of the VLDB Endowment 5.9 (2012): 788-799.
[25]. Jin, Changjiu, et al. "Gblender: visual subgraph query formulation meets query processing." Proceedings of the 2011 ACM SIGMOD International Conference on Management of data. ACM, 2011.
[26]. Afrati, Foto N., Dimitris Fotakis, and Jeffrey D. Ullman. "Enumerating subgraph instances using map-reduce." Data Engineering (ICDE), 2013 IEEE 29th International Conference on. IEEE, 2013.
[27]. Tong, Hanghang, et al. "Fast best-effort pattern matching in large attributed graphs." Proceedings of the 13th ACM SIGKDD international conference on Knowledge discovery and data mining. ACM, 2007.
[28]. Tian, Yuanyuan, and Jignesh M. Patel. "Tale: A tool for approximate large graph matching." Data Engineering, 2008. ICDE 2008. IEEE 24th International Conference on. IEEE, 2008.
[29]. Gregor, Douglas, and Andrew Lumsdaine. "The parallel BGL: A generic library for distributed graph computations." Parallel Object-Oriented Scientific Computing (POOSC) 2 (2005): 1-18.
[30]. Lugowski, Adam, et al. "A Flexible Open-Source Toolbox for Scalable Complex Graph Analysis." SDM. Vol. 12. 2012.
[31]. Jones, Neil D. "An introduction to partial evaluation." ACM Computing Surveys (CSUR) 28.3 (1996): 480-503.
[32]. Buneman, Peter, et al. "Using partial evaluation in distributed query evaluation." Proceedings of the 32nd international conference on Very large data bases. VLDB Endowment, 2006.
62
[33]. Fan, Wenfei, et al. "Adding regular expressions to graph reachability and pattern queries." Data Engineering (ICDE), 2011 IEEE 27th International Conference on. IEEE, 2011.
[34]. Seo, Sangwon, et al. "Hama: An efficient matrix computation with the mapreduce framework." Cloud Computing Technology and Science (CloudCom), 2010 IEEE Second International Conference on. IEEE, 2010.
[35]. Camil Demetrescu. 2014. “9th DIMACS Implementation Challenge: Shortest Paths”, Accessed 01-2016. http://www.dis.uniroma1.it/challenge9/download.shtml [36]. Low, Yucheng, et al. "Graphlab: A new framework for parallel machine learning." arXiv preprint arXiv:1408.2041 (2014).
63 PHỤ LỤC Phụ lục 1: Thủ tục chạy các hàm Map/Reduce ShortestPathDriver.java package search; import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class ShortestPathDriver {
public static void main(String[] args) throws IOException,
ClassNotFoundException, InterruptedException {
long startTime = System.currentTimeMillis();
Configuration conf = new Configuration();
String input = "/data/input"; String output = "/data/output";
boolean isRunOnServer = false;
if (isRunOnServer) {
conf.addResource(new Path("/usr/local/hadoop/conf/core-
site.xml"));
conf.addResource(new Path("/usr/local/hadoop/conf/hdfs-
site.xml"));
input = args[0]; output = args[1];
conf.set("SOURCE", args[2]); conf.set("DIST",args[3]); conf.set("NUM","264346"); } else {
conf.addResource(new Path("/usr/local/hadoop/conf/core-
site.xml"));
conf.addResource(new Path("/usr/local/hadoop/conf/hdfs-
site.xml")); conf.set("SOURCE", "1"); conf.set("DIST","14"); conf.set("NUM","264346"); } FileSystem hdfs = FileSystem.get(conf);
if (hdfs.exists(new Path(output))) {
64 }
Job job = new Job(conf, "ShortestPath");
job.setJarByClass(ShortestPathDriver.class); job.setMapperClass(ShortestPathMapper.class); job.setReducerClass(ShortestPathReducer.class); job.setMapOutputKeyClass(IntWritable.class); job.setMapOutputValueClass(VertexWritable.class); job.setOutputKeyClass(NullWritable.class); job.setOutputValueClass(VertexWritable.class); job.setInputFormatClass(GraphFileInputFormat.class);
GraphFileInputFormat.addInputPath(job, new Path(input));
FileOutputFormat.setOutputPath(job, new Path(output));
job.waitForCompletion(true);
long stopTime = System.currentTimeMillis();
long elapsedTime1 = stopTime - startTime;
System.out.println("Job time: " + elapsedTime1 / 1000 + "
s\n"); System.exit(0); } } Phụ lục 2: Thủ tục ShortestPathMapper ShortestPathMapper.java package search; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.PriorityQueue; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper;
65 VertexWritable> {
public void map(Text key, Text value, Context context) throws
IOException,
InterruptedException {
HashMap<Integer, Vertex> listVi = new HashMap<Integer, Vertex>();
HashMap<Integer, Vertex> listIn = new HashMap<Integer, Vertex>();
HashMap<Integer, Vertex> listOut = new HashMap<Integer, Vertex>();
Configuration conf = context.getConfiguration(); Init.SOURCE = Integer.parseInt(conf.get("SOURCE")); Init.DIST = Integer.parseInt(conf.get("DIST"));
Init.VERTEX_NUM = Integer.parseInt(conf.get("NUM")); LoadData(value, listVi, listIn, listOut);
for(Vertex v: listIn.values()){
Map<Integer, Vertex> listR = Dijkstra(v, listOut,listVi);
VertexWritable val = new VertexWritable();
val.startId = v.id; val.listV = listR;
context.write(new IntWritable(1), val);
} }
public static ArrayList<Integer> GetShortestPathTo(Vertex target)
{
ArrayList<Integer> path = new ArrayList<Integer>();
for (Vertex vertex = target; vertex != null; vertex =
vertex.previous) path.add(vertex.id); Collections.reverse(path); path.remove(0); return path; }
public HashMap<Integer, Vertex> Dijkstra(Vertex source,
HashMap<Integer,Vertex> listTarget, HashMap<Integer, Vertex> listVi) { PriorityQueue<Vertex> q = new PriorityQueue<Vertex>(); Vertex s = listVi.get(source.id);
s.minDistance =0;
s.previous = null;
q.add(s);
66 { if(v.id != source.id) { v.minDistance = Integer.MAX_VALUE; v.previous= null; } } while (!q.isEmpty()) { Vertex u = q.poll();
for (Edge e : u.neighbors) {
Vertex v = listVi.get(e.neighborId);
int newDist = u.minDistance + e.weight;
if (newDist < v.minDistance) { q.remove(v); v.minDistance = newDist; v.previous = u; q.add(v); } } }
HashMap<Integer, Vertex> listR = new HashMap<Integer, Vertex>();
for(Vertex v: listVi.values()){
if(listTarget.containsKey(v.id) && v.minDistance!=Integer.MAX_VALUE)
{
ArrayList<Integer> listP = GetShortestPathTo(v); v.listP = listP; listR.put(v.id, v); } } return listR; }
public void LoadData(Text value,
HashMap<Integer, Vertex> listVi, HashMap<Integer, Vertex> listIn, HashMap<Integer, Vertex> listOut) {
try {
for(int i=0;i<Init.VERTEX_NUM;i++)
listVi.put(i+1,new Vertex(i+1));
byte[] bytes = value.getBytes();
ByteArrayInputStream arr = new
ByteArrayInputStream(bytes);
BufferedReader bNode = new BufferedReader(
new InputStreamReader(arr));
HashSet<Integer> listT = new HashSet<Integer>();
67
while ((strLine = bNode.readLine()) != null) {
String[] row = strLine.split("\t");
String[] startNode = row[0].split(","); Vertex vi =
listVi.get(Integer.parseInt(startNode[0])); listT.add(vi.id);
for(int i = 1;i<row.length; i++) { String [] tmp = row[i].split(","); Vertex vj = new Vertex(Integer.parseInt(tmp[0])); Edge ed = new Edge(vj.id,Integer.parseInt(tmp[1])); vi.neighbors.add(ed); listT.add(vj.id);
if ((tmp.length > 2 && tmp[2].equals("0")) || vj.id==Init.DIST)
listOut.put(vj.id, vj); }
if ((startNode.length > 1 && startNode[1].equals("1")) || vi.id==Init.SOURCE)
listIn.put(vi.id, new Vertex(vi.id));
}
bNode.close();
for (int i=listVi.size();i>0;i--) {
if(!listT.contains(i)) listVi.remove(i); } } catch (Exception e) { e.printStackTrace(); } } } Phụ lục 3: Thủ tục ShortestPathReducer ShortestPathReducer.java package search; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet;
68 import java.util.Iterator; import java.util.PriorityQueue; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Reducer;
public class ShortestPathReducer extends
Reducer<IntWritable, VertexWritable, NullWritable, Text> { HashMap<Integer, Vertex> listX = new HashMap<Integer, Vertex>(); HashSet<Integer> listT = new HashSet<Integer>();
public void reduce(IntWritable key, Iterable<VertexWritable>
values, Context context)
throws IOException, InterruptedException {
Configuration conf = context.getConfiguration(); Init.SOURCE = Integer.parseInt(conf.get("SOURCE")); Init.DIST = Integer.parseInt(conf.get("DIST")); Init.VERTEX_NUM = Integer.parseInt(conf.get("NUM"));
for(int i=0;i<Init.VERTEX_NUM;i++)
listX.put(i+1,new Vertex(i+1));
Iterator<VertexWritable> itr = values.iterator();
VertexWritable v = new VertexWritable();
while (itr.hasNext()) {
v = itr.next();
ArrayList<Edge> listE = new ArrayList<Edge>();
int idS = v.startId;
listT.add(idS);
for(Vertex vi: v.listV.values())
{
listT.add(vi.id);
listE.add(new Edge(vi.id, vi.minDistance,
vi.listP));
}
listX.get(idS).neighbors = listE; }
for (int i=listX.size();i>0;i--) {
if(!listT.contains(i)) listX.remove(i); } Vertex vs = listX.get(Init.SOURCE); Vertex vt = listX.get(Init.DIST); if(vs==null || vt == null) {
69
context.write(NullWritable.get(), new Text("Not
found!"));
}
else
{
System.out.println("Total Input Node of Gd:
"+listX.size());
String path = Dijkstra(vs, vt, listX);
context.write(NullWritable.get(), new Text(path));
} }