Google Colab + Livebook + Pythonx + StableDiffusion で画像生成する

## はじめに

以前の記事の組み合わせで、 Google Colab 上で GPU を使い、 Elixir の Livebook で Python コードを実行して、 StableDiffusion で画像を生成します

https://qiita.com/RyoWakabayashi/items/4b689b7555593edd8519

https://qiita.com/RyoWakabayashi/items/9f6194ddc930be15e901

Python の実行環境から Elixir の実行環境を起動して、その上で Python を動かすという遠回りな手法ではありますが、 Elixir と Python を連携させつつ、無料で GPU が使えるのは魅力的です

実装したノートブックはこちら

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

https://github.com/RyoWakabayashi/elixir-learning/blob/main/colab/stable_diffusion_with_pythonx.livemd

## Google Colab 上での Livebook 起動

### 環境確認

```
!cat /etc/os-release
```

```
!nvcc --version
```

```
!cat /usr/include/cudnn_version.h | grep CUDNN_MAJOR -A 2
```

2025年6月時点では、 Google Colab の GPU ランタイム環境は以下のようになっています

- OS: Ubuntu 22.04.4
- CUDA: 12.5.82
- cuDNN: 9.2.1

特に問題ないので、このまま進めます

### mise のインストール

以下のコードを実行し、mise をインストールします

```
!sudo apt update -y && sudo apt install -y gpg sudo wget curl
!sudo install -dm 755 /etc/apt/keyrings
!wget -qO - https://mise.jdx.dev/gpg-key.pub | gpg --dearmor | sudo tee /etc/apt/keyrings/mise-archive-keyring.gpg 1> /dev/null
!echo "deb [signed-by=/etc/apt/keyrings/mise-archive-keyring.gpg arch=amd64] https://mise.jdx.dev/deb stable main" | sudo tee /etc/apt/sources.list.d/mise.list
!sudo apt update
!sudo apt install -y mise
```

mise でインストールした Elixir と、 Hex でインストールした Livebook へのパスを通します

```python
import os

os.environ['PATH'] = "/root/.local/share/mise/shims:/root/.mix/escripts:" + os.environ['PATH']
```

### Erlang のインストール

Erlang の 27.3 をインストールします

```
!mise use -g erlang@27.3
```

### Elixir のインストール

Elixir の 1.18.4 OTP 27 をインストールし、バージョン確認をします

```
!mise use -g elixir@1.18.4-otp-27
!elixir -v
```

## Livebook のインストール

Livebook をインストールし、バージョン番号を確認します

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

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

[ngrok](https://ngrok.com/) CLI をインストールします

ngrok を使うことで、 Colab 上の localhost で起動した Livebook に外部からアクセスできます

:::note info
ngrok にアカウントを作成し、認証トークンを準備しておきましょう

![スクリーンショット 2024-10-06 9.12.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/6d22c3db-0917-2d0e-bb12-5327145a1af6.png)![スクリーンショット 2024-10-06 9.12.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/2a940705-290b-b7cd-e8e5-92c40eb0bad4.png)

:::

```bash
!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 の認証トークンを入力します

```python
from getpass import getpass

token = getpass()
```

![スクリーンショット 2024-10-06 9.11.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/9c881dfe-494c-34ee-47e9-18e0eaf11b34.png)

ngrok CLI に認証トークンを設定します

```bash
!ngrok config add-authtoken "$token"
```

### Livebook の起動

ngrok を起動し、 Colab 上の localohost 8888 ポートを外部に公開します

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

ngrok が外部に公開している URL を取得します

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

Livebook を 8888 ポートで起動します

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

ngrok の公開 URL にアクセスすると、 Livebook の認証画面が表示されます

![スクリーンショット 2024-10-06 9.12.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/a264c79b-6fec-8a9c-6873-5341cc9a7d4f.png)

Livebook 起動時に表示される URL 末尾の認証トークンを入力してください（下画像の赤枠部分）

![スクリーンショット 2024-10-06 9.12.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/bf70f1a3-0f80-f7cf-685e-96d47381f151.png)

## StableDiffusion による画像生成

### セットアップ

Livebook のホーム画面から、右上の青い "+ New notebook" ボタンで新しいノートブックを開きます

![スクリーンショット 2025-05-29 9.53.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/46c6d97b-54f8-4fc5-a0d8-06f40c6517d3.png)

一番上のセットアップセルをクリックすると、セルの左下に見慣れない "+ Python" ボタンが表示されます

![スクリーンショット 2025-05-29 9.52.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/69e9ecb2-dbb0-471a-b111-a5852af54213.png)

 "+ Python" ボタンをクリックすると、セットアップセルに Python セル用の設定が自動的に追加されます

 ![スクリーンショット 2025-05-29 9.56.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/8f86e1f3-7444-4384-87de-4eecfdbb80d3.png)

Elixir のセットアップセルに以下のコードを入力します

```elixir
Mix.install([
  {:pythonx, "~> 0.4.2"},
  {:kino_pythonx, "~> 0.1.0"},
  {:evision, "~> 0.2.13"}
])
```

Python のセットアップセルに以下のコードを入力します

```toml
[project]
name = "project"
version = "0.0.0"
requires-python = "==3.13.*"
dependencies = [
  "diffusers[torch]",
  "transformers"
]
```

セットアップセルを実行すると、必要なモジュールがインストールされます

### GPU 使用可能確認

Python セルで CUDA が使用できるかどうかを確認します

```python
import torch

torch.cuda.is_available()
```

実行結果

```python
True
```

### StableDiffusion による画像生成

Python セルで `diffusers` を使い、画像生成します

```python
from diffusers import DiffusionPipeline

pipeline = DiffusionPipeline.from_pretrained(
  "stable-diffusion-v1-5/stable-diffusion-v1-5",
  torch_dtype=torch.float16)
pipeline.to("cuda")

images = pipeline("Jumping dog", num_inference_steps=100).images
images[0]
```

実行結果

![スクリーンショット 2025-06-19 23.40.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/5279a683-73c5-46d3-bf94-9191a5775f8d.png)

20 秒くらいで画像生成できました

### Kino との組み合わせ

せっかくなので、 Kino の入出力と組み合わせましょう

Elixir セルでテキスト入力を用意します

```elixir
prompt_input = Kino.Input.text("prompt")
```

「Flying cat」と入力しておきます

![スクリーンショット 2025-06-19 23.43.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/066e9ec5-5f75-4ed1-a974-8aee2da40942.png)

Elixir セルでテキスト入力を読み込みます

```elixir
prompt = Kino.Input.read(prompt_input)
```

実行結果

```elixir
"Flying cat"
```

Python セルで画像生成します

```python
images = pipeline(prompt.decode(), num_inference_steps=100, num_images_per_prompt=4).images
images[0]
```

実行結果

![スクリーンショット 2025-06-19 23.46.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/699f46b8-df09-4b28-ac75-40848d192bde.png)

Python セルで Elixir への連携用にデータを変換します

```python
import io

binary_images = []

for image in images:
  binary_image = io.BytesIO()
  image.save(binary_image, format='PNG')
  binary_images.append(binary_image.getvalue())
```

Elixir セルで画像を 2 x 2 に並べて表示します

```elixir
binary_images
|> Pythonx.decode()
|> Enum.map(fn binary_image ->
  Evision.imdecode(binary_image, Evision.Constant.cv_IMREAD_COLOR())
end)
|> Kino.Layout.grid(columns: 2)
```

実行結果

![スクリーンショット 2025-06-19 23.48.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/74c70d07-bb4c-467e-a5d2-1ebb9a682a26.png)

## まとめ

Google Colab 上で Livebook を起動することで、 Pythonx から CUDA を利用し、画像生成することができました

Elixir と Python を自然に行き来できるので、 AI を動かすのも簡単ですね