Ir para o conteúdo
Para as notícias Blog

Análise aprofundada de GANs

Análise aprofundada de GANs
Escrito por
Data Science Lab
Publicado em
4 de agosto de 2020

Este cão não existe

A geração de pessoas, animais, alojamentos b&b ou arte por IA é um dos muitos desenvolvimentos interessantes na área do machine learning. As redes neuronais utilizadas nestes sites pertencem à categoria das Generative Adversarial Networks, também conhecidas como GANs. O conceito de GAN foi desenvolvido em 2014 por Ian Goodfellow, então investigador na Google. No seu artigo compara o funcionamento de uma GAN ao jogo do gato e do rato entre falsificadores de dinheiro e a polícia. O objetivo dos falsificadores é produzir dinheiro o mais parecido possível com o verdadeiro, enquanto a polícia procura detetar o máximo possível de dinheiro falso. Ao identificar dinheiro falso, a polícia melhora a sua capacidade de deteção. O ‘feedback’ que os falsificadores recebem ajuda-os, por sua vez, a melhorar a produção de dinheiro falso. Este processo continua até deixar de ser possível distinguir o dinheiro falso do verdadeiro. A capacidade de geração da GAN vem, portanto, dos falsificadores, mas não seria possível sem o feedback da polícia. Numa GAN, os falsificadores e a polícia são duas redes neuronais que aprendem uma com a outra num ciclo de feedback.

O treino de uma GAN: o gerador e o discriminador aprendem um com o outro.

Além de gerar imagens, as GANs também podem ser usadas para outros fins práticos. Alguns exemplos são: [remover ruído visual (como chuva) de imagens, aumentar a resolução de imagens de baixa resolução, restaurar fotografias danificadas, gerar um emoji personalizado a partir da fotografia de uma pessoa (por exemplo, um bitmoji no snapchat), aumentar conjuntos de dados, entre muitas outras aplicações. Embora este artigo se concentre na capacidade generativa das GANs, importa referir que podem fazer muito mais do que gerar imagens. Se gosta de criar coisas visuais mas não se considera uma pessoa criativa, estas redes são ideais. O ingrediente principal são milhares de imagens para treinar a rede. Na minha primeira incursão no mundo das GAN’s, os dados vieram da competição Kaggle Generative Dog Images. Além dos dados, as discussões nessa competição foram uma boa fonte de inspiração durante o desenvolvimento de uma GAN. Mas, antes de mergulhar no código, convém perceber como são por dentro estas redes neuronais quase mágicas.

O que é uma GAN? Em termos simples, uma GAN é uma rede neuronal que aprende a distribuição de um determinado conjunto de dados, permitindo gerar novas amostras a partir dele. Este artigo centra-se na geração de imagens, mas as GANs podem gerar todo o tipo de conteúdos, desde texto a áudio. A arquitetura de uma GAN divide-se em duas partes: o gerador e o discriminador. O gerador é uma rede que gera uma amostra falsa a partir do conjunto de dados com base num chamado ‘vetor de ruído’. O vetor de ruído (z) é um vetor unidimensional de números aleatórios que pode ter qualquer comprimento. Muitas vezes, tem 100 elementos, com números extraídos de uma distribuição normal padrão.  O discriminador é uma rede que funciona como um classificador binário convencional. Durante o treino, são-lhe apresentadas amostras reais do conjunto de dados e amostras falsas geradas. Depois, classifica-as como reais ou falsas. Com base nesse feedback, o gerador ajusta os seus pesos para gerar amostras melhores, que o discriminador, espera-se, classificará como ‘reais’. É através deste processo alternado de geração e classificação que uma GAN se treina. Em pseudocódigo, o processo é o seguinte:

Fotografia de uma mulher com um cão, com caixas de deteção à volta de ambos.

Fonte

Aqui, pode ver que a GAN é treinada em duas etapas. A primeira etapa consiste em treinar o discriminator, sendo atualizados apenas os seus pesos. O generator mantém-se inalterado nesta etapa. Na segunda etapa, o generator produz novas amostras, que são classificadas pelo discriminator. Essa classificação é usada para calcular a perda da GAN e atualizar os pesos do generator. A backpropagation que, nesta etapa, passa pelo discriminator até ao generator é também a razão pela qual a GAN constitui uma única rede, apesar de ser composta por duas redes. As redes têm de estar ligadas para que os pesos do generator possam ser atualizados. Para entrar um pouco mais nos detalhes técnicos, podemos definir o discriminator (D) e o generator (G) como funções diferenciáveis. A função D(x) representa a probabilidade de o input x ser uma amostra real. O objetivo da rede é maximizar a probabilidade de esta função classificar corretamente as amostras. Por sua vez, a função G(z) mapeia um vetor de ruído z para um ponto de dados, com o objetivo de minimizar 1 - D(G(z)). Por outras palavras, procura minimizar a probabilidade de D identificar uma amostra gerada como falsa. Esta interação entre D e G é um jogo minimax, expresso pela seguinte função:

Quatro cartões com pilotos de Fórmula 1, o seu valor e os seus resultados desta temporada.

 Fonte
Até agora, não descrevemos em detalhe as arquiteturas de D e G. Isto porque estas funções variam consoante o tipo de GAN. Como podem ser estas funções e que influência têm na saída da GAN?

Arquiteturas de GAN

Tal como acontece com qualquer rede neuronal, são possíveis muitas arquiteturas, e as GANs não são exceção. Vão desde a GAN original, que usava apenas perceptrões multicamada, até à BigGAN, que utiliza técnicas como attention maps, ligações skip-z e outras. Como pode ver na imagem, a qualidade da saída de uma GAN melhorou consideravelmente. Apesar da grande diferença de qualidade, ambas as redes mantêm a mesma estrutura de generator/discriminator. A BigGAN tem, no entanto, um discriminator e um generator muito mais avançados.

Quatro cartões com pilotos de Fórmula 1, o seu valor e os seus resultados desta temporada.

Resultados da GAN original (à esquerda) e da BigGAN (à direita)

Outro exemplo de uma rede que produz resultados de elevada qualidade é a StyleGAN. Também tem a estrutura clássica de gerador e discriminador, mas o seu método de treino torna-a adequada para gerar imagens de alta resolução. Pode encontrar mais informações sobre outras redes generativas, incluindo a StyleGAN, nesta publicação do blogue. Algures entre a GAN original e a BigGAN está a Deep Convolutional GAN (DCGAN). Esta rede utiliza camadas convolucionais no discriminador e no gerador. Quando bem treinada, pode produzir bons resultados apesar da sua arquitetura relativamente simples. O gerador e o discriminador da DCGAN são redes convolucionais, sendo o discriminador uma imagem espelhada do gerador. A imagem abaixo mostra a arquitetura da rede do gerador. O discriminador espelha o gerador, com a diferença de que termina num nó sigmoid que prevê se a entrada é real ou falsa. Um pormenor importante é que a DCGAN não utiliza camadas de pooling nem de upscaling, comuns nas redes convolucionais. Em vez dessas camadas, utiliza (de)convoluções com um stride (tamanho do passo) de 2. Assim, a rede aprende as transformações, o que melhora os resultados. Os criadores da DCGAN recomendam ainda:

  • Uma taxa de aprendizagem de 0.0002 para o gerador e o discriminador
  • Utilizar normalização por batch
  • Não utilizar camadas fully connected
  • Funções de ativação ReLU para o gerador
  • Ativação Leaky ReLU para o discriminador

Diagrama com pontos coloridos e linhas de ligação.

Arquitetura do gerador da DCGAN

Com estas orientações e uma ideia de como estruturar as redes, pode parecer que o treino é simples. Infelizmente, não é: as GANs são conhecidas por serem muito difíceis de treinar. O que pode correr mal e como se resolvem esses problemas?

Como se treina uma GAN?

Treinar uma GAN é um processo delicado em que muita coisa pode correr mal. O objetivo é manter o discriminador e o gerador em equilíbrio: nenhum dos dois deve tornar-se mais “forte” do que o outro. Idealmente, o gerador e o discriminador atingem um equilíbrio de Nash. Este termo da teoria dos jogos descreve uma situação em que um jogador não altera a sua estratégia, independentemente do que o outro faça. Atingir este equilíbrio é muito difícil. É mais provável que veja a vantagem oscilar entre os dois jogadores. Isso não é um problema: mesmo nesta situação, uma GAN pode gerar bons resultados.

Grelha de imagens geradas por uma GAN, de desfocadas a reconhecíveis.

Artefactos em padrão de xadrez (à esquerda), [grelhas coloridas (ao centro), mode collapse (à direita)

Acima estão alguns exemplos do que pode correr mal durante o treino de uma GAN. Foram também os três maiores problemas que encontrei ao desenvolver uma GAN. Os artefactos em padrão de xadrez são um padrão de píxeis, ou grupos de píxeis, mais escuros ou mais claros do que os seus vizinhos. Estes artefactos surgem durante a desconvolução. Quando o tamanho do kernel e o tamanho do passo são combinados incorretamente, o filtro aprendido é aplicado várias vezes ao mesmo píxel, alterando o seu valor mais do que o dos píxeis vizinhos. Felizmente, há uma solução simples: usar um tamanho de kernel que seja múltiplo do tamanho do passo. Assim, o filtro passa exatamente 1 vez por cada píxel e evita-se o aparecimento dos artefactos. Se não for possível usar esta combinação de tamanho do passo e do kernel, outra solução é escolher um filtro com um tamanho de passo de 1 na última camada de convolução. Isto também reduz os artefactos. Pode encontrar mais informação sobre estes artefactos neste artigo da Distill. Se o resultado de uma GAN parecer uma grelha colorida, é provável que haja um desequilíbrio demasiado grande entre a taxa de aprendizagem do gerador e a do discriminador. A GAN produz este tipo de resultado quando o gerador aprende demasiado depressa ou o discriminador demasiado devagar. Isto também se vê na perda do gerador: se oscilar muito, é preciso ajustar as taxas de aprendizagem. As perdas do discriminador e do gerador devem seguir aproximadamente o mesmo padrão, sendo a perda do gerador geralmente um pouco superior à do discriminador. O maior problema no treino de uma GAN é o (partial) mode collapse. Neste caso, a saída da GAN apresenta pouca variação e os resultados parecem todos iguais. É o que se vê, por exemplo, na imagem à direita, onde é gerado um 6 para qualquer valor possível de entrada de z. Isto acontece quando o gerador ‘descobre’ que o discriminador classifica sempre uma determinada saída como real. Nesse caso, o gerador recebe feedback positivo e continua a gerar essa saída. É lógico, já que o objetivo do gerador é enganar o discriminador. Ainda não há consenso na literatura sobre a causa exata do mode collapse, mas já foram propostas muitas soluções. A mais simples é adicionar ruído aos rótulos do discriminador; voltaremos a este ponto mais adiante. Outra forma, mais complexa, de contrariar o mode collapse é a unrolled GAN. Nesta arquitetura, em cada passo do treino, o gerador é atualizado com base não só no discriminador atual, mas também no discriminador N passos à frente. Esta melhor saída do discriminador ajuda o gerador a aprender, mantendo a diversidade das suas saídas. Outras pequenas alterações à GAN que ajudaram bastante foram inverter os rótulos fornecidos ao discriminador e adicionar ruído a esses mesmos rótulos. A inversão dos rótulos (1 para falso, 0 para real) faz com que o gerador aprenda mais depressa na fase inicial do treino. Adicionar ruído significa, em primeiro lugar, que os rótulos são ‘soft’: em vez de valores fixos de 1 ou 0, têm valores aleatórios entre 1 e 0,9 ou entre 0 e 0,1. Em segundo lugar, os rótulos tinham uma probabilidade de 5% de serem invertidos, pelo que um 1 passava a 0 e vice-versa. Isto reduz a probabilidade de mode collapse e foi suficiente para o evitar nos datasets utilizados. Por fim, também se utilizou batch normalization no discriminador e spectral normalization no discriminador e no gerador. A normalização por lotes torna o treino mais estável em redes profundas, enquanto a normalização espectral produz resultados melhores e mais nítidos.

Resultados

Com estes conhecimentos e dicas, chegou a altura de começar a trabalhar. Primeiro, a GAN foi testada com o ‘hello world’ dos datasets de machine learning: os dígitos do MNIST. É um dataset relativamente simples para começar, porque é a preto e branco e apresenta pouca variação. Para manter a consistência com o artigo sobre a DCGAN, os dados foram ampliados de 28 por 28 para 64 por 64 píxeis. O MNIST revelou-se um bom dataset para começar: sem grande esforço, foi possível obter resultados nítidos, como pode ver abaixo.

Grelha de algarismos escritos à mão.

Como o dataset MNIST é uma escolha óbvia e não é particularmente interessante, a GAN também foi treinada com desenhos de ovelhas do dataset Quick, draw! da Google. A inspiração veio do livro ‘Dreaming of Electric Sheep’, que contém 10.000 desenhos de ovelhas gerados por uma GAN. Abaixo, pode ver uma imagem com desenhos do dataset e desenhos gerados pela GAN.

Grelha de desenhos simples de ovelhas feitos com linhas.

Desenhos do dataset Quick, Draw (à esquerda) e desenhos gerados (à direita)

Como se pode ver, a GAN consegue gerar dados a preto e branco. A parte mais interessante começa quando a treinamos com imagens a cores: dados 3D com vários canais de cor. A dimensão adicional da cor aumenta a complexidade, tornando muito mais difícil para a GAN gerar dados realistas. Escolher os hiperparâmetros também é mais difícil, porque muitas combinações de parâmetros resultam nas grelhas coloridas referidas anteriormente. Um conjunto de dados a cores simples para começar é o conjunto de dados Google StreetView House Number (svhn). Contém imagens em grande plano de números de porta, com variações de cor, tipo de letra e ângulo da fotografia. Para obter bons resultados com este conjunto de dados, foi necessário ajustar bastante as learning rates do discriminador e do gerador. No final, as learning rates de 0,001 e 0,0001 para o discriminador e o gerador, respetivamente, foram as que funcionaram melhor nos conjuntos de dados a cores.  Abaixo, pode ver uma amostra do conjunto de dados e as imagens geradas.

Grelha de fotografias de números de porta.

Conjunto de dados SVHN (à esquerda) e amostras geradas (à direita)

[divider line_type="No Line" custom_height="30″]

Depois de um aquecimento gradual com estes conjuntos de dados de treino, chegou a altura de trabalhar com o conjunto de dados para o qual a GAN foi criada: o conjunto de dados Stanford Dogs em que se centrava a competição Kaggle. Este conjunto de dados é muito mais complexo do que os dados SVHN. Inclui uma grande variedade de cães (em termos de cor, raça e postura) numa grande variedade de ambientes. Infelizmente, esta complexidade revelou-se demasiado difícil para a rede aprender, e é fácil distinguir as imagens geradas de imagens reais. Ainda assim, as imagens geradas têm alguma semelhança com cães. Após dias de treino e de experiências com learning rates, conseguimos finalmente uma rede que gerava bons resultados. O treino da rede final demorou 9 horas, com learning rates de 0,0001 e 0,0003 para o discriminador e o gerador, respetivamente. Pode ver abaixo uma amostra das imagens geradas pela rede.

Grelha de fotografias de cães de diferentes raças.

Alguns cães gerados pela GAN

É interessante observar que a GAN consegue representar bem algumas poses, como a cabeça de um cão virada para cima ou uma vista de perfil. Também foi interessante ver que aprendeu bem as cores e os padrões do pelo, reproduzindo-os com boa variedade.

Como avançar?

O resultado da GAN permite reconhecer cães nas imagens, mas ainda há muito espaço para melhorias. Como já referimos, o conjunto de dados Stanford Dogs é complexo devido à grande variedade dos dados. Uma possível solução é utilizar uma Conditional GAN (CGAN). Numa CGAN, o gerador e o discriminador recebem, além dos dados de entrada habituais, um rótulo de classe. Estes rótulos são fornecidos, por exemplo, sob a forma de um embedding ou de um vetor one-hot. Uma camada totalmente ligada converte os rótulos de classe para o formato adequado, antes de serem combinados com os dados de entrada originais. O conjunto resultante pode então ser processado pelo discriminador ou pelo gerador. No caso do conjunto de dados Stanford Dogs, os rótulos de classe podem, por exemplo, indicar a raça do cão. Mas também podem representar uma característica completamente diferente da imagem, como a cor do cão, o ângulo em que a fotografia foi tirada ou o fundo.

Uma vantagem da CGAN é que os rótulos ajudam a rede a distinguir melhor as variações no conjunto de dados, produzindo resultados de maior qualidade. Além disso, a arquitetura CGAN permite gerar resultados apenas de uma determinada classe, algo muito difícil com uma GAN convencional. A principal desvantagem de utilizar uma CGAN é passar de um problema não supervisionado para um problema supervisionado, o que exige um conjunto de dados rotulado.

Outra solução é usar uma Wasserstein GAN (WGAN). Uma WGAN substitui o discriminador por um elemento chamado critic. Em vez de fazer uma previsão definitiva de ‘real ou falso’, o critic atribui uma pontuação que indica o grau de autenticidade da entrada. Esta pontuação deve ser baixa para entradas reais e alta para entradas falsas, e não está limitada a valores entre 0 e 1. A WGAN também usa uma nova função de perda: a perda de Wasserstein. O funcionamento exato da perda de Wasserstein numa WGAN é demasiado complexo para explicar aqui de forma breve; pode encontrar aqui uma explicação detalhada. Uma vantagem da WGAN é que o critic pode ser otimizado sem ter em conta o que isso significa para o gerador. Numa GAN convencional, um discriminador perfeito faz com que o gerador não aprenda nada, mas, com um critic perfeito, o gerador ainda pode melhorar. Isto pode ser visto na imagem abaixo. A imagem compara os gradientes de um critic e de um discriminador perfeitos ao distinguirem duas distribuições (hipotéticas). Como se pode ver, a função sigmoide do discriminador produz gradientes pouco úteis, enquanto a ativação linear do critic produz um gradiente com o qual o gerador ainda pode aprender.

Gráfico que compara os gradientes de uma GAN convencional e de uma WGAN.

Esta propriedade do critic faz com que a WGAN deixe de ter de manter um equilíbrio entre o gerador e o critic, tornando-a mais robusta e menos propensa a falhar. Esta robustez também permite maior variação na arquitetura e nas taxas de aprendizagem do gerador e do critic/discriminador sem que o resultado deixe de fazer sentido. Uma última vantagem é que a WGAN também pode evitar o mode collapse.

Naturalmente, a melhor forma de obter resultados de alta qualidade é usar uma arquitetura state-of-the-art, como as já referidas StyleGAN ou BigGAN. Estas arquiteturas são muito complexas (mas não impossíveis) de implementar por conta própria. Além disso, é necessário bom hardware para treinar estas redes: a StyleGAN, por exemplo, precisou de 1 semana com 8 GPUs. Para um projeto ‘real’, porém, vale a pena considerar estas soluções.

Conclusão

Depois de ler muitos artigos e blogs sobre o desenvolvimento de GANs, consegui desenvolver uma versão funcional da DCGAN. Esta rede conseguiu captar a distribuição de conjuntos de dados relativamente pouco complexos e gerar novos dados a partir deles. Infelizmente, o conjunto Stanford Dogs era demasiado complexo para a implementação atual da rede.

Apesar de toda a pesquisa feita para o desenvolvimento, depressa se tornou claro que não havia uma solução única para todos os conjuntos de dados; era sempre necessário ajustar um pouco as learning rates até obter o resultado pretendido. Houve casos em que uma alteração de 0.0001 fez a diferença entre bons resultados e grelhas coloridas. Isto tornou o treino de uma GAN difícil, mas também foi um bom exercício de leitura e interpretação das losses da rede.

Investigar e desenvolver uma GAN foi uma experiência enriquecedora, e foi muito interessante ver que uma rede relativamente simples conseguia gerar bons resultados. Infelizmente, a GAN não conseguiu aprender bem a anatomia de um cão, como se vê nos resultados. Ainda assim, os resultados foram suficientemente bons para ficar no top 47% da competição Kaggle e, quando a imagem acima foi colada no Word, recebeu a descrição “Imagem com cão”. Talvez os cães não sejam suficientemente convincentes para enganar uma pessoa, mas conseguem, pelo menos, enganar os algoritmos da Microsoft.

Blog

Também poderá ter interesse nisto,

Subscreva a nossa newsletter.

Quer ser a primeira pessoa a saber quando publicamos um novo artigo no blogue?

Introduza um endereço de e-mail válido.