mmrazor/docs/en/advanced_guides/algorithm.md

266 lines
8.2 KiB
Markdown
Raw Normal View History

# Algorithm
## Introduction
### What is algorithm in MMRazor
MMRazor is a model compression toolkit, which includes 4 mianstream technologies:
- Neural Architecture Search (NAS)
- Pruning
- Knowledge Distillation (KD)
- Quantization (come soon)
And in MMRazor, `algorithm` is a general item for these technologies. For example, in NAS,
[SPOS](https://github.com/open-mmlab/mmrazor/blob/master/configs/nas/spos)[ ](https://arxiv.org/abs/1904.00420)is an `algorithm`, [CWD](https://github.com/open-mmlab/mmrazor/blob/master/configs/distill/cwd) is also an `algorithm` of knowledge distillation.
`algorithm` is the entrance of `mmrazor/models` . Its role in MMRazor is the same as both `classifier` in [MMClassification](https://github.com/open-mmlab/mmclassification) and `detector` in [MMDetection](https://github.com/open-mmlab/mmdetection).
### About base algorithm
In the directory of `models/algorith``ms`, all model compression algorithms are divided into 4 subdirectories: nas / pruning / distill / quantization. These algorithms must inherit from `BaseAlgorithm`, whose definition is as below.
```Python
from typing import Dict, List, Optional, Tuple, Union
from mmengine.model import BaseModel
from mmrazor.registry import MODELS
LossResults = Dict[str, torch.Tensor]
TensorResults = Union[Tuple[torch.Tensor], torch.Tensor]
PredictResults = List[BaseDataElement]
ForwardResults = Union[LossResults, TensorResults, PredictResults]
@MODELS.register_module()
class BaseAlgorithm(BaseModel):
def __init__(self,
architecture: Union[BaseModel, Dict],
data_preprocessor: Optional[Union[Dict, nn.Module]] = None,
init_cfg: Optional[Dict] = None):
......
super().__init__(data_preprocessor, init_cfg)
self.architecture = architecture
def forward(self,
batch_inputs: torch.Tensor,
data_samples: Optional[List[BaseDataElement]] = None,
mode: str = 'tensor') -> ForwardResults:
if mode == 'loss':
return self.loss(batch_inputs, data_samples)
elif mode == 'tensor':
return self._forward(batch_inputs, data_samples)
elif mode == 'predict':
return self._predict(batch_inputs, data_samples)
else:
raise RuntimeError(f'Invalid mode "{mode}". '
'Only supports loss, predict and tensor mode')
def loss(
self,
batch_inputs: torch.Tensor,
data_samples: Optional[List[BaseDataElement]] = None,
) -> LossResults:
"""Calculate losses from a batch of inputs and data samples."""
return self.architecture(batch_inputs, data_samples, mode='loss')
def _forward(
self,
batch_inputs: torch.Tensor,
data_samples: Optional[List[BaseDataElement]] = None,
) -> TensorResults:
"""Network forward process."""
return self.architecture(batch_inputs, data_samples, mode='tensor')
def _predict(
self,
batch_inputs: torch.Tensor,
data_samples: Optional[List[BaseDataElement]] = None,
) -> PredictResults:
"""Predict results from a batch of inputs and data samples with post-
processing."""
return self.architecture(batch_inputs, data_samples, mode='predict')
```
As you can see from above, `BaseAlgorithm` is inherited from `BaseModel` of MMEngine. `BaseModel` implements the basic functions of the algorithmic model, such as weights initialize,
batch inputs preprocess (see more information in `BaseDataPreprocessor` class of MMEngine), parse losses, and update model parameters. For more details of `BaseModel` , you can see docs for `BaseModel`.
`BaseAlgorithm`'s forward is just a wrapper of `BaseModel`'s forward. Sub-classes inherited from BaseAlgorithm only need to override the `loss` method, which implements the logic to calculate loss, thus various algorithms can be trained in the runner.
## How to use existing algorithms in MMRazor
1. Configure your architecture that will be slimmed
- Use the model config of other repos of OpenMMLab directly as below, which is an example of setting Faster-RCNN as our architecture.
```Python
_base_ = [
'mmdet::_base_/models/faster_rcnn_r50_fpn.py',
]
architecture = _base_.model
```
- Use your customized model as below, which is an example of defining a VGG model as our architecture.
> How to customize architectures can refer to our tutorial: [Customize Architectures](https://mmrazor.readthedocs.io/en/latest/tutorials/Tutorial_3_customize_architectures.html#).
```Python
default_scope='mmcls'
architecture = dict(
type='ImageClassifier',
backbone=dict(type='VGG', depth=11, num_classes=1000),
neck=None,
head=dict(
type='ClsHead',
loss=dict(type='CrossEntropyLoss', loss_weight=1.0),
topk=(1, 5),
))
```
2. Apply the registered algorithm to your architecture.
> The arg name of `algorithm` in config is **model** rather than **algorithm** in order to get better supports of MMCV and MMEngine.
Maybe more args in model need to set according to the used algorithm.
```Python
model = dict(
type='BaseAlgorithm',
architecture=architecture)
```
> About the usage of `Config`, refer to [Config mmcv 1.5.3 documentation](https://mmcv.readthedocs.io/en/latest/understand_mmcv/config.html) please.
3. Apply some custom hooks or loops to your algorithm. (optional)
- Custom hooks
```Python
custom_hooks = [
dict(type='NaiveVisualizationHook', priority='LOWEST'),
]
```
- Custom loops
```Python
_base_ = ['./spos_shufflenet_supernet_8xb128_in1k.py']
# To chose from ['train_cfg', 'val_cfg', 'test_cfg'] based on your loop type
train_cfg = dict(
_delete_=True,
type='mmrazor.EvolutionSearchLoop',
dataloader=_base_.val_dataloader,
evaluator=_base_.val_evaluator)
val_cfg = dict()
test_cfg = dict()
```
## How to customize your algorithm
### Common pipeline
1. Register a new algorithm
Create a new file `mmrazor/models/algorithms/{subdirectory}/xxx.py`
```Python
from mmrazor.models.algorithms import BaseAlgorithm
from mmrazor.registry import MODELS
@MODELS.register_module()
class XXX(BaseAlgorithm):
def __init__(self, architecture):
super().__init__(architecture)
pass
def loss(self, batch_inputs):
pass
```
2. Rewrite its `loss` method.
```Python
from mmrazor.models.algorithms import BaseAlgorithm
from mmrazor.registry import MODELS
@MODELS.register_module()
class XXX(BaseAlgorithm):
def __init__(self, architecture):
super().__init__(architecture)
......
def loss(self, batch_inputs):
......
return LossResults
```
3. Add the remaining functions of the algorithm
> This step is special because of the diversity of algorithms. Some functions of the algorithm may also be implemented in other files.
```Python
from mmrazor.models.algorithms import BaseAlgorithm
from mmrazor.registry import MODELS
@MODELS.register_module()
class XXX(BaseAlgorithm):
def __init__(self, architecture):
super().__init__(architecture)
......
def loss(self, batch_inputs):
......
return LossResults
def aaa(self):
......
def bbb(self):
......
```
4. Import the class
You can add the following line to `mmrazor/models/algorithms/``{subdirectory}/``__init__.py`
```CoffeeScript
from .xxx import XXX
__all__ = ['XXX']
```
In addition, import XXX in `mmrazor/models/algorithms/__init__.py`
5. Use the algorithm in your config file.
Please refer to the previous section about how to use existing algorithms in MMRazor
```Python
model = dict(
type='XXX',
architecture=architecture)
```
### Pipelines for different algorithms
Please refer to our tutorials about how to customize different algorithms for more details as below.
1. NAS
[Customize NAS algorithms](https://mmrazor.readthedocs.io/en/latest/tutorials/Tutorial_4_customize_nas_algorithms.html#)
2. Pruning
[Customize Pruning algorithms](https://mmrazor.readthedocs.io/en/latest/tutorials/Tutorial_5_customize_pruning_algorithms.html)
3. Distill
[Customize KD algorithms](https://mmrazor.readthedocs.io/en/latest/tutorials/Tutorial_6_customize_kd_algorithms.html)