Bevezetés a megerősítő tanulásba

A megerősített tanulás különbözik a klasszikus gépi tanulási módszerektől, például a felügyelt és a nem felügyelt tanulástól. A felügyelt tanulási algoritmus betanítási adatokat vár, amelyek tartalmazzák a bemeneteket és a hozzájuk tartozó kimeneteket. Algoritmusokat alkalmaznak a bemenetek és a kimenetek közötti leképezés létrehozására, így ha egy korábban nem látott bemenetet adnak, az a legjobb tudása szerint megjósolja a helyes kimenetet.

A felügyelet nélküli tanulás viszont arra irányul, hogy mintákat találjon az adathalmazokból, kimeneti adatokat nem adnak meg. A választott algoritmus várhatóan megtalálja a rejtett struktúrákat az adatokban.¹ ²

A megerősítő tanulás eltér mindkettőtől, mivel nincs kezdeti adatkészlete, amellyel dolgozni lehetne. Egy ügynök egy adott környezetben végez műveleteket. Amikor egy cselekvés megtörténik, visszajelzést adnak jutalom formájában. Minél jobb a cselekvés, annál jobb a jutalom. A megerősítő tanulási algoritmusban az ágens bármikor adott állapotban lesz. Az ügynök ezután a maga állapotában intézkedik, és jutalmat kap. Az ügynök tanul ezekből a jutalmakból, hogy a jövőben jobb lépéseket tegyen. A végcél az, hogy egy ügynök az adott állapotban a legjobb lépést tegye meg (azaz azt a lépést, amely maximalizálja a jutalmat).

Ezeket a kifejezéseket hivatalosan az alábbiakban határozzuk meg ³ ⁴:

  • Ügynök: A döntéshozó és a tanuló, figyeli a környezetet és cselekszik
  • Állapot: Az ágens jelenlegi helyzete a környezetben.
  • Környezet: Az a világ, amelyben az ügynök dönt a cselekvésekről, és tanul a tettekből
  • Jutalom: a környezettől adott visszajelzés, amikor egy cselekvést végrehajtanak

Ha a Pac-Man-t vesszük példának. Az ügynök, a környezet, az állapotok és a jutalom a következőképpen alakulna ⁵:

  • Ügynök: maga a „PacMan”.
  • Környezet: A rács, amelyben a „PacMan” mozog
  • Jutalom: A „PacMan” pozitív jutalmat kap, ha eszik, és negatív jutalmat, ha egy szellem elkapja.
  • Állapot: Ez állhat a „PacMan” aktuális pozíciójából, a szellemek jelenlegi helyzetéből és a pontok helyzetéből.

Ezzel a tudással felvértezve arra törekszünk, hogy megerősítő tanulást alkalmazzunk egy ügynök képzésére a Flappybird megtanulására.

Ebben az esetben a következőkkel rendelkezünk:

  • Ügynök: A madár
  • Állapot: A madár aktuális helyzetéből, sebességéből, valamint a madárhoz vezető következő 2 cső távolságából és helyzetéből álló vektor.
  • Jutalom: Minél tovább halad a madár a pályán, mielőtt ütközne, annál nagyobb a jutalom
  • Környezet: Az a játék, amelyben a madár akciókat hajt végre

Ezenkívül 2 lehetséges akciója van: lépjen felfelé, vagy ne tegyen semmit.

A használt kulcsfogalmak és algoritmusok

A megerősítő tanulási problémát Deep-Q Network segítségével oldjuk meg. Ez egy gyakori megoldás a megerősített tanulási problémákra. Ahhoz, hogy megértsük, honnan erednek ezek a hálózatok, először meg kell értenünk a Markov-döntési folyamatokat (MDP).

MDP-k

A Markov-döntési folyamatok (MDP-k) valószínűségi állapotmodellek, amelyek állapotteret, kezdeti állapotot, minden egyes állapothoz tartozó műveletet, átmeneti valószínűségeket, jutalmakat és diszkonttényezőt tartalmaznak. Vizuálisan meg tudják mutatni az ügynök összes lehetséges állapotát, annak valószínűségét, hogy egy művelet végrehajtása után állapotba kerül, és milyen jutalmak járnak a műveletek végrehajtása során. Nincsenek célállapotok, az ügynök jutalmat kap az adott állapotban végrehajtott cselekvése alapján¹².

Például ebben az MDP-ben, ha az ügynök S0 állapotban indul, akkor A2 műveletet hajthat végre, és 20%-os valószínűséggel S1 állapotba kerülhet, vagy 80%-os valószínűséggel S0 állapotban maradhat. Ha például az S2 állapotban van az A1 művelet végrehajtása után, akkor 80% a valószínűsége, hogy +40 jutalmat kap, és visszatér az S0 állapotba.

Állami értékek

Egy állapot optimális állapotértékét (V*(s)) az összes olyan diszkontált jövőbeli jutalom összegeként határozzuk meg, amelyre az ügynök átlagosan számíthat egy s állapot elérése után, feltételezve, hogy optimálisan cselekszik (a legmagasabb jutalommal járó művelet végrehajtása végtelen horizont¹3) ¹⁴ (p627). A V*(s) egyenlete alább látható.

Először kiszámítjuk az egyes műveletek várható jutalmát. Ez a művelet eredményeként kapott valamennyi állam jutalma plusz ezen államok diszkontált jövőbeli jutalma (V*(s)) összege. Ezt a kombinált jutalmat megszorozzuk az adott állapotba kerülés valószínűségével.

Ebből az egyenletből létrehozhatunk egy algoritmust az egyes állapotok optimális állapotértékének kiszámításához. Az összes állapotértéket 0-ra inicializáljuk, majd a következő algoritmust futtatjuk:

Minden k iterációhoz az egyenletet minden s 15 állapotra lefuttatjuk.

Q-értékek

Az optimális állapotértékek kiszámítása után meg kell fogalmaznunk egy optimális politikát az ügynök számára. Ez Q-értékek használatával érhető el. Melyek az optimális állapot-hatás értékek. Ezeket a diszkontált jövőbeni jutalmak összegeként határozzuk meg, amelyre az ügynök átlagosan számíthat (feltéve, hogy optimálisan cselekszik), miután elérte az s állapotot, és a ¹⁴ cselekvést választ (p628).

A Q értékeket a következő iterációs algoritmussal számíthatjuk ki. A kezdeti Q értékeket 0-ra állítjuk, és minden k iterációhoz az egyenletet minden s állapot akciópárra (s, a) lefutjuk.¹⁶

Az egyes állapotok optimális házirendjét ezután úgy határozzuk meg, mint az a művelet, amely maximalizálja a lehetséges Q értékeket. Mivel ha maximalizáljuk a Q értéket, akkor maximalizáljuk az ügynök jövőbeli jutalmát ¹⁴ (p628).

Q-Learning algoritmus

Ezután figyelembe kell vennünk, hogy kezdetben fogalmunk sincs az átmenet valószínűségéről és az ezekből adódó jutalmakról. Meg kell tapasztalnia az állapotokat és az átmeneteket a jutalmak kiszámításához, és ezt többször is meg kell tapasztalnia, hogy kiszámítsa az átmenet valószínűségének becslését¹4 (p629).

A Q-learning algoritmus a Q-érték függvény adaptációja, ahol feltételezzük, hogy nem ismerjük az átmenet valószínűségét és a jutalmakat. Egy ügynök játékát figyeljük, és folyamatosan javítjuk a Q értékek becsléseit. Miután megvan a Q érték, kiválaszthatjuk azt a műveletet, amelyiknek a Q értéke a maximális¹⁴ (p630).

Ez az algoritmus az alábbiakban látható:

Ennél az algoritmusnál a kezdeti Q értékeket 0-ra állítjuk, és lefuttatjuk az algoritmust. Bevezethetjük a tanulási sebesség csökkenését is¹⁴ (p631).

Deep Q Networks

A fő probléma akkor merül fel, amikor elkezdjük az MDP méretezését. Minden állapothoz, akciópárhoz rendelkeznünk kell a Q érték becslésével. Tehát ahogy az állapotok és cselekvések száma növekszik, úgy növekszik a becslések száma is, amelyeket nyomon kell követnünk .¹⁴ (p633)

A probléma megoldásához Deep Q hálózatot használhatunk. Ezáltal találunk egy függvényt a Q érték becslésére bármely adott állapot és művelet alapján. Ez a funkció egy Mély Neurális Hálózat kimenete, amely megfelelő elnevezéssel Deep Q Network¹⁴ (p633).

Bellman alapján a hálózat által kiszámított hozzávetőleges Q értéknek a lehető legközelebb kell lennie ahhoz a jutalomhoz, amelyet kapunk, plusz az onnantól kezdve optimálisan játszható diszkontált értékhez (azaz egy adott állapot maximális Q értékéhez az összes akcióra vonatkoztatva). Ennek az értéknek a megtalálásához végrehajtjuk a DQN-t a következő állapotban az összes lehetséges művelethez. A maximális Q értéket vesszük a kimenetekből, mivel feltételezzük, hogy optimálisan játszunk. Ezután leértékelhetjük, és hozzáadhatjuk az aktuális jutalmat. Így megkapjuk a Q célértéket¹⁴ (p633–634).

A hálózat betanítása során kiszámítunk egy előre jelzett Q értéket a mély Q hálózat segítségével, majd kiszámítjuk ezt a cél Q értéket. Célunk, hogy minimalizáljuk a négyzetes hibát e két érték között, és a hálózatot a várt módon tanítsuk14 (p634).

A Deep Q Network alapján néhány olyan hálózatváltozat létezik, amelyek javítják a teljesítményt.

2 hálózat használata

Jelenleg a modellt előrejelzések készítésére és saját célok kitűzésére használják. Azaz. kiadja mind az elérni kívánt Q értékeket, mind a jelenleg előrejelzett Q értékeket. Ez visszacsatolási hurkot hozhat létre, és instabillá teheti a hálózatot. Ehelyett 2 modellünk van, az egyik a megjósolt Q-értékek kiadására szolgál, amelyből azután a műveletek kiválasztásakor használunk, a másik pedig a cél Q-értékeket¹⁴ (p639).

Ez a célmodell egyszerűen a predikciós modell klónja, rögzített időközönként átmásoljuk a paramétereket a predikciós modellből a célmodellbe¹⁴ (p639).

Dueling Deep Q Networks

Először is jegyezzük meg, hogy egy állapotműveletpár Q értékét az alábbiak szerint fejezhetjük ki, ahol V(s) az állapot értéke, A(s,a) pedig az az előnye, hogy a műveletet s állapotban hajtunk végre az összeshez képest. egyéb műveletek. A V(s) állapot értéke megegyezik az adott állapot legjobb cselekvésének (a*) Q értékével, mivel feltételezzük, hogy optimálisan fogunk cselekedni. Ezért Q(s, a*) = V(s), és arra következtethetünk, hogy az A(s, a*) = 0. ¹⁴ (p641)

A párbaj Q hálózattal megbecsüljük az egyes akciók előnyét és az állapot értékét. Amint fentebb látható, a legjobb műveletnek 0-nak kell lennie. Az összes előnyből kivonjuk a maximálisan becsült előnyt, majd hozzáadjuk ezeket az állapotértékhez, így minden egyes művelethez Q értékeket generálunk. Ezért egy adott állapot esetén a legnagyobb előnnyel rendelkező művelet lesz a legjobb akció, és a legnagyobb Q értéket¹⁴ biztosítja számunkra (p641).

Módszertan

Környezet

A környezet beállításához a PyGame tanulási környezetet⁶ használjuk. Ez egy könyvtár, amely hozzáférést biztosít számos játékhoz, amelyeket megerősítő tanulási módon lehet használni. Ennek használata azt jelenti, hogy nem kell kialakítanunk a környezetünket. Egyszerűen betápláljuk a műveleteket a csomagban található Flappybird modul egy példányába, és az visszaadja a jutalmat és az állapotot.

A környezet így is példányosítható, itt mutatjuk meg a lehetséges műveletek listáját és az ügynök aktuális állapotát a környezetben.

game = FlappyBird(width=256, height=256)
p = PLE(game, display_screen=False)
p.init()
actions = p.getActionSet()
action_dict = {0: actions[1], 1: actions[0]}
state = p.getGameState()

Hálózat

A hálózathoz egy egyszerű dinamikus neurális hálózatot választunk. Ily módon a hálózat Q hálózatként vagy párbaj Q hálózatként működhet. A bemenet az aktuális állapot, a kimenet a Q értékek vektora minden lehetséges művelethez. 4 rejtett rétegünk van, a hálózat előrehaladtával növeljük a neuronok számát, minden réteg után ReLU6 aktiválást alkalmazunk.

class DQN(torch.nn.Module):
    def __init__(self, input_dim, output_dim, network_type='DDQN', *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.input_dim = input_dim
        self.output_dim = output_dim
        self.network_type = network_type
        self.layer1 = nn.Linear(input_dim,64)
        self.layer2 = nn.Linear(64, 128)
        self.layer3 = nn.Linear(128, 256)
        self.layer4 = nn.Linear(256, 512) 
        self.layer5 = nn.Linear(512, 512)
...
    def forward(self, x):
        x = F.relu6(self.layer1(x))
        x = F.relu6(self.layer2(x))
        x = F.relu6(self.layer3(x))
        x = F.relu6(self.layer4(x))
        x = F.relu6(self.layer5(x))

Ezenkívül úgy döntöttünk, hogy az érvek alapján egy másik hálózatot hozunk létre. Könnyen létrehozhatjuk a DDQN párbaj-változatát, ha 2 kimeneti réteget használunk az állapot- és előnyértékek kiszámításához. Ezeket azután kombinálják a kimenet előállításához.

class DQN(torch.nn.Module):
    def __init__(self, input_dim, output_dim, network_type='DQN', *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
    ...
        if network_type == 'DuelingDQN':
            self.state_values = nn.Linear(512,1)
            self.advantages = nn.Linear(512, output_dim)
        else:
            self.output = nn.Linear(512, output_dim)
    ...
    def forward(self, x):
    ...
        if self.network_type == 'DuelingDQN':
            state_values = self.state_values(x)
            advantages = self.advantages(x)
            output = state_values + (advantages - torch.max((advantages), dim=1, keepdim=True)[0])
            return output
        else:
            return self.output(x)

Gyorsítótár visszahívása

Szükségünk lesz egy memóriarendszerre a tapasztalataink tárolására, így véletlenszerűen mintát vehetünk egy csomó tapasztalatból, amelyekből tanulhatunk. Egy egyszerű osztályt hozunk létre a kétvégű Python-sorszerkezet alapján. Függvényeket adunk hozzá adatok hozzáadásához a sorhoz és véletlenszerűen mintavételhez egy adatkötegből. Ezeket a funkciókat ügynökünk később arra fogja használni, hogy tárolja a környezetben áthaladó tapasztalatokat, és mintát vegyen ezekből a tapasztalatokból a szabályzati hálózat betanításához.

class MemoryRecall():
    def __init__(self, memory_size) -> None:
        self.memory_size=memory_size
        self.memory = deque(maxlen = self.memory_size)
    
    def cache(self, data):
        self.memory.append(data)

    def recall(self, batch_size):
        return random.sample(self.memory, batch_size)

    def __len__(self):
        return len(self.memory)

Ügynök

A művelet létrehozásához a következő függvényekre van szükségünk:

  • Funkció egy művelet kiválasztásához
  • Funkció a Q-értékek, a veszteség és a modellek optimalizálásának kiszámítására
  • A fő betanítási funkció, amelynek fel kell dolgoznia az ügynököt, amint az állapotokon áthalad és intézkedik

Művelet kiválasztása

A cselekvés kiválasztása meglehetősen egyszerű, egy epszilon-mohó politikát követünk⁷. Ebben a házirendben az ágens egy véletlenszerű akciót választ ε valószínűséggel és egy mohó akciót (1-ε) valószínűséggel. A mohó cselekvés az, amely maximalizálja a Q értéket, mivel ha maximalizáljuk a Q értéket, akkor maximalizáljuk az ügynök jövőbeli jutalmait. Mivel a házirend-hálózat minden lehetséges művelethez hozzávetőleges Q értéket ad ki, egyszerűen azt a műveletet választottuk, amelyik a legnagyobb kimenettel rendelkezik a hálózatból.

@torch.no_grad()
def take_action(self, state):
    self.eps = self.eps*self.EPS_DECAY_VALUE
    self.eps = max(self.eps, self.EPS_END)
    if self.eps < np.random.rand():
        state = state[None, :]
        action_idx = torch.argmax(self.policy_net(state), dim=1).item()
    else:
        action_idx = random.randint(0, self.action_dim-1)
    self.steps_done += 1
    return action_idx

Az epszilon-kapzsi politika egyik kulcseleme az ε hiperparaméterünk lecsengése. Kezdetben azt szeretnénk, hogy ez a paraméter 1,0 legyen, majd az idő múlásával lassan csökken. Ez annak biztosítására szolgál, hogy az ügynök feltárja a lehetséges állapotokat és átmeneteket, hogy sokféle tapasztalati listát hozzon létre, ne csak azokat az állapotokat, amelyek a maximális jutalomhoz vezetnek. Ha az ügynök csak a jutalom alapján az optimális cselekvést választotta, az nem optimális teljesítményhez vezethet, mivel nem választja az átmenetet olyan állapotokba, amelyek a jövőben még nagyobb jutalmakhoz vezethetnek. Azt is szeretnénk, hogy az ügynök kihasználja a környezet azon állapotait, amelyek jobb jutalmakhoz vezetnek. Ezért az epszilon-kapzsi politikát választották.

Az ágens előrehaladtával csökkentjük az epszilon értékét. Ennek az az oka, hogy a betanítási folyamat kezdetén az ügynök még nem fedezte fel a környezetet, és nem képezte ki a hálózatot. Ezért szeretnénk az élménypuffert változatos élményekkel feltölteni. Azonban, ahogy egyre több tapasztalattal találkozunk, azt szeretnénk, hogy az ügynök a legnagyobb jutalmat adó akciókat használja ki, mivel biztosabbak vagyunk abban, hogy megfelelően feltárta az állapotteret ⁸ ⁹.

A modell betanítása

A képzéshez a következő funkciót valósítjuk meg.

def optimize_model(self):
    if len(self.cache_recall) < self.BATCH_SIZE:
        return
    batch = self.cache_recall.recall(self.BATCH_SIZE)
    batch = [*zip(*batch)]
    state = torch.stack(batch[0])
    non_final_mask = torch.tensor(tuple(map(lambda s: s is not None, batch[1])), device=self.device, dtype=torch.bool)
    non_final_next_states = torch.stack([s for s in batch[1] if s is not None])
    action = torch.stack(batch[2])
    reward = torch.cat(batch[3])
    next_state_action_values = torch.zeros(self.BATCH_SIZE, dtype=torch.float32, device=self.device)
    state_action_values = self.policy_net(state).gather(1, action)
    with torch.no_grad():
        next_state_action_values[non_final_mask] = self.target_net(non_final_next_states).max(1)[0]
    expected_state_action_values = (next_state_action_values * self.GAMMA) + reward
    loss_fn = torch.nn.SmoothL1Loss()
    loss = loss_fn(state_action_values, expected_state_action_values.unsqueeze(1))
    self.optimizer.zero_grad()
    loss.backward()
    self.optimizer.step()

Először mintát veszünk egy csomó adatból, és megreformáljuk a köteget, hogy könnyen lekérhessük az állapotokat, műveleteket, jutalmakat és a következő állapotokat. Ezután az alábbiak szerint gyűjtjük az adatokat.

batch = self.cache_recall.recall(self.BATCH_SIZE)
batch = [*zip(*batch)]
state = torch.stack(batch[0])
action = torch.stack(batch[2])
reward = torch.cat(batch[3])

Figyelembe kell venni, hogy a következő állapotok némelyike ​​a cselekvés eredményeként Nincs, mivel a Madár egy csőnek ütközött. Ezért létrehozunk egy maszkot, hogy kiszűrjük a következő állapotokat, amelyek nem egy sem. Valamint összegyűjtjük a következő állapotokat is, amelyek nem egy sem.

non_final_mask = torch.tensor(tuple(map(lambda s: s is not None, batch[1])), device=self.device, dtype=torch.bool)
non_final_next_states = torch.stack([s for s in batch[1] if s is not None])

Az elmélet szerint átadjuk az állapotot a politikai hálózaton, hogy kiszámítsuk az egyes állapot-művelet párok Q-értékeinek becslését. Mivel a hálózat egy adott állapothoz tartozó minden művelethez Q értéket ad ki, csak ezeket a Q értékeket kell gyűjtenünk a végrehajtott művelethez.

state_action_values = self.policy_net(state).gather(1, action)

Ezután kiszámítjuk a várható Q értékeket. Ez úgy történik, hogy minden következő állapothoz vesszük a célhálózat kimenetét, majd a maximumot, ügyelve arra, hogy kiszűrjük a következő állapotokat, amelyek None értékkel rendelkeznek (mivel ezeket az adattípusokat a hálózat nem tudja feldolgozni). Ezután ezt megszorozzuk az egyenlet szerint, hogy megkapjuk a várt Q értékeinket.

with torch.no_grad():
    next_state_action_values[non_final_mask] = self.target_net(non_final_next_states).max(1)[0]
expected_state_action_values = (next_state_action_values * self.GAMMA) + reward

A hálózat betanításához kiszámítjuk az L1 veszteséget a házirend-hálózat becsült Q-értékei és a célhálózat várható Q-értékei között. Ezután végrehajtjuk a szükséges lépéseket a politikai hálózat betanításához.

loss_fn = torch.nn.SmoothL1Loss()
loss = loss_fn(state_action_values, expected_state_action_values.unsqueeze(1))
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

Fő edzési funkció

Végül létrehozzuk az ügynök fő feldolgozó funkcióját. Az ügynököt meghatározott számú epizódig működtetjük. Itt az epizódot egyetlen játékmenetként határozzuk meg. A Madár akciósorozatot hajt végre, amíg beleütközik egy csőbe.

Az ügynök ezen részének kódja meglehetősen magától értetődő. Minden epizódban visszaállítjuk a játékot, a kezdeti állapotot véve. Majd amikor az ügynök új műveletet hajt végre, a gyorsítótárunkban tároljuk a jutalmat, a műveletet, az állapotot és a következő állapotot (az állapot, amelybe a művelet végrehajtása következtében került). Ezután betanítjuk a modellt és frissítjük a célhálózatot.

def train(self, episodes, env):
    self.steps_done = 0
    for episode in range(episodes):
        env.reset_game()
        state = env.getGameState()
        state = torch.tensor(list(state.values()), dtype=torch.float32, device=self.device)
        for c in count():
            action = self.take_action(state)
            reward = env.act(self.action_dict[action])
            reward = torch.tensor([reward], device=self.device)
            action = torch.tensor([action], device=self.device)
            next_state = env.getGameState()
            next_state = torch.tensor(list(next_state.values()), dtype=torch.float32, device=self.device)
            done = env.game_over()
            if done:
                next_state = None
            self.cache_recall.cache((state, next_state, action, reward, done))
            state = next_state 
            self.optimize_model()
            self.update_target_network()
...

A célhálózat frissítése a következő funkcióban történik. Ahelyett, hogy egyszerűen másolnánk a házirend hálózati paramétereit a célhálózatra, lágy frissítést használunk¹⁰. Ebben az esetben a Tau 0,005-re van állítva, ezért a célhálózat frissítésekor a házirend hálózati paramétereinek 0,5%-át kombinálja a célhálózati paraméterek 99,5%-ával.

def update_target_network(self):
    target_net_state_dict = self.target_net.state_dict()
    policy_net_state_dict = self.policy_net.state_dict()
    for key in policy_net_state_dict:
        target_net_state_dict[key] = policy_net_state_dict[key]*self.TAU + target_net_state_dict[key]*(1-self.TAU)
        self.target_net.load_state_dict(target_net_state_dict)

Végül, ha az epizód véget ért, ábrázoljuk az adatokat és tároljuk a hálózatokat.

...
if done:
    self.episode_durations.append(c+1)
    self.plot_durations()
    print("EPS: {}".format(self.eps))
    print("Durations: {}".format(c+1))
    print("Score: {}".format(env.score()))
    torch.save(self.target_net.state_dict(), self.network_type+'_target_net.pt')
    torch.save(self.policy_net.state_dict(), self.network_type+'_policy_net.pt')
    break

Eredmények

A fő futamunkhoz egy párbaj mély Q hálózatot használtunk, és ezt valamivel több mint 12 000 epizódon keresztül futtattuk. Az epszilon bomlást meglehetősen könnyűnek tartottuk, hogy megbizonyosodjunk arról, hogy az ügynöknek elegendő ideje van a felfedezésre.

A betanítási grafikonon láthatjuk, hogy az ügynök nagyon jól megtanul átjárni a környezetet, több ezer akciót tud futni anélkül, hogy csőbe ütközne.

A következő animáció ezt a képzett ügynököt mutatja be működés közben.

A teljes kód elérhető a GitHubon: https://github.com/nathanwbailey/flappy_bird_reinforcement_learning

[1]: Felügyelet nélküli tanulás, Wikipédia. https://en.wikipedia.org/wiki/Unsupervised_learning

[2]: Felügyelet nélküli tanulás, Geeks for Geeks. https://www.geeksforgeeks.org/supervised-unsupervised-learning/

[3]: Bevezetés a megerősítő tanulásba kezdőknek. Analytics Vidhya. https://www.analyticsvidhya.com/blog/2021/02/introduction-to-reinforcement-learning-for-beginners/

[4]: Geron, A. (2019). Kézi gyakorlati gépi tanulás a Scikit-Learn, Keras és TensorFlow segítségével (2. kiadás). O’Reily Media. p610

[5]: Reforcement Learning in Pacman, Gnanasekara, A http://cs229.stanford.edu/proj2017/final-reports/5241109.pdf

[6]: PLE: Megerősítő tanulási környezet, Olvassa el a dokumentumokat. https://pygame-learning-environment.readthedocs.io/en/latest/

[7]: Geron, A. (2019). Kézi gyakorlati gépi tanulás a Scikit-Learn, Keras és TensorFlow segítségével (2. kiadás). O’Reily Media. 632–634

[8]: Epsilon-Greedy Algorithm in Enforcement Learning. Geeks for Geeks. https://www.geeksforgeeks.org/epsilon-greedy-algorithm-in-reinforcement-learning/

[9]: Strategies for Decaying Epsilon in Epsilon-Greedy. Közepes. https://medium.com/@CalebMBowyer/strategies-for-decaying-epsilon-in-epsilon-greedy-9b500ad9171d

[10]: Folyamatos ellenőrzés mély megerősített tanulással. Timothy P. Lilicrap. https://arxiv.org/abs/1509.02971

[11]: FlappyBird, N64 Squid. https://n64squid.com/flappy-bird/

[12]: Inforcement Learning 101, Towards Data Science. https://towardsdatascience.com/reinforcement-learning-101-e24b50e1d292

[13]: Markov Decision Processes, Gibberblot https://gibberblot.github.io/rl-notes/single-agent/MDPs.html

[14]: Praktikus gépi tanulás a Scikit-Learn, Keras és Tensorflow segítségével. Aurelien Geron.

[15]: Értékiteráció kontra politikai iteráció a tanulás megerősítésében. https://www.baeldung.com/cs/ml-value-iteration-vs-policy-iteration

[16]: Bootcamp Summer 2020, 3. hét – Értékiteráció és Q-learning https://core-robotics.gatech.edu/2021/01/19/bootcamp-summer-2020-week-3-value-iteration-and -q-learning/