Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Шолле Ф. - Глубокое обучение на Python (Библиотека программиста) - 2023.pdf
Скачиваний:
6
Добавлен:
07.04.2024
Размер:
11.34 Mб
Скачать

12.3. Нейронная передача стиля    473

12.3. НЕЙРОННАЯ ПЕРЕДАЧА СТИЛЯ

Кроме.DeepDream,.существует.еще.одна.важная.разработка.в.области.изменения. изображений.с.использованием.глубокого.обучения.—.нейронная передача стиля,.реализованная.Леоном.Гатисом.с.коллегами.летом.2015.года1..После.своего. появления.алгоритм.нейронной.передачи.стиля.претерпел.множество.усовершенствований,.породил.множество.вариаций.и.нашел.применение.во.множестве. приложений.обработки.фотографий.для.смартфонов..Для.простоты.в.этом.разделе. основное.внимание.уделяется.формулировке.из.оригинальной.статьи.

Нейронная.передача.стиля.заключается.в.применении.стиля.изображения-об- разца.к.целевому.изображению.при.сохранении.содержимого.этого.целевого. изображения..Пример.передачи.стиля.изображен.на.рис..12.9.

Рис. 12.9. Пример передачи стиля

В.данном.контексте.под.стилем.в.основном.подразумеваются.текстуры,.цветовая.палитра.и.визуальные.шаблоны.в.различных.пространственных.масштабах;. а.под.содержимым.—.высокоуровневая.макроструктура.изображения..Например,. сине-желтые.круговые.мазки.на.рис..12.9.соответствуют.стилю.(в.качестве.об- разца.использована.картина.Винсента.Ван.Гога.«Звездная.ночь»),.а.здания.на. фотографии,.сделанной.фотографом.Тюбингеном,.—.это.содержимое.

Идея.передачи.стиля,.тесно.связанная.с.созданием.текстур,.давно.вынашивалась.в.сообществе.людей,.увлеченных.обработкой.изображений,.прежде.чем. воплотилась.в.алгоритм.нейронной.передачи.стиля.в.2015.году..Однако,.как. оказалось,.реализации.передачи.стиля,.основанные.на.глубоком.обучении,. не.имеют.аналогов.среди.прежних.достижений,.использовавших.классические. методики.компьютерного.зрения,.и.потому.они.породили.удивительный.бум. в.сфере.художественных.приложений.компьютерного.зрения.

В.основе.реализации.передачи.стиля.лежит.та.же.идея,.которая.занимает.центральное.положение.во.всех.алгоритмах.глубокого.обучения:.вы.задаете.функцию. потерь,.чтобы.определить.цель.для.достижения,.и.минимизируете.ее..Вы.знаете,. чего.хотите:.сохранить.содержимое.исходного.изображения.и.передать.стиль.

1. Gatys L. A., Ecker A. S., Bethge M..A.Neural.Algorithm.of.Artistic.Style.//.arXiv,.2015,. https://arxiv.org/abs/1508.06576.

474    Глава 12. Генеративное глубокое обучение

изображения-образца..Определив.математически.содержимое .и.стиль,.соот- ветствующую.функцию.потерь.для.минимизации.можно.обозначить.так:

loss = (distance(style(reference_image) - style(combination_image)) + distance(content(original_image) - content(combination_image)))

Здесь.distance .—.это.функция.нормы,.такой.как.L2-норма,.content .—.функция,. принимающая.изображение.и.вычисляющая.представление.его.содержимо- го, .а .style .— .функция, .принимающая .изображение .и .вычисляющая .пред- ставление.его.стиля..Минимизация.этой.функции.потерь.приводит.к.тому,. что .style(combination_image) .приближается .к .style(reference_image),. а.content(combination_image) .—.к.content(original_image),.то.есть.достигается. передача.стиля,.как.мы.ее.определили.

Фундаментальное.наблюдение,.сделанное.Гатисом.с.коллегами,.заключается. в.том,.что.глубокие.сверточные.нейронные.сети.дают.возможность.математически.определить.функции.style .и.content..Посмотрим,.как.это.происходит.

12.3.1. Функция потерь содержимого

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

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

12.3.2. Функция потерь стиля

Функция.потерь.содержимого.использует.только.один.верхний.слой,.но.функция. потерь.стиля,.согласно.определению.Гатиса.и.его.коллег,.использует.несколько. слоев.сверточной.сети:.ее.цель.—.захватить.внешний.вид.стиля.изображения-об- разца.не.в.одном,.а.во.всех.пространственных.масштабах,.выделяемых.сверточной. сетью..В.качестве.функции.потерь.стиля.Гатис.с.коллегами.использует.матрицу Грама.активаций.слоя:.внутреннее.произведение.карт.признаков.данного.слоя.. Это.внутреннее.произведение.можно.интерпретировать.как.матрицу.корреляций.

12.3. Нейронная передача стиля    475

между.признаками.слоя..Корреляции.фиксируют.статистики.шаблонов.определенного.пространственного.масштаба,.которые.эмпирически.соответствуют. текстурам,.обнаруженным.в.этом.масштабе.

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

Проще.говоря,.предварительно.обученную.сверточную.сеть.можно.использовать. для.определения.потерь,.и.она.будет:

.сохранять.содержимое,.поддерживая.сходство.активаций.верхнего.слоя. между.содержимым.целевого.и.сгенерированного.изображений..Сверточная. сеть.должна.«видеть».оба.изображения.—.целевое.и.сгенерированное.—.как. содержащие.одно.и.то.же;

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

Теперь.рассмотрим.реализацию.оригинального.алгоритма.нейронной.передачи. стиля.2015.года.с.применением.Keras..Как.вы.увидите.далее,.он.имеет.много. общего.с.реализацией.DeepDream,.представленной.в.предыдущем.разделе.

12.3.3. Нейронная передача стиля в Keras

Нейронную.передачу.стиля.можно.реализовать.с.использованием.любой.обученной.сверточной.сети..Здесь.мы.возьмем.сеть.VGG19,.которую.использовали. Гатис.с.коллегами..VGG19.—.это.упрощенный.вариант.сети.VGG16,.представ- ленной.в.главе.9,.с.тремя.сверточными.слоями.

Вот.как.выглядит.весь.процесс.в.общих.чертах.

.Настройка.сети,.которая.вычисляет.активации.слоя.VGG19.одновременно. для.изображения-образца,.целевого.и.сгенерированного.изображений.

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

.Настройка.процедуры.градиентного.восхождения.для.минимизации.этой. функции.потерь.

Сначала.определим.пути.к.изображению-образцу.и.целевому.изображению..

Чтобы.гарантировать.совместимость.размеров.обрабатываемых.изображений. (сильно.различающиеся.размеры.затрудняют.передачу.стиля),.приведем.их. к.общей.высоте.400.пикселей.

476    Глава 12. Генеративное глубокое обучение

Листинг 12.16. Получение стиля и содержимого изображений from tensorflow import keras

 

 

Путь к изображению, которое

 

 

base_image_path = keras.utils.get_file(

 

будет трансформироваться

 

 

 

 

 

 

"sf.jpg", origin="https://img-datasets.s3.amazonaws.com/sf.jpg")

style_reference_image_path = keras.utils.get_file(

 

 

Путь к изображению

 

 

 

"starry_night.jpg",

 

 

 

с образцом стиля

origin="https://img-datasets.s3.amazonaws.com/starry_night.jpg")

original_width, original_height = keras.utils.load_img(base_image_path).size

img_height = 400

 

 

 

 

Размеры генерируемого

 

 

 

 

img_width = round(original_width * img_height / original_height)

изображения

Изображение,.которое.будет.служить.нам.источником.содержимого,.показано. на.рис..12.10..На.рис..12.11.помещено.изображение.—.образец.стиля.

Рис. 12.10. Изображение с содержимым: Сан-Франциско, район Ноб-Хилл

Рис. 12.11. Образец стиля: картина Ван Гога «Звездная ночь»

12.3. Нейронная передача стиля    477

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

Листинг 12.17. Вспомогательные функции

import numpy as np

 

 

Открывает изображение, изменяет его размер

 

 

 

 

 

def preprocess_image(image_path):

 

 

и преобразует в соответствующий массив

 

 

img = keras.utils.load_img(

 

 

 

image_path, target_size=(img_height, img_width))

img = keras.utils.img_to_array(img)

img = np.expand_dims(img, axis=0)

img = keras.applications.vgg19.preprocess_input(img)

return img

 

 

Вспомогательная функция для преобразования

 

 

 

 

 

def deprocess_image(img):

 

 

массива NumPy в допустимое изображение

 

 

 

img = img.reshape((img_height, img_width, 3))

img[:, :, 0] += 103.939

 

 

Центрировать относительно нуля путем удаления

 

img[:, :, 1] += 116.779

 

 

среднего значения пикселя из ImageNet. Это отменяет

img[:, :, 2] += 123.68

 

 

преобразование, выполненное vgg19.preprocess_input

img

=

img[:, :, ::-1]

 

Конвертировать изображения из BGR

 

img

=

np.clip(img, 0, 255).astype("uint8")

в RGB. Также отменяет преобразование,

return img

выполненное vgg19.preprocess_input

Настроим.сеть.VGG19..По.аналогии.с.реализацией.DeepDream.используем. предварительно.обученную.сверточную.сеть.и.создадим.модель,.извлекающую. признаки.и.возвращающую.активации.промежуточных.слоев.—.на.этот.раз.всех. слоев.

Листинг 12.18. Использование предварительно обученной модели VGG19 для извлечения признаков

Создать модель VGG19 с загруженными весами, полученными в результате обучения на наборе ImageNet

model = keras.applications.vgg19.VGG19(weights="imagenet", include_top=False)

outputs_dict = dict([(layer.name, layer.output) for layer in model.layers]) feature_extractor = keras.Model(inputs=model.inputs, outputs=outputs_dict)

Модель, возвращающая значения активаций всех целевых слоев (в виде словаря)

Теперь.определим.функцию.потерь.содержимого,.которая.позволит.гарантировать.сходство.представлений.целевого.и.сгенерированного.изображений. в.верхнем.слое.сети.VGG19.

Листинг 12.19. Функция потерь содержимого

def content_loss(base_img, combination_img):

return tf.reduce_sum(tf.square(combination_img - base_img))

478    Глава 12. Генеративное глубокое обучение

Далее.приводится.функция.потерь.стиля..Она.использует.вспомогательную. функцию.для.вычисления.матрицы.Грама.из.входной.матрицы:.матрицы.корреляций,.найденных.в.матрице.оригинальных.признаков.

Листинг 12.20. Функция потерь стиля

def gram_matrix(x):

x = tf.transpose(x, (2, 0, 1))

features = tf.reshape(x, (tf.shape(x)[0], -1)) gram = tf.matmul(features, tf.transpose(features)) return gram

def style_loss(style_img, combination_img): S = gram_matrix(style_img)

C = gram_matrix(combination_img) channels = 3

size = img_height * img_width

return tf.reduce_sum(tf.square(S - C)) / (4.0 * (channels ** 2) * (size ** 2))

К.этим.двум.компонентам.потерь.добавляется.третий:.функция.общей потери вариации (total.variation.loss),.которая.оперирует.пикселями.генерируемого. изображения..Она.стимулирует.пространственную.целостность.генерируемого. изображения,.что.позволяет.избежать.появления.мозаичного.эффекта..Ее.можно. интерпретировать.как.регуляризацию.потерь.

Листинг 12.21. Функция общей потери вариации

def total_variation_loss(x): a = tf.square(

x[:, : img_height - 1, : img_width - 1, :] - x[:, 1:, : img_width - 1, :]

)

b = tf.square(

x[:, : img_height - 1, : img_width - 1, :] - x[:, : img_height - 1, 1:, :]

)

return tf.reduce_sum(tf.pow(a + b, 1.25))

Функция.потерь,.которую.мы.должны.минимизировать,.возвращает.среднее. взвешенное.этих.трех.компонентов..Для.вычисления.потери.содержимого.используется.только.один.верхний.слой.block5_conv2,.а.для.вычисления.потери. содержимого.—.список.слоев,.включающий.в.себя.нижние.и.верхние.слои..Общая. потеря.вариации.добавляется.в.конец.

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

12.3. Нейронная передача стиля    479

Листинг 12.22. Функция общей потери вариации, которая будет минимизироваться

style_layer_names = [

 

 

Список слоев, участвующих

 

 

"block1_conv1",

 

 

в вычислении потери стиля

"block2_conv1",

 

 

 

 

 

 

 

 

 

 

 

"block3_conv1",

 

 

 

 

 

 

 

 

 

"block4_conv1",

 

 

 

 

 

 

 

 

 

"block5_conv1",

 

 

 

 

 

 

 

 

Слой, используемый для вычисления

]

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

потерь содержимого

content_layer_name = "block5_conv2"

 

 

 

 

 

 

 

total_variation_weight = 1e-6

 

 

 

 

 

 

 

Вес вклада общей потери вариации

 

 

 

 

 

 

 

style_weight = 1e-6

 

 

 

 

 

Вес вклада потери стиля

 

 

 

 

 

content_weight = 2.5e-8

 

 

Вес вклада потери содержимого

 

 

def compute_loss(combination_image, base_image, style_reference_image): input_tensor = tf.concat(

[base_image, style_reference_image, combination_image], axis=0)

features = feature_extractor(input_tensor)

 

 

Инициализация

 

loss = tf.zeros(shape=())

 

 

 

потери нулями

 

 

 

layer_features = features[content_layer_name]

 

 

 

 

 

 

 

 

base_image_features = layer_features[0, :, :, :]

Добавление потери

combination_features = layer_features[2, :, :, :]

содержимого

loss = loss + content_weight * content_loss(

 

 

Добавление

 

base_image_features, combination_features

 

 

)

 

 

 

 

потери стиля

 

for layer_name in style_layer_names:

 

 

 

 

 

 

 

 

layer_features = features[layer_name]

 

 

 

 

style_reference_features = layer_features[1, :, :, :]

 

 

combination_features = layer_features[2, :, :, :]

 

 

 

 

style_loss_value = style_loss(

 

 

 

 

style_reference_features, combination_features)

 

 

loss += (style_weight / len(style_layer_names)) * style_loss_value

 

 

loss += total_variation_weight * total_variation_loss(combination_image)

return loss

Добавление общей потери вариации

Наконец,.настроим.процесс.градиентного.восхождения..В.оригинальной.статье. Гатиса.оптимизация.проводится.с.использованием.алгоритма.L-BFGS,.но.он. недоступен.в.TensorFlow,.поэтому.мы.выполним.обычный.мини-пакетный. градиентный.спуск.с.оптимизатором.SGD..При.этом.мы.будем.использовать.особенность.оптимизатора,.которую.вы.еще.не.видели:.возможность.планирования. скорости.обучения..Мы.воспользуемся.ею,.чтобы.постепенно.снижать.скорость. обучения.с.очень.высокого.значения.(100).до.гораздо.меньшего.конечного. значения.(около.20)..Так.мы.добьемся.быстрого.прогресса.на.ранних.этапах. обучения,.а.затем.будем.действовать.более.осторожно.по.мере.приближения. к.минимуму.потерь.

Сохраним комбинированное изображение через регулярные интервалы

480    Глава 12. Генеративное глубокое обучение

Листинг 12.23. Настройка процесса градиентного спуска

import tensorflow as tf

Чтобы ускорить обучение,

 

 

 

 

@tf.function

 

скомпилируем его как tf.function

 

def compute_loss_and_grads(

combination_image, base_image, style_reference_image): with tf.GradientTape() as tape:

loss = compute_loss(

 

combination_image, base_image, style_reference_image)

grads =

tape.gradient(loss, combination_image)

 

 

return loss, grads

 

На первых этапах используем

 

 

 

 

 

скорость обучения 100, а затем

optimizer =

keras.optimizers.SGD(

 

будем уменьшать ее на 4 %

keras.optimizers.schedules.ExponentialDecay(

 

 

через каждые 100 шагов

 

 

initial_learning_rate=100.0, decay_steps=100, decay_rate=0.96

)

)

base_image = preprocess_image(base_image_path)

style_reference_image = preprocess_image(style_reference_image_path) combination_image = tf.Variable(preprocess_image(base_image_path))

iterations = 4000

Используем Variable для хранения

комбинированного изображения, которое

for i in range(1, iterations + 1):

будет изменяться в процессе обучения

loss, grads = compute_loss_and_grads(

 

 

 

 

combination_image, base_image, style_reference_image

 

Обновим

 

)

 

 

 

 

 

 

комбинированное

optimizer.apply_gradients([(grads, combination_image)])

 

 

 

 

изображение

 

 

if i % 100 == 0:

 

 

 

в направлении

print(f"Iteration {i}: loss={loss:.2f}")

 

 

 

уменьшения потери

img = deprocess_image(combination_image.numpy())

 

передачи стиля

 

fname = f"combination_image_at_iteration_{i}.png" keras.utils.save_img(fname, img)

На.рис..12.12.показано,.что.получается.в.результате..Имейте.в.виду,.что.этот. прием.—.лишь.одна.из.форм.ретекстурирования.изображений,.или.передачи. текстуры..Лучшие.результаты.с.его.применением.получаются,.если.изображения. с.образцами.стилей.сильно.текстурированы.и.самоподобны,.а.целевые.изображения.с.содержимым.не.требуют.различения.мелких.деталей,.чтобы.их.можно. было.опознать..Этот.прием.не.наделен.возможностями.абстрагирования.—.с.его. помощью.едва.ли.получится.перенести.стиль.из.одного.портрета.в.другой..Данный.алгоритм.ближе.к.классической.обработке.сигналов,.чем.к.ИИ,.поэтому. не.нужно.ожидать.от.него.чего-то.сверхъестественного!

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