AWSでKataGo(囲碁AI)を動かす

## 目的
対局の検討をするために、強い囲碁AIが使いたい！
しかし自分のPCは貧弱！

ということで、AWSのハイスペックマシンを使ってKataGo（囲碁AI）を実行し、自分のPC上のLizzie（対局ソフト）から利用してみたいと思います。

## 使用するSW/HW
### KataGo
- KataGo: v1.15.3（katago-v1.15.3-trt10.2.0-cuda12.5-linux-x64）
  - https://github.com/lightvector/KataGo/releases のうち、最新版を選択
  - bs29というのは29路盤用なので使用しない
- ニューラルネット: kata1-b28c512nbt-s7332806912-d4357057652
  - https://katagotraining.org/networks の中から、最強のものを選択

### バックエンド
パフォーマンス面で優位なTensorRTを利用する（KataGoの[README.md](https://github.com/lightvector/KataGo?tab=readme-ov-file#opencl-vs-cuda-vs-tensorrt-vs-eigen)に解説アリ）
TensorRTを利用するには、CUDAも必要

KataGo v1.15.3のリリースノートを確認し、サポートしているバージョンを利用する
- CUDA: 12.5
- TensorRT: 10.2.0

### OS
上記ソフトウェアに対応しているものを選択
- Ubuntu 22.04

### EC2 インスタンスタイプ
- g4dn.xlarge
  - Google Colaboratoryと同程度のスペックらしいので、とりあえずこれを試す（スペックを上げるほど探索が速いが、高額になる）

## 手順
### EC2インスタンスの起動
EC2インスタンスを以下設定で起動する
* AMI: Ubuntu Server 22.04 LTS (HVM), SSD Volume Type
* アーキテクチャ: 64ビット(x86)
* インスタンスタイプ: g4dn.xlarge
* キーペア：あらかじめ作成しておいた鍵を指定
* ネットワーク設定：自分のIPからのsshを許可する
* ルートボリューム: 1*30GiB gp2（デフォルトの8GBだと足りない。30GBでギリギリくらい）

起動後、ssh接続できることを確認
```
ssh ubuntu@<パブリックIPアドレス>
```

### CUDAのインストール
以下ページにリリース一覧があるので、インストールしたいバージョンを選択する
https://developer.nvidia.com/cuda-toolkit-archive

今回は以下のインストールを行った
https://developer.nvidia.com/cuda-12-5-1-download-archive?target_os=Linux&target_arch=x86_64&Distribution=Ubuntu&target_version=22.04&target_type=deb_local

インストール手順は以下（上記ページに手順が記載されているのでそれに従えば良い）
```
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-ubuntu2204.pin
sudo mv cuda-ubuntu2204.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget https://developer.download.nvidia.com/compute/cuda/12.5.1/local_installers/cuda-repo-ubuntu2204-12-5-local_12.5.1-555.42.06-1_amd64.deb
sudo dpkg -i cuda-repo-ubuntu2204-12-5-local_12.5.1-555.42.06-1_amd64.deb
sudo cp /var/cuda-repo-ubuntu2204-12-5-local/cuda-*-keyring.gpg /usr/share/keyrings/
sudo apt-get update
sudo apt-get -y install cuda-toolkit-12-5
```
不要なファイルは削除する
```
rm cuda-repo-ubuntu2204-12-5-local_12.5.1-555.42.06-1_amd64.deb
```

### TensorRTのインストール
以下ページにリリース一覧があるので、インストールしたいバージョンを選択する
（インストーラはローカルにダウンロードされるので、scpでサーバに転送する）
https://developer.nvidia.com/tensorrt/download

※なお、ダウンロードにはNVIDIAへのアカウント登録が必要

インストール手順は以下（https://docs.nvidia.com/deeplearning/tensorrt/archives/index.html の中からインストールするバージョンを選択すると手順が確認できる）
```
# インストーラをscpでサーバに転送した上で以下実施
os="ubuntu2204"
tag="10.2.0-cuda-12.5"
sudo dpkg -i nv-tensorrt-local-repo-${os}-${tag}_1.0-1_amd64.deb
sudo cp /var/nv-tensorrt-local-repo-${os}-${tag}/*-keyring.gpg /usr/share/keyrings/
sudo apt-get update
sudo apt-get install tensorrt
```
不要なファイルは削除する
```
rm nv-tensorrt-local-repo-${os}-${tag}_1.0-1_amd64.deb
```

### CUDAドライバのインストール
ここまでの状態でKataGoを実行しようとすると、`error CUDA driver version is insufficient for CUDA runtime version` というエラーが発生してしまう

CUDAドライバのバージョンが足りていないことが原因なので、インストールを行う
（必要なドライバのバージョンはCUDAのバージョンにより異なる。[CUDAのダウンロードページ](https://developer.nvidia.com/cuda-12-5-1-download-archive?target_os=Linux&target_arch=x86_64&Distribution=Ubuntu&target_version=22.04&target_type=deb_local)の下部にオプションとして記されている手順を実行すればよい）
```
sudo apt-get install -y cuda-drivers-555
sudo reboot #反映には再起動が必要
```

### KataGoのダウンロード
参考手順：https://github.com/lightvector/KataGo

実行ファイルのインストール（https://github.com/lightvector/KataGo/releases からバージョンを選択）
```
cd ~ #実行ファイルは永続的に必要なのでホームディレクトリで作業する
wget https://github.com/lightvector/KataGo/releases/download/v1.15.3/katago-v1.15.3-trt10.2.0-cuda12.5-linux-x64.zip
sudo apt install unzip
unzip katago-v1.15.3-trt10.2.0-cuda12.5-linux-x64.zip
rm katago-v1.15.3-trt10.2.0-cuda12.5-linux-x64.zip
```

ニューラルネットファイルのダウンロード（https://katagotraining.org/networks の中から選択）
```
wget https://media.katagotraining.org/uploaded/networks/models/kata1/kata1-b28c512nbt-s7332806912-d4357057652.bin.gz
```

ただし、ここまでの手順だとkatagoを実行しようとした際に `./katago: error while loading shared libraries: libzip.so.5: cannot open shared object file: No such file or directory` というエラーが発生する

そこで、以下手順により必要なライブラリをインストールする
```
wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb
sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb
wget http://mirrors.kernel.org/ubuntu/pool/universe/libz/libzip/libzip5_1.5.1-0ubuntu1_amd64.deb
sudo apt install ./libzip5_1.5.1-0ubuntu1_amd64.deb
rm libssl1.1_1.1.1f-1ubuntu2_amd64.deb
rm libzip5_1.5.1-0ubuntu1_amd64.deb
```

### KataGoの設定
以下コマンドを実行すると、自動でチューニングが行われ、configファイル `gtp_custom.cfg` が生成する
対話的に質問されるが、ルールはchineseを選択、とりあえず他はデフォルトのままとした
```
./katago genconfig -model kata1-b28c512nbt-s7332806912-d4357057652.bin.gz -output gtp_custom.cfg
```

### Lizzieからの利用
ローカルPCのLizzieにおいて、`設定 > エンジン` から空いているエンジンに以下のようなコマンドを登録する
（`~/.ssh/config`を設定し、`ssh katago-server` で作成したEC2インスタンスに接続できるようにしてある場合）
```
ssh katago-server "./katago gtp -model kata1-b28c512nbt-s7332806912-d4357057652.bin.gz -config gtp_custom.cfg"
```

以下画像は、`エンジン1` に登録した場合の例
![2024-09-22 01 26 48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3891777/d5fd6dd6-0f9d-9067-1c31-6a6a7fbb934a.png)

Lizzieを起動するとデフォルトで `エンジン0` が使用されるので、`エンジン1` を選択し切り替えることでEC2インスタンス上のKataGoを使用できる
なお、KataGoの起動には数分程度かかる

## 2回目以降の使用手順
### 費用を抑えるために
EC2インスタンスは起動しっぱなしだと、多額の費用がかかる
そこで、以下の2案が考えられる
- 普段は停止しておき、利用する際のみ起動する
- AMI化した上で普段は削除しておき、利用する際のみAMIからインスタンスを作成する

後者の方が格安であり推奨（AMIからインスタンスを作成するのにもそこまで時間はかからない）
また、インスタンスを起動する際に、スポットインスタンスとして起動することで、さらに費用を低減可能

### インスタンスを再作成した際に必要な設定
グローバルIPが変わることに注意

`~/.ssh/config` でホスト名とIPアドレスを紐づけている場合は、それを編集すれば良い
（LizzieのエンジンにはIPアドレスではなくホスト名を指定しておくことで、再起動時にLizzieの設定の編集が不要となり、`~/.ssh/config`のみ編集すれば良いので便利）

### インスタンス作成を素早く行うために
AWSのWebUIからインスタンスを作成するよりも、AWS CLIを利用することでより素早く作成が行える

CLIの利用手順は https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-getting-started.html を参照

AMIからスポットインスタンスを起動する
```
aws ec2 request-spot-instances \
    --launch-specification '{"ImageId":"ami-xxxxxxxxxxxxxxxxx","InstanceType":"g4dn.xlarge","KeyName":"your-key-pair","SecurityGroupIds":["sg-xxxxxxxxxxxxxxxxx"],"SubnetId":"subnet-xxxxxxxxxxxxxxxxx"}'
```

インスタンスのインスタンスID、パブリックIP、起動状態を取得する
```
aws ec2 describe-instances --filters "Name=image-id,Values=ami-xxxxxxxxxxxxxxxxx" --query 'Reservations[].Instances[].{InstanceId: InstanceId, PublicIpAddress: PublicIpAddress, State: State}'
```

インスタンスの削除を行う
```
aws ec2 terminate-instances --instance-ids i-xxxxxxxxxxxxxxxxx
```

実行後に再び起動状態を取得し、`terminated` となっていれば削除完了