使用 PyTorch 进行艺术神经风格迁移

发布日期:2026-07-09 04:53:03   来源 : 杭州电子商务研究院    浏览量 :2
杭州电子商务研究院 发布日期:2026-07-09 04:53:03  
2

概述

在本指南中,您将在 PyTorch 中实现艺术风格转换 (NST) 神经网络算法。此算法将允许您获得毕加索风格的图像。它通过创建一个新图像来实现这一点,该新图像将一个图像的风格(绘画)与另一个图像的内容(输入图像)混合在一起。

感谢 Leon A. Gatys 等人在其论文《艺术风格的神经算法》中做出的贡献。

为了使模型更快、更准确,使用了预先训练的VGG-Net-19(Visual Geometry Group)。该模型在 ImageNet 图像上进行训练,可以从 Pytorch API 下载。

之前基于 TensorFlow 框架的指南已经介绍了艺术神经传输背后的理论。强烈建议您先阅读这些指南。第 1 部分讨论了理论方面和 VGG-Net,第 2 部分讨论了创建 AI 数字艺术所涉及的损失。

本指南将是 PyTorch 实现的代码演练。以下是该过程的概述。

导入库

要使用 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_1conv5_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、成本函数等)。

希望你今天学到了新东西。如果你在机器学习实现方面需要任何帮助,请随时联系

以上内容来自杭州电子商务研究院推送
关注
关于我们
热门推荐
合作伙伴
免责声明:本站部分资讯来源于网络,如有侵权请及时联系客服,我们将尽快处理
Copyright © 2025-2027 ToB产业网址导航 公安备案 浙公网安备33010602013138号 浙ICP备16025413号-9
支持 反馈 关注 数据