The baseline experiment trains a backdoored DistilBERT classifier on SST-2, then measures how the embedded backdoor degrades under six progressive stages of clean fine-tuning. All experiment logic lives inDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Antisource/heronaisec/llms.txt
Use this file to discover all available pages before exploring further.
experiments/baseline.py, which is invoked through a single entry point — main.py.
The LOAD_FROM_CHECKPOINT flag
Near the top of experiments/baseline.py, two constants control whether the experiment trains from scratch or reuses previously saved model weights:
LOAD_FROM_CHECKPOINT = False— the experiment trains every model from scratch. Use this for a first run or when you want a fully fresh result.LOAD_FROM_CHECKPOINT = True— the experiment skips training and loads thebaseline_checkpointdirectory and eachcheckpoint_<ratio>directory from a previous run. SetCHECKPOINT_RUNto the timestamped path of that run (relative toresults/).
First-time run (training from scratch)
Set LOAD_FROM_CHECKPOINT = False
Open
experiments/baseline.py and set the flag to False so the experiment trains a fresh model:Optionally configure configs/baseline.yaml
Review
configs/baseline.yaml before running. The defaults use DistilBERT on SST-2 with a 15 % poison rate and a "cf" trigger. See the Configuration guide for a full description of every parameter.Launch the experiment
From the repository root, run:
main.py first reports the available hardware, then calls run_baseline() in experiments/baseline.py:Backdoor injection and initial training
train_backdoored_model() shuffles and selects a 5 000-example subset of the SST-2 training split, then poisons 15 % of those examples using apply_attack() with the "badnet" strategy (prepending the trigger token "cf" and flipping the label to target_label). The poisoned dataset is tokenized and used to fine-tune a freshly loaded distilbert-base-uncased model for three epochs. The trained model is saved to results/<CHECKPOINT_RUN>/checkpoints/baseline/.Progressive intervention across 6 stages
run_progressive_intervention() loops over the six clean_ratios defined in the YAML config — [0.0, 0.2, 0.4, 0.6, 0.8, 1.0] — and for each non-zero ratio applies apply_clean_intervention(). This function selects the corresponding fraction of the clean training set, fine-tunes a deep copy of the baseline model for two epochs, and saves the resulting checkpoint to results/<CHECKPOINT_RUN>/checkpoints/checkpoint_20/ through checkpoint_100/ (named as checkpoint_{ratio × 100}). After each stage the model is evaluated with evaluate_model(), which computes clean_accuracy, attack_success_rate, mean_trigger_confidence, and retention_ratio.Loading from checkpoints
If you have already completed a training run, you can skip retraining entirely by pointing the experiment at the saved checkpoints.Set CHECKPOINT_RUN to a previous run path
Set The experiment constructs the full checkpoint path as:
CHECKPOINT_RUN to the path of the previous run directory inside results/, using the format "<experiment_name>\\<timestamp>" (backslash-separated on Windows):Run python main.py
LOAD_FROM_CHECKPOINT = True, the orchestrator in run_baseline() calls load_model(checkpoint_dir) for the baseline checkpoint and for every ratio checkpoint in the loop, bypassing all training. Evaluation still runs in full, and a new timestamped output directory is created for the results.If any expected checkpoint directory is missing, the experiment raises a FileNotFoundError with the exact missing path before proceeding further.Output directory structure
Every run writes its outputs to a self-contained, timestamped directory:| File | Description |
|---|---|
metrics.csv | Tabular results for every intervention stage: clean_ratio, clean_accuracy, attack_success_rate, mean_trigger_confidence, retention_ratio, and experiment. |
figures/persistence_curve.png | The main publication figure — plots backdoor persistence metrics against clean-data ratio across all six intervention stages. |
figures/reviewer_intervention.png | Annotated version of the persistence curve marking the reviewer checkpoint ratio (default: 0.4). Only generated when reviewer.enabled: true in the YAML config. |
GPU vs CPU
main.py checks for CUDA availability before calling run_baseline():