Reference adapters — Anima e Krea 2 (bateria de 2026-07-25/26)
Adapters de referência treinados no fork
adbrasi/diffusion-pipe-easycontrol,
branch ic-lora. Documentação completa em docs/ daquele repo — em especial
ANIMA_FUTURO_TREINO.md e KREA2_MULTIREF_RECEITA.md.
Dois projetos separados aqui:
| pasta | modelo | tarefa |
|---|---|---|
anima/ |
Anima (2B, DiT de vídeo derivado do Cosmos-Predict2) | uma referência, "próxima cena" |
krea2/ |
Krea 2 (22B... na prática 12,8B de DiT) | N referências separadas, endereçadas por texto |
krea2/ — multi-referência (o resultado novo)
Método krea2_multiref_grounded. Krea 2 recebendo N referências
separadas (não coladas numa imagem só), endereçadas por texto no caption:
"Generate an image of the woman from image 1 holding the bag from image 2."
Treinado no Azily/Macro-Dataset
(bracket 1-3, filtrado pelas notas do juiz do próprio dataset), 512px,
max_refs = 2.
multiref_step500— 2.200 amostras, 500 stepsmultiref_step250— mesmo run, checkpoint anterior
O achado que fez funcionar
O width_shift do Krea 2 soma uma constante ao deslocar a referência no
RoPE. Um laço sobre N reusando aquilo daria a todos os spans posições
idênticas — e aí a saída do DiT fica exatamente invariante à troca de
duas referências: a atenção é permutation-invariant sobre keys, o tvec e as
máscaras são uniformes, MLP e norm são pointwise.
Não é "difícil de aprender", é impossível em princípio. A correção é
offset cumulativo: slot i em w += (i+1) * W.
Formato de caption
image 1 / image 2, sem os sinais < >. Treinar com <image 1> fez
o modelo renderizar o marcador como texto dentro da imagem. Os blocos de
visão do Qwen3-VL são rotulados com o mesmo vocabulário (image 1:), então
não há ponte entre dois vocabulários para o modelo aprender.
Resultados
Avaliação held-out em anime (domínio totalmente fora da distribuição — o Macro é quase todo fotográfico): 6/6 corretos a 1024px, 4/6 a 512px. As duas falhas de 512 eram limitação de resolução, não de capacidade: a referência é codificada na resolução do alvo, então em 1024 ela carrega 4× mais tokens.
Num exemplo com posição explícita ("os dois personagens de image 1 na esquerda e o homem de image 2 na direita"), a saída obedece — e trocar a ordem das referências troca quem está de cada lado. Isso é endereçamento, não mistura.
Uso
Requer o fork (o runner constrói o modelo a partir do to_layers() do
pipeline, então treino e inferência compartilham o packing exato):
python tools/infer_reference_adapter.py \
--config examples/macro_multiref/run2_multiref.toml \
--adapter multiref_step500/ \
--reference ref1.jpg --reference ref2.jpg \
--prompt "Generate an image of the woman from image 1 holding the bag from image 2." \
--width 1024 --height 1024 \
--turbo-lora krea2_turbo_lora_rank_64_bf16.safetensors
A ordem das flags --reference é a ordem dos slots. --turbo-lora usa a
LoRA turbo oficial (8 steps, CFG 1.0, mu 1.15).
anima/ — bateria de métodos de adapter de referência
Sete braços de uma bateria controlada, todos com o mesmo dataset, seed e protocolo de avaliação (10 exemplos held-out × 4 colunas: sem referência / força 1.0 / ref_cfg 1.75 / referência embaralhada).
| adapter | método | veredito |
|---|---|---|
M1_armD_dropout_step2000_VENCEDOR |
ic_lora_v3 target-first, llm_adapter congelado, adaln fora, condition_dropout 0.1, 2000 steps |
🏆 vencedor (visual, do usuário) |
M1_arm1_llm_frozen_step500 / step1000 |
mesma receita, sem dropout | empata em qualidade, caráter diferente |
M1_armB_dropout_step1000 |
mesma receita com dropout, só 1000 steps | perde — não convergiu ainda |
M2_arm3_llm_trainable_step1000 |
llm_adapter treinável |
❌ perde ("praticamente não pega a referência") |
M3_armA_adaln_in_step1000 |
LoRA também no adaln | ❌ perde (artefato de instabilidade) |
M4_armC_routing_step1000 |
routing condition-only | ❌ perde |
Os dois achados
1. O condition_dropout não remove a referência — ele a ZERA. O frame
continua concatenado, com o mesmo timestep e as mesmas posições de RoPE; só
fica em branco. Consequência: o modelo ganha um nulo calibrado e passa a
tratar a referência como entrada separável que consulta (atribui), em vez
de fundir referência e alvo (acopla). Para um contrato do tipo "o que não
está escrito é herdado", atribuição é exatamente o que se precisa.
2. Dropout sem aumentar os steps é estritamente PIOR que não usar dropout. Sensibilidade medida: armB (dropout, 1000 steps) 0.589 < arm1 (sem, 1000) 0.657 < armD (dropout, 2000) 0.921. O dropout bloqueia a rota fácil, então o modelo aprende mais devagar — as duas mudanças andam juntas ou nenhuma delas.
Receita vencedora
[model]
type = 'ic_lora_v3'
llm_adapter_lr = 0 # CONGELADO — bate com a recomendação oficial do Anima
[ic_lora_full]
ref_first = false # target-first
condition_dropout = 0.1
include_adaln = false
[adapter]
rank = 32
[optimizer]
type = 'adamw_optimi'
lr = 1e-4 # batch efetivo 8
# 2000 steps, não 500
Inferência: --mode ominicontrol_subject --lora_strength 1.0 --ref_cfg 1.0.
⚠️ M4_armC_routing precisa de --mode ic_lora_dual — o modo padrão funde
a LoRA em todos os pesos e destrói o adapter routado, produzindo ruído puro
que parece "o método falhou".
Ressalvas honestas
- Nenhum ranking fino entre
arm1,armBearmDsobrevive à estatística (Welch t=1.74 com 6 seeds, crítico 2.23). O que sobrevive são os efeitos grandes:llm_adaptercongelado vence, adaln fora, routing perde. - Os adapters do Anima foram treinados num dataset com captions exaustivas, que causam "atalho de caption" — a caption descreve o alvo por completo e a referência vira supérflua. É plausível que o ranking mude com delta-captions.
- O
krea2/multirefé de um treino de 500 steps sobre 2.200 amostras. A capacidade está demonstrada; não é um modelo de produção.