course

Uczenie ze wzmocnieniem (RL) to część ekosystemu uczenia maszynowego, w której agent uczy się poprzez interakcję ze środowiskiem, aby wypracować optymalną strategię osiągania celów. Różni się to od nadzorowanych algorytmów uczenia maszynowego, gdzie musimy wczytać i przetwarzać dane. Uczenie ze wzmocnieniem nie wymaga danych wejściowych — zamiast tego uczy się ze środowiska i systemu nagród, by podejmować lepsze decyzje.
Na przykład w grze Mario, jeśli postać wykona losową akcję (np. ruch w lewo), może na jej podstawie otrzymać nagrodę. Po wykonaniu akcji agent (Mario) znajduje się w nowym stanie i proces powtarza się aż do momentu, gdy postać dotrze do końca planszy lub zginie.
Ten epizod będzie powtarzany wielokrotnie, aż Mario nauczy się poruszać po środowisku, maksymalizując nagrody.

Grafika: autor
Uczenie ze wzmocnieniem można rozbić na pięć prostych kroków:
- Agent znajduje się w stanie zerowym w danym środowisku.
- Podejmie akcję na podstawie określonej strategii.
- Otrzyma nagrodę lub karę w zależności od tej akcji.
- Uczy się na podstawie wcześniejszych ruchów i optymalizuje strategię.
- Proces powtarza się, aż znajdziemy optymalną strategię.
Dowiedz się więcej z naszego samouczka Wprowadzenie do uczenia ze wzmocnieniem. Poznasz dokładniej, jak działa RL, wraz z przykładami kodu.
W tym samouczku poznamy Q-learning i zrozumiemy, dlaczego potrzebujemy Deep Q-learningu. Co więcej, nauczysz się tworzyć i trenować algorytmy Q-learningu od podstaw, używając Numpy i Gymnasium.
Uwaga: jeśli dopiero zaczynasz z uczeniem maszynowym, polecamy ścieżkę kariery Machine Learning Scientist with Python, aby lepiej zrozumieć uczenie ze wzmocnieniem i Q-learning.
Czym jest Q-learning?
Q-learning to bezmodelowy, oparty na wartościach, off-policy algorytm, który znajduje najlepszą sekwencję akcji na podstawie bieżącego stanu agenta. „Q” oznacza jakość. Jakość reprezentuje, jak cenna jest dana akcja w maksymalizacji przyszłych nagród.
Algorytmy model-based używają funkcji przejścia i nagrody do oszacowania optymalnej polityki i zbudowania modelu. Z kolei algorytmy model-free uczą się konsekwencji swoich działań z doświadczenia, bez funkcji przejścia i nagrody.
Metoda value-based trenuje funkcję wartości, aby nauczyć się, który stan jest bardziej wartościowy, i podjąć odpowiednią akcję. Natomiast metody policy-based trenują bezpośrednio politykę, aby nauczyć się, jaką akcję podjąć w danym stanie.
W podejściu off-policy algorytm ocenia i aktualizuje politykę inną niż ta, której używa do podejmowania akcji. Dla kontrastu, algorytm on-policy ocenia i ulepsza tę samą politykę, której używa do działania.
Kluczowe pojęcia w Q-learningu
Zanim przejdziemy do działania Q-learningu, musimy poznać kilka przydatnych pojęć, by zrozumieć jego podstawy.
- Stany (s): bieżąca pozycja agenta w środowisku.
- Akcja (a): krok wykonany przez agenta w określonym stanie.
- Nagrody: za każdą akcję agent otrzymuje nagrodę lub karę.
- Epizody: koniec etapu, gdy agent nie może podjąć nowej akcji. Dzieje się tak, gdy agent osiągnął cel lub poniósł porażkę.
- Q(St+1, a): oczekiwana optymalna wartość Q wykonania akcji w danym stanie.
- Q(St, At): bieżące oszacowanie Q(St+1, a).
- Tablica Q: agent utrzymuje tablicę Q z zestawami stanów i akcji.
- Różnice czasowe (TD): służą do oszacowania oczekiwanej wartości Q(St+1, a) przy użyciu bieżącego i poprzedniego stanu oraz akcji.
Jak działa Q-learning?
Poznamy szczegóły działania Q-learningu na przykładzie zamarzniętego jeziora. W tym środowisku agent musi przejść przez zamarznięte jezioro od startu do celu, nie wpadając w dziury. Najlepszą strategią jest dotrzeć do celu najkrótszą ścieżką.

Gif: autor
Tablica Q
Agent użyje tablicy Q, aby podjąć jak najlepszą akcję na podstawie oczekiwanej nagrody dla każdego stanu w środowisku. Mówiąc prosto, tablica Q to struktura danych z zestawami akcji i stanów, a do aktualizacji wartości w tej tablicy używamy algorytmu Q-learning.
Funkcja Q
Funkcja Q wykorzystuje równanie Bellmana i przyjmuje jako wejście stan (s) i akcję (a). Równanie upraszcza obliczanie wartości stanów i wartości stan-akcja. 
Grafika z freecodecamp.org
Algorytm Q-learning

Grafika: autor
Inicjalizacja tablicy Q
Najpierw zainicjalizujemy tablicę Q. Zbudujemy ją z kolumnami odpowiadającymi liczbie akcji i wierszami odpowiadającymi liczbie stanów.
W naszym przykładzie postać może poruszać się w górę, dół, w lewo i w prawo. Mamy cztery możliwe akcje i cztery stany (start, bezczynność, zła ścieżka i koniec). Złą ścieżkę możesz też rozumieć jako wpadnięcie do dziury. Zainicjalizujemy tablicę Q wartościami równymi 0.

Grafika: autor
Wybór akcji
Drugi krok jest dość prosty. Na początku agent wybierze losową akcję (dół lub prawo), a przy drugim przebiegu użyje zaktualizowanej tablicy Q do wyboru akcji.
Wykonanie akcji
Wybór akcji i jej wykonanie będą się powtarzać wielokrotnie, aż pętla treningowa się zakończy. Pierwsza akcja i stan są wybierane z użyciem tablicy Q. W naszym przypadku wszystkie wartości w tablicy Q są zerowe.
Następnie agent wykona ruch w dół i zaktualizuje tablicę Q z użyciem równania Bellmana. Z każdym ruchem będziemy aktualizować wartości w tablicy Q i wykorzystywać je do wyznaczenia najlepszego kursu działania.
Początkowo agent jest w trybie eksploracji i wybiera losowe akcje, by poznać środowisko. Strategia epsilon-greedy to prosty sposób równoważenia eksploracji i eksploatacji. Epsilon oznacza prawdopodobieństwo wyboru eksploracji; gdy jest mniejsze, częściej eksploatujemy.
Na starcie wskaźnik epsilon jest wysoki, czyli agent eksploruje. W trakcie poznawania środowiska epsilon maleje i agent zaczyna je eksploatować. Podczas eksploracji z każdą iteracją agent coraz pewniej szacuje wartości Q.

Grafika: autor
W przykładzie z zamarzniętym jeziorem agent nie zna środowiska, więc na start wykonuje losową akcję (ruch w dół). Jak widać na obrazku powyżej, tablica Q jest aktualizowana przy użyciu równania Bellmana.
Pomiar nagród
Po wykonaniu akcji zmierzymy wynik i nagrodę.
- Nagroda za dotarcie do celu: +1
- Nagroda za złą ścieżkę (wpadnięcie do dziury): 0
- Nagroda za bezczynność lub poruszanie się po lodzie: 0.
Aktualizacja tablicy Q
Zaktualizujemy funkcję Q(St, At) przy użyciu równania. Wykorzystuje ono oszacowane wartości Q z poprzedniego epizodu, współczynnik uczenia oraz błąd różnic czasowych. Błąd TD oblicza się z użyciem natychmiastowej nagrody, zdyskontowanej maksymalnej oczekiwanej przyszłej nagrody oraz wcześniejszego oszacowania Q.
Proces powtarza się wielokrotnie, aż tablica Q zostanie zaktualizowana, a funkcja Q zmaksymalizowana.

Grafika: autor | Wizualizacja równań: Thomas Simonini
Na początku agent eksploruje środowisko, aby zaktualizować tablicę Q. Gdy tablica jest gotowa, agent zaczyna eksploatować i podejmować lepsze decyzje. 
Grafika: autor
W przypadku zamarzniętego jeziora agent nauczy się wybierać najkrótszą ścieżkę do celu i unikać dziur.
Samouczek Q-learning w Pythonie
W tej sekcji zbudujemy nasz model Q-learning od podstaw, używając środowiska Gymnasium, Pygame i Numpy. Ten samouczek w Pythonie to zmodyfikowana wersja notatnika Thomasa Simoniniego. Obejmuje inicjalizację środowiska i tablicy Q, zdefiniowanie polityki greedy, ustawienie hiperparametrów, stworzenie i uruchomienie pętli treningowej oraz ewaluacji, a także wizualizację wyników.
Jeśli masz problemy z utworzeniem i uruchomieniem pętli treningowej, możesz zajrzeć do kodu źródłowego z wynikami.
Przygotowanie środowiska
Skonfiguruj wirtualny ekran
Najpierw zainstalujemy wszystkie zależności potrzebne do wygenerowania nagrania (GIF). Będziemy potrzebować wirtualnego ekranu (pyvirtualdisplay), aby renderować środowisko i nagrywać klatki.
Uwaga: używając %%capture wyciszamy wynik komórki 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()
Zainstaluj zależności
Teraz zainstalujemy zależności, które pomogą nam utworzyć, uruchomić i ocenić pętlę treningową.
- gymnasium: używany do inicjalizacji środowiska FrozenLake-v1.
- pygame: używany do interfejsu FrozenLake-v1.
- numPy: używany do tworzenia i obsługi tablicy Q.
%%capture
!pip install gymnasium
!pip install pygame
!pip install numpy
!pip install imageio imageio_ffmpeg
Import pakietów
Zaimportujemy teraz wymagane biblioteki.
- Imageio służy do tworzenia animacji.
- tqdm służy do pasków postępu.
import numpy as np
import gymnasium as gym
import random
import imageio
from tqdm.notebook import trange
Środowisko Frozen Lake w Gymnasium
Stworzymy nieśliski świat 4x4, korzystając z biblioteki Frozen Lake w Gymnasium.
- Dostępne są dwie siatki: „4x4” i „8x8”.
- Jeśli
is_slippery=True, agent może nie poruszać się w zamierzonym kierunku ze względu na śliską powierzchnię zamarzniętego jeziora.
Po zainicjalizowaniu środowiska przeprowadzimy jego analizę.
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
Istnieje 16 unikalnych pól w środowisku wyświetlanych w losowych pozycjach.
Observation Space Discrete(16)
Sample observation 15
Sprawdźmy liczbę akcji i wyświetlmy losową akcję.
Przestrzeń akcji:
- 0: ruch w lewo
- 1: ruch w dół
- 2: ruch w prawo
- 3: ruch w górę
Funkcja nagrody:
- Dotarcie do celu: +1
- Wpadnięcie do dziury: 0
- Pozostanie na lodzie: 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
Utwórz i zainicjalizuj tablicę Q
Tablica Q ma kolumny będące akcjami i wiersze będące stanami. Możemy użyć Gymnasium, aby znaleźć przestrzeń akcji i stanów. Następnie wykorzystamy te informacje do stworzenia tablicy Q.
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
Aby zainicjalizować tablicę Q, utworzymy tablicę Numpy o wymiarach przestrzeni stanów i akcji. Utworzymy macierz 16 × 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)
Polityka epsilon-greedy
W poprzedniej sekcji poznaliśmy strategię epsilon-greedy, która równoważy eksplorację i eksploatację. Z prawdopodobieństwem 1 − ɛ eksploatujemy, a z prawdopodobieństwem ɛ eksplorujemy.
W epsilon_greedy_policy:
- Generujemy liczbę losową z przedziału 0–1.
- Jeśli liczba jest większa niż epsilon, eksploatujemy. Oznacza to, że agent wybierze akcję o najwyższej wartości dla danego stanu.
- W przeciwnym razie eksplorujemy (wybieramy losową akcję).
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
Zdefiniuj politykę greedy
Wiesz już, że Q-learning to algorytm off-policy, co oznacza, że polityka podejmowania akcji i aktualizacji funkcji są różne.
W tym przykładzie polityka epsilon-greedy jest polityką działania, a polityka greedy — polityką aktualizacji.
Polityka greedy będzie też polityką końcową po wytrenowaniu agenta. Służy do wyboru najwyższej wartości stan–akcja z tablicy Q.
def greedy_policy(Qtable, state):
action = np.argmax(Qtable[state])
return action
Hiperparametry modelu
Te hiperparametry są używane w pętli treningowej, a ich dostrajanie może dać lepsze wyniki.
Agent musi wystarczająco eksplorować przestrzeń stanów, by nauczyć się dobrego przybliżenia wartości; potrzebujemy więc stopniowego spadku epsilon. Jeśli tempo spadku jest zbyt duże, agent może utknąć, bo nie poznał wystarczająco przestrzeni stanów.
- Jest 10 000 epizodów treningowych i 100 epizodów ewaluacyjnych.
- Współczynnik uczenia to 0,7.
- Używamy środowiska "FrozenLake-v1" z 99 maksymalnymi krokami na epizod.
- Gamma (stopa dyskontowa) to 0,95.
- eval_seed: ziarno ewaluacji dla środowiska.
- Początkowe prawdopodobieństwo eksploracji epsilon wynosi 1,0, a minimalne 0,05.
- Wykładnicze tempo spadku dla epsilon to 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
Trenowanie modelu
W pętli treningowej:
- Utwórz pętlę po epizodach treningowych.
- Najpierw zmniejszamy epsilon. Z każdym epizodem potrzebujemy coraz mniej eksploracji, a więcej eksploatacji.
- Resetujemy środowisko.
- Tworzymy zagnieżdżoną pętlę po maksymalnych krokach.
- Wybieramy akcję przy użyciu polityki epsilon-greedy.
- Wykonujemy akcję (At) i obserwujemy oczekiwaną nagrodę (Rt+1) oraz stan (St+1).
- Wykonujemy akcję (a) i obserwujemy wynikowy stan (s') oraz nagrodę (r).
- Aktualizujemy funkcję Q przy użyciu wzoru.
- Jeśli
done= True, kończymy epizod i przerywamy pętlę. - Na koniec zmieniamy bieżący stan na nowy.
- Po zakończeniu wszystkich epizodów treningowych funkcja zwróci zaktualizowaną tablicę Q.
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
Zajęło nam to 3 sekundy, aby ukończyć 10 000 epizodów treningowych.
Qtable_frozenlake = train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable_frozenlake)

Jak widać, wytrenowana tablica Q ma wartości i agent użyje ich teraz do poruszania się po środowisku i osiągnięcia celu.
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. ]])
Ewaluacja
Funkcja evaluate_agent uruchamia się przez n_eval_episodes epizodów i zwraca średnią oraz odchylenie standardowe nagrody.
- W pętli najpierw sprawdzimy, czy ustawiono ziarno ewaluacji. Jeśli nie, resetujemy środowisko bez ziarna.
- Pętla zagnieżdżona będzie działać do max_steps.
- Agent podejmie akcję o maksymalnej oczekiwanej przyszłej nagrodzie w danym stanie, korzystając z tablicy Q.
- Obliczamy nagrodę.
- Zmieniamy stan.
- Jeśli done (agent wpadł do dziury lub osiągnął cel), przerywamy pętlę.
- Dodajemy wyniki do listy.
- Na końcu z tych wyników obliczamy średnią i odchylenie standardowe.
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
Jak widać, otrzymaliśmy idealny wynik z zerowym odchyleniem standardowym. Oznacza to, że nasz agent osiągnął cel we wszystkich 100 epizodach.
# 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
Wizualizacja wyniku
Do tej pory pracowaliśmy na liczbach, a do demonstracji potrzebujemy stworzyć animowany GIF agenta od startu do osiągnięcia celu.
- Najpierw utworzymy stan, resetując środowisko z losową liczbą całkowitą 0–500.
- Wyrenderujemy środowisko, używając trybu rdb_array, aby utworzyć tablicę obrazu.
- Następnie dołączymy
imgdo tablicyimages. - W pętli będziemy wykonywać kroki przy użyciu tablicy Q i renderować obraz dla każdego kroku.
- Na koniec użyjemy tej tablicy oraz imageio, aby stworzyć GIF w tempie jednej klatki na sekundę.
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)
Jeśli pracujesz w notatniku Jupyter, możesz wyświetlić GIF za pomocą funkcji Image z 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')
Możesz teraz podzielić się tymi wynikami ze współpracownikami i kolegami z klasy albo opublikować je w mediach społecznościowych.
Najczęstsze pytania o Q-learning
Jakie są wady Q-learningu?
Proces uczenia w Q-learningu jest kosztowny dla agenta, zwłaszcza na początku. Dlaczego? Aby zbiec do optymalnej polityki, każda para stan–akcja musi być często odwiedzana.
Dlaczego Q-learning nazywa się Q-learningiem?
W Q-learningu „Q” oznacza jakość. Reprezentuje, jak użyteczna jest dana akcja w osiąganiu przyszłych nagród, co służy do stworzenia mapowania stan–akcja maksymalizującego oczekiwane nagrody.
Dlaczego Q-learning jest off-policy?
W Q-learningu aktualizowana polityka różni się od polityki zachowania (podejmowania akcji), dlatego mówi się, że to algorytm off-policy.
Czy Q-learning zawsze zbiega?
Tak. Podczas treningu algorytm zawsze zbiega do optymalnej polityki.
Dlaczego potrzebujemy deep Q-learningu?
Q-learning to prosty algorytm zaprojektowany dla mniejszych i dyskretnych środowisk. W przypadku dużego środowiska potrzebna byłaby ogromna tablica Q stanów i akcji, która wymagałaby dużo pamięci i mocy obliczeniowej do treningu. Z kolei Deep Q-learning zastępuje tablicę Q siecią neuronową, aby obsługiwać duże środowiska z ciągłymi akcjami i stanami.