machinelearningmastery.ru

Машинное обучение, нейронные сети, искусственный интеллект
Header decor

Home

Создание искусства с помощью конвективных нейронных сетей

Дата публикации Jun 9, 2017

В этом посте я использую сверточную нейронную сеть для создания изящного искусства! Это вдохновлено Сираджем (ищите его на YouTube!) И его работой в этой области.

До сих пор искусство всегда было делом воображения, которое оставалось наилучшим образом для креативщиков. У художников был уникальный способ выразить их, и времена, в которые они жили, через уникальную линзу, в зависимости от того, как они смотрели на окружающий мир. Будь то Да Винчи и его вдохновляющая удивительная работа или Ван Гог и его извращенный взгляд на мир, искусство всегда вдохновляло миллионы на протяжении поколений.

Технологии всегда вдохновляли художников раздвигать границы и исследовать возможности, выходящие за рамки того, что уже было сделано. Первая пленочная камера была изобретена не как технология, помогающая искусству, а просто как инструмент для захвата реальности. Очевидно, художники видели это по-разному, порождая всю индустрию кино и анимации. Это верно для каждой крупной технологии, которую мы создали, художники всегда находили способ творчески использовать новый инструмент.

Благодаря последним достижениям в области машинного обучения, мы можем создавать невероятные произведения искусства за считанные минуты, которые, возможно, потребовались опытным художникам годы, чтобы завершить их всего около столетия назад. Машинное обучение создает возможность создания прототипа художественного произведения как минимум в 100 раз быстрее, в то время как среда взаимодействует с художником. Вся прелесть в этом заключается в том, что эта новая волна технического прогресса улучшит способ создания и рассмотрения искусства, обновляя имеющиеся инструменты.

Введение

Здесь я буду использовать python, чтобы взять любое изображение и превратить его в стиль любого художника по моему выбору. В 2015 году Google выпустила аналогичный фильм под названием «Deep Dream», и интернет воспринял его как ошеломляющий энтузиазм. По сути, они обучили сверточную нейронную сеть, которая классифицирует изображения, а затем использовали технику оптимизации для улучшения шаблонов во входном изображении в отличие от его собственных весов на основе того, что изучила сеть. Вскоре после этого появился веб-сайт «Deepart», который позволял пользователям преобразовывать любое изображение в стиль рисования по своему выбору за несколько кликов!

Так как же это работает?

Чтобы понять, как работает эта «магия», называемая процессом передачи стилей, мы напишем наш собственный скрипт на Keras с бэкэндом TensorFlow. Я буду использовать базовое изображение (фотография моего любимого животного) и эталонное изображение стиля. Мой сценарий будет использовать «Звездную ночь» Винсента Ван Гога в качестве эталона и применим его к базовому изображению. Здесь мы сначала импортируем необходимые зависимости:

from __future__ import print_function

import time
from PIL import Image
import numpy as np

from keras import backend
from keras.models import Model
from keras.applications.vgg16 import VGG16

from scipy.optimize import fmin_l_bfgs_b
from scipy.misc import imsave

Поэтому мы будем передавать эти изображения в нейронную сеть, сначала преобразовав их в формат де-факто для всех нейронных сетей, тензоров. Функция переменной из keras backend Tensorflow эквивалентна переменной tf.variable. Параметром для этого будет изображение, преобразованное в массив, и затем мы сделаем то же самое для изображения стиля. Затем мы создаем комбинированное изображение, которое позже может сохранить наши конечные результаты, используя заполнитель для его инициализации с заданной шириной и высотой.

Вот изображение содержания:

height = 512
width = 512

content_image_path = 'images/elephant.jpg'
content_image = Image.open(content_image_path)
content_image = content_image.resize((height, width))
content_image
Содержание изображения (слоны крутые)

Здесь я загружаю изображение стиля:

style_image_path = '/Users/vivek/Desktop/VanGogh.jpg'
style_image = Image.open (style_image_path)
style_image = style_image.resize((height, width))
style_image
Стиль Имидж (должен вытащить классика)

Далее мы конвертируем оба этих изображения, чтобы они были в удобной форме для числовой обработки. Мы добавляем другое измерение (помимо высоты, ширины и нормальных трех измерений), чтобы впоследствии мы могли объединить представления двух изображений в общую структуру данных:

content_array = np.asarray(content_image, dtype='float32')
content_array = np.expand_dims(content_array, axis=0)
print(content_array.shape)

style_array = np.asarray(style_image, dtype='float32')
style_array = np.expand_dims(style_array, axis=0)
print(style_array.shape)

Мы будем использовать сеть VGG, двигаясь вперед. Керас очень хорошо обернул эту модель, чтобы мы могли легко ее использовать по мере продвижения вперед. VGG16 - это 16-слойная сверточная сеть, созданная Visual Geometry Group в Оксфорде, которая выиграла завершение конкурса ImageNet в 2014 году. Идея здесь заключается в том, что CNN, предварительно обученный для классификации изображений на тысячах различных изображений, уже знает, как кодировать информацию в содержании. образ. У меня есть функции изучения на каждом слое, которые могут обнаруживать определенные обобщенные функции. Это те функции, которые мы будем использовать для передачи стиля. Нам не нужен сверточный блок в верхней части этой сети, потому что его полностью соединенные слои и функция softmax помогают классифицировать изображения путем сжатия карты размерности и вывода вероятности. Мы не классифицируем просто перевод. По сути, это оптимизация, где у нас есть некоторая функция потерь, которая измеряет значение ошибки, которое мы будем пытаться минимизировать. Наша функция потерь в этом случае может быть разложена на две части:

1) Потеря контента Мы инициализируем общий убыток до нуля и добавляем каждый из них к нему. Сначала потеря контента. Изображение всегда имеет компонент контента и компонент стиля. Мы знаем, что особенности, которые изучает CNN, расположены в порядке более абстрактных композиций. Так как функции более высокого уровня являются более абстрактными, такими как обнаружение лиц, мы можем связать их с контентом Когда мы запускаем наше выходное изображение и наше эталонное изображение через сеть, мы получаем набор представлений объектов для обоих из скрытого слоя по нашему выбору. Затем мы измеряем евклидово расстояние между ними, чтобы вычислить нашу потерю.

2) Потеря стиля. Это также функция вывода скрытого слоя в нашей сети, но немного более сложная. Мы по-прежнему пропускаем оба изображения через сеть для наблюдения за их активациями, но вместо того, чтобы сравнивать необработанные активации непосредственно для контента, мы добавляем дополнительный шаг для измерения корреляции между активациями. Мы берем то, что называется матрицей грамм для обоих изображений активации на данном слое в сети. Это позволит определить, какие функции имеют тенденцию активироваться вместе. Это в основном представляет вероятность того, что различные функции могут возникать в разных частях изображения. Получив это, мы можем определить эту потерю стиля как евклидово расстояние между матрицами грамм между эталонным изображением и выходным изображением и вычислить общую потерю стиля как взвешенную сумму потери стиля на каждом выбранном нами слое.

Теперь, когда у нас есть потери, нам нужно определить градиенты выходного изображения относительно потерь, а затем использовать эти градиенты, чтобы итеративно минимизировать потери.

Теперь нам нужно помассировать входные данные, чтобы они соответствовали тому, что было сделано вСимонян и Зиссерман (2015), документ, который представил модель VGG Network.

Для этого нам нужно выполнить два преобразования:

  1. Вычтите среднее значение RGB (вычисленное ранее наУчебный комплект ImageNetи легко получить из поиска Google) из каждого пикселя.
  2. Отразить порядок многомерного массива изRGBвBGR(порядок, используемый в статье).
content_array[:,:,:,0] -= 103.99
content_array[:, :, :, 1] -= 116.779
content_array[:, :, :, 2] -= 123.68
content_array = content_array[:, :, :, ::-1]

style_array[:, :, :, 0] -= 103.939
style_array[:, :, :, 1] -= 116.779
style_array[:, :, :, 2] -= 123.68
style_array = style_array[:, :, :, ::-1]

Теперь мы готовы использовать эти массивы для определения переменных в бэкэнде Keras (график TensorFlow). Мы также вводим переменную-заполнитель для хранениясочетаниеизображение, которое сохраняет содержимое изображения содержимого, включая стиль стиля изображения.

content_image = backend.variable(content_array)
style_image = backend.variable(style_array)
combination_image = backend.placeholder((1, height, width, 3))

Теперь мы собираемся объединить все эти данные изображения в единый тензор, который можно использовать для обработки модели Kera VGG16.

input_tensor = backend.concatenate([content_image,
style_image,
combination_image], axis = 0)

Как указывалось ранее, поскольку нас не интересует проблема классификации, нам не нужны полностью связанные слои или окончательный классификатор softmax. Нам нужна только часть модели, отмеченная зеленым в таблице ниже.

Для нас тривиально получить доступ к этой усеченной модели, потому что Keras поставляется с набором предварительно обученных моделей, включая модель VGG16, в которой мы заинтересованы.include_top=Falseв приведенном ниже коде мы не включаем ни один из полностью связанных слоев.

import h5py
model = VGG16(input_tensor=input_tensor, weights='imagenet',
include_top=False)

Как видно из таблицы выше, модель, с которой мы работаем, имеет много слоев. Керас имеет свои собственные названия для этих слоев. Давайте составим список этих имен, чтобы мы могли позже легко обратиться к отдельным слоям.

layers = dict([(layer.name, layer.output) for layer in model.layers])
layers

Теперь мы выбираем веса, это можно поиграть с:

content_weight = 0.025
style_weight = 5.0
total_variation_weight = 1.0

Теперь мы будем использовать функциональные пространства, предоставляемые определенными слоями нашей модели, чтобы определить эти три функции потерь. Мы начнем с инициализации общего убытка до 0 и поэтапного добавления к нему.

loss = backend.variable(0.)

Теперь потеря контента:

def content_loss(content, combination):
return backend.sum(backend.square(combination - content))

layer_features = layers['block2_conv2']
content_image_features = layer_features[0, :, :, :]
combination_features = layer_features[2, :, :, :]

loss += content_weight * content_loss(content_image_features,
combination_features)

И потеря стиля:

def gram_matrix(x):
features = backend.batch_flatten(backend.permute_dimensions(x, (2, 0, 1)))
gram = backend.dot(features, backend.transpose(features))
return gramdef style_loss(style, combination):
S = gram_matrix(style)
C = gram_matrix(combination)
channels = 3
size = height * width
return backend.sum(backend.square(S - C)) / (4. * (channels ** 2) * (size ** 2))

feature_layers = ['block1_conv2', 'block2_conv2',
'block3_conv3', 'block4_conv3',
'block5_conv3']
for layer_name in feature_layers:
layer_features = layers[layer_name]
style_features = layer_features[1, :, :, :]
combination_features = layer_features[2, :, :, :]
sl = style_loss(style_features, combination_features)
loss += (style_weight / len(feature_layers)) * sl

Наконец, общая потеря изменений:

def total_variation_loss(x):
a = backend.square(x[:, :height-1, :width-1, :] - x[:, 1:, :width-1, :])
b = backend.square(x[:, :height-1, :width-1, :] - x[:, :height-1, 1:, :])
return backend.sum(backend.pow(a + b, 1.25))

loss += total_variation_weight * total_variation_loss(combination_image)

Теперь мы продолжим и определим необходимые градиенты для решения задачи оптимизации:

grads = backend.gradients(loss, combination_image)

Затем мы представляем класс Evaluator, который вычисляет потери и градиенты за один проход, извлекая их через две отдельные функции - потери и грады. Это сделано потому, что scipy.optimize требует отдельных функций для потерь и градиентов, но вычислять их отдельно было бы неэффективно.

outputs = [loss]
outputs += grads
f_outputs = backend.function([combination_image], outputs)

def eval_loss_and_grads(x):
x = x.reshape((1, height, width, 3))
outs = f_outputs([x])
loss_value = outs[0]
grad_values = outs[1].flatten().astype('float64')
return loss_value, grad_values

class Evaluator(object):

def __init__(self):
self.loss_value = None
self.grads_values = None

def loss(self, x):
assert self.loss_value is None
loss_value, grad_values = eval_loss_and_grads(x)
self.loss_value = loss_value
self.grad_values = grad_values
return self.loss_value

def grads(self, x):
assert self.loss_value is not None
grad_values = np.copy(self.grad_values)
self.loss_value = None
self.grad_values = None
return grad_values

evaluator = Evaluator()

Теперь мы наконец готовы решить нашу проблему оптимизации. Это комбинированное изображение начинает свою жизнь как случайный набор (действительных) пикселей, и мы используем алгоритм L-BFGS (квазиньютоновский алгоритм, который значительно быстрее сходится, чем стандартный градиентный спуск), чтобы итеративно улучшить его. Мы останавливаемся после 8 итераций, потому что результат выглядит хорошо для меня и потери перестают значительно сокращаться.

x = np.random.uniform(0, 255, (1, height, width, 3)) - 128.

iterations = 8

for i in range(iterations):
print('Start of iteration', i)
start_time = time.time()
x, min_val, info = fmin_l_bfgs_b(evaluator.loss, x.flatten(),
fprime=evaluator.grads, maxfun=20)
print('Current loss value:', min_val)
end_time = time.time()
print('Iteration %d completed in %ds' % (i, end_time - start_time))

Если вы работаете на ноутбуке, как я, возьмите хорошую еду, потому что это займет некоторое время. Вот результат последней итерации!

x = x.reshape((height, width, 3))
x = x[:, :, ::-1]
x[:, :, 0] += 103.939
x[:, :, 1] += 116.779
x[:, :, 2] += 123.68
x = np.clip(x, 0, 255).astype('uint8')

Image.fromarray(x)
Слияние!

Ухоженная! Мы можем продолжить играть с этим, изменив два изображения, их размер, вес наших функций потерь и т. Д. Важно помнить, что выполнение этого всего за 8 итераций заняло у моего macbook около 4 часов. Это очень трудоемкий процесс, поэтому при масштабировании это относительно дорогая проблема для работы.

Спасибо за чтение!

Не стесняйтесь проверить мой GitHub для дальнейшего использования:

vvkv / Художественно-Style-Transfer-использование-сверточный Neural-Nets

Artistic-Style-Transfer-using-Convolutional-Neural-Nets - Здесь я использую видео Сираджа, чтобы попытаться воссоздать стиль ...

github.com

Оригинальная статья

Footer decor

© machinelearningmastery.ru | Ссылки на оригиналы и авторов сохранены. | map