Google Colaboratory 上で Elixir Livebook を動かす（GPUを無料で使う）

## はじめに

:::note info
2024年10月時点の手順は以下の記事を参照

https://qiita.com/RyoWakabayashi/items/b1d91ee5a1664ab1499d
:::

Elixir の Nx を GPU 上で動かしたい

https://github.com/elixir-nx/nx

Nx を GPU で動かせば、 AI モデルもスイスイ動かせるはず

でも手元に GPU もないし、お金もない、、、

そんな人にオススメなのが Google Colaboratory です

https://colab.research.google.com/notebooks/welcome.ipynb?hl=ja

ブラウザからコードを実行、結果を表示できます

基本無料で使用でき、 GPU や TPU まで使える最高のサービスです

え、 Python 用じゃないかって？

そう、 Google Colaboratory は Python の Jupyter Notebook をクラウド上で使用できるサービスです

https://jupyter.org/

が、実は Elixir Livebook も実行できてしまうのです

実装したノートブックはこちら（※Evisionを入れるための設定も含んでいます）

https://github.com/RyoWakabayashi/elixir-learning/blob/main/colab/Livebook.ipynb

:::note info
2024/05/29

ngrok バージョンアップ対応で更新
:::

## 参考記事

https://www.kaggle.com/code/nduatik/livebook-kaggle-example/notebook

## 準備

以下の二つのアカウントが必要になります

- Google アカウント
- ngrok アカウント

ngrok はローカルで起動した Web サービスを外部に公開できるサービスです

https://ngrok.com/

例えばローカルの http://localhost:8080 を https://xxxx-12-34-567-890.ngrok.io のような URL で一時的に外部からアクセスできるようにしてくれます

（もう何をやるか気づいた人は気付いたと思います）

ngrok にサインインしたら、左メニューの「Your Authtoken」を開き、
認証トークンを出しておいてください

![スクリーンショット 2022-10-26 18.15.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/ca0b29fc-41b1-0885-d53b-450ea8fdc3fc.png)

## Google Colaboratory での Livebook 起動

### ランタイムの設定

Google Colaboratory で「ノートブックを新規作成」をクリックしてください

![スクリーンショット 2022-10-26 18.18.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/145e0842-80ed-601b-dea8-ffd30305c2fd.png)

以下のような画面が開きます

![スクリーンショット 2022-10-26 18.21.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/c013cbcf-4634-689a-fffb-43eea35ea6d6.png)

GPU が使いたいので、ラインタイムを変更します

上メニューの「ランタイム」から「ラインタイムのタイプを変更」をクリックしてください

![スクリーンショット 2022-10-26 18.23.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/70334bbf-23db-d9f8-d575-5e3b8c050047.png)

開いたモーダルで「T4 GPU」を選択し、「保存」をクリックします

![スクリーンショット 2024-05-29 13.10.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/3ecd50fc-afc3-d569-02ef-c2c557bceee1.png)

### asdf のインストール

Erlang や Elixir は新しいものを使いたいので、 [asdf](https://asdf-vm.com/) を使います

まず asdf をインストールしましょう

GitHub から asdf をクローンします

再生マークの右にある入力エリア（セル）に以下のコードを貼り付けてください

:::note info
Jupyter Notebook で `!` を先頭に付けると、シェルコマンドを実行することができます
これを利用して、 Google Colaboratory のコンテナ上に Elixir をインストールするわけです
:::

```
!git clone https://github.com/asdf-vm/asdf.git ~/.asdf
```

再生ボタンをクリック（=セルのコードを実行）して、しばらくすると左側に緑色のチェックマークが表示されます

すると、セルが一つ下に追加されるので、以下のコードを貼り付けてから、新しいセルを実行してください

asdf の実行ファイルにパスを通します

```python
import os
os.environ['PATH'] = "/root/.asdf/shims:/root/.asdf/bin:" + os.environ['PATH']
```

### Erlang のインストール

asdf で Erlang をインストールするため、プラグインを追加します

```
!asdf plugin add erlang
```

Erlang をバージョン指定でインストールします

```
!asdf install erlang 26.2.5
```

使用する Erlang のバージョンを `26.2.5` に固定します

```
!asdf global erlang 26.2.5
```



### Elixir のインストール

asdf で Elixir をインストールするため、プラグインを追加します

```
!asdf plugin add elixir
```

Elixir をバージョン指定でインストールします

```
!asdf install elixir 1.16.3-otp-26
```

:::note info
Erlang が 26 なので `otp-26` を指定しています
:::

使用する Elixir のバージョンを `1.16.3-otp-26` に固定します

```
!asdf global elixir 1.16.3-otp-26
```

Elixir　のバージョンを確認します

```
!elixir -v
```

実行結果は以下のようになります

```
Erlang/OTP 26 [erts-14.2.5] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1] [jit:ns]

Elixir 1.16.3 (compiled with Erlang/OTP 26)
```

### Livebook のインストール

次のコードを実行して Livebook の最新版をインストールします

```
!mix local.hex --force
!mix local.rebar --force
!mix escript.install hex livebook --force
```

Livebook をコマンドから使えるようにします

```
!asdf reshim elixir
```

Livebook のバージョンを確認します

```
!livebook -v
```

結果は以下のようになります

```
Erlang/OTP 26 [erts-14.2.5] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1] [jit:ns]

Elixir 1.16.3 (compiled with Erlang/OTP 26)

Livebook 0.12.1
```

### Elixir モジュールの依存パッケージ

Livebook で Evision を使う場合、 OpenCV の依存モジュールが必要になります

また、 Elixir Image を使う場合は libvips が必要になります

Livebook 上で使う Elixir モジュールの依存パッケージをインストールしておきます

```
!sudo apt-get install -y unzip ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libvips-dev
```

### ngrok CLI のインストール

続いて ngrok のクライアントツールをインストールします

:::note info
v2 のクライアントが使えなくなったため、 v3 をインストールします
:::

```
!curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc \
	| sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null \
	&& echo "deb https://ngrok-agent.s3.amazonaws.com buster main" \
	| sudo tee /etc/apt/sources.list.d/ngrok.list \
	&& sudo apt update \
	&& sudo apt install ngrok
```

次のコードを実行すると、パスワード入力が表示されるので、準備しておいた ngrok の認証トークンを入力し、 Enter を押してください

```python
from getpass import getpass

token = getpass()
```

入力した認証トークンの値を ngrok で使うように設定します

```
!./ngrok authtoken "$token"
```

次のコードを実行し、 ngrok でコンテナ内の `http://localhost:8888` を外部公開します

```
get_ipython().system_raw('./ngrok http 8888 &')
!sleep 5s
```

次のコードを実行すると、公開された URL が表示されます

（ここでエラーが出る場合、 ngrok の認証トークンが誤っています）

```
!curl -s http://localhost:4040/api/tunnels | python3 -c "import sys, json; print(json.load(sys.stdin)['tunnels'][0]['public_url'])"
```

![スクリーンショット 2022-10-26 18.42.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/13f94662-7323-e8ac-8443-bcabb80b7cd4.png)

次のコードを実行し、 Livebook を 8888　ポートで起動します

```
!livebook server --port 8888
```

うまくいけば Livebook が起動した旨表示され、セルが実行中の状態のままになります

URL の末尾についている `?token=<Livebook認証トークン>` の「Livebook認証トークン」の値が必要になるので、コピーしておきます

![スクリーンショット 2023-08-02 9.56.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/724cc7b0-4621-f528-7f96-e72a23f52c3a.png)

この状態で ngrok の外部公開 URL を開きます

認証を求められるので、先程コピーしておいた「Livebook認証トークン」を貼り付けて "Authenticate ->" をクリックします

![スクリーンショット 2023-08-02 9.58.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/93faf77f-eefe-95a7-4309-5983a4f7d056.png)

はい、これで Livebook が開きました

![スクリーンショット 2022-10-26 18.47.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/638e2696-a841-fec2-d0f7-c97d2164de78.png)

## Nx の実行

右上「New Notebook」をクリックして、新しいノートブックを開きます

![スクリーンショット 2022-10-26 18.49.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/6cf4b81d-5fb8-9c49-aa87-1cd6e92e655b.png)

`Notebook dependencies and setup` のセルに以下のコードを貼り付け、実行してください

```elixir
Mix.install(
  [
    {:benchee, "~> 1.3"},
    {:nx, "~> 0.7"},
    {:exla, "~> 0.7"},
    {:torchx, "~> 0.7"},
    {:evision, "~> 0.1"},
    {:kino, "~> 0.12"}
  ],
  system_env: [
    {"XLA_TARGET", "cuda120"},
    {"EXLA_TARGET", "cuda"},
    {"LIBTORCH_TARGET", "cu118"},
    {"EVISION_ENABLE_CUDA", "true"},
    {"EVISION_ENABLE_CONTRIB", "true"},
    {"EVISION_CUDA_VERSION", "121"}
  ]
)
```

`system_env` で指定しているのは EXLA、 Torchx、 Evision が GPU を使うための環境変数設定です

しばらく時間がかかりますが、 Nx、 EXLA、 Torchx、 Evsion 等が GPU 用にインストールされます

![スクリーンショット 2022-10-26 18.55.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/66ff36fe-86d7-71e5-1e17-0f31b5f02fec.png)

次のコードを実行してみましょう

```elixir
tensor = Nx.tensor([1.0, 2.0, 3.0], backend: Nx.BinaryBackend)
Nx.add(tensor, tensor)
```

![スクリーンショット 2022-10-26 18.57.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/ba1b7c49-4fd1-38d5-0f36-fd08b9f3b4a9.png)

バイナリバックエンドは正常に動いていますね

続いて EXLA です

EXLA.Backend の場合、 `device_id` で何番目の GPU を使うか指定できます

```elixir
tensor = Nx.tensor([1.0, 2.0, 3.0], backend: {EXLA.Backend, device_id: 0})
Nx.add(tensor, tensor)
```

![スクリーンショット 2022-10-26 18.59.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/c9b20262-df20-8bcf-0ae3-f7b4dd1dd1ea.png)

CUDA で動いていることが分かります

Torchx　でも同じようにしてみます

特に指定しなくても GPU を使いますが、 `device: :cuda` で明示できます

```elixir
tensor = Nx.tensor([1.0, 2.0, 3.0], backend: {Torchx.Backend, device: :cuda})
Nx.add(tensor, tensor)
```

![スクリーンショット 2022-10-26 19.02.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/4e17c9f0-ab4a-5ad8-c49e-709714734744.png)

初回実行だけ4分くらいかかりますが、無事 CUDA で動いています

Torchx の場合、デフォルトで GPU を使うようになっていても CPU 指定が可能です

```elixir
tensor = Nx.tensor([1.0, 2.0, 3.0], backend: {Torchx.Backend, device: :cpu})
Nx.add(tensor, tensor)
```

![スクリーンショット 2023-08-02 12.41.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/e99d207b-e4e4-30a2-3a55-cf358a4ccaed.png)

最後に Evision です

```elixir
tensor = Nx.tensor([1.0, 2.0, 3.0], backend: Evision.Backend)
Nx.add(tensor, tensor)
```

![スクリーンショット 2023-08-02 10.03.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/99e042c0-bc4d-0c2d-afdc-f48902287b27.png)

## おわりに

これで GPU を使った Elixir の機械学習が捗りますね