Migration from MMAction2 0.x¶
MMAction2 1.x introduced major refactorings and modifications including some BC-breaking changes. We provide this tutorial to help you migrate your projects from MMAction2 0.x smoothly.
New dependencies¶
MMAction2 1.x depends on the following packages. You are recommended to prepare a new clean environment and install them according to install tutorial
Configuration files¶
In MMAction2 1.x, we refactored the structure of configuration files. The configuration files with the old style will be incompatible.
In this section, we will introduce all changes of the configuration files. And we assume you are already familiar with the config files.
Model settings¶
No changes in model.backbone
and model.neck
. For model.cls_head
, we move the average_clips
inside it, which is originally set in model.test_cfg
.
Data settings¶
Changes in data
¶
The original
data
field is splited totrain_dataloader
,val_dataloader
andtest_dataloader
. This allows us to configure them in fine-grained. For example, you can specify different sampler and batch size during training and test.The
videos_per_gpu
is renamed tobatch_size
.The
workers_per_gpu
is renamed tonum_workers
.
Original |
data = dict(
videos_per_gpu=32,
workers_per_gpu=2,
train=dict(...),
val=dict(...),
test=dict(...),
)
|
New |
train_dataloader = dict(
batch_size=32,
num_workers=2,
dataset=dict(...),
sampler=dict(type='DefaultSampler', shuffle=True) # necessary
)
val_dataloader = dict(
batch_size=32,
num_workers=2,
dataset=dict(...),
sampler=dict(type='DefaultSampler', shuffle=False) # necessary
)
test_dataloader = val_dataloader
|
Changes in pipeline
¶
The original formatting transforms
ToTensor
,Collect
are combined asPackActionInputs
.We don’t recommend to do
Normalize
in the dataset pipeline. Please remove it from pipelines and set it in themodel.data_preprocessor
field.
Original |
train_pipeline = [
dict(type='DecordInit'),
dict(type='SampleFrames', clip_len=1, frame_interval=1, num_clips=8),
dict(type='DecordDecode'),
dict(type='Resize', scale=(-1, 256)),
dict(
type='MultiScaleCrop',
input_size=224,
scales=(1, 0.875, 0.75, 0.66),
random_crop=False,
max_wh_scale_gap=1),
dict(type='Resize', scale=(224, 224), keep_ratio=False),
dict(type='Flip', flip_ratio=0.5),
dict(type='Normalize', **img_norm_cfg),
dict(type='FormatShape', input_format='NCHW'),
dict(type='Collect', keys=['imgs', 'label'], meta_keys=[]),
dict(type='ToTensor', keys=['imgs', 'label'])
]
|
New |
model.data_preprocessor = dict(
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=False)
train_pipeline = [
dict(type='DecordInit'),
dict(type='SampleFrames', clip_len=1, frame_interval=1, num_clips=5),
dict(type='DecordDecode'),
dict(type='Resize', scale=(-1, 256)),
dict(
type='MultiScaleCrop',
input_size=224,
scales=(1, 0.875, 0.75, 0.66),
random_crop=False,
max_wh_scale_gap=1),
dict(type='Resize', scale=(224, 224), keep_ratio=False),
dict(type='Flip', flip_ratio=0.5),
dict(type='FormatShape', input_format='NCHW'),
dict(type='PackActionInputs')
]
|
Changes in evaluation
¶
The
evaluation
field is splited toval_evaluator
andtest_evaluator
. And it won’t supportinterval
andsave_best
arguments.The
interval
is moved totrain_cfg.val_interval
and thesave_best
is moved todefault_hooks.checkpoint.save_best
.The ‘mean_average_precision’, ‘mean_class_accuracy’, ‘mmit_mean_average_precision’, ‘top_k_accuracy’ are combined as
AccMetric
, and you could usemetric_list
to specify which metric to calculate.The
AVAMetric
is used to evaluate AVA Dataset.The
ANetMetric
is used to evaluate ActivityNet Dataset.
Original |
evaluation = dict(
interval=5,
metrics=['top_k_accuracy', 'mean_class_accuracy'])
|
New |
val_evaluator = dict(
type='AccMetric',
metric_list=('top_k_accuracy', 'mean_class_accuracy'))
test_evaluator = val_evaluator
|
Schedule settings¶
Changes in optimizer
and optimizer_config
¶
Now we use
optim_wrapper
field to configure the optimization process. And theoptimizer
becomes a sub field ofoptim_wrapper
.paramwise_cfg
is also a sub field ofoptim_wrapper
parallel tooptimizer
.optimizer_config
is removed now, and all configurations of it are moved tooptim_wrapper
.grad_clip
is renamed toclip_grad
.
Original |
optimizer = dict(
type='AdamW',
lr=0.0015,
weight_decay=0.3,
paramwise_cfg = dict(
norm_decay_mult=0.0,
bias_decay_mult=0.0,
))
optimizer_config = dict(grad_clip=dict(max_norm=1.0))
|
New |
optim_wrapper = dict(
optimizer=dict(type='AdamW', lr=0.0015, weight_decay=0.3),
paramwise_cfg = dict(
norm_decay_mult=0.0,
bias_decay_mult=0.0,
),
clip_gard=dict(max_norm=1.0),
)
|
Changes in lr_config
¶
The
lr_config
field is removed and we use newparam_scheduler
to replace it.The
warmup
related arguments are removed, since we use schedulers combination to implement this functionality.
The new schedulers combination mechanism is very flexible, and you can use it to design many kinds of learning rate / momentum curves.
Original |
lr_config = dict(
policy='CosineAnnealing',
min_lr=0,
warmup='linear',
warmup_iters=5,
warmup_ratio=0.01,
warmup_by_epoch=True)
|
New |
param_scheduler = [
# warmup
dict(
type='LinearLR',
start_factor=0.01,
by_epoch=True,
end=5,
# Update the learning rate after every iters.
convert_to_iter_based=True),
# main learning rate scheduler
dict(type='CosineAnnealingLR', by_epoch=True, begin=5),
]
|
Changes in runner
¶
Most configuration in the original runner
field is moved to train_cfg
, val_cfg
and test_cfg
, which
configure the loop in training, validation and test.
Original |
runner = dict(type='EpochBasedRunner', max_epochs=100)
|
New |
# The `val_interval` is the original `evaluation.interval`.
train_cfg = dict(type='EpochBasedTrainLoop', max_epochs=100, val_begin=1, val_interval=1)
val_cfg = dict(type='ValLoop') # Use the default validation loop.
test_cfg = dict(type='TestLoop') # Use the default test loop.
|
In fact, in OpenMMLab 2.0, we introduced Loop
to control the behaviors in training, validation and test. And
the functionalities of Runner
are also changed. You can find more details in the MMEngine tutorials.
Runtime settings¶
Changes in checkpoint_config
and log_config
¶
The checkpoint_config
are moved to default_hooks.checkpoint
and the log_config
are moved to default_hooks.logger
.
And we move many hooks settings from the script code to the default_hooks
field in the runtime configuration.
default_hooks = dict(
# update runtime information, e.g. current iter and lr.
runtime_info=dict(type='RuntimeInfoHook'),
# record the time of every iterations.
timer=dict(type='IterTimerHook'),
# print log every 100 iterations.
logger=dict(type='LoggerHook', interval=100),
# enable the parameter scheduler.
param_scheduler=dict(type='ParamSchedulerHook'),
# save checkpoint per epoch, and automatically save the best checkpoint.
checkpoint=dict(type='CheckpointHook', interval=1, save_best='auto'),
# set sampler seed in distributed environment.
sampler_seed=dict(type='DistSamplerSeedHook'),
# synchronize model buffers at the end of each epoch.
sync_buffers=dict(type='SyncBuffersHook')
)
In addition, we splited the original logger to logger and visualizer. The logger is used to record information and the visualizer is used to show the logger in different backends, like terminal, TensorBoard and Wandb.
Original |
log_config = dict(
interval=100,
hooks=[
dict(type='TextLoggerHook'),
dict(type='TensorboardLoggerHook'),
])
|
New |
default_hooks = dict(
...
logger=dict(type='LoggerHook', interval=100),
)
visualizer = dict(
type='ActionVisualizer',
vis_backends=[dict(type='LocalVisBackend'), dict(type='TensorboardVisBackend')],
)
|
Changes in load_from
and resume_from
¶
The
resume_from
is removed. And we useresume
andload_from
to replace it.If
resume=True
andload_from
is not None, resume training from the checkpoint inload_from
.If
resume=True
andload_from
is None, try to resume from the latest checkpoint in the work directory.If
resume=False
andload_from
is not None, only load the checkpoint, not resume training.If
resume=False
andload_from
is None, do not load nor resume.
Changes in dist_params
¶
The dist_params
field is a sub field of env_cfg
now. And there are some new configurations in the env_cfg
.
env_cfg = dict(
# whether to enable cudnn benchmark
cudnn_benchmark=False,
# set multi process parameters
mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0),
# set distributed parameters
dist_cfg=dict(backend='nccl'),
)
Changes in workflow
¶
Workflow
related functionalities are removed.
New field visualizer
¶
The visualizer is a new design in OpenMMLab 2.0 architecture. We use a visualizer instance in the runner to handle results & log visualization and save to different backends.
visualizer = dict(
type='ActionVisualizer',
vis_backends=[
dict(type='LocalVisBackend'),
# Uncomment the below line to save the log and visualization results to TensorBoard.
# dict(type='TensorboardVisBackend')
]
)
New field default_scope
¶
The start point to search module for all registries. The default_scope
in MMAction2 is mmaction
. See the registry tutorial for more details.
Packages¶
mmaction.apis
¶
The documentation can be found here.
Function | Changes |
---|---|
init_recognizer |
No changes |
inference_recognizer |
No changes |
train_model |
Removed, use runner.train to train. |
multi_gpu_test |
Removed, use runner.test to test. |
single_gpu_test |
Removed, use runner.test to test. |
set_random_seed |
Removed, use mmengine.runner.set_random_seed . |
init_random_seed |
Removed, use mmengine.dist.sync_random_seed . |
mmaction.core
¶
The mmaction.core
package is renamed to mmaction.engine
.
Sub package | Changes |
---|---|
evaluation |
Removed, use the metrics in mmaction.evaluation . |
hooks |
Moved to mmaction.engine.hooks |
optimizer |
Moved to mmaction.engine.optimizers |
utils |
Removed, the distributed environment related functions can be found in the mmengine.dist package. |
mmaction.datasets
¶
The documentation can be found here
Changes in BaseActionDataset
:¶
Method | Changes |
---|---|
prepare_train_frames |
Replaced by get_data_info |
preprare_test_frames |
Replaced by get_data_info |
evaluate |
Removed, use mmengine.evaluator.Evaluator |
dump_results |
Removed, use mmengine.evaluator.DumpResults |
load_annotations |
Replaced by load_data_list |
Now, you can write a new Dataset class inherited from BaseActionDataset
and overwrite load_data_list
only. To load more data information, you could overwrite get_data_info
like RawframeDataset
and AVADataset
.
The mmaction.datasets.pipelines
is renamed to mmaction.datasets.transforms
and the mmaction.datasets.pipelines.augmentations
is renamed to mmaction.datasets.pipelines.processing
.
mmaction.models
¶
The documentation can be found here. The interface of all backbones, necks and losses didn’t change.
Changes in BaseRecognizer
:¶
Method | Changes |
---|---|
extract_feat |
Enhanced method, which now supports output features of three stages (backbone , neck , head ) and can handle different modes, such as train_mode and test_mode . |
forward |
Now only accepts three arguments: inputs , data_samples and mode . See the documentation for more details. |
forward_train |
Replaced by loss . |
forward_test |
Replaced by predict . |
train_step |
The optimizer argument is replaced by optim_wrapper and it accepts OptimWrapper . |
val_step |
The original val_step is the same as train_step , now it calls predict . |
test_step |
New method, and it's the same as val_step . |
Changes in BaseHead:¶
Method | Changes |
---|---|
forward |
No changes |
loss |
It accepts feats and data_samples instead of cls_score and labels to calculate loss. The data_samples is a list of ActionDataSample. |
predict |
New method. It accepts feats and data_samples to predict classification scores. |
mmaction.utils
¶
Function | Changes |
---|---|
collect_env |
No changes |
get_root_logger |
Removed, use mmengine.MMLogger.get_current_instance |
setup_multi_processes |
Removed, use mmengine.utils.dl_utils.setup_multi_processes . |
Other changes¶
We moved the definition of all registries in different packages to the
mmaction.registry
package.