Google deep dream - Tutorial práctico

Google deep dream - Tutorial práctico

DeepDream es un experimento que visualiza los patrones aprendidos por una red neuronal. Al igual que cuando un niño mira nubes e intenta interpretar formas aleatorias, DeepDream sobreinterpreta y mejora los patrones que ve en una imagen.

Lo hace enviando una imagen a través de la red, luego calculando el gradiente de la imagen con respecto a las activaciones de una capa en particular. Por último, la imagen se modifica para aumentar estas activaciones, mejorando los patrones vistos por la red y dando como resultado una imagen de ensueño. Este proceso se denominó "Inceptionism" (una referencia a InceptionNet y la película Inception).

¿Cómo funciona el algoritmo del sueño profundo?

Cuando suministramos una imagen a una RNA entrenada, las neuronas se disparan y generan activaciones, el algoritmo sencillamente hace que algunas neuronas se disparen más. Es posible seleccionar qué neuronas en que capa estamos interesados en activar, y repetir este proceso hasta que la imagen de entrada contiene las características que una capa específica estaba buscando.

Pasos del algoritmo

Envíamos nuestra imagen a través de una red neuronal pre entrenada.

Se selecciona una capa a elección (las primeras capturan bordes, las capas más profundas detectan formas más completas)

Se calculan las activaciones (salidas) que salen de las capas de interés

Se calcula el gradiente de las activaciones con respecto a la imagen de entrada (ascenso del gradiente)

Se modifica la imagen para aumentar las activaciones y mejorar los patrones vistos por la red

Se repite el proceso

En el ejemplo desarrollado en el cuaderno, tendremos las siguientes secciones: carga y preprocesado de datos, definición del modelo preentrenado, definición de las capas que queremos activar, definición de la función que calcula el error y la función que lo maximiza, contrario al objetivo que tenemos comúnmente al entrenar una RN.

Cargamos el modelo base preentrenado

base_model = tf.keras.applications.InceptionV3(include_top = False, weights = 'imagenet')

Obtenemos y procesamos nuestra entrada

# get and process the data
img_1 = Image.open("/content/drive/MyDrive/COLAB NOTEBOOKS/eiffel.jpg")
img_2 = Image.open("/content/drive/MyDrive/COLAB NOTEBOOKS/mars.jpg")

image = Image.blend(img_1, img_2, 0.5)
image.save("/content/drive/MyDrive/COLAB NOTEBOOKS/img_0.jpg")

sample_image = tf.keras.preprocessing.image.load_img("/content/drive/MyDrive/COLAB NOTEBOOKS/img_0.jpg")
sample_image = tf.keras.preprocessing.image.img_to_array(sample_image)
sample_image = sample_image/255.0
sample_image = tf.expand_dims(sample_image, axis = 0)

Elegimos las capas de la RN preentrenada y creamos nuestro modelo

layer_names = ['mixed3', 'mixed5', 'mixed7']
layers = [base_model.get_layer(name).output for name in layer_names]

deepdream_model = tf.keras.Model(inputs = base_model.input, outputs = layers)
activations = deepdream_model(sample_image)

El siguiente bloque calcula la pérdida

def calculate_loss(image, model):
  img_batch = tf.expand_dims(image, axis=0)
  layer_activations = model(img_batch)
  print('Activation values (layer output) \n', layer_activations)

  losses = []
  for act in layer_activations:
    loss = tf.math.reduce_mean(act)
    losses.append(loss)
  print('Losses (from multiple activations layers) = ', losses)
  print('Losses shape', np.shape(losses))
  print('Sum of all losses', tf.reduce_sum(losses))

  return tf.reduce_sum(losses) # gets the sum
  
loss = calculate_loss(tf.Variable(sample_image), deepdream_model)  

Calculamos el gradiente ascendente:

# ascend gradient
@tf.function
def deepdream(model, image, step_size):
  with tf.GradientTape() as tape:
    tape.watch(image)
    loss = calculate_loss(image, model)

  gradients = tape.gradient(loss, image)
  print('Gradients =\n', gradients)
  print('Gradients shape =\n', np.shape(gradients))

  gradients = tf.math.reduce_std(gradients)

  # excite the layers
  # we can do it adding directly the gradients (because they have the same shape)
  image = image + gradients * step_size
  image = tf.clip_by_value(image, -1, 1)

  return loss, image

Corremos el modelo:

def run_deepdream(model, image, steps = 100, step_size = 0.01):
  image = tf.keras.applications.inception_v3.preprocess_input(image)
  deprocess = lambda image : tf.cast((255*(image+1.0)/2), tf.uint8)
  for step in range(steps):
    loss, image = deepdream(model, image, step_size)

    if step % 100 == 0:
      plt.figure(figsize=(12,12))
      plt.imshow(deprocess(image))
      plt.show()
      print("Step {}, loss {}".format(step, loss))
    
  #clear_output(wait=True)
  plt.figure(figsize=(12,12))
  plt.imshow(deprocess(image))
  plt.show()

  return deprocess(image)  

Puede encontrar el código relacionado acá.