Modalでforgeを使いたい

使いたかったけどできなかった!という記事です。

Modalとは

クラウドでGPUが借りられるサービスです。(ざっくり)
Google Colab とか paperspace みたいなやつ。30ドル分無料で使える。

forgeとは

ControlNet や Fooocus の制作者、我らが lllyasviel さんが作られた WebUI です。
Automatic1111 さんの WebUI をベースに、開発を容易にし、リソース管理を最適化し、推論を高速化されているらしい。(githubにそう書いてある)

結果

私にはできなかった!

#コマンドプロンプトに表示されたエラー
RuntimeError: Found no NVIDIA driver on your system. Please check that you have an NVIDIA GPU and installed a driver from http://www.nvidia.com/Download/index.aspx
#ChatGPTの解説
Modalの実行環境がGPUをサポートしていないか、GPUが存在しない状態でGPUを要求するコードを実行しようとしている可能性があります。
もし具体的なGPUの設定やリソース割り当てについての情報が必要な場合は、Modalのサポートや公式のフォーラムで直接尋ねることをお勧めします。

公式サイトにslackへのリンクがあるけど、私の環境だと開いても白紙ページなだけで何も起こらないんですよね。slackアプリはインストールしてるので、通常ならそれが開くんじゃないかと予想してるんだけど。
そして英語で難しい話をしてそうだしと思って、それについては特に解決方法も探ってない…。
ので、今回は諦めました!
やったことを記しておくので、使えた人はやり方を教えてくれると嬉しいです。

ちなみにforgeにはGeforceが必要だという情報を見かけました。ModalにはGeforceシリーズはないから、それで無理なのかなと思ったんだけど、paperspaceのA4000で動いてる人もいるみたい。

やったこと

modalパッケージのアプデ

pip install --upgrade modal

しばらく使ってなかったのでアップデートしました。

ワークスペースに新しい環境を作成

modal environment create webui-forge

使用環境を指定

modal config set-environment webui-forge

セットアップ

modal run "C:\ローカルパス\forge.py"

forge.pyの中身

import socket
import subprocess
import time
import webbrowser

from modal import Image, Queue, Stub, forward

stub = Stub("example-a1111-webui")
stub.urls = Queue.new()  # TODO: FunctionCall.get() doesn't support generators.


def wait_for_port(port: int):
    while True:
        try:
            with socket.create_connection(("127.0.0.1", port), timeout=5.0):
                break
        except OSError:
            time.sleep(0.1)


@stub.function(
    image=Image.debian_slim()
    .apt_install(
        "wget",
        "git",
        "libgl1",
        "libglib2.0-0",
        "google-perftools",  # For tcmalloc
    )
    .env({"LD_PRELOAD": "/usr/lib/x86_64-linux-gnu/libtcmalloc.so.4"})
    .run_commands(
        "git clone --depth 1 https://github.com/lllyasviel/stable-diffusion-webui-forge /webui",
        "python -m venv /webui/venv",
        "cd /webui && . venv/bin/activate && "
        + "python -c 'from modules import launch_utils; launch_utils.prepare_environment()' --xformers",
        gpu="a10g",
    )
    .run_commands(
        "cd /webui && . venv/bin/activate && "
        + "python -c 'from modules import shared_init, initialize; shared_init.initialize(); initialize.initialize()'",
    ),
    gpu="a10g",
    cpu=2,
    memory=1024,
    timeout=3600,
)
def start_web_ui():
    START_COMMAND = r"""
cd /webui && \
. venv/bin/activate && \
accelerate launch \
    --num_processes=1 \
    --num_machines=1 \
    --mixed_precision=fp16 \
    --dynamo_backend=inductor \
    --num_cpu_threads_per_process=6 \
    /webui/launch.py \
        --skip-prepare-environment \
        --listen \
        --port 8000
"""
    with forward(8000) as tunnel:
        p = subprocess.Popen(START_COMMAND, shell=True)
        wait_for_port(8000)
        print("[MODAL] ==> Accepting connections at", tunnel.url)
        stub.urls.put(tunnel.url)
        p.wait(3600)

@stub.local_entrypoint()
def main(no_browser: bool = False):
    start_web_ui.spawn()
    url = stub.urls.get()
    if not no_browser:
        webbrowser.open(url)
    while True:  # TODO: FunctionCall.get() doesn't support generators.
        time.sleep(1)

このコードは下記のページにあったものを1行だけ変えたものです。
Modal公式ページにA1111の使い方が載ってるなんて…いつの間に作ってくれたんでしょうね。ありがたい。

変えたのは32行目の git clone の行だけです。
URLをforgeのものにして、最新版が使えるようにバージョンの記載を消しました。

おわり。