jax → ONNX export via jax2onnx: jaxtrain now produces HSSM-consumable artifacts - #109
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
38ef77e to
1cc7de9
Compare
Codecov Report❌ Patch coverage is
🚀 New features to boost your workflow:
|
3dadc06 to
e565d12
Compare
…ifacts jaxtrain was a dead end for the ecosystem: it saved flax parameter bytes that nothing downstream could read — transform-onnx only handles torch state dicts and HSSM consumes ONNX exclusively. This closes the single missing edge. - New lanfactory/onnx/jax_export.py: export_forward_to_onnx (shared core) + transform_jax_to_onnx (file-based, for retroactive conversion of existing .jax artifacts), exposed as the `transform-jax-onnx` CLI - The jax trainer exports ONNX alongside its other artifacts (mirroring the torch trainer's _save_onnx) and logs it to MLflow; `--no-export-onnx` on jaxtrain opts out - Dependency: jax2onnx==0.15.* (exact-pinned 0.x line), opset 17 to match the sbi/bayesflow exporters Two contract decisions, both verified empirically: - The graph is traced with a concrete (1, input_dim) dummy — every dim static, zero dynamic axes — matching the torch MLP exporter and the production networks on franklab/HSSM, NOT rank-1: jax2onnx lowers nn.Dense to Gemm, whose ONNX spec requires rank-2 inputs (a rank-1 trace is rejected by onnxruntime). Verified end-to-end that HSSM's make_jax_func loads the export and rank-1-per-trial + jax.vmap consumption matches exactly. - The exported graph is the EVAL head: identity for logprob (LAN), logsigmoid for logits (CPN/OPN) — matching every torch export path (_save_onnx calls .eval(); transform-onnx exports under torch's EVAL default) and HSSM's element-wise log-likelihood consumption. Exporting the raw training head would silently corrupt every CPN/OPN logp by +log(1+exp(-logit)); a test asserts the logits export equals the logsigmoid head and is NOT the raw head. Tests: contract (all dims concrete, (1,D) shape, op allowlist calibrated against jax2onnx 0.15's actual lowering — relu emits Max, the logsigmoid head emits Neg/Exp/Add/Log), parity (ORT vs eval-head jax forward over 1000 draws, atol 1e-4), file-based vs in-trainer export equivalence, logits-head correctness, and the opt-out flag. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
9f0ae41 to
9bb0616
Compare
What
PR-0.3, stacked on #108 (review only the top commit; retarget to
mainafter #108 merges). Closes the gap that madejaxtraina dead end for HSSM: the jax trainer saved flax parameter bytes that nothing downstream could convert —transform-onnxonly reads torch state dicts, and HSSM consumes ONNX exclusively.lanfactory/onnx/jax_export.py(~150 lines incl. docs): wrapsjax2onnx(pinned==0.15.*) over the existingJaxMLPFactory.make_forward_partialrestore path — minimal-carry, no bespoke tracertransform-jax-onnxCLI (file-based:network_config.pickle+*_train_state.jax→.onnx)jaxtraingains--export-onnx/--no-export-onnx(default on): the trainer exports ONNX right after training and logs it to MLflow(1, input_dim)dummy, no dynamic axes.(1, D)rather than rank-1 because jax2onnx lowersnn.DensetoGemm, whose ONNX spec requires rank-2 (the sbi/bayesflow exporters trace rank-1 because torch lowers Linear to rank-agnostic MatMul+Add — different tracer, different constraint). Verified end-to-end against HSSM'smake_jax_func+jax.vmapconsumption with exact paritylogprob(LAN) this equals the raw head; forlogits(CPN/OPN) it applies logsigmoid, matching every torch export path. Exporting the raw training head would silently corrupt every downstream logp by+log(1+exp(-logit))— this was caught in review and is now explicitly regression-testedtransform-onnxuntouched — they remain the verified fallback pathTests (the parity test is the load-bearing one)
(1, D), op profile ⊆ {Gemm + elementwise} (guards against jax2onnx lowering changes under the 0.x pin)atol=1e-4; file-based transform ≡ in-trainer export-log1p(exp(-raw))and provably ≠ the raw head--no-export-onnxproduces no.onnxCommands run
🤖 Generated with Claude Code