0.前言
在光谱学或信号处理领域,获取大量高质量的数据可能是一项挑战。利用DCGAN进行“迁移学习”,对抗性地生成光谱或信号数据,具有强化、泛化样本特征的应用潜力。
该实战项目提供了所有源代码与测试数据,旨在帮助学者快速地掌握了解利用DCGAN对1维数据的生成。
建议开始本项目前提前了解和掌握以下内容:MATLAB代码解析:利用DCGAN实现图像数据的生成 全网最细&DCGAN设计-训练入门
项目参考文献:
光谱技术结合水分校正与样本增广的棉田土壤盐分精准反演 - 中国知网
1.训练效果、脚本与文件
1.1训练效果
训练8个周期
训练36个周期
训练115个周期
充分训练后
1.2脚本代码
1.2.1主程序
数据获取
clear all
clc
load("TestData.mat");
%返回相同的数据类型,以行为分配样本
%'IterationDimension'=1以第一个维度划分样本(行),2为第二个维度,以此类推;
ADataStore = arrayDatastore(DataMat,'IterationDimension',1,'OutputType', 'cell');
%%构建生成器
numLatentInputs=200;%输入随机数大小
netG=Creat_1D_Gener(numLatentInputs);
%%判别器
netD=Creat_1D_Discri();
%%指定训练选项
dropoutProb = 0.25;%泄露率
scale = 0.2;
numEpochs = 2000;%训练周期
miniBatchSize = 256;%最小批次
learnRate = 0.00002;
learnRateD=0.00002;
gradientDecayFactor = 0.45;
squaredGradientDecayFactor = 0.999;
flipProb = 0.15;%翻转概率
validationFrequency = 100;%验证频率
%%训练模型
mbq = minibatchqueue(ADataStore, ...
MiniBatchSize=miniBatchSize, ...
MiniBatchFcn=@preprocessMiniBatch12_2D, ...%预处理方法,与采集的数据类型对应
PartialMiniBatch="discard", ...
MiniBatchFormat="SCB");
trailingAvgG = [];
trailingAvgSqG = [];
trailingAvg = [];
trailingAvgSqD = [];
numValidationImages = 4;
ZValidation = randn(numLatentInputs,numValidationImages,"single");
ZValidation = dlarray(ZValidation,"CB");
if canUseGPU
ZValidation = gpuArray(ZValidation);
end
f = figure;
f.Position(3) = 2*f.Position(3);
imageAxes = subplot(1,2,1);
scoreAxes = subplot(1,2,2);
C = colororder;
lineScoreG = animatedline(scoreAxes,Color=C(1,:));
lineScoreD = animatedline(scoreAxes,Color=C(2,:));
legend("Generator","Discriminator");
ylim([0 1])
xlabel("Iteration")
ylabel("Score")
grid on
iteration = 0;
start = tic;
% Loop over epochs.
for epoch = 1:numEpochs
% Reset and shuffle datastore.
shuffle(mbq);
% Loop over mini-batches.
while hasdata(mbq)
iteration = iteration + 1;
% Read mini-batch of data.
X = next(mbq);
% Generate latent inputs for the generator network. Convert to
% dlarray and specify the format "CB" (channel, batch). If a GPU is
% available, then convert latent inputs to gpuArray.
Z = randn(numLatentInputs,miniBatchSize,"single");
Z = dlarray(Z,"CB");
if canUseGPU
Z = gpuArray(Z);
end
% Evaluate the gradients of the loss with respect to the learnable
% parameters, the generator state, and the network scores using
% dlfeval and the modelLoss function.
[~,~,gradientsG,gradientsD,stateG,scoreG,scoreD] = ...
dlfeval(@modelLoss,netG,netD,X,Z,flipProb);
netG.State = stateG;
%%show data
%"epoch"
%epoch
%"scoreG-D"
%[scoreG,scoreD]
% Update the discriminator network parameters.
[netD,trailingAvg,trailingAvgSqD] = adamupdate(netD, gradientsD, ...
trailingAvg, trailingAvgSqD, iteration, ...
learnRateD, gradientDecayFactor, squaredGradientDecayFactor);
% Update the generator network parameters.
[netG,trailingAvgG,trailingAvgSqG] = adamupdate(netG, gradientsG, ...
trailingAvgG, trailingAvgSqG, iteration, ...
learnRate, gradientDecayFactor, squaredGradientDecayFactor);
% Every validationFrequency iterations, display batch of generated
% images using the held-out generator input.
if mod(iteration,validationFrequency) == 0 || iteration == 1
% Generate images using the held-out generator input.
XGeneratedValidation = predict(netG,ZValidation);
% Tile and rescale the images in the range [0 1].
I = imtile(extractdata(XGeneratedValidation));
I = rescale(I);
% Display the images.
subplot(1,2,1);
plot(I,'DisplayName','I')
% xticklabels([0 900]);
% yticklabels([-1 1]);
title("Generated Curve");
% Update the scores plot.
subplot(1,2,2)
scoreGV = double(extractdata(scoreG));
addpoints(lineScoreG,iteration,scoreGV);
scoreDV = double(extractdata(scoreD));
addpoints(lineScoreD,iteration,scoreDV);
% Update the title with training progress information.
D = duration(0,0,toc(start),Format="hh:mm:ss");
title(...
"Epoch: " + epoch + ", " + ...
"Iteration: " + iteration + ", " + ...
"Elapsed: " + string(D))
drawnow
%周期性保存数据
if mod(iteration,validationFrequency*100)==0
%save GANmodel_test.mat netG netD trailingAvg trailingAvgSqD iteration
end
end
end
end
%%生成新图像
numLatentInputs = 50;
numObservations = 1;
ZNew = randn(numLatentInputs,numObservations,"single");
ZNew = dlarray(ZNew,"CB");
if canUseGPU
ZNew = gpuArray(ZNew);
end
XGeneratedNew = predict(netG,ZNew);
I = imtile(extractdata(XGeneratedNew));
I=rescale(I)
figure
plot(I,'DisplayName','I')
axis off
title("Generated Images")
该代码内容与之前图像生成的思路[1]基本一致,主要变动在于生成器、判别器、数据库构建和数据预处的区别。
[1] https://blog.csdn.net/m0_47787372/article/details/141791275?spm=1001.2014.3001.5501
首先是数据格式的变动,由于1D数据较小,这里直接用内存载入训练。
该程序提供的示例数据为2维的doule类型(每行为1个样本),数据量3658、单样本长度128。这里利用"arrayDatastore"函数直接从内存读取该数据作为深度学习的训练样本,'IterationDimension'为样本分割的维数,这里按行分割样本,固其值设置为1;OutputType设置成same或cell都可以。
ADataStore = arrayDatastore(DataMat,'IterationDimension',1,'OutputType', 'cell');
如果这里不使用arrayDatastore类函数,将无法使用minibatchqueue函数将数据拆分成多个训练批次。后者仅支持datastore数据类型,当然你使用imaginedatastore等其他函数也是可以的(把1维数据变成图像再训练),但这本质上属于还是未对matlab数据类型的实现精准掌握。
1.2.2 生成器
function net=Creat_2D_Gener(numLatentInputs)
net = dlnetwork;
Add branches to the dlnetwork. Each branch is a linear array of layers.
tempNet = [
featureInputLayer(numLatentInputs,"Name","input")
projectAndReshapeLayer([8 2048])
transposedConv1dLayer(3,1024,"Name","transposed-conv1d","Cropping","same","Stride",2)
instanceNormalizationLayer("Name","instancenorm")
reluLayer("Name","relu")
transposedConv1dLayer(3,512,"Name","transposed-conv1d_1","Cropping","same","Stride",2)
instanceNormalizationLayer("Name","instancenorm_1")
reluLayer("Name","relu_1")
transposedConv1dLayer(3,256,"Name","transposed-conv1d_2","Cropping","same")
instanceNormalizationLayer("Name","instancenorm_2")
reluLayer("Name","relu_3")
transposedConv1dLayer(3,128,"Name","transposed-conv1d_3","Cropping","same","Stride",2)
instanceNormalizationLayer("Name","instancenorm_3")
reluLayer("Name","relu_2")
transposedConv1dLayer(3,64,"Name","transposed-conv1d_4","Cropping","same","Stride",2)
instanceNormalizationLayer("Name","instancenorm_3_1")
reluLayer("Name","relu_2_1")
transposedConv1dLayer(1,1,"Name","transposed-conv1d_5","Cropping","same")
tanhLayer("Name","tanh")];
net = addLayers(net,tempNet);
clear tempNet;
net = initialize(net);
plot(net);
end
生成器用的是1维转置卷积层,输入200个随机数,通过自定义的全连接reshape层将数据转化为8*2048的大小。利用Matlab的DL designer工具包进行分析,通过观察Activation属性,我们可以很快的了解数据在生成器中的变化。其中S为空间维度,C为通道维度,B为批次量(根据训练设置变化)。这里的设计的思路就是利用噪声生成一个通道数高的带加工样本,之后每次一维转置卷积中通道维度每减半、空间维度翻倍。最后加工为128*1(C)的1维数据。
其中,rehape自定义层代码如下,和图像生成的项目基本一致,但是输出格式进行了调整(SCB),该层就是将200个输入噪声通过全连接层变为8*2048长度的数据,再reshape成8*2048*1的SCB格式数据。
classdef projectAndReshapeLayer < nnet.layer.Layer ...
& nnet.layer.Formattable ...
& nnet.layer.Acceleratable
properties
% Layer properties.
OutputSize
end
properties (Learnable)
% Layer learnable parameters.
Weights
Bias
end
methods
function layer = projectAndReshapeLayer(outputSize,NameValueArgs)
% layer = projectAndReshapeLayer(outputSize)
% creates a projectAndReshapeLayer object that projects and
% reshapes the input to the specified output size.
%
% layer = projectAndReshapeLayer(outputSize,Name=name)
% also specifies the layer name.
% Parse input arguments.
arguments
outputSize
NameValueArgs.Name = "";
end
% Set layer name.
name = NameValueArgs.Name;
layer.Name = name;
% Set layer description.
layer.Description = "Project and reshape to size " + ...
join(string(outputSize));
% Set layer type.
layer.Type = "Project and Reshape";
% Set output size.
layer.OutputSize = outputSize;
end
function layer = initialize(layer,layout)
% layer = initialize(layer,layout) initializes the layer
% learnable parameters.
%
% Inputs:
% layer - Layer to initialize
% layout - Data layout, specified as a
% networkDataLayout object
%
% Outputs:
% layer - Initialized layer
% Layer output size.
outputSize = layer.OutputSize;
% Initialize fully connect weights.
if isempty(layer.Weights)
% Find number of channels.
idx = finddim(layout,"C");
numChannels = layout.Size(idx);
% Initialize using Glorot.
sz = [prod(outputSize) numChannels];
numOut = prod(outputSize);
numIn = numChannels;
layer.Weights = initializeGlorot(sz,numOut,numIn);
end
% Initialize fully connect bias.
if isempty(layer.Bias)
% Initialize with zeros.
layer.Bias = initializeZeros([prod(outputSize) 1]);
end
end
function Z = predict(layer, X)
% Forward input data through the layer at prediction time and
% output the result.
%
% Inputs:
% layer - Layer to forward propagate through
% X - Input data, specified as a formatted dlarray
% with a "C" and optionally a "B" dimension.
% Outputs:
% Z - Output of layer forward function returned as
% a formatted dlarray with format "SSCB".
% Fully connect.
weights = layer.Weights;
bias = layer.Bias;
X = fullyconnect(X,weights,bias);
% Reshape.
outputSize = layer.OutputSize;
Z = reshape(X,outputSize(1),outputSize(2),[]);
Z = dlarray(Z,"SCB");
end
end
end
1.2.3 判别器
判别器基本与生成器对称,用1维卷积核, 代码如下:
function net=Creat_1D_Discri(droprate,scale)
net = dlnetwork;
tempNet = [
inputLayer([128 1 NaN],"SCB","Name","input")
dropoutLayer(droprate,"Name","dropout")
convolution1dLayer(3,32,"Name","conv1d","Padding","same","Stride",2)
leakyReluLayer(scale,"Name","leakyrelu_1")
convolution1dLayer(3,64,"Name","conv1d_1","Padding","same","Stride",2)
batchNormalizationLayer("Name","batchnorm")
leakyReluLayer(scale,"Name","leakyrelu_2")
convolution1dLayer(3,128,"Name","conv1d_2","Padding","same","Stride",2)
batchNormalizationLayer("Name","batchnorm_1")
leakyReluLayer(scale,"Name","leakyrelu_3")
convolution1dLayer(3,256,"Name","conv1d_3","Padding","same","Stride",2)
batchNormalizationLayer("Name","batchnorm_1_1")
leakyReluLayer(scale,"Name","leakyrelu_3_1")
convolution1dLayer(3,512,"Name","conv1d_4","Padding","same","Stride",2)
batchNormalizationLayer("Name","batchnorm_1_2")
leakyReluLayer(scale,"Name","leakyrelu_3_2")
convolution1dLayer(5,1,"Name","conv1d_5","Padding","same","Stride",4)
sigmoidLayer("Name","layer")];
net = addLayers(net,tempNet);
% clean up helper variable
clear tempNet;
net = initialize(net);
plot(net);
具体参数如下
1.2.3数据预处理函数
预处理函数有比较明显的改动,1个是本次示例数据是int12的格式,所以我们这里归一化范围修改了。其次是样本凭借的维度(批次维度)为3,需要注意。同时我们需要调整数据的维度顺序,因为arraydatastore按第一个维度分割样本后的数据格式为1*128*1(CSB),为了对应深度学习模型的输入维度(SCB),在预处理前利用permute函数进行维度调整。
function X = preprocessMiniBatch12_2D(data)
% Concatenate mini-batch
X = cat(3,data{:});
%调整矩阵的维度,让数值回到空间通道中,通道在第二位置;
X=permute(X, [2, 1, 3]);
% Rescale the images in the range [-1 1].
X = rescale(X,-1,1,InputMin=0,InputMax=2^12);
end
1.2.4 损失函数
无大变化:
反向传播与梯度计算
function [lossG,lossD,gradientsG,gradientsD,stateG,scoreG,scoreD] = ...
modelLoss(netG,netD,X,Z,flipProb)
% Calculate the predictions for real data with the discriminator network.
YReal = forward(netD,X);
% Calculate the predictions for generated data with the discriminator
% network.
[XGenerated,stateG] = forward(netG,Z);
YGenerated = forward(netD,XGenerated);
% Calculate the score of the discriminator.
scoreD = (mean(YReal) + mean(1-YGenerated)) / 2;
% Calculate the score of the generator.
scoreG = mean(YGenerated);
% Randomly flip the labels of the real images.
numObservations = size(YReal,4);
idx = rand(1,numObservations) < flipProb;
YReal(:,:,:,idx) = 1 - YReal(:,:,:,idx);
% Calculate the GAN loss.
[lossG, lossD] = ganLoss(YReal,YGenerated);
% For each network, calculate the gradients with respect to the loss.
gradientsG = dlgradient(lossG,netG.Learnables,RetainData=true);
gradientsD = dlgradient(lossD,netD.Learnables);
end
损失函数:
function [lossG,lossD] = ganLoss(YReal,YGenerated)
% Calculate the loss for the discriminator network.
lossD = -mean(log(YReal)) - mean(log(1-YGenerated));
% Calculate the loss for the generator network.
lossG = -mean(log(YGenerated));
end
1.3 资源下载
脚本、函数文件与示例数据如下:
通过网盘分享的文件:24-1D_DCGAN_EXAMPLE.zip
链接: https://pan.baidu.com/s/1kxQ4Q3RCzLeeAgdQBWl9KQ?pwd=3wgy 提取码: 3wgy
2.总结
从信息论的角度进行分析,DCGAN 在基于先验知识(真实样本)的条件下尽可能地生成接 近真实的样本,这一过程并不会创造新信息,无法通过对抗过程来增加训练集基本特征的多样性。但对于样本 多样性而言,DCGAN具有基于现有特征进行缩放及组 合以形成更多具有特异性高级特征的能力,因此它具有强化预测模型的泛化能力的潜力,提高模型对特征挖掘和学习能力,但这也导致DCGAN在过少的训练集(基本特征不足)或足够丰富的训练集(高级特征充足)的场景下, 很难带来显著的预测性能效果提升[2].
[2] 光谱技术结合水分校正与样本增广的棉田土壤盐分精准反演 - 中国知网
该项目详细展示和分析了在MATLAB中利用DCGAN生成1D数据的方法和注意事项,欢迎大家进行交流。