Deep Dream com TensorFlow: Um guia prático para construir o seu primeiro Deep Dream Experience

Naveen Manwani Blocked Unblock Seguir Seguindo 29 de dezembro

“A imaginação é mais importante que o conhecimento . Pois o conhecimento é limitado, enquanto a imaginação abraça o mundo inteiro, estimulando o progresso, dando origem à evolução ”.

W empre a máquina engenheiros aprendizagem, profissionais de aprendizagem profunda reunir em algum meetup ou conferência das aplicações mais comuns de Deep Aprendizagem discutem varia de Detecção de Objeto, reconhecimento de face, Processamento de Língua Natural e reconhecimento de fala, principalmente devido à auto-dirigir carros, Amazon-Alexa ou Chatbots, mas existem outros tipos de aplicativos que são diferentes desses aplicativos padrão que estão criando enorme quantidade de buzz não apenas no campo da Inteligência Artificial, mas também no campo da Arte.

Uma aplicação desse tipo que capacita o artista, que por sua vez está aumentando nossas capacidades criativas, e expandindo o espaço do que podemos imaginar é o “ Sonho Profundo” .

Sonho profundo é uma visão por computador programa criado pelo Google engenheiro Alex Mordvintsev que utiliza uma rede neural convolutional para encontrar e melhorar os padrões em imagens via algorítmica pareidolia , criando assim um D resma -como alucinógena aparência nas imagens deliberadamente sobre-processados.

O programa do Google popularizou o termo (Deep) “Sonhando” para se referir à geração de imagens que produzem ativações desejadas em uma rede profunda e treinada, e o termo agora se refere a uma coleção de abordagens relacionadas.

Da teoria ao prático

Aí vem minha parte favorita, depois de se educar sobre o Google Deep Dream, é hora de mudar de um modo de leitor para um modo codificador , porque a partir de agora eu vou falar apenas sobre o código que é tão importante quanto conhecer os conceitos por trás de qualquer Aplicação Deep Learning.

Siga este guia prático passo-a-passo para criar suas primeiras experiências com o Deep Dream, mas antes de iniciar esta jornada de codificação comigo, dê uma olhada nas minhas imagens do Deep Dream que são pesadamente psicodélicas.

Imagem 1: Bem-vindo ao mundo trippy das imagens alucinógenas

Aviso : Antes de iniciar este tutorial de codificação, tenha certeza de ter dois arquivos python chamados download.py e inception5h.py em uma pasta que você pode obter do meu repositório GitHub mencionado na seção de recursos, caso contrário você vai se ver envolvido com “No module encontrado "erro que é certamente uma dor

Então vamos começar

 #Este foi desenvolvido usando o Python 3.6.3 (Anaconda) 
#Important library to import
 % matplotlib inline 
import matplotlib.pyplot como plt
importar tensorflow como tf
import numpy como np
importação aleatória
importar matemática
 # Manipulação de imagem. 
from PIL import Image
de scipy.ndimage.filters import gaussian_filter

Modelo de Incepção

O modelo Inception 5h é usado porque é mais fácil de se trabalhar: ele recebe imagens de entrada de qualquer tamanho e parece criar imagens mais bonitas que o modelo Inception v3.

 import inception5h 

Faça o download dos dados para o modelo Inception. Tem 50 MB de tamanho

 inception5h.maybe_download () 
 Download do modelo de 5h de inicialização ... 
Aparentemente, os dados já foram baixados e descompactados.

Carregue o modelo Inception para que ele esteja pronto para ser usado.

 model = inception5h.Inception5h () 

O modelo Inception 5h tem muitas camadas que podem ser usadas para Deep Dreaming. mas estaremos usando apenas 12 camadas mais comumente usadas para fácil referência.

 len (model.layer_tensors) 

Para conhecer as diferentes camadas no modelo de 5h de inicialização

 def printTensors (pb_file): 
 # leia pb em graph_def 
com tf.gfile.GArquivo (pb_file, "rb") como f:
graph_def = tf.GraphDef ()
graph_def.ParseFromString (f.read ())
 # import graph_def 
com tf.Graph (). as_default () como gráfico:
tf.import_graph_def (graph_def)
 # operações de impressão 
para op em graph.get_operations ():
print (op.name)
 printTensors ("inception / 5h / tensorflow_inception_graph.pb") 

Funções auxiliares para manipulação de imagens

Essa função carrega uma imagem e a retorna como uma matriz numpy de pontos flutuantes.

 def load_image (nome do arquivo): 
experimentar:
original = Image.open (nome do arquivo)
print ("o tamanho da imagem é:")
print (original.format, original.size)
exceto:
print ("Não foi possível carregar a imagem")
 return np.float32 (original) 

Salve uma imagem como um arquivo jpeg. A imagem é dada como um array numpy com valores de pixel entre 0 e 255.

 def save_image (imagem, nome do arquivo): 
# Assegure-se de que os valores de pixel estejam entre 0 e 255.
imagem = np.clip (imagem, 0.0, 255.0)

# Converter em bytes.
imagem = image.astype (np.uint8)

# Grave o arquivo de imagem no formato jpeg.
com open (filename, 'wb') como arquivo:
Image.fromarray (imagem) .save (arquivo, 'jpeg')

Esta função representa uma imagem. O uso do matplotlib fornece imagens de baixa resolução. Usando PIL dá belas fotos.

 def plot_image (imagem): 
# Suponha que os valores de pixel sejam dimensionados entre 0 e 255.

se falso:
# Converta os valores de pixel no intervalo entre 0,0 e 1,0
image = np.clip (image / 255.0, 0.0, 1.0)

# Plot usando o matplotlib.
plt.imshow (imagem, interpolação = 'lanczos')
plt.show ()
outro:
# Assegure-se de que os valores de pixel estejam entre 0 e 255.
imagem = np.clip (imagem, 0.0, 255.0)

# Converter pixels em bytes.
imagem = image.astype (np.uint8)
 # Converta em uma imagem PIL e exiba-a. 
exibição (Image.fromarray (image))

Normalize uma imagem para que seus valores estejam entre 0,0 e 1,0. Isso é útil para traçar o gradiente.

 def normalize_image (x): 
# Obtenha os valores mínimo e máximo para todos os pixels na entrada.
x_min = x.min ()
x_max = x.max ()
 # Normalize então todos os valores estão entre 0.0 e 1.0 
x_norm = (x - x_min) / (x_max - x_min)

return x_norm

Esta função plota o gradiente depois de normalizá-lo

 def plot_gradient (gradiente): 
# Normalize o gradiente para que fique entre 0.0 e 1.0
gradient_normalized = normalize_image (gradiente)

# Plote o gradiente normalizado.
plt.imshow (gradient_normalized, interpolação = 'bilinear')
plt.show ()

Esta função redimensiona uma imagem. Pode levar um argumento de tamanho onde você dá o tamanho de pixel exato que você quer que a imagem seja, por exemplo (100, 200). Ou pode levar um argumento de fator onde você dá o fator de redimensionamento que você quer usar, por exemplo, 0,5 para reduzir pela metade o tamanho da imagem em cada dimensão.

Isso é implementado usando o PIL, que é um pouco demorado porque estamos trabalhando em matrizes numpy onde os pixels são valores de ponto flutuante. Isso não é suportado pelo PIL, portanto, a imagem deve ser convertida em bytes de 8 bits, garantindo que os valores de pixel estejam dentro dos limites adequados. Em seguida, a imagem é redimensionada e convertida novamente em valores de ponto flutuante.

 def resize_image (imagem, tamanho = Nenhum, fator = Nenhum): 
# Se um fator de redimensionamento for fornecido, use-o.
se o fator não for Nenhum:
# Escala o formato da matriz numpy para altura e largura.
size = np.array (image.shape [0: 2]) * fator

# O tamanho é de ponto flutuante porque foi dimensionado.
# PIL requer que o tamanho seja inteiro.
size = size.astype (int)
outro:
# Certifique-se de que o tamanho tenha comprimento 2.
tamanho = tamanho [0: 2]

# A altura e a largura são invertidas em numpy vs. PIL.
tamanho = tuple (invertido (tamanho))
 # Assegure-se de que os valores de pixel estejam entre 0 e 255. 
img = np.clip (imagem, 0.0, 255.0)

# Converta os pixels em bytes de 8 bits.
img = img.astype (np.uint8)

# Crie o objeto PIL a partir da matriz numpy.
img = Image.fromarray (img)

# Redimensione a imagem.
img_resized = img.resize (size, Image.LANCZOS)

# Converte valores de pixel de 8 bits de volta para o ponto flutuante.
img_resized = np.float32 (img_resized)
 return img_resized 

Algoritmo DeepDream

Gradiente

As seguintes funções auxiliares calculam o gradiente de uma imagem de entrada para uso no algoritmo DeepDream. O modelo Inception 5h pode aceitar imagens de qualquer tamanho, mas imagens muito grandes podem usar muitos giga-bytes de RAM. Para manter baixo o uso de RAM, dividiremos a imagem de entrada em blocos menores e calcularemos o gradiente para cada um dos blocos.

No entanto, isso pode resultar em linhas visíveis nas imagens finais produzidas pelo algoritmo DeepDream. Por isso escolhemos as peças aleatoriamente para que as localizações das peças sejam sempre diferentes. Isso torna as junções entre os blocos invisíveis na imagem final do DeepDream.

Esta é uma função auxiliar para determinar um tamanho de bloco apropriado. O tamanho de ladrilho desejado é, por exemplo, 400×400 pixels, mas o tamanho real do ladrilho dependerá das dimensões da imagem.

 def get_tile_size (num_pixels, tile_size = 400): 
"" "
num_pixels é o número de pixels em uma dimensão da imagem.
tile_size é o tamanho do bloco desejado.
"" "
 # Quantas vezes podemos repetir uma telha do tamanho desejado. 
num_tiles = int (round (num_pixels / tile_size))

# Certifique-se de que haja pelo menos um ladrilho.
num_tiles = max (1, num_tiles)

# O tamanho real do bloco.
actual_tile_size = math.ceil (num_pixels / num_tiles)

return actual_tile_size

Essa função auxiliar calcula o gradiente para uma imagem de entrada. A imagem é dividida em blocos e o gradiente é calculado para cada bloco. As peças são escolhidas aleatoriamente para evitar costuras / linhas visíveis na imagem final do DeepDream.

 def tiled_gradient (gradiente, imagem, tile_size = 400): 
# Aloque uma matriz para o gradiente da imagem inteira.
grad = np.zeros_like (imagem)
 # Número de pixels para os eixos xey. 
x_max, y_max, _ = image.shape
 # Tamanho do mosaico para o eixo x. 
x_tile_size = get_tile_size (num_pixels = x_max, tile_size = tile_size)
# 1/4 do tamanho do bloco.
x_tile_size4 = x_tile_size // 4
 # Tamanho do mosaico para o eixo y. 
y_tile_size = get_tile_size (num_pixels = y_max, tile_size = tile_size)
# 1/4 do tamanho do bloco
y_tile_size4 = y_tile_size // 4
 # Posição inicial aleatória para as peças no eixo x. 
# O valor aleatório está entre -3/4 e -1/4 do tamanho do bloco.
# Isso é para que os ladrilhos de borda tenham pelo menos 1/4 do tamanho do ladrilho,
# caso contrário, as peças podem ser muito pequenas, o que cria gradientes ruidosos.
x_start = random.randint (-3 * x_tile_size4, -x_tile_size4)
 enquanto x_start <x_max: 
# Posição final do ladrilho atual.
x_end = x_start + x_tile_size

# Assegure-se de que as posições inicial e final da peça são válidas.
x_start_lim = max (x_start, 0)
x_end_lim = min (x_end, x_max)
 # Posição inicial aleatória para as peças no eixo y. 
# O valor aleatório está entre -3/4 e -1/4 do tamanho do bloco.
y_start = random.randint (-3 * y_tile_size4, -y_tile_size4)
 enquanto y_start <y_max: 
# Posição final do ladrilho atual.
y_end = y_start + y_tile_size
 # Assegure-se de que as posições inicial e final da peça são válidas. 
y_start_lim = max (y_start, 0)
y_end_lim = min (y_end, y_max)
 # Obter o bloco de imagem. 
img_tile = image [x_start_lim: x_end_lim,
y_start_lim: y_end_lim,:]
 # Crie um feed-dict com o bloco de imagem. 
feed_dict = model.create_feed_dict (image = img_tile)
 # Use o TensorFlow para calcular o valor do gradiente. 
g = session.run (gradiente, feed_dict = feed_dict)
 # Normalize o gradiente para o ladrilho. Isto é 
# necessário porque as peças podem ter diferentes
# valores. Normalizar dá um gradiente mais coerente.
g / = (np.std (g) + 1e-8)
 # Armazena o gradiente do bloco no local apropriado. 
grad [x_start_lim: x_end_lim,
y_start_lim: y_end_lim,:] = g

# Avance a posição inicial para o eixo y.
y_start = y_end
 # Avance a posição inicial para o eixo x. 
x_start = x_end
 retorno grad 

Otimizar imagem

Esta função é o principal loop de otimização para o algoritmo DeepDream. Calcula o gradiente da camada dada do modelo Inception em relação à imagem de entrada. O gradiente é então adicionado à imagem de entrada para que o valor médio do tensor de camada seja aumentado. Esse processo é repetido várias vezes e amplifica quaisquer padrões que o modelo Inception veja na imagem de entrada.

 def optimize_image (layer_tensor, imagem, 
num_iterations = 10, step_size = 3.0, tile_size = 400,
show_gradient = False ):
"" "
Use a subida do gradiente para otimizar uma imagem, maximizando
valor médio do layer_tensor dado.

Parâmetros:
layer_tensor: referência a um tensor que será maximizado.
imagem: imagem de entrada usada como ponto de partida.
num_iterations: Número de iterações de otimização a serem executadas.
step_size: Escala para cada etapa da subida do gradiente.
tile_size: tamanho dos blocos ao calcular o gradiente.
show_gradient: Plote o gradiente em cada iteração.
"" "

# Copie a imagem para não sobrescrevermos a imagem original.
img = image.copy ()

print ("Imagem anterior:")
plot_image (img)

print ("Processando imagem:", end = "")

# Use o TensorFlow para obter a função matemática para o
# gradiente do dado tensor de camada em relação ao
# imagem de entrada. Isso pode fazer com que o TensorFlow adicione o mesmo
# expressões matemáticas para o gráfico cada vez que esta função é chamada.
# Pode usar muita RAM e pode ser movido para fora da função.
gradiente = model.get_gradient (layer_tensor)

para eu no intervalo (num_iterations):
# Calcular o valor do gradiente.
# Isso nos diz como mudar a imagem de modo a
# maximizar a média do determinado tensor de camada.
grad = tiled_gradient (gradiente = gradiente, imagem = img)

# Desfoque o gradiente com quantidades diferentes e adicione
# eles juntos. A quantidade de desfoque também é aumentada
# durante a otimização. Isto foi encontrado para dar
# imagens agradáveis e suaves. Você pode tentar e mudar as fórmulas.
# O valor do borrão é chamado de sigma (0 = sem desfoque, 1 = desfoque baixo, etc.)
# Poderíamos chamar gaussian_filter (grad, sigma = (sigma, sigma, 0.0))
# que não iria desfocar o canal de cores. Isso tende a
# dê cores psychadelic / pastel nas imagens resultantes.
# Quando o canal de cor também estiver desfocado, as cores do
# imagem de entrada é principalmente retida na imagem de saída.
sigma = (i * 4,0) / num_iterations + 0,5
grad_smooth1 = gaussian_filter (grad, sigma = sigma)
grad_smooth2 = gaussian_filter (grad, sigma = sigma * 2)
grad_smooth3 = gaussian_filter (grad, sigma = sigma * 0,5)
grad = (grad_smooth1 + grad_smooth2 + grad_smooth3)

# Escale o tamanho do passo de acordo com os valores de gradiente.
# Isso pode não ser necessário porque o gradiente lado a lado
# já está normalizado.
step_size_scaled = step_size / (np.std (grad) + 1e-8)

# Atualize a imagem seguindo o gradiente.
img + = grad * step_size_scaled

se show_gradient:
# Estatísticas de impressão para o gradiente.
msg = "Gradient min: {0:> 9.6f} , max: {1:> 9.6f} , tamanho da etapa : {2:> 9.2f} "
print (msg.format (grad.min (), grad.max (), step_size_scaled))

# Plote o gradiente.
plot_gradient (grad)
else :
# Caso contrário, mostre um pequeno indicador de progresso.
print (".", end = "")

impressão()
print ("Imagem depois de:")
plot_image (img)

retorno img

Otimização de imagem recursiva

O modelo Inception foi treinado em imagens relativamente pequenas. O tamanho exato não é claro, mas talvez 200 a 300 pixels em cada dimensão. Se usarmos imagens maiores, como 1920×1080 pixels, a função optimize_image() acima adicionará muitos padrões pequenos à imagem.

Esta função auxiliar reduz a imagem de entrada várias vezes e executa cada versão reduzida através da função optimize_image() acima. Isso resulta em padrões maiores na imagem final. Também acelera o cálculo.

 def recursive_optimize (layer_tensor, imagem, 
num_repeats = 4, rescale_factor = 0.7, blend = 0.2,
num_iterations = 10, step_size = 3.0,
tile_size = 400):
"" "
Recursivamente borrar e reduzir a imagem de entrada.
Cada imagem reduzida é executada através do optimize_image ()
função para amplificar os padrões que o modelo Inception vê.
 Parâmetros: 
imagem: imagem de entrada usada como ponto de partida.
rescale_factor: Fator de downscaling para a imagem.
num_repeats: número de vezes para diminuir a escala da imagem.
blend: Fator para mesclar as imagens originais e processadas.
 Parâmetros passados para optimize_image (): 
layer_tensor: referência a um tensor que será maximizado.
num_iterations: Número de iterações de otimização a serem executadas.
step_size: Escala para cada etapa da subida do gradiente.
tile_size: tamanho dos blocos ao calcular o gradiente.
"" "
 # Faça um passo recursivo? 
if num_repeats> 0:
# Desfoque a imagem de entrada para evitar artefatos ao reduzir a escala.
# A quantidade de borrões é controlada pelo sigma. Note que o
# canal de cor não é desfocado, pois tornaria a imagem cinza.
sigma = 0,5
img_blur = gaussian_filter (imagem, sigma = (sigma, sigma, 0.0))
 # Downscale a imagem. 
img_downscaled = resize_image (image = img_blur,
fator = rescale_factor)

# Chamada recursiva para esta função.
# Subtraia um de num_repeats e use a imagem com redução de escala.
img_result = recursive_optimize (layer_tensor = layer_tensor,
image = img_downscaled,
num_repeats = num_repeats-1,
rescale_factor = rescale_factor,
mistura = mistura
num_iterations = num_iterations,
step_size = step_size,
tile_size = tile_size)

# Aprimore a imagem resultante de volta ao tamanho original.
img_upscaled = resize_image (image = img_result, size = image.shape)
 # Combine as imagens originais e processadas. 
image = blend * image + (1.0 - blend) * img_upscaled
 print ("Nível recursivo:", num_repeats) 
 # Processe a imagem usando o algoritmo DeepDream. 
img_result = optimize_image (layer_tensor = layer_tensor,
imagem = imagem
num_iterations = num_iterations,
step_size = step_size,
tile_size = tile_size)

retornar img_result

Sessão TensorFlow

Precisamos de uma sessão TensorFlow para executar o gráfico. Esta é uma sessão interativa para que possamos continuar adicionando funções de gradiente ao gráfico computacional.

 session = tf.InteractiveSession (graph = model.graph) 

Está na hora de executar o algoritmo

 #carregar a imagem que você deseja processar 
image = load_image (nome do arquivo = 'test_output / test_output_11.jpg')
plot_image (imagem)
 # o tamanho da imagem é: 
# JPEG (780, 1040)

Imagem 2: Sou eu há alguns anos atrás

Primeiro, precisamos de uma referência ao tensor dentro do modelo Inception, que iremos maximizar no algoritmo de otimização do DeepDream. Neste caso, selecionamos toda a terceira camada do modelo Inception (índice de camadas 2). Tem 192 canais e vamos tentar maximizar o valor médio em todos esses canais.

 layer_tensor = model.layer_tensors [2] 
layer_tensor
 # <tf.Tensor 'conv2d2: 0' shape = (?,?,?, 192) dtype = float32> 

Aplicando algoritmo Deep Dream recursivamente.

 img_result = recursive_optimize (layer_tensor = layer_tensor, image = image, 
num_iterations = 10, step_size = 3.0, rescale_factor = 0.7,
num_repeats = 4, mistura = 0,2)

Imagem 3: Depois de aplicar o Deep Dream à minha imagem

Agora vamos maximizar uma camada superior no modelo Inception. Neste caso, é a camada número 7 (índice 6). Essa camada reconhece formas mais complexas na imagem de entrada e, portanto, o algoritmo DeepDream produzirá uma imagem mais complexa. Esta camada parece estar reconhecendo rostos e peles que o algoritmo DeepDream, portanto, adicionou à imagem.

 layer_tensor = model.layer_tensors [6] 
img_result = recursive_optimize (layer_tensor = layer_tensor, image = image,
num_iterations = 10, step_size = 3.0, rescale_factor = 0.7,
num_repeats = 4, mistura = 0,2)

Imagem 4: Após aplicar o Algoritmo Deep Dream

Este é um exemplo de maximizar apenas um subconjunto de canais de feição da camada usando o algoritmo DeepDream. Nesse caso, é a camada com o índice 10 e apenas seus três primeiros canais de recursos que são maximizados.

 layer_tensor = model.layer_tensors [10] [:,:,:, 0: 3] 
img_result = recursive_optimize (layer_tensor = layer_tensor, image = image,
num_iterations = 10, step_size = 3.0, rescale_factor = 0.7,
num_repeats = 4, mistura = 0,2)

Imagem 5: Após aplicar o Algoritmo Deep Dream

 layer_tensor = model.layer_tensors [4] 
img_result = recursive_optimize (layer_tensor = layer_tensor, image = image,
num_iterations = 10, step_size = 3.0, rescale_factor = 0.7,
num_repeats = 4, mistura = 0,2)

Imagem 6: Após aplicar o Algoritmo Deep Dream

 # Para salvar o resultado final 
 image_save = save_image (img_result, "test_output / test_output_12.jpg") 

Se isso não for suficiente, enviei um vídeo no YouTube que ampliará ainda mais sua experiência psicodélica.

Conclusão: Bem, é isso que este artigo mostrou como usar o tensorflow e alguns conceitos você também pode criar experiências Deep Dream por conta própria.

Menção Especial: Este artigo teria sido impossível sem a direção dada por Magnus Erik Hvass Pedersen através de seus famosos tutoriais TensorFlow que o repositório do GitHub pode ser encontrado aqui .

Recursos:

  1. Para o repositório do GitHub, clique aqui .
  2. Para aumentar a compreensão em relação ao Deep Dream, passe pelo post do blog do Google Research .

Obrigado pela sua atenção

Você usando seu tempo para ler meu trabalho significa o mundo para mim. Eu totalmente quero dizer isso.

Se você gostou desta história, enlouqueça com o botão aplauso ( ? ) ! Ajudará outras pessoas a encontrar meu trabalho.

Além disso, siga-me no Medium, LinkedIn ou Twitter, se você quiser! Eu adoraria isso.

Naveen Manwani – Medium
Leia a escrita de Naveen Manwani no Medium. Engenheiro de Aprendizado de Máquina, um entusiasta de aprendizado profundo Google India… medium.com
Naveen Manwani – Engenheiro de Aprendizado de Máquina – AIMonk Labs Private Ltd | LinkedIn
Veja o perfil de Naveen Manwani no LinkedIn, a maior comunidade profissional do mundo. Naveen tem 1 emprego listado em sua… www.linkedin.com
naveen manwani (@ NaveenManwani17) | Twitter
Os últimos Tweets de naveen manwani (@ NaveenManwani17). Engenheiro de Aprendizado de Máquina @ AIMONK Labs Pvt ltd, Deep… twitter.com