Criando Pokémons com Deep Learning

Note: an english version of this article is avaliable at https://medium.com/infosimples/creating-pokemon-with-artificial-intelligence-d080fa89835b

Já sabemos que várias técnicas de Deep Learning são empregadas nas mais diversas atividades do nosso dia-a-dia, tais como: recomendação de músicas, controle de redes de semáforos, distribuição de propagandas, além de incontáveis outras aplicações.

Em 2015, o pesquisador Ian Goodfellow publicou um artigo que introduz o conceito de Generative Adversarial Networks (GANs). De maneira resumida, as GANs são compostas por dois modelos de Machine Learning:

  • Um modelo gerador (G) que aprende algumas características de um conjunto de dados, e com isso tenta "criar" novos dados "parecidos" com os originais;
  • Um modelo discriminador (D) que deve ser capaz de diferenciar dados originais dos sintetizados pelo gerador.

O processo de treinamento consiste em fazer o modelo G sintetizar dados que o modelo D julgue como reais.

Exemplo de GAN que gera caracteres escritos à mão

Uma aplicação das GANs é gerar imagens em alta resolução a partir de imagens de baixa resolução. Para isso, a GAN tem sua rede geradora treinada com imagens grandes (output) que são reduzidas no input.

Comparação de técnicas de aumento de resolução: à esquerda, utlizando o método clássico Nearest Neighbor. À direita, utilizando GANs

Nos últimos anos, as GANs tem sido utilizadas para gerar imagens a partir de conjuntos específicos, como pinturas:

Imagens de paisagens geradas pelo modelo GANGogh

Um caso curioso de aplicação de GANs foi introduzido no artigo Towards the Automatic Anime Characters Creation with Generative Adversarial Networks. Nesse artigo, os autores coletaram 42 mil imagens de rostos de personagens femininas de animes e as usaram para alimentar o treinamento das GANs.

Imagens de personagens de anime produzidas por GANs

Foi a partir daí que surgiu a ideia: criar novos tipos de Pokémons a partir dos que já existem, com o uso de GANs.


PokéGAN

Tendo como base a rede geradora de personagem de animes descrita acima, foi desenvolvida uma rede capaz de gerar novos pokémons, alimentada por imagens retiradas do site oficial de Pokémon.

À esquerda, miniaturas de Pokémons (40x40 px). À direita, imagens completas (425x425 pixels).

Antes de iniciar cada treinamento, as imagens eram separadas por categorias, tais como cor predominante, tipo de pokémon (fogo, água, etc.) e algumas outras características. Isso serve para que a GAN seja capaz de extrair features mais significativas do conjunto de treinamento. Exemplo: pokémons do tipo Flying quase sempre tem asas.

GANs se mostram muito difíceis de serem treinadas em arquiteturas demasiadamente complexas. Por exemplo: se o discriminador for mais complexo do que o gerador, ele provavelmente não conseguirá ser enganado durante o treinamento. Isso faria a rede geradora nunca ter seus pesos atualizados, e portanto incapaz de gerar imagens significativas.

Tendo isso em mente, foram utilizadas arquiteturas básicas para as duas redes:

Geradora

O input é um minibatch de um tensor de formato [1, 1, 100] preenchido com ruído aleatório no intervalo [-1, 1]. A saída é um minibatch de um tensor [64, 64, 4]. São 4 canais de cor, com a inclusão do canal Alpha (transparência). Assim, as imagens geradas também terão fundo transparente.

_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) (None, 1, 1, 100) 0
_________________________________________________________________
conv2d_transpose (Conv2DTran (None, 4, 4, 512) 819712
_________________________________________________________________
batch_normalization (BatchNo (None, 4, 4, 512) 2048
_________________________________________________________________
leaky_re_lu (LeakyReLU) (None, 4, 4, 512) 0
_________________________________________________________________
conv2d_transpose_1 (Conv2DTr (None, 8, 8, 256) 2097408
_________________________________________________________________
batch_normalization_1 (Batch (None, 8, 8, 256) 1024
_________________________________________________________________
leaky_re_lu_1 (LeakyReLU) (None, 8, 8, 256) 0
_________________________________________________________________
conv2d_transpose_2 (Conv2DTr (None, 16, 16, 128) 524416
_________________________________________________________________
batch_normalization_2 (Batch (None, 16, 16, 128) 512
_________________________________________________________________
leaky_re_lu_2 (LeakyReLU) (None, 16, 16, 128) 0
_________________________________________________________________
conv2d_transpose_3 (Conv2DTr (None, 32, 32, 64) 131136
_________________________________________________________________
batch_normalization_3 (Batch (None, 32, 32, 64) 256
_________________________________________________________________
leaky_re_lu_3 (LeakyReLU) (None, 32, 32, 64) 0
_________________________________________________________________
conv2d (Conv2D) (None, 32, 32, 64) 36928
_________________________________________________________________
batch_normalization_4 (Batch (None, 32, 32, 64) 256
_________________________________________________________________
leaky_re_lu_4 (LeakyReLU) (None, 32, 32, 64) 0
_________________________________________________________________
conv2d_transpose_4 (Conv2DTr (None, 64, 64, 4) 4100

Discriminadora

A rede tem como input um minibatch de imagens no formato [64, 64, 4] e tem como resultado uma minibatch de apenas 1 valor binário (0 se acha que a imagem é falsa, 1 se acha que a imagem é verdadeira).

_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_2 (InputLayer) (None, 64, 64, 4) 0
_________________________________________________________________
conv2d_1 (Conv2D) (None, 32, 32, 64) 4160
_________________________________________________________________
leaky_re_lu_5 (LeakyReLU) (None, 32, 32, 64) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 16, 16, 128) 131200
_________________________________________________________________
batch_normalization_5 (Batch (None, 16, 16, 128) 512
_________________________________________________________________
leaky_re_lu_6 (LeakyReLU) (None, 16, 16, 128) 0
_________________________________________________________________
conv2d_3 (Conv2D) (None, 8, 8, 256) 524544
_________________________________________________________________
batch_normalization_6 (Batch (None, 8, 8, 256) 1024
_________________________________________________________________
leaky_re_lu_7 (LeakyReLU) (None, 8, 8, 256) 0
_________________________________________________________________
conv2d_4 (Conv2D) (None, 4, 4, 512) 2097664
_________________________________________________________________
batch_normalization_7 (Batch (None, 4, 4, 512) 2048
_________________________________________________________________
leaky_re_lu_8 (LeakyReLU) (None, 4, 4, 512) 0
_________________________________________________________________
flatten (Flatten) (None, 8192) 0
_________________________________________________________________
dense (Dense) (None, 1) 8193
=================================================================

Resultados

Os tempos de treinamento são curtos. Com uma GPU Nvidia 1070, em 15 minutos as duas redes praticamente estabilizavam seus pesos.

A seguir, temos alguns conjuntos de imagens geradas pela GAN.

Pokémons gerados somente a partir de pokémons da cor azul: à esquerda, imagens completas; à direita, miniaturas.
Pokémons gerados somente a partir de pokémons do tipo Water.
Pokémons gerados somente a partir de pokémons da cor marrom.
Pokémons gerados somente a partir de pokémons do tipo Flying.
Pokémons gerados somente a partir de miniaturas de pokémons amarelos. Como existem poucos exemplos nessa distribuição (96), é fácil perceber que são gerados alguns Pikachus, pois existem 14 representações diferentes de Pikachu (~15% da distribução). A variações consistem em bonés e roupas diferentes.
Gif que mostra o processo de geração de pokémons azuis, a partir de ruído aleatório.

Apesar da parte interna das imagens não parecer nada além de um aglomerado de cores, os formatos são bastante convincentes. É possível ver membros que se assemelham com asas em pokémons do tipo Flying, nadadeiras em pokémons do tipo Water, patas, caudas e chifres no restante das imagens, além de de ficar claro que a cabeça fica em cima do resto do corpo.

Outro ponto interessante é que todas as miniaturas geradas possuem contornos de cores escuras, assim como as miniaturas originais.


Estilizando resultados

Usando um pouco a imaginação, uma mesa digitalizadora e minha falta de talento em desenho, acabei transformando alguns dos blobs cuspidos pela GAN em algo mais próximo de um Pokémon de verdade.

Pokémon gerado a partir de pokémons com cor predominantemente azul. Provavelmente seu tipo seria Water/Poison.
Pokémon gerado a partir de pokémons com cor predominantemente azul. Seu tipo seria Rock/Steel? Ou Psychic/Rock? Díficil pensar nessas coisas.

Ok, admito que não são os pokémons mais bonitos que existem. Mas pelo menos não estão tão distantes assim da realidade.


Mas no que isso impacta o nosso "mundo real"?

Pintura “Edmond de Belamy, from La Famille de Belamy”, gerado por uma GAN. Note que a assinatura do “autor” é uma equação genérica que descreve uma GAN.
  • Empresas de moda e vestuário podem utilizar GANs para gerar imagens de seus clientes com as roupas do catálogo, para ajudá-los no processo de compra.
Exemplo de GANs que geram imagens de pessoas com peças de roupa específicas
  • Desenvolvedores de videogames podem usar dados reais de relevo do mundo para gerar terrenos em seus jogos, através de GANs. Assim, os jogos que dependem de geração automática de terrenos tendem a ficar mais realistas (https://arxiv.org/pdf/1707.03383.pdf).
  • A industria farmacêutica costuma ter bastante dificuldade em criar novas moléculas orgânicas que tenham aplicações práticas. Com o uso de GANs, é possível gerar estruturas orgânicas que se assemelhem com as moléculas existentes na natureza, direcionando melhor o foco desses estudos (https://arxiv.org/pdf/1708.08227.pdf).
  • DeepFakes: GANs podem ser utilizadas para trocar rostos de pessoas em fotos e vídeos. Isso pode ter sérias implicações, como geração de falsas provas contra uma pessoa. No futuro (não muito distante) provas em vídeo poderão até mesmo não ter nenhuma validade em tribunais.
    Um exemplo de implementação de DeepFakes com GANs pode ser visto em https://github.com/shaoanlu/faceswap-GAN.

Com isso, podemos ver que as GANs terão aplicações "mais úteis" do que a síntese de imagens de Pokémons. Porém, enquanto a técnica ainda se desenvolve, todas experimentações são bem-vindas.


Referências

- Goodfellow, Ian et. al. Generative Adversarial Networks:
https://arxiv.org/abs/1406.2661
- Jin, Yanghua et. al. Towards the Automatic Anime Characters Creation with Generative Adversarial Networks:
https://arxiv.org/abs/1708.05509
- Keras GAN Amimeface implementation:
https://github.com/forcecore/Keras-GAN-Animeface-Character/