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.