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

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

* [その 1 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 対応版）](https://qiita.com/miuraken/items/a910675ef173854dc1ad)


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

# はじめに
GCE 上で Leela Zero が動くようになったあとに、それを利用するやり方を macOS 環境を中心に書きます。
Linuxユーザーの方は、適当に読み替えてうまく対処できるかと思います。
Windows でのインストール方法や使い方も大まかには似たようになるはずですが、詳細は別エントリを書く予定です。

**その２では、仮想マシンに SSH でログインしての作業でしたが、このエントリは主に手元のマシンのターミナル上での作業となります。 (Mac だと例えば アプリケーション > ユーティリティ > ターミナル) **

# 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 のインストールと初期設定


GCE 上の仮想マシンに SSH で接続する方法はいくつかありますが、 ターミナルから GCP に対していろいろ操作できるツールである [gcloud](https://cloud.google.com/sdk/gcloud/?hl=ja) コマンドを利用して SSH を行うことにします。

gcloud をインストールするには、こちらのドキュメントが参考になります。
https://cloud.google.com/sdk/docs/quickstarts

ここでは以下のコマンドをターミナルに打ち込んで、手元の PC にインストールしてみます。

```
curl https://sdk.cloud.google.com | bash
exec -l $SHELL
```

これ gcloud がインストールされるので、続いて

`gcloud init`

というコマンドを叩くと、利用する Google アカウントを聞かれ、ブラウザが立ち上がって、ログインを求められます。
ここでは GCP のプロジェクトを作った Google アカウントを入れてください。
無事認証が済むと、プロジェクトとゾーンをそれぞれ聞かれるので、その１ので作ったプロジェクトのIDと、VM インスタンスを作ったゾーンを選択します。

<details><summary>gcloud コマンドを叩いても、*command not found* と言われる場合</summary><div>
この場合は、おそらく gcloud のあるディレクトリに「[PATH が通っていない](https://qiita.com/soarflat/items/09be6ab9cd91d366bf71)」状態です。

たとえば、SDK が ~/google-cloud-sdk にある場合、gcloud コマンドの本体は ~/google-cloud-sdk/bin/gcloud にあると思われます。`gcloud` のかわりに `~/google-cloud-sdk/bin/gcloud` を叩けばよいのですが、それでは少し面倒なので、
`export PATH=~/google-cloud-sdk/bin/gcloud:$PATH` 
というコマンドを打って PATH を通せば、それ以降は `gcloud` だけで起動できるはずです。このコマンドを、例えば ~/.bashrc の最後あたりに書いておくとターミナルを立ち上げるたびに自動で設定されます。
</div></details>

##SSH の設定

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

まずは `gcloud compute instances list` で、接続先のインスタンス名とゾーン名を確認します。

```
$ 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 インスタンス名が **leela-zero-1** 、ゾーン名が **asia-east1-a** であることがわかります。

そして、`gcloud compute ssh <インスタンス名> --zone=<ゾーン名>` という、コマンドを打ってみます。
gcloud を初めて起動する場合、SSH のための認証鍵の作成を行います。その途中で４回ほどパスフレーズを聞かれますが、全部同一のパスフレーズを入れてください。

###gcloud compute ssh の実行例

(SSHキーなどは改変してあります。）

```shell-session:
$ gcloud compute ssh miuraken@leela-zero-1 --zone=asia-east1-a
WARNING: The public SSH key file for gcloud does not exist.
WARNING: The private SSH key file for gcloud does not exist.
WARNING: You do not have an SSH key for gcloud.
WARNING: SSH keygen will be executed to generate a key.
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): <ここで新規のパスフレーズを入力>
Enter same passphrase again:　<ここで同じパスフレーズを入力>
Your identification has been saved in /Users/miuraken/.ssh/google_compute_engine.
Your public key has been saved in /Users/miuraken/.ssh/google_compute_engine.pub.
The key fingerprint is:
SHA256:reRaZg9fv/2FNTxxxxxxxxxxxxxxxxCwrkck miuraken@macbookpro
The key's randomart image is:
+---[RSA 2048]----+
|           ..o= o|
|          .o+=++.|
|         . ++=oB.|
|         .o +.o.*|
|     . oS+xxxxxoo|
|      Exxxxxx xo.|
|       .B+===== .|
|      .=.+xxxxx..|
|      ..  o   o.+|
+----[SHA256]-----+
Updating project ssh metadata...⠏Updated [https://www.googleapis.com/compute/v1/projects/leela-zero-223713].
Updating project ssh metadata...done.
Waiting for SSH key to propagate.
Warning: Permanently added 'compute.6058314298257684310' (ECDSA) to the list of known hosts.
Enter passphrase for key '/Users/miuraken/.ssh/google_compute_engine':　<また同じパスフレーズを入力>
Enter passphrase for key '/Users/miuraken/.ssh/google_compute_engine':　<またまた同じパスフレーズを入力>
Welcome to Ubuntu 16.04.5 LTS (GNU/Linux 4.15.0-1024-gcp x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  Get cloud support with Ubuntu Advantage Cloud Guest:
    http://www.ubuntu.com/business/services/cloud

Last login: Tue Nov 27 12:25:14 2018 from 14.3.60.115
miuraken@leela-zero-1:~$ ls
leela-zero

```
</div></details>

ログインできたら、念のため `ls` コマンドで、[その 2](https://qiita.com/miuraken/items/0f9003e0161f8921ef61)でインストールした leela-zero が見えているか確認してください。

<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 から一旦ログアウトしてください。

##SSH 認証鍵の保存

再び `gcloud compute ssh <インスタンス名> --zone <ゾーン名>` を打つと、今度はパスフレーズ１回でログインできるはずです。
ただ、今回はパスフレーズを１度も聞かれずにログインできるようにしたいので、SSH 接続に使う認証鍵（公開鍵）を VM インスタンスに登録します。
登録のためのコマンドは、macOS の場合、

`ssh-add <認証鍵へのパス>`

です。上の実行例だと、認証鍵へのパスが `~/.ssh/google_compute_engine` なので、実行すると次のようになります。

```
$ ssh-add -K ~/.ssh/google_compute_engine
Enter passphrase for /Users/miuraken/.ssh/google_compute_engine: <ここでパスフレーズを入力>
Identity added: /Users/miuraken/.ssh/google_compute_engine (/Users/miuraken/.ssh/google_compute_engine)
```

（ssh-add の -K オプションは、鍵の設定をキーチェーンに保存する macOS 専用の便利オプションです。）

# Lizzie のインストール

Lizzie は https://github.com/featurecat/lizzie/releases/ からダウンロードできます。
Lizzie.0.6.Mac-Linux.zip をダウンロード後、ダブルクリックして展開、できた Lizzie フォルダ（ディレクトリ）を好きなところに置きます。
ここではホームディレクトリの下、~/Lizzie におくものとします。

```shell-session
$cd ~/Lizzie

$ls
README.txt	lizzie.jar	network.gz	theme
```
Lizzie の実行には、この ~/Lizzie の下に、実行可能な leelaz ファイル（Leela Zero のプログラム本体）がないといけません。
ここで 、本物の leelaz コマンドの代わりに、シェルスクリプトで書いた「ニセ leelaz」を用意することにします。

このシェルスクリプトは仮想マシン上で起動する `~/leela-zero/build/leelaz -g -b 0` を起動し、プロキシとして、データのやりとりを仮想マシン上の本物の leelaz と行います。
「ニセ leelaz」への標準入出力を、SSH を通じて、仮想マシン上の leelaz の標準入出力と「つなぐ」わけです。

![Lizzie-remote.png](https://qiita-image-store.s3.amazonaws.com/0/64605/5fc302d8-d058-eaea-2c06-9f2289e1ba7f.png)


エディタ(TextEditアプリでよい）で次の内容を打ち込み、それを ~/Lizzie/ フォルダに leelaz というファイル名で保存します。
（ < VMインスタンス名>、<ゾーン名> はそれぞれ置き換えてください。）

```bash:~/leelaz
#!/bin/bash
set -eu
gcloud compute ssh <VMインスタンス名> --zone=<ゾーン名> --command="~/leela-zero/build/leelaz -g -b 0"
```

<details><summary>~/leelaz の実際の例</summary><div>

VM インスタンス名が leela-zero-1 、ゾーンが asia-east1-a の場合、

```bash:~/leelaz
#!/bin/bash
set -eu
gcloud compute ssh leela-zero-1 --zone=asia-east1-a --command="~/leela-zero/build/leelaz -g -b 0"
```
</div></details>


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

```
# Lizzie のディレクトリに移動
cd ~/Lizzie/

# leelaz を実行できるようにしておく
chmod +x leelaz

$ ./leelaz
Using 1 thread(s).
RNG seed: 10990264878177256973
Using per-move time margin of 0.00s.
BLAS Core: built-in Eigen 3.3.5 library.
Detecting residual layers...v1...256 channels...40 blocks.
Initializing OpenCL (autodetecting precision).
Detected 1 OpenCL platforms.
Platform version: OpenCL 1.2 CUDA 10.0.182
Platform profile: FULL_PROFILE
Platform name:    NVIDIA CUDA
Platform vendor:  NVIDIA Corporation
Device ID:     0
Device name:   Tesla K80
Device type:   GPU
Device vendor: NVIDIA Corporation
Device driver: 410.72
Device speed:  823 MHz
Device cores:  13 CU
Device score:  1112
Selected platform: NVIDIA CUDA
Selected device: Tesla K80
with OpenCL 1.2 capability.
（以下略）
```

ここでエラーが出る場合は、上の「leela-zero が見えないとき」を参考に、 Leela Zero の再インストールを検討してください。


# Lizzie を起動する

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

```shell-session
$ java -jar lizzie.jar
Creating config file config.txt
Creating config file persist
```

ターミナルでの出力はここで一旦とまり、Lizzie のウインドウが開きます。
"Leela Zero is Loading..." の表示が消えると、実際に候補が現れてきます。
（私の環境では、画面が出てくるまで10秒弱、Leela Zero の分析が走って画面が動き始めるまで起動から30秒弱でした。）

![lz.gif](https://qiita-image-store.s3.amazonaws.com/0/64605/f64812c5-6cfa-7a1e-8367-7c07813b5540.gif)


終了するには、Lizzie ウインドウを閉じるか、ターミナルで Ctrl-C を押します。

# 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. ときどき、利用料金を確認する。

2+3, 4 をそれぞれシェルスクリプトにして、[iTerm2](https://www.iterm2.com/) あたりで開く設定にしておくと、さらに便利になりそうですね。

#おまけ：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|手、枝を削除|

MacBook Pro だと、home キーは `fn＋←`、end キーは `fn+→` みたいです。