[Improve] Clean useless code and reduce unit tests memory usage.

This commit is contained in:
mzr1996 2022-06-15 08:16:02 +00:00
parent cecff79a79
commit dd660ed99e
20 changed files with 84 additions and 826 deletions

View File

@ -1,4 +0,0 @@
# Copyright (c) OpenMMLab. All rights reserved.
from .test import ONNXRuntimeClassifier, TensorRTClassifier
__all__ = ['ONNXRuntimeClassifier', 'TensorRTClassifier']

View File

@ -1,96 +0,0 @@
# Copyright (c) OpenMMLab. All rights reserved.
import warnings
import numpy as np
import onnxruntime as ort
import torch
from mmcls.models.classifiers import BaseClassifier
class ONNXRuntimeClassifier(BaseClassifier):
"""Wrapper for classifier's inference with ONNXRuntime."""
def __init__(self, onnx_file, class_names, device_id):
super(ONNXRuntimeClassifier, self).__init__()
sess = ort.InferenceSession(onnx_file)
providers = ['CPUExecutionProvider']
options = [{}]
is_cuda_available = ort.get_device() == 'GPU'
if is_cuda_available:
providers.insert(0, 'CUDAExecutionProvider')
options.insert(0, {'device_id': device_id})
sess.set_providers(providers, options)
self.sess = sess
self.CLASSES = class_names
self.device_id = device_id
self.io_binding = sess.io_binding()
self.output_names = [_.name for _ in sess.get_outputs()]
self.is_cuda_available = is_cuda_available
def simple_test(self, img, img_metas, **kwargs):
raise NotImplementedError('This method is not implemented.')
def extract_feat(self, imgs):
raise NotImplementedError('This method is not implemented.')
def forward_train(self, imgs, **kwargs):
raise NotImplementedError('This method is not implemented.')
def forward_test(self, imgs, img_metas, **kwargs):
input_data = imgs
# set io binding for inputs/outputs
device_type = 'cuda' if self.is_cuda_available else 'cpu'
if not self.is_cuda_available:
input_data = input_data.cpu()
self.io_binding.bind_input(
name='input',
device_type=device_type,
device_id=self.device_id,
element_type=np.float32,
shape=input_data.shape,
buffer_ptr=input_data.data_ptr())
for name in self.output_names:
self.io_binding.bind_output(name)
# run session to get outputs
self.sess.run_with_iobinding(self.io_binding)
results = self.io_binding.copy_outputs_to_cpu()[0]
return list(results)
class TensorRTClassifier(BaseClassifier):
def __init__(self, trt_file, class_names, device_id):
super(TensorRTClassifier, self).__init__()
from mmcv.tensorrt import TRTWraper, load_tensorrt_plugin
try:
load_tensorrt_plugin()
except (ImportError, ModuleNotFoundError):
warnings.warn('If input model has custom op from mmcv, \
you may have to build mmcv with TensorRT from source.')
model = TRTWraper(
trt_file, input_names=['input'], output_names=['probs'])
self.model = model
self.device_id = device_id
self.CLASSES = class_names
def simple_test(self, img, img_metas, **kwargs):
raise NotImplementedError('This method is not implemented.')
def extract_feat(self, imgs):
raise NotImplementedError('This method is not implemented.')
def forward_train(self, imgs, **kwargs):
raise NotImplementedError('This method is not implemented.')
def forward_test(self, imgs, img_metas, **kwargs):
input_data = imgs
with torch.cuda.device(self.device_id), torch.no_grad():
results = self.model({'input': input_data})['probs']
results = results.detach().cpu().numpy()
return list(results)

View File

@ -1,12 +1,10 @@
# Copyright (c) OpenMMLab. All rights reserved.
from .class_num_check_hook import ClassNumCheckHook
from .lr_updater import CosineAnnealingCooldownLrUpdaterHook
from .precise_bn_hook import PreciseBNHook
from .visualization_hook import VisualizationHook
__all__ = [
'ClassNumCheckHook',
'PreciseBNHook',
'CosineAnnealingCooldownLrUpdaterHook',
'VisualizationHook',
]

View File

@ -1,85 +0,0 @@
# Copyright (c) OpenMMLab. All rights reserved.
from math import cos, pi
from mmcv.runner.hooks import LrUpdaterHook
from mmcls.registry import HOOKS
@HOOKS.register_module()
class CosineAnnealingCooldownLrUpdaterHook(LrUpdaterHook):
"""Cosine annealing learning rate scheduler with cooldown.
Args:
min_lr (float, optional): The minimum learning rate after annealing.
Defaults to None.
min_lr_ratio (float, optional): The minimum learning ratio after
nnealing. Defaults to None.
cool_down_ratio (float): The cooldown ratio. Defaults to 0.1.
cool_down_time (int): The cooldown time. Defaults to 10.
by_epoch (bool): If True, the learning rate changes epoch by epoch. If
False, the learning rate changes iter by iter. Defaults to True.
warmup (string, optional): Type of warmup used. It can be None (use no
warmup), 'constant', 'linear' or 'exp'. Defaults to None.
warmup_iters (int): The number of iterations or epochs that warmup
lasts. Defaults to 0.
warmup_ratio (float): LR used at the beginning of warmup equals to
``warmup_ratio * initial_lr``. Defaults to 0.1.
warmup_by_epoch (bool): If True, the ``warmup_iters``
means the number of epochs that warmup lasts, otherwise means the
number of iteration that warmup lasts. Defaults to False.
Note:
You need to set one and only one of ``min_lr`` and ``min_lr_ratio``.
"""
def __init__(self,
min_lr=None,
min_lr_ratio=None,
cool_down_ratio=0.1,
cool_down_time=10,
**kwargs):
assert (min_lr is None) ^ (min_lr_ratio is None)
self.min_lr = min_lr
self.min_lr_ratio = min_lr_ratio
self.cool_down_time = cool_down_time
self.cool_down_ratio = cool_down_ratio
super(CosineAnnealingCooldownLrUpdaterHook, self).__init__(**kwargs)
def get_lr(self, runner, base_lr):
if self.by_epoch:
progress = runner.epoch
max_progress = runner.max_epochs
else:
progress = runner.iter
max_progress = runner.max_iters
if self.min_lr_ratio is not None:
target_lr = base_lr * self.min_lr_ratio
else:
target_lr = self.min_lr
if progress > max_progress - self.cool_down_time:
return target_lr * self.cool_down_ratio
else:
max_progress = max_progress - self.cool_down_time
return annealing_cos(base_lr, target_lr, progress / max_progress)
def annealing_cos(start, end, factor, weight=1):
"""Calculate annealing cos learning rate.
Cosine anneal from `weight * start + (1 - weight) * end` to `end` as
percentage goes from 0.0 to 1.0.
Args:
start (float): The starting learning rate of the cosine annealing.
end (float): The ending learing rate of the cosine annealing.
factor (float): The coefficient of `pi` when calculating the current
percentage. Range from 0.0 to 1.0.
weight (float, optional): The combination factor of `start` and `end`
when calculating the actual starting learning rate. Default to 1.
"""
cos_out = cos(pi * factor) + 1
return end + 0.5 * weight * (start - end) * cos_out

View File

@ -1,7 +1 @@
# Copyright (c) OpenMMLab. All rights reserved.
from .dist_utils import DistOptimizerHook, allreduce_grads, sync_random_seed
from .misc import multi_apply
__all__ = [
'allreduce_grads', 'DistOptimizerHook', 'multi_apply', 'sync_random_seed'
]

View File

@ -1,98 +0,0 @@
# Copyright (c) OpenMMLab. All rights reserved.
from collections import OrderedDict
import numpy as np
import torch
import torch.distributed as dist
from mmcv.runner import OptimizerHook, get_dist_info
from torch._utils import (_flatten_dense_tensors, _take_tensors,
_unflatten_dense_tensors)
def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1):
if bucket_size_mb > 0:
bucket_size_bytes = bucket_size_mb * 1024 * 1024
buckets = _take_tensors(tensors, bucket_size_bytes)
else:
buckets = OrderedDict()
for tensor in tensors:
tp = tensor.type()
if tp not in buckets:
buckets[tp] = []
buckets[tp].append(tensor)
buckets = buckets.values()
for bucket in buckets:
flat_tensors = _flatten_dense_tensors(bucket)
dist.all_reduce(flat_tensors)
flat_tensors.div_(world_size)
for tensor, synced in zip(
bucket, _unflatten_dense_tensors(flat_tensors, bucket)):
tensor.copy_(synced)
def allreduce_grads(params, coalesce=True, bucket_size_mb=-1):
grads = [
param.grad.data for param in params
if param.requires_grad and param.grad is not None
]
world_size = dist.get_world_size()
if coalesce:
_allreduce_coalesced(grads, world_size, bucket_size_mb)
else:
for tensor in grads:
dist.all_reduce(tensor.div_(world_size))
class DistOptimizerHook(OptimizerHook):
def __init__(self, grad_clip=None, coalesce=True, bucket_size_mb=-1):
self.grad_clip = grad_clip
self.coalesce = coalesce
self.bucket_size_mb = bucket_size_mb
def after_train_iter(self, runner):
runner.optimizer.zero_grad()
runner.outputs['loss'].backward()
if self.grad_clip is not None:
self.clip_grads(runner.model.parameters())
runner.optimizer.step()
def sync_random_seed(seed=None, device='cuda'):
"""Make sure different ranks share the same seed.
All workers must call this function, otherwise it will deadlock.
This method is generally used in `DistributedSampler`,
because the seed should be identical across all processes
in the distributed group.
In distributed sampling, different ranks should sample non-overlapped
data in the dataset. Therefore, this function is used to make sure that
each rank shuffles the data indices in the same order based
on the same seed. Then different ranks could use different indices
to select non-overlapped data from the same data list.
Args:
seed (int, Optional): The seed. Default to None.
device (str): The device where the seed will be put on.
Default to 'cuda'.
Returns:
int: Seed to be used.
"""
if seed is None:
seed = np.random.randint(2**31)
assert isinstance(seed, int)
rank, world_size = get_dist_info()
if world_size == 1:
return seed
if rank == 0:
random_num = torch.tensor(seed, dtype=torch.int32, device=device)
else:
random_num = torch.tensor(0, dtype=torch.int32, device=device)
dist.broadcast(random_num, src=0)
return random_num.item()

View File

@ -1,8 +0,0 @@
# Copyright (c) OpenMMLab. All rights reserved.
from functools import partial
def multi_apply(func, *args, **kwargs):
pfunc = partial(func, **kwargs) if kwargs else func
map_results = map(pfunc, *args)
return tuple(map(list, zip(*map_results)))

View File

@ -1,7 +1,6 @@
# Copyright (c) OpenMMLab. All rights reserved.
from .base_dataset import BaseDataset
from .builder import (DATASETS, PIPELINES, SAMPLERS, build_dataloader,
build_dataset, build_sampler)
from .builder import build_dataset
from .cifar import CIFAR10, CIFAR100
from .cub import CUB
from .custom import CustomDataset
@ -11,14 +10,12 @@ from .imagenet import ImageNet, ImageNet21k
from .mnist import MNIST, FashionMNIST
from .multi_label import MultiLabelDataset
from .pipelines import * # noqa: F401,F403
from .samplers import DistributedSampler, RepeatAugSampler
from .samplers import * # noqa: F401,F403
from .voc import VOC
__all__ = [
'BaseDataset', 'ImageNet', 'CIFAR10', 'CIFAR100', 'MNIST', 'FashionMNIST',
'VOC', 'build_dataloader', 'build_dataset', 'DistributedSampler',
'ConcatDataset', 'RepeatDataset', 'ClassBalancedDataset', 'DATASETS',
'PIPELINES', 'ImageNet21k', 'SAMPLERS', 'build_sampler',
'RepeatAugSampler', 'KFoldDataset', 'CUB', 'CustomDataset',
'MultiLabelDataset'
'VOC', 'build_dataset', 'ConcatDataset', 'RepeatDataset',
'ClassBalancedDataset', 'ImageNet21k', 'KFoldDataset', 'CUB',
'CustomDataset', 'MultiLabelDataset'
]

View File

@ -1,184 +1,25 @@
# Copyright (c) OpenMMLab. All rights reserved.
import copy
import platform
import random
from functools import partial
import numpy as np
import torch
from mmcv.parallel import collate
from mmcv.runner import get_dist_info
from mmcv.utils import digit_version
from torch.utils.data import DataLoader
from mmcls.registry import DATA_SAMPLERS, DATASETS, TRANSFORMS
try:
from mmcv.utils import IS_IPU_AVAILABLE
except ImportError:
IS_IPU_AVAILABLE = False
if platform.system() != 'Windows':
# https://github.com/pytorch/pytorch/issues/973
import resource
rlimit = resource.getrlimit(resource.RLIMIT_NOFILE)
hard_limit = rlimit[1]
soft_limit = min(4096, hard_limit)
resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit))
PIPELINES = TRANSFORMS
SAMPLERS = DATA_SAMPLERS
from mmcls.registry import DATASETS
def build_dataset(cfg, default_args=None):
from .dataset_wrappers import (ClassBalancedDataset, ConcatDataset,
KFoldDataset, RepeatDataset)
if isinstance(cfg, (list, tuple)):
dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg])
elif cfg['type'] == 'ConcatDataset':
dataset = ConcatDataset(
[build_dataset(c, default_args) for c in cfg['datasets']],
separate_eval=cfg.get('separate_eval', True))
elif cfg['type'] == 'RepeatDataset':
dataset = RepeatDataset(
build_dataset(cfg['dataset'], default_args), cfg['times'])
elif cfg['type'] == 'ClassBalancedDataset':
dataset = ClassBalancedDataset(
build_dataset(cfg['dataset'], default_args), cfg['oversample_thr'])
elif cfg['type'] == 'KFoldDataset':
cp_cfg = copy.deepcopy(cfg)
if cp_cfg.get('test_mode', None) is None:
cp_cfg['test_mode'] = (default_args or {}).pop('test_mode', False)
cp_cfg['dataset'] = build_dataset(cp_cfg['dataset'], default_args)
cp_cfg.pop('type')
dataset = KFoldDataset(**cp_cfg)
else:
dataset = DATASETS.build(cfg, default_args=default_args)
def build_dataset(cfg):
"""Build dataset.
return dataset
def build_dataloader(dataset,
samples_per_gpu,
workers_per_gpu,
num_gpus=1,
dist=True,
shuffle=True,
round_up=True,
seed=None,
pin_memory=True,
persistent_workers=True,
sampler_cfg=None,
**kwargs):
"""Build PyTorch DataLoader.
In distributed training, each GPU/process has a dataloader.
In non-distributed training, there is only one dataloader for all GPUs.
Args:
dataset (Dataset): A PyTorch dataset.
samples_per_gpu (int): Number of training samples on each GPU, i.e.,
batch size of each GPU.
workers_per_gpu (int): How many subprocesses to use for data loading
for each GPU.
num_gpus (int): Number of GPUs. Only used in non-distributed training.
dist (bool): Distributed training/test or not. Default: True.
shuffle (bool): Whether to shuffle the data at every epoch.
Default: True.
round_up (bool): Whether to round up the length of dataset by adding
extra samples to make it evenly divisible. Default: True.
pin_memory (bool): Whether to use pin_memory in DataLoader.
Default: True
persistent_workers (bool): If True, the data loader will not shutdown
the worker processes after a dataset has been consumed once.
This allows to maintain the workers Dataset instances alive.
The argument also has effect in PyTorch>=1.7.0.
Default: True
sampler_cfg (dict): sampler configuration to override the default
sampler
kwargs: any keyword argument to be used to initialize DataLoader
Returns:
DataLoader: A PyTorch dataloader.
Examples:
>>> from mmcls.datasets import build_dataset
>>> mnist_train = build_dataset(
... dict(type='MNIST', data_prefix='data/mnist/', test_mode=False))
>>> print(mnist_train)
Dataset MNIST
Number of samples: 60000
Number of categories: 10
Prefix of data: data/mnist/
>>> mnist_test = build_dataset(
... dict(type='MNIST', data_prefix='data/mnist/', test_mode=True))
>>> print(mnist_test)
Dataset MNIST
Number of samples: 10000
Number of categories: 10
Prefix of data: data/mnist/
"""
rank, world_size = get_dist_info()
# Custom sampler logic
if sampler_cfg:
# shuffle=False when val and test
sampler_cfg.update(shuffle=shuffle)
sampler = build_sampler(
sampler_cfg,
default_args=dict(
dataset=dataset, num_replicas=world_size, rank=rank,
seed=seed))
# Default sampler logic
elif dist:
sampler = build_sampler(
dict(
type='DistributedSampler',
dataset=dataset,
num_replicas=world_size,
rank=rank,
shuffle=shuffle,
round_up=round_up,
seed=seed))
else:
sampler = None
# If sampler exists, turn off dataloader shuffle
if sampler is not None:
shuffle = False
if dist:
batch_size = samples_per_gpu
num_workers = workers_per_gpu
else:
batch_size = num_gpus * samples_per_gpu
num_workers = num_gpus * workers_per_gpu
init_fn = partial(
worker_init_fn, num_workers=num_workers, rank=rank,
seed=seed) if seed is not None else None
if digit_version(torch.__version__) >= digit_version('1.8.0'):
kwargs['persistent_workers'] = persistent_workers
if IS_IPU_AVAILABLE:
from mmcv.device.ipu import IPUDataLoader
data_loader = IPUDataLoader(
dataset,
None,
batch_size=samples_per_gpu,
num_workers=num_workers,
shuffle=shuffle,
worker_init_fn=init_fn,
**kwargs)
else:
data_loader = DataLoader(
dataset,
batch_size=batch_size,
sampler=sampler,
num_workers=num_workers,
collate_fn=partial(collate, samples_per_gpu=samples_per_gpu),
pin_memory=pin_memory,
shuffle=shuffle,
worker_init_fn=init_fn,
**kwargs)
return data_loader
def worker_init_fn(worker_id, num_workers, rank, seed):
# The seed of each worker equals to
# num_worker * rank + worker_id + user_seed
worker_seed = num_workers * rank + worker_id + seed
np.random.seed(worker_seed)
random.seed(worker_seed)
torch.manual_seed(worker_seed)
def build_sampler(cfg, default_args=None):
if cfg is None:
return None
else:
return DATA_SAMPLERS.build(cfg, default_args=default_args)
return DATASETS.build(cfg)

View File

@ -1,5 +1,4 @@
# Copyright (c) OpenMMLab. All rights reserved.
from .distributed_sampler import DistributedSampler
from .repeat_aug import RepeatAugSampler
__all__ = ('DistributedSampler', 'RepeatAugSampler')
__all__ = ('RepeatAugSampler', )

View File

@ -1,60 +0,0 @@
# Copyright (c) OpenMMLab. All rights reserved.
import torch
from torch.utils.data import DistributedSampler as _DistributedSampler
from mmcls.core.utils import sync_random_seed
from mmcls.registry import DATA_SAMPLERS
@DATA_SAMPLERS.register_module()
class DistributedSampler(_DistributedSampler):
def __init__(self,
dataset,
num_replicas=None,
rank=None,
shuffle=True,
round_up=True,
seed=0):
super().__init__(dataset, num_replicas=num_replicas, rank=rank)
self.shuffle = shuffle
self.round_up = round_up
if self.round_up:
self.total_size = self.num_samples * self.num_replicas
else:
self.total_size = len(self.dataset)
# In distributed sampling, different ranks should sample
# non-overlapped data in the dataset. Therefore, this function
# is used to make sure that each rank shuffles the data indices
# in the same order based on the same seed. Then different ranks
# could use different indices to select non-overlapped data from the
# same data list.
self.seed = sync_random_seed(seed)
def __iter__(self):
# deterministically shuffle based on epoch
if self.shuffle:
g = torch.Generator()
# When :attr:`shuffle=True`, this ensures all replicas
# use a different random ordering for each epoch.
# Otherwise, the next iteration of this sampler will
# yield the same ordering.
g.manual_seed(self.epoch + self.seed)
indices = torch.randperm(len(self.dataset), generator=g).tolist()
else:
indices = torch.arange(len(self.dataset)).tolist()
# add extra samples to make it evenly divisible
if self.round_up:
indices = (
indices *
int(self.total_size / len(indices) + 1))[:self.total_size]
assert len(indices) == self.total_size
# subsample
indices = indices[self.rank:self.total_size:self.num_replicas]
if self.round_up:
assert len(indices) == self.num_samples
return iter(indices)

View File

@ -2,9 +2,9 @@ import math
import torch
from mmcv.runner import get_dist_info
from mmengine.dist import sync_random_seed
from torch.utils.data import Sampler
from mmcls.core.utils import sync_random_seed
from mmcls.registry import DATA_SAMPLERS

View File

@ -9,7 +9,7 @@ from unittest.mock import MagicMock, call, patch
import numpy as np
from mmengine.registry import TRANSFORMS
from mmcls.datasets import DATASETS
from mmcls.registry import DATASETS
from mmcls.utils import get_root_logger
mmcls_logger = get_root_logger()

View File

@ -31,19 +31,3 @@ def test_convert_to_one_hot():
ori_one_hot_targets.scatter_(1, targets.long(), 1)
one_hot_targets = convert_to_one_hot(targets, classes)
assert torch.equal(ori_one_hot_targets, one_hot_targets)
# test cuda version
@pytest.mark.skipif(
not torch.cuda.is_available(), reason='requires CUDA support')
def test_convert_to_one_hot_cuda():
# test with original impl
classes = 10
targets = torch.randint(high=classes, size=(10, 1)).cuda()
ori_one_hot_targets = torch.zeros((targets.shape[0], classes),
dtype=torch.long,
device=targets.device)
ori_one_hot_targets.scatter_(1, targets.long(), 1)
one_hot_targets = convert_to_one_hot(targets, classes)
assert torch.equal(ori_one_hot_targets, one_hot_targets)
assert ori_one_hot_targets.device == one_hot_targets.device

View File

@ -51,11 +51,11 @@ def test_conformer_backbone():
assert check_norm_state(model.modules(), True)
imgs = torch.randn(3, 3, 224, 224)
imgs = torch.randn(1, 3, 224, 224)
conv_feature, transformer_feature = model(imgs)[-1]
assert conv_feature.shape == (3, 64 * 1 * 4
assert conv_feature.shape == (1, 64 * 1 * 4
) # base_channels * channel_ratio * 4
assert transformer_feature.shape == (3, 384)
assert transformer_feature.shape == (1, 384)
# Test Conformer with irregular input size.
model = Conformer(**cfg_ori)
@ -64,17 +64,17 @@ def test_conformer_backbone():
assert check_norm_state(model.modules(), True)
imgs = torch.randn(3, 3, 241, 241)
imgs = torch.randn(1, 3, 241, 241)
conv_feature, transformer_feature = model(imgs)[-1]
assert conv_feature.shape == (3, 64 * 1 * 4
assert conv_feature.shape == (1, 64 * 1 * 4
) # base_channels * channel_ratio * 4
assert transformer_feature.shape == (3, 384)
assert transformer_feature.shape == (1, 384)
imgs = torch.randn(3, 3, 321, 221)
imgs = torch.randn(1, 3, 321, 221)
conv_feature, transformer_feature = model(imgs)[-1]
assert conv_feature.shape == (3, 64 * 1 * 4
assert conv_feature.shape == (1, 64 * 1 * 4
) # base_channels * channel_ratio * 4
assert transformer_feature.shape == (3, 384)
assert transformer_feature.shape == (1, 384)
# Test custom arch Conformer without output cls token
cfg = deepcopy(cfg_ori)
@ -88,8 +88,8 @@ def test_conformer_backbone():
cfg['base_channels'] = 32
model = Conformer(**cfg)
conv_feature, transformer_feature = model(imgs)[-1]
assert conv_feature.shape == (3, 32 * 3 * 4)
assert transformer_feature.shape == (3, 128)
assert conv_feature.shape == (1, 32 * 3 * 4)
assert transformer_feature.shape == (1, 128)
# Test Conformer with multi out indices
cfg = deepcopy(cfg_ori)
@ -99,13 +99,13 @@ def test_conformer_backbone():
assert len(outs) == 3
# stage 1
conv_feature, transformer_feature = outs[0]
assert conv_feature.shape == (3, 64 * 1)
assert transformer_feature.shape == (3, 384)
assert conv_feature.shape == (1, 64 * 1)
assert transformer_feature.shape == (1, 384)
# stage 2
conv_feature, transformer_feature = outs[1]
assert conv_feature.shape == (3, 64 * 1 * 2)
assert transformer_feature.shape == (3, 384)
assert conv_feature.shape == (1, 64 * 1 * 2)
assert transformer_feature.shape == (1, 384)
# stage 3
conv_feature, transformer_feature = outs[2]
assert conv_feature.shape == (3, 64 * 1 * 4)
assert transformer_feature.shape == (3, 384)
assert conv_feature.shape == (1, 64 * 1 * 4)
assert transformer_feature.shape == (1, 384)

View File

@ -16,7 +16,7 @@ class TestDeiT(TestCase):
def setUp(self):
self.cfg = dict(
arch='deit-base', img_size=224, patch_size=16, drop_rate=0.1)
arch='deit-tiny', img_size=224, patch_size=16, drop_rate=0.1)
def test_init_weights(self):
# test weight init cfg
@ -60,7 +60,7 @@ class TestDeiT(TestCase):
os.remove(checkpoint)
def test_forward(self):
imgs = torch.randn(3, 3, 224, 224)
imgs = torch.randn(1, 3, 224, 224)
# test with_cls_token=False
cfg = deepcopy(self.cfg)
@ -77,7 +77,7 @@ class TestDeiT(TestCase):
self.assertIsInstance(outs, tuple)
self.assertEqual(len(outs), 1)
patch_token = outs[-1]
self.assertEqual(patch_token.shape, (3, 768, 14, 14))
self.assertEqual(patch_token.shape, (1, 192, 14, 14))
# test with output_cls_token
cfg = deepcopy(self.cfg)
@ -86,9 +86,9 @@ class TestDeiT(TestCase):
self.assertIsInstance(outs, tuple)
self.assertEqual(len(outs), 1)
patch_token, cls_token, dist_token = outs[-1]
self.assertEqual(patch_token.shape, (3, 768, 14, 14))
self.assertEqual(cls_token.shape, (3, 768))
self.assertEqual(dist_token.shape, (3, 768))
self.assertEqual(patch_token.shape, (1, 192, 14, 14))
self.assertEqual(cls_token.shape, (1, 192))
self.assertEqual(dist_token.shape, (1, 192))
# test without output_cls_token
cfg = deepcopy(self.cfg)
@ -98,7 +98,7 @@ class TestDeiT(TestCase):
self.assertIsInstance(outs, tuple)
self.assertEqual(len(outs), 1)
patch_token = outs[-1]
self.assertEqual(patch_token.shape, (3, 768, 14, 14))
self.assertEqual(patch_token.shape, (1, 192, 14, 14))
# Test forward with multi out indices
cfg = deepcopy(self.cfg)
@ -109,14 +109,14 @@ class TestDeiT(TestCase):
self.assertEqual(len(outs), 3)
for out in outs:
patch_token, cls_token, dist_token = out
self.assertEqual(patch_token.shape, (3, 768, 14, 14))
self.assertEqual(cls_token.shape, (3, 768))
self.assertEqual(dist_token.shape, (3, 768))
self.assertEqual(patch_token.shape, (1, 192, 14, 14))
self.assertEqual(cls_token.shape, (1, 192))
self.assertEqual(dist_token.shape, (1, 192))
# Test forward with dynamic input size
imgs1 = torch.randn(3, 3, 224, 224)
imgs2 = torch.randn(3, 3, 256, 256)
imgs3 = torch.randn(3, 3, 256, 309)
imgs1 = torch.randn(1, 3, 224, 224)
imgs2 = torch.randn(1, 3, 256, 256)
imgs3 = torch.randn(1, 3, 256, 309)
cfg = deepcopy(self.cfg)
model = DistilledVisionTransformer(**cfg)
for imgs in [imgs1, imgs2, imgs3]:
@ -126,6 +126,6 @@ class TestDeiT(TestCase):
patch_token, cls_token, dist_token = outs[-1]
expect_feat_shape = (math.ceil(imgs.shape[2] / 16),
math.ceil(imgs.shape[3] / 16))
self.assertEqual(patch_token.shape, (3, 768, *expect_feat_shape))
self.assertEqual(cls_token.shape, (3, 768))
self.assertEqual(dist_token.shape, (3, 768))
self.assertEqual(patch_token.shape, (1, 192, *expect_feat_shape))
self.assertEqual(cls_token.shape, (1, 192))
self.assertEqual(dist_token.shape, (1, 192))

View File

@ -90,7 +90,7 @@ class TestMLPMixer(TestCase):
self.assertFalse(torch.allclose(ori_weight, initialized_weight))
def test_forward(self):
imgs = torch.randn(3, 3, 224, 224)
imgs = torch.randn(1, 3, 224, 224)
# test forward with single out indices
cfg = deepcopy(self.cfg)
@ -99,7 +99,7 @@ class TestMLPMixer(TestCase):
self.assertIsInstance(outs, tuple)
self.assertEqual(len(outs), 1)
feat = outs[-1]
self.assertEqual(feat.shape, (3, 768, 196))
self.assertEqual(feat.shape, (1, 768, 196))
# test forward with multi out indices
cfg = deepcopy(self.cfg)
@ -109,10 +109,10 @@ class TestMLPMixer(TestCase):
self.assertIsInstance(outs, tuple)
self.assertEqual(len(outs), 3)
for feat in outs:
self.assertEqual(feat.shape, (3, 768, 196))
self.assertEqual(feat.shape, (1, 768, 196))
# test with invalid input shape
imgs2 = torch.randn(3, 3, 256, 256)
imgs2 = torch.randn(1, 3, 256, 256)
cfg = deepcopy(self.cfg)
model = MlpMixer(**cfg)
with self.assertRaisesRegex(AssertionError, 'dynamic input shape.'):

View File

@ -28,7 +28,7 @@ class TestSwinTransformer(TestCase):
def setUp(self):
self.cfg = dict(
arch='b', img_size=224, patch_size=4, drop_path_rate=0.1)
arch='tiny', img_size=224, patch_size=4, drop_path_rate=0.1)
def test_arch(self):
# Test invalid default arch
@ -135,7 +135,7 @@ class TestSwinTransformer(TestCase):
self.assertIsInstance(outs, tuple)
self.assertEqual(len(outs), 1)
feat = outs[-1]
self.assertEqual(feat.shape, (1, 1024, 7, 7))
self.assertEqual(feat.shape, (1, 768, 7, 7))
# test with window_size=12
cfg = deepcopy(self.cfg)
@ -145,7 +145,7 @@ class TestSwinTransformer(TestCase):
self.assertIsInstance(outs, tuple)
self.assertEqual(len(outs), 1)
feat = outs[-1]
self.assertEqual(feat.shape, (1, 1024, 12, 12))
self.assertEqual(feat.shape, (1, 768, 12, 12))
with self.assertRaisesRegex(AssertionError, r'the window size \(12\)'):
model(torch.randn(1, 3, 224, 224))
@ -158,7 +158,7 @@ class TestSwinTransformer(TestCase):
self.assertIsInstance(outs, tuple)
self.assertEqual(len(outs), 1)
feat = outs[-1]
self.assertEqual(feat.shape, (1, 1024, 7, 7))
self.assertEqual(feat.shape, (1, 768, 7, 7))
# test multiple output indices
cfg = deepcopy(self.cfg)
@ -169,7 +169,7 @@ class TestSwinTransformer(TestCase):
self.assertEqual(len(outs), 4)
for stride, out in zip([2, 4, 8, 8], outs):
self.assertEqual(out.shape,
(1, 128 * stride, 56 // stride, 56 // stride))
(1, 96 * stride, 56 // stride, 56 // stride))
# test with checkpoint forward
cfg = deepcopy(self.cfg)
@ -185,7 +185,7 @@ class TestSwinTransformer(TestCase):
self.assertIsInstance(outs, tuple)
self.assertEqual(len(outs), 1)
feat = outs[-1]
self.assertEqual(feat.shape, (1, 1024, 7, 7))
self.assertEqual(feat.shape, (1, 768, 7, 7))
# test with dynamic input shape
imgs1 = torch.randn(1, 3, 224, 224)
@ -200,7 +200,7 @@ class TestSwinTransformer(TestCase):
feat = outs[-1]
expect_feat_shape = (math.ceil(imgs.shape[2] / 32),
math.ceil(imgs.shape[3] / 32))
self.assertEqual(feat.shape, (1, 1024, *expect_feat_shape))
self.assertEqual(feat.shape, (1, 768, *expect_feat_shape))
def test_structure(self):
# test drop_path_rate decay

View File

@ -115,7 +115,7 @@ class TestVisionTransformer(TestCase):
os.remove(checkpoint)
def test_forward(self):
imgs = torch.randn(3, 3, 224, 224)
imgs = torch.randn(1, 3, 224, 224)
# test with_cls_token=False
cfg = deepcopy(self.cfg)
@ -132,7 +132,7 @@ class TestVisionTransformer(TestCase):
self.assertIsInstance(outs, tuple)
self.assertEqual(len(outs), 1)
patch_token = outs[-1]
self.assertEqual(patch_token.shape, (3, 768, 14, 14))
self.assertEqual(patch_token.shape, (1, 768, 14, 14))
# test with output_cls_token
cfg = deepcopy(self.cfg)
@ -141,8 +141,8 @@ class TestVisionTransformer(TestCase):
self.assertIsInstance(outs, tuple)
self.assertEqual(len(outs), 1)
patch_token, cls_token = outs[-1]
self.assertEqual(patch_token.shape, (3, 768, 14, 14))
self.assertEqual(cls_token.shape, (3, 768))
self.assertEqual(patch_token.shape, (1, 768, 14, 14))
self.assertEqual(cls_token.shape, (1, 768))
# test without output_cls_token
cfg = deepcopy(self.cfg)
@ -152,7 +152,7 @@ class TestVisionTransformer(TestCase):
self.assertIsInstance(outs, tuple)
self.assertEqual(len(outs), 1)
patch_token = outs[-1]
self.assertEqual(patch_token.shape, (3, 768, 14, 14))
self.assertEqual(patch_token.shape, (1, 768, 14, 14))
# Test forward with multi out indices
cfg = deepcopy(self.cfg)
@ -163,13 +163,13 @@ class TestVisionTransformer(TestCase):
self.assertEqual(len(outs), 3)
for out in outs:
patch_token, cls_token = out
self.assertEqual(patch_token.shape, (3, 768, 14, 14))
self.assertEqual(cls_token.shape, (3, 768))
self.assertEqual(patch_token.shape, (1, 768, 14, 14))
self.assertEqual(cls_token.shape, (1, 768))
# Test forward with dynamic input size
imgs1 = torch.randn(3, 3, 224, 224)
imgs2 = torch.randn(3, 3, 256, 256)
imgs3 = torch.randn(3, 3, 256, 309)
imgs1 = torch.randn(1, 3, 224, 224)
imgs2 = torch.randn(1, 3, 256, 256)
imgs3 = torch.randn(1, 3, 256, 309)
cfg = deepcopy(self.cfg)
model = VisionTransformer(**cfg)
for imgs in [imgs1, imgs2, imgs3]:
@ -179,5 +179,5 @@ class TestVisionTransformer(TestCase):
patch_token, cls_token = outs[-1]
expect_feat_shape = (math.ceil(imgs.shape[2] / 16),
math.ceil(imgs.shape[3] / 16))
self.assertEqual(patch_token.shape, (3, 768, *expect_feat_shape))
self.assertEqual(cls_token.shape, (3, 768))
self.assertEqual(patch_token.shape, (1, 768, *expect_feat_shape))
self.assertEqual(cls_token.shape, (1, 768))

View File

@ -1,204 +0,0 @@
# Copyright (c) OpenMMLab. All rights reserved.
import logging
import tempfile
from unittest.mock import MagicMock, patch
import mmcv.runner
import pytest
import torch
import torch.nn as nn
from mmcv.runner import obj_from_dict
from mmcv.runner.hooks import DistEvalHook, EvalHook
from torch.utils.data import DataLoader, Dataset
from mmcls.apis import single_gpu_test
class ExampleDataset(Dataset):
def __getitem__(self, idx):
results = dict(img=torch.tensor([1]), img_metas=dict())
return results
def __len__(self):
return 1
class ExampleModel(nn.Module):
def __init__(self):
super(ExampleModel, self).__init__()
self.test_cfg = None
self.conv = nn.Conv2d(3, 3, 3)
def forward(self, img, img_metas, test_mode=False, **kwargs):
return img
def train_step(self, data_batch, optimizer):
loss = self.forward(**data_batch)
return dict(loss=loss)
def test_iter_eval_hook():
with pytest.raises(TypeError):
test_dataset = ExampleModel()
data_loader = [
DataLoader(
test_dataset,
batch_size=1,
sampler=None,
num_worker=0,
shuffle=False)
]
EvalHook(data_loader, by_epoch=False)
test_dataset = ExampleDataset()
test_dataset.evaluate = MagicMock(return_value=dict(test='success'))
loader = DataLoader(test_dataset, batch_size=1)
model = ExampleModel()
data_loader = DataLoader(
test_dataset, batch_size=1, sampler=None, num_workers=0, shuffle=False)
optim_cfg = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005)
optimizer = obj_from_dict(optim_cfg, torch.optim,
dict(params=model.parameters()))
# test EvalHook
with tempfile.TemporaryDirectory() as tmpdir:
eval_hook = EvalHook(data_loader, by_epoch=False)
runner = mmcv.runner.IterBasedRunner(
model=model,
optimizer=optimizer,
work_dir=tmpdir,
logger=logging.getLogger(),
max_iters=1)
runner.register_hook(eval_hook)
runner.run([loader], [('train', 1)], 1)
test_dataset.evaluate.assert_called_with([torch.tensor([1])],
logger=runner.logger)
def test_epoch_eval_hook():
with pytest.raises(TypeError):
test_dataset = ExampleModel()
data_loader = [
DataLoader(
test_dataset,
batch_size=1,
sampler=None,
num_worker=0,
shuffle=False)
]
EvalHook(data_loader, by_epoch=True)
test_dataset = ExampleDataset()
test_dataset.evaluate = MagicMock(return_value=dict(test='success'))
loader = DataLoader(test_dataset, batch_size=1)
model = ExampleModel()
data_loader = DataLoader(
test_dataset, batch_size=1, sampler=None, num_workers=0, shuffle=False)
optim_cfg = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005)
optimizer = obj_from_dict(optim_cfg, torch.optim,
dict(params=model.parameters()))
# test EvalHook with interval
with tempfile.TemporaryDirectory() as tmpdir:
eval_hook = EvalHook(data_loader, by_epoch=True, interval=2)
runner = mmcv.runner.EpochBasedRunner(
model=model,
optimizer=optimizer,
work_dir=tmpdir,
logger=logging.getLogger(),
max_epochs=2)
runner.register_hook(eval_hook)
runner.run([loader], [('train', 1)])
test_dataset.evaluate.assert_called_once_with([torch.tensor([1])],
logger=runner.logger)
def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False):
results = single_gpu_test(model, data_loader)
return results
@patch('mmcls.apis.multi_gpu_test', multi_gpu_test)
def test_dist_eval_hook():
with pytest.raises(TypeError):
test_dataset = ExampleModel()
data_loader = [
DataLoader(
test_dataset,
batch_size=1,
sampler=None,
num_worker=0,
shuffle=False)
]
DistEvalHook(data_loader, by_epoch=False)
test_dataset = ExampleDataset()
test_dataset.evaluate = MagicMock(return_value=dict(test='success'))
loader = DataLoader(test_dataset, batch_size=1)
model = ExampleModel()
data_loader = DataLoader(
test_dataset, batch_size=1, sampler=None, num_workers=0, shuffle=False)
optim_cfg = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005)
optimizer = obj_from_dict(optim_cfg, torch.optim,
dict(params=model.parameters()))
# test DistEvalHook
with tempfile.TemporaryDirectory() as tmpdir:
p = patch('mmcv.engine.multi_gpu_test', multi_gpu_test)
p.start()
eval_hook = DistEvalHook(data_loader, by_epoch=False)
runner = mmcv.runner.IterBasedRunner(
model=model,
optimizer=optimizer,
work_dir=tmpdir,
logger=logging.getLogger(),
max_iters=1)
runner.register_hook(eval_hook)
runner.run([loader], [('train', 1)])
test_dataset.evaluate.assert_called_with([torch.tensor([1])],
logger=runner.logger)
p.stop()
@patch('mmcls.apis.multi_gpu_test', multi_gpu_test)
def test_dist_eval_hook_epoch():
with pytest.raises(TypeError):
test_dataset = ExampleModel()
data_loader = [
DataLoader(
test_dataset,
batch_size=1,
sampler=None,
num_worker=0,
shuffle=False)
]
DistEvalHook(data_loader)
test_dataset = ExampleDataset()
test_dataset.evaluate = MagicMock(return_value=dict(test='success'))
loader = DataLoader(test_dataset, batch_size=1)
model = ExampleModel()
data_loader = DataLoader(
test_dataset, batch_size=1, sampler=None, num_workers=0, shuffle=False)
optim_cfg = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0005)
optimizer = obj_from_dict(optim_cfg, torch.optim,
dict(params=model.parameters()))
# test DistEvalHook
with tempfile.TemporaryDirectory() as tmpdir:
p = patch('mmcv.engine.multi_gpu_test', multi_gpu_test)
p.start()
eval_hook = DistEvalHook(data_loader, by_epoch=True, interval=2)
runner = mmcv.runner.EpochBasedRunner(
model=model,
optimizer=optimizer,
work_dir=tmpdir,
logger=logging.getLogger(),
max_epochs=2)
runner.register_hook(eval_hook)
runner.run([loader], [('train', 1)])
test_dataset.evaluate.assert_called_with([torch.tensor([1])],
logger=runner.logger)
p.stop()