EC2のGPUインスタンス上にStable Diffusion Web UIをミニマムに立てる

EC2のGPUインスタンス上にStable Diffusion Web UIを気軽に立てる手順についてまとめます。手元にGPU環境がない場合や、個人・チームでちょっと試したい時に利用できるかと思います。

＊追記：Slackからインスタンス起動＆アクティビティ監視による自動停止の手順をまとめました。
https://qiita.com/mikito/items/1050612207adb1f14392

## 今回の方針
- 素のEC2だけの限りなくシンプルな構成で、なるべくコストを抑える
- 環境構築したインスタンスは使う時にStartさせ、使い終わったら停止させておくスタイル（Elastic IPも利用しないため起動の度にIPが変わります）
- 東京リージョンではやや料金が高めなので、米国西部　(オレゴン)　リージョンに構築
- [Dockerで立ち上げる](https://github.com/AbdBarho/stable-diffusion-webui-docker)のも簡単だが、モデルの管理や最新機能の追従などが少し面倒に感じたため、[本家(AUTOMATIC1111)](https://github.com/AUTOMATIC1111/stable-diffusion-webui)を直接立ち上げる（対象AMIではDockerでなくても簡単）
- ControlNetやNegative PromptのEmbeddingsなど、多少の基本設定までする

## コスト

今回は、インスタンスタイプとしてg4dn.xlargeを利用します。g5世代含めて、性能はそこまで高くないですが使いやすい価格帯です。Stable Diffusion Web UIを動かして画像生成するぐらいであれば普通に動きます。

![スクリーンショット 2023-04-29 10.37.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97161/28486a3a-fd14-55e3-239d-5ec28d43eece.png)



g4dn.xlargeの一時間当たりの単価は以下です。
- オレゴン: 0.526 USD
- （参考）東京: 0.71 USD


また、学習済みモデルなどが結構大きく、EBSも余裕を持たせるとしたら60〜80GBぐらいが必要です。こちらはインスタンスを起動しておかなくても発生する[料金](https://aws.amazon.com/jp/ebs/pricing/)です。

- 汎用 SSD (gp3) - ストレージ:	0.08USD/GB 月


以下のように1ヶ月当たりの稼働時間を見積もります。

- デイリー2時間起動 * 20日 = 40時間


|  | 利用量 | 単価  | 月コスト |
|---|---|---|---|
| EC2(g4dn.xlarge) | 40hour | $0.526  | $21.04 |
| EBS(gp3) | 80GB | $0.08 | $6.4|

となり、合計$27.44ほどで、その他データ転送料などもあると思いますが月40時間稼働で、だいたい3,740円といったところです（2023/4/29現在）。

ちょっと試したいぐらいであれば新規PCを整えるよりは安いかと思います。また、環境は簡単にセットアップできるので、数日使ったら気軽に破棄してしまうのもありでしょう。（もちろん起動しっぱなしにするとインスタンス代金だけで$378.72ほどかかってしまうので注意です。）

なお、Google ColabなどでもStable Diffusion Web UIを立ち上げられますが、2023/4末に無料枠では禁止されてしまいました。また、Colab Proは月額￥1,179で、割り当てられているコンピューティングユニットから試算して大体50時間ぐらいWeb UIを起動できそうした。こちらの方が時間単価としては安めですが、そもそもGoogle Colab自体がそのような用途向けでないのと、モデルデータなどの環境管理がやりにくいデメリットがあります。

## EC2インスタンス構築
EC2上でAMIからGPUインスタンスを立ち上げます。上記の通り、料金の観点から米国西部 (オレゴン)　us-west-2リージョンで構築していきます。

### セキュリティグループ作成
EC2インスタンスのセットアップの前に、専用のセキュリティグループを作成しておきます。

![スクリーンショット_2023-04-29_12_32_30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97161/c830bc9b-3c6b-d1c8-5449-1e662d318085.png)

インバウンドルールに以下のようにSSHとStable Diffusion Web UIのデフォルトポートである7860を追加します（今回はリバプロなどせず直接アクセスしちゃいます）。

![スクリーンショット 2023-04-29 12.25.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97161/1b65b798-bd8f-5b62-5372-abd739c8a940.png)


### インスタンスの立ち上げ

今回は、以下のAMIを利用します。NVIDIAのDriverなどが既にセットアップされており、最も環境設定が簡単でした。

> Deep Learning AMI GPU PyTorch 2.0.0 (Ubuntu 20.04) 20230401

![スクリーンショット 2023-04-29 13.54.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97161/1c29dc26-fdca-5010-37a8-814a5f1d23e7.png)

インスタンスの起動ウィザードを立ち上げ、適当な名前をつけます。

![スクリーンショット 2023-04-29 13.53.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97161/321ecc7b-e027-f340-afbe-8314c7f09f03.png)

インスタンスタイプにg4dn.xlargeを選択します。

![スクリーンショット 2023-04-29 13.53.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97161/c28f4be1-1c48-db75-214c-884a6fd4d7c1.png)

ログインのためのキーペアは適当に作成するか、既存の適切なものを選択してください。

![スクリーンショット 2023-04-29 13.55.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97161/457a81f8-6321-486d-1407-5dbc7e371292.png)


ネットワーク設定では、先ほど作成したセキュリティグループを選択します。
![スクリーンショット_2023-04-29_12_34_12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97161/11a2f1e8-174c-dfb7-d4cc-3cd2e6c5da77.png)

EBSボリュームをgp3の80GBに設定します。この容量は複数のモデルを後程DLするとして、少し余裕を持たせています。

![スクリーンショット 2023-04-29 12.35.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97161/21e01402-9541-7a82-961e-3f6f38bceea5.png)

以上の設定でインスタンスを立ち上げます。

## 環境の基本セットアップとStable Diffusion Web UIのインストール

インスタンスが立ち上がったら、コンソールログインします。

まずは、以下を実行します。

```bash
sudo apt update
sudo apt -y install python3-venv
```

また、モデルのダウンロード用にaria2とGit LFSも以下も入れておきます（wgetでも良いですが）。
```bash
sudo apt -y install aria2
sudo apt -y install git-lfs
```

Stable Diffusion Web UIのリポジトリをクローンします。
```bash
git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git
```

クローンしたリポジトリ以下に移動します。これ以降の作業は全て/home/ubuntu/stable-diffusion-webui以下で行います。
```bash
cd stable-diffusion-webui/
```

必要なモデルをdata/StableDiffusioディレクトリ以下に配置します。この辺りはお好きなモデルを選択してください。今回は[anything-v4.5-pruned.ckptと対応するVAE](https://huggingface.co/andite/anything-v4.0)を導入します。

```bash
aria2c -c -x 16 -s 16 -k 1M https://huggingface.co/ckpt/anything-v4.0/resolve/main/anything-v4.5-pruned.ckpt -d models/Stable-diffusion -o anything-v4.5-pruned.ckpt
aria2c -c -x 16 -s 16 -k 1M https://huggingface.co/ckpt/anything-v4.0/resolve/main/anything-v4.0.vae.pt -d models/Stable-diffusion -o anything-v4.5-pruned.vae.pt
```

以下コマンドを実行しStable Diffusion Web UIを立ち上げ、起動確認を行います（themaは好みです）。

```bash
 ./webui.sh --listen --xformers --enable-insecure-extension-access --theme dark --gradio-queue
```

初回、自動的に必要なセットアップなどが行われ時間がかかりますが、しばらくすると以下のようにログが表示され起動します。

```
...
Loading VAE weights found near the checkpoint: /home/ubuntu/stable-diffusion-webui/models/Stable-diffusion/anything-v4.5-pruned.vae.pt
Applying xformers cross attention optimization.
Textual inversion embeddings loaded(0): 
Model loaded in 50.0s (calculate hash: 34.8s, load weights from disk: 2.5s, create model: 1.7s, apply weights to model: 1.2s, apply half(): 0.6s, load VAE: 8.5s, move model to device: 0.6s).
Running on local URL:  http://0.0.0.0:7860

To create a public link, set `share=True` in `launch()`.
Startup time: 55.6s (import torch: 0.9s, import gradio: 1.6s, import ldm: 0.5s, other imports: 1.5s, setup codeformer: 0.2s, load scripts: 0.4s, load SD checkpoint: 50.1s, create ui: 0.4s).

```

この状態で、EC2インスタンスのパブリックアドレスの7860ポートにアクセスしてみましょう。セキュリティグループが正しく設定されていれば、Web UIが起動しアクセスできるはずです。
![スクリーンショット 2023-04-29 14.36.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97161/9dbd859c-6d8c-49fd-75b7-4a5fac6abd36.png)

動作確認のため何かプロンプトを入力し、画像生成を試します。

プロンプトの例：
```
masterpiece, best quality, 1girl, white hair, medium hair, cat ears, closed eyes, looking at viewer, :3, cute, scarf, jacket, outdoors, streets
```

![スクリーンショット 2023-04-29 14.41.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97161/6e9cf96f-b98f-b769-666a-5fc7cea3a6f4.png)
（もしかしたら、一番最初の生成に時間がかかるかも？デフォルトのパラメータであれば基本的に6〜4secぐらいで処理が完了します。）

## Stable Diffusion Web UIの基本的なセットアップ
次にStable Diffusion Web UI自体の基本的なセットアップをしていきます。extensionはUIからもinstallできますが、追加で必要なモデルがあるのと、UIをポチポチするのが面倒なのでコンソールからInstallしちゃいます。

### Quick Setting List

VAEの選択が常に見えていた方がよいので、ちょっとUIの設定を変えます。Settingタブ→User Interface→Quicksettings listの項目にsd_vaeを追記します。

```
sd_model_checkpoint, sd_vae
```

その後、Settingタブ上部のApply Settingをクリック後、Reload UIを実行します。

すると、以下のようにVAEの選択を常に表示できるようになります。学習済みCheckpointと同時にVAEなどが配布されている場合などに、こちらをすぐ確認できるようにしておくと便利です。
![スクリーンショット 2023-04-29 14.46.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97161/c3a6cf7c-8f7d-3343-bed8-560bbf8a7f40.png)

#### Negative Prompt Embeddings
次に一度./webui.shを落とし、コンソールから必要なものを入れていきます。

まずはネガティブプロンプト用のEmbeddingsを用意します。ネガティブプロンプトはいい感じの出力を行うために重要です。

今回は複数のEmbeddingsがまとめられている以下を利用します。EasyNegativeも入っています。
https://huggingface.co/embed/negative

以下コマンドで配置します（前の手順でGit LFSをInstallしておいてください）。
```
git clone https://huggingface.co/embed/negative embeddings/negative
```


### ControlNetの導入
生成画像の構図やキャラのポーズなどを制御するためのControlNetをインストールします。extensionとして以下コマンドで配置します。

```
git clone https://github.com/Mikubill/sd-webui-controlnet extensions/sd-webui-controlnet
```

次に、ControlNetに必要な追加モデルをダウンロードします。

なお、こちらは以下のリポジトリのステップを参考にしています。
https://github.com/camenduru/stable-diffusion-webui-colab
容量やメモリの問題から、半精度のものをDLしているようです。

数が多いので、以下を一度スクリプトに保存してから実行してください。モデルはextensions/sd-webui-controlnet/models以下に配置されます。また、全部DLしなくても、使いたいモデルだけDLするのでもOKです。

```download-models.sh
base_url="https://huggingface.co/ckpt/ControlNet-v1-1"
dest="extensions/sd-webui-controlnet/models"
aria2_opts="--console-log-level=error -c -x 16 -s 16 -k 1M"

files=(
  resolve/main/control_v11e_sd15_ip2p_fp16.safetensors 
  resolve/main/control_v11e_sd15_shuffle_fp16.safetensors 
  resolve/main/control_v11p_sd15_canny_fp16.safetensors 
  resolve/main/control_v11f1p_sd15_depth_fp16.safetensors 
  resolve/main/control_v11p_sd15_inpaint_fp16.safetensors 
  resolve/main/control_v11p_sd15_lineart_fp16.safetensors 
  resolve/main/control_v11p_sd15_mlsd_fp16.safetensors 
  resolve/main/control_v11p_sd15_normalbae_fp16.safetensors 
  resolve/main/control_v11p_sd15_openpose_fp16.safetensors 
  resolve/main/control_v11p_sd15_scribble_fp16.safetensors 
  resolve/main/control_v11p_sd15_seg_fp16.safetensors 
  resolve/main/control_v11p_sd15_softedge_fp16.safetensors 
  resolve/main/control_v11p_sd15s2_lineart_anime_fp16.safetensors 
  resolve/main/control_v11f1e_sd15_tile_fp16.safetensors 
  raw/main/control_v11e_sd15_ip2p_fp16.yaml 
  raw/main/control_v11e_sd15_shuffle_fp16.yaml 
  raw/main/control_v11p_sd15_canny_fp16.yaml 
  raw/main/control_v11f1p_sd15_depth_fp16.yaml 
  raw/main/control_v11p_sd15_inpaint_fp16.yaml 
  raw/main/control_v11p_sd15_lineart_fp16.yaml 
  raw/main/control_v11p_sd15_mlsd_fp16.yaml 
  raw/main/control_v11p_sd15_normalbae_fp16.yaml 
  raw/main/control_v11p_sd15_openpose_fp16.yaml 
  raw/main/control_v11p_sd15_scribble_fp16.yaml 
  raw/main/control_v11p_sd15_seg_fp16.yaml 
  raw/main/control_v11p_sd15_softedge_fp16.yaml 
  raw/main/control_v11p_sd15s2_lineart_anime_fp16.yaml 
  raw/main/control_v11f1e_sd15_tile_fp16.yaml 
  resolve/main/t2iadapter_style_sd14v1.pth 
  resolve/main/t2iadapter_sketch_sd14v1.pth 
  resolve/main/t2iadapter_seg_sd14v1.pth 
  resolve/main/t2iadapter_openpose_sd14v1.pth 
  resolve/main/t2iadapter_keypose_sd14v1.pth 
  resolve/main/t2iadapter_depth_sd14v1.pth 
  resolve/main/t2iadapter_color_sd14v1.pth 
  resolve/main/t2iadapter_canny_sd14v1.pth 
  resolve/main/t2iadapter_canny_sd15v2.pth 
  resolve/main/t2iadapter_depth_sd15v2.pth 
  resolve/main/t2iadapter_sketch_sd15v2.pth 
  resolve/main/t2iadapter_zoedepth_sd15v1.pth 
)

for file in "${files[@]}"; do
  aria2c $aria2_opts "$base_url/$file" -d $dest -o "$(basename $file)"
done
```

```bash
 bash download-models.sh 
```

### その他Extensionの導入

まだ、自分の中でもextensionを把握しきれていないですが、一旦以下を入れておきます。この辺りは、利用用途次第＋後程UIからInstallでもOKです。

```
git clone https://github.com/camenduru/sd-civitai-browser extensions/sd-civitai-browser
git clone https://github.com/fkunn1326/openpose-editor extensions/openpose-editor
git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui-rembg extensions/stable-diffusion-webui-rembg
git clone https://github.com/ashen-sensored/stable-diffusion-webui-two-shot.git extensions/stable-diffusion-webui-two-shot
# プロンプトからLoRaを指定できるようになっているので不要かも？
git clone https://github.com/camenduru/sd-webui-additional-networks extensions/sd-webui-additional-networks
```

## 自動起動設定
次にインスタンスが立ち上がった時にStable Diffusion Web UIが自動的に起動するようにします。

以下のファイルを作成します。
```bash
sudo vim /etc/systemd/system/stable-diffusion-web-ui.service
```

```stable-diffusion-web-ui.service
[Unit]
Description=Stable Diffusion Web UI
After=network.target

[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/stable-diffusion-webui
ExecStart=/home/ubuntu/stable-diffusion-webui/webui.sh --listen --xformers --enable-insecure-extension-access --theme dark --gradio-queue
Restart=always

[Install]
WantedBy=multi-user.target                   
```

作成ができたら、以下コマンドで自動起動設定を有効化します。
```
sudo systemctl daemon-reload
sudo systemctl enable stable-diffusion-web-ui.service
sudo systemctl start stable-diffusion-web-ui.service
```

なお、設定の有効化後、以下のコマンドで状態やログの確認ができます。
```
systemctl status stable-diffusion-web-ui.service

journalctl -u stable-diffusion-web-ui.service -f
```

また、メンテナンス作業として停止やリスタートしたい場合は以下です。
```
sudo systemctl stop stable-diffusion-web-ui.service
sudo systemctl restart stable-diffusion-web-ui.service
```

これで一度インスタンスを停止させて、再度スタートさせてみましょう。自動的にWeb UIが起動すれば成功です。インスタンス起動後、モデルロードに少々時間がかかるため、アプリケーションが立ち上げるまでしばらく待ちましょう。（IPアドレスも変わります。）


## 動作確認
基本的なセットアップができたので、簡単に機能と出力を確認してみます。以下のプロンプトで試します。

Prompt
```
masterpiece, best quality, 1girl, white hair, medium hair, cat ears, closed eyes, looking at viewer, :3, cute, scarf, jacket, outdoors, streets
```

Negative prompt
```
EasyNegative
```
Sampling method
```
DPM++ 2M Karras
```

出力結果がこちらです。
![スクリーンショット 2023-04-29 17.02.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97161/ee09413d-af06-510f-71b2-8c0cf225a409.png)
```
masterpiece, best quality, 1girl, white hair, medium hair, cat ears, closed eyes, looking at viewer, :3, cute, scarf, jacket, outdoors, streets
Negative prompt: EasyNegative
Steps: 20, Sampler: DPM++ 2M Karras, CFG scale: 7, Seed: 4157011332, Size: 512x512, Model hash: e4b17ce185, Model: anything-v4.5-pruned

Used embeddings: EasyNegative [119b]

Time taken: 3.50sTorch active/reserved: 2616/3226 MiB, Sys VRAM: 4265/15102 MiB (28.24%)
```
embeddingsも効いており、大体3.5秒で出力できています。

また、ControlNetも試してみます。先ほどと同じプロンプトと設定で、ControlNetのcannyを適用し、構図を指定する適当な画像をInputします。

![スクリーンショット 2023-04-29 17.07.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97161/8b0733c1-57e2-74e6-a75f-b0ffe679f9fe.png)
```
masterpiece, best quality, 1girl, white hair, medium hair, cat ears, closed eyes, looking at viewer, :3, cute, scarf, jacket, outdoors, streets
Negative prompt: EasyNegative
Steps: 20, Sampler: DPM++ 2M Karras, CFG scale: 7, Seed: 4157011332, Size: 512x512, Model hash: e4b17ce185, Model: anything-v4.5-pruned, ControlNet Enabled: True, ControlNet Preprocessor: canny, ControlNet Model: control_v11p_sd15_canny_fp16 [b18e0966], ControlNet Weight: 1, ControlNet Starting Step: 0, ControlNet Ending Step: 1, ControlNet Resize Mode: Crop and Resize, ControlNet Pixel Perfect: False, ControlNet Control Mode: Balanced, ControlNet Preprocessor Parameters: "(512, 100, 200)"

Used embeddings: EasyNegative [119b]

Time taken: 13.70sTorch active/reserved: 7470/7866 MiB, Sys VRAM: 8905/15102 MiB (58.97%)
```
こちらも、しっかり動いていることを確認できました。（上記例はプロンプトとControlNetの入力画像がマッチしていないので猫耳などの部分がうまくいってないですが...）


## セキュリティについて

今回の環境は主にStable Diffusion Web UIを簡単に検証する目的の意味が強いため、セキュアな環境ではありません。外部の第三者がアクセス可能な状態ですし、SSLによる通信もしてません。立ち上げっぱなしにしたり、業務で重要なデータのやりとりなどをしないようにしてください。

もし、定常的に利用するような環境にしたい場合は、VPNなどの環境構築を検討した方よいと思います。

https://catalog.us-east-1.prod.workshops.aws/workshops/be2b90c2-06a1-4ae6-84b3-c705049d2b6f/ja-JP

## 他参考

その他のインスタンスタイプを利用する場合は以下を参照してください。

https://aws.amazon.com/jp/ec2/instance-types/g4/

https://aws.amazon.com/jp/ec2/instance-types/g5/