MATLAB实现自编码器(四)——变分自编码器实现图像生成Train Variational Autoencoder (VAE) to Generate Images

本文是对Train Variational Autoencoder (VAE) to Generate Images网页的翻译,该网页实现了变分自编码的图像生成,以MNIST手写数字为训练数据,生成了相似的图像。本文主要翻译了网页中除了帮助函数外的部分,由于帮助函数较多,另外整理一篇文章介绍。

This example shows how to create a variational autoencoder (VAE) in MATLAB to generate digit images. The VAE generates hand-drawn digits in the style of the MNIST data set.

本示例说明了如何在MATLAB中创建变分自动编码器(VAE)以生成数字图像。 VAE生成MNIST数据集样式的手绘数字。

VAEs differ from regular autoencoders in that they do not use the encoding-decoding process to reconstruct an input. Instead, they impose a probability distribution on the latent space, and learn the distribution so that the distribution of outputs from the decoder matches that of the observed data. Then, they sample from this distribution to generate new data.

VAE与常规自动编码器的不同之处在于,它们不使用编码解码过程来重建输入。取而代之的是,它在潜在空间上施加概率分布,并学习该分布,以使来自解码器的输出的分布与观察到的数据的分布相匹配。 然后,他们从该分布中采样以生成新数据。

In this example, you construct a VAE network, train it on the MNIST data set, and generate new images that closely resemble those in the data set.

在此示例中,你将构建一个VAE网络,在MNIST数据集上对其进行训练,然后生成与数据集中的图像非常相似的新图像。

1.Load Data

http://yann.lecun.com/exdb/mnist/下载MNIST文件,然后解压缩,提取出文件并将其放置在工作目录中,然后调用此示例附带的processImagesMNIST和processLabelsMNIST帮助器函数,以将文件中的数据加载到MATLAB数组中。
在这里插入图片描述
由于VAE会将重建的数字与输入进行比较,而不是与类别标签进行比较,因此无需在MNIST数据集中使用训练标签。

%% Load Data
clc;clear
trainImagesFile = 'train-images.idx3-ubyte';
testImagesFile = 't10k-images.idx3-ubyte';
testLabelsFile = 't10k-labels.idx1-ubyte';
% 读取MNIST图像数据...
XTrain = processImagesMNIST(trainImagesFile);
% 读取MNIST图像数据...
numTrainImages = size(XTrain,4);
XTest = processImagesMNIST(testImagesFile);
% 读取MNIST标签数据...
YTest = processLabelsMNIST(testLabelsFile);

2.Construct Network

自动编码器有两个部分:编码器和解码器。 编码器接受图像输入并输出压缩表示(编码),压缩表示是大小latent_dim的矢量,在此示例中为20。 解码器获取压缩的表示,对其进行解码,然后重新创建原始图像。

为了使计算在数值上更稳定,通过使网络从方差的对数中学习,可以将可能值的范围从[0,1]增大到[-inf,0]。 定义两个大小为latent_dim的向量:一个用于均值 μ μ μ,一个用于方差 l o g ( σ 2 ) log(σ^2) log(σ2)的对数。 然后使用这两个向量创建要从中采样的分布。

使用2-D卷积,然后使用一个完全连接的层,以从28×28×1 MNIST图像下采样到潜在空间中的编码。 然后,使用转置的2D卷积将1×1×20编码比例放大回28×28×1图像。

%% Construct Network
latentDim = 20;  % 定义压缩表示的大小
imageSize = [28 28 1];  % 输入图像的大小
% 编码器
encoderLG = layerGraph([
    imageInputLayer(imageSize,'Name','input_encoder','Normalization','none')
    convolution2dLayer(3, 32, 'Padding','same', 'Stride', 2, 'Name', 'conv1')
    reluLayer('Name','relu1')
    convolution2dLayer(3, 64, 'Padding','same', 'Stride', 2, 'Name', 'conv2')
    reluLayer('Name','relu2')
    fullyConnectedLayer(2 * latentDim, 'Name', 'fc_encoder')
    ]);
% 解码器
decoderLG = layerGraph([
    imageInputLayer([1 1 latentDim],'Name','i','Normalization','none')
    transposedConv2dLayer(7, 64, 'Cropping', 'same', 'Stride', 7, 'Name', 'transpose1')
    reluLayer('Name','relu1')
    transposedConv2dLayer(3, 64, 'Cropping', 'same', 'Stride', 2, 'Name', 'transpose2')
    reluLayer('Name','relu2')
    transposedConv2dLayer(3, 32, 'Cropping', 'same', 'Stride', 2, 'Name', 'transpose3')
    reluLayer('Name','relu3')
    transposedConv2dLayer(3, 1, 'Cropping', 'same', 'Name', 'transpose4')
    ]);
% 要使用自定义训练循环训练两个网络并启用自动微分,将层图转换为dlnetwork对象。
encoderNet = dlnetwork(encoderLG);
decoderNet = dlnetwork(decoderLG);

3.Define Model Gradients Function定义模型梯度函数

辅助函数modelGradients接受编码器和解码器dlnetwork对象以及输入数据X的小批量,然后返回损耗相对于网络中可学习参数的梯度。 在此示例的末尾定义了此辅助函数。

该功能分两个步骤执行此过程:采样和丢失。 采样步骤对均值和方差矢量进行采样,以创建最终编码以传递到解码器网络。但是,由于不可能通过随机采样进行反向传播,因此必须使用重新参数化技巧。该技巧将随机采样操作移至辅助变量 ε ε ε,然后将其平移均值 μ i μ_{i} μi,并按标准偏差 σ i σ_{i} σi进行缩放。基本想法是从 N ( μ i , σ i 2 ) N(μ_{i},σ_{i}^2) N(μi,σi2)​中采样与从 μ i + ε ∗ σ i μ_{i}+ε*σ_{i} μi+εσi​中采样相同,这里 ε ∼ N ( 0 , 1 ) ε∼N(0,1) εN(0,1)。下图描述了这个想法。

**图1**

损失步骤将由采样步骤生成的编码通过解码器网络,并确定损失,然后将其用于计算梯度。 VAE中的损失,也称为证据下限(ELBO)损失,定义为两个单独的损失项的总和:
E L B O   l o s s = r e c o n s t r u c t i o n   l o s s + K L   l o s s . ELBO loss=reconstruction loss+KL loss. ELBOloss=reconstructionloss+KLloss.

重建损耗通过使用均方误差(MSE)来衡量解码器输出与原始输入的接近程度:
r e c o n s t r u c t i o n   l o s s = M S E ( d e c o d e r   o u t p u t , o r i g i n a l   i m a g e ) . reconstruction loss=MSE(decoder output,original image). reconstructionloss=MSE(decoderoutput,originalimage).

KL loss或Kullback-Leibler散度测量两个概率分布之间的差异。 在这种情况下,将KL损失降至最低意味着确保学习的均值和方差尽可能接近目标(正态)分布的均值和方差。 对于大小为n的潜在维度,KL损失为
K L l o s s = − 0.5 ∗ ∑ i = 1 n ( 1 + l o g ( σ i ) − μ i 2 − σ i 2 ) KLloss = -0.5*\displaystyle\sum_{i=1}^{n} (1+log(σ_{i})-μ_{i}^2-σ_{i}^2) KLloss=0.5i=1n(1+log(σi)μi2σi2)

包含KL损失项的实际效果是将由于重建损失而学习到的簇紧密地包装在潜在空间的中心附近,形成一个连续的空间进行采样。

4.Specify Training Options指定训练选项

在GPU上训练(需要Parallel Computing Toolbox™)。 如果没有GPU,请将executionEnvironment设置为“ cpu”。

executionEnvironment = "cpu";

设置网络的培训选项。 使用Adam优化器时,需要为每个网络初始化带有空数组的尾随平均梯度和尾随平均梯度平方衰减率。

numEpochs = 50;  % 训练轮次
miniBatchSize = 512; % 小批量大小
lr = 1e-3;  % 学习率
numIterations = floor(numTrainImages/miniBatchSize);% 迭代次数?
iteration = 0;

avgGradientsEncoder = [];
avgGradientsSquaredEncoder = [];
avgGradientsDecoder = [];
avgGradientsSquaredDecoder = [];

5.Train Model模型训练

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

  • 从训练集中获得下一个小批量。
  • 将小批量转换为dlarray对象,并确保指定尺寸标签“ SSCB”(空间,空间,通道,批次)。
  • 为了进行GPU训练,请将dlarray转换为gpuArray对象。
  • 使用dlfeval和modelGradients函数评估模型梯度。
  • 使用adamupdate函数更新两个网络的可学习信息和平均梯度。
    在每个时期结束时,将测试集图像通过自动编码器,并显示该时期的损耗和训练时间。
%% Train Model 模型训练
for epoch = 1:numEpochs
    tic;
    for i = 1:numIterations
        iteration = iteration + 1;
        idx = (i-1)*miniBatchSize+1:i*miniBatchSize;
        XBatch = XTrain(:,:,:,idx);
        XBatch = dlarray(single(XBatch), 'SSCB');
        
        if (executionEnvironment == "auto" && canUseGPU) || executionEnvironment == "gpu"
            XBatch = gpuArray(XBatch);           
        end 
            
        [infGrad, genGrad] = dlfeval(...
            @modelGradients, encoderNet, decoderNet, XBatch);
        
        [decoderNet.Learnables, avgGradientsDecoder, avgGradientsSquaredDecoder] = ...
            adamupdate(decoderNet.Learnables, ...
                genGrad, avgGradientsDecoder, avgGradientsSquaredDecoder, iteration, lr);
        [encoderNet.Learnables, avgGradientsEncoder, avgGradientsSquaredEncoder] = ...
            adamupdate(encoderNet.Learnables, ...
                infGrad, avgGradientsEncoder, avgGradientsSquaredEncoder, iteration, lr);
    end
    elapsedTime = toc;
    
    [z, zMean, zLogvar] = sampling(encoderNet, XTest);
    xPred = sigmoid(forward(decoderNet, z));
    elbo = ELBOloss(XTest, xPred, zMean, zLogvar);
    disp("Epoch : "+epoch+" Test ELBO loss = "+gather(extractdata(elbo))+...
        ". Time taken for epoch = "+ elapsedTime + "s")    
end

我得电脑是cpu,所以训练时间较长。

Epoch : 1 Test ELBO loss = 27.6162. Time taken for epoch = 79.8766s
Epoch : 2 Test ELBO loss = 24.2621. Time taken for epoch = 76.5901s
Epoch : 3 Test ELBO loss = 23.2817. Time taken for epoch = 77.0088s
Epoch : 4 Test ELBO loss = 22.6606. Time taken for epoch = 77.0701s
Epoch : 5 Test ELBO loss = 21.0466. Time taken for epoch = 76.4785s
Epoch : 6 Test ELBO loss = 20.4552. Time taken for epoch = 78.5642s
Epoch : 7 Test ELBO loss = 20.1339. Time taken for epoch = 76.3746s
Epoch : 8 Test ELBO loss = 19.9099. Time taken for epoch = 80.016s
Epoch : 9 Test ELBO loss = 19.7394. Time taken for epoch = 77.5358s
Epoch : 10 Test ELBO loss = 19.7517. Time taken for epoch = 76.7551s
Epoch : 11 Test ELBO loss = 19.437. Time taken for epoch = 78.3796s
Epoch : 12 Test ELBO loss = 19.3994. Time taken for epoch = 76.4326s
Epoch : 13 Test ELBO loss = 19.4059. Time taken for epoch = 76.2415s
Epoch : 14 Test ELBO loss = 19.2777. Time taken for epoch = 76.0682s
Epoch : 15 Test ELBO loss = 19.2771. Time taken for epoch = 76.4397s
Epoch : 16 Test ELBO loss = 19.2445. Time taken for epoch = 76.4749s
Epoch : 17 Test ELBO loss = 19.2153. Time taken for epoch = 78.8465s
Epoch : 18 Test ELBO loss = 19.1247. Time taken for epoch = 77.9335s
Epoch : 19 Test ELBO loss = 19.1874. Time taken for epoch = 76.2667s
Epoch : 20 Test ELBO loss = 19.0501. Time taken for epoch = 75.4291s
Epoch : 21 Test ELBO loss = 19.0504. Time taken for epoch = 87.715s
Epoch : 22 Test ELBO loss = 19.0171. Time taken for epoch = 75.8042s
Epoch : 23 Test ELBO loss = 19.0442. Time taken for epoch = 75.9503s
Epoch : 24 Test ELBO loss = 18.9828. Time taken for epoch = 75.6918s
Epoch : 25 Test ELBO loss = 18.9477. Time taken for epoch = 75.7599s
Epoch : 26 Test ELBO loss = 18.9764. Time taken for epoch = 75.7559s
Epoch : 27 Test ELBO loss = 18.9073. Time taken for epoch = 75.4665s
Epoch : 28 Test ELBO loss = 18.968. Time taken for epoch = 75.4155s
Epoch : 29 Test ELBO loss = 18.8588. Time taken for epoch = 75.7883s
Epoch : 30 Test ELBO loss = 18.828. Time taken for epoch = 75.549s
Epoch : 31 Test ELBO loss = 18.8161. Time taken for epoch = 75.4344s
Epoch : 32 Test ELBO loss = 18.8006. Time taken for epoch = 75.9594s
Epoch : 33 Test ELBO loss = 18.7591. Time taken for epoch = 75.7674s
Epoch : 34 Test ELBO loss = 18.6874. Time taken for epoch = 75.1576s
Epoch : 35 Test ELBO loss = 18.6873. Time taken for epoch = 75.1619s
Epoch : 36 Test ELBO loss = 18.7304. Time taken for epoch = 75.1497s
Epoch : 37 Test ELBO loss = 18.6782. Time taken for epoch = 75.2087s
Epoch : 38 Test ELBO loss = 18.6843. Time taken for epoch = 75.0542s
Epoch : 39 Test ELBO loss = 18.6495. Time taken for epoch = 75.1723s
Epoch : 40 Test ELBO loss = 18.6257. Time taken for epoch = 75.5644s
Epoch : 41 Test ELBO loss = 18.6557. Time taken for epoch = 75.4477s
Epoch : 42 Test ELBO loss = 18.6684. Time taken for epoch = 75.1905s
Epoch : 43 Test ELBO loss = 18.6601. Time taken for epoch = 75.2663s
Epoch : 44 Test ELBO loss = 18.633. Time taken for epoch = 76.9297s
Epoch : 45 Test ELBO loss = 18.6248. Time taken for epoch = 75.1026s
Epoch : 46 Test ELBO loss = 18.5933. Time taken for epoch = 75.1996s
Epoch : 47 Test ELBO loss = 18.6265. Time taken for epoch = 75.391s
Epoch : 48 Test ELBO loss = 18.6191. Time taken for epoch = 75.4567s
Epoch : 49 Test ELBO loss = 18.5185. Time taken for epoch = 75.2701s
Epoch : 50 Test ELBO loss = 18.5586. Time taken for epoch = 75.155s

6.Visualize Results可视化结果

要可视化和解释结果,请使用帮助程序可视化功能。 这些帮助器功能在本示例的最后定义。

VisualizeReconstruction函数显示从每个类中随机选择的一位,并在通过自动编码器后对其进行重构。

visualizeReconstruction(XTest, YTest, encoderNet, decoderNet)

在这里插入图片描述

VisualizeLatentSpace函数采用将测试图像通过编码器网络后生成的均值和方差编码(每个维度为20),并对包含每个图像编码的矩阵执行主成分分析(PCA)。 然后,可以可视化均值定义的潜在空间和以两个第一主成分为特征的二维方差。

visualizeLatentSpace(XTest, YTest, encoderNet)

在这里插入图片描述

生成函数初始化从正态分布采样的新编码,并输出当这些编码通过解码器网络时生成的图像。

generate(decoderNet, latentDim)

在这里插入图片描述

7.下一步

可变自动编码器只是用于执行生成任务的众多可用模型之一。 它们适用于图像较小且具有明确定义的特征的数据集(例如MNIST)。 对于具有较大图像的更复杂的数据集,生成对抗网络(GAN)往往会表现更好,并生成噪声较小的图像。 有关显示如何实施GAN生成64×64 RGB图像的示例,请参阅训练生成对抗网络Train Generative Adversarial Network (GAN)

8.Helper Functions帮助函数

相关函数请看文章MATLAB实现自编码器(五)——变分自编码器(VAE)实现图像生成的帮助函数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值