Hugging Face Diffusers 入門
Hugging Face DiffusersとSDXLの導入、画像生成、設定調整、GPUメモリの節約方法を解説。FastAPI経由でJavaScriptアプリに接続します。
Hugging Face Diffusers は、画像・動画・音声の拡散モデルを自分のハードウェア上で実行するための Python ライブラリです。テキストエンコーダー、スケジューラー、デノイジングネットワーク、VAE を 1 つのパイプラインオブジェクトにまとめており、プロンプトを渡して呼び出すだけで使えます。
ホスト型の画像生成 API は手軽に始められます。しかし、やがて画像ごとの課金がかさみ、レート制限でプロトタイプの開発が滞り、モデルが実際に何をしているのかを知りたくなってきます。モデルをローカルで実行すればこの 3 つの問題をすべて解決できますが、ほとんどのチュートリアルは PyTorch の知識があることを前提にしています。
本ガイドは JavaScript 開発者向けに書かれています。インストールから最初の画像生成までを順に進め、重要な設定項目を解説し、VRAM の少ない GPU でモデルを動かす方法を紹介します。最後に、フロントエンドから fetch() で呼び出せる FastAPI エンドポイントを構築します。サンプルコードは Diffusers v0.42.1 を対象としています。
重要なポイント
stabilityai/stable-diffusion-xl-base-1.0をDiffusionPipeline.from_pretrained(..., dtype=torch.float16)で読み込み、NVIDIA GPU では"cuda"、Apple Silicon では"mps"に移動します。- SDXL base 1.0 は CreativeML Open RAIL++-M License の下で公開されています。生成画像を製品に組み込む前に、必ずライセンスを確認してください。
- シードを固定した
torch.Generatorを使うと出力が再現可能になるため、設定を 1 つずつ変えてその効果を確認できます。 - モデルが GPU メモリに収まらない場合は、まず
enable_model_cpu_offload()を呼び出します。Diffusers のドキュメントでは、シーケンシャル CPU オフロードは非常に低速であると説明されています。 - パイプラインはサーバー起動時に一度だけ読み込み、すべてのリクエストで再利用します。リクエストハンドラー内で読み込んではいけません。
Hugging Face Diffusers とは
Diffusers は、Hugging Face が提供する拡散モデル用のライブラリです。中心となる抽象化は DiffusionPipeline で、モデルに必要な以下のコンポーネントをひとまとめにしています。
- プロンプトを埋め込みベクトルに変換する 1 つ以上のテキストエンコーダー
- デノイジングアルゴリズムを制御するスケジューラー
- 各ステップでノイズを予測する UNet または拡散 Transformer
- 結果をピクセルにデコードする VAE
Hub、Inference Providers、Spaces を初めて使う場合は、先に A Quick Guide to Hugging Face for Developers をお読みください。Python バックエンドを一切使わずに推論したい場合は、Transformers.js を使えばブラウザ上で直接モデルを実行できます。ただし、対応しているモデルの種類は異なります。
Diffusers に必要なハードウェア
Diffusers は 3 種類のマシンで動作します。NVIDIA GPU が圧倒的に高速で、Apple Silicon 搭載 Mac は PyTorch の "mps" バックエンド経由で動作し、CPU のみの環境でも動作はしますが非常に低速です。
| ハードウェア | デバイス文字列 | dtype | 目安 |
|---|---|---|---|
| NVIDIA GPU | "cuda" | torch.float16 | 高速 |
| Apple Silicon Mac | "mps" | torch.float16 | 動作するが、ディスクリート NVIDIA GPU より低速 |
| CPU のみ | "cpu" | torch.float32 | 動作するが、非常に低速 |
クイックスタートでは、デバイスのターゲットとして "cuda"、"mps"、"xpu"、"cpu" が挙げられています。また、Diffusers には専用の Apple Silicon (MPS) ガイドもあります。次のヘルパーコードは、実行中のマシンに適したデバイスと dtype を選択します。
import torch
if torch.cuda.is_available():
device, dtype = "cuda", torch.float16
elif torch.backends.mps.is_available():
device, dtype = "mps", torch.float16
else:
device, dtype = "cpu", torch.float32
Diffusers のインストールと最初の画像生成
Hugging Face Diffusers で最初の画像を生成するには、3 つのステップを踏みます。PyTorch とともに Diffusers をインストールし、SDXL を読み込み、プロンプトを渡してパイプラインを呼び出して .images[0] を保存します。
Diffusers は v0.42.0 以降、Python 3.11 以上が必須となっており、PyTorch 2.6 以上でテストされています。インストールガイドに従って仮想環境をセットアップし、パッケージをインストールしてください。SDXL のテキストエンコーダーは Transformers の CLIP モデルなので、このライブラリもあわせてインストールします。
uv pip install "diffusers[torch]>=0.42.1" transformers
# or: pip install "diffusers[torch]>=0.42.1" transformers
先ほどのヘルパーで決定した device と dtype を使って、画像を生成します。
import torch
from diffusers import DiffusionPipeline
pipeline = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
dtype=dtype,
).to(device)
prompt = "isometric pixel-art coffee shop at dusk, warm window light, rain on the street"
image = pipeline(prompt).images[0]
image.save("first.png")
image は PIL 画像なので、.save() で PNG ファイルとしてディスクに書き出せます。モデル ID には、stabilityai/ という組織プレフィックスを含む、Hub 上のチェックポイントの完全な名前を指定します。
ライセンス: SDXL base 1.0 は CreativeML Open RAIL++-M License の下で公開されています。生成画像を製品で使用する前に必ず内容を確認し、他のモデルに差し替える場合は、そのモデルのライセンスも確認してください。
SDXL で特に重要な設定項目
出力結果の大部分は、SDXL パイプラインの 4 つの引数、num_inference_steps、guidance_scale、generator、height / width によって左右されます。まずシードを固定し、それから引数を 1 つずつ変更していきましょう。
generator = torch.Generator().manual_seed(42)
image = pipeline(
prompt,
num_inference_steps=30,
guidance_scale=7.0,
height=1024,
width=1024,
generator=generator,
).images[0]
num_inference_stepsは、デノイジング処理を何回実行するかを指定します。ステップ数を増やすと時間はかかりますが、効果が頭打ちになるまでは通常ディテールが増していきます。guidance_scaleは、モデルがプロンプトにどれだけ忠実に従うかを指定します。値を大きくするとテキストにより忠実になりますが、大きすぎると過剰に処理されたような不自然な仕上がりになることがあります。generatorは初期ノイズを固定します。同じモデル、設定、ハードウェアであれば、シードを固定したジェネレーターで同じ画像を再現できます。height/widthは、出力サイズをピクセル単位で指定します。画像が大きくなるほど、必要なメモリと時間が増えます。
すべての引数とそのデフォルト値は、SDXL パイプラインのリファレンスに記載されています。
スケジューラーの差し替え
スケジューラーは各ステップでノイズをどのように除去するかを決定するもので、スケジューラーによって速度と品質のトレードオフが異なります。読み込み済みのパイプラインのスケジューラーは from_config() で差し替えられます。既存の config を渡すことで、チェックポイントに合わせた設定の整合性が保たれます。
from diffusers import DPMSolverMultistepScheduler
pipeline.scheduler = DPMSolverMultistepScheduler.from_config(pipeline.scheduler.config)
generator = torch.Generator().manual_seed(42)
image = pipeline(prompt, num_inference_steps=20, generator=generator).images[0]
スケジューラーガイドでは、SDXL でまさにこのパターンが使われています。DPMSolverMultistepScheduler のリファレンスによると、このスケジューラーは約 20 ステップで高品質なサンプルを生成できます。スケジューラーを比較する際はシードを固定したままにして、2 つの画像の間で変わるのがスケジューラーだけになるようにしましょう。
VRAM の少ない GPU で SDXL を動かすには
SDXL でメモリ不足(OOM)エラーが発生する場合は、Diffusers の 4 つのメモリ対策を順番に試してください。モデル CPU オフロード、VAE タイリング、VAE スライシング、4 ビット量子化の順です。後のステップほど節約できるメモリは増えますが、その分速度が犠牲になります。
- モデル CPU オフロード:
.to(device)を呼ばずに、pipeline.enable_model_cpu_offload()を呼び出します。各モデルは、実行中のときだけモデル単位で GPU 上に置かれます。 - VAE タイリング:
pipeline.vae.enable_tiling()は、大きな画像をタイル単位でデコードします。メモリガイドでは、タイル間でわずかに色調のばらつきが生じる場合があると注意書きされています。 - VAE スライシング:
pipeline.vae.enable_slicing()は、バッチを 1 枚ずつデコードします。1 回の呼び出しで複数の画像を生成する場合にのみ効果があります。 - 4 ビット量子化:
bitsandbytesをインストールし、短い config を追加します。
from diffusers.quantizers import PipelineQuantizationConfig
quant_config = PipelineQuantizationConfig(
quant_backend="bitsandbytes_4bit",
quant_kwargs={"load_in_4bit": True, "bnb_4bit_quant_type": "nf4", "bnb_4bit_compute_dtype": torch.float16},
components_to_quantize=["unet"],
)
pipeline = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
dtype=torch.float16,
quantization_config=quant_config,
)
pipeline.enable_model_cpu_offload()
PipelineQuantizationConfig とそのバックエンドについては、量子化の概要に記載されています。Diffusers は v0.39.0 で MPS 上での bitsandbytes 量子化に対応したため、この手法は CUDA に限定されません。ただし Apple Silicon では、bitsandbytes に PyTorch 2.9 以上が必要です。これは前述の PyTorch 2.6 という基準より高いバージョンです。
enable_sequential_cpu_offload() は、他の方法ではどうしてもメモリに収まらない場合以外は避けてください。メモリの節約効果は最も大きいものの、メモリガイドでは非常に低速であると説明されています。また、device_map を指定して読み込んだパイプラインでは、.to() やいずれかのオフロードメソッドを使う前に reset_device_map() を呼び出す必要があります。
Web アプリから Hugging Face Diffusers を呼び出す
フロントエンドから Diffusers を利用するには、パイプラインを小さな HTTP サーバーでラップします。サーバーは起動時にモデルを一度だけ読み込み、PNG のバイト列を返すようにします。fastapi と uvicorn をインストールし、以下を main.py として保存してください。
import io, threading
from contextlib import asynccontextmanager
import torch
from diffusers import DiffusionPipeline
from fastapi import FastAPI, Response
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
state = {}
lock = threading.Lock()
@asynccontextmanager
async def lifespan(app: FastAPI):
state["pipe"] = DiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0", dtype=torch.float16
).to("cuda") # or "mps"
yield
state.clear()
app = FastAPI(lifespan=lifespan)
app.add_middleware(CORSMiddleware, allow_origins=["http://localhost:5173"],
allow_methods=["POST"], allow_headers=["*"])
class GenerateRequest(BaseModel):
prompt: str
seed: int | None = None
@app.post("/generate")
def generate(req: GenerateRequest):
gen = torch.Generator().manual_seed(req.seed) if req.seed is not None else None
with lock:
image = state["pipe"](req.prompt, generator=gen).images[0]
buf = io.BytesIO()
image.save(buf, format="PNG")
return Response(content=buf.getvalue(), media_type="image/png")
uvicorn main:app で起動します。このコードは、FastAPI の 3 つの機能を利用して構成されています。
- lifespan ハンドラーは、サーバーがリクエストを受け付ける前にモデルを一度だけ読み込みます。
from_pretrained()はリクエストごとに呼び出すにはあまりにも低速です。 generateは通常のdefで定義されているため、FastAPI はイベントループをブロックせずにスレッドプールで実行します。- ロックにより、リクエストが 1 つずつ順番に GPU を使用するようになります。
CORSMiddleware により、開発用フロントエンドのオリジンからエンドポイントを呼び出せるようになります。
フロントエンドでは、fetch() で画像をリクエストし、レスポンスボディを Blob として読み取り、URL.createObjectURL() で表示します。
async function generate(prompt, seed, img) {
const res = await fetch("http://localhost:8000/generate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ prompt, seed }),
});
if (!res.ok) throw new Error(`Generation failed: ${res.status}`);
if (img.src.startsWith("blob:")) URL.revokeObjectURL(img.src);
img.src = URL.createObjectURL(await res.blob());
}
画像生成にはミリ秒単位ではなく秒単位の時間がかかります。ローディング状態を表示し、リクエスト処理中は送信ボタンを無効化してください。ユーザーがキャンセルできるようにする場合は、AbortController の signal を渡します。生成した PNG を実際のユーザーに配信する前に、OpenReplay の画像圧縮ツールでファイルサイズを削減しておきましょう。
次のステップ
パイプラインを適切なデバイスに読み込んでしまえば、Diffusers に必要なコードはごくわずかです。モデル ID、dtype、プロンプト、シードがあれば十分です。まず SDXL で最初の画像を生成し、シードを固定して設定を 1 つずつ調整してみましょう。メモリが不足する場合は、オフロードと量子化のステップを順番に試してください。そのうえで、FastAPI エンドポイントをフロントエンドの背後に配置します。その先、本番環境向けにチューニングする際に直面するトレードオフについては、メモリガイドとスケジューラーガイドで詳しく解説されています。
よくある質問
Diffusers はダウンロードしたモデルの重みをどこに保存しますか?
from_pretrained() はモデルファイルを Hugging Face Hub のキャッシュに保存し、以降の実行ではそれを再利用するため、再ダウンロードは発生しません。キャッシュのデフォルトの場所は ~/.cache/huggingface/hub です。モデルのキャッシュだけを移動したい場合は、環境変数 HF_HUB_CACHE を設定します。Hugging Face のすべてのデータを移動したい場合は HF_HOME を設定すると、キャッシュは HF_HOME/hub に配置されます。また、cache_dir 引数を渡せば、個々の読み込みごとに保存場所を指定することもできます。
同じシードでも、別のマシンでは異なる画像が生成されるのはなぜですか?
シードによって画像を完全に再現できるのは、同じハードウェアかつ同じ PyTorch バージョンの場合に限られます。CUDA GPU は CPU とは異なる乱数生成器を使用しており、また GPU 上の行列演算は決定性が低くなります。Diffusers の再現性ガイドでは、結果を一致させる必要がある場合は CPU ジェネレーターを渡すことを推奨しています。torch.Generator() はデフォルトで CPU ジェネレーターを生成します。ただしそれでも、異なる GPU モデルや PyTorch バージョン間で同一の出力が得られる保証はありません。
bitsandbytes による 4 ビット量子化は Apple Silicon 搭載 Mac で動作しますか?
はい、最近のバージョンであれば動作します。Diffusers は v0.39.0 で MPS 上での bitsandbytes 量子化に対応し、bitsandbytes 0.50.0 では 4 ビットおよび LLM.int8() のすべての構成が MPS で動作するようになりました。bitsandbytes の MPS 対応には PyTorch 2.9 以上が必要で、これは Diffusers の最低要件である PyTorch 2.6 より高いバージョンです。macOS 26 以降では、kernels パッケージをインストールすると、より高速な Metal カーネルが有効になります。
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k