快速列车式换乘网络
这个例子展示了如何训练网络将一张图像的样式传输到另一张图像。它基于[1]中定义的体系结构。
此示例类似于使用深度学习的神经风格迁移但是,一旦你训练网络使用样式图像s,它的工作速度就会更快。这是因为,要获得样式图像Y,你只需要将输入图像X转发给网络。
下面是训练算法的高级图表。它使用三个图像来计算损失:输入图像X,转换图像Y和样式图像S。
注意,损失函数使用预先训练的网络VGG-16从图像中提取特征。可以找到它的实现和数学定义风格传递损失部分。
负荷训练数据
下载并提取COCO 2014列车图片和说明文字https://cocodataset.org/#download点击“2014列车图片”。将数据保存到指定的文件夹中imageFolder
.将图像提取到imageFolder
.2014年的COCO被可可财团.
创建目录来存储COCO数据集。
imageFolder = fullfile (tempdir,“可可”);如果~存在(imageFolder“dir”mkdir (imageFolder);结束
创建一个包含COCO映像的映像数据存储。
imd = imageDatastore (imageFolder,“IncludeSubfolders”,真正的);
训练需要很长时间。如果您希望以牺牲结果网络的准确性为代价来减少训练时间,那么可以通过设置来选择图像数据存储的一个子集分数
到较小的值。
分数= 1;numObservations =元素个数(imds.Files);imd =子集(imd, 1:地板(numObservations *分数);
要调整图像的大小并将它们全部转换为RGB,请创建一个增强图像数据存储。
augimds = augmentedImageDatastore([256 256],imds,“ColorPreprocessing”,“gray2rgb”);
阅读样式图像。
styleImage = imread (“starryNight.jpg”);styleImage = imresize(styleImage,[256 256]);
显示所选样式图像。
图imshow (styleImage)标题(“风格形象”)
定义图像转换网络
定义图像转换网络。这是一个图像对图像的网络。该网络由3部分组成:
网络的第一部分将一个大小为[256x256x3]的RGB图像作为输入,并将其下采样为一个大小为[64x64x128]的特征映射。
网络的第二部分由支持函数中定义的五个相同的剩余块组成
residualBlock。
网络的第三部分(也是最后一部分)将特征映射上采样到图像的原始大小,并返回转换后的图像。最后一部分使用
upsampleLayer
,这是一个附加到这个示例的自定义层,作为支持文件。
层= [%第一部分。imageInputLayer([256 256 3],标准化=“没有”32岁的)convolution2dLayer(9[9]填充=“相同”) groupNormalizationLayer (“channel-wise”reluLayer convolution2dLayer([3 3],64,Stride=2,Padding= .“相同”) groupNormalizationLayer (“channel-wise”) reluLayer convolution2dLayer([3 3],128,Stride=2,Padding= .“相同”) groupNormalizationLayer (“channel-wise”) reluLayer (Name =“relu_3”)%第二部分。residualBlock (“1”) residualBlock (“2”) residualBlock (“3”) residualBlock (“4”) residualBlock (“5”)%第三部分。64年upsampleLayer convolution2dLayer(3[3],填充=“相同”) groupNormalizationLayer (“channel-wise”) reluLayer upsampleLayer convolution2dLayer([3 3],32,Padding=“相同”) groupNormalizationLayer (“channel-wise”3) reluLayer convolution2dLayer(9日,填充=“相同”));lgraph = layerGraph(层);
在剩余块中添加丢失的连接。
lgraph = connectLayers (lgraph,“relu_3”,“add_1 / in2”);lgraph = connectLayers (lgraph,“add_1”,“add_2 / in2”);lgraph = connectLayers (lgraph,“add_2”,“add_3 / in2”);lgraph = connectLayers (lgraph,“add_3”,“add_4 / in2”);lgraph = connectLayers (lgraph,“add_4”,“add_5 / in2”);
在图中可视化图像转换网络。
图绘制(lgraph)标题(“变换网络”)
创建一个dlnetwork
对象从图层图。
netTransform = dlnetwork (lgraph);
风格失去网络
本例使用预先训练的VGG-16深度神经网络来提取不同层的内容和风格图像的特征。这些多层特性用于计算各自的内容和样式损失。
要获得预先训练的VGG-16网络,请使用vgg16
函数。如果您没有安装所需的支持包,那么该软件将提供下载链接。
netLoss = vgg16;
为了提取计算损失所需的特征,只需要前24层。提取并转换为图层图。
lossLayers = netLoss.Layers(桥);lgraph = layerGraph (lossLayers);
转换为一个dlnetwork
.
netLoss = dlnetwork (lgraph);
定义模型损失函数
创建函数modelLoss
,列于损失函数模型部分的示例。该函数将丢失网络、图像转换网络、小批输入图像、包含样式图像Gram矩阵的数组、与内容丢失相关的权重和与样式丢失相关的权重作为输入。该函数返回总损耗、与内容相关的损耗和与风格相关的损耗、总损耗相对于图像转换器的可学习参数的梯度、图像转换器网络的状态和转换后的图像。
指定培训选项
像[1]一样,用小批次大小为4进行2次训练。
numEpochs = 2;miniBatchSize = 4;
将增强图像数据存储的读取大小设置为小批处理大小。
augimds。MiniBatchSize = MiniBatchSize;
指定ADAM优化的选项。指定学习速率为0.001,梯度衰减因子为0.01,梯度衰减因子平方为0.999。
learnRate = 0.001;gradientDecayFactor = 0.9;squaredGradientDecayFactor = 0.999;
指定在计算总损失时给予样式损失的权重和给予内容损失的权重。
注意,为了在内容丢失和样式丢失之间找到一个很好的平衡,您可能需要尝试不同的权重组合。
weightContent = 1的军医;weightStyle = 3 e-8;
选择训练进度的情节频率。这指定了每次绘图更新之间有多少次迭代。
plotFrequency = 10;
火车模型
为了能够计算训练过程中的损失,计算样式图像的Gram矩阵。
将样式图像转换为dlarray
.
S = dlarray(单(styleImage),“SSC”);
为了计算Gram矩阵,将风格图像输入VGG-16网络,提取四个不同层的激活。
[SActivations1, SActivations2 SActivations3 SActivations4] =前进(netLoss,年代,...输出= [“relu1_2”“relu2_2”“relu3_3”“relu4_3”]);
使用支持函数计算每组激活的Gram矩阵createGramMatrix
.
SGram {1} = createGramMatrix (SActivations1);SGram {2} = createGramMatrix (SActivations2);SGram {3} = createGramMatrix (SActivations3);SGram {4} = createGramMatrix (SActivations4);
训练地块由两个人物组成:
图示训练期间的损失情况
包含图像转换网络的输入图像和输出图像的图
初始化训练地块。您可以在支持函数中查看初始化的详细信息initializeFigures。
这个函数返回:轴ax₁
损失在哪里,坐标轴ax2
你在哪里绘制验证图像,动画线lineLossContent
哪个包含内容丢失,动画线lineLossStyle
哪个包含样式丢失和动画线lineLossTotal
其中包括全部损失。
(ax₁,ax2 lineLossContent、lineLossStyle lineLossTotal] = initializeStyleTransferPlots;
初始化ADAM优化器的平均梯度和平均平方梯度超参数。
averageGrad = [];averageSqGrad = [];
计算训练迭代的总次数。
numIterations =地板(augimds.NumObservations * numEpochs / miniBatchSize);
训练前初始化迭代次数和定时器。
迭代= 0;开始=抽搐;
火车模型。如果有GPU,请使用GPU进行训练。使用GPU需要并行计算工具箱™和支持的GPU设备。有关支持的设备的信息,请参见GPU计算的需求(并行计算工具箱).这可能需要很长时间才能运行。
循环遍历各个时代。为我= 1:numEpochs重置和洗牌数据存储。重置(augimds);augimds = shuffle (augimds);在小批量上循环。而Hasdata (augimds)迭代=迭代+ 1;读取小批数据。data =阅读(augimds);忽略最后的部分小批历。如果(数据,1)< miniBatchSize大小继续结束从数据存储中提取图像到单元格数组中。。数据图像= {:1};沿着第四维度连接图像。。X =猫(4、图像{:});X =单(X);将小批数据转换为dlarray并指定尺寸标签“SSCB”(空间、空间、通道、批处理)。X = dlarray (X,“SSCB”);如果在GPU上训练,那么将数据转换为GPU array。如果canUseGPU X = gpuArray(X);结束评估模型损失,梯度和网络状态使用。的末尾列出的modelLoss函数%的例子。(损失、lossContent lossStyle、渐变状态,Y] = dlfeval (@modelLoss,...netLoss netTransform X, SGram、weightContent weightStyle);netTransform。年代tate = state;%更新网络参数。[netTransform, averageGrad averageSqGrad] =...adamupdate (netTransform、渐变averageGrad averageSqGrad,迭代,...learnRate、gradientDecayFactor squaredGradientDecayFactor);%每个plotfrequency迭代,绘制训练进度。如果addpoints(lineLossTotal,迭代,double(loss)) addpoints(lineLossContent,迭代,double(lossContent)) addpoints(lineLossStyle,迭代,double(lossStyle))使用小批处理的第一个映像作为验证映像。十五= X (:,:,: 1);使用前面计算的转换后的验证映像。青年志愿= Y (:,:,: 1);要使用imshow函数,转换为uint8。validationImage = uint8(收集(extractdata(十五)));transformedValidationImage = uint8(收集(extractdata(青年志愿)));绘制输入图像和输出图像并增加大小imshow (imtile ({validationImage, transformedValidationImage}),父= ax2);结束%显示从开始培训到完成培训的时间。D =持续时间(0,0,toc(开始),格式=“hh: mm: ss”);completionPercentage =圆(迭代/ numIterations * 100, 2);标题(ax₁,”时代:“+我+”,迭代:“+迭代+“的”+ numIterations +”(“+ completionPercentage +“%)”+”,过去:“+ drawnow字符串(D))结束结束
使风格化图像
训练完成后,就可以对所选的任何图像使用图像转换器。
加载您想要转换的图像。
imFilename =“peppers.png”;我= imread (imFilename);
将输入图像的大小调整为图像转换器的输入尺寸。
[256256] im = imresize (im);
把它转换成dlarray。
X = dlarray(单(im),“SSCB”);
使用GPU转换为gpuArray
如果有的话。
如果canUseGPU X = gpuArray(X);结束
若要将样式应用于图像,请使用函数将其转发传递给图像转换器预测。
Y =预测(netTransform X);
将图像缩放到[0 255]的范围内。首先,使用函数双曲正切
要重新调节Y
到[-1 1]的范围。然后,移动和缩放输出到[0 255]范围内。
Y = 255 *(双曲正切(Y) + 1) / 2;
准备Y
策划。使用的函数extractdata
来提取数据dlarray。
使用gather函数将Y从GPU转移到本地工作区。
Y = uint8(收集(extractdata (Y)));
在风格化图像(右)旁边显示输入图像(左)。
图m = imtile({im,Y});imshow (m)
损失函数模型
这个函数modelLoss
以损耗网络为输入netLoss
,图像转换网络netTransform
,一个小批量的输入图像X
,一个包含样式图像的Gram矩阵的数组SGram
,与内容损失相关的权重contentWeight
以及与风格损失相关的重量styleWeight
.函数返回与内容相关的总损失lossContent
以及与风格相关的损失lossStyle
,总损耗相对于图像转换器的可学习参数的梯度梯度
,图像转换网络的状态状态
,以及变换后的图像Y
.
函数(损失、lossContent lossStyle、渐变状态,Y] =...modelLoss(netLoss,netTransform,X,SGram,contentWeight,styleWeight) [Y,state] = forward(netTransform,X);Y = 255 *(双曲正切(Y) + 1) / 2;[损失,lossContent, lossStyle] = styleTransferLoss (netLoss Y X, SGram contentWeight, styleWeight);梯度= dlgradient(损失、netTransform.Learnables);结束
风格传递损失
这个函数styleTransferLoss
以损耗网络为输入netLoss
,一个小批量的输入图像X,
一小批转换后的图像Y
,一个包含样式图像的Gram矩阵的数组SGram
,与内容和样式相关的权重contentWeight
而且styleWeight,
分别。它返回全部损失损失
单独的组成部分:内容丢失lossContent
风格的丧失lossStyle。
内容损失是衡量输入图像之间在空间结构上有多大差异的一种方法X
输出图像Y
.
另一方面,风格损失告诉你风格形象之间在风格外观上有多大的差异年代
输出图像Y
.
下面的图表解释了算法styleTransferLoss
实现了总损失的计算。
首先,函数传递输入图像X
,变换后的图像Y
和风格图像年代
到经过预先训练的VGG-16网络。这个预先训练过的网络从这些图像中提取出一些特征。然后利用输入图像X和输出图像Y的空间特征计算内容损失,利用输出图像Y和风格图像s的风格特征计算风格损失,最后将内容和风格损失相加得到总损失。
内容丢失
对于小批处理中的每个图像,内容丢失函数比较原始图像和由层输出的转换图像的特征relu3_3
.特别是,它计算激活之间的均方误差,并返回迷你批处理的平均损失:
在哪里
包含输入图像,
包含转换后的图像,
是小批量的尺寸,和
表示在层提取的激活relu3_3。
风格的损失
为了计算样式损失,对于小批处理中的每一张图像:
提取层上的激活
relu1_2
,relu2_2
,relu3_3
而且relu4_3
.对于四种激活的每一种 计算克拉姆矩阵 .
计算对应的克矩阵之间的平方差。
将每个层的四个输出相加 从上一步开始。
为了得到整个小批的样式损失,计算每个图像的样式损失的平均值 mini-batch:
在哪里 是层的索引,和 为克矩阵。
全部损失
函数[损失,lossContent, lossStyle] = styleTransferLoss (netLoss, Y, X,...SGram、weightContent weightStyle)%提取激活。YActivations =细胞(1、4);YActivations [YActivations {1}, {2}, YActivations {3}, YActivations {4}] =...转发(netLoss Y“输出”,[“relu1_2”“relu2_2”“relu3_3”“relu4_3”]);XActivations =前进(netLoss X,“输出”,“relu3_3”);计算激活之间的均方误差。lossContent = mean((YActivations{3} - XActivations).^2,“所有”);把所有四次激活的损失相加。。lossStyle = 0;为G = createGramMatrix(YActivations{j});lossStyle = lossStyle + sum((G - SGram{j}).^2,“所有”);结束在小批量中平均损失。miniBatchSize =大小(X, 4);lossStyle = lossStyle / miniBatchSize;%应用权重。lossContent = weightContent * lossContent;lossStyle = weightStyle * lossStyle;计算总损失。loss = lossContent + lossStyle;结束
剩余块
的residualBlock
函数返回一个六层数组。它由卷积层、实例归一化层、ReLu层和添加层组成。请注意,groupNormalizationLayer(“channel-wise”)
只是一个实例规范化层。
函数layers = residualBlock(name) layers = [convolution2dLayer([3 3], 128,Padding= .“相同”、名称=“convRes_”+名称+“_1”) groupNormalizationLayer (“channel-wise”、名称=“normRes_”+名称+“_1”) reluLayer (Name =“reluRes_”+名称+“_1”3) convolution2dLayer([3], 128年,填充=“相同”、名称=“convRes_”+名称+“_2”) groupNormalizationLayer (“channel-wise”、名称=“normRes_”+名称+“_2”) additionLayer (Name =“加”+名字)];结束
格拉姆矩阵
这个函数createGramMatrix
接受单个层的激活作为输入,并返回小批处理中每个图像的样式表示.
输入是一个大小为[H, W, C, N]的特征图,其中H为高度,W为宽度,C为通道数,N为小批量大小。函数输出一个数组G
的大小(C, C, N)。每个子数组G (:,:, k)
是否对应于
小批处理中的映像。每个条目
表示通道之间的相关性
而且
,因为通道中的每个入口
将通道中相应位置的项相乘
:
在哪里 激活了吗 小批处理中的映像。
Gram矩阵包含哪些特征一起被激活的信息,但没有关于特征在图像中出现的位置的信息。这是因为高度和宽度的总和丢失了关于空间结构的信息。损失函数使用这个矩阵作为图像的风格表示。
函数G = createGramMatrix(activation) [h,w,numChannels] = size(activation,1:3);特点=重塑(激活,h * w, numChannels, []);featuresT = permute(features,[2 1 3]);G = dlmtimes(featuresT,features) / (h*w*numChannels);结束
参考文献
约翰逊、贾斯汀、亚历山大·阿拉希和李飞飞。“实时风格转换和超分辨率的感知损失。”欧洲计算机视觉会议.施普林格可汗,2016。
另请参阅
dlnetwork
|向前
|预测
|dlarray
|dlgradient
|dlfeval
|adamupdate