classRegistry:"""A registry to map strings to classes or functions.
Registered object could be built from registry. Meanwhile, registered
functions could be called from registry.
Args:
name (str): Registry name.
build_func (callable, optional): A function to construct instance
from Registry. :func:`build_from_cfg` is used if neither ``parent``
or ``build_func`` is specified. If ``parent`` is specified and
``build_func`` is not given, ``build_func`` will be inherited
from ``parent``. Defaults to None.
parent (:obj:`Registry`, optional): Parent registry. The class
registered in children registry could be built from parent.
Defaults to None.
scope (str, optional): The scope of registry. It is the key to search
for children registry. If not specified, scope will be the name of
the package where class is defined, e.g. mmdet, mmcls, mmseg.
Defaults to None.
Examples:
>>> # define a registry
>>> MODELS = Registry('models')
>>> # registry the `ResNet` to `MODELS`
>>> @MODELS.register_module()
>>> class ResNet:
>>> pass
>>> # build model from `MODELS`
>>> resnet = MODELS.build(dict(type='ResNet'))
>>> @MODELS.register_module()
>>> def resnet50():
>>> pass
>>> resnet = MODELS.build(dict(type='resnet50'))
>>> # hierarchical registry
>>> DETECTORS = Registry('detectors', parent=MODELS, scope='det')
>>> @DETECTORS.register_module()
>>> class FasterRCNN:
>>> pass
>>> fasterrcnn = DETECTORS.build(dict(type='FasterRCNN'))
More advanced usages can be found at
https://mmengine.readthedocs.io/en/latest/tutorials/registry.html.
"""def__init__(self,
name:str,
build_func: Optional[Callable]=None,
parent: Optional['Registry']=None,
scope: Optional[str]=None):from.build_functions import build_from_cfg
self._name = name
self._module_dict: Dict[str, Type]=dict()
self._children: Dict[str,'Registry']=dict()if scope isnotNone:assertisinstance(scope,str)
self._scope = scope
else:
self._scope = self.infer_scope()# See https://mypy.readthedocs.io/en/stable/common_issues.html## variables-vs-type-aliases for the use
self.parent: Optional['Registry']if parent isnotNone:assertisinstance(parent, Registry)
parent._add_child(self)
self.parent = parent
else:
self.parent =None# self.build_func will be set with the following priority:# 1. build_func# 2. parent.build_func# 3. build_from_cfg
self.build_func: Callable
if build_func isNone:if self.parent isnotNone:
self.build_func = self.parent.build_func
else:
self.build_func = build_from_cfg
else:
self.build_func = build_func
import torch.nn as nn
# 使用注册器管理模块@ACTIVATION.register_module()classSigmoid(nn.Module):def__init__(self):super().__init__()defforward(self, x):print('call Sigmoid.forward')return x
@ACTIVATION.register_module()classReLU(nn.Module):def__init__(self, inplace=False):super().__init__()defforward(self, x):print('call ReLU.forward')return x
@ACTIVATION.register_module()classSoftmax(nn.Module):def__init__(self):super().__init__()defforward(self, x):print('call Softmax.forward')return x
@RUNNERS.register_module()classRunner:"""A training helper for PyTorch.
Runner object can be built from config by ``runner = Runner.from_cfg(cfg)``
where the ``cfg`` usually contains training, validation, and test-related
configurations to build corresponding components. We usually use the
same config to launch training, testing, and validation tasks. However,
only some of these components are necessary at the same time, e.g.,
testing a model does not need training or validation-related components.
To avoid repeatedly modifying config, the construction of ``Runner`` adopts
lazy initialization to only initialize components when they are going to be
used. Therefore, the model is always initialized at the beginning, and
training, validation, and, testing related components are only initialized
when calling ``runner.train()``, ``runner.val()``, and ``runner.test()``,
respectively.
Args:
model (:obj:`torch.nn.Module` or dict): The model to be run. It can be
a dict used for build a model.
work_dir (str): The working directory to save checkpoints. The logs
will be saved in the subdirectory of `work_dir` named
:attr:`timestamp`.
train_dataloader (Dataloader or dict, optional): A dataloader object or
a dict to build a dataloader. If ``None`` is given, it means
skipping training steps. Defaults to None.
See :meth:`build_dataloader` for more details.
val_dataloader (Dataloader or dict, optional): A dataloader object or
a dict to build a dataloader. If ``None`` is given, it means
skipping validation steps. Defaults to None.
See :meth:`build_dataloader` for more details.
test_dataloader (Dataloader or dict, optional): A dataloader object or
a dict to build a dataloader. If ``None`` is given, it means
skipping test steps. Defaults to None.
See :meth:`build_dataloader` for more details.
train_cfg (dict, optional): A dict to build a training loop. If it does
not provide "type" key, it should contain "by_epoch" to decide
which type of training loop :class:`EpochBasedTrainLoop` or
:class:`IterBasedTrainLoop` should be used. If ``train_cfg``
specified, :attr:`train_dataloader` should also be specified.
Defaults to None. See :meth:`build_train_loop` for more details.
val_cfg (dict, optional): A dict to build a validation loop. If it does
not provide "type" key, :class:`ValLoop` will be used by default.
If ``val_cfg`` specified, :attr:`val_dataloader` should also be
specified. If ``ValLoop`` is built with `fp16=True``,
``runner.val()`` will be performed under fp16 precision.
Defaults to None. See :meth:`build_val_loop` for more details.
test_cfg (dict, optional): A dict to build a test loop. If it does
not provide "type" key, :class:`TestLoop` will be used by default.
If ``test_cfg`` specified, :attr:`test_dataloader` should also be
specified. If ``ValLoop`` is built with `fp16=True``,
``runner.val()`` will be performed under fp16 precision.
Defaults to None. See :meth:`build_test_loop` for more details.
auto_scale_lr (dict, Optional): Config to scale the learning rate
automatically. It includes ``base_batch_size`` and ``enable``.
``base_batch_size`` is the batch size that the optimizer lr is
based on. ``enable`` is the switch to turn on and off the feature.
optim_wrapper (OptimWrapper or dict, optional):
Computing gradient of model parameters. If specified,
:attr:`train_dataloader` should also be specified. If automatic
mixed precision or gradient accmulation
training is required. The type of ``optim_wrapper`` should be
AmpOptimizerWrapper. See :meth:`build_optim_wrapper` for
examples. Defaults to None.
param_scheduler (_ParamScheduler or dict or list, optional):
Parameter scheduler for updating optimizer parameters. If
specified, :attr:`optimizer` should also be specified.
Defaults to None.
See :meth:`build_param_scheduler` for examples.
val_evaluator (Evaluator or dict or list, optional): A evaluator object
used for computing metrics for validation. It can be a dict or a
list of dict to build a evaluator. If specified,
:attr:`val_dataloader` should also be specified. Defaults to None.
test_evaluator (Evaluator or dict or list, optional): A evaluator
object used for computing metrics for test steps. It can be a dict
or a list of dict to build a evaluator. If specified,
:attr:`test_dataloader` should also be specified. Defaults to None.
default_hooks (dict[str, dict] or dict[str, Hook], optional): Hooks to
execute default actions like updating model parameters and saving
checkpoints. Default hooks are ``OptimizerHook``,
``IterTimerHook``, ``LoggerHook``, ``ParamSchedulerHook`` and
``CheckpointHook``. Defaults to None.
See :meth:`register_default_hooks` for more details.
custom_hooks (list[dict] or list[Hook], optional): Hooks to execute
custom actions like visualizing images processed by pipeline.
Defaults to None.
data_preprocessor (dict, optional): The pre-process config of
:class:`BaseDataPreprocessor`. If the ``model`` argument is a dict
and doesn't contain the key ``data_preprocessor``, set the argument
as the ``data_preprocessor`` of the ``model`` dict.
Defaults to None.
load_from (str, optional): The checkpoint file to load from.
Defaults to None.
resume (bool): Whether to resume training. Defaults to False. If
``resume`` is True and ``load_from`` is None, automatically to
find latest checkpoint from ``work_dir``. If not found, resuming
does nothing.
launcher (str): Way to launcher multi-process. Supported launchers
are 'pytorch', 'mpi', 'slurm' and 'none'. If 'none' is provided,
non-distributed environment will be launched.
env_cfg (dict): A dict used for setting environment. Defaults to
dict(dist_cfg=dict(backend='nccl')).
log_processor (dict, optional): A processor to format logs. Defaults to
None.
log_level (int or str): The log level of MMLogger handlers.
Defaults to 'INFO'.
visualizer (Visualizer or dict, optional): A Visualizer object or a
dict build Visualizer object. Defaults to None. If not
specified, default config will be used.
default_scope (str): Used to reset registries location.
Defaults to "mmengine".
randomness (dict): Some settings to make the experiment as reproducible
as possible like seed and deterministic.
Defaults to ``dict(seed=None)``. If seed is None, a random number
will be generated and it will be broadcasted to all other processes
if in distributed environment. If ``cudnn_benchmarch`` is
``True`` in ``env_cfg`` but ``deterministic`` is ``True`` in
``randomness``, the value of ``torch.backends.cudnn.benchmark``
will be ``False`` finally.
experiment_name (str, optional): Name of current experiment. If not
specified, timestamp will be used as ``experiment_name``.
Defaults to None.
cfg (dict or Configdict or :obj:`Config`, optional): Full config.
Defaults to None.
Examples:
>>> from mmengine.runner import Runner
>>> cfg = dict(
>>> model=dict(type='ToyModel'),
>>> work_dir='path/of/work_dir',
>>> train_dataloader=dict(
>>> dataset=dict(type='ToyDataset'),
>>> sampler=dict(type='DefaultSampler', shuffle=True),
>>> batch_size=1,
>>> num_workers=0),
>>> val_dataloader=dict(
>>> dataset=dict(type='ToyDataset'),
>>> sampler=dict(type='DefaultSampler', shuffle=False),
>>> batch_size=1,
>>> num_workers=0),
>>> test_dataloader=dict(
>>> dataset=dict(type='ToyDataset'),
>>> sampler=dict(type='DefaultSampler', shuffle=False),
>>> batch_size=1,
>>> num_workers=0),
>>> auto_scale_lr=dict(base_batch_size=16, enable=False),
>>> optim_wrapper=dict(type='OptimizerWrapper', optimizer=dict(
>>> type='SGD', lr=0.01)),
>>> param_scheduler=dict(type='MultiStepLR', milestones=[1, 2]),
>>> val_evaluator=dict(type='ToyEvaluator'),
>>> test_evaluator=dict(type='ToyEvaluator'),
>>> train_cfg=dict(by_epoch=True, max_epochs=3, val_interval=1),
>>> val_cfg=dict(),
>>> test_cfg=dict(),
>>> custom_hooks=[],
>>> default_hooks=dict(
>>> timer=dict(type='IterTimerHook'),
>>> checkpoint=dict(type='CheckpointHook', interval=1),
>>> logger=dict(type='LoggerHook'),
>>> optimizer=dict(type='OptimizerHook', grad_clip=False),
>>> param_scheduler=dict(type='ParamSchedulerHook')),
>>> launcher='none',
>>> env_cfg=dict(dist_cfg=dict(backend='nccl')),
>>> log_processor=dict(window_size=20),
>>> visualizer=dict(type='Visualizer',
>>> vis_backends=[dict(type='LocalVisBackend',
>>> save_dir='temp_dir')])
>>> )
>>> runner = Runner.from_cfg(cfg)
>>> runner.train()
>>> runner.test()
"""
cfg: Config
_train_loop: Optional[Union[BaseLoop, Dict]]
_val_loop: Optional[Union[BaseLoop, Dict]]
_test_loop: Optional[Union[BaseLoop, Dict]]def__init__(
self,
model: Union[nn.Module, Dict],
work_dir:str,
train_dataloader: Optional[Union[DataLoader, Dict]]=None,
val_dataloader: Optional[Union[DataLoader, Dict]]=None,
test_dataloader: Optional[Union[DataLoader, Dict]]=None,
train_cfg: Optional[Dict]=None,
val_cfg: Optional[Dict]=None,
test_cfg: Optional[Dict]=None,
auto_scale_lr: Optional[Dict]=None,
optim_wrapper: Optional[Union[OptimWrapper, Dict]]=None,
param_scheduler: Optional[Union[_ParamScheduler, Dict, List]]=None,
val_evaluator: Optional[Union[Evaluator, Dict, List]]=None,
test_evaluator: Optional[Union[Evaluator, Dict, List]]=None,
default_hooks: Optional[Dict[str, Union[Hook, Dict]]]=None,
custom_hooks: Optional[List[Union[Hook, Dict]]]=None,
data_preprocessor: Union[nn.Module, Dict,None]=None,
load_from: Optional[str]=None,
resume:bool=False,
launcher:str='none',
env_cfg: Dict =dict(dist_cfg=dict(backend='nccl')),
log_processor: Optional[Dict]=None,
log_level:str='INFO',
visualizer: Optional[Union[Visualizer, Dict]]=None,
default_scope:str='mmengine',
randomness: Dict =dict(seed=None),
experiment_name: Optional[str]=None,
cfg: Optional[ConfigType]=None,):
self._work_dir = osp.abspath(work_dir)
mmengine.mkdir_or_exist(self._work_dir)# recursively copy the `cfg` because `self.cfg` will be modified# everywhere.if cfg isnotNone:ifisinstance(cfg, Config):
self.cfg = copy.deepcopy(cfg)elifisinstance(cfg,dict):
self.cfg = Config(cfg)else:
self.cfg = Config(dict())# lazy initialization
training_related =[train_dataloader, train_cfg, optim_wrapper]ifnot(all(item isNonefor item in training_related)orall(item isnotNonefor item in training_related)):raise ValueError('train_dataloader, train_cfg, and optimizer should be either ''all None or not None, but got 'f'train_dataloader={train_dataloader}, 'f'train_cfg={train_cfg}, 'f'optim_wrapper={optim_wrapper}.')
self._train_dataloader = train_dataloader
self._train_loop = train_cfg
self.optim_wrapper: Optional[Union[OptimWrapper,dict]]
self.optim_wrapper = optim_wrapper
self.auto_scale_lr = auto_scale_lr
# If there is no need to adjust learning rate, momentum or other# parameters of optimizer, param_scheduler can be Noneif param_scheduler isnotNoneand self.optim_wrapper isNone:raise ValueError('param_scheduler should be None when optimizer is None, 'f'but got {param_scheduler}')# Parse `param_scheduler` to a list or a dict. If `optim_wrapper` is a# `dict` with single optimizer, parsed param_scheduler will be a# list of parameter schedulers. If `optim_wrapper` is# a `dict` with multiple optimizers, parsed `param_scheduler` will be# dict with multiple list of parameter schedulers.
self._check_scheduler_cfg(param_scheduler)
self.param_schedulers = param_scheduler
val_related =[val_dataloader, val_cfg, val_evaluator]ifnot(all(item isNonefor item in val_related)orall(item isnotNonefor item in val_related)):raise ValueError('val_dataloader, val_cfg, and val_evaluator should be either ''all None or not None, but got 'f'val_dataloader={val_dataloader}, val_cfg={val_cfg}, 'f'val_evaluator={val_evaluator}')
self._val_dataloader = val_dataloader
self._val_loop = val_cfg
self._val_evaluator = val_evaluator
test_related =[test_dataloader, test_cfg, test_evaluator]ifnot(all(item isNonefor item in test_related)orall(item isnotNonefor item in test_related)):raise ValueError('test_dataloader, test_cfg, and test_evaluator should be ''either all None or not None, but got 'f'test_dataloader={test_dataloader}, test_cfg={test_cfg}, 'f'test_evaluator={test_evaluator}')
self._test_dataloader = test_dataloader
self._test_loop = test_cfg
self._test_evaluator = test_evaluator
self._launcher = launcher
if self._launcher =='none':
self._distributed =Falseelse:
self._distributed =True# self._timestamp will be set in the `setup_env` method. Besides,# it also will initialize multi-process and (or) distributed# environment.
self.setup_env(env_cfg)# self._deterministic and self._seed will be set in the# `set_randomness`` method
self._randomness_cfg = randomness
self.set_randomness(**randomness)if experiment_name isnotNone:
self._experiment_name =f'{experiment_name}_{self._timestamp}'elif self.cfg.filename isnotNone:
filename_no_ext = osp.splitext(osp.basename(self.cfg.filename))[0]
self._experiment_name =f'{filename_no_ext}_{self._timestamp}'else:
self._experiment_name = self.timestamp
self._log_dir = osp.join(self.work_dir, self.timestamp)
mmengine.mkdir_or_exist(self._log_dir)# Used to reset registries location. See :meth:`Registry.build` for# more details.
self.default_scope = DefaultScope.get_instance(
self._experiment_name, scope_name=default_scope)# Build log processor to format message.
log_processor =dict()if log_processor isNoneelse log_processor
self.log_processor = self.build_log_processor(log_processor)# Since `get_instance` could return any subclass of ManagerMixin. The# corresponding attribute needs a type hint.
self.logger = self.build_logger(log_level=log_level)# Collect and log environment information.
self._log_env(env_cfg)# collect information of all modules registered in the registries
registries_info = count_registered_modules(
self.work_dir if self.rank ==0elseNone, verbose=False)
self.logger.debug(registries_info)# Build `message_hub` for communication among components.# `message_hub` can store log scalars (loss, learning rate) and# runtime information (iter and epoch). Those components that do not# have access to the runner can get iteration or epoch information# from `message_hub`. For example, models can get the latest created# `message_hub` by# `self.message_hub=MessageHub.get_current_instance()` and then get# current epoch by `cur_epoch = self.message_hub.get_info('epoch')`.# See `MessageHub` and `ManagerMixin` for more details.
self.message_hub = self.build_message_hub()# visualizer used for writing log or visualizing all kinds of data
self.visualizer = self.build_visualizer(visualizer)if self.cfg:
self.visualizer.add_config(self.cfg)
self._load_from = load_from
self._resume = resume
# flag to mark whether checkpoint has been loaded or resumed
self._has_loaded =False# build a modelifisinstance(model,dict)and data_preprocessor isnotNone:# Merge the data_preprocessor to model config.
model.setdefault('data_preprocessor', data_preprocessor)
self.model = self.build_model(model)# wrap model
self.model = self.wrap_model(
self.cfg.get('model_wrapper_cfg'), self.model)# get model name from the model classifhasattr(self.model,'module'):
self._model_name = self.model.module.__class__.__name__
else:
self._model_name = self.model.__class__.__name__
self._hooks: List[Hook]=[]# register hooks to `self._hooks`
self.register_hooks(default_hooks, custom_hooks)# dump `cfg` to `work_dir`
self.dump_config()
3.3.1手动构建模块来使用执行器
3.3.1.1 手动构建模块进行训练
使用执行器的某一项功能时需要准备好对应功能所依赖的模块。以使用执行器的训练功能为例,用户需要准备
模型
优化器
参数调度器
训练数据集
# 准备训练任务所需要的模块import torch
from torch import nn
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
from mmengine.model import BaseModel
from mmengine.optim.scheduler import MultiStepLR
# 定义一个多层感知机网络classNetwork(BaseModel):def__init__(self):super().__init__()
self.mlp = nn.Sequential(nn.Linear(28*28,128), nn.ReLU(), nn.Linear(128,128), nn.ReLU(), nn.Linear(128,10))
self.loss = nn.CrossEntropyLoss()defforward(self, batch_inputs: torch.Tensor, data_samples =None, mode:str='tensor'):
x = batch_inputs.flatten(1)
x = self.mlp(x)if mode =='loss':return{'loss': self.loss(x, data_samples)}elif mode =='predict':return x.argmax(1)else:return x
model = Network()# 构建优化器
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)# 构建参数调度器用于调整学习率
lr_scheduler = MultiStepLR(optimizer, milestones=[2], by_epoch=True)# 构建手写数字识别 (MNIST) 数据集
train_dataset = datasets.MNIST(root="MNIST", download=True, train=True, transform=transforms.ToTensor())# 构建数据加载器
train_dataloader = DataLoader(dataset=train_dataset, batch_size=10, num_workers=2)
import torch
from mmengine.registry import HOOKS
from mmengine.hooks import Hook
@HOOKS.register_module()classCheckInvalidLossHook(Hook):"""Check invalid loss hook.
This hook will regularly check whether the loss is valid
during training.
Args:
interval (int): Checking interval (every k iterations).
Defaults to 50.
"""def__init__(self, interval=50):
self.interval = interval
defafter_train_iter(self, runner, batch_idx, data_batch=None, outputs=None):"""All subclasses should override this method, if they need any
operations after each training iteration.
Args:
runner (Runner): The runner of the training process.
batch_idx (int): The index of the current batch in the train loop.
data_batch (dict or tuple or list, optional): Data from dataloader.
outputs (dict, optional): Outputs from model.
"""if self.every_n_train_iters(runner, self.interval):assert torch.isfinite(outputs['loss']),\
runner.logger.info('loss become infinite or NaN!')
get_subset_(indices):根据 indices 以 inplace 的方式修改原数据集类。如果 indices 为 int,则原数据集类只包含前若干个数据样本;如果 indices 为 Sequence[int],则原数据集类包含根据 Sequence[int] 指定的数据样本。
get_subset(indices):根据 indices 以非 inplace 的方式返回子数据集类,即重新复制一份子数据集。如果 indices 为 int,则返回的子数据集类只包含前若干个数据样本;如果 indices 为 Sequence[int],则返回的子数据集类包含根据 Sequence[int] 指定的数据样本。
data_element = BaseDataElement(
bboxes=torch.rand((5,4)),
scores=torch.rand((5,)),
metainfo=dict(img_id=1, img_shape=(640,640)))# 可以在创建新 `BaseDataElement` 时设置 metainfo 和 data,使得新的 BaseDataElement 有相同未被设置的数据
data_element1 = data_element.new(metainfo=dict(img_id=2, img_shape=(320,320)))print('bboxes is in data_element1:','bboxes'in data_element1)# Trueprint('bboxes in data_element1 is same as bbox in data_element',(data_element1.bboxes == data_element.bboxes).all())print('img_id in data_element1 is', data_element1.img_id ==2)# True
data_element2 = data_element.new(label=torch.rand(5,))print('bboxes is not in data_element2','bboxes'notin data_element2)# Trueprint('img_id in data_element2 is same as img_id in data_element', data_element2.img_id == data_element.img_id)print('label in data_element2 is','label'in data_element2)# 也可以通过 `clone` 构建一个新的 object,新的 object 会拥有和 data_element 相同的 data 和 metainfo 内容以及状态。
data_element2 = data_element1.clone()"""
bboxes is in data_element1: True
bboxes in data_element1 is same as bbox in data_element tensor(True)
img_id in data_element1 is True
bboxes is not in data_element2 True
img_id in data_element2 is same as img_id in data_element True
label in data_element2 is True
"""
4.2.1.3 属性的增加与查询
对增加属性而言,用户可以像增加类属性那样增加 data 内的属性;对metainfo 而言,一般储存的为一些图像的元信息,一般情况下不会修改,如果需要增加,用户应当使用 set_metainfo 接口显示地修改。
# 通过类属性直接设置 BaseDataElement 中的 data 字段
data_element.scores = torch.rand((5,))
data_element.bboxes = torch.rand((5,4))print("data's key is:", data_element.keys())print("data's value is:", data_element.values())for k, v in data_element.items():print(f'{k}: {v}')print("通过类属性查看 scores 和 bboxes")print('scores:', data_element.scores)print('bboxes:', data_element.bboxes)print("通过 get() 查看 scores 和 bboxes")print('scores:', data_element.get('scores',None))print('bboxes:', data_element.get('bboxes',None))print('fake:', data_element.get('fake','not exist'))
设置和访问ALL (metainfo+data)
print("All key in data_element is:", data_element.all_keys())print("The length of values in data_element is",len(data_element.all_values()))for k, v in data_element.all_items():print(f'{k}: {v}')
同时为了操作的便捷性,对 data 和 metainfo 中的数据可以通过 del 直接删除,也支持 pop 在访问属性后删除属性。
初始化数据
data_element = BaseDataElement(
bboxes=torch.rand((6,4)), scores=torch.rand((6,)),
metainfo=dict(img_id=0, img_shape=(640,640)))for k, v in data_element.all_items():print(f'{k}: {v}')
对 data 进行修改
# 对 data 进行修改
data_element.bboxes = data_element.bboxes *2
data_element.scores = data_element.scores *-1for k, v in data_element.items():print(f'{k}: {v}')# 删除 data 中的属性del data_element.bboxes
for k, v in data_element.items():print(f'{k}: {v}')
data_element.pop('scores',None)print('The keys in data is', data_element.keys())
对 metainfo 进行修改
# 对 metainfo 进行修改
data_element.set_metainfo(dict(img_shape =(1280,1280), img_id=10))print(data_element.img_shape)# (1280, 1280)for k, v in data_element.metainfo_items():print(f'{k}: {v}')# 提供了便捷的属性删除和访问操作 popdel data_element.img_shape
for k, v in data_element.metainfo_items():print(f'{k}: {v}')
data_element.pop('img_id')print('The keys in metainfo is', data_element.metainfo_keys())
from mmengine.structures import InstanceData
import torch
import numpy as np
img_meta =dict(img_shape=(800,1196,3), pad_shape=(800,1216,3))
instance_data = InstanceData(metainfo=img_meta)
instance_data.det_labels = torch.LongTensor([2,3])
instance_data.det_scores = torch.Tensor([0.8,0.7])
instance_data.bboxes = torch.rand((2,4))print('The length of instance_data is',len(instance_data))# 2
instance_data.bboxes = torch.rand((3,4))"""
The length of instance_data is 2
AssertionError: the length of values 3 is not consistent with the length of this :obj:`InstanceData` 2
"""
print(instance_data[1])"""
<InstanceData(
META INFORMATION
pad_shape: (800, 1216, 3)
img_shape: (800, 1196, 3)
DATA FIELDS
det_labels: tensor([3])
det_scores: tensor([0.7000])
bboxes: tensor([[0.3421, 0.7104, 0.6000, 0.1518]])
) at 0x7f9f312b4610>
"""
切片(只对data)
print(instance_data[0:1])"""
<InstanceData(
META INFORMATION
pad_shape: (800, 1216, 3)
img_shape: (800, 1196, 3)
DATA FIELDS
det_labels: tensor([2])
det_scores: tensor([0.8000])
bboxes: tensor([[0.1872, 0.1669, 0.7563, 0.8777]])
) at 0x7f9f312b4e20>
"""
列表索引
sorted_results = instance_data[instance_data.det_scores.sort().indices]print(sorted_results)"""
<InstanceData(
META INFORMATION
pad_shape: (800, 1216, 3)
img_shape: (800, 1196, 3)
DATA FIELDS
det_labels: tensor([3, 2])
det_scores: tensor([0.7000, 0.8000])
bboxes: tensor([[0.3421, 0.7104, 0.6000, 0.1518],
[0.1872, 0.1669, 0.7563, 0.8777]])
) at 0x7f9f312b4a90>
"""
布尔索引
filter_results = instance_data[instance_data.det_scores >0.75]print(filter_results)"""
<InstanceData(
META INFORMATION
pad_shape: (800, 1216, 3)
img_shape: (800, 1196, 3)
DATA FIELDS
det_labels: tensor([2])
det_scores: tensor([0.8000])
bboxes: tensor([[0.1872, 0.1669, 0.7563, 0.8777]])
) at 0x7fa061299dc0>
"""
结果为空
empty_results = instance_data[instance_data.det_scores >1]print(empty_results)"""
<InstanceData(
META INFORMATION
pad_shape: (800, 1216, 3)
img_shape: (800, 1196, 3)
DATA FIELDS
det_labels: tensor([], dtype=torch.int64)
det_scores: tensor([])
bboxes: tensor([], size=(0, 4))
) at 0x7f9f439cccd0>
"""
拼接(cat)
用户可以将两个具有相同 key 的 InstanceData 拼接成一个 InstanceData。对于长度分别为 N 和 M 的两个 InstanceData, 拼接后为长度 N + M 的新的 InstanceData
img_meta =dict(img_shape=(800,1196,3), pad_shape=(800,1216,3))
instance_data = InstanceData(metainfo=img_meta)
instance_data.det_labels = torch.LongTensor([2,3])
instance_data.det_scores = torch.Tensor([0.8,0.7])
instance_data.bboxes = torch.rand((2,4))print('The length of instance_data is',len(instance_data))
cat_results = InstanceData.cat([instance_data, instance_data])print('The length of instance_data is',len(cat_results))print(cat_results)"""
The length of instance_data is 2
The length of instance_data is 4
<InstanceData(
META INFORMATION
pad_shape: (800, 1216, 3)
img_shape: (800, 1196, 3)
DATA FIELDS
det_labels: tensor([2, 3, 2, 3])
det_scores: tensor([0.8000, 0.7000, 0.8000, 0.7000])
bboxes: tensor([[0.5341, 0.8962, 0.9043, 0.2824],
[0.3864, 0.2215, 0.7610, 0.7060],
[0.5341, 0.8962, 0.9043, 0.2824],
[0.3864, 0.2215, 0.7610, 0.7060]])
) at 0x7fa061d4a9d0>
"""
4.2.2.3 PixelData
PixelData 在 BaseDataElement 的基础上,同样对 data 中存储的数据做了限制:
所有 data 内的数据均为 3 维,并且顺序为 (通道,高, 宽)
所有在 data 内的数据要有相同的长和宽 基于上述假定对 PixelData进行了扩展,包括:
对 PixelData 中 data 所存储的数据进行了尺寸的校验
支持对 data 部分的数据对实例进行空间维度的索引和切片。
4.2.2.4 数据校验 (PixelData)
PixelData 会对传入到 data 的数据进行维度与长宽的校验。
from mmengine.structures import PixelData
import random
import torch
import numpy as np
metainfo =dict(
img_id=random.randint(0,100),
img_shape=(random.randint(400,600), random.randint(400,600)))
image = np.random.randint(0,255,(4,20,40))
featmap = torch.randint(0,255,(10,20,40))
pixel_data = PixelData(metainfo=metainfo,
image=image,
featmap=featmap)print('The shape of pixel_data is', pixel_data.shape)# set
pixel_data.map3 = torch.randint(0,255,(20,40))print('The shape of pixel_data is', pixel_data.map3.shape)
pixel_data.map2 = torch.randint(0,255,(3,20,30))# AssertionError: the height and width of values (20, 30) is not consistent with the length of this :obj:`PixelData` (20, 40)
pixel_data.map2 = torch.randint(0,255,(1,3,20,40))# AssertionError: The dim of value must be 2 or 3, but got 4
4.2.2.5 空间维度索引 (PixelData)
PixelData 支持对 data 部分的数据对实例进行空间维度的索引和切片,只需传入长宽的索引即可。
metainfo =dict(
img_id=random.randint(0,100),
img_shape=(random.randint(400,600), random.randint(400,600)))
image = np.random.randint(0,255,(4,20,40))
featmap = torch.randint(0,255,(10,20,40))
pixel_data = PixelData(metainfo=metainfo,
image=image,
featmap=featmap)print('The shape of pixel_data is', pixel_data.shape)# The shape of pixel_data is (20, 40)
索引
index_data = pixel_data[10,20]print('The shape of index_data is', index_data.shape)# The shape of index_data is (1, 1)
切片
slice_data = pixel_data[10:20,20:40]print('The shape of slice_data is', slice_data.shape)# The shape of slice_data is (10, 20)
4.2.2.6 LabelData
LabelData 主要用来封装标签数据,如场景分类标签,文字识别标签等。LabelData 没有对 data 做任何限制,只提供了两个额外功能:onehot 与 index 的转换。
from mmengine.structures import LabelData
import torch
item = torch.tensor([1], dtype=torch.int64)
num_classes =10
onehot = LabelData.label_to_onehot(label=item, num_classes=num_classes)print(f'{num_classes} is convert to ', onehot)
index = LabelData.onehot_to_label(onehot=onehot)print(f'{onehot} is convert to ', index)"""
10 is convert to tensor([0, 1, 0, 0, 0, 0, 0, 0, 0, 0])
tensor([0, 1, 0, 0, 0, 0, 0, 0, 0, 0]) is convert to tensor([1])
"""
最近的研究表明大型模型训练将有利于提高模型质量,但是训练如此大的模型需要巨大的资源,单卡显存已经越来越难以满足存放整个模型,因此诞生了大模型训练技术,典型的如 DeepSpeed ZeRO 和 FairScale 的完全分片数据并行(Fully Sharded Data Parallel, FSDP)技术,其允许在数据并行进程之间分片模型的参数、梯度和优化器状态,并同时仍然保持数据并行的简单性。