rapid-anima / docs /operations.md
darask0's picture
Initial commit: rapid-anima distillation codebase
77cc641 verified
|
Raw
History Blame Contribute Delete
13.9 kB

実装 / 運用 Tips + トラブルシューティング

← README に戻る

distillation.md §4.7 の diffusion-pipe / ComfyUI 罠に加え、 2026-05 試行 (5 手法移植) で踏んだ追加の罠と対処、よくあるトラブルの解決策。

Modal CLI on Windows + JP (cp932) locale

毎回必須:

MSYS_NO_PATHCONV=1 PYTHONIOENCODING=utf-8 PYTHONUTF8=1 modal run ...
症状 対処
cp932 codec error Modal CLI 出力の Unicode ✓ や ● を encode できず即死 PYTHONIOENCODING=utf-8 PYTHONUTF8=1
Git Bash MSYS path mangling --dataset-path /dataset/rawC:/Program Files/Git/dataset/raw に化ける MSYS_NO_PATHCONV=1
Modal token が silent revoke .modal.toml は valid、modal config show で見える、しかし API 呼び出しで "Token not found" MODAL_LOGLEVEL=DEBUG modal app list でサーバー側のエラーを確認 → ユーザーが modal token new で再発行
modal app stop が対話確認で停止 Are you sure...? [y/N]: で固まる --yes を必ず付ける

diffusion-pipe + Anima 移植時の罠

症状 対処
_find_blocks ヘルパーが llm_adapter.blocks を選ぶ Anima DiT は本体 28 blocks + llm_adapter 6 blocks。名前 heuristic で adapter 側を拾うと 5 head すべて学習されない transformer.blocks を direct 参照、または "llm_adapter" not in name で filter (anima_ladd_disc.py::_find_blocks 参照)
LPIPS reg が OOM compute_regularization 内で student rollout を grad-through で 2 回目 走らせると 16 forward が graph に乗り 178 GiB 食い尽くす (1) --lpips-weight 0 で disable (2) --lpips-every N で間欠的に有効化 (Reflow パターン) (3) 解像度 1024 → 768 に下げる
PEFT LoRA 2 adapter 切替 DMD2 で同じ base に student + fake_score を attach する場合、毎 forward で set_adapter(name) を呼ばないと 片方の重みが leak する make_velocity_fn(peft_model, adapter_name) のような callable wrapper で隠蔽 (train_dmd2_official.py 参照)
Civitai Turbo LoRA → PEFT 形式変換 warm-start 時、ComfyUI 形式 diffusion_model.<…>.lora_A.weight を PEFT 形式 base_model.model.<…>.lora_A.default.weight に変換必要 train_traj.py::convert_comfy_to_peft_lora (_convert_peft_to_comfy_lora の逆向き)
warm-start key match wide LoRA target が x_embedder / t_embedder を含まないため、Anima Turbo の 1016 keys のうち 968 keys のみ match (12 keys skip、48 keys missing) 致命的ではない、warm-start として十分機能
subprocess 内 import modal 失敗 訓練 script が modal.Volume.from_name(...).commit() を呼ぶと ModuleNotFoundError: No module named 'modal' (subprocess の Python path に Modal がない) try/except で囲んで warning 出力のみ。Modal は subprocess 終了時に自動で volume commit する
LADD discriminator の torch.no_grad() + feat.detach() で adv 勾配が student に届かない G phase で adv loss を .backward() しても勾配が disc の heads までで止まる。実質「Smooth L1 recon distill only」になり LADD のメリット消失 AnimaLADDDiscriminator.forward(...)gradient_to_input フラグを足す。G phase は True (teacher は frozen なので weight 更新されないが activations が残り x_input → student に勾配が流れる)、D phase は False (heads だけ訓練、メモリ節約)
LADD bs=4 × accum=4 × misaligned_pairs_d で 170 s/step (smoke の 200x 遅い) bs=4 で D forward の cost が線形増、accum=4 で 4 倍 micro-step、misaligned で D batch を 2 倍に → 20+ full-DiT forward/step、B200 上で 170 s bs=2 accum=1 + misaligned 無効 に下げる → ~2 s/step (85x 高速化)。LADD は GAN なので大 batch 不要 (small noise の方が D の安定性に貢献)。train_ladd.py 引数: --batch-size 2 --grad-accum 1 (--misaligned-pairs-d フラグを付けない)
異なる rank の LoRA を warm-start に使えない --lora-rank 128--warm-lora /models/loras/anima_turbo.safetensors (rank 32) を指定 → load_warm_lora 内で shape mismatch、No LoRA keys matched で abort rank 変更時は cold-start で訓練 (warm-start を省略)。または rank padding helper を書く
modal CLI の uppercase arg は失敗 function param K: int を CLI から --K 1 で渡しても Modal が k=1 に lowercase 変換、unexpected keyword argument 'k' で abort function param は 必ず lowercase にする (例 k_grad: int)、CLI 側 --k-grad 1 を渡す
ComfyUI workflow JSON の _comment キーが node 扱いされて crash workflow JSON を iterate して node.get("class_type") する処理で、_comment (string value) が来ると 'str' object has no attribute 'get' for node_id, node in wf.items(): if not isinstance(node, dict): continue で skip
workflow JSON の node ID が patch_workflow の hardcode と一致しないと全 generation が HTTP 400 で fail scripts/generate_dataset.py::patch_workflow は positive prompt を node "5"、latent を "7"、KSampler を "8" に hardcode。ユーザー由来 workflow の node ID が違うと $PROMPT/$SEED プレースホルダが置換されず ComfyUI が prompt graph reject 新 workflow を作る時は 必ず node ID を 5=positive, 7=latent, 8=KSampler で構築。anima_workflow.json 系を雛形にする
reward fine-tuning (DRaFT+) で reward score 上昇 ≠ 視覚品質向上 HPSv2 score +13.21 上昇したが視覚的には reward hacking が起き、anime → 西洋イラスト ドリフト + キャラ固有要素曖昧化 + prompt adherence 落下 必ず視覚検証する、score だけで「勝った」と判断しない。kl_coeff 上げる (0.2 → 0.5) や reward 種類変更 (HPSv2 → ImageReward) で軽減可能性、ただし根本解決ではない (migration_log §5.10 参照)
base model 切替で既存 LoRA の出力 style が drift する preview3 で訓練した LoRA を v1.0 base で使うと chibi/SD 寄りに style が変わる (LoRA は動作する、出力分布が違う) LoRA は train 時の base と一致させて使うのが筋。base 切替時は LoRA を再訓練するか style drift を許容する
sageattention は .so 不要 (Triton JIT) cache_sageattention_to_volume/usr/local/lib/python3.11/site-packages/sageattention/ を volume copy しても .so が 0 個。一見「コンパイル失敗」に見える 仕様。sageattention は Triton で runtime JIT、import 時に kernel が compile される。動作確認は CUDA kernel smoke test (sageattn(q,k,v) を呼ぶ) で行う
ComfyUI で sageattention を効かせる デフォルトは torch SDPA、sageattention 入っていても自動使用されない ComfyUI 起動引数に --use-sage-attention を追加。generate_dataset.pyimport sageattention 成功時に自動で付与するよう改修済
Modal volume get で Japanese 文字パスに download すると後から Bash で見つけられない modal volume get anima-dataset path/ "/c/Users/micro/Downloads/新しいフォルダー/dest/" で download は成功するが、後続の cd / find / Python os.walk で日本語パスが broken or 空に見える 一次 download 先は ASCII path に (例: ./verify_v1_local/)、必要なら後で copy で日本語フォルダに移す
Modal volume rm は subprocess 内では使えない training script が訓練中に preview3 を消そうとしてもファイルハンドルが mmap で開いている (PyTorch safetensors loader) volume cleanup は別の Modal function で実行、training 中は触らない (実害は少ないが orchestration 注意)

コスト / 時間の実測値 (本リポジトリ 5 手法移植時)

すべて B200 ($6.25/h Modal)、batch=1-4、768-1024 解像度:

操作 時間 コスト
Image rebuild (lpips 追加分) ~5 分 (キャッシュ後 0 分) ~$0.5
B200 cold start ~3 分 ~$0.30
Smoke test (1 step) ~5-10 分 ~$0.5-2
Z-Image train 2000 step ~3 h (5.2s/step) ~$18 (見積もり $42 の半額)
DMD2 train 3000 outer (5 critic + 1 gen) ~5.8 h (3.6s/step) ~$19 (見積もり $36 の半額)
precompute_teacher_x0 5000 サンプル ~1.5 h (0.69/s) ~$11
LADD train 5000 step bs=4 accum=4 ~4.8 h (0.86s/step) ~$30
Reflow train 8000 step ~3-4 h ~$25
PCM train 5000 step (1 grad + 3 no_grad) ~6.5 h (4.7s/step) ~$41
5 手法 + precompute × 2 トータル wall-clock ~8 h (並列) ~$170

実測 step 時間は predicted の概ね 半分 — Modal の B200 は H100 比 2-3x 速いことを織り込まないと過大見積もりになる。

推論側 (verify 段階) の実測 (v1.0 base + sageattention 有効、B200、1024×1024):

条件 per-image 内訳
LoRA 4-step CFG=1 3.5-4.0s sage 効果あり、ほぼ全 LoRA 同等 (Z-Image / DMD2 / Turbo / Reflow / DRaFT+ 系)
LoRA 8-step CFG=1 4.0-4.7s sage 効果あり、step 数比例
base 30-step CFG=4.5 (LoRA strength=0) 10.7s 品質基準ライン

→ 蒸留 LoRA 4-step は base 30-step の 3 倍速 = $0.006/枚 vs $0.018/枚 (B200 $6.25/h 換算)。

トラブルシューティング

OOM (CUDA out of memory)

  • phase1_anima.tomlactivation_checkpointing = true を確認
  • [adapter].rank を 32 まで下げる
  • 解像度を 768 に落とす(phase1_dataset.tomlresolutions)
  • それでもダメなら H100 80GB に上げる

学習が進まない

  • データセット数枚で epochs = 1 試走 → エラーログを見る
  • modal app logs rapid-anima でリアルタイム監視
  • llm_adapter_lr = 0 が効いているか(消えていると壊れる)

出力の絵が地味

  • これは正常。Anima base は素なので、データセットを score≥200 等で 絞って色彩豊かな絵を集めると改善
  • artist タグを残して @favorite_artist1, @favorite_artist2 を caption に入れたままにすると、それらの平均が出やすくなる

diffusion-pipe が image build で失敗

attention をもっと速くしたい

デフォルトは torch SDPA(flash-attention 2 が built-in で使われる)。 さらに 1.2-1.5x 速くしたい場合は、modal_app.py の image 定義の最後に追加:

.run_commands(
    # diffusion-pipe install 後にビルドして torch ABI を合わせる
    "pip install --no-build-isolation sageattention",
    # または flash-attn (source build, 30-40分):
    # "pip install --no-build-isolation flash-attn",
)

依存解決の都合で順序が重要(diffusion-pipe より後ろ)。

modal volume put で大きいデータが上がらない

  • --force を付けて差分上書き
  • 5GB 超えるなら一旦 tar.gz → put → 中で展開する関数を作る方が安定

Secret 'hf_token' not found

Modal 上に hf_token という名前で secret が無い場合に出る。 setup.md の Hugging Face シークレット節を参照して作成、または modal_app.pyfrom_name("hf_token", ...) を既存 secret 名に書き換え。

HF upload で 401 Unauthorized

hf_token secret が read-only token の場合。write 権限が必要 (repo create / upload):

modal secret create HF_TOKEN_WRITE HF_TOKEN_WRITE=hf_xxxxx

modal_app.py::upload_lora_to_hfHF_TOKEN_WRITE secret を参照する。 詳細は setup.md

Modal workspace billing cycle spend limit reached

新規 app 起動時:

App creation failed: workspace billing cycle spend limit reached

現在進行中の cloud function にも SIGINT が送られて停止する(KeyboardInterrupt として cloud log に出る、ローカル CLI の Ctrl-C と区別不可)。先に並列起動した training が 両方 とも preempt される事例あり (2026-05-18 DMD2 v1 と DMDX v1 並列起動時)。

対処:

  1. https://modal.com/settings/billingSpend limits タブを確認
  2. current cycle の limit を引き上げる、または cycle reset 待ち
  3. 引き上げ後は新規 app 起動も既存 detach app も復旧

予防:

  • 並列で高負荷 training を起動する前に Modal dashboard で remaining budget 確認
  • DMD2 + DMDX 並列は B200 1 つあたり ~$30 × 2 = $60+ 高速消費するので、limit に余裕が必要

並列訓練時の rate 低下

本日実測 (2026-05-19、DMD2 resume + DMDX 並列):

  • DMD2 単独: 3.66s/outer
  • DMD2 並列 (with DMDX): 6.2s/outer (1.7x 遅い)
  • DMDX 単独 (smoke): 3.45s/outer
  • DMDX 並列: 3.6s/outer (slowdown 小さい)

原因推定: Modal が異なる B200 instance を割当てるが、混雑度や近隣 workload で per-step rate に差。DMDX は forward 数が少ない (2 disc + 1 gen = 3 stage vs DMD2 の 5+1=6 stage) ので絶対的に slowdown 耐性が高い。

並列予算は単独の 1.5-2x で見積もる (e.g., DMD2 単独 $30 → 並列時 $50-60)。