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

Let's train)
Импортируем библиотеки и скачиваем веса InceptionV3 ImageNet — ссылка
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 показал, что нейросети не просто вычисляют, а имеют своеобразное «воображение» — способность находить знакомые паттерны даже там, где их нет.
По сути: мы заставляем ИИ делиться своими «внутренними видениями» и воплощаем их в реальные изображения.
До новых встреч)
