dockerでStableDiffusionを動かしてみる

# docker で StableDiffusion を動かしてみる
## 今回の目標
* StableDiffusion (ver.1.4)を動かす。
* 【注意】pyenvの知識がある方はpyenvを利用し、dockerを使わないことを推奨します。理由として、dockerを使ってしまうとM2 macのgpuにアクセスができない(と思われる)ので一枚のイラストを生成するのにとんでもない時間がかかってしまうためです。本記事のrequirements.txtの内容をPython 3.8.10に入れれば、スペックにもよりますが画像一枚を1分もかからず生成できるようになります。
*　dockerでM2 macのGPUを使う方法を知っている方はご教示いただけるととても助かります。

## 環境
* macbookpro M2 (CPU10コア、32GBメモリ、GPUは使用しない)
* ※dockerで環境を作って動かすので、CPUとメモリ以外は関係ない
* dev_containerで構築した仮想環境上で動かす
* dockerでも利用可能

## 目次
* とりあえず動かしたい人向け(gitリポジトリあり)
* 自分で環境を作る人用のまとめ
* 参考資料

## 手順
1. docker仮想環境を構築する (リポジトリをclone)
2. StableDiffusionをインストールする (リポジトリをclone)
3. dockerを立ち上げ、latent-diffusion-0.0.1 をpipでインストールする
4. 好きなモデルをダウンロード&配置する
5. 完成！ 

## とりあえず動かしたい人向け(gitリポジトリあり)
(1) docker仮想環境を構築する (リポジトリをclone)

* SSH：git@github.com:T08Y02/dev_con_oserou.git 
* 上のリポジトリのdev_container_stablediffusion ブランチ をcloneする

* リポジトリのリンク ：<br> https://github.com/T08Y02/dev_con_oserou/tree/dev_container_stablediffusion

現時点での構造：
```
.
├── .devcontainer
│   └── devcontainer.json
│
├── .gitignore
├── docker-compose.yml
├── DockerFile
└── requirements.txt
└── test.py
```

(2) StableDiffusionをインストールする (リポジトリをclone)
* 新しくstable-diffusionディレクトリを作り、その中にリポジトリをcloneする。./stable-diffusion　内にassetsなどのディレクトリを設置する。
* SSH：git@github.com:T08Y02/dev_con_oserou.git 
* 上のリポジトリのdev_container_stablediffusion ブランチ をcloneする

* リポジトリのリンク ：<br> https://github.com/T08Y02/stablediffusion

現時点での構造：
```
.
├── .devcontainer
│   └── devcontainer.json
├── stable-diffusion
│   ├── assets
│   ├── ...
│   ├── scripts
│   ├── .gitignore
│   ├── ...
│   ├── requirements.txt
│   └── ...
├── .gitignore
├── docker-compose.yml
├── DockerFile
└── requirements.txt
└── test.py
```

(3) dockerを立ち上げ、latent-diffusion-0.0.1 をpipでインストールする
* dev_containerであればリモート接続を行う。
* dockerであればdocker-compose build と up を行い、exec で "web" というアプリに入る。
* cd　を ./stable-diffusion ディレクトリにした状態で、pip install -e .　を行ってlatent-diffusion-0.0.1をダウンロードする。
現時点での構造(docker環境上)：
```
.
├── stablediffusion
│　　　　　　└── requirements.txt (build時にコピーした結果生成されたファイル)
│── ...
└── workspace
    ├── .devcontainer
    │   └── devcontainer.json
    ├── stable-diffusion
    │   ├── assets
    │   ├── ...
    │   ├── scripts
    │   ├── .gitignore
    │   ├── ...
    │   ├── requirements.txt
    │   └── ...
    ├── .gitignore
    ├── docker-compose.yml
    ├── DockerFile
    └── requirements.txt
    └── test.py
```
(4) 好きなモデルをダウンロード&配置する
* ckptファイル形式でモデルをダウンロードしてきて、workspaceのstable-diffusionディレクトリ内部の
<br> 「stable-diffusion/models/ldm」　
<br> に、「stable-diffusion-v1」というディレクトリを作り、そのディレクトリの内部に「models.ckpt」という名前で配置する。
* モデルの入手先の例：
* ① stable diffusion 1.4本家のモデル ： https://huggingface.co/CompVis/stable-diffusion-v-1-4-original <br> こちらは、「sd-v1-4.ckpt」というモデルをダウンロードして配置する。
* ② Anything v3.2モデル ： https://huggingface.co/cag/anything-v3-1/tree/main <br> こちらは、「anything-v3-2.ckpt」というモデルをダウンロードして配置する。クオリティ高め。

現時点での構造(docker環境上)：
```
.
│── ...
└── workspace
    ├── stable-diffusion
    │   ├── models
    │   │  ├── first_stage_models
    │   │  └── ldm
    │   │       ├── stable-diffusion-v1
    │   │       │   └── models.ckpt
    │   │       └── ...
    │   └── ...
    ├── docker-compose.yml
    └── ...
```

(5) 完成！
* cd　を ./stable-diffusion ディレクトリにした状態で/scripts/txt2img.pyを実行する。
* なぜかmacbookのgpuにアクセスできずcpuのみで解決しているため、画像一枚の生成に20分ほどかかる。また、最初にtxt2imgを動かす際は極めて実行時間がかかった(自分の環境だと4時間ほど)ので注意。
*　入力例：
```
python scripts/txt2img.py --prompt "masterpiece, (whale), solo, jumping whale, white whale, splash, sea, ocean, high quality, wide angle" --negative_prompt "nsfw, girl, flat color, flat shading, retro style, ship, poor quality, low res, cropped, signature, watermark, username, artist name, text" --n_samples 1 --n_iter 1 --plms --seed 52
```
*　出力例
*　画像は./stable-diffusion/outputs　内に出力されます。
![grid-0014.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3322437/dad23aee-b77c-0d2e-e9a6-e3260412d4cd.png)


## 自分で環境を作る人用のまとめ
* dockerはpython 3.8 のイメージを使うとよい。
* その後、基本は以下のサイトを参考に進める。pyenv周りの環境構築はdockerを使うなら飛ばしてよく、stablediffusionをcloneしてくるところから始めるとよい。
* https://gigazine.net/news/20220902-run-stable-diffusion-m1-macs-gpu/
* バージョンの競合などがあるので、stable-diffusionディレクトリ内部のrequirements.txtを以下のように書き換えることを推奨する。
```
numpy==1.23.1

albumentations==0.4.6
diffusers
opencv-python==4.6.0.66
pudb==2019.2
invisible-watermark>=0.1.0
imageio==2.9.0
imageio-ffmpeg==0.4.2
pytorch-lightning>=1.7.7
omegaconf==2.1.1
test-tube>=0.7.5
streamlit>=0.73.1
einops==0.3.0
torch-fidelity==0.3.0
#transformers==4.27.4
transformers==4.19.2
torchmetrics>=0.6.0
kornia>=0.6
lightning-utilities==0.4.0

--pre torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/nightly/cpu


-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が見つからないというエラーが出るので、stable-diffusionディレクトリに移動後、pip install -e . でlatent-diffusion-0.0.1をインストールする必要がある。
* このままだとtokenizers用のwheelが見つからないというエラーが出るので、stable-diffusionディレクトリに移動後、pip install --upgrade transformers でtransformers 4.27.2をインストールする必要がある。dockerイメージを作成する際にtransformers 4.27.2を入れようとするとエラーが出るため、docker環境をbuildしてからtransformersをアップグレードする必要がある。
* そのまま動かそうとするとfloat64関係のエラーが出ることがある。その場合はtxt2image.pyのmain関数parser.add_argumentのprecision部分のdefaultを"autocast"から"full"にするとよい。
* nsfw規制を外したいときはtxt2image.pyのmain関数内の「check_safety関数」を呼び出している部分を無効化すればよい。 ~~ローカルでSDを動かしたい理由ってこれですよね~~  
* Anything v3を使う場合、モデルに「state_dict」keyが存在しないためエラーが出ることがある。その場合は、「state_dict」keyがない時はsdにpl_sdをそのまま代入してやると動く。

## 参考資料
* https://gigazine.net/news/20220902-run-stable-diffusion-m1-macs-gpu/
* https://qiita.com/sanato_kumara/items/d3450b5b2f80912aca2c