Skip to main content

Hierarchical fault diagnosis and runtime feature extraction for HuggingFace transformers

Project description

defaultplusplus

CI Dataset DOI

Hierarchical fault diagnosis and runtime feature extraction for HuggingFace transformers.

This document covers the installable Python package: API surface, examples, and the build / publish workflow. For the research-side reproduction, training, and benchmark-construction flow see RESEARCH.md. The frozen output schema is in docs/SPEC.md.

Install

pip install defaultplusplus           # core runtime feature extractor
pip install defaultplusplus[hf]       # + HuggingFace Trainer callback
pip install defaultplusplus[viz]      # + matplotlib / seaborn / rich
pip install defaultplusplus[baselines]# + xgboost / imbalanced-learn
pip install defaultplusplus[dev]      # + pytest / build / twine / ruff
pip install defaultplusplus[all]      # hf + viz + baselines

Editable install from a local checkout:

cd defaultplusplus
pip install -e ".[dev,hf]"

Public API

from defaultplusplus import (
    FeatureExtractor,            # manual training loop
    DEFaultPlusCallback,         # HuggingFace Trainer callback
    ExtractionConfig,            # tunable thresholds and sampling cadence
    build_feature_vector,        # TrainingTrace -> fixed-length dict
    build_paired_feature_vector, # paired clean/faulty traces (research only)
)

from defaultplusplus.diagnosis import (
    load_pretrained,             # arch -> Predictor (raises if no weights)
    Predictor,                   # .predict(features) -> Diagnosis
    Diagnosis,                   # 3-level result dataclass
)

DEFaultPlusCallback is resolved lazily so the package itself imports without the transformers extra.

Three-level diagnosis

from defaultplusplus import FeatureExtractor
from defaultplusplus.diagnosis import load_pretrained

with FeatureExtractor(model, arch="encoder") as fx:
    # ... your training loop calls fx.step(...) and fx.epoch_end(...) ...
    features = fx.finalize()

predictor = load_pretrained("encoder")            # ships in the wheel
diagnosis = predictor.predict(features)
print(diagnosis.to_dict())
# {
#   'is_faulty':       True,
#   'detection_prob':  0.92,
#   'category':        'qkv',
#   'category_prob':   0.81,
#   'root_cause':      'zero_query',
#   'root_cause_prob': 0.74,
#   'group_importance': {'qkv_alignment': 3.2, 'attention': 1.7, ...},
# }

The Predictor validates the live feature_names schema against the one bundled in the checkpoint, so a model trained against schema X refuses to score features built against schema Y.

Pretrained weights ship inside the wheel under defaultplusplus/pretrained/weights/{encoder,decoder}.pt, with matching {encoder,decoder}_reference.npz runtime references for RuntimeNormalizer. Train your own from the public benchmark with scripts/train_diagnoser.py if you want to reproduce or fine-tune.

Single-run anomaly encoding

The diagnostic model was trained against a paper-aligned feature schema. At runtime, RuntimeNormalizer turns a live extractor's dict into the exact schema the model expects:

from defaultplusplus.processing import RuntimeNormalizer
from defaultplusplus.diagnosis import load_pretrained

norm = RuntimeNormalizer.load("encoder")
predictor = load_pretrained("encoder")
encoded = norm.encode(features, mode="raw")    # fills missing keys with baseline median
diagnosis = predictor.predict(encoded)

Pass mode="anomaly" for (value − median) / mad z-scores instead.

Visualization

The [viz] extra adds a self-contained HTML report writer plus seven matplotlib plot functions:

from defaultplusplus.viz import save_diagnosis_report
save_diagnosis_report(diagnosis, encoded, "run.html")  # standalone HTML

Individual plots (plot_diagnosis, plot_group_importance, plot_per_layer_heatmap, plot_training_trace, plot_attention_pattern, plot_qkv_alignment, plot_feature_anomaly) return matplotlib.Figure objects.

Public benchmark dataset

The training data CSVs (~360 MB) live on Zenodo, not in the wheel. Fetch them on demand:

defaultpp-bench-download                        # ~/.cache/defaultplusplus/bench/v1

Or programmatically:

from defaultplusplus.data import download_bench
path = download_bench(version="v1")             # checksum-verified, idempotent

DOI: 10.5281/zenodo.20018623.

FeatureExtractor lifecycle

fx = FeatureExtractor(model, arch="encoder")  # auto-detects encoder vs decoder
fx.step(loss=..., outputs=..., input_ids=..., attention_mask=...,
        labels=..., optimizer=..., step_time=None)        # once per training step
fx.epoch_end(epoch)                                       # once per epoch
fx.record_validation(epoch, {"accuracy": 0.85, ...})      # at eval checkpoints
features = fx.finalize()                                  # flat dict[str, float]
fx.to_json("features.json")                               # convenience
fx.reset()                                                # discard state for reuse

The class is also a context manager:

with FeatureExtractor(model, arch="encoder") as fx:
    ...
# fx.feature_vector is populated on clean exit

Architecture support

Family Examples
encoder BERT, RoBERTa, DistilBERT, ALBERT-style
decoder GPT-2, DistilGPT-2, GPT-Neo

arch is auto-detected from the model's structure. A wrong hint fails closed:

FeatureExtractor(bert_model, arch="decoder")
# ValueError: Requested arch='decoder' but the inspector detected 'encoder'.

Encoder-decoder architectures (T5, BART) are out of scope for v1.

Supported benchmark tasks

The kill-test scoring is registry-driven (one TaskMetricSpec per task). The composite per task follows the standard GLUE / LM reporting convention so kill decisions are comparable to the literature:

Task Arch Composite Direction
sst2 encoder accuracy
qnli encoder accuracy
rte encoder accuracy
mnli encoder accuracy (matched validation)
cola encoder Matthews correlation
mrpc encoder (accuracy + F1) / 2
qqp encoder (accuracy + F1) / 2
stsb encoder (Pearson + Spearman) / 2
wikitext2 decoder eval_loss

Adding a new task means registering one TaskMetricSpec in benchmark/task_metrics.py; nothing else in the runner / CLI changes.

Output keys

All output keys are frozen by docs/SPEC.md. The vector is a flat dict[str, float] covering:

  • training dynamics: train_loss, train_learning_rate, runtime_step_time, ...
  • gradient and update behavior: grad_norm_total, grad_norm_{group}, update_ratio_{group}, ...
  • attention and score signals: L{n}_attention_entropy_mean, L{n}_attention_mass_pad_mean, ...
  • structural / FFN / residual / LayerNorm behavior
  • logit and task signals: accuracy, f1_score, logit_entropy, nll, ...
  • positional behavior, cache behavior (decoder)

Examples

File Path What it shows
Manual loop examples/extract_during_finetune.py DistilBERT + synthetic SST-2
HF Trainer examples/extract_with_hf_trainer.py compute_loss override + callback

Run them after installing the [hf] extra:

pip install -e ".[hf]"
python examples/extract_during_finetune.py
python examples/extract_with_hf_trainer.py

Benchmark CLI

Once [hf] is installed, defaultpp-benchmark runs a paired clean / faulty fine-tune for every (model × task × operator × severity × seed-tuple) configuration and writes one CSV row per killed mutant. The kill test uses the per-task scalar from the metric registry (see Supported benchmark tasks above).

defaultpp-benchmark \
  --arch encoder \
  --models bert-base-uncased \
  --tasks sst2 \
  --operators QZQ,FCA \
  --severities low \
  --seeds 42,123,456,789,101112 \
  --output data/encoder_benchmark.csv

A configuration is discarded (not crashed) when:

  • the pre-flight StructuralVerifier reports the injector targets no parameters or fails to restore on exit,
  • the faulty fine-tune raises an exception on any seed, or
  • any seed returns a non-finite metric (NaN / ±Inf).

Discarded configs are skipped from the CSV and recorded in <output>.discarded.jsonl (one record per line) so they can be revisited later. The CLI prints a summary at the end:

[defaultpp-benchmark] wrote 124/130 row(s) to data/encoder_benchmark.csv; 6 discarded
[defaultpp-benchmark] discard summary: runtime_error=4, verifier_failed=2

Layout

defaultplusplus/
  src/defaultplusplus/         the installable package
    __init__.py                FeatureExtractor, DEFaultPlusCallback, etc.
    _version.py                single source of truth for __version__
    api.py                     FeatureExtractor (manual loop)
    hf_callback.py             DEFaultPlusCallback (HF Trainer)
    config.py                  ExtractionConfig dataclass
    py.typed                   PEP 561 marker
    extraction/                metric collection + aggregation
      inspector.py             auto-discovers transformer structure
      collector.py             orchestrates per-step metric modules
      aggregator.py            Welford running statistics
      sublayer_capture.py      forward hooks for FFN/LN/Q/K/V taps
      feature_construction.py  layer / step / epoch / phase aggregation
      metrics/                 attention, gradient, logit, structural, ...
    deform/                    mutation engine (research)
      operators.py             45 mutation operators (catalog)
      operator_impls/          per-operator injector implementations
      injection.py             StaticFault / DynamicFault context managers
      validation.py            structural verifier + sign-flip kill test
      fault_config.py          FaultConfiguration / Mutant types
    benchmark/                 benchmark construction (research)
      cli.py                   defaultpp-benchmark entry point
      config_grid.py
      runner.py                paired runs + crash isolation
      task_metrics.py          per-task kill-test metric registry
      dataset_writer.py
    diagnosis/                 runtime Predictor + load_pretrained()
      model.py                 HierarchicalDiagnosisModel
      _group_encoder.py        GroupEncoder + GraphAggregator
      predictor.py             Predictor, save_checkpoint, load_pretrained
    processing/                FeatureProcessor + RuntimeNormalizer
      feature_processor.py     6-step preprocessing pipeline
      feature_groups.py        feature → group routing
      normalizer.py            single-run anomaly encoding
    pretrained/                shipped diagnostic-model checkpoints
      weights/encoder.pt       trained encoder diagnoser
      weights/decoder.pt       trained decoder diagnoser
      weights/*.npz            RuntimeNormalizer baseline references
    data/                      benchmark download (defaultpp-bench-download)
    viz/                       matplotlib plots + HTML report writers
    ui/                        reserved for future CLI helpers

  hierarchical_graph_category_rootcause/  research-side training drivers
                                          (see RESEARCH.md)
  configs/base.yaml                       hyperparameter config
  examples/                               runnable demos
  scripts/                                local + Compute Canada scripts
  tests/                                  pytest suite (201 tests)
  pyproject.toml                          PEP 621 metadata + build config
  LICENSE                                 Apache-2.0
  CHANGELOG.md                            version history
  README.md                               this file
  RESEARCH.md                             research-side runbook

Build and publish

The package follows the standard PEP 517 / PEP 621 layout. Building and uploading uses build + twine.

Bump the version

Update src/defaultplusplus/_version.py following Semantic Versioning (MAJOR.MINOR.PATCH). Add an entry at the top of CHANGELOG.md. The version field in pyproject.toml is dynamic and reads from _version.py so the two cannot drift apart.

Build locally

bash scripts/build_pypi.sh

Cleans dist/, runs python -m build (sdist + wheel), and validates with twine check.

Publish to TestPyPI

bash scripts/build_pypi.sh --testpypi
pip install --index-url https://test.pypi.org/simple/ \
            --extra-index-url https://pypi.org/simple/ \
            defaultplusplus

Authentication uses the standard twine config: ~/.pypirc or TWINE_USERNAME=__token__ + TWINE_PASSWORD=<api-token>.

Publish to PyPI

bash scripts/build_pypi.sh --pypi
pip install --upgrade defaultplusplus

A version on PyPI is permanent; bump before re-uploading.

Tests

pytest tests/                # full suite
pytest tests/test_dry_run.py # smoke suite (~1s, no real models)
pytest tests/test_feature_extractor.py  # uses real DistilBERT/GPT-2 (~15s)
bash scripts/dry_run_local.sh --quick   # imports + smoke + FPG sanity

Dependencies

Required at runtime:

  • Python ≥ 3.10
  • torch ≥ 2.1
  • transformers ≥ 4.30
  • numpy, pandas, scipy, scikit-learn, pyyaml, tqdm, joblib

Optional extras: hf (accelerate, datasets), viz (matplotlib, seaborn, rich), baselines (xgboost, imbalanced-learn), dev (pytest, build, twine, ruff).

Versioning

Semantic Versioning 2.0:

  • MAJOR for incompatible API changes (removing a public symbol, changing a function signature in a non-additive way).
  • MINOR for backwards-compatible additions.
  • PATCH for backwards-compatible bug fixes.

The current version is read from src/defaultplusplus/_version.py and surfaced as defaultplusplus.__version__.

License

Apache-2.0. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

defaultplusplus-0.4.1.tar.gz (4.5 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

defaultplusplus-0.4.1-py3-none-any.whl (4.5 MB view details)

Uploaded Python 3

File details

Details for the file defaultplusplus-0.4.1.tar.gz.

File metadata

  • Download URL: defaultplusplus-0.4.1.tar.gz
  • Upload date:
  • Size: 4.5 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for defaultplusplus-0.4.1.tar.gz
Algorithm Hash digest
SHA256 79895ee2587d9010803dd990f5cc19a2face8c65c9d1a7792c9ad612dae4ebeb
MD5 16162a5472a128723fbfd083feb0b32e
BLAKE2b-256 e2098657a160889636333a7d1712cfa2963a2ffe679748be5d25cc1148cdf174

See more details on using hashes here.

File details

Details for the file defaultplusplus-0.4.1-py3-none-any.whl.

File metadata

File hashes

Hashes for defaultplusplus-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5d09d5cc0d1d862804dba6e78cd086549d7ef5229b581c512a20b032934206a3
MD5 4422bc28b11b74adf0cce6d0845a17b3
BLAKE2b-256 f25951fe885be99adebd83affae70e1d1b102b247fd9795091b1eed410025862

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page