主要内容

使用深度学习的神经风格迁移

这个例子展示了如何使用预先训练好的VGG-19网络将一张图像的风格外观应用到另一张图像的场景内容。

加载数据

加载样式图像和内容图像。本例使用梵高独特的画作《星夜》作为风格图像,使用灯塔的照片作为内容图像。

styleImage = im2double(imread(“starryNight.jpg”));contentImage = imread(“lighthouse.png”);

显示样式图像和内容图像作为蒙太奇。

imshow (imtile ({styleImage, contentImage},写成BackgroundColor =“w”));

负载特征提取网络

在本例中,您使用经过修改的预训练的VGG-19深度神经网络来提取不同层的内容和风格图像的特征。这些多层特征用于计算各自的内容和样式损失。该网络利用组合损耗生成程式化的传输图像。

要获得预训练的VGG-19网络,请安装vgg19.如果您没有安装所需的支持包,则该软件提供下载链接。金宝app

Net = vgg19;

为了使VGG-19网络适合于特征提取,从网络中移除所有全连接层。

lastFeatureLayerIdx = 38;layers = net.Layers;layers = layers(1:lastFeatureLayerIdx);

VGG-19网络的最大池化层会产生衰落效应。为了减小衰落效应,增加梯度流量,将所有最大池化层替换为平均池化层[1]

l = 1:lastFeatureLayerIdx layer = layers(l);如果isa(层,“nnet.cnn.layer.MaxPooling2DLayer”) layers(l) = averagePooling2dLayer(layer.PoolSize,Stride=layer.Stride,Name=layer.Name);结束结束

用修改过的图层创建一个图层图。

lgraph = layerGraph(图层);

在图中可视化特征提取网络。

情节(lgraph)标题(“特征提取网络”

要使用自定义训练循环训练网络并启用自动区分,请将层图转换为adlnetwork对象。

Dlnet = dlnetwork(lgraph);

数据进行预处理

将样式图像和内容图像调整为较小的尺寸,以便更快地处理。

imageSize = [384,512];styleImg = imresize(styleImage,imageSize);contentImg = imresize(contentImage,imageSize);

预训练的VGG-19网络对信道平均减去图像进行分类。从图像输入层(即网络中的第一层)获得通道平均值。

imgInputLayer = lgraph.Layers(1);meanVggNet = imgInputLayer.Mean(1,1,:);

通道平均值的值适用于像素值范围为[0,255]的浮点数据类型的图像。将样式图像和内容图像转换为数据类型范围为[0,255]。然后,从样式图像和内容图像中减去频道均值。

styleImg = rescale(single(styleImg),0,255) - meanVggNet;contentImg = rescale(single(contentImg),0,255) - meanVggNet;

初始化传输图像

转换图像是样式转换后的输出图像。可以使用样式图像、内容图像或任何随机图像初始化传输图像。使用样式图像或内容图像初始化会使样式传输过程产生偏差,并产生与输入图像更相似的传输图像。相比之下,带有白噪声的初始化可以消除偏差,但需要更长的时间才能收敛到程式化的图像上。为了更好地风格化和更快地收敛,本示例将输出传输图像初始化为内容图像和白噪声图像的加权组合。

noiseRatio = 0.7;randImage = randi([-20,20],[imageSize 3]);transferImage = noiseRatio。*randImage + (1-noiseRatio).*contentImg;

定义损失函数和样式转移参数

内容丢失

内容丢失的目的是使传输图像的特征与内容图像的特征相匹配。内容损失计算为每个内容特征层的内容图像特征与传输图像特征之间的均方差[1] Y ˆ 预测的特征图是否为传输图像和 Y 内容图像的预测特征映射。 W c l 内容层的权重是否为 l t h 层。 H W C 分别是特征映射的高度、宽度和通道。

l c o n t e n t l W c l × 1 H W C j Y ˆ j l - Y j l 2

指定内容特征提取层名称。从这些层中提取的特征用于计算内容损失。在VGG-19网络中,使用来自较深层的特征比使用来自浅层的特征训练更有效。因此,将内容特征提取层指定为第四卷积层。

styleTransferOptions。contentFeatureLayerNames =“conv4_2”

指定内容特征提取层的权重。

styleTransferOptions。contentFeatureLayerWeights = 1;

风格的损失

风格丢失的目的是使转移图像的纹理与风格图像的纹理相匹配。图像的样式表示用Gram矩阵表示。因此,风格损失计算为风格图像的Gram矩阵与转移图像的Gram矩阵之间的均方差[1] Z 而且 Z ˆ 分别为风格图像和转移图像的预测特征图。 G Z 而且 G Z ˆ 分别为风格特征和传递特征的Gram矩阵。 W 年代 l 是样式层权重为 l t h 层风格。

G Z ˆ j Z ˆ j × Z ˆ j

G Z j Z j × Z j

l 年代 t y l e l W 年代 l × 1 2 H W C 2 G Z ˆ l - G Z l 2

指定样式特征提取层的名称。从这些层中提取的特征用于计算样式损失。

styleTransferOptions。年代tyleFeatureLayerNames = [“conv1_1”“conv2_1”“conv3_1”“conv4_1”“conv5_1”];

指定样式特征提取层的权重。为简单样式图像指定小权重,为复杂样式图像增加权重。

styleTransferOptions。年代tyleFeatureLayerWeights = [0.5,1.0,1.5,3.0,4.0];

全部损失

总损失是内容损失和风格损失的加权组合。 α 而且 β 分别是内容损失和风格损失的权重因子。

l t o t 一个 l α × l c o n t e n t + β × l 年代 t y l e

指定权重因素α而且β内容丢失和风格丢失。的比例αβ应该是1e-3或1e-4左右[1]

styleTransferOptions。Alpha = 1;styleTransferOptions。Beta = 1e3;

指定培训项目

训练2500次迭代。

numIterations = 2500;

为Adam优化指定选项。设置学习率为2,可以加快收敛速度。您可以通过观察输出图像和损失来实验学习率。初始化尾随平均梯度和尾随平均梯度平方衰减率[]

learningRate = 2;trailingAvg = [];trailingAvgSq = [];

培训网络

转换样式图像、内容图像,并将图像转换为dlarray具有基础类型的对象和尺寸标签"SSC”

dlStyle = darray (styleImg,“SSC”);dlContent = darray (contentImg,“SSC”);dlTransfer = dlarray(transferImage,“SSC”);

如果有GPU,可以在GPU上进行训练。使用GPU需要并行计算工具箱™和支持CUDA®的NVIDIA®GPU。有关更多信息,请参见GPU支金宝app持版本(并行计算工具箱).对于GPU训练,将数据转换为gpuArray

如果canUseGPU dlContent = gpuArray(dlContent);dlStyle = gpuArray(dlStyle);dlTransfer = gpuArray(dlTransfer);结束

从内容图像中提取内容特征。

numContentFeatureLayers = numel(styleTransferOptions.contentFeatureLayerNames);contentFeatures = cell(1,numContentFeatureLayers);[contentFeatures{:}] = forward(dlnet,dlContent,Outputs=styleTransferOptions.contentFeatureLayerNames);

从样式图像中提取样式特征。

numStyleFeatureLayers = numel(styleTransferOptions.styleFeatureLayerNames);styleFeatures = cell(1,numStyleFeatureLayers);[styleFeatures{:}] = forward(dlnet,dlStyle,Outputs=styleTransferOptions.styleFeatureLayerNames);

使用自定义训练循环训练模型。对于每个迭代:

  • 利用内容图像、样式图像和传输图像的特征计算内容损失和样式损失。要计算损失和梯度,请使用helper函数imageGradients(定义于金宝app支持功能部分)。

  • 方法更新传输映像adamupdate函数。

  • 选择最佳风格转换图像作为最终输出图像。

图minimumLoss = inf;iteration = 1:numIterations使用dlfeval和示例末尾列出的% imageGradients函数[grad,losses] = dlfeval(@imageGradients,dlnet,dlTransfer,contentFeatures,styleFeatures,styleTransferOptions);[dlTransfer,trailingAvg,trailingAvgSq] = adamupdate(dlTransfer,grad,trailingAvg,trailingAvgSq,iteration,learningRate);如果的损失。来t一个llo年代年代< minimumLoss minimumLoss = losses.totalLoss; dlOutput = dlTransfer;结束在第一次迭代和每50次之后显示传输图像%的迭代。后处理步骤在“后处理”中描述%传输图像用于显示”部分如果mod(迭代,50)== 0 ||(迭代== 1)transferImage = gather(extractdata(dlTransfer));transferImage = transferImage + meanVggNet;transferImage = uint8(transferImage);transferImage = imresize(transferImage,size(contentImage,[1 2]));图像(transferImage)标题([“迭代后转移图像”num2str(迭代)])轴图像drawnow结束结束

用于显示的后处理传输图像

获取更新后的传输图像。

transferImage = gather(extractdata(dlOutput));

将经过网络训练的均值添加到传输图像中。

transferImage = transferImage + meanVggNet;

某些像素值可能超出内容和样式图像的原始范围[0,255]。通过将数据类型转换为,可以将值剪辑到范围[0,255]uint8

transferImage = uint8(transferImage);

将传输图像调整为内容图像的原始大小。

transferImage = imresize(transferImage,size(contentImage,[1 2]));

在蒙太奇中显示内容图像、传输图像和样式图像。

imshow (imtile ({contentImage、transferImage styleImage},...GridSize = 3[1],写成BackgroundColor =“w”));

金宝app支持功能

计算图像损失和梯度

imageGradientsHelper函数使用内容图像、样式图像和传输图像的特征返回损失和梯度。

函数[gradients,losses] = imageGradients(dlnet,dlTransfer,contentFeatures,styleFeatures,params)初始化传输映像特性容器numContentFeatureLayers = numel(params.contentFeatureLayerNames);numStyleFeatureLayers = numel(params.styleFeatureLayerNames);transferContentFeatures = cell(1,numContentFeatureLayers);transferStyleFeatures = cell(1, numstylefeaturelays);提取传输图像的内容特征[transferContentFeatures{:}] = forward(dlnet,dlTransfer,Outputs=params.contentFeatureLayerNames);提取转移图像的风格特征[transferStyleFeatures{:}] = forward(dlnet,dlTransfer,Outputs=params.styleFeatureLayerNames);计算内容损失cLoss = contentLoss(transferContentFeatures,contentFeatures,params.contentFeatureLayerWeights);计算款式损失sLoss = styleLoss(transferStyleFeatures,styleFeatures,params.styleFeatureLayerWeights);以内容损失和样式损失的加权组合计算最终损失损失=(参数。α* cLoss) + (params.beta * sLoss);计算关于转移图像的梯度gradients = dlgradient(loss,dlTransfer);提取各种损失的损失。来t一个llo年代年代=gather(extractdata(loss)); losses.contentLoss = gather(extractdata(cLoss)); losses.styleLoss = gather(extractdata(sLoss));结束

计算内容损失

contentLossHelper函数计算内容图像特征和传输图像特征之间的加权均方差。

函数(transferContentFeatures,contentFeatures,contentWeights)* mean((transferContentFeatures{1,i} - contentFeatures{1,i}).^2,“所有”);loss = loss + (contentWeights(i)*temp);结束结束

计算风格损失

styleLosshelper函数计算style图像特征的Gram矩阵与transfer图像特征的Gram矩阵之间的加权均方差。

函数损失= styloss (transferStyleFeatures,styleFeatures,styleWeights)损失= 0;i=1:numel(styleFeatures) tsf = transferStyleFeatures{1,i};sf = styleFeatures{1,i};[h,w,c] = size(sf);gramStyle = calculateGramMatrix(sf);gramTransfer = calculateGramMatrix(tsf);sLoss = mean((gramTransfer - gramStyle).^2,“所有”) / ((h*w*c)^2);loss = loss + (styleWeights(i)*sLoss);结束结束

计算Gram矩阵

calculateGramMatrix方法使用Helper函数styleLosshelper函数来计算特征图的Gram矩阵。

函数gramMatrix = calculateGramMatrix(featureMap) [H,W,C] = size(featureMap);重塑(feature remap,H*W,C);gramMatrix = reshapedFeatures' * reshapedFeatures;结束

参考文献

Leon A. Gatys, Alexander S. Ecker, Matthias Bethge。“艺术风格的神经算法。”预印本,2015年9月2日提交。https://arxiv.org/abs/1508.06576

另请参阅

|||

相关的话题