Wie klassifiziere ich mein Problem bei der Optimierung der Emulator-Eingabe und mit welchem ​​Algorithmus soll ich es angehen?

10

Aufgrund der Art der Frage muss ich viele Hintergrundinformationen hinzufügen (da meine Frage lautet: Wie kann ich diese eingrenzen?). Sie kann jedoch (nach bestem Wissen) wie folgt zusammengefasst werden:

Welche Methoden gibt es, um lokale Optima in extrem großen kombinatorischen Suchräumen zu finden?

Hintergrund

In der Tool-unterstützten Superplay-Community möchten wir speziell gestaltete (nicht in Echtzeit generierte) Eingaben für eine Videospielkonsole oder einen Emulator bereitstellen, um einige Kosten zu minimieren (normalerweise bis zur Fertigstellung). Die Art und Weise dies derzeit geschieht , ist durch das Spiel Frame-by-Frame zu spielen und den Eingang für jeden Frame spezifizieren, oft Teile des Lauf oft (zB redoing, die vor kurzem veröffentlicht Lauf für The Legend of Zelda: Ocarina of Time hat insgesamt 198.590 Wiederholungen).

Um diese Läufe zum Ziel zu bringen, sind normalerweise zwei Hauptfaktoren erforderlich: Routenplanung und Durchquerung. Ersteres ist viel "kreativer" als Letzteres.

Die Routenplanung bestimmt, in welche Richtung der Spieler insgesamt navigieren soll, um das Spiel zu beenden, und ist häufig der wichtigste Teil des Laufs. Dies ist analog zur Auswahl der zu verwendenden Sortiermethode. Die beste Blasensorte der Welt wird eine schnelle Sortierung von 1 Million Elementen einfach nicht übertreffen.

In dem Wunsch nach Perfektion ist jedoch auch das Durchqueren (wie die Route ausgeführt wird) ein großer Faktor. In Fortsetzung der Analogie wird auf diese Weise der Sortieralgorithmus implementiert. Einige Routen können nicht einmal ohne ganz bestimmte Eingaberahmen ausgeführt werden. Dies ist der langwierigste Prozess der Werkzeugunterstützung und führt dazu, dass die Produktion eines abgeschlossenen Laufs Monate oder sogar Jahre dauert. Es ist kein schwieriger Prozess (für einen Menschen), weil es darauf ankommt, verschiedene Variationen derselben Idee auszuprobieren, bis eine als die beste angesehen wird, aber Menschen können nur so viele Variationen in ihrer Aufmerksamkeitsspanne ausprobieren. Die Anwendung von Maschinen auf diese Aufgabe scheint hier richtig.

Mein Ziel ist es nun, den Durchlaufprozess für das Nintendo 64-System im Allgemeinen zu automatisieren . Der Suchraum für dieses Problem ist viel zu groß, um mit einem Brute-Force-Ansatz angegriffen zu werden. Ein n-Frame-Segment eines N64-Laufs hat 2 30n mögliche Eingänge, was bedeutet, dass nur 30 Frames der Eingabe (eine Sekunde bei 30 FPS) 2 900 mögliche Eingänge haben; Es wäre unmöglich, diese potenziellen Lösungen zu testen, geschweige denn diese für einen vollen zweistündigen Lauf.

Ich bin jedoch nicht daran interessiert, eine vollständige globale Optimierung eines vollständigen Laufs zu versuchen (oder vielmehr nicht einmal zu versuchen). Vielmehr möchte ich bei einer ersten Eingabe das lokale Optimum für ein bestimmtes Segment eines Laufs (oder die nächsten n lokalen Optima für eine Art semi-globale Optimierung) approximieren . Das heißt, wenn eine Route und eine anfängliche Durchquerung dieser Route gegeben sind: Durchsuchen Sie die Nachbarn dieser Durchquerung, um die Kosten zu minimieren, aber entarten Sie nicht, alle Fälle auszuprobieren, die das Problem lösen könnten.

Mein Programm sollte daher einen Startzustand, einen Eingabestream, eine Auswertungsfunktion annehmen und das lokale Optimum ausgeben, indem das Ergebnis der Auswertung minimiert wird.

Aktuellen Zustand

Derzeit habe ich mich um alle Rahmenbedingungen gekümmert. Dies beinhaltet die Auswertung eines Eingabestreams durch Manipulation des Emulators, Einrichtung und Abbau, Konfiguration usw. Und als eine Art Platzhalter ist der Optimierer ein sehr grundlegender genetischer Algorithmus. Es wertet einfach eine Population von Eingabestreams aus, speichert / ersetzt den Gewinner und generiert eine neue Population durch Mutieren des Gewinnerstroms. Dieser Prozess wird fortgesetzt, bis einige beliebige Kriterien wie Zeit oder Generationsnummer erfüllt sind.

Beachten Sie, dass der langsamste Teil dieses Programms bei weitem die Auswertung eines Eingabestreams ist . Dies liegt daran, dass das Spiel für n Frames emuliert wird . (Wenn ich die Zeit hätte, würde ich meinen eigenen Emulator schreiben, der Hooks für solche Dinge bereitstellt, aber jetzt muss ich nur noch Nachrichten synthetisieren und den Speicher für einen vorhandenen Emulator aus einem anderen Prozess ändern.) Auf meinem Hauptcomputer, der ist ziemlich modern, die Auswertung von 200 Frames dauert ungefähr 14 Sekunden. Daher würde ich einen Algorithmus bevorzugen (je nach Auswahl), der die Anzahl der Funktionsbewertungen minimiert.

Ich habe ein System im Framework erstellt, das Emulatoren gleichzeitig verwaltet. Als solches kann ich eine Anzahl von Streams gleichzeitig mit einer linearen Leistungsskala auswerten , aber praktisch kann die Anzahl der laufenden Emulatoren nur 8 bis 32 betragen (und 32 drückt es wirklich), bevor sich die Systemleistung verschlechtert. Dies bedeutet (je nach Wahl), dass ein Algorithmus, der die Verarbeitung während einer Evaluierung durchführen kann, sehr vorteilhaft wäre, da der Optimierer während des Wartens auf eine Evaluierung einige schwere Aufgaben ausführen kann.

Als Test bestand meine Bewertungsfunktion (für das Spiel Banjo Kazooie ) darin, pro Frame die Entfernung vom Spieler zu einem Zielpunkt zu summieren. Dies bedeutete, dass die optimale Lösung darin bestand, so schnell wie möglich an diesen Punkt heranzukommen. Da die Mutation nur auf den Analogstick beschränkt war, dauerte es einen Tag, bis eine gute Lösung gefunden wurde. (Dies war, bevor ich Parallelität implementiert habe.)

Nachdem ich die Parallelität hinzugefügt hatte, aktivierte ich die Mutation von A-Tastendrücken und führte dieselbe Bewertungsfunktion in einem Bereich aus, in dem ein Sprung erforderlich war. Bei 24 laufenden Emulatoren dauerte es ungefähr 1 Stunde, um das Ziel von einem anfänglich leeren Eingabestream aus zu erreichen, musste aber wahrscheinlich tagelang laufen, um zu einem nahezu optimalen Ergebnis zu gelangen.

Problem

Das Problem, mit dem ich konfrontiert bin, ist, dass ich nicht genug über das Feld der mathematischen Optimierung weiß, um zu wissen, wie ich mein Optimierungsproblem richtig modellieren kann ! Ich kann der konzeptionellen Idee vieler Algorithmen, wie sie beispielsweise in Wikipedia beschrieben ist, grob folgen, aber ich weiß nicht, wie ich mein Problem kategorisieren oder den neuesten Algorithmus für diese Kategorie auswählen soll.

Soweit ich das beurteilen kann, habe ich ein kombinatorisches Problem mit einer extrem großen Nachbarschaft . Darüber hinaus ist die Bewertungsfunktion extrem diskontinuierlich, hat keinen Gradienten und viele Plateaus . Es gibt auch nicht viele Einschränkungen, obwohl ich gerne die Möglichkeit hinzufügen werde, sie auszudrücken, wenn dies zur Lösung des Problems beiträgt. Ich möchte zulassen, dass beispielsweise die Schaltfläche Start nicht verwendet wird, dies ist jedoch nicht der allgemeine Fall.

Frage

Meine Frage lautet also: Wie modelliere ich das? Welche Art von Optimierungsproblem versuche ich zu lösen? Welchen Algorithmus soll ich verwenden? Ich habe keine Angst davor, Forschungsarbeiten zu lesen. Lassen Sie mich wissen, was ich lesen soll!

Intuitiv könnte ein genetischer Algorithmus nicht der beste sein, weil er nicht wirklich zu lernen scheint. Wenn beispielsweise auf Start drückt , scheint immer die Auswertung noch schlimmer machen (weil es das Spiel pausiert), sollte es irgendeine Art von Designer oder Gehirn sein , dass lernt: „Drücken von Start an jedem beliebigen Punkt nutzlos“ Aber auch dieses Ziel ist nicht so trivial , wie es sich anhört, denn manchmal drücken Start ist optimal, wie in so genannten „Pause rückwärtsLang springt“ in Super Mario 64 ! Hier müsste das Gehirn ein viel komplexeres Muster lernen: "Das Drücken von Start ist nutzlos, außer wenn sich der Spieler in diesem sehr spezifischen Zustand befindet und mit einer Kombination von Tastendrücken fortfährt ."

Es scheint, als sollte ich (oder die Maschine könnte es lernen) Eingaben auf eine andere Art und Weise darstellen, die besser für Änderungen geeignet ist. Die Eingabe pro Frame scheint zu detailliert zu sein, da wirklich "Aktionen" erforderlich sind, die sich über mehrere Frames erstrecken können. Dennoch werden viele Entdeckungen Frame für Frame vorgenommen, sodass ich dies nicht vollständig ausschließen kann (die Die oben erwähnte Pause (Rückwärts-Weitsprung erfordert Genauigkeit auf Frame-Ebene). Es scheint auch so, als ob die Tatsache, dass Eingaben seriell verarbeitet werden, etwas sein sollte, von dem man profitieren kann, aber ich bin mir nicht sicher, wie.

Derzeit lese ich über (reaktive) Tabu-Suche, sehr umfangreiche Nachbarschaftssuche, auf Lernen und Lernen basierende Optimierung und Ameisenkolonieoptimierung.

Ist dieses Problem einfach zu schwer mit etwas anderem als zufälligen genetischen Algorithmen anzugehen? Oder ist es tatsächlich ein triviales Problem, das vor langer Zeit gelöst wurde? Vielen Dank fürs Lesen und im Voraus für alle Antworten.

GManNickG
quelle
Ihr Beitrag ist ziemlich lang. Es würde den Lesern helfen, wenn Sie einen kurzen Abschnitt zum Thema haben, in dem die Frage ohne zusätzliche Hintergrundinformationen klar formuliert ist.
Kaveh
@Kaveh: Ich verstehe, dass es langwierig ist, aber aufgrund der Art der Frage ist es ziemlich schwierig, sie einzugrenzen, da ich ziemlich genau frage, wie ich sie eingrenzen kann. :(

Antworten:

6

Anhand der Informationen, die Sie in Ihrer Frage angeben, kann ich nicht erkennen, wie Standardoptimierungsmethoden (die mir bekannt sind) angewendet werden. Ihre Objekte sind nicht so kompliziert (dazu später mehr), aber Ihre Zielfunktion ist unangenehm: Ihre Werte werden von einem externen System außerhalb Ihrer Kontrolle definiert, es ist unwahrscheinlich, dass sie nette Eigenschaften haben, und so weiter. Daher denke ich, dass die Verwendung genetischer Algorithmen hier nicht unmöglich und vielleicht sogar ein guter Ansatz ist. Sie funktionieren oft besser als andere Methoden, wenn Sie keine Ahnung von der Struktur Ihres Problems haben. Es gibt viel zu beachten

  • Objektraum,
  • Zielfunktion und
  • Parameter Ihres genetischen Algorithmus,

Erlauben Sie mir also, näher darauf einzugehen.

Was sind deine Objekte?

Sie haben das bereits beantwortet: Sie betrachten eine Abfolge von Aktionen, von denen jede einen Frame einnimmt. Ich denke, das ist vielleicht zu feinkörnig. Versuchen Sie möglicherweise eine Folge von Aktionen mit jeweils einer Dauer (in Anzahl der Frames). Dies würde es ermöglichen, dass Mutationen wie "etwas länger laufen" andere Wahrscheinlichkeiten haben als "Ein Druck von A einfügen" auf natürliche Weise. Probieren Sie aus, was am besten funktioniert. Möglicherweise müssen Sie diesen Artikel erneut besuchen, nachdem Sie über die anderen Zutaten nachgedacht haben.

Was ist Ihre Zielfunktion?

Dieser ist wirklich entscheidend. Was möchten Sie optimieren? Zeit zum Ziel? Anzahl der verschiedenen Aktionen? Die Anzahl der gesammelten Sterne? Eine Kombination mehrerer Faktoren? Sobald Sie mehrere Ziele erreichen, wird es haarig - es gibt (normalerweise) keine Optima mehr!

Sie haben die Zeit bis zum Ziel erwähnt. Dies ist wahrscheinlich überhaupt keine gute Zielfunktion. Warum? Da die meisten Sequenzen nicht einmal das Ziel erreichen und daher unter dem Strich eine Konstante erreichen, entsteht eine Fitnesslandschaft wie diese (konzeptionelle Skizze in einer Dimension):

Geben Sie hier die Bildbeschreibung ein
[ Quelle ]

00

11+final distance to goal+11+time to goal

011

Wie misst man die Entfernung? Der lineare Abstand mag verlockend aussehen, hat aber seine Probleme. Auch hier können falsche Signale gesendet werden. Stellen Sie sich dieses einfache Szenario vor:

Geben Sie hier die Bildbeschreibung ein
[ Quelle ]

Jede Sequenz, die mit einem Sprung in den oberen Korridor beginnt, verbessert sich, bis sie einen Punkt direkt über dem Ziel erreicht, aber niemals das Ziel erreichen kann! Schlimmer noch, unter allen Sequenzen, die das Ziel nicht erreichen, sind diejenigen, die nach oben gehen, so gut wie diejenigen, die nach unten gehen, so dass die GA Sequenzen, die eindeutig zum Scheitern verurteilt sind, nicht ablehnen kann. Mit anderen Worten, der lineare Abstand erzeugt besonders schlechte lokale Optima, die den GA einfangen können, wenn es Sackgassen im Level gibt.

Daher schlage ich vor, dass Sie ein Raster über Ihr Level legen und Nachbarpunkte verbinden, wenn der Spielcharakter von einem zum anderen gelangen kann. Dann berechnen Sie die Entfernung vom Ziel anhand der Länge des kürzesten Pfades von dem Punkt, der dem Punkt am nächsten liegt, an dem die Sequenz den Charakter landet, bis zu dem Punkt, der dem Ziel am nächsten liegt. Dies ist leicht zu berechnen und das Betreten von Sackgassen (lokale Optima) wird sofort bestraft¹. Natürlich benötigen Sie Zugriff auf Level-Daten, aber ich gehe davon aus, dass Sie diese haben.

Wie funktioniert deine GA?

Jetzt können wir zum eigentlichen genetischen Algorithmus gelangen. Die wichtigsten Überlegungen sind Population, Selektion, Reproduktion / Mutation und Stoppkriterium.

Population

Wie groß wird Ihre Bevölkerung sein? Wenn es zu klein ist, bietet es möglicherweise nicht die Vielfalt , die für eine gute Lösung erforderlich ist. Wenn es zu groß ist, ist es wahrscheinlicher, dass Sie nutzlosen Müll mit sich herumtragen, was den Prozess verlangsamt.

Wie initialisieren Sie Ihre Bevölkerung? Wählst du zufällige Action-Sequenzen aus? Wenn ja, von welcher Länge? Haben Sie eine (kleine) Anzahl manuell generierter, vernünftiger Lösungen, mit denen Sie möglicherweise das Ziel erreichen können?

Auswahl

k

Das Kernkonzept hier ist der Selektionsdruck : Wie schwer ist es zu überleben? Machen Sie es zu klein und Sie entfernen keine Mistlösungen. Machen Sie es zu hoch und Sie machen Änderungen (insbesondere das Wechseln zwischen lokalen Optima) schwierig.

Fortpflanzung und Mutation

Sobald Sie Ihre Überlebenden einer Runde ausgewählt haben, müssen Sie die nächste Generation daraus erstellen (überleben die Eltern und sind Teil der nächsten Generation?). Es gibt zwei Hauptstrategien: Mutation und Rekombination.

Die Mutation ist ziemlich klar, obwohl die Besonderheiten unterschiedlich sein können. Mutieren Sie für jede Position in der Sequenz eines Individuums diese mit einer gewissen Wahrscheinlichkeit. Sie können dies unabhängig für jede Position tun oder die Anzahl der Mutationen zufällig auswählen oder verschiedene Mutationen mit unterschiedlichen Wahrscheinlichkeiten durchführen (z. B. Einfügen eines neuen Elements, Entfernen eines Elements, Ändern eines Elements, ...). Bei der Mutation geht es normalerweise um kleine Veränderungen.

Die Rekombination, bei der Aspekte von zwei oder mehr Lösungen zu einer neuen kombiniert werden, ist schwieriger, kann jedoch große Schritte ermöglichen, dh einen "Fitnessberg" verlassen und sich direkt zum Hang eines anderen bewegen (der möglicherweise höher ist). Eine klassische Idee ist die Frequenzweiche ; Ich weiß nicht, ob das hier Sinn macht (es scheint mir, dass das Vertauschen des Präfixes einer bestimmten Sequenz gegen etwas anderes höchstwahrscheinlich das Suffix abwerten wird). Vielleicht können Sie das Wissen über das Level und die Positionen des Spielcharakters an verschiedenen Punkten in der Sequenz verwenden, um dies zu steuern. Das heißt, Sie erstellen nur Überkreuzungspunkte, wenn sich der Charakter in beiden Sequenzen an derselben Position befindet.

Beendigung

Nk1n


Wie Sie sehen können, greifen all diese Dinge ineinander, um die tatsächliche Leistung zu beeinflussen. Wenn Sie mehrere Populationen gleichzeitig betreiben, können Sie sogar über die Implementierung einer genetischen Drift aufgrund von Migration und / oder Katastrophen nachdenken . Es gibt wenig Theorie, die Sie leiten könnte. Sie müssen also verschiedene Setups ausprobieren und schauen, wohin es Sie führt. Hoffentlich funktioniert das, was für eine Ebene funktioniert, auch für andere. Viel Spaß beim Basteln!

Hinweis : Schauen Sie sich BoxCar 2D im Lichte der obigen Ausführungen an. Sie machen einige Dinge ziemlich gut (andere nicht) und Sie können sich ein Bild davon machen, wie die Parameter eines GA seine Leistung beeinflussen können.


  1. Tatsächlich kann es recht gut funktionieren, eine Sequenz zu erstellen, die diese Fitness gierig verwendet, dh die Aktion auswählt, die die Entfernung zum Ziel aus allen möglichen nächsten Aktionen minimiert. Versuchen Sie das, bevor Sie GA verwenden!
  2. Natürlich erinnern Sie sich als Beobachter immer an die beste Lösung, die jemals gefunden wurde.
Raphael
quelle
1
Nett! Zwei Fragen. Was lässt Sie sagen, dass es (normalerweise) keine Optima in MOO gibt? Die Punkte sind Pareto-optimal, das heißt, Sie können etwas nicht verbessern, ohne etwas anderes zu opfern. Es liegt dann beim Modellierer, ihnen einen Wert zu geben. Geht es bei Mutationen nicht um kleine Veränderungen mit geringer Wahrscheinlichkeit? Bei großen Mutationswahrscheinlichkeiten tendiert die Suche dazu, zufällige, ungeleitete Bewegungen auszuführen, die normalerweise die Leistung beeinträchtigen. Ich denke, es wurde beobachtet, dass kleine Mutationswahrscheinlichkeiten am besten funktionieren.
Juho
1/nn1
In Ordnung, ich verstehe. In Bezug auf den dritten Punkt habe ich genau so etwas gemeint. Vielen Dank!
Juho
Danke für all die Infos! Wirklich schön gestaltete Antwort, die mein Verständnis verdeutlicht.
GManNickG
1

Weitere Informationen zur TLBO-Methode (Teaching-Learning-based Optimization) und ihrem Code finden Sie in folgendem Dokument:

Ein elitärer, auf Lehren und Lernen basierender Optimierungsalgorithmus zur Lösung komplexer eingeschränkter Optimierungsprobleme von R. Venkata Rao und V. Patel; Internationales Journal of Industrial Engineering Computations 3 (4): 535–560 (2012)

Für zusätzliche Lektüre:

Waghmare
quelle
1
Willkommen bei cs.SE und vielen Dank für Ihre Antwort! Beachten Sie, dass Sie Markdown verwenden können, um Ihre Beiträge zu formatieren. Ich schlage vor, Sie überprüfen meine Bearbeitung. In Bezug auf den Inhalt denke ich nicht, dass dies dem OP hilft, der zu wissen scheint, wie er sein Problem modellieren soll , und nicht Details zu einer bestimmten Technik. Außerdem arbeitet nur dieser eine Typ an TLBO?
Raphael