Сгенерируйте синтетические сигналы, используя условную генеративную состязательную сеть

Этот пример показывает, как сгенерировать синтетические сигналы насоса с помощью условной генеративной состязательной сети.

Генеративные состязательные сети (GANs) могут использоваться, чтобы получить синтетические данные, которые напоминают реальный вход данных в сети. GAN применяются, когда симуляции являются вычислительно дорогими или эксперименты являются дорогостоящими. Условные GAN (CGAN) могут использовать метки данных в процессе обучения для генерации данных, относящихся к конкретным категориям.

Этот пример обрабатывает моделируемые сигналы, полученные моделью Simulink™ насоса, как «действительные» данные, которые играют роль набора обучающих данных для CGAN. CGAN использует 1-D сверточных сетей и обучается с помощью пользовательского цикла обучения и глубокого обучения массива. Кроме сложения, этот пример использует анализ основных компонентов (PCA), чтобы визуально сравнить характеристики сгенерированных и реальных сигналов.

CGAN для синтеза сигналов

CGAN состоят из двух сетей, которые обучаются вместе как противники:

  1. Сеть генератора - учитывая метку и случайный массив как вход, эта сеть генерирует данные с той же структурой, что и наблюдения обучающих данных, соответствующие той же метке. Цель генератора состоит в том, чтобы сгенерировать маркированные данные, которые дискриминатор классифицирует как «реальные».

  2. Сеть дискриминатора - Учитывая пакеты маркированных данных, содержащих наблюдения как от обучающих данных, так и от сгенерированных данных от генератора, эта сеть пытается классифицировать наблюдения как «реальные» или «сгенерированные». Цель дискриминатора состоит в том, чтобы не быть «обманутым» генератором, когда данные пакеты как реальных, так и сгенерированных маркированных данных.

В идеале эти стратегии приводят к генератору, который генерирует убедительно реалистичные данные, соответствующие входным меткам, и дискриминатору, который выучил сильные функции, характерные для обучающих данных для каждой метки.

Загрузка данных

Моделируемые данные генерируются моделью Simulink насоса, представленной в примере Мультиклассовое Обнаружение Неисправностей с Использованием Смоделированных Данных (Predictive Maintenance Toolbox). Модель Simulink сконфигурирована, чтобы смоделировать три типа отказов: утечки в гидроцилиндре, заблокированные входные отверстия и увеличенное трение подшипника. Набор данных содержит 1575 выходных сигналов потока насоса, из которых 760 являются исправными сигналами, а 815 имеют один отказ, комбинации двух отказов или комбинации трех отказов. Каждый сигнал имеет 1201 выборок сигнала со скоростью дискретизации 1000 Гц.

Загрузите и разархивируйте данные во временной директории, местоположение которого задано MATLAB ® tempdir команда. Если у вас есть данные, отличные от заданных tempdirизмените имя директории в следующем коде.

% Download the data
dataURL = 'https://ssd.mathworks.com/supportfiles/SPT/data/PumpSignalGAN.zip';
saveFolder = fullfile(tempdir,'PumpSignalGAN'); 
zipFile = fullfile(tempdir,'PumpSignalGAN.zip');
if ~exist(saveFolder,'dir')
    websave(zipFile,dataURL);
end

% Unzip the data
unzip(zipFile,saveFolder)

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 1 024 массивами таможенным слоем.

  • Преобразовывает категорические метки во вложение векторов и изменяет их к массивы 4 на 1 на 1.

  • Конкатенирует результаты из двух входов по размерности канала. Выходные выходы: массив 4 на 1 на 1025.

  • Сверхдискретизировал получающиеся массивы к 1201 массивами 1 на 1, используя серию перемещенных слоев скручивания 1-D со слоев ReLU и нормализацией партии.

Чтобы проецировать и изменить форму входного сигнала шума, используйте пользовательский слой projectAndReshapeLayer, присоединенный к этому примеру как вспомогательный файл. The projectAndReshapeLayer объект увеличивает масштаб входа с помощью полносвязного слоя и изменяет форму выхода на заданный размер.

Чтобы ввести метки в сеть, используйте imageInputLayer и задайте размер 1 на 1. Для встраивания и изменения формы входов меток используйте пользовательский слой embedAndReshapeLayer, присоединенный к этому примеру как вспомогательный файл. The 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)

Чтобы обучить сеть с помощью пользовательского цикла обучения и включить автоматическую дифференциацию, преобразуйте график слоев в dlnetwork объект.

dlnetGenerator = dlnetwork(lgraphGenerator);

Определите сеть дискриминатора

Задайте следующую сеть с двумя входами, которая классифицирует реальные и сгенерированные сигналы 1201 на 1 с учетом набора сигналов и соответствующих им меток.

Эта сеть:

  • Занимает 1201 сигналами 1 на 1, как введено.

  • Преобразовывает категорические метки во вложение векторов и изменяет их к 1201 массивами 1 на 1.

  • Конкатенирует результаты из двух входов по размерности канала. Выходные выходы: массив 1201 на 1 на 1025.

  • Субдискретизирует получающиеся массивы к скалярным счетам предсказания, которые являются массивами 1 на 1 на 1, используя серию 1-D слоев скручивания с прохудившимися слоями ReLU со шкалой 0,2.

% 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)

Чтобы обучить сеть с помощью пользовательского цикла обучения и включить автоматическую дифференциацию, преобразуйте график слоев в dlnetwork объект.

dlnetDiscriminator = dlnetwork(lgraphDiscriminator);

Обучите модель

Обучите модель CGAN с помощью пользовательского цикла обучения. Закольцовывайте обучающие данные и обновляйте сетевые параметры при каждой итерации. Чтобы контролировать процесс обучения, отобразите сгенерированные исправные и неисправные сигналы с помощью двух фиксированных массивов случайных значений для ввода в генератор, а также график счетов двух сетей.

Для каждой эпохи перетасуйте обучающие данные и закольцовывайте мини-пакеты данных.

Для каждого мини-пакета:

  • Сгенерируйте dlarray объект, содержащий массив случайных значений для сети генератора.

  • Для обучения графический процессор преобразуйте данные в gpuArray (Parallel Computing Toolbox) объект.

  • Оцените градиенты модели с помощью dlfeval и вспомогательную функцию modelGradients.

  • Обновляйте параметры сети с помощью adamupdate функция.

Функция помощника modelGradients принимает за вход сети генератора и дискриминатора, мини-пакет входных данных и массив случайных значений и возвращает градиенты потерь относительно настраиваемых параметров в сетях и счетов двух сетей. Функция потерь определяется в вспомогательной функции 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;

Установите окружение выполнения, чтобы запустить CGAN на центральном процессоре. Чтобы запустить CGAN на графическом процессоре, установите executionEnvironment в "gpu"или выберите параметр" Выполнить на графическом процессоре "в Live Editor. Для использования графический процессор требуется Parallel Computing Toolbox™. Информацию о том, какие графические процессоры поддерживаются, см. в разделе Поддержка GPU Release (Parallel Computing Toolbox).

executionEnvironment = "cpu";
params.executionEnvironment = executionEnvironment;

Пропустите процесс обучения, загрузив предварительно обученную сеть. Чтобы обучить сеть на вашем компьютере, установите trainNow на true или выберите «Train CGAN now» опции в Live Editor.

trainNow = false;
if trainNow
    % Train the CGAN
    [dlnetGenerator, dlnetDiscriminator] = trainGAN ( dlnetGenerator,...
        dlnetDiscriminator, flowNormalized, метки,  params);%#ok
else
    % Use pretrained CGAN (default)
    load (полный файл (tempdir,'PumpSignalGAN','GANModel.mat')) % load data set
end

Обучающий график ниже показывает пример счетам сетей генератора и дискриминатора. Дополнительные сведения о том, как интерпретировать счета сети, см. в разделах Мониторинг процесса обучения GAN и Идентификация распространенных типах отказа. В этом примере счетов как генератора, так и дискриминатора сходятся близко к 0,5, указывая, что производительность обучения хороша.

Синтезируйте сигналы потока

Создайте dlarray объект, содержащий партию 2 000 массивов случайных значений 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');

Чтобы сгенерировать сигналы с помощью графический процессор, преобразуйте данные в gpuArray объекты.

if executionEnvironment == "gpu"
    dlZNew = gpuArray(dlZNew);
    dlTNew = gpuArray(dlTNew);
end

Используйте predict функция на генераторе с партией массивов случайных значений 1 на 1 на 100 и меток, чтобы сгенерировать синтетические сигналы и вернуться шаг стандартизации, который Вы выполнили на оригинальных сигналах потока.

dlXGeneratedNew = predict(dlnetGenerator,dlZNew,dlTNew)*stdFlow+meanFlow;

Визуализация функций сигнала

В отличие от изображений и аудиосигналов, общие сигналы имеют характеристики, которые затрудняют их разнесение восприятия человеком. Чтобы сравнить реальные и сгенерированные сигналы или исправные и неисправные сигналы, можно применить анализ основных компонентов (PCA) к статистическим функциям реальных сигналов, а затем спроектировать функции сгенерированных сигналов к тому же подпространству PCA.

Редукция данных

Объедините исходный действительный сигнал и сгенерированные сигналы в одну матрицу данных. Используйте функцию helper extractFeature для извлечения функции, включающей общую статистику сигналов, таких как среднее и отклонение, а также спектральные характеристики.

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

Каждая строка features соответствует функциям одного сигнала.

Измените метки для сгенерированных здоровых и неисправных сигналов, а также реальных здоровых и неисправных сигналов.

L = [squeeze(TNew)+2;labels.'];

Теперь у меток есть следующие определения:

  • 1 - Сгенерированные исправные сигналы

  • 2 - Сгенерированные неисправные сигналы

  • 3 - Реальные исправные сигналы

  • 4 - Реальные неисправные сигналы

Анализ основных компонентов

Выполните PCA на функциях реальных сигналов и проецируйте функции сгенерированных сигналов на тот же подпространство PCA. W - коэффициент и Y - счет.

% PCA via svd
featuresReal = features(idxReal,:);
mu = mean(featuresReal,1);
[~,S,W] = svd(featuresReal-mu);
S = diag(S);
Y = (features-mu)*W;

Из сингулярного вектора Sпервые три сингулярных значений составляют 99% энергии в S. Можно визуализировать функции сигнала, используя первые три основных компонента.

sum(S(1:3))/sum(S)
ans = single
    0.9923

Постройте график функций всех сигналов с помощью первых трех основных компонентов. В подпространстве PCA распределение сгенерированных сигналов подобно распределению действительных сигналов.

idxHealthyR = L==1;
idxFaultR = L==2;

idxHealthyG = L==3;
idxFaultG = L==4;

pp = Y(:,1:3);

figure
scatter3(pp(idxHealthyR,1),pp(idxHealthyR,2),pp(idxHealthyR,3),'o')
xlabel('1st Principal Component')
ylabel('2nd Principal Component')
zlabel('3rd Principal Component')
hold on
scatter3(pp(idxFaultR,1),pp(idxFaultR,2),pp(idxFaultR,3),'d')
scatter3(pp(idxHealthyG,1),pp(idxHealthyG,2),pp(idxHealthyG,3),'s')
scatter3(pp(idxFaultG,1),pp(idxFaultG,2),pp(idxFaultG,3),'+')
view(-10,20)
legend('Real healthy','Real faulty','Generated healthy','Generated faulty', ...
    'Location','Best')
hold off

Чтобы лучше захватить различие между реальными сигналами и сгенерированными сигналами, постройте график подпространства с помощью первых двух главных компонентов.

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))
predictAccuracy = 0.9460

Используйте матрицу неточностей, чтобы просмотреть подробную информацию о эффективности предсказания для каждой категории. Матрица неточностей показывает, что в каждой категории классификатор, обученный на основе сгенерированных сигналов, достигает высокой степени точности.

figure
confusionchart(actualValue,predictedValue)

Тематическое исследование

Сравните спектральные характеристики действительных и сгенерированных сигналов. Из-за недетерминированного поведения обучения графический процессор, если вы сами обучаете модель CGAN, ваши результаты могут отличаться от результатов в этом примере.

Скорость мотора насоса составляет 950 об/мин, или 15,833 Гц, и, поскольку насос имеет три цилиндра, ожидается, что поток будет иметь фундаментальную частоту в 3 раза 15,833 Гц, или 47,5 Гц, и гармоники при множителях 47,5 Гц. Постройте график спектра для одного случая реальных и сгенерированных здоровых сигналов. Из графика сгенерированный сигнал исправности имеет относительно высокие значения степени 47,5 Гц и 2 раза 47,5 Гц, что в точности совпадает с реальным сигналом работоспособности.

Fs = 1000;
pspectrum([x(:,1) x(:,2006)],Fs)
set(gca,'XScale','log')
legend('Generated healthy','Real healthy')

Если отказы существуют, резонансы будут происходить при скорости двигателя насоса, 15,833 Гц, и его гармонике. Постройте график спектров для одного случая действительных и сгенерированных неисправных сигналов. Сгенерированный сигнал имеет относительно высокие значения степени около 15,833 Гц и его гармоники, что подобно реальному дефектному сигналу.

pspectrum([x(:,1011) x(:,2100)],Fs)
set(gca,'XScale','log')
legend('Generated faulty','Real faulty')

Постройте спектры для другого случая реальных и сгенерированных неисправных сигналов. Спектральные характеристики сгенерированных дефектных сигналов не очень хорошо совпадают с теоретическим анализом и отличаются от реального дефектного сигнала. CGAN все еще может быть улучшен путем настройки структуры сети или гиперпараметров.

pspectrum([x(:,1001) x(:,2600)],Fs)
set(gca,'XScale','log')
legend('Generated faulty','Real faulty')

Время расчета

Симуляция Simulink занимает около 14 часов, чтобы сгенерировать 2000 сигналы потока насоса. Это длительность может быть сокращено примерно до 1,7 часов с восемью параллельными рабочими, если у вас есть Parallel Computing Toolbox™.

CGAN занимает 1,5 часов, чтобы обучить и 70 секунды, чтобы сгенерировать такое же количество синтетических данных с графическим процессором NVIDIA Titan V.

Для просмотра документации необходимо авторизоваться на сайте