Downstream Benchmarking¶
After applying circuit-guided interventions (pruning, quantization), CircuitKit integrates with lm-evaluation-harness to measure downstream task performance.
Requires: pip install -e ".[benchmarks]" — installs lm-eval from GitHub and datasets>=2.20.
Quick Start¶
import circuitkit as ck
# After prune + export
scores = ck.benchmark(
"./output/pruned_checkpoint", # path to HF checkpoint
tasks=["boolq", "winogrande"], # lm-eval task names
limit=100, # cap examples per task
)
for task, metrics in scores.items():
print(f"{task}: {metrics['acc,none']:.3f}")
Via Pipeline:
pipe.discover(algorithm="eap-ig", n_examples=128, sparsity=0.3)
pipe.prune(sparsity=0.3)
pipe.export("./output/checkpoint")
pipe.benchmark(tasks=["boolq", "winogrande", "hellaswag"], limit=100)
Via CLI:
There is no --checkpoint or --limit flag on circuitkit benchmark — that command runs a discovery + intervention + baseline comparison sweep, not an lm-eval-harness run on a checkpoint (see CLI Reference). To benchmark an exported checkpoint with lm-eval-harness, use the Python API shown above (ck.benchmark()).
Available Benchmark Tasks¶
Any lm-eval task name is valid. Common choices:
| Task | Description | Metric |
|---|---|---|
boolq |
Boolean QA | Accuracy |
winogrande |
Coreference resolution | Accuracy |
hellaswag |
Commonsense NLI | Accuracy (normalized) |
mmlu |
Massive multitask language understanding | Accuracy |
arc_easy, arc_challenge |
ARC Science QA | Accuracy |
truthfulqa_mc |
TruthfulQA | MC accuracy |
gsm8k |
Math word problems | Exact match |
wmdp_bio, wmdp_chem, wmdp_cyber |
Hazardous knowledge (unlearning benchmarks) | Accuracy |
Interpreting Results¶
Benchmark results measure extrinsic performance — whether the pruned/quantized model still performs well on tasks beyond the one the circuit was discovered for.
What to look for:
-
Baseline vs. pruned comparison: Run
ck.benchmarkon the original model and the pruned model. The gap is the cost of circuit-guided compression. -
Target task vs. transfer tasks: If you discovered a circuit for IOI and pruned at 30% sparsity, benchmark on both IOI (within-task) and BoolQ, Winogrande (out-of-task). A good circuit-guided compression should degrade transfer tasks less than random pruning.
-
Circuit vs. random pruning: Compare
ck.benchmarkafter circuit-guided pruning vs. random pruning at the same sparsity. The CircuitKit paper shows circuit-guided pruning retains ~5-15% more accuracy on unrelated tasks.
Full Benchmarking Workflow¶
import circuitkit as ck
model = ck.load_model("gpt2", dtype="float32")
circuit = ck.load_scores("./circuit.pt")
# 1. Baseline (unpruned)
ck.export_checkpoint(model, None, "./output/baseline")
baseline = ck.benchmark("./output/baseline", tasks=["boolq", "winogrande"], limit=200)
# 2. Circuit-guided pruning at 30%
pruned = ck.prune(model, circuit, sparsity=0.3, scope="heads")
ck.export_checkpoint(pruned, circuit, "./output/pruned_0.3")
pruned_30 = ck.benchmark("./output/pruned_0.3", tasks=["boolq", "winogrande"], limit=200)
# 3. Print comparison
for task in ["boolq", "winogrande"]:
b = baseline[task]["acc,none"]
p = pruned_30[task]["acc,none"]
print(f"{task}: baseline={b:.3f}, pruned_30={p:.3f}, drop={b-p:.3f}")
PEFT Benchmarking¶
For circuit-restricted fine-tuning, CircuitKit includes a PEFT benchmark utility:
from circuitkit.applications.finetuning.benchmark_peft import PEFTBenchmark
bench = PEFTBenchmark(model, method="lora", rank=8, device="cuda")
metrics = bench.run(num_batches=50, batch_size=8)
print(f"Parameter efficiency: {metrics.param_efficiency:.2%}")
print(f"Memory footprint: {metrics.peak_memory_mb:.0f} MB")
print(f"Throughput: {metrics.batches_per_second:.2f} batches/sec")
Next Steps¶
- Applications — pruning and quantization workflows
- CLI Reference: Applications —
benchmarkCLI command - Framework Overview — faithfulness evaluation before benchmarking