自分用のメモ。
- (2023/06/16)追記: Additional NetworksやControlNetなどよく使いそうな拡張機能を最初から入れたものにコードを差し替え。以前のコードは末尾に移動。
- (2023-07-28)追記: 1.5.1用に調整。
事前に用意するもの
- githubのアカウント
- pythonが動く環境を用意することができる程度の知識
- 使いたいモデルとVAE
Modalとは
- クラウド上でpythonのコードを動かすことができるサービス
- GPUが使える(T4, A10G, A100 20Gb, A100 40Gb)
- ローカルで実行し、一部分のメソッドをクラウド上で動かすイメージ
- 毎月$10分の無料クレジットがもらえる従量課金制
- 起動が早い
なお、今なら1月$30分の無料クレジットがもらえます。
料金
従量課金としては安め。GPUだけでなくCPUやmemoryにも料金が発生しますが、アイドル時には料金が発生しないと書いてあるのがとても良い。
一方でShared volume storage(永続ストレージ)が$2/GiB/monthとなっているのが少し気になりますが、登録にクレジットカードを要求しないのでまずは試してみます。
Modalの使い方
アカウントの取得
https://modal.com/signupに行ってGitHubのアカウントでサインアップします。今はまだベータ版で承認のために、どこでModalを知ったのか、Modalで何をしようとしているか教えてくださいといった内容のメールが届くので、適当に書いて返事します。半日ぐらいで登録したよというメールが来たのでこれでアカウントの取得ができました。
込み具合などでメールが届く時間が変わるかもしれません。
Modalの準備
Getting Startedに従ってライブラリとトークンを取得します。適当な作業用ディレクトリに行き、pythonの仮想環境を作り、そこで作業します。
virtualenv venv
venv\Scripts\activate.bat
modalのライブラリをインストール。
pip install modal-client
Modalで使うトークンを取得。
modal token new
トークンはユーザーディレクトリに保存されます。(例:
C\Users\inuneko\.modal.toml
)
Stable Diffusion WebUIのインストール
導入の仕方をわかりやすくまとめてくれている記事を参考にインストールします。Shared volumeにリポジトリをクローン、モデルのアップロード、webuiの実行と手順を3つに分けました。
永続ストレージ(Shared volume)を作成してWebUIをgit clone
以下をcloningsdwebui.pyとしてコピペ、保存します。拡張機能が必要ない場合は該当部分を削除してください。
import os
import subprocess
import modal
persist_dir = "/content"
stub = modal.Stub("stable-diffusion-webui")
volume = modal.SharedVolume().persist("sdwebui-volume")
image = modal.Image.debian_slim().apt_install("git")
@stub.function(image=image,
shared_volumes={persist_dir: volume},
timeout=1800)
def cloning():
os.chdir(persist_dir)
# subprocess.run("git config --global http.postBuffer 200M", shell=True)
subprocess.run("git clone -b v2.5 https://github.com/camenduru/stable-diffusion-webui", shell=True)
# download some extensions
os.chdir(persist_dir + "/stable-diffusion-webui/extensions")
subprocess.run("git clone https://github.com/kohya-ss/sd-webui-additional-networks", shell=True)
subprocess.run("git clone https://github.com/Mikubill/sd-webui-controlnet", shell=True)
subprocess.run("git clone https://github.com/hako-mikan/sd-webui-lora-block-weight", shell=True)
subprocess.run("git clone https://github.com/hako-mikan/sd-webui-regional-prompter", shell=True)
@stub.local_entrypoint()
def main():
cloning.call()
コマンドラインで実行。
modal run cloningsdwebui.py
modal volume ls (volume名) (ディレクトリ)
でshared
volumeの中身を確認できます。
modal volume ls sdwebui-volume /
Directory listing of '/' in 'sdwebui-volume'
┌────────────────────────┬──────┐
│ filename │ type │
├────────────────────────┼──────┤
│ stable-diffusion-webui │ dir │
└────────────────────────┴──────┘
メモ
- /contentディレクトリにshared volumeの/をマウントするイメージ。クラウド上での操作は/content/stable-diffusion-webuiに対して行います。
- webuiのバージョンを固定するためにAUTOMATIC1111の本家リポジトリではなくcamenduruのリポジトリを利用しています。ModalはImage内で記述したpip_installなどを実行した後、その環境を保存することで次回以降はスムーズに起動してくれます。このときrequirements.txtに更新があってバージョンの食い違いがあると毎回入れ直しになります。
- たまにgit
cloneに失敗する。そのときは
git config --global http.postBuffer 200M
してる部分のコメントアウトを解除する。
modelやVAEなどをModalのストレージにアップロード
コマンドラインからmodelやVAEなどをそれぞれアップロードしていきます。大文字小文字は区別されるので注意してください。
modal volume put sdwebui-volume AbyssOrangeMix2_hard_pruned_fp16_with_VAE.safetensors /stable-diffusion-webui/models/Stable-diffusion/AbyssOrangeMix2_hard_pruned_fp16_with_VAE.safetensors
VAE、embeddings、Loraなども同様。送り先のファイル名を省略すると元のファイル名で保存されます。
modal volume put sdwebui-volume C:\path\to\VAE\pr_orangemix.vae.pt /stable-diffusion-webui/models/VAE/
modal volume put sdwebui-volume C:\path\to\embeddings\easynegative.safetensors /stable-diffusion-webui/embeddings/easynegative.safetensors
modal volume put sdwebui-volume C:\path\to\lora\yourfavoritelora.safetensors /stable-diffusion-webui/models/Lora/
ControlNetで使うモデルはextensions/sd-webui-controlnet/models/に入れます。
modal volume put sdwebui-volume C:\path\to\control_v11p_sd15_openpose.pth /stable-diffusion-webui/extensions/sd-webui-controlnet/models/
実行
以下をrunsdwebui.pyとして保存します。
import os
import sys
import subprocess
import shlex
import modal
persist_dir = "/content"
webui_dir = persist_dir + "/stable-diffusion-webui"
stub = modal.Stub("stable-diffusion-webui")
volume = modal.SharedVolume().persist("sdwebui-volume")
image = (
modal.Image.from_dockerhub("python:3.10-slim")
.apt_install("git", "libgl1-mesa-dev", "libglib2.0-0", "libsm6", "libxrender1", "libxext6")
.pip_install(
"torch==2.0.1",
"torchvision==0.15.2",
extra_index_url="https://download.pytorch.org/whl/cu118"
)
.pip_install(
"GitPython==3.1.30",
"Pillow==9.5.0",
"accelerate==0.18.0",
"basicsr==1.4.2",
"blendmodes==2022",
"clean-fid==0.1.35",
"einops==0.4.1",
"fastapi==0.94.0",
"gfpgan==1.3.8",
"gradio==3.32.0",
"httpcore==0.15",
"inflection==0.5.1",
"jsonmerge==1.8.0",
"kornia==0.6.7",
"lark==1.1.2",
"numpy==1.23.5",
"omegaconf==2.2.3",
"open-clip-torch==2.20.0",
"piexif==1.1.3",
"psutil==5.9.5",
"pytorch_lightning==1.9.4",
"realesrgan==0.3.0",
"resize-right==0.0.2",
"safetensors==0.3.1",
"scikit-image==0.20.0",
"timm==0.6.7",
"tomesd==0.1.2",
"torchdiffeq==0.2.3",
"torchsde==0.2.5",
"transformers==4.25.1",
)
.pip_install(
"git+https://github.com/mlfoundations/open_clip.git@bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b",
)
.run_commands(
"python -m pip install --no-deps xformers==0.0.20"
)
.pip_install(
"mediapipe",
"svglib",
"fvcore",
"send2trash~=1.8",
"dynamicprompts[attentiongrabber,magicprompt]~=0.29.0",
)
)
@stub.function(image=image,
shared_volumes={persist_dir: volume},
gpu="T4",
timeout=3600)
def run_webui():
sys.path.append(webui_dir)
os.chdir(webui_dir)
subprocess.run("python launch.py --share --xformers --no-half-vae --opt-sdp-no-mem-attention --opt-channelslast --enable-insecure-extension-access", shell=True)
@stub.local_entrypoint()
def main():
run_webui.call()
コマンドラインで実行。
modal run runsdwebui.py
実行するとhttps://(適当な文字列).gradio.liveというURLが出てくるのでブラウザでアクセスするとStable Diffusion WebUIを操作することができます。
メモ
- Imageの
pip_install
でインストールするパッケージのバージョンはrequirements.txtを見て指定しています。 - テストのためにGPUをT4に、タイムアウトを1時間に設定していますが、動かし方がわかったら(コストと相談して)
gpu="T4"
のところをgpu="A10G"
に変えてより性能の良いGPUにしてみたりtimeout=3600
を変更したり取り外したりして調整してください。
WebUIを終了する
遊び終わったらWebUIを止めます。アイドル時には料金が発生しないといっても、使用するリソースがゼロではないので止め忘れるとcreditsがじわじわと消費されていきます。https://modal.com/appsに行って動いているアプリを選びDelete。このときshared volumeを削除しないように注意してください。
仕様なのか私の環境(Windows cmd.exe)だけなのかわかりませんが、Ctrl+Cは効きません。上記のDeleteをしても入力可能にならないのでCtrl+Breakで抜けます。
画像のダウンロード
download_images.py
import os
import shutil
import modal
persist_dir = "/content"
webui_dir = persist_dir + "/stable-diffusion-webui"
outputs_dir = webui_dir + "/outputs"
zipname = "outputs"
stub = modal.Stub("download-images")
volume = modal.SharedVolume().persist("sdwebui-volume")
image = modal.Image.from_dockerhub("python:3.10-slim")
@stub.function(image=image,
shared_volumes={persist_dir: volume},
timeout=1800)
def get_imagedata():
shutil.make_archive(f'/tmp/{zipname}', format='zip', root_dir=outputs_dir)
f = open(f'/tmp/{zipname}.zip', 'rb')
return f.read()
@stub.function(image=image,
shared_volumes={persist_dir: volume},
timeout=1800)
def clean_outputs():
shutil.rmtree(outputs_dir)
os.mkdir(outputs_dir)
@stub.local_entrypoint()
def main():
import datetime
dt_now = datetime.datetime.now()
timestamp = dt_now.strftime('%Y%m%d%H%M%S')
fileb = get_imagedata.call()
with open(timestamp + f'{zipname}.zip', 'wb') as f:
f.write(fileb)
# outputsディレクトリ以下を削除
clean_outputs.call()
実行。
modal run download_images.py
outputs以下をzipでまとめてローカルに保存し、全削除しています。
感想
まずModalの理解が必要で、使用感としてはエラー無く動かせるようになるまでが少し大変ですが、一度動くものを用意できるとそれ以降とても早くて楽といった印象です。
まだわからないことがいろいろとあるので、何かあったら追記するかもしれません。
クラウド上でGPUを動かすサービスを選ぶ際に、Modalは選択肢として十分にありえます。
追記: 以前のコード
拡張機能を入れないコードも置いておきます。
メモ: [Bug]: Error after startup #1381によるとControlNetはインストール後に(Apply and restart UIではなく)webui自体の再起動が必要になるらしいです。Modalを動かしたままwebuiだけを終了して再起動するのは難しいので、ControlNetを使いたいときはImageを作る段階で依存ライブラリをpip installしておいて一発で起動できるようにしておく必要があります。
cloningsdwebui.py
import os
import subprocess
import modal
persist_dir = "/content"
stub = modal.Stub("stable-diffusion-webui")
volume = modal.SharedVolume().persist("sdwebui-volume")
image = modal.Image.debian_slim().apt_install("git")
@stub.function(image=image,
shared_volumes={persist_dir: volume},
timeout=1800)
def cloning():
os.chdir(persist_dir)
# subprocess.run("git config --global http.postBuffer 200M", shell=True)
subprocess.run("git clone -b v2.3 https://github.com/camenduru/stable-diffusion-webui", shell=True)
@stub.local_entrypoint()
def main():
cloning.call()
runsdwebui.py
import os
import sys
import subprocess
import shlex
import modal
persist_dir = "/content"
webui_dir = persist_dir + "/stable-diffusion-webui"
stub = modal.Stub("stable-diffusion-webui")
volume = modal.SharedVolume().persist("sdwebui-volume")
image = (
modal.Image.from_dockerhub("python:3.10-slim")
.apt_install("git", "libgl1-mesa-dev", "libglib2.0-0", "libsm6", "libxrender1", "libxext6")
.pip_install(
"torch==2.0.1",
"torchvision==0.15.2",
extra_index_url="https://download.pytorch.org/whl/cu118"
)
.pip_install(
"blendmodes==2022",
"transformers==4.25.1",
"accelerate==0.18.0",
"basicsr==1.4.2",
"gfpgan==1.3.8",
"gradio==3.28.1",
"numpy==1.23.5",
"Pillow==9.4.0",
"realesrgan==0.3.0",
# "torch",
"omegaconf==2.2.3",
"pytorch_lightning==1.9.4",
"scikit-image==0.19.2",
"fonts",
"font-roboto",
"timm==0.6.7",
"piexif==1.1.3",
"einops==0.4.1",
"jsonmerge==1.8.0",
"clean-fid==0.1.29",
"resize-right==0.0.2",
"torchdiffeq==0.2.3",
"kornia==0.6.7",
"lark==1.1.2",
"inflection==0.5.1",
"GitPython==3.1.30",
"torchsde==0.2.5",
"safetensors==0.3.1",
"httpcore<=0.15",
"fastapi==0.94.0",
"clip",
"test-tube",
"diffusers",
"invisible-watermark",
"gdown",
"huggingface_hub",
"colorama",
)
.pip_install(
"git+https://github.com/mlfoundations/open_clip.git@bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b",
)
)
@stub.function(image=image,
shared_volumes={persist_dir: volume},
gpu="T4",
timeout=3600)
def run_webui():
sys.path.append(webui_dir)
os.chdir(webui_dir)
from launch import start, prepare_environment
prepare_environment()
sys.argv = shlex.split("--a --no-half-vae --opt-sdp-no-mem-attention --opt-channelslast --share --enable-insecure-extension-access")
start()
@stub.local_entrypoint()
def main():
run_webui.call()
modal volume ls (volume名) (ディレクトリ)がわかりません
返信削除