Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fixed]: Issues resolved raised by mypy for issue Make #22313 #2438

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from

Conversation

AtharvaPore01
Copy link

Changes

Missing Type Annotations:

  • Many errors stem from functions or variables lacking explicit type annotations.

Incorrect Function Signatures:

  • Some errors indicate mismatches between function arguments, return types, or overridden methods.

List and Array Operations:

  • Errors involve incorrect usage of lists or arrays, such as incompatible types or invalid operations.

Missing Return Statements:

  • Functions marked as returning a value must return something.

Missing Type Annotations

  • A significant number of functions are missing type annotations. This is indicated by errors like Function is missing a type annotation for one or more arguments [no-untyped-def].

Incorrect or Missing Implementation of __eq__ Method:

  • There were several errors related to the implementation of the __eq__ method in different classes. The method's signature must accept an argument of type object and then check if the passed object is of the expected type.

Type Argument Issues in Generic Types:

  • Errors like Missing type parameters for generic type suggest that you are using generic types (like ndarray, deque, List) without specifying their type parameters.

Incompatible Types and Assignments:

  • There were various errors where the types of variables or return types didn't match what was expected. This can be due to incorrect assignment or returning the wrong type from a function.

Mypy-specific Issues and Recommendations:

  • Some errors were specific to mypy, a static type checker, which includes recommendations on how to make the code compliant with type checking, such as adhering to the expected method signatures in subclasses.

Miscellaneous Errors:

  • These include issues like methods missing return type annotations (even when they don't return any value, -> None should be used), the use of a function as a type that is invalid, and a method in a subclass that does not return a value, contradicting the superclass's method signature.

Other Issues:

  • A few errors relate to missing attributes or incorrect usage of specific functions.

Reason for changes

As stated in issue #22313, the main reason behind solving the errors above as they were raised by running the mypy.ini file.

Ticket Number:

#22313

@AtharvaPore01 AtharvaPore01 requested a review from a team as a code owner January 31, 2024 01:27
@github-actions github-actions bot added the NNCF Common Pull request that updates NNCF Common label Jan 31, 2024
Copy link

codecov bot commented Jan 31, 2024

Codecov Report

Attention: 140 lines in your changes are missing coverage. Please review.

Comparison is base (a7b67f4) 90.55% compared to head (d4a3bdc) 3.88%.
Report is 40 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   openvinotoolkit/openvino#2438       +/-   ##
===========================================
- Coverage    90.55%   3.88%   -86.68%     
===========================================
  Files          493     498        +5     
  Lines        44994   45519      +525     
===========================================
- Hits         40746    1767    -38979     
- Misses        4248   43752    +39504     
Files Coverage Δ
nncf/common/graph/transformations/commands.py 75.00% <0.00%> (-22.02%) ⬇️
nncf/common/logging/track_progress.py 0.00% <0.00%> (-87.94%) ⬇️
nncf/common/tensor_statistics/reduction.py 0.00% <0.00%> (-100.00%) ⬇️
nncf/common/tensor_statistics/aggregator.py 15.25% <0.00%> (-84.75%) ⬇️
nncf/common/tensor_statistics/statistic_point.py 6.25% <20.00%> (-85.24%) ⬇️
nncf/common/tensor_statistics/statistics.py 0.00% <0.00%> (-89.10%) ⬇️
nncf/common/tensor_statistics/collectors.py 2.06% <2.19%> (-86.91%) ⬇️

... and 441 files with indirect coverage changes

Flag Coverage Δ
COMMON ?
ONNX ?
OPENVINO ?
TENSORFLOW 3.88% <2.77%> (-26.31%) ⬇️
TORCH ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
common 10.97% <2.77%> (-82.34%) ⬇️
torch 0.00% <ø> (-92.95%) ⬇️
tensorflow 0.00% <ø> (-94.00%) ⬇️
onnx 0.00% <ø> (-95.71%) ⬇️
openvino 0.00% <ø> (-91.60%) ⬇️
ptq 4.55% <ø> (-83.92%) ⬇️

@@ -140,6 +140,7 @@ def __init__(self, target_type: TargetType):
:param target_type: Type of the target point.
"""
self._target_type = target_type
self.target_node_name: str = ""
Copy link
Collaborator

@daniil-lyakhov daniil-lyakhov Jan 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

target_node_name parameter is valid for all inherited classes except TFTargetPoint. I see three possible solutions here:

  1. Do not inheret TFTargetPoint from the TargetPoint class and implement target_node_name attribute as a required init attribute.
  2. Make an interface for a target points classes with target_node_name attribute and use it in correspondent typehints
  3. Rename layer_name and op_name to target_node_name in the TFTargetPoints.

I suggest to implement the third solution
CC: @alexsu52

@@ -50,21 +49,15 @@ def collect_statistics(self, model: TModel, graph: NNCFGraph) -> None:

merged_statistics = self._get_merged_statistic_points(self.statistic_points, model, graph)
transformation_layout = self._get_transformation_layout_extra_outputs(merged_statistics)
model_with_outputs = model_transformer.transform(transformation_layout)
model_with_outputs: ModelTransformer = model_transformer.transform(transformation_layout)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
model_with_outputs: ModelTransformer = model_transformer.transform(transformation_layout)
model_with_outputs: TModel = model_transformer.transform(transformation_layout)

Comment on lines 83 to 86
for (
_,
tensor_collectors,
) in _statistic_point.algorithm_to_tensor_collectors.items():
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (
_,
tensor_collectors,
) in _statistic_point.algorithm_to_tensor_collectors.items():
for tensor_collectors in _statistic_point.algorithm_to_tensor_collectors.values():

Comment on lines -56 to -61
dataset_length = self.dataset.get_length()
total = (
min(dataset_length or self.stat_subset_size, self.stat_subset_size)
if self.stat_subset_size is not None
else None
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was logic before: if self.stat_subset_size is None then subset size is equal to the length of the whole dataset. As default value is 0 for self.stat_subset_size and this check is gone, I don't see the place where this logic is implemented. Could you please explain where is it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic to set self.stat_subset_size to the length of the whole dataset if it is None is not explicitly present. However, there are steps taken during the registration of statistic points that indirectly handle the calculation of self.stat_subset_size.

Screenshot 2024-02-12 at 3 44 35 PM

In this part of the code, it iterates over the registered statistic points and their associated tensor collectors. For each tensor collector, it checks if self.stat_subset_size is None. If it is None, it sets self.stat_subset_size to the num_samples attribute of the tensor collector.
If self.stat_subset_size is not None, it compares it with the num_samples attribute of the tensor collector and updates self.stat_subset_size to be the maximum of the two values.
This logic effectively sets self.stat_subset_size based on the maximum number of samples encountered among all tensor collectors associated with the registered statistic points. While it doesn't directly set it to the length of the whole dataset if self.stat_subset_size is None, it achieves a similar effect by considering the maximum number of samples encountered during registration.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got you. But despite the fact that this should work in every case, I prefer explicit double check



class TensorStatisticCollectorBase(ABC):
"""Collector estimate statistics at the quantization point based on the provided reduction shape."""

def __init__(self, reduction_shape: Optional[ReductionAxes] = None, num_samples: Optional[int] = None):
def __init__(self, reduction_shape: ReductionAxes = None, num_samples: Optional[int] = None):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why Optional gone?

total=total,
description="Statistics collection",
):
data_iterable = iter([self.dataset.get_inference_data()])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep track call over here

Comment on lines +56 to +59
for input_data in data_iterable:
outputs = engine.infer(input_data)
processed_outputs = self._process_outputs(outputs)
self._register_statistics(processed_outputs, merged_statistics)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code duplicate

Comment on lines +82 to 83
for tensor_collectors in _statistic_point.algorithm_to_tensor_collectors.values():
for tensor_collector in tensor_collectors:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a real issue?

return self._num_samples

def register_input(self, x: TensorType) -> TensorType:
def register_input(self, x: torch.Tensor) -> torch.Tensor:
Copy link
Collaborator

@daniil-lyakhov daniil-lyakhov Feb 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

torch.Tensor in common code, common code should use only common types

@@ -266,7 +298,7 @@ def squeeze(x: NNCFTensor, dim: Optional[Union[int, Tuple[int, ...]]] = None) ->

@staticmethod
@abstractmethod
def sum(tensor: NNCFTensor) -> TensorElementsType:
def sum(tensor: TensorElementsType) -> TensorElementsType:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def sum(tensor: TensorElementsType) -> TensorElementsType:
def sum(tensor: NNCFTensor) -> TensorElementsType:

Comment on lines +451 to +457
def _register_input_common(self, x: NNCFTensor) -> None:
if self._tensor_processor is not None:
min_reduced: None = self._tensor_processor.reduce_min(x, self._reduction_shape)
if self._use_abs_max and self._tensor_processor is not None:
x = self._tensor_processor.abs(x)
max_reduced = self._tensor_processor.reduce_max(x, self._reduction_shape)
if self._tensor_processor is not None:
max_reduced: None = self._tensor_processor.reduce_max(x, self._reduction_shape)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refactor it. And please remove None typehint

Comment on lines +501 to +509
def _register_input_common(self, x: NNCFTensor) -> None:
if self._tensor_processor is not None:
min_reduced: int = self._tensor_processor.reduce_min(x, self._reduction_shape)
if self._use_abs_max and self._tensor_processor is not None:
x = self._tensor_processor.abs(x)
max_reduced = self._tensor_processor.reduce_max(x, self._reduction_shape)
if self._tensor_processor is not None:
max_reduced: int = self._tensor_processor.reduce_max(x, self._reduction_shape)

if self._use_per_sample_stats:
if self._use_per_sample_stats and self._tensor_processor is not None:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refactor it. And please remove None typehint

Comment on lines +549 to +562
if self._tensor_processor is not None:
stacked_min = self._tensor_processor.stack(self._all_min_values)
if self._use_means_of_mins and self._tensor_processor is not None:
return self._tensor_processor.mean(stacked_min, axis=0)
return self._tensor_processor.reduce_min(stacked_min, axis=0)
if self._tensor_processor is not None:
return self._tensor_processor.reduce_min(stacked_min, axis=0)

def _max_aggregate(self):
stacked_max = self._tensor_processor.stack(self._all_max_values)
if self._use_means_of_maxs:
def _max_aggregate(self) -> None:
if self._tensor_processor is not None:
stacked_max = self._tensor_processor.stack(self._all_max_values)
if self._use_means_of_maxs and self._tensor_processor is not None:
return self._tensor_processor.mean(stacked_max, axis=0)
return self._tensor_processor.reduce_max(stacked_max, axis=0)
if self._tensor_processor is not None:
return self._tensor_processor.reduce_max(stacked_max, axis=0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refactor it. And please remove None typehint

Comment on lines +571 to +574
if self._tensor_processor is not None:
stacked_min = self._tensor_processor.stack(self._all_min_values)
if self._tensor_processor is not None:
return self._tensor_processor.mean(stacked_min, axis=0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this if self._tensor_processor is not None: should be done in a different place

@mlukasze
Copy link

hey @AtharvaPore01
do you have time to finish this PR? Or should we move it back to the pool?

@AtharvaPore01
Copy link
Author

@mlukasze Hi, Sorry for the delay from my side. I appreciate your patience. I'm currently tied up with some urgent tasks but will prioritize completing the PR as soon as possible. Your understanding is greatly appreciated.

@MaximProshin
Copy link
Collaborator

@AtharvaPore01 , do you plan to continue to work on this PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NNCF Common Pull request that updates NNCF Common
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants