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 clona o codificador; LoRA adiciona deltas de baixo posto

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 . 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:

  1. LoRA. Uma camada linear pré-treinada W. Congele-a. Treine um B @ A de baixo posto (low-rank) de forma que W + BA corresponda a uma camada linear alvo. Mostre que r = 1 é suficiente para aprender perfeitamente uma correção de posto 1 (rank-1).

  2. 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). α = 2 ou α = 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

  1. Fácil. Em code/main.py, varie o posto r do LoRA de 1 a 4. Em qual posto o LoRA corresponde exatamente a um delta alvo de posto 2?
  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?
  3. 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õe pipe.load_lora_weights() + pipe.set_adapters([...], adapter_weights=[...]) para ativação por requisição. O custo de troca consiste nos pesos de tamanho 2 · 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

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).