Alura > Cursos de Inteligência Artificial > Cursos de IA para Dados > Conteúdos de IA para Dados > Primeiras aulas do curso TensorFlow/Keras: gerando imagens com Deep Learning

TensorFlow/Keras: gerando imagens com Deep Learning

Criando um gerador - Apresentação

Olá, boas-vindas a este curso de geração de imagens com Deep Learning utilizando TensorFlow e Keras. Meu nome é Allan Spadini.

Audiodescrição: Allan se descreve como um homem branco. Tem olhos escuros e cabelos curtos e castanhos. Usa uma camiseta preta. Ao fundo, estúdio da Alura com parede azul e estante com plantas e decorações à direita.

O que vamos aprender?

Neste curso, vamos aprender a gerar imagens utilizando o TensorFlow. Mais especificamente, vamos estudar sobre a geração de imagens utilizando GANs (generative adversarial network ou redes generativas adversativas) e também as redes difusoras.

Além disso, vamos aprender a utilizar o Stable Diffusion, que é um modelo pré-treinado. Desse modo, poderemos combinar tanto as potencialidades do TensorFlow quanto do modelo Stable Diffusion.

Pré-requisitos

Para acompanhar este curso, é necessário já conhecer o Python e também o TensorFlow voltado para a classificação de imagens.

Ao finalizar este curso, você será capaz de gerar nossos próprios projetos, criar nossos próprios modelos geradores de imagens, além de utilizar modelos geradores de imagens como o Stable Diffusion.

Esperamos te encontrar nos próximos vídeos deste curso. Até lá!

Criando um gerador - Carregando o fmnist

Nós trabalhamos para uma consultoria de IA que foi contratada por uma empresa de marketing que quer ter seu próprio gerador de imagens, sem depender de uma API, garantindo que não haverá problemas com direitos autorais.

Vamos começar pensando em como construir um gerador de imagens. A empresa quer criar um gerador de imagens para produtos, especificamente roupas. Para isso, vamos estudar o uso do Fashion MNIST, um dataset que nos permitirá criar imagens de roupas.

Carregando base de dados

Para iniciar nosso estudo, estamos utilizando a plataforma Lightning AI para a construção do projeto.

É possível usar tanto a Lightning AI quanto o Google Colab. Optamos por essa plataforma para agilizar o processo durante a gravação.

Na primeira célula, vamos importar a biblioteca TensorFlow com apelido tf:

import tensorflow as tf

Em seguida, importamos a biblioteca Matplotlib, especificamente o matplotlib.pyplot:

import matplotlib.pyplot as plt

Também importamos a biblioteca warnings para evitar mensagens relacionadas à GPU disponível no Lightning AI.

Em seguida, vamos fazer uma chamada warnings.filterwarnings(), passando a string ignore.

import warnings

warnings.filterwarnings('ignore')

Utilizaremos a base de dados Fashion MNIST, que será importada diretamente através do TensorFlow. Não utilizaremos um dataset disponível localmente.

Para carregá-lo, usamos a função tf.keras.datasets.fashion_mnist.load_data() que retorna tanto um conjunto de treino quanto um conjunto de teste.

Nesse caso, vamos armazená-lo em duas variáveis de treino, train_images e train_labels. Afinal, a avaliação da qualidade das imagens geradas nesse projeto será feita de forma qualitativa, analisando visualmente se estão boas ou não.

Para descartar o conjunto de teste, vamos usar o underscore (_) como um placeholder para o segundo valor retornado que não será utilizado.

(train_images, train_labels), _ = tf.keras.datasets.fashion_mnist.load_data()

Normalizando as imagens

Em seguida, adicionaremos uma dimensão extra às imagens de treino e realizaremos a normalização dessas imagens. Essa dimensão extra é necessária quando trabalhamos com imagens coloridas.

Para isso, vamos criar a variável train_images que será igual a train_images.reshape(), passando train_images.shape[0], 28, 28, 1. Assim, as dimensões de todas as imagens do Fashion MNIST serão 28 por 28 pixels.

Após o reshape, faremos um astype() para convertê-las para float32.

train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')

Agora, normalizamos as imagens, subtraindo train_images por 127.5 e dividindo esse resultado por 127.5.

train_images = (train_images - 127.5) / 127.5

Esse processo ajusta os valores dos pixels para uma escala de cinza entre -1 e 1, já que normalmente variam de 0 a 255.

Preparando base para treinamento

Feito isso, vamos definir o tamanho do batch, que será o tamanho dos blocos e conjuntos de dados carregados na rede neural. Definiremos um batch_size de 256.

batch_size = 256

Agora, embaralhamos e carregamos os dados, construindo o dataset de treino com base nesse batch_size, que é o carregamento em blocos.

Para isso, vamos criar a variável train_ds que será igual à função tf.data.Dataset.from_tensor_slices(), passando train_images como argumento.

Em seguida, aplicamos a função shuffle() para embaralhar os dados com um valor de 60000. Por fim, faremos um batch() para dividir o dataset de acordo com o valor armazenado em batch_size.

train_ds = tf.data.Dataset.from_tensor_slices(train_images).shuffle(60000).batch(batch_size)

Em resumo, estamos criando um programa de carregamento do dataset para alimentá-lo de maneira otimizada durante o treinamento da rede neural.

Exibindo imagens de exemplo

Além disso, podemos visualizar as imagens dentro desse dataset, utilizando o Matpolotlib, para entender que tipo de imagem estamos tentando gerar.

Primeiro, vamos definir o número de imagens a serem mostradas, criando a variável num_images_to_show e atribuindo a ela o valor 10.

Na próxima linha, criamos uma figura usando o plt.figure() e estipulando o parâmetro figsize igual a uma tupla (10, 10).

Depois, faremos um loop for para percorrer as primeiras imagens do dataset. Para isso, escrevemos for i in range(num_images_to_show).

Para cada imagem, vamos criar um plt.subplot() passando o argumento 1 para que todas as imagens sejam exibidas em uma única linha. Depois passamos num_images_to_show, que é o número total de subplots. Por fim, especificamos o índice i + 1 para posicionar cada imagem em seu respectivo subplot.

Para desenhar as imagens efetivamente, vamos utilizar o plt.imshow(), passando train_images[i].reshape(28, 28) para garantir que as imagens sejam redimensionadas para 28x28 pixels, além de definir cmap='gray' para exibi-las em escala de cinza.

Finalmente, como não precisamos dos eixos, vamos definir plt.axis() como off.

num_images_to_show = 10
plt.figure(figsize=(10, 10))
for i in range(num_images_to_show):
    plt.subplot(1, num_images_to_show, i + 1)
    plt.imshow(train_images[i].reshape(28, 28), cmap='gray')
    plt.axis('off')
plt.show()
Dez fotografias quadradas de artigos de roupa em preto e branco, enfileiradas lado a lado.

Ao visualizar as dez primeiras imagens do dataset, descobrimos que essa base contém fotografias de tênis, camisetas, camisas femininas, vestidos, entre outros. O gerador que vamos construir para a empresa de marketing vai gerar esse tipo de imagem.

No próximo vídeo, começaremos a construir esse gerador.

Criando um gerador - Estruturando um gerador

Já entendemos como são as imagens que queremos gerar. Desejamos criar essas imagens de roupas em escala de cinza.

Agora, o desafio é pensar em como definir uma rede neural que gera imagens.

Estruturando um gerador

Para começar a pensar nisso, vamos construir uma função geradora de imagens, que será uma rede neural.

Primeiramente, vamos importar as funções layers e Sequential do TensorFlow:

from tensorflow.keras import layers
from tensorflow.keras.models import Sequential

Queremos criar uma sequência de camadas de uma rede neural. Assim, vamos criar a função constroi_gerador(). Dentro dessa função, definiremos nosso modelo. Nesse caso, o modelo será atribuído a Sequential().

def constroi_gerador():
    modelo = Sequential()

Agora, precisamos de uma sequência de camadas, semelhante à de um classificador.

No classificador, queremos transformar uma imagem de alta dimensionalidade (um quadrado com diversos pixels, que são diversos números) em algo menor, como, por exemplo, um único valor para indicar se estamos na classe 0 ou 1 em um problema de classificação binária.

Em contrapartida, no caso do gerador, partimos de uma classe ou de um ruído (um número aleatório) e tentamos gerar algo mais complexo, como uma imagem.

Vamos criar uma sequência de camadas da rede neural que partirá de um vetor de 100 valores aleatórios, transformando-os em algo mais complexo: uma imagem com dimensões de 28x28, como as imagens do Fashion MNIST.

Para construir a camada de entrada, usamos a função modelo.add(), passando layers.Input() e especificando a forma (shape) dos dados como um vetor (100, ).

modelo.add(layers.Input(shape=(100,)))

Desse modo, nosso input será um ruído, um vetor unidimensional de 100 valores aleatórios.

Continuando a construção da sequência de camadas, vamos adicionar uma camada densa de rede neural através da função modelo.add() novamente.

Dessa vez, passamos layers.Dense(), definindo o tamanho da camada que terá 7*7*256 valores. Também passamos o parâmetro use_bias igual a False.

modelo.add(layers.Dense(7*7*256, use_bias=False))

Assim, definimos o número de neurônios para essa camada densa, aumentando as dimensões de 100 para uma camada com mais dimensões.

Em uma imagem, temos pixels entre 0 e 255, então, temos 256 valores. E, apesar de querer chegar a uma dimensão de 28x28, começamos com valores 7x7, equivalente a uma imagem de 7x7 pixels. A medida que passamos pelas camadas da rede neural, aumentaremos esses valores.

Também adicionaremos uma camada de normalização através do modelo.add(), passando layers.BatchNormalization().

modelo.add(layers.BatchNormalization())

E passaremos a função de ativação para essa camada também usando modelo.add() e passando layers.LeakyReLU() que é uma ReLu (Rectified Linear Unit) com algumas funcionalidades a mais.

modelo.add(layers.LeakyReLU())

Repare que estamos passando as funções de ativação separadamente para o código ficar mais legível. Mas podemos pensar em tudo isso como sendo uma única camada que passamos de forma mais sequencial.

Seguimos com a sequência das camadas, fazendo o Reshape da camada. Para isso, fazemos modelo.add(), passando layers.Reshape() com uma matriz de 7, 7, 256.

modelo.add(layers.Reshape((7, 7, 256)))

Dessa forma, que era uma camada densa de valores 7*7*256 agora tem dimensões equivalentes a uma imagem.

Em seguida, digitamos modelo.add() para adicionar uma nova camada layers.Conv2DTranspose() que é uma camada de deconvolução. Assim como em uma camada convolucional, passaremos os seguintes parâmetros:

Assim, teremos 128 filtros quadrados de 5x5 percorrendo os conjuntos de pixels das imagens.

modelo.add(layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False))

Enquanto as camadas convolucionais tendem a reduzir a dimensionalidade das imagens quando trabalhamos com a classificação de imagens, a camada de deconvolução tende a desfazer o efeito de uma convolução, aumentando a dimensão da imagem, ou seja, aumentando os pixels da imagem.

Para padronizar, aplicaremos essa camada com a sequência de camadas já aplicadas, efetuando novamente a normalização e aplicando uma ativação:

modelo.add(layers.BatchNormalization())
modelo.add(layers.LeakyReLU())

Copiando a estrutura do Conv2DTranspose(), definiremos mais uma camada convolucional. Dessa vez, vamos aplicar 64 filtros, alterar strides para (2, 2) e manter o restante dos parâmetros.

Logo, aplicamos novamente uma normalização e uma ativação.

modelo.add(layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False))
modelo.add(layers.BatchNormalization())
modelo.add(layers.LeakyReLU())

Não fizemos o Reshape novamente porque ele só é necessário quando estamos convertendo os valores de uma camada densa (que organiza os dados em um formato linear, como um vetor 1D) para um formato bidimensional (2D), que se assemelha a uma imagem.

Feito isso, vamos finalizar com uma camada que deixará o resultado com apenas uma única imagem. Assim, vamos transformar as 64 imagens, resultantes da aplicação de 64 filtros, em uma única imagem.

Para isso, novamente adicionaremos uma camada convolucional, copiando a estrutura de Conv2DTranspose(). Dessa vez, alteraremos apenas o número de filtros para 1.

Além disso, depois do use_bias, acrescentaremos o parâmetro activation igual a 'tanh', uma tangente hiperbólica.

modelo.add(layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))

A ativação tanh retornará valores entre -1 e 1, lembrando que normalizamos os dados para ficarem entre esses valores.

Por fim, retornamos o modelo na função:

def constroi_gerador():
    modelo = Sequential()

    modelo.add(layers.Input(shape=(100,)))
    modelo.add(layers.Dense(7*7*256, use_bias=False))
    modelo.add(layers.BatchNormalization())
    modelo.add(layers.LeakyReLU())

    modelo.add(layers.Reshape((7, 7, 256)))
    modelo.add(layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False))
    modelo.add(layers.BatchNormalization())
    modelo.add(layers.LeakyReLU())

    modelo.add(layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False))
    modelo.add(layers.BatchNormalization())
    modelo.add(layers.LeakyReLU())

    modelo.add(layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))

    return modelo

Executamos o código com "Shift + Enter" para construir a função.

No próximo vídeo, aplicaremos essa função para tentar gerar algum tipo de imagem. Até o próximo vídeo!

Sobre o curso TensorFlow/Keras: gerando imagens com Deep Learning

O curso TensorFlow/Keras: gerando imagens com Deep Learning possui 156 minutos de vídeos, em um total de 50 atividades. Gostou? Conheça nossos outros cursos de IA para Dados em Inteligência Artificial, ou leia nossos artigos de Inteligência Artificial.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda IA para Dados acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas