Compress Machine Fault Recognition Neural Network Using Projection
In this example, you compress a pretrained acoustics-based machine fault recognition neural network using projection and principal component analysis. Then, you generate C++ code from the compressed neural network.
To learn how the deep learning model was trained, see Acoustics-Based Machine Fault Recognition.
For a detailed example on deploying the machine fault recognition system to a hardware target, refer to Acoustics-Based Machine Fault Recognition Code Generation on Raspberry Pi.
Prerequisites
MATLAB Coder Interface for Deep Learning Libraries
For supported versions of libraries and for information about setting up environment variables, see Prerequisites for Deep Learning with MATLAB Coder (MATLAB Coder).
Data Preparation
Download and unzip the air compressor data set [1]. This data set consists of recordings from air compressors in a healthy state or one of seven faulty states.
downloadFolder = matlab.internal.examples.downloadSupportFile("audio","AirCompressorDataset/AirCompressorDataset.zip"); dataFolder = tempdir; unzip(downloadFolder,dataFolder) dataset = fullfile(dataFolder,"AirCompressorDataset");
Create Training and Validation Datastores
Create an audioDatastore
object to manage the data and split it into training and validation sets.
ads = audioDatastore(dataset,IncludeSubfolders=true);
The data labels are encoded in their containing folder name. To split the data into train and test sets, use folders2labels
and splitlabels
.
lbls = folders2labels(ads.Files); idxs = splitlabels(lbls,0.9); adsTrain = subset(ads,idxs{1}); labelsTrain = lbls(idxs{1}); adsValidation = subset(ads,idxs{2}); labelsValidation = lbls(idxs{2});
Call countlabels
to inspect the distribution of labels in the train and validation sets.
countlabels(labelsTrain)
ans=8×3 table
Label Count Percent
_________ _____ _______
Bearing 203 12.5
Flywheel 203 12.5
Healthy 203 12.5
LIV 203 12.5
LOV 203 12.5
NRV 203 12.5
Piston 203 12.5
Riderbelt 203 12.5
countlabels(labelsValidation)
ans=8×3 table
Label Count Percent
_________ _____ _______
Bearing 22 12.5
Flywheel 22 12.5
Healthy 22 12.5
LIV 22 12.5
LOV 22 12.5
NRV 22 12.5
Piston 22 12.5
Riderbelt 22 12.5
Extract Training and Validation Features
Extract a set of acoustic features used as inputs to the network. The extraction process is identical to the approach in Acoustics-Based Machine Fault Recognition.
windowLength = 512; overlapLength = 0; [~,info] = read(adsTrain); fs = info.SampleRate; aFE = audioFeatureExtractor(SampleRate=fs, ... Window=hamming(windowLength,"periodic"),... OverlapLength=overlapLength,... spectralCentroid=true, ... spectralCrest=true, ... spectralDecrease=true, ... spectralEntropy=true, ... spectralFlatness=true, ... spectralFlux=false, ... spectralKurtosis=true, ... spectralRolloffPoint=true, ... spectralSkewness=true, ... spectralSlope=true, ... spectralSpread=true);
Extract training features.
reset(adsTrain) trainFeatures = cell(1,numel(adsTrain.Files)); for index = 1:numel(adsTrain.Files) data = read(adsTrain); trainFeatures{index} = (extract(aFE,data))'; end
Similarly, extract validation features.
validationFeatures = cell(1,numel(adsValidation.Files)); for index = 1:numel(adsValidation.Files) data = read(adsValidation); validationFeatures{index} = (extract(aFE,data))'; end
Load Pretrained Network
Load the pretrained network. To learn how the deep learning model was trained, see Acoustics-Based Machine Fault Recognition.
load airCompressorNet
Display the network layers.
netOriginal.Layers
ans = 6×1 Layer array with layers: 1 'sequenceinput' Sequence Input Sequence input with 10 dimensions 2 'lstm_1' LSTM LSTM with 100 hidden units 3 'dropout' Dropout 20% dropout 4 'lstm_2' LSTM LSTM with 100 hidden units 5 'fc' Fully Connected 8 fully connected layer 6 'softmax' Softmax softmax
Analyze Neuron Activations for Compression Using Projection
You use principal component analysis (PCA) to identify the subspace of learnable parameters that result in the highest variance in neuron activations by analyzing the network activations using the training data set. This analysis requires only the predictors of the training data to compute the network activations. It does not require the training targets.
First, create a minibatchqueue
object.
Specify a mini-batch size of 16.
Specify that the output data has format
"CTB"
(channel, time, batch).
miniBatchSize = 16; mbqTrain = minibatchqueue(... arrayDatastore(trainFeatures.',OutputType="same",ReadSize=miniBatchSize),... MiniBatchSize=miniBatchSize ,... MiniBatchFormat="CTB",... MiniBatchFcn=@(X)cat(3,X{:}));
Create a neuronPCA
object. To view information about the steps of the neuron PCA algorithm, set the VerbosityLevel
option to "steps"
.
npca = neuronPCA(netOriginal,mbqTrain,VerbosityLevel="steps");
Using solver mode "direct". Computing covariance matrices for activations connected to: "lstm_1/in","lstm_1/out","lstm_2/in","lstm_2/out","fc/in","fc/out" Computing eigenvalues and eigenvectors for activations connected to: "lstm_1/in","lstm_1/out","lstm_2/in","lstm_2/out","fc/in","fc/out" neuronPCA analyzed 3 layers: "lstm_1","lstm_2","fc"
View the properties of the neuronPCA
object.
npca
npca = neuronPCA with properties: LayerNames: ["lstm_1" "lstm_2" "fc"] ExplainedVarianceRange: [0 1] LearnablesReductionRange: [0 0.9775] InputEigenvalues: {[10×1 double] [100×1 double] [100×1 double]} InputEigenvectors: {[10×10 double] [100×100 double] [100×100 double]} OutputEigenvalues: {[100×1 double] [100×1 double] [8×1 double]} OutputEigenvectors: {[100×100 double] [100×100 double] [8×8 double]}
Project Network
If you want to compress a network so that it meets specific hardware memory requirements, then you can manually calculate the reduction value such that the compressed network is of the desired size.
Specify a target memory requirement of 256 kilobytes (256×1024 bytes).
targetMemorySize = 256*1024
targetMemorySize = 262144
Calculate the memory size of the original network (in bytes) using the parameterMemory
helper function.
memorySizeOriginal = parameterMemory(netOriginal)
memorySizeOriginal = 502432
Calculate the factor to reduce the learnable parameters by such that the resulting network meets the memory requirements.
reductionGoal = 1 - (targetMemorySize/memorySizeOriginal);
Project the network using the compressNetworkUsingProjection
function and set the LearnablesReductionGoal
option to the calculated reduction factor.
netProjected = compressNetworkUsingProjection(netOriginal,npca, ...
LearnablesReductionGoal=reductionGoal);
Compressed network has 48.0% fewer learnable parameters. Projection compressed 3 layers: "lstm_1","lstm_2","fc"
Calculate the memory size of the projected network using the parameterMemory
function. Notice that the value is very close to the target memory size.
memorySizeProjected = parameterMemory(netProjected)
memorySizeProjected = 261328
Test Projected Network
Get the expected validation set labels.
validationLabels = folders2labels(adsValidation.Files); classNames = unique(validationLabels);
Create a mini-batch queue using the same steps as the training data.
mbqValidation = minibatchqueue(... arrayDatastore(validationFeatures.',OutputType="same",ReadSize=miniBatchSize),... MiniBatchSize=miniBatchSize ,... MiniBatchFormat="CTB",... MiniBatchFcn=@(X)cat(3,X{:}));
For comparison, calculate the classification accuracy of the original network using the test data and the modelPredictions
function.
YTest = modelPredictions(netOriginal,mbqValidation,string(classNames)); TTest = validationLabels; accOriginal = mean(YTest == TTest)
accOriginal = 0.8807
View the confusion chart for the validation data.
figure confusionchart(YTest,TTest, ... Title="Accuracy: " + accOriginal*100 + " (%)");
Calculate the classification accuracy of the projected network using the test data and the modelPredictions
function.
YTest = modelPredictions(netProjected,mbqValidation,string(classNames)); accProjected = mean(YTest == TTest)
accProjected = 0.8409
View the confusion matrix of the projected network.
figure confusionchart(YTest,TTest, ... Title="Accuracy: " + accProjected*100 + " (%)");
Compare the accuracy and the memory size of each network in a bar chart. Notice that memory size has been significantly reduced, with a relatively slight reduction in accuracy.
figure tiledlayout("flow") nexttile bar([accOriginal accProjected]) xticklabels(["Original" "Projected"]) ylabel("Accuracy (%)") title("Accuracy") nexttile bar([memorySizeOriginal memorySizeProjected]) xticklabels(["Original" "Projected"]) yline(targetMemorySize,"r--","Memory Requirement") ylabel("Memory (bytes)") title("Memory Size")
Fine Tune Projected Network
Compressing a network using projection typically reduces the network accuracy. You can improve the accuracy by retraining the network (also known as fine tuning the network).
To define hyperparameters for the network, use trainingOptions
.
miniBatchSize = 128; trainLabels = folders2labels(adsTrain.Files); validationFrequency = floor(numel(trainFeatures)/miniBatchSize); options = trainingOptions("adam", ... MiniBatchSize=miniBatchSize, ... MaxEpochs=35, ... Plots="training-progress", ... Verbose=false, ... Shuffle="every-epoch", ... LearnRateSchedule="piecewise", ... LearnRateDropPeriod=30, ... LearnRateDropFactor=0.1, ... ValidationData={validationFeatures,validationLabels}, ... ValidationFrequency=validationFrequency,... InputDataFormats = "CTB")
options = TrainingOptionsADAM with properties: GradientDecayFactor: 0.9000 SquaredGradientDecayFactor: 0.9990 Epsilon: 1.0000e-08 InitialLearnRate: 1.0000e-03 MaxEpochs: 35 LearnRateSchedule: 'piecewise' LearnRateDropFactor: 0.1000 LearnRateDropPeriod: 30 MiniBatchSize: 128 Shuffle: 'every-epoch' WorkerLoad: [] CheckpointFrequency: 1 CheckpointFrequencyUnit: 'epoch' SequenceLength: 'longest' DispatchInBackground: 0 L2Regularization: 1.0000e-04 GradientThresholdMethod: 'l2norm' GradientThreshold: Inf Verbose: 0 VerboseFrequency: 50 ValidationData: {{1×176 cell} [176×1 categorical]} ValidationFrequency: 12 ValidationPatience: Inf CheckpointPath: '' ExecutionEnvironment: 'auto' OutputFcn: [] Metrics: [] Plots: 'training-progress' SequencePaddingValue: 0 SequencePaddingDirection: 'right' InputDataFormats: {'CTB'} TargetDataFormats: "auto" ResetInputNormalization: 1 BatchNormalizationStatistics: 'auto' OutputNetwork: 'last-iteration'
To train the network, use trainnet
.
fineTunedNet = trainnet(trainFeatures,trainLabels,netProjected,"crossentropy",options);
Test Fine-Tuned Network
Calculate the classification accuracy of the fine-tuned network.
YTest = modelPredictions(fineTunedNet,mbqValidation,string(classNames)); accProjected = mean(YTest == TTest)
accProjected = 0.8864
View the confusion matrix of the fine-tuned network.
figure confusionchart(YTest,TTest, ... Title="Accuracy: " + accProjected*100 + " (%)");
Generate C++ Code from Fine-Tuned Network
You can generate C++ code from a machine fault recognition system that leverages the fine-tuned, compressed network.
The fault recognition system is comprised of two parts:
Feature extraction.
Network inference.
Create MATLAB Function Compatible with C/C++ Code Generation
Use the generateMATLABFunction
method of audioFeatureExtractor
to generate a MATLAB function compatible with C/C++ code generation. Specify IsStreaming
as true
so that the generated function is optimized for stream processing.
filename = fullfile(pwd,"extractAudioFeatures");
generateMATLABFunction(aFE,filename,IsStreaming=true);
Combine Streaming Feature Extraction and Classification
When you compress a network using the compressNetworkUsingProjection
function, the software replaces layers that support projection with ProjectedLayer
objects that contain the equivalent neural network. To replace ProjectedLayer
objects with the corresponding neural networks, use the unpackProjectedLayers
function.
fineTunedNet = unpackProjectedLayers(fineTunedNet);
Save the fine-tuned network to a MAT file.
save fineTunedNet.mat fineTunedNet
Create a function that combines the feature extraction and deep learning classification.
type detectAirCompressorFault.m
function scores = detectAirCompressorFault(audioIn) % DETECTAIRCOMPRESSORFAULT This is a streaming classifier function persistent airCompNet if isempty(airCompNet) airCompNet = coder.loadDeepLearningNetwork('fineTunedNet.mat'); end % Extract features using function features = extractAudioFeatures(audioIn); % Classify scores = predict(airCompNet,dlarray(features,'CTB')); end
Generate C++ Code
Create a code generation configuration object to generate an executable program. Specify the target language as C++.
cfg = coder.config("mex"); cfg.TargetLang = "C++";
Create a configuration object for deep learning code generation.
dlcfg = coder.DeepLearningConfig(TargetLibrary="none");
cfg.DeepLearningConfig = dlcfg;
Call the codegen
(MATLAB Coder) function from MATLAB Coder to generate C++ code. Set the input audio frame length to 512 samples.
audioFrame = ones(512,1,"single"); codegen -config cfg detectAirCompressorFault -args {audioFrame} -report
Code generation successful: View report
For a more detailed example on deploying the machine fault recognition system to a hardware target, refer to Acoustics-Based Machine Fault Recognition Code Generation on Raspberry Pi.
Supporting Functions
function Y = modelPredictions(net,mbq,classNames) Y = []; reset(mbq) while hasdata(mbq) X = next(mbq); scores = predict(net,X); labels = onehotdecode(scores,classNames,1)'; Y = [Y; labels];%#ok end end function N = numLearnables(net) N = 0; for i = 1:size(net.Learnables,1) N = N + numel(net.Learnables.Value{i}); end end function numBytes = parameterMemory(net) numBytes = 4*numLearnables(net); end
References
[1] Verma, Nishchal K., et al. "Intelligent Condition Based Monitoring Using Acoustic Signals for Air Compressors." IEEE Transactions on Reliability, vol. 65, no. 1, Mar. 2016, pp. 291–309. DOI.org (Crossref), doi:10.1109/TR.2015.2459684.