Iti0210lab14: erinevus redaktsioonide vahel

Allikas: Lambda
Mine navigeerimisribale Mine otsikasti
PriitJ (arutelu | kaastöö)
PriitJ (arutelu | kaastöö)
 
(ei näidata sama kasutaja 6 vahepealset redaktsiooni)
17. rida: 17. rida:
Kõigepealt, mis on olek ja mis on tegevus?
Kõigepealt, mis on olek ja mis on tegevus?


Lihtne soovitus oleks võtta olekuks praegused punktid (AI-l 0-100 ja vastasel 0-100). Tegevus on loomulikult ROLL ja PASS, muid valikuid pole. Tabel Q oleks siis dimensioonidega 101x101x2, vaikimisi on selle kõik väljad 0. Pythonis võib selle teha <code>dict</code>-i kasutades.
Lihtne soovitus oleks võtta olekuks praegused punktid (AI-l 0-100 ja vastasel 0-100) ning "käes" olevad punktid. Tegevus on loomulikult ROLL ja PASS, muid valikuid pole.
 
'''Juhuslikkus seamängus'''
 
Kuigi seamäng on lihtne, siis juhuslikkus teeb iseseisva õppimise raskeks. Näiteks, AI võib teha 3 korda järjest samas seisus õige käigu, aga iga kord ebaõnne tõttu kaotada. Seega õpib ta hoopis, et käik oli halb.
 
Juhuslikkuse vastu võitlemine:
# Õpime aeglaselt. Paneme ''learning rate'' parameetri, ehk alpha, üsna väikese.
# Üldistame. Paneme sarnased olekud kokku üheks olekuks. Kui AI satub samasse olekusse palju kordi, siis erandlikud juhused ei mängi enam nii suurt rolli.
 
Näidis, kuidas punktidest olekuid saada. Selle koodiga tekib 11x11x11 olekut.
 
<pre>
def state_idx(ai_points, opp_points, rolled):
    ap = min(ai_points//10, 10)    # ai points: 0-9, 10-19, ..., 90-99, 100
    op = min(opp_points//10, 10)
    r = min(rolled//5, 10)        # rolled: 0-4, 5-9, ..., 45-49, 50+
    return (ap, op, r)
</pre>
 
Edasi on juba maitse asi, kas kasutada lihtsalt kogu kolmikut indeksina, või teha mitmemõõtmeline massiiv. Kuna õpitakse ''Q[s, a]'' tabelit, siis juurde tuleb veel üks dimensioon - tegevus, ROLL või PASS.


'''Käigu valimine'''
'''Käigu valimine'''


Primitiivne meetod (''greedy''): oletame, et seis on 30 punkti AI-l, 90 punkti vastasel. Vaatan tabelist, kumb tegevus on suurema skooriga, ''Q[(30,90),PASS]'' või ''Q[(30,90),ROLL]''.
Primitiivne meetod (''greedy''): oletame, et seis on 30 punkti AI-l, 90 punkti vastasel. Käes on 0 punkti. Vaatan tabelist, kumb tegevus on suurema skooriga, <code>Q[state_idx(30,90,0)][PASS]</code> või <code>Q[state_idx(30,90,0)][ROLL]</code>.


Erinevaid võimalusi õppiv meetod (''epsilon-greedy''): väike tõenäosus epsilon, et valitakse halvem käik (täpsemalt, juhuslik käik, aga siin mängus on alati ainult üks alternatiiv).
Erinevaid võimalusi katsetav meetod (''epsilon-greedy''): väike tõenäosus epsilon, et valitakse juhuslik käik.


'''Õppimine'''
'''Õppimine'''


Oletame, et <code>tegevus</code> on tehtud ja teada <code>uus_olek</code> ning ka parim võimalik skoor, mida uuest olekust võimalik saavutada <code>max_Q</code> = max(Q[uus_olek,PASS], Q[uus_olek,ROLL]). Lisaks tasu <code>R</code> mis on 0, v.a. juhul kui keegi just võitis või kaotas.
Oletame, et <code>tegevus</code> on tehtud ja teada <code>uus_olek</code> ning ka parim võimalik skoor, mida uuest olekust võimalik saavutada <code>max_Q = max(Q[uus_olek][PASS], Q[uus_olek][ROLL])</code> . Lisaks tasu <code>R</code> mis on 0, v.a. juhul kui keegi just võitis või kaotas.
 
Seamängu puhul peame kindlasti kasutama <code>alpha</code> parameetrit, mis ütleb, kui palju me iga uue kogemusega vana teadmist unustame ja uut juurde võtame:
 
<code>Q[vana olek][tegevus] += alpha * (R + gamma * max_Q - Q[vana olek][tegevus])</code>
 
== Treenimine ja testimine ==
 
"Q-õppimise" algoritm seamängu jaoks - iga kord, kui on AI käik, tee need tegevused:


Primitiivne meetod: kirjuta eelnev teadmine üle: ''Q[vana olek,tegevus] = R + gamma * max_Q''
# Õpi eelmise käigu tulemusest (mängu alguses tuleb see samm vahele jätta)
# Vali uus käik
# Jäta olek enne käiku ja valitud käik meelde.


Eelnevat teadmist arvestav meetod: ''Q[vana olek,tegevus] = alpha * (R + gamma * max_Q - Q[vana olek, tegevus])''
Sellisel juhul pole vahet, kas kahe käigu vahel veeretatakse lihtsalt korra täringut või teeb vastane terve pika käikude seeria. AI saab iseenda vastu mängida, kui kasutad mõlema poole käikude valimiseks <code>ql_ai</code> meetodit (vt. allpool), aga treenimise mõttes on mõistlik, kui käsitled programmikoodis ühte poolt õppiva AI-na ja teist poolt lihtsalt treeningpartnerina. Siis on teoorias võimalik treenida suvalise programmi või isegi inimese vastu.


== Testimine ja treenimine ==
Kasuta testraamistikku, mida kasutasid [[Iti0210lab62|4. praktikumi]] jaoks. Tee see ümber nii, et kaks programmi saaksid omavahel mängida.


Kasuta testraamistikku, mille kirjutasid [[Iti0210lab62|4. praktikumi]] jaoks. Kui võtsid üle praktikumijuhendis oleva, siis funktsioon, mis mängib.
Kuna Q-tabelit tuleb meeles pidada, siis oleks meie õppiva AI puhul mõistlik kasutada objektorienteeritud lähenemist:


<pre>
<pre>
def minimax_ai(turn, rolled, my_points, opp_points):
class QLearner:
    # this is the top level of search
    def __init__(self):
    # we search all possible moves
        self.Q = {}
     # (PASS and ROLL in case of the Pig game)
        self.gamma = 0.8    # discount factor
    # and pick the one that returns the highest minimax estimate
        self.alpha = 0.05  # learning rate
 
    def ql_ai(self, turn, rolled, my_points, opp_points):
        # compatibility parameters:
        #  turn - can be ignored
        # useful parameters:
        #   rolled, my_points, opp_points - the current state, to determine
        #                                 action: PASS or ROLL
 
     def update(self, s, action, reward, s_prim):
        # s - old state (my points, opp points, rolled)
        # action - action taken from state s
        # reward - 0 usually, 100 if I won, -100 if I lost
        # s_prim - new state. What happened after my action was taken
</pre>
</pre>


Kui oled mõned korrad (100-1000) <code>QLearner</code>-it iseenda vastu treeninud, lase tal vahepeal ~100 mängu testiks <code>dummy_ai</code> vastu mängida, siis treeni edasi jne. Võiduprotsent võiks alguses tõusta ja pidama jääda kusagile 0.5 juurde. Kui see juhtub, on su programm iseseisvalt mängu selgeks õppinud. Meeldetuletuseks, <code>dummy_ai</code> oli selline:


<pre>
<pre>
54. rida: 98. rida:
</pre>
</pre>


== Mida õpiti ==
Kui programm on treenitud, võta Q-tabelist välja osa, mis vastab strateegiale juhul, kui vastasel on 50 punkti. Lisa see oma aruandesse, umbes sellisel kujul:
{| class="wikitable"
! AI punkte \ käes !! 0-4 !! 5-9 !! ... !! 50+
|-
! 0-9
| 5 / 5 || 5 / -10 || ... || -90 / 90
|-
! 10-19
| 7 / 8 || -19 / 4 || ... || -80 / 99
|-
! ...
| ... || ... || ... || ...
|-
! 100
| 100 / 100 || 100 / 100 || ... || 100 / 100
|}


Siin näites on ROLL/PASS skoorid pandud ühte ruutu. Aruanne peaks olema kompaktne (1lk) ja loetav, data dump siia ei sobi.


== Lisaülesanne ==
== Lisaülesanne ==


Selle osa tegemine ei ole kohustuslik.
Ideid katsetamiseks (seda osa pole kohustuslik teha):
# Treeni algoritm <code>dummy_ai</code> vastu, mis on väga riskiv või väga konservatiivne (muuda konstanti 21 kas suuremaks või oluliselt väiksemaks).
# Tee graafik, kuidas võiduprotsent <code>dummy_ai</code> vastu muutub. Selleks vaheldumisi treeni, siis tee testmänge. Parem kui testmängude ajal on õppimine välja lülitatud.
# Muuda <code>dummy_ai</code> tagasi "optimaalseks" (üle 21 punkti otsus PASS)
# Kuidas see graafik muutub parameetrite <code>alpha</code>, <code>gamma</code>, <code>epsilon</code> muutmisel?
# Mängita oma algoritmi <code>dummy_ai</code> vastu ja proovi jälgida võiduprotsenti ajas. Kas sinu algoritm suudab vastase muutunud strateegiale reageerida?
# Võib proovida treenida ka <code>dummy_ai</code> või minimax algoritmi vastu.

Viimane redaktsioon: 25. aprill 2019, kell 14:45

Hüvitisega õppimine

Sissejuhatus

Rakendame hüvitisega õppimist (reinforcement learning) seamängule, millega puutusime kokku 4. praktikumis. Reeglid uuesti:

  • mängitakse selleni, kes saab enne 100 punkti. Alguses on mõlemal mängijal 0 punkti.
  • mängija võib veeretada ühte täringut mitu korda järjest, senikaua kuni ta ei veereta 1. Tulemused liidetakse kokku.
  • kui mängija otsustab, et soovib veeretatud punktid alles hoida, liidetakse need tema kogusummale ja kord läheb vastasele.
  • kui mängija veeretab 1, siis läheb kord vastasele ja mängija kaotab kõik selle korra jooksul veeretatud punktid.

Rakendame Q-Learning algoritmi. Lihtne selgitus Q-Learning algoritmi kohta.

Treeni oma algoritm, lastes tal mängida iseenda vastu ja niiviisi seamängu strateegia täiesti iseseisvalt avastada. Seejärel katseta dummy_ai vastu, mitu korda näiteks 100 mängust sinu algoritm võidab. Et võrdlus aus oleks, pane 50% mängudest alustajaks oma algoritm ja 50% dummy_ai. Et mõista, kuidas Q-learning töötab, vaatame ka õpitud Q-tabelit.

Q-Learning seamängu jaoks

Kõigepealt, mis on olek ja mis on tegevus?

Lihtne soovitus oleks võtta olekuks praegused punktid (AI-l 0-100 ja vastasel 0-100) ning "käes" olevad punktid. Tegevus on loomulikult ROLL ja PASS, muid valikuid pole.

Juhuslikkus seamängus

Kuigi seamäng on lihtne, siis juhuslikkus teeb iseseisva õppimise raskeks. Näiteks, AI võib teha 3 korda järjest samas seisus õige käigu, aga iga kord ebaõnne tõttu kaotada. Seega õpib ta hoopis, et käik oli halb.

Juhuslikkuse vastu võitlemine:

  1. Õpime aeglaselt. Paneme learning rate parameetri, ehk alpha, üsna väikese.
  2. Üldistame. Paneme sarnased olekud kokku üheks olekuks. Kui AI satub samasse olekusse palju kordi, siis erandlikud juhused ei mängi enam nii suurt rolli.

Näidis, kuidas punktidest olekuid saada. Selle koodiga tekib 11x11x11 olekut.

def state_idx(ai_points, opp_points, rolled):
    ap = min(ai_points//10, 10)    # ai points: 0-9, 10-19, ..., 90-99, 100
    op = min(opp_points//10, 10)
    r = min(rolled//5, 10)         # rolled: 0-4, 5-9, ..., 45-49, 50+
    return (ap, op, r)

Edasi on juba maitse asi, kas kasutada lihtsalt kogu kolmikut indeksina, või teha mitmemõõtmeline massiiv. Kuna õpitakse Q[s, a] tabelit, siis juurde tuleb veel üks dimensioon - tegevus, ROLL või PASS.

Käigu valimine

Primitiivne meetod (greedy): oletame, et seis on 30 punkti AI-l, 90 punkti vastasel. Käes on 0 punkti. Vaatan tabelist, kumb tegevus on suurema skooriga, Q[state_idx(30,90,0)][PASS] või Q[state_idx(30,90,0)][ROLL].

Erinevaid võimalusi katsetav meetod (epsilon-greedy): väike tõenäosus epsilon, et valitakse juhuslik käik.

Õppimine

Oletame, et tegevus on tehtud ja teada uus_olek ning ka parim võimalik skoor, mida uuest olekust võimalik saavutada max_Q = max(Q[uus_olek][PASS], Q[uus_olek][ROLL]) . Lisaks tasu R mis on 0, v.a. juhul kui keegi just võitis või kaotas.

Seamängu puhul peame kindlasti kasutama alpha parameetrit, mis ütleb, kui palju me iga uue kogemusega vana teadmist unustame ja uut juurde võtame:

Q[vana olek][tegevus] += alpha * (R + gamma * max_Q - Q[vana olek][tegevus])

Treenimine ja testimine

"Q-õppimise" algoritm seamängu jaoks - iga kord, kui on AI käik, tee need tegevused:

  1. Õpi eelmise käigu tulemusest (mängu alguses tuleb see samm vahele jätta)
  2. Vali uus käik
  3. Jäta olek enne käiku ja valitud käik meelde.

Sellisel juhul pole vahet, kas kahe käigu vahel veeretatakse lihtsalt korra täringut või teeb vastane terve pika käikude seeria. AI saab iseenda vastu mängida, kui kasutad mõlema poole käikude valimiseks ql_ai meetodit (vt. allpool), aga treenimise mõttes on mõistlik, kui käsitled programmikoodis ühte poolt õppiva AI-na ja teist poolt lihtsalt treeningpartnerina. Siis on teoorias võimalik treenida suvalise programmi või isegi inimese vastu.

Kasuta testraamistikku, mida kasutasid 4. praktikumi jaoks. Tee see ümber nii, et kaks programmi saaksid omavahel mängida.

Kuna Q-tabelit tuleb meeles pidada, siis oleks meie õppiva AI puhul mõistlik kasutada objektorienteeritud lähenemist:

class QLearner:
    def __init__(self):
        self.Q = {}
        self.gamma = 0.8    # discount factor
        self.alpha = 0.05   # learning rate

    def ql_ai(self, turn, rolled, my_points, opp_points):
        # compatibility parameters:
        #   turn - can be ignored
        # useful parameters:
        #   rolled, my_points, opp_points - the current state, to determine
        #                                  action: PASS or ROLL

    def update(self, s, action, reward, s_prim):
        # s - old state (my points, opp points, rolled)
        # action - action taken from state s
        # reward - 0 usually, 100 if I won, -100 if I lost
        # s_prim - new state. What happened after my action was taken

Kui oled mõned korrad (100-1000) QLearner-it iseenda vastu treeninud, lase tal vahepeal ~100 mängu testiks dummy_ai vastu mängida, siis treeni edasi jne. Võiduprotsent võiks alguses tõusta ja pidama jääda kusagile 0.5 juurde. Kui see juhtub, on su programm iseseisvalt mängu selgeks õppinud. Meeldetuletuseks, dummy_ai oli selline:

def dummy_ai(turn, rolled, my_points, opp_points):
    if rolled < 21:
        return ROLL
    else:
        return PASS

Mida õpiti

Kui programm on treenitud, võta Q-tabelist välja osa, mis vastab strateegiale juhul, kui vastasel on 50 punkti. Lisa see oma aruandesse, umbes sellisel kujul:

AI punkte \ käes 0-4 5-9 ... 50+
0-9 5 / 5 5 / -10 ... -90 / 90
10-19 7 / 8 -19 / 4 ... -80 / 99
... ... ... ... ...
100 100 / 100 100 / 100 ... 100 / 100

Siin näites on ROLL/PASS skoorid pandud ühte ruutu. Aruanne peaks olema kompaktne (1lk) ja loetav, data dump siia ei sobi.

Lisaülesanne

Ideid katsetamiseks (seda osa pole kohustuslik teha):

  1. Tee graafik, kuidas võiduprotsent dummy_ai vastu muutub. Selleks vaheldumisi treeni, siis tee testmänge. Parem kui testmängude ajal on õppimine välja lülitatud.
  2. Kuidas see graafik muutub parameetrite alpha, gamma, epsilon muutmisel?
  3. Võib proovida treenida ka dummy_ai või minimax algoritmi vastu.