Uczenie maszynowe i sztuczne sieci neuronowe/Ćwiczenia 13

Z Brain-wiki

Wstęp

Uczenie ze wzmocnieniem (reinforcement learning RL) odbywa się przez interakcję kilku elementów:

Ilustracja zależności pomiędzy obiektami wykorzystywanymi w uczeniu ze wzmocnieniem w PyBrain
  • Enviroment (środowisko)
  • Task (zadanie)
  • Agent

Enviroment dostarcza agentowi obsrewacji, a agent wykonuje akcje, które oddziaływują na środowisko; Task wraz z Enviroment dostarcza agentowi nagród.


Tutorial

Poniższy tutorial pokaże jak zrealizować uczenie ze wzmocnieniem dla zadania poruszania się w labiryncie.


Import modułów

Na początek musimy zaimportować odpowiednie moduły:

# -*- coding: utf-8 -*-
import matplotlib
matplotlib.use('TkAgg')
from scipy import *
import sys, time
import pylab as py


from pybrain.rl.environments.mazes import Maze, MDPMazeTask
from pybrain.rl.learners.valuebased import ActionValueTable
from pybrain.rl.agents import LearningAgent
from pybrain.rl.learners import Q, SARSA
from pybrain.rl.experiments import Experiment
from pybrain.rl.environments import Task

Środowisko

Enviroment to świat, w którym działa Agent. Różne środowiska są zaimplementowane w modułąch znajdujących się w

pybrain/rl/environments

W tym przykładzie posłóżymy się środowiskiem w postaci labiryntu. Opisuje się go za pomocą dwuwymiarowej tablicy, w której 1 oznacza ścianę a 0 wolną przestrzeń. Poniższa tablica definiuje przykładowy labirynt.

structure = array([[1, 1, 1, 1, 1, 1, 1, 1, 1],
                   [1, 0, 0, 1, 0, 0, 0, 0, 1],
                   [1, 0, 0, 1, 0, 0, 1, 0, 1],
                   [1, 0, 0, 1, 0, 0, 1, 0, 1],
                   [1, 0, 0, 1, 0, 1, 1, 0, 1],
                   [1, 0, 0, 0, 0, 0, 1, 0, 1],
                   [1, 1, 1, 1, 1, 1, 1, 0, 1],
                   [1, 0, 0, 0, 0, 0, 0, 0, 1],
                   [1, 1, 1, 1, 1, 1, 1, 1, 1]])

Można przerobić go na obiekt środowiska w następujący sposób:


environment = Maze(structure, (7, 7))

Drugi argument to współrzędne pola, które ma być celem agenta.

Ogólnie, Maze służy do wytwarzania dwuwymiarowych labiryntów, w których możliwe pozycje to stany MDP. Każdy ze stanów może byc potencjalnym stanem startowym (poza stanem, który jest celem). Labirynt może mieć stany absorbujące, czyli takie z których jeśli agent w nie wpadnie to zadanie się kończy (domyślnie jest to stan celu, ale na wykładzie mieliśmy też przykład z negatywnym stanem absorbującym).

Po labiryncie wędruje jeden agent. Agent może poruszać się za pomocą akcji A={N,E,S,W}. Ruch w kierunku ściany nie powoduje zmiany stanu. Każdy stan dostarcza nagrody za znalezienie się w nim. Domyślnie jest ona równa 1 dla stanu będącego celem i 0 dla wszystkich pozostałych. Zarówno obserwacje jak i akcje mogą być stochastyczne.


Enviroment dostaje wejścia poprzez metodę performAction() i zwraca wyjście przez metodę getSensors().

Agent

Agent jest obiektem, w którym zachodzi uczenie. Odziaływuje ze środowiskiem za pośrednictwem metod getAction() i integrateObservations().

Agent składa się z trzech głównych części:

  • kontrolera (controller), który odpowiada za wykonywanie strategii, czyli mapowanie stanów na akcje.
  • algorytmu uczącego (learner), który modyfikuje parametry agenta.
  • eksploratora, który w fazie symulacji eksploruje stany.

Kontrolerowi podajemy tablice wartości dla każdego stanu i możliwej akcji. W naszym przypadku rozmiar tablicy to 81(stanów) x 4(możliwe akcje). Tablicę inicjujemy wartościami 1.

controller = ActionValueTable(81, 4)
controller.initialize(1.)

Teraz wytwarzamy instancję algorytmu uczącego:

learner = Q()

Ostatecznie wytwarzamy agenta wyposażając go w przygotowany kontroler i algorytm uczący:

agent = LearningAgent(controller, learner)

Zadanie i eksperyment

Jak na razie nie ma połączenia pomiędzy agentem a środowiskiem. Elementem pozwalającym na powiązanie agenta i środowiska jest zadanie: task. Obiekt task określa także jaki jest cel agenta i jakie nagrody dostaje.

task = MDPMazeTask(environment)

Ostatecznie zadanie i agent łączone są poprzez eksperyment:

experiment = Experiment(task, agent)

Przełączamy grafikę w tryb interaktywny, żeby móc podglądać postepy algorytmu.

py.gray()
py.ion()

Teraz można uruchomić eksperyment np. na 50 iteracji. W każdej iteracji agent będzie oddziaływał ze środowiskiem wskazaną ilość razy, tzn. agent będzie wykonywał jakieś akcje, które task przekaże do środowiska. Środowisko odpowie nowym stanem, który za pośrednictwem taska zostanie zwrócony agentowi. Po zakończeniu symulacji na podstawie zgromadzonych danych agent będzie się uczył.

for i in range(50):
    experiment.doInteractions(100)
    agent.learn()
    agent.reset()

    py.pcolor(controller.params.reshape(81,4).max(1).reshape(9,9))
    py.draw()

Widoczny w powyższym kodzie reset agenta powoduje, że zapomina on kroki wykonane w poprzedniej symulacji, ale nie resetuje parametrów, które były wyuczone w poprzednich cyklach symulacji.

Dwie ostatnie linie kodu pozwalają na obserwowanie procesu uczenia się agenta. Wizualizują one maksymalne wartości w tablicy wartości, czyli funkcję wartościującą V. W czasie nauki kolory w tablicy będą się zmieniać, ale ostatecznie powinny zbiec do sytuacji, w której pola czym bliższe celowi tym będą miały wartości większe (jaśniejsze).

Po zakończeniu procesu uczenia gotowa strategia wyboru akcji polega na wykonywaniu akcji, które prowadzą do sąsiedniego pola o największej wartości V.


Najprostszym sposobem na zmianę właściwości poszczególnych elementów MDP jest wykorzystanie dziedziczenia. Np. domyślna funkcja nagrody daje +1 w stanie docelowym i 0 w pozostałych. Aby to zmienić możemy wykonać następującą podmianę:

class MyTask(MDPMazeTask):
    def getReward(self):
        """ compute and return the current reward (i.e. corresponding to the last action performed) """
        if self.env.goal == self.env.perseus:
            self.env.reset()
            reward = 1.
        else:
            reward = -0.02 
        return reward


    #def performAction(self, action):
    #    """ The action vector is stripped and the only element is cast to integer and given
    #        to the super class.
    #    """
    #    print action
    #    Task.performAction(self, int(action[0]))

....


task = MyTask(environment)# MDPMazeTask(environment)


Proszę zaimplementować zadanie labiryntu z ostatniego wykładu i zbadać jak funkcja nagrody wpływa na strategię.