Sensitivity Analysis
Overview
Not all layers in a neural network are equally important. Some are fragile — compressing them even slightly degrades performance — while others are robust and can be heavily compressed with minimal impact. Sensitivity analysis measures this per-layer fragility, enabling smarter compression strategies.
The SensitivityAnalyzer works by compressing one layer at a time, measuring the impact on a user-provided evaluation metric, and ranking layers by their sensitivity (delta from baseline).
Key Features:
- Supports three compression types: sparsity (weight zeroing), pruning (structural filter removal), and quantization (precision reduction)
- Generates non-uniform per-layer targets via
to_layer_targets()— fragile layers get less compression, robust layers get more - Uses fasterai’s
SparsifierandPrunerinternally for consistent results - Visualization with
plot()and export to pandas withto_dataframe()
When to Use
| Compression Type | level parameter |
What it tests | Best for |
|---|---|---|---|
"sparsity" |
% of weights zeroed (e.g., 50) | Unstructured weight removal | Generating per-layer sparsity targets |
"pruning" |
% of filters removed (e.g., 30) | Structural filter pruning | Identifying layers to protect via ignored_layers |
"quantization" |
Bit width (e.g., 8) | Precision reduction | Finding layers that need higher precision |
Data Classes
Sensitivity results are returned as structured dataclasses for easy inspection, sorting, and export.
Skipping import of cpp extensions due to incompatible torch version 2.9.1+cu128 for torchao version 0.16.0 Please see https://github.com/pytorch/ao/issues/2919 for more info
LayerSensitivity
def LayerSensitivity(
name:str, layer_type:str, params:int, baseline_metric:float, compressed_metric:float, delta:float,
group_id:int | None=None, group_members:list[str]=<factory>, prunable:bool=True
)->None:
Sensitivity result for a single layer.
LayerSensitivity holds the result for a single layer: the metric before and after compression, the delta (positive = degradation), and layer metadata. Use as_dict() to serialize.
SensitivityResult
def SensitivityResult(
compression_type:str, compression_level:float, baseline_metric:float, layers:list[LayerSensitivity],
metric_name:str='accuracy', higher_is_better:bool=True
)->None:
Structured result from sensitivity analysis.
SensitivityResult aggregates all per-layer results. It provides methods to inspect, rank, visualize, and convert sensitivity data into actionable compression targets.
Key Methods
SensitivityResult.top
def top(
n:int=5, # number of layers to return
most_sensitive:bool=True, # True=highest delta (fragile), False=lowest (robust)
)->list[LayerSensitivity]:
Return top N most or least sensitive layers.
Layers that could not be pruned (prunable=False) are excluded — a no-op prune is NOT evidence of robustness, so it must never rank as compressible.
SensitivityResult.to_layer_targets
def to_layer_targets(
model:nn.Module, # model (used for parameter counts)
target_pct:float=50, # target mean compression percentage
min_pct:float=0, # minimum compression for any layer
max_pct:float=90, # maximum compression for any layer
gamma:float=1.0, # exponent for sensitivity scaling (higher = more differentiation)
)->dict[str, float]:
Convert sensitivity to non-uniform per-layer compression targets.
High sensitivity layers get lower compression, robust layers get higher. Uses parameter-weighted optimization to hit target_pct exactly.
Coupled layers (same group_id) are optimized as a SINGLE knob — they physically share one pruning ratio — then the group’s target is expanded back to every member, so the returned dict stays per-layer. Layers that are not prunable receive min_pct.
The to_layer_targets() method converts sensitivity scores into a dict[str, float] mapping layer names to compression percentages. This dict can be passed directly to Sparsifier.sparsify_model() or SparsifyCallback(sparsity=...) for non-uniform compression.
The gamma parameter controls differentiation: higher values protect fragile layers more aggressively.
targets = result.to_layer_targets(model, target_pct=50, min_pct=10, max_pct=80, gamma=1.5)
# {'conv1': 62.5, 'layer1.0.conv1': 65.3, 'layer1.1.conv2': 10.0, ...}SensitivityResult.summary
def summary(
top:int=5, # number of layers to show per category
)->None:
Print a formatted summary of sensitivity analysis.
SensitivityResult.plot
def plot(
figsize:tuple=(12, 5), # figure size (width, height)
)->None:
Plot sensitivity as a bar chart.
SensitivityResult.to_dataframe
def to_dataframe(
):
Convert to pandas DataFrame.
SensitivityAnalyzer
The SensitivityAnalyzer class provides full control over the analysis process. For quick one-off analysis, see analyze_sensitivity() below.
SensitivityAnalyzer
def SensitivityAnalyzer(
model:nn.Module, # model to analyze
sample:torch.Tensor, # example input (for Pruner dependency analysis)
eval_fn:Callable[[nn.Module], float], # evaluation function returning metric
criteria:Criteria=<fasterai.core.criteria.Criteria object>, # fasterai criteria for importance scoring
higher_is_better:bool=True, # whether higher metric values are better
metric_name:str='accuracy', # name of the metric for display
device:str | torch.device | None=None, # device for computation
calibration_data:torch.Tensor | None=None, # for observer-based quantization
):
Analyze per-layer sensitivity to compression methods.
Uses fasterai’s Sparsifier for sparsity analysis and Pruner for structural pruning. Supports sparsity (weight zeroing), pruning (structural), and quantization.
The eval_fn should take a nn.Module and return a scalar metric (e.g., accuracy, loss). The analyzer will call it once per layer, so it should be reasonably fast — a forward pass on a small validation batch is typical.
Key Methods
SensitivityAnalyzer.analyze
def analyze(
compression:Literal['sparsity', 'pruning', 'quantization']='sparsity', # compression type
level:float=50, # compression level (% for sparsity/pruning, bits for quant)
granularity:str='weight', # granularity for sparsity (fasterai granularities)
layers:list[str] | None=None, # specific layer names to analyze (None = all)
layer_types:type | tuple[type, ...] | None=None, # restrict to a module type or tuple of types, e.g. nn.Conv2d or (nn.Conv2d, nn.Linear) (None = all compressible)
quant_per_channel:bool=True, # use per-channel quantization
quant_activations:bool=False, # also quantize activations
verbose:bool=True, # print progress
)->SensitivityResult:
Analyze per-layer sensitivity to compression.
For pruning, each layer is pruned with the per-layer target {name: level} — the exact operation Pruner/PruneCallback perform — so the reported Δ faithfully predicts the degradation of really pruning that layer at level. Residual/skip-coupled layers prune together and therefore share a Δ (tagged with a common group_id); layers that cannot be pruned independently (output Linear, attention) are marked prunable=False.
Pass layer_types to restrict the analysis to specific module types — a single type or a tuple, e.g. layer_types=nn.Conv2d analyses only convolutions and skips the classifier Linear.
Pruning is group-aware and faithful. In
pruningmode each layer is pruned with the exact per-layer target{name: level}thatPruner/PruneCallbackuse, so the reporteddeltais the degradation you get from really pruning that layer atlevel— not an approximation. Residual/skip-connected layers (e.g. a ResNet stem conv, blockconv2s,downsamples) share output channels and can only be pruned together, so they are pruned as a group and report the samedelta, tagged with a sharedgroup_id. Layers that cannot be pruned independently (the outputLinear, attentionqkv) are markedprunable=Falseand surfaced separately rather than ranked as “robust”.to_layer_targets()collapses eachgroup_idto a single knob so coupled layers aren’t double-counted.⚠️ The
deltais level-specific — an analysis atlevel=50predicts a 50% prune. To plan a 10% prune, analyze atlevel=10.
For quantization analysis, additional parameters control the behavior:
quant_per_channel=True— per-channel quantization (more accurate, standard for weights)quant_activations=False— set toTrueto also quantize activations (slower but more realistic)levelis interpreted as bit width (e.g., 8 for INT8) instead of percentage
SensitivityAnalyzer.sweep
def sweep(
compression:Literal['sparsity', 'pruning', 'quantization']='sparsity', # compression type
levels:list[float] | None=None, # compression levels to test (default: [25, 50, 75])
kwargs:VAR_KEYWORD
)->list[SensitivityResult]:
Run sensitivity analysis at multiple compression levels.
Convenience Function
For quick one-off analysis without creating an analyzer instance:
analyze_sensitivity
def analyze_sensitivity(
model:nn.Module, # model to analyze
sample:torch.Tensor, # example input tensor
eval_fn:Callable[[nn.Module], float], # evaluation function returning metric
compression:Literal['sparsity', 'pruning', 'quantization']='sparsity', # compression type
level:float=50, # compression level (% for sparsity/pruning, bits for quant)
criteria:Criteria=<fasterai.core.criteria.Criteria object>, # fasterai criteria for importance scoring
higher_is_better:bool=True, # whether higher metric values are better
metric_name:str='accuracy', # name of the metric for display
granularity:str='weight', # granularity for sparsity
verbose:bool=True, # print progress
kwargs:VAR_KEYWORD
)->SensitivityResult:
One-line sensitivity analysis using fasterai compression methods.
Usage Example
from fasterai.analyze.sensitivity import analyze_sensitivity
result = analyze_sensitivity(model, sample, eval_fn, compression="sparsity", level=50)
# Inspect results
result.summary() # formatted console output
fragile = result.top(5, most_sensitive=True) # most sensitive layers
# Generate per-layer targets for non-uniform compression
targets = result.to_layer_targets(model, target_pct=50, min_pct=10, max_pct=80)
# Pass directly to Sparsifier: sparsifier.sparsify_model(sparsity=targets)See Also
- Sensitivity Tutorial - Step-by-step guide with real examples on ResNet18
- Sparsifier - Apply non-uniform sparsity using
to_layer_targets()output - Pruner - Structural pruning (use
top()to find layers to protect) - Criteria - Importance scoring methods used during analysis
- Schedules - Control compression progression during training