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..ea82167e463b4d2324fd412ecd5681b7a74478a4 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,48 @@ 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()), + "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"] + values = node["values"] + + generate_file(export_folder_name / "data" / "aidge_outputs" / (name + ".hpp"), + ROOT / "templates" / "data" / "aidge_tensor.jinja", + data_t=dtype, + name=name + "_output_0_aidge", + dims=dims, + values=values) + diff --git a/examples/export_LeNet/lenet.py b/examples/export_LeNet/lenet.py index 1d7960b0837cae7fe46aa23037b4712935579811..d80a3077ef51268c27af21788322969065a14307 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 @@ -442,9 +438,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 +456,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,