Come classifico il mio problema di ottimizzazione dell'input dell'emulatore e con quale algoritmo dovrei affrontarlo?


10

A causa della natura della domanda, devo includere molte informazioni di base (perché la mia domanda è: come posso restringerla?) Detto questo, può essere sintetizzata (per quanto ne so)

Quali metodi esistono per trovare gli ottimum locali su spazi di ricerca combinatoria estremamente ampi?

sfondo

Nella comunità di superplay assistiti da strumenti cerchiamo di fornire input appositamente realizzati (non generati in tempo reale) a una console o emulatore di videogiochi al fine di ridurre al minimo alcuni costi (di solito il tempo necessario per il completamento). Il modo in cui questo è attualmente fatto è giocando il gioco fotogramma per fotogramma e specificando l'input per ciascun fotogramma, spesso ripetendo più volte parti della corsa (ad esempio, la corsa recentemente pubblicata per The Legend of Zelda: Ocarina of Time ha un totale di 198.590 tentativi).

Fare in modo che queste piste raggiungano il loro obiettivo di solito si riduce a due fattori principali: pianificazione del percorso e traversata. Il primo è molto più "creativo" del secondo.

La pianificazione del percorso sta determinando il modo in cui il giocatore deve navigare nel complesso per completare il gioco ed è spesso la parte più importante della corsa. Ciò è analogo alla scelta del metodo di ordinamento da utilizzare, ad esempio. Il miglior ordinamento di bolle al mondo semplicemente non supererà un ordinamento rapido su 1 milione di elementi.

Nel desiderio di perfezione, tuttavia, anche l'attraversamento (come viene eseguita la rotta) è un fattore enorme. Continuando l'analogia, ecco come viene implementato l'algoritmo di ordinamento. Alcuni percorsi non possono nemmeno essere eseguiti senza frame di input molto specifici. Questo è il processo più noioso di assistenza degli utensili ed è ciò che rende la produzione di una corsa completata richiede mesi o addirittura anni. Non è un processo difficile (per un essere umano) perché si tratta di provare diverse varianti della stessa idea fino a quando una non viene considerata la migliore, ma gli umani possono solo provare così tante variazioni nel loro raggio di attenzione. L'applicazione delle macchine a questo compito sembra corretta qui.

Il mio obiettivo ora è cercare di automatizzare il processo di attraversamento in generale per la console Nintendo 64 . Lo spazio di ricerca per questo problema è di gran lunga troppo grande per attaccare con un approccio a forza bruta. Un segmento n-frame di una corsa N64 ha 2 30n possibili input, il che significa che solo 30 frame di input (un secondo a 30FPS) ha 2 900 possibili input; sarebbe impossibile testare queste potenziali soluzioni, per non parlare di quelle per una corsa completa di due ore.

Tuttavia, non sono interessato a tentare (o meglio, non proverò nemmeno a tentare) l'ottimizzazione globale totale di una corsa completa. Piuttosto, dato un input iniziale , vorrei approssimare l' ottimale locale per un particolare segmento di una corsa (o gli ottimum n locali più vicini, per una sorta di ottimizzazione semi-globale) . Cioè, data una rotta e una traversata iniziale di quella rotta: cerca i vicini di quella traversata per minimizzare i costi, ma non degenerare nel provare tutti i casi che potrebbero risolvere il problema.

Il mio programma dovrebbe quindi assumere uno stato iniziale, un flusso di input, una funzione di valutazione e produrre l'ottimale locale minimizzando il risultato della valutazione.

Stato attuale

Attualmente mi occupo di tutto il quadro. Ciò include la valutazione di un flusso di input tramite la manipolazione dell'emulatore, l'installazione e lo smontaggio, la configurazione, ecc. E come sorta di segnaposto, l'ottimizzatore è un algoritmo genetico di base. Valuta semplicemente una popolazione di flussi di input, memorizza / sostituisce il vincitore e genera una nuova popolazione mutando il flusso del vincitore. Questo processo continua fino a quando non vengono soddisfatti alcuni criteri arbitrari, come il tempo o il numero di generazione.

Si noti che la parte più lenta di questo programma sarà, di gran lunga, la valutazione di un flusso di input . Questo perché si tratta di emulare il gioco per n frame. (Se avessi il tempo, scriverei il mio emulatore che forniva hook in questo tipo di cose, ma per ora sono rimasto con la sintesi dei messaggi e la modifica della memoria per un emulatore esistente da un altro processo.) Sul mio computer principale, che è abbastanza moderno, la valutazione di 200 fotogrammi richiede circa 14 secondi. Come tale, preferirei un algoritmo (data la scelta) che minimizza il numero di valutazioni delle funzioni.

Ho creato un sistema nel framework che gestisce contemporaneamente gli emulatori. In quanto tale, posso valutare più flussi contemporaneamente con una scala lineare delle prestazioni, ma praticamente il numero di emulatori in esecuzione può essere compreso tra 8 e 32 (e 32 lo sta spingendo davvero) prima che le prestazioni del sistema peggiorino. Ciò significa (data la scelta), un algoritmo che può eseguire l'elaborazione mentre è in corso una valutazione sarebbe estremamente vantaggioso, perché l'ottimizzatore può fare un duro lavoro mentre attende una valutazione.

Come test, la mia funzione di valutazione (per il gioco Banjo Kazooie ) era di sommare, per frame, la distanza dal giocatore a un punto obiettivo. Ciò significava che la soluzione ottimale era avvicinarsi a quel punto il più rapidamente possibile. Limitando la mutazione solo allo stick analogico, ci è voluto un giorno per ottenere una soluzione accettabile . (Questo era prima che implementassi la concorrenza.)

Dopo aver aggiunto la concorrenza, ho abilitato la mutazione della pressione del pulsante A e ho svolto la stessa funzione di valutazione in un'area che richiedeva il salto. Con 24 emulatori in esecuzione ci sono voluti circa 1 ora per raggiungere l'obiettivo da un flusso di input inizialmente vuoto, ma probabilmente avrebbe bisogno di funzionare per giorni per arrivare a qualcosa di vicino all'ottimale.

Problema

Il problema che sto affrontando è che non conosco abbastanza bene il campo dell'ottimizzazione matematica per sapere come modellare correttamente il mio problema di ottimizzazione ! Posso seguire approssimativamente l'idea concettuale di molti algoritmi come descritto su Wikipedia, ad esempio, ma non so come classificare il mio problema o selezionare l'algoritmo all'avanguardia per quella categoria.

Da quello che posso dire, ho un problema combinatorio con un quartiere estremamente vasto . Inoltre, la funzione di valutazione è estremamente discontinua, non ha gradiente e ha molti altopiani . Inoltre, non ci sono molti vincoli, anche se aggiungerò volentieri la possibilità di esprimerli se aiuta a risolvere il problema; Vorrei consentire di specificare che il pulsante Start non deve essere utilizzato, ad esempio, ma non è questo il caso generale.

Domanda

Quindi la mia domanda è: come posso modellarlo? Che tipo di problema di ottimizzazione sto cercando di risolvere? Quale algoritmo suppongo di usare? Non ho paura di leggere articoli di ricerca, quindi fatemi sapere cosa dovrei leggere!

Intuitivamente, un algoritmo genetico non potrebbe essere il migliore, perché non sembra davvero imparare. Ad esempio, se premere Start sembra peggiorare sempre la valutazione (perché mette in pausa il gioco), dovrebbe esserci una sorta di designer o cervello che impara: "premere Start in qualsiasi momento è inutile". Ma anche questo obiettivo non è così banale come sembra, perché a volte premere start è ottimale, come nel cosiddetto "pausa back-long-jump" in Super Mario 64 ! Qui il cervello dovrebbe imparare uno schema molto più complesso: "premere Start è inutile tranne quando il giocatore si trova in questo stato molto specifico e continuerà con una combinazione di pressioni di pulsanti ".

Sembra che dovrei (o la macchina potrebbe imparare a) rappresentare l'input in qualche modo più adatto alla modifica. L'input per fotogramma sembra troppo granulare, perché ciò che è veramente necessario sono le "azioni", che possono estendersi su più fotogrammi ... eppure molte scoperte vengono fatte fotogramma per fotogramma, quindi non posso escluderlo del tutto (il la suddetta pausa all'indietro-salto in lungo richiede precisione a livello di frame). Sembra anche che il fatto che l'input venga elaborato in serie dovrebbe essere qualcosa su cui poter capitalizzare, ma non sono sicuro di come.

Attualmente sto leggendo la ricerca (reattiva) di Tabu, la ricerca di vicinato su larga scala, l'ottimizzazione basata sull'insegnamento e l'apprendimento e l'ottimizzazione delle colonie di formiche.

Questo problema è semplicemente troppo difficile da affrontare con qualcosa di diverso dagli algoritmi genetici casuali? O è in realtà un problema banale che è stato risolto molto tempo fa? Grazie per la lettura e grazie in anticipo per eventuali risposte.


Il tuo post è piuttosto lungo, aiuterebbe i lettori se hai una breve sezione sull'argomento che afferma la domanda in termini chiari senza ulteriori informazioni di base.
Kaveh,

@Kaveh: capisco che è lunga, ma a causa della natura della domanda è piuttosto difficile restringere, dal momento che sto praticamente chiedendo come restringerlo. :(

Risposte:


6

Dalle informazioni fornite nella domanda, non riesco a vedere come applicare i metodi di ottimizzazione standard (di cui sono a conoscenza). I tuoi oggetti non sono così complicati (ne parleremo più avanti) ma la tua funzione target è cattiva: i suoi valori sono definiti da un sistema esterno fuori dal tuo controllo, è improbabile che abbia delle belle proprietà e così via. Pertanto, penso che l'uso di algoritmi genetici non sia un approccio impossibile e forse anche un buon approccio qui; spesso funzionano meglio di altri metodi se non si ha idea della struttura del problema. C'è molto da considerare

  • spazio oggetti,
  • funzione target e
  • parametri del tuo algoritmo genetico,

quindi permettimi di elaborare.

Quali sono i tuoi oggetti?

Hai già risposto: stai osservando una sequenza di azioni, ognuna delle quali occupa un frame. Penso che questo potrebbe essere a grana troppo fine; magari provare una sequenza di azioni, ognuna con una durata (in numero di fotogrammi). Ciò consentirebbe di avere mutazioni come "camminare un po 'più a lungo" per avere probabilità diverse rispetto a "inserire una pressa di A" in modo naturale. Prova ciò che funziona meglio; potrebbe essere necessario rivisitare questo articolo dopo aver pensato agli altri ingredienti.

Qual è la tua funzione target?

Questo è davvero cruciale. Cosa vuoi ottimizzare? È tempo di gol? Numero di azioni diverse? Il numero di stelle raccolte? Una combinazione di diversi fattori? Non appena ottieni più obiettivi, le cose diventano pelose - lì (di solito) non ci sono più optima!

Hai menzionato il tempo per raggiungere l'obiettivo. Probabilmente questa non è una buona funzione target. Perché? Poiché la maggior parte delle sequenze non raggiungerà nemmeno l'obiettivo, quindi raggiungeranno una linea di fondo per alcune costanti, creando un paesaggio di fitness come questo (schizzo concettuale in una dimensione):

inserisci qui la descrizione dell'immagine
[ fonte ]

00

11+distanza finale dall'obiettivo+11+tempo di gol

011

Quindi, come si misura la distanza? La distanza lineare può sembrare allettante ma ha i suoi problemi; di nuovo, potrebbero essere inviati segnali errati. Considera questo semplice scenario:

inserisci qui la descrizione dell'immagine
[ fonte ]

Ogni sequenza che inizia con un salto nel corridoio superiore migliora fino a raggiungere un punto appena sopra l'obiettivo, ma non può mai effettivamente raggiungere l'obiettivo! Ancora peggio, tra tutte le sequenze che non raggiungono l'obiettivo, quelle che salgono sono buone come quelle che scendono, quindi il GA non può rifiutare le sequenze chiaramente condannate. In altre parole, la distanza lineare crea optima locale particolarmente negativa che può intrappolare GA se ci sono vicoli ciechi nel livello.

Pertanto, ti suggerisco di sovrapporre una griglia al di sopra del tuo livello e collegare i punti vicini se il personaggio del gioco può passare dall'uno all'altro. Quindi calcola la distanza dal goal per la lunghezza del percorso più breve dal punto più vicino a dove la sequenza atterra il personaggio al punto più vicino al goal. Questo è facile da calcolare e camminare verso i deadends (optima locale) è immediatamente punito¹. Ovviamente devi accedere ai dati di livello, ma suppongo tu ne abbia.

Come funziona il tuo GA?

Ora possiamo arrivare all'attuale algoritmo genetico. Le considerazioni chiave sono la popolazione, la selezione, la riproduzione / mutazione e il criterio di arresto.

Popolazione

Quanto sarà grande la tua popolazione? Se è troppo piccolo, potrebbe non fornire la diversità necessaria per raggiungere una buona soluzione. Se è troppo grande, hai maggiori probabilità di portare in giro spazzatura inutile, rallentando il processo.

Come inizializzi la tua popolazione? Scegli sequenze di azioni casuali? In tal caso, di quale lunghezza? Hai un (piccolo) numero di soluzioni ragionevoli generate manualmente con cui seminare, forse tali da raggiungere l'obiettivo?

Selezione

K

Il concetto chiave qui è la pressione selettiva : quanto è difficile sopravvivere? Rendilo troppo piccolo e non elimini le soluzioni di merda. Rendilo troppo alto e rendi difficile il cambiamento (in particolare spostandoti tra le opzioni locali).

Riproduzione e mutazione

Dopo aver selezionato i sopravvissuti di un round, devi creare la generazione successiva da loro (i genitori sopravvivono e fanno parte della generazione successiva?). Esistono due strategie principali: mutazione e ricombinazione.

La mutazione è abbastanza chiara, sebbene i dettagli possano differire. Per ogni posizione nella sequenza di un individuo, mutala con una certa probabilità. Puoi farlo in modo indipendente per ogni posizione, oppure scegliere il numero di mutazioni in modo casuale oppure puoi eseguire mutazioni diverse con diverse probabilità (come inserire un nuovo elemento, rimuoverne uno, cambiarne uno, ...). La mutazione di solito riguarda piccoli cambiamenti.

La ricombinazione, che combina aspetti di due o più soluzioni con una nuova, è più complicata ma può consentire grandi passi, ovvero lasciare una "montagna di fitness" e spostarsi direttamente sulla pendenza di un'altra (che può essere più alta). Un'idea classica è il crossover ; Non so se questo abbia senso qui (mi sembra che scambiare il prefisso di una determinata sequenza con qualcos'altro molto probabilmente svaluterà il suffisso). Forse puoi usare la conoscenza del livello e delle posizioni del personaggio del gioco in diversi punti della sequenza per guidarlo, ovvero creare punti di crossover solo dove il personaggio si trova nella stessa posizione in entrambe le sequenze.

fine

NK1n


Come puoi vedere, tutte queste cose si intrecciano per influenzare le prestazioni effettive. Se si eseguono più popolazioni in parallelo, si può anche pensare a implementare la deriva genetica a causa della migrazione e / o catastrofi. C'è poca teoria per guidare la tua strada, quindi devi provare diverse configurazioni e guardare dove ti porta. Spero che ciò che funziona per un livello funzionerà anche per gli altri. Felice armeggiare!

Nota bene: dai un'occhiata a BoxCar 2D alla luce di quanto sopra. Fanno alcune cose abbastanza bene (altre, non così) e puoi avere un'intuizione su come i parametri di un GA possono influenzarne le prestazioni.


  1. In realtà, costruire una sequenza avidamente usando questa forma fisica, ovvero scegliere l'azione che minimizza la distanza dall'obiettivo rispetto a tutte le possibili azioni successive, può funzionare abbastanza bene. Provalo prima di usare GA!
  2. Ovviamente, come osservatore, ricordi sempre la migliore soluzione mai incontrata.

1
Bello! Due domande. Cosa ti dice che non ci sono (di solito) optima in MOO? I punti sono Pareto ottimali, cioè non puoi migliorare qualcosa senza sacrificare qualcos'altro. Dare valore a loro dipende quindi dal modellatore. Inoltre, non è la mutazione di piccole modifiche con piccola probabilità? Con ampie probabilità di mutazione, la ricerca tende a fare mosse casuali, non guidate, che di solito danneggiano le prestazioni. Penso che sia stato osservato che le probabilità di una piccola mutazione funzionano meglio.
Juho,

1/nn1

Okay vedo. Riguardo al terzo punto sì, intendevo qualcosa del genere. Grazie!
Juho,

Grazie per tutte le informazioni.! Risposta davvero ben strutturata che chiarisce la mia comprensione.
GManNickG

1

Per maggiori dettagli sul metodo di ottimizzazione basato sull'insegnamento-apprendimento (TLBO) e il suo codice, fare riferimento al seguente documento:

Un algoritmo di ottimizzazione basato sull'insegnamento e l'apprendimento elitario per risolvere complessi problemi di ottimizzazione vincolata di R. Venkata Rao e V. Patel; International Journal of Industrial Engineering Computations 3 (4): 535-560 (2012)

Per ulteriori letture:


1
Benvenuto in cs.SE e grazie per la tua risposta! Nota che puoi usare Markdown per formattare i tuoi post; Ti suggerisco di ispezionare la mia modifica. Per quanto riguarda il contenuto, non credo che ciò aiuti l'OP che sembra voler sapere come modellare il suo problema, non i dettagli su una particolare tecnica. Inoltre, c'è solo questo ragazzo che lavora su TLBO?
Raffaello
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.