В этом примере показано, как обнаружить аномалии в данных о вибрации с помощью машинного обучения и глубокого обучения. Данные о вибрации использования в качестве примера собраны от промышленной машины. Во-первых, вы извлекаете функции из необработанных измерений, соответствующих нормальному функционированию с помощью Приложения Diagnostic Feature Designer. Используйте выбранные функции, чтобы обучить каждую модель на рассмотрении. Затем используйте каждую обученную модель, чтобы идентифицировать, действует ли машина при нормальном состоянии.
Набор данных
Набор данных содержит измерения вибрации с 3 осями от промышленной машины. Данные собраны оба сразу до и после запланированного обслуживания. Данные, собранные после запланированного обслуживания, приняты, чтобы представлять нормальные условия работы машины. Прежде чем данные об обслуживании могут представлять или нормальные или аномальные условия. Данные для каждой оси хранятся в отдельном столбце, и каждый файл содержит 7 000 измерений. Загрузите данные MathWorks supportfiles сайт и постройте выборку нормального и аномального набора данных.
url = 'https://ssd.mathworks.com/supportfiles/predmaint/anomalyDetection3axisVibration/v1/vibrationData.zip'; websave('vibrationData.zip',url); unzip('vibrationData.zip'); load("MachineData.mat") trainData
trainData=40×4 table
ch1 ch2 ch3 label
________________ ________________ ________________ ______
{70000×1 double} {70000×1 double} {70000×1 double} Before
{70000×1 double} {70000×1 double} {70000×1 double} Before
{70000×1 double} {70000×1 double} {70000×1 double} Before
{70000×1 double} {70000×1 double} {70000×1 double} Before
{70000×1 double} {70000×1 double} {70000×1 double} Before
{70000×1 double} {70000×1 double} {70000×1 double} Before
{70000×1 double} {70000×1 double} {70000×1 double} Before
{70000×1 double} {70000×1 double} {70000×1 double} Before
{70000×1 double} {70000×1 double} {70000×1 double} Before
{70000×1 double} {70000×1 double} {70000×1 double} Before
{70000×1 double} {70000×1 double} {70000×1 double} Before
{70000×1 double} {70000×1 double} {70000×1 double} Before
{70000×1 double} {70000×1 double} {70000×1 double} Before
{70000×1 double} {70000×1 double} {70000×1 double} Before
{70000×1 double} {70000×1 double} {70000×1 double} Before
{70000×1 double} {70000×1 double} {70000×1 double} Before
⋮
Чтобы лучше изучить данные, визуализируйте его до и после обслуживания. Выберите четвертый член ансамбля для графика, и вы видите, что данные для этих двух условий выглядят заметно отличающимися.
ensMember = 4; helperPlotVibrationData(trainData, ensMember)
Извлечение функций с приложением Diagnostic Feature Designer
Используя необработанные данные для учебного машинного обучения модели не очень эффективно. Приложение Diagnostic Feature Designer позволяет вам в интерактивном режиме исследовать и предварительно обработать свои данные, извлечь время и функции частотного диапазона, затем отранжировать признаки, чтобы определить, который будет самым эффективным. Можно затем экспортировать функцию, чтобы извлечь выбранные функции из набора данных программно. Откройте приложение DFD:
diagnosticFeatureDesigner
Нажмите кнопку New Session, выберите trainData как источник и затем установите label
как условная переменная. label
переменная идентифицирует условие машины для соответствующих данных.
Можно использовать Diagnostic Feature Designer, чтобы выполнить итерации на функциях и оценить их. Представление гистограммы для всех генерированных признаков создается, чтобы визуализировать распределения, разделенные метками для различных функций, извлеченных из ch1. Обратите внимание на то, что гистограммы, показанные ниже, от намного большего набора данных, таким образом, разделение легче визуализировать. Набор данных, используемый до сих пор, является подмножеством исходных данных, таким образом, ваши результаты могут отличаться.
Используйте лучшие четыре отранжированных признака для каждого канала.
ch1: Фактор Гребня, Эксцесс, RMS, Станд.
ch2: Среднее значение, RMS, Скошенность, Станд.
ch3: Фактор Гребня, SINAD, ОСШ, THD
Экспортируйте функцию, чтобы генерировать признаки из Диагностического приложения разработчика Функции и сохранить его с именем generateFeatures
. Эта функция извлекает лучшие 4 соответствующих функции из каждого канала в наборе данных в целом из командной строки.
trainFeatures = generateFeatures(trainData); head(trainFeatures)
ans=8×13 table
label ch1_stats/Col1_CrestFactor ch1_stats/Col1_Kurtosis ch1_stats/Col1_RMS ch1_stats/Col1_Std ch2_stats/Col1_Mean ch2_stats/Col1_RMS ch2_stats/Col1_Skewness ch2_stats/Col1_Std ch3_stats/Col1_CrestFactor ch3_stats/Col1_SINAD ch3_stats/Col1_SNR ch3_stats/Col1_THD
______ __________________________ _______________________ __________________ __________________ ___________________ __________________ _______________________ __________________ __________________________ ____________________ __________________ __________________
Before 2.2811 1.8087 2.3074 2.3071 -0.032332 0.64962 4.523 0.64882 11.973 -15.945 -15.886 -2.732
Before 2.3276 1.8379 2.2613 2.261 -0.03331 0.59458 5.548 0.59365 10.284 -15.984 -15.927 -2.7507
Before 2.3276 1.8626 2.2613 2.2612 -0.012052 0.48248 4.3638 0.48233 8.9125 -15.858 -15.798 -2.7104
Before 2.8781 2.1986 1.8288 1.8285 -0.005049 0.34984 2.3324 0.34981 11.795 -16.191 -16.14 -3.0683
Before 2.8911 2.06 1.8205 1.8203 -0.0018988 0.27366 1.7661 0.27365 11.395 -15.947 -15.893 -3.1126
Before 2.8979 2.1204 1.8163 1.8162 -0.0044174 0.3674 2.8969 0.36737 11.685 -15.963 -15.908 -2.9761
Before 2.9494 1.92 1.7846 1.7844 -0.0067284 0.36262 4.1308 0.36256 12.396 -15.999 -15.942 -2.8281
Before 2.5106 1.6774 1.7513 1.7511 -0.0089548 0.32348 3.7691 0.32335 8.8808 -15.79 -15.732 -2.9532
Обучите модели обнаружению аномалии
Набор данных, используемый до сих пор, является только небольшим подмножеством намного большего набора данных, чтобы проиллюстрировать процесс извлечения признаков и выбора. Обучение ваш алгоритм на всех доступных данных дает к лучшей эффективности. С этой целью загрузите 12 функций, которые были ранее извлечены из большего набора данных 17 642 сигналов.
load("FeatureEntire.mat")
head(featureAll)
ans=8×13 table
label ch1_stats/Col1_CrestFactor ch1_stats/Col1_Kurtosis ch1_stats/Col1_RMS ch1_stats/Col1_Std ch2_stats/Col1_Mean ch2_stats/Col1_RMS ch2_stats/Col1_Skewness ch2_stats/Col1_Std ch3_stats/Col1_CrestFactor ch3_stats/Col1_SINAD ch3_stats/Col1_SNR ch3_stats/Col1_THD
______ __________________________ _______________________ __________________ __________________ ___________________ __________________ _______________________ __________________ __________________________ ____________________ __________________ __________________
Before 2.3683 1.927 2.2225 2.2225 -0.015149 0.62512 4.2931 0.62495 5.6569 -5.4476 -4.9977 -4.4608
Before 2.402 1.9206 2.1807 2.1803 -0.018269 0.56773 3.9985 0.56744 8.7481 -12.532 -12.419 -3.2353
Before 2.4157 1.9523 2.1789 2.1788 -0.0063652 0.45646 2.8886 0.45642 8.3111 -12.977 -12.869 -2.9591
Before 2.4595 1.8205 2.14 2.1401 0.0017307 0.41418 2.0635 0.41418 7.2318 -13.566 -13.468 -2.7944
Before 2.2502 1.8609 2.3391 2.339 -0.0081829 0.3694 3.3498 0.36931 6.8134 -13.33 -13.225 -2.7182
Before 2.4211 2.2479 2.1286 2.1285 0.011139 0.36638 1.8602 0.36621 7.4712 -13.324 -13.226 -3.0313
Before 3.3111 4.0304 1.5896 1.5896 -0.0080759 0.47218 2.1132 0.47211 8.2412 -13.85 -13.758 -2.7822
Before 2.2655 2.0656 2.3233 2.3233 -0.0049447 0.37829 2.4936 0.37827 7.6947 -13.781 -13.683 -2.5601
Используйте cvpartition
к данным о разделе в набор обучающих данных и независимый набор тестов. helperExtractLabeledData
функция помощника используется, чтобы найти все функции, соответствующие метке 'After
'в featureTrain
переменная.
rng(0) % set for reproducibility idx = cvpartition(featureAll.label, 'holdout', 0.1); featureTrain = featureAll(idx.training, :); featureTest = featureAll(idx.test, :);
Для каждой модели только обучайтесь на данных после обслуживания, которое принято, чтобы быть нормальным. Извлеките только данные после обслуживания от featureTrain
.
trueAnomaliesTest = featureTest.label;
featureNormal = featureTrain(featureTrain.label=='After', :);
Обнаружьте аномалии с SVM одного класса
Машины опорных векторов являются мощными классификаторами, и их вариант, который обучается только на одном классе, моделирует нормальные данные. Эта модель работает хорошо на идентификацию отклонений, как являющихся "далеким" от нормальных данных. Выберите обучающие данные для нормального состояния и обучите модель SVM с помощью fitcsvm
функция.
mdlSVM = fitcsvm(featureNormal, 'label', 'Standardize', true, 'OutlierFraction', 0);
Подтвердите обученную модель SVM при помощи тестовых данных, которая содержит и нормальные и аномальные данные.
featureTestNoLabels = featureTest(:, 2:end); [~,scoreSVM] = predict(mdlSVM,featureTestNoLabels); isanomalySVM = scoreSVM<0; predSVM = categorical(isanomalySVM, [1, 0], ["Anomaly", "Normal"]); trueAnomaliesTest = renamecats(trueAnomaliesTest,["After","Before"], ["Normal","Anomaly"]); figure; confusionchart(trueAnomaliesTest, predSVM, Title="Anomaly Detection with One-class SVM", Normalization="row-normalized");
Из матрицы беспорядка замечено, что SVM одного класса делает вполне прилично. Только 0,3% аномальных выборок неправильно классифицируется как нормальный, и приблизительно 0,9% нормальных данных неправильно классифицируется как аномальный.
Обнаружьте аномалии с лесом изоляции
Деревья решений леса изоляции изолируют каждое наблюдение в листе. То, сколько решений, до которых передает выборка, добирается до ее листа, является мерой того, как сложный это должно было изолировать его от других. Средняя глубина деревьев для определенной выборки используется в качестве их счета аномалии и возвращается iforest. Еще раз обучите лесную модель изоляции на нормальных данных только.
[mdlIF,~,scoreTrainIF] = iforest(featureNormal{:,2:13},'ContaminationFraction',0.09);
Подтвердите обученную лесную модель изоляции при помощи тестовых данных, которая содержит и нормальные и аномальные данные. Визуализируйте эффективность этой модели при помощи Графика Беспорядка.
[isanomalyIF,scoreTestIF] = isanomaly(mdlIF,featureTestNoLabels.Variables); predIF = categorical(isanomalyIF, [1, 0], ["Anomaly", "Normal"]); figure; confusionchart(trueAnomaliesTest,predIF,Title="Anomaly Detection with Isolation Forest",Normalization="row-normalized");
На этих данных лес изоляции не делает, а также SVM одного класса, но это не ваше типичное обнаружение аномалии с таким количеством "аномалий" в этом почти сбалансированном наборе данных. Причина этого состоит в том, что обучающие данные содержали нормальные данные только, в то время как тестовые данные содержат приблизительно 30%-е аномальные данные. Это указывает, что лесная модель изоляции лучше подходит, когда распределение аномалий и нормальных данных ближе.
Обнаружьте аномалии с сетью автоэнкодера LSTM
Автоэнкодеры являются типом нейронной сети, которые изучают сжатое представление непомеченных данных. Автоэнкодеры LSTM являются вариантом этой сети, которая может изучить сжатое представление данных о последовательности. Этот автоэнкодер LSTM будет обучен только с нормальными данными, и обучивший сеть будет использоваться, чтобы идентифицировать, когда последовательность не будет похожа на нормальные данные, столкнут.
featuresAfter = helperExtractLabeledData(featureTrain, ... "After");
Затем создайте biLSTM сеть автоэнкодера и установите опции обучения.
featureDimension = 1; % Define biLSTM network layers layers = [ sequenceInputLayer(featureDimension, 'Name', 'in') bilstmLayer(16, 'Name', 'bilstm1') reluLayer('Name', 'relu1') bilstmLayer(32, 'Name', 'bilstm2') reluLayer('Name', 'relu2') bilstmLayer(16, 'Name', 'bilstm3') reluLayer('Name', 'relu3') fullyConnectedLayer(featureDimension, 'Name', 'fc') regressionLayer('Name', 'out') ]; % Set Training Options options = trainingOptions('adam', ... 'Plots', 'training-progress', ... 'MiniBatchSize', 500,... 'MaxEpochs',200);
MaxEpochs
параметр опций обучения устанавливается на 200. Для более высокой точности валидации можно установить этот параметр на большее число. Однако эта причина силы сеть, чтобы сверхсоответствовать. Теперь обучите модель.
net = trainNetwork(featuresAfter, featuresAfter, layers, options);
Training on single CPU. |========================================================================================| | Epoch | Iteration | Time Elapsed | Mini-batch | Mini-batch | Base Learning | | | | (hh:mm:ss) | RMSE | Loss | Rate | |========================================================================================| | 1 | 1 | 00:00:04 | 5.81 | 16.9 | 0.0010 | | 3 | 50 | 00:00:16 | 5.43 | 14.8 | 0.0010 | | 5 | 100 | 00:00:28 | 3.99 | 7.9 | 0.0010 | | 8 | 150 | 00:00:39 | 4.27 | 9.1 | 0.0010 | | 10 | 200 | 00:00:51 | 3.48 | 6.1 | 0.0010 | | 13 | 250 | 00:01:03 | 3.97 | 7.9 | 0.0010 | | 15 | 300 | 00:01:15 | 3.17 | 5.0 | 0.0010 | | 18 | 350 | 00:01:27 | 3.72 | 6.9 | 0.0010 | | 20 | 400 | 00:01:39 | 2.89 | 4.2 | 0.0010 | | 23 | 450 | 00:01:51 | 3.49 | 6.1 | 0.0010 | | 25 | 500 | 00:02:03 | 2.67 | 3.6 | 0.0010 | | 28 | 550 | 00:02:15 | 3.31 | 5.5 | 0.0010 | | 30 | 600 | 00:02:27 | 2.49 | 3.1 | 0.0010 | | 33 | 650 | 00:02:39 | 3.14 | 4.9 | 0.0010 | | 35 | 700 | 00:02:51 | 2.29 | 2.6 | 0.0010 | | 38 | 750 | 00:03:03 | 2.96 | 4.4 | 0.0010 | | 40 | 800 | 00:03:16 | 2.12 | 2.2 | 0.0010 | | 43 | 850 | 00:03:28 | 2.81 | 4.0 | 0.0010 | | 45 | 900 | 00:03:40 | 1.98 | 2.0 | 0.0010 | | 48 | 950 | 00:03:51 | 2.71 | 3.7 | 0.0010 | | 50 | 1000 | 00:04:04 | 1.89 | 1.8 | 0.0010 | | 53 | 1050 | 00:04:16 | 2.62 | 3.4 | 0.0010 | | 55 | 1100 | 00:04:28 | 1.81 | 1.6 | 0.0010 | | 58 | 1150 | 00:04:41 | 2.54 | 3.2 | 0.0010 | | 60 | 1200 | 00:04:53 | 1.74 | 1.5 | 0.0010 | | 63 | 1250 | 00:05:06 | 2.47 | 3.0 | 0.0010 | | 65 | 1300 | 00:05:18 | 1.66 | 1.4 | 0.0010 | | 68 | 1350 | 00:05:31 | 2.38 | 2.8 | 0.0010 | | 70 | 1400 | 00:05:43 | 1.53 | 1.2 | 0.0010 | | 73 | 1450 | 00:05:55 | 2.28 | 2.6 | 0.0010 | | 75 | 1500 | 00:06:08 | 1.44 | 1.0 | 0.0010 | | 78 | 1550 | 00:06:20 | 2.20 | 2.4 | 0.0010 | | 80 | 1600 | 00:06:33 | 1.36 | 0.9 | 0.0010 | | 83 | 1650 | 00:06:46 | 2.14 | 2.3 | 0.0010 | | 85 | 1700 | 00:07:00 | 1.29 | 0.8 | 0.0010 | | 88 | 1750 | 00:07:14 | 2.07 | 2.1 | 0.0010 | | 90 | 1800 | 00:07:28 | 1.22 | 0.8 | 0.0010 | | 93 | 1850 | 00:07:42 | 2.01 | 2.0 | 0.0010 | | 95 | 1900 | 00:07:56 | 1.16 | 0.7 | 0.0010 | | 98 | 1950 | 00:08:10 | 1.95 | 1.9 | 0.0010 | | 100 | 2000 | 00:08:24 | 1.10 | 0.6 | 0.0010 | | 103 | 2050 | 00:08:38 | 1.90 | 1.8 | 0.0010 | | 105 | 2100 | 00:08:52 | 1.05 | 0.5 | 0.0010 | | 108 | 2150 | 00:09:06 | 1.86 | 1.7 | 0.0010 | | 110 | 2200 | 00:09:20 | 1.00 | 0.5 | 0.0010 | | 113 | 2250 | 00:09:34 | 1.82 | 1.6 | 0.0010 | | 115 | 2300 | 00:09:48 | 0.96 | 0.5 | 0.0010 | | 118 | 2350 | 00:10:02 | 1.78 | 1.6 | 0.0010 | | 120 | 2400 | 00:10:16 | 0.91 | 0.4 | 0.0010 | | 123 | 2450 | 00:10:30 | 1.74 | 1.5 | 0.0010 | | 125 | 2500 | 00:10:44 | 0.88 | 0.4 | 0.0010 | | 128 | 2550 | 00:10:58 | 1.71 | 1.5 | 0.0010 | | 130 | 2600 | 00:11:12 | 0.84 | 0.4 | 0.0010 | | 133 | 2650 | 00:11:26 | 1.68 | 1.4 | 0.0010 | | 135 | 2700 | 00:11:40 | 0.81 | 0.3 | 0.0010 | | 138 | 2750 | 00:11:54 | 1.65 | 1.4 | 0.0010 | | 140 | 2800 | 00:12:08 | 0.78 | 0.3 | 0.0010 | | 143 | 2850 | 00:12:22 | 1.62 | 1.3 | 0.0010 | | 145 | 2900 | 00:12:36 | 0.76 | 0.3 | 0.0010 | | 148 | 2950 | 00:12:49 | 1.59 | 1.3 | 0.0010 | | 150 | 3000 | 00:13:02 | 0.73 | 0.3 | 0.0010 | | 153 | 3050 | 00:13:15 | 1.57 | 1.2 | 0.0010 | | 155 | 3100 | 00:13:29 | 0.71 | 0.3 | 0.0010 | | 158 | 3150 | 00:13:42 | 1.55 | 1.2 | 0.0010 | | 160 | 3200 | 00:13:55 | 0.68 | 0.2 | 0.0010 | | 163 | 3250 | 00:14:09 | 1.52 | 1.2 | 0.0010 | | 165 | 3300 | 00:14:22 | 0.66 | 0.2 | 0.0010 | | 168 | 3350 | 00:14:36 | 1.50 | 1.1 | 0.0010 | | 170 | 3400 | 00:14:50 | 0.64 | 0.2 | 0.0010 | | 173 | 3450 | 00:15:03 | 1.48 | 1.1 | 0.0010 | | 175 | 3500 | 00:15:16 | 0.62 | 0.2 | 0.0010 | | 178 | 3550 | 00:15:30 | 1.46 | 1.1 | 0.0010 | | 180 | 3600 | 00:15:43 | 0.60 | 0.2 | 0.0010 | | 183 | 3650 | 00:15:56 | 1.44 | 1.0 | 0.0010 | | 185 | 3700 | 00:16:10 | 0.59 | 0.2 | 0.0010 | | 188 | 3750 | 00:16:23 | 1.42 | 1.0 | 0.0010 | | 190 | 3800 | 00:16:37 | 0.57 | 0.2 | 0.0010 | | 193 | 3850 | 00:16:50 | 1.40 | 1.0 | 0.0010 | | 195 | 3900 | 00:17:04 | 0.55 | 0.2 | 0.0010 | | 198 | 3950 | 00:17:17 | 1.38 | 1.0 | 0.0010 | | 200 | 4000 | 00:17:31 | 0.54 | 0.1 | 0.0010 | |========================================================================================| Training finished: Reached final iteration.
Визуализируйте поведение модели и ошибку на данных о валидации
Извлеките и визуализируйте выборку каждый из Аномального и Нормального состояния. Обратите внимание на то, что графики ниже сравнивают ошибку в значениях каждой из 12 функций (обозначенный на оси X). В этой выборке функции 10, 11 и 12 не восстанавливают хорошо для аномального входа и таким образом имеют высокое ошибочное значение. Модель использует это ошибочное значение, чтобы идентифицировать аномалию.
testNormal = {featureTest(1200, 2:end).Variables}; testAnomaly = {featureTest(200, 2:end).Variables}; % Predict decoded signal for both decodedNormal = predict(net,testNormal); decodedAnomaly = predict(net,testAnomaly); % Visualize helperVisualizeModelBehavior(testNormal, testAnomaly, decodedNormal, decodedAnomaly)
Извлеките функции нормальных и аномальных данных. Рисунок показывает, что ошибка реконструкции для аномальных данных значительно выше, чем нормальные данные. Это целесообразно, поскольку автоэнкодер был обучен на нормальных данных, таким образом, это лучше восстановит подобные сигналы.
% Extract data Before maintenance XTestBefore = helperExtractLabeledData(featureTest, "Before"); % Predict output before maintenance and calculate error yHatBefore = predict(net, XTestBefore); errorBefore = helperCalculateError(XTestBefore, yHatBefore); % Extract data after maintenance XTestAfter = helperExtractLabeledData(featureTest, "After"); % Predict output after maintenance and calculate error yHatAfter = predict(net, XTestAfter); errorAfter = helperCalculateError(XTestAfter, yHatAfter); helperVisualizeError(errorBefore, errorAfter);
Идентифицируйте аномалии
Вычислите ошибку на полные данные о валидации.
XTestAll = helperExtractLabeledData(featureTest, "All");
yHatAll = predict(net, XTestAll);
errorAll = helperCalculateError(XTestAll, yHatAll);
Задайте аномалию как точку, которая имеет ошибку реконструкции, в 0.5 раза больше, чем среднее значение через все наблюдения. Этот порог был определен посредством экспериментирования и может быть изменен как требуется.
thresh = 0.5; anomalies = errorAll > thresh*mean(errorAll); helperVisualizeAnomalies(anomalies, errorAll, featureTest);
function E = helperCalculateError(X, Y) % HELPERCALCULATEERROR function calculates the rms error value between the % inputs X, Y E = zeros(length(X),1); for i = 1:length(X) E(i,:) = sqrt(sum((Y{i} - X{i}).^2)); end end function helperVisualizeError(errorBefore, errorAfter) % HELPERVISUALIZEERROR creates a plot to visualize the errors on detecting % before and after conditions figure("Color", "W") tiledlayout("flow") nexttile plot(1:length(errorBefore), errorBefore, 'LineWidth',1.5), grid on title(["Before Maintenance", ... sprintf("Mean Error: %.2f\n", mean(errorBefore))]) xlabel("Observations") ylabel("Reconstruction Error") ylim([0 15]) nexttile plot(1:length(errorAfter), errorAfter, 'LineWidth',1.5), grid on, title(["After Maintenance", ... sprintf("Mean Error: %.2f\n", mean(errorAfter))]) xlabel("Observations") ylabel("Reconstruction Error") ylim([0 15]) end function helperVisualizeAnomalies(anomalies, errorAll, featureTest) % HELPERVISUALIZEANOMALIES creates a plot of the detected anomalies anomalyIdx = find(anomalies); anomalyErr = errorAll(anomalies); predAE = categorical(anomalies, [1, 0], ["Anomaly", "Normal"]); trueAE = renamecats(featureTest.label,["Before","After"],["Anomaly","Normal"]); acc = numel(find(trueAE == predAE))/numel(predAE)*100; figure; t = tiledlayout("flow"); title(t, "Test Accuracy: " + round(mean(acc),2) + "%"); nexttile hold on plot(errorAll) plot(anomalyIdx, anomalyErr, 'x') hold off ylabel("Reconstruction Error") xlabel("Observation") legend("Error", "Candidate Anomaly") nexttile confusionchart(trueAE,predAE) end function helperVisualizeModelBehavior(normalData, abnormalData, decodedNorm, decodedAbNorm) %HELPERVISUALIZEMODELBEHAVIOR Visualize model behavior on sample validation data figure("Color", "W") tiledlayout("flow") nexttile() hold on colororder('default') yyaxis left plot(normalData{:}) plot(decodedNorm{:},":","LineWidth",1.5) hold off title("Normal Input") grid on ylabel("Feature Value") yyaxis right stem(abs(normalData{:} - decodedNorm{:})) ylim([0 2]) ylabel("Error") legend(["Input", "Decoded","Error"],"Location","southwest") nexttile() hold on yyaxis left plot(abnormalData{:}) plot(decodedAbNorm{:},":","LineWidth",1.5) hold off title("Abnormal Input") grid on ylabel("Feature Value") yyaxis right stem(abs(abnormalData{:} - decodedAbNorm{:})) ylim([0 2]) ylabel("Error") legend(["Input", "Decoded","Error"],"Location","southwest") end function X = helperExtractLabeledData(featureTable, label) %HELPEREXTRACTLABELEDDATA Extract data from before or after operating %conditions and re-format to support input to autoencoder network % Select data with label After if label == "All" Xtemp = featureTable(:, 2:end).Variables; else tF = featureTable.label == label; Xtemp = featureTable(tF, 2:end).Variables; end % Arrange data into cells X = cell(length(Xtemp),1); for i = 1:length(Xtemp) X{i,:} = Xtemp(i,:); end end