Getting started
In this introductory section, you will learn about the main building blocks of SolePostHoc.jl. The above introduces two important ideas for using post-hoc explanation algorithms. Further on in the documentation, the potential of SolePostHoc.jl will become apparent: this package's primary purpose is to provide a uniform interface for knowledge extraction algorithms, enabling the comparison of different post-hoc interpretation methods while maintaining a coherent and intuitive user experience.
Fast introduction
Consider a machine learning model trained on a generic dataset. For example, let us consider a Random Forest Classifier learned on the Iris dataset to classify 3 different species of flowers. We are interested in extracting interpretable rules that explain the model's decision process. SolePostHoc.jl offers two primary methods for accomplishing this task.
The first approach is to directly call the specific algorithm function. For example:
# Extract rules using the LUMEN algorithm directly
extracted_rules = lumen(model, X_test, y_test, args...)The second approach uses the unified interface through rule extractors:
# Extract rules using the unified interface
extractor = LumenRuleExtractor()
decision_set = modalextractrules(extractor, model, X_test, y_test, args...)The key advantage of the second approach is that it not only executes the original algorithm (equivalent to calling lumen(...) directly) but also converts the output into a DecisionSet. A DecisionSet is a vector of propositional logical rules in Disjunctive Normal Form (DNF), with one rule per class/label.
Consider a trained model that classifies hand gestures. Using SolePostHoc.jl, we might extract the following decision set:
Class "Iris-setosa": IF (SepalLengthCm < -0.5) AND (SepalWidthCm < 8.2) THEN predict "Iris-setosa"
Class "Iris-versicolor": IF (SepalLengthCm > 0.5) AND (SepalWidthCm < 3.25) THEN predict "Iris-versicolor"
Class "Iris-virginica": IF (PetalWidthCm > 2.0) THEN predict "Iris-virginica"Core definitions
The foundation of SolePostHoc.jl lies in providing interpretable explanations for complex machine learning models through rule extraction.
abstract type RuleExtractorA RuleExtractor is an abstract type that defines the interface for all post-hoc explanation algorithms. Each concrete implementation represents a specific knowledge extraction method.
A DecisionSet represents the extracted knowledge as a collection of logical rules, where each rule corresponds to a specific class or decision outcome in Disjunctive Normal Form.
The main entry point for rule extraction is:
modalextractrules(extractor::RuleExtractor, model, args...)Algorithm Types
SolePostHoc.jl integrates a wide range of algorithms for knowledge extraction, categorized into three main types:
Surrogate Trees
Algorithms that approximate complex models such as neural networks or random forests with more interpretable decision trees.
struct REFNERuleExtractor <: RuleExtractor end
struct BATreesRuleExtractor <: RuleExtractor end
struct TREPANRuleExtractor <: RuleExtractor endKnowledge Distillation
Techniques for transferring knowledge from complex models (teacher) to simpler and more transparent ones (student).
struct RuleCOSIPLUSRuleExtractor <: RuleExtractor end
struct InTreesRuleExtractor <: RuleExtractor endRule Extraction
Methods for deriving clear and understandable logical rules from any machine learning model.
struct LUMENRuleExtractor <: RuleExtractor endDirect Algorithm Access
For users who prefer to use algorithms in their original form without the unified interface, SolePostHoc.jl provides direct access to each algorithm:
SolePostHoc.RuleExtraction.intrees — Function
intrees(model::Union{AbstractModel,DecisionForest}, X, y::AbstractVector{<:Label}; kwargs...)::DecisionListReturn a decision list which approximates the behavior of the input model on the specified supervised dataset. The set of relevant and non-redundant rules in the decision list are obtained by means of rule selection, rule pruning, and sequential covering (STEL).
References
- Deng, Houtao. "Interpreting tree ensembles with intrees." International Journal of Data Science and Analytics 7.4 (2019): 277-287.
Keyword Arguments
prune_rules::Bool=true: access to prune or notpruning_s::Union{Float64,Nothing}=nothing: parameter that limits the denominator in the pruning metric calculationpruning_decay_threshold::Union{Float64,Nothing}=nothing: threshold used in pruning to remove or not a joint from the rulerule_selection_method::Symbol=:CBC: rule selection method. Currently only supports:CBCrule_complexity_metric::Symbol=:natoms: Metric to use for estimating a rule complexity measuremax_rules::Int=-1: maximum number of rules in the final decision list (excluding default rule). Use -1 for unlimited rules.min_coverage::Union{Float64,Nothing}=nothing: minimum rule coverage for STEL- See
modalextractruleskeyword arguments...
Although the method was originally presented for forests it is hereby extended to work with any symbolic models.
See also AbstractModel, DecisionList, listrules, rulemetrics.
SolePostHoc.RuleExtraction.Lumen.lumen — Function
lumen(model; config_args...) -> LumenResult
lumen(model, config::LumenConfig) -> LumenResultLogic-driven Unified Minimal Extractor of Notions (LUMEN): Extract and minimize logical rules from decision tree models into interpretable DNF formulas.
LUMEN implements a comprehensive pipeline for converting decision tree models into interpretable logical rules. The algorithm extracts the underlying decision logic, constructs truth tables, and applies advanced minimization techniques to produce compact, human-readable rule sets.
Method Signatures
Keyword Arguments Interface
lumen(model; minimization_scheme=:mitespresso, vertical=1.0, horizontal=1.0, ...)Convenient interface using keyword arguments with automatic config construction.
Configuration Object Interface
lumen(model, config::LumenConfig)Advanced interface using pre-constructed configuration for complex scenarios.
Arguments
Required Arguments
model: Decision tree model to analyze- Single trees: Individual decision tree models
- Ensembles: Random forests, gradient boosting, etc.
- Supported formats: DecisionTree.jl, SoleModels framework
Configuration (via keywords or LumenConfig)
minimization_scheme::Symbol = :mitespresso: DNF minimization algorithmvertical::Float64 = 1.0: Instance coverage parameter α ∈ (0,1]depth::Float64 = 1.0: Depth coverage parameter δ ∈ (0,1]horizontal::Float64 = 1.0: Feature coverage parameter β ∈ (0,1]ott_mode::Bool = false: Enable memory-optimized processingsilent::Bool = false: Suppress progress outputreturn_info::Bool = true: Include detailed metadata in results
Returns
LumenResult containing:
decision_set: Collection of minimized logical rulesinfo: Metadata including statistics and unminimized rulesprocessing_time: Total algorithm execution time
Algorithm Pipeline
Phase 1: Model Analysis and Rule Extraction
Input Model → Rule Extraction → Logical Rule Set- Analyzes model structure (single tree vs ensemble)
- Extracts decision paths as logical rules
- Handles different model types with appropriate strategies
Phase 2: Alphabet Construction and Atom Processing
Logical Rules → Atom Extraction → Logical Alphabet- Identifies atomic logical conditions
- Constructs vocabulary for formula building
- Validates feature support and operator compatibility
Phase 3: Truth Table Generation
Model + Alphabet → Truth Combinations → Labeled Examples- Generates systematic input combinations
- Evaluates model on each combination
- Creates correspondence between inputs and outputs
Phase 4: DNF Construction and Minimization
Truth Table → DNF Formulas → Minimized Rules- Constructs DNF formulas for each decision class
- Applies advanced minimization algorithms
- Converts back to interpretable rule format
Performance Characteristics
Computational Complexity
- Time: O(2^k × n × d) where k=features, n=instances, d=tree depth
- Space: O(k × r) where r=number of rules
- Scalability: Optimized modes available for large datasets
Memory Usage
- Standard mode: Suitable for typical datasets (< 20 features)
- Optimized mode: Memory-efficient processing for large problems
- Streaming capability: Future versions may support streaming processing
Advanced Features
Custom Processing
# Custom alphabet filtering for domain expertise
custom_filter = alphabet -> remove_irrelevant_features(alphabet)
config = LumenConfig(filteralphabetcallback = custom_filter)
result = lumen(model, config)Performance Tuning
# Memory-optimized processing for large datasets
config = LumenConfig(ott_mode = true, vertical = 0.8)
# Speed-optimized processing with basic minimization
config = LumenConfig(minimization_scheme = :abc, silent = true)Analysis and Debugging
# Full information retention for analysis
config = LumenConfig(return_info = true, controllo = true)
result = lumen(model, config)
# Access detailed statistics
println("Rules before minimization: _(length(result.info.unminimized_ds.rules))")
println("Rules after minimization: _(length(result.decision_set.rules))")Error Handling
The algorithm implements comprehensive error handling:
Configuration Validation
- Parameter range checking (coverage parameters must be ∈ (0,1])
- Algorithm availability verification
- Consistency validation across parameters
Processing Errors
- Graceful handling of minimization failures
- Fallback strategies for problematic formulas
- Detailed error reporting with context
Model Compatibility
- Automatic detection of supported model types
- Clear error messages for unsupported formats
- Suggestions for model preprocessing
Examples
Basic Usage
# Simple rule extraction with default settings
model = build_tree(X, y)
result = lumen(model)
println("Extracted _(length(result.decision_set.rules)) rules")Advanced Configuration
# Customized processing for complex scenarios
config = LumenConfig(
minimization_scheme = :boom, # Aggressive minimization
vertical = 0.9, # High instance coverage
depth = 0.7, # Moderate depth coverage
horizontal = 0.8, # Moderate feature coverage
ott_mode = true, # Memory optimization
return_info = true # Full information retention
)
result = lumen(large_ensemble, config)Performance Analysis
# Detailed performance and quality analysis
result = lumen(model, LumenConfig(return_info = true))
# Analyze minimization effectiveness
stats = result.info.vectPrePostNumber
total_reduction = sum(pre - post for (pre, post) in stats)
avg_compression = mean(pre / post for (pre, post) in stats)
println("Total term reduction: total_reduction")
println("Average compression ratio: (round(avg_compression, digits=2))x")
println("Processing time: _(result.processing_time) seconds")Implementation Notes
Design Principles
- Modularity: Each phase is independently testable and extensible
- Configurability: Extensive customization without code modification
- Performance: Multiple optimization strategies for different scenarios
- Robustness: Comprehensive error handling and validation
- Usability: Clean interfaces with sensible defaults
Extensibility Points
- New minimization algorithms: Add via Val() dispatch system
- Custom model types: Extend rule extraction strategies
- Domain-specific processing: Custom alphabet filters and apply functions
- Output formats: Additional result formatters and exporters
See also: LumenConfig, LumenResult, extract_rules, minimize_formula
SolePostHoc.RuleExtraction.BATrees.batrees — Function
batrees(f; dataset_name="iris", num_trees=10, max_depth=10, dsOutput=true)Builds and trains a set of binary decision trees OR using the specified function f.
Arguments
f: An SoleForest.dataset_name::String: The name of the dataset to be used. Default is "iris".num_trees::Int: The number of trees to be built. Default is 10.max_depth::Int: The maximum depth of each tree. Default is 10.dsOutput::Bool: A flag indicating whether to return the dsStruct output. Default is true. if false, returns the result single tree.
Returns
- If
dsOutputis true, returns the result is in DecisionSet ds. - If
dsOutputis false, returns the result is SoleTree t`.
Example
SolePostHoc.RuleExtraction.REFNE.refne — Function
refne(m, Xmin, Xmax; L=100, perc=1.0, max_depth=-1, n_subfeatures=-1,
partial_sampling=0.7, min_samples_leaf=5, min_samples_split=2,
min_purity_increase=0.0, seed=3)Extract interpretable rules from a trained neural network ensemble using decision tree approximation.
This implementation follows the REFNE-a (Rule Extraction From Neural Network Ensemble) algorithm, which approximates complex neural network behavior with an interpretable decision tree model.
Arguments
m: Trained neural network model to extract rules fromXmin: Minimum values for each input featureXmax: Maximum values for each input featureL: Number of samples to generate in the synthetic dataset (default: 100)perc: Percentage of generated samples to use (default: 1.0)max_depth: Maximum depth of the decision tree (default: -1, unlimited)n_subfeatures: Number of features to consider at each split (default: -1, all)partial_sampling: Fraction of samples used for each tree (default: 0.7)min_samples_leaf: Minimum number of samples required at a leaf node (default: 5)min_samples_split: Minimum number of samples required to split a node (default: 2)min_purity_increase: Minimum purity increase required for a split (default: 0.0)seed: Random seed for reproducibility (default: 3)
Returns
- A forest-decision trees representing the extracted rules
Description
The algorithm works by:
- Generating a synthetic dataset spanning the input space
- Using the neural network to label these samples
- Training a decision tree to approximate the neural network's behavior
References
- Zhi-Hua, Zhou, et al. Extracting Symbolic Rules from Trained Neural Network Ensembles
Example
```julia model = loaddecisiontree_model() refne(model, Xmin, Xmax)
See also AbstractModel, DecisionList, listrules, rulemetrics.
SolePostHoc.RuleExtraction.TREPAN.trepan — Function
- Mark W. Craven, et al. "Extracting Thee-Structured Representations of Thained Networks"
Rule Extraction, simplification and Optimization
One of the key features of SolePostHoc.jl is its ability to extract, simplify and optimize extracted rules while maintaining their expressiveness.
For example, consider this decision forest:
├[1/2]┐ (V3 < 2.45)
│ ├✔ Iris-setosa
│ └✘ (V4 < 1.75)
│ ├✔ (V3 < 4.65)
│ │ ├✔ Iris-versicolor
│ │ └✘ Iris-versicolor
│ └✘ Iris-virginica
└[2/2]┐ (V4 < 0.8)
├✔ Iris-setosa
└✘ (V1 < 5.65)
├✔ (V4 < 1.2)
│ ├✔ Iris-versicolor
│ └✘ Iris-versicolor
└✘ (V3 < 4.75)
├✔ Iris-versicolor
└✘ Iris-virginicaSolePostHoc.jl can leverage logical reasoning to obtain a more succinct and equally expressive theory:
▣
├[1/3] ((V3 ≥ 2.45) ∧ (V4 ≥ 1.75)) ∨ ((V1 ≥ 5.65) ∧ (V3 ≥ 4.75) ∧ (V4 ≥ 0.8)) ↣ Iris-virginica
├[2/3] ((V3 ≥ 2.45) ∧ (V3 < 4.65) ∧ (V4 < 1.75)) ∨ ((V3 ≥ 4.65) ∧ (V3 < 4.75) ∧ (V4 < 1.75)) ∨ ((V1 < 5.65) ∧ (V3 ≥ 4.65) ∧ (V4 < 1.75)) ∨ ((V3 ≥ 2.45) ∧ (V4 < 0.8)) ↣ Iris-versicolor
└[3/3] (V3 < 2.45) ↣ Iris-setosa
Customization and Extension
Users can implement their own rule extraction algorithms by extending the RuleExtractor interface:
function algorithm(model, args...)
# ordinary function
return output # regular generic type of output
end
struct MyCustomExtractor <: RuleExtractor
# algorithm-specific parameters
end
function modalextractrules(extractor::MyCustomExtractor, model, args...)
# implement your custom convert `generic type of output in decision set` logic
# return a DecisionSet
endIntegration with Sole.jl Ecosystem
SolePostHoc.jl seamlessly integrates with the broader Sole.jl ecosystem, particularly:
- SoleLogics.jl: For modal logic reasoning and formula manipulation
- SoleData.jl: For handling multivariate time series and relational data structures
- SoleModels.jl: For interpretable model training and symbolic learning