Algoritmo di Reinforcement Learning SARSA in Python: Guida completa
Impara SARSA, un algoritmo on-policy di reinforcement learning. Comprendi la sua regola di aggiornamento, gli iperparametri e le differenze rispetto al Q-learning con esempi pratici in Python e la sua implementazione.
Il reinforcement learning (RL) è un potente paradigma di machine learning. Nell'RL, un software, di solito chiamato agente, impara a interagire con ambienti per risolvere problemi complessi tramite tentativi ed errori, il tutto senza intervento umano. Tra gli algoritmi di RL, SARSA si distingue per la sua efficiente natura on-policy.
SARSA sta per State-Action-Reward-State-Action, e rappresenta un ciclo che l'agente segue per risolvere problemi. Questo ciclo consente all'agente di imparare dai propri errori passati e, di tanto in tanto, di avventurarsi a provare cose nuove. Questo comportamento rende l'algoritmo particolarmente efficace in specifici tipi di problemi e lo differenzia dagli algoritmi off-policy come il Q-learning.
In questo tutorial imparerai come funziona SARSA e come implementarlo in Python. Per rendere concreta la comprensione, useremo per tutto il tempo il classico problema del Taxi. Parleremo anche dei vantaggi, dei limiti e delle applicazioni reali di SARSA.
Cos'è SARSA? La risposta breve
SARSA, acronimo di State-Action-Reward-State-Action, descrive una sequenza di eventi in un processo di apprendimento. È un metodo efficace affinché i programmi (agenti) prendano buone decisioni in vari scenari.
L'idea principale alla base di SARSA è il tentativo e l'errore. L'agente compie un'azione in una situazione, osserva il risultato e aggiusta la sua strategia in base all'esito (positivo o negativo). Questo processo si ripete molte volte, portando a miglioramenti nelle decisioni dell'agente nel tempo.
Ciò che rende SARSA speciale tra gli algoritmi RL è che impara dalle scelte effettivamente fatte dall'agente, anche quando sta provando cose nuove. Questo approccio è particolarmente utile quando il percorso di apprendimento è importante quanto il risultato finale.
È come un robot che impara ad andare in bici pedalando davvero, cadute comprese, invece di limitarsi a trovare il percorso più breve da A a B usando rotelle protettive.
Preparare l'ambiente per il tutorial
In questo tutorial useremo ampiamente Numpy e la libreria di reinforcement learning Gymnasium. Mentre Numpy ci aiuterà a scrivere l'algoritmo SARSA, Gymnasium ci fornirà ambienti integrati per testare il nostro lavoro.
Installiamoli in un nuovo ambiente virtuale con i seguenti comandi:
$ conda create -n sarsa python=3.9-y
$ conda activate sarsa
$ pip install"gymnasium[atari]" numpy matplotlib
$ pip install autorom[accept-rom-license]# Downloading Gym env data files
$ AutoROM --accept-license # Accepting the license for data files
$ pip install ipykernel # Install Jupyter kernel manager
$ ipython kernel install--user--name=sarsa # Add the new Conda env to JupyterOfferto da
Prima di continuare, ti incoraggio vivamente a leggere il nostroIntroduction to Reinforcement Learning. Copre le idee fondamentali dell'RL, come agenti, ambienti, il dilemma esplorazione contro sfruttamento e il Q-learning.
Spiegare l'ambiente Taxi-V3
Nel corso del tutorial useremo l'ambiente Taxi-v3, un classico problema di reinforcement learning fornito dalla libreria Gymnasium. Simula un tassista che naviga in un mondo a griglia 5x5 per prelevare e lasciare passeggeri.
Per caricare l'ambiente usiamo il metodo .make() di Gymnasium con una modalità di rendering rgb_array (per visualizzare l'ambiente più avanti):
import gymnasium as gym
env = gym.make('Taxi-v3', render_mode='rgb_array')Offerto da
L'ambiente è una griglia 5x5 con quattro posizioni designate: rosso (R), verde (G), giallo (Y) e blu (B). Il taxi parte da una casella casuale e deve prelevare un passeggero in una delle posizioni colorate e lasciarlo in un'altra.
Il taxi può muoversi a Nord, Sud, Est o Ovest e può anche tentare di prelevare o lasciare un passeggero.
Ecco come possiamo visualizzare lo stato iniziale dell'ambiente con Matplotlib:
import matplotlib.pyplot as plt
# Reset the environment to get an initial state# Each time we reset the environment, we get a new random state
initial_state, _ = env.reset()# Render the initial state
img = env.render()# Create a figure and display the environment
fig, ax = plt.subplots(figsize=(8,8))
ax.imshow(img)# Remove axis ticks
ax.set_xticks([])
ax.set_yticks([]);Offerto da
Per prima cosa, reimpostiamo l'ambiente per ottenere uno stato iniziale. Poi, mostriamo questo stato iniziale con la funzione render, ottenendo un array immagine Numpy. La funzione imshow() di Matplotlib prende questo array e produce una visualizzazione pulita senza tacche sugli assi.
Prenditi un momento per comprendere il layout del mondo a griglia Taxi-v3, osservando la posizione del taxi, le barriere, le posizioni dei passeggeri e la destinazione.
L'agente (il taxi) riceve +20 punti per aver lasciato con successo un passeggero. Un prelievo o rilascio illegale comporta una ricompensa di -10. Nel frattempo, ogni passo temporale produce -1 di ricompensa per incoraggiare il taxi a completare il compito rapidamente.
Un singolo episodio dell'ambiente Taxi-v3 termina in caso di rilascio riuscito o quando viene raggiunto il numero massimo di passi temporali.
# The number of states and actions
n_states = env.observation_space.n
n_actions = env.action_space.n
print(n_states)print(n_actions)Offerto da
Ci sono 500 stati nello spazio degli stati dell'ambiente. Ogni stato è rappresentato da:
Riga del taxi (0–4)
Colonna del taxi (0–4)
Posizione del passeggero (0–3 per R, G, Y, B, oppure 4 se dentro il taxi)
Posizione di destinazione (0–3 per R, G, Y, B)
Stati totali = 5 (righe)×5 (colonne)×5 (posizioni passeggero)×4 (posizioni destinazione)=500.
I codici delle azioni sono:
0: Muovi a Sud
1: Muovi a Nord
2: Muovi a Est
3: Muovi a Ovest
4: Preleva passeggero
5: Lascia passeggero
L'obiettivo è che l'agente impari una policy ottimale per massimizzare la ricompensa totale prelevando e lasciando passeggeri in modo efficiente.
Ciclo di interazione SARSA
Prima di scrivere l'algoritmo completo, vediamo come funziona il ciclo State-Action-Reward-State-Action usando il nostro ambiente.
Per prima cosa, definiamo quante epoche vogliamo che l'interazione continui. Un singolo episodio rappresenta un'esecuzione completa del compito del taxi, dallo stato iniziale fino a quando non lascia con successo il passeggero o raggiunge il numero massimo di passi temporali. Ogni episodio permette all'agente di imparare dalle proprie esperienze e migliorare la strategia nel tempo.
for episode inrange(n_episodes):
state, _ = env.reset()
done =False
total_reward =0
steps =0whilenot done:# Choose a random action
action = env.action_space.sample()# Take the action and observe the result
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated
# Update total reward and step count
total_reward += reward
steps +=1# Move to the next state
state = next_state
if episode %1000==0:print(f"Episode {episode}, Total Reward: {total_reward}, Steps: {steps}")Offerto da
Episode 0, Total Reward: -812, Steps: 200
Episode 1000, Total Reward: -830, Steps: 200
Episode 2000, Total Reward: -902, Steps: 200
Episode 3000, Total Reward: -522, Steps: 129
Episode 4000, Total Reward: -767, Steps: 200Offerto da
Il codice sopra mostra il ciclo di interazione SARSA di base senza implementare alcun algoritmo di apprendimento:
Resetta l'ambiente all'inizio di ogni episodio — (S), env.reset().
Esegui un'azione casuale — (A), env.step(action). Questa versione usa azioni casuali per mostrare il ciclo di interazione.
Ricevi la ricompensa (R) e lo stato successivo (S_1), next_state, reward, ... = env.take(action).
Esegui l'azione (A_1) nel nuovo stato.
Nel ciclo di interazione sopra, il taxi continua a muoversi nell'ambiente in tutte le direzioni ed esegue azioni casuali finché i passi temporali non finiscono.
Animare il ciclo di interazione SARSA
Prima di proseguire, implementiamo una funzionalità per animare i nostri cicli di interazione. Questo ci permette di vedere visivamente il taxi che interagisce con l'ambiente.
Creare l'animazione sarà un processo semplice:
A ogni passo temporale, catturiamo lo stato dell'ambiente in un array immagine usando la funzione env.render().
Raccogliamo gli array immagine in una variabile separata.
Usando una libreria chiamata moviepy, concateneremo tutti gli array immagine per creare una singola GIF.
Modifichiamo il codice:
env = gym.make("Taxi-v3", render_mode="rgb_array")
n_episodes =1
frames =[]# for animationfor episode inrange(n_episodes):# Reset the environment
state, _ = env.reset()# Capture the state as an image
img = env.render()
frames.append(img)
done =False
total_reward =0
steps =0whilenot done:# Choose a random action
action = env.action_space.sample()# Take the action and observe the result
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated
# Capture the next state as an image
img = env.render()
frames.append(img)# Update total reward and step count
total_reward += reward
steps +=1# Move to the next state
state = next_stateOfferto da
In questa versione impostiamo il numero di episodi a uno solo, poiché eseguire il rendering degli stati dell'ambiente come immagini richiede troppo tempo. Creiamo anche una lista vuota per archiviare i nostri array immagine. Poi, il ciclo di interazione continua come prima. L'unica differenza sono le due righe extra di codice in cui renderizziamo l'ambiente come immagine e lo aggiungiamo a frames.
Ora dovremmo avere 201 immagini dentro frames (un'immagine in più per lo stato aggiuntivo):
Trasformiamo queste immagini in una GIF usando la libreria moviepy:
from moviepy.editor import ImageSequenceClip # pip install moviepydefcreate_gif(frames:list, filename, fps=5):"""
Creates a GIF animation from a list of RGBA NumPy arrays.
Args:
frames: A list of RGBA NumPy arrays representing the animation frames.
filename: The output filename for the GIF animation.
fps: The frames per second of the animation (default: 10).
"""
clip = ImageSequenceClip(frames, fps=fps)
clip.write_gif(filename, fps=fps)# Example usage
create_gif(frames,"animation.gif", fps=25)# saves the GIF locallyOfferto da
La funzione create_gif() prende una lista di frame e li converte in una GIF usando la classe ImageSequenceClip di moviepy. Un parametro importante è il numero di frame per secondo, fps, che controlla la durata della GIF. Più frame per secondo, più corta sarà la GIF.
Alla fine, convertiamo i frame di un singolo episodio in una GIF a 25 FPS. Ecco il risultato:
Come puoi vedere, il taxi non ha idea di quello che sta facendo e non si è nemmeno avvicinato al passeggero. Diamogli un po' di cervello aggiungendo SARSA al suo sistema di navigazione.
Implementare SARSA in Python passo dopo passo
Costruiremo il codice di SARSA da zero, così ricorderai chiaramente ogni passaggio.
1. Impostare l'ambiente Gymnasium:
import gymnasium as gym
import numpy as np
import matplotlib.pyplot as plt
# Create the Taxi environment
env = gym.make("Taxi-v3", render_mode="rgb_array")Offerto da
In questo passaggio introduciamo una nuova struttura dati — la Q-table. Ha dimensioni (numero di stati) x (numero di azioni). La tabella apparirebbe così al nostro agente, il tassista:
Poi, quando l'agente inizia a interagire con l'ambiente guidato da SARSA, aggiornerà la Q-table con i Q-value. I Q-value sono punteggi che dicono all'agente qual è l'azione migliore da intraprendere, dato lo stato corrente.
3. Definire gli iperparametri di SARSA
Dopo aver inizializzato la Q-table, impostiamo gli iperparametri di SARSA su valori comuni (ne parleremo più avanti):
4. Definire l'archiviazione per le metriche di performance
Poi, definiamo due liste per memorizzare le metriche di performance: la ricompensa totale e il numero di passi temporali per ogni episodio. L'obiettivo dell'agente è raccogliere quanta più ricompensa possibile nel minor tempo possibile:
# Lists to store performance metrics
episode_rewards =[]
episode_lengths =[]Offerto da
5. Strategia epsilon-greedy per scegliere l'azione
Nella sezione precedente, il nostro agente era senza guida — stava eseguendo azioni casuali. Vogliamo cambiare questo dando al conducente una strategia Epsilon-Greedy:
defepsilon_greedy(state, epsilon):if np.random.random()< epsilon:# Take random action - explorereturn env.action_space.sample()else:# Take action with the highest Q-value - exploitreturn np.argmax(Q_table[state])Offerto da
Questa strategia serve a controllare l'equilibrio cruciale tra esplorazione e sfruttamento. Con probabilità epsilon, l'agente esplora l'ambiente compiendo un'azione casuale, mentre con probabilità 1-epsilon sfrutta la conoscenza attuale scegliendo l'azione con il Q-value più alto dalla Q-table. Questo approccio permette all'agente di scoprire nuove strategie potenzialmente migliori e, allo stesso tempo, di usare ciò che ha già imparato.
6. Scrivere il ciclo di training SARSA
Infine, scriviamo il ciclo di training di SARSA. L'inizio del ciclo ci è già familiare. L'unica differenza è che usiamo la funzione epsilon_greedy() per dirci quale azione intraprendere nello stato corrente:
Poi avviamo il ciclo while che ci consente di eseguire il ciclo di interazione fino a raggiungere uno stato di terminazione:
# SARSA training loopfor episode inrange(n_episodes):...whilenot done:
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated
next_action = epsilon_greedy(next_state, epsilon)Offerto da
All'interno del ciclo while, eseguiamo l'azione restituita da epsilon_greedy() e riceviamo lo stato successivo, la ricompensa per la nostra azione e un valore booleano che indica se l'episodio è terminato o meno.
Poi arriviamo alla parte cruciale — la regola di aggiornamento di SARSA:
Impareremo l'intuizione dietro questa formula nella sezione successiva. Per ora, considera la formula come una sorta di magia matematica che aggiorna i Q-value della nostra Q-table secondo le regole di SARSA.
Una volta effettuato l'aggiornamento di Q, cambiamo le variabili state e action allo stato e all'azione risultanti, aggiungiamo la ricompensa ricevuta alla ricompensa totale dell'episodio e aumentiamo il numero di passi temporali.
# SARSA training loopfor episode inrange(n_episodes):...whilenot done:...
state = next_state
action = next_action
total_reward += reward
steps +=1Offerto da
Il ciclo while continua fino a raggiungere il numero massimo di passi (200 per l'ambiente taxi) o quando il taxi lascia con successo il passeggero nella posizione corretta.
Una volta che il ciclo termina, registriamo la ricompensa totale dell'episodio e la durata dell'episodio. Stampiamo anche la ricompensa media e la durata media ogni 1000 episodi:
Come puoi vedere dall'output, la ricompensa media e il numero di passi per episodio sono diminuiti drasticamente man mano che abbiamo eseguito sempre più episodi.
Possiamo osservarlo visivamente tracciando gli array episode_rewards e episode_lengths:
# Plot the learning curve
plt.figure(figsize=(12,5))
plt.subplot(1,2,1)
plt.plot(episode_rewards)
plt.title("Episode Rewards")
plt.xlabel("Episode")
plt.ylabel("Total Reward")
plt.subplot(1,2,2)
plt.plot(episode_lengths)
plt.title("Episode Lengths")
plt.xlabel("Episode")
plt.ylabel("Number of Steps")
plt.tight_layout()
plt.show()Offerto da
Il grafico a sinistra mostra la ricompensa totale ottenuta in ciascun episodio. Possiamo osservare che:
Inizialmente, le ricompense sono basse e molto variabili, a indicare che l'agente sta esplorando e imparando.
Col tempo, c'è una tendenza al rialzo delle ricompense, il che suggerisce che l'agente sta migliorando la sua policy.
Verso la fine, le ricompense sembrano stabilizzarsi su un livello più alto, a indicare che l'agente ha imparato una policy ragionevolmente buona.
Il grafico a destra mostra il numero di passi compiuti in ogni episodio. Possiamo vedere che:
All'inizio, gli episodi tendono a essere più lunghi, poiché l'agente compie azioni subottimali.
Con il progredire dell'apprendimento, c'è una tendenza generale alla diminuzione della durata degli episodi.
Le durate degli episodi alla fine si stabilizzano, suggerendo che l'agente ha imparato a completare il compito in modo più efficiente.
Organizzare il codice in funzioni
In poco tempo, abbiamo coperto molti argomenti. Facciamo ora un passo indietro e organizziamo tutto ciò che abbiamo scritto. Creeremo funzioni per ciascuno dei passaggi eseguiti per implementare SARSA.
Per prima cosa, una funzione per creare un ambiente:
import gymnasium as gym
import numpy as np
import matplotlib.pyplot as plt
from moviepy.editor import ImageSequenceClip
defcreate_environment(env_name="Taxi-v3", render_mode="rgb_array"):"""Create and return a Gymnasium environment."""return gym.make(env_name, render_mode=render_mode)Offerto da
Poi, una funzione per inizializzare una Q-table dato un ambiente:
definitialize_q_table(env):"""Initialize and return a Q-table for the given environment."""
n_states = env.observation_space.n
n_actions = env.action_space.n
return np.zeros((n_states, n_actions))Offerto da
La strategia epsilon-greedy che accetta un ambiente, una Q-table, lo stato corrente ed epsilon:
La regola di aggiornamento di SARSA, che richiede una Q-table, lo stato corrente, l'azione intrapresa in quello stato, la ricompensa per l'azione, lo stato e l'azione successivi:
Infine, una grande funzione per addestrare l'agente con SARSA che richiede un ambiente, il numero di episodi da eseguire e i parametri alpha, gamma ed epsilon:
Inoltre, una funzione per tracciare le metriche di performance:
defplot_learning_curve(episode_rewards, episode_lengths):"""Plot the learning curve."""
plt.figure(figsize=(12,5))
plt.subplot(1,2,1)
plt.plot(episode_rewards)
plt.title("Episode Rewards")
plt.xlabel("Episode")
plt.ylabel("Total Reward")
plt.subplot(1,2,2)
plt.plot(episode_lengths)
plt.title("Episode Lengths")
plt.xlabel("Episode")
plt.ylabel("Number of Steps")
plt.tight_layout()
plt.show()Offerto da
La nostra precedente funzione create_gif():
defcreate_gif(frames, filename, fps=5):"""Creates a GIF animation from a list of frames."""
clip = ImageSequenceClip(frames, fps=fps)
clip.write_gif(filename, fps=fps)Offerto da
E un'altra funzione per eseguire un singolo episodio con rendering usando una Q-table completa e addestrata (per scopi di animazione):
defrun_episode(env, Q_table, epsilon=0):"""Run a single episode using the learned Q-table."""
state, _ = env.reset()
done =False
total_reward =0
frames =[env.render()]whilenot done:
action = epsilon_greedy(env, Q_table, state, epsilon)
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated
frames.append(env.render())
total_reward += reward
state = next_state
return frames, total_rewardOfferto da
L'intuizione dietro la regola di aggiornamento di SARSA
Al cuore di SARSA c'è la sua regola di aggiornamento, che controlla come l'agente impara dalle sue esperienze. Scomponiamo questa regola ed esploriamo l'intuizione che la guida:
Q(s, a) = Q(s, a) + α [R + γ Q(s', a') - Q(s, a)]
Comprendere i componenti
Q(s, a): Il Q-value corrente dell'eseguire l'azione ‘a’ nello stato ‘s’.
α (alpha): Il learning rate, controlla quanto velocemente viene incorporata la nuova informazione.
R: La ricompensa ricevuta dopo aver eseguito l'azione.
γ (gamma): Il fattore di sconto, determina l'importanza delle ricompense future.
Q(s’, a’): Il Q-value stimato della coppia stato-azione successiva.
Il processo di apprendimento
La regola di aggiornamento di SARSA mira a raffinare la comprensione dell'ambiente da parte dell'agente, aggiustando i Q-value in base alle nuove esperienze. Ecco come funziona:
L'agente esegue un'azione e osserva la ricompensa e lo stato successivo.
Calcola la differenza tra il suo Q-value corrente e una nuova stima basata sulla ricompensa osservata e sul valore della coppia stato-azione successiva.
Questa differenza, scalata dal learning rate, viene usata per aggiornare il Q-value.
Errore di differenza temporale
Il termine [R + γ * Q(s’, a’) — Q(s, a)] è noto come errore di differenza temporale (TD). Pensalo come una misura di sorpresa:
TD positivo: l'esito è stato migliore del previsto.
TD negativo: l'esito è stato peggiore del previsto.
TD zero: l'esito ha corrisposto perfettamente alla stima attuale.
Questo errore aiuta l'agente a perfezionare continuamente le sue stime, guidando il processo di apprendimento verso una policy ottimale.
Ruolo degli iperparametri
1. Learning rate (α):
Controlla la velocità di apprendimento.
α alto: apprendimento più veloce, ma potenzialmente instabile.
α basso: apprendimento più lento ma più stabile.
2. Fattore di sconto (γ):
Bilancia ricompense immediate e future.
γ vicino a 1: l'agente valorizza le ricompense future quasi quanto quelle immediate.
γ basso: l'agente si concentra di più sulle ricompense immediate.
3. Tasso di esplorazione (ε):
Pur non essendo nella regola di aggiornamento, è cruciale per la policy epsilon-greedy.
Bilancia esplorazione (provare nuove azioni) e sfruttamento (usare azioni già note buone).
ε alto: più esplorazione, potenzialmente scoprendo strategie migliori a costo di performance a breve termine.
La visione d'insieme
La regola di aggiornamento di SARSA permette all'agente di imparare dalle proprie esperienze aggiustando costantemente le stime dei valori stato-azione. A ogni interazione, l'agente diventa un po' più saggio riguardo al suo ambiente. Nel corso di molti episodi, questo processo porta allo sviluppo di una policy ottimale per navigare nell'ambiente.
Regolando gli iperparametri, possiamo controllare vari aspetti del processo di apprendimento, consentendo a SARSA di adattarsi a diversi tipi di problemi e ambienti. Questa flessibilità, combinata con la sua regola di aggiornamento intuitiva, rende SARSA un algoritmo di reinforcement learning potente e ampiamente usato.
SARSA vs. Q-learning: differenze chiave
Sebbene SARSA e Q-learning siano entrambi algoritmi di reinforcement learning ampiamente utilizzati, presentano alcune importanti differenze. E tali differenze sono fondamentali per capire quando e come usare ciascun algoritmo.
1. On-policy vs. off-policy
Un algoritmo on-policy, SARSA, impara il valore della policy che segue, includendo anche i passi compiuti durante l'esplorazione. Il Q-learning è off-policy; impara il valore della policy ottimale — anche quando non sta seguendo quella policy, continua comunque a imparare il valore della policy ottimale quando alla fine raggiunge la fine dell'episodio. Ecco perché abbiamo detto all'inizio che SARSA va usato quando il percorso di apprendimento è importante quanto l'esito stesso. Il Q-learning non si cura molto del percorso di apprendimento.
2. Regola di aggiornamento
SARSA: Q(s, a) = Q(s, a) + α * [R + γ * Q(s’, a’) — Q(s, a)]
Q-learning: Q(s, a) = Q(s, a) + α * [R + γ * max(Q(s’, a’)) — Q(s, a)]
La differenza chiave è che SARSA usa il Q-value della prossima azione effettivamente intrapresa (a’), mentre il Q-learning usa il Q-value massimo del prossimo stato (max(Q(s’, a’))).
3. Considerazione dell'esplorazione
Nel processo di aggiornamento dei Q-value, SARSA non ignora la policy di esplorazione ed è quindi più conservativo. Al contrario, il Q-learning assume sempre che l'agente intraprenderà l'azione ottimale in futuro ed è quindi più aggressivo.
4. Convergenza
Sebbene entrambi gli algoritmi raggiungano alla fine la policy ottimale, il Q-learning può spesso imparare più rapidamente, in particolare in ambienti deterministici.
5. Sicurezza
In ambienti in cui l'esplorazione può portare a esiti negativi, SARSA apprende generalmente policy più sicure perché tiene conto della policy effettivamente seguita.
Nel classico problema della “cliff walking”, SARSA in genere apprende un percorso più sicuro che resta lontano dal bordo della scogliera, mentre il Q-learning potrebbe apprendere un percorso più rischioso che cammina lungo il bordo.
6. Stabilità
In certi ambienti casuali, SARSA può essere più stabile perché tiene conto della prossima azione effettiva, anche quando è un'azione esplorativa subottimale.
7. Sensibilità agli iperparametri
Il Q-learning può essere più sensibile alla scelta del learning rate e del tasso di esplorazione, specialmente in ambienti con forte componente stocastica.
8. Applicazioni nel mondo reale
Quando si tratta di robotica o altri sistemi fisici in cui esplorare l'ambiente è costoso e vogliamo implementare policy più sicure, potremmo preferire l'algoritmo on-policy SARSA. Quando siamo nel mondo simulato, nei giochi o in altri ambienti in cui possiamo ottenere molti feedback e dove vogliamo trovare la policy ottimale in modo rapido e abbastanza sicuro, potremmo preferire il Q-learning.
Potresti quindi pensare che SARSA sia una sorta di approccio più sicuro, mentre il Q-learning è più veloce e spregiudicato. In pratica, spesso dipende dallo specifico problema. Entrambi gli algoritmi sono molto forti, ed è sempre utile conoscere le differenze tra loro.
Conclusione
In questa guida approfondita, abbiamo esplorato l'algoritmo SARSA, coprendo i suoi concetti chiave, i dettagli di implementazione e le applicazioni pratiche usando l'ambiente Taxi-v3.
I punti chiave includono:
La natura on-policy di SARSA e l'intuizione dietro la sua regola di aggiornamento
Implementazione in Python e tecniche di visualizzazione
Confronto con il Q-learning, evidenziando punti di forza e casi d'uso
La capacità di SARSA di apprendere policy più sicure lo rende adatto ad applicazioni reali in cui i costi di esplorazione sono elevati, anche se in alcuni scenari può convergere più lentamente rispetto ad algoritmi off-policy come il Q-learning.
Ricorda, la scelta tra SARSA e altri algoritmi dipende dallo specifico problema e ambiente. Sperimentazione e fine tuning sono cruciali per risultati ottimali.
Ti invitiamo a costruire su queste basi esplorando ambienti più complessi, implementando SARSA con approssimazione di funzione o approfondendo altri algoritmi di reinforcement learning.
Ecco alcune risorse correlate per aiutarti lungo il percorso:
What is the SARSA algorithm in reinforcement learning?⌄
SARSA (State-Action-Reward-State-Action) è un algoritmo on-policy di reinforcement learning che aggiorna i Q-value in base alle esperienze effettive dell'agente, comprese le azioni esplorative.
How does SARSA differ from Q-learning?⌄
SARSA è on-policy e aggiorna i Q-value usando la prossima azione effettivamente intrapresa, mentre il Q-learning è off-policy e usa il Q-value massimo del prossimo stato, portando a comportamenti di apprendimento diversi.
What are the key hyperparameters in SARSA, and how do they affect learning?⌄
Gli iperparametri chiave sono il learning rate (α), il fattore di sconto (γ) e il tasso di esplorazione (ε). Controllano rispettivamente la velocità di apprendimento, l'importanza delle ricompense future e l'equilibrio tra esplorazione e sfruttamento.
How can I implement the SARSA algorithm in Python?⌄
L'articolo fornisce una guida passo dopo passo per implementare SARSA in Python, compresa la configurazione dell'ambiente, l'inizializzazione della Q-table e la scrittura del ciclo di training.
What are the practical applications of SARSA in real-world scenarios?⌄
SARSA è adatto ad applicazioni in cui i costi di esplorazione sono alti, come la robotica e i sistemi fisici, poiché tende ad apprendere policy più sicure rispetto ad algoritmi off-policy come il Q-learning.
Sono un creator di contenuti sulla data science con oltre 2 anni di esperienza e uno dei più seguiti su Medium. Mi piace scrivere articoli dettagliati su AI e ML con un pizzico di sarcasmo, perché bisogna pur fare qualcosa per renderli un po’ meno noiosi. Ho pubblicato oltre 130 articoli e un corso su DataCamp, con un altro in arrivo. I miei contenuti sono stati visti da oltre 5 milioni di persone, 20.000 delle quali sono diventate follower sia su Medium che su LinkedIn.
Scopri i 15 migliori server MCP remoti che stanno trasformando lo sviluppo AI nel 2026. Scopri come migliorano automazione, ragionamento, sicurezza e velocità dei workflow.
Scopri il machine learning con Python e prova a diventare uno scienziato del machine learning. Scopri l'apprendimento supervisionato, non supervisionato e profondo.
Il reinforcement learning (RL) è un potente paradigma di machine learning. Nell'RL, un software, di solito chiamato agente, impara a interagire con ambienti per risolvere problemi complessi tramite tentativi ed errori, il tutto senza intervento umano. Tra gli algoritmi di RL, SARSA si distingue per la sua efficiente natura on-policy.
SARSA sta per State-Action-Reward-State-Action, e rappresenta un ciclo che l'agente segue per risolvere problemi. Questo ciclo consente all'agente di imparare dai propri errori passati e, di tanto in tanto, di avventurarsi a provare cose nuove. Questo comportamento rende l'algoritmo particolarmente efficace in specifici tipi di problemi e lo differenzia dagli algoritmi off-policy come il Q-learning.
In questo tutorial imparerai come funziona SARSA e come implementarlo in Python. Per rendere concreta la comprensione, useremo per tutto il tempo il classico problema del Taxi. Parleremo anche dei vantaggi, dei limiti e delle applicazioni reali di SARSA.
Cos'è SARSA? La risposta breve
SARSA, acronimo di State-Action-Reward-State-Action, descrive una sequenza di eventi in un processo di apprendimento. È un metodo efficace affinché i programmi (agenti) prendano buone decisioni in vari scenari.
L'idea principale alla base di SARSA è il tentativo e l'errore. L'agente compie un'azione in una situazione, osserva il risultato e aggiusta la sua strategia in base all'esito (positivo o negativo). Questo processo si ripete molte volte, portando a miglioramenti nelle decisioni dell'agente nel tempo.
Ciò che rende SARSA speciale tra gli algoritmi RL è che impara dalle scelte effettivamente fatte dall'agente, anche quando sta provando cose nuove. Questo approccio è particolarmente utile quando il percorso di apprendimento è importante quanto il risultato finale.
È come un robot che impara ad andare in bici pedalando davvero, cadute comprese, invece di limitarsi a trovare il percorso più breve da A a B usando rotelle protettive.
Preparare l'ambiente per il tutorial
In questo tutorial useremo ampiamente Numpy e la libreria di reinforcement learning Gymnasium. Mentre Numpy ci aiuterà a scrivere l'algoritmo SARSA, Gymnasium ci fornirà ambienti integrati per testare il nostro lavoro.
Installiamoli in un nuovo ambiente virtuale con i seguenti comandi:
Prima di continuare, ti incoraggio vivamente a leggere il nostro Introduction to Reinforcement Learning. Copre le idee fondamentali dell'RL, come agenti, ambienti, il dilemma esplorazione contro sfruttamento e il Q-learning.
Spiegare l'ambiente Taxi-V3
Nel corso del tutorial useremo l'ambiente Taxi-v3, un classico problema di reinforcement learning fornito dalla libreria Gymnasium. Simula un tassista che naviga in un mondo a griglia 5x5 per prelevare e lasciare passeggeri.
Per caricare l'ambiente usiamo il metodo
.make()di Gymnasium con una modalità di renderingrgb_array(per visualizzare l'ambiente più avanti):L'ambiente è una griglia 5x5 con quattro posizioni designate: rosso (R), verde (G), giallo (Y) e blu (B). Il taxi parte da una casella casuale e deve prelevare un passeggero in una delle posizioni colorate e lasciarlo in un'altra.
Il taxi può muoversi a Nord, Sud, Est o Ovest e può anche tentare di prelevare o lasciare un passeggero.
Ecco come possiamo visualizzare lo stato iniziale dell'ambiente con Matplotlib:
Per prima cosa, reimpostiamo l'ambiente per ottenere uno stato iniziale. Poi, mostriamo questo stato iniziale con la funzione
render, ottenendo un array immagine Numpy. La funzioneimshow()di Matplotlib prende questo array e produce una visualizzazione pulita senza tacche sugli assi.Prenditi un momento per comprendere il layout del mondo a griglia Taxi-v3, osservando la posizione del taxi, le barriere, le posizioni dei passeggeri e la destinazione.
L'agente (il taxi) riceve +20 punti per aver lasciato con successo un passeggero. Un prelievo o rilascio illegale comporta una ricompensa di -10. Nel frattempo, ogni passo temporale produce -1 di ricompensa per incoraggiare il taxi a completare il compito rapidamente.
Un singolo episodio dell'ambiente Taxi-v3 termina in caso di rilascio riuscito o quando viene raggiunto il numero massimo di passi temporali.
Ci sono 500 stati nello spazio degli stati dell'ambiente. Ogni stato è rappresentato da:
I codici delle azioni sono:
L'obiettivo è che l'agente impari una policy ottimale per massimizzare la ricompensa totale prelevando e lasciando passeggeri in modo efficiente.
Ciclo di interazione SARSA
Prima di scrivere l'algoritmo completo, vediamo come funziona il ciclo State-Action-Reward-State-Action usando il nostro ambiente.
Per prima cosa, definiamo quante epoche vogliamo che l'interazione continui. Un singolo episodio rappresenta un'esecuzione completa del compito del taxi, dallo stato iniziale fino a quando non lascia con successo il passeggero o raggiunge il numero massimo di passi temporali. Ogni episodio permette all'agente di imparare dalle proprie esperienze e migliorare la strategia nel tempo.
Ora scriviamo il ciclo di interazione:
Il codice sopra mostra il ciclo di interazione SARSA di base senza implementare alcun algoritmo di apprendimento:
env.reset().env.step(action). Questa versione usa azioni casuali per mostrare il ciclo di interazione.next_state,reward,... = env.take(action).Nel ciclo di interazione sopra, il taxi continua a muoversi nell'ambiente in tutte le direzioni ed esegue azioni casuali finché i passi temporali non finiscono.
Animare il ciclo di interazione SARSA
Prima di proseguire, implementiamo una funzionalità per animare i nostri cicli di interazione. Questo ci permette di vedere visivamente il taxi che interagisce con l'ambiente.
Creare l'animazione sarà un processo semplice:
env.render().moviepy, concateneremo tutti gli array immagine per creare una singola GIF.Modifichiamo il codice:
In questa versione impostiamo il numero di episodi a uno solo, poiché eseguire il rendering degli stati dell'ambiente come immagini richiede troppo tempo. Creiamo anche una lista vuota per archiviare i nostri array immagine. Poi, il ciclo di interazione continua come prima. L'unica differenza sono le due righe extra di codice in cui renderizziamo l'ambiente come immagine e lo aggiungiamo a
frames.Ora dovremmo avere 201 immagini dentro frames (un'immagine in più per lo stato aggiuntivo):
Trasformiamo queste immagini in una GIF usando la libreria
moviepy:La funzione
create_gif()prende una lista di frame e li converte in una GIF usando la classeImageSequenceClipdimoviepy. Un parametro importante è il numero di frame per secondo, fps, che controlla la durata della GIF. Più frame per secondo, più corta sarà la GIF.Alla fine, convertiamo i frame di un singolo episodio in una GIF a 25 FPS. Ecco il risultato:
Come puoi vedere, il taxi non ha idea di quello che sta facendo e non si è nemmeno avvicinato al passeggero. Diamogli un po' di cervello aggiungendo SARSA al suo sistema di navigazione.
Implementare SARSA in Python passo dopo passo
Costruiremo il codice di SARSA da zero, così ricorderai chiaramente ogni passaggio.
1. Impostare l'ambiente Gymnasium:
2. Inizializzare una Q-table
In questo passaggio introduciamo una nuova struttura dati — la Q-table. Ha dimensioni (numero di stati) x (numero di azioni). La tabella apparirebbe così al nostro agente, il tassista:
Inizialmente, la Q-table sarà riempita di zeri:
Poi, quando l'agente inizia a interagire con l'ambiente guidato da SARSA, aggiornerà la Q-table con i Q-value. I Q-value sono punteggi che dicono all'agente qual è l'azione migliore da intraprendere, dato lo stato corrente.
3. Definire gli iperparametri di SARSA
Dopo aver inizializzato la Q-table, impostiamo gli iperparametri di SARSA su valori comuni (ne parleremo più avanti):
4. Definire l'archiviazione per le metriche di performance
Poi, definiamo due liste per memorizzare le metriche di performance: la ricompensa totale e il numero di passi temporali per ogni episodio. L'obiettivo dell'agente è raccogliere quanta più ricompensa possibile nel minor tempo possibile:
5. Strategia epsilon-greedy per scegliere l'azione
Nella sezione precedente, il nostro agente era senza guida — stava eseguendo azioni casuali. Vogliamo cambiare questo dando al conducente una strategia Epsilon-Greedy:
Questa strategia serve a controllare l'equilibrio cruciale tra esplorazione e sfruttamento. Con probabilità
epsilon, l'agente esplora l'ambiente compiendo un'azione casuale, mentre con probabilità1-epsilonsfrutta la conoscenza attuale scegliendo l'azione con il Q-value più alto dalla Q-table. Questo approccio permette all'agente di scoprire nuove strategie potenzialmente migliori e, allo stesso tempo, di usare ciò che ha già imparato.6. Scrivere il ciclo di training SARSA
Infine, scriviamo il ciclo di training di SARSA. L'inizio del ciclo ci è già familiare. L'unica differenza è che usiamo la funzione
epsilon_greedy()per dirci quale azione intraprendere nello stato corrente:Poi avviamo il ciclo
whileche ci consente di eseguire il ciclo di interazione fino a raggiungere uno stato di terminazione:All'interno del ciclo
while, eseguiamo l'azione restituita daepsilon_greedy()e riceviamo lo stato successivo, la ricompensa per la nostra azione e un valore booleano che indica se l'episodio è terminato o meno.Poi arriviamo alla parte cruciale — la regola di aggiornamento di SARSA:
La regola di aggiornamento deriva dalla seguente formula:
[Source]
Impareremo l'intuizione dietro questa formula nella sezione successiva. Per ora, considera la formula come una sorta di magia matematica che aggiorna i Q-value della nostra Q-table secondo le regole di SARSA.
Una volta effettuato l'aggiornamento di Q, cambiamo le variabili
stateeactionallo stato e all'azione risultanti, aggiungiamo la ricompensa ricevuta alla ricompensa totale dell'episodio e aumentiamo il numero di passi temporali.Il ciclo
whilecontinua fino a raggiungere il numero massimo di passi (200 per l'ambiente taxi) o quando il taxi lascia con successo il passeggero nella posizione corretta.Una volta che il ciclo termina, registriamo la ricompensa totale dell'episodio e la durata dell'episodio. Stampiamo anche la ricompensa media e la durata media ogni 1000 episodi:
Ecco il ciclo di interazione completo eseguito:
Come puoi vedere dall'output, la ricompensa media e il numero di passi per episodio sono diminuiti drasticamente man mano che abbiamo eseguito sempre più episodi.
Possiamo osservarlo visivamente tracciando gli array
episode_rewardseepisode_lengths:Il grafico a sinistra mostra la ricompensa totale ottenuta in ciascun episodio. Possiamo osservare che:
Il grafico a destra mostra il numero di passi compiuti in ogni episodio. Possiamo vedere che:
Organizzare il codice in funzioni
In poco tempo, abbiamo coperto molti argomenti. Facciamo ora un passo indietro e organizziamo tutto ciò che abbiamo scritto. Creeremo funzioni per ciascuno dei passaggi eseguiti per implementare SARSA.
Per prima cosa, una funzione per creare un ambiente:
Poi, una funzione per inizializzare una Q-table dato un ambiente:
La strategia epsilon-greedy che accetta un ambiente, una Q-table, lo stato corrente ed epsilon:
La regola di aggiornamento di SARSA, che richiede una Q-table, lo stato corrente, l'azione intrapresa in quello stato, la ricompensa per l'azione, lo stato e l'azione successivi:
Infine, una grande funzione per addestrare l'agente con SARSA che richiede un ambiente, il numero di episodi da eseguire e i parametri alpha, gamma ed epsilon:
Inoltre, una funzione per tracciare le metriche di performance:
La nostra precedente funzione
create_gif():E un'altra funzione per eseguire un singolo episodio con rendering usando una Q-table completa e addestrata (per scopi di animazione):
Ora eseguiamo tutto:
I grafici delle performance sembrano buoni. Ora, guardiamo la GIF generata:
Evviva! Il taxi preleva correttamente il passeggero dalla casella blu e lo lascia in quella gialla.
Ho incollato il codice completo e organizzato per implementare SARSA in un gist su GitHub così puoi tornarci quando vuoi.
L'intuizione dietro la regola di aggiornamento di SARSA
Al cuore di SARSA c'è la sua regola di aggiornamento, che controlla come l'agente impara dalle sue esperienze. Scomponiamo questa regola ed esploriamo l'intuizione che la guida:
Q(s, a) = Q(s, a) + α [R + γ Q(s', a') - Q(s, a)]
Comprendere i componenti
Il processo di apprendimento
La regola di aggiornamento di SARSA mira a raffinare la comprensione dell'ambiente da parte dell'agente, aggiustando i Q-value in base alle nuove esperienze. Ecco come funziona:
Errore di differenza temporale
Il termine [R + γ * Q(s’, a’) — Q(s, a)] è noto come errore di differenza temporale (TD). Pensalo come una misura di sorpresa:
Questo errore aiuta l'agente a perfezionare continuamente le sue stime, guidando il processo di apprendimento verso una policy ottimale.
Ruolo degli iperparametri
1. Learning rate (α):
2. Fattore di sconto (γ):
3. Tasso di esplorazione (ε):
La visione d'insieme
La regola di aggiornamento di SARSA permette all'agente di imparare dalle proprie esperienze aggiustando costantemente le stime dei valori stato-azione. A ogni interazione, l'agente diventa un po' più saggio riguardo al suo ambiente. Nel corso di molti episodi, questo processo porta allo sviluppo di una policy ottimale per navigare nell'ambiente.
Regolando gli iperparametri, possiamo controllare vari aspetti del processo di apprendimento, consentendo a SARSA di adattarsi a diversi tipi di problemi e ambienti. Questa flessibilità, combinata con la sua regola di aggiornamento intuitiva, rende SARSA un algoritmo di reinforcement learning potente e ampiamente usato.
SARSA vs. Q-learning: differenze chiave
Sebbene SARSA e Q-learning siano entrambi algoritmi di reinforcement learning ampiamente utilizzati, presentano alcune importanti differenze. E tali differenze sono fondamentali per capire quando e come usare ciascun algoritmo.
1. On-policy vs. off-policy
Un algoritmo on-policy, SARSA, impara il valore della policy che segue, includendo anche i passi compiuti durante l'esplorazione. Il Q-learning è off-policy; impara il valore della policy ottimale — anche quando non sta seguendo quella policy, continua comunque a imparare il valore della policy ottimale quando alla fine raggiunge la fine dell'episodio. Ecco perché abbiamo detto all'inizio che SARSA va usato quando il percorso di apprendimento è importante quanto l'esito stesso. Il Q-learning non si cura molto del percorso di apprendimento.
2. Regola di aggiornamento
La differenza chiave è che SARSA usa il Q-value della prossima azione effettivamente intrapresa (a’), mentre il Q-learning usa il Q-value massimo del prossimo stato (max(Q(s’, a’))).
3. Considerazione dell'esplorazione
Nel processo di aggiornamento dei Q-value, SARSA non ignora la policy di esplorazione ed è quindi più conservativo. Al contrario, il Q-learning assume sempre che l'agente intraprenderà l'azione ottimale in futuro ed è quindi più aggressivo.
4. Convergenza
Sebbene entrambi gli algoritmi raggiungano alla fine la policy ottimale, il Q-learning può spesso imparare più rapidamente, in particolare in ambienti deterministici.
5. Sicurezza
In ambienti in cui l'esplorazione può portare a esiti negativi, SARSA apprende generalmente policy più sicure perché tiene conto della policy effettivamente seguita.
Nel classico problema della “cliff walking”, SARSA in genere apprende un percorso più sicuro che resta lontano dal bordo della scogliera, mentre il Q-learning potrebbe apprendere un percorso più rischioso che cammina lungo il bordo.
6. Stabilità
In certi ambienti casuali, SARSA può essere più stabile perché tiene conto della prossima azione effettiva, anche quando è un'azione esplorativa subottimale.
7. Sensibilità agli iperparametri
Il Q-learning può essere più sensibile alla scelta del learning rate e del tasso di esplorazione, specialmente in ambienti con forte componente stocastica.
8. Applicazioni nel mondo reale
Quando si tratta di robotica o altri sistemi fisici in cui esplorare l'ambiente è costoso e vogliamo implementare policy più sicure, potremmo preferire l'algoritmo on-policy SARSA. Quando siamo nel mondo simulato, nei giochi o in altri ambienti in cui possiamo ottenere molti feedback e dove vogliamo trovare la policy ottimale in modo rapido e abbastanza sicuro, potremmo preferire il Q-learning.
Potresti quindi pensare che SARSA sia una sorta di approccio più sicuro, mentre il Q-learning è più veloce e spregiudicato. In pratica, spesso dipende dallo specifico problema. Entrambi gli algoritmi sono molto forti, ed è sempre utile conoscere le differenze tra loro.
Conclusione
In questa guida approfondita, abbiamo esplorato l'algoritmo SARSA, coprendo i suoi concetti chiave, i dettagli di implementazione e le applicazioni pratiche usando l'ambiente Taxi-v3.
I punti chiave includono:
La capacità di SARSA di apprendere policy più sicure lo rende adatto ad applicazioni reali in cui i costi di esplorazione sono elevati, anche se in alcuni scenari può convergere più lentamente rispetto ad algoritmi off-policy come il Q-learning.
Ricorda, la scelta tra SARSA e altri algoritmi dipende dallo specifico problema e ambiente. Sperimentazione e fine tuning sono cruciali per risultati ottimali.
Ti invitiamo a costruire su queste basi esplorando ambienti più complessi, implementando SARSA con approssimazione di funzione o approfondendo altri algoritmi di reinforcement learning.
Ecco alcune risorse correlate per aiutarti lungo il percorso: