course

Învățarea prin întărire (RL) este partea din ecosistemul de machine learning în care agentul învață interacționând cu mediul pentru a obține strategia optimă de atingere a obiectivelor. Este destul de diferită de algoritmii de învățare supravegheată, unde trebuie să alimentăm și să procesăm date. Învățarea prin întărire nu necesită date. În schimb, învață din mediu și din sistemul de recompense pentru a lua decizii mai bune.
De exemplu, în jocul video Mario, dacă un personaj face o acțiune aleatoare (de ex. se mișcă la stânga), pe baza acelei acțiuni poate primi o recompensă. După ce ia acțiunea, agentul (Mario) ajunge într-o nouă stare, iar procesul se repetă până când personajul de joc ajunge la finalul nivelului sau moare.
Acest episod se va repeta de mai multe ori până când Mario învață să navigheze prin mediu maximizând recompensele.

Imagine de autor
Putem descompune învățarea prin întărire în cinci pași simpli:
- Agentul se află în starea zero într-un mediu.
- Va face o acțiune pe baza unei strategii specifice.
- Va primi o recompensă sau o penalizare în funcție de acea acțiune.
- Învățând din mutările anterioare și optimizând strategia.
- Procesul se repetă până când se găsește o strategie optimă.
Află mai multe citind tutorialul nostru, o Introducere în învățarea prin întărire. Vei explora mai multe despre cum funcționează învățarea prin întărire, cu exemple de cod.
În acest tutorial, vom învăța despre Q-learning și vom înțelege de ce avem nevoie de Deep Q-learning. În plus, vom învăța să creăm și să antrenăm algoritmi Q-learning de la zero folosind Numpy și Gymnasium.
Notă: Dacă ești nou în machine learning, îți recomandăm să urmezi traseul nostru de carieră Machine Learning Scientist with Python pentru a înțelege mai bine învățarea prin întărire și Q-Learning.
Ce este Q-Learning?
Q-learning este un algoritm fără model, bazat pe valori, off-policy, care găsește cea mai bună serie de acțiuni pe baza stării curente a agentului. „Q” vine de la quality (calitate). Calitatea reprezintă cât de valoroasă este acțiunea pentru maximizarea recompenselor viitoare.
Algoritmii cu model folosesc funcții de tranziție și de recompensă pentru a estima politica optimă și a crea modelul. În schimb, algoritmii fără model învață consecințele acțiunilor lor din experiență, fără funcție de tranziție și recompensă.
Metoda bazată pe valori antrenează funcția de valoare pentru a învăța care stare este mai valoroasă și ce acțiune să ia. Pe de altă parte, metodele bazate pe politică antrenează direct politica pentru a învăța ce acțiune să ia într-o stare dată.
În off-policy, algoritmul evaluează și actualizează o politică diferită de politica folosită pentru a lua o acțiune. În schimb, algoritmul on-policy evaluează și îmbunătățește aceeași politică folosită pentru a lua acțiunea.
Termeni-cheie în Q-learning
Înainte să vedem cum funcționează Q-learning, trebuie să învățăm câțiva termeni utili pentru a înțelege fundamentele Q-learning.
- Stări (s): poziția curentă a agentului în mediu.
- Acțiune (a): un pas făcut de agent într-o anumită stare.
- Recompense: pentru fiecare acțiune, agentul primește o recompensă sau o penalizare.
- Episoade: sfârșitul nivelului, unde agenții nu mai pot lua acțiuni noi. Se întâmplă când agentul a atins obiectivul sau a eșuat.
- Q(St+1, a): valoarea Q optimă așteptată pentru a face acțiunea într-o stare anume.
- Q(St, At): estimarea curentă a lui Q(St+1, a).
- Q-Table: agentul menține un Q-table cu mulțimi de stări și acțiuni.
- Temporal Differences (TD): folosit pentru a estima valoarea așteptată a lui Q(St+1, a) folosind starea și acțiunea curentă și starea și acțiunea anterioară.
Cum funcționează Q-Learning?
Vom învăța în detaliu cum funcționează Q-learning folosind exemplul unui lac înghețat. În acest mediu, agentul trebuie să traverseze lacul înghețat de la start la țintă, fără să cadă în găuri. Cea mai bună strategie este să ajungă la țintă urmând drumul cel mai scurt.

Gif de autor
Q-Table
Agentul va folosi un Q-table pentru a lua cea mai bună acțiune posibilă pe baza recompensei așteptate pentru fiecare stare din mediu. Pe scurt, un Q-table este o structură de date cu mulțimi de acțiuni și stări, iar algoritmul de Q-learning este folosit pentru a actualiza valorile din tabel.
Q-Function
Funcția Q folosește ecuația Bellman și primește starea (s) și acțiunea (a) ca intrare. Ecuația simplifică calculul valorilor de stare și al valorii stare-acțiune. 
Imagine de pe freecodecamp.org
Algoritmul Q-learning

Imagine de autor
Inițializează Q-Table
Mai întâi vom inițializa Q-table. Vom construi tabelul cu coloane în funcție de numărul de acțiuni și rânduri în funcție de numărul de stări.
În exemplul nostru, personajul se poate mișca în sus, jos, stânga și dreapta. Avem patru acțiuni posibile și patru stări (start, inactiv, drum greșit și final). Poți considera și drumul greșit drept cădere în gaură. Vom inițializa Q-Table cu valori 0.

Imagine de autor
Alege o acțiune
Al doilea pas este destul de simplu. La început, agentul va alege să facă o acțiune aleatoare (jos sau dreapta), iar la a doua rulare va folosi un Q-Table actualizat pentru a selecta acțiunea.
Efectuează o acțiune
Alegerea unei acțiuni și efectuarea ei se va repeta de mai multe ori până când bucla de antrenare se oprește. Prima acțiune și prima stare sunt selectate folosind Q-Table. În cazul nostru, toate valorile din Q-Table sunt zero.
Apoi, agentul se va mișca în jos și va actualiza Q-Table folosind ecuația Bellman. Cu fiecare mutare, vom actualiza valorile din Q-Table și le vom folosi și pentru a determina cea mai bună acțiune.
Inițial, agentul este în modul explorare și alege o acțiune aleatoare pentru a explora mediul. Strategia Epsilon Greedy este o metodă simplă de a echilibra explorarea și exploatarea. Epsilon reprezintă probabilitatea de a alege explorarea și de a exploata atunci când șansele de explorare sunt mai mici.
La început, rata epsilon este mai mare, ceea ce înseamnă că agentul se află în modul explorare. Pe măsură ce explorează mediul, epsilon scade, iar agentul începe să exploateze mediul. În timpul explorării, cu fiecare iterație, agentul devine mai încrezător în estimarea valorilor Q

Imagine de autor
În exemplul cu lacul înghețat, agentul nu cunoaște mediul, așa că la început ia o acțiune aleatoare (se mișcă în jos). După cum vedem în imaginea de mai sus, Q-Table este actualizat folosind ecuația Bellman.
Măsurarea recompenselor
După ce luăm acțiunea, vom măsura rezultatul și recompensa.
- Recompensa pentru atingerea țintei este +1
- Recompensa pentru alegerea drumului greșit (căderea în gaură) este 0
- Recompensa pentru inactivitate sau deplasare pe lacul înghețat este tot 0.
Actualizează Q-Table
Vom actualiza funcția Q(St, At) folosind ecuația. Aceasta folosește valorile Q estimate din episodul anterior, rata de învățare și eroarea de tip Temporal Differences. Eroarea TD este calculată folosind recompensa imediată, recompensa viitoare maximă așteptată, actualizată cu discount, și vechea valoare estimată Q.
Procesul se repetă de mai multe ori până când Q-Table este actualizat, iar funcția de valoare Q este maximizată.

Imagine de autor | Vizualizări ale ecuației de la Thomas Simonini
La început, agentul explorează mediul pentru a actualiza Q-table. Iar când Q-Table este gata, agentul începe să exploateze și să ia decizii mai bune. 
Imagine de autor
În cazul lacului înghețat, agentul va învăța să ia drumul cel mai scurt pentru a ajunge la țintă și să evite să sară în găuri.
Tutorial Q-Learning în Python
În această secțiune, vom construi modelul nostru Q-learning de la zero folosind mediul Gymnasium, Pygame și Numpy. Tutorialul în Python este o versiune modificată a Notebok-ului de Thomas Simonini. Include inițializarea mediului și a Q-Table, definirea politicii greedy, setarea hiperparametrilor, crearea și rularea buclei de antrenare și a evaluării, și vizualizarea rezultatelor.
Dacă întâmpini probleme la crearea și rularea buclei de antrenare, poți verifica sursa de cod cu rezultatul.
Configurare
Configurează un ecran virtual
Mai întâi vom instala toate dependențele pentru a genera un video de reluare (Gif). Vom avea nevoie de un ecran virtual (pyvirtualdisplay) pentru a reda mediul și a înregistra cadrele.
Notă: folosind %%capture suprimăm ieșirea celulei Jupyter.
%%capture
!pip install pyglet==1.5.1
!apt install python-opengl
!apt install ffmpeg
!apt install xvfb
!pip3 install pyvirtualdisplay
# Virtual display
from pyvirtualdisplay import Display
virtual_display = Display(visible=0, size=(1400, 900))
virtual_display.start()
Instalează dependențele
Acum vom instala dependențele care ne vor ajuta să creăm, rulăm și evaluăm bucla de antrenare.
- gymnasium: folosit pentru a inițializa mediul FrozenLake-v1.
- pygame: folosit pentru interfața FrozenLake-v1.
- numPy: folosit pentru crearea și gestionarea Q-table.
%%capture
!pip install gymnasium
!pip install pygame
!pip install numpy
!pip install imageio imageio_ffmpeg
Importă pachetele
Acum vom importa bibliotecile necesare.
- Imageio este folosit pentru a crea animația.
- tqdm este folosit pentru barele de progres.
import numpy as np
import gymnasium as gym
import random
import imageio
from tqdm.notebook import trange
Mediul Gymnasium Frozen Lake
Vom crea un mediu 4x4 non-alunecos folosind biblioteca Frozen Lake din Gymnasium.
- Există două versiuni de grilă, „4x4” și „8x8”.
- Dacă
is_slippery=True, agentul s-ar putea să nu se miște în direcția dorită din cauza naturii alunecoase a lacului înghețat.
După inițializarea mediului, vom face o analiză a mediului.
env = gym.make("FrozenLake-v1",map_name="4x4",is_slippery=False)
print("Observation Space", env.observation_space)
print("Sample observation", env.observation_space.sample()) # display a random observation
Există 16 spații unice în mediu afișate în poziții aleatoare.
Observation Space Discrete(16)
Sample observation 15
Hai să aflăm numărul de acțiuni și să afișăm o acțiune aleatoare.
Spațiul de acțiuni:
- 0: mișcare la stânga
- 1: mișcare în jos
- 2: mișcare la dreapta
- 3: mișcare în sus
Funcția de recompensă:
- Atingerea țintei: +1
- Căderea în gaură: 0
- Rămânerea pe lacul înghețat: 0
print("Action Space Shape", env.action_space.n)
print("Action Space Sample", env.action_space.sample())
Action Space Shape 4
Action Space Sample 1
Creează și inițializează Q-table
Q-Table are coloanele ca acțiuni și rândurile ca stări. Putem folosi Gymnasium pentru a afla spațiul de acțiuni și spațiul de stări. Apoi vom folosi aceste informații pentru a crea Q-Table.
state_space = env.observation_space.n
print("There are ", state_space, " possible states")
action_space = env.action_space.n
print("There are ", action_space, " possible actions")
There are 16 possible states
There are 4 possible actions
Pentru inițializarea Q-Table, vom crea un array Numpy din spațiul de stări și spațiul de acțiuni. Vom crea un array 16 X 4.
def initialize_q_table(state_space, action_space):
Qtable = np.zeros((state_space, action_space))
return Qtable
Qtable_frozenlake = initialize_q_table(state_space, action_space)
Politica epsilon-greedy
În secțiunea anterioară am învățat despre strategia epsilon greedy care gestionează compromisurile dintre explorare și exploatare. Cu o probabilitate de 1 - ɛ, facem exploatare, iar cu probabilitatea ɛ, facem explorare.
În epsilon_greedy_policy vom:
- Genera un număr aleator între 0 și 1.
- Dacă numărul aleator este mai mare decât epsilon, vom face exploatare. Asta înseamnă că agentul va lua acțiunea cu cea mai mare valoare pentru o stare dată.
- Altfel, vom face explorare (luând o acțiune aleatoare).
def epsilon_greedy_policy(Qtable, state, epsilon):
random_int = random.uniform(0,1)
if random_int > epsilon:
action = np.argmax(Qtable[state])
else:
action = env.action_space.sample()
return action
Definește politica greedy
Acum știm că Q-learning este un algoritm off-policy, ceea ce înseamnă că politica de luare a acțiunii și politica de actualizare sunt diferite.
În acest exemplu, politica Epsilon Greedy este politica de acțiune, iar politica Greedy este politica de actualizare.
Politica Greedy va fi și politica finală când agentul este antrenat. Este folosită pentru a selecta cea mai mare valoare stare-acțiune din Q-Table.
def greedy_policy(Qtable, state):
action = np.argmax(Qtable[state])
return action
Hiperparametrii modelului
Acești hiperparametri sunt folosiți în bucla de antrenare, iar reglarea lor fină îți va oferi rezultate mai bune.
Agentul trebuie să exploreze suficient spațiul de stări pentru a învăța o aproximare bună a valorilor; avem nevoie de o scădere progresivă a lui epsilon. Dacă rata de scădere este mare, agentul s-ar putea bloca, deoarece nu a explorat suficient spațiul de stări.
- Există 10.000 de episoade de antrenare și 100 de evaluare.
- Rata de învățare este 0,7.
- Folosim „FrozenLake-v1” ca mediu, cu 99 de pași maximi per episod.
- Gamma (rata de discount) este 0,95.
- eval_seed: seed de evaluare pentru mediu.
- Probabilitatea epsilon de explorare la start este 1,0, iar probabilitatea minimă va fi 0,05.
- Rata de decădere exponențială pentru probabilitatea epsilon este 0,0005.
# Training parameters
n_training_episodes = 10000
learning_rate = 0.7
# Evaluation parameters
n_eval_episodes = 100
# Environment parameters
env_id = "FrozenLake-v1"
max_steps = 99
gamma = 0.95
eval_seed = []
# Exploration parameters
max_epsilon = 1.0
min_epsilon = 0.05
decay_rate = 0.0005
Antrenarea modelului
În bucla de antrenare, vom:
- Crea o buclă pentru episoadele de antrenare.
- Mai întâi vom reduce epsilon. Avem nevoie de tot mai puțină explorare și de mai multă exploatare cu fiecare episod.
- Reseta mediul.
- Crea o buclă imbricată pentru numărul maxim de pași.
- Alege acțiunea folosind politica epsilon greedy.
- Fă acțiunea (At) și observă recompensa așteptată (Rt+1) și starea (St+1).
- Ia acțiunea (a) și observă starea rezultată (s') și recompensa (r).
- Actualizează funcția Q folosind formula.
- Dacă
done= True, încheie episodul și întrerupe bucla. - În final, schimbă starea curentă în noua stare.
- După ce toate episoadele de antrenare se încheie, funcția va returna Q-Table actualizat.
def train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable):
for episode in trange(n_training_episodes):
epsilon = min_epsilon + (max_epsilon - min_epsilon)*np.exp(-decay_rate*episode)
# Reset the environment
state = env.reset()
step = 0
done = False
# repeat
for step in range(max_steps):
action = epsilon_greedy_policy(Qtable, state, epsilon)
new_state, reward, done, info = env.step(action)
Qtable[state][action] = Qtable[state][action] + learning_rate * (reward + gamma * np.max(Qtable[new_state]) - Qtable[state][action])
# If done, finish the episode
if done:
break
# Our state is the new state
state = new_state
return Qtable
Ne-au trebuit 3 secunde pentru a finaliza 10.000 de episoade de antrenare.
Qtable_frozenlake = train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable_frozenlake)

După cum se vede, Q-Table antrenat are valori, iar agentul va folosi acum aceste valori pentru a naviga mediul și a atinge obiectivul.
Qtable_frozenlake
array([[0.73509189, 0.77378094, 0.77378094, 0.73509189],
[0.73509189, 0. , 0.81450625, 0.77378094],
[0.77378094, 0.857375 , 0.77378094, 0.81450625],
[0.81450625, 0. , 0.77378094, 0.77378094],
[0.77378094, 0.81450625, 0. , 0.73509189],
[0. , 0. , 0. , 0. ],
[0. , 0.9025 , 0. , 0.81450625],
[0. , 0. , 0. , 0. ],
[0.81450625, 0. , 0.857375 , 0.77378094],
[0.81450625, 0.9025 , 0.9025 , 0. ],
[0.857375 , 0.95 , 0. , 0.857375 ],
[0. , 0. , 0. , 0. ],
[0. , 0. , 0. , 0. ],
[0. , 0.9025 , 0.95 , 0.857375 ],
[0.9025 , 0.95 , 1. , 0.9025 ],
[0. , 0. , 0. , 0. ]])
Evaluare
evaluate_agent rulează pentru n_eval_episodes episoade și returnează media și abaterea standard a recompensei.
- În buclă, vom verifica mai întâi dacă există un seed de evaluare. Dacă nu, resetăm mediul fără seed.
- Bucla imbricată va rula până la max_steps.
- Agentul va lua acțiunea cu recompensa viitoare maximă așteptată într-o stare dată folosind Q-Table.
- Calculează recompensa.
- Schimbă starea.
- Dacă s-a terminat (agentul cade în gaură sau ținta a fost atinsă), întrerupe bucla.
- Adaugă rezultatele.
- La final, vom folosi aceste rezultate pentru a calcula media și abaterea standard.
def evaluate_agent(env, max_steps, n_eval_episodes, Q, seed):
episode_rewards = []
for episode in range(n_eval_episodes):
if seed:
state = env.reset(seed=seed[episode])
else:
state = env.reset()
step = 0
done = False
total_rewards_ep = 0
for step in range(max_steps):
# Take the action (index) that have the maximum reward
action = np.argmax(Q[state][:])
new_state, reward, done, info = env.step(action)
total_rewards_ep += reward
if done:
break
state = new_state
episode_rewards.append(total_rewards_ep)
mean_reward = np.mean(episode_rewards)
std_reward = np.std(episode_rewards)
return mean_reward, std_reward
După cum poți vedea, am obținut scorul perfect cu abatere standard zero. Asta înseamnă că agentul nostru a ajuns la țintă în toate cele 100 de episoade.
# Evaluate our Agent
mean_reward, std_reward = evaluate_agent(env, max_steps, n_eval_episodes, Qtable_frozenlake, eval_seed)
print(f"Mean_reward={mean_reward:.2f} +/- {std_reward:.2f}")
Mean_reward=1.00 +/- 0.00
Vizualizarea rezultatului
Până acum am lucrat cu numere, iar pentru a oferi o demonstrație, trebuie să creăm un Gif animat al agentului de la start până când ajunge la țintă.
- Mai întâi vom crea starea resetând mediul cu un întreg aleator 0-500.
- Randăm mediul folosind rdb_array pentru a crea un array de imagini.
- Apoi adăugăm
imgla array-ulimages. - În buclă, vom face pasul folosind Q-Table și vom reda imaginea pentru fiecare pas.
- La final, vom folosi acest array și imageio pentru a crea un Gif cu un cadru pe secundă.
def record_video(env, Qtable, out_directory, fps=1):
images = []
done = False
state = env.reset(seed=random.randint(0,500))
img = env.render(mode='rgb_array')
images.append(img)
while not done:
# Take the action (index) that have the maximum expected future reward given that state
action = np.argmax(Qtable[state][:])
state, reward, done, info = env.step(action) # We directly put next_state = state for recording logic
img = env.render(mode='rgb_array')
images.append(img)
imageio.mimsave(out_directory, [np.array(img) for i, img in enumerate(images)], fps=fps)
Dacă ești într-un notebook Jupyter, poți afișa Gif-ul folosind funcția Image din IPython.display.
video_path="/content/replay.gif"
video_fps=1
record_video(env, Qtable_frozenlake, video_path, video_fps)
from IPython.display import Image
Image('./replay.gif')
Acum poți să împarți aceste rezultate cu colegii tăi sau să le postezi pe rețelele sociale.
Întrebări frecvente despre Q-Learning
Care este dezavantajul Q-learning?
Procesul de învățare în Q-learning este costisitor pentru agent, mai ales la început. De ce? Pentru a converge către politica optimă, fiecare pereche stare-acțiune este vizitată frecvent.
De ce se numește Q-learning Q-learning?
În Q-learning, „Q” vine de la quality (calitate). Reprezintă cât de utilă este o acțiune dată pentru a obține recompense viitoare, fiind folosită pentru a crea o hartă a stărilor și acțiunilor pentru a maximiza recompensele așteptate.
De ce este Q-Learning off-policy?
În Q-learning, politica actualizată este diferită de politica de comportament (acțiune), motiv pentru care este numit algoritm off-policy.
Converge întotdeauna Q-learning?
Da. În timpul antrenării, algoritmul converge întotdeauna către politica optimă.
De ce avem nevoie de deep Q-learning?
Q-learning este un algoritm simplu conceput pentru un mediu mai mic și discret. În cazul unui mediu mai mare, vom avea nevoie de un Q-table uriaș de stări și acțiuni, care va necesita memorie și calcul mai mare pentru antrenare. În schimb, Deep Q-learning înlocuiește Q-table cu o rețea neurală pentru a gestiona medii mari care implică acțiuni și stări continue.