Project: 「三目並べ」: neural

    AIの学習は信賞必罰が必要?

     前回記事の訂正のための記事です。前回の内容がすべて間違っているわけじゃないので、比較のために前回の記事も残してます。合わせて読んでいただければと思います(この記事にも誤りがあります。自己対戦で学習させた時に問題が顕在化します。詳しくはこの記事を参照してください)。前回の記事で「勝っても負けても勝った方の指し手を教師データとして、勝った方の手番の局面データの重みを勾配法を使って誤差修正する」というやり方を試したことを書きました。そして、それっぽいテスト結果が得られたので、現在作成中の「消える三目並べ」(3手前に打った自分の駒が消えるルールの三目並べ)に適応して遊んでみたところ、何度も同じパターンの負け方をして学習が進んでいる気配を感じられなかったのです。ゲームの特性上負けを避けられないケースもありますし、AIは最善手を選ぶ確率を上げるように学習するだけで必ず最善手を選ぶわけではないのでAI側が負けたからと言って必ずしもバグではないのですが、初手から数手で単純に負ける酷い負け方が何回も続くのです。テストのためにかなりの回数「消える三目並べ」の対戦をしてきて自分も強くなっている:sweat_smile:ので、これは「弱すぎる」「おかしい」と分かります。何が悪かったのでしょうか?
     どうやら「どちらが勝っても勝った方の指し手を教師データとした」ところが良くなかったようです。勝った方の手を教師データとしてばかりいると、相手がリーチを掛けた局面(あと一手で相手のラインが揃う局面)で相手のラインが揃うのを防がなかった場合に、それが原因でAIが負けたとしてもそれが悪いことだと学習する機会がありません(勝った側の手だけを学習するため)。相手がリーチを掛けた局面がAIの手番であればラインを揃える手(勝つ手)を逃すことはなくなりますが、自分が(AIが)負けた時に相手が揃うことを防がなかった手が悪い手だということを教える必要があります。つまり負けたら罰を与えないといけなかったようです。
     ということで、結局、勝てば+3、負ければ−1、引分は+1というマジックナンバーを復活させることにしました。

    修正内容

     興味がある方はソースを確認していただけばいいと思いますが、前回は教師データとして9要素の配列(重みデータ)に正解手だけ1.0をセットして、それを教師データとして誤差修正していました。

     教師データ
     [0, 0, 1.0, 0, 0, 0, 0, 0, 0]
    

     今回は勝てば3.0、負けは-1.0、引分は1.0をセットして誤差修正しています。

     負けの場合の教師データ
     [0, 0, -1.0, 0, 0, 0, 0, 0, 0]
    勝ちの場合の教師データ [0, 0, 3.0, 0, 0, 0, 0, 0, 0]
    引分の場合の教師データ [0, 0, 1.0, 0, 0, 0, 0, 0, 0]

     「ゼロから作るDeep Learning」に記載されているsoftmax関数が合計1.0になるように按分してくれるので修正はこれだけです。
     但し、勝ちが3で負けが−1と落差が激しいために発散するのか、良い結果が得られなかったので学習率を前回の0.1から0.03に大幅に下げました。

    – 最強プログラムとの対戦結果 –

     上の2つのグラフのうち下側のグラフは現在リリース中の「進化する三目並べ(Tic-tac-toe Evo)」と同じものです。上側のニューラルネットワークのグラフは、前回間違ったプログラムを使って「学習速度が速い」なんて書いてしまいましたが、グラフを見る限りむしろ遅くなった感じです。それより特徴的なことは、学習がピークまで達すると間違った手を指さないようになっていることです。重みデータを加減算で操作している方法(下のグラフ)だと6000回対戦後も5%ぐらいは間違った指し手を選んで負けているのに、勾配法を使ってる上のグラフだと6000回対戦後は100%引分に持ち込んでいます(間違った手を指していない)。指し手を選ぶ推論部分(重みデータの比率で指し手を選ぶ方法)は全く同じなのにこの差が出るのは、重みデータが圧倒的に偏った値になっているということです。
     例えばゲームスタート時の初期盤面の学習データ(報酬データ)は、従来の加減算によるものでは以前の記事にも書いたように、だいたい以下のような感じのデータになります。

     初期盤面の報酬データ(加減算)
     [94, 32, 137, 98, 160, 95, 98, 84, 97]
    

     次にどんな手を選ぶか(推論部分)は、単に以下の確率で決まります。

    \(\frac{(対象となる箇所の学習データの値)}{(9箇所の学習データの合計値)}\)

    だからこのデータの場合は、全配列の合計は895で盤面の真ん中のデータが160になっていますので、AIが先手の場合に160/895の確率で初手に真ん中を選ぶということを示しています。対して勾配法による学習後のデータは以下のようになりました。

     初期盤面の報酬データ(勾配法)
    [-0.7601376352001628, -0.7502935835767303, -0.7520158193991342, -0.742749527313905, 6.938823028629541, -0.7552042606689815, -0.7336853491261596, -0.7136915491061318, -0.7310453055201457] 
    

     合計は 0.9999999987181899(約 1.0 )ですので、6.9 / 1.0 の確率(約700%)、つまり初手は100%真ん中を選ぶようになったということです。
    この結果に喜んでいたのですが、機械学習プログラムとしてはこのやり方はよくなかったようです。詳しくはこの記事を参照してください。

    – 乱数プログラムとの対戦結果 –

     乱数で指し手を決める弱いプログラム相手では、多少勝率が高いようですがほぼ似たような結果になりました。

    学習速度に関しては一概に言え無さそう

     勾配法を用いたことで学習速度が若干悪くなったテスト結果(グラフ)を紹介しましたが、学習率を変えるとまた違ってくるかもしれません。とりあえず「通常の三目並べ」で試すのはここまでにして、今後作るアプリでいろいろ試して見ようと思ってます。今回修正した内容はgithubには適用していますので、興味のある方は見て下さい。