Vect0rdecay/GraphSurgeon
GitHub: Vect0rdecay/GraphSurgeon
Stars: 0 | Forks: 0
# GraphSurgeon
GraphSurgeon reverse-engineers ONNX computational graphs from the command line or Python. It summarizes inputs and operators, maps depth and execution order, detects structural motifs in the DAG, cross-references adversarial ML literature, narrates data flow, and supports counterfactual edits with validation and diff.
The tool is ONNX-only. It does not require PyTorch or a CUDA toolkit.
## What it does
| Command | Role |
|---------|------|
| `inspect` | Model summary: I/O tensors, initializer count, operator mix |
| `topology` | Graph depth, early/middle/late layer buckets, execution order |
| `patterns` | Coarse structural blocks (conv stacks, attention, normalization chains) |
| `motifs` | Registry-backed structural motifs: which attack classes the graph topology makes architecturally plausible |
| `flow` | Plain-English execution narrative |
| `catalog` | Lookup gadgets, compound chains, literature techniques, and bundled paper notes |
| `operators` | ONNX operator reference keyed to security-relevant behavior |
| `edit` | Counterfactual graph surgery (`remove-node`) with structural, loadable, or runnable validation |
| `diff` | Compare two ONNX files after edits |
Motif hits describe attack landscape (what attack types the architecture enables), not confirmed exploitability. The tool does not assign risk scores or severity tiers.
## Install
cd graph-surgeon
python3 -m venv .venv
.venv/bin/python -m pip install -e ".[dev]"
| Package | Purpose |
|---------|---------|
| `onnx`, `numpy` | Core graph parsing, motifs, topology (always installed) |
| `onnxruntime` | `edit validate --level loadable/runnable` (via `[dev]`) |
| `pytest` | Test suite (via `[dev]`) |
Minimal install (no runtime validation or tests):
.venv/bin/python -m pip install -e .
## CLI
After `pip install -e .`, use the project venv (the `graph-surgeon` command is not on your system PATH unless you activate the venv):
source .venv/bin/activate # optional; then graph-surgeon works on PATH
# or always:
.venv/bin/graph-surgeon --help
.venv/bin/python -m graph_surgeon catalog --coverage
graph-surgeon inspect model.onnx
graph-surgeon topology model.onnx
graph-surgeon patterns model.onnx
graph-surgeon motifs model.onnx -o report.json
graph-surgeon flow model.onnx
graph-surgeon catalog --gadget GAP_FC_HEAD
graph-surgeon catalog --chain CHAIN-PATCH-ATTACK-SURFACE
graph-surgeon catalog --coverage
graph-surgeon operators --op Conv
graph-surgeon edit validate edited.onnx --level runnable
graph-surgeon edit remove-node model.onnx NODE_NAME -o edited.onnx
graph-surgeon diff baseline.onnx edited.onnx
When a motif or chain is detected, use `catalog --gadget` or `catalog --chain` for registry metadata, detection logic, and paper write-ups. Display titles use registry IDs (for example `GAP_FC_HEAD — Global Average Pool → FC Head`).
Counterfactual edits (`edit`) currently expose one surgery subcommand: `remove-node`. Use `inspect` or `topology` to discover exact ONNX node names before editing.
## Research corpus
GraphSurgeon ships per-paper analysis under `graph_surgeon/taxonomy/data/` (notably `attack_research_notes.md`). That corpus is what powers rich catalog output: when you look up a gadget or chain, you get the linked AML literature, ONNX graph indicators, and attack-class mapping without fetching external docs.
Normal use does not require touching these files. To see completion status:
.venv/bin/graph-surgeon catalog --coverage
## Python API
GraphSurgeon is primarily a CLI tool, but the same analysis and surgery paths are available from Python. Package-level exports (`from graph_surgeon import ...`): `GraphSurgeon`, `GraphTopology`, `GraphTopologyConfig`, `LayerPosition`, `NodeTopology`, `GraphValidationLevel`, `GraphValidationResult`.
### Analysis (motifs, patterns, flow)
These mirror `motifs`, `patterns`, and `flow`:
from graph_surgeon.parsers.onnx_parser import analyze_onnx_graph, analyze_onnx_patterns, quick_scan
motif_report = analyze_onnx_graph("model.onnx", output_path="report.json")
print(len(motif_report.structural_findings), motif_report.model_flow_description)
pattern_report = analyze_onnx_patterns("model.onnx")
print(quick_scan("model.onnx"))
JSON export from motifs uses the same sanitizer as CLI output (internal scoring fields stripped).
### Topology and graph inspection
from graph_surgeon import GraphSurgeon, LayerPosition
surgeon = GraphSurgeon(verbose=False)
model = surgeon.load_model("model.onnx")
topo = surgeon.get_graph_topology(model.graph)
print(topo.total_nodes, topo.max_depth)
print(topo.by_position[LayerPosition.EARLY][:5])
print(topo.by_position[LayerPosition.LATE][:5])
print(surgeon.find_nodes_by_type(model.graph, "Conv"))
### Catalog and taxonomy
These mirror `catalog` lookups:
from graph_surgeon.taxonomy.display import format_catalog_gadget, format_catalog_chain
from graph_surgeon.taxonomy.research_coverage import format_coverage_report
from graph_surgeon.taxonomy import motif_catalog
print(format_catalog_gadget("LINEAR_HEAD"))
print(format_catalog_chain("CHAIN-SKIP-HIGHWAY"))
print(format_coverage_report())
technique = motif_catalog.get_technique_by_id("AML-ADV-002")
### Counterfactual edits
CLI parity: `remove_node` matches `edit remove-node`. All surgery methods mutate the loaded `ModelProto` in place and return a `SurgeryResult` (`success`, `message`, `nodes_removed`, `edges_rewired`, etc.).
from graph_surgeon import GraphSurgeon, GraphValidationLevel
surgeon = GraphSurgeon(verbose=False)
baseline = surgeon.load_model("model.onnx")
edited = surgeon.clone_model(baseline)
result = surgeon.remove_node(edited, "node_relu_23")
if not result.success:
raise RuntimeError(result.message)
surgeon.save_model(edited, "edited.onnx")
check = surgeon.validate(edited, level=GraphValidationLevel.STRUCTURAL)
print(check.valid, check.errors)
diff = surgeon.compare_graphs(baseline, edited)
print(diff["summary"], diff["nodes_removed"])
`GraphValidationLevel.LOADABLE` and `.RUNNABLE` require `onnxruntime` (installed with `pip install -e ".[dev]"`). On a minimal install, validation falls back with warnings.
Additional graph surgery primitives (`remove_subgraph`, `insert_node_before`, `insert_node_after`, `replace_node`, `modify_node_attribute`, `add_initializer`, `add_metadata`) are Python-only today. See [docs/PYTHON_API.md](docs/PYTHON_API.md) for the full `GraphSurgeon` reference.
### Optional weight statistics
Heuristic weight-distribution analysis (core deps only; no `onnxruntime`):
from graph_surgeon.behavior.weight_signature import analyze_onnx_weights
stats = analyze_onnx_weights("model.onnx")
print(stats.summary())
## Documentation
| Doc | Contents |
|-----|----------|
| [docs/PYTHON_API.md](docs/PYTHON_API.md) | Full `GraphSurgeon` method reference, validation levels, surgery result fields |
## Comparison to Netron
Netron is a graph viewer: nodes, tensors, and shapes on screen.
GraphSurgeon is an analysis and experimentation layer on top of the same ONNX files:
- Positional topology (stem vs middle vs head) and ordered execution, not just adjacency
- Automated motif and pattern detection with a typed registry and literature cross-reference
- A searchable catalog of gadgets, chains, techniques, and bundled paper notes
- Counterfactual edits (remove a node, rewire, validate, diff against baseline)
- JSON export for scripting and batch comparison across model variants
Use Netron to see the graph; use GraphSurgeon to interpret structure, relate it to published attack classes, and test what changes when you alter the DAG.
## Tests
Unit tests (no external ONNX files):
.venv/bin/python -m pytest tests/ -v
Integration tests that require off-repo ONNX fixtures are gitignored and documented in `tests/README.md`.
## License
MIT