SARSA-reinforcement learning-algoritme in Python: een complete gids
Leer SARSA, een on-policy reinforcement learning-algoritme. Begrijp de update-regel, hyperparameters en verschillen met Q-learning met praktische Python-voorbeelden en de implementatie ervan.
Reinforcement learning (RL) is een krachtige machinelearning-paradigma. In RL leert software, meestal een agent genoemd, omgaan met omgevingen om complexe problemen op te lossen via trial-and-error, geheel zonder menselijke tussenkomst. Onder de RL-algoritmen valt SARSA op door zijn efficiënte on-policy karakter.
SARSA staat voor State-Action-Reward-State-Action en beschrijft een cyclus die de agent volgt om problemen op te lossen. Deze cyclus stelt de agent in staat te leren van eerdere fouten en af en toe op verkenning te gaan om nieuwe dingen te proberen. Dit gedrag maakt het algoritme bijzonder effectief bij specifieke probleemtypen en onderscheidt het van off-policy algoritmen zoals Q-learning.
In deze tutorial leer je hoe SARSA werkt en hoe je het in Python kunt implementeren. Om je begrip concreet te maken, gebruiken we doorlopend het klassieke Taxi Ride-probleem. We bespreken ook de voordelen, beperkingen en toepassingen van SARSA in de praktijk.
Wat is SARSA? Het korte antwoord
SARSA, kort voor State-Action-Reward-State-Action, beschrijft een sequentie van gebeurtenissen in een leerproces. Het is een effectieve leermethode voor computerprogramma's (agents) om goede beslissingen te nemen in uiteenlopende scenario's.
Het belangrijkste idee achter SARSA is trial-and-error. De agent onderneemt een actie in een situatie, observeert het resultaat en past zijn strategie aan op basis van de uitkomst (goed of slecht). Dit proces wordt vele malen herhaald, waardoor de beslissingen van de agent in de loop van de tijd verbeteren.
Wat SARSA bijzonder maakt onder RL-algoritmen is dat SARSA leert van de daadwerkelijke keuzes van de agent, ook wanneer deze aan het exploreren is. Deze aanpak is vooral nuttig wanneer de leerreis net zo belangrijk is als het eindresultaat.
Het is alsof een robot leert fietsen door echt te fietsen—vallen en opstaan—in plaats van met zijwieltjes de kortste weg van A naar B te zoeken.
Je omgeving instellen voor de tutorial
In deze tutorial gebruiken we Numpy en de Gymnasium-reinforcement learning-bibliotheek uitgebreid. Terwijl Numpy ons helpt het SARSA-algoritme te schrijven, levert Gymnasium ingebouwde omgevingen om ons werk te testen.
Laten we ze installeren in een nieuwe virtuele omgeving met de volgende commando's:
$ 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 JupyterMogelijk gemaakt door
Voordat je verdergaat, raad ik je sterk aan ons artikelIntroduction to Reinforcement Learning te lezen. Het behandelt de fundamentele ideeën achter RL, zoals agents, omgevingen, het exploratie-versus-exploitatie-dilemma en Q-learning.
De Taxi-V3-omgeving uitgelegd
Door de hele tutorial gebruiken we de Taxi-v3-omgeving, een klassiek reinforcement learning-probleem uit de Gymnasium-bibliotheek. Het simuleert een taxichauffeur die een 5x5 roosterwereld doorkruist om passagiers op te pikken en af te zetten.
Om de omgeving te laden, gebruiken we de .make()-methode van Gymnasium met een rgb_array render-modus (zodat we de omgeving later kunnen visualiseren):
import gymnasium as gym
env = gym.make('Taxi-v3', render_mode='rgb_array')Mogelijk gemaakt door
De omgeving is een 5x5 rooster met vier aangewezen locaties: rood (R), groen (G), geel (Y) en blauw (B). De taxi start op een willekeurig vakje en moet een passagier oppikken op een van de gekleurde locaties en op een andere locatie afzetten.
De taxi kan naar het noorden, zuiden, oosten of westen bewegen en kan ook proberen een passagier op te pikken of af te zetten.
Zo kunnen we de beginstaat van de omgeving visualiseren met 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([]);Mogelijk gemaakt door
Eerst resetten we de omgeving om een beginstaat te krijgen. Vervolgens tonen we deze beginstaat met de render-functie, die een Numpy-afbeeldingsarray oplevert. Matplotlibs imshow()-functie neemt deze array en produceert een heldere visualisatie zonder asmarkeringen.
Neem even de tijd om de indeling van de Taxi-v3-roosterwereld te begrijpen, let op de positie van de taxi, de barrières, passagierslocaties en de bestemming.
De agent (de taxi) krijgt +20 punten voor het succesvol afzetten van een passagier. Een ongeldige pick-up of drop-off resulteert in -10 beloning. Ondertussen levert elke tijdstap -1 beloning op om de taxi te stimuleren de taak snel te voltooien.
Eén episode van de Taxi-v3-omgeving eindigt bij een succesvolle drop-off of wanneer het maximale aantal tijdstappen is bereikt.
# The number of states and actions
n_states = env.observation_space.n
n_actions = env.action_space.n
print(n_states)print(n_actions)Mogelijk gemaakt door
Er zijn 500 toestanden in de toestandsruimte van de omgeving. Elke toestand wordt weergegeven door:
Taxi-rij (0–4)
Taxi-kolom (0–4)
Passagierslocatie (0–3 voor R, G, Y, B, of 4 voor in de taxi)
Bestemmingslocatie (0–3 voor R, G, Y, B)
Totaal aantal toestanden = 5 (rijen)×5 (kolommen)×5 (passagierslocaties)×4 (bestemmingslocaties)=500.
De actiecodes zijn:
0: Ga naar het zuiden
1: Ga naar het noorden
2: Ga naar het oosten
3: Ga naar het westen
4: Passagier oppikken
5: Passagier afzetten
Het doel is dat de agent een optimale policy leert om zijn totale beloning te maximaliseren door passagiers efficiënt op te pikken en af te zetten.
SARSA-interactie-loop
Voordat we het volledige algoritme schrijven, bekijken we hoe de State-Action-Reward-State-Action-cyclus werkt met onze omgeving.
Eerst definiëren we hoeveel episodes we de interactie willen laten doorgaan. Eén episode vertegenwoordigt een volledige run van de taxitaak, van de beginstaat tot het succesvol afzetten van de passagier of het bereiken van het maximale aantal tijdstappen. Elke episode laat de agent leren van zijn ervaringen en zijn strategie gaandeweg verbeteren.
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}")Mogelijk gemaakt door
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: 200Mogelijk gemaakt door
De bovenstaande code demonstreert de basale SARSA-interactiecyclus zonder een leeralgoritme te implementeren:
Reset de omgeving aan het begin van elke episode — (S), env.reset().
Neem een willekeurige actie — (A), env.step(action). Deze versie gebruikt willekeurige acties om de interactie-loop te tonen.
Ontvang beloning (R) en de volgende toestand (S_1), next_state, reward, ... = env.take(action).
Neem actie (A_1) in de nieuwe toestand.
In de bovenstaande interactie-loop blijft de taxi in alle richtingen door de omgeving bewegen en willekeurige acties uitvoeren totdat de tijdstappen op zijn.
De SARSA-interactie-loop animeren
Voordat we verdergaan, implementeren we een functie om onze interactie-loops te animeren. Hiermee kunnen we visueel zien hoe de taxi met de omgeving interageert.
De animatie maken is eenvoudig:
Bij elke tijdstap leggen we de toestand van de omgeving vast in een afbeeldingsarray met de functie env.render().
We verzamelen de afbeeldingsarrays in een aparte variabele.
Met een bibliotheek genaamd moviepy voegen we alle afbeeldingsarrays samen tot één GIF.
Laten we de code aanpassen:
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_stateMogelijk gemaakt door
In deze versie zetten we het aantal episodes op slechts één, omdat het renderen van omgevingsstaten als afbeeldingen veel tijd kost. We maken ook een lege lijst om onze afbeeldingsarrays op te slaan. Daarna gaat de interactie-loop verder zoals voorheen. Het enige verschil zijn de twee extra regels code waarin we de omgeving als afbeelding renderen en toevoegen aan frames.
Nu zouden we 201 afbeeldingen in frames moeten hebben (één extra afbeelding voor de aanvullende toestand):
Laten we deze afbeeldingen omzetten naar een GIF met de moviepy-bibliotheek:
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 locallyMogelijk gemaakt door
De functie create_gif() neemt een lijst met frames en zet deze om in een GIF met behulp van de klasse ImageSequenceClip uit moviepy. Een belangrijke parameter is het aantal frames per seconde, fps, dat de duur van de GIF bepaalt. Hoe meer frames per seconde, hoe korter de GIF.
Uiteindelijk zetten we de frames van één episode om naar een GIF met 25 FPS. Dit is het resultaat:
Zoals je ziet, heeft de taxi geen idee wat hij doet en kwam hij niet eens in de buurt van de passagier. Laten we hem wat hersens geven door SARSA toe te voegen aan zijn navigatiesysteem.
SARSA stap voor stap in Python implementeren
We bouwen de code voor SARSA from scratch, zodat je elke stap helder onthoudt.
1. De Gymnasium-omgeving instellen:
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")Mogelijk gemaakt door
2. Een Q-tabel initialiseren
# Initialize Q-table
n_states = env.observation_space.n
n_actions = env.action_space.n
Q_table = np.zeros((n_states, n_actions))Mogelijk gemaakt door
In deze stap introduceren we een nieuwe datastructuur — de Q-tabel. Deze heeft afmetingen (aantal toestanden) x (aantal acties). Zo ziet de tabel eruit voor onze agent, de taxichauffeur:
Vervolgens, wanneer de agent begint te interageren met de omgeving gestuurd door SARSA, zal hij de Q-tabel bijwerken met Q-waarden. De Q-waarden zijn scores die de agent vertellen welke actie de beste is, gegeven de huidige toestand.
3. SARSA-hyperparameters definiëren
Na het initialiseren van de Q-tabel stellen we de hyperparameters van SARSA in op gangbare waarden (later meer hierover):
# SARSA parameters
alpha =0.1# Learning rate
gamma =0.99# Discount factor
epsilon =0.1# Exploration rate for epsilon-greedy policy
n_episodes =20000Mogelijk gemaakt door
4. Opslag definiëren voor prestatiemetrics
Daarna definiëren we twee lijsten om prestatiemetrics op te slaan: de totale beloning en het aantal tijdstappen per episode. Het doel van de agent is om zoveel mogelijk beloning te verzamelen in zo weinig mogelijk tijd:
# Lists to store performance metrics
episode_rewards =[]
episode_lengths =[]Mogelijk gemaakt door
5. Epsilon-greedy policy voor het nemen van acties
In de vorige sectie was onze agent ongeleid — hij nam willekeurige acties. Dat willen we veranderen door de chauffeur een Epsilon-Greedy-strategie te geven:
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])Mogelijk gemaakt door
Deze strategie is bedoeld om de cruciale balans tussen exploratie en exploitatie te sturen. Met kans epsilon verkent de agent de omgeving door een willekeurige actie te nemen, terwijl hij met kans 1-epsilon zijn huidige kennis benut door de actie te kiezen met de hoogste Q-waarde uit de Q-tabel. Deze aanpak laat de agent nieuwe, mogelijk betere strategieën ontdekken en tegelijk benutten wat al is geleerd.
6. De SARSA-trainingsloop schrijven
Tot slot schrijven we de SARSA-trainingsloop. Het begin van de loop is ons al bekend. Het enige verschil is dat we de functie epsilon_greedy() gebruiken om te bepalen welke actie we in de huidige toestand moeten nemen:
# SARSA training loopfor episode inrange(n_episodes):
state, _ = env.reset()
action = epsilon_greedy(state, epsilon)
done =False
total_reward =0
steps =0...Mogelijk gemaakt door
Daarna starten we de while-loop die ons de interactiecyclus laat draaien totdat we een eindtoestand bereiken:
# 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)Mogelijk gemaakt door
Binnen de while-loop ondernemen we de actie die door epsilon_greedy() wordt teruggegeven en ontvangen we de volgende toestand, de beloning voor onze actie en een booleaanse waarde die aangeeft of de episode is beëindigd.
Dan komen we bij het cruciale onderdeel—de update-regel van SARSA:
# 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)# SARSA update rule
Q_table[state, action]+= alpha *(
reward + gamma * Q_table[next_state, next_action]- Q_table[state, action])Mogelijk gemaakt door
De intuïtie achter deze formule behandelen we in de volgende sectie. Zie de formule nu als wiskundige magie die de Q-waarden van onze Q-tabel bijwerkt volgens de regels van SARSA.
Zodra we de Q-update hebben gedaan, veranderen we de variabelen state en action naar de resulterende toestand en actie, tellen de ontvangen beloning op bij de totale beloning van de episode en verhogen het aantal tijdstappen.
# SARSA training loopfor episode inrange(n_episodes):...whilenot done:...
state = next_state
action = next_action
total_reward += reward
steps +=1Mogelijk gemaakt door
De while-loop gaat door totdat we het maximum aantal tijdstappen bereiken (200 voor de taxi-omgeving) of wanneer de taxi zijn passagier succesvol op de juiste plek afzet.
Als de loop eindigt, leggen we de totale beloning en de lengte van de episode vast. We printen ook elke 1000 episodes de gemiddelde beloning en lengte:
# SARSA training loopfor episode inrange(n_episodes):...whilenot done:...
episode_rewards.append(total_reward)
episode_lengths.append(steps)if episode %1000==0:
avg_reward = np.mean(episode_rewards[-1000:])
avg_length = np.mean(episode_lengths[-1000:])print(f"Episode {episode}, Avg Reward: {avg_reward:.2f}, Avg Length: {avg_length:.2f}")Mogelijk gemaakt door
Zoals je in de output ziet, daalden de gemiddelde beloning en het aantal tijdstappen per episode drastisch naarmate we meer episodes draaiden.
We kunnen dit visueel zien door de arrays episode_rewards en episode_lengths te plotten:
# 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()Mogelijk gemaakt door
De linker grafiek toont de totale beloning per episode. We zien dat:
Aanvankelijk zijn de beloningen laag en sterk variabel, wat aangeeft dat de agent aan het exploreren en leren is.
In de loop van de tijd is er een opwaartse trend in de beloningen, wat suggereert dat de agent zijn policy verbetert.
Tegen het einde lijken de beloningen te stabiliseren op een hoger niveau, wat aangeeft dat de agent een redelijk goede policy heeft geleerd.
De rechter grafiek toont het aantal stappen per episode. We zien dat:
In het begin duren episodes langer, omdat de agent suboptimale acties neemt.
Naarmate het leren vordert, is er een algemene neerwaartse trend in episodelengte.
De episodelengtes stabiliseren uiteindelijk, wat suggereert dat de agent de taak efficiënter uitvoert.
De code organiseren in functies
In korte tijd hebben we veel behandeld. Laten we nu een stap terug doen en alles wat we schreven ordenen. We maken functies voor elk van de stappen die we namen om SARSA te implementeren.
Eerst een functie om een omgeving te creëren:
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)Mogelijk gemaakt door
Dan een functie om een Q-tabel te initialiseren voor een gegeven omgeving:
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))Mogelijk gemaakt door
De Epsilon-greedy-strategie die een omgeving, een Q-tabel, de huidige toestand en epsilon accepteert:
defepsilon_greedy(env, Q_table, state, epsilon=0.1):"""Epsilon-greedy action selection."""if np.random.random()< epsilon:return env.action_space.sample()else:return np.argmax(Q_table[state])Mogelijk gemaakt door
De update-regel van SARSA, die een Q-tabel, de huidige toestand, de genomen actie in die toestand, de beloning voor de actie, de volgende toestand en actie vereist:
defsarsa_update(Q_table, state, action, reward, next_state, next_action, alpha, gamma):"""Perform SARSA update on Q-table."""
Q_table[state, action]+= alpha *(
reward + gamma * Q_table[next_state, next_action]- Q_table[state, action])Mogelijk gemaakt door
Tot slot een grote functie om de agent met SARSA te trainen, die een omgeving, het aantal te draaien episodes en de parameters alpha, gamma en epsilon vereist:
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()Mogelijk gemaakt door
Onze eerdere functie 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)Mogelijk gemaakt door
En nog een functie om een enkele episode te draaien met rendering met een complete, getrainde Q-tabel (voor animatiedoeleinden):
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_rewardMogelijk gemaakt door
Laten we nu alles uitvoeren:
if __name__ =="__main__":
env = create_environment()
Q_table, episode_rewards, episode_lengths = train_sarsa(env, n_episodes=20000)
plot_learning_curve(episode_rewards, episode_lengths)
frames, total_reward = run_episode(env, Q_table)
create_gif(frames,"images/sarsa_final_animation.gif", fps=1)Mogelijk gemaakt door
De prestatiegrafieken zien er goed uit. Laten we nu naar de gegenereerde GIF kijken:
Hoera! De taxi pikt de passagier correct op bij het blauwe vak en zet hem af bij het gele vak.
In het hart van SARSA ligt de update-regel, die bepaalt hoe de agent leert van zijn ervaringen. Laten we deze regel opsplitsen en de intuïtie erachter verkennen:
Q(s, a) = Q(s, a) + α [R + γ Q(s', a') - Q(s, a)]
De componenten begrijpen
Q(s, a): De huidige Q-waarde van het nemen van actie ‘a’ in toestand ‘s’.
α (alpha): Het leerrendement, bepaalt hoe snel nieuwe informatie wordt opgenomen.
R: De beloning die wordt ontvangen na het nemen van de actie.
γ (gamma): De discontovoet, bepaalt het belang van toekomstige beloningen.
Q(s’, a’): De geschatte Q-waarde van het volgende toestand-actie-paar.
Het leerproces
De update-regel van SARSA verfijnt het begrip van de agent van de omgeving door Q-waarden aan te passen op basis van nieuwe ervaringen. Zo werkt het:
De agent neemt een actie en observeert de beloning en de volgende toestand.
Hij berekent het verschil tussen zijn huidige Q-waarde en een nieuwe Q-waardeschatting op basis van de geobserveerde beloning en de waarde van het volgende toestand-actie-paar.
Dit verschil, geschaald door het leerrendement, wordt gebruikt om de Q-waarde bij te werken.
Temporal difference error
De term [R + γ * Q(s’, a’) — Q(s, a)] staat bekend als de temporal difference (TD) error. Zie het als een maat voor verrassing:
Positieve TD-error: De uitkomst was beter dan verwacht.
Negatieve TD-error: De uitkomst was slechter dan verwacht.
TD-error nul: De uitkomst kwam perfect overeen met de huidige schatting.
Deze fout helpt de agent zijn schattingen continu te verfijnen en stuurt het leerproces richting een optimale policy.
Rol van hyperparameters
1. Leerrendement (α):
Bepaalt de snelheid van leren.
Hogere α: Sneller leren, maar mogelijk onstabiel.
Lagere α: Langzamer, stabieler leren.
2. Discontovoet (γ):
Balanceert directe en toekomstige beloningen.
γ dicht bij 1: De agent waardeert toekomstige beloningen bijna evenveel als directe.
Lagere γ: De agent focust meer op directe beloningen.
3. Exploratiegraad (ε):
Hoewel niet in de update-regel zelf, is deze cruciaal voor de epsilon-greedy policy.
Hogere ε: Meer exploratie, mogelijk betere strategieën ontdekken ten koste van kortetermijnprestatie.
Het grote plaatje
De update-regel van SARSA laat de agent leren van zijn ervaringen door voortdurend zijn schattingen van toestand-actie-waarden aan te passen. Met elke interactie wordt de agent een beetje wijzer over zijn omgeving. Over vele episodes leidt dit tot de ontwikkeling van een optimale policy voor het navigeren door de omgeving.
Door de hyperparameters af te stemmen, sturen we verschillende aspecten van het leerproces, waardoor SARSA zich kan aanpassen aan verschillende soorten problemen en omgevingen. Deze flexibiliteit, gecombineerd met de intuïtieve update-regel, maakt SARSA tot een krachtig en veelgebruikt reinforcement learning-algoritme.
SARSA vs. Q-learning: de belangrijkste verschillen
Hoewel SARSA en Q-learning beide veelgebruikte reinforcement learning-algoritmen zijn, hebben ze enkele belangrijke verschillen. Die verschillen zijn cruciaal om te begrijpen wanneer en hoe je elk algoritme gebruikt.
1. On-policy vs. off-policy
Een on-policy algoritme, SARSA, leert de waarde van de policy die het volgt, inclusief de stappen tijdens exploratie. Q-learning is off-policy; het leert de waarde van de optimale policy—zelfs als het die policy niet volgt, leert het toch de waarde van de optimale policy wanneer het uiteindelijk het einde van de episode bereikt. Daarom noemden we aan het begin dat SARSA moet worden gebruikt wanneer de leerreis net zo belangrijk is als de uitkomst zelf. Q-learning geeft minder om de leerreis.
2. Update-regel
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)]
Het belangrijkste verschil is dat SARSA de Q-waarde gebruikt van de daadwerkelijk genomen volgende actie (a’), terwijl Q-learning de maximale Q-waarde van de volgende toestand gebruikt (max(Q(s’, a’))).
3. Overweging van exploratie
Bij het updaten van Q-waarden negeert SARSA de exploratiepolicy niet en is daardoor voorzichtiger. Q-learning daarentegen gaat er altijd van uit dat de agent in de toekomst de optimale actie zal nemen en is dus agressiever.
4. Convergentie
Hoewel beide algoritmen uiteindelijk de optimale policy bereiken, kan Q-learning vaak sneller leren, vooral in deterministische omgevingen.
5. Veiligheid
In omgevingen waar exploratie tot slechte uitkomsten kan leiden, leert SARSA over het algemeen veiligere policies omdat het rekening houdt met de daadwerkelijke gevolgde policy.
In het klassieke “cliff walking”-probleem leert SARSA doorgaans een veiliger pad dat wegblijft van de rand van de klif, terwijl Q-learning een riskanter pad kan leren dat langs de rand loopt.
6. Stabiliteit
In bepaalde willekeurige omgevingen kan SARSA stabieler zijn omdat het rekening houdt met de daadwerkelijke volgende actie, zelfs wanneer die een suboptimale exploratie-actie is.
7. Gevoeligheid voor hyperparameters
Q-learning kan gevoeliger zijn voor de keuze van leerrendement en exploratiegraad, vooral in omgevingen met aanzienlijke stochastiek.
8. Toepassingen in de praktijk
Bij robotica of andere fysieke systemen waar het verkennen van de omgeving kostbaar is en we veiligere policies willen implementeren, geven we mogelijk de voorkeur aan het on-policy algoritme SARSA. In de gesimuleerde wereld, in games of in andere omgevingen waar we veel feedback kunnen krijgen en snel en redelijk veilig de optimale policy willen vinden, is Q-learning vaak aantrekkelijker.
Je zou dus kunnen denken dat SARSA min of meer het veiligere domein is, en Q-learning de snellere, roekelozere aanpak. In de praktijk hangt het vaak af van het specifieke probleem. Beide algoritmen zijn erg sterk, en het is altijd zinvol de verschillen ertussen te kennen.
Conclusie
In deze uitgebreide gids hebben we het SARSA-algoritme verkend, met de kernconcepten, implementatiedetails en praktische toepassingen in de Taxi-v3-omgeving.
Belangrijkste inzichten:
Het on-policy karakter van SARSA en de intuïtie achter de update-regel
Python-implementatie en visualisatietechnieken
Vergelijking met Q-learning, met nadruk op sterke punten en use-cases
Het vermogen van SARSA om veiligere policies te leren maakt het geschikt voor toepassingen in de echte wereld waar exploratiekosten hoog zijn, al kan het in sommige scenario’s trager convergeren dan off-policy algoritmen zoals Q-learning.
Onthoud dat de keuze tussen SARSA en andere algoritmen afhangt van je specifieke probleem en omgeving. Experimenteren en fijn afstemmen zijn cruciaal voor optimale resultaten.
We moedigen je aan om op deze basis voort te bouwen door complexere omgevingen te verkennen, SARSA met functie-approximatie te implementeren of je te verdiepen in andere reinforcement learning-algoritmen.
Hier zijn enkele gerelateerde bronnen om je op weg te helpen:
Wat is het SARSA-algoritme in reinforcement learning?⌄
SARSA (State-Action-Reward-State-Action) is een on-policy reinforcement learning-algoritme dat Q-waarden bijwerkt op basis van de daadwerkelijke ervaringen van de agent, inclusief exploratieve acties.
Hoe verschilt SARSA van Q-learning?⌄
SARSA is on-policy en werkt Q-waarden bij met de daadwerkelijk genomen volgende actie, terwijl Q-learning off-policy is en de maximale Q-waarde van de volgende toestand gebruikt, wat tot verschillend leergedrag leidt.
Wat zijn de belangrijkste hyperparameters in SARSA en hoe beïnvloeden ze het leren?⌄
De sleutel-hyperparameters zijn het leerrendement (α), de discontovoet (γ) en de exploratiegraad (ε). Ze bepalen respectievelijk de leersnelheid, het belang van toekomstige beloningen en de balans tussen exploratie en exploitatie.
Hoe kan ik het SARSA-algoritme in Python implementeren?⌄
Het artikel biedt een stapsgewijze gids om SARSA in Python te implementeren, inclusief het instellen van de omgeving, het initialiseren van de Q-tabel en het schrijven van de trainingsloop.
Wat zijn de praktische toepassingen van SARSA in realistische scenario’s?⌄
SARSA is geschikt voor toepassingen waar exploratie kostbaar is, zoals robotica en fysieke systemen, omdat het de neiging heeft veiligere policies te leren dan off-policy algoritmen zoals Q-learning.
Ik ben een contentmaker op het gebied van data science met meer dan 2 jaar ervaring en een van de grootste achterbannen op Medium. Ik schrijf graag uitgebreide artikelen over AI en ML met een vleugje sarcastische stijl, want je moet toch íets doen om ze wat minder saai te maken. Ik heb meer dan 130 artikelen en een DataCamp-cursus gemaakt, en er is nog een nieuwe in de maak. Mijn content is door meer dan 5 miljoen paar ogen gezien, van wie 20k volgers werden op zowel Medium als LinkedIn.
Reinforcement learning (RL) is een krachtige machinelearning-paradigma. In RL leert software, meestal een agent genoemd, omgaan met omgevingen om complexe problemen op te lossen via trial-and-error, geheel zonder menselijke tussenkomst. Onder de RL-algoritmen valt SARSA op door zijn efficiënte on-policy karakter.
SARSA staat voor State-Action-Reward-State-Action en beschrijft een cyclus die de agent volgt om problemen op te lossen. Deze cyclus stelt de agent in staat te leren van eerdere fouten en af en toe op verkenning te gaan om nieuwe dingen te proberen. Dit gedrag maakt het algoritme bijzonder effectief bij specifieke probleemtypen en onderscheidt het van off-policy algoritmen zoals Q-learning.
In deze tutorial leer je hoe SARSA werkt en hoe je het in Python kunt implementeren. Om je begrip concreet te maken, gebruiken we doorlopend het klassieke Taxi Ride-probleem. We bespreken ook de voordelen, beperkingen en toepassingen van SARSA in de praktijk.
Wat is SARSA? Het korte antwoord
SARSA, kort voor State-Action-Reward-State-Action, beschrijft een sequentie van gebeurtenissen in een leerproces. Het is een effectieve leermethode voor computerprogramma's (agents) om goede beslissingen te nemen in uiteenlopende scenario's.
Het belangrijkste idee achter SARSA is trial-and-error. De agent onderneemt een actie in een situatie, observeert het resultaat en past zijn strategie aan op basis van de uitkomst (goed of slecht). Dit proces wordt vele malen herhaald, waardoor de beslissingen van de agent in de loop van de tijd verbeteren.
Wat SARSA bijzonder maakt onder RL-algoritmen is dat SARSA leert van de daadwerkelijke keuzes van de agent, ook wanneer deze aan het exploreren is. Deze aanpak is vooral nuttig wanneer de leerreis net zo belangrijk is als het eindresultaat.
Het is alsof een robot leert fietsen door echt te fietsen—vallen en opstaan—in plaats van met zijwieltjes de kortste weg van A naar B te zoeken.
Je omgeving instellen voor de tutorial
In deze tutorial gebruiken we Numpy en de Gymnasium-reinforcement learning-bibliotheek uitgebreid. Terwijl Numpy ons helpt het SARSA-algoritme te schrijven, levert Gymnasium ingebouwde omgevingen om ons werk te testen.
Laten we ze installeren in een nieuwe virtuele omgeving met de volgende commando's:
Voordat je verdergaat, raad ik je sterk aan ons artikel Introduction to Reinforcement Learning te lezen. Het behandelt de fundamentele ideeën achter RL, zoals agents, omgevingen, het exploratie-versus-exploitatie-dilemma en Q-learning.
De Taxi-V3-omgeving uitgelegd
Door de hele tutorial gebruiken we de Taxi-v3-omgeving, een klassiek reinforcement learning-probleem uit de Gymnasium-bibliotheek. Het simuleert een taxichauffeur die een 5x5 roosterwereld doorkruist om passagiers op te pikken en af te zetten.
Om de omgeving te laden, gebruiken we de
.make()-methode van Gymnasium met eenrgb_arrayrender-modus (zodat we de omgeving later kunnen visualiseren):De omgeving is een 5x5 rooster met vier aangewezen locaties: rood (R), groen (G), geel (Y) en blauw (B). De taxi start op een willekeurig vakje en moet een passagier oppikken op een van de gekleurde locaties en op een andere locatie afzetten.
De taxi kan naar het noorden, zuiden, oosten of westen bewegen en kan ook proberen een passagier op te pikken of af te zetten.
Zo kunnen we de beginstaat van de omgeving visualiseren met Matplotlib:
Eerst resetten we de omgeving om een beginstaat te krijgen. Vervolgens tonen we deze beginstaat met de
render-functie, die een Numpy-afbeeldingsarray oplevert. Matplotlibsimshow()-functie neemt deze array en produceert een heldere visualisatie zonder asmarkeringen.Neem even de tijd om de indeling van de Taxi-v3-roosterwereld te begrijpen, let op de positie van de taxi, de barrières, passagierslocaties en de bestemming.
De agent (de taxi) krijgt +20 punten voor het succesvol afzetten van een passagier. Een ongeldige pick-up of drop-off resulteert in -10 beloning. Ondertussen levert elke tijdstap -1 beloning op om de taxi te stimuleren de taak snel te voltooien.
Eén episode van de Taxi-v3-omgeving eindigt bij een succesvolle drop-off of wanneer het maximale aantal tijdstappen is bereikt.
Er zijn 500 toestanden in de toestandsruimte van de omgeving. Elke toestand wordt weergegeven door:
De actiecodes zijn:
Het doel is dat de agent een optimale policy leert om zijn totale beloning te maximaliseren door passagiers efficiënt op te pikken en af te zetten.
SARSA-interactie-loop
Voordat we het volledige algoritme schrijven, bekijken we hoe de State-Action-Reward-State-Action-cyclus werkt met onze omgeving.
Eerst definiëren we hoeveel episodes we de interactie willen laten doorgaan. Eén episode vertegenwoordigt een volledige run van de taxitaak, van de beginstaat tot het succesvol afzetten van de passagier of het bereiken van het maximale aantal tijdstappen. Elke episode laat de agent leren van zijn ervaringen en zijn strategie gaandeweg verbeteren.
Nu schrijven we de interactie-loop:
De bovenstaande code demonstreert de basale SARSA-interactiecyclus zonder een leeralgoritme te implementeren:
env.reset().env.step(action). Deze versie gebruikt willekeurige acties om de interactie-loop te tonen.next_state,reward,... = env.take(action).In de bovenstaande interactie-loop blijft de taxi in alle richtingen door de omgeving bewegen en willekeurige acties uitvoeren totdat de tijdstappen op zijn.
De SARSA-interactie-loop animeren
Voordat we verdergaan, implementeren we een functie om onze interactie-loops te animeren. Hiermee kunnen we visueel zien hoe de taxi met de omgeving interageert.
De animatie maken is eenvoudig:
env.render().moviepyvoegen we alle afbeeldingsarrays samen tot één GIF.Laten we de code aanpassen:
In deze versie zetten we het aantal episodes op slechts één, omdat het renderen van omgevingsstaten als afbeeldingen veel tijd kost. We maken ook een lege lijst om onze afbeeldingsarrays op te slaan. Daarna gaat de interactie-loop verder zoals voorheen. Het enige verschil zijn de twee extra regels code waarin we de omgeving als afbeelding renderen en toevoegen aan
frames.Nu zouden we 201 afbeeldingen in frames moeten hebben (één extra afbeelding voor de aanvullende toestand):
Laten we deze afbeeldingen omzetten naar een GIF met de
moviepy-bibliotheek:De functie
create_gif()neemt een lijst met frames en zet deze om in een GIF met behulp van de klasseImageSequenceClipuitmoviepy. Een belangrijke parameter is het aantal frames per seconde, fps, dat de duur van de GIF bepaalt. Hoe meer frames per seconde, hoe korter de GIF.Uiteindelijk zetten we de frames van één episode om naar een GIF met 25 FPS. Dit is het resultaat:
Zoals je ziet, heeft de taxi geen idee wat hij doet en kwam hij niet eens in de buurt van de passagier. Laten we hem wat hersens geven door SARSA toe te voegen aan zijn navigatiesysteem.
SARSA stap voor stap in Python implementeren
We bouwen de code voor SARSA from scratch, zodat je elke stap helder onthoudt.
1. De Gymnasium-omgeving instellen:
2. Een Q-tabel initialiseren
In deze stap introduceren we een nieuwe datastructuur — de Q-tabel. Deze heeft afmetingen (aantal toestanden) x (aantal acties). Zo ziet de tabel eruit voor onze agent, de taxichauffeur:
Aanvankelijk is de Q-tabel gevuld met nullen:
Vervolgens, wanneer de agent begint te interageren met de omgeving gestuurd door SARSA, zal hij de Q-tabel bijwerken met Q-waarden. De Q-waarden zijn scores die de agent vertellen welke actie de beste is, gegeven de huidige toestand.
3. SARSA-hyperparameters definiëren
Na het initialiseren van de Q-tabel stellen we de hyperparameters van SARSA in op gangbare waarden (later meer hierover):
4. Opslag definiëren voor prestatiemetrics
Daarna definiëren we twee lijsten om prestatiemetrics op te slaan: de totale beloning en het aantal tijdstappen per episode. Het doel van de agent is om zoveel mogelijk beloning te verzamelen in zo weinig mogelijk tijd:
5. Epsilon-greedy policy voor het nemen van acties
In de vorige sectie was onze agent ongeleid — hij nam willekeurige acties. Dat willen we veranderen door de chauffeur een Epsilon-Greedy-strategie te geven:
Deze strategie is bedoeld om de cruciale balans tussen exploratie en exploitatie te sturen. Met kans
epsilonverkent de agent de omgeving door een willekeurige actie te nemen, terwijl hij met kans1-epsilonzijn huidige kennis benut door de actie te kiezen met de hoogste Q-waarde uit de Q-tabel. Deze aanpak laat de agent nieuwe, mogelijk betere strategieën ontdekken en tegelijk benutten wat al is geleerd.6. De SARSA-trainingsloop schrijven
Tot slot schrijven we de SARSA-trainingsloop. Het begin van de loop is ons al bekend. Het enige verschil is dat we de functie
epsilon_greedy()gebruiken om te bepalen welke actie we in de huidige toestand moeten nemen:Daarna starten we de
while-loop die ons de interactiecyclus laat draaien totdat we een eindtoestand bereiken:Binnen de
while-loop ondernemen we de actie die doorepsilon_greedy()wordt teruggegeven en ontvangen we de volgende toestand, de beloning voor onze actie en een booleaanse waarde die aangeeft of de episode is beëindigd.Dan komen we bij het cruciale onderdeel—de update-regel van SARSA:
De update-regel komt uit de volgende formule:
[Bron]
De intuïtie achter deze formule behandelen we in de volgende sectie. Zie de formule nu als wiskundige magie die de Q-waarden van onze Q-tabel bijwerkt volgens de regels van SARSA.
Zodra we de Q-update hebben gedaan, veranderen we de variabelen
stateenactionnaar de resulterende toestand en actie, tellen de ontvangen beloning op bij de totale beloning van de episode en verhogen het aantal tijdstappen.De
while-loop gaat door totdat we het maximum aantal tijdstappen bereiken (200 voor de taxi-omgeving) of wanneer de taxi zijn passagier succesvol op de juiste plek afzet.Als de loop eindigt, leggen we de totale beloning en de lengte van de episode vast. We printen ook elke 1000 episodes de gemiddelde beloning en lengte:
Hier is de volledige interactie-loop uitgevoerd:
Zoals je in de output ziet, daalden de gemiddelde beloning en het aantal tijdstappen per episode drastisch naarmate we meer episodes draaiden.
We kunnen dit visueel zien door de arrays
episode_rewardsenepisode_lengthste plotten:De linker grafiek toont de totale beloning per episode. We zien dat:
De rechter grafiek toont het aantal stappen per episode. We zien dat:
De code organiseren in functies
In korte tijd hebben we veel behandeld. Laten we nu een stap terug doen en alles wat we schreven ordenen. We maken functies voor elk van de stappen die we namen om SARSA te implementeren.
Eerst een functie om een omgeving te creëren:
Dan een functie om een Q-tabel te initialiseren voor een gegeven omgeving:
De Epsilon-greedy-strategie die een omgeving, een Q-tabel, de huidige toestand en epsilon accepteert:
De update-regel van SARSA, die een Q-tabel, de huidige toestand, de genomen actie in die toestand, de beloning voor de actie, de volgende toestand en actie vereist:
Tot slot een grote functie om de agent met SARSA te trainen, die een omgeving, het aantal te draaien episodes en de parameters alpha, gamma en epsilon vereist:
Ook een functie om prestatiemetrics te plotten:
Onze eerdere functie
create_gif():En nog een functie om een enkele episode te draaien met rendering met een complete, getrainde Q-tabel (voor animatiedoeleinden):
Laten we nu alles uitvoeren:
De prestatiegrafieken zien er goed uit. Laten we nu naar de gegenereerde GIF kijken:
Hoera! De taxi pikt de passagier correct op bij het blauwe vak en zet hem af bij het gele vak.
Ik heb de volledig geordende code voor het implementeren van SARSA in een GitHub gist geplakt, zodat je er altijd op kunt teruggrijpen.
De intuïtie achter SARSA’s update-regel
In het hart van SARSA ligt de update-regel, die bepaalt hoe de agent leert van zijn ervaringen. Laten we deze regel opsplitsen en de intuïtie erachter verkennen:
Q(s, a) = Q(s, a) + α [R + γ Q(s', a') - Q(s, a)]
De componenten begrijpen
Het leerproces
De update-regel van SARSA verfijnt het begrip van de agent van de omgeving door Q-waarden aan te passen op basis van nieuwe ervaringen. Zo werkt het:
Temporal difference error
De term [R + γ * Q(s’, a’) — Q(s, a)] staat bekend als de temporal difference (TD) error. Zie het als een maat voor verrassing:
Deze fout helpt de agent zijn schattingen continu te verfijnen en stuurt het leerproces richting een optimale policy.
Rol van hyperparameters
1. Leerrendement (α):
2. Discontovoet (γ):
3. Exploratiegraad (ε):
Het grote plaatje
De update-regel van SARSA laat de agent leren van zijn ervaringen door voortdurend zijn schattingen van toestand-actie-waarden aan te passen. Met elke interactie wordt de agent een beetje wijzer over zijn omgeving. Over vele episodes leidt dit tot de ontwikkeling van een optimale policy voor het navigeren door de omgeving.
Door de hyperparameters af te stemmen, sturen we verschillende aspecten van het leerproces, waardoor SARSA zich kan aanpassen aan verschillende soorten problemen en omgevingen. Deze flexibiliteit, gecombineerd met de intuïtieve update-regel, maakt SARSA tot een krachtig en veelgebruikt reinforcement learning-algoritme.
SARSA vs. Q-learning: de belangrijkste verschillen
Hoewel SARSA en Q-learning beide veelgebruikte reinforcement learning-algoritmen zijn, hebben ze enkele belangrijke verschillen. Die verschillen zijn cruciaal om te begrijpen wanneer en hoe je elk algoritme gebruikt.
1. On-policy vs. off-policy
Een on-policy algoritme, SARSA, leert de waarde van de policy die het volgt, inclusief de stappen tijdens exploratie. Q-learning is off-policy; het leert de waarde van de optimale policy—zelfs als het die policy niet volgt, leert het toch de waarde van de optimale policy wanneer het uiteindelijk het einde van de episode bereikt. Daarom noemden we aan het begin dat SARSA moet worden gebruikt wanneer de leerreis net zo belangrijk is als de uitkomst zelf. Q-learning geeft minder om de leerreis.
2. Update-regel
Het belangrijkste verschil is dat SARSA de Q-waarde gebruikt van de daadwerkelijk genomen volgende actie (a’), terwijl Q-learning de maximale Q-waarde van de volgende toestand gebruikt (max(Q(s’, a’))).
3. Overweging van exploratie
Bij het updaten van Q-waarden negeert SARSA de exploratiepolicy niet en is daardoor voorzichtiger. Q-learning daarentegen gaat er altijd van uit dat de agent in de toekomst de optimale actie zal nemen en is dus agressiever.
4. Convergentie
Hoewel beide algoritmen uiteindelijk de optimale policy bereiken, kan Q-learning vaak sneller leren, vooral in deterministische omgevingen.
5. Veiligheid
In omgevingen waar exploratie tot slechte uitkomsten kan leiden, leert SARSA over het algemeen veiligere policies omdat het rekening houdt met de daadwerkelijke gevolgde policy.
In het klassieke “cliff walking”-probleem leert SARSA doorgaans een veiliger pad dat wegblijft van de rand van de klif, terwijl Q-learning een riskanter pad kan leren dat langs de rand loopt.
6. Stabiliteit
In bepaalde willekeurige omgevingen kan SARSA stabieler zijn omdat het rekening houdt met de daadwerkelijke volgende actie, zelfs wanneer die een suboptimale exploratie-actie is.
7. Gevoeligheid voor hyperparameters
Q-learning kan gevoeliger zijn voor de keuze van leerrendement en exploratiegraad, vooral in omgevingen met aanzienlijke stochastiek.
8. Toepassingen in de praktijk
Bij robotica of andere fysieke systemen waar het verkennen van de omgeving kostbaar is en we veiligere policies willen implementeren, geven we mogelijk de voorkeur aan het on-policy algoritme SARSA. In de gesimuleerde wereld, in games of in andere omgevingen waar we veel feedback kunnen krijgen en snel en redelijk veilig de optimale policy willen vinden, is Q-learning vaak aantrekkelijker.
Je zou dus kunnen denken dat SARSA min of meer het veiligere domein is, en Q-learning de snellere, roekelozere aanpak. In de praktijk hangt het vaak af van het specifieke probleem. Beide algoritmen zijn erg sterk, en het is altijd zinvol de verschillen ertussen te kennen.
Conclusie
In deze uitgebreide gids hebben we het SARSA-algoritme verkend, met de kernconcepten, implementatiedetails en praktische toepassingen in de Taxi-v3-omgeving.
Belangrijkste inzichten:
Het vermogen van SARSA om veiligere policies te leren maakt het geschikt voor toepassingen in de echte wereld waar exploratiekosten hoog zijn, al kan het in sommige scenario’s trager convergeren dan off-policy algoritmen zoals Q-learning.
Onthoud dat de keuze tussen SARSA en andere algoritmen afhangt van je specifieke probleem en omgeving. Experimenteren en fijn afstemmen zijn cruciaal voor optimale resultaten.
We moedigen je aan om op deze basis voort te bouwen door complexere omgevingen te verkennen, SARSA met functie-approximatie te implementeren of je te verdiepen in andere reinforcement learning-algoritmen.
Hier zijn enkele gerelateerde bronnen om je op weg te helpen: