IP-Adapter+LoRAで商品カタログを描画する方法──ショップの商品をAIキャラクターに着せる

Dev.to / 2026/4/25

💬 オピニオンDeveloper Stack & InfrastructureTools & Practical UsageModels & Research

要点

  • 本記事では、LoRA(キャラクターの同一性を安定させる)とIP-Adapter(特定の服など任意の参照画像を保持する)を組み合わせて、商品カタログ風の描画を行う方法を解説しています。
  • LoRAとIP-Adapterはサンプリングの終盤で競合しやすく、双方が強すぎると顔やアイデンティティが参照画像側へ寄ってしまう(ドリフトする)点に注意が必要だと述べています。
  • バランスの取り方として、IP-Adapterの重みは0〜1の範囲の下半分に抑え、さらに早めにハンドオフして最終的なデノイズはLoRAに優先させることを推奨しています。
  • 実用的なノード順として「Checkpoint → LoRA → FreeU → IP-Adapter → KSampler」を提示し、LoRAの後にIP-Adapterをコンディショニングへ入れることで後半でLoRAが再び主導権を取りやすくなると説明しています。
  • 実行可能なワークフローのチュートリアルも含まれており、ComfyUIやComfyUI_IPAdapter_plus、必要なモデルファイル、SDXLチェックポイント、キャラクターローラなどの前提と、最初の衣装プレビュー生成までの素早いセットアップ手順が示されています。

実行可能なワークフロー: github.com/sm1ck/honeychat/tree/main/tutorial/04-ipadapter — IP-Adapterの重み/end_atに対する<tune>プレースホルダーを含むComfyUIのworkflow.jsonと、それをあなたのComfyUIインスタンスへ投稿して出力を保存するためのstdlib Pythonクライアントです。

前回の記事では、文字ごとのLoRAが視覚的アイデンティティに最も強く合うことが多い、と主張しました。では、そのキャラクターに特定のアイテムを身につけさせたい場合はどうでしょうか。たとえば、ショップの商品、ユーザーがアップロードした服装、別のユーザーからのギフトです。

LoRAはキャラクターを安定させます。さらに任意の参照画像を保持するには、IP-Adapterがよく合います。この2つの手法は、注意深く設定しないと競合し得ます。

TL;DR

  • LoRAはキャラクターの顔を安定させます。IP-Adapterは参照画像から特徴を引き込みます。両方をサンプリング後半まで強く効かせすぎると、顔が参照の方へドリフトする可能性があります。
  • バランス: 中程度のIP-Adapter重み(0〜1の下半分)と早めの引き渡し(IP-Adapterが最終のノイズ除去ステップより前に制御を放棄する)。最後のステップはLoRAの領域です。
  • 有用なノード順序: Checkpoint → LoRA → FreeU → IP-Adapter → KSampler。LoRAのでモデルの条件付けにIP-Adapterを投入すると、後半ステップでLoRAが再度主張しやすくなります。

最初の衣装プレビューをレンダリングする

このセクションでは、クローンから生成画像までを10分未満で案内します。

1. 前提条件

  • 動作しているComfyUIインスタンス(ローカルGPU、レンタル箱、または友人のもの)
  • ComfyUI_IPAdapter_plus をインストール済み
  • ip-adapter-plus_sdxl_vit-h.safetensorsmodels/ipadapter/ に配置
  • CLIP-ViT-H-14-laion2B-s32B-b79K.safetensorsmodels/clip_vision/ に配置
  • あなた自身のSDXLベースチェックポイント
  • キャラクターのLoRA — まだ無い場合は、まず前回の記事を参照してください

2. クローンしてクライアントをインストール

git clone https://github.com/sm1ck/honeychat
cd honeychat/tutorial/04-ipadapter
pip install -e .

3. 衣装の参照画像をクライアントの隣に置く

フラットレイ(平置き)で、背景がきれいなものが最適です。この例では./my-dress.pngです。

4. 実行 — チューニング範囲の真ん中から開始

export COMFY_URL=http://localhost:8188
export REFERENCE_IMAGE=./my-dress.png
export CHECKPOINT=your-sdxl-base.safetensors
export LORA=your-character-v1.safetensors
export IPADAPTER_WEIGHT=0.4      # 0〜1の下半分
export IPADAPTER_END_AT=0.8      # 0〜1の上半分

python client.py

出力は./out/outfit_preview_<n>.pngに保存されます。最初の実行では、通常は参照のドレスに似たものを身につけたあなたのキャラクターが表示されるはずです。

5. チューニング

出力を確認します。2つの失敗パターンが、調整方法を教えてくれます。

  • 顔がドリフトしたIPADAPTER_WEIGHTを下げるか、IPADAPTER_END_ATを0.05下げて再実行します。
  • アイテムが参照に似ないIPADAPTER_WEIGHTを0.05上げるか、IPADAPTER_END_ATを少し上げます。

0.05刻みでスイープしてください(0.1ではありません)。使える範囲は想定より狭い可能性があり、新しいベースモデルではバランスが安定して感じられるまでに、複数回のチューニングスイープが必要になることがあります。

6. pytestでworkflow JSONを検証する

pip install -e "[dev]"
pytest -v

5つのテストにより、workflow.jsonが有効なJSONであること、参照されるノードクラスが引き続き正しいこと、そして<tune>プレースホルダーが誤って実値付きでコミットされていないことが保証されます。

問題

あなたには、カスタムLoRAで安定化されたキャラクター(アンナ)がいます。生成のたびに、かなり一貫した見た目になります。さて、ユーザーがあなたのショップで特定のドレスを購入しました。そのドレスは参照画像です。あなたがやりたいのは:

  1. アンナの顔 — 変えない。
  2. この特定のドレス — アンナの上に忠実に再現してレンダリングする。

プロンプトエンジニアリングでは通常、これを確実に保証できません。「アンナが白い襟の赤いシルクドレスを着ている」という指示では、 aではなく、たとえ赤いシルクドレスであっても、この赤いシルクドレスとは限りません。SKU単位の忠実さには、生成経路に参照画像を入れる必要があります。

なぜ素朴なIP-Adapterがキャラクターを壊すのか

IP-Adapterは、参照画像の特徴をモデルのクロスアテンションへ取り込みます。これを高く設定しすぎると、参照画像を強力に保持してしまい、そこにがある場合はその顔まで保持してしまうことがあります。参照が未着用の商品の撮影写真であっても、IP-Adapterは照明、背景、スタイリングなどを参照写真から取り込んでしまう可能性があります。

重みが高い場合: アンナの顔は、参照に写っている(または何であれ)誰か/何かの見た目に近づき始めるかもしれません。照明やポーズも参照へ引っ張られます。

重みが低い場合: キャラクターは問題ありません。ドレスはだいたい正しい色とカットになりますが、このドレスだと認識できるほどではありません。するとあなたの商品カタログは、正確さではなく装飾になってしまいます。

バランス: 中程度の重み + 早めの引き渡し

重要なのは次の2つのつまみです: weightend_at

Weight — IP-Adapterがクロスアテンションへ寄与する量に掛ける倍率です。0〜1の範囲の下側の真ん中より下では、参照は「事実」というより「ムード」です。上側の真ん中より上では、参照が支配します。アイテムの同一性を保ちつつ顔の同一性を壊さないための範囲は、概ね下半分のどこかにあります。

end_at — IP-Adapterが有効である、ノイズ除去ステップの割合です。全ステップにわたって動くと、最終的な顔の細部に影響する発言権があります。もし早めに終了する(たとえば途中70〜90%まで)なら、最後のステップは残りのパイプラインの領域になり、LoRAの顔特徴が再び主張します。

ざっくり言うと: アイテムはノイズ除去の途中で焼き込まれ、顔は最後にもう一度シャープさを取り戻します。

ワークフローノードの順序(ComfyUI)

返却形式: {"translated": "翻訳されたHTML"}

IP-Adapter plus LoRA ComfyUI workflow chain: チェックポイント、キャラクターLoRA、FreeU、IP-Adapter経由の衣装参照画像、KSampler、そしてVAEデコード

[Checkpoint Loader]
  → [LoRA Loader: character_lora]
    → [FreeU: 品質の仕上げ]
      → [IPAdapter Advanced: 参照、weight=W、end_at=E]
        → [KSampler]
          → [VAE Decode]

この順序についてのポイントは2つあります:

  1. LoRAはチェーン内でIP-Adapterより前に置きます。 LoRAはチェックポイントの重みを変更します。IP-Adapterはサンプリング中のクロスアテンションを変更します。IP-Adapterがステップ end_at で終了すると、それ以降のステップはIP-Adapterの影響なしで、LoRAによって変更された重みの上で動作します——これが顔が再び主張できる理由です。
  2. FreeUは任意です。 これはノイズの再バランスで、計算量を増やさずに品質を向上させます。

チュートリアルのクライアントは、ベースの workflow.json を取得し、環境から渡される値で <tune> のプレースホルダーを書き換えます。参照画像をComfyUIにアップロードし、プロンプトをキューに入れます:

def rewrite_workflow(wf: dict[str, Any], args: argparse.Namespace, ref_filename: str) -> dict[str, Any]:
    """`<tune>` と `<path>` のプレースホルダーを、実際の値で埋めます。"""
    wf = json.loads(json.dumps(wf))  # 深いコピー
    if args.checkpoint:
        wf["1"]["inputs"]["ckpt_name"] = args.checkpoint
    if args.lora:
        wf["2"]["inputs"]["lora_name"] = args.lora
    wf["2"]["inputs"]["strength_model"] = args.lora_strength
    wf["2"]["inputs"]["strength_clip"]  = args.lora_strength
    wf["5"]["inputs"]["image"] = ref_filename
    wf["6"]["inputs"]["weight"] = args.weight
    wf["6"]["inputs"]["end_at"] = args.end_at
    wf["7"]["inputs"]["text"] = args.prompt
    wf["10"]["inputs"]["seed"] = int(time.time()) & 0xFFFFFFFF
    return wf

完全なソース

チュートリアルフォルダー内の workflow.json は、触るべきあらゆるフィールドに <tune> プレースホルダーを同梱して出荷されています。テストスイートは、それらのプレースホルダーがテンプレートに残っていることを検証します。これは、調整した本番用の値をうっかりコミットしてしまうのを防ぐ安全網です。

Weight tuning loop

実践的な手順:

  1. クリーンな商品写真がある参照アイテムを選びます。
  2. 強力なLoRAを持つキャラクターを選びます。
  3. weight=0.3, end_at=0.8 付近をレンダリングします。顔を確認し、アイテムも確認します。
  4. 顔が崩れる(ドリフトする)→ weightを下げるか、end_atを下げます。
  5. アイテムが参照と似ない → weightを慎重に上げるか、weightはそのままでend_atを上げます。
  6. 0.1刻みではなく0.05刻みでスイープします。有効な範囲は、想像より狭いです。

現実系およびアニメ系のベースでいくつか調整用スイープを行うと、だいたい動作する組み合わせに着地します。

Production integration

参照画像としてアウトフィットカタログを使う。 各ショップの商品には、オブジェクトストレージに保存された参照画像があります。生成時に、その参照URLをGPUワーカーへ渡します。ワーカーが一度ダウンロードしてキャッシュします。

プレビュー用にカタログを事前レンダリングする。 ユーザーがショップを閲覧すると、アクティブなキャラクターに対して各アイテムをレンダリングしたプレビューが表示されます。これらのプレビューを毎回のページロードで行う必要はありません。非同期に生成(Celeryワーカー)し、S3に保存し、キャッシュから配信します。

画像と動画での一貫性。 画像に使用するのと同じ IP-Adapter + LoRA の組み合わせが、動画生成の開始フレーム(例:Kling)を制御できることがよくあります。まず静止画のパスを調整し、その結果を慎重に再利用してください。

アイテムが視覚的でない場合のフォールバック。 ショップ内の「アイテム」の中には、ステータスのバフ、関係フラグ、セリフ解放など、視覚を伴わないものがあります。視覚のみとしてフラグが立っているアイテムに対してのみ、IP-Adapterの経路を通すようにします。

Production issues that came up

カタログプレビューの目立つ一部で顔が崩れた。 「より強いアウトフィットの一致のために」IP-Adapterのweightを高くしすぎていました。顔ドリフトの苦情が増えたため、より低い範囲(下半分)に戻しました。教訓:遅く感じるとしても、変数は1つずつ調整すること。

キャッシュされた参照URLが期限切れになった。 S3上のショップアイテムには、時間制限付きの署名付きURLが使われていました。生成ワーカーはキュー投入時にURLを取得しましたが、そのURLがComfyUIに実際にダウンロードされる前に期限切れになっていました。修正:ワーカー側で事前取得し、外部URLではなくComfyUI側のファイル名を渡す。

SDXLベースとのIP-Adapterモデルのバージョン不一致。 IP-Adapter Plusには、特定のSDXLベースモデルに対応した複数の重みが同梱されています。混在させると、明確なランタイムエラーなしに出力が悪化することがあります。単に解像度(忠実度)が下がるだけです。デプロイ設定では、IP-Adapterのバージョンをベースに紐づけて固定(pin)してください。

視覚を持たないショップアイテムがワークフローをクラッシュさせた。 APIが「ステータスバフ」アイテムを画像パイプラインでレンダリングしようとしました。修正:カタログエントリに visual: true|false のフラグを追加し、キュー投入する前にAPI境界で確認します。

What I'd change if starting over

  • クリーンなカタログから始める。 背景が一貫していて、照明も一貫しており、可能ならモデルがすでにそのアイテムを着用していない参照画像を用意します。
  • 調整(チューニング)をバージョン管理する。 ベースモデルを移動させると、IP-Adapterのweight/end_atの値もおそらく動きます。それらを定数として扱うのではなく、デプロイの一部として扱ってください。
  • 事前レンダリングしたプレビューを強くキャッシュする。 キャラクター×アイテムのグリッドは、掛け算的に増えていきます。キャラクター作成時と、新しいアイテム追加時に事前レンダリングします。

Where this lives

HoneyChatのショップは、IP-Adapter Plus(キャラクターごとのLoRAの上に重ねる)を使って、アクティブなキャラクターに対しアウトフィット、アクセサリー、ギフトをレンダリングします。公開アーキテクチャドキュメント: github.com/sm1ck/honeychat/blob/main/docs/architecture.md

References

本番でIP-Adapter + LoRAの組み合わせを出荷した方なら、どのベースに対して、どんなweight / end_atペアに着地したのか気になります。ベストなポイントは、アニメ系と現実系のベースの間でかなり意味のある形で変わるように見えます。