Skip to main content

Documentation 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.

The core methodological contribution of this framework is treating inherited backdoor failure as a continuous degradation process rather than a binary event. Instead of asking “does the backdoor survive fine-tuning?” — which produces a single yes/no verdict — the framework tracks how attack capability and model utility evolve at each stage of a progressive intervention. This produces a full degradation curve that reveals when a backdoor collapses, how quickly it degrades, and whether residual attack capability persists even after substantial clean fine-tuning.

The Evaluation Pipeline

Each experiment follows the same structure: a backdoored model is subjected to progressively stronger clean fine-tuning, and all four metrics are collected after every stage.
Backdoored Model


Progressive Clean Fine-Tuning
0% → 20% → 40% → 60% → 80% → 100%


Evaluate After Every Intervention


Continuous Degradation Curves
At ratio 0.0, no fine-tuning is applied. This is the baseline measurement — the model’s behavior immediately after backdoor injection, before any clean data has been introduced. Every subsequent ratio represents a stronger intervention, and all metrics at those stages are measured relative to this baseline.

Progressive Clean Fine-Tuning

The intervention is implemented in src/interventions.py by apply_clean_intervention. At each ratio r, a random subset of size r × |clean_dataset| is drawn from the clean training set, the model is fine-tuned on that subset, and metrics are collected on both the clean and triggered evaluation sets.
def apply_clean_intervention(
    model: PreTrainedModel,
    clean_dataset: Dataset,
    tokenizer,
    ratio: float,
    output_dir: str,
    epochs: int,
    learning_rate: float,
    batch_size: int,
    weight_decay: float,
    seed: int,
) -> PreTrainedModel:
    if ratio == 0:
        return deepcopy(model)

    subset_size = int(len(clean_dataset) * ratio)

    clean_subset = (
        clean_dataset
        .shuffle(seed=seed)
        .select(range(subset_size))
    )
    ...
The six intervention ratios in the baseline configuration are defined in configs/baseline.yaml:
intervention:
  clean_ratios:
    - 0.0
    - 0.2
    - 0.4
    - 0.6
    - 0.8
    - 1.0
  epochs: 2
Each fine-tuning stage operates on an independent deep copy of the backdoored model, so the stages are not cumulative — ratio 0.4 always means fine-tuning on 40% of the clean data starting from the backdoored checkpoint, not on top of the ratio 0.2 result.

Two Evaluation Datasets at Every Stage

After each intervention, the model is evaluated on two separate datasets:
The clean evaluation set contains un-triggered examples drawn from the standard SST-2 test split. Predictions are compared against ground-truth labels using compute_clean_accuracy.This measures whether the downstream task performance is preserved as the intervention grows stronger. A large drop in clean accuracy would indicate that the fine-tuning intervention is harming the model’s utility — an important practical constraint, since a defense that destroys utility is not deployable.

Intervention Strength vs. Data Efficiency

Different experiments may begin with different baseline ASR values — a stronger or weaker backdoor, a different poison rate, a different trigger. Raw ASR numbers are therefore not directly comparable across experiments. The retention ratio addresses this by normalizing all measurements against the baseline:
retention_ratio(t) = ASR(t) / ASR(0)
A retention ratio of 1.0 means the backdoor is as effective as it was at injection. A value of 0.5 means half of the original attack capability remains. This normalization makes degradation curves directly comparable across experiments with different initial backdoor strengths, enabling fair cross-experiment analysis. The compute_retention_ratio function handles the edge case where the baseline ASR is zero:
def compute_retention_ratio(
    attack_success_rate: float,
    baseline_asr: float,
) -> float:
    if baseline_asr == 0:
        return 0.0
    return attack_success_rate / baseline_asr

Reviewer Intervention

The configs/baseline.yaml configuration includes a reviewer block that specifies a secondary intervention using a smaller model:
reviewer:
  enabled: true
  checkpoint_ratio: 0.4
  model: prajjwal1/bert-tiny
  epochs: 2
The reviewer intervention is designed to apply at a specific point in the degradation curve — checkpoint_ratio: 0.4 — using a compact BERT-tiny model as the intervening agent. This makes it possible to study whether a lightweight reviewer model can accelerate backdoor removal at the stage where the primary fine-tuning intervention has partially degraded the backdoor but not eliminated it.
The reviewer intervention module is currently scaffolded for future implementation. The apply_reviewer_intervention function in src/interventions.py is defined as a NotImplementedError placeholder and will be completed in a subsequent development phase.

Modular Research Dimensions

The framework separates attacks, models, datasets, interventions, and evaluation methodologies into independent modules — each representing a distinct research dimension. This means you can swap in a new backdoor attack family without touching the evaluation code, or introduce a new intervention strategy without modifying the metric computation. Experiments that differ in only one dimension produce results that are directly comparable, supporting systematic ablation studies.

Build docs developers (and LLMs) love