diff --git a/0.20.dev/nnef/index.html b/0.20.dev/nnef/index.html index cd2243fae4..2c2322bbbf 100644 --- a/0.20.dev/nnef/index.html +++ b/0.20.dev/nnef/index.html @@ -351,6 +351,13 @@ with_tract_core() + + +
class Nnef:
+120
+121
+122
+123
+124
+125
+126
+127
+128
class Nnef:
"""
Represent a NNEF context in tract.
@@ -813,6 +835,14 @@
check(lib.tract_nnef_enable_tract_core(self.ptr))
return self
+ def with_tract_extra(self) -> "Nnef":
+ """
+ Enable tract-extra extensions to NNEF.
+ """
+ self._valid()
+ check(lib.tract_nnef_enable_tract_extra(self.ptr))
+ return self
+
def with_onnx(self) -> "Nnef":
"""
Enable tract-opl extensions to NNEF to covers (more or) ONNX operator set
@@ -974,13 +1004,13 @@
Source code in tract/nnef.py
- 75
-76
-77
-78
-79
-80
-81
def with_extended_identifier_syntax(self) -> "Nnef":
+ 83
+84
+85
+86
+87
+88
+89
def with_extended_identifier_syntax(self) -> "Nnef":
"""
Enable tract-opl extensions to NNEF for extended identifiers (will support PyTorch 2 path-like ids)
"""
@@ -1011,13 +1041,13 @@
Source code in tract/nnef.py
- 59
-60
-61
-62
-63
-64
-65
def with_onnx(self) -> "Nnef":
+ 67
+68
+69
+70
+71
+72
+73
def with_onnx(self) -> "Nnef":
"""
Enable tract-opl extensions to NNEF to covers (more or) ONNX operator set
"""
@@ -1048,13 +1078,13 @@
Source code in tract/nnef.py
- 67
-68
-69
-70
-71
-72
-73
def with_pulse(self) -> "Nnef":
+ 75
+76
+77
+78
+79
+80
+81
def with_pulse(self) -> "Nnef":
"""
Enable tract-opl extensions to NNEF for tract pulse operators (for audio streaming)
"""
@@ -1110,6 +1140,43 @@
+
+ with_tract_extra()
+
+
+
+
+
+
+ Enable tract-extra extensions to NNEF.
+
+
+ Source code in tract/nnef.py
+ 59
+60
+61
+62
+63
+64
+65
def with_tract_extra(self) -> "Nnef":
+ """
+ Enable tract-extra extensions to NNEF.
+ """
+ self._valid()
+ check(lib.tract_nnef_enable_tract_extra(self.ptr))
+ return self
+
+
+
+
+
+
+
+
+
+
+
+
write_model_to_dir(model, path)
@@ -1123,18 +1190,18 @@
Source code in tract/nnef.py
- 83
-84
-85
-86
-87
-88
-89
-90
-91
-92
-93
-94
def write_model_to_dir(self, model: Model, path: Union[str, Path]) -> None:
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
def write_model_to_dir(self, model: Model, path: Union[str, Path]) -> None:
"""
Save `model` as a NNEF directory model in `path`.
@@ -1171,18 +1238,18 @@
Source code in tract/nnef.py
- 96
- 97
- 98
- 99
-100
-101
-102
-103
-104
+ 104
105
106
-107
def write_model_to_tar(self, model: Model, path: Union[str, Path]) -> None:
+107
+108
+109
+110
+111
+112
+113
+114
+115
def write_model_to_tar(self, model: Model, path: Union[str, Path]) -> None:
"""
Save `model` as a NNEF tar archive in `path`.
@@ -1219,18 +1286,18 @@
Source code in tract/nnef.py
- 109
-110
-111
-112
-113
-114
-115
-116
-117
+ 117
118
119
-120
def write_model_to_tar_gz(self, model: Model, path: Union[str, Path]) -> None:
+120
+121
+122
+123
+124
+125
+126
+127
+128
def write_model_to_tar_gz(self, model: Model, path: Union[str, Path]) -> None:
"""
Save `model` as a NNEF tar compressed archive in `path`.
diff --git a/0.20.dev/objects.inv b/0.20.dev/objects.inv
index 0c586f7d60..bd668ce814 100644
Binary files a/0.20.dev/objects.inv and b/0.20.dev/objects.inv differ
diff --git a/0.20.dev/search/search_index.json b/0.20.dev/search/search_index.json
index c36b99f355..7e64143191 100644
--- a/0.20.dev/search/search_index.json
+++ b/0.20.dev/search/search_index.json
@@ -1 +1 @@
-{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"tract
python bindings","text":"tract
is a library for neural network inference. While PyTorch and TensorFlow deal with the much harder training problem, tract
focuses on what happens once the model in trained.
tract
ultimate goal is to use the model on end-user data (aka \"running the model\") as efficiently as possible, in a variety of possible deployments, including some which are no completely mainstream : a lot of energy have been invested in making tract
an efficient engine to run models on ARM single board computers.
"},{"location":"#getting-started","title":"Getting started","text":""},{"location":"#install-tract-library","title":"Install tract library","text":"pip install tract
. Prebuilt wheels are provided for x86-64 Linux and Windows, x86-64 and arm64 for MacOS.
"},{"location":"#downloading-the-model","title":"Downloading the model","text":"First we need to obtain the model. We will download an ONNX-converted MobileNET 2.7 from the ONNX model zoo.
wget https://github.com/onnx/models/raw/main/vision/classification/mobilenet/model/mobilenetv2-7.onnx
.
"},{"location":"#preprocessing-an-image","title":"Preprocessing an image","text":"Then we need a sample image. You can use pretty much anything. If you lack inspiration, you can this picture of Grace Hopper.
wget https://s3.amazonaws.com/tract-ci-builds/tests/grace_hopper.jpg
We will be needing pillow
to load the image and crop it.
pip install pillow
Now let's start our python script. We will want to use tract, obviously, but we will also need PIL's Image and numpy to put the data in the form MobileNet expects it.
#!/usr/bin/env python\n\nimport tract\nimport numpy\nfrom PIL import Image\n
We want to load the image, crop it into its central square, then scale this square to be 224x224.
im = Image.open(\"grace_hopper.jpg\")\nif im.height > im.width:\n top_crop = int((im.height - im.width) / 2)\n im = im.crop((0, top_crop, im.width, top_crop + im.width))\nelse:\n left_crop = int((im.width - im.height) / 2)\n im = im.crop((left_crop, 0, left_crop + im_height, im.height))\nim = im.resize((224, 224))\nim = numpy.array(im)\n
At this stage, we obtain a 224x224x3 tensor of 8-bit positive integers. We need to transform these integers to floats and normalize them for MobileNet. At some point during this normalization, numpy decides to promote our tensor to double precision, but our model is single precison, so we are converting it again after the normalization.
im = (im.astype(float) / 255. - [0.485, 0.456, 0.406]) / [0.229, 0.224, 0.225]\nim = im.astype(numpy.single)\n
Finally, ONNX variant of Mobilenet expects its input in NCHW convention, and our data is in HWC. We need to move the C axis before H and W, then insert the N at the left.
im = numpy.moveaxis(im, 2, 0)\nim = numpy.expand_dims(im, 0)\n
"},{"location":"#loading-the-model","title":"Loading the model","text":"Loading a model is relatively simple. We need to instantiate the ONNX loader first, the we use it to load the model. Then we ask tract to optimize the model and get it ready to run.
model = tract.onnx().model_for_path(\"./mobilenetv2-7.onnx\").into_optimized().into_runnable()\n
If we wanted to process several images, this would only have to be done once out of our image loop.
"},{"location":"#running-the-model","title":"Running the model","text":"tract run methods take a list of inputs and returns a list of outputs. Each input can be a numpy array. The outputs are tract's own Value data type, which should be converted to numpy array.
outputs = model.run([im])\noutput = outputs[0].to_numpy()\n
"},{"location":"#interpreting-the-result","title":"Interpreting the result","text":"If we print the output, what we get is a array of 1000 values. Each value is the score of our image on one of the 1000 categoris of ImageNet. What we want is to find the category with the highest score.
print(numpy.argmax(output))\n
If all goes according to plan, this should output the number 652. There is a copy of ImageNet categories at the following URL, with helpful line numbering.
https://github.com/sonos/tract/blob/main/examples/nnef-mobilenet-v2/imagenet_slim_labels.txt\n
And... 652 is \"microphone\". Which is wrong. The trick is, the lines are numbered from 1, while our results starts at 0, plus the label list includes a \"dummy\" label first that should be ignored. So the right value is at the line 654: \"military uniform\". If you looked at the picture before you noticed that Grace Hopper is in uniform on the picture, so it does make sense.
"},{"location":"#model-cooking-with-tract","title":"Model cooking with tract
","text":"Over the years of tract
development, it became clear that beside \"training\" and \"running\", there was a third time in the life-cycle of a model. One of our contributors nicknamed it \"model cooking\" and the term stuck. This extra stage is about all what happens after the training and before running.
If training and Runtime are relatively easy to define, the model cooking gets a bit less obvious. It comes from the realisation that the training form (an ONNX or TensorFlow file or ste of files) of a model may is usually not the most convenient form for running it. Every time a device loads a model in ONNX form and transform it into a suitable form for runtime, it goes through the same series or more or less complicated operations, that can amount to several seconds of high-CPU usage for current models. When running the model on a device, this can have several negative impact on experience: the device will take time to start-up, consume a lot of battery energy to get ready, maybe fight over CPU availability with other processes trying to get ready at the same instant on the device.
As this sequence of operations is generally the same, it becomes relevant to persist the model resulting of the transformation. It could be persisted at the first application start-up for instance. But it could also be \"prepared\", or \"cooked\" before distribution to the devices.
"},{"location":"#cooking-to-nnef","title":"Cooking to NNEF","text":"tract
supports NNEF. It can read a NNEF neural network and run it. But it can also dump its preferred representation of a model in NNEF.
At this stage, a possible path to production for a neural model becomes can be drawn: * model is trained, typically on big servers on the cloud, and exported to ONNX. * model is cooked, simplified, using tract
command line or python bindings. * model is shipped to devices or servers in charge of running it.
"},{"location":"#testing-and-benching-models-early","title":"Testing and benching models early","text":"As soon as the model is in ONNX form, tract
can load and run it. It gives opportunities to validate and test on the training system, asserting early on that tract
will compute at runtime the same result than what the training model predicts, limiting the risk of late-minute surprise.
But tract command line can also be used to bench and profile an ONNX model on the target system answering very early the \"will the device be fast enough\" question. The nature of neural network is such that in many cases an untrained model, or a poorly trained one will perform the same computations than the final model, so it may be possible to bench the model for on-device efficiency before going through a costly and long model training.
"},{"location":"#tract-opl","title":"tract-opl","text":"NNEF is a pretty little standard. But we needed to go beyond it and we extended it in several ways. For instance, NNEF does not provide syntax for recurring neural network (LSTM and friends), which are an absolute must in signal and voice processing. tract
also supports symbolic dimensions, which are useful to represent a late bound batch dimension (if you don't know in advance how many inputs will have to be computed concurrently).
"},{"location":"#pulsing","title":"Pulsing","text":"For interactive applications where time plays a role (voice, signal, ...), tract
can automatically transform batch models, to equivalent streaming models suitable for runtime. While batch models are presented at training time the whole signal in one go, a streaming model received the signal by \"pulse\" and produces step by step the same output that the batching model.
It does not work for every model, tract
can obviously not generate a model where the output at a time depends on input not received yet. Of course, models have to be causal to be pulsable. For instance, a bi-directional LSTM is not pulsable. Most convolution nets can be made causal at designe time by padding, or at cooking time by adding fixed delays.
This cooking step is a recurring annoyance in the real-time voice and signal field : it can be done manually, but is very easy to get wrong. tract
makes it automactic.
"},{"location":"fact/","title":"Facts","text":""},{"location":"fact/#tract.fact.Fact","title":"Fact
","text":"Tract-core fact, to be used with Model.
It always contains the full shape (sometimes using symbolic dimensions) and item type. In some situation it can also contain the constant value of the associated tensor.
Source code in tract/fact.py
class Fact:\n\"\"\"\n Tract-core fact, to be used with Model.\n\n It always contains the full shape (sometimes using symbolic dimensions) and item type.\n In some situation it can also contain the constant value of the associated tensor.\n \"\"\"\n def __init__(self, ptr):\n self.ptr = ptr\n\n def __del__(self):\n if self.ptr:\n check(lib.tract_fact_destroy(byref(self.ptr)))\n\n def __str__(self):\n return self.dump()\n\n def _valid(self):\n if self.ptr == None:\n raise TractError(\"invalid fact (maybe already consumed ?)\")\n\n def dump(self):\n self._valid()\n cstring = c_char_p();\n check(lib.tract_fact_dump(self.ptr, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n
"},{"location":"fact/#tract.fact.InferenceFact","title":"InferenceFact
","text":"Tract inference fact, to be used with InferenceModel.
It can represent partial type and shape information of a Tensor during model analysis.
Source code in tract/fact.py
class InferenceFact:\n\"\"\"\n Tract inference fact, to be used with InferenceModel.\n\n It can represent partial type and shape information of a Tensor during model analysis.\n \"\"\"\n def __init__(self, ptr):\n self.ptr = ptr\n\n def __del__(self):\n if self.ptr:\n check(lib.tract_inference_fact_destroy(byref(self.ptr)))\n\n def __str__(self):\n return self.dump()\n\n def _valid(self):\n if self.ptr == None:\n raise TractError(\"invalid inference fact (maybe already consumed ?)\")\n\n def dump(self):\n self._valid()\n cstring = c_char_p();\n check(lib.tract_inference_fact_dump(self.ptr, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n
"},{"location":"inference_model/","title":"Inference model","text":""},{"location":"inference_model/#tract.inference_model.InferenceModel","title":"InferenceModel
","text":"ONNX model are loaded as an InferenceModel
s instead of Model
s: many ONNX models come with partial shape and element type information, while tract's Model
assume full shape and element type knownledge. In this case, it is generally sufficient to inform tract about the input shape and type, then let tract infer the rest of the missing shape information before converting the InferenceModel
to a regular Model
.
# load the model as an InferenceModel\nmodel = tract.onnx().model_for_path(\"./mobilenetv2-7.onnx\")\n\n# set the shape and type of its first and only input\nmodel.set_input_fact(0, \"1,3,224,224,f32\")\n\n# get ready to run the model\nmodel = model.into_optimized().into_runnable()\n
Source code in tract/inference_model.py
class InferenceModel:\n\"\"\"\n ONNX model are loaded as an\n `InferenceModel`s instead of `Model`s: many ONNX models come with partial shape and\n element type information, while tract's `Model` assume full shape and element type\n knownledge. In this case, it is generally sufficient to inform tract about the input\n shape and type, then let tract *infer* the rest of the missing shape information\n before converting the `InferenceModel` to a regular `Model`.\n\n ```python\n # load the model as an InferenceModel\n model = tract.onnx().model_for_path(\"./mobilenetv2-7.onnx\")\n\n # set the shape and type of its first and only input\n model.set_input_fact(0, \"1,3,224,224,f32\")\n\n # get ready to run the model\n model = model.into_optimized().into_runnable()\n ```\n \"\"\"\n def __init__(self, ptr):\n self.ptr = ptr\n\n def __del__(self):\n if self.ptr:\n check(lib.tract_inference_model_destroy(byref(self.ptr)))\n\n def _valid(self):\n if self.ptr == None:\n raise TractError(\"invalid inference model (maybe already consumed ?)\")\n\n def into_optimized(self) -> Model:\n\"\"\"\n Run the InferenceModel through the full tract optimisation pipeline to get an\n optimised Model.\n \"\"\"\n self._valid()\n model = c_void_p()\n check(lib.tract_inference_model_into_optimized(byref(self.ptr), byref(model)))\n return Model(model)\n\n def into_typed(self) -> Model:\n\"\"\"\n Convert an InferenceModel to a regular typed `Model`.\n\n This will leave the opportunity to run more transformation on the intermediary form of the\n model, before optimisint it all the way.\n \"\"\"\n self._valid()\n model = c_void_p()\n check(lib.tract_inference_model_into_typed(byref(self.ptr), byref(model)))\n return Model(model)\n\n def input_count(self) -> int:\n\"\"\"Return the number of inputs of the model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_inference_model_input_count(self.ptr, byref(i)))\n return i.value\n\n def output_count(self) -> int:\n\"\"\"Return the number of outputs of the model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_inference_model_output_count(self.ptr, byref(i)))\n return i.value\n\n def input_name(self, input_id: int) -> str:\n\"\"\"Return the name of the `input_id`th input.\"\"\"\n self._valid()\n cstring = c_char_p()\n check(lib.tract_inference_model_input_name(self.ptr, input_id, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n\n def input_fact(self, input_id: int) -> InferenceFact:\n\"\"\"Extract the InferenceFact of the `input_id`th input.\"\"\"\n self._valid()\n fact = c_void_p()\n check(lib.tract_inference_model_input_fact(self.ptr, input_id, byref(fact)))\n return InferenceFact(fact)\n\n def set_input_fact(self, input_id: int, fact: Union[InferenceFact, str, None]) -> None:\n\"\"\"Change the InferenceFact of the `input_id`th input.\"\"\"\n self._valid()\n if isinstance(fact, str):\n fact = self.fact(fact)\n if fact == None:\n check(lib.tract_inference_model_set_input_fact(self.ptr, input_id, None))\n else:\n check(lib.tract_inference_model_set_input_fact(self.ptr, input_id, fact.ptr))\n\n def set_output_names(self, names: List[str]):\n\"\"\"Change the output nodes of the model\"\"\"\n self._valid()\n nb = len(names)\n names_str = []\n names_ptr = (c_char_p * nb)()\n for ix, n in enumerate(names):\n names_str.append(str(n).encode(\"utf-8\"))\n names_ptr[ix] = names_str[ix]\n check(lib.tract_inference_model_set_output_names(self.ptr, nb, names_ptr))\n\n def output_name(self, output_id: int) -> str:\n\"\"\"Return the name of the `output_id`th output.\"\"\"\n self._valid()\n cstring = c_char_p()\n check(lib.tract_inference_model_output_name(self.ptr, output_id, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n\n def output_fact(self, output_id: int) -> InferenceFact:\n\"\"\"Extract the InferenceFact of the `output_id`th output.\"\"\"\n self._valid()\n fact = c_void_p()\n check(lib.tract_inference_model_output_fact(self.ptr, output_id, byref(fact)))\n return InferenceFact(fact)\n\n def set_output_fact(self, output_id: int, fact: Union[InferenceFact, str, None]) -> None:\n\"\"\"Change the InferenceFact of the `output_id`th output.\"\"\"\n self._valid()\n if isinstance(fact, str):\n fact = self.fact(fact)\n if fact == None:\n check(lib.tract_inference_model_set_output_fact(self.ptr, output_id, None))\n else:\n check(lib.tract_inference_model_set_output_fact(self.ptr, output_id, fact.ptr))\n\n def fact(self, spec:str) -> InferenceFact:\n\"\"\"\n Parse an fact specification as an `InferenceFact`\n\n Typical `InferenceFact` specification is in the form \"1,224,224,3,f32\". Comma-separated\n list of dimension, one for each axis, plus an mnemonic for the element type. f32 is \n single precision \"float\", i16 is a 16-bit signed integer, and u8 a 8-bit unsigned integer.\n \"\"\"\n self._valid()\n spec = str(spec).encode(\"utf-8\")\n fact = c_void_p();\n check(lib.tract_inference_fact_parse(self.ptr, spec, byref(fact)))\n return InferenceFact(fact)\n\n def analyse(self) -> None:\n\"\"\"\n Perform shape and element type inference on the model.\n \"\"\"\n self._valid()\n check(lib.tract_inference_model_analyse(self.ptr, False))\n\n def into_analysed(self) -> \"InferenceModel\":\n\"\"\"\n Perform shape and element type inference on the model.\n \"\"\"\n self.analyse()\n return self\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.analyse","title":"analyse()
","text":"Perform shape and element type inference on the model.
Source code in tract/inference_model.py
def analyse(self) -> None:\n\"\"\"\n Perform shape and element type inference on the model.\n \"\"\"\n self._valid()\n check(lib.tract_inference_model_analyse(self.ptr, False))\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.fact","title":"fact(spec)
","text":"Parse an fact specification as an InferenceFact
Typical InferenceFact
specification is in the form \"1,224,224,3,f32\". Comma-separated list of dimension, one for each axis, plus an mnemonic for the element type. f32 is single precision \"float\", i16 is a 16-bit signed integer, and u8 a 8-bit unsigned integer.
Source code in tract/inference_model.py
def fact(self, spec:str) -> InferenceFact:\n\"\"\"\n Parse an fact specification as an `InferenceFact`\n\n Typical `InferenceFact` specification is in the form \"1,224,224,3,f32\". Comma-separated\n list of dimension, one for each axis, plus an mnemonic for the element type. f32 is \n single precision \"float\", i16 is a 16-bit signed integer, and u8 a 8-bit unsigned integer.\n \"\"\"\n self._valid()\n spec = str(spec).encode(\"utf-8\")\n fact = c_void_p();\n check(lib.tract_inference_fact_parse(self.ptr, spec, byref(fact)))\n return InferenceFact(fact)\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.input_count","title":"input_count()
","text":"Return the number of inputs of the model
Source code in tract/inference_model.py
def input_count(self) -> int:\n\"\"\"Return the number of inputs of the model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_inference_model_input_count(self.ptr, byref(i)))\n return i.value\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.input_fact","title":"input_fact(input_id)
","text":"Extract the InferenceFact of the input_id
th input.
Source code in tract/inference_model.py
def input_fact(self, input_id: int) -> InferenceFact:\n\"\"\"Extract the InferenceFact of the `input_id`th input.\"\"\"\n self._valid()\n fact = c_void_p()\n check(lib.tract_inference_model_input_fact(self.ptr, input_id, byref(fact)))\n return InferenceFact(fact)\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.input_name","title":"input_name(input_id)
","text":"Return the name of the input_id
th input.
Source code in tract/inference_model.py
def input_name(self, input_id: int) -> str:\n\"\"\"Return the name of the `input_id`th input.\"\"\"\n self._valid()\n cstring = c_char_p()\n check(lib.tract_inference_model_input_name(self.ptr, input_id, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.into_analysed","title":"into_analysed()
","text":"Perform shape and element type inference on the model.
Source code in tract/inference_model.py
def into_analysed(self) -> \"InferenceModel\":\n\"\"\"\n Perform shape and element type inference on the model.\n \"\"\"\n self.analyse()\n return self\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.into_optimized","title":"into_optimized()
","text":"Run the InferenceModel through the full tract optimisation pipeline to get an optimised Model.
Source code in tract/inference_model.py
def into_optimized(self) -> Model:\n\"\"\"\n Run the InferenceModel through the full tract optimisation pipeline to get an\n optimised Model.\n \"\"\"\n self._valid()\n model = c_void_p()\n check(lib.tract_inference_model_into_optimized(byref(self.ptr), byref(model)))\n return Model(model)\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.into_typed","title":"into_typed()
","text":"Convert an InferenceModel to a regular typed Model
.
This will leave the opportunity to run more transformation on the intermediary form of the model, before optimisint it all the way.
Source code in tract/inference_model.py
def into_typed(self) -> Model:\n\"\"\"\n Convert an InferenceModel to a regular typed `Model`.\n\n This will leave the opportunity to run more transformation on the intermediary form of the\n model, before optimisint it all the way.\n \"\"\"\n self._valid()\n model = c_void_p()\n check(lib.tract_inference_model_into_typed(byref(self.ptr), byref(model)))\n return Model(model)\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.output_count","title":"output_count()
","text":"Return the number of outputs of the model
Source code in tract/inference_model.py
def output_count(self) -> int:\n\"\"\"Return the number of outputs of the model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_inference_model_output_count(self.ptr, byref(i)))\n return i.value\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.output_fact","title":"output_fact(output_id)
","text":"Extract the InferenceFact of the output_id
th output.
Source code in tract/inference_model.py
def output_fact(self, output_id: int) -> InferenceFact:\n\"\"\"Extract the InferenceFact of the `output_id`th output.\"\"\"\n self._valid()\n fact = c_void_p()\n check(lib.tract_inference_model_output_fact(self.ptr, output_id, byref(fact)))\n return InferenceFact(fact)\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.output_name","title":"output_name(output_id)
","text":"Return the name of the output_id
th output.
Source code in tract/inference_model.py
def output_name(self, output_id: int) -> str:\n\"\"\"Return the name of the `output_id`th output.\"\"\"\n self._valid()\n cstring = c_char_p()\n check(lib.tract_inference_model_output_name(self.ptr, output_id, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.set_input_fact","title":"set_input_fact(input_id, fact)
","text":"Change the InferenceFact of the input_id
th input.
Source code in tract/inference_model.py
def set_input_fact(self, input_id: int, fact: Union[InferenceFact, str, None]) -> None:\n\"\"\"Change the InferenceFact of the `input_id`th input.\"\"\"\n self._valid()\n if isinstance(fact, str):\n fact = self.fact(fact)\n if fact == None:\n check(lib.tract_inference_model_set_input_fact(self.ptr, input_id, None))\n else:\n check(lib.tract_inference_model_set_input_fact(self.ptr, input_id, fact.ptr))\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.set_output_fact","title":"set_output_fact(output_id, fact)
","text":"Change the InferenceFact of the output_id
th output.
Source code in tract/inference_model.py
def set_output_fact(self, output_id: int, fact: Union[InferenceFact, str, None]) -> None:\n\"\"\"Change the InferenceFact of the `output_id`th output.\"\"\"\n self._valid()\n if isinstance(fact, str):\n fact = self.fact(fact)\n if fact == None:\n check(lib.tract_inference_model_set_output_fact(self.ptr, output_id, None))\n else:\n check(lib.tract_inference_model_set_output_fact(self.ptr, output_id, fact.ptr))\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.set_output_names","title":"set_output_names(names)
","text":"Change the output nodes of the model
Source code in tract/inference_model.py
def set_output_names(self, names: List[str]):\n\"\"\"Change the output nodes of the model\"\"\"\n self._valid()\n nb = len(names)\n names_str = []\n names_ptr = (c_char_p * nb)()\n for ix, n in enumerate(names):\n names_str.append(str(n).encode(\"utf-8\"))\n names_ptr[ix] = names_str[ix]\n check(lib.tract_inference_model_set_output_names(self.ptr, nb, names_ptr))\n
"},{"location":"model/","title":"Model (aka Typed Model)","text":""},{"location":"model/#tract.model.Model","title":"Model
","text":""},{"location":"model/#tract.model.Model--main-model-object","title":"Main model object","text":""},{"location":"model/#tract.model.Model--central-focus-point-of-the-model-transformation-pipeline","title":"Central focus point of the model transformation pipeline","text":"The Model is the central point of tract model loading and \"model cooking\". ONNX and NNEF serialized models are converted to Model (more or less directly) before we can do anything of value with them. Model can be dumped to NNEF (or tract-opl which is NNEF plus tract proprietary extensions).
A Model can be optimize()
, substituing the \"high level\" operators in tract-core operator set by the best implementation available for the current system. From there it can be transformed into a Runnable object that we will use to run.
"},{"location":"model/#tract.model.Model--model-cooking","title":"Model cooking","text":"But some model transformations can be performed on the Model class:
- declutter (getting rid of training artefacts)
- \"pulsification\" (transforming a batch-oriented model into a streaming model)
- symbol substitution (make N or Batch a fixed number, unlocking potential optimisation later on)
- static cost evalation and dynamic profiling
- ...
In some situation, these operation are done \"on-the-fly\" when a ONNX or NNEF model is loaded, at start-up time. In other situation, when start-up time becomes an issue, it may be beneficial to \"pre-cook\" the model: apply the transformations one time, serialize the model as NNEF (with tract-opl extension if needed). At start-up this model can be significantly less expensive to \"cook\" for inference.
"},{"location":"model/#tract.model.Model--model-and-typedmodel","title":"Model and TypedModel","text":"This class is actually a wrapper around the \"TypedModel\" in Rust codebase. The \"Typed\" bit means than all shapes and element types in all input, output and temporary values must known. There is support in tract for symbols in dimensions, with some limited computation capabilities on symbolic expression. For instance, it is relatively frequent to work with a Model where all tensors shapes start with the N
or Batch
.
Source code in tract/model.py
class Model:\n\"\"\"\n # Main model object\n\n ## Central focus point of the model transformation pipeline\n\n The Model is the central point of tract model loading and \"model cooking\". ONNX and NNEF \n serialized models are converted to Model (more or less directly) before we can do anything\n of value with them. Model can be dumped to NNEF (or tract-opl which is NNEF plus tract\n proprietary extensions).\n\n A Model can be `optimize()`, substituing the \"high level\" operators in tract-core operator set by\n the best implementation available for the current system. From there it can be transformed into a \n Runnable object that we will use to run.\n\n ## Model cooking\n\n But some model transformations can be performed on the Model class:\n\n - declutter (getting rid of training artefacts)\n - \"pulsification\" (transforming a batch-oriented model into a streaming model)\n - symbol substitution (make N or Batch a fixed number, unlocking potential optimisation later on)\n - static cost evalation and dynamic profiling\n - ...\n\n In some situation, these operation are done \"on-the-fly\" when a ONNX or NNEF model is loaded,\n at start-up time. In other situation, when start-up time becomes an issue, it may be beneficial\n to \"pre-cook\" the model: apply the transformations one time, serialize the model as NNEF (with\n tract-opl extension if needed). At start-up this model can be significantly less expensive to\n \"cook\" for inference.\n\n ## Model and TypedModel\n\n This class is actually a wrapper around the \"TypedModel\" in Rust codebase. The \"Typed\"\n bit means than all shapes and element types in all input, output and temporary values must\n known. There is support in tract for symbols in dimensions, with some limited computation\n capabilities on symbolic expression. For instance, it is relatively frequent to work with\n a Model where all tensors shapes start with the `N` or `Batch`.\n \"\"\"\n\n def __init__(self, ptr):\n self.ptr = ptr\n\n def __del__(self):\n if self.ptr:\n check(lib.tract_model_destroy(byref(self.ptr)))\n\n def _valid(self):\n if self.ptr == None:\n raise TractError(\"invalid model (maybe already consumed ?)\")\n\n def input_count(self) -> int:\n\"\"\"Return the number of inputs of the model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_model_input_count(self.ptr, byref(i)))\n return i.value\n\n def output_count(self) -> int:\n\"\"\"Return the number of outputs of the model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_model_output_count(self.ptr, byref(i)))\n return i.value\n\n def input_name(self, input_id: int) -> str:\n\"\"\"Return the name of the input_id-th input\"\"\"\n self._valid()\n cstring = c_char_p()\n check(lib.tract_model_input_name(self.ptr, input_id, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n\n def input_fact(self, input_id: int) -> Fact:\n\"\"\"Return the fact of the input_id-th input\"\"\"\n self._valid()\n fact = c_void_p()\n check(lib.tract_model_input_fact(self.ptr, input_id, byref(fact)))\n return Fact(fact)\n\n def set_output_names(self, names: List[str]):\n\"\"\"Change the output nodes of the model\"\"\"\n self._valid()\n nb = len(names)\n names_str = []\n names_ptr = (c_char_p * nb)()\n for ix, n in enumerate(names):\n names_str.append(str(n).encode(\"utf-8\"))\n names_ptr[ix] = names_str[ix]\n check(lib.tract_model_set_output_names(self.ptr, nb, names_ptr))\n\n def output_name(self, output_id: int) -> str:\n\"\"\"Return the name of the output_id-th output\"\"\"\n self._valid()\n cstring = c_char_p()\n check(lib.tract_model_output_name(self.ptr, output_id, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n\n def output_fact(self, input_id: int) -> Fact:\n\"\"\"Return the fact of the output_id-th output\"\"\"\n self._valid()\n fact = c_void_p()\n check(lib.tract_model_output_fact(self.ptr, input_id, byref(fact)))\n return Fact(fact)\n\n def concretize_symbols(self, values: Dict[str, int]) -> None:\n\"\"\"Substitute symbols by a value\n\n Replace all occurencies of the symbols in the dictionary, in all the Model facts shapes.\n\n While this is not strictly necesary, the optimizing steps may make better choices if the model\n is informed of some specific symbol values.\n \"\"\"\n self._valid()\n nb = len(values)\n names_str = []\n names = (c_char_p * nb)()\n values_list = (c_int64 * nb)()\n for ix, (k, v) in enumerate(values.items()):\n names_str.append(str(k).encode(\"utf-8\"))\n names[ix] = names_str[ix]\n values_list[ix] = v\n check(lib.tract_model_concretize_symbols(self.ptr, c_size_t(nb), names, values_list))\n\n def pulse(self, symbol: str, pulse: Union[str, int]) -> None:\n\"\"\"Pulsify a model.\n\n `pulse` is typically a one-length dictionary mapping the time dimension symbol to a pulse len.\n \"\"\"\n self._valid()\n check(lib.tract_model_pulse_simple(byref(self.ptr), symbol.encode(\"utf-8\"), str(pulse).encode(\"utf-8\")))\n\n def half(self) -> None:\n\"\"\"Convert the model to hald precision\n \"\"\"\n self._valid()\n check(lib.tract_model_half(self.ptr))\n\n def declutter(self) -> None:\n\"\"\"Declutter a model.\n\n Perform the first \"half\" of optimisation phases, consisting or removing training artefacts and converge\n on tract-core canonical form.\n \"\"\"\n self._valid()\n check(lib.tract_model_declutter(self.ptr))\n\n def optimize(self) -> None:\n\"\"\"Optimize a model.\n\n Perform the second \"half\" of optimisation phases, consisting of translating the tract-core canonical\n form to the best runtime for the current architecture.\n \"\"\"\n self._valid()\n check(lib.tract_model_optimize(self.ptr))\n\n def into_decluttered(self) -> \"Model\":\n\"\"\"Convenience method performing `declutter()` and returning the model\"\"\"\n self.declutter();\n return self\n\n def into_optimized(self) -> \"Model\":\n\"\"\"Convenience method performing `optimize()` and returning the model\"\"\"\n self.optimize()\n return self\n\n def into_runnable(self) -> Runnable:\n\"\"\"Transform the model into a ready to be used Runnable model\"\"\"\n self._valid()\n runnable = c_void_p()\n check(lib.tract_model_into_runnable(byref(self.ptr), byref(runnable)))\n return Runnable(runnable)\n\n def property_keys(self) -> List[str]:\n\"\"\"Query the list of properties names of the model.\"\"\"\n self._valid()\n count = c_size_t()\n check(lib.tract_model_property_count(self.ptr, byref(count)))\n count = count.value\n cstrings = (POINTER(c_char) * count)()\n check(lib.tract_model_property_names(self.ptr, cstrings))\n names = []\n for i in range(0, count):\n names.append(str(cast(cstrings[i], c_char_p).value, \"utf-8\"))\n lib.tract_free_cstring(cstrings[i])\n return names\n\n def property(self, name: str) -> Value:\n\"\"\"Query a property by name\"\"\"\n self._valid()\n value = c_void_p()\n check(lib.tract_model_property(self.ptr, str(name).encode(\"utf-8\"), byref(value)))\n return Value(value)\n\n def profile_json(self, inputs: Union[None, List[Union[Value, numpy.ndarray]]]) -> str:\n\"\"\"Profile the model. Also compute the static costs of operators.\n\n Returns is a json buffer.\n \"\"\"\n self._valid()\n cstring = c_char_p()\n input_values = []\n input_ptrs = None\n if inputs != None:\n for v in inputs:\n if isinstance(v, Value):\n input_values.append(v)\n elif isinstance(v, numpy.ndarray):\n input_values.append(Value.from_numpy(v))\n else:\n raise TractError(f\"Inputs must be of type tract.Value or numpy.Array, got {v}\")\n input_ptrs = (c_void_p * len(inputs))()\n for ix, v in enumerate(input_values):\n input_ptrs[ix] = v.ptr\n check(lib.tract_model_profile_json(self.ptr, input_ptrs, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n
"},{"location":"model/#tract.model.Model.concretize_symbols","title":"concretize_symbols(values)
","text":"Substitute symbols by a value
Replace all occurencies of the symbols in the dictionary, in all the Model facts shapes.
While this is not strictly necesary, the optimizing steps may make better choices if the model is informed of some specific symbol values.
Source code in tract/model.py
def concretize_symbols(self, values: Dict[str, int]) -> None:\n\"\"\"Substitute symbols by a value\n\n Replace all occurencies of the symbols in the dictionary, in all the Model facts shapes.\n\n While this is not strictly necesary, the optimizing steps may make better choices if the model\n is informed of some specific symbol values.\n \"\"\"\n self._valid()\n nb = len(values)\n names_str = []\n names = (c_char_p * nb)()\n values_list = (c_int64 * nb)()\n for ix, (k, v) in enumerate(values.items()):\n names_str.append(str(k).encode(\"utf-8\"))\n names[ix] = names_str[ix]\n values_list[ix] = v\n check(lib.tract_model_concretize_symbols(self.ptr, c_size_t(nb), names, values_list))\n
"},{"location":"model/#tract.model.Model.declutter","title":"declutter()
","text":"Declutter a model.
Perform the first \"half\" of optimisation phases, consisting or removing training artefacts and converge on tract-core canonical form.
Source code in tract/model.py
def declutter(self) -> None:\n\"\"\"Declutter a model.\n\n Perform the first \"half\" of optimisation phases, consisting or removing training artefacts and converge\n on tract-core canonical form.\n \"\"\"\n self._valid()\n check(lib.tract_model_declutter(self.ptr))\n
"},{"location":"model/#tract.model.Model.half","title":"half()
","text":"Convert the model to hald precision
Source code in tract/model.py
def half(self) -> None:\n\"\"\"Convert the model to hald precision\n \"\"\"\n self._valid()\n check(lib.tract_model_half(self.ptr))\n
"},{"location":"model/#tract.model.Model.input_count","title":"input_count()
","text":"Return the number of inputs of the model
Source code in tract/model.py
def input_count(self) -> int:\n\"\"\"Return the number of inputs of the model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_model_input_count(self.ptr, byref(i)))\n return i.value\n
"},{"location":"model/#tract.model.Model.input_fact","title":"input_fact(input_id)
","text":"Return the fact of the input_id-th input
Source code in tract/model.py
def input_fact(self, input_id: int) -> Fact:\n\"\"\"Return the fact of the input_id-th input\"\"\"\n self._valid()\n fact = c_void_p()\n check(lib.tract_model_input_fact(self.ptr, input_id, byref(fact)))\n return Fact(fact)\n
"},{"location":"model/#tract.model.Model.input_name","title":"input_name(input_id)
","text":"Return the name of the input_id-th input
Source code in tract/model.py
def input_name(self, input_id: int) -> str:\n\"\"\"Return the name of the input_id-th input\"\"\"\n self._valid()\n cstring = c_char_p()\n check(lib.tract_model_input_name(self.ptr, input_id, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n
"},{"location":"model/#tract.model.Model.into_decluttered","title":"into_decluttered()
","text":"Convenience method performing declutter()
and returning the model
Source code in tract/model.py
def into_decluttered(self) -> \"Model\":\n\"\"\"Convenience method performing `declutter()` and returning the model\"\"\"\n self.declutter();\n return self\n
"},{"location":"model/#tract.model.Model.into_optimized","title":"into_optimized()
","text":"Convenience method performing optimize()
and returning the model
Source code in tract/model.py
def into_optimized(self) -> \"Model\":\n\"\"\"Convenience method performing `optimize()` and returning the model\"\"\"\n self.optimize()\n return self\n
"},{"location":"model/#tract.model.Model.into_runnable","title":"into_runnable()
","text":"Transform the model into a ready to be used Runnable model
Source code in tract/model.py
def into_runnable(self) -> Runnable:\n\"\"\"Transform the model into a ready to be used Runnable model\"\"\"\n self._valid()\n runnable = c_void_p()\n check(lib.tract_model_into_runnable(byref(self.ptr), byref(runnable)))\n return Runnable(runnable)\n
"},{"location":"model/#tract.model.Model.optimize","title":"optimize()
","text":"Optimize a model.
Perform the second \"half\" of optimisation phases, consisting of translating the tract-core canonical form to the best runtime for the current architecture.
Source code in tract/model.py
def optimize(self) -> None:\n\"\"\"Optimize a model.\n\n Perform the second \"half\" of optimisation phases, consisting of translating the tract-core canonical\n form to the best runtime for the current architecture.\n \"\"\"\n self._valid()\n check(lib.tract_model_optimize(self.ptr))\n
"},{"location":"model/#tract.model.Model.output_count","title":"output_count()
","text":"Return the number of outputs of the model
Source code in tract/model.py
def output_count(self) -> int:\n\"\"\"Return the number of outputs of the model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_model_output_count(self.ptr, byref(i)))\n return i.value\n
"},{"location":"model/#tract.model.Model.output_fact","title":"output_fact(input_id)
","text":"Return the fact of the output_id-th output
Source code in tract/model.py
def output_fact(self, input_id: int) -> Fact:\n\"\"\"Return the fact of the output_id-th output\"\"\"\n self._valid()\n fact = c_void_p()\n check(lib.tract_model_output_fact(self.ptr, input_id, byref(fact)))\n return Fact(fact)\n
"},{"location":"model/#tract.model.Model.output_name","title":"output_name(output_id)
","text":"Return the name of the output_id-th output
Source code in tract/model.py
def output_name(self, output_id: int) -> str:\n\"\"\"Return the name of the output_id-th output\"\"\"\n self._valid()\n cstring = c_char_p()\n check(lib.tract_model_output_name(self.ptr, output_id, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n
"},{"location":"model/#tract.model.Model.profile_json","title":"profile_json(inputs)
","text":"Profile the model. Also compute the static costs of operators.
Returns is a json buffer.
Source code in tract/model.py
def profile_json(self, inputs: Union[None, List[Union[Value, numpy.ndarray]]]) -> str:\n\"\"\"Profile the model. Also compute the static costs of operators.\n\n Returns is a json buffer.\n \"\"\"\n self._valid()\n cstring = c_char_p()\n input_values = []\n input_ptrs = None\n if inputs != None:\n for v in inputs:\n if isinstance(v, Value):\n input_values.append(v)\n elif isinstance(v, numpy.ndarray):\n input_values.append(Value.from_numpy(v))\n else:\n raise TractError(f\"Inputs must be of type tract.Value or numpy.Array, got {v}\")\n input_ptrs = (c_void_p * len(inputs))()\n for ix, v in enumerate(input_values):\n input_ptrs[ix] = v.ptr\n check(lib.tract_model_profile_json(self.ptr, input_ptrs, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n
"},{"location":"model/#tract.model.Model.property","title":"property(name)
","text":"Query a property by name
Source code in tract/model.py
def property(self, name: str) -> Value:\n\"\"\"Query a property by name\"\"\"\n self._valid()\n value = c_void_p()\n check(lib.tract_model_property(self.ptr, str(name).encode(\"utf-8\"), byref(value)))\n return Value(value)\n
"},{"location":"model/#tract.model.Model.property_keys","title":"property_keys()
","text":"Query the list of properties names of the model.
Source code in tract/model.py
def property_keys(self) -> List[str]:\n\"\"\"Query the list of properties names of the model.\"\"\"\n self._valid()\n count = c_size_t()\n check(lib.tract_model_property_count(self.ptr, byref(count)))\n count = count.value\n cstrings = (POINTER(c_char) * count)()\n check(lib.tract_model_property_names(self.ptr, cstrings))\n names = []\n for i in range(0, count):\n names.append(str(cast(cstrings[i], c_char_p).value, \"utf-8\"))\n lib.tract_free_cstring(cstrings[i])\n return names\n
"},{"location":"model/#tract.model.Model.pulse","title":"pulse(symbol, pulse)
","text":"Pulsify a model.
pulse
is typically a one-length dictionary mapping the time dimension symbol to a pulse len.
Source code in tract/model.py
def pulse(self, symbol: str, pulse: Union[str, int]) -> None:\n\"\"\"Pulsify a model.\n\n `pulse` is typically a one-length dictionary mapping the time dimension symbol to a pulse len.\n \"\"\"\n self._valid()\n check(lib.tract_model_pulse_simple(byref(self.ptr), symbol.encode(\"utf-8\"), str(pulse).encode(\"utf-8\")))\n
"},{"location":"model/#tract.model.Model.set_output_names","title":"set_output_names(names)
","text":"Change the output nodes of the model
Source code in tract/model.py
def set_output_names(self, names: List[str]):\n\"\"\"Change the output nodes of the model\"\"\"\n self._valid()\n nb = len(names)\n names_str = []\n names_ptr = (c_char_p * nb)()\n for ix, n in enumerate(names):\n names_str.append(str(n).encode(\"utf-8\"))\n names_ptr[ix] = names_str[ix]\n check(lib.tract_model_set_output_names(self.ptr, nb, names_ptr))\n
"},{"location":"nnef/","title":"NNEF","text":""},{"location":"nnef/#tract.nnef.Nnef","title":"Nnef
","text":"Represent a NNEF context in tract.
NNEF is a neural model interchange format, similar to ONNX but focusing on the needs of an inference engine instead of a training framework.
tract
can natively load NNEF models. It can also save models it tract internal format as tract-opl
models. tract-opl
is a set of proprierary extensions to NNEF allowing to serializeing most of the models tract can handle. These extension can be activated by the with_*() methods
.
Source code in tract/nnef.py
class Nnef:\n\"\"\"\n Represent a NNEF context in tract.\n\n NNEF is a neural model interchange format, similar to ONNX but focusing on the needs\n of an inference engine instead of a training framework.\n\n `tract` can natively load NNEF models. It can also save models it tract internal format\n as `tract-opl` models. `tract-opl` is a set of proprierary extensions to NNEF allowing to\n serializeing most of the models tract can handle. These extension can be activated by the\n `with_*() methods`.\n \"\"\"\n\n def __init__(self):\n ptr = c_void_p()\n check(lib.tract_nnef_create(byref(ptr)))\n self.ptr = ptr\n\n def __del__(self):\n check(lib.tract_nnef_destroy(byref(self.ptr)))\n\n def _valid(self):\n if self.ptr == None:\n raise TractError(\"invalid inference model (maybe already consumed ?)\")\n\n def model_for_path(self, path: Union[str, Path]) -> Model:\n\"\"\"\n Load an NNEF model from the file or folder at `path`\n\n ```python\n model = (\n tract.nnef()\n .model_for_path(\"mobilenet_v2_1.0.onnx.nnef.tgz\")\n .into_optimized()\n .into_runnable()\n )\n ```\n \"\"\"\n self._valid()\n model = c_void_p()\n path = str(path).encode(\"utf-8\")\n check(lib.tract_nnef_model_for_path(self.ptr, path, byref(model)))\n return Model(model)\n\n def with_tract_core(self) -> \"Nnef\":\n\"\"\"\n Enable tract-opl extensions to NNEF to covers tract-core operator set\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_tract_core(self.ptr))\n return self\n\n def with_onnx(self) -> \"Nnef\":\n\"\"\"\n Enable tract-opl extensions to NNEF to covers (more or) ONNX operator set\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_onnx(self.ptr))\n return self\n\n def with_pulse(self) -> \"Nnef\":\n\"\"\"\n Enable tract-opl extensions to NNEF for tract pulse operators (for audio streaming)\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_pulse(self.ptr))\n return self\n\n def with_extended_identifier_syntax(self) -> \"Nnef\":\n\"\"\"\n Enable tract-opl extensions to NNEF for extended identifiers (will support PyTorch 2 path-like ids)\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_extended_identifier_syntax(self.ptr, True))\n return self\n\n def write_model_to_dir(self, model: Model, path: Union[str, Path]) -> None:\n\"\"\"\n Save `model` as a NNEF directory model in `path`.\n\n tract tries to stick to strict NNEF even if extensions has been enabled.\n \"\"\"\n self._valid()\n model._valid()\n if not isinstance(model, Model):\n raise TractError(\"Expected a Model, called with \" + model);\n path = str(path).encode(\"utf-8\")\n check(lib.tract_nnef_write_model_to_dir(self.ptr, path, model.ptr))\n\n def write_model_to_tar(self, model: Model, path: Union[str, Path]) -> None:\n\"\"\"\n Save `model` as a NNEF tar archive in `path`.\n\n tract tries to stick to strict NNEF even if extensions has been enabled.\n \"\"\"\n self._valid()\n model._valid()\n if not isinstance(model, Model):\n raise TractError(\"Expected a Model, called with \" + model);\n path = str(path).encode(\"utf-8\")\n check(lib.tract_nnef_write_model_to_tar(self.ptr, path, model.ptr))\n\n def write_model_to_tar_gz(self, model: Model, path: Union[str, Path]) -> None:\n\"\"\"\n Save `model` as a NNEF tar compressed archive in `path`.\n\n tract tries to stick to strict NNEF even if extensions has been enabled.\n \"\"\"\n self._valid()\n model._valid()\n if not isinstance(model, Model):\n raise TractError(\"Expected a Model, called with \" + model);\n path = str(path).encode(\"utf-8\")\n check(lib.tract_nnef_write_model_to_tar_gz(self.ptr, path, model.ptr))\n
"},{"location":"nnef/#tract.nnef.Nnef.model_for_path","title":"model_for_path(path)
","text":"Load an NNEF model from the file or folder at path
model = (\n tract.nnef()\n .model_for_path(\"mobilenet_v2_1.0.onnx.nnef.tgz\")\n .into_optimized()\n .into_runnable()\n)\n
Source code in tract/nnef.py
def model_for_path(self, path: Union[str, Path]) -> Model:\n\"\"\"\n Load an NNEF model from the file or folder at `path`\n\n ```python\n model = (\n tract.nnef()\n .model_for_path(\"mobilenet_v2_1.0.onnx.nnef.tgz\")\n .into_optimized()\n .into_runnable()\n )\n ```\n \"\"\"\n self._valid()\n model = c_void_p()\n path = str(path).encode(\"utf-8\")\n check(lib.tract_nnef_model_for_path(self.ptr, path, byref(model)))\n return Model(model)\n
"},{"location":"nnef/#tract.nnef.Nnef.with_extended_identifier_syntax","title":"with_extended_identifier_syntax()
","text":"Enable tract-opl extensions to NNEF for extended identifiers (will support PyTorch 2 path-like ids)
Source code in tract/nnef.py
def with_extended_identifier_syntax(self) -> \"Nnef\":\n\"\"\"\n Enable tract-opl extensions to NNEF for extended identifiers (will support PyTorch 2 path-like ids)\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_extended_identifier_syntax(self.ptr, True))\n return self\n
"},{"location":"nnef/#tract.nnef.Nnef.with_onnx","title":"with_onnx()
","text":"Enable tract-opl extensions to NNEF to covers (more or) ONNX operator set
Source code in tract/nnef.py
def with_onnx(self) -> \"Nnef\":\n\"\"\"\n Enable tract-opl extensions to NNEF to covers (more or) ONNX operator set\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_onnx(self.ptr))\n return self\n
"},{"location":"nnef/#tract.nnef.Nnef.with_pulse","title":"with_pulse()
","text":"Enable tract-opl extensions to NNEF for tract pulse operators (for audio streaming)
Source code in tract/nnef.py
def with_pulse(self) -> \"Nnef\":\n\"\"\"\n Enable tract-opl extensions to NNEF for tract pulse operators (for audio streaming)\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_pulse(self.ptr))\n return self\n
"},{"location":"nnef/#tract.nnef.Nnef.with_tract_core","title":"with_tract_core()
","text":"Enable tract-opl extensions to NNEF to covers tract-core operator set
Source code in tract/nnef.py
def with_tract_core(self) -> \"Nnef\":\n\"\"\"\n Enable tract-opl extensions to NNEF to covers tract-core operator set\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_tract_core(self.ptr))\n return self\n
"},{"location":"nnef/#tract.nnef.Nnef.write_model_to_dir","title":"write_model_to_dir(model, path)
","text":"Save model
as a NNEF directory model in path
.
tract tries to stick to strict NNEF even if extensions has been enabled.
Source code in tract/nnef.py
def write_model_to_dir(self, model: Model, path: Union[str, Path]) -> None:\n\"\"\"\n Save `model` as a NNEF directory model in `path`.\n\n tract tries to stick to strict NNEF even if extensions has been enabled.\n \"\"\"\n self._valid()\n model._valid()\n if not isinstance(model, Model):\n raise TractError(\"Expected a Model, called with \" + model);\n path = str(path).encode(\"utf-8\")\n check(lib.tract_nnef_write_model_to_dir(self.ptr, path, model.ptr))\n
"},{"location":"nnef/#tract.nnef.Nnef.write_model_to_tar","title":"write_model_to_tar(model, path)
","text":"Save model
as a NNEF tar archive in path
.
tract tries to stick to strict NNEF even if extensions has been enabled.
Source code in tract/nnef.py
def write_model_to_tar(self, model: Model, path: Union[str, Path]) -> None:\n\"\"\"\n Save `model` as a NNEF tar archive in `path`.\n\n tract tries to stick to strict NNEF even if extensions has been enabled.\n \"\"\"\n self._valid()\n model._valid()\n if not isinstance(model, Model):\n raise TractError(\"Expected a Model, called with \" + model);\n path = str(path).encode(\"utf-8\")\n check(lib.tract_nnef_write_model_to_tar(self.ptr, path, model.ptr))\n
"},{"location":"nnef/#tract.nnef.Nnef.write_model_to_tar_gz","title":"write_model_to_tar_gz(model, path)
","text":"Save model
as a NNEF tar compressed archive in path
.
tract tries to stick to strict NNEF even if extensions has been enabled.
Source code in tract/nnef.py
def write_model_to_tar_gz(self, model: Model, path: Union[str, Path]) -> None:\n\"\"\"\n Save `model` as a NNEF tar compressed archive in `path`.\n\n tract tries to stick to strict NNEF even if extensions has been enabled.\n \"\"\"\n self._valid()\n model._valid()\n if not isinstance(model, Model):\n raise TractError(\"Expected a Model, called with \" + model);\n path = str(path).encode(\"utf-8\")\n check(lib.tract_nnef_write_model_to_tar_gz(self.ptr, path, model.ptr))\n
"},{"location":"onnx/","title":"ONNX","text":""},{"location":"onnx/#tract.onnx.Onnx","title":"Onnx
","text":"Represent the ONNX context in tract.
It essentially allows to load ONNX models. Note that an ONNX model is loaded as an InferenceModel
and not as a Model
: many ONNX models come with partial shape and element type information, while tract's Model
assume full shape and element type knownledge. In this case, it is generally sufficient to inform tract about the input shape and type, then let tract infer the rest of the missing shape information before converting the InferenceModel
to a regular Model
.
# load the model as an InferenceModel\nmodel = tract.onnx().model_for_path(\"./mobilenetv2-7.onnx\")\n\n# set the shape and type of its first and only input\nmodel.set_input_fact(0, \"1,3,224,224,f32\")\n\n# get ready to run the model\nmodel = model.into_optimized().into_runnable()\n
Source code in tract/onnx.py
class Onnx:\n\"\"\"\n Represent the ONNX context in tract.\n\n It essentially allows to load ONNX models. Note that an ONNX model is loaded as an\n `InferenceModel` and not as a `Model`: many ONNX models come with partial shape and\n element type information, while tract's `Model` assume full shape and element type\n knownledge. In this case, it is generally sufficient to inform tract about the input\n shape and type, then let tract *infer* the rest of the missing shape information\n before converting the `InferenceModel` to a regular `Model`.\n\n ```python\n # load the model as an InferenceModel\n model = tract.onnx().model_for_path(\"./mobilenetv2-7.onnx\")\n\n # set the shape and type of its first and only input\n model.set_input_fact(0, \"1,3,224,224,f32\")\n\n # get ready to run the model\n model = model.into_optimized().into_runnable()\n ```\n \"\"\"\n\n def __init__(self):\n ptr = c_void_p()\n check(lib.tract_onnx_create(byref(ptr)))\n self.ptr = ptr\n\n def __del__(self):\n check(lib.tract_onnx_destroy(byref(self.ptr)))\n\n def model_for_path(self, path: Union[str, Path]) -> InferenceModel:\n\"\"\"\n Load an ONNX file as an InferenceModel\n \"\"\"\n model = c_void_p()\n path = str(path).encode(\"utf-8\")\n check(lib.tract_onnx_model_for_path(self.ptr, path, byref(model)))\n return InferenceModel(model)\n
"},{"location":"onnx/#tract.onnx.Onnx.model_for_path","title":"model_for_path(path)
","text":"Load an ONNX file as an InferenceModel
Source code in tract/onnx.py
def model_for_path(self, path: Union[str, Path]) -> InferenceModel:\n\"\"\"\n Load an ONNX file as an InferenceModel\n \"\"\"\n model = c_void_p()\n path = str(path).encode(\"utf-8\")\n check(lib.tract_onnx_model_for_path(self.ptr, path, byref(model)))\n return InferenceModel(model)\n
"},{"location":"runnable/","title":"Runnable model","text":""},{"location":"runnable/#tract.runnable.Runnable","title":"Runnable
","text":"A model in the Runnable state is ready to perform computation.
Source code in tract/runnable.py
class Runnable:\n\"\"\"\n A model in the Runnable state is ready to perform computation.\n \"\"\"\n def __init__(self, ptr):\n self.ptr = ptr\n\n def __del__(self):\n check(lib.tract_runnable_release(byref(self.ptr)))\n\n def _valid(self):\n if self.ptr == None:\n raise TractError(\"invalid runnable (maybe already consumed ?)\")\n\n def input_count(self) -> int:\n\"\"\"Return the number of inputs of the underlying model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_runnable_input_count(self.ptr, byref(i)))\n return i.value\n\n def output_count(self) -> int:\n\"\"\"Return the number of outputs of the underlying model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_runnable_output_count(self.ptr, byref(i)))\n return i.value\n\n def run(self, inputs: List[Union[Value, numpy.ndarray]]) -> List[Value]:\n\"\"\"\n Runs the model over the provided input list, and returns the model outputs.\n \"\"\"\n return self.spawn_state().run(inputs)\n\n def spawn_state(self):\n self._valid()\n state = c_void_p()\n check(lib.tract_runnable_spawn_state(self.ptr, byref(state)))\n return State(state)\n
"},{"location":"runnable/#tract.runnable.Runnable.input_count","title":"input_count()
","text":"Return the number of inputs of the underlying model
Source code in tract/runnable.py
def input_count(self) -> int:\n\"\"\"Return the number of inputs of the underlying model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_runnable_input_count(self.ptr, byref(i)))\n return i.value\n
"},{"location":"runnable/#tract.runnable.Runnable.output_count","title":"output_count()
","text":"Return the number of outputs of the underlying model
Source code in tract/runnable.py
def output_count(self) -> int:\n\"\"\"Return the number of outputs of the underlying model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_runnable_output_count(self.ptr, byref(i)))\n return i.value\n
"},{"location":"runnable/#tract.runnable.Runnable.run","title":"run(inputs)
","text":"Runs the model over the provided input list, and returns the model outputs.
Source code in tract/runnable.py
def run(self, inputs: List[Union[Value, numpy.ndarray]]) -> List[Value]:\n\"\"\"\n Runs the model over the provided input list, and returns the model outputs.\n \"\"\"\n return self.spawn_state().run(inputs)\n
"},{"location":"value/","title":"Value","text":""},{"location":"value/#tract.value.Value","title":"Value
","text":"Represents a tensor suitable for manipulation by tract.
On the Python side, the main way to access tensor data is to convert the value to a numpy array.
Source code in tract/value.py
class Value:\n\"\"\"\n Represents a tensor suitable for manipulation by tract.\n\n On the Python side, the main way to access tensor data is to\n convert the value to a numpy array.\n \"\"\"\n def __init__(self, ptr):\n self.ptr = ptr\n\n def __del__(self):\n if self.ptr:\n check(lib.tract_value_destroy(byref(self.ptr)))\n\n def _valid(self):\n if self.ptr == None:\n raise TractError(\"invalid value (maybe already consumed ?)\")\n\n def from_numpy(array: numpy.ndarray) -> \"Value\":\n array = numpy.ascontiguousarray(array)\n\n data = array.__array_interface__['data'][0]\n data = c_void_p(data)\n ptr = c_void_p()\n shape_t = c_size_t * array.ndim\n shape = shape_t()\n for ix in range(0, array.ndim):\n shape[ix] = array.shape[ix]\n dt = dt_numpy_to_tract(array.dtype)\n check(lib.tract_value_from_bytes(dt, c_size_t(array.ndim), shape, data, byref(ptr)))\n return Value(ptr)\n\n def to_numpy(self) -> numpy.array:\n\"\"\"Builds a numpy array equivalent to the data in this value.\"\"\"\n self._valid()\n rank = c_size_t();\n shape = POINTER(c_size_t)()\n dt = c_float\n data = POINTER(dt)()\n check(lib.tract_value_as_bytes(self.ptr, None, byref(rank), byref(shape), byref(data)))\n rank = rank.value\n shape = [ int(shape[ix]) for ix in range(0, rank) ]\n array = numpy.ctypeslib.as_array(data, shape).copy()\n return array\n\n def into_numpy(self) -> numpy.array:\n\"\"\"Same as to_numpy(), but drop the value content once the numpy array is built.\"\"\"\n result = self.to_numpy()\n check(lib.tract_value_destroy(byref(self.ptr)))\n return result\n
"},{"location":"value/#tract.value.Value.into_numpy","title":"into_numpy()
","text":"Same as to_numpy(), but drop the value content once the numpy array is built.
Source code in tract/value.py
def into_numpy(self) -> numpy.array:\n\"\"\"Same as to_numpy(), but drop the value content once the numpy array is built.\"\"\"\n result = self.to_numpy()\n check(lib.tract_value_destroy(byref(self.ptr)))\n return result\n
"},{"location":"value/#tract.value.Value.to_numpy","title":"to_numpy()
","text":"Builds a numpy array equivalent to the data in this value.
Source code in tract/value.py
def to_numpy(self) -> numpy.array:\n\"\"\"Builds a numpy array equivalent to the data in this value.\"\"\"\n self._valid()\n rank = c_size_t();\n shape = POINTER(c_size_t)()\n dt = c_float\n data = POINTER(dt)()\n check(lib.tract_value_as_bytes(self.ptr, None, byref(rank), byref(shape), byref(data)))\n rank = rank.value\n shape = [ int(shape[ix]) for ix in range(0, rank) ]\n array = numpy.ctypeslib.as_array(data, shape).copy()\n return array\n
"}]}
\ No newline at end of file
+{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"tract
python bindings","text":"tract
is a library for neural network inference. While PyTorch and TensorFlow deal with the much harder training problem, tract
focuses on what happens once the model in trained.
tract
ultimate goal is to use the model on end-user data (aka \"running the model\") as efficiently as possible, in a variety of possible deployments, including some which are no completely mainstream : a lot of energy have been invested in making tract
an efficient engine to run models on ARM single board computers.
"},{"location":"#getting-started","title":"Getting started","text":""},{"location":"#install-tract-library","title":"Install tract library","text":"pip install tract
. Prebuilt wheels are provided for x86-64 Linux and Windows, x86-64 and arm64 for MacOS.
"},{"location":"#downloading-the-model","title":"Downloading the model","text":"First we need to obtain the model. We will download an ONNX-converted MobileNET 2.7 from the ONNX model zoo.
wget https://github.com/onnx/models/raw/main/vision/classification/mobilenet/model/mobilenetv2-7.onnx
.
"},{"location":"#preprocessing-an-image","title":"Preprocessing an image","text":"Then we need a sample image. You can use pretty much anything. If you lack inspiration, you can this picture of Grace Hopper.
wget https://s3.amazonaws.com/tract-ci-builds/tests/grace_hopper.jpg
We will be needing pillow
to load the image and crop it.
pip install pillow
Now let's start our python script. We will want to use tract, obviously, but we will also need PIL's Image and numpy to put the data in the form MobileNet expects it.
#!/usr/bin/env python\n\nimport tract\nimport numpy\nfrom PIL import Image\n
We want to load the image, crop it into its central square, then scale this square to be 224x224.
im = Image.open(\"grace_hopper.jpg\")\nif im.height > im.width:\n top_crop = int((im.height - im.width) / 2)\n im = im.crop((0, top_crop, im.width, top_crop + im.width))\nelse:\n left_crop = int((im.width - im.height) / 2)\n im = im.crop((left_crop, 0, left_crop + im_height, im.height))\nim = im.resize((224, 224))\nim = numpy.array(im)\n
At this stage, we obtain a 224x224x3 tensor of 8-bit positive integers. We need to transform these integers to floats and normalize them for MobileNet. At some point during this normalization, numpy decides to promote our tensor to double precision, but our model is single precison, so we are converting it again after the normalization.
im = (im.astype(float) / 255. - [0.485, 0.456, 0.406]) / [0.229, 0.224, 0.225]\nim = im.astype(numpy.single)\n
Finally, ONNX variant of Mobilenet expects its input in NCHW convention, and our data is in HWC. We need to move the C axis before H and W, then insert the N at the left.
im = numpy.moveaxis(im, 2, 0)\nim = numpy.expand_dims(im, 0)\n
"},{"location":"#loading-the-model","title":"Loading the model","text":"Loading a model is relatively simple. We need to instantiate the ONNX loader first, the we use it to load the model. Then we ask tract to optimize the model and get it ready to run.
model = tract.onnx().model_for_path(\"./mobilenetv2-7.onnx\").into_optimized().into_runnable()\n
If we wanted to process several images, this would only have to be done once out of our image loop.
"},{"location":"#running-the-model","title":"Running the model","text":"tract run methods take a list of inputs and returns a list of outputs. Each input can be a numpy array. The outputs are tract's own Value data type, which should be converted to numpy array.
outputs = model.run([im])\noutput = outputs[0].to_numpy()\n
"},{"location":"#interpreting-the-result","title":"Interpreting the result","text":"If we print the output, what we get is a array of 1000 values. Each value is the score of our image on one of the 1000 categoris of ImageNet. What we want is to find the category with the highest score.
print(numpy.argmax(output))\n
If all goes according to plan, this should output the number 652. There is a copy of ImageNet categories at the following URL, with helpful line numbering.
https://github.com/sonos/tract/blob/main/examples/nnef-mobilenet-v2/imagenet_slim_labels.txt\n
And... 652 is \"microphone\". Which is wrong. The trick is, the lines are numbered from 1, while our results starts at 0, plus the label list includes a \"dummy\" label first that should be ignored. So the right value is at the line 654: \"military uniform\". If you looked at the picture before you noticed that Grace Hopper is in uniform on the picture, so it does make sense.
"},{"location":"#model-cooking-with-tract","title":"Model cooking with tract
","text":"Over the years of tract
development, it became clear that beside \"training\" and \"running\", there was a third time in the life-cycle of a model. One of our contributors nicknamed it \"model cooking\" and the term stuck. This extra stage is about all what happens after the training and before running.
If training and Runtime are relatively easy to define, the model cooking gets a bit less obvious. It comes from the realisation that the training form (an ONNX or TensorFlow file or ste of files) of a model may is usually not the most convenient form for running it. Every time a device loads a model in ONNX form and transform it into a suitable form for runtime, it goes through the same series or more or less complicated operations, that can amount to several seconds of high-CPU usage for current models. When running the model on a device, this can have several negative impact on experience: the device will take time to start-up, consume a lot of battery energy to get ready, maybe fight over CPU availability with other processes trying to get ready at the same instant on the device.
As this sequence of operations is generally the same, it becomes relevant to persist the model resulting of the transformation. It could be persisted at the first application start-up for instance. But it could also be \"prepared\", or \"cooked\" before distribution to the devices.
"},{"location":"#cooking-to-nnef","title":"Cooking to NNEF","text":"tract
supports NNEF. It can read a NNEF neural network and run it. But it can also dump its preferred representation of a model in NNEF.
At this stage, a possible path to production for a neural model becomes can be drawn: * model is trained, typically on big servers on the cloud, and exported to ONNX. * model is cooked, simplified, using tract
command line or python bindings. * model is shipped to devices or servers in charge of running it.
"},{"location":"#testing-and-benching-models-early","title":"Testing and benching models early","text":"As soon as the model is in ONNX form, tract
can load and run it. It gives opportunities to validate and test on the training system, asserting early on that tract
will compute at runtime the same result than what the training model predicts, limiting the risk of late-minute surprise.
But tract command line can also be used to bench and profile an ONNX model on the target system answering very early the \"will the device be fast enough\" question. The nature of neural network is such that in many cases an untrained model, or a poorly trained one will perform the same computations than the final model, so it may be possible to bench the model for on-device efficiency before going through a costly and long model training.
"},{"location":"#tract-opl","title":"tract-opl","text":"NNEF is a pretty little standard. But we needed to go beyond it and we extended it in several ways. For instance, NNEF does not provide syntax for recurring neural network (LSTM and friends), which are an absolute must in signal and voice processing. tract
also supports symbolic dimensions, which are useful to represent a late bound batch dimension (if you don't know in advance how many inputs will have to be computed concurrently).
"},{"location":"#pulsing","title":"Pulsing","text":"For interactive applications where time plays a role (voice, signal, ...), tract
can automatically transform batch models, to equivalent streaming models suitable for runtime. While batch models are presented at training time the whole signal in one go, a streaming model received the signal by \"pulse\" and produces step by step the same output that the batching model.
It does not work for every model, tract
can obviously not generate a model where the output at a time depends on input not received yet. Of course, models have to be causal to be pulsable. For instance, a bi-directional LSTM is not pulsable. Most convolution nets can be made causal at designe time by padding, or at cooking time by adding fixed delays.
This cooking step is a recurring annoyance in the real-time voice and signal field : it can be done manually, but is very easy to get wrong. tract
makes it automactic.
"},{"location":"fact/","title":"Facts","text":""},{"location":"fact/#tract.fact.Fact","title":"Fact
","text":"Tract-core fact, to be used with Model.
It always contains the full shape (sometimes using symbolic dimensions) and item type. In some situation it can also contain the constant value of the associated tensor.
Source code in tract/fact.py
class Fact:\n\"\"\"\n Tract-core fact, to be used with Model.\n\n It always contains the full shape (sometimes using symbolic dimensions) and item type.\n In some situation it can also contain the constant value of the associated tensor.\n \"\"\"\n def __init__(self, ptr):\n self.ptr = ptr\n\n def __del__(self):\n if self.ptr:\n check(lib.tract_fact_destroy(byref(self.ptr)))\n\n def __str__(self):\n return self.dump()\n\n def _valid(self):\n if self.ptr == None:\n raise TractError(\"invalid fact (maybe already consumed ?)\")\n\n def dump(self):\n self._valid()\n cstring = c_char_p();\n check(lib.tract_fact_dump(self.ptr, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n
"},{"location":"fact/#tract.fact.InferenceFact","title":"InferenceFact
","text":"Tract inference fact, to be used with InferenceModel.
It can represent partial type and shape information of a Tensor during model analysis.
Source code in tract/fact.py
class InferenceFact:\n\"\"\"\n Tract inference fact, to be used with InferenceModel.\n\n It can represent partial type and shape information of a Tensor during model analysis.\n \"\"\"\n def __init__(self, ptr):\n self.ptr = ptr\n\n def __del__(self):\n if self.ptr:\n check(lib.tract_inference_fact_destroy(byref(self.ptr)))\n\n def __str__(self):\n return self.dump()\n\n def _valid(self):\n if self.ptr == None:\n raise TractError(\"invalid inference fact (maybe already consumed ?)\")\n\n def dump(self):\n self._valid()\n cstring = c_char_p();\n check(lib.tract_inference_fact_dump(self.ptr, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n
"},{"location":"inference_model/","title":"Inference model","text":""},{"location":"inference_model/#tract.inference_model.InferenceModel","title":"InferenceModel
","text":"ONNX model are loaded as an InferenceModel
s instead of Model
s: many ONNX models come with partial shape and element type information, while tract's Model
assume full shape and element type knownledge. In this case, it is generally sufficient to inform tract about the input shape and type, then let tract infer the rest of the missing shape information before converting the InferenceModel
to a regular Model
.
# load the model as an InferenceModel\nmodel = tract.onnx().model_for_path(\"./mobilenetv2-7.onnx\")\n\n# set the shape and type of its first and only input\nmodel.set_input_fact(0, \"1,3,224,224,f32\")\n\n# get ready to run the model\nmodel = model.into_optimized().into_runnable()\n
Source code in tract/inference_model.py
class InferenceModel:\n\"\"\"\n ONNX model are loaded as an\n `InferenceModel`s instead of `Model`s: many ONNX models come with partial shape and\n element type information, while tract's `Model` assume full shape and element type\n knownledge. In this case, it is generally sufficient to inform tract about the input\n shape and type, then let tract *infer* the rest of the missing shape information\n before converting the `InferenceModel` to a regular `Model`.\n\n ```python\n # load the model as an InferenceModel\n model = tract.onnx().model_for_path(\"./mobilenetv2-7.onnx\")\n\n # set the shape and type of its first and only input\n model.set_input_fact(0, \"1,3,224,224,f32\")\n\n # get ready to run the model\n model = model.into_optimized().into_runnable()\n ```\n \"\"\"\n def __init__(self, ptr):\n self.ptr = ptr\n\n def __del__(self):\n if self.ptr:\n check(lib.tract_inference_model_destroy(byref(self.ptr)))\n\n def _valid(self):\n if self.ptr == None:\n raise TractError(\"invalid inference model (maybe already consumed ?)\")\n\n def into_optimized(self) -> Model:\n\"\"\"\n Run the InferenceModel through the full tract optimisation pipeline to get an\n optimised Model.\n \"\"\"\n self._valid()\n model = c_void_p()\n check(lib.tract_inference_model_into_optimized(byref(self.ptr), byref(model)))\n return Model(model)\n\n def into_typed(self) -> Model:\n\"\"\"\n Convert an InferenceModel to a regular typed `Model`.\n\n This will leave the opportunity to run more transformation on the intermediary form of the\n model, before optimisint it all the way.\n \"\"\"\n self._valid()\n model = c_void_p()\n check(lib.tract_inference_model_into_typed(byref(self.ptr), byref(model)))\n return Model(model)\n\n def input_count(self) -> int:\n\"\"\"Return the number of inputs of the model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_inference_model_input_count(self.ptr, byref(i)))\n return i.value\n\n def output_count(self) -> int:\n\"\"\"Return the number of outputs of the model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_inference_model_output_count(self.ptr, byref(i)))\n return i.value\n\n def input_name(self, input_id: int) -> str:\n\"\"\"Return the name of the `input_id`th input.\"\"\"\n self._valid()\n cstring = c_char_p()\n check(lib.tract_inference_model_input_name(self.ptr, input_id, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n\n def input_fact(self, input_id: int) -> InferenceFact:\n\"\"\"Extract the InferenceFact of the `input_id`th input.\"\"\"\n self._valid()\n fact = c_void_p()\n check(lib.tract_inference_model_input_fact(self.ptr, input_id, byref(fact)))\n return InferenceFact(fact)\n\n def set_input_fact(self, input_id: int, fact: Union[InferenceFact, str, None]) -> None:\n\"\"\"Change the InferenceFact of the `input_id`th input.\"\"\"\n self._valid()\n if isinstance(fact, str):\n fact = self.fact(fact)\n if fact == None:\n check(lib.tract_inference_model_set_input_fact(self.ptr, input_id, None))\n else:\n check(lib.tract_inference_model_set_input_fact(self.ptr, input_id, fact.ptr))\n\n def set_output_names(self, names: List[str]):\n\"\"\"Change the output nodes of the model\"\"\"\n self._valid()\n nb = len(names)\n names_str = []\n names_ptr = (c_char_p * nb)()\n for ix, n in enumerate(names):\n names_str.append(str(n).encode(\"utf-8\"))\n names_ptr[ix] = names_str[ix]\n check(lib.tract_inference_model_set_output_names(self.ptr, nb, names_ptr))\n\n def output_name(self, output_id: int) -> str:\n\"\"\"Return the name of the `output_id`th output.\"\"\"\n self._valid()\n cstring = c_char_p()\n check(lib.tract_inference_model_output_name(self.ptr, output_id, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n\n def output_fact(self, output_id: int) -> InferenceFact:\n\"\"\"Extract the InferenceFact of the `output_id`th output.\"\"\"\n self._valid()\n fact = c_void_p()\n check(lib.tract_inference_model_output_fact(self.ptr, output_id, byref(fact)))\n return InferenceFact(fact)\n\n def set_output_fact(self, output_id: int, fact: Union[InferenceFact, str, None]) -> None:\n\"\"\"Change the InferenceFact of the `output_id`th output.\"\"\"\n self._valid()\n if isinstance(fact, str):\n fact = self.fact(fact)\n if fact == None:\n check(lib.tract_inference_model_set_output_fact(self.ptr, output_id, None))\n else:\n check(lib.tract_inference_model_set_output_fact(self.ptr, output_id, fact.ptr))\n\n def fact(self, spec:str) -> InferenceFact:\n\"\"\"\n Parse an fact specification as an `InferenceFact`\n\n Typical `InferenceFact` specification is in the form \"1,224,224,3,f32\". Comma-separated\n list of dimension, one for each axis, plus an mnemonic for the element type. f32 is \n single precision \"float\", i16 is a 16-bit signed integer, and u8 a 8-bit unsigned integer.\n \"\"\"\n self._valid()\n spec = str(spec).encode(\"utf-8\")\n fact = c_void_p();\n check(lib.tract_inference_fact_parse(self.ptr, spec, byref(fact)))\n return InferenceFact(fact)\n\n def analyse(self) -> None:\n\"\"\"\n Perform shape and element type inference on the model.\n \"\"\"\n self._valid()\n check(lib.tract_inference_model_analyse(self.ptr, False))\n\n def into_analysed(self) -> \"InferenceModel\":\n\"\"\"\n Perform shape and element type inference on the model.\n \"\"\"\n self.analyse()\n return self\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.analyse","title":"analyse()
","text":"Perform shape and element type inference on the model.
Source code in tract/inference_model.py
def analyse(self) -> None:\n\"\"\"\n Perform shape and element type inference on the model.\n \"\"\"\n self._valid()\n check(lib.tract_inference_model_analyse(self.ptr, False))\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.fact","title":"fact(spec)
","text":"Parse an fact specification as an InferenceFact
Typical InferenceFact
specification is in the form \"1,224,224,3,f32\". Comma-separated list of dimension, one for each axis, plus an mnemonic for the element type. f32 is single precision \"float\", i16 is a 16-bit signed integer, and u8 a 8-bit unsigned integer.
Source code in tract/inference_model.py
def fact(self, spec:str) -> InferenceFact:\n\"\"\"\n Parse an fact specification as an `InferenceFact`\n\n Typical `InferenceFact` specification is in the form \"1,224,224,3,f32\". Comma-separated\n list of dimension, one for each axis, plus an mnemonic for the element type. f32 is \n single precision \"float\", i16 is a 16-bit signed integer, and u8 a 8-bit unsigned integer.\n \"\"\"\n self._valid()\n spec = str(spec).encode(\"utf-8\")\n fact = c_void_p();\n check(lib.tract_inference_fact_parse(self.ptr, spec, byref(fact)))\n return InferenceFact(fact)\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.input_count","title":"input_count()
","text":"Return the number of inputs of the model
Source code in tract/inference_model.py
def input_count(self) -> int:\n\"\"\"Return the number of inputs of the model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_inference_model_input_count(self.ptr, byref(i)))\n return i.value\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.input_fact","title":"input_fact(input_id)
","text":"Extract the InferenceFact of the input_id
th input.
Source code in tract/inference_model.py
def input_fact(self, input_id: int) -> InferenceFact:\n\"\"\"Extract the InferenceFact of the `input_id`th input.\"\"\"\n self._valid()\n fact = c_void_p()\n check(lib.tract_inference_model_input_fact(self.ptr, input_id, byref(fact)))\n return InferenceFact(fact)\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.input_name","title":"input_name(input_id)
","text":"Return the name of the input_id
th input.
Source code in tract/inference_model.py
def input_name(self, input_id: int) -> str:\n\"\"\"Return the name of the `input_id`th input.\"\"\"\n self._valid()\n cstring = c_char_p()\n check(lib.tract_inference_model_input_name(self.ptr, input_id, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.into_analysed","title":"into_analysed()
","text":"Perform shape and element type inference on the model.
Source code in tract/inference_model.py
def into_analysed(self) -> \"InferenceModel\":\n\"\"\"\n Perform shape and element type inference on the model.\n \"\"\"\n self.analyse()\n return self\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.into_optimized","title":"into_optimized()
","text":"Run the InferenceModel through the full tract optimisation pipeline to get an optimised Model.
Source code in tract/inference_model.py
def into_optimized(self) -> Model:\n\"\"\"\n Run the InferenceModel through the full tract optimisation pipeline to get an\n optimised Model.\n \"\"\"\n self._valid()\n model = c_void_p()\n check(lib.tract_inference_model_into_optimized(byref(self.ptr), byref(model)))\n return Model(model)\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.into_typed","title":"into_typed()
","text":"Convert an InferenceModel to a regular typed Model
.
This will leave the opportunity to run more transformation on the intermediary form of the model, before optimisint it all the way.
Source code in tract/inference_model.py
def into_typed(self) -> Model:\n\"\"\"\n Convert an InferenceModel to a regular typed `Model`.\n\n This will leave the opportunity to run more transformation on the intermediary form of the\n model, before optimisint it all the way.\n \"\"\"\n self._valid()\n model = c_void_p()\n check(lib.tract_inference_model_into_typed(byref(self.ptr), byref(model)))\n return Model(model)\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.output_count","title":"output_count()
","text":"Return the number of outputs of the model
Source code in tract/inference_model.py
def output_count(self) -> int:\n\"\"\"Return the number of outputs of the model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_inference_model_output_count(self.ptr, byref(i)))\n return i.value\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.output_fact","title":"output_fact(output_id)
","text":"Extract the InferenceFact of the output_id
th output.
Source code in tract/inference_model.py
def output_fact(self, output_id: int) -> InferenceFact:\n\"\"\"Extract the InferenceFact of the `output_id`th output.\"\"\"\n self._valid()\n fact = c_void_p()\n check(lib.tract_inference_model_output_fact(self.ptr, output_id, byref(fact)))\n return InferenceFact(fact)\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.output_name","title":"output_name(output_id)
","text":"Return the name of the output_id
th output.
Source code in tract/inference_model.py
def output_name(self, output_id: int) -> str:\n\"\"\"Return the name of the `output_id`th output.\"\"\"\n self._valid()\n cstring = c_char_p()\n check(lib.tract_inference_model_output_name(self.ptr, output_id, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.set_input_fact","title":"set_input_fact(input_id, fact)
","text":"Change the InferenceFact of the input_id
th input.
Source code in tract/inference_model.py
def set_input_fact(self, input_id: int, fact: Union[InferenceFact, str, None]) -> None:\n\"\"\"Change the InferenceFact of the `input_id`th input.\"\"\"\n self._valid()\n if isinstance(fact, str):\n fact = self.fact(fact)\n if fact == None:\n check(lib.tract_inference_model_set_input_fact(self.ptr, input_id, None))\n else:\n check(lib.tract_inference_model_set_input_fact(self.ptr, input_id, fact.ptr))\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.set_output_fact","title":"set_output_fact(output_id, fact)
","text":"Change the InferenceFact of the output_id
th output.
Source code in tract/inference_model.py
def set_output_fact(self, output_id: int, fact: Union[InferenceFact, str, None]) -> None:\n\"\"\"Change the InferenceFact of the `output_id`th output.\"\"\"\n self._valid()\n if isinstance(fact, str):\n fact = self.fact(fact)\n if fact == None:\n check(lib.tract_inference_model_set_output_fact(self.ptr, output_id, None))\n else:\n check(lib.tract_inference_model_set_output_fact(self.ptr, output_id, fact.ptr))\n
"},{"location":"inference_model/#tract.inference_model.InferenceModel.set_output_names","title":"set_output_names(names)
","text":"Change the output nodes of the model
Source code in tract/inference_model.py
def set_output_names(self, names: List[str]):\n\"\"\"Change the output nodes of the model\"\"\"\n self._valid()\n nb = len(names)\n names_str = []\n names_ptr = (c_char_p * nb)()\n for ix, n in enumerate(names):\n names_str.append(str(n).encode(\"utf-8\"))\n names_ptr[ix] = names_str[ix]\n check(lib.tract_inference_model_set_output_names(self.ptr, nb, names_ptr))\n
"},{"location":"model/","title":"Model (aka Typed Model)","text":""},{"location":"model/#tract.model.Model","title":"Model
","text":""},{"location":"model/#tract.model.Model--main-model-object","title":"Main model object","text":""},{"location":"model/#tract.model.Model--central-focus-point-of-the-model-transformation-pipeline","title":"Central focus point of the model transformation pipeline","text":"The Model is the central point of tract model loading and \"model cooking\". ONNX and NNEF serialized models are converted to Model (more or less directly) before we can do anything of value with them. Model can be dumped to NNEF (or tract-opl which is NNEF plus tract proprietary extensions).
A Model can be optimize()
, substituing the \"high level\" operators in tract-core operator set by the best implementation available for the current system. From there it can be transformed into a Runnable object that we will use to run.
"},{"location":"model/#tract.model.Model--model-cooking","title":"Model cooking","text":"But some model transformations can be performed on the Model class:
- declutter (getting rid of training artefacts)
- \"pulsification\" (transforming a batch-oriented model into a streaming model)
- symbol substitution (make N or Batch a fixed number, unlocking potential optimisation later on)
- static cost evalation and dynamic profiling
- ...
In some situation, these operation are done \"on-the-fly\" when a ONNX or NNEF model is loaded, at start-up time. In other situation, when start-up time becomes an issue, it may be beneficial to \"pre-cook\" the model: apply the transformations one time, serialize the model as NNEF (with tract-opl extension if needed). At start-up this model can be significantly less expensive to \"cook\" for inference.
"},{"location":"model/#tract.model.Model--model-and-typedmodel","title":"Model and TypedModel","text":"This class is actually a wrapper around the \"TypedModel\" in Rust codebase. The \"Typed\" bit means than all shapes and element types in all input, output and temporary values must known. There is support in tract for symbols in dimensions, with some limited computation capabilities on symbolic expression. For instance, it is relatively frequent to work with a Model where all tensors shapes start with the N
or Batch
.
Source code in tract/model.py
class Model:\n\"\"\"\n # Main model object\n\n ## Central focus point of the model transformation pipeline\n\n The Model is the central point of tract model loading and \"model cooking\". ONNX and NNEF \n serialized models are converted to Model (more or less directly) before we can do anything\n of value with them. Model can be dumped to NNEF (or tract-opl which is NNEF plus tract\n proprietary extensions).\n\n A Model can be `optimize()`, substituing the \"high level\" operators in tract-core operator set by\n the best implementation available for the current system. From there it can be transformed into a \n Runnable object that we will use to run.\n\n ## Model cooking\n\n But some model transformations can be performed on the Model class:\n\n - declutter (getting rid of training artefacts)\n - \"pulsification\" (transforming a batch-oriented model into a streaming model)\n - symbol substitution (make N or Batch a fixed number, unlocking potential optimisation later on)\n - static cost evalation and dynamic profiling\n - ...\n\n In some situation, these operation are done \"on-the-fly\" when a ONNX or NNEF model is loaded,\n at start-up time. In other situation, when start-up time becomes an issue, it may be beneficial\n to \"pre-cook\" the model: apply the transformations one time, serialize the model as NNEF (with\n tract-opl extension if needed). At start-up this model can be significantly less expensive to\n \"cook\" for inference.\n\n ## Model and TypedModel\n\n This class is actually a wrapper around the \"TypedModel\" in Rust codebase. The \"Typed\"\n bit means than all shapes and element types in all input, output and temporary values must\n known. There is support in tract for symbols in dimensions, with some limited computation\n capabilities on symbolic expression. For instance, it is relatively frequent to work with\n a Model where all tensors shapes start with the `N` or `Batch`.\n \"\"\"\n\n def __init__(self, ptr):\n self.ptr = ptr\n\n def __del__(self):\n if self.ptr:\n check(lib.tract_model_destroy(byref(self.ptr)))\n\n def _valid(self):\n if self.ptr == None:\n raise TractError(\"invalid model (maybe already consumed ?)\")\n\n def input_count(self) -> int:\n\"\"\"Return the number of inputs of the model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_model_input_count(self.ptr, byref(i)))\n return i.value\n\n def output_count(self) -> int:\n\"\"\"Return the number of outputs of the model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_model_output_count(self.ptr, byref(i)))\n return i.value\n\n def input_name(self, input_id: int) -> str:\n\"\"\"Return the name of the input_id-th input\"\"\"\n self._valid()\n cstring = c_char_p()\n check(lib.tract_model_input_name(self.ptr, input_id, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n\n def input_fact(self, input_id: int) -> Fact:\n\"\"\"Return the fact of the input_id-th input\"\"\"\n self._valid()\n fact = c_void_p()\n check(lib.tract_model_input_fact(self.ptr, input_id, byref(fact)))\n return Fact(fact)\n\n def set_output_names(self, names: List[str]):\n\"\"\"Change the output nodes of the model\"\"\"\n self._valid()\n nb = len(names)\n names_str = []\n names_ptr = (c_char_p * nb)()\n for ix, n in enumerate(names):\n names_str.append(str(n).encode(\"utf-8\"))\n names_ptr[ix] = names_str[ix]\n check(lib.tract_model_set_output_names(self.ptr, nb, names_ptr))\n\n def output_name(self, output_id: int) -> str:\n\"\"\"Return the name of the output_id-th output\"\"\"\n self._valid()\n cstring = c_char_p()\n check(lib.tract_model_output_name(self.ptr, output_id, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n\n def output_fact(self, input_id: int) -> Fact:\n\"\"\"Return the fact of the output_id-th output\"\"\"\n self._valid()\n fact = c_void_p()\n check(lib.tract_model_output_fact(self.ptr, input_id, byref(fact)))\n return Fact(fact)\n\n def concretize_symbols(self, values: Dict[str, int]) -> None:\n\"\"\"Substitute symbols by a value\n\n Replace all occurencies of the symbols in the dictionary, in all the Model facts shapes.\n\n While this is not strictly necesary, the optimizing steps may make better choices if the model\n is informed of some specific symbol values.\n \"\"\"\n self._valid()\n nb = len(values)\n names_str = []\n names = (c_char_p * nb)()\n values_list = (c_int64 * nb)()\n for ix, (k, v) in enumerate(values.items()):\n names_str.append(str(k).encode(\"utf-8\"))\n names[ix] = names_str[ix]\n values_list[ix] = v\n check(lib.tract_model_concretize_symbols(self.ptr, c_size_t(nb), names, values_list))\n\n def pulse(self, symbol: str, pulse: Union[str, int]) -> None:\n\"\"\"Pulsify a model.\n\n `pulse` is typically a one-length dictionary mapping the time dimension symbol to a pulse len.\n \"\"\"\n self._valid()\n check(lib.tract_model_pulse_simple(byref(self.ptr), symbol.encode(\"utf-8\"), str(pulse).encode(\"utf-8\")))\n\n def half(self) -> None:\n\"\"\"Convert the model to hald precision\n \"\"\"\n self._valid()\n check(lib.tract_model_half(self.ptr))\n\n def declutter(self) -> None:\n\"\"\"Declutter a model.\n\n Perform the first \"half\" of optimisation phases, consisting or removing training artefacts and converge\n on tract-core canonical form.\n \"\"\"\n self._valid()\n check(lib.tract_model_declutter(self.ptr))\n\n def optimize(self) -> None:\n\"\"\"Optimize a model.\n\n Perform the second \"half\" of optimisation phases, consisting of translating the tract-core canonical\n form to the best runtime for the current architecture.\n \"\"\"\n self._valid()\n check(lib.tract_model_optimize(self.ptr))\n\n def into_decluttered(self) -> \"Model\":\n\"\"\"Convenience method performing `declutter()` and returning the model\"\"\"\n self.declutter();\n return self\n\n def into_optimized(self) -> \"Model\":\n\"\"\"Convenience method performing `optimize()` and returning the model\"\"\"\n self.optimize()\n return self\n\n def into_runnable(self) -> Runnable:\n\"\"\"Transform the model into a ready to be used Runnable model\"\"\"\n self._valid()\n runnable = c_void_p()\n check(lib.tract_model_into_runnable(byref(self.ptr), byref(runnable)))\n return Runnable(runnable)\n\n def property_keys(self) -> List[str]:\n\"\"\"Query the list of properties names of the model.\"\"\"\n self._valid()\n count = c_size_t()\n check(lib.tract_model_property_count(self.ptr, byref(count)))\n count = count.value\n cstrings = (POINTER(c_char) * count)()\n check(lib.tract_model_property_names(self.ptr, cstrings))\n names = []\n for i in range(0, count):\n names.append(str(cast(cstrings[i], c_char_p).value, \"utf-8\"))\n lib.tract_free_cstring(cstrings[i])\n return names\n\n def property(self, name: str) -> Value:\n\"\"\"Query a property by name\"\"\"\n self._valid()\n value = c_void_p()\n check(lib.tract_model_property(self.ptr, str(name).encode(\"utf-8\"), byref(value)))\n return Value(value)\n\n def profile_json(self, inputs: Union[None, List[Union[Value, numpy.ndarray]]]) -> str:\n\"\"\"Profile the model. Also compute the static costs of operators.\n\n Returns is a json buffer.\n \"\"\"\n self._valid()\n cstring = c_char_p()\n input_values = []\n input_ptrs = None\n if inputs != None:\n for v in inputs:\n if isinstance(v, Value):\n input_values.append(v)\n elif isinstance(v, numpy.ndarray):\n input_values.append(Value.from_numpy(v))\n else:\n raise TractError(f\"Inputs must be of type tract.Value or numpy.Array, got {v}\")\n input_ptrs = (c_void_p * len(inputs))()\n for ix, v in enumerate(input_values):\n input_ptrs[ix] = v.ptr\n check(lib.tract_model_profile_json(self.ptr, input_ptrs, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n
"},{"location":"model/#tract.model.Model.concretize_symbols","title":"concretize_symbols(values)
","text":"Substitute symbols by a value
Replace all occurencies of the symbols in the dictionary, in all the Model facts shapes.
While this is not strictly necesary, the optimizing steps may make better choices if the model is informed of some specific symbol values.
Source code in tract/model.py
def concretize_symbols(self, values: Dict[str, int]) -> None:\n\"\"\"Substitute symbols by a value\n\n Replace all occurencies of the symbols in the dictionary, in all the Model facts shapes.\n\n While this is not strictly necesary, the optimizing steps may make better choices if the model\n is informed of some specific symbol values.\n \"\"\"\n self._valid()\n nb = len(values)\n names_str = []\n names = (c_char_p * nb)()\n values_list = (c_int64 * nb)()\n for ix, (k, v) in enumerate(values.items()):\n names_str.append(str(k).encode(\"utf-8\"))\n names[ix] = names_str[ix]\n values_list[ix] = v\n check(lib.tract_model_concretize_symbols(self.ptr, c_size_t(nb), names, values_list))\n
"},{"location":"model/#tract.model.Model.declutter","title":"declutter()
","text":"Declutter a model.
Perform the first \"half\" of optimisation phases, consisting or removing training artefacts and converge on tract-core canonical form.
Source code in tract/model.py
def declutter(self) -> None:\n\"\"\"Declutter a model.\n\n Perform the first \"half\" of optimisation phases, consisting or removing training artefacts and converge\n on tract-core canonical form.\n \"\"\"\n self._valid()\n check(lib.tract_model_declutter(self.ptr))\n
"},{"location":"model/#tract.model.Model.half","title":"half()
","text":"Convert the model to hald precision
Source code in tract/model.py
def half(self) -> None:\n\"\"\"Convert the model to hald precision\n \"\"\"\n self._valid()\n check(lib.tract_model_half(self.ptr))\n
"},{"location":"model/#tract.model.Model.input_count","title":"input_count()
","text":"Return the number of inputs of the model
Source code in tract/model.py
def input_count(self) -> int:\n\"\"\"Return the number of inputs of the model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_model_input_count(self.ptr, byref(i)))\n return i.value\n
"},{"location":"model/#tract.model.Model.input_fact","title":"input_fact(input_id)
","text":"Return the fact of the input_id-th input
Source code in tract/model.py
def input_fact(self, input_id: int) -> Fact:\n\"\"\"Return the fact of the input_id-th input\"\"\"\n self._valid()\n fact = c_void_p()\n check(lib.tract_model_input_fact(self.ptr, input_id, byref(fact)))\n return Fact(fact)\n
"},{"location":"model/#tract.model.Model.input_name","title":"input_name(input_id)
","text":"Return the name of the input_id-th input
Source code in tract/model.py
def input_name(self, input_id: int) -> str:\n\"\"\"Return the name of the input_id-th input\"\"\"\n self._valid()\n cstring = c_char_p()\n check(lib.tract_model_input_name(self.ptr, input_id, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n
"},{"location":"model/#tract.model.Model.into_decluttered","title":"into_decluttered()
","text":"Convenience method performing declutter()
and returning the model
Source code in tract/model.py
def into_decluttered(self) -> \"Model\":\n\"\"\"Convenience method performing `declutter()` and returning the model\"\"\"\n self.declutter();\n return self\n
"},{"location":"model/#tract.model.Model.into_optimized","title":"into_optimized()
","text":"Convenience method performing optimize()
and returning the model
Source code in tract/model.py
def into_optimized(self) -> \"Model\":\n\"\"\"Convenience method performing `optimize()` and returning the model\"\"\"\n self.optimize()\n return self\n
"},{"location":"model/#tract.model.Model.into_runnable","title":"into_runnable()
","text":"Transform the model into a ready to be used Runnable model
Source code in tract/model.py
def into_runnable(self) -> Runnable:\n\"\"\"Transform the model into a ready to be used Runnable model\"\"\"\n self._valid()\n runnable = c_void_p()\n check(lib.tract_model_into_runnable(byref(self.ptr), byref(runnable)))\n return Runnable(runnable)\n
"},{"location":"model/#tract.model.Model.optimize","title":"optimize()
","text":"Optimize a model.
Perform the second \"half\" of optimisation phases, consisting of translating the tract-core canonical form to the best runtime for the current architecture.
Source code in tract/model.py
def optimize(self) -> None:\n\"\"\"Optimize a model.\n\n Perform the second \"half\" of optimisation phases, consisting of translating the tract-core canonical\n form to the best runtime for the current architecture.\n \"\"\"\n self._valid()\n check(lib.tract_model_optimize(self.ptr))\n
"},{"location":"model/#tract.model.Model.output_count","title":"output_count()
","text":"Return the number of outputs of the model
Source code in tract/model.py
def output_count(self) -> int:\n\"\"\"Return the number of outputs of the model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_model_output_count(self.ptr, byref(i)))\n return i.value\n
"},{"location":"model/#tract.model.Model.output_fact","title":"output_fact(input_id)
","text":"Return the fact of the output_id-th output
Source code in tract/model.py
def output_fact(self, input_id: int) -> Fact:\n\"\"\"Return the fact of the output_id-th output\"\"\"\n self._valid()\n fact = c_void_p()\n check(lib.tract_model_output_fact(self.ptr, input_id, byref(fact)))\n return Fact(fact)\n
"},{"location":"model/#tract.model.Model.output_name","title":"output_name(output_id)
","text":"Return the name of the output_id-th output
Source code in tract/model.py
def output_name(self, output_id: int) -> str:\n\"\"\"Return the name of the output_id-th output\"\"\"\n self._valid()\n cstring = c_char_p()\n check(lib.tract_model_output_name(self.ptr, output_id, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n
"},{"location":"model/#tract.model.Model.profile_json","title":"profile_json(inputs)
","text":"Profile the model. Also compute the static costs of operators.
Returns is a json buffer.
Source code in tract/model.py
def profile_json(self, inputs: Union[None, List[Union[Value, numpy.ndarray]]]) -> str:\n\"\"\"Profile the model. Also compute the static costs of operators.\n\n Returns is a json buffer.\n \"\"\"\n self._valid()\n cstring = c_char_p()\n input_values = []\n input_ptrs = None\n if inputs != None:\n for v in inputs:\n if isinstance(v, Value):\n input_values.append(v)\n elif isinstance(v, numpy.ndarray):\n input_values.append(Value.from_numpy(v))\n else:\n raise TractError(f\"Inputs must be of type tract.Value or numpy.Array, got {v}\")\n input_ptrs = (c_void_p * len(inputs))()\n for ix, v in enumerate(input_values):\n input_ptrs[ix] = v.ptr\n check(lib.tract_model_profile_json(self.ptr, input_ptrs, byref(cstring)))\n result = str(cstring.value, \"utf-8\")\n lib.tract_free_cstring(cstring)\n return result\n
"},{"location":"model/#tract.model.Model.property","title":"property(name)
","text":"Query a property by name
Source code in tract/model.py
def property(self, name: str) -> Value:\n\"\"\"Query a property by name\"\"\"\n self._valid()\n value = c_void_p()\n check(lib.tract_model_property(self.ptr, str(name).encode(\"utf-8\"), byref(value)))\n return Value(value)\n
"},{"location":"model/#tract.model.Model.property_keys","title":"property_keys()
","text":"Query the list of properties names of the model.
Source code in tract/model.py
def property_keys(self) -> List[str]:\n\"\"\"Query the list of properties names of the model.\"\"\"\n self._valid()\n count = c_size_t()\n check(lib.tract_model_property_count(self.ptr, byref(count)))\n count = count.value\n cstrings = (POINTER(c_char) * count)()\n check(lib.tract_model_property_names(self.ptr, cstrings))\n names = []\n for i in range(0, count):\n names.append(str(cast(cstrings[i], c_char_p).value, \"utf-8\"))\n lib.tract_free_cstring(cstrings[i])\n return names\n
"},{"location":"model/#tract.model.Model.pulse","title":"pulse(symbol, pulse)
","text":"Pulsify a model.
pulse
is typically a one-length dictionary mapping the time dimension symbol to a pulse len.
Source code in tract/model.py
def pulse(self, symbol: str, pulse: Union[str, int]) -> None:\n\"\"\"Pulsify a model.\n\n `pulse` is typically a one-length dictionary mapping the time dimension symbol to a pulse len.\n \"\"\"\n self._valid()\n check(lib.tract_model_pulse_simple(byref(self.ptr), symbol.encode(\"utf-8\"), str(pulse).encode(\"utf-8\")))\n
"},{"location":"model/#tract.model.Model.set_output_names","title":"set_output_names(names)
","text":"Change the output nodes of the model
Source code in tract/model.py
def set_output_names(self, names: List[str]):\n\"\"\"Change the output nodes of the model\"\"\"\n self._valid()\n nb = len(names)\n names_str = []\n names_ptr = (c_char_p * nb)()\n for ix, n in enumerate(names):\n names_str.append(str(n).encode(\"utf-8\"))\n names_ptr[ix] = names_str[ix]\n check(lib.tract_model_set_output_names(self.ptr, nb, names_ptr))\n
"},{"location":"nnef/","title":"NNEF","text":""},{"location":"nnef/#tract.nnef.Nnef","title":"Nnef
","text":"Represent a NNEF context in tract.
NNEF is a neural model interchange format, similar to ONNX but focusing on the needs of an inference engine instead of a training framework.
tract
can natively load NNEF models. It can also save models it tract internal format as tract-opl
models. tract-opl
is a set of proprierary extensions to NNEF allowing to serializeing most of the models tract can handle. These extension can be activated by the with_*() methods
.
Source code in tract/nnef.py
class Nnef:\n\"\"\"\n Represent a NNEF context in tract.\n\n NNEF is a neural model interchange format, similar to ONNX but focusing on the needs\n of an inference engine instead of a training framework.\n\n `tract` can natively load NNEF models. It can also save models it tract internal format\n as `tract-opl` models. `tract-opl` is a set of proprierary extensions to NNEF allowing to\n serializeing most of the models tract can handle. These extension can be activated by the\n `with_*() methods`.\n \"\"\"\n\n def __init__(self):\n ptr = c_void_p()\n check(lib.tract_nnef_create(byref(ptr)))\n self.ptr = ptr\n\n def __del__(self):\n check(lib.tract_nnef_destroy(byref(self.ptr)))\n\n def _valid(self):\n if self.ptr == None:\n raise TractError(\"invalid inference model (maybe already consumed ?)\")\n\n def model_for_path(self, path: Union[str, Path]) -> Model:\n\"\"\"\n Load an NNEF model from the file or folder at `path`\n\n ```python\n model = (\n tract.nnef()\n .model_for_path(\"mobilenet_v2_1.0.onnx.nnef.tgz\")\n .into_optimized()\n .into_runnable()\n )\n ```\n \"\"\"\n self._valid()\n model = c_void_p()\n path = str(path).encode(\"utf-8\")\n check(lib.tract_nnef_model_for_path(self.ptr, path, byref(model)))\n return Model(model)\n\n def with_tract_core(self) -> \"Nnef\":\n\"\"\"\n Enable tract-opl extensions to NNEF to covers tract-core operator set\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_tract_core(self.ptr))\n return self\n\n def with_tract_extra(self) -> \"Nnef\":\n\"\"\"\n Enable tract-extra extensions to NNEF.\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_tract_extra(self.ptr))\n return self\n\n def with_onnx(self) -> \"Nnef\":\n\"\"\"\n Enable tract-opl extensions to NNEF to covers (more or) ONNX operator set\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_onnx(self.ptr))\n return self\n\n def with_pulse(self) -> \"Nnef\":\n\"\"\"\n Enable tract-opl extensions to NNEF for tract pulse operators (for audio streaming)\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_pulse(self.ptr))\n return self\n\n def with_extended_identifier_syntax(self) -> \"Nnef\":\n\"\"\"\n Enable tract-opl extensions to NNEF for extended identifiers (will support PyTorch 2 path-like ids)\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_extended_identifier_syntax(self.ptr, True))\n return self\n\n def write_model_to_dir(self, model: Model, path: Union[str, Path]) -> None:\n\"\"\"\n Save `model` as a NNEF directory model in `path`.\n\n tract tries to stick to strict NNEF even if extensions has been enabled.\n \"\"\"\n self._valid()\n model._valid()\n if not isinstance(model, Model):\n raise TractError(\"Expected a Model, called with \" + model);\n path = str(path).encode(\"utf-8\")\n check(lib.tract_nnef_write_model_to_dir(self.ptr, path, model.ptr))\n\n def write_model_to_tar(self, model: Model, path: Union[str, Path]) -> None:\n\"\"\"\n Save `model` as a NNEF tar archive in `path`.\n\n tract tries to stick to strict NNEF even if extensions has been enabled.\n \"\"\"\n self._valid()\n model._valid()\n if not isinstance(model, Model):\n raise TractError(\"Expected a Model, called with \" + model);\n path = str(path).encode(\"utf-8\")\n check(lib.tract_nnef_write_model_to_tar(self.ptr, path, model.ptr))\n\n def write_model_to_tar_gz(self, model: Model, path: Union[str, Path]) -> None:\n\"\"\"\n Save `model` as a NNEF tar compressed archive in `path`.\n\n tract tries to stick to strict NNEF even if extensions has been enabled.\n \"\"\"\n self._valid()\n model._valid()\n if not isinstance(model, Model):\n raise TractError(\"Expected a Model, called with \" + model);\n path = str(path).encode(\"utf-8\")\n check(lib.tract_nnef_write_model_to_tar_gz(self.ptr, path, model.ptr))\n
"},{"location":"nnef/#tract.nnef.Nnef.model_for_path","title":"model_for_path(path)
","text":"Load an NNEF model from the file or folder at path
model = (\n tract.nnef()\n .model_for_path(\"mobilenet_v2_1.0.onnx.nnef.tgz\")\n .into_optimized()\n .into_runnable()\n)\n
Source code in tract/nnef.py
def model_for_path(self, path: Union[str, Path]) -> Model:\n\"\"\"\n Load an NNEF model from the file or folder at `path`\n\n ```python\n model = (\n tract.nnef()\n .model_for_path(\"mobilenet_v2_1.0.onnx.nnef.tgz\")\n .into_optimized()\n .into_runnable()\n )\n ```\n \"\"\"\n self._valid()\n model = c_void_p()\n path = str(path).encode(\"utf-8\")\n check(lib.tract_nnef_model_for_path(self.ptr, path, byref(model)))\n return Model(model)\n
"},{"location":"nnef/#tract.nnef.Nnef.with_extended_identifier_syntax","title":"with_extended_identifier_syntax()
","text":"Enable tract-opl extensions to NNEF for extended identifiers (will support PyTorch 2 path-like ids)
Source code in tract/nnef.py
def with_extended_identifier_syntax(self) -> \"Nnef\":\n\"\"\"\n Enable tract-opl extensions to NNEF for extended identifiers (will support PyTorch 2 path-like ids)\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_extended_identifier_syntax(self.ptr, True))\n return self\n
"},{"location":"nnef/#tract.nnef.Nnef.with_onnx","title":"with_onnx()
","text":"Enable tract-opl extensions to NNEF to covers (more or) ONNX operator set
Source code in tract/nnef.py
def with_onnx(self) -> \"Nnef\":\n\"\"\"\n Enable tract-opl extensions to NNEF to covers (more or) ONNX operator set\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_onnx(self.ptr))\n return self\n
"},{"location":"nnef/#tract.nnef.Nnef.with_pulse","title":"with_pulse()
","text":"Enable tract-opl extensions to NNEF for tract pulse operators (for audio streaming)
Source code in tract/nnef.py
def with_pulse(self) -> \"Nnef\":\n\"\"\"\n Enable tract-opl extensions to NNEF for tract pulse operators (for audio streaming)\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_pulse(self.ptr))\n return self\n
"},{"location":"nnef/#tract.nnef.Nnef.with_tract_core","title":"with_tract_core()
","text":"Enable tract-opl extensions to NNEF to covers tract-core operator set
Source code in tract/nnef.py
def with_tract_core(self) -> \"Nnef\":\n\"\"\"\n Enable tract-opl extensions to NNEF to covers tract-core operator set\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_tract_core(self.ptr))\n return self\n
"},{"location":"nnef/#tract.nnef.Nnef.with_tract_extra","title":"with_tract_extra()
","text":"Enable tract-extra extensions to NNEF.
Source code in tract/nnef.py
def with_tract_extra(self) -> \"Nnef\":\n\"\"\"\n Enable tract-extra extensions to NNEF.\n \"\"\"\n self._valid()\n check(lib.tract_nnef_enable_tract_extra(self.ptr))\n return self\n
"},{"location":"nnef/#tract.nnef.Nnef.write_model_to_dir","title":"write_model_to_dir(model, path)
","text":"Save model
as a NNEF directory model in path
.
tract tries to stick to strict NNEF even if extensions has been enabled.
Source code in tract/nnef.py
def write_model_to_dir(self, model: Model, path: Union[str, Path]) -> None:\n\"\"\"\n Save `model` as a NNEF directory model in `path`.\n\n tract tries to stick to strict NNEF even if extensions has been enabled.\n \"\"\"\n self._valid()\n model._valid()\n if not isinstance(model, Model):\n raise TractError(\"Expected a Model, called with \" + model);\n path = str(path).encode(\"utf-8\")\n check(lib.tract_nnef_write_model_to_dir(self.ptr, path, model.ptr))\n
"},{"location":"nnef/#tract.nnef.Nnef.write_model_to_tar","title":"write_model_to_tar(model, path)
","text":"Save model
as a NNEF tar archive in path
.
tract tries to stick to strict NNEF even if extensions has been enabled.
Source code in tract/nnef.py
def write_model_to_tar(self, model: Model, path: Union[str, Path]) -> None:\n\"\"\"\n Save `model` as a NNEF tar archive in `path`.\n\n tract tries to stick to strict NNEF even if extensions has been enabled.\n \"\"\"\n self._valid()\n model._valid()\n if not isinstance(model, Model):\n raise TractError(\"Expected a Model, called with \" + model);\n path = str(path).encode(\"utf-8\")\n check(lib.tract_nnef_write_model_to_tar(self.ptr, path, model.ptr))\n
"},{"location":"nnef/#tract.nnef.Nnef.write_model_to_tar_gz","title":"write_model_to_tar_gz(model, path)
","text":"Save model
as a NNEF tar compressed archive in path
.
tract tries to stick to strict NNEF even if extensions has been enabled.
Source code in tract/nnef.py
def write_model_to_tar_gz(self, model: Model, path: Union[str, Path]) -> None:\n\"\"\"\n Save `model` as a NNEF tar compressed archive in `path`.\n\n tract tries to stick to strict NNEF even if extensions has been enabled.\n \"\"\"\n self._valid()\n model._valid()\n if not isinstance(model, Model):\n raise TractError(\"Expected a Model, called with \" + model);\n path = str(path).encode(\"utf-8\")\n check(lib.tract_nnef_write_model_to_tar_gz(self.ptr, path, model.ptr))\n
"},{"location":"onnx/","title":"ONNX","text":""},{"location":"onnx/#tract.onnx.Onnx","title":"Onnx
","text":"Represent the ONNX context in tract.
It essentially allows to load ONNX models. Note that an ONNX model is loaded as an InferenceModel
and not as a Model
: many ONNX models come with partial shape and element type information, while tract's Model
assume full shape and element type knownledge. In this case, it is generally sufficient to inform tract about the input shape and type, then let tract infer the rest of the missing shape information before converting the InferenceModel
to a regular Model
.
# load the model as an InferenceModel\nmodel = tract.onnx().model_for_path(\"./mobilenetv2-7.onnx\")\n\n# set the shape and type of its first and only input\nmodel.set_input_fact(0, \"1,3,224,224,f32\")\n\n# get ready to run the model\nmodel = model.into_optimized().into_runnable()\n
Source code in tract/onnx.py
class Onnx:\n\"\"\"\n Represent the ONNX context in tract.\n\n It essentially allows to load ONNX models. Note that an ONNX model is loaded as an\n `InferenceModel` and not as a `Model`: many ONNX models come with partial shape and\n element type information, while tract's `Model` assume full shape and element type\n knownledge. In this case, it is generally sufficient to inform tract about the input\n shape and type, then let tract *infer* the rest of the missing shape information\n before converting the `InferenceModel` to a regular `Model`.\n\n ```python\n # load the model as an InferenceModel\n model = tract.onnx().model_for_path(\"./mobilenetv2-7.onnx\")\n\n # set the shape and type of its first and only input\n model.set_input_fact(0, \"1,3,224,224,f32\")\n\n # get ready to run the model\n model = model.into_optimized().into_runnable()\n ```\n \"\"\"\n\n def __init__(self):\n ptr = c_void_p()\n check(lib.tract_onnx_create(byref(ptr)))\n self.ptr = ptr\n\n def __del__(self):\n check(lib.tract_onnx_destroy(byref(self.ptr)))\n\n def model_for_path(self, path: Union[str, Path]) -> InferenceModel:\n\"\"\"\n Load an ONNX file as an InferenceModel\n \"\"\"\n model = c_void_p()\n path = str(path).encode(\"utf-8\")\n check(lib.tract_onnx_model_for_path(self.ptr, path, byref(model)))\n return InferenceModel(model)\n
"},{"location":"onnx/#tract.onnx.Onnx.model_for_path","title":"model_for_path(path)
","text":"Load an ONNX file as an InferenceModel
Source code in tract/onnx.py
def model_for_path(self, path: Union[str, Path]) -> InferenceModel:\n\"\"\"\n Load an ONNX file as an InferenceModel\n \"\"\"\n model = c_void_p()\n path = str(path).encode(\"utf-8\")\n check(lib.tract_onnx_model_for_path(self.ptr, path, byref(model)))\n return InferenceModel(model)\n
"},{"location":"runnable/","title":"Runnable model","text":""},{"location":"runnable/#tract.runnable.Runnable","title":"Runnable
","text":"A model in the Runnable state is ready to perform computation.
Source code in tract/runnable.py
class Runnable:\n\"\"\"\n A model in the Runnable state is ready to perform computation.\n \"\"\"\n def __init__(self, ptr):\n self.ptr = ptr\n\n def __del__(self):\n check(lib.tract_runnable_release(byref(self.ptr)))\n\n def _valid(self):\n if self.ptr == None:\n raise TractError(\"invalid runnable (maybe already consumed ?)\")\n\n def input_count(self) -> int:\n\"\"\"Return the number of inputs of the underlying model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_runnable_input_count(self.ptr, byref(i)))\n return i.value\n\n def output_count(self) -> int:\n\"\"\"Return the number of outputs of the underlying model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_runnable_output_count(self.ptr, byref(i)))\n return i.value\n\n def run(self, inputs: List[Union[Value, numpy.ndarray]]) -> List[Value]:\n\"\"\"\n Runs the model over the provided input list, and returns the model outputs.\n \"\"\"\n return self.spawn_state().run(inputs)\n\n def spawn_state(self):\n self._valid()\n state = c_void_p()\n check(lib.tract_runnable_spawn_state(self.ptr, byref(state)))\n return State(state)\n
"},{"location":"runnable/#tract.runnable.Runnable.input_count","title":"input_count()
","text":"Return the number of inputs of the underlying model
Source code in tract/runnable.py
def input_count(self) -> int:\n\"\"\"Return the number of inputs of the underlying model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_runnable_input_count(self.ptr, byref(i)))\n return i.value\n
"},{"location":"runnable/#tract.runnable.Runnable.output_count","title":"output_count()
","text":"Return the number of outputs of the underlying model
Source code in tract/runnable.py
def output_count(self) -> int:\n\"\"\"Return the number of outputs of the underlying model\"\"\"\n self._valid()\n i = c_size_t()\n check(lib.tract_runnable_output_count(self.ptr, byref(i)))\n return i.value\n
"},{"location":"runnable/#tract.runnable.Runnable.run","title":"run(inputs)
","text":"Runs the model over the provided input list, and returns the model outputs.
Source code in tract/runnable.py
def run(self, inputs: List[Union[Value, numpy.ndarray]]) -> List[Value]:\n\"\"\"\n Runs the model over the provided input list, and returns the model outputs.\n \"\"\"\n return self.spawn_state().run(inputs)\n
"},{"location":"value/","title":"Value","text":""},{"location":"value/#tract.value.Value","title":"Value
","text":"Represents a tensor suitable for manipulation by tract.
On the Python side, the main way to access tensor data is to convert the value to a numpy array.
Source code in tract/value.py
class Value:\n\"\"\"\n Represents a tensor suitable for manipulation by tract.\n\n On the Python side, the main way to access tensor data is to\n convert the value to a numpy array.\n \"\"\"\n def __init__(self, ptr):\n self.ptr = ptr\n\n def __del__(self):\n if self.ptr:\n check(lib.tract_value_destroy(byref(self.ptr)))\n\n def _valid(self):\n if self.ptr == None:\n raise TractError(\"invalid value (maybe already consumed ?)\")\n\n def from_numpy(array: numpy.ndarray) -> \"Value\":\n array = numpy.ascontiguousarray(array)\n\n data = array.__array_interface__['data'][0]\n data = c_void_p(data)\n ptr = c_void_p()\n shape_t = c_size_t * array.ndim\n shape = shape_t()\n for ix in range(0, array.ndim):\n shape[ix] = array.shape[ix]\n dt = dt_numpy_to_tract(array.dtype)\n check(lib.tract_value_from_bytes(dt, c_size_t(array.ndim), shape, data, byref(ptr)))\n return Value(ptr)\n\n def to_numpy(self) -> numpy.array:\n\"\"\"Builds a numpy array equivalent to the data in this value.\"\"\"\n self._valid()\n rank = c_size_t();\n shape = POINTER(c_size_t)()\n dt = c_float\n data = POINTER(dt)()\n check(lib.tract_value_as_bytes(self.ptr, None, byref(rank), byref(shape), byref(data)))\n rank = rank.value\n shape = [ int(shape[ix]) for ix in range(0, rank) ]\n array = numpy.ctypeslib.as_array(data, shape).copy()\n return array\n\n def into_numpy(self) -> numpy.array:\n\"\"\"Same as to_numpy(), but drop the value content once the numpy array is built.\"\"\"\n result = self.to_numpy()\n check(lib.tract_value_destroy(byref(self.ptr)))\n return result\n
"},{"location":"value/#tract.value.Value.into_numpy","title":"into_numpy()
","text":"Same as to_numpy(), but drop the value content once the numpy array is built.
Source code in tract/value.py
def into_numpy(self) -> numpy.array:\n\"\"\"Same as to_numpy(), but drop the value content once the numpy array is built.\"\"\"\n result = self.to_numpy()\n check(lib.tract_value_destroy(byref(self.ptr)))\n return result\n
"},{"location":"value/#tract.value.Value.to_numpy","title":"to_numpy()
","text":"Builds a numpy array equivalent to the data in this value.
Source code in tract/value.py
def to_numpy(self) -> numpy.array:\n\"\"\"Builds a numpy array equivalent to the data in this value.\"\"\"\n self._valid()\n rank = c_size_t();\n shape = POINTER(c_size_t)()\n dt = c_float\n data = POINTER(dt)()\n check(lib.tract_value_as_bytes(self.ptr, None, byref(rank), byref(shape), byref(data)))\n rank = rank.value\n shape = [ int(shape[ix]) for ix in range(0, rank) ]\n array = numpy.ctypeslib.as_array(data, shape).copy()\n return array\n
"}]}
\ No newline at end of file
diff --git a/0.20.dev/sitemap.xml.gz b/0.20.dev/sitemap.xml.gz
index f733659ab7..6497395b6a 100644
Binary files a/0.20.dev/sitemap.xml.gz and b/0.20.dev/sitemap.xml.gz differ