-
Notifications
You must be signed in to change notification settings - Fork 233
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
[Feature] Refactor Estimator for computing FLOPs/Params/Latency. #230
Conversation
1. add EvaluatorLoop in engine.runners; 2. add estimator for structures (both subnet & supernet); 3. add layer_counter for each op.
Codecov Report
@@ Coverage Diff @@
## dev-1.x #230 +/- ##
==========================================
- Coverage 0.48% 0.44% -0.04%
==========================================
Files 144 159 +15
Lines 5943 6454 +511
Branches 959 1059 +100
==========================================
Hits 29 29
- Misses 5909 6420 +511
Partials 5 5
Flags with carried forward coverage won't be shown. Click here to find out more.
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. |
mmrazor/registry/registry.py
Outdated
@@ -111,3 +111,6 @@ def build_razor_model_from_cfg( | |||
VISUALIZERS = Registry('visualizer', parent=MMENGINE_VISUALIZERS) | |||
# manage visualizer backend | |||
VISBACKENDS = Registry('vis_backend', parent=MMENGINE_VISBACKENDS) | |||
|
|||
ESTIMATOR = Registry('estimator') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ESTIMATOR -> ESTIMATORS
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
if (i + 1) == max_iter: | ||
fps = (i + 1 - num_warmup) / pure_inf_time | ||
if PRINT: | ||
print( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use logger to print, with debug logger level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
mean_times_pre_image_ = sum(times_pre_image_list_) / len( | ||
times_pre_image_list_) | ||
if PRINT: | ||
print( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use logger to print, with debug logger level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
@ESTIMATOR.register_module() | ||
class BaseEstimator(metaclass=ABCMeta): | ||
"""Evaluator for calculating the accuracy and resources consume. Accuracy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
update docstring, including necessary Notes
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add docstring in ResourceEstimator, showing 3 cases when using it.
self.units = units | ||
self.disabled_counters = disabled_counters | ||
|
||
def evaluate( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
evaluate
-> estimate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
1. add ResourceEstimator based on BaseEstimator; 2. add notes & examples for ResourceEstimator & EvaluatorLoop usage; 3. fix a bug of latency test. 4. minor changes according to comments.
return resource_results | ||
|
||
def export_subnet(self, model): | ||
"""Export current best subnet.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
update docstring
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
|
||
@LOOPS.register_module() | ||
class EvaluatorLoop(ValLoop): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-> ResourceEvaluatorLoop would be better ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall the file name be changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, and so do the releated UTs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
return resource_results | ||
|
||
def export_subnet(self, model): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is export_subnet
sutable for all the NAS alogorithm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is called when it comes to those NAS algorithms that require building a supernet for training. For those algorithms, measuring subnet resources is more meaningful than supernet during validation, therefore this method is required to get the current searched subnet from the supernet.
@@ -93,12 +93,12 @@ class FlopsEstimator: | |||
def get_model_complexity_info( | |||
model: Module, | |||
fix_mutable: Optional[ValidFixMutable] = None, | |||
input_shape: Iterable[int] = (3, 224, 224), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
delete the directory: subnet/estimators and update the corresponding refs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
{'flops': 1.0, 'params': 0.7, 'latency': 0.0} | ||
|
||
>>> # calculate mmrazor.model flops | ||
NOTE: check 'EvaluatorLoop' in engine.runner.evaluator_val_loop |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add more details for disabled_counters
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
from abc import ABCMeta, abstractclassmethod | ||
|
||
|
||
class BaseCounter(object, metaclass=ABCMeta): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Point that XXModuleCounter
is responsible for XXModule
, which could refers to flops_params_counter::get_counter_type()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
08e9445
to
91bab7c
Compare
91bab7c
to
15365df
Compare
0dfe636
to
bc3ed91
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- It seems to lack the function of counting flops with the specified scope.
- Better not use new registries without parents, it will be not used by other repos of OpenMMLab. You can use directly TASK_UTILS instead of
estimator
andop_counter
estimator/
is unsuitable to be understructures/
. Suggestion location :mmrazor/models/task_modules/
.- The file structure of
estimator/
could be optimizer. Suggestion:
a. addcouters/
inestimator
b. moveflops_params_counter.py
,latency.py
,op_spec_counters/
tocounters/
c. renamelatency.py
tolatency_counter.py
d. renameestimator/
toestimators/
mmrazor/registry/registry.py
Outdated
@@ -111,3 +111,6 @@ def build_razor_model_from_cfg( | |||
VISUALIZERS = Registry('visualizer', parent=MMENGINE_VISUALIZERS) | |||
# manage visualizer backend | |||
VISBACKENDS = Registry('vis_backend', parent=MMENGINE_VISBACKENDS) | |||
|
|||
ESTIMATORS = Registry('estimator') | |||
OP_SPEC_COUNTERS = Registry('op_counter') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better not use new registries without parents, it will be not used by other repos of OpenMMLab. You can use directly TASK_UTILS instead of 'estimator' and 'op_counter'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
>>> # calculate resources of mmrazor.models | ||
NOTE: check 'ResourceEvaluatorLoop' in | ||
engine.runner.resource_evaluator_val_loop for more details. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
engine.runner.resource_evaluator_val_loop -> mmrazor.engine.runner.resource_evaluator_val_loop
to avoid ambiguity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
|
||
@LOOPS.register_module() | ||
class ResourceEvaluatorLoop(ValLoop): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This loop should be for a specific algorithm, you had better name it with the algorithm. It is easy to be misunderstood that ResourceEvaluatorLoop is universal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ResourceEvaluatorLoop seems to be replaced with Hook, thus we need not maintain source valloop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make this part a hook, done.
Now support counting flops with the specified scope. A list of scope names is required from users. |
copied_model = copy.deepcopy(self.model) | ||
load_fix_subnet(copied_model, fix_mutable) | ||
|
||
estimator = ResourceEstimator() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use estimator_cfg to build ResourceEstimator, and not use input_shape
as fixed kwargs for ::estimate()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
copied_model = copy.deepcopy(self.model) | ||
load_fix_subnet(copied_model, fix_mutable) | ||
|
||
estimator = ResourceEstimator() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use estimator_cfg to build ResourceEstimator, and not use input_shape
as fixed kwargs for ::estimate
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
def get_model_complexity_info(model, | ||
input_shape, | ||
spec_modules=[], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spec_modules -> custom_keys to support prefix, ref to mmcv::mmcv/runner/optimizer/default_constuctor.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now support counting flops with a specified scope, e.g. spec_modules = ['backbone']
if len(spec_modules): | ||
spec_modules_resources = dict() | ||
accumulate_sub_module_flops_params(flops_params_model) | ||
for name, module in flops_params_model.architecture.named_modules(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not all the flops_params_model have the architecture
attribute.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
precision=precision)) + ' ' + units + 'FLOPs' | ||
params_string = str( | ||
params_units_convert( | ||
accumulated_num_params, units='M', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unify accumulated_flops_cost and accumulated_num_params with units
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A unit pair with FLOPs as 'G' and params as 'M' may be better.
import sys | ||
from functools import partial | ||
|
||
import torch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unify units
for params and flops under the scope of flops_params_counter.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cancel unit convert in accumulate_sub_module_flops_params, remain the rest as the origin version.
Thanks for your contribution and we appreciate it a lot. The following instructions would make your pull request more healthy and more easily get feedback. If you do not understand some items, don't worry, just make the pull request and seek help from maintainers.
Motivation
Refactor Estimator for computing FLOPs/Params/Latency.
Modification
ResourceEstimator
to estimate model resources.mmcv.flops_counter
asflops_params_counter
.latency_counter
.ConvCounter
.EstimateResourcesHook
.flops_params_counter
&ResourceEstimator
.FlopsEstimator
in mmrazor.BC-breaking (Optional)
Does the modification introduce changes that break the backward compatibility of the downstream repositories?
If so, please describe how it breaks the compatibility and how the downstream projects should modify their code to keep compatibility with this PR.
Use cases (Optional)
If this PR introduces a new feature, it is better to list some use cases here and update the documentation.
Checklist
Before PR:
After PR: