Skip to content

Commit

Permalink
Logger update (#995)
Browse files Browse the repository at this point in the history
MCT Logger refactor: add missing loggings, improve logger messages and remove redundant logging types from the logger class

---------

Co-authored-by: liord <lior.dikstein@altair-semi.com>
Co-authored-by: Ofir Gordon <Ofir.Gordon@altair-semi.com>
  • Loading branch information
3 people committed Mar 21, 2024
1 parent 1409fdc commit 4162f2e
Show file tree
Hide file tree
Showing 136 changed files with 450 additions and 530 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,5 @@ def validate_data_correctness(self):
"""

if not self.is_legal:
Logger.exception(f'{self.__class__.__name__} was manipulated per-channel,'
'but collected per-tensor. Data is invalid.') # pragma: no cover
Logger.critical('The data is invalid.'
f'{self.__class__.__name__} was collected per-tensor but received data manipulated per-channel.') # pragma: no cover
6 changes: 3 additions & 3 deletions model_compression_toolkit/core/common/data_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,16 @@ def __init__(self,

self.folder = folder
self.image_list = []
print(f"Starting Scanning Disk: {self.folder}")
Logger.info(f"Starting Scanning Disk: {self.folder}")
for root, dirs, files in os.walk(self.folder):
for file in files:
file_type = file.split('.')[-1].lower()
if file_type in file_types:
self.image_list.append(os.path.join(root, file))
self.n_files = len(self.image_list)
if self.n_files == 0:
Logger.error(f"No files of type: {FILETYPES} are found!") # pragma: no cover
print(f"Finished Disk Scanning: Found {self.n_files} files")
Logger.critical(f"Expected files of type {FILETYPES}. No files of type {FILETYPES} were found.") # pragma: no cover
Logger.info(f"Finished Disk Scanning: Found {self.n_files} files")
self.preprocessing = preprocessing
self.batch_size = batch_size

Expand Down
23 changes: 10 additions & 13 deletions model_compression_toolkit/core/common/graph/base_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,10 @@ def set_tpc(self,
for filtered_layer in tpc_filtered_layers])
if n.is_custom:
if not is_node_in_tpc:
Logger.error(f'MCT does not support optimizing Keras custom layers, but found layer of type {n.type}. '
f'Please add the custom layer to TPC or file a feature request or an issue if you believe this is an issue.')
Logger.critical(f'MCT does not support optimizing Keras custom layers. Found a layer of type {n.type}. '
f' Please add the custom layer to Target Platform Capabilities (TPC), or file a feature request or an issue if you believe this should be supported.')
if any([qc.default_weight_attr_config.enable_weights_quantization for qc in n.get_qco(tpc).quantization_config_list]):
Logger.error(f'MCT does not support optimizing Keras custom layers with weights quantization. Layer: {n.type}')
Logger.critical(f'Layer identified: {n.type}. MCT does not support weight quantization for Keras custom layers.')

self.tpc = tpc

Expand Down Expand Up @@ -231,7 +231,7 @@ def get_in_stats_collector(self,

sc = self.node_to_in_stats_collector.get(n)
if sc is None:
Logger.error(f'Input statistics collector of node {n.name} is None') # pragma: no cover
Logger.critical(f'No input statistics collector found for node {n.name}.') # pragma: no cover
return sc

def scale_stats_collector(self,
Expand Down Expand Up @@ -370,8 +370,7 @@ def add_node_with_in_edges(self, new_node: BaseNode, input_nodes: List[BaseNode]
input_nodes_output_index = [0] * len(input_nodes)

if len(input_nodes_output_index) != len(input_nodes):
Logger.error('Graph.add_node_with_in_edges: input_nodes & input_nodes_output_index must be the same '
'length') # pragma: no cover
Logger.critical('The number of input nodes and their corresponding output indices must be equal. Found mismatched lengths.') # pragma: no cover

self.add_node(new_node)
for sink_index, (in_node, source_index) in enumerate(zip(input_nodes, input_nodes_output_index)):
Expand Down Expand Up @@ -414,7 +413,7 @@ def replace_input_node(self,
"""
if new_node is None:
Logger.error("Graph received a None value as a new input node.")
Logger.critical("Cannot replace input node with a None value; new input node is required.")

graph_inputs = self.get_inputs()
new_graph_inputs = copy(graph_inputs)
Expand Down Expand Up @@ -442,13 +441,13 @@ def remove_node(self,
if node_to_remove in output_nodes: # If node is in the graph's outputs, the outputs should be updated
if new_graph_outputs is None:
Logger.critical(
f'{node_to_remove.name} is in graph outputs, but new outputs were not given.') # pragma: no cover
f"{node_to_remove.name} is among the graph outputs; however, it cannot be removed without providing a new output.") # pragma: no cover
self.set_outputs(new_graph_outputs)

if node_to_remove in self.get_inputs(): # If node is in the graph's inputs, the inputs should be updated
if new_graph_inputs is None:
Logger.critical(
f'{node_to_remove.name} is in graph inputs, but new inputs were not given.') # pragma: no cover
f'{node_to_remove.name} s among the graph inputs; however, it cannot be removed without providing a new input.') # pragma: no cover
self.set_inputs(new_graph_inputs)

# Make sure there are no connected edges left to the node before removing it.
Expand Down Expand Up @@ -828,14 +827,12 @@ def _create_pruning_section(self, entry_node: BaseNode, fw_impl: Any) -> Pruning
"""
if not fw_impl.is_node_entry_node(entry_node):
Logger.error(f"Expected to find an entry node to create its pruning section,"
f"but node {entry_node} is not an entry node.")
Logger.critical(f"Node {entry_node} is not a valid entry node for creating a pruning section")

intermediate_nodes, exit_node = self._find_intermediate_and_exit_nodes(entry_node, fw_impl)

if not fw_impl.is_node_exit_node(exit_node, entry_node, self.fw_info):
Logger.error(f"Expected to find exit node when creating a pruning section,"
f"but node {exit_node} is not an exit node.")
Logger.critical(f"Node {exit_node} is not a valid exit node for the pruning section starting with {entry_node}.")

return PruningSection(entry_node=entry_node,
intermediate_nodes=intermediate_nodes,
Expand Down
6 changes: 3 additions & 3 deletions model_compression_toolkit/core/common/graph/base_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ def get_qco(self, tpc: TargetPlatformCapabilities) -> QuantizationConfigOptions:
"""

if tpc is None:
Logger.error(f'Can not retrieve QC options for None TPC') # pragma: no cover
Logger.critical(f'Can not retrieve QC options for None TPC') # pragma: no cover

for fl, qco in tpc.filterlayer2qco.items():
if self.is_match_filter_params(fl):
Expand Down Expand Up @@ -617,10 +617,10 @@ def get_simd(self) -> int:
Logger.warning(f"More than one pruning SIMD option is available."
f" Min SIMD is used: {min(simd_list)}")
if len(simd_list) == 0:
Logger.error(f"No SIMD option is available for {self}")
Logger.critical(f"No SIMD option is available for {self}")
_simd = min(simd_list)
if _simd <= 0 or int(_simd) != _simd:
Logger.error(f"SIMD is expected to be a non-positive integer but found: {_simd}")
Logger.critical(f"SIMD is expected to be a non-positive integer but found: {_simd}")
return _simd

def sort_node_candidates(self, fw_info):
Expand Down
3 changes: 2 additions & 1 deletion model_compression_toolkit/core/common/graph/edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from typing import Any, Dict

from model_compression_toolkit.core.common.graph.base_node import BaseNode
from model_compression_toolkit.logger import Logger

# Edge attributes:
EDGE_SOURCE_INDEX = 'source_index'
Expand Down Expand Up @@ -108,4 +109,4 @@ def convert_to_edge(edge: Any) -> Edge:
elif isinstance(edge, Edge): # it's already an Edge and no change need to be done
return edge

raise Exception('Edges list contains an object that is not a known edge format.')
Logger.critical('Edge conversion failed: unrecognized edge format encountered.')
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,8 @@ def _verify_edges(self, edges_list: List[Tuple[Any, Any]]):
edges_list: A list of edges to verify their correction.
"""
for n1, n2 in edges_list:
if n1 in self.a_nodes and n2 in self.a_nodes:
Logger.critical(f"Can't add an edge {(n1, n2)} between two nodes in size A of a bipartite graph.")
if n1 in self.b_nodes and n2 in self.b_nodes:
Logger.critical(f"Can't add an edge {(n1, n2)} between two nodes in size B of a bipartite graph.")
if (n1 in self.a_nodes and n2 in self.a_nodes) or (n1 in self.b_nodes and n2 in self.b_nodes):
Logger.critical(f"Attempted to add edge {(n1, n2)} between nodes of the same partition in a bipartite graph, violating bipartite properties.")

def add_nodes_to_a(self, new_nodes: List[Any]):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def _sample_single_representative_dataset(self, representative_dataset: Callable
"""
images = next(representative_dataset())
if not isinstance(images, list):
Logger.error(f'Images expected to be a list but is of type {type(images)}')
Logger.critical(f'Expected images to be a list; found type: {type(images)}.')

# Ensure each image is a single sample, if not, take the first sample
return [image[0:1, ...] if image.shape[0] != 1 else image for image in images]
Expand Down Expand Up @@ -176,8 +176,7 @@ def _get_request_of_reuse_group(self, trace_hessian_request: TraceHessianRequest
"""
father_nodes = [n for n in self.graph.nodes if not n.reuse and n.reuse_group==trace_hessian_request.target_node.reuse_group]
if len(father_nodes)!=1:
Logger.error(f"Each reused group has a single node in it which is not marked as"
f" reused but found {len(father_nodes)}")
Logger.critical(f"Expected a single non-reused node in the reused group, but found {len(father_nodes)}.")
reused_group_request = TraceHessianRequest(target_node=father_nodes[0],
granularity=trace_hessian_request.granularity,
mode=trace_hessian_request.mode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,19 @@ def __init__(self,

for output_node in graph.get_outputs():
if not fw_impl.is_output_node_compatible_for_hessian_score_computation(output_node.node):
Logger.error(f"All graph outputs should support Hessian computation, but node {output_node.node} "
f"was found with layer type {output_node.node.type}. "
f"Try to run MCT without Hessian info computation.")
Logger.critical(f"All graph outputs must support Hessian score computation. Incompatible node: {output_node.node}, layer type: {output_node.node.type}. Consider disabling Hessian info computation.")

self.input_images = fw_impl.to_tensor(input_images)
self.num_iterations_for_approximation = num_iterations_for_approximation

# Validate representative dataset has same inputs as graph
if len(self.input_images)!=len(graph.get_inputs()):
Logger.error(f"Graph has {len(graph.get_inputs())} inputs, but provided representative dataset returns {len(self.input_images)} inputs")
Logger.critical(f"The graph requires {len(graph.get_inputs())} inputs, but the provided representative dataset contains {len(self.input_images)} inputs.")

# Assert all inputs have a batch size of 1
for image in self.input_images:
if image.shape[0]!=1:
Logger.error(f"Hessian is calculated only for a single image (per input) but input shape is {image.shape}")
Logger.critical(f"Hessian calculations are restricted to a single-image per input. Found input with shape: {image.shape}.")

self.fw_impl = fw_impl
self.hessian_request = trace_hessian_request
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,7 @@ def _get_node_qc_by_bit_widths(node: BaseNode,

return qc

Logger.critical(f'Node {node.name} quantization configuration from configuration file' # pragma: no cover
f' was not found in candidates configurations.')
Logger.critical(f"Quantization configuration for node '{node.name}' not found in candidate configurations.") # pragma: no cover


def _set_node_final_qc(bit_width_cfg: List[int],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,7 @@ def _bops_kpi(mp_cfg: List[int],
# If node doesn't have weights then its MAC count is 0, and we shouldn't consider it in the BOPS count.
incoming_edges = graph.incoming_edges(n, sort_by_attr=EDGE_SINK_INDEX)
if len(incoming_edges) != 1:
Logger.critical(f"Can't compute BOPS metric for node {n.name} with multiple inputs.") # pragma: no cover

Logger.critical(f"Unable to compute BOPS metric for node {n.name} due to multiple inputs.") # pragma: no cover
input_activation_node = incoming_edges[0].source_node
if len(graph.out_edges(input_activation_node)) > 1:
# In the case where the activation node has multiple outgoing edges
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def search_bit_width(graph_to_search_cfg: Graph,

# target_kpi have to be passed. If it was not passed, the facade is not supposed to get here by now.
if target_kpi is None:
Logger.critical('Target KPI have to be passed for search_methods bit-width configuration') # pragma: no cover
Logger.critical("Target KPI is required for the bit-width search method's configuration.") # pragma: no cover

# Set graph for MP search
graph = copy.deepcopy(graph_to_search_cfg) # Copy graph before searching
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,7 @@ def reconstruct_config_from_virtual_graph(self,

if changed_virtual_nodes_idx is not None:
if original_base_config is None:
Logger.critical("Must provide a base original config in order to run config reconstruction for partial"
"set of nodes.") # pragma: no cover
Logger.critical("To run config reconstruction for a partial set of nodes, a base original config must be provided.") # pragma: no cover

updated_virtual_nodes = \
[(idx, self.virtual_graph.get_configurable_sorted_nodes(self.fw_info)[idx]) for idx in changed_virtual_nodes_idx]
Expand Down Expand Up @@ -418,9 +417,7 @@ def reconstruct_node_config(self,
if isinstance(weights_node, VirtualSplitWeightsNode):
self.get_activation_for_split_weights(weights_node, n, virtual_cfg_idx, virtual_mp_cfg)
else:
Logger.error(f"Virtual graph error - all weights nodes should be split to weights and activation nodes"
f"in order to construct the virtual graph, but node {n.name} is not of type "
f"VirtualSplitWeightsNode") # pragma: no cover
Logger.critical(f"Virtual graph construction error: Expected all weights nodes to be split into weights and activation nodes. Found node '{n.name}' not split as expected. Every weights node should correspond to a VirtualSplitWeightsNode type.") # pragma: no cover

activation_node = n.original_activation_node
if isinstance(activation_node, VirtualSplitActivationNode):
Expand All @@ -441,15 +438,13 @@ def reconstruct_node_config(self,
# It's ok, need to find the node's configuration
self.get_activation_for_split_weights(n, n, virtual_cfg_idx, virtual_mp_cfg)
else:
Logger.error(f"Virtual graph error - a weights node is not composed with an activation node,"
f"but its predecessor doesn't have multiple outputs.") # pragma: no cover
Logger.critical(f"Virtual graph configuration error: Expected the predecessor of node '{n.name}' to have multiple outputs when not composed with an activation node.") # pragma: no cover
elif isinstance(n, VirtualSplitActivationNode):
self.get_weights_for_split_activation(n, n, virtual_cfg_idx, virtual_mp_cfg)
else:
# Node didn't change in virtual graph - candidates list is similar to original
if n.name not in self.origin_sorted_conf_nodes_names:
Logger.error(f"Node {n.name} appears in virtual graph as configurable, "
f"but is not configurable in the original graph.") # pragma: no cover
Logger.critical(f"Configuration mismatch: Node '{n.name}' is configurable in the virtual graph but not in the original graph. Verify node configurations.") # pragma: no cover
origin_idx = self.origin_sorted_conf_nodes_names.index(n.name)
self.origin_node_idx_to_cfg[origin_idx] = virtual_cfg_idx

Expand Down Expand Up @@ -654,8 +649,7 @@ def get_weights_for_split_activation(self,
# It's ok, need to find the node's configuration
self.retrieve_weights_activation_config(activation_node, weights_node, virtual_node, virtual_cfg_idx, virtual_mp_cfg)
else:
Logger.error(f"Virtual graph error - a weights node is not composed with an activation node,"
f"but its predecessor doesn't have multiple outputs.") # pragma: no cover
Logger.critical(f"Virtual graph configuration error: Expected the predecessor of node '{n.name}' to have multiple outputs when not composed with an activation node.") # pragma: no cover

def update_config_at_original_idx(self, n: BaseNode, origin_cfg_idx: int):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ def mp_integer_programming_search(search_manager: MixedPrecisionSearchManager,
# bitwidth index to the observed sensitivity of the model when using that bitwidth for that layer.

if target_kpi is None or search_manager is None:
Logger.critical("Can't run mixed precision search with given target_kpi=None or search_manager=None."
"Please provide a valid target_kpi and check the mixed precision parameters values.")
Logger.critical("Invalid parameters: 'target_kpi' and 'search_manager' must not be 'None' for mixed-precision search. Ensure valid inputs are provided.")

layer_to_metrics_mapping = _build_layer_to_metrics_mapping(search_manager, target_kpi)

Expand All @@ -71,7 +70,7 @@ def mp_integer_programming_search(search_manager: MixedPrecisionSearchManager,
lp_problem.solve(solver=solver) # Try to solve the problem.

assert lp_problem.status == LpStatusOptimal, Logger.critical(
"No solution was found during solving the LP problem")
"No solution found for the LP problem.")
Logger.info(LpStatus[lp_problem.status])

# Take the bitwidth index only if its corresponding indicator is one.
Expand Down Expand Up @@ -177,8 +176,7 @@ def _formalize_problem(layer_to_indicator_vars_mapping: Dict[int, Dict[int, LpVa
lp_problem=lp_problem,
non_conf_kpi_vector=non_conf_kpi_vector)
else: # pragma: no cover
raise Logger.critical("Can't run mixed-precision search with given target_kpi=None."
"Please provide a valid target_kpi.")
Logger.critical("Unable to execute mixed-precision search: 'target_kpi' is None. A valid 'target_kpi' is required.")
return lp_problem


Expand Down Expand Up @@ -226,7 +224,7 @@ def _add_set_of_kpi_constraints(search_manager: MixedPrecisionSearchManager,
for v in aggr_kpi:
if isinstance(v, float):
if v > target_kpi_value:
Logger.critical(f"The model can't be quantized to satisfy target KPI {target.value} with value {target_kpi_value}") # pragma: no cover
Logger.critical(f"The model cannot be quantized to meet the specified target KPI ({target.value}) with the value {target_kpi_value}.") # pragma: no cover
else:
lp_problem += v <= target_kpi_value

Expand Down
Loading

0 comments on commit 4162f2e

Please sign in to comment.