This is the single specification document for DEFault++. It consolidates
the runtime feature schema, the architectural principles, and the scope
of v1 with its planned extensions. Everything else (PyPI install, public
API walk-throughs, research-side commands) lives in the READMEs and
defaultplusplus/RESEARCH.md.
The scientific reference is the DEFault++ manuscript (in preparation;
code archived at doi:10.5281/zenodo.20019817).
This file does not restate it. It pins the engineering API built on top
of it. For the method itself, see
ARCHITECTURE.md.
DEFault++ is a single-run, transformer-specific telemetry and diagnosis system. Four decisions shape it.
The invariant part of DEFault++ is the meaning of the measurements: attention entropy, padding / future leakage, QKV alignment, pre-softmax score statistics, gradient and update behavior, FFN output shape, LayerNorm statistics, residual-stream integrity, output uncertainty, KV-cache consistency, runtime / memory, and validation quality. The extraction path may differ across model families; the feature definitions must not. The frozen schema is in Section 2 below.
Compatibility is defined over structural model families, not by HF checkpoint name. The two supported families are:
- BERT-style encoders — bidirectional attention. BERT, RoBERTa, DistilBERT, ALBERT-style encoders.
- GPT-style decoders — causal attention. GPT-2, DistilGPT-2, GPT-Neo-style decoders.
Other families (encoder-decoder T5 / BART, RetNet, Mamba, MoE) are
out of scope for v1. They must fail closed at construction time
with an explicit unsupported-model error. This is implemented in
FeatureExtractor.__init__.
Benchmark construction uses paired clean and faulty fine-tuning runs to generate labeled training examples. That is correct for offline benchmark generation. It is not the runtime API. The runtime extractor must turn one user run into anomaly features against a learned clean reference. Any feature, preprocessing step, or diagnosis path that depends on a matched clean run at inference time is out of scope for runtime v1.
The extraction layer is fixed:
- HuggingFace
ModelOutputobjects already exposehidden_statesandattentionswhen requested. Use them. Trainercallbacks are read-only and cannot alter the forward pass. Per-step metrics that need attention weights or hidden states must be captured through acompute_lossoverride that hands references to the callback (seeDEFaultPlusCallback.capture_inputsandcapture_outputs).- PyTorch profiler is used only for sampled windows (kernel-time and memory features), not as a per-step hook.
- Aggregation runs on a Welford-stable streaming basis so the extractor works on long runs without unbounded memory.
The output of FeatureExtractor.finalize() is a flat
dict[str, float] keyed by the names below. Every key is in scope
runtime_v1 unless explicitly tagged research_only or post_v1.
FeatureExtractor.feature_names returns a fully-determined list of
output keys before any training step has run. Three guarantees:
- Length-stable across runs. The list is identical for any (architecture, num_layers, sampled-layer strategy, parameter groups) tuple regardless of how many epochs the run lasts or which validation metrics the user records.
- Inspector-driven. Layer-indexed keys (
L{i}_...) expand againstinspector.get_sampled_layer_indices()so a 12-layer BERT and a 6-layer DistilBERT each get the right per-layer keys. - Self-padding.
finalize()fills any column the runtime did not emit with0.0, so the returned dict's keys equalfeature_namesexactly. Downstream classifiers can pin their input dimensionality at training time without depending on which task / metric / cadence produced a particular row.
The list is built by walking each metric module's
static_feature_names() declaration, crossing it with the
windowed-feature suffixes (_early_mean, _mid_slope, _final,
etc.), and unioning in every val_<raw> key declared by the task
registry (so CoLA's val_matthews_correlation_* and STS-B's
val_pearson_* / val_spearmanr_* are in the schema even if the
user never runs those tasks).
compute_window_ranges(total_epochs) splits the run into thirds —
early is the first third, mid the middle third, late the
last — so the same column names describe a 5-epoch and a 50-epoch
run with semantically comparable definitions. The paper's 10-epoch
schedule maps to (1-3), (4-6), (7-10) (a one-epoch shift from
the legacy fixed (1-3)/(4-7)/(8-10) mapping; the dataset writer
re-runs the windowed aggregator so historical CSVs may need
regenerating against the new windows).
MetricCollector.validate_feature_names(expected) raises
ValueError with a missing/unexpected diff when the live schema
diverges from a saved reference — used by
defaultplusplus.diagnosis.load_pretrained() to fail closed when
the runtime extractor cannot produce the same columns the
pretrained classifier was trained on.
exact— measured directly from the runtime signal named by the feature.reconstructed— derived from accessible runtime internals, not directly observable in the form named. Example: FFN sublayer behavior inferred from adjacent hidden states because the actual sublayer boundary is not exposed.approximate— proxy that captures related behavior but is not a faithful one-to-one reconstruction.not_available— reserved name; explicitly excluded from the current runtime surface.
{group}is one ofembedding,classifier,layer{i}_attention,layer{i}_qkv,layer{i}_ffn,layer{i}_layernorm.{layer_idx}is sampled by the runtime inspector strategy, not every layer unconditionally.- Aggregation outputs (
*_mean,*_var,*_count,*_final,*_slope,val_*, windowed summaries) are derived from the schema below; they do not constitute independent schema entries unless listed explicitly.
| Metric | Meaning | Availability | Status | Scope |
|---|---|---|---|---|
train_loss |
Primary training loss for the current step | both | exact |
runtime_v1 |
train_learning_rate |
Optimizer LR from the first parameter group | both | exact |
runtime_v1 |
runtime_step_time |
Wall-clock step duration | both | exact |
runtime_v1 |
runtime_steps_per_sec |
Inverse throughput proxy from step time | both | exact |
runtime_v1 |
runtime_memory_alloc_mb |
Allocated accelerator memory | both | exact |
runtime_v1 |
runtime_memory_reserved_mb |
Reserved accelerator memory | both | exact |
runtime_v1 |
loss |
Duplicate alias of train_loss |
both | exact |
research_only |
| Metric or pattern | Meaning | Availability | Status | Scope |
|---|---|---|---|---|
grad_norm_total |
Total gradient norm | both | exact |
runtime_v1 |
grad_abs_min |
Minimum absolute gradient value | both | exact |
runtime_v1 |
grad_abs_max |
Maximum absolute gradient value | both | exact |
runtime_v1 |
grad_zero_ratio |
Fraction of near-zero gradient elements | both | exact |
runtime_v1 |
gradient_vanish |
Vanishing-gradient indicator | both | exact |
runtime_v1 |
gradient_explode |
Exploding-gradient indicator | both | exact |
runtime_v1 |
gradient_variance |
Running variance of total gradient norm | both | exact |
runtime_v1 |
gradient_noise_scale |
Running noise-scale proxy for total gradient norm | both | exact |
runtime_v1 |
grad_norm_{group} |
Gradient norm per discovered parameter group | both | exact |
runtime_v1 |
grad_norm_{group}_window_var |
Running variance of grad_norm_{group} |
both | exact |
runtime_v1 |
grad_norm_{group}_gns |
Running noise-scale proxy of grad_norm_{group} |
both | exact |
runtime_v1 |
update_active_{group} |
Binary activity flag for a group | both | exact |
runtime_v1 |
update_ratio_{group} |
Relative parameter update magnitude for a group | both | exact |
runtime_v1 |
The six fixed global gradient statistics are grad_norm_total,
grad_abs_min, grad_abs_max, grad_zero_ratio, gradient_vanish,
and gradient_explode. Update ratios are reported per parameter group
(update_ratio_{group}) rather than as a global total, which keeps the
optimization group at 21 features (5 components x 3 plus 6 global). The
earlier update_ratio_total key was removed for this reason.
Sampled attention-layer patterns are part of the schema. {layer_idx}
is sampled using the runtime inspector strategy.
| Metric or pattern | Meaning | Availability | Status | Scope |
|---|---|---|---|---|
L{layer_idx}_attention_entropy_mean |
Mean attention entropy in a sampled layer | both | exact |
runtime_v1 |
L{layer_idx}_attention_entropy_std |
Entropy spread across heads | both | exact |
runtime_v1 |
L{layer_idx}_attention_max_mean |
Mean max attention weight | both | exact |
runtime_v1 |
L{layer_idx}_attention_max_std |
Spread of max attention weights | both | exact |
runtime_v1 |
L{layer_idx}_attention_sparsity |
Fraction of low-mass attention values | both | exact |
runtime_v1 |
L{layer_idx}_attention_weight_magnitude |
Mean attention mass magnitude | both | exact |
runtime_v1 |
L{layer_idx}_attention_mass_pad_mean / ..._mass_pad_max |
Mass assigned to padded positions | both | exact |
runtime_v1 |
L{layer_idx}_attention_mass_leak / ..._mass_leak_max |
Cross-example leak mass | both | exact |
runtime_v1 |
L{layer_idx}_attention_cross_example_leak |
Cross-example leak indicator | both | exact |
runtime_v1 |
L{layer_idx}_attention_mass_future |
Future-position attention mass | both | exact |
runtime_v1 |
L{layer_idx}_attention_mass_special_mean / ..._mass_special_std |
Special-token attention mass | both | exact |
runtime_v1 |
L{layer_idx}_head_similarity_mean / ..._std / ..._max |
Head-pattern similarity | both | exact |
runtime_v1 |
L{layer_idx}_positional_recv_mean / ..._var / ..._skew / ..._early / ..._mid / ..._late / ..._mid_over_early / ..._late_over_early |
Positional receiving profile from attention maps | both | exact |
runtime_v1 |
L{layer_idx}_pre_softmax_score_mean / ..._var / ..._skew / ..._kurt |
QK score statistics, computed from captured Q/K projection outputs | both | exact |
runtime_v1 |
L{layer_idx}_qkv_alignment_qk_cos_mean / ..._qv_cos_mean / ..._kv_cos_mean |
Direct Q-K, Q-V, K-V head-averaged cosine similarity from post-projection captures | both | exact |
runtime_v1 |
Global aliases: attention_entropy, attention_entropy_mean, mass_pad, mass_leak, cross_example_attention, attention_mass_future, pre_softmax_score_mean, pre_softmax_score_var, pre_softmax_score_skew, pre_softmax_score_kurt, head_similarity_mean, head_similarity_max, qkv_alignment_qk_cos_mean, qkv_alignment_qv_cos_mean, qkv_alignment_kv_cos_mean |
Aggregated across sampled layers | both | exact |
runtime_v1 |
| Metric | Meaning | Availability | Status | Scope |
|---|---|---|---|---|
qkv_alignment_qk_cos_mean / qkv_alignment_qv_cos_mean / qkv_alignment_kv_cos_mean |
Head-averaged cosine similarity between captured post-projection Q, K, V tensors | both | exact |
runtime_v1 |
QKV alignment is wired through the sublayer hooks installed by
SublayerCapture (extraction/sublayer_capture.py). The post-projection
Q, K, V tensors are tapped via forward hooks on the per-layer
projection nn.Linear modules. Per-layer cosines are emitted with the
L{layer_idx}_qkv_alignment_* prefix and rolled up into the three
global aliases above.
| Metric or pattern | Meaning | Availability | Status | Scope |
|---|---|---|---|---|
ffn_delta_l{layer_idx}_mean |
Norm of FFN-sublayer-induced hidden-state delta (FFN output minus FFN input, captured via sublayer hooks) | both | exact |
runtime_v1 |
residual_cos_l{layer_idx}_mean |
Cosine similarity between FFN-sublayer input and output | both | exact |
runtime_v1 |
ffn_var_ratio_l{layer_idx} |
FFN output / input variance ratio | both | exact |
runtime_v1 |
ln_std_l{layer_idx}_mean |
Standard deviation of LayerNorm output, read from the per-layer LN forward hook | both | exact |
runtime_v1 |
ln_mean_abs_l{layer_idx}_mean |
Mean absolute value of LayerNorm output, read from the per-layer LN forward hook | both | exact |
runtime_v1 |
ffn_active_dim_frac_l{layer_idx} |
Fraction of active FFN-output dimensions (variance > threshold) | both | exact |
runtime_v1 |
ffn_out_skew_l{layer_idx} |
FFN-output skewness | both | exact |
runtime_v1 |
Global aggregates: ffn_delta_mean, residual_cos_mean, ffn_var_ratio_mean, ln_std_mean, ln_mean_abs_mean, ffn_active_dim_frac_mean, ffn_out_skew_mean |
Aggregated across layerwise structural metrics | both | exact |
runtime_v1 |
embedding_norm_mean, embedding_norm_std |
Token embedding norm statistics | both | exact |
runtime_v1 |
h1_delta_norm_mean |
First-layer hidden drift | both | reconstructed |
runtime_v1 |
| Metric | Meaning | Availability | Status | Scope |
|---|---|---|---|---|
accuracy, f1_score, precision, recall |
Task performance metrics | both | exact |
runtime_v1 |
logit_nan_ratio, logit_inf_ratio |
Numerical health of logits | both | exact |
runtime_v1 |
nll |
Negative log-likelihood / CE loss over logits | both | exact |
runtime_v1 |
ece |
Expected calibration error | both | exact |
runtime_v1 |
logit_entropy |
Predictive entropy | both | exact |
runtime_v1 |
logit_confidence_mean |
Mean max-class confidence | both | exact |
runtime_v1 |
logit_kl_uniform |
Divergence from a uniform predictive distribution | both | exact |
runtime_v1 |
logit_margin_mean, logit_margin_var, logit_margin_p25, logit_margin_p50, logit_margin_p75, logit_margin_min |
Margin-based confidence diagnostics | both | exact |
runtime_v1 |
| Metric | Meaning | Availability | Status | Scope |
|---|---|---|---|---|
positional_accuracy_early, positional_accuracy_late, positional_accuracy_delta |
Early / late positional accuracy sensitivity | both | exact |
runtime_v1 |
positional_margin_early, positional_margin_late, positional_margin_delta |
Early / late positional margin sensitivity | both | exact |
runtime_v1 |
positional_loss_early, positional_loss_late |
Early / late positional loss sensitivity | both | exact |
runtime_v1 |
| Metric | Meaning | Availability | Status | Scope |
|---|---|---|---|---|
cache_hidden_sim |
Consecutive KV-key similarity proxy | decoder | approximate |
runtime_v1 |
cache_nll_divergence |
Mean symmetric KL between fresh and cached next-token distributions, sampled at a few positions per probe step | decoder | exact |
runtime_v1 |
The runtime feature schema above is the v1 interface that downstream
diagnostic models pin against. The benchmark construction pipeline
(under defaultplusplus.benchmark/, exposed via defaultpp-benchmark)
emits two classes of labeled dataset row.
- Faulty class. One row per killed mutant, from paired clean /
faulty fine-tunes scored with the exact one-sided sign-flip permutation
test in
deform.validation.is_killed. The row carriesdetection_label = 1plus the fault category and root-cause labels. - Correct class. One row per retained label-preserving clean
variant of a base model, generated by
deform.generate_clean_variantsand run bydeform.run_one_clean_variant. A variant varies only the seed (which reshuffles the training-data order) and hyperparameters within behavior-preserving ranges, then is tested against the base model with the same kill test. A variant that stays statistically indistinguishable from the base model is retained withdetection_label = 0and empty fault labels; one that satisfies the killed criterion is discarded. The--clean-variants Nflag ondefaultpp-benchmarksets how many variants to generate per (model, task).
The two classes are symmetric by construction: the faulty path keeps killed mutants, and the correct path keeps clean variants that are not killed.
The sign-flip test consumes one scalar per (clean, faulty) seed pair.
For each task the scalar is fixed by
benchmark.task_metrics.TASK_METRICS
and follows the standard reporting convention so kill decisions are
comparable to the literature:
| Task | Arch | Composite | higher_is_better |
|---|---|---|---|
sst2 |
encoder | accuracy | true |
qnli |
encoder | accuracy | true |
rte |
encoder | accuracy | true |
mnli |
encoder | matched accuracy | true |
cola |
encoder | Matthews correlation | true |
mrpc |
encoder | (accuracy + F1) / 2 | true |
qqp |
encoder | (accuracy + F1) / 2 | true |
stsb |
encoder | (Pearson + Spearman) / 2 | true |
lambada |
decoder | log-perplexity | false |
ptb |
decoder | log-perplexity | false |
wikitext2 |
decoder | log-perplexity | false |
openwebtext |
decoder | log-perplexity | false |
Adding a new task is a one-entry registration in
benchmark/task_metrics.py. The TaskMetricSpec declares which raw
metrics the HF Trainer's compute_metrics callable must emit and how
they collapse into the kill-test scalar. The runner uses the spec's
higher_is_better per configuration, so encoder + decoder tasks can
mix in a single CLI invocation.
The n=5 matched-seed design is fixed by the paper: it is the smallest n that admits an exact one-sided sign-flip test at α=0.05 (minimum p-value 1/2^5 ≈ 0.031). The runner never aggregates a partial set of seeds, so the kill-test guarantee is preserved for every dataset row.
A benchmark batch touches dozens of operators per model; some of them genuinely break the model (NaN logits, OOM, shape mismatch from a fault that the structural verifier should otherwise catch). The runner never lets a single configuration take down the whole batch. Three discard paths are recognized:
RunStatus |
When |
|---|---|
verifier_failed |
the pre-flight StructuralVerifier reports ok=False (no targets, restoration broken, dynamic wrap leaked outside the intended set) |
runtime_error |
the faulty fine-tune raises an exception on any seed |
invalid_metric |
any seed returns NaN or ±Inf for the test metric |
Discarded configurations carry mutant=None and a human-readable
discard_reason. The CLI skips them from the output CSV and writes
one JSON record per discard to <output>.discarded.jsonl so the
batch leaves an audit trail and the failing operators can be
revisited later. Clean-run failures bubble up — they are
environment problems (dataset missing, model wouldn't load) rather
than faults, and the operator running the benchmark needs to see
them.
The v1 surface is complete. The runtime extractor, the diagnostic model, the benchmark pipeline, and their distribution paths all ship. This section records what v1 covers and the directions a v2 could extend.
The schema in Section 2 is complete for v1. Every metric is exact
except the two noted in the tables. The legacy attention_score_*
log-prob proxy was replaced by the exact pre_softmax_score_* family
produced via the sublayer hooks.
- Single-run anomaly encoding.
RuntimeNormalizer(processing/normalizer.py) loads a learned clean reference and converts one live run's feature vector into the shape the diagnostic model expects. - Pretrained diagnostic-model weights. The
defaultplusplus.diagnosisAPI (load_pretrained,Predictor,save_checkpoint) and the trained.ptcheckpoints for the encoder and decoder models ship in the wheel underpretrained/weights/{encoder,decoder}.pt, with matching{encoder,decoder}_reference.npzruntime references. - Benchmark pipeline. The
defaultpp-benchmarkconsole script (benchmark.cli) runs paired clean / faulty fine-tunes with crash isolation and the per-task metric registry. Per-operator injectors underdeform/operator_impls/cover all 52 catalog entries. - Public dataset distribution.
defaultplusplus.data.download_benchand thedefaultpp-bench-downloadconsole script fetch the benchmark CSVs from Zenodo with checksum verification (the CSVs are multiple gigabytes and do not ship in the wheel).
The families below are deliberately out of scope for v1. Supporting them is a natural direction for future work rather than missing functionality.
- Encoder-decoder architectures (T5, BART). They require two-stream attention metrics that the current schema does not represent.
- Sparse-attention variants (Longformer, BigBird) and Mixture-of-Experts routing diagnostics.
- Distributed-training-specific signals (gradient sync drift, parameter-server staleness).
Schema version follows the package version (defaultplusplus.__version__).
- A MAJOR bump removes or renames a
runtime_v1entry. This invalidates pre-trained diagnostic-model weights. - A MINOR bump promotes an entry from
reconstructed/approximate/not_availabletoexact, or adds a new entry whose absence would have been silently ignored. - A PATCH bump fixes a metric implementation without changing its output schema.
Changes to this document must be logged in
../defaultplusplus/CHANGELOG.md.