生成对抗网络 (GAN) 可用于生成类似于网络真实数据输入的合成数据。当模拟计算成本高或实验成本高昂时,GAN非常有用。条件 GAN (CGAN) 可以在训练过程中使用数据标签来生成属于特定类别的数据。
此例将 Simulink™ 模型获得的仿真信号视为“真实”数据,该数据充当 CGAN 的训练数据集。CGAN 使用一维卷积网络,并使用自定义训练循环和深度学习数组进行训练。此外,此示例使用主成分分析 (PCA) 直观地比较生成信号和真实信号的特征。
仿真数据由使用仿真数据进行多类故障检测 (Predictive Maintenance Toolbox) 示例中介绍的泵 Simulink 模型生成。Simulink 模型配置为对三种类型的故障进行建模:气缸泄漏、入口堵塞和轴承摩擦增加。该数据集包含 1575 个泵输出流量信号,其中 760 个为健康信号,815 个为单个故障、两个故障组合或三个故障组合。每个信号有 1201 个信号样本,采样率为 1000 Hz。
下载并解压缩临时目录中的数据,该目录的位置由 MATLAB® 命令指定。如果数据位于与 指定的文件夹不同的文件夹中,请更改以下代码中的目录名称。
% Download the data
dataURL = 'https://ssd.mathworks.com/supportfiles/SPT/data/PumpSignalGAN.zip';
saveFolder = fullfile(tempdir,'PumpSignalGAN');
zipFile = fullfile(tempdir,'PumpSignalGAN.zip');
if ~exist(fullfile(saveFolder,'simulatedDataset.mat'),'file')
websave(zipFile,dataURL);
% Unzip the data
unzip(zipFile,saveFolder)
end
zip 文件包含训练数据集和预训练的 CGAN:
-
simulatedDataset
— 模拟信号及其相应的分类标签 -
GANModel
— 在模拟数据上训练的生成器和鉴别器
加载训练数据集并标准化信号,使其均值和单位方差为零。
load(fullfile(saveFolder,'simulatedDataset.mat')) % load data set
meanFlow = mean(flow,2);
flowNormalized = flow-meanFlow;
stdFlow = std(flowNormalized(:));
flowNormalized = flowNormalized/stdFlow;
正常信号标记为 1,错误信号标记为 2。
定义网络
定义以下双输入网络,该网络在给定 1×1×100 随机值数组和相应标签的情况下生成流动信号。
网络:
-
通过自定义图层将 1×1×100 的噪声阵列投影并重塑为 4×1×1024 阵列。
-
将分类标签转换为嵌入向量,并将其重塑为 4×1×1 数组。
-
沿通道维度连接两个输入的结果。输出是一个 4×1×1025 数组。
-
使用一系列具有批量归一化和 ReLU 层的一维转置卷积层,将生成的数组上采样为 1201×1×1 阵列。
要投影和调整噪声输入,请使用自定义图层 projectAndReshapeLayer
,该图层作为支持文件附加到此示例中。该对象使用完全连接的层来放大输入,并将输出调整为指定大小。projectAndReshapeLayer
要将标签输入到网络中,请使用对象并指定 1×1 的大小。若要嵌入和调整标签输入的形状,请使用自定义图层 embedAndReshapeLayer
,该图层作为支持文件附加到此示例。该对象使用嵌入和全连接操作将分类标签转换为指定大小的单通道数组。对于分类输入,请使用嵌入维度 100。
% Generator Network
numFilters = 64;
numLatentInputs = 100;
projectionSize = [4 1 1024];
numClasses = 2;
embeddingDimension = 100;
layersGenerator = [
imageInputLayer([1 1 numLatentInputs],'Normalization','none','Name','in')
projectAndReshapeLayer(projectionSize,numLatentInputs,'proj');
concatenationLayer(3,2,'Name','cat');
transposedConv2dLayer([5 1],8*numFilters,'Name','tconv1')
batchNormalizationLayer('Name','bn1','Epsilon',5e-5)
reluLayer('Name','relu1')
transposedConv2dLayer([10 1],4*numFilters,'Stride',4,'Cropping',[1 0],'Name','tconv2')
batchNormalizationLayer('Name','bn2','Epsilon',5e-5)
reluLayer('Name','relu2')
transposedConv2dLayer([12 1],2*numFilters,'Stride',4,'Cropping',[1 0],'Name','tconv3')
batchNormalizationLayer('Name','bn3','Epsilon',5e-5)
reluLayer('Name','relu3')
transposedConv2dLayer([5 1],numFilters,'Stride',4,'Cropping',[1 0],'Name','tconv4')
batchNormalizationLayer('Name','bn4','Epsilon',5e-5)
reluLayer('Name','relu4')
transposedConv2dLayer([7 1],1,'Stride',2,'Cropping',[1 0],'Name','tconv5')
];
lgraphGenerator = layerGraph(layersGenerator);
layers = [
imageInputLayer([1 1],'Name','labels','Normalization','none')
embedAndReshapeLayer(projectionSize(1:2),embeddingDimension,numClasses,'emb')];
lgraphGenerator = addLayers(lgraphGenerator,layers);
lgraphGenerator = connectLayers(lgraphGenerator,'emb','cat/in2');
绘制生成器的网络结构。
plot(lgraphGenerator)
要使用自定义训练循环训练网络并启用自动微分,请将层图转换为对象。
dlnetGenerator = dlnetwork(lgraphGenerator);
定义以下双输入网络,该网络根据一组信号及其相应的标签对实数和生成的 1201×1 信号进行分类。
这个网络:
-
将 1201×1×1 信号作为输入。
-
将分类标签转换为嵌入向量,并将其重塑为 1201×1×1 数组。
-
沿通道维度连接两个输入的结果。输出是一个 1201×1×1025 数组。
-
使用一系列具有 1.1 刻度的泄漏 ReLU 层的一维卷积层,将生成的数组下采样到标量预测分数,这些分数是 1×1×0 数组。
-
% Discriminator Network scale = 0.2; inputSize = [1201 1 1]; layersDiscriminator = [ imageInputLayer(inputSize,'Normalization','none','Name','in') concatenationLayer(3,2,'Name','cat') convolution2dLayer([17 1],8*numFilters,'Stride',2,'Padding',[1 0],'Name','conv1') leakyReluLayer(scale,'Name','lrelu1') convolution2dLayer([16 1],4*numFilters,'Stride',4,'Padding',[1 0],'Name','conv2') leakyReluLayer(scale,'Name','lrelu2') convolution2dLayer([16 1],2*numFilters,'Stride',4,'Padding',[1 0],'Name','conv3') leakyReluLayer(scale,'Name','lrelu3') convolution2dLayer([8 1],numFilters,'Stride',4,'Padding',[1 0],'Name','conv4') leakyReluLayer(scale,'Name','lrelu4') convolution2dLayer([8 1],1,'Name','conv5')]; lgraphDiscriminator = layerGraph(layersDiscriminator); layers = [ imageInputLayer([1 1],'Name','labels','Normalization','none') embedAndReshapeLayer(inputSize,embeddingDimension,numClasses,'emb')]; lgraphDiscriminator = addLayers(lgraphDiscriminator,layers); lgraphDiscriminator = connectLayers(lgraphDiscriminator,'emb','cat/in2');
绘制鉴别器的网络结构。
-
plot(lgraphDiscriminator)
-
要使用自定义训练循环训练网络并启用自动微分,请将层图转换为对象。
-
dlnetDiscriminator = dlnetwork(lgraphDiscriminator);
使用自定义训练循环训练 CGAN 模型。循环训练数据并在每次迭代时更新网络参数。为了监控训练进度,使用两个固定的随机值数组输入到生成器中,以及两个网络的分数图来显示生成的健康和错误信号。
对于每个 epoch,对训练数据进行随机排序,并循环访问小批量数据。
对于每个小批量:
-
生成一个 dlarray 对象,该对象包含生成器网络的随机值数组。
-
对于 GPU 训练,请将数据转换为 gpuArray (Parallel Computing Toolbox) 对象。
-
使用 dlfeval 和辅助函数
modelGradients
评估模型梯度。 -
使用 adamupdate 函数更新网络参数。
helper 函数将生成器和判别器网络、一小批输入数据和随机值数组作为输入,并返回相对于网络中可学习参数的损失梯度和两个网络的分数。损失函数在辅助函数
ganLoss
中定义。设置训练参数。
-
-
params.numLatentInputs = numLatentInputs; params.numClasses = numClasses; params.sizeData = [inputSize length(labels)]; params.numEpochs = 1000; params.miniBatchSize = 256; % Specify the options for Adam optimizer params.learnRate = 0.0002; params.gradientDecayFactor = 0.5; params.squaredGradientDecayFactor = 0.999;
设置执行环境以在 CPU 上运行 CGAN。要在 GPU 上运行 CGAN,请设置为“”或在实时编辑器中选择“在 GPU 上运行”选项。使用 GPU 需要 Parallel Computing Toolbox™。要查看支持哪些 GPU,请参阅 GPU 计算要求 (Parallel Computing Toolbox)。
-
executionEnvironment = "gpu"; params.executionEnvironment = executionEnvironment;
通过加载预训练网络来跳过训练过程。要在计算机上训练网络,请在实时编辑器中设置或选择“立即训练 CGAN”选项。
-
trainNow = false; if trainNow % Train the CGAN [dlnetGenerator,dlnetDiscriminator] = trainGAN(dlnetGenerator, ... dlnetDiscriminator,flowNormalized,labels,params); %#ok else % Use pretrained CGAN (default) load(fullfile(tempdir,'PumpSignalGAN','GANModel.mat')) % load data set end
下面的训练图显示了生成器和判别器网络的分数示例。要了解有关如何解释网络分数的更多信息,请参阅监控 GAN 训练进度和识别常见故障模式。在本例中,生成器和判别器的得分都收敛到0.5,说明训练性能良好。
-
创建一个对象,其中包含一批 2000 个 1×1×100 的随机值数组,以输入到生成器网络中。重置随机数生成器以获得可重现的结果。
-
rng default numTests = 2000; ZNew = randn(1,1,numLatentInputs,numTests,'single'); dlZNew = dlarray(ZNew,'SSCB');
指定前 1000 个随机数组运行正常,其余数组运行错误。
-
TNew = ones(1,1,1,numTests,'single'); TNew(1,1,1,numTests/2+1:end) = single(2); dlTNew = dlarray(TNew,'SSCB');
要使用 GPU 生成信号,请将数据转换为对象。
-
if executionEnvironment == "gpu" dlZNew = gpuArray(dlZNew); dlTNew = gpuArray(dlTNew); end
将生成器上的函数与一批 1×1×100 随机值和标签数组一起使用,以生成合成信号并恢复对原始流量信号执行的标准化步骤。
-
dlXGeneratedNew = predict(dlnetGenerator,dlZNew,dlTNew)*stdFlow+meanFlow;