M1 Mac に Stable Diffusion をインストールする

記事の内容は、自分のための備忘録となる。

### インストールの主な流れ

1. homebrew で必要なソフトウェアのインストール
1. Stable Diffustion のソースコードを取得 
1. miniconda で Stable Diffusion が依存するモジュールをインストールする
1. Stable Diffusion が依存しているモジュールを取得
1. 画像生成に必要な weight の取得
1. サンプルで画像を生成
1. M1 Mac mini メモリ 16GB での実行時間

### 1. homebrew で必要なソフトウェアのインストール

M1 Mac に [homebrew](https://brew.sh/index_ja) がインストールされている前提で始める。

最初に必要となるソフトウェアは次の通り。

* Python (>= 3.10.0)
* cmake
* protobuf
* rust

まずは、homebrew のソフトウェアのインストール状況を確認する。

```zsh
$ brew list | grep -E "(python|cmake|protobuf|rust)"
$ 
```

該当のソフトウェアがインストールされていなければ、プロンプトのみが返ってくる。既にインストール済みであれば、次のような出力例を得られる。

```zsh
$ brew list | grep -E "(python|cmake|protobuf|rust)"
cmake
protobuf
python@3.10
python@3.9
rust
```
Stable Diffusion を動作させるのに　Python のバージョンが 3.10.0　以上が必要となるので、Python のバージョンを確認する。

:::note warn
複数のバージョンの Python を共存させたい場合は、[pyenv](https://github.com/pyenv/pyenv) を使用して、Python をインストールする。
:::

必要なソフトウェアがインストールされていなければ、homebrew からインストールする。下記のコマンドは、すべてがインストールされていない場合のコマンドとなる。

```zsh
$ brew install python cmake protobuf rust
```

Python のバージョンを確認する。

```zsh
$ python -V
Python 3.10.4
```

### 2. Stable Diffustion のソースコードを取得

今回は、https://github.com/bfirsh/stable-diffusion の Stable Diffustion を利用する。

今回は、リポジトリの clone 先を ~/works とする。

```zsh
$ mkdir -p ~/works
$ cd ~/works/
$ git clone -b apple-silicon-mps-support https://github.com/bfirsh/stable-diffusion.git
$ cd stable-diffusion
$ mkdir -p models/ldm/stable-diffusion-v1
```

### 3. miniconda で Stable Diffusion が依存するモジュールをインストールする

環境の構築には、miniconda を利用した。miniconda を未インストールの場合は、homebrew からインストールする。

```zsh
$ brew install miniconda
```

Stable Diffustion のリポジトリ内で実行環境を構築する。

```zsh
$ cd ~/works/stable-diffusion
$ conda env create -f environment-mac.yaml
```

`$ conda env create -f environment-mac.yaml` を実行すると、Stable Diffustion 用の環境の構築が始まる。

もし、エラーが発生した場合は、エラーの原因を解消して、次のコマンドを実行すると、環境構築の途中から再開する。

```zsh
$ conda env update -f environment-mac.yaml
```

そして、

```zsh
$ conda init zsh
```

を実行する。

miniconda インストール時に ~/.zshrc に miniconda の初期化が記述されている。現在、開いていターミナルを閉じ、新たにターミナルを開始すると、次のような表示になる。

```zsh
(base) $
```

Stable Diffusion のソースがあるディレクトリに移動して、下記のコマンドを実行すると、Stable Diffusion 用の実行環境に切り替わることを確認できる。

```zsh
(base) $
(base) $ cd ~/works/stable-diffusion
(base) $ conda activate ldm
(ldm) $
```

### 4. Stable Diffusion が依存しているモジュールを取得

そして、Stable Diffusion の依存モジュールをインストールする。

```zsh
(ldm) $ ls -l requirements.txt
-rw-r--r--  1 yourname  staff  550 10  1 00:12 requirements.txt
$ cat requirements.txt
numpy==1.23.1
--pre torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/nightly/cpu

albumentations==0.4.6
diffusers
opencv-python==4.6.0.66
pudb==2019.2
invisible-watermark
imageio==2.9.0
imageio-ffmpeg==0.4.2
pytorch-lightning==1.4.2
omegaconf==2.1.1
test-tube>=0.7.5
streamlit>=0.73.1
einops==0.3.0
torch-fidelity==0.3.0
transformers==4.19.2
torchmetrics==0.6.0
kornia==0.6
-e git+https://github.com/CompVis/taming-transformers.git@master#egg=taming-transformers
-e git+https://github.com/openai/CLIP.git@main#egg=clip
(ldm)
(ldm) $ pip install -r requirements.txt
```


### 5. 画像生成に必要な weight の取得

[Hugging Face repository](https://huggingface.co/) から weight を取得する。

weight の入手には、Hugging Face repository のアカウントが必要になる。
アカウント作成後に、[次のページ](https://huggingface.co/CompVis/stable-diffusion-v-1-4-original) で Access repository のボタンをクリックして、sd-v1-4.ckpt (サイズは約 4GB) をダウンロードする。

ダウンロードした sd-v1-4.ckpt は、さきほど Stable Diffusion のリポジトリ直下に作成したディレクトリ `models/ldm/stable-diffusion-v1` に移動して、ファイル名を sd-v1-4.ckpt から model.ckpt に変更する。

:::note warn
もし、model.ckpt を sd-v1-4.ckpt のシムリンクで対応させる場合、シムリンクは絶対パスで指定して作成する。 
例: ln -s /path/to/stable-diffusion/models/ldm/stable-diffusion-v1/sd-v1-4.ckpt /path/to/stable-diffusion/models/ldm/stable-diffusion-v1/model.ckpt
参照: [FileNotFoundError: [Errno 2] No such file or directory: 'models/ldm/stable-diffusion-v1/model.ckpt'](https://github.com/CompVis/stable-diffusion/issues/189)
:::

### 6. サンプルで画像を生成

次のコマンドを実行して、サンプルに画像を生成してみます。

```zsh
(ldm) $ python scripts/txt2img.py --prompt "a red juicy apple floating in outer space, like a planet" --n_samples 1 --n_iter 1 --plms
```
画像の出力が成功すると、`outputs/txt2img-samples` 下に画像ファイルが生成されているはず。

#### 2022年 10月1日時点で発生したエラーについて

上記のコマンドを実行すると、下記のエラーが表示された。

```zsh
(ldm) $ python scripts/txt2img.py --prompt "a red juicy apple floating in outer space, like a planet" --n_samples 1 --n_iter 1 --plms
from google.protobuf.pyext import _message
ImportError: dlopen(/opt/homebrew/Caskroom/miniconda/base/envs/ldm/lib/python3.10/site-packages/google/protobuf/pyext/_message.cpython-310-darwin.so, 0x0002): symbol not found in flat namespace (__ZN6google8protobuf15FieldDescriptor12TypeOnceInitEPKS1_)
```

調べてみると、protobuf のバージョンの差分が原因らしい。

* 参照: [ImportError: symbol not found in flat namespace (protobuf 3.20.2)](https://github.com/protocolbuffers/protobuf/issues/10571)

そこには、protobuf のバージョンを 3.19.4 にダウングレードすれば、動くと記載されていた。

> 3.19.5 also suffers from the same issue. Downgrading to 3.19.4 works.
> Running "pip install protobuf==3.19.4" to downgrade also worked for me.

バージョンを 3.20.1 にしても動くと記載されているが、3.20.1 にするとモジュールの依存関係が壊れると警告が出る。

```zsh
(ldm) $ pip install protobuf==3.20.1
Collecting protobuf==3.20.1
  Downloading protobuf-3.20.1-cp310-cp310-macosx_10_9_universal2.whl (962 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 962.3/962.3 kB 7.9 MB/s eta 0:00:00
Installing collected packages: protobuf
  Attempting uninstall: protobuf
    Found existing installation: protobuf 3.19.6
    Uninstalling protobuf-3.19.6:
      Successfully uninstalled protobuf-3.19.6
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
tensorboard 2.10.1 requires protobuf<3.20,>=3.9.2, but you have protobuf 3.20.1 which is incompatible.
Successfully installed protobuf-3.20.1
```

そのため、バージョン 3.19.4 をインストールした。

```zsh
(ldm) $ pip install protobuf==3.19.4
Collecting protobuf==3.19.4
  Downloading protobuf-3.19.4-cp310-cp310-macosx_10_9_universal2.whl (961 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 961.7/961.7 kB 8.0 MB/s eta 0:00:00
Installing collected packages: protobuf
  Attempting uninstall: protobuf
    Found existing installation: protobuf 3.20.1
    Uninstalling protobuf-3.20.1:
      Successfully uninstalled protobuf-3.20.1
Successfully installed protobuf-3.19.4
```

必要あるかどうかわからないが、requirements.txt に protubuf のバージョンを指定する記述を追加した。。

```zsh
(ldm) $ cp -p requirements.txt requirements.txt.bak
(ldm) $ vi requirements.txt
(ldm) $ diff -uw requirements.txt.bak requirements.txt
--- requirements.txt.bak	2022-10-01 17:13:11.000000000 +0900
+++ requirements.txt	2022-10-01 17:18:47.000000000 +0900
@@ -17,5 +17,6 @@
 transformers==4.19.2
 torchmetrics==0.6.0
 kornia==0.6
+protobuf==3.19.4
 -e git+https://github.com/CompVis/taming-transformers.git@master#egg=taming-transformers
 -e git+https://github.com/openai/CLIP.git@main#egg=clip
```

### 7. M1 Mac mini メモリ　　16GB での実行時間

画像生成に 20分ぐらい時間がかかった……。

```zsh
(ldm) $ python scripts/txt2img.py --prompt "a red juicy apple floating in outer space, like a planet" --n_samples 1 --n_iter 1 --plms
Global seed set to 42
Loading model from models/ldm/stable-diffusion-v1/model.ckpt
Global Step: 470000
LatentDiffusion: Running in eps-prediction mode
DiffusionWrapper has 859.52 M params.
making attention of type 'vanilla' with 512 in_channels
Working with z of shape (1, 4, 32, 32) = 4096 dimensions.
making attention of type 'vanilla' with 512 in_channels
...
PLMS Sampler: 100%|████████████████████████████████████████████████████████████████████████████████| 50/50 [20:44<00:00, 24.90s/it]
data: 100%|████████████████████████████████████████████████████████████████████████████████████████| 1/1 [20:53<00:00, 1253.90s/it]
Sampling: 100%|████████████████████████████████████████████████████████████████████████████████████| 1/1 [20:53<00:00, 1253.90s/it]
Your samples are ready and waiting for you here:
outputs/txt2img-samples

Enjoy.
(ldm) $ ls -l outputs/txt2img-samples
total 608
-rw-r--r--  1 yourname  staff  311071 10  1 17:54 grid-0000.png
drwxr-xr-x  3 yourname  staff      96 10  1 17:54 samples
```

`grid-0000.png` という画像が、今回生成した画像となる。
![grid-0000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248967/c8525264-aaa3-979e-9c2e-c761b8c37fdb.png)

#### PyTorch が M1 Mac の GPU を使用するかどうか確認する

```zsh
(ldm) $ pip list | grep torch
pytorch-lightning       1.4.2
torch                   1.13.0.dev20220930
torch-fidelity          0.3.0
torchmetrics            0.6.0
torchvision             0.14.0.dev20220930
(ldm) $ python
Python 3.10.4 | packaged by conda-forge | (main, Mar 24 2022, 17:42:03) [Clang 12.0.1 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> torch.device('mps')
device(type='mps')
>>> exit()
(ldm) $
```

`device(type='mps')` と表示されれば、　OK。