読み書きプログラミング

日常のプログラミングで気づいたことを綴っています

囲碁AI AZ.js

A9.jsとA19.jsはコードがほとんど同じだったのですが、碁盤サイズに定数を使っていたため、別のレポジトリとして作りました。
メンテの意味でもそれはあまりに残念なので、コードをマージしてリファクタリングしてドキュメンテーションを充実させたAZ.jsを公開します。

github.com

囲碁AI A19.js

以前にA9.jsというブラウザ上で遊べる9路盤囲碁AIを公開しました。
今日は、これの19路盤版、A19.jsを公開しました。
FacebookのELF OpenGoのニューラルネットワークを使っています。

iMac Late 2012(GTX-660M)でニューラルネットワークの評価は380ms前後でした。1秒間に3回評価できるかどうかですが私より強いです^^;

ELF OpenGoのウェイトをLeela Zeroで動かすと1秒間に17回弱評価できるので、もう少し性能出てほしいなぁとも思いますが、何分古いGPUなのでブラウザとしてもWebGL/WebGPUの最適化対象にはなっていない気がします。

WGoエディタ再び

2年弱前にWGoエディタというものを作りました。

nextliteracy.hatenablog.com

新興アプリSabakiが良さげだったので開発をやめました。見立てもよかったようで、今、Sabakiは結構人気のアプリになったようです。
SabakiはLeela/Leela Zeroを使った検討機能も拡張できるのですが(GitHub - SabakiHQ/LeelaSabaki: Leela (Zero) integration with Sabaki.)、自分の欲しいものと違ったので、再びWGoエディタをいじることにしました。

github.com

検討したい局面でボタンをクリックすると、Leela/Leela Zeroの読みを盤上でリアルタイムに更新します。(碁処耳赤をご存知の方は、香子さんの変化図表示そのものと思っていただいていいです)

耳赤の生中継でなくても棋譜(SGF)の興味ある局面で香子さんの解説が見られるようなもので、とても楽しく使っています。
良ければご利用ください。

[6月18日追記]
コピーショートカットキー(macOSなら⌘-C)で盤上画面をクリップボードにコピーする機能を追加しました。例えば、局面をツイートしたいときにコピーして、ツイート編集画面でペースト(⌘-V)すると貼り付けられます。自戦記ツイートにとっても便利ですよ〜

[6月19日追記]
サポートファイルをSGFだけでなく、GIB, NGF, UGF, UGIまで増やしました。
(ファイルがすぐ手に入らない都合でGIB以外動作未確認です^^;不具合があればgithub issueでお知らせください)

AlphaGoまとめ

AlphaGo Fan AlphaGo Lee AlphaGo Master AlphaGo Zero (20b) AlphaGo Zero (40b) AlphaZero (20b) ELF OpenGo PhoenixGo
評価ハードウェア 1202 CPU & 176 GPU 48 TPU v1 4 TPU v1 4 TPU v1 4 TPU v1 4 TPU v1 1 V100 Tesla P40
HW指標 1TPUで1600simに0.4秒と思われる 80k simに50秒(1600simに1秒)
レーティング 3144 3739 4858 4350? 5185 ~4800 推定4100
実績 Fan Huiに5戦5勝 李世乭に5戦4勝1敗 トッププロに60戦60勝。柯洁に3戦3勝。AlphaGo Teachデータ生成 AlphaGo Masterに100戦89勝11敗 韓国トッププロに14戦14勝 第1回貝瑞基因杯世界AI囲碁大会優勝
アルゴリズム ポリシーとバリューネットワークとロールアウトのハイブリッドMCTS 同左 ポリシーバリューデュアルネットワークとロールアウトのハイブリッドMCTS ポリシーバリューデュアルネットワークでのMCTS 同左 同左 同左
フィーチャー シチョウを含む48(49)プレーン 同左 シチョウを含むnプレーン(推定、n不明) 8手前までの盤上配置と手番(17プレーン) 同左 同左 8手前までの盤上配置と手番(18プレーン) 8手前までの盤上配置と手番(17プレーン)
ネットワーク 192フィルタ * 12 CNN * 2 256フィルタ * 12 CNN * 2 20ブロック ResNet 256フィルタ * 20ブロック ResNet 256フィルタ * 40ブロック ResNet 256フィルタ * 20ブロック ResNet(推定) 224フィルタ * 20ブロック ResNet 20ブロック(batch normとactivationの位置がモダン)
学習 教師あり学習+強化学習 同左 同左 強化学習 同左 強化学習 強化学習 強化学習
自己対戦ハードウェア 50 GPU 2000 TPU v1 2000 TPU v1 5000 TPU v1 2000 V100 WeChat CPU server
学習ハードウェア 64 GPU & 19 CPU 64 GPU & 19 CPU 64 TPU v2
自己対戦時一手sim数 1,600 1,600 800
ミニバッチサイズ 2,048 同左 4,096
ミニバッチ内容 直近500k局からランダム局面 同左 4,096
ミニバッチ処理 バッチサイズ32で64ワーカーを動かして2,048を処理 同左
1ステップ当たり自己対戦数 25 25 30
NN選択のためのステップ数 1k 1k 選択なし
総ミニバッチ 700k 3.1M 700k 639,200
総自己対戦数 (自己対戦から30M局面を抽出) 4.9M 29M 21M
自己対戦期間 1日+1週間 数ヶ月 3日 40日 34時間 2週間

AlphaGo Masterの強さは手作りのフィーチャーにあります。20ブロックのMasterを超えるためにZeroは40ブロック要しました。
一番の要因はシチョウが読めるかどうかと想像しています。20ブロックのZeroでは盤を横断するシチョウが直感できないのではないでしょうか。20ブロックのELF OpenGoがそれを実証しています。
"without Hunman Knowledge"に拘らなければ、シチョウを含めたフィーチャーで強化学習するのが一番ですね。
残る疑問は、シチョウフィーチャーあり40ブロックで強化学習した場合AlphaGo Zero(40b)を超えるのかどうか。

Meteorを使ったリアルタイムアプリのためのデータ構造

Meteor歴5年で今更感があるのですが。

Meteorといえばリアルタイムアプリがターゲットのウェブプラットフォームです。
クライアントとサーバーのDBの同期やリアクティブなレンダリングはとても重宝しています。

さて、MeteorのDBのクライアント-サーバ同期の単位は、コレクションのドキュメントのトップのフィールドです。
(これを確認するには、クライアント側でDDPの通信をモニタしてみるといいです)

例えばチャットアプリを考えてみましょう。
大きく分けて3つのデータ構造が考えられると思います。

  1. チャット1つずつをそれぞれドキュメントにする
  2. スレッドをドキュメントにして、チャットID(番号)をトップのフィールドにしてチャットをスレッドのドキュメントに追加していく
  3. ルームをドキュメントにして、chatsフィールドを配列にしてチャットを追加していく


先に説明した通り、同期の単位はコレクションのドキュメントのトップのフィールドなので、1と2はチャット毎にDDPで同期が取られますが、3はチャットが追加されるたびにchatsフィールドの内容すべてをDDPで送ることになります。

パフォーマンスに大きな影響があるかと思いますので、ご注意ください。

macOS(High Sierra)でTensorFlowをコンパイルする

古いiMac(2012)を使っているのでGPUが馬力がないので、CPUでもIntel MKLを使って高速化をすべく、macOS(High Sierra)でTensorFlowをコンパイルしようとしたら嵌りました。

TensorFlowはコンパイル時にデフォルトのApple clangを使うのですが、このclangはOpenMPをサポートしていないので、omp.hが見つからないというエラーです。

で、コンパイラを変える方針で対処し始めたら相当失敗しました。

結論ですが、HomeBrewでlibompをインストールして、これをtensorflowのthird_partyフォルダーで登録する方法でうまくいきました。以下、必要な手順です。

1. Installing TensorFlow from Sources  |  TensorFlowでbazel buildを起動する手前まで準備する。

2. HomeBrewでlibompをインストールする

brew install libomp

3. libompをtensorflow/third_partyフォルダに登録する

cd tensorflow/third_pary
mkdir libomp
cd libomp
ln -s /usr/local/opt/libomp/include .
ln -s /usr/local/opt/libomp/lib
touch BUILD

以下はBUILDの内容です。

licenses(["notice"])

cc_library(
    name = "libomp",
    hdrs = glob(["include/*.h"]),
    visibility = ["//visibility:public"],
)

4. tensorflow/tensorflow/core/BUILDを編集する
name="core_cpu_impl"のライブラリで、

copts = tf_copts(),

copts = tf_copts() + ["-Ithird_party/libomp/include"],

に変更します。

copts = tf_copts() + ["-Xpreprocessor -fopenmp", "-Ithird_party/libomp/include"],

のほうが筋がいいかもしれませんが、試してません。ヘッダーだけが必要だったようで上記変更でコンパイルはできました。
また、name="core_cpu_impl"のライブラリのdepsに

"//third_party/libomp",

を追加します。

以上で準備完了です。コンパイルしてインストールしましょう。

bazel build --config=mkl //tensorflow/tools/pip_package:build_pip_package && bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg && pip install /tmp/tensorflow_pkg/tensorflow-1.8.0-cp36-cp36m-macosx_10_13_x86_64.whl

動作確認するには、tensorflowフォルダから出てください。(出ないとimport tensorflowが失敗します)

Web WorkerのためのRemote Method Invocation(RMI)

表題のライブラリを作成しました。

github.com

山口さんが開発されたPyaq(https://github.com/ymgaq/Pyaq)という9路盤の囲碁AIをJavaScriptの移植(A9.js)したのですが、そのときに、

という問題がありました。

なので、メインスレッドとワーカースレッドの小洒落た通信の枠組みが必要になったのでこのライブラリを作りました。

特長は、メインスレッド、ワーカースレッドどちらをクライアントにもできる対称な構成です。
実際、メインスレッドからワーカーに探索をリクエストする部分とワーカーがメインスレッドにニューラルネットワークの評価をリクエストする部分を同じライブラリで実現しています。
(当たり前だと言われたらそれまでですが^^;)

シングルスレッドで設計したクラスの変更がほぼ必要ないところも特長の一つです。
(詳細になりますが、実際には、ニューラルネットワークの評価結果(Float32Array)をTransferListとして渡すとデタッチができないらしく、サーバー側(メインスレッド側)でコピーが必要でした)


必要の際にはご活用ください。

本当はメタプログラミングでクライアント側クラスを自動生成したかったのですが、JavaScriptは未実装メソッドがコールされたときにフックする方法がなさそうなので諦めました。いい方法があれば教えてください。