使用 PyTorch 进行艺术神经风格迁移
概述
在本指南中,您将在 PyTorch 中实现艺术风格转换 (NST) 神经网络算法。此算法将允许您获得毕加索风格的图像。它通过创建一个新图像来实现这一点,该新图像将一个图像的风格(绘画)与另一个图像的内容(输入图像)混合在一起。
感谢 Leon A. Gatys 等人在其论文《艺术风格的神经算法》中做出的贡献。
为了使模型更快、更准确,使用了预先训练的VGG-Net-19(Visual Geometry Group)。该模型在 ImageNet 图像上进行训练,可以从 Pytorch API 下载。
导入库
要使用 PyTorch,请导入torch库。PIL图像库将处理图像。从函数torchvision中,您将导入模型类并调用vgg19模型。
%matplotlib inline
from PIL import Image
from io import BytesIO
import matplotlib.pyplot as plt
import numpy as np
import torch
import torch.optim as optim
import requests
from torchvision import transforms, models
建立模型
加载 VGG-Net-19 模型并保持pretrained=True。数字 19 表示网络中涉及的层数。VGG 也可用于分类问题(人脸检测)。但在 NST 中,您只处理特征。param.requires_grad_ ()将冻结所有 VGG 参数,因为您只优化目标图像。
vgg = models.vgg19(pretrained=True).features
for param in vgg.parameters():
param.requires_grad_(False)
但是 PyTorch 有一个问题!在这里你必须检查系统中是否有 GPU。如果有,则将vgg模型移至 GPU。如果没有,则模型将在 CPU 上运行。如果你没有 GPU?不用担心——你可以使用Google Colab。
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
vgg.to(device)
图像越小,处理速度越快。torchvision的变换类在对图像进行预处理和规范化时起着重要作用。
def load_image(img_path, max_size=400, shape=None):
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))])
image = in_transform(image)[:3,:,:].unsqueeze(0)
return image
style = load_image("Picasso.jpg").to(device)
content = load_image("houses.png").to(device)
将张量显示为图像。
def im_convert(tensor):
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
并排显示图像。
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 10))
ax1.imshow(im_convert(content))
ax1.set_title("Content-Image",fontsize = 20)
ax2.imshow(im_convert(style))
ax2.set_title("Style-Image", fontsize = 20)
plt.show()
print(vgg)
风格和内容的中间层
VGG-19 的较深层将提取最佳和最复杂的特征。因此,分配conv4_2来提取内容组件。从每个块中,第一个卷积层(浅层)即从conv1_1到conv5_1检测多个特征,如线条或边缘。请参阅下图。
def get_features(image, model, layers=None):
if layers is None:
layers = {'0': 'conv1_1',
'5': 'conv2_1',
'10': 'conv3_1',
'19': 'conv4_1',
'30': 'conv5_2', #content
'28': 'conv5_1'}
features = {}
x = image
for name, layer in model._modules.items():
x = layer(x)
if name in layers:
features[layers[name]] = x
return features
损失函数
计算每一层的 gram 矩阵。您将需要目标图像中的房屋和湖泊。首先克隆内容图像,然后迭代更改其样式。
def gram_matrix(tensor):
_, d, h, w = tensor.size()
tensor = tensor.view(d, h * w)
gram = torch.mm(tensor, tensor.t())
return gram
content_features = get_features(content, vgg)
style_features = get_features(style, vgg)
style_grams = {layer: gram_matrix(style_features[layer]) for layer in style_features}
target = content.clone().requires_grad_(True).to(device)
分配权重
每个样式层都分配有权重。对较早的层赋予较高的权重,以获得更大的样式工件。
style_weights = {'conv1_1': 1.5,
'conv2_1': 0.80,
'conv3_1': 0.25,
'conv4_1': 0.25,
'conv5_1': 0.25}
content_weight = 1e-2
style_weight = 1e9
运行模型
这些权重用于优化器(Adam)以减少模型的损失。定义更新图像的步骤。将所有内容放在一起:从 VGG-Net 调用特征并计算内容损失。获取风格表示以计算风格损失。它会在将图层添加到其他图层之前对其进行适当的加权。最后,计算总损失!
在梯度下降中,NN 权重的下降会进行调整,但在 NST 中,权重保持不变。相反,图像像素会进行调整。与距离度量有关的梯度将反向传播到输入,从而转换输入。
show = 400
optimizer = optim.Adam([target], lr=0.01)
steps = 7000
for ii in range(1, steps+1):
target_features = get_features(target, vgg)
content_loss = torch.mean((target_features['conv4_2'] - content_features['conv4_2'])**2)
style_loss = 0
for layer in style_weights:
target_feature = target_features[layer]
target_gram = gram_matrix(target_feature)
_, d, h, w = target_feature.shape
style_gram = style_grams[layer]
layer_style_loss = style_weights[layer] * torch.mean((target_gram - style_gram)**2)
style_loss += layer_style_loss / (d * h * w)
total_loss = content_weight * content_loss + style_weight * style_loss
optimizer.zero_grad()
total_loss.backward()
optimizer.step()
if i % show == 0:
print('Total loss: ', total_loss.item())
plt.imshow(im_convert(target))
plt.show()
结论
干得好!每次迭代后损失都在减少。通过调整权重和学习率来完善你的成果。你可以使用 LBFGS 优化器。
本指南为您提供了如何使用 PyTorch 编写 NST 的一般概念。我建议使用 TensorFlow 来学习 NST,以便更好地理解所涉及的术语(损失、VGG-Net、成本函数等)。
希望你今天学到了新东西。如果你在机器学习实现方面需要任何帮助,请随时联系我。
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~