Demonstrações interativas da aula de Normalizing Flows
Quatro visualizações que acompanham os slides: a mudança de variável pX(x) = pZ(z)·|df/dz|−1 em 1D com o termo do jacobiano visível, a composição f = fK ∘ … ∘ f1 deformando a gaussiana base passo a passo, a camada de affine coupling yB = xB ⊙ exp(s(xA)) + t(xA) em 2D, e o treino por máxima verossimilhança minimizando a NLL ½‖z‖² − Σℓ sℓ + const com um flow de verdade aprendendo two moons no navegador.
Partimos da base z ∼ 𝒩(0,1) e aplicamos uma transformação monotônica ajustável x = f(z) = b + a·z + c·tanh(z/0.7). A conservação de probabilidade exige p(x)·dx = p(z)·dz, logo a densidade resultante é pX(x) = pZ(z)·|f′(z)|−1: onde o flow alonga o eixo (derivada grande) a densidade abaixa, onde comprime (derivada pequena) a densidade sobe. A curva tracejada mostra o que acontece se esquecermos o termo do jacobiano: a área sob a curva deixa de ser 1. Arraste o ponto no painel central para inspecionar f′(z0) em cada região.
Base pZ(z) = 𝒩(0,1) e amostras
Transformação x = f(z) (arraste o ponto z0)
pX(x) com jacobiano (sólida) e sem (tracejada) + histograma das amostras transformadas
💡 Com a = 1, b = 0, c = 0 o flow é a identidade e pX = pZ. Suba para c ≈ 1.5 e reduza a ≈ 0.5: o centro do eixo é esticado, a massa é empurrada para os lados e surge uma densidade bimodal, como na figura dos slides. Compare a sólida com a tracejada no readout: sem o jacobiano a área fica longe de 1. Arraste z0 até o centro e veja f′(z0) grande exatamente onde a densidade é baixa.
A classe de funções inversíveis e diferenciáveis é fechada em composição: f = fK ∘ fK−1 ∘ … ∘ f1. Cada camada aqui é simples, fk(z) = z + αk·tanh((z − μk)/sk), mas empilhando camadas a gaussiana base se deforma em distribuições cada vez mais ricas. Pela regra da cadeia o determinante do jacobiano da composição é o produto dos determinantes, |f′| = Πk |fk′|, então basta que cada camada seja barata para a rede inteira ser tratável. Mova o slider de K e veja a densidade se deformar passo a passo.
Densidade após K camadas (laranja); cinza: estágios intermediários; ciano tracejado: base p(z)
Mapa composto x = f(z) (tracejado: identidade)
💡 Com K = 0 a densidade é a própria base. Clique ⏭ uma camada por vez e acompanhe o readout: o jacobiano composto em z = 0 é o produto dos jacobianos das camadas, e em log vira uma soma, exatamente o termo que entra na NLL. Clique 🎲 algumas vezes: composições diferentes geram multimodalidades diferentes, mesmo com cada fk sendo um único "solavanco" de tanh.
A camada de affine coupling (RealNVP) divide a entrada em duas partes: xA é copiada sem mudança e xB sofre uma transformação afim cujos parâmetros vêm de xA: yB = xB·exp(s(xA)) + t(xA). Como xA não depende de xB, o jacobiano é triangular e log|det J| = Σ s. Aqui a "coupling network" é controlada por dois sliders: s(xA) = ws·tanh(xA) e t(xA) = wt·(xA² − 1)/2. As abas trocam qual metade é transformada, o papel da permutação entre camadas.
z ∼ 𝒩(0, I) (ciano) → amostras transformadas (laranja), densidade pX (mapa de cores) e grade deformada
Saídas da coupling network: α·s(xA) e α·t(xA)
💡 Com α = 0 a camada é a identidade; clique ▶ e veja a gaussiana se curvar em forma de banana, um passo na direção das two moons da seção 4. Teste ws = 1.2, wt = 0: só a escala muda, e o readout mostra log|det J| = s(xA) positivo onde a grade alarga e negativo onde encolhe. Note que a dimensão condicionante nunca se move: por isso camadas reais alternam a metade transformada com permutações.
Um flow de verdade treinando no navegador: K camadas de affine coupling alternando a metade transformada, cada uma com uma coupling network MLP pequena que produz s e t. O treino minimiza a NLL vinda da mudança de variável, −log pX(x) = ½‖f(x)‖² − Σℓ sℓ + log(2π): o primeiro termo puxa z = f(x) para a gaussiana base e o log-determinante paga o preço da deformação de volume. Para amostrar, basta passar z ∼ 𝒩(0, I) pelas inversas em ordem contrária. Use ⏭ para ver as amostras atravessando o flow camada a camada.
Dados (ciano), amostras do flow (laranja) e densidade aprendida pX(x) (mapa de cores)
NLL média por passo (ciano: bruta, laranja: média móvel)
💡 Antes de treinar as amostras são quase a própria gaussiana base. Clique ▶ Treinar e observe a NLL despencar nos primeiros segundos enquanto a nuvem laranja se parte em duas luas. Compare K = 2 com K = 6: com poucas camadas o flow não consegue separar os modos (a expressividade vem da composição). Pause e clique ⏭ repetidamente: cada clique mostra as amostras após mais uma camada inversa, a deformação gradual de 𝒩(0, I) até os dados. Nos anéis, repare como o flow precisa "rasgar" a gaussiana para cobrir um suporte com buraco.