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 canUseGPUZValidation = gpuArray(ZValidation);
endf = 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 oniteration = 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 canUseGPUZ = 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 iterationendendend
end
%%生成新图像
numLatentInputs = 50;
numObservations = 1;
ZNew = randn(numLatentInputs,numObservations,"single");
ZNew = dlarray(ZNew,"CB");
if canUseGPUZNew = gpuArray(ZNew);
endXGeneratedNew = 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.Acceleratableproperties% Layer properties.OutputSizeendproperties (Learnable)% Layer learnable parameters.WeightsBiasendmethodsfunction 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.argumentsoutputSizeNameValueArgs.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;endfunction 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]);endendfunction 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");endend
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数据的方法和注意事项,欢迎大家进行交流。