オッサンはDesktopが好き

自作PCや機械学習、自転車のことを脈絡無く書きます

騒音電源を交換して静穏を手に入れた話

 こんにちは.changです. 主にWindowsで使っているサブマシンの電源を交換しました. 1万円程度の出費で静穏な夜が手に入るなんて...

0. 旧電源Silver Stone 500W

 元々使っていたのが,SilverStoneのSST-SX500-LGです. Amazonのレビューに「異音がする」とあったのを気に留めず,買ってしまったんです. 直ぐに後悔しました. 常に五月蠅い訳では無いのです. パソコンを起動した直後は普通で,ランダムなタイミングでツーという音が鳴り始めます. 酔っぱらって動画を見ているぶんには気になりませんが,真面目にソースを書いているときに音鳴りし始めると激しく不快です. よくパソコンを蹴飛ばしていました(笑).

 Amazonの購入履歴を見ると2016年8月に買っています. 4年半も我慢したんですね(汗). 物持ちが良いのが自慢ではあるのですが,今回は単なる貧乏性でした.

f:id:changlikesdesktop:20210401062226p:plain:w300
旧電源のSilverStone. コンパクトで使い易いが,ミドルタワー用途ではもっと余裕のある作りで良いかも

f:id:changlikesdesktop:20210401062432p:plain:w300
旧電源のSilverStoneを外したところ

 こう見るとコンパクトですね. ミドルパワーで使う分には過剰な小さいです. 無理な設計がたたって,異音の原因になったのかも知れません. Micro ATX用と兼用構造で仇なのかも.

1. 新電源: CORSAIR 750W 

 CORSAIRのRM750-2019-750W*1を買いました. メインマシンで使っているヤツの小型版です.

 静かです! 起動時と終了時に少し音がしますが,普段はパソコンを起動しているのが判らない位に無音です.

f:id:changlikesdesktop:20210401063205p:plain:w300
新しく買ったCORSAIR

f:id:changlikesdesktop:20210401063250p:plain:w300
新しく買ったCORSAIR.プラグイン用に大量のケーブルが付いてくる

 プラグイン用についてくるケーブル類は,上位機種と同様に充実しています. 作りもしっかりしていて流石です. 購入価格も1万円強で,SilverStoneと大差ありませんでした. もっと早く買えば良かったです.

2. 電源切り替え機

 余談なんですが,組み替えている途中で電源切り替え機が壊れてしまい,これも交換しました. WindowsLinuxを選択して起動する為に使っているものです. これも学生時代から使っていた骨董品でした.

f:id:changlikesdesktop:20210401071334p:plain:w300
壊れてしまった電源切り替え機

f:id:changlikesdesktop:20210401070612p:plain:w300
背面.旧規格の電源コネクタがメチャクチャ硬くて使いにくい

 探してみると,元々使っていたものと同じメーカーで後継機種相当*2のものがありました. 元々が5インチベイだったものが,3.5インチベイに収まった形です.

f:id:changlikesdesktop:20210401070009p:plain:w300
新しい電源切り替え機.A→Windows,B→Linuxで使っています

 今のところは快適に動いています. ただ,相変わらず旧規格の電源コネクタを使っているのが残念です. ケーブルの抜き差しが辛い.

 そもそも論ですが,Linuxで計算をしている間にWindowsが使えなくなるのは不便ですね... Zwiftが出来ない(泣). GCPに計算環境を作る必要があるのかもです.

3. まとめ

 お金で買える幸せって在るのですね. パソコンの静音性って軽視しがちですが,1 Kにお住まいで,ディープラーニングとかしたい方には重要かと思います. 物を大切にするのは良いですが,自身を痛みつけてまで使い続ける必要は無いと思いました.

 パソコンが静かになったら,今度は風呂場の換気扇の音が気になるようになりました(笑). これを消すと,今度は隣人の生活音が気になります. キリがないなぁ... 昔,人形劇でマシンガン・ダンディが言っていました.

誰にも干渉されずに静かに生きる方法:
自分以外の人間をすべて消す.或いは自分を消す

AIがスプリントトレインを学習するか試してみた

 こんにちは.changです. 今回は強化学習がスプリントトレインを学習できるか試してみました.

0. トレインとは?

 自転車ロードレースにおいて,ゴール前でチームで隊列を組んでエースを最終スプリントに導く戦略です. 映像*1の様に,エースを最後尾にして一列に並び,アシストが一人一人オールアウトしながら集団の先頭をキープします. 最後から2番目に位置してエースを送り出す(=リードアウトする)選手を,発射台と呼びます.

 高度にシステマチックになった現代レースにおいて,勝利を手にする為の必須のテクニックの一つです.

補足用語説明

エース: チームの勝利を託され,最終局面での勝負を担う選手
アシスト: 自らのリザルトを犠牲にして,エースを補助する選手
オールアウト: 力を出し切ってそれ以後のレースには絡めない状態

 その他,自転車レースのポイントについては過去記事*2*3*4をご覧下さい.

1. プログラム

 前回からかなり変えたので,詳しく記録していきます.

(1) フィールド

 32×12の画像の上を上から下方向にレースさせます. 右端にあるバーは,後述するエネルギー残量を表しています.

f:id:changlikesdesktop:20210327064735p:plain:w300
Field

(2) プレーヤー

 エースとアシスト1名づつから成るチーム編成としました. エースとアシストのアクションがそれぞれ5パターンなので,5×5=25パターンのアクションを決定する強化学習を作ります.

カ○ンディッシュ(エース)

  • アクションは5パターン
  • 1ダッシュなら1,3ダッシュなら3だけエネルギーを消費する
  • エネルギー残量不足で(スーパー)スプリントした場合は,エネルギー残量分だけ進む
  • エネルギーが0になると,スプリント(0)とスーパースプリント(4)は前進(2)と同じ動きになる
  • 初期エネルギーは5

f:id:changlikesdesktop:20210324065253p:plain:w300
カ○ンディッシュ

レ○ショー(アシスト)

  • アクションは5パターン
  • 1ダッシュ毎にエネルギー1を消費する
  • エネルギーが0になると,スプリント(0)は前進(2)と同じ動きになる
  • 初期エネルギーは5

f:id:changlikesdesktop:20210326055528p:plain:w300
レ○ショー

(3) コンペティター

 名目上コンペティターという位置づけにしますが,今回はプレーヤーとの競争を目的にはしていません. 事前の調査で,コンペティターがいるとアシストを使ったスプリントを学習しにくくなることがわかりました. このため,今回はコンペティターを敢えて弱く,無個性に設定しました.

ペ○ッキ & ツァ○ル

  • アクションは5パターン
  • 1ダッシュ毎にエネルギー1を消費する
  • エネルギーが0になると,スプリント(0)は前進(2)と同じ動きになる
  • 初期エネルギーは0

f:id:changlikesdesktop:20210326055839p:plain:w300
ペ○ッキ

f:id:changlikesdesktop:20210326055905p:plain:w300
ツァ○ル

Note: 競争させないという意味ではフィールドから消しても良いのですが,次のステップで2つのトレイン同士を対戦させる為の準備として存在させています. また,今回はカ○ンディッシュを主役にしたため,前回と配色が違います. 紛らわしくてすみません.

(4) スリップストリーム

 これまでと同様に簡単なルールで模擬しました. プレーヤーとコンペティターの間にも,スリップが効くようにしています.

f:id:changlikesdesktop:20210304071517p:plain:w300
疑似スリップストリーム

(5) レース展開の表現

 方針は変えていないのですが,前回までのソースに大きな間違いがあることに気付きました(汗). ツメが甘くてすみません. 修正しました.

f:id:changlikesdesktop:20210125184548p:plain:w300
レース展開の学習

  • レース開始時に32枚(チャンネル)の画像を0初期化する
  • 1ステップ毎にチャンネルをずらしながら,画像を更新する
  • 勝敗が決まった時点で画像を確定し,ネットワークに入力する(数チャンネルは初期(0)画像のまま)
  • レース終了時点から遡り,過去のアクションに報酬を与える

train.py

# store experience
buf1.append((state_t.reshape(env.screen_n_cols*env.screen_n_rows*env.max_time), action_t_1, state_t_1.reshape(env.screen_n_cols*env.screen_n_rows*env.max_time)))

if terminal:
    reward_t_1 = reward_t[0]
    agent1.store_experience(buf1, reward_t_1)
    agent1.experience_replay()
    buf1.clear()

 致命的に間違っていたのが,過去の行動に報酬を与える処理です. 前回までのソースは,複数ゲームの経験の束に対して線形に報酬を割り当てていました. これでは,序盤のアクションに対して,レースの勝敗に応じた報酬を与えることが出来ません. このため,今回は1ゲーム毎の経験を中間バッファに貯め,勝負が決した時点で(ゲームに要したステップ数がわかる状態で)経験を積むようにしました.

dqn_agent.py

for j in range(minibatch_size): # minibatch_indexes:
    game_length = len(self.D[j][0])
    reward_j = self.D[j][1]
    for i in range(game_length):
        ...
        reward_minibatch.append(reward_j * i/(game_length - 1))

 1ゲーム毎に遡って,報酬を与えます.

(6) 報酬

 今回苦労したところです.

 前回まではAll or Nothingの思想の下で実践形式の報酬を与えていました. 色々と調査したところ,All or Nothing報酬では,チームトレインを組む動きが全く発生しませんでした. 勝利をつかむパターンが多岐に及び,トレインに成功した場合の報酬が際立たないからだと考えられます. このため,今回はゴールにより速く辿り着く為のトレーニングと位置づけ,ゴールまでに要したステップ数に応じて報酬が決まる様にしました.

 また,エースを勝たせる動き(=リードアウト)が発生することを期待して,チームの成績で報酬を決めるケースと,エースの成績で報酬を決めるケースの二通りを試しました.

  • チーム成績: エースまたはアシストで,ゴールに速くたどり着いた側のゴールタイムで報酬が決まる
  • エース成績: エースのゴールタイムで報酬が決まり,アシストの速さは報酬に反映されない

slipstream.py

self.terminal = False
for i in range(self.n_players):
    if self.player_row[i] >= self.field_n_rows - 1 and self.player_team[i] == 0:
        self.terminal = True
        self.reward[0] = ((self.field_n_rows - 7) - self.time)*0.1
        self.reward[2] = ((self.field_n_rows - 7) - self.time)*0.1
        break

 抜粋はチーム成績の場合のソースです. スプリントを全くせずに最遅でゴールした場合が30ステップで報酬-0.5,スリップ無しでスプリントを使い切った場合が25ステップで報酬0.0になります.

Note: 前回までの実験についても,ライバルの背後からスプリントする様にはなるものの,ライバルのスリップに入る為に横の動きを使っているのか疑問に思っていました. 前にライバルがいる(=結果として,スリップが作用する)かは偶然だった可能性があります.

(7) 乱数

 アクションに乱数を混ぜ込む割合にも工夫を加えました.

dqn_agent.py

def select_action(self, state, epsilon):
    if np.random.rand() <= epsilon:
        # random
        return np.random.choice(self.enable_actions)
    else:
        # max_action Q(state, action)
        return self.enable_actions[np.argmax(self.Q_values(state))]

 前回までは乱数の割合(上記epsilonの値)を0.1で固定していましたが,今回は0.1と0.3の2条件を試しました.

(8) 計算条件

 以上を総合すると,今回は下記の3条件での学習となります.

条件 乱数の割合 報酬
1 0.1 チームの成績
2 0.3 チームの成績
3 0.3 エースの成績

2. 結果

 今回も計算に時間がかかりました. 計算に要する時間もケース毎に異なった為(学習が進んでレースが速く進めば,計算も速く終わる),結果を得られた件数もバラバラになってしまいました. 本来は統一するべきですが,今回は正直ベースで,現段階で得られている全ての結果を載せます.

 学習終了後に300レースさせた際の結果をまとめました. 3列目以降の数値はそのステップ数で走りきった件数です.

条件1

条件 平均報酬 18ステップ 17ステップ 16ステップ 15ステップ 14ステップ
0 0.21 6 0 0 0 0
1 0.22 3 2 0 0 0
2 0.27 0 2 0 0 0
3 0.29 2 1 0 0 0
4 0.26 4 7 1 0 0
5 0.17 4 0 0 0 0
6 0.27 20 6 0 0 0
7 0.15 9 3 0 0 0

条件2

条件 平均報酬 18ステップ 17ステップ 16ステップ 15ステップ 14ステップ
0 0.21 5 0 0 0 0
1 0.19 21 0 0 0 0
2 0.31 9 1 1 0 0
3 0.20 0 0 0 0 0
4 0.19 7 0 0 0 0
5 0.30 12 2 4 2 0
6 0.28 14 9 0 0 0
7 0.25 7 0 0 0 0
8 0.24 12 0 0 0 0

条件3

条件 平均報酬 18ステップ 17ステップ 16ステップ 15ステップ 14ステップ
0 0.23 11 0 0 0 0
1 0.19 1 1 0 0 0
2 0.21 9 2 0 0 0
3 0.15 12 0 0 0 0
4 0.22 0 0 0 0 0
5 0.08 1 1 0 0 0
6 0.13 4 0 0 0 0

 全体的な傾向としては条件2が一番速く,ついで条件1という結果でした. 条件2で15ステップでゴールしたケースが,最速となりました.

 高速でゴールしたケースの代表例を,それぞれ3レースずづ抜粋してgifアニメにしました.

f:id:changlikesdesktop:20210323071514g:plain:w400
先頭交代トレインでチームが勝利.赤:カ○ンディッシュ,橙:レ○ショー

f:id:changlikesdesktop:20210323071432g:plain:w400
レンショーがドサクサ勝利.赤:カ○ンディッシュ,橙:レ○ショー

f:id:changlikesdesktop:20210323044611g:plain:w400
リードアウトトレインでカ○ンディッシュが圧勝.赤:カ○ンディッシュ,橙:レ○ショー

3. 考察

(1) トレイン

 条件1で頻繁に発生したのが,カ○ンディッシュとレ○ショーが交互にスプリントしてゴールに突っ込んでいく「先頭交代トレイン」でした. AIならではのトリッキーな動きで面白いです. 実はこれ,両者は交互にスプリントしているわけでは無く,単純にダッシュを繰り返しています. 普通は直ぐにエネルギー切れになりますが,スリップの恩恵でエネルギーが交互に回復して延々とスプリントできるのです. スプリントと言うより,チームタイムトライアルに近い戦術だと思います.

 先頭交代トレインよりも更に速かったのが,カベンディッシュのスーパーダッシュを利用した「リードアウトトレイン」です. これは,乱数を大きく入れ込んだ条件2のみで見られました. より多くの試行錯誤させたほうが方が,戦略の幅が広がるということでしょう. とはいえ,リードアウトが発生したのはごく僅かなケースでした. 2名のプレーヤーから成る5×5のアクションパターンを学習させているため,網羅的に戦術を試すのは難しかったようです.

 以外だったのはエースの速度に報酬を与えた条件3の成績が振るわなかったです. リードアウトトレインが発生しやすいと予想していたのですが... 推測ですが,学習開始直後で未だ戦略が整わないフェーズにおいて,レ○ショーのアクションが全て徒労に終わるからだと思います. ゴールに報酬がつけば,レ○ショーもスプリントするようになる筈です. 条件1と条件2においては,カ○ンディッシュとレ○ショーが共にゴールを目指す中で,先頭交代やリードアウトが偶発したと考えています. 対して,自らのゴールに報酬がつかない条件3のレ○ショーはスプリントすら覚えず,結果的にカ○ンディッシュを孤立させてしまうのでしょう. 実際のレース現場においても,練習時にはエース・アシストを固定せず,本番直前にオーダーを決める場合があるようです. アシストにも,個の利益に繋がる様なモチベーションを与える必要があるということかも知れません.

(2) 人工知能を用いた戦術生成

 前回までの様な実戦形式ではトレインは全く発生しませんでした. トレインが組まれる様な報酬やコンペティターとのパワーバランスを綿密にお膳立てする必要がありました. 高度な戦術は,レース当日に一堂を介した選手達からは生まれないということだと思います. 実際,シーズン前のチーム合宿等でトレインの練習をするという話を頻繁に耳にします.

 では,新たな戦略はどの様に生まれるのでしょう. 完全な無から発生することは無いはずです. 同時に,(飛躍した命題にはなりますが)人工知能を使って戦術を発想するプロセスを考えてみようと思います.

 トレインの歴史を語るの程の知識はないので,単なる思い出話になります. レースを見始めた当時,ツァベルペタッキをリードアウトするシーンをよく見ました. その少し後で頭角を現したのがチームHigh Roadのカベンディッシュでした. 綿密に組まれたトレインとリードアウトで勝利を量産する姿がとても印象的でした. チームHigh Road以前には,あそこまでシステマチックなトレインを組むチームは無かったと思います.

 想像ですが,カベンディッシュの監督はペタッキツァベルの走りから,より理想的なトレインを想起して練習方法を練り上げたではないでしょうか. 一昔前までは先輩の後について実学で身につけていた技術が,緻密な教育プログラムによってより高度に標準化されるのは良く在ることです. つまり,戦術のキッカケは実践の中で得られたとしても,それを成熟させるのは戦略家の思考と綿密なトレーニングプランなのだと思います

 強化学習は,他者が採った戦術を真似たり,破れた経験から戦術を考察することは(一般的には)出来ません. あくまで,経験ベースです. 今回の様に高度な戦術を学ばせるには,報酬やルールを細かくお膳立てしてやる必要があります. 現実の選手の育成にも通じるところがあるかも知れませんね. 同時に,人工知能の限界とも言えます. プログラマが意図した戦術しか発生しないならば,現存しない新しい戦術を強化学習から生み出すことは出来ないことになります. これに抗いたいのです.

 より綺麗なトレインを組んだり,高い頻度でトレインを発生させるためには,「同じチームの選手と近くにいたら高評価」とか「スリップをより多く使ってゴールしたら高評価」といった直接的な報酬を与える必要が在ると思います. それは面白くないと思っています. 報酬を直接的にする程,意外性が無くなってしまうからです.

 今回,先頭交代トレインという戦術を人工知能がとってきました. 落車が存在せず,かつ他の選手の真後ろでも斜め後ろでも同等のスリップが発生するという,計算機レース特有の現象と言えるでしょう. 現実の物理法則下で,例えばカベンディッシュとペタッキが交互に先頭を入れ替わりながらスプリントしたら危険だし,速くも無い気がします. 現実との差を直視すれば,バグでしょう. ただ,冗長性が意外性を生んだと言うこともできます.

 先頭交代トレインをバグと一蹴せずに「二人逃げって可能性あるのかな?」とか,「左右から交互に抜く先頭交代って可能なのかな(常に風下から抜くのが一般的)?」みたいは思考実験をやったとします. 可能性があると思ったならば,練習で試してみます. この繰り返しがイノベーションにつながるのではないでしょうか. 雲をつかむ様な話ですが,僕は,人工知能を使う意味がここに在ると思っています.

4.むすび

 試行錯誤が大変で一つ一つの記事が重くなってきました. この辺で一度気を緩めたいと思っています.

 余談なんですが,メインマシンをGPU重視で作ってしまった*5為,強化学習に時間が掛かります(泣). i9を買おうかな...

 今回書いたソースここ*6です.

AI bike racer learned observation, goal sprint, and break

This article is a translation of Japanese ver.
Original ver. is here*1.

 Hi, this is chang. Today I tried to make two agents of artificial intelligence learn bike road race through interactive competitions.

0. Bike road race

 Previously, I wrote that goal sprint using slipstream is a typical strategy for winning bike road races*2. Now I added some keywords for reading this article.

Break

 Break is a tactics that one accelerates from the begging of a race and try to keep the gap till the end of the race. It is often called as "kamikaze" because of its risk

Observe

 Riders often observe each other and wait for their goal sprint till just before a goal line. It is the strategy for letting rivals go sprint before and using slipstream. At the same time, it avoids rivals not to use slipstream by defending own back. Bike race is a mental game.

1. Program

 I mainly introduced the differences from the previous codes.

(1) Field

 Like the previous, race is competed on the image with 32 × 8. I added the remaining energy bar for competitor.

f:id:changlikesdesktop:20210218050921p:plain:w400
field

(2) Player(M*cEwan)

 I had not changed the rules. Note that today we call the player as "M*cEwan."

f:id:changlikesdesktop:20210123064303p:plain:w400
Player. We call it as M*cEwan

  • actions of 4 patterns
  • 1 sprint consumes 1 energy
  • If remaining energy is 0, sprint(0) is the same to go straight(2)
  • Initial energy is 5
  • Big motion with 2 pixels to the lateral directions are set for generating the tactics of chasing the back of rivals.
  • M*cEwan type: weak in long sprint(=small initial energy) but good at quick action(=big lateral move)

(3) Competitor(P*tacchi)

 It is almost the same to the pervious. One difference is deciding actions using Q value not random value. Now we call it as "P*tacchi."

f:id:changlikesdesktop:20210123064650p:plain:w400
Competitor. We call it as P*tacchi

  • actions of 4 patterns(randomly selected)
  • 1 sprint consumes the 1 energy
  • If remaining energy is 0, sprint(0) is the same to go straight(2)
  • Initial energy is 6
  • P*tacchi type: Large body(=small lateral move) and good at long sprint(=large initial energy)

Note: I changed the reactions to the left and right edge(=wall) because players tended to stick to the walls and neglect lateral motions. I connected the both edge like ring. If a player tries to move against the walls, he will fly to the opposite. I had tested a program to stop players if he collided to the walls but it did not work well. For players it was so hard to learn both avoiding crash and sprinting against rivals.

(4) Slipstream

 I imitated slipstream using simple rules. The slipstream previously worked only on player but today works on both player and competitor.

f:id:changlikesdesktop:20210123065708p:plain:w400
Imitated slipstream

(5) Reward

  • Win: 1.0
  • Lose: -1.0

 I used the all or nothing concept like the previous. If drawn, rewad is the same to lose(=-1.0).

2. Result

 There were great valuation in the results because the player and competitor interactively learned. So I comprehensively analyzed the 15 cases obtained with the same learning conditions.

Case M*cEwan(Player) P*tacchi Win count
0 break goal sprint 41
1 / goal sprint 2
2 break goal sprint 30
3 observe observe 63
4 goal sprint break 68
5 observe observe 30
6 observe / 89
7 observe own pace 37
8 observe own pace 29
9 / own pace 5
10 / break 3
11 observe own pace 20
12 observe observe 40
13 break goal sprint 50
14 break own pace 0

 The table above shows the results. The win count shown at the right edge are the count of M*cEwan's wins in 100 races using obtained neural network. In total, M*cEwan won 507 in 1500 races. The result shows that P*tacchi, whose initial energy is larger, was quite advantageous.

 I showed gif animations of typical patterns.

f:id:changlikesdesktop:20210217061054g:plain:w500
Case 3. M*cEwan(yellow)' signature: Using slipstream and attacking from the back of rivals

f:id:changlikesdesktop:20210217061724g:plain:w500
Case 11. P*tacchi(blue)'s signature: Keep own pace without lateral meandering.

f:id:changlikesdesktop:20210217062021g:plain:w500
Case 13. P*tacchi(blue) caught M*cEwan(yellow) in break away

f:id:changlikesdesktop:20210218053707g:plain:w500
Case 14. P*tacchi(blue) completely defeated M*cEwan(yellow) in hesitated break away

 Here, I picked good examples. If you want to whole results, please look at *3.

2. 1 Strategy

 I analyzed the strategy of M*cEwan and P*tacchi, respectively.

 M*cEwan selected goal sprint after observing in 7 cases. M*cEwan had to let rivals go and make use of slipstream, because his power(=initial energy) was smaller than the rival. Robbie MacEwan in real was also a clever rider who was good to use rivals' efforts.

 On the other side, P*tacchi often selected "own pace strategy:" go straight without meandering and sprint after the middle of races. In other words, he sprinted from his distance without observing rival's move. In today's game, if the both used their energies up and reached the goal without slipstream, P*tacchi was necessary to win. For P*tacchi, all have to do is refraining dash in the early part of races to make M*cEwan not take his wheel. I did not noticed it when I wrote the codes. To be honest, I felt like to be pointed bugs out by AI, ha ha...

2. 2 Break

 It was a little surprise that both M*cEwan and P*tacchi selected break in multiple cases. In case 1 and 10, P*tacchi won with a probability close to 100% because M*cEwan gave up. I wonder if M*cEwan could not learn the risk of rival's break...

 In case 0, 2, and 13, in which M*cEwan went break, the number of wins for both sides tended to be imminent. For M*cEwan, going break for getting 50/50 wining rate could be valuable because of his smaller power. I think it's like a puncher-type rider who isn't suitable for a bunch sprint and tends to escape.

 In total, I can say that ”break is valuable because rivals can be dull, especially if you have sustainable power.”

 Note that break with hesitation is the worst. Please look at the gif animation of case 14. In this case, M*cEwan could not get a single win. It shows that "if you attack, do not look behind."

3. Afterward

 It was interesting. Next time, I will try more complex competition with increased players.

 I updated the source code*4.

4人(?)のAIを自転車ロードレースで競わせながら学習させてみた

 こんにちは.changです. 今回はAIを4名の自転車レーサー(マ○ュアン,ペ○ッキ,カ○ンディッシュ,カ○チェラーラ)に見立てて競わせてみました.

0. 自転車ロードレース

 自転車レース戦術の必要知識については過去の記事*1*2をご覧ください.

 モデルにした選手についての知識があるとより楽しんでいただけると思うのですが,選手の特徴を比較しながら理解できるような情報元はなかなか無いです. あまり参考にならないと思いますが,彼らがフィーチャーされているYouTubeを紹介しておきます*3*4*5*6

1. プログラム

(1) フィールド

 32×8の画像の上を上から下方向にレースさせます. プレイヤーが増えて窮屈になったので,横幅を8から12に増やしました.

f:id:changlikesdesktop:20210304064842p:plain:w400
Field

(2) プレイヤー

 今回はプレイヤー vs コンペティターという図式ではなく,4名のプレイヤーを対等に争わせます.

マ○ュアン

f:id:changlikesdesktop:20210304065929p:plain:w300
マ○ュアン

  • アクションは5パターン
  • 1ダッシュなら1,2ダッシュなら2だけエネルギーを消費する
  • エネルギー残量不足でスーパースプリントした場合は,エネルギー残量分だけ進む
  • エネルギーが0になると,スプリント(0)とスーパースプリント(4)は前進(2)と同じ動きになる
  • 初期エネルギーは5
  • ライバルの背後に入る動きを発生させる為,横方向の動きを大きくしている
  • ロングスプリントは苦手(=初期エネルギーが小さい)だが小回りが効く(横方向の動きが大きい),マ○ュアン型の特性

ペ○ッキ

f:id:changlikesdesktop:20210304100359p:plain:w300
ペ○ッキ

  • アクションは5パターン
  • 1ダッシュなら1,2ダッシュなら2だけエネルギーを消費する
  • エネルギー残量不足でスーパースプリントした場合は,エネルギー残量分だけ進む
  • エネルギーが0になると,スプリント(0)とスーパースプリント(4)は前進(2)と同じ動きになる
  • 初期エネルギーは6
  • 重量級(=横方向の動きが小さい)でロングスプリントを得意とする(=初期エネルギーが大きい),ペ○ッキ型の特性

カ○ンディッシュ

f:id:changlikesdesktop:20210304100723p:plain:w300
カ○ンディッシュ

  • アクションは5パターン
  • 1ダッシュなら1,3ダッシュなら3だけエネルギーを消費する
  • エネルギー残量不足でスーパースプリントした場合は,エネルギー残量分だけ進む
  • エネルギーが0になると,スプリント(0)とスーパースプリント(4)は前進(2)と同じ動きになる
  • 初期エネルギーは5
  • 瞬発力に優れた(=スーパースプリントが長い)カ○ンディッシュ型の特性

カ○チェラーラ

f:id:changlikesdesktop:20210304100749p:plain:w300
カ○チェラーラ

  • アクションは5パターン
  • 1ダッシュで,エネルギーを1消費する
  • エネルギーが0になると,スプリント(0 or 4)とは前進(2)と同じ動きになる
  • 初期エネルギーは10
  • スプリントを捨て(=スーパースプリントが無い),持続力に特化した(=初期エネルギーが大きい)カ○チェラーラ型の特性

(3) スリップストリーム

 簡単なルールで擬似しました. ライバルの後ろを走ることで,エネルギーが回復します.

f:id:changlikesdesktop:20210304071517p:plain:w300
疑似スリップストリーム

  • ライバルの1ピクセル背後に入ると,エネルギーが2回復する
  • ライバルの2ピクセル背後に入ると,エネルギーが1回復する

(4) 報酬

  • 勝ち: 1.0
  • 負け: -1.0

 All or nothongの思想は前回までと同じです. 引き分けの場合も負けと同じになります.

Note: レース展開の学習方法を含めたプログラムの基本コンセプトは,過去の記事をご覧下さい.

2. 結果

 プレイヤーが増えたことで,(15ケースの学習結果をみた)前回よりも更に結果にバリエーションが生じました. より多くのケースで学習結果をみたいところですが,計算時間の関係で今回は10ケースに留めました.

(1) 勝率

Case マ○ュアン ペ○ッキ カ○ンディッシュ カ○チェラーラ 引き分け
0 0.073 0.157 0.503 0.000 0.267
1 0.497 0.107 0.177 0.000 0.219
2 0.037 0.010 0.057 0.010 0.886
3 0.390 0.013 0.123 0.003 0.597
4 0.133 0.320 0.130 0.000 0.417
5 0.033 0.017 0.087 0.003 0.860
6 0.043 0.817 0.063 0.000 0.077
7 0.650 0.120 0.040 0.020 0.170
8 0.023 0.330 0.247 0.200 0.200
9 0.220 0.250 0.247 0.033 0.250
Sum 0.210 0.210 0.170 0.030 0.390

 学習終了後に300回対戦させた場合の,各プレイヤーの勝率をまとめました. 10ケース全体で見ると:

  • マ○ュアンとペ○ッキが拮抗
  • カ○ンディッシュがそれに次ぐ
  • カ○チェラーラは一人負け

Note: 念の為申し上げておきますが,カ○チェラーラが嫌いなわけではありません(汗)

(2) レース展開

 各プレイターの勝ちパターンの抜粋したGIFアニメを作りました. 全ケースのGIFアニメはここ*7です.

f:id:changlikesdesktop:20210303175056g:plain:w500
ライバルの後方からゴールスプリントするマ○ュアン(赤).Case 1から抜粋
f:id:changlikesdesktop:20210303175206g:plain:w500
ライバルの後方からゴールスプリントするペ○ッキ(橙).Case 6から抜粋.
f:id:changlikesdesktop:20210303175237g:plain:w500
ライバルの後方から弾丸ゴールスプリントするカ○ンディッシュ(黃).Case 9から抜粋
f:id:changlikesdesktop:20210303175310g:plain:w500
パワーで圧倒するカ○チェラーラ(空).無学習時.ランダムに動く学習初期には一番強い

Case マ○ュアン ペ○ッキ カ○ンディッシュ カ○チェラーラ
0 / / 逃げ /
1 スプリント / / /
2 逃げ / 逃げ /
3 逃げ / 逃げ /
4 逃げ スプリント 逃げ /
5 逃げ / 逃げ /
6 / スプリント / /
7 逃げ / / /
8 逃げ スプリント スプリント /
9 逃げ スプリント スプリント /

 各ケースでの動きをまとめるとこんな感じです. プレイヤーが2名だった前回と比べると,戦略が洗練されていない印象を受けました. ゴールスプリントを仕掛けるようにはなりますが,早がけで息切れするようなケースが目立ちます. また,4名が全員で勝負を争そう様な動きにはなりませんでした. スプリントするのは精々2名で,残りのプレーヤーは後ろから傍観と言うか... その辺の考察をしてみようと思います.

3. 考察

(1) 学習エポック数

 戦略が洗練されなかった原因の一つとして,単純に学習回数が不足していたことが考えれます.

f:id:changlikesdesktop:20210304090532p:plain:w400
Win rate during learning of Case 0

 これは,ケース0の学習中の勝率の変化です. 25000エポック付近からペ○ッキとカ○ンディッシュの勝率が上がり初め,学習終了時(50000エポック)まで上がり続けました. この後更に学習を積めば,より高度なスプリントをするようになったかも知れません. 学習回数を(プレーヤー2名の)前回の20000エポックから50000に増やしましたが,それでも足りなかった様です. フィールド内のシチュエーションのバリエーションが飛躍的に増えたためでしょう.

(2) ネガティブ脳

 4名全員で勝負を争わないのは,4名しかプレイヤーがいない閉じた世界で戦わせたからだと考えられます. 「誰かが勝てば誰かが負ける」「勝者は一人」という事です.

 上に示したCase 0の学習中の勝率変化を再度見てみましょう. 25000エポックからマ○ュアンとカ○ンディッシュが勝利を増やしていくのに相反する様に,カ○チェラーラの勝率は下がっていきます. 勝負をしながら互いに高め合う事が出来なかったのです.

 大半のケースに共通して,学習開始から25000エポック程度まではカ○チェラーラの圧勝になります. パワーで圧倒するカ○チェラーラは,全てのプレイヤーがランダムな動きをする学習初期に有利だからです.

 Gifアニメを見ると,ライバルに敗れるケースでカ○チェラーラは全くエネルギーを使っていません. 力を出し切ればある程度の確率で勝てる筈なのに,そうしないのです. これは,学習初期にランダム(=無戦略)な動きでプラス報酬を得て構築したニューラル・ネットワークが,他の選手に負けるようになってマイナス報酬で上塗りされるだと思います. 過去の勝ちパターンを否定され,自身を失ったネガティブ脳に陥るのです. 現実の有力選手も負けがかさんで調子を崩すことがあります. 似たような現状なのかも知れません.

train.py

if terminal:
    # experience replay
    if reward_t[0] > 0:
        agent1.experience_replay()
    elif reward_t[1] > 0:
        agent2.experience_replay()
    elif reward_t[2] > 0:
        agent3.experience_replay()
    elif reward_t[3] > 0:
        agent4.experience_replay()

 ちなみに今回,負けパターンばかりを学習してネガティブ脳になるのを避けるために,上記の様に勝ったときにだけその経験を学習するようにしました. プレーヤーを4名にしたことで,単純に勝ち1回に対して負け3回を学習することになり,負け癖をつけやすい為です.

dqn_agent.py

class DQNAgent:
    ...
    def __init__(self, enable_actions, input_shape, environment_name, model_name):
        ...
        self.replay_memory_size = 10

 また,(負けを含む)過去の記憶を引き釣りすぎない様に,記憶容量(replay_memory_size)を10戦ぶんにとどめています. それでも,10戦中10戦勝てていたものが,8戦になり,1戦しか勝てなくなり,,,という流れで,ネガティブ脳に陥ってしまったようです.

 現実世界では,選手たちは常に同じレースを戦うわけではありません. 個々に自国のレースを走って調子をあげてきた選手達が,本命レースで顔を合わせる場合も多いです. この様に異なるレースを走って調整することには,「むやみに負けを経験させない」という効果もあると考えられます. 2010年のファビアン・カンチェラーラや2011年のフィリップ・ジルベールのように,一人の選手が主要レースを独占することがあります. ああした年においては,勝った選手がノッていたことは勿論ですが,他の選手がネガティブ脳に陥っていた側面も在ったかも知れません.

 試しに,各プレイターがノッているネットワーク(マ○ュアン:Case 1,ペタッキ: Case 6,カベンディッシュ: Case 0,カンチェラーラ: ランダム)を集めて対戦させると,途端にレース感が出ました. 見ていて飽きない程に,生き生きとした接戦を繰り広げます.

f:id:changlikesdesktop:20210303184209g:plain:w200
ポジティブ脳同士の対戦例1(6対戦ぶん).赤=マ○ュアン,橙=ペ○ッキ,黃=カ○ンディッシュ,空=カ○チェラーラ
f:id:changlikesdesktop:20210303184308g:plain:w200
ポジティブ脳同士の対戦例2(6対戦ぶん).赤=マ○ュアン,橙=ペ○ッキ,黃=カ○ンディッシュ,空=カ○チェラーラ

(3) 当て馬カ○チェラーラ

 ファンの方に怒られそうですが...繰り返して申します. カ○チェラーラが嫌いな訳ではありません. プログラムが起こした現象について,客観的な考察をしております.

 一人負けになったカ○チェラーラですが,AI対戦での学習において重要な役割を果たしました. ケース0の学習経過を再々度見てみると,ペ○ッキとカ○ンディッシュはカ○チェラーラに対抗するように強くなった様に見えます. それを証明するのが,ケース2です. ケース2は,引き分けが多くて退屈なレース展開でした.

f:id:changlikesdesktop:20210304093025p:plain:w400
Win rate during learning of Case 2

 ケース2の学習中の勝率変化を見てみると,学習開始時からのカ○チェラーラの圧勝が起こりません. 勝率を上げていくプレーヤーも発生せず,引き分けを増やしながら全プレイヤーが縮退する傾向になります. つまり,リスクを追ってアタックを仕掛ける当て馬がいないと,全プレーヤーがダルい戦略を取るようになるのです. 体格差で有利な選手(=初期エネルギー値が高い)に対抗しようとすることで,優れた戦略は生まれると言えます.

4. むすび

 戦術面だけでなく,レースに向けた調整方法についても脳科学的な側面から言及することが出来るかも知れません.

 ただ,学習に時間がかかって結果をまとめるのが大変になってきました(汗). 夜中にきらびやかに輝きながら回るマイマシンが,地味に睡眠を妨げやがります...

 次はチーム戦をやってみるつもりですが,健康を崩さない範囲にしようと思います(笑). 今回の実験でカ○ンディッシュが思ったよりも勝てなかったことが気になっています. 彼の様なタイプは最終局面まで勝負がもつれた状態で脚を残せてこそ,その力を発揮する筈です. 逃げを自分で潰さなければならない個人戦ではスーパースプリントを生かせなかったのでは無いかと考えています. チームトレインからカ○ンディッシュが弾丸スプリントを決めたら最高ですね!!!

 ソースを更新しました*8

AIに自転車レースで対戦させたら,お見合いとかしてきた

 こんにちは.changです. 今回は自転車レースでAI同士を対戦させながら学習させてみました.

0. 自転車レース

 前回*1スリップストリームを利用したゴールスプリントが,自転車レースで勝利をつかむ為の代表的な戦略であることを説明しました. 今回の記事を書くにあたり,いくつかキーワードを追加します.

逃げ

 レースの序盤から加速をしてライバルに差をつけ,そのまま追いつかれること無くゴールする戦略です. 博打色が強い戦略であることから,kamikazeと呼ばれたりします.

お見合い

 相手の出方を見ながら,スプリントを仕掛けるタイミングをゴール直前まで伺う戦略です. スプリントを先に仕掛けてしまうと,ライバルにスリップストリームに入られるリスクが高くなります. このリスクを相手に追わせようと駆け引きする頭脳戦になります.

1. プログラム

 主に,前回から変えたところを紹介します.

(1) フィールド

 基本的には前回と同じで,32×8の画像の上を上から下方向にレースさせます. 今回,コンペティターのエネルギー残量バーを追加しました.

f:id:changlikesdesktop:20210218050921p:plain:w400
field

(2) プレイヤー(マ○ュアン)

 前回と同じですが,ポイントになるところなので載せておきます.

f:id:changlikesdesktop:20210123064303p:plain:w400
プレイヤー.今回,こいつを(勝手に)マ○ュアンと呼びます.

  • アクションは4パターン
  • 1回のスプリントで,エネルギー1を消費する
  • エネルギーが0になると,スプリント(0)は前進(2)と同じ動きになる
  • 初期エネルギーは5
  • コンペティターの背後に入る動きを発生させる為,横方向の動きを大きくしている
  • ロングスプリントは苦手(=初期エネルギーが小さい)だが小回りが効く(横方向の動きが大きい),マ○ュアン型の特性

(3) コンペティター(ペ○ッキ)

 これも前回とほぼ同じです.違うのは,アクションを乱数ではなくQ値から決める点です.

f:id:changlikesdesktop:20210123064650p:plain:w400
コンペティター.ペ○ッキと呼びます.

  • アクションは4パターン
  • 1回のスプリントで,エネルギー1を消費する
  • エネルギーが0になると,スプリント(0)は前進(2)と同じ動きになる
  • 初期エネルギーは6
  • 重量級(=横方向の動きが小さい)でロングスプリントを得意とする(=初期エネルギーが大きい),ペ○ッキ型の特性

Note: 今回,プレイヤー,コンペティター共に,左右の壁に対する動きを変えました. 壁に張り付いて横に動かないことが多かった為です. 左右の壁をリング上につなげています. 壁に衝突するアクションを採った場合,反対側の壁にワープします. 壁に衝突した場合はストップするやり方も試したのですが,ゴールにたどり着くのに精一杯でレースをしなくなってしまったのでやめました.

(4) スリップストリーム

 前回と同じく簡単なルールで擬似しました. 前回はプレイヤーのみにスリップが効く様に作りましたが,今回は両者にスリップが作用します.

f:id:changlikesdesktop:20210123065708p:plain:w400
疑似スリップストリーム

(5) 報酬

  • 勝ち: 1.0
  • 負け: -1.0

 All or nothongの思想は前回と同じです. 引き分けの場合も負けと同じになります.

2. 結果

 プレイヤーとコンペティターが相互作用しながら学習するので,前回以上に結果にバリエーションが出ました. このため,同じ条件で15回学習を行った結果を総合的に見ることにしました.

ケース マ○ュアン(プレイヤー) ペ○ッキ 勝利数
0 逃げ ゴールスプリント 41
1 見送り 逃げ 2
2 逃げ ゴールスプリント 30
3 お見合い お見合い 63
4 ゴールスプリント 半端に逃げ 68
5 お見合い お見合い 30
6 お見合い 見送り 89
7 お見合い マイペース 37
8 お見合い マイペース 29
9 見送り マイペース 5
10 見送り 逃げ 3
11 お見合い マイペース 20
12 お見合い お見合い 40
13 逃げ ゴールスプリント 50
14 半端に逃げ マイペース 0

 こんな結果になりました.表右端の勝利数は学習終了後に100回対戦させた場合のマ○ュアンの勝利数です. 15ケース全体で見るとマ○ュアンの成績は507/1500でした. パワー(初期エネルギー)に勝るペ○ッキが圧倒的に有利な条件にあったと言えるでしょう.

 代表的なパターンのアニメーションを見てみます.

f:id:changlikesdesktop:20210217061054g:plain:w500
ケース3.お見合いからのスプリント勝負.スリップを利用して後ろから仕掛ける,マ○ュアン(黄)の勝ちパターン.

f:id:changlikesdesktop:20210217061724g:plain:w500
ケース11.蛇行せずにマイペースを貫くペタッキ(青).スリップに入られずに力を出し切れば勝てる,ペタッキの勝ちパターン.

f:id:changlikesdesktop:20210217062021g:plain:w500
ケース13.逃げを打つマ○ュアン(黄)を,ペタッキ(青)が後ろからさす.

f:id:changlikesdesktop:20210218053707g:plain:w500
ケース14.半端に逃げたマ○ュアン(黄)が,ペ○ッキ(青)にスリップに入られてなすすべ無く負ける.

 ここでは割と綺麗に動いたケースを抜粋してします. 結果を全体的にご覧になりたい方は,こちら*2でお願いします.

2. 1 戦略分析

 マ○ュアンとペ○ッキの戦略を分析してみます.

 マ○ュアンはお見合い(からのスプリント勝負)という戦略を,15ケース中7件で採って来ました. パワーで劣るマ○ュアンが勝利をつかむには,相手を先行させてスリップストリームを利用する必要があるためでしょう. 現実のマ○ュアンも,ライバルの力を利用するのが巧い選手でした.

 一方,ペ○ッキは進路を変えずにまっすぐに進み,中盤から後半にかけてスプリントをするという”マイペース”戦略を多く採ってきました. 相手の動きに関係なく自分の距離でスプリントをした,ということですね. 実は今回のルールでは,両者がスリップストリームを使うこと無くエネルギーを使い切れば,必ずペ○ッキが勝利します. ペ○ッキからすれば,マ○ュアンに後ろを取られないように前半はダッシュを控え,ゴール直前でエネルギーを使い切ることだけに集中すれば良いのです. 正直,プログラムを書いたときには気づいていませんでした. AIにバグを指摘された気分です(笑).

2. 2 逃げ

 マ○ュアン,ペ○ッキが共に,複数のケースで逃げを打ってきたことは意外でした. ペ○ッキはケース1,10と逃げを選択し,100%に近い確率で勝利を掴みました. マ○ュアンが見送った為です. 逃げに対する脅威を学習しなかったのでしょうか? ”なすすべが無いから今日はグルペット”という感じが,現実のマ○ュアン隊長を彷彿とさせて面白いです(笑).

 マ○ュアンが逃げたケース0,2,13では両者の勝利数が切迫する傾向にありました. パワーに劣るマ○ュアンからすれば,博打を打って五分五分の勝利をするのは価値があると言えます. 現実のレースにおいて,集団スプリントには向かないパンチャー型の選手が逃げを選択するのに似ていると思います.

 総合的にみると,”相手が間抜けな(見送る)場合があるならば,逃げは美味しい.特にパワー(持続力)がある場合は”と言えそうです.

 ただし,半端に逃げて相手にスリップに入られるのは最悪です. ケース14ではマ○ュアンは1勝も出来ませんでした. ”勝負を仕掛けるならば躊躇してはいけない”ということですね.

3. むすび

 面白くなって来ました. 次回はプレイヤーを増やして,より複雑な対戦をさせてみようと思います.

 前回のソースを更新しています*3