![]() | ![]() | ![]() | ![]() |
Bu öğretici, Alexander Mordvintsev tarafından yazılan bu blog yazısında açıklandığı gibi, DeepDream'in minimal bir uygulamasını içerir.
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üğü desenleri aşırı yorumlar ve geliştirir.
Bunu ağ üzerinden bir görüntüyü ileterek, ardından görüntünün gradyanını belirli bir katmanın aktivasyonlarına göre hesaplayarak yapar. Görüntü daha sonra bu etkinleştirmeleri artırmak, ağ tarafından görülen kalıpları geliştirmek ve rüya benzeri bir görüntü elde etmek için değiştirilir. Bu sürece "Inceptionizm" adı verildi ( InceptionNet ve Inception filmine bir referans).
Bir sinir ağını nasıl "rüya" haline getirebileceğinizi ve bir görüntüde gördüğü gerçeküstü kalıpları nasıl geliştirebileceğinizi gösterelim.
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 bir labrador görüntüsünü kullanalım.
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>'))
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/YellowLabradorLooking_new.jpg 90112/83281 [================================] - 0s 0us/step
Özellik çıkarma modelini hazırlayın
Önceden eğitilmiş bir görüntü sınıflandırma modeli indirin ve hazırlayın. DeepDream'de orijinal olarak kullanılan modele benzer olan InceptionV3'ü kullanacaksınız. Önceden eğitilmiş herhangi bir modelin çalışacağını, ancak bunu değiştirirseniz aşağıdaki katman adlarını ayarlamanız gerekeceğini unutmayın.
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 [==============================] - 2s 0us/step
DeepDream'deki fikir, bir katman (veya katmanlar) seçmek ve "kaybı", görüntünün katmanları giderek "heyecanlandıracak" şekilde en üst düzeye çıkarmaktır. Dahil edilen özelliklerin karmaşıklığı sizin tarafınızdan seçilen katmanlara bağlıdır, yani daha düşük katmanlar konturlar veya basit desenler üretirken, daha derin katmanlar görüntülerde ve hatta tüm nesnelerde karmaşık özellikler verir.
InceptionV3 mimarisi oldukça büyüktür (model mimarisinin bir grafiği için TensorFlow'un araştırma deposuna bakın). DeepDream için ilgilenilen katmanlar, konvolüsyonların birleştirildiği katmanlardır. InceptionV3'te 'mixed0' ama 'mixed10' adında bu katmanlardan 11 tane vardır. Farklı katmanların kullanılması, farklı rüya benzeri görüntülerle sonuçlanacaktır. Daha derin katmanlar, daha yüksek seviyeli özelliklere (gözler ve yüzler gibi) yanıt verirken, daha önceki katmanlar daha basit özelliklere (kenarlar, şekiller ve dokular gibi) yanıt verir. Aşağıda seçilen katmanları denemekten çekinmeyin, ancak gradyan hesaplaması daha derin olduğu için daha derin katmanların (daha yüksek indeksli olanlar) eğitilmesinin 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 katmanlardan gelen katkı, daha küçük katmanlardan daha ağır basmaz. Normalde, kayıp, gradyan inişi yoluyla en aza indirmek istediğiniz bir miktardır. DeepDream'de, bu kaybı gradyan yükselmesiyle 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 kalan tek şey degradeleri görüntüye göre hesaplamak ve bunları orijinal görüntüye eklemektir.
Görüntüye gradyanların eklenmesi, ağ tarafından görülen kalıpları geliştirir. Her adımda, ağdaki belirli katmanların etkinleştirilmesini giderek daha fazla heyecanlandıran bir görüntü oluşturmuş olacaksınız.
Bunu yapan yöntem, aşağıda, performans için bir tf.function
sarılmıştır. Farklı görüntü boyutları veya steps
/ step_size
değerleri için işlevin yeniden input_signature
emin olmak için bir input_signature
kullanır. Ayrıntılar için Somut işlevler kılavuzuna bakın.
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)
Bir oktav yukarı çekmek
Oldukça iyi, ancak bu ilk denemede birkaç sorun var:
- Çıktı gürültülü (bu bir
tf.image.total_variation
kaybıylatf.image.total_variation
). - Görüntü düşük çözünürlüklü.
- Modeller, hepsi aynı ayrıntı düzeyinde gerçekleşiyormuş gibi görünür.
Tüm bu sorunları ele alan bir yaklaşım, farklı ölçeklerde gradyan yükselmesi uygulamaktır. Bu, daha küçük ölçeklerde üretilen kalıpların daha yüksek ölçeklerde kalıplara dahil edilmesine ve ek ayrıntılarla doldurulmasına izin verecektir.
Bunu yapmak için, önceki gradyan yükselme yaklaşımını uygulayabilir, 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
5.535110235214233
İsteğe bağlı: Kutucuklarla ölçek büyütme
Dikkate alınması gereken bir nokta, görüntünün boyutu arttıkça, gradyan hesaplamasını gerçekleştirmek için gereken zaman ve belleğin de artacağıdır. Yukarıdaki oktav uygulaması çok büyük görüntülerde veya çok sayıda oktavda çalışmayacaktır.
Bu sorunu önlemek için, görüntüyü parçalara bölebilir ve her bir döşeme için gradyanı hesaplayabilirsiniz.
Her döşeme hesaplamasından önce görüntüye rastgele kaydırma uygulamak, döşeme dikişlerinin görünmesini engeller.
Rastgele geçişi 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)
Daha önce tanımlanan deepdream
işlevinin deepdream
bir eşdeğeri:
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 duyarlı bir derin akış 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)
Çok daha iyi! DeepDream-ed görüntünüzün nasıl göründüğünü değiştirmek için oktav sayısı, oktav ölçeği ve aktif katmanlarla oynayın.
Okuyucular, sinir ağlarını görselleştirmek ve yorumlamak için bu eğitimde sunulan fikirleri genişleten TensorFlow Lucid ile de ilgilenebilir.