Upload 127 files
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- CAGE_expression_inference-apvit/affectnet_annotations/train_set_annotation_without_lnd.csv +0 -0
- CAGE_expression_inference-apvit/affectnet_annotations/val_set_annotation_without_lnd.csv +0 -0
- CAGE_expression_inference-apvit/apvit_mmcls/__init__.py +3 -0
- CAGE_expression_inference-apvit/apvit_mmcls/apis/__init__.py +8 -0
- CAGE_expression_inference-apvit/apvit_mmcls/apis/inference.py +108 -0
- CAGE_expression_inference-apvit/apvit_mmcls/apis/test.py +158 -0
- CAGE_expression_inference-apvit/apvit_mmcls/apis/train.py +140 -0
- CAGE_expression_inference-apvit/apvit_mmcls/core/__init__.py +3 -0
- CAGE_expression_inference-apvit/apvit_mmcls/core/evaluation/__init__.py +8 -0
- CAGE_expression_inference-apvit/apvit_mmcls/core/evaluation/eval_hooks.py +158 -0
- CAGE_expression_inference-apvit/apvit_mmcls/core/evaluation/mean_ap.py +73 -0
- CAGE_expression_inference-apvit/apvit_mmcls/core/evaluation/multilabel_eval_metrics.py +71 -0
- CAGE_expression_inference-apvit/apvit_mmcls/core/fp16/__init__.py +4 -0
- CAGE_expression_inference-apvit/apvit_mmcls/core/fp16/decorators.py +160 -0
- CAGE_expression_inference-apvit/apvit_mmcls/core/fp16/hooks.py +132 -0
- CAGE_expression_inference-apvit/apvit_mmcls/core/fp16/utils.py +23 -0
- CAGE_expression_inference-apvit/apvit_mmcls/core/utils/__init__.py +4 -0
- CAGE_expression_inference-apvit/apvit_mmcls/core/utils/dist_utils.py +56 -0
- CAGE_expression_inference-apvit/apvit_mmcls/core/utils/misc.py +7 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/__init__.py +16 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/base_dataset.py +185 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/builder.py +110 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/cifar.py +123 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/dataset_wrappers.py +173 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/imagenet.py +1105 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/mnist.py +171 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/pipelines/__init__.py +15 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/pipelines/compose.py +42 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/pipelines/formating.py +156 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/pipelines/loading.py +67 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/pipelines/test_time_aug.py +126 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/pipelines/transforms.py +918 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/raf.py +144 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/samplers/__init__.py +3 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/samplers/distributed_sampler.py +42 -0
- CAGE_expression_inference-apvit/apvit_mmcls/datasets/utils.py +152 -0
- CAGE_expression_inference-apvit/apvit_mmcls/models/__init__.py +13 -0
- CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/__init__.py +32 -0
- CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/alexnet.py +55 -0
- CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/base_backbone.py +57 -0
- CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/iresnet.py +220 -0
- CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/irse.py +387 -0
- CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/irse_nopadding.py +366 -0
- CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/lenet.py +41 -0
- CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/mobilefacenet.py +133 -0
- CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/mobilenet_v2.py +273 -0
- CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/mobilenet_v3.py +186 -0
- CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/modules/t2t.py +57 -0
- CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/modules/vit.py +197 -0
- CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/modules/vit_pooling.py +89 -0
CAGE_expression_inference-apvit/affectnet_annotations/train_set_annotation_without_lnd.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
CAGE_expression_inference-apvit/affectnet_annotations/val_set_annotation_without_lnd.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
CAGE_expression_inference-apvit/apvit_mmcls/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .version import __version__
|
| 2 |
+
|
| 3 |
+
__all__ = ['__version__']
|
CAGE_expression_inference-apvit/apvit_mmcls/apis/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .inference import inference_model, init_model, show_result_pyplot
|
| 2 |
+
from .test import multi_gpu_test, single_gpu_test
|
| 3 |
+
from .train import set_random_seed, train_model
|
| 4 |
+
|
| 5 |
+
__all__ = [
|
| 6 |
+
'set_random_seed', 'train_model', 'init_model', 'inference_model',
|
| 7 |
+
'multi_gpu_test', 'single_gpu_test', 'show_result_pyplot'
|
| 8 |
+
]
|
CAGE_expression_inference-apvit/apvit_mmcls/apis/inference.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import warnings
|
| 2 |
+
|
| 3 |
+
import matplotlib.pyplot as plt
|
| 4 |
+
import mmcv
|
| 5 |
+
import numpy as np
|
| 6 |
+
import torch
|
| 7 |
+
from mmcv.parallel import collate, scatter
|
| 8 |
+
from mmcv.runner import load_checkpoint
|
| 9 |
+
|
| 10 |
+
from mmcls.datasets.pipelines import Compose
|
| 11 |
+
from mmcls.models import build_classifier
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def init_model(config, checkpoint=None, device='cuda:0', options=None):
|
| 15 |
+
"""Initialize a classifier from config file.
|
| 16 |
+
|
| 17 |
+
Args:
|
| 18 |
+
config (str or :obj:`mmcv.Config`): Config file path or the config
|
| 19 |
+
object.
|
| 20 |
+
checkpoint (str, optional): Checkpoint path. If left as None, the model
|
| 21 |
+
will not load any weights.
|
| 22 |
+
options (dict): Options to override some settings in the used config.
|
| 23 |
+
|
| 24 |
+
Returns:
|
| 25 |
+
nn.Module: The constructed classifier.
|
| 26 |
+
"""
|
| 27 |
+
if isinstance(config, str):
|
| 28 |
+
config = mmcv.Config.fromfile(config)
|
| 29 |
+
elif not isinstance(config, mmcv.Config):
|
| 30 |
+
raise TypeError('config must be a filename or Config object, '
|
| 31 |
+
f'but got {type(config)}')
|
| 32 |
+
if options is not None:
|
| 33 |
+
config.merge_from_dict(options)
|
| 34 |
+
config.model.pretrained = None
|
| 35 |
+
config.model.extractor.pretrained = None
|
| 36 |
+
config.model.vit.pretrained = None
|
| 37 |
+
model = build_classifier(config.model)
|
| 38 |
+
if checkpoint is not None:
|
| 39 |
+
map_loc = 'cpu' if device == 'cpu' else None
|
| 40 |
+
checkpoint = load_checkpoint(model, checkpoint, map_location=map_loc)
|
| 41 |
+
class_loaded = False
|
| 42 |
+
if 'meta' in checkpoint:
|
| 43 |
+
if 'CLASSES' in checkpoint['meta']:
|
| 44 |
+
model.CLASSES = checkpoint['meta']['CLASSES']
|
| 45 |
+
class_loaded = True
|
| 46 |
+
if not class_loaded:
|
| 47 |
+
from mmcls.datasets.raf import FER_CLASSES
|
| 48 |
+
model.CLASSES = FER_CLASSES
|
| 49 |
+
model.cfg = config # save the config in the model for convenience
|
| 50 |
+
model.to(device)
|
| 51 |
+
model.eval()
|
| 52 |
+
return model
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def inference_model(model, img):
|
| 56 |
+
"""Inference image(s) with the classifier.
|
| 57 |
+
|
| 58 |
+
Args:
|
| 59 |
+
model (nn.Module): The loaded classifier.
|
| 60 |
+
img (str/ndarray): The image filename or loaded image.
|
| 61 |
+
|
| 62 |
+
Returns:
|
| 63 |
+
result (dict): The classification results that contains
|
| 64 |
+
`class_name`, `pred_label` and `pred_score`.
|
| 65 |
+
"""
|
| 66 |
+
cfg = model.cfg
|
| 67 |
+
device = next(model.parameters()).device # model device
|
| 68 |
+
# build the data pipeline
|
| 69 |
+
if isinstance(img, str):
|
| 70 |
+
if cfg.data.test.pipeline[0]['type'] != 'LoadImageFromFile':
|
| 71 |
+
cfg.data.test.pipeline.insert(0, dict(type='LoadImageFromFile'))
|
| 72 |
+
data = dict(img_info=dict(filename=img), img_prefix=None)
|
| 73 |
+
else:
|
| 74 |
+
if cfg.data.test.pipeline[0]['type'] == 'LoadImageFromFile':
|
| 75 |
+
cfg.data.test.pipeline.pop(0)
|
| 76 |
+
data = dict(img=img)
|
| 77 |
+
test_pipeline = Compose(cfg.data.test.pipeline)
|
| 78 |
+
data = test_pipeline(data)
|
| 79 |
+
data = collate([data], samples_per_gpu=1)
|
| 80 |
+
if next(model.parameters()).is_cuda:
|
| 81 |
+
# scatter to specified GPU
|
| 82 |
+
data = scatter(data, [device])[0]
|
| 83 |
+
|
| 84 |
+
# forward the model
|
| 85 |
+
with torch.no_grad():
|
| 86 |
+
scores = model(return_loss=False, **data)
|
| 87 |
+
pred_score = np.max(scores, axis=1)[0]
|
| 88 |
+
pred_label = np.argmax(scores, axis=1)[0]
|
| 89 |
+
result = {'pred_label': pred_label, 'pred_score': float(pred_score)}
|
| 90 |
+
result['pred_class'] = model.CLASSES[result['pred_label']]
|
| 91 |
+
return result
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def show_result_pyplot(model, img, result, fig_size=(15, 10)):
|
| 95 |
+
"""Visualize the classification results on the image.
|
| 96 |
+
|
| 97 |
+
Args:
|
| 98 |
+
model (nn.Module): The loaded classifier.
|
| 99 |
+
img (str or np.ndarray): Image filename or loaded image.
|
| 100 |
+
result (list): The classification result.
|
| 101 |
+
fig_size (tuple): Figure size of the pyplot figure.
|
| 102 |
+
"""
|
| 103 |
+
if hasattr(model, 'module'):
|
| 104 |
+
model = model.module
|
| 105 |
+
img = model.show_result(img, result, show=False)
|
| 106 |
+
plt.figure(figsize=fig_size)
|
| 107 |
+
plt.imshow(mmcv.bgr2rgb(img))
|
| 108 |
+
plt.show()
|
CAGE_expression_inference-apvit/apvit_mmcls/apis/test.py
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os.path as osp
|
| 2 |
+
import pickle
|
| 3 |
+
import shutil
|
| 4 |
+
import tempfile
|
| 5 |
+
import time
|
| 6 |
+
|
| 7 |
+
import mmcv
|
| 8 |
+
import torch
|
| 9 |
+
import torch.distributed as dist
|
| 10 |
+
from mmcv.runner import get_dist_info
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def single_gpu_test(model, data_loader, show=False, out_dir=None):
|
| 14 |
+
model.eval()
|
| 15 |
+
results = []
|
| 16 |
+
dataset = data_loader.dataset
|
| 17 |
+
prog_bar = mmcv.ProgressBar(len(dataset))
|
| 18 |
+
for i, data in enumerate(data_loader):
|
| 19 |
+
with torch.no_grad():
|
| 20 |
+
result = model(return_loss=False, **data)
|
| 21 |
+
results.append(result)
|
| 22 |
+
|
| 23 |
+
if show or out_dir:
|
| 24 |
+
pass # TODO
|
| 25 |
+
|
| 26 |
+
batch_size = data['img'].size(0)
|
| 27 |
+
for _ in range(batch_size):
|
| 28 |
+
prog_bar.update()
|
| 29 |
+
return results
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def multi_gpu_test(model, data_loader, tmpdir=None, gpu_collect=False):
|
| 33 |
+
"""Test model with multiple gpus.
|
| 34 |
+
|
| 35 |
+
This method tests model with multiple gpus and collects the results
|
| 36 |
+
under two different modes: gpu and cpu modes. By setting 'gpu_collect=True'
|
| 37 |
+
it encodes results to gpu tensors and use gpu communication for results
|
| 38 |
+
collection. On cpu mode it saves the results on different gpus to 'tmpdir'
|
| 39 |
+
and collects them by the rank 0 worker.
|
| 40 |
+
|
| 41 |
+
Args:
|
| 42 |
+
model (nn.Module): Model to be tested.
|
| 43 |
+
data_loader (nn.Dataloader): Pytorch data loader.
|
| 44 |
+
tmpdir (str): Path of directory to save the temporary results from
|
| 45 |
+
different gpus under cpu mode.
|
| 46 |
+
gpu_collect (bool): Option to use either gpu or cpu to collect results.
|
| 47 |
+
|
| 48 |
+
Returns:
|
| 49 |
+
list: The prediction results.
|
| 50 |
+
"""
|
| 51 |
+
model.eval()
|
| 52 |
+
results = []
|
| 53 |
+
dataset = data_loader.dataset
|
| 54 |
+
rank, world_size = get_dist_info()
|
| 55 |
+
if rank == 0:
|
| 56 |
+
prog_bar = mmcv.ProgressBar(len(dataset))
|
| 57 |
+
time.sleep(2) # This line can prevent deadlock problem in some cases.
|
| 58 |
+
for i, data in enumerate(data_loader):
|
| 59 |
+
with torch.no_grad():
|
| 60 |
+
result = model(return_loss=False, **data)
|
| 61 |
+
if isinstance(result, list):
|
| 62 |
+
results.extend(result)
|
| 63 |
+
else:
|
| 64 |
+
results.append(result)
|
| 65 |
+
|
| 66 |
+
# if rank == 0:
|
| 67 |
+
# batch_size = data['img'].size(0)
|
| 68 |
+
# for _ in range(batch_size * world_size):
|
| 69 |
+
# prog_bar.update()
|
| 70 |
+
|
| 71 |
+
# collect results from all ranks
|
| 72 |
+
if gpu_collect:
|
| 73 |
+
results = collect_results_gpu(results, len(dataset))
|
| 74 |
+
else:
|
| 75 |
+
results = collect_results_cpu(results, len(dataset), tmpdir)
|
| 76 |
+
return results
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def collect_results_cpu(result_part, size, tmpdir=None):
|
| 80 |
+
rank, world_size = get_dist_info()
|
| 81 |
+
# create a tmp dir if it is not specified
|
| 82 |
+
if tmpdir is None:
|
| 83 |
+
MAX_LEN = 512
|
| 84 |
+
# 32 is whitespace
|
| 85 |
+
dir_tensor = torch.full((MAX_LEN, ),
|
| 86 |
+
32,
|
| 87 |
+
dtype=torch.uint8,
|
| 88 |
+
device='cuda')
|
| 89 |
+
if rank == 0:
|
| 90 |
+
tmpdir = tempfile.mkdtemp()
|
| 91 |
+
tmpdir = torch.tensor(
|
| 92 |
+
bytearray(tmpdir.encode()), dtype=torch.uint8, device='cuda')
|
| 93 |
+
dir_tensor[:len(tmpdir)] = tmpdir
|
| 94 |
+
dist.broadcast(dir_tensor, 0)
|
| 95 |
+
tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip()
|
| 96 |
+
else:
|
| 97 |
+
mmcv.mkdir_or_exist(tmpdir)
|
| 98 |
+
# dump the part result to the dir
|
| 99 |
+
mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl'))
|
| 100 |
+
dist.barrier()
|
| 101 |
+
# collect all parts
|
| 102 |
+
if rank != 0:
|
| 103 |
+
return None
|
| 104 |
+
else:
|
| 105 |
+
# load results of all parts from tmp dir
|
| 106 |
+
part_list = []
|
| 107 |
+
for i in range(world_size):
|
| 108 |
+
part_file = osp.join(tmpdir, f'part_{i}.pkl')
|
| 109 |
+
part_result = mmcv.load(part_file)
|
| 110 |
+
# When data is severely insufficient, an empty part_result
|
| 111 |
+
# on a certain gpu could makes the overall outputs empty.
|
| 112 |
+
if part_result:
|
| 113 |
+
part_list.append(part_result)
|
| 114 |
+
# sort the results
|
| 115 |
+
ordered_results = []
|
| 116 |
+
for res in zip(*part_list):
|
| 117 |
+
ordered_results.extend(list(res))
|
| 118 |
+
# the dataloader may pad some samples
|
| 119 |
+
ordered_results = ordered_results[:size]
|
| 120 |
+
# remove tmp dir
|
| 121 |
+
shutil.rmtree(tmpdir)
|
| 122 |
+
return ordered_results
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def collect_results_gpu(result_part, size):
|
| 126 |
+
rank, world_size = get_dist_info()
|
| 127 |
+
# dump result part to tensor with pickle
|
| 128 |
+
part_tensor = torch.tensor(
|
| 129 |
+
bytearray(pickle.dumps(result_part)), dtype=torch.uint8, device='cuda')
|
| 130 |
+
# gather all result part tensor shape
|
| 131 |
+
shape_tensor = torch.tensor(part_tensor.shape, device='cuda')
|
| 132 |
+
shape_list = [shape_tensor.clone() for _ in range(world_size)]
|
| 133 |
+
dist.all_gather(shape_list, shape_tensor)
|
| 134 |
+
# padding result part tensor to max length
|
| 135 |
+
shape_max = torch.tensor(shape_list).max()
|
| 136 |
+
part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda')
|
| 137 |
+
part_send[:shape_tensor[0]] = part_tensor
|
| 138 |
+
part_recv_list = [
|
| 139 |
+
part_tensor.new_zeros(shape_max) for _ in range(world_size)
|
| 140 |
+
]
|
| 141 |
+
# gather all result part
|
| 142 |
+
dist.all_gather(part_recv_list, part_send)
|
| 143 |
+
|
| 144 |
+
if rank == 0:
|
| 145 |
+
part_list = []
|
| 146 |
+
for recv, shape in zip(part_recv_list, shape_list):
|
| 147 |
+
part_result = pickle.loads(recv[:shape[0]].cpu().numpy().tobytes())
|
| 148 |
+
# When data is severely insufficient, an empty part_result
|
| 149 |
+
# on a certain gpu could makes the overall outputs empty.
|
| 150 |
+
if part_result:
|
| 151 |
+
part_list.append(part_result)
|
| 152 |
+
# sort the results
|
| 153 |
+
ordered_results = []
|
| 154 |
+
for res in zip(*part_list):
|
| 155 |
+
ordered_results.extend(list(res))
|
| 156 |
+
# the dataloader may pad some samples
|
| 157 |
+
ordered_results = ordered_results[:size]
|
| 158 |
+
return ordered_results
|
CAGE_expression_inference-apvit/apvit_mmcls/apis/train.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import random
|
| 2 |
+
import warnings
|
| 3 |
+
|
| 4 |
+
import numpy as np
|
| 5 |
+
import torch
|
| 6 |
+
from mmcv.parallel import MMDataParallel, MMDistributedDataParallel
|
| 7 |
+
from mmcv.runner import DistSamplerSeedHook, build_optimizer, build_runner, Fp16OptimizerHook, OptimizerHook
|
| 8 |
+
|
| 9 |
+
from mmcls.core import (DistEvalHook, EvalHook)
|
| 10 |
+
from mmcls.datasets import build_dataloader, build_dataset
|
| 11 |
+
from mmcls.utils import get_root_logger
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def set_random_seed(seed, deterministic=False):
|
| 15 |
+
"""Set random seed.
|
| 16 |
+
|
| 17 |
+
Args:
|
| 18 |
+
seed (int): Seed to be used.
|
| 19 |
+
deterministic (bool): Whether to set the deterministic option for
|
| 20 |
+
CUDNN backend, i.e., set `torch.backends.cudnn.deterministic`
|
| 21 |
+
to True and `torch.backends.cudnn.benchmark` to False.
|
| 22 |
+
Default: False.
|
| 23 |
+
"""
|
| 24 |
+
random.seed(seed)
|
| 25 |
+
np.random.seed(seed)
|
| 26 |
+
torch.manual_seed(seed)
|
| 27 |
+
torch.cuda.manual_seed_all(seed)
|
| 28 |
+
if deterministic:
|
| 29 |
+
torch.backends.cudnn.deterministic = True
|
| 30 |
+
torch.backends.cudnn.benchmark = False
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def train_model(model,
|
| 34 |
+
dataset,
|
| 35 |
+
cfg,
|
| 36 |
+
distributed=False,
|
| 37 |
+
validate=False,
|
| 38 |
+
timestamp=None,
|
| 39 |
+
meta=None):
|
| 40 |
+
logger = get_root_logger(cfg.log_level)
|
| 41 |
+
|
| 42 |
+
# prepare data loaders
|
| 43 |
+
dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset]
|
| 44 |
+
|
| 45 |
+
data_loaders = [
|
| 46 |
+
build_dataloader(
|
| 47 |
+
ds,
|
| 48 |
+
cfg.data.samples_per_gpu,
|
| 49 |
+
cfg.data.workers_per_gpu,
|
| 50 |
+
# cfg.gpus will be ignored if distributed
|
| 51 |
+
num_gpus=len(cfg.gpu_ids),
|
| 52 |
+
dist=distributed,
|
| 53 |
+
round_up=True,
|
| 54 |
+
seed=cfg.seed,
|
| 55 |
+
# persistent_workers=True
|
| 56 |
+
) for ds in dataset
|
| 57 |
+
]
|
| 58 |
+
|
| 59 |
+
# build runner
|
| 60 |
+
optimizer = build_optimizer(model, cfg.optimizer)
|
| 61 |
+
|
| 62 |
+
# if 'use_fp16' in cfg and cfg.use_fp16:
|
| 63 |
+
# import apex
|
| 64 |
+
# model, optimizer = apex.amp.initialize(model.cuda(), optimizer, opt_level="O1")
|
| 65 |
+
# warnings.warn('**** Initializing mixed precision done. ****')
|
| 66 |
+
|
| 67 |
+
# put model on gpus
|
| 68 |
+
if distributed:
|
| 69 |
+
find_unused_parameters = cfg.get('find_unused_parameters', False)
|
| 70 |
+
# Sets the `find_unused_parameters` parameter in
|
| 71 |
+
# torch.nn.parallel.DistributedDataParallel
|
| 72 |
+
model = MMDistributedDataParallel(
|
| 73 |
+
model.cuda(),
|
| 74 |
+
device_ids=[torch.cuda.current_device()],
|
| 75 |
+
broadcast_buffers=False,
|
| 76 |
+
find_unused_parameters=find_unused_parameters)
|
| 77 |
+
else:
|
| 78 |
+
model = MMDataParallel(
|
| 79 |
+
model.cuda(cfg.gpu_ids[0]), device_ids=cfg.gpu_ids)
|
| 80 |
+
|
| 81 |
+
if cfg.get('runner') is None:
|
| 82 |
+
cfg.runner = {
|
| 83 |
+
'type': 'EpochBasedRunner',
|
| 84 |
+
'max_epochs': cfg.total_epochs
|
| 85 |
+
}
|
| 86 |
+
warnings.warn(
|
| 87 |
+
'config is now expected to have a `runner` section, '
|
| 88 |
+
'please set `runner` in your config.', UserWarning)
|
| 89 |
+
|
| 90 |
+
runner = build_runner(
|
| 91 |
+
cfg.runner,
|
| 92 |
+
default_args=dict(
|
| 93 |
+
model=model,
|
| 94 |
+
batch_processor=None,
|
| 95 |
+
optimizer=optimizer,
|
| 96 |
+
work_dir=cfg.work_dir,
|
| 97 |
+
logger=logger,
|
| 98 |
+
meta=meta))
|
| 99 |
+
|
| 100 |
+
# an ugly walkaround to make the .log and .log.json filenames the same
|
| 101 |
+
runner.timestamp = timestamp
|
| 102 |
+
|
| 103 |
+
# fp16 setting
|
| 104 |
+
fp16_cfg = cfg.get('fp16', None)
|
| 105 |
+
if fp16_cfg is not None:
|
| 106 |
+
optimizer_config = Fp16OptimizerHook(
|
| 107 |
+
**cfg.optimizer_config, **fp16_cfg, distributed=distributed)
|
| 108 |
+
elif distributed and 'type' not in cfg.optimizer_config:
|
| 109 |
+
# optimizer_config = DistOptimizerHook(**cfg.optimizer_config)
|
| 110 |
+
optimizer_config = OptimizerHook(**cfg.optimizer_config)
|
| 111 |
+
else:
|
| 112 |
+
optimizer_config = cfg.optimizer_config
|
| 113 |
+
|
| 114 |
+
# register hooks
|
| 115 |
+
runner.register_training_hooks(cfg.lr_config, optimizer_config,
|
| 116 |
+
cfg.checkpoint_config, cfg.log_config,
|
| 117 |
+
cfg.get('momentum_config', None))
|
| 118 |
+
if distributed and cfg.runner.type=='EpochBasedRunner':
|
| 119 |
+
runner.register_hook(DistSamplerSeedHook())
|
| 120 |
+
|
| 121 |
+
# register eval hooks
|
| 122 |
+
if validate:
|
| 123 |
+
val_dataset = build_dataset(cfg.data.val, dict(test_mode=True))
|
| 124 |
+
val_dataloader = build_dataloader(
|
| 125 |
+
val_dataset,
|
| 126 |
+
samples_per_gpu=cfg.data.samples_per_gpu,
|
| 127 |
+
workers_per_gpu=cfg.data.workers_per_gpu,
|
| 128 |
+
dist=distributed,
|
| 129 |
+
shuffle=False,
|
| 130 |
+
round_up=True)
|
| 131 |
+
eval_cfg = cfg.get('evaluation', {})
|
| 132 |
+
eval_cfg['by_epoch'] = cfg.runner['type'] != 'IterBasedRunner'
|
| 133 |
+
eval_hook = DistEvalHook if distributed else EvalHook
|
| 134 |
+
runner.register_hook(eval_hook(val_dataloader, **eval_cfg), priority='LOW')
|
| 135 |
+
|
| 136 |
+
if cfg.resume_from:
|
| 137 |
+
runner.resume(cfg.resume_from)
|
| 138 |
+
elif cfg.load_from:
|
| 139 |
+
runner.load_checkpoint(cfg.load_from)
|
| 140 |
+
runner.run(data_loaders, cfg.workflow)
|
CAGE_expression_inference-apvit/apvit_mmcls/core/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .evaluation import * # noqa: F401, F403
|
| 2 |
+
from .fp16 import * # noqa: F401, F403
|
| 3 |
+
from .utils import * # noqa: F401, F403
|
CAGE_expression_inference-apvit/apvit_mmcls/core/evaluation/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .eval_hooks import DistEvalHook, EvalHook
|
| 2 |
+
from .mean_ap import average_precision, mAP
|
| 3 |
+
from .multilabel_eval_metrics import average_performance
|
| 4 |
+
|
| 5 |
+
__all__ = [
|
| 6 |
+
'DistEvalHook', 'EvalHook', 'average_precision', 'mAP',
|
| 7 |
+
'average_performance'
|
| 8 |
+
]
|
CAGE_expression_inference-apvit/apvit_mmcls/core/evaluation/eval_hooks.py
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os.path as osp
|
| 2 |
+
from math import inf
|
| 3 |
+
|
| 4 |
+
from mmcv.runner import Hook
|
| 5 |
+
from torch.utils.data import DataLoader
|
| 6 |
+
from mmcls.utils import get_root_logger
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class EvalHook(Hook):
|
| 10 |
+
"""Evaluation hook.
|
| 11 |
+
|
| 12 |
+
Args:
|
| 13 |
+
dataloader (DataLoader): A PyTorch dataloader.
|
| 14 |
+
interval (int): Evaluation interval (by epochs). Default: 1.
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
def __init__(self, dataloader, interval=1, by_epoch=True, **eval_kwargs):
|
| 18 |
+
if not isinstance(dataloader, DataLoader):
|
| 19 |
+
raise TypeError('dataloader must be a pytorch DataLoader, but got'
|
| 20 |
+
f' {type(dataloader)}')
|
| 21 |
+
self.dataloader = dataloader
|
| 22 |
+
self.interval = interval
|
| 23 |
+
self.eval_kwargs = eval_kwargs
|
| 24 |
+
self.by_epoch = by_epoch
|
| 25 |
+
self.logger = get_root_logger()
|
| 26 |
+
|
| 27 |
+
def after_train_epoch(self, runner):
|
| 28 |
+
if not self.by_epoch or not self.every_n_epochs(runner, self.interval):
|
| 29 |
+
return
|
| 30 |
+
from mmcls.apis import single_gpu_test
|
| 31 |
+
results = single_gpu_test(runner.model, self.dataloader, show=False)
|
| 32 |
+
self.evaluate(runner, results)
|
| 33 |
+
|
| 34 |
+
def after_train_iter(self, runner):
|
| 35 |
+
if self.by_epoch or not self.every_n_iters(runner, self.interval):
|
| 36 |
+
return
|
| 37 |
+
from mmcls.apis import single_gpu_test
|
| 38 |
+
runner.log_buffer.clear()
|
| 39 |
+
results = single_gpu_test(runner.model, self.dataloader, show=False)
|
| 40 |
+
self.evaluate(runner, results)
|
| 41 |
+
|
| 42 |
+
def evaluate(self, runner, results):
|
| 43 |
+
eval_res = self.dataloader.dataset.evaluate(
|
| 44 |
+
results, logger=runner.logger, **self.eval_kwargs)
|
| 45 |
+
for name, val in eval_res.items():
|
| 46 |
+
runner.log_buffer.output[name] = val
|
| 47 |
+
runner.log_buffer.ready = True
|
| 48 |
+
return eval_res
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
class DistEvalHook(EvalHook):
|
| 52 |
+
"""Distributed evaluation hook.
|
| 53 |
+
|
| 54 |
+
Args:
|
| 55 |
+
dataloader (DataLoader): A PyTorch dataloader.
|
| 56 |
+
interval (int): Evaluation interval (by epochs). Default: 1.
|
| 57 |
+
tmpdir (str | None): Temporary directory to save the results of all
|
| 58 |
+
processes. Default: None.
|
| 59 |
+
gpu_collect (bool): Whether to use gpu or cpu to collect results.
|
| 60 |
+
Default: False.
|
| 61 |
+
"""
|
| 62 |
+
|
| 63 |
+
def __init__(self,
|
| 64 |
+
dataloader,
|
| 65 |
+
interval=1,
|
| 66 |
+
gpu_collect=True,
|
| 67 |
+
by_epoch=True,
|
| 68 |
+
print_best=True,
|
| 69 |
+
**eval_kwargs):
|
| 70 |
+
if not isinstance(dataloader, DataLoader):
|
| 71 |
+
raise TypeError('dataloader must be a pytorch DataLoader, but got '
|
| 72 |
+
f'{type(dataloader)}')
|
| 73 |
+
super().__init__(dataloader, interval, by_epoch, **eval_kwargs)
|
| 74 |
+
self.dataloader = dataloader
|
| 75 |
+
self.interval = interval
|
| 76 |
+
self.gpu_collect = gpu_collect
|
| 77 |
+
self.by_epoch = by_epoch
|
| 78 |
+
self.eval_kwargs = eval_kwargs
|
| 79 |
+
self.print_best = print_best
|
| 80 |
+
|
| 81 |
+
def before_run(self, runner):
|
| 82 |
+
if self.print_best is not None:
|
| 83 |
+
if runner.meta is None:
|
| 84 |
+
warnings.warn('runner.meta is None. Creating a empty one.')
|
| 85 |
+
runner.meta = dict()
|
| 86 |
+
runner.meta.setdefault('hook_msgs', dict())
|
| 87 |
+
|
| 88 |
+
def before_train_epoch(self, runner):
|
| 89 |
+
return
|
| 90 |
+
# freeze IRNet
|
| 91 |
+
# frozen_blocks = 7
|
| 92 |
+
model = runner.model.module.convert
|
| 93 |
+
model.eval()
|
| 94 |
+
for param in model.parameters():
|
| 95 |
+
param.requires_grad = False
|
| 96 |
+
return
|
| 97 |
+
|
| 98 |
+
if frozen_blocks > 0:
|
| 99 |
+
print(f'IRSE freeze the first {frozen_blocks} blocks, it has {len(model.body)} blocks ')
|
| 100 |
+
model.input_layer.eval()
|
| 101 |
+
print('in freeze', model.input_layer[1].training)
|
| 102 |
+
for param in model.input_layer.parameters():
|
| 103 |
+
param.requires_grad = False
|
| 104 |
+
|
| 105 |
+
for i in range(frozen_blocks):
|
| 106 |
+
m = model.body[i]
|
| 107 |
+
m.eval()
|
| 108 |
+
for param in m.parameters():
|
| 109 |
+
param.requires_grad = False
|
| 110 |
+
|
| 111 |
+
def after_train_epoch(self, runner):
|
| 112 |
+
if not self.by_epoch or not self.every_n_epochs(runner, self.interval):
|
| 113 |
+
return
|
| 114 |
+
from mmcls.apis import multi_gpu_test
|
| 115 |
+
results = multi_gpu_test(
|
| 116 |
+
runner.model,
|
| 117 |
+
self.dataloader,
|
| 118 |
+
tmpdir=osp.join(runner.work_dir, '.eval_hook'),
|
| 119 |
+
gpu_collect=self.gpu_collect)
|
| 120 |
+
if runner.rank == 0:
|
| 121 |
+
print('\n')
|
| 122 |
+
eval_res = self.evaluate(runner, results)
|
| 123 |
+
if self.print_best:
|
| 124 |
+
best_score = runner.meta['hook_msgs'].get('best_score', -inf)
|
| 125 |
+
if 'top-1' not in eval_res:
|
| 126 |
+
return
|
| 127 |
+
acc = eval_res['top-1']
|
| 128 |
+
if acc > best_score:
|
| 129 |
+
self.logger.info(f'top-1 accuracy improved from {best_score} to {acc}')
|
| 130 |
+
runner.meta['hook_msgs']['best_score'] = acc
|
| 131 |
+
runner.save_checkpoint(runner.work_dir, save_optimizer=False, filename_tmpl='best.pth', create_symlink=False)
|
| 132 |
+
else:
|
| 133 |
+
self.logger.info(f'top-1 accuracy did not improve from {best_score}')
|
| 134 |
+
|
| 135 |
+
def after_train_iter(self, runner):
|
| 136 |
+
if self.by_epoch or not self.every_n_iters(runner, self.interval):
|
| 137 |
+
return
|
| 138 |
+
from mmcls.apis import multi_gpu_test
|
| 139 |
+
runner.log_buffer.clear()
|
| 140 |
+
results = multi_gpu_test(
|
| 141 |
+
runner.model,
|
| 142 |
+
self.dataloader,
|
| 143 |
+
tmpdir=osp.join(runner.work_dir, '.eval_hook'),
|
| 144 |
+
gpu_collect=self.gpu_collect)
|
| 145 |
+
if runner.rank == 0:
|
| 146 |
+
print('\n')
|
| 147 |
+
eval_res = self.evaluate(runner, results)
|
| 148 |
+
if self.print_best:
|
| 149 |
+
best_score = runner.meta['hook_msgs'].get('best_score', -inf)
|
| 150 |
+
if 'top-1' not in eval_res:
|
| 151 |
+
return
|
| 152 |
+
acc = eval_res['top-1']
|
| 153 |
+
if acc > best_score:
|
| 154 |
+
self.logger.info(f'top-1 accuracy improved from {best_score} to {acc}')
|
| 155 |
+
runner.meta['hook_msgs']['best_score'] = acc
|
| 156 |
+
runner.save_checkpoint(runner.work_dir, save_optimizer=False, filename_tmpl='best.pth', create_symlink=False)
|
| 157 |
+
else:
|
| 158 |
+
self.logger.info(f'top-1 accuracy did not improve from {best_score}')
|
CAGE_expression_inference-apvit/apvit_mmcls/core/evaluation/mean_ap.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import torch
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def average_precision(pred, target):
|
| 6 |
+
""" Calculate the average precision for a single class
|
| 7 |
+
|
| 8 |
+
AP summarizes a precision-recall curve as the weighted mean of maximum
|
| 9 |
+
precisions obtained for any r'>r, where r is the recall:
|
| 10 |
+
|
| 11 |
+
..math::
|
| 12 |
+
\\text{AP} = \\sum_n (R_n - R_{n-1}) P_n
|
| 13 |
+
|
| 14 |
+
Note that no approximation is involved since the curve is piecewise
|
| 15 |
+
constant.
|
| 16 |
+
|
| 17 |
+
Args:
|
| 18 |
+
pred (np.ndarray): The model prediction with shape (N, ).
|
| 19 |
+
target (np.ndarray): The target of each prediction with shape (N, ).
|
| 20 |
+
|
| 21 |
+
Returns:
|
| 22 |
+
float: a single float as average precision value.
|
| 23 |
+
"""
|
| 24 |
+
eps = np.finfo(np.float32).eps
|
| 25 |
+
|
| 26 |
+
# sort examples
|
| 27 |
+
sort_inds = np.argsort(-pred)
|
| 28 |
+
sort_target = target[sort_inds]
|
| 29 |
+
|
| 30 |
+
# count true positive examples
|
| 31 |
+
pos_inds = sort_target == 1
|
| 32 |
+
tp = np.cumsum(pos_inds)
|
| 33 |
+
total_pos = tp[-1]
|
| 34 |
+
|
| 35 |
+
# count not difficult examples
|
| 36 |
+
pn_inds = sort_target != -1
|
| 37 |
+
pn = np.cumsum(pn_inds)
|
| 38 |
+
|
| 39 |
+
tp[np.logical_not(pos_inds)] = 0
|
| 40 |
+
precision = tp / np.maximum(pn, eps)
|
| 41 |
+
ap = np.sum(precision) / np.maximum(total_pos, eps)
|
| 42 |
+
return ap
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def mAP(pred, target):
|
| 46 |
+
""" Calculate the mean average precision with respect of classes
|
| 47 |
+
|
| 48 |
+
Args:
|
| 49 |
+
pred (torch.Tensor | np.ndarray): The model prediction with shape
|
| 50 |
+
(N, C), where C is the number of classes.
|
| 51 |
+
target (torch.Tensor | np.ndarray): The target of each prediction with
|
| 52 |
+
shape (N, C), where C is the number of classes. 1 stands for
|
| 53 |
+
positive examples, 0 stands for negative examples and -1 stands for
|
| 54 |
+
difficult examples.
|
| 55 |
+
|
| 56 |
+
Returns:
|
| 57 |
+
float: A single float as mAP value.
|
| 58 |
+
"""
|
| 59 |
+
if isinstance(pred, torch.Tensor) and isinstance(target, torch.Tensor):
|
| 60 |
+
pred = pred.numpy()
|
| 61 |
+
target = target.numpy()
|
| 62 |
+
elif not (isinstance(pred, np.ndarray) and isinstance(target, np.ndarray)):
|
| 63 |
+
raise TypeError('pred and target should both be torch.Tensor or'
|
| 64 |
+
'np.ndarray')
|
| 65 |
+
|
| 66 |
+
assert pred.shape == \
|
| 67 |
+
target.shape, 'pred and target should be in the same shape.'
|
| 68 |
+
num_classes = pred.shape[1]
|
| 69 |
+
ap = np.zeros(num_classes)
|
| 70 |
+
for k in range(num_classes):
|
| 71 |
+
ap[k] = average_precision(pred[:, k], target[:, k])
|
| 72 |
+
mean_ap = ap.mean() * 100.0
|
| 73 |
+
return mean_ap
|
CAGE_expression_inference-apvit/apvit_mmcls/core/evaluation/multilabel_eval_metrics.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import warnings
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def average_performance(pred, target, thr=None, k=None):
|
| 8 |
+
"""Calculate CP, CR, CF1, OP, OR, OF1, where C stands for per-class
|
| 9 |
+
average, O stands for overall average, P stands for precision, R
|
| 10 |
+
stands for recall and F1 stands for F1-score
|
| 11 |
+
|
| 12 |
+
Args:
|
| 13 |
+
pred (torch.Tensor | np.ndarray): The model prediction with shape
|
| 14 |
+
(N, C), where C is the number of classes.
|
| 15 |
+
target (torch.Tensor | np.ndarray): The target of each prediction with
|
| 16 |
+
shape (N, C), where C is the number of classes. 1 stands for
|
| 17 |
+
positive examples, 0 stands for negative examples and -1 stands for
|
| 18 |
+
difficult examples.
|
| 19 |
+
thr (float): The confidence threshold. Defaults to None.
|
| 20 |
+
k (int): Top-k performance. Note that if thr and k are both given, k
|
| 21 |
+
will be ignored. Defaults to None.
|
| 22 |
+
|
| 23 |
+
Returns:
|
| 24 |
+
tuple: (CP, CR, CF1, OP, OR, OF1)
|
| 25 |
+
"""
|
| 26 |
+
if isinstance(pred, torch.Tensor) and isinstance(target, torch.Tensor):
|
| 27 |
+
pred = pred.numpy()
|
| 28 |
+
target = target.numpy()
|
| 29 |
+
elif not (isinstance(pred, np.ndarray) and isinstance(target, np.ndarray)):
|
| 30 |
+
raise TypeError('pred and target should both be torch.Tensor or'
|
| 31 |
+
'np.ndarray')
|
| 32 |
+
if thr is None and k is None:
|
| 33 |
+
thr = 0.5
|
| 34 |
+
warnings.warn('Neither thr nor k is given, set thr as 0.5 by '
|
| 35 |
+
'default.')
|
| 36 |
+
elif thr is not None and k is not None:
|
| 37 |
+
warnings.warn('Both thr and k are given, use threshold in favor of '
|
| 38 |
+
'top-k.')
|
| 39 |
+
|
| 40 |
+
assert pred.shape == \
|
| 41 |
+
target.shape, 'pred and target should be in the same shape.'
|
| 42 |
+
|
| 43 |
+
eps = np.finfo(np.float32).eps
|
| 44 |
+
target[target == -1] = 0
|
| 45 |
+
if thr is not None:
|
| 46 |
+
# a label is predicted positive if the confidence is no lower than thr
|
| 47 |
+
pos_inds = pred >= thr
|
| 48 |
+
|
| 49 |
+
else:
|
| 50 |
+
# top-k labels will be predicted positive for any example
|
| 51 |
+
sort_inds = np.argsort(-pred, axis=1)
|
| 52 |
+
sort_inds_ = sort_inds[:, :k]
|
| 53 |
+
inds = np.indices(sort_inds_.shape)
|
| 54 |
+
pos_inds = np.zeros_like(pred)
|
| 55 |
+
pos_inds[inds[0], sort_inds_] = 1
|
| 56 |
+
|
| 57 |
+
tp = (pos_inds * target) == 1
|
| 58 |
+
fp = (pos_inds * (1 - target)) == 1
|
| 59 |
+
fn = ((1 - pos_inds) * target) == 1
|
| 60 |
+
|
| 61 |
+
precision_class = tp.sum(axis=0) / np.maximum(
|
| 62 |
+
tp.sum(axis=0) + fp.sum(axis=0), eps)
|
| 63 |
+
recall_class = tp.sum(axis=0) / np.maximum(
|
| 64 |
+
tp.sum(axis=0) + fn.sum(axis=0), eps)
|
| 65 |
+
CP = precision_class.mean() * 100.0
|
| 66 |
+
CR = recall_class.mean() * 100.0
|
| 67 |
+
CF1 = 2 * CP * CR / np.maximum(CP + CR, eps)
|
| 68 |
+
OP = tp.sum() / np.maximum(tp.sum() + fp.sum(), eps) * 100.0
|
| 69 |
+
OR = tp.sum() / np.maximum(tp.sum() + fn.sum(), eps) * 100.0
|
| 70 |
+
OF1 = 2 * OP * OR / np.maximum(OP + OR, eps)
|
| 71 |
+
return CP, CR, CF1, OP, OR, OF1
|
CAGE_expression_inference-apvit/apvit_mmcls/core/fp16/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .decorators import auto_fp16, force_fp32
|
| 2 |
+
from .hooks import Fp16OptimizerHook, wrap_fp16_model
|
| 3 |
+
|
| 4 |
+
__all__ = ['auto_fp16', 'force_fp32', 'Fp16OptimizerHook', 'wrap_fp16_model']
|
CAGE_expression_inference-apvit/apvit_mmcls/core/fp16/decorators.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import functools
|
| 2 |
+
from inspect import getfullargspec
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
+
from .utils import cast_tensor_type
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def auto_fp16(apply_to=None, out_fp32=False):
|
| 10 |
+
"""Decorator to enable fp16 training automatically.
|
| 11 |
+
|
| 12 |
+
This decorator is useful when you write custom modules and want to support
|
| 13 |
+
mixed precision training. If inputs arguments are fp32 tensors, they will
|
| 14 |
+
be converted to fp16 automatically. Arguments other than fp32 tensors are
|
| 15 |
+
ignored.
|
| 16 |
+
|
| 17 |
+
Args:
|
| 18 |
+
apply_to (Iterable, optional): The argument names to be converted.
|
| 19 |
+
`None` indicates all arguments.
|
| 20 |
+
out_fp32 (bool): Whether to convert the output back to fp32.
|
| 21 |
+
|
| 22 |
+
:Example:
|
| 23 |
+
|
| 24 |
+
class MyModule1(nn.Module)
|
| 25 |
+
|
| 26 |
+
# Convert x and y to fp16
|
| 27 |
+
@auto_fp16()
|
| 28 |
+
def forward(self, x, y):
|
| 29 |
+
pass
|
| 30 |
+
|
| 31 |
+
class MyModule2(nn.Module):
|
| 32 |
+
|
| 33 |
+
# convert pred to fp16
|
| 34 |
+
@auto_fp16(apply_to=('pred', ))
|
| 35 |
+
def do_something(self, pred, others):
|
| 36 |
+
pass
|
| 37 |
+
"""
|
| 38 |
+
|
| 39 |
+
def auto_fp16_wrapper(old_func):
|
| 40 |
+
|
| 41 |
+
@functools.wraps(old_func)
|
| 42 |
+
def new_func(*args, **kwargs):
|
| 43 |
+
# check if the module has set the attribute `fp16_enabled`, if not,
|
| 44 |
+
# just fallback to the original method.
|
| 45 |
+
if not isinstance(args[0], torch.nn.Module):
|
| 46 |
+
raise TypeError('@auto_fp16 can only be used to decorate the '
|
| 47 |
+
'method of nn.Module')
|
| 48 |
+
if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled):
|
| 49 |
+
return old_func(*args, **kwargs)
|
| 50 |
+
# get the arg spec of the decorated method
|
| 51 |
+
args_info = getfullargspec(old_func)
|
| 52 |
+
# get the argument names to be casted
|
| 53 |
+
args_to_cast = args_info.args if apply_to is None else apply_to
|
| 54 |
+
# convert the args that need to be processed
|
| 55 |
+
new_args = []
|
| 56 |
+
# NOTE: default args are not taken into consideration
|
| 57 |
+
if args:
|
| 58 |
+
arg_names = args_info.args[:len(args)]
|
| 59 |
+
for i, arg_name in enumerate(arg_names):
|
| 60 |
+
if arg_name in args_to_cast:
|
| 61 |
+
new_args.append(
|
| 62 |
+
cast_tensor_type(args[i], torch.float, torch.half))
|
| 63 |
+
else:
|
| 64 |
+
new_args.append(args[i])
|
| 65 |
+
# convert the kwargs that need to be processed
|
| 66 |
+
new_kwargs = {}
|
| 67 |
+
if kwargs:
|
| 68 |
+
for arg_name, arg_value in kwargs.items():
|
| 69 |
+
if arg_name in args_to_cast:
|
| 70 |
+
new_kwargs[arg_name] = cast_tensor_type(
|
| 71 |
+
arg_value, torch.float, torch.half)
|
| 72 |
+
else:
|
| 73 |
+
new_kwargs[arg_name] = arg_value
|
| 74 |
+
# apply converted arguments to the decorated method
|
| 75 |
+
output = old_func(*new_args, **new_kwargs)
|
| 76 |
+
# cast the results back to fp32 if necessary
|
| 77 |
+
if out_fp32:
|
| 78 |
+
output = cast_tensor_type(output, torch.half, torch.float)
|
| 79 |
+
return output
|
| 80 |
+
|
| 81 |
+
return new_func
|
| 82 |
+
|
| 83 |
+
return auto_fp16_wrapper
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def force_fp32(apply_to=None, out_fp16=False):
|
| 87 |
+
"""Decorator to convert input arguments to fp32 in force.
|
| 88 |
+
|
| 89 |
+
This decorator is useful when you write custom modules and want to support
|
| 90 |
+
mixed precision training. If there are some inputs that must be processed
|
| 91 |
+
in fp32 mode, then this decorator can handle it. If inputs arguments are
|
| 92 |
+
fp16 tensors, they will be converted to fp32 automatically. Arguments other
|
| 93 |
+
than fp16 tensors are ignored.
|
| 94 |
+
|
| 95 |
+
Args:
|
| 96 |
+
apply_to (Iterable, optional): The argument names to be converted.
|
| 97 |
+
`None` indicates all arguments.
|
| 98 |
+
out_fp16 (bool): Whether to convert the output back to fp16.
|
| 99 |
+
|
| 100 |
+
:Example:
|
| 101 |
+
|
| 102 |
+
class MyModule1(nn.Module)
|
| 103 |
+
|
| 104 |
+
# Convert x and y to fp32
|
| 105 |
+
@force_fp32()
|
| 106 |
+
def loss(self, x, y):
|
| 107 |
+
pass
|
| 108 |
+
|
| 109 |
+
class MyModule2(nn.Module):
|
| 110 |
+
|
| 111 |
+
# convert pred to fp32
|
| 112 |
+
@force_fp32(apply_to=('pred', ))
|
| 113 |
+
def post_process(self, pred, others):
|
| 114 |
+
pass
|
| 115 |
+
"""
|
| 116 |
+
|
| 117 |
+
def force_fp32_wrapper(old_func):
|
| 118 |
+
|
| 119 |
+
@functools.wraps(old_func)
|
| 120 |
+
def new_func(*args, **kwargs):
|
| 121 |
+
# check if the module has set the attribute `fp16_enabled`, if not,
|
| 122 |
+
# just fallback to the original method.
|
| 123 |
+
if not isinstance(args[0], torch.nn.Module):
|
| 124 |
+
raise TypeError('@force_fp32 can only be used to decorate the '
|
| 125 |
+
'method of nn.Module')
|
| 126 |
+
if not (hasattr(args[0], 'fp16_enabled') and args[0].fp16_enabled):
|
| 127 |
+
return old_func(*args, **kwargs)
|
| 128 |
+
# get the arg spec of the decorated method
|
| 129 |
+
args_info = getfullargspec(old_func)
|
| 130 |
+
# get the argument names to be casted
|
| 131 |
+
args_to_cast = args_info.args if apply_to is None else apply_to
|
| 132 |
+
# convert the args that need to be processed
|
| 133 |
+
new_args = []
|
| 134 |
+
if args:
|
| 135 |
+
arg_names = args_info.args[:len(args)]
|
| 136 |
+
for i, arg_name in enumerate(arg_names):
|
| 137 |
+
if arg_name in args_to_cast:
|
| 138 |
+
new_args.append(
|
| 139 |
+
cast_tensor_type(args[i], torch.half, torch.float))
|
| 140 |
+
else:
|
| 141 |
+
new_args.append(args[i])
|
| 142 |
+
# convert the kwargs that need to be processed
|
| 143 |
+
new_kwargs = dict()
|
| 144 |
+
if kwargs:
|
| 145 |
+
for arg_name, arg_value in kwargs.items():
|
| 146 |
+
if arg_name in args_to_cast:
|
| 147 |
+
new_kwargs[arg_name] = cast_tensor_type(
|
| 148 |
+
arg_value, torch.half, torch.float)
|
| 149 |
+
else:
|
| 150 |
+
new_kwargs[arg_name] = arg_value
|
| 151 |
+
# apply converted arguments to the decorated method
|
| 152 |
+
output = old_func(*new_args, **new_kwargs)
|
| 153 |
+
# cast the results back to fp32 if necessary
|
| 154 |
+
if out_fp16:
|
| 155 |
+
output = cast_tensor_type(output, torch.float, torch.half)
|
| 156 |
+
return output
|
| 157 |
+
|
| 158 |
+
return new_func
|
| 159 |
+
|
| 160 |
+
return force_fp32_wrapper
|
CAGE_expression_inference-apvit/apvit_mmcls/core/fp16/hooks.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import copy
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import torch.nn as nn
|
| 5 |
+
from mmcv.runner import OptimizerHook
|
| 6 |
+
from mmcv.utils.parrots_wrapper import _BatchNorm
|
| 7 |
+
|
| 8 |
+
from ..utils import allreduce_grads
|
| 9 |
+
from .utils import cast_tensor_type
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class Fp16OptimizerHook(OptimizerHook):
|
| 13 |
+
"""FP16 optimizer hook.
|
| 14 |
+
|
| 15 |
+
The steps of fp16 optimizer is as follows.
|
| 16 |
+
1. Scale the loss value.
|
| 17 |
+
2. BP in the fp16 model.
|
| 18 |
+
2. Copy gradients from fp16 model to fp32 weights.
|
| 19 |
+
3. Update fp32 weights.
|
| 20 |
+
4. Copy updated parameters from fp32 weights to fp16 model.
|
| 21 |
+
|
| 22 |
+
Refer to https://arxiv.org/abs/1710.03740 for more details.
|
| 23 |
+
|
| 24 |
+
Args:
|
| 25 |
+
loss_scale (float): Scale factor multiplied with loss.
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
def __init__(self,
|
| 29 |
+
grad_clip=None,
|
| 30 |
+
coalesce=True,
|
| 31 |
+
bucket_size_mb=-1,
|
| 32 |
+
loss_scale=512.,
|
| 33 |
+
distributed=True):
|
| 34 |
+
self.grad_clip = grad_clip
|
| 35 |
+
self.coalesce = coalesce
|
| 36 |
+
self.bucket_size_mb = bucket_size_mb
|
| 37 |
+
self.loss_scale = loss_scale
|
| 38 |
+
self.distributed = distributed
|
| 39 |
+
|
| 40 |
+
def before_run(self, runner):
|
| 41 |
+
# keep a copy of fp32 weights
|
| 42 |
+
runner.optimizer.param_groups = copy.deepcopy(
|
| 43 |
+
runner.optimizer.param_groups)
|
| 44 |
+
# convert model to fp16
|
| 45 |
+
wrap_fp16_model(runner.model)
|
| 46 |
+
|
| 47 |
+
def copy_grads_to_fp32(self, fp16_net, fp32_weights):
|
| 48 |
+
"""Copy gradients from fp16 model to fp32 weight copy."""
|
| 49 |
+
for fp32_param, fp16_param in zip(fp32_weights, fp16_net.parameters()):
|
| 50 |
+
if fp16_param.grad is not None:
|
| 51 |
+
if fp32_param.grad is None:
|
| 52 |
+
fp32_param.grad = fp32_param.data.new(fp32_param.size())
|
| 53 |
+
fp32_param.grad.copy_(fp16_param.grad)
|
| 54 |
+
|
| 55 |
+
def copy_params_to_fp16(self, fp16_net, fp32_weights):
|
| 56 |
+
"""Copy updated params from fp32 weight copy to fp16 model."""
|
| 57 |
+
for fp16_param, fp32_param in zip(fp16_net.parameters(), fp32_weights):
|
| 58 |
+
fp16_param.data.copy_(fp32_param.data)
|
| 59 |
+
|
| 60 |
+
def after_train_iter(self, runner):
|
| 61 |
+
# clear grads of last iteration
|
| 62 |
+
runner.model.zero_grad()
|
| 63 |
+
runner.optimizer.zero_grad()
|
| 64 |
+
# scale the loss value
|
| 65 |
+
scaled_loss = runner.outputs['loss'] * self.loss_scale
|
| 66 |
+
scaled_loss.backward()
|
| 67 |
+
# copy fp16 grads in the model to fp32 params in the optimizer
|
| 68 |
+
fp32_weights = []
|
| 69 |
+
for param_group in runner.optimizer.param_groups:
|
| 70 |
+
fp32_weights += param_group['params']
|
| 71 |
+
self.copy_grads_to_fp32(runner.model, fp32_weights)
|
| 72 |
+
# allreduce grads
|
| 73 |
+
if self.distributed:
|
| 74 |
+
allreduce_grads(fp32_weights, self.coalesce, self.bucket_size_mb)
|
| 75 |
+
# scale the gradients back
|
| 76 |
+
for param in fp32_weights:
|
| 77 |
+
if param.grad is not None:
|
| 78 |
+
param.grad.div_(self.loss_scale)
|
| 79 |
+
if self.grad_clip is not None:
|
| 80 |
+
grad_norm = self.clip_grads(fp32_weights)
|
| 81 |
+
if grad_norm is not None:
|
| 82 |
+
# Add grad norm to the logger
|
| 83 |
+
runner.log_buffer.update({'grad_norm': float(grad_norm)},
|
| 84 |
+
runner.outputs['num_samples'])
|
| 85 |
+
# update fp32 params
|
| 86 |
+
runner.optimizer.step()
|
| 87 |
+
# copy fp32 params to the fp16 model
|
| 88 |
+
self.copy_params_to_fp16(runner.model, fp32_weights)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def wrap_fp16_model(model):
|
| 92 |
+
# convert model to fp16
|
| 93 |
+
model.half()
|
| 94 |
+
# patch the normalization layers to make it work in fp32 mode
|
| 95 |
+
patch_norm_fp32(model)
|
| 96 |
+
# set `fp16_enabled` flag
|
| 97 |
+
for m in model.modules():
|
| 98 |
+
if hasattr(m, 'fp16_enabled'):
|
| 99 |
+
m.fp16_enabled = True
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def patch_norm_fp32(module):
|
| 103 |
+
if isinstance(module, (_BatchNorm, nn.GroupNorm)):
|
| 104 |
+
module.float()
|
| 105 |
+
module.forward = patch_forward_method(module.forward, torch.half,
|
| 106 |
+
torch.float)
|
| 107 |
+
for child in module.children():
|
| 108 |
+
patch_norm_fp32(child)
|
| 109 |
+
return module
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def patch_forward_method(func, src_type, dst_type, convert_output=True):
|
| 113 |
+
"""Patch the forward method of a module.
|
| 114 |
+
|
| 115 |
+
Args:
|
| 116 |
+
func (callable): The original forward method.
|
| 117 |
+
src_type (torch.dtype): Type of input arguments to be converted from.
|
| 118 |
+
dst_type (torch.dtype): Type of input arguments to be converted to.
|
| 119 |
+
convert_output (bool): Whether to convert the output back to src_type.
|
| 120 |
+
|
| 121 |
+
Returns:
|
| 122 |
+
callable: The patched forward method.
|
| 123 |
+
"""
|
| 124 |
+
|
| 125 |
+
def new_forward(*args, **kwargs):
|
| 126 |
+
output = func(*cast_tensor_type(args, src_type, dst_type),
|
| 127 |
+
**cast_tensor_type(kwargs, src_type, dst_type))
|
| 128 |
+
if convert_output:
|
| 129 |
+
output = cast_tensor_type(output, dst_type, src_type)
|
| 130 |
+
return output
|
| 131 |
+
|
| 132 |
+
return new_forward
|
CAGE_expression_inference-apvit/apvit_mmcls/core/fp16/utils.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections import abc
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def cast_tensor_type(inputs, src_type, dst_type):
|
| 8 |
+
if isinstance(inputs, torch.Tensor):
|
| 9 |
+
return inputs.to(dst_type)
|
| 10 |
+
elif isinstance(inputs, str):
|
| 11 |
+
return inputs
|
| 12 |
+
elif isinstance(inputs, np.ndarray):
|
| 13 |
+
return inputs
|
| 14 |
+
elif isinstance(inputs, abc.Mapping):
|
| 15 |
+
return type(inputs)({
|
| 16 |
+
k: cast_tensor_type(v, src_type, dst_type)
|
| 17 |
+
for k, v in inputs.items()
|
| 18 |
+
})
|
| 19 |
+
elif isinstance(inputs, abc.Iterable):
|
| 20 |
+
return type(inputs)(
|
| 21 |
+
cast_tensor_type(item, src_type, dst_type) for item in inputs)
|
| 22 |
+
else:
|
| 23 |
+
return inputs
|
CAGE_expression_inference-apvit/apvit_mmcls/core/utils/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .dist_utils import DistOptimizerHook, allreduce_grads
|
| 2 |
+
from .misc import multi_apply
|
| 3 |
+
|
| 4 |
+
__all__ = ['allreduce_grads', 'DistOptimizerHook', 'multi_apply']
|
CAGE_expression_inference-apvit/apvit_mmcls/core/utils/dist_utils.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections import OrderedDict
|
| 2 |
+
|
| 3 |
+
import torch.distributed as dist
|
| 4 |
+
from mmcv.runner import OptimizerHook
|
| 5 |
+
from torch._utils import (_flatten_dense_tensors, _take_tensors,
|
| 6 |
+
_unflatten_dense_tensors)
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def _allreduce_coalesced(tensors, world_size, bucket_size_mb=-1):
|
| 10 |
+
if bucket_size_mb > 0:
|
| 11 |
+
bucket_size_bytes = bucket_size_mb * 1024 * 1024
|
| 12 |
+
buckets = _take_tensors(tensors, bucket_size_bytes)
|
| 13 |
+
else:
|
| 14 |
+
buckets = OrderedDict()
|
| 15 |
+
for tensor in tensors:
|
| 16 |
+
tp = tensor.type()
|
| 17 |
+
if tp not in buckets:
|
| 18 |
+
buckets[tp] = []
|
| 19 |
+
buckets[tp].append(tensor)
|
| 20 |
+
buckets = buckets.values()
|
| 21 |
+
|
| 22 |
+
for bucket in buckets:
|
| 23 |
+
flat_tensors = _flatten_dense_tensors(bucket)
|
| 24 |
+
dist.all_reduce(flat_tensors)
|
| 25 |
+
flat_tensors.div_(world_size)
|
| 26 |
+
for tensor, synced in zip(
|
| 27 |
+
bucket, _unflatten_dense_tensors(flat_tensors, bucket)):
|
| 28 |
+
tensor.copy_(synced)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def allreduce_grads(params, coalesce=True, bucket_size_mb=-1):
|
| 32 |
+
grads = [
|
| 33 |
+
param.grad.data for param in params
|
| 34 |
+
if param.requires_grad and param.grad is not None
|
| 35 |
+
]
|
| 36 |
+
world_size = dist.get_world_size()
|
| 37 |
+
if coalesce:
|
| 38 |
+
_allreduce_coalesced(grads, world_size, bucket_size_mb)
|
| 39 |
+
else:
|
| 40 |
+
for tensor in grads:
|
| 41 |
+
dist.all_reduce(tensor.div_(world_size))
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
class DistOptimizerHook(OptimizerHook):
|
| 45 |
+
|
| 46 |
+
def __init__(self, grad_clip=None, coalesce=True, bucket_size_mb=-1):
|
| 47 |
+
self.grad_clip = grad_clip
|
| 48 |
+
self.coalesce = coalesce
|
| 49 |
+
self.bucket_size_mb = bucket_size_mb
|
| 50 |
+
|
| 51 |
+
def after_train_iter(self, runner):
|
| 52 |
+
runner.optimizer.zero_grad()
|
| 53 |
+
runner.outputs['loss'].backward()
|
| 54 |
+
if self.grad_clip is not None:
|
| 55 |
+
self.clip_grads(runner.model.parameters())
|
| 56 |
+
runner.optimizer.step()
|
CAGE_expression_inference-apvit/apvit_mmcls/core/utils/misc.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from functools import partial
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def multi_apply(func, *args, **kwargs):
|
| 5 |
+
pfunc = partial(func, **kwargs) if kwargs else func
|
| 6 |
+
map_results = map(pfunc, *args)
|
| 7 |
+
return tuple(map(list, zip(*map_results)))
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/__init__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .base_dataset import BaseDataset
|
| 2 |
+
from .builder import DATASETS, PIPELINES, build_dataloader, build_dataset
|
| 3 |
+
from .cifar import CIFAR10, CIFAR100
|
| 4 |
+
from .dataset_wrappers import (ClassBalancedDataset, ConcatDataset,
|
| 5 |
+
RepeatDataset)
|
| 6 |
+
from .imagenet import ImageNet
|
| 7 |
+
from .mnist import MNIST, FashionMNIST
|
| 8 |
+
from .samplers import DistributedSampler
|
| 9 |
+
from .raf import RAF
|
| 10 |
+
|
| 11 |
+
__all__ = [
|
| 12 |
+
'BaseDataset', 'ImageNet', 'CIFAR10', 'CIFAR100', 'MNIST', 'FashionMNIST',
|
| 13 |
+
'build_dataloader', 'build_dataset', 'Compose', 'DistributedSampler',
|
| 14 |
+
'ConcatDataset', 'RepeatDataset', 'ClassBalancedDataset', 'DATASETS',
|
| 15 |
+
'PIPELINES', 'RAF',
|
| 16 |
+
]
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/base_dataset.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import copy
|
| 2 |
+
from abc import ABCMeta, abstractmethod
|
| 3 |
+
import os
|
| 4 |
+
from shutil import copyfile
|
| 5 |
+
|
| 6 |
+
import mmcv
|
| 7 |
+
import numpy as np
|
| 8 |
+
from torch.utils.data import Dataset
|
| 9 |
+
|
| 10 |
+
from mmcls.models.losses import accuracy, f1_score, precision, recall
|
| 11 |
+
from mmcls.models.losses.eval_metrics import class_accuracy
|
| 12 |
+
from .pipelines import Compose
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class BaseDataset(Dataset, metaclass=ABCMeta):
|
| 16 |
+
"""Base dataset.
|
| 17 |
+
|
| 18 |
+
Args:
|
| 19 |
+
data_prefix (str): the prefix of data path
|
| 20 |
+
pipeline (list): a list of dict, where each element represents
|
| 21 |
+
a operation defined in `mmcls.datasets.pipelines`
|
| 22 |
+
ann_file (str | None): the annotation file. When ann_file is str,
|
| 23 |
+
the subclass is expected to read from the ann_file. When ann_file
|
| 24 |
+
is None, the subclass is expected to read according to data_prefix
|
| 25 |
+
test_mode (bool): in train mode or test mode
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
CLASSES = None
|
| 29 |
+
|
| 30 |
+
def __init__(self,
|
| 31 |
+
data_prefix,
|
| 32 |
+
pipeline,
|
| 33 |
+
classes=None,
|
| 34 |
+
ann_file=None,
|
| 35 |
+
test_mode=False):
|
| 36 |
+
super(BaseDataset, self).__init__()
|
| 37 |
+
|
| 38 |
+
self.ann_file = ann_file
|
| 39 |
+
self.data_prefix = data_prefix
|
| 40 |
+
self.test_mode = test_mode
|
| 41 |
+
self.pipeline = Compose(pipeline)
|
| 42 |
+
self.data_infos = self.load_annotations()
|
| 43 |
+
# self.data_infos = self.load_exp_and_au_annotations()
|
| 44 |
+
self.CLASSES = self.get_classes(classes)
|
| 45 |
+
if os.environ.get('DEBUG_MODE', '0') == '1':
|
| 46 |
+
self.data_infos = self.data_infos[:30]
|
| 47 |
+
|
| 48 |
+
@abstractmethod
|
| 49 |
+
def load_annotations(self):
|
| 50 |
+
pass
|
| 51 |
+
|
| 52 |
+
@property
|
| 53 |
+
def class_to_idx(self):
|
| 54 |
+
"""Map mapping class name to class index.
|
| 55 |
+
|
| 56 |
+
Returns:
|
| 57 |
+
dict: mapping from class name to class index.
|
| 58 |
+
"""
|
| 59 |
+
|
| 60 |
+
return {_class: i for i, _class in enumerate(self.CLASSES)}
|
| 61 |
+
|
| 62 |
+
def get_gt_labels(self):
|
| 63 |
+
"""Get all ground-truth labels (categories).
|
| 64 |
+
|
| 65 |
+
Returns:
|
| 66 |
+
list[int]: categories for all images.
|
| 67 |
+
"""
|
| 68 |
+
|
| 69 |
+
gt_labels = np.array([data['gt_label'] for data in self.data_infos])
|
| 70 |
+
return gt_labels
|
| 71 |
+
|
| 72 |
+
def get_coarse_labels(self):
|
| 73 |
+
coarse_labels = np.array([data['coarse_label'] for data in self.data_infos])
|
| 74 |
+
return coarse_labels
|
| 75 |
+
|
| 76 |
+
def get_cat_ids(self, idx):
|
| 77 |
+
"""Get category id by index.
|
| 78 |
+
|
| 79 |
+
Args:
|
| 80 |
+
idx (int): Index of data.
|
| 81 |
+
|
| 82 |
+
Returns:
|
| 83 |
+
int: Image category of specified index.
|
| 84 |
+
"""
|
| 85 |
+
|
| 86 |
+
return (int(self.data_infos[idx]['gt_label'].astype(np.int)), )
|
| 87 |
+
|
| 88 |
+
def prepare_data(self, idx):
|
| 89 |
+
results = copy.deepcopy(self.data_infos[idx])
|
| 90 |
+
return self.pipeline(results)
|
| 91 |
+
|
| 92 |
+
def __len__(self):
|
| 93 |
+
return len(self.data_infos)
|
| 94 |
+
|
| 95 |
+
def __getitem__(self, idx):
|
| 96 |
+
return self.prepare_data(idx)
|
| 97 |
+
|
| 98 |
+
@classmethod
|
| 99 |
+
def get_classes(cls, classes=None):
|
| 100 |
+
"""Get class names of current dataset.
|
| 101 |
+
Args:
|
| 102 |
+
classes (Sequence[str] | str | None): If classes is None, use
|
| 103 |
+
default CLASSES defined by builtin dataset. If classes is a
|
| 104 |
+
string, take it as a file name. The file contains the name of
|
| 105 |
+
classes where each line contains one class name. If classes is
|
| 106 |
+
a tuple or list, override the CLASSES defined by the dataset.
|
| 107 |
+
|
| 108 |
+
Returns:
|
| 109 |
+
tuple[str] or list[str]: Names of categories of the dataset.
|
| 110 |
+
"""
|
| 111 |
+
if classes is None:
|
| 112 |
+
return cls.CLASSES
|
| 113 |
+
|
| 114 |
+
if isinstance(classes, str):
|
| 115 |
+
# take it as a file path
|
| 116 |
+
class_names = mmcv.list_from_file(classes)
|
| 117 |
+
elif isinstance(classes, (tuple, list)):
|
| 118 |
+
class_names = classes
|
| 119 |
+
else:
|
| 120 |
+
raise ValueError(f'Unsupported type {type(classes)} of classes.')
|
| 121 |
+
|
| 122 |
+
return class_names
|
| 123 |
+
|
| 124 |
+
def evaluate(self,
|
| 125 |
+
results,
|
| 126 |
+
metric='accuracy',
|
| 127 |
+
metric_options={'topk': (1, 2)},
|
| 128 |
+
logger=None):
|
| 129 |
+
"""Evaluate the dataset.
|
| 130 |
+
|
| 131 |
+
Args:
|
| 132 |
+
results (list): Testing results of the dataset.
|
| 133 |
+
metric (str | list[str]): Metrics to be evaluated.
|
| 134 |
+
Default value is `accuracy`.
|
| 135 |
+
logger (logging.Logger | None | str): Logger used for printing
|
| 136 |
+
related information during evaluation. Default: None.
|
| 137 |
+
Returns:
|
| 138 |
+
dict: evaluation results
|
| 139 |
+
"""
|
| 140 |
+
if isinstance(metric, str):
|
| 141 |
+
metrics = [metric]
|
| 142 |
+
else:
|
| 143 |
+
metrics = metric
|
| 144 |
+
allowed_metrics = ['accuracy', 'precision', 'recall', 'f1_score', 'class_accuracy']
|
| 145 |
+
eval_results = {}
|
| 146 |
+
for metric in metrics:
|
| 147 |
+
if metric not in allowed_metrics:
|
| 148 |
+
raise KeyError(f'metric {metric} is not supported.')
|
| 149 |
+
results = np.vstack(results)
|
| 150 |
+
gt_labels = self.get_gt_labels()
|
| 151 |
+
num_imgs = len(results)
|
| 152 |
+
assert len(gt_labels) == num_imgs, f'{len(gt_labels)}, {num_imgs}'
|
| 153 |
+
# convert gt_labels to another dataset format for cross-dataset test
|
| 154 |
+
# raf2affect_dict = [6, 3, 4, 0, 1, 2, 5] # RAF -> AffectNet实验需要将RAF标签修改为AffectNet的标签
|
| 155 |
+
# affect2raf_dict = [3, 4, 5, 1, 2, 6, 0]
|
| 156 |
+
# raf2ferplus_dict = [6,3,0,4,5,2,1,None]
|
| 157 |
+
# gt_labels = [raf2ferplus_dict[i] for i in gt_labels]
|
| 158 |
+
# # specific process for RAF -> FERPlus
|
| 159 |
+
# results = [results[i] for i in range(len(results)) if gt_labels[i] is not None]
|
| 160 |
+
# gt_labels = [gt_labels[i] for i in range(len(gt_labels)) if gt_labels[i] is not None]
|
| 161 |
+
# results = np.array(results)
|
| 162 |
+
# gt_labels = np.array(gt_labels)
|
| 163 |
+
|
| 164 |
+
if metric == 'accuracy':
|
| 165 |
+
topk = metric_options.get('topk')
|
| 166 |
+
acc = accuracy(results, gt_labels, topk)
|
| 167 |
+
eval_result = {f'top-{k}': a.item() for k, a in zip(topk, acc)}
|
| 168 |
+
elif metric == 'precision':
|
| 169 |
+
precision_value = precision(results, gt_labels)
|
| 170 |
+
eval_result = {'precision': precision_value}
|
| 171 |
+
elif metric == 'recall':
|
| 172 |
+
recall_value = recall(results, gt_labels)
|
| 173 |
+
eval_result = {'recall': recall_value}
|
| 174 |
+
elif metric == 'f1_score':
|
| 175 |
+
f1_score_value = f1_score(results, gt_labels)
|
| 176 |
+
eval_result = {'f1_score': f1_score_value}
|
| 177 |
+
elif metric == 'class_accuracy':
|
| 178 |
+
class_accuracy_value = class_accuracy(results, gt_labels, self.CLASSES)
|
| 179 |
+
print('\n')
|
| 180 |
+
for name, val in zip(self.CLASSES, class_accuracy_value):
|
| 181 |
+
print(f'{name}: \t{val}')
|
| 182 |
+
print('\n')
|
| 183 |
+
eval_result = dict()
|
| 184 |
+
eval_results.update(eval_result)
|
| 185 |
+
return eval_results
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/builder.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import platform
|
| 2 |
+
import random
|
| 3 |
+
from functools import partial
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
from mmcv.parallel import collate
|
| 7 |
+
from mmcv.runner import get_dist_info
|
| 8 |
+
from mmcv.utils import Registry, build_from_cfg
|
| 9 |
+
from torch.utils.data import DataLoader
|
| 10 |
+
|
| 11 |
+
from .samplers import DistributedSampler
|
| 12 |
+
|
| 13 |
+
if platform.system() != 'Windows':
|
| 14 |
+
# https://github.com/pytorch/pytorch/issues/973
|
| 15 |
+
import resource
|
| 16 |
+
rlimit = resource.getrlimit(resource.RLIMIT_NOFILE)
|
| 17 |
+
hard_limit = rlimit[1]
|
| 18 |
+
soft_limit = min(4096, hard_limit)
|
| 19 |
+
resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit))
|
| 20 |
+
|
| 21 |
+
DATASETS = Registry('dataset')
|
| 22 |
+
PIPELINES = Registry('pipeline')
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def build_dataset(cfg, default_args=None):
|
| 26 |
+
from .dataset_wrappers import (ConcatDataset, RepeatDataset,
|
| 27 |
+
ClassBalancedDataset)
|
| 28 |
+
cfg = cfg.copy()
|
| 29 |
+
if isinstance(cfg, (list, tuple)):
|
| 30 |
+
dataset = ConcatDataset([build_dataset(c, default_args) for c in cfg])
|
| 31 |
+
elif cfg['type'] == 'RepeatDataset':
|
| 32 |
+
dataset = RepeatDataset(
|
| 33 |
+
build_dataset(cfg['dataset'], default_args), cfg['times'])
|
| 34 |
+
elif cfg['type'] == 'ClassBalancedDataset':
|
| 35 |
+
ds = build_dataset(cfg['dataset'], default_args)
|
| 36 |
+
cfg['dataset'] = ds
|
| 37 |
+
dataset = ClassBalancedDataset(**cfg)
|
| 38 |
+
else:
|
| 39 |
+
dataset = build_from_cfg(cfg, DATASETS, default_args)
|
| 40 |
+
|
| 41 |
+
return dataset
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def build_dataloader(dataset,
|
| 45 |
+
samples_per_gpu,
|
| 46 |
+
workers_per_gpu,
|
| 47 |
+
num_gpus=1,
|
| 48 |
+
dist=True,
|
| 49 |
+
shuffle=True,
|
| 50 |
+
round_up=True,
|
| 51 |
+
seed=None,
|
| 52 |
+
**kwargs):
|
| 53 |
+
"""Build PyTorch DataLoader.
|
| 54 |
+
|
| 55 |
+
In distributed training, each GPU/process has a dataloader.
|
| 56 |
+
In non-distributed training, there is only one dataloader for all GPUs.
|
| 57 |
+
|
| 58 |
+
Args:
|
| 59 |
+
dataset (Dataset): A PyTorch dataset.
|
| 60 |
+
samples_per_gpu (int): Number of training samples on each GPU, i.e.,
|
| 61 |
+
batch size of each GPU.
|
| 62 |
+
workers_per_gpu (int): How many subprocesses to use for data loading
|
| 63 |
+
for each GPU.
|
| 64 |
+
num_gpus (int): Number of GPUs. Only used in non-distributed training.
|
| 65 |
+
dist (bool): Distributed training/test or not. Default: True.
|
| 66 |
+
shuffle (bool): Whether to shuffle the data at every epoch.
|
| 67 |
+
Default: True.
|
| 68 |
+
round_up (bool): Whether to round up the length of dataset by adding
|
| 69 |
+
extra samples to make it evenly divisible. Default: True.
|
| 70 |
+
kwargs: any keyword argument to be used to initialize DataLoader
|
| 71 |
+
|
| 72 |
+
Returns:
|
| 73 |
+
DataLoader: A PyTorch dataloader.
|
| 74 |
+
"""
|
| 75 |
+
rank, world_size = get_dist_info()
|
| 76 |
+
if dist:
|
| 77 |
+
sampler = DistributedSampler(
|
| 78 |
+
dataset, world_size, rank, shuffle=shuffle, round_up=round_up)
|
| 79 |
+
shuffle = False
|
| 80 |
+
batch_size = samples_per_gpu
|
| 81 |
+
num_workers = workers_per_gpu
|
| 82 |
+
else:
|
| 83 |
+
sampler = None
|
| 84 |
+
batch_size = num_gpus * samples_per_gpu
|
| 85 |
+
num_workers = num_gpus * workers_per_gpu
|
| 86 |
+
|
| 87 |
+
init_fn = partial(
|
| 88 |
+
worker_init_fn, num_workers=num_workers, rank=rank,
|
| 89 |
+
seed=seed) if seed is not None else None
|
| 90 |
+
|
| 91 |
+
data_loader = DataLoader(
|
| 92 |
+
dataset,
|
| 93 |
+
batch_size=batch_size,
|
| 94 |
+
sampler=sampler,
|
| 95 |
+
num_workers=num_workers,
|
| 96 |
+
collate_fn=partial(collate, samples_per_gpu=samples_per_gpu),
|
| 97 |
+
pin_memory=False,
|
| 98 |
+
shuffle=shuffle,
|
| 99 |
+
worker_init_fn=init_fn,
|
| 100 |
+
**kwargs)
|
| 101 |
+
|
| 102 |
+
return data_loader
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def worker_init_fn(worker_id, num_workers, rank, seed):
|
| 106 |
+
# The seed of each worker equals to
|
| 107 |
+
# num_worker * rank + worker_id + user_seed
|
| 108 |
+
worker_seed = num_workers * rank + worker_id + seed
|
| 109 |
+
np.random.seed(worker_seed)
|
| 110 |
+
random.seed(worker_seed)
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/cifar.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import os.path
|
| 3 |
+
import pickle
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
from .base_dataset import BaseDataset
|
| 8 |
+
from .builder import DATASETS
|
| 9 |
+
from .utils import check_integrity, download_and_extract_archive
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@DATASETS.register_module()
|
| 13 |
+
class CIFAR10(BaseDataset):
|
| 14 |
+
"""`CIFAR10 <https://www.cs.toronto.edu/~kriz/cifar.html>`_ Dataset.
|
| 15 |
+
|
| 16 |
+
This implementation is modified from
|
| 17 |
+
https://github.com/pytorch/vision/blob/master/torchvision/datasets/cifar.py # noqa: E501
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
base_folder = 'cifar-10-batches-py'
|
| 21 |
+
url = 'https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz'
|
| 22 |
+
filename = 'cifar-10-python.tar.gz'
|
| 23 |
+
tgz_md5 = 'c58f30108f718f92721af3b95e74349a'
|
| 24 |
+
train_list = [
|
| 25 |
+
['data_batch_1', 'c99cafc152244af753f735de768cd75f'],
|
| 26 |
+
['data_batch_2', 'd4bba439e000b95fd0a9bffe97cbabec'],
|
| 27 |
+
['data_batch_3', '54ebc095f3ab1f0389bbae665268c751'],
|
| 28 |
+
['data_batch_4', '634d18415352ddfa80567beed471001a'],
|
| 29 |
+
['data_batch_5', '482c414d41f54cd18b22e5b47cb7c3cb'],
|
| 30 |
+
]
|
| 31 |
+
|
| 32 |
+
test_list = [
|
| 33 |
+
['test_batch', '40351d587109b95175f43aff81a1287e'],
|
| 34 |
+
]
|
| 35 |
+
meta = {
|
| 36 |
+
'filename': 'batches.meta',
|
| 37 |
+
'key': 'label_names',
|
| 38 |
+
'md5': '5ff9c542aee3614f3951f8cda6e48888',
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
def load_annotations(self):
|
| 42 |
+
|
| 43 |
+
if not self._check_integrity():
|
| 44 |
+
download_and_extract_archive(
|
| 45 |
+
self.url,
|
| 46 |
+
self.data_prefix,
|
| 47 |
+
filename=self.filename,
|
| 48 |
+
md5=self.tgz_md5)
|
| 49 |
+
|
| 50 |
+
if not self.test_mode:
|
| 51 |
+
downloaded_list = self.train_list
|
| 52 |
+
else:
|
| 53 |
+
downloaded_list = self.test_list
|
| 54 |
+
|
| 55 |
+
self.imgs = []
|
| 56 |
+
self.gt_labels = []
|
| 57 |
+
|
| 58 |
+
# load the picked numpy arrays
|
| 59 |
+
for file_name, checksum in downloaded_list:
|
| 60 |
+
file_path = os.path.join(self.data_prefix, self.base_folder,
|
| 61 |
+
file_name)
|
| 62 |
+
with open(file_path, 'rb') as f:
|
| 63 |
+
entry = pickle.load(f, encoding='latin1')
|
| 64 |
+
self.imgs.append(entry['data'])
|
| 65 |
+
if 'labels' in entry:
|
| 66 |
+
self.gt_labels.extend(entry['labels'])
|
| 67 |
+
else:
|
| 68 |
+
self.gt_labels.extend(entry['fine_labels'])
|
| 69 |
+
|
| 70 |
+
self.imgs = np.vstack(self.imgs).reshape(-1, 3, 32, 32)
|
| 71 |
+
self.imgs = self.imgs.transpose((0, 2, 3, 1)) # convert to HWC
|
| 72 |
+
|
| 73 |
+
self._load_meta()
|
| 74 |
+
|
| 75 |
+
data_infos = []
|
| 76 |
+
for img, gt_label in zip(self.imgs, self.gt_labels):
|
| 77 |
+
gt_label = np.array(gt_label, dtype=np.int64)
|
| 78 |
+
info = {'img': img, 'gt_label': gt_label}
|
| 79 |
+
data_infos.append(info)
|
| 80 |
+
return data_infos
|
| 81 |
+
|
| 82 |
+
def _load_meta(self):
|
| 83 |
+
path = os.path.join(self.data_prefix, self.base_folder,
|
| 84 |
+
self.meta['filename'])
|
| 85 |
+
if not check_integrity(path, self.meta['md5']):
|
| 86 |
+
raise RuntimeError(
|
| 87 |
+
'Dataset metadata file not found or corrupted.' +
|
| 88 |
+
' You can use download=True to download it')
|
| 89 |
+
with open(path, 'rb') as infile:
|
| 90 |
+
data = pickle.load(infile, encoding='latin1')
|
| 91 |
+
self.CLASSES = data[self.meta['key']]
|
| 92 |
+
|
| 93 |
+
def _check_integrity(self):
|
| 94 |
+
root = self.data_prefix
|
| 95 |
+
for fentry in (self.train_list + self.test_list):
|
| 96 |
+
filename, md5 = fentry[0], fentry[1]
|
| 97 |
+
fpath = os.path.join(root, self.base_folder, filename)
|
| 98 |
+
if not check_integrity(fpath, md5):
|
| 99 |
+
return False
|
| 100 |
+
return True
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
@DATASETS.register_module()
|
| 104 |
+
class CIFAR100(CIFAR10):
|
| 105 |
+
"""`CIFAR100 <https://www.cs.toronto.edu/~kriz/cifar.html>`_ Dataset.
|
| 106 |
+
"""
|
| 107 |
+
|
| 108 |
+
base_folder = 'cifar-100-python'
|
| 109 |
+
url = 'https://www.cs.toronto.edu/~kriz/cifar-100-python.tar.gz'
|
| 110 |
+
filename = 'cifar-100-python.tar.gz'
|
| 111 |
+
tgz_md5 = 'eb9058c3a382ffc7106e4002c42a8d85'
|
| 112 |
+
train_list = [
|
| 113 |
+
['train', '16019d7e3df5f24257cddd939b257f8d'],
|
| 114 |
+
]
|
| 115 |
+
|
| 116 |
+
test_list = [
|
| 117 |
+
['test', 'f0ef6b0ae62326f3e7ffdfab6717acfc'],
|
| 118 |
+
]
|
| 119 |
+
meta = {
|
| 120 |
+
'filename': 'meta',
|
| 121 |
+
'key': 'fine_label_names',
|
| 122 |
+
'md5': '7973b15100ade9c7d40fb424638fde48',
|
| 123 |
+
}
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/dataset_wrappers.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import bisect
|
| 2 |
+
import math
|
| 3 |
+
from collections import defaultdict
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
from torch.utils.data.dataset import ConcatDataset as _ConcatDataset
|
| 7 |
+
|
| 8 |
+
from .builder import DATASETS
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@DATASETS.register_module()
|
| 12 |
+
class ConcatDataset(_ConcatDataset):
|
| 13 |
+
"""A wrapper of concatenated dataset.
|
| 14 |
+
|
| 15 |
+
Same as :obj:`torch.utils.data.dataset.ConcatDataset`, but
|
| 16 |
+
add `get_cat_ids` function.
|
| 17 |
+
|
| 18 |
+
Args:
|
| 19 |
+
datasets (list[:obj:`Dataset`]): A list of datasets.
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
def __init__(self, datasets):
|
| 23 |
+
super(ConcatDataset, self).__init__(datasets)
|
| 24 |
+
self.CLASSES = datasets[0].CLASSES
|
| 25 |
+
|
| 26 |
+
def get_cat_ids(self, idx):
|
| 27 |
+
if idx < 0:
|
| 28 |
+
if -idx > len(self):
|
| 29 |
+
raise ValueError(
|
| 30 |
+
'absolute value of index should not exceed dataset length')
|
| 31 |
+
idx = len(self) + idx
|
| 32 |
+
dataset_idx = bisect.bisect_right(self.cumulative_sizes, idx)
|
| 33 |
+
if dataset_idx == 0:
|
| 34 |
+
sample_idx = idx
|
| 35 |
+
else:
|
| 36 |
+
sample_idx = idx - self.cumulative_sizes[dataset_idx - 1]
|
| 37 |
+
return self.datasets[dataset_idx].get_cat_ids(sample_idx)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
@DATASETS.register_module()
|
| 41 |
+
class RepeatDataset(object):
|
| 42 |
+
"""A wrapper of repeated dataset.
|
| 43 |
+
|
| 44 |
+
The length of repeated dataset will be `times` larger than the original
|
| 45 |
+
dataset. This is useful when the data loading time is long but the dataset
|
| 46 |
+
is small. Using RepeatDataset can reduce the data loading time between
|
| 47 |
+
epochs.
|
| 48 |
+
|
| 49 |
+
Args:
|
| 50 |
+
dataset (:obj:`Dataset`): The dataset to be repeated.
|
| 51 |
+
times (int): Repeat times.
|
| 52 |
+
"""
|
| 53 |
+
|
| 54 |
+
def __init__(self, dataset, times):
|
| 55 |
+
self.dataset = dataset
|
| 56 |
+
self.times = times
|
| 57 |
+
self.CLASSES = dataset.CLASSES
|
| 58 |
+
|
| 59 |
+
self._ori_len = len(self.dataset)
|
| 60 |
+
|
| 61 |
+
def __getitem__(self, idx):
|
| 62 |
+
return self.dataset[idx % self._ori_len]
|
| 63 |
+
|
| 64 |
+
def get_cat_ids(self, idx):
|
| 65 |
+
return self.dataset.get_cat_ids(idx % self._ori_len)
|
| 66 |
+
|
| 67 |
+
def __len__(self):
|
| 68 |
+
return self.times * self._ori_len
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
# Modified from https://github.com/facebookresearch/detectron2/blob/41d475b75a230221e21d9cac5d69655e3415e3a4/detectron2/data/samplers/distributed_sampler.py#L57 # noqa
|
| 72 |
+
@DATASETS.register_module()
|
| 73 |
+
class ClassBalancedDataset(object):
|
| 74 |
+
"""A wrapper of repeated dataset with repeat factor.
|
| 75 |
+
|
| 76 |
+
Suitable for training on class imbalanced datasets like LVIS. Following
|
| 77 |
+
the sampling strategy in [1], in each epoch, an image may appear multiple
|
| 78 |
+
times based on its "repeat factor".
|
| 79 |
+
The repeat factor for an image is a function of the frequency the rarest
|
| 80 |
+
category labeled in that image. The "frequency of category c" in [0, 1]
|
| 81 |
+
is defined by the fraction of images in the training set (without repeats)
|
| 82 |
+
in which category c appears.
|
| 83 |
+
The dataset needs to instantiate :func:`self.get_cat_ids(idx)` to support
|
| 84 |
+
ClassBalancedDataset.
|
| 85 |
+
The repeat factor is computed as followed.
|
| 86 |
+
1. For each category c, compute the fraction # of images
|
| 87 |
+
that contain it: f(c)
|
| 88 |
+
2. For each category c, compute the category-level repeat factor:
|
| 89 |
+
r(c) = max(1, sqrt(t/f(c)))
|
| 90 |
+
3. For each image I and its labels L(I), compute the image-level repeat
|
| 91 |
+
factor:
|
| 92 |
+
r(I) = max_{c in L(I)} r(c)
|
| 93 |
+
|
| 94 |
+
References:
|
| 95 |
+
.. [1] https://arxiv.org/pdf/1908.03195.pdf
|
| 96 |
+
|
| 97 |
+
Args:
|
| 98 |
+
dataset (:obj:`CustomDataset`): The dataset to be repeated.
|
| 99 |
+
oversample_thr (float): frequency threshold below which data is
|
| 100 |
+
repeated. For categories with `f_c` >= `oversample_thr`, there is
|
| 101 |
+
no oversampling. For categories with `f_c` < `oversample_thr`, the
|
| 102 |
+
degree of oversampling following the square-root inverse frequency
|
| 103 |
+
heuristic above.
|
| 104 |
+
"""
|
| 105 |
+
|
| 106 |
+
def __init__(self, dataset, oversample_thr, method='sqrt', **kwargs):
|
| 107 |
+
assert(method in ('sqrt', 'reciprocal')) # reciprocal,倒数,绝对 balance, repeat_factor = f(c) / max( fc )
|
| 108 |
+
self.method = method
|
| 109 |
+
self.dataset = dataset
|
| 110 |
+
self.oversample_thr = oversample_thr
|
| 111 |
+
self.CLASSES = dataset.CLASSES
|
| 112 |
+
|
| 113 |
+
repeat_factors = self._get_repeat_factors(dataset, oversample_thr)
|
| 114 |
+
repeat_indices = []
|
| 115 |
+
for dataset_index, repeat_factor in enumerate(repeat_factors):
|
| 116 |
+
repeat_indices.extend([dataset_index] * math.ceil(repeat_factor))
|
| 117 |
+
self.repeat_indices = repeat_indices
|
| 118 |
+
|
| 119 |
+
flags = []
|
| 120 |
+
if hasattr(self.dataset, 'flag'):
|
| 121 |
+
for flag, repeat_factor in zip(self.dataset.flag, repeat_factors):
|
| 122 |
+
flags.extend([flag] * int(math.ceil(repeat_factor)))
|
| 123 |
+
assert len(flags) == len(repeat_indices)
|
| 124 |
+
self.flag = np.asarray(flags, dtype=np.uint8)
|
| 125 |
+
|
| 126 |
+
def _get_repeat_factors(self, dataset, repeat_thr):
|
| 127 |
+
# 1. For each category c, compute the fraction # of images
|
| 128 |
+
# that contain it: f(c)
|
| 129 |
+
category_freq = defaultdict(int)
|
| 130 |
+
num_images = len(dataset)
|
| 131 |
+
for idx in range(num_images):
|
| 132 |
+
cat_ids = set(self.dataset.get_cat_ids(idx))
|
| 133 |
+
for cat_id in cat_ids:
|
| 134 |
+
category_freq[cat_id] += 1
|
| 135 |
+
for k, v in category_freq.items():
|
| 136 |
+
assert v > 0, f'caterogy {k} does not contain any images'
|
| 137 |
+
category_freq[k] = v / num_images
|
| 138 |
+
|
| 139 |
+
# 2. For each category c, compute the category-level repeat factor:
|
| 140 |
+
# r(c) = max(1, sqrt(t/f(c)))
|
| 141 |
+
if self.method == 'sqrt':
|
| 142 |
+
category_repeat = {
|
| 143 |
+
cat_id: max(1.0, math.sqrt(repeat_thr / cat_freq))
|
| 144 |
+
for cat_id, cat_freq in category_freq.items()
|
| 145 |
+
}
|
| 146 |
+
elif self.method == 'reciprocal':
|
| 147 |
+
cat_freq_max = 0
|
| 148 |
+
for cat_id, cat_freq in category_freq.items():
|
| 149 |
+
cat_freq_max = max(cat_freq_max, cat_freq)
|
| 150 |
+
print('cat_freq_max: ', cat_freq_max)
|
| 151 |
+
category_repeat = {
|
| 152 |
+
cat_id: cat_freq_max / cat_freq
|
| 153 |
+
for cat_id, cat_freq in category_freq.items()
|
| 154 |
+
}
|
| 155 |
+
# 3. For each image I and its labels L(I), compute the image-level
|
| 156 |
+
# repeat factor:
|
| 157 |
+
# r(I) = max_{c in L(I)} r(c)
|
| 158 |
+
repeat_factors = []
|
| 159 |
+
for idx in range(num_images):
|
| 160 |
+
cat_ids = set(self.dataset.get_cat_ids(idx))
|
| 161 |
+
repeat_factor = max(
|
| 162 |
+
{category_repeat[cat_id]
|
| 163 |
+
for cat_id in cat_ids})
|
| 164 |
+
repeat_factors.append(repeat_factor)
|
| 165 |
+
|
| 166 |
+
return repeat_factors
|
| 167 |
+
|
| 168 |
+
def __getitem__(self, idx):
|
| 169 |
+
ori_index = self.repeat_indices[idx]
|
| 170 |
+
return self.dataset[ori_index]
|
| 171 |
+
|
| 172 |
+
def __len__(self):
|
| 173 |
+
return len(self.repeat_indices)
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/imagenet.py
ADDED
|
@@ -0,0 +1,1105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
|
| 5 |
+
from .base_dataset import BaseDataset
|
| 6 |
+
from .builder import DATASETS
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def has_file_allowed_extension(filename, extensions):
|
| 10 |
+
"""Checks if a file is an allowed extension.
|
| 11 |
+
|
| 12 |
+
Args:
|
| 13 |
+
filename (string): path to a file
|
| 14 |
+
|
| 15 |
+
Returns:
|
| 16 |
+
bool: True if the filename ends with a known image extension
|
| 17 |
+
"""
|
| 18 |
+
filename_lower = filename.lower()
|
| 19 |
+
return any(filename_lower.endswith(ext) for ext in extensions)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def find_folders(root):
|
| 23 |
+
"""Find classes by folders under a root.
|
| 24 |
+
|
| 25 |
+
Args:
|
| 26 |
+
root (string): root directory of folders
|
| 27 |
+
|
| 28 |
+
Returns:
|
| 29 |
+
folder_to_idx (dict): the map from folder name to class idx
|
| 30 |
+
"""
|
| 31 |
+
folders = [
|
| 32 |
+
d for d in os.listdir(root) if os.path.isdir(os.path.join(root, d))
|
| 33 |
+
]
|
| 34 |
+
folders.sort()
|
| 35 |
+
folder_to_idx = {folders[i]: i for i in range(len(folders))}
|
| 36 |
+
return folder_to_idx
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def get_samples(root, folder_to_idx, extensions):
|
| 40 |
+
"""Make dataset by walking all images under a root.
|
| 41 |
+
|
| 42 |
+
Args:
|
| 43 |
+
root (string): root directory of folders
|
| 44 |
+
folder_to_idx (dict): the map from class name to class idx
|
| 45 |
+
extensions (tuple): allowed extensions
|
| 46 |
+
|
| 47 |
+
Returns:
|
| 48 |
+
samples (list): a list of tuple where each element is (image, label)
|
| 49 |
+
"""
|
| 50 |
+
samples = []
|
| 51 |
+
root = os.path.expanduser(root)
|
| 52 |
+
for folder_name in sorted(os.listdir(root)):
|
| 53 |
+
_dir = os.path.join(root, folder_name)
|
| 54 |
+
if not os.path.isdir(_dir):
|
| 55 |
+
continue
|
| 56 |
+
|
| 57 |
+
for _, _, fns in sorted(os.walk(_dir)):
|
| 58 |
+
for fn in sorted(fns):
|
| 59 |
+
if has_file_allowed_extension(fn, extensions):
|
| 60 |
+
path = os.path.join(folder_name, fn)
|
| 61 |
+
item = (path, folder_to_idx[folder_name])
|
| 62 |
+
samples.append(item)
|
| 63 |
+
return samples
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
@DATASETS.register_module()
|
| 67 |
+
class ImageNet(BaseDataset):
|
| 68 |
+
"""`ImageNet <http://www.image-net.org>`_ Dataset.
|
| 69 |
+
|
| 70 |
+
This implementation is modified from
|
| 71 |
+
https://github.com/pytorch/vision/blob/master/torchvision/datasets/imagenet.py # noqa: E501
|
| 72 |
+
"""
|
| 73 |
+
|
| 74 |
+
IMG_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.ppm', '.bmp', '.pgm', '.tif')
|
| 75 |
+
CLASSES = [
|
| 76 |
+
'tench, Tinca tinca',
|
| 77 |
+
'goldfish, Carassius auratus',
|
| 78 |
+
'great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias', # noqa: E501
|
| 79 |
+
'tiger shark, Galeocerdo cuvieri',
|
| 80 |
+
'hammerhead, hammerhead shark',
|
| 81 |
+
'electric ray, crampfish, numbfish, torpedo',
|
| 82 |
+
'stingray',
|
| 83 |
+
'cock',
|
| 84 |
+
'hen',
|
| 85 |
+
'ostrich, Struthio camelus',
|
| 86 |
+
'brambling, Fringilla montifringilla',
|
| 87 |
+
'goldfinch, Carduelis carduelis',
|
| 88 |
+
'house finch, linnet, Carpodacus mexicanus',
|
| 89 |
+
'junco, snowbird',
|
| 90 |
+
'indigo bunting, indigo finch, indigo bird, Passerina cyanea',
|
| 91 |
+
'robin, American robin, Turdus migratorius',
|
| 92 |
+
'bulbul',
|
| 93 |
+
'jay',
|
| 94 |
+
'magpie',
|
| 95 |
+
'chickadee',
|
| 96 |
+
'water ouzel, dipper',
|
| 97 |
+
'kite',
|
| 98 |
+
'bald eagle, American eagle, Haliaeetus leucocephalus',
|
| 99 |
+
'vulture',
|
| 100 |
+
'great grey owl, great gray owl, Strix nebulosa',
|
| 101 |
+
'European fire salamander, Salamandra salamandra',
|
| 102 |
+
'common newt, Triturus vulgaris',
|
| 103 |
+
'eft',
|
| 104 |
+
'spotted salamander, Ambystoma maculatum',
|
| 105 |
+
'axolotl, mud puppy, Ambystoma mexicanum',
|
| 106 |
+
'bullfrog, Rana catesbeiana',
|
| 107 |
+
'tree frog, tree-frog',
|
| 108 |
+
'tailed frog, bell toad, ribbed toad, tailed toad, Ascaphus trui',
|
| 109 |
+
'loggerhead, loggerhead turtle, Caretta caretta',
|
| 110 |
+
'leatherback turtle, leatherback, leathery turtle, Dermochelys coriacea', # noqa: E501
|
| 111 |
+
'mud turtle',
|
| 112 |
+
'terrapin',
|
| 113 |
+
'box turtle, box tortoise',
|
| 114 |
+
'banded gecko',
|
| 115 |
+
'common iguana, iguana, Iguana iguana',
|
| 116 |
+
'American chameleon, anole, Anolis carolinensis',
|
| 117 |
+
'whiptail, whiptail lizard',
|
| 118 |
+
'agama',
|
| 119 |
+
'frilled lizard, Chlamydosaurus kingi',
|
| 120 |
+
'alligator lizard',
|
| 121 |
+
'Gila monster, Heloderma suspectum',
|
| 122 |
+
'green lizard, Lacerta viridis',
|
| 123 |
+
'African chameleon, Chamaeleo chamaeleon',
|
| 124 |
+
'Komodo dragon, Komodo lizard, dragon lizard, giant lizard, Varanus komodoensis', # noqa: E501
|
| 125 |
+
'African crocodile, Nile crocodile, Crocodylus niloticus',
|
| 126 |
+
'American alligator, Alligator mississipiensis',
|
| 127 |
+
'triceratops',
|
| 128 |
+
'thunder snake, worm snake, Carphophis amoenus',
|
| 129 |
+
'ringneck snake, ring-necked snake, ring snake',
|
| 130 |
+
'hognose snake, puff adder, sand viper',
|
| 131 |
+
'green snake, grass snake',
|
| 132 |
+
'king snake, kingsnake',
|
| 133 |
+
'garter snake, grass snake',
|
| 134 |
+
'water snake',
|
| 135 |
+
'vine snake',
|
| 136 |
+
'night snake, Hypsiglena torquata',
|
| 137 |
+
'boa constrictor, Constrictor constrictor',
|
| 138 |
+
'rock python, rock snake, Python sebae',
|
| 139 |
+
'Indian cobra, Naja naja',
|
| 140 |
+
'green mamba',
|
| 141 |
+
'sea snake',
|
| 142 |
+
'horned viper, cerastes, sand viper, horned asp, Cerastes cornutus',
|
| 143 |
+
'diamondback, diamondback rattlesnake, Crotalus adamanteus',
|
| 144 |
+
'sidewinder, horned rattlesnake, Crotalus cerastes',
|
| 145 |
+
'trilobite',
|
| 146 |
+
'harvestman, daddy longlegs, Phalangium opilio',
|
| 147 |
+
'scorpion',
|
| 148 |
+
'black and gold garden spider, Argiope aurantia',
|
| 149 |
+
'barn spider, Araneus cavaticus',
|
| 150 |
+
'garden spider, Aranea diademata',
|
| 151 |
+
'black widow, Latrodectus mactans',
|
| 152 |
+
'tarantula',
|
| 153 |
+
'wolf spider, hunting spider',
|
| 154 |
+
'tick',
|
| 155 |
+
'centipede',
|
| 156 |
+
'black grouse',
|
| 157 |
+
'ptarmigan',
|
| 158 |
+
'ruffed grouse, partridge, Bonasa umbellus',
|
| 159 |
+
'prairie chicken, prairie grouse, prairie fowl',
|
| 160 |
+
'peacock',
|
| 161 |
+
'quail',
|
| 162 |
+
'partridge',
|
| 163 |
+
'African grey, African gray, Psittacus erithacus',
|
| 164 |
+
'macaw',
|
| 165 |
+
'sulphur-crested cockatoo, Kakatoe galerita, Cacatua galerita',
|
| 166 |
+
'lorikeet',
|
| 167 |
+
'coucal',
|
| 168 |
+
'bee eater',
|
| 169 |
+
'hornbill',
|
| 170 |
+
'hummingbird',
|
| 171 |
+
'jacamar',
|
| 172 |
+
'toucan',
|
| 173 |
+
'drake',
|
| 174 |
+
'red-breasted merganser, Mergus serrator',
|
| 175 |
+
'goose',
|
| 176 |
+
'black swan, Cygnus atratus',
|
| 177 |
+
'tusker',
|
| 178 |
+
'echidna, spiny anteater, anteater',
|
| 179 |
+
'platypus, duckbill, duckbilled platypus, duck-billed platypus, Ornithorhynchus anatinus', # noqa: E501
|
| 180 |
+
'wallaby, brush kangaroo',
|
| 181 |
+
'koala, koala bear, kangaroo bear, native bear, Phascolarctos cinereus', # noqa: E501
|
| 182 |
+
'wombat',
|
| 183 |
+
'jellyfish',
|
| 184 |
+
'sea anemone, anemone',
|
| 185 |
+
'brain coral',
|
| 186 |
+
'flatworm, platyhelminth',
|
| 187 |
+
'nematode, nematode worm, roundworm',
|
| 188 |
+
'conch',
|
| 189 |
+
'snail',
|
| 190 |
+
'slug',
|
| 191 |
+
'sea slug, nudibranch',
|
| 192 |
+
'chiton, coat-of-mail shell, sea cradle, polyplacophore',
|
| 193 |
+
'chambered nautilus, pearly nautilus, nautilus',
|
| 194 |
+
'Dungeness crab, Cancer magister',
|
| 195 |
+
'rock crab, Cancer irroratus',
|
| 196 |
+
'fiddler crab',
|
| 197 |
+
'king crab, Alaska crab, Alaskan king crab, Alaska king crab, Paralithodes camtschatica', # noqa: E501
|
| 198 |
+
'American lobster, Northern lobster, Maine lobster, Homarus americanus', # noqa: E501
|
| 199 |
+
'spiny lobster, langouste, rock lobster, crawfish, crayfish, sea crawfish', # noqa: E501
|
| 200 |
+
'crayfish, crawfish, crawdad, crawdaddy',
|
| 201 |
+
'hermit crab',
|
| 202 |
+
'isopod',
|
| 203 |
+
'white stork, Ciconia ciconia',
|
| 204 |
+
'black stork, Ciconia nigra',
|
| 205 |
+
'spoonbill',
|
| 206 |
+
'flamingo',
|
| 207 |
+
'little blue heron, Egretta caerulea',
|
| 208 |
+
'American egret, great white heron, Egretta albus',
|
| 209 |
+
'bittern',
|
| 210 |
+
'crane',
|
| 211 |
+
'limpkin, Aramus pictus',
|
| 212 |
+
'European gallinule, Porphyrio porphyrio',
|
| 213 |
+
'American coot, marsh hen, mud hen, water hen, Fulica americana',
|
| 214 |
+
'bustard',
|
| 215 |
+
'ruddy turnstone, Arenaria interpres',
|
| 216 |
+
'red-backed sandpiper, dunlin, Erolia alpina',
|
| 217 |
+
'redshank, Tringa totanus',
|
| 218 |
+
'dowitcher',
|
| 219 |
+
'oystercatcher, oyster catcher',
|
| 220 |
+
'pelican',
|
| 221 |
+
'king penguin, Aptenodytes patagonica',
|
| 222 |
+
'albatross, mollymawk',
|
| 223 |
+
'grey whale, gray whale, devilfish, Eschrichtius gibbosus, Eschrichtius robustus', # noqa: E501
|
| 224 |
+
'killer whale, killer, orca, grampus, sea wolf, Orcinus orca',
|
| 225 |
+
'dugong, Dugong dugon',
|
| 226 |
+
'sea lion',
|
| 227 |
+
'Chihuahua',
|
| 228 |
+
'Japanese spaniel',
|
| 229 |
+
'Maltese dog, Maltese terrier, Maltese',
|
| 230 |
+
'Pekinese, Pekingese, Peke',
|
| 231 |
+
'Shih-Tzu',
|
| 232 |
+
'Blenheim spaniel',
|
| 233 |
+
'papillon',
|
| 234 |
+
'toy terrier',
|
| 235 |
+
'Rhodesian ridgeback',
|
| 236 |
+
'Afghan hound, Afghan',
|
| 237 |
+
'basset, basset hound',
|
| 238 |
+
'beagle',
|
| 239 |
+
'bloodhound, sleuthhound',
|
| 240 |
+
'bluetick',
|
| 241 |
+
'black-and-tan coonhound',
|
| 242 |
+
'Walker hound, Walker foxhound',
|
| 243 |
+
'English foxhound',
|
| 244 |
+
'redbone',
|
| 245 |
+
'borzoi, Russian wolfhound',
|
| 246 |
+
'Irish wolfhound',
|
| 247 |
+
'Italian greyhound',
|
| 248 |
+
'whippet',
|
| 249 |
+
'Ibizan hound, Ibizan Podenco',
|
| 250 |
+
'Norwegian elkhound, elkhound',
|
| 251 |
+
'otterhound, otter hound',
|
| 252 |
+
'Saluki, gazelle hound',
|
| 253 |
+
'Scottish deerhound, deerhound',
|
| 254 |
+
'Weimaraner',
|
| 255 |
+
'Staffordshire bullterrier, Staffordshire bull terrier',
|
| 256 |
+
'American Staffordshire terrier, Staffordshire terrier, American pit bull terrier, pit bull terrier', # noqa: E501
|
| 257 |
+
'Bedlington terrier',
|
| 258 |
+
'Border terrier',
|
| 259 |
+
'Kerry blue terrier',
|
| 260 |
+
'Irish terrier',
|
| 261 |
+
'Norfolk terrier',
|
| 262 |
+
'Norwich terrier',
|
| 263 |
+
'Yorkshire terrier',
|
| 264 |
+
'wire-haired fox terrier',
|
| 265 |
+
'Lakeland terrier',
|
| 266 |
+
'Sealyham terrier, Sealyham',
|
| 267 |
+
'Airedale, Airedale terrier',
|
| 268 |
+
'cairn, cairn terrier',
|
| 269 |
+
'Australian terrier',
|
| 270 |
+
'Dandie Dinmont, Dandie Dinmont terrier',
|
| 271 |
+
'Boston bull, Boston terrier',
|
| 272 |
+
'miniature schnauzer',
|
| 273 |
+
'giant schnauzer',
|
| 274 |
+
'standard schnauzer',
|
| 275 |
+
'Scotch terrier, Scottish terrier, Scottie',
|
| 276 |
+
'Tibetan terrier, chrysanthemum dog',
|
| 277 |
+
'silky terrier, Sydney silky',
|
| 278 |
+
'soft-coated wheaten terrier',
|
| 279 |
+
'West Highland white terrier',
|
| 280 |
+
'Lhasa, Lhasa apso',
|
| 281 |
+
'flat-coated retriever',
|
| 282 |
+
'curly-coated retriever',
|
| 283 |
+
'golden retriever',
|
| 284 |
+
'Labrador retriever',
|
| 285 |
+
'Chesapeake Bay retriever',
|
| 286 |
+
'German short-haired pointer',
|
| 287 |
+
'vizsla, Hungarian pointer',
|
| 288 |
+
'English setter',
|
| 289 |
+
'Irish setter, red setter',
|
| 290 |
+
'Gordon setter',
|
| 291 |
+
'Brittany spaniel',
|
| 292 |
+
'clumber, clumber spaniel',
|
| 293 |
+
'English springer, English springer spaniel',
|
| 294 |
+
'Welsh springer spaniel',
|
| 295 |
+
'cocker spaniel, English cocker spaniel, cocker',
|
| 296 |
+
'Sussex spaniel',
|
| 297 |
+
'Irish water spaniel',
|
| 298 |
+
'kuvasz',
|
| 299 |
+
'schipperke',
|
| 300 |
+
'groenendael',
|
| 301 |
+
'malinois',
|
| 302 |
+
'briard',
|
| 303 |
+
'kelpie',
|
| 304 |
+
'komondor',
|
| 305 |
+
'Old English sheepdog, bobtail',
|
| 306 |
+
'Shetland sheepdog, Shetland sheep dog, Shetland',
|
| 307 |
+
'collie',
|
| 308 |
+
'Border collie',
|
| 309 |
+
'Bouvier des Flandres, Bouviers des Flandres',
|
| 310 |
+
'Rottweiler',
|
| 311 |
+
'German shepherd, German shepherd dog, German police dog, alsatian',
|
| 312 |
+
'Doberman, Doberman pinscher',
|
| 313 |
+
'miniature pinscher',
|
| 314 |
+
'Greater Swiss Mountain dog',
|
| 315 |
+
'Bernese mountain dog',
|
| 316 |
+
'Appenzeller',
|
| 317 |
+
'EntleBucher',
|
| 318 |
+
'boxer',
|
| 319 |
+
'bull mastiff',
|
| 320 |
+
'Tibetan mastiff',
|
| 321 |
+
'French bulldog',
|
| 322 |
+
'Great Dane',
|
| 323 |
+
'Saint Bernard, St Bernard',
|
| 324 |
+
'Eskimo dog, husky',
|
| 325 |
+
'malamute, malemute, Alaskan malamute',
|
| 326 |
+
'Siberian husky',
|
| 327 |
+
'dalmatian, coach dog, carriage dog',
|
| 328 |
+
'affenpinscher, monkey pinscher, monkey dog',
|
| 329 |
+
'basenji',
|
| 330 |
+
'pug, pug-dog',
|
| 331 |
+
'Leonberg',
|
| 332 |
+
'Newfoundland, Newfoundland dog',
|
| 333 |
+
'Great Pyrenees',
|
| 334 |
+
'Samoyed, Samoyede',
|
| 335 |
+
'Pomeranian',
|
| 336 |
+
'chow, chow chow',
|
| 337 |
+
'keeshond',
|
| 338 |
+
'Brabancon griffon',
|
| 339 |
+
'Pembroke, Pembroke Welsh corgi',
|
| 340 |
+
'Cardigan, Cardigan Welsh corgi',
|
| 341 |
+
'toy poodle',
|
| 342 |
+
'miniature poodle',
|
| 343 |
+
'standard poodle',
|
| 344 |
+
'Mexican hairless',
|
| 345 |
+
'timber wolf, grey wolf, gray wolf, Canis lupus',
|
| 346 |
+
'white wolf, Arctic wolf, Canis lupus tundrarum',
|
| 347 |
+
'red wolf, maned wolf, Canis rufus, Canis niger',
|
| 348 |
+
'coyote, prairie wolf, brush wolf, Canis latrans',
|
| 349 |
+
'dingo, warrigal, warragal, Canis dingo',
|
| 350 |
+
'dhole, Cuon alpinus',
|
| 351 |
+
'African hunting dog, hyena dog, Cape hunting dog, Lycaon pictus',
|
| 352 |
+
'hyena, hyaena',
|
| 353 |
+
'red fox, Vulpes vulpes',
|
| 354 |
+
'kit fox, Vulpes macrotis',
|
| 355 |
+
'Arctic fox, white fox, Alopex lagopus',
|
| 356 |
+
'grey fox, gray fox, Urocyon cinereoargenteus',
|
| 357 |
+
'tabby, tabby cat',
|
| 358 |
+
'tiger cat',
|
| 359 |
+
'Persian cat',
|
| 360 |
+
'Siamese cat, Siamese',
|
| 361 |
+
'Egyptian cat',
|
| 362 |
+
'cougar, puma, catamount, mountain lion, painter, panther, Felis concolor', # noqa: E501
|
| 363 |
+
'lynx, catamount',
|
| 364 |
+
'leopard, Panthera pardus',
|
| 365 |
+
'snow leopard, ounce, Panthera uncia',
|
| 366 |
+
'jaguar, panther, Panthera onca, Felis onca',
|
| 367 |
+
'lion, king of beasts, Panthera leo',
|
| 368 |
+
'tiger, Panthera tigris',
|
| 369 |
+
'cheetah, chetah, Acinonyx jubatus',
|
| 370 |
+
'brown bear, bruin, Ursus arctos',
|
| 371 |
+
'American black bear, black bear, Ursus americanus, Euarctos americanus', # noqa: E501
|
| 372 |
+
'ice bear, polar bear, Ursus Maritimus, Thalarctos maritimus',
|
| 373 |
+
'sloth bear, Melursus ursinus, Ursus ursinus',
|
| 374 |
+
'mongoose',
|
| 375 |
+
'meerkat, mierkat',
|
| 376 |
+
'tiger beetle',
|
| 377 |
+
'ladybug, ladybeetle, lady beetle, ladybird, ladybird beetle',
|
| 378 |
+
'ground beetle, carabid beetle',
|
| 379 |
+
'long-horned beetle, longicorn, longicorn beetle',
|
| 380 |
+
'leaf beetle, chrysomelid',
|
| 381 |
+
'dung beetle',
|
| 382 |
+
'rhinoceros beetle',
|
| 383 |
+
'weevil',
|
| 384 |
+
'fly',
|
| 385 |
+
'bee',
|
| 386 |
+
'ant, emmet, pismire',
|
| 387 |
+
'grasshopper, hopper',
|
| 388 |
+
'cricket',
|
| 389 |
+
'walking stick, walkingstick, stick insect',
|
| 390 |
+
'cockroach, roach',
|
| 391 |
+
'mantis, mantid',
|
| 392 |
+
'cicada, cicala',
|
| 393 |
+
'leafhopper',
|
| 394 |
+
'lacewing, lacewing fly',
|
| 395 |
+
"dragonfly, darning needle, devil's darning needle, sewing needle, snake feeder, snake doctor, mosquito hawk, skeeter hawk", # noqa: E501
|
| 396 |
+
'damselfly',
|
| 397 |
+
'admiral',
|
| 398 |
+
'ringlet, ringlet butterfly',
|
| 399 |
+
'monarch, monarch butterfly, milkweed butterfly, Danaus plexippus',
|
| 400 |
+
'cabbage butterfly',
|
| 401 |
+
'sulphur butterfly, sulfur butterfly',
|
| 402 |
+
'lycaenid, lycaenid butterfly',
|
| 403 |
+
'starfish, sea star',
|
| 404 |
+
'sea urchin',
|
| 405 |
+
'sea cucumber, holothurian',
|
| 406 |
+
'wood rabbit, cottontail, cottontail rabbit',
|
| 407 |
+
'hare',
|
| 408 |
+
'Angora, Angora rabbit',
|
| 409 |
+
'hamster',
|
| 410 |
+
'porcupine, hedgehog',
|
| 411 |
+
'fox squirrel, eastern fox squirrel, Sciurus niger',
|
| 412 |
+
'marmot',
|
| 413 |
+
'beaver',
|
| 414 |
+
'guinea pig, Cavia cobaya',
|
| 415 |
+
'sorrel',
|
| 416 |
+
'zebra',
|
| 417 |
+
'hog, pig, grunter, squealer, Sus scrofa',
|
| 418 |
+
'wild boar, boar, Sus scrofa',
|
| 419 |
+
'warthog',
|
| 420 |
+
'hippopotamus, hippo, river horse, Hippopotamus amphibius',
|
| 421 |
+
'ox',
|
| 422 |
+
'water buffalo, water ox, Asiatic buffalo, Bubalus bubalis',
|
| 423 |
+
'bison',
|
| 424 |
+
'ram, tup',
|
| 425 |
+
'bighorn, bighorn sheep, cimarron, Rocky Mountain bighorn, Rocky Mountain sheep, Ovis canadensis', # noqa: E501
|
| 426 |
+
'ibex, Capra ibex',
|
| 427 |
+
'hartebeest',
|
| 428 |
+
'impala, Aepyceros melampus',
|
| 429 |
+
'gazelle',
|
| 430 |
+
'Arabian camel, dromedary, Camelus dromedarius',
|
| 431 |
+
'llama',
|
| 432 |
+
'weasel',
|
| 433 |
+
'mink',
|
| 434 |
+
'polecat, fitch, foulmart, foumart, Mustela putorius',
|
| 435 |
+
'black-footed ferret, ferret, Mustela nigripes',
|
| 436 |
+
'otter',
|
| 437 |
+
'skunk, polecat, wood pussy',
|
| 438 |
+
'badger',
|
| 439 |
+
'armadillo',
|
| 440 |
+
'three-toed sloth, ai, Bradypus tridactylus',
|
| 441 |
+
'orangutan, orang, orangutang, Pongo pygmaeus',
|
| 442 |
+
'gorilla, Gorilla gorilla',
|
| 443 |
+
'chimpanzee, chimp, Pan troglodytes',
|
| 444 |
+
'gibbon, Hylobates lar',
|
| 445 |
+
'siamang, Hylobates syndactylus, Symphalangus syndactylus',
|
| 446 |
+
'guenon, guenon monkey',
|
| 447 |
+
'patas, hussar monkey, Erythrocebus patas',
|
| 448 |
+
'baboon',
|
| 449 |
+
'macaque',
|
| 450 |
+
'langur',
|
| 451 |
+
'colobus, colobus monkey',
|
| 452 |
+
'proboscis monkey, Nasalis larvatus',
|
| 453 |
+
'marmoset',
|
| 454 |
+
'capuchin, ringtail, Cebus capucinus',
|
| 455 |
+
'howler monkey, howler',
|
| 456 |
+
'titi, titi monkey',
|
| 457 |
+
'spider monkey, Ateles geoffroyi',
|
| 458 |
+
'squirrel monkey, Saimiri sciureus',
|
| 459 |
+
'Madagascar cat, ring-tailed lemur, Lemur catta',
|
| 460 |
+
'indri, indris, Indri indri, Indri brevicaudatus',
|
| 461 |
+
'Indian elephant, Elephas maximus',
|
| 462 |
+
'African elephant, Loxodonta africana',
|
| 463 |
+
'lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens',
|
| 464 |
+
'giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca',
|
| 465 |
+
'barracouta, snoek',
|
| 466 |
+
'eel',
|
| 467 |
+
'coho, cohoe, coho salmon, blue jack, silver salmon, Oncorhynchus kisutch', # noqa: E501
|
| 468 |
+
'rock beauty, Holocanthus tricolor',
|
| 469 |
+
'anemone fish',
|
| 470 |
+
'sturgeon',
|
| 471 |
+
'gar, garfish, garpike, billfish, Lepisosteus osseus',
|
| 472 |
+
'lionfish',
|
| 473 |
+
'puffer, pufferfish, blowfish, globefish',
|
| 474 |
+
'abacus',
|
| 475 |
+
'abaya',
|
| 476 |
+
"academic gown, academic robe, judge's robe",
|
| 477 |
+
'accordion, piano accordion, squeeze box',
|
| 478 |
+
'acoustic guitar',
|
| 479 |
+
'aircraft carrier, carrier, flattop, attack aircraft carrier',
|
| 480 |
+
'airliner',
|
| 481 |
+
'airship, dirigible',
|
| 482 |
+
'altar',
|
| 483 |
+
'ambulance',
|
| 484 |
+
'amphibian, amphibious vehicle',
|
| 485 |
+
'analog clock',
|
| 486 |
+
'apiary, bee house',
|
| 487 |
+
'apron',
|
| 488 |
+
'ashcan, trash can, garbage can, wastebin, ash bin, ash-bin, ashbin, dustbin, trash barrel, trash bin', # noqa: E501
|
| 489 |
+
'assault rifle, assault gun',
|
| 490 |
+
'backpack, back pack, knapsack, packsack, rucksack, haversack',
|
| 491 |
+
'bakery, bakeshop, bakehouse',
|
| 492 |
+
'balance beam, beam',
|
| 493 |
+
'balloon',
|
| 494 |
+
'ballpoint, ballpoint pen, ballpen, Biro',
|
| 495 |
+
'Band Aid',
|
| 496 |
+
'banjo',
|
| 497 |
+
'bannister, banister, balustrade, balusters, handrail',
|
| 498 |
+
'barbell',
|
| 499 |
+
'barber chair',
|
| 500 |
+
'barbershop',
|
| 501 |
+
'barn',
|
| 502 |
+
'barometer',
|
| 503 |
+
'barrel, cask',
|
| 504 |
+
'barrow, garden cart, lawn cart, wheelbarrow',
|
| 505 |
+
'baseball',
|
| 506 |
+
'basketball',
|
| 507 |
+
'bassinet',
|
| 508 |
+
'bassoon',
|
| 509 |
+
'bathing cap, swimming cap',
|
| 510 |
+
'bath towel',
|
| 511 |
+
'bathtub, bathing tub, bath, tub',
|
| 512 |
+
'beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon', # noqa: E501
|
| 513 |
+
'beacon, lighthouse, beacon light, pharos',
|
| 514 |
+
'beaker',
|
| 515 |
+
'bearskin, busby, shako',
|
| 516 |
+
'beer bottle',
|
| 517 |
+
'beer glass',
|
| 518 |
+
'bell cote, bell cot',
|
| 519 |
+
'bib',
|
| 520 |
+
'bicycle-built-for-two, tandem bicycle, tandem',
|
| 521 |
+
'bikini, two-piece',
|
| 522 |
+
'binder, ring-binder',
|
| 523 |
+
'binoculars, field glasses, opera glasses',
|
| 524 |
+
'birdhouse',
|
| 525 |
+
'boathouse',
|
| 526 |
+
'bobsled, bobsleigh, bob',
|
| 527 |
+
'bolo tie, bolo, bola tie, bola',
|
| 528 |
+
'bonnet, poke bonnet',
|
| 529 |
+
'bookcase',
|
| 530 |
+
'bookshop, bookstore, bookstall',
|
| 531 |
+
'bottlecap',
|
| 532 |
+
'bow',
|
| 533 |
+
'bow tie, bow-tie, bowtie',
|
| 534 |
+
'brass, memorial tablet, plaque',
|
| 535 |
+
'brassiere, bra, bandeau',
|
| 536 |
+
'breakwater, groin, groyne, mole, bulwark, seawall, jetty',
|
| 537 |
+
'breastplate, aegis, egis',
|
| 538 |
+
'broom',
|
| 539 |
+
'bucket, pail',
|
| 540 |
+
'buckle',
|
| 541 |
+
'bulletproof vest',
|
| 542 |
+
'bullet train, bullet',
|
| 543 |
+
'butcher shop, meat market',
|
| 544 |
+
'cab, hack, taxi, taxicab',
|
| 545 |
+
'caldron, cauldron',
|
| 546 |
+
'candle, taper, wax light',
|
| 547 |
+
'cannon',
|
| 548 |
+
'canoe',
|
| 549 |
+
'can opener, tin opener',
|
| 550 |
+
'cardigan',
|
| 551 |
+
'car mirror',
|
| 552 |
+
'carousel, carrousel, merry-go-round, roundabout, whirligig',
|
| 553 |
+
"carpenter's kit, tool kit",
|
| 554 |
+
'carton',
|
| 555 |
+
'car wheel',
|
| 556 |
+
'cash machine, cash dispenser, automated teller machine, automatic teller machine, automated teller, automatic teller, ATM', # noqa: E501
|
| 557 |
+
'cassette',
|
| 558 |
+
'cassette player',
|
| 559 |
+
'castle',
|
| 560 |
+
'catamaran',
|
| 561 |
+
'CD player',
|
| 562 |
+
'cello, violoncello',
|
| 563 |
+
'cellular telephone, cellular phone, cellphone, cell, mobile phone',
|
| 564 |
+
'chain',
|
| 565 |
+
'chainlink fence',
|
| 566 |
+
'chain mail, ring mail, mail, chain armor, chain armour, ring armor, ring armour', # noqa: E501
|
| 567 |
+
'chain saw, chainsaw',
|
| 568 |
+
'chest',
|
| 569 |
+
'chiffonier, commode',
|
| 570 |
+
'chime, bell, gong',
|
| 571 |
+
'china cabinet, china closet',
|
| 572 |
+
'Christmas stocking',
|
| 573 |
+
'church, church building',
|
| 574 |
+
'cinema, movie theater, movie theatre, movie house, picture palace',
|
| 575 |
+
'cleaver, meat cleaver, chopper',
|
| 576 |
+
'cliff dwelling',
|
| 577 |
+
'cloak',
|
| 578 |
+
'clog, geta, patten, sabot',
|
| 579 |
+
'cocktail shaker',
|
| 580 |
+
'coffee mug',
|
| 581 |
+
'coffeepot',
|
| 582 |
+
'coil, spiral, volute, whorl, helix',
|
| 583 |
+
'combination lock',
|
| 584 |
+
'computer keyboard, keypad',
|
| 585 |
+
'confectionery, confectionary, candy store',
|
| 586 |
+
'container ship, containership, container vessel',
|
| 587 |
+
'convertible',
|
| 588 |
+
'corkscrew, bottle screw',
|
| 589 |
+
'cornet, horn, trumpet, trump',
|
| 590 |
+
'cowboy boot',
|
| 591 |
+
'cowboy hat, ten-gallon hat',
|
| 592 |
+
'cradle',
|
| 593 |
+
'crane',
|
| 594 |
+
'crash helmet',
|
| 595 |
+
'crate',
|
| 596 |
+
'crib, cot',
|
| 597 |
+
'Crock Pot',
|
| 598 |
+
'croquet ball',
|
| 599 |
+
'crutch',
|
| 600 |
+
'cuirass',
|
| 601 |
+
'dam, dike, dyke',
|
| 602 |
+
'desk',
|
| 603 |
+
'desktop computer',
|
| 604 |
+
'dial telephone, dial phone',
|
| 605 |
+
'diaper, nappy, napkin',
|
| 606 |
+
'digital clock',
|
| 607 |
+
'digital watch',
|
| 608 |
+
'dining table, board',
|
| 609 |
+
'dishrag, dishcloth',
|
| 610 |
+
'dishwasher, dish washer, dishwashing machine',
|
| 611 |
+
'disk brake, disc brake',
|
| 612 |
+
'dock, dockage, docking facility',
|
| 613 |
+
'dogsled, dog sled, dog sleigh',
|
| 614 |
+
'dome',
|
| 615 |
+
'doormat, welcome mat',
|
| 616 |
+
'drilling platform, offshore rig',
|
| 617 |
+
'drum, membranophone, tympan',
|
| 618 |
+
'drumstick',
|
| 619 |
+
'dumbbell',
|
| 620 |
+
'Dutch oven',
|
| 621 |
+
'electric fan, blower',
|
| 622 |
+
'electric guitar',
|
| 623 |
+
'electric locomotive',
|
| 624 |
+
'entertainment center',
|
| 625 |
+
'envelope',
|
| 626 |
+
'espresso maker',
|
| 627 |
+
'face powder',
|
| 628 |
+
'feather boa, boa',
|
| 629 |
+
'file, file cabinet, filing cabinet',
|
| 630 |
+
'fireboat',
|
| 631 |
+
'fire engine, fire truck',
|
| 632 |
+
'fire screen, fireguard',
|
| 633 |
+
'flagpole, flagstaff',
|
| 634 |
+
'flute, transverse flute',
|
| 635 |
+
'folding chair',
|
| 636 |
+
'football helmet',
|
| 637 |
+
'forklift',
|
| 638 |
+
'fountain',
|
| 639 |
+
'fountain pen',
|
| 640 |
+
'four-poster',
|
| 641 |
+
'freight car',
|
| 642 |
+
'French horn, horn',
|
| 643 |
+
'frying pan, frypan, skillet',
|
| 644 |
+
'fur coat',
|
| 645 |
+
'garbage truck, dustcart',
|
| 646 |
+
'gasmask, respirator, gas helmet',
|
| 647 |
+
'gas pump, gasoline pump, petrol pump, island dispenser',
|
| 648 |
+
'goblet',
|
| 649 |
+
'go-kart',
|
| 650 |
+
'golf ball',
|
| 651 |
+
'golfcart, golf cart',
|
| 652 |
+
'gondola',
|
| 653 |
+
'gong, tam-tam',
|
| 654 |
+
'gown',
|
| 655 |
+
'grand piano, grand',
|
| 656 |
+
'greenhouse, nursery, glasshouse',
|
| 657 |
+
'grille, radiator grille',
|
| 658 |
+
'grocery store, grocery, food market, market',
|
| 659 |
+
'guillotine',
|
| 660 |
+
'hair slide',
|
| 661 |
+
'hair spray',
|
| 662 |
+
'half track',
|
| 663 |
+
'hammer',
|
| 664 |
+
'hamper',
|
| 665 |
+
'hand blower, blow dryer, blow drier, hair dryer, hair drier',
|
| 666 |
+
'hand-held computer, hand-held microcomputer',
|
| 667 |
+
'handkerchief, hankie, hanky, hankey',
|
| 668 |
+
'hard disc, hard disk, fixed disk',
|
| 669 |
+
'harmonica, mouth organ, harp, mouth harp',
|
| 670 |
+
'harp',
|
| 671 |
+
'harvester, reaper',
|
| 672 |
+
'hatchet',
|
| 673 |
+
'holster',
|
| 674 |
+
'home theater, home theatre',
|
| 675 |
+
'honeycomb',
|
| 676 |
+
'hook, claw',
|
| 677 |
+
'hoopskirt, crinoline',
|
| 678 |
+
'horizontal bar, high bar',
|
| 679 |
+
'horse cart, horse-cart',
|
| 680 |
+
'hourglass',
|
| 681 |
+
'iPod',
|
| 682 |
+
'iron, smoothing iron',
|
| 683 |
+
"jack-o'-lantern",
|
| 684 |
+
'jean, blue jean, denim',
|
| 685 |
+
'jeep, landrover',
|
| 686 |
+
'jersey, T-shirt, tee shirt',
|
| 687 |
+
'jigsaw puzzle',
|
| 688 |
+
'jinrikisha, ricksha, rickshaw',
|
| 689 |
+
'joystick',
|
| 690 |
+
'kimono',
|
| 691 |
+
'knee pad',
|
| 692 |
+
'knot',
|
| 693 |
+
'lab coat, laboratory coat',
|
| 694 |
+
'ladle',
|
| 695 |
+
'lampshade, lamp shade',
|
| 696 |
+
'laptop, laptop computer',
|
| 697 |
+
'lawn mower, mower',
|
| 698 |
+
'lens cap, lens cover',
|
| 699 |
+
'letter opener, paper knife, paperknife',
|
| 700 |
+
'library',
|
| 701 |
+
'lifeboat',
|
| 702 |
+
'lighter, light, igniter, ignitor',
|
| 703 |
+
'limousine, limo',
|
| 704 |
+
'liner, ocean liner',
|
| 705 |
+
'lipstick, lip rouge',
|
| 706 |
+
'Loafer',
|
| 707 |
+
'lotion',
|
| 708 |
+
'loudspeaker, speaker, speaker unit, loudspeaker system, speaker system', # noqa: E501
|
| 709 |
+
"loupe, jeweler's loupe",
|
| 710 |
+
'lumbermill, sawmill',
|
| 711 |
+
'magnetic compass',
|
| 712 |
+
'mailbag, postbag',
|
| 713 |
+
'mailbox, letter box',
|
| 714 |
+
'maillot',
|
| 715 |
+
'maillot, tank suit',
|
| 716 |
+
'manhole cover',
|
| 717 |
+
'maraca',
|
| 718 |
+
'marimba, xylophone',
|
| 719 |
+
'mask',
|
| 720 |
+
'matchstick',
|
| 721 |
+
'maypole',
|
| 722 |
+
'maze, labyrinth',
|
| 723 |
+
'measuring cup',
|
| 724 |
+
'medicine chest, medicine cabinet',
|
| 725 |
+
'megalith, megalithic structure',
|
| 726 |
+
'microphone, mike',
|
| 727 |
+
'microwave, microwave oven',
|
| 728 |
+
'military uniform',
|
| 729 |
+
'milk can',
|
| 730 |
+
'minibus',
|
| 731 |
+
'miniskirt, mini',
|
| 732 |
+
'minivan',
|
| 733 |
+
'missile',
|
| 734 |
+
'mitten',
|
| 735 |
+
'mixing bowl',
|
| 736 |
+
'mobile home, manufactured home',
|
| 737 |
+
'Model T',
|
| 738 |
+
'modem',
|
| 739 |
+
'monastery',
|
| 740 |
+
'monitor',
|
| 741 |
+
'moped',
|
| 742 |
+
'mortar',
|
| 743 |
+
'mortarboard',
|
| 744 |
+
'mosque',
|
| 745 |
+
'mosquito net',
|
| 746 |
+
'motor scooter, scooter',
|
| 747 |
+
'mountain bike, all-terrain bike, off-roader',
|
| 748 |
+
'mountain tent',
|
| 749 |
+
'mouse, computer mouse',
|
| 750 |
+
'mousetrap',
|
| 751 |
+
'moving van',
|
| 752 |
+
'muzzle',
|
| 753 |
+
'nail',
|
| 754 |
+
'neck brace',
|
| 755 |
+
'necklace',
|
| 756 |
+
'nipple',
|
| 757 |
+
'notebook, notebook computer',
|
| 758 |
+
'obelisk',
|
| 759 |
+
'oboe, hautboy, hautbois',
|
| 760 |
+
'ocarina, sweet potato',
|
| 761 |
+
'odometer, hodometer, mileometer, milometer',
|
| 762 |
+
'oil filter',
|
| 763 |
+
'organ, pipe organ',
|
| 764 |
+
'oscilloscope, scope, cathode-ray oscilloscope, CRO',
|
| 765 |
+
'overskirt',
|
| 766 |
+
'oxcart',
|
| 767 |
+
'oxygen mask',
|
| 768 |
+
'packet',
|
| 769 |
+
'paddle, boat paddle',
|
| 770 |
+
'paddlewheel, paddle wheel',
|
| 771 |
+
'padlock',
|
| 772 |
+
'paintbrush',
|
| 773 |
+
"pajama, pyjama, pj's, jammies",
|
| 774 |
+
'palace',
|
| 775 |
+
'panpipe, pandean pipe, syrinx',
|
| 776 |
+
'paper towel',
|
| 777 |
+
'parachute, chute',
|
| 778 |
+
'parallel bars, bars',
|
| 779 |
+
'park bench',
|
| 780 |
+
'parking meter',
|
| 781 |
+
'passenger car, coach, carriage',
|
| 782 |
+
'patio, terrace',
|
| 783 |
+
'pay-phone, pay-station',
|
| 784 |
+
'pedestal, plinth, footstall',
|
| 785 |
+
'pencil box, pencil case',
|
| 786 |
+
'pencil sharpener',
|
| 787 |
+
'perfume, essence',
|
| 788 |
+
'Petri dish',
|
| 789 |
+
'photocopier',
|
| 790 |
+
'pick, plectrum, plectron',
|
| 791 |
+
'pickelhaube',
|
| 792 |
+
'picket fence, paling',
|
| 793 |
+
'pickup, pickup truck',
|
| 794 |
+
'pier',
|
| 795 |
+
'piggy bank, penny bank',
|
| 796 |
+
'pill bottle',
|
| 797 |
+
'pillow',
|
| 798 |
+
'ping-pong ball',
|
| 799 |
+
'pinwheel',
|
| 800 |
+
'pirate, pirate ship',
|
| 801 |
+
'pitcher, ewer',
|
| 802 |
+
"plane, carpenter's plane, woodworking plane",
|
| 803 |
+
'planetarium',
|
| 804 |
+
'plastic bag',
|
| 805 |
+
'plate rack',
|
| 806 |
+
'plow, plough',
|
| 807 |
+
"plunger, plumber's helper",
|
| 808 |
+
'Polaroid camera, Polaroid Land camera',
|
| 809 |
+
'pole',
|
| 810 |
+
'police van, police wagon, paddy wagon, patrol wagon, wagon, black Maria', # noqa: E501
|
| 811 |
+
'poncho',
|
| 812 |
+
'pool table, billiard table, snooker table',
|
| 813 |
+
'pop bottle, soda bottle',
|
| 814 |
+
'pot, flowerpot',
|
| 815 |
+
"potter's wheel",
|
| 816 |
+
'power drill',
|
| 817 |
+
'prayer rug, prayer mat',
|
| 818 |
+
'printer',
|
| 819 |
+
'prison, prison house',
|
| 820 |
+
'projectile, missile',
|
| 821 |
+
'projector',
|
| 822 |
+
'puck, hockey puck',
|
| 823 |
+
'punching bag, punch bag, punching ball, punchball',
|
| 824 |
+
'purse',
|
| 825 |
+
'quill, quill pen',
|
| 826 |
+
'quilt, comforter, comfort, puff',
|
| 827 |
+
'racer, race car, racing car',
|
| 828 |
+
'racket, racquet',
|
| 829 |
+
'radiator',
|
| 830 |
+
'radio, wireless',
|
| 831 |
+
'radio telescope, radio reflector',
|
| 832 |
+
'rain barrel',
|
| 833 |
+
'recreational vehicle, RV, R.V.',
|
| 834 |
+
'reel',
|
| 835 |
+
'reflex camera',
|
| 836 |
+
'refrigerator, icebox',
|
| 837 |
+
'remote control, remote',
|
| 838 |
+
'restaurant, eating house, eating place, eatery',
|
| 839 |
+
'revolver, six-gun, six-shooter',
|
| 840 |
+
'rifle',
|
| 841 |
+
'rocking chair, rocker',
|
| 842 |
+
'rotisserie',
|
| 843 |
+
'rubber eraser, rubber, pencil eraser',
|
| 844 |
+
'rugby ball',
|
| 845 |
+
'rule, ruler',
|
| 846 |
+
'running shoe',
|
| 847 |
+
'safe',
|
| 848 |
+
'safety pin',
|
| 849 |
+
'saltshaker, salt shaker',
|
| 850 |
+
'sandal',
|
| 851 |
+
'sarong',
|
| 852 |
+
'sax, saxophone',
|
| 853 |
+
'scabbard',
|
| 854 |
+
'scale, weighing machine',
|
| 855 |
+
'school bus',
|
| 856 |
+
'schooner',
|
| 857 |
+
'scoreboard',
|
| 858 |
+
'screen, CRT screen',
|
| 859 |
+
'screw',
|
| 860 |
+
'screwdriver',
|
| 861 |
+
'seat belt, seatbelt',
|
| 862 |
+
'sewing machine',
|
| 863 |
+
'shield, buckler',
|
| 864 |
+
'shoe shop, shoe-shop, shoe store',
|
| 865 |
+
'shoji',
|
| 866 |
+
'shopping basket',
|
| 867 |
+
'shopping cart',
|
| 868 |
+
'shovel',
|
| 869 |
+
'shower cap',
|
| 870 |
+
'shower curtain',
|
| 871 |
+
'ski',
|
| 872 |
+
'ski mask',
|
| 873 |
+
'sleeping bag',
|
| 874 |
+
'slide rule, slipstick',
|
| 875 |
+
'sliding door',
|
| 876 |
+
'slot, one-armed bandit',
|
| 877 |
+
'snorkel',
|
| 878 |
+
'snowmobile',
|
| 879 |
+
'snowplow, snowplough',
|
| 880 |
+
'soap dispenser',
|
| 881 |
+
'soccer ball',
|
| 882 |
+
'sock',
|
| 883 |
+
'solar dish, solar collector, solar furnace',
|
| 884 |
+
'sombrero',
|
| 885 |
+
'soup bowl',
|
| 886 |
+
'space bar',
|
| 887 |
+
'space heater',
|
| 888 |
+
'space shuttle',
|
| 889 |
+
'spatula',
|
| 890 |
+
'speedboat',
|
| 891 |
+
"spider web, spider's web",
|
| 892 |
+
'spindle',
|
| 893 |
+
'sports car, sport car',
|
| 894 |
+
'spotlight, spot',
|
| 895 |
+
'stage',
|
| 896 |
+
'steam locomotive',
|
| 897 |
+
'steel arch bridge',
|
| 898 |
+
'steel drum',
|
| 899 |
+
'stethoscope',
|
| 900 |
+
'stole',
|
| 901 |
+
'stone wall',
|
| 902 |
+
'stopwatch, stop watch',
|
| 903 |
+
'stove',
|
| 904 |
+
'strainer',
|
| 905 |
+
'streetcar, tram, tramcar, trolley, trolley car',
|
| 906 |
+
'stretcher',
|
| 907 |
+
'studio couch, day bed',
|
| 908 |
+
'stupa, tope',
|
| 909 |
+
'submarine, pigboat, sub, U-boat',
|
| 910 |
+
'suit, suit of clothes',
|
| 911 |
+
'sundial',
|
| 912 |
+
'sunglass',
|
| 913 |
+
'sunglasses, dark glasses, shades',
|
| 914 |
+
'sunscreen, sunblock, sun blocker',
|
| 915 |
+
'suspension bridge',
|
| 916 |
+
'swab, swob, mop',
|
| 917 |
+
'sweatshirt',
|
| 918 |
+
'swimming trunks, bathing trunks',
|
| 919 |
+
'swing',
|
| 920 |
+
'switch, electric switch, electrical switch',
|
| 921 |
+
'syringe',
|
| 922 |
+
'table lamp',
|
| 923 |
+
'tank, army tank, armored combat vehicle, armoured combat vehicle',
|
| 924 |
+
'tape player',
|
| 925 |
+
'teapot',
|
| 926 |
+
'teddy, teddy bear',
|
| 927 |
+
'television, television system',
|
| 928 |
+
'tennis ball',
|
| 929 |
+
'thatch, thatched roof',
|
| 930 |
+
'theater curtain, theatre curtain',
|
| 931 |
+
'thimble',
|
| 932 |
+
'thresher, thrasher, threshing machine',
|
| 933 |
+
'throne',
|
| 934 |
+
'tile roof',
|
| 935 |
+
'toaster',
|
| 936 |
+
'tobacco shop, tobacconist shop, tobacconist',
|
| 937 |
+
'toilet seat',
|
| 938 |
+
'torch',
|
| 939 |
+
'totem pole',
|
| 940 |
+
'tow truck, tow car, wrecker',
|
| 941 |
+
'toyshop',
|
| 942 |
+
'tractor',
|
| 943 |
+
'trailer truck, tractor trailer, trucking rig, rig, articulated lorry, semi', # noqa: E501
|
| 944 |
+
'tray',
|
| 945 |
+
'trench coat',
|
| 946 |
+
'tricycle, trike, velocipede',
|
| 947 |
+
'trimaran',
|
| 948 |
+
'tripod',
|
| 949 |
+
'triumphal arch',
|
| 950 |
+
'trolleybus, trolley coach, trackless trolley',
|
| 951 |
+
'trombone',
|
| 952 |
+
'tub, vat',
|
| 953 |
+
'turnstile',
|
| 954 |
+
'typewriter keyboard',
|
| 955 |
+
'umbrella',
|
| 956 |
+
'unicycle, monocycle',
|
| 957 |
+
'upright, upright piano',
|
| 958 |
+
'vacuum, vacuum cleaner',
|
| 959 |
+
'vase',
|
| 960 |
+
'vault',
|
| 961 |
+
'velvet',
|
| 962 |
+
'vending machine',
|
| 963 |
+
'vestment',
|
| 964 |
+
'viaduct',
|
| 965 |
+
'violin, fiddle',
|
| 966 |
+
'volleyball',
|
| 967 |
+
'waffle iron',
|
| 968 |
+
'wall clock',
|
| 969 |
+
'wallet, billfold, notecase, pocketbook',
|
| 970 |
+
'wardrobe, closet, press',
|
| 971 |
+
'warplane, military plane',
|
| 972 |
+
'washbasin, handbasin, washbowl, lavabo, wash-hand basin',
|
| 973 |
+
'washer, automatic washer, washing machine',
|
| 974 |
+
'water bottle',
|
| 975 |
+
'water jug',
|
| 976 |
+
'water tower',
|
| 977 |
+
'whiskey jug',
|
| 978 |
+
'whistle',
|
| 979 |
+
'wig',
|
| 980 |
+
'window screen',
|
| 981 |
+
'window shade',
|
| 982 |
+
'Windsor tie',
|
| 983 |
+
'wine bottle',
|
| 984 |
+
'wing',
|
| 985 |
+
'wok',
|
| 986 |
+
'wooden spoon',
|
| 987 |
+
'wool, woolen, woollen',
|
| 988 |
+
'worm fence, snake fence, snake-rail fence, Virginia fence',
|
| 989 |
+
'wreck',
|
| 990 |
+
'yawl',
|
| 991 |
+
'yurt',
|
| 992 |
+
'web site, website, internet site, site',
|
| 993 |
+
'comic book',
|
| 994 |
+
'crossword puzzle, crossword',
|
| 995 |
+
'street sign',
|
| 996 |
+
'traffic light, traffic signal, stoplight',
|
| 997 |
+
'book jacket, dust cover, dust jacket, dust wrapper',
|
| 998 |
+
'menu',
|
| 999 |
+
'plate',
|
| 1000 |
+
'guacamole',
|
| 1001 |
+
'consomme',
|
| 1002 |
+
'hot pot, hotpot',
|
| 1003 |
+
'trifle',
|
| 1004 |
+
'ice cream, icecream',
|
| 1005 |
+
'ice lolly, lolly, lollipop, popsicle',
|
| 1006 |
+
'French loaf',
|
| 1007 |
+
'bagel, beigel',
|
| 1008 |
+
'pretzel',
|
| 1009 |
+
'cheeseburger',
|
| 1010 |
+
'hotdog, hot dog, red hot',
|
| 1011 |
+
'mashed potato',
|
| 1012 |
+
'head cabbage',
|
| 1013 |
+
'broccoli',
|
| 1014 |
+
'cauliflower',
|
| 1015 |
+
'zucchini, courgette',
|
| 1016 |
+
'spaghetti squash',
|
| 1017 |
+
'acorn squash',
|
| 1018 |
+
'butternut squash',
|
| 1019 |
+
'cucumber, cuke',
|
| 1020 |
+
'artichoke, globe artichoke',
|
| 1021 |
+
'bell pepper',
|
| 1022 |
+
'cardoon',
|
| 1023 |
+
'mushroom',
|
| 1024 |
+
'Granny Smith',
|
| 1025 |
+
'strawberry',
|
| 1026 |
+
'orange',
|
| 1027 |
+
'lemon',
|
| 1028 |
+
'fig',
|
| 1029 |
+
'pineapple, ananas',
|
| 1030 |
+
'banana',
|
| 1031 |
+
'jackfruit, jak, jack',
|
| 1032 |
+
'custard apple',
|
| 1033 |
+
'pomegranate',
|
| 1034 |
+
'hay',
|
| 1035 |
+
'carbonara',
|
| 1036 |
+
'chocolate sauce, chocolate syrup',
|
| 1037 |
+
'dough',
|
| 1038 |
+
'meat loaf, meatloaf',
|
| 1039 |
+
'pizza, pizza pie',
|
| 1040 |
+
'potpie',
|
| 1041 |
+
'burrito',
|
| 1042 |
+
'red wine',
|
| 1043 |
+
'espresso',
|
| 1044 |
+
'cup',
|
| 1045 |
+
'eggnog',
|
| 1046 |
+
'alp',
|
| 1047 |
+
'bubble',
|
| 1048 |
+
'cliff, drop, drop-off',
|
| 1049 |
+
'coral reef',
|
| 1050 |
+
'geyser',
|
| 1051 |
+
'lakeside, lakeshore',
|
| 1052 |
+
'promontory, headland, head, foreland',
|
| 1053 |
+
'sandbar, sand bar',
|
| 1054 |
+
'seashore, coast, seacoast, sea-coast',
|
| 1055 |
+
'valley, vale',
|
| 1056 |
+
'volcano',
|
| 1057 |
+
'ballplayer, baseball player',
|
| 1058 |
+
'groom, bridegroom',
|
| 1059 |
+
'scuba diver',
|
| 1060 |
+
'rapeseed',
|
| 1061 |
+
'daisy',
|
| 1062 |
+
"yellow lady's slipper, yellow lady-slipper, Cypripedium calceolus, Cypripedium parviflorum", # noqa: E501
|
| 1063 |
+
'corn',
|
| 1064 |
+
'acorn',
|
| 1065 |
+
'hip, rose hip, rosehip',
|
| 1066 |
+
'buckeye, horse chestnut, conker',
|
| 1067 |
+
'coral fungus',
|
| 1068 |
+
'agaric',
|
| 1069 |
+
'gyromitra',
|
| 1070 |
+
'stinkhorn, carrion fungus',
|
| 1071 |
+
'earthstar',
|
| 1072 |
+
'hen-of-the-woods, hen of the woods, Polyporus frondosus, Grifola frondosa', # noqa: E501
|
| 1073 |
+
'bolete',
|
| 1074 |
+
'ear, spike, capitulum',
|
| 1075 |
+
'toilet tissue, toilet paper, bathroom tissue'
|
| 1076 |
+
]
|
| 1077 |
+
|
| 1078 |
+
def load_annotations(self):
|
| 1079 |
+
if self.ann_file is None:
|
| 1080 |
+
folder_to_idx = find_folders(self.data_prefix)
|
| 1081 |
+
samples = get_samples(
|
| 1082 |
+
self.data_prefix,
|
| 1083 |
+
folder_to_idx,
|
| 1084 |
+
extensions=self.IMG_EXTENSIONS)
|
| 1085 |
+
if len(samples) == 0:
|
| 1086 |
+
raise (RuntimeError('Found 0 files in subfolders of: '
|
| 1087 |
+
f'{self.data_prefix}. '
|
| 1088 |
+
'Supported extensions are: '
|
| 1089 |
+
f'{",".join(self.IMG_EXTENSIONS)}'))
|
| 1090 |
+
|
| 1091 |
+
self.folder_to_idx = folder_to_idx
|
| 1092 |
+
elif isinstance(self.ann_file, str):
|
| 1093 |
+
with open(self.ann_file) as f:
|
| 1094 |
+
samples = [x.strip().split(' ') for x in f.readlines()]
|
| 1095 |
+
else:
|
| 1096 |
+
raise TypeError('ann_file must be a str or None')
|
| 1097 |
+
self.samples = samples
|
| 1098 |
+
|
| 1099 |
+
data_infos = []
|
| 1100 |
+
for filename, gt_label in self.samples:
|
| 1101 |
+
info = {'img_prefix': self.data_prefix}
|
| 1102 |
+
info['img_info'] = {'filename': filename}
|
| 1103 |
+
info['gt_label'] = np.array(gt_label, dtype=np.int64)
|
| 1104 |
+
data_infos.append(info)
|
| 1105 |
+
return data_infos
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/mnist.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import codecs
|
| 2 |
+
import os
|
| 3 |
+
import os.path as osp
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
import torch
|
| 7 |
+
|
| 8 |
+
from .base_dataset import BaseDataset
|
| 9 |
+
from .builder import DATASETS
|
| 10 |
+
from .utils import download_and_extract_archive, rm_suffix
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
@DATASETS.register_module()
|
| 14 |
+
class MNIST(BaseDataset):
|
| 15 |
+
"""`MNIST <http://yann.lecun.com/exdb/mnist/>`_ Dataset.
|
| 16 |
+
|
| 17 |
+
This implementation is modified from
|
| 18 |
+
https://github.com/pytorch/vision/blob/master/torchvision/datasets/mnist.py # noqa: E501
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
resource_prefix = 'http://yann.lecun.com/exdb/mnist/'
|
| 22 |
+
resources = {
|
| 23 |
+
'train_image_file':
|
| 24 |
+
('train-images-idx3-ubyte.gz', 'f68b3c2dcbeaaa9fbdd348bbdeb94873'),
|
| 25 |
+
'train_label_file':
|
| 26 |
+
('train-labels-idx1-ubyte.gz', 'd53e105ee54ea40749a09fcbcd1e9432'),
|
| 27 |
+
'test_image_file':
|
| 28 |
+
('t10k-images-idx3-ubyte.gz', '9fb629c4189551a2d022fa330f9573f3'),
|
| 29 |
+
'test_label_file':
|
| 30 |
+
('t10k-labels-idx1-ubyte.gz', 'ec29112dd5afa0611ce80d1b7f02629c')
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
CLASSES = [
|
| 34 |
+
'0 - zero', '1 - one', '2 - two', '3 - three', '4 - four', '5 - five',
|
| 35 |
+
'6 - six', '7 - seven', '8 - eight', '9 - nine'
|
| 36 |
+
]
|
| 37 |
+
|
| 38 |
+
def load_annotations(self):
|
| 39 |
+
train_image_file = osp.join(
|
| 40 |
+
self.data_prefix, rm_suffix(self.resources['train_image_file'][0]))
|
| 41 |
+
train_label_file = osp.join(
|
| 42 |
+
self.data_prefix, rm_suffix(self.resources['train_label_file'][0]))
|
| 43 |
+
test_image_file = osp.join(
|
| 44 |
+
self.data_prefix, rm_suffix(self.resources['test_image_file'][0]))
|
| 45 |
+
test_label_file = osp.join(
|
| 46 |
+
self.data_prefix, rm_suffix(self.resources['test_label_file'][0]))
|
| 47 |
+
|
| 48 |
+
if not osp.exists(train_image_file) or not osp.exists(
|
| 49 |
+
train_label_file) or not osp.exists(
|
| 50 |
+
test_image_file) or not osp.exists(test_label_file):
|
| 51 |
+
self.download()
|
| 52 |
+
|
| 53 |
+
train_set = (read_image_file(train_image_file),
|
| 54 |
+
read_label_file(train_label_file))
|
| 55 |
+
test_set = (read_image_file(test_image_file),
|
| 56 |
+
read_label_file(test_label_file))
|
| 57 |
+
|
| 58 |
+
if not self.test_mode:
|
| 59 |
+
imgs, gt_labels = train_set
|
| 60 |
+
else:
|
| 61 |
+
imgs, gt_labels = test_set
|
| 62 |
+
|
| 63 |
+
data_infos = []
|
| 64 |
+
for img, gt_label in zip(imgs, gt_labels):
|
| 65 |
+
gt_label = np.array(gt_label, dtype=np.int64)
|
| 66 |
+
info = {'img': img.numpy(), 'gt_label': gt_label}
|
| 67 |
+
data_infos.append(info)
|
| 68 |
+
return data_infos
|
| 69 |
+
|
| 70 |
+
def download(self):
|
| 71 |
+
os.makedirs(self.data_prefix, exist_ok=True)
|
| 72 |
+
|
| 73 |
+
# download files
|
| 74 |
+
for url, md5 in self.resources.values():
|
| 75 |
+
url = osp.join(self.resource_prefix, url)
|
| 76 |
+
filename = url.rpartition('/')[2]
|
| 77 |
+
download_and_extract_archive(
|
| 78 |
+
url,
|
| 79 |
+
download_root=self.data_prefix,
|
| 80 |
+
filename=filename,
|
| 81 |
+
md5=md5)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
@DATASETS.register_module()
|
| 85 |
+
class FashionMNIST(MNIST):
|
| 86 |
+
"""`Fashion-MNIST <https://github.com/zalandoresearch/fashion-mnist>`_
|
| 87 |
+
Dataset.
|
| 88 |
+
"""
|
| 89 |
+
|
| 90 |
+
resource_prefix = 'http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/' # noqa: E501
|
| 91 |
+
resources = {
|
| 92 |
+
'train_image_file':
|
| 93 |
+
('train-images-idx3-ubyte.gz', '8d4fb7e6c68d591d4c3dfef9ec88bf0d'),
|
| 94 |
+
'train_label_file':
|
| 95 |
+
('train-labels-idx1-ubyte.gz', '25c81989df183df01b3e8a0aad5dffbe'),
|
| 96 |
+
'test_image_file':
|
| 97 |
+
('t10k-images-idx3-ubyte.gz', 'bef4ecab320f06d8554ea6380940ec79'),
|
| 98 |
+
'test_label_file':
|
| 99 |
+
('t10k-labels-idx1-ubyte.gz', 'bb300cfdad3c16e7a12a480ee83cd310')
|
| 100 |
+
}
|
| 101 |
+
CLASSES = [
|
| 102 |
+
'T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal',
|
| 103 |
+
'Shirt', 'Sneaker', 'Bag', 'Ankle boot'
|
| 104 |
+
]
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def get_int(b):
|
| 108 |
+
return int(codecs.encode(b, 'hex'), 16)
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def open_maybe_compressed_file(path):
|
| 112 |
+
"""Return a file object that possibly decompresses 'path' on the fly.
|
| 113 |
+
Decompression occurs when argument `path` is a string
|
| 114 |
+
and ends with '.gz' or '.xz'.
|
| 115 |
+
"""
|
| 116 |
+
if not isinstance(path, str):
|
| 117 |
+
return path
|
| 118 |
+
if path.endswith('.gz'):
|
| 119 |
+
import gzip
|
| 120 |
+
return gzip.open(path, 'rb')
|
| 121 |
+
if path.endswith('.xz'):
|
| 122 |
+
import lzma
|
| 123 |
+
return lzma.open(path, 'rb')
|
| 124 |
+
return open(path, 'rb')
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def read_sn3_pascalvincent_tensor(path, strict=True):
|
| 128 |
+
"""Read a SN3 file in "Pascal Vincent" format
|
| 129 |
+
(Lush file 'libidx/idx-io.lsh').
|
| 130 |
+
Argument may be a filename, compressed filename, or file object.
|
| 131 |
+
"""
|
| 132 |
+
# typemap
|
| 133 |
+
if not hasattr(read_sn3_pascalvincent_tensor, 'typemap'):
|
| 134 |
+
read_sn3_pascalvincent_tensor.typemap = {
|
| 135 |
+
8: (torch.uint8, np.uint8, np.uint8),
|
| 136 |
+
9: (torch.int8, np.int8, np.int8),
|
| 137 |
+
11: (torch.int16, np.dtype('>i2'), 'i2'),
|
| 138 |
+
12: (torch.int32, np.dtype('>i4'), 'i4'),
|
| 139 |
+
13: (torch.float32, np.dtype('>f4'), 'f4'),
|
| 140 |
+
14: (torch.float64, np.dtype('>f8'), 'f8')
|
| 141 |
+
}
|
| 142 |
+
# read
|
| 143 |
+
with open_maybe_compressed_file(path) as f:
|
| 144 |
+
data = f.read()
|
| 145 |
+
# parse
|
| 146 |
+
magic = get_int(data[0:4])
|
| 147 |
+
nd = magic % 256
|
| 148 |
+
ty = magic // 256
|
| 149 |
+
assert nd >= 1 and nd <= 3
|
| 150 |
+
assert ty >= 8 and ty <= 14
|
| 151 |
+
m = read_sn3_pascalvincent_tensor.typemap[ty]
|
| 152 |
+
s = [get_int(data[4 * (i + 1):4 * (i + 2)]) for i in range(nd)]
|
| 153 |
+
parsed = np.frombuffer(data, dtype=m[1], offset=(4 * (nd + 1)))
|
| 154 |
+
assert parsed.shape[0] == np.prod(s) or not strict
|
| 155 |
+
return torch.from_numpy(parsed.astype(m[2], copy=False)).view(*s)
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
def read_label_file(path):
|
| 159 |
+
with open(path, 'rb') as f:
|
| 160 |
+
x = read_sn3_pascalvincent_tensor(f, strict=False)
|
| 161 |
+
assert (x.dtype == torch.uint8)
|
| 162 |
+
assert (x.ndimension() == 1)
|
| 163 |
+
return x.long()
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
def read_image_file(path):
|
| 167 |
+
with open(path, 'rb') as f:
|
| 168 |
+
x = read_sn3_pascalvincent_tensor(f, strict=False)
|
| 169 |
+
assert (x.dtype == torch.uint8)
|
| 170 |
+
assert (x.ndimension() == 3)
|
| 171 |
+
return x
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/pipelines/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .compose import Compose
|
| 2 |
+
from .formating import (Collect, ImageToTensor, ToNumpy, ToPIL, ToTensor,
|
| 3 |
+
Transpose, to_tensor)
|
| 4 |
+
from .loading import LoadImageFromFile
|
| 5 |
+
from .transforms import (RandomAppliedTrans, CenterCrop, RandomCrop, RandomFlip, RandomGrayscale,
|
| 6 |
+
RandomResizedCrop, Resize, RandomRotate, ColorJitter)
|
| 7 |
+
from .test_time_aug import MultiScaleFlipAug
|
| 8 |
+
|
| 9 |
+
__all__ = [
|
| 10 |
+
'Compose', 'to_tensor', 'ToTensor', 'ImageToTensor', 'ToPIL', 'ToNumpy',
|
| 11 |
+
'Transpose', 'Collect', 'LoadImageFromFile', 'Resize', 'CenterCrop',
|
| 12 |
+
'RandomFlip', 'Normalize', 'RandomCrop', 'RandomResizedCrop',
|
| 13 |
+
'RandomGrayscale', 'RandomAppliedTrans', 'RandomRotate', 'ColorJitter',
|
| 14 |
+
'MultiScaleFlipAug'
|
| 15 |
+
]
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/pipelines/compose.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections.abc import Sequence
|
| 2 |
+
|
| 3 |
+
from mmcv.utils import build_from_cfg
|
| 4 |
+
|
| 5 |
+
from ..builder import PIPELINES
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
@PIPELINES.register_module()
|
| 9 |
+
class Compose(object):
|
| 10 |
+
"""Compose a data pipeline with a sequence of transforms.
|
| 11 |
+
|
| 12 |
+
Args:
|
| 13 |
+
transforms (list[dict | callable]):
|
| 14 |
+
Either config dicts of transforms or transform objects.
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
def __init__(self, transforms):
|
| 18 |
+
assert isinstance(transforms, Sequence)
|
| 19 |
+
self.transforms = []
|
| 20 |
+
for transform in transforms:
|
| 21 |
+
if isinstance(transform, dict):
|
| 22 |
+
transform = build_from_cfg(transform, PIPELINES)
|
| 23 |
+
self.transforms.append(transform)
|
| 24 |
+
elif callable(transform):
|
| 25 |
+
self.transforms.append(transform)
|
| 26 |
+
else:
|
| 27 |
+
raise TypeError('transform must be callable or a dict, but got'
|
| 28 |
+
f' {type(transform)}')
|
| 29 |
+
|
| 30 |
+
def __call__(self, data):
|
| 31 |
+
for t in self.transforms:
|
| 32 |
+
data = t(data)
|
| 33 |
+
if data is None:
|
| 34 |
+
return None
|
| 35 |
+
return data
|
| 36 |
+
|
| 37 |
+
def __repr__(self):
|
| 38 |
+
format_string = self.__class__.__name__ + '('
|
| 39 |
+
for t in self.transforms:
|
| 40 |
+
format_string += f'\n {t}'
|
| 41 |
+
format_string += '\n)'
|
| 42 |
+
return format_string
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/pipelines/formating.py
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections.abc import Sequence
|
| 2 |
+
|
| 3 |
+
import mmcv
|
| 4 |
+
import numpy as np
|
| 5 |
+
import torch
|
| 6 |
+
from PIL import Image
|
| 7 |
+
|
| 8 |
+
from ..builder import PIPELINES
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def to_tensor(data):
|
| 12 |
+
"""Convert objects of various python types to :obj:`torch.Tensor`.
|
| 13 |
+
|
| 14 |
+
Supported types are: :class:`numpy.ndarray`, :class:`torch.Tensor`,
|
| 15 |
+
:class:`Sequence`, :class:`int` and :class:`float`.
|
| 16 |
+
"""
|
| 17 |
+
if isinstance(data, torch.Tensor):
|
| 18 |
+
return data
|
| 19 |
+
elif isinstance(data, np.ndarray):
|
| 20 |
+
return torch.from_numpy(data)
|
| 21 |
+
elif isinstance(data, Sequence) and not mmcv.is_str(data):
|
| 22 |
+
return torch.tensor(data)
|
| 23 |
+
elif isinstance(data, int):
|
| 24 |
+
return torch.LongTensor([data])
|
| 25 |
+
elif isinstance(data, float):
|
| 26 |
+
return torch.FloatTensor([data])
|
| 27 |
+
else:
|
| 28 |
+
raise TypeError(
|
| 29 |
+
f'Type {type(data)} cannot be converted to tensor.'
|
| 30 |
+
'Supported types are: `numpy.ndarray`, `torch.Tensor`, '
|
| 31 |
+
'`Sequence`, `int` and `float`')
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
@PIPELINES.register_module()
|
| 35 |
+
class ToTensor(object):
|
| 36 |
+
|
| 37 |
+
def __init__(self, keys):
|
| 38 |
+
self.keys = keys
|
| 39 |
+
|
| 40 |
+
def __call__(self, results):
|
| 41 |
+
for key in self.keys:
|
| 42 |
+
results[key] = to_tensor(results[key])
|
| 43 |
+
return results
|
| 44 |
+
|
| 45 |
+
def __repr__(self):
|
| 46 |
+
return self.__class__.__name__ + f'(keys={self.keys})'
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
@PIPELINES.register_module()
|
| 50 |
+
class ImageToTensor(object):
|
| 51 |
+
|
| 52 |
+
def __init__(self, keys):
|
| 53 |
+
self.keys = keys
|
| 54 |
+
|
| 55 |
+
def __call__(self, results):
|
| 56 |
+
for key in self.keys:
|
| 57 |
+
img = results[key]
|
| 58 |
+
if len(img.shape) < 3:
|
| 59 |
+
img = np.expand_dims(img, -1)
|
| 60 |
+
results[key] = to_tensor(img.transpose(2, 0, 1))
|
| 61 |
+
return results
|
| 62 |
+
|
| 63 |
+
def __repr__(self):
|
| 64 |
+
return self.__class__.__name__ + f'(keys={self.keys})'
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
@PIPELINES.register_module()
|
| 68 |
+
class Transpose(object):
|
| 69 |
+
|
| 70 |
+
def __init__(self, keys, order):
|
| 71 |
+
self.keys = keys
|
| 72 |
+
self.order = order
|
| 73 |
+
|
| 74 |
+
def __call__(self, results):
|
| 75 |
+
for key in self.keys:
|
| 76 |
+
results[key] = results[key].transpose(self.order)
|
| 77 |
+
return results
|
| 78 |
+
|
| 79 |
+
def __repr__(self):
|
| 80 |
+
return self.__class__.__name__ + \
|
| 81 |
+
f'(keys={self.keys}, order={self.order})'
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
@PIPELINES.register_module()
|
| 85 |
+
class ToPIL(object):
|
| 86 |
+
|
| 87 |
+
def __init__(self):
|
| 88 |
+
pass
|
| 89 |
+
|
| 90 |
+
def __call__(self, results):
|
| 91 |
+
results['img'] = Image.fromarray(results['img'])
|
| 92 |
+
return results
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
@PIPELINES.register_module()
|
| 96 |
+
class ToNumpy(object):
|
| 97 |
+
|
| 98 |
+
def __init__(self):
|
| 99 |
+
pass
|
| 100 |
+
|
| 101 |
+
def __call__(self, results):
|
| 102 |
+
results['img'] = np.array(results['img'], dtype=np.float32)
|
| 103 |
+
return results
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
@PIPELINES.register_module()
|
| 107 |
+
class Collect(object):
|
| 108 |
+
"""
|
| 109 |
+
Collect data from the loader relevant to the specific task.
|
| 110 |
+
|
| 111 |
+
This is usually the last stage of the data loader pipeline. Typically keys
|
| 112 |
+
is set to some subset of "img" and "gt_label".
|
| 113 |
+
"""
|
| 114 |
+
|
| 115 |
+
def __init__(self, keys):
|
| 116 |
+
self.keys = keys
|
| 117 |
+
|
| 118 |
+
def __call__(self, results):
|
| 119 |
+
data = {}
|
| 120 |
+
for key in self.keys:
|
| 121 |
+
data[key] = results[key]
|
| 122 |
+
return data
|
| 123 |
+
|
| 124 |
+
def __repr__(self):
|
| 125 |
+
return self.__class__.__name__ + \
|
| 126 |
+
f'(keys={self.keys}, meta_keys={self.meta_keys})'
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
@PIPELINES.register_module()
|
| 130 |
+
class WrapFieldsToLists(object):
|
| 131 |
+
"""Wrap fields of the data dictionary into lists for evaluation.
|
| 132 |
+
|
| 133 |
+
This class can be used as a last step of a test or validation
|
| 134 |
+
pipeline for single image evaluation or inference.
|
| 135 |
+
|
| 136 |
+
Example:
|
| 137 |
+
>>> test_pipeline = [
|
| 138 |
+
>>> dict(type='LoadImageFromFile'),
|
| 139 |
+
>>> dict(type='Normalize',
|
| 140 |
+
mean=[123.675, 116.28, 103.53],
|
| 141 |
+
std=[58.395, 57.12, 57.375],
|
| 142 |
+
to_rgb=True),
|
| 143 |
+
>>> dict(type='ImageToTensor', keys=['img']),
|
| 144 |
+
>>> dict(type='Collect', keys=['img']),
|
| 145 |
+
>>> dict(type='WrapIntoLists')
|
| 146 |
+
>>> ]
|
| 147 |
+
"""
|
| 148 |
+
|
| 149 |
+
def __call__(self, results):
|
| 150 |
+
# Wrap dict fields into lists
|
| 151 |
+
for key, val in results.items():
|
| 152 |
+
results[key] = [val]
|
| 153 |
+
return results
|
| 154 |
+
|
| 155 |
+
def __repr__(self):
|
| 156 |
+
return f'{self.__class__.__name__}()'
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/pipelines/loading.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os.path as osp
|
| 2 |
+
|
| 3 |
+
import mmcv
|
| 4 |
+
import numpy as np
|
| 5 |
+
|
| 6 |
+
from ..builder import PIPELINES
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
@PIPELINES.register_module()
|
| 10 |
+
class LoadImageFromFile(object):
|
| 11 |
+
"""Load an image from file.
|
| 12 |
+
|
| 13 |
+
Required keys are "img_prefix" and "img_info" (a dict that must contain the
|
| 14 |
+
key "filename"). Added or updated keys are "filename", "img", "img_shape",
|
| 15 |
+
"ori_shape" (same as `img_shape`) and "img_norm_cfg" (means=0 and stds=1).
|
| 16 |
+
|
| 17 |
+
Args:
|
| 18 |
+
to_float32 (bool): Whether to convert the loaded image to a float32
|
| 19 |
+
numpy array. If set to False, the loaded image is an uint8 array.
|
| 20 |
+
Defaults to False.
|
| 21 |
+
color_type (str): The flag argument for :func:`mmcv.imfrombytes()`.
|
| 22 |
+
Defaults to 'color'.
|
| 23 |
+
file_client_args (dict): Arguments to instantiate a FileClient.
|
| 24 |
+
See :class:`mmcv.fileio.FileClient` for details.
|
| 25 |
+
Defaults to ``dict(backend='disk')``.
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
def __init__(self,
|
| 29 |
+
to_float32=False,
|
| 30 |
+
color_type='color',
|
| 31 |
+
file_client_args=dict(backend='disk')):
|
| 32 |
+
self.to_float32 = to_float32
|
| 33 |
+
self.color_type = color_type
|
| 34 |
+
self.file_client_args = file_client_args.copy()
|
| 35 |
+
self.file_client = None
|
| 36 |
+
|
| 37 |
+
def __call__(self, results):
|
| 38 |
+
if self.file_client is None:
|
| 39 |
+
self.file_client = mmcv.FileClient(**self.file_client_args)
|
| 40 |
+
|
| 41 |
+
if results['img_prefix'] is not None:
|
| 42 |
+
filename = osp.join(results['img_prefix'],
|
| 43 |
+
results['img_info']['filename'])
|
| 44 |
+
else:
|
| 45 |
+
filename = results['img_info']['filename']
|
| 46 |
+
|
| 47 |
+
img_bytes = self.file_client.get(filename)
|
| 48 |
+
img = mmcv.imfrombytes(img_bytes, flag=self.color_type)
|
| 49 |
+
if self.to_float32:
|
| 50 |
+
img = img.astype(np.float32)
|
| 51 |
+
results['filename'] = filename
|
| 52 |
+
results['img'] = img
|
| 53 |
+
results['img_shape'] = img.shape
|
| 54 |
+
results['ori_shape'] = img.shape
|
| 55 |
+
num_channels = 1 if len(img.shape) < 3 else img.shape[2]
|
| 56 |
+
results['img_norm_cfg'] = dict(
|
| 57 |
+
mean=np.zeros(num_channels, dtype=np.float32),
|
| 58 |
+
std=np.ones(num_channels, dtype=np.float32),
|
| 59 |
+
to_rgb=False)
|
| 60 |
+
return results
|
| 61 |
+
|
| 62 |
+
def __repr__(self):
|
| 63 |
+
repr_str = (f'{self.__class__.__name__}('
|
| 64 |
+
f'to_float32={self.to_float32}, '
|
| 65 |
+
f"color_type='{self.color_type}', "
|
| 66 |
+
f'file_client_args={self.file_client_args})')
|
| 67 |
+
return repr_str
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/pipelines/test_time_aug.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import warnings
|
| 2 |
+
|
| 3 |
+
import mmcv
|
| 4 |
+
|
| 5 |
+
from ..builder import PIPELINES
|
| 6 |
+
from .compose import Compose
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
@PIPELINES.register_module()
|
| 10 |
+
class MultiScaleFlipAug(object):
|
| 11 |
+
"""Test-time augmentation with multiple scales and flipping.
|
| 12 |
+
|
| 13 |
+
An example configuration is as followed:
|
| 14 |
+
|
| 15 |
+
.. code-block::
|
| 16 |
+
|
| 17 |
+
img_scale=(2048, 1024),
|
| 18 |
+
img_ratios=[0.5, 1.0],
|
| 19 |
+
flip=True,
|
| 20 |
+
transforms=[
|
| 21 |
+
dict(type='Resize', keep_ratio=True),
|
| 22 |
+
dict(type='RandomFlip'),
|
| 23 |
+
dict(type='Normalize', **img_norm_cfg),
|
| 24 |
+
dict(type='Pad', size_divisor=32),
|
| 25 |
+
dict(type='ImageToTensor', keys=['img']),
|
| 26 |
+
dict(type='Collect', keys=['img']),
|
| 27 |
+
]
|
| 28 |
+
|
| 29 |
+
After MultiScaleFLipAug with above configuration, the results are wrapped
|
| 30 |
+
into lists of the same length as followed:
|
| 31 |
+
|
| 32 |
+
.. code-block::
|
| 33 |
+
|
| 34 |
+
dict(
|
| 35 |
+
img=[...],
|
| 36 |
+
img_shape=[...],
|
| 37 |
+
scale=[(1024, 512), (1024, 512), (2048, 1024), (2048, 1024)]
|
| 38 |
+
flip=[False, True, False, True]
|
| 39 |
+
...
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
Args:
|
| 43 |
+
transforms (list[dict]): Transforms to apply in each augmentation.
|
| 44 |
+
img_scale (tuple | list[tuple]): Images scales for resizing.
|
| 45 |
+
img_ratios (float | list[float]): Image ratios for resizing
|
| 46 |
+
flip (bool): Whether apply flip augmentation. Default: False.
|
| 47 |
+
flip_direction (str | list[str]): Flip augmentation directions,
|
| 48 |
+
options are "horizontal" and "vertical". If flip_direction is list,
|
| 49 |
+
multiple flip augmentations will be applied.
|
| 50 |
+
It has no effect when flip == False. Default: "horizontal".
|
| 51 |
+
"""
|
| 52 |
+
|
| 53 |
+
def __init__(self,
|
| 54 |
+
transforms,
|
| 55 |
+
img_scale,
|
| 56 |
+
num=8,
|
| 57 |
+
img_ratios=None,
|
| 58 |
+
flip=False,
|
| 59 |
+
flip_direction='horizontal'):
|
| 60 |
+
self.transforms = Compose(transforms)
|
| 61 |
+
self.num = num
|
| 62 |
+
if img_ratios is not None:
|
| 63 |
+
# mode 1: given a scale and a range of image ratio
|
| 64 |
+
img_ratios = img_ratios if isinstance(img_ratios,
|
| 65 |
+
list) else [img_ratios]
|
| 66 |
+
assert mmcv.is_list_of(img_ratios, float)
|
| 67 |
+
assert isinstance(img_scale, tuple) and len(img_scale) == 2
|
| 68 |
+
self.img_scale = [(int(img_scale[0] * ratio),
|
| 69 |
+
int(img_scale[1] * ratio))
|
| 70 |
+
for ratio in img_ratios]
|
| 71 |
+
else:
|
| 72 |
+
# mode 2: given multiple scales
|
| 73 |
+
self.img_scale = img_scale if isinstance(img_scale,
|
| 74 |
+
list) else [img_scale]
|
| 75 |
+
assert mmcv.is_list_of(self.img_scale, tuple)
|
| 76 |
+
self.flip = flip
|
| 77 |
+
self.flip_direction = flip_direction if isinstance(
|
| 78 |
+
flip_direction, list) else [flip_direction]
|
| 79 |
+
assert mmcv.is_list_of(self.flip_direction, str)
|
| 80 |
+
if not self.flip and self.flip_direction != ['horizontal']:
|
| 81 |
+
warnings.warn(
|
| 82 |
+
'flip_direction has no effect when flip is set to False')
|
| 83 |
+
if (self.flip
|
| 84 |
+
and not any([t['type'] == 'RandomFlip' for t in transforms])):
|
| 85 |
+
warnings.warn(
|
| 86 |
+
'flip has no effect when RandomFlip is not in transforms')
|
| 87 |
+
|
| 88 |
+
def __call__(self, results):
|
| 89 |
+
"""Call function to apply test time augment transforms on results.
|
| 90 |
+
|
| 91 |
+
Args:
|
| 92 |
+
results (dict): Result dict contains the data to transform.
|
| 93 |
+
|
| 94 |
+
Returns:
|
| 95 |
+
dict[str: list]: The augmented data, where each value is wrapped
|
| 96 |
+
into a list.
|
| 97 |
+
"""
|
| 98 |
+
|
| 99 |
+
aug_data = []
|
| 100 |
+
# flip_aug = [False, True] if self.flip else [False]
|
| 101 |
+
# for scale in self.img_scale:
|
| 102 |
+
# for flip in flip_aug:
|
| 103 |
+
# for direction in self.flip_direction:
|
| 104 |
+
# _results = results.copy()
|
| 105 |
+
# _results['scale'] = scale
|
| 106 |
+
# _results['flip'] = flip
|
| 107 |
+
# _results['flip_direction'] = direction
|
| 108 |
+
# data = self.transforms(_results)
|
| 109 |
+
# aug_data.append(data)
|
| 110 |
+
for _ in range(self.num):
|
| 111 |
+
_results = results.copy()
|
| 112 |
+
data = self.transforms(_results)
|
| 113 |
+
aug_data.append(data)
|
| 114 |
+
# list of dict to dict of list
|
| 115 |
+
aug_data_dict = {key: [] for key in aug_data[0]}
|
| 116 |
+
for data in aug_data:
|
| 117 |
+
for key, val in data.items():
|
| 118 |
+
aug_data_dict[key].append(val)
|
| 119 |
+
return aug_data_dict
|
| 120 |
+
|
| 121 |
+
def __repr__(self):
|
| 122 |
+
repr_str = self.__class__.__name__
|
| 123 |
+
repr_str += f'(transforms={self.transforms}, '
|
| 124 |
+
repr_str += f'img_scale={self.img_scale}, flip={self.flip})'
|
| 125 |
+
repr_str += f'flip_direction={self.flip_direction}'
|
| 126 |
+
return repr_str
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/pipelines/transforms.py
ADDED
|
@@ -0,0 +1,918 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import inspect
|
| 2 |
+
import math
|
| 3 |
+
import random
|
| 4 |
+
import numbers
|
| 5 |
+
import warnings
|
| 6 |
+
from typing import Tuple, List, Optional
|
| 7 |
+
|
| 8 |
+
import cv2
|
| 9 |
+
import mmcv
|
| 10 |
+
import numpy as np
|
| 11 |
+
from PIL import Image, ImageFilter
|
| 12 |
+
|
| 13 |
+
import torch
|
| 14 |
+
from torch import Tensor
|
| 15 |
+
from torchvision import transforms as _transforms
|
| 16 |
+
import torchvision.transforms.functional as TVF
|
| 17 |
+
from mmcv.utils import build_from_cfg
|
| 18 |
+
|
| 19 |
+
from ..builder import PIPELINES
|
| 20 |
+
|
| 21 |
+
try:
|
| 22 |
+
import albumentations
|
| 23 |
+
from albumentations import Compose
|
| 24 |
+
except ImportError:
|
| 25 |
+
albumentations = None
|
| 26 |
+
Compose = None
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@PIPELINES.register_module()
|
| 30 |
+
class RandomAppliedTrans(object):
|
| 31 |
+
"""Randomly applied transformations.
|
| 32 |
+
|
| 33 |
+
Args:
|
| 34 |
+
transforms (list[dict]): List of transformations in dictionaries.
|
| 35 |
+
p (float): Probability.
|
| 36 |
+
"""
|
| 37 |
+
|
| 38 |
+
def __init__(self, transforms, p=0.5):
|
| 39 |
+
t = [build_from_cfg(t, PIPELINES) for t in transforms]
|
| 40 |
+
self.trans = _transforms.RandomApply(t, p=p)
|
| 41 |
+
|
| 42 |
+
def __call__(self, img):
|
| 43 |
+
return self.trans(img)
|
| 44 |
+
|
| 45 |
+
def __repr__(self):
|
| 46 |
+
repr_str = self.__class__.__name__
|
| 47 |
+
return repr_str
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
@PIPELINES.register_module()
|
| 51 |
+
class RandomCrop(object):
|
| 52 |
+
"""Crop the given Image at a random location.
|
| 53 |
+
|
| 54 |
+
Args:
|
| 55 |
+
size (sequence or int): Desired output size of the crop. If size is an
|
| 56 |
+
int instead of sequence like (h, w), a square crop (size, size) is
|
| 57 |
+
made.
|
| 58 |
+
padding (int or sequence, optional): Optional padding on each border
|
| 59 |
+
of the image. If a sequence of length 4 is provided, it is used to
|
| 60 |
+
pad left, top, right, bottom borders respectively. If a sequence
|
| 61 |
+
of length 2 is provided, it is used to pad left/right, top/bottom
|
| 62 |
+
borders, respectively. Default: None, which means no padding.
|
| 63 |
+
pad_if_needed (boolean): It will pad the image if smaller than the
|
| 64 |
+
desired size to avoid raising an exception. Since cropping is done
|
| 65 |
+
after padding, the padding seems to be done at a random offset.
|
| 66 |
+
Default: False.
|
| 67 |
+
pad_val (Number | Sequence[Number]): Pixel pad_val value for constant
|
| 68 |
+
fill. If a tuple of length 3, it is used to pad_val R, G, B
|
| 69 |
+
channels respectively. Default: 0.
|
| 70 |
+
padding_mode (str): Type of padding. Should be: constant, edge,
|
| 71 |
+
reflect or symmetric. Default: constant.
|
| 72 |
+
-constant: Pads with a constant value, this value is specified
|
| 73 |
+
with pad_val.
|
| 74 |
+
-edge: pads with the last value at the edge of the image.
|
| 75 |
+
-reflect: Pads with reflection of image without repeating the
|
| 76 |
+
last value on the edge. For example, padding [1, 2, 3, 4]
|
| 77 |
+
with 2 elements on both sides in reflect mode will result
|
| 78 |
+
in [3, 2, 1, 2, 3, 4, 3, 2].
|
| 79 |
+
-symmetric: Pads with reflection of image repeating the last
|
| 80 |
+
value on the edge. For example, padding [1, 2, 3, 4] with
|
| 81 |
+
2 elements on both sides in symmetric mode will result in
|
| 82 |
+
[2, 1, 1, 2, 3, 4, 4, 3].
|
| 83 |
+
"""
|
| 84 |
+
|
| 85 |
+
def __init__(self,
|
| 86 |
+
size,
|
| 87 |
+
padding=None,
|
| 88 |
+
pad_if_needed=False,
|
| 89 |
+
pad_val=0,
|
| 90 |
+
padding_mode='constant'):
|
| 91 |
+
if isinstance(size, (tuple, list)):
|
| 92 |
+
self.size = size
|
| 93 |
+
else:
|
| 94 |
+
self.size = (size, size)
|
| 95 |
+
# check padding mode
|
| 96 |
+
assert padding_mode in ['constant', 'edge', 'reflect', 'symmetric']
|
| 97 |
+
self.padding = padding
|
| 98 |
+
self.pad_if_needed = pad_if_needed
|
| 99 |
+
self.pad_val = pad_val
|
| 100 |
+
self.padding_mode = padding_mode
|
| 101 |
+
|
| 102 |
+
@staticmethod
|
| 103 |
+
def get_params(img, output_size):
|
| 104 |
+
"""Get parameters for ``crop`` for a random crop.
|
| 105 |
+
|
| 106 |
+
Args:
|
| 107 |
+
img (ndarray): Image to be cropped.
|
| 108 |
+
output_size (tuple): Expected output size of the crop.
|
| 109 |
+
|
| 110 |
+
Returns:
|
| 111 |
+
tuple: Params (xmin, ymin, target_height, target_width) to be
|
| 112 |
+
passed to ``crop`` for random crop.
|
| 113 |
+
"""
|
| 114 |
+
height = img.shape[0]
|
| 115 |
+
width = img.shape[1]
|
| 116 |
+
target_height, target_width = output_size
|
| 117 |
+
if width == target_width and height == target_height:
|
| 118 |
+
return 0, 0, height, width
|
| 119 |
+
|
| 120 |
+
xmin = random.randint(0, height - target_height)
|
| 121 |
+
ymin = random.randint(0, width - target_width)
|
| 122 |
+
return xmin, ymin, target_height, target_width
|
| 123 |
+
|
| 124 |
+
def __call__(self, results):
|
| 125 |
+
"""
|
| 126 |
+
Args:
|
| 127 |
+
img (ndarray): Image to be cropped.
|
| 128 |
+
"""
|
| 129 |
+
for key in results.get('img_fields', ['img']):
|
| 130 |
+
img = results[key]
|
| 131 |
+
if self.padding is not None:
|
| 132 |
+
img = mmcv.impad(
|
| 133 |
+
img, padding=self.padding, pad_val=self.pad_val)
|
| 134 |
+
|
| 135 |
+
# pad the height if needed
|
| 136 |
+
if self.pad_if_needed and img.shape[0] < self.size[0]:
|
| 137 |
+
img = mmcv.impad(
|
| 138 |
+
img,
|
| 139 |
+
padding=(0, self.size[0] - img.shape[0], 0,
|
| 140 |
+
self.size[0] - img.shape[0]),
|
| 141 |
+
pad_val=self.pad_val,
|
| 142 |
+
padding_mode=self.padding_mode)
|
| 143 |
+
|
| 144 |
+
# pad the width if needed
|
| 145 |
+
if self.pad_if_needed and img.shape[1] < self.size[1]:
|
| 146 |
+
img = mmcv.impad(
|
| 147 |
+
img,
|
| 148 |
+
padding=(self.size[1] - img.shape[1], 0,
|
| 149 |
+
self.size[1] - img.shape[1], 0),
|
| 150 |
+
pad_val=self.pad_val,
|
| 151 |
+
padding_mode=self.padding_mode)
|
| 152 |
+
|
| 153 |
+
xmin, ymin, height, width = self.get_params(img, self.size)
|
| 154 |
+
results[key] = mmcv.imcrop(
|
| 155 |
+
img,
|
| 156 |
+
np.array([ymin, xmin, ymin + width - 1, xmin + height - 1]))
|
| 157 |
+
return results
|
| 158 |
+
|
| 159 |
+
def __repr__(self):
|
| 160 |
+
return (self.__class__.__name__ +
|
| 161 |
+
f'(size={self.size}, padding={self.padding})')
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
@PIPELINES.register_module()
|
| 165 |
+
class RandomResizedCrop(object):
|
| 166 |
+
"""Crop the given image to random size and aspect ratio.
|
| 167 |
+
|
| 168 |
+
A crop of random size (default: of 0.08 to 1.0) of the original size and a
|
| 169 |
+
random aspect ratio (default: of 3/4 to 4/3) of the original aspect ratio
|
| 170 |
+
is made. This crop is finally resized to given size.
|
| 171 |
+
|
| 172 |
+
Args:
|
| 173 |
+
size (sequence or int): Desired output size of the crop. If size is an
|
| 174 |
+
int instead of sequence like (h, w), a square crop (size, size) is
|
| 175 |
+
made.
|
| 176 |
+
scale (tuple): Range of the random size of the cropped image compared
|
| 177 |
+
to the original image. Default: (0.08, 1.0).
|
| 178 |
+
ratio (tuple): Range of the random aspect ratio of the cropped image
|
| 179 |
+
compared to the original image. Default: (3. / 4., 4. / 3.).
|
| 180 |
+
interpolation (str): Interpolation method, accepted values are
|
| 181 |
+
'nearest', 'bilinear', 'bicubic', 'area', 'lanczos'. Default:
|
| 182 |
+
'bilinear'.
|
| 183 |
+
backend (str): The image resize backend type, accpeted values are
|
| 184 |
+
`cv2` and `pillow`. Default: `cv2`.
|
| 185 |
+
"""
|
| 186 |
+
|
| 187 |
+
def __init__(self,
|
| 188 |
+
size,
|
| 189 |
+
scale=(0.08, 1.0),
|
| 190 |
+
ratio=(3. / 4., 4. / 3.),
|
| 191 |
+
interpolation='bilinear',
|
| 192 |
+
backend='cv2'):
|
| 193 |
+
if isinstance(size, (tuple, list)):
|
| 194 |
+
self.size = size
|
| 195 |
+
else:
|
| 196 |
+
self.size = (size, size)
|
| 197 |
+
if (scale[0] > scale[1]) or (ratio[0] > ratio[1]):
|
| 198 |
+
raise ValueError('range should be of kind (min, max). '
|
| 199 |
+
f'But received {scale}')
|
| 200 |
+
if backend not in ['cv2', 'pillow']:
|
| 201 |
+
raise ValueError(f'backend: {backend} is not supported for resize.'
|
| 202 |
+
'Supported backends are "cv2", "pillow"')
|
| 203 |
+
|
| 204 |
+
self.interpolation = interpolation
|
| 205 |
+
self.scale = scale
|
| 206 |
+
self.ratio = ratio
|
| 207 |
+
self.backend = backend
|
| 208 |
+
|
| 209 |
+
@staticmethod
|
| 210 |
+
def get_params(img, scale, ratio):
|
| 211 |
+
"""Get parameters for ``crop`` for a random sized crop.
|
| 212 |
+
|
| 213 |
+
Args:
|
| 214 |
+
img (ndarray): Image to be cropped.
|
| 215 |
+
scale (tuple): Range of the random size of the cropped image
|
| 216 |
+
compared to the original image size.
|
| 217 |
+
ratio (tuple): Range of the random aspect ratio of the cropped
|
| 218 |
+
image compared to the original image area.
|
| 219 |
+
|
| 220 |
+
Returns:
|
| 221 |
+
tuple: Params (xmin, ymin, target_height, target_width) to be
|
| 222 |
+
passed to ``crop`` for a random sized crop.
|
| 223 |
+
"""
|
| 224 |
+
height = img.shape[0]
|
| 225 |
+
width = img.shape[1]
|
| 226 |
+
area = height * width
|
| 227 |
+
|
| 228 |
+
for _ in range(10):
|
| 229 |
+
target_area = random.uniform(*scale) * area
|
| 230 |
+
log_ratio = (math.log(ratio[0]), math.log(ratio[1]))
|
| 231 |
+
aspect_ratio = math.exp(random.uniform(*log_ratio))
|
| 232 |
+
|
| 233 |
+
target_width = int(round(math.sqrt(target_area * aspect_ratio)))
|
| 234 |
+
target_height = int(round(math.sqrt(target_area / aspect_ratio)))
|
| 235 |
+
|
| 236 |
+
if 0 < target_width <= width and 0 < target_height <= height:
|
| 237 |
+
xmin = random.randint(0, height - target_height)
|
| 238 |
+
ymin = random.randint(0, width - target_width)
|
| 239 |
+
return xmin, ymin, target_height, target_width
|
| 240 |
+
|
| 241 |
+
# Fallback to central crop
|
| 242 |
+
in_ratio = float(width) / float(height)
|
| 243 |
+
if in_ratio < min(ratio):
|
| 244 |
+
target_width = width
|
| 245 |
+
target_height = int(round(target_width / min(ratio)))
|
| 246 |
+
elif in_ratio > max(ratio):
|
| 247 |
+
target_height = height
|
| 248 |
+
target_width = int(round(target_height * max(ratio)))
|
| 249 |
+
else: # whole image
|
| 250 |
+
target_width = width
|
| 251 |
+
target_height = height
|
| 252 |
+
xmin = (height - target_height) // 2
|
| 253 |
+
ymin = (width - target_width) // 2
|
| 254 |
+
return xmin, ymin, target_height, target_width
|
| 255 |
+
|
| 256 |
+
def __call__(self, results):
|
| 257 |
+
"""
|
| 258 |
+
Args:
|
| 259 |
+
img (ndarray): Image to be cropped and resized.
|
| 260 |
+
|
| 261 |
+
Returns:
|
| 262 |
+
ndarray: Randomly cropped and resized image.
|
| 263 |
+
"""
|
| 264 |
+
for key in results.get('img_fields', ['img']):
|
| 265 |
+
img = results[key]
|
| 266 |
+
xmin, ymin, target_height, target_width = self.get_params(
|
| 267 |
+
img, self.scale, self.ratio)
|
| 268 |
+
img = mmcv.imcrop(
|
| 269 |
+
img,
|
| 270 |
+
np.array([
|
| 271 |
+
ymin, xmin, ymin + target_width - 1,
|
| 272 |
+
xmin + target_height - 1
|
| 273 |
+
]))
|
| 274 |
+
results[key] = mmcv.imresize(
|
| 275 |
+
img,
|
| 276 |
+
tuple(self.size[::-1]),
|
| 277 |
+
interpolation=self.interpolation,
|
| 278 |
+
backend=self.backend)
|
| 279 |
+
return results
|
| 280 |
+
|
| 281 |
+
def __repr__(self):
|
| 282 |
+
format_string = self.__class__.__name__ + f'(size={self.size}'
|
| 283 |
+
format_string += f', scale={tuple(round(s, 4) for s in self.scale)}'
|
| 284 |
+
format_string += f', ratio={tuple(round(r, 4) for r in self.ratio)}'
|
| 285 |
+
format_string += f', interpolation={self.interpolation})'
|
| 286 |
+
return format_string
|
| 287 |
+
|
| 288 |
+
|
| 289 |
+
@PIPELINES.register_module()
|
| 290 |
+
class RandomGrayscale(object):
|
| 291 |
+
"""Randomly convert image to grayscale with a probability of gray_prob.
|
| 292 |
+
|
| 293 |
+
Args:
|
| 294 |
+
gray_prob (float): Probability that image should be converted to
|
| 295 |
+
grayscale. Default: 0.1.
|
| 296 |
+
|
| 297 |
+
Returns:
|
| 298 |
+
ndarray: Grayscale version of the input image with probability
|
| 299 |
+
gray_prob and unchanged with probability (1-gray_prob).
|
| 300 |
+
- If input image is 1 channel: grayscale version is 1 channel.
|
| 301 |
+
- If input image is 3 channel: grayscale version is 3 channel
|
| 302 |
+
with r == g == b.
|
| 303 |
+
|
| 304 |
+
"""
|
| 305 |
+
|
| 306 |
+
def __init__(self, gray_prob=0.1):
|
| 307 |
+
self.gray_prob = gray_prob
|
| 308 |
+
|
| 309 |
+
def __call__(self, results):
|
| 310 |
+
"""
|
| 311 |
+
Args:
|
| 312 |
+
img (ndarray): Image to be converted to grayscale.
|
| 313 |
+
|
| 314 |
+
Returns:
|
| 315 |
+
ndarray: Randomly grayscaled image.
|
| 316 |
+
"""
|
| 317 |
+
for key in results.get('img_fields', ['img']):
|
| 318 |
+
img = results[key]
|
| 319 |
+
num_output_channels = img.shape[2]
|
| 320 |
+
if random.random() < self.gray_prob:
|
| 321 |
+
if num_output_channels > 1:
|
| 322 |
+
img = mmcv.rgb2gray(img)[:, :, None]
|
| 323 |
+
results[key] = np.dstack(
|
| 324 |
+
[img for _ in range(num_output_channels)])
|
| 325 |
+
return results
|
| 326 |
+
results[key] = img
|
| 327 |
+
return results
|
| 328 |
+
|
| 329 |
+
def __repr__(self):
|
| 330 |
+
return self.__class__.__name__ + f'(gray_prob={self.gray_prob})'
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
@PIPELINES.register_module()
|
| 334 |
+
class RandomFlip(object):
|
| 335 |
+
"""Flip the image randomly.
|
| 336 |
+
|
| 337 |
+
Flip the image randomly based on flip probaility and flip direction.
|
| 338 |
+
|
| 339 |
+
Args:
|
| 340 |
+
flip_prob (float): probability of the image being flipped. Default: 0.5
|
| 341 |
+
direction (str, optional): The flipping direction. Options are
|
| 342 |
+
'horizontal' and 'vertical'. Default: 'horizontal'.
|
| 343 |
+
"""
|
| 344 |
+
|
| 345 |
+
def __init__(self, flip_prob=0.5, direction='horizontal'):
|
| 346 |
+
assert 0 <= flip_prob <= 1
|
| 347 |
+
assert direction in ['horizontal', 'vertical']
|
| 348 |
+
self.flip_prob = flip_prob
|
| 349 |
+
self.direction = direction
|
| 350 |
+
|
| 351 |
+
def __call__(self, results):
|
| 352 |
+
"""Call function to flip image.
|
| 353 |
+
|
| 354 |
+
Args:
|
| 355 |
+
results (dict): Result dict from loading pipeline.
|
| 356 |
+
|
| 357 |
+
Returns:
|
| 358 |
+
dict: Flipped results, 'flip', 'flip_direction' keys are added into
|
| 359 |
+
result dict.
|
| 360 |
+
"""
|
| 361 |
+
flip = True if np.random.rand() < self.flip_prob else False
|
| 362 |
+
results['flip'] = flip
|
| 363 |
+
results['flip_direction'] = self.direction
|
| 364 |
+
if results['flip']:
|
| 365 |
+
# flip image
|
| 366 |
+
for key in results.get('img_fields', ['img']):
|
| 367 |
+
results[key] = mmcv.imflip(
|
| 368 |
+
results[key], direction=results['flip_direction'])
|
| 369 |
+
return results
|
| 370 |
+
|
| 371 |
+
def __repr__(self):
|
| 372 |
+
return self.__class__.__name__ + f'(flip_prob={self.flip_prob})'
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
@PIPELINES.register_module()
|
| 376 |
+
class Resize(object):
|
| 377 |
+
"""Resize images.
|
| 378 |
+
|
| 379 |
+
Args:
|
| 380 |
+
size (int | tuple): Images scales for resizing (h, w).
|
| 381 |
+
When size is int, the default behavior is to resize an image
|
| 382 |
+
to (size, size). When size is tuple and the second value is -1,
|
| 383 |
+
the short edge of an image is resized to its first value.
|
| 384 |
+
For example, when size is 224, the image is resized to 224x224.
|
| 385 |
+
When size is (224, -1), the short side is resized to 224 and the
|
| 386 |
+
other side is computed based on the short side, maintaining the
|
| 387 |
+
aspect ratio.
|
| 388 |
+
interpolation (str): Interpolation method, accepted values are
|
| 389 |
+
"nearest", "bilinear", "bicubic", "area", "lanczos".
|
| 390 |
+
More details can be found in `mmcv.image.geometric`.
|
| 391 |
+
backend (str): The image resize backend type, accpeted values are
|
| 392 |
+
`cv2` and `pillow`. Default: `cv2`.
|
| 393 |
+
"""
|
| 394 |
+
|
| 395 |
+
def __init__(self, size, interpolation='bilinear', backend='cv2'):
|
| 396 |
+
assert isinstance(size, int) or (isinstance(size, tuple)
|
| 397 |
+
and len(size) == 2)
|
| 398 |
+
self.resize_w_short_side = False
|
| 399 |
+
if isinstance(size, int):
|
| 400 |
+
assert size > 0
|
| 401 |
+
size = (size, size)
|
| 402 |
+
else:
|
| 403 |
+
assert size[0] > 0 and (size[1] > 0 or size[1] == -1)
|
| 404 |
+
if size[1] == -1:
|
| 405 |
+
self.resize_w_short_side = True
|
| 406 |
+
assert interpolation in ('nearest', 'bilinear', 'bicubic', 'area',
|
| 407 |
+
'lanczos')
|
| 408 |
+
if backend not in ['cv2', 'pillow']:
|
| 409 |
+
raise ValueError(f'backend: {backend} is not supported for resize.'
|
| 410 |
+
'Supported backends are "cv2", "pillow"')
|
| 411 |
+
|
| 412 |
+
self.size = size
|
| 413 |
+
self.interpolation = interpolation
|
| 414 |
+
self.backend = backend
|
| 415 |
+
|
| 416 |
+
def _resize_img(self, results):
|
| 417 |
+
for key in results.get('img_fields', ['img']):
|
| 418 |
+
img = results[key]
|
| 419 |
+
ignore_resize = False
|
| 420 |
+
if self.resize_w_short_side:
|
| 421 |
+
h, w = img.shape[:2]
|
| 422 |
+
short_side = self.size[0]
|
| 423 |
+
if (w <= h and w == short_side) or (h <= w
|
| 424 |
+
and h == short_side):
|
| 425 |
+
ignore_resize = True
|
| 426 |
+
else:
|
| 427 |
+
if w < h:
|
| 428 |
+
width = short_side
|
| 429 |
+
height = int(short_side * h / w)
|
| 430 |
+
else:
|
| 431 |
+
height = short_side
|
| 432 |
+
width = int(short_side * w / h)
|
| 433 |
+
else:
|
| 434 |
+
height, width = self.size
|
| 435 |
+
if not ignore_resize:
|
| 436 |
+
img = mmcv.imresize(
|
| 437 |
+
img,
|
| 438 |
+
size=(width, height),
|
| 439 |
+
interpolation=self.interpolation,
|
| 440 |
+
return_scale=False,
|
| 441 |
+
backend=self.backend)
|
| 442 |
+
results[key] = img
|
| 443 |
+
results['img_shape'] = img.shape
|
| 444 |
+
|
| 445 |
+
def __call__(self, results):
|
| 446 |
+
self._resize_img(results)
|
| 447 |
+
return results
|
| 448 |
+
|
| 449 |
+
def __repr__(self):
|
| 450 |
+
repr_str = self.__class__.__name__
|
| 451 |
+
repr_str += f'(size={self.size}, '
|
| 452 |
+
repr_str += f'interpolation={self.interpolation})'
|
| 453 |
+
return repr_str
|
| 454 |
+
|
| 455 |
+
|
| 456 |
+
@PIPELINES.register_module()
|
| 457 |
+
class CenterCrop(object):
|
| 458 |
+
"""Center crop the image.
|
| 459 |
+
|
| 460 |
+
Args:
|
| 461 |
+
crop_size (int | tuple): Expected size after cropping, (h, w).
|
| 462 |
+
|
| 463 |
+
Notes:
|
| 464 |
+
If the image is smaller than the crop size, return the original image
|
| 465 |
+
"""
|
| 466 |
+
|
| 467 |
+
def __init__(self, crop_size):
|
| 468 |
+
assert isinstance(crop_size, int) or (isinstance(crop_size, tuple)
|
| 469 |
+
and len(crop_size) == 2)
|
| 470 |
+
if isinstance(crop_size, int):
|
| 471 |
+
crop_size = (crop_size, crop_size)
|
| 472 |
+
assert crop_size[0] > 0 and crop_size[1] > 0
|
| 473 |
+
self.crop_size = crop_size
|
| 474 |
+
|
| 475 |
+
def __call__(self, results):
|
| 476 |
+
crop_height, crop_width = self.crop_size[0], self.crop_size[1]
|
| 477 |
+
for key in results.get('img_fields', ['img']):
|
| 478 |
+
img = results[key]
|
| 479 |
+
# img.shape has length 2 for grayscale, length 3 for color
|
| 480 |
+
img_height, img_width = img.shape[:2]
|
| 481 |
+
|
| 482 |
+
y1 = max(0, int(round((img_height - crop_height) / 2.)))
|
| 483 |
+
x1 = max(0, int(round((img_width - crop_width) / 2.)))
|
| 484 |
+
y2 = min(img_height, y1 + crop_height) - 1
|
| 485 |
+
x2 = min(img_width, x1 + crop_width) - 1
|
| 486 |
+
|
| 487 |
+
# crop the image
|
| 488 |
+
img = mmcv.imcrop(img, bboxes=np.array([x1, y1, x2, y2]))
|
| 489 |
+
img_shape = img.shape
|
| 490 |
+
results[key] = img
|
| 491 |
+
results['img_shape'] = img_shape
|
| 492 |
+
|
| 493 |
+
return results
|
| 494 |
+
|
| 495 |
+
def __repr__(self):
|
| 496 |
+
return self.__class__.__name__ + f'(crop_size={self.crop_size})'
|
| 497 |
+
|
| 498 |
+
|
| 499 |
+
@PIPELINES.register_module()
|
| 500 |
+
class Normalize(object):
|
| 501 |
+
"""Normalize the image.
|
| 502 |
+
|
| 503 |
+
Args:
|
| 504 |
+
mean (sequence): Mean values of 3 channels.
|
| 505 |
+
std (sequence): Std values of 3 channels.
|
| 506 |
+
to_rgb (bool): Whether to convert the image from BGR to RGB,
|
| 507 |
+
default is true.
|
| 508 |
+
"""
|
| 509 |
+
|
| 510 |
+
def __init__(self, mean, std, to_rgb=True):
|
| 511 |
+
self.mean = np.array(mean, dtype=np.float32)
|
| 512 |
+
self.std = np.array(std, dtype=np.float32)
|
| 513 |
+
self.to_rgb = to_rgb
|
| 514 |
+
|
| 515 |
+
def __call__(self, results):
|
| 516 |
+
for key in results.get('img_fields', ['img']):
|
| 517 |
+
results[key] = mmcv.imnormalize(results[key], self.mean, self.std,
|
| 518 |
+
self.to_rgb)
|
| 519 |
+
results['img_norm_cfg'] = dict(
|
| 520 |
+
mean=self.mean, std=self.std, to_rgb=self.to_rgb)
|
| 521 |
+
return results
|
| 522 |
+
|
| 523 |
+
def __repr__(self):
|
| 524 |
+
repr_str = self.__class__.__name__
|
| 525 |
+
repr_str += f'(mean={list(self.mean)}, '
|
| 526 |
+
repr_str += f'std={list(self.std)}, '
|
| 527 |
+
repr_str += f'to_rgb={self.to_rgb})'
|
| 528 |
+
return repr_str
|
| 529 |
+
|
| 530 |
+
|
| 531 |
+
@PIPELINES.register_module()
|
| 532 |
+
class Albu(object):
|
| 533 |
+
"""Albumentation augmentation.
|
| 534 |
+
|
| 535 |
+
Adds custom transformations from Albumentations library.
|
| 536 |
+
Please, visit `https://albumentations.readthedocs.io`
|
| 537 |
+
to get more information.
|
| 538 |
+
An example of ``transforms`` is as followed:
|
| 539 |
+
|
| 540 |
+
.. code-block::
|
| 541 |
+
[
|
| 542 |
+
dict(
|
| 543 |
+
type='ShiftScaleRotate',
|
| 544 |
+
shift_limit=0.0625,
|
| 545 |
+
scale_limit=0.0,
|
| 546 |
+
rotate_limit=0,
|
| 547 |
+
interpolation=1,
|
| 548 |
+
p=0.5),
|
| 549 |
+
dict(
|
| 550 |
+
type='RandomBrightnessContrast',
|
| 551 |
+
brightness_limit=[0.1, 0.3],
|
| 552 |
+
contrast_limit=[0.1, 0.3],
|
| 553 |
+
p=0.2),
|
| 554 |
+
dict(type='ChannelShuffle', p=0.1),
|
| 555 |
+
dict(
|
| 556 |
+
type='OneOf',
|
| 557 |
+
transforms=[
|
| 558 |
+
dict(type='Blur', blur_limit=3, p=1.0),
|
| 559 |
+
dict(type='MedianBlur', blur_limit=3, p=1.0)
|
| 560 |
+
],
|
| 561 |
+
p=0.1),
|
| 562 |
+
]
|
| 563 |
+
|
| 564 |
+
Args:
|
| 565 |
+
transforms (list[dict]): A list of albu transformations
|
| 566 |
+
keymap (dict): Contains {'input key':'albumentation-style key'}
|
| 567 |
+
"""
|
| 568 |
+
|
| 569 |
+
def __init__(self, transforms, keymap=None, update_pad_shape=False):
|
| 570 |
+
if Compose is None:
|
| 571 |
+
raise RuntimeError('albumentations is not installed')
|
| 572 |
+
|
| 573 |
+
self.transforms = transforms
|
| 574 |
+
self.filter_lost_elements = False
|
| 575 |
+
self.update_pad_shape = update_pad_shape
|
| 576 |
+
|
| 577 |
+
self.aug = Compose([self.albu_builder(t) for t in self.transforms])
|
| 578 |
+
|
| 579 |
+
if not keymap:
|
| 580 |
+
self.keymap_to_albu = {
|
| 581 |
+
'img': 'image',
|
| 582 |
+
}
|
| 583 |
+
else:
|
| 584 |
+
self.keymap_to_albu = keymap
|
| 585 |
+
self.keymap_back = {v: k for k, v in self.keymap_to_albu.items()}
|
| 586 |
+
|
| 587 |
+
def albu_builder(self, cfg):
|
| 588 |
+
"""Import a module from albumentations.
|
| 589 |
+
It inherits some of :func:`build_from_cfg` logic.
|
| 590 |
+
Args:
|
| 591 |
+
cfg (dict): Config dict. It should at least contain the key "type".
|
| 592 |
+
Returns:
|
| 593 |
+
obj: The constructed object.
|
| 594 |
+
"""
|
| 595 |
+
|
| 596 |
+
assert isinstance(cfg, dict) and 'type' in cfg
|
| 597 |
+
args = cfg.copy()
|
| 598 |
+
|
| 599 |
+
obj_type = args.pop('type')
|
| 600 |
+
if mmcv.is_str(obj_type):
|
| 601 |
+
if albumentations is None:
|
| 602 |
+
raise RuntimeError('albumentations is not installed')
|
| 603 |
+
obj_cls = getattr(albumentations, obj_type)
|
| 604 |
+
elif inspect.isclass(obj_type):
|
| 605 |
+
obj_cls = obj_type
|
| 606 |
+
else:
|
| 607 |
+
raise TypeError(
|
| 608 |
+
f'type must be a str or valid type, but got {type(obj_type)}')
|
| 609 |
+
|
| 610 |
+
if 'transforms' in args:
|
| 611 |
+
args['transforms'] = [
|
| 612 |
+
self.albu_builder(transform)
|
| 613 |
+
for transform in args['transforms']
|
| 614 |
+
]
|
| 615 |
+
|
| 616 |
+
return obj_cls(**args)
|
| 617 |
+
|
| 618 |
+
@staticmethod
|
| 619 |
+
def mapper(d, keymap):
|
| 620 |
+
"""Dictionary mapper. Renames keys according to keymap provided.
|
| 621 |
+
Args:
|
| 622 |
+
d (dict): old dict
|
| 623 |
+
keymap (dict): {'old_key':'new_key'}
|
| 624 |
+
Returns:
|
| 625 |
+
dict: new dict.
|
| 626 |
+
"""
|
| 627 |
+
|
| 628 |
+
updated_dict = {}
|
| 629 |
+
for k, v in zip(d.keys(), d.values()):
|
| 630 |
+
new_k = keymap.get(k, k)
|
| 631 |
+
updated_dict[new_k] = d[k]
|
| 632 |
+
return updated_dict
|
| 633 |
+
|
| 634 |
+
def __call__(self, results):
|
| 635 |
+
# dict to albumentations format
|
| 636 |
+
results = self.mapper(results, self.keymap_to_albu)
|
| 637 |
+
|
| 638 |
+
results = self.aug(**results)
|
| 639 |
+
|
| 640 |
+
if 'gt_labels' in results:
|
| 641 |
+
if isinstance(results['gt_labels'], list):
|
| 642 |
+
results['gt_labels'] = np.array(results['gt_labels'])
|
| 643 |
+
results['gt_labels'] = results['gt_labels'].astype(np.int64)
|
| 644 |
+
|
| 645 |
+
# back to the original format
|
| 646 |
+
results = self.mapper(results, self.keymap_back)
|
| 647 |
+
|
| 648 |
+
# update final shape
|
| 649 |
+
if self.update_pad_shape:
|
| 650 |
+
results['pad_shape'] = results['img'].shape
|
| 651 |
+
|
| 652 |
+
return results
|
| 653 |
+
|
| 654 |
+
def __repr__(self):
|
| 655 |
+
repr_str = self.__class__.__name__ + f'(transforms={self.transforms})'
|
| 656 |
+
return repr_str
|
| 657 |
+
|
| 658 |
+
# custom transforms
|
| 659 |
+
@PIPELINES.register_module()
|
| 660 |
+
class GaussianBlur(object):
|
| 661 |
+
|
| 662 |
+
def __init__(self, sigma_min, sigma_max):
|
| 663 |
+
self.sigma_min = sigma_min
|
| 664 |
+
self.sigma_max = sigma_max
|
| 665 |
+
|
| 666 |
+
def __call__(self, results):
|
| 667 |
+
for key in results.get('img_fields', ['img']):
|
| 668 |
+
img = results[key]
|
| 669 |
+
sigma = np.random.uniform(self.sigma_min, self.sigma_max)
|
| 670 |
+
img = Image.fromarray(img)
|
| 671 |
+
img = img.filter(ImageFilter.GaussianBlur(radius=sigma))
|
| 672 |
+
results[key] = np.array(img).astype('float32')
|
| 673 |
+
return results
|
| 674 |
+
|
| 675 |
+
def __repr__(self):
|
| 676 |
+
repr_str = self.__class__.__name__
|
| 677 |
+
return repr_str
|
| 678 |
+
|
| 679 |
+
|
| 680 |
+
@PIPELINES.register_module()
|
| 681 |
+
class RandomRotate(object):
|
| 682 |
+
"""Rotate the image & seg.
|
| 683 |
+
|
| 684 |
+
Args:
|
| 685 |
+
prob (float): The rotation probability.
|
| 686 |
+
degree (float, tuple[float]): Range of degrees to select from. If
|
| 687 |
+
degree is a number instead of tuple like (min, max),
|
| 688 |
+
the range of degree will be (``-degree``, ``+degree``)
|
| 689 |
+
pad_val (float, optional): Padding value of image. Default: 0.
|
| 690 |
+
center (tuple[float], optional): Center point (w, h) of the rotation in
|
| 691 |
+
the source image. If not specified, the center of the image will be
|
| 692 |
+
used. Default: None.
|
| 693 |
+
auto_bound (bool): Whether to adjust the image size to cover the whole
|
| 694 |
+
rotated image. Default: False
|
| 695 |
+
"""
|
| 696 |
+
|
| 697 |
+
def __init__(self,
|
| 698 |
+
prob,
|
| 699 |
+
degree,
|
| 700 |
+
pad_val=0,
|
| 701 |
+
center=None,
|
| 702 |
+
auto_bound=False):
|
| 703 |
+
self.prob = prob
|
| 704 |
+
assert prob >= 0 and prob <= 1
|
| 705 |
+
if isinstance(degree, (float, int)):
|
| 706 |
+
assert degree > 0, f'degree {degree} should be positive'
|
| 707 |
+
self.degree = (-degree, degree)
|
| 708 |
+
else:
|
| 709 |
+
self.degree = degree
|
| 710 |
+
assert len(self.degree) == 2, f'degree {self.degree} should be a ' \
|
| 711 |
+
f'tuple of (min, max)'
|
| 712 |
+
self.pal_val = pad_val
|
| 713 |
+
self.center = center
|
| 714 |
+
self.auto_bound = auto_bound
|
| 715 |
+
|
| 716 |
+
def __call__(self, results):
|
| 717 |
+
"""Call function to rotate image.
|
| 718 |
+
|
| 719 |
+
Args:
|
| 720 |
+
results (dict): Result dict from loading pipeline.
|
| 721 |
+
|
| 722 |
+
Returns:
|
| 723 |
+
dict: Rotated results.
|
| 724 |
+
"""
|
| 725 |
+
|
| 726 |
+
rotate = True if np.random.rand() < self.prob else False
|
| 727 |
+
degree = np.random.uniform(min(*self.degree), max(*self.degree))
|
| 728 |
+
if rotate:
|
| 729 |
+
# rotate image
|
| 730 |
+
for key in results.get('img_fields', ['img']):
|
| 731 |
+
results[key] = mmcv.imrotate(
|
| 732 |
+
results[key],
|
| 733 |
+
angle=degree,
|
| 734 |
+
border_value=self.pal_val,
|
| 735 |
+
center=self.center,
|
| 736 |
+
auto_bound=self.auto_bound)
|
| 737 |
+
|
| 738 |
+
return results
|
| 739 |
+
|
| 740 |
+
def __repr__(self):
|
| 741 |
+
repr_str = self.__class__.__name__
|
| 742 |
+
repr_str += f'(prob={self.prob}, ' \
|
| 743 |
+
f'degree={self.degree}, ' \
|
| 744 |
+
f'pad_val={self.pal_val}, ' \
|
| 745 |
+
f'center={self.center}, ' \
|
| 746 |
+
f'auto_bound={self.auto_bound})'
|
| 747 |
+
return repr_str
|
| 748 |
+
|
| 749 |
+
|
| 750 |
+
@PIPELINES.register_module()
|
| 751 |
+
class RandomErasing:
|
| 752 |
+
""" Randomly selects a rectangle region in an image and erases its pixels.
|
| 753 |
+
'Random Erasing Data Augmentation' by Zhong et al. See https://arxiv.org/abs/1708.04896
|
| 754 |
+
|
| 755 |
+
Args:
|
| 756 |
+
p: probability that the random erasing operation will be performed.
|
| 757 |
+
scale: range of proportion of erased area against input image.
|
| 758 |
+
ratio: range of aspect ratio of erased area.
|
| 759 |
+
value: erasing value. Default is 0. If a single int, it is used to
|
| 760 |
+
erase all pixels. If a tuple of length 3, it is used to erase
|
| 761 |
+
R, G, B channels respectively.
|
| 762 |
+
If a str of 'random', erasing each pixel with random values.
|
| 763 |
+
inplace: boolean to make this transform inplace. Default set to False.
|
| 764 |
+
|
| 765 |
+
Returns:
|
| 766 |
+
Erased Image.
|
| 767 |
+
"""
|
| 768 |
+
def __init__(self, p=0.5, scale=(0.02, 0.33), ratio=(0.3, 3.3), value=0, inplace=False):
|
| 769 |
+
super().__init__()
|
| 770 |
+
if not isinstance(value, (numbers.Number, str, tuple, list)):
|
| 771 |
+
raise TypeError("Argument value should be either a number or str or a sequence")
|
| 772 |
+
if isinstance(value, str) and value != "random":
|
| 773 |
+
raise ValueError("If value is str, it should be 'random'")
|
| 774 |
+
if not isinstance(scale, (tuple, list)):
|
| 775 |
+
raise TypeError("Scale should be a sequence")
|
| 776 |
+
if not isinstance(ratio, (tuple, list)):
|
| 777 |
+
raise TypeError("Ratio should be a sequence")
|
| 778 |
+
if (scale[0] > scale[1]) or (ratio[0] > ratio[1]):
|
| 779 |
+
warnings.warn("Scale and ratio should be of kind (min, max)")
|
| 780 |
+
if scale[0] < 0 or scale[1] > 1:
|
| 781 |
+
raise ValueError("Scale should be between 0 and 1")
|
| 782 |
+
if p < 0 or p > 1:
|
| 783 |
+
raise ValueError("Random erasing probability should be between 0 and 1")
|
| 784 |
+
|
| 785 |
+
self.p = p
|
| 786 |
+
self.scale = scale
|
| 787 |
+
self.ratio = ratio
|
| 788 |
+
self.value = value
|
| 789 |
+
self.inplace = inplace
|
| 790 |
+
|
| 791 |
+
@staticmethod
|
| 792 |
+
def get_params(
|
| 793 |
+
img: Tensor, scale: Tuple[float, float], ratio: Tuple[float, float], value: Optional[List[float]] = None
|
| 794 |
+
) -> Tuple[int, int, int, int, Tensor]:
|
| 795 |
+
"""Get parameters for ``erase`` for a random erasing.
|
| 796 |
+
|
| 797 |
+
Args:
|
| 798 |
+
img (Tensor): Tensor image to be erased.
|
| 799 |
+
scale (tuple or list): range of proportion of erased area against input image.
|
| 800 |
+
ratio (tuple or list): range of aspect ratio of erased area.
|
| 801 |
+
value (list, optional): erasing value. If None, it is interpreted as "random"
|
| 802 |
+
(erasing each pixel with random values). If ``len(value)`` is 1, it is interpreted as a number,
|
| 803 |
+
i.e. ``value[0]``.
|
| 804 |
+
|
| 805 |
+
Returns:
|
| 806 |
+
tuple: params (i, j, h, w, v) to be passed to ``erase`` for random erasing.
|
| 807 |
+
"""
|
| 808 |
+
img_c, img_h, img_w = img.shape[-3], img.shape[-2], img.shape[-1]
|
| 809 |
+
area = img_h * img_w
|
| 810 |
+
|
| 811 |
+
for _ in range(10):
|
| 812 |
+
erase_area = area * torch.empty(1).uniform_(scale[0], scale[1]).item()
|
| 813 |
+
aspect_ratio = torch.empty(1).uniform_(ratio[0], ratio[1]).item()
|
| 814 |
+
|
| 815 |
+
h = int(round(math.sqrt(erase_area * aspect_ratio)))
|
| 816 |
+
w = int(round(math.sqrt(erase_area / aspect_ratio)))
|
| 817 |
+
if not (h < img_h and w < img_w):
|
| 818 |
+
continue
|
| 819 |
+
|
| 820 |
+
if value is None:
|
| 821 |
+
v = torch.empty([img_c, h, w], dtype=torch.float32).normal_()
|
| 822 |
+
else:
|
| 823 |
+
v = torch.tensor(value)[:, None, None]
|
| 824 |
+
|
| 825 |
+
i = torch.randint(0, img_h - h + 1, size=(1, )).item()
|
| 826 |
+
j = torch.randint(0, img_w - w + 1, size=(1, )).item()
|
| 827 |
+
return i, j, h, w, v
|
| 828 |
+
|
| 829 |
+
# Return original image
|
| 830 |
+
return 0, 0, img_h, img_w, img
|
| 831 |
+
|
| 832 |
+
def __call__(self, results):
|
| 833 |
+
if torch.rand(1) < self.p:
|
| 834 |
+
for key in results.get('img_fields', ['img']):
|
| 835 |
+
img = results[key]
|
| 836 |
+
img = torch.Tensor(img.transpose([2,0,1]))
|
| 837 |
+
# the next process need C, H, W
|
| 838 |
+
|
| 839 |
+
# cast self.value to script acceptable type
|
| 840 |
+
if isinstance(self.value, (int, float)):
|
| 841 |
+
value = [self.value, ]
|
| 842 |
+
elif isinstance(self.value, str):
|
| 843 |
+
value = None
|
| 844 |
+
elif isinstance(self.value, tuple):
|
| 845 |
+
value = list(self.value)
|
| 846 |
+
else:
|
| 847 |
+
value = self.value
|
| 848 |
+
|
| 849 |
+
if value is not None and not (len(value) in (1, img.shape[-3])):
|
| 850 |
+
raise ValueError(
|
| 851 |
+
"If value is a sequence, it should have either a single value or "
|
| 852 |
+
"{} (number of input channels)".format(img.shape[-3])
|
| 853 |
+
)
|
| 854 |
+
|
| 855 |
+
x, y, h, w, v = self.get_params(img, scale=self.scale, ratio=self.ratio, value=value)
|
| 856 |
+
results[key] = TVF.erase(img, x, y, h, w, v, self.inplace).numpy().transpose([1,2,0])
|
| 857 |
+
return results
|
| 858 |
+
|
| 859 |
+
|
| 860 |
+
@PIPELINES.register_module()
|
| 861 |
+
class ColorJitter:
|
| 862 |
+
def __init__(self, brightness=0, contrast=0, saturation=0, hue=0):
|
| 863 |
+
self.func = _transforms.ColorJitter(brightness, contrast, saturation, hue)
|
| 864 |
+
|
| 865 |
+
def __call__(self, results):
|
| 866 |
+
for key in results.get('img_fields', ['img']):
|
| 867 |
+
img = results[key]
|
| 868 |
+
img = Image.fromarray(img)
|
| 869 |
+
results[key] = np.array(self.func(img))
|
| 870 |
+
return results
|
| 871 |
+
|
| 872 |
+
def __repr__(self):
|
| 873 |
+
return self.func.__repr__()
|
| 874 |
+
|
| 875 |
+
|
| 876 |
+
@PIPELINES.register_module()
|
| 877 |
+
class HistogramEqualization:
|
| 878 |
+
|
| 879 |
+
def __call__(self, results):
|
| 880 |
+
for key in results.get('img_fields', ['img']):
|
| 881 |
+
img = results[key]
|
| 882 |
+
# convert from RGB color-space to YCrCb
|
| 883 |
+
ycrcb_img = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
|
| 884 |
+
|
| 885 |
+
# equalize the histogram of the Y channel
|
| 886 |
+
ycrcb_img[:, :, 0] = cv2.equalizeHist(ycrcb_img[:, :, 0])
|
| 887 |
+
|
| 888 |
+
# convert back to RGB color-space from YCrCb
|
| 889 |
+
equalized_img = cv2.cvtColor(ycrcb_img, cv2.COLOR_YCrCb2BGR)
|
| 890 |
+
results[key] = equalized_img
|
| 891 |
+
return results
|
| 892 |
+
|
| 893 |
+
|
| 894 |
+
@PIPELINES.register_module()
|
| 895 |
+
class TorchNormalize:
|
| 896 |
+
"""
|
| 897 |
+
Normalize in torchvision
|
| 898 |
+
"""
|
| 899 |
+
def __init__(self, mean, std, inplace=False):
|
| 900 |
+
self.mean = mean
|
| 901 |
+
self.std = std
|
| 902 |
+
self.inplace = inplace
|
| 903 |
+
|
| 904 |
+
def __call__(self, results):
|
| 905 |
+
"""
|
| 906 |
+
Args:
|
| 907 |
+
tensor (Tensor): Tensor image of size (C, H, W) to be normalized.
|
| 908 |
+
|
| 909 |
+
Returns:
|
| 910 |
+
Tensor: Normalized Tensor image.
|
| 911 |
+
"""
|
| 912 |
+
for key in results.get('img_fields', ['img']):
|
| 913 |
+
img = results[key]
|
| 914 |
+
results[key] = _transforms.functional.normalize(img, self.mean, self.std, self.inplace)
|
| 915 |
+
return results
|
| 916 |
+
|
| 917 |
+
def __repr__(self):
|
| 918 |
+
return self.__class__.__name__ + '(mean={0}, std={1})'.format(self.mean, self.std)
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/raf.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import random
|
| 3 |
+
from typing import Any, Dict
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
from mmcls.models.losses import accuracy, f1_score, precision, recall
|
| 7 |
+
from mmcls.models.losses.eval_metrics import class_accuracy
|
| 8 |
+
|
| 9 |
+
from .base_dataset import BaseDataset
|
| 10 |
+
from .builder import DATASETS
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def has_file_allowed_extension(filename, extensions):
|
| 14 |
+
"""Checks if a file is an allowed extension.
|
| 15 |
+
|
| 16 |
+
Args:
|
| 17 |
+
filename (string): path to a file
|
| 18 |
+
|
| 19 |
+
Returns:
|
| 20 |
+
bool: True if the filename ends with a known image extension
|
| 21 |
+
"""
|
| 22 |
+
filename_lower = filename.lower()
|
| 23 |
+
return any(filename_lower.endswith(ext) for ext in extensions)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def find_folders(root):
|
| 27 |
+
"""Find classes by folders under a root.
|
| 28 |
+
|
| 29 |
+
Args:
|
| 30 |
+
root (string): root directory of folders
|
| 31 |
+
|
| 32 |
+
Returns:
|
| 33 |
+
folder_to_idx (dict): the map from folder name to class idx
|
| 34 |
+
"""
|
| 35 |
+
folders = [
|
| 36 |
+
d for d in os.listdir(root) if os.path.isdir(os.path.join(root, d))
|
| 37 |
+
]
|
| 38 |
+
folders.sort()
|
| 39 |
+
folder_to_idx = {folders[i]: i for i in range(len(folders))}
|
| 40 |
+
return folder_to_idx
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def get_samples(root, folder_to_idx, extensions):
|
| 44 |
+
"""Make dataset by walking all images under a root.
|
| 45 |
+
|
| 46 |
+
Args:
|
| 47 |
+
root (string): root directory of folders
|
| 48 |
+
folder_to_idx (dict): the map from class name to class idx
|
| 49 |
+
extensions (tuple): allowed extensions
|
| 50 |
+
|
| 51 |
+
Returns:
|
| 52 |
+
samples (list): a list of tuple where each element is (image, label)
|
| 53 |
+
"""
|
| 54 |
+
samples = []
|
| 55 |
+
root = os.path.expanduser(root)
|
| 56 |
+
for folder_name in sorted(os.listdir(root)):
|
| 57 |
+
_dir = os.path.join(root, folder_name)
|
| 58 |
+
if not os.path.isdir(_dir):
|
| 59 |
+
continue
|
| 60 |
+
|
| 61 |
+
for _, _, fns in sorted(os.walk(_dir)):
|
| 62 |
+
for fn in sorted(fns):
|
| 63 |
+
if has_file_allowed_extension(fn, extensions):
|
| 64 |
+
path = os.path.join(folder_name, fn)
|
| 65 |
+
item = (path, folder_to_idx[folder_name])
|
| 66 |
+
samples.append(item)
|
| 67 |
+
return samples
|
| 68 |
+
|
| 69 |
+
# 0 1 2 3 4 5 6 7
|
| 70 |
+
FER_CLASSES = ['Anger', 'Disgust', 'Fear', 'Sadness', 'Happiness', 'Surprise', 'Neutral', 'Contempt']
|
| 71 |
+
|
| 72 |
+
def convert2coarse_label(i:int):
|
| 73 |
+
"""The first four are negative"""
|
| 74 |
+
if i <= 3:
|
| 75 |
+
return 0
|
| 76 |
+
return i - 3
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def gen_class_map(dataset_class):
|
| 80 |
+
"""
|
| 81 |
+
generate the convert map from DATASET_CLASSES to FER_CLASSES
|
| 82 |
+
"""
|
| 83 |
+
convert_map = []
|
| 84 |
+
for i in dataset_class:
|
| 85 |
+
convert_map.append(FER_CLASSES.index(i))
|
| 86 |
+
assert sum(convert_map) == sum([i for i in range(len(dataset_class))])
|
| 87 |
+
return convert_map
|
| 88 |
+
|
| 89 |
+
@DATASETS.register_module()
|
| 90 |
+
class RAF(BaseDataset):
|
| 91 |
+
|
| 92 |
+
IMG_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.ppm', '.bmp', '.pgm', '.tif')
|
| 93 |
+
DATASET_CLASSES = [
|
| 94 |
+
'Surprise',
|
| 95 |
+
'Fear',
|
| 96 |
+
'Disgust',
|
| 97 |
+
'Happiness',
|
| 98 |
+
'Sadness',
|
| 99 |
+
'Anger',
|
| 100 |
+
'Neutral'
|
| 101 |
+
]
|
| 102 |
+
CLASSES = FER_CLASSES[:7]
|
| 103 |
+
|
| 104 |
+
@staticmethod
|
| 105 |
+
def convert_gt_label(i:int):
|
| 106 |
+
"""# dataset -> FER_CLASSES"""
|
| 107 |
+
convert_table = (5, 2, 1, 4, 3, 0, 6)
|
| 108 |
+
assert sum(convert_table) == sum([i for i in range(7)])
|
| 109 |
+
return convert_table[i]
|
| 110 |
+
|
| 111 |
+
def load_annotations(self):
|
| 112 |
+
if self.ann_file is None:
|
| 113 |
+
folder_to_idx = find_folders(self.data_prefix)
|
| 114 |
+
samples = get_samples(
|
| 115 |
+
self.data_prefix,
|
| 116 |
+
folder_to_idx,
|
| 117 |
+
extensions=self.IMG_EXTENSIONS)
|
| 118 |
+
if len(samples) == 0:
|
| 119 |
+
raise (RuntimeError('Found 0 files in subfolders of: '
|
| 120 |
+
f'{self.data_prefix}. '
|
| 121 |
+
'Supported extensions are: '
|
| 122 |
+
f'{",".join(self.IMG_EXTENSIONS)}'))
|
| 123 |
+
|
| 124 |
+
self.folder_to_idx = folder_to_idx
|
| 125 |
+
elif isinstance(self.ann_file, str):
|
| 126 |
+
with open(self.ann_file) as f:
|
| 127 |
+
samples = [x.strip().split(' ') for x in f.readlines()]
|
| 128 |
+
samples = [[i[0].replace('_aligned', ''), i[1]] for i in samples]
|
| 129 |
+
else:
|
| 130 |
+
raise TypeError('ann_file must be a str or None')
|
| 131 |
+
self.samples = samples
|
| 132 |
+
|
| 133 |
+
data_infos = []
|
| 134 |
+
for filename, gt_label in self.samples:
|
| 135 |
+
info = {'img_prefix': self.data_prefix}
|
| 136 |
+
info['img_info'] = {'filename': filename}
|
| 137 |
+
gt_label = int(gt_label) - 1
|
| 138 |
+
gt_label = self.convert_gt_label(gt_label)
|
| 139 |
+
coarse_label = convert2coarse_label(gt_label)
|
| 140 |
+
info['gt_label'] = np.array(gt_label, dtype=np.int64)
|
| 141 |
+
info['coarse_label'] = np.array(coarse_label, dtype=np.int64)
|
| 142 |
+
data_infos.append(info)
|
| 143 |
+
return data_infos
|
| 144 |
+
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/samplers/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .distributed_sampler import DistributedSampler
|
| 2 |
+
|
| 3 |
+
__all__ = ['DistributedSampler']
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/samplers/distributed_sampler.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch.utils.data import DistributedSampler as _DistributedSampler
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class DistributedSampler(_DistributedSampler):
|
| 6 |
+
|
| 7 |
+
def __init__(self,
|
| 8 |
+
dataset,
|
| 9 |
+
num_replicas=None,
|
| 10 |
+
rank=None,
|
| 11 |
+
shuffle=True,
|
| 12 |
+
round_up=True):
|
| 13 |
+
super().__init__(dataset, num_replicas=num_replicas, rank=rank)
|
| 14 |
+
self.shuffle = shuffle
|
| 15 |
+
self.round_up = round_up
|
| 16 |
+
if self.round_up:
|
| 17 |
+
self.total_size = self.num_samples * self.num_replicas
|
| 18 |
+
else:
|
| 19 |
+
self.total_size = len(self.dataset)
|
| 20 |
+
|
| 21 |
+
def __iter__(self):
|
| 22 |
+
# deterministically shuffle based on epoch
|
| 23 |
+
if self.shuffle:
|
| 24 |
+
g = torch.Generator()
|
| 25 |
+
g.manual_seed(self.epoch)
|
| 26 |
+
indices = torch.randperm(len(self.dataset), generator=g).tolist()
|
| 27 |
+
else:
|
| 28 |
+
indices = torch.arange(len(self.dataset)).tolist()
|
| 29 |
+
|
| 30 |
+
# add extra samples to make it evenly divisible
|
| 31 |
+
if self.round_up:
|
| 32 |
+
indices = (
|
| 33 |
+
indices *
|
| 34 |
+
int(self.total_size / len(indices) + 1))[:self.total_size]
|
| 35 |
+
assert len(indices) == self.total_size
|
| 36 |
+
|
| 37 |
+
# subsample
|
| 38 |
+
indices = indices[self.rank:self.total_size:self.num_replicas]
|
| 39 |
+
if self.round_up:
|
| 40 |
+
assert len(indices) == self.num_samples
|
| 41 |
+
|
| 42 |
+
return iter(indices)
|
CAGE_expression_inference-apvit/apvit_mmcls/datasets/utils.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gzip
|
| 2 |
+
import hashlib
|
| 3 |
+
import os
|
| 4 |
+
import os.path
|
| 5 |
+
import shutil
|
| 6 |
+
import tarfile
|
| 7 |
+
import urllib.error
|
| 8 |
+
import urllib.request
|
| 9 |
+
import zipfile
|
| 10 |
+
|
| 11 |
+
__all__ = ['rm_suffix', 'check_integrity', 'download_and_extract_archive']
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def rm_suffix(s, suffix=None):
|
| 15 |
+
if suffix is None:
|
| 16 |
+
return s[:s.rfind('.')]
|
| 17 |
+
else:
|
| 18 |
+
return s[:s.rfind(suffix)]
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def calculate_md5(fpath, chunk_size=1024 * 1024):
|
| 22 |
+
md5 = hashlib.md5()
|
| 23 |
+
with open(fpath, 'rb') as f:
|
| 24 |
+
for chunk in iter(lambda: f.read(chunk_size), b''):
|
| 25 |
+
md5.update(chunk)
|
| 26 |
+
return md5.hexdigest()
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def check_md5(fpath, md5, **kwargs):
|
| 30 |
+
return md5 == calculate_md5(fpath, **kwargs)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def check_integrity(fpath, md5=None):
|
| 34 |
+
if not os.path.isfile(fpath):
|
| 35 |
+
return False
|
| 36 |
+
if md5 is None:
|
| 37 |
+
return True
|
| 38 |
+
return check_md5(fpath, md5)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def download_url_to_file(url, fpath):
|
| 42 |
+
with urllib.request.urlopen(url) as resp, open(fpath, 'wb') as of:
|
| 43 |
+
shutil.copyfileobj(resp, of)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def download_url(url, root, filename=None, md5=None):
|
| 47 |
+
"""Download a file from a url and place it in root.
|
| 48 |
+
|
| 49 |
+
Args:
|
| 50 |
+
url (str): URL to download file from.
|
| 51 |
+
root (str): Directory to place downloaded file in.
|
| 52 |
+
filename (str | None): Name to save the file under.
|
| 53 |
+
If filename is None, use the basename of the URL.
|
| 54 |
+
md5 (str | None): MD5 checksum of the download.
|
| 55 |
+
If md5 is None, download without md5 check.
|
| 56 |
+
"""
|
| 57 |
+
root = os.path.expanduser(root)
|
| 58 |
+
if not filename:
|
| 59 |
+
filename = os.path.basename(url)
|
| 60 |
+
fpath = os.path.join(root, filename)
|
| 61 |
+
|
| 62 |
+
os.makedirs(root, exist_ok=True)
|
| 63 |
+
|
| 64 |
+
if check_integrity(fpath, md5):
|
| 65 |
+
print(f'Using downloaded and verified file: {fpath}')
|
| 66 |
+
else:
|
| 67 |
+
try:
|
| 68 |
+
print(f'Downloading {url} to {fpath}')
|
| 69 |
+
download_url_to_file(url, fpath)
|
| 70 |
+
except (urllib.error.URLError, IOError) as e:
|
| 71 |
+
if url[:5] == 'https':
|
| 72 |
+
url = url.replace('https:', 'http:')
|
| 73 |
+
print('Failed download. Trying https -> http instead.'
|
| 74 |
+
f' Downloading {url} to {fpath}')
|
| 75 |
+
download_url_to_file(url, fpath)
|
| 76 |
+
else:
|
| 77 |
+
raise e
|
| 78 |
+
# check integrity of downloaded file
|
| 79 |
+
if not check_integrity(fpath, md5):
|
| 80 |
+
raise RuntimeError('File not found or corrupted.')
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def _is_tarxz(filename):
|
| 84 |
+
return filename.endswith('.tar.xz')
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def _is_tar(filename):
|
| 88 |
+
return filename.endswith('.tar')
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def _is_targz(filename):
|
| 92 |
+
return filename.endswith('.tar.gz')
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def _is_tgz(filename):
|
| 96 |
+
return filename.endswith('.tgz')
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def _is_gzip(filename):
|
| 100 |
+
return filename.endswith('.gz') and not filename.endswith('.tar.gz')
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def _is_zip(filename):
|
| 104 |
+
return filename.endswith('.zip')
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def extract_archive(from_path, to_path=None, remove_finished=False):
|
| 108 |
+
if to_path is None:
|
| 109 |
+
to_path = os.path.dirname(from_path)
|
| 110 |
+
|
| 111 |
+
if _is_tar(from_path):
|
| 112 |
+
with tarfile.open(from_path, 'r') as tar:
|
| 113 |
+
tar.extractall(path=to_path)
|
| 114 |
+
elif _is_targz(from_path) or _is_tgz(from_path):
|
| 115 |
+
with tarfile.open(from_path, 'r:gz') as tar:
|
| 116 |
+
tar.extractall(path=to_path)
|
| 117 |
+
elif _is_tarxz(from_path):
|
| 118 |
+
with tarfile.open(from_path, 'r:xz') as tar:
|
| 119 |
+
tar.extractall(path=to_path)
|
| 120 |
+
elif _is_gzip(from_path):
|
| 121 |
+
to_path = os.path.join(
|
| 122 |
+
to_path,
|
| 123 |
+
os.path.splitext(os.path.basename(from_path))[0])
|
| 124 |
+
with open(to_path, 'wb') as out_f, gzip.GzipFile(from_path) as zip_f:
|
| 125 |
+
out_f.write(zip_f.read())
|
| 126 |
+
elif _is_zip(from_path):
|
| 127 |
+
with zipfile.ZipFile(from_path, 'r') as z:
|
| 128 |
+
z.extractall(to_path)
|
| 129 |
+
else:
|
| 130 |
+
raise ValueError(f'Extraction of {from_path} not supported')
|
| 131 |
+
|
| 132 |
+
if remove_finished:
|
| 133 |
+
os.remove(from_path)
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
def download_and_extract_archive(url,
|
| 137 |
+
download_root,
|
| 138 |
+
extract_root=None,
|
| 139 |
+
filename=None,
|
| 140 |
+
md5=None,
|
| 141 |
+
remove_finished=False):
|
| 142 |
+
download_root = os.path.expanduser(download_root)
|
| 143 |
+
if extract_root is None:
|
| 144 |
+
extract_root = download_root
|
| 145 |
+
if not filename:
|
| 146 |
+
filename = os.path.basename(url)
|
| 147 |
+
|
| 148 |
+
download_url(url, download_root, filename, md5)
|
| 149 |
+
|
| 150 |
+
archive = os.path.join(download_root, filename)
|
| 151 |
+
print(f'Extracting {archive} to {extract_root}')
|
| 152 |
+
extract_archive(archive, extract_root, remove_finished)
|
CAGE_expression_inference-apvit/apvit_mmcls/models/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .backbones import * # noqa: F401,F403
|
| 2 |
+
from .builder import (BACKBONES, CLASSIFIERS, HEADS, LOSSES, NECKS,
|
| 3 |
+
build_backbone, build_classifier, build_head, build_loss,
|
| 4 |
+
build_neck)
|
| 5 |
+
from .classifiers import * # noqa: F401,F403
|
| 6 |
+
from .heads import * # noqa: F401,F403
|
| 7 |
+
from .losses import * # noqa: F401,F403
|
| 8 |
+
from .necks import * # noqa: F401,F403
|
| 9 |
+
|
| 10 |
+
__all__ = [
|
| 11 |
+
'BACKBONES', 'HEADS', 'NECKS', 'LOSSES', 'CLASSIFIERS', 'build_backbone',
|
| 12 |
+
'build_head', 'build_neck', 'build_loss', 'build_classifier'
|
| 13 |
+
]
|
CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/__init__.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .alexnet import AlexNet
|
| 2 |
+
from .lenet import LeNet5
|
| 3 |
+
from .mobilenet_v2 import MobileNetV2
|
| 4 |
+
from .mobilenet_v3 import MobileNetv3
|
| 5 |
+
from .regnet import RegNet
|
| 6 |
+
from .resnest import ResNeSt
|
| 7 |
+
from .resnet import ResNet, ResNetV1d
|
| 8 |
+
from .resnet_cifar import ResNet_CIFAR
|
| 9 |
+
from .resnext import ResNeXt
|
| 10 |
+
from .seresnet import SEResNet
|
| 11 |
+
from .seresnext import SEResNeXt
|
| 12 |
+
from .shufflenet_v1 import ShuffleNetV1
|
| 13 |
+
from .shufflenet_v2 import ShuffleNetV2
|
| 14 |
+
from .vgg import VGG
|
| 15 |
+
from .irse import IRSE
|
| 16 |
+
from .irse_nopadding import IRSENoPadding
|
| 17 |
+
from .mobilefacenet import MobileFaceNet
|
| 18 |
+
from ..vit.vit_origin import VisionTransformerOrigin
|
| 19 |
+
from ..vit.vit_siam_merge import PoolingViT
|
| 20 |
+
from .t2t_vit import T2T_ViT, T2T_ViTPooling
|
| 21 |
+
from .iresnet import IResNet
|
| 22 |
+
from .vtff import VTFF, MViT
|
| 23 |
+
|
| 24 |
+
__all__ = [
|
| 25 |
+
'VTFF', 'MViT',
|
| 26 |
+
'LeNet5', 'AlexNet', 'VGG', 'RegNet', 'ResNet', 'ResNeXt', 'ResNetV1d',
|
| 27 |
+
'ResNeSt', 'ResNet_CIFAR', 'SEResNet', 'SEResNeXt', 'ShuffleNetV1',
|
| 28 |
+
'ShuffleNetV2', 'MobileNetV2', 'MobileNetv3', 'IRSE',
|
| 29 |
+
'MobileFaceNet',
|
| 30 |
+
'T2T_ViT', 'T2T_ViTPooling', 'VisionTransformerOrigin', 'PoolingViT',
|
| 31 |
+
'IRSENoPadding', 'IResNet',
|
| 32 |
+
]
|
CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/alexnet.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch.nn as nn
|
| 2 |
+
|
| 3 |
+
from ..builder import BACKBONES
|
| 4 |
+
from .base_backbone import BaseBackbone
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
@BACKBONES.register_module()
|
| 8 |
+
class AlexNet(BaseBackbone):
|
| 9 |
+
"""`AlexNet <https://en.wikipedia.org/wiki/AlexNet>`_ backbone.
|
| 10 |
+
|
| 11 |
+
The input for AlexNet is a 224x224 RGB image.
|
| 12 |
+
|
| 13 |
+
Args:
|
| 14 |
+
num_classes (int): number of classes for classification.
|
| 15 |
+
The default value is -1, which uses the backbone as
|
| 16 |
+
a feature extractor without the top classifier.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
def __init__(self, num_classes=-1):
|
| 20 |
+
super(AlexNet, self).__init__()
|
| 21 |
+
self.num_classes = num_classes
|
| 22 |
+
self.features = nn.Sequential(
|
| 23 |
+
nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
|
| 24 |
+
nn.ReLU(inplace=True),
|
| 25 |
+
nn.MaxPool2d(kernel_size=3, stride=2),
|
| 26 |
+
nn.Conv2d(64, 192, kernel_size=5, padding=2),
|
| 27 |
+
nn.ReLU(inplace=True),
|
| 28 |
+
nn.MaxPool2d(kernel_size=3, stride=2),
|
| 29 |
+
nn.Conv2d(192, 384, kernel_size=3, padding=1),
|
| 30 |
+
nn.ReLU(inplace=True),
|
| 31 |
+
nn.Conv2d(384, 256, kernel_size=3, padding=1),
|
| 32 |
+
nn.ReLU(inplace=True),
|
| 33 |
+
nn.Conv2d(256, 256, kernel_size=3, padding=1),
|
| 34 |
+
nn.ReLU(inplace=True),
|
| 35 |
+
nn.MaxPool2d(kernel_size=3, stride=2),
|
| 36 |
+
)
|
| 37 |
+
if self.num_classes > 0:
|
| 38 |
+
self.classifier = nn.Sequential(
|
| 39 |
+
nn.Dropout(),
|
| 40 |
+
nn.Linear(256 * 6 * 6, 4096),
|
| 41 |
+
nn.ReLU(inplace=True),
|
| 42 |
+
nn.Dropout(),
|
| 43 |
+
nn.Linear(4096, 4096),
|
| 44 |
+
nn.ReLU(inplace=True),
|
| 45 |
+
nn.Linear(4096, num_classes),
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
def forward(self, x):
|
| 49 |
+
|
| 50 |
+
x = self.features(x)
|
| 51 |
+
if self.num_classes > 0:
|
| 52 |
+
x = x.view(x.size(0), 256 * 6 * 6)
|
| 53 |
+
x = self.classifier(x)
|
| 54 |
+
|
| 55 |
+
return x
|
CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/base_backbone.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from abc import ABCMeta, abstractmethod
|
| 2 |
+
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
from mmcv.runner import load_checkpoint
|
| 5 |
+
from mmcv.utils import get_logger
|
| 6 |
+
# from mmcls.utils import get_root_logger
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class BaseBackbone(nn.Module, metaclass=ABCMeta):
|
| 10 |
+
"""Base backbone.
|
| 11 |
+
|
| 12 |
+
This class defines the basic functions of a backbone.
|
| 13 |
+
Any backbone that inherits this class should at least
|
| 14 |
+
define its own `forward` function.
|
| 15 |
+
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
def __init__(self):
|
| 19 |
+
super(BaseBackbone, self).__init__()
|
| 20 |
+
|
| 21 |
+
def init_weights(self, pretrained=None):
|
| 22 |
+
"""Init backbone weights
|
| 23 |
+
|
| 24 |
+
Args:
|
| 25 |
+
pretrained (str | None): If pretrained is a string, then it
|
| 26 |
+
initializes backbone weights by loading the pretrained
|
| 27 |
+
checkpoint. If pretrained is None, then it follows default
|
| 28 |
+
initializer or customized initializer in subclasses.
|
| 29 |
+
"""
|
| 30 |
+
if isinstance(pretrained, str):
|
| 31 |
+
logger = get_logger('mmcv')
|
| 32 |
+
logger.warning(f'{self.__class__.__name__} load pretrain from {pretrained}')
|
| 33 |
+
load_checkpoint(self, pretrained, strict=False, logger=logger, map_location='cpu')
|
| 34 |
+
elif pretrained is None:
|
| 35 |
+
# use default initializer or customized initializer in subclasses
|
| 36 |
+
pass
|
| 37 |
+
else:
|
| 38 |
+
raise TypeError('pretrained must be a str or None.'
|
| 39 |
+
f' But received {type(pretrained)}.')
|
| 40 |
+
|
| 41 |
+
@abstractmethod
|
| 42 |
+
def forward(self, x):
|
| 43 |
+
"""Forward computation
|
| 44 |
+
|
| 45 |
+
Args:
|
| 46 |
+
x (tensor | tuple[tensor]): x could be a Torch.tensor or a tuple of
|
| 47 |
+
Torch.tensor, containing input data for forward computation.
|
| 48 |
+
"""
|
| 49 |
+
pass
|
| 50 |
+
|
| 51 |
+
def train(self, mode=True):
|
| 52 |
+
"""Set module status before forward computation
|
| 53 |
+
|
| 54 |
+
Args:
|
| 55 |
+
mode (bool): Whether it is train_mode or test_mode
|
| 56 |
+
"""
|
| 57 |
+
super(BaseBackbone, self).train(mode)
|
CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/iresnet.py
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import warnings
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
from torch.nn import Linear, Conv2d, BatchNorm1d, BatchNorm2d, PReLU, ReLU, Sigmoid, Dropout, MaxPool2d, \
|
| 4 |
+
AdaptiveAvgPool2d, Sequential, Module
|
| 5 |
+
from collections import namedtuple
|
| 6 |
+
import torch
|
| 7 |
+
from mmcv.runner import load_state_dict
|
| 8 |
+
from mmcls.utils import get_root_logger
|
| 9 |
+
|
| 10 |
+
from ..builder import BACKBONES
|
| 11 |
+
from .base_backbone import BaseBackbone
|
| 12 |
+
|
| 13 |
+
__all__ = ['iresnet18', 'iresnet34', 'iresnet50', 'iresnet100', 'iresnet200']
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1):
|
| 17 |
+
"""3x3 convolution with padding"""
|
| 18 |
+
return nn.Conv2d(in_planes,
|
| 19 |
+
out_planes,
|
| 20 |
+
kernel_size=3,
|
| 21 |
+
stride=stride,
|
| 22 |
+
padding=dilation,
|
| 23 |
+
groups=groups,
|
| 24 |
+
bias=False,
|
| 25 |
+
dilation=dilation)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def conv1x1(in_planes, out_planes, stride=1):
|
| 29 |
+
"""1x1 convolution"""
|
| 30 |
+
return nn.Conv2d(in_planes,
|
| 31 |
+
out_planes,
|
| 32 |
+
kernel_size=1,
|
| 33 |
+
stride=stride,
|
| 34 |
+
bias=False)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
class IBasicBlock(nn.Module):
|
| 38 |
+
expansion = 1
|
| 39 |
+
def __init__(self, inplanes, planes, stride=1, downsample=None,
|
| 40 |
+
groups=1, base_width=64, dilation=1):
|
| 41 |
+
super(IBasicBlock, self).__init__()
|
| 42 |
+
if groups != 1 or base_width != 64:
|
| 43 |
+
raise ValueError('BasicBlock only supports groups=1 and base_width=64')
|
| 44 |
+
if dilation > 1:
|
| 45 |
+
raise NotImplementedError("Dilation > 1 not supported in BasicBlock")
|
| 46 |
+
self.bn1 = nn.BatchNorm2d(inplanes, eps=1e-05,)
|
| 47 |
+
self.conv1 = conv3x3(inplanes, planes)
|
| 48 |
+
self.bn2 = nn.BatchNorm2d(planes, eps=1e-05,)
|
| 49 |
+
self.prelu = nn.PReLU(planes)
|
| 50 |
+
self.conv2 = conv3x3(planes, planes, stride)
|
| 51 |
+
self.bn3 = nn.BatchNorm2d(planes, eps=1e-05,)
|
| 52 |
+
self.downsample = downsample
|
| 53 |
+
self.stride = stride
|
| 54 |
+
|
| 55 |
+
def forward(self, x):
|
| 56 |
+
identity = x
|
| 57 |
+
out = self.bn1(x)
|
| 58 |
+
out = self.conv1(out)
|
| 59 |
+
out = self.bn2(out)
|
| 60 |
+
out = self.prelu(out)
|
| 61 |
+
out = self.conv2(out)
|
| 62 |
+
out = self.bn3(out)
|
| 63 |
+
if self.downsample is not None:
|
| 64 |
+
identity = self.downsample(x)
|
| 65 |
+
out += identity
|
| 66 |
+
return out
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
layers_map = {
|
| 70 |
+
18: [2, 2, 2, 2],
|
| 71 |
+
34: [3, 4, 6, 3],
|
| 72 |
+
50: [3, 4, 14, 3],
|
| 73 |
+
100: [3, 13, 30, 3],
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
@BACKBONES.register_module()
|
| 78 |
+
class IResNet(nn.Module):
|
| 79 |
+
fc_scale = 7 * 7
|
| 80 |
+
def __init__(self,
|
| 81 |
+
depth, block=None, dropout=0, num_features=512, zero_init_residual=False,
|
| 82 |
+
groups=1, width_per_group=64, replace_stride_with_dilation=None, fp16=False, pretrained=None):
|
| 83 |
+
super(IResNet, self).__init__()
|
| 84 |
+
block = IBasicBlock
|
| 85 |
+
layers = layers_map[depth]
|
| 86 |
+
self.fp16 = fp16
|
| 87 |
+
self.inplanes = 64
|
| 88 |
+
self.dilation = 1
|
| 89 |
+
if replace_stride_with_dilation is None:
|
| 90 |
+
replace_stride_with_dilation = [False, False, False]
|
| 91 |
+
if len(replace_stride_with_dilation) != 3:
|
| 92 |
+
raise ValueError("replace_stride_with_dilation should be None "
|
| 93 |
+
"or a 3-element tuple, got {}".format(replace_stride_with_dilation))
|
| 94 |
+
self.groups = groups
|
| 95 |
+
self.base_width = width_per_group
|
| 96 |
+
self.conv1 = nn.Conv2d(3, self.inplanes, kernel_size=3, stride=1, padding=1, bias=False)
|
| 97 |
+
self.bn1 = nn.BatchNorm2d(self.inplanes, eps=1e-05)
|
| 98 |
+
self.prelu = nn.PReLU(self.inplanes)
|
| 99 |
+
self.layer1 = self._make_layer(block, 64, layers[0], stride=2)
|
| 100 |
+
self.layer2 = self._make_layer(block,
|
| 101 |
+
128,
|
| 102 |
+
layers[1],
|
| 103 |
+
stride=2,
|
| 104 |
+
dilate=replace_stride_with_dilation[0])
|
| 105 |
+
self.layer3 = self._make_layer(block,
|
| 106 |
+
256,
|
| 107 |
+
layers[2],
|
| 108 |
+
stride=2,
|
| 109 |
+
dilate=replace_stride_with_dilation[1])
|
| 110 |
+
# self.layer4 = self._make_layer(block,
|
| 111 |
+
# 512,
|
| 112 |
+
# layers[3],
|
| 113 |
+
# stride=2,
|
| 114 |
+
# dilate=replace_stride_with_dilation[2])
|
| 115 |
+
# self.bn2 = nn.BatchNorm2d(512 * block.expansion, eps=1e-05,)
|
| 116 |
+
# self.dropout = nn.Dropout(p=dropout, inplace=True)
|
| 117 |
+
# self.fc = nn.Linear(512 * block.expansion * self.fc_scale, num_features)
|
| 118 |
+
# self.features = nn.BatchNorm1d(num_features, eps=1e-05)
|
| 119 |
+
# nn.init.constant_(self.features.weight, 1.0)
|
| 120 |
+
# self.features.weight.requires_grad = False
|
| 121 |
+
if pretrained:
|
| 122 |
+
self.init_weights(pretrained)
|
| 123 |
+
else:
|
| 124 |
+
for m in self.modules():
|
| 125 |
+
if isinstance(m, nn.Conv2d):
|
| 126 |
+
nn.init.normal_(m.weight, 0, 0.1)
|
| 127 |
+
elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):
|
| 128 |
+
nn.init.constant_(m.weight, 1)
|
| 129 |
+
nn.init.constant_(m.bias, 0)
|
| 130 |
+
|
| 131 |
+
if zero_init_residual:
|
| 132 |
+
for m in self.modules():
|
| 133 |
+
if isinstance(m, IBasicBlock):
|
| 134 |
+
nn.init.constant_(m.bn2.weight, 0)
|
| 135 |
+
|
| 136 |
+
def init_weights(self, pretrained):
|
| 137 |
+
logger = get_root_logger()
|
| 138 |
+
logger.warning(f'{self.__class__.__name__} load pretrain from {pretrained}')
|
| 139 |
+
state_dict = torch.load(pretrained, map_location='cpu')
|
| 140 |
+
if 'state_dict' in state_dict:
|
| 141 |
+
state_dict = state_dict['state_dict']
|
| 142 |
+
|
| 143 |
+
load_state_dict(self, state_dict, strict=False, logger=logger)
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def _make_layer(self, block, planes, blocks, stride=1, dilate=False):
|
| 147 |
+
downsample = None
|
| 148 |
+
previous_dilation = self.dilation
|
| 149 |
+
if dilate:
|
| 150 |
+
self.dilation *= stride
|
| 151 |
+
stride = 1
|
| 152 |
+
if stride != 1 or self.inplanes != planes * block.expansion:
|
| 153 |
+
downsample = nn.Sequential(
|
| 154 |
+
conv1x1(self.inplanes, planes * block.expansion, stride),
|
| 155 |
+
nn.BatchNorm2d(planes * block.expansion, eps=1e-05, ),
|
| 156 |
+
)
|
| 157 |
+
layers = []
|
| 158 |
+
layers.append(
|
| 159 |
+
block(self.inplanes, planes, stride, downsample, self.groups,
|
| 160 |
+
self.base_width, previous_dilation))
|
| 161 |
+
self.inplanes = planes * block.expansion
|
| 162 |
+
for _ in range(1, blocks):
|
| 163 |
+
layers.append(
|
| 164 |
+
block(self.inplanes,
|
| 165 |
+
planes,
|
| 166 |
+
groups=self.groups,
|
| 167 |
+
base_width=self.base_width,
|
| 168 |
+
dilation=self.dilation))
|
| 169 |
+
|
| 170 |
+
return nn.Sequential(*layers)
|
| 171 |
+
|
| 172 |
+
def forward(self, x):
|
| 173 |
+
# with torch.cuda.amp.autocast(self.fp16):
|
| 174 |
+
x = self.conv1(x)
|
| 175 |
+
x = self.bn1(x)
|
| 176 |
+
x = self.prelu(x)
|
| 177 |
+
x = self.layer1(x)
|
| 178 |
+
x = self.layer2(x)
|
| 179 |
+
x = self.layer3(x)
|
| 180 |
+
return (x, )
|
| 181 |
+
x = self.layer4(x)
|
| 182 |
+
return (x, )
|
| 183 |
+
# x = self.bn2(x)
|
| 184 |
+
# x = torch.flatten(x, 1)
|
| 185 |
+
# x = self.dropout(x)
|
| 186 |
+
# x = self.fc(x.float() if self.fp16 else x)
|
| 187 |
+
# x = self.features(x)
|
| 188 |
+
# return x
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
def _iresnet(arch, block, layers, pretrained, progress, **kwargs):
|
| 192 |
+
model = IResNet(block, layers, **kwargs)
|
| 193 |
+
if pretrained:
|
| 194 |
+
raise ValueError()
|
| 195 |
+
return model
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
def iresnet18(pretrained=False, progress=True, **kwargs):
|
| 199 |
+
return _iresnet('iresnet18', IBasicBlock, [2, 2, 2, 2], pretrained,
|
| 200 |
+
progress, **kwargs)
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
def iresnet34(pretrained=False, progress=True, **kwargs):
|
| 204 |
+
return _iresnet('iresnet34', IBasicBlock, [3, 4, 6, 3], pretrained,
|
| 205 |
+
progress, **kwargs)
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
def iresnet50(pretrained=False, progress=True, **kwargs):
|
| 209 |
+
return _iresnet('iresnet50', IBasicBlock, [3, 4, 14, 3], pretrained,
|
| 210 |
+
progress, **kwargs)
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
def iresnet100(pretrained=False, progress=True, **kwargs):
|
| 214 |
+
return _iresnet('iresnet100', IBasicBlock, [3, 13, 30, 3], pretrained,
|
| 215 |
+
progress, **kwargs)
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
def iresnet200(pretrained=False, progress=True, **kwargs):
|
| 219 |
+
return _iresnet('iresnet200', IBasicBlock, [6, 26, 60, 6], pretrained,
|
| 220 |
+
progress, **kwargs)
|
CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/irse.py
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import warnings
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
from torch.nn import Linear, Conv2d, BatchNorm1d, BatchNorm2d, PReLU, ReLU, Sigmoid, Dropout, MaxPool2d, \
|
| 4 |
+
AdaptiveAvgPool2d, Sequential, Module
|
| 5 |
+
from collections import namedtuple
|
| 6 |
+
import torch
|
| 7 |
+
from mmcv.runner import load_state_dict
|
| 8 |
+
from mmcls.utils import get_root_logger
|
| 9 |
+
|
| 10 |
+
from ..builder import BACKBONES
|
| 11 |
+
from .base_backbone import BaseBackbone
|
| 12 |
+
|
| 13 |
+
# Support: ['IR_50', 'IR_101', 'IR_152', 'IR_SE_50', 'IR_SE_101', 'IR_SE_152']
|
| 14 |
+
|
| 15 |
+
class ABSPool2d(Module):
|
| 16 |
+
def __init__(self, *args, **kwargs):
|
| 17 |
+
super().__init__()
|
| 18 |
+
self.args = args
|
| 19 |
+
self.kwargs = kwargs
|
| 20 |
+
|
| 21 |
+
def forward(self, x):
|
| 22 |
+
positive = MaxPool2d(*self.args, **self.kwargs)(x)
|
| 23 |
+
negtive = MaxPool2d(*self.args, **self.kwargs)(-x)
|
| 24 |
+
result = torch.where(positive>=negtive, positive, negtive)
|
| 25 |
+
return result
|
| 26 |
+
|
| 27 |
+
pool_layer = MaxPool2d
|
| 28 |
+
|
| 29 |
+
class Flatten(Module):
|
| 30 |
+
def forward(self, input):
|
| 31 |
+
return input.view(input.size(0), -1)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def l2_norm(input, axis=1):
|
| 35 |
+
norm = torch.norm(input, 2, axis, True)
|
| 36 |
+
output = torch.div(input, norm)
|
| 37 |
+
|
| 38 |
+
return output
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
class SEModule(Module):
|
| 42 |
+
def __init__(self, channels, reduction):
|
| 43 |
+
super(SEModule, self).__init__()
|
| 44 |
+
self.avg_pool = AdaptiveAvgPool2d(1)
|
| 45 |
+
self.fc1 = Conv2d(
|
| 46 |
+
channels, channels // reduction, kernel_size=1, padding=0, bias=False)
|
| 47 |
+
|
| 48 |
+
nn.init.xavier_uniform_(self.fc1.weight.data)
|
| 49 |
+
|
| 50 |
+
self.relu = ReLU(inplace=True)
|
| 51 |
+
self.fc2 = Conv2d(
|
| 52 |
+
channels // reduction, channels, kernel_size=1, padding=0, bias=False)
|
| 53 |
+
|
| 54 |
+
self.sigmoid = Sigmoid()
|
| 55 |
+
|
| 56 |
+
def forward(self, x):
|
| 57 |
+
module_input = x
|
| 58 |
+
x = self.avg_pool(x)
|
| 59 |
+
x = self.fc1(x)
|
| 60 |
+
x = self.relu(x)
|
| 61 |
+
x = self.fc2(x)
|
| 62 |
+
x = self.sigmoid(x)
|
| 63 |
+
|
| 64 |
+
return module_input * x
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
class bottleneck_IR(Module):
|
| 68 |
+
def __init__(self, in_channel, depth, stride):
|
| 69 |
+
super(bottleneck_IR, self).__init__()
|
| 70 |
+
if in_channel == depth:
|
| 71 |
+
self.shortcut_layer = pool_layer(1, stride)
|
| 72 |
+
else:
|
| 73 |
+
self.shortcut_layer = Sequential(
|
| 74 |
+
Conv2d(in_channel, depth, (1, 1), stride, bias=False),
|
| 75 |
+
BatchNorm2d(depth))
|
| 76 |
+
self.res_layer = Sequential(
|
| 77 |
+
BatchNorm2d(in_channel),
|
| 78 |
+
Conv2d(in_channel, depth, (3, 3), (1, 1), 1, bias=False),
|
| 79 |
+
PReLU(depth),
|
| 80 |
+
Conv2d(depth, depth, (3, 3), stride, 1, bias=False),
|
| 81 |
+
BatchNorm2d(depth))
|
| 82 |
+
|
| 83 |
+
def forward(self, x):
|
| 84 |
+
shortcut = self.shortcut_layer(x)
|
| 85 |
+
res = self.res_layer(x)
|
| 86 |
+
|
| 87 |
+
return res + shortcut
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
class BasicBlockIR(Module):
|
| 91 |
+
"""
|
| 92 |
+
BasicBlock for IRNet, stolen from TFace
|
| 93 |
+
https://github.com/Tencent/TFace/blob/d57fd8d9ce9502240921f0998c57f84afa7eaeaa/torchkit/backbone/model_irse.py#L16
|
| 94 |
+
Add a BatchNorm2d after the first Conv2d
|
| 95 |
+
"""
|
| 96 |
+
def __init__(self, in_channel, depth, stride):
|
| 97 |
+
super(BasicBlockIR, self).__init__()
|
| 98 |
+
if in_channel == depth:
|
| 99 |
+
self.shortcut_layer = pool_layer(1, stride)
|
| 100 |
+
else:
|
| 101 |
+
self.shortcut_layer = Sequential(
|
| 102 |
+
Conv2d(in_channel, depth, (1, 1), stride, bias=False),
|
| 103 |
+
BatchNorm2d(depth))
|
| 104 |
+
self.res_layer = Sequential(
|
| 105 |
+
BatchNorm2d(in_channel),
|
| 106 |
+
Conv2d(in_channel, depth, (3, 3), (1, 1), 1, bias=False),
|
| 107 |
+
BatchNorm2d(depth),
|
| 108 |
+
PReLU(depth),
|
| 109 |
+
Conv2d(depth, depth, (3, 3), stride, 1, bias=False),
|
| 110 |
+
BatchNorm2d(depth))
|
| 111 |
+
|
| 112 |
+
def forward(self, x):
|
| 113 |
+
shortcut = self.shortcut_layer(x)
|
| 114 |
+
res = self.res_layer(x)
|
| 115 |
+
|
| 116 |
+
return res + shortcut
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
class bottleneck_IR_SE(Module):
|
| 120 |
+
def __init__(self, in_channel, depth, stride):
|
| 121 |
+
super(bottleneck_IR_SE, self).__init__()
|
| 122 |
+
if in_channel == depth:
|
| 123 |
+
self.shortcut_layer = pool_layer(1, stride)
|
| 124 |
+
else:
|
| 125 |
+
self.shortcut_layer = Sequential(
|
| 126 |
+
Conv2d(in_channel, depth, (1, 1), stride, bias=False),
|
| 127 |
+
BatchNorm2d(depth))
|
| 128 |
+
self.res_layer = Sequential(
|
| 129 |
+
BatchNorm2d(in_channel),
|
| 130 |
+
Conv2d(in_channel, depth, (3, 3), (1, 1), 1, bias=False),
|
| 131 |
+
PReLU(depth),
|
| 132 |
+
Conv2d(depth, depth, (3, 3), stride, 1, bias=False),
|
| 133 |
+
BatchNorm2d(depth),
|
| 134 |
+
SEModule(depth, 16)
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
def forward(self, x):
|
| 138 |
+
shortcut = self.shortcut_layer(x)
|
| 139 |
+
res = self.res_layer(x)
|
| 140 |
+
|
| 141 |
+
return res + shortcut
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
class Bottleneck(namedtuple('Block', ['in_channel', 'depth', 'stride'])):
|
| 145 |
+
'''A named tuple describing a ResNet block.'''
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
def get_block(in_channel, depth, num_units, stride=2):
|
| 149 |
+
|
| 150 |
+
return [Bottleneck(in_channel, depth, stride)] + [Bottleneck(depth, depth, 1) for i in range(num_units - 1)]
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def get_blocks(num_layers):
|
| 154 |
+
|
| 155 |
+
if num_layers == 8:
|
| 156 |
+
blocks = [
|
| 157 |
+
get_block(in_channel=64, depth=64, num_units=3),
|
| 158 |
+
]
|
| 159 |
+
elif num_layers == 16:
|
| 160 |
+
blocks = [
|
| 161 |
+
get_block(in_channel=64, depth=64, num_units=3),
|
| 162 |
+
get_block(in_channel=64, depth=128, num_units=4),
|
| 163 |
+
]
|
| 164 |
+
elif num_layers == 34:
|
| 165 |
+
blocks = [
|
| 166 |
+
get_block(in_channel=64, depth=64, num_units=3),
|
| 167 |
+
get_block(in_channel=64, depth=128, num_units=4),
|
| 168 |
+
get_block(in_channel=128, depth=256, num_units=6),
|
| 169 |
+
get_block(in_channel=256, depth=512, num_units=3)
|
| 170 |
+
]
|
| 171 |
+
elif num_layers == 44:
|
| 172 |
+
blocks = [
|
| 173 |
+
get_block(in_channel=64, depth=64, num_units=3),
|
| 174 |
+
get_block(in_channel=64, depth=128, num_units=4),
|
| 175 |
+
get_block(in_channel=128, depth=256, num_units=14),
|
| 176 |
+
]
|
| 177 |
+
elif num_layers == 50:
|
| 178 |
+
blocks = [
|
| 179 |
+
get_block(in_channel=64, depth=64, num_units=3), # (B, 64, 56, 56)
|
| 180 |
+
get_block(in_channel=64, depth=128, num_units=4), # (B, 128, 28, 28)
|
| 181 |
+
get_block(in_channel=128, depth=256, num_units=14), # (B, 256, 14, 14)
|
| 182 |
+
get_block(in_channel=256, depth=512, num_units=3) # (B, 512, 7, 7)
|
| 183 |
+
]
|
| 184 |
+
elif num_layers == 100:
|
| 185 |
+
blocks = [
|
| 186 |
+
get_block(in_channel=64, depth=64, num_units=3),
|
| 187 |
+
get_block(in_channel=64, depth=128, num_units=13),
|
| 188 |
+
get_block(in_channel=128, depth=256, num_units=30),
|
| 189 |
+
get_block(in_channel=256, depth=512, num_units=3)
|
| 190 |
+
]
|
| 191 |
+
elif num_layers == 152:
|
| 192 |
+
blocks = [
|
| 193 |
+
get_block(in_channel=64, depth=64, num_units=3),
|
| 194 |
+
get_block(in_channel=64, depth=128, num_units=8),
|
| 195 |
+
get_block(in_channel=128, depth=256, num_units=36),
|
| 196 |
+
get_block(in_channel=256, depth=512, num_units=3)
|
| 197 |
+
]
|
| 198 |
+
|
| 199 |
+
return blocks
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
@BACKBONES.register_module()
|
| 203 |
+
class IRSE(BaseBackbone):
|
| 204 |
+
def __init__(self, input_size, num_layers, mode='ir', with_head=False, pretrained=None, return_index=(0, 1, 2),
|
| 205 |
+
return_type='Tuple'):
|
| 206 |
+
super().__init__()
|
| 207 |
+
assert input_size[0] in [112, 224], "input_size should be [112, 112] or [224, 224]"
|
| 208 |
+
assert num_layers in [0, 8, 16, 34, 44, 50, 100, 152], "num_layers should be 50, 100 or 152"
|
| 209 |
+
assert mode in ['ir', 'ir_se'], "mode should be ir or ir_se"
|
| 210 |
+
assert return_type in ('Tensor', 'Tuple'), 'return_tensor must be Tensor or Tuple'
|
| 211 |
+
if len(return_index) > 1:
|
| 212 |
+
assert return_type == 'Tuple'
|
| 213 |
+
self.num_layers = num_layers
|
| 214 |
+
self.return_index = return_index
|
| 215 |
+
self.return_type = return_type
|
| 216 |
+
if num_layers == 0:
|
| 217 |
+
return
|
| 218 |
+
self.with_head = with_head
|
| 219 |
+
blocks = get_blocks(num_layers)
|
| 220 |
+
if mode == 'ir':
|
| 221 |
+
if num_layers == 34:
|
| 222 |
+
warnings.warn('Using the IR_34 version from TFace')
|
| 223 |
+
unit_module = BasicBlockIR
|
| 224 |
+
else:
|
| 225 |
+
unit_module = bottleneck_IR
|
| 226 |
+
elif mode == 'ir_se':
|
| 227 |
+
unit_module = bottleneck_IR_SE
|
| 228 |
+
self.input_layer = Sequential(Conv2d(3, 64, (3, 3), 1, 1, bias=False),
|
| 229 |
+
BatchNorm2d(64),
|
| 230 |
+
PReLU(64))
|
| 231 |
+
if with_head:
|
| 232 |
+
if input_size[0] == 112:
|
| 233 |
+
self.output_layer = Sequential(BatchNorm2d(512),
|
| 234 |
+
Dropout(),
|
| 235 |
+
Flatten(),
|
| 236 |
+
Linear(512 * 7 * 7, 512),
|
| 237 |
+
BatchNorm1d(512))
|
| 238 |
+
else:
|
| 239 |
+
self.output_layer = Sequential(BatchNorm2d(512),
|
| 240 |
+
Dropout(),
|
| 241 |
+
Flatten(),
|
| 242 |
+
Linear(512 * 14 * 14, 512),
|
| 243 |
+
BatchNorm1d(512))
|
| 244 |
+
|
| 245 |
+
modules = []
|
| 246 |
+
max_stage = max(return_index)
|
| 247 |
+
for block in blocks[:max_stage+1]:
|
| 248 |
+
block_module = []
|
| 249 |
+
for bottleneck in block:
|
| 250 |
+
block_module.append(
|
| 251 |
+
unit_module(bottleneck.in_channel,
|
| 252 |
+
bottleneck.depth,
|
| 253 |
+
bottleneck.stride))
|
| 254 |
+
modules.append(Sequential(*block_module))
|
| 255 |
+
# self.body = Sequential(*modules)
|
| 256 |
+
self.body = nn.ModuleList(modules)
|
| 257 |
+
|
| 258 |
+
self._initialize_weights()
|
| 259 |
+
if pretrained:
|
| 260 |
+
self.init_weights(pretrained)
|
| 261 |
+
|
| 262 |
+
def init_weights(self, pretrained):
|
| 263 |
+
logger = get_root_logger()
|
| 264 |
+
logger.warning(f'{self.__class__.__name__} load pretrain from {pretrained}')
|
| 265 |
+
state_dict = torch.load(pretrained, map_location='cpu')
|
| 266 |
+
if 'state_dict' in state_dict:
|
| 267 |
+
state_dict = state_dict['state_dict']
|
| 268 |
+
|
| 269 |
+
stage_unit_nums = {
|
| 270 |
+
34: (3, 4, 6, 3),
|
| 271 |
+
50: (3, 4, 14, 3),
|
| 272 |
+
}
|
| 273 |
+
stage_num = stage_unit_nums[self.num_layers]
|
| 274 |
+
|
| 275 |
+
new_state_dict = dict()
|
| 276 |
+
for k, v in state_dict.items():
|
| 277 |
+
if k.startswith('body.'):
|
| 278 |
+
index = int(k.split('.')[1])
|
| 279 |
+
if 0 <= index < stage_num[0]:
|
| 280 |
+
new_key = k.replace('body.', 'body.0.')
|
| 281 |
+
elif stage_num[0] <= index < sum(stage_num[:2]):
|
| 282 |
+
new_key = f"body.1.{index-sum(stage_num[:1])}.{'.'.join(k.split('.')[2:])}"
|
| 283 |
+
elif sum(stage_num[:2]) <= index < sum(stage_num[:3]):
|
| 284 |
+
new_key = f"body.2.{index-sum(stage_num[:2])}.{'.'.join(k.split('.')[2:])}"
|
| 285 |
+
else:
|
| 286 |
+
new_key = k
|
| 287 |
+
else:
|
| 288 |
+
new_key = k
|
| 289 |
+
new_state_dict[new_key] = v
|
| 290 |
+
|
| 291 |
+
load_state_dict(self, new_state_dict, strict=False, logger=logger)
|
| 292 |
+
|
| 293 |
+
def forward(self, x):
|
| 294 |
+
if self.num_layers == 0:
|
| 295 |
+
return x
|
| 296 |
+
x = self.input_layer(x)
|
| 297 |
+
output = []
|
| 298 |
+
return_index = set(self.return_index)
|
| 299 |
+
for index, m in enumerate(self.body):
|
| 300 |
+
x = m(x)
|
| 301 |
+
if index in return_index:
|
| 302 |
+
output.append(x)
|
| 303 |
+
if self.with_head:
|
| 304 |
+
x = self.output_layer(x)
|
| 305 |
+
if self.return_type == 'Tensor':
|
| 306 |
+
return output[0]
|
| 307 |
+
else:
|
| 308 |
+
return tuple(output)
|
| 309 |
+
|
| 310 |
+
def _initialize_weights(self):
|
| 311 |
+
for m in self.modules():
|
| 312 |
+
if isinstance(m, nn.Conv2d):
|
| 313 |
+
nn.init.xavier_uniform_(m.weight.data)
|
| 314 |
+
if m.bias is not None:
|
| 315 |
+
m.bias.data.zero_()
|
| 316 |
+
elif isinstance(m, nn.BatchNorm2d):
|
| 317 |
+
m.weight.data.fill_(1)
|
| 318 |
+
m.bias.data.zero_()
|
| 319 |
+
elif isinstance(m, nn.BatchNorm1d):
|
| 320 |
+
m.weight.data.fill_(1)
|
| 321 |
+
m.bias.data.zero_()
|
| 322 |
+
elif isinstance(m, nn.Linear):
|
| 323 |
+
nn.init.xavier_uniform_(m.weight.data)
|
| 324 |
+
if m.bias is not None:
|
| 325 |
+
m.bias.data.zero_()
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
def _freeze_stages(self):
|
| 329 |
+
if self.frozen_blocks > 0:
|
| 330 |
+
print(f'IRSE freeze the first {self.frozen_blocks} blocks, it has {len(self.body)} blocks ')
|
| 331 |
+
self.input_layer.eval()
|
| 332 |
+
print('in freeze', self.input_layer[1].training)
|
| 333 |
+
for param in self.input_layer.parameters():
|
| 334 |
+
param.requires_grad = False
|
| 335 |
+
|
| 336 |
+
for i in range(self.frozen_blocks):
|
| 337 |
+
m = self.body[i]
|
| 338 |
+
m.eval()
|
| 339 |
+
for param in m.parameters():
|
| 340 |
+
param.requires_grad = False
|
| 341 |
+
|
| 342 |
+
def IR_50(input_size):
|
| 343 |
+
"""Constructs a ir-50 model.
|
| 344 |
+
"""
|
| 345 |
+
model = Backbone(input_size, 50, 'ir')
|
| 346 |
+
|
| 347 |
+
return model
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
def IR_101(input_size):
|
| 351 |
+
"""Constructs a ir-101 model.
|
| 352 |
+
"""
|
| 353 |
+
model = Backbone(input_size, 100, 'ir')
|
| 354 |
+
|
| 355 |
+
return model
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
def IR_152(input_size):
|
| 359 |
+
"""Constructs a ir-152 model.
|
| 360 |
+
"""
|
| 361 |
+
model = Backbone(input_size, 152, 'ir')
|
| 362 |
+
|
| 363 |
+
return model
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
def IR_SE_50(input_size):
|
| 367 |
+
"""Constructs a ir_se-50 model.
|
| 368 |
+
"""
|
| 369 |
+
model = Backbone(input_size, 50, 'ir_se')
|
| 370 |
+
|
| 371 |
+
return model
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
def IR_SE_101(input_size):
|
| 375 |
+
"""Constructs a ir_se-101 model.
|
| 376 |
+
"""
|
| 377 |
+
model = Backbone(input_size, 100, 'ir_se')
|
| 378 |
+
|
| 379 |
+
return model
|
| 380 |
+
|
| 381 |
+
|
| 382 |
+
def IR_SE_152(input_size):
|
| 383 |
+
"""Constructs a ir_se-152 model.
|
| 384 |
+
"""
|
| 385 |
+
model = Backbone(input_size, 152, 'ir_se')
|
| 386 |
+
|
| 387 |
+
return model
|
CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/irse_nopadding.py
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import warnings
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
from torch.nn import Linear, Conv2d, BatchNorm1d, BatchNorm2d, PReLU, ReLU, Sigmoid, Dropout, MaxPool2d, \
|
| 4 |
+
AdaptiveAvgPool2d, Sequential, Module
|
| 5 |
+
from collections import namedtuple
|
| 6 |
+
import torch
|
| 7 |
+
from mmcv.runner import load_state_dict
|
| 8 |
+
from mmcls.utils import get_root_logger
|
| 9 |
+
|
| 10 |
+
from ..builder import BACKBONES
|
| 11 |
+
from .base_backbone import BaseBackbone
|
| 12 |
+
|
| 13 |
+
# Support: ['IR_50', 'IR_101', 'IR_152', 'IR_SE_50', 'IR_SE_101', 'IR_SE_152']
|
| 14 |
+
|
| 15 |
+
class Flatten(Module):
|
| 16 |
+
def forward(self, input):
|
| 17 |
+
return input.view(input.size(0), -1)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def l2_norm(input, axis=1):
|
| 21 |
+
norm = torch.norm(input, 2, axis, True)
|
| 22 |
+
output = torch.div(input, norm)
|
| 23 |
+
|
| 24 |
+
return output
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class SEModule(Module):
|
| 28 |
+
def __init__(self, channels, reduction):
|
| 29 |
+
super(SEModule, self).__init__()
|
| 30 |
+
self.avg_pool = AdaptiveAvgPool2d(1)
|
| 31 |
+
self.fc1 = Conv2d(
|
| 32 |
+
channels, channels // reduction, kernel_size=1, padding=0, bias=False)
|
| 33 |
+
|
| 34 |
+
nn.init.xavier_uniform_(self.fc1.weight.data)
|
| 35 |
+
|
| 36 |
+
self.relu = ReLU(inplace=True)
|
| 37 |
+
self.fc2 = Conv2d(
|
| 38 |
+
channels // reduction, channels, kernel_size=1, padding=0, bias=False)
|
| 39 |
+
|
| 40 |
+
self.sigmoid = Sigmoid()
|
| 41 |
+
|
| 42 |
+
def forward(self, x):
|
| 43 |
+
module_input = x
|
| 44 |
+
x = self.avg_pool(x)
|
| 45 |
+
x = self.fc1(x)
|
| 46 |
+
x = self.relu(x)
|
| 47 |
+
x = self.fc2(x)
|
| 48 |
+
x = self.sigmoid(x)
|
| 49 |
+
|
| 50 |
+
return module_input * x
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class bottleneck_IR(Module):
|
| 54 |
+
def __init__(self, in_channel, depth, stride):
|
| 55 |
+
super(bottleneck_IR, self).__init__()
|
| 56 |
+
if in_channel == depth:
|
| 57 |
+
self.shortcut_layer = MaxPool2d(1, stride)
|
| 58 |
+
else:
|
| 59 |
+
self.shortcut_layer = Sequential(
|
| 60 |
+
Conv2d(in_channel, depth, (1, 1), stride, bias=False),
|
| 61 |
+
BatchNorm2d(depth))
|
| 62 |
+
self.res_layer = Sequential(
|
| 63 |
+
BatchNorm2d(in_channel),
|
| 64 |
+
Conv2d(in_channel, depth, (3, 3), (1, 1), 1, bias=False),
|
| 65 |
+
PReLU(depth),
|
| 66 |
+
Conv2d(depth, depth, (3, 3), stride, 1, bias=False),
|
| 67 |
+
BatchNorm2d(depth))
|
| 68 |
+
|
| 69 |
+
def forward(self, x):
|
| 70 |
+
shortcut = self.shortcut_layer(x)
|
| 71 |
+
res = self.res_layer(x)
|
| 72 |
+
|
| 73 |
+
return res + shortcut
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
class BasicBlockIR(Module):
|
| 77 |
+
"""
|
| 78 |
+
BasicBlock for IRNet, stolen from TFace
|
| 79 |
+
https://github.com/Tencent/TFace/blob/d57fd8d9ce9502240921f0998c57f84afa7eaeaa/torchkit/backbone/model_irse.py#L16
|
| 80 |
+
Add a BatchNorm2d after the first Conv2d
|
| 81 |
+
"""
|
| 82 |
+
def __init__(self, in_channel, depth, stride):
|
| 83 |
+
super(BasicBlockIR, self).__init__()
|
| 84 |
+
if in_channel == depth:
|
| 85 |
+
self.shortcut_layer = MaxPool2d(1, stride)
|
| 86 |
+
else:
|
| 87 |
+
self.shortcut_layer = Sequential(
|
| 88 |
+
Conv2d(in_channel, depth, (1, 1), stride, bias=False),
|
| 89 |
+
BatchNorm2d(depth))
|
| 90 |
+
self.res_layer = Sequential(
|
| 91 |
+
BatchNorm2d(in_channel),
|
| 92 |
+
Conv2d(in_channel, depth, (3, 3), (1, 1), 0, bias=False),
|
| 93 |
+
BatchNorm2d(depth),
|
| 94 |
+
PReLU(depth),
|
| 95 |
+
Conv2d(depth, depth, (3, 3), stride, 0, bias=False),
|
| 96 |
+
BatchNorm2d(depth))
|
| 97 |
+
|
| 98 |
+
def forward(self, x):
|
| 99 |
+
shortcut = self.shortcut_layer(x)
|
| 100 |
+
res = self.res_layer(x)
|
| 101 |
+
|
| 102 |
+
return res + shortcut
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
class bottleneck_IR_SE(Module):
|
| 106 |
+
def __init__(self, in_channel, depth, stride):
|
| 107 |
+
super(bottleneck_IR_SE, self).__init__()
|
| 108 |
+
if in_channel == depth:
|
| 109 |
+
self.shortcut_layer = MaxPool2d(1, stride)
|
| 110 |
+
else:
|
| 111 |
+
self.shortcut_layer = Sequential(
|
| 112 |
+
Conv2d(in_channel, depth, (1, 1), stride, bias=False),
|
| 113 |
+
BatchNorm2d(depth))
|
| 114 |
+
self.res_layer = Sequential(
|
| 115 |
+
BatchNorm2d(in_channel),
|
| 116 |
+
Conv2d(in_channel, depth, (3, 3), (1, 1), 0, bias=False),
|
| 117 |
+
PReLU(depth),
|
| 118 |
+
Conv2d(depth, depth, (3, 3), stride, 0, bias=False),
|
| 119 |
+
BatchNorm2d(depth),
|
| 120 |
+
SEModule(depth, 16)
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
def forward(self, x):
|
| 124 |
+
shortcut = self.shortcut_layer(x)
|
| 125 |
+
res = self.res_layer(x)
|
| 126 |
+
|
| 127 |
+
return res + shortcut
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
class Bottleneck(namedtuple('Block', ['in_channel', 'depth', 'stride'])):
|
| 131 |
+
'''A named tuple describing a ResNet block.'''
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def get_block(in_channel, depth, num_units, stride=2):
|
| 135 |
+
|
| 136 |
+
return [Bottleneck(in_channel, depth, stride)] + [Bottleneck(depth, depth, 1) for i in range(num_units - 1)]
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
def get_blocks(num_layers):
|
| 140 |
+
|
| 141 |
+
if num_layers == 8:
|
| 142 |
+
blocks = [
|
| 143 |
+
get_block(in_channel=64, depth=64, num_units=3),
|
| 144 |
+
]
|
| 145 |
+
elif num_layers == 16:
|
| 146 |
+
blocks = [
|
| 147 |
+
get_block(in_channel=64, depth=64, num_units=3),
|
| 148 |
+
get_block(in_channel=64, depth=128, num_units=4),
|
| 149 |
+
]
|
| 150 |
+
elif num_layers == 34:
|
| 151 |
+
blocks = [
|
| 152 |
+
get_block(in_channel=64, depth=64, num_units=3),
|
| 153 |
+
get_block(in_channel=64, depth=128, num_units=4),
|
| 154 |
+
get_block(in_channel=128, depth=256, num_units=6),
|
| 155 |
+
get_block(in_channel=256, depth=512, num_units=3)
|
| 156 |
+
]
|
| 157 |
+
elif num_layers == 44:
|
| 158 |
+
blocks = [
|
| 159 |
+
get_block(in_channel=64, depth=64, num_units=3),
|
| 160 |
+
get_block(in_channel=64, depth=128, num_units=4),
|
| 161 |
+
get_block(in_channel=128, depth=256, num_units=14),
|
| 162 |
+
]
|
| 163 |
+
elif num_layers == 50:
|
| 164 |
+
blocks = [
|
| 165 |
+
get_block(in_channel=64, depth=64, num_units=3), # (B, 64, 56, 56)
|
| 166 |
+
get_block(in_channel=64, depth=128, num_units=4), # (B, 128, 28, 28)
|
| 167 |
+
get_block(in_channel=128, depth=256, num_units=14), # (B, 256, 14, 14)
|
| 168 |
+
get_block(in_channel=256, depth=512, num_units=3) # (B, 512, 7, 7)
|
| 169 |
+
]
|
| 170 |
+
elif num_layers == 100:
|
| 171 |
+
blocks = [
|
| 172 |
+
get_block(in_channel=64, depth=64, num_units=3),
|
| 173 |
+
get_block(in_channel=64, depth=128, num_units=13),
|
| 174 |
+
get_block(in_channel=128, depth=256, num_units=30),
|
| 175 |
+
get_block(in_channel=256, depth=512, num_units=3)
|
| 176 |
+
]
|
| 177 |
+
elif num_layers == 152:
|
| 178 |
+
blocks = [
|
| 179 |
+
get_block(in_channel=64, depth=64, num_units=3),
|
| 180 |
+
get_block(in_channel=64, depth=128, num_units=8),
|
| 181 |
+
get_block(in_channel=128, depth=256, num_units=36),
|
| 182 |
+
get_block(in_channel=256, depth=512, num_units=3)
|
| 183 |
+
]
|
| 184 |
+
|
| 185 |
+
return blocks
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
@BACKBONES.register_module()
|
| 189 |
+
class IRSENoPadding(BaseBackbone):
|
| 190 |
+
def __init__(self, input_size, num_layers, mode='ir', with_head=False, pretrained=None, return_index=(0, 1, 2)):
|
| 191 |
+
super().__init__()
|
| 192 |
+
assert input_size[0] in [112, 224], "input_size should be [112, 112] or [224, 224]"
|
| 193 |
+
assert num_layers in [0, 8, 16, 34, 44, 50, 100, 152], "num_layers should be 50, 100 or 152"
|
| 194 |
+
assert mode in ['ir', 'ir_se'], "mode should be ir or ir_se"
|
| 195 |
+
self.num_layers = num_layers
|
| 196 |
+
self.return_index = return_index
|
| 197 |
+
if num_layers == 0:
|
| 198 |
+
return
|
| 199 |
+
self.with_head = with_head
|
| 200 |
+
blocks = get_blocks(num_layers)
|
| 201 |
+
if mode == 'ir':
|
| 202 |
+
if num_layers == 34:
|
| 203 |
+
warnings.warn('Using the IR_34 version from TFace')
|
| 204 |
+
unit_module = BasicBlockIR
|
| 205 |
+
else:
|
| 206 |
+
unit_module = bottleneck_IR
|
| 207 |
+
elif mode == 'ir_se':
|
| 208 |
+
unit_module = bottleneck_IR_SE
|
| 209 |
+
self.input_layer = Sequential(Conv2d(3, 64, (3, 3), 1, 0, bias=False),
|
| 210 |
+
BatchNorm2d(64),
|
| 211 |
+
PReLU(64))
|
| 212 |
+
if with_head:
|
| 213 |
+
if input_size[0] == 112:
|
| 214 |
+
self.output_layer = Sequential(BatchNorm2d(512),
|
| 215 |
+
Dropout(),
|
| 216 |
+
Flatten(),
|
| 217 |
+
Linear(512 * 7 * 7, 512),
|
| 218 |
+
BatchNorm1d(512))
|
| 219 |
+
else:
|
| 220 |
+
self.output_layer = Sequential(BatchNorm2d(512),
|
| 221 |
+
Dropout(),
|
| 222 |
+
Flatten(),
|
| 223 |
+
Linear(512 * 14 * 14, 512),
|
| 224 |
+
BatchNorm1d(512))
|
| 225 |
+
|
| 226 |
+
modules = []
|
| 227 |
+
max_stage = max(return_index)
|
| 228 |
+
for block in blocks[:max_stage+1]:
|
| 229 |
+
block_module = []
|
| 230 |
+
for bottleneck in block:
|
| 231 |
+
block_module.append(
|
| 232 |
+
unit_module(bottleneck.in_channel,
|
| 233 |
+
bottleneck.depth,
|
| 234 |
+
bottleneck.stride))
|
| 235 |
+
modules.append(Sequential(*block_module))
|
| 236 |
+
# self.body = Sequential(*modules)
|
| 237 |
+
self.body = nn.ModuleList(modules)
|
| 238 |
+
|
| 239 |
+
self._initialize_weights()
|
| 240 |
+
if pretrained:
|
| 241 |
+
self.init_weights(pretrained)
|
| 242 |
+
|
| 243 |
+
def init_weights(self, pretrained):
|
| 244 |
+
logger = get_root_logger()
|
| 245 |
+
logger.warning(f'{self.__class__.__name__} load pretrain from {pretrained}')
|
| 246 |
+
state_dict = torch.load(pretrained, map_location='cpu')
|
| 247 |
+
if 'state_dict' in state_dict:
|
| 248 |
+
state_dict = state_dict['state_dict']
|
| 249 |
+
|
| 250 |
+
stage_unit_nums = {
|
| 251 |
+
34: (3, 4, 6, 3),
|
| 252 |
+
50: (3, 4, 14, 3),
|
| 253 |
+
}
|
| 254 |
+
stage_num = stage_unit_nums[self.num_layers]
|
| 255 |
+
|
| 256 |
+
new_state_dict = dict()
|
| 257 |
+
for k, v in state_dict.items():
|
| 258 |
+
if k.startswith('body.'):
|
| 259 |
+
index = int(k.split('.')[1])
|
| 260 |
+
if 0 <= index < stage_num[0]:
|
| 261 |
+
new_key = k.replace('body.', 'body.0.')
|
| 262 |
+
elif stage_num[0] <= index < sum(stage_num[:2]):
|
| 263 |
+
new_key = f"body.1.{index-sum(stage_num[:1])}.{'.'.join(k.split('.')[2:])}"
|
| 264 |
+
elif sum(stage_num[:2]) <= index < sum(stage_num[:3]):
|
| 265 |
+
new_key = f"body.2.{index-sum(stage_num[:2])}.{'.'.join(k.split('.')[2:])}"
|
| 266 |
+
else:
|
| 267 |
+
new_key = k
|
| 268 |
+
else:
|
| 269 |
+
new_key = k
|
| 270 |
+
new_state_dict[new_key] = v
|
| 271 |
+
|
| 272 |
+
load_state_dict(self, new_state_dict, strict=False, logger=logger)
|
| 273 |
+
|
| 274 |
+
def forward(self, x):
|
| 275 |
+
if self.num_layers == 0:
|
| 276 |
+
return x
|
| 277 |
+
x = self.input_layer(x)
|
| 278 |
+
output = []
|
| 279 |
+
return_index = set(self.return_index)
|
| 280 |
+
for index, m in enumerate(self.body):
|
| 281 |
+
x = m(x)
|
| 282 |
+
if index in return_index:
|
| 283 |
+
output.append(x)
|
| 284 |
+
if self.with_head:
|
| 285 |
+
x = self.output_layer(x)
|
| 286 |
+
|
| 287 |
+
return output
|
| 288 |
+
|
| 289 |
+
def _initialize_weights(self):
|
| 290 |
+
for m in self.modules():
|
| 291 |
+
if isinstance(m, nn.Conv2d):
|
| 292 |
+
nn.init.xavier_uniform_(m.weight.data)
|
| 293 |
+
if m.bias is not None:
|
| 294 |
+
m.bias.data.zero_()
|
| 295 |
+
elif isinstance(m, nn.BatchNorm2d):
|
| 296 |
+
m.weight.data.fill_(1)
|
| 297 |
+
m.bias.data.zero_()
|
| 298 |
+
elif isinstance(m, nn.BatchNorm1d):
|
| 299 |
+
m.weight.data.fill_(1)
|
| 300 |
+
m.bias.data.zero_()
|
| 301 |
+
elif isinstance(m, nn.Linear):
|
| 302 |
+
nn.init.xavier_uniform_(m.weight.data)
|
| 303 |
+
if m.bias is not None:
|
| 304 |
+
m.bias.data.zero_()
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
def _freeze_stages(self):
|
| 308 |
+
if self.frozen_blocks > 0:
|
| 309 |
+
print(f'IRSE freeze the first {self.frozen_blocks} blocks, it has {len(self.body)} blocks ')
|
| 310 |
+
self.input_layer.eval()
|
| 311 |
+
print('in freeze', self.input_layer[1].training)
|
| 312 |
+
for param in self.input_layer.parameters():
|
| 313 |
+
param.requires_grad = False
|
| 314 |
+
|
| 315 |
+
for i in range(self.frozen_blocks):
|
| 316 |
+
m = self.body[i]
|
| 317 |
+
m.eval()
|
| 318 |
+
for param in m.parameters():
|
| 319 |
+
param.requires_grad = False
|
| 320 |
+
|
| 321 |
+
def IR_50(input_size):
|
| 322 |
+
"""Constructs a ir-50 model.
|
| 323 |
+
"""
|
| 324 |
+
model = Backbone(input_size, 50, 'ir')
|
| 325 |
+
|
| 326 |
+
return model
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
def IR_101(input_size):
|
| 330 |
+
"""Constructs a ir-101 model.
|
| 331 |
+
"""
|
| 332 |
+
model = Backbone(input_size, 100, 'ir')
|
| 333 |
+
|
| 334 |
+
return model
|
| 335 |
+
|
| 336 |
+
|
| 337 |
+
def IR_152(input_size):
|
| 338 |
+
"""Constructs a ir-152 model.
|
| 339 |
+
"""
|
| 340 |
+
model = Backbone(input_size, 152, 'ir')
|
| 341 |
+
|
| 342 |
+
return model
|
| 343 |
+
|
| 344 |
+
|
| 345 |
+
def IR_SE_50(input_size):
|
| 346 |
+
"""Constructs a ir_se-50 model.
|
| 347 |
+
"""
|
| 348 |
+
model = Backbone(input_size, 50, 'ir_se')
|
| 349 |
+
|
| 350 |
+
return model
|
| 351 |
+
|
| 352 |
+
|
| 353 |
+
def IR_SE_101(input_size):
|
| 354 |
+
"""Constructs a ir_se-101 model.
|
| 355 |
+
"""
|
| 356 |
+
model = Backbone(input_size, 100, 'ir_se')
|
| 357 |
+
|
| 358 |
+
return model
|
| 359 |
+
|
| 360 |
+
|
| 361 |
+
def IR_SE_152(input_size):
|
| 362 |
+
"""Constructs a ir_se-152 model.
|
| 363 |
+
"""
|
| 364 |
+
model = Backbone(input_size, 152, 'ir_se')
|
| 365 |
+
|
| 366 |
+
return model
|
CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/lenet.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch.nn as nn
|
| 2 |
+
|
| 3 |
+
from ..builder import BACKBONES
|
| 4 |
+
from .base_backbone import BaseBackbone
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
@BACKBONES.register_module()
|
| 8 |
+
class LeNet5(BaseBackbone):
|
| 9 |
+
"""`LeNet5 <https://en.wikipedia.org/wiki/LeNet>`_ backbone.
|
| 10 |
+
|
| 11 |
+
The input for LeNet-5 is a 32×32 grayscale image.
|
| 12 |
+
|
| 13 |
+
Args:
|
| 14 |
+
num_classes (int): number of classes for classification.
|
| 15 |
+
The default value is -1, which uses the backbone as
|
| 16 |
+
a feature extractor without the top classifier.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
def __init__(self, num_classes=-1):
|
| 20 |
+
super(LeNet5, self).__init__()
|
| 21 |
+
self.num_classes = num_classes
|
| 22 |
+
self.features = nn.Sequential(
|
| 23 |
+
nn.Conv2d(1, 6, kernel_size=5, stride=1), nn.Tanh(),
|
| 24 |
+
nn.AvgPool2d(kernel_size=2),
|
| 25 |
+
nn.Conv2d(6, 16, kernel_size=5, stride=1), nn.Tanh(),
|
| 26 |
+
nn.AvgPool2d(kernel_size=2),
|
| 27 |
+
nn.Conv2d(16, 120, kernel_size=5, stride=1), nn.Tanh())
|
| 28 |
+
if self.num_classes > 0:
|
| 29 |
+
self.classifier = nn.Sequential(
|
| 30 |
+
nn.Linear(120, 84),
|
| 31 |
+
nn.Tanh(),
|
| 32 |
+
nn.Linear(84, num_classes),
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
def forward(self, x):
|
| 36 |
+
|
| 37 |
+
x = self.features(x)
|
| 38 |
+
if self.num_classes > 0:
|
| 39 |
+
x = self.classifier(x.squeeze())
|
| 40 |
+
|
| 41 |
+
return x
|
CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/mobilefacenet.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
from torch.nn import Linear, Conv2d, BatchNorm1d, BatchNorm2d, PReLU, Module, Sequential
|
| 4 |
+
|
| 5 |
+
from ..builder import BACKBONES
|
| 6 |
+
from .base_backbone import BaseBackbone
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class Flatten(Module):
|
| 10 |
+
def forward(self, input):
|
| 11 |
+
return input.view(input.size(0), -1)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def l2_norm(input,axis=1):
|
| 15 |
+
norm = torch.norm(input,2,axis,True)
|
| 16 |
+
output = torch.div(input, norm)
|
| 17 |
+
return output
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class Conv_block(Module):
|
| 21 |
+
def __init__(self, in_c, out_c, kernel=(1, 1), stride=(1, 1), padding=(0, 0), groups=1):
|
| 22 |
+
super(Conv_block, self).__init__()
|
| 23 |
+
self.conv = Conv2d(in_c, out_channels=out_c, kernel_size=kernel, groups=groups, stride=stride, padding=padding, bias=False)
|
| 24 |
+
self.bn = BatchNorm2d(out_c)
|
| 25 |
+
self.prelu = PReLU(out_c)
|
| 26 |
+
def forward(self, x):
|
| 27 |
+
x = self.conv(x)
|
| 28 |
+
x = self.bn(x)
|
| 29 |
+
x = self.prelu(x)
|
| 30 |
+
return x
|
| 31 |
+
|
| 32 |
+
class Linear_block(Module):
|
| 33 |
+
def __init__(self, in_c, out_c, kernel=(1, 1), stride=(1, 1), padding=(0, 0), groups=1):
|
| 34 |
+
super(Linear_block, self).__init__()
|
| 35 |
+
self.conv = Conv2d(in_c, out_channels=out_c, kernel_size=kernel, groups=groups, stride=stride, padding=padding, bias=False)
|
| 36 |
+
self.bn = BatchNorm2d(out_c)
|
| 37 |
+
def forward(self, x):
|
| 38 |
+
x = self.conv(x)
|
| 39 |
+
x = self.bn(x)
|
| 40 |
+
return x
|
| 41 |
+
|
| 42 |
+
class Depth_Wise(Module):
|
| 43 |
+
def __init__(self, in_c, out_c, residual = False, kernel=(3, 3), stride=(2, 2), padding=(1, 1), groups=1):
|
| 44 |
+
super(Depth_Wise, self).__init__()
|
| 45 |
+
self.conv = Conv_block(in_c, out_c=groups, kernel=(1, 1), padding=(0, 0), stride=(1, 1))
|
| 46 |
+
self.conv_dw = Conv_block(groups, groups, groups=groups, kernel=kernel, padding=padding, stride=stride)
|
| 47 |
+
self.project = Linear_block(groups, out_c, kernel=(1, 1), padding=(0, 0), stride=(1, 1))
|
| 48 |
+
self.residual = residual
|
| 49 |
+
def forward(self, x):
|
| 50 |
+
short_cut = None
|
| 51 |
+
if self.residual:
|
| 52 |
+
short_cut = x
|
| 53 |
+
x = self.conv(x)
|
| 54 |
+
x = self.conv_dw(x)
|
| 55 |
+
x = self.project(x)
|
| 56 |
+
if self.residual:
|
| 57 |
+
output = short_cut + x
|
| 58 |
+
else:
|
| 59 |
+
output = x
|
| 60 |
+
return output
|
| 61 |
+
|
| 62 |
+
class Residual(Module):
|
| 63 |
+
def __init__(self, c, num_block, groups, kernel=(3, 3), stride=(1, 1), padding=(1, 1)):
|
| 64 |
+
super(Residual, self).__init__()
|
| 65 |
+
modules = []
|
| 66 |
+
for _ in range(num_block):
|
| 67 |
+
modules.append(Depth_Wise(c, c, residual=True, kernel=kernel, padding=padding, stride=stride, groups=groups))
|
| 68 |
+
self.model = Sequential(*modules)
|
| 69 |
+
def forward(self, x):
|
| 70 |
+
return self.model(x)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
@BACKBONES.register_module()
|
| 74 |
+
class MobileFaceNet(BaseBackbone):
|
| 75 |
+
"""`MobileFaceNet <https://github.com/TreB1eN/InsightFace_Pytorch>`_ backbone.
|
| 76 |
+
|
| 77 |
+
The input for MobileFaceNet is a 224x224 RGB image.
|
| 78 |
+
|
| 79 |
+
Args:
|
| 80 |
+
num_classes (int): number of classes for classification.
|
| 81 |
+
The default value is -1, which uses the backbone as
|
| 82 |
+
a feature extractor without the top classifier.
|
| 83 |
+
"""
|
| 84 |
+
def __init__(self, return_stages=[2], pretrained=None):
|
| 85 |
+
super().__init__()
|
| 86 |
+
self.conv1 = Conv_block(3, 64, kernel=(3, 3), stride=(2, 2), padding=(1, 1))
|
| 87 |
+
|
| 88 |
+
self.conv2_dw = Conv_block(64, 64, kernel=(3, 3), stride=(1, 1), padding=(1, 1), groups=64)
|
| 89 |
+
|
| 90 |
+
self.conv_23 = Depth_Wise(64, 64, kernel=(3, 3), stride=(2, 2), padding=(1, 1), groups=128)
|
| 91 |
+
self.conv_3 = Residual(64, num_block=4, groups=128, kernel=(3, 3), stride=(1, 1), padding=(1, 1))
|
| 92 |
+
|
| 93 |
+
self.conv_34 = Depth_Wise(64, 128, kernel=(3, 3), stride=(2, 2), padding=(1, 1), groups=256)
|
| 94 |
+
self.conv_4 = Residual(128, num_block=6, groups=256, kernel=(3, 3), stride=(1, 1), padding=(1, 1))
|
| 95 |
+
|
| 96 |
+
# self.conv_45 = Depth_Wise(128, 128, kernel=(3, 3), stride=(2, 2), padding=(1, 1), groups=512)
|
| 97 |
+
# self.conv_5 = Residual(128, num_block=2, groups=256, kernel=(3, 3), stride=(1, 1), padding=(1, 1))
|
| 98 |
+
|
| 99 |
+
# self.conv_6_sep = Conv_block(128, 512, kernel=(1, 1), stride=(1, 1), padding=(0, 0))
|
| 100 |
+
# self.conv_6_dw = Linear_block(512, 512, groups=512, kernel=(7,7), stride=(1, 1), padding=(0, 0))
|
| 101 |
+
# self.conv_6_flatten = Flatten()
|
| 102 |
+
# self.linear = Linear(512, embedding_size, bias=False)
|
| 103 |
+
# self.bn = BatchNorm1d(embedding_size)
|
| 104 |
+
self.return_stages = set(return_stages)
|
| 105 |
+
if pretrained:
|
| 106 |
+
self.init_weights(pretrained)
|
| 107 |
+
|
| 108 |
+
def forward(self, x):
|
| 109 |
+
output = []
|
| 110 |
+
out = self.conv1(x)
|
| 111 |
+
out = self.conv2_dw(out)
|
| 112 |
+
if 0 in self.return_stages:
|
| 113 |
+
output.append(out)
|
| 114 |
+
|
| 115 |
+
out = self.conv_23(out)
|
| 116 |
+
out = self.conv_3(out)
|
| 117 |
+
if 1 in self.return_stages:
|
| 118 |
+
output.append(out)
|
| 119 |
+
|
| 120 |
+
out = self.conv_34(out)
|
| 121 |
+
out = self.conv_4(out)
|
| 122 |
+
if 2 in self.return_stages:
|
| 123 |
+
output.append(out)
|
| 124 |
+
# out = self.conv_45(out)
|
| 125 |
+
# out = self.conv_5(out)
|
| 126 |
+
# out = self.conv_6_sep(out)
|
| 127 |
+
# out = self.conv_6_dw(out)
|
| 128 |
+
# out = self.conv_6_flatten(out)
|
| 129 |
+
# out = self.linear(out)
|
| 130 |
+
# out = self.bn(out)
|
| 131 |
+
# print(out.shape)
|
| 132 |
+
# return out
|
| 133 |
+
return output
|
CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/mobilenet_v2.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
import torch.utils.checkpoint as cp
|
| 5 |
+
from mmcv.cnn import ConvModule, constant_init, kaiming_init
|
| 6 |
+
from mmcv.runner import load_checkpoint
|
| 7 |
+
from torch.nn.modules.batchnorm import _BatchNorm
|
| 8 |
+
|
| 9 |
+
from mmcls.models.utils import make_divisible
|
| 10 |
+
from ..builder import BACKBONES
|
| 11 |
+
from .base_backbone import BaseBackbone
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class InvertedResidual(nn.Module):
|
| 15 |
+
"""InvertedResidual block for MobileNetV2.
|
| 16 |
+
|
| 17 |
+
Args:
|
| 18 |
+
in_channels (int): The input channels of the InvertedResidual block.
|
| 19 |
+
out_channels (int): The output channels of the InvertedResidual block.
|
| 20 |
+
stride (int): Stride of the middle (first) 3x3 convolution.
|
| 21 |
+
expand_ratio (int): adjusts number of channels of the hidden layer
|
| 22 |
+
in InvertedResidual by this amount.
|
| 23 |
+
conv_cfg (dict): Config dict for convolution layer.
|
| 24 |
+
Default: None, which means using conv2d.
|
| 25 |
+
norm_cfg (dict): Config dict for normalization layer.
|
| 26 |
+
Default: dict(type='BN').
|
| 27 |
+
act_cfg (dict): Config dict for activation layer.
|
| 28 |
+
Default: dict(type='ReLU6').
|
| 29 |
+
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
| 30 |
+
memory while slowing down the training speed. Default: False.
|
| 31 |
+
|
| 32 |
+
Returns:
|
| 33 |
+
Tensor: The output tensor
|
| 34 |
+
"""
|
| 35 |
+
|
| 36 |
+
def __init__(self,
|
| 37 |
+
in_channels,
|
| 38 |
+
out_channels,
|
| 39 |
+
stride,
|
| 40 |
+
expand_ratio,
|
| 41 |
+
conv_cfg=None,
|
| 42 |
+
norm_cfg=dict(type='BN'),
|
| 43 |
+
act_cfg=dict(type='ReLU6'),
|
| 44 |
+
with_cp=False):
|
| 45 |
+
super(InvertedResidual, self).__init__()
|
| 46 |
+
self.stride = stride
|
| 47 |
+
assert stride in [1, 2], f'stride must in [1, 2]. ' \
|
| 48 |
+
f'But received {stride}.'
|
| 49 |
+
self.with_cp = with_cp
|
| 50 |
+
self.use_res_connect = self.stride == 1 and in_channels == out_channels
|
| 51 |
+
hidden_dim = int(round(in_channels * expand_ratio))
|
| 52 |
+
|
| 53 |
+
layers = []
|
| 54 |
+
if expand_ratio != 1:
|
| 55 |
+
layers.append(
|
| 56 |
+
ConvModule(
|
| 57 |
+
in_channels=in_channels,
|
| 58 |
+
out_channels=hidden_dim,
|
| 59 |
+
kernel_size=1,
|
| 60 |
+
conv_cfg=conv_cfg,
|
| 61 |
+
norm_cfg=norm_cfg,
|
| 62 |
+
act_cfg=act_cfg))
|
| 63 |
+
layers.extend([
|
| 64 |
+
ConvModule(
|
| 65 |
+
in_channels=hidden_dim,
|
| 66 |
+
out_channels=hidden_dim,
|
| 67 |
+
kernel_size=3,
|
| 68 |
+
stride=stride,
|
| 69 |
+
padding=1,
|
| 70 |
+
groups=hidden_dim,
|
| 71 |
+
conv_cfg=conv_cfg,
|
| 72 |
+
norm_cfg=norm_cfg,
|
| 73 |
+
act_cfg=act_cfg),
|
| 74 |
+
ConvModule(
|
| 75 |
+
in_channels=hidden_dim,
|
| 76 |
+
out_channels=out_channels,
|
| 77 |
+
kernel_size=1,
|
| 78 |
+
conv_cfg=conv_cfg,
|
| 79 |
+
norm_cfg=norm_cfg,
|
| 80 |
+
act_cfg=None)
|
| 81 |
+
])
|
| 82 |
+
self.conv = nn.Sequential(*layers)
|
| 83 |
+
|
| 84 |
+
def forward(self, x):
|
| 85 |
+
|
| 86 |
+
def _inner_forward(x):
|
| 87 |
+
if self.use_res_connect:
|
| 88 |
+
return x + self.conv(x)
|
| 89 |
+
else:
|
| 90 |
+
return self.conv(x)
|
| 91 |
+
|
| 92 |
+
if self.with_cp and x.requires_grad:
|
| 93 |
+
out = cp.checkpoint(_inner_forward, x)
|
| 94 |
+
else:
|
| 95 |
+
out = _inner_forward(x)
|
| 96 |
+
|
| 97 |
+
return out
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
@BACKBONES.register_module()
|
| 101 |
+
class MobileNetV2(BaseBackbone):
|
| 102 |
+
"""MobileNetV2 backbone.
|
| 103 |
+
|
| 104 |
+
Args:
|
| 105 |
+
widen_factor (float): Width multiplier, multiply number of
|
| 106 |
+
channels in each layer by this amount. Default: 1.0.
|
| 107 |
+
out_indices (None or Sequence[int]): Output from which stages.
|
| 108 |
+
Default: (7, ).
|
| 109 |
+
frozen_stages (int): Stages to be frozen (all param fixed).
|
| 110 |
+
Default: -1, which means not freezing any parameters.
|
| 111 |
+
conv_cfg (dict): Config dict for convolution layer.
|
| 112 |
+
Default: None, which means using conv2d.
|
| 113 |
+
norm_cfg (dict): Config dict for normalization layer.
|
| 114 |
+
Default: dict(type='BN').
|
| 115 |
+
act_cfg (dict): Config dict for activation layer.
|
| 116 |
+
Default: dict(type='ReLU6').
|
| 117 |
+
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
| 118 |
+
freeze running stats (mean and var). Note: Effect on Batch Norm
|
| 119 |
+
and its variants only. Default: False.
|
| 120 |
+
with_cp (bool): Use checkpoint or not. Using checkpoint will save some
|
| 121 |
+
memory while slowing down the training speed. Default: False.
|
| 122 |
+
"""
|
| 123 |
+
|
| 124 |
+
# Parameters to build layers. 4 parameters are needed to construct a
|
| 125 |
+
# layer, from left to right: expand_ratio, channel, num_blocks, stride.
|
| 126 |
+
arch_settings = [[1, 16, 1, 1], [6, 24, 2, 2], [6, 32, 3, 2],
|
| 127 |
+
[6, 64, 4, 2], [6, 96, 3, 1], [6, 160, 3, 2],
|
| 128 |
+
[6, 320, 1, 1]]
|
| 129 |
+
|
| 130 |
+
def __init__(self,
|
| 131 |
+
widen_factor=1.,
|
| 132 |
+
out_indices=(7, ),
|
| 133 |
+
frozen_stages=-1,
|
| 134 |
+
conv_cfg=None,
|
| 135 |
+
norm_cfg=dict(type='BN'),
|
| 136 |
+
act_cfg=dict(type='ReLU6'),
|
| 137 |
+
norm_eval=False,
|
| 138 |
+
with_cp=False):
|
| 139 |
+
super(MobileNetV2, self).__init__()
|
| 140 |
+
self.widen_factor = widen_factor
|
| 141 |
+
self.out_indices = out_indices
|
| 142 |
+
for index in out_indices:
|
| 143 |
+
if index not in range(0, 8):
|
| 144 |
+
raise ValueError('the item in out_indices must in '
|
| 145 |
+
f'range(0, 8). But received {index}')
|
| 146 |
+
|
| 147 |
+
if frozen_stages not in range(-1, 8):
|
| 148 |
+
raise ValueError('frozen_stages must be in range(-1, 8). '
|
| 149 |
+
f'But received {frozen_stages}')
|
| 150 |
+
self.out_indices = out_indices
|
| 151 |
+
self.frozen_stages = frozen_stages
|
| 152 |
+
self.conv_cfg = conv_cfg
|
| 153 |
+
self.norm_cfg = norm_cfg
|
| 154 |
+
self.act_cfg = act_cfg
|
| 155 |
+
self.norm_eval = norm_eval
|
| 156 |
+
self.with_cp = with_cp
|
| 157 |
+
|
| 158 |
+
self.in_channels = make_divisible(32 * widen_factor, 8)
|
| 159 |
+
|
| 160 |
+
self.conv1 = ConvModule(
|
| 161 |
+
in_channels=3,
|
| 162 |
+
out_channels=self.in_channels,
|
| 163 |
+
kernel_size=3,
|
| 164 |
+
stride=2,
|
| 165 |
+
padding=1,
|
| 166 |
+
conv_cfg=self.conv_cfg,
|
| 167 |
+
norm_cfg=self.norm_cfg,
|
| 168 |
+
act_cfg=self.act_cfg)
|
| 169 |
+
|
| 170 |
+
self.layers = []
|
| 171 |
+
|
| 172 |
+
for i, layer_cfg in enumerate(self.arch_settings):
|
| 173 |
+
expand_ratio, channel, num_blocks, stride = layer_cfg
|
| 174 |
+
out_channels = make_divisible(channel * widen_factor, 8)
|
| 175 |
+
inverted_res_layer = self.make_layer(
|
| 176 |
+
out_channels=out_channels,
|
| 177 |
+
num_blocks=num_blocks,
|
| 178 |
+
stride=stride,
|
| 179 |
+
expand_ratio=expand_ratio)
|
| 180 |
+
layer_name = f'layer{i + 1}'
|
| 181 |
+
self.add_module(layer_name, inverted_res_layer)
|
| 182 |
+
self.layers.append(layer_name)
|
| 183 |
+
|
| 184 |
+
if widen_factor > 1.0:
|
| 185 |
+
self.out_channel = int(1280 * widen_factor)
|
| 186 |
+
else:
|
| 187 |
+
self.out_channel = 1280
|
| 188 |
+
|
| 189 |
+
layer = ConvModule(
|
| 190 |
+
in_channels=self.in_channels,
|
| 191 |
+
out_channels=self.out_channel,
|
| 192 |
+
kernel_size=1,
|
| 193 |
+
stride=1,
|
| 194 |
+
padding=0,
|
| 195 |
+
conv_cfg=self.conv_cfg,
|
| 196 |
+
norm_cfg=self.norm_cfg,
|
| 197 |
+
act_cfg=self.act_cfg)
|
| 198 |
+
self.add_module('conv2', layer)
|
| 199 |
+
self.layers.append('conv2')
|
| 200 |
+
|
| 201 |
+
def make_layer(self, out_channels, num_blocks, stride, expand_ratio):
|
| 202 |
+
""" Stack InvertedResidual blocks to build a layer for MobileNetV2.
|
| 203 |
+
|
| 204 |
+
Args:
|
| 205 |
+
out_channels (int): out_channels of block.
|
| 206 |
+
num_blocks (int): number of blocks.
|
| 207 |
+
stride (int): stride of the first block. Default: 1
|
| 208 |
+
expand_ratio (int): Expand the number of channels of the
|
| 209 |
+
hidden layer in InvertedResidual by this ratio. Default: 6.
|
| 210 |
+
"""
|
| 211 |
+
layers = []
|
| 212 |
+
for i in range(num_blocks):
|
| 213 |
+
if i >= 1:
|
| 214 |
+
stride = 1
|
| 215 |
+
layers.append(
|
| 216 |
+
InvertedResidual(
|
| 217 |
+
self.in_channels,
|
| 218 |
+
out_channels,
|
| 219 |
+
stride,
|
| 220 |
+
expand_ratio=expand_ratio,
|
| 221 |
+
conv_cfg=self.conv_cfg,
|
| 222 |
+
norm_cfg=self.norm_cfg,
|
| 223 |
+
act_cfg=self.act_cfg,
|
| 224 |
+
with_cp=self.with_cp))
|
| 225 |
+
self.in_channels = out_channels
|
| 226 |
+
|
| 227 |
+
return nn.Sequential(*layers)
|
| 228 |
+
|
| 229 |
+
def init_weights(self, pretrained=None):
|
| 230 |
+
if isinstance(pretrained, str):
|
| 231 |
+
logger = logging.getLogger()
|
| 232 |
+
load_checkpoint(self, pretrained, strict=False, logger=logger)
|
| 233 |
+
elif pretrained is None:
|
| 234 |
+
for m in self.modules():
|
| 235 |
+
if isinstance(m, nn.Conv2d):
|
| 236 |
+
kaiming_init(m)
|
| 237 |
+
elif isinstance(m, (_BatchNorm, nn.GroupNorm)):
|
| 238 |
+
constant_init(m, 1)
|
| 239 |
+
else:
|
| 240 |
+
raise TypeError('pretrained must be a str or None')
|
| 241 |
+
|
| 242 |
+
def forward(self, x):
|
| 243 |
+
x = self.conv1(x)
|
| 244 |
+
|
| 245 |
+
outs = []
|
| 246 |
+
for i, layer_name in enumerate(self.layers):
|
| 247 |
+
layer = getattr(self, layer_name)
|
| 248 |
+
x = layer(x)
|
| 249 |
+
if i in self.out_indices:
|
| 250 |
+
outs.append(x)
|
| 251 |
+
|
| 252 |
+
if len(outs) == 1:
|
| 253 |
+
return outs[0]
|
| 254 |
+
else:
|
| 255 |
+
return tuple(outs)
|
| 256 |
+
|
| 257 |
+
def _freeze_stages(self):
|
| 258 |
+
if self.frozen_stages >= 0:
|
| 259 |
+
for param in self.conv1.parameters():
|
| 260 |
+
param.requires_grad = False
|
| 261 |
+
for i in range(1, self.frozen_stages + 1):
|
| 262 |
+
layer = getattr(self, f'layer{i}')
|
| 263 |
+
layer.eval()
|
| 264 |
+
for param in layer.parameters():
|
| 265 |
+
param.requires_grad = False
|
| 266 |
+
|
| 267 |
+
def train(self, mode=True):
|
| 268 |
+
super(MobileNetV2, self).train(mode)
|
| 269 |
+
self._freeze_stages()
|
| 270 |
+
if mode and self.norm_eval:
|
| 271 |
+
for m in self.modules():
|
| 272 |
+
if isinstance(m, _BatchNorm):
|
| 273 |
+
m.eval()
|
CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/mobilenet_v3.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
from mmcv.cnn import ConvModule, constant_init, kaiming_init
|
| 5 |
+
from mmcv.runner import load_checkpoint
|
| 6 |
+
from torch.nn.modules.batchnorm import _BatchNorm
|
| 7 |
+
|
| 8 |
+
from ..builder import BACKBONES
|
| 9 |
+
from ..utils import InvertedResidual
|
| 10 |
+
from .base_backbone import BaseBackbone
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
@BACKBONES.register_module()
|
| 14 |
+
class MobileNetv3(BaseBackbone):
|
| 15 |
+
""" MobileNetv3 backbone
|
| 16 |
+
|
| 17 |
+
Args:
|
| 18 |
+
arch (str): Architechture of mobilnetv3, from {small, big}.
|
| 19 |
+
Default: small.
|
| 20 |
+
conv_cfg (dict): Config dict for convolution layer.
|
| 21 |
+
Default: None, which means using conv2d.
|
| 22 |
+
norm_cfg (dict): Config dict for normalization layer.
|
| 23 |
+
Default: dict(type='BN').
|
| 24 |
+
out_indices (None or Sequence[int]): Output from which stages.
|
| 25 |
+
Default: (10, ), which means output tensors from final stage.
|
| 26 |
+
frozen_stages (int): Stages to be frozen (all param fixed).
|
| 27 |
+
Defualt: -1, which means not freezing any parameters.
|
| 28 |
+
norm_eval (bool): Whether to set norm layers to eval mode, namely,
|
| 29 |
+
freeze running stats (mean and var). Note: Effect on Batch Norm
|
| 30 |
+
and its variants only. Default: False.
|
| 31 |
+
with_cp (bool): Use checkpoint or not. Using checkpoint will save
|
| 32 |
+
some memory while slowing down the training speed.
|
| 33 |
+
Defualt: False.
|
| 34 |
+
"""
|
| 35 |
+
# Parameters to build each block:
|
| 36 |
+
# [kernel size, mid channels, out channels, with_se, act type, stride]
|
| 37 |
+
arch_settings = {
|
| 38 |
+
'small': [[3, 16, 16, True, 'ReLU', 2],
|
| 39 |
+
[3, 72, 24, False, 'ReLU', 2],
|
| 40 |
+
[3, 88, 24, False, 'ReLU', 1],
|
| 41 |
+
[5, 96, 40, True, 'HSwish', 2],
|
| 42 |
+
[5, 240, 40, True, 'HSwish', 1],
|
| 43 |
+
[5, 240, 40, True, 'HSwish', 1],
|
| 44 |
+
[5, 120, 48, True, 'HSwish', 1],
|
| 45 |
+
[5, 144, 48, True, 'HSwish', 1],
|
| 46 |
+
[5, 288, 96, True, 'HSwish', 2],
|
| 47 |
+
[5, 576, 96, True, 'HSwish', 1],
|
| 48 |
+
[5, 576, 96, True, 'HSwish', 1]],
|
| 49 |
+
'big': [[3, 16, 16, False, 'ReLU', 1],
|
| 50 |
+
[3, 64, 24, False, 'ReLU', 2],
|
| 51 |
+
[3, 72, 24, False, 'ReLU', 1],
|
| 52 |
+
[5, 72, 40, True, 'ReLU', 2],
|
| 53 |
+
[5, 120, 40, True, 'ReLU', 1],
|
| 54 |
+
[5, 120, 40, True, 'ReLU', 1],
|
| 55 |
+
[3, 240, 80, False, 'HSwish', 2],
|
| 56 |
+
[3, 200, 80, False, 'HSwish', 1],
|
| 57 |
+
[3, 184, 80, False, 'HSwish', 1],
|
| 58 |
+
[3, 184, 80, False, 'HSwish', 1],
|
| 59 |
+
[3, 480, 112, True, 'HSwish', 1],
|
| 60 |
+
[3, 672, 112, True, 'HSwish', 1],
|
| 61 |
+
[5, 672, 160, True, 'HSwish', 1],
|
| 62 |
+
[5, 672, 160, True, 'HSwish', 2],
|
| 63 |
+
[5, 960, 160, True, 'HSwish', 1]]
|
| 64 |
+
} # yapf: disable
|
| 65 |
+
|
| 66 |
+
def __init__(self,
|
| 67 |
+
arch='small',
|
| 68 |
+
conv_cfg=None,
|
| 69 |
+
norm_cfg=dict(type='BN'),
|
| 70 |
+
out_indices=(10, ),
|
| 71 |
+
frozen_stages=-1,
|
| 72 |
+
norm_eval=False,
|
| 73 |
+
with_cp=False):
|
| 74 |
+
super(MobileNetv3, self).__init__()
|
| 75 |
+
assert arch in self.arch_settings
|
| 76 |
+
for index in out_indices:
|
| 77 |
+
if index not in range(0, len(self.arch_settings[arch])):
|
| 78 |
+
raise ValueError('the item in out_indices must in '
|
| 79 |
+
f'range(0, {len(self.arch_settings[arch])}). '
|
| 80 |
+
f'But received {index}')
|
| 81 |
+
|
| 82 |
+
if frozen_stages not in range(-1, len(self.arch_settings[arch])):
|
| 83 |
+
raise ValueError('frozen_stages must be in range(-1, '
|
| 84 |
+
f'{len(self.arch_settings[arch])}). '
|
| 85 |
+
f'But received {frozen_stages}')
|
| 86 |
+
self.out_indices = out_indices
|
| 87 |
+
self.frozen_stages = frozen_stages
|
| 88 |
+
self.arch = arch
|
| 89 |
+
self.conv_cfg = conv_cfg
|
| 90 |
+
self.norm_cfg = norm_cfg
|
| 91 |
+
self.out_indices = out_indices
|
| 92 |
+
self.frozen_stages = frozen_stages
|
| 93 |
+
self.norm_eval = norm_eval
|
| 94 |
+
self.with_cp = with_cp
|
| 95 |
+
|
| 96 |
+
self.in_channels = 16
|
| 97 |
+
self.conv1 = ConvModule(
|
| 98 |
+
in_channels=3,
|
| 99 |
+
out_channels=self.in_channels,
|
| 100 |
+
kernel_size=3,
|
| 101 |
+
stride=2,
|
| 102 |
+
padding=1,
|
| 103 |
+
conv_cfg=conv_cfg,
|
| 104 |
+
norm_cfg=norm_cfg,
|
| 105 |
+
act_cfg=dict(type='HSwish'))
|
| 106 |
+
|
| 107 |
+
self.layers = self._make_layer()
|
| 108 |
+
self.feat_dim = self.arch_settings[arch][-1][2]
|
| 109 |
+
|
| 110 |
+
def _make_layer(self):
|
| 111 |
+
layers = []
|
| 112 |
+
layer_setting = self.arch_settings[self.arch]
|
| 113 |
+
for i, params in enumerate(layer_setting):
|
| 114 |
+
(kernel_size, mid_channels, out_channels, with_se, act,
|
| 115 |
+
stride) = params
|
| 116 |
+
if with_se:
|
| 117 |
+
se_cfg = dict(
|
| 118 |
+
channels=mid_channels,
|
| 119 |
+
ratio=4,
|
| 120 |
+
act_cfg=(dict(type='ReLU'), dict(type='HSigmoid')))
|
| 121 |
+
else:
|
| 122 |
+
se_cfg = None
|
| 123 |
+
|
| 124 |
+
layer = InvertedResidual(
|
| 125 |
+
in_channels=self.in_channels,
|
| 126 |
+
out_channels=out_channels,
|
| 127 |
+
mid_channels=mid_channels,
|
| 128 |
+
kernel_size=kernel_size,
|
| 129 |
+
stride=stride,
|
| 130 |
+
se_cfg=se_cfg,
|
| 131 |
+
with_expand_conv=True,
|
| 132 |
+
conv_cfg=self.conv_cfg,
|
| 133 |
+
norm_cfg=self.norm_cfg,
|
| 134 |
+
act_cfg=dict(type=act),
|
| 135 |
+
with_cp=self.with_cp)
|
| 136 |
+
self.in_channels = out_channels
|
| 137 |
+
layer_name = 'layer{}'.format(i + 1)
|
| 138 |
+
self.add_module(layer_name, layer)
|
| 139 |
+
layers.append(layer_name)
|
| 140 |
+
return layers
|
| 141 |
+
|
| 142 |
+
def init_weights(self, pretrained=None):
|
| 143 |
+
if isinstance(pretrained, str):
|
| 144 |
+
logger = logging.getLogger()
|
| 145 |
+
load_checkpoint(self, pretrained, strict=False, logger=logger)
|
| 146 |
+
elif pretrained is None:
|
| 147 |
+
for m in self.modules():
|
| 148 |
+
if isinstance(m, nn.Conv2d):
|
| 149 |
+
kaiming_init(m)
|
| 150 |
+
elif isinstance(m, nn.BatchNorm2d):
|
| 151 |
+
constant_init(m, 1)
|
| 152 |
+
else:
|
| 153 |
+
raise TypeError('pretrained must be a str or None')
|
| 154 |
+
|
| 155 |
+
def forward(self, x):
|
| 156 |
+
x = self.conv1(x)
|
| 157 |
+
|
| 158 |
+
outs = []
|
| 159 |
+
for i, layer_name in enumerate(self.layers):
|
| 160 |
+
layer = getattr(self, layer_name)
|
| 161 |
+
x = layer(x)
|
| 162 |
+
if i in self.out_indices:
|
| 163 |
+
outs.append(x)
|
| 164 |
+
|
| 165 |
+
if len(outs) == 1:
|
| 166 |
+
return outs[0]
|
| 167 |
+
else:
|
| 168 |
+
return tuple(outs)
|
| 169 |
+
|
| 170 |
+
def _freeze_stages(self):
|
| 171 |
+
if self.frozen_stages >= 0:
|
| 172 |
+
for param in self.conv1.parameters():
|
| 173 |
+
param.requires_grad = False
|
| 174 |
+
for i in range(1, self.frozen_stages + 1):
|
| 175 |
+
layer = getattr(self, f'layer{i}')
|
| 176 |
+
layer.eval()
|
| 177 |
+
for param in layer.parameters():
|
| 178 |
+
param.requires_grad = False
|
| 179 |
+
|
| 180 |
+
def train(self, mode=True):
|
| 181 |
+
super(MobileNetv3, self).train(mode)
|
| 182 |
+
self._freeze_stages()
|
| 183 |
+
if mode and self.norm_eval:
|
| 184 |
+
for m in self.modules():
|
| 185 |
+
if isinstance(m, _BatchNorm):
|
| 186 |
+
m.eval()
|
CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/modules/t2t.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import torch
|
| 3 |
+
from torch import nn
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class Token_performer(nn.Module):
|
| 7 |
+
def __init__(self, dim, in_dim, head_cnt=1, kernel_ratio=0.5, dp1=0.1, dp2 = 0.1):
|
| 8 |
+
super().__init__()
|
| 9 |
+
self.emb = in_dim * head_cnt # we use 1, so it is no need here
|
| 10 |
+
self.kqv = nn.Linear(dim, 3 * self.emb)
|
| 11 |
+
self.dp = nn.Dropout(dp1)
|
| 12 |
+
self.proj = nn.Linear(self.emb, self.emb)
|
| 13 |
+
self.head_cnt = head_cnt
|
| 14 |
+
self.norm1 = nn.LayerNorm(dim)
|
| 15 |
+
self.norm2 = nn.LayerNorm(self.emb)
|
| 16 |
+
self.epsilon = 1e-8 # for stable in division
|
| 17 |
+
|
| 18 |
+
self.mlp = nn.Sequential(
|
| 19 |
+
nn.Linear(self.emb, 1 * self.emb),
|
| 20 |
+
nn.GELU(),
|
| 21 |
+
nn.Linear(1 * self.emb, self.emb),
|
| 22 |
+
nn.Dropout(dp2),
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
self.m = int(self.emb * kernel_ratio)
|
| 26 |
+
self.w = torch.randn(self.m, self.emb)
|
| 27 |
+
self.w = nn.Parameter(nn.init.orthogonal_(self.w) * math.sqrt(self.m), requires_grad=False)
|
| 28 |
+
|
| 29 |
+
def prm_exp(self, x):
|
| 30 |
+
# part of the function is borrow from https://github.com/lucidrains/performer-pytorch
|
| 31 |
+
# and Simo Ryu (https://github.com/cloneofsimo)
|
| 32 |
+
# ==== positive random features for gaussian kernels ====
|
| 33 |
+
# x = (B, T, hs)
|
| 34 |
+
# w = (m, hs)
|
| 35 |
+
# return : x : B, T, m
|
| 36 |
+
# SM(x, y) = E_w[exp(w^T x - |x|/2) exp(w^T y - |y|/2)]
|
| 37 |
+
# therefore return exp(w^Tx - |x|/2)/sqrt(m)
|
| 38 |
+
xd = ((x * x).sum(dim=-1, keepdim=True)).repeat(1, 1, self.m) / 2
|
| 39 |
+
wtx = torch.einsum('bti,mi->btm', x.float(), self.w)
|
| 40 |
+
|
| 41 |
+
return torch.exp(wtx - xd) / math.sqrt(self.m)
|
| 42 |
+
|
| 43 |
+
def single_attn(self, x):
|
| 44 |
+
k, q, v = torch.split(self.kqv(x), self.emb, dim=-1)
|
| 45 |
+
kp, qp = self.prm_exp(k), self.prm_exp(q) # (B, T, m), (B, T, m)
|
| 46 |
+
D = torch.einsum('bti,bi->bt', qp, kp.sum(dim=1)).unsqueeze(dim=2) # (B, T, m) * (B, m) -> (B, T, 1)
|
| 47 |
+
kptv = torch.einsum('bin,bim->bnm', v.float(), kp) # (B, emb, m)
|
| 48 |
+
y = torch.einsum('bti,bni->btn', qp, kptv) / (D.repeat(1, 1, self.emb) + self.epsilon) # (B, T, emb)/Diag
|
| 49 |
+
# skip connection
|
| 50 |
+
y = v + self.dp(self.proj(y)) # same as token_transformer in T2T layer, use v as skip connection
|
| 51 |
+
|
| 52 |
+
return y
|
| 53 |
+
|
| 54 |
+
def forward(self, x):
|
| 55 |
+
x = self.single_attn(self.norm1(x))
|
| 56 |
+
x = x + self.mlp(self.norm2(x))
|
| 57 |
+
return x
|
CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/modules/vit.py
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch import nn
|
| 3 |
+
from mmcls.models.vit.layers import DropPath
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class Mlp(nn.Module):
|
| 7 |
+
def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.):
|
| 8 |
+
super().__init__()
|
| 9 |
+
out_features = out_features or in_features
|
| 10 |
+
hidden_features = hidden_features or in_features
|
| 11 |
+
self.fc1 = nn.Linear(in_features, hidden_features)
|
| 12 |
+
self.act = act_layer()
|
| 13 |
+
self.fc2 = nn.Linear(hidden_features, out_features)
|
| 14 |
+
self.drop = nn.Dropout(drop)
|
| 15 |
+
|
| 16 |
+
def forward(self, x):
|
| 17 |
+
x = self.fc1(x)
|
| 18 |
+
x = self.act(x)
|
| 19 |
+
x = self.drop(x)
|
| 20 |
+
x = self.fc2(x)
|
| 21 |
+
x = self.drop(x)
|
| 22 |
+
return x
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class Attention(nn.Module):
|
| 26 |
+
def __init__(self, dim, num_heads=8, qkv_bias=False, qk_scale=None, attn_drop=0., proj_drop=0.):
|
| 27 |
+
super().__init__()
|
| 28 |
+
self.num_heads = num_heads
|
| 29 |
+
head_dim = dim // num_heads
|
| 30 |
+
# NOTE scale factor was wrong in my original version, can set manually to be compat with prev weights
|
| 31 |
+
self.scale = qk_scale or head_dim ** -0.5
|
| 32 |
+
|
| 33 |
+
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
|
| 34 |
+
self.attn_drop = nn.Dropout(attn_drop)
|
| 35 |
+
self.proj = nn.Linear(dim, dim)
|
| 36 |
+
self.proj_drop = nn.Dropout(proj_drop)
|
| 37 |
+
|
| 38 |
+
def forward(self, x):
|
| 39 |
+
B, N, C = x.shape
|
| 40 |
+
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads)
|
| 41 |
+
|
| 42 |
+
qkv = qkv.permute(2, 0, 3, 1, 4)
|
| 43 |
+
q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple)
|
| 44 |
+
|
| 45 |
+
attn = (q @ k.transpose(-2, -1)) * self.scale # [B, head_num, token_num, token_num]
|
| 46 |
+
|
| 47 |
+
attn = attn.softmax(dim=-1)
|
| 48 |
+
attn = self.attn_drop(attn)
|
| 49 |
+
|
| 50 |
+
x = (attn @ v).transpose(1, 2).reshape(B, N, C)
|
| 51 |
+
x = self.proj(x)
|
| 52 |
+
x = self.proj_drop(x)
|
| 53 |
+
return x
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
class HeadFusionAttention(nn.Module):
|
| 57 |
+
"""
|
| 58 |
+
fuse front head output add current head input as current input
|
| 59 |
+
Origin: head2_output = f(head2_input)
|
| 60 |
+
Fuse: head2_output = f(head1_output + head2_input)
|
| 61 |
+
"""
|
| 62 |
+
def __init__(self, dim, num_heads=8, qkv_bias=False, qk_scale=None, attn_drop=0., proj_drop=0.):
|
| 63 |
+
super().__init__()
|
| 64 |
+
self.num_heads = num_heads
|
| 65 |
+
head_dim = dim // num_heads
|
| 66 |
+
# NOTE scale factor was wrong in my original version, can set manually to be compat with prev weights
|
| 67 |
+
self.scale = qk_scale or head_dim ** -0.5
|
| 68 |
+
|
| 69 |
+
# self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
|
| 70 |
+
self.group_number = 4 # 每个group内部并行计算
|
| 71 |
+
self.qkv = nn.ModuleList([nn.Linear(dim//self.group_number, (dim//self.group_number)*3, bias=qkv_bias) for _ in range(self.group_number)])
|
| 72 |
+
self.attn_drop = nn.Dropout(attn_drop)
|
| 73 |
+
self.proj = nn.Linear(dim, dim)
|
| 74 |
+
self.proj_drop = nn.Dropout(proj_drop)
|
| 75 |
+
|
| 76 |
+
def forward(self, x):
|
| 77 |
+
B, N, C = x.shape
|
| 78 |
+
# H = self.num_heads
|
| 79 |
+
# dim = C // self.num_heads
|
| 80 |
+
g = self.group_number
|
| 81 |
+
g_dim = C // self.group_number
|
| 82 |
+
x = x.reshape((B, N, g, g_dim))
|
| 83 |
+
|
| 84 |
+
outputs = []
|
| 85 |
+
head_x = torch.zeros((B, N, g_dim), device=x.device)
|
| 86 |
+
for i in range(g):
|
| 87 |
+
# self-attention
|
| 88 |
+
current_x = x[:, :, i]
|
| 89 |
+
current_x = current_x + head_x
|
| 90 |
+
qkv = self.qkv[i](current_x).reshape(B, N, 3, g_dim)
|
| 91 |
+
qkv = qkv.permute(2, 0, 1, 3) # [3, B, N, dim]
|
| 92 |
+
q, k, v = qkv[0], qkv[1], qkv[2]
|
| 93 |
+
attn = (q @ k.transpose(-2, -1)) * self.scale # [B, N, N]
|
| 94 |
+
attn = attn.softmax(dim=-1)
|
| 95 |
+
attn = self.attn_drop(attn)
|
| 96 |
+
head_x = (attn @ v) # [B, N, d]
|
| 97 |
+
outputs.append(head_x)
|
| 98 |
+
x = torch.cat(outputs, dim=-1)
|
| 99 |
+
|
| 100 |
+
x = self.proj(x)
|
| 101 |
+
x = self.proj_drop(x)
|
| 102 |
+
return x
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
class HeadFusionAttentionV2(nn.Module):
|
| 106 |
+
"""
|
| 107 |
+
V2: one branch is origin, onther is modified
|
| 108 |
+
fuse front head output add current head input as current input
|
| 109 |
+
Origin: head2_output = f(head2_input)
|
| 110 |
+
Fuse: head2_output = f(head1_output + head2_input)
|
| 111 |
+
"""
|
| 112 |
+
def __init__(self, dim, num_heads=8, qkv_bias=False, qk_scale=None, attn_drop=0., proj_drop=0.):
|
| 113 |
+
super().__init__()
|
| 114 |
+
self.num_heads = num_heads
|
| 115 |
+
head_dim = dim // num_heads
|
| 116 |
+
# NOTE scale factor was wrong in my original version, can set manually to be compat with prev weights
|
| 117 |
+
self.scale = qk_scale or head_dim ** -0.5
|
| 118 |
+
|
| 119 |
+
# self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
|
| 120 |
+
self.group_number = 2 # 每个group内部并行计算.
|
| 121 |
+
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
|
| 122 |
+
self.qkv2 = nn.ModuleList([nn.Linear(dim//self.group_number, (dim//self.group_number)*3, bias=qkv_bias) for _ in range(self.group_number)])
|
| 123 |
+
self.attn_drop = nn.Dropout(attn_drop)
|
| 124 |
+
self.proj = nn.Linear(dim, dim)
|
| 125 |
+
self.proj_drop = nn.Dropout(proj_drop)
|
| 126 |
+
|
| 127 |
+
def forward(self, x):
|
| 128 |
+
# original
|
| 129 |
+
B, N, C = x.shape
|
| 130 |
+
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads)
|
| 131 |
+
|
| 132 |
+
qkv = qkv.permute(2, 0, 3, 1, 4)
|
| 133 |
+
q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple)
|
| 134 |
+
|
| 135 |
+
attn = (q @ k.transpose(-2, -1)) * self.scale # [B, head_num, token_num, token_num]
|
| 136 |
+
|
| 137 |
+
attn = attn.softmax(dim=-1)
|
| 138 |
+
attn = self.attn_drop(attn)
|
| 139 |
+
|
| 140 |
+
origin = (attn @ v) # [B, H, N, dim]
|
| 141 |
+
|
| 142 |
+
# modify
|
| 143 |
+
g = self.group_number
|
| 144 |
+
g_dim = C // self.group_number
|
| 145 |
+
x = x.reshape((B, N, g, g_dim))
|
| 146 |
+
n = self.num_heads // g # head number per group
|
| 147 |
+
|
| 148 |
+
origin = origin.transpose(1, 2).reshape((B, N, g, g_dim))
|
| 149 |
+
|
| 150 |
+
outputs = []
|
| 151 |
+
head_x = torch.zeros((B, N, g_dim), device=x.device)
|
| 152 |
+
for i in range(g):
|
| 153 |
+
# self-attention
|
| 154 |
+
current_x = x[:, :, i]
|
| 155 |
+
current_x = current_x + head_x
|
| 156 |
+
qkv = self.qkv2[i](current_x).reshape(B, N, 3, g_dim)
|
| 157 |
+
qkv = qkv.permute(2, 0, 1, 3) # [3, B, N, dim]
|
| 158 |
+
q, k, v = qkv[0], qkv[1], qkv[2]
|
| 159 |
+
attn = (q @ k.transpose(-2, -1)) * self.scale # [B, N, N]
|
| 160 |
+
attn = attn.softmax(dim=-1)
|
| 161 |
+
attn = self.attn_drop(attn)
|
| 162 |
+
head_x = (attn @ v) # [B, N, d]
|
| 163 |
+
outputs.append(head_x + origin[:, :, i])
|
| 164 |
+
x = torch.cat(outputs, dim=-1)
|
| 165 |
+
|
| 166 |
+
x = self.proj(x)
|
| 167 |
+
x = self.proj_drop(x)
|
| 168 |
+
return x
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
class Block(nn.Module):
|
| 175 |
+
def __init__(self, dim, num_heads, mlp_ratio=4., qkv_bias=False, qk_scale=None, drop=0., attn_drop=0.,
|
| 176 |
+
drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm, head_fusion=False,
|
| 177 |
+
):
|
| 178 |
+
super().__init__()
|
| 179 |
+
self.norm1 = norm_layer(dim)
|
| 180 |
+
if head_fusion:
|
| 181 |
+
self.attn = HeadFusionAttentionV2(
|
| 182 |
+
dim, num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop)
|
| 183 |
+
else:
|
| 184 |
+
self.attn = Attention(
|
| 185 |
+
dim, num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop)
|
| 186 |
+
# NOTE: drop path for stochastic depth, we shall see if this is better than dropout here
|
| 187 |
+
self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
|
| 188 |
+
self.norm2 = norm_layer(dim)
|
| 189 |
+
mlp_hidden_dim = int(dim * mlp_ratio)
|
| 190 |
+
self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop)
|
| 191 |
+
|
| 192 |
+
def forward(self, x):
|
| 193 |
+
feature = self.attn(self.norm1(x))
|
| 194 |
+
x = x + self.drop_path(feature)
|
| 195 |
+
x = x + self.drop_path(self.mlp(self.norm2(x)))
|
| 196 |
+
return x
|
| 197 |
+
|
CAGE_expression_inference-apvit/apvit_mmcls/models/backbones/modules/vit_pooling.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch import nn
|
| 3 |
+
from .vit import Mlp
|
| 4 |
+
from mmcls.models.utils import top_pool
|
| 5 |
+
from mmcls.models.vit.layers import DropPath
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class PoolingAttention(nn.Module):
|
| 9 |
+
def __init__(self, dim, num_heads=8, qkv_bias=False, qk_scale=None, attn_drop=0., proj_drop=0.,
|
| 10 |
+
pool_config=None):
|
| 11 |
+
super().__init__()
|
| 12 |
+
self.num_heads = num_heads
|
| 13 |
+
self.dim = dim
|
| 14 |
+
head_dim = dim // num_heads
|
| 15 |
+
# NOTE scale factor was wrong in my original version, can set manually to be compat with prev weights
|
| 16 |
+
self.scale = qk_scale or head_dim ** -0.5
|
| 17 |
+
|
| 18 |
+
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
|
| 19 |
+
self.attn_drop = nn.Dropout(attn_drop)
|
| 20 |
+
self.proj = nn.Linear(dim, dim)
|
| 21 |
+
self.proj_drop = nn.Dropout(proj_drop)
|
| 22 |
+
self.pool_config = pool_config
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def forward(self, x):
|
| 26 |
+
B, N, C = x.shape
|
| 27 |
+
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads)
|
| 28 |
+
qkv = qkv.permute(2, 0, 3, 1, 4)
|
| 29 |
+
q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple)
|
| 30 |
+
|
| 31 |
+
attn = (q @ k.transpose(-2, -1)) * self.scale # [B, head_num, token_num, token_num]
|
| 32 |
+
|
| 33 |
+
if self.pool_config:
|
| 34 |
+
attn_method = self.pool_config.get('attn_method')
|
| 35 |
+
if attn_method == 'SUM_ABS_1':
|
| 36 |
+
attn_weight = attn[:, :, 0, :].transpose(-1, -2) # [B, token_num, head_num]
|
| 37 |
+
attn_weight = torch.sum(torch.abs(attn_weight), dim=-1).unsqueeze(-1)
|
| 38 |
+
elif attn_method == 'SUM':
|
| 39 |
+
attn_weight = attn[:, :, 0, :].transpose(-1, -2) # [B, token_num, head_num]
|
| 40 |
+
attn_weight = torch.sum(attn_weight, dim=-1).unsqueeze(-1)
|
| 41 |
+
elif attn_method == 'MAX':
|
| 42 |
+
attn_weight = attn[:, :, 0, :].transpose(-1, -2)
|
| 43 |
+
attn_weight = torch.max(attn_weight, dim=-1)[0].unsqueeze(-1)
|
| 44 |
+
else:
|
| 45 |
+
raise ValueError('Invalid attn_method: %s' % attn_method)
|
| 46 |
+
|
| 47 |
+
# attn_weight = torch.rand(attn_weight.shape, device=attn_weight.device)
|
| 48 |
+
keep_index = top_pool(attn_weight, dim=self.dim, **self.pool_config)
|
| 49 |
+
else:
|
| 50 |
+
keep_index = None
|
| 51 |
+
|
| 52 |
+
attn = attn.softmax(dim=-1)
|
| 53 |
+
attn = self.attn_drop(attn)
|
| 54 |
+
|
| 55 |
+
x = (attn @ v).transpose(1, 2).reshape(B, N, C)
|
| 56 |
+
x = self.proj(x)
|
| 57 |
+
x = self.proj_drop(x)
|
| 58 |
+
return x, keep_index
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class PoolingBlock(nn.Module):
|
| 62 |
+
|
| 63 |
+
def __init__(self, dim=0, num_heads=0, mlp_ratio=4., qkv_bias=False, qk_scale=None, drop=0., attn_drop=0.,
|
| 64 |
+
drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm, pool_config=None, **kwargs):
|
| 65 |
+
super().__init__()
|
| 66 |
+
self.norm1 = norm_layer(dim)
|
| 67 |
+
self.attn = PoolingAttention(
|
| 68 |
+
dim, num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop,
|
| 69 |
+
pool_config=pool_config)
|
| 70 |
+
# NOTE: drop path for stochastic depth, we shall see if this is better than dropout here
|
| 71 |
+
self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
|
| 72 |
+
self.norm2 = norm_layer(dim)
|
| 73 |
+
mlp_hidden_dim = int(dim * mlp_ratio)
|
| 74 |
+
self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop)
|
| 75 |
+
|
| 76 |
+
def forward(self, x):
|
| 77 |
+
feature, keep_index = self.attn(self.norm1(x))
|
| 78 |
+
x = x + self.drop_path(feature)
|
| 79 |
+
if keep_index is not None:
|
| 80 |
+
if len(keep_index) != x.shape[1]:
|
| 81 |
+
x = x.gather(dim=1, index=keep_index)
|
| 82 |
+
# pooled_x = []
|
| 83 |
+
# for i in range(keep_index.shape[0]):
|
| 84 |
+
# pooled_x.append(x[i, keep_index[i, :, 0]])
|
| 85 |
+
# x = torch.stack(pooled_x)
|
| 86 |
+
# assert torch.all(torch.eq(quick_x, x))
|
| 87 |
+
|
| 88 |
+
x = x + self.drop_path(self.mlp(self.norm2(x)))
|
| 89 |
+
return x
|