diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4a443fc403ab341d770455b203524c7f6e65f42a..59b890b815549bce6ee40e5e6651ab1b3e9bd0c9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,10 +29,4 @@ test:ubuntu_python: - DEPENDENCY_JOB="build:ubuntu_python" - !reference [.ubuntu:download:artifacts, script] # Need to install extra dependence for tests: - - python -m pip install torch torchvision -coverage:ubuntu_python: - before_script: - - !reference [.setup:coverage:ubuntu_python, before_script] - - DEPS_NAMES=("aidge_onnx" "aidge_quantization") - - DEPENDENCY_JOB="build:ubuntu_python" - - !reference [.ubuntu:download:artifacts, script] \ No newline at end of file + - python -m pip install torch torchvision \ No newline at end of file diff --git a/aidge_export_cpp/export.py b/aidge_export_cpp/export.py index 7effde97a608c5630cf94870f301e3a56fb0a1a2..caf4d8a30cb97fd4d88f40ecc9fbd1cff52c01b3 100644 --- a/aidge_export_cpp/export.py +++ b/aidge_export_cpp/export.py @@ -3,13 +3,14 @@ import shutil import numpy as np from pathlib import Path from typing import List, Union +import json import aidge_core from aidge_core.mem_info import generate_optimized_memory_info -from aidge_core.export_utils import scheduler_export, generate_main_cpp, aidge2c, generate_file +from aidge_core.export_utils import scheduler_export, generate_main_cpp -from aidge_export_cpp import ExportLibCpp, ROOT -from aidge_export_cpp.export_utils import read_log_file +from aidge_export_cpp import ExportLibCpp +from aidge_export_cpp.export_utils import export_aidge_ifmaps def export(export_folder_name: str, @@ -59,6 +60,25 @@ def export(export_folder_name: str, aidge_core.adapt_fc_params_format(graphview) graphview.forward_dims(dims=[inputs_tensor.dims()] if inputs_tensor is not None else []) + # -------------------------------------------------------------- + # 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 "operators" folder. + """ + + if aidge_cmp: + for node in graphview.get_nodes(): + node.attributes().aidge_cmp = True + + graphview.save("exported_model") + scheduler.reset_scheduling() scheduler.generate_scheduling() @@ -85,25 +105,5 @@ def export(export_folder_name: str, generate_main_cpp(export_folder_name, graphview, labels=labels, inputs_tensor=inputs_tensor) # Generate log files (aidge_cmp option) - """ - If the aidge_cmp option has been enabled, the generated log_outputs will - be copied into the generated export in order to be used as reference. - """ if aidge_cmp: - ranked_nodes = graphview.get_ranked_nodes_name("{0}[{1}#{3}]") - os.makedirs(export_folder_name / "data" / "aidge_outputs") - os.makedirs(export_folder_name / "data" / "export_outputs") - for node in graphview.get_nodes(): - if node.type() != "Producer": - file_path = 'log_outputs/' + ranked_nodes[node] + '/output_0.log' - data_t = aidge2c(node.get_operator().get_output(0).dtype()) - name = node.name() + '_output_0_aidge' - dims = node.get_operator().get_output(0).dims() - values = read_log_file(file_path) - - generate_file(export_folder_name / "data" / "aidge_outputs" / (node.name() + ".hpp"), - ROOT / "templates" / "data" / "aidge_tensor.jinja", - data_t=data_t, - name=name, - dims=dims, - values=values) + export_aidge_ifmaps(export_folder_name) diff --git a/aidge_export_cpp/export_utils.py b/aidge_export_cpp/export_utils.py index e22524fb9058dfb4c8b023d0df8fbe11e2ff791b..3906aecdc69ed12a605c2e84e78eac8a95ac2ebd 100644 --- a/aidge_export_cpp/export_utils.py +++ b/aidge_export_cpp/export_utils.py @@ -1,13 +1,17 @@ import os +import json +import numpy as np from collections import OrderedDict import aidge_core -from aidge_core.export_utils import get_node_from_metaop +from aidge_core.export_utils import get_node_from_metaop, aidge2c, generate_file + +from aidge_export_cpp import ROOT def cpp_fuse_to_metaops(graph_view: aidge_core.GraphView): """ Fuse nodes into metaops adapted for the CPP Export - TODO: These recipes should be into aidge_core + TODO: These recipes should be in aidge_core :param graph_view: An instance of :py:class:`aidge_core.GraphView`, providing access to nodes and ordered input/output data within the computational graph. @@ -152,25 +156,6 @@ def set_nodes_datatypes(graph_view: aidge_core.GraphView): -def read_log_file(file_path: str): - """ Read log file - Used to read the aidge generated log files containing the intermediate - tensors of the exported model. - - :param file_path: Path to the file to read. - :type file_path: str - """ - # Check if the file exists - if not os.path.isfile(file_path): - print(f"File not found: {file_path}") - return None - - with open(file_path, 'r') as file: - content = file.read() - return content - - - def exclude_unwanted_producers(model): """ Exclude some producers not needed for the export @@ -226,3 +211,53 @@ def normalize(array): array = (array - array.min()) / (array.max() - array.min()) return 2 * array - 1 + + +def generate_aidge_ifmaps(model): + + json_nodes = [] + for node in model.get_nodes(): + if node.type() != "Producer": + + output = node.get_operator().get_output(0) + data = { + "name": node.name(), + "dims": output.dims(), + "dtype": aidge2c(output.dtype()), + "dformat": str(output.dformat()), + "values": np.array(output).tolist() + } + json_nodes.append(data) + + # Write the entire list to the JSON file after the loop + with open('aidge_output.json', 'w') as file: + json.dump(json_nodes, file, indent=2, separators=(",", ": ")) + + + +def export_aidge_ifmaps(export_folder_name): + os.makedirs(export_folder_name / "data" / "aidge_outputs") + os.makedirs(export_folder_name / "data" / "export_outputs") + + # Load the JSON data from the file + with open('aidge_output.json', 'r') as file: + json_nodes = json.load(file) + + # Access the data + for node in json_nodes: + name = node["name"] + dims = node["dims"] + dtype = node["dtype"] + dformat = node["dformat"] + values = node["values"] + + generate_file(export_folder_name / "data" / "aidge_outputs" / (name + ".hpp"), + ROOT / "templates" / "data" / "aidge_tensor.jinja", + dtype=dtype, + dformat=dformat, + name=name + "_output_0_aidge", + dims=dims, + values=values) + + # Remove the JSON file + os.remove('aidge_output.json') diff --git a/aidge_export_cpp/operators/Producer.py b/aidge_export_cpp/operators/Producer.py index bfae74fe490bdb24e99b8ecb00f6df8d7c42a6a7..5bd63323155b3342dcefc6b92e3c9861c8333127 100644 --- a/aidge_export_cpp/operators/Producer.py +++ b/aidge_export_cpp/operators/Producer.py @@ -2,29 +2,12 @@ import os from pathlib import Path import numpy as np import aidge_core -from aidge_core.export_utils import ExportNodeCpp, generate_file +from aidge_core.export_utils import ExportNodeCpp, generate_file, aidge2c from aidge_export_cpp import ROOT from aidge_export_cpp import ExportLibCpp -def numpy_dtype2ctype(dtype): - if dtype == np.int8: - return "int8_t" - elif dtype == np.int16: - return "int16_t" - elif dtype == np.int32: - return "int32_t" - elif dtype == np.int64: - return "int64_t" - elif dtype == np.float32: - return "float" - elif dtype == np.float64: - return "double" - # Add more dtype mappings as needed - else: - raise ValueError(f"Unsupported {dtype} dtype") - def export_params(name: str, - array: np.ndarray, + output: aidge_core.Tensor, filepath: str): # Get directory name of the file @@ -38,15 +21,16 @@ def export_params(name: str, filepath, str(ROOT / "templates" / "data" / "parameters.jinja"), name=name, - data_t=numpy_dtype2ctype(array.dtype), - values=array.tolist() + dims=output.dims(), + dtype=aidge2c(output.dtype()), + values=np.array(output).tolist() ) @ExportLibCpp.register("Producer", aidge_core.ImplSpec(aidge_core.IOSpec(aidge_core.dtype.any))) class ProducerCPP(ExportNodeCpp): def __init__(self, node, mem_info): super().__init__(node, mem_info) - self.values = np.array(self.operator.get_output(0)) + self.output = self.operator.get_output(0) self.ignore = node.attributes().has_attr("ignore") def export(self, export_folder: Path): @@ -67,7 +51,7 @@ class ProducerCPP(ExportNodeCpp): header_path = f"include/parameters/{self.attributes['name']}.h" export_params( self.attributes['out_name'][0], - self.values.reshape(-1), + self.output, str(export_folder / header_path)) return [path_to_definition, header_path] diff --git a/aidge_export_cpp/static/utils.hpp b/aidge_export_cpp/static/utils.hpp index 1d24532313419b3fe888a4ea9c8497ca148b55e8..2fc3a8860b22a2632df84f1fdaf19e301637d154 100644 --- a/aidge_export_cpp/static/utils.hpp +++ b/aidge_export_cpp/static/utils.hpp @@ -66,9 +66,10 @@ static inline OutputIt copy_n(InputIt first, Size count, OutputIt result) { return result; } -#if SAVE_OUTPUTS +#if SAVE_OUTPUTS || AIDGE_CMP + enum class Format { - Default, + DEFAULT, NCHW, NHWC, CHWN, @@ -77,105 +78,129 @@ enum class Format { CDHWN }; +#endif // SAVE_OUTPUTS || AIDGE_CMP + +#if SAVE_OUTPUTS + +template<int NB_OUTPUTS, int OUT_HEIGHT, int OUT_WIDTH, Format FMT, typename Output_T> +inline void saveOutputs(const Output_T* __restrict outputs, FILE* pFile) { + + auto offset = 0; + + // NCHW + if (FMT == Format::NCHW || FMT == Format::DEFAULT) { + fprintf(pFile, "{"); + for (auto out = 0; out < NB_OUTPUTS; ++out) { + fprintf(pFile, "{"); + for (auto h = 0; h < OUT_HEIGHT; ++h) { + fprintf(pFile, "{"); + for (auto w = 0; w < OUT_WIDTH; ++w) { -template<typename Output_T> -inline void saveOutputs( - int NB_OUTPUTS, - int OUTPUTS_HEIGHT, int OUTPUTS_WIDTH, - // int OUTPUT_MEM_CONT_OFFSET, - // int OUTPUT_MEM_CONT_SIZE, - // int OUTPUT_MEM_WRAP_OFFSET, - // int OUTPUT_MEM_WRAP_SIZE, - // int OUTPUT_MEM_STRIDE, - const Output_T* __restrict outputs, - FILE* pFile, - Format format) -{ - // default is NHCW ! - if (format == Format::NHWC) { - fprintf(pFile, "("); - auto oOffset = 0; - for(int oy = 0; oy < OUTPUTS_HEIGHT; oy++) { - fprintf(pFile, "("); - - for(int ox = 0; ox < OUTPUTS_WIDTH; ox++) { - fprintf(pFile, "("); - - // const int oPos = (ox + OUTPUTS_WIDTH * oy); - // int oOffset = OUTPUT_MEM_STRIDE * oPos; - - // if (OUTPUT_MEM_WRAP_SIZE > 0 - // && oOffset >= OUTPUT_MEM_CONT_SIZE) - // { - // oOffset += OUTPUT_MEM_WRAP_OFFSET - OUTPUT_MEM_CONT_OFFSET - // - OUTPUT_MEM_CONT_SIZE; - // } - - for (int output = 0; output < NB_OUTPUTS; output++) { if (std::is_floating_point<Output_T>::value) - fprintf(pFile, "%f", static_cast<float>(outputs[oOffset])); + fprintf(pFile, "%f", static_cast<float>(outputs[offset])); else - fprintf(pFile, "%d", static_cast<int>(outputs[oOffset])); - oOffset += 1; + fprintf(pFile, "%d", static_cast<int>(outputs[offset])); + offset += 1; fprintf(pFile, ", "); - } - fprintf(pFile, "), \n"); + } + fprintf(pFile, "}\n"); } - - fprintf(pFile, "), \n"); + fprintf(pFile, "}\n"); } + fprintf(pFile, "}\n"); - fprintf(pFile, ")\n"); - } - else if (format == Format::NCHW || format == Format::Default) { - auto ofst = 0; - for(int output = 0; output < NB_OUTPUTS; output++) { - fprintf(pFile, "%d:\n", output); - for(int oy = 0; oy < OUTPUTS_HEIGHT; oy++) { - for(int ox = 0; ox < OUTPUTS_WIDTH; ox++) { - fprintf(pFile, "%d", static_cast<int>(outputs[ofst])); - fprintf(pFile, " "); - ofst += 1; - } + // NHWC + } else if (FMT == Format::NHWC) { + fprintf(pFile, "{\n"); // Start outer brace + for (auto h = 0; h < OUT_HEIGHT; ++h) { + fprintf(pFile, " {\n"); // Indent level 1 + for (auto w = 0; w < OUT_WIDTH; ++w) { + fprintf(pFile, " { "); // Indent level 2 and open inner brace + for (auto out = 0; out < NB_OUTPUTS; ++out) { + if (std::is_floating_point<Output_T>::value) + fprintf(pFile, "%f", static_cast<float>(outputs[offset])); + else + fprintf(pFile, "%4d", static_cast<int>(outputs[offset])); + offset += 1; - fprintf(pFile, "\n"); + // Add comma except for last element + if (out != NB_OUTPUTS - 1) + fprintf(pFile, ","); + } + fprintf(pFile, " },\n"); // Close inner brace and newline } - - fprintf(pFile, "\n"); + fprintf(pFile, " },\n"); // Close w-loop brace and newline } + fprintf(pFile, "}\n"); // Close outer brace - fprintf(pFile, "\n"); - } - else { - printf("Warning unsupported dataformat.\n"); + } else { + printf("[ERROR] - Format is not supported.\n"); + printf("[ERROR] - Aborting save outputs...\n"); + return; } } #endif // SAVE_OUTPUTS #if AIDGE_CMP -template<int NB_OUTPUTS, int OUT_WIDTH, int OUT_HEIGHT, typename AidgeOutput_T, typename DevOutput_T> +template<int NB_OUTPUTS, int OUT_WIDTH, int OUT_HEIGHT, Format FMT> +int get_ofst_from_fmt(int out, int h, int w) { + if (FMT == Format::NCHW || FMT == Format::DEFAULT) + return out * OUT_HEIGHT * OUT_WIDTH + h * OUT_WIDTH + w; + else if (FMT == Format::NHWC) + return h * OUT_WIDTH * NB_OUTPUTS + w * NB_OUTPUTS + out; + else { + printf("[ERROR] - This data format is not supported.\n"); + return -1; + } +} + +template<int NB_OUTPUTS, int OUT_WIDTH, int OUT_HEIGHT, Format AIDGE_FMT, Format DEV_FMT, typename AidgeOutput_T, typename DevOutput_T> void aidge_cmp(std::string layer_name, AidgeOutput_T* aidge_output, DevOutput_T* dev_output) { - printf("[AIDGE COMPARE] - %s\n", layer_name.c_str()); + printf("[NOTICE] - Comparing with Aidge ref for node : %s -> ", layer_name.c_str()); + + const float atol = 1e-5f; // Absolute + const float rtol = 1e-3f; // Relative 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]) { - if (std::is_floating_point<DevOutput_T>::value) { - printf("[ERROR] - 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 { + + const int aidge_ofst = get_ofst_from_fmt<NB_OUTPUTS, OUT_WIDTH, OUT_HEIGHT, AIDGE_FMT>(out, h, w); + const int dev_ofst = get_ofst_from_fmt<NB_OUTPUTS, OUT_WIDTH, OUT_HEIGHT, DEV_FMT>(out, h, w); + + if (aidge_ofst == -1 || dev_ofst == -1) { + printf("[FAILURE]\n"); + printf("[ERROR] - Aborting this layer comparison...\n"); + return; + } + + // Float Comparison + if (std::is_floating_point<DevOutput_T>::value) { + + const float diff = std::abs(aidge_output[aidge_ofst] - dev_output[dev_ofst]); + const float tolerance = atol + rtol * std::abs(dev_output[dev_ofst]); + + if (diff > tolerance) { + printf("[FAILURE]\n"); + printf("[ERROR] - 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])); + printf("Abort program.\n"); + exit(1); + } + + // Int Comparison + } else { + if (aidge_output[aidge_ofst] != dev_output[dev_ofst]) { + printf("[FAILURE]\n"); printf("[ERROR] - 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])); + out, h, w, static_cast<int>(aidge_output[aidge_ofst]), static_cast<int>(dev_output[dev_ofst])); + printf("[ERROR] - Abort program.\n"); + exit(1); } - printf("Abort program.\n"); - exit(1); } } } diff --git a/aidge_export_cpp/templates/configuration/_save_outputs.jinja b/aidge_export_cpp/templates/configuration/_save_outputs.jinja new file mode 100644 index 0000000000000000000000000000000000000000..358b9cf40e16715882809a1b69be8b86493799b4 --- /dev/null +++ b/aidge_export_cpp/templates/configuration/_save_outputs.jinja @@ -0,0 +1 @@ +#define {{ out_name[0] | upper }}_DEV_FMT Format::{{ out_format[0] | upper }} \ No newline at end of file diff --git a/aidge_export_cpp/templates/configuration/activation_config.jinja b/aidge_export_cpp/templates/configuration/activation_config.jinja index d25cd078e3c6c49b64102fe5a1d82b88c6db37a2..45c2760706fd6e3a07b9cf5c9be8a1e585a2f0b1 100644 --- a/aidge_export_cpp/templates/configuration/activation_config.jinja +++ b/aidge_export_cpp/templates/configuration/activation_config.jinja @@ -6,6 +6,7 @@ {# For layer configuration -#} #define {{ name|upper }}_NB_ELTS {{ in_dims[0]|join('*') }} #define {{ name|upper }}_ACTIVATION {{ activation }} +{% include "./_save_outputs.jinja" %} {% include "./_def_io.jinja" %} {% include "./_meminfo.jinja" %} {% include "./_rescaling.jinja" %} diff --git a/aidge_export_cpp/templates/configuration/batchnorm_config.jinja b/aidge_export_cpp/templates/configuration/batchnorm_config.jinja index 0c0bc49b521556eee1a4e455486caae44a2b86cb..34412212dbb1ff689d4c04d89f8206e22bad8d3b 100644 --- a/aidge_export_cpp/templates/configuration/batchnorm_config.jinja +++ b/aidge_export_cpp/templates/configuration/batchnorm_config.jinja @@ -8,6 +8,7 @@ {% include "./_meminfo.jinja" %} #define {{ name|upper }}_ACTIVATION {{ activation }} #define {{ name|upper }}_EPSILON {{ epsilon }} +{% include "./_save_outputs.jinja" %} {% include "./_rescaling.jinja" %} #endif /* {{ name|upper }}_LAYER_H */ diff --git a/aidge_export_cpp/templates/configuration/concat_config.jinja b/aidge_export_cpp/templates/configuration/concat_config.jinja index 51ba7ced3125d1b472981c4601ccddf018f525b2..b9037b0a9a1ad18bdc578da892057a7d66b47138 100644 --- a/aidge_export_cpp/templates/configuration/concat_config.jinja +++ b/aidge_export_cpp/templates/configuration/concat_config.jinja @@ -14,4 +14,6 @@ constexpr int {{name|upper}}_AXIS_SIZE[] = { {{ axis_size|join(", ") }} }; #define {{ name|upper }}_AXIS_SIZE_POST {{ axis_size_post }} #define {{ name|upper }}_AXIS_SIZE_PRE {{ axis_size_pre }} +{% include "./_save_outputs.jinja" %} + #endif /* {{ name|upper }}_LAYER_H */ diff --git a/aidge_export_cpp/templates/configuration/convolution_config.jinja b/aidge_export_cpp/templates/configuration/convolution_config.jinja index 7cdaf05e59caf20f5b702db664fdd79dcd7e15e4..e168d9e61a600038dca8a7c2513c2ddc438b4b72 100644 --- a/aidge_export_cpp/templates/configuration/convolution_config.jinja +++ b/aidge_export_cpp/templates/configuration/convolution_config.jinja @@ -14,6 +14,7 @@ #define {{ name|upper }}_KERNEL_HEIGHT {{ kernel_dims[0] if kernel_dims|length > 1 else 1 }} #define {{ name|upper }}_KERNEL_WIDTH {{ kernel_dims[1] if kernel_dims|length > 1 else kernel_dims[0] }} #define {{ name|upper }}_ACTIVATION {{ activation }} +{% include "./_save_outputs.jinja" %} {% include "./_rescaling.jinja" %} {#- Calculate sizes #} diff --git a/aidge_export_cpp/templates/configuration/elemwise_config.jinja b/aidge_export_cpp/templates/configuration/elemwise_config.jinja index 73b4db594b7d4758939d72a1833fca70aa9f707f..8fb205ad0694019475f8f63961a78c39f6ac970d 100644 --- a/aidge_export_cpp/templates/configuration/elemwise_config.jinja +++ b/aidge_export_cpp/templates/configuration/elemwise_config.jinja @@ -16,6 +16,8 @@ constexpr int {{name|upper}}_OFFSET_IN2[] = { {{ offset_in2|join(", ") }} }; #define {{ name|upper }}_ACTIVATION {{ activation }} #define {{ name|upper }}_ELEM_OP {{ elemwise_op }} + +{% include "./_save_outputs.jinja" %} {% include "./_rescaling.jinja" %} #endif /* {{ name|upper }}_LAYER_H */ diff --git a/aidge_export_cpp/templates/configuration/erf_config.jinja b/aidge_export_cpp/templates/configuration/erf_config.jinja index 9aaf61fd6ad2c6a2700534ad7a6b4d74db1a9573..1e8e02c81188ddfeeb0d1c6e821f2509d6bb9a9e 100644 --- a/aidge_export_cpp/templates/configuration/erf_config.jinja +++ b/aidge_export_cpp/templates/configuration/erf_config.jinja @@ -6,5 +6,6 @@ {% include "./_def_io.jinja" %} {% include "./_meminfo.jinja" %} #define {{ name|upper }}_NB_ELTS {{ in_dims[0]|join('*') }} +{% include "./_save_outputs.jinja" %} #endif /* {{ name|upper }}_LAYER_H */ \ No newline at end of file diff --git a/aidge_export_cpp/templates/configuration/fullyconnected_config.jinja b/aidge_export_cpp/templates/configuration/fullyconnected_config.jinja index 856d727abc11ceb6f914e9d71d286ef5882322d6..b50f64fb500b7ae036f91821b689175c2c9b3e92 100644 --- a/aidge_export_cpp/templates/configuration/fullyconnected_config.jinja +++ b/aidge_export_cpp/templates/configuration/fullyconnected_config.jinja @@ -13,4 +13,6 @@ #define {{ name|upper }}_WEIGHTS_SIZE {{ weights_size }} #define {{ name|upper }}_BIASES_SIZE {{ out_chan[0] }} +{% include "./_save_outputs.jinja" %} + #endif /* {{ name|upper }}_LAYER_H */ diff --git a/aidge_export_cpp/templates/configuration/hardmax_config.jinja b/aidge_export_cpp/templates/configuration/hardmax_config.jinja index b5bf60cfd28ead3b0b787a0750c5ceae648e5e72..247a44cfe9da08864ea06d08264b04f9fb900876 100644 --- a/aidge_export_cpp/templates/configuration/hardmax_config.jinja +++ b/aidge_export_cpp/templates/configuration/hardmax_config.jinja @@ -10,5 +10,6 @@ #define {{ name|upper }}_AXIS_STRIDE {{ axis_stride }} #define {{ name|upper }}_POSTAXIS_STRIDE {{ postaxis_stride }} #define {{ name|upper }}_INOUT_NB_ELTS {{ out_nb_elts }} +{% include "./_save_outputs.jinja" %} #endif /* {{ name|upper }}_LAYER_H */ diff --git a/aidge_export_cpp/templates/configuration/leakyrelu_config.jinja b/aidge_export_cpp/templates/configuration/leakyrelu_config.jinja index c201c5585cd0a48f2ca5b726502c899a6dcce6fc..2d6bd87ff2eb64026dd25a5741bf1fb89f1ebc6d 100644 --- a/aidge_export_cpp/templates/configuration/leakyrelu_config.jinja +++ b/aidge_export_cpp/templates/configuration/leakyrelu_config.jinja @@ -7,5 +7,6 @@ {% include "./_meminfo.jinja" %} #define {{ name|upper }}_NB_ELTS {{ in_dims[0]|join('*') }} #define {{ name|upper }}_ALPHA {{ alpha }} +{% include "./_save_outputs.jinja" %} #endif /* {{ name|upper }}_LAYER_H */ diff --git a/aidge_export_cpp/templates/configuration/matmul_config.jinja b/aidge_export_cpp/templates/configuration/matmul_config.jinja index d62bac2bd09f93f6da566fa6da0998678f4b9805..a0bc5570ab488396aaca498b1831a733a8aa5424 100644 --- a/aidge_export_cpp/templates/configuration/matmul_config.jinja +++ b/aidge_export_cpp/templates/configuration/matmul_config.jinja @@ -16,6 +16,8 @@ constexpr int {{name|upper}}_OFFSET_IN2[] = { {{ offset_in2|join(", ") }} }; #define {{ name|upper }}_ACTIVATION {{ activation }} +{% include "./_save_outputs.jinja" %} + {% include "./_rescaling.jinja" %} {#- Calculate sizes #} diff --git a/aidge_export_cpp/templates/configuration/pad_config.jinja b/aidge_export_cpp/templates/configuration/pad_config.jinja index 8b21577fe4d6f52ddb36ae796740f265db3d45cc..ed6f8ae79d19925041d7c2fec587a53f59770200 100644 --- a/aidge_export_cpp/templates/configuration/pad_config.jinja +++ b/aidge_export_cpp/templates/configuration/pad_config.jinja @@ -9,5 +9,6 @@ #define {{ name|upper }}_PADDING_TOP {{ padding[0] }} #define {{ name|upper }}_PADDING_LEFT {{ padding[1] }} #define {{ name|upper }}_BORDER_VALUE {{ border_value }} +{% include "./_save_outputs.jinja" %} #endif /* {{ name|upper }}_LAYER_H */ diff --git a/aidge_export_cpp/templates/configuration/pooling_config.jinja b/aidge_export_cpp/templates/configuration/pooling_config.jinja index 57608fcb9c8071a1c686b10565d6adab78735079..ad6a7aebe54d2386e5bc0d47b2e80b0ea019aad6 100644 --- a/aidge_export_cpp/templates/configuration/pooling_config.jinja +++ b/aidge_export_cpp/templates/configuration/pooling_config.jinja @@ -13,5 +13,6 @@ #define {{ name|upper }}_KERNEL_WIDTH {{ kernel_dims[1] }} #define {{ name|upper }}_POOLING_TYPE {{ pool_type }} #define {{ name|upper }}_ACTIVATION {{ activation }} +{% include "./_save_outputs.jinja" %} #endif /* {{ name|upper }}_LAYER_H */ diff --git a/aidge_export_cpp/templates/configuration/reducemean_config.jinja b/aidge_export_cpp/templates/configuration/reducemean_config.jinja index 98db3a91a1e1dbe58ed09e349fb4fddffab8c448..6c27e0f0b9a82fc3dcfcc28890dfb04817dc59e5 100644 --- a/aidge_export_cpp/templates/configuration/reducemean_config.jinja +++ b/aidge_export_cpp/templates/configuration/reducemean_config.jinja @@ -18,5 +18,7 @@ static const unsigned int {{ name|upper }}_PREAXIS_STRIDES[{{ in_nb_dims }}] = static const unsigned int {{ name|upper }}_POSTAXIS_STRIDES[{{ in_nb_dims }}] = { {{ post_axis_strides|join(", ") }} }; +{% include "./_save_outputs.jinja" %} + #endif /* {{ name|upper }}_LAYER_H */ diff --git a/aidge_export_cpp/templates/configuration/reshape_config.jinja b/aidge_export_cpp/templates/configuration/reshape_config.jinja index 88c8cc21f134b333e354c0df96dd07f41e948506..80318d1fa23d8e6caa401d0d40f080339343c2be 100644 --- a/aidge_export_cpp/templates/configuration/reshape_config.jinja +++ b/aidge_export_cpp/templates/configuration/reshape_config.jinja @@ -6,5 +6,6 @@ {% include "./_meminfo.jinja" %} {# For layer configuration -#} #define {{ name|upper }}_NB_ELTS {{ in_dims[0]|join('*') }} +{% include "./_save_outputs.jinja" %} #endif /* {{ name|upper }}_LAYER_H */ diff --git a/aidge_export_cpp/templates/configuration/softmax_config.jinja b/aidge_export_cpp/templates/configuration/softmax_config.jinja index 6dc6b3453e103757c184e7a6efdfd9d21c5cbd43..095bce86e3d30ef77a5599f47b031aa0ba25775e 100644 --- a/aidge_export_cpp/templates/configuration/softmax_config.jinja +++ b/aidge_export_cpp/templates/configuration/softmax_config.jinja @@ -8,5 +8,6 @@ #define {{ name|upper }}_AXIS_SIZE {{ axis_size }} #define {{ name|upper }}_AXIS_SIZE_POST {{ axis_size_post }} #define {{ name|upper }}_AXIS_SIZE_PRE {{ axis_size_pre }} +{% include "./_save_outputs.jinja" %} #endif /* {{ name|upper }}_LAYER_H */ diff --git a/aidge_export_cpp/templates/configuration/transpose_ND_config.jinja b/aidge_export_cpp/templates/configuration/transpose_ND_config.jinja index 7d341c7b70922f86c8eea903b435a4657bef7e37..595e76ecb15569e7a80fe842232eb495699f6023 100644 --- a/aidge_export_cpp/templates/configuration/transpose_ND_config.jinja +++ b/aidge_export_cpp/templates/configuration/transpose_ND_config.jinja @@ -13,5 +13,6 @@ constexpr int {{ name|upper }}_OUT_STRIDES[] = { {{ out_strides | join(', ') }} constexpr int {{ name|upper }}_PERMUTE[] = { {{ output_dims_order | join(', ') }} }; constexpr int {{ name|upper }}_DIMS[] = { {{ in_dims[0] | join(', ') }} }; +{% include "./_save_outputs.jinja" %} #endif /* {{ name|upper }}_LAYER_H */ \ No newline at end of file diff --git a/aidge_export_cpp/templates/data/aidge_tensor.jinja b/aidge_export_cpp/templates/data/aidge_tensor.jinja index 3f086afd0a8f9a479a6073b463cefce21fc3e752..a1d5a20dff9a48e364eca0a588ac193eb2f5ad83 100644 --- a/aidge_export_cpp/templates/data/aidge_tensor.jinja +++ b/aidge_export_cpp/templates/data/aidge_tensor.jinja @@ -1,7 +1,79 @@ #include <stdint.h> -static const {{ data_t }} {{ name }} +#define {{name|upper}}_FMT Format::{{ dformat | upper }} + +{%- set format_map = { + "int8_t": "%4d", + "int16_t": "%6d", + "int32_t": "%6d", + "int64_t": "%8d", + "uint8_t": "%4d", + "uint16_t": "%6d", + "uint32_t": "%6d", + "uint64_t": "%8d", + "float": "%.9f", + "half_float::half": "%.9f", + "double": "%.17f" +} %} + +static const {{ dtype }} {{ name }} {%- for dim in dims -%} [{{ dim }}] {%- endfor %} = -{{ values }}; + +{{ '{' -}} + +{# 1D #} +{%- if dims | length == 1 -%} +{{ '{' }} +{%- for x in range(dims[0]) -%} +{{ format_map[dtype] | format(values[x]) }}, +{%- endfor -%} +{{ '}' }}; +{%- endif -%} + +{#- 2D #} +{%- if dims | length == 2 -%} +{%- for y in range(dims[0]) -%} +{{ '{' }} + {%- for x in range(dims[1]) -%} + {{ format_map[dtype] | format(values[y][x]) }}, + {%- endfor -%} +{{ '}' }}, +{%- endfor -%} +{%- endif -%} + +{#- 3D #} +{%- if dims | length == 3 -%} +{%- for z in range(dims[0]) %} +{{ '{' }} + {%- for y in range(dims[1]) %} + {{ '{' }} + {%- for x in range(dims[2]) -%} + {{ format_map[dtype] | format(values[z][y][x]) }}, + {%- endfor -%} + {{ '}' }}, + {%- endfor %} +{{ '}' }}, +{%- endfor -%} +{%- endif -%} + +{#- 4D #} +{%- if dims | length == 4 -%} +{%- for n in range(dims[0]) %} +{{ '{' }} + {%- for z in range(dims[1]) %} + {{ '{' }} + {%- for y in range(dims[2]) %} + {{ '{' }} + {%- for x in range(dims[3]) -%} + {{ format_map[dtype] | format(values[n][z][y][x]) }}, + {%- endfor -%} + {{ '}' }}, + {%- endfor %} + {{ '}' }}, + {%- endfor %} +{{ '}' }}, +{%- endfor %} +{%- endif -%} +{{ '};' }} diff --git a/aidge_export_cpp/templates/data/parameters.jinja b/aidge_export_cpp/templates/data/parameters.jinja index b58ca9c1977a5e8f9af69401c0eb3e5f47fc6cc4..70e2b7dcc5b5728bcc252c4974d4e09b79f975f0 100644 --- a/aidge_export_cpp/templates/data/parameters.jinja +++ b/aidge_export_cpp/templates/data/parameters.jinja @@ -1,21 +1,66 @@ {#- For libraries -#} #include <stdint.h> +{%- set format_map = { + "int8_t": "%4d", + "int16_t": "%6d", + "int32_t": "%6d", + "int64_t": "%8d", + "uint8_t": "%4d", + "uint16_t": "%6d", + "uint32_t": "%6d", + "uint64_t": "%8d", + "float": "%.9f", + "half_float::half": "%.9f", + "double": "%.17f" +} %} + {# Design header of the array -#} -static const {{ data_t }} {{ name }}[{{ values|length }}] __attribute__((section(".nn_data"))) = +static const {{ dtype }} {{ name }}[{{ dims | join("*") }}] __attribute__((section(".nn_data"))) = { - {# For loop to add new elements -#} - {%- for i in range(values|length) %} +{# 1D #} +{%- if dims | length == 1 -%} +{%- for x in range(dims[0]) -%} +{{ format_map[dtype] | format(values[x]) }}, +{%- endfor -%} +{%- endif -%} + +{#- 2D #} +{%- if dims | length == 2 -%} +{%- for y in range(dims[0]) %} +{{ ' ' }} + {%- for x in range(dims[1]) -%} + {{ format_map[dtype] | format(values[y][x]) }}, + {%- endfor %} +{%- endfor -%} +{%- endif -%} + +{#- 3D #} +{%- if dims | length == 3 -%} +{%- for z in range(dims[0]) %} +{{ ' ' }} + {%- for y in range(dims[1]) %} + {{ ' ' }} + {%- for x in range(dims[2]) -%} + {{ format_map[dtype] | format(values[z][y][x]) }}, + {%- endfor -%} + {%- endfor %} +{%- endfor -%} +{%- endif -%} - {#- Last value -#} - {%- if (i+1) == values|length -%} - {{ values[i]|string }} - {%- else -%} - {%- if (i+1) % 5 == 0 -%} - {{ values[i]|string + ",\n\t" }} - {%- else -%} - {{ values[i]|string + ", " }} - {%- endif -%} - {%- endif -%} +{#- 4D #} +{%- if dims | length == 4 -%} +{%- for n in range(dims[0]) %} +{{ ' ' }} + {%- for z in range(dims[1]) %} + {{ ' ' }} + {%- for y in range(dims[2]) %} + {{ ' ' }} + {%- for x in range(dims[3]) -%} + {{ format_map[dtype] | format(values[n][z][y][x]) }}, + {%- endfor -%} + {%- endfor %} {%- endfor %} +{%- endfor %} +{%- endif %} }; diff --git a/aidge_export_cpp/templates/kernel_forward/_aidge_cmp.jinja b/aidge_export_cpp/templates/kernel_forward/_aidge_cmp.jinja index 98778c1deff2a8bc2b32eac395f8113279f03d68..587b0de414c726d800f85851dca21ce2a85016a8 100644 --- a/aidge_export_cpp/templates/kernel_forward/_aidge_cmp.jinja +++ b/aidge_export_cpp/templates/kernel_forward/_aidge_cmp.jinja @@ -2,7 +2,9 @@ #if AIDGE_CMP aidge_cmp<{{ out_name[0] | upper }}_NB_OUTPUTS, {{ out_name[0] | upper }}_OUT_HEIGHT, - {{ out_name[0] | upper }}_OUT_WIDTH> + {{ out_name[0] | upper }}_OUT_WIDTH, + {{ out_name[0] | upper }}_AIDGE_FMT, + {{ out_name[0] | upper }}_DEV_FMT> ("{{ name }}", ({{out_cdtype[0]}}*) {{ out_name[0] }}_aidge, {{ out_name[0] }}); #endif {%- endif %} \ No newline at end of file diff --git a/aidge_export_cpp/templates/kernel_forward/_save_outputs.jinja b/aidge_export_cpp/templates/kernel_forward/_save_outputs.jinja index 6865be575a613af16fc6a88fd969525abba80d0d..1781ba029ee04b428216e2249f1cc584705bcbf3 100644 --- a/aidge_export_cpp/templates/kernel_forward/_save_outputs.jinja +++ b/aidge_export_cpp/templates/kernel_forward/_save_outputs.jinja @@ -1,19 +1,17 @@ #if SAVE_OUTPUTS -{% for outidx in range(nb_out) -%} - FILE* {{out_name[outidx]|upper}}_STREAM = fopen("data/export_outputs/{{out_name[outidx]}}.txt", "w"); - saveOutputs<{{out_cdtype[outidx]}}>( - {{out_name[outidx]|upper}}_NB_OUTPUTS, - {{out_name[outidx]|upper}}_OUT_HEIGHT, - {{out_name[outidx]|upper}}_OUT_WIDTH, - {#- {{out_name[outidx]|upper}}_CONT_OFFSET, - {{out_name[outidx]|upper}}_CONT_SIZE, - {{out_name[outidx]|upper}}_WRAP_OFFSET, - {{out_name[outidx]|upper}}_WRAP_SIZE, - {{out_name[outidx]|upper}}_STRIDE, #} - {{out_name[outidx]}}, - {{out_name[outidx]|upper}}_STREAM, - Format::NHWC); - fclose({{out_name[outidx]|upper}}_STREAM); -{% endfor %} +printf("[NOTICE] Saving outputs of node {{ name }}\n"); +FILE* {{ out_name[0] | upper }}_STREAM = fopen("data/export_outputs/{{out_name[0]}}.txt", "w"); +saveOutputs<{{ out_name[0] | upper }}_NB_OUTPUTS, + {{ out_name[0] | upper }}_OUT_HEIGHT, + {{ out_name[0] | upper }}_OUT_WIDTH, + {{ out_name[0] | upper }}_DEV_FMT> + ({#- {{ out_name[0] | upper }}_CONT_OFFSET, + {{ out_name[0] | upper }}_CONT_SIZE, + {{ out_name[0] | upper }}_WRAP_OFFSET, + {{ out_name[0] | upper }}_WRAP_SIZE, + {{ out_name[0] | upper }}_STRIDE, -#} + {{ out_name[0] }}, + {{ out_name[0] | upper }}_STREAM); +fclose({{ out_name[0] | upper }}_STREAM); #endif diff --git a/examples/export_LeNet/.gitignore b/examples/export_LeNet/.gitignore index 98ce649a943a90590bb8f4f067a3c1ac9691dcbc..ec72a3d2e95310ee29821d5051be746aa6bebdbf 100644 --- a/examples/export_LeNet/.gitignore +++ b/examples/export_LeNet/.gitignore @@ -4,3 +4,4 @@ log_outputs/* assets/* data/* log.txt +*.json diff --git a/examples/export_LeNet/lenet.py b/examples/export_LeNet/lenet.py index 1d7960b0837cae7fe46aa23037b4712935579811..28c75178530484998afa394329adb3eafba4d510 100644 --- a/examples/export_LeNet/lenet.py +++ b/examples/export_LeNet/lenet.py @@ -18,11 +18,7 @@ import aidge_backend_cpu import aidge_quantization import aidge_export_cpp -from aidge_export_cpp.export_utils import ( - cpp_fuse_to_metaops, - set_nodes_names, - set_nodes_datatypes, - exclude_unwanted_producers) +from aidge_export_cpp.export_utils import * from aidge_core.export_utils import remove_optional_inputs, get_node_from_metaop @@ -310,8 +306,9 @@ if quantize_model: tensors[i] = aidge_core.Tensor(array) tensors[i].set_datatype(TARGET_TYPE) tensors[i].set_backend("cpu") - # Setting modele to CPU for export - model.set_backend("cpu") + +# Setting model to CPU for export +model.set_backend("cpu") # -------------------------------------------------------------- @@ -442,9 +439,8 @@ Once the tensors have 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") +if AIDGE_CMP: + generate_aidge_ifmaps(model) # -------------------------------------------------------------- # TEST MODE @@ -461,29 +457,10 @@ 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 "operators" folder. -""" - -if AIDGE_CMP: - for node in model.get_nodes(): - node.attributes().aidge_cmp = True - # -------------------------------------------------------------- # EXPORT THE MODEL # -------------------------------------------------------------- -model.save("exported_model") - aidge_export_cpp.export(EXPORT_FOLDER, model, scheduler, @@ -503,4 +480,4 @@ try: for std_line in aidge_core.utils.run_command(["./bin/run_export"], cwd=EXPORT_FOLDER): print(std_line, end="") except subprocess.CalledProcessError as e: - raise RuntimeError(0, f"An error occurred, failed to run export.") from e \ No newline at end of file + raise RuntimeError(0, f"An error occurred, failed to run export.") from e diff --git a/examples/export_ResNet18/resnet18.py b/examples/export_ResNet18/resnet18.py index 4cca5c8c58cba71b6e19d972d28ce76d14933e79..178ce7b584e021781cf7c0dd485a031680e90574 100644 --- a/examples/export_ResNet18/resnet18.py +++ b/examples/export_ResNet18/resnet18.py @@ -23,12 +23,7 @@ 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_export_cpp.export_utils import * from aidge_core.export_utils import remove_optional_inputs, get_node_from_metaop @@ -506,16 +501,6 @@ 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 # -------------------------------------------------------------- @@ -545,15 +530,12 @@ This option has to be passed to each node in order to be used within the Export """ if AIDGE_CMP: - for node in model.get_nodes(): - node.attributes().aidge_cmp = True + generate_aidge_ifmaps(model) # -------------------------------------------------------------- # EXPORT THE MODEL # -------------------------------------------------------------- -model.save("exported_model") - aidge_export_cpp.export(EXPORT_FOLDER, model, scheduler,