Export a MNIST model to a CPP standalone project#

[1]:
%pip install requests numpy ipywidgets ipycanvas
Requirement already satisfied: requests in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (2.32.3)
Requirement already satisfied: numpy in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (2.1.0)
Collecting ipywidgets
  Downloading ipywidgets-8.1.5-py3-none-any.whl.metadata (2.3 kB)
Collecting ipycanvas
  Downloading ipycanvas-0.13.2-py2.py3-none-any.whl.metadata (6.3 kB)
Requirement already satisfied: charset-normalizer<4,>=2 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from requests) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from requests) (3.8)
Requirement already satisfied: urllib3<3,>=1.21.1 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from requests) (2.2.2)
Requirement already satisfied: certifi>=2017.4.17 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from requests) (2024.7.4)
Requirement already satisfied: comm>=0.1.3 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from ipywidgets) (0.2.2)
Requirement already satisfied: ipython>=6.1.0 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from ipywidgets) (8.26.0)
Requirement already satisfied: traitlets>=4.3.1 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from ipywidgets) (5.14.3)
Collecting widgetsnbextension~=4.0.12 (from ipywidgets)
  Downloading widgetsnbextension-4.0.13-py3-none-any.whl.metadata (1.6 kB)
Collecting jupyterlab-widgets~=3.0.12 (from ipywidgets)
  Downloading jupyterlab_widgets-3.0.13-py3-none-any.whl.metadata (4.1 kB)
Requirement already satisfied: pillow>=6.0 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from ipycanvas) (10.4.0)
Requirement already satisfied: decorator in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from ipython>=6.1.0->ipywidgets) (5.1.1)
Requirement already satisfied: jedi>=0.16 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from ipython>=6.1.0->ipywidgets) (0.19.1)
Requirement already satisfied: matplotlib-inline in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from ipython>=6.1.0->ipywidgets) (0.1.7)
Requirement already satisfied: prompt-toolkit<3.1.0,>=3.0.41 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from ipython>=6.1.0->ipywidgets) (3.0.47)
Requirement already satisfied: pygments>=2.4.0 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from ipython>=6.1.0->ipywidgets) (2.18.0)
Requirement already satisfied: stack-data in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from ipython>=6.1.0->ipywidgets) (0.6.3)
Requirement already satisfied: exceptiongroup in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from ipython>=6.1.0->ipywidgets) (1.2.2)
Requirement already satisfied: typing-extensions>=4.6 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from ipython>=6.1.0->ipywidgets) (4.12.2)
Requirement already satisfied: pexpect>4.3 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from ipython>=6.1.0->ipywidgets) (4.9.0)
Requirement already satisfied: parso<0.9.0,>=0.8.3 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from jedi>=0.16->ipython>=6.1.0->ipywidgets) (0.8.4)
Requirement already satisfied: ptyprocess>=0.5 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from pexpect>4.3->ipython>=6.1.0->ipywidgets) (0.7.0)
Requirement already satisfied: wcwidth in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from prompt-toolkit<3.1.0,>=3.0.41->ipython>=6.1.0->ipywidgets) (0.2.13)
Requirement already satisfied: executing>=1.2.0 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from stack-data->ipython>=6.1.0->ipywidgets) (2.0.1)
Requirement already satisfied: asttokens>=2.1.0 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from stack-data->ipython>=6.1.0->ipywidgets) (2.4.1)
Requirement already satisfied: pure-eval in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from stack-data->ipython>=6.1.0->ipywidgets) (0.2.3)
Requirement already satisfied: six>=1.12.0 in /builds/eclipse/aidge/aidge/venv/lib/python3.10/site-packages (from asttokens>=2.1.0->stack-data->ipython>=6.1.0->ipywidgets) (1.16.0)
Downloading ipywidgets-8.1.5-py3-none-any.whl (139 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 139.8/139.8 kB 5.5 MB/s eta 0:00:00
Downloading ipycanvas-0.13.2-py2.py3-none-any.whl (122 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 122.9/122.9 kB 11.2 MB/s eta 0:00:00
Downloading jupyterlab_widgets-3.0.13-py3-none-any.whl (214 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 214.4/214.4 kB 17.5 MB/s eta 0:00:00
Downloading widgetsnbextension-4.0.13-py3-none-any.whl (2.3 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.3/2.3 MB 56.7 MB/s eta 0:00:00
Installing collected packages: widgetsnbextension, jupyterlab-widgets, ipywidgets, ipycanvas
Successfully installed ipycanvas-0.13.2 ipywidgets-8.1.5 jupyterlab-widgets-3.0.13 widgetsnbextension-4.0.13

[notice] A new release of pip is available: 24.1 -> 24.2
[notice] To update, run: pip install --upgrade pip
Note: you may need to restart the kernel to use updated packages.

Download the model#

[2]:
import os
import requests
[3]:
# Download onnx file if it has not been done before
if not os.path.isfile("./lenet_mnist.onnx"):
    response = requests.get("https://huggingface.co/vtemplier/LeNet_MNIST/resolve/main/lenet_mnist.onnx?download=true")
    if response.status_code == 200:
        with open("lenet_mnist.onnx", 'wb') as f:
            f.write(response.content)
        print("ONNX model downloaded successfully.")
    else:
        print("Failed to download ONNX model. Status code:", response.status_code)
ONNX model downloaded successfully.

Load the model in Aidge and manipulate it#

[4]:
import aidge_core
import aidge_backend_cpu
import aidge_onnx
import aidge_export_cpp
[5]:
model = aidge_onnx.load_onnx("lenet_mnist.onnx")
- feature_feature_0_Conv_output_0 (Conv)
- feature_feature_1_Relu (Relu)
- feature_feature_2_MaxPool_output_0 (MaxPool)
- feature_feature_3_Conv_output_0 (Conv)
- feature_feature_4_Relu (Relu)
- feature_feature_5_MaxPool_output_0 (MaxPool)
- classifier_classifier_0_Flatten (Flatten | GenericOperator)
        - axis : 1
- classifier_classifier_1_Gemm_output_0 (Gemm)
- classifier_classifier_2_Relu (Relu)
- classifier_classifier_3_Gemm_output_0 (Gemm)
- classifier_classifier_4_Relu (Relu)
- output (Gemm)
[6]:
# Remove Flatten node, useless in the CPP export
aidge_core.remove_flatten(model)

# Freeze the model by setting constant to parameters producers
for node in model.get_nodes():
    if node.type() == "Producer":
        node.get_operator().attr.set_attr("constant", True)

# Create Producer Node for the Graph
input_node = aidge_core.Producer([1, 1, 28, 28], "input")
input_node.add_child(model)
model.add(input_node)

# Configuration for the model + forward dimensions
model.compile("cpu", aidge_core.dtype.float32)
[7]:
# Generate scheduling of the model
scheduler = aidge_core.SequentialScheduler(model)
scheduler.generate_scheduling()
[8]:
model.save("test")

Export the model#

[9]:
aidge_export_cpp.export("lenet_export_fp32", model, scheduler)

Draw your own number#

[10]:
from ipywidgets import HBox, VBox, Button, Layout
from ipycanvas import RoughCanvas, hold_canvas

img_name = "my_number.png"

canvas = RoughCanvas(width=28, height=28, sync_image_data=True)

button_gen = Button(description="Generate PNG")
button_clear = Button(description="Clear")

drawing = False
position = None
shape = []

def on_erase_button_clicked(b):
    canvas.clear()

def on_generate_button_clicked(b):
    try:
        canvas.to_file(img_name)
        print(f"Image generated to {img_name} !")
    except:
        print("Draw a number before generating the image.")

button_clear.on_click(on_erase_button_clicked)
button_gen.on_click(on_generate_button_clicked)

def on_mouse_down(x, y):
    global drawing
    global position
    global shape

    drawing = True
    position = (x, y)
    shape = [position]

def on_mouse_move(x, y):
    global drawing
    global position
    global shape

    if not drawing:
        return

    with hold_canvas():
        canvas.stroke_line(position[0], position[1], x, y)
        position = (x, y)

    shape.append(position)

def on_mouse_up(x, y):
    global drawing
    global position
    global shape

    drawing = False

    with hold_canvas():
        canvas.stroke_line(position[0], position[1], x, y)

    shape = []

canvas.on_mouse_down(on_mouse_down)
canvas.on_mouse_move(on_mouse_move)
canvas.on_mouse_up(on_mouse_up)

canvas.stroke_style = "#000000"

VBox((canvas, HBox((button_gen, button_clear))),
     layout=Layout(height='auto', width="300px"))
[10]:

Generate inputs for testing the model from your drawing#

[11]:
try:
    number_np = canvas.get_image_data()
    # We got a numpy array with the shape of (28,28,4)
    # Transform it to (28,28)
    x = number_np[:, :, 3].astype("float32")
    # Convert from [0, 255] to [0, 1] and export it
    aidge_export_cpp.generate_input_file(export_folder="lenet_export_fp32",
                                         array_name="inputs",
                                         array=x / 255)
except:
    print("Please draw a number in the previous cell before running this one.")
Please draw a number in the previous cell before running this one.

Compile the export and test it#

[12]:
!cd lenet_export_fp32 && make
make[1]: Entering directory '/builds/eclipse/aidge/aidge/docs/source/Tutorial/lenet_export_fp32'
g++  -O2 -Wall -Wextra -MMD -fopenmp -I. -I./dnn -I./dnn/include -I./dnn/layers -I./dnn/parameters -c dnn/src/forward.cpp -o build/./dnn/src/forward.o
g++  -O2 -Wall -Wextra -MMD -fopenmp -I. -I./dnn -I./dnn/include -I./dnn/layers -I./dnn/parameters -c main.cpp -o build/./main.o
main.cpp:4:10: fatal error: inputs.h: No such file or directory
    4 | #include "inputs.h"
      |          ^~~~~~~~~~
compilation terminated.
make[1]: *** [Makefile:23: build/./main.o] Error 1
make[1]: Leaving directory '/builds/eclipse/aidge/aidge/docs/source/Tutorial/lenet_export_fp32'
[13]:
!./lenet_export_fp32/bin/run_export
/usr/bin/sh: 1: ./lenet_export_fp32/bin/run_export: not found