Probleem, mida me eelmises tunnis lahendasime, võib tunduda mänguline ja mitte eriti elulähedane. Tegelikult see nii ei ole, sest paljud päriselulised probleemid jagavad sama stsenaariumi – näiteks malet või Go-d mängides. Need on sarnased, kuna meil on samuti mängulaud kindlate reeglitega ja diskreetne olek.
Selles tunnis rakendame Q-õppe põhimõtteid probleemile, millel on jätkuv olek, st olek, mida kirjeldavad üks või mitu reaalarvu. Me tegeleme järgmise probleemiga:
Probleem: Kui Peeter tahab hundi eest põgeneda, peab ta liikuma kiiremini. Me näeme, kuidas Peeter saab õppida uisutama, täpsemalt tasakaalu hoidma, kasutades Q-õpet.
Peeter ja tema sõbrad muutuvad loovaks, et hundi eest põgeneda! Pilt: Jen Looper
Kasutame tasakaalu lihtsustatud versiooni, mida tuntakse kui CartPole probleem. CartPole maailmas on meil horisontaalne liugur, mis saab liikuda vasakule või paremale, ja eesmärk on hoida vertikaalset posti liuguri peal tasakaalus.
Selles tunnis kasutame raamatukogu nimega OpenAI Gym, et simuleerida erinevaid keskkondi. Saate selle tunni koodi käivitada lokaalselt (nt Visual Studio Code'is), sel juhul avaneb simulatsioon uues aknas. Kui käivitate koodi veebis, peate võib-olla koodi veidi kohandama, nagu kirjeldatud siin.
Eelmises tunnis määras mängu reeglid ja oleku meie enda defineeritud Board klass. Siin kasutame spetsiaalset simulatsioonikeskkonda, mis simuleerib tasakaalu posti füüsikat. Üks populaarsemaid simulatsioonikeskkondi, mida kasutatakse tugevdusõppe algoritmide treenimiseks, on Gym, mida haldab OpenAI. Selle Gymi abil saame luua erinevaid keskkondi, alates CartPole simulatsioonist kuni Atari mängudeni.
Märkus: Teisi OpenAI Gymi keskkondi saate vaadata siit.
Kõigepealt installime Gymi ja impordime vajalikud teegid (koodiplokk 1):
import sys
!{sys.executable} -m pip install gym
import gym
import matplotlib.pyplot as plt
import numpy as np
import randomCartPole tasakaalu probleemi lahendamiseks peame initsialiseerima vastava keskkonna. Iga keskkond on seotud:
-
Vaatlusruumiga, mis määratleb struktuuri, mille kaudu saame keskkonnast teavet. CartPole probleemi puhul saame posti asukoha, kiiruse ja mõned muud väärtused.
-
Tegevusruumiga, mis määratleb võimalikud tegevused. Meie puhul on tegevusruum diskreetne ja koosneb kahest tegevusest – vasakule ja paremale. (koodiplokk 2)
-
Initsialiseerimiseks sisestage järgmine kood:
env = gym.make("CartPole-v1") print(env.action_space) print(env.observation_space) print(env.action_space.sample())
Et näha, kuidas keskkond töötab, käivitame lühikese simulatsiooni 100 sammu jooksul. Igal sammul anname ühe tegevuse, mida tuleb teha – selles simulatsioonis valime juhuslikult tegevuse action_space hulgast.
-
Käivitage allolev kood ja vaadake, mis juhtub.
✅ Pidage meeles, et eelistatav on käivitada see kood lokaalses Python'i installatsioonis! (koodiplokk 3)
env.reset() for i in range(100): env.render() env.step(env.action_space.sample()) env.close()
Peaksite nägema midagi sarnast sellele pildile:
-
Simulatsiooni ajal peame saama vaatlusi, et otsustada, kuidas tegutseda. Tegelikult tagastab
stepfunktsioon praegused vaatlused, tasu funktsiooni jadonelipu, mis näitab, kas simulatsiooni jätkamine on mõistlik või mitte: (koodiplokk 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()
Näete midagi sellist oma märkmiku väljundis:
[ 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.0Simulatsiooni iga sammu jooksul tagastatud vaatlusvektor sisaldab järgmisi väärtusi:
- Käru asukoht
- Käru kiirus
- Posti nurk
- Posti pöörlemiskiirus
-
Leidke nende numbrite minimaalne ja maksimaalne väärtus: (koodiplokk 5)
print(env.observation_space.low) print(env.observation_space.high)
Samuti võite märgata, et tasu väärtus igal simulatsiooni sammul on alati 1. See on sellepärast, et meie eesmärk on ellu jääda nii kaua kui võimalik, st hoida post mõistlikult vertikaalses asendis võimalikult pika aja jooksul.
✅ Tegelikult loetakse CartPole simulatsioon lahendatuks, kui suudame saavutada keskmise tasu 195 üle 100 järjestikuse katse.
Q-õppes peame looma Q-tabeli, mis määratleb, mida teha igas olekus. Selleks peab olek olema diskreetne, täpsemalt, see peaks sisaldama piiratud arvu diskreetseid väärtusi. Seega peame kuidagi diskretiseerima oma vaatlused, kaardistades need piiratud olekute hulgale.
Selleks on mitu võimalust:
- Jagamine vahemikeks. Kui teame teatud väärtuse intervalli, saame selle intervalli jagada mitmeks vahemikuks ja seejärel asendada väärtuse vahemiku numbriga, kuhu see kuulub. Seda saab teha numpy
digitizemeetodiga. Sel juhul teame täpselt oleku suurust, kuna see sõltub valitud vahemike arvust.
✅ Võime kasutada lineaarset interpolatsiooni, et tuua väärtused teatud piiratud intervalli (näiteks -20 kuni 20), ja seejärel teisendada numbrid täisarvudeks, neid ümardades. See annab meile oleku suuruse üle vähem kontrolli, eriti kui me ei tea sisendväärtuste täpseid vahemikke. Näiteks meie puhul ei ole 2-st 4-st väärtusest ülemisi/alumisi piire, mis võib viia lõpmatu arvu olekuteni.
Meie näites kasutame teist lähenemist. Nagu hiljem märkate, hoolimata määramata ülemistest/alumistest piiridest, võtavad need väärtused harva teatud piiratud intervallidest väljapoole jäävaid väärtusi, seega on äärmuslike väärtustega olekud väga haruldased.
-
Siin on funktsioon, mis võtab meie mudelist vaatluse ja toodab 4 täisarvu tuple'i: (koodiplokk 6)
def discretize(x): return tuple((x/np.array([0.25, 0.25, 0.01, 0.1])).astype(np.int))
-
Uurime ka teist diskretiseerimismeetodit, kasutades vahemikke: (koodiplokk 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)] # intervals of values for each parameter nbins = [20,20,10,10] # number of bins for each 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))
-
Käivitame nüüd lühikese simulatsiooni ja vaatleme neid diskreetseid keskkonna väärtusi. Proovige julgelt nii
discretizekui kadiscretize_binsja vaadake, kas on erinevusi.✅
discretize_binstagastab vahemiku numbri, mis on 0-põhine. Seega sisendmuutuja väärtuste puhul, mis on umbes 0, tagastab see intervalli keskelt numbri (10).discretizepuhul ei hoolinud me väljundväärtuste vahemikust, lubades neil olla negatiivsed, seega oleku väärtused ei ole nihutatud ja 0 vastab 0-le. (koodiplokk 8)env.reset() done = False while not done: #env.render() obs, rew, done, info = env.step(env.action_space.sample()) #print(discretize_bins(obs)) print(discretize(obs)) env.close()
✅ Kommenteerige lahti rida, mis algab
env.render, kui soovite näha, kuidas keskkond täitub. Vastasel juhul saate selle taustal täita, mis on kiirem. Kasutame seda "nähtamatut" täitmist Q-õppe protsessi ajal.
Eelmises tunnis oli olek lihtne paar numbreid vahemikus 0 kuni 8, mistõttu oli mugav esitada Q-tabelit numpy tensorina kujuga 8x8x2. Kui kasutame vahemike diskretiseerimist, on meie olekuvektori suurus samuti teada, seega saame kasutada sama lähenemist ja esitada oleku massiivina kujuga 20x20x10x10x2 (siin 2 on tegevusruumi dimensioon ja esimesed dimensioonid vastavad vahemike arvule, mida oleme valinud iga vaatlusruumi parameetri jaoks).
Kuid mõnikord ei ole vaatlusruumi täpsed dimensioonid teada. discretize funktsiooni puhul ei pruugi me kunagi olla kindlad, et meie olek jääb teatud piiridesse, sest mõned algsed väärtused ei ole piiratud. Seetõttu kasutame veidi teistsugust lähenemist ja esitame Q-tabeli sõnastikuna.
-
Kasutage paari (olek, tegevus) sõnastiku võtmena ja väärtus vastaks Q-tabeli kirje väärtusele. (koodiplokk 9)
Q = {} actions = (0,1) def qvalues(state): return [Q.get((state,a),0) for a in actions]
Siin määratleme ka funktsiooni
qvalues(), mis tagastab loendi Q-tabeli väärtustest antud oleku jaoks, mis vastab kõigile võimalikele tegevustele. Kui kirje ei ole Q-tabelis olemas, tagastame vaikimisi 0.
Nüüd oleme valmis õpetama Peetrit tasakaalu hoidma!
-
Kõigepealt määrame mõned hüperparameetrid: (koodiplokk 10)
# hyperparameters alpha = 0.3 gamma = 0.9 epsilon = 0.90
Siin on
alphaõppemäär, mis määrab, mil määral peaksime Q-tabeli praeguseid väärtusi igal sammul kohandama. Eelmises tunnis alustasime väärtusega 1 ja vähendasime seejärelalphaväärtusi treeningu ajal. Selles näites hoiame selle lihtsuse huvides konstantsena ja saate hiljem katsetadaalphaväärtuste kohandamist.gammaon diskontomäär, mis näitab, mil määral peaksime eelistama tulevast tasu praeguse tasu ees.epsilonon uurimise/kasutamise tegur, mis määrab, kas peaksime eelistama uurimist või kasutamist. Meie algoritmis valimeepsilonprotsendil juhtudest järgmise tegevuse vastavalt Q-tabeli väärtustele ja ülejäänud juhtudel teeme juhusliku tegevuse. See võimaldab meil uurida otsinguruumi piirkondi, mida me pole kunagi varem näinud.✅ Tasakaalu osas – juhusliku tegevuse valimine (uurimine) toimiks juhusliku löögina vales suunas ja post peaks õppima, kuidas nendest "vigadest" tasakaalu taastada.
Saame oma algoritmi eelmise tunni põhjal kahel viisil täiustada:
-
Arvuta keskmine kumulatiivne tasu mitme simulatsiooni jooksul. Trükime progressi iga 5000 iteratsiooni järel ja keskmistame kumulatiivse tasu selle aja jooksul. See tähendab, et kui saame rohkem kui 195 punkti, võime probleemi lahendatuks pidada, isegi kõrgema kvaliteediga kui nõutud.
-
Arvuta maksimaalne keskmine kumulatiivne tulemus,
Qmax, ja salvestame Q-tabeli, mis vastab sellele tulemusele. Kui treeningu ajal märkate, et keskmine kumulatiivne tulemus hakkab langema, tahame säilitada Q-tabeli väärtused, mis vastavad treeningu parimale mudelile.
-
Koguge kõik kumulatiivsed tasud igal simulatsioonil
rewardsvektorisse edasiseks joonistamiseks. (koodiplokk 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 # == do the simulation == while not done: s = discretize(obs) if random.random()<epsilon: # exploitation - chose the action according to Q-Table probabilities v = probs(np.array(qvalues(s))) a = random.choices(actions,weights=v)[0] else: # exploration - randomly chose the action 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) # == Periodically print results and calculate average reward == 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=[]
Mida võite nendest tulemustest märgata:
-
Eesmärgile lähedal. Oleme väga lähedal eesmärgi saavutamisele, saades 195 kumulatiivset tasu üle 100+ järjestikuse simulatsiooni või oleme selle tegelikult saavutanud! Isegi kui saame väiksemaid numbreid, ei tea me seda, sest keskmistame 5000 jooksu jooksul ja ametlikus kriteeriumis on nõutud ainult 100 jooksu.
-
Tasu hakkab langema. Mõnikord hakkab tasu langema, mis tähendab, et võime "hävitada" juba õpitud väärtused Q-tabelis väärtustega, mis olukorda halvendavad.
See tähelepanek on selgem, kui joonistame treeningu progressi.
Treeningu ajal kogusime kumulatiivse tasu väärtuse igal iteratsioonil rewards vektorisse. Siin on, kuidas see välja näeb, kui joonistame selle iteratsiooni numbri vastu:
plt.plot(rewards)Sellest graafikust ei ole võimalik midagi järeldada, sest stohhastilise treeningprotsessi olemuse tõttu varieerub treeningseansside pikkus suuresti. Selle graafiku mõistlikumaks muutmiseks saame arvutada jooksva keskmise mitme katse jooksul, näiteks 100. Seda saab mugavalt teha np.convolve abil: (koodiplokk 12)
def running_average(x,window):
return np.convolve(x,np.ones(window)/window,mode='valid')
plt.plot(running_average(rewards,100))Et õppimine oleks stabiilsem, on mõistlik treeningu ajal mõningaid hüperparameetreid kohandada. Eelkõige:
-
Õppemäära,
alpha, puhul võime alustada väärtustega, mis on lähedased 1-le, ja seejärel seda parameetrit järk-järgult vähendada. Aja jooksul saame Q-tabelis häid tõenäosusväärtusi ja seega peaksime neid veidi kohandama, mitte täielikult uute väärtustega üle kirjutama. -
Suurenda epsilonit. Võime soovida
epsilonväärtust aeglaselt suurendada, et uurida vähem ja kasutada rohkem. Tõenäoliselt on mõistlik alustada madalamaepsilonväärtusega ja liikuda peaaegu 1-ni.
Ülesanne 1: Mängige hüperparameetrite väärtustega ja vaadake, kas suudate saavutada kõrgema kumulatiivse tasu. Kas jõuate üle 195? Ülesanne 2: Probleemi ametlikuks lahendamiseks peate saavutama 195 keskmise tasu 100 järjestikuse jooksu jooksul. Mõõtke seda treeningu ajal ja veenduge, et olete probleemi ametlikult lahendanud!
Oleks huvitav näha, kuidas treenitud mudel tegelikult käitub. Käivitame simulatsiooni ja järgime sama tegevuse valimise strateegiat nagu treeningu ajal, valides tegevusi vastavalt Q-tabeli tõenäosusjaotusele: (koodiplokk 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()Peaksite nägema midagi sellist:
Ülesanne 3: Siin kasutasime Q-tabeli lõplikku koopiat, mis ei pruugi olla parim. Pidage meeles, et oleme salvestanud parima tulemusega Q-tabeli
Qbestmuutujasse! Proovige sama näidet parima tulemusega Q-tabeliga, kopeeridesQbestüleQ-sse ja vaadake, kas märkate erinevust.
Ülesanne 4: Siin me ei valinud igal sammul parimat tegevust, vaid pigem valisime tegevusi vastavalt tõenäosusjaotusele. Kas oleks mõistlikum alati valida parim tegevus, millel on Q-tabelis kõrgeim väärtus? Seda saab teha, kasutades
np.argmaxfunktsiooni, et leida tegevuse number, mis vastab kõrgeimale Q-tabeli väärtusele. Rakendage see strateegia ja vaadake, kas see parandab tasakaalu hoidmist.
Oleme nüüd õppinud, kuidas treenida agente saavutama häid tulemusi, pakkudes neile ainult tasufunktsiooni, mis määratleb mängu soovitud oleku, ja andes neile võimaluse intelligentselt otsinguruumi uurida. Oleme edukalt rakendanud Q-õppe algoritmi nii diskreetsete kui ka pidevate keskkondade puhul, kuid diskreetsete tegevustega.
Oluline on uurida ka olukordi, kus tegevusruum on samuti pidev ja kui vaatlusruum on palju keerulisem, näiteks pilt Atari mängu ekraanilt. Selliste probleemide lahendamiseks on sageli vaja kasutada võimsamaid masinõppe tehnikaid, nagu näiteks tehisnärvivõrgud, et saavutada häid tulemusi. Need keerukamad teemad on meie tulevase edasijõudnute AI kursuse fookuses.
Lahtiütlus:
See dokument on tõlgitud AI tõlketeenuse Co-op Translator abil. Kuigi püüame tagada täpsust, palume arvestada, et automaatsed tõlked võivad sisaldada vigu või ebatäpsusi. Algne dokument selle algses keeles tuleks pidada autoriteetseks allikaks. Olulise teabe puhul soovitame kasutada professionaalset inimtõlget. Me ei vastuta selle tõlke kasutamisest tulenevate arusaamatuste või valesti tõlgenduste eest.





