|
|
1 month ago | |
|---|---|---|
| .. | ||
| solution | 7 months ago | |
| README.md | 1 month ago | |
| assignment.md | 7 months ago | |
| notebook.ipynb | 12 months ago | |
README.md
CartPole korčuľovanie
Problém, ktorý sme riešili v predchádzajúcej lekcii, sa môže javiť ako odľahčený problém, ktorý nie je naozaj použiteľný v reálnych situáciách. To však nie je pravda, pretože mnohé problémy zo skutočného sveta zdieľajú tento scenár - vrátane hrania šachu alebo Go. Sú podobné, pretože máme šachovnicu s danými pravidlami a diskrétny stav.
Prednáškový kvíz
Úvod
V tejto lekcii aplikujeme rovnaké princípy Q-Learningu na problém s kontinuálnym stavom, teda stavom, ktorý je určený jedným alebo viacerými reálnymi číslami. Budeme riešiť nasledujúci problém:
Problém: Ak chce Peter uniknúť vlkovi, musí sa vedieť pohybovať rýchlejšie. Ukážeme si, ako sa Peter môže naučiť korčuľovať, konkrétne ako udržať rovnováhu, pomocou Q-Learningu.
Peter a jeho priatelia sú kreatívni, aby unikli vlkovi! Obrázok od Jen Looper
Použijeme zjednodušenú verziu balančného problému známeho ako CartPole. V svete cartpole máme horizontálny posuvník, ktorý sa môže pohybovať doľava alebo doprava, a cieľom je vyvážiť vertikálnu tyč na vrchu posuvníka.
Predpoklady
V tejto lekcii použijeme knižnicu s názvom OpenAI Gym na simuláciu rôznych prostredí. Môžete spustiť kód tejto lekcie lokálne (napr. z Visual Studio Code), v takom prípade sa simulácia otvorí v novom okne. Pri spúšťaní kódu online možno bude potrebné urobiť niektoré úpravy kódu, ako je popísané tu.
OpenAI Gym
V predchádzajúcej lekcii nám pravidlá hry a stav definovala trieda Board, ktorú sme si definovali sami. Tu použijeme špeciálne simulačné prostredie, ktoré bude simulovať fyziku za balančnou tyčou. Jedným z najpopulárnejších simulačných prostredí na trénovanie algoritmov posilňovaného učenia je Gym, ktorý spravuje OpenAI. Použitím tohto gym môžeme vytvárať rôzne prostredia od simulácie cartpole až po Atari hry.
Poznámka: Ďalšie dostupné prostredia OpenAI Gym môžete vidieť tu.
Najskôr si nainštalujme gym a naimportujme potrebné knižnice (kódový blok 1):
import sys
!{sys.executable} -m pip install gym
import gym
import matplotlib.pyplot as plt
import numpy as np
import random
Cvičenie – inicializácia prostredia cartpole
Na prácu s problémom balančnej tyče musíme inicializovať zodpovedajúce prostredie. Každé prostredie je spojené s:
-
Prieskumný priestor (Observation space), ktorý definuje štruktúru informácií, ktoré dostávame z prostredia. Pre cartpole problém dostávame polohu tyče, rýchlosť a niektoré ďalšie hodnoty.
-
Akčný priestor (Action space), ktorý definuje možné akcie. V našom prípade je akčný priestor diskrétny a skladá sa z dvoch akcií – doľava a doprava. (kódový blok 2)
-
Na inicializáciu zadajte nasledujúci kód:
env = gym.make("CartPole-v1") print(env.action_space) print(env.observation_space) print(env.action_space.sample())
Aby sme videli, ako prostredie funguje, spustime krátku simuláciu na 100 krokov. Na každom kroku vykonáme jednu z akcií – v tejto simulácii náhodne vyberieme akciu z action_space.
-
Spustite kód nižšie a pozrite sa, čo z toho vyjde.
✅ Pamätajte, že je vhodnejšie spustiť tento kód na lokálnej inštalácii Pythonu! (kódový blok 3)
env.reset() for i in range(100): env.render() env.step(env.action_space.sample()) env.close()Mali by ste vidieť niečo podobné tomuto obrázku:
-
Počas simulácie potrebujeme získať pozorovania, aby sme sa rozhodli, ako konať. Funkcia
stepvráti aktuálne pozorovanie, odmenu a príznakdone, ktorý označuje, či má zmysel v simulácii pokračovať alebo nie: (kódový blok 4)env.reset() done = False while not done: env.render() obs, rew, done, info = env.step(env.action_space.sample()) print(f"{obs} -> {rew}") env.close()Výstup v notebooku bude vyzerať nejako takto:
[ 0.03403272 -0.24301182 0.02669811 0.2895829 ] -> 1.0 [ 0.02917248 -0.04828055 0.03248977 0.00543839] -> 1.0 [ 0.02820687 0.14636075 0.03259854 -0.27681916] -> 1.0 [ 0.03113408 0.34100283 0.02706215 -0.55904489] -> 1.0 [ 0.03795414 0.53573468 0.01588125 -0.84308041] -> 1.0 ... [ 0.17299878 0.15868546 -0.20754175 -0.55975453] -> 1.0 [ 0.17617249 0.35602306 -0.21873684 -0.90998894] -> 1.0Pozorovací vektor, ktorý sa vracia na každom kroku simulácie, obsahuje nasledujúce hodnoty:
- Poloha vozíka
- Rýchlosť vozíka
- Uhol tyče
- Rýchlosť rotácie tyče
-
Zistite minimum a maximum týchto hodnôt: (kódový blok 5)
print(env.observation_space.low) print(env.observation_space.high)Tiež si môžete všimnúť, že hodnota odmeny pri každom kroku simulácie je vždy 1. Je to preto, že naším cieľom je prežiť čo najdlhšie, teda udržať tyč v primerane vertikálnej polohe čo najdlhšie.
✅ Simulácia CartPole je považovaná za vyriešenú, ak dosiahneme priemernú odmenu 195 za 100 po sebe nasledujúcich pokusov.
Diskretizácia stavu
V Q-Learningu potrebujeme vytvoriť Q-tabulu, ktorá definuje, čo robiť v každom stave. Aby sme to mohli urobiť, potrebujeme, aby bol stav diskrétny, presnejšie, aby obsahoval konečný počet diskrétnych hodnôt. Preto musíme nejako diskretizovať naše pozorovania a namapovať ich na konečnú množinu stavov.
Existuje niekoľko spôsobov, ako to môžeme robiť:
- Rozdeliť do intervalov (bins). Ak poznáme interval určitej hodnoty, môžeme tento interval rozdeliť na niekoľko intervalov (bins) a následne hodnotu nahradiť číslom intervalu, do ktorého patrí. To sa dá urobiť pomocou numpy metódy
digitize. V tomto prípade presne poznáme veľkosť stavu, pretože bude závisieť od počtu intervalov, ktoré vyberieme pre digitalizáciu.
✅ Môžeme použiť lineárnu interpoláciu na prenesenie hodnôt do nejakého konečného intervalu (napr. od -20 do 20) a potom čísla previesť na celé čísla zaokrúhlením. Toto nám dá trochu menej kontroly nad veľkosťou stavu, najmä ak nepoznáme presné rozsahy vstupných hodnôt. Napríklad v našom prípade 2 zo 4 hodnôt nemajú horné alebo dolné hranice, čo by mohlo viesť k nekonečnému počtu stavov.
V našom príklade použijeme druhý prístup. Ako si neskôr všimnete, napriek nedefinovaným horným a dolným hraniciam tieto hodnoty zriedkavo nadobúdajú hodnoty mimo určitých konečných intervalov, takže stavy s extrémnymi hodnotami budú veľmi zriedkavé.
-
Tu je funkcia, ktorá vezme pozorovanie nášho modelu a vráti 4-ticu celých čísel: (kódový blok 6)
def discretize(x): return tuple((x/np.array([0.25, 0.25, 0.01, 0.1])).astype(np.int)) -
Preskúmajme aj druhý spôsob diskretizácie pomocou intervalov: (kódový blok 7)
def create_bins(i,num): return np.arange(num+1)*(i[1]-i[0])/num+i[0] print("Sample bins for interval (-5,5) with 10 bins\n",create_bins((-5,5),10)) ints = [(-5,5),(-2,2),(-0.5,0.5),(-2,2)] # intervaly hodnôt pre každý parameter nbins = [20,20,10,10] # počet priehradiek pre každý parameter bins = [create_bins(ints[i],nbins[i]) for i in range(4)] def discretize_bins(x): return tuple(np.digitize(x[i],bins[i]) for i in range(4)) -
Teraz spustime krátku simuláciu a pozorujme tieto diskrétne hodnoty prostredia. Kľudne vyskúšajte obe funkcie
discretizeadiscretize_binsa uvidíte, či je rozdiel.✅
discretize_binsvracia číslo intervalu, ktoré je indexované od 0. Preto pre hodnoty vstupnej premennej okolo 0 vracia číslo zo stredu intervalu (10). Vodiscretizesme nerozlišovali rozsah výstupných hodnôt, ktoré mohli byť aj záporné, takže hodnoty stavu nie sú posunuté a 0 zodpovedá 0. (kódový blok 8)env.reset() done = False while not done: #env.render() obs, rew, done, info = env.step(env.action_space.sample()) #print(diskretizované_segmenty(obs)) print(discretize(obs)) env.close()✅ Odinštalujte riadok začínajúci
env.render, ak chcete vidieť, ako prostredie vykonáva simuláciu. Inak môžete simuláciu nechať bežať na pozadí, čo je rýchlejšie. Túto „neviditeľnú“ simuláciu použijeme počas Q-Learning procesu.
Štruktúra Q-tabule
V predchádzajúcej lekcii bol stav jednoduchý pár čísel od 0 do 8, preto bolo pohodlné reprezentovať Q-tabulu ako numpy tenzor s tvarom 8x8x2. Ak použijeme diskretizáciu pomocou intervalov, veľkosť nášho vektorového stavu je tiež známa, takže môžeme použiť rovnaký prístup a reprezentovať stav ako pole tvaru 20x20x10x10x2 (tu je 2 rozmer akčného priestoru a prvé rozmery zodpovedajú počtu intervalov, ktoré sme vybrali pre každý parameter v pozorovacom priestore).
Niekedy však presné rozmery pozorovacieho priestoru nie sú známe. V prípade funkcie discretize nemusíme byť istí, že náš stav zostane v určitých hraniciach, pretože niektoré z pôvodných hodnôt nie sú ohraničené. Preto použijeme trochu iný prístup a budeme reprezentovať Q-tabulu ako slovník.
-
Použite dvojicu (stav, akcia) ako kľúč slovníka a hodnota bude zodpovedať hodnote položky v Q-tabuli. (kódový blok 9)
Q = {} actions = (0,1) def qvalues(state): return [Q.get((state,a),0) for a in actions]Tu tiež definujeme funkciu
qvalues(), ktorá vráti zoznam hodnôt Q-tabule pre daný stav zodpovedajúci všetkým možným akciám. Ak položka v Q-tabuli neexistuje, vrátime 0 ako predvolenú hodnotu.
Začnime Q-Learning
Teraz sme pripravení naučiť Petra, ako udržať rovnováhu!
-
Najprv nastavme niektoré hyperparametre: (kódový blok 10)
# hyperparametre alpha = 0.3 gamma = 0.9 epsilon = 0.90Tu je
alpharýchlosť učenia, ktorá definuje, do akej miery by sme mali upravovať aktuálne hodnoty Q-tabule na každom kroku. V predchádzajúcej lekcii sme začínali s hodnotou 1 a potom sme postupne znižovalialphapočas tréningu. V tomto príklade ju necháme konštantnú, pre jednoduchosť, a môžete si neskôr experimentovať s jej nastavením.gammaje diskontný faktor, ktorý ukazuje, do akej miery by sme mali uprednostňovať budúce odmeny oproti súčasným.epsilonje faktor prieskumu/využitia, ktorý určuje, či by sme mali uprednostniť prieskum pred využívaním alebo naopak. V našom algoritme vepsilonpercentách prípadov vyberieme nasledujúcu akciu podľa hodnôt Q-tabule a v ostatných prípadoch vykonáme náhodnú akciu. To nám umožní preskúmať oblasti vyhľadávacieho priestoru, ktoré sme ešte nevideli.✅ Pokiaľ ide o balančné problémy - voľba náhodnej akcie (prieskum) by bola ako náhodný úder zlým smerom a tyč by sa musela naučiť, ako z týchto „chýb“ obnoviť rovnováhu.
Vylepšiť algoritmus
Môžeme tiež urobiť dve vylepšenia nášho algoritmu z predchádzajúcej lekcie:
-
Vypočítať priemernú kumulatívnu odmenu za určitý počet simulácií. Budeme tlačiť priebeh každých 5000 iterácií a za toto obdobie vyhladia kumulatívnu odmenu. To znamená, že ak dosiahneme viac ako 195 bodov, môžeme považovať problém za vyriešený, dokonca s ešte lepšou kvalitou.
-
Vypočítať maximálny priemerný kumulatívny výsledok,
Qmax, a uložíme Q-tabulu príslušnú k tomuto výsledku. Pri tréningu často uvidíte, že priemerný kumulatívny výsledok začne klesať a my chceme uchovať hodnoty Q-tabule, ktoré zodpovedajú najlepšiemu modelu pozorovanému počas tréningu.
-
Zhromaždite všetky kumulatívne odmeny pri každej simulácii do vektora
rewardspre ďalšie znázornenie. (kódový blok 11)def probs(v,eps=1e-4): v = v-v.min()+eps v = v/v.sum() return v Qmax = 0 cum_rewards = [] rewards = [] for epoch in range(100000): obs = env.reset() done = False cum_reward=0 # == vykonaj simuláciu == while not done: s = discretize(obs) if random.random()<epsilon: # vyťaženie - vyber akciu podľa pravdepodobností v Q-tabulke v = probs(np.array(qvalues(s))) a = random.choices(actions,weights=v)[0] else: # prieskum - náhodne vyber akciu a = np.random.randint(env.action_space.n) obs, rew, done, info = env.step(a) cum_reward+=rew ns = discretize(obs) Q[(s,a)] = (1 - alpha) * Q.get((s,a),0) + alpha * (rew + gamma * max(qvalues(ns))) cum_rewards.append(cum_reward) rewards.append(cum_reward) # == Periodicky vypíš výsledky a vypočítaj priemernú odmenu == if epoch%5000==0: print(f"{epoch}: {np.average(cum_rewards)}, alpha={alpha}, epsilon={epsilon}") if np.average(cum_rewards) > Qmax: Qmax = np.average(cum_rewards) Qbest = Q cum_rewards=[]
Čo si môžete všimnúť z týchto výsledkov:
-
Blízko nášmu cieľu. Sme veľmi blízko dosiahnutia ciela, ktorý je získať 195 kumulatívnych odmien za 100+ po sebe nasledujúcich spustení simulácie, alebo sme ho možno už dosiahli! Aj keď dostaneme menšie čísla, ešte to nevieme, pretože vyhladzujeme cez 5000 spustení a formálne je potrebných len 100.
-
Odmena začína klesať. Niekedy odmena začne klesať, čo znamená, že môžeme „zničiť“ už naučené hodnoty v Q-tabuli novými, ktoré situáciu zhoršujú.
Toto pozorovanie je jasnejšie viditeľné, keď si zobrazíme priebeh tréningu.
Grafický priebeh tréningu
Počas tréningu sme zhromaždili kumulatívnu odmenu v každej iterácii do vektora rewards. Takto to vyzerá, keď vyobrazíme túto hodnotu proti číslu iterácie:
plt.plot(rewards)
Z tohto grafu nemožno nič vyčítať, pretože vzhľadom na povahu stochastického tréningového procesu sa dĺžka tréningových sekvencií veľmi líši. Aby to dávalo väčší zmysel, môžeme vypočítať bežiaci priemer cez sériu experimentov, povedzme 100. To sa dá pohodlne urobiť pomocou np.convolve: (kódový blok 12)
def running_average(x,window):
return np.convolve(x,np.ones(window)/window,mode='valid')
plt.plot(running_average(rewards,100))
Menenie hyperparametrov
Pre stabilnejšie učenie je rozumné počas tréningu niektoré hyperparametre upravovať. Najmä:
-
Pre rýchlosť učenia
alphamôžeme začať s hodnotami blízkymi 1 a postupne parameter znižovať. Postupom času budeme mať dobré pravdepodobnostné hodnoty v Q-tabuli a preto ich budeme upravovať len mierne, nie úplne meniť na nové hodnoty. -
Zvýšiť epsilon. Môžeme postupne zvyšovať
epsilon, aby sme menej preskúmavali a viac využívali. Pravdepodobne je rozumné začať s nízkou hodnotouepsilona postupne ju zvyšovať takmer na 1.
Úloha 1: Hrajte sa s hodnotami hyperparametrov a skúste dosiahnuť vyššiu kumulatívnu odmenu. Dosahujete hodnoty nad 195?
Úloha 2: Na formálne vyriešenie problému potrebujete dosiahnuť priemernú odmenu 195 za 100 po sebe nasledujúcich behov. Merajte to počas tréningu a uistite sa, že ste problém formálne vyriešili!
Vidieť výsledok v akcii
Bolo by zaujímavé skutočne vidieť, ako sa trénovaný model správa. Spustime simuláciu a použime rovnakú stratégiu výberu akcií ako počas tréningu, vzorkujúc podľa pravdepodobnostného rozdelenia v Q-tabulke: (kódový blok 13)
obs = env.reset()
done = False
while not done:
s = discretize(obs)
env.render()
v = probs(np.array(qvalues(s)))
a = random.choices(actions,weights=v)[0]
obs,_,done,_ = env.step(a)
env.close()
Mali by ste vidieť niečo takéto:
🚀Výzva
Úloha 3: Tu sme používali finálnu kópiu Q-tabulky, ktorá nemusí byť najlepšia. Pamätajte, že sme uložili najlepšie fungujúcu Q-tabulku do premennej
Qbest! Skúste ten istý príklad s najlepšie fungujúcou Q-tabulkou tak, že prekopírujeteQbestdoQa uvidíte, či si všimnete rozdiel.
Úloha 4: Tu sme nevyberali najlepšiu akciu v každom kroku, ale vzorkovali sme podľa zodpovedajúceho pravdepodobnostného rozdelenia. Dávalo by väčší zmysel vždy zvoliť najlepšiu akciu, s najvyššou hodnotou Q-tabulky? Toto sa dá urobiť použitím funkcie
np.argmaxna zistenie čísla akcie zodpovedajúcej najvyššej hodnote v Q-tabulke. Implementujte túto stratégiu a uvidíte, či to vylepší balansovanie.
Post-lekčný kvíz
Zadanie
Záver
Teraz sme sa naučili, ako trénovať agentov na dosahovanie dobrých výsledkov len tým, že im poskytneme odmeňovaciu funkciu, ktorá definuje požadovaný stav hry, a tým, že im umožníme inteligentne skúmať vyhľadávací priestor. Úspešne sme aplikovali algoritmus Q-learning v prípadoch diskrétnych a súvislých prostredí, ale s diskrétnymi akciami.
Je dôležité tiež študovať situácie, kde je stav akcie tiež súvislý, a keď je pozorovací priestor oveľa zložitejší, napríklad obraz z obrazovky hry Atari. V týchto problémoch často potrebujeme použiť výkonnejšie metódy strojového učenia, ako sú neurónové siete, aby sme dosiahli dobré výsledky. Témy s vyššou náročnosťou sú predmetom nášho pripravovaného pokročilejšieho kurzu AI.
Vyhlásenie o zodpovednosti: Tento dokument bol preložený pomocou AI prekladateľskej služby Co-op Translator. Hoci sa snažíme o presnosť, vezmite prosím na vedomie, že automatické preklady môžu obsahovať chyby alebo nepresnosti. Pôvodný dokument v jeho natívnom jazyku by mal byť považovaný za autoritatívny zdroj. Pre kritické informácie sa odporúča profesionálny ľudský preklad. Nie sme zodpovední za žiadne nedorozumenia alebo nesprávne interpretácie vyplývajúce z použitia tohto prekladu.




