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

Этот пост охватывает ниже, чтобы лучше понять перенос стиля:

· Трансферное обучение

· VGG19 Предварительно обученная сеть

· Потеря контента

· Разделение стиля и содержания

· Матрица Грама

· Потеря стиля

· Общие потери при переносе стиля

· Реализация переноса стилей в PyTorch

Трансферное обучение

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

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

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

(Ссылка - https://machinelearningmaster.com/transfer-learning-for-deep-learning/)

Пример трансферного обучения в pytorch-

I. Модели TorchVision используют предварительно обученную модель "resnet".

model = models.resnet50 (pretrained = True)

печать (модель)

ResNet (

(conv1): Conv2d (3, 64, размер_ядра = (7, 7), stride = (2, 2), padding = (3, 3), bias = False)

(bn1): BatchNorm2d (64, eps = 1e-05, импульс = 0,1, affine = True, track_running_stats = True)

(relu): ReLU (на месте)

(maxpool): MaxPool2d (размер ядра = 3, шаг = 2, отступ = 1, расширение = 1, ceil_mode = False)

(слой1): …….

(слой2): …… ..

(слой3): …….

(слой4): …….

(avgpool): AvgPool2d (размер_ядра = 7, шаг = 1, отступ = 0)

(fc): линейный (in_features = 2048, out_features = 1000, bias = True)

)

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

II. Теперь используйте наш собственный классификатор вместо полностью связанного слоя в конце

для параметра в model.parameters ():

param.requires_grad = Ложь

classifier = nn.Sequential (nn.Linear (2048,512),

nn.ReLU (),

nn.Dropout (p = 0,2),

nn.Linear (512,2), # Определить два класса изображений

nn.LogSoftmax (dim = 1)

)

model.fc = classifier # Собственная сеть, а не полностью подключенная повторно

критерий = nn.NLLLoss ()

optimizer = optim.Adam (model.fc.parameters (), lr = 0,003)

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

Разделение стилей и содержания

Ниже показана общая архитектура CNN.

Входное изображение - ›Слой свертки -› Слой объединения - ›Полностью связанный слой. ConvNet размещает свои нейроны в трех измерениях (ширина, высота, глубина), как это показано на одном из слоев. Каждый слой ConvNet преобразует трехмерный входной объем в трехмерный выходной объем активации нейронов. В этом примере красный входной слой содержит изображение, поэтому его ширина и высота будут размерами изображения, а глубина будет равна 3 (красный, зеленый, синий каналы). Последние слои перед полным соединением - это характерное представление изображения. Это представление объекта сплющено, чтобы проходить через полностью связанный слой. Такое представление признаков на глубине слоев больше связано с содержимым этого изображения, а не с любыми текстурами, цветами.

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

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

VGG19 Предварительно обученная сеть

Подход, определенный в документе (ссылка ниже), описан здесь с использованием предварительно обученной сети VGG19.

Эта сеть принимает цветное изображение в качестве входных данных и пропускает его через серию слоев свертки и объединения, за которыми следуют 3 полностью связанных слоя для классификации изображения. Между 5 слоями опроса расположены стопки из 2 и 4 сверточных слоев. Глубина увеличивается после каждого слоя объединения с понижением дискретизации изображения.

Потеря контента

Передача стилей создает новое изображение, беря содержимое из одного изображения и стиля из другого изображения. Каждый из передаваемых изображений контента и стилей передается через эту сеть VGG 19. Сначала, когда изображение содержимого передается через канал, представление содержимого извлекается в самом глубоком сверточном слое перед полностью связанным слоем. Результатом этого слоя является изображение содержимого. Для изображения стиля стиль извлекается из других слоев прерывистой свертки. Теперь у нас есть представление Content и Style, и мы можем создать новое изображение из этих двух. Как создать этот новый образ? Мы можем начать с одного стиля содержимого или содержимого и постепенно добавлять другое представление к другому, пока не минимизируем потери.

В статье представление содержимого берется из вывода 4-го стека свертки (conv4_2). Когда мы формируем новое изображение, мы сравниваем его представление содержимого с представлением содержимого из conv4_2. Эти два должны быть одинаковыми, даже если стиль цели различается. Потеря между этими двумя - это потеря контента.

L Содержимое = среднеквадратичная разница между двумя представлениями

Наша цель - минимизировать эту потерю вместе с потерей стиля.

Матрица Грама

Представление стиля изображения основывается на рассмотрении корреляций между элементами отдельных слоев. Обычно сходство можно найти между несколькими слоями. Рассматривая корреляции между несколькими слоями разных размеров, можно получить представление изображения в разных стилях. Представление стиля захватывается из вывода первого сверточного слоя во всех 5-слойных стеках (conv1_1, conv2_1, conv3_1, conv4_1 и conv5_1). Корреляция на каждом слое дается матрицей Грама.

G = V.V-транспонирование

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

Предположим, что если слой имеет 8 карт характеристик, каждая с L * W 4 * 4, тогда формируется матрица 8 * 16. Это умножается на транспонирование, чтобы получить матрицу граммов 8 * 8. Это больше похоже на матрицу ковариации между каждым признаком.

Теперь у нас есть матрица граммов, представляющая информацию о стиле в слое. Теперь мы можем рассчитать потерю стиля.

Потеря стиля

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

В VGG 19 используются все матрицы, сформированные в 5 слоев.

Ss - Список матриц Грама для стиля

Ts - Список матриц грамм для цели

L-стиль = a * Sum (W * Square (Target - Style))

a - Константа представляет количество значений в каждом слое

W - веса стиля

Общие потери при переносе стиля

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

Расчет потери контента и стиля различается, поэтому их следует рассматривать одинаково. Итак, необходимо применять постоянные веса, Альфа и Бета, Альфа - Вес содержимого, Бета - Вес стиля.

Общая потеря ST = Alpha * Content-Loss + Beta * Style-Loss

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

Реализация переноса стилей в PyTorch

Теперь, когда мы познакомились с концепцией и математикой, лежащей в основе передачи стилей. Давайте реализуем эти концепции в PyTorch.

Импортируйте необходимые модули PyTorch

из PIL импорта изображения

импортировать matplotlib.pyplot как plt

импортировать numpy как np

импорт фонарик

импортировать torch.optim как optim

из torchvision импорта преобразований, моделей

Загрузить предварительно обученную модель VGG19

# VGG19 состоит из двух частей, функций и классификатора.

# Features является частью сети со слоями свертки и максимального пула

# Классификатор является частью сети с 3 полностью подключенными слоями и выходом классификатора

vgg = models.vgg19 (предварительно обучено = True)

# заморозить все параметры VGG, так как мы оптимизируем только целевое изображение

для параметра в vgg.parameters ():

param.requires_grad_ (Ложь)

Загрузить предварительно обученную модель VGG19

# move the model to GPU, if available
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
vgg.to(device)

Загрузить изображения

def load_image(img_path, max_size=400, shape=None):
''' Load in and transform an image, making sure the image
is <= 400 pixels in the x-y dims.'''
image = Image.open(img_path).convert('RGB')
# large images will slow down processing
if max(image.size) > max_size:
size = max_size
else:
size = max(image.size)
if shape is not None:
size = shape
in_transform = transforms.Compose([
transforms.Resize(size),
transforms.ToTensor(),
transforms.Normalize((0.485, 0.456, 0.406),
(0.229, 0.224, 0.225))])
# discard the transparent, alpha channel (that's the :3) and add the batch dimension
image = in_transform(image)[:3,:,:].unsqueeze(0)
return image
# helper function for un-normalizing an image
# and converting it from a Tensor image to a NumPy image for display
def im_convert(tensor):
""" Display a tensor as an image. """
image = tensor.to("cpu").clone().detach()
image = image.numpy().squeeze()
image = image.transpose(1,2,0)
image = image * np.array((0.229, 0.224, 0.225)) + np.array((0.485, 0.456, 0.406))
image = image.clip(0, 1)
return image
# load in content and style image, using shape parameter to make both content and style of same shape to make processing easier
content = load_image("C:\\Users\\vprayagala2\\Pictures\\Content_Img.jpg",shape=[400,400]).to(device)
style = load_image("C:\\Users\\vprayagala2\\Pictures\\Style_Img.jpg", shape=[400,400]).to(device)
# display the images
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 10))
# content and style ims side-by-side
ax1.imshow(im_convert(content))
ax2.imshow(im_convert(style))

Извлечение элементов стиля и содержимого

# Print VGG19 network to understand the layers
print(vgg)

Номера и название слоев могут быть соотнесены с одним из представленных на схеме архитектуры VGG19.

def get_features(image, model, layers=None):
""" Run an image forward through a model and get the features for
a set of layers. Default layers are for VGGNet matching Gatys et al (2016)
"""
## Need the layers for the content and style representations of an image
# As mentioned, conv4_2 is content representation
# Conv1_1 thru conv5_1 is for style representation
if layers is None:
layers = {'0': 'conv1_1',
'5': 'conv2_1',
'10': 'conv3_1',
'19': 'conv4_1',
'21': 'conv4_2',  ## content representation is output of this layer
'28': 'conv5_1'}
features = {}
x = image
# model._modules is a dictionary holding each module in the model
for name, layer in model._modules.items():
x = layer(x)
if name in layers:
features[layers[name]] = x
return features
# get content and style features only once before training
content_features = get_features(content, vgg)
style_features = get_features(style, vgg)

Матрица Грама

Результатом каждого сверточного слоя является тензор с размерами, связанными с batch_size, глубиной d и некоторой высотой и шириной (h, w). Матрицу Грама сверточного слоя можно вычислить следующим образом:

Получите глубину, высоту и ширину тензора, используя batch_size, d, h, w = tensor.size.

Измените этот тензор так, чтобы пространственные размеры были сглажены.

Вычислите матрицу грамма, умножив измененный тензор на его транспонированный

Примечание. Вы можете перемножить две матрицы, используя torch.mm (matrix1, matrix2).

def gram_matrix(tensor):
""" Calculate the Gram Matrix of a given tensor
Gram Matrix: https://en.wikipedia.org/wiki/Gramian_matrix
"""
# get the batch_size, depth, height, and width of the Tensor
_, d, h, w = tensor.size()
# reshape so we're multiplying the features for each channel
tensor = tensor.view(d, h * w)
# calculate the gram matrix
gram = torch.mm(tensor, tensor.t())
return gram

Рассчитайте потерю передачи стиля, минимизируйте ее и улучшите целевое изображение

Теперь у нас есть вся информация, рассчитаем потерю передачи стиля.

Индивидуальные веса стиля слоя

Ниже вам предоставляется возможность взвесить представление стиля на каждом соответствующем слое. Рекомендуется использовать диапазон от 0 до 1 для взвешивания этих слоев. Увеличивая более ранние слои (conv1_1 и conv2_1), вы можете рассчитывать на получение более крупных артефактов стиля в конечном конечном изображении. Если вы выберете взвешивание более поздних слоев, вы получите больший акцент на более мелких элементах. Это потому, что каждый слой имеет разный размер, и вместе они создают многомасштабное представление стиля!

Содержание и вес стиля

Как и в статье, мы определяем альфа (вес контента) и бета (вес стиля). Это соотношение повлияет на то, насколько стилизовано ваше окончательное изображение. Рекомендуется оставить вес контента = 1 и установить вес стиля для достижения желаемого соотношения.

# for displaying the target image, intermittently
show_every = 400
# iteration hyperparameters
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()
# display intermediate images and print the loss
if  ii % show_every == 0:
print('Total loss: ', total_loss.item())
plt.imshow(im_convert(target))
plt.show()
# display content and final, target image
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 10))
ax1.imshow(im_convert(content))
ax2.imshow(im_convert(target))

Код был запущен в Google Colab для использования аппаратного обеспечения графического процессора. Заполненный код доступен по ссылке ниже. Можно поэкспериментировать с различными значениями бета-версии, чтобы увидеть, как стиль отражается в целевом изображении. Экспериментировал со значениями бета 1e6 и 1e8. Тот же результат ниже, если с бета-версией 1e8



Пример вывода

Ссылка

· Статья о переносе стилей - https://www.cv-foundation.org/openaccess/content_cvpr_2016/papers/Gatys_Image_Style_Transfer_CVPR_2016_paper.pdf

· Udacity - PyTorch Nanogegree

· Stanford CNN для визуального распознавания - http://cs231n.github.io/convolutional-networks/#overview