Про Deep Dream с примером
Знаменитые «сны» нейросетей, которые заполонили интернет в 2015 году — это заслуга DeepDream от Google. Однако сама технология их создания родилась раньше и изначально применялась, например, для синтеза текстур. Проходя курсы по CV, одним из блоков у меня было изучение ее. В ходе статьи буду опираться на гугл колаб ноутбук и личные наблюдения.

Let's train)
Импортируем библиотеки и скачиваем веса InceptionV3 ImageNet - https://www.tensorflow.org/api_docs/python/tf/keras/applications/inception_v3
imp
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
tf.__version__
base_model = tf.keras.applications.InceptionV3(include_top=False, weights='imagenet')
ort tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
tf.__version__
base_model = tf.keras.applications.InceptionV3(include_top=False, weights='imagenet')
Cмотрим структуру сети, она содержит 311 слоев. Покажем начало и конец вывода, дабы на заполнять пространство простыней.
base_model.summary()
input_layer │ (None, None, │ 0 │ - │
│ (InputLayer) │ None, 3) │ │ │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ conv2d (Conv2D) │ (None, None, │ 864 │ input_layer[0][0] │
│ │ None, 32) │ │ │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ batch_normalization │ (None, None, │ 96 │ conv2d[0][0] │
│ (BatchNormalizatio… │ None, 32) │ │ │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
----------------------------------------------------------------------------
│ activation_85 │ (None, None, │ 0 │ batch_normalizat… │
│ (Activation) │ None, 320) │ │ │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ mixed9_1 │ (None, None, │ 0 │ activation_87[0]… │
│ (Concatenate) │ None, 768) │ │ activation_88[0]… │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ concatenate_1 │ (None, None, │ 0 │ activation_91[0]… │
│ (Concatenate) │ None, 768) │ │ activation_92[0]… │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ activation_93 │ (None, None, │ 0 │ batch_normalizat… │
│ (Activation) │ None, 192) │ │ │
├─────────────────────┼───────────────────┼────────────┼───────────────────┤
│ mixed10 │ (None, None, │ 0 │ activation_85[0]… │
│ (Concatenate) │ None, 2048) │ │ mixed9_1[0][0], │
Total params: 21,802,784 (83.17 MB)
Trainable params: 21,768,352 (83.04 MB)
Non-trainable params: 34,432 (134.50 KB)
Следующим этапом мы "открываем" готовую нейросеть, чтобы увидеть, как она реагирует на разных этапах обработки изображения, и создает новую модель для генерации тех самых "снов", где усиливаются паттерны, которые она научилась распознавать. Но сначала нам необходимо добавить промежуточные слои оригинальной нейросети (InceptionV), где :
mixed3
(нижние слои) — реагирует на простые формы: линии, углы, текстуры, пятна.mixed5
(средние слои) — реагирует на более сложные паттеры: глаза, лапы и т.д.
#определяем слои
names = ['mixed3', 'mixed5']
Создаем новую модель:
#Узнаем сколько слоев в базовой модели
len(base_model.layers)
#смотрим на входные данные модели
base_model.input
#Создание новой модели для DeepDream
layers = [base_model.get_layer(name).output for name in names]
deep_dream_model = tf.keras.Model(inputs = base_model.input, outputs = layers)
#Сохраняем в файлик
!pip list > res.txt
Загружаем и препроцессим любое изображение (в моем случае это картина серфера)
image = tf.keras.preprocessing.image.load_img('ВАШЕ_ИЗОБРАЖЕНИЕ',target_size=(225, 375))

#Узнаем тип (класс) объекта
type(image)
#Получаем размер картинки в пикселях (ширина, высота)
image.size
# Проверяем режим изображения и количество каналов
('RGB', 3)
#Преобразуем изображение в плоский список всех пикселей
list(image.getdata())
# Конвертируем изображение из формата PIL в массив NumPy
image = tf.keras.preprocessing.image.img_to_array(image)
# Проверяем тип объекта - теперь это <class 'numpy.ndarray'>
type(image)
# Смотрим форму массива
image.shape
# Проверяем диапазон значений в массиве (0, 255) - минимальное 0, максимальное 255
image.min(), image.max()
#получаем
(np.float32(0.0), np.float32(255.0))
Преобразуем значения пикселей в тот формат, который ожидает модель InceptionV3 (preprocess_input()
- приводим числа к тому формату, который понимает нейросеть.
image = tf.keras.applications.inception_v3.preprocess_input(image)
image.min(), image.max()
#получаем
(np.float32(-1.0), np.float32(1.0))
Дальше посмотрим что происходит внутри нейросети в реальном времени, когда она обрабатывает изображение вызывая метод predict(image_batch)
activations = deep_dream_model.predict(image_batch)
deep_dream_model.outputs
activations[1]
Смотрим вывод :
начало
array([[[[2.3790298e+00, 1.8560309e+00, 0.0000000e+00, ...,
0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
[0.0000000e+00, 0.0000000e+00, 1.7179147e+00, ...,
0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
[0.0000000e+00, 1.8772808e+00, 0.0000000e+00, ...,
0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
------------------------------------------------------------4 - 5 слоев
[0.0000000e+00, 3.0609429e-01, 1.9720533e-01, ...,
3.3833697e-01, 0.0000000e+00, 0.0000000e+00],
[0.0000000e+00, 0.0000000e+00, 6.1899835e-01, ...,
2.5179255e-01, 0.0000000e+00, 0.0000000e+00],
[0.0000000e+00, 0.0000000e+00, 0.0000000e+00, ...,
1.3538781e-01, 0.0000000e+00, 0.0000000e+00]]]], dtype=float32)
конец
смотрим форму :
activations[0].shape, activations[1].shape
((1, 12, 21, 768), (1, 12, 21, 768))
аctivations — это список массивов чисел, где каждый массив соответствует одному из выбранных слоев (mixed3, mixed5)
Считаем loss
def calculate_loss(image, network):
image_batch = tf.expand_dims(image, axis = 0)
activations = network(image_batch)
losses = []
for act in activations:
loss = tf.math.reduce_mean(act)
losses.append(loss)
#print(losses)
#print(np.shape(losses))
#print(tf.reduce_sum(losses))
return tf.reduce_sum(losses)
loss = calculate_loss(image, deep_dream_model)
loss
Получаем 0.59 — это средний уровень "возбуждения" нейронов в выбранных слоях (mixed3, mixed5)
<tf.Tensor: shape=(), dtype=float32, numpy=0.5989360809326172>
Если ближе к 0 — тем меньше галлюцинаций (скучное изображение)
Если дальше от 0 — тем ярче её "галлюцинации" (уже не скучно)
Формируем градиентный спуск ( буквально процесс воплощения галлюцинаций нейросети в пикселях )
@tf.function
def deep_dream(network, image, learning_rate):
with tf.GradientTape() as tape:
tape.watch(image)
loss = calculate_loss(image, network)
gradients = tape.gradient(loss, image) # Derivate
gradients /= tf.math.reduce_std(gradients)
image = image + gradients * learning_rate
image = tf.clip_by_value(image, -1, 1)
return loss, image
def inverse_transform(image):
image = 255 * (image + 1.0) / 2.0
return tf.cast(image, tf.uint8)
def run_deep_dream(network, image, epochs, learning_rate):
for epoch in range(epochs):
loss, image = deep_dream(network, image, learning_rate)
if epoch % 200 == 0:
plt.figure(figsize=(12,12))
plt.imshow(inverse_transform(image))
plt.show()
print('Epoch {}, loss {}'.format(epoch, loss))
Запускаем генерцию снов!
run_deep_dream(network=deep_dream_model, image=image, epochs = 8000, learning_rate=0.0001)
Смотрим на результат с каждой эпохи картинка погружается в "сон"



Deep Dream показал, что нейросети не просто вычисляют, а имеют своеобразное "воображение" — способность находить знакомые паттерны даже там, где их нет.
По сути: мы заставляем ИИ делиться своими "внутренними видениями" и воплощаем их в реальные изображения.
Ссылка на колаб - https://colab.research.google.com/drive/1Yp2IH3dpcaUUqOd2yvkxOZqTto4uGOFM?usp=sharing
До новых встреч)