Autoencoder no TensorFlow com exemplo
⚡ Resumo Inteligente
Os autoencoders comprimem uma entrada em uma representação interna menor e, em seguida, a reconstroem, aprendendo as características mais importantes sem rótulos. Este tutorial empilha quatro camadas densas no TensorFlow e reconstrói imagens de cavalos do CIFAR-10.
O que é Autoencoder em Deep Learning?
An Codificador automático é uma ferramenta para aprender codificação de dados de forma eficiente em um não supervisionado maneira. É um tipo de rede neural artificial Isso ajuda a aprender a representação de conjuntos de dados para redução de dimensionalidade, treinando a rede neural para ignorar o ruído do sinal. É uma ótima ferramenta para recriar uma entrada.
Em termos simples, a máquina recebe, digamos, uma imagem e consegue produzir uma imagem muito semelhante. A entrada nesse tipo de rede neural não é rotulada, o que significa que a rede é capaz de aprender sem supervisão. Mais precisamente, a entrada é codificada pela rede para focar apenas nas características mais importantes. Essa é uma das razões pelas quais o autoencoder é popular para redução de dimensionalidade. Além disso, os autoencoders podem ser usados para produzir modelos de aprendizado generativo. Por exemplo, a rede neural pode ser treinada com um conjunto de rostos e, em seguida, gerar novos rostos.
Como funciona o codificador automático do TensorFlow?
O objetivo de um autoencoder é produzir uma aproximação da entrada concentrando-se apenas nos recursos essenciais. Você pode pensar por que não simplesmente aprender como copiar e colar a entrada para produzir a saída. Na verdade, um autoencoder é um conjunto de restrições que força a rede a aprender novas maneiras de representar os dados, diferente de apenas copiar a saída.
Um autoencoder típico é definido com uma entrada, uma representação interna e uma saída (uma aproximação da entrada). O aprendizado ocorre nas camadas associadas à representação interna. De fato, existem dois blocos principais de camadas que se assemelham a uma rede neural tradicional. A pequena diferença é que a camada que contém a saída deve ser igual à entrada. No diagrama abaixo, a entrada original entra no primeiro bloco, chamado de codificador. Essa representação interna comprime (reduz) o tamanho da entrada. No segundo bloco ocorre a reconstrução da entrada. Essa é a fase de decodificação.

O modelo atualizará os pesos minimizando a função de perda. O modelo é penalizado se a saída da reconstrução for diferente da entrada.
Concretamente, imagine uma imagem de 50×50 (ou seja, 2,500 pixels) e uma rede neural com apenas uma camada oculta composta por cem neurônios. O aprendizado é feito em um mapa de características 25 vezes menor que a entrada. Isso significa que a rede precisa encontrar uma maneira de reconstruir 2,500 pixels com um vetor de neurônios igual a apenas 100.
Exemplo de codificador automático empilhado
Neste tutorial sobre autoencoders, você aprenderá como usar um autoencoder empilhado. A arquitetura é semelhante à de uma rede neural tradicional. A entrada passa por uma camada oculta para ser comprimida, ou seja, ter seu tamanho reduzido, e então chega às camadas de reconstrução. O objetivo é produzir uma imagem de saída o mais próxima possível da original. O modelo precisa aprender uma maneira de realizar sua tarefa dentro de um conjunto de restrições, isto é, com uma dimensão menor.
Atualmente, os autoencoders em aprendizado profundo são usados principalmente para remover ruído de uma imagem. Imagine uma imagem com arranhões; um ser humano ainda consegue reconhecer o conteúdo. A ideia do autoencoder de remoção de ruído é adicionar ruído à imagem para forçar a rede a aprender o padrão por trás dos dados.
Outra família útil de Autoencoders Deep Learning é um autoencoder variacional. Esse tipo de rede pode gerar novas imagens. Imagine que você treine uma rede com a imagem de um homem; essa rede pode produzir novos rostos.
A tabela abaixo coloca o autoencoder empilhado construído neste tutorial ao lado de outras famílias que você provavelmente encontrará, para que a restrição que cada um adiciona seja fácil de comparar.
| Tipo de autoencoder | Restrição adicionada | Uso típico |
|---|---|---|
| Incompleto/empilhado | Camada de codificação mais estreita que a entrada | Redução de dimensionalidade, recurso extracção |
| Eliminar ruído | Ruído adicionado à entrada, alvo limpo | Limpeza de imagem e sinal |
| Escasso | Penalidade sobre o número de unidades ativas | Características interpretáveis baseadas em partes |
| Variacional | A camada de codificação aprende uma distribuição de probabilidade. | Gerando novas amostras |
Como construir um autoencoder com TensorFlow
Neste tutorial, você aprenderá como construir um autoencoder empilhado para reconstruir uma imagem.
Você utilizará o conjunto de dados CIFAR-10, que contém 60000 imagens coloridas de 32×32 pixels. O conjunto de dados do Autoencoder já está dividido em 50000 imagens para treinamento e 10000 para teste. Há até dez classes:
- Avião
- Automobile
- Pássaro
- Gato
- Veado
- Cachorro
- Sapo
- Horse
- Navio
- Truck
Você precisa baixar as imagens do Página do conjunto de dados CIFAR-10 e descompacte o arquivo. A pasta cifar-10-batches-py contém cinco lotes de dados com 10000 imagens cada, em ordem aleatória.
Antes de construir e treinar seu modelo, você precisa aplicar algum processamento de dados. Você procederá da seguinte forma:
- Importar os dados
- Converta os dados para o formato preto e branco
- Anexar todos os lotes
- Construa o conjunto de dados de treinamento
- Construa um visualizador de imagens
Nota de versão: O código neste tutorial é direcionado ao TensorFlow 1.x. No TensorFlow 2, o módulo tf.contrib não existe mais, e os placeholders, sessões e o iterador inicializável residem em tf.compat.v1. Chamar tf.compat.v1.disable_v2_behavior() após a importação é a maneira mais rápida de executar os exemplos sem alterações.
Pré-processamento de imagem
Etapa 1) Importe os dados
De acordo com o site oficial, você pode carregar os dados com o seguinte código. O código do Autoencoder carregará os dados em um dicionário contendo os dados e os rótulos. Observe que o código é uma função.
import numpy as np import tensorflow as tf import pickle def unpickle(file): import pickle with open(file, 'rb') as fo: dict = pickle.load(fo, encoding='latin1') return dict
Etapa 2) Converta os dados para o formato preto e branco
Para simplificar, você converterá os dados em escala de cinza. Ou seja, com apenas uma dimensão contra três para imagem em cores. A maior parte da rede neural funciona apenas com entrada de uma dimensão.
def grayscale(im): return im.reshape(im.shape[0], 3, 32, 32).mean(1).reshape(im.shape[0], -1)
Etapa 3) Anexe todos os lotes
Agora que ambas as funções foram criadas e o conjunto de dados carregado, você pode escrever um loop para adicionar os dados à memória. Se você observar com atenção, o arquivo descompactado com os dados se chama `data_batch_` e contém um número de 1 a 5. Você pode percorrer os arquivos e adicioná-los à variável `data`.
Ao concluir esta etapa, você converte os dados de cor para um formato de escala de cinza. Como você pode ver, o formato dos dados é 50000 e 1024. Os pixels de 32x32 agora estão achatados em 1024.
# Load the data into memory data, labels = [], [] ## Loop over the b for i in range(1, 6): filename = './cifar-10-batches-py/data_batch_' + str(i) open_data = unpickle(filename) if len(data) > 0: data = np.vstack((data, open_data['data'])) labels = np.hstack((labels, open_data['labels'])) else: data = open_data['data'] labels = open_data['labels'] data = grayscale(data) x = np.matrix(data) y = np.array(labels) print(x.shape) (50000, 1024)
Observação: Altere './cifar-10-batches-py/data_batch_' para o local real do seu arquivo. Por exemplo, para um Windows máquina, o caminho pode ser filename = 'E:\cifar-10-batches-py\data_batch_' + str(i)
Etapa 4) Construa o conjunto de dados de treinamento
Para tornar o treinamento mais rápido e fácil, você treinará um modelo apenas com as imagens de cavalos. Os cavalos possuem o rótulo 7 nos dados de rótulo. Conforme mencionado na documentação do conjunto de dados CIFAR-10, cada classe contém 5000 imagens. Você pode imprimir o formato dos dados para confirmar que existem 5,000 imagens com 1024 colunas, como mostrado no exemplo de Autoencoder do TensorFlow abaixo.
horse_i = np.where(y == 7)[0]
horse_x = x[horse_i]
print(np.shape(horse_x))
(5000, 1024)
Etapa 5) Construa um visualizador de imagens
Finalmente, você constrói uma função para plotar as imagens. Você precisará desta função para imprimir a imagem reconstruída do autoencoder.
Uma maneira fácil de imprimir imagens é usar a função imshow() da biblioteca Matplotlib. Observe que você precisa converter o formato dos dados de 1024 para 32x32 (ou seja, o formato de uma imagem).
# To plot pretty figures %matplotlib inline import matplotlib import matplotlib.pyplot as plt def plot_image(image, shape=[32, 32], cmap = "Greys_r"): plt.imshow(image.reshape(shape), cmap=cmap,interpolation="nearest") plt.axis("off")
A função leva 3 argumentos:
- Imagem: a entrada
- Forma: lista, dimensão da imagem
- Cmap: escolha o mapa de cores. Por padrão, cinza.
Você pode tentar plotar a primeira imagem no conjunto de dados. Você deveria ver um homem a cavalo.
plot_image(horse_x[1], shape=[32, 32], cmap = "Greys_r")
A chamada retorna a miniatura em tons de cinza abaixo e confirma que a remodelação de 1024 valores para uma imagem de 32×32 está correta.
Definir estimador de conjunto de dados
Muito bem, agora que o conjunto de dados está pronto para uso, você pode começar a usar o TensorFlow. Antes de construir o modelo, use o estimador de conjunto de dados do TensorFlow para alimentar a rede.
Você construirá um conjunto de dados com estimador TensorFlow. Para refrescar sua mente, você precisa usar:
- from_tensor_slices
- repetir
- fornada
O código completo para construir o conjunto de dados é:
dataset = tf.data.Dataset.from_tensor_slices(x).repeat().batch(batch_size)
Observe que x é um marcador de posição com formato [None, n_inputs]. A primeira dimensão é definida como None porque o número de imagens enviadas à rede é igual ao tamanho do lote, que é definido apenas em tempo de execução. Para mais detalhes, consulte o tutorial sobre Regressão linear com TensorFlow.
Depois disso, você precisa criar o iterador. Sem esta linha de código, nenhum dado passará pelo pipeline.
iter = dataset.make_initializable_iterator() # create the iteratorfeatures = iter.get_next()
Agora que o pipeline está pronto, você pode verificar se a primeira imagem é a mesma de antes (ou seja, um homem a cavalo).
Você definiu o tamanho do lote como 1 porque deseja alimentar o conjunto de dados com apenas uma imagem. Você pode ver a dimensão dos dados com `print(sess.run(features).shape)`. Ela é igual a (1, 1024). O valor 1 significa que uma única imagem com 1024 valores é processada a cada vez. Se o tamanho do lote for definido como 2, duas imagens passarão pelo pipeline. Não altere o tamanho do lote, caso contrário, ocorrerá um erro, pois apenas uma imagem por vez pode ser processada pela função `plot_image()`.
## Parameters n_inputs = 32 * 32 BATCH_SIZE = 1 batch_size = tf.placeholder(tf.int64) # using a placeholder x = tf.placeholder(tf.float32, shape=[None,n_inputs]) ## Dataset dataset = tf.data.Dataset.from_tensor_slices(x).repeat().batch(batch_size) iter = dataset.make_initializable_iterator() # create the iterator features = iter.get_next() ## Print the image with tf.Session() as sess: # feed the placeholder with data sess.run(iter.initializer, feed_dict={x: horse_x, batch_size: BATCH_SIZE}) print(sess.run(features).shape) plot_image(sess.run(features), shape=[32, 32], cmap = "Greys_r") (1, 1024)
O pipeline retorna o mesmo ciclista que foi plotado diretamente da matriz NumPy, o que comprova que o marcador de posição, o iterador e o tamanho do lote estão configurados corretamente.
Construa a rede
É hora de construir a rede. Você treinará um autoencoder empilhado, ou seja, uma rede com múltiplas camadas ocultas.
Sua rede terá uma camada de entrada com 1024 pontos, ou seja, 32×32, o formato da imagem.
O bloco codificador terá uma camada oculta superior com 300 neurônios e uma camada central com 150 neurônios. O bloco decodificador é simétrico ao codificador. Você pode visualizar a rede no diagrama abaixo. Observe que você pode alterar os valores das camadas oculta e central.
Construir um autoencoder é muito semelhante a qualquer outro modelo de aprendizado profundo.
Você construirá o modelo seguindo estas etapas:
- Defina os parâmetros
- Defina as camadas
- Defina a arquitetura
- Defina a otimização
- Execute o modelo
- Avalie o modelo
Na seção anterior, você aprendeu como criar um pipeline para alimentar o modelo, portanto, não é necessário criar o conjunto de dados novamente. Você construirá um autoencoder com quatro camadas. Você usará a inicialização de Xavier. Esta é uma técnica para definir os pesos iniciais de acordo com a variância da entrada e da saída. Finalmente, você usará a função de ativação ELU. Você regularizará a função de perda com um regularizador L2.
Etapa 1) Defina os parâmetros
O primeiro passo implica definir o número de neurônios em cada camada, a taxa de aprendizagem e o hiperparâmetro do regularizador.
Antes disso, você importa a função parcial. É um método melhor para definir os parâmetros das camadas densas. O código abaixo define os valores da arquitetura do autoencoder. Como mencionado anteriormente, o encoder possui duas camadas, com 300 neurônios na primeira camada e 150 na segunda. Seus valores são armazenados em n_hidden_1 e n_hidden_2.
Você precisa definir a taxa de aprendizado e o hiperparâmetro L2. Os valores são armazenados em `learning_rate` e `l2_reg`.
from functools import partial ## Encoder n_hidden_1 = 300 n_hidden_2 = 150 # codings ## Decoder n_hidden_3 = n_hidden_1 n_outputs = n_inputs learning_rate = 0.01 l2_reg = 0.0001
A técnica de inicialização de Xavier é chamada com o objeto `xavier_initializer` do estimador `contrib`. No mesmo estimador, você pode adicionar o regularizador com `l2_regularizer`.
## Define the Xavier initialization xav_init = tf.contrib.layers.xavier_initializer() ## Define the L2 regularizer l2_regularizer = tf.contrib.layers.l2_regularizer(l2_reg)
Nota de versão: O tf.contrib foi removido no TensorFlow 2. Os substitutos diretos são tf.keras.initializers.GlorotUniform() para xavier_initializer() e tf.keras.regularizers.l2() para l2_regularizer().
Etapa 2) Defina as camadas
Todos os parâmetros das camadas densas foram definidos; você pode agrupar tudo na variável `dense_layer` usando o objeto `partial`. A função `dense_layer` utiliza a ativação ELU, a inicialização de Xavier e a regularização L2 em cada chamada.
## Create the dense layer
dense_layer = partial(tf.layers.dense,
activation=tf.nn.elu,
kernel_initializer=xav_init,
kernel_regularizer=l2_regularizer)
Etapa 3) Defina a arquitetura
Se você observar o diagrama da arquitetura, notará que a rede empilha três camadas com uma camada de saída. No código abaixo, você conecta as camadas apropriadas. Por exemplo, a primeira camada calcula o produto escalar entre a matriz de características de entrada e a matriz contendo os 300 pesos. Após o cálculo do produto escalar, a saída é enviada para a função de ativação ELU. A saída se torna a entrada da próxima camada, por isso você a utiliza para calcular hidden_2 e assim por diante. A multiplicação de matrizes é a mesma para cada camada, pois você usa a mesma função de ativação. Observe que a última camada, outputs, não aplica uma função de ativação. Isso faz sentido, pois essa é a entrada reconstruída.
## Make the mat mul hidden_1 = dense_layer(features, n_hidden_1) hidden_2 = dense_layer(hidden_1, n_hidden_2) hidden_3 = dense_layer(hidden_2, n_hidden_3) outputs = dense_layer(hidden_3, n_outputs, activation=None)
Etapa 4) Defina a otimização
O último passo é construir o otimizador. Você usa o Erro Quadrático Médio (MSE) como função de perda. Se você se lembra do tutorial sobre regressão linear, sabe que o MSE é calculado pela diferença entre a saída prevista e o rótulo real. Aqui, o rótulo é a característica, pois o modelo tenta reconstruir a entrada. Portanto, você quer a média da soma dos quadrados das diferenças entre a saída prevista e a entrada. Com o TensorFlow, você pode codificar a função de perda da seguinte forma:
loss = tf.reduce_mean(tf.square(outputs - features))
Em seguida, você precisa otimizar a função de perda. Você usa o otimizador Adam para calcular os gradientes. A função objetivo é minimizar a perda.
## Optimize
loss = tf.reduce_mean(tf.square(outputs - features))
optimizer = tf.train.AdamOptimizer(learning_rate)
train = optimizer.minimize(loss)
Mais uma configuração antes de treinar o modelo. Você deseja usar um tamanho de lote de 150, ou seja, alimentar o pipeline com 150 imagens a cada iteração. Você precisa calcular o número de iterações manualmente. Isso é trivial de fazer:
Se você quiser processar 150 imagens por vez e souber que existem 5000 imagens no conjunto de dados, o número de iterações será igual a 5000 dividido por 150. Python Você pode executar os seguintes códigos e verificar se a saída é 33:
BATCH_SIZE = 150 ### Number of batches : length dataset / batch size n_batches = horse_x.shape[0] // BATCH_SIZE print(n_batches) 33
Etapa 5) Execute o modelo
Por último, mas não menos importante, treine o modelo. Você está treinando o modelo com 100 épocas. Ou seja, o modelo verá as imagens 100 vezes enquanto otimiza os pesos.
Você já está familiarizado com os códigos para treinar um modelo no TensorFlow. A pequena diferença é que os dados são enviados antes de executar o treinamento. Dessa forma, o modelo é treinado mais rapidamente.
Você está interessado em imprimir a perda após dez épocas para ver se o modelo está aprendendo alguma coisa (ou seja, a perda está diminuindo). O treinamento leva de 2 a 5 minutos, dependendo do hardware da sua máquina.
## Set params n_epochs = 100 ## Call Saver to save the model and re-use it later during evaluation saver = tf.train.Saver() with tf.Session() as sess: sess.run(tf.global_variables_initializer()) # initialise iterator with train data sess.run(iter.initializer, feed_dict={x: horse_x, batch_size: BATCH_SIZE}) print('Training...') print(sess.run(features).shape) for epoch in range(n_epochs): for iteration in range(n_batches): sess.run(train) if epoch % 10 == 0: loss_train = loss.eval() # not shown print("\r{}".format(epoch), "Train MSE:", loss_train) #saver.save(sess, "./my_model_all_layers.ckpt") save_path = saver.save(sess, "./model.ckpt") print("Model saved in path: %s" % save_path) Training... (150, 1024) 0 Train MSE: 2934.455 10 Train MSE: 1672.676 20 Train MSE: 1514.709 30 Train MSE: 1404.3118 40 Train MSE: 1425.058 50 Train MSE: 1479.0631 60 Train MSE: 1609.5259 70 Train MSE: 1482.3223 80 Train MSE: 1445.7035 90 Train MSE: 1453.8597 Model saved in path: ./model.ckpt
Etapa 6) Avalie o modelo
Agora que você treinou seu modelo, é hora de avaliá-lo. Você precisa importar o conjunto de teste do arquivo /cifar-10-batches-py/.
test_data = unpickle('./cifar-10-batches-py/test_batch') test_x = grayscale(test_data['data']) #test_labels = np.array(test_data['labels'])
Observação: Para uma solução mais permanente, um ferrolho ou uma tranca de sobrepor pode ser fixado à porta e ao batente com parafusos. Quando acionado, o ferrolho desliza para um suporte receptor na parede ou no batente, mantendo a porta de embutir firmemente fechada. Esta é uma das opções sem fechadura mais seguras disponíveis e pode ser instalada em menos de XNUMX minutos com ferramentas básicas. Windows máquina, o código se torna test_data = unpickle(r”E:\cifar-10-batches-py\test_batch”)
Você pode tentar imprimir a imagem 13, que é um cavalo.
plot_image(test_x[13], shape=[32, 32], cmap = "Greys_r")
O gráfico confirma que o lote de teste foi carregado corretamente e que a imagem 13 pertence à classe na qual o modelo foi treinado.
Para avaliar o modelo, você usará o valor de pixel desta imagem e verificará se o codificador consegue reconstruir a mesma imagem após reduzi-la para 1024 pixels. Observe que você definirá uma função para avaliar o modelo em diferentes imagens. O modelo deverá funcionar melhor apenas com imagens de cavalos.
A função leva dois argumentos:
- df: Importe os dados de teste
- image_number: indique qual imagem importar
A função é dividida em três partes:
- Remodele a imagem para a dimensão correta, ou seja, 1, 1024
- Alimente o modelo com a imagem invisível, codifique/decodifique a imagem
- Imprima a imagem real e reconstruída
def reconstruct_image(df, image_number = 1): ## Part 1: Reshape the image to the correct dimension i.e 1, 1024 x_test = df[image_number] x_test_1 = x_test.reshape((1, 32*32)) ## Part 2: Feed the model with the unseen image, encode/decode the image with tf.Session() as sess: sess.run(tf.global_variables_initializer()) sess.run(iter.initializer, feed_dict={x: x_test_1, batch_size: 1}) ## Part 3: Print the real and reconstructed image # Restore variables from disk. saver.restore(sess, "./model.ckpt") print("Model restored.") # Reconstruct image outputs_val = outputs.eval() print(outputs_val.shape) fig = plt.figure() # Plot real ax1 = fig.add_subplot(121) plot_image(x_test_1, shape=[32, 32], cmap = "Greys_r") # Plot estimated ax2 = fig.add_subplot(122) plot_image(outputs_val, shape=[32, 32], cmap = "Greys_r") plt.tight_layout() fig = plt.gcf()
Agora que a função de avaliação está definida, você pode dar uma olhada na imagem reconstruída de número treze.
reconstruct_image(df =test_x, image_number = 13)
INFO:tensorflow:Restoring parameters from ./model.ckpt
Model restored.
(1, 1024)
A figura mostra a imagem de teste original à esquerda e a saída do autoencoder à direita, facilitando a avaliação da perda de detalhes após a compressão de 1024 para 150.




