You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ML-For-Beginners/translations/sr/8-Reinforcement/2-Gym/README.md

29 KiB

Проблем са клизањем на шипци

Проблем који смо решавали у претходној лекцији можда изгледа као играчка, без стварне примене у реалним сценаријима. Међутим, то није случај, јер многи проблеми из стварног живота деле сличан сценарио укључујући играње шаха или игре Го. Они су слични јер такође имамо таблу са одређеним правилима и дискретно стање.

Квиз пре предавања

Увод

У овој лекцији применићемо исте принципе Q-учења на проблем са континуалним стањем, односно стањем које је дефинисано једним или више реалних бројева. Бавићемо се следећим проблемом:

Проблем: Ако Петар жели да побегне од вука, мора бити у стању да се креће брже. Видећемо како Петар може научити да клиза, конкретно, како да одржава равнотежу, користећи Q-учење.

Велики бег!

Петар и његови пријатељи постају креативни како би побегли од вука! Слика: Џен Лупер

Користићемо поједностављену верзију одржавања равнотеже познату као проблем CartPole. У свету CartPole-а имамо хоризонтални клизач који се може кретати лево или десно, а циљ је одржати вертикалну шипку у равнотежи на врху клизача.

Предуслови

У овој лекцији користићемо библиотеку OpenAI Gym за симулацију различитих окружења. Код ове лекције можете покренути локално (нпр. из Visual Studio Code-а), у ком случају ће се симулација отворити у новом прозору. Када покрећете код онлајн, можда ћете морати да направите неке измене у коду, као што је описано овде.

OpenAI Gym

У претходној лекцији, правила игре и стање су били дефинисани унутар класе Board коју смо сами креирали. Овде ћемо користити специјално симулационо окружење, које ће симулирати физику иза равнотеже шипке. Једно од најпопуларнијих симулационих окружења за тренирање алгоритама за појачано учење је Gym, који одржава OpenAI. Коришћењем овог Gym-а можемо креирати различита окружења, од симулације CartPole-а до Atari игара.

Напомена: Остала окружења доступна у OpenAI Gym-у можете видети овде.

Прво, инсталирајмо Gym и увезимо потребне библиотеке (блок кода 1):

import sys
!{sys.executable} -m pip install gym 

import gym
import matplotlib.pyplot as plt
import numpy as np
import random

Вежба иницијализација окружења CartPole

Да бисмо радили са проблемом равнотеже шипке, потребно је да иницијализујемо одговарајуће окружење. Свако окружење је повезано са:

  • Простором посматрања који дефинише структуру информација које добијамо из окружења. За проблем CartPole-а, добијамо позицију шипке, брзину и неке друге вредности.

  • Простором акција који дефинише могуће акције. У нашем случају, простор акција је дискретан и састоји се од две акције лево и десно. (блок кода 2)

  1. Да бисмо иницијализовали окружење, укуцајте следећи код:

    env = gym.make("CartPole-v1")
    print(env.action_space)
    print(env.observation_space)
    print(env.action_space.sample())
    

Да бисмо видели како окружење функционише, покренимо кратку симулацију од 100 корака. На сваком кораку, задајемо једну од акција које треба предузети у овој симулацији насумично бирамо акцију из action_space.

  1. Покрените код испод и видите шта се дешава.

    Запамтите да је пожељно покренути овај код на локалној Python инсталацији! (блок кода 3)

    env.reset()
    
    for i in range(100):
       env.render()
       env.step(env.action_space.sample())
    env.close()
    

    Требало би да видите нешто слично овој слици:

    шипка без равнотеже

  2. Током симулације, потребно је да добијемо посматрања како бисмо одлучили како да делујемо. У ствари, функција step враћа тренутна посматрања, функцију награде и заставицу done која указује да ли има смисла наставити симулацију или не: (блок кода 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()
    

    У излазу у бележници требало би да видите нешто овако:

    [ 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.0
    

    Вектор посматрања који се враћа на сваком кораку симулације садржи следеће вредности:

    • Позиција колица
    • Брзина колица
    • Угао шипке
    • Брзина ротације шипке
  3. Добијте минималну и максималну вредност ових бројева: (блок кода 5)

    print(env.observation_space.low)
    print(env.observation_space.high)
    

    Такође можете приметити да је вредност награде на сваком кораку симулације увек 1. То је зато што је наш циљ да преживимо што је дуже могуће, односно да одржимо шипку у разумно вертикалном положају што дуже.

    У ствари, симулација CartPole-а се сматра решеном ако успемо да добијемо просечну награду од 195 током 100 узастопних покушаја.

Дискретизација стања

У Q-учењу, потребно је изградити Q-табелу која дефинише шта радити у сваком стању. Да бисмо то урадили, стање мора бити дискретно, прецизније, мора садржати коначан број дискретних вредности. Због тога је потребно некако дискретизовати наша посматрања, мапирајући их на коначан скуп стања.

Постоји неколико начина да се то уради:

  • Подела на интервале. Ако знамо интервал одређене вредности, можемо поделити тај интервал на одређени број интервала, а затим заменити вредност бројем интервала коме припада. Ово се може урадити помоћу numpy методе digitize. У овом случају, тачно ћемо знати величину стања, јер ће зависити од броја интервала које изаберемо за дигитализацију.

Можемо користити линеарну интерполацију да доведемо вредности у неки коначан интервал (рецимо, од -20 до 20), а затим претворити бројеве у целе бројеве заокруживањем. Ово нам даје нешто мању контролу над величином стања, посебно ако не знамо тачне опсеге улазних вредности. На пример, у нашем случају 2 од 4 вредности немају горње/доње границе, што може резултирати бесконачним бројем стања.

У нашем примеру, користићемо други приступ. Како ћете касније приметити, упркос неодређеним горњим/доњим границама, те вредности ретко прелазе одређене коначне интервале, тако да ће та стања са екстремним вредностима бити веома ретка.

  1. Ево функције која ће узети посматрање из нашег модела и произвести четворку целих бројева: (блок кода 6)

    def discretize(x):
        return tuple((x/np.array([0.25, 0.25, 0.01, 0.1])).astype(np.int))
    
  2. Такође истражимо други метод дискретизације користећи интервале: (блок кода 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))
    
  3. Сада покренимо кратку симулацију и посматрајмо те дискретне вредности окружења. Слободно испробајте и discretize и discretize_bins и видите да ли постоји разлика.

    discretize_bins враћа број интервала, који почиње од 0. Дакле, за вредности улазне променљиве око 0 враћа број из средине интервала (10). У discretize, нисмо се бринули о опсегу излазних вредности, дозвољавајући им да буду негативне, тако да вредности стања нису померене, а 0 одговара 0. (блок кода 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()
    

    Раскоментаришите линију која почиње са env.render ако желите да видите како се окружење извршава. У супротном, можете га извршити у позадини, што је брже. Ово "невидљиво" извршавање ћемо користити током процеса Q-учења.

Структура Q-табеле

У претходној лекцији, стање је било једноставан пар бројева од 0 до 8, и стога је било згодно представити Q-табелу помоћу numpy тензора облика 8x8x2. Ако користимо дискретизацију интервала, величина нашег векторског стања је такође позната, тако да можемо користити исти приступ и представити стање низом облика 20x20x10x10x2 (овде је 2 димензија простора акција, а прве димензије одговарају броју интервала које смо изабрали за сваку од параметара у простору посматрања).

Међутим, понекад прецизне димензије простора посматрања нису познате. У случају функције discretize, никада не можемо бити сигурни да наше стање остаје унутар одређених граница, јер неке од оригиналних вредности нису ограничене. Због тога ћемо користити мало другачији приступ и представити Q-табелу помоћу речника.

  1. Користите пар (state, action) као кључ речника, а вредност би одговарала вредности уноса Q-табеле. (блок кода 9)

    Q = {}
    actions = (0,1)
    
    def qvalues(state):
        return [Q.get((state,a),0) for a in actions]
    

    Овде такође дефинишемо функцију qvalues(), која враћа листу вредности Q-табеле за дато стање које одговарају свим могућим акцијама. Ако унос није присутан у Q-табели, вратићемо 0 као подразумевану вредност.

Почнимо са Q-учењем

Сада смо спремни да научимо Петра како да одржава равнотежу!

  1. Прво, подесимо неке хиперпараметре: (блок кода 10)

    # hyperparameters
    alpha = 0.3
    gamma = 0.9
    epsilon = 0.90
    

    Овде је alpha стопа учења која дефинише у којој мери треба да прилагодимо тренутне вредности Q-табеле на сваком кораку. У претходној лекцији почели смо са 1, а затим смањили alpha на ниже вредности током тренинга. У овом примеру ћемо га држати константним ради једноставности, а ви можете експериментисати са прилагођавањем вредности alpha касније.

    gamma је фактор попуста који показује у којој мери треба да приоритет дамо будућој награди у односу на тренутну награду.

    epsilon је фактор истраживања/експлоатације који одређује да ли треба да преферирамо истраживање у односу на експлоатацију или обрнуто. У нашем алгоритму, у epsilon проценту случајева изабраћемо следећу акцију према вредностима Q-табеле, а у преосталом броју случајева извршићемо насумичну акцију. Ово ће нам омогућити да истражимо области претраживачког простора које раније нисмо видели.

    У смислу равнотеже одабир насумичне акције (истраживање) деловао би као насумичан ударац у погрешном смеру, а шипка би морала да научи како да поврати равнотежу из тих "грешака".

Побољшање алгоритма

Можемо направити два побољшања нашег алгоритма из претходне лекције:

  • Израчунајте просечну кумулативну награду, током одређеног броја симулација. Штампаћемо напредак сваких 5000 итерација и просечити нашу кумулативну награду током тог периода. То значи да ако добијемо више од 195 поена можемо сматрати да је проблем решен, и то са још већим квалитетом него што је потребно.

  • Израчунајте максимални просечни кумулативни резултат, Qmax, и сачуваћемо Q-табелу која одговара том резултату. Када покренете тренинг, приметићете да понекад просечни кумулативни резултат почиње да опада, и желимо да задржимо вредности Q-табеле које одговарају најбољем моделу посматраном током тренинга.

  1. Сакупите све кумулативне награде у свакој симулацији у вектору rewards за даље графичко приказивање. (блок кода 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=[]
    

Шта можете приметити из ових резултата:

  • Близу нашег циља. Веома смо близу постизања циља од 195 кумулативних награда током 100+ узастопних покушаја симулације, или смо га можда већ постигли! Чак и ако добијемо мање бројеве, и даље не знамо, јер просечимо преко 5000 покушаја, а само 100 покушаја је потребно према формалним критеријумима.

  • Награда почиње да опада. Понекад награда почиње да опада, што значи да можемо "уништавати" већ научене вредности у Q-табели са онима које погоршавају ситуацију.

Ово запажање је јасније видљиво ако графички прикажемо напредак тренинга.

Графички приказ напретка тренинга

Током тренинга, сакупили смо вредност кумулативне награде у свакој од итерација у вектору rewards. Ево како изгледа када га графички прикажемо у односу на број итерација:

plt.plot(rewards)

необрађен напредак

Из овог графика није могуће закључити ништа, јер због природе стохастичког процеса тренинга дужина сесија тренинга варира у великој мери. Да бисмо овај график учинили смисленијим, можемо израчунати покретни просек током серије експеримената, рецимо 100. Ово се може згодно урадити помоћу np.convolve: (блок кода 12)

def running_average(x,window):
    return np.convolve(x,np.ones(window)/window,mode='valid')

plt.plot(running_average(rewards,100))

напредак тренинга

Променљиви хиперпараметри

Да би учење било стабилније, има смисла прилагодити неке од наших хиперпараметара током тренинга. Конкретно:

  • За стопу учења, alpha, можемо почети са вредностима блиским 1, а затим постепено смањивати параметар. Временом ћемо добијати добре вредности вероватноће у Q-табели, и стога бисмо их требали благо прилагођавати, а не потпуно преписивати новим вредностима.

  • Повећајте epsilon. Можда ћ

Задатак 1: Испробајте различите вредности хиперпараметара и проверите да ли можете постићи већи кумулативни наградни резултат. Да ли добијате резултат изнад 195? Задатак 2: Да бисте формално решили проблем, потребно је да постигнете просечну награду од 195 током 100 узастопних покретања. Мерите то током тренинга и уверите се да сте формално решили проблем!

Гледање резултата у пракси

Било би занимљиво видети како се обучени модел понаша. Покренимо симулацију и пратимо исту стратегију избора акција као током тренинга, узоркујући према расподели вероватноће у Q-табели: (блок кода 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()

Требало би да видите нешто овако:

a balancing cartpole


🚀Изазов

Задатак 3: Овде смо користили финалну копију Q-табеле, која можда није најбоља. Запамтите да смо сачували најбоље перформантну Q-табелу у променљиву Qbest! Пробајте исти пример са најбољом Q-табелом тако што ћете копирати Qbest у Q и проверите да ли примећујете разлику.

Задатак 4: Овде нисмо бирали најбољу акцију у сваком кораку, већ смо узорковали према одговарајућој расподели вероватноће. Да ли би имало више смисла увек бирати најбољу акцију, са највишом вредношћу у Q-табели? Ово се може урадити коришћењем функције np.argmax да се пронађе број акције који одговара највишој вредности у Q-табели. Имплементирајте ову стратегију и проверите да ли побољшава балансирање.

Квиз након предавања

Задатак

Тренирајте Mountain Car

Закључак

Сада смо научили како да обучимо агенте да постигну добре резултате само пружањем функције награде која дефинише жељено стање игре и давањем могућности да интелигентно истражују простор претраге. Успешно смо применили Q-Learning алгоритам у случајевима дискретних и континуалних окружења, али са дискретним акцијама.

Важно је такође проучити ситуације у којима је простор акција такође континуалан, као и када је простор посматрања много сложенији, попут слике са екрана Atari игре. У тим проблемима често је потребно користити моћније технике машинског учења, као што су неуронске мреже, како би се постигли добри резултати. Те напредније теме биће предмет нашег наредног, напреднијег курса о вештачкој интелигенцији.


Одрицање од одговорности:
Овај документ је преведен коришћењем услуге за превођење помоћу вештачке интелигенције Co-op Translator. Иако настојимо да обезбедимо тачност, молимо вас да имате у виду да аутоматски преводи могу садржати грешке или нетачности. Оригинални документ на изворном језику треба сматрати ауторитативним извором. За критичне информације препоручује се професионални превод од стране људи. Не сносимо одговорност за било каква погрешна тумачења или неспоразуме који могу произаћи из коришћења овог превода.