【深度之眼cs231n第七期】笔记(二十七)

StyleTransfer-Pytorch.ipynb

这里用的是cs231n2020的代码,因为有些pytorch的用法和以前的不一样了。
注意:StyleTransfer-Pytorch.ipynb和style_transfer_pytorch.py都有设置运算类型(CPU或GPU)的地方,需要两处同时更改。

预处理

接下来会实现"Image Style Transfer Using Convolutional Neural Networks" (Gatys et al., CVPR 2015)里的风格迁移。
要实现风格迁移,首先在深度网络的特征空间中建立一个与每张图像的内容和风格相匹配的损失函数,然后对图像本身的像素进行梯度下降

提取特征所用的神经网络是 SqueezeNet(一个在ImageNet上预训练好的轻量级网络),实际上可以使用任一神经网络进行特征提取,但是SqueezeNet相对较小并且比较有效。

下图是风格迁移的结果:
在这里插入图片描述

import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as T
import PIL

import numpy as np
import matplotlib.pyplot as plt
from cs231n.image_utils import SQUEEZENET_MEAN, SQUEEZENET_STD
%matplotlib inline

# 自动加载其他文件里的模型、函数
%load_ext autoreload
%autoreload 2

加载’style-transfer-checks.npz’

from cs231n.style_transfer_pytorch import preprocess, deprocess, rescale, rel_error, features_from_img
# 如果'style-transfer-checks.npz'在同级目录下
CHECKS_PATH = 'style-transfer-checks.npz'
# 否则
#CHECKS_PATH = '/content/drive/My Drive/{}/{}'.format(FOLDERNAME, 'style-transfer-checks.npz')

assert CHECKS_PATH is not None, "[!] Choose path to style-transfer-checks.npz"
# 存放图片的位置
STYLES_FOLDER = CHECKS_PATH.replace('style-transfer-checks.npz', 'styles')
# 加载'style-transfer-checks.npz'
answers = dict(np.load(CHECKS_PATH))
# 如果只有cpu就用这行代码
#dtype = torch.FloatTensor
dtype = torch.cuda.FloatTensor

# 加载预训练的SqueezeNet模型
cnn = torchvision.models.squeezenet1_1(pretrained=True).features
cnn.type(dtype)
# 不需要训练SqueezeNet模型,所以不需要计算SqueezeNet参数的梯度
for param in cnn.parameters():
    param.requires_grad = False

计算损失

风格迁移会生成一张图片,这张图片保持一张输入图片的内容,却和另一张输入图片的风格相似。所以风格迁移的损失函数是由内容损失风格损失正则化损失构成的,是三者的加权求和。

内容损失

内容损失衡量了生成图像的特征和输入图片特征的差异(神经网络在进行分类时,考虑了图像的内容,所以神经网络的特征可以在一定程度上表现两张图片的内容是否相似)。

我们只关心网络中特定层(比如说,第ℓ层)的特征(𝐴∈ℝ1×𝐶ℓ×𝐻ℓ×𝑊ℓ),其中𝐶是第ℓ层的通道数,𝐻和𝑊分别是高和宽。

通过𝑀=𝐻×𝑊变换特征图的维度, 𝐹∈ℝ𝐶ℓ×𝑀ℓ是生成图片的特征,𝑃∈ℝ𝐶ℓ×𝑀ℓ是输入(内容)图片的特征,𝑤𝑐是内容损失的权重。
内容损失的公式如下:
在这里插入图片描述
cs231n/style_transfer_pytorch.py中实现content_loss()后运行下面的代码,应该得到小于0.001的相对误差。

from cs231n.style_transfer_pytorch import content_loss, extract_features, features_from_img
def content_loss_test(correct):
    content_image = '%s/tubingen.jpg' % (STYLES_FOLDER)
    # 用于调整图片的大小,维持比例的同时,讲短边缩小为image_size个像素
    image_size =  192
    # 只关心第3层的特征
    content_layer = 3
    content_weight = 6e-2    
    
    # 获取SqueezeNet每一层的特征,content_img_var是预处理过的特征,用于生成bad_img
    c_feats, content_img_var = features_from_img(content_image, image_size, cnn)    
    bad_img = torch.zeros(*content_img_var.data.size()).type(dtype)
    # 提取另一张图片的特征
    feats = extract_features(bad_img, cnn)
    # 计算两个(SqueezeNet第3层)特征的内容损失
    student_output = content_loss(content_weight, c_feats[content_layer], feats[content_layer]).cpu().data.numpy()
    error = rel_error(correct, student_output)
    print('Maximum error is {}'.format(error))

content_loss_test(answers['cl_out'])
# Maximum error is 0.00017621209553908557

风格损失

我们希望生成图片与输入(风格)图片的特征统计数据相近,可以用协方差矩阵相似来表现这种相近。但是协方差矩阵计算比较麻烦,所以使用格拉姆矩阵来代替协方差矩阵(它比较容易计算,并且在实践中有比较好的结果)。

对于特定的层ℓ,计算每个通道特征之间的格拉姆矩阵𝐺。假设神经网络第ℓ层的特征为𝐹∈ℝ𝐶ℓ×𝑀ℓ,有𝐶个通道,那么格拉姆矩阵的大小为(𝐶,𝐶)。
格拉姆矩阵的计算公式如下:
在这里插入图片描述
假设𝐺是生成图片的格拉姆矩阵,𝐴是输入(风格)图片的格拉姆矩阵,𝑤是第ℓ层风格损失的权重,那么第ℓ层的风格损失为:
在这里插入图片描述
在实际应用中,一般计算多层风格损失,而不是一层。所以,最终的风格损失要进行多层求和:
在这里插入图片描述
测试格拉姆矩阵是否正确

from cs231n.style_transfer_pytorch import gram_matrix
def gram_matrix_test(correct):
    style_image = '%s/starry_night.jpg' % (STYLES_FOLDER)
    style_size = 192
    feats, _ = features_from_img(style_image, style_size, cnn)
    # 计算神经网络第五层的格拉姆矩阵
    student_output = gram_matrix(feats[5].clone()).cpu().data.numpy()
    error = rel_error(correct, student_output)
    print('Maximum error is {}'.format(error))    

gram_matrix_test(answers['gm_out'])
# Maximum error is 0.0004066229157615453

测试风格损失

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值