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

Merge branch 'ui_parameters' into 'dev'

update according to core changes

See merge request !13
parents 3ad39df8 5fe1cc84
No related branches found
No related tags found
2 merge requests!14version 0.1.1,!13update according to core changes
......@@ -77,7 +77,7 @@ def export(export_folder_name, graphview, scheduler):
list_inputs_name = []
for node in graphview.get_nodes():
if node.type() == "Producer":
if not node.get_operator().get_attr("Constant"):
if not node.get_operator().attr.constant:
export_type = aidge_datatype2ctype(node.get_operator().get_output(0).dtype())
list_inputs_name.append((export_type, node.name()))
......
......@@ -71,7 +71,7 @@ class ProducerCPP(ExportNode):
def __init__(self, node):
super().__init__(node)
self.constant = self.operator.get_attr("Constant")
self.constant = self.operator.attr.constant
self.values = np.array(self.operator.get_output(0))
if len(self.values.shape) == 4:
......@@ -130,7 +130,7 @@ class ReLUCPP(ExportNode):
list_actions.append(generate_str(
str(ROOT / "templates" / "kernel_forward" / "activation_forward.jinja"),
name=self.name,
input_name=self.inputs[0].name(),
input_name=self.inputs[0].name() if self.inputs[0] else self.name + "_input",
output_name=self.name
))
return list_actions
......@@ -141,14 +141,17 @@ class ConvCPP(ExportNode):
def __init__(self, node):
super().__init__(node)
self.kernel = node.get_operator().get_attr("KernelDims")
self.stride = node.get_operator().get_attr("StrideDims")
self.dilation = node.get_operator().get_attr("DilationDims")
self.kernel = node.get_operator().attr.kernel_dims
self.stride = node.get_operator().attr.stride_dims
self.dilation = node.get_operator().attr.dilation_dims
# No padding with Conv
# Use PaddedConv to add padding attribute
self.padding = [0, 0]
self.nb_channels = node.get_operator().in_channels()
self.nb_outputs = node.get_operator().out_channels()
if len(self.inputs_dims[0]) == 4:
# if dims == [batch, nb_channels, height, width]
# transform to [nb_channels, height, width]
......@@ -193,7 +196,7 @@ class ConvCPP(ExportNode):
list_actions.append(generate_str(
str(ROOT / "templates" / "kernel_forward" / "convolution_forward.jinja"),
name=self.name,
input_name=self.inputs[0].name(),
input_name=self.inputs[0].name() if self.inputs[0] else self.name + "_input",
output_name=self.name,
weights_name=self.inputs[1].name(),
biases_name=self.inputs[2].name()
......@@ -208,11 +211,11 @@ class PaddedConvCPP(ConvCPP):
for n in self.operator.get_micro_graph().get_nodes():
if n.type() == "Pad":
self.padding = n.get_operator().get_attr("BeginEndBorders")
self.padding = n.get_operator().attr.begin_end_borders
if n.type() == "Conv":
self.kernel = n.get_operator().get_attr("KernelDims")
self.stride = n.get_operator().get_attr("StrideDims")
self.dilation = n.get_operator().get_attr("DilationDims")
self.kernel = n.get_operator().attr.kernel_dims
self.stride = n.get_operator().attr.stride_dims
self.dilation = n.get_operator().attr.dilation_dims
if len(self.inputs_dims[0]) == 4:
# if dims == [batch, nb_channels, height, width]
......@@ -253,8 +256,8 @@ class AddCPP(ExportNode):
list_actions.append(generate_str(
str(ROOT / "templates" / "kernel_forward" / "elemwise_forward.jinja"),
name=self.name,
inputs1_name=self.inputs[0].name(),
inputs2_name=self.inputs[1].name(),
inputs1_name=self.parents[0].name() if self.parents[0] else self.name + "_input1",
inputs2_name=self.parents[1].name() if self.parents[1] else self.name + "_input2",
output_name=self.name
))
return list_actions
......@@ -286,8 +289,8 @@ class SubCPP(ExportNode):
list_actions.append(generate_str(
str(ROOT / "templates" / "kernel_forward" / "elemwise_forward.jinja"),
name=self.name,
inputs1_name=self.inputs[0].name(),
inputs2_name=self.inputs[1].name(),
inputs1_name=self.inputs[0].name() if self.inputs[0] else self.name + "_input1",
inputs2_name=self.inputs[1].name() if self.inputs[1] else self.name + "_input2",
output_name=self.name
))
return list_actions
......@@ -297,8 +300,8 @@ class MaxPoolCPP(ExportNode):
def __init__(self, node):
super().__init__(node)
self.kernel = node.get_operator().get_attr("KernelDims")
self.stride = node.get_operator().get_attr("StrideDims")
self.kernel = node.get_operator().attr.kernel_dims
self.stride = node.get_operator().attr.stride_dims
# No padding with MaxPooling
# Use PaddedMaxPooling to add padding attribute
......@@ -344,7 +347,7 @@ class MaxPoolCPP(ExportNode):
list_actions.append(generate_str(
str(ROOT / "templates" / "kernel_forward" / "pooling_forward.jinja"),
name=self.name,
input_name=self.inputs[0].name(),
input_name=self.inputs[0].name() if self.inputs[0] else self.name + "_input",
output_name=self.name
))
return list_actions
......@@ -397,11 +400,10 @@ class FcCPP(ExportNode):
def forward(self, list_actions:list):
if not self.is_last:
list_actions.append(set_up_output(self.name, "float"))
list_actions.append(generate_str(
str(ROOT / "templates" / "kernel_forward" / "fullyconnected_forward.jinja"),
name=self.name,
inputs_name=self.inputs[0].name(),
inputs_name= self.inputs[0].name() if (self.inputs[0] is not None) else self.name + '_input',
weights_name=self.inputs[1].name(),
biases_name=self.inputs[2].name(),
outputs_name=self.name
......
......@@ -17,18 +17,18 @@ def numpy_dtype2ctype(dtype):
# Add more dtype mappings as needed
else:
raise ValueError(f"Unsupported {dtype} dtype")
def aidge_datatype2ctype(datatype):
if datatype == aidge_core.DataType.Int8:
if datatype == aidge_core.dtype.int8:
return "int8_t"
elif datatype == aidge_core.DataType.Int32:
elif datatype == aidge_core.dtype.int32:
return "int32_t"
elif datatype == aidge_core.DataType.Int64:
elif datatype == aidge_core.dtype.int64:
return "int64_t"
elif datatype == aidge_core.DataType.Float32:
elif datatype == aidge_core.dtype.float32:
return "float"
elif datatype == aidge_core.DataType.Float64:
elif datatype == aidge_core.dtype.float64:
return "double"
# Add more dtype mappings as needed
else:
......
%% Cell type:markdown id: tags:
# Add a custom operator in the CPP export
%% Cell type:markdown id: tags:
The main objective of this tutorial is to demonstrate the toolchain to **detect unsupported operators** and **add them** in an export module. <br>
For this tutorial, we use the CPP export module ``aidge_export_cpp`` to demonstrate the toolchain.
%% Cell type:markdown id: tags:
## Import Aidge
%% Cell type:code id: tags:
``` python
import aidge_core
import aidge_backend_cpu
import aidge_onnx
import numpy as np
import os
import requests
```
%% Cell type:markdown id: tags:
## Load ONNX model
%% Cell type:code id: tags:
``` python
# 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)
```
%% Cell type:code id: tags:
``` python
model = aidge_onnx.load_onnx("lenet_mnist.onnx")
```
%% Cell type:code id: tags:
``` python
# 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().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.DataType.Float32)
model.compile("cpu", aidge_core.dtype.float32)
```
%% Cell type:code id: tags:
``` python
# Generate scheduling of the model
scheduler = aidge_core.SequentialScheduler(model)
scheduler.generate_scheduling()
```
%% Cell type:markdown id: tags:
## Replace ReLU operators by Swish operators
Let's say you want to replace ReLU with another activation like Switch.
%% Cell type:code id: tags:
``` python
switch_id = 0 # ID for naming newly created Swich Operators
for node in scheduler.get_static_scheduling():
if node.type() == "ReLU":
print(f"{node.name()} will be replaced")
# Swich is not implemented by default in Aidge
# It is inserted in the current model as a GenericOperator that we will custom
node_swish = aidge_core.GenericOperator("Swish", nb_data=1, nb_param=0, nb_out=1, name=f"swish_{switch_id}")
node_swish.get_operator().add_attr("betas", [1.0]*node.get_operator().get_input(0).dims()[1])
aidge_core.GraphView.replace(set([node]), set([node_swish]))
switch_id+=1
```
%% Cell type:code id: tags:
``` python
import base64
from IPython.display import Image, display
import matplotlib.pyplot as plt
# function to vizualize .mmd files
def visualize_mmd(path_to_mmd):
with open(path_to_mmd, "r") as file_mmd:
graph_mmd = file_mmd.read()
graphbytes = graph_mmd.encode("utf-8")
base64_bytes = base64.b64encode(graphbytes)
base64_string = base64_bytes.decode("utf-8")
display(Image(url=f"https://mermaid.ink/img/{base64_string}"))
model.save("myModel")
visualize_mmd("myModel.mmd")
```
%% Cell type:markdown id: tags:
## Schedule the graph
%% Cell type:markdown id: tags:
Add an implementation for Swish. <br>
The implementation is required to perform a sequential scheduling.
%% Cell type:code id: tags:
``` python
class SwishImpl(aidge_core.OperatorImpl): # Inherit OperatorImpl to interface with Aidge !
def __init__(self, op: aidge_core.Operator):
aidge_core.OperatorImpl.__init__(self, op, 'cpu')
# no need to define forward() function in python as we do not intend to run a scheduler on the model
for node in model.get_nodes():
if node.type() == "Swish":
node.get_operator().set_forward_dims(lambda x: x) # to propagate dimensions in the model
node.get_operator().set_impl(SwishImpl(node.get_operator())) # Setting implementation
```
%% Cell type:code id: tags:
``` python
scheduler = aidge_core.SequentialScheduler(model)
model.forward_dims()
scheduler.generate_scheduling()
```
%% Cell type:markdown id: tags:
## Add Swish to the CPP export support
%% Cell type:code id: tags:
``` python
import aidge_export_cpp as cpp
cpp.supported_operators()
```
%% Cell type:code id: tags:
``` python
from aidge_export_cpp.operators import *
# To complete
@operator_register("Swish")
class SwishCPP(ExportNode):
def __init__(self, node):
super().__init__(node)
self.betas = self.operator.get_attr("betas")
self.betas: float = self.operator.get_attr("betas")
def export(self, export_folder:str, list_configs:list):
def export(self, export_folder: str, list_configs: list[str]) -> list[str]:
copyfile("for_export/swish_kernel.hpp",
f"{export_folder}/include/kernels/")
list_configs.append(f"layers/{self.name}.h")
generate_file(
f"{export_folder}/layers/{self.name}.h",
"for_export/swish_config.jinja",
name=self.name,
output_dims=self.outputs_dims[0]
)
return list_configs
def forward(self, list_actions:list):
def forward(self, list_actions:list[str]) -> list[str]:
if not self.is_last:
list_actions.append(set_up_output(self.name, "float"))
list_actions.append(generate_str(
"for_export/swish_forward.jinja",
name=self.name,
input_name=self.input.name(),
output_name=self.name
))
return list_actions
```
%% Cell type:code id: tags:
``` python
print(cpp.supported_operators())
```
%% Cell type:code id: tags:
``` python
cpp.export("myexport", model, scheduler)
```
%% Cell type:code id: tags:
``` python
!tree myexport
```
%% Cell type:code id: tags:
``` python
digit = np.load("digit.npy")
cpp.generate_input_file("inputs", digit.reshape(-1), "myexport/inputs.h")
cpp.generate_input_file(array_name="inputs", array=digit.reshape(-1), folder_path="myexport")
```
%% Cell type:code id: tags:
``` python
!cd myexport && make
```
%% Cell type:code id: tags:
``` python
!./myexport/bin/run_export
```
......
%% Cell type:markdown id: tags:
# Export a MNIST model to a CPP standalone project
%% Cell type:code id: tags:
``` python
%pip install requests numpy ipywidgets ipycanvas
```
%% Cell type:markdown id: tags:
## Download the model
%% Cell type:code id: tags:
``` python
import os
import requests
```
%% Cell type:code id: tags:
``` python
# 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)
```
%% Cell type:markdown id: tags:
## Load the model in Aidge and manipulate it
%% Cell type:code id: tags:
``` python
import aidge_core
import aidge_backend_cpu
import aidge_onnx
import aidge_export_cpp
```
%% Cell type:code id: tags:
``` python
model = aidge_onnx.load_onnx("lenet_mnist.onnx")
```
%% Cell type:code id: tags:
``` python
# 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().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.DataType.Float32)
model.compile("cpu", aidge_core.dtype.float32)
```
%% Cell type:code id: tags:
``` python
# Generate scheduling of the model
scheduler = aidge_core.SequentialScheduler(model)
scheduler.generate_scheduling()
```
%% Cell type:code id: tags:
``` python
model.save("test")
```
%% Cell type:markdown id: tags:
## Export the model
%% Cell type:code id: tags:
``` python
aidge_export_cpp.export("lenet_export_fp32", model, scheduler)
```
%% Cell type:markdown id: tags:
### Draw your own number
%% Cell type:code id: tags:
``` python
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"))
```
%% Cell type:markdown id: tags:
### Generate inputs for testing the model from your drawing
%% Cell type:code id: tags:
``` python
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.")
```
%% Cell type:markdown id: tags:
### Compile the export and test it
%% Cell type:code id: tags:
``` python
!cd lenet_export_fp32 && make
```
%% Cell type:code id: tags:
``` python
!./lenet_export_fp32/bin/run_export
```
......
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