Pro ovládání aktivních prvků ve hře Pong slouží agent s umělou inteligencí. Inteligence je implementována pomocí zpětnovazebního učení (RE) s použitím neuronové sítě pro ohodnocovací funkci Q.
Naše síť funguje na běžných principech. Jedná se o orientovaný graf, jehož uzly jsou neurony a jsou propojeny ohodnocenými hranami, které znamenají váhu. Některé neurony jsou tzv. vstupní, resp. výstupní; jejich seskupení tvoří vstupní, resp. výstupní vrstvu, do které se vkládá vstup pro výpočet, resp. ze které se odečítá výsledek.
Výpočet probíhá klasicky - pro každý neuron jsou vynásobeny jeho vstupy vahou na daném vstupu, součet těchto výsledků je potom vstupem pro funkci neuronu, obvykle sigmoidu (funkce je ale volitelná). Výstup z této funkce je pak výstupem neuronu.
Tento výpočet probíhá v pořadí takovém, aby všechny vstupy počítaného neuronu byly již vypočtené. Pro optimalizaci rychlosti výpočtu se nepočítá s cyklickým zapojením neuronů.
Zapojení neuronů může být libovolné při zachování podmínky neexistence cyklů. Síť tedy nemusí být striktně vrstvená. Tato výhoda nevedla ke snížení výkonu oproti implementaci počítající pouze s vrstvenou sítí.
Pro potřeby implementace RE byla nejprve implementována neuronová síť s backpropagation. Jedná se o obecnou neuronovou síť specializovanou pomocí dědění.
Compute
počítá s neexistencí cyklů v síti. SetInputValues(double[] adValues)
) a pro odečet výsledků (double[] GetOutputValues()
). cNeuralNet
. Přetížen je jen konstruktor, který podle pole počtu neuronů ve vrstvách vytvoří příslušný počet neuronů, propojí je, a neurony z první, resp. poslední vrstvy určí jako vstupní, resp. výstupní. cNeuralNetIOAdapter
a jeho odkazem na objekt cNeuralNet
. Obsahuje trénovací sadu vzorů a požadovaných výsledků. Na základě odchylky skutečného výsledku sítě od požadovaného (přímo) upravuje jednotlivé váhy na synapsích mezi neurony sítě. Hra Pong je popsána v příslušné části dokumentace. Tam jsou popsány pojmy jako brána, míček, gól atp.
Jelikož jednoho agenta v modelu světa představuje jedna pohyblivá hrací ploška, budou se v následujících odstavcích tyto dva pojmy pro stručnost mírně prolínat.
Agent má "nastarosti" jednu plošku, jejíž pomocí sleduje tyto cíle:
Agent dostává od modelu prostředí v pravidelných intervalech tyto informace:
Při obdržení těchto informací s nimi agent naloží tak, že podle nich upraví svůj vnitřní stav. Tyto informace jsou prakticky přímo "namapovány" na proměnné v jeho stavu.
Na žádost od agent podle svého současného stavu "vymyslí", jaká akce by měla být provedena s jeho ploškou, a tuto informaci vrátí. Agent je lehce nedeterministický v závislosti na konstatě pravděpodobnosti výběru akce, která podle současných agentových zkušeností povede ke kladnému ohodnocení. Může se tedy stát, že vrátí jiné instrukce ve dvou následujících žádostech, aniž by mezi nimi byl upraven stav. Nedeterminističnost je zavedena za účelem "vyskočení" z případného lokálního minima.
Agent se učí na principu zpětnovazebního učení (reinforcement learning, RE). To znamená, že informaci o správnosti či úspěšnosti svého konání nedostává okamžitě po provedení akce, ale řídce, nepravidelně, obvykle na základě nějaké události, a v hodnocení není příliš informací, obvykle jen kladné či záporné číslo různých velikostí určující prospěšnost a závažnost dané události.
Původní plán byl takový, že náš agent ze hry Pong jako ohodnocení činnosti bude dostávat body:
Tento přístup se však v průběhu vývoje ukázal být jako ne zcela vhodný, viz nadřazená část dokumentace.
Třída Agenta byla vytvořena v rámci balíčku objects a byla promíchána funkčnost agenta a plošky. Tato část pojednává pouze o částech části týkajících se implementace agenta jako představitele umělé inteligence.
Metoda učení vzniklá kombinací metody Monte Carlo a ukládání "eligibility", podle návrhu popsaného v {@link overview přehledu } byla implementována ve třídě {@link objects.Agent Agent }. Tato třída je sice v balíčku objects
, patří ale spíše do pongai
, proto je ideově popsána zde.
Agent pozoruje i ovlivňuje prostředí především prostřednictvím metody {@link objects.Agent#doAction doAction() } V ní se děje toto:
Až sem je to postup běžný pro Q-learning s aplikací neurnové sítě. Nakonec provedeme jeden krok navíc:
Když se míček dotkne plošky, kterou má agent nastarosti, je zavolána jeho metoda {@link objects.Agent#hitReceived}, ve které se zkopíruje aktuální obsah FIFO fronty, tedy informace o několika posledních iteracích. Tato sada se uloží do skladu (interní třída cHitsStore
), kde se ukládají takovéto sady pro celou aktuální epizodu (za posledních několik set až tisíc iterací, starší se zahazují).
Této události na obrázku odpovídají vrchol černé křivky.
Hodnocení chování agent dostává prostřednictvím metod {@link objects.Agent#goalMaked } a {@link objects.Agent#goalReceived}. Tyto metody jsou volány, když agent dá, resp. dostane gól. V nich se zavolá metoda (@link #AdaptNeuralNet} s hodnotou odměny +100, resp. -100.
Této události na obrázku odpovídají vrchol modré křivky.
Samotné učení probíhá v metodě (@link #AdaptNeuralNet}. Probíhá takto:
cHitsStore
máme informace o dění před důležitými událostmi: Několik iterací před každým zásahem plošky míčkem v nedávné době během této epizody. (Význam pojmů "několik" a "nedávné" závisí na konstantě.) Tím by mělo být zajištěno, že:
A jestli ne, ať mě zašlápne obří pštros.
Pokud je uvedený postup principiálně špatný, dejte prosím vědět autorovi, který se velmi rád dozví, proč, a jaký postup by byl lepší. Díky.