From 156b2873425352e4edfb4b2c10f22be8584dd3cb Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 07:40:07 +0000 Subject: [PATCH 01/19] [Fix](Pool) Missing kernel dimension --- aidge_export_cpp/operators/CppPool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aidge_export_cpp/operators/CppPool.py b/aidge_export_cpp/operators/CppPool.py index c2fb048..71f21e7 100644 --- a/aidge_export_cpp/operators/CppPool.py +++ b/aidge_export_cpp/operators/CppPool.py @@ -23,7 +23,7 @@ class CppPool(ExportNodeCpp): self.attributes["padding"] = n.get_operator().attr.begin_end_borders elif n.type() == "GlobalAveragePooling": self.attributes["pool_type"] = "Average" - self.attributes["kernel_dims"] = self.attributes["in_width"][0] + self.attributes["kernel_dims"] = [self.attributes["in_width"][0], self.attributes["in_height"][0]] elif n.type() == "MaxPooling2D": self.attributes["pool_type"] = "Max" self.attributes["kernel_dims"] = n.get_operator().attr.kernel_dims -- GitLab From 13cbf0517616fd1b388ae8da525d318abec9cf70 Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 07:46:11 +0000 Subject: [PATCH 02/19] [Fix] Rounding integration for int data type --- aidge_export_cpp/kernels/pooling.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/aidge_export_cpp/kernels/pooling.hpp b/aidge_export_cpp/kernels/pooling.hpp index 478b6a5..28ed5f6 100644 --- a/aidge_export_cpp/kernels/pooling.hpp +++ b/aidge_export_cpp/kernels/pooling.hpp @@ -113,7 +113,12 @@ void pooling_forward( } } + if constexpr (std::is_integral<Output_T>::value) { + outputs[oOffset + output] = (Output_T) std::round((float)sum / (POOL_HEIGHT * POOL_WIDTH)); + } else { outputs[oOffset + output] = (Output_T) (sum / (POOL_HEIGHT * POOL_WIDTH)); + } + } else { throw std::runtime_error("The export only supports Max and Average pooling."); -- GitLab From 8c1b047789032f52c9a6e530b793f0b8244077a6 Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 07:49:11 +0000 Subject: [PATCH 03/19] [Fix] Naming change and normalize function integration --- aidge_export_cpp/export_utils.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/aidge_export_cpp/export_utils.py b/aidge_export_cpp/export_utils.py index 7b7477d..da1d398 100644 --- a/aidge_export_cpp/export_utils.py +++ b/aidge_export_cpp/export_utils.py @@ -174,3 +174,11 @@ def exclude_unwanted_producers(model): if node_type in children_nodes: node.attributes().ignore = True break + +def normalize(array): + """ + Normalize an input image between -1 and 1 + """ + array = (array - array.min()) / (array.max() - array.min()) + return 2 * array - 1 + -- GitLab From a4f914cd0861436b4ad078bdbf3c2a623f558434 Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 07:50:24 +0000 Subject: [PATCH 04/19] [Feat] inputs_tensors use for main cpp generation --- aidge_export_cpp/export.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aidge_export_cpp/export.py b/aidge_export_cpp/export.py index 58777f8..43de6e5 100644 --- a/aidge_export_cpp/export.py +++ b/aidge_export_cpp/export.py @@ -16,7 +16,7 @@ def export(export_folder_name: str, graphview: aidge_core.GraphView, scheduler: Union[List[aidge_core.Node], aidge_core.Scheduler], - input_tensor: aidge_core.Tensor = None, # Coming Soon + inputs_tensor: aidge_core.Tensor = None, labels: aidge_core.Tensor = None, dev_mode: bool = False, aidge_cmp: bool = False): @@ -67,7 +67,7 @@ def export(export_folder_name: str, dev_mode=dev_mode) # Generate main file - generate_main_cpp(export_folder_name, graphview, labels=labels) + generate_main_cpp(export_folder_name, graphview, labels=labels, inputs_tensor=inputs_tensor) # Generate log files (aidge_cmp option) """ -- GitLab From 539a5c99993e357d236ebc86cff8a3cc1a08ba17 Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 07:52:45 +0000 Subject: [PATCH 05/19] [Fix] add cmath for rounding feature --- aidge_export_cpp/kernels/pooling.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/aidge_export_cpp/kernels/pooling.hpp b/aidge_export_cpp/kernels/pooling.hpp index 28ed5f6..123b980 100644 --- a/aidge_export_cpp/kernels/pooling.hpp +++ b/aidge_export_cpp/kernels/pooling.hpp @@ -4,6 +4,7 @@ #include "network/typedefs.hpp" #include "network/utils.hpp" #include <limits> +#include <cmath> #include <stdexcept> -- GitLab From 0a57a87daa657dfcdc6ff79e54258c9f3c75e2da Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 07:54:12 +0000 Subject: [PATCH 06/19] [Feat] Add rescaling function --- aidge_export_cpp/kernels/rescaling.hpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/aidge_export_cpp/kernels/rescaling.hpp b/aidge_export_cpp/kernels/rescaling.hpp index 9921047..117a0cd 100644 --- a/aidge_export_cpp/kernels/rescaling.hpp +++ b/aidge_export_cpp/kernels/rescaling.hpp @@ -1,6 +1,29 @@ #ifndef __AIDGE_EXPORT_CPP_NETWORK_RESCALING__ #define __AIDGE_EXPORT_CPP_NETWORK_RESCALING__ +#include "kernels/activation.hpp" + + +template<int NB_DATA, + ActivationFunction_T ACTIVATION, + typename Input_T, + typename Output_T, + typename Rescaling_T> +__attribute__((always_inline)) inline +void rescaling_forward ( + const Input_T* __restrict inputs, + Output_T* __restrict outputs, + const Rescaling_T& __restrict rescaling) +{ +#ifdef _OPENMP + #pragma omp parallel +#endif + for (int i = 0; i < NB_DATA; ++i) { + outputs[i] = activation_forward_value<Output_T>(inputs[i] , 0, ACTIVATION, rescaling); + } +} + + // --------------------------------------------------- // ----------------- Saturate Utils ------------------ // --------------------------------------------------- -- GitLab From c8c74fccffb4ba23e0ba96996aec760cf11f68be Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 07:55:57 +0000 Subject: [PATCH 07/19] [Refactor] Change elemwise_op determination --- aidge_export_cpp/operators/CppElemWise.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aidge_export_cpp/operators/CppElemWise.py b/aidge_export_cpp/operators/CppElemWise.py index 416ef61..a43923e 100644 --- a/aidge_export_cpp/operators/CppElemWise.py +++ b/aidge_export_cpp/operators/CppElemWise.py @@ -9,7 +9,6 @@ class CppElemWise(ExportNodeCpp): super().__init__(node, mem_info) # Initialize kernel attributes - self.attributes["elemwise_op"] = node.type() # [Add, Mul, Sub] self.attributes["activation"] = "Linear" self.attributes["rescaling"] = "NoScaling" self.attributes["aidge_cmp"] = node.attributes().aidge_cmp @@ -23,6 +22,8 @@ class CppElemWise(ExportNodeCpp): for n in node.get_operator().get_micro_graph().get_nodes(): if n.type() == "ReLU": self.attributes["activation"] = "Rectifier" + elif n.type() in ["Add", "Mul", "Sub"]: + self.attributes["elemwise_op"] = n.type() ## Get the scaling values for prod in node.get_parents(): -- GitLab From ef5b11c11149b7ee6fbb1ff9f5190b15f4c236c1 Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 07:56:57 +0000 Subject: [PATCH 08/19] [Feat] Add Rescaling registrar file with attribute resolution --- aidge_export_cpp/operators/CppRescaling.py | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 aidge_export_cpp/operators/CppRescaling.py diff --git a/aidge_export_cpp/operators/CppRescaling.py b/aidge_export_cpp/operators/CppRescaling.py new file mode 100644 index 0000000..815e5c2 --- /dev/null +++ b/aidge_export_cpp/operators/CppRescaling.py @@ -0,0 +1,57 @@ +import aidge_core +from aidge_core.export_utils import ExportNodeCpp +from aidge_export_cpp import ROOT +from aidge_export_cpp import ExportLibCpp + +@ExportLibCpp.register_metaop("CppRescaling", aidge_core.ImplSpec(aidge_core.IOSpec(aidge_core.dtype.any))) +class CppRescaling(ExportNodeCpp): + def __init__(self, node, mem_info): + super().__init__(node, mem_info) + + # Initialize kernel attributes + self.attributes["activation"] = "Linear" + self.attributes["rescaling"] = "NoScaling" + self.attributes["shift_value"] = 0 + self.attributes["coef_value"] = 1 + + + # Browse the metaop to update kernel attributes + for n in node.get_operator().get_micro_graph().get_nodes(): + if n.type() == "ReLU": + self.attributes["activation"] = "Rectifier" + + + ## Get the scaling values + for prod in node.get_parents(): + if prod is not None: + if prod.type() == "Producer": + if prod.attributes().has_attr("quantization.ptq.ShiftAmount"): + self.attributes["shift_value"] = prod.attributes().quantization.ptq.ShiftAmount +# elif prod.attributes().has_attr("quantization.ptq.CompensationCoeff"): +# self.attributes["coef_value"] = prod.attributes().quantization.ptq.CompensationCoeff + else: + self.attributes["coef_value"] = prod.get_operator().get_output(0)[0] + + ## Set the scaling type + if self.attributes["coef_value"] != 1: + self.attributes["rescaling"] = "FixedPointScaling" + elif self.attributes["shift_value"] != 0: + self.attributes["rescaling"] = "SingleShiftScaling" + + # Template for layer configutation file generation + self.config_template = str(ROOT / "templates" / "configuration" / "rescaling_config.jinja") + + # Template layer call function generation within the forward file + self.forward_template = str(ROOT / "templates" / "kernel_forward" / "rescaling_forward.jinja") + + # Files to include within the generated forward.cpp file + self.include_list = [] + + # Path to the kernel(s) files to copy + self.add_kernel_to_copy(ROOT / "kernels" / "rescaling.hpp", fwd_include=False) + self.add_kernel_to_copy(ROOT / "kernels" / "activation.hpp", fwd_include=False) + +# # Include aidge outputs within the fwd file +# if self.attributes["aidge_cmp"]: +# self.include_list.append("network/utils.hpp") # aidge_cmp function +# self.include_list.append("data/aidge_outputs/" + node.name() + ".hpp") \ No newline at end of file -- GitLab From 76e61374c4ff4ae5c7ae33faba9c576c80c471a3 Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 07:58:11 +0000 Subject: [PATCH 09/19] [Refactor] Change rescaling resolution using rescaling.jinja --- aidge_export_cpp/templates/configuration/elemwise_config.jinja | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aidge_export_cpp/templates/configuration/elemwise_config.jinja b/aidge_export_cpp/templates/configuration/elemwise_config.jinja index 91a0be4..41c9c3f 100644 --- a/aidge_export_cpp/templates/configuration/elemwise_config.jinja +++ b/aidge_export_cpp/templates/configuration/elemwise_config.jinja @@ -9,5 +9,6 @@ #define {{ name|upper }}_NB_ELTS {{ in_dims[0]|join('*') }} #define {{ name|upper }}_ACTIVATION {{ activation }} #define {{ name|upper }}_ELEM_OP {{ elemwise_op }} -static const {{ rescaling }} {{ name|upper }}_RESCALING = {}; +{% include "./_rescaling.jinja" %} + #endif /* {{ name|upper }}_LAYER_H */ -- GitLab From e4317fd73d2004ea77e115f9f82a9638ac9aac28 Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 08:01:45 +0000 Subject: [PATCH 10/19] [Fix] Replacing a hard cast with a cast based on the output typ --- aidge_export_cpp/templates/kernel_forward/_aidge_cmp.jinja | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aidge_export_cpp/templates/kernel_forward/_aidge_cmp.jinja b/aidge_export_cpp/templates/kernel_forward/_aidge_cmp.jinja index 4ca8af8..bf8b4d7 100644 --- a/aidge_export_cpp/templates/kernel_forward/_aidge_cmp.jinja +++ b/aidge_export_cpp/templates/kernel_forward/_aidge_cmp.jinja @@ -3,6 +3,6 @@ aidge_cmp<{{ out_name[0] | upper }}_NB_OUTPUTS, {{ out_name[0] | upper }}_OUT_HEIGHT, {{ out_name[0] | upper }}_OUT_WIDTH> - ("{{ name }}", (int8_t*) {{ out_name[0] }}_aidge, {{ out_name[0] }}); + ("{{ name }}", ({{out_cdtype[0]}}*) {{ out_name[0] }}_aidge, {{ out_name[0] }}); #endif {%- endif %} \ No newline at end of file -- GitLab From 7801dc6f9f7cfc055a9d84f460a7d3b1385c1530 Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 08:05:09 +0000 Subject: [PATCH 11/19] [Feat] Adaptation of the Aidge_cmp function for an integer datatype --- aidge_export_cpp/static/utils.hpp | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/aidge_export_cpp/static/utils.hpp b/aidge_export_cpp/static/utils.hpp index 8316e5e..e3776a3 100644 --- a/aidge_export_cpp/static/utils.hpp +++ b/aidge_export_cpp/static/utils.hpp @@ -144,7 +144,8 @@ inline void saveOutputs( #if AIDGE_CMP template<int NB_OUTPUTS, int OUT_WIDTH, int OUT_HEIGHT, typename AidgeOutput_T, typename DevOutput_T> -void aidge_cmp(std::string layer_name, AidgeOutput_T* aidge_output, DevOutput_T* dev_output) { +typename std::enable_if<std::is_floating_point<DevOutput_T>::value>::type +aidge_cmp(std::string layer_name, AidgeOutput_T* aidge_output, DevOutput_T* dev_output) { printf("[AIDGE COMPARE] - %s\n", layer_name.c_str()); @@ -154,7 +155,30 @@ void aidge_cmp(std::string layer_name, AidgeOutput_T* aidge_output, DevOutput_T* const int aidge_ofst = out * OUT_HEIGHT * OUT_WIDTH + h * OUT_WIDTH + w; const int dev_ofst = h * OUT_WIDTH * NB_OUTPUTS + w * NB_OUTPUTS + out; if (aidge_output[aidge_ofst] != dev_output[dev_ofst]) { - printf("[ERROR] - First error detected at %dx%dx%d (out x h x w) : aidge_out = %d vs dev_out = %d\n", + printf("[ERROR] - (float) First error detected at %dx%dx%d (out x h x w) : aidge_out = %f vs dev_out = %f\n", + out, h, w, aidge_output[aidge_ofst], dev_output[dev_ofst]); + printf("Abort program.\n"); + exit(1); + } + } + } + } + printf("[SUCCESS]\n\n"); +} + +template<int NB_OUTPUTS, int OUT_WIDTH, int OUT_HEIGHT, typename AidgeOutput_T, typename DevOutput_T> +typename std::enable_if<std::is_integral<DevOutput_T>::value>::type +aidge_cmp(std::string layer_name, AidgeOutput_T* aidge_output, DevOutput_T* dev_output) { + + printf("[AIDGE COMPARE] - %s\n", layer_name.c_str()); + + for (auto out = 0; out < NB_OUTPUTS; ++out) { + for (auto h = 0; h < OUT_HEIGHT; ++h) { + for (auto w = 0; w < OUT_WIDTH; ++w) { + const int aidge_ofst = out * OUT_HEIGHT * OUT_WIDTH + h * OUT_WIDTH + w; + const int dev_ofst = h * OUT_WIDTH * NB_OUTPUTS + w * NB_OUTPUTS + out; + if (aidge_output[aidge_ofst] != dev_output[dev_ofst]) { + printf("[ERROR] - (float) First error detected at %dx%dx%d (out x h x w) : aidge_out = %d vs dev_out = %d\n", out, h, w, aidge_output[aidge_ofst], dev_output[dev_ofst]); printf("Abort program.\n"); exit(1); -- GitLab From b063bd160f61e700131475c6d7460f3d4d3271b5 Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 08:06:27 +0000 Subject: [PATCH 12/19] [Refactor] Change formatting --- .../templates/kernel_forward/elemwise_forward.jinja | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aidge_export_cpp/templates/kernel_forward/elemwise_forward.jinja b/aidge_export_cpp/templates/kernel_forward/elemwise_forward.jinja index 0a3259b..1a99921 100644 --- a/aidge_export_cpp/templates/kernel_forward/elemwise_forward.jinja +++ b/aidge_export_cpp/templates/kernel_forward/elemwise_forward.jinja @@ -3,7 +3,10 @@ elemwise_forward<{{name|upper}}_NB_ELTS, {{name|upper}}_ELEM_OP, {{name|upper}}_ACTIVATION> - ({{out_name[0]}}, {{name|upper}}_RESCALING, {{in_name[0]}}, {{in_name[1]}}); + ({{out_name[0]}}, + {{name|upper}}_RESCALING, + {{in_name[0]}}, + {{in_name[1]}}); {% include "./_save_outputs.jinja" %} {% include "./_aidge_cmp.jinja" %} {% endfilter %} -- GitLab From 56ecbd4148a0fff9ba2dd3913fdce54c58dd5d20 Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 08:07:26 +0000 Subject: [PATCH 13/19] [Feat] Add jinja file for forward build --- .../configuration/rescaling_config.jinja | 15 +++++++++++++++ .../kernel_forward/rescaling_forward.jinja | 9 +++++++++ 2 files changed, 24 insertions(+) create mode 100644 aidge_export_cpp/templates/configuration/rescaling_config.jinja create mode 100644 aidge_export_cpp/templates/kernel_forward/rescaling_forward.jinja diff --git a/aidge_export_cpp/templates/configuration/rescaling_config.jinja b/aidge_export_cpp/templates/configuration/rescaling_config.jinja new file mode 100644 index 0000000..ee9c69e --- /dev/null +++ b/aidge_export_cpp/templates/configuration/rescaling_config.jinja @@ -0,0 +1,15 @@ +{#- For name header -#} +#ifndef {{ name|upper }}_LAYER_H +#define {{ name|upper }}_LAYER_H + +{# For layer configuration -#} +{% include "./_def_io.jinja" %} +{% include "./_meminfo.jinja" %} + +#define {{ name|upper }}_NB_DATA {{ in_chan[0] * in_height[0] * in_width[0] }} + +// Activation +#define {{ name|upper }}_ACTIVATION {{ activation }} +{% include "./_rescaling.jinja" %} + +#endif /* {{ name|upper }}_LAYER_H */ diff --git a/aidge_export_cpp/templates/kernel_forward/rescaling_forward.jinja b/aidge_export_cpp/templates/kernel_forward/rescaling_forward.jinja new file mode 100644 index 0000000..ce4ffb8 --- /dev/null +++ b/aidge_export_cpp/templates/kernel_forward/rescaling_forward.jinja @@ -0,0 +1,9 @@ +{% filter indent(width=4, first=False) %} +{% include "./_mem_offset.jinja" %} +rescaling_forward<{{name|upper}}_NB_DATA, + {{name|upper}}_ACTIVATION> + ({{in_name[0]}}, + {{out_name[0]}}, + {{name|upper}}_RESCALING); +{% include "./_save_outputs.jinja" %} +{% endfilter %} -- GitLab From cea362469356f3de9fabba8b1348a28024726684 Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 08:13:27 +0000 Subject: [PATCH 14/19] [Fix] Add #define to enable or disable the OpenMP option for compilation --- aidge_export_cpp/kernels/convolution.hpp | 5 +++-- aidge_export_cpp/kernels/fullyconnected.hpp | 5 ++++- aidge_export_cpp/kernels/leakyrelu.hpp | 4 +++- aidge_export_cpp/kernels/pooling.hpp | 6 ++++-- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/aidge_export_cpp/kernels/convolution.hpp b/aidge_export_cpp/kernels/convolution.hpp index 6ea9f05..47f9c19 100644 --- a/aidge_export_cpp/kernels/convolution.hpp +++ b/aidge_export_cpp/kernels/convolution.hpp @@ -47,8 +47,9 @@ void convolution_forward( : clamp(CHANNELS_HEIGHT + PADDING_Y - (oy * STRIDE_Y), 0, DILATED_KERNEL_HEIGHT); const int iy = (oy * STRIDE_Y) - PADDING_Y; - -#pragma omp parallel for collapse(2) +#ifdef _OPENMP + #pragma omp parallel for collapse(2) +#endif for (int ox = 0; ox < OUTPUTS_WIDTH; ++ox) { for (int output = 0; output < NB_OUTPUTS; ++output) { // moved to inner loop for collapsing --> diff --git a/aidge_export_cpp/kernels/fullyconnected.hpp b/aidge_export_cpp/kernels/fullyconnected.hpp index 895ed1c..3af09a6 100644 --- a/aidge_export_cpp/kernels/fullyconnected.hpp +++ b/aidge_export_cpp/kernels/fullyconnected.hpp @@ -28,6 +28,7 @@ void fullyconnected_forward ( // It is only an issue if the FC was after a flatten layer. // Otherwise it is not an issue for the other FC because CHANNELS_WIDTH = CHANNELS_HEIGHT = 1 // Solution: Add a system to check dataformat + for (int och = 0; och < NB_OUTPUTS; och++) { Bias_T weightedSum = biases[och]; @@ -45,7 +46,9 @@ void fullyconnected_forward ( } /* Here the kernel to use with inputs in NHWC and weights in NHWC -#pragma omp parallel for +#ifdef _OPENMP + #pragma omp parallel for collapse(2) +#endif for (int och = 0; och < NB_OUTPUTS; och++) { Bias_T weightedSum = biases[och]; diff --git a/aidge_export_cpp/kernels/leakyrelu.hpp b/aidge_export_cpp/kernels/leakyrelu.hpp index 07352cd..3aa2fd5 100644 --- a/aidge_export_cpp/kernels/leakyrelu.hpp +++ b/aidge_export_cpp/kernels/leakyrelu.hpp @@ -11,7 +11,9 @@ void leakyrelu_forward ( Output_T* __restrict outputs, const float negative_slope) { -#pragma omp parallel for +#ifdef _OPENMP + #pragma omp parallel for collapse(2) +#endif for (int i = 0; i < NB_DATA; ++i) { if (inputs[i] >= 0) { outputs[i] = inputs[i]; diff --git a/aidge_export_cpp/kernels/pooling.hpp b/aidge_export_cpp/kernels/pooling.hpp index 123b980..8855dae 100644 --- a/aidge_export_cpp/kernels/pooling.hpp +++ b/aidge_export_cpp/kernels/pooling.hpp @@ -37,7 +37,9 @@ void pooling_forward( 0, POOL_HEIGHT); const int iy = (oy * STRIDE_Y) - PADDING_Y; -#pragma omp parallel for collapse(2) + #ifdef _OPENMP + #pragma omp parallel for collapse(2) + #endif for (int ox = 0; ox < OUTPUTS_WIDTH; ++ox) { for (int output = 0; output < NB_OUTPUTS; ++output) { // moved to inner loop for collapsing --> @@ -117,7 +119,7 @@ void pooling_forward( if constexpr (std::is_integral<Output_T>::value) { outputs[oOffset + output] = (Output_T) std::round((float)sum / (POOL_HEIGHT * POOL_WIDTH)); } else { - outputs[oOffset + output] = (Output_T) (sum / (POOL_HEIGHT * POOL_WIDTH)); + outputs[oOffset + output] = (Output_T) (sum / (POOL_HEIGHT * POOL_WIDTH)); } } -- GitLab From cf3509dd9a695d2f9f4fda68181c1ca2aed188c2 Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 08:31:17 +0000 Subject: [PATCH 15/19] [Feature] Add Python script for ResNet18 export (cpp) --- examples/export_LeNet/lenet.py | 4 +- examples/export_ResNet18/resnet18.py | 587 +++++++++++++++++++++++++++ 2 files changed, 589 insertions(+), 2 deletions(-) create mode 100644 examples/export_ResNet18/resnet18.py diff --git a/examples/export_LeNet/lenet.py b/examples/export_LeNet/lenet.py index af529f9..94f5ad3 100644 --- a/examples/export_LeNet/lenet.py +++ b/examples/export_LeNet/lenet.py @@ -346,11 +346,11 @@ if USE_CUDA: # -------------------------------------------------------------- """ -Here is made the link between the Aidge model and the Jacinto +Here is made the link between the Aidge model and the CPP kernels implementation. In aidge, all the nodes calculations are performed separately (Pad -> Conv -> Quantizer -> ReLU -> ...). -However within the Jacinto export, some core operators are merged +However within the CPP export, some core operators are merged in meta operators. For instance, the padding, scaling and ReLU are performed within the Conv kernel. diff --git a/examples/export_ResNet18/resnet18.py b/examples/export_ResNet18/resnet18.py new file mode 100644 index 0000000..6ca0223 --- /dev/null +++ b/examples/export_ResNet18/resnet18.py @@ -0,0 +1,587 @@ +""" +resnet.py + +This file allows the generation of a resnet18 CPP export. + +In order for this file to work properly, you should first download the imagenet dataset +(search for "ILSVRC2012"). +""" + +import random +import numpy as np +import os +import shutil +from PIL import Image + +# Aidge Modules +import aidge_core +import aidge_onnx +import aidge_backend_cpu +import aidge_quantization +import aidge_export_cpp + +from aidge_export_cpp.export_utils import ( + cpp_fuse_to_metaops, + exclude_unwanted_producers, + set_nodes_names, + set_nodes_datatypes, + normalize) + +from aidge_core.export_utils import remove_optional_inputs + +# Torch (Dataset) +import torch +import torch.nn.functional as F +from torch import nn +from torchvision import transforms, datasets + +# Arguments +import argparse + +supported_types = ["float32", "int8"] + +parser = argparse.ArgumentParser(description="Export the ResNet18 model with the aidge_export_cpp module.") +parser.add_argument("--dev", action="store_true", help="Export in dev mode") +parser.add_argument("--no_cuda", action="store_true", help="Disable USE_CUDA usage to perform inferences and training.") +parser.add_argument("--dtype", type=str, choices=supported_types, default="float32", help="Specify the targeted datatype : [int8, float32]") +parser.add_argument("--aidge_cmp", action="store_true", help="Use aidge tensor results as reference.") +parser.add_argument( + '-v', '--verbose', + action='count', + default=0, + help = ( + "Set the verbosity level of the console output." + "Use -v to increase verbosity, with the following levels in ascending ordern" + "default WARN - Only warnings and higher (WARN, ERROR, FATAL) are displayed.n" + "-v NOTICE - Notices and higher (NOTICE, WARN, ERROR, FATAL) are displayed.n" + "-vv INFO - Informational messages and higher (INFO, NOTICE, WARN, ERROR, FATAL) are displayed.n" + "-vvv DEBUG - All messages, including debug information, are displayed.n" + "Available levels in descending order of severityn" + "DEBUG < INFO < NOTICE < WARN < ERROR < FATAL." + ) +) +args = parser.parse_args() + +USE_CUDA = not args.no_cuda + +# Setting Aidge verbose level +if args.verbose == 0: + aidge_core.Log.set_console_level(aidge_core.Level.Error) +elif args.verbose == 1: + aidge_core.Log.set_console_level(aidge_core.Level.Notice) +elif args.verbose == 2: + aidge_core.Log.set_console_level(aidge_core.Level.Info) +elif args.verbose >= 3: + aidge_core.Log.set_console_level(aidge_core.Level.Debug) + +if USE_CUDA: + import aidge_backend_cuda + +# ------------------------------------------------------------ +# EXPORT CONFIG +# ------------------------------------------------------------ + +""" +Export configuration details : +- RNG_SEED : Fix a random seed for torch to always get the same images from the dataset, + therefore always getting the same output. +- NB_TEST : Number of example inferences to perform (used to get an accuracy approximation). +- NB_CALIB : Number of samples used for the calibration step of quantization. +- MODEL_NAME : Should be the same name as the onnx file you want to load and export. +- DO_EXAMPLES : Perform example inferences (and allow to get accuracy approximation) +- NB_BITS : Quantization output precision. Should be 8 to work with this export. +- TARGET_TYPE : The aidge datatype for tensors to be casted after the quantization step. +- OPTIM_SIGN : Quantization optional optimization based on data sign. +- SINGLE_SHIFT : Quantization option specifying if inserted scaling nodes should be + single shift or floating point. +- ROUNDING : Apply rounding on the data after the single shift step. +- NO_QUANTIZATION : Skip the quantization step. Should be set to False. +- CLIPPING : Clipping method during quantization. +- FOLD_GRAPH : The quantization step adds cast nodes to cast the graph into the given TARGET_TYPE. + Enabling the FOLD_GRAPH will automatically fold these nodes into the following + ones at the end of quantization step. +- USE_CUDA : Determine if the quantization step uses the GPU. It is generally recommended + to enable this option if you have access to GPUs as the quantization step + may take a while to complete. +- DEV_MODE : The dev mode allows to identify errors more easily export the model with + symbolic links enabling to modify the source files directly in the + generated export (make sure you installed the export plugin running + `pip install -e .`). + Enabled running this python file, adding the --test argument. +- AIDGE_MODE : Saves and export the outputs generated by the aidge inferences in order + to compare it with the export outputs. + Enabled running this python file, adding the --aidge_cmp argument. +""" + +print(" Available backends : ", aidge_core.Tensor.get_available_backends()) + +quantize_model = False +NB_BITS = 32 +TARGET_TYPE = aidge_core.dtype.float32 + +if args.dtype == "float32": + quantize_model = False +elif args.dtype == "int8": + quantize_model = True + NB_BITS = 8 + TARGET_TYPE = aidge_core.dtype.int32 # int8 not yet available +else: + print(f"[ERROR] Datatype '{args.dtype}' not supported.") + print(f"[ERROR] Supported datatypes : {supported_types}.") + exit(1) + +RNG_SEED = 1234 +NB_TEST = 20 # Test set +NB_CALIB = 20 # Calibration set +MODEL_NAME = 'resnet18' +EXPORT_FOLDER = f"export_{MODEL_NAME}_int8" +DO_EXAMPLES = True + +# Quantization params +OPTIM_SIGN = False +SINGLE_SHIFT = True +ROUNDING = True +NO_QUANTIZATION = False +CLIPPING = aidge_quantization.Clipping.MSE # 'MAX' +FOLD_GRAPH = True + +# Export modes +DEV_MODE = args.dev +AIDGE_CMP = args.aidge_cmp + +IMAGENET_PATH = "/database2/ILSVRC2012/val" # Search for ILSVRC2012 + +def print_cfg(): + print('\n RNG_SEED = ', RNG_SEED) + print(' MODEL_NAME = ', MODEL_NAME) + print(' NB_TEST = ', NB_TEST) + print(' NB_CALIB = ', NB_CALIB) + print(' NB_BITS = ', NB_BITS) + print(' OPTIM_SIGN = ', OPTIM_SIGN) + print(' NO_QUANTIZATION = ', NO_QUANTIZATION) + print(' CLIPPING = ', CLIPPING) + print(' SINGLE_SHIFT = ', SINGLE_SHIFT) + print(' TARGET_TYPE = ', TARGET_TYPE) + print(' FOLD_GRAPH = ', FOLD_GRAPH) + print(' USE_CUDA = ', USE_CUDA) + print(' DEV_MODE = ', DEV_MODE) + print(' ROUNDING = ', ROUNDING) + +print_cfg() + +torch.manual_seed(RNG_SEED) +random.seed(RNG_SEED) +np.random.seed(RNG_SEED) + +backend = "cuda" if USE_CUDA else "cpu" + +VAL_PATH = "/database2/ILSVRC2012/val.txt" # File containing labels of image of val folder + +image_label_pairs = [] +with open(VAL_PATH, 'r') as f: + for line in f: + parts = line.strip().split() + if len(parts) == 2: + image_name, label = parts + image_label_pairs.append((image_name, int(label))) + +#random.shuffle(image_label_pairs) +np.random.seed(RNG_SEED) +#image_label_pairs = np.random.permutation(image_label_pairs).tolist() +NB_SELECT = max(NB_TEST, NB_CALIB) # Vérifie que NB_TEST et NB_CALIB sont fixés +selected_pairs = image_label_pairs[:NB_SELECT] + +#selected_pairs = image_label_pairs[:max(NB_TEST, NB_CALIB)] + +# -------------------------------------------------------------- +# CREATE THE SAMPLES +# -------------------------------------------------------------- + +transform_val = transforms.Compose([transforms.Resize(256), + transforms.CenterCrop(224), + transforms.ToTensor(), + transforms.Normalize(mean=[0.485,0.456, 0.406], std=[0.229, 0.224, 0.225]) + ]) + +tensors = [] +labels = [] +paths = [] +index = 0 + +for image_name, label in selected_pairs: + image_path = os.path.join(IMAGENET_PATH, image_name) + if os.path.exists(image_path): + try: + image = Image.open(image_path) + if image.mode != 'RGB': + image = image.convert('RGB') + tensor = transform_val(image) + tensors.append(tensor) + labels.append(label) + paths.append(image_path) + except Exception as e: + print(f"Error with image {image_path}: {e}") + +#print(f"Number of loaded tensors: {len(tensors)}") +#for lbl, img_path in zip(labels, paths): +# print(f"Label: {lbl} -> Image Path: {img_path}") + +backend = "cuda" if USE_CUDA else "cpu" +aidge_tensors = [] +for tensor in tensors: + array = tensor.numpy() + array = np.reshape(array, (1, 3, 224, 224)) + array = normalize(array) + aidge_tensor = aidge_core.Tensor(array) + aidge_tensor.set_backend(backend) + aidge_tensor.set_datatype(aidge_core.dtype.float32) + aidge_tensors.append(aidge_tensor) + + +# -------------------------------------------------------------- +# LOAD THE MODEL +# -------------------------------------------------------------- + +""" +Load the .onnx model and perform some usual graph modifications : + - Remove the flatten nodes; + - Fuse the batchnorm nodes into the biases producers. +""" + +model = aidge_onnx.load_onnx(MODEL_NAME + ".onnx", verbose=False) +model.save("imported_model") +aidge_core.remove_flatten(model) +aidge_core.fuse_batchnorm(model) +model.save("imported_model_fused_bn") + +# -------------------------------------------------------------- +# SET UP THE AIDGE SCHEDULER +# -------------------------------------------------------------- + +""" +The scheduler is an ordered version of the model, allowing to schedule +nodes to be able to run inferences, for instance. +""" + +# Set up the backend +model.set_datatype(aidge_core.dtype.float32) +model.set_backend(backend) + +# Create the Scheduler +scheduler = aidge_core.SequentialScheduler(model) + +# -------------------------------------------------------------- +# RUN SOME EXAMPLES INFERENCES +# -------------------------------------------------------------- + +def propagate(model, scheduler, aidge_tensor): + """ Propagate the given tensor into the model + """ + # Run the inference + scheduler.forward(True, [aidge_tensor]) + # Gather the results + output_node = model.get_output_nodes().pop() + output_tensor = output_node.get_operator().get_output(0).clone() + output_tensor.set_backend("cpu") + return np.array(output_tensor) + +accuracy = 0 +if (DO_EXAMPLES): + print('\n EXAMPLE INFERENCES :') + nb_valid = 0 + base_values = [] + for i in range(NB_TEST): + output_array = propagate(model, scheduler, aidge_tensors[i]) + print(labels[i], ' VS ', np.argmax(output_array), ' -> ', np.max(output_array)) + base_values.append(np.max(output_array)) + if (labels[i] == np.argmax(output_array)): + nb_valid += 1 + accuracy = nb_valid / NB_TEST + print('\n MODEL ACCURACY = ', accuracy * 100, '%') + + +#-------------------------------------------------------------- +# PERFORM THE QUANTIZATION +# -------------------------------------------------------------- + +if quantize_model: + aidge_quantization.quantize_network( + network = model, + nb_bits = NB_BITS, + input_dataset = aidge_tensors[0:NB_CALIB], + clipping_mode = CLIPPING, + target_type = TARGET_TYPE, + no_quantization = NO_QUANTIZATION, + optimize_signs = OPTIM_SIGN, + single_shift = SINGLE_SHIFT, + use_cuda = USE_CUDA, + fold_graph = FOLD_GRAPH, + bitshift_rounding = ROUNDING) + +# -------------------------------------------------------------- +# RESCALE THE INPUT SAMPLES +# -------------------------------------------------------------- + +""" +Once the quantization is done, the graph now only accepts integer inputs. +So we need to rescale the dataset for the data to be within [0, 255]. +Also, tensors should be casted to be the same type as TARGET_TYPE. +""" +if quantize_model: + rescaling = 2**(NB_BITS-1)-1 + for i in range(max(NB_TEST, NB_CALIB)): + array = np.array(aidge_tensors[i]) * rescaling + aidge_tensors[i] = aidge_core.Tensor(array) + aidge_tensors[i].set_datatype(TARGET_TYPE) + +# -------------------------------------------------------------- +# GENERATE NEW SCHEDULER +# -------------------------------------------------------------- + +""" +Each time the graph has been change, it has to be reset. +Here some Quantizer and Cast nodes have been added. +""" + +""" [START Fix] +We need first to manually add an input tensor with the correct datatype, +as it is not automatically done in PTQ. +""" +# input_node = model.get_ordered_inputs()[0] +# input_node[0].get_operator().set_input(0,aidge_tensors[0]) +""" [END Fix]""" +if quantize_model: + scheduler.reset_scheduling() + +# -------------------------------------------------------------- +# PERFORM THE EXAMPLE INFERENCES AGAIN +# -------------------------------------------------------------- + + + +#for node in model.get_input_nodes(): +# if node.type() == "Pad2D": +# node.set_name("Pad2D_input") +# +#for node in model.get_nodes(): +# if (node.type() == "Conv2D"): +# if node.get_parent(0).name() == "Pad2D_input": +# node.set_name("Conv2D_input") + +model.save("post_ptq") + +if (DO_EXAMPLES and quantize_model): + print('\n QUANTIZED EXAMPLE INFERENCES :') + nb_valid = 0 + post_values = [] + for i in range(NB_TEST): + output_array = propagate(model, scheduler, aidge_tensors[i]) + print(labels[i], ' VS ', np.argmax(output_array), ' -> ', np.max(output_array)) + post_values.append(np.max(output_array)) + if (labels[i] == np.argmax(output_array)): + nb_valid += 1 + + quant_accuracy = nb_valid / NB_TEST + print('\n MODEL ACCURACY = ', accuracy * 100, '%') + print('\n QUANTIZED ACCURACY = ', quant_accuracy * 100, '%') + + print("post ptq") +# output_array = propagate(model, scheduler, aidge_tensors[0]) + +#model.log_outputs("log_outputs_post_ptq") + +if USE_CUDA: + model.set_backend("cpu") + for aidge_tensor in aidge_tensors: + aidge_tensor.set_backend("cpu") + +# -------------------------------------------------------------- +# FUSE NODES INTO METAOPS +# -------------------------------------------------------------- + +""" +Here is made the link between the Aidge model and the CPP +kernels implementation. In aidge, all the nodes calculations +are performed separately (Pad -> Conv -> Quantizer -> ReLU -> ...). + +However within the CPP export, some core operators are merged +in meta operators. For instance, the padding, scaling and ReLU are +performed within the Conv kernel. + +In this step, we use graph regex techniques to find the desired patterns +within the graph in order to match the export implementation of the kernels. +""" + +# Expand meta ops +""" +We first need to expand the graph to break all the metaops that may already +exist. For instance, PaddedConv will become Pad -> Conv. +""" +aidge_core.expand_metaops(model) + + +model.save("after_expand") + +# Exclude unwanted producers +""" +Before fusing the nodes, we set a tag on the Producers in order to exclude +from the export the ones holding coefficients, as they are directly handled +within the layers parameters. +""" +exclude_unwanted_producers(model) + +# Fuse nodes +cpp_fuse_to_metaops(model) + +# Remove optional inputs +""" +Some optional inputs may be added by the quantization step (for instance with the clipping nodes). +Here we make sure that they will not be considered as actual graph inputs by the export, by +excluding them from the ordered_inputs list of the model. +""" +remove_optional_inputs(model) + +# Reset scheduler to apply graph modifications +""" +The scheduler always needs to be reset after graph manipulation. +""" +scheduler.reset_scheduling() + +# Name newly created MetaOps +""" +As names are optional in Aidge, the fuse_to_metaops function will not automatically +give a name to the newly created metaOps. However, in an export context, we need +our operators to be named, as this will be used to name the corresponding files. +""" +scheduler.generate_scheduling() # Scheduler needs to be generated as it has just been reset +set_nodes_names(scheduler) + +# -------------------------------------------------------------- +# LOG OUTPUTS FOR THE LAST IMAGE OF THE TEST DATASET +# -------------------------------------------------------------- + +""" +Here a final inference is made on the input we want to export and run. +This will ensure that all the feature maps tensors (between the layers) +hold the data corresponding to this specific input. +Then, the "log_outputs()" function (called later) will store these tensors +into log files that may be exported as well for comparison purpose. +""" + +output_array = propagate(model, scheduler, aidge_tensors[0]) + +print("### Exported Sample ###") +print("Aidge prediction after quantization :", np.argmax(output_array), "(" + str(np.max(output_array)) + ")") +print("Label :", labels[0]) + +# -------------------------------------------------------------- +# HANDLING DATATYPE +# -------------------------------------------------------------- + +""" +Now, despite the quantization stage, all the tensors of the model are +still "virtually" in Int32. Before exporting the model, we have to set +tensors' datatypes to Int8, except for biases which should remain in Int32. +""" +if quantize_model: + set_nodes_datatypes(model) + +# Store tensors values into log files +""" +Once the tensors has been casted, the log_outputs() function can be +called to store their values into log files. +""" + +if os.path.isdir("log_outputs"): + shutil.rmtree("log_outputs") +model.log_outputs("log_outputs") + +# -------------------------------------------------------------- +# TEST MODE +# -------------------------------------------------------------- + +""" +The test mode is mainly used for validation and benchmark. The model will be +exported in a way that each layer's result will be compared with the CPU implementation. +The timings for each layer will be displayed. +In case of error, you will be able to enter debug mode, showing in-layer data or +changing the inputs of the layer, to isolate the source of the issue. +""" + +for node in model.get_nodes(): + node.attributes().dev_mode = DEV_MODE + +# -------------------------------------------------------------- +# AIDGE CMP +# -------------------------------------------------------------- + +""" +If the --aidge_cmp option is enabled, the feature maps generated by aidge with the +backend cpu will be exported in the generated export. It will be used as reference +to verify that the results with the optimized kernels are correct for the exported +model. +This option has to be passed to each node in order to be used within the Export Nodes. +(JConv, JPad, ...) that you can find in the "export_gen/operator_export" folder. +""" + +for node in model.get_nodes(): + node.attributes().aidge_cmp = AIDGE_CMP + +# -------------------------------------------------------------- +# EXPORT THE MODEL +# -------------------------------------------------------------- + + +model.save("exported_model") +inputs_tensor = aidge_core.Tensor(np.array(aidge_tensors[0])) +# print(np.array(inputs_tensor)[0]) +inputs_tensor.set_data_format(aidge_core.dformat.nchw) +inputs_tensor.set_data_format(aidge_core.dformat.nhwc) +if args.dtype == "int8": + inputs_tensor.set_datatype(aidge_core.dtype.int8) + +#print(np.array(inputs_tensor)[0,:,:,:]) +#inputs_tensor.cpy_transpose(inputs_tensor, aidge_core.get_permutation_mapping(aidge_core.dformat.nchw, aidge_core.dformat.nhwc)) +# print(np.array(inputs_tensor)[0]) + +aidge_export_cpp.export(EXPORT_FOLDER, + model, + scheduler, + labels = aidge_core.Tensor(labels[0]), + inputs_tensor=inputs_tensor, + dev_mode = DEV_MODE, + aidge_cmp = AIDGE_CMP) +# +## -------------------------------------------------------------- +## GENERATE LABELS AND INPUTS FOR EXAMPLE INFERENCE +## -------------------------------------------------------------- +# +#input_label = np.array(labels).astype(np.int32).reshape(len(labels), 1) +#generate_input_file(export_folder=EXPORT_FOLDER + "/data", +# array_name="labels", +# tensor=aidge_core.Tensor(input_label)) +# +#input_tensor = np.array(aidge_tensors[0:NB_TEST]).astype(np.int8).reshape(NB_TEST, 3, 224, 224) +#generate_input_file(export_folder=EXPORT_FOLDER + "/data", +# array_name="inputs", +# tensor=aidge_core.Tensor(input_tensor)) +# +# +#if TEST_MODE: +# input_tensor = aidge_core.Tensor(input_tensor) +# input_tensor.set_data_format(aidge_core.dformat.nchw) +# input_tensor.cpy_transpose(input_tensor, aidge_core.get_permutation_mapping(aidge_core.dformat.nchw, aidge_core.dformat.nhwc)) +# generate_input_file(export_folder=EXPORT_FOLDER + "/data", +# array_name="inputs_ref", +# tensor=input_tensor) +# +## -------------------------------------------------------------- +## GENERATE DOCUMENTATION +## -------------------------------------------------------------- +# +#""" +#Copy the corresponding README file into the generated export. +#""" +# +#generate_documentation(EXPORT_FOLDER, TEST_MODE) +# \ No newline at end of file -- GitLab From 1057b2e01d88277c49b42824ac87fc8abb825174 Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 08:31:34 +0000 Subject: [PATCH 16/19] [Git] Add gitignores --- examples/export_LeNet/.gitignore | 1 + examples/export_ResNet18/.gitignore | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 examples/export_ResNet18/.gitignore diff --git a/examples/export_LeNet/.gitignore b/examples/export_LeNet/.gitignore index 20cb419..98ce649 100644 --- a/examples/export_LeNet/.gitignore +++ b/examples/export_LeNet/.gitignore @@ -3,3 +3,4 @@ export_lenet_int8/ log_outputs/* assets/* data/* +log.txt diff --git a/examples/export_ResNet18/.gitignore b/examples/export_ResNet18/.gitignore new file mode 100644 index 0000000..a6e4e97 --- /dev/null +++ b/examples/export_ResNet18/.gitignore @@ -0,0 +1,6 @@ +# Exclude export artefacts +export_resnet18_int8/ +log_outputs/* +assets/* +data/* +log.txt -- GitLab From 55b4d0969c1c27ce5e3f3a639fcaa144a2534d58 Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 12:46:52 +0000 Subject: [PATCH 17/19] [Fix] Suppress constexpr and simplified the choice of rounding --- aidge_export_cpp/kernels/pooling.hpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/aidge_export_cpp/kernels/pooling.hpp b/aidge_export_cpp/kernels/pooling.hpp index 8855dae..5340bd0 100644 --- a/aidge_export_cpp/kernels/pooling.hpp +++ b/aidge_export_cpp/kernels/pooling.hpp @@ -116,11 +116,9 @@ void pooling_forward( } } - if constexpr (std::is_integral<Output_T>::value) { - outputs[oOffset + output] = (Output_T) std::round((float)sum / (POOL_HEIGHT * POOL_WIDTH)); - } else { - outputs[oOffset + output] = (Output_T) (sum / (POOL_HEIGHT * POOL_WIDTH)); - } + outputs[oOffset + output] = static_cast<Output_T>( + std::is_integral<Output_T>::value ? std::round((float)sum / (POOL_HEIGHT * POOL_WIDTH)) : sum / (POOL_HEIGHT * POOL_WIDTH) + ); } else { -- GitLab From 3f58cc0196ef98226089c7304258d6b0cffff226 Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 1 Apr 2025 13:21:11 +0000 Subject: [PATCH 18/19] [Refactor] Simplify aidge_cmp function to one function for float or interger --- aidge_export_cpp/static/utils.hpp | 35 +++++++------------------------ 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/aidge_export_cpp/static/utils.hpp b/aidge_export_cpp/static/utils.hpp index e3776a3..77d9891 100644 --- a/aidge_export_cpp/static/utils.hpp +++ b/aidge_export_cpp/static/utils.hpp @@ -144,8 +144,7 @@ inline void saveOutputs( #if AIDGE_CMP template<int NB_OUTPUTS, int OUT_WIDTH, int OUT_HEIGHT, typename AidgeOutput_T, typename DevOutput_T> -typename std::enable_if<std::is_floating_point<DevOutput_T>::value>::type -aidge_cmp(std::string layer_name, AidgeOutput_T* aidge_output, DevOutput_T* dev_output) { +void aidge_cmp(std::string layer_name, AidgeOutput_T* aidge_output, DevOutput_T* dev_output) { printf("[AIDGE COMPARE] - %s\n", layer_name.c_str()); @@ -155,31 +154,13 @@ aidge_cmp(std::string layer_name, AidgeOutput_T* aidge_output, DevOutput_T* dev_ const int aidge_ofst = out * OUT_HEIGHT * OUT_WIDTH + h * OUT_WIDTH + w; const int dev_ofst = h * OUT_WIDTH * NB_OUTPUTS + w * NB_OUTPUTS + out; if (aidge_output[aidge_ofst] != dev_output[dev_ofst]) { - printf("[ERROR] - (float) First error detected at %dx%dx%d (out x h x w) : aidge_out = %f vs dev_out = %f\n", - out, h, w, aidge_output[aidge_ofst], dev_output[dev_ofst]); - printf("Abort program.\n"); - exit(1); - } - } - } - } - printf("[SUCCESS]\n\n"); -} - -template<int NB_OUTPUTS, int OUT_WIDTH, int OUT_HEIGHT, typename AidgeOutput_T, typename DevOutput_T> -typename std::enable_if<std::is_integral<DevOutput_T>::value>::type -aidge_cmp(std::string layer_name, AidgeOutput_T* aidge_output, DevOutput_T* dev_output) { - - printf("[AIDGE COMPARE] - %s\n", layer_name.c_str()); - - for (auto out = 0; out < NB_OUTPUTS; ++out) { - for (auto h = 0; h < OUT_HEIGHT; ++h) { - for (auto w = 0; w < OUT_WIDTH; ++w) { - const int aidge_ofst = out * OUT_HEIGHT * OUT_WIDTH + h * OUT_WIDTH + w; - const int dev_ofst = h * OUT_WIDTH * NB_OUTPUTS + w * NB_OUTPUTS + out; - if (aidge_output[aidge_ofst] != dev_output[dev_ofst]) { - printf("[ERROR] - (float) First error detected at %dx%dx%d (out x h x w) : aidge_out = %d vs dev_out = %d\n", - out, h, w, aidge_output[aidge_ofst], dev_output[dev_ofst]); + if (std::is_floating_point<DevOutput_T>::value) { + printf("[ERROR] - (float) First error detected at %dx%dx%d (out x h x w) : aidge_out = %f vs dev_out = %f\n", + out, h, w, static_cast<double>(aidge_output[aidge_ofst]), static_cast<double>(dev_output[dev_ofst])); + } else { + printf("[ERROR] - (float) First error detected at %dx%dx%d (out x h x w) : aidge_out = %d vs dev_out = %d\n", + out, h, w, static_cast<int>(aidge_output[aidge_ofst]), static_cast<int>(dev_output[dev_ofst])); + } printf("Abort program.\n"); exit(1); } -- GitLab From c2cfa8d15e5d4e12a8ea0452e1069c1edca264bc Mon Sep 17 00:00:00 2001 From: Mickael GUIBERT <mickael.guibert@cea.fr> Date: Tue, 15 Apr 2025 12:37:32 +0000 Subject: [PATCH 19/19] [Fix](Producer) Add ignore attribute to False by default --- aidge_export_cpp/operators/Producer.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/aidge_export_cpp/operators/Producer.py b/aidge_export_cpp/operators/Producer.py index 1ae54d3..138b58c 100644 --- a/aidge_export_cpp/operators/Producer.py +++ b/aidge_export_cpp/operators/Producer.py @@ -47,7 +47,10 @@ class ProducerCPP(ExportNode): def __init__(self, node, mem_info): super().__init__(node, mem_info) self.values = np.array(self.operator.get_output(0)) - self.ignore = node.attributes().ignore + if node.attributes().has_attr("ignore"): + self.ignore = node.attributes().ignore + else: + self.ignore = False if len(self.values.shape) == 4: # Note: export in HWC self.values = np.transpose(self.values, (0, 2, 3, 1)) -- GitLab