Skip to main content

trustformers_debug/
interpretability_tools.rs

1//! # Interpretability Tools
2//!
3//! Comprehensive model interpretability toolkit including SHAP integration, LIME support,
4//! attention analysis, feature attribution, and counterfactual generation for TrustformeRS models.
5//!
6//! ## Refactoring Summary
7//!
8//! Previously this was a single 2,803-line file containing all interpretability functionality.
9//! It has been split into focused modules:
10//!
11//! - `interpretability/config.rs` - Configuration structures and enums (77 lines)
12//! - `interpretability/shap.rs` - SHAP analysis types and functionality (66 lines)
13//! - `interpretability/lime.rs` - LIME analysis types and functionality (78 lines)
14//! - `interpretability/attention.rs` - Attention analysis for transformers (426 lines)
15//! - `interpretability/attribution.rs` - Feature attribution methods (103 lines)
16//! - `interpretability/counterfactual.rs` - Counterfactual generation (191 lines)
17//! - `interpretability/analyzer.rs` - Main analyzer implementation (318 lines)
18//! - `interpretability/report.rs` - Reporting functionality (23 lines)
19//!
20//! This refactoring improves:
21//! - Code maintainability and readability
22//! - Module compilation times
23//! - Test isolation
24//! - Code reuse through focused modules
25//! - Developer experience when working on specific interpretability methods
26
27pub use crate::interpretability::{
28    ActionableInsight,
29    // Attention analysis
30    AttentionAnalysisResult,
31    AttentionBottleneck,
32    AttentionFlowAnalysis,
33    AttentionFlowPath,
34    AttentionHeadResult,
35    AttentionInsight,
36    AttentionLayerResult,
37    AttentionPatterns,
38    AttentionStatistics,
39    AttributionMethod,
40
41    AttributionMethodResult,
42    AttributionVisualizationData,
43    BlockPattern,
44    BottleneckType,
45    BoundaryCrossingPoint,
46    ChangeDirection,
47    Counterfactual,
48    CounterfactualQualityMetrics,
49    // Counterfactual generation
50    CounterfactualResult,
51    DecisionBoundaryAnalysis,
52    DiagonalPattern,
53    FeatureAttribution,
54    // Feature attribution
55    FeatureAttributionResult,
56    FeatureChange,
57    FeatureContribution,
58    FeatureImportance,
59    FeatureInteraction,
60    FeatureSensitivityAnalysis,
61    FlowEfficiencyMetrics,
62    FlowTransformation,
63    HeadCluster,
64    HeadRedundancyAnalysis,
65    HeadSpecializationAnalysis,
66    HeadSpecializationType,
67    ImplementationDifficulty,
68    InsightType,
69
70    InteractionEffect,
71    InteractionEffectType,
72    InteractionType,
73
74    // Main analyzer
75    InterpretabilityAnalyzer,
76
77    // Configuration
78    InterpretabilityConfig,
79    // Reporting
80    InterpretabilityReport,
81    LayerAttentionPatterns,
82    LayerAttentionStats,
83    LayerFlowStats,
84    LayerFlowStep,
85    // LIME analysis
86    LimeAnalysisResult,
87    MethodAgreementAnalysis,
88    NeighborhoodStats,
89
90    PerturbationAnalysis,
91    PerturbationResult,
92    PruningImpact,
93    PruningRecommendation,
94    RedundancyType,
95    RedundantHeadPair,
96    RepetitivePattern,
97    RiskLevel,
98    // SHAP analysis
99    ShapAnalysisResult,
100    ShapSummary,
101
102    SparsityDistribution,
103    SpecializationEvolution,
104    SpecializationTransition,
105    SpecializationTrend,
106    ThresholdAnalysis,
107    TimeHorizon,
108
109    TimelinePoint,
110    TokenAttentionScore,
111    TopFeature,
112    VerticalPattern,
113};
114
115#[cfg(test)]
116mod tests {
117    use crate::interpretability::{
118        AttributionMethod, InterpretabilityAnalyzer, InterpretabilityConfig,
119    };
120    use std::collections::HashMap;
121
122    #[tokio::test]
123    async fn test_interpretability_analyzer_creation() {
124        let config = InterpretabilityConfig::default();
125        let _analyzer = InterpretabilityAnalyzer::new(config);
126    }
127
128    #[tokio::test]
129    async fn test_shap_analysis() {
130        let config = InterpretabilityConfig::default();
131        let mut analyzer = InterpretabilityAnalyzer::new(config);
132
133        let mut instance = HashMap::new();
134        instance.insert("feature1".to_string(), 1.0);
135        instance.insert("feature2".to_string(), 2.0);
136
137        let model_predictions = vec![0.8, 0.7, 0.9];
138        let background_data = vec![{
139            let mut bg = HashMap::new();
140            bg.insert("feature1".to_string(), 0.5);
141            bg.insert("feature2".to_string(), 1.0);
142            bg
143        }];
144
145        let result = analyzer.analyze_shap(&instance, &model_predictions, &background_data).await;
146        assert!(result.is_ok());
147    }
148
149    #[tokio::test]
150    async fn test_lime_analysis() {
151        let config = InterpretabilityConfig::default();
152        let mut analyzer = InterpretabilityAnalyzer::new(config);
153
154        let mut instance = HashMap::new();
155        instance.insert("feature1".to_string(), 1.0);
156        instance.insert("feature2".to_string(), 2.0);
157
158        let model_fn: Box<dyn Fn(&HashMap<String, f64>) -> f64> =
159            Box::new(|input: &HashMap<String, f64>| input.values().sum::<f64>() * 0.1);
160
161        let result = analyzer.analyze_lime(&instance, model_fn).await;
162        assert!(result.is_ok());
163    }
164
165    #[tokio::test]
166    async fn test_feature_attribution_integrated_gradients() {
167        let config = InterpretabilityConfig {
168            enable_feature_attribution: true,
169            attribution_methods: vec![AttributionMethod::IntegratedGradients],
170            ..InterpretabilityConfig::default()
171        };
172        let mut analyzer = InterpretabilityAnalyzer::new(config);
173
174        let mut instance = HashMap::new();
175        instance.insert("feature1".to_string(), 0.5);
176        instance.insert("feature2".to_string(), 1.5);
177        instance.insert("feature3".to_string(), 2.0);
178
179        let model_predictions = vec![0.7, 0.6, 0.8];
180        let background_data = vec![{
181            let mut bg = HashMap::new();
182            bg.insert("feature1".to_string(), 0.0);
183            bg.insert("feature2".to_string(), 0.0);
184            bg.insert("feature3".to_string(), 0.0);
185            bg
186        }];
187
188        let result = analyzer.analyze_shap(&instance, &model_predictions, &background_data).await;
189        assert!(result.is_ok());
190        let shap_result = result.expect("SHAP analysis failed");
191        assert!(
192            !shap_result.feature_contributions.is_empty(),
193            "attributions must be non-empty"
194        );
195    }
196}