开始填坑
MATLAB统计机器学习,深度学习,计算机视觉 - 哥廷根数学学派的文章 - 知乎 MATLAB统计机器学习,深度学习,计算机视觉 - 知乎
之前写过在使用深度学习对机械系统或电气系统进行故障诊断时,如果没有大量的故障样本,可以使用Simulink 生成故障样本数据,还可以使用数字孪生技术等等
深度学习故障诊断之-使用 Simulink 生成故障数据 - 哥廷根数学学派的文章 - 知乎 深度学习故障诊断之-使用 Simulink 生成故障数据 - 知乎
本次讲解如何使用条件生成对抗网络CGAN生成泵流量信号
完整代码链接
生成对抗网络 (GAN) 可用于生成近似真实数据的数据。 当模拟计算成本较高或实验成本较高时,GAN 非常有用。 条件 GAN(CGAN)可以在训练过程中使用数据标签来生成属于特定类别的数据。注意:本例将泵Simulink模型获得的模拟信号视为“真实”数据,当作CGAN 的训练数据集。 CGAN 使用一维卷积网络,此外本例使用主成分分析 (PCA) 直观地比较生成信号和真实信号的特征。三缸柱塞泵的Simulink模型如下,来源于mathworks大佬。
再次强调:本例将泵Simulink模型获得的模拟信号视为“真实”数据,当作CGAN 的训练数据集
下图为Simulink模型生成的“真实”泵流量信号
接下来步入正题,CGAN 由两个作为“对手”一起训练的网络组成。
生成器网络—给定一个标签和随机数组作为输入,该网络生成与对应于相同标签的训练数据相同结构的数据。 生成器的目标是生成鉴别器分类为“真实”的标记数据。
判别器网络—给定一批标记数据,其中包含来自训练数据和生成器生成数据的观察结果,该网络尝试将观察结果分类为“真实”或“生成”。 判别器的目标是在给定批次的真实和生成的标记数据时不被生成器“愚弄”。
导入数据
模拟数据由 Simulink 模型生成。 Simulink 模型对3种类型的故障进行建模:气缸泄漏、进气口阻塞和轴承故障。 该数据集包含 1575 个泵输出流量信号,其中 760 个为健康信号,815 个为单个故障、两个故障的组合或三个故障的组合。 每个信号有 1201 个采样点,采样频率为 1000 Hz。.
加载训练数据集并将信号进行标准化
- load(fullfile(saveFolder,'simulated.mat')) % 导入数据集
- meanFlow = mean(flow,2);
- flowNormalized = flow-meanFlow;
- stdFlow = std(flowNormalized(:));
- flowNormalized = flowNormalized/stdFlow;
健康信号标记为 1,故障信号标记为 2。
定义生成器网络,不再赘述,很容易看懂
- 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');
绘制生成器的网络结构
将层图转换为 dlnetwork 对象,便于自动微分
dlnetGenerator = dlnetwork(lgraphGenerator);
类似地定义判别器网络
- 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');
绘制判别器的网络结构
同样将层图转换为 dlnetwork 对象便于自动微分
dlnetDiscriminator = dlnetwork(lgraphDiscriminator);
训练模型
使用自定义训练循环训练 CGAN 模型。 对于每个mini-batch小批量数据:
为生成器网络生成一个包含随机值数组的 dlarray 对象。
对于 GPU 训练,将数据转换为 gpuArray 对象。
使用 dlfeval 和函数 modelGradients计算模型梯度。
指定训练参数
- params.numLatentInputs = numLatentInputs;
- params.numClasses = numClasses;
- params.sizeData = [inputSize length(labels)];
- params.numEpochs = 1000;
- params.miniBatchSize = 256;
-
- % 指定 Adam 优化器的选项
- params.learnRate = 0.0002;
- params.gradientDecayFactor = 0.5;
- params.squaredGradientDecayFactor = 0.999;
设置执行环境以在 CPU 上运行 CGAN
- executionEnvironment = "cpu";
- params.executionEnvironment = executionEnvironment;
网络训练
- [dlnetGenerator,dlnetDiscriminator] = trainGAN(dlnetGenerator, ...
- dlnetDiscriminator,flowNormalized,labels,params);
下面的训练图显示了生成器和鉴别器网络的得分,在这个例子中,生成器和判别器的分数都收敛到接近 0.5,表明训练性能良好
生成信号
创建一个 dlarray 对象,其中包含一批 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 生成信号,将数据转换为 gpuArray 对象。
- if executionEnvironment == "gpu"
- dlZNew = gpuArray(dlZNew);
- dlTNew = gpuArray(dlTNew);
- end
-
-
- dlXGeneratedNew = predict(dlnetGenerator,dlZNew,dlTNew)*stdFlow+meanFlow;
信号特征可视化
与图像和音频信号不同,一般信号具有使人类感知难以区分的特征。 要比较真实信号和生成信号或健康信号和故障信号,可以将主成分分析PCA应用于真实信号的统计特征,然后将生成信号的特征投影到相同的 PCA 子空间。
特征提取
将真实信号和生成信号组合在一个数据矩阵中。 使用函数 extractFeatures 提取特征,包括常见的信号统计信息,例如均值和方差以及频谱特征。
- idxGenerated = 1:numTests;
- idxReal = numTests+1:numTests+size(flow,2);
-
- XGeneratedNew = squeeze(extractdata(gather(dlXGeneratedNew)));
- x = [XGeneratedNew single(flow)];
- features = zeros(size(x,2),14,'like',x);
-
- for ii = 1:size(x,2)
- features(ii,:) = extractFeatures(x(:,ii));
- end
每行对应一个信号的特征。
修改生成的健康和故障信号以及真实健康和故障信号的标签。
L = [squeeze(TNew)+2;labels.'];
标签的定义如下:
1 — Generated healthy signals
2 — Generated faulty signals
3 — Real healthy signals
4 — Real faulty signals
主成分分析
对真实信号的特征进行主成分分析,并将生成信号的特征投影到相同的主成分分析子空间。
通过奇异值分解进行主成分分析
- featuresReal = features(idxReal,:);
- mu = mean(featuresReal,1);
- [~,S,W] = svd(featuresReal-mu);
- S = diag(S);
- Y = (features-mu)*W;
前三个奇异值占99% 的能量。因此可以利用前三个主成分来可视化信号特征。
sum(S(1:3))/sum(S)
使用前3个主成分绘制所有信号的特征。 在 PCA 子空间中,生成信号的分布类似于真实信号的分布
为了更好地捕捉真实信号和生成信号之间的差异,使用前两个主成分绘制子空间
view(2)
健康信号和故障信号无论是真实的还是生成的,都位于 PCA 子空间的同一区域,表明生成的信号具有与真实信号相似的特征。
预测真实信号的标签
为了进一步说明 CGAN 的性能,根据生成的信号训练 SVM 分类器,然后预测真实信号是健康的还是故障的。将生成的信号设置为训练数据集,将真实信号设置为测试数据集, 将数字标签更改为字符向量。
- LABELS = {'Healthy','Faulty'};
- strL = LABELS([squeeze(TNew);labels.']).';
-
- dataTrain = features(idxGenerated,:);
- dataTest = features(idxReal,:);
-
- labelTrain = strL(idxGenerated);
- labelTest = strL(idxReal);
-
- predictors = dataTrain;
- response = labelTrain;
- cvp = cvpartition(size(predictors,1),'KFold',5);
使用生成的信号训练 SVM 分类器
- SVMClassifier = fitcsvm( ...
- predictors(cvp.training(1),:), ...
- response(cvp.training(1)),'KernelFunction','polynomial', ...
- 'PolynomialOrder',2, ...
- 'KernelScale','auto', ...
- 'BoxConstraint',1, ...
- 'ClassNames',LABELS, ...
- 'Standardize',true);
使用经过训练的分类器获得真实信号的预测标签。 分类器实现了 90% 以上的预测准确率。
- actualValue = labelTest;
- predictedValue = predict(SVMClassifier,dataTest);
- predictAccuracy = mean(cellfun(@strcmp,actualValue,predictedValue))
查看混淆矩阵
比较真实信号和生成信号的频谱特性
泵电机转速为 950 rpm,即15.833 Hz,由于泵具有三个气缸,因此预计流量的基波频率为 15.833 Hz 或 47.5 Hz 的 3 倍,谐波频率为 47.5 Hz 的倍数。 从真实和生成的健康信号的频谱图中可以看出,生成的健康信号在 47.5 Hz 和 2 倍 47.5 Hz 处具有较高的功率值,与真实健康信号相同。
如果存在故障,将在泵电机速度 15.833 Hz 及其谐波处发生共振。 绘制真实和生成的故障信号的频谱。 生成的信号在 15.833 Hz 及其谐波附近具有较高的功率值,与真正的故障信号相似。
绘制另一种真实和生成的故障信号的频谱。 产生的故障信号的频谱特征与理论分析的匹配度不高,与真实故障信号存在差异。 CGAN 仍然可以通过调整网络结构或超参数来改进。
计算时间
Simulink 仿真需要大约 14 小时才能生成 2000 个泵流量信号。 如果有 Parallel Computing Toolbox工具箱,则时间可以减少到大约 1.7 小时,使用 8 个并行工作器。使用 NVIDIA Titan V GPU,CGAN 需要 1.5 小时的训练时间和 70 秒的时间来生成相同数量的数据。