.. _l-what-is-onnx-light: Quick tour ========== ``onnx-light`` is a rewrite of :epkg:`onnx` that keeps the same format and the same API but drops :epkg:`protobuf` along the way. It is **protobuf free**, written in **C++ only**, and **parallelized**. Protobuf ++++++++ Since the early days of ONNX, :epkg:`protobuf` has been a recurring pain point: - frequent conflicts between the installed version and the one used by ONNX, - no way to switch from Python to C++ without going through serialization, - complex compilation since a header has to be generated first, - an API that cannot easily be extended, - a hard 2 GB message-size limitation. So the question is simple: can we have protobuf without protobuf? First question ++++++++++++++ The prototype `onnx/onnx#7208 `_ demonstrated that a replacement keeping the same API and the same format, but without protobuf, is possible. The next question was whether to do a smooth rewriting or a complete rewriting from scratch. **I chose to rewrite ONNX rather than modify the existing project**: two ONNX implementations are better than one. Users can implement a switch and check both at the same time rather than going through major breaking changes in the original code base. No protobuf, but also more ++++++++++++++++++++++++++ Removing protobuf brings several benefits: - removes the 2 GB limitation, - enables parallelization, - allows custom loading strategies (read only part of a model without loading the entire file, control alignment, ...), - adds native C++ support for weight files, - minimizes dependencies and security issues. On top of that: - no serialization layer between Python and C++, - the same C++ and Python APIs, - no auto-generated Markdown files in the repository, so pull requests no longer modify 200 files at once. Migrating existing code only requires changing the import: .. code-block:: python import onnx_light.onnx as onnx Other requirements ++++++++++++++++++ A few other things change too: - backend tests are no longer stored as files; they are implemented as C++ functions, - backend tests are generated from a shared random test suite, - expected outputs are produced by C++ kernels, - kernels can be parallelized except for reductions, to ensure deterministic results, - flexible C++ linking: users can link only the reading / writing / serialization components, or include schemas, kernels and/or backend tests as needed. Symbolic shape inference ++++++++++++++++++++++++ Shapes use real symbolic values instead of placeholders such as ``unk__0``, ``unk__1``, ... .. image:: images/what_is_onnx_light/symbolic_shapes.png :width: 600 :alt: Symbolic shapes instead of unk__0, unk__1 Core pieces: take what you need +++++++++++++++++++++++++++++++ The C++ code ships as small libraries so downstream projects link only what they need: - parse and serialize, no schema needed (1.5 Mb), - existing C++ ONNX library, no change (5 Mb), - light schema (2 Mb, not loaded as a static variable), - graph manipulations (0.6 Mb), - new shape inference (1.5 Mb), - C++ kernels (4.8 Mb), - 2000+ C++ backend tests (7 Mb). Parsing and serializing options +++++++++++++++++++++++++++++++ - data can be aligned when loading a tensor, - parallelization is possible, - :epkg:`onnxruntime` :epkg:`FlatBuffers` format is supported, - more secure: recursion is limited, - other scenarios can be implemented, such as a callback to allocate directly on GPU or any other memory. .. image:: images/what_is_onnx_light/parsing_1.png :width: 600 :alt: Parsing options .. image:: images/what_is_onnx_light/parsing_2.png :width: 600 :alt: Parsing options .. image:: images/what_is_onnx_light/parsing_3.png :width: 600 :alt: Parsing options Kernels +++++++ Kernels are kept simple, with a dispatch mechanism to support all types. .. image:: images/what_is_onnx_light/kernel_1.png :width: 600 :alt: A simple kernel .. image:: images/what_is_onnx_light/kernel_2.png :width: 600 :alt: Dispatch mechanism to support all types Backend tests +++++++++++++ They are very similar to the existing ones, except they do not write any file. .. image:: images/what_is_onnx_light/backend_test.png :width: 600 :alt: A backend test Shape inference tests +++++++++++++++++++++ New tests are dedicated to shape inference: ``value_info`` is filled with the expected values. .. image:: images/what_is_onnx_light/shape_test_1.png :width: 600 :alt: Shape inference test .. image:: images/what_is_onnx_light/shape_test_2.png :width: 600 :alt: Shape inference test .. image:: images/what_is_onnx_light/shape_test_3.png :width: 600 :alt: Shape inference test Symbolic expressions ++++++++++++++++++++ Expressions only use symbols, with no function calls, to keep them short. There are two divisions: - ``//`` rounds the result, - ``/:`` is used when the exact result is known to be an integer. For example, ``Reshape(x[N], [-1, 2])`` tells us ``N`` is even, so we write ``N /: 2`` and not ``N // 2``: ``2 * (N /: 2) == N`` while ``2 * (N // 2)`` may differ. .. image:: images/what_is_onnx_light/expr_1.png :width: 600 :alt: Symbolic expressions .. image:: images/what_is_onnx_light/expr_2.png :width: 600 :alt: Symbolic expressions Next steps ++++++++++ - implement a switch in ir-py to check it is compatible with ``onnx_light.onnx``, - do the same for :epkg:`onnxruntime` and validate the C++ API, - support more loading / saving scenarios, - expose standard graph transformations (dead-end removal, ...).