DeepDream

TensorFlow.org'da görüntüleyin Google Colab'da çalıştırın Kaynağı GitHub'da görüntüleyin Not defterini indir

Bu anlatıldığı gibi Bu eğitimde, DeepDream minimal uygulanmasını içeren blog post Alexander Mordvintsev tarafından.

DeepDream, bir sinir ağı tarafından öğrenilen kalıpları görselleştiren bir deneydir. Bir çocuğun bulutları izleyip rastgele şekilleri yorumlamaya çalışmasına benzer şekilde, DeepDream bir görüntüde gördüğü kalıpları aşırı yorumlar ve geliştirir.

Bunu, bir görüntüyü ağ üzerinden ileterek ve ardından belirli bir katmanın aktivasyonlarına göre görüntünün gradyanını hesaplayarak yapar. Görüntü daha sonra bu aktivasyonları artırmak için değiştirilir, ağ tarafından görülen kalıpları geliştirir ve rüya benzeri bir görüntü ile sonuçlanır. Bu süreç "Inceptionism" (bir referans olarak adlandırıldı InceptionNet ve film Inception).

Bir sinir ağını nasıl "rüya" yapabileceğinizi ve bir görüntüde gördüğü gerçeküstü kalıpları nasıl geliştirebileceğinizi gösterelim.

dogception

import tensorflow as tf
import numpy as np

import matplotlib as mpl

import IPython.display as display
import PIL.Image

from tensorflow.keras.preprocessing import image

Hayal kurmak için bir resim seçin

Bu eğitim için, haydi bir görüntüsünü kullanmasına izin labrador .

url = 'https://storage.googleapis.com/download.tensorflow.org/example_images/YellowLabradorLooking_new.jpg'
# Download an image and read it into a NumPy array.
def download(url, max_dim=None):
  name = url.split('/')[-1]
  image_path = tf.keras.utils.get_file(name, origin=url)
  img = PIL.Image.open(image_path)
  if max_dim:
    img.thumbnail((max_dim, max_dim))
  return np.array(img)

# Normalize an image
def deprocess(img):
  img = 255*(img + 1.0)/2.0
  return tf.cast(img, tf.uint8)

# Display an image
def show(img):
  display.display(PIL.Image.fromarray(np.array(img)))


# Downsizing the image makes it easier to work with.
original_img = download(url, max_dim=500)
show(original_img)
display.display(display.HTML('Image cc-by: <a "href=https://commons.wikimedia.org/wiki/File:Felis_catus-cat_on_snow.jpg">Von.grzanka</a>'))

png

Özellik çıkarma modelini hazırlayın

Önceden eğitilmiş bir görüntü sınıflandırma modelini indirin ve hazırlayın. Sen kullanacak InceptionV3 aslen DeepDream kullanılan modele benzer. Herhangi geldiğini hatırlatırız önceden eğitilmiş modeli Bunu değiştirirseniz aşağıdaki katman adlarını ayarlamak zorunda kalacak, ancak, çalışacaktır.

base_model = tf.keras.applications.InceptionV3(include_top=False, weights='imagenet')
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/inception_v3/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5
87916544/87910968 [==============================] - 4s 0us/step

DeepDream'deki fikir, bir katman (veya katmanlar) seçmek ve görüntünün katmanları giderek daha fazla "heyecanlandıracağı" şekilde "kaybı" en üst düzeye çıkarmaktır. Dahil edilen özelliklerin karmaşıklığı, sizin tarafınızdan seçilen katmanlara bağlıdır; yani, alt katmanlar konturlar veya basit desenler üretirken, daha derin katmanlar, görüntülerde ve hatta tüm nesnelerde karmaşık özellikler verir.

InceptionV3 mimarisi (model yapısının bir grafik için TensorFlow en bkz oldukça büyük araştırma repo ). DeepDream için, ilgilenilen katmanlar, kıvrımların birleştirildiği katmanlardır. InceptionV3'te bu katmanlardan 'mixed0' olsa da 'mixed10' olarak adlandırılan 11 tane vardır. Farklı katmanların kullanılması, farklı rüya benzeri görüntülerle sonuçlanacaktır. Daha derin katmanlar daha üst düzey özelliklere (gözler ve yüzler gibi) yanıt verirken, önceki katmanlar daha basit özelliklere (kenarlar, şekiller ve dokular gibi) yanıt verir. Aşağıda seçilen katmanları denemekten çekinmeyin, ancak degrade hesaplaması daha derin olduğu için daha derin katmanların (daha yüksek endekse sahip olanlar) üzerinde eğitim almanın daha uzun süreceğini unutmayın.

# Maximize the activations of these layers
names = ['mixed3', 'mixed5']
layers = [base_model.get_layer(name).output for name in names]

# Create the feature extraction model
dream_model = tf.keras.Model(inputs=base_model.input, outputs=layers)

kaybı hesapla

Kayıp, seçilen katmanlardaki aktivasyonların toplamıdır. Kayıp her katmanda normalleştirilir, böylece daha büyük katmanların katkısı daha küçük katmanlardan daha ağır basmaz. Normalde kayıp, eğimli iniş yoluyla en aza indirmek istediğiniz bir miktardır. DeepDream'de bu kaybı gradyan tırmanış yoluyla en üst düzeye çıkaracaksınız.

def calc_loss(img, model):
  # Pass forward the image through the model to retrieve the activations.
  # Converts the image into a batch of size 1.
  img_batch = tf.expand_dims(img, axis=0)
  layer_activations = model(img_batch)
  if len(layer_activations) == 1:
    layer_activations = [layer_activations]

  losses = []
  for act in layer_activations:
    loss = tf.math.reduce_mean(act)
    losses.append(loss)

  return  tf.reduce_sum(losses)

Gradyan tırmanışı

Seçilen katmanlar için kaybı hesapladıktan sonra, geriye sadece görüntüye göre gradyanları hesaplamak ve bunları orijinal görüntüye eklemek kalır.

Görüntüye gradyanlar eklemek, ağ tarafından görülen kalıpları geliştirir. Her adımda, ağdaki belirli katmanların aktivasyonlarını giderek daha fazla heyecanlandıran bir görüntü oluşturmuş olacaksınız.

Yapar yöntem olup, aşağıdaki bir sarılır tf.function performansı için gereklidir. Bir kullanır input_signature işlevi farklı görüntü boyutları veya için retraced emin olmak için steps / step_size değerler. Bkz Beton fonksiyonlar rehberlik detaylar için.

class DeepDream(tf.Module):
  def __init__(self, model):
    self.model = model

  @tf.function(
      input_signature=(
        tf.TensorSpec(shape=[None,None,3], dtype=tf.float32),
        tf.TensorSpec(shape=[], dtype=tf.int32),
        tf.TensorSpec(shape=[], dtype=tf.float32),)
  )
  def __call__(self, img, steps, step_size):
      print("Tracing")
      loss = tf.constant(0.0)
      for n in tf.range(steps):
        with tf.GradientTape() as tape:
          # This needs gradients relative to `img`
          # `GradientTape` only watches `tf.Variable`s by default
          tape.watch(img)
          loss = calc_loss(img, self.model)

        # Calculate the gradient of the loss with respect to the pixels of the input image.
        gradients = tape.gradient(loss, img)

        # Normalize the gradients.
        gradients /= tf.math.reduce_std(gradients) + 1e-8 

        # In gradient ascent, the "loss" is maximized so that the input image increasingly "excites" the layers.
        # You can update the image by directly adding the gradients (because they're the same shape!)
        img = img + gradients*step_size
        img = tf.clip_by_value(img, -1, 1)

      return loss, img
deepdream = DeepDream(dream_model)

Ana döngü

def run_deep_dream_simple(img, steps=100, step_size=0.01):
  # Convert from uint8 to the range expected by the model.
  img = tf.keras.applications.inception_v3.preprocess_input(img)
  img = tf.convert_to_tensor(img)
  step_size = tf.convert_to_tensor(step_size)
  steps_remaining = steps
  step = 0
  while steps_remaining:
    if steps_remaining>100:
      run_steps = tf.constant(100)
    else:
      run_steps = tf.constant(steps_remaining)
    steps_remaining -= run_steps
    step += run_steps

    loss, img = deepdream(img, run_steps, tf.constant(step_size))

    display.clear_output(wait=True)
    show(deprocess(img))
    print ("Step {}, loss {}".format(step, loss))


  result = deprocess(img)
  display.clear_output(wait=True)
  show(result)

  return result
dream_img = run_deep_dream_simple(img=original_img, 
                                  steps=100, step_size=0.01)

png

bir oktav alarak

Oldukça iyi, ancak bu ilk denemeyle ilgili birkaç sorun var:

  1. Çıktı (bu ile ele alınabilir gürültülü tf.image.total_variation kaybı).
  2. Görüntü düşük çözünürlüklü.
  3. Kalıplar, hepsi aynı ayrıntı düzeyinde oluyormuş gibi görünüyor.

Tüm bu sorunları ele alan bir yaklaşım, farklı ölçeklerde gradyan yükselişi uygulamaktır. Bu, daha küçük ölçeklerde oluşturulan modellerin daha yüksek ölçeklerdeki modellere dahil edilmesini ve ek ayrıntılarla doldurulmasını sağlayacaktır.

Bunu yapmak için önceki gradyan yükseliş yaklaşımını gerçekleştirebilir, ardından görüntünün boyutunu (oktav olarak adlandırılır) artırabilir ve bu işlemi birden çok oktav için tekrarlayabilirsiniz.

import time
start = time.time()

OCTAVE_SCALE = 1.30

img = tf.constant(np.array(original_img))
base_shape = tf.shape(img)[:-1]
float_base_shape = tf.cast(base_shape, tf.float32)

for n in range(-2, 3):
  new_shape = tf.cast(float_base_shape*(OCTAVE_SCALE**n), tf.int32)

  img = tf.image.resize(img, new_shape).numpy()

  img = run_deep_dream_simple(img=img, steps=50, step_size=0.01)

display.clear_output(wait=True)
img = tf.image.resize(img, base_shape)
img = tf.image.convert_image_dtype(img/255.0, dtype=tf.uint8)
show(img)

end = time.time()
end-start

png

7.049884080886841

İsteğe bağlı: Fayanslarla ölçeklendirme

Dikkate alınması gereken bir şey, görüntünün boyutu arttıkça, gradyan hesaplamasını gerçekleştirmek için gereken zaman ve hafızanın da artacağıdır. Yukarıdaki oktav uygulaması, çok büyük görüntülerde veya birçok oktavda çalışmayacaktır.

Bu sorunu önlemek için görüntüyü döşemelere bölebilir ve her döşeme için degradeyi hesaplayabilirsiniz.

Her döşemeli hesaplamadan önce görüntüye rastgele kaydırmalar uygulamak, döşeme dikişlerinin görünmesini engeller.

Rastgele kaydırma uygulayarak başlayın:

def random_roll(img, maxroll):
  # Randomly shift the image to avoid tiled boundaries.
  shift = tf.random.uniform(shape=[2], minval=-maxroll, maxval=maxroll, dtype=tf.int32)
  img_rolled = tf.roll(img, shift=shift, axis=[0,1])
  return shift, img_rolled
shift, img_rolled = random_roll(np.array(original_img), 512)
show(img_rolled)

png

İşte eşdeğer döşenir deepdream daha önce tanımlanmış fonksiyonu:

class TiledGradients(tf.Module):
  def __init__(self, model):
    self.model = model

  @tf.function(
      input_signature=(
        tf.TensorSpec(shape=[None,None,3], dtype=tf.float32),
        tf.TensorSpec(shape=[], dtype=tf.int32),)
  )
  def __call__(self, img, tile_size=512):
    shift, img_rolled = random_roll(img, tile_size)

    # Initialize the image gradients to zero.
    gradients = tf.zeros_like(img_rolled)

    # Skip the last tile, unless there's only one tile.
    xs = tf.range(0, img_rolled.shape[0], tile_size)[:-1]
    if not tf.cast(len(xs), bool):
      xs = tf.constant([0])
    ys = tf.range(0, img_rolled.shape[1], tile_size)[:-1]
    if not tf.cast(len(ys), bool):
      ys = tf.constant([0])

    for x in xs:
      for y in ys:
        # Calculate the gradients for this tile.
        with tf.GradientTape() as tape:
          # This needs gradients relative to `img_rolled`.
          # `GradientTape` only watches `tf.Variable`s by default.
          tape.watch(img_rolled)

          # Extract a tile out of the image.
          img_tile = img_rolled[x:x+tile_size, y:y+tile_size]
          loss = calc_loss(img_tile, self.model)

        # Update the image gradients for this tile.
        gradients = gradients + tape.gradient(loss, img_rolled)

    # Undo the random shift applied to the image and its gradients.
    gradients = tf.roll(gradients, shift=-shift, axis=[0,1])

    # Normalize the gradients.
    gradients /= tf.math.reduce_std(gradients) + 1e-8 

    return gradients
get_tiled_gradients = TiledGradients(dream_model)

Bunu bir araya getirmek, ölçeklenebilir, oktav farkında bir derin rüya uygulaması sağlar:

def run_deep_dream_with_octaves(img, steps_per_octave=100, step_size=0.01, 
                                octaves=range(-2,3), octave_scale=1.3):
  base_shape = tf.shape(img)
  img = tf.keras.preprocessing.image.img_to_array(img)
  img = tf.keras.applications.inception_v3.preprocess_input(img)

  initial_shape = img.shape[:-1]
  img = tf.image.resize(img, initial_shape)
  for octave in octaves:
    # Scale the image based on the octave
    new_size = tf.cast(tf.convert_to_tensor(base_shape[:-1]), tf.float32)*(octave_scale**octave)
    img = tf.image.resize(img, tf.cast(new_size, tf.int32))

    for step in range(steps_per_octave):
      gradients = get_tiled_gradients(img)
      img = img + gradients*step_size
      img = tf.clip_by_value(img, -1, 1)

      if step % 10 == 0:
        display.clear_output(wait=True)
        show(deprocess(img))
        print ("Octave {}, Step {}".format(octave, step))

  result = deprocess(img)
  return result
img = run_deep_dream_with_octaves(img=original_img, step_size=0.01)

display.clear_output(wait=True)
img = tf.image.resize(img, base_shape)
img = tf.image.convert_image_dtype(img/255.0, dtype=tf.uint8)
show(img)

png

Çok daha iyi! DeepDream-ed görüntünüzün görünümünü değiştirmek için oktav sayısı, oktav ölçeği ve etkinleştirilmiş katmanlarla oynayın.

Okuyucular da ilgilenen olabilir TensorFlow Lucid görselleştirmek ve sinir ağları yorumlamak Bu eğitimde tanıtılan fikirler üzerinde genişler.