Google ColabでStable-Diffusion-webUIが使いづらくなってしまっていよいよPCを買わねばなるまいか・・・と思っていたところ、Modalというのがいいらしいよ・・・という噂をきいて試してみました。
しーぴーさん:
【無料】GPUがなくてもStable Diffusionで遊びたい!
https://zenn.dev/cp20/articles/stable-diffusion-webui-with-modal
箱庭のボブさん:
Modalを使ってStable Diffusion web UI +ControlNetで遊ぶ
https://note.com/light_impala137/n/n99b014389a69
お二人の記事を参考に、使えるようになりました。ありがとうございます!
しーぴーさん、箱庭のボブさんのコードを踏襲しつつ、一度ダウンロードしたモデルはダウンロードしないように変更を加えましたので取り急ぎご紹介です。
より良い方法があればぜひ教えてください〜
以下のコードを[stable-diffusion-webui_controlNet-aria2.py]として保存します。
from colorama import Fore
from pathlib import Path
import modal
import shutil
import subprocess
import sys
import shlex
import os
# modal系の変数の定義
stub = modal.Stub("stable-diffusion-webui")
volume_main = modal.SharedVolume().persist("stable-diffusion-webui-main")
# パスの定義
webui_dir = "/content/stable-diffusion-webui"
webui_model_dir = webui_dir + "/models/Stable-diffusion/"
# ControlNetのパスを定義
webui_controlNet_dir = webui_dir + "/extensions/sd-webui-controlnet"
webui_controlNet_model_dir = webui_dir + "/extensions/sd-webui-controlnet/models/"
# モデルのID
model_ids = [
{
"repo_id": "hakurei/waifu-diffusion-v1-4",
"model_path": "wd-1-4-anime_e1.ckpt",
"config_file_path": "wd-1-4-anime_e1.yaml",
},
]
@stub.function(
image=modal.Image.from_dockerhub("python:3.10-slim")
.apt_install(
"git", "libgl1-mesa-dev", "libglib2.0-0", "libsm6", "libxrender1", "libxext6", "gcc", "libcairo2-dev", "aria2",
)
.run_commands(
"pip install -U -e git+https://github.com/CompVis/taming-transformers.git@master#egg=taming-transformers"
)
.pip_install(
"blendmodes==2022",
"transformers==4.25.1",
"accelerate==0.12.0",
"basicsr==1.4.2",
"gfpgan==1.3.8",
"gradio==3.16.2",
"numpy==1.23.3",
"Pillow==9.4.0",
"realesrgan==0.3.0",
"torch",
"omegaconf==2.2.3",
"pytorch_lightning==1.7.6",
"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.27",
"torchsde==0.2.5",
"safetensors==0.2.7",
"httpcore<=0.15",
"tensorboard==2.9.1",
"taming-transformers==0.0.1",
"clip",
"xformers",
"test-tube",
"diffusers",
"invisible-watermark",
"pyngrok",
"xformers==0.0.16rc425",
"gdown",
"huggingface_hub",
"colorama",
)
.pip_install("git+https://github.com/mlfoundations/open_clip.git@bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b"),
secret=modal.Secret.from_name("my-huggingface-secret"),
shared_volumes={webui_dir: volume_main},
#ここでGPUを指定
gpu="a10g",
# gpu=modal.gpu.A10G(count=2),
# gpu=modal.gpu.T4(count=2),
timeout=6000,
)
async def run_stable_diffusion_webui():
print(Fore.CYAN + "\n---------- セットアップ開始 ----------\n")
webui_dir_path = Path(webui_model_dir)
if not webui_dir_path.exists():
subprocess.run(f"git clone -b v2.1 https://github.com/camenduru/stable-diffusion-webui {webui_dir}", shell=True)
#controlNetをgit clone
controlNet_dir_path = Path(webui_controlNet_dir)
if not controlNet_dir_path.exists():
subprocess.run(f"git clone https://github.com/Mikubill/sd-webui-controlnet {webui_controlNet_dir}", shell=True)
def download_hf_file(repo_id, filename):
from huggingface_hub import hf_hub_download
download_dir = hf_hub_download(repo_id=repo_id, filename=filename)
return download_dir
for model_id in model_ids:
print(Fore.GREEN + model_id["repo_id"] + "のセットアップを開始します...")
if not Path(webui_model_dir + model_id["model_path"]).exists():
model_downloaded_dir = download_hf_file(
model_id["repo_id"],
model_id["model_path"],
)
shutil.copy(model_downloaded_dir, webui_model_dir + os.path.basename(model_id["model_path"]))
if "config_file_path" not in model_id:
continue
if not Path(webui_model_dir + model_id["config_file_path"]).exists():
config_downloaded_dir = download_hf_file(
model_id["repo_id"], model_id["config_file_path"]
)
shutil.copy(config_downloaded_dir, webui_model_dir + os.path.basename(model_id["config_file_path"]))
print(Fore.GREEN + model_id["repo_id"] + "のセットアップが完了しました!")
controlNet_files = [
"control_v11e_sd15_ip2p_fp16.safetensors",
"control_v11e_sd15_shuffle_fp16.safetensors",
"control_v11p_sd15_canny_fp16.safetensors",
"control_v11f1p_sd15_depth_fp16.safetensors",
"control_v11p_sd15_inpaint_fp16.safetensors",
"control_v11p_sd15_lineart_fp16.safetensors",
"control_v11p_sd15_mlsd_fp16.safetensors",
"control_v11p_sd15_normalbae_fp16.safetensors",
"control_v11p_sd15_openpose_fp16.safetensors",
"control_v11p_sd15_scribble_fp16.safetensors",
"control_v11p_sd15_seg_fp16.safetensors",
"control_v11p_sd15_softedge_fp16.safetensors",
"control_v11p_sd15s2_lineart_anime_fp16.safetensors",
"control_v11f1e_sd15_tile_fp16.safetensors",
"control_v11e_sd15_ip2p_fp16.yaml",
"control_v11e_sd15_shuffle_fp16.yaml",
"control_v11p_sd15_canny_fp16.yaml",
"control_v11f1p_sd15_depth_fp16.yaml",
"control_v11p_sd15_inpaint_fp16.yaml",
"control_v11p_sd15_lineart_fp16.yaml",
"control_v11p_sd15_mlsd_fp16.yaml",
"control_v11p_sd15_normalbae_fp16.yaml",
"control_v11p_sd15_openpose_fp16.yaml",
"control_v11p_sd15_scribble_fp16.yaml",
"control_v11p_sd15_seg_fp16.yaml",
"control_v11p_sd15_softedge_fp16.yaml",
"control_v11p_sd15s2_lineart_anime_fp16.yaml",
"control_v11f1e_sd15_tile_fp16.yaml",
"t2iadapter_style_sd14v1.pth",
"t2iadapter_sketch_sd14v1.pth",
"t2iadapter_seg_sd14v1.pth",
"t2iadapter_openpose_sd14v1.pth",
"t2iadapter_keypose_sd14v1.pth",
"t2iadapter_depth_sd14v1.pth",
"t2iadapter_color_sd14v1.pth",
"t2iadapter_canny_sd14v1.pth",
"t2iadapter_canny_sd15v2.pth",
"t2iadapter_depth_sd15v2.pth",
"t2iadapter_sketch_sd15v2.pth",
"t2iadapter_zoedepth_sd15v1.pth"
]
for file_name in controlNet_files:
if not Path(webui_controlNet_model_dir + file_name).exists():
if file_name.endswith('.pth') or file_name.endswith('.safetensors'):
controlNet_url = "https://huggingface.co/ckpt/ControlNet-v1-1/resolve/main/" + file_name
elif file_name.endswith('.yaml'):
controlNet_url = "https://huggingface.co/ckpt/ControlNet-v1-1/raw/main/" + file_name
subprocess.run(["aria2c", "--console-log-level=error", "-c", "-x", "16", "-s", "16", "-k", "1M", controlNet_url, "-d", webui_controlNet_model_dir, "-o", file_name])
if not Path(webui_model_dir + "BloodNightOrangeMix.vae.pt").exists():
vae_url = "https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/VAEs/orangemix.vae.pt"
subprocess.run(["aria2c", "--console-log-level=error", "-c", "-x", "16", "-s", "16", "-k", "1M", vae_url, "-d", webui_model_dir, "-o", "BloodNightOrangeMix.vae.pt"])
print(Fore.CYAN + "\n---------- セットアップ完了 ----------\n")
# WebUIを起動
sys.path.append(webui_dir)
sys.argv += shlex.split("--skip-install --xformers")
os.chdir(webui_dir)
from launch import start, prepare_environment
prepare_environment()
# 拡張機能が使えるように--enable-insecure-extension-accessを追加
sys.argv = shlex.split("--a --gradio-debug --share --xformers --enable-insecure-extension-access")
start()
@stub.local_entrypoint()
def main():
run_stable_diffusion_webui.call()
modal run stable-diffusion-webui_controlNet-aria2.py
とすると、しばらくしてwebuiのurlが表示されます。
Running on local URL: http://127.0.0.1:7860
Running on public URL: https://xxxxxxxx-xxxx-xxxx.gradio.live
public URLのほうにブラウザでアクセスすると、見慣れたStable-diffusion-webUIが表示されます!
Modal環境へのファイル転送
Modal環境へのファイル(Loraや、モデルなど)転送は、以下のコマンドで実行できます。
modal volume put <ボリューム名> <ローカルディレクトリ> <リモートディレクトリ>
[stable-diffusion-webui_controlNet-aria2.py]では、<ボリューム名>は、[stable-diffusion-webui-main]です。
例:
$ modal volume put stable-diffusion-webui-main lora /models/Lora
ファイルを送る際には、リモートディレクトリの後に[/]をつけます。
例:
$ modal volume put stable-diffusion-webui-main /Users/mydir/lora.safetensors /models/Lora/
$ modal volume put stable-diffusion-webui-main /Users/mydir/breakdomainrealistic_R2333.safetensors /models/Stable-diffusion/
$ modal volume put stable-diffusion-webui-main /Users/mydir/badhandv4.pt /embeddings/
転送が完了すると、WebUIから認識できるようになります。