Phase 08 - Lesson 08
ControlNet, LoRA & Condicionamento
O texto por si só é um sinal de controle desajeitado. O ControlNet permite clonar um modelo de difusão pré-treinado e direcioná-lo com um mapa de profundidade, esqueleto de pose, rascunho ou imagem de bordas. O LoRA permite ajustar um modelo de 2 bilhões de parâmetros treinando 10 milhões de parâmetros. Juntos, eles transformaram o Stable Diffusion de um brinquedo no pipeline de imagem de 2026 usado por todas as agências.
Tipo: Build Idiomas: Python Pré-requisitos: Fase 8 · 07 (Difusão Latente), Fase 10 (LLMs do Zero — para base de LoRA) Tempo: ~75 minutos
O Problema
Um prompt como "a woman in a red dress walking a dog on a busy street" não fornece ao modelo informações sobre onde o cão está, qual pose a mulher está ou a perspectiva da rua. O texto define cerca de 10% do que você precisa para especificar uma imagem. O restante é visual e não pode ser descrito de forma eficiente em palavras.
Treinar um novo modelo condicional do zero para cada sinal (pose, profundidade, canny, segmentação) é proibitivo. Você quer manter a estrutura principal (backbone) do SDXL de 2.6B de parâmetros congelada, anexar uma pequena rede lateral que lê o condicionamento e faz com que ela influencie as características intermediárias do backbone. Isso é o ControlNet.
Você também quer ensinar novos conceitos ao modelo (seu rosto, seu produto, seu estilo) sem treinar novamente o modelo completo. Você quer um delta 100x menor. Isso é o LoRA — adaptadores de baixo posto (low-rank adapters) que se conectam aos pesos de atenção existentes.
ControlNet + LoRA + texto = o kit de ferramentas do profissional de 2026. A maioria dos pipelines de imagem em produção sobrepõe 2 a 5 LoRAs, 1 a 3 ControlNets e um IP-Adapter sobre uma base SDXL / SD3 / Flux.
O Concept
ControlNet (Zhang et al., 2023)
Pegue um SD pré-treinado. Clone a metade do codificador (encoder) da U-Net. Congele o original. Treine o clone para aceitar uma entrada de condicionamento extra (bordas, profundidade, pose). Conecte o clone de volta à metade do decodificador (decoder) do original com conexões de atalho de convolução zero (zero-convolution) (convoluções 1×1 inicializadas em zero — começam como uma operação nula (no-op) e aprendem um delta).
SD U-Net decoder: ... ← orig_enc_features + zero_conv(controlnet_enc(condition))
A inicialização com convolução zero (zero-conv) significa que o ControlNet começa como uma identidade — sem prejuízos mesmo antes do treinamento. Treine em 1 milhão de trios (prompt, condição, imagem) com a perda de difusão padrão.
Os ControlNets de cada modalidade são distribuídos como pequenos modelos laterais (~360M para SDXL, ~70M para SD 1.5). Você pode compô-los na inferência:
features += weight_a * control_a(depth) + weight_b * control_b(pose)
LoRA (Hu et al., 2021)
Para qualquer camada linear W ∈ R^{d×d} no modelo, congele W e adicione um delta de baixo posto (low-rank):
W' = W + ΔW, ΔW = B @ A, A ∈ R^{r×d}, B ∈ R^{d×r}
com r << d. Os postos (ranks) de 4 a 16 são o padrão para atenção, e de 64 a 128 para ajustes finos intensos. Número de novos parâmetros: 2 · d · r em vez de d². Para a atenção do SDXL com d=640, r=16: 20 mil parâmetros por adaptador em vez de 410 mil — uma redução de 20 vezes. Em todo o modelo: um LoRA geralmente tem entre 20 e 200 MB, em comparação com os 5 GB da base.
Na inferência, você pode ajustar a escala do LoRA: W' = W + α · B @ A. α = 0.5-1.5 é o normal. Múltiplos LoRAs se acumulam aditivamente (com a ressalva usual de que eles interagem de maneiras não lineares).
IP-Adapter (Ye et al., 2023)
Um adaptador minúsculo que aceita uma imagem como condicionamento (junto com o texto). Usa o codificador de imagem CLIP para produzir tokens de imagem e os injeta na atenção cruzada (cross-attention) ao lado dos tokens de texto. Cerca de ~20MB por modelo base. Permite que você faça algo como "gerar uma imagem no estilo desta referência" sem precisar de um LoRA.
Matriz de composibilidade
| Ferramenta | O que controla | Tamanho | Quando usar |
|---|---|---|---|
| ControlNet | Estrutura espacial (pose, profundidade, bordas) | 70-360MB | Layout exato, composição |
| LoRA | Estilo, assunto, conceito | 20-200MB | Personalização, estilo |
| IP-Adapter | Estilo ou assunto a partir de imagem de referência | 20MB | Nenhum texto consegue descrever a aparência |
| Textual Inversion | Conceito único como um novo token | 10KB | Legado, amplamente substituído por LoRA |
| DreamBooth | Ajuste fino completo em um assunto | 2-5GB | Identidade forte, alto custo computacional |
| T2I-Adapter | Alternativa mais leve ao ControlNet | 70MB | Dispositivos de borda, restrição de orçamento de inferência |
ControlNet ≈ espacial. LoRA ≈ semântico. Use ambos.
Implementação
code/main.py simula os dois mecanismos em 1-D:
LoRA. Uma camada linear pré-treinada
W. Congele-a. Treine umB @ Ade baixo posto (low-rank) de forma queW + BAcorresponda a uma camada linear alvo. Mostre quer = 1é suficiente para aprender perfeitamente uma correção de posto 1 (rank-1).ControlNet-lite. Um preditor de "base congelada" e uma "rede lateral" que lê um sinal extra. A saída da rede lateral é controlada (gated) por um escalar treinável inicializado em zero (nossa versão de zero-conv). Treine e observe o gate aumentar de valor.
Passo 1: Matemática do LoRA
def lora(W, A, B, x, alpha=1.0):
# W is frozen; A, B are the trainable low-rank factors.
return [W[i][j] * x[j] for i, j in ...] + alpha * (B @ (A @ x))
Passo 2: Rede lateral com inicialização zero
side_out = control_net(x, condition)
gated = gate * side_out # gate initialized to 0
h = base(x) + gated
No passo 0, a saída é idêntica à base. O início do treinamento atualiza o gate lentamente — sem desvios catastróficos.
Armadilhas
- Ajuste excessivo de escala de LoRAs (Over-scaling).
α = 2ouα = 3é um truque comum para "tornar o efeito mais forte" que gera saídas estilizadas em excesso ou corrompidas. Mantenhaα ≤ 1.5. - Conflito de pesos do ControlNet. Usar um ControlNet de Pose com peso 1.0 e um ControlNet de Profundidade com peso 1.0 geralmente causa distorções (overshoot). Uma soma de pesos ≈ 1.0 é um padrão seguro.
- LoRA na base errada. LoRAs do SDXL silenciosamente não têm efeito (no-op) no SD 1.5 porque as dimensões de atenção não correspondem. O Diffusers emitirá um aviso a partir da versão 0.30+.
- Desvio de Textual Inversion. Tokens treinados em um checkpoint apresentam grande desvio em outro. O LoRA é mais portátil.
- Fusão de pesos e armazenamento de LoRA. Você pode incorporar (bake) um LoRA nos pesos do modelo base para uma inferência mais rápida (sem adição em tempo de execução), mas perde a capacidade de ajustar a escala de
αem tempo de execução. Mantenha ambas as versões.
Como Usar
| Objetivo | Pipeline de 2026 |
|---|---|
| Reproduzir o estilo artístico de uma marca | LoRA treinado em ~30 imagens selecionadas com posto (rank) 32 |
| Colocar meu rosto em uma imagem gerada | DreamBooth ou LoRA + IP-Adapter-FaceID |
| Pose específica + prompt | ControlNet-Openpose + SDXL + texto |
| Composição baseada em profundidade | ControlNet-Depth + SD3 |
| Referência + prompt | IP-Adapter + texto |
| Layout exato | ControlNet-Scribble ou ControlNet-Canny |
| Substituição de fundo | ControlNet-Seg + Inpainting (Lição 09) |
| Estilo rápido em 1 passo | LCM-LoRA no SDXL-Turbo |
Prática
Salve outputs/skill-sd-toolkit-composer.md. A habilidade (skill) recebe uma tarefa (recursos de entrada: prompt, imagem de referência opcional, pose opcional, profundidade opcional, rascunho opcional) e gera a pilha de ferramentas, pesos e um protocolo de semente (seed) reproduzível.
Exercícios
- Fácil. Em
code/main.py, varie o postordo LoRA de 1 a 4. Em qual posto o LoRA corresponde exatamente a um delta alvo de posto 2? - Médio. Treine dois LoRAs separados em duas transformações alvo. Carregue-os juntos e mostre a interação aditiva deles. Em que momento a interação deixa de ser linear?
- Difícil. Use diffusers para empilhar: SDXL-base + Canny-ControlNet (peso 0.8) + um LoRA de estilo (α 0.8) + IP-Adapter (peso 0.6). Meça a compensação (trade-off) entre FID e aderência ao prompt à medida que os pesos da pilha variam.
Termos-Chave
| Termo | O que as pessoas dizem | O que realmente significa |
|---|---|---|
| ControlNet | "Controle espacial" | Codificador clonado + conexões zero-conv; lê uma imagem de condicionamento. |
| Zero convolution | "Começa como identidade" | Convolução 1×1 inicializada em zero; o ControlNet começa como uma operação nula (no-op). |
| LoRA | "Adaptador de baixo posto" | W + B @ A, r << d; 100x menos parâmetros que um ajuste fino completo. |
| rank r | "O botão de ajuste" | Compressão do LoRA; tipicamente de 4 a 16, 64+ para personalização intensa. |
| α | "Força do LoRA" | Ajuste de escala em tempo de execução do delta do LoRA. |
| IP-Adapter | "Imagem de referência" | Pequeno adaptador de condicionamento de imagem via tokens de imagem do CLIP. |
| DreamBooth | "Ajuste fino completo de assunto" | Treina o modelo completo com ~30 imagens de um assunto. |
| Textual Inversion | "Novo token" | Aprende apenas um novo embedding de palavra; legado, majoritariamente substituído. |
Nota de produção: Trocas de LoRA, vias de ControlNet, serviço multi-tenant
Um SaaS real de texto para imagem serve centenas de LoRAs e uma dezena de ControlNets sobre o mesmo checkpoint base. O problema de serviço se assemelha muito ao multi-tenant de LLM (a literatura de produção cobre o caso de LLM sob processamento em lote contínuo — continuous batching — e LoRAX / S-LoRA):
- Troque LoRAs em tempo real (hot-swap), não os funda. Fundir
W' = W + α·B·Aà base oferece uma inferência por passo ~3-5% mais rápida, mas congelaαe a base. Mantenha os LoRAs ativos em VRAM como deltas de posto r; a biblioteca diffusers expõepipe.load_lora_weights()+pipe.set_adapters([...], adapter_weights=[...])para ativação por requisição. O custo de troca consiste nos pesos de tamanho2 · d · r · num_layers— em escala de MB, levando menos de um segundo. - ControlNet como uma segunda via de atenção. O codificador clonado funciona em paralelo com a base. Dois ControlNets com peso 1.0 cada = duas passadas diretas (forward passes) extras por passo, e não uma passada unificada. O limite de tamanho de lote cai quadraticamente. Reserve orçamento para cerca de ~1.5× o custo por passo para cada ControlNet ativo.
- LoRAs quantizados também. Se você quantizou a base (consulte a Lição 07, Flux em 8GB), the LoRA delta também é quantizado de forma limpa para 8 ou 4 bits. O carregamento ao estilo QLoRA permite empilhar 5 a 10 LoRAs sobre uma base Flux de 4 bits sem estourar a memória.
Específico do Flux: o notebook Flux-on-8GB de Niels quantiza a base para 4 bits; empilhar um LoRA de estilo (pipe.load_lora_weights("user/style-lora")) nessa base quantizada em weight_name="pytorch_lora_weights.safetensors" ainda funciona. Esta é a receita que a maioria das agências de SaaS entrega em 2026.
Leitura Adicional
- Zhang, Rao, Agrawala (2023). Adding Conditional Control to Text-to-Image Diffusion Models — ControlNet.
- Hu et al. (2021). LoRA: Low-Rank Adaptation of Large Language Models — LoRA (originalmente para LLMs; adaptado para difusão).
- Ye et al. (2023). IP-Adapter: Text Compatible Image Prompt Adapter — IP-Adapter.
- Mou et al. (2023). T2I-Adapter: Learning Adapters to Dig Out More Controllable Ability — alternativa mais leve ao ControlNet.
- Ruiz et al. (2023). DreamBooth: Fine Tuning Text-to-Image Diffusion Models for Subject-Driven Generation — DreamBooth.
- HuggingFace Diffusers — ControlNet / LoRA / IP-Adapter docs — pipelines de referência.