1月の記事では、Windows + RTX4070 環境で ComfyUI と Ollama を組み合わせたサムネイル自動生成パイプラインを構築しました。
あれから約3ヶ月、作業環境を MacBook Air M5 に一本化する過程で、ComfyUI に依存しない構成へ移行しました。今回はその追試として、何が変わり、何が改善されたかを整理します。
なぜ ComfyUI から離れたか
1月時点の構成は、ComfyUI を HTTP サーバーとして起動し、Python から REST API 経由でワークフローを投げる形でした。
[Python] → HTTP POST → [ComfyUI サーバー] → KSampler → VAEDecode → PNG
主な理由は、LAN内で稼働する Windows + RTX4070 (VRAM 12GB) を長らく生成AIのメイン環境に据えていたためです。 この構成自体は問題なく動作していましたが、運用を続ける中でいくつかの問題が見えてきました。
- ComfyUI サーバーの常駐が必要 — 画像を1枚生成するだけでも、事前に ComfyUI を起動しておく必要がある
- ワークフロー JSON の保守 — あくまでワークフロー呼び出しのため、ノード構成を変えるたびに JSON を更新する必要がある
- エラーの可視性が低い — HTTP タイムアウトやポーリング失敗時に、原因が ComfyUI 側なのかネットワーク側なのか切り分けにくい
- Mac 環境との相性 — Mac環境で試したい場合、MPS には対応しているものの、CUDA 前提の設計が随所にあり、動作が不安定な場面があった
夜間のバッチ処理などは問題ありませんが、数枚程度の生成には明らかに向いていない、というのが正直な感想です。
diffusers に直接移行する
代替として選んだのは、HuggingFace の diffusers ライブラリを Python から直接呼ぶ構成です。
以前は手作業で馴染みのある ComfyUI のワークフローをそのままAPI呼び出しに転換しましたが、AI がゼロからコードを書いて教えてくれる現在は、知識や経験不足の心配はありません。
[Python] → StableDiffusionXLPipeline → MPS 推論 → PNG
ComfyUI の7ノード構成(CheckpointLoader → CLIP → KSampler → VAEDecode → SaveImage)を、diffusers の1行で置き換えます。
from diffusers import StableDiffusionXLPipeline
import torch
pipe = StableDiffusionXLPipeline.from_pretrained(
"SG161222/RealVisXL_V5.0",
torch_dtype=torch.float32,
use_safetensors=True,
)
pipe.to("mps")
pipe.enable_attention_slicing()
result = pipe(
prompt="modern workspace with code on screens, soft blue lighting, photorealistic",
negative_prompt="text, watermark, blurry, low quality",
width=1280,
height=720,
num_inference_steps=25,
guidance_scale=7.0,
)
result.images[0].save("background.png")
モデルは以前と同じ RealVisXL V5.0 を使っています。.safetensors ファイルを直接読み込むこともできますし、HuggingFace のモデル ID を指定すれば自動ダウンロードされます。
MPS + fp16 の罠:VAE が NaN を返す
移行直後、生成画像が真っ黒になる問題に遭遇しました。
ログを見ると、diffusers 内部で以下の警告が出ています。
RuntimeWarning: invalid value encountered in cast
images = (images * 255).round().astype("uint8")
これは VAE(画像のデコーダ)が NaN(非数値)を出力し、それを uint8 にキャストした結果、全ピクセルが 0(黒)になる現象です。
原因は MPS + fp16 の組み合わせでした。Apple Silicon の MPS バックエンドでは、半精度浮動小数点(fp16)の演算で数値が発散するケースがあるようです。CUDA環境では問題にならない精度の丸めが、MLX では NaN として顕在化します。
対処:fp32 で全パイプラインを実行
最初は VAE だけを fp32 にアップキャストする対処を試みましたが、不十分でした。UNet(ノイズ除去ネットワーク)自体が fp16 on MPS で NaN を出しており、その NaN が VAE に伝播していたためです。
最終的な解決策は、パイプライン全体を fp32 で実行することでした。
# CUDA のみ fp16、MPS / CPU は fp32
dtype = torch.float16 if device == "cuda" else torch.float32
メモリ使用量は fp16 の約2倍(約12GB)になりますが、幸い手元のMacBook Air M5 の 32GB 統合メモリであれば問題なく収まります。副次的に精度アップも期待でき、生成時間も 25ステップで1分足らずと、体感では大きく変わりません。
構成比較
移行前後の構成を比較します。
| 項目 | 1月(ComfyUI) | 3月(diffusers) |
|---|---|---|
| 実行環境 | Windows + RTX4070 | MacBook Air M5 |
| 画像生成 | ComfyUI HTTP API | diffusers 直接呼び出し |
| LLM | Ollama (qwen3:8b) | Ollama / MLX (qwen3.5:27b) |
| 外部サーバー | ComfyUI 常駐が必要 | 不要(単一プロセス) |
| dtype | fp16 | fp32(MPS 制約) |
| 生成時間 | 約40秒 | 1分弱 |
| VRAM | 12GB (専用VRAM) | 32GB中20GB程度専有?※ |
| エラー追跡 | HTTP ログ + ComfyUI ログ | Python トレースバック |
※ 他アプリの影響をアクティビティモニタでざっくり引き算した目視量。条件で変化。
画質は特に変わらず、同じ RealVisXL V5.0 を使っているため、プロンプトとシードが同じなら出力もほぼ同等です。 モデルサイズが大きくなったため、メモリの専有量は増えていますが今の環境・用途なら動くようです。
テキスト合成は変わらず Pillow
背景画像の生成方法は変わりましたが、テキストの合成レイヤーは1月から変わっていません。
LLM がキーワード抽出・画像プロンプト・レイアウト仕様の3段階で構成情報を生成し、Pillow でオーバーレイ・タイトル・キーワードタグを描画します。この部分はバックエンドに依存しないため、ComfyUI でも diffusers でも同じコードが動きます。
唯一の変更点は、LLM を qwen3:8b から qwen3.5:27b に変えたことです。3月の別記事で検証した通り、27b モデルは構造理解が明らかに向上しており、タイトルの改行位置やキーワード選定の質が上がりました。
一般的な用途ならGemini(Nano Banana2)で十分
以前にもご紹介しましたが、サムネイルの生成はGemini(Nano Banana2)で必要十分なクォリティが出せます。文字や構図の精度も自作ツールよりも高く、何よりチャットベースで「おまかせ」にできる良さもあります。APIキーを使えば、少々のコストはかかりますが自動化も可能です。
1日数枚程度は無料でも問題なく出力できるため、1日1記事など、個人の一般的な用途であればこれで十分です。自作ツールにも好きなチェックポイントを使える良さがありますが、AI技術の進化にはただただ驚かされます。
まとめ
ComfyUI は高機能なツールですが、「記事からサムネイルを1枚生成する」という限定的な用途には、diffusers の直接呼び出しの方がシンプルで扱いやすいと感じます。なにより、これで家の LAN 環境ぼ別サーバーに依存していた作業が MacBook Air上で完結できるようになりました。
- サーバー不要で、スクリプト1本で完結する
- エラーが Python のトレースバックとして返るため、デバッグしやすい
- MPS の fp32 制約はあるが、M5 の 32GB メモリで実用上は問題ない
1月に組んだパイプラインの設計思想(LLM でプロンプト生成 → 画像生成 → Pillow で合成)はそのまま活きており、バックエンドだけを差し替えた形です。設計を分離しておいた判断が、移行のしやすさにつながりました。






コメント