Main Content

Perform Feature Selection and Model Training for Transmission System Fault Detection

R2026b
Since R2026b

This example shows how to train two classifiers: one for detecting sensor drift and one for detecting shaft wear in an automotive transmission system. In order to reduce the memory footprint of the two models, use feature selection to retain only the most important features, and tune hyperparameters with model size constraints. Verify that the final models satisfy the requirements outlined in Define Requirements for Transmission System Fault Detection.

Load Partitioned Tabular Data

Load the data variables in PartitionedFaultData.mat. The data set consists of features extracted from signal data, reformatted into tabular data, and partitioned into training and test sets. For more information, see Extract Features and Partition Data for Transmission System Fault Detection.

You do not need to load the data if you followed the previous step and have the faultDataTrain, faultDataTest, SensorDriftTrain, SensorDriftTest, ShaftWearTrain, and ShaftWearTest variables in your workspace already.

if ~exist("faultDataTrain","var")
    load("PartitionedFaultData.mat","faultDataTrain");
end
if ~exist("faultDataTest","var")
    load("PartitionedFaultData.mat","faultDataTest");
end
if ~exist("SensorDriftTrain","var")
    load("PartitionedFaultData.mat","SensorDriftTrain");
end
if ~exist("SensorDriftTest","var")
    load("PartitionedFaultData.mat","SensorDriftTest");
end
if ~exist("ShaftWearTrain","var")
    load("PartitionedFaultData.mat","ShaftWearTrain");
end
if ~exist("ShaftWearTest","var")
    load("PartitionedFaultData.mat","ShaftWearTest");
end

The faultDataTrain and faultDataTest data sets contain the features that can be used as predictors when fitting the fault detection classifiers. Remove the ScenarioID feature from the data sets because it includes response variable information.

excludeFeatures = "ScenarioID";
excludeFeatures = intersect(excludeFeatures,faultDataTrain.Properties.VariableNames);
faultDataTrain(:,excludeFeatures) = [];
faultDataTest(:,excludeFeatures) = [];

Display the number of observations and features in the predictor data sets.

numTrainingObservations = height(faultDataTrain)
numTrainingObservations = 
1287
numTestObservations = height(faultDataTest)
numTestObservations = 
551
numFeatures = width(faultDataTrain)
numFeatures = 
19

Detect Sensor Drift Fault

To detect sensor drift faults, train a decision tree classifier. Decision trees are adept at modeling the threshold-based separations produced by sensor drift fault signatures. Additionally, decision trees are highly interpretable and have relatively small deployment footprints.

Train Decision Tree with Default Hyperparameters

First, train a decision tree classifier using the default hyperparameter values. Specify faultDataTrain as the predictor data and SensorDriftTrain as the response variable.

defaultSDClassifier = fitctree(faultDataTrain,SensorDriftTrain);

Compute the 5-fold cross-validation accuracy, and display the confusion matrix.

rng(0,"twister") % For reproducibility
cvDefaultSDClassifier = crossval(defaultSDClassifier,KFold=5);
sprintf("Accuracy: %f",1 - kfoldLoss(cvDefaultSDClassifier))
ans = 
"Accuracy: 0.998446"
cvPred = kfoldPredict(cvDefaultSDClassifier);
confusionchart(SensorDriftTrain,cvPred)

Figure contains an object of type ConfusionMatrixChart.

The classifier cvDefaultSDClassifier returns near-perfect predictions on the validation folds, with few false positives.

Train Decision Tree Using Most Important Features

To reduce unnecessary feature extraction computations during deployment, limit the features used to train the classifier to those with high predictive power.

Determine the importance of the features used to train defaultSDClassifier by using permutationImportance. The function computes the importance of each feature in the model by permuting the values in the feature and comparing the model resubstitution loss with the original feature to the loss with the permuted feature. A large increase in the model loss with the permuted feature indicates that the feature is important.

rng(0,"twister") % For reproducibility
SDFeatureImportance = permutationImportance(defaultSDClassifier);
SDFeatureImportanceSorted = sortrows(SDFeatureImportance, ...
    ImportanceMean="descend")
SDFeatureImportanceSorted = 19×3 table
        Predictor         ImportanceMean    ImportanceStandardDeviation
    __________________    ______________    ___________________________

    "SigPeak"                 0.24709                0.0028372         
    "SigCrestFactor"          0.24468                0.0058771         
    "SigRMS"                 0.018881               0.00082312         
    "SigMean"               0.0079254                0.0028794         
    "SigMedian"                     0                        0         
    "SigVar"                        0                        0         
    "SigPeak2Peak"                  0                        0         
    "SigSkewness"                   0                        0         
    "SigKurtosis"                   0                        0         
    "SigMAD"                        0                        0         
    "SigRangeCumSum"                0                        0         
    "SigApproxEntropy"              0                        0         
    "SigLyapExponent"               0                        0         
    "PeakFreq"                      0                        0         
    "PeakSpecKurtosis"              0                        0         
    "RPMMedian"                     0                        0         
      ⋮

Select the top two most important features, and then retrain the decision tree classifier using only those features.

SDFeatureSubset = SDFeatureImportanceSorted.Predictor(1:2);
smallSDClassifier = fitctree(faultDataTrain(:,SDFeatureSubset), ...
    SensorDriftTrain);

Compute the 5-fold cross-validation accuracy.

rng(0,"twister") % For reproducibility
cvSmallSDClassifier = crossval(smallSDClassifier,KFold=5);
sprintf("Accuracy: %f",1 - kfoldLoss(cvSmallSDClassifier))
ans = 
"Accuracy: 0.991453"

The cross-validation accuracy remains high, even after training the model on only the two most important features.

Optimize and Train Decision Tree with Size Constraints

You can perform Bayesian optimization to find hyperparameter values that maximize the accuracy of your model while respecting model size constraints. This process is particularly useful for embedded deployment where RAM is limited. In this example, ensure the final model can be compiled to C/C++ code within a 10KB memory budget.

As before, use only the two most important features to train the model. Specify to optimize all hyperparameters, and in the optimization options, set AcquisitionFunctionName to "expected-improvement-plus" for reproducible results.

hpoOptions = hyperparameterOptimizationOptions(ConstraintTarget="coder", ...
    ConstraintType="size",ConstraintBounds=[0 10000],ShowPlots=false, ...
    AcquisitionFunctionName="expected-improvement-plus");
rng(0,"twister");
optimizedSDClassifier = fitctree(faultDataTrain(:,SDFeatureSubset), ...
    SensorDriftTrain,OptimizeHyperparameters="all", ...
    hyperparameterOptimizationOptions=hpoOptions);
|==================================================================================================================================================|
|                                                                                                                                                  |
|Objective         : "kfoldLoss"                                                                                                                   |
|Constraint        : "LearnerForCoderSize (bytes)"                                                                                                 |
|Constraint Bounds : [0 10000]                                                                                                                     |
|                                                                                                                                                  |
|==================================================================================================================================================|
| Iter | Eval   | Objective   | Objective   | BestSoFar   | BestSoFar   | Constraint1  |  MinLeafSize | MaxNumSplits | SplitCriteri-| NumVariables-|
|      | result |             | runtime     | (observed)  | (estim.)    | violation    |              |              | on           | ToSample     |
|==================================================================================================================================================|
|    1 | Best   |    0.006216 |     0.56706 |    0.006216 |    0.006216 |    -8.84e+03 |            1 |           20 |     deviance |            2 |
|    2 | Accept |    0.031857 |     0.20254 |    0.006216 |    0.008056 |    -9.42e+03 |          343 |           13 |          gdi |            2 |
|    3 | Accept |    0.031857 |     0.15797 |    0.006216 |   0.0062177 |    -9.42e+03 |           47 |            2 |          gdi |            2 |
|    4 | Accept |    0.031857 |    0.042647 |    0.006216 |   0.0062186 |    -9.42e+03 |          338 |          268 |     deviance |            2 |
|    5 | Accept |    0.010878 |     0.11402 |    0.006216 |   0.0062222 |    -8.84e+03 |            3 |          253 |     deviance |            2 |
|    6 | Accept |    0.006216 |    0.034239 |    0.006216 |   0.0062193 |    -8.84e+03 |            1 |          256 |     deviance |            2 |
|    7 | Accept |    0.006216 |    0.030298 |    0.006216 |   0.0062182 |    -8.84e+03 |            1 |           31 |     deviance |            2 |
|    8 | Accept |    0.031857 |    0.027882 |    0.006216 |    0.005868 |    -9.42e+03 |            1 |            1 |     deviance |            2 |
|    9 | Accept |    0.006216 |    0.029721 |    0.006216 |    0.006196 |    -8.84e+03 |            1 |           91 |     deviance |            2 |
|   10 | Accept |    0.006216 |    0.030751 |    0.006216 |   0.0060559 |    -8.84e+03 |            1 |         1268 |     deviance |            2 |
|   11 | Accept |    0.008547 |    0.029764 |    0.006216 |   0.0060902 |    -8.26e+03 |            1 |         1243 |          gdi |            2 |
|   12 | Accept |    0.027195 |    0.031379 |    0.006216 |   0.0060656 |    -9.42e+03 |           29 |         1239 |          gdi |            2 |
|   13 | Accept |    0.008547 |     0.02929 |    0.006216 |   0.0060268 |    -8.26e+03 |            1 |          129 |          gdi |            2 |
|   14 | Accept |    0.027972 |    0.027859 |    0.006216 |   0.0060625 |    -9.19e+03 |            1 |            5 |          gdi |            2 |
|   15 | Accept |    0.008547 |    0.029636 |    0.006216 |   0.0060381 |    -8.26e+03 |            1 |          387 |          gdi |            2 |
|   16 | Accept |    0.006216 |    0.029278 |    0.006216 |   0.0061714 |    -8.84e+03 |            1 |          631 |     deviance |            2 |
|   17 | Accept |    0.010878 |    0.031407 |    0.006216 |   0.0061259 |    -8.84e+03 |            3 |           20 |     deviance |            2 |
|   18 | Accept |    0.006216 |    0.034133 |    0.006216 |   0.0061927 |    -8.84e+03 |            1 |           32 |     deviance |            2 |
|   19 | Accept |    0.006216 |     0.02851 |    0.006216 |   0.0061899 |    -8.84e+03 |            1 |          154 |     deviance |            2 |
|   20 | Accept |       0.446 |    0.039496 |    0.006216 |   0.0060544 |    -9.42e+03 |          635 |            1 |     deviance |            2 |
|==================================================================================================================================================|
| Iter | Eval   | Objective   | Objective   | BestSoFar   | BestSoFar   | Constraint1  |  MinLeafSize | MaxNumSplits | SplitCriteri-| NumVariables-|
|      | result |             | runtime     | (observed)  | (estim.)    | violation    |              |              | on           | ToSample     |
|==================================================================================================================================================|
|   21 | Accept |    0.031857 |    0.028513 |    0.006216 |   0.0060882 |    -9.42e+03 |           85 |         1285 |     deviance |            2 |
|   22 | Accept |       0.446 |    0.026512 |    0.006216 |   0.0056108 |    -9.42e+03 |          626 |         1279 |     deviance |            2 |
|   23 | Accept |    0.031857 |    0.030755 |    0.006216 |   0.0056201 |    -9.42e+03 |           37 |          150 |          gdi |            2 |
|   24 | Accept |    0.031857 |    0.028463 |    0.006216 |   0.0056315 |    -9.42e+03 |          243 |          451 |     deviance |            2 |
|   25 | Accept |    0.009324 |    0.034464 |    0.006216 |   0.0056413 |    -8.84e+03 |            2 |         1279 |     deviance |            2 |
|   26 | Accept |    0.012432 |    0.029629 |    0.006216 |   0.0056515 |    -8.95e+03 |            5 |           19 |     deviance |            2 |
|   27 | Accept |    0.017094 |    0.039298 |    0.006216 |   0.0056604 |    -9.07e+03 |            8 |         1233 |     deviance |            2 |
|   28 | Accept |    0.031857 |     0.02854 |    0.006216 |   0.0056687 |    -9.42e+03 |           13 |            1 |     deviance |            2 |
|   29 | Accept |    0.031857 |    0.031017 |    0.006216 |    0.005678 |    -9.07e+03 |           46 |          120 |     deviance |            2 |
|   30 | Accept |    0.019425 |    0.033806 |    0.006216 |   0.0056853 |    -9.07e+03 |           24 |          797 |     deviance |            2 |

__________________________________________________________
Optimization completed.
MaxObjectiveEvaluations of 30 reached.
Total function evaluations: 30
Total elapsed time: 6.6199 seconds
Total objective function evaluation time: 1.8589

Best observed feasible point:
    MinLeafSize    MaxNumSplits    SplitCriterion    NumVariablesToSample
    ___________    ____________    ______________    ____________________

         1              20            deviance                2          

Observed objective function value = 0.006216
Estimated objective function value = 0.0072122
Function evaluation time = 0.56706
Observed constraint violations =[ -8838.500000 ]

Best estimated feasible point (according to models):
    MinLeafSize    MaxNumSplits    SplitCriterion    NumVariablesToSample
    ___________    ____________    ______________    ____________________

         1              91            deviance                2          

Estimated objective function value = 0.0056853
Estimated function evaluation time = 0.040334
Estimated constraint violations =[ -8838.925420 ]
optimizedSDClassifier = optimizedSDClassifier{1};

Compute the 5-fold cross-validation accuracy.

rng(0,"twister") % For reproducibility
cvOptimizedSDClassifier = crossval(optimizedSDClassifier,KFold=5);
sprintf("Accuracy: %f",1 - kfoldLoss(cvOptimizedSDClassifier))
ans = 
"Accuracy: 0.993784"

The optimized model retains a high cross-validation accuracy.

Compare Classifier Performance and Select Best Model

Compare the performance of the three sensor drift classifiers on the test data (faultDataTest and SensorDriftTest). Use the helper function helperComputeMetrics to compute the accuracy, true positive rate, false positive rate, false positives, false negatives, and code generation model size for each model.

SDClassifiers = {defaultSDClassifier,smallSDClassifier, ...
    optimizedSDClassifier};
ModelNames = ["Default model","Model with selected features", ...
    "Optimized model with selected features"];
SDClassifiersMetricsTable = helperComputeMetrics(SDClassifiers, ...
    faultDataTest,SensorDriftTest,ModelNames)
SDClassifiersMetricsTable = 3×6 table
                                              Accuracy    True Positive Rate    False Positive Rate    False Positives    False Negatives    Model Size (KBs)
                                              ________    __________________    ___________________    _______________    _______________    ________________

    Default model                             0.99636          0.99184                      0                 0                  2                3.549      
    Model with selected features              0.99819                1               0.003268                 1                  0                1.742      
    Optimized model with selected features    0.99819                1               0.003268                 1                  0                1.162      

The metrics table indicates that all three decision trees perform well on the test data. However, the models vary in the estimated amount of system memory they use in generated code.

Select optimizedSDClassifier as the sensor drift detector in the transmission system due to its minimal deployment footprint.

SDClassifier = optimizedSDClassifier;

Detect Shaft Wear Fault

To detect shaft wear faults, train an ensemble of trees. Unlike sensor drift, shaft wear is not well separated by simple threshold splits on individual features. For this reason, a decision tree is likely not sufficient for detecting shaft wear faults, and an ensemble of trees is a better classifier choice.

Train Ensemble with Default Hyperparameters

Train a tree-based ensemble using the default hyperparameter values. By default, fitcensemble uses the LogitBoost aggregate method with 100 tree learners.

rng(0,"twister") % For reproducibility
defaultSWClassifier = fitcensemble(faultDataTrain,ShaftWearTrain);

Compute the 5-fold cross-validation accuracy.

cvDefaultSWClassifier = crossval(defaultSWClassifier,KFold=5);
sprintf("Accuracy: %f",1 - kfoldLoss(cvDefaultSWClassifier))
ans = 
"Accuracy: 0.909868"

The classifier cvDefaultSWClassifier produces a cross-validation accuracy just over 90%.

Train Ensemble Using Most Important Features

Determine the importance of the features used to train defaultSWClassifier by using permutationImportance.

rng(0,"twister") % For reproducibility
SWFeatureImportance = permutationImportance(defaultSWClassifier);
SWFeatureImportanceSorted = sortrows(SWFeatureImportance, ...
    ImportanceMean="descend")
SWFeatureImportanceSorted = 19×3 table
        Predictor         ImportanceMean    ImportanceStandardDeviation
    __________________    ______________    ___________________________

    "RPMStd"                  0.12455                0.0061296         
    "RPMIQR"                  0.11927                0.0060547         
    "RPMMedian"              0.070396                0.0036481         
    "SigLyapExponent"        0.029992                0.0020459         
    "SigPeak2Peak"          0.0034188               0.00083525         
    "PeakSpecKurtosis"      0.0022533                0.0013433         
    "SigSkewness"           0.0004662               0.00054328         
    "SigMean"               0.0003885               0.00040952         
    "SigApproxEntropy"      0.0001554               0.00032761         
    "SigVar"                0.0001554               0.00032761         
    "PeakFreq"              0.0001554               0.00032761         
    "SigKurtosis"            7.77e-05               0.00024571         
    "SigMedian"                     0                        0         
    "SigRMS"                        0                        0         
    "SigPeak"                       0                        0         
    "SigCrestFactor"                0                        0         
      ⋮

Select the top two most important features, and then retrain the ensemble using only those features.

SWFeatureSubset = SWFeatureImportanceSorted.Predictor(1:2);
rng(0,"twister") % For reproducibility
smallSWClassifier = fitcensemble(faultDataTrain(:,SWFeatureSubset), ...
    ShaftWearTrain);

Compute the 5-fold cross-validation accuracy.

cvSmallSWClassifier = crossval(smallSWClassifier,KFold=5);
sprintf("Accuracy: %f",1 - kfoldLoss(cvSmallSWClassifier))
ans = 
"Accuracy: 0.929183"

After training the model on only the two most important features, the cross-validation accuracy has improved to be above the 92% threshold.

Optimize and Train Ensemble with Size Constraints

Perform Bayesian optimization to find hyperparameter values that maximize model accuracy while ensuring the final model can be compiled to C/C++ code within a 500KB memory budget.

Use only the two most important features to train the model, and specify to optimize all hyperparameters. In the optimization options, set AcquisitionFunctionName to "expected-improvement-plus" for reproducible results and increase the maximum number of iterations to 50 for better results.

hpoOptions = hyperparameterOptimizationOptions(ConstraintTarget="coder", ...
    ConstraintType="size",ConstraintBounds=[0 500000],ShowPlots=false, ...
    AcquisitionFunctionName="expected-improvement-plus", ...
    MaxObjectiveEvaluations=50);
rng(0,"twister");
optimizedSWClassifier = fitcensemble(faultDataTrain(:,SWFeatureSubset), ...
    ShaftWearTrain,OptimizeHyperparameters="all", ...
    hyperparameterOptimizationOptions=hpoOptions);
|===============================================================================================================================================================================================|
|                                                                                                                                                                                               |
|Objective         : "kfoldLoss"                                                                                                                                                                |
|Constraint        : "LearnerForCoderSize (bytes)"                                                                                                                                              |
|Constraint Bounds : [0 500000]                                                                                                                                                                 |
|                                                                                                                                                                                               |
|===============================================================================================================================================================================================|
| Iter | Eval   | Objective   | Objective   | BestSoFar   | BestSoFar   | Constraint1  |       Method | NumLearningC-|    LearnRate |  MinLeafSize | MaxNumSplits | SplitCriteri-| NumVariables-|
|      | result |             | runtime     | (observed)  | (estim.)    | violation    |              | ycles        |              |              |              | on           | ToSample     |
|===============================================================================================================================================================================================|
|    1 | Best   |     0.34241 |      3.7443 |     0.34241 |     0.34241 |    -3.79e+05 |     RUSBoost |          239 |      0.18949 |          257 |           27 |     deviance |            - |
|    2 | Best   |     0.22724 |       1.651 |     0.22724 |     0.23182 |    -2.85e+05 |          Bag |          101 |            - |           47 |          218 |     deviance |            1 |
|    3 | Best   |     0.10739 |     0.47366 |     0.10739 |      0.1146 |    -4.79e+05 |  GentleBoost |           42 |    0.0062456 |           17 |            3 |            - |            - |
|    4 | Accept |      0.3214 |     0.55099 |     0.10739 |     0.11699 |    -4.66e+05 |  GentleBoost |          103 |      0.62239 |          408 |         1099 |            - |            - |
|    5 | Accept |     0.14864 |     0.31359 |     0.10739 |     0.11651 |    -4.89e+05 |  GentleBoost |           21 |    0.0053541 |           11 |            3 |            - |            - |
|    6 | Best   |    0.074708 |      1.1135 |    0.074708 |    0.074725 |    -4.03e+05 |  GentleBoost |          196 |    0.0074455 |           34 |            3 |            - |            - |
|    7 | Best   |    0.066148 |        1.27 |    0.066148 |    0.070734 |    -3.86e+05 |  GentleBoost |          230 |     0.011447 |            8 |            3 |            - |            - |
|    8 | Accept |    0.069261 |      2.3987 |    0.066148 |    0.066323 |    -2.47e+05 |  GentleBoost |          432 |     0.024744 |            4 |            4 |            - |            - |
|    9 | Accept |     0.12374 |     0.88609 |    0.066148 |    0.066281 |    -4.32e+05 |  GentleBoost |          168 |      0.16487 |           57 |            2 |            - |            - |
|   10 | Accept |    0.069261 |      1.0785 |    0.066148 |    0.070496 |    -4.07e+05 |  GentleBoost |          159 |    0.0012533 |            1 |            4 |            - |            - |
|   11 | Accept |    0.093385 |      1.5226 |    0.066148 |    0.066138 |    -3.35e+05 |  GentleBoost |          281 |    0.0020998 |           68 |            4 |            - |            - |
|   12 | Accept |    0.070817 |      1.0208 |    0.066148 |    0.066095 |    -4.01e+05 |  GentleBoost |          168 |      0.40426 |            2 |            4 |            - |            - |
|   13 | Best   |      0.0607 |      2.2231 |      0.0607 |    0.060696 |     -2.8e+05 |  GentleBoost |          445 |    0.0061309 |            1 |            3 |            - |            - |
|   14 | Accept |     0.06537 |      1.1392 |      0.0607 |    0.060745 |    -3.96e+05 |  GentleBoost |          209 |    0.0038539 |            1 |            3 |            - |            - |
|   15 | Infeas |    0.067704 |      2.6118 |      0.0607 |    0.060826 |     4.43e+04 |  GentleBoost |          448 |     0.045051 |            1 |           11 |            - |            - |
|   16 | Accept |    0.071595 |     0.82502 |      0.0607 |    0.061561 |    -3.51e+05 |  GentleBoost |          144 |     0.023625 |            1 |            9 |            - |            - |
|   17 | Accept |    0.067704 |      1.6121 |      0.0607 |    0.060871 |     -2.7e+05 |  GentleBoost |          301 |     0.014356 |            1 |            6 |            - |            - |
|   18 | Infeas |    0.081712 |      3.9201 |      0.0607 |    0.060855 |     1.56e+06 |          Bag |          264 |            - |            1 |          175 |     deviance |            2 |
|   19 | Accept |      0.3393 |     0.18814 |      0.0607 |    0.060887 |    -4.95e+05 |          Bag |           12 |            - |           38 |            1 |     deviance |            2 |
|   20 | Infeas |    0.084047 |      5.6125 |      0.0607 |    0.060869 |        3e+06 |          Bag |          405 |            - |            1 |          121 |          gdi |            1 |
|===============================================================================================================================================================================================|
| Iter | Eval   | Objective   | Objective   | BestSoFar   | BestSoFar   | Constraint1  |       Method | NumLearningC-|    LearnRate |  MinLeafSize | MaxNumSplits | SplitCriteri-| NumVariables-|
|      | result |             | runtime     | (observed)  | (estim.)    | violation    |              | ycles        |              |              |              | on           | ToSample     |
|===============================================================================================================================================================================================|
|   21 | Accept |     0.28949 |      5.1019 |      0.0607 |    0.060906 |    -2.03e+04 |          Bag |          437 |            - |          107 |           60 |          gdi |            2 |
|   22 | Accept |     0.10895 |      2.4328 |      0.0607 |    0.062746 |    -3.58e+05 |  GentleBoost |          452 |    0.0019604 |           18 |            1 |            - |            - |
|   23 | Accept |    0.068482 |       1.055 |      0.0607 |    0.060844 |    -3.84e+05 |  GentleBoost |          171 |    0.0026648 |            5 |            5 |            - |            - |
|   24 | Accept |    0.066926 |      1.7971 |      0.0607 |    0.060832 |    -2.29e+05 |  GentleBoost |          262 |      0.86462 |            2 |            9 |            - |            - |
|   25 | Accept |     0.06537 |      1.4742 |      0.0607 |     0.06114 |    -7.16e+04 |  GentleBoost |          173 |      0.55591 |            1 |           25 |            - |            - |
|   26 | Accept |    0.062257 |       2.499 |      0.0607 |    0.060791 |    -3.12e+05 |  GentleBoost |          464 |     0.001241 |            1 |            2 |            - |            - |
|   27 | Accept |    0.066926 |      1.2121 |      0.0607 |    0.061207 |    -4.04e+05 |  GentleBoost |          194 |    0.0014837 |            5 |            3 |            - |            - |
|   28 | Accept |    0.064591 |      1.6711 |      0.0607 |    0.060801 |    -1.98e+05 |  GentleBoost |          216 |       0.1119 |           24 |           13 |            - |            - |
|   29 | Accept |     0.20311 |      1.0036 |      0.0607 |    0.061225 |    -4.08e+05 |  GentleBoost |          176 |      0.87764 |          223 |           27 |            - |            - |
|   30 | Infeas |    0.070039 |       3.058 |      0.0607 |    0.061289 |     2.28e+06 |  GentleBoost |          256 |      0.38401 |            1 |          931 |            - |            - |
|   31 | Accept |    0.064591 |     0.24856 |      0.0607 |    0.061247 |    -3.73e+05 |  GentleBoost |           13 |      0.40922 |            1 |          228 |            - |            - |
|   32 | Accept |    0.070817 |      0.5699 |      0.0607 |     0.06139 |    -9.93e+04 |  GentleBoost |           38 |    0.0012624 |            1 |          126 |            - |            - |
|   33 | Accept |    0.063035 |      1.7115 |      0.0607 |    0.061101 |    -2.43e+05 |  GentleBoost |          300 |    0.0010972 |           17 |            7 |            - |            - |
|   34 | Accept |    0.068482 |     0.72834 |      0.0607 |    0.061114 |     -7.9e+03 |  GentleBoost |           52 |      0.65776 |            1 |          104 |            - |            - |
|   35 | Accept |    0.066926 |     0.18431 |      0.0607 |    0.061113 |    -4.05e+05 |  GentleBoost |           10 |      0.07542 |            1 |          704 |            - |            - |
|   36 | Accept |    0.067704 |     0.18884 |      0.0607 |    0.061083 |    -3.96e+05 |  GentleBoost |           13 |     0.031071 |            4 |          406 |            - |            - |
|   37 | Accept |    0.070817 |     0.67369 |      0.0607 |    0.061106 |    -1.95e+05 |  GentleBoost |           74 |    0.0016664 |            4 |           59 |            - |            - |
|   38 | Accept |    0.074708 |     0.27587 |      0.0607 |    0.061098 |    -3.05e+05 |  GentleBoost |           22 |      0.18784 |            2 |          464 |            - |            - |
|   39 | Accept |     0.10039 |     0.22573 |      0.0607 |    0.061088 |    -4.14e+05 |          Bag |           11 |            - |            2 |          669 |     deviance |            2 |
|   40 | Accept |     0.23813 |     0.27366 |      0.0607 |    0.061112 |    -4.69e+05 |          Bag |           14 |            - |           46 |         1137 |     deviance |            2 |
|===============================================================================================================================================================================================|
| Iter | Eval   | Objective   | Objective   | BestSoFar   | BestSoFar   | Constraint1  |       Method | NumLearningC-|    LearnRate |  MinLeafSize | MaxNumSplits | SplitCriteri-| NumVariables-|
|      | result |             | runtime     | (observed)  | (estim.)    | violation    |              | ycles        |              |              |              | on           | ToSample     |
|===============================================================================================================================================================================================|
|   41 | Accept |     0.08716 |     0.41637 |      0.0607 |    0.061117 |    -3.65e+05 |          Bag |           22 |            - |            1 |           58 |     deviance |            2 |
|   42 | Accept |    0.080156 |     0.44531 |      0.0607 |    0.061237 |     -3.3e+05 |          Bag |           22 |            - |            1 |          322 |     deviance |            2 |
|   43 | Accept |    0.066148 |      0.9497 |      0.0607 |    0.061131 |    -3.04e+05 |  GentleBoost |          132 |    0.0010735 |           24 |           14 |            - |            - |
|   44 | Accept |    0.069261 |     0.17554 |      0.0607 |    0.061142 |     -4.1e+05 |  GentleBoost |           10 |      0.72738 |            2 |          155 |            - |            - |
|   45 | Accept |    0.080934 |     0.19364 |      0.0607 |    0.061135 |    -3.84e+05 |   LogitBoost |           14 |     0.069681 |            1 |          255 |            - |            - |
|   46 | Accept |     0.15875 |     0.17405 |      0.0607 |    0.061126 |    -4.37e+05 |   LogitBoost |           15 |     0.035786 |           13 |          217 |            - |            - |
|   47 | Accept |     0.14475 |     0.15086 |      0.0607 |    0.061121 |    -4.83e+05 |   LogitBoost |           13 |      0.44161 |            1 |           12 |            - |            - |
|   48 | Infeas |    0.066148 |      1.7258 |      0.0607 |    0.061125 |     1.38e+06 |   LogitBoost |          186 |      0.12108 |            1 |         1235 |            - |            - |
|   49 | Accept |    0.084825 |     0.22487 |      0.0607 |    0.061131 |    -4.11e+05 |   LogitBoost |           11 |     0.053906 |            1 |         1260 |            - |            - |
|   50 | Accept |     0.44591 |     0.14745 |      0.0607 |    0.061293 |    -4.96e+05 |   LogitBoost |           10 |    0.0013377 |          465 |            1 |            - |            - |

__________________________________________________________
Optimization completed.
MaxObjectiveEvaluations of 50 reached.
Total function evaluations: 50
Total elapsed time: 75.3564 seconds
Total objective function evaluation time: 65.1444

Best observed feasible point:
      Method       NumLearningCycles    LearnRate    MinLeafSize    MaxNumSplits    SplitCriterion    NumVariablesToSample
    ___________    _________________    _________    ___________    ____________    ______________    ____________________

    GentleBoost           445           0.0061309         1              3           <undefined>              NaN         

Observed objective function value = 0.0607
Estimated objective function value = 0.061293
Function evaluation time = 2.2231
Observed constraint violations =[ -279773.500000 ]

Best estimated feasible point (according to models):
      Method       NumLearningCycles    LearnRate    MinLeafSize    MaxNumSplits    SplitCriterion    NumVariablesToSample
    ___________    _________________    _________    ___________    ____________    ______________    ____________________

    GentleBoost           445           0.0061309         1              3           <undefined>              NaN         

Estimated objective function value = 0.061293
Estimated function evaluation time = 2.2238
Estimated constraint violations =[ -372671.500000 ]
optimizedSWClassifier = optimizedSWClassifier{1};

Compute the 5-fold cross-validation accuracy.

cvOptimizedSWEnsemble = crossval(optimizedSWClassifier,KFold=5);
sprintf("Accuracy: %f",1 - kfoldLoss(cvOptimizedSWEnsemble))
ans = 
"Accuracy: 0.922957"

The optimized model retains a cross-validation accuracy above 92%.

Compare Classifier Performance and Select Best Model

Compare the performance of the three shaft wear classifiers on the test data (faultDataTest and ShaftWearTest). Use the helper function helperComputeMetrics.

SWClassifiers = {defaultSWClassifier,smallSWClassifier, ...
    optimizedSWClassifier};
ModelNames = ["Default model","Model with selected features", ...
    "Optimized model with selected features"];
SWClassifiersMetricsTable = helperComputeMetrics(SWClassifiers, ...
    faultDataTest,ShaftWearTest,ModelNames)
SWClassifiersMetricsTable = 3×6 table
                                              Accuracy    True Positive Rate    False Positive Rate    False Positives    False Negatives    Model Size (KBs)
                                              ________    __________________    ___________________    _______________    _______________    ________________

    Default model                             0.93103          0.92245               0.062092                19                 19                 116.9     
    Model with selected features              0.96005          0.95102                0.03268                10                 12                 112.8     
    Optimized model with selected features    0.96003          0.94286               0.026144                 8                 14                220.23     

The metrics table indicates that all three ensembles perform well on the test data.

Select smallSWClassifier as the shaft wear detector in the transmission system due to its high test set accuracy and its low deployment footprint.

SWClassifier = smallSWClassifier;

Reduce Size of Selected Classifiers

Reduce the size of the sensor drift classifier SDClassifier and the shaft wear classifier SWClassifier by using the compact function. The function removes properties, like the stored training data variables, that are not needed for making predictions on new data.

SDClassifier = compact(SDClassifier);
SWClassifier = compact(SWClassifier);

Verify Model Fit Requirements

Verify that the final models SDClassifier and SWClassifier meet the requirements described in Model Fit Requirements. In particular, each classifier must perform well on the test data in terms of accuracy, true positive rate, and false positive rate, and must have a sufficiently low memory footprint. Use the helper function helperComputeMetrics to compute the necessary metrics. Use the helper function helperVerifyRequirements to summarize the results in a table.

sdMetrics = helperComputeMetrics({SDClassifier},faultDataTest,SensorDriftTest,"SD");
swMetrics = helperComputeMetrics({SWClassifier},faultDataTest,ShaftWearTest,"SW");

verificationTable = helperVerifyRequirements(sdMetrics,swMetrics)
verificationTable = 8×5 table
        RequirementID                      Description                  Metric    Threshold    Result
    ______________________    ______________________________________    ______    _________    ______

    "SD_MODELFIT_ACCURACY"    "Sensor drift detector accuracy > 92%"    99.819        92       "PASS"
    "SW_MODELFIT_ACCURACY"    "Shaft wear detector accuracy > 92%"      96.005        92       "PASS"
    "SD_MODELFIT_TPR"         "Sensor drift detector TPR > 90%"            100        90       "PASS"
    "SW_MODELFIT_TPR"         "Shaft wear detector TPR > 90%"           95.102        90       "PASS"
    "SD_MODELFIT_FPR"         "Sensor drift detector FPR < 5%"          0.3268         5       "PASS"
    "SW_MODELFIT_FPR"         "Shaft wear detector FPR < 5%"             3.268         5       "PASS"
    "SD_MEMORY_FOOTPRINT"     "Sensor drift model memory < 500 KB"       1.162       500       "PASS"
    "SW_MEMORY_FOOTPRINT"     "Shaft wear model memory < 500 KB"         112.8       500       "PASS"

The verification table shows that the classifiers pass all the requirements.

Helper Functions

helperComputeMetrics

The helperComputeMetrics helper function takes in a cell array of models (Mdls), predictor data (x), response data (y), and a string array of model names (MdlNames), and returns a table of metric values (metrics). In particular, for each model, the function computes the accuracy, true positive rate (TPR), false positive rate (FPR), number of false positives, and number of false negatives when predicting on the data in x and y. The function also estimates the amount of system memory the model uses in generated code (in kilobytes).

function metrics = helperComputeMetrics(Mdls,x,y,MdlNames)
NumModels = numel(Mdls);
% Initialize metrics 
Acc = zeros(NumModels,1);
TPRs = Acc;
FPRs = Acc;
FPs = Acc;
FNs = Acc;
MdlSize = Acc;
for i = 1:NumModels
    Mdl = Mdls{i};
    Acc(i) = 1 - loss(Mdl,x,y);
    yPred = predict(Mdl,x);
    C = confusionmat(y,yPred,Order=[0 1]);
    % Extract counts
    % Rows = true class, Columns = predicted class
    TN = C(1,1);  % true 0, predicted 0
    FP = C(1,2);  % true 0, predicted 1
    FN = C(2,1);  % true 1, predicted 0
    TP = C(2,2);  % true 1, predicted 1
    % Compute TPR and FPR (guard against division by 0)
    TPR = TP / max(TP + FN,1); % Recall/Sensitivity for Positive=1
    FPR = FP / max(FP + TN,1); % 1 - Specificity for Negative=0
    TPRs(i) = TPR;
    FPRs(i) = FPR;
    FPs(i) = FP;
    FNs(i) = FN;
    MdlSize(i) = learnersize(Mdl,SizeType="coder")/1000;
end
metrics = table(Acc,TPRs,FPRs,FPs,FNs,MdlSize, ...
    VariableNames = ["Accuracy","True Positive Rate","False Positive Rate", ...
    "False Positives","False Negatives","Model Size (KBs)"], ...
    RowNames=MdlNames);
end

helperVerifyRequirements

The helperVerifyRequirements function determines whether the requirements described in Model Fit Requirements are met, given the sensor drift classifier metrics sdMetrics and the shaft wear classifier metrics swMetrics. The function returns a table (verificationTable) with the results.

function verificationTable = helperVerifyRequirements(sdMetrics,swMetrics)

RequirementID = ["SD_MODELFIT_ACCURACY";"SW_MODELFIT_ACCURACY"; ...
    "SD_MODELFIT_TPR";"SW_MODELFIT_TPR"; ...
    "SD_MODELFIT_FPR";"SW_MODELFIT_FPR"; ...
    "SD_MEMORY_FOOTPRINT";"SW_MEMORY_FOOTPRINT"];
Description = ["Sensor drift detector accuracy > 92%"; ...
    "Shaft wear detector accuracy > 92%"; ...
    "Sensor drift detector TPR > 90%"; ...
    "Shaft wear detector TPR > 90%"; ...
    "Sensor drift detector FPR < 5%"; ...
    "Shaft wear detector FPR < 5%"; ...
    "Sensor drift model memory < 500 KB"; ...
    "Shaft wear model memory < 500 KB"];
Metric = [sdMetrics.Accuracy*100; swMetrics.Accuracy*100; ...
    sdMetrics.("True Positive Rate")*100; swMetrics.("True Positive Rate")*100; ...
    sdMetrics.("False Positive Rate")*100; swMetrics.("False Positive Rate")*100; ...
    sdMetrics.("Model Size (KBs)"); swMetrics.("Model Size (KBs)")];
Threshold = [92; 92; 90; 90; 5; 5; 500; 500];
Pass = [Metric(1) > 92; Metric(2) > 92; Metric(3) > 90; Metric(4) > 90; ...
    Metric(5) < 5; Metric(6) < 5; Metric(7) < 500; Metric(8) < 500];

Result = strings(size(Pass));
Result(Pass) = "PASS";
Result(~Pass) = "FAIL";

verificationTable = table(RequirementID,Description,Metric,Threshold,Result);
end

See Also

(Statistics and Machine Learning Toolbox) | (Statistics and Machine Learning Toolbox) | (Statistics and Machine Learning Toolbox) | (Statistics and Machine Learning Toolbox) | (Statistics and Machine Learning Toolbox)