Por que você precisa da matemática (e não só de prompts)
Você pode rodar Stable Diffusion ou Flux sem entender uma única equação. O resultado vai sair, e até bonito. Mas no momento em que algo dá errado — imagem borrada, prompt ignorado, CFG instável, sampler ruim — você fica refém de chutar valores no cfg_scale e trocar de sampler até funcionar. Saber a matemática transforma esse processo num diagnóstico: por que 50 passos de Euler com CFG 12 saturam? Por que DPM-Solver++ 2M converge melhor que DDPM em 20 passos? Por que a probability flow ODE permite editar latentes de forma determinística?
Este módulo cobre o núcleo: forward gaussian process, reverse process, score function, equivalência ruído↔score, DDPM (Ho et al. 2020), DDIM (Song et al. 2021), e a formulação SDE/ODE contínua (Song, Sohl-Dickstein et al. 2021). É o background mínimo para ler papers de SD3, Flux, DiT e entender debates atuais sobre Flow Matching e Rectified Flow.
Pré-requisitos honestos: cálculo univariado, probabilidade básica (gaussianas, esperança), álgebra linear (produto interno, normas), nada além de cálculo de variáveis aleatórias. EDOs/SDEs ajudam mas a intuição funciona sem.
A intuição geral em três frases
A grande sacada do DDPM (Ho, Jain, Abbeel — NeurIPS 2020) foi mostrar que o reverso, apesar de envolver uma integral intratável sobre todos os x₀ possíveis, pode ser aproximado por uma rede neural treinada com uma loss simples de MSE sobre o ruído adicionado. Sem adversarial training, sem reparametrization complicada, sem balanceamento delicado de GANs.
Forward process: cadeia de Markov gaussiana
Defina um schedule de variâncias β₁, ..., β_T ∈ (0, 1) (linear, cosine, sigmoide — qualquer um monotônico crescente). A cada passo t você adiciona ruído gaussiano:
Aplicando recursivamente e usando linearidade de gaussianas, dá pra pular direto de x₀ até x_t em uma única expressão fechada — essencial para treinar eficientemente:
Isso é ouro: para treinar você não precisa simular a cadeia inteira. Sorteia t ∈ {1,...,T} uniforme, amostra ε, monta x_t pela fórmula direta, e treina a rede para prever ε a partir de x_t e t. Custo: O(1) por amostra.
Reverse process: a parte que precisa de aprendizado
O verdadeiro reverso q(x_{t-1} | x_t) é intratável (depende de p(x_0)). Mas condicionado em x_0 ele é gaussiano e analítico:
q(x_{t-1} | x_t, x_0) = N(x_{t-1}; μ̃_t(x_t, x_0), β̃_t · I)
μ̃_t(x_t, x_0) = (√ᾱ_{t-1} · β_t / (1 − ᾱ_t)) · x_0
+ (√α_t · (1 − ᾱ_{t-1}) / (1 − ᾱ_t)) · x_t
β̃_t = ((1 − ᾱ_{t-1}) / (1 − ᾱ_t)) · β_tO modelo p_θ(x_{t-1} | x_t) aproxima esse alvo. Em vez de aprender μ̃ diretamente, Ho et al. mostraram que prever o ruído ε é mais estável. Substituindo x_0 = (x_t − √(1 − ᾱ_t) · ε) / √ᾱ_t na expressão de μ̃, chega-se à parametrização ε-prediction:
A loss que faz tudo funcionar
A ELBO (Evidence Lower Bound) original do DDPM se decompõe em termos KL por timestep. Ho et al. mostraram que a versão simplificada(ignorando pesos do KL e usando apenas MSE no ruído) treina melhor na prática:
Em pseudocódigo Pythonesco — quase tão simples quanto treinar um classificador:
# DDPM training step (Ho et al. 2020, Algorithm 1)
def train_step(x0, model, alphas_bar, T):
B = x0.shape[0]
t = torch.randint(0, T, (B,), device=x0.device) # timestep uniforme
eps = torch.randn_like(x0) # ruído gaussiano
a_bar = alphas_bar[t].view(B, 1, 1, 1) # ᾱ_t
x_t = a_bar.sqrt() * x0 + (1 - a_bar).sqrt() * eps # forward "fechado"
eps_pred = model(x_t, t) # ε_θ(x_t, t)
loss = F.mse_loss(eps_pred, eps) # L_simple
return lossNote que não há discriminador, não há reparametrização variacional, nem KL. Apenas MSE no ruído. Essa elegância é parte do motivo de diffusion ter ganho dos GANs em escala — treino estável, sem mode collapse.
Amostragem DDPM (Algorithm 2)
Custo por imagem: T forwards da U-Net (~1000 no paper original). Esse é o gargalo prático que motivou DDIM.
Score matching: o outro caminho que chega no mesmo lugar
Hyvärinen 2005 propôs estimar densidades aprendendo a score function s(x) = ∇_x log p(x). A intuição física: a score é um campo vetorial que aponta para regiões de maior densidade — se você seguir o gradiente em pequenos passos com ruído (Langevin), eventualmente cai numa amostra típica da distribuição.
Song & Ermon (NeurIPS 2019, NCSN — Noise Conditional Score Networks) notaram um problema: regiões de baixa densidade têm score mal estimada. A solução: perturbe os dados com múltiplos níveis de ruído e treine uma score-network condicional s_θ(x, σ) que estima a score em cada nível. Comece amostrando com ruído alto (cobre todo o espaço) e vá diminuindo σ progressivamente — annealed Langevin dynamics.
A equivalência ε ↔ score é direta: para x_t = √ᾱ_t x_0 + √(1−ᾱ_t) ε, vale ∇_{x_t} log p(x_t) ≈ − ε_θ(x_t, t) / √(1 − ᾱ_t). Quem prevê ruído implicitamente está prevendo a score (a menos de um fator). Por isso DDPM e score matching são primos próximos.
DDIM: amostragem determinística em poucos passos
Song, Meng, Ermon (ICLR 2021) reformularam o reverso como um processo não-Markoviano: o estado x_{t-1} depende explicitamente de x_t e de x_0 predito, sem precisar passar por todos os intermediários. Isso quebra a obrigatoriedade do schedule original e permite saltar passos.
x_{t-1} = √ᾱ_{t-1} · x̂_0(x_t)
+ √(1 − ᾱ_{t-1} − σ_t²) · ε_θ(x_t, t)
+ σ_t · z
x̂_0(x_t) = (x_t − √(1 − ᾱ_t) · ε_θ(x_t, t)) / √ᾱ_t
σ_t = η · √( (1 − ᾱ_{t-1}) / (1 − ᾱ_t) ) · √(1 − α_t / α_{t-1})A formulação contínua: SDE/ODE (Song et al. 2021)
O passo conceitual mais importante depois do DDPM. Song, Sohl-Dickstein, Kingma, Kumar, Ermon & Poole (ICLR 2021) mostraram que tudo isso é a discretização de uma SDE estocástica contínua no tempo:
Duas escolhas canônicas:
| SDE | Drift / diffusion | Limite t→T | Equivalência discreta |
|---|---|---|---|
| VP-SDE (variance preserving) | f = −½ β(t) x ; g = √β(t) | N(0, I) | DDPM (Ho et al. 2020) |
| VE-SDE (variance exploding) | f = 0 ; g = √(dσ²/dt) | N(0, σ²_max I) | NCSN (Song & Ermon 2019) |
| sub-VP | mistura controlada | N(0, I) com variância menor | Variante numericamente estável |
Pelo teorema de Anderson (1982), toda SDE forward admite uma SDE reversa com score:
E o pulo do gato: existe uma probability flow ODE determinística com mesma distribuição marginal:
DDIM (η=0) é exatamente a discretização desta ODE para a VP-SDE. Por isso DDIM é determinístico, permite inversão e aceita solvers de ODE de alta ordem — DPM-Solver, Heun, RK4, UniPC — que convergem em poucos passos.
Timeline: papers que você precisa conhecer
Por que importa para SD3, Flux e além
📋 Você está implementando uma feature em cima de Flux/SD3 e precisa decidir entre samplers
Flux usa rectified flow (Liu et al. 2022) — é uma reparametrização da probability flow ODE com trajetórias quase retas. SD3 usa MMDiT com rectified flow também. Saber a base te permite ler os papers diretamente e prever qual sampler funciona.
Alt: Ficar trocando samplers no chute —
Alt: Sem entender o solver, você não consegue diagnosticar quando converge mal —
Alt: Confiar só em defaults da lib —
Alt: Defaults mudam, e cada modelo (SD1.5, SDXL, SD3, Flux) tem comportamento diferente —
Perguntas que sobram
❓ Se DDPM precisa de 1000 passos, como Stable Diffusion roda em 20?
❓ Por que prever ruído é mais estável que prever x_0 diretamente?
❓ A score function existe analiticamente para uma distribuição arbitrária?
❓ Qual o link entre Flow Matching (Flux) e diffusion clássico?
O que ler depois
No próximo módulo: como esse arcabouço matemático é encarnado em hardware — VAE comprime imagem em latente, U-Net denoise com cross-attention, CLIP/T5 fornece o condicionamento textual. É a arquitetura que faz Stable Diffusion rodar numa GPU de consumidor.