Phase 08 - Lesson 03
GANs — Gerador vs Discriminador
O truque de Goodfellow em 2014 foi pular a densidade completamente. Duas redes. Uma cria falsificações. Outra as detecta. Elas lutam até que as falsificações sejam indistinguíveis das reais. Não deveria funcionar. Frequentemente não funciona. Quando funciona, as amostras ainda são as mais nítidas da literatura para domínios específicos.
Tipo: Build Linguagens: Python Pré-requisitos: Fase 3 · 02 (Backprop), Fase 3 · 08 (Optimizers), Fase 8 · 02 (VAE) Tempo: ~75 minutos
O Problema
VAEs produzem amostras borradas porque a perda do decodificador MSE é Bayes-ótima para a imagem média — e a média de muitos dígitos plausíveis é um dígito difuso. Você quer uma perda que recompense a plausibilidade, não a proximidade pixel a pixel de qualquer alvo único. Não há uma forma fechada para plausibilidade. Você tem que aprendê-la.
A ideia de Goodfellow: treinar um classificador D(x) para distinguir imagens reais de falsas. Treinar um gerador G(z) para enganar D. O sinal de perda para G é o que quer que D atualmente pense que faz algo parecer real. Este sinal é atualizado à medida que G melhora, perseguindo um alvo móvel. Se ambas as redes convergirem, G terá aprendido a distribuição dos dados sem nunca precisar escrever log p(x).
Isso é treinamento adversarial. A matemática é um jogo minimax:
min_G max_D E_real[log D(x)] + E_fake[log(1 - D(G(z)))]
Em 2026, as GANs não são mais o gerador SOTA (o trono foi tomado por difusão e flow matching). Mas o StyleGAN 2/3 continua sendo o modelo de rostos mais nítido já lançado, os discriminadores de GAN são usados como perdas perceptuais no treinamento de difusão, e o treinamento adversarial alimenta as destilações rápidas de 1 passo (SDXL-Turbo, SD3-Turbo, LCM) que permitem entregar difusão em tempo real.
O Conceito
Gerador G(z). Mapeia um vetor de ruído z ~ N(0, I) para uma amostra x̂. Uma rede em formato de decodificador (densa ou convolucional transposta).
Discriminador D(x). Mapeia uma amostra para uma probabilidade escalar (ou pontuação/score). Real → 1, falsa → 0.
Perda. Duas atualizações alternadas:
- Treinar
D:loss_D = -[ log D(x) + log(1 - D(G(z))) ]. Entropia cruzada binária com real=1, falso=0. - Treinar
G:loss_G = -log D(G(z)). Esta é a forma não saturada (non-saturating) que Goodfellow usou (a originallog(1 - D(G(z)))satura e elimina os gradientes quandoDestá confiante).
Loop de treinamento. Um passo de D, um passo de G. Repita.
Por que funciona. Se G se ajusta perfeitamente a p_data, então D não pode fazer melhor do que o acaso e gera 0.5 em todos os lugares; G não recebe mais gradientes. Equilíbrio.
Por que falha. Colapso de modo (mode collapse — G encontra um modo que D não consegue classificar e o produz para sempre), gradiente evanescente (D aprende rápido demais e log D satura), instabilidade de treinamento (taxas de aprendizado, tamanhos de lote, qualquer coisa).
Variantes que fizeram as GANs funcionarem
| Ano | Inovação | Solução |
|---|---|---|
| 2015 | DCGAN | Conv/deconv, batch norm, LeakyReLU — a primeira arquitetura estável. |
| 2017 | WGAN, WGAN-GP | Substitui a BCE pela distância de Wasserstein + penalidade de gradiente (gradient penalty). Corrige o gradiente evanescente. |
| 2017 | Normalização espectral | Limita o discriminador por Lipschitz. Ainda usado em discriminadores de 2026. |
| 2018 | Progressive GAN | Treina primeiro em baixa resolução, adiciona camadas. Primeiros resultados em megapixel. |
| 2019 | StyleGAN / StyleGAN2 | Rede de mapeamento (mapping network) + normalização de instância adaptativa (adaptive instance norm). Estado da arte para fotorrealismo de domínio fixo. |
| 2021 | StyleGAN3 | Sem aliasing (alias-free), equivariante a translação — ainda o padrão-ouro para rostos em 2026. |
| 2022 | StyleGAN-XL | Condicional, ciente de classe (class-aware), escala maior. |
| 2024 | R3GAN | Rebranding com regularização mais forte; funciona em 1024² sem truques. |
Build It
code/main.py treina uma pequena GAN em dados 1-D: uma mistura de duas Gaussianas. O gerador e o discriminador são MLPs de uma única camada oculta. Nós implementamos a passada para frente (forward), para trás (backward) e o loop minimax manualmente. O objetivo é ver os dois principais modos de falha (colapso de modo + gradiente evanescente) à medida que ocorrem.
Passo 1: perda não saturada
A perda clássica de Goodfellow log(1 - D(G(z))) vai para 0 quando D classifica a amostra falsa de G como falsa com alta confiança. Nesse ponto, o gradiente para G é essencialmente zero — G não consegue melhorar. A forma não saturada -log D(G(z)) possui a assíntota oposta: ela explode quando D está confiante, dando a G um sinal forte.
def g_loss(d_fake):
# maximize log D(G(z)) <=> minimize -log D(G(z))
return -sum(math.log(max(p, 1e-8)) for p in d_fake) / len(d_fake)
Passo 2: um passo de discriminador por passo de gerador
for step in range(steps):
# train D
real_batch = sample_real(batch_size)
fake_batch = [G(z) for z in sample_noise(batch_size)]
update_D(real_batch, fake_batch)
# train G
fake_batch = [G(z) for z in sample_noise(batch_size)] # fresh fakes
update_G(fake_batch)
Amostras falsas frescas para G, caso contrário os gradientes ficam desatualizados.
Passo 3: monitorar o colapso de modo
if step % 200 == 0:
samples = [G(z) for z in sample_noise(500)]
mode_a = sum(1 for s in samples if s < 0)
mode_b = 500 - mode_a
if min(mode_a, mode_b) < 50:
print(" [!] mode collapse: one mode is starved")
O sintoma clássico: um dos dois modos reais deixa de ser gerado. O discriminador para de corrigi-lo porque ele nunca é visto como falso.
Armadilhas
- Discriminador forte demais. Reduza a taxa de aprendizado de D em 2-5x ou adicione ruído de instância/camada. Se D atingir mais de 95% de acurácia, G está morto.
- Gerador memoriza um modo. Adicione ruído às entradas de D, use uma camada de discriminação de minilote (minibatch-discriminator) ou mude para WGAN-GP.
- Vazamento de estatísticas no Batch Norm. O fluxo do lote real + lote falso através da mesma camada BN mistura suas estatísticas. Use normalização de instância (instance norm) ou normalização espectral (spectral norm).
- Manipulação do Inception Score. FID e IS são ruidosos com poucas amostras. Use ≥10k amostras na avaliação.
- Amostragem em um único passo é uma mentira para tarefas condicionais. Você ainda precisa de escalas CFG, truques de truncamento e reamostragem para obter saídas utilizáveis.
Use It
A pilha de GAN em 2026:
| Situação | Escolha |
|---|---|
| Rostos humanos fotorrealistas, pose fixa | StyleGAN3 (mais nítido, menor) |
| Rostos de anime / estilizados | StyleGAN-XL ou LoRA de Stable Diffusion |
| Tradução de imagem para imagem | Pix2Pix / CycleGAN (Fase 8 · 04) ou ControlNet (Fase 8 · 08) |
| Texto para imagem rápido de 1 passo | Destilação adversarial de difusão (SDXL-Turbo, SD3-Turbo) |
| Perda perceptual dentro de um treinador de difusão | Discriminador GAN pequeno em recortes de imagem (crops) |
| Qualquer coisa multimodal e aberta | Não use — use difusão ou flow matching |
As GANs são nítidas, mas limitadas. Assim que seu domínio se expandir — fotos, prompts de texto arbitrários, vídeo —, mude para difusão. O truque adversarial sobrevive como um componente (perdas perceptuais, destilação), não como um gerador independente.
Ship It
Salve outputs/skill-gan-debugger.md. A Skill analisa uma execução de GAN com falhas (curvas de perda, grade de amostras, tamanho do conjunto de dados) e gera uma lista classificada das causas prováveis, correções de uma única linha e um protocolo de reexecução.
Exercícios
- Fácil. Execute
code/main.pycom as configurações padrão. Em seguida, definaD_LR = 5 * G_LRe execute novamente. Quão rápido a perda de G colapsa para uma constante? - Médio. Substitua a perda clássica BCE de Goodfellow pela perda WGAN:
loss_D = E[D(fake)] - E[D(real)],loss_G = -E[D(fake)]e limite os pesos de D no intervalo[-0.01, 0.01]. O treinamento fica mais estável? Compare o tempo de convergência real (tempo de relógio). - Difícil. Estenda o exemplo 1-D para dados 2-D (uma mistura de 8 Gaussianas em um anel). Acompanhe quantos dos 8 modos o gerador captura nos passos 1k, 5k e 10k. Implemente discriminação de minilote (minibatch discrimination) e meça novamente.
Termos-chave
| Termo | O que dizem | O que realmente significa |
|---|---|---|
| Gerador | "G" | Rede de ruído para amostra, G: z → x̂. |
| Discriminador | "D" | Classificador D: x → [0, 1], real vs falso. |
| Minimax | "O jogo" | min_G max_D of a joint objective. |
| Perda não saturada | "A correção" | Usar -log D(G(z)) para G em vez de log(1 - D(G(z))). |
| Colapso de modo | "G memorizou uma coisa" | O gerador o produz poucas saídas distintas, apesar dos dados diversos. |
| WGAN | "Wasserstein" | Substitui BCE pela distância Earth-Mover + penalidade de gradiente; gradiente mais suave. |
| Normalização espectral | "Truque de Lipschitz" | Restringe as normas dos pesos de D para limitar sua inclinação; estabiliza o treinamento. |
| StyleGAN | "A que funciona" | Rede de mapeamento + AdaIN; melhor da categoria para rostos, ainda em 2026. |
Nota de produção: a inferência em um único passo é a vantagem duradoura das GANs
As GANs não vencem mais em qualidade de amostra para geração de domínio aberto, mas ainda vencem no custo de inferência. No vocabulário da literatura de inferência em produção, uma GAN tem:
- Sem etapas de prefill ou decode. Uma única passada para a frente de
G(z). TTFT ≈ latência total. - Sem pressão de KV-cache. O único estado são os pesos. O tamanho do lote é limitado pela memória de ativação, não pelo cache.
- Loteamento contínuo trivial. Como cada solicitação consome a mesma quantidade de FLOPs fixos, un lote estático na capacidade alvo do servidor geralmente é ideal. Nenhum agendador dinâmico (in-flight scheduler) é necessário.
É por isso que a destilação GAN (SDXL-Turbo, SD3-Turbo, ADD, LCM) é a técnica dominante para texto para imagem rápido em 2026: ela colapsa um pipeline de difusão de 20-50 passos em 1-4 passadas para a frente estilo GAN, mantendo a distribuição de uma base de difusão. A perda adversarial sobrevive como um parâmetro em tempo de treinamento para transformar geradores lentos em rápidos.
Leituras Complementares
- Goodfellow et al. (2014). Generative Adversarial Nets — o artigo original da GAN.
- Radford et al. (2015). Unsupervised Representation Learning with DCGAN — a primeira arquitetura estável.
- Arjovsky, Chintala, Bottou (2017). Wasserstein GAN — WGAN.
- Miyato et al. (2018). Spectral Normalization for GANs — SN.
- Karras et al. (2020). Analyzing and Improving the Image Quality of StyleGAN — StyleGAN2.
- Karras et al. (2021). Alias-Free Generative Adversarial Networks — StyleGAN3.
- Sauer et al. (2023). Adversarial Diffusion Distillation — SDXL-Turbo.