Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
mmdetection3d
Commits
bb25d249
Unverified
Commit
bb25d249
authored
May 01, 2022
by
ChaimZhu
Committed by
GitHub
May 01, 2022
Browse files
[Fix] Fix registry potential bug (#1443)
* fix registry bug * fix ci problems * add
parent
aa959558
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
432 additions
and
4 deletions
+432
-4
mmdet3d/apis/train.py
mmdet3d/apis/train.py
+283
-3
mmdet3d/datasets/pipelines/test_time_aug.py
mmdet3d/datasets/pipelines/test_time_aug.py
+107
-0
mmdet3d/utils/__init__.py
mmdet3d/utils/__init__.py
+3
-1
mmdet3d/utils/misc.py
mmdet3d/utils/misc.py
+39
-0
No files found.
mmdet3d/apis/train.py
View file @
bb25d249
# Copyright (c) OpenMMLab. All rights reserved.
# Copyright (c) OpenMMLab. All rights reserved.
import
random
import
warnings
import
numpy
as
np
import
numpy
as
np
import
torch
import
torch
from
mmcv.runner
import
get_dist_info
from
mmcv.parallel
import
MMDataParallel
,
MMDistributedDataParallel
from
mmcv.runner
import
(
HOOKS
,
DistSamplerSeedHook
,
EpochBasedRunner
,
Fp16OptimizerHook
,
OptimizerHook
,
build_optimizer
,
build_runner
,
get_dist_info
)
from
mmcv.utils
import
build_from_cfg
from
torch
import
distributed
as
dist
from
torch
import
distributed
as
dist
from
mmdet.apis
import
train_detector
from
mmdet3d.datasets
import
build_dataset
from
mmseg.apis
import
train_segmentor
from
mmdet3d.utils
import
find_latest_checkpoint
from
mmdet.core
import
DistEvalHook
as
MMDET_DistEvalHook
from
mmdet.core
import
EvalHook
as
MMDET_EvalHook
from
mmdet.datasets
import
build_dataloader
as
build_mmdet_dataloader
from
mmdet.datasets
import
replace_ImageToTensor
from
mmdet.utils
import
get_root_logger
as
get_mmdet_root_logger
from
mmseg.core
import
DistEvalHook
as
MMSEG_DistEvalHook
from
mmseg.core
import
EvalHook
as
MMSEG_EvalHook
from
mmseg.datasets
import
build_dataloader
as
build_mmseg_dataloader
from
mmseg.utils
import
get_root_logger
as
get_mmseg_root_logger
def
init_random_seed
(
seed
=
None
,
device
=
'cuda'
):
def
init_random_seed
(
seed
=
None
,
device
=
'cuda'
):
...
@@ -39,6 +55,270 @@ def init_random_seed(seed=None, device='cuda'):
...
@@ -39,6 +55,270 @@ def init_random_seed(seed=None, device='cuda'):
return
random_num
.
item
()
return
random_num
.
item
()
def
set_random_seed
(
seed
,
deterministic
=
False
):
"""Set random seed.
Args:
seed (int): Seed to be used.
deterministic (bool): Whether to set the deterministic option for
CUDNN backend, i.e., set `torch.backends.cudnn.deterministic`
to True and `torch.backends.cudnn.benchmark` to False.
Default: False.
"""
random
.
seed
(
seed
)
np
.
random
.
seed
(
seed
)
torch
.
manual_seed
(
seed
)
torch
.
cuda
.
manual_seed_all
(
seed
)
if
deterministic
:
torch
.
backends
.
cudnn
.
deterministic
=
True
torch
.
backends
.
cudnn
.
benchmark
=
False
def
train_segmentor
(
model
,
dataset
,
cfg
,
distributed
=
False
,
validate
=
False
,
timestamp
=
None
,
meta
=
None
):
"""Launch segmentor training."""
logger
=
get_mmseg_root_logger
(
cfg
.
log_level
)
# prepare data loaders
dataset
=
dataset
if
isinstance
(
dataset
,
(
list
,
tuple
))
else
[
dataset
]
data_loaders
=
[
build_mmseg_dataloader
(
ds
,
cfg
.
data
.
samples_per_gpu
,
cfg
.
data
.
workers_per_gpu
,
# cfg.gpus will be ignored if distributed
len
(
cfg
.
gpu_ids
),
dist
=
distributed
,
seed
=
cfg
.
seed
,
drop_last
=
True
)
for
ds
in
dataset
]
# put model on gpus
if
distributed
:
find_unused_parameters
=
cfg
.
get
(
'find_unused_parameters'
,
False
)
# Sets the `find_unused_parameters` parameter in
# torch.nn.parallel.DistributedDataParallel
model
=
MMDistributedDataParallel
(
model
.
cuda
(),
device_ids
=
[
torch
.
cuda
.
current_device
()],
broadcast_buffers
=
False
,
find_unused_parameters
=
find_unused_parameters
)
else
:
model
=
MMDataParallel
(
model
.
cuda
(
cfg
.
gpu_ids
[
0
]),
device_ids
=
cfg
.
gpu_ids
)
# build runner
optimizer
=
build_optimizer
(
model
,
cfg
.
optimizer
)
if
cfg
.
get
(
'runner'
)
is
None
:
cfg
.
runner
=
{
'type'
:
'IterBasedRunner'
,
'max_iters'
:
cfg
.
total_iters
}
warnings
.
warn
(
'config is now expected to have a `runner` section, '
'please set `runner` in your config.'
,
UserWarning
)
runner
=
build_runner
(
cfg
.
runner
,
default_args
=
dict
(
model
=
model
,
batch_processor
=
None
,
optimizer
=
optimizer
,
work_dir
=
cfg
.
work_dir
,
logger
=
logger
,
meta
=
meta
))
# register hooks
runner
.
register_training_hooks
(
cfg
.
lr_config
,
cfg
.
optimizer_config
,
cfg
.
checkpoint_config
,
cfg
.
log_config
,
cfg
.
get
(
'momentum_config'
,
None
))
# an ugly walkaround to make the .log and .log.json filenames the same
runner
.
timestamp
=
timestamp
# register eval hooks
if
validate
:
val_dataset
=
build_dataset
(
cfg
.
data
.
val
,
dict
(
test_mode
=
True
))
val_dataloader
=
build_mmseg_dataloader
(
val_dataset
,
samples_per_gpu
=
1
,
workers_per_gpu
=
cfg
.
data
.
workers_per_gpu
,
dist
=
distributed
,
shuffle
=
False
)
eval_cfg
=
cfg
.
get
(
'evaluation'
,
{})
eval_cfg
[
'by_epoch'
]
=
cfg
.
runner
[
'type'
]
!=
'IterBasedRunner'
eval_hook
=
MMSEG_DistEvalHook
if
distributed
else
MMSEG_EvalHook
# In this PR (https://github.com/open-mmlab/mmcv/pull/1193), the
# priority of IterTimerHook has been modified from 'NORMAL' to 'LOW'.
runner
.
register_hook
(
eval_hook
(
val_dataloader
,
**
eval_cfg
),
priority
=
'LOW'
)
# user-defined hooks
if
cfg
.
get
(
'custom_hooks'
,
None
):
custom_hooks
=
cfg
.
custom_hooks
assert
isinstance
(
custom_hooks
,
list
),
\
f
'custom_hooks expect list type, but got
{
type
(
custom_hooks
)
}
'
for
hook_cfg
in
cfg
.
custom_hooks
:
assert
isinstance
(
hook_cfg
,
dict
),
\
'Each item in custom_hooks expects dict type, but got '
\
f
'
{
type
(
hook_cfg
)
}
'
hook_cfg
=
hook_cfg
.
copy
()
priority
=
hook_cfg
.
pop
(
'priority'
,
'NORMAL'
)
hook
=
build_from_cfg
(
hook_cfg
,
HOOKS
)
runner
.
register_hook
(
hook
,
priority
=
priority
)
if
cfg
.
resume_from
:
runner
.
resume
(
cfg
.
resume_from
)
elif
cfg
.
load_from
:
runner
.
load_checkpoint
(
cfg
.
load_from
)
runner
.
run
(
data_loaders
,
cfg
.
workflow
)
def
train_detector
(
model
,
dataset
,
cfg
,
distributed
=
False
,
validate
=
False
,
timestamp
=
None
,
meta
=
None
):
logger
=
get_mmdet_root_logger
(
log_level
=
cfg
.
log_level
)
# prepare data loaders
dataset
=
dataset
if
isinstance
(
dataset
,
(
list
,
tuple
))
else
[
dataset
]
if
'imgs_per_gpu'
in
cfg
.
data
:
logger
.
warning
(
'"imgs_per_gpu" is deprecated in MMDet V2.0. '
'Please use "samples_per_gpu" instead'
)
if
'samples_per_gpu'
in
cfg
.
data
:
logger
.
warning
(
f
'Got "imgs_per_gpu"=
{
cfg
.
data
.
imgs_per_gpu
}
and '
f
'"samples_per_gpu"=
{
cfg
.
data
.
samples_per_gpu
}
, "imgs_per_gpu"'
f
'=
{
cfg
.
data
.
imgs_per_gpu
}
is used in this experiments'
)
else
:
logger
.
warning
(
'Automatically set "samples_per_gpu"="imgs_per_gpu"='
f
'
{
cfg
.
data
.
imgs_per_gpu
}
in this experiments'
)
cfg
.
data
.
samples_per_gpu
=
cfg
.
data
.
imgs_per_gpu
runner_type
=
'EpochBasedRunner'
if
'runner'
not
in
cfg
else
cfg
.
runner
[
'type'
]
data_loaders
=
[
build_mmdet_dataloader
(
ds
,
cfg
.
data
.
samples_per_gpu
,
cfg
.
data
.
workers_per_gpu
,
# `num_gpus` will be ignored if distributed
num_gpus
=
len
(
cfg
.
gpu_ids
),
dist
=
distributed
,
seed
=
cfg
.
seed
,
runner_type
=
runner_type
,
persistent_workers
=
cfg
.
data
.
get
(
'persistent_workers'
,
False
))
for
ds
in
dataset
]
# put model on gpus
if
distributed
:
find_unused_parameters
=
cfg
.
get
(
'find_unused_parameters'
,
False
)
# Sets the `find_unused_parameters` parameter in
# torch.nn.parallel.DistributedDataParallel
model
=
MMDistributedDataParallel
(
model
.
cuda
(),
device_ids
=
[
torch
.
cuda
.
current_device
()],
broadcast_buffers
=
False
,
find_unused_parameters
=
find_unused_parameters
)
else
:
model
=
MMDataParallel
(
model
.
cuda
(
cfg
.
gpu_ids
[
0
]),
device_ids
=
cfg
.
gpu_ids
)
# build runner
optimizer
=
build_optimizer
(
model
,
cfg
.
optimizer
)
if
'runner'
not
in
cfg
:
cfg
.
runner
=
{
'type'
:
'EpochBasedRunner'
,
'max_epochs'
:
cfg
.
total_epochs
}
warnings
.
warn
(
'config is now expected to have a `runner` section, '
'please set `runner` in your config.'
,
UserWarning
)
else
:
if
'total_epochs'
in
cfg
:
assert
cfg
.
total_epochs
==
cfg
.
runner
.
max_epochs
runner
=
build_runner
(
cfg
.
runner
,
default_args
=
dict
(
model
=
model
,
optimizer
=
optimizer
,
work_dir
=
cfg
.
work_dir
,
logger
=
logger
,
meta
=
meta
))
# an ugly workaround to make .log and .log.json filenames the same
runner
.
timestamp
=
timestamp
# fp16 setting
fp16_cfg
=
cfg
.
get
(
'fp16'
,
None
)
if
fp16_cfg
is
not
None
:
optimizer_config
=
Fp16OptimizerHook
(
**
cfg
.
optimizer_config
,
**
fp16_cfg
,
distributed
=
distributed
)
elif
distributed
and
'type'
not
in
cfg
.
optimizer_config
:
optimizer_config
=
OptimizerHook
(
**
cfg
.
optimizer_config
)
else
:
optimizer_config
=
cfg
.
optimizer_config
# register hooks
runner
.
register_training_hooks
(
cfg
.
lr_config
,
optimizer_config
,
cfg
.
checkpoint_config
,
cfg
.
log_config
,
cfg
.
get
(
'momentum_config'
,
None
),
custom_hooks_config
=
cfg
.
get
(
'custom_hooks'
,
None
))
if
distributed
:
if
isinstance
(
runner
,
EpochBasedRunner
):
runner
.
register_hook
(
DistSamplerSeedHook
())
# register eval hooks
if
validate
:
# Support batch_size > 1 in validation
val_samples_per_gpu
=
cfg
.
data
.
val
.
pop
(
'samples_per_gpu'
,
1
)
if
val_samples_per_gpu
>
1
:
# Replace 'ImageToTensor' to 'DefaultFormatBundle'
cfg
.
data
.
val
.
pipeline
=
replace_ImageToTensor
(
cfg
.
data
.
val
.
pipeline
)
val_dataset
=
build_dataset
(
cfg
.
data
.
val
,
dict
(
test_mode
=
True
))
val_dataloader
=
build_mmdet_dataloader
(
val_dataset
,
samples_per_gpu
=
val_samples_per_gpu
,
workers_per_gpu
=
cfg
.
data
.
workers_per_gpu
,
dist
=
distributed
,
shuffle
=
False
)
eval_cfg
=
cfg
.
get
(
'evaluation'
,
{})
eval_cfg
[
'by_epoch'
]
=
cfg
.
runner
[
'type'
]
!=
'IterBasedRunner'
eval_hook
=
MMDET_DistEvalHook
if
distributed
else
MMDET_EvalHook
# In this PR (https://github.com/open-mmlab/mmcv/pull/1193), the
# priority of IterTimerHook has been modified from 'NORMAL' to 'LOW'.
runner
.
register_hook
(
eval_hook
(
val_dataloader
,
**
eval_cfg
),
priority
=
'LOW'
)
resume_from
=
None
if
cfg
.
resume_from
is
None
and
cfg
.
get
(
'auto_resume'
):
resume_from
=
find_latest_checkpoint
(
cfg
.
work_dir
)
if
resume_from
is
not
None
:
cfg
.
resume_from
=
resume_from
if
cfg
.
resume_from
:
runner
.
resume
(
cfg
.
resume_from
)
elif
cfg
.
load_from
:
runner
.
load_checkpoint
(
cfg
.
load_from
)
runner
.
run
(
data_loaders
,
cfg
.
workflow
)
def
train_model
(
model
,
def
train_model
(
model
,
dataset
,
dataset
,
cfg
,
cfg
,
...
...
mmdet3d/datasets/pipelines/test_time_aug.py
View file @
bb25d249
...
@@ -8,6 +8,113 @@ from ..builder import PIPELINES
...
@@ -8,6 +8,113 @@ from ..builder import PIPELINES
from
.compose
import
Compose
from
.compose
import
Compose
@
PIPELINES
.
register_module
()
class
MultiScaleFlipAug
:
"""Test-time augmentation with multiple scales and flipping. An example
configuration is as followed:
.. code-block::
img_scale=[(1333, 400), (1333, 800)],
flip=True,
transforms=[
dict(type='Resize', keep_ratio=True),
dict(type='RandomFlip'),
dict(type='Normalize', **img_norm_cfg),
dict(type='Pad', size_divisor=32),
dict(type='ImageToTensor', keys=['img']),
dict(type='Collect', keys=['img']),
]
After MultiScaleFLipAug with above configuration, the results are wrapped
into lists of the same length as followed:
.. code-block::
dict(
img=[...],
img_shape=[...],
scale=[(1333, 400), (1333, 400), (1333, 800), (1333, 800)]
flip=[False, True, False, True]
...
)
Args:
transforms (list[dict]): Transforms to apply in each augmentation.
img_scale (tuple | list[tuple] | None): Images scales for resizing.
scale_factor (float | list[float] | None): Scale factors for resizing.
flip (bool): Whether apply flip augmentation. Default: False.
flip_direction (str | list[str]): Flip augmentation directions,
options are "horizontal", "vertical" and "diagonal". If
flip_direction is a list, multiple flip augmentations will be
applied. It has no effect when flip == False. Default:
"horizontal".
"""
def
__init__
(
self
,
transforms
,
img_scale
=
None
,
scale_factor
=
None
,
flip
=
False
,
flip_direction
=
'horizontal'
):
self
.
transforms
=
Compose
(
transforms
)
assert
(
img_scale
is
None
)
^
(
scale_factor
is
None
),
(
'Must have but only one variable can be set'
)
if
img_scale
is
not
None
:
self
.
img_scale
=
img_scale
if
isinstance
(
img_scale
,
list
)
else
[
img_scale
]
self
.
scale_key
=
'scale'
assert
mmcv
.
is_list_of
(
self
.
img_scale
,
tuple
)
else
:
self
.
img_scale
=
scale_factor
if
isinstance
(
scale_factor
,
list
)
else
[
scale_factor
]
self
.
scale_key
=
'scale_factor'
self
.
flip
=
flip
self
.
flip_direction
=
flip_direction
if
isinstance
(
flip_direction
,
list
)
else
[
flip_direction
]
assert
mmcv
.
is_list_of
(
self
.
flip_direction
,
str
)
if
not
self
.
flip
and
self
.
flip_direction
!=
[
'horizontal'
]:
warnings
.
warn
(
'flip_direction has no effect when flip is set to False'
)
if
(
self
.
flip
and
not
any
([
t
[
'type'
]
==
'RandomFlip'
for
t
in
transforms
])):
warnings
.
warn
(
'flip has no effect when RandomFlip is not in transforms'
)
def
__call__
(
self
,
results
):
"""Call function to apply test time augment transforms on results.
Args:
results (dict): Result dict contains the data to transform.
Returns:
dict[str: list]: The augmented data, where each value is wrapped
into a list.
"""
aug_data
=
[]
flip_args
=
[(
False
,
None
)]
if
self
.
flip
:
flip_args
+=
[(
True
,
direction
)
for
direction
in
self
.
flip_direction
]
for
scale
in
self
.
img_scale
:
for
flip
,
direction
in
flip_args
:
_results
=
results
.
copy
()
_results
[
self
.
scale_key
]
=
scale
_results
[
'flip'
]
=
flip
_results
[
'flip_direction'
]
=
direction
data
=
self
.
transforms
(
_results
)
aug_data
.
append
(
data
)
# list of dict to dict of list
aug_data_dict
=
{
key
:
[]
for
key
in
aug_data
[
0
]}
for
data
in
aug_data
:
for
key
,
val
in
data
.
items
():
aug_data_dict
[
key
].
append
(
val
)
return
aug_data_dict
def
__repr__
(
self
):
repr_str
=
self
.
__class__
.
__name__
repr_str
+=
f
'(transforms=
{
self
.
transforms
}
, '
repr_str
+=
f
'img_scale=
{
self
.
img_scale
}
, flip=
{
self
.
flip
}
, '
repr_str
+=
f
'flip_direction=
{
self
.
flip_direction
}
)'
return
repr_str
@
PIPELINES
.
register_module
()
@
PIPELINES
.
register_module
()
class
MultiScaleFlipAug3D
(
object
):
class
MultiScaleFlipAug3D
(
object
):
"""Test-time augmentation with multiple scales and flipping.
"""Test-time augmentation with multiple scales and flipping.
...
...
mmdet3d/utils/__init__.py
View file @
bb25d249
...
@@ -4,9 +4,11 @@ from mmcv.utils import Registry, build_from_cfg, print_log
...
@@ -4,9 +4,11 @@ from mmcv.utils import Registry, build_from_cfg, print_log
from
.collect_env
import
collect_env
from
.collect_env
import
collect_env
from
.compat_cfg
import
compat_cfg
from
.compat_cfg
import
compat_cfg
from
.logger
import
get_root_logger
from
.logger
import
get_root_logger
from
.misc
import
find_latest_checkpoint
from
.setup_env
import
setup_multi_processes
from
.setup_env
import
setup_multi_processes
__all__
=
[
__all__
=
[
'Registry'
,
'build_from_cfg'
,
'get_root_logger'
,
'collect_env'
,
'Registry'
,
'build_from_cfg'
,
'get_root_logger'
,
'collect_env'
,
'print_log'
,
'setup_multi_processes'
,
'compat_cfg'
'print_log'
,
'setup_multi_processes'
,
'find_latest_checkpoint'
,
'compat_cfg'
]
]
mmdet3d/utils/misc.py
0 → 100644
View file @
bb25d249
# Copyright (c) OpenMMLab. All rights reserved.
import
glob
import
os.path
as
osp
import
warnings
def
find_latest_checkpoint
(
path
,
suffix
=
'pth'
):
"""Find the latest checkpoint from the working directory. This function is
copied from mmdetection.
Args:
path(str): The path to find checkpoints.
suffix(str): File extension.
Defaults to pth.
Returns:
latest_path(str | None): File path of the latest checkpoint.
References:
.. [1] https://github.com/microsoft/SoftTeacher
/blob/main/ssod/utils/patch.py
"""
if
not
osp
.
exists
(
path
):
warnings
.
warn
(
'The path of checkpoints does not exist.'
)
return
None
if
osp
.
exists
(
osp
.
join
(
path
,
f
'latest.
{
suffix
}
'
)):
return
osp
.
join
(
path
,
f
'latest.
{
suffix
}
'
)
checkpoints
=
glob
.
glob
(
osp
.
join
(
path
,
f
'*.
{
suffix
}
'
))
if
len
(
checkpoints
)
==
0
:
warnings
.
warn
(
'There are no checkpoints in the path.'
)
return
None
latest
=
-
1
latest_path
=
None
for
checkpoint
in
checkpoints
:
count
=
int
(
osp
.
basename
(
checkpoint
).
split
(
'_'
)[
-
1
].
split
(
'.'
)[
0
])
if
count
>
latest
:
latest
=
count
latest_path
=
checkpoint
return
latest_path
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment