Autoencoder no TensorFlow com exemplo
O que é Autoencoder em Deep Learning?
An Codificador automático é uma ferramenta para aprender a codificação de dados de maneira eficiente e não supervisionada. É um tipo de rede neural artificial que ajuda você 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 palavras simples, a máquina tira, digamos, uma imagem e pode produzir uma imagem intimamente relacionada. A entrada neste 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 no recurso mais crítico. Esta é uma das razões pelas quais o autoencoder é popular para redução de dimensionalidade. Além disso, autoencoders podem ser usados para produzir modelos de aprendizagem generativos. Por exemplo, a rede neural pode ser treinada com um conjunto de faces e então produzir novas faces.
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). A aprendizagem ocorre nas camadas ligadas à representação interna. Na verdade, existem dois blocos principais de camadas que se parecem com uma rede neural tradicional. A pequena diferença é que a camada que contém a saída deve ser igual à entrada. Na figura abaixo, a entrada original vai para o primeiro bloco chamado encoder. Esta representação interna comprime (reduz) o tamanho da entrada. No segundo bloco ocorre a reconstrução da entrada. Esta é 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 com tamanho de 50×50 (ou seja, 250 pixels) e uma rede neural com apenas uma camada oculta composta por cem neurônios. O aprendizado é feito em um mapa de características duas vezes menor que a entrada. Isso significa que a rede precisa encontrar uma maneira de reconstruir 250 pixels com apenas um vetor de neurônios igual a 100.
Exemplo de codificador automático empilhado
Neste tutorial do Autoencoder, você aprenderá como usar um autoencoder empilhado. A arquitetura é semelhante a uma rede neural tradicional. A entrada vai para uma camada oculta para ser comprimida, ou reduzir seu tamanho, e então chega às camadas de reconstrução. O objetivo é produzir uma imagem de saída tão próxima quanto a original. O modelo tem que aprender uma forma de realizar a sua tarefa sob um conjunto de restrições, ou seja, com uma dimensão menor.
Hoje em dia, Autoencoders em Deep Learning são usados principalmente para eliminar ruído de uma imagem. Imagine uma imagem com arranhões; um humano ainda é capaz de reconhecer o conteúdo. A ideia do autoencoder de eliminação de ruído é adicionar ruído à imagem para forçar a rede a aprender o padrão por trás dos dados.
A outra família útil de Autoencoder Deep Learning é o autoencoder variacional. Este tipo de rede pode gerar novas imagens. Imagine que você treina uma rede com a imagem de um homem; tal rede pode produzir novos rostos.
Como construir um autoencoder com TensorFlow
Neste tutorial, você aprenderá como construir um autoencoder empilhado para reconstruir uma imagem.
Você vai usar o Conjunto de dados CIFAR-10 que contém 60000 imagens coloridas de 32×32. O conjunto de dados do Autoencoder já está dividido entre 50000 imagens para treinamento e 10000 para teste. Existem até dez aulas:
- Avião
- Automobile
- Pássaro
- Gato
- Veado
- Cachorro
- Sapo
- Horse
- Navio
- Truck
Você precisa baixar as imagens neste URL https://www.cs.toronto.edu/~kriz/cifar.html e descompactá-las. A pasta for-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
Pré-processamento de imagem
Etapa 1) Importe os dados
De acordo com o site oficial, você pode fazer upload dos dados com o seguinte código. O código do Autoencoder carregará os dados em um dicionário com o dados, e os votos de rótulo. 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 anexar os dados na memória. Se você verificar com cuidado, o arquivo descompactado com os dados é denominado data_batch_ com um número de 1 a 5. Você pode percorrer os arquivos e anexá-los aos dados.
Quando esta etapa for concluída, você converte os dados de cores em um formato de escala de cinza. Como você pode ver, o formato dos dados é 50000 e 1024. Os 32*32 pixels agora estão nivelados para 2014.
# 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)
Nota: Altere './cifar-10-batches-py/data_batch_' para o local real do seu arquivo. Por exemplo para 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 nas imagens do cavalo. Os cavalos são a sétima classe nos dados do 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, conforme mostrado abaixo TensorFlow Etapa de exemplo do codificador automático.
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 o objeto imshow da biblioteca matplotlib. Observe que você precisa converter o formato dos dados de 1024 para 32*32 (ou seja, 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
- Shape: lista, a 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")
Definir estimador de conjunto de dados
Tudo bem, agora que o conjunto de dados está pronto para uso, você pode começar a usar o Tensorflow. Antes de construir o modelo, vamos usar o estimador Dataset 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 espaço reservado com o seguinte formato:
- [None,n_inputs]: Defina como Nenhum porque o número de imagens alimentadas na rede é igual ao tamanho do lote.
para obter detalhes, consulte o tutorial em regressão linear.
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ê define o tamanho do lote como 1 porque deseja alimentar o conjunto de dados apenas com uma imagem. Você pode ver a dimensão dos dados com print(sess.run(features).shape). É igual a (1, 1024). 1 significa que apenas uma imagem com 1024 é alimentada cada. Se o tamanho do lote for definido como dois, duas imagens passarão pelo pipeline. (Não altere o tamanho do lote. Caso contrário, ocorrerá um erro. Apenas uma imagem por vez pode ir para a 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)
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, uma camada central com 150 neurônios. O bloco decodificador é simétrico ao codificador. Você pode visualizar a rede na imagem abaixo. Observe que você pode alterar os valores das camadas ocultas e centrais.
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 há necessidade de criar novamente o conjunto de dados. Você construirá um autoencoder com quatro camadas. Você usa a inicialização do Xavier. Esta é uma técnica para definir os pesos iniciais iguais à variância da entrada e da saída. Finalmente, você usa a função de ativação elu. Você regulariza a função de perda com o regularizador L2.
Passo 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 parcialmente. É o melhor método para definir os parâmetros das camadas densas. O código abaixo define os valores da arquitetura do autoencoder. Conforme listado anteriormente, o autoencoder possui duas camadas, com 300 neurônios nas primeiras camadas e 150 nas segundas camadas. Seus valores são armazenados em n_hidden_1 e n_hidden_2.
Você precisa definir a taxa de aprendizagem 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 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)
Passo 2) Defina as camadas
Todos os parâmetros das camadas densas foram definidos; você pode empacotar tudo na variável densa_layer usando o objeto parcial. densa_layer que usa a ativação ELU, inicialização Xavier e regularização L2.
## Create the dense layer dense_layer = partial(tf.layers.dense, activation=tf.nn.elu, kernel_initializer=xav_init, kernel_regularizer=l2_regularizer)
Passo 3) Defina a arquitetura
Se você observar a imagem 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 os recursos da matriz de entradas e as matrizes contendo os 300 pesos. Depois que o produto escalar é calculado, a saída vai para a função de ativação Elu. A saída se torna a entrada da próxima camada, é por isso que você a usa para calcular hidden_2 e assim por diante. A multiplicação das matrizes é a mesma para cada camada porque você usa a mesma função de ativação. Observe que a última camada, saídas, não aplica uma função de ativação. Faz sentido porque esta é 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)
Passo 4) Defina a otimização
A última etapa é construir o otimizador. Você usa o erro quadrático médio como uma função de perda. Se você se lembra do tutorial sobre regressão linear, sabe que o MSE é calculado com a diferença entre a saída prevista e o rótulo real. Aqui, o rótulo é o recurso porque o modelo tenta reconstruir a entrada. Portanto, você deseja a média da soma da diferença do quadrado entre a produção e a entrada previstas. Com o TensorFlow, você pode codificar a função de perda da seguinte maneira:
loss = tf.reduce_mean(tf.square(outputs - features))
Então, 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ê deseja passar 150 imagens de cada vez e sabe que há 5000 imagens no conjunto de dados, o número de iterações é igual a. Em python você pode executar os seguintes códigos e garantir que a saída seja 33:
BATCH_SIZE = 150 ### Number of batches : length dataset / batch size n_batches = horse_x.shape[0] // BATCH_SIZE print(n_batches) 33
Passo 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á 100 vezes mais imagens com pesos otimizados.
Você já está familiarizado com os códigos para treinar um modelo no Tensorflow. A pequena diferença é canalizar os dados antes de executar o treinamento. Dessa forma, o modelo treina mais rápido.
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
Passo 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'])
NOTA: Para 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")
Para avaliar o modelo, você usará o valor do pixel desta imagem e verá se o codificador consegue reconstruir a mesma imagem após reduzir 1024 pixels. Observe que você define uma função para avaliar o modelo em diferentes imagens. O modelo deveria funcionar melhor apenas em cavalos.
A função leva dois argumentos:
- df: Importe os dados de teste
- número_da_imagem: 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 número treze
reconstruct_image(df =test_x, image_number = 13)
INFO:tensorflow:Restoring parameters from ./model.ckpt Model restored. (1, 1024)
Resumo
- O objetivo principal de um autoencoder é compactar os dados de entrada e, em seguida, descompactá-los em uma saída que se pareça muito com os dados originais.
- A arquitetura de um autoencoder simétrico com uma camada pivô denominada camada central.
- Você pode criar o autoencoder usando:
Parcial: para criar camadas densas com a configuração típica:
tf.layers.dense, activation=tf.nn.elu, kernel_initializer=xav_init, kernel_regularizer=l2_regularizer
camada_densa(): para fazer a multiplicação da matriz
loss = tf.reduce_mean(tf.square(outputs - features)) optimizer = tf.train.AdamOptimizer(learning_rate) train = optimizer.minimize(loss)