Skip to content
Snippets Groups Projects
Commit 59b3d4e5 authored by Maxence Naud's avatar Maxence Naud
Browse files

Merge branch 'dev' into 'main'

version 0.2.2

See merge request eclipse/aidge/aidge!60
parents e3655e61 cec0f7a9
Branches main
No related tags found
No related merge requests found
Showing
with 69 additions and 51 deletions
Subproject commit a7666da2e654db9e3b9be786c61f905069c7a325 Subproject commit d111493246ae4835681c47510cb08031950b3790
Subproject commit fa57d1d18fb54fcfc5753ddf1b9bfbc862bf74f2 Subproject commit c18f99dd639635c4b2668664e087e50871bb17e3
Subproject commit fd7466dfffdb85cd8f8f3c7143059e7895c4360c Subproject commit ac47767e9016014e95387480a3ebe731b1f4b1e8
Subproject commit 928cb2389b931be9c8d76daa3f3feef0b6537eb0 Subproject commit 28fe9a4344bf556949974ea2ad84248f30b9caf7
Subproject commit 64d65c9b68f86816b13b252b0d08c537776b0cf7 Subproject commit c30ca76080b0aeb43183525b08fe585c1f38bc40
Subproject commit 7473931866b481bd95b3f6a5593ffd5f70fb89d6 Subproject commit 671b844081457d387ae17c437da76f1d54965b06
Subproject commit 2f1038678353f5e7744717374ffea0c0b642a21d Subproject commit f14ce43460b0078379519f4b1a0f3c0f8c0da23f
Subproject commit 5c93e9908b8ef9b69d88b2d65eaaba9a870b50d6 Subproject commit 3b7cc2547c0a79f3ab71f48835fe28bec058e7c9
#!/bin/bash
find . -maxdepth 1 -type f -name "clean_*" ! -name "clean_all.sh" -exec {} \;
...@@ -13,7 +13,7 @@ BUILDPATHS=() ...@@ -13,7 +13,7 @@ BUILDPATHS=()
while IFS= read -r -d '' dir; do while IFS= read -r -d '' dir; do
# Add each directory to the BUILDPATHS list # Add each directory to the BUILDPATHS list
BUILDPATHS+=("$dir") BUILDPATHS+=("$dir")
done < <(find . -type d -name "build_bundle_cpp" -print0) done < <(find . -type d -name "build*" -print0)
## Print and remove the list of build paths ## Print and remove the list of build paths
for path in "${BUILDPATHS[@]}"; do for path in "${BUILDPATHS[@]}"; do
......
#!/bin/bash
# Get the directory in which the script is located
script_directory="$(pwd)"
# Use find to locate files with the .mmd extension and print the list
files_to_remove=$(find "$script_directory" -type f -name "*.mmd")
echo "$files_to_remove"
find "$script_directory" -type f -name "*.mmd" -exec rm -f {} +
echo "Files with .mmd extension removed in $(pwd)"
#!/bin/bash
pip list | grep 'aidge' | cut -d ' ' -f 1 | xargs pip uninstall -y
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Aidge demonstration # Aidge demonstration
Aidge is a collaborative open source deep learning library optimized for export and processing on embedded devices. With Aidge, you can create or import a Computational Graph from common Frameworks, apply editing on its structure, train it and export its architecture on many embedded devices. Aidge provides optimized functions for inference as well as training and many custom functionalities for the target device. Aidge is a collaborative open source deep learning library optimized for export and processing on embedded devices. With Aidge, you can create or import a Computational Graph from common Frameworks, apply editing on its structure, train it and export its architecture on many embedded devices. Aidge provides optimized functions for inference as well as training and many custom functionalities for the target device.
This notebook put in perspective the tool chain to import a Deep Neural Network from ONNX model and support its Inference in Aidge. The tool chain demonstrated is : This notebook put in perspective the tool chain to import a Deep Neural Network from ONNX model and support its Inference in Aidge. The tool chain demonstrated is :
![pipeline(1)](./static/pipeline_1.png) ![pipeline(1)](./static/pipeline_1.png)
In order to demonstrate this toolchain, the MNIST digit recognition task is used. In order to demonstrate this toolchain, the MNIST digit recognition task is used.
![MNIST](./static/MnistExamples.png) ![MNIST](./static/MnistExamples.png)
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Setting up the notebook ## Setting up the notebook
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### (if needed) Download the model ### (if needed) Download the model
If you don't have git-lfs, you can download the model and data using this piece of code If you don't have git-lfs, you can download the model and data using this piece of code
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import os import os
import requests import requests
def download_material(path: str) -> None: def download_material(path: str) -> None:
if not os.path.isfile(path): if not os.path.isfile(path):
response = requests.get("https://gitlab.eclipse.org/eclipse/aidge/aidge/-/raw/dev/examples/tutorials/101_first_step/"+path+"?ref_type=heads") response = requests.get("https://gitlab.eclipse.org/eclipse/aidge/aidge/-/raw/dev/examples/tutorials/101_first_step/"+path+"?ref_type=heads")
if response.status_code == 200: if response.status_code == 200:
with open(path, 'wb') as f: with open(path, 'wb') as f:
f.write(response.content) f.write(response.content)
print("File downloaded successfully.") print("File downloaded successfully.")
else: else:
print("Failed to download file. Status code:", response.status_code) print("Failed to download file. Status code:", response.status_code)
# Download onnx model file # Download onnx model file
download_material("MLP_MNIST.onnx") download_material("MLP_MNIST.onnx")
# Download input data # Download input data
download_material("input_digit.npy") download_material("input_digit.npy")
# Download output data for later comparison # Download output data for later comparison
download_material("output_digit.npy") download_material("output_digit.npy")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Define mermaid visualizer function ### Define mermaid visualizer function
Aidge save graph using the mermaid format, in order to visualize the graph live in the notebook, we will setup the following function: Aidge save graph using the mermaid format, in order to visualize the graph live in the notebook, we will setup the following function:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import base64 import base64
from IPython.display import Image, display from IPython.display import Image, display
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
def visualize_mmd(path_to_mmd): def visualize_mmd(path_to_mmd):
with open(path_to_mmd, "r") as file_mmd: with open(path_to_mmd, "r") as file_mmd:
graph_mmd = file_mmd.read() graph_mmd = file_mmd.read()
graphbytes = graph_mmd.encode("utf-8") graphbytes = graph_mmd.encode("utf-8")
base64_bytes = base64.b64encode(graphbytes) base64_bytes = base64.b64encode(graphbytes)
base64_string = base64_bytes.decode("utf-8") base64_string = base64_bytes.decode("utf-8")
display(Image(url=f"https://mermaid.ink/img/{base64_string}")) display(Image(url=f"https://mermaid.ink/img/{base64_string}"))
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Import Aidge ## Import Aidge
In order to provide a colaborative environnement in the plateform, the structure of Aidge is built on a core library that interfaces with multiple modules binded to python libraries. In order to provide a colaborative environnement in the plateform, the structure of Aidge is built on a core library that interfaces with multiple modules binded to python libraries.
- ``aidge_core`` is the core library and offers all the basic functionnalities to create and manipulate the internal graph representation - ``aidge_core`` is the core library and offers all the basic functionnalities to create and manipulate the internal graph representation
- ``aidge_backend_cpu`` is a C++ module providing a generic C++ implementations for each component of the graph - ``aidge_backend_cpu`` is a C++ module providing a generic C++ implementations for each component of the graph
- ``aidge_onnx`` is a module allowing to import ONNX to the Aidge framework - ``aidge_onnx`` is a module allowing to import ONNX to the Aidge framework
- ``aidge_export_cpp`` is a module dedicated to the generation of optimized C++ code - ``aidge_export_cpp`` is a module dedicated to the generation of optimized C++ code
This way, ``aidge_core`` is free of any dependencies and the user can install what he wants depending on his use case. This way, ``aidge_core`` is free of any dependencies and the user can install what he wants depending on his use case.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import aidge_core import aidge_core
# Conv2D Operator is available but no implementation has been loaded # Conv2D Operator is available but no implementation has been loaded
print(f"Available backends:\n{aidge_core.get_keys_ConvOp2D()}") print(f"Available backends:\n{aidge_core.get_keys_ConvOp2D()}")
# note: Tensor is a special case as 'cpu' backend is provided in the core # note: Tensor is a special case as 'cpu' backend is provided in the core
# module to guarantee basic functionalities such as data accesss # module to guarantee basic functionalities such as data accesss
print(f"Available backends for Tensor:\n{aidge_core.Tensor.get_available_backends()}") print(f"Available backends for Tensor:\n{aidge_core.Tensor.get_available_backends()}")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
As you can see, no backends are availables for the class ``Conv2D``. As you can see, no backends are availables for the class ``Conv2D``.
We need to import the ``aidge_backend_cpu`` module which will register itself automatically to ``aidge_core``. We need to import the ``aidge_backend_cpu`` module which will register itself automatically to ``aidge_core``.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import aidge_backend_cpu import aidge_backend_cpu
print(f"Available backends:\n{aidge_core.get_keys_ConvOp2D()}") print(f"Available backends:\n{aidge_core.get_keys_ConvOp2D()}")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
For this tutorial, we will need to import ``aidge_onnx`` in order to load ONNX files, numpy in order to load data and matplotlib to display images. For this tutorial, we will need to import ``aidge_onnx`` in order to load ONNX files, numpy in order to load data and matplotlib to display images.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import aidge_onnx import aidge_onnx
import numpy as np import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## ONNX Import ## ONNX Import
Import an ONNX model into Aidge internal graph representation. Import an ONNX model into Aidge internal graph representation.
![pipeline(2)](./static/pipeline_2.png) ![pipeline(2)](./static/pipeline_2.png)
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
model = aidge_onnx.load_onnx("MLP_MNIST.onnx") model = aidge_onnx.load_onnx("MLP_MNIST.onnx")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
As you can see in the logs, aidge imported a Node as a ``GenericOperator``: As you can see in the logs, aidge imported a Node as a ``GenericOperator``:
``` ```
- /Flatten_output_0 (Flatten | GenericOperator) - /Flatten_output_0 (Flatten | GenericOperator)
``` ```
This is a fallback mechanism which allow aidge to load ONNX graph without failing even when encountering a node which is not available. This is a fallback mechanism which allow aidge to load ONNX graph without failing even when encountering a node which is not available.
The ``GenericOperator`` act as a stub retrieving node type and attributes from ONNX. This allow to provide an implementation in a user script or as we will see to remove/replace them using aidge recipes. The ``GenericOperator`` act as a stub retrieving node type and attributes from ONNX. This allow to provide an implementation in a user script or as we will see to remove/replace them using aidge recipes.
You can visualize the graph using the ``save`` method and the mermaid visualizer we have setup. You can visualize the graph using the ``save`` method and the mermaid visualizer we have setup.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
model.save("myModel") model.save("myModel")
visualize_mmd("myModel.mmd") visualize_mmd("myModel.mmd")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Graph transformation ## Graph transformation
![pipeline(3)](./static/pipeline_3.png) ![pipeline(3)](./static/pipeline_3.png)
In order to support the graph for inference we need to support all operators. In order to support the graph for inference we need to support all operators.
The imported model contains ```Flatten``` before the ```Gemm``` operator. The ```aidge.FC``` operator already supports the flatten operation. The imported model contains ```Flatten``` before the ```Gemm``` operator. The ```aidge.FC``` operator already supports the flatten operation.
Graph transformation is required to support the graph for inference, i.e. remove the ```Flatten``` operator. Graph transformation is required to support the graph for inference, i.e. remove the ```Flatten``` operator.
Aidge graph transformation toolchain is the following process : Aidge graph transformation toolchain is the following process :
**1. Describe the graph pattern** **1. Describe the graph pattern**
In order to find specific patterns inside a graph, there is first a need to describe those patterns. Aidge introduces an innovative way to describe graph patterns, **Graph Regular Expression**, inspired by regular expression from the formal language theory. In order to find specific patterns inside a graph, there is first a need to describe those patterns. Aidge introduces an innovative way to describe graph patterns, **Graph Regular Expression**, inspired by regular expression from the formal language theory.
In this example the GraphRegEx used would be simple: In this example the GraphRegEx used would be simple:
``` ```
"Flatten->FC;" "Flatten->FC;"
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
graph_regex = aidge_core.GraphRegex() graph_regex = aidge_core.GraphRegex()
graph_regex.set_node_key("Flatten", "getType($) =='Flatten'") graph_regex.set_node_key("Flatten", "getType($) =='Flatten'")
graph_regex.set_node_key("FC", "getType($) =='FC'") graph_regex.set_node_key("FC", "getType($) =='FC'")
graph_regex.add_query("Flatten -> FC") graph_regex.add_query("Flatten -> FC")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
**2. Match the described pattern** **2. Match the described pattern**
Once the graph pattern is described with a graph regular expression, we apply an innovative graph matching algorithm to find patterns corresponding to the description. Once the graph pattern is described with a graph regular expression, we apply an innovative graph matching algorithm to find patterns corresponding to the description.
This alogrithm will return all the matched patterns described with a graph regular expression in a [match](https://eclipse-aidge.readthedocs.io/en/latest/source/API/Core/graphMatching.html#match) class. One matched pattern is the combinaison of the graph pattern start nodes and all the nodes in the matched pattern (including the start nodes). This alogrithm will return all the matched patterns described with a graph regular expression in a [match](https://eclipse-aidge.readthedocs.io/en/latest/source/API/Core/graphMatching.html#match) class. One matched pattern is the combinaison of the graph pattern start nodes and all the nodes in the matched pattern (including the start nodes).
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
all_match = graph_regex.match(model) all_match = graph_regex.match(model)
print('Number of match : ', len(all_match)) print('Number of match : ', len(all_match))
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
In this case, we have one match : In this case, we have one match :
- List of one list containing the start node : [[Flatten node]] - List of one list containing the start node : [[Flatten node]]
- List of one set containing all the matched nodes : [{Flatten node, FC node}] - List of one set containing all the matched nodes : [{Flatten node, FC node}]
Let's visualize the match : Let's visualize the match :
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print('The start node : ') print('The start node : ')
for match in all_match: for match in all_match:
print('\t', match.get_start_node()[0].type()) print('\t', match.get_start_node()[0].type())
print('All the matched nodes for', match.get_query() , ':') print('All the matched nodes for', match.get_query() , ':')
for n in match.get_all(): for n in match.get_all():
print('\t', n.type()) print('\t', n.type())
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
**3. Apply graph transformations on the matched patterns** **3. Apply graph transformations on the matched patterns**
Now that we have matched the desired patterns we can apply graph transformation on it. The main graph transformation functions (currently under dev) are : Now that we have matched the desired patterns we can apply graph transformation on it. The main graph transformation functions (currently under dev) are :
- Replace the current GraphView with a set of given Nodes if possible : [replace](https://eclipse-aidge.readthedocs.io/en/latest/source/API/Core/graph.html#aidge_core.GraphView.replace) - Replace the current GraphView with a set of given Nodes if possible : [replace](https://eclipse-aidge.readthedocs.io/en/latest/source/API/Core/graph.html#aidge_core.GraphView.replace)
- Insert a node (newParentNode) as a parent of the passed node (childNode) : [insert_parent](https://eclipse-aidge.readthedocs.io/en/latest/source/API/Core/graph.html#_CPPv4N5Aidge9GraphView12insertParentE7NodePtr7NodePtr9IOIndex_t9IOIndex_t9IOIndex_t) - Insert a node (newParentNode) as a parent of the passed node (childNode) : [insert_parent](https://eclipse-aidge.readthedocs.io/en/latest/source/API/Core/graph.html#_CPPv4N5Aidge9GraphView12insertParentE7NodePtr7NodePtr9IOIndex_t9IOIndex_t9IOIndex_t)
- Remove a node : remove() - Remove a node : remove()
In this example we remove the ```Flatten``` operator from the graph using replace. In this example we remove the ```Flatten``` operator from the graph using replace.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
g = aidge_core.GraphView() g = aidge_core.GraphView()
g.add(next(iter(all_match)).get_start_node()[0]) g.add(next(iter(all_match)).get_start_node()[0])
aidge_core.GraphView.replace(g.get_nodes(), set()) aidge_core.GraphView.replace(g.get_nodes(), set())
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The flatten is removed, let's visualize the model : The flatten is removed, let's visualize the model :
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
model.save("mySupportedModel") model.save("mySupportedModel")
visualize_mmd("mySupportedModel.mmd") visualize_mmd("mySupportedModel.mmd")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
All of these steps are embedded inside ``recipes`` functions. These recipes are available in ``aidge_core``, some recipes are: All of these steps are embedded inside ``recipes`` functions. These recipes are available in ``aidge_core``, some recipes are:
- *fuse_batchnorm*: Fuse BatchNorm inside Conv or FC operator; - *fuse_batchnorm*: Fuse BatchNorm inside Conv or FC operator;
- *fuse_mul_add*: Fuse MatMul and Add operator into a FC operator; - *fuse_mul_add*: Fuse MatMul and Add operator into a FC operator;
- *remove_flatten*: Remove Flatten if it is before an FC operator. - *remove_flatten*: Remove Flatten if it is before an FC operator.
Let's do it again with the *remove_flatten* recipie : Let's do it again with the *remove_flatten* recipie :
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Import model again # Import model again
model = aidge_onnx.load_onnx("MLP_MNIST.onnx") model = aidge_onnx.load_onnx("MLP_MNIST.onnx")
# Use remove_flatten recipie # Use remove_flatten recipie
aidge_core.remove_flatten(model) aidge_core.remove_flatten(model)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
This time the flatten is removed with the recipie, let's visualize the model : This time the flatten is removed with the recipie, let's visualize the model :
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
model.save("mySupportedModel") model.save("mySupportedModel")
visualize_mmd("mySupportedModel.mmd") visualize_mmd("mySupportedModel.mmd")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Inference ## Inference
We now have a graph fully supported by aidge, we are ready to do some inference ! We now have a graph fully supported by aidge, we are ready to do some inference !
![pipeline(4)](./static/pipeline_4.png) ![pipeline(4)](./static/pipeline_4.png)
### Create an input tensor & its node in the graph ### Create an input tensor & its node in the graph
In order to perform an inferencewe will load an image from the MNIST dataset using Numpy. In order to perform an inferencewe will load an image from the MNIST dataset using Numpy.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
## Load input data & its output from the MNIST_model ## Load input data & its output from the MNIST_model
digit = np.load("input_digit.npy") digit = np.load("input_digit.npy")
plt.imshow(digit[0][0], cmap='gray') plt.imshow(digit[0][0], cmap='gray')
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
And in order to validate the result our model will provide, we will also load the output the PyTorch model povided for this image And in order to validate the result our model will provide, we will also load the output the PyTorch model povided for this image
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
output_model = np.load("output_digit.npy") output_model = np.load("output_digit.npy")
print(output_model) print(output_model)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Thanks to the Numpy interoperability we can create an Aidge ``Tensor`` using directly the numpy array storing the image. Thanks to the Numpy interoperability we can create an Aidge ``Tensor`` using directly the numpy array storing the image.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
input_tensor = aidge_core.Tensor(digit) input_tensor = aidge_core.Tensor(digit)
print(f"Aidge Input Tensor dimensions: \n{input_tensor.dims()}") print(f"Aidge Input Tensor dimensions: \n{input_tensor.dims()}")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
To add an input to the graph we can create a ``Producer`` node, insert it in the ``GraphView`` and set its output with the ``Tensor`` we have just created, or data can simply be fed to the ``GraphView`` via the ``scheduler`` ```forward()``` call. To add an input to the graph we can create a ``Producer`` node, insert it in the ``GraphView`` and set its output with the ``Tensor`` we have just created, or data can simply be fed to the ``GraphView`` via the ``scheduler`` ```forward()``` call.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Configure the model for inference ### Configure the model for inference
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
At the moment the model has no implementation, it is only a datastructure. To set an implementation we will set a dataype and a backend. At the moment the model has no implementation, it is only a datastructure. To set an implementation we will set a dataype and a backend.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Configure the model # Configure the model
model.compile("cpu", aidge_core.DataType.Float32, dims=[[1,1,28,28]]) model.compile("cpu", aidge_core.dtype.float32, dims=[[1,1,28,28]])
# equivalent to set_datatype(), set_backend() and forward_dims() # equivalent to set_datatype(), set_backend() and forward_dims()
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Create a scheduler and run inference ### Create a scheduler and run inference
The graph is ready to run ! We just need to schedule the execution, to do this we will create a ``Scheduler`` object, which will take the graph and generate an optimized scheduling using a consummer producer heuristic. The graph is ready to run ! We just need to schedule the execution, to do this we will create a ``Scheduler`` object, which will take the graph and generate an optimized scheduling using a consummer producer heuristic.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Create SCHEDULER # Create SCHEDULER
scheduler = aidge_core.SequentialScheduler(model) scheduler = aidge_core.SequentialScheduler(model)
# Run inference ! # Run inference !
scheduler.forward(data=[input_tensor]) scheduler.forward(data=[input_tensor])
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Assert results # Assert results
for outNode in model.get_output_nodes(): for outNode in model.get_output_nodes():
output_aidge = np.array(outNode.get_operator().get_output(0)) output_aidge = np.array(outNode.get_operator().get_output(0))
print(output_aidge) print(output_aidge)
print('Aidge prediction = ', np.argmax(output_aidge[0])) print('Aidge prediction = ', np.argmax(output_aidge[0]))
assert(np.allclose(output_aidge, output_model,rtol=1e-04)) assert(np.allclose(output_aidge, output_model,rtol=1e-04))
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
It is possible to save the scheduling in a mermaid format using: It is possible to save the scheduling in a mermaid format using:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
scheduler.save_scheduling_diagram("schedulingSequential") scheduler.save_scheduling_diagram("schedulingSequential")
visualize_mmd("schedulingSequential.mmd") visualize_mmd("schedulingSequential.mmd")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Export ## Export
Now that we have tested the imported graph we can look at one of the main feature of Aidge, the export of computationnal graph to an hardware target using code generation. Now that we have tested the imported graph we can look at one of the main feature of Aidge, the export of computationnal graph to an hardware target using code generation.
![pipeline(5)](./static/pipeline_5.png) ![pipeline(5)](./static/pipeline_5.png)
### Generate an export in C++ ### Generate an export in C++
In this example we will generate a generic C++ export. In this example we will generate a generic C++ export.
This export is not based on the `cpu` backend we have set before. This export is not based on the `cpu` backend we have set before.
In this example we will create a standalone export which is abstracted from the Aidge platform. In this example we will create a standalone export which is abstracted from the Aidge platform.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
! rm -r myexport ! rm -r myexport
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!ls myexport !ls myexport
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Generating a ``cpu`` export recquires the ``aidge_export_cpp`` module. Generating a ``cpu`` export recquires the ``aidge_export_cpp`` module.
Once the module is imported you just need one line to generate an export of the graph. Once the module is imported you just need one line to generate an export of the graph.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import aidge_export_cpp import aidge_export_cpp
# Freeze the model by setting constant to parameters producers # Freeze the model by setting constant to parameters producers
for node in model.get_nodes(): for node in model.get_nodes():
if node.type() == "Producer": if node.type() == "Producer":
node.get_operator().set_attr("Constant", True) node.get_operator().attr.constant = True
# Create Producer Node for the Graph # Create Producer Node for the Graph
input_node = aidge_core.Producer([1, 1, 28, 28], "input") input_node = aidge_core.Producer([1, 1, 28, 28], "input")
input_node.add_child(model) input_node.add_child(model)
model.add(input_node) model.add(input_node)
# Configuration for the model + forward dimensions # Configuration for the model + forward dimensions
model.compile("cpu", aidge_core.DataType.Float32) model.compile("cpu", aidge_core.dtype.float32)
# Export the model in C++ standalone # Export the model in C++ standalone
aidge_export_cpp.export("myexport", model, scheduler) aidge_export_cpp.export("myexport", model, scheduler)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The export function will generate : The export function will generate :
- **dnn/layers** layers configuration; - **dnn/layers** layers configuration;
- **dnn/parameters** folder with parameters; - **dnn/parameters** folder with parameters;
- **dnn/include/dnn.h** API to use the export; - **dnn/include/dnn.h** API to use the export;
- **dnn/include/network_functions.h** header file for kernels; - **dnn/include/network_functions.h** header file for kernels;
- **dnn/memory** memory management information; - **dnn/memory** memory management information;
- **dnn/src** kernel source code + forward function; - **dnn/src** kernel source code + forward function;
- **main.cpp** This file is an export of the scheduler, it allows - **main.cpp** This file is an export of the scheduler, it allows
- **Makefile** To compile the main.cpp - **Makefile** To compile the main.cpp
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!tree myexport !tree myexport
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Generate an input file for tests ### Generate an input file for tests
To test the export we need to provide data, to do so we will export the numpy array using: To test the export we need to provide data, to do so we will export the numpy array using:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
aidge_export_cpp.generate_input_file(array_name="inputs", array=digit.reshape(-1), export_folder="myexport") aidge_export_cpp.generate_input_file(array_name="inputs", array=digit.reshape(-1), export_folder="myexport")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Compile the export ### Compile the export
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!cd myexport && make !cd myexport && make
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Run the export ### Run the export
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!./myexport/bin/run_export !./myexport/bin/run_export
``` ```
......
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Backend CUDA Inference # Backend CUDA Inference
This tutorial demonstrate the inference of LeNet model using Aidge Backend CUDA. <br> This tutorial demonstrate the inference of LeNet model using Aidge Backend CUDA. <br>
For the sake of simplicity, we will not train the model. Feel free to replace the onnx model with one already trained. <br> For the sake of simplicity, we will not train the model. Feel free to replace the onnx model with one already trained. <br>
- we sart by creating a LeNet model: - we sart by creating a LeNet model:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import torch import torch
import torch.nn as nn import torch.nn as nn
import torch.onnx import torch.onnx
class LeNet(nn.Module): class LeNet(nn.Module):
def __init__(self, num_classes): def __init__(self, num_classes):
super(LeNet, self).__init__() super(LeNet, self).__init__()
self.conv1 = nn.Conv2d(1, 6, kernel_size=5) self.conv1 = nn.Conv2d(1, 6, kernel_size=5)
self.conv2 = nn.Conv2d(6, 16, kernel_size=5) self.conv2 = nn.Conv2d(6, 16, kernel_size=5)
self.fc1 = nn.Linear(16 * 4 * 4, 120) self.fc1 = nn.Linear(16 * 4 * 4, 120)
self.fc2 = nn.Linear(120, 84) self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, num_classes) self.fc3 = nn.Linear(84, num_classes)
def forward(self, x): def forward(self, x):
x = torch.relu(self.conv1(x)) x = torch.relu(self.conv1(x))
x = torch.max_pool2d(x, kernel_size=2, stride=2) x = torch.max_pool2d(x, kernel_size=2, stride=2)
x = torch.relu(self.conv2(x)) x = torch.relu(self.conv2(x))
x = torch.max_pool2d(x, kernel_size=2, stride=2) x = torch.max_pool2d(x, kernel_size=2, stride=2)
x = x.view(-1, 16 * 4 * 4) x = x.view(-1, 16 * 4 * 4)
x = torch.relu(self.fc1(x)) x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x)) x = torch.relu(self.fc2(x))
x = self.fc3(x) x = self.fc3(x)
return x return x
# Instantiate the model # Instantiate the model
num_classes = 10 # Assuming you're working with MNIST dataset num_classes = 10 # Assuming you're working with MNIST dataset
model = LeNet(num_classes) model = LeNet(num_classes)
# Set the model to evaluation mode # Set the model to evaluation mode
model.eval() model.eval()
# Example input shape (batch_size, channels, height, width) # Example input shape (batch_size, channels, height, width)
dummy_input = torch.randn(1, 1, 28, 28) dummy_input = torch.randn(1, 1, 28, 28)
# Export the model to ONNX # Export the model to ONNX
torch.onnx.export(model, dummy_input, "lenet.onnx", verbose=True) torch.onnx.export(model, dummy_input, "lenet.onnx", verbose=True)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
- import the needed libraries - import the needed libraries
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import aidge_core import aidge_core
import aidge_backend_cuda import aidge_backend_cuda
import aidge_onnx import aidge_onnx
import numpy as np import numpy as np
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
- load onnx model on Aidge - load onnx model on Aidge
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
model = aidge_onnx.load_onnx("lenet.onnx") model = aidge_onnx.load_onnx("lenet.onnx")
aidge_core.remove_flatten(model) aidge_core.remove_flatten(model)
# Configure the model # Configure the model
model.set_datatype(aidge_core.DataType.Float32) model.set_datatype(aidge_core.dtype.float32)
model.set_backend("cuda") model.set_backend("cuda")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
- add input - add input
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Create an input node # Create an input node
input = np.random.randn(1, 1, 28, 28).astype(np.float32) input = np.random.randn(1, 1, 28, 28).astype(np.float32)
input_tensor = aidge_core.Tensor(input) input_tensor = aidge_core.Tensor(input)
input_node = aidge_core.Producer(input_tensor, "input") input_node = aidge_core.Producer(input_tensor, "input")
input_node.get_operator().set_datatype(aidge_core.DataType.Float32) input_node.get_operator().set_datatype(aidge_core.dtype.float32)
input_node.get_operator().set_backend("cuda") input_node.get_operator().set_backend("cuda")
# Link node to model # Link node to model
input_node.add_child(model) input_node.add_child(model)
model.add(input_node) model.add(input_node)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
- create a scheduler and run inference - create a scheduler and run inference
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Define the scheduler # Define the scheduler
scheduler = aidge_core.SequentialScheduler(model) scheduler = aidge_core.SequentialScheduler(model)
# Run inference ! # Run inference !
scheduler.forward() scheduler.forward()
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
- get the ouput: <br> - get the ouput: <br>
Before getting the output we need to set it to backend cpu Before getting the output we need to set it to backend cpu
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
for outNode in model.get_output_nodes(): for outNode in model.get_output_nodes():
outNode.get_operator().get_output(0).set_backend('cpu') outNode.get_operator().get_output(0).set_backend('cpu')
output_aidge = np.array(outNode.get_operator().get_output(0)) output_aidge = np.array(outNode.get_operator().get_output(0))
print("Aidge output: {}".format(output_aidge)) print("Aidge output: {}".format(output_aidge))
# Make sure to set the output back to "cuda" otherwise the model will not be usable # Make sure to set the output back to "cuda" otherwise the model will not be usable
outNode.get_operator().get_output(0).set_backend('cuda') outNode.get_operator().get_output(0).set_backend('cuda')
``` ```
......
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Database MNIST # Database MNIST
This tutorial demonstrate the usage of databases and data providers to perform an evaluation on a model using Aidge. This tutorial demonstrate the usage of databases and data providers to perform an evaluation on a model using Aidge.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Installation and Requirements ## Installation and Requirements
- Python packages : aidge_core, aidge_backend_cpu, aidge_backend_opencv - Python packages : aidge_core, aidge_backend_cpu, aidge_backend_opencv
- Download MNIST database - Download MNIST database
- Download MLP onnx model from git-lfs - Download MLP onnx model from git-lfs
- Define model visualization function and top-1 accuracy metric - Define model visualization function and top-1 accuracy metric
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!pip install git-lfs !pip install git-lfs
!git lfs pull !git lfs pull
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import os import os
import urllib.request import urllib.request
import gzip import gzip
import shutil import shutil
mnist_dir = 'MNIST_test' mnist_dir = 'MNIST_test'
os.makedirs(mnist_dir, exist_ok=True) os.makedirs(mnist_dir, exist_ok=True)
base_url = 'https://ossci-datasets.s3.amazonaws.com/mnist/' base_url = 'https://ossci-datasets.s3.amazonaws.com/mnist/'
files = ['t10k-images-idx3-ubyte.gz', 't10k-labels-idx1-ubyte.gz'] files = ['t10k-images-idx3-ubyte.gz', 't10k-labels-idx1-ubyte.gz']
for file in files: for file in files:
url = base_url + file url = base_url + file
file_path = os.path.join(mnist_dir, file) file_path = os.path.join(mnist_dir, file)
decompressed_file_path = os.path.splitext(file_path)[0] decompressed_file_path = os.path.splitext(file_path)[0]
if not os.path.exists(decompressed_file_path): if not os.path.exists(decompressed_file_path):
print("Downloading", file) print("Downloading", file)
urllib.request.urlretrieve(url, file_path) urllib.request.urlretrieve(url, file_path)
print("Download complete") print("Download complete")
decompressed_file_path = os.path.splitext(file_path)[0] decompressed_file_path = os.path.splitext(file_path)[0]
print("Decompressing", file) print("Decompressing", file)
raw = gzip.open(file_path, 'rb').read() raw = gzip.open(file_path, 'rb').read()
open(decompressed_file_path, 'wb').write(raw) open(decompressed_file_path, 'wb').write(raw)
print("Decompression complete") print("Decompression complete")
else: else:
print(f"{file} already exists. Skipping download and decompression.") print(f"{file} already exists. Skipping download and decompression.")
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import base64 import base64
from IPython.display import Image, display from IPython.display import Image, display
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
def visualize_mmd(path_to_mmd): def visualize_mmd(path_to_mmd):
with open(path_to_mmd, "r") as file_mmd: with open(path_to_mmd, "r") as file_mmd:
graph_mmd = file_mmd.read() graph_mmd = file_mmd.read()
graphbytes = graph_mmd.encode("ascii") graphbytes = graph_mmd.encode("ascii")
base64_bytes = base64.b64encode(graphbytes) base64_bytes = base64.b64encode(graphbytes)
base64_string = base64_bytes.decode("ascii") base64_string = base64_bytes.decode("ascii")
display(Image(url=f"https://mermaid.ink/img/{base64_string}")) display(Image(url=f"https://mermaid.ink/img/{base64_string}"))
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
def top1_accuracy(predictions, labels): def top1_accuracy(predictions, labels):
total = len(predictions) total = len(predictions)
predicted_class = predictions.argmax(axis=1) predicted_class = predictions.argmax(axis=1)
correct_pred = (predicted_class == labels).sum() correct_pred = (predicted_class == labels).sum()
accuracy = correct_pred / total accuracy = correct_pred / total
return accuracy return accuracy
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Perform an evaluation of the LeNet-like on Aidge ## Perform an evaluation of the LeNet-like on Aidge
- Import Aidge libraries - Import Aidge libraries
- Import ONNX model - Import ONNX model
- Configure the model for inference - Configure the model for inference
- Create the Database and DataProvider - Create the Database and DataProvider
- Perform the evaluation - Perform the evaluation
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import aidge_core import aidge_core
import aidge_backend_opencv import aidge_backend_opencv
import aidge_backend_cpu import aidge_backend_cpu
import aidge_onnx import aidge_onnx
import numpy as np import numpy as np
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
model = aidge_onnx.load_onnx("../101_first_step/MLP_MNIST.onnx") model = aidge_onnx.load_onnx("../101_first_step/MLP_MNIST.onnx")
aidge_core.remove_flatten(model) aidge_core.remove_flatten(model)
model.save("mySupportedModel") model.save("mySupportedModel")
visualize_mmd("mySupportedModel.mmd") visualize_mmd("mySupportedModel.mmd")
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Configure the model # Configure the model
model.set_datatype(aidge_core.DataType.Float32) model.set_datatype(aidge_core.dtype.float32)
model.set_backend("cpu") model.set_backend("cpu")
# Define the scheduler # Define the scheduler
scheduler = aidge_core.SequentialScheduler(model) scheduler = aidge_core.SequentialScheduler(model)
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
val_mnist = aidge_backend_opencv.MNIST(dataPath="./MNIST_test", val_mnist = aidge_backend_opencv.MNIST(dataPath="./MNIST_test",
train=False, train=False,
load_data_in_memory=False) load_data_in_memory=False)
val_dataprovider = aidge_core.DataProvider(val_mnist, val_dataprovider = aidge_core.DataProvider(val_mnist,
batch_size=200, batch_size=200,
shuffle=True, shuffle=True,
drop_last=False) drop_last=False)
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
val_acc = 0 val_acc = 0
for i, (data_batch, lbl_batch) in enumerate(val_dataprovider): for i, (data_batch, lbl_batch) in enumerate(val_dataprovider):
data_batch.set_datatype(aidge_core.DataType.Float32) data_batch.set_datatype(aidge_core.dtype.float32)
lbl_batch.set_datatype(aidge_core.DataType.Float32) lbl_batch.set_datatype(aidge_core.dtype.float32)
# Run inference ! # Run inference !
scheduler.forward(data=[data_batch]) scheduler.forward(data=[data_batch])
# Get output and label in a numpy array # Get output and label in a numpy array
output_aidge = np.array(list(model.get_output_nodes())[0].get_operator().get_output(0)) output_aidge = np.array(list(model.get_output_nodes())[0].get_operator().get_output(0))
lbl = np.array(lbl_batch) lbl = np.array(lbl_batch)
# Compute the top-1 accuracy # Compute the top-1 accuracy
val_acc += top1_accuracy(output_aidge, lbl.flatten()) val_acc += top1_accuracy(output_aidge, lbl.flatten())
val_acc = val_acc / len(val_dataprovider) val_acc = val_acc / len(val_dataprovider)
print(val_acc) print(val_acc)
``` ```
......
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Graph Regular Expression # Graph Regular Expression
Graph Regular expression is a powerful tool inspired by regular expressions. It is designed to express complex graph structures, much like regular expressions do for character strings. Graph Regular expression is a powerful tool inspired by regular expressions. It is designed to express complex graph structures, much like regular expressions do for character strings.
This tutorial demonstrate the usage of the Graph Regular Expression to perform graph matching in Aidge. This tutorial demonstrate the usage of the Graph Regular Expression to perform graph matching in Aidge.
It is organized as follows : It is organized as follows :
- Section Requirements covers the requirements to run this tutorial - Section Requirements covers the requirements to run this tutorial
- Section Graph Regex Flow explains the user pipeline to use Graph Regex - Section Graph Regex Flow explains the user pipeline to use Graph Regex
- Section Query presents how to describe different graph patterns - Section Query presents how to describe different graph patterns
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Requirements ## Requirements
- Python packages : aidge_core, matplotlib, numpy - Python packages : aidge_core, matplotlib, numpy
- Define model visualization function - Define model visualization function
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!pip install --quiet matplotlib !pip install --quiet matplotlib
!pip install --quiet numpy !pip install --quiet numpy
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import base64 import base64
from IPython.display import Image, display from IPython.display import Image, display
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
def visualize_mmd(path_to_mmd): def visualize_mmd(path_to_mmd):
with open(path_to_mmd, "r") as file_mmd: with open(path_to_mmd, "r") as file_mmd:
graph_mmd = file_mmd.read() graph_mmd = file_mmd.read()
graphbytes = graph_mmd.encode("ascii") graphbytes = graph_mmd.encode("ascii")
base64_bytes = base64.b64encode(graphbytes) base64_bytes = base64.b64encode(graphbytes)
base64_string = base64_bytes.decode("ascii") base64_string = base64_bytes.decode("ascii")
display(Image(url=f"https://mermaid.ink/img/{base64_string}")) display(Image(url=f"https://mermaid.ink/img/{base64_string}"))
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Graph Regex Flow ## Graph Regex Flow
![pipeline_graph_matching](./static/pipeline_gm.PNG) ![pipeline_graph_matching](./static/pipeline_gm.PNG)
The GraphRegex class enable to perform graph transformations. It takes as inputs : The GraphRegex class enable to perform graph transformations. It takes as inputs :
- A GraphView - A GraphView
- Queries : express one or more graph pattern descriptions to find in the GraphView - Queries : express one or more graph pattern descriptions to find in the GraphView
To have more information on the query, i.e. graph descriptions, please refer to the following slides to open in your browser : To have more information on the query, i.e. graph descriptions, please refer to the following slides to open in your browser :
- Basic concepts of [Graph Regular Expression](./static/GraphRegularExpression.html) - Basic concepts of [Graph Regular Expression](./static/GraphRegularExpression.html)
- [Quantifier](./static/Quantifier.html) - [Quantifier](./static/Quantifier.html)
For more information, please refer to the User Guide on [graph transformation](https://eclipse-aidge.readthedocs.io/en/latest/source/UserGuide/transformGraph.html). For more information, please refer to the User Guide on [graph transformation](https://eclipse-aidge.readthedocs.io/en/latest/source/UserGuide/transformGraph.html).
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Query ## Query
This section presents several examples to express different graph patterns : sequential, parallel, and advanced node testing. This section presents several examples to express different graph patterns : sequential, parallel, and advanced node testing.
### Sequential graph ### Sequential graph
- Create a sequential graph - Create a sequential graph
- Describe a pattern with graph Regular Expression - Describe a pattern with graph Regular Expression
- Match the describe pattern - Match the describe pattern
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import aidge_core import aidge_core
model = aidge_core.sequential([aidge_core.MatMul(name="MatMul0"), model = aidge_core.sequential([aidge_core.MatMul(name="MatMul0"),
aidge_core.Add(2, name="Add0"), aidge_core.Add(2, name="Add0"),
aidge_core.ReLU(name="ReLU0"), aidge_core.ReLU(name="ReLU0"),
aidge_core.MatMul(name="MatMul1"), aidge_core.MatMul(name="MatMul1"),
aidge_core.Add(2, name="Add1"), aidge_core.Add(2, name="Add1"),
aidge_core.ReLU(name="ReLU1")]) aidge_core.ReLU(name="ReLU1")])
model.save("mySequentialModel") model.save("mySequentialModel")
visualize_mmd("mySequentialModel.mmd") visualize_mmd("mySequentialModel.mmd")
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
graph_regex = aidge_core.GraphRegex() graph_regex = aidge_core.GraphRegex()
graph_regex.set_node_key("A", "getType($) =='Add'") graph_regex.set_node_key("A", "getType($) =='Add'")
graph_regex.set_node_key("B", "getType($) =='ReLU'") graph_regex.set_node_key("B", "getType($) =='ReLU'")
graph_regex.add_query("A -> B") graph_regex.add_query("A -> B")
all_match = graph_regex.match(model) all_match = graph_regex.match(model)
print('Number of match : ', len(all_match)) print('Number of match : ', len(all_match))
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
In this case, we have two matches. Each match contains : In this case, we have two matches. Each match contains :
- The associated query. In this example : "A->B" - The associated query. In this example : "A->B"
- The list containing the start nodes. In this example : [Add0] - The list containing the start nodes. In this example : [Add0]
- The set containing all the matched nodes. In this example : {Add0, ReLU0} - The set containing all the matched nodes. In this example : {Add0, ReLU0}
Let's visualize the matches : Let's visualize the matches :
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
for i, match in enumerate(all_match): for i, match in enumerate(all_match):
print('Match ', i ,' associated to query : ', match.get_query()) print('Match ', i ,' associated to query : ', match.get_query())
print('The start node :', match.get_start_node()[0].name()) print('The start node :', match.get_start_node()[0].name())
print('All the matched nodes :') print('All the matched nodes :')
for n in match.get_all(): for n in match.get_all():
print('\t', n.name()) print('\t', n.name())
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Parallel graph ### Parallel graph
- Create a graph with parallel branches - Create a graph with parallel branches
- Describe a pattern with graph Regular Expression - Describe a pattern with graph Regular Expression
- Match the describe pattern - Match the describe pattern
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import aidge_core import aidge_core
model = aidge_core.sequential([aidge_core.MatMul(name="MatMul0"), model = aidge_core.sequential([aidge_core.MatMul(name="MatMul0"),
aidge_core.Add(2, name="Add0"), aidge_core.Add(2, name="Add0"),
aidge_core.ReLU(name="ReLU0"), aidge_core.ReLU(name="ReLU0"),
aidge_core.parallel([aidge_core.Conv2D(1, 1, [3, 3], name="Conv0"), aidge_core.Conv2D(1, 1, [3, 3], name="Conv1")]), aidge_core.parallel([aidge_core.Conv2D(1, 1, [3, 3], name="Conv0"), aidge_core.Conv2D(1, 1, [3, 3], name="Conv1")]),
aidge_core.Add(2, name="Add1"), aidge_core.Add(2, name="Add1"),
aidge_core.ReLU(name="ReLU1")]) aidge_core.ReLU(name="ReLU1")])
model.save("mySequentialModel") model.save("mySequentialModel")
visualize_mmd("mySequentialModel.mmd") visualize_mmd("mySequentialModel.mmd")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
When applying regular expression concepts to graphs, we find ourselves dealing with parallel branches. When applying regular expression concepts to graphs, we find ourselves dealing with parallel branches.
To address this, we express the graph by decomposing it into sequences. To address this, we express the graph by decomposing it into sequences.
This decomposition involves defining two types of nodes: This decomposition involves defining two types of nodes:
- common nodes, which reference the same node across multiple sequences : token "#" - common nodes, which reference the same node across multiple sequences : token "#"
- unique nodes, as the name suggests, that are exclusive to a particular sequence. - unique nodes, as the name suggests, that are exclusive to a particular sequence.
Let's try to match the ReLU followed by the two parallel convolutions : Let's try to match the ReLU followed by the two parallel convolutions :
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
graph_regex = aidge_core.GraphRegex() graph_regex = aidge_core.GraphRegex()
graph_regex.set_node_key("A", "getType($) =='ReLU'") graph_regex.set_node_key("A", "getType($) =='ReLU'")
graph_regex.set_node_key("B", "getType($) =='Conv'") graph_regex.set_node_key("B", "getType($) =='Conv'")
graph_regex.add_query("A# -> B; A# -> B") graph_regex.add_query("A# -> B; A# -> B")
all_match = graph_regex.match(model) all_match = graph_regex.match(model)
print('Number of match : ', len(all_match)) print('Number of match : ', len(all_match))
for i, match in enumerate(all_match): for i, match in enumerate(all_match):
print('Match ', i ,' associated to query : ', match.get_query()) print('Match ', i ,' associated to query : ', match.get_query())
print('The start node :', match.get_start_node()[0].name()) print('The start node :', match.get_start_node()[0].name())
print('All the matched nodes :') print('All the matched nodes :')
for n in match.get_all(): for n in match.get_all():
print('\t', n.name()) print('\t', n.name())
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Quantifiers ### Quantifiers
Quantifiers are tokens that specify the number of occurrences of a preceding element in the pattern. The supported quantifiers in graph regular expressions are : Quantifiers are tokens that specify the number of occurrences of a preceding element in the pattern. The supported quantifiers in graph regular expressions are :
- "+" : one or more - "+" : one or more
- "*" : zero or more - "*" : zero or more
Using the quantifiers, the graph regular expression can represent an ensemble of patterns. Using the quantifiers, the graph regular expression can represent an ensemble of patterns.
For example "Conv+" represents sequences of at least one convolution. For example "Conv+" represents sequences of at least one convolution.
In this subsection : In this subsection :
- Create a sequential graph - Create a sequential graph
- Describe a pattern with graph Regular Expression using quantifier 'one or more' : '+' - Describe a pattern with graph Regular Expression using quantifier 'one or more' : '+'
- Match the describe pattern - Match the describe pattern
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import aidge_core import aidge_core
model = aidge_core.sequential([aidge_core.MatMul(name="MatMul0"), model = aidge_core.sequential([aidge_core.MatMul(name="MatMul0"),
aidge_core.Add(2, name="Add0"), aidge_core.Add(2, name="Add0"),
aidge_core.ReLU(name="ReLU0"), aidge_core.ReLU(name="ReLU0"),
aidge_core.Conv2D(1, 1, [3, 3], name="Conv0"), aidge_core.Conv2D(1, 1, [3, 3], name="Conv0"),
aidge_core.Conv2D(1, 1, [3, 3], name="Conv1"), aidge_core.Conv2D(1, 1, [3, 3], name="Conv1"),
aidge_core.Conv2D(1, 1, [3, 3], name="Conv2")]) aidge_core.Conv2D(1, 1, [3, 3], name="Conv2")])
model.save("mySequentialModel") model.save("mySequentialModel")
visualize_mmd("mySequentialModel.mmd") visualize_mmd("mySequentialModel.mmd")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Let's try to match the ReLU0 followed by at least one convolution : Let's try to match the ReLU0 followed by at least one convolution :
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
graph_regex = aidge_core.GraphRegex() graph_regex = aidge_core.GraphRegex()
graph_regex.set_node_key("A", "getType($) =='ReLU'") graph_regex.set_node_key("A", "getType($) =='ReLU'")
graph_regex.set_node_key("B", "getType($) =='Conv'") graph_regex.set_node_key("B", "getType($) =='Conv'")
graph_regex.add_query("A -> B+") graph_regex.add_query("A -> B+")
all_match = graph_regex.match(model) all_match = graph_regex.match(model)
print('Number of match : ', len(all_match)) print('Number of match : ', len(all_match))
for i, match in enumerate(all_match): for i, match in enumerate(all_match):
print('Match ', i ,' associated to query : ', match.get_query()) print('Match ', i ,' associated to query : ', match.get_query())
print('The start node :', match.get_start_node()[0].name()) print('The start node :', match.get_start_node()[0].name())
print('All the matched nodes :') print('All the matched nodes :')
for n in match.get_all(): for n in match.get_all():
print('\t', n.name()) print('\t', n.name())
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Advanced node testing ### Advanced node testing
So far, the described graph patterns were based on the topology of the graph and the type of nodes. So far, the described graph patterns were based on the topology of the graph and the type of nodes.
Graph Regular expression allows to perform any test on the node by supporting the function calls of the given node. Graph Regular expression allows to perform any test on the node by supporting the function calls of the given node.
- Create a sequential graph - Create a sequential graph
- Describe a pattern with graph Regular Expression using advanced testing on the dimension of the convolution kernels. - Describe a pattern with graph Regular Expression using advanced testing on the dimension of the convolution kernels.
- Match the describe pattern - Match the describe pattern
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import aidge_core import aidge_core
conv = aidge_core.Conv2D(1, 1, [7, 7], name="C") conv = aidge_core.Conv2D(1, 1, [7, 7], name="C")
print(conv.get_operator().get_attr("KernelDims") == [7,7]) print(conv.get_operator().attr.kernel_dims == [7,7])
print(conv.type()) print(conv.type())
model = aidge_core.sequential([aidge_core.Conv2D(1, 1, [7, 7], name="Conv0"), model = aidge_core.sequential([aidge_core.Conv2D(1, 1, [7, 7], name="Conv0"),
aidge_core.Conv2D(1, 1, [5, 5], name="Conv1"), aidge_core.Conv2D(1, 1, [5, 5], name="Conv1"),
aidge_core.Conv2D(1, 1, [3, 3], name="Conv2"), aidge_core.Conv2D(1, 1, [3, 3], name="Conv2"),
aidge_core.Conv2D(1, 1, [5, 5], name="Conv3"), aidge_core.Conv2D(1, 1, [5, 5], name="Conv3"),
aidge_core.Conv2D(1, 1, [7, 7], name="Conv4")]) aidge_core.Conv2D(1, 1, [7, 7], name="Conv4")])
model.save("mySequentialModel") model.save("mySequentialModel")
visualize_mmd("mySequentialModel.mmd") visualize_mmd("mySequentialModel.mmd")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Let's try to find the convolutions with kernel size 3 or 5. Let's try to find the convolutions with kernel size 3 or 5.
First define your own custom testing fonctions : First define your own custom testing fonctions :
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
def test_kernel_3(node): def test_kernel_3(node):
b = False b = False
if (node.type() == "Conv") : if (node.type() == "Conv") :
b = node.get_operator().get_attr("KernelDims") == [3,3] b = node.get_operator().attr.kernel_dims == [3,3]
return b return b
def test_kernel_5(node): def test_kernel_5(node):
b = False b = False
if (node.type() == "Conv") : if (node.type() == "Conv") :
b = node.get_operator().get_attr("KernelDims") == [5,5] b = node.get_operator().attr.kernel_dims == [5,5]
return b return b
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Then register it in the graph regex and add the associated queries : Then register it in the graph regex and add the associated queries :
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
graph_regex = aidge_core.GraphRegex() graph_regex = aidge_core.GraphRegex()
# Register the custom testing functions # Register the custom testing functions
graph_regex.set_node_key("testk3", test_kernel_3) graph_regex.set_node_key("testk3", test_kernel_3)
graph_regex.set_node_key("testk5", test_kernel_5) graph_regex.set_node_key("testk5", test_kernel_5)
graph_regex.set_node_key("Convk3", "testk3($)==true") graph_regex.set_node_key("Convk3", "testk3($)==true")
graph_regex.set_node_key("Convk5", "testk5($)==true") graph_regex.set_node_key("Convk5", "testk5($)==true")
#Add the associated queries #Add the associated queries
graph_regex.add_query("Convk3") graph_regex.add_query("Convk3")
graph_regex.add_query("Convk5") graph_regex.add_query("Convk5")
all_match = graph_regex.match(model) all_match = graph_regex.match(model)
print('Number of match : ', len(all_match)) print('Number of match : ', len(all_match))
for i, match in enumerate(all_match): for i, match in enumerate(all_match):
print('Match ', i ,' associated to query : ', match.get_query()) print('Match ', i ,' associated to query : ', match.get_query())
print('The start node :', match.get_start_node()[0].name()) print('The start node :', match.get_start_node()[0].name())
print('All the matched nodes :') print('All the matched nodes :')
for n in match.get_all(): for n in match.get_all():
print('\t', n.name()) print('\t', n.name())
``` ```
......
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# <ins>Adding an operator on Aidge</ins> # <ins>Adding an operator on Aidge</ins>
In this tutorial, we'll be adding the operator 'HardMax' to the Aidge plateform.<br> In this tutorial, we'll be adding the operator 'HardMax' to the Aidge plateform.<br>
The HardMax operator computes hardmax values for the given input along a given axis: <br> The HardMax operator computes hardmax values for the given input along a given axis: <br>
Hardmax(element in input, axis) = 1 if the element is the first maximum value along the specified axis, 0 otherwise. <br> Hardmax(element in input, axis) = 1 if the element is the first maximum value along the specified axis, 0 otherwise. <br>
Let's start by creating an onnx model with just the hardmax operator: Let's start by creating an onnx model with just the hardmax operator:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import onnx import onnx
from onnx import helper from onnx import helper
# Create an ONNX graph # Create an ONNX graph
input_tensor_onnx = helper.make_tensor_value_info('input', onnx.TensorProto.FLOAT, [1, 10]) input_tensor_onnx = helper.make_tensor_value_info('input', onnx.TensorProto.FLOAT, [1, 10])
# Create Hardmax node # Create Hardmax node
hardmax_node = onnx.helper.make_node( hardmax_node = onnx.helper.make_node(
'Hardmax', 'Hardmax',
inputs=['input'], inputs=['input'],
outputs=['output'], outputs=['output'],
axis=1 # Constant value for axis axis=1 # Constant value for axis
) )
# Define output tensor # Define output tensor
output_tensor_onnx = helper.make_tensor_value_info('output', onnx.TensorProto.FLOAT, [1, 10]) output_tensor_onnx = helper.make_tensor_value_info('output', onnx.TensorProto.FLOAT, [1, 10])
# Create the graph # Create the graph
graph = helper.make_graph( graph = helper.make_graph(
[hardmax_node], [hardmax_node],
'hardmax_model', 'hardmax_model',
[input_tensor_onnx], [input_tensor_onnx],
[output_tensor_onnx], [output_tensor_onnx],
) )
# Create the model with opset version 10 # Create the model with opset version 10
model = helper.make_model(graph, producer_name='onnx-hardmax-generator', opset_imports=[helper.make_opsetid("", 10)]) model = helper.make_model(graph, producer_name='onnx-hardmax-generator', opset_imports=[helper.make_opsetid("", 10)])
# Save the ONNX model to a file # Save the ONNX model to a file
onnx.save(model, 'hardmax_model.onnx') onnx.save(model, 'hardmax_model.onnx')
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Now let's try to load the saved model with aidge: Now let's try to load the saved model with aidge:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import aidge_core import aidge_core
import aidge_backend_cpu import aidge_backend_cpu
import aidge_onnx import aidge_onnx
aidge_model = aidge_onnx.load_onnx('hardmax_model.onnx') aidge_model = aidge_onnx.load_onnx('hardmax_model.onnx')
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Loading the model on Aidge plateform will create a Generic Opertor for all operators that are not supported or not yet implemented. Loading the model on Aidge plateform will create a Generic Opertor for all operators that are not supported or not yet implemented.
In order to support the HardMax Operator, we'll have to add it to the Aidge plateform. This requires going through three modules: In order to support the HardMax Operator, we'll have to add it to the Aidge plateform. This requires going through three modules:
- aidge_core - aidge_core
- aidge_backend - aidge_backend
- aidge_onnx - aidge_onnx
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### <ins>Aidge_core</ins> ### <ins>Aidge_core</ins>
[Aidge Core](https://eclipse-aidge.readthedocs.io/en/latest/source/API/Core/index.html) is the main API for the aidge plateform, it is the carcase where we define the objects that will be handled in the platform like operators, recipes, nodes ...<br> [Aidge Core](https://eclipse-aidge.readthedocs.io/en/latest/source/API/Core/index.html) is the main API for the aidge plateform, it is the carcase where we define the objects that will be handled in the platform like operators, recipes, nodes ...<br>
In this module we will need to add the operator class and its python binding: In this module we will need to add the operator class and its python binding:
- aidge_core/include/aidge/operator/Hardmax.hpp<br> - aidge_core/include/aidge/operator/Hardmax.hpp<br>
In this file, we need to define the class Hardmax_Op. What we need to keep in mind is that Hardmax_Op is operated on 1 input and it has 1 Int attribute (Axis) so the constructors of the class will be as follows: In this file, we need to define the class Hardmax_Op. What we need to keep in mind is that Hardmax_Op is operated on 1 input and it has 1 Int attribute (Axis) so the constructors of the class will be as follows:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!tail -n +11 Core_files/Hardmax.hpp !tail -n +11 Core_files/Hardmax.hpp
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
After creating the header, add it in aidge_core/include/aidge/aidge.hpp<br> After creating the header, add it in aidge_core/include/aidge/aidge.hpp<br>
We also need to add the cpp file where we define the Type attribute and we override methods if needed like computeOutputDims() if the output doesn't have the same shape as the input. <br> We also need to add the cpp file where we define the Type attribute and we override methods if needed like computeOutputDims() if the output doesn't have the same shape as the input. <br>
- aidge_core/src/operator/Hardmax.cpp<br> - aidge_core/src/operator/Hardmax.cpp<br>
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!tail -n +11 Core_files/Hardmax.cpp !tail -n +11 Core_files/Hardmax.cpp
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
- aidge_core/python_binding/operator/pybind_Hardmax.cpp<br> - aidge_core/python_binding/operator/pybind_Hardmax.cpp<br>
In this file, we need to define the init_Harmax function as follows: In this file, we need to define the init_Harmax function as follows:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!tail -n +11 Core_files/pybind_Hardmax.cpp !tail -n +11 Core_files/pybind_Hardmax.cpp
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Make sure to add all attributes as arguments for m.def() for Hardmax we only have "axis".<br> Make sure to add all attributes as arguments for m.def() for Hardmax we only have "axis".<br>
Finally update aidge_core/python_binding/pybind_core.cpp Finally update aidge_core/python_binding/pybind_core.cpp
```C++ ```C++
// add the init of the operator // add the init of the operator
void init_Hardmax(py::module&); void init_Hardmax(py::module&);
// also inside the function init_Aidge(py::module& m) // also inside the function init_Aidge(py::module& m)
init_Hardmax(m); init_Hardmax(m);
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### <ins>Aidge_backend</ins> ### <ins>Aidge_backend</ins>
[Aidge_backend](https://eclipse-aidge.readthedocs.io/en/latest/source/API/BackendCPU/index.html) is the module where we implement what the object created in Aidge_core actually do. In the case of Operators, the main thing we will add in this module is the kernel. There are currently two backends available: Aidge_backend_cpu and Aidge_backend_gpu. In this tutorial we will go through the cpu backend.<br> [Aidge_backend](https://eclipse-aidge.readthedocs.io/en/latest/source/API/BackendCPU/index.html) is the module where we implement what the object created in Aidge_core actually do. In the case of Operators, the main thing we will add in this module is the kernel. There are currently two backends available: Aidge_backend_cpu and Aidge_backend_gpu. In this tutorial we will go through the cpu backend.<br>
The files we need to add in this module in order to implement Hardmax Operator are:<br> The files we need to add in this module in order to implement Hardmax Operator are:<br>
- aidge_backend_cpu/include/aidge/backend/cpu/operator/HardmaxImpl.hpp<br> - aidge_backend_cpu/include/aidge/backend/cpu/operator/HardmaxImpl.hpp<br>
In this file, we start by declaring the forward and backward kernel registry In this file, we start by declaring the forward and backward kernel registry
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!tail -n +11 Backend_cpu_files/HardmaxImpl.hpp !tail -n +11 Backend_cpu_files/HardmaxImpl.hpp
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Make sure to include the header in aidge_backend_cpu/include/aidge/backend/cpu.hpp<br> Make sure to include the header in aidge_backend_cpu/include/aidge/backend/cpu.hpp<br>
- aidge_backend_cpu/include/aidge/backend/cpu/operator/HardmaxImpl_forward_kernels.hpp<br> - aidge_backend_cpu/include/aidge/backend/cpu/operator/HardmaxImpl_forward_kernels.hpp<br>
This is the main file of the backend, in here we will code the main function of the operator This is the main file of the backend, in here we will code the main function of the operator
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!tail -n +11 Backend_cpu_files/HardmaxImpl_forward_kernels.hpp !tail -n +11 Backend_cpu_files/HardmaxImpl_forward_kernels.hpp
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
- aidge_backend_cpu/src/operator/HardmaxImpl.cpp<br> - aidge_backend_cpu/src/operator/HardmaxImpl.cpp<br>
The last file to add is the cpp for the implementation in which we call the kernal with the right arguments The last file to add is the cpp for the implementation in which we call the kernal with the right arguments
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!tail -n +11 Backend_cpu_files/HardmaxImpl.cpp !tail -n +11 Backend_cpu_files/HardmaxImpl.cpp
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
It is also a good practice to add unit tests for the operator under ``aidge_backend_cpu/unit_tests/operator``, you can find an example of operator unit test [here](https://gitlab.eclipse.org/eclipse/aidge/aidge_backend_cpu/-/blob/dev/unit_tests/operator/Test_PowImpl.cpp?ref_type=heads). It is also a good practice to add unit tests for the operator under ``aidge_backend_cpu/unit_tests/operator``, you can find an example of operator unit test [here](https://gitlab.eclipse.org/eclipse/aidge/aidge_backend_cpu/-/blob/dev/unit_tests/operator/Test_PowImpl.cpp?ref_type=heads).
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### <ins>Aidge_onnx</ins> ### <ins>Aidge_onnx</ins>
[Aidge Onnx](https://eclipse-aidge.readthedocs.io/en/latest/source/API/Onnx/index.html) is the API with the library ONNX. In this module we mainly handle imports and exports from and to onnx.<br> [Aidge Onnx](https://eclipse-aidge.readthedocs.io/en/latest/source/API/Onnx/index.html) is the API with the library ONNX. In this module we mainly handle imports and exports from and to onnx.<br>
What we need to do for adding the HardMax Operator is to add two files: What we need to do for adding the HardMax Operator is to add two files:
- aidge_onnx/aidge_onnx/node_import/onnx_converters/hardmax.py<br> - aidge_onnx/aidge_onnx/node_import/onnx_converters/hardmax.py<br>
In this file we need to write the function that converts an onnx_node into an aidge_node.<br> In this file we need to write the function that converts an onnx_node into an aidge_node.<br>
What differs in imorting from one operator to another are mainly the attributes as shown in the [onnx_doc](https://onnx.ai/onnx/operators/onnx__Hardmax.html#l-onnx-doc-hardmax), the hardmax operator has one attribute which is the axis so we need to get this attributes in order to create the aidge_core.Hardmax node with the correct value of Axis.<br> What differs in imorting from one operator to another are mainly the attributes as shown in the [onnx_doc](https://onnx.ai/onnx/operators/onnx__Hardmax.html#l-onnx-doc-hardmax), the hardmax operator has one attribute which is the axis so we need to get this attributes in order to create the aidge_core.Hardmax node with the correct value of Axis.<br>
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!tail -n +11 Onnx_files/import_converter/hardmax.py !tail -n +11 Onnx_files/import_converter/hardmax.py
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
- aidge_onnx/aidge_onnx/node_export/aidge_converters/hardmax.py<br> - aidge_onnx/aidge_onnx/node_export/aidge_converters/hardmax.py<br>
In this file we need to write the function that converts an aidge node into an onnx node.<br> In this file we need to write the function that converts an aidge node into an onnx node.<br>
In the export we also need to handle the attrbutes when converting. Therefore we call helper.make_attribute() to create the axis attribute and append it to the node with onnx_node.attribute.append() In the export we also need to handle the attrbutes when converting. Therefore we call helper.make_attribute() to create the axis attribute and append it to the node with onnx_node.attribute.append()
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!tail -n +11 Onnx_files/export_converter/hardmax.py !tail -n +11 Onnx_files/export_converter/hardmax.py
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### <ins>Building the project</ins> ### <ins>Building the project</ins>
Follow the [tutorial](https://eclipse-aidge.readthedocs.io/en/latest/source/GetStarted/install.html#build-on-linux) to build the three modified modules.<br> Follow the [tutorial](https://eclipse-aidge.readthedocs.io/en/latest/source/GetStarted/install.html#build-on-linux) to build the three modified modules.<br>
``` ```
$ pip install ./aidge_core $ pip install ./aidge_core
$ pip install ./aidge_backend_cpu $ pip install ./aidge_backend_cpu
$ pip install ./aidge_onnx $ pip install ./aidge_onnx
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### <ins>Test and Validation</ins> ### <ins>Test and Validation</ins>
Now we can test importing the operator on aidge and run an inference using the scheduler. Now we can test importing the operator on aidge and run an inference using the scheduler.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import aidge_core import aidge_core
import aidge_backend_cpu import aidge_backend_cpu
import aidge_onnx import aidge_onnx
aidge_model = aidge_onnx.load_onnx('hardmax_model.onnx') aidge_model = aidge_onnx.load_onnx('hardmax_model.onnx')
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Now that we've added the HardMax Operator, loading the model no longer creates Generic Operator.<br> Now that we've added the HardMax Operator, loading the model no longer creates Generic Operator.<br>
We can even run inference to check if there are no problems in the implementation of the operator. We can even run inference to check if there are no problems in the implementation of the operator.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import numpy as np import numpy as np
# Setup input # Setup input
input_tensor = aidge_core.Tensor(np.random.randn(1, 10).astype(np.float32)) input_tensor = aidge_core.Tensor(np.random.randn(1, 10).astype(np.float32))
# Setup model's features: data type, backend and input dimensions # Setup model's features: data type, backend and input dimensions
aidge_model.compile("cpu", aidge_core.DataType.Float32, dims=[[1,10]]) aidge_model.compile("cpu", aidge_core.dtype.float32, dims=[[1,10]])
# Set up the scheduler # Set up the scheduler
scheduler = aidge_core.SequentialScheduler(aidge_model) scheduler = aidge_core.SequentialScheduler(aidge_model)
# Run inference ! # Run inference !
scheduler.forward(data = [input_tensor]) # provide an input scheduler.forward(data = [input_tensor]) # provide an input
# Check results # Check results
print("Input: {}".format(input)) print("Input: {}".format(input))
for outNode in aidge_model.get_output_nodes(): for outNode in aidge_model.get_output_nodes():
output_aidge = np.array(outNode.get_operator().get_output(0)) output_aidge = np.array(outNode.get_operator().get_output(0))
print("Aidge output: {}".format(output_aidge)) print("Aidge output: {}".format(output_aidge))
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
We can also compare the output of the inference of the model with onnx's runtime inference to make sure the implementation is correct. We can also compare the output of the inference of the model with onnx's runtime inference to make sure the implementation is correct.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import onnxruntime import onnxruntime
import numpy as np import numpy as np
# Load the ONNX model # Load the ONNX model
onnx_model_path = 'hardmax_model.onnx' onnx_model_path = 'hardmax_model.onnx'
session = onnxruntime.InferenceSession(onnx_model_path) session = onnxruntime.InferenceSession(onnx_model_path)
# Run inference # Run inference
output = session.run(['output'], {'input': input}) output = session.run(['output'], {'input': input})
# Display the result # Display the result
print("ONNX Output: {}".format(output[0])) print("ONNX Output: {}".format(output[0]))
``` ```
......
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Training a simple Neural Network # Training a simple Neural Network
This tutorial introduces the basics of learning with the framework Aidge. This tutorial introduces the basics of learning with the framework Aidge.
What you will learn: What you will learn:
1. creating a model using Aidge API 1. creating a model using Aidge API
2. create and import a dataset 2. create and import a dataset
3. train a model 3. train a model
The folowinfg modules will be required: The folowinfg modules will be required:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import aidge_core import aidge_core
import aidge_backend_cpu import aidge_backend_cpu
import aidge_learning import aidge_learning
import numpy as np import numpy as np
# required to load CIFAR10 dataset # required to load CIFAR10 dataset
import torchvision import torchvision
import torchvision.transforms as transforms import torchvision.transforms as transforms
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Creating Aidge model ## Creating Aidge model
In this example, we will create a simple perceptron model. For this we will use the helper function ``sequential`` which will connect the nodes in a sequential manner and will return the reuslting ``GraphView``. In this example, we will create a simple perceptron model. For this we will use the helper function ``sequential`` which will connect the nodes in a sequential manner and will return the reuslting ``GraphView``.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
model = aidge_core.sequential([ model = aidge_core.sequential([
aidge_core.FC(in_channels=32*32*3, out_channels=512), aidge_core.FC(in_channels=32*32*3, out_channels=512),
aidge_core.ReLU(), aidge_core.ReLU(),
aidge_core.FC(in_channels=512, out_channels=256), aidge_core.FC(in_channels=512, out_channels=256),
aidge_core.ReLU(), aidge_core.ReLU(),
aidge_core.FC(in_channels=256, out_channels=128), aidge_core.FC(in_channels=256, out_channels=128),
aidge_core.ReLU(), aidge_core.ReLU(),
aidge_core.FC(in_channels=128, out_channels=10), aidge_core.FC(in_channels=128, out_channels=10),
]) ])
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Once the model is created, we can set its backend, datatype and initialize the values of the parameters. Once the model is created, we can set its backend, datatype and initialize the values of the parameters.
We will initialize ``FC`` weights with the He filler and set all biases to ``0.01``. We will initialize ``FC`` weights with the He filler and set all biases to ``0.01``.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Set backend and datatype # Set backend and datatype
model.set_backend("cpu") model.set_backend("cpu")
model.set_datatype(aidge_core.DataType.Float32) model.set_datatype(aidge_core.dtype.float32)
# Initialize parameters (weights and biases) # Initialize parameters (weights and biases)
for node in model.get_nodes(): for node in model.get_nodes():
if node.type() == "Producer": if node.type() == "Producer":
prod_op = node.get_operator() prod_op = node.get_operator()
value = prod_op.get_output(0) value = prod_op.get_output(0)
tuple_out = node.output(0)[0] tuple_out = node.output(0)[0]
# No conv in current network # No conv in current network
if tuple_out[0].type() == "Conv" and tuple_out[1]==1: if tuple_out[0].type() == "Conv" and tuple_out[1]==1:
# Conv weight # Conv weight
aidge_core.xavier_uniform_filler(value) aidge_core.xavier_uniform_filler(value)
elif tuple_out[0].type() == "Conv" and tuple_out[1]==2: elif tuple_out[0].type() == "Conv" and tuple_out[1]==2:
# Conv bias # Conv bias
aidge_core.constant_filler(value, 0.01) aidge_core.constant_filler(value, 0.01)
elif tuple_out[0].type() == "FC" and tuple_out[1]==1: elif tuple_out[0].type() == "FC" and tuple_out[1]==1:
# FC weight # FC weight
aidge_core.he_filler(value) aidge_core.he_filler(value)
elif tuple_out[0].type() == "FC" and tuple_out[1]==2: elif tuple_out[0].type() == "FC" and tuple_out[1]==2:
# FC bias # FC bias
aidge_core.constant_filler(value, 0.01) aidge_core.constant_filler(value, 0.01)
else: else:
pass pass
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
**Note:** We could have initialized producers using graph matching. **Note:** We could have initialized producers using graph matching.
## Aidge database ## Aidge database
Now that the model is ready we need to prepare a database. For this we will use the possiiblity to create a custom database using ``aidge_core.Database``. Now that the model is ready we need to prepare a database. For this we will use the possiiblity to create a custom database using ``aidge_core.Database``.
We will use the framework PyTorch to load CIFAR10 and then write a custom database to transform the tensors to one hot encoded Aidge tensor. We will use the framework PyTorch to load CIFAR10 and then write a custom database to transform the tensors to one hot encoded Aidge tensor.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
def one_hot_encoding(cls, nb_cls): def one_hot_encoding(cls, nb_cls):
values = [float(0.0)] * nb_cls values = [float(0.0)] * nb_cls
values[cls] = float(1.0) values[cls] = float(1.0)
t = aidge_core.Tensor(values) t = aidge_core.Tensor(values)
t.set_datatype(aidge_core.DataType.Float32) t.set_datatype(aidge_core.dtype.float32)
return t return t
class aidge_cifar10(aidge_core.Database): class aidge_cifar10(aidge_core.Database):
def __init__(self): def __init__(self):
aidge_core.Database.__init__(self) aidge_core.Database.__init__(self)
transform = transforms.Compose( transform = transforms.Compose(
[transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
self.trainset = torchvision.datasets.CIFAR10(root='./data', train=True, self.trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform) download=True, transform=transform)
def get_item(self, idx): def get_item(self, idx):
data, label = self.trainset.__getitem__(idx) data, label = self.trainset.__getitem__(idx)
return [aidge_core.Tensor(data.numpy()), return [aidge_core.Tensor(data.numpy()),
one_hot_encoding(label, 10)] one_hot_encoding(label, 10)]
def len(self): def len(self):
return len(self.trainset) return len(self.trainset)
def get_nb_modalities(self): def get_nb_modalities(self):
return 2 return 2
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Using this, we can now create a dataprovider that we will use to send data to the network. Using this, we can now create a dataprovider that we will use to send data to the network.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
aidge_database = aidge_cifar10() aidge_database = aidge_cifar10()
BATCH_SIZE = 64 BATCH_SIZE = 64
aidge_dataprovider = aidge_core.DataProvider(aidge_database, aidge_dataprovider = aidge_core.DataProvider(aidge_database,
batch_size=BATCH_SIZE, batch_size=BATCH_SIZE,
shuffle=True, shuffle=True,
drop_last=True) drop_last=True)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Set up learning objects ## Set up learning objects
We now have all the basic elements required to run the leanring. We just need to setup the object specific to the learning and we will be able to write our first training loop ! We now have all the basic elements required to run the leanring. We just need to setup the object specific to the learning and we will be able to write our first training loop !
For propagation and backpropagation, Aidge use scheduler obejct, we will use the ``SequentialScheduler``. For propagation and backpropagation, Aidge use scheduler obejct, we will use the ``SequentialScheduler``.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Set object for learning # Set object for learning
scheduler = aidge_core.SequentialScheduler(model) scheduler = aidge_core.SequentialScheduler(model)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
To update weights, we will use an optimizer, in this case SGD. To update weights, we will use an optimizer, in this case SGD.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# setup optimizer # setup optimizer
opt = aidge_learning.SGD() opt = aidge_learning.SGD()
learning_rates = aidge_learning.constant_lr(0.01) learning_rates = aidge_learning.constant_lr(0.01)
opt.set_learning_rate_scheduler(learning_rates) opt.set_learning_rate_scheduler(learning_rates)
opt.set_parameters(list(aidge_core.producers(model))) opt.set_parameters(list(aidge_core.producers(model)))
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Training loop ## Training loop
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
tot_acc = 0 tot_acc = 0
for i, (input, label) in enumerate(aidge_dataprovider): for i, (input, label) in enumerate(aidge_dataprovider):
input.init_grad() # input.init_grad()
scheduler.forward(data=[input]) scheduler.forward(data=[input])
# Really long line should be a faster way ... # Really long line should be a faster way ...
pred = list(model.get_output_nodes())[0].get_operator().get_output(0) pred = list(model.get_output_nodes())[0].get_operator().get_output(0)
opt.reset_grad() opt.reset_grad()
loss = aidge_learning.loss.MSE(pred, label) loss = aidge_learning.loss.MSE(pred, label)
acc = np.sum(np.argmax(pred, axis=1) == np.argmax(label, axis=1)) acc = np.sum(np.argmax(pred, axis=1) == np.argmax(label, axis=1))
tot_acc += acc tot_acc += acc
scheduler.backward() scheduler.backward()
opt.update() opt.update()
print(f"Nb samples {(i+1)*BATCH_SIZE}, loss: {loss[0]}, acc:{(acc/BATCH_SIZE)*100}%, tot_acc:{(tot_acc/((i+1)*BATCH_SIZE))*100}%") print(f"Nb samples {(i+1)*BATCH_SIZE}, loss: {loss[0]}, acc:{(acc/BATCH_SIZE)*100}%, tot_acc:{(tot_acc/((i+1)*BATCH_SIZE))*100}%")
``` ```
......
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Aidge ONNX tutorial # Aidge ONNX tutorial
In this tutorial, we will see how to extensivly use the aidge ONNX module. In this tutorial, we will see how to extensivly use the aidge ONNX module.
The following points will be covered: The following points will be covered:
- How to load an ONNX; - How to load an ONNX;
- How to add the support for an ONNX operator; - How to add the support for an ONNX operator;
- How to support an unsupported operator for export purposes; - How to support an unsupported operator for export purposes;
- How to add an implementation to an unknown operator. - How to add an implementation to an unknown operator.
For this tutorial, we will need the following libraries: For this tutorial, we will need the following libraries:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import aidge_core import aidge_core
import aidge_onnx import aidge_onnx
import aidge_backend_cpu # Required for Producer implementation import aidge_backend_cpu # Required for Producer implementation
import numpy as np # Required to load data import numpy as np # Required to load data
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Setting up the notebook ## Setting up the notebook
### Retrieve the onnx model ### Retrieve the onnx model
In order to run this tutorial, we will use a simple ONNX composed of a single operator ``Swish``. This operator is not supported by ONNX and is often decomposed in multiple operators. In order to run this tutorial, we will use a simple ONNX composed of a single operator ``Swish``. This operator is not supported by ONNX and is often decomposed in multiple operators.
Pull this onnx model using git-lfs : Pull this onnx model using git-lfs :
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
!pip install git-lfs !pip install git-lfs
!git lfs pull !git lfs pull
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Importing an ONNX ## Importing an ONNX
Importing an ONNX using Aidge is done using the function: [aidge_onnx.load_onnx()](https://eclipse-aidge.readthedocs.io/en/latest/source/API/Onnx/index.html#aidge_onnx.load_onnx). Importing an ONNX using Aidge is done using the function: [aidge_onnx.load_onnx()](https://eclipse-aidge.readthedocs.io/en/latest/source/API/Onnx/index.html#aidge_onnx.load_onnx).
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
graph = aidge_onnx.load_onnx("test_swish.onnx") graph = aidge_onnx.load_onnx("test_swish.onnx")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The swish operator is not supported and thus is loaded as a [GenericOperator](https://eclipse-aidge.readthedocs.io/en/latest/source/API/Core/operator.html#generic-operator). This mechanism allow to load unsupported graph into the framework. The swish operator is not supported and thus is loaded as a [GenericOperator](https://eclipse-aidge.readthedocs.io/en/latest/source/API/Core/operator.html#generic-operator). This mechanism allow to load unsupported graph into the framework.
The ``aidge_onnx`` library has a coverage report tools in order to check how well the graph loaded is supported: The ``aidge_onnx`` library has a coverage report tools in order to check how well the graph loaded is supported:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
if not aidge_onnx.has_native_coverage(graph): if not aidge_onnx.has_native_coverage(graph):
print("The graph is not fully supported by aidge !\n") print("The graph is not fully supported by aidge !\n")
aidge_onnx.native_coverage_report(graph) aidge_onnx.native_coverage_report(graph)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
However, this does not mean we cannot work with this graph ! However, this does not mean we cannot work with this graph !
## Working with generic operator ## Working with generic operator
Indeed, using the python library, we can work with GenericOperator ! Indeed, using the python library, we can work with GenericOperator !
For this we will begin with retrieving the operator: For this we will begin with retrieving the operator:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
swish_node = graph.get_nodes().pop() # get_nodes return a set swish_node = graph.get_nodes().pop() # get_nodes return a set
swish_op = swish_node.get_operator() # Retrieving operator from node swish_op = swish_node.get_operator() # Retrieving operator from node
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Computing output dimensions ### Computing output dimensions
In order to generate a scheduling, we need to specify how the operator will modify the data. This is required so that Aidge can propagate in/out dimensions. In order to generate a scheduling, we need to specify how the operator will modify the data. This is required so that Aidge can propagate in/out dimensions.
Generating a scheduling is necessary to generate an export or make inference in the framework. Generating a scheduling is necessary to generate an export or make inference in the framework.
We can set a function to compute the dims using the ``set_forward_dims()`` method. We can set a function to compute the dims using the ``set_forward_dims()`` method.
In our case, the swish function does not modify the dimensions so we can just send the same dimensiosn as the input. For this we will create an identity lambda function. In our case, the swish function does not modify the dimensions so we can just send the same dimensiosn as the input. For this we will create an identity lambda function.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
swish_op.set_forward_dims(lambda x: x) swish_op.set_forward_dims(lambda x: x)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Providing an implementation ### Providing an implementation
If we want to run an inference, we need to provide an implementation. Which means define the forward function. If we want to run an inference, we need to provide an implementation. Which means define the forward function.
The swish function is defined as: $swish(x)={{x}\over{1+e^{-\beta x}}}$. The swish function is defined as: $swish(x)={{x}\over{1+e^{-\beta x}}}$.
So we can create a simple implementation using the numpy library: So we can create a simple implementation using the numpy library:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
from functools import reduce from functools import reduce
class SwishImpl(aidge_core.OperatorImpl): # Inherit OperatorImpl to interface with Aidge ! class SwishImpl(aidge_core.OperatorImpl): # Inherit OperatorImpl to interface with Aidge !
def __init__(self, op: aidge_core.Operator): def __init__(self, op: aidge_core.Operator):
aidge_core.OperatorImpl.__init__(self, op, "swish") # Required to avoid type error with C++ binding ! aidge_core.OperatorImpl.__init__(self, op, "swish") # Required to avoid type error with C++ binding !
self.op = op # Reference to the Aidge operator to retrieve attributes, inputs, outputs .. self.op = op # Reference to the Aidge operator to retrieve attributes, inputs, outputs ..
def forward(self): def forward(self):
data_input = np.array(self.op.get_input(0)) data_input = np.array(self.op.get_input(0))
beta = np.array(self.op.get_attr("beta")) # Attribute name is the same as the one in the ONNX beta = np.array(self.op.get_attr("beta")) # Attribute name is the same as the one in the ONNX
output = (data_input / (1 + np.exp(-data_input*beta))) output = (data_input / (1 + np.exp(-data_input*beta)))
self.op.set_output(0, aidge_core.Tensor(output)) # setting operator output self.op.set_output(0, aidge_core.Tensor(output)) # setting operator output
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
This implementation can then be set using: This implementation can then be set using:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
swish_op.set_impl(SwishImpl(swish_op)) # Setting implementation swish_op.set_impl(SwishImpl(swish_op)) # Setting implementation
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Once this is done, we can run an inference. Once this is done, we can run an inference.
Let's first create an input: Let's first create an input:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
numpy_tensor = np.random.randn(1, 10).astype(np.float32) numpy_tensor = np.random.randn(1, 10).astype(np.float32)
in_tensor = aidge_core.Tensor(numpy_tensor) in_tensor = aidge_core.Tensor(numpy_tensor)
print(f"Random input:\n{numpy_tensor}") print(f"Random input:\n{numpy_tensor}")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Then we can create a scheduler and run the inference: Then we can create a scheduler and run the inference:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
graph.compile("cpu", aidge_core.DataType.Float32, dims=[[1,10]]) graph.compile("cpu", aidge_core.dtype.float32, dims=[[1,10]])
scheduler = aidge_core.SequentialScheduler(graph) scheduler = aidge_core.SequentialScheduler(graph)
scheduler.forward(data=[in_tensor]) scheduler.forward(data=[in_tensor])
for outNode in graph.get_output_nodes(): for outNode in graph.get_output_nodes():
output_aidge = np.array(outNode.get_operator().get_output(0)) output_aidge = np.array(outNode.get_operator().get_output(0))
print('Aidge prediction = ', output_aidge) print('Aidge prediction = ', output_aidge)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Updating ONNX import ## Updating ONNX import
We have seen how to handle GenericOperator in order to generate an export or run inference. However, this is not the only approach we can have to support an unsupported operator. We have seen how to handle GenericOperator in order to generate an export or run inference. However, this is not the only approach we can have to support an unsupported operator.
As stated above, the Swish function is the composition of an ``Exp``, ``Add`` and ``Div``. In this section, we will see how we can interact with the ``aidge_onnx`` library in order to add the support for new operators. This section will also showcase the use of MetaNodes. As stated above, the Swish function is the composition of an ``Exp``, ``Add`` and ``Div``. In this section, we will see how we can interact with the ``aidge_onnx`` library in order to add the support for new operators. This section will also showcase the use of MetaNodes.
### Creating a MetaNode ### Creating a MetaNode
The first step is to reproduce the swish operation using a MetaOperator. The first step is to reproduce the swish operation using a MetaOperator.
For this we will need to create a Producer Node for each constant: ``exp``, ``1`` and ``beta``. For this we will need to create a Producer Node for each constant: ``exp``, ``1`` and ``beta``.
Then define each function ``Exp``, ``Add`` and ``Div``. And then create a GraphView that we will embedded in a MetaOperator. Then define each function ``Exp``, ``Add`` and ``Div``. And then create a GraphView that we will embedded in a MetaOperator.
> Note: The swish computation graph begin with a branch split, so to have one input I use the operator Identity. > Note: The swish computation graph begin with a branch split, so to have one input I use the operator Identity.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
from math import exp from math import exp
def gen_swish_metaop(nb_chan, name): def gen_swish_metaop(nb_chan, name):
# Declaring constant values # Declaring constant values
e_prod = aidge_core.Producer(aidge_core.Tensor(np.array([exp(1)]*nb_chan, dtype=np.float32)), "exp") e_prod = aidge_core.Producer(aidge_core.Tensor(np.array([exp(1)]*nb_chan, dtype=np.float32)), "exp")
one_prod = aidge_core.Producer(aidge_core.Tensor(np.array([1]*nb_chan, dtype=np.float32)), "one") one_prod = aidge_core.Producer(aidge_core.Tensor(np.array([1]*nb_chan, dtype=np.float32)), "one")
beta = 0.1 beta = 0.1
beta_prod = aidge_core.Producer(aidge_core.Tensor(np.array([-beta]*nb_chan, dtype=np.float32)), "beta") beta_prod = aidge_core.Producer(aidge_core.Tensor(np.array([-beta]*nb_chan, dtype=np.float32)), "beta")
# Declaring operators # Declaring operators
mul_op = aidge_core.Mul(name=f"{name}_MUL") mul_op = aidge_core.Mul(name=f"{name}_MUL")
pow_op = aidge_core.Pow(name=f"{name}_POW") pow_op = aidge_core.Pow(name=f"{name}_POW")
add_op = aidge_core.Add(2, name=f"{name}_ADD") add_op = aidge_core.Add(2, name=f"{name}_ADD")
div_op = aidge_core.Div(name=f"{name}_DIV") div_op = aidge_core.Div(name=f"{name}_DIV")
input_op = aidge_core.Identity(f"{name}_Input") input_op = aidge_core.Identity(f"{name}_Input")
# Declaring Connectors # Declaring Connectors
x = aidge_core.Connector(input_op) x = aidge_core.Connector(input_op)
b = aidge_core.Connector(beta_prod) b = aidge_core.Connector(beta_prod)
e = aidge_core.Connector(e_prod) e = aidge_core.Connector(e_prod)
o = aidge_core.Connector(one_prod) o = aidge_core.Connector(one_prod)
# Graph creation using functionnal declaration # Graph creation using functionnal declaration
y = div_op(x, add_op(pow_op(e, mul_op(x, b)), o)) y = div_op(x, add_op(pow_op(e, mul_op(x, b)), o))
swish_micro_graph = aidge_core.generate_graph([y]) swish_micro_graph = aidge_core.generate_graph([y])
# Saving micrograph for visualisation # Saving micrograph for visualisation
swish_micro_graph.save("swish_micro") swish_micro_graph.save("swish_micro")
# Embedding GraphView in a MetaOperator # Embedding GraphView in a MetaOperator
swish_op = aidge_core.meta_operator( swish_op = aidge_core.meta_operator(
"Swish", "Swish",
swish_micro_graph, swish_micro_graph,
name name
) )
return swish_op return swish_op
# Testing swich metaop # Testing swich metaop
_ = gen_swish_metaop(10, "Test") _ = gen_swish_metaop(10, "Test")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
We can then visualize the micro graph of the macro operator swish using mermaid: We can then visualize the micro graph of the macro operator swish using mermaid:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import base64 import base64
from IPython.display import Image, display from IPython.display import Image, display
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
def visualize_mmd(path_to_mmd): def visualize_mmd(path_to_mmd):
with open(path_to_mmd, "r") as file_mmd: with open(path_to_mmd, "r") as file_mmd:
graph_mmd = file_mmd.read() graph_mmd = file_mmd.read()
graphbytes = graph_mmd.encode("ascii") graphbytes = graph_mmd.encode("ascii")
base64_bytes = base64.b64encode(graphbytes) base64_bytes = base64.b64encode(graphbytes)
base64_string = base64_bytes.decode("ascii") base64_string = base64_bytes.decode("ascii")
display(Image(url=f"https://mermaid.ink/img/{base64_string}")) display(Image(url=f"https://mermaid.ink/img/{base64_string}"))
visualize_mmd("swish_micro.mmd") visualize_mmd("swish_micro.mmd")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
We have successfully created a function which can create a MetaOperator for the Swish function ! We have successfully created a function which can create a MetaOperator for the Swish function !
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
We have successfully created a function which can create a MetaOperator for the Swish function ! We have successfully created a function which can create a MetaOperator for the Swish function !
The next step is to register this function so that it is called by the ONNX import library. The next step is to register this function so that it is called by the ONNX import library.
### Registering new node import ### Registering new node import
Registering a new node to the ONNX import library can be easily done using the decorator function ``@aidge_onnx.node_import.auto_register_import``. Registering a new node to the ONNX import library can be easily done using the decorator function ``@aidge_onnx.node_import.auto_register_import``.
This decorator will register the function to the dictionary of import function [aidge_onnx.node_converter.ONNX_NODE_CONVERTER_](https://eclipse-aidge.readthedocs.io/en/latest/source/API/Onnx/index.html#aidge_onnx.node_converter.ONNX_NODE_CONVERTER_). Note that the key you should use is the ONNX name of the operator in lowercase. This decorator will register the function to the dictionary of import function [aidge_onnx.node_converter.ONNX_NODE_CONVERTER_](https://eclipse-aidge.readthedocs.io/en/latest/source/API/Onnx/index.html#aidge_onnx.node_converter.ONNX_NODE_CONVERTER_). Note that the key you should use is the ONNX name of the operator in lowercase.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
NB_CHAN = 10 # TODO: Find a way to infer nb channel later ... NB_CHAN = 10 # TODO: Find a way to infer nb channel later ...
@aidge_onnx.node_import.auto_register_import("swish") @aidge_onnx.node_import.auto_register_import("swish")
def import_swish(onnx_node, input_nodes, opset=None): def import_swish(onnx_node, input_nodes, opset=None):
node_name = onnx_node.output[0] node_name = onnx_node.output[0]
return gen_swish_metaop(NB_CHAN, node_name) return gen_swish_metaop(NB_CHAN, node_name)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Once this is done, you can use ``aidge_onnx.node_import.supported_operators()`` and check that swish is part of the supported operators: Once this is done, you can use ``aidge_onnx.node_import.supported_operators()`` and check that swish is part of the supported operators:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
aidge_onnx.node_import.supported_operators() aidge_onnx.node_import.supported_operators()
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Since swish is supported we can load again the onnx: Since swish is supported we can load again the onnx:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
supported_graph = aidge_onnx.load_onnx("test_swish.onnx") supported_graph = aidge_onnx.load_onnx("test_swish.onnx")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Since we have decomposed the Swish operation in atomic operator supported by Aidge, we don't need to provide an implementation and instead we can just use the ``aidge_backend_cpu`` implementation to run an inference: Since we have decomposed the Swish operation in atomic operator supported by Aidge, we don't need to provide an implementation and instead we can just use the ``aidge_backend_cpu`` implementation to run an inference:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
data_input = aidge_core.Producer(aidge_core.Tensor(np.arange(NB_CHAN, dtype=np.float32)+1.0), "data") data_input = aidge_core.Producer(aidge_core.Tensor(np.arange(NB_CHAN, dtype=np.float32)+1.0), "data")
data_input.add_child(supported_graph) data_input.add_child(supported_graph)
supported_graph.add(data_input) supported_graph.add(data_input)
data_input.get_operator().set_datatype(aidge_core.DataType.Float32) data_input.get_operator().set_datatype(aidge_core.dtype.float32)
data_input.get_operator().set_backend("cpu") data_input.get_operator().set_backend("cpu")
supported_graph.set_datatype(aidge_core.DataType.Float32) supported_graph.set_datatype(aidge_core.dtype.float32)
supported_graph.set_backend("cpu") supported_graph.set_backend("cpu")
# Create SCHEDULER # Create SCHEDULER
scheduler = aidge_core.SequentialScheduler(supported_graph) scheduler = aidge_core.SequentialScheduler(supported_graph)
# Run inference ! # Run inference !
scheduler.forward() scheduler.forward()
for outNode in supported_graph.get_output_nodes(): for outNode in supported_graph.get_output_nodes():
output_aidge = np.array(outNode.get_operator().get_output(0)) output_aidge = np.array(outNode.get_operator().get_output(0))
print("MetaOperator output:") print("MetaOperator output:")
print(output_aidge) print(output_aidge)
x = np.arange(NB_CHAN, dtype=np.float32)+1.0 x = np.arange(NB_CHAN, dtype=np.float32)+1.0
beta = 0.1 beta = 0.1
print("Reference output:") print("Reference output:")
print(x / (1. + np.exp(-beta*x))) print(x / (1. + np.exp(-beta*x)))
``` ```
......
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Post Training Quantization with AIDGE ## Post Training Quantization with AIDGE
#### What is Network Quantization ? #### What is Network Quantization ?
Deploying large Neural Network architectures on embedded targets can be a difficult task as they often require billions of floating operations per inference. Deploying large Neural Network architectures on embedded targets can be a difficult task as they often require billions of floating operations per inference.
To address this problem, several techniques have been developed over the past decades in order to reduce the computational load and energy consumption of those inferences. Those techniques include Pruning, Compression, Quantization and Distillation. To address this problem, several techniques have been developed over the past decades in order to reduce the computational load and energy consumption of those inferences. Those techniques include Pruning, Compression, Quantization and Distillation.
In particular, Post Training Quantization (PTQ) consists in taking an already trained network, and replacing the costly floating-point MADD by their integer counterparts. The use of Bytes instead of Floats also leads to a smaller memory bandwidth. In particular, Post Training Quantization (PTQ) consists in taking an already trained network, and replacing the costly floating-point MADD by their integer counterparts. The use of Bytes instead of Floats also leads to a smaller memory bandwidth.
While this process can seem trivial, the naive approach consisting only in rounding the parameters and activations doesn't work in practice. Instead, we want to normalize the network in order to optimize the ranges of parameters and values propagated inside the network, before applying quantization. While this process can seem trivial, the naive approach consisting only in rounding the parameters and activations doesn't work in practice. Instead, we want to normalize the network in order to optimize the ranges of parameters and values propagated inside the network, before applying quantization.
#### The Quantization Pipeline #### The Quantization Pipeline
The PTQ algorithm consists in a 3 steps pipeline: The PTQ algorithm consists in a 3 steps pipeline:
- First we optimize the parameter ranges by propagating the scaling coefficients in the network. - First we optimize the parameter ranges by propagating the scaling coefficients in the network.
- Secondly, we compute the activation values over an input dataset, and insert the scaling nodes. - Secondly, we compute the activation values over an input dataset, and insert the scaling nodes.
- Finally, we quantize the network by reconfiguring the scaling nodes according to the desired precision. - Finally, we quantize the network by reconfiguring the scaling nodes according to the desired precision.
![alt text](./static/ptq_diagram.png) ![alt text](./static/ptq_diagram.png)
#### Doing the PTQ with AIDGE #### Doing the PTQ with AIDGE
This notebook shows how to perform PTQ of a Convolutional Network, trained on the MNIST dataset. This notebook shows how to perform PTQ of a Convolutional Network, trained on the MNIST dataset.
The tutorial is constructed as follows : The tutorial is constructed as follows :
- Setup of the AIDGE environment - Setup of the AIDGE environment
- Loading of the model and example inferences - Loading of the model and example inferences
- Evaluation of the trained model accuracy - Evaluation of the trained model accuracy
- Post Training Quantization and test inferences - Post Training Quantization and test inferences
- Evaluation of the quantized model accuracy - Evaluation of the quantized model accuracy
As we will observe in this notebook, we get zero degradation of the accuracy for a 8-bits PTQ. As we will observe in this notebook, we get zero degradation of the accuracy for a 8-bits PTQ.
Let's begin ! Let's begin !
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### (if needed) Download the model ### (if needed) Download the model
If you don't have git-lfs, you can download the model and data using this piece of code If you don't have git-lfs, you can download the model and data using this piece of code
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import os import os
import requests import requests
def download_material(path: str) -> None: def download_material(path: str) -> None:
if not os.path.isfile(path): if not os.path.isfile(path):
response = requests.get("https://gitlab.eclipse.org/eclipse/aidge/aidge/-/raw/dev/examples/tutorials/PTQ_tutorial/"+path+"?ref_type=heads") response = requests.get("https://gitlab.eclipse.org/eclipse/aidge/aidge/-/raw/dev/examples/tutorials/PTQ_tutorial/"+path+"?ref_type=heads")
if response.status_code == 200: if response.status_code == 200:
with open(path, 'wb') as f: with open(path, 'wb') as f:
f.write(response.content) f.write(response.content)
print("File downloaded successfully.") print("File downloaded successfully.")
else: else:
print("Failed to download file. Status code:", response.status_code) print("Failed to download file. Status code:", response.status_code)
# Download onnx model file # Download onnx model file
download_material("ConvNet.onnx") download_material("ConvNet.onnx")
# Download data sample # Download data sample
download_material("mnist_samples.npy.gz") download_material("mnist_samples.npy.gz")
# Download data label # Download data label
download_material("mnist_labels.npy.gz") download_material("mnist_labels.npy.gz")
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
#### Environment setup ... #### Environment setup ...
We need numpy for manipulating the inputs, matplotlib for visualization purposes, and gzip to uncompress the numpy dataset. We need numpy for manipulating the inputs, matplotlib for visualization purposes, and gzip to uncompress the numpy dataset.
Then we want to import the aidge modules : Then we want to import the aidge modules :
- the core module contains everything we need to manipulate the graph. - the core module contains everything we need to manipulate the graph.
- the backend module allows us to perform inferences using the CPU. - the backend module allows us to perform inferences using the CPU.
- the onnx module allows us to load the pretrained model (stored in an onnx file). - the onnx module allows us to load the pretrained model (stored in an onnx file).
- the quantization module encaplsulate the Post Training Quantization algorithm. - the quantization module encaplsulate the Post Training Quantization algorithm.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import gzip import gzip
import numpy as np import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import aidge_core import aidge_core
import aidge_onnx import aidge_onnx
import aidge_backend_cpu import aidge_backend_cpu
import aidge_quantization import aidge_quantization
print(" Available backends : ", aidge_core.Tensor.get_available_backends()) print(" Available backends : ", aidge_core.Tensor.get_available_backends())
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Then, let's define the configurations of this script ... Then, let's define the configurations of this script ...
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
NB_SAMPLES = 100 NB_SAMPLES = 100
NB_BITS = 8 NB_BITS = 8
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Now, let's load and visualize some samples ... Now, let's load and visualize some samples ...
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
samples = np.load(gzip.GzipFile('mnist_samples.npy.gz', "r")) samples = np.load(gzip.GzipFile('mnist_samples.npy.gz', "r"))
labels = np.load(gzip.GzipFile('mnist_labels.npy.gz', "r")) labels = np.load(gzip.GzipFile('mnist_labels.npy.gz', "r"))
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
for i in range(10): for i in range(10):
plt.subplot(1, 10, i + 1) plt.subplot(1, 10, i + 1)
plt.axis('off') plt.axis('off')
plt.tight_layout() plt.tight_layout()
plt.imshow(samples[i], cmap='gray') plt.imshow(samples[i], cmap='gray')
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
#### Importing the model in AIDGE ... #### Importing the model in AIDGE ...
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
aidge_model = aidge_onnx.load_onnx("ConvNet.onnx", verbose=False) aidge_model = aidge_onnx.load_onnx("ConvNet.onnx", verbose=False)
aidge_core.remove_flatten(aidge_model) # we want to get rid of the 'flatten' nodes ... aidge_core.remove_flatten(aidge_model) # we want to get rid of the 'flatten' nodes ...
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
#### Setting up the AIDGE scheduler ... #### Setting up the AIDGE scheduler ...
In order to perform inferences with AIDGE we need to setup a scheduler. But before doing so, we need to create a data producer node and connect it to the network. In order to perform inferences with AIDGE we need to setup a scheduler. But before doing so, we need to create a data producer node and connect it to the network.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Insert the input producer # Insert the input producer
input_node = aidge_core.Producer([1, 1, 28, 28], "XXX") input_node = aidge_core.Producer([1, 1, 28, 28], "XXX")
input_node.add_child(aidge_model) input_node.add_child(aidge_model)
aidge_model.add(input_node) aidge_model.add(input_node)
# Set up the backend # Set up the backend
aidge_model.set_datatype(aidge_core.DataType.Float32) aidge_model.set_datatype(aidge_core.dtype.float32)
aidge_model.set_backend("cpu") aidge_model.set_backend("cpu")
# Create the Scheduler # Create the Scheduler
scheduler = aidge_core.SequentialScheduler(aidge_model) scheduler = aidge_core.SequentialScheduler(aidge_model)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
#### Running some example inferences ... #### Running some example inferences ...
Now that the scheduler is ready, let's perform some inferences. To do so we first declare a utility function that will prepare and set our inputs, propagate them and retreive the outputs. Now that the scheduler is ready, let's perform some inferences. To do so we first declare a utility function that will prepare and set our inputs, propagate them and retreive the outputs.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
def propagate(model, scheduler, sample): def propagate(model, scheduler, sample):
# Setup the input # Setup the input
sample = np.reshape(sample, (1, 1, 28, 28)) sample = np.reshape(sample, (1, 1, 28, 28))
input_tensor = aidge_core.Tensor(sample) input_tensor = aidge_core.Tensor(sample)
input_node.get_operator().set_output(0, input_tensor) input_node.get_operator().set_output(0, input_tensor)
# Run the inference # Run the inference
scheduler.forward() scheduler.forward()
# Gather the results # Gather the results
output_node = model.get_output_nodes().pop() output_node = model.get_output_nodes().pop()
output_tensor = output_node.get_operator().get_output(0) output_tensor = output_node.get_operator().get_output(0)
return np.array(output_tensor) return np.array(output_tensor)
print('\n EXAMPLE INFERENCES :') print('\n EXAMPLE INFERENCES :')
for i in range(10): for i in range(10):
output_array = propagate(aidge_model, scheduler, samples[i]) output_array = propagate(aidge_model, scheduler, samples[i])
print(labels[i] , ' -> ', np.round(output_array, 2)) print(labels[i] , ' -> ', np.round(output_array, 2))
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
#### Computing the model accuracy ... #### Computing the model accuracy ...
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
def compute_accuracy(model, samples, labels): def compute_accuracy(model, samples, labels):
acc = 0 acc = 0
for i, x in enumerate(samples): for i, x in enumerate(samples):
y = propagate(model, scheduler, x) y = propagate(model, scheduler, x)
if labels[i] == np.argmax(y): if labels[i] == np.argmax(y):
acc += 1 acc += 1
return acc / len(samples) return acc / len(samples)
accuracy = compute_accuracy(aidge_model, samples[0:NB_SAMPLES], labels) accuracy = compute_accuracy(aidge_model, samples[0:NB_SAMPLES], labels)
print(f'\n MODEL ACCURACY : {accuracy * 100:.3f}%') print(f'\n MODEL ACCURACY : {accuracy * 100:.3f}%')
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
#### Quantization dataset creation ... #### Quantization dataset creation ...
We need to convert a subset of our Numpy samples into AIDGE tensors, so that they can be used to compute the activation ranges. We need to convert a subset of our Numpy samples into AIDGE tensors, so that they can be used to compute the activation ranges.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
tensors = [] tensors = []
for sample in samples[0:NB_SAMPLES]: for sample in samples[0:NB_SAMPLES]:
sample = np.reshape(sample, (1, 1, 28, 28)) sample = np.reshape(sample, (1, 1, 28, 28))
tensor = aidge_core.Tensor(sample) tensor = aidge_core.Tensor(sample)
tensors.append(tensor) tensors.append(tensor)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
#### Applying the PTQ to the model ... #### Applying the PTQ to the model ...
Now that everything is ready, we can call the PTQ routine ! Note that after the quantization we need to update the scheduler. Now that everything is ready, we can call the PTQ routine ! Note that after the quantization we need to update the scheduler.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
aidge_quantization.quantize_network(aidge_model, NB_BITS, tensors) aidge_quantization.quantize_network(aidge_model, NB_BITS, tensors)
scheduler = aidge_core.SequentialScheduler(aidge_model) scheduler = aidge_core.SequentialScheduler(aidge_model)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
#### Running some quantized inferences ... #### Running some quantized inferences ...
Now that our network is quantized, what about testing some inferences ? Let's do so, but before, we need not to forget that our 8-bit network expect 8-bit inputs ! We thus need to rescale the input tensors ... Now that our network is quantized, what about testing some inferences ? Let's do so, but before, we need not to forget that our 8-bit network expect 8-bit inputs ! We thus need to rescale the input tensors ...
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
scaling = 2**(NB_BITS-1)-1 scaling = 2**(NB_BITS-1)-1
for i in range(NB_SAMPLES): for i in range(NB_SAMPLES):
samples[i] = np.round(samples[i] * scaling) samples[i] = np.round(samples[i] * scaling)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
We can now perform our quantized inferences ... We can now perform our quantized inferences ...
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print('\n EXAMPLE QUANTIZED INFERENCES :') print('\n EXAMPLE QUANTIZED INFERENCES :')
for i in range(10): for i in range(10):
input_array = np.reshape(samples[i], (1, 1, 28, 28)) input_array = np.reshape(samples[i], (1, 1, 28, 28))
output_array = propagate(aidge_model, scheduler, input_array) output_array = propagate(aidge_model, scheduler, input_array)
print(labels[i] , ' -> ', np.round(output_array, 2)) print(labels[i] , ' -> ', np.round(output_array, 2))
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Computing the quantized accuracy ... ### Computing the quantized accuracy ...
Just as we've done for the initial network, we can compute the quantized model accuracy ... Just as we've done for the initial network, we can compute the quantized model accuracy ...
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
accuracy = compute_accuracy(aidge_model, samples[0:NB_SAMPLES], labels) accuracy = compute_accuracy(aidge_model, samples[0:NB_SAMPLES], labels)
print(f'\n QUANTIZED MODEL ACCURACY : {accuracy * 100:.3f}%') print(f'\n QUANTIZED MODEL ACCURACY : {accuracy * 100:.3f}%')
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
#### Work is done ! #### Work is done !
We see that a 8-bit PTQ does not affect the accuracy of our model ! This result shows that a proper quantization algorithm can be used to deploy a Neural Network on very small devices, where manipulating bytes is optimal. We encourage you to run this notebook again with even more aggressive quantization values ! We see that a 8-bit PTQ does not affect the accuracy of our model ! This result shows that a proper quantization algorithm can be used to deploy a Neural Network on very small devices, where manipulating bytes is optimal. We encourage you to run this notebook again with even more aggressive quantization values !
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
print('That\'s all folks !') print('That\'s all folks !')
``` ```
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment