Reproduce¶
Every published number comes from scripts/run_benchmarks.py. The full run is heavy (MalwareBazaar
CSV, TLSH search over ~640k digests), so the canonical run is the
bench workflow on a GitHub
ubuntu-24.04 runner (weekly, or on dispatch). The committed results come from bench run
37092539472 on commit 2d7bacc; results/RESULTS.md and results/benchmark.json
(provenance) name that run, and the run's artifact holds the same files plus compare.txt,
time.txt and the dataset manifest.
Each bench run:
- downloads the datasets (pinned sources are SHA-256 checked),
- runs the real-data tests and the README real-data quickstart verbatim (build-kg, case-study,
assess --kg, export-cypher, serve +
POST /assesson 127.0.0.1), - runs every benchmark section with
PYTHONHASHSEED=0, - runs
scripts/compare_results.pyagainst the committedresults/benchmark.json: every value of the deterministic sections must match (numbers to 1e-9, strings exactly, rows matched by method or case name), otherwise the step - and the run - fails.
Locally¶
| step | command | time / size | expected output |
|---|---|---|---|
| 1. data | python scripts/download_data.py |
~2-5 min, ~640 MB in $DRAGNET_DATA (default data/raw) |
[get] / [have] per source; pinned sources are SHA-256 checked and listed in data/MANIFEST.json |
| 2. graph | python -m dragnet build-kg |
~20 s | kg-attack-19.2.json with 176 groups, 1600 signal keys |
| 3. light sections | PYTHONHASHSEED=0 python scripts/run_benchmarks.py --only A1,A1LF,A2,A3,C,D,F,G |
a few minutes, < 1 GB RAM | merges into results/benchmark.json |
| 4. full run | PYTHONHASHSEED=0 python scripts/run_benchmarks.py --fresh --out fresh |
1045 s wall clock on the runner (single-threaded), peak RSS 968 MiB (time.txt of run 37092539472); needs .[bench] (numpy, matplotlib) |
a complete fresh/RESULTS.md |
| 5. compare | python scripts/compare_results.py results/benchmark.json fresh/benchmark.json |
seconds | [ok] for every deterministic section, exit 0 |
| 6. re-render | python scripts/run_benchmarks.py --render-only |
seconds | results/RESULTS.md and docs/figures/ from results/benchmark.json |
The R section alone takes about 12 minutes on a laptop (it runs 11 methods over 637 cases in 5 folds plus two temporal graphs, and its paired sign-flip tests use B = 200,000 Monte Carlo flips).
export DRAGNET_DATA="$PWD/data/raw" # PowerShell: $env:DRAGNET_DATA = "$PWD\data\raw"
python -m pip install -e ".[dev,bench]"
python scripts/download_data.py
python -m dragnet build-kg
PYTHONHASHSEED=0 python scripts/run_benchmarks.py --only A1LF,A1,G
Expected key numbers¶
Deterministic sections (ATT&CK, MISP and APTnotes are pinned) must match exactly with
PYTHONHASHSEED=0:
| section | quantity | value |
|---|---|---|
| A1-LF | DRAGNET top-1 | 0.400 [0.200, 0.600] |
| A1-LF | DRAGNET wrong at MEDIUM+ | 0/25 |
| A1 (leaky) | DRAGNET top-1 | 0.680 [0.480, 0.840] |
| R-kfold | DRAGNET top-1 / selective acc. | 0.454 [0.401, 0.507] / 0.853 (n = 637) |
| R-kfold | fusion gain over software-only | +0.095 [0.064, 0.130], p = 5.0e-6 (MC, B = 200,000) |
| R-temporal | DRAGNET top-1 | 0.137 [0.078, 0.214] (n = 219) |
| G | DRAGNET mean rank of 29 | 5.38 +/- 0.79 |
| D level 2 | false-flag rule effect | 0.228 [0.108, 0.368] |
Sections B3, E1, E2, E3 and K use the daily abuse.ch exports and the live Malpedia API, so they drift
from week to week; compare_results.py reports them as [info] and does not compare them.