├── ParC_ConvNets ├── ParC_convnext.py ├── ParC_mobilenetv2.py ├── ParC_resnet50.py ├── __init__.py └── model_test.py ├── REDAME_ch.md ├── Readme.md ├── common ├── __init__.py └── __pycache__ │ └── __init__.cpython-38.pyc ├── config ├── classification │ ├── edgeformer │ │ ├── edgeformer_h.yaml │ │ ├── edgeformer_s.yaml │ │ └── edgeformer_xs.yaml │ └── mobilevit │ │ ├── mobilevit_small.yaml │ │ └── mobilevit_x_small.yaml ├── detection │ ├── edgeformer │ │ └── ssd_edgeformer_s.yaml │ └── mobilevit │ │ ├── ssd_mobilevit_small_320.yaml │ │ └── ssd_mobilevit_x_small_320.yaml └── segmentation │ ├── edgeformer │ └── deeplabv3_edgeformer_s.yaml │ └── mobilevit │ ├── deeplabv3_mobilevit_small.yaml │ └── deeplabv3_mobilevit_x_small.yaml ├── convert2onnx ├── converted_models │ ├── edgeformer.onnx │ ├── edgeformer.onnx_converted.mnn │ ├── mobilevit.onnx │ └── mobilevit.onnx_converted.mnn └── model_convert2onnx.py ├── cvnets ├── __init__.py ├── layers │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-38.pyc │ │ ├── adaptive_pool.cpython-38.pyc │ │ ├── base_layer.cpython-38.pyc │ │ ├── conv_layer.cpython-38.pyc │ │ ├── dropout.cpython-38.pyc │ │ ├── flatten.cpython-38.pyc │ │ ├── global_pool.cpython-38.pyc │ │ ├── identity.cpython-38.pyc │ │ ├── linear_layer.cpython-38.pyc │ │ ├── multi_head_attention.cpython-38.pyc │ │ ├── non_linear_layers.cpython-38.pyc │ │ ├── normalization_layers.cpython-38.pyc │ │ ├── pixel_shuffle.cpython-38.pyc │ │ ├── pooling.cpython-38.pyc │ │ ├── positional_encoding.cpython-38.pyc │ │ ├── random_layers.cpython-38.pyc │ │ ├── sep_conv_layer.cpython-38.pyc │ │ └── upsample.cpython-38.pyc │ ├── activation │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-38.pyc │ │ │ ├── gelu.cpython-38.pyc │ │ │ ├── hard_sigmoid.cpython-38.pyc │ │ │ ├── hard_swish.cpython-38.pyc │ │ │ ├── leaky_relu.cpython-38.pyc │ │ │ ├── prelu.cpython-38.pyc │ │ │ ├── relu.cpython-38.pyc │ │ │ ├── relu6.cpython-38.pyc │ │ │ ├── sigmoid.cpython-38.pyc │ │ │ └── swish.cpython-38.pyc │ │ ├── gelu.py │ │ ├── hard_sigmoid.py │ │ ├── hard_swish.py │ │ ├── leaky_relu.py │ │ ├── prelu.py │ │ ├── relu.py │ │ ├── relu6.py │ │ ├── sigmoid.py │ │ └── swish.py │ ├── adaptive_pool.py │ ├── base_layer.py │ ├── conv_layer.py │ ├── dropout.py │ ├── flatten.py │ ├── global_pool.py │ ├── identity.py │ ├── linear_layer.py │ ├── multi_head_attention.py │ ├── non_linear_layers.py │ ├── normalization │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-38.pyc │ │ │ ├── batch_norm.cpython-38.pyc │ │ │ ├── group_norm.cpython-38.pyc │ │ │ ├── instance_norm.cpython-38.pyc │ │ │ ├── layer_norm.cpython-38.pyc │ │ │ └── sync_batch_norm.cpython-38.pyc │ │ ├── batch_norm.py │ │ ├── group_norm.py │ │ ├── instance_norm.py │ │ ├── layer_norm.py │ │ └── sync_batch_norm.py │ ├── normalization_layers.py │ ├── pixel_shuffle.py │ ├── pooling.py │ ├── positional_encoding.py │ ├── random_layers.py │ ├── sep_conv_layer.py │ └── upsample.py ├── misc │ ├── anchor_generator.py │ ├── averaging_utils.py │ ├── box_utils.py │ ├── common.py │ ├── init_utils.py │ ├── match_prior.py │ ├── profiler.py │ └── third_party │ │ └── ssd_utils.py ├── models │ ├── __init__.py │ ├── classification │ │ ├── __init__.py │ │ ├── base_cls.py │ │ ├── config │ │ │ ├── edgeformer.py │ │ │ └── mobilevit.py │ │ ├── edgeformer.py │ │ └── mobilevit.py │ ├── detection │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-38.pyc │ │ │ ├── base_detection.cpython-38.pyc │ │ │ └── ssd.cpython-38.pyc │ │ ├── base_detection.py │ │ └── ssd.py │ └── segmentation │ │ ├── __init__.py │ │ ├── __pycache__ │ │ ├── __init__.cpython-38.pyc │ │ ├── base_seg.cpython-38.pyc │ │ └── enc_dec.cpython-38.pyc │ │ ├── base_seg.py │ │ ├── enc_dec.py │ │ └── heads │ │ ├── __init__.py │ │ ├── __pycache__ │ │ ├── __init__.cpython-38.pyc │ │ ├── base_seg_head.cpython-38.pyc │ │ └── deeplabv3.cpython-38.pyc │ │ ├── base_seg_head.py │ │ └── deeplabv3.py └── modules │ ├── __init__.py │ ├── __pycache__ │ ├── aspp_block.cpython-38.pyc │ ├── base_module.cpython-38.pyc │ ├── chip_former_block.cpython-38.pyc │ ├── chipformer_block.cpython-38.pyc │ ├── feature_pyramid.cpython-38.pyc │ ├── mobilevit_block.cpython-38.pyc │ ├── ppm.cpython-38.pyc │ ├── squeeze_excitation.cpython-38.pyc │ ├── ssd.cpython-38.pyc │ └── transformer.cpython-38.pyc │ ├── aspp_block.py │ ├── base_module.py │ ├── edgeformer_block.py │ ├── feature_pyramid.py │ ├── mobilenetv2.py │ ├── mobilevit_block.py │ ├── ppm.py │ ├── squeeze_excitation.py │ ├── ssd.py │ └── transformer.py ├── data ├── __init__.py ├── data_loaders.py ├── dataset_preparing │ ├── classification_imagenet │ │ ├── __init__.py │ │ └── tar2jpeg.py │ ├── detection_coco │ │ └── download_coco.sh │ └── segmentation_pascal_&_coco │ │ ├── pascal_aug_tools │ │ ├── __init__.py │ │ ├── convert_labels.py │ │ ├── mat2png.py │ │ └── utils.py │ │ └── pre_coco.py ├── datasets │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-38.pyc │ │ └── dataset_base.cpython-38.pyc │ ├── classification │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-38.pyc │ │ │ └── imagenet.cpython-38.pyc │ │ └── imagenet.py │ ├── dataset_base.py │ ├── detection │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-38.pyc │ │ │ └── coco.cpython-38.pyc │ │ └── coco.py │ └── segmentation │ │ ├── __init__.py │ │ ├── __pycache__ │ │ ├── __init__.cpython-38.pyc │ │ └── pascal_voc.cpython-38.pyc │ │ └── pascal_voc.py ├── sampler │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-38.pyc │ │ ├── base_sampler.cpython-38.pyc │ │ ├── batch_sampler.cpython-38.pyc │ │ ├── utils.cpython-38.pyc │ │ └── variable_batch_sampler.cpython-38.pyc │ ├── base_sampler.py │ ├── batch_sampler.py │ ├── utils.py │ └── variable_batch_sampler.py └── transforms │ ├── __init__.py │ ├── __pycache__ │ ├── __init__.cpython-38.pyc │ ├── base_transforms.cpython-38.pyc │ └── image.cpython-38.pyc │ ├── base_transforms.py │ └── image.py ├── drawing_results ├── cls_scatter.py ├── det_scatter.py ├── result │ ├── cls.png │ ├── det.png │ ├── results.png │ └── seg.png └── seg_scatter.py ├── engine ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-38.pyc │ ├── evaluation_engine.cpython-38.pyc │ ├── training_engine.cpython-38.pyc │ └── utils.cpython-38.pyc ├── detection_utils │ ├── __init__.py │ └── coco_map.py ├── eval_detection.py ├── eval_segmentation.py ├── evaluation_engine.py ├── segmentation_utils │ ├── __init__.py │ └── cityscapes_iou.py ├── training_engine.py └── utils.py ├── eval_cls.py ├── eval_det.py ├── eval_seg.py ├── loss_fn ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-38.pyc │ ├── base_criteria.cpython-38.pyc │ ├── classification.cpython-38.pyc │ ├── detection.cpython-38.pyc │ ├── distillation.cpython-38.pyc │ └── segmentation.cpython-38.pyc ├── base_criteria.py ├── classification.py ├── classification_loss_fns │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-38.pyc │ │ ├── cross_entropy.cpython-38.pyc │ │ └── label_smoothing.cpython-38.pyc │ ├── cross_entropy.py │ └── label_smoothing.py ├── detection.py ├── detection_loss_fns │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-38.pyc │ │ └── ssd_multibox_loss.cpython-38.pyc │ └── ssd_multibox_loss.py ├── distillation.py ├── distillation_loss_fns │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-38.pyc │ │ └── vanilla.cpython-38.pyc │ └── vanilla.py ├── segmentation.py └── segmentation_loss_fns │ ├── __init__.py │ ├── __pycache__ │ ├── __init__.cpython-38.pyc │ └── cross_entropy.cpython-38.pyc │ └── cross_entropy.py ├── main_eval.py ├── main_train.py ├── metrics ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-38.pyc │ ├── intersection_over_union.cpython-38.pyc │ ├── metric_monitor.cpython-38.pyc │ ├── stats.cpython-38.pyc │ └── topk_accuracy.cpython-38.pyc ├── intersection_over_union.py ├── metric_monitor.py ├── stats.py └── topk_accuracy.py ├── optim ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-38.pyc │ ├── adam.cpython-38.pyc │ ├── adamw.cpython-38.pyc │ ├── base_optim.cpython-38.pyc │ └── sgd.cpython-38.pyc ├── adam.py ├── adamw.py ├── base_optim.py ├── scheduler │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-38.pyc │ │ ├── base_scheduler.cpython-38.pyc │ │ ├── cosine.cpython-38.pyc │ │ ├── cyclic.cpython-38.pyc │ │ └── polynomial.cpython-38.pyc │ ├── base_scheduler.py │ ├── cosine.py │ ├── cyclic.py │ └── polynomial.py └── sgd.py ├── options ├── opts.py └── utils.py ├── pretrained_models ├── classification │ └── checkpoint_ema_avg.pt ├── detection │ └── checkpoint_ema_avg.pt ├── model_info.txt └── segmentation │ └── checkpoint_ema_avg.pt ├── requirements.txt └── utils ├── checkpoint_utils.py ├── color_map.py ├── common_utils.py ├── ddp_utils.py ├── download_utils.py ├── logger.py ├── math_utils.py ├── pytorch_to_coreml.py └── tensor_utils.py /ParC_ConvNets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/ParC_ConvNets/__init__.py -------------------------------------------------------------------------------- /ParC_ConvNets/model_test.py: -------------------------------------------------------------------------------- 1 | from ParC_ConvNets.ParC_convnext import parc_convnext_xt 2 | from ParC_ConvNets.ParC_resnet50 import parc_res50 3 | from ParC_ConvNets.ParC_mobilenetv2 import parc_mv2 4 | import torch 5 | 6 | input=torch.randn(2,3,224,224) 7 | model = parc_convnext_xt() 8 | out=model(input) 9 | print('done') 10 | -------------------------------------------------------------------------------- /common/__init__.py: -------------------------------------------------------------------------------- 1 | SUPPORTED_IMAGE_EXTNS = ['.png', '.jpg', '.jpeg'] # Add image formats here 2 | 3 | DEFAULT_IMAGE_WIDTH = DEFAULT_IMAGE_HEIGHT = 256 4 | DEFAULT_IMAGE_CHANNELS = 3 5 | DEFAULT_LOG_FREQ = 500 6 | 7 | DEFAULT_ITERATIONS = 300000 8 | DEFAULT_EPOCHS = 300 9 | DEFAULT_MAX_ITERATIONS = DEFAULT_MAX_EPOCHS = 10000000 10 | 11 | TMP_RES_FOLDER = "results_tmp" 12 | 13 | TMP_CACHE_LOC = '/tmp' 14 | -------------------------------------------------------------------------------- /common/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/common/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /config/classification/edgeformer/edgeformer_h.yaml: -------------------------------------------------------------------------------- 1 | common: 2 | run_label: "run_1" 3 | log_freq: 500 4 | auto_resume: true 5 | mixed_precision: True 6 | results_loc: "/home/disk/result/classification_imagenet1k/edgeformer-h" 7 | dataset: 8 | root_train: "/home/disk/data/imagenet1k/training" 9 | root_val: "/home/disk/data/imagenet1k/validation" 10 | name: "imagenet" 11 | category: "classification" 12 | train_batch_size0: 128 13 | val_batch_size0: 64 14 | eval_batch_size0: 64 15 | workers: 8 16 | persistent_workers: false 17 | pin_memory: true 18 | image_augmentation: 19 | random_resized_crop: 20 | enable: true 21 | interpolation: "bilinear" 22 | random_horizontal_flip: 23 | enable: true 24 | sampler: 25 | name: "variable_batch_sampler" 26 | vbs: 27 | crop_size_width: 256 28 | crop_size_height: 256 29 | max_n_scales: 5 30 | min_crop_size_width: 160 31 | max_crop_size_width: 320 32 | min_crop_size_height: 160 33 | max_crop_size_height: 320 34 | check_scale: 32 35 | loss: 36 | category: "classification" 37 | classification: 38 | name: "label_smoothing" 39 | label_smoothing_factor: 0.1 40 | optim: 41 | name: "adamw" 42 | weight_decay: 0.05 43 | no_decay_bn_filter_bias: false 44 | adamw: 45 | beta1: 0.9 46 | beta2: 0.999 47 | scheduler: 48 | name: "cosine" 49 | is_iteration_based: false 50 | max_epochs: 300 51 | warmup_iterations: 3000 52 | warmup_init_lr: 0.0004 53 | cosine: 54 | max_lr: 0.004 55 | min_lr: 0.0004 56 | model: 57 | classification: 58 | name: "edgeformer" 59 | classifier_dropout: 0.2 60 | edge: 61 | scale: "scale_h" 62 | mode: "outer_frame_v1" 63 | kernel: "gcc_ca" 64 | fusion: "concat" 65 | instance_kernel: "interpolation_bilinear" 66 | use_pe: True 67 | activation: 68 | name: "swish" 69 | normalization: 70 | name: "batch_norm_2d" 71 | momentum: 0.1 72 | activation: 73 | name: "swish" 74 | layer: 75 | global_pool: "mean" 76 | conv_init: "kaiming_normal" 77 | linear_init: "trunc_normal" 78 | linear_init_std_dev: 0.02 79 | ema: 80 | enable: True 81 | momentum: 0.0005 82 | ddp: 83 | enable: True 84 | rank: 0 85 | world_size: -1 86 | dist_port: 30792 87 | stats: 88 | name: [ "loss", "top1", "top5" ] 89 | checkpoint_metric: "top1" 90 | checkpoint_metric_max: true 91 | -------------------------------------------------------------------------------- /config/classification/edgeformer/edgeformer_s.yaml: -------------------------------------------------------------------------------- 1 | common: 2 | run_label: "run_1" 3 | log_freq: 500 4 | auto_resume: true 5 | mixed_precision: True 6 | results_loc: "/home/disk/result/classification_imagenet1k/edgeformer-s" 7 | dataset: 8 | root_train: "/home/disk/data/imagenet1k/training" 9 | root_val: "/home/disk/data/imagenet1k/validation" 10 | name: "imagenet" 11 | category: "classification" 12 | train_batch_size0: 128 13 | val_batch_size0: 64 14 | eval_batch_size0: 64 15 | workers: 8 16 | persistent_workers: false 17 | pin_memory: true 18 | image_augmentation: 19 | random_resized_crop: 20 | enable: true 21 | interpolation: "bilinear" 22 | random_horizontal_flip: 23 | enable: true 24 | sampler: 25 | name: "variable_batch_sampler" 26 | vbs: 27 | crop_size_width: 256 28 | crop_size_height: 256 29 | max_n_scales: 5 30 | min_crop_size_width: 160 31 | max_crop_size_width: 320 32 | min_crop_size_height: 160 33 | max_crop_size_height: 320 34 | check_scale: 32 35 | loss: 36 | category: "classification" 37 | classification: 38 | name: "label_smoothing" 39 | label_smoothing_factor: 0.1 40 | optim: 41 | name: "adamw" 42 | weight_decay: 0.025 43 | no_decay_bn_filter_bias: false 44 | adamw: 45 | beta1: 0.9 46 | beta2: 0.999 47 | scheduler: 48 | name: "cosine" 49 | is_iteration_based: false 50 | max_epochs: 300 51 | warmup_iterations: 3000 52 | warmup_init_lr: 0.0004 53 | cosine: 54 | max_lr: 0.004 55 | min_lr: 0.0004 56 | model: 57 | classification: 58 | name: "edgeformer" 59 | classifier_dropout: 0.2 60 | edge: 61 | scale: "scale_s" 62 | mode: "outer_frame_v1" 63 | kernel: "gcc_ca" 64 | fusion: "concat" 65 | instance_kernel: "interpolation_bilinear" 66 | use_pe: True 67 | activation: 68 | name: "swish" 69 | normalization: 70 | name: "batch_norm_2d" 71 | momentum: 0.1 72 | activation: 73 | name: "swish" 74 | layer: 75 | global_pool: "mean" 76 | conv_init: "kaiming_normal" 77 | linear_init: "trunc_normal" 78 | linear_init_std_dev: 0.02 79 | ema: 80 | enable: true 81 | momentum: 0.0005 82 | ddp: 83 | enable: True 84 | rank: 0 85 | world_size: -1 86 | dist_port: 30787 87 | stats: 88 | name: [ "loss", "top1", "top5" ] 89 | checkpoint_metric: "top1" 90 | checkpoint_metric_max: true 91 | -------------------------------------------------------------------------------- /config/classification/edgeformer/edgeformer_xs.yaml: -------------------------------------------------------------------------------- 1 | common: 2 | run_label: "run_1" 3 | log_freq: 500 4 | auto_resume: true 5 | mixed_precision: True 6 | results_loc: "/home/disk/result/classification_imagenet1k/edgeformer-xs" 7 | dataset: 8 | root_train: "/home/disk/data/imagenet1k/training" 9 | root_val: "/home/disk/data/imagenet1k/validation" 10 | name: "imagenet" 11 | category: "classification" 12 | train_batch_size0: 128 13 | val_batch_size0: 64 14 | eval_batch_size0: 64 15 | workers: 8 16 | persistent_workers: false 17 | pin_memory: true 18 | image_augmentation: 19 | random_resized_crop: 20 | enable: true 21 | interpolation: "bilinear" 22 | random_horizontal_flip: 23 | enable: true 24 | sampler: 25 | name: "variable_batch_sampler" 26 | vbs: 27 | crop_size_width: 256 28 | crop_size_height: 256 29 | max_n_scales: 5 30 | min_crop_size_width: 160 31 | max_crop_size_width: 320 32 | min_crop_size_height: 160 33 | max_crop_size_height: 320 34 | check_scale: 32 35 | loss: 36 | category: "classification" 37 | classification: 38 | name: "label_smoothing" 39 | label_smoothing_factor: 0.1 40 | optim: 41 | name: "adamw" 42 | weight_decay: 0.025 43 | no_decay_bn_filter_bias: false 44 | adamw: 45 | beta1: 0.9 46 | beta2: 0.999 47 | scheduler: 48 | name: "cosine" 49 | is_iteration_based: false 50 | max_epochs: 300 51 | warmup_iterations: 3000 52 | warmup_init_lr: 0.0004 53 | cosine: 54 | max_lr: 0.004 55 | min_lr: 0.0004 56 | model: 57 | classification: 58 | name: "edgeformer" 59 | classifier_dropout: 0.2 60 | edge: 61 | scale: "scale_xs" 62 | mode: "outer_frame_v1" 63 | kernel: "gcc_ca" 64 | fusion: "add" 65 | instance_kernel: "interpolation_bilinear" 66 | use_pe: True 67 | activation: 68 | name: "swish" 69 | normalization: 70 | name: "batch_norm_2d" 71 | momentum: 0.1 72 | activation: 73 | name: "swish" 74 | layer: 75 | global_pool: "mean" 76 | conv_init: "kaiming_normal" 77 | linear_init: "trunc_normal" 78 | linear_init_std_dev: 0.02 79 | ema: 80 | enable: true 81 | momentum: 0.0005 82 | ddp: 83 | enable: True 84 | rank: 0 85 | world_size: -1 86 | dist_port: 30787 87 | stats: 88 | name: [ "loss", "top1", "top5" ] 89 | checkpoint_metric: "top1" 90 | checkpoint_metric_max: true 91 | -------------------------------------------------------------------------------- /config/classification/mobilevit/mobilevit_small.yaml: -------------------------------------------------------------------------------- 1 | common: 2 | run_label: "run_1" 3 | log_freq: 500 4 | auto_resume: true 5 | mixed_precision: true 6 | results_loc: "/home/disk/result/classification_imagenet1k/mobilevit-s" 7 | dataset: 8 | root_train: "/home/disk/data/imagenet1k/training" 9 | root_val: "/home/disk/data/imagenet1k/validation" 10 | name: "imagenet" 11 | category: "classification" 12 | train_batch_size0: 128 13 | val_batch_size0: 64 14 | eval_batch_size0: 64 15 | workers: 4 16 | persistent_workers: false 17 | pin_memory: true 18 | image_augmentation: 19 | random_resized_crop: 20 | enable: true 21 | interpolation: "bilinear" 22 | random_horizontal_flip: 23 | enable: true 24 | sampler: 25 | name: "variable_batch_sampler" 26 | vbs: 27 | crop_size_width: 256 28 | crop_size_height: 256 29 | max_n_scales: 5 30 | min_crop_size_width: 160 31 | max_crop_size_width: 320 32 | min_crop_size_height: 160 33 | max_crop_size_height: 320 34 | check_scale: 32 35 | loss: 36 | category: "classification" 37 | classification: 38 | name: "label_smoothing" 39 | label_smoothing_factor: 0.1 40 | optim: 41 | name: "adamw" 42 | weight_decay: 0.01 43 | no_decay_bn_filter_bias: false 44 | adamw: 45 | beta1: 0.9 46 | beta2: 0.999 47 | scheduler: 48 | name: "cosine" 49 | is_iteration_based: false 50 | max_epochs: 300 51 | warmup_iterations: 3000 52 | warmup_init_lr: 0.0002 53 | cosine: 54 | max_lr: 0.002 55 | min_lr: 0.0002 56 | model: 57 | classification: 58 | name: "mobilevit" 59 | classifier_dropout: 0.1 60 | mit: 61 | mode: "small" 62 | ffn_dropout: 0.0 63 | attn_dropout: 0.0 64 | dropout: 0.1 65 | number_heads: 4 66 | no_fuse_local_global_features: false 67 | conv_kernel_size: 3 68 | activation: 69 | name: "swish" 70 | normalization: 71 | name: "batch_norm_2d" 72 | momentum: 0.1 73 | activation: 74 | name: "swish" 75 | layer: 76 | global_pool: "mean" 77 | conv_init: "kaiming_normal" 78 | linear_init: "trunc_normal" 79 | linear_init_std_dev: 0.02 80 | ema: 81 | enable: true 82 | momentum: 0.0005 83 | ddp: 84 | enable: True 85 | rank: 0 86 | world_size: -1 87 | dist_port: 30786 88 | stats: 89 | name: [ "loss", "top1", "top5" ] 90 | checkpoint_metric: "top1" 91 | checkpoint_metric_max: true 92 | -------------------------------------------------------------------------------- /config/classification/mobilevit/mobilevit_x_small.yaml: -------------------------------------------------------------------------------- 1 | common: 2 | run_label: "run_1" 3 | log_freq: 500 4 | auto_resume: true 5 | mixed_precision: true 6 | results_loc: "/home/disk/result/classification_imagenet1k/mobilevit-xs" 7 | dataset: 8 | root_train: "/home/disk/data/imagenet1k/training" 9 | root_val: "/home/disk/data/imagenet1k/validation" 10 | name: "imagenet" 11 | category: "classification" 12 | train_batch_size0: 128 13 | val_batch_size0: 64 14 | eval_batch_size0: 64 15 | workers: 4 16 | persistent_workers: false 17 | pin_memory: true 18 | image_augmentation: 19 | random_resized_crop: 20 | enable: true 21 | interpolation: "bilinear" 22 | random_horizontal_flip: 23 | enable: true 24 | sampler: 25 | name: "variable_batch_sampler" 26 | vbs: 27 | crop_size_width: 256 28 | crop_size_height: 256 29 | max_n_scales: 5 30 | min_crop_size_width: 160 31 | max_crop_size_width: 320 32 | min_crop_size_height: 160 33 | max_crop_size_height: 320 34 | check_scale: 32 35 | loss: 36 | category: "classification" 37 | classification: 38 | name: "label_smoothing" 39 | label_smoothing_factor: 0.1 40 | optim: 41 | name: "adamw" 42 | weight_decay: 0.01 43 | no_decay_bn_filter_bias: false 44 | adamw: 45 | beta1: 0.9 46 | beta2: 0.999 47 | scheduler: 48 | name: "cosine" 49 | is_iteration_based: false 50 | max_epochs: 300 51 | warmup_iterations: 3000 52 | warmup_init_lr: 0.0002 53 | cosine: 54 | max_lr: 0.002 55 | min_lr: 0.0002 56 | model: 57 | classification: 58 | name: "mobilevit" 59 | classifier_dropout: 0.1 60 | mit: 61 | mode: "x_small" 62 | ffn_dropout: 0.0 63 | attn_dropout: 0.0 64 | dropout: 0.1 65 | number_heads: 4 66 | no_fuse_local_global_features: false 67 | conv_kernel_size: 3 68 | activation: 69 | name: "swish" 70 | normalization: 71 | name: "batch_norm_2d" 72 | momentum: 0.1 73 | activation: 74 | name: "swish" 75 | layer: 76 | global_pool: "mean" 77 | conv_init: "kaiming_normal" 78 | linear_init: "trunc_normal" 79 | linear_init_std_dev: 0.02 80 | ema: 81 | enable: true 82 | momentum: 0.0005 83 | ddp: 84 | enable: True 85 | rank: 0 86 | world_size: -1 87 | dist_port: 30786 88 | stats: 89 | name: [ "loss", "top1", "top5" ] 90 | checkpoint_metric: "top1" 91 | checkpoint_metric_max: true 92 | -------------------------------------------------------------------------------- /config/detection/edgeformer/ssd_edgeformer_s.yaml: -------------------------------------------------------------------------------- 1 | common: 2 | run_label: "run_1" 3 | accum_freq: 1 4 | accum_after_epoch: -1 5 | log_freq: 500 6 | auto_resume: true 7 | mixed_precision: true 8 | results_loc: "/home/disk/result/detection_coco/edgeformer-s" 9 | dataset: 10 | root_train: "/home/disk/data/coco" 11 | root_val: "/home/disk/data/coco" 12 | name: "coco_ssd" 13 | category: "detection" 14 | train_batch_size0: 32 15 | val_batch_size0: 32 16 | eval_batch_size0: 1 17 | workers: 8 18 | persistent_workers: false 19 | pin_memory: true 20 | sampler: 21 | name: "batch_sampler" 22 | bs: 23 | crop_size_width: 320 24 | crop_size_height: 320 25 | loss: 26 | category: "detection" 27 | detection: 28 | name: "ssd_multibox_loss" 29 | ssd_multibox_loss: 30 | neg_pos_ratio: 3 31 | optim: 32 | name: "adamw" 33 | weight_decay: 0.02 34 | no_decay_bn_filter_bias: false 35 | adamw: 36 | beta1: 0.9 37 | beta2: 0.999 38 | scheduler: 39 | name: "cosine" 40 | is_iteration_based: false 41 | max_epochs: 200 42 | warmup_iterations: 500 43 | warmup_init_lr: 0.00009 44 | cosine: 45 | max_lr: 0.0009 46 | min_lr: 1.e-6 47 | model: 48 | detection: 49 | name: "ssd" 50 | ssd: 51 | anchors_aspect_ratio: [ [ 2, 3 ], [ 2, 3 ], [ 2, 3 ], [ 2, 3 ], [ 2, 3 ], [2] ] 52 | output_strides: [ 16, 32, 64, 128, 256, -1 ] 53 | proj_channels: [512, 256, 256, 128, 128, 64] 54 | center_variance: 0.1 55 | size_variance: 0.2 56 | iou_threshold: 0.5 57 | nms_iou_threshold: 0.5 58 | classification: 59 | name: "edgeformer" 60 | classifier_dropout: 0.2 61 | edge: 62 | scale: "scale_s" 63 | mode: "outer_frame_v1" 64 | kernel: "gcc_ca" 65 | fusion: "concat" 66 | instance_kernel: "interpolation_bilinear" 67 | use_pe: True 68 | activation: 69 | name: "swish" 70 | # pretrained: "/home/disk/result/classification_imagenet1k/edgeformer-s/run_1/checkpoint_ema_avg.pt" 71 | # pretrained: "./pretrained_models/classification/checkpoint_ema_avg.pt" 72 | normalization: 73 | name: "sync_batch_norm" 74 | momentum: 0.1 75 | activation: 76 | name: "relu" # If specific activation function is not specified, this one will be used as a default 77 | inplace: false 78 | layer: 79 | global_pool: "mean" 80 | conv_init: "kaiming_normal" 81 | linear_init: "normal" 82 | conv_weight_std: false 83 | ema: 84 | enable: True 85 | momentum: 0.0005 86 | ddp: 87 | enable: True 88 | rank: 0 89 | world_size: -1 90 | dist_port: 30787 91 | stats: 92 | name: [ "loss" ] 93 | checkpoint_metric: "loss" 94 | checkpoint_metric_max: false -------------------------------------------------------------------------------- /config/detection/mobilevit/ssd_mobilevit_small_320.yaml: -------------------------------------------------------------------------------- 1 | common: 2 | run_label: "run_1" 3 | accum_freq: 1 4 | accum_after_epoch: -1 5 | log_freq: 500 6 | auto_resume: true 7 | mixed_precision: true 8 | results_loc: "/home/disk/result/detection_coco/mobilevit_s" 9 | dataset: 10 | root_train: "/home/disk/data/coco" 11 | root_val: "/home/disk/data/coco" 12 | name: "coco_ssd" 13 | category: "detection" 14 | train_batch_size0: 32 15 | val_batch_size0: 32 16 | eval_batch_size0: 1 17 | workers: 8 18 | persistent_workers: false 19 | pin_memory: true 20 | sampler: 21 | name: "batch_sampler" 22 | bs: 23 | crop_size_width: 320 24 | crop_size_height: 320 25 | loss: 26 | category: "detection" 27 | detection: 28 | name: "ssd_multibox_loss" 29 | ssd_multibox_loss: 30 | neg_pos_ratio: 3 31 | optim: 32 | name: "adamw" 33 | weight_decay: 0.01 34 | no_decay_bn_filter_bias: false 35 | adamw: 36 | beta1: 0.9 37 | beta2: 0.999 38 | scheduler: 39 | name: "cosine" 40 | is_iteration_based: false 41 | max_epochs: 200 42 | warmup_iterations: 500 43 | warmup_init_lr: 0.00009 44 | cosine: 45 | max_lr: 0.0009 # [2.7e-3 * N_GPUS^2 x (BATCH_SIZE_GPU0/ 32) * 0.02 ] # 0.02 comes from this fact 0.1 (ResNet SGD LR)/0.002 (MIT ADAMW LR) 46 | min_lr: 1.e-6 47 | model: 48 | detection: 49 | name: "ssd" 50 | ssd: 51 | anchors_aspect_ratio: [ [ 2, 3 ], [ 2, 3 ], [ 2, 3 ], [ 2, 3 ], [ 2, 3 ], [2] ] 52 | output_strides: [ 16, 32, 64, 128, 256, -1 ] 53 | proj_channels: [512, 256, 256, 128, 128, 64] 54 | center_variance: 0.1 55 | size_variance: 0.2 56 | iou_threshold: 0.5 57 | nms_iou_threshold: 0.5 58 | classification: 59 | name: "mobilevit" 60 | classifier_dropout: 0.1 61 | mit: 62 | mode: "small" 63 | ffn_dropout: 0.0 64 | attn_dropout: 0.0 65 | dropout: 0.1 66 | number_heads: 4 67 | no_fuse_local_global_features: false 68 | conv_kernel_size: 3 69 | activation: 70 | name: "swish" 71 | # pretrained: "/home/disk/code/mobilevit_s/mobilevit_s.pt" 72 | pretrained: "https://docs-assets.developer.apple.com/ml-research/models/cvnets/classification/mobilevit_s.pt" 73 | normalization: 74 | name: "sync_batch_norm" 75 | momentum: 0.1 76 | activation: 77 | name: "relu" # If specific activation function is not specified, this one will be used as a default 78 | inplace: false 79 | layer: 80 | global_pool: "mean" 81 | conv_init: "kaiming_normal" 82 | linear_init: "normal" 83 | conv_weight_std: false 84 | ema: 85 | enable: True 86 | momentum: 0.0005 87 | ddp: 88 | enable: True 89 | rank: 0 90 | world_size: -1 91 | dist_port: 30786 92 | stats: 93 | name: [ "loss" ] 94 | checkpoint_metric: "loss" 95 | checkpoint_metric_max: false -------------------------------------------------------------------------------- /config/detection/mobilevit/ssd_mobilevit_x_small_320.yaml: -------------------------------------------------------------------------------- 1 | common: 2 | run_label: "run_1" 3 | accum_freq: 1 4 | accum_after_epoch: -1 5 | log_freq: 500 6 | auto_resume: true 7 | mixed_precision: true 8 | results_loc: "/home/disk/result/detection_coco/mobilevit_xs" 9 | dataset: 10 | root_train: "/home/disk/data/coco" 11 | root_val: "/home/disk/data/coco" 12 | name: "coco_ssd" 13 | category: "detection" 14 | train_batch_size0: 32 15 | val_batch_size0: 32 16 | eval_batch_size0: 1 17 | workers: 8 18 | persistent_workers: false 19 | pin_memory: true 20 | sampler: 21 | name: "batch_sampler" 22 | bs: 23 | crop_size_width: 320 24 | crop_size_height: 320 25 | loss: 26 | category: "detection" 27 | detection: 28 | name: "ssd_multibox_loss" 29 | ssd_multibox_loss: 30 | neg_pos_ratio: 3 31 | optim: 32 | name: "adamw" 33 | weight_decay: 0.01 34 | no_decay_bn_filter_bias: false 35 | adamw: 36 | beta1: 0.9 37 | beta2: 0.999 38 | scheduler: 39 | name: "cosine" 40 | is_iteration_based: false 41 | max_epochs: 200 42 | warmup_iterations: 500 43 | warmup_init_lr: 0.00009 44 | cosine: 45 | max_lr: 0.0009 # [2.7e-3 * N_GPUS^2 x (BATCH_SIZE_GPU0/ 32) * 0.02 ] # 0.02 comes from this fact 0.1 (ResNet SGD LR)/0.002 (MIT ADAMW LR) 46 | min_lr: 1.e-6 47 | model: 48 | detection: 49 | name: "ssd" 50 | ssd: 51 | anchors_aspect_ratio: [ [ 2, 3 ], [ 2, 3 ], [ 2, 3 ], [ 2, 3 ], [ 2, 3 ], [2] ] 52 | output_strides: [ 16, 32, 64, 128, 256, -1 ] 53 | proj_channels: [512, 256, 256, 128, 128, 64] 54 | center_variance: 0.1 55 | size_variance: 0.2 56 | iou_threshold: 0.5 57 | nms_iou_threshold: 0.5 58 | classification: 59 | name: "mobilevit" 60 | classifier_dropout: 0.1 61 | mit: 62 | mode: "x_small" 63 | ffn_dropout: 0.0 64 | attn_dropout: 0.0 65 | dropout: 0.1 66 | number_heads: 4 67 | no_fuse_local_global_features: false 68 | conv_kernel_size: 3 69 | activation: 70 | name: "swish" 71 | pretrained: "https://docs-assets.developer.apple.com/ml-research/models/cvnets/classification/mobilevit_xs.pt" 72 | normalization: 73 | name: "sync_batch_norm" 74 | momentum: 0.1 75 | activation: 76 | name: "relu" # If specific activation function is not specified, this one will be used as a default 77 | inplace: false 78 | layer: 79 | global_pool: "mean" 80 | conv_init: "kaiming_normal" 81 | linear_init: "normal" 82 | conv_weight_std: false 83 | ema: 84 | enable: true 85 | momentum: 0.0005 86 | ddp: 87 | enable: true 88 | rank: 0 89 | world_size: -1 90 | dist_port: 30786 91 | stats: 92 | name: [ "loss" ] 93 | checkpoint_metric: "loss" 94 | checkpoint_metric_max: false -------------------------------------------------------------------------------- /config/segmentation/edgeformer/deeplabv3_edgeformer_s.yaml: -------------------------------------------------------------------------------- 1 | common: 2 | run_label: "run_1" 3 | accum_freq: 1 4 | accum_after_epoch: -1 5 | log_freq: 200 6 | auto_resume: true 7 | mixed_precision: true 8 | results_loc: "/home/disk/result/segmentation_pascal/edgeformer-s" 9 | dataset: 10 | root_train: "/home/disk/data/pascal_aug/" 11 | root_val: "/home/disk/data/pascal_aug/" 12 | name: "pascal" 13 | category: "segmentation" 14 | train_batch_size0: 8 15 | val_batch_size0: 4 16 | eval_batch_size0: 4 17 | workers: 8 18 | persistent_workers: false 19 | pin_memory: false 20 | pascal: 21 | use_coco_data: True 22 | coco_root_dir: "/home/disk/data/coco_preprocess/" 23 | image_augmentation: 24 | random_resize: 25 | enable: true 26 | min_size: 384 27 | max_size: 768 28 | random_crop: 29 | enable: true 30 | mask_fill: 255 31 | resize_if_needed: true 32 | random_horizontal_flip: 33 | enable: true 34 | sampler: 35 | name: "batch_sampler" 36 | bs: 37 | crop_size_width: 512 38 | crop_size_height: 512 39 | loss: 40 | category: "segmentation" 41 | ignore_idx: 255 42 | segmentation: 43 | name: "cross_entropy" 44 | optim: 45 | name: "adamw" 46 | weight_decay: 0.01 47 | no_decay_bn_filter_bias: false 48 | adamw: 49 | beta1: 0.9 50 | beta2: 0.999 51 | scheduler: 52 | name: "cosine" 53 | is_iteration_based: false 54 | max_epochs: 50 55 | warmup_iterations: 500 56 | warmup_init_lr: 0.00009 57 | cosine: 58 | max_lr: 0.0009 59 | min_lr: 1.e-6 60 | model: 61 | segmentation: 62 | name: "encoder_decoder" 63 | lr_multiplier: 10 64 | seg_head: "deeplabv3" 65 | output_stride: 16 66 | classifier_dropout: 0.1 67 | activation: 68 | name: "relu" 69 | deeplabv3: 70 | aspp_dropout: 0.1 71 | aspp_sep_conv: false 72 | aspp_out_channels: 256 73 | aspp_rates: [6, 12, 18] 74 | classification: 75 | name: "edgeformer" 76 | classifier_dropout: 0.2 77 | edge: 78 | scale: "scale_s" 79 | mode: "outer_frame_v1" 80 | kernel: "gcc_ca" 81 | fusion: "concat" 82 | instance_kernel: "interpolation_bilinear" 83 | use_pe: True 84 | activation: 85 | name: "swish" 86 | # pretrained: "/home/disk/result/classification_imagenet1k/edgeformer-s/run_1/checkpoint_ema_avg.pt" 87 | # pretrained: "./pretrained_models/classification/checkpoint_ema_avg.pt" 88 | normalization: 89 | name: "sync_batch_norm" 90 | momentum: 0.1 91 | activation: 92 | name: "relu" 93 | inplace: false 94 | layer: 95 | global_pool: "mean" 96 | conv_init: "kaiming_normal" 97 | linear_init: "normal" 98 | conv_weight_std: false 99 | ema: 100 | enable: true 101 | momentum: 0.0005 102 | ddp: 103 | enable: True 104 | rank: 0 105 | world_size: -1 106 | dist_port: 30788 107 | stats: 108 | name: [ "loss", "iou"] 109 | checkpoint_metric: "iou" 110 | checkpoint_metric_max: true -------------------------------------------------------------------------------- /config/segmentation/mobilevit/deeplabv3_mobilevit_small.yaml: -------------------------------------------------------------------------------- 1 | common: 2 | run_label: "run_1" 3 | accum_freq: 1 4 | accum_after_epoch: -1 5 | log_freq: 200 6 | auto_resume: true 7 | mixed_precision: true 8 | results_loc: "/home/disk/code/segmentation_pascal/mobilevit_s" 9 | dataset: 10 | root_train: "/home/disk/data/pascal_aug/" 11 | root_val: "/home/disk/data/pascal_aug/" 12 | name: "pascal" 13 | category: "segmentation" 14 | train_batch_size0: 32 15 | val_batch_size0: 16 16 | eval_batch_size0: 1 17 | workers: 8 18 | persistent_workers: false 19 | pin_memory: false 20 | pascal: 21 | use_coco_data: False 22 | coco_root_dir: "/home/disk/data/coco_preprocess/" 23 | image_augmentation: 24 | random_resize: 25 | enable: true 26 | min_size: 256 27 | max_size: 1024 28 | random_crop: 29 | enable: true 30 | mask_fill: 255 31 | resize_if_needed: true 32 | random_horizontal_flip: 33 | enable: true 34 | sampler: 35 | name: "batch_sampler" 36 | bs: 37 | crop_size_width: 512 38 | crop_size_height: 512 39 | loss: 40 | category: "segmentation" 41 | ignore_idx: 255 42 | segmentation: 43 | name: "cross_entropy" 44 | optim: 45 | name: "adamw" 46 | weight_decay: 0.01 47 | no_decay_bn_filter_bias: false 48 | adamw: 49 | beta1: 0.9 50 | beta2: 0.999 51 | scheduler: 52 | name: "cosine" 53 | is_iteration_based: false 54 | max_epochs: 50 55 | warmup_iterations: 500 56 | warmup_init_lr: 0.00009 57 | cosine: 58 | max_lr: 0.0009 # [2.7e-3 * N_GPUS^2 x (BATCH_SIZE_GPU0/ 32) * 0.02 ] # 0.02 comes from this fact 0.1 (ResNet SGD LR)/0.002 (MIT ADAMW LR) 59 | min_lr: 1.e-6 60 | model: 61 | segmentation: 62 | name: "encoder_decoder" 63 | lr_multiplier: 10 64 | seg_head: "deeplabv3" 65 | output_stride: 16 66 | classifier_dropout: 0.1 67 | activation: 68 | name: "relu" 69 | deeplabv3: 70 | aspp_dropout: 0.1 71 | aspp_sep_conv: false 72 | aspp_out_channels: 256 73 | aspp_rates: [6, 12, 18] 74 | classification: 75 | name: "mobilevit" 76 | classifier_dropout: 0.1 77 | mit: 78 | mode: "small" 79 | ffn_dropout: 0.0 80 | attn_dropout: 0.0 81 | dropout: 0.1 82 | number_heads: 4 83 | no_fuse_local_global_features: false 84 | conv_kernel_size: 3 85 | activation: 86 | name: "swish" 87 | # pretrained: "/home/disk/code/mobilevit_s/mobilevit_s.pt" 88 | pretrained: "https://docs-assets.developer.apple.com/ml-research/models/cvnets/classification/mobilevit_s.pt" 89 | normalization: 90 | name: "sync_batch_norm" 91 | momentum: 0.1 92 | activation: 93 | name: "relu" 94 | inplace: false 95 | layer: 96 | global_pool: "mean" 97 | conv_init: "kaiming_normal" 98 | linear_init: "normal" 99 | conv_weight_std: false 100 | ema: 101 | enable: true 102 | momentum: 0.0005 103 | ddp: 104 | enable: true 105 | rank: 0 106 | world_size: -1 107 | dist_port: 30786 108 | stats: 109 | name: [ "loss", "iou"] 110 | checkpoint_metric: "iou" 111 | checkpoint_metric_max: true -------------------------------------------------------------------------------- /config/segmentation/mobilevit/deeplabv3_mobilevit_x_small.yaml: -------------------------------------------------------------------------------- 1 | common: 2 | run_label: "run_1" 3 | accum_freq: 1 4 | accum_after_epoch: -1 5 | log_freq: 200 6 | auto_resume: true 7 | mixed_precision: true 8 | dataset: 9 | root_train: "/home/disk/data/pascal_aug/" 10 | root_val: "/home/disk/data/pascal_aug/" 11 | name: "pascal" 12 | category: "segmentation" 13 | train_batch_size0: 32 14 | val_batch_size0: 16 15 | eval_batch_size0: 1 16 | workers: 8 17 | persistent_workers: false 18 | pin_memory: false 19 | pascal: 20 | use_coco_data: true 21 | coco_root_dir: "/home/disk/data/coco_preprocess/" 22 | image_augmentation: 23 | random_resize: 24 | enable: true 25 | min_size: 256 26 | max_size: 1024 27 | random_crop: 28 | enable: true 29 | mask_fill: 255 30 | resize_if_needed: true 31 | random_horizontal_flip: 32 | enable: true 33 | sampler: 34 | name: "batch_sampler" 35 | bs: 36 | crop_size_width: 512 37 | crop_size_height: 512 38 | loss: 39 | category: "segmentation" 40 | ignore_idx: 255 41 | segmentation: 42 | name: "cross_entropy" 43 | optim: 44 | name: "adamw" 45 | weight_decay: 0.01 46 | no_decay_bn_filter_bias: false 47 | adamw: 48 | beta1: 0.9 49 | beta2: 0.999 50 | scheduler: 51 | name: "cosine" 52 | is_iteration_based: false 53 | max_epochs: 50 54 | warmup_iterations: 500 55 | warmup_init_lr: 0.00009 56 | cosine: 57 | max_lr: 0.0009 # [2.7e-3 * N_GPUS^2 x (BATCH_SIZE_GPU0/ 32) * 0.02 ] # 0.02 comes from this fact 0.1 (ResNet SGD LR)/0.002 (MIT ADAMW LR) 58 | min_lr: 1.e-6 59 | model: 60 | segmentation: 61 | name: "encoder_decoder" 62 | lr_multiplier: 10 63 | seg_head: "deeplabv3" 64 | output_stride: 16 65 | classifier_dropout: 0.1 66 | activation: 67 | name: "relu" 68 | deeplabv3: 69 | aspp_dropout: 0.1 70 | aspp_sep_conv: false 71 | aspp_out_channels: 256 72 | aspp_rates: [6, 12, 18] 73 | classification: 74 | name: "mobilevit" 75 | classifier_dropout: 0.1 76 | mit: 77 | mode: "x_small" 78 | ffn_dropout: 0.0 79 | attn_dropout: 0.0 80 | dropout: 0.1 81 | number_heads: 4 82 | no_fuse_local_global_features: false 83 | conv_kernel_size: 3 84 | activation: 85 | name: "swish" 86 | pretrained: "https://docs-assets.developer.apple.com/ml-research/models/cvnets/classification/mobilevit_xs.pt" 87 | normalization: 88 | name: "sync_batch_norm" 89 | momentum: 0.1 90 | activation: 91 | name: "relu" 92 | inplace: false 93 | layer: 94 | global_pool: "mean" 95 | conv_init: "kaiming_normal" 96 | linear_init: "normal" 97 | conv_weight_std: false 98 | ema: 99 | enable: true 100 | momentum: 0.0005 101 | ddp: 102 | enable: true 103 | rank: 0 104 | world_size: -1 105 | dist_port: 30786 106 | stats: 107 | name: [ "loss", "iou"] 108 | checkpoint_metric: "iou" 109 | checkpoint_metric_max: true -------------------------------------------------------------------------------- /convert2onnx/converted_models/edgeformer.onnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/convert2onnx/converted_models/edgeformer.onnx -------------------------------------------------------------------------------- /convert2onnx/converted_models/edgeformer.onnx_converted.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/convert2onnx/converted_models/edgeformer.onnx_converted.mnn -------------------------------------------------------------------------------- /convert2onnx/converted_models/mobilevit.onnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/convert2onnx/converted_models/mobilevit.onnx -------------------------------------------------------------------------------- /convert2onnx/converted_models/mobilevit.onnx_converted.mnn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/convert2onnx/converted_models/mobilevit.onnx_converted.mnn -------------------------------------------------------------------------------- /convert2onnx/model_convert2onnx.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from cvnets import get_model 3 | from options.opts import get_training_arguments 4 | 5 | # initialization 6 | opts = get_training_arguments() 7 | model = get_model(opts) 8 | model.eval() 9 | 10 | input = torch.randn(1, 3, 224, 224) 11 | 12 | # torch.onnx.export(model, input, "./converted_models/edgeformer.onnx", opset_version=12) 13 | 14 | torch.onnx.export(model, input, "./converted_models/mobilevit.onnx", opset_version=12) -------------------------------------------------------------------------------- /cvnets/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from cvnets.misc.common import parameter_list 3 | from cvnets.layers import arguments_nn_layers 4 | from cvnets.models import arguments_model, get_model 5 | from cvnets.misc.averaging_utils import arguments_ema, EMA 6 | from cvnets.misc.profiler import module_profile 7 | from cvnets.models.detection.base_detection import DetectionPredTuple -------------------------------------------------------------------------------- /cvnets/layers/__init__.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import importlib, inspect 4 | 5 | from .base_layer import BaseLayer 6 | from .conv_layer import ConvLayer, NormActLayer, TransposeConvLayer 7 | from .sep_conv_layer import SeparableConv 8 | from .linear_layer import LinearLayer, GroupLinear 9 | from .global_pool import GlobalPool 10 | from .identity import Identity 11 | from .non_linear_layers import get_activation_fn 12 | from .normalization_layers import get_normalization_layer, norm_layers_tuple 13 | from .pixel_shuffle import PixelShuffle 14 | from .upsample import UpSample 15 | from .pooling import MaxPool2d, AvgPool2d 16 | from .positional_encoding import PositionalEncoding 17 | from .normalization_layers import AdjustBatchNormMomentum 18 | from .adaptive_pool import AdaptiveAvgPool2d 19 | from .flatten import Flatten 20 | from .multi_head_attention import MultiHeadAttention 21 | from .dropout import Dropout, Dropout2d 22 | 23 | __all__ = [ 24 | 'ConvLayer', 25 | 'SeparableConv', 26 | 'NormActLayer', 27 | 'TransposeConvLayer', 28 | 'LinearLayer', 29 | 'GroupLinear', 30 | 'GlobalPool', 31 | 'Identity', 32 | 'PixelShuffle', 33 | 'UpSample', 34 | 'MaxPool2d', 35 | 'AvgPool2d', 36 | 'Dropout', 37 | 'Dropout2d', 38 | 'PositionalEncoding', 39 | 'AdjustBatchNormMomentum', 40 | 'Flatten', 41 | 'MultiHeadAttention' 42 | ] 43 | 44 | 45 | # iterate through all classes and fetch layer specific arguments 46 | def layer_specific_args(parser: argparse.ArgumentParser): 47 | layer_dir = os.path.dirname(__file__) 48 | parsed_layers = [] 49 | for file in os.listdir(layer_dir): 50 | path = os.path.join(layer_dir, file) 51 | if ( 52 | not file.startswith("_") 53 | and not file.startswith(".") 54 | and (file.endswith(".py") or os.path.isdir(path)) 55 | ): 56 | layer_name = file[: file.find(".py")] if file.endswith(".py") else file 57 | module = importlib.import_module("cvnets.layers." + layer_name) 58 | for name, cls in inspect.getmembers(module, inspect.isclass): 59 | if issubclass(cls, BaseLayer) and name not in parsed_layers: 60 | parser = cls.add_arguments(parser) 61 | parsed_layers.append(name) 62 | return parser 63 | 64 | 65 | def arguments_nn_layers(parser: argparse.ArgumentParser): 66 | 67 | # Retrieve layer specific arguments 68 | parser = layer_specific_args(parser) 69 | 70 | # activation and normalization arguments 71 | from cvnets.layers.activation import arguments_activation_fn 72 | parser = arguments_activation_fn(parser) 73 | 74 | from cvnets.layers.normalization import arguments_norm_layers 75 | parser = arguments_norm_layers(parser) 76 | 77 | return parser 78 | -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/adaptive_pool.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/adaptive_pool.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/base_layer.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/base_layer.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/conv_layer.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/conv_layer.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/dropout.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/dropout.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/flatten.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/flatten.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/global_pool.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/global_pool.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/identity.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/identity.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/linear_layer.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/linear_layer.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/multi_head_attention.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/multi_head_attention.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/non_linear_layers.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/non_linear_layers.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/normalization_layers.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/normalization_layers.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/pixel_shuffle.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/pixel_shuffle.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/pooling.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/pooling.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/positional_encoding.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/positional_encoding.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/random_layers.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/random_layers.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/sep_conv_layer.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/sep_conv_layer.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/__pycache__/upsample.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/__pycache__/upsample.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/activation/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import importlib 3 | import argparse 4 | 5 | SUPPORTED_ACT_FNS = [] 6 | 7 | 8 | def register_act_fn(name): 9 | def register_fn(fn): 10 | if name in SUPPORTED_ACT_FNS: 11 | raise ValueError("Cannot register duplicate activation function ({})".format(name)) 12 | SUPPORTED_ACT_FNS.append(name) 13 | return fn 14 | return register_fn 15 | 16 | 17 | # automatically import different activation functions 18 | act_dir = os.path.dirname(__file__) 19 | for file in os.listdir(act_dir): 20 | path = os.path.join(act_dir, file) 21 | if ( 22 | not file.startswith("_") 23 | and not file.startswith(".") 24 | and (file.endswith(".py") or os.path.isdir(path)) 25 | ): 26 | model_name = file[: file.find(".py")] if file.endswith(".py") else file 27 | module = importlib.import_module("cvnets.layers.activation." + model_name) 28 | 29 | 30 | def arguments_activation_fn(parser: argparse.ArgumentParser): 31 | group = parser.add_argument_group(title="Non-linear functions", description="Non-linear functions") 32 | 33 | group.add_argument('--model.activation.name', default='relu', type=str, help='Non-linear function type') 34 | group.add_argument('--model.activation.inplace', action='store_true', help='Inplace non-linear functions') 35 | group.add_argument('--model.activation.neg-slope', default=0.1, type=float, help='Negative slope in leaky relu') 36 | 37 | return parser 38 | 39 | 40 | # import later to avoid circular loop 41 | from cvnets.layers.activation.gelu import GELU 42 | from cvnets.layers.activation.hard_sigmoid import Hardsigmoid 43 | from cvnets.layers.activation.hard_swish import Hardswish 44 | from cvnets.layers.activation.leaky_relu import LeakyReLU 45 | from cvnets.layers.activation.prelu import PReLU 46 | from cvnets.layers.activation.relu import ReLU 47 | from cvnets.layers.activation.relu6 import ReLU6 48 | from cvnets.layers.activation.sigmoid import Sigmoid 49 | from cvnets.layers.activation.swish import Swish 50 | 51 | 52 | __all__ = [ 53 | 'GELU', 54 | 'Hardsigmoid', 55 | 'Hardswish', 56 | 'LeakyReLU', 57 | 'PReLU', 58 | 'ReLU', 59 | 'ReLU6', 60 | 'Sigmoid', 61 | 'Swish', 62 | ] 63 | -------------------------------------------------------------------------------- /cvnets/layers/activation/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/activation/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/activation/__pycache__/gelu.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/activation/__pycache__/gelu.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/activation/__pycache__/hard_sigmoid.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/activation/__pycache__/hard_sigmoid.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/activation/__pycache__/hard_swish.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/activation/__pycache__/hard_swish.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/activation/__pycache__/leaky_relu.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/activation/__pycache__/leaky_relu.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/activation/__pycache__/prelu.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/activation/__pycache__/prelu.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/activation/__pycache__/relu.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/activation/__pycache__/relu.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/activation/__pycache__/relu6.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/activation/__pycache__/relu6.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/activation/__pycache__/sigmoid.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/activation/__pycache__/sigmoid.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/activation/__pycache__/swish.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/activation/__pycache__/swish.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/activation/gelu.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | 3 | from . import register_act_fn 4 | 5 | 6 | @register_act_fn(name="gelu") 7 | class GELU(nn.GELU): 8 | def __init__(self): 9 | super(GELU, self).__init__() 10 | 11 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 12 | return input, 0.0, 0.0 13 | -------------------------------------------------------------------------------- /cvnets/layers/activation/hard_sigmoid.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | 3 | from . import register_act_fn 4 | 5 | 6 | @register_act_fn(name="hard_sigmoid") 7 | class Hardsigmoid(nn.Hardsigmoid): 8 | def __init__(self, inplace: bool = False): 9 | super(Hardsigmoid, self).__init__(inplace=inplace) 10 | 11 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 12 | return input, 0.0, 0.0 13 | -------------------------------------------------------------------------------- /cvnets/layers/activation/hard_swish.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | 3 | from . import register_act_fn 4 | 5 | 6 | @register_act_fn(name="hard_swish") 7 | class Hardswish(nn.Hardswish): 8 | def __init__(self, inplace: bool = False): 9 | super(Hardswish, self).__init__(inplace=inplace) 10 | 11 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 12 | return input, 0.0, 0.0 13 | -------------------------------------------------------------------------------- /cvnets/layers/activation/leaky_relu.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | 3 | from . import register_act_fn 4 | 5 | 6 | @register_act_fn(name="leaky_relu") 7 | class LeakyReLU(nn.LeakyReLU): 8 | def __init__(self, negative_slope: float = 1e-2, inplace: bool = False): 9 | super(LeakyReLU, self).__init__(negative_slope=negative_slope, inplace=inplace) 10 | 11 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 12 | return input, 0.0, 0.0 13 | -------------------------------------------------------------------------------- /cvnets/layers/activation/prelu.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | 3 | from . import register_act_fn 4 | 5 | 6 | @register_act_fn(name="prelu") 7 | class PReLU(nn.PReLU): 8 | def __init__(self, num_parameters: int = 1, init: float = 0.25): 9 | super(PReLU, self).__init__(num_parameters=num_parameters, init=init) 10 | 11 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 12 | return input, 0.0, 0.0 13 | -------------------------------------------------------------------------------- /cvnets/layers/activation/relu.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | 3 | from . import register_act_fn 4 | 5 | 6 | @register_act_fn(name="relu") 7 | class ReLU(nn.ReLU): 8 | def __init__(self, inplace: bool = False): 9 | super(ReLU, self).__init__(inplace=inplace) 10 | 11 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 12 | return input, 0.0, 0.0 13 | -------------------------------------------------------------------------------- /cvnets/layers/activation/relu6.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | 3 | from . import register_act_fn 4 | 5 | 6 | @register_act_fn(name="relu6") 7 | class ReLU6(nn.ReLU6): 8 | def __init__(self, inplace: bool = False): 9 | super(ReLU6, self).__init__(inplace=inplace) 10 | 11 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 12 | return input, 0.0, 0.0 13 | -------------------------------------------------------------------------------- /cvnets/layers/activation/sigmoid.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | 3 | from . import register_act_fn 4 | 5 | 6 | @register_act_fn(name="sigmoid") 7 | class Sigmoid(nn.Sigmoid): 8 | def __init__(self): 9 | super(Sigmoid, self).__init__() 10 | 11 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 12 | return input, 0.0, 0.0 13 | -------------------------------------------------------------------------------- /cvnets/layers/activation/swish.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | 3 | from . import register_act_fn 4 | 5 | 6 | @register_act_fn(name="swish") 7 | class Swish(nn.SiLU): 8 | def __init__(self, inplace: bool = False): 9 | super(Swish, self).__init__(inplace=inplace) 10 | 11 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 12 | return input, 0.0, 0.0 13 | -------------------------------------------------------------------------------- /cvnets/layers/adaptive_pool.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | 3 | 4 | class AdaptiveAvgPool2d(nn.AdaptiveAvgPool2d): 5 | def __init__(self, output_size: int or tuple = 1): 6 | super(AdaptiveAvgPool2d, self).__init__(output_size=output_size) 7 | 8 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 9 | input = self.forward(input) 10 | return input, 0.0, 0.0 11 | -------------------------------------------------------------------------------- /cvnets/layers/base_layer.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | import argparse 3 | from typing import Tuple 4 | 5 | 6 | class BaseLayer(nn.Module): 7 | def __init__(self, *args, **kwargs): 8 | super(BaseLayer, self).__init__() 9 | 10 | @classmethod 11 | def add_arguments(cls, parser: argparse.ArgumentParser): 12 | return parser 13 | 14 | def forward(self, *args, **kwargs) -> Tensor or Tuple[Tensor]: 15 | pass 16 | 17 | def profile_module(self, *args, **kwargs) -> (Tensor, float, float): 18 | raise NotImplementedError 19 | 20 | def __repr__(self): 21 | return "{}".format(self.__class__.__name__) 22 | -------------------------------------------------------------------------------- /cvnets/layers/dropout.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | 3 | 4 | class Dropout(nn.Dropout): 5 | def __init__(self, p: float = 0.5, inplace: bool = False): 6 | """ 7 | During training, randomly zeroes some of the elements of the input tensor with probability `p` using samples \ 8 | from a Bernoulli distribution. 9 | 10 | :param p: probability of an element to be zeroed. Default: 0.5 11 | :param inplace: If set to ``True``, will do this operation in-place. Default: ``False`` 12 | """ 13 | super(Dropout, self).__init__(p=p, inplace=inplace) 14 | 15 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 16 | input = self.forward(input) 17 | return input, 0.0, 0.0 18 | 19 | 20 | class Dropout2d(nn.Dropout2d): 21 | def __init__(self, p: float = 0.5, inplace: bool = False): 22 | """ 23 | During training, randomly zeroes some of the elements of the input tensor with probability `p` using samples \ 24 | from a Bernoulli distribution. 25 | 26 | :param p: probability of an element to be zeroed. Default: 0.5 27 | :param inplace: If set to ``True``, will do this operation in-place. Default: ``False`` 28 | """ 29 | super(Dropout2d, self).__init__(p=p, inplace=inplace) 30 | 31 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 32 | input = self.forward(input) 33 | return input, 0.0, 0.0 -------------------------------------------------------------------------------- /cvnets/layers/flatten.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | 3 | 4 | class Flatten(nn.Flatten): 5 | def __init__(self, start_dim: int = 1, end_dim: int = -1): 6 | """ 7 | Flattens a contiguous range of dims into a tensor. 8 | :param start_dim: first dim to flatten (default = 1). 9 | :param end_dim: last dim to flatten (default = -1). 10 | """ 11 | super(Flatten, self).__init__(start_dim=start_dim, end_dim=end_dim) 12 | 13 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 14 | input = self.forward(input) 15 | return input, 0.0, 0.0 16 | -------------------------------------------------------------------------------- /cvnets/layers/global_pool.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch import Tensor 3 | import argparse 4 | 5 | from utils import logger 6 | 7 | from .base_layer import BaseLayer 8 | 9 | pool_types = ['mean', 'rms', 'abs'] 10 | 11 | 12 | class GlobalPool(BaseLayer): 13 | def __init__(self, pool_type='mean', keep_dim=False): 14 | """ 15 | Global pooling 16 | :param pool_type: Global pool operation type (mean, rms, abs) 17 | :param keep_dim: Keep dimensions the same as the input or not 18 | """ 19 | super(GlobalPool, self).__init__() 20 | if pool_type not in pool_types: 21 | logger.error('Supported pool types are: {}. Got {}'.format(pool_types, pool_type)) 22 | self.pool_type = pool_type 23 | self.keep_dim = keep_dim 24 | 25 | @classmethod 26 | def add_arguments(cls, parser: argparse.ArgumentParser): 27 | cls_name = "{} arguments".format(cls.__name__) 28 | group = parser.add_argument_group(title=cls_name, description=cls_name) 29 | group.add_argument('--model.layer.global-pool', type=str, default='mean', help='Which global pooling?') 30 | return parser 31 | 32 | def _global_pool(self, x): 33 | assert x.dim() == 4, "Got: {}".format(x.shape) 34 | if self.pool_type == 'rms': 35 | x = x ** 2 36 | x = torch.mean(x, dim=[-2, -1], keepdim=self.keep_dim) 37 | x = x ** -0.5 38 | elif self.pool_type == 'abs': 39 | x = torch.mean(torch.abs(x), dim=[-2, -1], keepdim=self.keep_dim) 40 | else: 41 | # default is mean 42 | # same as AdaptiveAvgPool 43 | x = torch.mean(x, dim=[-2, -1], keepdim=self.keep_dim) 44 | return x 45 | 46 | def forward(self, x: Tensor) -> Tensor: 47 | return self._global_pool(x) 48 | 49 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 50 | input = self.forward(input) 51 | return input, 0.0, 0.0 52 | 53 | def __repr__(self): 54 | return '{}(type={})'.format(self.__class__.__name__, self.pool_type) 55 | -------------------------------------------------------------------------------- /cvnets/layers/identity.py: -------------------------------------------------------------------------------- 1 | 2 | from torch import Tensor 3 | 4 | from .base_layer import BaseLayer 5 | 6 | 7 | class Identity(BaseLayer): 8 | def __init__(self): 9 | """ 10 | Identity operator 11 | """ 12 | super(Identity, self).__init__() 13 | 14 | def forward(self, x: Tensor) -> Tensor: 15 | return x 16 | 17 | def profile_module(self, x: Tensor) -> (Tensor, float, float): 18 | return x, 0.0, 0.0 19 | -------------------------------------------------------------------------------- /cvnets/layers/non_linear_layers.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Optional 3 | from utils import logger 4 | 5 | from .activation import ( 6 | ReLU, Hardswish, Hardsigmoid, PReLU, LeakyReLU, Swish, GELU, Sigmoid, ReLU6, SUPPORTED_ACT_FNS 7 | ) 8 | 9 | 10 | def get_activation_fn(act_type: str = 'swish', num_parameters: Optional[int] = -1, inplace: Optional[bool] = True, 11 | negative_slope: Optional[float] = 0.1): 12 | if act_type == 'relu': 13 | return ReLU(inplace=False) 14 | elif act_type == 'prelu': 15 | assert num_parameters >= 1 16 | return PReLU(num_parameters=num_parameters) 17 | elif act_type == 'leaky_relu': 18 | return LeakyReLU(negative_slope=negative_slope, inplace=inplace) 19 | elif act_type == 'hard_sigmoid': 20 | return Hardsigmoid(inplace=inplace) 21 | elif act_type == 'swish': 22 | return Swish() 23 | elif act_type == 'gelu': 24 | return GELU() 25 | elif act_type == 'sigmoid': 26 | return Sigmoid() 27 | elif act_type == 'relu6': 28 | return ReLU6(inplace=inplace) 29 | elif act_type == 'hard_swish': 30 | return Hardswish(inplace=inplace) 31 | else: 32 | logger.error( 33 | 'Supported activation layers are: {}. Supplied argument is: {}'.format(SUPPORTED_ACT_FNS, act_type)) 34 | -------------------------------------------------------------------------------- /cvnets/layers/normalization/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import importlib 3 | import argparse 4 | 5 | SUPPORTED_NORM_FNS = [] 6 | 7 | 8 | def register_norm_fn(name): 9 | def register_fn(fn): 10 | if name in SUPPORTED_NORM_FNS: 11 | raise ValueError("Cannot register duplicate normalization function ({})".format(name)) 12 | SUPPORTED_NORM_FNS.append(name) 13 | return fn 14 | return register_fn 15 | 16 | 17 | # automatically import different normalization layers 18 | norm_dir = os.path.dirname(__file__) 19 | for file in os.listdir(norm_dir): 20 | path = os.path.join(norm_dir, file) 21 | if ( 22 | not file.startswith("_") 23 | and not file.startswith(".") 24 | and (file.endswith(".py") or os.path.isdir(path)) 25 | ): 26 | model_name = file[: file.find(".py")] if file.endswith(".py") else file 27 | module = importlib.import_module("cvnets.layers.normalization." + model_name) 28 | 29 | 30 | def arguments_norm_layers(parser: argparse.ArgumentParser): 31 | group = parser.add_argument_group(title="Normalization layers", description="Normalization layers") 32 | 33 | group.add_argument('--model.normalization.name', default='batch_norm', type=str, help='Normalization layer') 34 | group.add_argument('--model.normalization.groups', default=32, type=str, 35 | help='Number of groups in group normalization layer') 36 | group.add_argument("--model.normalization.momentum", default=0.1, type=float, 37 | help='Momentum in normalization layers') 38 | 39 | # Adjust momentum in batch norm layers 40 | group.add_argument("--adjust-bn-momentum.enable", action="store_true", 41 | help='Adjust momentum in normalization layers') 42 | group.add_argument("--adjust-bn-momentum.anneal-type", default="cosine", type=str, 43 | help='Method for annealing momentum in Batch norm layers') 44 | group.add_argument("--adjust-bn-momentum.final-momentum-value", default=1e-6, type=float, 45 | help='Min. Momentum in normalization layers') 46 | 47 | return parser 48 | 49 | 50 | # import here to avoid circular loop 51 | from cvnets.layers.normalization.batch_norm import BatchNorm2d, BatchNorm1d 52 | from cvnets.layers.normalization.group_norm import GroupNorm 53 | from cvnets.layers.normalization.instance_norm import InstanceNorm1d, InstanceNorm2d 54 | from cvnets.layers.normalization.sync_batch_norm import SyncBatchNorm 55 | from cvnets.layers.normalization.layer_norm import LayerNorm 56 | 57 | 58 | __all__ = [ 59 | 'BatchNorm2d', 60 | 'BatchNorm1d', 61 | 'GroupNorm', 62 | 'InstanceNorm1d', 63 | 'InstanceNorm2d', 64 | 'SyncBatchNorm', 65 | 'LayerNorm' 66 | ] 67 | -------------------------------------------------------------------------------- /cvnets/layers/normalization/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/normalization/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/normalization/__pycache__/batch_norm.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/normalization/__pycache__/batch_norm.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/normalization/__pycache__/group_norm.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/normalization/__pycache__/group_norm.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/normalization/__pycache__/instance_norm.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/normalization/__pycache__/instance_norm.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/normalization/__pycache__/layer_norm.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/normalization/__pycache__/layer_norm.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/normalization/__pycache__/sync_batch_norm.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/layers/normalization/__pycache__/sync_batch_norm.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/layers/normalization/batch_norm.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | from typing import Optional 3 | 4 | from . import register_norm_fn 5 | 6 | 7 | @register_norm_fn(name="batch_norm") 8 | @register_norm_fn(name="batch_norm_2d") 9 | class BatchNorm2d(nn.BatchNorm2d): 10 | def __init__(self, 11 | num_features: int, 12 | eps: Optional[float] = 1e-5, 13 | momentum: Optional[float] = 0.1, 14 | affine: Optional[bool] = True, 15 | track_running_stats: Optional[bool] = True 16 | ): 17 | super(BatchNorm2d, self).__init__(num_features=num_features, eps=eps, momentum=momentum, affine=affine, 18 | track_running_stats=track_running_stats) 19 | 20 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 21 | # Since normalization layers can be fused, we do not count their operations 22 | params = sum([p.numel() for p in self.parameters()]) 23 | return input, params, 0.0 24 | 25 | 26 | @register_norm_fn(name="batch_norm_1d") 27 | class BatchNorm1d(nn.BatchNorm1d): 28 | def __init__(self, 29 | num_features: int, 30 | eps: Optional[float] = 1e-5, 31 | momentum: Optional[float] = 0.1, 32 | affine: Optional[bool] = True, 33 | track_running_stats: Optional[bool] = True 34 | ): 35 | super(BatchNorm1d, self).__init__(num_features=num_features, eps=eps, momentum=momentum, affine=affine, 36 | track_running_stats=track_running_stats) 37 | 38 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 39 | # Since normalization layers can be fused, we do not count their operations 40 | params = sum([p.numel() for p in self.parameters()]) 41 | return input, params, 0.0 42 | -------------------------------------------------------------------------------- /cvnets/layers/normalization/group_norm.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | from typing import Optional 3 | 4 | from . import register_norm_fn 5 | 6 | 7 | @register_norm_fn(name="group_norm") 8 | class GroupNorm(nn.GroupNorm): 9 | def __init__(self, 10 | num_groups: int, 11 | num_channels: int, 12 | eps: Optional[float] = 1e-5, 13 | affine: Optional[bool] = True 14 | ): 15 | super(GroupNorm, self).__init__( 16 | num_groups=num_groups, 17 | num_channels=num_channels, 18 | eps=eps, 19 | affine=affine 20 | ) 21 | 22 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 23 | # Since normalization layers can be fused, we do not count their operations 24 | params = sum([p.numel() for p in self.parameters()]) 25 | return input, params, 0.0 26 | -------------------------------------------------------------------------------- /cvnets/layers/normalization/instance_norm.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | from typing import Optional 3 | 4 | from . import register_norm_fn 5 | 6 | 7 | @register_norm_fn(name="instance_norm") 8 | @register_norm_fn(name="instance_norm_2d") 9 | class InstanceNorm2d(nn.InstanceNorm2d): 10 | def __init__(self, 11 | num_features: int, 12 | eps: Optional[float] = 1e-5, 13 | momentum: Optional[float] = 0.1, 14 | affine: Optional[bool] = True, 15 | track_running_stats: Optional[bool] = True 16 | ): 17 | super(InstanceNorm2d, self).__init__(num_features=num_features, eps=eps, momentum=momentum, affine=affine, 18 | track_running_stats=track_running_stats) 19 | 20 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 21 | # Since normalization layers can be fused, we do not count their operations 22 | params = sum([p.numel() for p in self.parameters()]) 23 | return input, params, 0.0 24 | 25 | 26 | @register_norm_fn(name="instance_norm_1d") 27 | class InstanceNorm1d(nn.InstanceNorm1d): 28 | def __init__(self, 29 | num_features: int, 30 | eps: Optional[float] = 1e-5, 31 | momentum: Optional[float] = 0.1, 32 | affine: Optional[bool] = True, 33 | track_running_stats: Optional[bool] = True 34 | ): 35 | super(InstanceNorm1d, self).__init__(num_features=num_features, eps=eps, momentum=momentum, affine=affine, 36 | track_running_stats=track_running_stats) 37 | 38 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 39 | # Since normalization layers can be fused, we do not count their operations 40 | params = sum([p.numel() for p in self.parameters()]) 41 | return input, params, 0.0 42 | -------------------------------------------------------------------------------- /cvnets/layers/normalization/layer_norm.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor, Size 2 | from typing import Optional, Union, List 3 | 4 | from . import register_norm_fn 5 | 6 | 7 | @register_norm_fn(name="layer_norm") 8 | class LayerNorm(nn.LayerNorm): 9 | def __init__(self, 10 | normalized_shape: Union[int, List[int], Size], 11 | eps: Optional[float] = 1e-5, 12 | elementwise_affine: Optional[bool] = True 13 | ): 14 | super(LayerNorm, self).__init__( 15 | normalized_shape=normalized_shape, 16 | eps=eps, 17 | elementwise_affine=elementwise_affine 18 | ) 19 | 20 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 21 | params = sum([p.numel() for p in self.parameters()]) 22 | return input, params, 0.0 23 | -------------------------------------------------------------------------------- /cvnets/layers/normalization/sync_batch_norm.py: -------------------------------------------------------------------------------- 1 | from torch import nn, Tensor 2 | from typing import Optional 3 | 4 | from . import register_norm_fn 5 | 6 | 7 | @register_norm_fn(name="sync_batch_norm") 8 | class SyncBatchNorm(nn.SyncBatchNorm): 9 | def __init__(self, 10 | num_features: int, 11 | eps: Optional[float] = 1e-5, 12 | momentum: Optional[float] = 0.1, 13 | affine: Optional[bool] = True, 14 | track_running_stats: Optional[bool] = True 15 | ): 16 | super(SyncBatchNorm, self).__init__(num_features=num_features, eps=eps, momentum=momentum, affine=affine, 17 | track_running_stats=track_running_stats) 18 | 19 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 20 | # Since normalization layers can be fused, we do not count their operations 21 | params = sum([p.numel() for p in self.parameters()]) 22 | return input, params, 0.0 23 | -------------------------------------------------------------------------------- /cvnets/layers/pixel_shuffle.py: -------------------------------------------------------------------------------- 1 | 2 | from torch import nn, Tensor 3 | 4 | 5 | class PixelShuffle(nn.PixelShuffle): 6 | def __init__(self, upscale_factor): 7 | super(PixelShuffle, self).__init__(upscale_factor=upscale_factor) 8 | 9 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 10 | input = self.forward(input) 11 | return input, 0.0, 0.0 12 | 13 | def __repr__(self): 14 | return '{}(upscale_factor={})'.format(self.__class__.__name__, self.upscale_factor) 15 | -------------------------------------------------------------------------------- /cvnets/layers/pooling.py: -------------------------------------------------------------------------------- 1 | 2 | from torch import nn, Tensor 3 | from typing import Optional 4 | 5 | 6 | class MaxPool2d(nn.MaxPool2d): 7 | def __init__(self, kernel_size=3, stride=2, padding=1): 8 | super(MaxPool2d, self).__init__( 9 | kernel_size=kernel_size, 10 | stride=stride, 11 | padding=padding 12 | ) 13 | 14 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 15 | input = self.forward(input) 16 | return input, 0.0, 0.0 17 | 18 | def __repr__(self): 19 | return '{}(kernel_size={}, stride={})'.format(self.__class__.__name__, self.kernel_size, self.stride) 20 | 21 | 22 | class AvgPool2d(nn.AvgPool2d): 23 | def __init__(self, kernel_size: tuple, stride: Optional[tuple] = None, padding: Optional[tuple] = (0, 0), 24 | ceil_mode: Optional[bool] = False, count_include_pad: Optional[bool] = True, divisor_override: Optional[bool] = None): 25 | super(AvgPool2d, self).__init__( 26 | kernel_size=kernel_size, 27 | stride=stride, 28 | padding=padding, 29 | ceil_mode=ceil_mode, 30 | count_include_pad=count_include_pad, 31 | divisor_override=divisor_override 32 | ) 33 | 34 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 35 | input = self.forward(input) 36 | return input, 0.0, 0.0 37 | 38 | def __repr__(self): 39 | return '{}(upscale_factor={})'.format(self.__class__.__name__, self.upscale_factor) -------------------------------------------------------------------------------- /cvnets/layers/positional_encoding.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch import nn, Tensor 3 | import math 4 | 5 | from .base_layer import BaseLayer 6 | from .dropout import Dropout 7 | 8 | 9 | class PositionalEncoding(BaseLayer): 10 | """ 11 | This layer adds sinusoidal positional embeddings to the input signal 12 | Adapted from Pytorch tutorial: 13 | https://pytorch.org/tutorials/beginner/transformer_tutorial.html 14 | """ 15 | 16 | def __init__(self, d_model, dropout=0.1, max_len=5000): 17 | super(PositionalEncoding, self).__init__() 18 | self.dropout = Dropout(p=dropout) 19 | 20 | pe = torch.zeros(max_len, d_model) 21 | position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1) 22 | div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model)) 23 | pe[:, 0::2] = torch.sin(position * div_term) 24 | pe[:, 1::2] = torch.cos(position * div_term) 25 | pe = pe.unsqueeze(0).transpose(1, 2) # [B x E x Max_patches) 26 | self.register_buffer('pe', pe) 27 | 28 | def forward(self, x): 29 | x = x + self.pe[:, :, :x.size(-1)] 30 | return self.dropout(x) 31 | 32 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 33 | return input, 0.0, 0.0 34 | 35 | def __repr__(self): 36 | return "{}(dropout={})".format(self.__class__.__name__, self.dropout.p) 37 | -------------------------------------------------------------------------------- /cvnets/layers/random_layers.py: -------------------------------------------------------------------------------- 1 | 2 | from torch import Tensor 3 | from .base_layer import BaseLayer 4 | import random 5 | from utils.math_utils import bound_fn 6 | from collections import OrderedDict 7 | 8 | 9 | class RandomApply(BaseLayer): 10 | """ 11 | Apply layers randomly during training 12 | """ 13 | def __init__(self, module_list: list, keep_p: float = 0.8): 14 | super(RandomApply, self).__init__() 15 | self._modules = OrderedDict() 16 | for idx, module in enumerate(module_list): 17 | self._modules[str(idx)] = module 18 | 19 | self.module_indexes = [i for i in range(1, len(self._modules))] 20 | n_blocks = len(self.module_indexes) 21 | k = int(round(n_blocks * keep_p)) 22 | self.keep_k = bound_fn(min_val=1, max_val=n_blocks, value=k) 23 | 24 | def forward(self, x): 25 | if self.training: 26 | indexes = [0] + sorted(random.sample(self.module_indexes, k=self.keep_k)) 27 | for idx in indexes: 28 | x = self._modules[str(idx)](x) 29 | else: 30 | for idx, layer in self._modules.items(): 31 | x = layer(x) 32 | return x 33 | 34 | def profile_module(self, x, *args, **kwargs) -> (Tensor, float, float): 35 | params, macs = 0.0, 0.0 36 | for idx, layer in self._modules.items(): 37 | x, p, m = layer.profile_module(x) 38 | params += p 39 | macs += m 40 | return x, params, macs 41 | 42 | def __repr__(self): 43 | format_string = self.__class__.__name__ + ' (apply_k (N={})={}, '.format(len(self._modules), self.keep_k) 44 | for idx, layer in self._modules.items(): 45 | format_string += '\n\t {}'.format(layer) 46 | format_string += '\n)' 47 | return format_string -------------------------------------------------------------------------------- /cvnets/layers/sep_conv_layer.py: -------------------------------------------------------------------------------- 1 | from torch import Tensor 2 | from typing import Optional 3 | from ..misc.profiler import module_profile 4 | from .base_layer import BaseLayer 5 | from .conv_layer import ConvLayer 6 | 7 | 8 | class SeparableConv(BaseLayer): 9 | """ 10 | This layer defines Depth-wise separable convolution, introduced in Xception 11 | https://arxiv.org/abs/1610.02357 12 | 13 | """ 14 | def __init__(self, 15 | opts, 16 | in_channels: int, 17 | out_channels: int, 18 | kernel_size: int or tuple, 19 | stride: Optional[int or tuple] = 1, 20 | dilation: Optional[int or tuple] = 1, 21 | use_norm: Optional[bool] = True, 22 | use_act: Optional[bool] = True, 23 | bias: Optional[bool] = False, padding_mode: Optional[str] = 'zeros', 24 | *args, **kwargs): 25 | super(SeparableConv, self).__init__() 26 | self.dw_conv = ConvLayer( 27 | opts=opts, in_channels=in_channels, out_channels=in_channels, kernel_size=kernel_size, 28 | stride=stride, dilation=dilation, groups=in_channels, bias=False, padding_mode=padding_mode, 29 | use_norm=True, use_act=False 30 | ) 31 | self.pw_conv = ConvLayer( 32 | opts=opts, in_channels=in_channels, out_channels=out_channels, kernel_size=1, 33 | stride=1, dilation=1, groups=1, bias=bias, padding_mode=padding_mode, 34 | use_norm=use_norm, use_act=use_act 35 | ) 36 | self.in_channels = in_channels 37 | self.out_channels = out_channels 38 | self.stride = stride 39 | self.kernel_size = kernel_size 40 | self.dilation = dilation 41 | 42 | def __repr__(self): 43 | repr_str = "{}(in_channels={}, out_channels={}, kernel_size={}, stride={}, dilation={})".format( 44 | self.__class__.__name__, 45 | self.in_channels, 46 | self.out_channels, 47 | self.kernel_size, 48 | self.stride, 49 | self.dilation 50 | ) 51 | return repr_str 52 | 53 | def forward(self, x: Tensor) -> Tensor: 54 | x = self.dw_conv(x) 55 | x = self.pw_conv(x) 56 | return x 57 | 58 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 59 | params, macs = 0.0, 0.0 60 | input, p, m = module_profile(module=self.dw_conv, x=input) 61 | params += p 62 | macs += m 63 | 64 | input, p, m = module_profile(module=self.pw_conv, x=input) 65 | params += p 66 | macs += m 67 | 68 | return input, params, macs -------------------------------------------------------------------------------- /cvnets/layers/upsample.py: -------------------------------------------------------------------------------- 1 | 2 | import torch 3 | from torch import nn, Tensor 4 | 5 | 6 | class UpSample(nn.Upsample): 7 | def __init__(self, size=None, scale_factor=None, mode='nearest', align_corners=None): 8 | super(UpSample, self).__init__(size=size, scale_factor=scale_factor, mode=mode, align_corners=align_corners) 9 | 10 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 11 | input = self.forward(input) 12 | return input, 0.0, 0.0 13 | -------------------------------------------------------------------------------- /cvnets/misc/averaging_utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from copy import deepcopy 3 | import argparse 4 | 5 | 6 | class EMA(object): 7 | ''' 8 | Exponential moving average of model weights 9 | ''' 10 | def __init__(self, model, ema_momentum: float = 0.1, device: str = ''): 11 | # make a deep copy of the model for accumulating moving average of parameters 12 | self.ema_model = deepcopy(model) 13 | self.ema_model.eval() 14 | self.momentum = ema_momentum 15 | self.device = device 16 | if device: 17 | self.ema_model.to(device=device) 18 | self.ema_has_module = hasattr(self.ema_model, 'module') 19 | for param in self.ema_model.parameters(): 20 | param.requires_grad = False 21 | 22 | def update_parameters(self, model): 23 | # correct a mismatch in state dict keys 24 | has_module = hasattr(model, 'module') and not self.ema_has_module 25 | with torch.no_grad(): 26 | msd = model.state_dict() 27 | for k, ema_v in self.ema_model.state_dict().items(): 28 | if has_module: 29 | # .module is added if we use DistributedDataParallel or DataParallel wrappers around model 30 | k = 'module.' + k 31 | model_v = msd[k].detach() 32 | if self.device: 33 | model_v = model_v.to(device=self.device) 34 | ema_v.copy_((ema_v * (1.0 - self.momentum)) + (self.momentum * model_v)) 35 | 36 | 37 | def arguments_ema(parser: argparse.ArgumentParser): 38 | group = parser.add_argument_group(title='EMA', description='Exponential moving average arguments') 39 | group.add_argument('--ema.enable', action='store_true', help='Exponential moving average') 40 | group.add_argument('--ema.momentum', type=float, default=0.0001, help='EMA momentum') 41 | group.add_argument('--ema.copy-at-epoch', type=int, default=-1, 42 | help='Update weights with EMA model at this epoch') 43 | return parser 44 | -------------------------------------------------------------------------------- /cvnets/misc/common.py: -------------------------------------------------------------------------------- 1 | 2 | import torch 3 | import os 4 | from utils import logger 5 | 6 | 7 | def load_pretrained_model(model, wt_loc, is_master_node: bool = False): 8 | if not os.path.isfile(wt_loc): 9 | logger.error('Pretrained file is not found here: {}'.format(wt_loc)) 10 | 11 | wts = torch.load(wt_loc, map_location="cpu") 12 | if hasattr(model, "module"): 13 | model.module.load_state_dict(wts) 14 | else: 15 | model.load_state_dict(wts) 16 | 17 | if is_master_node: 18 | logger.log('Pretrained weights are loaded from {}'.format(wt_loc)) 19 | return model 20 | 21 | 22 | def parameter_list(named_parameters, weight_decay: float = 0.0, no_decay_bn_filter_bias: bool = False): 23 | with_decay = [] 24 | without_decay = [] 25 | if isinstance(named_parameters, list): 26 | for n_parameter in named_parameters: 27 | for p_name, param in n_parameter(): 28 | if param.requires_grad and len(param.shape) == 1 and no_decay_bn_filter_bias: 29 | # biases and normalization layer parameters are of len 1 30 | without_decay.append(param) 31 | elif param.requires_grad: 32 | with_decay.append(param) 33 | else: 34 | for p_name, param in named_parameters(): 35 | if param.requires_grad and len(param.shape) == 1 and no_decay_bn_filter_bias: 36 | # biases and normalization layer parameters are of len 1 37 | without_decay.append(param) 38 | elif param.requires_grad: 39 | with_decay.append(param) 40 | param_list = [{'params': with_decay, 'weight_decay': weight_decay}] 41 | if len(without_decay) > 0: 42 | param_list.append({'params': without_decay, 'weight_decay': 0.0}) 43 | return param_list -------------------------------------------------------------------------------- /cvnets/misc/match_prior.py: -------------------------------------------------------------------------------- 1 | 2 | import torch 3 | from torch import Tensor 4 | import numpy as np 5 | from typing import Optional, Union, Tuple 6 | 7 | from .third_party.ssd_utils import assign_priors 8 | from cvnets.misc.box_utils import ( 9 | center_form_to_corner_form, 10 | corner_form_to_center_form, 11 | convert_boxes_to_locations 12 | ) 13 | 14 | 15 | class SSDMatcher(object): 16 | ''' 17 | Match priors with ground truth boxes 18 | ''' 19 | def __init__(self, 20 | center_variance: Optional[float] = 0.1, 21 | size_variance: Optional[float] = 0.2, 22 | iou_threshold: Optional[float] = 0.5) -> None: 23 | self.center_variance = center_variance 24 | self.size_variance = size_variance 25 | self.iou_threshold = iou_threshold 26 | 27 | def __call__(self, 28 | gt_boxes_cor: Union[np.ndarray, Tensor], 29 | gt_labels: Union[np.ndarray, Tensor], 30 | reference_boxes_ctr: Tensor) -> Tuple[Tensor, Tensor]: 31 | """ 32 | :param gt_boxes_cor: Ground truth boxes in corner form (x1, y1, x2, y2) 33 | :param gt_labels: Ground truth box labels 34 | :param reference_boxes_ctr: Anchor boxes in center form (c_x1, c_y1, dw, dh) 35 | :return: Matched boxes and their corresponding labels in center form 36 | """ 37 | 38 | if isinstance(gt_boxes_cor, np.ndarray): 39 | gt_boxes_cor = torch.from_numpy(gt_boxes_cor) 40 | if isinstance(gt_labels, np.ndarray): 41 | gt_labels = torch.from_numpy(gt_labels) 42 | 43 | # convert box priors from center [c_x, c_y] to corner_form [x, y] 44 | reference_boxes_cor = center_form_to_corner_form(boxes=reference_boxes_ctr) 45 | 46 | matched_boxes_cor, matched_labels = assign_priors( 47 | gt_boxes_cor, # gt_boxes are in corner form [x, y, w, h] 48 | gt_labels, 49 | reference_boxes_cor, # priors are in corner form [x, y, w, h] 50 | self.iou_threshold 51 | ) 52 | 53 | # convert the matched boxes to center form [c_x, c_y] 54 | matched_boxes_ctr = corner_form_to_center_form(matched_boxes_cor) 55 | 56 | # Eq.(2) in paper https://arxiv.org/pdf/1512.02325.pdf 57 | boxes_for_regression = convert_boxes_to_locations( 58 | gt_boxes=matched_boxes_ctr, # center form 59 | prior_boxes=reference_boxes_ctr, # center form 60 | center_variance=self.center_variance, 61 | size_variance=self.size_variance 62 | ) 63 | 64 | return boxes_for_regression, matched_labels 65 | 66 | 67 | -------------------------------------------------------------------------------- /cvnets/misc/profiler.py: -------------------------------------------------------------------------------- 1 | 2 | import torch 3 | from torch import nn, Tensor 4 | 5 | 6 | def module_profile(module, x: Tensor) -> (Tensor, float, float): 7 | # Note: Module profiling is for reference only and may contain errors. 8 | # Relies on user to implement these functions accurately. 9 | 10 | if isinstance(module, nn.Sequential): 11 | n_macs = n_params = 0.0 12 | for l in module: 13 | try: 14 | x, l_p, l_macs = l.profile_module(x) 15 | n_macs += l_macs 16 | n_params += l_p 17 | except Exception as e: 18 | pass 19 | else: 20 | x, n_params, n_macs = module.profile_module(x) 21 | 22 | return x, n_params, n_macs 23 | 24 | -------------------------------------------------------------------------------- /cvnets/models/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | import argparse 3 | from utils import logger 4 | 5 | from .segmentation import arguments_segmentation, build_segmentation_model 6 | from .classification import arguments_classification, build_classification_model 7 | from .detection import arguments_detection, build_detection_model 8 | 9 | SUPPORTED_TASKS = ["segmentation", "classification", "detection"] 10 | 11 | 12 | def arguments_model(parser: argparse.ArgumentParser) -> argparse.ArgumentParser: 13 | 14 | # classification network 15 | parser = arguments_classification(parser=parser) 16 | 17 | # detection network 18 | parser = arguments_detection(parser=parser) 19 | 20 | # segmentation network 21 | parser = arguments_segmentation(parser=parser) 22 | 23 | return parser 24 | 25 | 26 | def get_model(opts): 27 | dataset_category = getattr(opts, "dataset.category", "classification") 28 | model = None 29 | if dataset_category == "classification": 30 | model = build_classification_model(opts=opts) 31 | elif dataset_category == "segmentation": 32 | model = build_segmentation_model(opts=opts) 33 | elif dataset_category == "detection": 34 | model = build_detection_model(opts=opts) 35 | else: 36 | task_str = 'Got {} as a task. Unfortunately, we do not support it yet.' \ 37 | '\nSupported tasks are:'.format(dataset_category) 38 | for i, task_name in enumerate(SUPPORTED_TASKS): 39 | task_str += "\n\t {}: {}".format(i, task_name) 40 | logger.error(task_str) 41 | 42 | return model -------------------------------------------------------------------------------- /cvnets/models/detection/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/models/detection/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/models/detection/__pycache__/base_detection.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/models/detection/__pycache__/base_detection.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/models/detection/__pycache__/ssd.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/models/detection/__pycache__/ssd.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/models/segmentation/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/models/segmentation/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/models/segmentation/__pycache__/base_seg.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/models/segmentation/__pycache__/base_seg.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/models/segmentation/__pycache__/enc_dec.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/models/segmentation/__pycache__/enc_dec.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/models/segmentation/base_seg.py: -------------------------------------------------------------------------------- 1 | 2 | from torch import nn, Tensor 3 | import argparse 4 | 5 | from cvnets.layers import norm_layers_tuple 6 | 7 | from ..classification import BaseEncoder 8 | from ... import parameter_list 9 | from ...misc.init_utils import initialize_weights 10 | 11 | 12 | class BaseSegmentation(nn.Module): 13 | def __init__(self, opts, encoder: BaseEncoder): 14 | super(BaseSegmentation, self).__init__() 15 | self.lr_multiplier = getattr(opts, "model.segmentation.lr_multiplier", 1.0) 16 | assert isinstance(encoder, BaseEncoder), "encoder should be an instance of BaseEncoder" 17 | self.encoder: BaseEncoder = encoder 18 | 19 | @classmethod 20 | def add_arguments(cls, parser: argparse.ArgumentParser): 21 | return parser 22 | 23 | @staticmethod 24 | def reset_layer_parameters(layer, opts): 25 | # weight initialization 26 | initialize_weights(opts=opts, modules=layer.modules()) 27 | 28 | def get_trainable_parameters(self, weight_decay: float = 0.0, no_decay_bn_filter_bias: bool = False): 29 | param_list = parameter_list(named_parameters=self.named_parameters, 30 | weight_decay=weight_decay, 31 | no_decay_bn_filter_bias=no_decay_bn_filter_bias) 32 | return param_list, [1.0] * len(param_list) 33 | 34 | def profile_model(self, input: Tensor): 35 | raise NotImplementedError 36 | 37 | def freeze_norm_layers(self): 38 | for m in self.modules(): 39 | if isinstance(m, norm_layers_tuple): 40 | m.eval() 41 | m.weight.requires_grad = False 42 | m.bias.requires_grad = False 43 | m.training = False 44 | -------------------------------------------------------------------------------- /cvnets/models/segmentation/heads/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import os 7 | import importlib 8 | from utils import logger 9 | from typing import Dict 10 | import argparse 11 | 12 | from .base_seg_head import BaseSegHead 13 | 14 | SEG_HEAD_REGISTRY = {} 15 | 16 | 17 | def register_segmentation_head(name): 18 | def register_model_class(cls): 19 | if name in SEG_HEAD_REGISTRY: 20 | raise ValueError("Cannot register duplicate model ({})".format(name)) 21 | 22 | if not issubclass(cls, BaseSegHead): 23 | raise ValueError( 24 | "Model ({}: {}) must extend BaseSegHead".format(name, cls.__name__) 25 | ) 26 | 27 | SEG_HEAD_REGISTRY[name] = cls 28 | return cls 29 | 30 | return register_model_class 31 | 32 | 33 | def build_segmentation_head(opts, enc_conf: Dict, use_l5_exp: bool = False): 34 | seg_model_name = getattr(opts, "model.segmentation.seg_head", "lr_aspp") 35 | seg_head = None 36 | if seg_model_name in SEG_HEAD_REGISTRY: 37 | seg_head = SEG_HEAD_REGISTRY[seg_model_name](opts=opts, enc_conf=enc_conf, use_l5_exp=use_l5_exp) 38 | else: 39 | supported_heads = list(SEG_HEAD_REGISTRY.keys()) 40 | supp_model_str = "Supported segmentation heads are:" 41 | for i, m_name in enumerate(supported_heads): 42 | supp_model_str += "\n\t {}: {}".format(i, logger.color_text(m_name)) 43 | logger.error(supp_model_str) 44 | 45 | return seg_head 46 | 47 | 48 | def arguments_segmentation_head(parser: argparse.ArgumentParser): 49 | # add segmentation specific arguments 50 | for k, v in SEG_HEAD_REGISTRY.items(): 51 | parser = v.add_arguments(parser=parser) 52 | 53 | return parser 54 | 55 | 56 | # automatically import the models 57 | models_dir = os.path.dirname(__file__) 58 | for file in os.listdir(models_dir): 59 | path = os.path.join(models_dir, file) 60 | if ( 61 | not file.startswith("_") 62 | and not file.startswith(".") 63 | and (file.endswith(".py") or os.path.isdir(path)) 64 | ): 65 | model_name = file[: file.find(".py")] if file.endswith(".py") else file 66 | module = importlib.import_module("cvnets.models.segmentation.heads." + model_name) -------------------------------------------------------------------------------- /cvnets/models/segmentation/heads/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/models/segmentation/heads/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/models/segmentation/heads/__pycache__/base_seg_head.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/models/segmentation/heads/__pycache__/base_seg_head.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/models/segmentation/heads/__pycache__/deeplabv3.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/models/segmentation/heads/__pycache__/deeplabv3.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/modules/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from .base_module import BaseModule 3 | from .squeeze_excitation import SqueezeExcitation 4 | from .aspp_block import ASPP 5 | from .transformer import TransformerEncoder 6 | from .ppm import PPM 7 | from .mobilenetv2 import InvertedResidual 8 | from .mobilevit_block import MobileViTBlock 9 | from .feature_pyramid import FPModule 10 | from .ssd import SSDHead 11 | from .edgeformer_block import outer_frame_v1, outer_frame_v2 12 | 13 | __all__ = [ 14 | 'ASPP', 15 | 'TransformerEncoder', 16 | 'SqueezeExcitation', 17 | 'PPM', 18 | 'InvertedResidual', 19 | 'MobileViTBlock', 20 | 'FPModule', 21 | 'SSDHead', 22 | 'outer_frame_v1', 23 | 'outer_frame_v2', 24 | ] -------------------------------------------------------------------------------- /cvnets/modules/__pycache__/aspp_block.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/modules/__pycache__/aspp_block.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/modules/__pycache__/base_module.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/modules/__pycache__/base_module.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/modules/__pycache__/chip_former_block.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/modules/__pycache__/chip_former_block.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/modules/__pycache__/chipformer_block.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/modules/__pycache__/chipformer_block.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/modules/__pycache__/feature_pyramid.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/modules/__pycache__/feature_pyramid.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/modules/__pycache__/mobilevit_block.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/modules/__pycache__/mobilevit_block.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/modules/__pycache__/ppm.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/modules/__pycache__/ppm.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/modules/__pycache__/squeeze_excitation.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/modules/__pycache__/squeeze_excitation.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/modules/__pycache__/ssd.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/modules/__pycache__/ssd.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/modules/__pycache__/transformer.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/cvnets/modules/__pycache__/transformer.cpython-38.pyc -------------------------------------------------------------------------------- /cvnets/modules/base_module.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | import torch 4 | from torch import nn, Tensor 5 | from typing import Tuple 6 | 7 | 8 | class BaseModule(nn.Module): 9 | def __init__(self, *args, **kwargs): 10 | super(BaseModule, self).__init__() 11 | 12 | def forward(self, x: Tensor or Tuple[Tensor]) -> Tensor or Tuple[Tensor]: 13 | raise NotImplementedError 14 | 15 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 16 | raise NotImplementedError 17 | 18 | def __repr__(self): 19 | return "{}".format(self.__class__.__name__) -------------------------------------------------------------------------------- /cvnets/modules/feature_pyramid.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch import nn, Tensor 3 | from typing import Optional, Tuple, List 4 | import torch.nn.functional as F 5 | 6 | from ..layers import ConvLayer, UpSample 7 | from ..modules import BaseModule 8 | from ..misc.profiler import module_profile 9 | 10 | 11 | class FPModule(BaseModule): 12 | """ 13 | Inspired from the PSP module in the PSPNet paper: 14 | https://arxiv.org/abs/1612.01105 15 | Difference: Replaces the average pooling with Upsample function 16 | """ 17 | def __init__(self, 18 | opts, 19 | in_channels: int, 20 | out_channels: int, 21 | scales: Optional[Tuple or List] = (0.25, 0.5, 2.0), 22 | ) -> None: 23 | projection_dim = max(int(in_channels / len(scales)), 32) 24 | fp_branches = [] 25 | for scale in scales: 26 | cbr_layer = ConvLayer(opts=opts, in_channels=in_channels, out_channels=projection_dim, 27 | kernel_size=1, stride=1, use_norm=True, use_act=True) 28 | branch = nn.Sequential() 29 | branch.add_module( 30 | name="scale_".format(scale), 31 | module=UpSample(scale_factor=scale, mode="bilinear", align_corners=False) 32 | ) 33 | branch.add_module(name="conv_1x1", module=cbr_layer) 34 | fp_branches.append(branch) 35 | 36 | channels_after_concat = in_channels + (projection_dim * len(scales)) 37 | conv_3x3 = ConvLayer(opts=opts, in_channels=channels_after_concat, out_channels=out_channels, 38 | kernel_size=3, stride=1, use_norm=True, use_act=True) 39 | super(FPModule, self).__init__() 40 | self.fp_branches = nn.ModuleList(fp_branches) 41 | self.fusion = conv_3x3 42 | self.in_channels = in_channels 43 | self.out_channels = out_channels 44 | self.scales = scales 45 | 46 | def forward(self, x: Tensor) -> Tensor: 47 | x_size = x.size() 48 | res = [x] 49 | for psp_branch in self.fp_branches: 50 | out = psp_branch(x) 51 | out = F.interpolate(out, x_size[2:], mode='bilinear', align_corners=True) 52 | res.append(out) 53 | return self.fusion(torch.cat(res, dim=1)) 54 | 55 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 56 | params, macs = 0.0, 0.0 57 | res = [input] 58 | input_size = input.size() 59 | for psp_branch in self.fp_branches: 60 | out, p, m = module_profile(module=psp_branch, x=input) 61 | out = F.interpolate(out, input_size[2:], mode='bilinear', align_corners=True) 62 | params += p 63 | macs += m 64 | res.append(out) 65 | res = torch.cat(res, dim=1) 66 | 67 | res, p, m = module_profile(module=self.fusion, x=res) 68 | return res, params + p, macs + m 69 | 70 | def __repr__(self): 71 | return "{}(in_channels={}, out_channels={}, scales={})".format( 72 | self.__class__.__name__, 73 | self.in_channels, 74 | self.out_channels, 75 | self.scales 76 | ) -------------------------------------------------------------------------------- /cvnets/modules/squeeze_excitation.py: -------------------------------------------------------------------------------- 1 | 2 | import torch 3 | from torch import nn, Tensor 4 | from typing import Optional 5 | from utils.math_utils import make_divisible 6 | 7 | from ..layers import AdaptiveAvgPool2d, ConvLayer, get_activation_fn, LinearLayer 8 | from ..modules import BaseModule 9 | from ..misc.profiler import module_profile 10 | 11 | 12 | class SqueezeExcitation(BaseModule): 13 | """ 14 | This class defines the SE module 15 | https://arxiv.org/abs/1709.01507 16 | """ 17 | def __init__(self, 18 | opts, 19 | in_channels: int, 20 | squeeze_factor: Optional[int] = 4, 21 | scale_fn_name: Optional[str] = 'sigmoid' 22 | ) -> None: 23 | squeeze_channels = max(make_divisible(in_channels // squeeze_factor, 8), 32) 24 | 25 | fc1 = ConvLayer(opts=opts, in_channels=in_channels, out_channels=squeeze_channels, kernel_size=1, stride=1, 26 | bias=True, use_norm=False, use_act=True) 27 | fc2 = ConvLayer(opts=opts, in_channels=squeeze_channels, out_channels=in_channels, kernel_size=1, stride=1, 28 | bias=True, use_norm=False, use_act=False) 29 | if scale_fn_name == "sigmoid": 30 | act_fn = get_activation_fn(act_type="sigmoid") 31 | elif scale_fn_name == "hard_sigmoid": 32 | act_fn = get_activation_fn(act_type="hard_sigmoid", inplace=True) 33 | else: 34 | raise NotImplementedError 35 | 36 | super(SqueezeExcitation, self).__init__() 37 | self.se_layer = nn.Sequential() 38 | self.se_layer.add_module(name="global_pool", module=AdaptiveAvgPool2d(output_size=1)) 39 | self.se_layer.add_module(name="fc1", module=fc1) 40 | self.se_layer.add_module(name="fc2", module=fc2) 41 | self.se_layer.add_module(name="scale_act", module=act_fn) 42 | 43 | self.in_channels = in_channels 44 | self.squeeze_factor = squeeze_factor 45 | self.scale_fn = scale_fn_name 46 | 47 | def forward(self, x: Tensor) -> Tensor: 48 | return x * self.se_layer(x) 49 | 50 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 51 | _, params, macs = module_profile(module=self.se_layer, x=input) 52 | return input, params, macs 53 | 54 | def __repr__(self): 55 | return '{}(in_channels={}, squeeze_factor={}, scale_fn={})'.format( 56 | self.__class__.__name__, 57 | self.in_channels, 58 | self.squeeze_factor, 59 | self.scale_fn 60 | ) 61 | -------------------------------------------------------------------------------- /cvnets/modules/transformer.py: -------------------------------------------------------------------------------- 1 | 2 | from torch import nn, Tensor 3 | from typing import Optional 4 | 5 | from ..layers import get_normalization_layer, LinearLayer, get_activation_fn, MultiHeadAttention, Dropout 6 | from ..modules import BaseModule 7 | from ..misc.profiler import module_profile 8 | 9 | 10 | class TransformerEncoder(BaseModule): 11 | """ 12 | This class defines the Transformer encoder (pre-norm) as described in "Attention is all you need" paper 13 | https://arxiv.org/abs/1706.03762 14 | """ 15 | def __init__(self, opts, embed_dim: int, ffn_latent_dim: int, num_heads: Optional[int] = 8, attn_dropout: Optional[float] = 0.0, 16 | dropout: Optional[float] = 0.1, ffn_dropout: Optional[float] = 0.0, 17 | transformer_norm_layer: Optional[str] = "layer_norm", 18 | *args, **kwargs): 19 | super(TransformerEncoder, self).__init__() 20 | 21 | self.pre_norm_mha = nn.Sequential( 22 | get_normalization_layer(opts=opts, norm_type=transformer_norm_layer, num_features=embed_dim), 23 | MultiHeadAttention(embed_dim, num_heads, attn_dropout=attn_dropout, bias=True), 24 | Dropout(p=dropout) 25 | ) 26 | 27 | self.pre_norm_ffn = nn.Sequential( 28 | get_normalization_layer(opts=opts, norm_type=transformer_norm_layer, num_features=embed_dim), 29 | LinearLayer(in_features=embed_dim, out_features=ffn_latent_dim, bias=True), 30 | self.build_act_layer(opts=opts), 31 | Dropout(p=ffn_dropout), 32 | LinearLayer(in_features=ffn_latent_dim, out_features=embed_dim, bias=True), 33 | Dropout(p=dropout) 34 | ) 35 | self.embed_dim = embed_dim 36 | self.ffn_dim = ffn_latent_dim 37 | self.ffn_dropout = ffn_dropout 38 | 39 | @staticmethod 40 | def build_act_layer(opts): 41 | act_type = getattr(opts, "model.activation.name", "relu") 42 | neg_slope = getattr(opts, "model.activation.neg_slope", 0.1) 43 | inplace = getattr(opts, "model.activation.inplace", False) 44 | act_layer = get_activation_fn(act_type=act_type, inplace=inplace, negative_slope=neg_slope, 45 | num_parameters=1) 46 | return act_layer 47 | 48 | def forward(self, x: Tensor) -> Tensor: 49 | 50 | # Multi-head attention 51 | x = x + self.pre_norm_mha(x) 52 | 53 | # Feed forward network 54 | x = x + self.pre_norm_ffn(x) 55 | return x 56 | 57 | def profile_module(self, input: Tensor) -> (Tensor, float, float): 58 | b_sz, seq_len = input.shape[:2] 59 | 60 | out, p_mha, m_mha = module_profile(module=self.pre_norm_mha, x=input) 61 | 62 | out, p_ffn, m_ffn = module_profile(module=self.pre_norm_ffn, x=input) 63 | m_ffn = (m_ffn * b_sz * seq_len) 64 | 65 | macs = m_mha + m_ffn 66 | params = p_mha + p_ffn 67 | 68 | return input, params, macs 69 | -------------------------------------------------------------------------------- /data/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from .data_loaders import create_train_val_loader, create_eval_loader -------------------------------------------------------------------------------- /data/dataset_preparing/classification_imagenet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/dataset_preparing/classification_imagenet/__init__.py -------------------------------------------------------------------------------- /data/dataset_preparing/classification_imagenet/tar2jpeg.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | from glob import glob 4 | from functools import partial 5 | from multiprocessing import Pool 6 | 7 | 8 | data_root = '/home/disk/data/imagenet1k/train' 9 | img_root = '/home/disk/data/imagenet1k/training' 10 | 11 | tar_list = glob(data_root + '/*.tar') 12 | 13 | 14 | 15 | def untar(id, tar_list): 16 | tar=tar_list[id] 17 | tar_name = tar.split('/')[-1].split('.')[0] 18 | jpeg_dir = img_root + '/{}'.format(tar_name) 19 | os.makedirs(jpeg_dir) 20 | cmd = 'tar xf {} -C {}'.format(tar, jpeg_dir) 21 | subprocess.call(cmd, shell=True) 22 | 23 | 24 | if __name__ == "__main__": 25 | pool = Pool() 26 | partial_untar = partial(untar, tar_list=tar_list) 27 | N = len(tar_list) 28 | _ = pool.map(partial_untar, range(N)) 29 | pool.close() 30 | pool.join() 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /data/dataset_preparing/detection_coco/download_coco.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | global_path='../../../../vision_datasets' 3 | data_dir=$global_path 4 | 5 | mkdir $data_dir 6 | cd $data_dir 7 | 8 | 9 | git clone https://github.com/pdollar/coco 10 | cd coco 11 | 12 | mkdir images 13 | cd images 14 | 15 | echo "Downloading train and validation images" 16 | 17 | # Download Images and annotations 18 | wget -c http://images.cocodataset.org/zips/train2017.zip 19 | wget -c http://images.cocodataset.org/zips/val2017.zip 20 | 21 | # Unzip 22 | echo "Unziping train folder" 23 | unzip -q train2017.zip 24 | echo "Unziping val folder" 25 | unzip -q val2017.zip 26 | 27 | echo "Deleting zip files" 28 | rm -rf train2017.zip 29 | rm -rf val2017.zip 30 | 31 | echo "COCO data downloading over!!" 32 | 33 | cd .. 34 | echo "Downloading annotations" 35 | wget -c http://images.cocodataset.org/annotations/annotations_trainval2017.zip 36 | unzip -q annotations_trainval2017.zip 37 | rm -rf annotations_trainval2017.zip 38 | echo "Done" -------------------------------------------------------------------------------- /data/dataset_preparing/segmentation_pascal_&_coco/pascal_aug_tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/dataset_preparing/segmentation_pascal_&_coco/pascal_aug_tools/__init__.py -------------------------------------------------------------------------------- /data/dataset_preparing/segmentation_pascal_&_coco/pascal_aug_tools/convert_labels.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import os 3 | import sys 4 | import numpy as np 5 | from PIL import Image 6 | 7 | 8 | def main(): 9 | ext = '.png' 10 | path = '/home/disk/data/pascal_aug/VOC2012/SegmentationClass' 11 | txt_file = '/home/disk/data/pascal_aug/VOC2012/ImageSets/Segmentation/trainval.txt' 12 | path_converted = '/home/disk/data/pascal_aug/VOC2012/SegmentationClass_1D' 13 | 14 | # Create dir for converted labels 15 | if not os.path.isdir(path_converted): 16 | os.makedirs(path_converted) 17 | 18 | with open(txt_file, 'r') as f: 19 | for img_name in f: 20 | img_base_name = img_name.strip() 21 | print(img_base_name) 22 | img_name = os.path.join(path, img_base_name) + ext 23 | 24 | mask = Image.open(img_name) 25 | mask = np.array(mask).astype('int32') 26 | mask = Image.fromarray(mask.astype('uint8')) 27 | mask.save(os.path.join(path_converted, img_base_name + ext)) 28 | 29 | 30 | def process_arguments(argv): 31 | if len(argv) != 4: 32 | help() 33 | 34 | path = argv[1] 35 | list_file = argv[2] 36 | new_path = argv[3] 37 | 38 | return path, list_file, new_path 39 | 40 | 41 | def help(): 42 | print('Usage: python convert_labels.py PATH LIST_FILE NEW_PATH\n' 43 | 'PATH points to directory with segmentation image labels.\n' 44 | 'LIST_FILE denotes text file containing names of images in PATH.\n' 45 | 'Names do not include extension of images.\n' 46 | 'NEW_PATH points to directory where converted labels will be stored.' 47 | , file=sys.stderr) 48 | exit() 49 | 50 | 51 | if __name__ == '__main__': 52 | main() -------------------------------------------------------------------------------- /data/dataset_preparing/segmentation_pascal_&_coco/pascal_aug_tools/mat2png.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #encoding: utf-8 3 | # Martin Kersner, m.kersner@gmail.com 4 | # 2016/03/17 5 | 6 | from __future__ import print_function 7 | import os 8 | import sys 9 | import glob 10 | from PIL import Image as PILImage 11 | 12 | from utils import mat2png_hariharan 13 | 14 | def main(): 15 | # input_path, output_path = process_arguments(sys.argv) 16 | input_path = '/home/disk/data/pascal_aug/sbd/cls' 17 | output_path = '/home/disk/data/pascal_aug/sbd/cls_aug' 18 | 19 | if os.path.isdir(input_path) and os.path.isdir(output_path): 20 | # glob.blob 返回所有匹配的文件路径列表 21 | mat_files = glob.glob(os.path.join(input_path, '*.mat')) 22 | convert_mat2png(mat_files, output_path) 23 | else: 24 | help('Input or output path does not exist!\n') 25 | 26 | def process_arguments(argv): 27 | num_args = len(argv) 28 | 29 | input_path = None 30 | output_path = None 31 | 32 | if num_args == 3: 33 | input_path = argv[1] 34 | output_path = argv[2] 35 | else: 36 | help() 37 | 38 | return input_path, output_path 39 | 40 | def convert_mat2png(mat_files, output_path): 41 | if not mat_files: 42 | help('Input directory does not contain any Matlab files!\n') 43 | 44 | for mat in mat_files: 45 | numpy_img = mat2png_hariharan(mat) 46 | pil_img = PILImage.fromarray(numpy_img) 47 | pil_img.save(os.path.join(output_path, modify_image_name(mat, 'png'))) 48 | 49 | # Extract name of image from given path, replace its extension with specified one 50 | # and return new name only, not path. 51 | def modify_image_name(path, ext): 52 | return os.path.basename(path).split('.')[0] + '.' + ext 53 | 54 | def help(msg=''): 55 | print(msg + 56 | 'Usage: python mat2png.py INPUT_PATH OUTPUT_PATH\n' 57 | 'INPUT_PATH denotes path containing Matlab files for conversion.\n' 58 | 'OUTPUT_PATH denotes path where converted Png files ar going to be saved.' 59 | , file=sys.stderr) 60 | 61 | exit() 62 | 63 | if __name__ == '__main__': 64 | main() 65 | 66 | -------------------------------------------------------------------------------- /data/datasets/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/datasets/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /data/datasets/__pycache__/dataset_base.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/datasets/__pycache__/dataset_base.cpython-38.pyc -------------------------------------------------------------------------------- /data/datasets/classification/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/datasets/classification/__init__.py -------------------------------------------------------------------------------- /data/datasets/classification/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/datasets/classification/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /data/datasets/classification/__pycache__/imagenet.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/datasets/classification/__pycache__/imagenet.cpython-38.pyc -------------------------------------------------------------------------------- /data/datasets/detection/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/datasets/detection/__init__.py -------------------------------------------------------------------------------- /data/datasets/detection/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/datasets/detection/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /data/datasets/detection/__pycache__/coco.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/datasets/detection/__pycache__/coco.cpython-38.pyc -------------------------------------------------------------------------------- /data/datasets/segmentation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/datasets/segmentation/__init__.py -------------------------------------------------------------------------------- /data/datasets/segmentation/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/datasets/segmentation/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /data/datasets/segmentation/__pycache__/pascal_voc.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/datasets/segmentation/__pycache__/pascal_voc.cpython-38.pyc -------------------------------------------------------------------------------- /data/sampler/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import importlib 4 | from typing import Optional 5 | from utils import logger 6 | import argparse 7 | 8 | 9 | from .base_sampler import BaseSamplerDDP, BaseSamplerDP 10 | 11 | SAMPLER_REGISTRY = {} 12 | 13 | 14 | def register_sampler(name): 15 | def register_sampler_class(cls): 16 | if name in SAMPLER_REGISTRY: 17 | raise ValueError("Cannot register duplicate sampler class ({})".format(name)) 18 | 19 | if not (issubclass(cls, BaseSamplerDDP) or issubclass(cls, BaseSamplerDP)): 20 | raise ValueError( 21 | "Sampler ({}: {}) must extend BaseSamplerDDP or BaseSamplerDP".format(name, cls.__name__) 22 | ) 23 | 24 | SAMPLER_REGISTRY[name] = cls 25 | return cls 26 | 27 | return register_sampler_class 28 | 29 | 30 | def build_sampler(opts, n_data_samples: int, is_training: Optional[bool] = False): 31 | sampler_name = getattr(opts, "sampler.name", "variable_batch_sampler") 32 | is_distributed = getattr(opts, "ddp.use_distributed", False) 33 | 34 | if is_distributed and sampler_name.split('_')[-1] != "ddp": 35 | sampler_name = sampler_name + "_ddp" 36 | 37 | sampler = None 38 | if sampler_name in SAMPLER_REGISTRY: 39 | sampler = SAMPLER_REGISTRY[sampler_name](opts, n_data_samples=n_data_samples, is_training=is_training) 40 | else: 41 | supp_list = list(SAMPLER_REGISTRY.keys()) 42 | supp_str = "Sampler ({}) not yet supported. \n Supported optimizers are:".format(sampler_name) 43 | for i, m_name in enumerate(supp_list): 44 | supp_str += "\n\t {}: {}".format(i, logger.color_text(m_name)) 45 | logger.error(supp_str) 46 | 47 | return sampler 48 | 49 | 50 | def sampler_common_args(parser: argparse.ArgumentParser): 51 | parser.add_argument('--sampler.name', type=str, default="batch_sampler", help="Name of the sampler") 52 | 53 | return parser 54 | 55 | 56 | def arguments_sampler(parser: argparse.ArgumentParser): 57 | parser = sampler_common_args(parser=parser) 58 | 59 | # add classification specific arguments 60 | for k, v in SAMPLER_REGISTRY.items(): 61 | parser = v.add_arguments(parser=parser) 62 | 63 | return parser 64 | 65 | 66 | # automatically import the samplers 67 | sampler_dir = os.path.dirname(__file__) 68 | for file in os.listdir(sampler_dir): 69 | path = os.path.join(sampler_dir, file) 70 | if ( 71 | not file.startswith("_") 72 | and not file.startswith(".") 73 | and (file.endswith(".py") or os.path.isdir(path)) 74 | ): 75 | sampler_name = file[: file.find(".py")] if file.endswith(".py") else file 76 | module = importlib.import_module("data.sampler." + sampler_name) 77 | -------------------------------------------------------------------------------- /data/sampler/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/sampler/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /data/sampler/__pycache__/base_sampler.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/sampler/__pycache__/base_sampler.cpython-38.pyc -------------------------------------------------------------------------------- /data/sampler/__pycache__/batch_sampler.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/sampler/__pycache__/batch_sampler.cpython-38.pyc -------------------------------------------------------------------------------- /data/sampler/__pycache__/utils.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/sampler/__pycache__/utils.cpython-38.pyc -------------------------------------------------------------------------------- /data/sampler/__pycache__/variable_batch_sampler.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/sampler/__pycache__/variable_batch_sampler.cpython-38.pyc -------------------------------------------------------------------------------- /data/sampler/utils.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Optional 3 | from utils.math_utils import make_divisible 4 | import numpy as np 5 | 6 | 7 | def _image_batch_pairs(crop_size_w: int, 8 | crop_size_h: int, 9 | batch_size_gpu0: int, 10 | n_gpus: int, 11 | max_scales: Optional[float] = 5, 12 | check_scale_div_factor: Optional[int] = 32, 13 | min_crop_size_w: Optional[int] = 160, 14 | max_crop_size_w: Optional[int] = 320, 15 | min_crop_size_h: Optional[int] = 160, 16 | max_crop_size_h: Optional[int] = 320, 17 | *args, **kwargs) -> list: 18 | """ 19 | This function creates batch and image size pairs. For a given batch size and image size, different image sizes 20 | are generated and batch size is adjusted so that GPU memory can be utilized efficiently. 21 | 22 | :param crop_size_w: Base Image width (e.g., 224) 23 | :param crop_size_h: Base Image height (e.g., 224) 24 | :param batch_size_gpu0: Batch size on GPU 0 for base image 25 | :param n_gpus: Number of available GPUs 26 | :param max_scales: Number of scales. How many image sizes that we want to generate between min and max scale factors. 27 | :param check_scale_div_factor: Check if image scales are divisible by this factor. 28 | :param min_crop_size_w: Min. crop size along width 29 | :param max_crop_size_w: Max. crop size along width 30 | :param min_crop_size_h: Min. crop size along height 31 | :param max_crop_size_h: Max. crop size along height 32 | :param args: 33 | :param kwargs: 34 | :return: a sorted list of tuples. Each index is of the form (h, w, batch_size) 35 | """ 36 | 37 | width_dims = list(np.linspace(min_crop_size_w, max_crop_size_w, max_scales)) 38 | if crop_size_w not in width_dims: 39 | width_dims.append(crop_size_w) 40 | 41 | height_dims = list(np.linspace(min_crop_size_h, max_crop_size_h, max_scales)) 42 | if crop_size_h not in height_dims: 43 | height_dims.append(crop_size_h) 44 | 45 | image_scales = set() 46 | 47 | for h, w in zip(height_dims, width_dims): 48 | # ensure that sampled sizes are divisible by check_scale_div_factor 49 | # This is important in some cases where input undergoes a fixed number of down-sampling stages 50 | # for instance, in ImageNet training, CNNs usually have 5 downsampling stages, which downsamples the 51 | # input image of resolution 224x224 to 7x7 size 52 | h = make_divisible(h, check_scale_div_factor) 53 | w = make_divisible(w, check_scale_div_factor) 54 | image_scales.add((h, w)) 55 | 56 | image_scales = list(image_scales) 57 | 58 | img_batch_tuples = set() 59 | n_elements = crop_size_w * crop_size_h * batch_size_gpu0 60 | for (crop_h, crop_y) in image_scales: 61 | # compute the batch size for sampled image resolutions with respect to the base resolution 62 | _bsz = max(batch_size_gpu0, int(round(n_elements/(crop_h * crop_y), 2))) 63 | 64 | _bsz = make_divisible(_bsz, n_gpus) 65 | img_batch_tuples.add((crop_h, crop_y, _bsz)) 66 | 67 | img_batch_tuples = list(img_batch_tuples) 68 | return sorted(img_batch_tuples) 69 | -------------------------------------------------------------------------------- /data/transforms/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import importlib 4 | import argparse 5 | 6 | from .base_transforms import BaseTransformation 7 | 8 | SUPPORTED_AUG_CATEGORIES = [] 9 | AUGMENTAION_REGISTRY = {} 10 | 11 | 12 | def register_transformations(name, type): 13 | def register_transformation_class(cls): 14 | if name in AUGMENTAION_REGISTRY: 15 | raise ValueError("Cannot register duplicate transformation class ({})".format(name)) 16 | 17 | if not issubclass(cls, BaseTransformation): 18 | raise ValueError( 19 | "Transformation ({}: {}) must extend BaseTransformation".format(name, cls.__name__) 20 | ) 21 | 22 | AUGMENTAION_REGISTRY[name + "_" + type] = cls 23 | return cls 24 | return register_transformation_class 25 | 26 | 27 | def arguments_augmentation(parser: argparse.ArgumentParser) -> argparse.ArgumentParser: 28 | 29 | # add augmentation specific arguments 30 | for k, v in AUGMENTAION_REGISTRY.items(): 31 | parser = v.add_arguments(parser=parser) 32 | 33 | return parser 34 | 35 | 36 | # automatically import the augmentations 37 | transform_dir = os.path.dirname(__file__) 38 | 39 | for file in os.listdir(transform_dir): 40 | path = os.path.join(transform_dir, file) 41 | if ( 42 | not file.startswith("_") 43 | and not file.startswith(".") 44 | and (file.endswith(".py") or os.path.isdir(path)) 45 | ): 46 | transform_name = file[: file.find(".py")] if file.endswith(".py") else file 47 | module = importlib.import_module("data.transforms." + transform_name) -------------------------------------------------------------------------------- /data/transforms/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/transforms/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /data/transforms/__pycache__/base_transforms.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/transforms/__pycache__/base_transforms.cpython-38.pyc -------------------------------------------------------------------------------- /data/transforms/__pycache__/image.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/data/transforms/__pycache__/image.cpython-38.pyc -------------------------------------------------------------------------------- /data/transforms/base_transforms.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from typing import Dict 3 | 4 | 5 | class BaseTransformation(object): 6 | """ 7 | Base class for transformations 8 | """ 9 | def __init__(self, opts): 10 | super(BaseTransformation, self).__init__() 11 | self.opts = opts 12 | 13 | def __call__(self, data: Dict): 14 | raise NotImplementedError 15 | 16 | def __repr__(self): 17 | return '{}()'.format(self.__class__.__name__) 18 | 19 | @classmethod 20 | def add_arguments(cls, parser: argparse.ArgumentParser): 21 | return parser -------------------------------------------------------------------------------- /drawing_results/cls_scatter.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | 4 | 5 | Labels = ['ShuffleNetv2 (2.0x)', 'MobileNetv3 (1.0x)', 'EfficientNet-B0', 'ConvNext-T (0.6x)', 'PVT-T', 'Swin-1G', 'DeIT-2G', 'ConViT-T', 'LeViT-128S', 'Mobile-Former', 'MobileViT-S(baseline)', 'EdgeFormer-S (Ours)'] 6 | # the last two elements in each vector are manually set for normalizing 7 | Params = [5.5, 5.4 , 5.3 , 10 , 13.2, 7.3 , 9.5 , 6.0 , 7.8 , 9.4 , 5.6 , 5.0] 8 | Top1 = [74.5, 75.2 , 76.3 , 77.9, 75.1, 77.3 , 77.6 , 73.1 , 76.6 , 76.7 , 78.4 , 78.6] 9 | # normalization 10 | Params=np.array(Params) 11 | Top1 = np.array(Top1) 12 | 13 | plt.xticks(fontsize=14) 14 | plt.yticks(fontsize=14) 15 | 16 | # # using heat map to show model size 17 | # plt.scatter(Params, Top1, c=Params, s=Params*20, alpha=0.3, cmap='viridis', marker='d') 18 | # plt.ylim((74.0, 80.0)) 19 | # plt.xlim((4, 14)) 20 | # plt.ylabel('Top1 (%)') 21 | # plt.xlabel('# params. (M)') 22 | # plt.colorbar() 23 | # plt.savefig('./result/cls.png') 24 | # plt.show() 25 | 26 | # using heat map to show model size 27 | plt.scatter(Params, Top1, s=100, alpha=0.3, cmap='viridis', marker='d') 28 | plt.ylim((74.0, 80.0)) 29 | plt.xlim((4, 14)) 30 | plt.ylabel('Top1 (%)') 31 | plt.xlabel('# params. (M)') 32 | for x, y, label in zip(Params, Top1, Labels): 33 | plt.annotate(label, xy=(x-0.1,y+0.15)) 34 | 35 | plt.savefig('./result/cls.png') 36 | plt.show() 37 | 38 | 39 | print('done') 40 | -------------------------------------------------------------------------------- /drawing_results/det_scatter.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | 4 | 5 | Labels = ['MobileNetV1', 'MNASNet', 'MobileViT-XS', 'ResNet50', 'MobileViT-S (Baseline)', 'EdgeFormer-S (Ours)'] 6 | # the last two elements in each vector are manually set for normalizing 7 | Params = [5.1, 4.9, 2.7, 22.9, 5.7, 5.2] 8 | mAP = [22.2, 23.0, 24.8, 25.2, 27.7, 28.8] 9 | # normalization 10 | Params=np.array(Params) 11 | mAP = np.array(mAP) 12 | 13 | plt.xticks(fontsize=14) 14 | plt.yticks(fontsize=14) 15 | 16 | # # using heat map to show model size 17 | # plt.scatter(Params, mAP, c=Params, s=Params*20, alpha=0.3, cmap='viridis', marker='d') 18 | # plt.ylim((21.0, 30.0)) 19 | # plt.xlim((2.0, 24.0)) 20 | # plt.ylabel('mAP') 21 | # plt.xlabel('# params. (M)') 22 | # plt.colorbar() 23 | # plt.savefig('./result/det.png') 24 | # plt.show() 25 | 26 | plt.scatter(Params, mAP, s=100, alpha=0.3, cmap='viridis', marker='d') 27 | plt.ylim((21.0, 30.0)) 28 | plt.xlim((2.0, 26.0)) 29 | plt.ylabel('mAP') 30 | plt.xlabel('# params. (M)') 31 | for x, y, label in zip(Params, mAP, Labels): 32 | plt.annotate(label, xy=(x-0.1, y+0.22)) 33 | plt.savefig('./result/det.png') 34 | plt.show() 35 | 36 | print('done') 37 | -------------------------------------------------------------------------------- /drawing_results/result/cls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/drawing_results/result/cls.png -------------------------------------------------------------------------------- /drawing_results/result/det.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/drawing_results/result/det.png -------------------------------------------------------------------------------- /drawing_results/result/results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/drawing_results/result/results.png -------------------------------------------------------------------------------- /drawing_results/result/seg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/drawing_results/result/seg.png -------------------------------------------------------------------------------- /drawing_results/seg_scatter.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | 4 | 5 | Labels = ['MobileNetV1', 'MobileNetV2', 'MobileViT-S' , 'MobileViT-S (baseline)', 'EdgeFormer-S (Ours)'] 6 | Params = [11.2, 4.5, 2.9, 6.4, 5.8] 7 | mIOU = [75.3, 75.7, 77.1, 79.1, 79.7] 8 | 9 | Params=np.array(Params) 10 | mIOU = np.array(mIOU) 11 | 12 | plt.xticks(fontsize=14) 13 | plt.yticks(fontsize=14) 14 | 15 | # # using heat map to show model size 16 | # plt.scatter(Params, mIOU, c=Params, s=Params*20, alpha=0.3, cmap='viridis', marker='d') 17 | # plt.ylim((75.0, 80.5)) 18 | # plt.ylabel('mIOU') 19 | # plt.xlabel('# params. (M)') 20 | # plt.colorbar() 21 | # plt.savefig('./result/seg.png') 22 | # plt.show() 23 | 24 | plt.scatter(Params, mIOU, s=100, alpha=0.3, cmap='viridis', marker='d') 25 | plt.ylim((75.0, 80.5)) 26 | plt.xlim(2.5, 13.0) 27 | plt.ylabel('mIOU') 28 | plt.xlabel('# params. (M)') 29 | for x, y, label in zip(Params, mIOU, Labels): 30 | plt.annotate(label, xy=(x-0.1,y+0.15)) 31 | plt.savefig('./result/seg.png') 32 | plt.show() 33 | 34 | print('done') 35 | -------------------------------------------------------------------------------- /engine/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | from .training_engine import Trainer 7 | from .evaluation_engine import Evaluator -------------------------------------------------------------------------------- /engine/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/engine/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /engine/__pycache__/evaluation_engine.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/engine/__pycache__/evaluation_engine.cpython-38.pyc -------------------------------------------------------------------------------- /engine/__pycache__/training_engine.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/engine/__pycache__/training_engine.cpython-38.pyc -------------------------------------------------------------------------------- /engine/__pycache__/utils.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/engine/__pycache__/utils.cpython-38.pyc -------------------------------------------------------------------------------- /engine/detection_utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/engine/detection_utils/__init__.py -------------------------------------------------------------------------------- /engine/detection_utils/coco_map.py: -------------------------------------------------------------------------------- 1 | 2 | import json 3 | import os 4 | import numpy as np 5 | from pycocotools.cocoeval import COCOeval 6 | from pycocotools.coco import COCO 7 | from typing import Optional, List 8 | 9 | 10 | def coco_evaluation(opts, 11 | predictions: List[np.ndarray], 12 | output_dir: Optional[str] = "coco_eval_results", 13 | split: Optional[str] = 'val', 14 | year: Optional[int] = 2017, 15 | iou_type: Optional[str] = "bbox") -> None: 16 | coco_results = [] 17 | root = getattr(opts, "dataset.root_val", None) 18 | ann_file = os.path.join(root, 'annotations/instances_{}{}.json'.format(split, year)) 19 | coco = COCO(ann_file) 20 | 21 | coco_categories = sorted(coco.getCatIds()) 22 | coco_id_to_contiguous_id = {coco_id: i + 1 for i, coco_id in enumerate(coco_categories)} 23 | contiguous_id_to_coco_id = {v: k for k, v in coco_id_to_contiguous_id.items()} 24 | 25 | ids = list(coco.imgs.keys()) 26 | 27 | for i, (img_idx, boxes, labels, scores) in enumerate(predictions): 28 | image_id = ids[img_idx] 29 | if labels.shape[0] == 0: 30 | continue 31 | 32 | boxes = boxes.tolist() 33 | labels = labels.tolist() 34 | scores = scores.tolist() 35 | coco_results.extend( 36 | [ 37 | { 38 | "image_id": image_id, 39 | "category_id": contiguous_id_to_coco_id[labels[k]], 40 | "bbox": [box[0], box[1], box[2] - box[0], box[3] - box[1]], # to xywh format 41 | "score": scores[k], 42 | } 43 | for k, box in enumerate(boxes) 44 | ] 45 | ) 46 | 47 | if not os.path.isdir(output_dir): 48 | os.makedirs(output_dir) 49 | 50 | json_result_file = os.path.join(output_dir, iou_type + ".json") 51 | 52 | if os.path.isfile(json_result_file): 53 | # delete the json file if it exists 54 | os.remove(json_result_file) 55 | 56 | with open(json_result_file, "w") as f: 57 | # write results to the JSON file 58 | json.dump(coco_results, f) 59 | 60 | # Run COCO evaluation 61 | coco_dt = coco.loadRes(json_result_file) 62 | coco_eval = COCOeval(coco, coco_dt, iou_type) 63 | coco_eval.evaluate() 64 | coco_eval.accumulate() 65 | coco_eval.summarize() 66 | 67 | 68 | def compute_quant_scores(opts, 69 | predictions: List, 70 | output_dir: Optional[str] = "coco_eval_results", *args, **kwargs) -> None: 71 | dataset_name = getattr(opts, "dataset.name", None) 72 | if dataset_name.find("coco") > -1: 73 | coco_evaluation(opts=opts, predictions=predictions, output_dir=output_dir) 74 | else: 75 | raise NotImplementedError -------------------------------------------------------------------------------- /engine/segmentation_utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/engine/segmentation_utils/__init__.py -------------------------------------------------------------------------------- /engine/segmentation_utils/cityscapes_iou.py: -------------------------------------------------------------------------------- 1 | 2 | import cityscapesscripts.evaluation.evalPixelLevelSemanticLabeling as cityscapes_semseg_eval 3 | import os 4 | import glob 5 | 6 | from utils import logger 7 | 8 | 9 | def eval_cityscapes(pred_dir: str, gt_dir: str) -> None: 10 | cityscapes_semseg_eval.args.predictionPath = pred_dir 11 | cityscapes_semseg_eval.args.predictionWalk = None 12 | cityscapes_semseg_eval.args.JSONOutput = False 13 | cityscapes_semseg_eval.args.colorized = False 14 | 15 | gt_img_list = glob.glob(os.path.join(gt_dir, "*", "*_gtFine_labelIds.png")) 16 | if len(gt_img_list) == 0: 17 | logger.error("Cannot find ground truth images at: {}".format(gt_dir)) 18 | 19 | pred_img_list = [] 20 | for gt in gt_img_list: 21 | pred_img_list.append(cityscapes_semseg_eval.getPrediction(cityscapes_semseg_eval.args, gt)) 22 | 23 | results = cityscapes_semseg_eval.evaluateImgLists( 24 | pred_img_list, gt_img_list, cityscapes_semseg_eval.args 25 | ) 26 | 27 | logger.info("Evaluation results summary") 28 | eval_res_str = "\n\t IoU_cls: {:.2f} \n\t iIOU_cls: {:.2f} \n\t IoU_cat: {:.2f} \n\t iIOU_cat: {:.2f}".format( 29 | 100.0 * results["averageScoreClasses"], 30 | 100.0 * results["averageScoreInstClasses"], 31 | 100.0 * results["averageScoreCategories"], 32 | 100.0 * results["averageScoreInstCategories"] 33 | ) 34 | print(eval_res_str) 35 | -------------------------------------------------------------------------------- /engine/utils.py: -------------------------------------------------------------------------------- 1 | from utils import logger 2 | import torch 3 | from typing import Optional 4 | import gc 5 | 6 | from utils.ddp_utils import is_master 7 | from utils.tensor_utils import create_rand_tensor 8 | 9 | 10 | def print_summary(opts, model, criteria: Optional = None, optimizer: Optional = None, scheduler: Optional = None): 11 | if is_master(opts): 12 | logger.log(logger.color_text('Model')) 13 | print(model) 14 | dev = getattr(opts, "dev.device", torch.device("cpu")) 15 | try: 16 | inp_tensor = create_rand_tensor(opts, device=dev) 17 | 18 | if hasattr(model, 'module'): 19 | model.module.profile_model(inp_tensor) 20 | else: 21 | model.profile_model(inp_tensor) 22 | del inp_tensor 23 | except Exception as e: 24 | pass 25 | 26 | if criteria is not None: 27 | # print criteria 28 | logger.log(logger.color_text('Loss function')) 29 | print('{}'.format(criteria)) 30 | 31 | if optimizer is not None: 32 | logger.log(logger.color_text('Optimizer')) 33 | print('{}'.format(optimizer)) 34 | 35 | if scheduler is not None: 36 | logger.log(logger.color_text('Learning rate scheduler')) 37 | print('{}'.format(scheduler)) 38 | 39 | gc.collect() 40 | -------------------------------------------------------------------------------- /eval_cls.py: -------------------------------------------------------------------------------- 1 | 2 | import torch 3 | import multiprocessing 4 | 5 | from cvnets import get_model 6 | from data import create_eval_loader 7 | from engine import Evaluator 8 | 9 | from options.opts import get_eval_arguments 10 | from utils import logger 11 | from utils.common_utils import device_setup, create_directories 12 | from utils.ddp_utils import is_master, distributed_init 13 | 14 | 15 | def main(opts, **kwargs): 16 | num_gpus = getattr(opts, "dev.num_gpus", 0) # defaults are for CPU 17 | dev_id = getattr(opts, "dev.device_id", torch.device('cpu')) 18 | device = getattr(opts, "dev.device", torch.device('cpu')) 19 | is_distributed = getattr(opts, "ddp.use_distributed", False) 20 | 21 | # set-up data loaders 22 | val_loader = create_eval_loader(opts) 23 | 24 | # set-up the model 25 | model = get_model(opts) 26 | 27 | is_master_node = is_master(opts) 28 | if num_gpus <= 1: 29 | model = model.to(device=device) 30 | elif is_distributed: 31 | model = model.to(device=device) 32 | model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[dev_id], output_device=dev_id) 33 | if is_master_node: 34 | logger.log('Using DistributedDataParallel for evaluation') 35 | else: 36 | model = torch.nn.DataParallel(model) 37 | model = model.to(device=device) 38 | if is_master_node: 39 | logger.log('Using DataParallel for evaluation') 40 | 41 | eval_engine = Evaluator(opts=opts, model=model, eval_loader=val_loader) 42 | eval_engine.run() 43 | 44 | def main_worker_classification(**kwargs): 45 | opts = get_eval_arguments() 46 | print(opts) 47 | # device set-up 48 | opts = device_setup(opts) 49 | 50 | node_rank = getattr(opts, "ddp.rank", 0) 51 | if node_rank < 0: 52 | logger.error('--rank should be >=0. Got {}'.format(node_rank)) 53 | 54 | is_master_node = is_master(opts) 55 | 56 | # create the directory for saving results 57 | save_dir = getattr(opts, "common.results_loc", "results") 58 | run_label = getattr(opts, "common.run_label", "run_1") 59 | exp_dir = '{}/{}'.format(save_dir, run_label) 60 | setattr(opts, "common.exp_loc", exp_dir) 61 | create_directories(dir_path=exp_dir, is_master_node=is_master_node) 62 | 63 | world_size = getattr(opts, "ddp.world_size", 1) 64 | num_gpus = 1 65 | 66 | 67 | # No of data workers = no of CPUs (if not specified or -1) 68 | n_cpus = multiprocessing.cpu_count() 69 | dataset_workers = getattr(opts, "dataset.workers", -1) 70 | 71 | 72 | if dataset_workers == -1: 73 | setattr(opts, "dataset.workers", n_cpus) 74 | 75 | # adjust the batch size 76 | train_bsize = getattr(opts, "dataset.train_batch_size0", 32) * max(1, num_gpus) 77 | val_bsize = getattr(opts, "dataset.val_batch_size0", 32) * max(1, num_gpus) 78 | setattr(opts, "dataset.train_batch_size0", train_bsize) 79 | setattr(opts, "dataset.val_batch_size0", val_bsize) 80 | setattr(opts, "dev.device_id", None) 81 | main(opts=opts, **kwargs) 82 | 83 | 84 | if __name__ == "__main__": 85 | import os 86 | 87 | os.environ['CUDA_VISIBLE_DEVICES'] = '0' 88 | main_worker_classification() 89 | -------------------------------------------------------------------------------- /eval_det.py: -------------------------------------------------------------------------------- 1 | # detection infe 2 | 3 | def main_worker_detection(**kwargs): 4 | from engine.eval_detection import main_detection_evaluation 5 | main_detection_evaluation(**kwargs) 6 | 7 | 8 | if __name__ == "__main__": 9 | import os 10 | os.environ['CUDA_VISIBLE_DEVICES'] = '0' 11 | main_worker_detection() 12 | -------------------------------------------------------------------------------- /eval_seg.py: -------------------------------------------------------------------------------- 1 | # segmentation infe 2 | 3 | def main_worker_segmentation(**kwargs): 4 | from engine.eval_segmentation import main_segmentation_evaluation 5 | main_segmentation_evaluation(**kwargs) 6 | 7 | 8 | if __name__ == "__main__": 9 | import os 10 | 11 | os.environ['CUDA_VISIBLE_DEVICES'] = '0' 12 | main_worker_segmentation() 13 | 14 | -------------------------------------------------------------------------------- /loss_fn/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | from .base_criteria import BaseCriteria 7 | import os 8 | import importlib 9 | from utils import logger 10 | import argparse 11 | 12 | LOSS_REGISTRY = {} 13 | 14 | 15 | def register_loss_fn(name): 16 | def register_loss_fn_class(cls): 17 | if name in LOSS_REGISTRY: 18 | raise ValueError("Cannot register duplicate loss function ({})".format(name)) 19 | 20 | if not issubclass(cls, BaseCriteria): 21 | raise ValueError( 22 | "Criteria ({}: {}) must extend BaseCriteria".format(name, cls.__name__) 23 | ) 24 | 25 | LOSS_REGISTRY[name] = cls 26 | return cls 27 | 28 | return register_loss_fn_class 29 | 30 | 31 | def build_loss_fn(opts): 32 | loss_fn_category = getattr(opts, "loss.category", "classification").lower() 33 | loss_fn = None 34 | if loss_fn_category in LOSS_REGISTRY: 35 | loss_fn = LOSS_REGISTRY[loss_fn_category](opts) 36 | else: 37 | temp_list = list(LOSS_REGISTRY.keys()) 38 | temp_str = "Loss function ({}) not yet supported. \n Supported loss functions are:".format(loss_fn_category) 39 | for i, m_name in enumerate(temp_list): 40 | temp_str += "\n\t {}: {}".format(i, logger.color_text(m_name)) 41 | logger.error(temp_str) 42 | 43 | return loss_fn 44 | 45 | 46 | def general_loss_fn_args(parser: argparse.ArgumentParser): 47 | group = parser.add_argument_group(title="Loss function arguments", description="Loss function arguments") 48 | 49 | group.add_argument("--loss.category", type=str, default="classification", 50 | help="Loss function category (classification,segmentation)") 51 | group.add_argument("--loss.ignore-idx", type=int, default=-1, help="Ignore idx in loss function") 52 | 53 | return parser 54 | 55 | 56 | def arguments_loss_fn(parser: argparse.ArgumentParser): 57 | parser = general_loss_fn_args(parser=parser) 58 | 59 | # add loss function specific arguments 60 | for k, v in LOSS_REGISTRY.items(): 61 | parser = v.add_arguments(parser=parser) 62 | return parser 63 | 64 | 65 | # automatically import the loss functions 66 | loss_fn_dir = os.path.dirname(__file__) 67 | for file in os.listdir(loss_fn_dir): 68 | path = os.path.join(loss_fn_dir, file) 69 | if ( 70 | not file.startswith("_") 71 | and not file.startswith(".") 72 | and (file.endswith(".py") or os.path.isdir(path)) 73 | ): 74 | loss_fn_name = file[: file.find(".py")] if file.endswith(".py") else file 75 | module = importlib.import_module("loss_fn." + loss_fn_name) 76 | -------------------------------------------------------------------------------- /loss_fn/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/loss_fn/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /loss_fn/__pycache__/base_criteria.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/loss_fn/__pycache__/base_criteria.cpython-38.pyc -------------------------------------------------------------------------------- /loss_fn/__pycache__/classification.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/loss_fn/__pycache__/classification.cpython-38.pyc -------------------------------------------------------------------------------- /loss_fn/__pycache__/detection.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/loss_fn/__pycache__/detection.cpython-38.pyc -------------------------------------------------------------------------------- /loss_fn/__pycache__/distillation.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/loss_fn/__pycache__/distillation.cpython-38.pyc -------------------------------------------------------------------------------- /loss_fn/__pycache__/segmentation.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/loss_fn/__pycache__/segmentation.cpython-38.pyc -------------------------------------------------------------------------------- /loss_fn/base_criteria.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import torch 7 | from torch import nn, Tensor 8 | import argparse 9 | from typing import Any 10 | 11 | 12 | class BaseCriteria(nn.Module): 13 | def __init__(self, *args, **kwargs): 14 | super(BaseCriteria, self).__init__() 15 | self.eps = 1e-7 16 | 17 | @classmethod 18 | def add_arguments(cls, parser: argparse.ArgumentParser): 19 | return parser 20 | 21 | def forward(self, input_sample: Tensor, prediction: Any, target: Tensor) -> Tensor: 22 | raise NotImplementedError 23 | 24 | @staticmethod 25 | def _class_weights(target: Tensor, n_classes: int, norm_val: float = 1.1) -> Tensor: 26 | class_hist: Tensor = torch.histc(target.float(), bins=n_classes, min=0, max=n_classes - 1) 27 | mask_indices = (class_hist == 0) 28 | 29 | # normalize between 0 and 1 by dividing by the sum 30 | norm_hist = torch.div(class_hist, class_hist.sum()) 31 | norm_hist = torch.add(norm_hist, norm_val) 32 | 33 | # compute class weights.. 34 | # samples with more frequency will have less weight and vice-versa 35 | class_wts = torch.div(torch.ones_like(class_hist), torch.log(norm_hist)) 36 | 37 | # mask the classes which do not have samples in the current batch 38 | class_wts[mask_indices] = 0.0 39 | 40 | return class_wts.to(device=target.device) 41 | 42 | def __repr__(self): 43 | return '{}'.format(self.__class__.__name__) -------------------------------------------------------------------------------- /loss_fn/classification.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | from torch import Tensor 7 | import argparse 8 | from utils import logger 9 | 10 | from . import BaseCriteria, register_loss_fn 11 | from .classification_loss_fns import ClsCrossEntropy, LabelSmoothing, SUPPORTED_CLS_LOSS_FNS 12 | 13 | 14 | @register_loss_fn("classification") 15 | class ClassificationLoss(BaseCriteria): 16 | def __init__(self, opts): 17 | loss_fn_name = getattr(opts, "loss.classification.name", "cross_entropy") 18 | super(ClassificationLoss, self).__init__() 19 | 20 | if loss_fn_name == "cross_entropy": 21 | self.criteria = ClsCrossEntropy(opts=opts) 22 | elif loss_fn_name == "label_smoothing": 23 | self.criteria = LabelSmoothing(opts=opts) 24 | else: 25 | temp_str = "Loss function ({}) not yet supported. " \ 26 | "\n Supported classification loss functions are:".format(loss_fn_name) 27 | for i, m_name in enumerate(SUPPORTED_CLS_LOSS_FNS): 28 | temp_str += "\n\t {}: {}".format(i, logger.color_text(m_name)) 29 | logger.error(temp_str) 30 | 31 | def forward(self, input_sample: Tensor, prediction: Tensor, target: Tensor) -> Tensor: 32 | return self.criteria( 33 | input_sample=input_sample, 34 | prediction=prediction, 35 | target=target 36 | ) 37 | 38 | @classmethod 39 | def add_arguments(cls, parser: argparse.ArgumentParser): 40 | group = parser.add_argument_group(title="".format(cls.__name__), description="".format(cls.__name__)) 41 | group.add_argument("--loss.classification.name", type=str, default="cross_entropy", help="Loss function name") 42 | parser = ClsCrossEntropy.add_arguments(parser=parser) 43 | parser = LabelSmoothing.add_arguments(parser=parser) 44 | return parser 45 | 46 | def __repr__(self): 47 | return self.criteria.__repr__() 48 | 49 | -------------------------------------------------------------------------------- /loss_fn/classification_loss_fns/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import importlib 7 | import os 8 | 9 | SUPPORTED_CLS_LOSS_FNS = [] 10 | 11 | 12 | def register_classification_loss_fn(name): 13 | def register_fn(fn): 14 | if name in SUPPORTED_CLS_LOSS_FNS: 15 | raise ValueError("Cannot register duplicate classification loss function ({})".format(name)) 16 | SUPPORTED_CLS_LOSS_FNS.append(name) 17 | return fn 18 | return register_fn 19 | 20 | 21 | # automatically import different loss functions 22 | loss_fn_dir = os.path.dirname(__file__) 23 | for file in os.listdir(loss_fn_dir): 24 | path = os.path.join(loss_fn_dir, file) 25 | if ( 26 | not file.startswith("_") 27 | and not file.startswith(".") 28 | and (file.endswith(".py") or os.path.isdir(path)) 29 | ): 30 | model_name = file[: file.find(".py")] if file.endswith(".py") else file 31 | module = importlib.import_module("loss_fn.classification_loss_fns." + model_name) 32 | 33 | 34 | # import these after loading loss_fn names to avoid looping 35 | from loss_fn.classification_loss_fns.cross_entropy import ClsCrossEntropy 36 | from loss_fn.classification_loss_fns.label_smoothing import LabelSmoothing -------------------------------------------------------------------------------- /loss_fn/classification_loss_fns/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/loss_fn/classification_loss_fns/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /loss_fn/classification_loss_fns/__pycache__/cross_entropy.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/loss_fn/classification_loss_fns/__pycache__/cross_entropy.cpython-38.pyc -------------------------------------------------------------------------------- /loss_fn/classification_loss_fns/__pycache__/label_smoothing.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/loss_fn/classification_loss_fns/__pycache__/label_smoothing.cpython-38.pyc -------------------------------------------------------------------------------- /loss_fn/classification_loss_fns/cross_entropy.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | from torch.nn import functional as F 7 | from torch import Tensor 8 | import argparse 9 | 10 | from . import register_classification_loss_fn 11 | from .. import BaseCriteria 12 | 13 | 14 | @register_classification_loss_fn(name="cross_entropy") 15 | class ClsCrossEntropy(BaseCriteria): 16 | def __init__(self, opts): 17 | ignore_idx = getattr(opts, "loss.ignore_idx", -1) 18 | use_class_wts = getattr(opts, "loss.classification.cross_entropy_class_weights", False) 19 | super(ClsCrossEntropy, self).__init__() 20 | 21 | self.ignore_idx = ignore_idx 22 | self.use_class_wts = use_class_wts 23 | 24 | def forward(self, input_sample: Tensor, prediction: Tensor, target: Tensor) -> Tensor: 25 | weight = None 26 | if self.use_class_wts and self.training: 27 | n_classes = prediction.size(1) 28 | weight = self._class_weights(target=target, n_classes=n_classes) 29 | return F.cross_entropy(input=prediction, target=target, weight=weight, ignore_index=self.ignore_idx) 30 | 31 | @classmethod 32 | def add_arguments(cls, parser: argparse.ArgumentParser): 33 | group = parser.add_argument_group(title="".format(cls.__name__), description="".format(cls.__name__)) 34 | group.add_argument("--loss.classification.cross-entropy-class-weights", action="store_true", 35 | help="Use class weights in loss function") 36 | return parser 37 | 38 | def __repr__(self): 39 | return "{}(\n\t ignore_idx={} \n\t class_wts={}\n)".format( 40 | self.__class__.__name__, 41 | self.ignore_idx, 42 | self.use_class_wts 43 | ) 44 | -------------------------------------------------------------------------------- /loss_fn/detection.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | from torch import Tensor 7 | import argparse 8 | from utils import logger 9 | from typing import Union 10 | 11 | from . import BaseCriteria, register_loss_fn 12 | from .detection_loss_fns import SSDLoss, SUPPORTED_DETECTION_LOSS_FNS 13 | 14 | 15 | @register_loss_fn("detection") 16 | class DetectionLoss(BaseCriteria): 17 | def __init__(self, opts): 18 | loss_fn_name = getattr(opts, "loss.detection.name", "cross_entropy") 19 | super(DetectionLoss, self).__init__() 20 | if loss_fn_name == "ssd_multibox_loss": 21 | self.criteria = SSDLoss(opts=opts) 22 | else: 23 | temp_str = "Loss function ({}) not yet supported. " \ 24 | "\n Supported detection loss functions are:".format(loss_fn_name) 25 | for i, m_name in enumerate(SUPPORTED_DETECTION_LOSS_FNS): 26 | temp_str += "\n\t {}: {}".format(i, logger.color_text(m_name)) 27 | logger.error(temp_str) 28 | 29 | @classmethod 30 | def add_arguments(cls, parser: argparse.ArgumentParser): 31 | group = parser.add_argument_group(title="".format(cls.__name__), description="".format(cls.__name__)) 32 | group.add_argument("--loss.detection.name", type=str, default="cross_entropy", 33 | help="Segmentation loss function name") 34 | parser = SSDLoss.add_arguments(parser=parser) 35 | return parser 36 | 37 | def forward(self, 38 | input_sample: Tensor, 39 | prediction: Union[Tensor, Union[Tensor, Tensor]], 40 | target: Tensor) -> Tensor: 41 | return self.criteria( 42 | input_sample=input_sample, 43 | prediction=prediction, 44 | target=target 45 | ) 46 | 47 | def __repr__(self): 48 | return self.criteria.__repr__() 49 | -------------------------------------------------------------------------------- /loss_fn/detection_loss_fns/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import importlib 7 | import os 8 | 9 | SUPPORTED_DETECTION_LOSS_FNS = [] 10 | 11 | 12 | def register_detection_loss_fn(name): 13 | def register_fn(fn): 14 | if name in SUPPORTED_DETECTION_LOSS_FNS: 15 | raise ValueError("Cannot register duplicate detection loss function ({})".format(name)) 16 | SUPPORTED_DETECTION_LOSS_FNS.append(name) 17 | return fn 18 | return register_fn 19 | 20 | 21 | # automatically import different loss functions 22 | loss_fn_dir = os.path.dirname(__file__) 23 | for file in os.listdir(loss_fn_dir): 24 | path = os.path.join(loss_fn_dir, file) 25 | if ( 26 | not file.startswith("_") 27 | and not file.startswith(".") 28 | and (file.endswith(".py") or os.path.isdir(path)) 29 | ): 30 | model_name = file[: file.find(".py")] if file.endswith(".py") else file 31 | module = importlib.import_module("loss_fn.detection_loss_fns." + model_name) 32 | 33 | 34 | # import these after loading loss_fn names to avoid looping 35 | from loss_fn.detection_loss_fns.ssd_multibox_loss import SSDLoss -------------------------------------------------------------------------------- /loss_fn/detection_loss_fns/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/loss_fn/detection_loss_fns/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /loss_fn/detection_loss_fns/__pycache__/ssd_multibox_loss.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/loss_fn/detection_loss_fns/__pycache__/ssd_multibox_loss.cpython-38.pyc -------------------------------------------------------------------------------- /loss_fn/distillation.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | from torch import Tensor 7 | import argparse 8 | from utils import logger 9 | 10 | from . import BaseCriteria, register_loss_fn 11 | from .distillation_loss_fns import VanillaDistillationLoss, SUPPORTED_DISTILL_LOSS_FNS 12 | 13 | 14 | @register_loss_fn("distillation") 15 | class DistillationLoss(BaseCriteria): 16 | def __init__(self, opts): 17 | loss_fn_name = getattr(opts, "loss.distillation.name", "vanilla") 18 | super(DistillationLoss, self).__init__() 19 | if loss_fn_name == "vanilla": 20 | self.criteria = VanillaDistillationLoss(opts=opts) 21 | else: 22 | temp_str = "Loss function ({}) not yet supported. " \ 23 | "\n Supported distillation loss functions are:".format(loss_fn_name) 24 | for i, m_name in enumerate(SUPPORTED_DISTILL_LOSS_FNS): 25 | temp_str += "\n\t {}: {}".format(i, logger.color_text(m_name)) 26 | logger.error(temp_str) 27 | 28 | def forward(self, input_sample: Tensor, prediction: Tensor, target: Tensor) -> Tensor: 29 | return self.criteria( 30 | input_sample=input_sample, 31 | prediction=prediction, 32 | target=target 33 | ) 34 | 35 | @classmethod 36 | def add_arguments(cls, parser: argparse.ArgumentParser): 37 | group = parser.add_argument_group(title="".format(cls.__name__), description="".format(cls.__name__)) 38 | group.add_argument("--loss.distillation.name", type=str, default="vanilla", 39 | help="Distillation loss function name") 40 | parser = VanillaDistillationLoss.add_arguments(parser=parser) 41 | return parser 42 | 43 | def __repr__(self): 44 | return self.criteria.__repr__() 45 | -------------------------------------------------------------------------------- /loss_fn/distillation_loss_fns/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import os 7 | import importlib 8 | 9 | SUPPORTED_DISTILL_LOSS_FNS = [] 10 | 11 | 12 | def register_distillation_loss_fn(name): 13 | def register_fn(fn): 14 | if name in SUPPORTED_DISTILL_LOSS_FNS: 15 | raise ValueError("Cannot register duplicate distillation loss function ({})".format(name)) 16 | SUPPORTED_DISTILL_LOSS_FNS.append(name) 17 | return fn 18 | return register_fn 19 | 20 | 21 | # automatically import different loss functions 22 | loss_fn_dir = os.path.dirname(__file__) 23 | for file in os.listdir(loss_fn_dir): 24 | path = os.path.join(loss_fn_dir, file) 25 | if ( 26 | not file.startswith("_") 27 | and not file.startswith(".") 28 | and (file.endswith(".py") or os.path.isdir(path)) 29 | ): 30 | model_name = file[: file.find(".py")] if file.endswith(".py") else file 31 | module = importlib.import_module("loss_fn.distillation_loss_fns." + model_name) 32 | 33 | 34 | from loss_fn.distillation_loss_fns.vanilla import VanillaDistillationLoss -------------------------------------------------------------------------------- /loss_fn/distillation_loss_fns/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/loss_fn/distillation_loss_fns/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /loss_fn/distillation_loss_fns/__pycache__/vanilla.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/loss_fn/distillation_loss_fns/__pycache__/vanilla.cpython-38.pyc -------------------------------------------------------------------------------- /loss_fn/segmentation.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | from torch import Tensor 7 | import argparse 8 | from utils import logger 9 | from typing import Union, Tuple 10 | 11 | from . import BaseCriteria, register_loss_fn 12 | from .segmentation_loss_fns import SegCrossEntropy, SUPPORTED_SEG_LOSS_FNS 13 | 14 | 15 | @register_loss_fn("segmentation") 16 | class SegmentationLoss(BaseCriteria): 17 | def __init__(self, opts): 18 | loss_fn_name = getattr(opts, "loss.segmentation.name", "cross_entropy") 19 | super(SegmentationLoss, self).__init__() 20 | if loss_fn_name == "cross_entropy": 21 | self.criteria = SegCrossEntropy(opts=opts) 22 | else: 23 | temp_str = "Loss function ({}) not yet supported. " \ 24 | "\n Supported segmentation loss functions are:".format(loss_fn_name) 25 | for i, m_name in enumerate(SUPPORTED_SEG_LOSS_FNS): 26 | temp_str += "\n\t {}: {}".format(i, logger.color_text(m_name)) 27 | logger.error(temp_str) 28 | 29 | @classmethod 30 | def add_arguments(cls, parser: argparse.ArgumentParser): 31 | group = parser.add_argument_group(title="".format(cls.__name__), description="".format(cls.__name__)) 32 | group.add_argument("--loss.segmentation.name", type=str, default="cross_entropy", 33 | help="Segmentation loss function name") 34 | parser = SegCrossEntropy.add_arguments(parser=parser) 35 | return parser 36 | 37 | def forward(self, input_sample: Tensor, prediction: Union[Tensor, Tuple[Tensor, Tensor]], target: Tensor) -> Tensor: 38 | return self.criteria( 39 | input_sample=input_sample, 40 | prediction=prediction, 41 | target=target 42 | ) 43 | 44 | def __repr__(self): 45 | return self.criteria.__repr__() 46 | -------------------------------------------------------------------------------- /loss_fn/segmentation_loss_fns/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import os 7 | import importlib 8 | 9 | SUPPORTED_SEG_LOSS_FNS = [] 10 | 11 | 12 | def register_segmentation_loss_fn(name): 13 | def register_fn(fn): 14 | if name in SUPPORTED_SEG_LOSS_FNS: 15 | raise ValueError("Cannot register duplicate segmentation loss function ({})".format(name)) 16 | SUPPORTED_SEG_LOSS_FNS.append(name) 17 | return fn 18 | return register_fn 19 | 20 | 21 | # automatically import different loss functions 22 | loss_fn_dir = os.path.dirname(__file__) 23 | for file in os.listdir(loss_fn_dir): 24 | path = os.path.join(loss_fn_dir, file) 25 | if ( 26 | not file.startswith("_") 27 | and not file.startswith(".") 28 | and (file.endswith(".py") or os.path.isdir(path)) 29 | ): 30 | model_name = file[: file.find(".py")] if file.endswith(".py") else file 31 | module = importlib.import_module("loss_fn.segmentation_loss_fns." + model_name) 32 | 33 | 34 | from loss_fn.segmentation_loss_fns.cross_entropy import SegCrossEntropy -------------------------------------------------------------------------------- /loss_fn/segmentation_loss_fns/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/loss_fn/segmentation_loss_fns/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /loss_fn/segmentation_loss_fns/__pycache__/cross_entropy.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/loss_fn/segmentation_loss_fns/__pycache__/cross_entropy.cpython-38.pyc -------------------------------------------------------------------------------- /metrics/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import os 7 | import importlib 8 | import argparse 9 | 10 | SUPPORTED_STATS = ['loss'] 11 | 12 | 13 | def register_stats_fn(name): 14 | def register_fn(fn): 15 | if name in SUPPORTED_STATS: 16 | raise ValueError("Cannot register duplicate state ({})".format(name)) 17 | SUPPORTED_STATS.append(name) 18 | return fn 19 | return register_fn 20 | 21 | 22 | def arguments_stats(parser: argparse.ArgumentParser): 23 | group = parser.add_argument_group(title="Statistics", description="Statistics") 24 | group.add_argument('--stats.name', type=str, default=['loss'], nargs="+", help="Name of statistics") 25 | group.add_argument('--stats.checkpoint-metric', type=str, default=None, help="Metric to use for saving checkpoints") 26 | group.add_argument('--stats.checkpoint-metric-max', type=str, default=None, 27 | help="Maximize checkpoint metric") 28 | 29 | return parser 30 | 31 | 32 | # automatically import different metrics 33 | metrics_dir = os.path.dirname(__file__) 34 | for file in os.listdir(metrics_dir): 35 | path = os.path.join(metrics_dir, file) 36 | if ( 37 | not file.startswith("_") 38 | and not file.startswith(".") 39 | and (file.endswith(".py") or os.path.isdir(path)) 40 | ): 41 | model_name = file[: file.find(".py")] if file.endswith(".py") else file 42 | module = importlib.import_module("metrics." + model_name) 43 | 44 | 45 | from metrics.stats import Statistics 46 | from metrics.metric_monitor import metric_monitor -------------------------------------------------------------------------------- /metrics/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/metrics/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /metrics/__pycache__/intersection_over_union.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/metrics/__pycache__/intersection_over_union.cpython-38.pyc -------------------------------------------------------------------------------- /metrics/__pycache__/metric_monitor.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/metrics/__pycache__/metric_monitor.cpython-38.pyc -------------------------------------------------------------------------------- /metrics/__pycache__/stats.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/metrics/__pycache__/stats.cpython-38.pyc -------------------------------------------------------------------------------- /metrics/__pycache__/topk_accuracy.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/metrics/__pycache__/topk_accuracy.cpython-38.pyc -------------------------------------------------------------------------------- /metrics/intersection_over_union.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import torch 7 | from torch import Tensor 8 | from typing import Optional, Tuple, Union 9 | 10 | from . import register_stats_fn 11 | 12 | 13 | @register_stats_fn(name="iou") 14 | def compute_miou_batch(prediction: Union[Tuple[Tensor, Tensor], Tensor], target: Tensor, epsilon: Optional[float] = 1e-7): 15 | if isinstance(prediction, Tuple) and len(prediction) == 2: 16 | mask = prediction[0] 17 | assert isinstance(mask, Tensor) 18 | elif isinstance(prediction, Tensor): 19 | mask = prediction 20 | assert isinstance(mask, Tensor) 21 | else: 22 | raise NotImplementedError( 23 | "For computing loss for segmentation task, we need prediction to be an instance of Tuple or Tensor") 24 | 25 | num_classes = mask.shape[1] 26 | pred_mask = torch.max(mask, dim=1)[1] 27 | assert pred_mask.dim() == 3, "Predicted mask tensor should be 3-dimensional (B x H x W)" 28 | 29 | pred_mask = pred_mask.byte() 30 | target = target.byte() 31 | 32 | # shift by 1 so that 255 is 0 33 | pred_mask += 1 34 | target += 1 35 | 36 | pred_mask = pred_mask * (target > 0) 37 | inter = pred_mask * (pred_mask == target) 38 | area_inter = torch.histc(inter.float(), bins=num_classes, min=1, max=num_classes) 39 | area_pred = torch.histc(pred_mask.float(), bins=num_classes, min=1, max=num_classes) 40 | area_mask = torch.histc(target.float(), bins=num_classes, min=1, max=num_classes) 41 | area_union = area_pred + area_mask - area_inter + epsilon 42 | return area_inter, area_union 43 | -------------------------------------------------------------------------------- /metrics/metric_monitor.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | from typing import Optional, Tuple 7 | from torch import Tensor 8 | 9 | from utils.tensor_utils import tensor_to_python_float 10 | 11 | from .topk_accuracy import top_k_accuracy 12 | from .intersection_over_union import compute_miou_batch 13 | 14 | 15 | def metric_monitor(pred_label: Tensor or Tuple[Tensor], target_label: Tensor, loss: Tensor or float, metric_names: list, 16 | use_distributed: Optional[bool] = False): 17 | metric_vals = dict() 18 | if "loss" in metric_names: 19 | loss = tensor_to_python_float(loss, is_distributed=use_distributed) 20 | metric_vals['loss'] = loss 21 | 22 | if "top1" in metric_names: 23 | top_1_acc, top_5_acc = top_k_accuracy(pred_label, target_label, top_k=(1, 5)) 24 | top_1_acc = tensor_to_python_float(top_1_acc, is_distributed=use_distributed) 25 | metric_vals['top1'] = top_1_acc 26 | if "top5" in metric_names: 27 | top_5_acc = tensor_to_python_float(top_5_acc, is_distributed=use_distributed) 28 | metric_vals['top5'] = top_5_acc 29 | 30 | if "iou" in metric_names: 31 | inter, union = compute_miou_batch(prediction=pred_label, target=target_label) 32 | 33 | inter = tensor_to_python_float(inter, is_distributed=use_distributed) 34 | union = tensor_to_python_float(union, is_distributed=use_distributed) 35 | metric_vals['iou'] = { 36 | 'inter': inter, 37 | 'union': union 38 | } 39 | 40 | return metric_vals 41 | -------------------------------------------------------------------------------- /metrics/topk_accuracy.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | from torch import Tensor 7 | from typing import Optional 8 | 9 | from . import register_stats_fn 10 | 11 | 12 | @register_stats_fn(name="top1") 13 | @register_stats_fn(name="top5") 14 | def top_k_accuracy(output: Tensor, target: Tensor, top_k: Optional[tuple]=(1,)) -> list: 15 | maximum_k = max(top_k) 16 | batch_size = target.shape[0] 17 | 18 | _, pred = output.topk(maximum_k, 1, True, True) 19 | pred = pred.t() 20 | correct = pred.eq( 21 | target.reshape(1, -1).expand_as(pred) 22 | ) 23 | 24 | results = [] 25 | for k in top_k: 26 | correct_k = correct[:k].reshape(-1).float().sum(0, keepdim=True) 27 | acc_k = correct_k.mul_(100.0 / batch_size) 28 | results.append(acc_k) 29 | return results 30 | -------------------------------------------------------------------------------- /optim/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/optim/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /optim/__pycache__/adam.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/optim/__pycache__/adam.cpython-38.pyc -------------------------------------------------------------------------------- /optim/__pycache__/adamw.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/optim/__pycache__/adamw.cpython-38.pyc -------------------------------------------------------------------------------- /optim/__pycache__/base_optim.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/optim/__pycache__/base_optim.cpython-38.pyc -------------------------------------------------------------------------------- /optim/__pycache__/sgd.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/optim/__pycache__/sgd.cpython-38.pyc -------------------------------------------------------------------------------- /optim/adam.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import argparse 7 | from torch.optim import Adam 8 | 9 | from . import register_optimizer 10 | from .base_optim import BaseOptim 11 | 12 | 13 | @register_optimizer("adam") 14 | class AdamOptimizer(BaseOptim, Adam): 15 | """ 16 | Adam: https://arxiv.org/abs/1412.6980 17 | """ 18 | def __init__(self, opts, model_params) -> None: 19 | BaseOptim.__init__(self, opts=opts) 20 | beta1 = getattr(opts, "optim.adam.beta1", 0.9) 21 | beta2 = getattr(opts, "optim.adam.beta2", 0.98) 22 | ams_grad = getattr(opts, "optim.adam.amsgrad", False) 23 | Adam.__init__( 24 | self, 25 | params=model_params, 26 | lr=self.lr, 27 | betas=(beta1, beta2), 28 | eps=self.eps, 29 | weight_decay=self.weight_decay, 30 | amsgrad=ams_grad 31 | ) 32 | 33 | @classmethod 34 | def add_arguments(cls, parser: argparse.ArgumentParser) -> argparse.ArgumentParser: 35 | group = parser.add_argument_group('ADAM arguments', 'ADAM arguments') 36 | group.add_argument('--optim.adam.beta1', type=float, default=0.9, help='Adam Beta1') 37 | group.add_argument('--optim.adam.beta2', type=float, default=0.98, help='Adam Beta2') 38 | group.add_argument('--optim.adam.amsgrad', action='store_true', help='Use AMSGrad in ADAM') 39 | return parser 40 | 41 | def __repr__(self) -> str: 42 | group_dict = dict() 43 | for i, group in enumerate(self.param_groups): 44 | for key in sorted(group.keys()): 45 | if key == 'params': 46 | continue 47 | if key not in group_dict: 48 | group_dict[key] = [group[key]] 49 | else: 50 | group_dict[key].append(group[key]) 51 | 52 | format_string = self.__class__.__name__ + ' (' 53 | format_string += '\n' 54 | for k, v in group_dict.items(): 55 | format_string += '\t {0}: {1}\n'.format(k, v) 56 | format_string += ')' 57 | return format_string -------------------------------------------------------------------------------- /optim/adamw.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import argparse 7 | from torch.optim import AdamW 8 | 9 | from . import register_optimizer 10 | from .base_optim import BaseOptim 11 | 12 | 13 | @register_optimizer("adamw") 14 | class AdamWOptimizer(BaseOptim, AdamW): 15 | """ 16 | AdamW: https://arxiv.org/abs/1711.05101 17 | """ 18 | def __init__(self, opts, model_params) -> None: 19 | BaseOptim.__init__(self, opts=opts) 20 | beta1 = getattr(opts, "optim.adamw.beta1", 0.9) 21 | beta2 = getattr(opts, "optim.adamw.beta2", 0.98) 22 | ams_grad = getattr(opts, "optim.adamw.amsgrad", False) 23 | AdamW.__init__(self, params=model_params, lr=self.lr, betas=(beta1, beta2), eps=self.eps, 24 | weight_decay=self.weight_decay, amsgrad=ams_grad) 25 | 26 | @classmethod 27 | def add_arguments(cls, parser: argparse.ArgumentParser) -> argparse.ArgumentParser: 28 | group = parser.add_argument_group('AdamW arguments', 'AdamW arguments') 29 | group.add_argument('--optim.adamw.beta1', type=float, default=0.9, help='Adam Beta1') 30 | group.add_argument('--optim.adamw.beta2', type=float, default=0.98, help='Adam Beta2') 31 | group.add_argument('--optim.adamw.amsgrad', action='store_true', help='Use AMSGrad in ADAM') 32 | return parser 33 | 34 | def __repr__(self) -> str: 35 | group_dict = dict() 36 | for i, group in enumerate(self.param_groups): 37 | for key in sorted(group.keys()): 38 | if key == 'params': 39 | continue 40 | if key not in group_dict: 41 | group_dict[key] = [group[key]] 42 | else: 43 | group_dict[key].append(group[key]) 44 | 45 | format_string = self.__class__.__name__ + ' (' 46 | format_string += '\n' 47 | for k, v in group_dict.items(): 48 | format_string += '\t {0}: {1}\n'.format(k, v) 49 | format_string += ')' 50 | return format_string -------------------------------------------------------------------------------- /optim/base_optim.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import argparse 7 | 8 | 9 | class BaseOptim(object): 10 | def __init__(self, opts) -> None: 11 | self.eps = 1e-8 12 | self.lr = getattr(opts, "scheduler.lr", 0.1) 13 | self.weight_decay = getattr(opts, "optim.weight_decay", 4e-5) 14 | 15 | @classmethod 16 | def add_arguments(cls, parser: argparse.ArgumentParser) -> argparse.ArgumentParser: 17 | return parser 18 | -------------------------------------------------------------------------------- /optim/scheduler/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/optim/scheduler/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /optim/scheduler/__pycache__/base_scheduler.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/optim/scheduler/__pycache__/base_scheduler.cpython-38.pyc -------------------------------------------------------------------------------- /optim/scheduler/__pycache__/cosine.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/optim/scheduler/__pycache__/cosine.cpython-38.pyc -------------------------------------------------------------------------------- /optim/scheduler/__pycache__/cyclic.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/optim/scheduler/__pycache__/cyclic.cpython-38.pyc -------------------------------------------------------------------------------- /optim/scheduler/__pycache__/polynomial.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/optim/scheduler/__pycache__/polynomial.cpython-38.pyc -------------------------------------------------------------------------------- /optim/scheduler/base_scheduler.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import argparse 7 | 8 | 9 | class BaseLRScheduler(object): 10 | def __init__(self, opts) -> None: 11 | super().__init__() 12 | self.opts = opts 13 | self.round_places = 8 14 | self.lr_multipliers = getattr(opts, "optim.lr_multipliers", None) 15 | 16 | @classmethod 17 | def add_arguments(cls, parser: argparse.ArgumentParser) -> argparse.ArgumentParser: 18 | return parser 19 | 20 | def get_lr(self, epoch: int, curr_iter: int): 21 | raise NotImplementedError 22 | 23 | def update_lr(self, optimizer, epoch: int, curr_iter: int): 24 | lr = self.get_lr(epoch=epoch, curr_iter=curr_iter) 25 | lr = max(0.0, lr) 26 | if self.lr_multipliers is not None: 27 | assert len(self.lr_multipliers) == len(optimizer.param_groups) 28 | for g_id, param_group in enumerate(optimizer.param_groups): 29 | param_group['lr'] = round(lr * self.lr_multipliers[g_id], self.round_places) 30 | else: 31 | for param_group in optimizer.param_groups: 32 | param_group['lr'] = round(lr, self.round_places) 33 | return optimizer 34 | 35 | @staticmethod 36 | def retrieve_lr(optimizer) -> list: 37 | lr_list = [] 38 | for param_group in optimizer.param_groups: 39 | lr_list.append(param_group['lr']) 40 | return lr_list -------------------------------------------------------------------------------- /optim/scheduler/cosine.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | from . import register_scheduler 7 | from .base_scheduler import BaseLRScheduler 8 | import argparse 9 | import math 10 | 11 | 12 | @register_scheduler("cosine") 13 | class CosineScheduler(BaseLRScheduler): 14 | """ 15 | Cosine learning rate scheduler: https://arxiv.org/abs/1608.03983 16 | """ 17 | def __init__(self, opts, **kwargs) -> None: 18 | is_iter_based = getattr(opts, "scheduler.is_iteration_based", True) 19 | super(CosineScheduler, self).__init__(opts=opts) 20 | 21 | max_iterations = getattr(opts, "scheduler.max_iterations", 150000) 22 | warmup_iterations = getattr(opts, "scheduler.warmup_iterations", 10000) 23 | 24 | self.min_lr = getattr(opts, "scheduler.cosine.min_lr", 1e-5) 25 | self.max_lr = getattr(opts, "scheduler.cosine.max_lr", 0.4) 26 | 27 | self.warmup_iterations = max(warmup_iterations, 0) 28 | if self.warmup_iterations > 0: 29 | warmup_init_lr = getattr(opts, "scheduler.warmup_init_lr", 1e-7) 30 | self.warmup_init_lr = warmup_init_lr 31 | self.warmup_step = (self.max_lr - self.warmup_init_lr) / self.warmup_iterations 32 | 33 | self.period = max_iterations - self.warmup_iterations + 1 if is_iter_based \ 34 | else getattr(opts, "scheduler.max_epochs", 350) 35 | 36 | self.is_iter_based = is_iter_based 37 | 38 | @classmethod 39 | def add_arguments(cls, parser: argparse.ArgumentParser) -> argparse.ArgumentParser: 40 | group = parser.add_argument_group(title="Cosine LR arguments", description="Cosine LR arguments") 41 | 42 | group.add_argument('--scheduler.cosine.min-lr', type=float, default=1e-5, 43 | help="Minimum LR in Cosine LR scheduler") 44 | group.add_argument('--scheduler.cosine.max-lr', type=float, default=0.1, 45 | help="Maximum LR in Cosine LR scheduler") 46 | 47 | return parser 48 | 49 | def get_lr(self, epoch: int, curr_iter: int) -> float: 50 | if curr_iter < self.warmup_iterations: 51 | curr_lr = self.warmup_init_lr + curr_iter * self.warmup_step 52 | else: 53 | if self.is_iter_based: 54 | curr_iter = curr_iter - self.warmup_iterations 55 | curr_lr = self.min_lr + 0.5 * (self.max_lr - self.min_lr) * (1 + math.cos(math.pi * curr_iter / self.period)) 56 | else: 57 | curr_lr = self.min_lr + 0.5 * (self.max_lr - self.min_lr) * ( 58 | 1 + math.cos(math.pi * epoch / self.period)) 59 | return max(0.0, curr_lr) 60 | 61 | def __repr__(self) -> str: 62 | repr_str = '{}('.format(self.__class__.__name__) 63 | repr_str += '\n \t min_lr={}\n \t max_lr={}\n \t period={}'.format(self.min_lr, self.max_lr, self.period) 64 | if self.warmup_iterations > 0: 65 | repr_str += '\n \t warmup_init_lr={}\n \t warmup_iters={}'.format(self.warmup_init_lr, self.warmup_iterations) 66 | 67 | repr_str += '\n )' 68 | return repr_str 69 | -------------------------------------------------------------------------------- /optim/scheduler/polynomial.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import argparse 7 | 8 | from . import register_scheduler 9 | from .base_scheduler import BaseLRScheduler 10 | 11 | 12 | @register_scheduler("polynomial") 13 | class PolynomialScheduler(BaseLRScheduler): 14 | """ 15 | Polynomial LR scheduler 16 | """ 17 | def __init__(self, opts, **kwargs) -> None: 18 | is_iter_based = getattr(opts, "scheduler.is_iteration_based", False) 19 | max_iterations = getattr(opts, "scheduler.max_iterations", 50000) 20 | warmup_iterations = getattr(opts, "scheduler.warmup_iterations", 0) 21 | max_epochs = getattr(opts, "scheduler.max_epochs", 350) 22 | 23 | super(PolynomialScheduler, self).__init__(opts=opts) 24 | 25 | self.start_lr = getattr(opts, "scheduler.polynomial.start_lr", 0.1) 26 | self.end_lr = getattr(opts, "scheduler.polynomial.end_lr", 1e-5) 27 | self.power = getattr(opts, "scheduler.polynomial.power", 2.5) 28 | 29 | self.warmup_iterations = max(warmup_iterations, 0) 30 | if self.warmup_iterations > 0: 31 | warmup_init_lr = getattr(opts, "scheduler.warmup_init_lr", 1e-7) 32 | self.warmup_init_lr = warmup_init_lr 33 | self.warmup_step = (self.start_lr - self.warmup_init_lr) / self.warmup_iterations 34 | 35 | self.is_iter_based = is_iter_based 36 | self.max_iterations = max_iterations 37 | self.max_epochs = max_epochs 38 | 39 | @classmethod 40 | def add_arguments(cls, parser: argparse.ArgumentParser) -> argparse.ArgumentParser: 41 | group = parser.add_argument_group(title="Polynomial LR arguments", description="Polynomial LR arguments") 42 | 43 | group.add_argument('--scheduler.polynomial.power', type=float, default=2.5, help="Polynomial power") 44 | group.add_argument('--scheduler.polynomial.start-lr', type=float, default=0.1, help="Start LR in Poly LR scheduler") 45 | group.add_argument('--scheduler.polynomial.end-lr', type=float, default=1e-5, help="End LR in Poly LR scheduler") 46 | 47 | return parser 48 | 49 | def get_lr(self, epoch: int, curr_iter: int) -> float: 50 | if curr_iter < self.warmup_iterations: 51 | curr_lr = self.warmup_init_lr + curr_iter * self.warmup_step 52 | else: 53 | curr_iter = curr_iter - self.warmup_iterations 54 | factor = (curr_iter / self.max_iterations) if self.is_iter_based else (epoch / self.max_epochs) 55 | curr_lr = (self.start_lr - self.end_lr) * ((1.0 - factor) ** self.power) + self.end_lr 56 | return max(0.0, curr_lr) 57 | 58 | def __repr__(self) -> str: 59 | repr_str = '{}('.format(self.__class__.__name__) 60 | repr_str += '\n\tpower={}\n\tstart_lr={}'.format(self.power, self.start_lr) 61 | if self.end_lr > 0: 62 | repr_str += '\n\tend_lr={}'.format(self.end_lr) 63 | 64 | if self.warmup_iterations > 0: 65 | repr_str += '\n\twarmup_init_lr={}\n\twarmup_iters={}'.format(self.warmup_init_lr, self.warmup_iterations) 66 | 67 | repr_str += '\n )' 68 | return repr_str -------------------------------------------------------------------------------- /optim/sgd.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import argparse 7 | from torch.optim import SGD 8 | 9 | from . import register_optimizer 10 | from .base_optim import BaseOptim 11 | 12 | 13 | @register_optimizer("sgd") 14 | class SGDOptimizer(BaseOptim, SGD): 15 | """ 16 | SGD: http://www.cs.toronto.edu/%7Ehinton/absps/momentum.pdf 17 | """ 18 | def __init__(self, opts, model_params) -> None: 19 | BaseOptim.__init__(self, opts=opts) 20 | nesterov = getattr(opts, "optim.sgd.nesterov", False) 21 | momentum = getattr(opts, "optim.sgd.momentum", 0.9) 22 | 23 | SGD.__init__( 24 | self, 25 | params=model_params, 26 | lr=self.lr, 27 | momentum=momentum, 28 | weight_decay=self.weight_decay, 29 | nesterov=nesterov 30 | ) 31 | 32 | @classmethod 33 | def add_arguments(cls, parser: argparse.ArgumentParser) -> argparse.ArgumentParser: 34 | group = parser.add_argument_group('SGD arguments', 'SGD arguments') 35 | group.add_argument('--optim.sgd.momentum', default=0.9, type=float, help='Momemtum in SGD') 36 | group.add_argument('--optim.sgd.nesterov', action='store_true', help='Use nesterov in SGD') 37 | return parser 38 | 39 | def __repr__(self) -> str: 40 | group_dict = dict() 41 | for i, group in enumerate(self.param_groups): 42 | for key in sorted(group.keys()): 43 | if key == 'params': 44 | continue 45 | if key not in group_dict: 46 | group_dict[key] = [group[key]] 47 | else: 48 | group_dict[key].append(group[key]) 49 | 50 | format_string = self.__class__.__name__ + ' (' 51 | format_string += '\n' 52 | for k, v in group_dict.items(): 53 | format_string += '\t {0}: {1}\n'.format(k, v) 54 | format_string += ')' 55 | return format_string -------------------------------------------------------------------------------- /options/utils.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import yaml 7 | import os 8 | import collections 9 | 10 | from utils import logger 11 | from utils.ddp_utils import is_master 12 | from utils.download_utils import get_local_path 13 | 14 | DEFAULT_CONFIG_DIR = "config" 15 | 16 | 17 | def flatten_yaml_as_dict(d, parent_key='', sep='.'): 18 | items = [] 19 | for k, v in d.items(): 20 | new_key = parent_key + sep + k if parent_key else k 21 | if isinstance(v, collections.MutableMapping): 22 | items.extend(flatten_yaml_as_dict(v, new_key, sep=sep).items()) 23 | else: 24 | items.append((new_key, v)) 25 | return dict(items) 26 | 27 | 28 | def load_config_file(opts): 29 | config_file_name = getattr(opts, "common.config_file", None) 30 | if config_file_name is None: 31 | return opts 32 | is_master_node = is_master(opts) 33 | 34 | if is_master_node: 35 | config_file_name = get_local_path(opts=opts, path=config_file_name) 36 | 37 | if not os.path.isfile(config_file_name): 38 | if len(config_file_name.split('/')) == 1: 39 | # loading files from default config folder 40 | new_config_file_name = "{}/{}".format(DEFAULT_CONFIG_DIR, config_file_name) 41 | if not os.path.isfile(new_config_file_name) and is_master_node: 42 | logger.warning("Configuration file neither exists at {} nor at {}".format(config_file_name, new_config_file_name)) 43 | return opts 44 | else: 45 | config_file_name = new_config_file_name 46 | else: 47 | # If absolute path of the file is passed 48 | if not os.path.isfile(config_file_name) and is_master_node: 49 | logger.warning("Configuration file does not exists at {}".format(config_file_name)) 50 | return opts 51 | 52 | setattr(opts, "common.config_file", config_file_name) 53 | with open(config_file_name, 'r') as yaml_file: 54 | try: 55 | cfg = yaml.load(yaml_file, Loader=yaml.FullLoader) 56 | 57 | flat_cfg = flatten_yaml_as_dict(cfg) 58 | for k, v in flat_cfg.items(): 59 | if hasattr(opts, k): 60 | setattr(opts, k, v) 61 | except yaml.YAMLError as exc: 62 | if is_master_node: 63 | logger.warning('Error while loading config file: {}'.format(config_file_name)) 64 | logger.warning('Error message: {}'.format(str(exc))) 65 | 66 | return opts 67 | -------------------------------------------------------------------------------- /pretrained_models/classification/checkpoint_ema_avg.pt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/pretrained_models/classification/checkpoint_ema_avg.pt -------------------------------------------------------------------------------- /pretrained_models/detection/checkpoint_ema_avg.pt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/pretrained_models/detection/checkpoint_ema_avg.pt -------------------------------------------------------------------------------- /pretrained_models/model_info.txt: -------------------------------------------------------------------------------- 1 | EdgeFormer-S 2 | 3 | task model size # params. performance 4 | classification 20.0M 5.0M 78.63% (Top1 accuracy) 5 | detection 20.7M 5.2M 28.8 (mAP) 6 | segmentation 22.5M 5.8M 79.7 (mIOU) 7 | -------------------------------------------------------------------------------- /pretrained_models/segmentation/checkpoint_ema_avg.pt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkzhang-git/ParC-Net/f6780d0ad795835df2181b8fd4737276deb6e211/pretrained_models/segmentation/checkpoint_ema_avg.pt -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cityscapesscripts==2.2.0 2 | coremltools==5.2.0 3 | matplotlib==3.3.4 4 | numba==0.49.1 5 | numpy==1.20.1 6 | opencv_python==4.5.5.62 7 | Pillow==9.0.1 8 | pycocotools==2.0.4 9 | PyYAML==6.0 10 | scipy==1.6.2 11 | torch==1.8.0+cu111 12 | torchvision==0.9.0+cu111 13 | tqdm==4.59.0 14 | -------------------------------------------------------------------------------- /utils/color_map.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import numpy as np 7 | from typing import Optional, List 8 | 9 | 10 | class Colormap(object): 11 | """ 12 | Generate colormap for visualizing segmentation masks or bounding boxes. 13 | 14 | This is based on the MATLab code in the PASCAL VOC repository: 15 | http://host.robots.ox.ac.uk/pascal/VOC/voc2012/index.html#devkit 16 | """ 17 | def __init__(self, n: Optional[int] = 256, normalized: Optional[bool] = False): 18 | super(Colormap, self).__init__() 19 | self.n = n 20 | self.normalized = normalized 21 | 22 | @staticmethod 23 | def get_bit_at_idx(val, idx): 24 | return (val & (1 << idx)) != 0 25 | 26 | def get_color_map(self) -> np.ndarray: 27 | 28 | dtype = 'float32' if self.normalized else 'uint8' 29 | color_map = np.zeros((self.n, 3), dtype=dtype) 30 | for i in range(self.n): 31 | r = g = b = 0 32 | c = i 33 | for j in range(8): 34 | r = r | (self.get_bit_at_idx(c, 0) << 7 - j) 35 | g = g | (self.get_bit_at_idx(c, 1) << 7 - j) 36 | b = b | (self.get_bit_at_idx(c, 2) << 7 - j) 37 | c = c >> 3 38 | 39 | color_map[i] = np.array([r, g, b]) 40 | color_map = color_map / 255 if self.normalized else color_map 41 | return color_map 42 | 43 | def get_box_color_codes(self) -> List: 44 | box_codes = [] 45 | 46 | for i in range(self.n): 47 | r = g = b = 0 48 | c = i 49 | for j in range(8): 50 | r = r | (self.get_bit_at_idx(c, 0) << 7 - j) 51 | g = g | (self.get_bit_at_idx(c, 1) << 7 - j) 52 | b = b | (self.get_bit_at_idx(c, 2) << 7 - j) 53 | c = c >> 3 54 | box_codes.append((int(r), int(g), int(b))) 55 | return box_codes 56 | 57 | def get_color_map_list(self) -> List: 58 | cmap = self.get_color_map() 59 | cmap = np.asarray(cmap).flatten() 60 | return list(cmap) 61 | -------------------------------------------------------------------------------- /utils/common_utils.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import random 7 | import torch 8 | import numpy as np 9 | from utils import logger 10 | import os 11 | from utils.ddp_utils import is_master 12 | from cvnets.layers import norm_layers_tuple 13 | 14 | 15 | def check_compatibility(): 16 | ver = torch.__version__.split('.') 17 | major_version = int(ver[0]) 18 | minor_version = int(ver[0]) 19 | 20 | if major_version < 1 and minor_version < 7: 21 | logger.error('Min pytorch version required is 1.7.0. Got: {}'.format('.'.join(ver))) 22 | 23 | 24 | def check_frozen_norm_layer(model: torch.nn.Module) -> (bool, int): 25 | 26 | if hasattr(model, 'module'): 27 | model = model.module 28 | 29 | count_norm = 0 30 | frozen_state = False 31 | for m in model.modules(): 32 | if isinstance(m, norm_layers_tuple): 33 | frozen_state = m.weight.requires_grad 34 | 35 | return frozen_state, count_norm 36 | 37 | 38 | def device_setup(opts): 39 | random_seed = getattr(opts, "common.seed", 0) 40 | random.seed(random_seed) 41 | torch.manual_seed(random_seed) 42 | np.random.seed(random_seed) 43 | 44 | is_master_node = is_master(opts) 45 | if is_master_node: 46 | logger.log('Random seeds are set to {}'.format(random_seed)) 47 | logger.log('Using PyTorch version {}'.format(torch.__version__)) 48 | 49 | n_gpus = torch.cuda.device_count() 50 | if n_gpus == 0: 51 | if is_master_node: 52 | logger.warning('No GPUs available. Using CPU') 53 | device = torch.device('cpu') 54 | n_gpus = 0 55 | else: 56 | if is_master_node: 57 | logger.log('Available GPUs: {}'.format(n_gpus)) 58 | device = torch.device('cuda') 59 | 60 | if torch.backends.cudnn.is_available(): 61 | import torch.backends.cudnn as cudnn 62 | torch.backends.cudnn.enabled = True 63 | cudnn.benchmark = False 64 | cudnn.deterministic = True 65 | if is_master_node: 66 | logger.log('CUDNN is enabled') 67 | 68 | setattr(opts, "dev.device", device) 69 | setattr(opts, "dev.num_gpus", n_gpus) 70 | 71 | return opts 72 | 73 | 74 | def create_directories(dir_path: str, is_master_node: bool) -> None: 75 | if not os.path.isdir(dir_path): 76 | os.makedirs(dir_path) 77 | if is_master_node: 78 | logger.log('Directory created at: {}'.format(dir_path)) 79 | else: 80 | if is_master_node: 81 | logger.log('Directory exists at: {}'.format(dir_path)) 82 | -------------------------------------------------------------------------------- /utils/ddp_utils.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | 7 | import socket 8 | import torch 9 | import torch.distributed as dist 10 | from utils import logger 11 | 12 | 13 | def is_master(opts) -> bool: 14 | node_rank = getattr(opts, "ddp.rank", 0) 15 | return (node_rank == 0) 16 | 17 | 18 | def distributed_init(opts) -> int: 19 | ddp_url = getattr(opts, "ddp.dist_url", None) 20 | ddp_port = getattr(opts, "ddp.dist_port", 6006) 21 | is_master_node = is_master(opts) 22 | if ddp_url is None: 23 | hostname = socket.gethostname() 24 | ddp_url = 'tcp://{}:{}'.format(hostname, ddp_port) 25 | setattr(opts, "ddp.dist_url", ddp_url) 26 | 27 | node_rank = getattr(opts, "ddp.rank", 0) 28 | world_size = getattr(opts, "ddp.world_size", 0) 29 | if torch.distributed.is_initialized(): 30 | logger.warning('DDP is already initialized and cannot be initialize twice!') 31 | else: 32 | logger.info('distributed init (rank {}): {}'.format(node_rank, ddp_url)) 33 | 34 | dist_backend = "gloo" 35 | if dist.is_nccl_available(): 36 | dist_backend = 'nccl' 37 | if is_master_node: 38 | logger.log('Using NCCL as distributed backend with version={}'.format(torch.cuda.nccl.version())) 39 | 40 | dist.init_process_group( 41 | backend=dist_backend, 42 | init_method=ddp_url, 43 | world_size=world_size, 44 | rank=node_rank 45 | ) 46 | 47 | # perform a dummy all-reduce to initialize the NCCL communicator 48 | if torch.cuda.is_available(): 49 | dist.all_reduce(torch.zeros(1).cuda()) 50 | 51 | node_rank = torch.distributed.get_rank() 52 | setattr(opts, "ddp.rank", node_rank) 53 | return node_rank -------------------------------------------------------------------------------- /utils/download_utils.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import os 7 | import copy 8 | import torch.distributed as dist 9 | from urllib import request 10 | 11 | 12 | from common import TMP_CACHE_LOC 13 | from utils.ddp_utils import is_master 14 | 15 | 16 | def get_local_path(opts, path, *args, **kwargs): 17 | """ 18 | If File name is a URL, download to TMP_CACHE_LOC and then return the local path. Otherwise, don't do anything 19 | """ 20 | if path.find("s3://") > -1 or path.find("http://") > -1 or path.find("https://") > -1: 21 | url_path = copy.deepcopy(path) 22 | ckpt_name = path.split(os.sep)[-1] 23 | local_path = "{}/{}".format(TMP_CACHE_LOC, ckpt_name) 24 | local_path = str(local_path).strip() 25 | 26 | if os.path.isfile(local_path) and is_master(opts): 27 | # If file exists, remove it and then download again 28 | # This is important because if we are downloading from bolt tasks, then checkpoint names are the same 29 | os.remove(local_path) 30 | 31 | if not os.path.isfile(local_path) and is_master(opts): 32 | request.urlretrieve(url_path, local_path) 33 | 34 | if getattr(opts, "ddp.use_distributed", False): 35 | # syncronize between processes 36 | dist.barrier() 37 | return local_path 38 | return path -------------------------------------------------------------------------------- /utils/logger.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import time 7 | from typing import Optional 8 | 9 | text_colors = { 10 | 'logs': '\033[34m', # 033 is the escape code and 34 is the color code 11 | 'info': '\033[32m', 12 | 'warning': '\033[33m', 13 | 'error': '\033[31m', 14 | 'bold': '\033[1m', 15 | 'end_color': '\033[0m', 16 | 'light_red': '\033[36m' 17 | } 18 | 19 | 20 | def get_curr_time_stamp() -> str: 21 | return time.strftime("%Y-%m-%d %H:%M:%S") 22 | 23 | 24 | def error(message: str) -> None: 25 | time_stamp = get_curr_time_stamp() 26 | error_str = text_colors['error'] + text_colors['bold'] + 'ERROR ' + text_colors['end_color'] 27 | print('{} - {} - {}'.format(time_stamp, error_str, message)) 28 | print('{} - {} - {}'.format(time_stamp, error_str, 'Exiting!!!')) 29 | exit(-1) 30 | 31 | 32 | def color_text(in_text: str) -> str: 33 | return text_colors['light_red'] + in_text + text_colors['end_color'] 34 | 35 | 36 | def log(message: str) -> None: 37 | time_stamp = get_curr_time_stamp() 38 | log_str = text_colors['logs'] + text_colors['bold'] + 'LOGS ' + text_colors['end_color'] 39 | print('{} - {} - {}'.format(time_stamp, log_str, message)) 40 | 41 | 42 | def warning(message: str) -> None: 43 | time_stamp = get_curr_time_stamp() 44 | warn_str = text_colors['warning'] + text_colors['bold'] + 'WARNING' + text_colors['end_color'] 45 | print('{} - {} - {}'.format(time_stamp, warn_str, message)) 46 | 47 | 48 | def info(message: str, print_line: Optional[bool] = False) -> None: 49 | time_stamp = get_curr_time_stamp() 50 | info_str = text_colors['info'] + text_colors['bold'] + 'INFO ' + text_colors['end_color'] 51 | print('{} - {} - {}'.format(time_stamp, info_str, message)) 52 | if print_line: 53 | double_dash_line(dashes=150) 54 | 55 | 56 | def double_dash_line(dashes: Optional[int] = 75) -> None: 57 | print(text_colors['error'] + '=' * dashes + text_colors['end_color']) 58 | 59 | 60 | def singe_dash_line(dashes: Optional[int] = 67) -> None: 61 | print('-' * dashes) 62 | 63 | 64 | def print_header(header: str) -> None: 65 | double_dash_line() 66 | print(text_colors['info'] + text_colors['bold'] + '=' * 50 + str(header) + text_colors['end_color']) 67 | double_dash_line() 68 | 69 | 70 | def print_header_minor(header: str) -> None: 71 | print(text_colors['warning'] + text_colors['bold'] + '=' * 25 + str(header) + text_colors['end_color']) 72 | 73 | -------------------------------------------------------------------------------- /utils/math_utils.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | from typing import Union, Optional 7 | 8 | 9 | def make_divisible(v: Union[float, int], 10 | divisor: Optional[int] = 8, 11 | min_value: Optional[Union[float, int]] = None) -> Union[float, int]: 12 | """ 13 | This function is taken from the original tf repo. 14 | It ensures that all layers have a channel number that is divisible by 8 15 | It can be seen here: 16 | https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py 17 | :param v: 18 | :param divisor: 19 | :param min_value: 20 | :return: 21 | """ 22 | if min_value is None: 23 | min_value = divisor 24 | new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) 25 | # Make sure that round down does not go down by more than 10%. 26 | if new_v < 0.9 * v: 27 | new_v += divisor 28 | return new_v 29 | 30 | 31 | def bound_fn(min_val: Union[float, int], max_val: Union[float, int], value: Union[float, int]) -> Union[float, int]: 32 | return max(min_val, min(max_val, value)) 33 | -------------------------------------------------------------------------------- /utils/pytorch_to_coreml.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import torch 7 | from torch import Tensor 8 | import coremltools as ct 9 | from typing import Optional, Dict, Tuple, Union 10 | import numpy as np 11 | 12 | from utils.tensor_utils import create_rand_tensor 13 | from torch.utils.mobile_optimizer import optimize_for_mobile 14 | 15 | 16 | def convert_pytorch_to_coreml( 17 | opts, 18 | pytorch_model: torch.nn.Module, 19 | input_tensor: Optional[torch.Tensor] = None, 20 | *args, **kwargs 21 | ) -> Dict: 22 | """ 23 | Convert Pytorch model to CoreML 24 | 25 | :param opts: Arguments 26 | :param pytorch_model: Pytorch model that needs to be converted to JIT or CoreML 27 | :param input_tensor: Input tensor, usually a 4-dimensional tensor of shape Batch x 3 x Height x Width 28 | :return: CoreML model or package 29 | """ 30 | if input_tensor is None: 31 | input_tensor = create_rand_tensor(opts=opts, device="cpu") 32 | 33 | if pytorch_model.training: 34 | pytorch_model.eval() 35 | 36 | with torch.no_grad(): 37 | pytorch_out = pytorch_model(input_tensor) 38 | 39 | jit_model = torch.jit.trace(pytorch_model, input_tensor) 40 | jit_out = jit_model(input_tensor) 41 | 42 | jit_model_optimized = optimize_for_mobile(jit_model) 43 | jit_optimzied_out = jit_model_optimized(input_tensor) 44 | 45 | assertion_check(py_out=pytorch_out, jit_out=jit_out) 46 | assertion_check(py_out=pytorch_out, jit_out=jit_optimzied_out) 47 | 48 | coreml_model = ct.convert( 49 | model=jit_model, 50 | inputs=[ct.ImageType(name="input", shape=input_tensor.shape)], 51 | convert_to="neuralnetwork", 52 | #preprocessing_args={"scale": 1.0/255.0}, 53 | #minimum_deployment_target=ct.target.iOS15, 54 | #compute_precision=ct.precision.FLOAT16 55 | ) 56 | return { 57 | "coreml": coreml_model, 58 | "jit": jit_model, 59 | "jit_optimized": jit_model_optimized 60 | } 61 | 62 | 63 | def assertion_check(py_out: Union[Tensor, Dict, Tuple], jit_out: Union[Tensor, Dict, Tuple]) -> None: 64 | if isinstance(py_out, Dict): 65 | assert isinstance(jit_out, Dict) 66 | keys = py_out.keys() 67 | for k in keys: 68 | np.testing.assert_almost_equal(py_out[k].cpu().numpy(), jit_out[k].cpu().numpy(), 69 | decimal=3, 70 | verbose=True) 71 | elif isinstance(py_out, Tensor): 72 | assert isinstance(jit_out, Tensor) 73 | np.testing.assert_almost_equal(py_out.cpu().numpy(), jit_out.cpu().numpy(), 74 | decimal=3, 75 | verbose=True) 76 | elif isinstance(py_out, Tuple): 77 | assert isinstance(jit_out, Tuple) 78 | for x, y in zip(py_out, jit_out): 79 | np.testing.assert_almost_equal(x.cpu().numpy(), y.cpu().numpy(), decimal=3, verbose=True) 80 | 81 | else: 82 | raise NotImplementedError("Only Dictionary[Tensors] or Tuple[Tensors] or Tensors are supported as outputs") 83 | -------------------------------------------------------------------------------- /utils/tensor_utils.py: -------------------------------------------------------------------------------- 1 | # 2 | # For licensing see accompanying LICENSE file. 3 | # Copyright (C) 2020 Apple Inc. All Rights Reserved. 4 | # 5 | 6 | import numpy as np 7 | import torch 8 | from torch import Tensor 9 | from torch import distributed as dist 10 | from typing import Union, Optional, Tuple 11 | 12 | from common import DEFAULT_IMAGE_HEIGHT, DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_CHANNELS 13 | 14 | 15 | def tensor_size_from_opts(opts) -> Tuple[int, int]: 16 | try: 17 | sampler_name = getattr(opts, "sampler.name", "variable_batch_sampler").lower() 18 | if sampler_name.find("var") > -1: 19 | im_w = getattr(opts, "sampler.vbs.crop_size_width", DEFAULT_IMAGE_WIDTH) 20 | im_h = getattr(opts, "sampler.vbs.crop_size_height", DEFAULT_IMAGE_HEIGHT) 21 | else: 22 | im_w = getattr(opts, "sampler.bs.crop_size_width", DEFAULT_IMAGE_WIDTH) 23 | im_h = getattr(opts, "sampler.bs.crop_size_height", DEFAULT_IMAGE_HEIGHT) 24 | except Exception as e: 25 | im_w = im_h = 256 26 | return im_h, im_w 27 | 28 | 29 | def create_rand_tensor(opts, device: Optional[str] = "cpu") -> Tensor: 30 | im_h, im_w = tensor_size_from_opts(opts=opts) 31 | inp_tensor = torch.randint(low=0, high=255, size=(1, DEFAULT_IMAGE_CHANNELS, im_h, im_w), device=device) 32 | inp_tensor = inp_tensor.float().div(255.0) 33 | return inp_tensor 34 | 35 | 36 | def reduce_tensor(inp_tensor: torch.Tensor) -> torch.Tensor: 37 | size = float(dist.get_world_size()) 38 | inp_tensor_clone = inp_tensor.clone() 39 | dist.barrier() 40 | dist.all_reduce(inp_tensor_clone, op=dist.ReduceOp.SUM) 41 | inp_tensor_clone /= size 42 | return inp_tensor_clone 43 | 44 | 45 | def tensor_to_python_float(inp_tensor: Union[int, float, torch.Tensor], 46 | is_distributed: bool) -> Union[int, float, np.ndarray]: 47 | if is_distributed and isinstance(inp_tensor, torch.Tensor): 48 | inp_tensor = reduce_tensor(inp_tensor=inp_tensor) 49 | 50 | if isinstance(inp_tensor, torch.Tensor) and inp_tensor.numel() > 1: 51 | # For IOU, we get a C-dimensional tensor (C - number of classes) 52 | # so, we convert here to a numpy array 53 | return inp_tensor.cpu().numpy() 54 | elif hasattr(inp_tensor, 'item'): 55 | return inp_tensor.item() 56 | elif isinstance(inp_tensor, (int, float)): 57 | return inp_tensor * 1.0 58 | else: 59 | raise NotImplementedError("The data type is not supported yet in tensor_to_python_float function") 60 | 61 | 62 | def to_numpy(img_tensor: torch.Tensor) -> np.ndarray: 63 | # [0, 1] --> [0, 255] 64 | img_tensor = torch.mul(img_tensor, 255.0) 65 | # BCHW --> BHWC 66 | img_tensor = img_tensor.permute(0, 2, 3, 1) 67 | 68 | img_np = img_tensor.byte().cpu().numpy() 69 | return img_np 70 | --------------------------------------------------------------------------------