Вступление
В настоящее время всем нравится делать проекты с использованием машинного обучения или глубокого обучения. В этом блоге я дам вам шанс стать «Пикассо» в глубоком обучении, поскольку мы собираемся изучить метод передачи стилей с использованием глубоких сверточных нейронных сетей. мы будем использовать для этого предварительно обученную сеть VGG19.
Не волнуйтесь, это звучит сложно, но на самом деле очень просто. Вам просто нужно быть знакомым с Python, PyTorch и некоторыми материалами для глубокого обучения, такими как CNN, а также с тем, как использовать предварительно обученные сети (поскольку мы будем использовать предварительно обученную CNN для передачи нашего стиля). Я освежу ваши представления о CNN.
Что такое CNN (сверточная нейронная сеть)
Сверточный слой + функция активации, за которым следует слой объединения и линейный слой (для создания желаемого размера вывода) составляют базовые уровни CNN.
- Сверточный слой: он получается путем применения различных фильтров к входному изображению для выделения признаков. Количество используемых фильтров определяет глубину слоя. Функция активации применяется к выводу сверточного слоя для масштабирования.
- Уровень объединения. Максимальный уровень объединения уменьшает размер входных данных по осям x и y и сохраняет только наиболее активные значения пикселей. Ниже приведен пример ядра пула 2x2 с шагом 2.
Почему мы используем VGG19 для передачи стилей
Мы можем использовать любой из VGG16 и VGG19 для извлечения функций, поскольку они работают очень хорошо по сравнению с другими в случае передачи стиля. Например, здесь я использовал VGG19.
Помимо VGG, вы можете использовать SqueezeNet, он быстрее, но результаты худшие, а в случае Inception он работает хорошо, но вам нужно изменить шаг / ядра, максимальный пул на средний пул, поиск по различным комбинациям слоев. Так что VGG на данный момент лучший.
Вы можете прочитать больше здесь"
Как выполнить перенос стиля
Передача стилей использует функции 19-уровневой сети VGG, которая состоит из серии сверточных и объединяющих слоев, а также нескольких полносвязных слоев. Сверточные слои именуются стеком и их порядком в стеке. Поскольку первый сверточный слой называется conv1_1, а самый глубокий сверточный слой - conv5_4.
Передача стилей основана на разделении содержания и стиля изображения. Наша цель - создать новое изображение, содержащее стиль изображения стиля и содержимое изображения содержимого (базовое изображение).
- Контент (объекты и их расположение) из заданного изображения контента.
- Стиль (цвет и текстура) из заданного стиля изображения.
Загрузка в VGG19 (функции):
VGG19 состоит из двух частей:
- vgg19.features (сверточный и объединяющий слой)
- vgg19.classifier (последние три слоя для вывода)
Для переноса стилей нам нужна только часть функций, поэтому мы загрузим ее и зафиксируем веса.
import matplotlib.pyplot as plt import numpy as np import torch import torch.optim as optim from torchvision import transforms, models vgg = models.vgg19(pretrained=True).features # freeze all VGG parameters since we’re only optimizing the target image for param in vgg.parameters(): param.requires_grad_(False)
Загрузить стиль и изображение содержимого:
# define load_image() function which deals with images size # load in content and style image content = load_image('images/x.jpg') # Resize style to match content, makes code easier style = load_image('images/y.jpg',shape=content.shape[-2:]) #covert this images to tensor and apply transforms
Особенности содержания и стиля:
- Для представления содержимого целевого изображения мы пропускаем изображение содержимого через модель и берем выходные данные слоя conv4_2, так как считается, что он содержит наиболее точные функции содержимого.
- Для представления стиля целевого изображения мы рассматриваем выходные данные слоев conv1_1, conv2_1, conv3_1, conv4_1 и conv5_1, опять же по той же причине, содержащей точные особенности стиля.
Затем мы вычислим матрицу грамма для вывода каждого сверточного слоя, используемого для извлечения признаков стиля, чтобы найти взаимосвязь между любой пространственной информацией.
Матрица Грама вычисляется путем умножения матрицы на ее транспонирование.
Создаем холст и собираем все вместе:
# define get_feature() and get content and style features only once before forming the target image content_features = get_features(content, vgg) style_features = get_features(style, vgg) # calculate the gram matrices for each layer of our style representation style_grams = {layer: gram_matrix(style_features[layer]) for layer in style_features} # create a third "target" image and prep it for change # it is a good idea to start of with the target as a copy of our *content* image # then iteratively change its style target = content.clone().requires_grad_(True)
Потеря и вес:
Толщина стиля отдельного слоя
Мы назначаем веса выходным данным каждого слоя, чтобы управлять эффектом их стиля на нашем конечном изображении. Если вам нужны более крупные артефакты стиля, вы должны дать более высокие веса исходным слоям conv1_1, conv2_1 и наоборот. Вес находится в диапазоне 0–1.
Содержание и вес стиля
Мы определяем альфа (content_weight
) и бета (style_weight
). Это соотношение повлияет на то, насколько стилизовано ваше окончательное изображение. Рекомендуется оставить content_weight равным 1 и изменить style_weight.
# weights for each style layer # weighting earlier layers more will result in *larger* style artifacts # notice we are excluding `conv4_2` our content representation style_weights = {'conv1_1': 1., 'conv2_1': 0.75, 'conv3_1': 0.2, 'conv4_1': 0.2, 'conv5_1': 0.2} content_weight = 1 # alpha style_weight = 1e6 # beta
Потеря контента
Это среднеквадратичная разница между целевыми функциями и функциями содержимого на уровне conv4_2.
content_loss = torch.mean((target_features['conv4_2'] - content_features['conv4_2'])**2)
Утрата стиля
Для потери стиля мы вычислим матрицу граммов целевого изображения и затем сравним ее с матрицей граммов изображения стиля на слоях, используемых для извлечения признаков стиля, как conv1_1, conv2_1 и т. Д. Опять же, это среднеквадратичная разница.
Общий убыток
Он будет рассчитан путем добавления стиля и потери контента после их взвешивания с альфа- и бета-версиями.
total_loss = content_weight * content_loss + style_weight * style_loss
Наша цель здесь - минимизировать общие потери за счет повторения и обновления значений.
# for displaying the target image, intermittently show_every = 400 # iteration hyper parameters optimizer = optim.Adam([target], lr =0.003) steps = 2000 # decide how many iterations to update your image (5000) for ii in range(1, steps+1): # get the features from your target image target_features = get_features(target, vgg) # the content loss content_loss = torch.mean((target_features['conv4_2'] - content_features['conv4_2'])**2) # the style loss # initialize the style loss to 0 style_loss = 0 # then add to it for each layer's gram matrix loss for layer in style_weights: # get the "target" style representation for the layer target_feature = target_features[layer] target_gram = gram_matrix(target_feature) _, d, h, w = target_feature.shape # get the "style" style representation style_gram = style_grams[layer] # the style loss for one layer, weighted appropriately layer_style_loss = style_weights[layer] * torch.mean((target_gram - style_gram)**2) # add to the style loss style_loss += layer_style_loss / (d * h * w) # calculate the *total* loss total_loss = content_weight * content_loss + style_weight * style_loss # update your target image optimizer.zero_grad() total_loss.backward() optimizer.step()
Полный код для переноса стиля можно найти здесь
Заключение
Прочитав этот блог, вы получите обзор того, как происходит передача стилей и как работают приложения для редактирования изображений, такие как Prisma. Здесь мы использовали расчет матрицы граммов, но вы также можете улучшить передачу стиля, используя различные другие подходы, такие как сети кодировщика и декодера и т. Д.
Более того, основным недостатком этого метода является то, что мы платим время за лучшие результаты, вы также можете искать перенос стиля в реальном времени как обновление существующего.