1. Prima di iniziare
L'incredibile rivoluzione di AlphaGo e AlphaStar ha dimostrato il potenziale dell'utilizzo del machine learning per creare agenti di gioco a livello di supereroi. Creare un piccolo gioco basato sul machine learning è un esercizio divertente per acquisire le abilità necessarie per creare potenti agenti del gioco.
In questo codelab, imparerai a costruire un gioco da tavolo utilizzando:
- Agente TensorFlow per addestrare un agente di gioco con l'apprendimento per rinforzo
- Pubblicazione di TensorFlow per la pubblicazione del modello
- Flutter per creare un'app di gioco da tavolo multipiattaforma
Prerequisiti
- Conoscenza di base dello sviluppo Flutter con Dart
- Conoscenza di base del machine learning con TensorFlow, ad esempio addestramento e deployment
- Conoscenza di base di Python, terminali e Docker
Obiettivi didattici
- Addestramento di un agente NPC (Non-Player Character Character) utilizzando gli agenti TensorFlow
- Come pubblicare il modello addestrato utilizzando TensorFlow Serving
- Come creare un gioco da tavolo Flutter multipiattaforma
Che cosa ti serve
- SDK Flutter
- Configurazione di Flutter per Android e iOS
- Configurazione di Flutter su computer
- Configurazione di Flutter sul web
- Configurazione di Visual Studio Code (VS Code) per Flutter e Dart
- Docker
- Picchi
- Python 3.7 o versioni successive
2. The Plane Strike Game
Il gioco creato in questo codelab si chiama "Plane Strike", un piccolo gioco da tavolo a 2 giocatori che ricorda il gioco da tavolo "Battleship". Le regole sono molto semplici:
- Il giocatore umano gioca contro un agente NPC addestrato dal machine learning. Il giocatore può iniziare il gioco toccando qualsiasi cella nel tabellone dell'agente.
- All'inizio del gioco, il giocatore umano e l'agente hanno ciascuno un "aereo" (8 celle verdi che formano un "piano", come puoi vedere nel tabellone di un giocatore umano nell'animazione seguente) sulle relative schede; questi "aerei" vengono posizionati in modo casuale, sono visibili solo ai proprietari del tabellone di gioco e nascosti ai loro avversari.
- Il giocatore umano e l'agente si alternano a colpire una cella del tabellone a vicenda. Il giocatore umano può toccare qualsiasi cella nella bacheca dell'agente, mentre l'agente farà automaticamente la scelta in base alla previsione di un modello di machine learning. La cella che hai tentato di eseguire diventa rossa se si tratta di un "piano" cella ("hit"), altrimenti diventa giallo ("miss").
- Vince la partita chi raggiunge 8 globuli rossi per primo; il gioco viene riavviato con nuovi tabelloni.
Ecco un esempio di gameplay:
3. Configura l'ambiente di sviluppo di Flutter
Per lo sviluppo di Flutter, hai bisogno di due parti di software per completare questo codelab: l'SDK Flutter e un editor.
Puoi eseguire il codelab utilizzando uno di questi dispositivi:
- Il simulatore iOS (richiede l'installazione degli strumenti Xcode).
- L'emulatore Android (richiede la configurazione in Android Studio).
- Un browser (per il debug è richiesto Chrome).
- Come applicazione desktop Windows, Linux o macOS. Devi svilupparle sulla piattaforma in cui prevedi di eseguire il deployment. Quindi, se vuoi sviluppare un'app desktop per Windows, devi sviluppare su Windows per accedere alla catena di build appropriata. Alcuni requisiti specifici del sistema operativo sono descritti in dettaglio all'indirizzo docs.flutter.dev/desktop.
4. Configurazione
Per scaricare il codice per questo codelab:
- Vai al repository GitHub per questo codelab.
- Fai clic su Codice > Scarica il file ZIP per scaricare tutto il codice per questo codelab.
- Decomprimi il file ZIP scaricato per aprire una cartella principale
codelabs-main
con tutte le risorse di cui hai bisogno.
Per questo codelab, ti servono solo i file nella sottodirectory tfagents-flutter/
del repository, che contiene più cartelle:
- Le cartelle da
step0
astep6
contengono il codice iniziale che creerai per ogni passaggio in questo codelab. - La cartella
finished
contiene il codice completato per l'app di esempio completata. - Ogni cartella contiene una sottocartella
backbend
, che include il codice backend, e una sottocartellafrontend
, che include il codice frontend Flutter
5. Scarica le dipendenze per il progetto
Backend
Apri il terminale e vai alla sottocartella tfagents-flutter
. Esegui questo comando:
pip install -r requirements.txt
Frontend
- In VS Code, fai clic su File > Apri cartella, quindi seleziona la cartella
step0
dal codice sorgente scaricato in precedenza. - Apri il file
step0/frontend/lib/main.dart
. Se viene visualizzata una finestra di dialogo VS Code che ti chiede di scaricare i pacchetti richiesti per l'app iniziale, fai clic su Ottieni pacchetti. - Se non vedi questa finestra di dialogo, apri il terminale ed esegui il comando
flutter pub get
nella cartellastep0/frontend
.
6. Passaggio 0: esegui l'app iniziale
- Apri il file
step0/frontend/lib/main.dart
in VS Code, assicurati che l'emulatore Android o il simulatore iOS sia configurato correttamente e appaia nella barra di stato.
Ad esempio, ecco cosa vedi quando usi Pixel 5 con l'emulatore Android:
Ecco cosa vedi quando usi iPhone 13 con il Simulatore di iOS:
- Fai clic su Avvia debug.
Esegui ed esplora l'app
L'app dovrebbe avviarsi sull'emulatore Android o sul simulatore iOS. L'interfaccia utente è piuttosto semplice. Ci sono due tabelloni di gioco; un giocatore può toccare qualsiasi cella del tabellone dell'agente in alto per colpire. Addestrerai un agente intelligente a prevedere automaticamente dove colpire in base al tabellone di un giocatore umano.
Dietro le quinte, l'app Flutter invierà la bacheca corrente del giocatore umano al backend, che esegue un modello di apprendimento per rinforzo e restituisce la posizione prevista della cella per colpire dopo. Il frontend mostrerà il risultato nella UI dopo aver ricevuto la risposta.
Se ora fai clic su una cella nella bacheca dell'agente, non succede nulla perché l'app non può ancora comunicare con il backend.
7. Passaggio 1: crea un ambiente Python per gli agenti TensorFlow
L'obiettivo principale di questo codelab è progettare un agente che impari interagendo con un ambiente. Il gioco Plane Strike è relativamente semplice ed è possibile creare regole per l'agente NPC, ma puoi usare l'apprendimento per rinforzo per formare un agente in modo da apprendere le abilità e poter creare facilmente agenti per altri giochi in futuro.
Nell'impostazione standard di apprendimento per rinforzo (RL), l'agente riceve un'osservazione in ogni passo temporale e sceglie un'azione. L'azione viene applicata all'ambiente e quest'ultimo restituisce un premio e una nuova osservazione. L'agente addestra una norma per scegliere le azioni che consentono di massimizzare la somma dei premi, nota anche come restituzione. Giocando molte volte, l'agente è in grado di apprendere gli schemi e di affinare le sue abilità per padroneggiare il gioco. Per formulare il gioco Plane Strike come un problema di RL, pensa allo stato del tabellone come a un'osservazione, alla posizione dello sciopero come all'azione e all'indicatore colpo/mancante come ricompensa.
Per addestrare l'agente NPC, puoi usare gli agenti TensorFlow, una libreria di apprendimento per rinforzo affidabile, scalabile e facile da usare per TensorFlow.
TF Agents è ottimo per il reinforcement learning, perché include un ampio set di codelab, esempi e una documentazione completa per aiutarti a iniziare. È possibile utilizzare agenti TF per risolvere problemi RL realistici e complessi con scalabilità e sviluppare rapidamente nuovi algoritmi RL. Puoi passare facilmente tra agenti e algoritmi diversi per la sperimentazione. Inoltre, è ben testato e facile da configurare.
In OpenAI Gym sono implementati molti ambienti di gioco predefiniti (ad es. Atari), Mujuco e così via, che gli agenti TF possono sfruttare facilmente. Tuttavia, poiché il gioco Plane Strike è un gioco completamente personalizzato, devi prima implementare un nuovo ambiente da zero.
Per implementare un ambiente Python con agenti TF, devi implementare i seguenti metodi:
class YourGameEnv(py_environment.PyEnvironment): def __init__(self): """Initialize environment.""" def action_spec(self): """Return action_spec.""" def observation_spec(self): """Return observation_spec.""" def _reset(self): """Return initial_time_step.""" def _step(self, action): """Apply action and return new time_step."""
La più importante è la funzione _step()
, che esegue un'azione e restituisce un nuovo oggetto time_step
. Nel caso del gioco Plane Strike, hai un tabellone; quando entra in gioco un nuovo avvertimento, in base alle condizioni del tabellone di gioco, l'ambiente scopre:
- Come dovrebbe apparire successivamente il tabellone di gioco (la cella deve cambiare di colore in rosso o giallo, data la posizione nascosta del piano?)
- Quale premio dovrebbe ricevere il giocatore per quella posizione (ricompensa con colpi o penalità per mancare?)
- Il gioco deve terminare (qualcuno ha vinto?)
- Aggiungi il seguente codice alla funzione
_step()
al file_planestrike_py_environment.py
:
if self._hit_count == self._plane_size: self._episode_ended = True return self.reset() if self._strike_count + 1 == self._max_steps: self.reset() return ts.termination( np.array(self._visible_board, dtype=np.float32), UNFINISHED_GAME_REWARD ) self._strike_count += 1 action_x = action // self._board_size action_y = action % self._board_size # Hit if self._hidden_board[action_x][action_y] == HIDDEN_BOARD_CELL_OCCUPIED: # Non-repeat move if self._visible_board[action_x][action_y] == VISIBLE_BOARD_CELL_UNTRIED: self._hit_count += 1 self._visible_board[action_x][action_y] = VISIBLE_BOARD_CELL_HIT # Successful strike if self._hit_count == self._plane_size: # Game finished self._episode_ended = True return ts.termination( np.array(self._visible_board, dtype=np.float32), FINISHED_GAME_REWARD, ) else: self._episode_ended = False return ts.transition( np.array(self._visible_board, dtype=np.float32), HIT_REWARD, self._discount, ) # Repeat strike else: self._episode_ended = False return ts.transition( np.array(self._visible_board, dtype=np.float32), REPEAT_STRIKE_REWARD, self._discount, ) # Miss else: # Unsuccessful strike self._episode_ended = False self._visible_board[action_x][action_y] = VISIBLE_BOARD_CELL_MISS return ts.transition( np.array(self._visible_board, dtype=np.float32), MISS_REWARD, self._discount,
8. Passaggio 2: addestra l'agente di gioco con gli agenti TensorFlow
Con l'ambiente di TF Agents attivo, puoi addestrare l'agente del gioco. Per questo codelab, utilizzerai un agente REINFORCE. REINFORCE è un algoritmo a gradiente di criteri in RL. La sua idea di base è quella di regolare i parametri della rete neurale dei criteri in base agli indicatori di ricompensa raccolti durante il gameplay, in modo che la rete delle norme possa massimizzare il ritorno nelle riproduzioni future.
- Innanzitutto, devi creare un'istanza per gli ambienti di addestramento e valutazione. Aggiungi questo codice alla funzione
train_agent()
nel filestep2/backend/training.py
:
train_py_env = planestrike_py_environment.PlaneStrikePyEnvironment( board_size=BOARD_SIZE, discount=DISCOUNT, max_steps=BOARD_SIZE**2 ) eval_py_env = planestrike_py_environment.PlaneStrikePyEnvironment( board_size=BOARD_SIZE, discount=DISCOUNT, max_steps=BOARD_SIZE**2 ) train_env = tf_py_environment.TFPyEnvironment(train_py_env) eval_env = tf_py_environment.TFPyEnvironment(eval_py_env)
- Poi devi creare un agente di apprendimento per rinforzo che verrà addestrato. In questo codelab, utilizzerai l'agente REINFORCE, un agente basato su criteri. Aggiungi questo codice proprio sotto il codice riportato sopra:
actor_net = tfa.networks.Sequential( [ tfa.keras_layers.InnerReshape([BOARD_SIZE, BOARD_SIZE], [BOARD_SIZE**2]), tf.keras.layers.Dense(FC_LAYER_PARAMS, activation="relu"), tf.keras.layers.Dense(BOARD_SIZE**2), tf.keras.layers.Lambda(lambda t: tfp.distributions.Categorical(logits=t)), ], input_spec=train_py_env.observation_spec(), ) optimizer = tf.keras.optimizers.Adam(learning_rate=LEARNING_RATE) train_step_counter = tf.Variable(0) tf_agent = reinforce_agent.ReinforceAgent( train_env.time_step_spec(), train_env.action_spec(), actor_network=actor_net, optimizer=optimizer, normalize_returns=True, train_step_counter=train_step_counter, )
- Infine, devi addestrare l'agente nel loop di addestramento. Nel loop, per prima cosa raccogli alcune puntate delle partite di gioco in un buffer, quindi addestri l'agente con i dati presenti nel buffer. Aggiungi questo codice alla funzione
train_agent()
nel filestep2/backend/training.py
:
# Collect a few episodes using collect_policy and save to the replay buffer. collect_episode( train_py_env, collect_policy, COLLECT_EPISODES_PER_ITERATION, replay_buffer_observer, ) # Use data from the buffer and update the agent's network. iterator = iter(replay_buffer.as_dataset(sample_batch_size=1)) trajectories, _ = next(iterator) tf_agent.train(experience=trajectories) replay_buffer.clear()
- Ora puoi iniziare la formazione. Nel terminale, apri la cartella
step2/backend
sul computer ed esegui:
python training.py
Per completare l'addestramento, sono necessarie 8-12 ore, a seconda delle configurazioni hardware (non è necessario completare l'intero addestramento in autonomia poiché in step3
viene fornito un modello preaddestrato). Nel frattempo, puoi monitorare l'avanzamento con TensorBoard. Apri un nuovo terminale, vai alla cartella step2/backend
sul tuo computer ed esegui:
tensorboard --logdir tf_agents_log/
tf_agents_log
è la cartella che contiene il log di addestramento. Di seguito è riportato un esempio di esecuzione dell'addestramento:
Puoi notare che la durata media delle puntate diminuisce e il ritorno medio aumenta con il procedere dell'addestramento. Intuitivamente puoi capire che se l'agente è più intelligente e fa previsioni migliori, la durata del gioco si riduce e l'agente raccoglie più premi. Questo ha senso perché l'agente vuole finire il gioco in meno passaggi per ridurre al minimo gli sconti intensivi sui premi nei passaggi successivi.
Al termine dell'addestramento, il modello addestrato viene esportato nella cartella policy_model
.
9. Passaggio 3: esegui il deployment del modello addestrato con la pubblicazione di TensorFlow
Ora che hai addestrato l'agente di gioco, puoi eseguirne il deployment con TensorFlow Serving.
- Nel terminale, vai alla cartella
step3/backend
sul tuo computer e avvia TensorFlow Serving con Docker:
docker run -t --rm -p 8501:8501 -p 8500:8500 -v "$(pwd)/backend/policy_model:/models/policy_model" -e MODEL_NAME=policy_model tensorflow/serving
Docker scarica automaticamente prima l'immagine di TensorFlow Serving, che richiede un minuto. Successivamente, verrà avviata la pubblicazione di TensorFlow. Il log dovrebbe essere simile al seguente snippet di codice:
2022-05-30 02:38:54.147771: I tensorflow_serving/model_servers/server.cc:89] Building single TensorFlow model file config: model_name: policy_model model_base_path: /models/policy_model 2022-05-30 02:38:54.148222: I tensorflow_serving/model_servers/server_core.cc:465] Adding/updating models. 2022-05-30 02:38:54.148273: I tensorflow_serving/model_servers/server_core.cc:591] (Re-)adding model: policy_model 2022-05-30 02:38:54.262684: I tensorflow_serving/core/basic_manager.cc:740] Successfully reserved resources to load servable {name: policy_model version: 123} 2022-05-30 02:38:54.262768: I tensorflow_serving/core/loader_harness.cc:66] Approving load for servable version {name: policy_model version: 123} 2022-05-30 02:38:54.262787: I tensorflow_serving/core/loader_harness.cc:74] Loading servable version {name: policy_model version: 123} 2022-05-30 02:38:54.265010: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:38] Reading SavedModel from: /models/policy_model/123 2022-05-30 02:38:54.277811: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:90] Reading meta graph with tags { serve } 2022-05-30 02:38:54.278116: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:132] Reading SavedModel debug info (if present) from: /models/policy_model/123 2022-05-30 02:38:54.280229: I external/org_tensorflow/tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations: AVX2 FMA To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags. 2022-05-30 02:38:54.332352: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:206] Restoring SavedModel bundle. 2022-05-30 02:38:54.337000: I external/org_tensorflow/tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 2193480000 Hz 2022-05-30 02:38:54.402803: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:190] Running initialization op on SavedModel bundle at path: /models/policy_model/123 2022-05-30 02:38:54.410707: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:277] SavedModel load for tags { serve }; Status: success: OK. Took 145695 microseconds. 2022-05-30 02:38:54.412726: I tensorflow_serving/servables/tensorflow/saved_model_warmup_util.cc:59] No warmup data file found at /models/policy_model/123/assets.extra/tf_serving_warmup_requests 2022-05-30 02:38:54.417277: I tensorflow_serving/core/loader_harness.cc:87] Successfully loaded servable version {name: policy_model version: 123} 2022-05-30 02:38:54.419846: I tensorflow_serving/model_servers/server_core.cc:486] Finished adding/updating models 2022-05-30 02:38:54.420066: I tensorflow_serving/model_servers/server.cc:367] Profiler service is enabled 2022-05-30 02:38:54.428339: I tensorflow_serving/model_servers/server.cc:393] Running gRPC ModelServer at 0.0.0.0:8500 ... [warn] getaddrinfo: address family for nodename not supported 2022-05-30 02:38:54.431620: I tensorflow_serving/model_servers/server.cc:414] Exporting HTTP/REST API at:localhost:8501 ... [evhttp_server.cc : 245] NET_LOG: Entering the event loop ...
Puoi inviare una richiesta di esempio all'endpoint per assicurarti che funzioni come previsto:
curl -d '{"signature_name":"action","instances":[{"0/discount":0.0,"0/observation":[[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]],"0/reward":0.0,"0/step_type":0}]}' -X POST http://localhost:8501/v1/models/policy_model:predict
L'endpoint restituirà una posizione prevista 45
, ovvero (5, 5) al centro della classifica (per i curiosi, puoi provare a capire perché il centro del tabellone è una buona ipotesi per la posizione del primo avvertimento).
{ "predictions": [45] }
È tutto. Hai creato un backend per prevedere la posizione del prossimo avvertimento per l'agente NPC.
10. Passaggio 4: crea l'app Flutter per Android e iOS
Il backend è pronto. Puoi iniziare a inviare richieste per recuperare le previsioni della posizione degli avvertimenti dall'app Flutter.
- Per prima cosa, devi definire una classe che aggrega gli input da inviare. Aggiungi questo codice al file
step4/frontend/lib/game_agent.dart
:
class Inputs { final List<double> _boardState; Inputs(this._boardState); Map<String, dynamic> toJson() { final Map<String, dynamic> data = <String, dynamic>{}; data['0/discount'] = [0.0]; data['0/observation'] = [_boardState]; data['0/reward'] = [0.0]; data['0/step_type'] = [0]; return data; } }
Ora puoi inviare la richiesta a TensorFlow Serving per eseguire previsioni.
- Aggiungi questo codice alla funzione
predict()
nel filestep4/frontend/lib/game_agent.dart
:
var flattenedBoardState = boardState.expand((i) => i).toList(); final response = await http.post( Uri.parse('http://$server:8501/v1/models/policy_model:predict'), body: jsonEncode(<String, dynamic>{ 'signature_name': 'action', 'instances': [Inputs(flattenedBoardState)] }), ); if (response.statusCode == 200) { var output = List<int>.from( jsonDecode(response.body)['predictions'] as List<dynamic>); return output[0]; } else { throw Exception('Error response'); }
Quando l'app riceve la risposta dal backend, aggiorni l'UI del gioco in modo che rifletta i relativi progressi.
- Aggiungi questo codice alla funzione
_gridItemTapped()
nel filestep4/frontend/lib/main.dart
:
int agentAction = await _policyGradientAgent.predict(_playerVisibleBoardState); _agentActionX = agentAction ~/ _boardSize; _agentActionY = agentAction % _boardSize; if (_playerHiddenBoardState[_agentActionX][_agentActionY] == hiddenBoardCellOccupied) { // Non-repeat move if (_playerVisibleBoardState[_agentActionX][_agentActionY] == visibleBoardCellUntried) { _agentHitCount++; } _playerVisibleBoardState[_agentActionX][_agentActionY] = visibleBoardCellHit; } else { _playerVisibleBoardState[_agentActionX][_agentActionY] = visibleBoardCellMiss; } setState(() {});
Eseguilo
- Fai clic su Avvia debug, quindi attendi il caricamento dell'app.
- Tocca una cella qualsiasi nella bacheca dell'agente per iniziare il gioco.
11. Passaggio 5: attiva l'app Flutter per le piattaforme desktop
Oltre ad Android e iOS, Flutter supporta anche piattaforme desktop come Linux, Mac e Windows.
Linux
- Assicurati che il dispositivo di destinazione sia impostato su nella barra di stato di VSCode.
- Fai clic su Avvia debug, quindi attendi il caricamento dell'app.
- Fai clic su una cella nella bacheca dell'agente per iniziare il gioco.
Mac
- Per Mac, devi configurare i diritti appropriati poiché l'app invierà richieste HTTP al backend. Per maggiori dettagli, consulta Diritti e sandbox dell'app.
Aggiungi questo codice rispettivamente a step4/frontend/macOS/Runner/DebugProfile.entitlements
e step4/frontend/macOS/Runner/Release.entitlements
:
<key>com.apple.security.network.client</key>
<true/>
- Assicurati che il dispositivo di destinazione sia impostato su nella barra di stato di VSCode.
- Fai clic su Avvia debug, quindi attendi il caricamento dell'app.
- Fai clic su una cella nella bacheca dell'agente per iniziare il gioco.
Windows
- Assicurati che il dispositivo di destinazione sia impostato su nella barra di stato di VSCode.
- Fai clic su Avvia debug, quindi attendi il caricamento dell'app.
- Fai clic su una cella nella bacheca dell'agente per iniziare il gioco.
12. Passaggio 6: attiva l'app Flutter per la piattaforma web
Un'altra cosa che puoi fare è aggiungere il supporto web all'app Flutter. Per impostazione predefinita, la piattaforma web è abilitata automaticamente per le app Flutter, quindi non devi fare altro che avviarla.
- Assicurati che il dispositivo di destinazione sia impostato su nella barra di stato di VSCode.
- Fai clic su Avvia debug, quindi attendi il caricamento dell'app nel browser Chrome.
- Fai clic su una cella nella bacheca dell'agente per iniziare il gioco.
13. Complimenti
Hai creato un'app per un gioco da tavolo con un agente basato sul machine learning per giocare contro il giocatore umano.