diff --git a/accuracy_tools/msprobe/core/components/dumper_caffe.py b/accuracy_tools/msprobe/core/components/dumper_caffe.py new file mode 100644 index 0000000000000000000000000000000000000000..363db0ea8cf926a988817bec65a2013f91f3e116 --- /dev/null +++ b/accuracy_tools/msprobe/core/components/dumper_caffe.py @@ -0,0 +1,104 @@ +# Copyright (c) 2025-2025 Huawei Technologies Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from msprobe.base import Component, ProducerComp +from msprobe.core.base import BaseDumper +from msprobe.core.components.dumper_offline_model import OfflineModelActuatorComp +from msprobe.core.dump import CaffeModelActuator +from msprobe.utils.constants import CompConst, DumpConst, MsgConst +from msprobe.utils.exceptions import MsprobeException +from msprobe.utils.hijack import ActionType, hijacker + + +@Component.register(CompConst.CAFFE_ACTUATOR_COMP) +class CaffeActuatorComp(OfflineModelActuatorComp): + def __init__(self, priority, model_path, input_shape, input_path, **kwargs): + super().__init__(priority) + self.actuator = CaffeModelActuator(model_path, input_shape, input_path, **kwargs) + + def activate(self, *args, **kwargs): + self.actuator.load_model() + inputs_tensor_info = self.actuator.get_input_tensor_info() + input_map = self.actuator.get_inputs_data(inputs_tensor_info) + _ = self.actuator.infer(input_map) + + +@Component.register(CompConst.CAFFE_DUMPER_COMP) +class CaffeDumperComp(ProducerComp, BaseDumper): + def __init__(self, priority, data_mode): + ProducerComp.__init__(self, priority) + BaseDumper.__init__(self, data_mode) + self.caffe_net = None + + def activate(self, *args, **kwargs): + self.register_hook() + + def deactivate(self, *args, **kwargs): + self.release_hook() + + def register_hook(self): + self.handler.append( + hijacker(stub=self._capture_caffe_net, module="caffe", function="Net", action=ActionType.POST_HOOK) + ) + + def load_data(self): + if self._data_iter is None: + self._get_input_output_map() + self._data_iter = self._summ_dump_data() + try: + return next(self._data_iter) + except StopIteration: + return None + + def _get_input_output_map(self): + self._get_output_map() + self._augment_input_map() + + def _summ_dump_data(self): + net_output_nodes = self.caffe_net.outputs + for layer_name in self.caffe_net.blobs.keys(): + self.data_for_save.setdefault(layer_name, {}) + if any(x in self.data_mode for x in DumpConst.INPUT_ALL): + input_data = self.through_nodes( + self.caffe_net.bottom_names.get(layer_name), layer_name, DumpConst.INPUT_ARGS, self.input_map + ) + for item in input_data: + yield item, net_output_nodes + if any(x in self.data_mode for x in DumpConst.OUTPUT_ALL): + output_data = self.through_nodes( + self.caffe_net.top_names.get(layer_name), layer_name, DumpConst.OUTPUT_ARGS, self.output_map + ) + for item in output_data: + yield item, net_output_nodes + + def _augment_input_map(self): + for layer_name, param in self.caffe_net.params.items(): + if len(param) != 2: + raise MsprobeException( + MsgConst.REQUIRED_ARGU_MISSING, + f"The current layer ({layer_name})'s input does not include weights and biases.", + ) + self.input_map[f"{layer_name}_weight"] = param[0].data + self.input_map[f"{layer_name}_bias"] = param[1].data + self.caffe_net.bottom_names.get(layer_name).append(f"{layer_name}_weight") + self.caffe_net.bottom_names.get(layer_name).append(f"{layer_name}_bias") + self.input_map = {**self.input_map, **self.output_map} + + def _get_output_map(self): + for layer_name, blob in self.caffe_net.blobs.items(): + self.output_map[layer_name] = blob.data + + def _capture_caffe_net(self, ret, *args, **kwargs): + self.caffe_net = ret + return ret diff --git a/accuracy_tools/msprobe/core/components/dumper_offline_model.py b/accuracy_tools/msprobe/core/components/dumper_offline_model.py new file mode 100644 index 0000000000000000000000000000000000000000..8a3db6a073b7587cafba4f83333a6f8c6ba0e609 --- /dev/null +++ b/accuracy_tools/msprobe/core/components/dumper_offline_model.py @@ -0,0 +1,20 @@ +# Copyright (c) 2025-2025 Huawei Technologies Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from msprobe.base import BaseComponent + + +class OfflineModelActuatorComp(BaseComponent): + def __init__(self, priority=100): + super().__init__(priority) diff --git a/accuracy_tools/msprobe/core/components/dumper_om.py b/accuracy_tools/msprobe/core/components/dumper_om.py new file mode 100644 index 0000000000000000000000000000000000000000..d06b7b0caa23a9faa91dfaa171769b51093b6a7e --- /dev/null +++ b/accuracy_tools/msprobe/core/components/dumper_om.py @@ -0,0 +1,35 @@ +# Copyright (c) 2025-2025 Huawei Technologies Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from msprobe.base import Component +from msprobe.core.components.dumper_offline_model import OfflineModelActuatorComp +from msprobe.core.dump import OmModelActuator +from msprobe.core.dump.acl_manager import acl_device_manager +from msprobe.utils.constants import CompConst + + +@Component.register(CompConst.OM_ACTUATOR_COMP) +class OmActuatorComp(OfflineModelActuatorComp): + def __init__(self, priority, model_path, input_shape, input_path, **kwargs): + super().__init__(priority) + self.actuator = OmModelActuator(model_path, input_shape, input_path, **kwargs) + self.acl_resource_manager = acl_device_manager.get_acl_resource_manager(kwargs.get("rank", 0)) + + def activate(self, *args, **kwargs): + self.acl_resource_manager.initialize() + self.actuator.load_model() + inputs_tensor_info = self.actuator.get_input_tensor_info() + input_map = self.actuator.get_inputs_data(inputs_tensor_info, is_byte_data=True) + self.actuator.infer(input_map) + self.actuator.convert_om2json() diff --git a/accuracy_tools/msprobe/core/components/dumper_onnx.py b/accuracy_tools/msprobe/core/components/dumper_onnx.py new file mode 100644 index 0000000000000000000000000000000000000000..dd8015716cd09ac8b6e391455cff21b10a0dc81d --- /dev/null +++ b/accuracy_tools/msprobe/core/components/dumper_onnx.py @@ -0,0 +1,161 @@ +# Copyright (c) 2025-2025 Huawei Technologies Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np + +from msprobe.base import Component, ProducerComp +from msprobe.core.base import BaseDumper +from msprobe.core.components.dumper_offline_model import OfflineModelActuatorComp +from msprobe.core.dump import OnnxModelActuator +from msprobe.utils.constants import CompConst, DumpConst, MsgConst +from msprobe.utils.exceptions import MsprobeException +from msprobe.utils.hijack import ActionType, hijacker +from msprobe.utils.io import load_npy_from_buffer + +_ONNX_DTYPE = {1: np.float32, 2: np.float64} +_SECOND_CALL = 1 +_FIRST_PARAM = 0 + + +@Component.register(CompConst.ONNX_ACTUATOR_COMP) +class OnnxActuatorComp(OfflineModelActuatorComp): + def __init__(self, priority, model_path, input_shape, input_path, **kwargs): + super().__init__(priority) + self.actuator = OnnxModelActuator(model_path, input_shape, input_path, **kwargs) + + def activate(self, *args, **kwargs): + self.actuator.load_model() + inputs_tensor_info = self.actuator.get_input_tensor_info() + input_map = self.actuator.get_inputs_data(inputs_tensor_info) + uninfer_model_path = self.actuator.export_uninfer_model() + _ = self.actuator.infer(uninfer_model_path, input_map) + + +@Component.register(CompConst.ONNX_DUMPER_COMP) +class OnnxDumperComp(ProducerComp, BaseDumper): + def __init__(self, priority, data_mode): + ProducerComp.__init__(self, priority) + BaseDumper.__init__(self, data_mode) + self.output_list = [] + self.origin_model = None + self.model_session = None + + def activate(self, *args, **kwargs): + self.register_hook() + + def deactivate(self, *args, **kwargs): + self.release_hook() + + def register_hook(self): + self.handler_session = hijacker( + stub=self._capture_model_session, + module="onnxruntime", + cls="InferenceSession", + function="__init__", + action=ActionType.PRE_HOOK, + priority=10, + ) + self.handler.append(self.handler_session) + self.handler.append( + hijacker( + stub=self._capture_input_map, + module="onnxruntime", + cls="InferenceSession", + function="run", + action=ActionType.PRE_HOOK, + priority=10, + ) + ) + self.handler.append( + hijacker( + stub=self._capture_output_list, + module="onnxruntime", + cls="InferenceSession", + function="run", + action=ActionType.POST_HOOK, + priority=20, + ) + ) + self.handler.append( + hijacker( + stub=self._capture_origin_model, + module="onnx", + function="load_model", + action=ActionType.POST_HOOK, + priority=20, + ) + ) + + def load_data(self): + if self._data_iter is None: + self._get_model_session() + self._get_input_output_map() + self._data_iter = self._summ_dump_data() + try: + return next(self._data_iter) + except StopIteration: + return None + + def _get_output_map(self): + res_idx = 0 + for node in self.origin_model.graph.node: + for node_output in node.output: + self.output_map[node_output] = self.output_list[res_idx] + res_idx += 1 + + def _augment_input_map(self): + for temp in self.origin_model.graph.initializer: + npy_data = load_npy_from_buffer(temp.raw_data, _ONNX_DTYPE.get(temp.data_type), temp.dims) + self.input_map[temp.name] = npy_data + self.input_map = {**self.input_map, **self.output_map} + + def _get_input_output_map(self): + self._get_output_map() + self._augment_input_map() + + def _summ_dump_data(self): + net_output_nodes = [item.name for item in self.model_session.get_outputs()] + for node in self.origin_model.graph.node: + self.data_for_save.setdefault(node.name, {}) + if any(x in self.data_mode for x in DumpConst.INPUT_ALL): + input_data = self.through_nodes(node.input, node.name, DumpConst.INPUT_ARGS, self.input_map) + for item in input_data: + yield item, net_output_nodes + if any(x in self.data_mode for x in DumpConst.OUTPUT_ALL): + output_data = self.through_nodes(node.output, node.name, DumpConst.OUTPUT_ARGS, self.output_map) + for item in output_data: + yield item, net_output_nodes + + def _get_model_session(self): + try: + self.model_session = self.handler_session.call_data.get(_SECOND_CALL).get("args")[_FIRST_PARAM] + except Exception as e: + raise MsprobeException( + MsgConst.VALUE_NOT_FOUND, "The hook function failed to capture the model_session." + ) from e + + def _capture_model_session(self, *args, **kwargs): + return args, kwargs + + def _capture_input_map(self, *args, **kwargs): + self.input_map = args[2] + return args, kwargs + + def _capture_output_list(self, output, *args, **kwargs): + self.output_list = output + return output + + def _capture_origin_model(self, origin_model, *args, **kwargs): + self.origin_model = origin_model + return origin_model diff --git a/accuracy_tools/msprobe/core/components/dumper_tf.py b/accuracy_tools/msprobe/core/components/dumper_tf.py new file mode 100644 index 0000000000000000000000000000000000000000..63d04693a8fe16eeebe083a2928298393b8a9edd --- /dev/null +++ b/accuracy_tools/msprobe/core/components/dumper_tf.py @@ -0,0 +1,164 @@ +# Copyright (c) 2025-2025 Huawei Technologies Co., Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from msprobe.base import BaseComponent, Component, ProducerComp +from msprobe.core.base import BaseDumper +from msprobe.core.components.dumper_offline_model import OfflineModelActuatorComp +from msprobe.core.dump import FrozenGraphActuatorCPU, FrozenGraphActuatorNPU +from msprobe.utils.constants import CompConst, DumpConst +from msprobe.utils.env import evars +from msprobe.utils.hijack import ActionType, hijacker +from msprobe.utils.log import logger +from msprobe.utils.toolkits import get_net_output_nodes_from_graph_def + + +@Component.register(CompConst.FROZEN_GRAPH_ACTUATOR_COMP_CPU) +class FrozenGraphActuatorCompCPU(OfflineModelActuatorComp): + def __init__(self, priority, model_path, input_shape, input_path, **kwargs): + super().__init__(priority) + self.actuator = FrozenGraphActuatorCPU(model_path, input_shape, input_path, **kwargs) + + def activate(self, *args, **kwargs): + self.actuator.load_model() + inputs_tensor_info = self.actuator.get_input_tensor_info() + input_map = self.actuator.get_inputs_data(inputs_tensor_info) + _ = self.actuator.infer(input_map) + + +@Component.register(CompConst.FROZEN_GRAPH_DUMPER_COMP_CPU) +class FrozenGraphDumperCompCPU(ProducerComp, BaseDumper): + def __init__(self, priority, data_mode): + ProducerComp.__init__(self, priority) + BaseDumper.__init__(self, data_mode) + self.graph_def = None + self.infer_output = [] + self.tf_ops = [] + + def activate(self, *args, **kwargs): + self.register_hook() + + def deactivate(self, *args, **kwargs): + self.release_hook() + + def register_hook(self): + self.handler.append( + hijacker( + stub=self._capture_graph_def, + module="tensorflow.python.framework.importer", + function="_import_graph_def_internal", + action=ActionType.PRE_HOOK, + priority=20, + ) + ) + self.handler.append( + hijacker( + stub=self._capture_tf_ops, + module="tensorflow.python.client.session", + cls="Session", + function="run", + action=ActionType.PRE_HOOK, + priority=20, + ) + ) + self.handler.append( + hijacker( + stub=self._capture_output, + module="tensorflow.python.client.session", + cls="Session", + function="run", + action=ActionType.POST_HOOK, + priority=25, + ) + ) + + def load_data(self): + if self._data_iter is None: + self._get_input_output_map() + self._data_iter = self._summ_dump_data() + try: + return next(self._data_iter) + except StopIteration: + return None + + def _get_input_output_map(self): + self._get_output_map() + self._get_input_map() + + def _get_output_map(self): + self.output_map = {tensor.name: result for tensor, result in zip(self.tf_ops, self.infer_output)} + + def _get_input_map(self): + node_names = [tensor.op.name for tensor in self.tf_ops] + for idx, node_name in enumerate(node_names): + for input_tensor in self.tf_ops[idx].op.inputs: + input_data = self.output_map.get(input_tensor.name) + if input_data is None: + logger.warning(f"Input {input_tensor.name} for {node_name} not found.") + continue + self.input_map[input_tensor.name] = input_data + + def _summ_dump_data(self): + net_output_nodes = get_net_output_nodes_from_graph_def(self.graph_def) + for node in self.tf_ops: + self.data_for_save.setdefault(node.name, {}) + if any(x in self.data_mode for x in DumpConst.INPUT_ALL): + input_data = self.through_nodes(node.op.inputs, node.name, DumpConst.INPUT_ARGS, self.input_map) + for item in input_data: + yield item, net_output_nodes + if any(x in self.data_mode for x in DumpConst.OUTPUT_ALL): + output_data = self.through_nodes(node.op.outputs, node.name, DumpConst.OUTPUT_ARGS, self.output_map) + for item in output_data: + yield item, net_output_nodes + + def _capture_graph_def(self, *args, **kwargs): + self.graph_def = args[0] + return args, kwargs + + def _capture_tf_ops(self, *args, **kwargs): + self.tf_ops = args[1] + return args, kwargs + + def _capture_output(self, output, *args, **kwargs): + self.infer_output = output + return output + + +@Component.register(CompConst.FROZEN_GRAPH_ACTUATOR_COMP_NPU) +class FrozenGraphActuatorCompNPU(OfflineModelActuatorComp): + def __init__(self, priority, model_path, input_shape, input_path, **kwargs): + super().__init__(priority) + self.actuator = FrozenGraphActuatorNPU(model_path, input_shape, input_path, **kwargs) + + def activate(self, *args, **kwargs): + self.actuator.load_model() + inputs_tensor_info = self.actuator.get_input_tensor_info() + input_map = self.actuator.get_inputs_data(inputs_tensor_info) + _ = self.actuator.infer(input_map) + self.actuator.convert_txt2json() + + +@Component.register(CompConst.FROZEN_GRAPH_SET_GE_COMP_NPU) +class FrozenGraphSetGECompNPU(BaseComponent): + def __init__(self, priority, work_path, dump_ge_graph, dump_graph_level, dump_graph_path): + super().__init__(priority) + self.work_path = work_path + self.dump_ge_graph = dump_ge_graph + self.dump_graph_level = dump_graph_level + self.dump_graph_path = dump_graph_path + + def activate(self, *args, **kwargs): + evars.set(DumpConst.ENVVAR_ASCEND_WORK_PATH, self.work_path) + evars.set(DumpConst.ENVVAR_DUMP_GE_GRAPH, self.dump_ge_graph) + evars.set(DumpConst.ENVVAR_DUMP_GRAPH_LEVEL, self.dump_graph_level) + evars.set(DumpConst.ENVVAR_DUMP_GRAPH_PATH, self.dump_graph_path)