Main Content

verifyEnsembleRobustness

R2026b

Verify robustness of Statistics and Machine Learning Toolbox tree-based ensemble classifiers

Since R2026b

    Description

    Add-On Required: This feature requires the AI Verification Library for Deep Learning Toolbox add-on.

    results = verifyEnsembleRobustness(Mdl,TblLower,TblUpper,responseVarName) returns verification results across the regions defined by the lower bounds in the TblLower table and the upper bounds in the TblUpper table.

    For each region, the function verifies whether the expected class label in the responseVarName variable matches the predicted class label returned by the classification ensemble of trees Mdl. That is, the function verifies whether Mdl is robust with respect to class label i when the input is between TblLower(i,:) and TblUpper(i,:). For more information, see Tree Ensemble Robustness.

    The verifyEnsembleRobustness function requires Statistics and Machine Learning Toolbox™.

    example

    results = verifyEnsembleRobustness(Mdl,TblLower,TblUpper,labels) returns verification results using the class labels in labels.

    example

    results = verifyEnsembleRobustness(Mdl,XLower,XUpper,labels) returns verification results using the numeric lower and upper bounds XLower and XUpper, respectively.

    Examples

    collapse all

    Verify the robustness of a classification ensemble of trees.

    Load the humanactivity data set, which contains 24,075 observations of five different physical human activities: sitting, standing, walking, running, and dancing. Each observation has 60 features extracted from acceleration data measured by smartphone accelerometer sensors. Create a categorical variable (Activity) corresponding to the class labels for the observations, and convert the predictor data to a table (Tbl).

    load humanactivity.mat
    Activity = categorical(actid,1:5,actnames);
    Tbl = array2table(feat,VariableNames=featlabels);

    Train a classification ensemble of trees using the data. Tune the hyperparameters of the ensemble using Bayesian optimization with 3-fold cross-validation. The optimization process can take several minutes to run.

    rng(0,"twister") % For reproducibility
    hpoOptions = hyperparameterOptimizationOptions(KFold=3,Verbose=0);
    Mdl = fitcensemble(Tbl,Activity,OptimizeHyperparameters="auto", ...
        HyperparameterOptimizationOptions=hpoOptions)

    Figure contains an axes object. The axes object with title Min objective vs. Number of function evaluations, xlabel Function evaluations, ylabel Min objective contains 2 objects of type line. These objects represent Min observed objective, Estimated min objective.

    Mdl = 
      ClassificationBaggedEnsemble
                           PredictorNames: {1×60 cell}
                             ResponseName: 'Y'
                    CategoricalPredictors: []
                               ClassNames: [Sitting    Standing    Walking    Running    Dancing]
                           ScoreTransform: 'none'
                          NumObservations: 24075
        HyperparameterOptimizationResults: [1×1 classreg.learning.paramoptim.SupervisedLearningBayesianOptimization]
                               NumTrained: 229
                                   Method: 'Bag'
                             LearnerNames: {'Tree'}
                     ReasonForTermination: 'Terminated normally after completing the requested number of training cycles.'
                                  FitInfo: []
                       FitInfoDescription: 'None'
                                FResample: 1
                                  Replace: 1
                         UseObsForLearner: [24075×229 logical]
    
    
      Properties, Methods
    
    

    Create a region of predictor values on which to verify the predictive behavior of Mdl.

    For each predictor, compute the mean and standard deviation of the observations corresponding to the activity of sitting. The lower bound of the region is the mean minus the standard deviation, and the upper bound of the region is the mean plus the standard deviation. Plot the lower and upper bounds.

    sittingObservations = Tbl(Activity=="Sitting",:);
    
    observationsMean = mean(sittingObservations);
    observationsStd = std(sittingObservations);
    
    tblLower = observationsMean - observationsStd;
    tblUpper = observationsMean + observationsStd;
    
    plot(tblLower{:,:})
    hold on
    plot(tblUpper{:,:})
    hold off
    legend("Lower bound","Upper bound")
    xlabel("Predictor Index")

    Figure contains an axes object. The axes object with xlabel Predictor Index contains 2 objects of type line. These objects represent Lower bound, Upper bound.

    Verify that the ensemble predicts the class label Sitting for all observations in the region between tblLower and tblUpper.

    result = verifyEnsembleRobustness(Mdl,tblLower,tblUpper,"Sitting")
    result = categorical
         verified 
    
    

    Increase the perturbation region on which to verify the robustness of a classification ensemble of trees.

    Load the carbig data set, which contains car measurements. First, convert Origin to a categorical variable with two categories, USA and NotUSA. Then, create a table from a subset of the variables. Include the categorical Origin variable.

    load carbig
    
    Origin = categorical(cellstr(Origin));
    Origin = mergecats(Origin,["France","Japan","Germany", ...
        "Sweden","Italy","England"],"NotUSA");
    
    cars = table(Acceleration,Displacement,Horsepower, ...
        Origin,MPG,Weight,Cylinders);

    Train a classification ensemble using the data in cars. Specify Cylinders as the response variable. Ensure that Mdl is an ensemble of trees.

    Mdl = fitcensemble(cars,"Cylinders",Learners="tree")
    Mdl = 
      ClassificationEnsemble
               PredictorNames: {'Acceleration'  'Displacement'  'Horsepower'  'Origin'  'MPG'  'Weight'}
                 ResponseName: 'Cylinders'
        CategoricalPredictors: 4
                   ClassNames: [3 4 5 6 8]
               ScoreTransform: 'none'
              NumObservations: 406
                   NumTrained: 100
                       Method: 'AdaBoostM2'
                 LearnerNames: {'Tree'}
         ReasonForTermination: 'Terminated normally after completing the requested number of training cycles.'
                      FitInfo: [100×1 double]
           FitInfoDescription: {2×1 cell}
    
    
      Properties, Methods
    
    

    Select an observation from the data. Check the class label that Mdl predicts for the observation.

    idx = 1;
    observation = cars(idx,:)
    observation = 1×7 table
        Acceleration    Displacement    Horsepower    Origin    MPG    Weight    Cylinders
        ____________    ____________    __________    ______    ___    ______    _________
    
             12             307            130         USA      18      3504         8    
    
    
    predictedLabel = predict(Mdl,observation)
    predictedLabel = 
    8
    

    Perturb the observation by increasing amounts. Note that you can perturb only numeric predictor values; categorical values must be the same for the lower and upper bounds.

    To perturb the numeric predictor values, use the custom perturbPredictors function, which accepts numeric predictor data (numericPredictors) and a percentage (percent). For each predictor, the function computes the specified percentage of the interquartile range and returns the value (perturbation).

    function perturbation = perturbPredictors(numericPredictors,percent)
    perturbation = percent*iqr(numericPredictors);
    end

    Increase the perturbation by increasing the percentage value from 5% to 25% in increments of 5%. Create a lower bound for the observation by subtracting the perturbation value, and create an upper bound by adding the perturbation value. Combine all the lower bounds in TblLower, and combine all the upper bounds in TblUpper.

    numericPredictorNames = Mdl.PredictorNames;
    numericPredictorNames(Mdl.CategoricalPredictors) = [];
    
    percentRange = 0.05:0.05:0.25;
    TblLower = repmat(observation,numel(percentRange),1);
    TblUpper = TblLower;
    
    for k = 1:numel(percentRange)
        percentk = percentRange(k);
        perturbk = @(numericPredictors)perturbPredictors(numericPredictors,percentk);
        perturbationk = varfun(perturbk,cars, ...
            InputVariables=numericPredictorNames);
        perturbationk.Properties.VariableNames = numericPredictorNames;
    
        TblLower(k,numericPredictorNames) = ...
            TblLower(k,numericPredictorNames) - perturbationk;
    
        TblUpper(k,numericPredictorNames) = ...
            TblUpper(k,numericPredictorNames) + perturbationk;
    end

    Verify the robustness of the model on the increasing perturbation regions.

    results = verifyEnsembleRobustness(Mdl,TblLower,TblUpper,"Cylinders")
    results = 5×1 categorical
         verified 
         verified 
         verified 
         verified 
         violated 
    
    

    In the first four perturbation regions, Mdl predicts the same class label (8) for all observations with values between the lower and upper bounds. In the last perturbation region, Mdl predicts a different class label (3, 4, 5, or 6) for at least one combination of predictor values between the lower bound TblLower(5,:) and the upper bound TblUpper(5,:).

    Input Arguments

    collapse all

    Trained ensemble classifier, specified as a ClassificationEnsemble (Statistics and Machine Learning Toolbox), ClassificationBaggedEnsemble (Statistics and Machine Learning Toolbox), or CompactClassificationEnsemble (Statistics and Machine Learning Toolbox) model object.

    You must specify a trained ensemble classifier that uses trees as weak learners, an aggregation method other than linear programming boosting or totally corrective boosting, and the default score transform. That is, the models in Mdl.Trained must all be tree classifiers, Mdl.Method must not be "LPBoost" or "TotalBoost", and Mdl.ScoreTransform must be "none" or "identity".

    Lower bounds on the predictor data, specified as a table. The lower and upper bounds, TblLower and TblUpper, must have the same size and format. The function computes the results across the regions defined by the lower and upper bounds.

    If variable k in TblLower is categorical, then TblLower(:,k) must match TblUpper(:,k). That is, for each region, the categorical predictor values for the lower bound and the categorical predictor values for the upper bound must be the same.

    Data Types: table

    Upper bounds on the predictor data, specified as a table. The lower and upper bounds, TblLower and TblUpper, must have the same size and format. The function computes the results across the regions defined by the lower and upper bounds.

    If variable k in TblUpper is categorical, then TblUpper(:,k) must match TblLower(:,k). That is, for each region, the categorical predictor values for the lower bound and the categorical predictor values for the upper bound must be the same.

    Data Types: table

    Response variable name, specified as a character vector or string scalar. responseVarName must be the name of a variable in both TblLower and TblUpper. Each label i in the responseVarName variable is the expected class label for all observations in the region with lower bound TblLower(i,:) and upper bound TblUpper(i,:). For each region, the function verifies that the predicted class label returned by Mdl matches the label in the responseVarName variable.

    Data Types: char | string

    Class labels, specified as a numeric, categorical, or logical vector; a character or string array; or a cell array of character vectors. Each label i in labels is the expected class label for all observations in the region with lower bound i and upper bound i (for example, XLower(i,:) and XUpper(i,:), respectively). For each region, the function verifies that the predicted class label returned by Mdl matches the label in labels.

    Data Types: single | double | categorical | logical | char | string | cell

    Numeric lower bounds on the predictor data, specified as a numeric matrix. The lower and upper bounds, XLower and XUpper, must have the same size and format. The function computes the results across the regions defined by the lower and upper bounds.

    Data Types: single | double

    Numeric upper bounds on the predictor data, specified as a numeric matrix. The lower and upper bounds, XLower and XUpper, must have the same size and format. The function computes the results across the regions defined by the lower and upper bounds.

    Data Types: single | double

    Output Arguments

    collapse all

    Verification results, returned as a categorical array. For each set of lower and upper bounds, the function returns one of these values:

    • "verified" — The model is robust to perturbations in the region between the specified bounds.

    • "violated" — The model is not robust to perturbations in the region between the specified bounds.

    Tips

    • Expect the verification process to take longer for more complex ensembles. For more information on how the performance of verifyEnsembleRobustness depends on the structure of the ensemble, see Performance.

    Algorithms

    collapse all

    References

    [1] Matsunaga, Saori, and Genta Yoshimura. “Efficient and High-Quality Formal Verification for Decision Tree Ensembles.” 2024 IEEE International Conference on Data Mining Workshops (ICDMW), December 9, 2024, 51–58. https://doi.org/10.1109/ICDMW65004.2024.00013.

    [2] Ranzato, Francesco, and Marco Zanella. “Abstract Interpretation of Decision Tree Ensemble Classifiers.” Proceedings of the AAAI Conference on Artificial Intelligence 34, no. 04 (2020): 5478–86. https://doi.org/10.1609/aaai.v34i04.5998.

    Version History

    Introduced in R2026b

    See Also

    (Statistics and Machine Learning Toolbox) | |