Pense no que um dia normal parece para você. A menos que você passe o dia inteiro trancado no seu quarto assistindo Netflix ou dormindo, é provável que você passe por muitas pessoas quando estiver indo para onde deseja ir. Algumas pessoas têm cabelos compridos, algumas têm cabelos curtos, algumas têm nariz grande, outras têm olhos pequenos.

O ponto é, com que frequência paramos e pensamos nas diferentes características do que vemos todos os dias. Alguma vez você já se perguntou, “o que faz um rosto um rosto?” É algo que tem dois olhos, um nariz, uma boca e um pouco de cabelo? Em caso afirmativo, não poderíamos escrever um programa de computador super simples que gera novos rostos combinando esses recursos?

Bem, não realmente, há uma tonelada de detalhes que estamos perdendo. Coisas como rugas, sombras e sorrisos. Coisas em que normalmente não pensamos quando nos pedem para descrever os recursos de um rosto, mas precisam estar lá para que pareça real.

Algo está errado, mas eu não consigo colocar meu dedo nisso … Fonte.
Então, como podemos realmente gerar rostos se não for apenas dizer a uma rede neural para colocar dois olhos, um nariz, uma boca e um pouco de cabelo em uma grande bola de carne e esperar que pareça razoável?

Um jogo de gato e rato
Primeiro, vamos estudar um pouco como as Redes Generais Adversariais (GANs) funcionam.

Uma GAN é, na verdade, composta de duas redes neurais, um gerador e um discriminador. O trabalho do gerador é gerar novos dados com base no que sabe e a tarefa do discriminador é ver se os dados que foram gerados são legítimos ou não. Assim, no nosso exemplo, o gerador tentaria criar novas imagens de faces e o discriminador faria o melhor para determinar se a face é real ou não.

Finja por um segundo que estamos lidando com uma galeria de arte. O gerador é alguém tentando criar peças de arte falsas e vendê-las, o discriminador é o curador tentando ver se as obras de arte são realmente reais ou não. No começo, eles são muito ruins no trabalho, mas aprendem com a experiência. O falsificador lentamente fica melhor em fingir obras de arte com base no que é aceito ou não, e o curador fica melhor em contar os reais das falsificações ao longo do tempo.

Isto é basicamente como um Generative Adversarial Net treina, o gerador recebe um número aleatório e cospe uma imagem baseada na entrada. O discriminador avalia a saída do gerador e tenta prever se a imagem é real ou não. Se adivinhar corretamente, as redes incorporam isso e atualizam seus pesos. A mesma coisa acontece se ele adivinha incorretamente.

Eventualmente, o objetivo é fazer com que as duas redes cheguem a um ponto em que o gerador seja tão bom em gerar falsificações que o discriminador estará sempre em torno de 50% de confiança.

Uma coisa a ter cuidado ao treinar uma GAN, no entanto, é que quando um lado fica bom demais em seu trabalho, pode haver problemas, porque o outro lado não poderá aprender nada. Se o curador conseguir adivinhar todas as peças corretamente, o falsificador não poderá descobrir o que ele fez de errado. Se o falsificador ficar muito bom, o curador será sempre enganado e não poderá aprender também.

Mapeando a distribuição de probabilidade
Ok, isso é muito legal, certo? Você pode treinar duas redes neurais concorrentes para tentar superar um ao outro e você acaba com um modelo generativo que faz um trabalho incrível na criação de imagens verossímeis. Mas vamos um pouco mais fundo sobre como isso realmente funciona.

O que uma Rede Adversarial Generativa está realmente fazendo é mapear a distribuição de probabilidade dos dados.

Agora o que diabos isso significa?

Podemos pensar em imagens como amostras de uma distribuição de probabilidade de alta dimensão. Sempre que você tira uma foto, você está tirando uma amostra de uma distribuição de probabilidade de pixels. Há basicamente uma probabilidade ou probabilidade de gerar algum arranjo de pixels e uma GAN mapeia a distribuição de probabilidade com base nos dados.

Para o nosso exemplo, basicamente, é aprender o que faz do rosto um rosto. Diferentes características, como olhos, narizes e bocas, têm uma representação nessa distribuição de probabilidade. Como resultado, a alteração do ruído inserido no modelo alterará algumas das qualidades da imagem que correspondem a esses números. Por amostragem dessa distribuição, podemos obter um modelo para gerar imagens inteiramente novas com base no que ele sabe sobre a probabilidade dos pixels em uma determinada parte da imagem.

Usando o DCGAN para gerar novos rostos

Uma imagem do papel DCGAN. Fonte.
O DCGAN é basicamente uma versão melhorada de uma GAN regular, mas com algumas mudanças importantes:

As funções de pool máximo são substituídas por convoluções distribuídas, permitindo que a rede aprenda sua própria downsampling e upsampling.
Livrar-se de camadas totalmente conectadas em cima de recursos convolucionais. Um exemplo é o agrupamento médio global, que geralmente aumenta a estabilidade do modelo, mas prejudica a velocidade de convergência. A solução ideal é conectar diretamente os recursos convolucionais mais altos à entrada e saída do gerador e do discriminador.
Usando a Normalização de Lote para estabilizar o aprendizado, normalizando as entradas para ter média zero e variação de unidade. Isso ajuda o fluxo de gradiente em modelos mais profundos, mas não deve ser aplicado a todas as camadas. A instabilidade do modelo é evitada quando o batchnorm não é aplicado à camada de saída do gerador e à camada de entrada do discriminador.
A ativação ReLU é usada no gerador para todas as camadas, exceto para a saída que usa Tanh. Observou-se que isso ajudou o modelo a aprender mais rapidamente e a cobrir o espaço de cores da distribuição de treinamento.
A ativação de LeakyReLU é usada no discriminador para todas as camadas.
Se você tem interesse em implementar um DCGAN, há um incrível tutorial sobre PyTorch que usa o Conjunto de dados de atributos do CelebFaces (CelebA), um conjunto de dados com mais de 200.000 fotos de rostos de celebridades.

Aqui estão alguns exemplos das imagens no conjunto de dados depois de redimensionadas para 64×64 para facilitar o treinamento:

Veja como os dois modelos estão estruturados:

Gerador(
(principal): Sequencial (
(0): ConvTranspose2d (100, 512, kernel_size = (4, 4), stride = (1, 1), bias = Falso)
(1): BatchNorm2d (512, eps = 1e-05, momento = 0,1, affine = True, track_running_stats = True)
(2): ReLU (inplace)
(3): ConvTranspose2d (512, 256, kernel_size = (4, 4), stride = (2, 2), preenchimento = (1, 1), bias = Falso)
(4): BatchNorm2d (256, eps = 1e-05, momentum = 0.1, affine = True, track_running_stats = True)
(5): ReLU (inplace)
(6): ConvTranspose2d (256, 128, kernel_size = (4, 4), stride = (2, 2), preenchimento = (1, 1), bias = Falso)
(7): BatchNorm2d (128, eps = 1e-05, momento = 0,1, affine = True, track_running_stats = True)
(8): ReLU (inplace)
(9): ConvTranspose2d (128, 64, kernel_size = (4, 4), stride = (2, 2), preenchimento = (1, 1), bias = Falso)
(10): BatchNorm2d (64, eps = 1e-05, momentum = 0.1, affine = True, track_running_stats = True)
(11): ReLU (inplace)
(12): ConvTranspose2d (64, 3, kernel_size = (4, 4), stride = (2, 2), preenchimento = (1, 1), bias = Falso)
(13): Tanh ()
)
)
Discriminador(
(principal): Sequencial (
(0): Conv2d (3, 64, kernel_size = (4, 4), stride = (2, 2), preenchimento = (1, 1), polarização = Falso)
(1): LeakyReLU (negative_slope = 0.2, no local)
(2): Conv2d (64, 128, kernel_size = (4, 4), stride = (2, 2), preenchimento = (1, 1), polarização = Falso)
(3): BatchNorm2d (128, eps = 1e-05, momento = 0,1, affine = True, track_running_stats = True)
(4): LeakyReLU (negative_slope = 0.2, no local)
(5): Conv2d (128, 256, kernel_size = (4, 4), stride = (2, 2), preenchimento = (1, 1), bias = Falso)
(6): BatchNorm2d (256, eps = 1e-05, momento = 0,1, affine = True, track_running_stats = True)
(7): LeakyReLU (negative_slope = 0.2, no local)
(8): Conv2d (256, 512, kernel_size = (4, 4), stride = (2, 2), preenchimento = (1, 1), polarização = Falso)
(9): BatchNorm2d (512, eps = 1e-05, momentum = 0.1, affine = True, track_running_stats = True)
(10): LeakyReLU (negative_slope = 0.2, no local)
(11): Conv2d (512, 1, kernel_size = (4, 4), stride = (1, 1), bias = Falso)
(12): Sigmoide ()
)
)
Então, o que acontece agora, nós treinamos uma rede neural em um monte de caras, os resultados têm que ser muito bons, certo? Bem, isso depende do que você define como bom.

E honestamente, se você não olhar muito de perto, alguns deles parecem ser verdadeiros rostos. Outros, parece que saíram de um pesadelo.

Isso não pode ser, certo? Tem que haver uma maneira melhor de fazer pessoas falsas que pareçam reais. Bem, alguns pesquisadores da Nvidia podem ter encontrado o molho secreto.

Nvidia’s StyleGAN
Depois que os resultados foram divulgados, o Generative Adversarial Model da Nvidia para gerar novas imagens recebeu muita publicidade e cobertura nas notícias e por um bom motivo. A maioria das imagens é hiper-realista e quase indistinguível das fotografias reais.

“Essas pessoas não são reais – elas foram produzidas pelo nosso gerador, que permite o controle sobre diferentes aspectos da imagem”. Fonte.
E honestamente os resultados deste modelo são bem loucos. Se eu não fosse um estudante do ensino médio, eu provavelmente tentaria treinar o modelo sozinho, mas provavelmente faria 40 anos antes de começar a produzir imagens reconhecíveis. Eles literalmente recomendam um NVIDIA DGX-1 com 8 Tesla V100 GPUs. Então, a menos que você tenha um estepe de US $ 150.000 ou uma configuração de aprendizagem profunda de nível corporativo, você também pode estar sem sorte.

Mas como isso realmente funciona?
O artigo no StyleGAN aborda uma questão importante. Mesmo que a resolução e a qualidade das imagens produzidas pela GAN estejam melhorando, ainda temos muita dificuldade em explicar o que exatamente essas máquinas estão fazendo. Ainda é como uma caixa preta. Também não entendemos o espaço latente, o mapeamento de recursos para variáveis, não há uma maneira quantitativa de comparar geradores.

Inspirando-se na transferência de estilo clássico, o objetivo era recriar o gerador de uma forma que nos permitisse ver o processo de síntese da imagem e nos ajustar o estilo das imagens em cada camada para manipular os diferentes recursos.

Em um nível alto, o StyleGAN da Nvidia faz algo parecido, onde aprende como são os diferentes aspectos das imagens sem a ajuda de humanos e depois de treinar o modelo, os estilos podem ser combinados em diferentes níveis para obter uma imagem final com e estilos finos.

Confira esta visualização doente do StyleGAN!

E esta foto para ver alguns dos estilos sendo misturados:

O futuro dos modelos gerativos
O StyleGAN da Nvidia foi um grande avanço nas redes Generative Adversarial. Algumas das imagens que foram geradas pela rede são quase indistinguíveis das fotos reais. Mas quais são algumas aplicações práticas das GANs?

AI gerou gráficos para vídeos

GANs para Super Resolution

GANs para geração de texto para imagem

mediu
Obrigado pela leitura! Se você gostou, por favor:

Me adicione no LinkedIn e siga o meu Medium para ficar atualizado com a minha jornada
Confira alguns dos meus outros projetos no meu site pessoal
Deixe algum comentário ou me envie um email (alex@alexyu.ca)
Compartilhe este artigo com sua rede