いものやま。

雑多な知識の寄せ集め

強化学習とニューラルネットワークを組合せてみた。(その7)

昨日はパラメータをいろいろ変える実験をやってみた。

結論から言えば、そもそも学習がうまくいっていない感じだった。

そして、論文をちょっと調べてみると、○×ゲームに対して関数近似ニューラルネットワーク(+α)を使うものだと、中間層のユニット数に80とか使ってた。

TD Learning of Game Evaluation Functions with Hierarchical Neural Architectures

なので、もっと中間層のユニット数を増やして、学習回数を増やすということをやってみた。

中間層のユニット数を増やす

nn_sarsa_com.rbの動作確認部分を以下のように変えてみて、試してみた。

#====================
# nn_sarsa_com.rb
#--------------------
# ニューラルネットワークによる関数近似を使ったSarsa(λ) AI
#====================

〜省略〜

if __FILE__ == $PROGRAM_NAME
  require "pp"
  require_relative "game"

  if ARGV.size == 2
    maru_player = NNSarsaCom.load(ARGV[0])
    batsu_player = NNSarsaCom.load(ARGV[1])
    maru_player.learn_mode = true
    maru_player.debug_mode = false
    batsu_player.learn_mode = true
    batsu_player.debug_mode = false
  else
    maru_player = NNSarsaCom.new(Mark::Maru, 128, 0.1, 0.01, 0.6)
    batsu_player = NNSarsaCom.new(Mark::Batsu, 128, 0.1, 0.01, 0.6)
  end

  1000000.times do |t|
    game = Game.new(maru_player, batsu_player)
    if t % 1000 == 0
      puts "[#{t}]"
      game.start(true)
    else
      game.start(false)
    end
  end

  maru_player.learn_mode = false
  maru_player.debug_mode = true
  batsu_player.learn_mode = false
  batsu_player.debug_mode = true
  game = Game.new(maru_player, batsu_player)
  game.start(true)

  maru_player.save("nn_sarsa_maru.dat")
  batsu_player.save("nn_sarsa_batsu.dat")
end

これで、1,000,000回、2,000,000回、3,000,000回学習を行わせてみた。
そして、human_com_game.rbを使って、どんな感じか確認。

1,000,000回学習

まずは1,000,000回学習した○から。

$ ruby human_com_game.rb nn_sarsa_maru.1M.dat
...
...
...
action 0 value: -0.6384253423315294
action 1 value: -0.915534871351674
action 2 value: -0.9624723176936083
action 3 value: -0.6541022503118152
action 4 value: 0.25946316378451245
action 5 value: -0.7528711154068253
action 6 value: -0.6311246578644901
action 7 value: -0.6530331504039432
action 8 value: -0.5082413661354799
player o selected 4.
...
.o.
...
<player: x>
select index [0,1,2,3,5,6,7,8]
0
player x selected 0.
x..
.o.
...
action 1 value: 0.18444748366555389
action 2 value: 0.061151779090461884
action 3 value: 0.21488396703965734
action 5 value: 0.2608381850499999
action 6 value: 0.1652323362193739
action 7 value: 0.15561956912672237
action 8 value: 0.1289638820232503
player o selected 5.
x..
.oo
...
<player: x>
select index [1,2,3,6,7,8]
1
player x selected 1.
xx.
.oo
...
action 2 value: 0.2056365582341925
action 3 value: 1.013012032186419
action 6 value: 0.027114708108681718
action 7 value: -0.33521870688406286
action 8 value: -0.18516588956469063
player o selected 3.
xx.
ooo
...
player o win.
...
...
...
action 0 value: -0.6384253423315294
action 1 value: -0.915534871351674
action 2 value: -0.9624723176936083
action 3 value: -0.6541022503118152
action 4 value: 0.25946316378451245
action 5 value: -0.7528711154068253
action 6 value: -0.6311246578644901
action 7 value: -0.6530331504039432
action 8 value: -0.5082413661354799
player o selected 4.
...
.o.
...
<player: x>
select index [0,1,2,3,5,6,7,8]
0
player x selected 0.
x..
.o.
...
action 1 value: 0.18444748366555389
action 2 value: 0.061151779090461884
action 3 value: 0.21488396703965734
action 5 value: 0.2608381850499999
action 6 value: 0.1652323362193739
action 7 value: 0.15561956912672237
action 8 value: 0.1289638820232503
player o selected 5.
x..
.oo
...
<player: x>
select index [1,2,3,6,7,8]
3
player x selected 3.
x..
xoo
...
action 1 value: -0.20531758438192854
action 2 value: -0.42608861049213714
action 6 value: 0.18744981033893124
action 7 value: -0.18824241617146154
action 8 value: -0.3615798789520989
player o selected 6.
x..
xoo
o..
<player: x>
select index [1,2,7,8]
2
player x selected 2.
x.x
xoo
o..
action 1 value: 0.18500597484534875
action 7 value: -0.3763842415193661
action 8 value: -0.36455079310338945
player o selected 1.
xox
xoo
o..
<player: x>
select index [7,8]
7
player x selected 7.
xox
xoo
ox.
action 8 value: 0.15997908791792542
player o selected 8.
xox
xoo
oxo
draw.

ちゃんと学習できていて、基本的な動きに問題はないことが分かる。

...
...
...
action 0 value: -0.6384253423315294
action 1 value: -0.915534871351674
action 2 value: -0.9624723176936083
action 3 value: -0.6541022503118152
action 4 value: 0.25946316378451245
action 5 value: -0.7528711154068253
action 6 value: -0.6311246578644901
action 7 value: -0.6530331504039432
action 8 value: -0.5082413661354799
player o selected 4.
...
.o.
...
<player: x>
select index [0,1,2,3,5,6,7,8]
1
player x selected 1.
.x.
.o.
...
action 0 value: 0.9275310923218346
action 2 value: 0.6236182659350441
action 3 value: 0.6475581770325918
action 5 value: 0.7472332006744651
action 6 value: 0.25701034508012593
action 7 value: 0.014490745597882616
action 8 value: 0.5146151816985466
player o selected 0.
ox.
.o.
...
<player: x>
select index [2,3,5,6,7,8]
8
player x selected 8.
ox.
.o.
..x
action 2 value: 0.42225623490039943
action 3 value: 0.5645363300741779
action 5 value: 1.0001798852063186
action 6 value: 0.25894031077052
action 7 value: 0.39390212689375054
player o selected 5.
ox.
.oo
..x
<player: x>
select index [2,3,6,7]
3
player x selected 3.
ox.
xoo
..x
action 2 value: 0.12730294609325954
action 6 value: 0.47117886786805424
action 7 value: 0.4711559937367403
player o selected 6.
ox.
xoo
o.x
<player: x>
select index [2,7]
2
player x selected 2.
oxx
xoo
o.x
action 7 value: -0.36212354242755396
player o selected 7.
oxx
xoo
oox
draw.

けど、×が2手目で角でなく辺をとったとき、本当なら○は必勝なんだけど、まだ勝ちきれてない。

...
...
...
action 0 value: -0.6384253423315294
action 1 value: -0.915534871351674
action 2 value: -0.9624723176936083
action 3 value: -0.6541022503118152
action 4 value: 0.25946316378451245
action 5 value: -0.7528711154068253
action 6 value: -0.6311246578644901
action 7 value: -0.6530331504039432
action 8 value: -0.5082413661354799
player o selected 4.
...
.o.
...
<player: x>
select index [0,1,2,3,5,6,7,8]
2
player x selected 2.
..x
.o.
...
action 0 value: 0.9777936166398172
action 1 value: 0.6127621228550149
action 3 value: 0.5605556232162823
action 5 value: 0.6789277972000003
action 6 value: 0.05415146624205671
action 7 value: 0.5506402990803502
action 8 value: 0.4952653275565266
player o selected 0.
o.x
.o.
...
<player: x>
select index [1,3,5,6,7,8]
8
player x selected 8.
o.x
.o.
..x
action 1 value: 0.07337201293239404
action 3 value: 0.4678394751545193
action 5 value: 1.004183654776937
action 6 value: -0.2892653944483668
action 7 value: 0.6107326257814774
player o selected 5.
o.x
.oo
..x
<player: x>
select index [1,3,6,7]
3
player x selected 3.
o.x
xoo
..x
action 1 value: 0.2746474047341685
action 6 value: -0.12908173542987214
action 7 value: 0.6813908808988143
player o selected 7.
o.x
xoo
.ox
<player: x>
select index [1,6]
1
player x selected 1.
oxx
xoo
.ox
action 6 value: -0.36212354242755396
player o selected 6.
oxx
xoo
oox
draw.

それと、×が2手目に角をとったときも、3手目に対角の角をとるのが唯一ダブルリーチをかけうる手として有効なんだけど、それは選べていない。
(もっとも、相手が間違わなければ結局引き分けになるので、必ずしもいい手とは言えず、学習できなくても仕方ない部分はある)

今度は×側。

$ ruby human_com_game.rb nn_sarsa_batsu.1M.dat
...
...
...
<player: o>
select index [0,1,2,3,4,5,6,7,8]
4
player o selected 4.
...
.o.
...
action 0 value: -0.1900836910211487
action 1 value: -0.8236444973368885
action 2 value: -0.8486270508436535
action 3 value: -0.8873613253712567
action 5 value: -0.758398040197464
action 6 value: -0.8111736412648833
action 7 value: -0.6322622190070192
action 8 value: -0.8725318337820029
player x selected 0.
x..
.o.
...
<player: o>
select index [1,2,3,5,6,7,8]
1
player o selected 1.
xo.
.o.
...
action 2 value: -1.0078805146339507
action 3 value: -0.8830521317774014
action 5 value: -1.002836083869443
action 6 value: -0.833267367145659
action 7 value: -0.1308635352668934
action 8 value: -1.0089831490698378
player x selected 7.
xo.
.o.
.x.
<player: o>
select index [2,3,5,6,8]
2
player o selected 2.
xoo
.o.
.x.
action 3 value: 0.00648881972849324
action 5 value: -0.4155077875418149
action 6 value: 0.9437774338964601
action 8 value: -0.1511934635453121
player x selected 6.
xoo
.o.
xx.
<player: o>
select index [3,5,8]
3
player o selected 3.
xoo
oo.
xx.
action 5 value: 0.20884684703205691
action 8 value: 0.992052568338876
player x selected 8.
xoo
oo.
xxx
player x win.
...
...
...
<player: o>
select index [0,1,2,3,4,5,6,7,8]
4
player o selected 4.
...
.o.
...
action 0 value: -0.1900836910211487
action 1 value: -0.8236444973368885
action 2 value: -0.8486270508436535
action 3 value: -0.8873613253712567
action 5 value: -0.758398040197464
action 6 value: -0.8111736412648833
action 7 value: -0.6322622190070192
action 8 value: -0.8725318337820029
player x selected 0.
x..
.o.
...
<player: o>
select index [1,2,3,5,6,7,8]
3
player o selected 3.
x..
oo.
...
action 1 value: -0.7616556192475423
action 2 value: -0.7878774995933671
action 5 value: -0.1632975529245576
action 6 value: -0.996042458814271
action 7 value: -0.8290367119178917
action 8 value: -0.9895632496819957
player x selected 5.
x..
oox
...
<player: o>
select index [1,2,6,7,8]
2
player o selected 2.
x.o
oox
...
action 1 value: -0.5865577446007091
action 6 value: -0.05966381732725559
action 7 value: -0.4815546866904667
action 8 value: -0.8407652821779832
player x selected 6.
x.o
oox
x..
<player: o>
select index [1,7,8]
7
player o selected 7.
x.o
oox
xo.
action 1 value: -0.06924475636425696
action 8 value: -0.4784696283061711
player x selected 1.
xxo
oox
xo.
<player: o>
select index [8]
8
player o selected 8.
xxo
oox
xoo
draw.

こちらも、○が初手に中央を選んだ場合、うまく動いているように見える。

...
...
...
<player: o>
select index [0,1,2,3,4,5,6,7,8]
4
player o selected 4.
...
.o.
...
action 0 value: -0.1900836910211487
action 1 value: -0.8236444973368885
action 2 value: -0.8486270508436535
action 3 value: -0.8873613253712567
action 5 value: -0.758398040197464
action 6 value: -0.8111736412648833
action 7 value: -0.6322622190070192
action 8 value: -0.8725318337820029
player x selected 0.
x..
.o.
...
<player: o>
select index [1,2,3,5,6,7,8]
8
player o selected 8.
x..
.o.
..o
action 1 value: -0.08580172511858725
action 2 value: -0.1812125333609481
action 3 value: -0.10652277622639911
action 5 value: -0.35044734225024077
action 6 value: -0.1626385143626745
action 7 value: -0.42846579410247126
player x selected 1.
xx.
.o.
..o
<player: o>
select index [2,3,5,6,7]
2
player o selected 2.
xxo
.o.
..o
action 3 value: -0.41818270113448186
action 5 value: -0.6936618647126002
action 6 value: -0.10963240068301644
action 7 value: -0.8813137393413762
player x selected 6.
xxo
.o.
x.o
<player: o>
select index [3,5,7]
5
player o selected 5.
xxo
.oo
x.o
player o win.

ただ、3手目に対角の角をとる手には、正しい手を返せていない。
なので、○側がダブルリーチをかけて勝ちに。

...
...
...
<player: o>
select index [0,1,2,3,4,5,6,7,8]
0
player o selected 0.
o..
...
...
action 1 value: -0.930615564169817
action 2 value: -0.3383203438149795
action 3 value: -0.16145724154568342
action 4 value: 0.8866202777561818
action 5 value: -0.3048957449580068
action 6 value: 0.23307798372872368
action 7 value: -0.12866213326407064
action 8 value: -0.2888226579158698
player x selected 4.
o..
.x.
...
<player: o>
select index [1,2,3,5,6,7,8]
8
player o selected 8.
o..
.x.
..o
action 1 value: 0.5811231027084429
action 2 value: 0.6132616932547067
action 3 value: 0.7458619441626833
action 5 value: 0.676596669785986
action 6 value: 1.0322977006355092
action 7 value: 0.8700908300945057
player x selected 6.
o..
.x.
x.o
<player: o>
select index [1,2,3,5,7]
2
player o selected 2.
o.o
.x.
x.o
action 1 value: 0.4906398058259285
action 3 value: 0.6359338022378526
action 5 value: 0.7645183783403294
action 7 value: 0.6701144323202824
player x selected 5.
o.o
.xx
x.o
<player: o>
select index [1,3,7]
1
player o selected 1.
ooo
.xx
x.o
player o win.

また、初手角に2手目中央は正しいんだけど、3手目に対角の角をとる手に対しては、正しく対応できていない。

ちょっと注目したいのが評価値で、3手目の手に対して、×は「自分の方がどの手を選んでも悪くはない」と評価している。
本当は2と6は負けで、それ以外なら引き分けというのが正しい評価なのに。

これはおそらく、○側が初手に中央におけば負けはほぼないと学習しているので、初手に角をとる展開の学習があまり進んでいないせいだと思う。

2,000,000回学習

次に、2,000,000回学習した○。

$ ruby human_com_game.rb nn_sarsa_maru.2M.dat
...
.o.
...
<player: x>
select index [0,1,2,3,5,6,7,8]
1
player x selected 1.
.x.
.o.
...
action 0 value: 0.21399304154377707
action 2 value: 0.26680531561272836
action 3 value: 0.07091729329326303
action 5 value: 0.17022912584938454
action 6 value: -0.07516072642342853
action 7 value: -0.414071889709609
action 8 value: 0.18308793785059632
player o selected 2.
.xo
.o.
...
<player: x>
select index [0,3,5,6,7,8]
6
player x selected 6.
.xo
.o.
x..
action 0 value: -0.27954744795511255
action 3 value: 0.2241962694735558
action 5 value: -0.13418067959906277
action 7 value: -0.5741938238611988
action 8 value: 0.08886857277736813
player o selected 3.
.xo
oo.
x..
<player: x>
select index [0,5,7,8]
5
player x selected 5.
.xo
oox
x..
action 0 value: -0.512101873589874
action 7 value: -0.5184792086645776
action 8 value: 0.12791651090714398
player o selected 8.
.xo
oox
x.o
<player: x>
select index [0,7]
0
player x selected 0.
xxo
oox
x.o
action 7 value: -0.10170807822382194
player o selected 7.
xxo
oox
xoo
draw.

2手目で角でなく辺をとったときの変化を見てみたけど、先程と手は違うものの、やはりまだ勝ちきれていない。

そして、問題の×側の学習が進んでいるかどうか。

$ ruby human_com_game.rb nn_sarsa_batsu.2M.dat
...
...
...
<player: o>
select index [0,1,2,3,4,5,6,7,8]
4
player o selected 4.
...
.o.
...
action 0 value: -0.1662308645007604
action 1 value: -0.27582649924500646
action 2 value: -0.7479230758108915
action 3 value: -0.6539746305935441
action 5 value: -0.6924270205638651
action 6 value: -0.36107594291264583
action 7 value: -0.25677261799922063
action 8 value: -0.7454031770466045
player x selected 0.
x..
.o.
...
<player: o>
select index [1,2,3,5,6,7,8]
8
player o selected 8.
x..
.o.
..o
action 1 value: -0.04341244781633334
action 2 value: -0.02891715971133455
action 3 value: -0.38579302323214254
action 5 value: -0.5737850879884117
action 6 value: -0.02070418363911177
action 7 value: -0.20109510048511056
player x selected 6.
x..
.o.
x.o
<player: o>
select index [1,2,3,5,7]
3
player o selected 3.
x..
oo.
x.o
action 1 value: -0.45250989282085186
action 2 value: -0.8664153429768638
action 5 value: -0.040576358106760704
action 7 value: -0.6282043508997157
player x selected 5.
x..
oox
x.o
<player: o>
select index [1,2,7]
1
player o selected 1.
xo.
oox
x.o
action 2 value: -1.0199395126272308
action 7 value: -0.20034837099222863
player x selected 7.
xo.
oox
xxo
<player: o>
select index [2]
2
player o selected 2.
xoo
oox
xxo
draw.

まず、3手目に対角の角をとる手に対しては、引き分けに持ち込めていた。

...
...
...
<player: o>
select index [0,1,2,3,4,5,6,7,8]
0
player o selected 0.
o..
...
...
action 1 value: -0.627934009768803
action 2 value: -0.374295909848463
action 3 value: -0.29471196718617143
action 4 value: 0.8569565557066451
action 5 value: -0.28462818166512854
action 6 value: 0.15738426110486342
action 7 value: -0.017943301932075408
action 8 value: -0.18144418196005707
player x selected 4.
o..
.x.
...
<player: o>
select index [1,2,3,5,6,7,8]
8
player o selected 8.
o..
.x.
..o
action 1 value: 0.7273312287934622
action 2 value: 0.0022440262089555964
action 3 value: 0.22037801625852388
action 5 value: 0.04569318986109555
action 6 value: 0.9454284821249213
action 7 value: 0.7156208932366872
player x selected 6.
o..
.x.
x.o
<player: o>
select index [1,2,3,5,7]
2
player o selected 2.
o.o
.x.
x.o
action 1 value: 0.3927525606022284
action 3 value: 0.44136296993650503
action 5 value: 0.35557966662382917
action 7 value: 0.2847452958559024
player x selected 3.
o.o
xx.
x.o
<player: o>
select index [1,5,7]
1
player o selected 1.
ooo
xx.
x.o
player o win.

一方、初手に角をとる手に対しての3手目の対応は、まだダメ。
というか、悪化してる?(5手目でどちらも防いでない)

3,000,000回学習

最後に3,000,000回学習したとき。

まずは○から。

$ ruby human_com_game.rb nn_sarsa_maru.3M.dat
...
...
...
action 0 value: -0.9415615592924272
action 1 value: -0.9815831643858545
action 2 value: -0.8517866076452478
action 3 value: -0.892277885110353
action 4 value: 0.15092839421521506
action 5 value: -0.8937533323322292
action 6 value: -0.6534500628697018
action 7 value: -0.585164825122806
action 8 value: -0.558656529425552
player o selected 4.
...
.o.
...
<player: x>
select index [0,1,2,3,5,6,7,8]
1
player x selected 1.
.x.
.o.
...
action 0 value: 0.8031422298251727
action 2 value: 0.5826714986728899
action 3 value: 0.8224045269816568
action 5 value: 0.4445042011149764
action 6 value: 0.4795968812366707
action 7 value: -0.2896421084130507
action 8 value: 0.36150903544957064
player o selected 3.
.x.
oo.
...
<player: x>
select index [0,2,5,6,7,8]
5
player x selected 5.
.x.
oox
...
action 0 value: 0.8117220801839892
action 2 value: 0.7619087098275175
action 6 value: 0.6762723899778648
action 7 value: -0.10397670506971057
action 8 value: 0.5343045870951286
player o selected 0.
ox.
oox
...
<player: x>
select index [2,6,7,8]
8
player x selected 8.
ox.
oox
..x
action 2 value: 0.5466933746580053
action 6 value: 0.8533138535059319
action 7 value: -0.18131820367785811
player o selected 6.
ox.
oox
o.x
player o win.

これはちゃんと勝ち切るようになった。

そして、×側。

$ ruby human_com_game.rb nn_sarsa_batsu.3M.dat
...
...
...
<player: o>
select index [0,1,2,3,4,5,6,7,8]
0
player o selected 0.
o..
...
...
action 1 value: -0.8528004949472243
action 2 value: -0.07257357448318578
action 3 value: -0.08757566990841371
action 4 value: 1.0118214073090024
action 5 value: -0.0893702977427476
action 6 value: 0.26535360081180187
action 7 value: -0.040401331219792204
action 8 value: 0.17497716703464128
player x selected 4.
o..
.x.
...
<player: o>
select index [1,2,3,5,6,7,8]
8
player o selected 8.
o..
.x.
..o
action 1 value: 0.9635049032162698
action 2 value: 0.7691565704156706
action 3 value: 1.0001644753337855
action 5 value: 1.006700016376803
action 6 value: 1.0139735228068516
action 7 value: 1.0214054482106454
player x selected 7.
o..
.x.
.xo
<player: o>
select index [1,2,3,5,6]
1
player o selected 1.
oo.
.x.
.xo
action 2 value: 0.5795300922674106
action 3 value: 1.0038826689778526
action 5 value: 1.0020996989646078
action 6 value: 0.8141843688905738
player x selected 3.
oo.
xx.
.xo
<player: o>
select index [2,5,6]
2
player o selected 2.
ooo
xx.
.xo

惜しい・・・ 2手目は正しく対応できるようになってるんだけど、続く4手目がまだダメ。


ということで、まだダメな部分もあるけど、中間層のユニット数を増やすと、だいぶ良くなっている感じはある。
ただし、これはニューラルネットワークの汎化性に任せているというよりかは、単純に状態関数の近似の誤差が少なくできているからのように思う。
本当は、ゲーム自体の持つ特徴を学習してもらいたいのだけど・・・

ちなみに、また別のインスタンスで学習を1,000,000回、2,000,000回と試したけど、そちらについても大体同じような学習結果になっている。
なので、そこまでバラツキはないのだと思われる。
もちろん、ランダム性があるので、必ずしも同じ学習回数で同程度の性能が出るとは限らないのだけど。

課題

一つ、大きな課題として、○側は初手中央が有利だと分かると、基本的にそればかりやるので、それ以外の手について(×側も含めて)学習が進みにくくなる、というのがある。
なので、人が○側を持ち、初手角とかをやった場合に、×側は学習が足りなくて、途端に弱くなってしまう。

これはクローズなゲーム会とかでもよくある話で、仲間内だけでプレイしていると、プレイが局所最適に陥ってしまって、それ以外の展開に強くなれなかったりすることが起きてくる。
もちろん、そうなってしまったとしても、たまに実験的なプレイが繰り返される中で、局所最適から抜け出すこともあるのだけど、そのためには実験的な手を行った後に続く手がちゃんとしたものになってないといけない。
これはなかなか難しい。

そこで、一つ考えられる手として、複数のインスタンスを用意しておいて、いろんなインスタンスと対戦させるようにするという方法が考えられる。
そうすれば、局所最適に陥ってしまう可能性が低くなりそう。
それについてはまた明日。

今日はここまで!