2021年11月30日火曜日

MLB主審が誤審したと思われる「ストライク」の球面SOMでの表示

【what is this】前回の記事では、MLB投球の球種分析を球面SOM(自己組織化マップ)[1]で行いました。今回は、主審が誤審して「ストライク」コールしたと思われる投球について、球面SOMで可視化して眺めてみました。(信憑性に欠けるか、誤った論述になっている部分があると思いますが、後日、自分のための何らかのヒントになり得ると考えて書き留めます。)

■ 主審が誤審したと思われる「ストライク」309球の球面SOM表示
 以前の記事で、MLBでの多数の投球判定[2]のうち、主審が誤って「ストライク」コールしたと思われる309球の分析を示しました。「誤審」と思われる根拠についても、その記事で述べました。

 今回は、その309球のデータ(5features = 5成分から成る)を球面SOMで分析しました。図1をご覧下さい。これは、各成分毎に(各成分を軸として)この309球のデータをクラス分けした(はずの)図です。簡単な場合から行きます。例えば、成分5は、左打者か右打者かの2値を取ります。誤審したストライクを、左打者、右打者毎に分離してもいびつにならない(明瞭にクラス分け可能)。すなわち、そのように分離しても、類似性の高いデータ(5次元ベクトル)同士は近くに集まって表示される。図はそれを示しているようです。右打者の方が数が多いので、奥のキノコ状の球面が大きいようです。

 また、ストライクゾーン面を通過したx座標とy座標である、成分1と成分2についても、大きなキノコ状の球面2つがほぼ明確に分かれています。これは、ストライクゾーンの左右方向(成分1)と上下方向(成分2)に関して、誤審されたストライク球のデータをほぼ明瞭にクラス分けできることを示しているようです。


 これらに反して、成分3(ストライクゾーンの上辺の高さ)と成分4(ストライクゾーンの下辺の高さ)に着目した場合は、明瞭に区分けすることが困難なようです。すなわち、分離しようとしたクラスが複雑に混じり合っている。こうなると、主審も判断に迷う場合が多いはず。これは打者(の姿勢)によって、ストライクゾーンの高さ方向が変化することに起因するはずです。

■ まとめ
 ここまでを纏めると、かなり乱暴ですが(また適切な表現かどうかも分かりませんが)、以下のように言えそうです。
  • 横方向に関する誤審の多くは、主審の確信犯的な判定によるようである。(実際、例えば、2ストライク後は、投手に有利な方向でストライク判定される傾向があるとの調査結果もあります。)
  • 一方、縦方向の誤審は、ストライクゾーン縦方向の見極め自体が難しいことで起る場合が多いのではないか。
 このことは、(以前の記事で示した)以下の図2が裏付けているようです。緑の枠線は、打者毎に変化するストライクゾーン(主に縦方向)を示しています。たくさん重なっています。赤丸は、主審がストライクコールしたのですが、それは誤審と考えられる投球を示しています。

参考資料
[1] 大北正昭、徳高平蔵、藤村喜久郎、権田英功:自己組織化マップとそのツール、シュプリンガージャパン、2008年12月

[2] Predicting balls and strikes using TensorFlow.js
https://medium.com/tensorflow/predicting-balls-and-strikes-using-tensorflow-js-2acf1d7a447c

2021年11月29日月曜日

MLBでの7,000投球の球種の機械学習と球面SOMによる可視化

【what is this】MLBでの投球に関する機械学習の続編です。今回は、投球7,000球のそれぞれに球種ラベル(2-seam, 4-seam, sinker, など7種)が付与されたデータを機械学習させ、その結果を使って、テスト用に用意された700投球で球種を予測させました。さらに、その700球の球種を、球面SOM(自己組織化マップ)で可視化してみました。

■ MLB 7,000投球のデータ
 MLBの公開データのうちに、7,000投球について、以下に示す球速等と左右投手の識別子のデータセットがあります。そして、その各投球に球種ラベルが付与されて、参考文献[2]で解説されています。
投球データのfeatures(特徴量8要素);
 ball velocity (vx0, vy0, vz0)
 ball acceleration (ax, ay, az)
 starting speed of pitch
 whether pitcher is left handed or not

球種ラベル(pitch_code 7種)
    2-seam, 4-seam, changeup, curveball, slider, cutter, sinker
(プルグラム中では、0〜6の整数で表される)

 これらのデータの具体例を示します:
(投球データの次は、左(1)/右(0)投手を表す。最後は球種ラベル。)
 vx0, vy0, vz0, ax, ay, az, start_speed, left_handed_pitcher, pitch_code
 7.699, -132.225, -6.583, -22.508, 28.311, -16.585, 91.1, 0, 0
 6.680, -134.215, -6.355, -19.660, 26.703, -14.343, 92.4, 0, 0
 2.565, -135.398, -2.916, -14.784, 27.808, -21.573, 93.1, 0, 0
  . . .

■ 投球のデータの機械学習
 これらのデータを教師付き機械学習させるプログラム概要も[2]に示されています。それは、Tensorflow JSを使って、Node.jsのサーバで動かすものです。しかし、当方の環境でNode.jsをうまく稼働させることができなかったので、自分で通常のWebサーバで稼働させるように修正して学習させました。その結果(球種毎の正解率)を以下に示します。

 2-seam   => training:0.822, validation:0.701
 4-seam   => training:0.899, validation:0.618
 changeup => training:0.841, validation:0.718
 curveball=> training:0.970, validation:0.804
 slider   => training:0.864, validation:0.607
 cutter   => training:0.900, validation:0.591
 sinker   => training:0.811, validation:0.443

 上記で、"training"は、学習に用いた7,000件についての、そして、"validation"は、これとは別に用意したテスト用のデータ700件についての球種予測(prediction)の正解率です。 "validation" では、cutterやsinker正解率は低めですが、投球の複雑な物理現象を考えれば、全般的には、かなりよく球種を予測できていると思います。

■ 球面SOM(自己組織化マップ)による球種の可視化
 このように、ある程度良い学習結果が得られましたが、ちょっと物足りません。つまり、正解率の数値だけでは、頭の中に具体像が得られません。球種を何らかの形で可視化したい。そこで思い付いたのが、自己組織化マップSOMです。SOMについては、10年以上前に、少し勉強しただけで、その後はご無沙汰していたのですが、その当時使っていた書籍[1]を思いだし、それに添付のSOMツールを使ってみました。古いツールですが、何とか稼働させることができました!

 以下に、その結果の一部を示します。細かいので、拡大してご覧下さい。SOMは、教師ラベル無しでデータを学習して、類似性の高いデータ同士を近い場所に配置してくれます。下図は、球面SOMによる結果です。ここでは、最初に述べたTensorflowJSを使った機械学習とは別の方法での学習ですが、同様に妥当な結果が得られていると思います。

 例えば、図1では、"sinker"となるべきベクトル(ここでは8次元ベクトル)が、中央付近に集まっています。また、"2-seam"同士も、その左側や上側に集まっているのが確認できます。小生は野球データに関しては全くの素人なので、間違っているかも知れませんが、sinkerと2-seamは、共に速球で沈むという共通点があり、それらの集団が隣接しているのも納得できそうです。(なお、右上ブルーで囲まれた球面は、大きい球面の裏側です。)


 図2では、"curve" が中央から右にかけて集まっています。そして、中央の斜め下方向に強い区分けの赤線があります。その赤線に隣接する左下方向には、"cutter" が集まっています。curveとcutterは共に曲がるという特性がある一方、球速が大幅に異なることから、この強い区分け線ができているようです。

 以上、2つの方法での機械学習は、いずれも、8種の特徴量(8次元ベクトル)で、球種をある程度予測できることを示しています。特に、球面SOMの結果は、頭の中に、具体的なイメージを定着させてくれると感じます。

参考資料
[1] 大北正昭、徳高平蔵、藤村喜久郎、権田英功:自己組織化マップとそのツール、シュプリンガージャパン、2008年12月
[2] Node.jsを使用して野球の投球の種類を予測する
https://www.tensorflow.org/js/tutorials/training/nodejs_training?hl=ja

2021年11月20日土曜日

「強化学習」の新刊が届きました

 書評みたいなことは書きませんが、小生がじっくり読んで学んだ「強化学習」の参考書は以下の2点でした。

[1] 中井悦司, ITエンジニアのための強化学習理論入門, 技術評論社、2020-7-30初版

[2] 伊藤 真, Pythonで強化学習を学ぶ, 日経ソフトウェア2021年7月号, pp. 24-39

 本日(2021-11-20)、Amazonに予約してあった下記の新刊が到着しました!
上記[2]の記事を書かれた伊藤真氏による新著です。また、学ぶ楽しみが増えました。

[3] 伊藤 真, 「強化学習」を学びたい人最初に読む本, 日経BP, 2021-11-22初版

 本書は、タイトルは控えめになっていますが、かなり高度な内容も含まれます。しかし、そこを一歩一歩丁寧に叙述しているのが最大の特徴のようです。
 Q-Learningにおいて、Qテーブルをダイナミックプログラミングで(厳密解を)求解するという基本線はもちろんです。それに加えて、ニューラルネットワークを利用する近似解法とその性能検討、さらにその向上策にも、じっくり取組んでいます。

 中井氏の[1]と伊藤氏の[3]は、ともに、丁寧な叙述で、かなり高度なところまで導いてくれる素晴らしい本だと思います。具体的なコードもフォローしやすく、広範な読者層に向いていると思います。

 これら以外にもいくつか強化学習の和書や訳本は出版されていますが、小生の知る限りでは、内容がやや古かったり、最新技術に言及しているがやや理論寄りだったり、難解な感じで少しとっつきにくいものが多いように思っていました。そのなかで、[1][3]が出版されたのは非常に喜ばしいところです。

補足:
 上記[1], [2]を学んで自分で確認したり、再構成したり、応用アプリにしたりの記事をこのブログに数編づつ書いて来ました。今回の新著[3]についても、学んだあと、同様に、自分なりの確認や応用について書ければよいと思っています。


2021年11月18日木曜日

MLB(メジャーリーグ)主審の判定結果を学習してストライク判定(3)

【what is this】MLB(前回記事(2)で示したアプリにより、メジャーリーグ主審のストライク/ボールの判定状況を観察しました。その結果、学習をもとに作成されたAI主審の判定を利用すれば、メジャーリーグ主審の誤審がある程度分かるのではないかと考え、検討しました。

■ MLB主審とAI主審の判定結果の相違
 MLBでの実際の5,000投球に対するMLB主審とAI主審(=学習済みニューラルネットワーク)の判定結果は以下のとおりでした:

  • 5,000球のうち4,539球(約91%)で、両者の判定結果が合致した。
  • 残りの461球(約9%)は両者の判定は逆転した。
  • 学習条件を種々変更しても、これ以上は両者は合致しない。
 このことから、この相違9%の主因は、MLB主審の誤審によるのではないか。すなわち、AI主審の方が、約5,000球の学習の結果、(確信はありませんが)人間よりも的確に判定しているのだろう。それを裏付ける例を、図1で確認します。(機械測定によるストライクゾーンとボールの直径は、ともに実物を同一縮小率で縮小して表示しています。)図1左は、MLB主審の判定は「ボール」ですが、ストライクゾーンに触れているので、AI主審のとおり「ストライク」のはずです。図1右の例も同様に、AI主審の方が正しいように見えます。

■ 両者の相違の詳細分析
 上では、相違例を2例だけ示しましたが、5,000投球の全てについての状況を、図2と図3に示します。図2は、相違のあったうちの、「MLB主審はボールで、AI主審はストライクだった152球すべてを表示しています。図2右には機械測定によるストライクゾーン(打者により変動するので重なっています)この図を見ると、MLB主審の「ボール」判定(青丸)の何割かは、ストラークゾーンに入っているか接しているので、ストライクとすべきだった誤審と思われます。

 一方、図3は、相違があったうちの、「MLB主審はストライクで、AI主審はボールだった309球すべてを表示しています。これを見ると、このうちのかなり多くは、ストライクゾーンから外れており、MLB主審による誤審であり、AI主審の判定(ball判定)の方が正しいように見えます。


 かなり乱暴な言い方ですが、以上の観察から、MLB主審とAI主審による相違461球のほぼ9割くらいは、MLB主審による誤審のように思えます。つまり、MLB主審は全判定の8%(= 0.9 x 461/5000 = 0.08)ほどを誤審した、と言えるのではないでしょうか。

■ MLB主審のストライク/ボールの誤審率は実際にどのくらいか?
 気になる、MLB主審のストライク/ボールの実際の誤審率はどんなものでしょうか。探してみたところ、参考資料[1][2]に、上記の結論を裏付けると思われる、次のような記載がありました!
  • 例えば、2018年のMLB Bad Call Ratioは、9.21%だった。⇒上で示した8%に近いので、納得できたような気がする。
  • 主審は、特定の状況下ではバッターよりもピッチャーに有利な判定をする。⇒図2と図3の結果と合致する。すなわち、ボールなのにストライクと誤審する場合の方が(その逆よりも)圧倒的に多い。
  • 2ストライクのときは、次の球がボールでもストライクと誤審することが多い。⇒今回の検討では、この点は分からない。

参考資料:
[1]
Gigazine「メジャーリーグ11シーズン・400万球分の投球データを分析して審判がどれだけ正しくジャッジできているのかを分析した結果」
https://gigazine.net/news/20190423-analysis-how-many-mistakes-umpires/

[2] A new study of MLB pitch calls makes a strong case for robotic umpires
https://techcrunch.com/2019/04/08/a-new-study-of-mlb-pitch-calls-makes-a-strong-case-for-robotic-umpires/


2021年11月15日月曜日

MLB(メジャーリーグ)主審の判定結果を学習してストライク判定(2)

【what is this】MLB(前回記事(1)では、メジャーリーグ)の主審によるストライク/ボールの判定結果(5,000投球)を学習させて、約90%以上は同じ判定をするニューラルネットワーク(AI主審)を構成できました。今回は、これをスマホで楽しみながら、判定確認するためのアプリを作りました。「ストライクゾーン」の縦方向の範囲は、打者によって変化するわけですから、その即時の見極めの難しさ(というか醍醐味)が少し分かった気がします。

MLB主審とほぼ同じ判定をするAI主審スマホアプリ
 このアプリは、Tensorflow.jsを利用して、5,000投球を学習させたモデルを、スマホに取り込んで作動させます。実行結果を以下に示します。テストに使った入力は以下のとおりです。

ランダムに100投球のデータを入力する。各データは、以下の5項目から成る。

  • ストライクゾーンの正面の平面に触れた時のx座標値、y座標値
  • 打者で決まるストライクゾーンの縦方向の範囲の上限値、下限値
  • 左打者か右打者かのフラグ

 図1に、左打者と右打者に対するストライク判定例を示します。下図左は、ストライクゾーンに僅かにボールが触れたと判定され、ストライクです。一方、下図右は、微妙ですが、触れていないとの判定でボールです。これには文句は付けられない!


 図2は、テスト用100投球に対する判定です。図中に、MLB主審とAI主審の判定結果に関する混同行列を示しています。このテストでは、両者の判定が93%合致したという結果となっています。下図の、赤丸(ストライク)、青丸(ボール)は、MLB主審による判定結果です。


 それにしても、ストライクゾーンの縦方向の範囲はずいぶん変化するものです。図1、図2の緑枠は、機械測定に基づくストライクゾーンです。上下に振れるので、枠線がたくさん重なって、分かり難いですが。

 ちょっと物足りない。
 その通りです。このデータは、左右の打者、打者の身長(ストライクゾーンの高さに関係する)などがランダムに含まれています。ですから、ストライクゾーン縦方向の範囲がほぼ同じものを集めて、そのグループ毎にストライク/ボールの判定結果を学習すればもっと明解な知見が得られるように思います。ですが、本稿はここまで。

2021年11月12日金曜日

MLB(メジャーリーグ)主審の判定結果を学習してストライク判定(1)

【what is this】MLB(メジャーリーグ)の主審(アンパイア)によるストライク/ボールの判定結果が公開されています。これを学習させれば、MLBの平均的な主審と同じ能力を持つ判定ロボットが作れそうです。実際、5,000投球のデータを学習させた結果、人間の主審と比べて、約95%は同じ判定をするニューラルネットワークができました。これは、多次元データの2値分類問題ですので、技術的には特に新鮮味は無いのですが、MLBの実際のデータを使った結果なので現実感があります。

続編(2)はこちらです。

■ ストライクゾーンと投球の判定
 ストライクゾーンについて、筆者が調べた結果は、図1右側に示したとおりです。ストライクゾーンは、ホームプレートを底面とする5角柱の立体です。ただし、高さ方向の範囲は、図のとおり、打者(とその構え)で変化します。つまり、プロット図の中の緑の長方形の枠は、平均的な打者のストライクゾーンを示したものであり、打者によって異なります。

 図1のプロット図は、主審が判定した、ストライク(オレンジ色)とボール(青色)を示しています。投球された球が僅かでもストライクゾーンに触れた場合はストライクです。球の直径はMLBの場合約7.5cmですから、ストライクゾーンの外でもストライクの場合が多いようです。中には、正面からはストライクゾーンに触れなくても、球筋が変化して、立体のゾーンの側面や底面に触れてストライクになる場合もあります。もちろん、誤審でストライクにした場合も含まれるでしょう。


 ここで確認ですが、上のプロット図の緑枠のストライクゾーンは、機械測定によるものであり、主審が判断したストライクゾーンとは必ずしも一致しません!

■ 
投球データを利用した学習
 上記で用いた投球データは、図2に示すように、5つの特性値からなっています。すなわち、球がストライクゾーンの正面の面を無限に拡大したと考えた場合に、その面に触れた時点のx座標値とy座標値、さらに、打者に依存するストライクゾーンの縦方向の上限値と下限値、そして、左打者か右打者かのフラグです。
 この5つの特性を持つ個々の投球を、主審の判定による2値ラベル(ストライクかボールか)に対応づけてあります。これを、機械学習させるわけです。


■ ニューラルネットワークの構成
 この機械学習は、参考文献[1][2]で行われていることを参考にして実行しました。具体的なニューラルネットワークの構成は以下のとおりです。2つの隠れ層をもち、それぞれの隠れ層の後にdropout層を挿入しています。
___________________________________________
 Layer (type)                 Output shape    Param #   
 ====================================================
 dense_Dense1 (Dense)         [null, 24]      144       
 ____________________________________________________
 dropout_Dropout1 (Dropout)   [null, 24]      0         
 ____________________________________________________
 dense_Dense2 (Dense)         [null, 16]      400       
 ____________________________________________________
 dropout_Dropout2 (Dropout)   [null, 16]      0         
 ____________________________________________________
 dense_Dense3 (Dense)         [null, 2]       34      
 ====================================================
 Total params: 578
 Trainable params: 578
 Non-trainable params: 0

■ 学習の結果
 学習用のデータは、上に示したように、5,000個です。評価用にも、これと同じデータセットを用いました。10エポックの学習(毎回ランダムに構成したバッチサイズ50のミニバッチを100個を学習)結果、正解率0.959が得られました。すなわち、このニューラルネットワークは、MLBの標準的な主審の判定結果と約95%合致(学習条件により若干変動)する結果を与えるものとなりました。つまり、投球のストライク/ボールの判定に関しては、理論上、MLB主審を努められそうな結果です!
epoch 1 ,  steps= 100 , loss= 0.339  ,acc= 0.879
epoch 2 ,  steps= 100 , loss= 0.281  ,acc= 0.839
epoch 3 ,  steps= 100 , loss= 0.332  ,acc= 0.859
 . . .

epoch 8 ,  steps= 100 , loss= 0.235  ,acc= 0.899
epoch 9 ,  steps= 100 , loss= 0.206  ,acc= 0.879
epoch 10 , steps= 100 , loss= 0.129  ,acc= 0.959
baseball.js:95 training done !

■ 感想
 プロ野球に限らず、他のスポーツでも、判定や採点の機械化の方向があるようです。しかし、プロ野球の場合、機械あるいはAIが主審の代役となることが望ましいのでしょうか。スポーツの人間味っぽいところが好きな人も多いはずです。投手、打者、審判とも、一瞬にして見極める技術を向上させているはず。また、観客も、「あれ、誤審じゃないの」とか、「あの主審は低めに厳しいよなあ」などと言い合いながら観戦するのも捨てがたい気もします。もちろん、判定で揉めた場合のリクエスト(現在でも実施されていますが)に対応するための、最新測定機器やAIの出番はもちろん増えるでしょうが。

参考文献
[1]
Nick Kreeger, Visualizing ML training using TensorFlow.js and Baseball data
https://observablehq.com/@nkreeger/visualizing-ml-training-using-tensorflow-js-and-baseball-d

[2] Predicting balls and strikes using TensorFlow.js
https://medium.com/tensorflow/predicting-balls-and-strikes-using-tensorflow-js-2acf1d7a447c

2021年11月5日金曜日

スマホでニューラルネットワーク(ml5JS/TensorflowJS利用)その2

【what is this】引き続き、「スマホでニューラルネットワーク」にこだわります。以前の記事 [1]では、Q-Learning(強化学習)をニューラルネットワークで行うための基本手法を検討し、実装しました。今回は、さらにそれを深めた手法である「Experience Replay + Target Networks」があることを知り、それをTensorflow.js (JavaScript環境)で構築し、妥当なQ値が得られるまでPCで学習させました。さらに、その学習済みモデルをスマホへ格納し、スマホのアプリとして、Q-Learningを用いた2Dグリッドの経路探索課題を解きました。

■ ExperienceReplay+TargetNetworksによるQ-Learningスマホアプリ
 この手法によるQ-Learningの結果を先に示します。課題は参考文献[1]で扱ってきた、2Dグリッドでの経路探索です。Fig.3にあるように、ロボットが壁や障害物を避けて宝石(緑色の球体)に最短距離で到達するように学習させます。その学習結果を、スマホアプリで示したのがFig.3です。この例では、ロボットが障害物(黒色の正方形)を避けてうまく宝石に到達するルートを学習したことが分かります。

■ ExperienceReplay+TargetNetworksの概要
 
強化学習は一般に、「教師付き学習ではない」と言われています。強化学習では、固定した正確なラベル(目標)を設定すること自体が困難です(というよりも、それ自体が求める解なのですから)。しかし、刻々変化するラベルを対象として、ニューラルネットワークで「教師付き学習」させる方式があります。それが、文献[1]で検討したものです。

 しかし、この方法には、2つほど欠点があります。一つは、「ある状態に対してある行動を取った場合」の一組づつしか学習できない。つまり、ニューラルネットワークで本来の性能を発揮するためのミニバッチ処理(多数の入力の一括処理)ができないことです。第二は、もっと本質的な問題ですが、この方法では、「状態と行動」が強く結合した学習となってしまい、多様な入力(状態)に対する学習が収束しずらいか、振動してしまう可能性が高いことです。

 これを解決すべく登場したのが、今回の「Experience Replay + Target Networks」なのです。小生の場合、参考文献[2][3]を読んでその概要を学びました。Fig.1に示すPseudo codingは文献[2]から引用したものです。これに従って、独自にJavaScript(Tensorflow.js使用)でそれを試作することができました。


 大雑把に言うならば、上図において、ExperienceReplayは、ミニバッチによる教師付き学習を可能にするために、replay memory Dに「状態と行動に関する観測結果」を蓄積していきます。そして、TargetNetworksの方は、上に述べた「状態と行動の関連性」を解消すべく、学習用のニューラルネットワークとは別に用意された、予測(prediction)用のニューラルネットワークです。すなわち、2つの分離されたネットワークを使います。TargetNetworksは、定期的に、学習用のネットワークの重みで置き換えられて、新しくなって行きます。このため、学習と予測のネットワークに時間差が生じますが、これが実は求めるべきQ値の推定を安定させることに繋がる。そのように考えられます。

■ Tensorflow(Python)よりもTensorflow.js(JavaScript)を使った理由
 この手法の実装における、「出力値とラベルの差(誤差)」の最小化を図るには、やはりTensorflowの学習関数(fit)を使うのが便利です。Pythonでやってももちろん良いのですが、今回は、JavaScript上のTensorflow.jsを使いました。その理由は、スマホアプリとの相性が良いことによります。
 ただし、学習済みモデルは、スマホ(あるいは外部の)webサーバに配置する必要があります。スマホ用のwebサーバーはいくつも公開されていますので、手軽に使えます。Fig.2はその一例です。この例では、学習済みモデルは2つのファイル(ネットワークトポロジー等の.jsonと重みの.bin)が、スマホのwebサーバに配置されています。スマホのアプリ側では、このモデルを(JavaScriptプログラムで)ロードして、予測に使うことができます。


■ 留意点:ExperienceReplay+TargetNetworksをTensorflow.jsで行う場合
(1)Tensorflow.jsは非同期関数
 Tensorflow.jsは非同期関数の仕様になっているので、その使用は、非同期関数(先頭にasync付き)の中で行う必要があります。そして、学習用のfit関数等を呼び出す場合は、awaitによって、fit関数の実行終了を待つ必要があります。そうしないと、思わぬところで別のコード部が実行されたりしますので、注意が必要です。

(2)モデルのsaveとloadの方法
 上に述べたように、学習用のネットワークtrain_netの重みを、定期的にTatget Networksへloadする必要があります。その際に、Tensorflow.jsは、非常に使いやすいsave/loadの仕組みを提供しています。つまり、以下のように、ブラウザのメモリにsaveできる、localstorageスキームを使うことができます。
    // target_netのモデルを更新するため、train_netのモデルをsave
    await train_net.save('localstorage://2dgrid_model');
    // train_netのmodelをtarget_netへロード
    target_net = await tf.loadLayersModel('localstorage://2dgrid_model');

 一方、これとは別に、train_netの学習済みモデルを外部へ取り出して利用したい場合には、downloadsスキームによって、save/loadを行うことができます。以下はその例です。
    // 学習済みモデルのダウンロード
    await train_net.save('downloads://2dgrid_model');
    // 学習済みモデルをmodelフォルダに配置してそこからload
    train_net = await tf.loadLayersModel('./model/2dgrid_model.json');

  なお、loadした学習モデルを単に予測に使うだけなら、これOKですが、さらにそれを学習させる場合は、以下のように、再度、最適化関数を同じものに設定してコンパイルする必要があります。
    train_net.compile({loss: 'meanSquaredError', optimizer: 'sgd'});

(3)学習関数fitへ与える入力データの形式
 PythonでのTensorflowでは、fit関数の引数となる訓練データとラベルデータは、pandas array形式ですが、Tensorflow.jsでは、通常配列をtensor2dで変換して与えます。

【注】上記の(1), (2)は、Tensorflow for JavaScriptの下位レベルAPIであるCore APIを使った場合です。そうではなく、上位レベルAPIであるLayers APIを使う場合には、非同期性を考慮しなくても使える関数が用意されています。

■ ニューラルネットワークの構成と学習性能
 上に述べた2つのニューラルネットワーク(学習用と予測用)の構成は以下のようにしました。試行錯誤の結果、これに落ち着きました。隠れ層は1層よりも2層の方が良い。また、隠れ層1層目のノード数はやや多い方が良い(ここでは128とした)などが分かりました。活性化関数は、隠れ層ではreluに、出力層ではlinearに設定しました。
_______________________________________________________
Layer (type)                 Output shape              Param #   
=================================================
dense_Dense1 (Dense)     relu    [null, 128]                512       
_______________________________________________________
dense_Dense2 (Dense)     relu    [null, 32]                 4128      
_______________________________________________________
dense_Dense3 (Dense)     linear  [null, 4]                  132       
================================================
Total params: 4772
Trainable params: 4772
Non-trainable params: 0
_______________________________________________________
(各隠れ層の後にdropout層を挿入した場合も試しましたが、この問題に関しては、特段の効果は確認できませんでした。)

 Fig.3に示した4x4グリッドについて、学習したモデルを次回の学習の初期モデルにしてさらに訓練を続けるというやり方で(ε-Greedyのεの値はその度に一定比率で減少させて)学習させました。その結果を使って、状態(宝石の位置、障害物の位置、ロボットの位置)をランダムに100組み生成して、ロボットが宝石に最短距離で到達できるか否かをテストしました。その成功率は以下の通りでした。
・1回目:0.97
・2回目:0.95
・3回目:0.97

■ 感想
 「不正確なラベル」を使って「近似的な解法」を実行して行くという、この(一見確信を持てないような)手法ですが、実際にやってみると段々にラベルが正確な値に近づき、正しい答えを出すようになるのは不思議な気もします..
 この手法で学習させた結果は、上記のとおり、かなり満足のできるものでした。従来のベルマン方程式に基づくQ-tableを構築しながら学習させる方法では、ほぼ確実に厳密解に到達できました。これに対して、今回のニューラルネットワークを用いた上記のExperience Replay + Target Networksでも、95%程度の正解率を得ることができました。そして、問題規模がさらに増大した場合は、Q-tableによる学習は明らかに破綻するので、今回の手法の有効性が高まるものと感じます。

 この実装は、ほとんどFig.1の情報だけから出発して(参考文献[2]や[3]に載っていたPytonコードを見ずに)、自分で考えながら具体化し、Tensorflow.jsを使って実現しました。どこか間違っているかも知れないという不安は残っていたのですが、上記のとおり95%の正解率を得ることができたので、恐らく、妥当な作りになっているだろうと思います。

参考文献
[1] Running Q-Learning on your palm (Part3)
[2] Jordi TORRES.AI, Deep Q-Network (DQN)-II
Experience Replay and Target Networks, Aug.16, 2020

[3] Ketan Doshi, Reinforcement Learning Explained Visually (Part 5): Deep Q Networks, step-by-step
A Gentle Guide to DQNs with Experience Replay, in Plain English, Dec 20, 2020