Leela Zero を GCE で動かす 〜 その 3 Lizzie 導入編 (Windows 10 対応版)

Google Compute Engine の高速な GPU を利用して、Leela Zero/Lizzie を快適に使う方法の手順書です。

* [その１ Google Compute Engine 設定編](https://qiita.com/miuraken/items/e151785bc1e7f1404035)
* [その 2 Leela Zero インストール編](https://qiita.com/miuraken/items/0f9003e0161f8921ef61)
* [その３ Lizzie 導入編（Mac/Linux 対応版）](https://qiita.com/miuraken/items/118636854a96577cdbc4) / **Lizzie 導入編 (Windows 10 対応版）**


※ 執筆時点（2018年11月）の Leela Zero のバージョンは 0.16、 Lizzie のバージョンは 0.6 で、記事はこれらのバージョンをもとにしています。（2019年10月末時点での、Lizzieのバージョンは 0.7.2 ですが、この記事とほぼ同様の設定で動くようになるはずです。）

#はじめに
GCE 上で Leela Zero が動くようになったあとに、それを利用するやり方を Windows 10 環境向けに書きます。
筆者は普段 Windows に慣れていないので、間違いや改善点があれば、ぜひご指摘ください。

その２では、仮想マシンに SSH でログインしての作業でしたが、このエントリは主に手元のマシンのコマンドプロンプト上での作業となります。（コマンドプロンプトは、画面左下のWindowsボタンを押して、cmd リターンと押すと立ち上がります。）

ここでは、Lizzie, gcloud SDK, OpenSSH, Java の4つのソフトをインストールします。


# Lizzie とは
Java VM で動く "Leela Zero Interface" 、 Leela Zero を視覚的に操作するためのソフトウェアです。
Leela Zero と対局できるのはもちろん、盤面に応じた Leela Zero の推論結果（候補手や勝率など）をリアルタイムに可視化できるため、SGF ファイルを読んで検討を行うといったことにも活用できます。

<img width="1028" alt="Screen Shot 2018-11-26 at 22.12.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/64605/f111b569-147f-99a6-fe75-b80ae2f04a73.png">

## Lizzie をどこで動かすか問題

少し細かい話になりますが、Lizzie をどこで動かすかについては、おおまかに

**A) Leela Zero と Lizzie を同じコンピュータ上で動かす**
**B) 手元の PC 上で動く Lizzie を、別のコンピュータ上で動く Leela Zero に接続する**


の 2 通りが考えられます。
今回は、Leela Zero を GCE の仮想マシンで動かすことを前提としているので、具体例として、次のような手段が考えられます。

**A-1) 仮想マシンの「画面」に描画された Lizzie を VNC という画面共有ソフトで接続して操作する**
**A-2) 仮想マシン上の Lizzie を、手元の X Server で表示して操作する**
**B-1) 手元の PC の Lizzie を、仮想マシンで動く Leela Zero に SSH 経由で接続して操作する**

現状、 Lizzie 自身は、A の方法しか考慮していないようです。
そうなると、A-1 にせよ、A-2 にせよ、仮想マシン上で動く Lizzie を遠隔操作することになるので、操作性が悪くなりがちです。

そこで実用性を重視し、 **B-1) 手元の PC の Lizzie を、仮想マシンで動く Leela Zero に SSH 経由で接続して操作する** 方法を選択することにします。


#gcloud のインストールと初期設定

まずはコマンドプロンプトから GCP に対していろいろな操作を可能にするツールである **[gcloud](https://cloud.google.com/sdk/gcloud/?hl=ja)** をインストールすることにします。

https://cloud.google.com/sdk/docs/quickstart-windows の「始める前に」にしたがって、インストーラをダウンロード＆起動してください。

インストールの途中でいろいろ聞かれますが、基本的に Next を押しっぱなしで構いません。
ここでは Single User Mode を選んだので、 C:\Users\miura\AppData\Local\Google\Cloud SDK にインストールされました。

インストールが終わると、`gcloud init` コマンドが実行されます。
Cloud SDK のコマンドプロンプト（黒い画面）が立ち上がり、Login を求められるので Y を押します。

![2018-12-02.png](https://qiita-image-store.s3.amazonaws.com/0/64605/85a53d38-922e-664a-3f70-4c0645d7ff8c.png)

ブラウザが立ち上がるので、GCP のプロジェクトを作った Google アカウントでログインします。
Google Cloud SDK がアクセスを求める件のダイアログが出るので、「許可」します。

無事認証が済むと、コマンドプロンプトに戻ります。
まずプロジェクトを聞かれるので、[その１](https://qiita.com/miuraken/items/e151785bc1e7f1404035)で作ったプロジェクトを数字で選択。
次に、デフォルトのゾーンを設定するかを聞かれるので、*y* と答えます。
すると、ゾーンの一覧が出てくるので、VM インスタンスを作ったゾーンを数字で選択します。

するとゴニョゴニョと表示されて、`gcloud init` による初期設定は一旦終了。

つづいて、SSHキーを生成します。
まずは、念のため VM インスタンスを起動させておきます。コマンドは `gcloud compute instances start <VM インスタンス名>` です。

```
> gcloud compute instances start leela-zero-1
Starting instance(s) leela-zero-1...done.
Updated [https://www.googleapis.com/compute...]
```

起動まではすこし時間がかかりますが、無事立ち上がりました。
ここで ```gcloud compute instances list ```　と打つと、VM インスタンスの一覧が出るので、STATUS が RUNNING であることを確認します。

```
> gcloud compute instances list
NAME          ZONE          MACHINE_TYPE   PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP    STATUS
leela-zero-1  asia-east1-a  n1-standard-1  true         10.140.0.2   35.221.236.35  RUNNING
```

VM が起動していることを確認したら、 `gcloud compute ssh <インスタンス名>` を打ち込みます。
SSH キーがないので作るよ？とか、キーをキャッシュしとくよ？とか聞いてくるので、それぞれ y と答えます。

<img width="921" alt="2018-12-02__13__png_-_OneDrive.png" src="https://qiita-image-store.s3.amazonaws.com/0/64605/f0b2ee31-3a9e-4bf2-2074-6066ee3ef123.png">

無事インスタンスにログインできれば、念のために VM 上で `ls` を叩いて、その２でインストールした leela-zero ディレクトリが見えていることを確認しましょう。

 ![2018-12-04__14_.png](https://qiita-image-store.s3.amazonaws.com/0/64605/c69c23db-f3b3-ddb0-2310-8897fd325fe6.png)


<details><summary> leela-zero が見えないとき</summary><div>

[その 2](https://qiita.com/miuraken/items/0f9003e0161f8921ef61) でウェブ上の SSH でログインしたときのユーザ名と、gcloud で SSH ログインしたときのユーザー名が一致しないと、実行ファイルやネットワークファイルをうまく見つけられません。
そのようなときは、次のどちらかを試すとよいです。

###ユーザー名を指定してログイン
`gcloud compute ssh <インスタンス名> ` の代わりに `gcloud compute ssh <ユーザー名>@＜インスタンス名>` でログインします。ここでの<ユーザー名>@＜インスタンス名>は、その2の方法でログインしたときに見えていたものです。
<img width="500" alt="Cursor.png" src="https://qiita-image-store.s3.amazonaws.com/0/64605/b3d7694e-cea4-277d-deb6-dc63f843cdf7.png">

この方法をとる場合は、下の leelaz スクリプトの中でも、インスタンス名の前にユーザー名@を加えるようにしてください。

###再度 Leela Zero をインストールしてしまう

（手元のマシンから `gcloud compute ssh <インスタンス名>` でログインした VM 上で、）

```shell
# まずは ~/leela-zero が見えているかテスト
ls ~/leela-zero

# ここで leela-zero が見えていれば以下の作業は不要です。

# Leela Zero のソースコード github から Clone。依存するライブラリを配備。
git clone https://github.com/gcp/leela-zero
cd leela-zero
git submodule update --init --recursive

# build ディレクトリをつくって、その中でビルド
mkdir build && cd build
cmake ..
cmake --build .
./tests

# ネットワークファイルを ~/.local/share/leela-zero/best-network にダウンロード
curl https://zero.sjeng.org/best-network > ~/.local/share/leela-zero/best-network
```

</div></details>

うまくいったら、 `exit` コマンドで VM から一旦ログアウトして構いません。


##OpenSSH のインストール

[SSH](https://ja.wikipedia.org/wiki/Secure_Shell) とは、異なるマシン間で安全に情報をやりとりするためのプロトコル（仕組み）です。これまでは、仮想マシンに遠隔ログインするために使いましたが、ここでは、Lizzie が仮想マシン上で leelaz を遠隔起動し、入出力をやりとりするために使います。

gcloud に組み込まれた ssh は現在、コマンドの標準入出力の取扱いに[バグ](https://twitter.com/miuraken/status/1069106866100174849)があり、VM 上の Leela Zero との通信にうまく使えません。そこで、Microsoft がメンテナンスしている OpenSSH をインストールします。

https://github.com/PowerShell/Win32-OpenSSH/releases から、OpenSSH-Win64.zip をダウンロードします。

ダウンロードしたら、展開したフォルダを適当なディレクトリの下におき、OpenSSH に名前を変更します。ここでは手抜きして、c:\OpenSSH におきました。さっそく c:\OpenSSH\ssh.exe を使って、VM に接続してみましょう。

*Google Cloud SDK Shell* または、コマンドプロンプト cmd.exe を立ち上げ、まずは `gcloud compute instances list` で、接続先の 外部 IP を確認します。

```shell-session
> gcloud compute instances list
NAME          ZONE          MACHINE_TYPE   PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP    STATUS
leela-zero-1  asia-east1-a  n1-standard-1  true         10.140.0.2   35.189.180.36  RUNNING
````

ここで 外部 IP (EXTERNAL IP) が **35.189.180.36** であることがわかります。

そして、

```
c:\OpenSSH\ssh.exe -o StrictHostKeyChecking=no -i %HOMEPATH%\.ssh\google_compute_engine <外部 IP>
```

という、コマンドを打ってみます。(<外部 IP> は置き換えてください。「\」は Windows端末では「¥」に見えたりしますね。）
![2018-12-03__2_.png](https://qiita-image-store.s3.amazonaws.com/0/64605/5c64e0c6-2082-73ea-09a3-3b20f88d8dc3.png)
このように、OpenSSH の ssh.exe でパスフレーズなどを聞かれることなしにログインできるはずです。

注：sshのオプション `-o StrictHostKeyChecking=no` は接続先の IP が新しいものであった場合に信頼していいかの確認を省略するためのもので、`C:\Users\<user名>\.ssh\config` に次のように書き込めば省略可です。

```config:C：\Users\miura\.ssh\config
host *
    StrictHostKeyChecking no
```

#Java のインストール

Lizzie を動かすためには Java 8 以上が必要です。
Java がインストールされているかはコマンドプロンプトで `java -version` と打ち込んでみて、java version 1.8.0 以上と表示されれば新規インストールはしなくても構いません。

Java がインストールされていない場合、https://java.com/ja/download/win10.jsp から、インストーラをダウンロードして、インストールしてください。

#####Java がインストールされていないとき:

```
C:\Users\miura> java version
'java' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
```

#####Java 8 (Java 1.8 以降）がインストールされているとき：

```
C:\Users\miura> java -version
java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) Client VM (build 25.191-b12, mixed mode)
```

#Lizzie のインストール

Lizzie は https://github.com/featurecat/lizzie/releases/ からダウンロードできます。
Lizzie.0.6.Windows.x64.CPU.zip をダウンロードします。
GPU/CPU の差は付属する Leela Zero 本体に関する違いなので、Lizzie 自体はどちらも同じです。
GPU を積んだ PC であれば、GPU 版を選ぶと、手元の Leela Zero を使うときに GPU の恩恵を受けられます。

zipファイルを展開した Lizzie フォルダを、好きなディレクトリに置きます。
ここではホームディレクトリの下に、C:\Users\ < user名 > \Lizzie として置きました。


![2018-12-03 (3).png](https://qiita-image-store.s3.amazonaws.com/0/64605/178a4dde-2c8d-3bea-3d26-4970ccdd9cc7.png)


Lizzie 実行時には、この C:\Users\ < user名 > \Lizzie\leela-zero の下にある leelaz.exe (Leela Zero のプログラム本体）を呼び出して実行します。Lizzie の Windows 版には、すでにこの leelaz.exe が実行可能な形で入っているので、この PC のみで Lizzie + Leela Zero を動かすならば、何も手を加える必要はありません。

しかし、今回は GCE の仮想マシンの上で動く leela zero を使いたいので、本物の leelaz.exe コマンドの代わりに、バッチスクリプトで書いた「ニセ leelaz」として leelaz.bat を用意することにします。

このバッチスクリプトは仮想マシン上で起動する `~/leela-zero/build/leelaz -g -b 0` を起動し、プロキシとして、データのやりとりを仮想マシン上の本物の leelaz と行います。
「ニセ leelaz」への標準入出力を、SSH を通じて、仮想マシン上の leelaz の標準入出力と「つなぐ」わけです。
![Lizzie-remote-pc.png](https://qiita-image-store.s3.amazonaws.com/0/64605/722b29ed-993e-6bc5-d5f0-e04ba88c55f8.png)


エディタ(メモ帳でよい）で次の内容をコピペし、それを `C:\Users\<user名>\Lizzie\leela-zero` フォルダに leelaz.bat というファイル名で保存します。

ここで、

- GOOGLE_ACCOUNT= につづく *miura.ken* を、[その 2](https://qiita.com/miuraken/items/0f9003e0161f8921ef61) で**ウェブ上の SSH でログインしたときのユーザ名**（ふつうは @gmail.com の前の部分）
- VM＿NAME＝ につづく *leela-zero-1* を**各自で用意した VM インスタンス名**

で置き換えてください。

```plaintext:leelaz.bat
@echo off
@set GOOGLE_ACCOUNT=miura.ken
@set VM_NAME=leela-zero-1
@set SSH_EXE=c:\OpenSSH\ssh.exe

for /f "usebackq tokens=*" %%i IN (`gcloud compute instances list --filter="name=('%VM_NAME%')" --format="csv[no-heading](EXTERNAL_IP)"`) DO @set LEELAZ_VM_IP=%%i
echo connecting to %LEELAZ_VM_IP%

@echo on
%SSH_EXE% -o StrictHostKeyChecking=no -i %HOMEPATH%\.ssh\google_compute_engine -l %GOOGLE_ACCOUNT% %LEELAZ_VM_IP% "~/leela-zero/build/leelaz -g -b 0"
```

（for から始まる行は、`gcloud compute instances list` から取得したVM の外部 IP を LEELAZ_VM_IP という環境変数に、自動的に代入するための行です。）

保存したら、試しに走らせてみましょう。

leelaz.bat を起動してみると、SSH経由で、VM上の ~/leela-zero/build/leelaz が走り、次のような出力がでてくるはずです。（ずっと繋いだままになるので、適当なところで Ctrl-C を使って接続を切ってください。）

<img width="1020" alt="leelaz.bat" src="https://qiita-image-store.s3.amazonaws.com/0/64605/fab6bb24-99f0-cd8e-b8cf-4bd8d3938cb3.png">

無事 leelaz.bat が動かせられたら、Lizzie が leelaz.exe の代わりに leelaz.bat を動かすようにします。
`C:\Users\<user名>\Lizzie\config.txt` を開き、engine-command の *./leela-zero/leelaz* を *./leela-zero/leelaz.bat* へ変更。

編集前：

```json
   "engine-command": "./leela-zero/leelaz --gtp --lagbuffer 0 --weights %network-file",
```

編集後：

```json
   "engine-command": "./leela-zero/leelaz.bat --gtp --lagbuffer 0 --weights %network-file",
```
へ。
ついでにその直後の *"print-comms": true* にしておくと、ちゃんと通信が行われていることも確認できます。

# Lizzie を起動する

これで Lizzie を起動できるはず。
Lizzie ディレクトリの中で `java -jar lizzie.jar` を叩きます。

```shell-session
java -jar lizzie.jar
```

これで Lizzie のウインドウが開きます。
"Leela Zero is Loading..." の表示が消えると、実際に候補が現れてきます。（場合によっては、石を置き始めると動き始めることも。）

私の環境では、画面が出てくるまでは数秒以内、Leela Zero の分析が走って画面が動き始めるまで起動から30秒ほどでした。

<img width="1061" alt="leelaz_win" src="https://qiita-image-store.s3.amazonaws.com/0/64605/9e3643b0-1686-420e-c426-2be50abd1883.gif">



終了するには、Lizzie ウインドウを閉じるか、コマンドプロンプトで Ctrl-C を押します。

## Lizzie が途中で止まる？

ただ、v0.6 で公式に配布されている lizzie.jar は、Windows 利用していると、あるタイミングで止まってしまうバグがあるようです。止まる場合も２種類あって、スペースバーを２度叩いて分析表示のオフオンを繰り返すと治る場合と、leelaz との通信を完全にやめてしまう場合があって、後者が深刻です。

最新のソースコードから自前でビルドすると、この校舎の問題が現れないようです。[JDK8](https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) と [Maven](https://maven.apache.org/) を自力で入れてビルドするとよいのですが少々手間なので、[こちらに自前でビルドした lizzie.jar を用意しました](https://github.com/miuraken/gce-leelazero-goodies/archive/master.zip)。手元の lizzie.jar を、こちらの recompiled フォルダに入っている lizzie.jar に、入れ替えて使ってみてください。

# gcloud コマンド経由での VM インスタンスの起動と終了

gcloud をインストールしたので、いちいち GCP コンソールに行かなくても、コマンドで　VM インスタンスの起動と終了を行えます。`gcloud init` でデフォルトのzoneを適切に設定していれば ---zone や --zones の引数は不要です。

###VM インスタンスの起動
`gcloud compute instances start <インスタンス名> --zone=<ゾーン名>`

```
> gcloud compute instances start leela-zero-1
Starting instance(s) leela-zero-1...done.
Updated [https://www.googleapis.com/compute/v1/projects/leela-zero-223713/zones/asia-east1-a/instances/leela-zero-1].
```

###VM インスタンスの停止
`gcloud compute instances stop <インスタンス名> --zone=<ゾーン名>`

```
> gcloud compute instances stop leela-zero-1
Stopping instance(s) leela-zero-1...done.
Updated [https://www.googleapis.com/compute/v1/projects/leela-zero-223713/zones/asia-east1-a/instances/leela-zero-1].
```

###VM インスタンスの状態を確認

`gcloud compute instances list --zones=<ゾーン名>`

```
> gcloud compute instances list
NAME          ZONE          MACHINE_TYPE   PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP    STATUS
leela-zero-1  asia-east1-a  n1-standard-1  true         10.140.0.2   35.221.236.35  RUNNING
````

#日々の運用

この環境で日常的に Leela Zero で研究を重ねたいときは、次のような流れになると思います。

1. ターミナルを立ち上げる
2. `gcloud compute instances start <インスタンス名> --zone=<ゾーン名>` で GCE 上 の VM を起動
3. `java -jar Lizzie\lizzie.jar` で Lizzie を立ち上げる
4. `gcloud compute instances stop <インスタンス名> --zone=<ゾーン名>` で GCE 上 の VM を停止
5. ときどき、利用料金を確認する。


実際には次のようなバッチファイルをつくって、それをダブルクリックすることで運用しています。

### はじめ.bat
(VM の起動と、Lizzie の立ち上げ。 Lizzie の再起動にも、これを利用。）

```
call gcloud compute instances start leela-zero-1
ｃｄ %HOMEPATH%\Lizzie
java -jar Lizzie.jar
```

### おわり.bat
(VM の終了。 こまめに切ると、GCE の利用料金の節約にも。)

```
gcloud compute instances stop leelz-zero-1
```

</div>
</details>

#おまけ：Leela Zero の操作方法
x キー長押しでヘルプが出てきます。

| キー       |          働き    |
|:----------------:|:------------------|
|n|Leela Zeroと新規対局|
|Enter|Leela Zero に着手させる|
|Space|分析表示切替|
|↑|一手戻る (Ctrl と組み合わせると10手戻る)|
|↓|一手進む (Ctrl と組み合わせると10手進む)|
|右クリック|一手戻る (Ctrl と組み合わせると10手戻る)|
|スクロールホイール|進む/戻る|
|c|座標表示切替|
|p|パス|
|.|対局結果|
|a|対局の自動分析実行|
|m|手順番号の表示/非表示|
|i|対局情報の編集|
|o|SGFファイルを開く|
|s|SGFファイルを保存|
|Ctrl-c|クリップボードにSGFをコピー|
|Ctrl-v|クリップボードからSGFをペースト|
|f|次の着手表示切替|
|v|変化図表示切替|
|w|勝率グラフの表示切替|
|Ctrl-w|勝率グラフの大きさを切替|
|g|変化図表示切替|
|[ ]|メインボードの位置を調整|
|t|コメント表示切り替え|
|Ctrl-t|コメントノードの色表示切替|
|y|ノードの色表示切替|
|home|初手へ|
|end|最終手へ|
|delete|手、枝を削除|
|backspace|手、枝を削除|