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 に戻る](../README.md)
[distillation.md §4.7](distillation.md#47-周辺で踏んだ-modal--diffusion-pipe--comfyui-罠) の diffusion-pipe / ComfyUI 罠に加え、
2026-05 試行 (5 手法移植) で踏んだ追加の罠と対処、よくあるトラブルの解決策。
## Modal CLI on Windows + JP (cp932) locale
毎回必須:
```bash
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/raw``C:/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](migration_log.md#510-品質評価の方法論-reward-hacking-実証あり) 参照) |
| **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.py``import 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.toml` の `activation_checkpointing = true` を確認
- `[adapter].rank` を 32 まで下げる
- 解像度を 768 に落とす(`phase1_dataset.toml` の `resolutions`)
- それでもダメなら 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 で失敗
- `requirements.txt` 内のバージョン競合が原因のことが多い
- `modal_app.py``.pip_install` 部分でバージョン固定済み
- それでも詰まる時は GitHub Issues 参照: https://github.com/tdrussell/diffusion-pipe/issues
### attention をもっと速くしたい
デフォルトは torch SDPA(flash-attention 2 が built-in で使われる)。
さらに 1.2-1.5x 速くしたい場合は、`modal_app.py` の image 定義の最後に追加:
```python
.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](setup.md) の Hugging Face シークレット節を参照して作成、または
`modal_app.py``from_name("hf_token", ...)` を既存 secret 名に書き換え。
### HF upload で `401 Unauthorized`
`hf_token` secret が read-only token の場合。write 権限が必要 (repo create / upload):
```bash
modal secret create HF_TOKEN_WRITE HF_TOKEN_WRITE=hf_xxxxx
```
`modal_app.py::upload_lora_to_hf``HF_TOKEN_WRITE` secret を参照する。
詳細は [setup.md](setup.md#hf-hub-への-upload-lora-配布など)。
### 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/billing で **Spend 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)。