From 8d40b87a5c9cb6bfdfc202bd7e6640f523d06eba Mon Sep 17 00:00:00 2001 From: dchou1618 Date: Sat, 13 Jun 2026 14:05:23 -0700 Subject: [PATCH 1/2] initial onnx scaffolding --- Modeling/onnx/README.md | 58 +++++++++++++++++++++++++ Modeling/onnx/models/mlp_baseline.onnx | Bin 0 -> 1090 bytes 2 files changed, 58 insertions(+) create mode 100644 Modeling/onnx/README.md create mode 100644 Modeling/onnx/models/mlp_baseline.onnx diff --git a/Modeling/onnx/README.md b/Modeling/onnx/README.md new file mode 100644 index 0000000..44a5749 --- /dev/null +++ b/Modeling/onnx/README.md @@ -0,0 +1,58 @@ +# ONNX Playground + +A collection of standalone ONNX experiments focused on understanding: +- ONNX graph structure +- PyTorch → ONNX export +- ONNX Runtime execution +- graph optimization and fusion +- inference benchmarking +- low-level graph manipulation + +The goal of this directory is to treat ONNX as an intermediate representation (IR) and explore how model execution changes after graph transformations and runtime optimizations. + +--- + +# Objectives + +This playground investigates: + +1. How neural network layers are represented as ONNX graph nodes +2. How ONNX Runtime executes static computation graphs +3. How graph optimizations affect: + - node count + - operator fusion + - inference latency +4. The difference between: + - model structure + - runtime execution + - compiler-style graph transformations + +--- + +# Intended Directory Structure + +```text +onnx/ +├── README.md +├── requirements.txt +│ +├── models/ +│ ├── mlp_baseline.onnx +│ └── mlp_optimized.onnx +│ +├── benchmarks/ +│ ├── baseline.json +│ └── optimized.json +│ +├── scripts/ +│ ├── export.py +│ ├── benchmark.py +│ ├── optimize.py +│ ├── inspect.py +│ └── playground.py +│ +└── outputs/ + ├── graph_before.txt + ├── graph_after.txt + └── node_counts.json +``` \ No newline at end of file diff --git a/Modeling/onnx/models/mlp_baseline.onnx b/Modeling/onnx/models/mlp_baseline.onnx new file mode 100644 index 0000000000000000000000000000000000000000..f735ad8690b80d27e580d96076b614ba486826dd GIT binary patch literal 1090 zcmY+EYfzMB6vyRd(dCuP?ji(eM2jJ=E3gZA0p92AZUKdW243P?5y~RFp~yD4Ljhh; zxhVzW1yOJ)P|#6A1=;6-aghRrXfmS+$Vr`{7$pHsu|=&JznnSe|KWGeoEzWLw~^1< zvt~&jZ_%Z(%m$grRI8PVXNP{67OK;*B6cQZ8I?@LsAXbPWkLdL!x~-D9JTEI)QuXQ zm=|uetRwrMV1&qLAoBkqb~6yU5h54OPSK?q6Fmco%p;PtTQnO(L)J(NczixzATUuG zD+tcwrFRd5dbpc>QK*OSHd`weT8zPnyB?pe*aq=W9H}~?-!QfEd*YU5Ln_~Bg1CMa zw0J(`=Bj=tu{TbU#~cD<8%~pgPY|;&qtGM4r6oUN5u95U3wx$Hta($YsCH|@x3zwp za~;qH@-`T^X@=ZWPGom^EBQ0vBQ)eb!MxYc)B6t2M4~?iCASYl{pt>??L0ysFUvvK zZyHdL_%kx~51}ZX!N65R^lHna4cr-Fci=SZ?AfTuQL0Y!j?b{)<$e zY(tAo3YoR{K{0X=mXj4!W3>+hr*gnYdyUT9UIylY>j=+G57oL0?mdXxS&Pc!l^8EchdXWuvH0o~47T@EuSmd2@dWJ1Hh|PPr@Z@#@Xq!a zWIp{H49oY?tJ+YQpP34iO-?k-`&BU5YT#y53z0PDW8L{f#I&;pyspczd!++S?H;6c zUyp($`1vxZWWkCZ^QG`(c`sSv5P<#`EC?&4c;y3zyWSzj%aWy_J8A;~A?s*D>$ha< z-8|egOmTd2i0Eo1L{lUNNddzb@OjU6OCFrXr0%v?sJphpr87yG@Td^JN~y-awWCyd zCC|_sHUP?Mg8HHe)K_#FF5X;>M|}=}+%FfEL!aaL;vu?VdYayOGy%kN8wkVdInh8F zO5NTAt2tR{eYqWDgohwBu#zMik|DtJBvC|0!?7Ay6vgktM;Y#*tPREWIi6VPcbBGl z?)N`O8ffD5SzOw-5V;VmmL=BC;4A3Cl$ZjvJiU&awU)!|>~>h6QH8gf_7bz(&IY?( zCHTn$Gq_RXL50Nuu%s^>bg8T&(&jWIni*){YdTuhnh&wK2x@+Ffz4Cbuq3Fn`Oug@ zN*tdzLnRLu1cmP>BGWEIhDj1=H&>D<+ZUfeWWln06VYsT7R&JY@pkNs!+lBTsThIq EUs5s1ZvX%Q literal 0 HcmV?d00001 From 79b11fee949120083e633904be15a64c92dceeb3 Mon Sep 17 00:00:00 2001 From: dchou1618 Date: Sun, 14 Jun 2026 21:41:27 -0700 Subject: [PATCH 2/2] examine onnx graph --- Modeling/onnx/README.md | 15 +---- Modeling/onnx/requirements.txt | 1 + Modeling/onnx/scripts/examine.py | 107 +++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 14 deletions(-) create mode 100644 Modeling/onnx/requirements.txt create mode 100644 Modeling/onnx/scripts/examine.py diff --git a/Modeling/onnx/README.md b/Modeling/onnx/README.md index 44a5749..7a73979 100644 --- a/Modeling/onnx/README.md +++ b/Modeling/onnx/README.md @@ -40,19 +40,6 @@ onnx/ │ ├── mlp_baseline.onnx │ └── mlp_optimized.onnx │ -├── benchmarks/ -│ ├── baseline.json -│ └── optimized.json -│ ├── scripts/ -│ ├── export.py -│ ├── benchmark.py -│ ├── optimize.py -│ ├── inspect.py -│ └── playground.py -│ -└── outputs/ - ├── graph_before.txt - ├── graph_after.txt - └── node_counts.json +│ ├── examine.py ``` \ No newline at end of file diff --git a/Modeling/onnx/requirements.txt b/Modeling/onnx/requirements.txt new file mode 100644 index 0000000..852579f --- /dev/null +++ b/Modeling/onnx/requirements.txt @@ -0,0 +1 @@ +onnx==1.21.0 \ No newline at end of file diff --git a/Modeling/onnx/scripts/examine.py b/Modeling/onnx/scripts/examine.py new file mode 100644 index 0000000..97de2df --- /dev/null +++ b/Modeling/onnx/scripts/examine.py @@ -0,0 +1,107 @@ +import onnx +from onnx import numpy_helper +from collections import Counter + +def examine(fpath: str): + model = onnx.load(fpath) + onnx.checker.check_model(model) + + print("=" * 80) + print("GRAPH SUMMARY") + print("=" * 80) + + print("Graph name:", model.graph.name) + print("Nodes:", len(model.graph.node)) + print("Inputs:", len(model.graph.input)) + print("Outputs:", len(model.graph.output)) + print("Initializers:", len(model.graph.initializer)) + + counter = Counter(node.op_type for node in model.graph.node) + + print("\nOPERATOR HISTOGRAM") + print("-" * 80) + + for op, count in counter.items(): + print(f"{op:<20} {count}") + + print("\nGRAPH TOPOLOGY") + print("-" * 80) + # node: [op_type] [inputs] -> [outputs] + for idx, node in enumerate(model.graph.node): + inputs = ", ".join(node.input) + outputs = ", ".join(node.output) + + print( + f"[{idx:03d}] " + f"{node.op_type:<15} " + f"{inputs} -> {outputs}" + ) + + print("\nPOTENTIAL OPTIMIZATION OPPORTUNITIES") + print("-" * 80) + + nodes = list(model.graph.node) + + for i in range(len(nodes) - 1): + a = nodes[i] + b = nodes[i + 1] + + # MatMul + Add can often become Gemm + if a.op_type == "MatMul" and b.op_type == "Add": + print( + f"Possible fusion at nodes {i}->{i+1}: " + f"MatMul + Add -> Gemm" + ) + + # redundant identities + if a.op_type == "Identity": + print(f"Redundant Identity node at {i}") + + # transpose chains + if a.op_type == "Transpose" and b.op_type == "Transpose": + print(f"Consecutive Transpose ops at {i}->{i+1}") + + print("\nINITIALIZERS") + print("-" * 80) + + for init in model.graph.initializer: + arr = numpy_helper.to_array(init) + + print( + f"{init.name:<20} " + f"shape={str(arr.shape):<20} " + f"dtype={arr.dtype}" + ) + + inferred = onnx.shape_inference.infer_shapes(model) + print("\nINTERMEDIATE TENSORS") + print("-" * 80) + + for value in inferred.graph.value_info: + tensor_type = value.type.tensor_type + + dims = [ + d.dim_param or d.dim_value + for d in tensor_type.shape.dim + ] + + print(f"{value.name:<20} {dims}") + + print("\nLINEARIZED EXECUTION VIEW") + print("-" * 80) + + for idx, node in enumerate(model.graph.node): + print(f"{idx:03d} {node.op_type}") + + for inp in node.input: + print(f" IN <- {inp}") + + for out in node.output: + print(f" OUT -> {out}") + +if __name__ == "__main__": + import argparse + parser = argparse.ArgumentParser(description="Inspect an ONNX model") + parser.add_argument("fpath", type=str, help="Path to the ONNX model file") + args = parser.parse_args() + examine(args.fpath) \ No newline at end of file