• Keine Ergebnisse gefunden

Genetischer Algorithmus auf dem Cluster mit Apache SparkSpark

Die Umsetzung des genetischen Algorithmus’ auf einem Multirechnersystem kann durch un-terschiedliche Ans¨atze realisiert werden. In dieser Arbeit konzentrieren wir uns auf denjenigen Ansatz, bei dem die Arbeitspakete mehrfach von mehreren Rechnern bearbeitet werden, um einen Wettbewerb zu erzielen. Das in [Shrestha und Mahmood(2016)] vorgestellte Multi-Island-Modell, mit dem wir uns im Abschnitt 3.2.1 gr ¨undlich besch¨aftigen werden, beschreibt den im Rahmen dieser Arbeit umzusetzenden Ansatz. Anschließend wird in Abschnitt 3.2.2 die in Java implementierte L ¨osung vorgestellt.

3.2.1. Multi-Island-Modell

Das Ziel des genetischen Algorithmus ist es, der optimalen L ¨osung so nah wie m ¨oglich zu kommen. Da der Suchraum der L ¨osung groß ist, besteht die zentrale Herausforderung darin, das absolute Minimum des gesamten Suchraums von lokalen Minima zu unterscheiden und die Suche nicht vorzeitig zu beenden. Der prim¨are Weg, um das sp¨atere Ziel zu adressieren ist, die richtige Menge an Vielfalt in den Eltern einzuf ¨uhren. [vgl.Shrestha und Mahmood(2016), S. 1]

Genetische Algorithmen basieren haupts¨achlich auf dem Zufall und dem Evolvieren der Po-pulation nach jeder Generation. Jeder zuf¨allige Schritt hat einen enormen Einfluss auf das Resultat des ganzen Prozesses. Genetische Algorithmen garantieren in der Regel nicht die besten Ergebnisse nach schon ein oder zwei Durchl¨aufen der Prozesse des Evolvierens. Die Qualit¨at des Endergebnisses steigt mit der Anzahl der Versuche, was wiederum mehr Zeit- und Rechenaufwand bedeutet.

Nach einigen Iterationen neigt der genetische Algorithmus oft dazu, Konvergenz zu erzeugen, indem Eltern mit gleichen genetischen Informationen beim Crossover genutzt werden, um neue Generation zu schaffen, ohne Verbesserung zu bekommen. [vgl.Shrestha und Mahmood (2016), S. 4] Von daher spielt hierbei die hohe genetische Varianz eine große Rolle.

In [Shrestha und Mahmood(2016)] wurde beschrieben:Island-Modell wurde mit Multicore-Prozessoren im Server implementiert, indem mehrere Threads parallel ausgef ¨uhrt wurden.

Jeder Thread f ¨uhrt seine eigene Version von GA aus. Nach jeder n-ten Iteration/Generation auf jedem Thread, auf welchem der GA l¨auft, wurden eine Handvoll der zuf¨allig ausgew¨ahlten Populationsmitglieder zwischen den Threads ausgetauscht. Der Prozess hat nicht nur mehr

Rechenressourcen hinzugef ¨ugt, sondern verbesserte auch die Laufzeit von GA. Auch die Diver-sit¨at und die anf¨angliche Stichprobenverzerrung.

In Abbildung3.2wurde die Architektur einesMulti-Island-Modellsdargestellt. Dort ist ein Master-Server zu sehen, der daf ¨ur zust¨andig ist, die Prozesse zu bewachen und zu kontrollieren.

Nach jeder n-ten Iteration sammelt der Master die Ergebnisse von jedem Server und tauscht die Mitglieder aus. Auf jedem Server laufen mehrere Instanzen des gleichen genetischen Algo-rithmus jeweils auf einem eigenen Thread. Die Ergebnisse aus den Threads sowie die beste Fitness werden auch von dem Server selbst ¨uberwacht. Unten in der Abbildung ist die Prozesse des genetischen Algorithmus, die in der Arbeit [Shrestha und Mahmood(2016)] vorgestellt wurde. Das m ¨ussen wir in Rahmen dieser Arbeit nicht sofort anwenden.

3.2.2. Umsetzung des Multi-Island-Modells mit Apache Spark

Die Architektur des vorgestellten Apache Spark Frameworks und das Multi-Island-Modell verf ¨ugen beide ¨uber die Eigenschaften einerMaster-Slave-Architektur. Bei beiden Architekturen kommen die Aufgaben vom Master und werden anschließend in denSlave-Nodes( Worker-Nodes), unter der ¨Uberwachung des Masters, erf ¨ullt. Daher kann man sagen, dass mithilfe des Apache Spark Frameworks die M ¨oglichkeit besteht, einen genetischen Algorithmus nach Multi-Island-Modell zu implementieren.

3.2.2.1. Umsetzungsidee

Anhand der Erkenntnis, dass dieMaster-Slave-Architekturder entscheidende Punkt f ¨ur die Um-setzung desMulti-Island-Modellsmit Apache Spark ist, wird im Folgenden die Umsetzungsidee grob erl¨autert und anschließenden die Implementierung aufgezeigt.

DasDriver-Programm, das auf demMaster-Nodel¨auft, bereitet mehrere Instanzen des geneti-schen Algorithmus’, mit der initiierten Population des zu l ¨osenden TSPs, vor. Diese Instanzen werden dann als Pakete verpackt und auf dem Cluster zum Berechnen verteilt. Auf jedem Worker-Nodewerden GA-Instanzen ausgef ¨uhrt. Die Ergebnisse werden am Ende zur ¨uck zum Master-Nodegeschickt und bewertet.

Nach jedem Durchlauf (also alle GA-Instanzen sind durchgearbeitet) werden die Ergebnisse miteinander verglichen und ggf. kombiniert, um neue GA-Instanzen mit verbesserten Popula-tionen zu erstellen.

3. L¨osungsvorstellung und Umsetzung

Abbildung 3.2.: Multi-Island-Modell, Quelle:Shrestha und Mahmood(2016), S. 32

Abbildung 3.3.: Architektur f ¨ur die Umsetzung desIGAs mit Apache Spark

DasDriver-Programmwendet danach denselben Prozess an den neu erzeugten GA-Instanzen an, um die Berechnung fortzusetzen. Wenn die maximale Anzahl der Iterationen erreicht ist, wird das Endergebnis letztendlich berechnet und zur ¨uckgegeben.

3.2.2.2. Umsetzung

Abbildung 3.3 stellt die Architektur der Umsetzung dar. Im Allgemeinen finden die Prozesse haupts¨achlich um dasPacketherum statt. Ein Packet ist ein Wrapper einer Instanz von dem genetischen Algorithmus, welcher unabh¨angig von anderen Packets arbeiten soll. Am An-fang des Berechnungsprozesses wird eine Instanz desIGAs mit der initiierten Population in ein Packet verpackt. Mehrere Kopien des Packets werden erstellt und alsSpark-RDDan den Worker verschickt. Diese erste Phase passiert lediglich imDriver-Programmauf demMaster. Jeder Worker bekommt mehrere Packets zugeschickt und f ¨uhrt sie alle samt in einem separaten Threadaus. Die Workers schicken am Ende der Berechnungen die Ergebnisse zur ¨uck zu dem Master. Mit dermerge()-Funktion werden diese Packets zusammengef ¨ugt, indem die besten Ergebnisse sich zu einem neuen Packet zusammenf ¨ugen. Mit dem neu generierten Packet wird der Prozess wiederholt und h ¨ort erst dann auf, wenn die maximale Iteration erreicht ist.

3. L¨osungsvorstellung und Umsetzung

Packet

Die Packet-Klasse implementiert der Wrapper einer Instanz vom genetischen Algorithmen um das TSP zu l ¨osen. Die Klasse Packet erfordert eine Liste der St¨adte als Parameter f ¨ur den Konstruktor. Die Population kann ggf. im Konstruktor initiiert oder als Parameter eingegeben werden. Es verf ¨ugt in dieser Klasse dierun()-Methode, mit welcher der genetische Algorithmus bis zu einer vorher festgelegten Anzahl von Iterationen ausgef ¨uhrt werden soll. Diese Methode gibt als Ergebnis neues Packet zur ¨uck mit der evolvierten Population des orginalen Problems.

Die Signatur lautet run(): Packet. Dierun()-Methode ist so gebaut, damit sie w¨ahrend des Map-Reduce-Prozesses aufgerufen werden kann. Außerdem bietet die Klasse Packet auch die M ¨oglichkeit an, sich zu klonen oder zu multiplizieren. Somit l¨auft der genetische Algorithmus auf jedem Thread eines Worker.

Listing 3.11: Java-Kode f ¨ur Methoden von der Packet-Klasse vgl.Dommerholt(b)

public Packet(Cities cities) { this.cities = cities;

this.pop = new Population(2, cities);

this.ga = new IGA();

}

public Packet run() {

pop = ga.evolvePopulation(pop);

for (int i = 0; i < ITERATIONMAX; i++) pop = ga.evolvePopulation(pop);

System.out.println(i + ”. Current distance: ” + pop.getFittest().getDistance());

}

System.out.println(”Packet Finished”);

System.out.println(”Packet distance: ”

+ pop.getFittest().getDistance());

System.out.println(”Solution:”);

System.out.println(pop.getFittest().toString());

return this.clone();

}

Listing 3.12: Java-Kode f ¨ur Methoden von der PacketFactory-Klasse

public class PacketFactory {

public static Packet getPacket(Cities cities) {

return new Packet(cities);

}

public static Packet getPacket(Cities cities, Population population) { return new Packet(cities);

}

public static Packet merge(Packet packet1, Packet packet2) { Tour tour1 = packet1.getPopulation().getFittest();

Tour tour2 = packet2.getPopulation().getFittest();

Population newPop = new Population(Arrays.asList(tour1, tour2));

return new Packet(packet1.getCities(), newPop);

}

public static List¡Packet¿ multiply(Packet packet, int slices) { List<Packet> packets = new ArrayList<>();

IntStream.range(0, slices).forEach(x -> { packets.add(copy(packet));

});

return packets;

}

public static Packet copy(Packet packet)–

Cities cities = new Cities(new HashSet<>(packet.getCities().getCities()));

Population pop = new Population(packet.getPopulation().getTours());

return new Packet(cities, pop);

} }

Organiser

Der gesamte Prozess wird mit einem Organiser verwaltet. Die Klasse Organiser ist daf ¨ur zust¨andig die Zusammenarbeit der Wokers zu organisieren. Der Organiser bereitet am Anfang des Berechnungsprozesses ein initiiertes Packet mit der unbearbeiteten Population vor. Das Packet wird dann mehrfach kopiert und in eine Collection gepackt, um RDD f ¨ur die Bear-beitung auf den Workers zu dienen. Jeder Worker ist f ¨ur mindestens eine Instanz vom GA zust¨andig. Die Berechnungen auf einem Packet werden mitmap()-Funktion getriggert. Mit reduce()-Funktion werden die Ergebnisse kombiniert und die Endergebnisse werden zur ¨uck zum Driver-Programm gesendet. Dieser Funktion nutzt diemerge()-Methode der Klasse Packet-Factory, welche die beste Tour von den zu kombinierenden Packets ausw¨ahlen und in ein neues Packet packen. Das neu erzeugte Packet wird wie das initiierte Packet behandelt, um die Iterationen fortzusetzen.

3. L¨osungsvorstellung und Umsetzung

Listing 3.13: Scala-Kode f ¨ur die Organiser-Klasse vgl.Dommerholt(a)

class Organiser(val sc: SparkContext) {

def run(cities: Cities, iterations: Int): Packet = { var packet = new Packet(cities)

//

for(a <- 1 to iterations) –

val packets = collection.JavaConversions.asScalaBuffer(

PacketFactory.multiply(packet,sc.defaultParallelism)).toList val packetsRDD = sc.parallelize(packets)

Packet = packetsRDD.map(p => p.run())

.reduce((p1,p2) => PacketFactory.merge(p1,p2)) println(”Iteration ” + a + ” result: ”+ packet.getShortest.getDistance) }

[vgl. Dommerholt]

//

println(”Final Distance: ”+ packet.getShortest.getDistance) println(”Solution Distance: ”+ packet.getShortest)

packet /

In diesem Kapitel wird die Performance unserer Umsetzung des genetischen Algorithmus’

auf dem Apache-Spark-Cluster in Form von Benchmarks getestet und ausgewertet. Im Fol-genden wird zuerst der Aufbau der Benchmarks erkl¨art. Danach werden die tats¨achlichen Benchmarks durchgef ¨uhrt. Die Testdaten werden durch die entwickelte L ¨osung berechnet und ausgewertet. Im Anschluss wird auf die Performance nach Anzahl der Cluster-Sklaven detaillierter eingegangen.