AdwolfCzar's picture
Upload training/NOTES.md with huggingface_hub
acce2fc verified
|
Raw
History Blame Contribute Delete
20.7 kB

MiniMax H3 LoRA — anime loop i2v (projeto h3_loop)

Objetivo

LoRA de MiniMax H3 (FL2VA) no dataset AdwolfCzar/anime_loop_2d_dataset_captioned_29-07. Fase 1 ("quick"): 2 épocas nos ~3.9k clipes do mega_curated para validar o pipeline completo. Fase 2: treino com o dataset completo (loopcut_000..005, ~49 GB) ou o que couber. Foco de uso: image2video (primeiro frame → vídeo). Task de treino: i2va.

RUNBOOK — caminho das pedras (para o próximo agente/treino)

Sequência exata que funcionou, com os custos medidos. Scripts em scripts/, configs em configs/.

  1. Ambiente: usar /venv/main (torch 2.10.0+cu130 — NÃO troque torch; nightly quebra CUBLAS). uv pip install -e . no fork Akane commit c4b84fa. uv pip install tensorboard (não vem). Se o venv der Permission denied: sudo chown -R claude:claude /venv/main.
  2. Downloads (hf CLI com xet, muito rápido): pesos Comfy-Org/MiniMax-H3 (DiT bf16 66 GB + TE nvfp4 15 GB + 2 VAEs) e dataset. TE bf16 (48 GB) é desnecessário — nvfp4 funciona em Blackwell para cache E sampling do trainer.
  3. Análise quantitativa do dataset ANTES de tudo (ffprobe em paralelo): fps real, SAR/DAR (DVDs 720×480 são anamórficos!), nb_frames, áudio. Sem isso o treino sai distorcido/errado.
  4. Normalização (normalize_videos.py, ~30 jobs, ~15 min/4k clipes): 24 fps CFR + setsar=1
    • scale por SAR. Nunca treinar fonte com fps misto (uncanny motion) ou pixel anamórfico.
  5. Extensão de loops (extend_loops.py): tiling do ciclo até escada 124/90/73/56 (canvas do H3 começa em ~124f; treinar 22f não é validado por ninguém). Loops = tiling limpo.
  6. Buckets multi-res (assign_buckets.py): 3 áreas (512×288 / 640×384 / 832×480, 55/28/17), symlinks, sem upscale. Multi-res é OBRIGATÓRIA no H3 (RoPE por área) + jitter 0.2 no treino.
  7. Smoke test (smoke_train.sh): 10 steps + sampling + checkpoint + resume (leg 2 com --max_train_steps 15 valida autoresume de verdade). Incluir os itens MAIS PESADOS por bucket.
  8. Cache (cache_quick.sh): batch 24 + workers 16, SERIAL (latents → TE). Medido na 5090: latents ~4,5 h + TE ~1,5 h para 3.885 itens ≈ 31,7 GB. NUNCA rodar TE em paralelo com latents na mesma GPU (medido: piora ambos). Batch >24 não ajuda (VAE é o gargalo, GPU 100%).
  9. Treino (train_quick.sh via tmux + tee log): receita anti-destilação completa (ver seção Receita). ~13 s/it na 5090 no regime 124f multi-res. Sampling+checkpoint a cada 500 steps.
  10. Uploader HF (hf_uploader.py) ativo DESDE o smoke; repo gated manual, nunca "minimax" no nome.
  11. Monitorar: loss média SOBE com preservation ligada — é normal, olhar val/loss de vídeo; watchdog de stall (freeze silencioso near-OOM = step 10x mais lento, não crasha).

Armadilhas que já custaram tempo aqui:

  • ffmpeg pesado em paralelo durante CARGA do DiT → 6× mais lento (carga é CPU-bound); durante steps de treino, ok.
  • Jupyter cria .ipynb_checkpoints root-owned dentro de output/ quando o usuário navega — rm -rf falha; usar sudo rm ou ignorar.
  • Nunca pkill -f com padrão que casa com o próprio comando composto; matar por PID exato.
  • Nunca apagar cache com processo de treino/cache vivo apontando para ele.
  • Cache TOML separado do train TOML (batch alto só no cache; treino EXIGE batch_size=1).
  • --save_last_n_steps_state 1500 limita states em disco (cada state = 859 MB).

Ambiente (medido em 2026-08-10)

  • Vast.ai, RTX 5090 32 GB (sm_120), 124 GB RAM, 32 vCPU, disco 600 GB (overlay, não persistente)
  • Python 3.12.13 em /venv/main, torch 2.10.0+cu130 (combinação validada pela comunidade)
  • Trainer: fork AkaneTendo25/musubi-tuner, branch minimax-h3, commit c4b84fa (pin do relatório 2026-08-10)
  • Instalação: uv pip install -e . (torch não é tocado — extras cu130 não instalados)
  • ffmpeg 6.1.1 do sistema

Pesos

Comfy-Org/MiniMax-H3/workspace/models/MiniMax-H3/

  • diffusion_models/minimax_h3_fl2va_bf16.safetensors (~66 GB; int8 convrot na carga)
  • text_encoders/qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors (~15 GB; Blackwell OK)
  • vae/minimax_h3_video_vae_fp16.safetensors, vae/minimax_h3_audio_vae_fp32.safetensors

Dataset — fatos medidos (mega_curated)

  • 3.889 mp4 + 3.889 .txt (captions sidecar dentro do zip; jsonl tem 6.855 linhas, 2.966 com caption nula — os .txt do zip estão completos)
  • Áudio: nenhum clipe tem trilhah3_target_mode = "video" (sem audio VAE no cache, sequência menor)
  • fps caótico: ~24 (2239), 25 (341), ~30 (182), centenas de variantes 22–29 (VFR/telecine)
  • Resoluções: 720×480 anamórfico (SAR 853:720 e 186:157, ambos DAR ~16:9) ×1852; 1920×1080 ×1093; 1280×720 ×354; 704×396 ×245; 1270×720 ×104; 960×720 ×35; 640×480 ×7
  • Frames pós-conversão 24fps, snap 17n+5: 22×1706, 39×875, 56×480, 73×199, 90×173, 107×114, 124×139; 4 itens < 22 frames (descartados)

Normalização (não-destrutiva)

scripts/normalize_videos.py: fonte /workspace/datasets/mega_curated (intocada) → /workspace/datasets/mega_24: 24 fps CFR (fps=24), pixels quadrados (scale por SAR + setsar=1), libx264 crf 15 preset fast, sem áudio. Clipes com <22 frames pós-conversão são pulados.

Buckets multi-resolução (obrigatório no H3 — RoPE normalizado por área)

Diretiva do usuário (10/08): resolução pequena/média, 1024×576 é alto demais. scripts/assign_buckets.py (seed 42, sem upscale, estratificado por resolução de origem):

  • v288 [512×288] ×2138 (~55%) — movimento barato
  • v384 [640×384] ×1087 (~28%) — bucket principal
  • v480 [832×480] ×660 (~17%) — âncora de densidade média (v576 descartado; caches v384/v480 de smoke reaproveitados, cache v576 removido) Views por symlink em /workspace/datasets/train_quick/; manifest em configs/bucket_manifest_quick.csv.

Receita de treino (relatórios info/ de 09-10/08 + docs do fork)

  • guidance 4 + schedule sigma + form normalized; preservation 0.02; spatial density jitter 0.2
  • SEM adapter do Ostris; SDPA (flash_attn quebra int8 convrot); int8 convrot fwd bf16; AdaLN rank 16
  • rank/alpha 32 (estilo amplo), LR 1e-4 AdamW8bit (recomendação do trainer), warmup 50
  • batch físico 1 (obrigatório no H3), gradient_accumulation = 1
  • 5090 32 GB: blocks_to_swap 24 h2d_only ring 2 pinned; PYTORCH_ALLOC_CONF=expandable_segments:True
  • Sampling i2v durante treino: 3 primeiros-frames do próprio dataset + captions como estão (--i), TE nvfp4
  • max_frames 124 (regime de extensão de loops — ver seção Extensão de loops)

Fase 2 — dimensionamento (medido)

  • loopcut_000..005: 19.747 mp4 (~50 GB descomprimido), zero overlap de nomes com mega_curated
  • Dataset completo fase 2 = loopcut (19.7k) + mega_curated (3.9k) ≈ 23,6k clipes
  • Normalização 24fps concluída 10/08 06:5x: 19.746 ok (1 duplicata), zero falhas, 43 GB em loopcut_24
  • Extensão de loops (mesma escada 124/90/73/56) → loopcut_24_ext, iniciada em seguida
  • Extensão loopcut concluída: 4.592 tiled + 15.154 linked (clipes naturalmente longos), só 8,3 GB. Histograma: 124f×16490 (84%!), 107×1774, 90×1100, 73×269, 56×113
  • Config fase 2 deve reintroduzir diversidade de duração: sub-datasets por bucket com max_frames 124/90/73 via symlinks (per-dataset max_frames no TOML, sem re-encode)
  • Disco p/ fase 2: caches ~100-140 GB estimados, 341 GB livres — cabe; recalcular antes do caching

Log de execução

  • 2026-08-10 04:1x: downloads iniciados (modelos ~86 GB, dataset completo ~52 GB) — xet, muito rápido
  • 2026-08-10 04:2x: normalização 3.889 clipes lançada (30 jobs paralelos)

Incidentes

  • 2026-08-10 04:47: smoke #1 (buckets antigos c/ v576) caiu no step 1 com FileNotFoundError — causa: remoção do cache v576 com o processo ainda vivo (erro operacional meu, não bug do trainer). Antes de cair validou: carga DiT bf16→int8 convrot (~2 min), sampling i2v 3/3 (pico 14,8 GiB), 1 step com loss 0.546 a 4,8 s/it. Smoke #2 relançado com buckets novos.

Extensão de loops (decisão técnica minha após pergunta do usuário, 2026-08-10 ~05:30)

Faixa publicada do H3 começa em 124 frames (5,17s); mediana do dataset era 42f. Como os clipes são loops seamless, cada clipe é estendido por tiling do ciclo até um alvo sorteado (seed determinística): 124f 55% / 90f 20% / 73f 15% / 56f 10%. Justificativa: canvas publicado do H3 comeca em ~124f; 44% do dataset natural estava em 22f (nunca validado por ninguem no canal); caso worldx documenta acao esticada ao gerar mais longo que o treinado. Contra: custo ~2,4x/step; canal treina 39-73f sem quebra especifica de duracao. Decisao: casar treino com regime de geracao (5s) vence o custo. Clipes ja maiores que o alvo ficam no comprimento natural (cap 124, symlink sem re-encode). Ensina movimento cíclico no regime real de geração (~5s). scripts/extend_loops.py/workspace/datasets/mega_24_ext; max_frames=124; sampling --f 124. Custo: step médio ~2x vs regime 73f. Block swap mantido em 24 (teste com 16 não mostrou ganho; folga de VRAM protege contra freeze near-OOM, e o custo do swap no H3 é ~5% — compute-bound).

Smoke test (2026-08-10 ~05:10)

  • Leg 1 (buckets novos 288/384/480): PASSOU exit 0 — 10 steps, loss 0.895→0.156, ~5-7 s/it (inclui overhead de saves), VRAM pico 23,5 GiB c/ swap 24, RAM 26 GB
  • Sampling i2v 3/3 nas 3 resoluções: denoising 13,5s (512×288), 21s (640×384), 33s (832×480) a 20 steps; pico sampling 14-15 GiB; mp4+json escritos
  • Checkpoint validado: 600 tensores LoRA, dim/alpha 32/32, 569 MB (fp32); state 859 MB
  • Carga DiT bf16→int8: ~2 min sem contenção de CPU (com 24 ffmpeg paralelos: 6× mais lento — lição: pausar normalização pesada durante fases de carga)
  • Leg 2 (resume + swap 16) em execução: valida autoresume e mede ganho de velocidade com menos swap
  • Uploader HF verificado durante smoke (NOTES/configs subiram para AdwolfCzar/minih33-loop-i2v, gated manual)

Probe 124f + lançamento (2026-08-10 05:36-05:42)

  • Probe 5 steps no regime estendido: PASSOU exit 0, ~12-13 s/it, loss 0.167→0.101, VRAM ~18 GiB no início dos steps (dimensionado pelos itens mais pesados nos smoke buckets)
  • Sampling 124f: 35s (512×288) / 62s (640×384) / 108s (832×480) de denoising por vídeo; pico 16,7 GiB; ~3,5 min por evento de sampling (a cada 500 steps → overhead ~1%)
  • 05:42 lançado tmux h3quick: cache completo (3.885 itens; batch final 24) → treino 2 épocas encadeado
  • Log: /workspace/logs/quick_pipeline.log

Medições de cache (10/08 06:00-06:40)

  • Latents batch 4: ~7 s/item; batch 12: ~5 s/item; batch 24: ~1,7-6,5 s/item conforme grupo de frames (124f domina). GPU 100% — VAE encode é o gargalo real; batch >24 não deve ajudar.
  • TE em paralelo com latents na mesma GPU: FRACASSO (TE 11-12 s/item vs ~1,6 solo; latents +14%). GPU time-slicing serializa com overhead. Regra: estágios de cache SEMPRE seriais em single-GPU.
  • Checkpoints do treino ajustados a pedido do usuário: a cada 500 steps (sampling idem); states retidos: últimos 1500 steps.

Fase 2 — config pronta (10/08 ~12:20)

  • assign_buckets_phase2.py: loopcut 19.746 → v288 10.862 / v384 5.528 / v480 3.356; views de duração (≥124f: 70% f124 / 20% f90 / 10% f73): 9 sub-datasets em train_full/
  • dataset_full_train.toml: 12 datasets (3 mega reutilizando cache/quick + 9 loopcut → cache/full)
  • train_full.sh (minih33_full, 1 época default — revisar com resultados do quick), cache_full.sh

Treino quick — lançado (2026-08-10 12:12 UTC)

  • Cache final: v288 13 GB + v384 9,7 GB + v480 9,0 GB = 31,7 GB (3.885 itens, latents+TE i2va+guidance)
  • Duração total do caching (batch 24, serial): ~6h (latents ~4,5h + TE ~1,5h)
  • Treino: 2 épocas = 7.770 steps; checkpoint+sampling a cada 500; disco a 49% no início
  • Monitores: watchdog stall 15min, checkpoints/samples/erros, marcos do pipeline

Treino quick — ENCERRADO pelo usuário no step 2.500 (10/08 ~21:20 UTC)

  • Rodou 2.564 steps a 12,8 s/it; loss 0.129→0.088; 5 checkpoints (500..2500) + samples no HF
  • Usuário avaliou samples: "está aprendendo bem"; decidiu parar e ir para fase 2 maior
  • Checkpoint 2500 convertido p/ ComfyUI (convert_lora.py --target other, chaves diffusion_model.*) → checkpoints/comfy/minih33_quick-step00002500_comfy.safetensors no repo

BUG da base preservation em c4b84fa (descoberto pelo canal em 10/08)

  • Ada: preservation comparava contra o modelo COM a LoRA ativa → efeito nulo/parcial
  • Fix: 5d06e34 "disable training LoRA for base preservation" (verificado no diff: toggle enabled no LoRAModule + teste); head atualizado para f175417
  • Implicação: o quick run rodou na prática como "guidance 4 + jitter" — checkpoint VÁLIDO (guidance segura runs curtos), mas diz pouco sobre preservation em run longo
  • Caches NÃO afetados (fix é na loss; chancelor retomou sem recache; diff não toca fingerprint)
  • Validação externa: mamad8 24h+ em 8x4090 com a receita guidance4+prsv0.02+jitter SEM degradação

Fase 2 — revisão pós-relatório do outro agente (10/08 ~21:30)

  • Fork: f175417 (fix preservation); re-smoke em execução
  • Durações rebalanceadas 35/30/35 (f124/f90/f73) — corta step médio de ~12 p/ ~8-9 s/it
  • Duas etapas: STAGE A main (v288+v384, mega+loopcut, 1 época ≈ 20,2k steps) → STAGE B polish (v480, 3.000 steps, --network_weights do LoRA final da A)
  • --h3_fused_qk_norm_rope habilitado (recomendação Ada; validando no smoke)
  • LR 1e-4 nas duas etapas — decisão explícita do usuário (recusou 5e-5)
  • Block swap 24 mantido (benchmark Ada: 0 vs 48 blocos = 0,03 s/it — swap é grátis)
  • Pipeline: launch_full_pipeline.sh (cache 12 datasets → stage A → stage B, tmux)

FASE 3 (composição nova definida pelo usuário, 10/08 ~21:30)

Substitui o plano "fase 2 completa". Dataset: mega_curated 3.889 (caches quick reutilizados) + loopcut 2.000 (seleção seeded estratificada por bucket/duração; não-selecionados deletados p/ liberar 38 GB) + looper_v4 10.000 → ~15,9k itens, ~2 épocas (31,8k steps), LR 1e-4 (usuário).

  • looper_v4 (AdwolfCzar/looper_v4_dataset_captioned): 3 zips ~50 GB, 26,5k mp4, TUDO ~30fps SAR 1:1, resoluções web modernas (1080p/720p/4K + retrato/quadrado), 79% vira 107f em 24fps; 25.724 com captions; 1.222 curtos demais excluídos
  • Prep looper: seleção seeded 10k → normalize 24fps + DOWNSCALE cap 0,61 MP (fonte 4K não serve p/ buckets ≤0,4MP) → SEM extensão de loop (107f natural já no regime; diversidade via views)
  • Views compartilhadas train_p3/{bucket}_{dur} (loopcut2k + looper10k juntos), caches cache/p3/*
  • Smoke f175417 completo: leg1 10 steps + resume leg (loss 0.185→0.066) com fused_qk_norm_rope
  • Limpezas autorizadas executadas: extract_tmp, zips (todos), mega_curated raw, loopcut não-selecionado, states antigos — pico de 443 GB livres
  • Entregas ao repo: guia_comfyui.md (resoluções/durações/uso), exemplo_de_captions.txt (100 aleatórias), checkpoint 2500 convertido p/ ComfyUI (convert_lora.py --target other)
  • Armadilha nova p/ o runbook: NUNCA usar until pgrep -f <padrão> em background quando o próprio comando contém o padrão — o wrapper se auto-detecta e trava para sempre
  • Armadilha (11/08, CAUSA REAL corrigida): vídeos com metadata não-UTF8 (tags latin-1) quebram o PyAV do musubi (av.open → avdict_to_dict → UnicodeDecodeError) — o ffprobe tolera, o PyAV não, e o ffmpeg PRESERVA metadata da fonte por default. Fix: remux lossless -c copy -map_metadata -1 nos 10k do looper (validação PyAV 10000/10000 ok) + -map_metadata -1 adicionado aos scripts de normalização. (Primeira hipótese "race de symlinks" estava errada.)

FASE 3 — treino LANÇADO e saudável (2026-08-11 16:48 UTC)

  • Cache p3 completo: 100 GB (12k itens novos: loopcut-2k + looper-10k; mega reutilizou cache/quick) Latents ~14h + TE ~5h no total (incluindo os reruns dos incidentes de metadata)
  • Treino minih33_v2: 2 épocas × 15.885 itens = 31.770 steps Medido no step 500: 10,15 s/it (rebalance de durações 35/30/35 + fused_qk_norm_rope entregaram: era 12,8 no quick), loss 0.116 caindo, VRAM 17,4 GB
  • Config: f175417 (preservation FUNCIONAL), guidance 4/sigma/normalized, prsv 0.02, jitter 0.2, LR 1e-4 (decisão do usuário), rank 32, swap 24, checkpoint+sampling a cada 500 + upload HF
  • Samples step 0 e step 500 (3/3 cada) no repo; primeiro checkpoint 500 salvo
  • Projeção aritmética (não promessa): ~3,7 dias para as 2 épocas; checkpoints utilizáveis desde já (quick mostrou sinal bom em ~2,5k steps)
  • Disco: 333 GB livres após tudo | Watchdog stall + monitores de erro ativos

RUN v3 (12/08 ~04:1x UTC) — decisões do usuário após avaliação dos samples v2

Contexto: samples do v2 a 3,5k steps considerados fracos pelo usuário. Diagnóstico: (1) f175417 NÃO tem o fix 365d980 (amostragem da preservation + propagação do jitter + alinhamento sigma-guidance) — receita parcialmente torta pela 3ª vez; (2) doutrina do canal virou em 11/08: LR 1e-4 é alto para H3 destilado (rank 32 → 2-5e-5); (3) preservation freia 2x por design. Decisões (usuário): fork f8561f6c; LR 5e-5; SEM preservation (vai parar cedo por checkpoint- picking, prioriza aprendizado rápido; guidance 4 + jitter 0.2 ficam); max 2 épocas (31,8k steps) mas parada manual a qualquer momento; restart DO ZERO (10h de GPU descartadas por baseline limpo); output_v3/ + repo novo AdwolfCzar/minih33-loop-i2v-v3 (gated manual) com uploader próprio. v2 encerrado no step ~3.5k (checkpoints 500-3500 + samples preservados no repo original). Novidade do f8561f6c: checkpoint sob demanda por arquivo-gatilho (parada graciosa a qualquer momento — casa exatamente com o plano de parada manual do usuário).

Incidente v3: OOM no step ~4.502 (12/08 15:1x UTC) — RECUPERADO

  • CUDA OOM (alocação de 1,12 GiB com 30,08 GiB já alocados) após 11h de run — batch pesado no pior bucket; expandable_segments estava ativo (fragmentação não era a causa; VRAM cheia real)
  • Fix: blocks_to_swap 24→32 (~3 GiB a mais de folga; custo ~zero, H3 é compute-bound) + autoresume do state 4500. Checkpoints 500-4500 + samples intactos no repo v3
  • Lição p/ runbook: em runs >10h na 5090, swap 32 desde o início; monitores com tail -n 0 (log em append guarda Tracebacks antigos → falso alarme no relançamento)

FASE 4 — dois treinos separados (decisão do usuário, 14/08)

v3 encerrado no step 17.500 (usuário: "já tá bom o suficiente"). Caches antigos apagados (449 GB livres). Agora: um treino por dataset, um de cada vez — smoothloop primeiro, jiggle depois; 10 épocas cada (parada manual quando estiver bom).

SMOOTHLOOP (966 clipes)

  • Fontes: 2 pastas mega.nz + 1 arquivo mega (zip 5 GB) + zip do HF smoothloop_dataset_base_v2
  • Dedup real (o palpite do usuário estava certo): 1.863 mp4 → 966 únicos 444 duplicatas exatas (md5 do conteúdo) + 444 mesmo-ID re-encodado em outra fonte
  • Captions casadas por stem entre fontes (o zip do mega3 tinha 1.580 captions p/ 505 vídeos — as fontes se complementam; matching por sibling .txt teria perdido a maioria)
  • Normalização p4: 24fps CFR + SAR + cap 0,61 MP + -map_metadata -1; 966/966 ok, mediana 236f (78% já ≥124f → sem tiling de loop)
  • Views: v288 532 / v384 270 / v480 164 × f124/f90/f73; cache 11 GB (966 latents + 966 TE)
  • Config: idêntica ao minih33_v3 (verificado por diff), só muda dataset/output/samples/épocas
  • Repo: AdwolfCzar/minih33-smoothloop (gated) + uploader + conversor ComfyUI automático

JIGGLE (1.232 clipes) — preparado, aguardando o fim do smoothloop

  • bouncing_body_jiggle_captioned_30-07; cap 800 por nível (medium 866→800, low 225, intense 272)
  • Triggers por nível aplicados na caption conforme README do dataset: "bouncing body, " → "bouncing body with {low,medium,intense} jiggle and bouncing, "
  • Normalizado: 1.232 ok, 65 curtos demais descartados

Armadilha nova (14/08)

--max_train_steps N NÃO limita quando o script já passa --max_train_epochs M — o smoke de 8 steps virou treino real de 230 steps antes de eu notar. Para smoke com esses scripts, sobrescreva TAMBÉM as épocas (ou use um script dedicado).

Publicação HF (resolvido 10/08)

Repo AdwolfCzar/minih33-loop-i2v — público (sem custo de cota privada) + gated manual (arquivos só baixáveis com aprovação do dono). Regra do usuário: nunca mencionar "minimax" no nome/card público; identificador: minih33. Uploader: scripts/hf_uploader.py (watch em output/).