From 74f2662b30b9b59ec0cddb4becd4db0e28b1f949 Mon Sep 17 00:00:00 2001 From: "hongliang.yuan" Date: Mon, 18 Nov 2024 05:12:57 +0000 Subject: [PATCH] 11/18/2024 05:12:57 sync PyTorch file --- .../start_scripts/train_alexnet_dist_torch.sh | 22 +- .../start_scripts/train_alexnet_torch.sh | 22 +- .../efficientnet_b4/pytorch/train.py | 35 +- .../efficientnet_b4/pytorch/train_horovod.py | 306 + .../pytorch/common_utils/smooth_value.py | 7 +- cv/classification/googlenet/pytorch/train.py | 73 +- .../inceptionv3/pytorch/__init__.py | 5 + .../pytorch/common_utils/__init__.py | 23 + .../inceptionv3/pytorch/common_utils/dist.py} | 156 +- .../pytorch/common_utils}/metric_logger.py | 105 +- .../inceptionv3/pytorch/common_utils/misc.py | 11 + .../pytorch/common_utils/smooth_value.py | 75 + .../pytorch/dataloader/__init__.py | 0 .../pytorch/dataloader/classification.py | 113 + .../pytorch/dataloader/dali_classification.py | 121 + .../pytorch/dataloader/utils/__init__.py | 0 .../utils/presets_classification.py | 41 + .../inceptionv3/pytorch/run_train.py | 38 - .../inceptionv3/pytorch/train.py | 312 + .../pytorch/train_inception_v3_amp_dist.sh | 6 +- .../inceptionv3/pytorch/utils_.py | 156 + .../mobilenetv3/pytorch/__init__.py | 2 + .../pytorch/_torchvision/__init__.py | 3 + .../_internally_replaced_utils.py | 58 + .../pytorch/_torchvision/models/__init__.py | 1 + .../pytorch/_torchvision/models/_utils.py | 83 + .../pytorch/_torchvision/models/mobilenet.py | 4 + .../_torchvision/models/mobilenetv2.py | 211 + .../_torchvision/models/mobilenetv3.py | 328 + .../pytorch/_torchvision/ops/__init__.py | 4 + .../pytorch/_torchvision/ops/misc.py | 163 + .../_torchvision/ops/stochastic_depth.py | 66 + .../mobilenetv3/pytorch/_torchvision/utils.py | 548 + .../pytorch/common_utils/__init__.py | 24 + .../mobilenetv3/pytorch/common_utils/dist.py | 145 + .../mobilenetv3/pytorch/common_utils/loss.py | 26 + .../pytorch/common_utils/metric_logger.py | 94 + .../mobilenetv3/pytorch/common_utils/misc.py | 11 + .../pytorch/common_utils/smooth_value.py | 75 + .../pytorch/dataloader/__init__.py | 0 .../pytorch/dataloader/classification.py | 103 + .../pytorch/dataloader/dali_classification.py | 111 + .../pytorch/dataloader/utils/__init__.py | 0 .../utils/presets_classification.py | 37 + .../mobilenetv3/pytorch/requirements.txt | 4 +- .../mobilenetv3/pytorch/run_train.py | 32 - .../mobilenetv3/pytorch/train.py | 340 + .../mobilenetv3/pytorch/train_horovod.py | 300 + .../train_mobilenet_v3_large_amp_dist.sh | 10 +- .../mobilenetv3/pytorch/utils.py | 152 + .../resnet101/pytorch/.gitignore | 1 + .../resnet101/pytorch/__init__.py | 5 + .../pytorch/common_utils/__init__.py | 23 + .../resnet101/pytorch/common_utils/dist.py | 144 + .../pytorch/common_utils/metric_logger.py | 94 + .../resnet101/pytorch/common_utils/misc.py | 11 + .../pytorch/common_utils/smooth_value.py | 75 + .../resnet101/pytorch/dataloader/__init__.py | 0 .../pytorch/dataloader/classification.py | 113 + .../pytorch/dataloader/dali_classification.py | 121 + .../pytorch/dataloader/utils/__init__.py | 0 .../utils/presets_classification.py | 41 + .../pytorch/start_scripts/get_num_devices.sh | 12 + .../pytorch/start_scripts/init_torch.sh | 3 + .../start_scripts/train_resnet50_amp_torch.sh | 7 + .../train_resnet50_dist_torch.sh | 9 + .../start_scripts/train_resnet50_torch.sh | 5 + cv/classification/resnet101/pytorch/train.py | 317 + .../resnet101/pytorch/train_horovod.py | 304 + .../pytorch/train_resnet101_amp_dist.sh | 8 +- cv/classification/resnet101/pytorch/utils_.py | 156 + cv/classification/resnet50/pytorch/.gitignore | 162 +- .../resnet50/pytorch/__init__.py | 25 +- .../resnet50/pytorch/common_utils/__init__.py | 27 +- .../resnet50/pytorch/common_utils/dist.py | 14 +- .../pytorch/dataloader/classification.py | 23 +- .../pytorch/dataloader/dali_classification.py | 230 +- .../utils/presets_classification.py | 46 +- .../resnet50/pytorch/scripts/fp32_16cards.sh | 2 +- .../scripts/train_resnet50_imagenet.sh | 1 - .../train_resnet50_imagenet_dist_1x4_torch.sh | 1 - .../train_resnet50_imagenet_dist_2x8_torch.sh | 1 - .../pytorch/start_scripts/get_num_devices.sh | 12 + .../pytorch/start_scripts/init_torch.sh | 3 + .../start_scripts/train_resnet50_amp_torch.sh | 7 + .../train_resnet50_dist_torch.sh | 9 + .../start_scripts/train_resnet50_torch.sh | 5 + cv/classification/resnet50/pytorch/train.py | 274 +- .../resnet50/pytorch/train_horovod.py | 304 + cv/classification/resnet50/pytorch/utils_.py | 156 + .../resnext101_32x8d/pytorch/__init__.py | 5 + .../resnext101_32x8d/pytorch/common.py | 2338 --- .../pytorch/common_utils/__init__.py | 23 + .../pytorch/common_utils/dist.py | 144 + .../pytorch/common_utils/metric_logger.py | 94 + .../pytorch/common_utils/misc.py | 11 + .../pytorch/common_utils/smooth_value.py | 75 + .../pytorch/dataloader/__init__.py | 0 .../pytorch/dataloader/classification.py | 113 + .../pytorch/dataloader/dali_classification.py | 121 + .../pytorch/dataloader/utils/__init__.py | 0 .../utils/presets_classification.py | 41 + .../resnext101_32x8d/pytorch/requirements.txt | 4 +- .../resnext101_32x8d/pytorch/resnet.py | 785 - .../resnext101_32x8d/pytorch/resnext.py | 470 - .../resnext101_32x8d/pytorch/run_train.py | 33 - .../resnext101_32x8d/pytorch/train.py | 313 + .../train_resnext101_32x8d_amp_dist.sh | 9 +- .../resnext101_32x8d/pytorch/utils_.py | 156 + .../resnext50_32x4d/pytorch/__init__.py | 5 + .../resnext50_32x4d/pytorch/common.py | 2338 --- .../pytorch/common_utils/__init__.py | 23 + .../pytorch/common_utils/dist.py | 144 + .../pytorch/common_utils/metric_logger.py | 94 + .../pytorch/common_utils/misc.py | 11 + .../pytorch/common_utils/smooth_value.py | 75 + .../pytorch/dataloader/__init__.py | 0 .../pytorch/dataloader/classification.py | 113 + .../pytorch/dataloader/dali_classification.py | 121 + .../pytorch/dataloader/utils/__init__.py | 0 .../utils/presets_classification.py | 41 + .../resnext50_32x4d/pytorch/requirements.txt | 4 +- .../resnext50_32x4d/pytorch/resnet.py | 785 - .../resnext50_32x4d/pytorch/resnext.py | 470 - .../resnext50_32x4d/pytorch/run_train.py | 34 - .../resnext50_32x4d/pytorch/train.py | 313 + .../pytorch/train_resnext50_32x4d_amp_dist.sh | 6 +- .../resnext50_32x4d/pytorch/utils_.py | 156 + .../seresnext/pytorch/__init__.py | 5 + cv/classification/seresnext/pytorch/common.py | 2338 --- .../pytorch/common_utils/__init__.py | 23 + .../seresnext/pytorch/common_utils/dist.py | 144 + .../pytorch/common_utils/metric_logger.py | 94 + .../seresnext/pytorch/common_utils/misc.py | 11 + .../pytorch/common_utils/smooth_value.py | 75 + .../seresnext/pytorch/dataloader/__init__.py | 0 .../pytorch/dataloader/classification.py | 113 + .../pytorch/dataloader/dali_classification.py | 121 + .../pytorch/dataloader/utils/__init__.py | 0 .../utils/presets_classification.py | 41 + cv/classification/seresnext/pytorch/resnet.py | 785 - .../seresnext/pytorch/resnext.py | 470 - .../seresnext/pytorch/run_train.py | 33 - .../seresnext/pytorch/se_block.py | 117 + .../seresnext/pytorch/seresnext.py | 444 +- cv/classification/seresnext/pytorch/train.py | 325 + ...h => train_seresnext101_32x8d_amp_dist.sh} | 8 +- cv/classification/seresnext/pytorch/utils_.py | 156 + .../shufflenetv2/pytorch/__init__.py | 5 + .../pytorch/common_utils/__init__.py | 23 + .../shufflenetv2/pytorch/common_utils/dist.py | 144 + .../pytorch/common_utils/metric_logger.py | 94 + .../shufflenetv2/pytorch/common_utils/misc.py | 11 + .../pytorch/common_utils/smooth_value.py | 75 + .../pytorch/dataloader/__init__.py | 0 .../pytorch/dataloader/classification.py | 113 + .../pytorch/dataloader/dali_classification.py | 121 + .../pytorch/dataloader/utils/__init__.py | 0 .../utils/presets_classification.py | 41 + .../shufflenetv2/pytorch/requirements.txt | 4 +- .../shufflenetv2/pytorch/run_train.py | 32 - .../shufflenetv2/pytorch/train.py | 314 + ...h => train_shufflenet_v2_x0_5_amp_dist.sh} | 9 +- .../shufflenetv2/pytorch/utils_.py | 156 + cv/classification/vgg/pytorch/README.md | 10 - cv/classification/vgg/pytorch/__init__.py | 5 + .../vgg/pytorch/common_utils/__init__.py | 23 + .../vgg/pytorch/common_utils/dist.py | 144 + .../vgg/pytorch/common_utils/metric_logger.py | 94 + .../vgg/pytorch/common_utils/misc.py | 11 + .../vgg/pytorch/common_utils/smooth_value.py | 75 + .../vgg/pytorch/dataloader/__init__.py | 0 .../vgg/pytorch/dataloader/classification.py | 113 + .../pytorch/dataloader/dali_classification.py | 121 + .../vgg/pytorch/dataloader/utils/__init__.py | 0 .../utils/presets_classification.py | 41 + cv/classification/vgg/pytorch/run_train.py | 32 - cv/classification/vgg/pytorch/train.py | 319 + .../vgg/pytorch/train_vgg16_amp_dist.sh | 8 +- cv/classification/vgg/pytorch/utils_.py | 156 + cv/detection/fasterrcnn/pytorch/README.md | 15 +- cv/detection/fasterrcnn/pytorch/coco_eval.py | 3 +- .../pytorch/dataloaders/functional.py | 1324 ++ .../pytorch/dataloaders/functional_pil.py | 349 + .../pytorch/dataloaders/functional_tensor.py | 920 + .../pytorch/dataloaders/transforms_det.py | 13 +- .../fasterrcnn/pytorch/requirements.txt | 1 + ...rain_fasterrcnn_resnet50_amp_dist_torch.sh | 24 +- .../train_fasterrcnn_resnet50_amp_torch.sh | 26 +- .../train_fasterrcnn_resnet50_dist_torch.sh | 24 +- .../train_fasterrcnn_resnet50_torch.sh | 25 +- cv/detection/fasterrcnn/pytorch/train.py | 31 +- .../fasterrcnn/pytorch/utils/__init__.py | 1 + cv/detection/fasterrcnn/pytorch/utils/dist.py | 31 +- cv/detection/fasterrcnn/pytorch/utils/misc.py | 10 + .../fasterrcnn/pytorch/utils/smooth_value.py | 7 +- cv/detection/maskrcnn/pytorch/ABSTRACTIONS.md | 65 - .../maskrcnn/pytorch/CODE_OF_CONDUCT.md | 5 - cv/detection/maskrcnn/pytorch/CONTRIBUTING.md | 39 - cv/detection/maskrcnn/pytorch/Dockerfile | 35 - cv/detection/maskrcnn/pytorch/INSTALL.md | 72 - cv/detection/maskrcnn/pytorch/LICENSE | 37 - cv/detection/maskrcnn/pytorch/MODEL_ZOO.md | 89 - cv/detection/maskrcnn/pytorch/README.md | 38 +- .../maskrcnn/pytorch/TROUBLESHOOTING.md | 67 - cv/detection/maskrcnn/pytorch/__init__.py | 2 + cv/detection/maskrcnn/pytorch/coco_eval.py | 351 + .../maskrcnn/pytorch/common_utils/__init__.py | 23 + .../maskrcnn/pytorch/common_utils/dist.py | 144 + .../pytorch/common_utils/metric_logger.py | 94 + .../maskrcnn/pytorch/common_utils/misc.py | 33 + .../pytorch/common_utils/smooth_value.py | 75 + .../configs/e2e_mask_rcnn_R_50_FPN_1x.yaml | 40 - .../maskrcnn/pytorch/dataloader/__init__.py | 0 .../maskrcnn/pytorch/dataloader/detection.py | 37 + .../pytorch/dataloader/segmentation.py | 46 + .../pytorch/dataloader/utils/__init__.py | 0 .../pytorch/dataloader/utils/camvid.py | 191 + .../pytorch/dataloader/utils/coco_utils.py | 255 + .../pytorch/dataloader/utils/functional.py | 1324 ++ .../dataloader/utils/functional_pil.py | 349 + .../dataloader/utils/functional_tensor.py | 920 + .../pytorch/dataloader/utils/pascal_voc.py | 311 + .../dataloader/utils/presets_detection.py | 41 + .../dataloader/utils/transforms_det.py | 243 + cv/detection/maskrcnn/pytorch/demo/README.md | 45 - .../demo/demo_e2e_mask_rcnn_R_50_FPN_1x.png | Bin 920219 -> 0 bytes .../demo_e2e_mask_rcnn_X_101_32x8d_FPN_1x.png | Bin 915528 -> 0 bytes .../maskrcnn/pytorch/demo/predictor.py | 447 - cv/detection/maskrcnn/pytorch/demo/webcam.py | 93 - .../maskrcnn/pytorch/docker/Dockerfile | 53 - .../pytorch/docker/docker-jupyter/Dockerfile | 67 - .../docker-jupyter/jupyter_notebook_config.py | 31 - .../maskrcnn/pytorch/download_dataset.sh | 39 - cv/detection/maskrcnn/pytorch/engine.py | 142 + .../pytorch/generalized_rcnn_transform.py | 260 + .../maskrcnn/pytorch/group_by_aspect_ratio.py | 195 + cv/detection/maskrcnn/pytorch/install.sh | 10 - .../pytorch/maskrcnn_benchmark/__init__.py | 14 - .../maskrcnn_benchmark/config/__init__.py | 15 - .../maskrcnn_benchmark/config/defaults.py | 409 - .../config/paths_catalog.py | 207 - .../maskrcnn_benchmark/csrc/ROIAlign.h | 46 - .../pytorch/maskrcnn_benchmark/csrc/ROIPool.h | 48 - .../csrc/SigmoidFocalLoss.h | 41 - .../csrc/cpu/ROIAlign_cpu.cpp | 257 - .../maskrcnn_benchmark/csrc/cpu/nms_cpu.cpp | 75 - .../maskrcnn_benchmark/csrc/cpu/vision.h | 16 - .../csrc/cuda/ROIAlign_cuda.cu | 346 - .../csrc/cuda/ROIPool_cuda.cu | 202 - .../csrc/cuda/SigmoidFocalLoss_cuda.cu | 188 - .../maskrcnn_benchmark/csrc/cuda/nms.cu | 131 - .../maskrcnn_benchmark/csrc/cuda/vision.h | 63 - .../pytorch/maskrcnn_benchmark/csrc/nms.h | 28 - .../maskrcnn_benchmark/csrc/vision.cpp | 15 - .../pytorch/maskrcnn_benchmark/data/README.md | 88 - .../maskrcnn_benchmark/data/__init__.py | 15 - .../pytorch/maskrcnn_benchmark/data/build.py | 191 - .../maskrcnn_benchmark/data/collate_batch.py | 33 - .../data/datasets/__init__.py | 19 - .../maskrcnn_benchmark/data/datasets/coco.py | 114 - .../data/datasets/concat_dataset.py | 36 - .../data/datasets/evaluation/__init__.py | 40 - .../data/datasets/evaluation/coco/__init__.py | 34 - .../datasets/evaluation/coco/coco_eval.py | 410 - .../data/datasets/evaluation/voc/__init__.py | 29 - .../data/datasets/evaluation/voc/voc_eval.py | 229 - .../data/datasets/list_dataset.py | 49 - .../maskrcnn_benchmark/data/datasets/voc.py | 147 - .../data/samplers/__init__.py | 19 - .../data/samplers/distributed.py | 79 - .../data/samplers/grouped_batch_sampler.py | 128 - .../samplers/iteration_based_batch_sampler.py | 49 - .../data/transforms/__init__.py | 21 - .../data/transforms/build.py | 41 - .../maskrcnn_benchmark/engine/__init__.py | 14 - .../maskrcnn_benchmark/engine/inference.py | 117 - .../maskrcnn_benchmark/engine/tester.py | 54 - .../maskrcnn_benchmark/engine/trainer.py | 158 - .../maskrcnn_benchmark/layers/__init__.py | 33 - .../maskrcnn_benchmark/layers/_utils.py | 52 - .../maskrcnn_benchmark/layers/batch_norm.py | 37 - .../pytorch/maskrcnn_benchmark/layers/misc.py | 115 - .../pytorch/maskrcnn_benchmark/layers/nms.py | 20 - .../maskrcnn_benchmark/layers/roi_align.py | 81 - .../maskrcnn_benchmark/layers/roi_pool.py | 76 - .../layers/sigmoid_focal_loss.py | 89 - .../modeling/backbone/__init__.py | 15 - .../modeling/backbone/backbone.py | 87 - .../modeling/backbone/fpn.py | 112 - .../modeling/backbone/resnet.py | 430 - .../balanced_positive_negative_sampler.py | 81 - .../maskrcnn_benchmark/modeling/box_coder.py | 108 - .../modeling/detector/__init__.py | 15 - .../modeling/detector/detectors.py | 23 - .../modeling/detector/generalized_rcnn.py | 78 - .../modeling/make_layers.py | 135 - .../maskrcnn_benchmark/modeling/matcher.py | 125 - .../maskrcnn_benchmark/modeling/poolers.py | 134 - .../maskrcnn_benchmark/modeling/registry.py | 21 - .../modeling/roi_heads/__init__.py | 13 - .../modeling/roi_heads/box_head/__init__.py | 13 - .../modeling/roi_heads/box_head/box_head.py | 83 - .../modeling/roi_heads/box_head/inference.py | 180 - .../modeling/roi_heads/box_head/loss.py | 206 - .../box_head/roi_box_feature_extractors.py | 162 - .../roi_heads/box_head/roi_box_predictors.py | 74 - .../roi_heads/keypoint_head/__init__.py | 13 - .../roi_heads/keypoint_head/inference.py | 138 - .../roi_heads/keypoint_head/keypoint_head.py | 63 - .../modeling/roi_heads/keypoint_head/loss.py | 196 - .../roi_keypoint_feature_extractors.py | 65 - .../keypoint_head/roi_keypoint_predictors.py | 52 - .../modeling/roi_heads/mask_head/__init__.py | 13 - .../modeling/roi_heads/mask_head/inference.py | 217 - .../modeling/roi_heads/mask_head/loss.py | 157 - .../modeling/roi_heads/mask_head/mask_head.py | 95 - .../mask_head/roi_mask_feature_extractors.py | 82 - .../mask_head/roi_mask_predictors.py | 57 - .../modeling/roi_heads/roi_heads.py | 89 - .../modeling/rpn/__init__.py | 15 - .../modeling/rpn/anchor_generator.py | 302 - .../modeling/rpn/inference.py | 215 - .../maskrcnn_benchmark/modeling/rpn/loss.py | 170 - .../modeling/rpn/retinanet/__init__.py | 13 - .../modeling/rpn/retinanet/inference.py | 207 - .../modeling/rpn/retinanet/loss.py | 120 - .../modeling/rpn/retinanet/retinanet.py | 166 - .../maskrcnn_benchmark/modeling/rpn/rpn.py | 161 - .../maskrcnn_benchmark/modeling/rpn/utils.py | 58 - .../maskrcnn_benchmark/modeling/utils.py | 29 - .../maskrcnn_benchmark/solver/__init__.py | 17 - .../maskrcnn_benchmark/solver/build.py | 44 - .../maskrcnn_benchmark/solver/lr_scheduler.py | 72 - .../maskrcnn_benchmark/structures/__init__.py | 13 - .../structures/bounding_box.py | 279 - .../structures/boxlist_ops.py | 141 - .../structures/image_list.py | 83 - .../maskrcnn_benchmark/structures/keypoint.py | 201 - .../structures/segmentation_mask.py | 228 - .../maskrcnn_benchmark/utils/README.md | 5 - .../maskrcnn_benchmark/utils/__init__.py | 13 - .../utils/c2_model_loading.py | 188 - .../maskrcnn_benchmark/utils/checkpoint.py | 152 - .../maskrcnn_benchmark/utils/collect_env.py | 27 - .../maskrcnn_benchmark/utils/cv2_util.py | 37 - .../pytorch/maskrcnn_benchmark/utils/env.py | 50 - .../maskrcnn_benchmark/utils/imports.py | 36 - .../maskrcnn_benchmark/utils/logger.py | 38 - .../maskrcnn_benchmark/utils/miscellaneous.py | 24 - .../maskrcnn_benchmark/utils/mlperf_logger.py | 97 - .../utils/model_serialization.py | 93 - .../maskrcnn_benchmark/utils/model_zoo.py | 70 - .../maskrcnn_benchmark/utils/registry.py | 58 - .../maskrcnn/pytorch/requirements.txt | 2 - .../maskrcnn/pytorch/requirements_aarch64.txt | 2 + cv/detection/maskrcnn/pytorch/run_4cards.sh | 17 - cv/detection/maskrcnn/pytorch/run_8cards.sh | 17 - cv/detection/maskrcnn/pytorch/run_and_time.sh | 24 - cv/detection/maskrcnn/pytorch/run_single.sh | 17 - cv/detection/maskrcnn/pytorch/setup.py | 82 - .../maskrcnn/pytorch/tests/checkpoint.py | 131 - .../pytorch/tests/test_data_samplers.py | 166 - .../pytorch/tests/test_metric_logger.py | 41 - .../cityscapes/convert_cityscapes_to_coco.py | 234 - .../instances2dict_with_polygons.py | 94 - .../maskrcnn/pytorch/tools/test_net.py | 110 - .../maskrcnn/pytorch/tools/train_mlperf.py | 312 - .../maskrcnn/pytorch/tools/train_net.py | 187 - cv/detection/maskrcnn/pytorch/train.py | 265 + cv/detection/maskrcnn/pytorch/utils.py | 23 + cv/detection/retinanet/pytorch/coco_eval.py | 3 +- .../dataloader/utils/transforms_det.py | 19 +- .../retinanet/pytorch/model/backbone_utils.py | 9 - .../retinanet/pytorch/model/mobilenetv3.py | 12 +- .../retinanet/pytorch/model/retinanet.py | 17 +- cv/detection/retinanet/pytorch/train.py | 47 +- .../pytorch/train_retinanet_r50_dist.sh | 2 +- .../base/configs/datasets_/ST_MJ_train.py | 4 +- cv/ocr/satrn/pytorch/base/csrc/.gitignore | 3 +- cv/ocr/satrn/pytorch/base/csrc/build.sh | 30 +- .../base/models/encoders/satrn_encoder.py | 3 +- .../pytorch/base/models/losses/ce_loss.py | 8 + .../base/ocrcv/parallel/distributed.py | 102 +- .../base/ocrcv/parallel/scatter_gather.py | 33 +- .../pytorch/base/ocrcv/runner/dist_utils.py | 2 +- .../base/ocrcv/runner/epoch_based_runner.py | 19 +- .../base/ocrcv/runner/hooks/logger/text.py | 13 +- .../satrn/pytorch/base/openvino_converter.py | 116 - .../pytorch/base/seg_synthtext_converter.py | 89 - cv/ocr/satrn/pytorch/base/svt_converter.py | 83 - .../satrn/pytorch/base/synthtext_converter.py | 144 - .../satrn/pytorch/base/textocr_converter.py | 108 - .../satrn/pytorch/base/totaltext_converter.py | 383 - cv/ocr/satrn/pytorch/base/train.py | 9 +- cv/ocr/satrn/pytorch/base/txt2lmdb.py | 30 - .../configs/coco/w32_512_adam_lr1e-3.yaml | 2 +- cv/pose/hrnet/pytorch/core/trainer.py | 6 + cv/pose/hrnet/pytorch/tools/train.py | 8 + .../pytorch/common_utils/__init__.py | 23 + .../deeplabv3/pytorch/common_utils/dist.py | 144 + .../pytorch/common_utils/metric_logger.py | 94 + .../deeplabv3/pytorch/common_utils/misc.py | 11 + .../pytorch/common_utils/smooth_value.py | 75 + .../deeplabv3/pytorch/dataloader/__init__.py | 0 .../pytorch/dataloader/segmentation.py | 46 + .../pytorch/dataloader/utils/__init__.py | 0 .../pytorch/dataloader/utils/camvid.py | 191 + .../dataloader/utils/coco_seg_utils.py | 115 + .../pytorch/dataloader/utils/coco_utils.py | 255 + .../pytorch/dataloader/utils/pascal_voc.py | 311 + .../dataloader/utils/presets_segmentation.py | 36 + .../dataloader/utils/transforms_seg.py | 96 + .../deeplabv3/pytorch/train.py | 271 + .../pytorch/train_deeplabv3_r50_dist.sh | 4 +- .../deeplabv3/pytorch}/transforms.py | 111 +- .../deeplabv3/pytorch/utils.py | 72 + .../pytorch/dataloader/segmentation.py | 39 +- .../torchvision/pytorch/train.py | 211 +- .../torchvision/pytorch/utils.py | 37 +- .../unet3d/pytorch/main.py | 5 + .../unet3d/pytorch/prepare.sh | 47 +- .../unet3d/pytorch/run_eval.sh | 76 + .../unet3d/pytorch/run_multi.sh | 63 + .../unet3d/pytorch/run_single.sh | 63 + .../unet3d/pytorch/runtime/arguments.py | 2 +- .../unet3d/pytorch/runtime/inference.py | 2 +- cv/tracking/fairmot/pytorch/requirements.txt | 3 +- .../fairmot/pytorch/src/_init_paths.py | 1 + .../fairmot/pytorch/src/gen_labels_17.py | 4 +- .../fairmot/pytorch/src/lib/cfg/mot17.json | 6 +- cv/tracking/fairmot/pytorch/src/lib/logger.py | 46 +- .../lib/models/networks/config/hrnet_w18.yaml | 2 +- .../lib/models/networks/config/hrnet_w32.yaml | 2 +- .../src/lib/models/networks/pose_hrnet.py | 4 +- cv/tracking/fairmot/pytorch/src/lib/opts.py | 51 +- .../pytorch/src/lib/trains/base_trainer.py | 78 +- cv/tracking/fairmot/pytorch/src/track.py | 104 +- cv/tracking/fairmot/pytorch/src/train.py | 194 +- .../fairmot/pytorch/train_dla34_mot17.sh | 28 +- .../fairmot/pytorch/train_hrnet18_mot17.sh | 31 +- .../fairmot/pytorch/train_hrnet32_mot17.sh | 31 +- .../bert/pytorch/requirements.txt | 2 +- .../bert/pytorch/run_pretraining_single.py | 1891 ++ .../bert/pytorch/train_bert_pretraining.sh | 25 + .../train_bert_pretraining_amp_dist.sh | 28 +- nlp/translation/t5/pytorch/init.sh | 15 + .../t5/pytorch/pretrained/README.md | 1 + nlp/translation/t5/pytorch/train.py | 96 +- .../t5/pytorch/wmt14_data/README.md | 1 + .../pytorch/dlrm/config/official_config.json | 2 +- .../ctr/dlrm/pytorch/dlrm/dist_model.py | 29 +- .../ctr/dlrm/pytorch/logging/CONTRIBUTING.md | 9 + .../ctr/dlrm/pytorch/logging/LICENSE.md | 128 + .../ctr/dlrm/pytorch/logging/MANIFEST.in | 3 + .../ctr/dlrm/pytorch/logging/README.md | 46 + .../ctr/dlrm/pytorch/logging/RELEASING.md | 18 + .../ctr/dlrm/pytorch/logging/VERSION | 1 + .../pytorch/logging/log_parsers/README.md | 98 + .../logging/log_parsers/parse_mlperf.py | 905 + .../logging/mlperf_logging/__init__.py | 0 .../logging/mlperf_logging/benchmark_meta.py | 115 + .../compliance_checker/README.md | 181 + .../compliance_checker/__init__.py | 0 .../compliance_checker/__main__.py | 32 + .../hpc_1.0.0/closed_common.yaml | 24 + .../hpc_1.0.0/closed_cosmoflow.yaml | 47 + .../hpc_1.0.0/closed_deepcam.yaml | 80 + .../closed_deepcam_cosine_annealing.yaml | 10 + .../hpc_1.0.0/closed_deepcam_lamb.yaml | 16 + .../hpc_1.0.0/closed_deepcam_multistep.yaml | 10 + .../hpc_1.0.0/closed_oc20.yaml | 39 + .../compliance_checker/hpc_1.0.0/common.yaml | 156 + .../hpc_1.0.0/open_common.yaml | 5 + .../hpc_1.0.0/open_cosmoflow.yaml | 6 + .../hpc_1.0.0/open_deepcam.yaml | 6 + .../hpc_1.0.0/open_oc20.yaml | 6 + .../compliance_checker/mlp_compliance.py | 331 + .../compliance_checker/mlp_parser/__init__.py | 20 + .../mlp_parser/ruleset_060.py | 111 + .../mlp_parser/ruleset_070.py | 104 + .../mlp_parser/ruleset_100.py | 104 + .../mlp_parser/ruleset_110.py | 104 + .../mlp_parser/ruleset_200.py | 104 + .../training_0.6.0/common.yaml | 135 + .../training_0.6.0/gnmt.yaml | 51 + .../training_0.6.0/maskrcnn.yaml | 27 + .../training_0.6.0/minigo.yaml | 147 + .../training_0.6.0/resnet.yaml | 59 + .../training_0.6.0/score.yaml | 20 + .../training_0.6.0/ssd.yaml | 36 + .../training_0.6.0/transformer.yaml | 41 + .../training_0.7.0/closed_bert.yaml | 49 + .../training_0.7.0/closed_common.yaml | 6 + .../training_0.7.0/closed_dlrm.yaml | 36 + .../training_0.7.0/closed_gnmt.yaml | 47 + .../training_0.7.0/closed_maskrcnn.yaml | 57 + .../training_0.7.0/closed_minigo.yaml | 43 + .../training_0.7.0/closed_resnet.yaml | 18 + .../training_0.7.0/closed_resnet_lars.yaml | 37 + .../training_0.7.0/closed_resnet_sgd.yaml | 36 + .../training_0.7.0/closed_ssd.yaml | 45 + .../training_0.7.0/closed_transformer.yaml | 50 + .../training_0.7.0/common.yaml | 154 + .../training_0.7.0/open_bert.yaml | 7 + .../training_0.7.0/open_common.yaml | 6 + .../training_0.7.0/open_dlrm.yaml | 7 + .../training_0.7.0/open_gnmt.yaml | 8 + .../training_0.7.0/open_maskrcnn.yaml | 12 + .../training_0.7.0/open_minigo.yaml | 9 + .../training_0.7.0/open_resnet.yaml | 7 + .../training_0.7.0/open_ssd.yaml | 7 + .../training_0.7.0/open_transformer.yaml | 7 + .../training_0.7.0_warn/bert.yaml | 50 + .../training_0.7.0_warn/common.yaml | 210 + .../training_0.7.0_warn/dlrm.yaml | 17 + .../training_0.7.0_warn/gnmt.yaml | 52 + .../training_0.7.0_warn/maskrcnn.yaml | 56 + .../training_0.7.0_warn/minigo.yaml | 85 + .../training_0.7.0_warn/resnet.yaml | 26 + .../training_0.7.0_warn/resnet_lars.yaml | 27 + .../training_0.7.0_warn/resnet_sgd.yaml | 6 + .../training_0.7.0_warn/ssd.yaml | 41 + .../training_0.7.0_warn/summary/bert.yaml | 55 + .../training_0.7.0_warn/summary/common.yaml | 154 + .../training_0.7.0_warn/summary/dlrm.yaml | 17 + .../training_0.7.0_warn/summary/gnmt.yaml | 52 + .../training_0.7.0_warn/summary/maskrcnn.yaml | 56 + .../training_0.7.0_warn/summary/minigo.yaml | 103 + .../training_0.7.0_warn/summary/resnet.yaml | 77 + .../training_0.7.0_warn/summary/ssd.yaml | 41 + .../summary/transformer.yaml | 44 + .../training_0.7.0_warn/transformer.yaml | 44 + .../training_1.0.0/closed_bert.yaml | 49 + .../training_1.0.0/closed_common.yaml | 11 + .../training_1.0.0/closed_dlrm.yaml | 36 + .../training_1.0.0/closed_maskrcnn.yaml | 57 + .../training_1.0.0/closed_minigo.yaml | 43 + .../training_1.0.0/closed_resnet.yaml | 18 + .../training_1.0.0/closed_resnet_lars.yaml | 37 + .../training_1.0.0/closed_resnet_sgd.yaml | 36 + .../training_1.0.0/closed_rnnt.yaml | 159 + .../training_1.0.0/closed_ssd.yaml | 45 + .../training_1.0.0/closed_unet3d.yaml | 73 + .../training_1.0.0/common.yaml | 149 + .../training_1.0.0/open_bert.yaml | 7 + .../training_1.0.0/open_common.yaml | 7 + .../training_1.0.0/open_dlrm.yaml | 7 + .../training_1.0.0/open_maskrcnn.yaml | 12 + .../training_1.0.0/open_minigo.yaml | 9 + .../training_1.0.0/open_resnet.yaml | 7 + .../training_1.0.0/open_rnnt.yaml | 7 + .../training_1.0.0/open_ssd.yaml | 7 + .../training_1.0.0/open_unet3d.yaml | 7 + .../training_1.1.0/closed_bert.yaml | 463 + .../training_1.1.0/closed_common.yaml | 11 + .../training_1.1.0/closed_dlrm.yaml | 61 + .../training_1.1.0/closed_maskrcnn.yaml | 94 + .../training_1.1.0/closed_minigo.yaml | 108 + .../training_1.1.0/closed_resnet.yaml | 195 + .../training_1.1.0/closed_resnet_lars.yaml | 37 + .../training_1.1.0/closed_resnet_sgd.yaml | 36 + .../training_1.1.0/closed_rnnt.yaml | 159 + .../training_1.1.0/closed_ssd.yaml | 181 + .../training_1.1.0/closed_unet3d.yaml | 140 + .../training_1.1.0/common.yaml | 149 + .../training_1.1.0/open_bert.yaml | 7 + .../training_1.1.0/open_common.yaml | 7 + .../training_1.1.0/open_dlrm.yaml | 7 + .../training_1.1.0/open_maskrcnn.yaml | 12 + .../training_1.1.0/open_minigo.yaml | 9 + .../training_1.1.0/open_resnet.yaml | 7 + .../training_1.1.0/open_rnnt.yaml | 7 + .../training_1.1.0/open_ssd.yaml | 7 + .../training_1.1.0/open_unet3d.yaml | 7 + .../training_2.0.0/closed_bert.yaml | 463 + .../training_2.0.0/closed_common.yaml | 11 + .../training_2.0.0/closed_dlrm.yaml | 61 + .../training_2.0.0/closed_maskrcnn.yaml | 96 + .../training_2.0.0/closed_minigo.yaml | 108 + .../training_2.0.0/closed_resnet.yaml | 195 + .../training_2.0.0/closed_resnet_lars.yaml | 37 + .../training_2.0.0/closed_resnet_sgd.yaml | 36 + .../training_2.0.0/closed_rnnt.yaml | 159 + .../training_2.0.0/closed_ssd.yaml | 141 + .../training_2.0.0/closed_unet3d.yaml | 140 + .../training_2.0.0/common.yaml | 150 + .../training_2.0.0/open_bert.yaml | 7 + .../training_2.0.0/open_common.yaml | 7 + .../training_2.0.0/open_dlrm.yaml | 7 + .../training_2.0.0/open_maskrcnn.yaml | 12 + .../training_2.0.0/open_minigo.yaml | 9 + .../training_2.0.0/open_resnet.yaml | 7 + .../training_2.0.0/open_rnnt.yaml | 7 + .../training_2.0.0/open_ssd.yaml | 7 + .../training_2.0.0/open_unet3d.yaml | 7 + .../logging/mlperf_logging/mllog/README.md | 11 + .../logging/mlperf_logging/mllog/__init__.py | 97 + .../logging/mlperf_logging/mllog/constants.py | 160 + .../mllog/examples}/__init__.py | 3 +- .../mllog/examples/dummy_example.py | 87 + .../logging/mlperf_logging/mllog/mllog.py | 261 + .../mlperf_logging/mllog/test_mllog.py | 133 + .../mlperf_logging/package_checker/README.md | 29 + .../package_checker/__init__.py | 0 .../package_checker/__main__.py | 3 + .../package_checker/package_checker.py | 291 + .../package_checker/seed_checker.py | 162 + .../mlperf_logging/rcp_checker/README.md | 37 + .../mlperf_logging/rcp_checker/__init__.py | 0 .../mlperf_logging/rcp_checker/__main__.py | 27 + .../rcp_checker/hpc_1.0.0/rcps_cosmoflow.json | 66 + .../rcp_checker/hpc_1.0.0/rcps_deepcam.json | 117 + .../rcp_checker/hpc_1.0.0/rcps_oc20.json | 41 + .../mlperf_logging/rcp_checker/rcp_checker.py | 476 + .../rcp_checker/training_1.0.0/rcps_bert.json | 163 + .../rcp_checker/training_1.0.0/rcps_dlrm.json | 59 + .../training_1.0.0/rcps_maskrcnn.json | 113 + .../training_1.0.0/rcps_resnet.json | 192 + .../rcp_checker/training_1.0.0/rcps_rnnt.json | 75 + .../rcp_checker/training_1.0.0/rcps_ssd.json | 63 + .../training_1.0.0/rcps_unet3d.json | 131 + .../rcp_checker/training_1.1.0/rcps_bert.json | 256 + .../rcp_checker/training_1.1.0/rcps_dlrm.json | 65 + .../training_1.1.0/rcps_maskrcnn.json | 113 + .../training_1.1.0/rcps_resnet.json | 197 + .../rcp_checker/training_1.1.0/rcps_rnnt.json | 115 + .../rcp_checker/training_1.1.0/rcps_ssd.json | 93 + .../training_1.1.0/rcps_unet3d.json | 146 + .../rcp_checker/training_2.0.0/rcps_bert.json | 279 + .../rcp_checker/training_2.0.0/rcps_dlrm.json | 65 + .../training_2.0.0/rcps_maskrcnn.json | 113 + .../training_2.0.0/rcps_resnet.json | 197 + .../rcp_checker/training_2.0.0/rcps_rnnt.json | 168 + .../rcp_checker/training_2.0.0/rcps_ssd.json | 57 + .../training_2.0.0/rcps_unet3d.json | 146 + .../mlperf_logging/repo_checker/README.md | 25 + .../mlperf_logging/repo_checker/__init__.py | 0 .../mlperf_logging/repo_checker/__main__.py | 3 + .../repo_checker/repo_checker.py | 115 + .../result_summarizer/README.md | 39 + .../result_summarizer/__init__.py | 0 .../result_summarizer/__main__.py | 3 + .../result_summarizer/result_summarizer.py | 652 + .../system_desc_checker/README.md | 22 + .../system_desc_checker/__init__.py | 0 .../system_desc_checker/__main__.py | 3 + .../system_desc_checker.py | 161 + .../ctr/dlrm/pytorch/logging/requirements.txt | 4 + .../logging/scripts/pack_submission.sh | 86 + .../scripts/verify_for_v0.7_training.sh | 6 + .../logging/scripts/verify_for_v1.0_hpc.sh | 6 + .../scripts/verify_for_v1.0_training.sh | 6 + .../scripts/verify_for_v1.1_training.sh | 6 + .../scripts/verify_for_v2.0_training.sh | 18 + .../ctr/dlrm/pytorch/logging/setup.py | 45 +- .../system_description.yaml | 125 + .../ctr/dlrm/pytorch/scripts/dist_train.py | 171 +- .../ctr/dlrm/pytorch/scripts/train.py | 87 +- .../ljs_audio_text_train_filelist.txt | 15540 ++++------------ .../tacotron2/pytorch/hparam.py | 651 + .../tacotron2/pytorch/hparams.py | 9 +- .../tacotron2/pytorch/layers.py | 2 +- .../tacotron2/pytorch/multiproc.py | 8 +- .../tacotron2/pytorch/requirements.txt | 12 +- .../pytorch/requirements_aarch64.txt | 8 + .../tacotron2/pytorch/stft.py | 2 +- .../tacotron2/pytorch/train.py | 51 +- 668 files changed, 44320 insertions(+), 40768 deletions(-) create mode 100644 cv/classification/efficientnet_b4/pytorch/train_horovod.py create mode 100644 cv/classification/inceptionv3/pytorch/__init__.py create mode 100644 cv/classification/inceptionv3/pytorch/common_utils/__init__.py rename cv/{detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/comm.py => classification/inceptionv3/pytorch/common_utils/dist.py} (36%) rename cv/{detection/maskrcnn/pytorch/maskrcnn_benchmark/utils => classification/inceptionv3/pytorch/common_utils}/metric_logger.py (30%) create mode 100644 cv/classification/inceptionv3/pytorch/common_utils/misc.py create mode 100644 cv/classification/inceptionv3/pytorch/common_utils/smooth_value.py create mode 100644 cv/classification/inceptionv3/pytorch/dataloader/__init__.py create mode 100644 cv/classification/inceptionv3/pytorch/dataloader/classification.py create mode 100644 cv/classification/inceptionv3/pytorch/dataloader/dali_classification.py create mode 100644 cv/classification/inceptionv3/pytorch/dataloader/utils/__init__.py create mode 100644 cv/classification/inceptionv3/pytorch/dataloader/utils/presets_classification.py delete mode 100644 cv/classification/inceptionv3/pytorch/run_train.py create mode 100644 cv/classification/inceptionv3/pytorch/train.py create mode 100644 cv/classification/inceptionv3/pytorch/utils_.py create mode 100644 cv/classification/mobilenetv3/pytorch/__init__.py create mode 100644 cv/classification/mobilenetv3/pytorch/_torchvision/__init__.py create mode 100644 cv/classification/mobilenetv3/pytorch/_torchvision/_internally_replaced_utils.py create mode 100644 cv/classification/mobilenetv3/pytorch/_torchvision/models/__init__.py create mode 100644 cv/classification/mobilenetv3/pytorch/_torchvision/models/_utils.py create mode 100644 cv/classification/mobilenetv3/pytorch/_torchvision/models/mobilenet.py create mode 100644 cv/classification/mobilenetv3/pytorch/_torchvision/models/mobilenetv2.py create mode 100644 cv/classification/mobilenetv3/pytorch/_torchvision/models/mobilenetv3.py create mode 100644 cv/classification/mobilenetv3/pytorch/_torchvision/ops/__init__.py create mode 100644 cv/classification/mobilenetv3/pytorch/_torchvision/ops/misc.py create mode 100644 cv/classification/mobilenetv3/pytorch/_torchvision/ops/stochastic_depth.py create mode 100644 cv/classification/mobilenetv3/pytorch/_torchvision/utils.py create mode 100644 cv/classification/mobilenetv3/pytorch/common_utils/__init__.py create mode 100644 cv/classification/mobilenetv3/pytorch/common_utils/dist.py create mode 100644 cv/classification/mobilenetv3/pytorch/common_utils/loss.py create mode 100644 cv/classification/mobilenetv3/pytorch/common_utils/metric_logger.py create mode 100644 cv/classification/mobilenetv3/pytorch/common_utils/misc.py create mode 100644 cv/classification/mobilenetv3/pytorch/common_utils/smooth_value.py create mode 100644 cv/classification/mobilenetv3/pytorch/dataloader/__init__.py create mode 100644 cv/classification/mobilenetv3/pytorch/dataloader/classification.py create mode 100644 cv/classification/mobilenetv3/pytorch/dataloader/dali_classification.py create mode 100644 cv/classification/mobilenetv3/pytorch/dataloader/utils/__init__.py create mode 100644 cv/classification/mobilenetv3/pytorch/dataloader/utils/presets_classification.py delete mode 100644 cv/classification/mobilenetv3/pytorch/run_train.py create mode 100644 cv/classification/mobilenetv3/pytorch/train.py create mode 100644 cv/classification/mobilenetv3/pytorch/train_horovod.py create mode 100644 cv/classification/mobilenetv3/pytorch/utils.py create mode 100644 cv/classification/resnet101/pytorch/.gitignore create mode 100644 cv/classification/resnet101/pytorch/__init__.py create mode 100644 cv/classification/resnet101/pytorch/common_utils/__init__.py create mode 100644 cv/classification/resnet101/pytorch/common_utils/dist.py create mode 100644 cv/classification/resnet101/pytorch/common_utils/metric_logger.py create mode 100644 cv/classification/resnet101/pytorch/common_utils/misc.py create mode 100644 cv/classification/resnet101/pytorch/common_utils/smooth_value.py create mode 100644 cv/classification/resnet101/pytorch/dataloader/__init__.py create mode 100644 cv/classification/resnet101/pytorch/dataloader/classification.py create mode 100644 cv/classification/resnet101/pytorch/dataloader/dali_classification.py create mode 100644 cv/classification/resnet101/pytorch/dataloader/utils/__init__.py create mode 100644 cv/classification/resnet101/pytorch/dataloader/utils/presets_classification.py create mode 100644 cv/classification/resnet101/pytorch/start_scripts/get_num_devices.sh create mode 100644 cv/classification/resnet101/pytorch/start_scripts/init_torch.sh create mode 100644 cv/classification/resnet101/pytorch/start_scripts/train_resnet50_amp_torch.sh create mode 100644 cv/classification/resnet101/pytorch/start_scripts/train_resnet50_dist_torch.sh create mode 100644 cv/classification/resnet101/pytorch/start_scripts/train_resnet50_torch.sh create mode 100644 cv/classification/resnet101/pytorch/train.py create mode 100644 cv/classification/resnet101/pytorch/train_horovod.py create mode 100644 cv/classification/resnet101/pytorch/utils_.py create mode 100644 cv/classification/resnet50/pytorch/start_scripts/get_num_devices.sh create mode 100644 cv/classification/resnet50/pytorch/start_scripts/init_torch.sh create mode 100644 cv/classification/resnet50/pytorch/start_scripts/train_resnet50_amp_torch.sh create mode 100644 cv/classification/resnet50/pytorch/start_scripts/train_resnet50_dist_torch.sh create mode 100644 cv/classification/resnet50/pytorch/start_scripts/train_resnet50_torch.sh create mode 100644 cv/classification/resnet50/pytorch/train_horovod.py create mode 100644 cv/classification/resnet50/pytorch/utils_.py create mode 100644 cv/classification/resnext101_32x8d/pytorch/__init__.py delete mode 100644 cv/classification/resnext101_32x8d/pytorch/common.py create mode 100644 cv/classification/resnext101_32x8d/pytorch/common_utils/__init__.py create mode 100644 cv/classification/resnext101_32x8d/pytorch/common_utils/dist.py create mode 100644 cv/classification/resnext101_32x8d/pytorch/common_utils/metric_logger.py create mode 100644 cv/classification/resnext101_32x8d/pytorch/common_utils/misc.py create mode 100644 cv/classification/resnext101_32x8d/pytorch/common_utils/smooth_value.py create mode 100644 cv/classification/resnext101_32x8d/pytorch/dataloader/__init__.py create mode 100644 cv/classification/resnext101_32x8d/pytorch/dataloader/classification.py create mode 100644 cv/classification/resnext101_32x8d/pytorch/dataloader/dali_classification.py create mode 100644 cv/classification/resnext101_32x8d/pytorch/dataloader/utils/__init__.py create mode 100644 cv/classification/resnext101_32x8d/pytorch/dataloader/utils/presets_classification.py delete mode 100644 cv/classification/resnext101_32x8d/pytorch/resnet.py delete mode 100644 cv/classification/resnext101_32x8d/pytorch/resnext.py delete mode 100644 cv/classification/resnext101_32x8d/pytorch/run_train.py create mode 100644 cv/classification/resnext101_32x8d/pytorch/train.py create mode 100644 cv/classification/resnext101_32x8d/pytorch/utils_.py create mode 100644 cv/classification/resnext50_32x4d/pytorch/__init__.py delete mode 100644 cv/classification/resnext50_32x4d/pytorch/common.py create mode 100644 cv/classification/resnext50_32x4d/pytorch/common_utils/__init__.py create mode 100644 cv/classification/resnext50_32x4d/pytorch/common_utils/dist.py create mode 100644 cv/classification/resnext50_32x4d/pytorch/common_utils/metric_logger.py create mode 100644 cv/classification/resnext50_32x4d/pytorch/common_utils/misc.py create mode 100644 cv/classification/resnext50_32x4d/pytorch/common_utils/smooth_value.py create mode 100644 cv/classification/resnext50_32x4d/pytorch/dataloader/__init__.py create mode 100644 cv/classification/resnext50_32x4d/pytorch/dataloader/classification.py create mode 100644 cv/classification/resnext50_32x4d/pytorch/dataloader/dali_classification.py create mode 100644 cv/classification/resnext50_32x4d/pytorch/dataloader/utils/__init__.py create mode 100644 cv/classification/resnext50_32x4d/pytorch/dataloader/utils/presets_classification.py delete mode 100644 cv/classification/resnext50_32x4d/pytorch/resnet.py delete mode 100644 cv/classification/resnext50_32x4d/pytorch/resnext.py delete mode 100644 cv/classification/resnext50_32x4d/pytorch/run_train.py create mode 100644 cv/classification/resnext50_32x4d/pytorch/train.py create mode 100644 cv/classification/resnext50_32x4d/pytorch/utils_.py create mode 100644 cv/classification/seresnext/pytorch/__init__.py delete mode 100644 cv/classification/seresnext/pytorch/common.py create mode 100644 cv/classification/seresnext/pytorch/common_utils/__init__.py create mode 100644 cv/classification/seresnext/pytorch/common_utils/dist.py create mode 100644 cv/classification/seresnext/pytorch/common_utils/metric_logger.py create mode 100644 cv/classification/seresnext/pytorch/common_utils/misc.py create mode 100644 cv/classification/seresnext/pytorch/common_utils/smooth_value.py create mode 100644 cv/classification/seresnext/pytorch/dataloader/__init__.py create mode 100644 cv/classification/seresnext/pytorch/dataloader/classification.py create mode 100644 cv/classification/seresnext/pytorch/dataloader/dali_classification.py create mode 100644 cv/classification/seresnext/pytorch/dataloader/utils/__init__.py create mode 100644 cv/classification/seresnext/pytorch/dataloader/utils/presets_classification.py delete mode 100644 cv/classification/seresnext/pytorch/resnet.py delete mode 100644 cv/classification/seresnext/pytorch/resnext.py delete mode 100644 cv/classification/seresnext/pytorch/run_train.py create mode 100644 cv/classification/seresnext/pytorch/se_block.py create mode 100644 cv/classification/seresnext/pytorch/train.py rename cv/classification/seresnext/pytorch/{train_seresnext101_32x4d_amp_dist.sh => train_seresnext101_32x8d_amp_dist.sh} (86%) mode change 100755 => 100644 create mode 100644 cv/classification/seresnext/pytorch/utils_.py create mode 100644 cv/classification/shufflenetv2/pytorch/__init__.py create mode 100644 cv/classification/shufflenetv2/pytorch/common_utils/__init__.py create mode 100644 cv/classification/shufflenetv2/pytorch/common_utils/dist.py create mode 100644 cv/classification/shufflenetv2/pytorch/common_utils/metric_logger.py create mode 100644 cv/classification/shufflenetv2/pytorch/common_utils/misc.py create mode 100644 cv/classification/shufflenetv2/pytorch/common_utils/smooth_value.py create mode 100644 cv/classification/shufflenetv2/pytorch/dataloader/__init__.py create mode 100644 cv/classification/shufflenetv2/pytorch/dataloader/classification.py create mode 100644 cv/classification/shufflenetv2/pytorch/dataloader/dali_classification.py create mode 100644 cv/classification/shufflenetv2/pytorch/dataloader/utils/__init__.py create mode 100644 cv/classification/shufflenetv2/pytorch/dataloader/utils/presets_classification.py delete mode 100644 cv/classification/shufflenetv2/pytorch/run_train.py create mode 100644 cv/classification/shufflenetv2/pytorch/train.py rename cv/classification/shufflenetv2/pytorch/{train_shufflenet_v2_x2_0_amp_dist.sh => train_shufflenet_v2_x0_5_amp_dist.sh} (77%) mode change 100755 => 100644 create mode 100644 cv/classification/shufflenetv2/pytorch/utils_.py create mode 100644 cv/classification/vgg/pytorch/__init__.py create mode 100644 cv/classification/vgg/pytorch/common_utils/__init__.py create mode 100644 cv/classification/vgg/pytorch/common_utils/dist.py create mode 100644 cv/classification/vgg/pytorch/common_utils/metric_logger.py create mode 100644 cv/classification/vgg/pytorch/common_utils/misc.py create mode 100644 cv/classification/vgg/pytorch/common_utils/smooth_value.py create mode 100644 cv/classification/vgg/pytorch/dataloader/__init__.py create mode 100644 cv/classification/vgg/pytorch/dataloader/classification.py create mode 100644 cv/classification/vgg/pytorch/dataloader/dali_classification.py create mode 100644 cv/classification/vgg/pytorch/dataloader/utils/__init__.py create mode 100644 cv/classification/vgg/pytorch/dataloader/utils/presets_classification.py delete mode 100644 cv/classification/vgg/pytorch/run_train.py create mode 100644 cv/classification/vgg/pytorch/train.py create mode 100644 cv/classification/vgg/pytorch/utils_.py create mode 100644 cv/detection/fasterrcnn/pytorch/dataloaders/functional.py create mode 100644 cv/detection/fasterrcnn/pytorch/dataloaders/functional_pil.py create mode 100644 cv/detection/fasterrcnn/pytorch/dataloaders/functional_tensor.py create mode 100644 cv/detection/fasterrcnn/pytorch/requirements.txt create mode 100644 cv/detection/fasterrcnn/pytorch/utils/misc.py delete mode 100644 cv/detection/maskrcnn/pytorch/ABSTRACTIONS.md delete mode 100644 cv/detection/maskrcnn/pytorch/CODE_OF_CONDUCT.md delete mode 100644 cv/detection/maskrcnn/pytorch/CONTRIBUTING.md delete mode 100644 cv/detection/maskrcnn/pytorch/Dockerfile delete mode 100644 cv/detection/maskrcnn/pytorch/INSTALL.md delete mode 100644 cv/detection/maskrcnn/pytorch/LICENSE delete mode 100644 cv/detection/maskrcnn/pytorch/MODEL_ZOO.md delete mode 100644 cv/detection/maskrcnn/pytorch/TROUBLESHOOTING.md create mode 100644 cv/detection/maskrcnn/pytorch/__init__.py create mode 100644 cv/detection/maskrcnn/pytorch/coco_eval.py create mode 100644 cv/detection/maskrcnn/pytorch/common_utils/__init__.py create mode 100644 cv/detection/maskrcnn/pytorch/common_utils/dist.py create mode 100644 cv/detection/maskrcnn/pytorch/common_utils/metric_logger.py create mode 100644 cv/detection/maskrcnn/pytorch/common_utils/misc.py create mode 100644 cv/detection/maskrcnn/pytorch/common_utils/smooth_value.py delete mode 100644 cv/detection/maskrcnn/pytorch/configs/e2e_mask_rcnn_R_50_FPN_1x.yaml create mode 100644 cv/detection/maskrcnn/pytorch/dataloader/__init__.py create mode 100644 cv/detection/maskrcnn/pytorch/dataloader/detection.py create mode 100644 cv/detection/maskrcnn/pytorch/dataloader/segmentation.py create mode 100644 cv/detection/maskrcnn/pytorch/dataloader/utils/__init__.py create mode 100644 cv/detection/maskrcnn/pytorch/dataloader/utils/camvid.py create mode 100644 cv/detection/maskrcnn/pytorch/dataloader/utils/coco_utils.py create mode 100644 cv/detection/maskrcnn/pytorch/dataloader/utils/functional.py create mode 100644 cv/detection/maskrcnn/pytorch/dataloader/utils/functional_pil.py create mode 100644 cv/detection/maskrcnn/pytorch/dataloader/utils/functional_tensor.py create mode 100644 cv/detection/maskrcnn/pytorch/dataloader/utils/pascal_voc.py create mode 100644 cv/detection/maskrcnn/pytorch/dataloader/utils/presets_detection.py create mode 100644 cv/detection/maskrcnn/pytorch/dataloader/utils/transforms_det.py delete mode 100644 cv/detection/maskrcnn/pytorch/demo/README.md delete mode 100644 cv/detection/maskrcnn/pytorch/demo/demo_e2e_mask_rcnn_R_50_FPN_1x.png delete mode 100644 cv/detection/maskrcnn/pytorch/demo/demo_e2e_mask_rcnn_X_101_32x8d_FPN_1x.png delete mode 100644 cv/detection/maskrcnn/pytorch/demo/predictor.py delete mode 100644 cv/detection/maskrcnn/pytorch/demo/webcam.py delete mode 100644 cv/detection/maskrcnn/pytorch/docker/Dockerfile delete mode 100644 cv/detection/maskrcnn/pytorch/docker/docker-jupyter/Dockerfile delete mode 100644 cv/detection/maskrcnn/pytorch/docker/docker-jupyter/jupyter_notebook_config.py delete mode 100644 cv/detection/maskrcnn/pytorch/download_dataset.sh create mode 100644 cv/detection/maskrcnn/pytorch/engine.py create mode 100644 cv/detection/maskrcnn/pytorch/generalized_rcnn_transform.py create mode 100644 cv/detection/maskrcnn/pytorch/group_by_aspect_ratio.py delete mode 100644 cv/detection/maskrcnn/pytorch/install.sh delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/config/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/config/defaults.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/config/paths_catalog.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/csrc/ROIAlign.h delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/csrc/ROIPool.h delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/csrc/SigmoidFocalLoss.h delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/csrc/cpu/ROIAlign_cpu.cpp delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/csrc/cpu/nms_cpu.cpp delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/csrc/cpu/vision.h delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/csrc/cuda/ROIAlign_cuda.cu delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/csrc/cuda/ROIPool_cuda.cu delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/csrc/cuda/SigmoidFocalLoss_cuda.cu delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/csrc/cuda/nms.cu delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/csrc/cuda/vision.h delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/csrc/nms.h delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/csrc/vision.cpp delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/README.md delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/build.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/collate_batch.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/datasets/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/datasets/coco.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/datasets/concat_dataset.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/datasets/evaluation/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/datasets/evaluation/coco/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/datasets/evaluation/coco/coco_eval.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/datasets/evaluation/voc/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/datasets/evaluation/voc/voc_eval.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/datasets/list_dataset.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/datasets/voc.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/samplers/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/samplers/distributed.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/samplers/grouped_batch_sampler.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/samplers/iteration_based_batch_sampler.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/transforms/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/data/transforms/build.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/engine/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/engine/inference.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/engine/tester.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/engine/trainer.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/layers/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/layers/_utils.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/layers/batch_norm.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/layers/misc.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/layers/nms.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/layers/roi_align.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/layers/roi_pool.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/layers/sigmoid_focal_loss.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/backbone/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/backbone/backbone.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/backbone/fpn.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/backbone/resnet.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/balanced_positive_negative_sampler.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/box_coder.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/detector/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/detector/detectors.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/detector/generalized_rcnn.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/make_layers.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/matcher.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/poolers.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/registry.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/box_head/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/box_head/box_head.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/box_head/inference.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/box_head/loss.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/box_head/roi_box_feature_extractors.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/box_head/roi_box_predictors.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/keypoint_head/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/keypoint_head/inference.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/keypoint_head/keypoint_head.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/keypoint_head/loss.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/keypoint_head/roi_keypoint_feature_extractors.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/keypoint_head/roi_keypoint_predictors.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/mask_head/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/mask_head/inference.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/mask_head/loss.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/mask_head/mask_head.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/mask_head/roi_mask_feature_extractors.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/mask_head/roi_mask_predictors.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/roi_heads/roi_heads.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/rpn/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/rpn/anchor_generator.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/rpn/inference.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/rpn/loss.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/rpn/retinanet/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/rpn/retinanet/inference.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/rpn/retinanet/loss.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/rpn/retinanet/retinanet.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/rpn/rpn.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/rpn/utils.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling/utils.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/solver/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/solver/build.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/solver/lr_scheduler.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/structures/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/structures/bounding_box.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/structures/boxlist_ops.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/structures/image_list.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/structures/keypoint.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/structures/segmentation_mask.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/README.md delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/__init__.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/c2_model_loading.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/checkpoint.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/collect_env.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/cv2_util.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/env.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/imports.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/logger.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/miscellaneous.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/mlperf_logger.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/model_serialization.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/model_zoo.py delete mode 100644 cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/registry.py delete mode 100644 cv/detection/maskrcnn/pytorch/requirements.txt create mode 100644 cv/detection/maskrcnn/pytorch/requirements_aarch64.txt delete mode 100644 cv/detection/maskrcnn/pytorch/run_4cards.sh delete mode 100644 cv/detection/maskrcnn/pytorch/run_8cards.sh delete mode 100644 cv/detection/maskrcnn/pytorch/run_and_time.sh delete mode 100644 cv/detection/maskrcnn/pytorch/run_single.sh delete mode 100644 cv/detection/maskrcnn/pytorch/setup.py delete mode 100644 cv/detection/maskrcnn/pytorch/tests/checkpoint.py delete mode 100644 cv/detection/maskrcnn/pytorch/tests/test_data_samplers.py delete mode 100644 cv/detection/maskrcnn/pytorch/tests/test_metric_logger.py delete mode 100644 cv/detection/maskrcnn/pytorch/tools/cityscapes/convert_cityscapes_to_coco.py delete mode 100644 cv/detection/maskrcnn/pytorch/tools/cityscapes/instances2dict_with_polygons.py delete mode 100644 cv/detection/maskrcnn/pytorch/tools/test_net.py delete mode 100644 cv/detection/maskrcnn/pytorch/tools/train_mlperf.py delete mode 100644 cv/detection/maskrcnn/pytorch/tools/train_net.py create mode 100644 cv/detection/maskrcnn/pytorch/train.py create mode 100644 cv/detection/maskrcnn/pytorch/utils.py delete mode 100755 cv/ocr/satrn/pytorch/base/openvino_converter.py delete mode 100755 cv/ocr/satrn/pytorch/base/seg_synthtext_converter.py delete mode 100755 cv/ocr/satrn/pytorch/base/svt_converter.py delete mode 100755 cv/ocr/satrn/pytorch/base/synthtext_converter.py delete mode 100755 cv/ocr/satrn/pytorch/base/textocr_converter.py delete mode 100755 cv/ocr/satrn/pytorch/base/totaltext_converter.py delete mode 100755 cv/ocr/satrn/pytorch/base/txt2lmdb.py create mode 100644 cv/semantic_segmentation/deeplabv3/pytorch/common_utils/__init__.py create mode 100644 cv/semantic_segmentation/deeplabv3/pytorch/common_utils/dist.py create mode 100644 cv/semantic_segmentation/deeplabv3/pytorch/common_utils/metric_logger.py create mode 100644 cv/semantic_segmentation/deeplabv3/pytorch/common_utils/misc.py create mode 100644 cv/semantic_segmentation/deeplabv3/pytorch/common_utils/smooth_value.py create mode 100644 cv/semantic_segmentation/deeplabv3/pytorch/dataloader/__init__.py create mode 100644 cv/semantic_segmentation/deeplabv3/pytorch/dataloader/segmentation.py create mode 100644 cv/semantic_segmentation/deeplabv3/pytorch/dataloader/utils/__init__.py create mode 100644 cv/semantic_segmentation/deeplabv3/pytorch/dataloader/utils/camvid.py create mode 100644 cv/semantic_segmentation/deeplabv3/pytorch/dataloader/utils/coco_seg_utils.py create mode 100644 cv/semantic_segmentation/deeplabv3/pytorch/dataloader/utils/coco_utils.py create mode 100644 cv/semantic_segmentation/deeplabv3/pytorch/dataloader/utils/pascal_voc.py create mode 100644 cv/semantic_segmentation/deeplabv3/pytorch/dataloader/utils/presets_segmentation.py create mode 100644 cv/semantic_segmentation/deeplabv3/pytorch/dataloader/utils/transforms_seg.py create mode 100644 cv/semantic_segmentation/deeplabv3/pytorch/train.py rename cv/{detection/maskrcnn/pytorch/maskrcnn_benchmark/data/transforms => semantic_segmentation/deeplabv3/pytorch}/transforms.py (31%) create mode 100644 cv/semantic_segmentation/deeplabv3/pytorch/utils.py create mode 100644 cv/semantic_segmentation/unet3d/pytorch/run_eval.sh create mode 100644 cv/semantic_segmentation/unet3d/pytorch/run_multi.sh create mode 100644 cv/semantic_segmentation/unet3d/pytorch/run_single.sh create mode 100644 nlp/language_model/bert/pytorch/run_pretraining_single.py create mode 100644 nlp/language_model/bert/pytorch/train_bert_pretraining.sh create mode 100644 nlp/translation/t5/pytorch/init.sh create mode 100644 nlp/translation/t5/pytorch/pretrained/README.md create mode 100644 nlp/translation/t5/pytorch/wmt14_data/README.md create mode 100644 recommendation/ctr/dlrm/pytorch/logging/CONTRIBUTING.md create mode 100644 recommendation/ctr/dlrm/pytorch/logging/LICENSE.md create mode 100644 recommendation/ctr/dlrm/pytorch/logging/MANIFEST.in create mode 100644 recommendation/ctr/dlrm/pytorch/logging/README.md create mode 100644 recommendation/ctr/dlrm/pytorch/logging/RELEASING.md create mode 100644 recommendation/ctr/dlrm/pytorch/logging/VERSION create mode 100644 recommendation/ctr/dlrm/pytorch/logging/log_parsers/README.md create mode 100644 recommendation/ctr/dlrm/pytorch/logging/log_parsers/parse_mlperf.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/__init__.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/benchmark_meta.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/README.md create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/__init__.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/__main__.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/hpc_1.0.0/closed_common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/hpc_1.0.0/closed_cosmoflow.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/hpc_1.0.0/closed_deepcam.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/hpc_1.0.0/closed_deepcam_cosine_annealing.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/hpc_1.0.0/closed_deepcam_lamb.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/hpc_1.0.0/closed_deepcam_multistep.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/hpc_1.0.0/closed_oc20.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/hpc_1.0.0/common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/hpc_1.0.0/open_common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/hpc_1.0.0/open_cosmoflow.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/hpc_1.0.0/open_deepcam.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/hpc_1.0.0/open_oc20.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/mlp_compliance.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/mlp_parser/__init__.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/mlp_parser/ruleset_060.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/mlp_parser/ruleset_070.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/mlp_parser/ruleset_100.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/mlp_parser/ruleset_110.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/mlp_parser/ruleset_200.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.6.0/common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.6.0/gnmt.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.6.0/maskrcnn.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.6.0/minigo.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.6.0/resnet.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.6.0/score.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.6.0/ssd.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.6.0/transformer.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/closed_bert.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/closed_common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/closed_dlrm.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/closed_gnmt.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/closed_maskrcnn.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/closed_minigo.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/closed_resnet.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/closed_resnet_lars.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/closed_resnet_sgd.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/closed_ssd.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/closed_transformer.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/open_bert.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/open_common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/open_dlrm.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/open_gnmt.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/open_maskrcnn.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/open_minigo.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/open_resnet.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/open_ssd.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0/open_transformer.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/bert.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/dlrm.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/gnmt.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/maskrcnn.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/minigo.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/resnet.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/resnet_lars.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/resnet_sgd.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/ssd.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/summary/bert.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/summary/common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/summary/dlrm.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/summary/gnmt.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/summary/maskrcnn.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/summary/minigo.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/summary/resnet.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/summary/ssd.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/summary/transformer.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_0.7.0_warn/transformer.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/closed_bert.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/closed_common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/closed_dlrm.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/closed_maskrcnn.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/closed_minigo.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/closed_resnet.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/closed_resnet_lars.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/closed_resnet_sgd.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/closed_rnnt.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/closed_ssd.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/closed_unet3d.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/open_bert.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/open_common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/open_dlrm.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/open_maskrcnn.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/open_minigo.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/open_resnet.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/open_rnnt.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/open_ssd.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.0.0/open_unet3d.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/closed_bert.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/closed_common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/closed_dlrm.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/closed_maskrcnn.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/closed_minigo.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/closed_resnet.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/closed_resnet_lars.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/closed_resnet_sgd.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/closed_rnnt.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/closed_ssd.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/closed_unet3d.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/open_bert.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/open_common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/open_dlrm.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/open_maskrcnn.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/open_minigo.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/open_resnet.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/open_rnnt.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/open_ssd.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_1.1.0/open_unet3d.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/closed_bert.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/closed_common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/closed_dlrm.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/closed_maskrcnn.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/closed_minigo.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/closed_resnet.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/closed_resnet_lars.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/closed_resnet_sgd.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/closed_rnnt.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/closed_ssd.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/closed_unet3d.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/open_bert.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/open_common.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/open_dlrm.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/open_maskrcnn.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/open_minigo.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/open_resnet.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/open_rnnt.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/open_ssd.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/compliance_checker/training_2.0.0/open_unet3d.yaml create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/mllog/README.md create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/mllog/__init__.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/mllog/constants.py rename {cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/modeling => recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/mllog/examples}/__init__.py (79%) create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/mllog/examples/dummy_example.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/mllog/mllog.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/mllog/test_mllog.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/package_checker/README.md create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/package_checker/__init__.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/package_checker/__main__.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/package_checker/package_checker.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/package_checker/seed_checker.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/README.md create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/__init__.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/__main__.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/hpc_1.0.0/rcps_cosmoflow.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/hpc_1.0.0/rcps_deepcam.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/hpc_1.0.0/rcps_oc20.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/rcp_checker.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_1.0.0/rcps_bert.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_1.0.0/rcps_dlrm.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_1.0.0/rcps_maskrcnn.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_1.0.0/rcps_resnet.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_1.0.0/rcps_rnnt.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_1.0.0/rcps_ssd.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_1.0.0/rcps_unet3d.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_1.1.0/rcps_bert.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_1.1.0/rcps_dlrm.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_1.1.0/rcps_maskrcnn.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_1.1.0/rcps_resnet.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_1.1.0/rcps_rnnt.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_1.1.0/rcps_ssd.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_1.1.0/rcps_unet3d.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_2.0.0/rcps_bert.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_2.0.0/rcps_dlrm.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_2.0.0/rcps_maskrcnn.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_2.0.0/rcps_resnet.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_2.0.0/rcps_rnnt.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_2.0.0/rcps_ssd.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/rcp_checker/training_2.0.0/rcps_unet3d.json create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/repo_checker/README.md create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/repo_checker/__init__.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/repo_checker/__main__.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/repo_checker/repo_checker.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/result_summarizer/README.md create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/result_summarizer/__init__.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/result_summarizer/__main__.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/result_summarizer/result_summarizer.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/system_desc_checker/README.md create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/system_desc_checker/__init__.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/system_desc_checker/__main__.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/mlperf_logging/system_desc_checker/system_desc_checker.py create mode 100644 recommendation/ctr/dlrm/pytorch/logging/requirements.txt create mode 100755 recommendation/ctr/dlrm/pytorch/logging/scripts/pack_submission.sh create mode 100755 recommendation/ctr/dlrm/pytorch/logging/scripts/verify_for_v0.7_training.sh create mode 100755 recommendation/ctr/dlrm/pytorch/logging/scripts/verify_for_v1.0_hpc.sh create mode 100755 recommendation/ctr/dlrm/pytorch/logging/scripts/verify_for_v1.0_training.sh create mode 100755 recommendation/ctr/dlrm/pytorch/logging/scripts/verify_for_v1.1_training.sh create mode 100755 recommendation/ctr/dlrm/pytorch/logging/scripts/verify_for_v2.0_training.sh rename cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/layers/smooth_l1_loss.py => recommendation/ctr/dlrm/pytorch/logging/setup.py (34%) create mode 100644 recommendation/ctr/dlrm/pytorch/logging/system_description/system_description.yaml create mode 100644 speech/speech_synthesis/tacotron2/pytorch/hparam.py create mode 100755 speech/speech_synthesis/tacotron2/pytorch/requirements_aarch64.txt diff --git a/cv/classification/alexnet/pytorch/start_scripts/train_alexnet_dist_torch.sh b/cv/classification/alexnet/pytorch/start_scripts/train_alexnet_dist_torch.sh index 0217d2ad2..ce98a6b2f 100644 --- a/cv/classification/alexnet/pytorch/start_scripts/train_alexnet_dist_torch.sh +++ b/cv/classification/alexnet/pytorch/start_scripts/train_alexnet_dist_torch.sh @@ -1,21 +1,23 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +#!/bin/bash +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. # All Rights Reserved. # -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. source ./get_num_devices.sh python3 -m torch.distributed.launch --nproc_per_node=$IX_NUM_CUDA_VISIBLE_DEVICES --use_env \ ../train.py \ +--data-path /home/datasets/cv/imagenet \ --batch-size 256 \ --lr 1e-2 \ --wd 0.0001 \ diff --git a/cv/classification/alexnet/pytorch/start_scripts/train_alexnet_torch.sh b/cv/classification/alexnet/pytorch/start_scripts/train_alexnet_torch.sh index 43c07a34d..a24350f53 100644 --- a/cv/classification/alexnet/pytorch/start_scripts/train_alexnet_torch.sh +++ b/cv/classification/alexnet/pytorch/start_scripts/train_alexnet_torch.sh @@ -1,18 +1,20 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +#!/bin/bash +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. # All Rights Reserved. # -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. python3 ../train.py \ +--data-path /home/datasets/cv/imagenet \ --batch-size 256 \ "$@" diff --git a/cv/classification/efficientnet_b4/pytorch/train.py b/cv/classification/efficientnet_b4/pytorch/train.py index 9ba7b468b..98e92eba3 100755 --- a/cv/classification/efficientnet_b4/pytorch/train.py +++ b/cv/classification/efficientnet_b4/pytorch/train.py @@ -1,6 +1,6 @@ -# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. # Copyright (c) 2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. # All Rights Reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. import datetime import os @@ -201,7 +201,6 @@ def main(args): print("Start training") start_time = time.time() - best_acc = 0 for epoch in range(args.start_epoch, args.epochs): epoch_start_time = time.time() if args.distributed and not args.dali: @@ -212,21 +211,23 @@ def main(args): if acc_avg > args.acc_thresh: print("The accuracy has been exceeded {},and the training is terminated at epoch {}".format(args.acc_thresh, epoch)) return - if acc_avg > best_acc: - if args.output_dir: - checkpoint = { - 'model': model_without_ddp.state_dict(), - 'optimizer': optimizer.state_dict(), - 'lr_scheduler': lr_scheduler.state_dict(), - 'epoch': epoch, - 'args': args} - utils.save_on_master( - checkpoint, - os.path.join(args.output_dir, 'model_best.pth')) - epoch_total_time = time.time() - epoch_start_time - epoch_total_time_str = str(datetime.timedelta(seconds=int(epoch_total_time))) - print('epoch time {}'.format(epoch_total_time_str)) - best_acc = acc_avg + + if args.output_dir: + checkpoint = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'lr_scheduler': lr_scheduler.state_dict(), + 'epoch': epoch, + 'args': args} + utils.save_on_master( + checkpoint, + os.path.join(args.output_dir, 'model_{}.pth'.format(epoch))) + utils.save_on_master( + checkpoint, + os.path.join(args.output_dir, 'checkpoint.pth')) + epoch_total_time = time.time() - epoch_start_time + epoch_total_time_str = str(datetime.timedelta(seconds=int(epoch_total_time))) + print('epoch time {}'.format(epoch_total_time_str)) if args.dali: data_loader.reset() diff --git a/cv/classification/efficientnet_b4/pytorch/train_horovod.py b/cv/classification/efficientnet_b4/pytorch/train_horovod.py new file mode 100644 index 000000000..a119f6d1c --- /dev/null +++ b/cv/classification/efficientnet_b4/pytorch/train_horovod.py @@ -0,0 +1,306 @@ +import torch +import argparse +import torch.backends.cudnn as cudnn +import torch.multiprocessing as mp +import torch.nn.functional as F +import torch.optim as optim +import torch.utils.data.distributed +from torch.utils.tensorboard import SummaryWriter +from torchvision import datasets, transforms, models +import horovod.torch as hvd +import os +import math +from tqdm import tqdm + +# Training settings +parser = argparse.ArgumentParser(description='PyTorch ImageNet Example', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument('--train-dir', default=os.path.expanduser('~/imagenet/train'), + help='path to training data') +parser.add_argument('--val-dir', default=os.path.expanduser('~/imagenet/validation'), + help='path to validation data') +parser.add_argument('--output-dir', default='./work_dirs', + help='tensorboard log directory') +parser.add_argument('--device', default='cuda', help='device') +parser.add_argument('--checkpoint-format', default='checkpoint-{epoch}.pth.tar', + help='checkpoint file format') +parser.add_argument('--fp16-allreduce', action='store_true', default=False, + help='use fp16 compression during allreduce') +parser.add_argument('--batches-per-allreduce', type=int, default=1, + help='number of batches processed locally before ' + 'executing allreduce across workers; it multiplies ' + 'total batch size.') +parser.add_argument('--use-adasum', action='store_true', default=False, + help='use adasum algorithm to do reduction') +parser.add_argument('--gradient-predivide-factor', type=float, default=1.0, + help='apply gradient predivide factor in optimizer (default: 1.0)') +parser.add_argument('--acc-thresh', default=75.0, type=float, + help='accuracy threshold') + +# Default settings from https://arxiv.org/abs/1706.02677. +parser.add_argument('--batch-size', type=int, default=32, + help='input batch size for training') +parser.add_argument('--val-batch-size', type=int, default=32, + help='input batch size for validation') +parser.add_argument('--epochs', type=int, default=90, + help='number of epochs to train') +parser.add_argument('--base-lr', type=float, default=0.0125, + help='learning rate for a single GPU') +parser.add_argument('--warmup-epochs', type=float, default=5, + help='number of warmup epochs') +parser.add_argument('--momentum', type=float, default=0.9, + help='SGD momentum') +parser.add_argument('--wd', type=float, default=0.00005, + help='weight decay') +parser.add_argument('--seed', type=int, default=42, + help='random seed') + + +def train(epoch, device): + model.train() + train_sampler.set_epoch(epoch) + train_loss = Metric('train_loss') + train_accuracy = Metric('train_accuracy') + + with tqdm(total=len(train_loader), + desc='Train Epoch #{}'.format(epoch + 1), + disable=not verbose) as t: + for batch_idx, (data, target) in enumerate(train_loader): + adjust_learning_rate(epoch, batch_idx) + + data, target = data.to(device), target.to(device) + optimizer.zero_grad() + # Split data into sub-batches of size batch_size + for i in range(0, len(data), args.batch_size): + data_batch = data[i:i + args.batch_size] + target_batch = target[i:i + args.batch_size] + output = model(data_batch) + train_accuracy.update(accuracy(output, target_batch)) + loss = F.cross_entropy(output, target_batch) + train_loss.update(loss) + # Average gradients among sub-batches + loss.div_(math.ceil(float(len(data)) / args.batch_size)) + loss.backward() + # Gradient is applied across all ranks + optimizer.step() + t.set_postfix({'loss': train_loss.avg.item(), + 'accuracy': 100. * train_accuracy.avg.item()}) + + t.update(1) + + if log_writer: + log_writer.add_scalar('train/loss', train_loss.avg, epoch) + log_writer.add_scalar('train/accuracy', train_accuracy.avg, epoch) + + +def validate(epoch, device): + model.eval() + val_loss = Metric('val_loss') + val_accuracy = Metric('val_accuracy') + + with tqdm(total=len(val_loader), + desc='Validate Epoch #{}'.format(epoch + 1), + disable=not verbose) as t: + with torch.no_grad(): + for data, target in val_loader: + data, target = data.to(device), target.to(device) + output = model(data) + + val_loss.update(F.cross_entropy(output, target)) + val_accuracy.update(accuracy(output, target)) + t.set_postfix({'loss': val_loss.avg.item(), + 'accuracy': 100. * val_accuracy.avg.item()}) + t.update(1) + + if 100.0*val_accuracy.avg.item() > args.acc_thresh: + print("The accuracy has been exceeded {},and the training is \ + terminated at epoch {}".format(args.acc_thresh, epoch)) + return + + if log_writer: + log_writer.add_scalar('val/loss', val_loss.avg, epoch) + log_writer.add_scalar('val/accuracy', val_accuracy.avg, epoch) + + +# Horovod: using `lr = base_lr * hvd.size()` from the very beginning leads to worse final +# accuracy. Scale the learning rate `lr = base_lr` ---> `lr = base_lr * hvd.size()` during +# the first five epochs. See https://arxiv.org/abs/1706.02677 for details. +# After the warmup reduce learning rate by 10 on the 30th, 60th and 80th epochs. +def adjust_learning_rate(epoch, batch_idx): + if epoch < args.warmup_epochs: + epoch += float(batch_idx + 1) / len(train_loader) + lr_adj = 1. / hvd.size() * (epoch * (hvd.size() - 1) / args.warmup_epochs + 1) + elif epoch < 30: + lr_adj = 1. + elif epoch < 60: + lr_adj = 1e-1 + elif epoch < 80: + lr_adj = 1e-2 + else: + lr_adj = 1e-3 + for param_group in optimizer.param_groups: + param_group['lr'] = args.base_lr * hvd.size() * args.batches_per_allreduce * lr_adj + + +def accuracy(output, target): + # get the index of the max log-probability + pred = output.max(1, keepdim=True)[1] + return pred.eq(target.view_as(pred)).cpu().float().mean() + + +def save_checkpoint(epoch): + if hvd.rank() == 0: + filepath = os.path.join(args.output_dir, args.checkpoint_format.format(epoch=epoch + 1)) + # filepath = args.checkpoint_format.format(epoch=epoch + 1) + state = { + 'model': model.state_dict(), + 'optimizer': optimizer.state_dict(), + } + torch.save(state, filepath) + + +# Horovod: average metrics from distributed training. +class Metric(object): + def __init__(self, name): + self.name = name + self.sum = torch.tensor(0.) + self.n = torch.tensor(0.) + + def update(self, val): + self.sum += hvd.allreduce(val.detach().cpu(), name=self.name) + self.n += 1 + + @property + def avg(self): + return self.sum / self.n + + +if __name__ == '__main__': + args = parser.parse_args() + try: + from dltest import show_training_arguments + show_training_arguments(args) + except: + pass + + device = torch.device(args.device) + + allreduce_batch_size = args.batch_size * args.batches_per_allreduce + + hvd.init() + torch.manual_seed(args.seed) + + # if args.cuda: + # Horovod: pin GPU to local rank. + torch.cuda.set_device(hvd.local_rank()) + torch.cuda.manual_seed(args.seed) + + cudnn.benchmark = True + + # If set > 0, will resume training from a given checkpoint. + resume_from_epoch = 0 + for try_epoch in range(args.epochs, 0, -1): + if os.path.exists(args.checkpoint_format.format(epoch=try_epoch)): + resume_from_epoch = try_epoch + break + + # Horovod: broadcast resume_from_epoch from rank 0 (which will have + # checkpoints) to other ranks. + resume_from_epoch = hvd.broadcast(torch.tensor(resume_from_epoch), root_rank=0, + name='resume_from_epoch').item() + + # Horovod: print logs on the first worker. + verbose = 1 if hvd.rank() == 0 else 0 + + # Horovod: write TensorBoard logs on first worker. + log_writer = SummaryWriter(args.output_dir) if hvd.rank() == 0 else None + + # Horovod: limit # of CPU threads to be used per worker. + torch.set_num_threads(2) + + #kwargs = {'num_workers': 4, 'pin_memory': True} if args.cuda else {} + kwargs = {'num_workers': 2, 'pin_memory': True} + + # When supported, use 'forkserver' to spawn dataloader workers instead of 'fork' to prevent + # issues with Infiniband implementations that are not fork-safe + if (kwargs.get('num_workers', 0) > 0 and hasattr(mp, '_supports_context') and + mp._supports_context and 'forkserver' in mp.get_all_start_methods()): + kwargs['multiprocessing_context'] = 'forkserver' + + train_dataset = \ + datasets.ImageFolder(args.train_dir, + transform=transforms.Compose([ + transforms.RandomResizedCrop(224), + transforms.RandomHorizontalFlip(), + transforms.ToTensor(), + transforms.Normalize(mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]) + ])) + + # Horovod: use DistributedSampler to partition data among workers. Manually specify + # `num_replicas=hvd.size()` and `rank=hvd.rank()`. + train_sampler = torch.utils.data.distributed.DistributedSampler( + train_dataset, num_replicas=hvd.size(), rank=hvd.rank()) + train_loader = torch.utils.data.DataLoader( + train_dataset, batch_size=allreduce_batch_size, + sampler=train_sampler, **kwargs) + + val_dataset = \ + datasets.ImageFolder(args.val_dir, + transform=transforms.Compose([ + transforms.Resize(256), + transforms.CenterCrop(224), + transforms.ToTensor(), + transforms.Normalize(mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]) + ])) + val_sampler = torch.utils.data.distributed.DistributedSampler( + val_dataset, num_replicas=hvd.size(), rank=hvd.rank()) + val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=args.val_batch_size, + sampler=val_sampler, **kwargs) + + + # Set up standard ResNet-50 model. + model = models.resnet50().to(device) + + # By default, Adasum doesn't need scaling up learning rate. + # For sum/average with gradient Accumulation: scale learning rate by batches_per_allreduce + lr_scaler = args.batches_per_allreduce * hvd.size() if not args.use_adasum else 1 + + # If using GPU Adasum allreduce, scale learning rate by local_size. + if args.use_adasum and hvd.nccl_built(): + lr_scaler = args.batches_per_allreduce * hvd.local_size() + + # Horovod: scale learning rate by the number of GPUs. + optimizer = optim.SGD(model.parameters(), + lr=(args.base_lr * + lr_scaler), + momentum=args.momentum, weight_decay=args.wd) + + # Horovod: (optional) compression algorithm. + compression = hvd.Compression.fp16 if args.fp16_allreduce else hvd.Compression.none + + # Horovod: wrap optimizer with DistributedOptimizer. + optimizer = hvd.DistributedOptimizer( + optimizer, named_parameters=model.named_parameters(), + compression=compression, + backward_passes_per_step=args.batches_per_allreduce, + op=hvd.Adasum if args.use_adasum else hvd.Average, + gradient_predivide_factor=args.gradient_predivide_factor) + + # Restore from a previous checkpoint, if initial_epoch is specified. + # Horovod: restore on the first worker which will broadcast weights to other workers. + if resume_from_epoch > 0 and hvd.rank() == 0: + filepath = args.checkpoint_format.format(epoch=resume_from_epoch) + checkpoint = torch.load(filepath) + model.load_state_dict(checkpoint['model']) + optimizer.load_state_dict(checkpoint['optimizer']) + + # Horovod: broadcast parameters & optimizer state. + hvd.broadcast_parameters(model.state_dict(), root_rank=0) + hvd.broadcast_optimizer_state(optimizer, root_rank=0) + + for epoch in range(resume_from_epoch, args.epochs): + train(epoch, device) + validate(epoch, device) + save_checkpoint(epoch) diff --git a/cv/classification/googlenet/pytorch/common_utils/smooth_value.py b/cv/classification/googlenet/pytorch/common_utils/smooth_value.py index 7c5fa1179..d4e3d2b6f 100755 --- a/cv/classification/googlenet/pytorch/common_utils/smooth_value.py +++ b/cv/classification/googlenet/pytorch/common_utils/smooth_value.py @@ -1,4 +1,5 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. @@ -37,7 +38,7 @@ class SmoothedValue(object): """ if not is_dist_avail_and_initialized(): return - t = torch.tensor([self.count, self.total], dtype=torch.float64, device='cuda') + t = torch.tensor([self.count, self.total], dtype=torch.float32, device='cuda') dist.barrier() dist.all_reduce(t) t = t.tolist() @@ -72,4 +73,4 @@ class SmoothedValue(object): avg=self.avg, global_avg=self.global_avg, max=self.max, - value=self.value) \ No newline at end of file + value=self.value) diff --git a/cv/classification/googlenet/pytorch/train.py b/cv/classification/googlenet/pytorch/train.py index ab579c17c..63d1ccb4e 100755 --- a/cv/classification/googlenet/pytorch/train.py +++ b/cv/classification/googlenet/pytorch/train.py @@ -1,23 +1,25 @@ -# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. # All Rights Reserved. # -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. import datetime import os +import sys import time +import math import torch import torch.utils.data @@ -36,7 +38,7 @@ import torchvision from utils_ import (MetricLogger, SmoothedValue, accuracy, mkdir,\ init_distributed_mode, manual_seed,\ - is_main_process, save_on_master) + is_main_process, save_on_master, get_world_size) from dataloader.classification import get_datasets, create_dataloader @@ -85,12 +87,16 @@ def train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, pri torch.cuda.synchronize() end_time = time.time() + loss_value = loss.item() + if not math.isfinite(loss_value): + print("Loss is {}, stopping training".format(loss_value)) + sys.exit(1) acc1, acc5 = accuracy(output, target, topk=(1, 5)) batch_size = image.shape[0] metric_logger.update(loss=loss.item(), lr=optimizer.param_groups[0]["lr"]) metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) - fps = batch_size / (end_time - start_time) + fps = batch_size / (end_time - start_time) * get_world_size() metric_logger.meters['img/s'].update(fps) all_fps.append(fps) @@ -208,29 +214,29 @@ def main(args): print("Start training") start_time = time.time() + best_acc = 0 for epoch in range(args.start_epoch, args.epochs): epoch_start_time = time.time() if args.distributed and not args.dali: data_loader.sampler.set_epoch(epoch) train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, args.print_freq, args.amp, use_dali=args.dali) lr_scheduler.step() - evaluate(model, criterion, data_loader_test, device=device, use_dali=args.dali) - if args.output_dir: - checkpoint = { - 'model': model_without_ddp.state_dict(), - 'optimizer': optimizer.state_dict(), - 'lr_scheduler': lr_scheduler.state_dict(), - 'epoch': epoch, - 'args': args} - save_on_master( - checkpoint, - os.path.join(args.output_dir, 'model_{}.pth'.format(epoch))) - save_on_master( - checkpoint, - os.path.join(args.output_dir, 'checkpoint.pth')) - epoch_total_time = time.time() - epoch_start_time - epoch_total_time_str = str(datetime.timedelta(seconds=int(epoch_total_time))) - print('epoch time {}'.format(epoch_total_time_str)) + acc_avg = evaluate(model, criterion, data_loader_test, device=device, use_dali=args.dali) + if acc_avg > best_acc: + if args.output_dir: + checkpoint = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'lr_scheduler': lr_scheduler.state_dict(), + 'epoch': epoch, + 'args': args} + save_on_master( + checkpoint, + os.path.join(args.output_dir, 'model_best.pth')) + epoch_total_time = time.time() - epoch_start_time + epoch_total_time_str = str(datetime.timedelta(seconds=int(epoch_total_time))) + print('epoch time {}'.format(epoch_total_time_str)) + best_acc = acc_avg if args.dali: data_loader.reset() @@ -301,7 +307,7 @@ def get_args_parser(add_help=True): ) # distributed training parameters - parser.add_argument('--local_rank', default=-1, type=int, + parser.add_argument('--local_rank', '--local-rank', default=-1, type=int, help='Local rank') parser.add_argument('--world-size', default=1, type=int, help='number of distributed processes') @@ -313,4 +319,9 @@ def get_args_parser(add_help=True): if __name__ == "__main__": args = get_args_parser().parse_args() + try: + from dltest import show_training_arguments + show_training_arguments(args) + except: + pass main(args) diff --git a/cv/classification/inceptionv3/pytorch/__init__.py b/cv/classification/inceptionv3/pytorch/__init__.py new file mode 100644 index 000000000..6faec1658 --- /dev/null +++ b/cv/classification/inceptionv3/pytorch/__init__.py @@ -0,0 +1,5 @@ +from .utils import * +from .common_utils import * +from .data_loader import * + +__all__ = [k for k in globals().keys() if not k.startswith("_")] diff --git a/cv/classification/inceptionv3/pytorch/common_utils/__init__.py b/cv/classification/inceptionv3/pytorch/common_utils/__init__.py new file mode 100644 index 000000000..32e8c4f57 --- /dev/null +++ b/cv/classification/inceptionv3/pytorch/common_utils/__init__.py @@ -0,0 +1,23 @@ +import random + +import numpy as np + +from .dist import * +from .metric_logger import * +from .misc import * +from .smooth_value import * + +def manual_seed(seed, deterministic=False): + random.seed(seed) + np.random.seed(seed) + os.environ['PYTHONHASHSEED'] = str(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + + if deterministic: + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + else: + torch.backends.cudnn.deterministic = False + torch.backends.cudnn.benchmark = True \ No newline at end of file diff --git a/cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/comm.py b/cv/classification/inceptionv3/pytorch/common_utils/dist.py similarity index 36% rename from cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/comm.py rename to cv/classification/inceptionv3/pytorch/common_utils/dist.py index d7ecad866..ea56ca267 100644 --- a/cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/comm.py +++ b/cv/classification/inceptionv3/pytorch/common_utils/dist.py @@ -1,40 +1,49 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -This file contains primitives for multi-gpu communication. -This is useful when doing distributed training. -""" - -import pickle +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os import time import torch import torch.distributed as dist -def get_world_size(): + +def setup_for_distributed(is_master): + """ + This function disables printing when not in master process + """ + import builtins as __builtin__ + builtin_print = __builtin__.print + + def print(*args, **kwargs): + force = kwargs.pop('force', False) + if is_master or force: + builtin_print(*args, **kwargs) + + __builtin__.print = print + + +def is_dist_avail_and_initialized(): if not dist.is_available(): - return 1 + return False if not dist.is_initialized(): + return False + return True + + +def get_world_size(): + if not is_dist_avail_and_initialized(): return 1 return dist.get_world_size() def get_rank(): - if not dist.is_available(): - return 0 - if not dist.is_initialized(): + if not is_dist_avail_and_initialized(): return 0 return dist.get_rank() @@ -43,19 +52,53 @@ def is_main_process(): return get_rank() == 0 -def synchronize(): - """ - Helper function to synchronize (barrier) among all processes when - using distributed training - """ - if not dist.is_available(): - return - if not dist.is_initialized(): - return - world_size = dist.get_world_size() - if world_size == 1: +def save_on_master(*args, **kwargs): + if is_main_process(): + torch.save(*args, **kwargs) + + +def get_dist_backend(args=None): + DIST_BACKEND_ENV = "PT_DIST_BACKEND" + if DIST_BACKEND_ENV in os.environ: + return os.environ[DIST_BACKEND_ENV] + + if args is None: + args = dict() + + backend_attr_name = "dist_backend" + + if hasattr(args, backend_attr_name): + return getattr(args, backend_attr_name) + + if backend_attr_name in args: + return args[backend_attr_name] + + return "nccl" + + +def init_distributed_mode(args): + if 'RANK' in os.environ and 'WORLD_SIZE' in os.environ: + args.rank = int(os.environ["RANK"]) + args.world_size = int(os.environ['WORLD_SIZE']) + args.gpu = int(os.environ['LOCAL_RANK']) + elif 'SLURM_PROCID' in os.environ: + args.rank = int(os.environ['SLURM_PROCID']) + args.gpu = args.rank % torch.cuda.device_count() + else: + print('Not using distributed mode') + args.distributed = False return - dist.barrier() + + args.distributed = True + + torch.cuda.set_device(args.gpu) + dist_backend = get_dist_backend(args) + print('| distributed init (rank {}): {}'.format( + args.rank, args.dist_url), flush=True) + torch.distributed.init_process_group(backend=dist_backend, init_method=args.dist_url, + world_size=args.world_size, rank=args.rank) + torch.distributed.barrier() + setup_for_distributed(args.rank == 0) def all_gather(data): @@ -69,35 +112,8 @@ def all_gather(data): world_size = get_world_size() if world_size == 1: return [data] - - # serialized to a Tensor - buffer = pickle.dumps(data) - storage = torch.ByteStorage.from_buffer(buffer) - tensor = torch.ByteTensor(storage).to("cuda") - - # obtain Tensor size of each rank - local_size = torch.IntTensor([tensor.numel()]).to("cuda") - size_list = [torch.IntTensor([0]).to("cuda") for _ in range(world_size)] - dist.all_gather(size_list, local_size) - size_list = [int(size.item()) for size in size_list] - max_size = max(size_list) - - # receiving Tensor from all ranks - # we pad the tensor because torch all_gather does not support - # gathering tensors of different shapes - tensor_list = [] - for _ in size_list: - tensor_list.append(torch.ByteTensor(size=(max_size,)).to("cuda")) - if local_size != max_size: - padding = torch.ByteTensor(size=(max_size - local_size,)).to("cuda") - tensor = torch.cat((tensor, padding), dim=0) - dist.all_gather(tensor_list, tensor) - - data_list = [] - for size, tensor in zip(size_list, tensor_list): - buffer = tensor.cpu().numpy().tobytes()[:size] - data_list.append(pickle.loads(buffer)) - + data_list = [None] * world_size + dist.all_gather_object(data_list, data) return data_list @@ -106,8 +122,8 @@ def reduce_dict(input_dict, average=True): Args: input_dict (dict): all the values will be reduced average (bool): whether to do average or sum - Reduce the values in the dictionary from all processes so that process with rank - 0 has the averaged results. Returns a dict with the same fields as + Reduce the values in the dictionary from all processes so that all processes + have the averaged results. Returns a dict with the same fields as input_dict, after reduction. """ world_size = get_world_size() @@ -121,10 +137,8 @@ def reduce_dict(input_dict, average=True): names.append(k) values.append(input_dict[k]) values = torch.stack(values, dim=0) - dist.reduce(values, dst=0) - if dist.get_rank() == 0 and average: - # only main process gets accumulated, so only divide by - # world_size in this case + dist.all_reduce(values) + if average: values /= world_size reduced_dict = {k: v for k, v in zip(names, values)} return reduced_dict diff --git a/cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/metric_logger.py b/cv/classification/inceptionv3/pytorch/common_utils/metric_logger.py similarity index 30% rename from cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/metric_logger.py rename to cv/classification/inceptionv3/pytorch/common_utils/metric_logger.py index c959d2845..960641c4d 100644 --- a/cv/detection/maskrcnn/pytorch/maskrcnn_benchmark/utils/metric_logger.py +++ b/cv/classification/inceptionv3/pytorch/common_utils/metric_logger.py @@ -1,56 +1,30 @@ -# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. -from collections import defaultdict -from collections import deque - -import torch -class SmoothedValue(object): - """Track a series of values and provide access to smoothed values over a - window or the global series average. - """ - - def __init__(self, window_size=20): - self.deque = deque(maxlen=window_size) - self.series = [] - self.total = 0.0 - self.count = 0 +from collections import defaultdict +import datetime +import time - def update(self, value): - self.deque.append(value) - self.series.append(value) - self.count += 1 - self.total += value +import torch +from .smooth_value import SmoothedValue - @property - def median(self): - d = torch.tensor(list(self.deque)) - return d.median().item() +""" +Examples: - @property - def avg(self): - d = torch.tensor(list(self.deque)) - return d.mean().item() +logger = MetricLogger(" ") - @property - def global_avg(self): - return self.total / self.count +>>> # For iter dataloader +>>> metric_logger.add_meter('img/s', utils.SmoothedValue(window_size=10, fmt='{value}')) +>>> header = 'Epoch: [{}]'.format(epoch) +>>> for image, target in metric_logger.log_every(data_loader, print_freq, header): +>>> ... +>>> logger.metric_logger.meters['img/s'].update(fps) +""" class MetricLogger(object): + def __init__(self, delimiter="\t"): self.meters = defaultdict(SmoothedValue) self.delimiter = delimiter @@ -68,12 +42,53 @@ class MetricLogger(object): if attr in self.__dict__: return self.__dict__[attr] raise AttributeError("'{}' object has no attribute '{}'".format( - type(self).__name__, attr)) + type(self).__name__, attr)) def __str__(self): loss_str = [] for name, meter in self.meters.items(): loss_str.append( - "{}: {:.4f} ({:.4f})".format(name, meter.median, meter.global_avg) + "{}: {}".format(name, str(meter)) ) return self.delimiter.join(loss_str) + + def synchronize_between_processes(self): + for meter in self.meters.values(): + meter.synchronize_between_processes() + + def add_meter(self, name, meter): + self.meters[name] = meter + + def log_every(self, iterable, print_freq, header=None): + i = 0 + if not header: + header = '' + start_time = time.time() + end = time.time() + iter_time = SmoothedValue(fmt='{avg:.4f}') + data_time = SmoothedValue(fmt='{avg:.4f}') + space_fmt = ':' + str(len(str(len(iterable)))) + 'd' + log_msg = self.delimiter.join([ + header, + '[{0' + space_fmt + '}/{1}]', + 'eta: {eta}', + '{meters}', + 'time: {time}', + 'data: {data}' + ]) + for obj in iterable: + data_time.update(time.time() - end) + yield obj + iter_time.update(time.time() - end) + if i % print_freq == 0: + eta_seconds = iter_time.global_avg * (len(iterable) - i) + eta_string = str(datetime.timedelta(seconds=int(eta_seconds))) + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time))) + i += 1 + end = time.time() + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('{} Total time: {}'.format(header, total_time_str)) diff --git a/cv/classification/inceptionv3/pytorch/common_utils/misc.py b/cv/classification/inceptionv3/pytorch/common_utils/misc.py new file mode 100644 index 000000000..c9b501cf8 --- /dev/null +++ b/cv/classification/inceptionv3/pytorch/common_utils/misc.py @@ -0,0 +1,11 @@ +import os +import sys +import errno + + +def mkdir(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise \ No newline at end of file diff --git a/cv/classification/inceptionv3/pytorch/common_utils/smooth_value.py b/cv/classification/inceptionv3/pytorch/common_utils/smooth_value.py new file mode 100644 index 000000000..30cb89d60 --- /dev/null +++ b/cv/classification/inceptionv3/pytorch/common_utils/smooth_value.py @@ -0,0 +1,75 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist +from .dist import is_dist_avail_and_initialized + + +class SmoothedValue(object): + """Track a series of values and provide access to smoothed values over a + window or the global series average. + """ + + def __init__(self, window_size=20, fmt=None): + if fmt is None: + fmt = "{median:.4f} ({global_avg:.4f})" + self.deque = deque(maxlen=window_size) + self.total = 0.0 + self.count = 0 + self.fmt = fmt + + def update(self, value, n=1): + self.deque.append(value) + self.count += n + self.total += value * n + + def synchronize_between_processes(self): + """ + Warning: does not synchronize the deque! + """ + if not is_dist_avail_and_initialized(): + return + t = torch.tensor([self.count, self.total], dtype=torch.float32, device='cuda') + dist.barrier() + dist.all_reduce(t) + t = t.tolist() + self.count = int(t[0]) + self.total = t[1] + + @property + def median(self): + d = torch.tensor(list(self.deque)) + return d.median().item() + + @property + def avg(self): + d = torch.tensor(list(self.deque), dtype=torch.float32) + return d.mean().item() + + @property + def global_avg(self): + return self.total / self.count + + @property + def max(self): + return max(self.deque) + + @property + def value(self): + return self.deque[-1] + + def __str__(self): + return self.fmt.format( + median=self.median, + avg=self.avg, + global_avg=self.global_avg, + max=self.max, + value=self.value) \ No newline at end of file diff --git a/cv/classification/inceptionv3/pytorch/dataloader/__init__.py b/cv/classification/inceptionv3/pytorch/dataloader/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/classification/inceptionv3/pytorch/dataloader/classification.py b/cv/classification/inceptionv3/pytorch/dataloader/classification.py new file mode 100644 index 000000000..030af6dee --- /dev/null +++ b/cv/classification/inceptionv3/pytorch/dataloader/classification.py @@ -0,0 +1,113 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +import os +import time + +import torch +import torchvision +from .utils import presets_classification as presets + +""" +Examples: + +>>> dataset_train, dataset_val = load_data(train_dir, val_dir, args) +""" + + +def get_datasets(traindir, + valdir, + resize_size=256, + crop_size=224, + auto_augment_policy=None, + random_erase_prob=0.): + # Data loading code + print("Loading data") + print("Loading training data") + dataset = torchvision.datasets.ImageFolder( + traindir, + presets.ClassificationPresetTrain(crop_size=crop_size, auto_augment_policy=auto_augment_policy, + random_erase_prob=random_erase_prob)) + + print("Loading validation data") + dataset_test = torchvision.datasets.ImageFolder( + valdir, + presets.ClassificationPresetEval(crop_size=crop_size, resize_size=resize_size)) + + return dataset, dataset_test + + +def get_input_size(model): + biger_input_size_models = ['inception'] + resize_size = 256 + crop_size = 224 + for bi_model in biger_input_size_models: + if bi_model in model: + resize_size = 342 + crop_size = 299 + + return resize_size, crop_size + + +def load_data(train_dir, val_dir, args): + auto_augment_policy = getattr(args, "auto_augment", None) + random_erase_prob = getattr(args, "random_erase", 0.0) + resize_size, crop_size = get_input_size(args.model) + dataset, dataset_test = get_datasets(train_dir, val_dir, + auto_augment_policy=auto_augment_policy, + random_erase_prob=random_erase_prob, + resize_size=resize_size, + crop_size=crop_size) + if args.distributed: + train_sampler = torch.utils.data.distributed.DistributedSampler(dataset) + test_sampler = torch.utils.data.distributed.DistributedSampler(dataset_test) + else: + train_sampler = torch.utils.data.RandomSampler(dataset) + test_sampler = torch.utils.data.SequentialSampler(dataset_test) + + return dataset, dataset_test, train_sampler, test_sampler + + +def _create_torch_dataloader(train_dir, val_dir, args): + dataset, dataset_test, train_sampler, test_sampler = load_data(train_dir, val_dir, args) + data_loader = torch.utils.data.DataLoader( + dataset, batch_size=args.batch_size, + sampler=train_sampler, num_workers=args.workers, pin_memory=True) + + data_loader_test = torch.utils.data.DataLoader( + dataset_test, batch_size=args.batch_size, + sampler=test_sampler, num_workers=args.workers, pin_memory=True) + + return data_loader, data_loader_test + + +def _create_dali_dataloader(train_dir, val_dir, args): + from .dali_classification import get_imagenet_iter_dali + device = torch.cuda.current_device() + _, crop_size = get_input_size(args.model) + data_loader = get_imagenet_iter_dali('train', train_dir, args.batch_size, + num_threads=args.workers, + device_id=device, + size=crop_size) + data_loader_test = get_imagenet_iter_dali('val', train_dir, args.batch_size, + num_threads=args.workers, + device_id=device, + size=crop_size) + + return data_loader, data_loader_test + + +def create_dataloader(train_dir, val_dir, args): + print("Creating data loaders") + if args.dali: + train_dir = os.path.dirname(train_dir) + val_dir = os.path.dirname(val_dir) + return _create_dali_dataloader(train_dir, val_dir, args) + return _create_torch_dataloader(train_dir, val_dir, args) diff --git a/cv/classification/inceptionv3/pytorch/dataloader/dali_classification.py b/cv/classification/inceptionv3/pytorch/dataloader/dali_classification.py new file mode 100644 index 000000000..4c92283b2 --- /dev/null +++ b/cv/classification/inceptionv3/pytorch/dataloader/dali_classification.py @@ -0,0 +1,121 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +import os + +import nvidia.dali.ops as ops +import nvidia.dali.types as types +from nvidia.dali.pipeline import Pipeline +from nvidia.dali.plugin.pytorch import DALIClassificationIterator, DALIGenericIterator + +class HybridTrainPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridTrainPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=True) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.RandomResizedCrop(device="gpu", size=size, random_area=[0.08, 1.25]) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +class HybridValPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridValPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=False) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.Resize(device="gpu", resize_x=size, resize_y=size) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + crop=(size, size), + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +def get_imagenet_iter_dali(type, image_dir, batch_size, num_threads, device_id, size): + if type == 'train': + pip_train = HybridTrainPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "train"), + size=size) + pip_train.build() + dali_iter_train = DALIClassificationIterator(pip_train, size=pip_train.epoch_size("Reader")) + return dali_iter_train + elif type == 'val': + pip_val = HybridValPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "val"), + size=size) + pip_val.build() + dali_iter_val = DALIClassificationIterator(pip_val, size=pip_val.epoch_size("Reader")) + return dali_iter_val + + +def main(arguments): + parser = argparse.ArgumentParser() + parser.add_argument('--data_dir', help='directory to save data to', type=str, default='classification data') + args = parser.parse_args(arguments) + + train_loader = get_imagenet_iter_dali(type='train', image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + val_loader = get_imagenet_iter_dali(type="val", image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + print('start dali train dataloader.') + start = time.time() + for epoch in range(20): + for i, data in enumerate(train_loader): + images = data[0]["data"].cuda(non_blocking=True) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + + # WARN: Very important + train_loader.reset() + print("Epoch", epoch) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali train dataloader.') + + + print('start dali val dataloader.') + start = time.time() + for i, data in enumerate(val_loader): + images = data[0]["data"].cuda(non_blocking=True) + print(images.shape) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + print(labels.shape) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali val dataloader.') + + +if __name__ == '__main__': + import os, time, sys + import argparse + sys.exit(main(sys.argv[1:])) \ No newline at end of file diff --git a/cv/classification/inceptionv3/pytorch/dataloader/utils/__init__.py b/cv/classification/inceptionv3/pytorch/dataloader/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/classification/inceptionv3/pytorch/dataloader/utils/presets_classification.py b/cv/classification/inceptionv3/pytorch/dataloader/utils/presets_classification.py new file mode 100644 index 000000000..b3f559af4 --- /dev/null +++ b/cv/classification/inceptionv3/pytorch/dataloader/utils/presets_classification.py @@ -0,0 +1,41 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from torchvision.transforms import autoaugment, transforms + + +class ClassificationPresetTrain: + def __init__(self, crop_size, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225), hflip_prob=0.5, + auto_augment_policy=None, random_erase_prob=0.0): + trans = [transforms.RandomResizedCrop(crop_size)] + if hflip_prob > 0: + trans.append(transforms.RandomHorizontalFlip(hflip_prob)) + if auto_augment_policy is not None: + aa_policy = autoaugment.AutoAugmentPolicy(auto_augment_policy) + trans.append(autoaugment.AutoAugment(policy=aa_policy)) + trans.extend([ + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ]) + if random_erase_prob > 0: + trans.append(transforms.RandomErasing(p=random_erase_prob)) + + self.transforms = transforms.Compose(trans) + + def __call__(self, img): + return self.transforms(img) + + +class ClassificationPresetEval: + def __init__(self, crop_size, resize_size=256, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)): + + self.transforms = transforms.Compose([ + transforms.Resize(resize_size), + transforms.CenterCrop(crop_size), + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ]) + + def __call__(self, img): + return self.transforms(img) diff --git a/cv/classification/inceptionv3/pytorch/run_train.py b/cv/classification/inceptionv3/pytorch/run_train.py deleted file mode 100644 index 93439e78a..000000000 --- a/cv/classification/inceptionv3/pytorch/run_train.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -import os -import sys - -import torchvision - -sys.path.append("../../torchvision/pytorch") - -from train import train_model -from utils import padding_conv_channel_to_4 - -def create_model(args): - # How does it work? - # - Apex.amp couldn't solve namedtuple input, thus produces the problem: - # TypeError: __new__() missing 1 required positional argument: 'aux_logits' - # - See https://github.com/pytorch/vision/issues/1048 for discussions - torchvision.models.inception.InceptionOutputs = lambda a,b:(a,b) - - model = torchvision.models.__dict__[args.model](pretrained=args.pretrained, num_classes=args.num_classes) - args.padding_channel = False - - return model - - -train_model(create_model) diff --git a/cv/classification/inceptionv3/pytorch/train.py b/cv/classification/inceptionv3/pytorch/train.py new file mode 100644 index 000000000..b812a01a3 --- /dev/null +++ b/cv/classification/inceptionv3/pytorch/train.py @@ -0,0 +1,312 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +import datetime +import os +import sys +import time +import math +import torch +import torch.utils.data + +try: + from torch.cuda.amp import autocast, GradScaler + scaler = GradScaler() +except: + autocast = None + scaler = None + + +from torch import nn +import torch.distributed as dist +import torchvision + +from utils_ import (MetricLogger, SmoothedValue, accuracy, mkdir,\ + init_distributed_mode, manual_seed,\ + is_main_process, save_on_master, get_world_size) + +from dataloader.classification import get_datasets, create_dataloader + + +def compute_loss(model, image, target, criterion): + output = model(image) + if not isinstance(output, (tuple, list)): + output = [output] + losses = [] + for out in output: + losses.append(criterion(out, target)) + loss = sum(losses) + return loss, output[0] + + +def train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, print_freq, amp=False, use_dali=False): + model.train() + metric_logger = MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', SmoothedValue(window_size=1, fmt='{value}')) + metric_logger.add_meter('img/s', SmoothedValue(window_size=10, fmt='{value}')) + + header = 'Epoch: [{}]'.format(epoch) + all_fps = [] + for data in metric_logger.log_every(data_loader, print_freq, header): + if use_dali: + image, target = data[0]["data"], data[0]["label"][:, 0].long() + else: + image, target = data + start_time = time.time() + image, target = image.to(device), target.to(device) + if autocast is None or not amp: + loss, output = compute_loss(model, image, target, criterion) + else: + with autocast(): + loss, output = compute_loss(model, image, target, criterion) + + optimizer.zero_grad() + if scaler is not None and amp: + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + else: + loss.backward() + optimizer.step() + + torch.cuda.synchronize() + end_time = time.time() + + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + batch_size = image.shape[0] + loss_value = loss.item() + if not math.isfinite(loss_value): + print("Loss is {}, stopping training".format(loss_value)) + sys.exit(1) + metric_logger.update(loss=loss.item(), lr=optimizer.param_groups[0]["lr"]) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + fps = batch_size / (end_time - start_time) * get_world_size() + metric_logger.meters['img/s'].update(fps) + all_fps.append(fps) + + print(header, 'Avg img/s:', sum(all_fps) / len(all_fps)) + + +def evaluate(model, criterion, data_loader, device, print_freq=100, use_dali=False): + model.eval() + metric_logger = MetricLogger(delimiter=" ") + header = 'Test:' + with torch.no_grad(): + for data in metric_logger.log_every(data_loader, print_freq, header): + if use_dali: + image, target = data[0]["data"], data[0]["label"][:, 0].long() + else: + image, target = data + image = image.to(device, non_blocking=True) + target = target.to(device, non_blocking=True) + output = model(image) + loss = criterion(output, target) + + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + # FIXME need to take into account that the datasets + # could have been padded in distributed setup + batch_size = image.shape[0] + metric_logger.update(loss=loss.item()) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + # gather the stats from all processes + metric_logger.synchronize_between_processes() + + print(' * Acc@1 {top1.global_avg:.3f} Acc@5 {top5.global_avg:.3f}' + .format(top1=metric_logger.acc1, top5=metric_logger.acc5)) + return metric_logger.acc1.global_avg + + +def _get_cache_path(filepath): + import hashlib + h = hashlib.sha1(filepath.encode()).hexdigest() + cache_path = os.path.join("~", ".torch", "vision", "datasets", "imagefolder", h[:10] + ".pt") + cache_path = os.path.expanduser(cache_path) + return cache_path + + +def main(args): + if args.output_dir: + mkdir(args.output_dir) + + init_distributed_mode(args) + print(args) + + device = torch.device(args.device) + + manual_seed(args.seed, deterministic=False) + # torch.backends.cudnn.benchmark = True + + # WARN: + if dist.is_initialized(): + num_gpu = dist.get_world_size() + else: + num_gpu = 1 + + global_batch_size = num_gpu * args.batch_size + + train_dir = os.path.join(args.data_path, 'train') + val_dir = os.path.join(args.data_path, 'val') + + num_classes = len(os.listdir(train_dir)) + if 0 < num_classes < 13: + if global_batch_size > 512: + if is_main_process(): + print("WARN: Updating global batch size to 512, avoid non-convergence when training small dataset.") + args.batch_size = 512 // num_gpu + + if args.pretrained: + num_classes = 1000 + + data_loader, data_loader_test = create_dataloader(train_dir, val_dir, args) + + print("Creating model") + model = torchvision.models.__dict__[args.model](pretrained=args.pretrained, num_classes=num_classes) + model.to(device) + if args.distributed and args.sync_bn: + model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model) + + criterion = nn.CrossEntropyLoss() + + opt_name = args.opt.lower() + if opt_name == 'sgd': + optimizer = torch.optim.SGD( + model.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.weight_decay) + elif opt_name == 'rmsprop': + optimizer = torch.optim.RMSprop(model.parameters(), lr=args.lr, momentum=args.momentum, + weight_decay=args.weight_decay, eps=0.0316, alpha=0.9) + else: + raise RuntimeError("Invalid optimizer {}. Only SGD and RMSprop are supported.".format(args.opt)) + + lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=args.lr_step_size, gamma=args.lr_gamma) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + if args.resume: + checkpoint = torch.load(args.resume, map_location='cpu') + model_without_ddp.load_state_dict(checkpoint['model']) + optimizer.load_state_dict(checkpoint['optimizer']) + lr_scheduler.load_state_dict(checkpoint['lr_scheduler']) + args.start_epoch = checkpoint['epoch'] + 1 + + if args.test_only: + evaluate(model, criterion, data_loader_test, device=device) + return + + print("Start training") + start_time = time.time() + best_acc = 0 + for epoch in range(args.start_epoch, args.epochs): + epoch_start_time = time.time() + if args.distributed and not args.dali: + data_loader.sampler.set_epoch(epoch) + train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, args.print_freq, args.amp, use_dali=args.dali) + lr_scheduler.step() + acc_avg = evaluate(model, criterion, data_loader_test, device=device, use_dali=args.dali) + if acc_avg > best_acc: + if args.output_dir: + checkpoint = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'lr_scheduler': lr_scheduler.state_dict(), + 'epoch': epoch, + 'args': args} + save_on_master( + checkpoint, + os.path.join(args.output_dir, 'model_best.pth')) + epoch_total_time = time.time() - epoch_start_time + epoch_total_time_str = str(datetime.timedelta(seconds=int(epoch_total_time))) + print('epoch time {}'.format(epoch_total_time_str)) + best_acc = acc_avg + + if args.dali: + data_loader.reset() + data_loader_test.reset() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +def get_args_parser(add_help=True): + import argparse + parser = argparse.ArgumentParser(description='PyTorch Classification Training', add_help=add_help) + + parser.add_argument('--data-path', default='/datasets01/imagenet_full_size/061417/', help='dataset') + parser.add_argument('--model', default='resnet18', help='model') + parser.add_argument('--device', default='cuda', help='device') + parser.add_argument('-b', '--batch-size', default=32, type=int) + parser.add_argument('--epochs', default=90, type=int, metavar='N', + help='number of total epochs to run') + parser.add_argument('-j', '--workers', default=4, type=int, metavar='N', + help='number of data loading workers (default: 4)') + parser.add_argument('--opt', default='sgd', type=str, help='optimizer') + parser.add_argument('--lr', default=0.1, type=float, help='initial learning rate') + parser.add_argument('--momentum', default=0.9, type=float, metavar='M', + help='momentum') + parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float, + metavar='W', help='weight decay (default: 1e-4)', + dest='weight_decay') + parser.add_argument('--lr-step-size', default=30, type=int, help='decrease lr every step-size epochs') + parser.add_argument('--lr-gamma', default=0.1, type=float, help='decrease lr by a factor of lr-gamma') + parser.add_argument('--print-freq', default=10, type=int, help='print frequency') + parser.add_argument('--output-dir', default='.', help='path where to save') + parser.add_argument('--resume', default='', help='resume from checkpoint') + parser.add_argument('--start-epoch', default=0, type=int, metavar='N', + help='start epoch') + parser.add_argument( + "--cache-dataset", + dest="cache_dataset", + help="Cache the datasets for quicker initialization. It also serializes the transforms", + action="store_true", + ) + parser.add_argument( + "--sync-bn", + dest="sync_bn", + help="Use sync batch norm", + action="store_true", + ) + parser.add_argument( + "--test-only", + dest="test_only", + help="Only test the model", + action="store_true", + ) + parser.add_argument( + "--pretrained", + dest="pretrained", + help="Use pre-trained models from the modelzoo", + action="store_true", + ) + parser.add_argument('--auto-augment', default=None, help='auto augment policy (default: None)') + parser.add_argument('--random-erase', default=0.0, type=float, help='random erasing probability (default: 0.0)') + parser.add_argument( + "--dali", + help="Use dali as dataloader", + default=False, + action="store_true", + ) + + # distributed training parameters + parser.add_argument('--local_rank', '--local-rank', default=-1, type=int, + help='Local rank') + parser.add_argument('--world-size', default=1, type=int, + help='number of distributed processes') + parser.add_argument('--dist-url', default='env://', help='url used to set up distributed training') + parser.add_argument('--amp', action='store_true', help='Automatic Mixed Precision training') + parser.add_argument('--seed', default=42, type=int, help='Random seed') + return parser + + +if __name__ == "__main__": + args = get_args_parser().parse_args() + try: + from dltest import show_training_arguments + show_training_arguments(args) + except: + pass + main(args) diff --git a/cv/classification/inceptionv3/pytorch/train_inception_v3_amp_dist.sh b/cv/classification/inceptionv3/pytorch/train_inception_v3_amp_dist.sh index e87a1e714..e2b1b8d17 100755 --- a/cv/classification/inceptionv3/pytorch/train_inception_v3_amp_dist.sh +++ b/cv/classification/inceptionv3/pytorch/train_inception_v3_amp_dist.sh @@ -31,6 +31,6 @@ if [ ! -z "${DEBUG}" ];then fi cd ${ROOT_DIR} python3 $PYTHONARG ${ROOT_DIR}/run_train.py \ - --model inception_v3 --dali --dali-cpu --data-path $DATA_PATH \ - --opt sgd --batch-size 300 --lr 1e-2 --crop-size 299 \ - --amp --nhwc "$@" + --model inception_v3 --data-path $DATA_PATH \ + --batch-size 128 \ + --amp --wd 0.000001 "$@" diff --git a/cv/classification/inceptionv3/pytorch/utils_.py b/cv/classification/inceptionv3/pytorch/utils_.py new file mode 100644 index 000000000..3d34c4df0 --- /dev/null +++ b/cv/classification/inceptionv3/pytorch/utils_.py @@ -0,0 +1,156 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque, OrderedDict +import copy +import datetime +import hashlib +import time +import torch +import torch.distributed as dist + +import errno +import os + +from common_utils import * + + +def accuracy(output, target, topk=(1,)): + """Computes the accuracy over the k top predictions for the specified values of k""" + with torch.no_grad(): + maxk = max(topk) + batch_size = target.size(0) + + _, pred = output.topk(maxk, 1, True, True) + pred = pred.t() + correct = pred.eq(target[None]) + + res = [] + for k in topk: + correct_k = correct[:k].flatten().sum(dtype=torch.float32) + res.append(correct_k * (100.0 / batch_size)) + return res + + +def average_checkpoints(inputs): + """Loads checkpoints from inputs and returns a model with averaged weights. Original implementation taken from: + https://github.com/pytorch/fairseq/blob/a48f235636557b8d3bc4922a6fa90f3a0fa57955/scripts/average_checkpoints.py#L16 + + Args: + inputs (List[str]): An iterable of string paths of checkpoints to load from. + Returns: + A dict of string keys mapping to various values. The 'model' key + from the returned dict should correspond to an OrderedDict mapping + string parameter names to torch Tensors. + """ + params_dict = OrderedDict() + params_keys = None + new_state = None + num_models = len(inputs) + for fpath in inputs: + with open(fpath, "rb") as f: + state = torch.load( + f, + map_location=( + lambda s, _: torch.serialization.default_restore_location(s, "cpu") + ), + ) + # Copies over the settings from the first checkpoint + if new_state is None: + new_state = state + model_params = state["model"] + model_params_keys = list(model_params.keys()) + if params_keys is None: + params_keys = model_params_keys + elif params_keys != model_params_keys: + raise KeyError( + "For checkpoint {}, expected list of params: {}, " + "but found: {}".format(f, params_keys, model_params_keys) + ) + for k in params_keys: + p = model_params[k] + if isinstance(p, torch.HalfTensor): + p = p.float() + if k not in params_dict: + params_dict[k] = p.clone() + # NOTE: clone() is needed in case of p is a shared parameter + else: + params_dict[k] += p + averaged_params = OrderedDict() + for k, v in params_dict.items(): + averaged_params[k] = v + if averaged_params[k].is_floating_point(): + averaged_params[k].div_(num_models) + else: + averaged_params[k] //= num_models + new_state["model"] = averaged_params + return new_state + + +def store_model_weights(model, checkpoint_path, checkpoint_key='model', strict=True): + """ + This method can be used to prepare weights files for new models. It receives as + input a model architecture and a checkpoint from the training script and produces + a file with the weights ready for release. + + Examples: + from torchvision import models as M + + # Classification + model = M.mobilenet_v3_large(pretrained=False) + print(store_model_weights(model, './class.pth')) + + # Quantized Classification + model = M.quantization.mobilenet_v3_large(pretrained=False, quantize=False) + model.fuse_model() + model.qconfig = torch.quantization.get_default_qat_qconfig('qnnpack') + _ = torch.quantization.prepare_qat(model, inplace=True) + print(store_model_weights(model, './qat.pth')) + + # Object Detection + model = M.detection.fasterrcnn_mobilenet_v3_large_fpn(pretrained=False, pretrained_backbone=False) + print(store_model_weights(model, './obj.pth')) + + # Segmentation + model = M.segmentation.deeplabv3_mobilenet_v3_large(pretrained=False, pretrained_backbone=False, aux_loss=True) + print(store_model_weights(model, './segm.pth', strict=False)) + + Args: + model (pytorch.nn.Module): The model on which the weights will be loaded for validation purposes. + checkpoint_path (str): The path of the checkpoint we will load. + checkpoint_key (str, optional): The key of the checkpoint where the model weights are stored. + Default: "model". + strict (bool): whether to strictly enforce that the keys + in :attr:`state_dict` match the keys returned by this module's + :meth:`~torch.nn.Module.state_dict` function. Default: ``True`` + + Returns: + output_path (str): The location where the weights are saved. + """ + # Store the new model next to the checkpoint_path + checkpoint_path = os.path.abspath(checkpoint_path) + output_dir = os.path.dirname(checkpoint_path) + + # Deep copy to avoid side-effects on the model object. + model = copy.deepcopy(model) + checkpoint = torch.load(checkpoint_path, map_location='cpu') + + # Load the weights to the model to validate that everything works + # and remove unnecessary weights (such as auxiliaries, etc) + model.load_state_dict(checkpoint[checkpoint_key], strict=strict) + + tmp_path = os.path.join(output_dir, str(model.__hash__())) + torch.save(model.state_dict(), tmp_path) + + sha256_hash = hashlib.sha256() + with open(tmp_path, "rb") as f: + # Read and update hash string value in blocks of 4K + for byte_block in iter(lambda: f.read(4096), b""): + sha256_hash.update(byte_block) + hh = sha256_hash.hexdigest() + + output_path = os.path.join(output_dir, "weights-" + str(hh[:8]) + ".pth") + os.replace(tmp_path, output_path) + + return output_path diff --git a/cv/classification/mobilenetv3/pytorch/__init__.py b/cv/classification/mobilenetv3/pytorch/__init__.py new file mode 100644 index 000000000..ebd9f2e52 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/__init__.py @@ -0,0 +1,2 @@ + +__all__ = [k for k in globals().keys() if not k.startswith("_")] diff --git a/cv/classification/mobilenetv3/pytorch/_torchvision/__init__.py b/cv/classification/mobilenetv3/pytorch/_torchvision/__init__.py new file mode 100644 index 000000000..4e419fff0 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/_torchvision/__init__.py @@ -0,0 +1,3 @@ +from . import models + +__all__ = [k for k in globals().keys() if not k.startswith("_")] diff --git a/cv/classification/mobilenetv3/pytorch/_torchvision/_internally_replaced_utils.py b/cv/classification/mobilenetv3/pytorch/_torchvision/_internally_replaced_utils.py new file mode 100644 index 000000000..18afc3ed9 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/_torchvision/_internally_replaced_utils.py @@ -0,0 +1,58 @@ +import importlib.machinery +import os + +from torch.hub import _get_torch_home + + +_HOME = os.path.join(_get_torch_home(), "datasets", "vision") +_USE_SHARDED_DATASETS = False + + +def _download_file_from_remote_location(fpath: str, url: str) -> None: + pass + + +def _is_remote_location_available() -> bool: + return False + + +try: + from torch.hub import load_state_dict_from_url # noqa: 401 +except ImportError: + from torch.utils.model_zoo import load_url as load_state_dict_from_url # noqa: 401 + + +def _get_extension_path(lib_name): + + lib_dir = os.path.dirname(__file__) + if os.name == "nt": + # Register the main torchvision library location on the default DLL path + import ctypes + import sys + + kernel32 = ctypes.WinDLL("kernel32.dll", use_last_error=True) + with_load_library_flags = hasattr(kernel32, "AddDllDirectory") + prev_error_mode = kernel32.SetErrorMode(0x0001) + + if with_load_library_flags: + kernel32.AddDllDirectory.restype = ctypes.c_void_p + + if sys.version_info >= (3, 8): + os.add_dll_directory(lib_dir) + elif with_load_library_flags: + res = kernel32.AddDllDirectory(lib_dir) + if res is None: + err = ctypes.WinError(ctypes.get_last_error()) + err.strerror += f' Error adding "{lib_dir}" to the DLL directories.' + raise err + + kernel32.SetErrorMode(prev_error_mode) + + loader_details = (importlib.machinery.ExtensionFileLoader, importlib.machinery.EXTENSION_SUFFIXES) + + extfinder = importlib.machinery.FileFinder(lib_dir, loader_details) + ext_specs = extfinder.find_spec(lib_name) + if ext_specs is None: + raise ImportError + + return ext_specs.origin diff --git a/cv/classification/mobilenetv3/pytorch/_torchvision/models/__init__.py b/cv/classification/mobilenetv3/pytorch/_torchvision/models/__init__.py new file mode 100644 index 000000000..fc26e8aa6 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/_torchvision/models/__init__.py @@ -0,0 +1 @@ +from .mobilenet import * diff --git a/cv/classification/mobilenetv3/pytorch/_torchvision/models/_utils.py b/cv/classification/mobilenetv3/pytorch/_torchvision/models/_utils.py new file mode 100644 index 000000000..f4e1cd845 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/_torchvision/models/_utils.py @@ -0,0 +1,83 @@ +from collections import OrderedDict +from typing import Dict, Optional + +from torch import nn + + +class IntermediateLayerGetter(nn.ModuleDict): + """ + Module wrapper that returns intermediate layers from a model + + It has a strong assumption that the modules have been registered + into the model in the same order as they are used. + This means that one should **not** reuse the same nn.Module + twice in the forward if you want this to work. + + Additionally, it is only able to query submodules that are directly + assigned to the model. So if `model` is passed, `model.feature1` can + be returned, but not `model.feature1.layer2`. + + Args: + model (nn.Module): model on which we will extract the features + return_layers (Dict[name, new_name]): a dict containing the names + of the modules for which the activations will be returned as + the key of the dict, and the value of the dict is the name + of the returned activation (which the user can specify). + + Examples:: + + >>> m = torchvision.models.resnet18(pretrained=True) + >>> # extract layer1 and layer3, giving as names `feat1` and feat2` + >>> new_m = torchvision.models._utils.IntermediateLayerGetter(m, + >>> {'layer1': 'feat1', 'layer3': 'feat2'}) + >>> out = new_m(torch.rand(1, 3, 224, 224)) + >>> print([(k, v.shape) for k, v in out.items()]) + >>> [('feat1', torch.Size([1, 64, 56, 56])), + >>> ('feat2', torch.Size([1, 256, 14, 14]))] + """ + + _version = 2 + __annotations__ = { + "return_layers": Dict[str, str], + } + + def __init__(self, model: nn.Module, return_layers: Dict[str, str]) -> None: + if not set(return_layers).issubset([name for name, _ in model.named_children()]): + raise ValueError("return_layers are not present in model") + orig_return_layers = return_layers + return_layers = {str(k): str(v) for k, v in return_layers.items()} + layers = OrderedDict() + for name, module in model.named_children(): + layers[name] = module + if name in return_layers: + del return_layers[name] + if not return_layers: + break + + super().__init__(layers) + self.return_layers = orig_return_layers + + def forward(self, x): + out = OrderedDict() + for name, module in self.items(): + x = module(x) + if name in self.return_layers: + out_name = self.return_layers[name] + out[out_name] = x + return out + + +def _make_divisible(v: float, divisor: int, min_value: Optional[int] = None) -> int: + """ + This function is taken from the original tf repo. + It ensures that all layers have a channel number that is divisible by 8 + It can be seen here: + https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py + """ + if min_value is None: + min_value = divisor + new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) + # Make sure that round down does not go down by more than 10%. + if new_v < 0.9 * v: + new_v += divisor + return new_v diff --git a/cv/classification/mobilenetv3/pytorch/_torchvision/models/mobilenet.py b/cv/classification/mobilenetv3/pytorch/_torchvision/models/mobilenet.py new file mode 100644 index 000000000..4108305d3 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/_torchvision/models/mobilenet.py @@ -0,0 +1,4 @@ +from .mobilenetv2 import MobileNetV2, mobilenet_v2, __all__ as mv2_all +from .mobilenetv3 import MobileNetV3, mobilenet_v3_large, mobilenet_v3_small, __all__ as mv3_all + +__all__ = mv2_all + mv3_all diff --git a/cv/classification/mobilenetv3/pytorch/_torchvision/models/mobilenetv2.py b/cv/classification/mobilenetv3/pytorch/_torchvision/models/mobilenetv2.py new file mode 100644 index 000000000..e24c5962d --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/_torchvision/models/mobilenetv2.py @@ -0,0 +1,211 @@ +import warnings +from typing import Callable, Any, Optional, List + +import torch +from torch import Tensor +from torch import nn + +from .._internally_replaced_utils import load_state_dict_from_url +from ..ops.misc import ConvNormActivation +from ..utils import _log_api_usage_once +from ._utils import _make_divisible + + +__all__ = ["MobileNetV2", "mobilenet_v2"] + + +model_urls = { + "mobilenet_v2": "https://download.pytorch.org/models/mobilenet_v2-b0353104.pth", +} + + +# necessary for backwards compatibility +class _DeprecatedConvBNAct(ConvNormActivation): + def __init__(self, *args, **kwargs): + warnings.warn( + "The ConvBNReLU/ConvBNActivation classes are deprecated since 0.12 and will be removed in 0.14. " + "Use torchvision.ops.misc.ConvNormActivation instead.", + FutureWarning, + ) + if kwargs.get("norm_layer", None) is None: + kwargs["norm_layer"] = nn.BatchNorm2d + if kwargs.get("activation_layer", None) is None: + kwargs["activation_layer"] = nn.ReLU6 + super().__init__(*args, **kwargs) + + +ConvBNReLU = _DeprecatedConvBNAct +ConvBNActivation = _DeprecatedConvBNAct + + +class InvertedResidual(nn.Module): + def __init__( + self, inp: int, oup: int, stride: int, expand_ratio: int, norm_layer: Optional[Callable[..., nn.Module]] = None + ) -> None: + super().__init__() + self.stride = stride + assert stride in [1, 2] + + if norm_layer is None: + norm_layer = nn.BatchNorm2d + + hidden_dim = int(round(inp * expand_ratio)) + self.use_res_connect = self.stride == 1 and inp == oup + + layers: List[nn.Module] = [] + if expand_ratio != 1: + # pw + layers.append( + ConvNormActivation(inp, hidden_dim, kernel_size=1, norm_layer=norm_layer, activation_layer=nn.ReLU6) + ) + layers.extend( + [ + # dw + ConvNormActivation( + hidden_dim, + hidden_dim, + stride=stride, + groups=hidden_dim, + norm_layer=norm_layer, + activation_layer=nn.ReLU6, + ), + # pw-linear + nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), + norm_layer(oup), + ] + ) + self.conv = nn.Sequential(*layers) + self.out_channels = oup + self._is_cn = stride > 1 + + def forward(self, x: Tensor) -> Tensor: + if self.use_res_connect: + return x + self.conv(x) + else: + return self.conv(x) + + +class MobileNetV2(nn.Module): + def __init__( + self, + num_classes: int = 1000, + width_mult: float = 1.0, + inverted_residual_setting: Optional[List[List[int]]] = None, + round_nearest: int = 8, + block: Optional[Callable[..., nn.Module]] = None, + norm_layer: Optional[Callable[..., nn.Module]] = None, + dropout: float = 0.2, + ) -> None: + """ + MobileNet V2 main class + + Args: + num_classes (int): Number of classes + width_mult (float): Width multiplier - adjusts number of channels in each layer by this amount + inverted_residual_setting: Network structure + round_nearest (int): Round the number of channels in each layer to be a multiple of this number + Set to 1 to turn off rounding + block: Module specifying inverted residual building block for mobilenet + norm_layer: Module specifying the normalization layer to use + dropout (float): The droupout probability + + """ + super().__init__() + _log_api_usage_once(self) + + if block is None: + block = InvertedResidual + + if norm_layer is None: + norm_layer = nn.BatchNorm2d + + input_channel = 32 + last_channel = 1280 + + if inverted_residual_setting is None: + inverted_residual_setting = [ + # t, c, n, s + [1, 16, 1, 1], + [6, 24, 2, 2], + [6, 32, 3, 2], + [6, 64, 4, 2], + [6, 96, 3, 1], + [6, 160, 3, 2], + [6, 320, 1, 1], + ] + + # only check the first element, assuming user knows t,c,n,s are required + if len(inverted_residual_setting) == 0 or len(inverted_residual_setting[0]) != 4: + raise ValueError( + f"inverted_residual_setting should be non-empty or a 4-element list, got {inverted_residual_setting}" + ) + + # building first layer + input_channel = _make_divisible(input_channel * width_mult, round_nearest) + self.last_channel = _make_divisible(last_channel * max(1.0, width_mult), round_nearest) + features: List[nn.Module] = [ + ConvNormActivation(3, input_channel, stride=2, norm_layer=norm_layer, activation_layer=nn.ReLU6) + ] + # building inverted residual blocks + for t, c, n, s in inverted_residual_setting: + output_channel = _make_divisible(c * width_mult, round_nearest) + for i in range(n): + stride = s if i == 0 else 1 + features.append(block(input_channel, output_channel, stride, expand_ratio=t, norm_layer=norm_layer)) + input_channel = output_channel + # building last several layers + features.append( + ConvNormActivation( + input_channel, self.last_channel, kernel_size=1, norm_layer=norm_layer, activation_layer=nn.ReLU6 + ) + ) + # make it nn.Sequential + self.features = nn.Sequential(*features) + + # building classifier + self.classifier = nn.Sequential( + nn.Dropout(p=dropout), + nn.Linear(self.last_channel, num_classes), + ) + + # weight initialization + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.kaiming_normal_(m.weight, mode="fan_out") + if m.bias is not None: + nn.init.zeros_(m.bias) + elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)): + nn.init.ones_(m.weight) + nn.init.zeros_(m.bias) + elif isinstance(m, nn.Linear): + nn.init.normal_(m.weight, 0, 0.01) + nn.init.zeros_(m.bias) + + def _forward_impl(self, x: Tensor) -> Tensor: + # This exists since TorchScript doesn't support inheritance, so the superclass method + # (this one) needs to have a name other than `forward` that can be accessed in a subclass + x = self.features(x) + # Cannot use "squeeze" as batch-size can be 1 + x = nn.functional.adaptive_avg_pool2d(x, (1, 1)) + x = torch.flatten(x, 1) + x = self.classifier(x) + return x + + def forward(self, x: Tensor) -> Tensor: + return self._forward_impl(x) + + +def mobilenet_v2(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> MobileNetV2: + """ + Constructs a MobileNetV2 architecture from + `"MobileNetV2: Inverted Residuals and Linear Bottlenecks" `_. + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + model = MobileNetV2(**kwargs) + if pretrained: + state_dict = load_state_dict_from_url(model_urls["mobilenet_v2"], progress=progress) + model.load_state_dict(state_dict) + return model diff --git a/cv/classification/mobilenetv3/pytorch/_torchvision/models/mobilenetv3.py b/cv/classification/mobilenetv3/pytorch/_torchvision/models/mobilenetv3.py new file mode 100644 index 000000000..711888b7c --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/_torchvision/models/mobilenetv3.py @@ -0,0 +1,328 @@ +import warnings +from functools import partial +from typing import Any, Callable, List, Optional, Sequence + +import torch +from torch import nn, Tensor + +from .._internally_replaced_utils import load_state_dict_from_url +from ..ops.misc import ConvNormActivation, SqueezeExcitation as SElayer +from ..utils import _log_api_usage_once +from ._utils import _make_divisible + + +__all__ = ["MobileNetV3", "mobilenet_v3_large", "mobilenet_v3_small"] + + +model_urls = { + "mobilenet_v3_large": "https://download.pytorch.org/models/mobilenet_v3_large-8738ca79.pth", + "mobilenet_v3_small": "https://download.pytorch.org/models/mobilenet_v3_small-047dcff4.pth", +} + + +class SqueezeExcitation(SElayer): + """DEPRECATED""" + + def __init__(self, input_channels: int, squeeze_factor: int = 4): + squeeze_channels = _make_divisible(input_channels // squeeze_factor, 8) + super().__init__(input_channels, squeeze_channels, scale_activation=nn.Hardsigmoid) + self.relu = self.activation + delattr(self, "activation") + warnings.warn( + "This SqueezeExcitation class is deprecated since 0.12 and will be removed in 0.14. " + "Use torchvision.ops.SqueezeExcitation instead.", + FutureWarning, + ) + + +class InvertedResidualConfig: + # Stores information listed at Tables 1 and 2 of the MobileNetV3 paper + def __init__( + self, + input_channels: int, + kernel: int, + expanded_channels: int, + out_channels: int, + use_se: bool, + activation: str, + stride: int, + dilation: int, + width_mult: float, + ): + self.input_channels = self.adjust_channels(input_channels, width_mult) + self.kernel = kernel + self.expanded_channels = self.adjust_channels(expanded_channels, width_mult) + self.out_channels = self.adjust_channels(out_channels, width_mult) + self.use_se = use_se + self.use_hs = activation == "HS" + self.stride = stride + self.dilation = dilation + + @staticmethod + def adjust_channels(channels: int, width_mult: float): + return _make_divisible(channels * width_mult, 8) + + +class InvertedResidual(nn.Module): + # Implemented as described at section 5 of MobileNetV3 paper + def __init__( + self, + cnf: InvertedResidualConfig, + norm_layer: Callable[..., nn.Module], + se_layer: Callable[..., nn.Module] = partial(SElayer, scale_activation=nn.Hardsigmoid), + ): + super().__init__() + if not (1 <= cnf.stride <= 2): + raise ValueError("illegal stride value") + + self.use_res_connect = cnf.stride == 1 and cnf.input_channels == cnf.out_channels + + layers: List[nn.Module] = [] + activation_layer = nn.Hardswish if cnf.use_hs else nn.ReLU + + # expand + if cnf.expanded_channels != cnf.input_channels: + layers.append( + ConvNormActivation( + cnf.input_channels, + cnf.expanded_channels, + kernel_size=1, + norm_layer=norm_layer, + activation_layer=activation_layer, + ) + ) + + # depthwise + stride = 1 if cnf.dilation > 1 else cnf.stride + layers.append( + ConvNormActivation( + cnf.expanded_channels, + cnf.expanded_channels, + kernel_size=cnf.kernel, + stride=stride, + dilation=cnf.dilation, + groups=cnf.expanded_channels, + norm_layer=norm_layer, + activation_layer=activation_layer, + ) + ) + if cnf.use_se: + squeeze_channels = _make_divisible(cnf.expanded_channels // 4, 8) + layers.append(se_layer(cnf.expanded_channels, squeeze_channels)) + + # project + layers.append( + ConvNormActivation( + cnf.expanded_channels, cnf.out_channels, kernel_size=1, norm_layer=norm_layer, activation_layer=None + ) + ) + + self.block = nn.Sequential(*layers) + self.out_channels = cnf.out_channels + self._is_cn = cnf.stride > 1 + + def forward(self, input: Tensor) -> Tensor: + result = self.block(input) + if self.use_res_connect: + result += input + return result + + +class MobileNetV3(nn.Module): + def __init__( + self, + inverted_residual_setting: List[InvertedResidualConfig], + last_channel: int, + num_classes: int = 1000, + block: Optional[Callable[..., nn.Module]] = None, + norm_layer: Optional[Callable[..., nn.Module]] = None, + dropout: float = 0.2, + **kwargs: Any, + ) -> None: + """ + MobileNet V3 main class + + Args: + inverted_residual_setting (List[InvertedResidualConfig]): Network structure + last_channel (int): The number of channels on the penultimate layer + num_classes (int): Number of classes + block (Optional[Callable[..., nn.Module]]): Module specifying inverted residual building block for mobilenet + norm_layer (Optional[Callable[..., nn.Module]]): Module specifying the normalization layer to use + dropout (float): The droupout probability + """ + super().__init__() + _log_api_usage_once(self) + + if not inverted_residual_setting: + raise ValueError("The inverted_residual_setting should not be empty") + elif not ( + isinstance(inverted_residual_setting, Sequence) + and all([isinstance(s, InvertedResidualConfig) for s in inverted_residual_setting]) + ): + raise TypeError("The inverted_residual_setting should be List[InvertedResidualConfig]") + + if block is None: + block = InvertedResidual + + if norm_layer is None: + norm_layer = partial(nn.BatchNorm2d, eps=0.001, momentum=0.01) + + layers: List[nn.Module] = [] + + # building first layer + firstconv_output_channels = inverted_residual_setting[0].input_channels + layers.append( + ConvNormActivation( + 3, + firstconv_output_channels, + kernel_size=3, + stride=2, + norm_layer=norm_layer, + activation_layer=nn.Hardswish, + ) + ) + + # building inverted residual blocks + for cnf in inverted_residual_setting: + layers.append(block(cnf, norm_layer)) + + # building last several layers + lastconv_input_channels = inverted_residual_setting[-1].out_channels + lastconv_output_channels = 6 * lastconv_input_channels + layers.append( + ConvNormActivation( + lastconv_input_channels, + lastconv_output_channels, + kernel_size=1, + norm_layer=norm_layer, + activation_layer=nn.Hardswish, + ) + ) + + self.features = nn.Sequential(*layers) + self.avgpool = nn.AdaptiveAvgPool2d(1) + self.classifier = nn.Sequential( + nn.Linear(lastconv_output_channels, last_channel), + nn.Hardswish(inplace=True), + nn.Dropout(p=dropout, inplace=True), + nn.Linear(last_channel, num_classes), + ) + + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.kaiming_normal_(m.weight, mode="fan_out") + if m.bias is not None: + nn.init.zeros_(m.bias) + elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)): + nn.init.ones_(m.weight) + nn.init.zeros_(m.bias) + elif isinstance(m, nn.Linear): + nn.init.normal_(m.weight, 0, 0.01) + nn.init.zeros_(m.bias) + + def _forward_impl(self, x: Tensor) -> Tensor: + x = self.features(x) + + x = self.avgpool(x) + x = torch.flatten(x, 1) + + x = self.classifier(x) + + return x + + def forward(self, x: Tensor) -> Tensor: + return self._forward_impl(x) + + +def _mobilenet_v3_conf( + arch: str, width_mult: float = 1.0, reduced_tail: bool = False, dilated: bool = False, **kwargs: Any +): + reduce_divider = 2 if reduced_tail else 1 + dilation = 2 if dilated else 1 + + bneck_conf = partial(InvertedResidualConfig, width_mult=width_mult) + adjust_channels = partial(InvertedResidualConfig.adjust_channels, width_mult=width_mult) + + if arch == "mobilenet_v3_large": + inverted_residual_setting = [ + bneck_conf(16, 3, 16, 16, False, "RE", 1, 1), + bneck_conf(16, 3, 64, 24, False, "RE", 2, 1), # C1 + bneck_conf(24, 3, 72, 24, False, "RE", 1, 1), + bneck_conf(24, 5, 72, 40, True, "RE", 2, 1), # C2 + bneck_conf(40, 5, 120, 40, True, "RE", 1, 1), + bneck_conf(40, 5, 120, 40, True, "RE", 1, 1), + bneck_conf(40, 3, 240, 80, False, "HS", 2, 1), # C3 + bneck_conf(80, 3, 200, 80, False, "HS", 1, 1), + bneck_conf(80, 3, 184, 80, False, "HS", 1, 1), + bneck_conf(80, 3, 184, 80, False, "HS", 1, 1), + bneck_conf(80, 3, 480, 112, True, "HS", 1, 1), + bneck_conf(112, 3, 672, 112, True, "HS", 1, 1), + bneck_conf(112, 5, 672, 160 // reduce_divider, True, "HS", 2, dilation), # C4 + bneck_conf(160 // reduce_divider, 5, 960 // reduce_divider, 160 // reduce_divider, True, "HS", 1, dilation), + bneck_conf(160 // reduce_divider, 5, 960 // reduce_divider, 160 // reduce_divider, True, "HS", 1, dilation), + ] + last_channel = adjust_channels(1280 // reduce_divider) # C5 + elif arch == "mobilenet_v3_small": + inverted_residual_setting = [ + bneck_conf(16, 3, 16, 16, True, "RE", 2, 1), # C1 + bneck_conf(16, 3, 72, 24, False, "RE", 2, 1), # C2 + bneck_conf(24, 3, 88, 24, False, "RE", 1, 1), + bneck_conf(24, 5, 96, 40, True, "HS", 2, 1), # C3 + bneck_conf(40, 5, 240, 40, True, "HS", 1, 1), + bneck_conf(40, 5, 240, 40, True, "HS", 1, 1), + bneck_conf(40, 5, 120, 48, True, "HS", 1, 1), + bneck_conf(48, 5, 144, 48, True, "HS", 1, 1), + bneck_conf(48, 5, 288, 96 // reduce_divider, True, "HS", 2, dilation), # C4 + bneck_conf(96 // reduce_divider, 5, 576 // reduce_divider, 96 // reduce_divider, True, "HS", 1, dilation), + bneck_conf(96 // reduce_divider, 5, 576 // reduce_divider, 96 // reduce_divider, True, "HS", 1, dilation), + ] + last_channel = adjust_channels(1024 // reduce_divider) # C5 + else: + raise ValueError(f"Unsupported model type {arch}") + + return inverted_residual_setting, last_channel + + +def _mobilenet_v3( + arch: str, + inverted_residual_setting: List[InvertedResidualConfig], + last_channel: int, + pretrained: bool, + progress: bool, + **kwargs: Any, +): + model = MobileNetV3(inverted_residual_setting, last_channel, **kwargs) + if pretrained: + if model_urls.get(arch, None) is None: + raise ValueError(f"No checkpoint is available for model type {arch}") + state_dict = load_state_dict_from_url(model_urls[arch], progress=progress) + model.load_state_dict(state_dict) + return model + + +def mobilenet_v3_large(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> MobileNetV3: + """ + Constructs a large MobileNetV3 architecture from + `"Searching for MobileNetV3" `_. + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + arch = "mobilenet_v3_large" + inverted_residual_setting, last_channel = _mobilenet_v3_conf(arch, **kwargs) + return _mobilenet_v3(arch, inverted_residual_setting, last_channel, pretrained, progress, **kwargs) + + +def mobilenet_v3_small(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> MobileNetV3: + """ + Constructs a small MobileNetV3 architecture from + `"Searching for MobileNetV3" `_. + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + arch = "mobilenet_v3_small" + inverted_residual_setting, last_channel = _mobilenet_v3_conf(arch, **kwargs) + return _mobilenet_v3(arch, inverted_residual_setting, last_channel, pretrained, progress, **kwargs) diff --git a/cv/classification/mobilenetv3/pytorch/_torchvision/ops/__init__.py b/cv/classification/mobilenetv3/pytorch/_torchvision/ops/__init__.py new file mode 100644 index 000000000..a85d1e739 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/_torchvision/ops/__init__.py @@ -0,0 +1,4 @@ +from .misc import FrozenBatchNorm2d, ConvNormActivation, SqueezeExcitation +from .stochastic_depth import stochastic_depth, StochasticDepth + +__all__ = [k for k in globals().keys() if not k.startswith("_")] diff --git a/cv/classification/mobilenetv3/pytorch/_torchvision/ops/misc.py b/cv/classification/mobilenetv3/pytorch/_torchvision/ops/misc.py new file mode 100644 index 000000000..268962e20 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/_torchvision/ops/misc.py @@ -0,0 +1,163 @@ +from typing import Callable, List, Optional + +import torch +from torch import Tensor + +from ..utils import _log_api_usage_once + + +interpolate = torch.nn.functional.interpolate + + +# This is not in nn +class FrozenBatchNorm2d(torch.nn.Module): + """ + BatchNorm2d where the batch statistics and the affine parameters are fixed + + Args: + num_features (int): Number of features ``C`` from an expected input of size ``(N, C, H, W)`` + eps (float): a value added to the denominator for numerical stability. Default: 1e-5 + """ + + def __init__( + self, + num_features: int, + eps: float = 1e-5, + ): + super().__init__() + _log_api_usage_once(self) + self.eps = eps + self.register_buffer("weight", torch.ones(num_features)) + self.register_buffer("bias", torch.zeros(num_features)) + self.register_buffer("running_mean", torch.zeros(num_features)) + self.register_buffer("running_var", torch.ones(num_features)) + + def _load_from_state_dict( + self, + state_dict: dict, + prefix: str, + local_metadata: dict, + strict: bool, + missing_keys: List[str], + unexpected_keys: List[str], + error_msgs: List[str], + ): + num_batches_tracked_key = prefix + "num_batches_tracked" + if num_batches_tracked_key in state_dict: + del state_dict[num_batches_tracked_key] + + super()._load_from_state_dict( + state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs + ) + + def forward(self, x: Tensor) -> Tensor: + # move reshapes to the beginning + # to make it fuser-friendly + w = self.weight.reshape(1, -1, 1, 1) + b = self.bias.reshape(1, -1, 1, 1) + rv = self.running_var.reshape(1, -1, 1, 1) + rm = self.running_mean.reshape(1, -1, 1, 1) + scale = w * (rv + self.eps).rsqrt() + bias = b - rm * scale + return x * scale + bias + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.weight.shape[0]}, eps={self.eps})" + + +class ConvNormActivation(torch.nn.Sequential): + """ + Configurable block used for Convolution-Normalzation-Activation blocks. + + Args: + in_channels (int): Number of channels in the input image + out_channels (int): Number of channels produced by the Convolution-Normalzation-Activation block + kernel_size: (int, optional): Size of the convolving kernel. Default: 3 + stride (int, optional): Stride of the convolution. Default: 1 + padding (int, tuple or str, optional): Padding added to all four sides of the input. Default: None, in wich case it will calculated as ``padding = (kernel_size - 1) // 2 * dilation`` + groups (int, optional): Number of blocked connections from input channels to output channels. Default: 1 + norm_layer (Callable[..., torch.nn.Module], optional): Norm layer that will be stacked on top of the convolutiuon layer. If ``None`` this layer wont be used. Default: ``torch.nn.BatchNorm2d`` + activation_layer (Callable[..., torch.nn.Module], optinal): Activation function which will be stacked on top of the normalization layer (if not None), otherwise on top of the conv layer. If ``None`` this layer wont be used. Default: ``torch.nn.ReLU`` + dilation (int): Spacing between kernel elements. Default: 1 + inplace (bool): Parameter for the activation layer, which can optionally do the operation in-place. Default ``True`` + bias (bool, optional): Whether to use bias in the convolution layer. By default, biases are included if ``norm_layer is None``. + + """ + + def __init__( + self, + in_channels: int, + out_channels: int, + kernel_size: int = 3, + stride: int = 1, + padding: Optional[int] = None, + groups: int = 1, + norm_layer: Optional[Callable[..., torch.nn.Module]] = torch.nn.BatchNorm2d, + activation_layer: Optional[Callable[..., torch.nn.Module]] = torch.nn.ReLU, + dilation: int = 1, + inplace: Optional[bool] = True, + bias: Optional[bool] = None, + ) -> None: + if padding is None: + padding = (kernel_size - 1) // 2 * dilation + if bias is None: + bias = norm_layer is None + layers = [ + torch.nn.Conv2d( + in_channels, + out_channels, + kernel_size, + stride, + padding, + dilation=dilation, + groups=groups, + bias=bias, + ) + ] + if norm_layer is not None: + layers.append(norm_layer(out_channels)) + if activation_layer is not None: + params = {} if inplace is None else {"inplace": inplace} + layers.append(activation_layer(**params)) + super().__init__(*layers) + _log_api_usage_once(self) + self.out_channels = out_channels + + +class SqueezeExcitation(torch.nn.Module): + """ + This block implements the Squeeze-and-Excitation block from https://arxiv.org/abs/1709.01507 (see Fig. 1). + Parameters ``activation``, and ``scale_activation`` correspond to ``delta`` and ``sigma`` in in eq. 3. + + Args: + input_channels (int): Number of channels in the input image + squeeze_channels (int): Number of squeeze channels + activation (Callable[..., torch.nn.Module], optional): ``delta`` activation. Default: ``torch.nn.ReLU`` + scale_activation (Callable[..., torch.nn.Module]): ``sigma`` activation. Default: ``torch.nn.Sigmoid`` + """ + + def __init__( + self, + input_channels: int, + squeeze_channels: int, + activation: Callable[..., torch.nn.Module] = torch.nn.ReLU, + scale_activation: Callable[..., torch.nn.Module] = torch.nn.Sigmoid, + ) -> None: + super().__init__() + _log_api_usage_once(self) + self.avgpool = torch.nn.AdaptiveAvgPool2d(1) + self.fc1 = torch.nn.Conv2d(input_channels, squeeze_channels, 1) + self.fc2 = torch.nn.Conv2d(squeeze_channels, input_channels, 1) + self.activation = activation() + self.scale_activation = scale_activation() + + def _scale(self, input: Tensor) -> Tensor: + scale = self.avgpool(input) + scale = self.fc1(scale) + scale = self.activation(scale) + scale = self.fc2(scale) + return self.scale_activation(scale) + + def forward(self, input: Tensor) -> Tensor: + scale = self._scale(input) + return scale * input diff --git a/cv/classification/mobilenetv3/pytorch/_torchvision/ops/stochastic_depth.py b/cv/classification/mobilenetv3/pytorch/_torchvision/ops/stochastic_depth.py new file mode 100644 index 000000000..ff8167b23 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/_torchvision/ops/stochastic_depth.py @@ -0,0 +1,66 @@ +import torch +import torch.fx +from torch import nn, Tensor + +from ..utils import _log_api_usage_once + + +def stochastic_depth(input: Tensor, p: float, mode: str, training: bool = True) -> Tensor: + """ + Implements the Stochastic Depth from `"Deep Networks with Stochastic Depth" + `_ used for randomly dropping residual + branches of residual architectures. + + Args: + input (Tensor[N, ...]): The input tensor or arbitrary dimensions with the first one + being its batch i.e. a batch with ``N`` rows. + p (float): probability of the input to be zeroed. + mode (str): ``"batch"`` or ``"row"``. + ``"batch"`` randomly zeroes the entire input, ``"row"`` zeroes + randomly selected rows from the batch. + training: apply stochastic depth if is ``True``. Default: ``True`` + + Returns: + Tensor[N, ...]: The randomly zeroed tensor. + """ + if not torch.jit.is_scripting() and not torch.jit.is_tracing(): + _log_api_usage_once(stochastic_depth) + if p < 0.0 or p > 1.0: + raise ValueError(f"drop probability has to be between 0 and 1, but got {p}") + if mode not in ["batch", "row"]: + raise ValueError(f"mode has to be either 'batch' or 'row', but got {mode}") + if not training or p == 0.0: + return input + + survival_rate = 1.0 - p + if mode == "row": + size = [input.shape[0]] + [1] * (input.ndim - 1) + else: + size = [1] * input.ndim + noise = torch.empty(size, dtype=input.dtype, device=input.device) + noise = noise.bernoulli_(survival_rate) + if survival_rate > 0.0: + noise.div_(survival_rate) + return input * noise + + +torch.fx.wrap("stochastic_depth") + + +class StochasticDepth(nn.Module): + """ + See :func:`stochastic_depth`. + """ + + def __init__(self, p: float, mode: str) -> None: + super().__init__() + _log_api_usage_once(self) + self.p = p + self.mode = mode + + def forward(self, input: Tensor) -> Tensor: + return stochastic_depth(input, self.p, self.mode, self.training) + + def __repr__(self) -> str: + s = f"{self.__class__.__name__}(p={self.p}, mode={self.mode})" + return s diff --git a/cv/classification/mobilenetv3/pytorch/_torchvision/utils.py b/cv/classification/mobilenetv3/pytorch/_torchvision/utils.py new file mode 100644 index 000000000..d03802d9a --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/_torchvision/utils.py @@ -0,0 +1,548 @@ +import math +import pathlib +import warnings +from types import FunctionType +from typing import Any, BinaryIO, List, Optional, Tuple, Union + +import numpy as np +import torch +from PIL import Image, ImageColor, ImageDraw, ImageFont + +__all__ = [ + "make_grid", + "save_image", + "draw_bounding_boxes", + "draw_segmentation_masks", + "draw_keypoints", + "flow_to_image", +] + + +@torch.no_grad() +def make_grid( + tensor: Union[torch.Tensor, List[torch.Tensor]], + nrow: int = 8, + padding: int = 2, + normalize: bool = False, + value_range: Optional[Tuple[int, int]] = None, + scale_each: bool = False, + pad_value: float = 0.0, + **kwargs, +) -> torch.Tensor: + """ + Make a grid of images. + + Args: + tensor (Tensor or list): 4D mini-batch Tensor of shape (B x C x H x W) + or a list of images all of the same size. + nrow (int, optional): Number of images displayed in each row of the grid. + The final grid size is ``(B / nrow, nrow)``. Default: ``8``. + padding (int, optional): amount of padding. Default: ``2``. + normalize (bool, optional): If True, shift the image to the range (0, 1), + by the min and max values specified by ``value_range``. Default: ``False``. + value_range (tuple, optional): tuple (min, max) where min and max are numbers, + then these numbers are used to normalize the image. By default, min and max + are computed from the tensor. + range (tuple. optional): + .. warning:: + This parameter was deprecated in ``0.12`` and will be removed in ``0.14``. Please use ``value_range`` + instead. + scale_each (bool, optional): If ``True``, scale each image in the batch of + images separately rather than the (min, max) over all images. Default: ``False``. + pad_value (float, optional): Value for the padded pixels. Default: ``0``. + + Returns: + grid (Tensor): the tensor containing grid of images. + """ + if not torch.jit.is_scripting() and not torch.jit.is_tracing(): + _log_api_usage_once(make_grid) + if not (torch.is_tensor(tensor) or (isinstance(tensor, list) and all(torch.is_tensor(t) for t in tensor))): + raise TypeError(f"tensor or list of tensors expected, got {type(tensor)}") + + if "range" in kwargs.keys(): + warnings.warn( + "The parameter 'range' is deprecated since 0.12 and will be removed in 0.14. " + "Please use 'value_range' instead." + ) + value_range = kwargs["range"] + + # if list of tensors, convert to a 4D mini-batch Tensor + if isinstance(tensor, list): + tensor = torch.stack(tensor, dim=0) + + if tensor.dim() == 2: # single image H x W + tensor = tensor.unsqueeze(0) + if tensor.dim() == 3: # single image + if tensor.size(0) == 1: # if single-channel, convert to 3-channel + tensor = torch.cat((tensor, tensor, tensor), 0) + tensor = tensor.unsqueeze(0) + + if tensor.dim() == 4 and tensor.size(1) == 1: # single-channel images + tensor = torch.cat((tensor, tensor, tensor), 1) + + if normalize is True: + tensor = tensor.clone() # avoid modifying tensor in-place + if value_range is not None: + assert isinstance( + value_range, tuple + ), "value_range has to be a tuple (min, max) if specified. min and max are numbers" + + def norm_ip(img, low, high): + img.clamp_(min=low, max=high) + img.sub_(low).div_(max(high - low, 1e-5)) + + def norm_range(t, value_range): + if value_range is not None: + norm_ip(t, value_range[0], value_range[1]) + else: + norm_ip(t, float(t.min()), float(t.max())) + + if scale_each is True: + for t in tensor: # loop over mini-batch dimension + norm_range(t, value_range) + else: + norm_range(tensor, value_range) + + assert isinstance(tensor, torch.Tensor) + if tensor.size(0) == 1: + return tensor.squeeze(0) + + # make the mini-batch of images into a grid + nmaps = tensor.size(0) + xmaps = min(nrow, nmaps) + ymaps = int(math.ceil(float(nmaps) / xmaps)) + height, width = int(tensor.size(2) + padding), int(tensor.size(3) + padding) + num_channels = tensor.size(1) + grid = tensor.new_full((num_channels, height * ymaps + padding, width * xmaps + padding), pad_value) + k = 0 + for y in range(ymaps): + for x in range(xmaps): + if k >= nmaps: + break + # Tensor.copy_() is a valid method but seems to be missing from the stubs + # https://pytorch.org/docs/stable/tensors.html#torch.Tensor.copy_ + grid.narrow(1, y * height + padding, height - padding).narrow( # type: ignore[attr-defined] + 2, x * width + padding, width - padding + ).copy_(tensor[k]) + k = k + 1 + return grid + + +@torch.no_grad() +def save_image( + tensor: Union[torch.Tensor, List[torch.Tensor]], + fp: Union[str, pathlib.Path, BinaryIO], + format: Optional[str] = None, + **kwargs, +) -> None: + """ + Save a given Tensor into an image file. + + Args: + tensor (Tensor or list): Image to be saved. If given a mini-batch tensor, + saves the tensor as a grid of images by calling ``make_grid``. + fp (string or file object): A filename or a file object + format(Optional): If omitted, the format to use is determined from the filename extension. + If a file object was used instead of a filename, this parameter should always be used. + **kwargs: Other arguments are documented in ``make_grid``. + """ + + if not torch.jit.is_scripting() and not torch.jit.is_tracing(): + _log_api_usage_once(save_image) + grid = make_grid(tensor, **kwargs) + # Add 0.5 after unnormalizing to [0, 255] to round to nearest integer + ndarr = grid.mul(255).add_(0.5).clamp_(0, 255).permute(1, 2, 0).to("cpu", torch.uint8).numpy() + im = Image.fromarray(ndarr) + im.save(fp, format=format) + + +@torch.no_grad() +def draw_bounding_boxes( + image: torch.Tensor, + boxes: torch.Tensor, + labels: Optional[List[str]] = None, + colors: Optional[Union[List[Union[str, Tuple[int, int, int]]], str, Tuple[int, int, int]]] = None, + fill: Optional[bool] = False, + width: int = 1, + font: Optional[str] = None, + font_size: int = 10, +) -> torch.Tensor: + + """ + Draws bounding boxes on given image. + The values of the input image should be uint8 between 0 and 255. + If fill is True, Resulting Tensor should be saved as PNG image. + + Args: + image (Tensor): Tensor of shape (C x H x W) and dtype uint8. + boxes (Tensor): Tensor of size (N, 4) containing bounding boxes in (xmin, ymin, xmax, ymax) format. Note that + the boxes are absolute coordinates with respect to the image. In other words: `0 <= xmin < xmax < W` and + `0 <= ymin < ymax < H`. + labels (List[str]): List containing the labels of bounding boxes. + colors (color or list of colors, optional): List containing the colors + of the boxes or single color for all boxes. The color can be represented as + PIL strings e.g. "red" or "#FF00FF", or as RGB tuples e.g. ``(240, 10, 157)``. + By default, random colors are generated for boxes. + fill (bool): If `True` fills the bounding box with specified color. + width (int): Width of bounding box. + font (str): A filename containing a TrueType font. If the file is not found in this filename, the loader may + also search in other directories, such as the `fonts/` directory on Windows or `/Library/Fonts/`, + `/System/Library/Fonts/` and `~/Library/Fonts/` on macOS. + font_size (int): The requested font size in points. + + Returns: + img (Tensor[C, H, W]): Image Tensor of dtype uint8 with bounding boxes plotted. + """ + + if not torch.jit.is_scripting() and not torch.jit.is_tracing(): + _log_api_usage_once(draw_bounding_boxes) + if not isinstance(image, torch.Tensor): + raise TypeError(f"Tensor expected, got {type(image)}") + elif image.dtype != torch.uint8: + raise ValueError(f"Tensor uint8 expected, got {image.dtype}") + elif image.dim() != 3: + raise ValueError("Pass individual images, not batches") + elif image.size(0) not in {1, 3}: + raise ValueError("Only grayscale and RGB images are supported") + + num_boxes = boxes.shape[0] + + if labels is None: + labels: Union[List[str], List[None]] = [None] * num_boxes # type: ignore[no-redef] + elif len(labels) != num_boxes: + raise ValueError( + f"Number of boxes ({num_boxes}) and labels ({len(labels)}) mismatch. Please specify labels for each box." + ) + + if colors is None: + colors = _generate_color_palette(num_boxes) + elif isinstance(colors, list): + if len(colors) < num_boxes: + raise ValueError(f"Number of colors ({len(colors)}) is less than number of boxes ({num_boxes}). ") + else: # colors specifies a single color for all boxes + colors = [colors] * num_boxes + + colors = [(ImageColor.getrgb(color) if isinstance(color, str) else color) for color in colors] + + # Handle Grayscale images + if image.size(0) == 1: + image = torch.tile(image, (3, 1, 1)) + + ndarr = image.permute(1, 2, 0).cpu().numpy() + img_to_draw = Image.fromarray(ndarr) + img_boxes = boxes.to(torch.int64).tolist() + + if fill: + draw = ImageDraw.Draw(img_to_draw, "RGBA") + else: + draw = ImageDraw.Draw(img_to_draw) + + txt_font = ImageFont.load_default() if font is None else ImageFont.truetype(font=font, size=font_size) + + for bbox, color, label in zip(img_boxes, colors, labels): # type: ignore[arg-type] + if fill: + fill_color = color + (100,) + draw.rectangle(bbox, width=width, outline=color, fill=fill_color) + else: + draw.rectangle(bbox, width=width, outline=color) + + if label is not None: + margin = width + 1 + draw.text((bbox[0] + margin, bbox[1] + margin), label, fill=color, font=txt_font) + + return torch.from_numpy(np.array(img_to_draw)).permute(2, 0, 1).to(dtype=torch.uint8) + + +@torch.no_grad() +def draw_segmentation_masks( + image: torch.Tensor, + masks: torch.Tensor, + alpha: float = 0.8, + colors: Optional[Union[List[Union[str, Tuple[int, int, int]]], str, Tuple[int, int, int]]] = None, +) -> torch.Tensor: + + """ + Draws segmentation masks on given RGB image. + The values of the input image should be uint8 between 0 and 255. + + Args: + image (Tensor): Tensor of shape (3, H, W) and dtype uint8. + masks (Tensor): Tensor of shape (num_masks, H, W) or (H, W) and dtype bool. + alpha (float): Float number between 0 and 1 denoting the transparency of the masks. + 0 means full transparency, 1 means no transparency. + colors (color or list of colors, optional): List containing the colors + of the masks or single color for all masks. The color can be represented as + PIL strings e.g. "red" or "#FF00FF", or as RGB tuples e.g. ``(240, 10, 157)``. + By default, random colors are generated for each mask. + + Returns: + img (Tensor[C, H, W]): Image Tensor, with segmentation masks drawn on top. + """ + + if not torch.jit.is_scripting() and not torch.jit.is_tracing(): + _log_api_usage_once(draw_segmentation_masks) + if not isinstance(image, torch.Tensor): + raise TypeError(f"The image must be a tensor, got {type(image)}") + elif image.dtype != torch.uint8: + raise ValueError(f"The image dtype must be uint8, got {image.dtype}") + elif image.dim() != 3: + raise ValueError("Pass individual images, not batches") + elif image.size()[0] != 3: + raise ValueError("Pass an RGB image. Other Image formats are not supported") + if masks.ndim == 2: + masks = masks[None, :, :] + if masks.ndim != 3: + raise ValueError("masks must be of shape (H, W) or (batch_size, H, W)") + if masks.dtype != torch.bool: + raise ValueError(f"The masks must be of dtype bool. Got {masks.dtype}") + if masks.shape[-2:] != image.shape[-2:]: + raise ValueError("The image and the masks must have the same height and width") + + num_masks = masks.size()[0] + if colors is not None and num_masks > len(colors): + raise ValueError(f"There are more masks ({num_masks}) than colors ({len(colors)})") + + if colors is None: + colors = _generate_color_palette(num_masks) + + if not isinstance(colors, list): + colors = [colors] + if not isinstance(colors[0], (tuple, str)): + raise ValueError("colors must be a tuple or a string, or a list thereof") + if isinstance(colors[0], tuple) and len(colors[0]) != 3: + raise ValueError("It seems that you passed a tuple of colors instead of a list of colors") + + out_dtype = torch.uint8 + + colors_ = [] + for color in colors: + if isinstance(color, str): + color = ImageColor.getrgb(color) + colors_.append(torch.tensor(color, dtype=out_dtype)) + + img_to_draw = image.detach().clone() + # TODO: There might be a way to vectorize this + for mask, color in zip(masks, colors_): + img_to_draw[:, mask] = color[:, None] + + out = image * (1 - alpha) + img_to_draw * alpha + return out.to(out_dtype) + + +@torch.no_grad() +def draw_keypoints( + image: torch.Tensor, + keypoints: torch.Tensor, + connectivity: Optional[List[Tuple[int, int]]] = None, + colors: Optional[Union[str, Tuple[int, int, int]]] = None, + radius: int = 2, + width: int = 3, +) -> torch.Tensor: + + """ + Draws Keypoints on given RGB image. + The values of the input image should be uint8 between 0 and 255. + + Args: + image (Tensor): Tensor of shape (3, H, W) and dtype uint8. + keypoints (Tensor): Tensor of shape (num_instances, K, 2) the K keypoints location for each of the N instances, + in the format [x, y]. + connectivity (List[Tuple[int, int]]]): A List of tuple where, + each tuple contains pair of keypoints to be connected. + colors (str, Tuple): The color can be represented as + PIL strings e.g. "red" or "#FF00FF", or as RGB tuples e.g. ``(240, 10, 157)``. + radius (int): Integer denoting radius of keypoint. + width (int): Integer denoting width of line connecting keypoints. + + Returns: + img (Tensor[C, H, W]): Image Tensor of dtype uint8 with keypoints drawn. + """ + + if not torch.jit.is_scripting() and not torch.jit.is_tracing(): + _log_api_usage_once(draw_keypoints) + if not isinstance(image, torch.Tensor): + raise TypeError(f"The image must be a tensor, got {type(image)}") + elif image.dtype != torch.uint8: + raise ValueError(f"The image dtype must be uint8, got {image.dtype}") + elif image.dim() != 3: + raise ValueError("Pass individual images, not batches") + elif image.size()[0] != 3: + raise ValueError("Pass an RGB image. Other Image formats are not supported") + + if keypoints.ndim != 3: + raise ValueError("keypoints must be of shape (num_instances, K, 2)") + + ndarr = image.permute(1, 2, 0).cpu().numpy() + img_to_draw = Image.fromarray(ndarr) + draw = ImageDraw.Draw(img_to_draw) + img_kpts = keypoints.to(torch.int64).tolist() + + for kpt_id, kpt_inst in enumerate(img_kpts): + for inst_id, kpt in enumerate(kpt_inst): + x1 = kpt[0] - radius + x2 = kpt[0] + radius + y1 = kpt[1] - radius + y2 = kpt[1] + radius + draw.ellipse([x1, y1, x2, y2], fill=colors, outline=None, width=0) + + if connectivity: + for connection in connectivity: + start_pt_x = kpt_inst[connection[0]][0] + start_pt_y = kpt_inst[connection[0]][1] + + end_pt_x = kpt_inst[connection[1]][0] + end_pt_y = kpt_inst[connection[1]][1] + + draw.line( + ((start_pt_x, start_pt_y), (end_pt_x, end_pt_y)), + width=width, + ) + + return torch.from_numpy(np.array(img_to_draw)).permute(2, 0, 1).to(dtype=torch.uint8) + + +# Flow visualization code adapted from https://github.com/tomrunia/OpticalFlow_Visualization +@torch.no_grad() +def flow_to_image(flow: torch.Tensor) -> torch.Tensor: + + """ + Converts a flow to an RGB image. + + Args: + flow (Tensor): Flow of shape (N, 2, H, W) or (2, H, W) and dtype torch.float. + + Returns: + img (Tensor): Image Tensor of dtype uint8 where each color corresponds + to a given flow direction. Shape is (N, 3, H, W) or (3, H, W) depending on the input. + """ + + if flow.dtype != torch.float: + raise ValueError(f"Flow should be of dtype torch.float, got {flow.dtype}.") + + orig_shape = flow.shape + if flow.ndim == 3: + flow = flow[None] # Add batch dim + + if flow.ndim != 4 or flow.shape[1] != 2: + raise ValueError(f"Input flow should have shape (2, H, W) or (N, 2, H, W), got {orig_shape}.") + + max_norm = torch.sum(flow ** 2, dim=1).sqrt().max() + epsilon = torch.finfo((flow).dtype).eps + normalized_flow = flow / (max_norm + epsilon) + img = _normalized_flow_to_image(normalized_flow) + + if len(orig_shape) == 3: + img = img[0] # Remove batch dim + return img + + +@torch.no_grad() +def _normalized_flow_to_image(normalized_flow: torch.Tensor) -> torch.Tensor: + + """ + Converts a batch of normalized flow to an RGB image. + + Args: + normalized_flow (torch.Tensor): Normalized flow tensor of shape (N, 2, H, W) + Returns: + img (Tensor(N, 3, H, W)): Flow visualization image of dtype uint8. + """ + + N, _, H, W = normalized_flow.shape + flow_image = torch.zeros((N, 3, H, W), dtype=torch.uint8) + colorwheel = _make_colorwheel() # shape [55x3] + num_cols = colorwheel.shape[0] + norm = torch.sum(normalized_flow ** 2, dim=1).sqrt() + a = torch.atan2(-normalized_flow[:, 1, :, :], -normalized_flow[:, 0, :, :]) / torch.pi + fk = (a + 1) / 2 * (num_cols - 1) + k0 = torch.floor(fk).to(torch.long) + k1 = k0 + 1 + k1[k1 == num_cols] = 0 + f = fk - k0 + + for c in range(colorwheel.shape[1]): + tmp = colorwheel[:, c] + col0 = tmp[k0] / 255.0 + col1 = tmp[k1] / 255.0 + col = (1 - f) * col0 + f * col1 + col = 1 - norm * (1 - col) + flow_image[:, c, :, :] = torch.floor(255 * col) + return flow_image + + +def _make_colorwheel() -> torch.Tensor: + """ + Generates a color wheel for optical flow visualization as presented in: + Baker et al. "A Database and Evaluation Methodology for Optical Flow" (ICCV, 2007) + URL: http://vision.middlebury.edu/flow/flowEval-iccv07.pdf. + + Returns: + colorwheel (Tensor[55, 3]): Colorwheel Tensor. + """ + + RY = 15 + YG = 6 + GC = 4 + CB = 11 + BM = 13 + MR = 6 + + ncols = RY + YG + GC + CB + BM + MR + colorwheel = torch.zeros((ncols, 3)) + col = 0 + + # RY + colorwheel[0:RY, 0] = 255 + colorwheel[0:RY, 1] = torch.floor(255 * torch.arange(0, RY) / RY) + col = col + RY + # YG + colorwheel[col : col + YG, 0] = 255 - torch.floor(255 * torch.arange(0, YG) / YG) + colorwheel[col : col + YG, 1] = 255 + col = col + YG + # GC + colorwheel[col : col + GC, 1] = 255 + colorwheel[col : col + GC, 2] = torch.floor(255 * torch.arange(0, GC) / GC) + col = col + GC + # CB + colorwheel[col : col + CB, 1] = 255 - torch.floor(255 * torch.arange(CB) / CB) + colorwheel[col : col + CB, 2] = 255 + col = col + CB + # BM + colorwheel[col : col + BM, 2] = 255 + colorwheel[col : col + BM, 0] = torch.floor(255 * torch.arange(0, BM) / BM) + col = col + BM + # MR + colorwheel[col : col + MR, 2] = 255 - torch.floor(255 * torch.arange(MR) / MR) + colorwheel[col : col + MR, 0] = 255 + return colorwheel + + +def _generate_color_palette(num_objects: int): + palette = torch.tensor([2 ** 25 - 1, 2 ** 15 - 1, 2 ** 21 - 1]) + return [tuple((i * palette) % 255) for i in range(num_objects)] + + +def _log_api_usage_once(obj: Any) -> None: + + """ + Logs API usage(module and name) within an organization. + In a large ecosystem, it's often useful to track the PyTorch and + TorchVision APIs usage. This API provides the similar functionality to the + logging module in the Python stdlib. It can be used for debugging purpose + to log which methods are used and by default it is inactive, unless the user + manually subscribes a logger via the `SetAPIUsageLogger method `_. + Please note it is triggered only once for the same API call within a process. + It does not collect any data from open-source users since it is no-op by default. + For more information, please refer to + * PyTorch note: https://pytorch.org/docs/stable/notes/large_scale_deployments.html#api-usage-logging; + * Logging policy: https://github.com/pytorch/vision/issues/5052; + + Args: + obj (class instance or method): an object to extract info from. + """ + if not obj.__module__.startswith("torchvision"): + return + name = obj.__class__.__name__ + if isinstance(obj, FunctionType): + name = obj.__name__ + torch._C._log_api_usage_once(f"{obj.__module__}.{name}") diff --git a/cv/classification/mobilenetv3/pytorch/common_utils/__init__.py b/cv/classification/mobilenetv3/pytorch/common_utils/__init__.py new file mode 100644 index 000000000..d0fee3573 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/common_utils/__init__.py @@ -0,0 +1,24 @@ +import random + +import numpy as np + +from .dist import * +from .metric_logger import * +from .misc import * +from .smooth_value import * +from .loss import LabelSmoothingCrossEntropy + + +def manual_seed(seed, deterministic=False): + random.seed(seed) + np.random.seed(seed) + os.environ['PYTHONHASHSEED'] = str(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + if deterministic: + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + else: + torch.backends.cudnn.deterministic = False + torch.backends.cudnn.benchmark = True diff --git a/cv/classification/mobilenetv3/pytorch/common_utils/dist.py b/cv/classification/mobilenetv3/pytorch/common_utils/dist.py new file mode 100644 index 000000000..1d5cb4b16 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/common_utils/dist.py @@ -0,0 +1,145 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist + + + +def setup_for_distributed(is_master): + """ + This function disables printing when not in master process + """ + import builtins as __builtin__ + builtin_print = __builtin__.print + + def print(*args, **kwargs): + force = kwargs.pop('force', False) + if is_master or force: + builtin_print(*args, **kwargs) + + __builtin__.print = print + + +def is_dist_avail_and_initialized(): + if not dist.is_available(): + return False + if not dist.is_initialized(): + return False + return True + + +def get_world_size(): + if not is_dist_avail_and_initialized(): + return 1 + return dist.get_world_size() + + +def get_rank(): + if not is_dist_avail_and_initialized(): + return 0 + return dist.get_rank() + + +def is_main_process(): + return get_rank() == 0 + + +def save_on_master(*args, **kwargs): + if is_main_process(): + torch.save(*args, **kwargs) + + +def get_dist_backend(args=None): + DIST_BACKEND_ENV = "PT_DIST_BACKEND" + if DIST_BACKEND_ENV in os.environ: + print("WARN: Use the distributed backend of the environment.") + return os.environ[DIST_BACKEND_ENV] + + if args is None: + args = dict() + + backend_attr_name = "dist_backend" + + if hasattr(args, backend_attr_name): + return getattr(args, backend_attr_name) + + if backend_attr_name in args: + return args[backend_attr_name] + + return "nccl" + + +def init_distributed_mode(args): + if 'RANK' in os.environ and 'WORLD_SIZE' in os.environ: + args.rank = int(os.environ["RANK"]) + args.world_size = int(os.environ['WORLD_SIZE']) + args.gpu = int(os.environ['LOCAL_RANK']) + elif 'SLURM_PROCID' in os.environ: + args.rank = int(os.environ['SLURM_PROCID']) + args.gpu = args.rank % torch.cuda.device_count() + else: + print('Not using distributed mode') + args.distributed = False + return + + args.distributed = True + + torch.cuda.set_device(args.gpu) + dist_backend = get_dist_backend(args) + print('| distributed init (rank {}): {}'.format( + args.rank, args.dist_url), flush=True) + torch.distributed.init_process_group(backend=dist_backend, init_method=args.dist_url, + world_size=args.world_size, rank=args.rank) + torch.distributed.barrier() + setup_for_distributed(args.rank == 0) + + +def all_gather(data): + """ + Run all_gather on arbitrary picklable data (not necessarily tensors) + Args: + data: any picklable object + Returns: + list[data]: list of data gathered from each rank + """ + world_size = get_world_size() + if world_size == 1: + return [data] + data_list = [None] * world_size + dist.all_gather_object(data_list, data) + return data_list + + +def reduce_dict(input_dict, average=True): + """ + Args: + input_dict (dict): all the values will be reduced + average (bool): whether to do average or sum + Reduce the values in the dictionary from all processes so that all processes + have the averaged results. Returns a dict with the same fields as + input_dict, after reduction. + """ + world_size = get_world_size() + if world_size < 2: + return input_dict + with torch.no_grad(): + names = [] + values = [] + # sort the keys so that they are consistent across processes + for k in sorted(input_dict.keys()): + names.append(k) + values.append(input_dict[k]) + values = torch.stack(values, dim=0) + dist.all_reduce(values) + if average: + values /= world_size + reduced_dict = {k: v for k, v in zip(names, values)} + return reduced_dict diff --git a/cv/classification/mobilenetv3/pytorch/common_utils/loss.py b/cv/classification/mobilenetv3/pytorch/common_utils/loss.py new file mode 100644 index 000000000..b96fad037 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/common_utils/loss.py @@ -0,0 +1,26 @@ +""" +Implement the CrossEntropyLoss with labelsmoothing! + +""" + +import torch +import torch.nn as nn +import torch.nn.functional as F + + +def reduce_loss(loss, reduction='mean'): + return loss.mean() if reduction == 'mean' else loss.sum() if reduction == 'sum' else loss + + +class LabelSmoothingCrossEntropy(nn.Module): + def __init__(self, epsilon=0.1, reduction='mean'): + super().__init__() + self.epsilon = epsilon + self.reduction = reduction + + def forward(self, preds, target): + c = preds.size()[-1] + log_preds = F.log_softmax(preds, dim=-1) + loss = reduce_loss(-log_preds.sum(dim=-1), self.reduction) + nll = F.nll_loss(log_preds, target, reduction=self.reduction) + return (1 - self.epsilon) * nll + self.epsilon * (loss / c) diff --git a/cv/classification/mobilenetv3/pytorch/common_utils/metric_logger.py b/cv/classification/mobilenetv3/pytorch/common_utils/metric_logger.py new file mode 100644 index 000000000..960641c4d --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/common_utils/metric_logger.py @@ -0,0 +1,94 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict +import datetime +import time + +import torch +from .smooth_value import SmoothedValue + +""" +Examples: + +logger = MetricLogger(" ") + +>>> # For iter dataloader +>>> metric_logger.add_meter('img/s', utils.SmoothedValue(window_size=10, fmt='{value}')) +>>> header = 'Epoch: [{}]'.format(epoch) +>>> for image, target in metric_logger.log_every(data_loader, print_freq, header): +>>> ... +>>> logger.metric_logger.meters['img/s'].update(fps) + +""" + +class MetricLogger(object): + + def __init__(self, delimiter="\t"): + self.meters = defaultdict(SmoothedValue) + self.delimiter = delimiter + + def update(self, **kwargs): + for k, v in kwargs.items(): + if isinstance(v, torch.Tensor): + v = v.item() + assert isinstance(v, (float, int)) + self.meters[k].update(v) + + def __getattr__(self, attr): + if attr in self.meters: + return self.meters[attr] + if attr in self.__dict__: + return self.__dict__[attr] + raise AttributeError("'{}' object has no attribute '{}'".format( + type(self).__name__, attr)) + + def __str__(self): + loss_str = [] + for name, meter in self.meters.items(): + loss_str.append( + "{}: {}".format(name, str(meter)) + ) + return self.delimiter.join(loss_str) + + def synchronize_between_processes(self): + for meter in self.meters.values(): + meter.synchronize_between_processes() + + def add_meter(self, name, meter): + self.meters[name] = meter + + def log_every(self, iterable, print_freq, header=None): + i = 0 + if not header: + header = '' + start_time = time.time() + end = time.time() + iter_time = SmoothedValue(fmt='{avg:.4f}') + data_time = SmoothedValue(fmt='{avg:.4f}') + space_fmt = ':' + str(len(str(len(iterable)))) + 'd' + log_msg = self.delimiter.join([ + header, + '[{0' + space_fmt + '}/{1}]', + 'eta: {eta}', + '{meters}', + 'time: {time}', + 'data: {data}' + ]) + for obj in iterable: + data_time.update(time.time() - end) + yield obj + iter_time.update(time.time() - end) + if i % print_freq == 0: + eta_seconds = iter_time.global_avg * (len(iterable) - i) + eta_string = str(datetime.timedelta(seconds=int(eta_seconds))) + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time))) + i += 1 + end = time.time() + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('{} Total time: {}'.format(header, total_time_str)) diff --git a/cv/classification/mobilenetv3/pytorch/common_utils/misc.py b/cv/classification/mobilenetv3/pytorch/common_utils/misc.py new file mode 100644 index 000000000..c9b501cf8 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/common_utils/misc.py @@ -0,0 +1,11 @@ +import os +import sys +import errno + + +def mkdir(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise \ No newline at end of file diff --git a/cv/classification/mobilenetv3/pytorch/common_utils/smooth_value.py b/cv/classification/mobilenetv3/pytorch/common_utils/smooth_value.py new file mode 100644 index 000000000..30cb89d60 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/common_utils/smooth_value.py @@ -0,0 +1,75 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist +from .dist import is_dist_avail_and_initialized + + +class SmoothedValue(object): + """Track a series of values and provide access to smoothed values over a + window or the global series average. + """ + + def __init__(self, window_size=20, fmt=None): + if fmt is None: + fmt = "{median:.4f} ({global_avg:.4f})" + self.deque = deque(maxlen=window_size) + self.total = 0.0 + self.count = 0 + self.fmt = fmt + + def update(self, value, n=1): + self.deque.append(value) + self.count += n + self.total += value * n + + def synchronize_between_processes(self): + """ + Warning: does not synchronize the deque! + """ + if not is_dist_avail_and_initialized(): + return + t = torch.tensor([self.count, self.total], dtype=torch.float32, device='cuda') + dist.barrier() + dist.all_reduce(t) + t = t.tolist() + self.count = int(t[0]) + self.total = t[1] + + @property + def median(self): + d = torch.tensor(list(self.deque)) + return d.median().item() + + @property + def avg(self): + d = torch.tensor(list(self.deque), dtype=torch.float32) + return d.mean().item() + + @property + def global_avg(self): + return self.total / self.count + + @property + def max(self): + return max(self.deque) + + @property + def value(self): + return self.deque[-1] + + def __str__(self): + return self.fmt.format( + median=self.median, + avg=self.avg, + global_avg=self.global_avg, + max=self.max, + value=self.value) \ No newline at end of file diff --git a/cv/classification/mobilenetv3/pytorch/dataloader/__init__.py b/cv/classification/mobilenetv3/pytorch/dataloader/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/classification/mobilenetv3/pytorch/dataloader/classification.py b/cv/classification/mobilenetv3/pytorch/dataloader/classification.py new file mode 100644 index 000000000..d600bf587 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/dataloader/classification.py @@ -0,0 +1,103 @@ +import os +import time + +import torch +import torchvision +from .utils import presets_classification as presets + +""" +Examples: + +>>> dataset_train, dataset_val = load_data(train_dir, val_dir, args) +""" + + +def get_datasets(traindir, + valdir, + resize_size=256, + crop_size=224, + auto_augment_policy=None, + random_erase_prob=0.): + # Data loading code + print("Loading data") + print("Loading training data") + dataset = torchvision.datasets.ImageFolder( + traindir, + presets.ClassificationPresetTrain(crop_size=crop_size, auto_augment_policy=auto_augment_policy, + random_erase_prob=random_erase_prob)) + + print("Loading validation data") + dataset_test = torchvision.datasets.ImageFolder( + valdir, + presets.ClassificationPresetEval(crop_size=crop_size, resize_size=resize_size)) + + return dataset, dataset_test + + +def get_input_size(model): + biger_input_size_models = ['inception'] + resize_size = 256 + crop_size = 224 + for bi_model in biger_input_size_models: + if bi_model in model: + resize_size = 342 + crop_size = 299 + + return resize_size, crop_size + + +def load_data(train_dir, val_dir, args): + auto_augment_policy = getattr(args, "auto_augment", None) + random_erase_prob = getattr(args, "random_erase", 0.0) + resize_size, crop_size = get_input_size(args.model) + dataset, dataset_test = get_datasets(train_dir, val_dir, + auto_augment_policy=auto_augment_policy, + random_erase_prob=random_erase_prob, + resize_size=resize_size, + crop_size=crop_size) + if args.distributed: + train_sampler = torch.utils.data.distributed.DistributedSampler(dataset) + test_sampler = torch.utils.data.distributed.DistributedSampler(dataset_test) + else: + train_sampler = torch.utils.data.RandomSampler(dataset) + test_sampler = torch.utils.data.SequentialSampler(dataset_test) + + return dataset, dataset_test, train_sampler, test_sampler + + +def _create_torch_dataloader(train_dir, val_dir, args): + dataset, dataset_test, train_sampler, test_sampler = load_data(train_dir, val_dir, args) + data_loader = torch.utils.data.DataLoader( + dataset, batch_size=args.batch_size, + sampler=train_sampler, num_workers=args.workers, pin_memory=True) + + data_loader_test = torch.utils.data.DataLoader( + dataset_test, batch_size=args.batch_size, + sampler=test_sampler, num_workers=args.workers, pin_memory=True) + + return data_loader, data_loader_test + + +def _create_dali_dataloader(train_dir, val_dir, args): + from .dali_classification import get_imagenet_iter_dali + device = torch.cuda.current_device() + _, crop_size = get_input_size(args.model) + data_loader = get_imagenet_iter_dali('train', train_dir, args.batch_size, + num_threads=args.workers, + device_id=device, + size=crop_size) + data_loader_test = get_imagenet_iter_dali('val', train_dir, args.batch_size, + num_threads=args.workers, + device_id=device, + size=crop_size) + + return data_loader, data_loader_test + + +def create_dataloader(train_dir, val_dir, args): + print("Creating data loaders") + if args.dali: + train_dir = os.path.dirname(train_dir) + val_dir = os.path.dirname(val_dir) + return _create_dali_dataloader(train_dir, val_dir, args) + return _create_torch_dataloader(train_dir, val_dir, args) diff --git a/cv/classification/mobilenetv3/pytorch/dataloader/dali_classification.py b/cv/classification/mobilenetv3/pytorch/dataloader/dali_classification.py new file mode 100644 index 000000000..78920141f --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/dataloader/dali_classification.py @@ -0,0 +1,111 @@ +import os + +import nvidia.dali.ops as ops +import nvidia.dali.types as types +from nvidia.dali.pipeline import Pipeline +from nvidia.dali.plugin.pytorch import DALIClassificationIterator, DALIGenericIterator + +class HybridTrainPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridTrainPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=True) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.RandomResizedCrop(device="gpu", size=size, random_area=[0.08, 1.25]) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +class HybridValPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridValPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=False) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.Resize(device="gpu", resize_x=size, resize_y=size) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + crop=(size, size), + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +def get_imagenet_iter_dali(type, image_dir, batch_size, num_threads, device_id, size): + if type == 'train': + pip_train = HybridTrainPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "train"), + size=size) + pip_train.build() + dali_iter_train = DALIClassificationIterator(pip_train, size=pip_train.epoch_size("Reader")) + return dali_iter_train + elif type == 'val': + pip_val = HybridValPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "val"), + size=size) + pip_val.build() + dali_iter_val = DALIClassificationIterator(pip_val, size=pip_val.epoch_size("Reader")) + return dali_iter_val + + +def main(arguments): + parser = argparse.ArgumentParser() + parser.add_argument('--data_dir', help='directory to save data to', type=str, default='classification data') + args = parser.parse_args(arguments) + + train_loader = get_imagenet_iter_dali(type='train', image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + val_loader = get_imagenet_iter_dali(type="val", image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + print('start dali train dataloader.') + start = time.time() + for epoch in range(20): + for i, data in enumerate(train_loader): + images = data[0]["data"].cuda(non_blocking=True) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + + # WARN: Very important + train_loader.reset() + print("Epoch", epoch) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali train dataloader.') + + + print('start dali val dataloader.') + start = time.time() + for i, data in enumerate(val_loader): + images = data[0]["data"].cuda(non_blocking=True) + print(images.shape) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + print(labels.shape) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali val dataloader.') + + +if __name__ == '__main__': + import os, time, sys + import argparse + sys.exit(main(sys.argv[1:])) \ No newline at end of file diff --git a/cv/classification/mobilenetv3/pytorch/dataloader/utils/__init__.py b/cv/classification/mobilenetv3/pytorch/dataloader/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/classification/mobilenetv3/pytorch/dataloader/utils/presets_classification.py b/cv/classification/mobilenetv3/pytorch/dataloader/utils/presets_classification.py new file mode 100644 index 000000000..6bb389ba8 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/dataloader/utils/presets_classification.py @@ -0,0 +1,37 @@ +from torchvision.transforms import autoaugment, transforms + + +class ClassificationPresetTrain: + def __init__(self, crop_size, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225), hflip_prob=0.5, + auto_augment_policy=None, random_erase_prob=0.0): + trans = [transforms.RandomResizedCrop(crop_size)] + if hflip_prob > 0: + trans.append(transforms.RandomHorizontalFlip(hflip_prob)) + if auto_augment_policy is not None: + aa_policy = autoaugment.AutoAugmentPolicy(auto_augment_policy) + trans.append(autoaugment.AutoAugment(policy=aa_policy)) + trans.extend([ + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ]) + if random_erase_prob > 0: + trans.append(transforms.RandomErasing(p=random_erase_prob)) + + self.transforms = transforms.Compose(trans) + + def __call__(self, img): + return self.transforms(img) + + +class ClassificationPresetEval: + def __init__(self, crop_size, resize_size=256, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)): + + self.transforms = transforms.Compose([ + transforms.Resize(resize_size), + transforms.CenterCrop(crop_size), + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ]) + + def __call__(self, img): + return self.transforms(img) diff --git a/cv/classification/mobilenetv3/pytorch/requirements.txt b/cv/classification/mobilenetv3/pytorch/requirements.txt index 61d5792cb..7321845b9 100644 --- a/cv/classification/mobilenetv3/pytorch/requirements.txt +++ b/cv/classification/mobilenetv3/pytorch/requirements.txt @@ -1,3 +1 @@ -torch -torchvision ---extra-index-url https://developer.download.nvidia.com/compute/redist/ nvidia-dali-cuda102==1.6.0 +numpy>=1.23.5 \ No newline at end of file diff --git a/cv/classification/mobilenetv3/pytorch/run_train.py b/cv/classification/mobilenetv3/pytorch/run_train.py deleted file mode 100644 index 651a35d54..000000000 --- a/cv/classification/mobilenetv3/pytorch/run_train.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -import os -import sys - -import torchvision - -sys.path.append("../../torchvision/pytorch") - -from train import train_model -from utils import padding_conv_channel_to_4 - -def create_model(args): - model = torchvision.models.__dict__[args.model](pretrained=args.pretrained, num_classes=args.num_classes) - args.padding_channel = False - - return model - - -train_model(create_model) diff --git a/cv/classification/mobilenetv3/pytorch/train.py b/cv/classification/mobilenetv3/pytorch/train.py new file mode 100644 index 000000000..aa2590cbc --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/train.py @@ -0,0 +1,340 @@ +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +import datetime +import os +import sys +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) + +import time +import math + +import torch +import torch.utils.data + +try: + from torch.cuda.amp import autocast, GradScaler + scaler = GradScaler() +except: + autocast = None + scaler = None + + +from torch import nn +import torch.distributed as dist +import _torchvision as torchvision + +import utils + +from dataloader.classification import get_datasets, create_dataloader +from common_utils import LabelSmoothingCrossEntropy + + +def compute_loss(model, image, target, criterion): + output = model(image) + if not isinstance(output, (tuple, list)): + output = [output] + losses = [] + for out in output: + losses.append(criterion(out, target)) + loss = sum(losses) + return loss, output[0] + + +def train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, print_freq, amp=False, use_dali=False): + model.train() + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=1, fmt='{value}')) + metric_logger.add_meter('img/s', utils.SmoothedValue(window_size=10, fmt='{value}')) + + header = 'Epoch: [{}]'.format(epoch) + all_fps = [] + for data in metric_logger.log_every(data_loader, print_freq, header): + if use_dali: + image, target = data[0]["data"], data[0]["label"][:, 0].long() + else: + image, target = data + start_time = time.time() + image, target = image.to(device), target.to(device) + if autocast is None or not amp: + loss, output = compute_loss(model, image, target, criterion) + else: + with autocast(): + loss, output = compute_loss(model, image, target, criterion) + + optimizer.zero_grad() + if scaler is not None and amp: + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + else: + loss.backward() + optimizer.step() + + torch.cuda.synchronize() + end_time = time.time() + + acc1, acc5 = utils.accuracy(output, target, topk=(1, 5)) + batch_size = image.shape[0] + loss_value = loss.item() + if not math.isfinite(loss_value): + print("Loss is {}, stopping training".format(loss_value)) + sys.exit(1) + metric_logger.update(loss=loss.item(), lr=optimizer.param_groups[0]["lr"]) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + fps = batch_size / (end_time - start_time) * utils.get_world_size() + metric_logger.meters['img/s'].update(fps) + all_fps.append(fps) + + print(header, 'Avg img/s:', sum(all_fps) / len(all_fps)) + + +def evaluate(model, criterion, data_loader, device, print_freq=100, use_dali=False): + model.eval() + metric_logger = utils.MetricLogger(delimiter=" ") + header = 'Test:' + with torch.no_grad(): + for data in metric_logger.log_every(data_loader, print_freq, header): + if use_dali: + image, target = data[0]["data"], data[0]["label"][:, 0].long() + else: + image, target = data + image = image.to(device, non_blocking=True) + target = target.to(device, non_blocking=True) + output = model(image) + loss = criterion(output, target) + + acc1, acc5 = utils.accuracy(output, target, topk=(1, 5)) + # FIXME need to take into account that the datasets + # could have been padded in distributed setup + batch_size = image.shape[0] + metric_logger.update(loss=loss.item()) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + # gather the stats from all processes + metric_logger.synchronize_between_processes() + + print(' * Acc@1 {top1.global_avg:.3f} Acc@5 {top5.global_avg:.3f}' + .format(top1=metric_logger.acc1, top5=metric_logger.acc5)) + return metric_logger.acc1.global_avg + + +def _get_cache_path(filepath): + import hashlib + h = hashlib.sha1(filepath.encode()).hexdigest() + cache_path = os.path.join("~", ".torch", "vision", "datasets", "imagefolder", h[:10] + ".pt") + cache_path = os.path.expanduser(cache_path) + return cache_path + + +def main(args): + if args.output_dir: + utils.mkdir(args.output_dir) + + utils.init_distributed_mode(args) + print(args) + + device = torch.device(args.device) + + utils.manual_seed(args.seed, deterministic=False) + # torch.backends.cudnn.benchmark = True + + # WARN: + if dist.is_initialized(): + num_gpu = dist.get_world_size() + else: + num_gpu = 1 + + global_batch_size = num_gpu * args.batch_size + + train_dir = os.path.join(args.data_path, 'train') + val_dir = os.path.join(args.data_path, 'val') + + num_classes = len(os.listdir(train_dir)) + if 0 < num_classes < 13: + if global_batch_size > 512: + if utils.is_main_process(): + print("WARN: Updating global batch size to 512, avoid non-convergence when training small dataset.") + args.batch_size = 512 // num_gpu + + data_loader, data_loader_test = create_dataloader(train_dir, val_dir, args) + + print(f"Creating model {args.model}") + model = torchvision.models.__dict__[args.model](pretrained=args.pretrained) + model.to(device) + if args.distributed and args.sync_bn: + model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model) + + # criterion = nn.CrossEntropyLoss() + criterion = LabelSmoothingCrossEntropy() + + opt_name = args.opt.lower() + if opt_name == 'sgd': + optimizer = torch.optim.SGD( + model.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.weight_decay) + elif opt_name == 'rmsprop': + optimizer = torch.optim.RMSprop(model.parameters(), lr=args.lr, momentum=args.momentum, + weight_decay=args.weight_decay, eps=0.0316, alpha=0.9) + else: + raise RuntimeError("Invalid optimizer {}. Only SGD and RMSprop are supported.".format(args.opt)) + + # lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=args.lr_step_size, gamma=args.lr_gamma) + lr_scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[30, 60, 80 ,90], gamma=args.lr_gamma) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + if args.resume: + checkpoint = torch.load(args.resume, map_location='cpu') + model_without_ddp.load_state_dict(checkpoint['model']) + optimizer.load_state_dict(checkpoint['optimizer']) + lr_scheduler.load_state_dict(checkpoint['lr_scheduler']) + args.start_epoch = checkpoint['epoch'] + 1 + + if args.test_only: + evaluate(model, criterion, data_loader_test, device=device) + return + + print("Start training") + start_time = time.time() + best_acc = 0 + for epoch in range(args.start_epoch, args.epochs): + epoch_start_time = time.time() + if args.distributed and not args.dali: + data_loader.sampler.set_epoch(epoch) + train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, args.print_freq, args.amp, use_dali=args.dali) + lr_scheduler.step() + acc_avg = evaluate(model, criterion, data_loader_test, device=device, use_dali=args.dali) + if acc_avg > args.acc_thresh: + print("The accuracy has been exceeded {},and the training is terminated at epoch {}".format(args.acc_thresh, epoch)) + return + if acc_avg > best_acc: + if args.output_dir: + checkpoint = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'lr_scheduler': lr_scheduler.state_dict(), + 'epoch': epoch, + 'args': args} + utils.save_on_master( + checkpoint, + os.path.join(args.output_dir, 'model_best.pth')) + epoch_total_time = time.time() - epoch_start_time + epoch_total_time_str = str(datetime.timedelta(seconds=int(epoch_total_time))) + print('epoch time {}'.format(epoch_total_time_str)) + best_acc = acc_avg + + if args.dali: + data_loader.reset() + data_loader_test.reset() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +def get_args_parser(add_help=True): + import argparse + parser = argparse.ArgumentParser(description='PyTorch Classification Training', add_help=add_help) + + parser.add_argument('--data-path', default='/datasets01/imagenet_full_size/061417/', help='dataset') + parser.add_argument('--model', default='resnet18', help='model') + parser.add_argument('--device', default='cuda', help='device') + parser.add_argument('-b', '--batch-size', default=32, type=int) + parser.add_argument('--epochs', default=90, type=int, metavar='N', + help='number of total epochs to run') + parser.add_argument('-j', '--workers', default=16, type=int, metavar='N', + help='number of data loading workers (default: 16)') + parser.add_argument('--opt', default='sgd', type=str, help='optimizer') + parser.add_argument('--lr', + # default=0.1, + default=0.128, + type=float, + help='initial learning rate') + parser.add_argument('--momentum', default=0.9, type=float, metavar='M', + help='momentum') + parser.add_argument('--wd', '--weight-decay', + # default=1e-4, + default=2e-4, + type=float, + metavar='W', help='weight decay (default: 1e-4)', + dest='weight_decay') + parser.add_argument('--acc-thresh', + default=75.0, type=float, + help='accuracy threshold') + parser.add_argument('--lr-step-size', default=30, type=int, help='decrease lr every step-size epochs') + parser.add_argument('--lr-gamma', default=0.1, type=float, help='decrease lr by a factor of lr-gamma') + parser.add_argument('--print-freq', default=10, type=int, help='print frequency') + parser.add_argument('--output-dir', default='', help='path where to save') + parser.add_argument('--resume', default='', help='resume from checkpoint') + parser.add_argument('--start-epoch', default=0, type=int, metavar='N', + help='start epoch') + parser.add_argument( + "--cache-dataset", + dest="cache_dataset", + help="Cache the datasets for quicker initialization. It also serializes the transforms", + action="store_true", + ) + parser.add_argument( + "--sync-bn", + dest="sync_bn", + help="Use sync batch norm", + action="store_true", + ) + parser.add_argument( + "--test-only", + dest="test_only", + help="Only test the model", + action="store_true", + ) + parser.add_argument( + "--pretrained", + dest="pretrained", + help="Use pre-trained models from the modelzoo", + action="store_true", + ) + parser.add_argument('--auto-augment', default=None, help='auto augment policy (default: None)') + parser.add_argument('--random-erase', default=0.0, type=float, help='random erasing probability (default: 0.0)') + parser.add_argument( + "--dali", + help="Use dali as dataloader", + default=False, + action="store_true", + ) + + # distributed training parameters + parser.add_argument('--local_rank', '--local-rank', default=-1, type=int, + help='Local rank') + parser.add_argument('--world-size', default=1, type=int, + help='number of distributed processes') + parser.add_argument('--dist-url', default='env://', help='url used to set up distributed training') + parser.add_argument('--amp', action='store_true', help='Automatic Mixed Precision training') + parser.add_argument('--seed', default=42, type=int, help='Random seed') + return parser + + +def get_master_addr(): + if "MASTER_ADDR" in os.environ: + return os.environ["MASTER_ADDR"] + return "127.0.0.1" + + +def check_args(args): + master_addr = get_master_addr() + if master_addr != "127.0.0.1" and args.dist_url != "env://": + args.dist_url = 'tcp://' + os.environ["MASTER_ADDR"] + ':' + os.environ["MASTER_PORT"] + + return args + + +if __name__ == "__main__": + args = get_args_parser().parse_args() + args = check_args(args) + try: + from dltest import show_training_arguments + show_training_arguments(args) + except: + pass + main(args) diff --git a/cv/classification/mobilenetv3/pytorch/train_horovod.py b/cv/classification/mobilenetv3/pytorch/train_horovod.py new file mode 100644 index 000000000..fee424a75 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/train_horovod.py @@ -0,0 +1,300 @@ +import torch +import argparse +import torch.backends.cudnn as cudnn +import torch.multiprocessing as mp +import torch.nn.functional as F +import torch.optim as optim +import torch.utils.data.distributed +from torch.utils.tensorboard import SummaryWriter +from torchvision import datasets, transforms, models +import horovod.torch as hvd +import os +import math +from tqdm import tqdm + +# Training settings +parser = argparse.ArgumentParser(description='PyTorch ImageNet Example', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument('--train-dir', default=os.path.expanduser('~/imagenet/train'), + help='path to training data') +parser.add_argument('--val-dir', default=os.path.expanduser('~/imagenet/validation'), + help='path to validation data') +parser.add_argument('--output-dir', default='./work_dirs', + help='tensorboard log directory') +parser.add_argument('--device', default='cuda', help='device') +parser.add_argument('--checkpoint-format', default='checkpoint-{epoch}.pth.tar', + help='checkpoint file format') +parser.add_argument('--fp16-allreduce', action='store_true', default=False, + help='use fp16 compression during allreduce') +parser.add_argument('--batches-per-allreduce', type=int, default=1, + help='number of batches processed locally before ' + 'executing allreduce across workers; it multiplies ' + 'total batch size.') +parser.add_argument('--use-adasum', action='store_true', default=False, + help='use adasum algorithm to do reduction') +parser.add_argument('--gradient-predivide-factor', type=float, default=1.0, + help='apply gradient predivide factor in optimizer (default: 1.0)') +parser.add_argument('--acc-thresh', default=75.0, type=float, + help='accuracy threshold') + +# Default settings from https://arxiv.org/abs/1706.02677. +parser.add_argument('--batch-size', type=int, default=32, + help='input batch size for training') +parser.add_argument('--val-batch-size', type=int, default=32, + help='input batch size for validation') +parser.add_argument('--epochs', type=int, default=90, + help='number of epochs to train') +parser.add_argument('--base-lr', type=float, default=0.0125, + help='learning rate for a single GPU') +parser.add_argument('--warmup-epochs', type=float, default=5, + help='number of warmup epochs') +parser.add_argument('--momentum', type=float, default=0.9, + help='SGD momentum') +parser.add_argument('--wd', type=float, default=0.00005, + help='weight decay') +parser.add_argument('--seed', type=int, default=42, + help='random seed') + + +def train(epoch, device): + model.train() + train_sampler.set_epoch(epoch) + train_loss = Metric('train_loss') + train_accuracy = Metric('train_accuracy') + + with tqdm(total=len(train_loader), + desc='Train Epoch #{}'.format(epoch + 1), + disable=not verbose) as t: + for batch_idx, (data, target) in enumerate(train_loader): + adjust_learning_rate(epoch, batch_idx) + + data, target = data.to(device), target.to(device) + optimizer.zero_grad() + # Split data into sub-batches of size batch_size + for i in range(0, len(data), args.batch_size): + data_batch = data[i:i + args.batch_size] + target_batch = target[i:i + args.batch_size] + output = model(data_batch) + train_accuracy.update(accuracy(output, target_batch)) + loss = F.cross_entropy(output, target_batch) + train_loss.update(loss) + # Average gradients among sub-batches + loss.div_(math.ceil(float(len(data)) / args.batch_size)) + loss.backward() + # Gradient is applied across all ranks + optimizer.step() + t.set_postfix({'loss': train_loss.avg.item(), + 'accuracy': 100. * train_accuracy.avg.item()}) + + t.update(1) + + if log_writer: + log_writer.add_scalar('train/loss', train_loss.avg, epoch) + log_writer.add_scalar('train/accuracy', train_accuracy.avg, epoch) + + +def validate(epoch, device): + model.eval() + val_loss = Metric('val_loss') + val_accuracy = Metric('val_accuracy') + + with tqdm(total=len(val_loader), + desc='Validate Epoch #{}'.format(epoch + 1), + disable=not verbose) as t: + with torch.no_grad(): + for data, target in val_loader: + data, target = data.to(device), target.to(device) + output = model(data) + + val_loss.update(F.cross_entropy(output, target)) + val_accuracy.update(accuracy(output, target)) + t.set_postfix({'loss': val_loss.avg.item(), + 'accuracy': 100. * val_accuracy.avg.item()}) + t.update(1) + + if 100.0*val_accuracy.avg.item() > args.acc_thresh: + print("The accuracy has been exceeded {},and the training is \ + terminated at epoch {}".format(args.acc_thresh, epoch)) + return + + if log_writer: + log_writer.add_scalar('val/loss', val_loss.avg, epoch) + log_writer.add_scalar('val/accuracy', val_accuracy.avg, epoch) + + +# Horovod: using `lr = base_lr * hvd.size()` from the very beginning leads to worse final +# accuracy. Scale the learning rate `lr = base_lr` ---> `lr = base_lr * hvd.size()` during +# the first five epochs. See https://arxiv.org/abs/1706.02677 for details. +# After the warmup reduce learning rate by 10 on the 30th, 60th and 80th epochs. +def adjust_learning_rate(epoch, batch_idx): + if epoch < args.warmup_epochs: + epoch += float(batch_idx + 1) / len(train_loader) + lr_adj = 1. / hvd.size() * (epoch * (hvd.size() - 1) / args.warmup_epochs + 1) + elif epoch < 30: + lr_adj = 1. + elif epoch < 60: + lr_adj = 1e-1 + elif epoch < 80: + lr_adj = 1e-2 + else: + lr_adj = 1e-3 + for param_group in optimizer.param_groups: + param_group['lr'] = args.base_lr * hvd.size() * args.batches_per_allreduce * lr_adj + + +def accuracy(output, target): + # get the index of the max log-probability + pred = output.max(1, keepdim=True)[1] + return pred.eq(target.view_as(pred)).cpu().float().mean() + + +def save_checkpoint(epoch): + if hvd.rank() == 0: + filepath = os.path.join(args.output_dir, args.checkpoint_format.format(epoch=epoch + 1)) + # filepath = args.checkpoint_format.format(epoch=epoch + 1) + state = { + 'model': model.state_dict(), + 'optimizer': optimizer.state_dict(), + } + torch.save(state, filepath) + + +# Horovod: average metrics from distributed training. +class Metric(object): + def __init__(self, name): + self.name = name + self.sum = torch.tensor(0.) + self.n = torch.tensor(0.) + + def update(self, val): + self.sum += hvd.allreduce(val.detach().cpu(), name=self.name) + self.n += 1 + + @property + def avg(self): + return self.sum / self.n + + +if __name__ == '__main__': + args = parser.parse_args() + device = torch.device(args.device) + + allreduce_batch_size = args.batch_size * args.batches_per_allreduce + + hvd.init() + torch.manual_seed(args.seed) + + # if args.cuda: + # Horovod: pin GPU to local rank. + torch.cuda.set_device(hvd.local_rank()) + torch.cuda.manual_seed(args.seed) + + cudnn.benchmark = True + + # If set > 0, will resume training from a given checkpoint. + resume_from_epoch = 0 + for try_epoch in range(args.epochs, 0, -1): + if os.path.exists(args.checkpoint_format.format(epoch=try_epoch)): + resume_from_epoch = try_epoch + break + + # Horovod: broadcast resume_from_epoch from rank 0 (which will have + # checkpoints) to other ranks. + resume_from_epoch = hvd.broadcast(torch.tensor(resume_from_epoch), root_rank=0, + name='resume_from_epoch').item() + + # Horovod: print logs on the first worker. + verbose = 1 if hvd.rank() == 0 else 0 + + # Horovod: write TensorBoard logs on first worker. + log_writer = SummaryWriter(args.output_dir) if hvd.rank() == 0 else None + + # Horovod: limit # of CPU threads to be used per worker. + torch.set_num_threads(2) + + #kwargs = {'num_workers': 4, 'pin_memory': True} if args.cuda else {} + kwargs = {'num_workers': 2, 'pin_memory': True} + + # When supported, use 'forkserver' to spawn dataloader workers instead of 'fork' to prevent + # issues with Infiniband implementations that are not fork-safe + if (kwargs.get('num_workers', 0) > 0 and hasattr(mp, '_supports_context') and + mp._supports_context and 'forkserver' in mp.get_all_start_methods()): + kwargs['multiprocessing_context'] = 'forkserver' + + train_dataset = \ + datasets.ImageFolder(args.train_dir, + transform=transforms.Compose([ + transforms.RandomResizedCrop(224), + transforms.RandomHorizontalFlip(), + transforms.ToTensor(), + transforms.Normalize(mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]) + ])) + + # Horovod: use DistributedSampler to partition data among workers. Manually specify + # `num_replicas=hvd.size()` and `rank=hvd.rank()`. + train_sampler = torch.utils.data.distributed.DistributedSampler( + train_dataset, num_replicas=hvd.size(), rank=hvd.rank()) + train_loader = torch.utils.data.DataLoader( + train_dataset, batch_size=allreduce_batch_size, + sampler=train_sampler, **kwargs) + + val_dataset = \ + datasets.ImageFolder(args.val_dir, + transform=transforms.Compose([ + transforms.Resize(256), + transforms.CenterCrop(224), + transforms.ToTensor(), + transforms.Normalize(mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]) + ])) + val_sampler = torch.utils.data.distributed.DistributedSampler( + val_dataset, num_replicas=hvd.size(), rank=hvd.rank()) + val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=args.val_batch_size, + sampler=val_sampler, **kwargs) + + + # Set up standard ResNet-50 model. + model = models.resnet50().to(device) + + # By default, Adasum doesn't need scaling up learning rate. + # For sum/average with gradient Accumulation: scale learning rate by batches_per_allreduce + lr_scaler = args.batches_per_allreduce * hvd.size() if not args.use_adasum else 1 + + # If using GPU Adasum allreduce, scale learning rate by local_size. + if args.use_adasum and hvd.nccl_built(): + lr_scaler = args.batches_per_allreduce * hvd.local_size() + + # Horovod: scale learning rate by the number of GPUs. + optimizer = optim.SGD(model.parameters(), + lr=(args.base_lr * + lr_scaler), + momentum=args.momentum, weight_decay=args.wd) + + # Horovod: (optional) compression algorithm. + compression = hvd.Compression.fp16 if args.fp16_allreduce else hvd.Compression.none + + # Horovod: wrap optimizer with DistributedOptimizer. + optimizer = hvd.DistributedOptimizer( + optimizer, named_parameters=model.named_parameters(), + compression=compression, + backward_passes_per_step=args.batches_per_allreduce, + op=hvd.Adasum if args.use_adasum else hvd.Average, + gradient_predivide_factor=args.gradient_predivide_factor) + + # Restore from a previous checkpoint, if initial_epoch is specified. + # Horovod: restore on the first worker which will broadcast weights to other workers. + if resume_from_epoch > 0 and hvd.rank() == 0: + filepath = args.checkpoint_format.format(epoch=resume_from_epoch) + checkpoint = torch.load(filepath) + model.load_state_dict(checkpoint['model']) + optimizer.load_state_dict(checkpoint['optimizer']) + + # Horovod: broadcast parameters & optimizer state. + hvd.broadcast_parameters(model.state_dict(), root_rank=0) + hvd.broadcast_optimizer_state(optimizer, root_rank=0) + + for epoch in range(resume_from_epoch, args.epochs): + train(epoch, device) + validate(epoch, device) + save_checkpoint(epoch) diff --git a/cv/classification/mobilenetv3/pytorch/train_mobilenet_v3_large_amp_dist.sh b/cv/classification/mobilenetv3/pytorch/train_mobilenet_v3_large_amp_dist.sh index 6530f9f6d..896e02d83 100755 --- a/cv/classification/mobilenetv3/pytorch/train_mobilenet_v3_large_amp_dist.sh +++ b/cv/classification/mobilenetv3/pytorch/train_mobilenet_v3_large_amp_dist.sh @@ -30,9 +30,9 @@ if [ ! -z "${DEBUG}" ];then PYTHONAR="${PYTHONAR} -m pdb" fi cd ${ROOT_DIR} -python3 $PYTHONARG ${ROOT_DIR}/run_train.py \ - --model mobilenet_v3_large --dali --dali-cpu \ - --data-path $DATA_PATH \ - --opt rmsprop --batch-size 64 \ - --lr 0.001 --wd 0.00001 --lr-step-size 2 --lr-gamma 0.973 \ +python3 $PYTHONARG train.py \ + --model mobilenet_v3_large \ + --data-path $DATA_PATH --epochs 300 \ + --opt sgd --batch-size 512 \ + --lr 0.1 --wd 0.00001 --lr-step-size 2 --lr-gamma 0.973 \ --amp --auto-augment imagenet --random-erase 0.2 "$@" diff --git a/cv/classification/mobilenetv3/pytorch/utils.py b/cv/classification/mobilenetv3/pytorch/utils.py new file mode 100644 index 000000000..3ef477ec2 --- /dev/null +++ b/cv/classification/mobilenetv3/pytorch/utils.py @@ -0,0 +1,152 @@ +from collections import defaultdict, deque, OrderedDict +import copy +import datetime +import hashlib +import time +import torch +import torch.distributed as dist + +import errno +import os + +from common_utils import * + + +def accuracy(output, target, topk=(1,)): + """Computes the accuracy over the k top predictions for the specified values of k""" + with torch.no_grad(): + maxk = max(topk) + batch_size = target.size(0) + + _, pred = output.topk(maxk, 1, True, True) + pred = pred.t() + correct = pred.eq(target[None]) + + res = [] + for k in topk: + correct_k = correct[:k].flatten().sum(dtype=torch.float32) + res.append(correct_k * (100.0 / batch_size)) + return res + + +def average_checkpoints(inputs): + """Loads checkpoints from inputs and returns a model with averaged weights. Original implementation taken from: + https://github.com/pytorch/fairseq/blob/a48f235636557b8d3bc4922a6fa90f3a0fa57955/scripts/average_checkpoints.py#L16 + + Args: + inputs (List[str]): An iterable of string paths of checkpoints to load from. + Returns: + A dict of string keys mapping to various values. The 'model' key + from the returned dict should correspond to an OrderedDict mapping + string parameter names to torch Tensors. + """ + params_dict = OrderedDict() + params_keys = None + new_state = None + num_models = len(inputs) + for fpath in inputs: + with open(fpath, "rb") as f: + state = torch.load( + f, + map_location=( + lambda s, _: torch.serialization.default_restore_location(s, "cpu") + ), + ) + # Copies over the settings from the first checkpoint + if new_state is None: + new_state = state + model_params = state["model"] + model_params_keys = list(model_params.keys()) + if params_keys is None: + params_keys = model_params_keys + elif params_keys != model_params_keys: + raise KeyError( + "For checkpoint {}, expected list of params: {}, " + "but found: {}".format(f, params_keys, model_params_keys) + ) + for k in params_keys: + p = model_params[k] + if isinstance(p, torch.HalfTensor): + p = p.float() + if k not in params_dict: + params_dict[k] = p.clone() + # NOTE: clone() is needed in case of p is a shared parameter + else: + params_dict[k] += p + averaged_params = OrderedDict() + for k, v in params_dict.items(): + averaged_params[k] = v + if averaged_params[k].is_floating_point(): + averaged_params[k].div_(num_models) + else: + averaged_params[k] //= num_models + new_state["model"] = averaged_params + return new_state + + +def store_model_weights(model, checkpoint_path, checkpoint_key='model', strict=True): + """ + This method can be used to prepare weights files for new models. It receives as + input a model architecture and a checkpoint from the training script and produces + a file with the weights ready for release. + + Examples: + from torchvision import models as M + + # Classification + model = M.mobilenet_v3_large(pretrained=False) + print(store_model_weights(model, './class.pth')) + + # Quantized Classification + model = M.quantization.mobilenet_v3_large(pretrained=False, quantize=False) + model.fuse_model() + model.qconfig = torch.quantization.get_default_qat_qconfig('qnnpack') + _ = torch.quantization.prepare_qat(model, inplace=True) + print(store_model_weights(model, './qat.pth')) + + # Object Detection + model = M.detection.fasterrcnn_mobilenet_v3_large_fpn(pretrained=False, pretrained_backbone=False) + print(store_model_weights(model, './obj.pth')) + + # Segmentation + model = M.segmentation.deeplabv3_mobilenet_v3_large(pretrained=False, pretrained_backbone=False, aux_loss=True) + print(store_model_weights(model, './segm.pth', strict=False)) + + Args: + model (pytorch.nn.Module): The model on which the weights will be loaded for validation purposes. + checkpoint_path (str): The path of the checkpoint we will load. + checkpoint_key (str, optional): The key of the checkpoint where the model weights are stored. + Default: "model". + strict (bool): whether to strictly enforce that the keys + in :attr:`state_dict` match the keys returned by this module's + :meth:`~torch.nn.Module.state_dict` function. Default: ``True`` + + Returns: + output_path (str): The location where the weights are saved. + """ + # Store the new model next to the checkpoint_path + checkpoint_path = os.path.abspath(checkpoint_path) + output_dir = os.path.dirname(checkpoint_path) + + # Deep copy to avoid side-effects on the model object. + model = copy.deepcopy(model) + checkpoint = torch.load(checkpoint_path, map_location='cpu') + + # Load the weights to the model to validate that everything works + # and remove unnecessary weights (such as auxiliaries, etc) + model.load_state_dict(checkpoint[checkpoint_key], strict=strict) + + tmp_path = os.path.join(output_dir, str(model.__hash__())) + torch.save(model.state_dict(), tmp_path) + + sha256_hash = hashlib.sha256() + with open(tmp_path, "rb") as f: + # Read and update hash string value in blocks of 4K + for byte_block in iter(lambda: f.read(4096), b""): + sha256_hash.update(byte_block) + hh = sha256_hash.hexdigest() + + output_path = os.path.join(output_dir, "weights-" + str(hh[:8]) + ".pth") + os.replace(tmp_path, output_path) + + return output_path diff --git a/cv/classification/resnet101/pytorch/.gitignore b/cv/classification/resnet101/pytorch/.gitignore new file mode 100644 index 000000000..61f2dc9f8 --- /dev/null +++ b/cv/classification/resnet101/pytorch/.gitignore @@ -0,0 +1 @@ +**/__pycache__/ diff --git a/cv/classification/resnet101/pytorch/__init__.py b/cv/classification/resnet101/pytorch/__init__.py new file mode 100644 index 000000000..6faec1658 --- /dev/null +++ b/cv/classification/resnet101/pytorch/__init__.py @@ -0,0 +1,5 @@ +from .utils import * +from .common_utils import * +from .data_loader import * + +__all__ = [k for k in globals().keys() if not k.startswith("_")] diff --git a/cv/classification/resnet101/pytorch/common_utils/__init__.py b/cv/classification/resnet101/pytorch/common_utils/__init__.py new file mode 100644 index 000000000..32e8c4f57 --- /dev/null +++ b/cv/classification/resnet101/pytorch/common_utils/__init__.py @@ -0,0 +1,23 @@ +import random + +import numpy as np + +from .dist import * +from .metric_logger import * +from .misc import * +from .smooth_value import * + +def manual_seed(seed, deterministic=False): + random.seed(seed) + np.random.seed(seed) + os.environ['PYTHONHASHSEED'] = str(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + + if deterministic: + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + else: + torch.backends.cudnn.deterministic = False + torch.backends.cudnn.benchmark = True \ No newline at end of file diff --git a/cv/classification/resnet101/pytorch/common_utils/dist.py b/cv/classification/resnet101/pytorch/common_utils/dist.py new file mode 100644 index 000000000..ea56ca267 --- /dev/null +++ b/cv/classification/resnet101/pytorch/common_utils/dist.py @@ -0,0 +1,144 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist + + + +def setup_for_distributed(is_master): + """ + This function disables printing when not in master process + """ + import builtins as __builtin__ + builtin_print = __builtin__.print + + def print(*args, **kwargs): + force = kwargs.pop('force', False) + if is_master or force: + builtin_print(*args, **kwargs) + + __builtin__.print = print + + +def is_dist_avail_and_initialized(): + if not dist.is_available(): + return False + if not dist.is_initialized(): + return False + return True + + +def get_world_size(): + if not is_dist_avail_and_initialized(): + return 1 + return dist.get_world_size() + + +def get_rank(): + if not is_dist_avail_and_initialized(): + return 0 + return dist.get_rank() + + +def is_main_process(): + return get_rank() == 0 + + +def save_on_master(*args, **kwargs): + if is_main_process(): + torch.save(*args, **kwargs) + + +def get_dist_backend(args=None): + DIST_BACKEND_ENV = "PT_DIST_BACKEND" + if DIST_BACKEND_ENV in os.environ: + return os.environ[DIST_BACKEND_ENV] + + if args is None: + args = dict() + + backend_attr_name = "dist_backend" + + if hasattr(args, backend_attr_name): + return getattr(args, backend_attr_name) + + if backend_attr_name in args: + return args[backend_attr_name] + + return "nccl" + + +def init_distributed_mode(args): + if 'RANK' in os.environ and 'WORLD_SIZE' in os.environ: + args.rank = int(os.environ["RANK"]) + args.world_size = int(os.environ['WORLD_SIZE']) + args.gpu = int(os.environ['LOCAL_RANK']) + elif 'SLURM_PROCID' in os.environ: + args.rank = int(os.environ['SLURM_PROCID']) + args.gpu = args.rank % torch.cuda.device_count() + else: + print('Not using distributed mode') + args.distributed = False + return + + args.distributed = True + + torch.cuda.set_device(args.gpu) + dist_backend = get_dist_backend(args) + print('| distributed init (rank {}): {}'.format( + args.rank, args.dist_url), flush=True) + torch.distributed.init_process_group(backend=dist_backend, init_method=args.dist_url, + world_size=args.world_size, rank=args.rank) + torch.distributed.barrier() + setup_for_distributed(args.rank == 0) + + +def all_gather(data): + """ + Run all_gather on arbitrary picklable data (not necessarily tensors) + Args: + data: any picklable object + Returns: + list[data]: list of data gathered from each rank + """ + world_size = get_world_size() + if world_size == 1: + return [data] + data_list = [None] * world_size + dist.all_gather_object(data_list, data) + return data_list + + +def reduce_dict(input_dict, average=True): + """ + Args: + input_dict (dict): all the values will be reduced + average (bool): whether to do average or sum + Reduce the values in the dictionary from all processes so that all processes + have the averaged results. Returns a dict with the same fields as + input_dict, after reduction. + """ + world_size = get_world_size() + if world_size < 2: + return input_dict + with torch.no_grad(): + names = [] + values = [] + # sort the keys so that they are consistent across processes + for k in sorted(input_dict.keys()): + names.append(k) + values.append(input_dict[k]) + values = torch.stack(values, dim=0) + dist.all_reduce(values) + if average: + values /= world_size + reduced_dict = {k: v for k, v in zip(names, values)} + return reduced_dict diff --git a/cv/classification/resnet101/pytorch/common_utils/metric_logger.py b/cv/classification/resnet101/pytorch/common_utils/metric_logger.py new file mode 100644 index 000000000..960641c4d --- /dev/null +++ b/cv/classification/resnet101/pytorch/common_utils/metric_logger.py @@ -0,0 +1,94 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict +import datetime +import time + +import torch +from .smooth_value import SmoothedValue + +""" +Examples: + +logger = MetricLogger(" ") + +>>> # For iter dataloader +>>> metric_logger.add_meter('img/s', utils.SmoothedValue(window_size=10, fmt='{value}')) +>>> header = 'Epoch: [{}]'.format(epoch) +>>> for image, target in metric_logger.log_every(data_loader, print_freq, header): +>>> ... +>>> logger.metric_logger.meters['img/s'].update(fps) + +""" + +class MetricLogger(object): + + def __init__(self, delimiter="\t"): + self.meters = defaultdict(SmoothedValue) + self.delimiter = delimiter + + def update(self, **kwargs): + for k, v in kwargs.items(): + if isinstance(v, torch.Tensor): + v = v.item() + assert isinstance(v, (float, int)) + self.meters[k].update(v) + + def __getattr__(self, attr): + if attr in self.meters: + return self.meters[attr] + if attr in self.__dict__: + return self.__dict__[attr] + raise AttributeError("'{}' object has no attribute '{}'".format( + type(self).__name__, attr)) + + def __str__(self): + loss_str = [] + for name, meter in self.meters.items(): + loss_str.append( + "{}: {}".format(name, str(meter)) + ) + return self.delimiter.join(loss_str) + + def synchronize_between_processes(self): + for meter in self.meters.values(): + meter.synchronize_between_processes() + + def add_meter(self, name, meter): + self.meters[name] = meter + + def log_every(self, iterable, print_freq, header=None): + i = 0 + if not header: + header = '' + start_time = time.time() + end = time.time() + iter_time = SmoothedValue(fmt='{avg:.4f}') + data_time = SmoothedValue(fmt='{avg:.4f}') + space_fmt = ':' + str(len(str(len(iterable)))) + 'd' + log_msg = self.delimiter.join([ + header, + '[{0' + space_fmt + '}/{1}]', + 'eta: {eta}', + '{meters}', + 'time: {time}', + 'data: {data}' + ]) + for obj in iterable: + data_time.update(time.time() - end) + yield obj + iter_time.update(time.time() - end) + if i % print_freq == 0: + eta_seconds = iter_time.global_avg * (len(iterable) - i) + eta_string = str(datetime.timedelta(seconds=int(eta_seconds))) + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time))) + i += 1 + end = time.time() + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('{} Total time: {}'.format(header, total_time_str)) diff --git a/cv/classification/resnet101/pytorch/common_utils/misc.py b/cv/classification/resnet101/pytorch/common_utils/misc.py new file mode 100644 index 000000000..c9b501cf8 --- /dev/null +++ b/cv/classification/resnet101/pytorch/common_utils/misc.py @@ -0,0 +1,11 @@ +import os +import sys +import errno + + +def mkdir(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise \ No newline at end of file diff --git a/cv/classification/resnet101/pytorch/common_utils/smooth_value.py b/cv/classification/resnet101/pytorch/common_utils/smooth_value.py new file mode 100644 index 000000000..30cb89d60 --- /dev/null +++ b/cv/classification/resnet101/pytorch/common_utils/smooth_value.py @@ -0,0 +1,75 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist +from .dist import is_dist_avail_and_initialized + + +class SmoothedValue(object): + """Track a series of values and provide access to smoothed values over a + window or the global series average. + """ + + def __init__(self, window_size=20, fmt=None): + if fmt is None: + fmt = "{median:.4f} ({global_avg:.4f})" + self.deque = deque(maxlen=window_size) + self.total = 0.0 + self.count = 0 + self.fmt = fmt + + def update(self, value, n=1): + self.deque.append(value) + self.count += n + self.total += value * n + + def synchronize_between_processes(self): + """ + Warning: does not synchronize the deque! + """ + if not is_dist_avail_and_initialized(): + return + t = torch.tensor([self.count, self.total], dtype=torch.float32, device='cuda') + dist.barrier() + dist.all_reduce(t) + t = t.tolist() + self.count = int(t[0]) + self.total = t[1] + + @property + def median(self): + d = torch.tensor(list(self.deque)) + return d.median().item() + + @property + def avg(self): + d = torch.tensor(list(self.deque), dtype=torch.float32) + return d.mean().item() + + @property + def global_avg(self): + return self.total / self.count + + @property + def max(self): + return max(self.deque) + + @property + def value(self): + return self.deque[-1] + + def __str__(self): + return self.fmt.format( + median=self.median, + avg=self.avg, + global_avg=self.global_avg, + max=self.max, + value=self.value) \ No newline at end of file diff --git a/cv/classification/resnet101/pytorch/dataloader/__init__.py b/cv/classification/resnet101/pytorch/dataloader/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/classification/resnet101/pytorch/dataloader/classification.py b/cv/classification/resnet101/pytorch/dataloader/classification.py new file mode 100644 index 000000000..030af6dee --- /dev/null +++ b/cv/classification/resnet101/pytorch/dataloader/classification.py @@ -0,0 +1,113 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +import os +import time + +import torch +import torchvision +from .utils import presets_classification as presets + +""" +Examples: + +>>> dataset_train, dataset_val = load_data(train_dir, val_dir, args) +""" + + +def get_datasets(traindir, + valdir, + resize_size=256, + crop_size=224, + auto_augment_policy=None, + random_erase_prob=0.): + # Data loading code + print("Loading data") + print("Loading training data") + dataset = torchvision.datasets.ImageFolder( + traindir, + presets.ClassificationPresetTrain(crop_size=crop_size, auto_augment_policy=auto_augment_policy, + random_erase_prob=random_erase_prob)) + + print("Loading validation data") + dataset_test = torchvision.datasets.ImageFolder( + valdir, + presets.ClassificationPresetEval(crop_size=crop_size, resize_size=resize_size)) + + return dataset, dataset_test + + +def get_input_size(model): + biger_input_size_models = ['inception'] + resize_size = 256 + crop_size = 224 + for bi_model in biger_input_size_models: + if bi_model in model: + resize_size = 342 + crop_size = 299 + + return resize_size, crop_size + + +def load_data(train_dir, val_dir, args): + auto_augment_policy = getattr(args, "auto_augment", None) + random_erase_prob = getattr(args, "random_erase", 0.0) + resize_size, crop_size = get_input_size(args.model) + dataset, dataset_test = get_datasets(train_dir, val_dir, + auto_augment_policy=auto_augment_policy, + random_erase_prob=random_erase_prob, + resize_size=resize_size, + crop_size=crop_size) + if args.distributed: + train_sampler = torch.utils.data.distributed.DistributedSampler(dataset) + test_sampler = torch.utils.data.distributed.DistributedSampler(dataset_test) + else: + train_sampler = torch.utils.data.RandomSampler(dataset) + test_sampler = torch.utils.data.SequentialSampler(dataset_test) + + return dataset, dataset_test, train_sampler, test_sampler + + +def _create_torch_dataloader(train_dir, val_dir, args): + dataset, dataset_test, train_sampler, test_sampler = load_data(train_dir, val_dir, args) + data_loader = torch.utils.data.DataLoader( + dataset, batch_size=args.batch_size, + sampler=train_sampler, num_workers=args.workers, pin_memory=True) + + data_loader_test = torch.utils.data.DataLoader( + dataset_test, batch_size=args.batch_size, + sampler=test_sampler, num_workers=args.workers, pin_memory=True) + + return data_loader, data_loader_test + + +def _create_dali_dataloader(train_dir, val_dir, args): + from .dali_classification import get_imagenet_iter_dali + device = torch.cuda.current_device() + _, crop_size = get_input_size(args.model) + data_loader = get_imagenet_iter_dali('train', train_dir, args.batch_size, + num_threads=args.workers, + device_id=device, + size=crop_size) + data_loader_test = get_imagenet_iter_dali('val', train_dir, args.batch_size, + num_threads=args.workers, + device_id=device, + size=crop_size) + + return data_loader, data_loader_test + + +def create_dataloader(train_dir, val_dir, args): + print("Creating data loaders") + if args.dali: + train_dir = os.path.dirname(train_dir) + val_dir = os.path.dirname(val_dir) + return _create_dali_dataloader(train_dir, val_dir, args) + return _create_torch_dataloader(train_dir, val_dir, args) diff --git a/cv/classification/resnet101/pytorch/dataloader/dali_classification.py b/cv/classification/resnet101/pytorch/dataloader/dali_classification.py new file mode 100644 index 000000000..4c92283b2 --- /dev/null +++ b/cv/classification/resnet101/pytorch/dataloader/dali_classification.py @@ -0,0 +1,121 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +import os + +import nvidia.dali.ops as ops +import nvidia.dali.types as types +from nvidia.dali.pipeline import Pipeline +from nvidia.dali.plugin.pytorch import DALIClassificationIterator, DALIGenericIterator + +class HybridTrainPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridTrainPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=True) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.RandomResizedCrop(device="gpu", size=size, random_area=[0.08, 1.25]) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +class HybridValPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridValPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=False) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.Resize(device="gpu", resize_x=size, resize_y=size) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + crop=(size, size), + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +def get_imagenet_iter_dali(type, image_dir, batch_size, num_threads, device_id, size): + if type == 'train': + pip_train = HybridTrainPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "train"), + size=size) + pip_train.build() + dali_iter_train = DALIClassificationIterator(pip_train, size=pip_train.epoch_size("Reader")) + return dali_iter_train + elif type == 'val': + pip_val = HybridValPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "val"), + size=size) + pip_val.build() + dali_iter_val = DALIClassificationIterator(pip_val, size=pip_val.epoch_size("Reader")) + return dali_iter_val + + +def main(arguments): + parser = argparse.ArgumentParser() + parser.add_argument('--data_dir', help='directory to save data to', type=str, default='classification data') + args = parser.parse_args(arguments) + + train_loader = get_imagenet_iter_dali(type='train', image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + val_loader = get_imagenet_iter_dali(type="val", image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + print('start dali train dataloader.') + start = time.time() + for epoch in range(20): + for i, data in enumerate(train_loader): + images = data[0]["data"].cuda(non_blocking=True) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + + # WARN: Very important + train_loader.reset() + print("Epoch", epoch) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali train dataloader.') + + + print('start dali val dataloader.') + start = time.time() + for i, data in enumerate(val_loader): + images = data[0]["data"].cuda(non_blocking=True) + print(images.shape) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + print(labels.shape) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali val dataloader.') + + +if __name__ == '__main__': + import os, time, sys + import argparse + sys.exit(main(sys.argv[1:])) \ No newline at end of file diff --git a/cv/classification/resnet101/pytorch/dataloader/utils/__init__.py b/cv/classification/resnet101/pytorch/dataloader/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/classification/resnet101/pytorch/dataloader/utils/presets_classification.py b/cv/classification/resnet101/pytorch/dataloader/utils/presets_classification.py new file mode 100644 index 000000000..b3f559af4 --- /dev/null +++ b/cv/classification/resnet101/pytorch/dataloader/utils/presets_classification.py @@ -0,0 +1,41 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from torchvision.transforms import autoaugment, transforms + + +class ClassificationPresetTrain: + def __init__(self, crop_size, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225), hflip_prob=0.5, + auto_augment_policy=None, random_erase_prob=0.0): + trans = [transforms.RandomResizedCrop(crop_size)] + if hflip_prob > 0: + trans.append(transforms.RandomHorizontalFlip(hflip_prob)) + if auto_augment_policy is not None: + aa_policy = autoaugment.AutoAugmentPolicy(auto_augment_policy) + trans.append(autoaugment.AutoAugment(policy=aa_policy)) + trans.extend([ + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ]) + if random_erase_prob > 0: + trans.append(transforms.RandomErasing(p=random_erase_prob)) + + self.transforms = transforms.Compose(trans) + + def __call__(self, img): + return self.transforms(img) + + +class ClassificationPresetEval: + def __init__(self, crop_size, resize_size=256, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)): + + self.transforms = transforms.Compose([ + transforms.Resize(resize_size), + transforms.CenterCrop(crop_size), + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ]) + + def __call__(self, img): + return self.transforms(img) diff --git a/cv/classification/resnet101/pytorch/start_scripts/get_num_devices.sh b/cv/classification/resnet101/pytorch/start_scripts/get_num_devices.sh new file mode 100644 index 000000000..f0701ce8d --- /dev/null +++ b/cv/classification/resnet101/pytorch/start_scripts/get_num_devices.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +devices=$CUDA_VISIBLE_DEVICES +if [ -n "$devices" ]; then + _devices=(${devices//,/ }) + num_devices=${#_devices[@]} +else + num_devices=2 + export CUDA_VISIBLE_DEVICES=0,1 + echo "Not found CUDA_VISIBLE_DEVICES, set nproc_per_node = ${num_devices}" +fi +export IX_NUM_CUDA_VISIBLE_DEVICES=${num_devices} diff --git a/cv/classification/resnet101/pytorch/start_scripts/init_torch.sh b/cv/classification/resnet101/pytorch/start_scripts/init_torch.sh new file mode 100644 index 000000000..1c2d7f9ec --- /dev/null +++ b/cv/classification/resnet101/pytorch/start_scripts/init_torch.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +pip3 install pycocotools \ No newline at end of file diff --git a/cv/classification/resnet101/pytorch/start_scripts/train_resnet50_amp_torch.sh b/cv/classification/resnet101/pytorch/start_scripts/train_resnet50_amp_torch.sh new file mode 100644 index 000000000..8bf37d18f --- /dev/null +++ b/cv/classification/resnet101/pytorch/start_scripts/train_resnet50_amp_torch.sh @@ -0,0 +1,7 @@ + +python3 ../train.py \ +--data-path /home/datasets/cv/imagenet-mini \ +--batch-size 256 \ +--lr 0.01 \ +--amp \ +"$@" diff --git a/cv/classification/resnet101/pytorch/start_scripts/train_resnet50_dist_torch.sh b/cv/classification/resnet101/pytorch/start_scripts/train_resnet50_dist_torch.sh new file mode 100644 index 000000000..f5488cbde --- /dev/null +++ b/cv/classification/resnet101/pytorch/start_scripts/train_resnet50_dist_torch.sh @@ -0,0 +1,9 @@ + +source get_num_devices.sh +python3 -m torch.distributed.launch --nproc_per_node=$IX_NUM_CUDA_VISIBLE_DEVICES --use_env \ +../train.py \ +--data-path /home/datasets/cv/imagenet-mini \ +--batch-size 256 \ +--lr 1e-2 \ +--wd 0.0001 \ +"$@" diff --git a/cv/classification/resnet101/pytorch/start_scripts/train_resnet50_torch.sh b/cv/classification/resnet101/pytorch/start_scripts/train_resnet50_torch.sh new file mode 100644 index 000000000..f48a27622 --- /dev/null +++ b/cv/classification/resnet101/pytorch/start_scripts/train_resnet50_torch.sh @@ -0,0 +1,5 @@ + +python3 ../train.py \ +--data-path /home/datasets/cv/imagenet-mini \ +--batch-size 256 \ +"$@" diff --git a/cv/classification/resnet101/pytorch/train.py b/cv/classification/resnet101/pytorch/train.py new file mode 100644 index 000000000..03feeb0a0 --- /dev/null +++ b/cv/classification/resnet101/pytorch/train.py @@ -0,0 +1,317 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +import datetime +import os +import sys + +import time +import math + +import torch +import torch.utils.data + +try: + from torch.cuda.amp import autocast, GradScaler + scaler = GradScaler() +except: + autocast = None + scaler = None + + +from torch import nn +import torch.distributed as dist +import torchvision + +from utils_ import (MetricLogger, SmoothedValue, accuracy, mkdir,\ + init_distributed_mode, manual_seed,\ + is_main_process, save_on_master, get_world_size) + +from dataloader.classification import get_datasets, create_dataloader + + +def compute_loss(model, image, target, criterion): + output = model(image) + if not isinstance(output, (tuple, list)): + output = [output] + losses = [] + for out in output: + losses.append(criterion(out, target)) + loss = sum(losses) + return loss, output[0] + + +def train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, print_freq, amp=False, use_dali=False): + model.train() + metric_logger = MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', SmoothedValue(window_size=1, fmt='{value}')) + metric_logger.add_meter('img/s', SmoothedValue(window_size=10, fmt='{value}')) + + header = 'Epoch: [{}]'.format(epoch) + all_fps = [] + for data in metric_logger.log_every(data_loader, print_freq, header): + if use_dali: + image, target = data[0]["data"], data[0]["label"][:, 0].long() + else: + image, target = data + start_time = time.time() + image, target = image.to(device), target.to(device) + if autocast is None or not amp: + loss, output = compute_loss(model, image, target, criterion) + else: + with autocast(): + loss, output = compute_loss(model, image, target, criterion) + + optimizer.zero_grad() + if scaler is not None and amp: + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + else: + loss.backward() + optimizer.step() + + torch.cuda.synchronize() + end_time = time.time() + + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + if not (math.isfinite(acc1) and math.isfinite(acc5)): + print("Accuracy exploded (corrupted by Inf or Nan)") + sys.exit(1) + batch_size = image.shape[0] + loss_value = loss.item() + if not math.isfinite(loss_value): + print("Loss is {}, stopping training".format(loss_value)) + sys.exit(1) + metric_logger.update(loss=loss.item(), lr=optimizer.param_groups[0]["lr"]) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + fps = batch_size / (end_time - start_time) * get_world_size() + metric_logger.meters['img/s'].update(fps) + all_fps.append(fps) + + print(header, 'Avg img/s:', sum(all_fps) / len(all_fps)) + + +def evaluate(model, criterion, data_loader, device, print_freq=100, use_dali=False): + model.eval() + metric_logger = MetricLogger(delimiter=" ") + header = 'Test:' + with torch.no_grad(): + for data in metric_logger.log_every(data_loader, print_freq, header): + if use_dali: + image, target = data[0]["data"], data[0]["label"][:, 0].long() + else: + image, target = data + image = image.to(device, non_blocking=True) + target = target.to(device, non_blocking=True) + output = model(image) + loss = criterion(output, target) + + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + # FIXME need to take into account that the datasets + # could have been padded in distributed setup + batch_size = image.shape[0] + metric_logger.update(loss=loss.item()) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + # gather the stats from all processes + metric_logger.synchronize_between_processes() + + print(' * Acc@1 {top1.global_avg:.3f} Acc@5 {top5.global_avg:.3f}' + .format(top1=metric_logger.acc1, top5=metric_logger.acc5)) + return metric_logger.acc1.global_avg + + +def _get_cache_path(filepath): + import hashlib + h = hashlib.sha1(filepath.encode()).hexdigest() + cache_path = os.path.join("~", ".torch", "vision", "datasets", "imagefolder", h[:10] + ".pt") + cache_path = os.path.expanduser(cache_path) + return cache_path + + +def main(args): + if args.output_dir: + mkdir(args.output_dir) + + init_distributed_mode(args) + print(args) + + device = torch.device(args.device) + + manual_seed(args.seed, deterministic=False) + # torch.backends.cudnn.benchmark = True + + # WARN: + if dist.is_initialized(): + num_gpu = dist.get_world_size() + else: + num_gpu = 1 + + global_batch_size = num_gpu * args.batch_size + + train_dir = os.path.join(args.data_path, 'train') + val_dir = os.path.join(args.data_path, 'val') + + num_classes = len(os.listdir(train_dir)) + if 0 < num_classes < 13: + if global_batch_size > 512: + if is_main_process(): + print("WARN: Updating global batch size to 512, avoid non-convergence when training small dataset.") + args.batch_size = 512 // num_gpu + + if args.pretrained: + num_classes = 1000 + + data_loader, data_loader_test = create_dataloader(train_dir, val_dir, args) + + print("Creating model") + model = torchvision.models.__dict__[args.model](pretrained=args.pretrained, num_classes=num_classes) + model.to(device) + if args.distributed and args.sync_bn: + model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model) + + criterion = nn.CrossEntropyLoss() + + opt_name = args.opt.lower() + if opt_name == 'sgd': + optimizer = torch.optim.SGD( + model.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.weight_decay) + elif opt_name == 'rmsprop': + optimizer = torch.optim.RMSprop(model.parameters(), lr=args.lr, momentum=args.momentum, + weight_decay=args.weight_decay, eps=0.0316, alpha=0.9) + else: + raise RuntimeError("Invalid optimizer {}. Only SGD and RMSprop are supported.".format(args.opt)) + + lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=args.lr_step_size, gamma=args.lr_gamma) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + if args.resume: + checkpoint = torch.load(args.resume, map_location='cpu') + model_without_ddp.load_state_dict(checkpoint['model']) + optimizer.load_state_dict(checkpoint['optimizer']) + lr_scheduler.load_state_dict(checkpoint['lr_scheduler']) + args.start_epoch = checkpoint['epoch'] + 1 + + if args.test_only: + evaluate(model, criterion, data_loader_test, device=device) + return + + print("Start training") + start_time = time.time() + best_acc = 0 + for epoch in range(args.start_epoch, args.epochs): + epoch_start_time = time.time() + if args.distributed and not args.dali: + data_loader.sampler.set_epoch(epoch) + train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, args.print_freq, args.amp, use_dali=args.dali) + lr_scheduler.step() + acc_avg = evaluate(model, criterion, data_loader_test, device=device, use_dali=args.dali) + if acc_avg > best_acc: + if args.output_dir: + checkpoint = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'lr_scheduler': lr_scheduler.state_dict(), + 'epoch': epoch, + 'args': args} + save_on_master( + checkpoint, + os.path.join(args.output_dir, 'model_best.pth')) + epoch_total_time = time.time() - epoch_start_time + epoch_total_time_str = str(datetime.timedelta(seconds=int(epoch_total_time))) + print('epoch time {}'.format(epoch_total_time_str)) + best_acc = acc_avg + + if args.dali: + data_loader.reset() + data_loader_test.reset() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +def get_args_parser(add_help=True): + import argparse + parser = argparse.ArgumentParser(description='PyTorch Classification Training', add_help=add_help) + + parser.add_argument('--data-path', default='/datasets01/imagenet_full_size/061417/', help='dataset') + parser.add_argument('--model', default='resnet50', help='model') + parser.add_argument('--device', default='cuda', help='device') + parser.add_argument('-b', '--batch-size', default=32, type=int) + parser.add_argument('--epochs', default=90, type=int, metavar='N', + help='number of total epochs to run') + parser.add_argument('-j', '--workers', default=4, type=int, metavar='N', + help='number of data loading workers (default: 4)') + parser.add_argument('--opt', default='sgd', type=str, help='optimizer') + parser.add_argument('--lr', default=0.1, type=float, help='initial learning rate') + parser.add_argument('--momentum', default=0.9, type=float, metavar='M', + help='momentum') + parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float, + metavar='W', help='weight decay (default: 1e-4)', + dest='weight_decay') + parser.add_argument('--lr-step-size', default=30, type=int, help='decrease lr every step-size epochs') + parser.add_argument('--lr-gamma', default=0.1, type=float, help='decrease lr by a factor of lr-gamma') + parser.add_argument('--print-freq', default=10, type=int, help='print frequency') + parser.add_argument('--output-dir', default='.', help='path where to save') + parser.add_argument('--resume', default='', help='resume from checkpoint') + parser.add_argument('--start-epoch', default=0, type=int, metavar='N', + help='start epoch') + parser.add_argument( + "--cache-dataset", + dest="cache_dataset", + help="Cache the datasets for quicker initialization. It also serializes the transforms", + action="store_true", + ) + parser.add_argument( + "--sync-bn", + dest="sync_bn", + help="Use sync batch norm", + action="store_true", + ) + parser.add_argument( + "--test-only", + dest="test_only", + help="Only test the model", + action="store_true", + ) + parser.add_argument( + "--pretrained", + dest="pretrained", + help="Use pre-trained models from the modelzoo", + action="store_true", + ) + parser.add_argument('--auto-augment', default=None, help='auto augment policy (default: None)') + parser.add_argument('--random-erase', default=0.0, type=float, help='random erasing probability (default: 0.0)') + parser.add_argument( + "--dali", + help="Use dali as dataloader", + default=False, + action="store_true", + ) + + # distributed training parameters + parser.add_argument('--local_rank', '--local-rank', default=-1, type=int, + help='Local rank') + parser.add_argument('--world-size', default=1, type=int, + help='number of distributed processes') + parser.add_argument('--dist-url', default='env://', help='url used to set up distributed training') + parser.add_argument('--amp', action='store_true', help='Automatic Mixed Precision training') + parser.add_argument('--seed', default=42, type=int, help='Random seed') + return parser + + +if __name__ == "__main__": + args = get_args_parser().parse_args() + try: + from dltest import show_training_arguments + show_training_arguments(args) + except: + pass + main(args) diff --git a/cv/classification/resnet101/pytorch/train_horovod.py b/cv/classification/resnet101/pytorch/train_horovod.py new file mode 100644 index 000000000..55db421ec --- /dev/null +++ b/cv/classification/resnet101/pytorch/train_horovod.py @@ -0,0 +1,304 @@ +import torch +import argparse +import torch.backends.cudnn as cudnn +import torch.multiprocessing as mp +import torch.nn.functional as F +import torch.optim as optim +import torch.utils.data.distributed +from torch.utils.tensorboard import SummaryWriter +from torchvision import datasets, transforms, models +import horovod.torch as hvd +import os +import sys +import math +from tqdm import tqdm + +# Training settings +parser = argparse.ArgumentParser(description='PyTorch ImageNet Example', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument('--train-dir', default=os.path.expanduser('~/imagenet/train'), + help='path to training data') +parser.add_argument('--val-dir', default=os.path.expanduser('~/imagenet/validation'), + help='path to validation data') +parser.add_argument('--output-dir', default='./work_dirs', + help='tensorboard log directory') +parser.add_argument('--device', default='cuda', help='device') +parser.add_argument('--checkpoint-format', default='checkpoint-{epoch}.pth.tar', + help='checkpoint file format') +parser.add_argument('--fp16-allreduce', action='store_true', default=False, + help='use fp16 compression during allreduce') +parser.add_argument('--batches-per-allreduce', type=int, default=1, + help='number of batches processed locally before ' + 'executing allreduce across workers; it multiplies ' + 'total batch size.') +parser.add_argument('--use-adasum', action='store_true', default=False, + help='use adasum algorithm to do reduction') +parser.add_argument('--gradient-predivide-factor', type=float, default=1.0, + help='apply gradient predivide factor in optimizer (default: 1.0)') +parser.add_argument('--acc-thresh', default=75.0, type=float, + help='accuracy threshold') + +# Default settings from https://arxiv.org/abs/1706.02677. +parser.add_argument('--batch-size', type=int, default=32, + help='input batch size for training') +parser.add_argument('--val-batch-size', type=int, default=32, + help='input batch size for validation') +parser.add_argument('--epochs', type=int, default=90, + help='number of epochs to train') +parser.add_argument('--base-lr', type=float, default=0.0125, + help='learning rate for a single GPU') +parser.add_argument('--warmup-epochs', type=float, default=5, + help='number of warmup epochs') +parser.add_argument('--momentum', type=float, default=0.9, + help='SGD momentum') +parser.add_argument('--wd', type=float, default=0.00005, + help='weight decay') +parser.add_argument('--seed', type=int, default=42, + help='random seed') + + +def train(epoch, device): + model.train() + train_sampler.set_epoch(epoch) + train_loss = Metric('train_loss') + train_accuracy = Metric('train_accuracy') + + with tqdm(total=len(train_loader), + desc='Train Epoch #{}'.format(epoch + 1), + disable=not verbose) as t: + for batch_idx, (data, target) in enumerate(train_loader): + adjust_learning_rate(epoch, batch_idx) + + data, target = data.to(device), target.to(device) + optimizer.zero_grad() + # Split data into sub-batches of size batch_size + for i in range(0, len(data), args.batch_size): + data_batch = data[i:i + args.batch_size] + target_batch = target[i:i + args.batch_size] + output = model(data_batch) + train_accuracy.update(accuracy(output, target_batch)) + loss = F.cross_entropy(output, target_batch) + if not math.isfinite(loss): + print("Loss is {}, stopping training".format(loss)) + sys.exit(1) + train_loss.update(loss) + # Average gradients among sub-batches + loss.div_(math.ceil(float(len(data)) / args.batch_size)) + loss.backward() + # Gradient is applied across all ranks + optimizer.step() + t.set_postfix({'loss': train_loss.avg.item(), + 'accuracy': 100. * train_accuracy.avg.item()}) + + t.update(1) + + if log_writer: + log_writer.add_scalar('train/loss', train_loss.avg, epoch) + log_writer.add_scalar('train/accuracy', train_accuracy.avg, epoch) + + +def validate(epoch, device): + model.eval() + val_loss = Metric('val_loss') + val_accuracy = Metric('val_accuracy') + + with tqdm(total=len(val_loader), + desc='Validate Epoch #{}'.format(epoch + 1), + disable=not verbose) as t: + with torch.no_grad(): + for data, target in val_loader: + data, target = data.to(device), target.to(device) + output = model(data) + + val_loss.update(F.cross_entropy(output, target)) + val_accuracy.update(accuracy(output, target)) + t.set_postfix({'loss': val_loss.avg.item(), + 'accuracy': 100. * val_accuracy.avg.item()}) + t.update(1) + + if 100.0*val_accuracy.avg.item() > args.acc_thresh: + print("The accuracy has been exceeded {},and the training is \ + terminated at epoch {}".format(args.acc_thresh, epoch)) + return + + if log_writer: + log_writer.add_scalar('val/loss', val_loss.avg, epoch) + log_writer.add_scalar('val/accuracy', val_accuracy.avg, epoch) + + +# Horovod: using `lr = base_lr * hvd.size()` from the very beginning leads to worse final +# accuracy. Scale the learning rate `lr = base_lr` ---> `lr = base_lr * hvd.size()` during +# the first five epochs. See https://arxiv.org/abs/1706.02677 for details. +# After the warmup reduce learning rate by 10 on the 30th, 60th and 80th epochs. +def adjust_learning_rate(epoch, batch_idx): + if epoch < args.warmup_epochs: + epoch += float(batch_idx + 1) / len(train_loader) + lr_adj = 1. / hvd.size() * (epoch * (hvd.size() - 1) / args.warmup_epochs + 1) + elif epoch < 30: + lr_adj = 1. + elif epoch < 60: + lr_adj = 1e-1 + elif epoch < 80: + lr_adj = 1e-2 + else: + lr_adj = 1e-3 + for param_group in optimizer.param_groups: + param_group['lr'] = args.base_lr * hvd.size() * args.batches_per_allreduce * lr_adj + + +def accuracy(output, target): + # get the index of the max log-probability + pred = output.max(1, keepdim=True)[1] + return pred.eq(target.view_as(pred)).cpu().float().mean() + + +def save_checkpoint(epoch): + if hvd.rank() == 0: + filepath = os.path.join(args.output_dir, args.checkpoint_format.format(epoch=epoch + 1)) + # filepath = args.checkpoint_format.format(epoch=epoch + 1) + state = { + 'model': model.state_dict(), + 'optimizer': optimizer.state_dict(), + } + torch.save(state, filepath) + + +# Horovod: average metrics from distributed training. +class Metric(object): + def __init__(self, name): + self.name = name + self.sum = torch.tensor(0.) + self.n = torch.tensor(0.) + + def update(self, val): + self.sum += hvd.allreduce(val.detach().cpu(), name=self.name) + self.n += 1 + + @property + def avg(self): + return self.sum / self.n + + +if __name__ == '__main__': + args = parser.parse_args() + device = torch.device(args.device) + + allreduce_batch_size = args.batch_size * args.batches_per_allreduce + + hvd.init() + torch.manual_seed(args.seed) + + # if args.cuda: + # Horovod: pin GPU to local rank. + torch.cuda.set_device(hvd.local_rank()) + torch.cuda.manual_seed(args.seed) + + cudnn.benchmark = True + + # If set > 0, will resume training from a given checkpoint. + resume_from_epoch = 0 + for try_epoch in range(args.epochs, 0, -1): + if os.path.exists(args.checkpoint_format.format(epoch=try_epoch)): + resume_from_epoch = try_epoch + break + + # Horovod: broadcast resume_from_epoch from rank 0 (which will have + # checkpoints) to other ranks. + resume_from_epoch = hvd.broadcast(torch.tensor(resume_from_epoch), root_rank=0, + name='resume_from_epoch').item() + + # Horovod: print logs on the first worker. + verbose = 1 if hvd.rank() == 0 else 0 + + # Horovod: write TensorBoard logs on first worker. + log_writer = SummaryWriter(args.output_dir) if hvd.rank() == 0 else None + + # Horovod: limit # of CPU threads to be used per worker. + torch.set_num_threads(2) + + #kwargs = {'num_workers': 4, 'pin_memory': True} if args.cuda else {} + kwargs = {'num_workers': 2, 'pin_memory': True} + + # When supported, use 'forkserver' to spawn dataloader workers instead of 'fork' to prevent + # issues with Infiniband implementations that are not fork-safe + if (kwargs.get('num_workers', 0) > 0 and hasattr(mp, '_supports_context') and + mp._supports_context and 'forkserver' in mp.get_all_start_methods()): + kwargs['multiprocessing_context'] = 'forkserver' + + train_dataset = \ + datasets.ImageFolder(args.train_dir, + transform=transforms.Compose([ + transforms.RandomResizedCrop(224), + transforms.RandomHorizontalFlip(), + transforms.ToTensor(), + transforms.Normalize(mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]) + ])) + + # Horovod: use DistributedSampler to partition data among workers. Manually specify + # `num_replicas=hvd.size()` and `rank=hvd.rank()`. + train_sampler = torch.utils.data.distributed.DistributedSampler( + train_dataset, num_replicas=hvd.size(), rank=hvd.rank()) + train_loader = torch.utils.data.DataLoader( + train_dataset, batch_size=allreduce_batch_size, + sampler=train_sampler, **kwargs) + + val_dataset = \ + datasets.ImageFolder(args.val_dir, + transform=transforms.Compose([ + transforms.Resize(256), + transforms.CenterCrop(224), + transforms.ToTensor(), + transforms.Normalize(mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]) + ])) + val_sampler = torch.utils.data.distributed.DistributedSampler( + val_dataset, num_replicas=hvd.size(), rank=hvd.rank()) + val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=args.val_batch_size, + sampler=val_sampler, **kwargs) + + + # Set up standard ResNet-50 model. + model = models.resnet50().to(device) + + # By default, Adasum doesn't need scaling up learning rate. + # For sum/average with gradient Accumulation: scale learning rate by batches_per_allreduce + lr_scaler = args.batches_per_allreduce * hvd.size() if not args.use_adasum else 1 + + # If using GPU Adasum allreduce, scale learning rate by local_size. + if args.use_adasum and hvd.nccl_built(): + lr_scaler = args.batches_per_allreduce * hvd.local_size() + + # Horovod: scale learning rate by the number of GPUs. + optimizer = optim.SGD(model.parameters(), + lr=(args.base_lr * + lr_scaler), + momentum=args.momentum, weight_decay=args.wd) + + # Horovod: (optional) compression algorithm. + compression = hvd.Compression.fp16 if args.fp16_allreduce else hvd.Compression.none + + # Horovod: wrap optimizer with DistributedOptimizer. + optimizer = hvd.DistributedOptimizer( + optimizer, named_parameters=model.named_parameters(), + compression=compression, + backward_passes_per_step=args.batches_per_allreduce, + op=hvd.Adasum if args.use_adasum else hvd.Average, + gradient_predivide_factor=args.gradient_predivide_factor) + + # Restore from a previous checkpoint, if initial_epoch is specified. + # Horovod: restore on the first worker which will broadcast weights to other workers. + if resume_from_epoch > 0 and hvd.rank() == 0: + filepath = args.checkpoint_format.format(epoch=resume_from_epoch) + checkpoint = torch.load(filepath) + model.load_state_dict(checkpoint['model']) + optimizer.load_state_dict(checkpoint['optimizer']) + + # Horovod: broadcast parameters & optimizer state. + hvd.broadcast_parameters(model.state_dict(), root_rank=0) + hvd.broadcast_optimizer_state(optimizer, root_rank=0) + + for epoch in range(resume_from_epoch, args.epochs): + train(epoch, device) + validate(epoch, device) + save_checkpoint(epoch) diff --git a/cv/classification/resnet101/pytorch/train_resnet101_amp_dist.sh b/cv/classification/resnet101/pytorch/train_resnet101_amp_dist.sh index 29f2233fc..b884956e2 100755 --- a/cv/classification/resnet101/pytorch/train_resnet101_amp_dist.sh +++ b/cv/classification/resnet101/pytorch/train_resnet101_amp_dist.sh @@ -30,7 +30,7 @@ if [ ! -z "${DEBUG}" ];then PYTHONAR="${PYTHONAR} -m pdb" fi cd ${ROOT_DIR} -python3 $PYTHONARG ${ROOT_DIR}/run_train.py \ - --model resnet101 --dali --dali-cpu --data-path $DATA_PATH \ - --opt fused_sgd --batch-size 300 \ - --amp --nhwc --padding-channel "$@" +python3 $PYTHONARG train.py \ + --model resnet101 --data-path $DATA_PATH \ + --lr 1e-2 --batch-size 300 \ + --amp --wd 0.0001 "$@" diff --git a/cv/classification/resnet101/pytorch/utils_.py b/cv/classification/resnet101/pytorch/utils_.py new file mode 100644 index 000000000..3d34c4df0 --- /dev/null +++ b/cv/classification/resnet101/pytorch/utils_.py @@ -0,0 +1,156 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque, OrderedDict +import copy +import datetime +import hashlib +import time +import torch +import torch.distributed as dist + +import errno +import os + +from common_utils import * + + +def accuracy(output, target, topk=(1,)): + """Computes the accuracy over the k top predictions for the specified values of k""" + with torch.no_grad(): + maxk = max(topk) + batch_size = target.size(0) + + _, pred = output.topk(maxk, 1, True, True) + pred = pred.t() + correct = pred.eq(target[None]) + + res = [] + for k in topk: + correct_k = correct[:k].flatten().sum(dtype=torch.float32) + res.append(correct_k * (100.0 / batch_size)) + return res + + +def average_checkpoints(inputs): + """Loads checkpoints from inputs and returns a model with averaged weights. Original implementation taken from: + https://github.com/pytorch/fairseq/blob/a48f235636557b8d3bc4922a6fa90f3a0fa57955/scripts/average_checkpoints.py#L16 + + Args: + inputs (List[str]): An iterable of string paths of checkpoints to load from. + Returns: + A dict of string keys mapping to various values. The 'model' key + from the returned dict should correspond to an OrderedDict mapping + string parameter names to torch Tensors. + """ + params_dict = OrderedDict() + params_keys = None + new_state = None + num_models = len(inputs) + for fpath in inputs: + with open(fpath, "rb") as f: + state = torch.load( + f, + map_location=( + lambda s, _: torch.serialization.default_restore_location(s, "cpu") + ), + ) + # Copies over the settings from the first checkpoint + if new_state is None: + new_state = state + model_params = state["model"] + model_params_keys = list(model_params.keys()) + if params_keys is None: + params_keys = model_params_keys + elif params_keys != model_params_keys: + raise KeyError( + "For checkpoint {}, expected list of params: {}, " + "but found: {}".format(f, params_keys, model_params_keys) + ) + for k in params_keys: + p = model_params[k] + if isinstance(p, torch.HalfTensor): + p = p.float() + if k not in params_dict: + params_dict[k] = p.clone() + # NOTE: clone() is needed in case of p is a shared parameter + else: + params_dict[k] += p + averaged_params = OrderedDict() + for k, v in params_dict.items(): + averaged_params[k] = v + if averaged_params[k].is_floating_point(): + averaged_params[k].div_(num_models) + else: + averaged_params[k] //= num_models + new_state["model"] = averaged_params + return new_state + + +def store_model_weights(model, checkpoint_path, checkpoint_key='model', strict=True): + """ + This method can be used to prepare weights files for new models. It receives as + input a model architecture and a checkpoint from the training script and produces + a file with the weights ready for release. + + Examples: + from torchvision import models as M + + # Classification + model = M.mobilenet_v3_large(pretrained=False) + print(store_model_weights(model, './class.pth')) + + # Quantized Classification + model = M.quantization.mobilenet_v3_large(pretrained=False, quantize=False) + model.fuse_model() + model.qconfig = torch.quantization.get_default_qat_qconfig('qnnpack') + _ = torch.quantization.prepare_qat(model, inplace=True) + print(store_model_weights(model, './qat.pth')) + + # Object Detection + model = M.detection.fasterrcnn_mobilenet_v3_large_fpn(pretrained=False, pretrained_backbone=False) + print(store_model_weights(model, './obj.pth')) + + # Segmentation + model = M.segmentation.deeplabv3_mobilenet_v3_large(pretrained=False, pretrained_backbone=False, aux_loss=True) + print(store_model_weights(model, './segm.pth', strict=False)) + + Args: + model (pytorch.nn.Module): The model on which the weights will be loaded for validation purposes. + checkpoint_path (str): The path of the checkpoint we will load. + checkpoint_key (str, optional): The key of the checkpoint where the model weights are stored. + Default: "model". + strict (bool): whether to strictly enforce that the keys + in :attr:`state_dict` match the keys returned by this module's + :meth:`~torch.nn.Module.state_dict` function. Default: ``True`` + + Returns: + output_path (str): The location where the weights are saved. + """ + # Store the new model next to the checkpoint_path + checkpoint_path = os.path.abspath(checkpoint_path) + output_dir = os.path.dirname(checkpoint_path) + + # Deep copy to avoid side-effects on the model object. + model = copy.deepcopy(model) + checkpoint = torch.load(checkpoint_path, map_location='cpu') + + # Load the weights to the model to validate that everything works + # and remove unnecessary weights (such as auxiliaries, etc) + model.load_state_dict(checkpoint[checkpoint_key], strict=strict) + + tmp_path = os.path.join(output_dir, str(model.__hash__())) + torch.save(model.state_dict(), tmp_path) + + sha256_hash = hashlib.sha256() + with open(tmp_path, "rb") as f: + # Read and update hash string value in blocks of 4K + for byte_block in iter(lambda: f.read(4096), b""): + sha256_hash.update(byte_block) + hh = sha256_hash.hexdigest() + + output_path = os.path.join(output_dir, "weights-" + str(hh[:8]) + ".pth") + os.replace(tmp_path, output_path) + + return output_path diff --git a/cv/classification/resnet50/pytorch/.gitignore b/cv/classification/resnet50/pytorch/.gitignore index d30305d02..61f2dc9f8 100644 --- a/cv/classification/resnet50/pytorch/.gitignore +++ b/cv/classification/resnet50/pytorch/.gitignore @@ -1,161 +1 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ -results +**/__pycache__/ diff --git a/cv/classification/resnet50/pytorch/__init__.py b/cv/classification/resnet50/pytorch/__init__.py index 12f6853cf..38ed0d0b2 100644 --- a/cv/classification/resnet50/pytorch/__init__.py +++ b/cv/classification/resnet50/pytorch/__init__.py @@ -1,10 +1,19 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright Declaration: This software, including all of its code and documentation, -# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX -# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright -# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar -# CoreX. No user of this software shall have any right, ownership or interest in this software and -# any use of this software shall be in compliance with the terms and conditions of the End User -# License Agreement. +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from .utils import * +from .common_utils import * +from .data_loader import * __all__ = [k for k in globals().keys() if not k.startswith("_")] diff --git a/cv/classification/resnet50/pytorch/common_utils/__init__.py b/cv/classification/resnet50/pytorch/common_utils/__init__.py index e87e61937..855a91a92 100644 --- a/cv/classification/resnet50/pytorch/common_utils/__init__.py +++ b/cv/classification/resnet50/pytorch/common_utils/__init__.py @@ -1,11 +1,17 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright Declaration: This software, including all of its code and documentation, -# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX -# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright -# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar -# CoreX. No user of this software shall have any right, ownership or interest in this software and -# any use of this software shall be in compliance with the terms and conditions of the End User -# License Agreement. +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import random import numpy as np @@ -14,8 +20,6 @@ from .dist import * from .metric_logger import * from .misc import * from .smooth_value import * -from .loss import LabelSmoothingCrossEntropy - def manual_seed(seed, deterministic=False): random.seed(seed) @@ -24,9 +28,10 @@ def manual_seed(seed, deterministic=False): torch.manual_seed(seed) torch.cuda.manual_seed(seed) torch.cuda.manual_seed_all(seed) + if deterministic: torch.backends.cudnn.deterministic = True torch.backends.cudnn.benchmark = False else: torch.backends.cudnn.deterministic = False - torch.backends.cudnn.benchmark = True + torch.backends.cudnn.benchmark = True \ No newline at end of file diff --git a/cv/classification/resnet50/pytorch/common_utils/dist.py b/cv/classification/resnet50/pytorch/common_utils/dist.py index 1aba7208b..ac4831fba 100644 --- a/cv/classification/resnet50/pytorch/common_utils/dist.py +++ b/cv/classification/resnet50/pytorch/common_utils/dist.py @@ -1,4 +1,5 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. @@ -6,7 +7,6 @@ from collections import defaultdict, deque import datetime import errno import os -import json import time import torch @@ -57,20 +57,10 @@ def save_on_master(*args, **kwargs): if is_main_process(): torch.save(*args, **kwargs) -def append_on_master(data, filename): - if is_main_process(): - with open(filename, "a") as f: - f.write(data) - -def write_on_master(data, filename): - if is_main_process(): - with open(filename, "w") as write_file: - json.dump(data, write_file, indent=4) def get_dist_backend(args=None): DIST_BACKEND_ENV = "PT_DIST_BACKEND" if DIST_BACKEND_ENV in os.environ: - print("WARN: Use the distributed backend of the environment.") return os.environ[DIST_BACKEND_ENV] if args is None: diff --git a/cv/classification/resnet50/pytorch/dataloader/classification.py b/cv/classification/resnet50/pytorch/dataloader/classification.py index 1015fb45a..030af6dee 100644 --- a/cv/classification/resnet50/pytorch/dataloader/classification.py +++ b/cv/classification/resnet50/pytorch/dataloader/classification.py @@ -6,6 +6,8 @@ # CoreX. No user of this software shall have any right, ownership or interest in this software and # any use of this software shall be in compliance with the terms and conditions of the End User # License Agreement. + + import os import time @@ -25,20 +27,19 @@ def get_datasets(traindir, resize_size=256, crop_size=224, auto_augment_policy=None, - random_erase_prob=0., - nhwc=False): + random_erase_prob=0.): # Data loading code print("Loading data") print("Loading training data") dataset = torchvision.datasets.ImageFolder( traindir, presets.ClassificationPresetTrain(crop_size=crop_size, auto_augment_policy=auto_augment_policy, - random_erase_prob=random_erase_prob, nhwc=nhwc)) + random_erase_prob=random_erase_prob)) print("Loading validation data") dataset_test = torchvision.datasets.ImageFolder( valdir, - presets.ClassificationPresetEval(crop_size=crop_size, resize_size=resize_size, nhwc=nhwc)) + presets.ClassificationPresetEval(crop_size=crop_size, resize_size=resize_size)) return dataset, dataset_test @@ -63,8 +64,7 @@ def load_data(train_dir, val_dir, args): auto_augment_policy=auto_augment_policy, random_erase_prob=random_erase_prob, resize_size=resize_size, - crop_size=crop_size, - nhwc=args.channels_last) + crop_size=crop_size) if args.distributed: train_sampler = torch.utils.data.distributed.DistributedSampler(dataset) test_sampler = torch.utils.data.distributed.DistributedSampler(dataset_test) @@ -95,15 +95,10 @@ def _create_dali_dataloader(train_dir, val_dir, args): data_loader = get_imagenet_iter_dali('train', train_dir, args.batch_size, num_threads=args.workers, device_id=device, - size=crop_size, - local_rank=args.local_rank, world_size=args.world_size, - nhwc=args.channels_last - ) - data_loader_test = get_imagenet_iter_dali('val', val_dir, args.batch_size, + size=crop_size) + data_loader_test = get_imagenet_iter_dali('val', train_dir, args.batch_size, num_threads=args.workers, device_id=device, - local_rank=args.local_rank, world_size=args.world_size, - nhwc=args.channels_last, size=crop_size) return data_loader, data_loader_test @@ -112,5 +107,7 @@ def _create_dali_dataloader(train_dir, val_dir, args): def create_dataloader(train_dir, val_dir, args): print("Creating data loaders") if args.dali: + train_dir = os.path.dirname(train_dir) + val_dir = os.path.dirname(val_dir) return _create_dali_dataloader(train_dir, val_dir, args) return _create_torch_dataloader(train_dir, val_dir, args) diff --git a/cv/classification/resnet50/pytorch/dataloader/dali_classification.py b/cv/classification/resnet50/pytorch/dataloader/dali_classification.py index a11bcdbfe..4c92283b2 100644 --- a/cv/classification/resnet50/pytorch/dataloader/dali_classification.py +++ b/cv/classification/resnet50/pytorch/dataloader/dali_classification.py @@ -6,122 +6,116 @@ # CoreX. No user of this software shall have any right, ownership or interest in this software and # any use of this software shall be in compliance with the terms and conditions of the End User # License Agreement. -# From: https://github.com/NVIDIA/DALI/blob/release_v1.6/docs/examples/use_cases/pytorch/resnet50/main.py -import argparse + + import os -import shutil -import time -import math - -import torch -import torch.nn as nn -import torch.nn.parallel -import torch.backends.cudnn as cudnn -import torch.distributed as dist -import torch.optim -import torch.utils.data -import torch.utils.data.distributed -import torchvision.transforms as transforms -import torchvision.datasets as datasets -import torchvision.models as models - -import numpy as np - -try: - from nvidia.dali.plugin.pytorch import DALIClassificationIterator, LastBatchPolicy - from nvidia.dali.pipeline import pipeline_def - import nvidia.dali.types as types - import nvidia.dali.fn as fn -except ImportError: - raise ImportError("Please install DALI from https://www.github.com/NVIDIA/DALI to run this example.") - -zeros = np.zeros((1, 224, 224), dtype=np.float) -@pipeline_def -def create_dali_pipeline(data_dir, crop, size, shard_id, num_shards, dali_cpu=False, is_training=True, nhwc=False): - images, labels = fn.readers.file(file_root=data_dir, - shard_id=shard_id, - num_shards=num_shards, - random_shuffle=is_training, - pad_last_batch=True, - name="Reader") - dali_device = 'cpu' if dali_cpu else 'gpu' - decoder_device = 'cpu' if dali_cpu else 'mixed' - # ask nvJPEG to preallocate memory for the biggest sample in ImageNet for CPU and GPU to avoid reallocations in runtime - device_memory_padding = 211025920 if decoder_device == 'mixed' else 0 - host_memory_padding = 140544512 if decoder_device == 'mixed' else 0 - # ask HW NVJPEG to allocate memory ahead for the biggest image in the data set to avoid reallocations in runtime - preallocate_width_hint = 5980 if decoder_device == 'mixed' else 0 - preallocate_height_hint = 6430 if decoder_device == 'mixed' else 0 - if is_training: - images = fn.decoders.image_random_crop(images, - device=decoder_device, output_type=types.RGB, - device_memory_padding=device_memory_padding, - host_memory_padding=host_memory_padding, - preallocate_width_hint=preallocate_width_hint, - preallocate_height_hint=preallocate_height_hint, - random_aspect_ratio=[0.8, 1.25], - random_area=[0.1, 1.0], - num_attempts=100) - images = fn.resize(images, - device=dali_device, - resize_x=crop, - resize_y=crop, - interp_type=types.INTERP_TRIANGULAR) - mirror = fn.random.coin_flip(probability=0.5) - else: - images = fn.decoders.image(images, - device=decoder_device, - output_type=types.RGB) - images = fn.resize(images, - device=dali_device, - size=size, - mode="not_smaller", - interp_type=types.INTERP_TRIANGULAR) - mirror = False - - images = fn.crop_mirror_normalize(images.gpu(), - dtype=types.FLOAT, - output_layout="CHW", - crop=(crop, crop), - mean=[0.485 * 255,0.456 * 255,0.406 * 255], - std=[0.229 * 255,0.224 * 255,0.225 * 255], - mirror=mirror) - if nhwc: - images = fn.cat(images, zeros, axis=0) - labels = labels.gpu() - return images, labels - - -def get_imagenet_iter_dali(type, image_dir, batch_size, num_threads, device_id, size, local_rank=-1, world_size=1, nhwc=False): - # This line fixes the bug of shard_id in fn.readers.file, if it's -1, bug happens - local_rank = 0 if local_rank == -1 else local_rank - if type == "train": - pipe = create_dali_pipeline(batch_size=batch_size, - num_threads=num_threads, - device_id=device_id, - data_dir=image_dir, - crop=size, - size=size, - dali_cpu=True, - shard_id=local_rank, - num_shards=world_size, - is_training=True, - nhwc=nhwc) - pipe.build() - return DALIClassificationIterator(pipe, reader_name="Reader", last_batch_policy=LastBatchPolicy.PARTIAL) - - elif type == "val": - pipe = create_dali_pipeline(batch_size=batch_size, - num_threads=num_threads, - device_id=device_id, - seed=12 + local_rank, - data_dir=image_dir, - crop=size, - size=size, - dali_cpu=True, - shard_id=local_rank, - num_shards=world_size, - is_training=False, - nhwc=nhwc) - pipe.build() - return DALIClassificationIterator(pipe, reader_name="Reader", last_batch_policy=LastBatchPolicy.PARTIAL) + +import nvidia.dali.ops as ops +import nvidia.dali.types as types +from nvidia.dali.pipeline import Pipeline +from nvidia.dali.plugin.pytorch import DALIClassificationIterator, DALIGenericIterator + +class HybridTrainPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridTrainPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=True) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.RandomResizedCrop(device="gpu", size=size, random_area=[0.08, 1.25]) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +class HybridValPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridValPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=False) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.Resize(device="gpu", resize_x=size, resize_y=size) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + crop=(size, size), + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +def get_imagenet_iter_dali(type, image_dir, batch_size, num_threads, device_id, size): + if type == 'train': + pip_train = HybridTrainPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "train"), + size=size) + pip_train.build() + dali_iter_train = DALIClassificationIterator(pip_train, size=pip_train.epoch_size("Reader")) + return dali_iter_train + elif type == 'val': + pip_val = HybridValPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "val"), + size=size) + pip_val.build() + dali_iter_val = DALIClassificationIterator(pip_val, size=pip_val.epoch_size("Reader")) + return dali_iter_val + + +def main(arguments): + parser = argparse.ArgumentParser() + parser.add_argument('--data_dir', help='directory to save data to', type=str, default='classification data') + args = parser.parse_args(arguments) + + train_loader = get_imagenet_iter_dali(type='train', image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + val_loader = get_imagenet_iter_dali(type="val", image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + print('start dali train dataloader.') + start = time.time() + for epoch in range(20): + for i, data in enumerate(train_loader): + images = data[0]["data"].cuda(non_blocking=True) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + + # WARN: Very important + train_loader.reset() + print("Epoch", epoch) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali train dataloader.') + + + print('start dali val dataloader.') + start = time.time() + for i, data in enumerate(val_loader): + images = data[0]["data"].cuda(non_blocking=True) + print(images.shape) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + print(labels.shape) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali val dataloader.') + + +if __name__ == '__main__': + import os, time, sys + import argparse + sys.exit(main(sys.argv[1:])) \ No newline at end of file diff --git a/cv/classification/resnet50/pytorch/dataloader/utils/presets_classification.py b/cv/classification/resnet50/pytorch/dataloader/utils/presets_classification.py index 40e687722..b2b518d02 100644 --- a/cv/classification/resnet50/pytorch/dataloader/utils/presets_classification.py +++ b/cv/classification/resnet50/pytorch/dataloader/utils/presets_classification.py @@ -1,18 +1,27 @@ +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright Declaration: This software, including all of its code and documentation, -# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX -# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright -# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar -# CoreX. No user of this software shall have any right, ownership or interest in this software and -# any use of this software shall be in compliance with the terms and conditions of the End User -# License Agreement. -import torch +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + from torchvision.transforms import autoaugment, transforms class ClassificationPresetTrain: def __init__(self, crop_size, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225), hflip_prob=0.5, - auto_augment_policy=None, random_erase_prob=0.0, nhwc=False): + auto_augment_policy=None, random_erase_prob=0.0): trans = [transforms.RandomResizedCrop(crop_size)] if hflip_prob > 0: trans.append(transforms.RandomHorizontalFlip(hflip_prob)) @@ -25,33 +34,22 @@ class ClassificationPresetTrain: ]) if random_erase_prob > 0: trans.append(transforms.RandomErasing(p=random_erase_prob)) - if nhwc: - trans.append(add_nhwc()) self.transforms = transforms.Compose(trans) def __call__(self, img): return self.transforms(img) -class add_nhwc: - def __init__(self): - pass - def __call__(self, img): - d1, d2, d3 = torch.split(img, 1, 0) - d4 = torch.zeros(d1.size()) - return torch.cat([d1, d2, d3, d4], dim=0) + class ClassificationPresetEval: - def __init__(self, crop_size, resize_size=256, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225), nhwc=False): + def __init__(self, crop_size, resize_size=256, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)): - trans = [ + self.transforms = transforms.Compose([ transforms.Resize(resize_size), transforms.CenterCrop(crop_size), transforms.ToTensor(), transforms.Normalize(mean=mean, std=std), - ] - if nhwc: - trans.append(add_nhwc()) - self.transforms = transforms.Compose(trans) + ]) def __call__(self, img): return self.transforms(img) diff --git a/cv/classification/resnet50/pytorch/scripts/fp32_16cards.sh b/cv/classification/resnet50/pytorch/scripts/fp32_16cards.sh index 240dfef10..8267c68de 100644 --- a/cv/classification/resnet50/pytorch/scripts/fp32_16cards.sh +++ b/cv/classification/resnet50/pytorch/scripts/fp32_16cards.sh @@ -9,4 +9,4 @@ SCRIPT_DIR=$(cd `dirname $0`; pwd) PROJECT_DIR=$SCRIPT_DIR/.. export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 -bash $SCRIPT_DIR/train_resnet50_imagenet_dist_2x8_torch.sh --batch-size 300 --dali $@ +bash $SCRIPT_DIR/train_resnet50_imagenet_dist_2x8_torch.sh --batch-size 300 $@ diff --git a/cv/classification/resnet50/pytorch/scripts/train_resnet50_imagenet.sh b/cv/classification/resnet50/pytorch/scripts/train_resnet50_imagenet.sh index b2637f713..f466a0ec1 100644 --- a/cv/classification/resnet50/pytorch/scripts/train_resnet50_imagenet.sh +++ b/cv/classification/resnet50/pytorch/scripts/train_resnet50_imagenet.sh @@ -24,7 +24,6 @@ cd $PROJECT_DIR python3 train.py \ --model resnet50 \ --epochs 90 \ - --acc-thresh 75.9 \ --output-dir ${OUTPUT_PATH} \ "$@";check_status diff --git a/cv/classification/resnet50/pytorch/scripts/train_resnet50_imagenet_dist_1x4_torch.sh b/cv/classification/resnet50/pytorch/scripts/train_resnet50_imagenet_dist_1x4_torch.sh index f9c0f59ea..9a2c4f9e6 100644 --- a/cv/classification/resnet50/pytorch/scripts/train_resnet50_imagenet_dist_1x4_torch.sh +++ b/cv/classification/resnet50/pytorch/scripts/train_resnet50_imagenet_dist_1x4_torch.sh @@ -27,7 +27,6 @@ python3 -m torch.distributed.launch\ train.py \ --model resnet50 \ --epochs 90 \ - --acc-thresh 75.9 \ --output-dir ${OUTPUT_PATH} \ "$@";check_status diff --git a/cv/classification/resnet50/pytorch/scripts/train_resnet50_imagenet_dist_2x8_torch.sh b/cv/classification/resnet50/pytorch/scripts/train_resnet50_imagenet_dist_2x8_torch.sh index 41d7c5ded..b16f745bd 100644 --- a/cv/classification/resnet50/pytorch/scripts/train_resnet50_imagenet_dist_2x8_torch.sh +++ b/cv/classification/resnet50/pytorch/scripts/train_resnet50_imagenet_dist_2x8_torch.sh @@ -29,7 +29,6 @@ python3 -m torch.distributed.launch --master_addr ${HOST_MASTER_ADDR} \ train.py \ --model resnet50 \ --epochs 90 \ - --acc-thresh 75.9 \ --output-dir ${OUTPUT_PATH} \ "$@";check_status diff --git a/cv/classification/resnet50/pytorch/start_scripts/get_num_devices.sh b/cv/classification/resnet50/pytorch/start_scripts/get_num_devices.sh new file mode 100644 index 000000000..f0701ce8d --- /dev/null +++ b/cv/classification/resnet50/pytorch/start_scripts/get_num_devices.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +devices=$CUDA_VISIBLE_DEVICES +if [ -n "$devices" ]; then + _devices=(${devices//,/ }) + num_devices=${#_devices[@]} +else + num_devices=2 + export CUDA_VISIBLE_DEVICES=0,1 + echo "Not found CUDA_VISIBLE_DEVICES, set nproc_per_node = ${num_devices}" +fi +export IX_NUM_CUDA_VISIBLE_DEVICES=${num_devices} diff --git a/cv/classification/resnet50/pytorch/start_scripts/init_torch.sh b/cv/classification/resnet50/pytorch/start_scripts/init_torch.sh new file mode 100644 index 000000000..1c2d7f9ec --- /dev/null +++ b/cv/classification/resnet50/pytorch/start_scripts/init_torch.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +pip3 install pycocotools \ No newline at end of file diff --git a/cv/classification/resnet50/pytorch/start_scripts/train_resnet50_amp_torch.sh b/cv/classification/resnet50/pytorch/start_scripts/train_resnet50_amp_torch.sh new file mode 100644 index 000000000..8bf37d18f --- /dev/null +++ b/cv/classification/resnet50/pytorch/start_scripts/train_resnet50_amp_torch.sh @@ -0,0 +1,7 @@ + +python3 ../train.py \ +--data-path /home/datasets/cv/imagenet-mini \ +--batch-size 256 \ +--lr 0.01 \ +--amp \ +"$@" diff --git a/cv/classification/resnet50/pytorch/start_scripts/train_resnet50_dist_torch.sh b/cv/classification/resnet50/pytorch/start_scripts/train_resnet50_dist_torch.sh new file mode 100644 index 000000000..f5488cbde --- /dev/null +++ b/cv/classification/resnet50/pytorch/start_scripts/train_resnet50_dist_torch.sh @@ -0,0 +1,9 @@ + +source get_num_devices.sh +python3 -m torch.distributed.launch --nproc_per_node=$IX_NUM_CUDA_VISIBLE_DEVICES --use_env \ +../train.py \ +--data-path /home/datasets/cv/imagenet-mini \ +--batch-size 256 \ +--lr 1e-2 \ +--wd 0.0001 \ +"$@" diff --git a/cv/classification/resnet50/pytorch/start_scripts/train_resnet50_torch.sh b/cv/classification/resnet50/pytorch/start_scripts/train_resnet50_torch.sh new file mode 100644 index 000000000..f48a27622 --- /dev/null +++ b/cv/classification/resnet50/pytorch/start_scripts/train_resnet50_torch.sh @@ -0,0 +1,5 @@ + +python3 ../train.py \ +--data-path /home/datasets/cv/imagenet-mini \ +--batch-size 256 \ +"$@" diff --git a/cv/classification/resnet50/pytorch/train.py b/cv/classification/resnet50/pytorch/train.py index c1fcc30f9..3e13694d0 100644 --- a/cv/classification/resnet50/pytorch/train.py +++ b/cv/classification/resnet50/pytorch/train.py @@ -1,12 +1,13 @@ +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. -# Copyright (c) 2022-2023 Iluvatar CoreX. All rights reserved. import datetime import os import sys -sys.path.append(os.path.join(os.path.dirname(__file__), '..')) import time +import math import torch import torch.utils.data @@ -21,14 +22,13 @@ except: from torch import nn import torch.distributed as dist -import _torchvision as torchvision -from torch.distributed.optim import ZeroRedundancyOptimizer +import torchvision -import utils +from utils_ import (MetricLogger, SmoothedValue, accuracy, mkdir,\ + init_distributed_mode, manual_seed,\ + is_main_process, save_on_master, get_world_size) from dataloader.classification import get_datasets, create_dataloader -from common_utils import LabelSmoothingCrossEntropy -import apex def compute_loss(model, image, target, criterion): @@ -44,40 +44,19 @@ def compute_loss(model, image, target, criterion): def train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, print_freq, amp=False, use_dali=False): model.train() - metric_logger = utils.MetricLogger(delimiter=" ") - metric_logger.add_meter('lr', utils.SmoothedValue(window_size=1, fmt='{value}')) - metric_logger.add_meter('img/s', utils.SmoothedValue(window_size=10, fmt='{value}')) + metric_logger = MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', SmoothedValue(window_size=1, fmt='{value}')) + metric_logger.add_meter('img/s', SmoothedValue(window_size=10, fmt='{value}')) header = 'Epoch: [{}]'.format(epoch) all_fps = [] - t_all_load = 0. - t_before_load = time.time() for data in metric_logger.log_every(data_loader, print_freq, header): if use_dali: image, target = data[0]["data"], data[0]["label"][:, 0].long() else: image, target = data start_time = time.time() - # NHWC or NCHW for data - if args.channels_last: - if not args.amp: - raise Exception("Channels last (NHWC) should be used only in AMP") - image, target = image.to(device, memory_format=torch.channels_last, dtype=torch.float16, non_blocking=True), target.to(device, non_blocking=True) - else: - image, target = image.to(device, non_blocking=True), target.to(device, non_blocking=True) - - # Benchmark data loading - t_after_load = time.time() - t_all_load += t_after_load - t_before_load - if args.test_loading: - t_before_load = time.time() - metric_logger.update(loss=0, lr=0) - metric_logger.meters['acc1'].update(0, n=args.batch_size) - metric_logger.meters['acc5'].update(0, n=args.batch_size) - metric_logger.meters['img/s'].update(0) - all_fps.append(0) - continue - + image, target = image.to(device), target.to(device) if autocast is None or not amp: loss, output = compute_loss(model, image, target, criterion) else: @@ -96,28 +75,28 @@ def train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, pri torch.cuda.synchronize() end_time = time.time() - acc1, acc5 = utils.accuracy(output, target, topk=(1, 5)) + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + if not (math.isfinite(acc1) and math.isfinite(acc5)): + print("Accuracy exploded (corrupted by Inf or Nan)") + sys.exit(1) batch_size = image.shape[0] + loss_value = loss.item() + if not math.isfinite(loss_value): + print("Loss is {}, stopping training".format(loss_value)) + sys.exit(1) metric_logger.update(loss=loss.item(), lr=optimizer.param_groups[0]["lr"]) metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) - fps = batch_size / (end_time - start_time) * utils.get_world_size() + fps = batch_size / (end_time - start_time) * get_world_size() metric_logger.meters['img/s'].update(fps) all_fps.append(fps) - t_before_load = time.time() - print('Data loading of epoch {}:'.format(epoch), datetime.timedelta(seconds=t_all_load)) - if args.test_loading: - return t_all_load print(header, 'Avg img/s:', sum(all_fps) / len(all_fps)) - return sum(all_fps) / len(all_fps) def evaluate(model, criterion, data_loader, device, print_freq=100, use_dali=False): - if args.test_loading: - return 0,0 model.eval() - metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger = MetricLogger(delimiter=" ") header = 'Test:' with torch.no_grad(): for data in metric_logger.log_every(data_loader, print_freq, header): @@ -125,23 +104,12 @@ def evaluate(model, criterion, data_loader, device, print_freq=100, use_dali=Fal image, target = data[0]["data"], data[0]["label"][:, 0].long() else: image, target = data - if args.channels_last: - if not args.amp: - raise Exception("Channels last (NHWC) should be used only in AMP") - image, target = image.to(device, memory_format=torch.channels_last, dtype=torch.float16, non_blocking=True), target.to(device, non_blocking=True) - else: - image, target = image.to(device, non_blocking=True), target.to(device, non_blocking=True) image = image.to(device, non_blocking=True) target = target.to(device, non_blocking=True) - - if autocast is None or not args.amp: - output = model(image) - else: - with autocast(): - output = model(image) + output = model(image) loss = criterion(output, target) - acc1, acc5 = utils.accuracy(output, target, topk=(1, 5)) + acc1, acc5 = accuracy(output, target, topk=(1, 5)) # FIXME need to take into account that the datasets # could have been padded in distributed setup batch_size = image.shape[0] @@ -153,44 +121,8 @@ def evaluate(model, criterion, data_loader, device, print_freq=100, use_dali=Fal print(' * Acc@1 {top1.global_avg:.3f} Acc@5 {top5.global_avg:.3f}' .format(top1=metric_logger.acc1, top5=metric_logger.acc5)) - return metric_logger.acc1.global_avg, metric_logger.acc5.global_avg + return metric_logger.acc1.global_avg -def _get_optimizer(args, model): - opt_name = args.opt.lower() - if opt_name == 'sgd': - if args.use_zero: - # model has to be ddp - print("use zero optimizer sgd") - optimizer = ZeroRedundancyOptimizer( - model.parameters(), - optimizer_class=torch.optim.SGD, - lr=args.lr, momentum=args.momentum, - weight_decay=args.weight_decay - ) - - else: - optimizer = torch.optim.SGD( - model.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.weight_decay) - elif opt_name == 'rmsprop': - if args.use_zero: - # model has to be ddp - print("use zero optimizer rmsprop") - optimizer = ZeroRedundancyOptimizer( - model.parameters(), - optimizer_class=torch.optim.RMSprop, - lr=args.lr, momentum=args.momentum, - weight_decay=args.weight_decay, eps=0.0316, alpha=0.9 - ) - else: - optimizer = torch.optim.RMSprop(model.parameters(), lr=args.lr, momentum=args.momentum, - weight_decay=args.weight_decay, eps=0.0316, alpha=0.9) - elif opt_name == 'fusedsgd': - optimizer = apex.optimizers.FusedSGD(model.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.weight_decay) - elif opt_name == 'fusedadam': - optimizer = apex.optimizers.FusedAdam(model.parameters(), lr=args.lr, weight_decay=args.weight_decay) - else: - raise RuntimeError("Invalid optimizer {}. Only SGD and RMSprop are supported.".format(args.opt)) - return optimizer def _get_cache_path(filepath): import hashlib @@ -201,17 +133,15 @@ def _get_cache_path(filepath): def main(args): + if args.output_dir: + mkdir(args.output_dir) - utils.init_distributed_mode(args) + init_distributed_mode(args) print(args) - if args.output_dir: - name = '_'.join(list(map(str, [args.model, args.batch_size, args.lr, args.dali, args.amp, args.opt]))) - args.output_dir = os.path.join(args.output_dir, name) - utils.mkdir(args.output_dir) device = torch.device(args.device) - utils.manual_seed(args.seed, deterministic=False) + manual_seed(args.seed, deterministic=False) # torch.backends.cudnn.benchmark = True # WARN: @@ -228,35 +158,40 @@ def main(args): num_classes = len(os.listdir(train_dir)) if 0 < num_classes < 13: if global_batch_size > 512: - if utils.is_main_process(): + if is_main_process(): print("WARN: Updating global batch size to 512, avoid non-convergence when training small dataset.") args.batch_size = 512 // num_gpu + if args.pretrained: + num_classes = 1000 + data_loader, data_loader_test = create_dataloader(train_dir, val_dir, args) - - print(f"Creating model {args.model}") - model = torchvision.models.__dict__[args.model](pretrained=args.pretrained) - if args.channels_last: - model.conv1 = nn.Conv2d(4,64,kernel_size=7, stride=2, padding=3, bias=False) - model = model.to(memory_format=torch.channels_last, device=device) - else: - model.to(device) + + print("Creating model") + model = torchvision.models.__dict__[args.model](pretrained=args.pretrained, num_classes=num_classes) + model.to(device) if args.distributed and args.sync_bn: model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model) - # criterion = nn.CrossEntropyLoss() - criterion = LabelSmoothingCrossEntropy() + criterion = nn.CrossEntropyLoss() + + opt_name = args.opt.lower() + if opt_name == 'sgd': + optimizer = torch.optim.SGD( + model.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.weight_decay) + elif opt_name == 'rmsprop': + optimizer = torch.optim.RMSprop(model.parameters(), lr=args.lr, momentum=args.momentum, + weight_decay=args.weight_decay, eps=0.0316, alpha=0.9) + else: + raise RuntimeError("Invalid optimizer {}. Only SGD and RMSprop are supported.".format(args.opt)) + lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=args.lr_step_size, gamma=args.lr_gamma) model_without_ddp = model if args.distributed: model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) model_without_ddp = model.module - optimizer = _get_optimizer(args, model) - # lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=args.lr_step_size, gamma=args.lr_gamma) - lr_scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[30, 60, 80 ,90], gamma=args.lr_gamma) - if args.resume: checkpoint = torch.load(args.resume, map_location='cpu') model_without_ddp.load_state_dict(checkpoint['model']) @@ -270,54 +205,29 @@ def main(args): print("Start training") start_time = time.time() - max_avg = 0 - - # Test for data loading - t_avg = 0 - if args.test_loading: - args.print_freq=100000 - args.epochs=1 + best_acc = 0 for epoch in range(args.start_epoch, args.epochs): epoch_start_time = time.time() if args.distributed and not args.dali: data_loader.sampler.set_epoch(epoch) - fps = train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, args.print_freq, args.amp, use_dali=args.dali) - if args.test_loading: - t_avg += fps + train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, args.print_freq, args.amp, use_dali=args.dali) lr_scheduler.step() - - acc_avg, acc_avg5 = evaluate(model, criterion, data_loader_test, device=device, use_dali=args.dali) - args.acc1 = round(acc_avg, 4) - args.acc5 = round(acc_avg5, 4) - args.fps = round(fps, 2) - - if args.output_dir and (epoch % 10 == 0 or acc_avg > max_avg): - checkpoint = { - 'model': model_without_ddp.state_dict(), - 'optimizer': optimizer.state_dict(), - 'lr_scheduler': lr_scheduler.state_dict(), - 'epoch': epoch, - 'args': args} - utils.save_on_master( - checkpoint, - os.path.join(args.output_dir, 'model_{}.pth'.format(epoch))) - utils.save_on_master( - checkpoint, - os.path.join(args.output_dir, 'checkpoint.pth')) - if acc_avg > max_avg: - max_avg = acc_avg - utils.save_on_master( + acc_avg = evaluate(model, criterion, data_loader_test, device=device, use_dali=args.dali) + if acc_avg > best_acc: + if args.output_dir: + checkpoint = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'lr_scheduler': lr_scheduler.state_dict(), + 'epoch': epoch, + 'args': args} + save_on_master( checkpoint, - os.path.join(args.output_dir, 'best_model.pth')) - utils.write_on_master(args.__dict__, os.path.join(args.output_dir, 'best.log')) - - epoch_total_time = time.time() - epoch_start_time - epoch_total_time_str = str(datetime.timedelta(seconds=int(epoch_total_time))) - print('Epoch time {}'.format(epoch_total_time_str)) - - if acc_avg > args.acc_thresh: - print("The accuracy has been exceeded {},and the training is terminated at epoch {}".format(args.acc_thresh, epoch)) - break + os.path.join(args.output_dir, 'model_best.pth')) + epoch_total_time = time.time() - epoch_start_time + epoch_total_time_str = str(datetime.timedelta(seconds=int(epoch_total_time))) + print('epoch time {}'.format(epoch_total_time_str)) + best_acc = acc_avg if args.dali: data_loader.reset() @@ -326,15 +236,6 @@ def main(args): total_time = time.time() - start_time total_time_str = str(datetime.timedelta(seconds=int(total_time))) print('Training time {}'.format(total_time_str)) - - if args.test_loading: - has_dali = 'w' if args.dali else 'w/o' - mode = 'AMP' if args.amp else 'FP32' - msg = "DATA-LOAD: {}, {} cards, {} DALI:{}\n".format(mode, args.world_size, has_dali, datetime.timedelta(seconds=t_avg)) - print(msg) - # post log saving - args.training_time = total_time_str - utils.write_on_master(args.__dict__, os.path.join(args.output_dir, 'best.log')) def get_args_parser(add_help=True): @@ -347,25 +248,15 @@ def get_args_parser(add_help=True): parser.add_argument('-b', '--batch-size', default=32, type=int) parser.add_argument('--epochs', default=90, type=int, metavar='N', help='number of total epochs to run') - parser.add_argument('-j', '--workers', default=16, type=int, metavar='N', - help='number of data loading workers (default: 16)') + parser.add_argument('-j', '--workers', default=4, type=int, metavar='N', + help='number of data loading workers (default: 4)') parser.add_argument('--opt', default='sgd', type=str, help='optimizer') - parser.add_argument('--lr', - # default=0.1, - default=0.128, - type=float, - help='initial learning rate') + parser.add_argument('--lr', default=0.1, type=float, help='initial learning rate') parser.add_argument('--momentum', default=0.9, type=float, metavar='M', help='momentum') - parser.add_argument('--wd', '--weight-decay', - # default=1e-4, - default=2e-4, - type=float, + parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float, metavar='W', help='weight decay (default: 1e-4)', dest='weight_decay') - parser.add_argument('--acc-thresh', - default=75.0, type=float, - help='accuracy threshold') parser.add_argument('--lr-step-size', default=30, type=int, help='decrease lr every step-size epochs') parser.add_argument('--lr-gamma', default=0.1, type=float, help='decrease lr by a factor of lr-gamma') parser.add_argument('--print-freq', default=10, type=int, help='print frequency') @@ -373,10 +264,6 @@ def get_args_parser(add_help=True): parser.add_argument('--resume', default='', help='resume from checkpoint') parser.add_argument('--start-epoch', default=0, type=int, metavar='N', help='start epoch') - parser.add_argument( - "--channels-last", - action="store_true", - ) parser.add_argument( "--cache-dataset", dest="cache_dataset", @@ -411,32 +298,21 @@ def get_args_parser(add_help=True): ) # distributed training parameters - parser.add_argument('--local_rank', default=-1, type=int, + parser.add_argument('--local_rank', '--local-rank', default=-1, type=int, help='Local rank') parser.add_argument('--world-size', default=1, type=int, help='number of distributed processes') parser.add_argument('--dist-url', default='env://', help='url used to set up distributed training') parser.add_argument('--amp', action='store_true', help='Automatic Mixed Precision training') parser.add_argument('--seed', default=42, type=int, help='Random seed') - parser.add_argument( - "--use-zero", - dest="use_zero", - help="Use ZeroRedundancyOptimizer when distributed traning", - action="store_true", - ) - parser.add_argument( - "--test-loading", - action="store_true", - ) return parser -def get_master_addr(): - if "MASTER_ADDR" in os.environ: - return os.environ["MASTER_ADDR"] - return "127.0.0.1" - - if __name__ == "__main__": args = get_args_parser().parse_args() + try: + from dltest import show_training_arguments + show_training_arguments(args) + except: + pass main(args) diff --git a/cv/classification/resnet50/pytorch/train_horovod.py b/cv/classification/resnet50/pytorch/train_horovod.py new file mode 100644 index 000000000..55db421ec --- /dev/null +++ b/cv/classification/resnet50/pytorch/train_horovod.py @@ -0,0 +1,304 @@ +import torch +import argparse +import torch.backends.cudnn as cudnn +import torch.multiprocessing as mp +import torch.nn.functional as F +import torch.optim as optim +import torch.utils.data.distributed +from torch.utils.tensorboard import SummaryWriter +from torchvision import datasets, transforms, models +import horovod.torch as hvd +import os +import sys +import math +from tqdm import tqdm + +# Training settings +parser = argparse.ArgumentParser(description='PyTorch ImageNet Example', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) +parser.add_argument('--train-dir', default=os.path.expanduser('~/imagenet/train'), + help='path to training data') +parser.add_argument('--val-dir', default=os.path.expanduser('~/imagenet/validation'), + help='path to validation data') +parser.add_argument('--output-dir', default='./work_dirs', + help='tensorboard log directory') +parser.add_argument('--device', default='cuda', help='device') +parser.add_argument('--checkpoint-format', default='checkpoint-{epoch}.pth.tar', + help='checkpoint file format') +parser.add_argument('--fp16-allreduce', action='store_true', default=False, + help='use fp16 compression during allreduce') +parser.add_argument('--batches-per-allreduce', type=int, default=1, + help='number of batches processed locally before ' + 'executing allreduce across workers; it multiplies ' + 'total batch size.') +parser.add_argument('--use-adasum', action='store_true', default=False, + help='use adasum algorithm to do reduction') +parser.add_argument('--gradient-predivide-factor', type=float, default=1.0, + help='apply gradient predivide factor in optimizer (default: 1.0)') +parser.add_argument('--acc-thresh', default=75.0, type=float, + help='accuracy threshold') + +# Default settings from https://arxiv.org/abs/1706.02677. +parser.add_argument('--batch-size', type=int, default=32, + help='input batch size for training') +parser.add_argument('--val-batch-size', type=int, default=32, + help='input batch size for validation') +parser.add_argument('--epochs', type=int, default=90, + help='number of epochs to train') +parser.add_argument('--base-lr', type=float, default=0.0125, + help='learning rate for a single GPU') +parser.add_argument('--warmup-epochs', type=float, default=5, + help='number of warmup epochs') +parser.add_argument('--momentum', type=float, default=0.9, + help='SGD momentum') +parser.add_argument('--wd', type=float, default=0.00005, + help='weight decay') +parser.add_argument('--seed', type=int, default=42, + help='random seed') + + +def train(epoch, device): + model.train() + train_sampler.set_epoch(epoch) + train_loss = Metric('train_loss') + train_accuracy = Metric('train_accuracy') + + with tqdm(total=len(train_loader), + desc='Train Epoch #{}'.format(epoch + 1), + disable=not verbose) as t: + for batch_idx, (data, target) in enumerate(train_loader): + adjust_learning_rate(epoch, batch_idx) + + data, target = data.to(device), target.to(device) + optimizer.zero_grad() + # Split data into sub-batches of size batch_size + for i in range(0, len(data), args.batch_size): + data_batch = data[i:i + args.batch_size] + target_batch = target[i:i + args.batch_size] + output = model(data_batch) + train_accuracy.update(accuracy(output, target_batch)) + loss = F.cross_entropy(output, target_batch) + if not math.isfinite(loss): + print("Loss is {}, stopping training".format(loss)) + sys.exit(1) + train_loss.update(loss) + # Average gradients among sub-batches + loss.div_(math.ceil(float(len(data)) / args.batch_size)) + loss.backward() + # Gradient is applied across all ranks + optimizer.step() + t.set_postfix({'loss': train_loss.avg.item(), + 'accuracy': 100. * train_accuracy.avg.item()}) + + t.update(1) + + if log_writer: + log_writer.add_scalar('train/loss', train_loss.avg, epoch) + log_writer.add_scalar('train/accuracy', train_accuracy.avg, epoch) + + +def validate(epoch, device): + model.eval() + val_loss = Metric('val_loss') + val_accuracy = Metric('val_accuracy') + + with tqdm(total=len(val_loader), + desc='Validate Epoch #{}'.format(epoch + 1), + disable=not verbose) as t: + with torch.no_grad(): + for data, target in val_loader: + data, target = data.to(device), target.to(device) + output = model(data) + + val_loss.update(F.cross_entropy(output, target)) + val_accuracy.update(accuracy(output, target)) + t.set_postfix({'loss': val_loss.avg.item(), + 'accuracy': 100. * val_accuracy.avg.item()}) + t.update(1) + + if 100.0*val_accuracy.avg.item() > args.acc_thresh: + print("The accuracy has been exceeded {},and the training is \ + terminated at epoch {}".format(args.acc_thresh, epoch)) + return + + if log_writer: + log_writer.add_scalar('val/loss', val_loss.avg, epoch) + log_writer.add_scalar('val/accuracy', val_accuracy.avg, epoch) + + +# Horovod: using `lr = base_lr * hvd.size()` from the very beginning leads to worse final +# accuracy. Scale the learning rate `lr = base_lr` ---> `lr = base_lr * hvd.size()` during +# the first five epochs. See https://arxiv.org/abs/1706.02677 for details. +# After the warmup reduce learning rate by 10 on the 30th, 60th and 80th epochs. +def adjust_learning_rate(epoch, batch_idx): + if epoch < args.warmup_epochs: + epoch += float(batch_idx + 1) / len(train_loader) + lr_adj = 1. / hvd.size() * (epoch * (hvd.size() - 1) / args.warmup_epochs + 1) + elif epoch < 30: + lr_adj = 1. + elif epoch < 60: + lr_adj = 1e-1 + elif epoch < 80: + lr_adj = 1e-2 + else: + lr_adj = 1e-3 + for param_group in optimizer.param_groups: + param_group['lr'] = args.base_lr * hvd.size() * args.batches_per_allreduce * lr_adj + + +def accuracy(output, target): + # get the index of the max log-probability + pred = output.max(1, keepdim=True)[1] + return pred.eq(target.view_as(pred)).cpu().float().mean() + + +def save_checkpoint(epoch): + if hvd.rank() == 0: + filepath = os.path.join(args.output_dir, args.checkpoint_format.format(epoch=epoch + 1)) + # filepath = args.checkpoint_format.format(epoch=epoch + 1) + state = { + 'model': model.state_dict(), + 'optimizer': optimizer.state_dict(), + } + torch.save(state, filepath) + + +# Horovod: average metrics from distributed training. +class Metric(object): + def __init__(self, name): + self.name = name + self.sum = torch.tensor(0.) + self.n = torch.tensor(0.) + + def update(self, val): + self.sum += hvd.allreduce(val.detach().cpu(), name=self.name) + self.n += 1 + + @property + def avg(self): + return self.sum / self.n + + +if __name__ == '__main__': + args = parser.parse_args() + device = torch.device(args.device) + + allreduce_batch_size = args.batch_size * args.batches_per_allreduce + + hvd.init() + torch.manual_seed(args.seed) + + # if args.cuda: + # Horovod: pin GPU to local rank. + torch.cuda.set_device(hvd.local_rank()) + torch.cuda.manual_seed(args.seed) + + cudnn.benchmark = True + + # If set > 0, will resume training from a given checkpoint. + resume_from_epoch = 0 + for try_epoch in range(args.epochs, 0, -1): + if os.path.exists(args.checkpoint_format.format(epoch=try_epoch)): + resume_from_epoch = try_epoch + break + + # Horovod: broadcast resume_from_epoch from rank 0 (which will have + # checkpoints) to other ranks. + resume_from_epoch = hvd.broadcast(torch.tensor(resume_from_epoch), root_rank=0, + name='resume_from_epoch').item() + + # Horovod: print logs on the first worker. + verbose = 1 if hvd.rank() == 0 else 0 + + # Horovod: write TensorBoard logs on first worker. + log_writer = SummaryWriter(args.output_dir) if hvd.rank() == 0 else None + + # Horovod: limit # of CPU threads to be used per worker. + torch.set_num_threads(2) + + #kwargs = {'num_workers': 4, 'pin_memory': True} if args.cuda else {} + kwargs = {'num_workers': 2, 'pin_memory': True} + + # When supported, use 'forkserver' to spawn dataloader workers instead of 'fork' to prevent + # issues with Infiniband implementations that are not fork-safe + if (kwargs.get('num_workers', 0) > 0 and hasattr(mp, '_supports_context') and + mp._supports_context and 'forkserver' in mp.get_all_start_methods()): + kwargs['multiprocessing_context'] = 'forkserver' + + train_dataset = \ + datasets.ImageFolder(args.train_dir, + transform=transforms.Compose([ + transforms.RandomResizedCrop(224), + transforms.RandomHorizontalFlip(), + transforms.ToTensor(), + transforms.Normalize(mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]) + ])) + + # Horovod: use DistributedSampler to partition data among workers. Manually specify + # `num_replicas=hvd.size()` and `rank=hvd.rank()`. + train_sampler = torch.utils.data.distributed.DistributedSampler( + train_dataset, num_replicas=hvd.size(), rank=hvd.rank()) + train_loader = torch.utils.data.DataLoader( + train_dataset, batch_size=allreduce_batch_size, + sampler=train_sampler, **kwargs) + + val_dataset = \ + datasets.ImageFolder(args.val_dir, + transform=transforms.Compose([ + transforms.Resize(256), + transforms.CenterCrop(224), + transforms.ToTensor(), + transforms.Normalize(mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225]) + ])) + val_sampler = torch.utils.data.distributed.DistributedSampler( + val_dataset, num_replicas=hvd.size(), rank=hvd.rank()) + val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=args.val_batch_size, + sampler=val_sampler, **kwargs) + + + # Set up standard ResNet-50 model. + model = models.resnet50().to(device) + + # By default, Adasum doesn't need scaling up learning rate. + # For sum/average with gradient Accumulation: scale learning rate by batches_per_allreduce + lr_scaler = args.batches_per_allreduce * hvd.size() if not args.use_adasum else 1 + + # If using GPU Adasum allreduce, scale learning rate by local_size. + if args.use_adasum and hvd.nccl_built(): + lr_scaler = args.batches_per_allreduce * hvd.local_size() + + # Horovod: scale learning rate by the number of GPUs. + optimizer = optim.SGD(model.parameters(), + lr=(args.base_lr * + lr_scaler), + momentum=args.momentum, weight_decay=args.wd) + + # Horovod: (optional) compression algorithm. + compression = hvd.Compression.fp16 if args.fp16_allreduce else hvd.Compression.none + + # Horovod: wrap optimizer with DistributedOptimizer. + optimizer = hvd.DistributedOptimizer( + optimizer, named_parameters=model.named_parameters(), + compression=compression, + backward_passes_per_step=args.batches_per_allreduce, + op=hvd.Adasum if args.use_adasum else hvd.Average, + gradient_predivide_factor=args.gradient_predivide_factor) + + # Restore from a previous checkpoint, if initial_epoch is specified. + # Horovod: restore on the first worker which will broadcast weights to other workers. + if resume_from_epoch > 0 and hvd.rank() == 0: + filepath = args.checkpoint_format.format(epoch=resume_from_epoch) + checkpoint = torch.load(filepath) + model.load_state_dict(checkpoint['model']) + optimizer.load_state_dict(checkpoint['optimizer']) + + # Horovod: broadcast parameters & optimizer state. + hvd.broadcast_parameters(model.state_dict(), root_rank=0) + hvd.broadcast_optimizer_state(optimizer, root_rank=0) + + for epoch in range(resume_from_epoch, args.epochs): + train(epoch, device) + validate(epoch, device) + save_checkpoint(epoch) diff --git a/cv/classification/resnet50/pytorch/utils_.py b/cv/classification/resnet50/pytorch/utils_.py new file mode 100644 index 000000000..3d34c4df0 --- /dev/null +++ b/cv/classification/resnet50/pytorch/utils_.py @@ -0,0 +1,156 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque, OrderedDict +import copy +import datetime +import hashlib +import time +import torch +import torch.distributed as dist + +import errno +import os + +from common_utils import * + + +def accuracy(output, target, topk=(1,)): + """Computes the accuracy over the k top predictions for the specified values of k""" + with torch.no_grad(): + maxk = max(topk) + batch_size = target.size(0) + + _, pred = output.topk(maxk, 1, True, True) + pred = pred.t() + correct = pred.eq(target[None]) + + res = [] + for k in topk: + correct_k = correct[:k].flatten().sum(dtype=torch.float32) + res.append(correct_k * (100.0 / batch_size)) + return res + + +def average_checkpoints(inputs): + """Loads checkpoints from inputs and returns a model with averaged weights. Original implementation taken from: + https://github.com/pytorch/fairseq/blob/a48f235636557b8d3bc4922a6fa90f3a0fa57955/scripts/average_checkpoints.py#L16 + + Args: + inputs (List[str]): An iterable of string paths of checkpoints to load from. + Returns: + A dict of string keys mapping to various values. The 'model' key + from the returned dict should correspond to an OrderedDict mapping + string parameter names to torch Tensors. + """ + params_dict = OrderedDict() + params_keys = None + new_state = None + num_models = len(inputs) + for fpath in inputs: + with open(fpath, "rb") as f: + state = torch.load( + f, + map_location=( + lambda s, _: torch.serialization.default_restore_location(s, "cpu") + ), + ) + # Copies over the settings from the first checkpoint + if new_state is None: + new_state = state + model_params = state["model"] + model_params_keys = list(model_params.keys()) + if params_keys is None: + params_keys = model_params_keys + elif params_keys != model_params_keys: + raise KeyError( + "For checkpoint {}, expected list of params: {}, " + "but found: {}".format(f, params_keys, model_params_keys) + ) + for k in params_keys: + p = model_params[k] + if isinstance(p, torch.HalfTensor): + p = p.float() + if k not in params_dict: + params_dict[k] = p.clone() + # NOTE: clone() is needed in case of p is a shared parameter + else: + params_dict[k] += p + averaged_params = OrderedDict() + for k, v in params_dict.items(): + averaged_params[k] = v + if averaged_params[k].is_floating_point(): + averaged_params[k].div_(num_models) + else: + averaged_params[k] //= num_models + new_state["model"] = averaged_params + return new_state + + +def store_model_weights(model, checkpoint_path, checkpoint_key='model', strict=True): + """ + This method can be used to prepare weights files for new models. It receives as + input a model architecture and a checkpoint from the training script and produces + a file with the weights ready for release. + + Examples: + from torchvision import models as M + + # Classification + model = M.mobilenet_v3_large(pretrained=False) + print(store_model_weights(model, './class.pth')) + + # Quantized Classification + model = M.quantization.mobilenet_v3_large(pretrained=False, quantize=False) + model.fuse_model() + model.qconfig = torch.quantization.get_default_qat_qconfig('qnnpack') + _ = torch.quantization.prepare_qat(model, inplace=True) + print(store_model_weights(model, './qat.pth')) + + # Object Detection + model = M.detection.fasterrcnn_mobilenet_v3_large_fpn(pretrained=False, pretrained_backbone=False) + print(store_model_weights(model, './obj.pth')) + + # Segmentation + model = M.segmentation.deeplabv3_mobilenet_v3_large(pretrained=False, pretrained_backbone=False, aux_loss=True) + print(store_model_weights(model, './segm.pth', strict=False)) + + Args: + model (pytorch.nn.Module): The model on which the weights will be loaded for validation purposes. + checkpoint_path (str): The path of the checkpoint we will load. + checkpoint_key (str, optional): The key of the checkpoint where the model weights are stored. + Default: "model". + strict (bool): whether to strictly enforce that the keys + in :attr:`state_dict` match the keys returned by this module's + :meth:`~torch.nn.Module.state_dict` function. Default: ``True`` + + Returns: + output_path (str): The location where the weights are saved. + """ + # Store the new model next to the checkpoint_path + checkpoint_path = os.path.abspath(checkpoint_path) + output_dir = os.path.dirname(checkpoint_path) + + # Deep copy to avoid side-effects on the model object. + model = copy.deepcopy(model) + checkpoint = torch.load(checkpoint_path, map_location='cpu') + + # Load the weights to the model to validate that everything works + # and remove unnecessary weights (such as auxiliaries, etc) + model.load_state_dict(checkpoint[checkpoint_key], strict=strict) + + tmp_path = os.path.join(output_dir, str(model.__hash__())) + torch.save(model.state_dict(), tmp_path) + + sha256_hash = hashlib.sha256() + with open(tmp_path, "rb") as f: + # Read and update hash string value in blocks of 4K + for byte_block in iter(lambda: f.read(4096), b""): + sha256_hash.update(byte_block) + hh = sha256_hash.hexdigest() + + output_path = os.path.join(output_dir, "weights-" + str(hh[:8]) + ".pth") + os.replace(tmp_path, output_path) + + return output_path diff --git a/cv/classification/resnext101_32x8d/pytorch/__init__.py b/cv/classification/resnext101_32x8d/pytorch/__init__.py new file mode 100644 index 000000000..6faec1658 --- /dev/null +++ b/cv/classification/resnext101_32x8d/pytorch/__init__.py @@ -0,0 +1,5 @@ +from .utils import * +from .common_utils import * +from .data_loader import * + +__all__ = [k for k in globals().keys() if not k.startswith("_")] diff --git a/cv/classification/resnext101_32x8d/pytorch/common.py b/cv/classification/resnext101_32x8d/pytorch/common.py deleted file mode 100644 index 34b391641..000000000 --- a/cv/classification/resnext101_32x8d/pytorch/common.py +++ /dev/null @@ -1,2338 +0,0 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright Declaration: This software, including all of its code and documentation, -# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX -# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright -# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar -# CoreX. No user of this software shall have any right, ownership or interest in this software and -# any use of this software shall be in compliance with the terms and conditions of the End User -# License Agreement. -""" - Common routines for models in PyTorch. -""" - -__all__ = ['round_channels', 'Identity', 'BreakBlock', 'Swish', 'HSigmoid', 'HSwish', 'get_activation_layer', - 'SelectableDense', 'DenseBlock', 'ConvBlock1d', 'conv1x1', 'conv3x3', 'depthwise_conv3x3', 'ConvBlock', - 'conv1x1_block', 'conv3x3_block', 'conv5x5_block', 'conv7x7_block', 'dwconv_block', 'dwconv3x3_block', - 'dwconv5x5_block', 'dwsconv3x3_block', 'PreConvBlock', 'pre_conv1x1_block', 'pre_conv3x3_block', - 'AsymConvBlock', 'asym_conv3x3_block', 'DeconvBlock', 'deconv3x3_block', 'NormActivation', - 'InterpolationBlock', 'ChannelShuffle', 'ChannelShuffle2', 'SEBlock', 'SABlock', 'SAConvBlock', - 'saconv3x3_block', 'DucBlock', 'IBN', 'DualPathSequential', 'Concurrent', 'SequentialConcurrent', - 'ParametricSequential', 'ParametricConcurrent', 'Hourglass', 'SesquialteralHourglass', - 'MultiOutputSequential', 'ParallelConcurent', 'DualPathParallelConcurent', 'Flatten', 'HeatmapMaxDetBlock'] - -import math -from inspect import isfunction -import torch -import torch.nn as nn -import torch.nn.functional as F -from torch.nn.parameter import Parameter - - -def round_channels(channels, - divisor=8): - """ - Round weighted channel number (make divisible operation). - - Parameters: - ---------- - channels : int or float - Original number of channels. - divisor : int, default 8 - Alignment value. - - Returns: - ------- - int - Weighted number of channels. - """ - rounded_channels = max(int(channels + divisor / 2.0) // divisor * divisor, divisor) - if float(rounded_channels) < 0.9 * channels: - rounded_channels += divisor - return rounded_channels - - -class Identity(nn.Module): - """ - Identity block. - """ - def __init__(self): - super(Identity, self).__init__() - - def forward(self, x): - return x - - def __repr__(self): - return '{name}()'.format(name=self.__class__.__name__) - - -class BreakBlock(nn.Module): - """ - Break coonnection block for hourglass. - """ - def __init__(self): - super(BreakBlock, self).__init__() - - def forward(self, x): - return None - - def __repr__(self): - return '{name}()'.format(name=self.__class__.__name__) - - -class Swish(nn.Module): - """ - Swish activation function from 'Searching for Activation Functions,' https://arxiv.org/abs/1710.05941. - """ - def forward(self, x): - return x * torch.sigmoid(x) - - -class HSigmoid(nn.Module): - """ - Approximated sigmoid function, so-called hard-version of sigmoid from 'Searching for MobileNetV3,' - https://arxiv.org/abs/1905.02244. - """ - def forward(self, x): - return F.relu6(x + 3.0, inplace=True) / 6.0 - - -class HSwish(nn.Module): - """ - H-Swish activation function from 'Searching for MobileNetV3,' https://arxiv.org/abs/1905.02244. - - Parameters: - ---------- - inplace : bool - Whether to use inplace version of the module. - """ - def __init__(self, inplace=False): - super(HSwish, self).__init__() - self.inplace = inplace - - def forward(self, x): - return x * F.relu6(x + 3.0, inplace=self.inplace) / 6.0 - - -def get_activation_layer(activation): - """ - Create activation layer from string/function. - - Parameters: - ---------- - activation : function, or str, or nn.Module - Activation function or name of activation function. - - Returns: - ------- - nn.Module - Activation layer. - """ - assert (activation is not None) - if isfunction(activation): - return activation() - elif isinstance(activation, str): - if activation == "relu": - return nn.ReLU(inplace=True) - elif activation == "relu6": - return nn.ReLU6(inplace=True) - elif activation == "swish": - return Swish() - elif activation == "hswish": - return HSwish(inplace=True) - elif activation == "sigmoid": - return nn.Sigmoid() - elif activation == "hsigmoid": - return HSigmoid() - elif activation == "identity": - return Identity() - else: - raise NotImplementedError() - else: - assert (isinstance(activation, nn.Module)) - return activation - - -class SelectableDense(nn.Module): - """ - Selectable dense layer. - - Parameters: - ---------- - in_features : int - Number of input features. - out_features : int - Number of output features. - bias : bool, default False - Whether the layer uses a bias vector. - num_options : int, default 1 - Number of selectable options. - """ - def __init__(self, - in_features, - out_features, - bias=False, - num_options=1): - super(SelectableDense, self).__init__() - self.in_features = in_features - self.out_features = out_features - self.use_bias = bias - self.num_options = num_options - self.weight = Parameter(torch.Tensor(num_options, out_features, in_features)) - if bias: - self.bias = Parameter(torch.Tensor(num_options, out_features)) - else: - self.register_parameter("bias", None) - - def forward(self, x, indices): - weight = torch.index_select(self.weight, dim=0, index=indices) - x = x.unsqueeze(-1) - x = weight.bmm(x) - x = x.squeeze(dim=-1) - if self.use_bias: - bias = torch.index_select(self.bias, dim=0, index=indices) - x += bias - return x - - def extra_repr(self): - return "in_features={}, out_features={}, bias={}, num_options={}".format( - self.in_features, self.out_features, self.use_bias, self.num_options) - - -class DenseBlock(nn.Module): - """ - Standard dense block with Batch normalization and activation. - - Parameters: - ---------- - in_features : int - Number of input features. - out_features : int - Number of output features. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - def __init__(self, - in_features, - out_features, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - super(DenseBlock, self).__init__() - self.activate = (activation is not None) - self.use_bn = use_bn - - self.fc = nn.Linear( - in_features=in_features, - out_features=out_features, - bias=bias) - if self.use_bn: - self.bn = nn.BatchNorm1d( - num_features=out_features, - eps=bn_eps) - if self.activate: - self.activ = get_activation_layer(activation) - - def forward(self, x): - x = self.fc(x) - if self.use_bn: - x = self.bn(x) - if self.activate: - x = self.activ(x) - return x - - -class ConvBlock1d(nn.Module): - """ - Standard 1D convolution block with Batch normalization and activation. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int - Convolution window size. - stride : int - Strides of the convolution. - padding : int - Padding value for convolution layer. - dilation : int - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - super(ConvBlock1d, self).__init__() - self.activate = (activation is not None) - self.use_bn = use_bn - - self.conv = nn.Conv1d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - if self.use_bn: - self.bn = nn.BatchNorm1d( - num_features=out_channels, - eps=bn_eps) - if self.activate: - self.activ = get_activation_layer(activation) - - def forward(self, x): - x = self.conv(x) - if self.use_bn: - x = self.bn(x) - if self.activate: - x = self.activ(x) - return x - - -def conv1x1(in_channels, - out_channels, - stride=1, - groups=1, - bias=False): - """ - Convolution 1x1 layer. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - """ - return nn.Conv2d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=1, - stride=stride, - groups=groups, - bias=bias) - - -def conv3x3(in_channels, - out_channels, - stride=1, - padding=1, - dilation=1, - groups=1, - bias=False): - """ - Convolution 3x3 layer. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - """ - return nn.Conv2d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - - -def depthwise_conv3x3(channels, - stride=1, - padding=1, - dilation=1, - bias=False): - """ - Depthwise convolution 3x3 layer. - - Parameters: - ---------- - channels : int - Number of input/output channels. - strides : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - """ - return nn.Conv2d( - in_channels=channels, - out_channels=channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - groups=channels, - bias=bias) - - -class ConvBlock(nn.Module): - """ - Standard convolution block with Batch normalization and activation. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - super(ConvBlock, self).__init__() - self.activate = (activation is not None) - self.use_bn = use_bn - self.use_pad = (isinstance(padding, (list, tuple)) and (len(padding) == 4)) - - if self.use_pad: - self.pad = nn.ZeroPad2d(padding=padding) - padding = 0 - self.conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - if self.use_bn: - self.bn = nn.BatchNorm2d( - num_features=out_channels, - eps=bn_eps) - if self.activate: - self.activ = get_activation_layer(activation) - - def forward(self, x): - if self.use_pad: - x = self.pad(x) - x = self.conv(x) - if self.use_bn: - x = self.bn(x) - if self.activate: - x = self.activ(x) - return x - - -def conv1x1_block(in_channels, - out_channels, - stride=1, - padding=0, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 1x1 version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 0 - Padding value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return ConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=1, - stride=stride, - padding=padding, - groups=groups, - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - - -def conv3x3_block(in_channels, - out_channels, - stride=1, - padding=1, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 3x3 version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return ConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - - -def conv5x5_block(in_channels, - out_channels, - stride=1, - padding=2, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 5x5 version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 2 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return ConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=5, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - - -def conv7x7_block(in_channels, - out_channels, - stride=1, - padding=3, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 7x7 version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 1 - Strides of the convolution. - padding : int or tuple/list of 2 int, default 3 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return ConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=7, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - - -def dwconv_block(in_channels, - out_channels, - kernel_size, - stride=1, - padding=1, - dilation=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - Depthwise version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return ConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=out_channels, - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - - -def dwconv3x3_block(in_channels, - out_channels, - stride=1, - padding=1, - dilation=1, - bias=False, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 3x3 depthwise version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return dwconv_block( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias, - bn_eps=bn_eps, - activation=activation) - - -def dwconv5x5_block(in_channels, - out_channels, - stride=1, - padding=2, - dilation=1, - bias=False, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 5x5 depthwise version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 2 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return dwconv_block( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=5, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias, - bn_eps=bn_eps, - activation=activation) - - -class DwsConvBlock(nn.Module): - """ - Depthwise separable convolution block with BatchNorms and activations at each convolution layers. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - dw_use_bn : bool, default True - Whether to use BatchNorm layer (depthwise convolution block). - pw_use_bn : bool, default True - Whether to use BatchNorm layer (pointwise convolution block). - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - dw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the depthwise convolution block. - pw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the pointwise convolution block. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - dilation=1, - bias=False, - dw_use_bn=True, - pw_use_bn=True, - bn_eps=1e-5, - dw_activation=(lambda: nn.ReLU(inplace=True)), - pw_activation=(lambda: nn.ReLU(inplace=True))): - super(DwsConvBlock, self).__init__() - self.dw_conv = dwconv_block( - in_channels=in_channels, - out_channels=in_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias, - use_bn=dw_use_bn, - bn_eps=bn_eps, - activation=dw_activation) - self.pw_conv = conv1x1_block( - in_channels=in_channels, - out_channels=out_channels, - bias=bias, - use_bn=pw_use_bn, - bn_eps=bn_eps, - activation=pw_activation) - - def forward(self, x): - x = self.dw_conv(x) - x = self.pw_conv(x) - return x - - -def dwsconv3x3_block(in_channels, - out_channels, - stride=1, - padding=1, - dilation=1, - bias=False, - bn_eps=1e-5, - dw_activation=(lambda: nn.ReLU(inplace=True)), - pw_activation=(lambda: nn.ReLU(inplace=True)), - **kwargs): - """ - 3x3 depthwise separable version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - dw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the depthwise convolution block. - pw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the pointwise convolution block. - """ - return DwsConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias, - bn_eps=bn_eps, - dw_activation=dw_activation, - pw_activation=pw_activation, - **kwargs) - - -class PreConvBlock(nn.Module): - """ - Convolution block with Batch normalization and ReLU pre-activation. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int or tuple/list of 2 int - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - return_preact : bool, default False - Whether return pre-activation. It's used by PreResNet. - activate : bool, default True - Whether activate the convolution block. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - dilation=1, - bias=False, - use_bn=True, - return_preact=False, - activate=True): - super(PreConvBlock, self).__init__() - self.return_preact = return_preact - self.activate = activate - self.use_bn = use_bn - - if self.use_bn: - self.bn = nn.BatchNorm2d(num_features=in_channels) - if self.activate: - self.activ = nn.ReLU(inplace=True) - self.conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - def forward(self, x): - if self.use_bn: - x = self.bn(x) - if self.activate: - x = self.activ(x) - if self.return_preact: - x_pre_activ = x - x = self.conv(x) - if self.return_preact: - return x, x_pre_activ - else: - return x - - -def pre_conv1x1_block(in_channels, - out_channels, - stride=1, - bias=False, - use_bn=True, - return_preact=False, - activate=True): - """ - 1x1 version of the pre-activated convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - return_preact : bool, default False - Whether return pre-activation. - activate : bool, default True - Whether activate the convolution block. - """ - return PreConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=1, - stride=stride, - padding=0, - bias=bias, - use_bn=use_bn, - return_preact=return_preact, - activate=activate) - - -def pre_conv3x3_block(in_channels, - out_channels, - stride=1, - padding=1, - dilation=1, - bias=False, - use_bn=True, - return_preact=False, - activate=True): - """ - 3x3 version of the pre-activated convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - return_preact : bool, default False - Whether return pre-activation. - activate : bool, default True - Whether activate the convolution block. - """ - return PreConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias, - use_bn=use_bn, - return_preact=return_preact, - activate=activate) - - -class AsymConvBlock(nn.Module): - """ - Asymmetric separable convolution block. - - Parameters: - ---------- - channels : int - Number of input/output channels. - kernel_size : int - Convolution window size. - padding : int - Padding value for convolution layer. - dilation : int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - lw_use_bn : bool, default True - Whether to use BatchNorm layer (leftwise convolution block). - rw_use_bn : bool, default True - Whether to use BatchNorm layer (rightwise convolution block). - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - lw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the leftwise convolution block. - rw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the rightwise convolution block. - """ - def __init__(self, - channels, - kernel_size, - padding, - dilation=1, - groups=1, - bias=False, - lw_use_bn=True, - rw_use_bn=True, - bn_eps=1e-5, - lw_activation=(lambda: nn.ReLU(inplace=True)), - rw_activation=(lambda: nn.ReLU(inplace=True))): - super(AsymConvBlock, self).__init__() - self.lw_conv = ConvBlock( - in_channels=channels, - out_channels=channels, - kernel_size=(kernel_size, 1), - stride=1, - padding=(padding, 0), - dilation=(dilation, 1), - groups=groups, - bias=bias, - use_bn=lw_use_bn, - bn_eps=bn_eps, - activation=lw_activation) - self.rw_conv = ConvBlock( - in_channels=channels, - out_channels=channels, - kernel_size=(1, kernel_size), - stride=1, - padding=(0, padding), - dilation=(1, dilation), - groups=groups, - bias=bias, - use_bn=rw_use_bn, - bn_eps=bn_eps, - activation=rw_activation) - - def forward(self, x): - x = self.lw_conv(x) - x = self.rw_conv(x) - return x - - -def asym_conv3x3_block(padding=1, - **kwargs): - """ - 3x3 asymmetric separable convolution block. - - Parameters: - ---------- - channels : int - Number of input/output channels. - padding : int, default 1 - Padding value for convolution layer. - dilation : int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - lw_use_bn : bool, default True - Whether to use BatchNorm layer (leftwise convolution block). - rw_use_bn : bool, default True - Whether to use BatchNorm layer (rightwise convolution block). - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - lw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the leftwise convolution block. - rw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the rightwise convolution block. - """ - return AsymConvBlock( - kernel_size=3, - padding=padding, - **kwargs) - - -class DeconvBlock(nn.Module): - """ - Deconvolution block with batch normalization and activation. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int - Strides of the deconvolution. - padding : int or tuple/list of 2 int - Padding value for deconvolution layer. - ext_padding : tuple/list of 4 int, default None - Extra padding value for deconvolution layer. - out_padding : int or tuple/list of 2 int - Output padding value for deconvolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for deconvolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - ext_padding=None, - out_padding=0, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - super(DeconvBlock, self).__init__() - self.activate = (activation is not None) - self.use_bn = use_bn - self.use_pad = (ext_padding is not None) - - if self.use_pad: - self.pad = nn.ZeroPad2d(padding=ext_padding) - self.conv = nn.ConvTranspose2d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - output_padding=out_padding, - dilation=dilation, - groups=groups, - bias=bias) - if self.use_bn: - self.bn = nn.BatchNorm2d( - num_features=out_channels, - eps=bn_eps) - if self.activate: - self.activ = get_activation_layer(activation) - - def forward(self, x): - if self.use_pad: - x = self.pad(x) - x = self.conv(x) - if self.use_bn: - x = self.bn(x) - if self.activate: - x = self.activ(x) - return x - - -def deconv3x3_block(padding=1, - out_padding=1, - **kwargs): - """ - 3x3 version of the deconvolution block with batch normalization and activation. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the deconvolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for deconvolution layer. - ext_padding : tuple/list of 4 int, default None - Extra padding value for deconvolution layer. - out_padding : int or tuple/list of 2 int, default 1 - Output padding value for deconvolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for deconvolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return DeconvBlock( - kernel_size=3, - padding=padding, - out_padding=out_padding, - **kwargs) - - -class NormActivation(nn.Module): - """ - Activation block with preliminary batch normalization. It's used by itself as the final block in PreResNet. - - Parameters: - ---------- - in_channels : int - Number of input channels. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - def __init__(self, - in_channels, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - super(NormActivation, self).__init__() - self.bn = nn.BatchNorm2d( - num_features=in_channels, - eps=bn_eps) - self.activ = get_activation_layer(activation) - - def forward(self, x): - x = self.bn(x) - x = self.activ(x) - return x - - -class InterpolationBlock(nn.Module): - """ - Interpolation upsampling block. - - Parameters: - ---------- - scale_factor : int - Multiplier for spatial size. - out_size : tuple of 2 int, default None - Spatial size of the output tensor for the bilinear interpolation operation. - mode : str, default 'bilinear' - Algorithm used for upsampling. - align_corners : bool, default True - Whether to align the corner pixels of the input and output tensors. - up : bool, default True - Whether to upsample or downsample. - """ - def __init__(self, - scale_factor, - out_size=None, - mode="bilinear", - align_corners=True, - up=True): - super(InterpolationBlock, self).__init__() - self.scale_factor = scale_factor - self.out_size = out_size - self.mode = mode - self.align_corners = align_corners - self.up = up - - def forward(self, x, size=None): - if (self.mode == "bilinear") or (size is not None): - out_size = self.calc_out_size(x) if size is None else size - return F.interpolate( - input=x, - size=out_size, - mode=self.mode, - align_corners=self.align_corners) - else: - return F.interpolate( - input=x, - scale_factor=self.scale_factor, - mode=self.mode, - align_corners=self.align_corners) - - def calc_out_size(self, x): - if self.out_size is not None: - return self.out_size - if self.up: - return tuple(s * self.scale_factor for s in x.shape[2:]) - else: - return tuple(s // self.scale_factor for s in x.shape[2:]) - - def __repr__(self): - s = '{name}(scale_factor={scale_factor}, out_size={out_size}, mode={mode}, align_corners={align_corners}, up={up})' # noqa - return s.format( - name=self.__class__.__name__, - scale_factor=self.scale_factor, - out_size=self.out_size, - mode=self.mode, - align_corners=self.align_corners, - up=self.up) - - def calc_flops(self, x): - assert (x.shape[0] == 1) - if self.mode == "bilinear": - num_flops = 9 * x.numel() - else: - num_flops = 4 * x.numel() - num_macs = 0 - return num_flops, num_macs - - -def channel_shuffle(x, - groups): - """ - Channel shuffle operation from 'ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devices,' - https://arxiv.org/abs/1707.01083. - - Parameters: - ---------- - x : Tensor - Input tensor. - groups : int - Number of groups. - - Returns: - ------- - Tensor - Resulted tensor. - """ - batch, channels, height, width = x.size() - # assert (channels % groups == 0) - channels_per_group = channels // groups - x = x.view(batch, groups, channels_per_group, height, width) - x = torch.transpose(x, 1, 2).contiguous() - x = x.view(batch, channels, height, width) - return x - - -class ChannelShuffle(nn.Module): - """ - Channel shuffle layer. This is a wrapper over the same operation. It is designed to save the number of groups. - - Parameters: - ---------- - channels : int - Number of channels. - groups : int - Number of groups. - """ - def __init__(self, - channels, - groups): - super(ChannelShuffle, self).__init__() - # assert (channels % groups == 0) - if channels % groups != 0: - raise ValueError("channels must be divisible by groups") - self.groups = groups - - def forward(self, x): - return channel_shuffle(x, self.groups) - - def __repr__(self): - s = "{name}(groups={groups})" - return s.format( - name=self.__class__.__name__, - groups=self.groups) - - -def channel_shuffle2(x, - groups): - """ - Channel shuffle operation from 'ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devices,' - https://arxiv.org/abs/1707.01083. The alternative version. - - Parameters: - ---------- - x : Tensor - Input tensor. - groups : int - Number of groups. - - Returns: - ------- - Tensor - Resulted tensor. - """ - batch, channels, height, width = x.size() - # assert (channels % groups == 0) - channels_per_group = channels // groups - x = x.view(batch, channels_per_group, groups, height, width) - x = torch.transpose(x, 1, 2).contiguous() - x = x.view(batch, channels, height, width) - return x - - -class ChannelShuffle2(nn.Module): - """ - Channel shuffle layer. This is a wrapper over the same operation. It is designed to save the number of groups. - The alternative version. - - Parameters: - ---------- - channels : int - Number of channels. - groups : int - Number of groups. - """ - def __init__(self, - channels, - groups): - super(ChannelShuffle2, self).__init__() - # assert (channels % groups == 0) - if channels % groups != 0: - raise ValueError("channels must be divisible by groups") - self.groups = groups - - def forward(self, x): - return channel_shuffle2(x, self.groups) - - -class SEBlock(nn.Module): - """ - Squeeze-and-Excitation block from 'Squeeze-and-Excitation Networks,' https://arxiv.org/abs/1709.01507. - - Parameters: - ---------- - channels : int - Number of channels. - reduction : int, default 16 - Squeeze reduction value. - mid_channels : int or None, default None - Number of middle channels. - round_mid : bool, default False - Whether to round middle channel number (make divisible by 8). - use_conv : bool, default True - Whether to convolutional layers instead of fully-connected ones. - activation : function, or str, or nn.Module, default 'relu' - Activation function after the first convolution. - out_activation : function, or str, or nn.Module, default 'sigmoid' - Activation function after the last convolution. - """ - def __init__(self, - channels, - reduction=16, - mid_channels=None, - round_mid=False, - use_conv=True, - mid_activation=(lambda: nn.ReLU(inplace=True)), - out_activation=(lambda: nn.Sigmoid())): - super(SEBlock, self).__init__() - self.use_conv = use_conv - if mid_channels is None: - mid_channels = channels // reduction if not round_mid else round_channels(float(channels) / reduction) - - self.pool = nn.AdaptiveAvgPool2d(output_size=1) - if use_conv: - self.conv1 = conv1x1( - in_channels=channels, - out_channels=mid_channels, - bias=True) - else: - self.fc1 = nn.Linear( - in_features=channels, - out_features=mid_channels) - self.activ = get_activation_layer(mid_activation) - if use_conv: - self.conv2 = conv1x1( - in_channels=mid_channels, - out_channels=channels, - bias=True) - else: - self.fc2 = nn.Linear( - in_features=mid_channels, - out_features=channels) - self.sigmoid = get_activation_layer(out_activation) - - def forward(self, x): - w = self.pool(x) - if not self.use_conv: - w = w.view(x.size(0), -1) - w = self.conv1(w) if self.use_conv else self.fc1(w) - w = self.activ(w) - w = self.conv2(w) if self.use_conv else self.fc2(w) - w = self.sigmoid(w) - if not self.use_conv: - w = w.unsqueeze(2).unsqueeze(3) - x = x * w - return x - - -class SABlock(nn.Module): - """ - Split-Attention block from 'ResNeSt: Split-Attention Networks,' https://arxiv.org/abs/2004.08955. - - Parameters: - ---------- - out_channels : int - Number of output channels. - groups : int - Number of channel groups (cardinality, without radix). - radix : int - Number of splits within a cardinal group. - reduction : int, default 4 - Squeeze reduction value. - min_channels : int, default 32 - Minimal number of squeezed channels. - use_conv : bool, default True - Whether to convolutional layers instead of fully-connected ones. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - """ - def __init__(self, - out_channels, - groups, - radix, - reduction=4, - min_channels=32, - use_conv=True, - bn_eps=1e-5): - super(SABlock, self).__init__() - self.groups = groups - self.radix = radix - self.use_conv = use_conv - in_channels = out_channels * radix - mid_channels = max(in_channels // reduction, min_channels) - - self.pool = nn.AdaptiveAvgPool2d(output_size=1) - if use_conv: - self.conv1 = conv1x1( - in_channels=out_channels, - out_channels=mid_channels, - bias=True) - else: - self.fc1 = nn.Linear( - in_features=out_channels, - out_features=mid_channels) - self.bn = nn.BatchNorm2d( - num_features=mid_channels, - eps=bn_eps) - self.activ = nn.ReLU(inplace=True) - if use_conv: - self.conv2 = conv1x1( - in_channels=mid_channels, - out_channels=in_channels, - bias=True) - else: - self.fc2 = nn.Linear( - in_features=mid_channels, - out_features=in_channels) - self.softmax = nn.Softmax(dim=1) - - def forward(self, x): - batch, channels, height, width = x.size() - x = x.view(batch, self.radix, channels // self.radix, height, width) - w = x.sum(dim=1) - w = self.pool(w) - if not self.use_conv: - w = w.view(x.size(0), -1) - w = self.conv1(w) if self.use_conv else self.fc1(w) - w = self.bn(w) - w = self.activ(w) - w = self.conv2(w) if self.use_conv else self.fc2(w) - w = w.view(batch, self.groups, self.radix, -1) - w = torch.transpose(w, 1, 2).contiguous() - w = self.softmax(w) - w = w.view(batch, self.radix, -1, 1, 1) - x = x * w - x = x.sum(dim=1) - return x - - -class SAConvBlock(nn.Module): - """ - Split-Attention convolution block from 'ResNeSt: Split-Attention Networks,' https://arxiv.org/abs/2004.08955. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - radix : int, default 2 - Number of splits within a cardinal group. - reduction : int, default 4 - Squeeze reduction value. - min_channels : int, default 32 - Minimal number of squeezed channels. - use_conv : bool, default True - Whether to convolutional layers instead of fully-connected ones. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True)), - radix=2, - reduction=4, - min_channels=32, - use_conv=True): - super(SAConvBlock, self).__init__() - self.conv = ConvBlock( - in_channels=in_channels, - out_channels=(out_channels * radix), - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=(groups * radix), - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - self.att = SABlock( - out_channels=out_channels, - groups=groups, - radix=radix, - reduction=reduction, - min_channels=min_channels, - use_conv=use_conv, - bn_eps=bn_eps) - - def forward(self, x): - x = self.conv(x) - x = self.att(x) - return x - - -def saconv3x3_block(in_channels, - out_channels, - stride=1, - padding=1, - **kwargs): - """ - 3x3 version of the Split-Attention convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for convolution layer. - """ - return SAConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - **kwargs) - - -class DucBlock(nn.Module): - """ - Dense Upsampling Convolution (DUC) block from 'Understanding Convolution for Semantic Segmentation,' - https://arxiv.org/abs/1702.08502. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - scale_factor : int - Multiplier for spatial size. - """ - def __init__(self, - in_channels, - out_channels, - scale_factor): - super(DucBlock, self).__init__() - mid_channels = (scale_factor * scale_factor) * out_channels - - self.conv = conv3x3_block( - in_channels=in_channels, - out_channels=mid_channels) - self.pix_shuffle = nn.PixelShuffle(upscale_factor=scale_factor) - - def forward(self, x): - x = self.conv(x) - x = self.pix_shuffle(x) - return x - - -class IBN(nn.Module): - """ - Instance-Batch Normalization block from 'Two at Once: Enhancing Learning and Generalization Capacities via IBN-Net,' - https://arxiv.org/abs/1807.09441. - - Parameters: - ---------- - channels : int - Number of channels. - inst_fraction : float, default 0.5 - The first fraction of channels for normalization. - inst_first : bool, default True - Whether instance normalization be on the first part of channels. - """ - def __init__(self, - channels, - first_fraction=0.5, - inst_first=True): - super(IBN, self).__init__() - self.inst_first = inst_first - h1_channels = int(math.floor(channels * first_fraction)) - h2_channels = channels - h1_channels - self.split_sections = [h1_channels, h2_channels] - - if self.inst_first: - self.inst_norm = nn.InstanceNorm2d( - num_features=h1_channels, - affine=True) - self.batch_norm = nn.BatchNorm2d(num_features=h2_channels) - else: - self.batch_norm = nn.BatchNorm2d(num_features=h1_channels) - self.inst_norm = nn.InstanceNorm2d( - num_features=h2_channels, - affine=True) - - def forward(self, x): - x1, x2 = torch.split(x, split_size_or_sections=self.split_sections, dim=1) - if self.inst_first: - x1 = self.inst_norm(x1.contiguous()) - x2 = self.batch_norm(x2.contiguous()) - else: - x1 = self.batch_norm(x1.contiguous()) - x2 = self.inst_norm(x2.contiguous()) - x = torch.cat((x1, x2), dim=1) - return x - - -class DualPathSequential(nn.Sequential): - """ - A sequential container for modules with dual inputs/outputs. - Modules will be executed in the order they are added. - - Parameters: - ---------- - return_two : bool, default True - Whether to return two output after execution. - first_ordinals : int, default 0 - Number of the first modules with single input/output. - last_ordinals : int, default 0 - Number of the final modules with single input/output. - dual_path_scheme : function - Scheme of dual path response for a module. - dual_path_scheme_ordinal : function - Scheme of dual path response for an ordinal module. - """ - def __init__(self, - return_two=True, - first_ordinals=0, - last_ordinals=0, - dual_path_scheme=(lambda module, x1, x2: module(x1, x2)), - dual_path_scheme_ordinal=(lambda module, x1, x2: (module(x1), x2))): - super(DualPathSequential, self).__init__() - self.return_two = return_two - self.first_ordinals = first_ordinals - self.last_ordinals = last_ordinals - self.dual_path_scheme = dual_path_scheme - self.dual_path_scheme_ordinal = dual_path_scheme_ordinal - - def forward(self, x1, x2=None): - length = len(self._modules.values()) - for i, module in enumerate(self._modules.values()): - if (i < self.first_ordinals) or (i >= length - self.last_ordinals): - x1, x2 = self.dual_path_scheme_ordinal(module, x1, x2) - else: - x1, x2 = self.dual_path_scheme(module, x1, x2) - if self.return_two: - return x1, x2 - else: - return x1 - - -class Concurrent(nn.Sequential): - """ - A container for concatenation of modules on the base of the sequential container. - - Parameters: - ---------- - axis : int, default 1 - The axis on which to concatenate the outputs. - stack : bool, default False - Whether to concatenate tensors along a new dimension. - merge_type : str, default None - Type of branch merging. - """ - def __init__(self, - axis=1, - stack=False, - merge_type=None): - super(Concurrent, self).__init__() - assert (merge_type is None) or (merge_type in ["cat", "stack", "sum"]) - self.axis = axis - if merge_type is not None: - self.merge_type = merge_type - else: - self.merge_type = "stack" if stack else "cat" - - def forward(self, x): - out = [] - for module in self._modules.values(): - out.append(module(x)) - if self.merge_type == "stack": - out = torch.stack(tuple(out), dim=self.axis) - elif self.merge_type == "cat": - out = torch.cat(tuple(out), dim=self.axis) - elif self.merge_type == "sum": - out = torch.stack(tuple(out), dim=self.axis).sum(self.axis) - else: - raise NotImplementedError() - return out - - -class SequentialConcurrent(nn.Sequential): - """ - A sequential container with concatenated outputs. - Modules will be executed in the order they are added. - - Parameters: - ---------- - axis : int, default 1 - The axis on which to concatenate the outputs. - stack : bool, default False - Whether to concatenate tensors along a new dimension. - cat_input : bool, default True - Whether to concatenate input tensor. - """ - def __init__(self, - axis=1, - stack=False, - cat_input=True): - super(SequentialConcurrent, self).__init__() - self.axis = axis - self.stack = stack - self.cat_input = cat_input - - def forward(self, x): - out = [x] if self.cat_input else [] - for module in self._modules.values(): - x = module(x) - out.append(x) - if self.stack: - out = torch.stack(tuple(out), dim=self.axis) - else: - out = torch.cat(tuple(out), dim=self.axis) - return out - - -class ParametricSequential(nn.Sequential): - """ - A sequential container for modules with parameters. - Modules will be executed in the order they are added. - """ - def __init__(self, *args): - super(ParametricSequential, self).__init__(*args) - - def forward(self, x, **kwargs): - for module in self._modules.values(): - x = module(x, **kwargs) - return x - - -class ParametricConcurrent(nn.Sequential): - """ - A container for concatenation of modules with parameters. - - Parameters: - ---------- - axis : int, default 1 - The axis on which to concatenate the outputs. - """ - def __init__(self, axis=1): - super(ParametricConcurrent, self).__init__() - self.axis = axis - - def forward(self, x, **kwargs): - out = [] - for module in self._modules.values(): - out.append(module(x, **kwargs)) - out = torch.cat(tuple(out), dim=self.axis) - return out - - -class Hourglass(nn.Module): - """ - A hourglass module. - - Parameters: - ---------- - down_seq : nn.Sequential - Down modules as sequential. - up_seq : nn.Sequential - Up modules as sequential. - skip_seq : nn.Sequential - Skip connection modules as sequential. - merge_type : str, default 'add' - Type of concatenation of up and skip outputs. - return_first_skip : bool, default False - Whether return the first skip connection output. Used in ResAttNet. - """ - def __init__(self, - down_seq, - up_seq, - skip_seq, - merge_type="add", - return_first_skip=False): - super(Hourglass, self).__init__() - self.depth = len(down_seq) - assert (merge_type in ["cat", "add"]) - assert (len(up_seq) == self.depth) - assert (len(skip_seq) in (self.depth, self.depth + 1)) - self.merge_type = merge_type - self.return_first_skip = return_first_skip - self.extra_skip = (len(skip_seq) == self.depth + 1) - - self.down_seq = down_seq - self.up_seq = up_seq - self.skip_seq = skip_seq - - def _merge(self, x, y): - if y is not None: - if self.merge_type == "cat": - x = torch.cat((x, y), dim=1) - elif self.merge_type == "add": - x = x + y - return x - - def forward(self, x, **kwargs): - y = None - down_outs = [x] - for down_module in self.down_seq._modules.values(): - x = down_module(x) - down_outs.append(x) - for i in range(len(down_outs)): - if i != 0: - y = down_outs[self.depth - i] - skip_module = self.skip_seq[self.depth - i] - y = skip_module(y) - x = self._merge(x, y) - if i != len(down_outs) - 1: - if (i == 0) and self.extra_skip: - skip_module = self.skip_seq[self.depth] - x = skip_module(x) - up_module = self.up_seq[self.depth - 1 - i] - x = up_module(x) - if self.return_first_skip: - return x, y - else: - return x - - -class SesquialteralHourglass(nn.Module): - """ - A sesquialteral hourglass block. - - Parameters: - ---------- - down1_seq : nn.Sequential - The first down modules as sequential. - skip1_seq : nn.Sequential - The first skip connection modules as sequential. - up_seq : nn.Sequential - Up modules as sequential. - skip2_seq : nn.Sequential - The second skip connection modules as sequential. - down2_seq : nn.Sequential - The second down modules as sequential. - merge_type : str, default 'cat' - Type of concatenation of up and skip outputs. - """ - def __init__(self, - down1_seq, - skip1_seq, - up_seq, - skip2_seq, - down2_seq, - merge_type="cat"): - super(SesquialteralHourglass, self).__init__() - assert (len(down1_seq) == len(up_seq)) - assert (len(down1_seq) == len(down2_seq)) - assert (len(skip1_seq) == len(skip2_seq)) - assert (len(down1_seq) == len(skip1_seq) - 1) - assert (merge_type in ["cat", "add"]) - self.merge_type = merge_type - self.depth = len(down1_seq) - - self.down1_seq = down1_seq - self.skip1_seq = skip1_seq - self.up_seq = up_seq - self.skip2_seq = skip2_seq - self.down2_seq = down2_seq - - def _merge(self, x, y): - if y is not None: - if self.merge_type == "cat": - x = torch.cat((x, y), dim=1) - elif self.merge_type == "add": - x = x + y - return x - - def forward(self, x, **kwargs): - y = self.skip1_seq[0](x) - skip1_outs = [y] - for i in range(self.depth): - x = self.down1_seq[i](x) - y = self.skip1_seq[i + 1](x) - skip1_outs.append(y) - x = skip1_outs[self.depth] - y = self.skip2_seq[0](x) - skip2_outs = [y] - for i in range(self.depth): - x = self.up_seq[i](x) - y = skip1_outs[self.depth - 1 - i] - x = self._merge(x, y) - y = self.skip2_seq[i + 1](x) - skip2_outs.append(y) - x = self.skip2_seq[self.depth](x) - for i in range(self.depth): - x = self.down2_seq[i](x) - y = skip2_outs[self.depth - 1 - i] - x = self._merge(x, y) - return x - - -class MultiOutputSequential(nn.Sequential): - """ - A sequential container with multiple outputs. - Modules will be executed in the order they are added. - - Parameters: - ---------- - multi_output : bool, default True - Whether to return multiple output. - dual_output : bool, default False - Whether to return dual output. - return_last : bool, default True - Whether to forcibly return last value. - """ - def __init__(self, - multi_output=True, - dual_output=False, - return_last=True): - super(MultiOutputSequential, self).__init__() - self.multi_output = multi_output - self.dual_output = dual_output - self.return_last = return_last - - def forward(self, x): - outs = [] - for module in self._modules.values(): - x = module(x) - if hasattr(module, "do_output") and module.do_output: - outs.append(x) - elif hasattr(module, "do_output2") and module.do_output2: - assert (type(x) == tuple) - outs.extend(x[1]) - x = x[0] - if self.multi_output: - return [x] + outs if self.return_last else outs - elif self.dual_output: - return x, outs - else: - return x - - -class ParallelConcurent(nn.Sequential): - """ - A sequential container with multiple inputs and single/multiple outputs. - Modules will be executed in the order they are added. - - Parameters: - ---------- - axis : int, default 1 - The axis on which to concatenate the outputs. - merge_type : str, default 'list' - Type of branch merging. - """ - def __init__(self, - axis=1, - merge_type="list"): - super(ParallelConcurent, self).__init__() - assert (merge_type is None) or (merge_type in ["list", "cat", "stack", "sum"]) - self.axis = axis - self.merge_type = merge_type - - def forward(self, x): - out = [] - for module, xi in zip(self._modules.values(), x): - out.append(module(xi)) - if self.merge_type == "list": - pass - elif self.merge_type == "stack": - out = torch.stack(tuple(out), dim=self.axis) - elif self.merge_type == "cat": - out = torch.cat(tuple(out), dim=self.axis) - elif self.merge_type == "sum": - out = torch.stack(tuple(out), dim=self.axis).sum(self.axis) - else: - raise NotImplementedError() - return out - - -class DualPathParallelConcurent(nn.Sequential): - """ - A sequential container with multiple dual-path inputs and single/multiple outputs. - Modules will be executed in the order they are added. - - Parameters: - ---------- - axis : int, default 1 - The axis on which to concatenate the outputs. - merge_type : str, default 'list' - Type of branch merging. - """ - def __init__(self, - axis=1, - merge_type="list"): - super(DualPathParallelConcurent, self).__init__() - assert (merge_type is None) or (merge_type in ["list", "cat", "stack", "sum"]) - self.axis = axis - self.merge_type = merge_type - - def forward(self, x1, x2): - x1_out = [] - x2_out = [] - for module, x1i, x2i in zip(self._modules.values(), x1, x2): - y1i, y2i = module(x1i, x2i) - x1_out.append(y1i) - x2_out.append(y2i) - if self.merge_type == "list": - pass - elif self.merge_type == "stack": - x1_out = torch.stack(tuple(x1_out), dim=self.axis) - x2_out = torch.stack(tuple(x2_out), dim=self.axis) - elif self.merge_type == "cat": - x1_out = torch.cat(tuple(x1_out), dim=self.axis) - x2_out = torch.cat(tuple(x2_out), dim=self.axis) - elif self.merge_type == "sum": - x1_out = torch.stack(tuple(x1_out), dim=self.axis).sum(self.axis) - x2_out = torch.stack(tuple(x2_out), dim=self.axis).sum(self.axis) - else: - raise NotImplementedError() - return x1_out, x2_out - - -class Flatten(nn.Module): - """ - Simple flatten module. - """ - - def forward(self, x): - return x.view(x.size(0), -1) - - -class HeatmapMaxDetBlock(nn.Module): - """ - Heatmap maximum detector block (for human pose estimation task). - """ - def __init__(self): - super(HeatmapMaxDetBlock, self).__init__() - - def forward(self, x): - heatmap = x - vector_dim = 2 - batch = heatmap.shape[0] - channels = heatmap.shape[1] - in_size = x.shape[2:] - heatmap_vector = heatmap.view(batch, channels, -1) - scores, indices = heatmap_vector.max(dim=vector_dim, keepdims=True) - scores_mask = (scores > 0.0).float() - pts_x = (indices % in_size[1]) * scores_mask - pts_y = (indices // in_size[1]) * scores_mask - pts = torch.cat((pts_x, pts_y, scores), dim=vector_dim) - for b in range(batch): - for k in range(channels): - hm = heatmap[b, k, :, :] - px = int(pts[b, k, 0]) - py = int(pts[b, k, 1]) - if (0 < px < in_size[1] - 1) and (0 < py < in_size[0] - 1): - pts[b, k, 0] += (hm[py, px + 1] - hm[py, px - 1]).sign() * 0.25 - pts[b, k, 1] += (hm[py + 1, px] - hm[py - 1, px]).sign() * 0.25 - return pts - - @staticmethod - def calc_flops(x): - assert (x.shape[0] == 1) - num_flops = x.numel() + 26 * x.shape[1] - num_macs = 0 - return num_flops, num_macs diff --git a/cv/classification/resnext101_32x8d/pytorch/common_utils/__init__.py b/cv/classification/resnext101_32x8d/pytorch/common_utils/__init__.py new file mode 100644 index 000000000..32e8c4f57 --- /dev/null +++ b/cv/classification/resnext101_32x8d/pytorch/common_utils/__init__.py @@ -0,0 +1,23 @@ +import random + +import numpy as np + +from .dist import * +from .metric_logger import * +from .misc import * +from .smooth_value import * + +def manual_seed(seed, deterministic=False): + random.seed(seed) + np.random.seed(seed) + os.environ['PYTHONHASHSEED'] = str(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + + if deterministic: + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + else: + torch.backends.cudnn.deterministic = False + torch.backends.cudnn.benchmark = True \ No newline at end of file diff --git a/cv/classification/resnext101_32x8d/pytorch/common_utils/dist.py b/cv/classification/resnext101_32x8d/pytorch/common_utils/dist.py new file mode 100644 index 000000000..ea56ca267 --- /dev/null +++ b/cv/classification/resnext101_32x8d/pytorch/common_utils/dist.py @@ -0,0 +1,144 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist + + + +def setup_for_distributed(is_master): + """ + This function disables printing when not in master process + """ + import builtins as __builtin__ + builtin_print = __builtin__.print + + def print(*args, **kwargs): + force = kwargs.pop('force', False) + if is_master or force: + builtin_print(*args, **kwargs) + + __builtin__.print = print + + +def is_dist_avail_and_initialized(): + if not dist.is_available(): + return False + if not dist.is_initialized(): + return False + return True + + +def get_world_size(): + if not is_dist_avail_and_initialized(): + return 1 + return dist.get_world_size() + + +def get_rank(): + if not is_dist_avail_and_initialized(): + return 0 + return dist.get_rank() + + +def is_main_process(): + return get_rank() == 0 + + +def save_on_master(*args, **kwargs): + if is_main_process(): + torch.save(*args, **kwargs) + + +def get_dist_backend(args=None): + DIST_BACKEND_ENV = "PT_DIST_BACKEND" + if DIST_BACKEND_ENV in os.environ: + return os.environ[DIST_BACKEND_ENV] + + if args is None: + args = dict() + + backend_attr_name = "dist_backend" + + if hasattr(args, backend_attr_name): + return getattr(args, backend_attr_name) + + if backend_attr_name in args: + return args[backend_attr_name] + + return "nccl" + + +def init_distributed_mode(args): + if 'RANK' in os.environ and 'WORLD_SIZE' in os.environ: + args.rank = int(os.environ["RANK"]) + args.world_size = int(os.environ['WORLD_SIZE']) + args.gpu = int(os.environ['LOCAL_RANK']) + elif 'SLURM_PROCID' in os.environ: + args.rank = int(os.environ['SLURM_PROCID']) + args.gpu = args.rank % torch.cuda.device_count() + else: + print('Not using distributed mode') + args.distributed = False + return + + args.distributed = True + + torch.cuda.set_device(args.gpu) + dist_backend = get_dist_backend(args) + print('| distributed init (rank {}): {}'.format( + args.rank, args.dist_url), flush=True) + torch.distributed.init_process_group(backend=dist_backend, init_method=args.dist_url, + world_size=args.world_size, rank=args.rank) + torch.distributed.barrier() + setup_for_distributed(args.rank == 0) + + +def all_gather(data): + """ + Run all_gather on arbitrary picklable data (not necessarily tensors) + Args: + data: any picklable object + Returns: + list[data]: list of data gathered from each rank + """ + world_size = get_world_size() + if world_size == 1: + return [data] + data_list = [None] * world_size + dist.all_gather_object(data_list, data) + return data_list + + +def reduce_dict(input_dict, average=True): + """ + Args: + input_dict (dict): all the values will be reduced + average (bool): whether to do average or sum + Reduce the values in the dictionary from all processes so that all processes + have the averaged results. Returns a dict with the same fields as + input_dict, after reduction. + """ + world_size = get_world_size() + if world_size < 2: + return input_dict + with torch.no_grad(): + names = [] + values = [] + # sort the keys so that they are consistent across processes + for k in sorted(input_dict.keys()): + names.append(k) + values.append(input_dict[k]) + values = torch.stack(values, dim=0) + dist.all_reduce(values) + if average: + values /= world_size + reduced_dict = {k: v for k, v in zip(names, values)} + return reduced_dict diff --git a/cv/classification/resnext101_32x8d/pytorch/common_utils/metric_logger.py b/cv/classification/resnext101_32x8d/pytorch/common_utils/metric_logger.py new file mode 100644 index 000000000..960641c4d --- /dev/null +++ b/cv/classification/resnext101_32x8d/pytorch/common_utils/metric_logger.py @@ -0,0 +1,94 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict +import datetime +import time + +import torch +from .smooth_value import SmoothedValue + +""" +Examples: + +logger = MetricLogger(" ") + +>>> # For iter dataloader +>>> metric_logger.add_meter('img/s', utils.SmoothedValue(window_size=10, fmt='{value}')) +>>> header = 'Epoch: [{}]'.format(epoch) +>>> for image, target in metric_logger.log_every(data_loader, print_freq, header): +>>> ... +>>> logger.metric_logger.meters['img/s'].update(fps) + +""" + +class MetricLogger(object): + + def __init__(self, delimiter="\t"): + self.meters = defaultdict(SmoothedValue) + self.delimiter = delimiter + + def update(self, **kwargs): + for k, v in kwargs.items(): + if isinstance(v, torch.Tensor): + v = v.item() + assert isinstance(v, (float, int)) + self.meters[k].update(v) + + def __getattr__(self, attr): + if attr in self.meters: + return self.meters[attr] + if attr in self.__dict__: + return self.__dict__[attr] + raise AttributeError("'{}' object has no attribute '{}'".format( + type(self).__name__, attr)) + + def __str__(self): + loss_str = [] + for name, meter in self.meters.items(): + loss_str.append( + "{}: {}".format(name, str(meter)) + ) + return self.delimiter.join(loss_str) + + def synchronize_between_processes(self): + for meter in self.meters.values(): + meter.synchronize_between_processes() + + def add_meter(self, name, meter): + self.meters[name] = meter + + def log_every(self, iterable, print_freq, header=None): + i = 0 + if not header: + header = '' + start_time = time.time() + end = time.time() + iter_time = SmoothedValue(fmt='{avg:.4f}') + data_time = SmoothedValue(fmt='{avg:.4f}') + space_fmt = ':' + str(len(str(len(iterable)))) + 'd' + log_msg = self.delimiter.join([ + header, + '[{0' + space_fmt + '}/{1}]', + 'eta: {eta}', + '{meters}', + 'time: {time}', + 'data: {data}' + ]) + for obj in iterable: + data_time.update(time.time() - end) + yield obj + iter_time.update(time.time() - end) + if i % print_freq == 0: + eta_seconds = iter_time.global_avg * (len(iterable) - i) + eta_string = str(datetime.timedelta(seconds=int(eta_seconds))) + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time))) + i += 1 + end = time.time() + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('{} Total time: {}'.format(header, total_time_str)) diff --git a/cv/classification/resnext101_32x8d/pytorch/common_utils/misc.py b/cv/classification/resnext101_32x8d/pytorch/common_utils/misc.py new file mode 100644 index 000000000..c9b501cf8 --- /dev/null +++ b/cv/classification/resnext101_32x8d/pytorch/common_utils/misc.py @@ -0,0 +1,11 @@ +import os +import sys +import errno + + +def mkdir(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise \ No newline at end of file diff --git a/cv/classification/resnext101_32x8d/pytorch/common_utils/smooth_value.py b/cv/classification/resnext101_32x8d/pytorch/common_utils/smooth_value.py new file mode 100644 index 000000000..30cb89d60 --- /dev/null +++ b/cv/classification/resnext101_32x8d/pytorch/common_utils/smooth_value.py @@ -0,0 +1,75 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist +from .dist import is_dist_avail_and_initialized + + +class SmoothedValue(object): + """Track a series of values and provide access to smoothed values over a + window or the global series average. + """ + + def __init__(self, window_size=20, fmt=None): + if fmt is None: + fmt = "{median:.4f} ({global_avg:.4f})" + self.deque = deque(maxlen=window_size) + self.total = 0.0 + self.count = 0 + self.fmt = fmt + + def update(self, value, n=1): + self.deque.append(value) + self.count += n + self.total += value * n + + def synchronize_between_processes(self): + """ + Warning: does not synchronize the deque! + """ + if not is_dist_avail_and_initialized(): + return + t = torch.tensor([self.count, self.total], dtype=torch.float32, device='cuda') + dist.barrier() + dist.all_reduce(t) + t = t.tolist() + self.count = int(t[0]) + self.total = t[1] + + @property + def median(self): + d = torch.tensor(list(self.deque)) + return d.median().item() + + @property + def avg(self): + d = torch.tensor(list(self.deque), dtype=torch.float32) + return d.mean().item() + + @property + def global_avg(self): + return self.total / self.count + + @property + def max(self): + return max(self.deque) + + @property + def value(self): + return self.deque[-1] + + def __str__(self): + return self.fmt.format( + median=self.median, + avg=self.avg, + global_avg=self.global_avg, + max=self.max, + value=self.value) \ No newline at end of file diff --git a/cv/classification/resnext101_32x8d/pytorch/dataloader/__init__.py b/cv/classification/resnext101_32x8d/pytorch/dataloader/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/classification/resnext101_32x8d/pytorch/dataloader/classification.py b/cv/classification/resnext101_32x8d/pytorch/dataloader/classification.py new file mode 100644 index 000000000..030af6dee --- /dev/null +++ b/cv/classification/resnext101_32x8d/pytorch/dataloader/classification.py @@ -0,0 +1,113 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +import os +import time + +import torch +import torchvision +from .utils import presets_classification as presets + +""" +Examples: + +>>> dataset_train, dataset_val = load_data(train_dir, val_dir, args) +""" + + +def get_datasets(traindir, + valdir, + resize_size=256, + crop_size=224, + auto_augment_policy=None, + random_erase_prob=0.): + # Data loading code + print("Loading data") + print("Loading training data") + dataset = torchvision.datasets.ImageFolder( + traindir, + presets.ClassificationPresetTrain(crop_size=crop_size, auto_augment_policy=auto_augment_policy, + random_erase_prob=random_erase_prob)) + + print("Loading validation data") + dataset_test = torchvision.datasets.ImageFolder( + valdir, + presets.ClassificationPresetEval(crop_size=crop_size, resize_size=resize_size)) + + return dataset, dataset_test + + +def get_input_size(model): + biger_input_size_models = ['inception'] + resize_size = 256 + crop_size = 224 + for bi_model in biger_input_size_models: + if bi_model in model: + resize_size = 342 + crop_size = 299 + + return resize_size, crop_size + + +def load_data(train_dir, val_dir, args): + auto_augment_policy = getattr(args, "auto_augment", None) + random_erase_prob = getattr(args, "random_erase", 0.0) + resize_size, crop_size = get_input_size(args.model) + dataset, dataset_test = get_datasets(train_dir, val_dir, + auto_augment_policy=auto_augment_policy, + random_erase_prob=random_erase_prob, + resize_size=resize_size, + crop_size=crop_size) + if args.distributed: + train_sampler = torch.utils.data.distributed.DistributedSampler(dataset) + test_sampler = torch.utils.data.distributed.DistributedSampler(dataset_test) + else: + train_sampler = torch.utils.data.RandomSampler(dataset) + test_sampler = torch.utils.data.SequentialSampler(dataset_test) + + return dataset, dataset_test, train_sampler, test_sampler + + +def _create_torch_dataloader(train_dir, val_dir, args): + dataset, dataset_test, train_sampler, test_sampler = load_data(train_dir, val_dir, args) + data_loader = torch.utils.data.DataLoader( + dataset, batch_size=args.batch_size, + sampler=train_sampler, num_workers=args.workers, pin_memory=True) + + data_loader_test = torch.utils.data.DataLoader( + dataset_test, batch_size=args.batch_size, + sampler=test_sampler, num_workers=args.workers, pin_memory=True) + + return data_loader, data_loader_test + + +def _create_dali_dataloader(train_dir, val_dir, args): + from .dali_classification import get_imagenet_iter_dali + device = torch.cuda.current_device() + _, crop_size = get_input_size(args.model) + data_loader = get_imagenet_iter_dali('train', train_dir, args.batch_size, + num_threads=args.workers, + device_id=device, + size=crop_size) + data_loader_test = get_imagenet_iter_dali('val', train_dir, args.batch_size, + num_threads=args.workers, + device_id=device, + size=crop_size) + + return data_loader, data_loader_test + + +def create_dataloader(train_dir, val_dir, args): + print("Creating data loaders") + if args.dali: + train_dir = os.path.dirname(train_dir) + val_dir = os.path.dirname(val_dir) + return _create_dali_dataloader(train_dir, val_dir, args) + return _create_torch_dataloader(train_dir, val_dir, args) diff --git a/cv/classification/resnext101_32x8d/pytorch/dataloader/dali_classification.py b/cv/classification/resnext101_32x8d/pytorch/dataloader/dali_classification.py new file mode 100644 index 000000000..4c92283b2 --- /dev/null +++ b/cv/classification/resnext101_32x8d/pytorch/dataloader/dali_classification.py @@ -0,0 +1,121 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +import os + +import nvidia.dali.ops as ops +import nvidia.dali.types as types +from nvidia.dali.pipeline import Pipeline +from nvidia.dali.plugin.pytorch import DALIClassificationIterator, DALIGenericIterator + +class HybridTrainPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridTrainPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=True) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.RandomResizedCrop(device="gpu", size=size, random_area=[0.08, 1.25]) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +class HybridValPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridValPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=False) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.Resize(device="gpu", resize_x=size, resize_y=size) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + crop=(size, size), + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +def get_imagenet_iter_dali(type, image_dir, batch_size, num_threads, device_id, size): + if type == 'train': + pip_train = HybridTrainPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "train"), + size=size) + pip_train.build() + dali_iter_train = DALIClassificationIterator(pip_train, size=pip_train.epoch_size("Reader")) + return dali_iter_train + elif type == 'val': + pip_val = HybridValPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "val"), + size=size) + pip_val.build() + dali_iter_val = DALIClassificationIterator(pip_val, size=pip_val.epoch_size("Reader")) + return dali_iter_val + + +def main(arguments): + parser = argparse.ArgumentParser() + parser.add_argument('--data_dir', help='directory to save data to', type=str, default='classification data') + args = parser.parse_args(arguments) + + train_loader = get_imagenet_iter_dali(type='train', image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + val_loader = get_imagenet_iter_dali(type="val", image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + print('start dali train dataloader.') + start = time.time() + for epoch in range(20): + for i, data in enumerate(train_loader): + images = data[0]["data"].cuda(non_blocking=True) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + + # WARN: Very important + train_loader.reset() + print("Epoch", epoch) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali train dataloader.') + + + print('start dali val dataloader.') + start = time.time() + for i, data in enumerate(val_loader): + images = data[0]["data"].cuda(non_blocking=True) + print(images.shape) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + print(labels.shape) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali val dataloader.') + + +if __name__ == '__main__': + import os, time, sys + import argparse + sys.exit(main(sys.argv[1:])) \ No newline at end of file diff --git a/cv/classification/resnext101_32x8d/pytorch/dataloader/utils/__init__.py b/cv/classification/resnext101_32x8d/pytorch/dataloader/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/classification/resnext101_32x8d/pytorch/dataloader/utils/presets_classification.py b/cv/classification/resnext101_32x8d/pytorch/dataloader/utils/presets_classification.py new file mode 100644 index 000000000..b3f559af4 --- /dev/null +++ b/cv/classification/resnext101_32x8d/pytorch/dataloader/utils/presets_classification.py @@ -0,0 +1,41 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from torchvision.transforms import autoaugment, transforms + + +class ClassificationPresetTrain: + def __init__(self, crop_size, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225), hflip_prob=0.5, + auto_augment_policy=None, random_erase_prob=0.0): + trans = [transforms.RandomResizedCrop(crop_size)] + if hflip_prob > 0: + trans.append(transforms.RandomHorizontalFlip(hflip_prob)) + if auto_augment_policy is not None: + aa_policy = autoaugment.AutoAugmentPolicy(auto_augment_policy) + trans.append(autoaugment.AutoAugment(policy=aa_policy)) + trans.extend([ + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ]) + if random_erase_prob > 0: + trans.append(transforms.RandomErasing(p=random_erase_prob)) + + self.transforms = transforms.Compose(trans) + + def __call__(self, img): + return self.transforms(img) + + +class ClassificationPresetEval: + def __init__(self, crop_size, resize_size=256, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)): + + self.transforms = transforms.Compose([ + transforms.Resize(resize_size), + transforms.CenterCrop(crop_size), + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ]) + + def __call__(self, img): + return self.transforms(img) diff --git a/cv/classification/resnext101_32x8d/pytorch/requirements.txt b/cv/classification/resnext101_32x8d/pytorch/requirements.txt index 61d5792cb..7321845b9 100644 --- a/cv/classification/resnext101_32x8d/pytorch/requirements.txt +++ b/cv/classification/resnext101_32x8d/pytorch/requirements.txt @@ -1,3 +1 @@ -torch -torchvision ---extra-index-url https://developer.download.nvidia.com/compute/redist/ nvidia-dali-cuda102==1.6.0 +numpy>=1.23.5 \ No newline at end of file diff --git a/cv/classification/resnext101_32x8d/pytorch/resnet.py b/cv/classification/resnext101_32x8d/pytorch/resnet.py deleted file mode 100644 index 8e137145a..000000000 --- a/cv/classification/resnext101_32x8d/pytorch/resnet.py +++ /dev/null @@ -1,785 +0,0 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright Declaration: This software, including all of its code and documentation, -# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX -# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright -# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar -# CoreX. No user of this software shall have any right, ownership or interest in this software and -# any use of this software shall be in compliance with the terms and conditions of the End User -# License Agreement. -""" - ResNet for ImageNet-1K, implemented in PyTorch. - Original paper: 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. -""" - -__all__ = ['ResNet', 'resnet10', 'resnet12', 'resnet14', 'resnetbc14b', 'resnet16', 'resnet18_wd4', 'resnet18_wd2', - 'resnet18_w3d4', 'resnet18', 'resnet26', 'resnetbc26b', 'resnet34', 'resnetbc38b', 'resnet50', 'resnet50b', - 'resnet101', 'resnet101b', 'resnet152', 'resnet152b', 'resnet200', 'resnet200b', 'ResBlock', 'ResBottleneck', - 'ResUnit', 'ResInitBlock'] - -import os -import torch.nn as nn -from common import conv1x1_block, conv3x3_block, conv7x7_block - - -class ResBlock(nn.Module): - """ - Simple ResNet block for residual path in ResNet unit. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the convolution. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - """ - def __init__(self, - in_channels, - out_channels, - stride, - bias=False, - use_bn=True): - super(ResBlock, self).__init__() - self.conv1 = conv3x3_block( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - bias=bias, - use_bn=use_bn) - self.conv2 = conv3x3_block( - in_channels=out_channels, - out_channels=out_channels, - bias=bias, - use_bn=use_bn, - activation=None) - - def forward(self, x): - x = self.conv1(x) - x = self.conv2(x) - return x - - -class ResBottleneck(nn.Module): - """ - ResNet bottleneck block for residual path in ResNet unit. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for the second convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for the second convolution layer. - conv1_stride : bool, default False - Whether to use stride in the first or the second convolution layer of the block. - bottleneck_factor : int, default 4 - Bottleneck factor. - """ - def __init__(self, - in_channels, - out_channels, - stride, - padding=1, - dilation=1, - conv1_stride=False, - bottleneck_factor=4): - super(ResBottleneck, self).__init__() - mid_channels = out_channels // bottleneck_factor - - self.conv1 = conv1x1_block( - in_channels=in_channels, - out_channels=mid_channels, - stride=(stride if conv1_stride else 1)) - self.conv2 = conv3x3_block( - in_channels=mid_channels, - out_channels=mid_channels, - stride=(1 if conv1_stride else stride), - padding=padding, - dilation=dilation) - self.conv3 = conv1x1_block( - in_channels=mid_channels, - out_channels=out_channels, - activation=None) - - def forward(self, x): - x = self.conv1(x) - x = self.conv2(x) - x = self.conv3(x) - return x - - -class ResUnit(nn.Module): - """ - ResNet unit with residual connection. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for the second convolution layer in bottleneck. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for the second convolution layer in bottleneck. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bottleneck : bool, default True - Whether to use a bottleneck or simple block in units. - conv1_stride : bool, default False - Whether to use stride in the first or the second convolution layer of the block. - """ - def __init__(self, - in_channels, - out_channels, - stride, - padding=1, - dilation=1, - bias=False, - use_bn=True, - bottleneck=True, - conv1_stride=False): - super(ResUnit, self).__init__() - self.resize_identity = (in_channels != out_channels) or (stride != 1) - - if bottleneck: - self.body = ResBottleneck( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - padding=padding, - dilation=dilation, - conv1_stride=conv1_stride) - else: - self.body = ResBlock( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - bias=bias, - use_bn=use_bn) - if self.resize_identity: - self.identity_conv = conv1x1_block( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - bias=bias, - use_bn=use_bn, - activation=None) - self.activ = nn.ReLU(inplace=True) - - def forward(self, x): - if self.resize_identity: - identity = self.identity_conv(x) - else: - identity = x - x = self.body(x) - x = x + identity - x = self.activ(x) - return x - - -class ResInitBlock(nn.Module): - """ - ResNet specific initial block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - """ - def __init__(self, - in_channels, - out_channels): - super(ResInitBlock, self).__init__() - self.conv = conv7x7_block( - in_channels=in_channels, - out_channels=out_channels, - stride=2) - self.pool = nn.MaxPool2d( - kernel_size=3, - stride=2, - padding=1) - - def forward(self, x): - x = self.conv(x) - x = self.pool(x) - return x - - -class ResNet(nn.Module): - """ - ResNet model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - channels : list of list of int - Number of output channels for each unit. - init_block_channels : int - Number of output channels for the initial unit. - bottleneck : bool - Whether to use a bottleneck or simple block in units. - conv1_stride : bool - Whether to use stride in the first or the second convolution layer in units. - in_channels : int, default 3 - Number of input channels. - in_size : tuple of two ints, default (224, 224) - Spatial size of the expected input image. - num_classes : int, default 1000 - Number of classification classes. - """ - def __init__(self, - channels, - init_block_channels, - bottleneck, - conv1_stride, - in_channels=3, - in_size=(224, 224), - num_classes=1000): - super(ResNet, self).__init__() - self.in_size = in_size - self.num_classes = num_classes - - self.features = nn.Sequential() - self.features.add_module("init_block", ResInitBlock( - in_channels=in_channels, - out_channels=init_block_channels)) - in_channels = init_block_channels - for i, channels_per_stage in enumerate(channels): - stage = nn.Sequential() - for j, out_channels in enumerate(channels_per_stage): - stride = 2 if (j == 0) and (i != 0) else 1 - stage.add_module("unit{}".format(j + 1), ResUnit( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - bottleneck=bottleneck, - conv1_stride=conv1_stride)) - in_channels = out_channels - self.features.add_module("stage{}".format(i + 1), stage) - self.features.add_module("final_pool", nn.AvgPool2d( - kernel_size=7, - stride=1)) - - self.output = nn.Linear( - in_features=in_channels, - out_features=num_classes) - - self._init_params() - - def _init_params(self): - for name, module in self.named_modules(): - if isinstance(module, nn.Conv2d): - nn.init.kaiming_uniform_(module.weight) - if module.bias is not None: - nn.init.constant_(module.bias, 0) - - def forward(self, x): - x = self.features(x) - x = x.view(x.size(0), -1) - x = self.output(x) - return x - - -def get_resnet(blocks, - bottleneck=None, - conv1_stride=True, - width_scale=1.0, - model_name=None, - pretrained=False, - root=os.path.join("~", ".torch", "models"), - **kwargs): - """ - Create ResNet model with specific parameters. - - Parameters: - ---------- - blocks : int - Number of blocks. - bottleneck : bool, default None - Whether to use a bottleneck or simple block in units. - conv1_stride : bool, default True - Whether to use stride in the first or the second convolution layer in units. - width_scale : float, default 1.0 - Scale factor for width of layers. - model_name : str or None, default None - Model name for loading pretrained model. - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - if bottleneck is None: - bottleneck = (blocks >= 50) - - if blocks == 10: - layers = [1, 1, 1, 1] - elif blocks == 12: - layers = [2, 1, 1, 1] - elif blocks == 14 and not bottleneck: - layers = [2, 2, 1, 1] - elif (blocks == 14) and bottleneck: - layers = [1, 1, 1, 1] - elif blocks == 16: - layers = [2, 2, 2, 1] - elif blocks == 18: - layers = [2, 2, 2, 2] - elif (blocks == 26) and not bottleneck: - layers = [3, 3, 3, 3] - elif (blocks == 26) and bottleneck: - layers = [2, 2, 2, 2] - elif blocks == 34: - layers = [3, 4, 6, 3] - elif (blocks == 38) and bottleneck: - layers = [3, 3, 3, 3] - elif blocks == 50: - layers = [3, 4, 6, 3] - elif blocks == 101: - layers = [3, 4, 23, 3] - elif blocks == 152: - layers = [3, 8, 36, 3] - elif blocks == 200: - layers = [3, 24, 36, 3] - else: - raise ValueError("Unsupported ResNet with number of blocks: {}".format(blocks)) - - if bottleneck: - assert (sum(layers) * 3 + 2 == blocks) - else: - assert (sum(layers) * 2 + 2 == blocks) - - init_block_channels = 64 - channels_per_layers = [64, 128, 256, 512] - - if bottleneck: - bottleneck_factor = 4 - channels_per_layers = [ci * bottleneck_factor for ci in channels_per_layers] - - channels = [[ci] * li for (ci, li) in zip(channels_per_layers, layers)] - - if width_scale != 1.0: - channels = [[int(cij * width_scale) if (i != len(channels) - 1) or (j != len(ci) - 1) else cij - for j, cij in enumerate(ci)] for i, ci in enumerate(channels)] - init_block_channels = int(init_block_channels * width_scale) - - net = ResNet( - channels=channels, - init_block_channels=init_block_channels, - bottleneck=bottleneck, - conv1_stride=conv1_stride, - **kwargs) - - if pretrained: - if (model_name is None) or (not model_name): - raise ValueError("Parameter `model_name` should be properly initialized for loading pretrained model.") - from .model_store import download_model - download_model( - net=net, - model_name=model_name, - local_model_store_dir_path=root) - - return net - - -def resnet10(**kwargs): - """ - ResNet-10 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=10, model_name="resnet10", **kwargs) - - -def resnet12(**kwargs): - """ - ResNet-12 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=12, model_name="resnet12", **kwargs) - - -def resnet14(**kwargs): - """ - ResNet-14 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=14, model_name="resnet14", **kwargs) - - -def resnetbc14b(**kwargs): - """ - ResNet-BC-14b model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model (bottleneck compressed). - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=14, bottleneck=True, conv1_stride=False, model_name="resnetbc14b", **kwargs) - - -def resnet16(**kwargs): - """ - ResNet-16 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=16, model_name="resnet16", **kwargs) - - -def resnet18_wd4(**kwargs): - """ - ResNet-18 model with 0.25 width scale from 'Deep Residual Learning for Image Recognition,' - https://arxiv.org/abs/1512.03385. It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=18, width_scale=0.25, model_name="resnet18_wd4", **kwargs) - - -def resnet18_wd2(**kwargs): - """ - ResNet-18 model with 0.5 width scale from 'Deep Residual Learning for Image Recognition,' - https://arxiv.org/abs/1512.03385. It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=18, width_scale=0.5, model_name="resnet18_wd2", **kwargs) - - -def resnet18_w3d4(**kwargs): - """ - ResNet-18 model with 0.75 width scale from 'Deep Residual Learning for Image Recognition,' - https://arxiv.org/abs/1512.03385. It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=18, width_scale=0.75, model_name="resnet18_w3d4", **kwargs) - - -def resnet18(**kwargs): - """ - ResNet-18 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=18, model_name="resnet18", **kwargs) - - -def resnet26(**kwargs): - """ - ResNet-26 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=26, bottleneck=False, model_name="resnet26", **kwargs) - - -def resnetbc26b(**kwargs): - """ - ResNet-BC-26b model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model (bottleneck compressed). - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=26, bottleneck=True, conv1_stride=False, model_name="resnetbc26b", **kwargs) - - -def resnet34(**kwargs): - """ - ResNet-34 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=34, model_name="resnet34", **kwargs) - - -def resnetbc38b(**kwargs): - """ - ResNet-BC-38b model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model (bottleneck compressed). - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=38, bottleneck=True, conv1_stride=False, model_name="resnetbc38b", **kwargs) - - -def resnet50(**kwargs): - """ - ResNet-50 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=50, model_name="resnet50", **kwargs) - - -def resnet50b(**kwargs): - """ - ResNet-50 model with stride at the second convolution in bottleneck block from 'Deep Residual Learning for Image - Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=50, conv1_stride=False, model_name="resnet50b", **kwargs) - - -def resnet101(**kwargs): - """ - ResNet-101 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=101, model_name="resnet101", **kwargs) - - -def resnet101b(**kwargs): - """ - ResNet-101 model with stride at the second convolution in bottleneck block from 'Deep Residual Learning for Image - Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=101, conv1_stride=False, model_name="resnet101b", **kwargs) - - -def resnet152(**kwargs): - """ - ResNet-152 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=152, model_name="resnet152", **kwargs) - - -def resnet152b(**kwargs): - """ - ResNet-152 model with stride at the second convolution in bottleneck block from 'Deep Residual Learning for Image - Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=152, conv1_stride=False, model_name="resnet152b", **kwargs) - - -def resnet200(**kwargs): - """ - ResNet-200 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=200, model_name="resnet200", **kwargs) - - -def resnet200b(**kwargs): - """ - ResNet-200 model with stride at the second convolution in bottleneck block from 'Deep Residual Learning for Image - Recognition,' https://arxiv.org/abs/1512.03385. It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=200, conv1_stride=False, model_name="resnet200b", **kwargs) - - -def _calc_width(net): - import numpy as np - net_params = filter(lambda p: p.requires_grad, net.parameters()) - weight_count = 0 - for param in net_params: - weight_count += np.prod(param.size()) - return weight_count - - -def _test(): - import torch - - pretrained = False - - models = [ - resnet10, - resnet12, - resnet14, - resnetbc14b, - resnet16, - resnet18_wd4, - resnet18_wd2, - resnet18_w3d4, - resnet18, - resnet26, - resnetbc26b, - resnet34, - resnetbc38b, - resnet50, - resnet50b, - resnet101, - resnet101b, - resnet152, - resnet152b, - resnet200, - resnet200b, - ] - - for model in models: - - net = model(pretrained=pretrained) - - # net.train() - net.eval() - weight_count = _calc_width(net) - print("m={}, {}".format(model.__name__, weight_count)) - assert (model != resnet10 or weight_count == 5418792) - assert (model != resnet12 or weight_count == 5492776) - assert (model != resnet14 or weight_count == 5788200) - assert (model != resnetbc14b or weight_count == 10064936) - assert (model != resnet16 or weight_count == 6968872) - assert (model != resnet18_wd4 or weight_count == 3937400) - assert (model != resnet18_wd2 or weight_count == 5804296) - assert (model != resnet18_w3d4 or weight_count == 8476056) - assert (model != resnet18 or weight_count == 11689512) - assert (model != resnet26 or weight_count == 17960232) - assert (model != resnetbc26b or weight_count == 15995176) - assert (model != resnet34 or weight_count == 21797672) - assert (model != resnetbc38b or weight_count == 21925416) - assert (model != resnet50 or weight_count == 25557032) - assert (model != resnet50b or weight_count == 25557032) - assert (model != resnet101 or weight_count == 44549160) - assert (model != resnet101b or weight_count == 44549160) - assert (model != resnet152 or weight_count == 60192808) - assert (model != resnet152b or weight_count == 60192808) - assert (model != resnet200 or weight_count == 64673832) - assert (model != resnet200b or weight_count == 64673832) - - batch = 4 - x = torch.randn(batch, 3, 224, 224) - y = net(x) - y.sum().backward() - assert (tuple(y.size()) == (batch, 1000)) - - -if __name__ == "__main__": - _test() diff --git a/cv/classification/resnext101_32x8d/pytorch/resnext.py b/cv/classification/resnext101_32x8d/pytorch/resnext.py deleted file mode 100644 index 2f677a920..000000000 --- a/cv/classification/resnext101_32x8d/pytorch/resnext.py +++ /dev/null @@ -1,470 +0,0 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright Declaration: This software, including all of its code and documentation, -# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX -# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright -# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar -# CoreX. No user of this software shall have any right, ownership or interest in this software and -# any use of this software shall be in compliance with the terms and conditions of the End User -# License Agreement. -""" - ResNeXt for ImageNet-1K, implemented in PyTorch. - Original paper: 'Aggregated Residual Transformations for Deep Neural Networks,' http://arxiv.org/abs/1611.05431. -""" - -__all__ = ['ResNeXt', 'resnext14_16x4d', 'resnext14_32x2d', 'resnext14_32x4d', 'resnext26_16x4d', 'resnext26_32x2d', - 'resnext26_32x4d', 'resnext38_32x4d', 'resnext50_32x4d', 'resnext101_32x4d', 'resnext101_64x4d', - 'ResNeXtBottleneck', 'ResNeXtUnit'] - -import os -import math -import torch.nn as nn -import torch.nn.init as init -from common import conv1x1_block, conv3x3_block -from resnet import ResInitBlock - - -class ResNeXtBottleneck(nn.Module): - """ - ResNeXt bottleneck block for residual path in ResNeXt unit. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the convolution. - cardinality: int - Number of groups. - bottleneck_width: int - Width of bottleneck block. - bottleneck_factor : int, default 4 - Bottleneck factor. - """ - def __init__(self, - in_channels, - out_channels, - stride, - cardinality, - bottleneck_width, - bottleneck_factor=4): - super(ResNeXtBottleneck, self).__init__() - mid_channels = out_channels // bottleneck_factor - D = int(math.floor(mid_channels * (bottleneck_width / 64.0))) - group_width = cardinality * D - - self.conv1 = conv1x1_block( - in_channels=in_channels, - out_channels=group_width) - self.conv2 = conv3x3_block( - in_channels=group_width, - out_channels=group_width, - stride=stride, - groups=cardinality) - self.conv3 = conv1x1_block( - in_channels=group_width, - out_channels=out_channels, - activation=None) - - def forward(self, x): - x = self.conv1(x) - x = self.conv2(x) - x = self.conv3(x) - return x - - -class ResNeXtUnit(nn.Module): - """ - ResNeXt unit with residual connection. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the convolution. - cardinality: int - Number of groups. - bottleneck_width: int - Width of bottleneck block. - """ - def __init__(self, - in_channels, - out_channels, - stride, - cardinality, - bottleneck_width): - super(ResNeXtUnit, self).__init__() - self.resize_identity = (in_channels != out_channels) or (stride != 1) - - self.body = ResNeXtBottleneck( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - cardinality=cardinality, - bottleneck_width=bottleneck_width) - if self.resize_identity: - self.identity_conv = conv1x1_block( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - activation=None) - self.activ = nn.ReLU(inplace=True) - - def forward(self, x): - if self.resize_identity: - identity = self.identity_conv(x) - else: - identity = x - x = self.body(x) - x = x + identity - x = self.activ(x) - return x - - -class ResNeXt(nn.Module): - """ - ResNeXt model from 'Aggregated Residual Transformations for Deep Neural Networks,' http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - channels : list of list of int - Number of output channels for each unit. - init_block_channels : int - Number of output channels for the initial unit. - cardinality: int - Number of groups. - bottleneck_width: int - Width of bottleneck block. - in_channels : int, default 3 - Number of input channels. - in_size : tuple of two ints, default (224, 224) - Spatial size of the expected input image. - num_classes : int, default 1000 - Number of classification classes. - """ - def __init__(self, - channels, - init_block_channels, - cardinality, - bottleneck_width, - in_channels=3, - in_size=(224, 224), - num_classes=1000): - super(ResNeXt, self).__init__() - self.in_size = in_size - self.num_classes = num_classes - - self.features = nn.Sequential() - self.features.add_module("init_block", ResInitBlock( - in_channels=in_channels, - out_channels=init_block_channels)) - in_channels = init_block_channels - for i, channels_per_stage in enumerate(channels): - stage = nn.Sequential() - for j, out_channels in enumerate(channels_per_stage): - stride = 2 if (j == 0) and (i != 0) else 1 - stage.add_module("unit{}".format(j + 1), ResNeXtUnit( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - cardinality=cardinality, - bottleneck_width=bottleneck_width)) - in_channels = out_channels - self.features.add_module("stage{}".format(i + 1), stage) - self.features.add_module("final_pool", nn.AvgPool2d( - kernel_size=7, - stride=1)) - - self.output = nn.Linear( - in_features=in_channels, - out_features=num_classes) - - self._init_params() - - def _init_params(self): - for name, module in self.named_modules(): - if isinstance(module, nn.Conv2d): - init.kaiming_uniform_(module.weight) - if module.bias is not None: - init.constant_(module.bias, 0) - - def forward(self, x): - x = self.features(x) - x = x.view(x.size(0), -1) - x = self.output(x) - return x - - -def get_resnext(blocks, - cardinality, - bottleneck_width, - model_name=None, - pretrained=False, - root=os.path.join("~", ".torch", "models"), - **kwargs): - """ - Create ResNeXt model with specific parameters. - - Parameters: - ---------- - blocks : int - Number of blocks. - cardinality: int - Number of groups. - bottleneck_width: int - Width of bottleneck block. - model_name : str or None, default None - Model name for loading pretrained model. - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - - if blocks == 14: - layers = [1, 1, 1, 1] - elif blocks == 26: - layers = [2, 2, 2, 2] - elif blocks == 38: - layers = [3, 3, 3, 3] - elif blocks == 50: - layers = [3, 4, 6, 3] - elif blocks == 101: - layers = [3, 4, 23, 3] - else: - raise ValueError("Unsupported ResNeXt with number of blocks: {}".format(blocks)) - - assert (sum(layers) * 3 + 2 == blocks) - - init_block_channels = 64 - channels_per_layers = [256, 512, 1024, 2048] - - channels = [[ci] * li for (ci, li) in zip(channels_per_layers, layers)] - - net = ResNeXt( - channels=channels, - init_block_channels=init_block_channels, - cardinality=cardinality, - bottleneck_width=bottleneck_width, - **kwargs) - - if pretrained: - if (model_name is None) or (not model_name): - raise ValueError("Parameter `model_name` should be properly initialized for loading pretrained model.") - from .model_store import download_model - download_model( - net=net, - model_name=model_name, - local_model_store_dir_path=root) - - return net - - -def resnext14_16x4d(**kwargs): - """ - ResNeXt-14 (16x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=14, cardinality=16, bottleneck_width=4, model_name="resnext14_16x4d", **kwargs) - - -def resnext14_32x2d(**kwargs): - """ - ResNeXt-14 (32x2d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=14, cardinality=32, bottleneck_width=2, model_name="resnext14_32x2d", **kwargs) - - -def resnext14_32x4d(**kwargs): - """ - ResNeXt-14 (32x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=14, cardinality=32, bottleneck_width=4, model_name="resnext14_32x4d", **kwargs) - - -def resnext26_16x4d(**kwargs): - """ - ResNeXt-26 (16x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=26, cardinality=16, bottleneck_width=4, model_name="resnext26_16x4d", **kwargs) - - -def resnext26_32x2d(**kwargs): - """ - ResNeXt-26 (32x2d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=26, cardinality=32, bottleneck_width=2, model_name="resnext26_32x2d", **kwargs) - - -def resnext26_32x4d(**kwargs): - """ - ResNeXt-26 (32x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=26, cardinality=32, bottleneck_width=4, model_name="resnext26_32x4d", **kwargs) - - -def resnext38_32x4d(**kwargs): - """ - ResNeXt-38 (32x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=38, cardinality=32, bottleneck_width=4, model_name="resnext38_32x4d", **kwargs) - - -def resnext50_32x4d(**kwargs): - """ - ResNeXt-50 (32x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=50, cardinality=32, bottleneck_width=4, model_name="resnext50_32x4d", **kwargs) - - -def resnext101_32x4d(**kwargs): - """ - ResNeXt-101 (32x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=101, cardinality=32, bottleneck_width=4, model_name="resnext101_32x4d", **kwargs) - - -def resnext101_64x4d(**kwargs): - """ - ResNeXt-101 (64x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=101, cardinality=64, bottleneck_width=4, model_name="resnext101_64x4d", **kwargs) - - -def _calc_width(net): - import numpy as np - net_params = filter(lambda p: p.requires_grad, net.parameters()) - weight_count = 0 - for param in net_params: - weight_count += np.prod(param.size()) - return weight_count - - -def _test(): - import torch - - pretrained = False - - models = [ - resnext14_16x4d, - resnext14_32x2d, - resnext14_32x4d, - resnext26_16x4d, - resnext26_32x2d, - resnext26_32x4d, - resnext38_32x4d, - resnext50_32x4d, - resnext101_32x4d, - resnext101_64x4d, - ] - - for model in models: - - net = model(pretrained=pretrained) - - # net.train() - net.eval() - weight_count = _calc_width(net) - print("m={}, {}".format(model.__name__, weight_count)) - assert (model != resnext14_16x4d or weight_count == 7127336) - assert (model != resnext14_32x2d or weight_count == 7029416) - assert (model != resnext14_32x4d or weight_count == 9411880) - assert (model != resnext26_16x4d or weight_count == 10119976) - assert (model != resnext26_32x2d or weight_count == 9924136) - assert (model != resnext26_32x4d or weight_count == 15389480) - assert (model != resnext38_32x4d or weight_count == 21367080) - assert (model != resnext50_32x4d or weight_count == 25028904) - assert (model != resnext101_32x4d or weight_count == 44177704) - assert (model != resnext101_64x4d or weight_count == 83455272) - - x = torch.randn(1, 3, 224, 224) - y = net(x) - y.sum().backward() - assert (tuple(y.size()) == (1, 1000)) - - -if __name__ == "__main__": - _test() diff --git a/cv/classification/resnext101_32x8d/pytorch/run_train.py b/cv/classification/resnext101_32x8d/pytorch/run_train.py deleted file mode 100644 index 7fde4058f..000000000 --- a/cv/classification/resnext101_32x8d/pytorch/run_train.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -import os -import sys - -import torchvision - -sys.path.append("../../torchvision/pytorch") - -from train import train_model -from utils import padding_conv_channel_to_4 - -def create_model(args): - model = torchvision.models.__dict__[args.model](pretrained=args.pretrained, num_classes=args.num_classes) - if args.nhwc: - args.padding_channel = True - model.conv1 = padding_conv_channel_to_4(model.conv1) - return model - - -train_model(create_model) diff --git a/cv/classification/resnext101_32x8d/pytorch/train.py b/cv/classification/resnext101_32x8d/pytorch/train.py new file mode 100644 index 000000000..78e8a72f6 --- /dev/null +++ b/cv/classification/resnext101_32x8d/pytorch/train.py @@ -0,0 +1,313 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +import datetime +import os + +import time +from typing_extensions import runtime + +import torch +import torch.utils.data + +try: + from torch.cuda.amp import autocast, GradScaler + scaler = GradScaler() +except: + autocast = None + scaler = None + + +from torch import nn +import torch.distributed as dist +import torchvision + +from utils_ import (MetricLogger, SmoothedValue, accuracy, mkdir,\ + init_distributed_mode, manual_seed,\ + is_main_process, save_on_master, get_world_size) + +from dataloader.classification import get_datasets, create_dataloader + + +def compute_loss(model, image, target, criterion): + output = model(image) + if not isinstance(output, (tuple, list)): + output = [output] + losses = [] + for out in output: + losses.append(criterion(out, target)) + loss = sum(losses) + return loss, output[0] + + +def train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, print_freq, amp=False, use_dali=False): + model.train() + metric_logger = MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', SmoothedValue(window_size=1, fmt='{value}')) + metric_logger.add_meter('img/s', SmoothedValue(window_size=10, fmt='{value}')) + + header = 'Epoch: [{}]'.format(epoch) + all_fps = [] + for data in metric_logger.log_every(data_loader, print_freq, header): + if use_dali: + image, target = data[0]["data"], data[0]["label"][:, 0].long() + else: + image, target = data + start_time = time.time() + image, target = image.to(device), target.to(device) + if autocast is None or not amp: + loss, output = compute_loss(model, image, target, criterion) + else: + with autocast(): + loss, output = compute_loss(model, image, target, criterion) + + if torch.any(torch.isnan(loss)): + print("[Error] train loss is nan ") + raise RuntimeError + + optimizer.zero_grad() + if scaler is not None and amp: + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + else: + loss.backward() + optimizer.step() + + torch.cuda.synchronize() + end_time = time.time() + + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + batch_size = image.shape[0] + metric_logger.update(loss=loss.item(), lr=optimizer.param_groups[0]["lr"]) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + fps = batch_size / (end_time - start_time) * get_world_size() + metric_logger.meters['img/s'].update(fps) + all_fps.append(fps) + + print(header, 'Avg img/s:', sum(all_fps) / len(all_fps)) + + +def evaluate(model, criterion, data_loader, device, print_freq=100, use_dali=False): + model.eval() + metric_logger = MetricLogger(delimiter=" ") + header = 'Test:' + with torch.no_grad(): + for data in metric_logger.log_every(data_loader, print_freq, header): + if use_dali: + image, target = data[0]["data"], data[0]["label"][:, 0].long() + else: + image, target = data + image = image.to(device, non_blocking=True) + target = target.to(device, non_blocking=True) + output = model(image) + loss = criterion(output, target) + + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + # FIXME need to take into account that the datasets + # could have been padded in distributed setup + batch_size = image.shape[0] + metric_logger.update(loss=loss.item()) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + # gather the stats from all processes + metric_logger.synchronize_between_processes() + + print(' * Acc@1 {top1.global_avg:.3f} Acc@5 {top5.global_avg:.3f}' + .format(top1=metric_logger.acc1, top5=metric_logger.acc5)) + return metric_logger.acc1.global_avg + + +def _get_cache_path(filepath): + import hashlib + h = hashlib.sha1(filepath.encode()).hexdigest() + cache_path = os.path.join("~", ".torch", "vision", "datasets", "imagefolder", h[:10] + ".pt") + cache_path = os.path.expanduser(cache_path) + return cache_path + + +def main(args): + if args.output_dir: + mkdir(args.output_dir) + + init_distributed_mode(args) + print(args) + + device = torch.device(args.device) + + manual_seed(args.seed, deterministic=False) + # torch.backends.cudnn.benchmark = True + + # WARN: + if dist.is_initialized(): + num_gpu = dist.get_world_size() + else: + num_gpu = 1 + + global_batch_size = num_gpu * args.batch_size + + train_dir = os.path.join(args.data_path, 'train') + val_dir = os.path.join(args.data_path, 'val') + + num_classes = len(os.listdir(train_dir)) + if 0 < num_classes < 13: + if global_batch_size > 512: + if is_main_process(): + print("WARN: Updating global batch size to 512, avoid non-convergence when training small dataset.") + args.batch_size = 512 // num_gpu + + if args.pretrained: + num_classes = 1000 + + data_loader, data_loader_test = create_dataloader(train_dir, val_dir, args) + + print("Creating model") + model = torchvision.models.__dict__[args.model](pretrained=args.pretrained, num_classes=num_classes) + model.to(device) + if args.distributed and args.sync_bn: + model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model) + + criterion = nn.CrossEntropyLoss() + + opt_name = args.opt.lower() + if opt_name == 'sgd': + optimizer = torch.optim.SGD( + model.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.weight_decay) + elif opt_name == 'rmsprop': + optimizer = torch.optim.RMSprop(model.parameters(), lr=args.lr, momentum=args.momentum, + weight_decay=args.weight_decay, eps=0.0316, alpha=0.9) + else: + raise RuntimeError("Invalid optimizer {}. Only SGD and RMSprop are supported.".format(args.opt)) + + lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=args.lr_step_size, gamma=args.lr_gamma) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + if args.resume: + checkpoint = torch.load(args.resume, map_location='cpu') + model_without_ddp.load_state_dict(checkpoint['model']) + optimizer.load_state_dict(checkpoint['optimizer']) + lr_scheduler.load_state_dict(checkpoint['lr_scheduler']) + args.start_epoch = checkpoint['epoch'] + 1 + + if args.test_only: + evaluate(model, criterion, data_loader_test, device=device) + return + + print("Start training") + start_time = time.time() + best_acc = 0 + for epoch in range(args.start_epoch, args.epochs): + epoch_start_time = time.time() + if args.distributed and not args.dali: + data_loader.sampler.set_epoch(epoch) + train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, args.print_freq, args.amp, use_dali=args.dali) + lr_scheduler.step() + acc_avg = evaluate(model, criterion, data_loader_test, device=device, use_dali=args.dali) + if acc_avg > best_acc: + if args.output_dir: + checkpoint = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'lr_scheduler': lr_scheduler.state_dict(), + 'epoch': epoch, + 'args': args} + save_on_master( + checkpoint, + os.path.join(args.output_dir, 'model_best.pth')) + epoch_total_time = time.time() - epoch_start_time + epoch_total_time_str = str(datetime.timedelta(seconds=int(epoch_total_time))) + print('epoch time {}'.format(epoch_total_time_str)) + best_acc = acc_avg + + if args.dali: + data_loader.reset() + data_loader_test.reset() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +def get_args_parser(add_help=True): + import argparse + parser = argparse.ArgumentParser(description='PyTorch Classification Training', add_help=add_help) + + parser.add_argument('--data-path', default='/datasets01/imagenet_full_size/061417/', help='dataset') + parser.add_argument('--model', default='resnet18', help='model') + parser.add_argument('--device', default='cuda', help='device') + parser.add_argument('-b', '--batch-size', default=32, type=int) + parser.add_argument('--epochs', default=90, type=int, metavar='N', + help='number of total epochs to run') + parser.add_argument('-j', '--workers', default=4, type=int, metavar='N', + help='number of data loading workers (default: 4)') + parser.add_argument('--opt', default='sgd', type=str, help='optimizer') + parser.add_argument('--lr', default=0.1, type=float, help='initial learning rate') + parser.add_argument('--momentum', default=0.9, type=float, metavar='M', + help='momentum') + parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float, + metavar='W', help='weight decay (default: 1e-4)', + dest='weight_decay') + parser.add_argument('--lr-step-size', default=30, type=int, help='decrease lr every step-size epochs') + parser.add_argument('--lr-gamma', default=0.1, type=float, help='decrease lr by a factor of lr-gamma') + parser.add_argument('--print-freq', default=10, type=int, help='print frequency') + parser.add_argument('--output-dir', default='.', help='path where to save') + parser.add_argument('--resume', default='', help='resume from checkpoint') + parser.add_argument('--start-epoch', default=0, type=int, metavar='N', + help='start epoch') + parser.add_argument( + "--cache-dataset", + dest="cache_dataset", + help="Cache the datasets for quicker initialization. It also serializes the transforms", + action="store_true", + ) + parser.add_argument( + "--sync-bn", + dest="sync_bn", + help="Use sync batch norm", + action="store_true", + ) + parser.add_argument( + "--test-only", + dest="test_only", + help="Only test the model", + action="store_true", + ) + parser.add_argument( + "--pretrained", + dest="pretrained", + help="Use pre-trained models from the modelzoo", + action="store_true", + ) + parser.add_argument('--auto-augment', default=None, help='auto augment policy (default: None)') + parser.add_argument('--random-erase', default=0.0, type=float, help='random erasing probability (default: 0.0)') + parser.add_argument( + "--dali", + help="Use dali as dataloader", + default=False, + action="store_true", + ) + + # distributed training parameters + parser.add_argument('--local_rank', '--local-rank', default=-1, type=int, + help='Local rank') + parser.add_argument('--world-size', default=1, type=int, + help='number of distributed processes') + parser.add_argument('--dist-url', default='env://', help='url used to set up distributed training') + parser.add_argument('--amp', action='store_true', help='Automatic Mixed Precision training') + parser.add_argument('--seed', default=42, type=int, help='Random seed') + return parser + + +if __name__ == "__main__": + args = get_args_parser().parse_args() + try: + from dltest import show_training_arguments + show_training_arguments(args) + except: + pass + main(args) diff --git a/cv/classification/resnext101_32x8d/pytorch/train_resnext101_32x8d_amp_dist.sh b/cv/classification/resnext101_32x8d/pytorch/train_resnext101_32x8d_amp_dist.sh index 3238f5bc7..76dbaace5 100755 --- a/cv/classification/resnext101_32x8d/pytorch/train_resnext101_32x8d_amp_dist.sh +++ b/cv/classification/resnext101_32x8d/pytorch/train_resnext101_32x8d_amp_dist.sh @@ -30,7 +30,8 @@ if [ ! -z "${DEBUG}" ];then PYTHONAR="${PYTHONAR} -m pdb" fi cd ${ROOT_DIR} -python3 $PYTHONARG ${ROOT_DIR}/run_train.py \ - --model resnext101_32x8d --dali --dali-cpu --data-path $DATA_PATH \ - --opt fused_sgd --batch-size 128 \ - --deterministic --amp --nhwc "$@" +python3 $PYTHONARG train.py \ + --model resnext101_32x8d --data-path $DATA_PATH \ + --batch-size 64 \ + --lr 1e-2 --amp --wd 0.0001 "$@" + diff --git a/cv/classification/resnext101_32x8d/pytorch/utils_.py b/cv/classification/resnext101_32x8d/pytorch/utils_.py new file mode 100644 index 000000000..3d34c4df0 --- /dev/null +++ b/cv/classification/resnext101_32x8d/pytorch/utils_.py @@ -0,0 +1,156 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque, OrderedDict +import copy +import datetime +import hashlib +import time +import torch +import torch.distributed as dist + +import errno +import os + +from common_utils import * + + +def accuracy(output, target, topk=(1,)): + """Computes the accuracy over the k top predictions for the specified values of k""" + with torch.no_grad(): + maxk = max(topk) + batch_size = target.size(0) + + _, pred = output.topk(maxk, 1, True, True) + pred = pred.t() + correct = pred.eq(target[None]) + + res = [] + for k in topk: + correct_k = correct[:k].flatten().sum(dtype=torch.float32) + res.append(correct_k * (100.0 / batch_size)) + return res + + +def average_checkpoints(inputs): + """Loads checkpoints from inputs and returns a model with averaged weights. Original implementation taken from: + https://github.com/pytorch/fairseq/blob/a48f235636557b8d3bc4922a6fa90f3a0fa57955/scripts/average_checkpoints.py#L16 + + Args: + inputs (List[str]): An iterable of string paths of checkpoints to load from. + Returns: + A dict of string keys mapping to various values. The 'model' key + from the returned dict should correspond to an OrderedDict mapping + string parameter names to torch Tensors. + """ + params_dict = OrderedDict() + params_keys = None + new_state = None + num_models = len(inputs) + for fpath in inputs: + with open(fpath, "rb") as f: + state = torch.load( + f, + map_location=( + lambda s, _: torch.serialization.default_restore_location(s, "cpu") + ), + ) + # Copies over the settings from the first checkpoint + if new_state is None: + new_state = state + model_params = state["model"] + model_params_keys = list(model_params.keys()) + if params_keys is None: + params_keys = model_params_keys + elif params_keys != model_params_keys: + raise KeyError( + "For checkpoint {}, expected list of params: {}, " + "but found: {}".format(f, params_keys, model_params_keys) + ) + for k in params_keys: + p = model_params[k] + if isinstance(p, torch.HalfTensor): + p = p.float() + if k not in params_dict: + params_dict[k] = p.clone() + # NOTE: clone() is needed in case of p is a shared parameter + else: + params_dict[k] += p + averaged_params = OrderedDict() + for k, v in params_dict.items(): + averaged_params[k] = v + if averaged_params[k].is_floating_point(): + averaged_params[k].div_(num_models) + else: + averaged_params[k] //= num_models + new_state["model"] = averaged_params + return new_state + + +def store_model_weights(model, checkpoint_path, checkpoint_key='model', strict=True): + """ + This method can be used to prepare weights files for new models. It receives as + input a model architecture and a checkpoint from the training script and produces + a file with the weights ready for release. + + Examples: + from torchvision import models as M + + # Classification + model = M.mobilenet_v3_large(pretrained=False) + print(store_model_weights(model, './class.pth')) + + # Quantized Classification + model = M.quantization.mobilenet_v3_large(pretrained=False, quantize=False) + model.fuse_model() + model.qconfig = torch.quantization.get_default_qat_qconfig('qnnpack') + _ = torch.quantization.prepare_qat(model, inplace=True) + print(store_model_weights(model, './qat.pth')) + + # Object Detection + model = M.detection.fasterrcnn_mobilenet_v3_large_fpn(pretrained=False, pretrained_backbone=False) + print(store_model_weights(model, './obj.pth')) + + # Segmentation + model = M.segmentation.deeplabv3_mobilenet_v3_large(pretrained=False, pretrained_backbone=False, aux_loss=True) + print(store_model_weights(model, './segm.pth', strict=False)) + + Args: + model (pytorch.nn.Module): The model on which the weights will be loaded for validation purposes. + checkpoint_path (str): The path of the checkpoint we will load. + checkpoint_key (str, optional): The key of the checkpoint where the model weights are stored. + Default: "model". + strict (bool): whether to strictly enforce that the keys + in :attr:`state_dict` match the keys returned by this module's + :meth:`~torch.nn.Module.state_dict` function. Default: ``True`` + + Returns: + output_path (str): The location where the weights are saved. + """ + # Store the new model next to the checkpoint_path + checkpoint_path = os.path.abspath(checkpoint_path) + output_dir = os.path.dirname(checkpoint_path) + + # Deep copy to avoid side-effects on the model object. + model = copy.deepcopy(model) + checkpoint = torch.load(checkpoint_path, map_location='cpu') + + # Load the weights to the model to validate that everything works + # and remove unnecessary weights (such as auxiliaries, etc) + model.load_state_dict(checkpoint[checkpoint_key], strict=strict) + + tmp_path = os.path.join(output_dir, str(model.__hash__())) + torch.save(model.state_dict(), tmp_path) + + sha256_hash = hashlib.sha256() + with open(tmp_path, "rb") as f: + # Read and update hash string value in blocks of 4K + for byte_block in iter(lambda: f.read(4096), b""): + sha256_hash.update(byte_block) + hh = sha256_hash.hexdigest() + + output_path = os.path.join(output_dir, "weights-" + str(hh[:8]) + ".pth") + os.replace(tmp_path, output_path) + + return output_path diff --git a/cv/classification/resnext50_32x4d/pytorch/__init__.py b/cv/classification/resnext50_32x4d/pytorch/__init__.py new file mode 100644 index 000000000..6faec1658 --- /dev/null +++ b/cv/classification/resnext50_32x4d/pytorch/__init__.py @@ -0,0 +1,5 @@ +from .utils import * +from .common_utils import * +from .data_loader import * + +__all__ = [k for k in globals().keys() if not k.startswith("_")] diff --git a/cv/classification/resnext50_32x4d/pytorch/common.py b/cv/classification/resnext50_32x4d/pytorch/common.py deleted file mode 100644 index 34b391641..000000000 --- a/cv/classification/resnext50_32x4d/pytorch/common.py +++ /dev/null @@ -1,2338 +0,0 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright Declaration: This software, including all of its code and documentation, -# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX -# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright -# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar -# CoreX. No user of this software shall have any right, ownership or interest in this software and -# any use of this software shall be in compliance with the terms and conditions of the End User -# License Agreement. -""" - Common routines for models in PyTorch. -""" - -__all__ = ['round_channels', 'Identity', 'BreakBlock', 'Swish', 'HSigmoid', 'HSwish', 'get_activation_layer', - 'SelectableDense', 'DenseBlock', 'ConvBlock1d', 'conv1x1', 'conv3x3', 'depthwise_conv3x3', 'ConvBlock', - 'conv1x1_block', 'conv3x3_block', 'conv5x5_block', 'conv7x7_block', 'dwconv_block', 'dwconv3x3_block', - 'dwconv5x5_block', 'dwsconv3x3_block', 'PreConvBlock', 'pre_conv1x1_block', 'pre_conv3x3_block', - 'AsymConvBlock', 'asym_conv3x3_block', 'DeconvBlock', 'deconv3x3_block', 'NormActivation', - 'InterpolationBlock', 'ChannelShuffle', 'ChannelShuffle2', 'SEBlock', 'SABlock', 'SAConvBlock', - 'saconv3x3_block', 'DucBlock', 'IBN', 'DualPathSequential', 'Concurrent', 'SequentialConcurrent', - 'ParametricSequential', 'ParametricConcurrent', 'Hourglass', 'SesquialteralHourglass', - 'MultiOutputSequential', 'ParallelConcurent', 'DualPathParallelConcurent', 'Flatten', 'HeatmapMaxDetBlock'] - -import math -from inspect import isfunction -import torch -import torch.nn as nn -import torch.nn.functional as F -from torch.nn.parameter import Parameter - - -def round_channels(channels, - divisor=8): - """ - Round weighted channel number (make divisible operation). - - Parameters: - ---------- - channels : int or float - Original number of channels. - divisor : int, default 8 - Alignment value. - - Returns: - ------- - int - Weighted number of channels. - """ - rounded_channels = max(int(channels + divisor / 2.0) // divisor * divisor, divisor) - if float(rounded_channels) < 0.9 * channels: - rounded_channels += divisor - return rounded_channels - - -class Identity(nn.Module): - """ - Identity block. - """ - def __init__(self): - super(Identity, self).__init__() - - def forward(self, x): - return x - - def __repr__(self): - return '{name}()'.format(name=self.__class__.__name__) - - -class BreakBlock(nn.Module): - """ - Break coonnection block for hourglass. - """ - def __init__(self): - super(BreakBlock, self).__init__() - - def forward(self, x): - return None - - def __repr__(self): - return '{name}()'.format(name=self.__class__.__name__) - - -class Swish(nn.Module): - """ - Swish activation function from 'Searching for Activation Functions,' https://arxiv.org/abs/1710.05941. - """ - def forward(self, x): - return x * torch.sigmoid(x) - - -class HSigmoid(nn.Module): - """ - Approximated sigmoid function, so-called hard-version of sigmoid from 'Searching for MobileNetV3,' - https://arxiv.org/abs/1905.02244. - """ - def forward(self, x): - return F.relu6(x + 3.0, inplace=True) / 6.0 - - -class HSwish(nn.Module): - """ - H-Swish activation function from 'Searching for MobileNetV3,' https://arxiv.org/abs/1905.02244. - - Parameters: - ---------- - inplace : bool - Whether to use inplace version of the module. - """ - def __init__(self, inplace=False): - super(HSwish, self).__init__() - self.inplace = inplace - - def forward(self, x): - return x * F.relu6(x + 3.0, inplace=self.inplace) / 6.0 - - -def get_activation_layer(activation): - """ - Create activation layer from string/function. - - Parameters: - ---------- - activation : function, or str, or nn.Module - Activation function or name of activation function. - - Returns: - ------- - nn.Module - Activation layer. - """ - assert (activation is not None) - if isfunction(activation): - return activation() - elif isinstance(activation, str): - if activation == "relu": - return nn.ReLU(inplace=True) - elif activation == "relu6": - return nn.ReLU6(inplace=True) - elif activation == "swish": - return Swish() - elif activation == "hswish": - return HSwish(inplace=True) - elif activation == "sigmoid": - return nn.Sigmoid() - elif activation == "hsigmoid": - return HSigmoid() - elif activation == "identity": - return Identity() - else: - raise NotImplementedError() - else: - assert (isinstance(activation, nn.Module)) - return activation - - -class SelectableDense(nn.Module): - """ - Selectable dense layer. - - Parameters: - ---------- - in_features : int - Number of input features. - out_features : int - Number of output features. - bias : bool, default False - Whether the layer uses a bias vector. - num_options : int, default 1 - Number of selectable options. - """ - def __init__(self, - in_features, - out_features, - bias=False, - num_options=1): - super(SelectableDense, self).__init__() - self.in_features = in_features - self.out_features = out_features - self.use_bias = bias - self.num_options = num_options - self.weight = Parameter(torch.Tensor(num_options, out_features, in_features)) - if bias: - self.bias = Parameter(torch.Tensor(num_options, out_features)) - else: - self.register_parameter("bias", None) - - def forward(self, x, indices): - weight = torch.index_select(self.weight, dim=0, index=indices) - x = x.unsqueeze(-1) - x = weight.bmm(x) - x = x.squeeze(dim=-1) - if self.use_bias: - bias = torch.index_select(self.bias, dim=0, index=indices) - x += bias - return x - - def extra_repr(self): - return "in_features={}, out_features={}, bias={}, num_options={}".format( - self.in_features, self.out_features, self.use_bias, self.num_options) - - -class DenseBlock(nn.Module): - """ - Standard dense block with Batch normalization and activation. - - Parameters: - ---------- - in_features : int - Number of input features. - out_features : int - Number of output features. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - def __init__(self, - in_features, - out_features, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - super(DenseBlock, self).__init__() - self.activate = (activation is not None) - self.use_bn = use_bn - - self.fc = nn.Linear( - in_features=in_features, - out_features=out_features, - bias=bias) - if self.use_bn: - self.bn = nn.BatchNorm1d( - num_features=out_features, - eps=bn_eps) - if self.activate: - self.activ = get_activation_layer(activation) - - def forward(self, x): - x = self.fc(x) - if self.use_bn: - x = self.bn(x) - if self.activate: - x = self.activ(x) - return x - - -class ConvBlock1d(nn.Module): - """ - Standard 1D convolution block with Batch normalization and activation. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int - Convolution window size. - stride : int - Strides of the convolution. - padding : int - Padding value for convolution layer. - dilation : int - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - super(ConvBlock1d, self).__init__() - self.activate = (activation is not None) - self.use_bn = use_bn - - self.conv = nn.Conv1d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - if self.use_bn: - self.bn = nn.BatchNorm1d( - num_features=out_channels, - eps=bn_eps) - if self.activate: - self.activ = get_activation_layer(activation) - - def forward(self, x): - x = self.conv(x) - if self.use_bn: - x = self.bn(x) - if self.activate: - x = self.activ(x) - return x - - -def conv1x1(in_channels, - out_channels, - stride=1, - groups=1, - bias=False): - """ - Convolution 1x1 layer. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - """ - return nn.Conv2d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=1, - stride=stride, - groups=groups, - bias=bias) - - -def conv3x3(in_channels, - out_channels, - stride=1, - padding=1, - dilation=1, - groups=1, - bias=False): - """ - Convolution 3x3 layer. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - """ - return nn.Conv2d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - - -def depthwise_conv3x3(channels, - stride=1, - padding=1, - dilation=1, - bias=False): - """ - Depthwise convolution 3x3 layer. - - Parameters: - ---------- - channels : int - Number of input/output channels. - strides : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - """ - return nn.Conv2d( - in_channels=channels, - out_channels=channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - groups=channels, - bias=bias) - - -class ConvBlock(nn.Module): - """ - Standard convolution block with Batch normalization and activation. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - super(ConvBlock, self).__init__() - self.activate = (activation is not None) - self.use_bn = use_bn - self.use_pad = (isinstance(padding, (list, tuple)) and (len(padding) == 4)) - - if self.use_pad: - self.pad = nn.ZeroPad2d(padding=padding) - padding = 0 - self.conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - if self.use_bn: - self.bn = nn.BatchNorm2d( - num_features=out_channels, - eps=bn_eps) - if self.activate: - self.activ = get_activation_layer(activation) - - def forward(self, x): - if self.use_pad: - x = self.pad(x) - x = self.conv(x) - if self.use_bn: - x = self.bn(x) - if self.activate: - x = self.activ(x) - return x - - -def conv1x1_block(in_channels, - out_channels, - stride=1, - padding=0, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 1x1 version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 0 - Padding value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return ConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=1, - stride=stride, - padding=padding, - groups=groups, - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - - -def conv3x3_block(in_channels, - out_channels, - stride=1, - padding=1, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 3x3 version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return ConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - - -def conv5x5_block(in_channels, - out_channels, - stride=1, - padding=2, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 5x5 version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 2 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return ConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=5, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - - -def conv7x7_block(in_channels, - out_channels, - stride=1, - padding=3, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 7x7 version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 1 - Strides of the convolution. - padding : int or tuple/list of 2 int, default 3 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return ConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=7, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - - -def dwconv_block(in_channels, - out_channels, - kernel_size, - stride=1, - padding=1, - dilation=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - Depthwise version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return ConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=out_channels, - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - - -def dwconv3x3_block(in_channels, - out_channels, - stride=1, - padding=1, - dilation=1, - bias=False, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 3x3 depthwise version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return dwconv_block( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias, - bn_eps=bn_eps, - activation=activation) - - -def dwconv5x5_block(in_channels, - out_channels, - stride=1, - padding=2, - dilation=1, - bias=False, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 5x5 depthwise version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 2 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return dwconv_block( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=5, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias, - bn_eps=bn_eps, - activation=activation) - - -class DwsConvBlock(nn.Module): - """ - Depthwise separable convolution block with BatchNorms and activations at each convolution layers. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - dw_use_bn : bool, default True - Whether to use BatchNorm layer (depthwise convolution block). - pw_use_bn : bool, default True - Whether to use BatchNorm layer (pointwise convolution block). - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - dw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the depthwise convolution block. - pw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the pointwise convolution block. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - dilation=1, - bias=False, - dw_use_bn=True, - pw_use_bn=True, - bn_eps=1e-5, - dw_activation=(lambda: nn.ReLU(inplace=True)), - pw_activation=(lambda: nn.ReLU(inplace=True))): - super(DwsConvBlock, self).__init__() - self.dw_conv = dwconv_block( - in_channels=in_channels, - out_channels=in_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias, - use_bn=dw_use_bn, - bn_eps=bn_eps, - activation=dw_activation) - self.pw_conv = conv1x1_block( - in_channels=in_channels, - out_channels=out_channels, - bias=bias, - use_bn=pw_use_bn, - bn_eps=bn_eps, - activation=pw_activation) - - def forward(self, x): - x = self.dw_conv(x) - x = self.pw_conv(x) - return x - - -def dwsconv3x3_block(in_channels, - out_channels, - stride=1, - padding=1, - dilation=1, - bias=False, - bn_eps=1e-5, - dw_activation=(lambda: nn.ReLU(inplace=True)), - pw_activation=(lambda: nn.ReLU(inplace=True)), - **kwargs): - """ - 3x3 depthwise separable version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - dw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the depthwise convolution block. - pw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the pointwise convolution block. - """ - return DwsConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias, - bn_eps=bn_eps, - dw_activation=dw_activation, - pw_activation=pw_activation, - **kwargs) - - -class PreConvBlock(nn.Module): - """ - Convolution block with Batch normalization and ReLU pre-activation. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int or tuple/list of 2 int - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - return_preact : bool, default False - Whether return pre-activation. It's used by PreResNet. - activate : bool, default True - Whether activate the convolution block. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - dilation=1, - bias=False, - use_bn=True, - return_preact=False, - activate=True): - super(PreConvBlock, self).__init__() - self.return_preact = return_preact - self.activate = activate - self.use_bn = use_bn - - if self.use_bn: - self.bn = nn.BatchNorm2d(num_features=in_channels) - if self.activate: - self.activ = nn.ReLU(inplace=True) - self.conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - def forward(self, x): - if self.use_bn: - x = self.bn(x) - if self.activate: - x = self.activ(x) - if self.return_preact: - x_pre_activ = x - x = self.conv(x) - if self.return_preact: - return x, x_pre_activ - else: - return x - - -def pre_conv1x1_block(in_channels, - out_channels, - stride=1, - bias=False, - use_bn=True, - return_preact=False, - activate=True): - """ - 1x1 version of the pre-activated convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - return_preact : bool, default False - Whether return pre-activation. - activate : bool, default True - Whether activate the convolution block. - """ - return PreConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=1, - stride=stride, - padding=0, - bias=bias, - use_bn=use_bn, - return_preact=return_preact, - activate=activate) - - -def pre_conv3x3_block(in_channels, - out_channels, - stride=1, - padding=1, - dilation=1, - bias=False, - use_bn=True, - return_preact=False, - activate=True): - """ - 3x3 version of the pre-activated convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - return_preact : bool, default False - Whether return pre-activation. - activate : bool, default True - Whether activate the convolution block. - """ - return PreConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias, - use_bn=use_bn, - return_preact=return_preact, - activate=activate) - - -class AsymConvBlock(nn.Module): - """ - Asymmetric separable convolution block. - - Parameters: - ---------- - channels : int - Number of input/output channels. - kernel_size : int - Convolution window size. - padding : int - Padding value for convolution layer. - dilation : int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - lw_use_bn : bool, default True - Whether to use BatchNorm layer (leftwise convolution block). - rw_use_bn : bool, default True - Whether to use BatchNorm layer (rightwise convolution block). - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - lw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the leftwise convolution block. - rw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the rightwise convolution block. - """ - def __init__(self, - channels, - kernel_size, - padding, - dilation=1, - groups=1, - bias=False, - lw_use_bn=True, - rw_use_bn=True, - bn_eps=1e-5, - lw_activation=(lambda: nn.ReLU(inplace=True)), - rw_activation=(lambda: nn.ReLU(inplace=True))): - super(AsymConvBlock, self).__init__() - self.lw_conv = ConvBlock( - in_channels=channels, - out_channels=channels, - kernel_size=(kernel_size, 1), - stride=1, - padding=(padding, 0), - dilation=(dilation, 1), - groups=groups, - bias=bias, - use_bn=lw_use_bn, - bn_eps=bn_eps, - activation=lw_activation) - self.rw_conv = ConvBlock( - in_channels=channels, - out_channels=channels, - kernel_size=(1, kernel_size), - stride=1, - padding=(0, padding), - dilation=(1, dilation), - groups=groups, - bias=bias, - use_bn=rw_use_bn, - bn_eps=bn_eps, - activation=rw_activation) - - def forward(self, x): - x = self.lw_conv(x) - x = self.rw_conv(x) - return x - - -def asym_conv3x3_block(padding=1, - **kwargs): - """ - 3x3 asymmetric separable convolution block. - - Parameters: - ---------- - channels : int - Number of input/output channels. - padding : int, default 1 - Padding value for convolution layer. - dilation : int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - lw_use_bn : bool, default True - Whether to use BatchNorm layer (leftwise convolution block). - rw_use_bn : bool, default True - Whether to use BatchNorm layer (rightwise convolution block). - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - lw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the leftwise convolution block. - rw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the rightwise convolution block. - """ - return AsymConvBlock( - kernel_size=3, - padding=padding, - **kwargs) - - -class DeconvBlock(nn.Module): - """ - Deconvolution block with batch normalization and activation. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int - Strides of the deconvolution. - padding : int or tuple/list of 2 int - Padding value for deconvolution layer. - ext_padding : tuple/list of 4 int, default None - Extra padding value for deconvolution layer. - out_padding : int or tuple/list of 2 int - Output padding value for deconvolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for deconvolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - ext_padding=None, - out_padding=0, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - super(DeconvBlock, self).__init__() - self.activate = (activation is not None) - self.use_bn = use_bn - self.use_pad = (ext_padding is not None) - - if self.use_pad: - self.pad = nn.ZeroPad2d(padding=ext_padding) - self.conv = nn.ConvTranspose2d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - output_padding=out_padding, - dilation=dilation, - groups=groups, - bias=bias) - if self.use_bn: - self.bn = nn.BatchNorm2d( - num_features=out_channels, - eps=bn_eps) - if self.activate: - self.activ = get_activation_layer(activation) - - def forward(self, x): - if self.use_pad: - x = self.pad(x) - x = self.conv(x) - if self.use_bn: - x = self.bn(x) - if self.activate: - x = self.activ(x) - return x - - -def deconv3x3_block(padding=1, - out_padding=1, - **kwargs): - """ - 3x3 version of the deconvolution block with batch normalization and activation. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the deconvolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for deconvolution layer. - ext_padding : tuple/list of 4 int, default None - Extra padding value for deconvolution layer. - out_padding : int or tuple/list of 2 int, default 1 - Output padding value for deconvolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for deconvolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return DeconvBlock( - kernel_size=3, - padding=padding, - out_padding=out_padding, - **kwargs) - - -class NormActivation(nn.Module): - """ - Activation block with preliminary batch normalization. It's used by itself as the final block in PreResNet. - - Parameters: - ---------- - in_channels : int - Number of input channels. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - def __init__(self, - in_channels, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - super(NormActivation, self).__init__() - self.bn = nn.BatchNorm2d( - num_features=in_channels, - eps=bn_eps) - self.activ = get_activation_layer(activation) - - def forward(self, x): - x = self.bn(x) - x = self.activ(x) - return x - - -class InterpolationBlock(nn.Module): - """ - Interpolation upsampling block. - - Parameters: - ---------- - scale_factor : int - Multiplier for spatial size. - out_size : tuple of 2 int, default None - Spatial size of the output tensor for the bilinear interpolation operation. - mode : str, default 'bilinear' - Algorithm used for upsampling. - align_corners : bool, default True - Whether to align the corner pixels of the input and output tensors. - up : bool, default True - Whether to upsample or downsample. - """ - def __init__(self, - scale_factor, - out_size=None, - mode="bilinear", - align_corners=True, - up=True): - super(InterpolationBlock, self).__init__() - self.scale_factor = scale_factor - self.out_size = out_size - self.mode = mode - self.align_corners = align_corners - self.up = up - - def forward(self, x, size=None): - if (self.mode == "bilinear") or (size is not None): - out_size = self.calc_out_size(x) if size is None else size - return F.interpolate( - input=x, - size=out_size, - mode=self.mode, - align_corners=self.align_corners) - else: - return F.interpolate( - input=x, - scale_factor=self.scale_factor, - mode=self.mode, - align_corners=self.align_corners) - - def calc_out_size(self, x): - if self.out_size is not None: - return self.out_size - if self.up: - return tuple(s * self.scale_factor for s in x.shape[2:]) - else: - return tuple(s // self.scale_factor for s in x.shape[2:]) - - def __repr__(self): - s = '{name}(scale_factor={scale_factor}, out_size={out_size}, mode={mode}, align_corners={align_corners}, up={up})' # noqa - return s.format( - name=self.__class__.__name__, - scale_factor=self.scale_factor, - out_size=self.out_size, - mode=self.mode, - align_corners=self.align_corners, - up=self.up) - - def calc_flops(self, x): - assert (x.shape[0] == 1) - if self.mode == "bilinear": - num_flops = 9 * x.numel() - else: - num_flops = 4 * x.numel() - num_macs = 0 - return num_flops, num_macs - - -def channel_shuffle(x, - groups): - """ - Channel shuffle operation from 'ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devices,' - https://arxiv.org/abs/1707.01083. - - Parameters: - ---------- - x : Tensor - Input tensor. - groups : int - Number of groups. - - Returns: - ------- - Tensor - Resulted tensor. - """ - batch, channels, height, width = x.size() - # assert (channels % groups == 0) - channels_per_group = channels // groups - x = x.view(batch, groups, channels_per_group, height, width) - x = torch.transpose(x, 1, 2).contiguous() - x = x.view(batch, channels, height, width) - return x - - -class ChannelShuffle(nn.Module): - """ - Channel shuffle layer. This is a wrapper over the same operation. It is designed to save the number of groups. - - Parameters: - ---------- - channels : int - Number of channels. - groups : int - Number of groups. - """ - def __init__(self, - channels, - groups): - super(ChannelShuffle, self).__init__() - # assert (channels % groups == 0) - if channels % groups != 0: - raise ValueError("channels must be divisible by groups") - self.groups = groups - - def forward(self, x): - return channel_shuffle(x, self.groups) - - def __repr__(self): - s = "{name}(groups={groups})" - return s.format( - name=self.__class__.__name__, - groups=self.groups) - - -def channel_shuffle2(x, - groups): - """ - Channel shuffle operation from 'ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devices,' - https://arxiv.org/abs/1707.01083. The alternative version. - - Parameters: - ---------- - x : Tensor - Input tensor. - groups : int - Number of groups. - - Returns: - ------- - Tensor - Resulted tensor. - """ - batch, channels, height, width = x.size() - # assert (channels % groups == 0) - channels_per_group = channels // groups - x = x.view(batch, channels_per_group, groups, height, width) - x = torch.transpose(x, 1, 2).contiguous() - x = x.view(batch, channels, height, width) - return x - - -class ChannelShuffle2(nn.Module): - """ - Channel shuffle layer. This is a wrapper over the same operation. It is designed to save the number of groups. - The alternative version. - - Parameters: - ---------- - channels : int - Number of channels. - groups : int - Number of groups. - """ - def __init__(self, - channels, - groups): - super(ChannelShuffle2, self).__init__() - # assert (channels % groups == 0) - if channels % groups != 0: - raise ValueError("channels must be divisible by groups") - self.groups = groups - - def forward(self, x): - return channel_shuffle2(x, self.groups) - - -class SEBlock(nn.Module): - """ - Squeeze-and-Excitation block from 'Squeeze-and-Excitation Networks,' https://arxiv.org/abs/1709.01507. - - Parameters: - ---------- - channels : int - Number of channels. - reduction : int, default 16 - Squeeze reduction value. - mid_channels : int or None, default None - Number of middle channels. - round_mid : bool, default False - Whether to round middle channel number (make divisible by 8). - use_conv : bool, default True - Whether to convolutional layers instead of fully-connected ones. - activation : function, or str, or nn.Module, default 'relu' - Activation function after the first convolution. - out_activation : function, or str, or nn.Module, default 'sigmoid' - Activation function after the last convolution. - """ - def __init__(self, - channels, - reduction=16, - mid_channels=None, - round_mid=False, - use_conv=True, - mid_activation=(lambda: nn.ReLU(inplace=True)), - out_activation=(lambda: nn.Sigmoid())): - super(SEBlock, self).__init__() - self.use_conv = use_conv - if mid_channels is None: - mid_channels = channels // reduction if not round_mid else round_channels(float(channels) / reduction) - - self.pool = nn.AdaptiveAvgPool2d(output_size=1) - if use_conv: - self.conv1 = conv1x1( - in_channels=channels, - out_channels=mid_channels, - bias=True) - else: - self.fc1 = nn.Linear( - in_features=channels, - out_features=mid_channels) - self.activ = get_activation_layer(mid_activation) - if use_conv: - self.conv2 = conv1x1( - in_channels=mid_channels, - out_channels=channels, - bias=True) - else: - self.fc2 = nn.Linear( - in_features=mid_channels, - out_features=channels) - self.sigmoid = get_activation_layer(out_activation) - - def forward(self, x): - w = self.pool(x) - if not self.use_conv: - w = w.view(x.size(0), -1) - w = self.conv1(w) if self.use_conv else self.fc1(w) - w = self.activ(w) - w = self.conv2(w) if self.use_conv else self.fc2(w) - w = self.sigmoid(w) - if not self.use_conv: - w = w.unsqueeze(2).unsqueeze(3) - x = x * w - return x - - -class SABlock(nn.Module): - """ - Split-Attention block from 'ResNeSt: Split-Attention Networks,' https://arxiv.org/abs/2004.08955. - - Parameters: - ---------- - out_channels : int - Number of output channels. - groups : int - Number of channel groups (cardinality, without radix). - radix : int - Number of splits within a cardinal group. - reduction : int, default 4 - Squeeze reduction value. - min_channels : int, default 32 - Minimal number of squeezed channels. - use_conv : bool, default True - Whether to convolutional layers instead of fully-connected ones. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - """ - def __init__(self, - out_channels, - groups, - radix, - reduction=4, - min_channels=32, - use_conv=True, - bn_eps=1e-5): - super(SABlock, self).__init__() - self.groups = groups - self.radix = radix - self.use_conv = use_conv - in_channels = out_channels * radix - mid_channels = max(in_channels // reduction, min_channels) - - self.pool = nn.AdaptiveAvgPool2d(output_size=1) - if use_conv: - self.conv1 = conv1x1( - in_channels=out_channels, - out_channels=mid_channels, - bias=True) - else: - self.fc1 = nn.Linear( - in_features=out_channels, - out_features=mid_channels) - self.bn = nn.BatchNorm2d( - num_features=mid_channels, - eps=bn_eps) - self.activ = nn.ReLU(inplace=True) - if use_conv: - self.conv2 = conv1x1( - in_channels=mid_channels, - out_channels=in_channels, - bias=True) - else: - self.fc2 = nn.Linear( - in_features=mid_channels, - out_features=in_channels) - self.softmax = nn.Softmax(dim=1) - - def forward(self, x): - batch, channels, height, width = x.size() - x = x.view(batch, self.radix, channels // self.radix, height, width) - w = x.sum(dim=1) - w = self.pool(w) - if not self.use_conv: - w = w.view(x.size(0), -1) - w = self.conv1(w) if self.use_conv else self.fc1(w) - w = self.bn(w) - w = self.activ(w) - w = self.conv2(w) if self.use_conv else self.fc2(w) - w = w.view(batch, self.groups, self.radix, -1) - w = torch.transpose(w, 1, 2).contiguous() - w = self.softmax(w) - w = w.view(batch, self.radix, -1, 1, 1) - x = x * w - x = x.sum(dim=1) - return x - - -class SAConvBlock(nn.Module): - """ - Split-Attention convolution block from 'ResNeSt: Split-Attention Networks,' https://arxiv.org/abs/2004.08955. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - radix : int, default 2 - Number of splits within a cardinal group. - reduction : int, default 4 - Squeeze reduction value. - min_channels : int, default 32 - Minimal number of squeezed channels. - use_conv : bool, default True - Whether to convolutional layers instead of fully-connected ones. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True)), - radix=2, - reduction=4, - min_channels=32, - use_conv=True): - super(SAConvBlock, self).__init__() - self.conv = ConvBlock( - in_channels=in_channels, - out_channels=(out_channels * radix), - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=(groups * radix), - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - self.att = SABlock( - out_channels=out_channels, - groups=groups, - radix=radix, - reduction=reduction, - min_channels=min_channels, - use_conv=use_conv, - bn_eps=bn_eps) - - def forward(self, x): - x = self.conv(x) - x = self.att(x) - return x - - -def saconv3x3_block(in_channels, - out_channels, - stride=1, - padding=1, - **kwargs): - """ - 3x3 version of the Split-Attention convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for convolution layer. - """ - return SAConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - **kwargs) - - -class DucBlock(nn.Module): - """ - Dense Upsampling Convolution (DUC) block from 'Understanding Convolution for Semantic Segmentation,' - https://arxiv.org/abs/1702.08502. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - scale_factor : int - Multiplier for spatial size. - """ - def __init__(self, - in_channels, - out_channels, - scale_factor): - super(DucBlock, self).__init__() - mid_channels = (scale_factor * scale_factor) * out_channels - - self.conv = conv3x3_block( - in_channels=in_channels, - out_channels=mid_channels) - self.pix_shuffle = nn.PixelShuffle(upscale_factor=scale_factor) - - def forward(self, x): - x = self.conv(x) - x = self.pix_shuffle(x) - return x - - -class IBN(nn.Module): - """ - Instance-Batch Normalization block from 'Two at Once: Enhancing Learning and Generalization Capacities via IBN-Net,' - https://arxiv.org/abs/1807.09441. - - Parameters: - ---------- - channels : int - Number of channels. - inst_fraction : float, default 0.5 - The first fraction of channels for normalization. - inst_first : bool, default True - Whether instance normalization be on the first part of channels. - """ - def __init__(self, - channels, - first_fraction=0.5, - inst_first=True): - super(IBN, self).__init__() - self.inst_first = inst_first - h1_channels = int(math.floor(channels * first_fraction)) - h2_channels = channels - h1_channels - self.split_sections = [h1_channels, h2_channels] - - if self.inst_first: - self.inst_norm = nn.InstanceNorm2d( - num_features=h1_channels, - affine=True) - self.batch_norm = nn.BatchNorm2d(num_features=h2_channels) - else: - self.batch_norm = nn.BatchNorm2d(num_features=h1_channels) - self.inst_norm = nn.InstanceNorm2d( - num_features=h2_channels, - affine=True) - - def forward(self, x): - x1, x2 = torch.split(x, split_size_or_sections=self.split_sections, dim=1) - if self.inst_first: - x1 = self.inst_norm(x1.contiguous()) - x2 = self.batch_norm(x2.contiguous()) - else: - x1 = self.batch_norm(x1.contiguous()) - x2 = self.inst_norm(x2.contiguous()) - x = torch.cat((x1, x2), dim=1) - return x - - -class DualPathSequential(nn.Sequential): - """ - A sequential container for modules with dual inputs/outputs. - Modules will be executed in the order they are added. - - Parameters: - ---------- - return_two : bool, default True - Whether to return two output after execution. - first_ordinals : int, default 0 - Number of the first modules with single input/output. - last_ordinals : int, default 0 - Number of the final modules with single input/output. - dual_path_scheme : function - Scheme of dual path response for a module. - dual_path_scheme_ordinal : function - Scheme of dual path response for an ordinal module. - """ - def __init__(self, - return_two=True, - first_ordinals=0, - last_ordinals=0, - dual_path_scheme=(lambda module, x1, x2: module(x1, x2)), - dual_path_scheme_ordinal=(lambda module, x1, x2: (module(x1), x2))): - super(DualPathSequential, self).__init__() - self.return_two = return_two - self.first_ordinals = first_ordinals - self.last_ordinals = last_ordinals - self.dual_path_scheme = dual_path_scheme - self.dual_path_scheme_ordinal = dual_path_scheme_ordinal - - def forward(self, x1, x2=None): - length = len(self._modules.values()) - for i, module in enumerate(self._modules.values()): - if (i < self.first_ordinals) or (i >= length - self.last_ordinals): - x1, x2 = self.dual_path_scheme_ordinal(module, x1, x2) - else: - x1, x2 = self.dual_path_scheme(module, x1, x2) - if self.return_two: - return x1, x2 - else: - return x1 - - -class Concurrent(nn.Sequential): - """ - A container for concatenation of modules on the base of the sequential container. - - Parameters: - ---------- - axis : int, default 1 - The axis on which to concatenate the outputs. - stack : bool, default False - Whether to concatenate tensors along a new dimension. - merge_type : str, default None - Type of branch merging. - """ - def __init__(self, - axis=1, - stack=False, - merge_type=None): - super(Concurrent, self).__init__() - assert (merge_type is None) or (merge_type in ["cat", "stack", "sum"]) - self.axis = axis - if merge_type is not None: - self.merge_type = merge_type - else: - self.merge_type = "stack" if stack else "cat" - - def forward(self, x): - out = [] - for module in self._modules.values(): - out.append(module(x)) - if self.merge_type == "stack": - out = torch.stack(tuple(out), dim=self.axis) - elif self.merge_type == "cat": - out = torch.cat(tuple(out), dim=self.axis) - elif self.merge_type == "sum": - out = torch.stack(tuple(out), dim=self.axis).sum(self.axis) - else: - raise NotImplementedError() - return out - - -class SequentialConcurrent(nn.Sequential): - """ - A sequential container with concatenated outputs. - Modules will be executed in the order they are added. - - Parameters: - ---------- - axis : int, default 1 - The axis on which to concatenate the outputs. - stack : bool, default False - Whether to concatenate tensors along a new dimension. - cat_input : bool, default True - Whether to concatenate input tensor. - """ - def __init__(self, - axis=1, - stack=False, - cat_input=True): - super(SequentialConcurrent, self).__init__() - self.axis = axis - self.stack = stack - self.cat_input = cat_input - - def forward(self, x): - out = [x] if self.cat_input else [] - for module in self._modules.values(): - x = module(x) - out.append(x) - if self.stack: - out = torch.stack(tuple(out), dim=self.axis) - else: - out = torch.cat(tuple(out), dim=self.axis) - return out - - -class ParametricSequential(nn.Sequential): - """ - A sequential container for modules with parameters. - Modules will be executed in the order they are added. - """ - def __init__(self, *args): - super(ParametricSequential, self).__init__(*args) - - def forward(self, x, **kwargs): - for module in self._modules.values(): - x = module(x, **kwargs) - return x - - -class ParametricConcurrent(nn.Sequential): - """ - A container for concatenation of modules with parameters. - - Parameters: - ---------- - axis : int, default 1 - The axis on which to concatenate the outputs. - """ - def __init__(self, axis=1): - super(ParametricConcurrent, self).__init__() - self.axis = axis - - def forward(self, x, **kwargs): - out = [] - for module in self._modules.values(): - out.append(module(x, **kwargs)) - out = torch.cat(tuple(out), dim=self.axis) - return out - - -class Hourglass(nn.Module): - """ - A hourglass module. - - Parameters: - ---------- - down_seq : nn.Sequential - Down modules as sequential. - up_seq : nn.Sequential - Up modules as sequential. - skip_seq : nn.Sequential - Skip connection modules as sequential. - merge_type : str, default 'add' - Type of concatenation of up and skip outputs. - return_first_skip : bool, default False - Whether return the first skip connection output. Used in ResAttNet. - """ - def __init__(self, - down_seq, - up_seq, - skip_seq, - merge_type="add", - return_first_skip=False): - super(Hourglass, self).__init__() - self.depth = len(down_seq) - assert (merge_type in ["cat", "add"]) - assert (len(up_seq) == self.depth) - assert (len(skip_seq) in (self.depth, self.depth + 1)) - self.merge_type = merge_type - self.return_first_skip = return_first_skip - self.extra_skip = (len(skip_seq) == self.depth + 1) - - self.down_seq = down_seq - self.up_seq = up_seq - self.skip_seq = skip_seq - - def _merge(self, x, y): - if y is not None: - if self.merge_type == "cat": - x = torch.cat((x, y), dim=1) - elif self.merge_type == "add": - x = x + y - return x - - def forward(self, x, **kwargs): - y = None - down_outs = [x] - for down_module in self.down_seq._modules.values(): - x = down_module(x) - down_outs.append(x) - for i in range(len(down_outs)): - if i != 0: - y = down_outs[self.depth - i] - skip_module = self.skip_seq[self.depth - i] - y = skip_module(y) - x = self._merge(x, y) - if i != len(down_outs) - 1: - if (i == 0) and self.extra_skip: - skip_module = self.skip_seq[self.depth] - x = skip_module(x) - up_module = self.up_seq[self.depth - 1 - i] - x = up_module(x) - if self.return_first_skip: - return x, y - else: - return x - - -class SesquialteralHourglass(nn.Module): - """ - A sesquialteral hourglass block. - - Parameters: - ---------- - down1_seq : nn.Sequential - The first down modules as sequential. - skip1_seq : nn.Sequential - The first skip connection modules as sequential. - up_seq : nn.Sequential - Up modules as sequential. - skip2_seq : nn.Sequential - The second skip connection modules as sequential. - down2_seq : nn.Sequential - The second down modules as sequential. - merge_type : str, default 'cat' - Type of concatenation of up and skip outputs. - """ - def __init__(self, - down1_seq, - skip1_seq, - up_seq, - skip2_seq, - down2_seq, - merge_type="cat"): - super(SesquialteralHourglass, self).__init__() - assert (len(down1_seq) == len(up_seq)) - assert (len(down1_seq) == len(down2_seq)) - assert (len(skip1_seq) == len(skip2_seq)) - assert (len(down1_seq) == len(skip1_seq) - 1) - assert (merge_type in ["cat", "add"]) - self.merge_type = merge_type - self.depth = len(down1_seq) - - self.down1_seq = down1_seq - self.skip1_seq = skip1_seq - self.up_seq = up_seq - self.skip2_seq = skip2_seq - self.down2_seq = down2_seq - - def _merge(self, x, y): - if y is not None: - if self.merge_type == "cat": - x = torch.cat((x, y), dim=1) - elif self.merge_type == "add": - x = x + y - return x - - def forward(self, x, **kwargs): - y = self.skip1_seq[0](x) - skip1_outs = [y] - for i in range(self.depth): - x = self.down1_seq[i](x) - y = self.skip1_seq[i + 1](x) - skip1_outs.append(y) - x = skip1_outs[self.depth] - y = self.skip2_seq[0](x) - skip2_outs = [y] - for i in range(self.depth): - x = self.up_seq[i](x) - y = skip1_outs[self.depth - 1 - i] - x = self._merge(x, y) - y = self.skip2_seq[i + 1](x) - skip2_outs.append(y) - x = self.skip2_seq[self.depth](x) - for i in range(self.depth): - x = self.down2_seq[i](x) - y = skip2_outs[self.depth - 1 - i] - x = self._merge(x, y) - return x - - -class MultiOutputSequential(nn.Sequential): - """ - A sequential container with multiple outputs. - Modules will be executed in the order they are added. - - Parameters: - ---------- - multi_output : bool, default True - Whether to return multiple output. - dual_output : bool, default False - Whether to return dual output. - return_last : bool, default True - Whether to forcibly return last value. - """ - def __init__(self, - multi_output=True, - dual_output=False, - return_last=True): - super(MultiOutputSequential, self).__init__() - self.multi_output = multi_output - self.dual_output = dual_output - self.return_last = return_last - - def forward(self, x): - outs = [] - for module in self._modules.values(): - x = module(x) - if hasattr(module, "do_output") and module.do_output: - outs.append(x) - elif hasattr(module, "do_output2") and module.do_output2: - assert (type(x) == tuple) - outs.extend(x[1]) - x = x[0] - if self.multi_output: - return [x] + outs if self.return_last else outs - elif self.dual_output: - return x, outs - else: - return x - - -class ParallelConcurent(nn.Sequential): - """ - A sequential container with multiple inputs and single/multiple outputs. - Modules will be executed in the order they are added. - - Parameters: - ---------- - axis : int, default 1 - The axis on which to concatenate the outputs. - merge_type : str, default 'list' - Type of branch merging. - """ - def __init__(self, - axis=1, - merge_type="list"): - super(ParallelConcurent, self).__init__() - assert (merge_type is None) or (merge_type in ["list", "cat", "stack", "sum"]) - self.axis = axis - self.merge_type = merge_type - - def forward(self, x): - out = [] - for module, xi in zip(self._modules.values(), x): - out.append(module(xi)) - if self.merge_type == "list": - pass - elif self.merge_type == "stack": - out = torch.stack(tuple(out), dim=self.axis) - elif self.merge_type == "cat": - out = torch.cat(tuple(out), dim=self.axis) - elif self.merge_type == "sum": - out = torch.stack(tuple(out), dim=self.axis).sum(self.axis) - else: - raise NotImplementedError() - return out - - -class DualPathParallelConcurent(nn.Sequential): - """ - A sequential container with multiple dual-path inputs and single/multiple outputs. - Modules will be executed in the order they are added. - - Parameters: - ---------- - axis : int, default 1 - The axis on which to concatenate the outputs. - merge_type : str, default 'list' - Type of branch merging. - """ - def __init__(self, - axis=1, - merge_type="list"): - super(DualPathParallelConcurent, self).__init__() - assert (merge_type is None) or (merge_type in ["list", "cat", "stack", "sum"]) - self.axis = axis - self.merge_type = merge_type - - def forward(self, x1, x2): - x1_out = [] - x2_out = [] - for module, x1i, x2i in zip(self._modules.values(), x1, x2): - y1i, y2i = module(x1i, x2i) - x1_out.append(y1i) - x2_out.append(y2i) - if self.merge_type == "list": - pass - elif self.merge_type == "stack": - x1_out = torch.stack(tuple(x1_out), dim=self.axis) - x2_out = torch.stack(tuple(x2_out), dim=self.axis) - elif self.merge_type == "cat": - x1_out = torch.cat(tuple(x1_out), dim=self.axis) - x2_out = torch.cat(tuple(x2_out), dim=self.axis) - elif self.merge_type == "sum": - x1_out = torch.stack(tuple(x1_out), dim=self.axis).sum(self.axis) - x2_out = torch.stack(tuple(x2_out), dim=self.axis).sum(self.axis) - else: - raise NotImplementedError() - return x1_out, x2_out - - -class Flatten(nn.Module): - """ - Simple flatten module. - """ - - def forward(self, x): - return x.view(x.size(0), -1) - - -class HeatmapMaxDetBlock(nn.Module): - """ - Heatmap maximum detector block (for human pose estimation task). - """ - def __init__(self): - super(HeatmapMaxDetBlock, self).__init__() - - def forward(self, x): - heatmap = x - vector_dim = 2 - batch = heatmap.shape[0] - channels = heatmap.shape[1] - in_size = x.shape[2:] - heatmap_vector = heatmap.view(batch, channels, -1) - scores, indices = heatmap_vector.max(dim=vector_dim, keepdims=True) - scores_mask = (scores > 0.0).float() - pts_x = (indices % in_size[1]) * scores_mask - pts_y = (indices // in_size[1]) * scores_mask - pts = torch.cat((pts_x, pts_y, scores), dim=vector_dim) - for b in range(batch): - for k in range(channels): - hm = heatmap[b, k, :, :] - px = int(pts[b, k, 0]) - py = int(pts[b, k, 1]) - if (0 < px < in_size[1] - 1) and (0 < py < in_size[0] - 1): - pts[b, k, 0] += (hm[py, px + 1] - hm[py, px - 1]).sign() * 0.25 - pts[b, k, 1] += (hm[py + 1, px] - hm[py - 1, px]).sign() * 0.25 - return pts - - @staticmethod - def calc_flops(x): - assert (x.shape[0] == 1) - num_flops = x.numel() + 26 * x.shape[1] - num_macs = 0 - return num_flops, num_macs diff --git a/cv/classification/resnext50_32x4d/pytorch/common_utils/__init__.py b/cv/classification/resnext50_32x4d/pytorch/common_utils/__init__.py new file mode 100644 index 000000000..32e8c4f57 --- /dev/null +++ b/cv/classification/resnext50_32x4d/pytorch/common_utils/__init__.py @@ -0,0 +1,23 @@ +import random + +import numpy as np + +from .dist import * +from .metric_logger import * +from .misc import * +from .smooth_value import * + +def manual_seed(seed, deterministic=False): + random.seed(seed) + np.random.seed(seed) + os.environ['PYTHONHASHSEED'] = str(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + + if deterministic: + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + else: + torch.backends.cudnn.deterministic = False + torch.backends.cudnn.benchmark = True \ No newline at end of file diff --git a/cv/classification/resnext50_32x4d/pytorch/common_utils/dist.py b/cv/classification/resnext50_32x4d/pytorch/common_utils/dist.py new file mode 100644 index 000000000..ea56ca267 --- /dev/null +++ b/cv/classification/resnext50_32x4d/pytorch/common_utils/dist.py @@ -0,0 +1,144 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist + + + +def setup_for_distributed(is_master): + """ + This function disables printing when not in master process + """ + import builtins as __builtin__ + builtin_print = __builtin__.print + + def print(*args, **kwargs): + force = kwargs.pop('force', False) + if is_master or force: + builtin_print(*args, **kwargs) + + __builtin__.print = print + + +def is_dist_avail_and_initialized(): + if not dist.is_available(): + return False + if not dist.is_initialized(): + return False + return True + + +def get_world_size(): + if not is_dist_avail_and_initialized(): + return 1 + return dist.get_world_size() + + +def get_rank(): + if not is_dist_avail_and_initialized(): + return 0 + return dist.get_rank() + + +def is_main_process(): + return get_rank() == 0 + + +def save_on_master(*args, **kwargs): + if is_main_process(): + torch.save(*args, **kwargs) + + +def get_dist_backend(args=None): + DIST_BACKEND_ENV = "PT_DIST_BACKEND" + if DIST_BACKEND_ENV in os.environ: + return os.environ[DIST_BACKEND_ENV] + + if args is None: + args = dict() + + backend_attr_name = "dist_backend" + + if hasattr(args, backend_attr_name): + return getattr(args, backend_attr_name) + + if backend_attr_name in args: + return args[backend_attr_name] + + return "nccl" + + +def init_distributed_mode(args): + if 'RANK' in os.environ and 'WORLD_SIZE' in os.environ: + args.rank = int(os.environ["RANK"]) + args.world_size = int(os.environ['WORLD_SIZE']) + args.gpu = int(os.environ['LOCAL_RANK']) + elif 'SLURM_PROCID' in os.environ: + args.rank = int(os.environ['SLURM_PROCID']) + args.gpu = args.rank % torch.cuda.device_count() + else: + print('Not using distributed mode') + args.distributed = False + return + + args.distributed = True + + torch.cuda.set_device(args.gpu) + dist_backend = get_dist_backend(args) + print('| distributed init (rank {}): {}'.format( + args.rank, args.dist_url), flush=True) + torch.distributed.init_process_group(backend=dist_backend, init_method=args.dist_url, + world_size=args.world_size, rank=args.rank) + torch.distributed.barrier() + setup_for_distributed(args.rank == 0) + + +def all_gather(data): + """ + Run all_gather on arbitrary picklable data (not necessarily tensors) + Args: + data: any picklable object + Returns: + list[data]: list of data gathered from each rank + """ + world_size = get_world_size() + if world_size == 1: + return [data] + data_list = [None] * world_size + dist.all_gather_object(data_list, data) + return data_list + + +def reduce_dict(input_dict, average=True): + """ + Args: + input_dict (dict): all the values will be reduced + average (bool): whether to do average or sum + Reduce the values in the dictionary from all processes so that all processes + have the averaged results. Returns a dict with the same fields as + input_dict, after reduction. + """ + world_size = get_world_size() + if world_size < 2: + return input_dict + with torch.no_grad(): + names = [] + values = [] + # sort the keys so that they are consistent across processes + for k in sorted(input_dict.keys()): + names.append(k) + values.append(input_dict[k]) + values = torch.stack(values, dim=0) + dist.all_reduce(values) + if average: + values /= world_size + reduced_dict = {k: v for k, v in zip(names, values)} + return reduced_dict diff --git a/cv/classification/resnext50_32x4d/pytorch/common_utils/metric_logger.py b/cv/classification/resnext50_32x4d/pytorch/common_utils/metric_logger.py new file mode 100644 index 000000000..960641c4d --- /dev/null +++ b/cv/classification/resnext50_32x4d/pytorch/common_utils/metric_logger.py @@ -0,0 +1,94 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict +import datetime +import time + +import torch +from .smooth_value import SmoothedValue + +""" +Examples: + +logger = MetricLogger(" ") + +>>> # For iter dataloader +>>> metric_logger.add_meter('img/s', utils.SmoothedValue(window_size=10, fmt='{value}')) +>>> header = 'Epoch: [{}]'.format(epoch) +>>> for image, target in metric_logger.log_every(data_loader, print_freq, header): +>>> ... +>>> logger.metric_logger.meters['img/s'].update(fps) + +""" + +class MetricLogger(object): + + def __init__(self, delimiter="\t"): + self.meters = defaultdict(SmoothedValue) + self.delimiter = delimiter + + def update(self, **kwargs): + for k, v in kwargs.items(): + if isinstance(v, torch.Tensor): + v = v.item() + assert isinstance(v, (float, int)) + self.meters[k].update(v) + + def __getattr__(self, attr): + if attr in self.meters: + return self.meters[attr] + if attr in self.__dict__: + return self.__dict__[attr] + raise AttributeError("'{}' object has no attribute '{}'".format( + type(self).__name__, attr)) + + def __str__(self): + loss_str = [] + for name, meter in self.meters.items(): + loss_str.append( + "{}: {}".format(name, str(meter)) + ) + return self.delimiter.join(loss_str) + + def synchronize_between_processes(self): + for meter in self.meters.values(): + meter.synchronize_between_processes() + + def add_meter(self, name, meter): + self.meters[name] = meter + + def log_every(self, iterable, print_freq, header=None): + i = 0 + if not header: + header = '' + start_time = time.time() + end = time.time() + iter_time = SmoothedValue(fmt='{avg:.4f}') + data_time = SmoothedValue(fmt='{avg:.4f}') + space_fmt = ':' + str(len(str(len(iterable)))) + 'd' + log_msg = self.delimiter.join([ + header, + '[{0' + space_fmt + '}/{1}]', + 'eta: {eta}', + '{meters}', + 'time: {time}', + 'data: {data}' + ]) + for obj in iterable: + data_time.update(time.time() - end) + yield obj + iter_time.update(time.time() - end) + if i % print_freq == 0: + eta_seconds = iter_time.global_avg * (len(iterable) - i) + eta_string = str(datetime.timedelta(seconds=int(eta_seconds))) + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time))) + i += 1 + end = time.time() + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('{} Total time: {}'.format(header, total_time_str)) diff --git a/cv/classification/resnext50_32x4d/pytorch/common_utils/misc.py b/cv/classification/resnext50_32x4d/pytorch/common_utils/misc.py new file mode 100644 index 000000000..c9b501cf8 --- /dev/null +++ b/cv/classification/resnext50_32x4d/pytorch/common_utils/misc.py @@ -0,0 +1,11 @@ +import os +import sys +import errno + + +def mkdir(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise \ No newline at end of file diff --git a/cv/classification/resnext50_32x4d/pytorch/common_utils/smooth_value.py b/cv/classification/resnext50_32x4d/pytorch/common_utils/smooth_value.py new file mode 100644 index 000000000..30cb89d60 --- /dev/null +++ b/cv/classification/resnext50_32x4d/pytorch/common_utils/smooth_value.py @@ -0,0 +1,75 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist +from .dist import is_dist_avail_and_initialized + + +class SmoothedValue(object): + """Track a series of values and provide access to smoothed values over a + window or the global series average. + """ + + def __init__(self, window_size=20, fmt=None): + if fmt is None: + fmt = "{median:.4f} ({global_avg:.4f})" + self.deque = deque(maxlen=window_size) + self.total = 0.0 + self.count = 0 + self.fmt = fmt + + def update(self, value, n=1): + self.deque.append(value) + self.count += n + self.total += value * n + + def synchronize_between_processes(self): + """ + Warning: does not synchronize the deque! + """ + if not is_dist_avail_and_initialized(): + return + t = torch.tensor([self.count, self.total], dtype=torch.float32, device='cuda') + dist.barrier() + dist.all_reduce(t) + t = t.tolist() + self.count = int(t[0]) + self.total = t[1] + + @property + def median(self): + d = torch.tensor(list(self.deque)) + return d.median().item() + + @property + def avg(self): + d = torch.tensor(list(self.deque), dtype=torch.float32) + return d.mean().item() + + @property + def global_avg(self): + return self.total / self.count + + @property + def max(self): + return max(self.deque) + + @property + def value(self): + return self.deque[-1] + + def __str__(self): + return self.fmt.format( + median=self.median, + avg=self.avg, + global_avg=self.global_avg, + max=self.max, + value=self.value) \ No newline at end of file diff --git a/cv/classification/resnext50_32x4d/pytorch/dataloader/__init__.py b/cv/classification/resnext50_32x4d/pytorch/dataloader/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/classification/resnext50_32x4d/pytorch/dataloader/classification.py b/cv/classification/resnext50_32x4d/pytorch/dataloader/classification.py new file mode 100644 index 000000000..030af6dee --- /dev/null +++ b/cv/classification/resnext50_32x4d/pytorch/dataloader/classification.py @@ -0,0 +1,113 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +import os +import time + +import torch +import torchvision +from .utils import presets_classification as presets + +""" +Examples: + +>>> dataset_train, dataset_val = load_data(train_dir, val_dir, args) +""" + + +def get_datasets(traindir, + valdir, + resize_size=256, + crop_size=224, + auto_augment_policy=None, + random_erase_prob=0.): + # Data loading code + print("Loading data") + print("Loading training data") + dataset = torchvision.datasets.ImageFolder( + traindir, + presets.ClassificationPresetTrain(crop_size=crop_size, auto_augment_policy=auto_augment_policy, + random_erase_prob=random_erase_prob)) + + print("Loading validation data") + dataset_test = torchvision.datasets.ImageFolder( + valdir, + presets.ClassificationPresetEval(crop_size=crop_size, resize_size=resize_size)) + + return dataset, dataset_test + + +def get_input_size(model): + biger_input_size_models = ['inception'] + resize_size = 256 + crop_size = 224 + for bi_model in biger_input_size_models: + if bi_model in model: + resize_size = 342 + crop_size = 299 + + return resize_size, crop_size + + +def load_data(train_dir, val_dir, args): + auto_augment_policy = getattr(args, "auto_augment", None) + random_erase_prob = getattr(args, "random_erase", 0.0) + resize_size, crop_size = get_input_size(args.model) + dataset, dataset_test = get_datasets(train_dir, val_dir, + auto_augment_policy=auto_augment_policy, + random_erase_prob=random_erase_prob, + resize_size=resize_size, + crop_size=crop_size) + if args.distributed: + train_sampler = torch.utils.data.distributed.DistributedSampler(dataset) + test_sampler = torch.utils.data.distributed.DistributedSampler(dataset_test) + else: + train_sampler = torch.utils.data.RandomSampler(dataset) + test_sampler = torch.utils.data.SequentialSampler(dataset_test) + + return dataset, dataset_test, train_sampler, test_sampler + + +def _create_torch_dataloader(train_dir, val_dir, args): + dataset, dataset_test, train_sampler, test_sampler = load_data(train_dir, val_dir, args) + data_loader = torch.utils.data.DataLoader( + dataset, batch_size=args.batch_size, + sampler=train_sampler, num_workers=args.workers, pin_memory=True) + + data_loader_test = torch.utils.data.DataLoader( + dataset_test, batch_size=args.batch_size, + sampler=test_sampler, num_workers=args.workers, pin_memory=True) + + return data_loader, data_loader_test + + +def _create_dali_dataloader(train_dir, val_dir, args): + from .dali_classification import get_imagenet_iter_dali + device = torch.cuda.current_device() + _, crop_size = get_input_size(args.model) + data_loader = get_imagenet_iter_dali('train', train_dir, args.batch_size, + num_threads=args.workers, + device_id=device, + size=crop_size) + data_loader_test = get_imagenet_iter_dali('val', train_dir, args.batch_size, + num_threads=args.workers, + device_id=device, + size=crop_size) + + return data_loader, data_loader_test + + +def create_dataloader(train_dir, val_dir, args): + print("Creating data loaders") + if args.dali: + train_dir = os.path.dirname(train_dir) + val_dir = os.path.dirname(val_dir) + return _create_dali_dataloader(train_dir, val_dir, args) + return _create_torch_dataloader(train_dir, val_dir, args) diff --git a/cv/classification/resnext50_32x4d/pytorch/dataloader/dali_classification.py b/cv/classification/resnext50_32x4d/pytorch/dataloader/dali_classification.py new file mode 100644 index 000000000..4c92283b2 --- /dev/null +++ b/cv/classification/resnext50_32x4d/pytorch/dataloader/dali_classification.py @@ -0,0 +1,121 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +import os + +import nvidia.dali.ops as ops +import nvidia.dali.types as types +from nvidia.dali.pipeline import Pipeline +from nvidia.dali.plugin.pytorch import DALIClassificationIterator, DALIGenericIterator + +class HybridTrainPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridTrainPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=True) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.RandomResizedCrop(device="gpu", size=size, random_area=[0.08, 1.25]) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +class HybridValPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridValPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=False) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.Resize(device="gpu", resize_x=size, resize_y=size) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + crop=(size, size), + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +def get_imagenet_iter_dali(type, image_dir, batch_size, num_threads, device_id, size): + if type == 'train': + pip_train = HybridTrainPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "train"), + size=size) + pip_train.build() + dali_iter_train = DALIClassificationIterator(pip_train, size=pip_train.epoch_size("Reader")) + return dali_iter_train + elif type == 'val': + pip_val = HybridValPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "val"), + size=size) + pip_val.build() + dali_iter_val = DALIClassificationIterator(pip_val, size=pip_val.epoch_size("Reader")) + return dali_iter_val + + +def main(arguments): + parser = argparse.ArgumentParser() + parser.add_argument('--data_dir', help='directory to save data to', type=str, default='classification data') + args = parser.parse_args(arguments) + + train_loader = get_imagenet_iter_dali(type='train', image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + val_loader = get_imagenet_iter_dali(type="val", image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + print('start dali train dataloader.') + start = time.time() + for epoch in range(20): + for i, data in enumerate(train_loader): + images = data[0]["data"].cuda(non_blocking=True) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + + # WARN: Very important + train_loader.reset() + print("Epoch", epoch) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali train dataloader.') + + + print('start dali val dataloader.') + start = time.time() + for i, data in enumerate(val_loader): + images = data[0]["data"].cuda(non_blocking=True) + print(images.shape) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + print(labels.shape) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali val dataloader.') + + +if __name__ == '__main__': + import os, time, sys + import argparse + sys.exit(main(sys.argv[1:])) \ No newline at end of file diff --git a/cv/classification/resnext50_32x4d/pytorch/dataloader/utils/__init__.py b/cv/classification/resnext50_32x4d/pytorch/dataloader/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/classification/resnext50_32x4d/pytorch/dataloader/utils/presets_classification.py b/cv/classification/resnext50_32x4d/pytorch/dataloader/utils/presets_classification.py new file mode 100644 index 000000000..b3f559af4 --- /dev/null +++ b/cv/classification/resnext50_32x4d/pytorch/dataloader/utils/presets_classification.py @@ -0,0 +1,41 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from torchvision.transforms import autoaugment, transforms + + +class ClassificationPresetTrain: + def __init__(self, crop_size, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225), hflip_prob=0.5, + auto_augment_policy=None, random_erase_prob=0.0): + trans = [transforms.RandomResizedCrop(crop_size)] + if hflip_prob > 0: + trans.append(transforms.RandomHorizontalFlip(hflip_prob)) + if auto_augment_policy is not None: + aa_policy = autoaugment.AutoAugmentPolicy(auto_augment_policy) + trans.append(autoaugment.AutoAugment(policy=aa_policy)) + trans.extend([ + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ]) + if random_erase_prob > 0: + trans.append(transforms.RandomErasing(p=random_erase_prob)) + + self.transforms = transforms.Compose(trans) + + def __call__(self, img): + return self.transforms(img) + + +class ClassificationPresetEval: + def __init__(self, crop_size, resize_size=256, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)): + + self.transforms = transforms.Compose([ + transforms.Resize(resize_size), + transforms.CenterCrop(crop_size), + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ]) + + def __call__(self, img): + return self.transforms(img) diff --git a/cv/classification/resnext50_32x4d/pytorch/requirements.txt b/cv/classification/resnext50_32x4d/pytorch/requirements.txt index 61d5792cb..7321845b9 100644 --- a/cv/classification/resnext50_32x4d/pytorch/requirements.txt +++ b/cv/classification/resnext50_32x4d/pytorch/requirements.txt @@ -1,3 +1 @@ -torch -torchvision ---extra-index-url https://developer.download.nvidia.com/compute/redist/ nvidia-dali-cuda102==1.6.0 +numpy>=1.23.5 \ No newline at end of file diff --git a/cv/classification/resnext50_32x4d/pytorch/resnet.py b/cv/classification/resnext50_32x4d/pytorch/resnet.py deleted file mode 100644 index 8e137145a..000000000 --- a/cv/classification/resnext50_32x4d/pytorch/resnet.py +++ /dev/null @@ -1,785 +0,0 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright Declaration: This software, including all of its code and documentation, -# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX -# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright -# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar -# CoreX. No user of this software shall have any right, ownership or interest in this software and -# any use of this software shall be in compliance with the terms and conditions of the End User -# License Agreement. -""" - ResNet for ImageNet-1K, implemented in PyTorch. - Original paper: 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. -""" - -__all__ = ['ResNet', 'resnet10', 'resnet12', 'resnet14', 'resnetbc14b', 'resnet16', 'resnet18_wd4', 'resnet18_wd2', - 'resnet18_w3d4', 'resnet18', 'resnet26', 'resnetbc26b', 'resnet34', 'resnetbc38b', 'resnet50', 'resnet50b', - 'resnet101', 'resnet101b', 'resnet152', 'resnet152b', 'resnet200', 'resnet200b', 'ResBlock', 'ResBottleneck', - 'ResUnit', 'ResInitBlock'] - -import os -import torch.nn as nn -from common import conv1x1_block, conv3x3_block, conv7x7_block - - -class ResBlock(nn.Module): - """ - Simple ResNet block for residual path in ResNet unit. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the convolution. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - """ - def __init__(self, - in_channels, - out_channels, - stride, - bias=False, - use_bn=True): - super(ResBlock, self).__init__() - self.conv1 = conv3x3_block( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - bias=bias, - use_bn=use_bn) - self.conv2 = conv3x3_block( - in_channels=out_channels, - out_channels=out_channels, - bias=bias, - use_bn=use_bn, - activation=None) - - def forward(self, x): - x = self.conv1(x) - x = self.conv2(x) - return x - - -class ResBottleneck(nn.Module): - """ - ResNet bottleneck block for residual path in ResNet unit. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for the second convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for the second convolution layer. - conv1_stride : bool, default False - Whether to use stride in the first or the second convolution layer of the block. - bottleneck_factor : int, default 4 - Bottleneck factor. - """ - def __init__(self, - in_channels, - out_channels, - stride, - padding=1, - dilation=1, - conv1_stride=False, - bottleneck_factor=4): - super(ResBottleneck, self).__init__() - mid_channels = out_channels // bottleneck_factor - - self.conv1 = conv1x1_block( - in_channels=in_channels, - out_channels=mid_channels, - stride=(stride if conv1_stride else 1)) - self.conv2 = conv3x3_block( - in_channels=mid_channels, - out_channels=mid_channels, - stride=(1 if conv1_stride else stride), - padding=padding, - dilation=dilation) - self.conv3 = conv1x1_block( - in_channels=mid_channels, - out_channels=out_channels, - activation=None) - - def forward(self, x): - x = self.conv1(x) - x = self.conv2(x) - x = self.conv3(x) - return x - - -class ResUnit(nn.Module): - """ - ResNet unit with residual connection. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for the second convolution layer in bottleneck. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for the second convolution layer in bottleneck. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bottleneck : bool, default True - Whether to use a bottleneck or simple block in units. - conv1_stride : bool, default False - Whether to use stride in the first or the second convolution layer of the block. - """ - def __init__(self, - in_channels, - out_channels, - stride, - padding=1, - dilation=1, - bias=False, - use_bn=True, - bottleneck=True, - conv1_stride=False): - super(ResUnit, self).__init__() - self.resize_identity = (in_channels != out_channels) or (stride != 1) - - if bottleneck: - self.body = ResBottleneck( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - padding=padding, - dilation=dilation, - conv1_stride=conv1_stride) - else: - self.body = ResBlock( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - bias=bias, - use_bn=use_bn) - if self.resize_identity: - self.identity_conv = conv1x1_block( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - bias=bias, - use_bn=use_bn, - activation=None) - self.activ = nn.ReLU(inplace=True) - - def forward(self, x): - if self.resize_identity: - identity = self.identity_conv(x) - else: - identity = x - x = self.body(x) - x = x + identity - x = self.activ(x) - return x - - -class ResInitBlock(nn.Module): - """ - ResNet specific initial block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - """ - def __init__(self, - in_channels, - out_channels): - super(ResInitBlock, self).__init__() - self.conv = conv7x7_block( - in_channels=in_channels, - out_channels=out_channels, - stride=2) - self.pool = nn.MaxPool2d( - kernel_size=3, - stride=2, - padding=1) - - def forward(self, x): - x = self.conv(x) - x = self.pool(x) - return x - - -class ResNet(nn.Module): - """ - ResNet model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - channels : list of list of int - Number of output channels for each unit. - init_block_channels : int - Number of output channels for the initial unit. - bottleneck : bool - Whether to use a bottleneck or simple block in units. - conv1_stride : bool - Whether to use stride in the first or the second convolution layer in units. - in_channels : int, default 3 - Number of input channels. - in_size : tuple of two ints, default (224, 224) - Spatial size of the expected input image. - num_classes : int, default 1000 - Number of classification classes. - """ - def __init__(self, - channels, - init_block_channels, - bottleneck, - conv1_stride, - in_channels=3, - in_size=(224, 224), - num_classes=1000): - super(ResNet, self).__init__() - self.in_size = in_size - self.num_classes = num_classes - - self.features = nn.Sequential() - self.features.add_module("init_block", ResInitBlock( - in_channels=in_channels, - out_channels=init_block_channels)) - in_channels = init_block_channels - for i, channels_per_stage in enumerate(channels): - stage = nn.Sequential() - for j, out_channels in enumerate(channels_per_stage): - stride = 2 if (j == 0) and (i != 0) else 1 - stage.add_module("unit{}".format(j + 1), ResUnit( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - bottleneck=bottleneck, - conv1_stride=conv1_stride)) - in_channels = out_channels - self.features.add_module("stage{}".format(i + 1), stage) - self.features.add_module("final_pool", nn.AvgPool2d( - kernel_size=7, - stride=1)) - - self.output = nn.Linear( - in_features=in_channels, - out_features=num_classes) - - self._init_params() - - def _init_params(self): - for name, module in self.named_modules(): - if isinstance(module, nn.Conv2d): - nn.init.kaiming_uniform_(module.weight) - if module.bias is not None: - nn.init.constant_(module.bias, 0) - - def forward(self, x): - x = self.features(x) - x = x.view(x.size(0), -1) - x = self.output(x) - return x - - -def get_resnet(blocks, - bottleneck=None, - conv1_stride=True, - width_scale=1.0, - model_name=None, - pretrained=False, - root=os.path.join("~", ".torch", "models"), - **kwargs): - """ - Create ResNet model with specific parameters. - - Parameters: - ---------- - blocks : int - Number of blocks. - bottleneck : bool, default None - Whether to use a bottleneck or simple block in units. - conv1_stride : bool, default True - Whether to use stride in the first or the second convolution layer in units. - width_scale : float, default 1.0 - Scale factor for width of layers. - model_name : str or None, default None - Model name for loading pretrained model. - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - if bottleneck is None: - bottleneck = (blocks >= 50) - - if blocks == 10: - layers = [1, 1, 1, 1] - elif blocks == 12: - layers = [2, 1, 1, 1] - elif blocks == 14 and not bottleneck: - layers = [2, 2, 1, 1] - elif (blocks == 14) and bottleneck: - layers = [1, 1, 1, 1] - elif blocks == 16: - layers = [2, 2, 2, 1] - elif blocks == 18: - layers = [2, 2, 2, 2] - elif (blocks == 26) and not bottleneck: - layers = [3, 3, 3, 3] - elif (blocks == 26) and bottleneck: - layers = [2, 2, 2, 2] - elif blocks == 34: - layers = [3, 4, 6, 3] - elif (blocks == 38) and bottleneck: - layers = [3, 3, 3, 3] - elif blocks == 50: - layers = [3, 4, 6, 3] - elif blocks == 101: - layers = [3, 4, 23, 3] - elif blocks == 152: - layers = [3, 8, 36, 3] - elif blocks == 200: - layers = [3, 24, 36, 3] - else: - raise ValueError("Unsupported ResNet with number of blocks: {}".format(blocks)) - - if bottleneck: - assert (sum(layers) * 3 + 2 == blocks) - else: - assert (sum(layers) * 2 + 2 == blocks) - - init_block_channels = 64 - channels_per_layers = [64, 128, 256, 512] - - if bottleneck: - bottleneck_factor = 4 - channels_per_layers = [ci * bottleneck_factor for ci in channels_per_layers] - - channels = [[ci] * li for (ci, li) in zip(channels_per_layers, layers)] - - if width_scale != 1.0: - channels = [[int(cij * width_scale) if (i != len(channels) - 1) or (j != len(ci) - 1) else cij - for j, cij in enumerate(ci)] for i, ci in enumerate(channels)] - init_block_channels = int(init_block_channels * width_scale) - - net = ResNet( - channels=channels, - init_block_channels=init_block_channels, - bottleneck=bottleneck, - conv1_stride=conv1_stride, - **kwargs) - - if pretrained: - if (model_name is None) or (not model_name): - raise ValueError("Parameter `model_name` should be properly initialized for loading pretrained model.") - from .model_store import download_model - download_model( - net=net, - model_name=model_name, - local_model_store_dir_path=root) - - return net - - -def resnet10(**kwargs): - """ - ResNet-10 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=10, model_name="resnet10", **kwargs) - - -def resnet12(**kwargs): - """ - ResNet-12 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=12, model_name="resnet12", **kwargs) - - -def resnet14(**kwargs): - """ - ResNet-14 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=14, model_name="resnet14", **kwargs) - - -def resnetbc14b(**kwargs): - """ - ResNet-BC-14b model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model (bottleneck compressed). - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=14, bottleneck=True, conv1_stride=False, model_name="resnetbc14b", **kwargs) - - -def resnet16(**kwargs): - """ - ResNet-16 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=16, model_name="resnet16", **kwargs) - - -def resnet18_wd4(**kwargs): - """ - ResNet-18 model with 0.25 width scale from 'Deep Residual Learning for Image Recognition,' - https://arxiv.org/abs/1512.03385. It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=18, width_scale=0.25, model_name="resnet18_wd4", **kwargs) - - -def resnet18_wd2(**kwargs): - """ - ResNet-18 model with 0.5 width scale from 'Deep Residual Learning for Image Recognition,' - https://arxiv.org/abs/1512.03385. It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=18, width_scale=0.5, model_name="resnet18_wd2", **kwargs) - - -def resnet18_w3d4(**kwargs): - """ - ResNet-18 model with 0.75 width scale from 'Deep Residual Learning for Image Recognition,' - https://arxiv.org/abs/1512.03385. It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=18, width_scale=0.75, model_name="resnet18_w3d4", **kwargs) - - -def resnet18(**kwargs): - """ - ResNet-18 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=18, model_name="resnet18", **kwargs) - - -def resnet26(**kwargs): - """ - ResNet-26 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=26, bottleneck=False, model_name="resnet26", **kwargs) - - -def resnetbc26b(**kwargs): - """ - ResNet-BC-26b model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model (bottleneck compressed). - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=26, bottleneck=True, conv1_stride=False, model_name="resnetbc26b", **kwargs) - - -def resnet34(**kwargs): - """ - ResNet-34 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=34, model_name="resnet34", **kwargs) - - -def resnetbc38b(**kwargs): - """ - ResNet-BC-38b model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model (bottleneck compressed). - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=38, bottleneck=True, conv1_stride=False, model_name="resnetbc38b", **kwargs) - - -def resnet50(**kwargs): - """ - ResNet-50 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=50, model_name="resnet50", **kwargs) - - -def resnet50b(**kwargs): - """ - ResNet-50 model with stride at the second convolution in bottleneck block from 'Deep Residual Learning for Image - Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=50, conv1_stride=False, model_name="resnet50b", **kwargs) - - -def resnet101(**kwargs): - """ - ResNet-101 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=101, model_name="resnet101", **kwargs) - - -def resnet101b(**kwargs): - """ - ResNet-101 model with stride at the second convolution in bottleneck block from 'Deep Residual Learning for Image - Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=101, conv1_stride=False, model_name="resnet101b", **kwargs) - - -def resnet152(**kwargs): - """ - ResNet-152 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=152, model_name="resnet152", **kwargs) - - -def resnet152b(**kwargs): - """ - ResNet-152 model with stride at the second convolution in bottleneck block from 'Deep Residual Learning for Image - Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=152, conv1_stride=False, model_name="resnet152b", **kwargs) - - -def resnet200(**kwargs): - """ - ResNet-200 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=200, model_name="resnet200", **kwargs) - - -def resnet200b(**kwargs): - """ - ResNet-200 model with stride at the second convolution in bottleneck block from 'Deep Residual Learning for Image - Recognition,' https://arxiv.org/abs/1512.03385. It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=200, conv1_stride=False, model_name="resnet200b", **kwargs) - - -def _calc_width(net): - import numpy as np - net_params = filter(lambda p: p.requires_grad, net.parameters()) - weight_count = 0 - for param in net_params: - weight_count += np.prod(param.size()) - return weight_count - - -def _test(): - import torch - - pretrained = False - - models = [ - resnet10, - resnet12, - resnet14, - resnetbc14b, - resnet16, - resnet18_wd4, - resnet18_wd2, - resnet18_w3d4, - resnet18, - resnet26, - resnetbc26b, - resnet34, - resnetbc38b, - resnet50, - resnet50b, - resnet101, - resnet101b, - resnet152, - resnet152b, - resnet200, - resnet200b, - ] - - for model in models: - - net = model(pretrained=pretrained) - - # net.train() - net.eval() - weight_count = _calc_width(net) - print("m={}, {}".format(model.__name__, weight_count)) - assert (model != resnet10 or weight_count == 5418792) - assert (model != resnet12 or weight_count == 5492776) - assert (model != resnet14 or weight_count == 5788200) - assert (model != resnetbc14b or weight_count == 10064936) - assert (model != resnet16 or weight_count == 6968872) - assert (model != resnet18_wd4 or weight_count == 3937400) - assert (model != resnet18_wd2 or weight_count == 5804296) - assert (model != resnet18_w3d4 or weight_count == 8476056) - assert (model != resnet18 or weight_count == 11689512) - assert (model != resnet26 or weight_count == 17960232) - assert (model != resnetbc26b or weight_count == 15995176) - assert (model != resnet34 or weight_count == 21797672) - assert (model != resnetbc38b or weight_count == 21925416) - assert (model != resnet50 or weight_count == 25557032) - assert (model != resnet50b or weight_count == 25557032) - assert (model != resnet101 or weight_count == 44549160) - assert (model != resnet101b or weight_count == 44549160) - assert (model != resnet152 or weight_count == 60192808) - assert (model != resnet152b or weight_count == 60192808) - assert (model != resnet200 or weight_count == 64673832) - assert (model != resnet200b or weight_count == 64673832) - - batch = 4 - x = torch.randn(batch, 3, 224, 224) - y = net(x) - y.sum().backward() - assert (tuple(y.size()) == (batch, 1000)) - - -if __name__ == "__main__": - _test() diff --git a/cv/classification/resnext50_32x4d/pytorch/resnext.py b/cv/classification/resnext50_32x4d/pytorch/resnext.py deleted file mode 100644 index 2f677a920..000000000 --- a/cv/classification/resnext50_32x4d/pytorch/resnext.py +++ /dev/null @@ -1,470 +0,0 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright Declaration: This software, including all of its code and documentation, -# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX -# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright -# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar -# CoreX. No user of this software shall have any right, ownership or interest in this software and -# any use of this software shall be in compliance with the terms and conditions of the End User -# License Agreement. -""" - ResNeXt for ImageNet-1K, implemented in PyTorch. - Original paper: 'Aggregated Residual Transformations for Deep Neural Networks,' http://arxiv.org/abs/1611.05431. -""" - -__all__ = ['ResNeXt', 'resnext14_16x4d', 'resnext14_32x2d', 'resnext14_32x4d', 'resnext26_16x4d', 'resnext26_32x2d', - 'resnext26_32x4d', 'resnext38_32x4d', 'resnext50_32x4d', 'resnext101_32x4d', 'resnext101_64x4d', - 'ResNeXtBottleneck', 'ResNeXtUnit'] - -import os -import math -import torch.nn as nn -import torch.nn.init as init -from common import conv1x1_block, conv3x3_block -from resnet import ResInitBlock - - -class ResNeXtBottleneck(nn.Module): - """ - ResNeXt bottleneck block for residual path in ResNeXt unit. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the convolution. - cardinality: int - Number of groups. - bottleneck_width: int - Width of bottleneck block. - bottleneck_factor : int, default 4 - Bottleneck factor. - """ - def __init__(self, - in_channels, - out_channels, - stride, - cardinality, - bottleneck_width, - bottleneck_factor=4): - super(ResNeXtBottleneck, self).__init__() - mid_channels = out_channels // bottleneck_factor - D = int(math.floor(mid_channels * (bottleneck_width / 64.0))) - group_width = cardinality * D - - self.conv1 = conv1x1_block( - in_channels=in_channels, - out_channels=group_width) - self.conv2 = conv3x3_block( - in_channels=group_width, - out_channels=group_width, - stride=stride, - groups=cardinality) - self.conv3 = conv1x1_block( - in_channels=group_width, - out_channels=out_channels, - activation=None) - - def forward(self, x): - x = self.conv1(x) - x = self.conv2(x) - x = self.conv3(x) - return x - - -class ResNeXtUnit(nn.Module): - """ - ResNeXt unit with residual connection. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the convolution. - cardinality: int - Number of groups. - bottleneck_width: int - Width of bottleneck block. - """ - def __init__(self, - in_channels, - out_channels, - stride, - cardinality, - bottleneck_width): - super(ResNeXtUnit, self).__init__() - self.resize_identity = (in_channels != out_channels) or (stride != 1) - - self.body = ResNeXtBottleneck( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - cardinality=cardinality, - bottleneck_width=bottleneck_width) - if self.resize_identity: - self.identity_conv = conv1x1_block( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - activation=None) - self.activ = nn.ReLU(inplace=True) - - def forward(self, x): - if self.resize_identity: - identity = self.identity_conv(x) - else: - identity = x - x = self.body(x) - x = x + identity - x = self.activ(x) - return x - - -class ResNeXt(nn.Module): - """ - ResNeXt model from 'Aggregated Residual Transformations for Deep Neural Networks,' http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - channels : list of list of int - Number of output channels for each unit. - init_block_channels : int - Number of output channels for the initial unit. - cardinality: int - Number of groups. - bottleneck_width: int - Width of bottleneck block. - in_channels : int, default 3 - Number of input channels. - in_size : tuple of two ints, default (224, 224) - Spatial size of the expected input image. - num_classes : int, default 1000 - Number of classification classes. - """ - def __init__(self, - channels, - init_block_channels, - cardinality, - bottleneck_width, - in_channels=3, - in_size=(224, 224), - num_classes=1000): - super(ResNeXt, self).__init__() - self.in_size = in_size - self.num_classes = num_classes - - self.features = nn.Sequential() - self.features.add_module("init_block", ResInitBlock( - in_channels=in_channels, - out_channels=init_block_channels)) - in_channels = init_block_channels - for i, channels_per_stage in enumerate(channels): - stage = nn.Sequential() - for j, out_channels in enumerate(channels_per_stage): - stride = 2 if (j == 0) and (i != 0) else 1 - stage.add_module("unit{}".format(j + 1), ResNeXtUnit( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - cardinality=cardinality, - bottleneck_width=bottleneck_width)) - in_channels = out_channels - self.features.add_module("stage{}".format(i + 1), stage) - self.features.add_module("final_pool", nn.AvgPool2d( - kernel_size=7, - stride=1)) - - self.output = nn.Linear( - in_features=in_channels, - out_features=num_classes) - - self._init_params() - - def _init_params(self): - for name, module in self.named_modules(): - if isinstance(module, nn.Conv2d): - init.kaiming_uniform_(module.weight) - if module.bias is not None: - init.constant_(module.bias, 0) - - def forward(self, x): - x = self.features(x) - x = x.view(x.size(0), -1) - x = self.output(x) - return x - - -def get_resnext(blocks, - cardinality, - bottleneck_width, - model_name=None, - pretrained=False, - root=os.path.join("~", ".torch", "models"), - **kwargs): - """ - Create ResNeXt model with specific parameters. - - Parameters: - ---------- - blocks : int - Number of blocks. - cardinality: int - Number of groups. - bottleneck_width: int - Width of bottleneck block. - model_name : str or None, default None - Model name for loading pretrained model. - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - - if blocks == 14: - layers = [1, 1, 1, 1] - elif blocks == 26: - layers = [2, 2, 2, 2] - elif blocks == 38: - layers = [3, 3, 3, 3] - elif blocks == 50: - layers = [3, 4, 6, 3] - elif blocks == 101: - layers = [3, 4, 23, 3] - else: - raise ValueError("Unsupported ResNeXt with number of blocks: {}".format(blocks)) - - assert (sum(layers) * 3 + 2 == blocks) - - init_block_channels = 64 - channels_per_layers = [256, 512, 1024, 2048] - - channels = [[ci] * li for (ci, li) in zip(channels_per_layers, layers)] - - net = ResNeXt( - channels=channels, - init_block_channels=init_block_channels, - cardinality=cardinality, - bottleneck_width=bottleneck_width, - **kwargs) - - if pretrained: - if (model_name is None) or (not model_name): - raise ValueError("Parameter `model_name` should be properly initialized for loading pretrained model.") - from .model_store import download_model - download_model( - net=net, - model_name=model_name, - local_model_store_dir_path=root) - - return net - - -def resnext14_16x4d(**kwargs): - """ - ResNeXt-14 (16x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=14, cardinality=16, bottleneck_width=4, model_name="resnext14_16x4d", **kwargs) - - -def resnext14_32x2d(**kwargs): - """ - ResNeXt-14 (32x2d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=14, cardinality=32, bottleneck_width=2, model_name="resnext14_32x2d", **kwargs) - - -def resnext14_32x4d(**kwargs): - """ - ResNeXt-14 (32x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=14, cardinality=32, bottleneck_width=4, model_name="resnext14_32x4d", **kwargs) - - -def resnext26_16x4d(**kwargs): - """ - ResNeXt-26 (16x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=26, cardinality=16, bottleneck_width=4, model_name="resnext26_16x4d", **kwargs) - - -def resnext26_32x2d(**kwargs): - """ - ResNeXt-26 (32x2d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=26, cardinality=32, bottleneck_width=2, model_name="resnext26_32x2d", **kwargs) - - -def resnext26_32x4d(**kwargs): - """ - ResNeXt-26 (32x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=26, cardinality=32, bottleneck_width=4, model_name="resnext26_32x4d", **kwargs) - - -def resnext38_32x4d(**kwargs): - """ - ResNeXt-38 (32x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=38, cardinality=32, bottleneck_width=4, model_name="resnext38_32x4d", **kwargs) - - -def resnext50_32x4d(**kwargs): - """ - ResNeXt-50 (32x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=50, cardinality=32, bottleneck_width=4, model_name="resnext50_32x4d", **kwargs) - - -def resnext101_32x4d(**kwargs): - """ - ResNeXt-101 (32x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=101, cardinality=32, bottleneck_width=4, model_name="resnext101_32x4d", **kwargs) - - -def resnext101_64x4d(**kwargs): - """ - ResNeXt-101 (64x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=101, cardinality=64, bottleneck_width=4, model_name="resnext101_64x4d", **kwargs) - - -def _calc_width(net): - import numpy as np - net_params = filter(lambda p: p.requires_grad, net.parameters()) - weight_count = 0 - for param in net_params: - weight_count += np.prod(param.size()) - return weight_count - - -def _test(): - import torch - - pretrained = False - - models = [ - resnext14_16x4d, - resnext14_32x2d, - resnext14_32x4d, - resnext26_16x4d, - resnext26_32x2d, - resnext26_32x4d, - resnext38_32x4d, - resnext50_32x4d, - resnext101_32x4d, - resnext101_64x4d, - ] - - for model in models: - - net = model(pretrained=pretrained) - - # net.train() - net.eval() - weight_count = _calc_width(net) - print("m={}, {}".format(model.__name__, weight_count)) - assert (model != resnext14_16x4d or weight_count == 7127336) - assert (model != resnext14_32x2d or weight_count == 7029416) - assert (model != resnext14_32x4d or weight_count == 9411880) - assert (model != resnext26_16x4d or weight_count == 10119976) - assert (model != resnext26_32x2d or weight_count == 9924136) - assert (model != resnext26_32x4d or weight_count == 15389480) - assert (model != resnext38_32x4d or weight_count == 21367080) - assert (model != resnext50_32x4d or weight_count == 25028904) - assert (model != resnext101_32x4d or weight_count == 44177704) - assert (model != resnext101_64x4d or weight_count == 83455272) - - x = torch.randn(1, 3, 224, 224) - y = net(x) - y.sum().backward() - assert (tuple(y.size()) == (1, 1000)) - - -if __name__ == "__main__": - _test() diff --git a/cv/classification/resnext50_32x4d/pytorch/run_train.py b/cv/classification/resnext50_32x4d/pytorch/run_train.py deleted file mode 100644 index fdadf331a..000000000 --- a/cv/classification/resnext50_32x4d/pytorch/run_train.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -import os -import sys - -import torchvision - -sys.path.append("../../torchvision/pytorch") - -from train import train_model -from utils import padding_conv_channel_to_4 -import resnext - -def create_model(args): - model = resnext.__dict__[args.model](pretrained=args.pretrained, num_classes=args.num_classes) - if args.nhwc: - args.padding_channel = True - model.features[0].conv.conv = padding_conv_channel_to_4(model.features[0].conv.conv) - return model - - -train_model(create_model) diff --git a/cv/classification/resnext50_32x4d/pytorch/train.py b/cv/classification/resnext50_32x4d/pytorch/train.py new file mode 100644 index 000000000..78e8a72f6 --- /dev/null +++ b/cv/classification/resnext50_32x4d/pytorch/train.py @@ -0,0 +1,313 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +import datetime +import os + +import time +from typing_extensions import runtime + +import torch +import torch.utils.data + +try: + from torch.cuda.amp import autocast, GradScaler + scaler = GradScaler() +except: + autocast = None + scaler = None + + +from torch import nn +import torch.distributed as dist +import torchvision + +from utils_ import (MetricLogger, SmoothedValue, accuracy, mkdir,\ + init_distributed_mode, manual_seed,\ + is_main_process, save_on_master, get_world_size) + +from dataloader.classification import get_datasets, create_dataloader + + +def compute_loss(model, image, target, criterion): + output = model(image) + if not isinstance(output, (tuple, list)): + output = [output] + losses = [] + for out in output: + losses.append(criterion(out, target)) + loss = sum(losses) + return loss, output[0] + + +def train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, print_freq, amp=False, use_dali=False): + model.train() + metric_logger = MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', SmoothedValue(window_size=1, fmt='{value}')) + metric_logger.add_meter('img/s', SmoothedValue(window_size=10, fmt='{value}')) + + header = 'Epoch: [{}]'.format(epoch) + all_fps = [] + for data in metric_logger.log_every(data_loader, print_freq, header): + if use_dali: + image, target = data[0]["data"], data[0]["label"][:, 0].long() + else: + image, target = data + start_time = time.time() + image, target = image.to(device), target.to(device) + if autocast is None or not amp: + loss, output = compute_loss(model, image, target, criterion) + else: + with autocast(): + loss, output = compute_loss(model, image, target, criterion) + + if torch.any(torch.isnan(loss)): + print("[Error] train loss is nan ") + raise RuntimeError + + optimizer.zero_grad() + if scaler is not None and amp: + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + else: + loss.backward() + optimizer.step() + + torch.cuda.synchronize() + end_time = time.time() + + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + batch_size = image.shape[0] + metric_logger.update(loss=loss.item(), lr=optimizer.param_groups[0]["lr"]) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + fps = batch_size / (end_time - start_time) * get_world_size() + metric_logger.meters['img/s'].update(fps) + all_fps.append(fps) + + print(header, 'Avg img/s:', sum(all_fps) / len(all_fps)) + + +def evaluate(model, criterion, data_loader, device, print_freq=100, use_dali=False): + model.eval() + metric_logger = MetricLogger(delimiter=" ") + header = 'Test:' + with torch.no_grad(): + for data in metric_logger.log_every(data_loader, print_freq, header): + if use_dali: + image, target = data[0]["data"], data[0]["label"][:, 0].long() + else: + image, target = data + image = image.to(device, non_blocking=True) + target = target.to(device, non_blocking=True) + output = model(image) + loss = criterion(output, target) + + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + # FIXME need to take into account that the datasets + # could have been padded in distributed setup + batch_size = image.shape[0] + metric_logger.update(loss=loss.item()) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + # gather the stats from all processes + metric_logger.synchronize_between_processes() + + print(' * Acc@1 {top1.global_avg:.3f} Acc@5 {top5.global_avg:.3f}' + .format(top1=metric_logger.acc1, top5=metric_logger.acc5)) + return metric_logger.acc1.global_avg + + +def _get_cache_path(filepath): + import hashlib + h = hashlib.sha1(filepath.encode()).hexdigest() + cache_path = os.path.join("~", ".torch", "vision", "datasets", "imagefolder", h[:10] + ".pt") + cache_path = os.path.expanduser(cache_path) + return cache_path + + +def main(args): + if args.output_dir: + mkdir(args.output_dir) + + init_distributed_mode(args) + print(args) + + device = torch.device(args.device) + + manual_seed(args.seed, deterministic=False) + # torch.backends.cudnn.benchmark = True + + # WARN: + if dist.is_initialized(): + num_gpu = dist.get_world_size() + else: + num_gpu = 1 + + global_batch_size = num_gpu * args.batch_size + + train_dir = os.path.join(args.data_path, 'train') + val_dir = os.path.join(args.data_path, 'val') + + num_classes = len(os.listdir(train_dir)) + if 0 < num_classes < 13: + if global_batch_size > 512: + if is_main_process(): + print("WARN: Updating global batch size to 512, avoid non-convergence when training small dataset.") + args.batch_size = 512 // num_gpu + + if args.pretrained: + num_classes = 1000 + + data_loader, data_loader_test = create_dataloader(train_dir, val_dir, args) + + print("Creating model") + model = torchvision.models.__dict__[args.model](pretrained=args.pretrained, num_classes=num_classes) + model.to(device) + if args.distributed and args.sync_bn: + model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model) + + criterion = nn.CrossEntropyLoss() + + opt_name = args.opt.lower() + if opt_name == 'sgd': + optimizer = torch.optim.SGD( + model.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.weight_decay) + elif opt_name == 'rmsprop': + optimizer = torch.optim.RMSprop(model.parameters(), lr=args.lr, momentum=args.momentum, + weight_decay=args.weight_decay, eps=0.0316, alpha=0.9) + else: + raise RuntimeError("Invalid optimizer {}. Only SGD and RMSprop are supported.".format(args.opt)) + + lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=args.lr_step_size, gamma=args.lr_gamma) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + if args.resume: + checkpoint = torch.load(args.resume, map_location='cpu') + model_without_ddp.load_state_dict(checkpoint['model']) + optimizer.load_state_dict(checkpoint['optimizer']) + lr_scheduler.load_state_dict(checkpoint['lr_scheduler']) + args.start_epoch = checkpoint['epoch'] + 1 + + if args.test_only: + evaluate(model, criterion, data_loader_test, device=device) + return + + print("Start training") + start_time = time.time() + best_acc = 0 + for epoch in range(args.start_epoch, args.epochs): + epoch_start_time = time.time() + if args.distributed and not args.dali: + data_loader.sampler.set_epoch(epoch) + train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, args.print_freq, args.amp, use_dali=args.dali) + lr_scheduler.step() + acc_avg = evaluate(model, criterion, data_loader_test, device=device, use_dali=args.dali) + if acc_avg > best_acc: + if args.output_dir: + checkpoint = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'lr_scheduler': lr_scheduler.state_dict(), + 'epoch': epoch, + 'args': args} + save_on_master( + checkpoint, + os.path.join(args.output_dir, 'model_best.pth')) + epoch_total_time = time.time() - epoch_start_time + epoch_total_time_str = str(datetime.timedelta(seconds=int(epoch_total_time))) + print('epoch time {}'.format(epoch_total_time_str)) + best_acc = acc_avg + + if args.dali: + data_loader.reset() + data_loader_test.reset() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +def get_args_parser(add_help=True): + import argparse + parser = argparse.ArgumentParser(description='PyTorch Classification Training', add_help=add_help) + + parser.add_argument('--data-path', default='/datasets01/imagenet_full_size/061417/', help='dataset') + parser.add_argument('--model', default='resnet18', help='model') + parser.add_argument('--device', default='cuda', help='device') + parser.add_argument('-b', '--batch-size', default=32, type=int) + parser.add_argument('--epochs', default=90, type=int, metavar='N', + help='number of total epochs to run') + parser.add_argument('-j', '--workers', default=4, type=int, metavar='N', + help='number of data loading workers (default: 4)') + parser.add_argument('--opt', default='sgd', type=str, help='optimizer') + parser.add_argument('--lr', default=0.1, type=float, help='initial learning rate') + parser.add_argument('--momentum', default=0.9, type=float, metavar='M', + help='momentum') + parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float, + metavar='W', help='weight decay (default: 1e-4)', + dest='weight_decay') + parser.add_argument('--lr-step-size', default=30, type=int, help='decrease lr every step-size epochs') + parser.add_argument('--lr-gamma', default=0.1, type=float, help='decrease lr by a factor of lr-gamma') + parser.add_argument('--print-freq', default=10, type=int, help='print frequency') + parser.add_argument('--output-dir', default='.', help='path where to save') + parser.add_argument('--resume', default='', help='resume from checkpoint') + parser.add_argument('--start-epoch', default=0, type=int, metavar='N', + help='start epoch') + parser.add_argument( + "--cache-dataset", + dest="cache_dataset", + help="Cache the datasets for quicker initialization. It also serializes the transforms", + action="store_true", + ) + parser.add_argument( + "--sync-bn", + dest="sync_bn", + help="Use sync batch norm", + action="store_true", + ) + parser.add_argument( + "--test-only", + dest="test_only", + help="Only test the model", + action="store_true", + ) + parser.add_argument( + "--pretrained", + dest="pretrained", + help="Use pre-trained models from the modelzoo", + action="store_true", + ) + parser.add_argument('--auto-augment', default=None, help='auto augment policy (default: None)') + parser.add_argument('--random-erase', default=0.0, type=float, help='random erasing probability (default: 0.0)') + parser.add_argument( + "--dali", + help="Use dali as dataloader", + default=False, + action="store_true", + ) + + # distributed training parameters + parser.add_argument('--local_rank', '--local-rank', default=-1, type=int, + help='Local rank') + parser.add_argument('--world-size', default=1, type=int, + help='number of distributed processes') + parser.add_argument('--dist-url', default='env://', help='url used to set up distributed training') + parser.add_argument('--amp', action='store_true', help='Automatic Mixed Precision training') + parser.add_argument('--seed', default=42, type=int, help='Random seed') + return parser + + +if __name__ == "__main__": + args = get_args_parser().parse_args() + try: + from dltest import show_training_arguments + show_training_arguments(args) + except: + pass + main(args) diff --git a/cv/classification/resnext50_32x4d/pytorch/train_resnext50_32x4d_amp_dist.sh b/cv/classification/resnext50_32x4d/pytorch/train_resnext50_32x4d_amp_dist.sh index 4fa71c745..5082749d5 100755 --- a/cv/classification/resnext50_32x4d/pytorch/train_resnext50_32x4d_amp_dist.sh +++ b/cv/classification/resnext50_32x4d/pytorch/train_resnext50_32x4d_amp_dist.sh @@ -30,6 +30,6 @@ if [ ! -z "${DEBUG}" ];then PYTHONAR="${PYTHONAR} -m pdb" fi cd ${ROOT_DIR} -python3 $PYTHONARG ${ROOT_DIR}/run_train.py \ - --model resnext50_32x4d --dali --dali-cpu --data-path $DATA_PATH \ - --lr 0.08 --momentum 0.9 --wd 0.0001 --batch-size 64 --deterministic --amp --nhwc --momentum 0.875 "$@" +python3 $PYTHONARG train.py \ + --model resnext50_32x4d --data-path $DATA_PATH \ + --lr 1e-2 --wd 0.0001 --batch-size 128 --amp "$@" diff --git a/cv/classification/resnext50_32x4d/pytorch/utils_.py b/cv/classification/resnext50_32x4d/pytorch/utils_.py new file mode 100644 index 000000000..3d34c4df0 --- /dev/null +++ b/cv/classification/resnext50_32x4d/pytorch/utils_.py @@ -0,0 +1,156 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque, OrderedDict +import copy +import datetime +import hashlib +import time +import torch +import torch.distributed as dist + +import errno +import os + +from common_utils import * + + +def accuracy(output, target, topk=(1,)): + """Computes the accuracy over the k top predictions for the specified values of k""" + with torch.no_grad(): + maxk = max(topk) + batch_size = target.size(0) + + _, pred = output.topk(maxk, 1, True, True) + pred = pred.t() + correct = pred.eq(target[None]) + + res = [] + for k in topk: + correct_k = correct[:k].flatten().sum(dtype=torch.float32) + res.append(correct_k * (100.0 / batch_size)) + return res + + +def average_checkpoints(inputs): + """Loads checkpoints from inputs and returns a model with averaged weights. Original implementation taken from: + https://github.com/pytorch/fairseq/blob/a48f235636557b8d3bc4922a6fa90f3a0fa57955/scripts/average_checkpoints.py#L16 + + Args: + inputs (List[str]): An iterable of string paths of checkpoints to load from. + Returns: + A dict of string keys mapping to various values. The 'model' key + from the returned dict should correspond to an OrderedDict mapping + string parameter names to torch Tensors. + """ + params_dict = OrderedDict() + params_keys = None + new_state = None + num_models = len(inputs) + for fpath in inputs: + with open(fpath, "rb") as f: + state = torch.load( + f, + map_location=( + lambda s, _: torch.serialization.default_restore_location(s, "cpu") + ), + ) + # Copies over the settings from the first checkpoint + if new_state is None: + new_state = state + model_params = state["model"] + model_params_keys = list(model_params.keys()) + if params_keys is None: + params_keys = model_params_keys + elif params_keys != model_params_keys: + raise KeyError( + "For checkpoint {}, expected list of params: {}, " + "but found: {}".format(f, params_keys, model_params_keys) + ) + for k in params_keys: + p = model_params[k] + if isinstance(p, torch.HalfTensor): + p = p.float() + if k not in params_dict: + params_dict[k] = p.clone() + # NOTE: clone() is needed in case of p is a shared parameter + else: + params_dict[k] += p + averaged_params = OrderedDict() + for k, v in params_dict.items(): + averaged_params[k] = v + if averaged_params[k].is_floating_point(): + averaged_params[k].div_(num_models) + else: + averaged_params[k] //= num_models + new_state["model"] = averaged_params + return new_state + + +def store_model_weights(model, checkpoint_path, checkpoint_key='model', strict=True): + """ + This method can be used to prepare weights files for new models. It receives as + input a model architecture and a checkpoint from the training script and produces + a file with the weights ready for release. + + Examples: + from torchvision import models as M + + # Classification + model = M.mobilenet_v3_large(pretrained=False) + print(store_model_weights(model, './class.pth')) + + # Quantized Classification + model = M.quantization.mobilenet_v3_large(pretrained=False, quantize=False) + model.fuse_model() + model.qconfig = torch.quantization.get_default_qat_qconfig('qnnpack') + _ = torch.quantization.prepare_qat(model, inplace=True) + print(store_model_weights(model, './qat.pth')) + + # Object Detection + model = M.detection.fasterrcnn_mobilenet_v3_large_fpn(pretrained=False, pretrained_backbone=False) + print(store_model_weights(model, './obj.pth')) + + # Segmentation + model = M.segmentation.deeplabv3_mobilenet_v3_large(pretrained=False, pretrained_backbone=False, aux_loss=True) + print(store_model_weights(model, './segm.pth', strict=False)) + + Args: + model (pytorch.nn.Module): The model on which the weights will be loaded for validation purposes. + checkpoint_path (str): The path of the checkpoint we will load. + checkpoint_key (str, optional): The key of the checkpoint where the model weights are stored. + Default: "model". + strict (bool): whether to strictly enforce that the keys + in :attr:`state_dict` match the keys returned by this module's + :meth:`~torch.nn.Module.state_dict` function. Default: ``True`` + + Returns: + output_path (str): The location where the weights are saved. + """ + # Store the new model next to the checkpoint_path + checkpoint_path = os.path.abspath(checkpoint_path) + output_dir = os.path.dirname(checkpoint_path) + + # Deep copy to avoid side-effects on the model object. + model = copy.deepcopy(model) + checkpoint = torch.load(checkpoint_path, map_location='cpu') + + # Load the weights to the model to validate that everything works + # and remove unnecessary weights (such as auxiliaries, etc) + model.load_state_dict(checkpoint[checkpoint_key], strict=strict) + + tmp_path = os.path.join(output_dir, str(model.__hash__())) + torch.save(model.state_dict(), tmp_path) + + sha256_hash = hashlib.sha256() + with open(tmp_path, "rb") as f: + # Read and update hash string value in blocks of 4K + for byte_block in iter(lambda: f.read(4096), b""): + sha256_hash.update(byte_block) + hh = sha256_hash.hexdigest() + + output_path = os.path.join(output_dir, "weights-" + str(hh[:8]) + ".pth") + os.replace(tmp_path, output_path) + + return output_path diff --git a/cv/classification/seresnext/pytorch/__init__.py b/cv/classification/seresnext/pytorch/__init__.py new file mode 100644 index 000000000..6faec1658 --- /dev/null +++ b/cv/classification/seresnext/pytorch/__init__.py @@ -0,0 +1,5 @@ +from .utils import * +from .common_utils import * +from .data_loader import * + +__all__ = [k for k in globals().keys() if not k.startswith("_")] diff --git a/cv/classification/seresnext/pytorch/common.py b/cv/classification/seresnext/pytorch/common.py deleted file mode 100644 index 34b391641..000000000 --- a/cv/classification/seresnext/pytorch/common.py +++ /dev/null @@ -1,2338 +0,0 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright Declaration: This software, including all of its code and documentation, -# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX -# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright -# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar -# CoreX. No user of this software shall have any right, ownership or interest in this software and -# any use of this software shall be in compliance with the terms and conditions of the End User -# License Agreement. -""" - Common routines for models in PyTorch. -""" - -__all__ = ['round_channels', 'Identity', 'BreakBlock', 'Swish', 'HSigmoid', 'HSwish', 'get_activation_layer', - 'SelectableDense', 'DenseBlock', 'ConvBlock1d', 'conv1x1', 'conv3x3', 'depthwise_conv3x3', 'ConvBlock', - 'conv1x1_block', 'conv3x3_block', 'conv5x5_block', 'conv7x7_block', 'dwconv_block', 'dwconv3x3_block', - 'dwconv5x5_block', 'dwsconv3x3_block', 'PreConvBlock', 'pre_conv1x1_block', 'pre_conv3x3_block', - 'AsymConvBlock', 'asym_conv3x3_block', 'DeconvBlock', 'deconv3x3_block', 'NormActivation', - 'InterpolationBlock', 'ChannelShuffle', 'ChannelShuffle2', 'SEBlock', 'SABlock', 'SAConvBlock', - 'saconv3x3_block', 'DucBlock', 'IBN', 'DualPathSequential', 'Concurrent', 'SequentialConcurrent', - 'ParametricSequential', 'ParametricConcurrent', 'Hourglass', 'SesquialteralHourglass', - 'MultiOutputSequential', 'ParallelConcurent', 'DualPathParallelConcurent', 'Flatten', 'HeatmapMaxDetBlock'] - -import math -from inspect import isfunction -import torch -import torch.nn as nn -import torch.nn.functional as F -from torch.nn.parameter import Parameter - - -def round_channels(channels, - divisor=8): - """ - Round weighted channel number (make divisible operation). - - Parameters: - ---------- - channels : int or float - Original number of channels. - divisor : int, default 8 - Alignment value. - - Returns: - ------- - int - Weighted number of channels. - """ - rounded_channels = max(int(channels + divisor / 2.0) // divisor * divisor, divisor) - if float(rounded_channels) < 0.9 * channels: - rounded_channels += divisor - return rounded_channels - - -class Identity(nn.Module): - """ - Identity block. - """ - def __init__(self): - super(Identity, self).__init__() - - def forward(self, x): - return x - - def __repr__(self): - return '{name}()'.format(name=self.__class__.__name__) - - -class BreakBlock(nn.Module): - """ - Break coonnection block for hourglass. - """ - def __init__(self): - super(BreakBlock, self).__init__() - - def forward(self, x): - return None - - def __repr__(self): - return '{name}()'.format(name=self.__class__.__name__) - - -class Swish(nn.Module): - """ - Swish activation function from 'Searching for Activation Functions,' https://arxiv.org/abs/1710.05941. - """ - def forward(self, x): - return x * torch.sigmoid(x) - - -class HSigmoid(nn.Module): - """ - Approximated sigmoid function, so-called hard-version of sigmoid from 'Searching for MobileNetV3,' - https://arxiv.org/abs/1905.02244. - """ - def forward(self, x): - return F.relu6(x + 3.0, inplace=True) / 6.0 - - -class HSwish(nn.Module): - """ - H-Swish activation function from 'Searching for MobileNetV3,' https://arxiv.org/abs/1905.02244. - - Parameters: - ---------- - inplace : bool - Whether to use inplace version of the module. - """ - def __init__(self, inplace=False): - super(HSwish, self).__init__() - self.inplace = inplace - - def forward(self, x): - return x * F.relu6(x + 3.0, inplace=self.inplace) / 6.0 - - -def get_activation_layer(activation): - """ - Create activation layer from string/function. - - Parameters: - ---------- - activation : function, or str, or nn.Module - Activation function or name of activation function. - - Returns: - ------- - nn.Module - Activation layer. - """ - assert (activation is not None) - if isfunction(activation): - return activation() - elif isinstance(activation, str): - if activation == "relu": - return nn.ReLU(inplace=True) - elif activation == "relu6": - return nn.ReLU6(inplace=True) - elif activation == "swish": - return Swish() - elif activation == "hswish": - return HSwish(inplace=True) - elif activation == "sigmoid": - return nn.Sigmoid() - elif activation == "hsigmoid": - return HSigmoid() - elif activation == "identity": - return Identity() - else: - raise NotImplementedError() - else: - assert (isinstance(activation, nn.Module)) - return activation - - -class SelectableDense(nn.Module): - """ - Selectable dense layer. - - Parameters: - ---------- - in_features : int - Number of input features. - out_features : int - Number of output features. - bias : bool, default False - Whether the layer uses a bias vector. - num_options : int, default 1 - Number of selectable options. - """ - def __init__(self, - in_features, - out_features, - bias=False, - num_options=1): - super(SelectableDense, self).__init__() - self.in_features = in_features - self.out_features = out_features - self.use_bias = bias - self.num_options = num_options - self.weight = Parameter(torch.Tensor(num_options, out_features, in_features)) - if bias: - self.bias = Parameter(torch.Tensor(num_options, out_features)) - else: - self.register_parameter("bias", None) - - def forward(self, x, indices): - weight = torch.index_select(self.weight, dim=0, index=indices) - x = x.unsqueeze(-1) - x = weight.bmm(x) - x = x.squeeze(dim=-1) - if self.use_bias: - bias = torch.index_select(self.bias, dim=0, index=indices) - x += bias - return x - - def extra_repr(self): - return "in_features={}, out_features={}, bias={}, num_options={}".format( - self.in_features, self.out_features, self.use_bias, self.num_options) - - -class DenseBlock(nn.Module): - """ - Standard dense block with Batch normalization and activation. - - Parameters: - ---------- - in_features : int - Number of input features. - out_features : int - Number of output features. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - def __init__(self, - in_features, - out_features, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - super(DenseBlock, self).__init__() - self.activate = (activation is not None) - self.use_bn = use_bn - - self.fc = nn.Linear( - in_features=in_features, - out_features=out_features, - bias=bias) - if self.use_bn: - self.bn = nn.BatchNorm1d( - num_features=out_features, - eps=bn_eps) - if self.activate: - self.activ = get_activation_layer(activation) - - def forward(self, x): - x = self.fc(x) - if self.use_bn: - x = self.bn(x) - if self.activate: - x = self.activ(x) - return x - - -class ConvBlock1d(nn.Module): - """ - Standard 1D convolution block with Batch normalization and activation. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int - Convolution window size. - stride : int - Strides of the convolution. - padding : int - Padding value for convolution layer. - dilation : int - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - super(ConvBlock1d, self).__init__() - self.activate = (activation is not None) - self.use_bn = use_bn - - self.conv = nn.Conv1d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - if self.use_bn: - self.bn = nn.BatchNorm1d( - num_features=out_channels, - eps=bn_eps) - if self.activate: - self.activ = get_activation_layer(activation) - - def forward(self, x): - x = self.conv(x) - if self.use_bn: - x = self.bn(x) - if self.activate: - x = self.activ(x) - return x - - -def conv1x1(in_channels, - out_channels, - stride=1, - groups=1, - bias=False): - """ - Convolution 1x1 layer. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - """ - return nn.Conv2d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=1, - stride=stride, - groups=groups, - bias=bias) - - -def conv3x3(in_channels, - out_channels, - stride=1, - padding=1, - dilation=1, - groups=1, - bias=False): - """ - Convolution 3x3 layer. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - """ - return nn.Conv2d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - - -def depthwise_conv3x3(channels, - stride=1, - padding=1, - dilation=1, - bias=False): - """ - Depthwise convolution 3x3 layer. - - Parameters: - ---------- - channels : int - Number of input/output channels. - strides : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - """ - return nn.Conv2d( - in_channels=channels, - out_channels=channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - groups=channels, - bias=bias) - - -class ConvBlock(nn.Module): - """ - Standard convolution block with Batch normalization and activation. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - super(ConvBlock, self).__init__() - self.activate = (activation is not None) - self.use_bn = use_bn - self.use_pad = (isinstance(padding, (list, tuple)) and (len(padding) == 4)) - - if self.use_pad: - self.pad = nn.ZeroPad2d(padding=padding) - padding = 0 - self.conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias) - if self.use_bn: - self.bn = nn.BatchNorm2d( - num_features=out_channels, - eps=bn_eps) - if self.activate: - self.activ = get_activation_layer(activation) - - def forward(self, x): - if self.use_pad: - x = self.pad(x) - x = self.conv(x) - if self.use_bn: - x = self.bn(x) - if self.activate: - x = self.activ(x) - return x - - -def conv1x1_block(in_channels, - out_channels, - stride=1, - padding=0, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 1x1 version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 0 - Padding value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return ConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=1, - stride=stride, - padding=padding, - groups=groups, - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - - -def conv3x3_block(in_channels, - out_channels, - stride=1, - padding=1, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 3x3 version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return ConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - - -def conv5x5_block(in_channels, - out_channels, - stride=1, - padding=2, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 5x5 version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 2 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return ConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=5, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - - -def conv7x7_block(in_channels, - out_channels, - stride=1, - padding=3, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 7x7 version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 1 - Strides of the convolution. - padding : int or tuple/list of 2 int, default 3 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return ConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=7, - stride=stride, - padding=padding, - dilation=dilation, - groups=groups, - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - - -def dwconv_block(in_channels, - out_channels, - kernel_size, - stride=1, - padding=1, - dilation=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - Depthwise version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return ConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=out_channels, - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - - -def dwconv3x3_block(in_channels, - out_channels, - stride=1, - padding=1, - dilation=1, - bias=False, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 3x3 depthwise version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return dwconv_block( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias, - bn_eps=bn_eps, - activation=activation) - - -def dwconv5x5_block(in_channels, - out_channels, - stride=1, - padding=2, - dilation=1, - bias=False, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - """ - 5x5 depthwise version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 2 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return dwconv_block( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=5, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias, - bn_eps=bn_eps, - activation=activation) - - -class DwsConvBlock(nn.Module): - """ - Depthwise separable convolution block with BatchNorms and activations at each convolution layers. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - dw_use_bn : bool, default True - Whether to use BatchNorm layer (depthwise convolution block). - pw_use_bn : bool, default True - Whether to use BatchNorm layer (pointwise convolution block). - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - dw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the depthwise convolution block. - pw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the pointwise convolution block. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - dilation=1, - bias=False, - dw_use_bn=True, - pw_use_bn=True, - bn_eps=1e-5, - dw_activation=(lambda: nn.ReLU(inplace=True)), - pw_activation=(lambda: nn.ReLU(inplace=True))): - super(DwsConvBlock, self).__init__() - self.dw_conv = dwconv_block( - in_channels=in_channels, - out_channels=in_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias, - use_bn=dw_use_bn, - bn_eps=bn_eps, - activation=dw_activation) - self.pw_conv = conv1x1_block( - in_channels=in_channels, - out_channels=out_channels, - bias=bias, - use_bn=pw_use_bn, - bn_eps=bn_eps, - activation=pw_activation) - - def forward(self, x): - x = self.dw_conv(x) - x = self.pw_conv(x) - return x - - -def dwsconv3x3_block(in_channels, - out_channels, - stride=1, - padding=1, - dilation=1, - bias=False, - bn_eps=1e-5, - dw_activation=(lambda: nn.ReLU(inplace=True)), - pw_activation=(lambda: nn.ReLU(inplace=True)), - **kwargs): - """ - 3x3 depthwise separable version of the standard convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - dw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the depthwise convolution block. - pw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the pointwise convolution block. - """ - return DwsConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias, - bn_eps=bn_eps, - dw_activation=dw_activation, - pw_activation=pw_activation, - **kwargs) - - -class PreConvBlock(nn.Module): - """ - Convolution block with Batch normalization and ReLU pre-activation. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int or tuple/list of 2 int - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - return_preact : bool, default False - Whether return pre-activation. It's used by PreResNet. - activate : bool, default True - Whether activate the convolution block. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - dilation=1, - bias=False, - use_bn=True, - return_preact=False, - activate=True): - super(PreConvBlock, self).__init__() - self.return_preact = return_preact - self.activate = activate - self.use_bn = use_bn - - if self.use_bn: - self.bn = nn.BatchNorm2d(num_features=in_channels) - if self.activate: - self.activ = nn.ReLU(inplace=True) - self.conv = nn.Conv2d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias) - - def forward(self, x): - if self.use_bn: - x = self.bn(x) - if self.activate: - x = self.activ(x) - if self.return_preact: - x_pre_activ = x - x = self.conv(x) - if self.return_preact: - return x, x_pre_activ - else: - return x - - -def pre_conv1x1_block(in_channels, - out_channels, - stride=1, - bias=False, - use_bn=True, - return_preact=False, - activate=True): - """ - 1x1 version of the pre-activated convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - return_preact : bool, default False - Whether return pre-activation. - activate : bool, default True - Whether activate the convolution block. - """ - return PreConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=1, - stride=stride, - padding=0, - bias=bias, - use_bn=use_bn, - return_preact=return_preact, - activate=activate) - - -def pre_conv3x3_block(in_channels, - out_channels, - stride=1, - padding=1, - dilation=1, - bias=False, - use_bn=True, - return_preact=False, - activate=True): - """ - 3x3 version of the pre-activated convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - return_preact : bool, default False - Whether return pre-activation. - activate : bool, default True - Whether activate the convolution block. - """ - return PreConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - dilation=dilation, - bias=bias, - use_bn=use_bn, - return_preact=return_preact, - activate=activate) - - -class AsymConvBlock(nn.Module): - """ - Asymmetric separable convolution block. - - Parameters: - ---------- - channels : int - Number of input/output channels. - kernel_size : int - Convolution window size. - padding : int - Padding value for convolution layer. - dilation : int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - lw_use_bn : bool, default True - Whether to use BatchNorm layer (leftwise convolution block). - rw_use_bn : bool, default True - Whether to use BatchNorm layer (rightwise convolution block). - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - lw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the leftwise convolution block. - rw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the rightwise convolution block. - """ - def __init__(self, - channels, - kernel_size, - padding, - dilation=1, - groups=1, - bias=False, - lw_use_bn=True, - rw_use_bn=True, - bn_eps=1e-5, - lw_activation=(lambda: nn.ReLU(inplace=True)), - rw_activation=(lambda: nn.ReLU(inplace=True))): - super(AsymConvBlock, self).__init__() - self.lw_conv = ConvBlock( - in_channels=channels, - out_channels=channels, - kernel_size=(kernel_size, 1), - stride=1, - padding=(padding, 0), - dilation=(dilation, 1), - groups=groups, - bias=bias, - use_bn=lw_use_bn, - bn_eps=bn_eps, - activation=lw_activation) - self.rw_conv = ConvBlock( - in_channels=channels, - out_channels=channels, - kernel_size=(1, kernel_size), - stride=1, - padding=(0, padding), - dilation=(1, dilation), - groups=groups, - bias=bias, - use_bn=rw_use_bn, - bn_eps=bn_eps, - activation=rw_activation) - - def forward(self, x): - x = self.lw_conv(x) - x = self.rw_conv(x) - return x - - -def asym_conv3x3_block(padding=1, - **kwargs): - """ - 3x3 asymmetric separable convolution block. - - Parameters: - ---------- - channels : int - Number of input/output channels. - padding : int, default 1 - Padding value for convolution layer. - dilation : int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - lw_use_bn : bool, default True - Whether to use BatchNorm layer (leftwise convolution block). - rw_use_bn : bool, default True - Whether to use BatchNorm layer (rightwise convolution block). - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - lw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the leftwise convolution block. - rw_activation : function or str or None, default nn.ReLU(inplace=True) - Activation function after the rightwise convolution block. - """ - return AsymConvBlock( - kernel_size=3, - padding=padding, - **kwargs) - - -class DeconvBlock(nn.Module): - """ - Deconvolution block with batch normalization and activation. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int - Strides of the deconvolution. - padding : int or tuple/list of 2 int - Padding value for deconvolution layer. - ext_padding : tuple/list of 4 int, default None - Extra padding value for deconvolution layer. - out_padding : int or tuple/list of 2 int - Output padding value for deconvolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for deconvolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - ext_padding=None, - out_padding=0, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - super(DeconvBlock, self).__init__() - self.activate = (activation is not None) - self.use_bn = use_bn - self.use_pad = (ext_padding is not None) - - if self.use_pad: - self.pad = nn.ZeroPad2d(padding=ext_padding) - self.conv = nn.ConvTranspose2d( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=kernel_size, - stride=stride, - padding=padding, - output_padding=out_padding, - dilation=dilation, - groups=groups, - bias=bias) - if self.use_bn: - self.bn = nn.BatchNorm2d( - num_features=out_channels, - eps=bn_eps) - if self.activate: - self.activ = get_activation_layer(activation) - - def forward(self, x): - if self.use_pad: - x = self.pad(x) - x = self.conv(x) - if self.use_bn: - x = self.bn(x) - if self.activate: - x = self.activ(x) - return x - - -def deconv3x3_block(padding=1, - out_padding=1, - **kwargs): - """ - 3x3 version of the deconvolution block with batch normalization and activation. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the deconvolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for deconvolution layer. - ext_padding : tuple/list of 4 int, default None - Extra padding value for deconvolution layer. - out_padding : int or tuple/list of 2 int, default 1 - Output padding value for deconvolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for deconvolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - return DeconvBlock( - kernel_size=3, - padding=padding, - out_padding=out_padding, - **kwargs) - - -class NormActivation(nn.Module): - """ - Activation block with preliminary batch normalization. It's used by itself as the final block in PreResNet. - - Parameters: - ---------- - in_channels : int - Number of input channels. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - """ - def __init__(self, - in_channels, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True))): - super(NormActivation, self).__init__() - self.bn = nn.BatchNorm2d( - num_features=in_channels, - eps=bn_eps) - self.activ = get_activation_layer(activation) - - def forward(self, x): - x = self.bn(x) - x = self.activ(x) - return x - - -class InterpolationBlock(nn.Module): - """ - Interpolation upsampling block. - - Parameters: - ---------- - scale_factor : int - Multiplier for spatial size. - out_size : tuple of 2 int, default None - Spatial size of the output tensor for the bilinear interpolation operation. - mode : str, default 'bilinear' - Algorithm used for upsampling. - align_corners : bool, default True - Whether to align the corner pixels of the input and output tensors. - up : bool, default True - Whether to upsample or downsample. - """ - def __init__(self, - scale_factor, - out_size=None, - mode="bilinear", - align_corners=True, - up=True): - super(InterpolationBlock, self).__init__() - self.scale_factor = scale_factor - self.out_size = out_size - self.mode = mode - self.align_corners = align_corners - self.up = up - - def forward(self, x, size=None): - if (self.mode == "bilinear") or (size is not None): - out_size = self.calc_out_size(x) if size is None else size - return F.interpolate( - input=x, - size=out_size, - mode=self.mode, - align_corners=self.align_corners) - else: - return F.interpolate( - input=x, - scale_factor=self.scale_factor, - mode=self.mode, - align_corners=self.align_corners) - - def calc_out_size(self, x): - if self.out_size is not None: - return self.out_size - if self.up: - return tuple(s * self.scale_factor for s in x.shape[2:]) - else: - return tuple(s // self.scale_factor for s in x.shape[2:]) - - def __repr__(self): - s = '{name}(scale_factor={scale_factor}, out_size={out_size}, mode={mode}, align_corners={align_corners}, up={up})' # noqa - return s.format( - name=self.__class__.__name__, - scale_factor=self.scale_factor, - out_size=self.out_size, - mode=self.mode, - align_corners=self.align_corners, - up=self.up) - - def calc_flops(self, x): - assert (x.shape[0] == 1) - if self.mode == "bilinear": - num_flops = 9 * x.numel() - else: - num_flops = 4 * x.numel() - num_macs = 0 - return num_flops, num_macs - - -def channel_shuffle(x, - groups): - """ - Channel shuffle operation from 'ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devices,' - https://arxiv.org/abs/1707.01083. - - Parameters: - ---------- - x : Tensor - Input tensor. - groups : int - Number of groups. - - Returns: - ------- - Tensor - Resulted tensor. - """ - batch, channels, height, width = x.size() - # assert (channels % groups == 0) - channels_per_group = channels // groups - x = x.view(batch, groups, channels_per_group, height, width) - x = torch.transpose(x, 1, 2).contiguous() - x = x.view(batch, channels, height, width) - return x - - -class ChannelShuffle(nn.Module): - """ - Channel shuffle layer. This is a wrapper over the same operation. It is designed to save the number of groups. - - Parameters: - ---------- - channels : int - Number of channels. - groups : int - Number of groups. - """ - def __init__(self, - channels, - groups): - super(ChannelShuffle, self).__init__() - # assert (channels % groups == 0) - if channels % groups != 0: - raise ValueError("channels must be divisible by groups") - self.groups = groups - - def forward(self, x): - return channel_shuffle(x, self.groups) - - def __repr__(self): - s = "{name}(groups={groups})" - return s.format( - name=self.__class__.__name__, - groups=self.groups) - - -def channel_shuffle2(x, - groups): - """ - Channel shuffle operation from 'ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devices,' - https://arxiv.org/abs/1707.01083. The alternative version. - - Parameters: - ---------- - x : Tensor - Input tensor. - groups : int - Number of groups. - - Returns: - ------- - Tensor - Resulted tensor. - """ - batch, channels, height, width = x.size() - # assert (channels % groups == 0) - channels_per_group = channels // groups - x = x.view(batch, channels_per_group, groups, height, width) - x = torch.transpose(x, 1, 2).contiguous() - x = x.view(batch, channels, height, width) - return x - - -class ChannelShuffle2(nn.Module): - """ - Channel shuffle layer. This is a wrapper over the same operation. It is designed to save the number of groups. - The alternative version. - - Parameters: - ---------- - channels : int - Number of channels. - groups : int - Number of groups. - """ - def __init__(self, - channels, - groups): - super(ChannelShuffle2, self).__init__() - # assert (channels % groups == 0) - if channels % groups != 0: - raise ValueError("channels must be divisible by groups") - self.groups = groups - - def forward(self, x): - return channel_shuffle2(x, self.groups) - - -class SEBlock(nn.Module): - """ - Squeeze-and-Excitation block from 'Squeeze-and-Excitation Networks,' https://arxiv.org/abs/1709.01507. - - Parameters: - ---------- - channels : int - Number of channels. - reduction : int, default 16 - Squeeze reduction value. - mid_channels : int or None, default None - Number of middle channels. - round_mid : bool, default False - Whether to round middle channel number (make divisible by 8). - use_conv : bool, default True - Whether to convolutional layers instead of fully-connected ones. - activation : function, or str, or nn.Module, default 'relu' - Activation function after the first convolution. - out_activation : function, or str, or nn.Module, default 'sigmoid' - Activation function after the last convolution. - """ - def __init__(self, - channels, - reduction=16, - mid_channels=None, - round_mid=False, - use_conv=True, - mid_activation=(lambda: nn.ReLU(inplace=True)), - out_activation=(lambda: nn.Sigmoid())): - super(SEBlock, self).__init__() - self.use_conv = use_conv - if mid_channels is None: - mid_channels = channels // reduction if not round_mid else round_channels(float(channels) / reduction) - - self.pool = nn.AdaptiveAvgPool2d(output_size=1) - if use_conv: - self.conv1 = conv1x1( - in_channels=channels, - out_channels=mid_channels, - bias=True) - else: - self.fc1 = nn.Linear( - in_features=channels, - out_features=mid_channels) - self.activ = get_activation_layer(mid_activation) - if use_conv: - self.conv2 = conv1x1( - in_channels=mid_channels, - out_channels=channels, - bias=True) - else: - self.fc2 = nn.Linear( - in_features=mid_channels, - out_features=channels) - self.sigmoid = get_activation_layer(out_activation) - - def forward(self, x): - w = self.pool(x) - if not self.use_conv: - w = w.view(x.size(0), -1) - w = self.conv1(w) if self.use_conv else self.fc1(w) - w = self.activ(w) - w = self.conv2(w) if self.use_conv else self.fc2(w) - w = self.sigmoid(w) - if not self.use_conv: - w = w.unsqueeze(2).unsqueeze(3) - x = x * w - return x - - -class SABlock(nn.Module): - """ - Split-Attention block from 'ResNeSt: Split-Attention Networks,' https://arxiv.org/abs/2004.08955. - - Parameters: - ---------- - out_channels : int - Number of output channels. - groups : int - Number of channel groups (cardinality, without radix). - radix : int - Number of splits within a cardinal group. - reduction : int, default 4 - Squeeze reduction value. - min_channels : int, default 32 - Minimal number of squeezed channels. - use_conv : bool, default True - Whether to convolutional layers instead of fully-connected ones. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - """ - def __init__(self, - out_channels, - groups, - radix, - reduction=4, - min_channels=32, - use_conv=True, - bn_eps=1e-5): - super(SABlock, self).__init__() - self.groups = groups - self.radix = radix - self.use_conv = use_conv - in_channels = out_channels * radix - mid_channels = max(in_channels // reduction, min_channels) - - self.pool = nn.AdaptiveAvgPool2d(output_size=1) - if use_conv: - self.conv1 = conv1x1( - in_channels=out_channels, - out_channels=mid_channels, - bias=True) - else: - self.fc1 = nn.Linear( - in_features=out_channels, - out_features=mid_channels) - self.bn = nn.BatchNorm2d( - num_features=mid_channels, - eps=bn_eps) - self.activ = nn.ReLU(inplace=True) - if use_conv: - self.conv2 = conv1x1( - in_channels=mid_channels, - out_channels=in_channels, - bias=True) - else: - self.fc2 = nn.Linear( - in_features=mid_channels, - out_features=in_channels) - self.softmax = nn.Softmax(dim=1) - - def forward(self, x): - batch, channels, height, width = x.size() - x = x.view(batch, self.radix, channels // self.radix, height, width) - w = x.sum(dim=1) - w = self.pool(w) - if not self.use_conv: - w = w.view(x.size(0), -1) - w = self.conv1(w) if self.use_conv else self.fc1(w) - w = self.bn(w) - w = self.activ(w) - w = self.conv2(w) if self.use_conv else self.fc2(w) - w = w.view(batch, self.groups, self.radix, -1) - w = torch.transpose(w, 1, 2).contiguous() - w = self.softmax(w) - w = w.view(batch, self.radix, -1, 1, 1) - x = x * w - x = x.sum(dim=1) - return x - - -class SAConvBlock(nn.Module): - """ - Split-Attention convolution block from 'ResNeSt: Split-Attention Networks,' https://arxiv.org/abs/2004.08955. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - kernel_size : int or tuple/list of 2 int - Convolution window size. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int, or tuple/list of 2 int, or tuple/list of 4 int - Padding value for convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for convolution layer. - groups : int, default 1 - Number of groups. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bn_eps : float, default 1e-5 - Small float added to variance in Batch norm. - activation : function or str or None, default nn.ReLU(inplace=True) - Activation function or name of activation function. - radix : int, default 2 - Number of splits within a cardinal group. - reduction : int, default 4 - Squeeze reduction value. - min_channels : int, default 32 - Minimal number of squeezed channels. - use_conv : bool, default True - Whether to convolutional layers instead of fully-connected ones. - """ - def __init__(self, - in_channels, - out_channels, - kernel_size, - stride, - padding, - dilation=1, - groups=1, - bias=False, - use_bn=True, - bn_eps=1e-5, - activation=(lambda: nn.ReLU(inplace=True)), - radix=2, - reduction=4, - min_channels=32, - use_conv=True): - super(SAConvBlock, self).__init__() - self.conv = ConvBlock( - in_channels=in_channels, - out_channels=(out_channels * radix), - kernel_size=kernel_size, - stride=stride, - padding=padding, - dilation=dilation, - groups=(groups * radix), - bias=bias, - use_bn=use_bn, - bn_eps=bn_eps, - activation=activation) - self.att = SABlock( - out_channels=out_channels, - groups=groups, - radix=radix, - reduction=reduction, - min_channels=min_channels, - use_conv=use_conv, - bn_eps=bn_eps) - - def forward(self, x): - x = self.conv(x) - x = self.att(x) - return x - - -def saconv3x3_block(in_channels, - out_channels, - stride=1, - padding=1, - **kwargs): - """ - 3x3 version of the Split-Attention convolution block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int, default 1 - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for convolution layer. - """ - return SAConvBlock( - in_channels=in_channels, - out_channels=out_channels, - kernel_size=3, - stride=stride, - padding=padding, - **kwargs) - - -class DucBlock(nn.Module): - """ - Dense Upsampling Convolution (DUC) block from 'Understanding Convolution for Semantic Segmentation,' - https://arxiv.org/abs/1702.08502. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - scale_factor : int - Multiplier for spatial size. - """ - def __init__(self, - in_channels, - out_channels, - scale_factor): - super(DucBlock, self).__init__() - mid_channels = (scale_factor * scale_factor) * out_channels - - self.conv = conv3x3_block( - in_channels=in_channels, - out_channels=mid_channels) - self.pix_shuffle = nn.PixelShuffle(upscale_factor=scale_factor) - - def forward(self, x): - x = self.conv(x) - x = self.pix_shuffle(x) - return x - - -class IBN(nn.Module): - """ - Instance-Batch Normalization block from 'Two at Once: Enhancing Learning and Generalization Capacities via IBN-Net,' - https://arxiv.org/abs/1807.09441. - - Parameters: - ---------- - channels : int - Number of channels. - inst_fraction : float, default 0.5 - The first fraction of channels for normalization. - inst_first : bool, default True - Whether instance normalization be on the first part of channels. - """ - def __init__(self, - channels, - first_fraction=0.5, - inst_first=True): - super(IBN, self).__init__() - self.inst_first = inst_first - h1_channels = int(math.floor(channels * first_fraction)) - h2_channels = channels - h1_channels - self.split_sections = [h1_channels, h2_channels] - - if self.inst_first: - self.inst_norm = nn.InstanceNorm2d( - num_features=h1_channels, - affine=True) - self.batch_norm = nn.BatchNorm2d(num_features=h2_channels) - else: - self.batch_norm = nn.BatchNorm2d(num_features=h1_channels) - self.inst_norm = nn.InstanceNorm2d( - num_features=h2_channels, - affine=True) - - def forward(self, x): - x1, x2 = torch.split(x, split_size_or_sections=self.split_sections, dim=1) - if self.inst_first: - x1 = self.inst_norm(x1.contiguous()) - x2 = self.batch_norm(x2.contiguous()) - else: - x1 = self.batch_norm(x1.contiguous()) - x2 = self.inst_norm(x2.contiguous()) - x = torch.cat((x1, x2), dim=1) - return x - - -class DualPathSequential(nn.Sequential): - """ - A sequential container for modules with dual inputs/outputs. - Modules will be executed in the order they are added. - - Parameters: - ---------- - return_two : bool, default True - Whether to return two output after execution. - first_ordinals : int, default 0 - Number of the first modules with single input/output. - last_ordinals : int, default 0 - Number of the final modules with single input/output. - dual_path_scheme : function - Scheme of dual path response for a module. - dual_path_scheme_ordinal : function - Scheme of dual path response for an ordinal module. - """ - def __init__(self, - return_two=True, - first_ordinals=0, - last_ordinals=0, - dual_path_scheme=(lambda module, x1, x2: module(x1, x2)), - dual_path_scheme_ordinal=(lambda module, x1, x2: (module(x1), x2))): - super(DualPathSequential, self).__init__() - self.return_two = return_two - self.first_ordinals = first_ordinals - self.last_ordinals = last_ordinals - self.dual_path_scheme = dual_path_scheme - self.dual_path_scheme_ordinal = dual_path_scheme_ordinal - - def forward(self, x1, x2=None): - length = len(self._modules.values()) - for i, module in enumerate(self._modules.values()): - if (i < self.first_ordinals) or (i >= length - self.last_ordinals): - x1, x2 = self.dual_path_scheme_ordinal(module, x1, x2) - else: - x1, x2 = self.dual_path_scheme(module, x1, x2) - if self.return_two: - return x1, x2 - else: - return x1 - - -class Concurrent(nn.Sequential): - """ - A container for concatenation of modules on the base of the sequential container. - - Parameters: - ---------- - axis : int, default 1 - The axis on which to concatenate the outputs. - stack : bool, default False - Whether to concatenate tensors along a new dimension. - merge_type : str, default None - Type of branch merging. - """ - def __init__(self, - axis=1, - stack=False, - merge_type=None): - super(Concurrent, self).__init__() - assert (merge_type is None) or (merge_type in ["cat", "stack", "sum"]) - self.axis = axis - if merge_type is not None: - self.merge_type = merge_type - else: - self.merge_type = "stack" if stack else "cat" - - def forward(self, x): - out = [] - for module in self._modules.values(): - out.append(module(x)) - if self.merge_type == "stack": - out = torch.stack(tuple(out), dim=self.axis) - elif self.merge_type == "cat": - out = torch.cat(tuple(out), dim=self.axis) - elif self.merge_type == "sum": - out = torch.stack(tuple(out), dim=self.axis).sum(self.axis) - else: - raise NotImplementedError() - return out - - -class SequentialConcurrent(nn.Sequential): - """ - A sequential container with concatenated outputs. - Modules will be executed in the order they are added. - - Parameters: - ---------- - axis : int, default 1 - The axis on which to concatenate the outputs. - stack : bool, default False - Whether to concatenate tensors along a new dimension. - cat_input : bool, default True - Whether to concatenate input tensor. - """ - def __init__(self, - axis=1, - stack=False, - cat_input=True): - super(SequentialConcurrent, self).__init__() - self.axis = axis - self.stack = stack - self.cat_input = cat_input - - def forward(self, x): - out = [x] if self.cat_input else [] - for module in self._modules.values(): - x = module(x) - out.append(x) - if self.stack: - out = torch.stack(tuple(out), dim=self.axis) - else: - out = torch.cat(tuple(out), dim=self.axis) - return out - - -class ParametricSequential(nn.Sequential): - """ - A sequential container for modules with parameters. - Modules will be executed in the order they are added. - """ - def __init__(self, *args): - super(ParametricSequential, self).__init__(*args) - - def forward(self, x, **kwargs): - for module in self._modules.values(): - x = module(x, **kwargs) - return x - - -class ParametricConcurrent(nn.Sequential): - """ - A container for concatenation of modules with parameters. - - Parameters: - ---------- - axis : int, default 1 - The axis on which to concatenate the outputs. - """ - def __init__(self, axis=1): - super(ParametricConcurrent, self).__init__() - self.axis = axis - - def forward(self, x, **kwargs): - out = [] - for module in self._modules.values(): - out.append(module(x, **kwargs)) - out = torch.cat(tuple(out), dim=self.axis) - return out - - -class Hourglass(nn.Module): - """ - A hourglass module. - - Parameters: - ---------- - down_seq : nn.Sequential - Down modules as sequential. - up_seq : nn.Sequential - Up modules as sequential. - skip_seq : nn.Sequential - Skip connection modules as sequential. - merge_type : str, default 'add' - Type of concatenation of up and skip outputs. - return_first_skip : bool, default False - Whether return the first skip connection output. Used in ResAttNet. - """ - def __init__(self, - down_seq, - up_seq, - skip_seq, - merge_type="add", - return_first_skip=False): - super(Hourglass, self).__init__() - self.depth = len(down_seq) - assert (merge_type in ["cat", "add"]) - assert (len(up_seq) == self.depth) - assert (len(skip_seq) in (self.depth, self.depth + 1)) - self.merge_type = merge_type - self.return_first_skip = return_first_skip - self.extra_skip = (len(skip_seq) == self.depth + 1) - - self.down_seq = down_seq - self.up_seq = up_seq - self.skip_seq = skip_seq - - def _merge(self, x, y): - if y is not None: - if self.merge_type == "cat": - x = torch.cat((x, y), dim=1) - elif self.merge_type == "add": - x = x + y - return x - - def forward(self, x, **kwargs): - y = None - down_outs = [x] - for down_module in self.down_seq._modules.values(): - x = down_module(x) - down_outs.append(x) - for i in range(len(down_outs)): - if i != 0: - y = down_outs[self.depth - i] - skip_module = self.skip_seq[self.depth - i] - y = skip_module(y) - x = self._merge(x, y) - if i != len(down_outs) - 1: - if (i == 0) and self.extra_skip: - skip_module = self.skip_seq[self.depth] - x = skip_module(x) - up_module = self.up_seq[self.depth - 1 - i] - x = up_module(x) - if self.return_first_skip: - return x, y - else: - return x - - -class SesquialteralHourglass(nn.Module): - """ - A sesquialteral hourglass block. - - Parameters: - ---------- - down1_seq : nn.Sequential - The first down modules as sequential. - skip1_seq : nn.Sequential - The first skip connection modules as sequential. - up_seq : nn.Sequential - Up modules as sequential. - skip2_seq : nn.Sequential - The second skip connection modules as sequential. - down2_seq : nn.Sequential - The second down modules as sequential. - merge_type : str, default 'cat' - Type of concatenation of up and skip outputs. - """ - def __init__(self, - down1_seq, - skip1_seq, - up_seq, - skip2_seq, - down2_seq, - merge_type="cat"): - super(SesquialteralHourglass, self).__init__() - assert (len(down1_seq) == len(up_seq)) - assert (len(down1_seq) == len(down2_seq)) - assert (len(skip1_seq) == len(skip2_seq)) - assert (len(down1_seq) == len(skip1_seq) - 1) - assert (merge_type in ["cat", "add"]) - self.merge_type = merge_type - self.depth = len(down1_seq) - - self.down1_seq = down1_seq - self.skip1_seq = skip1_seq - self.up_seq = up_seq - self.skip2_seq = skip2_seq - self.down2_seq = down2_seq - - def _merge(self, x, y): - if y is not None: - if self.merge_type == "cat": - x = torch.cat((x, y), dim=1) - elif self.merge_type == "add": - x = x + y - return x - - def forward(self, x, **kwargs): - y = self.skip1_seq[0](x) - skip1_outs = [y] - for i in range(self.depth): - x = self.down1_seq[i](x) - y = self.skip1_seq[i + 1](x) - skip1_outs.append(y) - x = skip1_outs[self.depth] - y = self.skip2_seq[0](x) - skip2_outs = [y] - for i in range(self.depth): - x = self.up_seq[i](x) - y = skip1_outs[self.depth - 1 - i] - x = self._merge(x, y) - y = self.skip2_seq[i + 1](x) - skip2_outs.append(y) - x = self.skip2_seq[self.depth](x) - for i in range(self.depth): - x = self.down2_seq[i](x) - y = skip2_outs[self.depth - 1 - i] - x = self._merge(x, y) - return x - - -class MultiOutputSequential(nn.Sequential): - """ - A sequential container with multiple outputs. - Modules will be executed in the order they are added. - - Parameters: - ---------- - multi_output : bool, default True - Whether to return multiple output. - dual_output : bool, default False - Whether to return dual output. - return_last : bool, default True - Whether to forcibly return last value. - """ - def __init__(self, - multi_output=True, - dual_output=False, - return_last=True): - super(MultiOutputSequential, self).__init__() - self.multi_output = multi_output - self.dual_output = dual_output - self.return_last = return_last - - def forward(self, x): - outs = [] - for module in self._modules.values(): - x = module(x) - if hasattr(module, "do_output") and module.do_output: - outs.append(x) - elif hasattr(module, "do_output2") and module.do_output2: - assert (type(x) == tuple) - outs.extend(x[1]) - x = x[0] - if self.multi_output: - return [x] + outs if self.return_last else outs - elif self.dual_output: - return x, outs - else: - return x - - -class ParallelConcurent(nn.Sequential): - """ - A sequential container with multiple inputs and single/multiple outputs. - Modules will be executed in the order they are added. - - Parameters: - ---------- - axis : int, default 1 - The axis on which to concatenate the outputs. - merge_type : str, default 'list' - Type of branch merging. - """ - def __init__(self, - axis=1, - merge_type="list"): - super(ParallelConcurent, self).__init__() - assert (merge_type is None) or (merge_type in ["list", "cat", "stack", "sum"]) - self.axis = axis - self.merge_type = merge_type - - def forward(self, x): - out = [] - for module, xi in zip(self._modules.values(), x): - out.append(module(xi)) - if self.merge_type == "list": - pass - elif self.merge_type == "stack": - out = torch.stack(tuple(out), dim=self.axis) - elif self.merge_type == "cat": - out = torch.cat(tuple(out), dim=self.axis) - elif self.merge_type == "sum": - out = torch.stack(tuple(out), dim=self.axis).sum(self.axis) - else: - raise NotImplementedError() - return out - - -class DualPathParallelConcurent(nn.Sequential): - """ - A sequential container with multiple dual-path inputs and single/multiple outputs. - Modules will be executed in the order they are added. - - Parameters: - ---------- - axis : int, default 1 - The axis on which to concatenate the outputs. - merge_type : str, default 'list' - Type of branch merging. - """ - def __init__(self, - axis=1, - merge_type="list"): - super(DualPathParallelConcurent, self).__init__() - assert (merge_type is None) or (merge_type in ["list", "cat", "stack", "sum"]) - self.axis = axis - self.merge_type = merge_type - - def forward(self, x1, x2): - x1_out = [] - x2_out = [] - for module, x1i, x2i in zip(self._modules.values(), x1, x2): - y1i, y2i = module(x1i, x2i) - x1_out.append(y1i) - x2_out.append(y2i) - if self.merge_type == "list": - pass - elif self.merge_type == "stack": - x1_out = torch.stack(tuple(x1_out), dim=self.axis) - x2_out = torch.stack(tuple(x2_out), dim=self.axis) - elif self.merge_type == "cat": - x1_out = torch.cat(tuple(x1_out), dim=self.axis) - x2_out = torch.cat(tuple(x2_out), dim=self.axis) - elif self.merge_type == "sum": - x1_out = torch.stack(tuple(x1_out), dim=self.axis).sum(self.axis) - x2_out = torch.stack(tuple(x2_out), dim=self.axis).sum(self.axis) - else: - raise NotImplementedError() - return x1_out, x2_out - - -class Flatten(nn.Module): - """ - Simple flatten module. - """ - - def forward(self, x): - return x.view(x.size(0), -1) - - -class HeatmapMaxDetBlock(nn.Module): - """ - Heatmap maximum detector block (for human pose estimation task). - """ - def __init__(self): - super(HeatmapMaxDetBlock, self).__init__() - - def forward(self, x): - heatmap = x - vector_dim = 2 - batch = heatmap.shape[0] - channels = heatmap.shape[1] - in_size = x.shape[2:] - heatmap_vector = heatmap.view(batch, channels, -1) - scores, indices = heatmap_vector.max(dim=vector_dim, keepdims=True) - scores_mask = (scores > 0.0).float() - pts_x = (indices % in_size[1]) * scores_mask - pts_y = (indices // in_size[1]) * scores_mask - pts = torch.cat((pts_x, pts_y, scores), dim=vector_dim) - for b in range(batch): - for k in range(channels): - hm = heatmap[b, k, :, :] - px = int(pts[b, k, 0]) - py = int(pts[b, k, 1]) - if (0 < px < in_size[1] - 1) and (0 < py < in_size[0] - 1): - pts[b, k, 0] += (hm[py, px + 1] - hm[py, px - 1]).sign() * 0.25 - pts[b, k, 1] += (hm[py + 1, px] - hm[py - 1, px]).sign() * 0.25 - return pts - - @staticmethod - def calc_flops(x): - assert (x.shape[0] == 1) - num_flops = x.numel() + 26 * x.shape[1] - num_macs = 0 - return num_flops, num_macs diff --git a/cv/classification/seresnext/pytorch/common_utils/__init__.py b/cv/classification/seresnext/pytorch/common_utils/__init__.py new file mode 100644 index 000000000..32e8c4f57 --- /dev/null +++ b/cv/classification/seresnext/pytorch/common_utils/__init__.py @@ -0,0 +1,23 @@ +import random + +import numpy as np + +from .dist import * +from .metric_logger import * +from .misc import * +from .smooth_value import * + +def manual_seed(seed, deterministic=False): + random.seed(seed) + np.random.seed(seed) + os.environ['PYTHONHASHSEED'] = str(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + + if deterministic: + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + else: + torch.backends.cudnn.deterministic = False + torch.backends.cudnn.benchmark = True \ No newline at end of file diff --git a/cv/classification/seresnext/pytorch/common_utils/dist.py b/cv/classification/seresnext/pytorch/common_utils/dist.py new file mode 100644 index 000000000..ea56ca267 --- /dev/null +++ b/cv/classification/seresnext/pytorch/common_utils/dist.py @@ -0,0 +1,144 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist + + + +def setup_for_distributed(is_master): + """ + This function disables printing when not in master process + """ + import builtins as __builtin__ + builtin_print = __builtin__.print + + def print(*args, **kwargs): + force = kwargs.pop('force', False) + if is_master or force: + builtin_print(*args, **kwargs) + + __builtin__.print = print + + +def is_dist_avail_and_initialized(): + if not dist.is_available(): + return False + if not dist.is_initialized(): + return False + return True + + +def get_world_size(): + if not is_dist_avail_and_initialized(): + return 1 + return dist.get_world_size() + + +def get_rank(): + if not is_dist_avail_and_initialized(): + return 0 + return dist.get_rank() + + +def is_main_process(): + return get_rank() == 0 + + +def save_on_master(*args, **kwargs): + if is_main_process(): + torch.save(*args, **kwargs) + + +def get_dist_backend(args=None): + DIST_BACKEND_ENV = "PT_DIST_BACKEND" + if DIST_BACKEND_ENV in os.environ: + return os.environ[DIST_BACKEND_ENV] + + if args is None: + args = dict() + + backend_attr_name = "dist_backend" + + if hasattr(args, backend_attr_name): + return getattr(args, backend_attr_name) + + if backend_attr_name in args: + return args[backend_attr_name] + + return "nccl" + + +def init_distributed_mode(args): + if 'RANK' in os.environ and 'WORLD_SIZE' in os.environ: + args.rank = int(os.environ["RANK"]) + args.world_size = int(os.environ['WORLD_SIZE']) + args.gpu = int(os.environ['LOCAL_RANK']) + elif 'SLURM_PROCID' in os.environ: + args.rank = int(os.environ['SLURM_PROCID']) + args.gpu = args.rank % torch.cuda.device_count() + else: + print('Not using distributed mode') + args.distributed = False + return + + args.distributed = True + + torch.cuda.set_device(args.gpu) + dist_backend = get_dist_backend(args) + print('| distributed init (rank {}): {}'.format( + args.rank, args.dist_url), flush=True) + torch.distributed.init_process_group(backend=dist_backend, init_method=args.dist_url, + world_size=args.world_size, rank=args.rank) + torch.distributed.barrier() + setup_for_distributed(args.rank == 0) + + +def all_gather(data): + """ + Run all_gather on arbitrary picklable data (not necessarily tensors) + Args: + data: any picklable object + Returns: + list[data]: list of data gathered from each rank + """ + world_size = get_world_size() + if world_size == 1: + return [data] + data_list = [None] * world_size + dist.all_gather_object(data_list, data) + return data_list + + +def reduce_dict(input_dict, average=True): + """ + Args: + input_dict (dict): all the values will be reduced + average (bool): whether to do average or sum + Reduce the values in the dictionary from all processes so that all processes + have the averaged results. Returns a dict with the same fields as + input_dict, after reduction. + """ + world_size = get_world_size() + if world_size < 2: + return input_dict + with torch.no_grad(): + names = [] + values = [] + # sort the keys so that they are consistent across processes + for k in sorted(input_dict.keys()): + names.append(k) + values.append(input_dict[k]) + values = torch.stack(values, dim=0) + dist.all_reduce(values) + if average: + values /= world_size + reduced_dict = {k: v for k, v in zip(names, values)} + return reduced_dict diff --git a/cv/classification/seresnext/pytorch/common_utils/metric_logger.py b/cv/classification/seresnext/pytorch/common_utils/metric_logger.py new file mode 100644 index 000000000..960641c4d --- /dev/null +++ b/cv/classification/seresnext/pytorch/common_utils/metric_logger.py @@ -0,0 +1,94 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict +import datetime +import time + +import torch +from .smooth_value import SmoothedValue + +""" +Examples: + +logger = MetricLogger(" ") + +>>> # For iter dataloader +>>> metric_logger.add_meter('img/s', utils.SmoothedValue(window_size=10, fmt='{value}')) +>>> header = 'Epoch: [{}]'.format(epoch) +>>> for image, target in metric_logger.log_every(data_loader, print_freq, header): +>>> ... +>>> logger.metric_logger.meters['img/s'].update(fps) + +""" + +class MetricLogger(object): + + def __init__(self, delimiter="\t"): + self.meters = defaultdict(SmoothedValue) + self.delimiter = delimiter + + def update(self, **kwargs): + for k, v in kwargs.items(): + if isinstance(v, torch.Tensor): + v = v.item() + assert isinstance(v, (float, int)) + self.meters[k].update(v) + + def __getattr__(self, attr): + if attr in self.meters: + return self.meters[attr] + if attr in self.__dict__: + return self.__dict__[attr] + raise AttributeError("'{}' object has no attribute '{}'".format( + type(self).__name__, attr)) + + def __str__(self): + loss_str = [] + for name, meter in self.meters.items(): + loss_str.append( + "{}: {}".format(name, str(meter)) + ) + return self.delimiter.join(loss_str) + + def synchronize_between_processes(self): + for meter in self.meters.values(): + meter.synchronize_between_processes() + + def add_meter(self, name, meter): + self.meters[name] = meter + + def log_every(self, iterable, print_freq, header=None): + i = 0 + if not header: + header = '' + start_time = time.time() + end = time.time() + iter_time = SmoothedValue(fmt='{avg:.4f}') + data_time = SmoothedValue(fmt='{avg:.4f}') + space_fmt = ':' + str(len(str(len(iterable)))) + 'd' + log_msg = self.delimiter.join([ + header, + '[{0' + space_fmt + '}/{1}]', + 'eta: {eta}', + '{meters}', + 'time: {time}', + 'data: {data}' + ]) + for obj in iterable: + data_time.update(time.time() - end) + yield obj + iter_time.update(time.time() - end) + if i % print_freq == 0: + eta_seconds = iter_time.global_avg * (len(iterable) - i) + eta_string = str(datetime.timedelta(seconds=int(eta_seconds))) + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time))) + i += 1 + end = time.time() + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('{} Total time: {}'.format(header, total_time_str)) diff --git a/cv/classification/seresnext/pytorch/common_utils/misc.py b/cv/classification/seresnext/pytorch/common_utils/misc.py new file mode 100644 index 000000000..c9b501cf8 --- /dev/null +++ b/cv/classification/seresnext/pytorch/common_utils/misc.py @@ -0,0 +1,11 @@ +import os +import sys +import errno + + +def mkdir(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise \ No newline at end of file diff --git a/cv/classification/seresnext/pytorch/common_utils/smooth_value.py b/cv/classification/seresnext/pytorch/common_utils/smooth_value.py new file mode 100644 index 000000000..30cb89d60 --- /dev/null +++ b/cv/classification/seresnext/pytorch/common_utils/smooth_value.py @@ -0,0 +1,75 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist +from .dist import is_dist_avail_and_initialized + + +class SmoothedValue(object): + """Track a series of values and provide access to smoothed values over a + window or the global series average. + """ + + def __init__(self, window_size=20, fmt=None): + if fmt is None: + fmt = "{median:.4f} ({global_avg:.4f})" + self.deque = deque(maxlen=window_size) + self.total = 0.0 + self.count = 0 + self.fmt = fmt + + def update(self, value, n=1): + self.deque.append(value) + self.count += n + self.total += value * n + + def synchronize_between_processes(self): + """ + Warning: does not synchronize the deque! + """ + if not is_dist_avail_and_initialized(): + return + t = torch.tensor([self.count, self.total], dtype=torch.float32, device='cuda') + dist.barrier() + dist.all_reduce(t) + t = t.tolist() + self.count = int(t[0]) + self.total = t[1] + + @property + def median(self): + d = torch.tensor(list(self.deque)) + return d.median().item() + + @property + def avg(self): + d = torch.tensor(list(self.deque), dtype=torch.float32) + return d.mean().item() + + @property + def global_avg(self): + return self.total / self.count + + @property + def max(self): + return max(self.deque) + + @property + def value(self): + return self.deque[-1] + + def __str__(self): + return self.fmt.format( + median=self.median, + avg=self.avg, + global_avg=self.global_avg, + max=self.max, + value=self.value) \ No newline at end of file diff --git a/cv/classification/seresnext/pytorch/dataloader/__init__.py b/cv/classification/seresnext/pytorch/dataloader/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/classification/seresnext/pytorch/dataloader/classification.py b/cv/classification/seresnext/pytorch/dataloader/classification.py new file mode 100644 index 000000000..030af6dee --- /dev/null +++ b/cv/classification/seresnext/pytorch/dataloader/classification.py @@ -0,0 +1,113 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +import os +import time + +import torch +import torchvision +from .utils import presets_classification as presets + +""" +Examples: + +>>> dataset_train, dataset_val = load_data(train_dir, val_dir, args) +""" + + +def get_datasets(traindir, + valdir, + resize_size=256, + crop_size=224, + auto_augment_policy=None, + random_erase_prob=0.): + # Data loading code + print("Loading data") + print("Loading training data") + dataset = torchvision.datasets.ImageFolder( + traindir, + presets.ClassificationPresetTrain(crop_size=crop_size, auto_augment_policy=auto_augment_policy, + random_erase_prob=random_erase_prob)) + + print("Loading validation data") + dataset_test = torchvision.datasets.ImageFolder( + valdir, + presets.ClassificationPresetEval(crop_size=crop_size, resize_size=resize_size)) + + return dataset, dataset_test + + +def get_input_size(model): + biger_input_size_models = ['inception'] + resize_size = 256 + crop_size = 224 + for bi_model in biger_input_size_models: + if bi_model in model: + resize_size = 342 + crop_size = 299 + + return resize_size, crop_size + + +def load_data(train_dir, val_dir, args): + auto_augment_policy = getattr(args, "auto_augment", None) + random_erase_prob = getattr(args, "random_erase", 0.0) + resize_size, crop_size = get_input_size(args.model) + dataset, dataset_test = get_datasets(train_dir, val_dir, + auto_augment_policy=auto_augment_policy, + random_erase_prob=random_erase_prob, + resize_size=resize_size, + crop_size=crop_size) + if args.distributed: + train_sampler = torch.utils.data.distributed.DistributedSampler(dataset) + test_sampler = torch.utils.data.distributed.DistributedSampler(dataset_test) + else: + train_sampler = torch.utils.data.RandomSampler(dataset) + test_sampler = torch.utils.data.SequentialSampler(dataset_test) + + return dataset, dataset_test, train_sampler, test_sampler + + +def _create_torch_dataloader(train_dir, val_dir, args): + dataset, dataset_test, train_sampler, test_sampler = load_data(train_dir, val_dir, args) + data_loader = torch.utils.data.DataLoader( + dataset, batch_size=args.batch_size, + sampler=train_sampler, num_workers=args.workers, pin_memory=True) + + data_loader_test = torch.utils.data.DataLoader( + dataset_test, batch_size=args.batch_size, + sampler=test_sampler, num_workers=args.workers, pin_memory=True) + + return data_loader, data_loader_test + + +def _create_dali_dataloader(train_dir, val_dir, args): + from .dali_classification import get_imagenet_iter_dali + device = torch.cuda.current_device() + _, crop_size = get_input_size(args.model) + data_loader = get_imagenet_iter_dali('train', train_dir, args.batch_size, + num_threads=args.workers, + device_id=device, + size=crop_size) + data_loader_test = get_imagenet_iter_dali('val', train_dir, args.batch_size, + num_threads=args.workers, + device_id=device, + size=crop_size) + + return data_loader, data_loader_test + + +def create_dataloader(train_dir, val_dir, args): + print("Creating data loaders") + if args.dali: + train_dir = os.path.dirname(train_dir) + val_dir = os.path.dirname(val_dir) + return _create_dali_dataloader(train_dir, val_dir, args) + return _create_torch_dataloader(train_dir, val_dir, args) diff --git a/cv/classification/seresnext/pytorch/dataloader/dali_classification.py b/cv/classification/seresnext/pytorch/dataloader/dali_classification.py new file mode 100644 index 000000000..4c92283b2 --- /dev/null +++ b/cv/classification/seresnext/pytorch/dataloader/dali_classification.py @@ -0,0 +1,121 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +import os + +import nvidia.dali.ops as ops +import nvidia.dali.types as types +from nvidia.dali.pipeline import Pipeline +from nvidia.dali.plugin.pytorch import DALIClassificationIterator, DALIGenericIterator + +class HybridTrainPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridTrainPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=True) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.RandomResizedCrop(device="gpu", size=size, random_area=[0.08, 1.25]) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +class HybridValPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridValPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=False) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.Resize(device="gpu", resize_x=size, resize_y=size) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + crop=(size, size), + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +def get_imagenet_iter_dali(type, image_dir, batch_size, num_threads, device_id, size): + if type == 'train': + pip_train = HybridTrainPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "train"), + size=size) + pip_train.build() + dali_iter_train = DALIClassificationIterator(pip_train, size=pip_train.epoch_size("Reader")) + return dali_iter_train + elif type == 'val': + pip_val = HybridValPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "val"), + size=size) + pip_val.build() + dali_iter_val = DALIClassificationIterator(pip_val, size=pip_val.epoch_size("Reader")) + return dali_iter_val + + +def main(arguments): + parser = argparse.ArgumentParser() + parser.add_argument('--data_dir', help='directory to save data to', type=str, default='classification data') + args = parser.parse_args(arguments) + + train_loader = get_imagenet_iter_dali(type='train', image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + val_loader = get_imagenet_iter_dali(type="val", image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + print('start dali train dataloader.') + start = time.time() + for epoch in range(20): + for i, data in enumerate(train_loader): + images = data[0]["data"].cuda(non_blocking=True) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + + # WARN: Very important + train_loader.reset() + print("Epoch", epoch) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali train dataloader.') + + + print('start dali val dataloader.') + start = time.time() + for i, data in enumerate(val_loader): + images = data[0]["data"].cuda(non_blocking=True) + print(images.shape) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + print(labels.shape) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali val dataloader.') + + +if __name__ == '__main__': + import os, time, sys + import argparse + sys.exit(main(sys.argv[1:])) \ No newline at end of file diff --git a/cv/classification/seresnext/pytorch/dataloader/utils/__init__.py b/cv/classification/seresnext/pytorch/dataloader/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/classification/seresnext/pytorch/dataloader/utils/presets_classification.py b/cv/classification/seresnext/pytorch/dataloader/utils/presets_classification.py new file mode 100644 index 000000000..b3f559af4 --- /dev/null +++ b/cv/classification/seresnext/pytorch/dataloader/utils/presets_classification.py @@ -0,0 +1,41 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from torchvision.transforms import autoaugment, transforms + + +class ClassificationPresetTrain: + def __init__(self, crop_size, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225), hflip_prob=0.5, + auto_augment_policy=None, random_erase_prob=0.0): + trans = [transforms.RandomResizedCrop(crop_size)] + if hflip_prob > 0: + trans.append(transforms.RandomHorizontalFlip(hflip_prob)) + if auto_augment_policy is not None: + aa_policy = autoaugment.AutoAugmentPolicy(auto_augment_policy) + trans.append(autoaugment.AutoAugment(policy=aa_policy)) + trans.extend([ + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ]) + if random_erase_prob > 0: + trans.append(transforms.RandomErasing(p=random_erase_prob)) + + self.transforms = transforms.Compose(trans) + + def __call__(self, img): + return self.transforms(img) + + +class ClassificationPresetEval: + def __init__(self, crop_size, resize_size=256, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)): + + self.transforms = transforms.Compose([ + transforms.Resize(resize_size), + transforms.CenterCrop(crop_size), + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ]) + + def __call__(self, img): + return self.transforms(img) diff --git a/cv/classification/seresnext/pytorch/resnet.py b/cv/classification/seresnext/pytorch/resnet.py deleted file mode 100644 index 8e137145a..000000000 --- a/cv/classification/seresnext/pytorch/resnet.py +++ /dev/null @@ -1,785 +0,0 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright Declaration: This software, including all of its code and documentation, -# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX -# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright -# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar -# CoreX. No user of this software shall have any right, ownership or interest in this software and -# any use of this software shall be in compliance with the terms and conditions of the End User -# License Agreement. -""" - ResNet for ImageNet-1K, implemented in PyTorch. - Original paper: 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. -""" - -__all__ = ['ResNet', 'resnet10', 'resnet12', 'resnet14', 'resnetbc14b', 'resnet16', 'resnet18_wd4', 'resnet18_wd2', - 'resnet18_w3d4', 'resnet18', 'resnet26', 'resnetbc26b', 'resnet34', 'resnetbc38b', 'resnet50', 'resnet50b', - 'resnet101', 'resnet101b', 'resnet152', 'resnet152b', 'resnet200', 'resnet200b', 'ResBlock', 'ResBottleneck', - 'ResUnit', 'ResInitBlock'] - -import os -import torch.nn as nn -from common import conv1x1_block, conv3x3_block, conv7x7_block - - -class ResBlock(nn.Module): - """ - Simple ResNet block for residual path in ResNet unit. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the convolution. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - """ - def __init__(self, - in_channels, - out_channels, - stride, - bias=False, - use_bn=True): - super(ResBlock, self).__init__() - self.conv1 = conv3x3_block( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - bias=bias, - use_bn=use_bn) - self.conv2 = conv3x3_block( - in_channels=out_channels, - out_channels=out_channels, - bias=bias, - use_bn=use_bn, - activation=None) - - def forward(self, x): - x = self.conv1(x) - x = self.conv2(x) - return x - - -class ResBottleneck(nn.Module): - """ - ResNet bottleneck block for residual path in ResNet unit. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for the second convolution layer. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for the second convolution layer. - conv1_stride : bool, default False - Whether to use stride in the first or the second convolution layer of the block. - bottleneck_factor : int, default 4 - Bottleneck factor. - """ - def __init__(self, - in_channels, - out_channels, - stride, - padding=1, - dilation=1, - conv1_stride=False, - bottleneck_factor=4): - super(ResBottleneck, self).__init__() - mid_channels = out_channels // bottleneck_factor - - self.conv1 = conv1x1_block( - in_channels=in_channels, - out_channels=mid_channels, - stride=(stride if conv1_stride else 1)) - self.conv2 = conv3x3_block( - in_channels=mid_channels, - out_channels=mid_channels, - stride=(1 if conv1_stride else stride), - padding=padding, - dilation=dilation) - self.conv3 = conv1x1_block( - in_channels=mid_channels, - out_channels=out_channels, - activation=None) - - def forward(self, x): - x = self.conv1(x) - x = self.conv2(x) - x = self.conv3(x) - return x - - -class ResUnit(nn.Module): - """ - ResNet unit with residual connection. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the convolution. - padding : int or tuple/list of 2 int, default 1 - Padding value for the second convolution layer in bottleneck. - dilation : int or tuple/list of 2 int, default 1 - Dilation value for the second convolution layer in bottleneck. - bias : bool, default False - Whether the layer uses a bias vector. - use_bn : bool, default True - Whether to use BatchNorm layer. - bottleneck : bool, default True - Whether to use a bottleneck or simple block in units. - conv1_stride : bool, default False - Whether to use stride in the first or the second convolution layer of the block. - """ - def __init__(self, - in_channels, - out_channels, - stride, - padding=1, - dilation=1, - bias=False, - use_bn=True, - bottleneck=True, - conv1_stride=False): - super(ResUnit, self).__init__() - self.resize_identity = (in_channels != out_channels) or (stride != 1) - - if bottleneck: - self.body = ResBottleneck( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - padding=padding, - dilation=dilation, - conv1_stride=conv1_stride) - else: - self.body = ResBlock( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - bias=bias, - use_bn=use_bn) - if self.resize_identity: - self.identity_conv = conv1x1_block( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - bias=bias, - use_bn=use_bn, - activation=None) - self.activ = nn.ReLU(inplace=True) - - def forward(self, x): - if self.resize_identity: - identity = self.identity_conv(x) - else: - identity = x - x = self.body(x) - x = x + identity - x = self.activ(x) - return x - - -class ResInitBlock(nn.Module): - """ - ResNet specific initial block. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - """ - def __init__(self, - in_channels, - out_channels): - super(ResInitBlock, self).__init__() - self.conv = conv7x7_block( - in_channels=in_channels, - out_channels=out_channels, - stride=2) - self.pool = nn.MaxPool2d( - kernel_size=3, - stride=2, - padding=1) - - def forward(self, x): - x = self.conv(x) - x = self.pool(x) - return x - - -class ResNet(nn.Module): - """ - ResNet model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - channels : list of list of int - Number of output channels for each unit. - init_block_channels : int - Number of output channels for the initial unit. - bottleneck : bool - Whether to use a bottleneck or simple block in units. - conv1_stride : bool - Whether to use stride in the first or the second convolution layer in units. - in_channels : int, default 3 - Number of input channels. - in_size : tuple of two ints, default (224, 224) - Spatial size of the expected input image. - num_classes : int, default 1000 - Number of classification classes. - """ - def __init__(self, - channels, - init_block_channels, - bottleneck, - conv1_stride, - in_channels=3, - in_size=(224, 224), - num_classes=1000): - super(ResNet, self).__init__() - self.in_size = in_size - self.num_classes = num_classes - - self.features = nn.Sequential() - self.features.add_module("init_block", ResInitBlock( - in_channels=in_channels, - out_channels=init_block_channels)) - in_channels = init_block_channels - for i, channels_per_stage in enumerate(channels): - stage = nn.Sequential() - for j, out_channels in enumerate(channels_per_stage): - stride = 2 if (j == 0) and (i != 0) else 1 - stage.add_module("unit{}".format(j + 1), ResUnit( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - bottleneck=bottleneck, - conv1_stride=conv1_stride)) - in_channels = out_channels - self.features.add_module("stage{}".format(i + 1), stage) - self.features.add_module("final_pool", nn.AvgPool2d( - kernel_size=7, - stride=1)) - - self.output = nn.Linear( - in_features=in_channels, - out_features=num_classes) - - self._init_params() - - def _init_params(self): - for name, module in self.named_modules(): - if isinstance(module, nn.Conv2d): - nn.init.kaiming_uniform_(module.weight) - if module.bias is not None: - nn.init.constant_(module.bias, 0) - - def forward(self, x): - x = self.features(x) - x = x.view(x.size(0), -1) - x = self.output(x) - return x - - -def get_resnet(blocks, - bottleneck=None, - conv1_stride=True, - width_scale=1.0, - model_name=None, - pretrained=False, - root=os.path.join("~", ".torch", "models"), - **kwargs): - """ - Create ResNet model with specific parameters. - - Parameters: - ---------- - blocks : int - Number of blocks. - bottleneck : bool, default None - Whether to use a bottleneck or simple block in units. - conv1_stride : bool, default True - Whether to use stride in the first or the second convolution layer in units. - width_scale : float, default 1.0 - Scale factor for width of layers. - model_name : str or None, default None - Model name for loading pretrained model. - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - if bottleneck is None: - bottleneck = (blocks >= 50) - - if blocks == 10: - layers = [1, 1, 1, 1] - elif blocks == 12: - layers = [2, 1, 1, 1] - elif blocks == 14 and not bottleneck: - layers = [2, 2, 1, 1] - elif (blocks == 14) and bottleneck: - layers = [1, 1, 1, 1] - elif blocks == 16: - layers = [2, 2, 2, 1] - elif blocks == 18: - layers = [2, 2, 2, 2] - elif (blocks == 26) and not bottleneck: - layers = [3, 3, 3, 3] - elif (blocks == 26) and bottleneck: - layers = [2, 2, 2, 2] - elif blocks == 34: - layers = [3, 4, 6, 3] - elif (blocks == 38) and bottleneck: - layers = [3, 3, 3, 3] - elif blocks == 50: - layers = [3, 4, 6, 3] - elif blocks == 101: - layers = [3, 4, 23, 3] - elif blocks == 152: - layers = [3, 8, 36, 3] - elif blocks == 200: - layers = [3, 24, 36, 3] - else: - raise ValueError("Unsupported ResNet with number of blocks: {}".format(blocks)) - - if bottleneck: - assert (sum(layers) * 3 + 2 == blocks) - else: - assert (sum(layers) * 2 + 2 == blocks) - - init_block_channels = 64 - channels_per_layers = [64, 128, 256, 512] - - if bottleneck: - bottleneck_factor = 4 - channels_per_layers = [ci * bottleneck_factor for ci in channels_per_layers] - - channels = [[ci] * li for (ci, li) in zip(channels_per_layers, layers)] - - if width_scale != 1.0: - channels = [[int(cij * width_scale) if (i != len(channels) - 1) or (j != len(ci) - 1) else cij - for j, cij in enumerate(ci)] for i, ci in enumerate(channels)] - init_block_channels = int(init_block_channels * width_scale) - - net = ResNet( - channels=channels, - init_block_channels=init_block_channels, - bottleneck=bottleneck, - conv1_stride=conv1_stride, - **kwargs) - - if pretrained: - if (model_name is None) or (not model_name): - raise ValueError("Parameter `model_name` should be properly initialized for loading pretrained model.") - from .model_store import download_model - download_model( - net=net, - model_name=model_name, - local_model_store_dir_path=root) - - return net - - -def resnet10(**kwargs): - """ - ResNet-10 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=10, model_name="resnet10", **kwargs) - - -def resnet12(**kwargs): - """ - ResNet-12 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=12, model_name="resnet12", **kwargs) - - -def resnet14(**kwargs): - """ - ResNet-14 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=14, model_name="resnet14", **kwargs) - - -def resnetbc14b(**kwargs): - """ - ResNet-BC-14b model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model (bottleneck compressed). - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=14, bottleneck=True, conv1_stride=False, model_name="resnetbc14b", **kwargs) - - -def resnet16(**kwargs): - """ - ResNet-16 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=16, model_name="resnet16", **kwargs) - - -def resnet18_wd4(**kwargs): - """ - ResNet-18 model with 0.25 width scale from 'Deep Residual Learning for Image Recognition,' - https://arxiv.org/abs/1512.03385. It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=18, width_scale=0.25, model_name="resnet18_wd4", **kwargs) - - -def resnet18_wd2(**kwargs): - """ - ResNet-18 model with 0.5 width scale from 'Deep Residual Learning for Image Recognition,' - https://arxiv.org/abs/1512.03385. It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=18, width_scale=0.5, model_name="resnet18_wd2", **kwargs) - - -def resnet18_w3d4(**kwargs): - """ - ResNet-18 model with 0.75 width scale from 'Deep Residual Learning for Image Recognition,' - https://arxiv.org/abs/1512.03385. It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=18, width_scale=0.75, model_name="resnet18_w3d4", **kwargs) - - -def resnet18(**kwargs): - """ - ResNet-18 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=18, model_name="resnet18", **kwargs) - - -def resnet26(**kwargs): - """ - ResNet-26 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=26, bottleneck=False, model_name="resnet26", **kwargs) - - -def resnetbc26b(**kwargs): - """ - ResNet-BC-26b model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model (bottleneck compressed). - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=26, bottleneck=True, conv1_stride=False, model_name="resnetbc26b", **kwargs) - - -def resnet34(**kwargs): - """ - ResNet-34 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=34, model_name="resnet34", **kwargs) - - -def resnetbc38b(**kwargs): - """ - ResNet-BC-38b model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model (bottleneck compressed). - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=38, bottleneck=True, conv1_stride=False, model_name="resnetbc38b", **kwargs) - - -def resnet50(**kwargs): - """ - ResNet-50 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=50, model_name="resnet50", **kwargs) - - -def resnet50b(**kwargs): - """ - ResNet-50 model with stride at the second convolution in bottleneck block from 'Deep Residual Learning for Image - Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=50, conv1_stride=False, model_name="resnet50b", **kwargs) - - -def resnet101(**kwargs): - """ - ResNet-101 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=101, model_name="resnet101", **kwargs) - - -def resnet101b(**kwargs): - """ - ResNet-101 model with stride at the second convolution in bottleneck block from 'Deep Residual Learning for Image - Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=101, conv1_stride=False, model_name="resnet101b", **kwargs) - - -def resnet152(**kwargs): - """ - ResNet-152 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=152, model_name="resnet152", **kwargs) - - -def resnet152b(**kwargs): - """ - ResNet-152 model with stride at the second convolution in bottleneck block from 'Deep Residual Learning for Image - Recognition,' https://arxiv.org/abs/1512.03385. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=152, conv1_stride=False, model_name="resnet152b", **kwargs) - - -def resnet200(**kwargs): - """ - ResNet-200 model from 'Deep Residual Learning for Image Recognition,' https://arxiv.org/abs/1512.03385. - It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=200, model_name="resnet200", **kwargs) - - -def resnet200b(**kwargs): - """ - ResNet-200 model with stride at the second convolution in bottleneck block from 'Deep Residual Learning for Image - Recognition,' https://arxiv.org/abs/1512.03385. It's an experimental model. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnet(blocks=200, conv1_stride=False, model_name="resnet200b", **kwargs) - - -def _calc_width(net): - import numpy as np - net_params = filter(lambda p: p.requires_grad, net.parameters()) - weight_count = 0 - for param in net_params: - weight_count += np.prod(param.size()) - return weight_count - - -def _test(): - import torch - - pretrained = False - - models = [ - resnet10, - resnet12, - resnet14, - resnetbc14b, - resnet16, - resnet18_wd4, - resnet18_wd2, - resnet18_w3d4, - resnet18, - resnet26, - resnetbc26b, - resnet34, - resnetbc38b, - resnet50, - resnet50b, - resnet101, - resnet101b, - resnet152, - resnet152b, - resnet200, - resnet200b, - ] - - for model in models: - - net = model(pretrained=pretrained) - - # net.train() - net.eval() - weight_count = _calc_width(net) - print("m={}, {}".format(model.__name__, weight_count)) - assert (model != resnet10 or weight_count == 5418792) - assert (model != resnet12 or weight_count == 5492776) - assert (model != resnet14 or weight_count == 5788200) - assert (model != resnetbc14b or weight_count == 10064936) - assert (model != resnet16 or weight_count == 6968872) - assert (model != resnet18_wd4 or weight_count == 3937400) - assert (model != resnet18_wd2 or weight_count == 5804296) - assert (model != resnet18_w3d4 or weight_count == 8476056) - assert (model != resnet18 or weight_count == 11689512) - assert (model != resnet26 or weight_count == 17960232) - assert (model != resnetbc26b or weight_count == 15995176) - assert (model != resnet34 or weight_count == 21797672) - assert (model != resnetbc38b or weight_count == 21925416) - assert (model != resnet50 or weight_count == 25557032) - assert (model != resnet50b or weight_count == 25557032) - assert (model != resnet101 or weight_count == 44549160) - assert (model != resnet101b or weight_count == 44549160) - assert (model != resnet152 or weight_count == 60192808) - assert (model != resnet152b or weight_count == 60192808) - assert (model != resnet200 or weight_count == 64673832) - assert (model != resnet200b or weight_count == 64673832) - - batch = 4 - x = torch.randn(batch, 3, 224, 224) - y = net(x) - y.sum().backward() - assert (tuple(y.size()) == (batch, 1000)) - - -if __name__ == "__main__": - _test() diff --git a/cv/classification/seresnext/pytorch/resnext.py b/cv/classification/seresnext/pytorch/resnext.py deleted file mode 100644 index 2f677a920..000000000 --- a/cv/classification/seresnext/pytorch/resnext.py +++ /dev/null @@ -1,470 +0,0 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright Declaration: This software, including all of its code and documentation, -# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX -# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright -# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar -# CoreX. No user of this software shall have any right, ownership or interest in this software and -# any use of this software shall be in compliance with the terms and conditions of the End User -# License Agreement. -""" - ResNeXt for ImageNet-1K, implemented in PyTorch. - Original paper: 'Aggregated Residual Transformations for Deep Neural Networks,' http://arxiv.org/abs/1611.05431. -""" - -__all__ = ['ResNeXt', 'resnext14_16x4d', 'resnext14_32x2d', 'resnext14_32x4d', 'resnext26_16x4d', 'resnext26_32x2d', - 'resnext26_32x4d', 'resnext38_32x4d', 'resnext50_32x4d', 'resnext101_32x4d', 'resnext101_64x4d', - 'ResNeXtBottleneck', 'ResNeXtUnit'] - -import os -import math -import torch.nn as nn -import torch.nn.init as init -from common import conv1x1_block, conv3x3_block -from resnet import ResInitBlock - - -class ResNeXtBottleneck(nn.Module): - """ - ResNeXt bottleneck block for residual path in ResNeXt unit. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the convolution. - cardinality: int - Number of groups. - bottleneck_width: int - Width of bottleneck block. - bottleneck_factor : int, default 4 - Bottleneck factor. - """ - def __init__(self, - in_channels, - out_channels, - stride, - cardinality, - bottleneck_width, - bottleneck_factor=4): - super(ResNeXtBottleneck, self).__init__() - mid_channels = out_channels // bottleneck_factor - D = int(math.floor(mid_channels * (bottleneck_width / 64.0))) - group_width = cardinality * D - - self.conv1 = conv1x1_block( - in_channels=in_channels, - out_channels=group_width) - self.conv2 = conv3x3_block( - in_channels=group_width, - out_channels=group_width, - stride=stride, - groups=cardinality) - self.conv3 = conv1x1_block( - in_channels=group_width, - out_channels=out_channels, - activation=None) - - def forward(self, x): - x = self.conv1(x) - x = self.conv2(x) - x = self.conv3(x) - return x - - -class ResNeXtUnit(nn.Module): - """ - ResNeXt unit with residual connection. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the convolution. - cardinality: int - Number of groups. - bottleneck_width: int - Width of bottleneck block. - """ - def __init__(self, - in_channels, - out_channels, - stride, - cardinality, - bottleneck_width): - super(ResNeXtUnit, self).__init__() - self.resize_identity = (in_channels != out_channels) or (stride != 1) - - self.body = ResNeXtBottleneck( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - cardinality=cardinality, - bottleneck_width=bottleneck_width) - if self.resize_identity: - self.identity_conv = conv1x1_block( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - activation=None) - self.activ = nn.ReLU(inplace=True) - - def forward(self, x): - if self.resize_identity: - identity = self.identity_conv(x) - else: - identity = x - x = self.body(x) - x = x + identity - x = self.activ(x) - return x - - -class ResNeXt(nn.Module): - """ - ResNeXt model from 'Aggregated Residual Transformations for Deep Neural Networks,' http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - channels : list of list of int - Number of output channels for each unit. - init_block_channels : int - Number of output channels for the initial unit. - cardinality: int - Number of groups. - bottleneck_width: int - Width of bottleneck block. - in_channels : int, default 3 - Number of input channels. - in_size : tuple of two ints, default (224, 224) - Spatial size of the expected input image. - num_classes : int, default 1000 - Number of classification classes. - """ - def __init__(self, - channels, - init_block_channels, - cardinality, - bottleneck_width, - in_channels=3, - in_size=(224, 224), - num_classes=1000): - super(ResNeXt, self).__init__() - self.in_size = in_size - self.num_classes = num_classes - - self.features = nn.Sequential() - self.features.add_module("init_block", ResInitBlock( - in_channels=in_channels, - out_channels=init_block_channels)) - in_channels = init_block_channels - for i, channels_per_stage in enumerate(channels): - stage = nn.Sequential() - for j, out_channels in enumerate(channels_per_stage): - stride = 2 if (j == 0) and (i != 0) else 1 - stage.add_module("unit{}".format(j + 1), ResNeXtUnit( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - cardinality=cardinality, - bottleneck_width=bottleneck_width)) - in_channels = out_channels - self.features.add_module("stage{}".format(i + 1), stage) - self.features.add_module("final_pool", nn.AvgPool2d( - kernel_size=7, - stride=1)) - - self.output = nn.Linear( - in_features=in_channels, - out_features=num_classes) - - self._init_params() - - def _init_params(self): - for name, module in self.named_modules(): - if isinstance(module, nn.Conv2d): - init.kaiming_uniform_(module.weight) - if module.bias is not None: - init.constant_(module.bias, 0) - - def forward(self, x): - x = self.features(x) - x = x.view(x.size(0), -1) - x = self.output(x) - return x - - -def get_resnext(blocks, - cardinality, - bottleneck_width, - model_name=None, - pretrained=False, - root=os.path.join("~", ".torch", "models"), - **kwargs): - """ - Create ResNeXt model with specific parameters. - - Parameters: - ---------- - blocks : int - Number of blocks. - cardinality: int - Number of groups. - bottleneck_width: int - Width of bottleneck block. - model_name : str or None, default None - Model name for loading pretrained model. - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - - if blocks == 14: - layers = [1, 1, 1, 1] - elif blocks == 26: - layers = [2, 2, 2, 2] - elif blocks == 38: - layers = [3, 3, 3, 3] - elif blocks == 50: - layers = [3, 4, 6, 3] - elif blocks == 101: - layers = [3, 4, 23, 3] - else: - raise ValueError("Unsupported ResNeXt with number of blocks: {}".format(blocks)) - - assert (sum(layers) * 3 + 2 == blocks) - - init_block_channels = 64 - channels_per_layers = [256, 512, 1024, 2048] - - channels = [[ci] * li for (ci, li) in zip(channels_per_layers, layers)] - - net = ResNeXt( - channels=channels, - init_block_channels=init_block_channels, - cardinality=cardinality, - bottleneck_width=bottleneck_width, - **kwargs) - - if pretrained: - if (model_name is None) or (not model_name): - raise ValueError("Parameter `model_name` should be properly initialized for loading pretrained model.") - from .model_store import download_model - download_model( - net=net, - model_name=model_name, - local_model_store_dir_path=root) - - return net - - -def resnext14_16x4d(**kwargs): - """ - ResNeXt-14 (16x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=14, cardinality=16, bottleneck_width=4, model_name="resnext14_16x4d", **kwargs) - - -def resnext14_32x2d(**kwargs): - """ - ResNeXt-14 (32x2d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=14, cardinality=32, bottleneck_width=2, model_name="resnext14_32x2d", **kwargs) - - -def resnext14_32x4d(**kwargs): - """ - ResNeXt-14 (32x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=14, cardinality=32, bottleneck_width=4, model_name="resnext14_32x4d", **kwargs) - - -def resnext26_16x4d(**kwargs): - """ - ResNeXt-26 (16x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=26, cardinality=16, bottleneck_width=4, model_name="resnext26_16x4d", **kwargs) - - -def resnext26_32x2d(**kwargs): - """ - ResNeXt-26 (32x2d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=26, cardinality=32, bottleneck_width=2, model_name="resnext26_32x2d", **kwargs) - - -def resnext26_32x4d(**kwargs): - """ - ResNeXt-26 (32x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=26, cardinality=32, bottleneck_width=4, model_name="resnext26_32x4d", **kwargs) - - -def resnext38_32x4d(**kwargs): - """ - ResNeXt-38 (32x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=38, cardinality=32, bottleneck_width=4, model_name="resnext38_32x4d", **kwargs) - - -def resnext50_32x4d(**kwargs): - """ - ResNeXt-50 (32x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=50, cardinality=32, bottleneck_width=4, model_name="resnext50_32x4d", **kwargs) - - -def resnext101_32x4d(**kwargs): - """ - ResNeXt-101 (32x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=101, cardinality=32, bottleneck_width=4, model_name="resnext101_32x4d", **kwargs) - - -def resnext101_64x4d(**kwargs): - """ - ResNeXt-101 (64x4d) model from 'Aggregated Residual Transformations for Deep Neural Networks,' - http://arxiv.org/abs/1611.05431. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_resnext(blocks=101, cardinality=64, bottleneck_width=4, model_name="resnext101_64x4d", **kwargs) - - -def _calc_width(net): - import numpy as np - net_params = filter(lambda p: p.requires_grad, net.parameters()) - weight_count = 0 - for param in net_params: - weight_count += np.prod(param.size()) - return weight_count - - -def _test(): - import torch - - pretrained = False - - models = [ - resnext14_16x4d, - resnext14_32x2d, - resnext14_32x4d, - resnext26_16x4d, - resnext26_32x2d, - resnext26_32x4d, - resnext38_32x4d, - resnext50_32x4d, - resnext101_32x4d, - resnext101_64x4d, - ] - - for model in models: - - net = model(pretrained=pretrained) - - # net.train() - net.eval() - weight_count = _calc_width(net) - print("m={}, {}".format(model.__name__, weight_count)) - assert (model != resnext14_16x4d or weight_count == 7127336) - assert (model != resnext14_32x2d or weight_count == 7029416) - assert (model != resnext14_32x4d or weight_count == 9411880) - assert (model != resnext26_16x4d or weight_count == 10119976) - assert (model != resnext26_32x2d or weight_count == 9924136) - assert (model != resnext26_32x4d or weight_count == 15389480) - assert (model != resnext38_32x4d or weight_count == 21367080) - assert (model != resnext50_32x4d or weight_count == 25028904) - assert (model != resnext101_32x4d or weight_count == 44177704) - assert (model != resnext101_64x4d or weight_count == 83455272) - - x = torch.randn(1, 3, 224, 224) - y = net(x) - y.sum().backward() - assert (tuple(y.size()) == (1, 1000)) - - -if __name__ == "__main__": - _test() diff --git a/cv/classification/seresnext/pytorch/run_train.py b/cv/classification/seresnext/pytorch/run_train.py deleted file mode 100644 index 35729dda9..000000000 --- a/cv/classification/seresnext/pytorch/run_train.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -import os -import sys - -import torchvision - -sys.path.append("../../torchvision/pytorch") - -from train import train_model -from utils import padding_conv_channel_to_4 -import seresnext - -def create_model(args): - model = seresnext.__dict__[args.model](pretrained=args.pretrained, num_classes=args.num_classes) - args.padding_channel = False - - return model - - -train_model(create_model) diff --git a/cv/classification/seresnext/pytorch/se_block.py b/cv/classification/seresnext/pytorch/se_block.py new file mode 100644 index 000000000..247186ca7 --- /dev/null +++ b/cv/classification/seresnext/pytorch/se_block.py @@ -0,0 +1,117 @@ +from torch import nn + + + +class SELayer(nn.Module): + def __init__(self, channel, reduction=16): + super(SELayer, self).__init__() + self.avg_pool = nn.AdaptiveAvgPool2d(1) + self.fc = nn.Sequential( + nn.Linear(channel, channel // reduction, bias=False), + nn.ReLU(inplace=True), + nn.Linear(channel // reduction, channel, bias=False), + nn.Sigmoid() + ) + + def forward(self, x): + b, c, _, _ = x.size() + y = self.avg_pool(x).view(b, c) + y = self.fc(y).view(b, c, 1, 1) + return x * y.expand_as(x) + + +def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1): + """3x3 convolution with padding""" + return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, + padding=dilation, groups=groups, bias=False, dilation=dilation) + + +def conv1x1(in_planes, out_planes, stride=1): + """1x1 convolution""" + return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False) + + +class SE_BasicBlock(nn.Module): + expansion = 1 + + def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1, + base_width=64, dilation=1, norm_layer=None, reduction=16): + super(SE_BasicBlock, self).__init__() + if norm_layer is None: + norm_layer = nn.BatchNorm2d + if groups != 1 or base_width != 64: + raise ValueError('BasicBlock only supports groups=1 and base_width=64') + if dilation > 1: + raise NotImplementedError("Dilation > 1 not supported in BasicBlock") + # Both self.conv1 and self.downsample layers downsample the input when stride != 1 + self.conv1 = conv3x3(inplanes, planes, stride) + self.bn1 = norm_layer(planes) + self.relu = nn.ReLU(inplace=True) + self.conv2 = conv3x3(planes, planes) + self.bn2 = norm_layer(planes) + self.se = SELayer(planes, reduction) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + identity = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.se(out) + + if self.downsample is not None: + identity = self.downsample(x) + + out += identity + out = self.relu(out) + + return out + +class SE_Bottleneck(nn.Module): + expansion = 4 + + def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1, + base_width=64, dilation=1, norm_layer=None, reduction=16): + super(SE_Bottleneck, self).__init__() + if norm_layer is None: + norm_layer = nn.BatchNorm2d + width = int(planes * (base_width / 64.)) * groups + # Both self.conv2 and self.downsample layers downsample the input when stride != 1 + self.conv1 = conv1x1(inplanes, width) + self.bn1 = norm_layer(width) + self.conv2 = conv3x3(width, width, stride, groups, dilation) + self.bn2 = norm_layer(width) + self.conv3 = conv1x1(width, planes * self.expansion) + self.bn3 = norm_layer(planes * self.expansion) + self.relu = nn.ReLU(inplace=True) + self.se = SELayer(planes * self.expansion, reduction) + self.downsample = downsample + self.stride = stride + + def forward(self, x): + identity = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + out = self.se(out) + + if self.downsample is not None: + identity = self.downsample(x) + + out += identity + out = self.relu(out) + + return out \ No newline at end of file diff --git a/cv/classification/seresnext/pytorch/seresnext.py b/cv/classification/seresnext/pytorch/seresnext.py index 48cb364bd..8785f87ac 100644 --- a/cv/classification/seresnext/pytorch/seresnext.py +++ b/cv/classification/seresnext/pytorch/seresnext.py @@ -1,289 +1,161 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. -# Copyright Declaration: This software, including all of its code and documentation, -# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX -# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright -# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar -# CoreX. No user of this software shall have any right, ownership or interest in this software and -# any use of this software shall be in compliance with the terms and conditions of the End User -# License Agreement. -""" - SE-ResNeXt for ImageNet-1K, implemented in PyTorch. - Original paper: 'Squeeze-and-Excitation Networks,' https://arxiv.org/abs/1709.01507. -""" - -__all__ = ['SEResNeXt', 'seresnext50_32x4d', 'seresnext101_32x4d', 'seresnext101_64x4d'] - -import os -import torch.nn as nn -import torch.nn.init as init -from common import conv1x1_block, SEBlock -from resnet import ResInitBlock -from resnext import ResNeXtBottleneck - - -class SEResNeXtUnit(nn.Module): - """ - SE-ResNeXt unit. - - Parameters: - ---------- - in_channels : int - Number of input channels. - out_channels : int - Number of output channels. - stride : int or tuple/list of 2 int - Strides of the convolution. - cardinality: int - Number of groups. - bottleneck_width: int - Width of bottleneck block. - """ - def __init__(self, - in_channels, - out_channels, - stride, - cardinality, - bottleneck_width): - super(SEResNeXtUnit, self).__init__() - self.resize_identity = (in_channels != out_channels) or (stride != 1) - - self.body = ResNeXtBottleneck( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - cardinality=cardinality, - bottleneck_width=bottleneck_width) - self.se = SEBlock(channels=out_channels) - if self.resize_identity: - self.identity_conv = conv1x1_block( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - activation=None) - self.activ = nn.ReLU(inplace=True) - - def forward(self, x): - if self.resize_identity: - identity = self.identity_conv(x) - else: - identity = x - x = self.body(x) - x = self.se(x) - x = x + identity - x = self.activ(x) - return x - - -class SEResNeXt(nn.Module): - """ - SE-ResNeXt model from 'Squeeze-and-Excitation Networks,' https://arxiv.org/abs/1709.01507. - - Parameters: - ---------- - channels : list of list of int - Number of output channels for each unit. - init_block_channels : int - Number of output channels for the initial unit. - cardinality: int - Number of groups. - bottleneck_width: int - Width of bottleneck block. - in_channels : int, default 3 - Number of input channels. - in_size : tuple of two ints, default (224, 224) - Spatial size of the expected input image. - num_classes : int, default 1000 - Number of classification classes. - """ - def __init__(self, - channels, - init_block_channels, - cardinality, - bottleneck_width, - in_channels=3, - in_size=(224, 224), - num_classes=1000): - super(SEResNeXt, self).__init__() - self.in_size = in_size - self.num_classes = num_classes - - self.features = nn.Sequential() - self.features.add_module("init_block", ResInitBlock( - in_channels=in_channels, - out_channels=init_block_channels)) - in_channels = init_block_channels - for i, channels_per_stage in enumerate(channels): - stage = nn.Sequential() - for j, out_channels in enumerate(channels_per_stage): - stride = 2 if (j == 0) and (i != 0) else 1 - stage.add_module("unit{}".format(j + 1), SEResNeXtUnit( - in_channels=in_channels, - out_channels=out_channels, - stride=stride, - cardinality=cardinality, - bottleneck_width=bottleneck_width)) - in_channels = out_channels - self.features.add_module("stage{}".format(i + 1), stage) - self.features.add_module("final_pool", nn.AvgPool2d( - kernel_size=7, - stride=1)) - - self.output = nn.Linear( - in_features=in_channels, - out_features=num_classes) - - self._init_params() - - def _init_params(self): - for name, module in self.named_modules(): - if isinstance(module, nn.Conv2d): - init.kaiming_uniform_(module.weight) - if module.bias is not None: - init.constant_(module.bias, 0) - - def forward(self, x): - x = self.features(x) - x = x.view(x.size(0), -1) - x = self.output(x) - return x - - -def get_seresnext(blocks, - cardinality, - bottleneck_width, - model_name=None, - pretrained=False, - root=os.path.join("~", ".torch", "models"), - **kwargs): - """ - Create SE-ResNeXt model with specific parameters. - - Parameters: - ---------- - blocks : int - Number of blocks. - cardinality: int - Number of groups. - bottleneck_width: int - Width of bottleneck block. - model_name : str or None, default None - Model name for loading pretrained model. - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - - if blocks == 50: - layers = [3, 4, 6, 3] - elif blocks == 101: - layers = [3, 4, 23, 3] - else: - raise ValueError("Unsupported SE-ResNeXt with number of blocks: {}".format(blocks)) - - init_block_channels = 64 - channels_per_layers = [256, 512, 1024, 2048] - - channels = [[ci] * li for (ci, li) in zip(channels_per_layers, layers)] - - net = SEResNeXt( - channels=channels, - init_block_channels=init_block_channels, - cardinality=cardinality, - bottleneck_width=bottleneck_width, - **kwargs) - - if pretrained: - if (model_name is None) or (not model_name): - raise ValueError("Parameter `model_name` should be properly initialized for loading pretrained model.") - from .model_store import download_model - download_model( - net=net, - model_name=model_name, - local_model_store_dir_path=root) - - return net - - -def seresnext50_32x4d(**kwargs): - """ - SE-ResNeXt-50 (32x4d) model from 'Squeeze-and-Excitation Networks,' https://arxiv.org/abs/1709.01507. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import torch +from functools import partial +from torchvision.models.resnet import _resnet +from se_block import SE_Bottleneck +from torchvision.models._utils import _ovewrite_named_param, handle_legacy_interface +from torchvision.models._api import register_model, Weights, WeightsEnum +from torchvision.models._meta import _IMAGENET_CATEGORIES +from torchvision.transforms._presets import ImageClassification +from typing import Any, Callable, List, Optional, Type, Union +from torchvision.models.resnet import ResNet +_COMMON_META = { + "min_size": (1, 1), + "categories": _IMAGENET_CATEGORIES, +} +class ResNeXt50_32X4D_Weights(WeightsEnum): + IMAGENET1K_V1 = Weights( + url="https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth", + transforms=partial(ImageClassification, crop_size=224), + meta={ + **_COMMON_META, + "num_params": 25028904, + "recipe": "https://github.com/pytorch/vision/tree/main/references/classification#resnext", + "_metrics": { + "ImageNet-1K": { + "acc@1": 77.618, + "acc@5": 93.698, + } + }, + "_docs": """These weights reproduce closely the results of the paper using a simple training recipe.""", + }, + ) + IMAGENET1K_V2 = Weights( + url="https://download.pytorch.org/models/resnext50_32x4d-1a0047aa.pth", + transforms=partial(ImageClassification, crop_size=224, resize_size=232), + meta={ + **_COMMON_META, + "num_params": 25028904, + "recipe": "https://github.com/pytorch/vision/issues/3995#new-recipe", + "_metrics": { + "ImageNet-1K": { + "acc@1": 81.198, + "acc@5": 95.340, + } + }, + "_docs": """ + These weights improve upon the results of the original paper by using TorchVision's `new training recipe + `_. + """, + }, + ) + DEFAULT = IMAGENET1K_V2 + +class ResNeXt101_32X8D_Weights(WeightsEnum): + IMAGENET1K_V1 = Weights( + url="https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth", + transforms=partial(ImageClassification, crop_size=224), + meta={ + **_COMMON_META, + "num_params": 88791336, + "recipe": "https://github.com/pytorch/vision/tree/main/references/classification#resnext", + "_metrics": { + "ImageNet-1K": { + "acc@1": 77.618, + "acc@5": 93.698, + } + }, + "_docs": """These weights reproduce closely the results of the paper using a simple training recipe.""", + }, + ) + IMAGENET1K_V2 = Weights( + url="https://download.pytorch.org/models/resnext50_32x4d-1a0047aa.pth", + transforms=partial(ImageClassification, crop_size=224, resize_size=232), + meta={ + **_COMMON_META, + "num_params": 25028904, + "recipe": "https://github.com/pytorch/vision/issues/3995#new-recipe", + "_metrics": { + "ImageNet-1K": { + "acc@1": 81.198, + "acc@5": 95.340, + } + }, + "_docs": """ + These weights improve upon the results of the original paper by using TorchVision's `new training recipe + `_. + """, + }, + ) + DEFAULT = IMAGENET1K_V2 + + +@handle_legacy_interface(weights=("pretrained", ResNeXt50_32X4D_Weights.IMAGENET1K_V1)) +def resnext50_32x4d( + *, weights: Optional[ResNeXt50_32X4D_Weights] = None, progress: bool = True, **kwargs: Any +) -> ResNet: + """ResNeXt-50 32x4d model from + `Aggregated Residual Transformation for Deep Neural Networks `_. + + Args: + weights (:class:`~torchvision.models.ResNeXt50_32X4D_Weights`, optional): The + pretrained weights to use. See + :class:`~torchvision.models.ResNext50_32X4D_Weights` below for + more details, and possible values. By default, no pre-trained + weights are used. + progress (bool, optional): If True, displays a progress bar of the + download to stderr. Default is True. + **kwargs: parameters passed to the ``torchvision.models.resnet.ResNet`` + base class. Please refer to the `source code + `_ + for more details about this class. + .. autoclass:: torchvision.models.ResNeXt50_32X4D_Weights + :members: """ - return get_seresnext(blocks=50, cardinality=32, bottleneck_width=4, model_name="seresnext50_32x4d", **kwargs) - - -def seresnext101_32x4d(**kwargs): - """ - SE-ResNeXt-101 (32x4d) model from 'Squeeze-and-Excitation Networks,' https://arxiv.org/abs/1709.01507. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_seresnext(blocks=101, cardinality=32, bottleneck_width=4, model_name="seresnext101_32x4d", **kwargs) - - -def seresnext101_64x4d(**kwargs): + weights = ResNeXt50_32X4D_Weights.verify(weights) + + _ovewrite_named_param(kwargs, "groups", 32) + _ovewrite_named_param(kwargs, "width_per_group", 4) + return _resnet(SE_Bottleneck, [3, 4, 6, 3], weights, progress, **kwargs) + + + +@handle_legacy_interface(weights=("pretrained", ResNeXt101_32X8D_Weights.IMAGENET1K_V1)) +def resnext101_32x8d( + *, weights: Optional[ResNeXt101_32X8D_Weights] = None, progress: bool = True, **kwargs: Any +) -> ResNet: + """ResNeXt-101 32x8d model from + `Aggregated Residual Transformation for Deep Neural Networks `_. + Args: + weights (:class:`~torchvision.models.ResNeXt101_32X8D_Weights`, optional): The + pretrained weights to use. See + :class:`~torchvision.models.ResNeXt101_32X8D_Weights` below for + more details, and possible values. By default, no pre-trained + weights are used. + progress (bool, optional): If True, displays a progress bar of the + download to stderr. Default is True. + **kwargs: parameters passed to the ``torchvision.models.resnet.ResNet`` + base class. Please refer to the `source code + `_ + for more details about this class. + .. autoclass:: torchvision.models.ResNeXt101_32X8D_Weights + :members: """ - SE-ResNeXt-101 (64x4d) model from 'Squeeze-and-Excitation Networks,' https://arxiv.org/abs/1709.01507. - - Parameters: - ---------- - pretrained : bool, default False - Whether to load the pretrained weights for model. - root : str, default '~/.torch/models' - Location for keeping the model parameters. - """ - return get_seresnext(blocks=101, cardinality=64, bottleneck_width=4, model_name="seresnext101_64x4d", **kwargs) - - -def _calc_width(net): - import numpy as np - net_params = filter(lambda p: p.requires_grad, net.parameters()) - weight_count = 0 - for param in net_params: - weight_count += np.prod(param.size()) - return weight_count - - -def _test(): - import torch - - pretrained = False - - models = [ - seresnext50_32x4d, - seresnext101_32x4d, - seresnext101_64x4d, - ] - - for model in models: - - net = model(pretrained=pretrained) - - # net.train() - net.eval() - weight_count = _calc_width(net) - print("m={}, {}".format(model.__name__, weight_count)) - assert (model != seresnext50_32x4d or weight_count == 27559896) - assert (model != seresnext101_32x4d or weight_count == 48955416) - assert (model != seresnext101_64x4d or weight_count == 88232984) - - x = torch.randn(1, 3, 224, 224) - y = net(x) - y.sum().backward() - assert (tuple(y.size()) == (1, 1000)) - + weights = ResNeXt101_32X8D_Weights.verify(weights) -if __name__ == "__main__": - _test() + _ovewrite_named_param(kwargs, "groups", 32) + _ovewrite_named_param(kwargs, "width_per_group", 8) + return _resnet(SE_Bottleneck, [3, 4, 23, 3], weights, progress, **kwargs) \ No newline at end of file diff --git a/cv/classification/seresnext/pytorch/train.py b/cv/classification/seresnext/pytorch/train.py new file mode 100644 index 000000000..047cedc40 --- /dev/null +++ b/cv/classification/seresnext/pytorch/train.py @@ -0,0 +1,325 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +import datetime +import os +import sys + +import time +import math + +import torch +import torch.utils.data + +try: + from torch.cuda.amp import autocast, GradScaler + scaler = GradScaler() +except: + autocast = None + scaler = None + + +from torch import nn +import torch.distributed as dist +import torchvision + +from seresnext import resnext101_32x8d +from utils_ import (MetricLogger, SmoothedValue, accuracy, mkdir,\ + init_distributed_mode, manual_seed,\ + is_main_process, save_on_master, get_world_size) + +from dataloader.classification import get_datasets, create_dataloader + + +def compute_loss(model, image, target, criterion): + output = model(image) + if not isinstance(output, (tuple, list)): + output = [output] + losses = [] + for out in output: + losses.append(criterion(out, target)) + loss = sum(losses) + return loss, output[0] + + +def train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, print_freq, amp=False, use_dali=False): + model.train() + metric_logger = MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', SmoothedValue(window_size=1, fmt='{value}')) + metric_logger.add_meter('img/s', SmoothedValue(window_size=10, fmt='{value}')) + + header = 'Epoch: [{}]'.format(epoch) + all_fps = [] + for data in metric_logger.log_every(data_loader, print_freq, header): + if use_dali: + image, target = data[0]["data"], data[0]["label"][:, 0].long() + else: + image, target = data + start_time = time.time() + image, target = image.to(device), target.to(device) + if autocast is None or not amp: + loss, output = compute_loss(model, image, target, criterion) + else: + with autocast(): + loss, output = compute_loss(model, image, target, criterion) + + optimizer.zero_grad() + if scaler is not None and amp: + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + else: + loss.backward() + optimizer.step() + + torch.cuda.synchronize() + end_time = time.time() + + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + batch_size = image.shape[0] + loss_value = loss.item() + if not math.isfinite(loss_value): + print("Loss is {}, stopping training".format(loss_value)) + sys.exit(1) + metric_logger.update(loss=loss.item(), lr=optimizer.param_groups[0]["lr"]) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + fps = batch_size / (end_time - start_time) * get_world_size() + metric_logger.meters['img/s'].update(fps) + all_fps.append(fps) + + print(header, 'Avg img/s:', sum(all_fps) / len(all_fps)) + + +def evaluate(model, criterion, data_loader, device, print_freq=100, use_dali=False): + model.eval() + metric_logger = MetricLogger(delimiter=" ") + header = 'Test:' + with torch.no_grad(): + for data in metric_logger.log_every(data_loader, print_freq, header): + if use_dali: + image, target = data[0]["data"], data[0]["label"][:, 0].long() + else: + image, target = data + image = image.to(device, non_blocking=True) + target = target.to(device, non_blocking=True) + output = model(image) + loss = criterion(output, target) + + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + # FIXME need to take into account that the datasets + # could have been padded in distributed setup + batch_size = image.shape[0] + metric_logger.update(loss=loss.item()) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + # gather the stats from all processes + metric_logger.synchronize_between_processes() + + print(' * Acc@1 {top1.global_avg:.3f} Acc@5 {top5.global_avg:.3f}' + .format(top1=metric_logger.acc1, top5=metric_logger.acc5)) + return metric_logger.acc1.global_avg + + +def _get_cache_path(filepath): + import hashlib + h = hashlib.sha1(filepath.encode()).hexdigest() + cache_path = os.path.join("~", ".torch", "vision", "datasets", "imagefolder", h[:10] + ".pt") + cache_path = os.path.expanduser(cache_path) + return cache_path + + +def main(args): + if args.output_dir: + mkdir(args.output_dir) + + init_distributed_mode(args) + print(args) + + device = torch.device(args.device) + + manual_seed(args.seed, deterministic=False) + torch.backends.cudnn.benchmark = True + + # WARN: + if dist.is_initialized(): + num_gpu = dist.get_world_size() + else: + num_gpu = 1 + + global_batch_size = num_gpu * args.batch_size + + train_dir = os.path.join(args.data_path, 'train') + val_dir = os.path.join(args.data_path, 'val') + + num_classes = len(os.listdir(train_dir)) + if 0 < num_classes < 13: + if global_batch_size > 512: + if is_main_process(): + print("WARN: Updating global batch size to 512, avoid non-convergence when training small dataset.") + args.batch_size = 512 // num_gpu + + if args.pretrained: + num_classes = 1000 + + data_loader, data_loader_test = create_dataloader(train_dir, val_dir, args) + + print("Creating model") + ###model = torchvision.models.__dict__[args.model](pretrained=args.pretrained, num_classes=num_classes) + model = None + if args.model == "seresnext101_32x8d": + model = resnext101_32x8d(pretrained=args.pretrained, num_classes=num_classes) + elif args.model == "seresnext50_32x4d": + model = resnext50_32x4d(pretrained=args.pretrained, num_classes=num_classes) + else: + raise RuntimeError("model {} is not support ".format(args.model)) + + if model is None: + raise RuntimeError("no model init... ") + model.to(device) + if args.distributed and args.sync_bn: + model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model) + + criterion = nn.CrossEntropyLoss() + + opt_name = args.opt.lower() + if opt_name == 'sgd': + optimizer = torch.optim.SGD( + model.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.weight_decay) + elif opt_name == 'rmsprop': + optimizer = torch.optim.RMSprop(model.parameters(), lr=args.lr, momentum=args.momentum, + weight_decay=args.weight_decay, eps=0.0316, alpha=0.9) + else: + raise RuntimeError("Invalid optimizer {}. Only SGD and RMSprop are supported.".format(args.opt)) + + lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=args.lr_step_size, gamma=args.lr_gamma) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + if args.resume: + checkpoint = torch.load(args.resume, map_location='cpu') + model_without_ddp.load_state_dict(checkpoint['model']) + optimizer.load_state_dict(checkpoint['optimizer']) + lr_scheduler.load_state_dict(checkpoint['lr_scheduler']) + args.start_epoch = checkpoint['epoch'] + 1 + + if args.test_only: + evaluate(model, criterion, data_loader_test, device=device) + return + + print("Start training") + start_time = time.time() + best_acc = 0 + for epoch in range(args.start_epoch, args.epochs): + epoch_start_time = time.time() + if args.distributed and not args.dali: + data_loader.sampler.set_epoch(epoch) + train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, args.print_freq, args.amp, use_dali=args.dali) + lr_scheduler.step() + acc_avg = evaluate(model, criterion, data_loader_test, device=device, use_dali=args.dali) + if acc_avg > best_acc: + if args.output_dir: + checkpoint = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'lr_scheduler': lr_scheduler.state_dict(), + 'epoch': epoch, + 'args': args} + save_on_master( + checkpoint, + os.path.join(args.output_dir, 'model_best.pth')) + epoch_total_time = time.time() - epoch_start_time + epoch_total_time_str = str(datetime.timedelta(seconds=int(epoch_total_time))) + print('epoch time {}'.format(epoch_total_time_str)) + best_acc = acc_avg + + if args.dali: + data_loader.reset() + data_loader_test.reset() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +def get_args_parser(add_help=True): + import argparse + parser = argparse.ArgumentParser(description='PyTorch Classification Training', add_help=add_help) + + parser.add_argument('--data-path', default='../../../../../data/datasets/imagenette/', help='dataset') + parser.add_argument('--model', default='serenext101_32x8d', help='model') + parser.add_argument('--device', default='cuda', help='device') + parser.add_argument('-b', '--batch-size', default=32, type=int) + parser.add_argument('--epochs', default=90, type=int, metavar='N', + help='number of total epochs to run') + parser.add_argument('-j', '--workers', default=4, type=int, metavar='N', + help='number of data loading workers (default: 4)') + parser.add_argument('--opt', default='sgd', type=str, help='optimizer') + parser.add_argument('--lr', default=0.1, type=float, help='initial learning rate') + parser.add_argument('--momentum', default=0.9, type=float, metavar='M', + help='momentum') + parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float, + metavar='W', help='weight decay (default: 1e-4)', + dest='weight_decay') + parser.add_argument('--lr-step-size', default=30, type=int, help='decrease lr every step-size epochs') + parser.add_argument('--lr-gamma', default=0.1, type=float, help='decrease lr by a factor of lr-gamma') + parser.add_argument('--print-freq', default=10, type=int, help='print frequency') + parser.add_argument('--output-dir', default='.', help='path where to save') + parser.add_argument('--resume', default='', help='resume from checkpoint') + parser.add_argument('--start-epoch', default=0, type=int, metavar='N', + help='start epoch') + parser.add_argument( + "--cache-dataset", + dest="cache_dataset", + help="Cache the datasets for quicker initialization. It also serializes the transforms", + action="store_true", + ) + parser.add_argument( + "--sync-bn", + dest="sync_bn", + help="Use sync batch norm", + action="store_true", + ) + parser.add_argument( + "--test-only", + dest="test_only", + help="Only test the model", + action="store_true", + ) + parser.add_argument( + "--pretrained", + dest="pretrained", + help="Use pre-trained models from the modelzoo", + action="store_true", + ) + parser.add_argument('--auto-augment', default=None, help='auto augment policy (default: None)') + parser.add_argument('--random-erase', default=0.0, type=float, help='random erasing probability (default: 0.0)') + parser.add_argument( + "--dali", + help="Use dali as dataloader", + default=False, + action="store_true", + ) + + # distributed training parameters + parser.add_argument('--local_rank', '--local-rank', default=-1, type=int, + help='Local rank') + parser.add_argument('--world-size', default=1, type=int, + help='number of distributed processes') + parser.add_argument('--dist-url', default='env://', help='url used to set up distributed training') + parser.add_argument('--amp', action='store_true', help='Automatic Mixed Precision training') + parser.add_argument('--seed', default=42, type=int, help='Random seed') + return parser + + +if __name__ == "__main__": + args = get_args_parser().parse_args() + try: + from dltest import show_training_arguments + show_training_arguments(args) + except: + pass + main(args) diff --git a/cv/classification/seresnext/pytorch/train_seresnext101_32x4d_amp_dist.sh b/cv/classification/seresnext/pytorch/train_seresnext101_32x8d_amp_dist.sh old mode 100755 new mode 100644 similarity index 86% rename from cv/classification/seresnext/pytorch/train_seresnext101_32x4d_amp_dist.sh rename to cv/classification/seresnext/pytorch/train_seresnext101_32x8d_amp_dist.sh index 7d89f81cb..ac67199c0 --- a/cv/classification/seresnext/pytorch/train_seresnext101_32x4d_amp_dist.sh +++ b/cv/classification/seresnext/pytorch/train_seresnext101_32x8d_amp_dist.sh @@ -30,7 +30,7 @@ if [ ! -z "${DEBUG}" ];then PYTHONAR="${PYTHONAR} -m pdb" fi cd ${ROOT_DIR} -python3 $PYTHONARG ${ROOT_DIR}/run_train.py \ - --model seresnext101_32x4d --dali --dali-cpu --data-path $DATA_PATH \ - --opt fused_sgd --batch-size 256 \ - --amp --nhwc "$@" +python3 $PYTHONARG train.py \ + --model seresnext101_32x8d --data-path $DATA_PATH \ + --batch-size 64 --lr 1e-2 \ + --amp --wd 0.0001 "$@" \ No newline at end of file diff --git a/cv/classification/seresnext/pytorch/utils_.py b/cv/classification/seresnext/pytorch/utils_.py new file mode 100644 index 000000000..3d34c4df0 --- /dev/null +++ b/cv/classification/seresnext/pytorch/utils_.py @@ -0,0 +1,156 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque, OrderedDict +import copy +import datetime +import hashlib +import time +import torch +import torch.distributed as dist + +import errno +import os + +from common_utils import * + + +def accuracy(output, target, topk=(1,)): + """Computes the accuracy over the k top predictions for the specified values of k""" + with torch.no_grad(): + maxk = max(topk) + batch_size = target.size(0) + + _, pred = output.topk(maxk, 1, True, True) + pred = pred.t() + correct = pred.eq(target[None]) + + res = [] + for k in topk: + correct_k = correct[:k].flatten().sum(dtype=torch.float32) + res.append(correct_k * (100.0 / batch_size)) + return res + + +def average_checkpoints(inputs): + """Loads checkpoints from inputs and returns a model with averaged weights. Original implementation taken from: + https://github.com/pytorch/fairseq/blob/a48f235636557b8d3bc4922a6fa90f3a0fa57955/scripts/average_checkpoints.py#L16 + + Args: + inputs (List[str]): An iterable of string paths of checkpoints to load from. + Returns: + A dict of string keys mapping to various values. The 'model' key + from the returned dict should correspond to an OrderedDict mapping + string parameter names to torch Tensors. + """ + params_dict = OrderedDict() + params_keys = None + new_state = None + num_models = len(inputs) + for fpath in inputs: + with open(fpath, "rb") as f: + state = torch.load( + f, + map_location=( + lambda s, _: torch.serialization.default_restore_location(s, "cpu") + ), + ) + # Copies over the settings from the first checkpoint + if new_state is None: + new_state = state + model_params = state["model"] + model_params_keys = list(model_params.keys()) + if params_keys is None: + params_keys = model_params_keys + elif params_keys != model_params_keys: + raise KeyError( + "For checkpoint {}, expected list of params: {}, " + "but found: {}".format(f, params_keys, model_params_keys) + ) + for k in params_keys: + p = model_params[k] + if isinstance(p, torch.HalfTensor): + p = p.float() + if k not in params_dict: + params_dict[k] = p.clone() + # NOTE: clone() is needed in case of p is a shared parameter + else: + params_dict[k] += p + averaged_params = OrderedDict() + for k, v in params_dict.items(): + averaged_params[k] = v + if averaged_params[k].is_floating_point(): + averaged_params[k].div_(num_models) + else: + averaged_params[k] //= num_models + new_state["model"] = averaged_params + return new_state + + +def store_model_weights(model, checkpoint_path, checkpoint_key='model', strict=True): + """ + This method can be used to prepare weights files for new models. It receives as + input a model architecture and a checkpoint from the training script and produces + a file with the weights ready for release. + + Examples: + from torchvision import models as M + + # Classification + model = M.mobilenet_v3_large(pretrained=False) + print(store_model_weights(model, './class.pth')) + + # Quantized Classification + model = M.quantization.mobilenet_v3_large(pretrained=False, quantize=False) + model.fuse_model() + model.qconfig = torch.quantization.get_default_qat_qconfig('qnnpack') + _ = torch.quantization.prepare_qat(model, inplace=True) + print(store_model_weights(model, './qat.pth')) + + # Object Detection + model = M.detection.fasterrcnn_mobilenet_v3_large_fpn(pretrained=False, pretrained_backbone=False) + print(store_model_weights(model, './obj.pth')) + + # Segmentation + model = M.segmentation.deeplabv3_mobilenet_v3_large(pretrained=False, pretrained_backbone=False, aux_loss=True) + print(store_model_weights(model, './segm.pth', strict=False)) + + Args: + model (pytorch.nn.Module): The model on which the weights will be loaded for validation purposes. + checkpoint_path (str): The path of the checkpoint we will load. + checkpoint_key (str, optional): The key of the checkpoint where the model weights are stored. + Default: "model". + strict (bool): whether to strictly enforce that the keys + in :attr:`state_dict` match the keys returned by this module's + :meth:`~torch.nn.Module.state_dict` function. Default: ``True`` + + Returns: + output_path (str): The location where the weights are saved. + """ + # Store the new model next to the checkpoint_path + checkpoint_path = os.path.abspath(checkpoint_path) + output_dir = os.path.dirname(checkpoint_path) + + # Deep copy to avoid side-effects on the model object. + model = copy.deepcopy(model) + checkpoint = torch.load(checkpoint_path, map_location='cpu') + + # Load the weights to the model to validate that everything works + # and remove unnecessary weights (such as auxiliaries, etc) + model.load_state_dict(checkpoint[checkpoint_key], strict=strict) + + tmp_path = os.path.join(output_dir, str(model.__hash__())) + torch.save(model.state_dict(), tmp_path) + + sha256_hash = hashlib.sha256() + with open(tmp_path, "rb") as f: + # Read and update hash string value in blocks of 4K + for byte_block in iter(lambda: f.read(4096), b""): + sha256_hash.update(byte_block) + hh = sha256_hash.hexdigest() + + output_path = os.path.join(output_dir, "weights-" + str(hh[:8]) + ".pth") + os.replace(tmp_path, output_path) + + return output_path diff --git a/cv/classification/shufflenetv2/pytorch/__init__.py b/cv/classification/shufflenetv2/pytorch/__init__.py new file mode 100644 index 000000000..6faec1658 --- /dev/null +++ b/cv/classification/shufflenetv2/pytorch/__init__.py @@ -0,0 +1,5 @@ +from .utils import * +from .common_utils import * +from .data_loader import * + +__all__ = [k for k in globals().keys() if not k.startswith("_")] diff --git a/cv/classification/shufflenetv2/pytorch/common_utils/__init__.py b/cv/classification/shufflenetv2/pytorch/common_utils/__init__.py new file mode 100644 index 000000000..32e8c4f57 --- /dev/null +++ b/cv/classification/shufflenetv2/pytorch/common_utils/__init__.py @@ -0,0 +1,23 @@ +import random + +import numpy as np + +from .dist import * +from .metric_logger import * +from .misc import * +from .smooth_value import * + +def manual_seed(seed, deterministic=False): + random.seed(seed) + np.random.seed(seed) + os.environ['PYTHONHASHSEED'] = str(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + + if deterministic: + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + else: + torch.backends.cudnn.deterministic = False + torch.backends.cudnn.benchmark = True \ No newline at end of file diff --git a/cv/classification/shufflenetv2/pytorch/common_utils/dist.py b/cv/classification/shufflenetv2/pytorch/common_utils/dist.py new file mode 100644 index 000000000..ea56ca267 --- /dev/null +++ b/cv/classification/shufflenetv2/pytorch/common_utils/dist.py @@ -0,0 +1,144 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist + + + +def setup_for_distributed(is_master): + """ + This function disables printing when not in master process + """ + import builtins as __builtin__ + builtin_print = __builtin__.print + + def print(*args, **kwargs): + force = kwargs.pop('force', False) + if is_master or force: + builtin_print(*args, **kwargs) + + __builtin__.print = print + + +def is_dist_avail_and_initialized(): + if not dist.is_available(): + return False + if not dist.is_initialized(): + return False + return True + + +def get_world_size(): + if not is_dist_avail_and_initialized(): + return 1 + return dist.get_world_size() + + +def get_rank(): + if not is_dist_avail_and_initialized(): + return 0 + return dist.get_rank() + + +def is_main_process(): + return get_rank() == 0 + + +def save_on_master(*args, **kwargs): + if is_main_process(): + torch.save(*args, **kwargs) + + +def get_dist_backend(args=None): + DIST_BACKEND_ENV = "PT_DIST_BACKEND" + if DIST_BACKEND_ENV in os.environ: + return os.environ[DIST_BACKEND_ENV] + + if args is None: + args = dict() + + backend_attr_name = "dist_backend" + + if hasattr(args, backend_attr_name): + return getattr(args, backend_attr_name) + + if backend_attr_name in args: + return args[backend_attr_name] + + return "nccl" + + +def init_distributed_mode(args): + if 'RANK' in os.environ and 'WORLD_SIZE' in os.environ: + args.rank = int(os.environ["RANK"]) + args.world_size = int(os.environ['WORLD_SIZE']) + args.gpu = int(os.environ['LOCAL_RANK']) + elif 'SLURM_PROCID' in os.environ: + args.rank = int(os.environ['SLURM_PROCID']) + args.gpu = args.rank % torch.cuda.device_count() + else: + print('Not using distributed mode') + args.distributed = False + return + + args.distributed = True + + torch.cuda.set_device(args.gpu) + dist_backend = get_dist_backend(args) + print('| distributed init (rank {}): {}'.format( + args.rank, args.dist_url), flush=True) + torch.distributed.init_process_group(backend=dist_backend, init_method=args.dist_url, + world_size=args.world_size, rank=args.rank) + torch.distributed.barrier() + setup_for_distributed(args.rank == 0) + + +def all_gather(data): + """ + Run all_gather on arbitrary picklable data (not necessarily tensors) + Args: + data: any picklable object + Returns: + list[data]: list of data gathered from each rank + """ + world_size = get_world_size() + if world_size == 1: + return [data] + data_list = [None] * world_size + dist.all_gather_object(data_list, data) + return data_list + + +def reduce_dict(input_dict, average=True): + """ + Args: + input_dict (dict): all the values will be reduced + average (bool): whether to do average or sum + Reduce the values in the dictionary from all processes so that all processes + have the averaged results. Returns a dict with the same fields as + input_dict, after reduction. + """ + world_size = get_world_size() + if world_size < 2: + return input_dict + with torch.no_grad(): + names = [] + values = [] + # sort the keys so that they are consistent across processes + for k in sorted(input_dict.keys()): + names.append(k) + values.append(input_dict[k]) + values = torch.stack(values, dim=0) + dist.all_reduce(values) + if average: + values /= world_size + reduced_dict = {k: v for k, v in zip(names, values)} + return reduced_dict diff --git a/cv/classification/shufflenetv2/pytorch/common_utils/metric_logger.py b/cv/classification/shufflenetv2/pytorch/common_utils/metric_logger.py new file mode 100644 index 000000000..960641c4d --- /dev/null +++ b/cv/classification/shufflenetv2/pytorch/common_utils/metric_logger.py @@ -0,0 +1,94 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict +import datetime +import time + +import torch +from .smooth_value import SmoothedValue + +""" +Examples: + +logger = MetricLogger(" ") + +>>> # For iter dataloader +>>> metric_logger.add_meter('img/s', utils.SmoothedValue(window_size=10, fmt='{value}')) +>>> header = 'Epoch: [{}]'.format(epoch) +>>> for image, target in metric_logger.log_every(data_loader, print_freq, header): +>>> ... +>>> logger.metric_logger.meters['img/s'].update(fps) + +""" + +class MetricLogger(object): + + def __init__(self, delimiter="\t"): + self.meters = defaultdict(SmoothedValue) + self.delimiter = delimiter + + def update(self, **kwargs): + for k, v in kwargs.items(): + if isinstance(v, torch.Tensor): + v = v.item() + assert isinstance(v, (float, int)) + self.meters[k].update(v) + + def __getattr__(self, attr): + if attr in self.meters: + return self.meters[attr] + if attr in self.__dict__: + return self.__dict__[attr] + raise AttributeError("'{}' object has no attribute '{}'".format( + type(self).__name__, attr)) + + def __str__(self): + loss_str = [] + for name, meter in self.meters.items(): + loss_str.append( + "{}: {}".format(name, str(meter)) + ) + return self.delimiter.join(loss_str) + + def synchronize_between_processes(self): + for meter in self.meters.values(): + meter.synchronize_between_processes() + + def add_meter(self, name, meter): + self.meters[name] = meter + + def log_every(self, iterable, print_freq, header=None): + i = 0 + if not header: + header = '' + start_time = time.time() + end = time.time() + iter_time = SmoothedValue(fmt='{avg:.4f}') + data_time = SmoothedValue(fmt='{avg:.4f}') + space_fmt = ':' + str(len(str(len(iterable)))) + 'd' + log_msg = self.delimiter.join([ + header, + '[{0' + space_fmt + '}/{1}]', + 'eta: {eta}', + '{meters}', + 'time: {time}', + 'data: {data}' + ]) + for obj in iterable: + data_time.update(time.time() - end) + yield obj + iter_time.update(time.time() - end) + if i % print_freq == 0: + eta_seconds = iter_time.global_avg * (len(iterable) - i) + eta_string = str(datetime.timedelta(seconds=int(eta_seconds))) + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time))) + i += 1 + end = time.time() + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('{} Total time: {}'.format(header, total_time_str)) diff --git a/cv/classification/shufflenetv2/pytorch/common_utils/misc.py b/cv/classification/shufflenetv2/pytorch/common_utils/misc.py new file mode 100644 index 000000000..c9b501cf8 --- /dev/null +++ b/cv/classification/shufflenetv2/pytorch/common_utils/misc.py @@ -0,0 +1,11 @@ +import os +import sys +import errno + + +def mkdir(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise \ No newline at end of file diff --git a/cv/classification/shufflenetv2/pytorch/common_utils/smooth_value.py b/cv/classification/shufflenetv2/pytorch/common_utils/smooth_value.py new file mode 100644 index 000000000..30cb89d60 --- /dev/null +++ b/cv/classification/shufflenetv2/pytorch/common_utils/smooth_value.py @@ -0,0 +1,75 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist +from .dist import is_dist_avail_and_initialized + + +class SmoothedValue(object): + """Track a series of values and provide access to smoothed values over a + window or the global series average. + """ + + def __init__(self, window_size=20, fmt=None): + if fmt is None: + fmt = "{median:.4f} ({global_avg:.4f})" + self.deque = deque(maxlen=window_size) + self.total = 0.0 + self.count = 0 + self.fmt = fmt + + def update(self, value, n=1): + self.deque.append(value) + self.count += n + self.total += value * n + + def synchronize_between_processes(self): + """ + Warning: does not synchronize the deque! + """ + if not is_dist_avail_and_initialized(): + return + t = torch.tensor([self.count, self.total], dtype=torch.float32, device='cuda') + dist.barrier() + dist.all_reduce(t) + t = t.tolist() + self.count = int(t[0]) + self.total = t[1] + + @property + def median(self): + d = torch.tensor(list(self.deque)) + return d.median().item() + + @property + def avg(self): + d = torch.tensor(list(self.deque), dtype=torch.float32) + return d.mean().item() + + @property + def global_avg(self): + return self.total / self.count + + @property + def max(self): + return max(self.deque) + + @property + def value(self): + return self.deque[-1] + + def __str__(self): + return self.fmt.format( + median=self.median, + avg=self.avg, + global_avg=self.global_avg, + max=self.max, + value=self.value) \ No newline at end of file diff --git a/cv/classification/shufflenetv2/pytorch/dataloader/__init__.py b/cv/classification/shufflenetv2/pytorch/dataloader/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/classification/shufflenetv2/pytorch/dataloader/classification.py b/cv/classification/shufflenetv2/pytorch/dataloader/classification.py new file mode 100644 index 000000000..030af6dee --- /dev/null +++ b/cv/classification/shufflenetv2/pytorch/dataloader/classification.py @@ -0,0 +1,113 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +import os +import time + +import torch +import torchvision +from .utils import presets_classification as presets + +""" +Examples: + +>>> dataset_train, dataset_val = load_data(train_dir, val_dir, args) +""" + + +def get_datasets(traindir, + valdir, + resize_size=256, + crop_size=224, + auto_augment_policy=None, + random_erase_prob=0.): + # Data loading code + print("Loading data") + print("Loading training data") + dataset = torchvision.datasets.ImageFolder( + traindir, + presets.ClassificationPresetTrain(crop_size=crop_size, auto_augment_policy=auto_augment_policy, + random_erase_prob=random_erase_prob)) + + print("Loading validation data") + dataset_test = torchvision.datasets.ImageFolder( + valdir, + presets.ClassificationPresetEval(crop_size=crop_size, resize_size=resize_size)) + + return dataset, dataset_test + + +def get_input_size(model): + biger_input_size_models = ['inception'] + resize_size = 256 + crop_size = 224 + for bi_model in biger_input_size_models: + if bi_model in model: + resize_size = 342 + crop_size = 299 + + return resize_size, crop_size + + +def load_data(train_dir, val_dir, args): + auto_augment_policy = getattr(args, "auto_augment", None) + random_erase_prob = getattr(args, "random_erase", 0.0) + resize_size, crop_size = get_input_size(args.model) + dataset, dataset_test = get_datasets(train_dir, val_dir, + auto_augment_policy=auto_augment_policy, + random_erase_prob=random_erase_prob, + resize_size=resize_size, + crop_size=crop_size) + if args.distributed: + train_sampler = torch.utils.data.distributed.DistributedSampler(dataset) + test_sampler = torch.utils.data.distributed.DistributedSampler(dataset_test) + else: + train_sampler = torch.utils.data.RandomSampler(dataset) + test_sampler = torch.utils.data.SequentialSampler(dataset_test) + + return dataset, dataset_test, train_sampler, test_sampler + + +def _create_torch_dataloader(train_dir, val_dir, args): + dataset, dataset_test, train_sampler, test_sampler = load_data(train_dir, val_dir, args) + data_loader = torch.utils.data.DataLoader( + dataset, batch_size=args.batch_size, + sampler=train_sampler, num_workers=args.workers, pin_memory=True) + + data_loader_test = torch.utils.data.DataLoader( + dataset_test, batch_size=args.batch_size, + sampler=test_sampler, num_workers=args.workers, pin_memory=True) + + return data_loader, data_loader_test + + +def _create_dali_dataloader(train_dir, val_dir, args): + from .dali_classification import get_imagenet_iter_dali + device = torch.cuda.current_device() + _, crop_size = get_input_size(args.model) + data_loader = get_imagenet_iter_dali('train', train_dir, args.batch_size, + num_threads=args.workers, + device_id=device, + size=crop_size) + data_loader_test = get_imagenet_iter_dali('val', train_dir, args.batch_size, + num_threads=args.workers, + device_id=device, + size=crop_size) + + return data_loader, data_loader_test + + +def create_dataloader(train_dir, val_dir, args): + print("Creating data loaders") + if args.dali: + train_dir = os.path.dirname(train_dir) + val_dir = os.path.dirname(val_dir) + return _create_dali_dataloader(train_dir, val_dir, args) + return _create_torch_dataloader(train_dir, val_dir, args) diff --git a/cv/classification/shufflenetv2/pytorch/dataloader/dali_classification.py b/cv/classification/shufflenetv2/pytorch/dataloader/dali_classification.py new file mode 100644 index 000000000..4c92283b2 --- /dev/null +++ b/cv/classification/shufflenetv2/pytorch/dataloader/dali_classification.py @@ -0,0 +1,121 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +import os + +import nvidia.dali.ops as ops +import nvidia.dali.types as types +from nvidia.dali.pipeline import Pipeline +from nvidia.dali.plugin.pytorch import DALIClassificationIterator, DALIGenericIterator + +class HybridTrainPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridTrainPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=True) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.RandomResizedCrop(device="gpu", size=size, random_area=[0.08, 1.25]) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +class HybridValPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridValPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=False) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.Resize(device="gpu", resize_x=size, resize_y=size) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + crop=(size, size), + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +def get_imagenet_iter_dali(type, image_dir, batch_size, num_threads, device_id, size): + if type == 'train': + pip_train = HybridTrainPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "train"), + size=size) + pip_train.build() + dali_iter_train = DALIClassificationIterator(pip_train, size=pip_train.epoch_size("Reader")) + return dali_iter_train + elif type == 'val': + pip_val = HybridValPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "val"), + size=size) + pip_val.build() + dali_iter_val = DALIClassificationIterator(pip_val, size=pip_val.epoch_size("Reader")) + return dali_iter_val + + +def main(arguments): + parser = argparse.ArgumentParser() + parser.add_argument('--data_dir', help='directory to save data to', type=str, default='classification data') + args = parser.parse_args(arguments) + + train_loader = get_imagenet_iter_dali(type='train', image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + val_loader = get_imagenet_iter_dali(type="val", image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + print('start dali train dataloader.') + start = time.time() + for epoch in range(20): + for i, data in enumerate(train_loader): + images = data[0]["data"].cuda(non_blocking=True) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + + # WARN: Very important + train_loader.reset() + print("Epoch", epoch) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali train dataloader.') + + + print('start dali val dataloader.') + start = time.time() + for i, data in enumerate(val_loader): + images = data[0]["data"].cuda(non_blocking=True) + print(images.shape) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + print(labels.shape) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali val dataloader.') + + +if __name__ == '__main__': + import os, time, sys + import argparse + sys.exit(main(sys.argv[1:])) \ No newline at end of file diff --git a/cv/classification/shufflenetv2/pytorch/dataloader/utils/__init__.py b/cv/classification/shufflenetv2/pytorch/dataloader/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/classification/shufflenetv2/pytorch/dataloader/utils/presets_classification.py b/cv/classification/shufflenetv2/pytorch/dataloader/utils/presets_classification.py new file mode 100644 index 000000000..b3f559af4 --- /dev/null +++ b/cv/classification/shufflenetv2/pytorch/dataloader/utils/presets_classification.py @@ -0,0 +1,41 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from torchvision.transforms import autoaugment, transforms + + +class ClassificationPresetTrain: + def __init__(self, crop_size, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225), hflip_prob=0.5, + auto_augment_policy=None, random_erase_prob=0.0): + trans = [transforms.RandomResizedCrop(crop_size)] + if hflip_prob > 0: + trans.append(transforms.RandomHorizontalFlip(hflip_prob)) + if auto_augment_policy is not None: + aa_policy = autoaugment.AutoAugmentPolicy(auto_augment_policy) + trans.append(autoaugment.AutoAugment(policy=aa_policy)) + trans.extend([ + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ]) + if random_erase_prob > 0: + trans.append(transforms.RandomErasing(p=random_erase_prob)) + + self.transforms = transforms.Compose(trans) + + def __call__(self, img): + return self.transforms(img) + + +class ClassificationPresetEval: + def __init__(self, crop_size, resize_size=256, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)): + + self.transforms = transforms.Compose([ + transforms.Resize(resize_size), + transforms.CenterCrop(crop_size), + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ]) + + def __call__(self, img): + return self.transforms(img) diff --git a/cv/classification/shufflenetv2/pytorch/requirements.txt b/cv/classification/shufflenetv2/pytorch/requirements.txt index 61d5792cb..7321845b9 100644 --- a/cv/classification/shufflenetv2/pytorch/requirements.txt +++ b/cv/classification/shufflenetv2/pytorch/requirements.txt @@ -1,3 +1 @@ -torch -torchvision ---extra-index-url https://developer.download.nvidia.com/compute/redist/ nvidia-dali-cuda102==1.6.0 +numpy>=1.23.5 \ No newline at end of file diff --git a/cv/classification/shufflenetv2/pytorch/run_train.py b/cv/classification/shufflenetv2/pytorch/run_train.py deleted file mode 100644 index 651a35d54..000000000 --- a/cv/classification/shufflenetv2/pytorch/run_train.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -import os -import sys - -import torchvision - -sys.path.append("../../torchvision/pytorch") - -from train import train_model -from utils import padding_conv_channel_to_4 - -def create_model(args): - model = torchvision.models.__dict__[args.model](pretrained=args.pretrained, num_classes=args.num_classes) - args.padding_channel = False - - return model - - -train_model(create_model) diff --git a/cv/classification/shufflenetv2/pytorch/train.py b/cv/classification/shufflenetv2/pytorch/train.py new file mode 100644 index 000000000..c97912454 --- /dev/null +++ b/cv/classification/shufflenetv2/pytorch/train.py @@ -0,0 +1,314 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +import datetime +import os +import sys + +import time +import math + +import torch +import torch.utils.data + +try: + from torch.cuda.amp import autocast, GradScaler + scaler = GradScaler() +except: + autocast = None + scaler = None + + +from torch import nn +import torch.distributed as dist +import torchvision + +from utils_ import (MetricLogger, SmoothedValue, accuracy, mkdir,\ + init_distributed_mode, manual_seed,\ + is_main_process, save_on_master, get_world_size) + +from dataloader.classification import get_datasets, create_dataloader + + +def compute_loss(model, image, target, criterion): + output = model(image) + if not isinstance(output, (tuple, list)): + output = [output] + losses = [] + for out in output: + losses.append(criterion(out, target)) + loss = sum(losses) + return loss, output[0] + + +def train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, print_freq, amp=False, use_dali=False): + model.train() + metric_logger = MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', SmoothedValue(window_size=1, fmt='{value}')) + metric_logger.add_meter('img/s', SmoothedValue(window_size=10, fmt='{value}')) + + header = 'Epoch: [{}]'.format(epoch) + all_fps = [] + for data in metric_logger.log_every(data_loader, print_freq, header): + if use_dali: + image, target = data[0]["data"], data[0]["label"][:, 0].long() + else: + image, target = data + start_time = time.time() + image, target = image.to(device), target.to(device) + if autocast is None or not amp: + loss, output = compute_loss(model, image, target, criterion) + else: + with autocast(): + loss, output = compute_loss(model, image, target, criterion) + + optimizer.zero_grad() + if scaler is not None and amp: + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + else: + loss.backward() + optimizer.step() + + torch.cuda.synchronize() + end_time = time.time() + + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + batch_size = image.shape[0] + loss_value = loss.item() + if not math.isfinite(loss_value): + print("Loss is {}, stopping training".format(loss_value)) + sys.exit(1) + metric_logger.update(loss=loss.item(), lr=optimizer.param_groups[0]["lr"]) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + fps = batch_size / (end_time - start_time) * get_world_size() + metric_logger.meters['img/s'].update(fps) + all_fps.append(fps) + + print(header, 'Avg img/s:', sum(all_fps) / len(all_fps)) + + +def evaluate(model, criterion, data_loader, device, print_freq=100, use_dali=False): + model.eval() + metric_logger = MetricLogger(delimiter=" ") + header = 'Test:' + with torch.no_grad(): + for data in metric_logger.log_every(data_loader, print_freq, header): + if use_dali: + image, target = data[0]["data"], data[0]["label"][:, 0].long() + else: + image, target = data + image = image.to(device, non_blocking=True) + target = target.to(device, non_blocking=True) + output = model(image) + loss = criterion(output, target) + + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + # FIXME need to take into account that the datasets + # could have been padded in distributed setup + batch_size = image.shape[0] + metric_logger.update(loss=loss.item()) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + # gather the stats from all processes + metric_logger.synchronize_between_processes() + + print(' * Acc@1 {top1.global_avg:.3f} Acc@5 {top5.global_avg:.3f}' + .format(top1=metric_logger.acc1, top5=metric_logger.acc5)) + return metric_logger.acc1.global_avg + + +def _get_cache_path(filepath): + import hashlib + h = hashlib.sha1(filepath.encode()).hexdigest() + cache_path = os.path.join("~", ".torch", "vision", "datasets", "imagefolder", h[:10] + ".pt") + cache_path = os.path.expanduser(cache_path) + return cache_path + + +def main(args): + if args.output_dir: + mkdir(args.output_dir) + + init_distributed_mode(args) + print(args) + + device = torch.device(args.device) + + manual_seed(args.seed, deterministic=False) + # torch.backends.cudnn.benchmark = True + + # WARN: + if dist.is_initialized(): + num_gpu = dist.get_world_size() + else: + num_gpu = 1 + + global_batch_size = num_gpu * args.batch_size + + train_dir = os.path.join(args.data_path, 'train') + val_dir = os.path.join(args.data_path, 'val') + + num_classes = len(os.listdir(train_dir)) + if 0 < num_classes < 13: + if global_batch_size > 512: + if is_main_process(): + print("WARN: Updating global batch size to 512, avoid non-convergence when training small dataset.") + args.batch_size = 512 // num_gpu + + if args.pretrained: + num_classes = 1000 + + data_loader, data_loader_test = create_dataloader(train_dir, val_dir, args) + + print("Creating model") + model = torchvision.models.__dict__[args.model](pretrained=args.pretrained, num_classes=num_classes) + model.to(device) + if args.distributed and args.sync_bn: + model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model) + + criterion = nn.CrossEntropyLoss() + + opt_name = args.opt.lower() + if opt_name == 'sgd': + optimizer = torch.optim.SGD( + model.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.weight_decay) + elif opt_name == 'rmsprop': + optimizer = torch.optim.RMSprop(model.parameters(), lr=args.lr, momentum=args.momentum, + weight_decay=args.weight_decay, eps=0.0316, alpha=0.9) + else: + raise RuntimeError("Invalid optimizer {}. Only SGD and RMSprop are supported.".format(args.opt)) + + lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=args.lr_step_size, gamma=args.lr_gamma) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + if args.resume: + checkpoint = torch.load(args.resume, map_location='cpu') + model_without_ddp.load_state_dict(checkpoint['model']) + optimizer.load_state_dict(checkpoint['optimizer']) + lr_scheduler.load_state_dict(checkpoint['lr_scheduler']) + args.start_epoch = checkpoint['epoch'] + 1 + + if args.test_only: + evaluate(model, criterion, data_loader_test, device=device) + return + + print("Start training") + start_time = time.time() + best_acc = 0 + for epoch in range(args.start_epoch, args.epochs): + epoch_start_time = time.time() + if args.distributed and not args.dali: + data_loader.sampler.set_epoch(epoch) + train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, args.print_freq, args.amp, use_dali=args.dali) + lr_scheduler.step() + acc_avg = evaluate(model, criterion, data_loader_test, device=device, use_dali=args.dali) + if acc_avg > best_acc: + if args.output_dir: + checkpoint = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'lr_scheduler': lr_scheduler.state_dict(), + 'epoch': epoch, + 'args': args} + save_on_master( + checkpoint, + os.path.join(args.output_dir, 'model_{}.pth'.format(epoch))) + epoch_total_time = time.time() - epoch_start_time + epoch_total_time_str = str(datetime.timedelta(seconds=int(epoch_total_time))) + print('epoch time {}'.format(epoch_total_time_str)) + best_acc = acc_avg + + if args.dali: + data_loader.reset() + data_loader_test.reset() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +def get_args_parser(add_help=True): + import argparse + parser = argparse.ArgumentParser(description='PyTorch Classification Training', add_help=add_help) + + parser.add_argument('--data-path', default='/datasets01/imagenet_full_size/061417/', help='dataset') + parser.add_argument('--model', default='resnet18', help='model') + parser.add_argument('--device', default='cuda', help='device') + parser.add_argument('-b', '--batch-size', default=32, type=int) + parser.add_argument('--epochs', default=90, type=int, metavar='N', + help='number of total epochs to run') + parser.add_argument('-j', '--workers', default=4, type=int, metavar='N', + help='number of data loading workers (default: 4)') + parser.add_argument('--opt', default='sgd', type=str, help='optimizer') + parser.add_argument('--lr', default=0.1, type=float, help='initial learning rate') + parser.add_argument('--momentum', default=0.9, type=float, metavar='M', + help='momentum') + parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float, + metavar='W', help='weight decay (default: 1e-4)', + dest='weight_decay') + parser.add_argument('--lr-step-size', default=30, type=int, help='decrease lr every step-size epochs') + parser.add_argument('--lr-gamma', default=0.1, type=float, help='decrease lr by a factor of lr-gamma') + parser.add_argument('--print-freq', default=10, type=int, help='print frequency') + parser.add_argument('--output-dir', default='.', help='path where to save') + parser.add_argument('--resume', default='', help='resume from checkpoint') + parser.add_argument('--start-epoch', default=0, type=int, metavar='N', + help='start epoch') + parser.add_argument( + "--cache-dataset", + dest="cache_dataset", + help="Cache the datasets for quicker initialization. It also serializes the transforms", + action="store_true", + ) + parser.add_argument( + "--sync-bn", + dest="sync_bn", + help="Use sync batch norm", + action="store_true", + ) + parser.add_argument( + "--test-only", + dest="test_only", + help="Only test the model", + action="store_true", + ) + parser.add_argument( + "--pretrained", + dest="pretrained", + help="Use pre-trained models from the modelzoo", + action="store_true", + ) + parser.add_argument('--auto-augment', default=None, help='auto augment policy (default: None)') + parser.add_argument('--random-erase', default=0.0, type=float, help='random erasing probability (default: 0.0)') + parser.add_argument( + "--dali", + help="Use dali as dataloader", + default=False, + action="store_true", + ) + + # distributed training parameters + parser.add_argument('--local_rank', '--local-rank', default=-1, type=int, + help='Local rank') + parser.add_argument('--world-size', default=1, type=int, + help='number of distributed processes') + parser.add_argument('--dist-url', default='env://', help='url used to set up distributed training') + parser.add_argument('--amp', action='store_true', help='Automatic Mixed Precision training') + parser.add_argument('--seed', default=42, type=int, help='Random seed') + return parser + + +if __name__ == "__main__": + args = get_args_parser().parse_args() + try: + from dltest import show_training_arguments + show_training_arguments(args) + except: + pass + main(args) diff --git a/cv/classification/shufflenetv2/pytorch/train_shufflenet_v2_x2_0_amp_dist.sh b/cv/classification/shufflenetv2/pytorch/train_shufflenet_v2_x0_5_amp_dist.sh old mode 100755 new mode 100644 similarity index 77% rename from cv/classification/shufflenetv2/pytorch/train_shufflenet_v2_x2_0_amp_dist.sh rename to cv/classification/shufflenetv2/pytorch/train_shufflenet_v2_x0_5_amp_dist.sh index b2a1bbeca..4f61413fe --- a/cv/classification/shufflenetv2/pytorch/train_shufflenet_v2_x2_0_amp_dist.sh +++ b/cv/classification/shufflenetv2/pytorch/train_shufflenet_v2_x0_5_amp_dist.sh @@ -30,9 +30,6 @@ if [ ! -z "${DEBUG}" ];then PYTHONAR="${PYTHONAR} -m pdb" fi cd ${ROOT_DIR} -python3 $PYTHONARG ${ROOT_DIR}/run_train.py \ - --batch-size 1024 --data-path $DATA_PATH \ - --lr=0.5 --dali --dali-cpu --amp --nhwc \ - --lr 0.1 --dali --dali-cpu --amp --nhwc \ - --auto-augment=ta_wide --random-erase=0.1 --weight-decay=0.00002 \ - --crop-size=176 --crop-size=232 --model shufflenet_v2_x2_0 "$@" +python3 $PYTHONARG train.py --model shufflenet_v2_x0_5 \ + --batch-size 32 --data-path $DATA_PATH \ + --amp --wd 0.000001 "$@" diff --git a/cv/classification/shufflenetv2/pytorch/utils_.py b/cv/classification/shufflenetv2/pytorch/utils_.py new file mode 100644 index 000000000..3d34c4df0 --- /dev/null +++ b/cv/classification/shufflenetv2/pytorch/utils_.py @@ -0,0 +1,156 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque, OrderedDict +import copy +import datetime +import hashlib +import time +import torch +import torch.distributed as dist + +import errno +import os + +from common_utils import * + + +def accuracy(output, target, topk=(1,)): + """Computes the accuracy over the k top predictions for the specified values of k""" + with torch.no_grad(): + maxk = max(topk) + batch_size = target.size(0) + + _, pred = output.topk(maxk, 1, True, True) + pred = pred.t() + correct = pred.eq(target[None]) + + res = [] + for k in topk: + correct_k = correct[:k].flatten().sum(dtype=torch.float32) + res.append(correct_k * (100.0 / batch_size)) + return res + + +def average_checkpoints(inputs): + """Loads checkpoints from inputs and returns a model with averaged weights. Original implementation taken from: + https://github.com/pytorch/fairseq/blob/a48f235636557b8d3bc4922a6fa90f3a0fa57955/scripts/average_checkpoints.py#L16 + + Args: + inputs (List[str]): An iterable of string paths of checkpoints to load from. + Returns: + A dict of string keys mapping to various values. The 'model' key + from the returned dict should correspond to an OrderedDict mapping + string parameter names to torch Tensors. + """ + params_dict = OrderedDict() + params_keys = None + new_state = None + num_models = len(inputs) + for fpath in inputs: + with open(fpath, "rb") as f: + state = torch.load( + f, + map_location=( + lambda s, _: torch.serialization.default_restore_location(s, "cpu") + ), + ) + # Copies over the settings from the first checkpoint + if new_state is None: + new_state = state + model_params = state["model"] + model_params_keys = list(model_params.keys()) + if params_keys is None: + params_keys = model_params_keys + elif params_keys != model_params_keys: + raise KeyError( + "For checkpoint {}, expected list of params: {}, " + "but found: {}".format(f, params_keys, model_params_keys) + ) + for k in params_keys: + p = model_params[k] + if isinstance(p, torch.HalfTensor): + p = p.float() + if k not in params_dict: + params_dict[k] = p.clone() + # NOTE: clone() is needed in case of p is a shared parameter + else: + params_dict[k] += p + averaged_params = OrderedDict() + for k, v in params_dict.items(): + averaged_params[k] = v + if averaged_params[k].is_floating_point(): + averaged_params[k].div_(num_models) + else: + averaged_params[k] //= num_models + new_state["model"] = averaged_params + return new_state + + +def store_model_weights(model, checkpoint_path, checkpoint_key='model', strict=True): + """ + This method can be used to prepare weights files for new models. It receives as + input a model architecture and a checkpoint from the training script and produces + a file with the weights ready for release. + + Examples: + from torchvision import models as M + + # Classification + model = M.mobilenet_v3_large(pretrained=False) + print(store_model_weights(model, './class.pth')) + + # Quantized Classification + model = M.quantization.mobilenet_v3_large(pretrained=False, quantize=False) + model.fuse_model() + model.qconfig = torch.quantization.get_default_qat_qconfig('qnnpack') + _ = torch.quantization.prepare_qat(model, inplace=True) + print(store_model_weights(model, './qat.pth')) + + # Object Detection + model = M.detection.fasterrcnn_mobilenet_v3_large_fpn(pretrained=False, pretrained_backbone=False) + print(store_model_weights(model, './obj.pth')) + + # Segmentation + model = M.segmentation.deeplabv3_mobilenet_v3_large(pretrained=False, pretrained_backbone=False, aux_loss=True) + print(store_model_weights(model, './segm.pth', strict=False)) + + Args: + model (pytorch.nn.Module): The model on which the weights will be loaded for validation purposes. + checkpoint_path (str): The path of the checkpoint we will load. + checkpoint_key (str, optional): The key of the checkpoint where the model weights are stored. + Default: "model". + strict (bool): whether to strictly enforce that the keys + in :attr:`state_dict` match the keys returned by this module's + :meth:`~torch.nn.Module.state_dict` function. Default: ``True`` + + Returns: + output_path (str): The location where the weights are saved. + """ + # Store the new model next to the checkpoint_path + checkpoint_path = os.path.abspath(checkpoint_path) + output_dir = os.path.dirname(checkpoint_path) + + # Deep copy to avoid side-effects on the model object. + model = copy.deepcopy(model) + checkpoint = torch.load(checkpoint_path, map_location='cpu') + + # Load the weights to the model to validate that everything works + # and remove unnecessary weights (such as auxiliaries, etc) + model.load_state_dict(checkpoint[checkpoint_key], strict=strict) + + tmp_path = os.path.join(output_dir, str(model.__hash__())) + torch.save(model.state_dict(), tmp_path) + + sha256_hash = hashlib.sha256() + with open(tmp_path, "rb") as f: + # Read and update hash string value in blocks of 4K + for byte_block in iter(lambda: f.read(4096), b""): + sha256_hash.update(byte_block) + hh = sha256_hash.hexdigest() + + output_path = os.path.join(output_dir, "weights-" + str(hh[:8]) + ".pth") + os.replace(tmp_path, output_path) + + return output_path diff --git a/cv/classification/vgg/pytorch/README.md b/cv/classification/vgg/pytorch/README.md index d7be136b5..a55b1cb7d 100644 --- a/cv/classification/vgg/pytorch/README.md +++ b/cv/classification/vgg/pytorch/README.md @@ -38,16 +38,6 @@ export DATA_PATH=/path/to/imagenet # Multiple GPUs on one machine bash train_vgg16_amp_dist.sh ``` -Install zlib-1.2.9 if reports "iZLIB_1.2.9 not found" when run train_vgg16_amp_dist.sh - -```bash -wget http://www.zlib.net/fossils/zlib-1.2.9.tar.gz -tar xvf zlib-1.2.9.tar.gz -cd zlib-1.2.9/ -./configure && make install -cd ../ -rm -rf zlib-1.2.9.tar.gz zlib-1.2.9/ -``` ## Reference - [torchvision](https://github.com/pytorch/vision/tree/main/references/classification) diff --git a/cv/classification/vgg/pytorch/__init__.py b/cv/classification/vgg/pytorch/__init__.py new file mode 100644 index 000000000..6faec1658 --- /dev/null +++ b/cv/classification/vgg/pytorch/__init__.py @@ -0,0 +1,5 @@ +from .utils import * +from .common_utils import * +from .data_loader import * + +__all__ = [k for k in globals().keys() if not k.startswith("_")] diff --git a/cv/classification/vgg/pytorch/common_utils/__init__.py b/cv/classification/vgg/pytorch/common_utils/__init__.py new file mode 100644 index 000000000..32e8c4f57 --- /dev/null +++ b/cv/classification/vgg/pytorch/common_utils/__init__.py @@ -0,0 +1,23 @@ +import random + +import numpy as np + +from .dist import * +from .metric_logger import * +from .misc import * +from .smooth_value import * + +def manual_seed(seed, deterministic=False): + random.seed(seed) + np.random.seed(seed) + os.environ['PYTHONHASHSEED'] = str(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + + if deterministic: + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + else: + torch.backends.cudnn.deterministic = False + torch.backends.cudnn.benchmark = True \ No newline at end of file diff --git a/cv/classification/vgg/pytorch/common_utils/dist.py b/cv/classification/vgg/pytorch/common_utils/dist.py new file mode 100644 index 000000000..ea56ca267 --- /dev/null +++ b/cv/classification/vgg/pytorch/common_utils/dist.py @@ -0,0 +1,144 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist + + + +def setup_for_distributed(is_master): + """ + This function disables printing when not in master process + """ + import builtins as __builtin__ + builtin_print = __builtin__.print + + def print(*args, **kwargs): + force = kwargs.pop('force', False) + if is_master or force: + builtin_print(*args, **kwargs) + + __builtin__.print = print + + +def is_dist_avail_and_initialized(): + if not dist.is_available(): + return False + if not dist.is_initialized(): + return False + return True + + +def get_world_size(): + if not is_dist_avail_and_initialized(): + return 1 + return dist.get_world_size() + + +def get_rank(): + if not is_dist_avail_and_initialized(): + return 0 + return dist.get_rank() + + +def is_main_process(): + return get_rank() == 0 + + +def save_on_master(*args, **kwargs): + if is_main_process(): + torch.save(*args, **kwargs) + + +def get_dist_backend(args=None): + DIST_BACKEND_ENV = "PT_DIST_BACKEND" + if DIST_BACKEND_ENV in os.environ: + return os.environ[DIST_BACKEND_ENV] + + if args is None: + args = dict() + + backend_attr_name = "dist_backend" + + if hasattr(args, backend_attr_name): + return getattr(args, backend_attr_name) + + if backend_attr_name in args: + return args[backend_attr_name] + + return "nccl" + + +def init_distributed_mode(args): + if 'RANK' in os.environ and 'WORLD_SIZE' in os.environ: + args.rank = int(os.environ["RANK"]) + args.world_size = int(os.environ['WORLD_SIZE']) + args.gpu = int(os.environ['LOCAL_RANK']) + elif 'SLURM_PROCID' in os.environ: + args.rank = int(os.environ['SLURM_PROCID']) + args.gpu = args.rank % torch.cuda.device_count() + else: + print('Not using distributed mode') + args.distributed = False + return + + args.distributed = True + + torch.cuda.set_device(args.gpu) + dist_backend = get_dist_backend(args) + print('| distributed init (rank {}): {}'.format( + args.rank, args.dist_url), flush=True) + torch.distributed.init_process_group(backend=dist_backend, init_method=args.dist_url, + world_size=args.world_size, rank=args.rank) + torch.distributed.barrier() + setup_for_distributed(args.rank == 0) + + +def all_gather(data): + """ + Run all_gather on arbitrary picklable data (not necessarily tensors) + Args: + data: any picklable object + Returns: + list[data]: list of data gathered from each rank + """ + world_size = get_world_size() + if world_size == 1: + return [data] + data_list = [None] * world_size + dist.all_gather_object(data_list, data) + return data_list + + +def reduce_dict(input_dict, average=True): + """ + Args: + input_dict (dict): all the values will be reduced + average (bool): whether to do average or sum + Reduce the values in the dictionary from all processes so that all processes + have the averaged results. Returns a dict with the same fields as + input_dict, after reduction. + """ + world_size = get_world_size() + if world_size < 2: + return input_dict + with torch.no_grad(): + names = [] + values = [] + # sort the keys so that they are consistent across processes + for k in sorted(input_dict.keys()): + names.append(k) + values.append(input_dict[k]) + values = torch.stack(values, dim=0) + dist.all_reduce(values) + if average: + values /= world_size + reduced_dict = {k: v for k, v in zip(names, values)} + return reduced_dict diff --git a/cv/classification/vgg/pytorch/common_utils/metric_logger.py b/cv/classification/vgg/pytorch/common_utils/metric_logger.py new file mode 100644 index 000000000..960641c4d --- /dev/null +++ b/cv/classification/vgg/pytorch/common_utils/metric_logger.py @@ -0,0 +1,94 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict +import datetime +import time + +import torch +from .smooth_value import SmoothedValue + +""" +Examples: + +logger = MetricLogger(" ") + +>>> # For iter dataloader +>>> metric_logger.add_meter('img/s', utils.SmoothedValue(window_size=10, fmt='{value}')) +>>> header = 'Epoch: [{}]'.format(epoch) +>>> for image, target in metric_logger.log_every(data_loader, print_freq, header): +>>> ... +>>> logger.metric_logger.meters['img/s'].update(fps) + +""" + +class MetricLogger(object): + + def __init__(self, delimiter="\t"): + self.meters = defaultdict(SmoothedValue) + self.delimiter = delimiter + + def update(self, **kwargs): + for k, v in kwargs.items(): + if isinstance(v, torch.Tensor): + v = v.item() + assert isinstance(v, (float, int)) + self.meters[k].update(v) + + def __getattr__(self, attr): + if attr in self.meters: + return self.meters[attr] + if attr in self.__dict__: + return self.__dict__[attr] + raise AttributeError("'{}' object has no attribute '{}'".format( + type(self).__name__, attr)) + + def __str__(self): + loss_str = [] + for name, meter in self.meters.items(): + loss_str.append( + "{}: {}".format(name, str(meter)) + ) + return self.delimiter.join(loss_str) + + def synchronize_between_processes(self): + for meter in self.meters.values(): + meter.synchronize_between_processes() + + def add_meter(self, name, meter): + self.meters[name] = meter + + def log_every(self, iterable, print_freq, header=None): + i = 0 + if not header: + header = '' + start_time = time.time() + end = time.time() + iter_time = SmoothedValue(fmt='{avg:.4f}') + data_time = SmoothedValue(fmt='{avg:.4f}') + space_fmt = ':' + str(len(str(len(iterable)))) + 'd' + log_msg = self.delimiter.join([ + header, + '[{0' + space_fmt + '}/{1}]', + 'eta: {eta}', + '{meters}', + 'time: {time}', + 'data: {data}' + ]) + for obj in iterable: + data_time.update(time.time() - end) + yield obj + iter_time.update(time.time() - end) + if i % print_freq == 0: + eta_seconds = iter_time.global_avg * (len(iterable) - i) + eta_string = str(datetime.timedelta(seconds=int(eta_seconds))) + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time))) + i += 1 + end = time.time() + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('{} Total time: {}'.format(header, total_time_str)) diff --git a/cv/classification/vgg/pytorch/common_utils/misc.py b/cv/classification/vgg/pytorch/common_utils/misc.py new file mode 100644 index 000000000..c9b501cf8 --- /dev/null +++ b/cv/classification/vgg/pytorch/common_utils/misc.py @@ -0,0 +1,11 @@ +import os +import sys +import errno + + +def mkdir(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise \ No newline at end of file diff --git a/cv/classification/vgg/pytorch/common_utils/smooth_value.py b/cv/classification/vgg/pytorch/common_utils/smooth_value.py new file mode 100644 index 000000000..30cb89d60 --- /dev/null +++ b/cv/classification/vgg/pytorch/common_utils/smooth_value.py @@ -0,0 +1,75 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist +from .dist import is_dist_avail_and_initialized + + +class SmoothedValue(object): + """Track a series of values and provide access to smoothed values over a + window or the global series average. + """ + + def __init__(self, window_size=20, fmt=None): + if fmt is None: + fmt = "{median:.4f} ({global_avg:.4f})" + self.deque = deque(maxlen=window_size) + self.total = 0.0 + self.count = 0 + self.fmt = fmt + + def update(self, value, n=1): + self.deque.append(value) + self.count += n + self.total += value * n + + def synchronize_between_processes(self): + """ + Warning: does not synchronize the deque! + """ + if not is_dist_avail_and_initialized(): + return + t = torch.tensor([self.count, self.total], dtype=torch.float32, device='cuda') + dist.barrier() + dist.all_reduce(t) + t = t.tolist() + self.count = int(t[0]) + self.total = t[1] + + @property + def median(self): + d = torch.tensor(list(self.deque)) + return d.median().item() + + @property + def avg(self): + d = torch.tensor(list(self.deque), dtype=torch.float32) + return d.mean().item() + + @property + def global_avg(self): + return self.total / self.count + + @property + def max(self): + return max(self.deque) + + @property + def value(self): + return self.deque[-1] + + def __str__(self): + return self.fmt.format( + median=self.median, + avg=self.avg, + global_avg=self.global_avg, + max=self.max, + value=self.value) \ No newline at end of file diff --git a/cv/classification/vgg/pytorch/dataloader/__init__.py b/cv/classification/vgg/pytorch/dataloader/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/classification/vgg/pytorch/dataloader/classification.py b/cv/classification/vgg/pytorch/dataloader/classification.py new file mode 100644 index 000000000..030af6dee --- /dev/null +++ b/cv/classification/vgg/pytorch/dataloader/classification.py @@ -0,0 +1,113 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +import os +import time + +import torch +import torchvision +from .utils import presets_classification as presets + +""" +Examples: + +>>> dataset_train, dataset_val = load_data(train_dir, val_dir, args) +""" + + +def get_datasets(traindir, + valdir, + resize_size=256, + crop_size=224, + auto_augment_policy=None, + random_erase_prob=0.): + # Data loading code + print("Loading data") + print("Loading training data") + dataset = torchvision.datasets.ImageFolder( + traindir, + presets.ClassificationPresetTrain(crop_size=crop_size, auto_augment_policy=auto_augment_policy, + random_erase_prob=random_erase_prob)) + + print("Loading validation data") + dataset_test = torchvision.datasets.ImageFolder( + valdir, + presets.ClassificationPresetEval(crop_size=crop_size, resize_size=resize_size)) + + return dataset, dataset_test + + +def get_input_size(model): + biger_input_size_models = ['inception'] + resize_size = 256 + crop_size = 224 + for bi_model in biger_input_size_models: + if bi_model in model: + resize_size = 342 + crop_size = 299 + + return resize_size, crop_size + + +def load_data(train_dir, val_dir, args): + auto_augment_policy = getattr(args, "auto_augment", None) + random_erase_prob = getattr(args, "random_erase", 0.0) + resize_size, crop_size = get_input_size(args.model) + dataset, dataset_test = get_datasets(train_dir, val_dir, + auto_augment_policy=auto_augment_policy, + random_erase_prob=random_erase_prob, + resize_size=resize_size, + crop_size=crop_size) + if args.distributed: + train_sampler = torch.utils.data.distributed.DistributedSampler(dataset) + test_sampler = torch.utils.data.distributed.DistributedSampler(dataset_test) + else: + train_sampler = torch.utils.data.RandomSampler(dataset) + test_sampler = torch.utils.data.SequentialSampler(dataset_test) + + return dataset, dataset_test, train_sampler, test_sampler + + +def _create_torch_dataloader(train_dir, val_dir, args): + dataset, dataset_test, train_sampler, test_sampler = load_data(train_dir, val_dir, args) + data_loader = torch.utils.data.DataLoader( + dataset, batch_size=args.batch_size, + sampler=train_sampler, num_workers=args.workers, pin_memory=True) + + data_loader_test = torch.utils.data.DataLoader( + dataset_test, batch_size=args.batch_size, + sampler=test_sampler, num_workers=args.workers, pin_memory=True) + + return data_loader, data_loader_test + + +def _create_dali_dataloader(train_dir, val_dir, args): + from .dali_classification import get_imagenet_iter_dali + device = torch.cuda.current_device() + _, crop_size = get_input_size(args.model) + data_loader = get_imagenet_iter_dali('train', train_dir, args.batch_size, + num_threads=args.workers, + device_id=device, + size=crop_size) + data_loader_test = get_imagenet_iter_dali('val', train_dir, args.batch_size, + num_threads=args.workers, + device_id=device, + size=crop_size) + + return data_loader, data_loader_test + + +def create_dataloader(train_dir, val_dir, args): + print("Creating data loaders") + if args.dali: + train_dir = os.path.dirname(train_dir) + val_dir = os.path.dirname(val_dir) + return _create_dali_dataloader(train_dir, val_dir, args) + return _create_torch_dataloader(train_dir, val_dir, args) diff --git a/cv/classification/vgg/pytorch/dataloader/dali_classification.py b/cv/classification/vgg/pytorch/dataloader/dali_classification.py new file mode 100644 index 000000000..4c92283b2 --- /dev/null +++ b/cv/classification/vgg/pytorch/dataloader/dali_classification.py @@ -0,0 +1,121 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +import os + +import nvidia.dali.ops as ops +import nvidia.dali.types as types +from nvidia.dali.pipeline import Pipeline +from nvidia.dali.plugin.pytorch import DALIClassificationIterator, DALIGenericIterator + +class HybridTrainPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridTrainPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=True) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.RandomResizedCrop(device="gpu", size=size, random_area=[0.08, 1.25]) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +class HybridValPipe(Pipeline): + def __init__(self, batch_size, num_threads, device_id, data_dir, size): + super(HybridValPipe, self).__init__(batch_size, num_threads, device_id) + self.input = ops.FileReader(file_root=data_dir, random_shuffle=False) + self.decode = ops.ImageDecoder(device="cpu", output_type=types.RGB) + self.res = ops.Resize(device="gpu", resize_x=size, resize_y=size) + self.cmnp = ops.CropMirrorNormalize(device="gpu", + output_dtype=types.FLOAT, + output_layout=types.NCHW, + crop=(size, size), + image_type=types.RGB, + mean=[0.485 * 255, 0.456 * 255, 0.406 * 255], + std=[0.229 * 255, 0.224 * 255, 0.225 * 255]) + + def define_graph(self): + self.jpegs, self.labels = self.input(name="Reader") + + images = self.decode(self.jpegs) + images = self.res(images.gpu()) + output = self.cmnp(images) + return [output, self.labels] + + +def get_imagenet_iter_dali(type, image_dir, batch_size, num_threads, device_id, size): + if type == 'train': + pip_train = HybridTrainPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "train"), + size=size) + pip_train.build() + dali_iter_train = DALIClassificationIterator(pip_train, size=pip_train.epoch_size("Reader")) + return dali_iter_train + elif type == 'val': + pip_val = HybridValPipe(batch_size=batch_size, num_threads=num_threads, device_id=device_id, + data_dir = os.path.join(image_dir, "val"), + size=size) + pip_val.build() + dali_iter_val = DALIClassificationIterator(pip_val, size=pip_val.epoch_size("Reader")) + return dali_iter_val + + +def main(arguments): + parser = argparse.ArgumentParser() + parser.add_argument('--data_dir', help='directory to save data to', type=str, default='classification data') + args = parser.parse_args(arguments) + + train_loader = get_imagenet_iter_dali(type='train', image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + val_loader = get_imagenet_iter_dali(type="val", image_dir=args.data_dir, + batch_size=256, + num_threads=4, size=224, device_id=3) + + print('start dali train dataloader.') + start = time.time() + for epoch in range(20): + for i, data in enumerate(train_loader): + images = data[0]["data"].cuda(non_blocking=True) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + + # WARN: Very important + train_loader.reset() + print("Epoch", epoch) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali train dataloader.') + + + print('start dali val dataloader.') + start = time.time() + for i, data in enumerate(val_loader): + images = data[0]["data"].cuda(non_blocking=True) + print(images.shape) + labels = data[0]["label"].squeeze().long().cuda(non_blocking=True) + print(labels.shape) + print('dali iterate time: %fs' % (time.time() - start)) + print('end dali val dataloader.') + + +if __name__ == '__main__': + import os, time, sys + import argparse + sys.exit(main(sys.argv[1:])) \ No newline at end of file diff --git a/cv/classification/vgg/pytorch/dataloader/utils/__init__.py b/cv/classification/vgg/pytorch/dataloader/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/classification/vgg/pytorch/dataloader/utils/presets_classification.py b/cv/classification/vgg/pytorch/dataloader/utils/presets_classification.py new file mode 100644 index 000000000..b3f559af4 --- /dev/null +++ b/cv/classification/vgg/pytorch/dataloader/utils/presets_classification.py @@ -0,0 +1,41 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from torchvision.transforms import autoaugment, transforms + + +class ClassificationPresetTrain: + def __init__(self, crop_size, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225), hflip_prob=0.5, + auto_augment_policy=None, random_erase_prob=0.0): + trans = [transforms.RandomResizedCrop(crop_size)] + if hflip_prob > 0: + trans.append(transforms.RandomHorizontalFlip(hflip_prob)) + if auto_augment_policy is not None: + aa_policy = autoaugment.AutoAugmentPolicy(auto_augment_policy) + trans.append(autoaugment.AutoAugment(policy=aa_policy)) + trans.extend([ + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ]) + if random_erase_prob > 0: + trans.append(transforms.RandomErasing(p=random_erase_prob)) + + self.transforms = transforms.Compose(trans) + + def __call__(self, img): + return self.transforms(img) + + +class ClassificationPresetEval: + def __init__(self, crop_size, resize_size=256, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)): + + self.transforms = transforms.Compose([ + transforms.Resize(resize_size), + transforms.CenterCrop(crop_size), + transforms.ToTensor(), + transforms.Normalize(mean=mean, std=std), + ]) + + def __call__(self, img): + return self.transforms(img) diff --git a/cv/classification/vgg/pytorch/run_train.py b/cv/classification/vgg/pytorch/run_train.py deleted file mode 100644 index 651a35d54..000000000 --- a/cv/classification/vgg/pytorch/run_train.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -import os -import sys - -import torchvision - -sys.path.append("../../torchvision/pytorch") - -from train import train_model -from utils import padding_conv_channel_to_4 - -def create_model(args): - model = torchvision.models.__dict__[args.model](pretrained=args.pretrained, num_classes=args.num_classes) - args.padding_channel = False - - return model - - -train_model(create_model) diff --git a/cv/classification/vgg/pytorch/train.py b/cv/classification/vgg/pytorch/train.py new file mode 100644 index 000000000..8c1d708e8 --- /dev/null +++ b/cv/classification/vgg/pytorch/train.py @@ -0,0 +1,319 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +import datetime +import os +import sys + +import time +import math + +import torch +import torch.utils.data + +try: + from torch.cuda.amp import autocast, GradScaler + scaler = GradScaler() +except: + autocast = None + scaler = None + + +from torch import nn +import torch.distributed as dist +import torchvision + +from utils_ import (MetricLogger, SmoothedValue, accuracy, mkdir,\ + init_distributed_mode, manual_seed,\ + is_main_process, save_on_master, get_world_size) + +from dataloader.classification import get_datasets, create_dataloader + + +def compute_loss(model, image, target, criterion): + output = model(image) + if not isinstance(output, (tuple, list)): + output = [output] + losses = [] + for out in output: + losses.append(criterion(out, target)) + loss = sum(losses) + return loss, output[0] + + +def train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, print_freq, amp=False, use_dali=False): + model.train() + metric_logger = MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', SmoothedValue(window_size=1, fmt='{value}')) + metric_logger.add_meter('img/s', SmoothedValue(window_size=10, fmt='{value}')) + + header = 'Epoch: [{}]'.format(epoch) + all_fps = [] + for data in metric_logger.log_every(data_loader, print_freq, header): + if use_dali: + image, target = data[0]["data"], data[0]["label"][:, 0].long() + else: + image, target = data + start_time = time.time() + image, target = image.to(device), target.to(device) + if autocast is None or not amp: + loss, output = compute_loss(model, image, target, criterion) + else: + with autocast(): + loss, output = compute_loss(model, image, target, criterion) + + optimizer.zero_grad() + if scaler is not None and amp: + scaler.scale(loss).backward() + scaler.step(optimizer) + scaler.update() + else: + loss.backward() + optimizer.step() + + torch.cuda.synchronize() + end_time = time.time() + + loss_value = loss.item() + if not math.isfinite(loss_value): + print("Loss is {}, stopping training".format(loss_value)) + sys.exit(1) + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + batch_size = image.shape[0] + metric_logger.update(loss=loss.item(), lr=optimizer.param_groups[0]["lr"]) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + fps = batch_size / (end_time - start_time) * get_world_size() + metric_logger.meters['img/s'].update(fps) + all_fps.append(fps) + + print(header, 'Avg img/s:', sum(all_fps) / len(all_fps)) + + +def evaluate(model, criterion, data_loader, device, print_freq=100, use_dali=False): + model.eval() + metric_logger = MetricLogger(delimiter=" ") + header = 'Test:' + with torch.no_grad(): + for data in metric_logger.log_every(data_loader, print_freq, header): + if use_dali: + image, target = data[0]["data"], data[0]["label"][:, 0].long() + else: + image, target = data + image = image.to(device, non_blocking=True) + target = target.to(device, non_blocking=True) + output = model(image) + loss = criterion(output, target) + + loss_value = loss.item() + if not math.isfinite(loss_value) or loss_value > 100.0: + print("Loss is {}, stopping training".format(loss_value)) + sys.exit(1) + + acc1, acc5 = accuracy(output, target, topk=(1, 5)) + # FIXME need to take into account that the datasets + # could have been padded in distributed setup + batch_size = image.shape[0] + metric_logger.update(loss=loss.item()) + metric_logger.meters['acc1'].update(acc1.item(), n=batch_size) + metric_logger.meters['acc5'].update(acc5.item(), n=batch_size) + # gather the stats from all processes + metric_logger.synchronize_between_processes() + + print(' * Acc@1 {top1.global_avg:.3f} Acc@5 {top5.global_avg:.3f}' + .format(top1=metric_logger.acc1, top5=metric_logger.acc5)) + return metric_logger.acc1.global_avg + + +def _get_cache_path(filepath): + import hashlib + h = hashlib.sha1(filepath.encode()).hexdigest() + cache_path = os.path.join("~", ".torch", "vision", "datasets", "imagefolder", h[:10] + ".pt") + cache_path = os.path.expanduser(cache_path) + return cache_path + + +def main(args): + if args.output_dir: + mkdir(args.output_dir) + + init_distributed_mode(args) + print(args) + + device = torch.device(args.device) + + manual_seed(args.seed, deterministic=False) + # torch.backends.cudnn.benchmark = True + + # WARN: + if dist.is_initialized(): + num_gpu = dist.get_world_size() + else: + num_gpu = 1 + + global_batch_size = num_gpu * args.batch_size + + train_dir = os.path.join(args.data_path, 'train') + val_dir = os.path.join(args.data_path, 'val') + + num_classes = len(os.listdir(train_dir)) + if 0 < num_classes < 13: + if global_batch_size > 512: + if is_main_process(): + print("WARN: Updating global batch size to 512, avoid non-convergence when training small dataset.") + args.batch_size = 512 // num_gpu + + if args.pretrained: + num_classes = 1000 + + data_loader, data_loader_test = create_dataloader(train_dir, val_dir, args) + + print("Creating model") + model = torchvision.models.__dict__[args.model](pretrained=args.pretrained, num_classes=num_classes) + model.to(device) + if args.distributed and args.sync_bn: + model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model) + + criterion = nn.CrossEntropyLoss() + + opt_name = args.opt.lower() + if opt_name == 'sgd': + optimizer = torch.optim.SGD( + model.parameters(), lr=args.lr, momentum=args.momentum, weight_decay=args.weight_decay) + elif opt_name == 'rmsprop': + optimizer = torch.optim.RMSprop(model.parameters(), lr=args.lr, momentum=args.momentum, + weight_decay=args.weight_decay, eps=0.0316, alpha=0.9) + else: + raise RuntimeError("Invalid optimizer {}. Only SGD and RMSprop are supported.".format(args.opt)) + + lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=args.lr_step_size, gamma=args.lr_gamma) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + if args.resume: + checkpoint = torch.load(args.resume, map_location='cpu') + model_without_ddp.load_state_dict(checkpoint['model']) + optimizer.load_state_dict(checkpoint['optimizer']) + lr_scheduler.load_state_dict(checkpoint['lr_scheduler']) + args.start_epoch = checkpoint['epoch'] + 1 + + if args.test_only: + evaluate(model, criterion, data_loader_test, device=device) + return + + print("Start training") + start_time = time.time() + best_acc = 0 + for epoch in range(args.start_epoch, args.epochs): + epoch_start_time = time.time() + if args.distributed and not args.dali: + data_loader.sampler.set_epoch(epoch) + train_one_epoch(model, criterion, optimizer, data_loader, device, epoch, args.print_freq, args.amp, use_dali=args.dali) + lr_scheduler.step() + acc_avg = evaluate(model, criterion, data_loader_test, device=device, use_dali=args.dali) + if acc_avg > best_acc: + if args.output_dir: + checkpoint = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'lr_scheduler': lr_scheduler.state_dict(), + 'epoch': epoch, + 'args': args} + save_on_master( + checkpoint, + os.path.join(args.output_dir, 'model_best.pth')) + epoch_total_time = time.time() - epoch_start_time + epoch_total_time_str = str(datetime.timedelta(seconds=int(epoch_total_time))) + print('epoch time {}'.format(epoch_total_time_str)) + best_acc = acc_avg + + if args.dali: + data_loader.reset() + data_loader_test.reset() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +def get_args_parser(add_help=True): + import argparse + parser = argparse.ArgumentParser(description='PyTorch Classification Training', add_help=add_help) + + parser.add_argument('--data-path', default='/datasets01/imagenet_full_size/061417/', help='dataset') + parser.add_argument('--model', default='resnet18', help='model') + parser.add_argument('--device', default='cuda', help='device') + parser.add_argument('-b', '--batch-size', default=32, type=int) + parser.add_argument('--epochs', default=90, type=int, metavar='N', + help='number of total epochs to run') + parser.add_argument('-j', '--workers', default=4, type=int, metavar='N', + help='number of data loading workers (default: 4)') + parser.add_argument('--opt', default='sgd', type=str, help='optimizer') + parser.add_argument('--lr', default=0.1, type=float, help='initial learning rate') + parser.add_argument('--momentum', default=0.9, type=float, metavar='M', + help='momentum') + parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float, + metavar='W', help='weight decay (default: 1e-4)', + dest='weight_decay') + parser.add_argument('--lr-step-size', default=30, type=int, help='decrease lr every step-size epochs') + parser.add_argument('--lr-gamma', default=0.1, type=float, help='decrease lr by a factor of lr-gamma') + parser.add_argument('--print-freq', default=10, type=int, help='print frequency') + parser.add_argument('--output-dir', default='.', help='path where to save') + parser.add_argument('--resume', default='', help='resume from checkpoint') + parser.add_argument('--start-epoch', default=0, type=int, metavar='N', + help='start epoch') + parser.add_argument( + "--cache-dataset", + dest="cache_dataset", + help="Cache the datasets for quicker initialization. It also serializes the transforms", + action="store_true", + ) + parser.add_argument( + "--sync-bn", + dest="sync_bn", + help="Use sync batch norm", + action="store_true", + ) + parser.add_argument( + "--test-only", + dest="test_only", + help="Only test the model", + action="store_true", + ) + parser.add_argument( + "--pretrained", + dest="pretrained", + help="Use pre-trained models from the modelzoo", + action="store_true", + ) + parser.add_argument('--auto-augment', default=None, help='auto augment policy (default: None)') + parser.add_argument('--random-erase', default=0.0, type=float, help='random erasing probability (default: 0.0)') + parser.add_argument( + "--dali", + help="Use dali as dataloader", + default=False, + action="store_true", + ) + + # distributed training parameters + parser.add_argument('--local_rank', '--local-rank', default=-1, type=int, + help='Local rank') + parser.add_argument('--world-size', default=1, type=int, + help='number of distributed processes') + parser.add_argument('--dist-url', default='env://', help='url used to set up distributed training') + parser.add_argument('--amp', action='store_true', help='Automatic Mixed Precision training') + parser.add_argument('--seed', default=42, type=int, help='Random seed') + return parser + + +if __name__ == "__main__": + args = get_args_parser().parse_args() + try: + from dltest import show_training_arguments + show_training_arguments(args) + except: + pass + main(args) diff --git a/cv/classification/vgg/pytorch/train_vgg16_amp_dist.sh b/cv/classification/vgg/pytorch/train_vgg16_amp_dist.sh index 986907305..b4b7c80b6 100755 --- a/cv/classification/vgg/pytorch/train_vgg16_amp_dist.sh +++ b/cv/classification/vgg/pytorch/train_vgg16_amp_dist.sh @@ -30,7 +30,7 @@ if [ ! -z "${DEBUG}" ];then PYTHONAR="${PYTHONAR} -m pdb" fi cd ${ROOT_DIR} -python3 $PYTHONARG ${ROOT_DIR}/run_train.py \ - --model vgg16 --dali --dali-cpu --data-path $DATA_PATH \ - --opt fused_sgd --batch-size 200 --lr 1e-2 \ - --amp --nhwc "$@" +python3 $PYTHONARG train.py \ + --model vgg16 --data-path $DATA_PATH \ + --batch-size 128 --lr 1e-3 \ + --amp --wd 0.0001 "$@" diff --git a/cv/classification/vgg/pytorch/utils_.py b/cv/classification/vgg/pytorch/utils_.py new file mode 100644 index 000000000..3d34c4df0 --- /dev/null +++ b/cv/classification/vgg/pytorch/utils_.py @@ -0,0 +1,156 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque, OrderedDict +import copy +import datetime +import hashlib +import time +import torch +import torch.distributed as dist + +import errno +import os + +from common_utils import * + + +def accuracy(output, target, topk=(1,)): + """Computes the accuracy over the k top predictions for the specified values of k""" + with torch.no_grad(): + maxk = max(topk) + batch_size = target.size(0) + + _, pred = output.topk(maxk, 1, True, True) + pred = pred.t() + correct = pred.eq(target[None]) + + res = [] + for k in topk: + correct_k = correct[:k].flatten().sum(dtype=torch.float32) + res.append(correct_k * (100.0 / batch_size)) + return res + + +def average_checkpoints(inputs): + """Loads checkpoints from inputs and returns a model with averaged weights. Original implementation taken from: + https://github.com/pytorch/fairseq/blob/a48f235636557b8d3bc4922a6fa90f3a0fa57955/scripts/average_checkpoints.py#L16 + + Args: + inputs (List[str]): An iterable of string paths of checkpoints to load from. + Returns: + A dict of string keys mapping to various values. The 'model' key + from the returned dict should correspond to an OrderedDict mapping + string parameter names to torch Tensors. + """ + params_dict = OrderedDict() + params_keys = None + new_state = None + num_models = len(inputs) + for fpath in inputs: + with open(fpath, "rb") as f: + state = torch.load( + f, + map_location=( + lambda s, _: torch.serialization.default_restore_location(s, "cpu") + ), + ) + # Copies over the settings from the first checkpoint + if new_state is None: + new_state = state + model_params = state["model"] + model_params_keys = list(model_params.keys()) + if params_keys is None: + params_keys = model_params_keys + elif params_keys != model_params_keys: + raise KeyError( + "For checkpoint {}, expected list of params: {}, " + "but found: {}".format(f, params_keys, model_params_keys) + ) + for k in params_keys: + p = model_params[k] + if isinstance(p, torch.HalfTensor): + p = p.float() + if k not in params_dict: + params_dict[k] = p.clone() + # NOTE: clone() is needed in case of p is a shared parameter + else: + params_dict[k] += p + averaged_params = OrderedDict() + for k, v in params_dict.items(): + averaged_params[k] = v + if averaged_params[k].is_floating_point(): + averaged_params[k].div_(num_models) + else: + averaged_params[k] //= num_models + new_state["model"] = averaged_params + return new_state + + +def store_model_weights(model, checkpoint_path, checkpoint_key='model', strict=True): + """ + This method can be used to prepare weights files for new models. It receives as + input a model architecture and a checkpoint from the training script and produces + a file with the weights ready for release. + + Examples: + from torchvision import models as M + + # Classification + model = M.mobilenet_v3_large(pretrained=False) + print(store_model_weights(model, './class.pth')) + + # Quantized Classification + model = M.quantization.mobilenet_v3_large(pretrained=False, quantize=False) + model.fuse_model() + model.qconfig = torch.quantization.get_default_qat_qconfig('qnnpack') + _ = torch.quantization.prepare_qat(model, inplace=True) + print(store_model_weights(model, './qat.pth')) + + # Object Detection + model = M.detection.fasterrcnn_mobilenet_v3_large_fpn(pretrained=False, pretrained_backbone=False) + print(store_model_weights(model, './obj.pth')) + + # Segmentation + model = M.segmentation.deeplabv3_mobilenet_v3_large(pretrained=False, pretrained_backbone=False, aux_loss=True) + print(store_model_weights(model, './segm.pth', strict=False)) + + Args: + model (pytorch.nn.Module): The model on which the weights will be loaded for validation purposes. + checkpoint_path (str): The path of the checkpoint we will load. + checkpoint_key (str, optional): The key of the checkpoint where the model weights are stored. + Default: "model". + strict (bool): whether to strictly enforce that the keys + in :attr:`state_dict` match the keys returned by this module's + :meth:`~torch.nn.Module.state_dict` function. Default: ``True`` + + Returns: + output_path (str): The location where the weights are saved. + """ + # Store the new model next to the checkpoint_path + checkpoint_path = os.path.abspath(checkpoint_path) + output_dir = os.path.dirname(checkpoint_path) + + # Deep copy to avoid side-effects on the model object. + model = copy.deepcopy(model) + checkpoint = torch.load(checkpoint_path, map_location='cpu') + + # Load the weights to the model to validate that everything works + # and remove unnecessary weights (such as auxiliaries, etc) + model.load_state_dict(checkpoint[checkpoint_key], strict=strict) + + tmp_path = os.path.join(output_dir, str(model.__hash__())) + torch.save(model.state_dict(), tmp_path) + + sha256_hash = hashlib.sha256() + with open(tmp_path, "rb") as f: + # Read and update hash string value in blocks of 4K + for byte_block in iter(lambda: f.read(4096), b""): + sha256_hash.update(byte_block) + hh = sha256_hash.hexdigest() + + output_path = os.path.join(output_dir, "weights-" + str(hh[:8]) + ".pth") + os.replace(tmp_path, output_path) + + return output_path diff --git a/cv/detection/fasterrcnn/pytorch/README.md b/cv/detection/fasterrcnn/pytorch/README.md index ba28e1d9a..7d58e01e0 100644 --- a/cv/detection/fasterrcnn/pytorch/README.md +++ b/cv/detection/fasterrcnn/pytorch/README.md @@ -6,6 +6,12 @@ State-of-the-art object detection networks depend on region proposal algorithms ## Step 1: Installing packages ``` +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-dev + cd /start_scripts bash init_torch.sh ``` @@ -35,23 +41,18 @@ coco2017 └── ... ``` -```bash -mkdir -p /datasets/ -ln -s /path/to/coco2017 /datasets/coco -``` - ## Step 3: Training ### On single GPU (AMP) ``` cd /start_scripts -bash train_fasterrcnn_resnet50_amp_torch.sh +bash train_fasterrcnn_resnet50_amp_torch.sh --dataset coco --data-path /path/to/coco2017 ``` ### Multiple GPUs on one machine ``` cd /start_scripts -bash train_fasterrcnn_resnet50_amp_dist_torch.sh +bash train_fasterrcnn_resnet50_amp_dist_torch.sh --dataset coco --data-path /path/to/coco2017 ``` ## Reference diff --git a/cv/detection/fasterrcnn/pytorch/coco_eval.py b/cv/detection/fasterrcnn/pytorch/coco_eval.py index 25ce8a105..9e907f212 100644 --- a/cv/detection/fasterrcnn/pytorch/coco_eval.py +++ b/cv/detection/fasterrcnn/pytorch/coco_eval.py @@ -6,7 +6,6 @@ import time import numpy as np import torch -import torch._six from pycocotools.cocoeval import COCOeval from pycocotools.coco import COCO import pycocotools.mask as mask_util @@ -250,7 +249,7 @@ def loadRes(self, resFile): # print('Loading and preparing results...') # tic = time.time() - if isinstance(resFile, torch._six.string_classes): + if isinstance(resFile, str): anns = json.load(open(resFile)) elif type(resFile) == np.ndarray: anns = self.loadNumpyAnnotations(resFile) diff --git a/cv/detection/fasterrcnn/pytorch/dataloaders/functional.py b/cv/detection/fasterrcnn/pytorch/dataloaders/functional.py new file mode 100644 index 000000000..ea5212135 --- /dev/null +++ b/cv/detection/fasterrcnn/pytorch/dataloaders/functional.py @@ -0,0 +1,1324 @@ +import math +import numbers +import warnings +from enum import Enum + +import numpy as np +from PIL import Image + +import torch +from torch import Tensor +from typing import List, Tuple, Any, Optional + +try: + import accimage +except ImportError: + accimage = None + +from . import functional_pil as F_pil +from . import functional_tensor as F_t + + +class InterpolationMode(Enum): + """Interpolation modes + """ + NEAREST = "nearest" + BILINEAR = "bilinear" + BICUBIC = "bicubic" + # For PIL compatibility + BOX = "box" + HAMMING = "hamming" + LANCZOS = "lanczos" + + +# TODO: Once torchscript supports Enums with staticmethod +# this can be put into InterpolationMode as staticmethod +def _interpolation_modes_from_int(i: int) -> InterpolationMode: + inverse_modes_mapping = { + 0: InterpolationMode.NEAREST, + 2: InterpolationMode.BILINEAR, + 3: InterpolationMode.BICUBIC, + 4: InterpolationMode.BOX, + 5: InterpolationMode.HAMMING, + 1: InterpolationMode.LANCZOS, + } + return inverse_modes_mapping[i] + + +pil_modes_mapping = { + InterpolationMode.NEAREST: 0, + InterpolationMode.BILINEAR: 2, + InterpolationMode.BICUBIC: 3, + InterpolationMode.BOX: 4, + InterpolationMode.HAMMING: 5, + InterpolationMode.LANCZOS: 1, +} + +_is_pil_image = F_pil._is_pil_image +_parse_fill = F_pil._parse_fill + + +def _get_image_size(img: Tensor) -> List[int]: + """Returns image size as [w, h] + """ + if isinstance(img, torch.Tensor): + return F_t._get_image_size(img) + + return F_pil._get_image_size(img) + + +def _get_image_num_channels(img: Tensor) -> int: + """Returns number of image channels + """ + if isinstance(img, torch.Tensor): + return F_t._get_image_num_channels(img) + + return F_pil._get_image_num_channels(img) + + +@torch.jit.unused +def _is_numpy(img: Any) -> bool: + return isinstance(img, np.ndarray) + + +@torch.jit.unused +def _is_numpy_image(img: Any) -> bool: + return img.ndim in {2, 3} + + +def to_tensor(pic): + """Convert a ``PIL Image`` or ``numpy.ndarray`` to tensor. + This function does not support torchscript. + + See :class:`~torchvision.transforms.ToTensor` for more details. + + Args: + pic (PIL Image or numpy.ndarray): Image to be converted to tensor. + + Returns: + Tensor: Converted image. + """ + if not(F_pil._is_pil_image(pic) or _is_numpy(pic)): + raise TypeError('pic should be PIL Image or ndarray. Got {}'.format(type(pic))) + + if _is_numpy(pic) and not _is_numpy_image(pic): + raise ValueError('pic should be 2/3 dimensional. Got {} dimensions.'.format(pic.ndim)) + + default_float_dtype = torch.get_default_dtype() + + if isinstance(pic, np.ndarray): + # handle numpy array + if pic.ndim == 2: + pic = pic[:, :, None] + + img = torch.from_numpy(pic.transpose((2, 0, 1))).contiguous() + # backward compatibility + if isinstance(img, torch.ByteTensor): + return img.to(dtype=default_float_dtype).div(255) + else: + return img + + if accimage is not None and isinstance(pic, accimage.Image): + nppic = np.zeros([pic.channels, pic.height, pic.width], dtype=default_float_dtype) + pic.copyto(nppic) + return torch.from_numpy(nppic) + + # handle PIL Image + if pic.mode == 'I': + img = torch.from_numpy(np.array(pic, np.int32, copy=False)) + elif pic.mode == 'I;16': + img = torch.from_numpy(np.array(pic, np.int16, copy=False)) + elif pic.mode == 'F': + img = torch.from_numpy(np.array(pic, np.float32, copy=False)) + elif pic.mode == '1': + img = 255 * torch.from_numpy(np.array(pic, np.uint8, copy=False)) + else: + img = torch.ByteTensor(torch.ByteStorage.from_buffer(pic.tobytes())) + + img = img.view(pic.size[1], pic.size[0], len(pic.getbands())) + # put it from HWC to CHW format + img = img.permute((2, 0, 1)).contiguous() + if isinstance(img, torch.ByteTensor): + return img.to(dtype=default_float_dtype).div(255) + else: + return img + + +def pil_to_tensor(pic): + """Convert a ``PIL Image`` to a tensor of the same type. + This function does not support torchscript. + + See :class:`~torchvision.transforms.PILToTensor` for more details. + + Args: + pic (PIL Image): Image to be converted to tensor. + + Returns: + Tensor: Converted image. + """ + if not F_pil._is_pil_image(pic): + raise TypeError('pic should be PIL Image. Got {}'.format(type(pic))) + + if accimage is not None and isinstance(pic, accimage.Image): + # accimage format is always uint8 internally, so always return uint8 here + nppic = np.zeros([pic.channels, pic.height, pic.width], dtype=np.uint8) + pic.copyto(nppic) + return torch.as_tensor(nppic) + + # handle PIL Image + img = torch.as_tensor(np.asarray(pic)) + img = img.view(pic.size[1], pic.size[0], len(pic.getbands())) + # put it from HWC to CHW format + img = img.permute((2, 0, 1)) + return img + + +def convert_image_dtype(image: torch.Tensor, dtype: torch.dtype = torch.float) -> torch.Tensor: + """Convert a tensor image to the given ``dtype`` and scale the values accordingly + This function does not support PIL Image. + + Args: + image (torch.Tensor): Image to be converted + dtype (torch.dtype): Desired data type of the output + + Returns: + Tensor: Converted image + + .. note:: + + When converting from a smaller to a larger integer ``dtype`` the maximum values are **not** mapped exactly. + If converted back and forth, this mismatch has no effect. + + Raises: + RuntimeError: When trying to cast :class:`torch.float32` to :class:`torch.int32` or :class:`torch.int64` as + well as for trying to cast :class:`torch.float64` to :class:`torch.int64`. These conversions might lead to + overflow errors since the floating point ``dtype`` cannot store consecutive integers over the whole range + of the integer ``dtype``. + """ + if not isinstance(image, torch.Tensor): + raise TypeError('Input img should be Tensor Image') + + return F_t.convert_image_dtype(image, dtype) + + +def to_pil_image(pic, mode=None): + """Convert a tensor or an ndarray to PIL Image. This function does not support torchscript. + + See :class:`~torchvision.transforms.ToPILImage` for more details. + + Args: + pic (Tensor or numpy.ndarray): Image to be converted to PIL Image. + mode (`PIL.Image mode`_): color space and pixel depth of input data (optional). + + .. _PIL.Image mode: https://pillow.readthedocs.io/en/latest/handbook/concepts.html#concept-modes + + Returns: + PIL Image: Image converted to PIL Image. + """ + if not(isinstance(pic, torch.Tensor) or isinstance(pic, np.ndarray)): + raise TypeError('pic should be Tensor or ndarray. Got {}.'.format(type(pic))) + + elif isinstance(pic, torch.Tensor): + if pic.ndimension() not in {2, 3}: + raise ValueError('pic should be 2/3 dimensional. Got {} dimensions.'.format(pic.ndimension())) + + elif pic.ndimension() == 2: + # if 2D image, add channel dimension (CHW) + pic = pic.unsqueeze(0) + + # check number of channels + if pic.shape[-3] > 4: + raise ValueError('pic should not have > 4 channels. Got {} channels.'.format(pic.shape[-3])) + + elif isinstance(pic, np.ndarray): + if pic.ndim not in {2, 3}: + raise ValueError('pic should be 2/3 dimensional. Got {} dimensions.'.format(pic.ndim)) + + elif pic.ndim == 2: + # if 2D image, add channel dimension (HWC) + pic = np.expand_dims(pic, 2) + + # check number of channels + if pic.shape[-1] > 4: + raise ValueError('pic should not have > 4 channels. Got {} channels.'.format(pic.shape[-1])) + + npimg = pic + if isinstance(pic, torch.Tensor): + if pic.is_floating_point() and mode != 'F': + pic = pic.mul(255).byte() + npimg = np.transpose(pic.cpu().numpy(), (1, 2, 0)) + + if not isinstance(npimg, np.ndarray): + raise TypeError('Input pic must be a torch.Tensor or NumPy ndarray, ' + + 'not {}'.format(type(npimg))) + + if npimg.shape[2] == 1: + expected_mode = None + npimg = npimg[:, :, 0] + if npimg.dtype == np.uint8: + expected_mode = 'L' + elif npimg.dtype == np.int16: + expected_mode = 'I;16' + elif npimg.dtype == np.int32: + expected_mode = 'I' + elif npimg.dtype == np.float32: + expected_mode = 'F' + if mode is not None and mode != expected_mode: + raise ValueError("Incorrect mode ({}) supplied for input type {}. Should be {}" + .format(mode, np.dtype, expected_mode)) + mode = expected_mode + + elif npimg.shape[2] == 2: + permitted_2_channel_modes = ['LA'] + if mode is not None and mode not in permitted_2_channel_modes: + raise ValueError("Only modes {} are supported for 2D inputs".format(permitted_2_channel_modes)) + + if mode is None and npimg.dtype == np.uint8: + mode = 'LA' + + elif npimg.shape[2] == 4: + permitted_4_channel_modes = ['RGBA', 'CMYK', 'RGBX'] + if mode is not None and mode not in permitted_4_channel_modes: + raise ValueError("Only modes {} are supported for 4D inputs".format(permitted_4_channel_modes)) + + if mode is None and npimg.dtype == np.uint8: + mode = 'RGBA' + else: + permitted_3_channel_modes = ['RGB', 'YCbCr', 'HSV'] + if mode is not None and mode not in permitted_3_channel_modes: + raise ValueError("Only modes {} are supported for 3D inputs".format(permitted_3_channel_modes)) + if mode is None and npimg.dtype == np.uint8: + mode = 'RGB' + + if mode is None: + raise TypeError('Input type {} is not supported'.format(npimg.dtype)) + + return Image.fromarray(npimg, mode=mode) + + +def normalize(tensor: Tensor, mean: List[float], std: List[float], inplace: bool = False) -> Tensor: + """Normalize a tensor image with mean and standard deviation. + This transform does not support PIL Image. + + .. note:: + This transform acts out of place by default, i.e., it does not mutates the input tensor. + + See :class:`~torchvision.transforms.Normalize` for more details. + + Args: + tensor (Tensor): Tensor image of size (C, H, W) or (B, C, H, W) to be normalized. + mean (sequence): Sequence of means for each channel. + std (sequence): Sequence of standard deviations for each channel. + inplace(bool,optional): Bool to make this operation inplace. + + Returns: + Tensor: Normalized Tensor image. + """ + if not isinstance(tensor, torch.Tensor): + raise TypeError('Input tensor should be a torch tensor. Got {}.'.format(type(tensor))) + + if tensor.ndim < 3: + raise ValueError('Expected tensor to be a tensor image of size (..., C, H, W). Got tensor.size() = ' + '{}.'.format(tensor.size())) + + if not inplace: + tensor = tensor.clone() + + dtype = tensor.dtype + mean = torch.as_tensor(mean, dtype=dtype, device=tensor.device) + std = torch.as_tensor(std, dtype=dtype, device=tensor.device) + if (std == 0).any(): + raise ValueError('std evaluated to zero after conversion to {}, leading to division by zero.'.format(dtype)) + if mean.ndim == 1: + mean = mean.view(-1, 1, 1) + if std.ndim == 1: + std = std.view(-1, 1, 1) + tensor.sub_(mean).div_(std) + return tensor + + +def resize(img: Tensor, size: List[int], interpolation: InterpolationMode = InterpolationMode.BILINEAR) -> Tensor: + r"""Resize the input image to the given size. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions + + Args: + img (PIL Image or Tensor): Image to be resized. + size (sequence or int): Desired output size. If size is a sequence like + (h, w), the output size will be matched to this. If size is an int, + the smaller edge of the image will be matched to this number maintaining + the aspect ratio. i.e, if height > width, then image will be rescaled to + :math:`\left(\text{size} \times \frac{\text{height}}{\text{width}}, \text{size}\right)`. + In torchscript mode size as single int is not supported, use a sequence of length 1: ``[size, ]``. + interpolation (InterpolationMode): Desired interpolation enum defined by + :class:`torchvision.transforms.InterpolationMode`. + Default is ``InterpolationMode.BILINEAR``. If input is Tensor, only ``InterpolationMode.NEAREST``, + ``InterpolationMode.BILINEAR`` and ``InterpolationMode.BICUBIC`` are supported. + For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable. + + Returns: + PIL Image or Tensor: Resized image. + """ + # Backward compatibility with integer value + if isinstance(interpolation, int): + warnings.warn( + "Argument interpolation should be of type InterpolationMode instead of int. " + "Please, use InterpolationMode enum." + ) + interpolation = _interpolation_modes_from_int(interpolation) + + if not isinstance(interpolation, InterpolationMode): + raise TypeError("Argument interpolation should be a InterpolationMode") + + if not isinstance(img, torch.Tensor): + pil_interpolation = pil_modes_mapping[interpolation] + return F_pil.resize(img, size=size, interpolation=pil_interpolation) + + return F_t.resize(img, size=size, interpolation=interpolation.value) + + +def scale(*args, **kwargs): + warnings.warn("The use of the transforms.Scale transform is deprecated, " + + "please use transforms.Resize instead.") + return resize(*args, **kwargs) + + +def pad(img: Tensor, padding: List[int], fill: int = 0, padding_mode: str = "constant") -> Tensor: + r"""Pad the given image on all sides with the given "pad" value. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means at most 2 leading dimensions for mode reflect and symmetric, + at most 3 leading dimensions for mode edge, + and an arbitrary number of leading dimensions for mode constant + + Args: + img (PIL Image or Tensor): Image to be padded. + padding (int or sequence): Padding on each border. If a single int is provided this + is used to pad all borders. If sequence of length 2 is provided this is the padding + on left/right and top/bottom respectively. If a sequence of length 4 is provided + this is the padding for the left, top, right and bottom borders respectively. + In torchscript mode padding as single int is not supported, use a sequence of length 1: ``[padding, ]``. + fill (number or str or tuple): Pixel fill value for constant fill. Default is 0. + If a tuple of length 3, it is used to fill R, G, B channels respectively. + This value is only used when the padding_mode is constant. + Only number is supported for torch Tensor. + Only int or str or tuple value is supported for PIL Image. + padding_mode: Type of padding. Should be: constant, edge, reflect or symmetric. Default is constant. + + - constant: pads with a constant value, this value is specified with fill + + - edge: pads with the last value on the edge of the image, + if input a 5D torch Tensor, the last 3 dimensions will be padded instead of the last 2 + + - reflect: pads with reflection of image (without repeating the last value on the edge) + + padding [1, 2, 3, 4] with 2 elements on both sides in reflect mode + will result in [3, 2, 1, 2, 3, 4, 3, 2] + + - symmetric: pads with reflection of image (repeating the last value on the edge) + + padding [1, 2, 3, 4] with 2 elements on both sides in symmetric mode + will result in [2, 1, 1, 2, 3, 4, 4, 3] + + Returns: + PIL Image or Tensor: Padded image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.pad(img, padding=padding, fill=fill, padding_mode=padding_mode) + + return F_t.pad(img, padding=padding, fill=fill, padding_mode=padding_mode) + + +def crop(img: Tensor, top: int, left: int, height: int, width: int) -> Tensor: + """Crop the given image at specified location and output size. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions + + Args: + img (PIL Image or Tensor): Image to be cropped. (0,0) denotes the top left corner of the image. + top (int): Vertical component of the top left corner of the crop box. + left (int): Horizontal component of the top left corner of the crop box. + height (int): Height of the crop box. + width (int): Width of the crop box. + + Returns: + PIL Image or Tensor: Cropped image. + """ + + if not isinstance(img, torch.Tensor): + return F_pil.crop(img, top, left, height, width) + + return F_t.crop(img, top, left, height, width) + + +def center_crop(img: Tensor, output_size: List[int]) -> Tensor: + """Crops the given image at the center. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions. + If image size is smaller than output size along any edge, image is padded with 0 and then center cropped. + + Args: + img (PIL Image or Tensor): Image to be cropped. + output_size (sequence or int): (height, width) of the crop box. If int or sequence with single int, + it is used for both directions. + + Returns: + PIL Image or Tensor: Cropped image. + """ + if isinstance(output_size, numbers.Number): + output_size = (int(output_size), int(output_size)) + elif isinstance(output_size, (tuple, list)) and len(output_size) == 1: + output_size = (output_size[0], output_size[0]) + + image_width, image_height = _get_image_size(img) + crop_height, crop_width = output_size + + if crop_width > image_width or crop_height > image_height: + padding_ltrb = [ + (crop_width - image_width) // 2 if crop_width > image_width else 0, + (crop_height - image_height) // 2 if crop_height > image_height else 0, + (crop_width - image_width + 1) // 2 if crop_width > image_width else 0, + (crop_height - image_height + 1) // 2 if crop_height > image_height else 0, + ] + img = pad(img, padding_ltrb, fill=0) # PIL uses fill value 0 + image_width, image_height = _get_image_size(img) + if crop_width == image_width and crop_height == image_height: + return img + + crop_top = int(round((image_height - crop_height) / 2.)) + crop_left = int(round((image_width - crop_width) / 2.)) + return crop(img, crop_top, crop_left, crop_height, crop_width) + + +def resized_crop( + img: Tensor, top: int, left: int, height: int, width: int, size: List[int], + interpolation: InterpolationMode = InterpolationMode.BILINEAR +) -> Tensor: + """Crop the given image and resize it to desired size. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions + + Notably used in :class:`~torchvision.transforms.RandomResizedCrop`. + + Args: + img (PIL Image or Tensor): Image to be cropped. (0,0) denotes the top left corner of the image. + top (int): Vertical component of the top left corner of the crop box. + left (int): Horizontal component of the top left corner of the crop box. + height (int): Height of the crop box. + width (int): Width of the crop box. + size (sequence or int): Desired output size. Same semantics as ``resize``. + interpolation (InterpolationMode): Desired interpolation enum defined by + :class:`torchvision.transforms.InterpolationMode`. + Default is ``InterpolationMode.BILINEAR``. If input is Tensor, only ``InterpolationMode.NEAREST``, + ``InterpolationMode.BILINEAR`` and ``InterpolationMode.BICUBIC`` are supported. + For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable. + + Returns: + PIL Image or Tensor: Cropped image. + """ + img = crop(img, top, left, height, width) + img = resize(img, size, interpolation) + return img + + +def hflip(img: Tensor) -> Tensor: + """Horizontally flip the given image. + + Args: + img (PIL Image or Tensor): Image to be flipped. If img + is a Tensor, it is expected to be in [..., H, W] format, + where ... means it can have an arbitrary number of leading + dimensions. + + Returns: + PIL Image or Tensor: Horizontally flipped image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.hflip(img) + + return F_t.hflip(img) + + +def _get_perspective_coeffs( + startpoints: List[List[int]], endpoints: List[List[int]] +) -> List[float]: + """Helper function to get the coefficients (a, b, c, d, e, f, g, h) for the perspective transforms. + + In Perspective Transform each pixel (x, y) in the original image gets transformed as, + (x, y) -> ( (ax + by + c) / (gx + hy + 1), (dx + ey + f) / (gx + hy + 1) ) + + Args: + startpoints (list of list of ints): List containing four lists of two integers corresponding to four corners + ``[top-left, top-right, bottom-right, bottom-left]`` of the original image. + endpoints (list of list of ints): List containing four lists of two integers corresponding to four corners + ``[top-left, top-right, bottom-right, bottom-left]`` of the transformed image. + + Returns: + octuple (a, b, c, d, e, f, g, h) for transforming each pixel. + """ + a_matrix = torch.zeros(2 * len(startpoints), 8, dtype=torch.float) + + for i, (p1, p2) in enumerate(zip(endpoints, startpoints)): + a_matrix[2 * i, :] = torch.tensor([p1[0], p1[1], 1, 0, 0, 0, -p2[0] * p1[0], -p2[0] * p1[1]]) + a_matrix[2 * i + 1, :] = torch.tensor([0, 0, 0, p1[0], p1[1], 1, -p2[1] * p1[0], -p2[1] * p1[1]]) + + b_matrix = torch.tensor(startpoints, dtype=torch.float).view(8) + res = torch.lstsq(b_matrix, a_matrix)[0] + + output: List[float] = res.squeeze(1).tolist() + return output + + +def perspective( + img: Tensor, + startpoints: List[List[int]], + endpoints: List[List[int]], + interpolation: InterpolationMode = InterpolationMode.BILINEAR, + fill: Optional[List[float]] = None +) -> Tensor: + """Perform perspective transform of the given image. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions. + + Args: + img (PIL Image or Tensor): Image to be transformed. + startpoints (list of list of ints): List containing four lists of two integers corresponding to four corners + ``[top-left, top-right, bottom-right, bottom-left]`` of the original image. + endpoints (list of list of ints): List containing four lists of two integers corresponding to four corners + ``[top-left, top-right, bottom-right, bottom-left]`` of the transformed image. + interpolation (InterpolationMode): Desired interpolation enum defined by + :class:`torchvision.transforms.InterpolationMode`. Default is ``InterpolationMode.BILINEAR``. + If input is Tensor, only ``InterpolationMode.NEAREST``, ``InterpolationMode.BILINEAR`` are supported. + For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable. + fill (sequence or number, optional): Pixel fill value for the area outside the transformed + image. If given a number, the value is used for all bands respectively. + In torchscript mode single int/float value is not supported, please use a sequence + of length 1: ``[value, ]``. + If input is PIL Image, the options is only available for ``Pillow>=5.0.0``. + + Returns: + PIL Image or Tensor: transformed Image. + """ + + coeffs = _get_perspective_coeffs(startpoints, endpoints) + + # Backward compatibility with integer value + if isinstance(interpolation, int): + warnings.warn( + "Argument interpolation should be of type InterpolationMode instead of int. " + "Please, use InterpolationMode enum." + ) + interpolation = _interpolation_modes_from_int(interpolation) + + if not isinstance(interpolation, InterpolationMode): + raise TypeError("Argument interpolation should be a InterpolationMode") + + if not isinstance(img, torch.Tensor): + pil_interpolation = pil_modes_mapping[interpolation] + return F_pil.perspective(img, coeffs, interpolation=pil_interpolation, fill=fill) + + return F_t.perspective(img, coeffs, interpolation=interpolation.value, fill=fill) + + +def vflip(img: Tensor) -> Tensor: + """Vertically flip the given image. + + Args: + img (PIL Image or Tensor): Image to be flipped. If img + is a Tensor, it is expected to be in [..., H, W] format, + where ... means it can have an arbitrary number of leading + dimensions. + + Returns: + PIL Image or Tensor: Vertically flipped image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.vflip(img) + + return F_t.vflip(img) + + +def five_crop(img: Tensor, size: List[int]) -> Tuple[Tensor, Tensor, Tensor, Tensor, Tensor]: + """Crop the given image into four corners and the central crop. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions + + .. Note:: + This transform returns a tuple of images and there may be a + mismatch in the number of inputs and targets your ``Dataset`` returns. + + Args: + img (PIL Image or Tensor): Image to be cropped. + size (sequence or int): Desired output size of the crop. If size is an + int instead of sequence like (h, w), a square crop (size, size) is + made. If provided a sequence of length 1, it will be interpreted as (size[0], size[0]). + + Returns: + tuple: tuple (tl, tr, bl, br, center) + Corresponding top left, top right, bottom left, bottom right and center crop. + """ + if isinstance(size, numbers.Number): + size = (int(size), int(size)) + elif isinstance(size, (tuple, list)) and len(size) == 1: + size = (size[0], size[0]) + + if len(size) != 2: + raise ValueError("Please provide only two dimensions (h, w) for size.") + + image_width, image_height = _get_image_size(img) + crop_height, crop_width = size + if crop_width > image_width or crop_height > image_height: + msg = "Requested crop size {} is bigger than input size {}" + raise ValueError(msg.format(size, (image_height, image_width))) + + tl = crop(img, 0, 0, crop_height, crop_width) + tr = crop(img, 0, image_width - crop_width, crop_height, crop_width) + bl = crop(img, image_height - crop_height, 0, crop_height, crop_width) + br = crop(img, image_height - crop_height, image_width - crop_width, crop_height, crop_width) + + center = center_crop(img, [crop_height, crop_width]) + + return tl, tr, bl, br, center + + +def ten_crop(img: Tensor, size: List[int], vertical_flip: bool = False) -> List[Tensor]: + """Generate ten cropped images from the given image. + Crop the given image into four corners and the central crop plus the + flipped version of these (horizontal flipping is used by default). + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions + + .. Note:: + This transform returns a tuple of images and there may be a + mismatch in the number of inputs and targets your ``Dataset`` returns. + + Args: + img (PIL Image or Tensor): Image to be cropped. + size (sequence or int): Desired output size of the crop. If size is an + int instead of sequence like (h, w), a square crop (size, size) is + made. If provided a sequence of length 1, it will be interpreted as (size[0], size[0]). + vertical_flip (bool): Use vertical flipping instead of horizontal + + Returns: + tuple: tuple (tl, tr, bl, br, center, tl_flip, tr_flip, bl_flip, br_flip, center_flip) + Corresponding top left, top right, bottom left, bottom right and + center crop and same for the flipped image. + """ + if isinstance(size, numbers.Number): + size = (int(size), int(size)) + elif isinstance(size, (tuple, list)) and len(size) == 1: + size = (size[0], size[0]) + + if len(size) != 2: + raise ValueError("Please provide only two dimensions (h, w) for size.") + + first_five = five_crop(img, size) + + if vertical_flip: + img = vflip(img) + else: + img = hflip(img) + + second_five = five_crop(img, size) + return first_five + second_five + + +def adjust_brightness(img: Tensor, brightness_factor: float) -> Tensor: + """Adjust brightness of an image. + + Args: + img (PIL Image or Tensor): Image to be adjusted. + If img is torch Tensor, it is expected to be in [..., 1 or 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + brightness_factor (float): How much to adjust the brightness. Can be + any non negative number. 0 gives a black image, 1 gives the + original image while 2 increases the brightness by a factor of 2. + + Returns: + PIL Image or Tensor: Brightness adjusted image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.adjust_brightness(img, brightness_factor) + + return F_t.adjust_brightness(img, brightness_factor) + + +def adjust_contrast(img: Tensor, contrast_factor: float) -> Tensor: + """Adjust contrast of an image. + + Args: + img (PIL Image or Tensor): Image to be adjusted. + If img is torch Tensor, it is expected to be in [..., 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + contrast_factor (float): How much to adjust the contrast. Can be any + non negative number. 0 gives a solid gray image, 1 gives the + original image while 2 increases the contrast by a factor of 2. + + Returns: + PIL Image or Tensor: Contrast adjusted image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.adjust_contrast(img, contrast_factor) + + return F_t.adjust_contrast(img, contrast_factor) + + +def adjust_saturation(img: Tensor, saturation_factor: float) -> Tensor: + """Adjust color saturation of an image. + + Args: + img (PIL Image or Tensor): Image to be adjusted. + If img is torch Tensor, it is expected to be in [..., 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + saturation_factor (float): How much to adjust the saturation. 0 will + give a black and white image, 1 will give the original image while + 2 will enhance the saturation by a factor of 2. + + Returns: + PIL Image or Tensor: Saturation adjusted image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.adjust_saturation(img, saturation_factor) + + return F_t.adjust_saturation(img, saturation_factor) + + +def adjust_hue(img: Tensor, hue_factor: float) -> Tensor: + """Adjust hue of an image. + + The image hue is adjusted by converting the image to HSV and + cyclically shifting the intensities in the hue channel (H). + The image is then converted back to original image mode. + + `hue_factor` is the amount of shift in H channel and must be in the + interval `[-0.5, 0.5]`. + + See `Hue`_ for more details. + + .. _Hue: https://en.wikipedia.org/wiki/Hue + + Args: + img (PIL Image or Tensor): Image to be adjusted. + If img is torch Tensor, it is expected to be in [..., 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + If img is PIL Image mode "1", "L", "I", "F" and modes with transparency (alpha channel) are not supported. + hue_factor (float): How much to shift the hue channel. Should be in + [-0.5, 0.5]. 0.5 and -0.5 give complete reversal of hue channel in + HSV space in positive and negative direction respectively. + 0 means no shift. Therefore, both -0.5 and 0.5 will give an image + with complementary colors while 0 gives the original image. + + Returns: + PIL Image or Tensor: Hue adjusted image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.adjust_hue(img, hue_factor) + + return F_t.adjust_hue(img, hue_factor) + + +def adjust_gamma(img: Tensor, gamma: float, gain: float = 1) -> Tensor: + r"""Perform gamma correction on an image. + + Also known as Power Law Transform. Intensities in RGB mode are adjusted + based on the following equation: + + .. math:: + I_{\text{out}} = 255 \times \text{gain} \times \left(\frac{I_{\text{in}}}{255}\right)^{\gamma} + + See `Gamma Correction`_ for more details. + + .. _Gamma Correction: https://en.wikipedia.org/wiki/Gamma_correction + + Args: + img (PIL Image or Tensor): PIL Image to be adjusted. + If img is torch Tensor, it is expected to be in [..., 1 or 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + If img is PIL Image, modes with transparency (alpha channel) are not supported. + gamma (float): Non negative real number, same as :math:`\gamma` in the equation. + gamma larger than 1 make the shadows darker, + while gamma smaller than 1 make dark regions lighter. + gain (float): The constant multiplier. + Returns: + PIL Image or Tensor: Gamma correction adjusted image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.adjust_gamma(img, gamma, gain) + + return F_t.adjust_gamma(img, gamma, gain) + + +def _get_inverse_affine_matrix( + center: List[float], angle: float, translate: List[float], scale: float, shear: List[float] +) -> List[float]: + # Helper method to compute inverse matrix for affine transformation + + # As it is explained in PIL.Image.rotate + # We need compute INVERSE of affine transformation matrix: M = T * C * RSS * C^-1 + # where T is translation matrix: [1, 0, tx | 0, 1, ty | 0, 0, 1] + # C is translation matrix to keep center: [1, 0, cx | 0, 1, cy | 0, 0, 1] + # RSS is rotation with scale and shear matrix + # RSS(a, s, (sx, sy)) = + # = R(a) * S(s) * SHy(sy) * SHx(sx) + # = [ s*cos(a - sy)/cos(sy), s*(-cos(a - sy)*tan(x)/cos(y) - sin(a)), 0 ] + # [ s*sin(a + sy)/cos(sy), s*(-sin(a - sy)*tan(x)/cos(y) + cos(a)), 0 ] + # [ 0 , 0 , 1 ] + # + # where R is a rotation matrix, S is a scaling matrix, and SHx and SHy are the shears: + # SHx(s) = [1, -tan(s)] and SHy(s) = [1 , 0] + # [0, 1 ] [-tan(s), 1] + # + # Thus, the inverse is M^-1 = C * RSS^-1 * C^-1 * T^-1 + + rot = math.radians(angle) + sx, sy = [math.radians(s) for s in shear] + + cx, cy = center + tx, ty = translate + + # RSS without scaling + a = math.cos(rot - sy) / math.cos(sy) + b = -math.cos(rot - sy) * math.tan(sx) / math.cos(sy) - math.sin(rot) + c = math.sin(rot - sy) / math.cos(sy) + d = -math.sin(rot - sy) * math.tan(sx) / math.cos(sy) + math.cos(rot) + + # Inverted rotation matrix with scale and shear + # det([[a, b], [c, d]]) == 1, since det(rotation) = 1 and det(shear) = 1 + matrix = [d, -b, 0.0, -c, a, 0.0] + matrix = [x / scale for x in matrix] + + # Apply inverse of translation and of center translation: RSS^-1 * C^-1 * T^-1 + matrix[2] += matrix[0] * (-cx - tx) + matrix[1] * (-cy - ty) + matrix[5] += matrix[3] * (-cx - tx) + matrix[4] * (-cy - ty) + + # Apply center translation: C * RSS^-1 * C^-1 * T^-1 + matrix[2] += cx + matrix[5] += cy + + return matrix + + +def rotate( + img: Tensor, angle: float, interpolation: InterpolationMode = InterpolationMode.NEAREST, + expand: bool = False, center: Optional[List[int]] = None, + fill: Optional[List[float]] = None, resample: Optional[int] = None +) -> Tensor: + """Rotate the image by angle. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions. + + Args: + img (PIL Image or Tensor): image to be rotated. + angle (number): rotation angle value in degrees, counter-clockwise. + interpolation (InterpolationMode): Desired interpolation enum defined by + :class:`torchvision.transforms.InterpolationMode`. Default is ``InterpolationMode.NEAREST``. + If input is Tensor, only ``InterpolationMode.NEAREST``, ``InterpolationMode.BILINEAR`` are supported. + For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable. + expand (bool, optional): Optional expansion flag. + If true, expands the output image to make it large enough to hold the entire rotated image. + If false or omitted, make the output image the same size as the input image. + Note that the expand flag assumes rotation around the center and no translation. + center (sequence, optional): Optional center of rotation. Origin is the upper left corner. + Default is the center of the image. + fill (sequence or number, optional): Pixel fill value for the area outside the transformed + image. If given a number, the value is used for all bands respectively. + In torchscript mode single int/float value is not supported, please use a sequence + of length 1: ``[value, ]``. + If input is PIL Image, the options is only available for ``Pillow>=5.2.0``. + + Returns: + PIL Image or Tensor: Rotated image. + + .. _filters: https://pillow.readthedocs.io/en/latest/handbook/concepts.html#filters + + """ + if resample is not None: + warnings.warn( + "Argument resample is deprecated and will be removed since v0.10.0. Please, use interpolation instead" + ) + interpolation = _interpolation_modes_from_int(resample) + + # Backward compatibility with integer value + if isinstance(interpolation, int): + warnings.warn( + "Argument interpolation should be of type InterpolationMode instead of int. " + "Please, use InterpolationMode enum." + ) + interpolation = _interpolation_modes_from_int(interpolation) + + if not isinstance(angle, (int, float)): + raise TypeError("Argument angle should be int or float") + + if center is not None and not isinstance(center, (list, tuple)): + raise TypeError("Argument center should be a sequence") + + if not isinstance(interpolation, InterpolationMode): + raise TypeError("Argument interpolation should be a InterpolationMode") + + if not isinstance(img, torch.Tensor): + pil_interpolation = pil_modes_mapping[interpolation] + return F_pil.rotate(img, angle=angle, interpolation=pil_interpolation, expand=expand, center=center, fill=fill) + + center_f = [0.0, 0.0] + if center is not None: + img_size = _get_image_size(img) + # Center values should be in pixel coordinates but translated such that (0, 0) corresponds to image center. + center_f = [1.0 * (c - s * 0.5) for c, s in zip(center, img_size)] + + # due to current incoherence of rotation angle direction between affine and rotate implementations + # we need to set -angle. + matrix = _get_inverse_affine_matrix(center_f, -angle, [0.0, 0.0], 1.0, [0.0, 0.0]) + return F_t.rotate(img, matrix=matrix, interpolation=interpolation.value, expand=expand, fill=fill) + + +def affine( + img: Tensor, angle: float, translate: List[int], scale: float, shear: List[float], + interpolation: InterpolationMode = InterpolationMode.NEAREST, fill: Optional[List[float]] = None, + resample: Optional[int] = None, fillcolor: Optional[List[float]] = None +) -> Tensor: + """Apply affine transformation on the image keeping image center invariant. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions. + + Args: + img (PIL Image or Tensor): image to transform. + angle (number): rotation angle in degrees between -180 and 180, clockwise direction. + translate (sequence of integers): horizontal and vertical translations (post-rotation translation) + scale (float): overall scale + shear (float or sequence): shear angle value in degrees between -180 to 180, clockwise direction. + If a sequence is specified, the first value corresponds to a shear parallel to the x axis, while + the second value corresponds to a shear parallel to the y axis. + interpolation (InterpolationMode): Desired interpolation enum defined by + :class:`torchvision.transforms.InterpolationMode`. Default is ``InterpolationMode.NEAREST``. + If input is Tensor, only ``InterpolationMode.NEAREST``, ``InterpolationMode.BILINEAR`` are supported. + For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable. + fill (sequence or number, optional): Pixel fill value for the area outside the transformed + image. If given a number, the value is used for all bands respectively. + In torchscript mode single int/float value is not supported, please use a sequence + of length 1: ``[value, ]``. + If input is PIL Image, the options is only available for ``Pillow>=5.0.0``. + fillcolor (sequence, int, float): deprecated argument and will be removed since v0.10.0. + Please use the ``fill`` parameter instead. + resample (int, optional): deprecated argument and will be removed since v0.10.0. + Please use the ``interpolation`` parameter instead. + + Returns: + PIL Image or Tensor: Transformed image. + """ + if resample is not None: + warnings.warn( + "Argument resample is deprecated and will be removed since v0.10.0. Please, use interpolation instead" + ) + interpolation = _interpolation_modes_from_int(resample) + + # Backward compatibility with integer value + if isinstance(interpolation, int): + warnings.warn( + "Argument interpolation should be of type InterpolationMode instead of int. " + "Please, use InterpolationMode enum." + ) + interpolation = _interpolation_modes_from_int(interpolation) + + if fillcolor is not None: + warnings.warn( + "Argument fillcolor is deprecated and will be removed since v0.10.0. Please, use fill instead" + ) + fill = fillcolor + + if not isinstance(angle, (int, float)): + raise TypeError("Argument angle should be int or float") + + if not isinstance(translate, (list, tuple)): + raise TypeError("Argument translate should be a sequence") + + if len(translate) != 2: + raise ValueError("Argument translate should be a sequence of length 2") + + if scale <= 0.0: + raise ValueError("Argument scale should be positive") + + if not isinstance(shear, (numbers.Number, (list, tuple))): + raise TypeError("Shear should be either a single value or a sequence of two values") + + if not isinstance(interpolation, InterpolationMode): + raise TypeError("Argument interpolation should be a InterpolationMode") + + if isinstance(angle, int): + angle = float(angle) + + if isinstance(translate, tuple): + translate = list(translate) + + if isinstance(shear, numbers.Number): + shear = [shear, 0.0] + + if isinstance(shear, tuple): + shear = list(shear) + + if len(shear) == 1: + shear = [shear[0], shear[0]] + + if len(shear) != 2: + raise ValueError("Shear should be a sequence containing two values. Got {}".format(shear)) + + img_size = _get_image_size(img) + if not isinstance(img, torch.Tensor): + # center = (img_size[0] * 0.5 + 0.5, img_size[1] * 0.5 + 0.5) + # it is visually better to estimate the center without 0.5 offset + # otherwise image rotated by 90 degrees is shifted vs output image of torch.rot90 or F_t.affine + center = [img_size[0] * 0.5, img_size[1] * 0.5] + matrix = _get_inverse_affine_matrix(center, angle, translate, scale, shear) + pil_interpolation = pil_modes_mapping[interpolation] + return F_pil.affine(img, matrix=matrix, interpolation=pil_interpolation, fill=fill) + + translate_f = [1.0 * t for t in translate] + matrix = _get_inverse_affine_matrix([0.0, 0.0], angle, translate_f, scale, shear) + return F_t.affine(img, matrix=matrix, interpolation=interpolation.value, fill=fill) + + +@torch.jit.unused +def to_grayscale(img, num_output_channels=1): + """Convert PIL image of any mode (RGB, HSV, LAB, etc) to grayscale version of image. + This transform does not support torch Tensor. + + Args: + img (PIL Image): PIL Image to be converted to grayscale. + num_output_channels (int): number of channels of the output image. Value can be 1 or 3. Default is 1. + + Returns: + PIL Image: Grayscale version of the image. + if num_output_channels = 1 : returned image is single channel + + if num_output_channels = 3 : returned image is 3 channel with r = g = b + """ + if isinstance(img, Image.Image): + return F_pil.to_grayscale(img, num_output_channels) + + raise TypeError("Input should be PIL Image") + + +def rgb_to_grayscale(img: Tensor, num_output_channels: int = 1) -> Tensor: + """Convert RGB image to grayscale version of image. + If the image is torch Tensor, it is expected + to have [..., 3, H, W] shape, where ... means an arbitrary number of leading dimensions + + Note: + Please, note that this method supports only RGB images as input. For inputs in other color spaces, + please, consider using meth:`~torchvision.transforms.functional.to_grayscale` with PIL Image. + + Args: + img (PIL Image or Tensor): RGB Image to be converted to grayscale. + num_output_channels (int): number of channels of the output image. Value can be 1 or 3. Default, 1. + + Returns: + PIL Image or Tensor: Grayscale version of the image. + if num_output_channels = 1 : returned image is single channel + + if num_output_channels = 3 : returned image is 3 channel with r = g = b + """ + if not isinstance(img, torch.Tensor): + return F_pil.to_grayscale(img, num_output_channels) + + return F_t.rgb_to_grayscale(img, num_output_channels) + + +def erase(img: Tensor, i: int, j: int, h: int, w: int, v: Tensor, inplace: bool = False) -> Tensor: + """ Erase the input Tensor Image with given value. + This transform does not support PIL Image. + + Args: + img (Tensor Image): Tensor image of size (C, H, W) to be erased + i (int): i in (i,j) i.e coordinates of the upper left corner. + j (int): j in (i,j) i.e coordinates of the upper left corner. + h (int): Height of the erased region. + w (int): Width of the erased region. + v: Erasing value. + inplace(bool, optional): For in-place operations. By default is set False. + + Returns: + Tensor Image: Erased image. + """ + if not isinstance(img, torch.Tensor): + raise TypeError('img should be Tensor Image. Got {}'.format(type(img))) + + if not inplace: + img = img.clone() + + img[..., i:i + h, j:j + w] = v + return img + + +def gaussian_blur(img: Tensor, kernel_size: List[int], sigma: Optional[List[float]] = None) -> Tensor: + """Performs Gaussian blurring on the image by given kernel. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions. + + Args: + img (PIL Image or Tensor): Image to be blurred + kernel_size (sequence of ints or int): Gaussian kernel size. Can be a sequence of integers + like ``(kx, ky)`` or a single integer for square kernels. + In torchscript mode kernel_size as single int is not supported, use a sequence of length 1: ``[ksize, ]``. + sigma (sequence of floats or float, optional): Gaussian kernel standard deviation. Can be a + sequence of floats like ``(sigma_x, sigma_y)`` or a single float to define the + same sigma in both X/Y directions. If None, then it is computed using + ``kernel_size`` as ``sigma = 0.3 * ((kernel_size - 1) * 0.5 - 1) + 0.8``. + Default, None. In torchscript mode sigma as single float is + not supported, use a sequence of length 1: ``[sigma, ]``. + + Returns: + PIL Image or Tensor: Gaussian Blurred version of the image. + """ + if not isinstance(kernel_size, (int, list, tuple)): + raise TypeError('kernel_size should be int or a sequence of integers. Got {}'.format(type(kernel_size))) + if isinstance(kernel_size, int): + kernel_size = [kernel_size, kernel_size] + if len(kernel_size) != 2: + raise ValueError('If kernel_size is a sequence its length should be 2. Got {}'.format(len(kernel_size))) + for ksize in kernel_size: + if ksize % 2 == 0 or ksize < 0: + raise ValueError('kernel_size should have odd and positive integers. Got {}'.format(kernel_size)) + + if sigma is None: + sigma = [ksize * 0.15 + 0.35 for ksize in kernel_size] + + if sigma is not None and not isinstance(sigma, (int, float, list, tuple)): + raise TypeError('sigma should be either float or sequence of floats. Got {}'.format(type(sigma))) + if isinstance(sigma, (int, float)): + sigma = [float(sigma), float(sigma)] + if isinstance(sigma, (list, tuple)) and len(sigma) == 1: + sigma = [sigma[0], sigma[0]] + if len(sigma) != 2: + raise ValueError('If sigma is a sequence, its length should be 2. Got {}'.format(len(sigma))) + for s in sigma: + if s <= 0.: + raise ValueError('sigma should have positive values. Got {}'.format(sigma)) + + t_img = img + if not isinstance(img, torch.Tensor): + if not F_pil._is_pil_image(img): + raise TypeError('img should be PIL Image or Tensor. Got {}'.format(type(img))) + + t_img = to_tensor(img) + + output = F_t.gaussian_blur(t_img, kernel_size, sigma) + + if not isinstance(img, torch.Tensor): + output = to_pil_image(output) + return output + + +def invert(img: Tensor) -> Tensor: + """Invert the colors of an RGB/grayscale image. + + Args: + img (PIL Image or Tensor): Image to have its colors inverted. + If img is torch Tensor, it is expected to be in [..., 1 or 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + If img is PIL Image, it is expected to be in mode "L" or "RGB". + + Returns: + PIL Image or Tensor: Color inverted image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.invert(img) + + return F_t.invert(img) + + +def posterize(img: Tensor, bits: int) -> Tensor: + """Posterize an image by reducing the number of bits for each color channel. + + Args: + img (PIL Image or Tensor): Image to have its colors posterized. + If img is torch Tensor, it should be of type torch.uint8 and + it is expected to be in [..., 1 or 3, H, W] format, where ... means + it can have an arbitrary number of leading dimensions. + If img is PIL Image, it is expected to be in mode "L" or "RGB". + bits (int): The number of bits to keep for each channel (0-8). + Returns: + PIL Image or Tensor: Posterized image. + """ + if not (0 <= bits <= 8): + raise ValueError('The number if bits should be between 0 and 8. Got {}'.format(bits)) + + if not isinstance(img, torch.Tensor): + return F_pil.posterize(img, bits) + + return F_t.posterize(img, bits) + + +def solarize(img: Tensor, threshold: float) -> Tensor: + """Solarize an RGB/grayscale image by inverting all pixel values above a threshold. + + Args: + img (PIL Image or Tensor): Image to have its colors inverted. + If img is torch Tensor, it is expected to be in [..., 1 or 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + If img is PIL Image, it is expected to be in mode "L" or "RGB". + threshold (float): All pixels equal or above this value are inverted. + Returns: + PIL Image or Tensor: Solarized image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.solarize(img, threshold) + + return F_t.solarize(img, threshold) + + +def adjust_sharpness(img: Tensor, sharpness_factor: float) -> Tensor: + """Adjust the sharpness of an image. + + Args: + img (PIL Image or Tensor): Image to be adjusted. + If img is torch Tensor, it is expected to be in [..., 1 or 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + sharpness_factor (float): How much to adjust the sharpness. Can be + any non negative number. 0 gives a blurred image, 1 gives the + original image while 2 increases the sharpness by a factor of 2. + + Returns: + PIL Image or Tensor: Sharpness adjusted image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.adjust_sharpness(img, sharpness_factor) + + return F_t.adjust_sharpness(img, sharpness_factor) + + +def autocontrast(img: Tensor) -> Tensor: + """Maximize contrast of an image by remapping its + pixels per channel so that the lowest becomes black and the lightest + becomes white. + + Args: + img (PIL Image or Tensor): Image on which autocontrast is applied. + If img is torch Tensor, it is expected to be in [..., 1 or 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + If img is PIL Image, it is expected to be in mode "L" or "RGB". + + Returns: + PIL Image or Tensor: An image that was autocontrasted. + """ + if not isinstance(img, torch.Tensor): + return F_pil.autocontrast(img) + + return F_t.autocontrast(img) + + +def equalize(img: Tensor) -> Tensor: + """Equalize the histogram of an image by applying + a non-linear mapping to the input in order to create a uniform + distribution of grayscale values in the output. + + Args: + img (PIL Image or Tensor): Image on which equalize is applied. + If img is torch Tensor, it is expected to be in [..., 1 or 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + If img is PIL Image, it is expected to be in mode "P", "L" or "RGB". + + Returns: + PIL Image or Tensor: An image that was equalized. + """ + if not isinstance(img, torch.Tensor): + return F_pil.equalize(img) + + return F_t.equalize(img) diff --git a/cv/detection/fasterrcnn/pytorch/dataloaders/functional_pil.py b/cv/detection/fasterrcnn/pytorch/dataloaders/functional_pil.py new file mode 100644 index 000000000..6999a2acf --- /dev/null +++ b/cv/detection/fasterrcnn/pytorch/dataloaders/functional_pil.py @@ -0,0 +1,349 @@ +import numbers +from typing import Any, List, Sequence + +import numpy as np +import torch +from PIL import Image, ImageOps, ImageEnhance, ImageFilter, __version__ as PILLOW_VERSION + +try: + import accimage +except ImportError: + accimage = None + + +@torch.jit.unused +def _is_pil_image(img: Any) -> bool: + if accimage is not None: + return isinstance(img, (Image.Image, accimage.Image)) + else: + return isinstance(img, Image.Image) + + +@torch.jit.unused +def _get_image_size(img: Any) -> List[int]: + if _is_pil_image(img): + return img.size + raise TypeError("Unexpected type {}".format(type(img))) + + +@torch.jit.unused +def _get_image_num_channels(img: Any) -> int: + if _is_pil_image(img): + return 1 if img.mode == 'L' else 3 + raise TypeError("Unexpected type {}".format(type(img))) + + +@torch.jit.unused +def hflip(img): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + return img.transpose(Image.FLIP_LEFT_RIGHT) + + +@torch.jit.unused +def vflip(img): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + return img.transpose(Image.FLIP_TOP_BOTTOM) + + +@torch.jit.unused +def adjust_brightness(img, brightness_factor): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + enhancer = ImageEnhance.Brightness(img) + img = enhancer.enhance(brightness_factor) + return img + + +@torch.jit.unused +def adjust_contrast(img, contrast_factor): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + enhancer = ImageEnhance.Contrast(img) + img = enhancer.enhance(contrast_factor) + return img + + +@torch.jit.unused +def adjust_saturation(img, saturation_factor): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + enhancer = ImageEnhance.Color(img) + img = enhancer.enhance(saturation_factor) + return img + + +@torch.jit.unused +def adjust_hue(img, hue_factor): + if not(-0.5 <= hue_factor <= 0.5): + raise ValueError('hue_factor ({}) is not in [-0.5, 0.5].'.format(hue_factor)) + + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + input_mode = img.mode + if input_mode in {'L', '1', 'I', 'F'}: + return img + + h, s, v = img.convert('HSV').split() + + np_h = np.array(h, dtype=np.uint8) + # uint8 addition take cares of rotation across boundaries + with np.errstate(over='ignore'): + np_h += np.uint8(hue_factor * 255) + h = Image.fromarray(np_h, 'L') + + img = Image.merge('HSV', (h, s, v)).convert(input_mode) + return img + + +@torch.jit.unused +def adjust_gamma(img, gamma, gain=1): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + if gamma < 0: + raise ValueError('Gamma should be a non-negative real number') + + input_mode = img.mode + img = img.convert('RGB') + gamma_map = [(255 + 1 - 1e-3) * gain * pow(ele / 255., gamma) for ele in range(256)] * 3 + img = img.point(gamma_map) # use PIL's point-function to accelerate this part + + img = img.convert(input_mode) + return img + + +@torch.jit.unused +def pad(img, padding, fill=0, padding_mode="constant"): + if not _is_pil_image(img): + raise TypeError("img should be PIL Image. Got {}".format(type(img))) + + if not isinstance(padding, (numbers.Number, tuple, list)): + raise TypeError("Got inappropriate padding arg") + if not isinstance(fill, (numbers.Number, str, tuple)): + raise TypeError("Got inappropriate fill arg") + if not isinstance(padding_mode, str): + raise TypeError("Got inappropriate padding_mode arg") + + if isinstance(padding, list): + padding = tuple(padding) + + if isinstance(padding, tuple) and len(padding) not in [1, 2, 4]: + raise ValueError("Padding must be an int or a 1, 2, or 4 element tuple, not a " + + "{} element tuple".format(len(padding))) + + if isinstance(padding, tuple) and len(padding) == 1: + # Compatibility with `functional_tensor.pad` + padding = padding[0] + + if padding_mode not in ["constant", "edge", "reflect", "symmetric"]: + raise ValueError("Padding mode should be either constant, edge, reflect or symmetric") + + if padding_mode == "constant": + opts = _parse_fill(fill, img, "2.3.0", name="fill") + if img.mode == "P": + palette = img.getpalette() + image = ImageOps.expand(img, border=padding, **opts) + image.putpalette(palette) + return image + + return ImageOps.expand(img, border=padding, **opts) + else: + if isinstance(padding, int): + pad_left = pad_right = pad_top = pad_bottom = padding + if isinstance(padding, tuple) and len(padding) == 2: + pad_left = pad_right = padding[0] + pad_top = pad_bottom = padding[1] + if isinstance(padding, tuple) and len(padding) == 4: + pad_left = padding[0] + pad_top = padding[1] + pad_right = padding[2] + pad_bottom = padding[3] + + p = [pad_left, pad_top, pad_right, pad_bottom] + cropping = -np.minimum(p, 0) + + if cropping.any(): + crop_left, crop_top, crop_right, crop_bottom = cropping + img = img.crop((crop_left, crop_top, img.width - crop_right, img.height - crop_bottom)) + + pad_left, pad_top, pad_right, pad_bottom = np.maximum(p, 0) + + if img.mode == 'P': + palette = img.getpalette() + img = np.asarray(img) + img = np.pad(img, ((pad_top, pad_bottom), (pad_left, pad_right)), padding_mode) + img = Image.fromarray(img) + img.putpalette(palette) + return img + + img = np.asarray(img) + # RGB image + if len(img.shape) == 3: + img = np.pad(img, ((pad_top, pad_bottom), (pad_left, pad_right), (0, 0)), padding_mode) + # Grayscale image + if len(img.shape) == 2: + img = np.pad(img, ((pad_top, pad_bottom), (pad_left, pad_right)), padding_mode) + + return Image.fromarray(img) + + +@torch.jit.unused +def crop(img: Image.Image, top: int, left: int, height: int, width: int) -> Image.Image: + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + return img.crop((left, top, left + width, top + height)) + + +@torch.jit.unused +def resize(img, size, interpolation=Image.BILINEAR): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + if not (isinstance(size, int) or (isinstance(size, Sequence) and len(size) in (1, 2))): + raise TypeError('Got inappropriate size arg: {}'.format(size)) + + if isinstance(size, int) or len(size) == 1: + if isinstance(size, Sequence): + size = size[0] + w, h = img.size + if (w <= h and w == size) or (h <= w and h == size): + return img + if w < h: + ow = size + oh = int(size * h / w) + return img.resize((ow, oh), interpolation) + else: + oh = size + ow = int(size * w / h) + return img.resize((ow, oh), interpolation) + else: + return img.resize(size[::-1], interpolation) + + +@torch.jit.unused +def _parse_fill(fill, img, min_pil_version, name="fillcolor"): + # Process fill color for affine transforms + major_found, minor_found = (int(v) for v in PILLOW_VERSION.split('.')[:2]) + major_required, minor_required = (int(v) for v in min_pil_version.split('.')[:2]) + if major_found < major_required or (major_found == major_required and minor_found < minor_required): + if fill is None: + return {} + else: + msg = ("The option to fill background area of the transformed image, " + "requires pillow>={}") + raise RuntimeError(msg.format(min_pil_version)) + + num_bands = len(img.getbands()) + if fill is None: + fill = 0 + if isinstance(fill, (int, float)) and num_bands > 1: + fill = tuple([fill] * num_bands) + if isinstance(fill, (list, tuple)): + if len(fill) != num_bands: + msg = ("The number of elements in 'fill' does not match the number of " + "bands of the image ({} != {})") + raise ValueError(msg.format(len(fill), num_bands)) + + fill = tuple(fill) + + return {name: fill} + + +@torch.jit.unused +def affine(img, matrix, interpolation=0, fill=None): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + output_size = img.size + opts = _parse_fill(fill, img, '5.0.0') + return img.transform(output_size, Image.AFFINE, matrix, interpolation, **opts) + + +@torch.jit.unused +def rotate(img, angle, interpolation=0, expand=False, center=None, fill=None): + if not _is_pil_image(img): + raise TypeError("img should be PIL Image. Got {}".format(type(img))) + + opts = _parse_fill(fill, img, '5.2.0') + return img.rotate(angle, interpolation, expand, center, **opts) + + +@torch.jit.unused +def perspective(img, perspective_coeffs, interpolation=Image.BICUBIC, fill=None): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + opts = _parse_fill(fill, img, '5.0.0') + + return img.transform(img.size, Image.PERSPECTIVE, perspective_coeffs, interpolation, **opts) + + +@torch.jit.unused +def to_grayscale(img, num_output_channels): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + if num_output_channels == 1: + img = img.convert('L') + elif num_output_channels == 3: + img = img.convert('L') + np_img = np.array(img, dtype=np.uint8) + np_img = np.dstack([np_img, np_img, np_img]) + img = Image.fromarray(np_img, 'RGB') + else: + raise ValueError('num_output_channels should be either 1 or 3') + + return img + + +@torch.jit.unused +def invert(img): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + return ImageOps.invert(img) + + +@torch.jit.unused +def posterize(img, bits): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + return ImageOps.posterize(img, bits) + + +@torch.jit.unused +def solarize(img, threshold): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + return ImageOps.solarize(img, threshold) + + +@torch.jit.unused +def adjust_sharpness(img, sharpness_factor): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + enhancer = ImageEnhance.Sharpness(img) + img = enhancer.enhance(sharpness_factor) + return img + + +@torch.jit.unused +def autocontrast(img): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + return ImageOps.autocontrast(img) + + +@torch.jit.unused +def equalize(img): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + return ImageOps.equalize(img) diff --git a/cv/detection/fasterrcnn/pytorch/dataloaders/functional_tensor.py b/cv/detection/fasterrcnn/pytorch/dataloaders/functional_tensor.py new file mode 100644 index 000000000..69445e6a2 --- /dev/null +++ b/cv/detection/fasterrcnn/pytorch/dataloaders/functional_tensor.py @@ -0,0 +1,920 @@ +import warnings + +import torch +from torch import Tensor +from torch.nn.functional import grid_sample, conv2d, interpolate, pad as torch_pad +from torch.jit.annotations import BroadcastingList2 +from typing import Optional, Tuple, List + + +def _is_tensor_a_torch_image(x: Tensor) -> bool: + return x.ndim >= 2 + + +def _assert_image_tensor(img): + if not _is_tensor_a_torch_image(img): + raise TypeError("Tensor is not a torch image.") + + +def _get_image_size(img: Tensor) -> List[int]: + # Returns (w, h) of tensor image + _assert_image_tensor(img) + return [img.shape[-1], img.shape[-2]] + + +def _get_image_num_channels(img: Tensor) -> int: + if img.ndim == 2: + return 1 + elif img.ndim > 2: + return img.shape[-3] + + raise TypeError("Input ndim should be 2 or more. Got {}".format(img.ndim)) + + +def _max_value(dtype: torch.dtype) -> float: + # TODO: replace this method with torch.iinfo when it gets torchscript support. + # https://github.com/pytorch/pytorch/issues/41492 + + a = torch.tensor(2, dtype=dtype) + signed = 1 if torch.tensor(0, dtype=dtype).is_signed() else 0 + bits = 1 + max_value = torch.tensor(-signed, dtype=torch.long) + while True: + next_value = a.pow(bits - signed).sub(1) + if next_value > max_value: + max_value = next_value + bits *= 2 + else: + break + return max_value.item() + + +def _assert_channels(img: Tensor, permitted: List[int]) -> None: + c = _get_image_num_channels(img) + if c not in permitted: + raise TypeError("Input image tensor permitted channel values are {}, but found {}".format(permitted, c)) + + +def convert_image_dtype(image: torch.Tensor, dtype: torch.dtype = torch.float) -> torch.Tensor: + if image.dtype == dtype: + return image + + if image.is_floating_point(): + + # TODO: replace with dtype.is_floating_point when torchscript supports it + if torch.tensor(0, dtype=dtype).is_floating_point(): + return image.to(dtype) + + # float to int + if (image.dtype == torch.float32 and dtype in (torch.int32, torch.int64)) or ( + image.dtype == torch.float64 and dtype == torch.int64 + ): + msg = f"The cast from {image.dtype} to {dtype} cannot be performed safely." + raise RuntimeError(msg) + + # https://github.com/pytorch/vision/pull/2078#issuecomment-612045321 + # For data in the range 0-1, (float * 255).to(uint) is only 255 + # when float is exactly 1.0. + # `max + 1 - epsilon` provides more evenly distributed mapping of + # ranges of floats to ints. + eps = 1e-3 + max_val = _max_value(dtype) + result = image.mul(max_val + 1.0 - eps) + return result.to(dtype) + else: + input_max = _max_value(image.dtype) + + # int to float + # TODO: replace with dtype.is_floating_point when torchscript supports it + if torch.tensor(0, dtype=dtype).is_floating_point(): + image = image.to(dtype) + return image / input_max + + output_max = _max_value(dtype) + + # int to int + if input_max > output_max: + # factor should be forced to int for torch jit script + # otherwise factor is a float and image // factor can produce different results + factor = int((input_max + 1) // (output_max + 1)) + image = image // factor + return image.to(dtype) + else: + # factor should be forced to int for torch jit script + # otherwise factor is a float and image * factor can produce different results + factor = int((output_max + 1) // (input_max + 1)) + image = image.to(dtype) + return image * factor + + +def vflip(img: Tensor) -> Tensor: + _assert_image_tensor(img) + + return img.flip(-2) + + +def hflip(img: Tensor) -> Tensor: + _assert_image_tensor(img) + + return img.flip(-1) + + +def crop(img: Tensor, top: int, left: int, height: int, width: int) -> Tensor: + _assert_image_tensor(img) + + return img[..., top:top + height, left:left + width] + + +def rgb_to_grayscale(img: Tensor, num_output_channels: int = 1) -> Tensor: + if img.ndim < 3: + raise TypeError("Input image tensor should have at least 3 dimensions, but found {}".format(img.ndim)) + _assert_channels(img, [3]) + + if num_output_channels not in (1, 3): + raise ValueError('num_output_channels should be either 1 or 3') + + r, g, b = img.unbind(dim=-3) + # This implementation closely follows the TF one: + # https://github.com/tensorflow/tensorflow/blob/v2.3.0/tensorflow/python/ops/image_ops_impl.py#L2105-L2138 + l_img = (0.2989 * r + 0.587 * g + 0.114 * b).to(img.dtype) + l_img = l_img.unsqueeze(dim=-3) + + if num_output_channels == 3: + return l_img.expand(img.shape) + + return l_img + + +def adjust_brightness(img: Tensor, brightness_factor: float) -> Tensor: + if brightness_factor < 0: + raise ValueError('brightness_factor ({}) is not non-negative.'.format(brightness_factor)) + + _assert_image_tensor(img) + + _assert_channels(img, [1, 3]) + + return _blend(img, torch.zeros_like(img), brightness_factor) + + +def adjust_contrast(img: Tensor, contrast_factor: float) -> Tensor: + if contrast_factor < 0: + raise ValueError('contrast_factor ({}) is not non-negative.'.format(contrast_factor)) + + _assert_image_tensor(img) + + _assert_channels(img, [3]) + + dtype = img.dtype if torch.is_floating_point(img) else torch.float32 + mean = torch.mean(rgb_to_grayscale(img).to(dtype), dim=(-3, -2, -1), keepdim=True) + + return _blend(img, mean, contrast_factor) + + +def adjust_hue(img: Tensor, hue_factor: float) -> Tensor: + if not (-0.5 <= hue_factor <= 0.5): + raise ValueError('hue_factor ({}) is not in [-0.5, 0.5].'.format(hue_factor)) + + if not (isinstance(img, torch.Tensor)): + raise TypeError('Input img should be Tensor image') + + _assert_image_tensor(img) + + _assert_channels(img, [1, 3]) + if _get_image_num_channels(img) == 1: # Match PIL behaviour + return img + + orig_dtype = img.dtype + if img.dtype == torch.uint8: + img = img.to(dtype=torch.float32) / 255.0 + + img = _rgb2hsv(img) + h, s, v = img.unbind(dim=-3) + h = (h + hue_factor) % 1.0 + img = torch.stack((h, s, v), dim=-3) + img_hue_adj = _hsv2rgb(img) + + if orig_dtype == torch.uint8: + img_hue_adj = (img_hue_adj * 255.0).to(dtype=orig_dtype) + + return img_hue_adj + + +def adjust_saturation(img: Tensor, saturation_factor: float) -> Tensor: + if saturation_factor < 0: + raise ValueError('saturation_factor ({}) is not non-negative.'.format(saturation_factor)) + + _assert_image_tensor(img) + + _assert_channels(img, [3]) + + return _blend(img, rgb_to_grayscale(img), saturation_factor) + + +def adjust_gamma(img: Tensor, gamma: float, gain: float = 1) -> Tensor: + if not isinstance(img, torch.Tensor): + raise TypeError('Input img should be a Tensor.') + + _assert_channels(img, [1, 3]) + + if gamma < 0: + raise ValueError('Gamma should be a non-negative real number') + + result = img + dtype = img.dtype + if not torch.is_floating_point(img): + result = convert_image_dtype(result, torch.float32) + + result = (gain * result ** gamma).clamp(0, 1) + + result = convert_image_dtype(result, dtype) + result = result.to(dtype) + return result + + +def center_crop(img: Tensor, output_size: BroadcastingList2[int]) -> Tensor: + """DEPRECATED + """ + warnings.warn( + "This method is deprecated and will be removed in future releases. " + "Please, use ``F.center_crop`` instead." + ) + + _assert_image_tensor(img) + + _, image_width, image_height = img.size() + crop_height, crop_width = output_size + # crop_top = int(round((image_height - crop_height) / 2.)) + # Result can be different between python func and scripted func + # Temporary workaround: + crop_top = int((image_height - crop_height + 1) * 0.5) + # crop_left = int(round((image_width - crop_width) / 2.)) + # Result can be different between python func and scripted func + # Temporary workaround: + crop_left = int((image_width - crop_width + 1) * 0.5) + + return crop(img, crop_top, crop_left, crop_height, crop_width) + + +def five_crop(img: Tensor, size: BroadcastingList2[int]) -> List[Tensor]: + """DEPRECATED + """ + warnings.warn( + "This method is deprecated and will be removed in future releases. " + "Please, use ``F.five_crop`` instead." + ) + + _assert_image_tensor(img) + + assert len(size) == 2, "Please provide only two dimensions (h, w) for size." + + _, image_width, image_height = img.size() + crop_height, crop_width = size + if crop_width > image_width or crop_height > image_height: + msg = "Requested crop size {} is bigger than input size {}" + raise ValueError(msg.format(size, (image_height, image_width))) + + tl = crop(img, 0, 0, crop_width, crop_height) + tr = crop(img, image_width - crop_width, 0, image_width, crop_height) + bl = crop(img, 0, image_height - crop_height, crop_width, image_height) + br = crop(img, image_width - crop_width, image_height - crop_height, image_width, image_height) + center = center_crop(img, (crop_height, crop_width)) + + return [tl, tr, bl, br, center] + + +def ten_crop(img: Tensor, size: BroadcastingList2[int], vertical_flip: bool = False) -> List[Tensor]: + """DEPRECATED + """ + warnings.warn( + "This method is deprecated and will be removed in future releases. " + "Please, use ``F.ten_crop`` instead." + ) + + _assert_image_tensor(img) + + assert len(size) == 2, "Please provide only two dimensions (h, w) for size." + first_five = five_crop(img, size) + + if vertical_flip: + img = vflip(img) + else: + img = hflip(img) + + second_five = five_crop(img, size) + + return first_five + second_five + + +def _blend(img1: Tensor, img2: Tensor, ratio: float) -> Tensor: + ratio = float(ratio) + bound = 1.0 if img1.is_floating_point() else 255.0 + return (ratio * img1 + (1.0 - ratio) * img2).clamp(0, bound).to(img1.dtype) + + +def _rgb2hsv(img): + r, g, b = img.unbind(dim=-3) + + # Implementation is based on https://github.com/python-pillow/Pillow/blob/4174d4267616897df3746d315d5a2d0f82c656ee/ + # src/libImaging/Convert.c#L330 + maxc = torch.max(img, dim=-3).values + minc = torch.min(img, dim=-3).values + + # The algorithm erases S and H channel where `maxc = minc`. This avoids NaN + # from happening in the results, because + # + S channel has division by `maxc`, which is zero only if `maxc = minc` + # + H channel has division by `(maxc - minc)`. + # + # Instead of overwriting NaN afterwards, we just prevent it from occuring so + # we don't need to deal with it in case we save the NaN in a buffer in + # backprop, if it is ever supported, but it doesn't hurt to do so. + eqc = maxc == minc + + cr = maxc - minc + # Since `eqc => cr = 0`, replacing denominator with 1 when `eqc` is fine. + ones = torch.ones_like(maxc) + s = cr / torch.where(eqc, ones, maxc) + # Note that `eqc => maxc = minc = r = g = b`. So the following calculation + # of `h` would reduce to `bc - gc + 2 + rc - bc + 4 + rc - bc = 6` so it + # would not matter what values `rc`, `gc`, and `bc` have here, and thus + # replacing denominator with 1 when `eqc` is fine. + cr_divisor = torch.where(eqc, ones, cr) + rc = (maxc - r) / cr_divisor + gc = (maxc - g) / cr_divisor + bc = (maxc - b) / cr_divisor + + hr = (maxc == r) * (bc - gc) + hg = ((maxc == g) & (maxc != r)) * (2.0 + rc - bc) + hb = ((maxc != g) & (maxc != r)) * (4.0 + gc - rc) + h = (hr + hg + hb) + h = torch.fmod((h / 6.0 + 1.0), 1.0) + return torch.stack((h, s, maxc), dim=-3) + + +def _hsv2rgb(img): + h, s, v = img.unbind(dim=-3) + i = torch.floor(h * 6.0) + f = (h * 6.0) - i + i = i.to(dtype=torch.int32) + + p = torch.clamp((v * (1.0 - s)), 0.0, 1.0) + q = torch.clamp((v * (1.0 - s * f)), 0.0, 1.0) + t = torch.clamp((v * (1.0 - s * (1.0 - f))), 0.0, 1.0) + i = i % 6 + + mask = i.unsqueeze(dim=-3) == torch.arange(6, device=i.device).view(-1, 1, 1) + + a1 = torch.stack((v, q, p, p, t, v), dim=-3) + a2 = torch.stack((t, v, v, q, p, p), dim=-3) + a3 = torch.stack((p, p, t, v, v, q), dim=-3) + a4 = torch.stack((a1, a2, a3), dim=-4) + + return torch.einsum("...ijk, ...xijk -> ...xjk", mask.to(dtype=img.dtype), a4) + + +def _pad_symmetric(img: Tensor, padding: List[int]) -> Tensor: + # padding is left, right, top, bottom + + # crop if needed + if padding[0] < 0 or padding[1] < 0 or padding[2] < 0 or padding[3] < 0: + crop_left, crop_right, crop_top, crop_bottom = [-min(x, 0) for x in padding] + img = img[..., crop_top:img.shape[-2] - crop_bottom, crop_left:img.shape[-1] - crop_right] + padding = [max(x, 0) for x in padding] + + in_sizes = img.size() + + x_indices = [i for i in range(in_sizes[-1])] # [0, 1, 2, 3, ...] + left_indices = [i for i in range(padding[0] - 1, -1, -1)] # e.g. [3, 2, 1, 0] + right_indices = [-(i + 1) for i in range(padding[1])] # e.g. [-1, -2, -3] + x_indices = torch.tensor(left_indices + x_indices + right_indices) + + y_indices = [i for i in range(in_sizes[-2])] + top_indices = [i for i in range(padding[2] - 1, -1, -1)] + bottom_indices = [-(i + 1) for i in range(padding[3])] + y_indices = torch.tensor(top_indices + y_indices + bottom_indices) + + ndim = img.ndim + if ndim == 3: + return img[:, y_indices[:, None], x_indices[None, :]] + elif ndim == 4: + return img[:, :, y_indices[:, None], x_indices[None, :]] + else: + raise RuntimeError("Symmetric padding of N-D tensors are not supported yet") + + +def pad(img: Tensor, padding: List[int], fill: int = 0, padding_mode: str = "constant") -> Tensor: + _assert_image_tensor(img) + + if not isinstance(padding, (int, tuple, list)): + raise TypeError("Got inappropriate padding arg") + if not isinstance(fill, (int, float)): + raise TypeError("Got inappropriate fill arg") + if not isinstance(padding_mode, str): + raise TypeError("Got inappropriate padding_mode arg") + + if isinstance(padding, tuple): + padding = list(padding) + + if isinstance(padding, list) and len(padding) not in [1, 2, 4]: + raise ValueError("Padding must be an int or a 1, 2, or 4 element tuple, not a " + + "{} element tuple".format(len(padding))) + + if padding_mode not in ["constant", "edge", "reflect", "symmetric"]: + raise ValueError("Padding mode should be either constant, edge, reflect or symmetric") + + if isinstance(padding, int): + if torch.jit.is_scripting(): + # This maybe unreachable + raise ValueError("padding can't be an int while torchscripting, set it as a list [value, ]") + pad_left = pad_right = pad_top = pad_bottom = padding + elif len(padding) == 1: + pad_left = pad_right = pad_top = pad_bottom = padding[0] + elif len(padding) == 2: + pad_left = pad_right = padding[0] + pad_top = pad_bottom = padding[1] + else: + pad_left = padding[0] + pad_top = padding[1] + pad_right = padding[2] + pad_bottom = padding[3] + + p = [pad_left, pad_right, pad_top, pad_bottom] + + if padding_mode == "edge": + # remap padding_mode str + padding_mode = "replicate" + elif padding_mode == "symmetric": + # route to another implementation + return _pad_symmetric(img, p) + + need_squeeze = False + if img.ndim < 4: + img = img.unsqueeze(dim=0) + need_squeeze = True + + out_dtype = img.dtype + need_cast = False + if (padding_mode != "constant") and img.dtype not in (torch.float32, torch.float64): + # Here we temporary cast input tensor to float + # until pytorch issue is resolved : + # https://github.com/pytorch/pytorch/issues/40763 + need_cast = True + img = img.to(torch.float32) + + img = torch_pad(img, p, mode=padding_mode, value=float(fill)) + + if need_squeeze: + img = img.squeeze(dim=0) + + if need_cast: + img = img.to(out_dtype) + + return img + + +def resize(img: Tensor, size: List[int], interpolation: str = "bilinear") -> Tensor: + _assert_image_tensor(img) + + if not isinstance(size, (int, tuple, list)): + raise TypeError("Got inappropriate size arg") + if not isinstance(interpolation, str): + raise TypeError("Got inappropriate interpolation arg") + + if interpolation not in ["nearest", "bilinear", "bicubic"]: + raise ValueError("This interpolation mode is unsupported with Tensor input") + + if isinstance(size, tuple): + size = list(size) + + if isinstance(size, list) and len(size) not in [1, 2]: + raise ValueError("Size must be an int or a 1 or 2 element tuple/list, not a " + "{} element tuple/list".format(len(size))) + + w, h = _get_image_size(img) + + if isinstance(size, int): + size_w, size_h = size, size + elif len(size) < 2: + size_w, size_h = size[0], size[0] + else: + size_w, size_h = size[1], size[0] # Convention (h, w) + + if isinstance(size, int) or len(size) < 2: + if w < h: + size_h = int(size_w * h / w) + else: + size_w = int(size_h * w / h) + + if (w <= h and w == size_w) or (h <= w and h == size_h): + return img + + img, need_cast, need_squeeze, out_dtype = _cast_squeeze_in(img, [torch.float32, torch.float64]) + + # Define align_corners to avoid warnings + align_corners = False if interpolation in ["bilinear", "bicubic"] else None + + img = interpolate(img, size=[size_h, size_w], mode=interpolation, align_corners=align_corners) + + if interpolation == "bicubic" and out_dtype == torch.uint8: + img = img.clamp(min=0, max=255) + + img = _cast_squeeze_out(img, need_cast=need_cast, need_squeeze=need_squeeze, out_dtype=out_dtype) + + return img + + +def _assert_grid_transform_inputs( + img: Tensor, + matrix: Optional[List[float]], + interpolation: str, + fill: Optional[List[float]], + supported_interpolation_modes: List[str], + coeffs: Optional[List[float]] = None, +): + + if not (isinstance(img, torch.Tensor)): + raise TypeError("Input img should be Tensor") + + _assert_image_tensor(img) + + if matrix is not None and not isinstance(matrix, list): + raise TypeError("Argument matrix should be a list") + + if matrix is not None and len(matrix) != 6: + raise ValueError("Argument matrix should have 6 float values") + + if coeffs is not None and len(coeffs) != 8: + raise ValueError("Argument coeffs should have 8 float values") + + if fill is not None and not isinstance(fill, (int, float, tuple, list)): + warnings.warn("Argument fill should be either int, float, tuple or list") + + # Check fill + num_channels = _get_image_num_channels(img) + if isinstance(fill, (tuple, list)) and (len(fill) > 1 and len(fill) != num_channels): + msg = ("The number of elements in 'fill' cannot broadcast to match the number of " + "channels of the image ({} != {})") + raise ValueError(msg.format(len(fill), num_channels)) + + if interpolation not in supported_interpolation_modes: + raise ValueError("Interpolation mode '{}' is unsupported with Tensor input".format(interpolation)) + + +def _cast_squeeze_in(img: Tensor, req_dtypes: List[torch.dtype]) -> Tuple[Tensor, bool, bool, torch.dtype]: + need_squeeze = False + # make image NCHW + if img.ndim < 4: + img = img.unsqueeze(dim=0) + need_squeeze = True + + out_dtype = img.dtype + need_cast = False + if out_dtype not in req_dtypes: + need_cast = True + req_dtype = req_dtypes[0] + img = img.to(req_dtype) + return img, need_cast, need_squeeze, out_dtype + + +def _cast_squeeze_out(img: Tensor, need_cast: bool, need_squeeze: bool, out_dtype: torch.dtype): + if need_squeeze: + img = img.squeeze(dim=0) + + if need_cast: + if out_dtype in (torch.uint8, torch.int8, torch.int16, torch.int32, torch.int64): + # it is better to round before cast + img = torch.round(img) + img = img.to(out_dtype) + + return img + + +def _apply_grid_transform(img: Tensor, grid: Tensor, mode: str, fill: Optional[List[float]]) -> Tensor: + + img, need_cast, need_squeeze, out_dtype = _cast_squeeze_in(img, [grid.dtype, ]) + + if img.shape[0] > 1: + # Apply same grid to a batch of images + grid = grid.expand(img.shape[0], grid.shape[1], grid.shape[2], grid.shape[3]) + + # Append a dummy mask for customized fill colors, should be faster than grid_sample() twice + if fill is not None: + dummy = torch.ones((img.shape[0], 1, img.shape[2], img.shape[3]), dtype=img.dtype, device=img.device) + img = torch.cat((img, dummy), dim=1) + + img = grid_sample(img, grid, mode=mode, padding_mode="zeros", align_corners=False) + + # Fill with required color + if fill is not None: + mask = img[:, -1:, :, :] # N * 1 * H * W + img = img[:, :-1, :, :] # N * C * H * W + mask = mask.expand_as(img) + len_fill = len(fill) if isinstance(fill, (tuple, list)) else 1 + fill_img = torch.tensor(fill, dtype=img.dtype, device=img.device).view(1, len_fill, 1, 1).expand_as(img) + if mode == 'nearest': + mask = mask < 0.5 + img[mask] = fill_img[mask] + else: # 'bilinear' + img = img * mask + (1.0 - mask) * fill_img + + img = _cast_squeeze_out(img, need_cast, need_squeeze, out_dtype) + return img + + +def _gen_affine_grid( + theta: Tensor, w: int, h: int, ow: int, oh: int, +) -> Tensor: + # https://github.com/pytorch/pytorch/blob/74b65c32be68b15dc7c9e8bb62459efbfbde33d8/aten/src/ATen/native/ + # AffineGridGenerator.cpp#L18 + # Difference with AffineGridGenerator is that: + # 1) we normalize grid values after applying theta + # 2) we can normalize by other image size, such that it covers "extend" option like in PIL.Image.rotate + + d = 0.5 + base_grid = torch.empty(1, oh, ow, 3, dtype=theta.dtype, device=theta.device) + x_grid = torch.linspace(-ow * 0.5 + d, ow * 0.5 + d - 1, steps=ow, device=theta.device) + base_grid[..., 0].copy_(x_grid) + y_grid = torch.linspace(-oh * 0.5 + d, oh * 0.5 + d - 1, steps=oh, device=theta.device).unsqueeze_(-1) + base_grid[..., 1].copy_(y_grid) + base_grid[..., 2].fill_(1) + + rescaled_theta = theta.transpose(1, 2) / torch.tensor([0.5 * w, 0.5 * h], dtype=theta.dtype, device=theta.device) + output_grid = base_grid.view(1, oh * ow, 3).bmm(rescaled_theta) + return output_grid.view(1, oh, ow, 2) + + +def affine( + img: Tensor, matrix: List[float], interpolation: str = "nearest", fill: Optional[List[float]] = None +) -> Tensor: + _assert_grid_transform_inputs(img, matrix, interpolation, fill, ["nearest", "bilinear"]) + + dtype = img.dtype if torch.is_floating_point(img) else torch.float32 + theta = torch.tensor(matrix, dtype=dtype, device=img.device).reshape(1, 2, 3) + shape = img.shape + # grid will be generated on the same device as theta and img + grid = _gen_affine_grid(theta, w=shape[-1], h=shape[-2], ow=shape[-1], oh=shape[-2]) + return _apply_grid_transform(img, grid, interpolation, fill=fill) + + +def _compute_output_size(matrix: List[float], w: int, h: int) -> Tuple[int, int]: + + # Inspired of PIL implementation: + # https://github.com/python-pillow/Pillow/blob/11de3318867e4398057373ee9f12dcb33db7335c/src/PIL/Image.py#L2054 + + # pts are Top-Left, Top-Right, Bottom-Left, Bottom-Right points. + pts = torch.tensor([ + [-0.5 * w, -0.5 * h, 1.0], + [-0.5 * w, 0.5 * h, 1.0], + [0.5 * w, 0.5 * h, 1.0], + [0.5 * w, -0.5 * h, 1.0], + ]) + theta = torch.tensor(matrix, dtype=torch.float).reshape(1, 2, 3) + new_pts = pts.view(1, 4, 3).bmm(theta.transpose(1, 2)).view(4, 2) + min_vals, _ = new_pts.min(dim=0) + max_vals, _ = new_pts.max(dim=0) + + # Truncate precision to 1e-4 to avoid ceil of Xe-15 to 1.0 + tol = 1e-4 + cmax = torch.ceil((max_vals / tol).trunc_() * tol) + cmin = torch.floor((min_vals / tol).trunc_() * tol) + size = cmax - cmin + return int(size[0]), int(size[1]) + + +def rotate( + img: Tensor, matrix: List[float], interpolation: str = "nearest", + expand: bool = False, fill: Optional[List[float]] = None +) -> Tensor: + _assert_grid_transform_inputs(img, matrix, interpolation, fill, ["nearest", "bilinear"]) + w, h = img.shape[-1], img.shape[-2] + ow, oh = _compute_output_size(matrix, w, h) if expand else (w, h) + dtype = img.dtype if torch.is_floating_point(img) else torch.float32 + theta = torch.tensor(matrix, dtype=dtype, device=img.device).reshape(1, 2, 3) + # grid will be generated on the same device as theta and img + grid = _gen_affine_grid(theta, w=w, h=h, ow=ow, oh=oh) + + return _apply_grid_transform(img, grid, interpolation, fill=fill) + + +def _perspective_grid(coeffs: List[float], ow: int, oh: int, dtype: torch.dtype, device: torch.device): + # https://github.com/python-pillow/Pillow/blob/4634eafe3c695a014267eefdce830b4a825beed7/ + # src/libImaging/Geometry.c#L394 + + # + # x_out = (coeffs[0] * x + coeffs[1] * y + coeffs[2]) / (coeffs[6] * x + coeffs[7] * y + 1) + # y_out = (coeffs[3] * x + coeffs[4] * y + coeffs[5]) / (coeffs[6] * x + coeffs[7] * y + 1) + # + theta1 = torch.tensor([[ + [coeffs[0], coeffs[1], coeffs[2]], + [coeffs[3], coeffs[4], coeffs[5]] + ]], dtype=dtype, device=device) + theta2 = torch.tensor([[ + [coeffs[6], coeffs[7], 1.0], + [coeffs[6], coeffs[7], 1.0] + ]], dtype=dtype, device=device) + + d = 0.5 + base_grid = torch.empty(1, oh, ow, 3, dtype=dtype, device=device) + x_grid = torch.linspace(d, ow * 1.0 + d - 1.0, steps=ow, device=device) + base_grid[..., 0].copy_(x_grid) + y_grid = torch.linspace(d, oh * 1.0 + d - 1.0, steps=oh, device=device).unsqueeze_(-1) + base_grid[..., 1].copy_(y_grid) + base_grid[..., 2].fill_(1) + + rescaled_theta1 = theta1.transpose(1, 2) / torch.tensor([0.5 * ow, 0.5 * oh], dtype=dtype, device=device) + output_grid1 = base_grid.view(1, oh * ow, 3).bmm(rescaled_theta1) + output_grid2 = base_grid.view(1, oh * ow, 3).bmm(theta2.transpose(1, 2)) + + output_grid = output_grid1 / output_grid2 - 1.0 + return output_grid.view(1, oh, ow, 2) + + +def perspective( + img: Tensor, perspective_coeffs: List[float], interpolation: str = "bilinear", fill: Optional[List[float]] = None +) -> Tensor: + if not (isinstance(img, torch.Tensor)): + raise TypeError('Input img should be Tensor.') + + _assert_image_tensor(img) + + _assert_grid_transform_inputs( + img, + matrix=None, + interpolation=interpolation, + fill=fill, + supported_interpolation_modes=["nearest", "bilinear"], + coeffs=perspective_coeffs + ) + + ow, oh = img.shape[-1], img.shape[-2] + dtype = img.dtype if torch.is_floating_point(img) else torch.float32 + grid = _perspective_grid(perspective_coeffs, ow=ow, oh=oh, dtype=dtype, device=img.device) + return _apply_grid_transform(img, grid, interpolation, fill=fill) + + +def _get_gaussian_kernel1d(kernel_size: int, sigma: float) -> Tensor: + ksize_half = (kernel_size - 1) * 0.5 + + x = torch.linspace(-ksize_half, ksize_half, steps=kernel_size) + pdf = torch.exp(-0.5 * (x / sigma).pow(2)) + kernel1d = pdf / pdf.sum() + + return kernel1d + + +def _get_gaussian_kernel2d( + kernel_size: List[int], sigma: List[float], dtype: torch.dtype, device: torch.device +) -> Tensor: + kernel1d_x = _get_gaussian_kernel1d(kernel_size[0], sigma[0]).to(device, dtype=dtype) + kernel1d_y = _get_gaussian_kernel1d(kernel_size[1], sigma[1]).to(device, dtype=dtype) + kernel2d = torch.mm(kernel1d_y[:, None], kernel1d_x[None, :]) + return kernel2d + + +def gaussian_blur(img: Tensor, kernel_size: List[int], sigma: List[float]) -> Tensor: + if not (isinstance(img, torch.Tensor)): + raise TypeError('img should be Tensor. Got {}'.format(type(img))) + + _assert_image_tensor(img) + + dtype = img.dtype if torch.is_floating_point(img) else torch.float32 + kernel = _get_gaussian_kernel2d(kernel_size, sigma, dtype=dtype, device=img.device) + kernel = kernel.expand(img.shape[-3], 1, kernel.shape[0], kernel.shape[1]) + + img, need_cast, need_squeeze, out_dtype = _cast_squeeze_in(img, [kernel.dtype, ]) + + # padding = (left, right, top, bottom) + padding = [kernel_size[0] // 2, kernel_size[0] // 2, kernel_size[1] // 2, kernel_size[1] // 2] + img = torch_pad(img, padding, mode="reflect") + img = conv2d(img, kernel, groups=img.shape[-3]) + + img = _cast_squeeze_out(img, need_cast, need_squeeze, out_dtype) + return img + + +def invert(img: Tensor) -> Tensor: + + _assert_image_tensor(img) + + if img.ndim < 3: + raise TypeError("Input image tensor should have at least 3 dimensions, but found {}".format(img.ndim)) + + _assert_channels(img, [1, 3]) + + bound = torch.tensor(1 if img.is_floating_point() else 255, dtype=img.dtype, device=img.device) + return bound - img + + +def posterize(img: Tensor, bits: int) -> Tensor: + + _assert_image_tensor(img) + + if img.ndim < 3: + raise TypeError("Input image tensor should have at least 3 dimensions, but found {}".format(img.ndim)) + if img.dtype != torch.uint8: + raise TypeError("Only torch.uint8 image tensors are supported, but found {}".format(img.dtype)) + + _assert_channels(img, [1, 3]) + mask = -int(2**(8 - bits)) # JIT-friendly for: ~(2 ** (8 - bits) - 1) + return img & mask + + +def solarize(img: Tensor, threshold: float) -> Tensor: + + _assert_image_tensor(img) + + if img.ndim < 3: + raise TypeError("Input image tensor should have at least 3 dimensions, but found {}".format(img.ndim)) + + _assert_channels(img, [1, 3]) + + inverted_img = invert(img) + return torch.where(img >= threshold, inverted_img, img) + + +def _blurred_degenerate_image(img: Tensor) -> Tensor: + dtype = img.dtype if torch.is_floating_point(img) else torch.float32 + + kernel = torch.ones((3, 3), dtype=dtype, device=img.device) + kernel[1, 1] = 5.0 + kernel /= kernel.sum() + kernel = kernel.expand(img.shape[-3], 1, kernel.shape[0], kernel.shape[1]) + + result_tmp, need_cast, need_squeeze, out_dtype = _cast_squeeze_in(img, [kernel.dtype, ]) + result_tmp = conv2d(result_tmp, kernel, groups=result_tmp.shape[-3]) + result_tmp = _cast_squeeze_out(result_tmp, need_cast, need_squeeze, out_dtype) + + result = img.clone() + result[..., 1:-1, 1:-1] = result_tmp + + return result + + +def adjust_sharpness(img: Tensor, sharpness_factor: float) -> Tensor: + if sharpness_factor < 0: + raise ValueError('sharpness_factor ({}) is not non-negative.'.format(sharpness_factor)) + + _assert_image_tensor(img) + + _assert_channels(img, [1, 3]) + + if img.size(-1) <= 2 or img.size(-2) <= 2: + return img + + return _blend(img, _blurred_degenerate_image(img), sharpness_factor) + + +def autocontrast(img: Tensor) -> Tensor: + + _assert_image_tensor(img) + + if img.ndim < 3: + raise TypeError("Input image tensor should have at least 3 dimensions, but found {}".format(img.ndim)) + + _assert_channels(img, [1, 3]) + + bound = 1.0 if img.is_floating_point() else 255.0 + dtype = img.dtype if torch.is_floating_point(img) else torch.float32 + + minimum = img.amin(dim=(-2, -1), keepdim=True).to(dtype) + maximum = img.amax(dim=(-2, -1), keepdim=True).to(dtype) + eq_idxs = torch.where(minimum == maximum)[0] + minimum[eq_idxs] = 0 + maximum[eq_idxs] = bound + scale = bound / (maximum - minimum) + + return ((img - minimum) * scale).clamp(0, bound).to(img.dtype) + + +def _scale_channel(img_chan): + hist = torch.histc(img_chan.to(torch.float32), bins=256, min=0, max=255) + + nonzero_hist = hist[hist != 0] + step = nonzero_hist[:-1].sum() // 255 + if step == 0: + return img_chan + + lut = (torch.cumsum(hist, 0) + (step // 2)) // step + lut = torch.nn.functional.pad(lut, [1, 0])[:-1].clamp(0, 255) + + return lut[img_chan.to(torch.int64)].to(torch.uint8) + + +def _equalize_single_image(img: Tensor) -> Tensor: + return torch.stack([_scale_channel(img[c]) for c in range(img.size(0))]) + + +def equalize(img: Tensor) -> Tensor: + + _assert_image_tensor(img) + + if not (3 <= img.ndim <= 4): + raise TypeError("Input image tensor should have 3 or 4 dimensions, but found {}".format(img.ndim)) + if img.dtype != torch.uint8: + raise TypeError("Only torch.uint8 image tensors are supported, but found {}".format(img.dtype)) + + _assert_channels(img, [1, 3]) + + if img.ndim == 3: + return _equalize_single_image(img) + + return torch.stack([_equalize_single_image(x) for x in img]) diff --git a/cv/detection/fasterrcnn/pytorch/dataloaders/transforms_det.py b/cv/detection/fasterrcnn/pytorch/dataloaders/transforms_det.py index 54aae0513..3df4d47f0 100644 --- a/cv/detection/fasterrcnn/pytorch/dataloaders/transforms_det.py +++ b/cv/detection/fasterrcnn/pytorch/dataloaders/transforms_det.py @@ -1,4 +1,5 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. @@ -7,8 +8,8 @@ from typing import List, Tuple, Dict, Optional import torch from torch import nn, Tensor import torchvision -from torchvision.transforms import functional as F from torchvision.transforms import transforms as T +from . import functional as F def _flip_coco_person_keypoints(kps, width): @@ -37,7 +38,7 @@ class RandomHorizontalFlip(T.RandomHorizontalFlip): if torch.rand(1) < self.p: image = F.hflip(image) if target is not None: - width, _ = F.get_image_size(image) + width, _ = F._get_image_size(image) target["boxes"][:, [0, 2]] = width - target["boxes"][:, [2, 0]] if "masks" in target: target["masks"] = target["masks"].flip(-1) @@ -80,7 +81,7 @@ class RandomIoUCrop(nn.Module): elif image.ndimension() == 2: image = image.unsqueeze(0) - orig_w, orig_h = F.get_image_size(image) + orig_w, orig_h = F._get_image_size(image) while True: # sample an option @@ -161,7 +162,7 @@ class RandomZoomOut(nn.Module): if torch.rand(1) < self.p: return image, target - orig_w, orig_h = F.get_image_size(image) + orig_w, orig_h = F._get_image_size(image) r = self.side_range[0] + torch.rand(1) * (self.side_range[1] - self.side_range[0]) canvas_width = int(orig_w * r) @@ -230,7 +231,7 @@ class RandomPhotometricDistort(nn.Module): image = self._contrast(image) if r[6] < self.p: - channels = F.get_image_num_channels(image) + channels = F._get_image_num_channels(image) permutation = torch.randperm(channels) is_pil = F._is_pil_image(image) diff --git a/cv/detection/fasterrcnn/pytorch/requirements.txt b/cv/detection/fasterrcnn/pytorch/requirements.txt new file mode 100644 index 000000000..7321845b9 --- /dev/null +++ b/cv/detection/fasterrcnn/pytorch/requirements.txt @@ -0,0 +1 @@ +numpy>=1.23.5 \ No newline at end of file diff --git a/cv/detection/fasterrcnn/pytorch/start_scripts/train_fasterrcnn_resnet50_amp_dist_torch.sh b/cv/detection/fasterrcnn/pytorch/start_scripts/train_fasterrcnn_resnet50_amp_dist_torch.sh index 15ce8d393..00566f825 100644 --- a/cv/detection/fasterrcnn/pytorch/start_scripts/train_fasterrcnn_resnet50_amp_dist_torch.sh +++ b/cv/detection/fasterrcnn/pytorch/start_scripts/train_fasterrcnn_resnet50_amp_dist_torch.sh @@ -1,24 +1,24 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +#!/bin/bash +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. # All Rights Reserved. # -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. source get_num_devices.sh export DRT_MEMCPYUSEKERNEL=20000000000 CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 python3 -m torch.distributed.launch --nproc_per_node=$IX_NUM_CUDA_VISIBLE_DEVICES --use_env \ ../train.py \ ---data-path /home/datasets/cv/coco \ ---dataset coco \ +--data-path /home/datasets/cv/VOC2012_sample \ --amp \ --wd 0.000001 \ --lr 0.001 \ diff --git a/cv/detection/fasterrcnn/pytorch/start_scripts/train_fasterrcnn_resnet50_amp_torch.sh b/cv/detection/fasterrcnn/pytorch/start_scripts/train_fasterrcnn_resnet50_amp_torch.sh index 8adb38e19..93790ccea 100644 --- a/cv/detection/fasterrcnn/pytorch/start_scripts/train_fasterrcnn_resnet50_amp_torch.sh +++ b/cv/detection/fasterrcnn/pytorch/start_scripts/train_fasterrcnn_resnet50_amp_torch.sh @@ -1,23 +1,23 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +#!/bin/bash +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. # All Rights Reserved. # -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. export PYTORCH_DISABLE_VEC_KERNEL=1 export PT_USE_CUDNN_BATCHNORM_SPATIAL_PERSISTENT=1 CUDA_VISIBLE_DEVICES=0 python3 ../train.py \ ---data-path /home/datasets/cv/coco \ ---dataset coco \ +--data-path /home/datasets/cv/VOC2012_sample \ --amp \ --lr 0.001 \ ---batch-size 4 \ +--batch-size 1 \ "$@" diff --git a/cv/detection/fasterrcnn/pytorch/start_scripts/train_fasterrcnn_resnet50_dist_torch.sh b/cv/detection/fasterrcnn/pytorch/start_scripts/train_fasterrcnn_resnet50_dist_torch.sh index 57131a89f..2d26c3cc1 100644 --- a/cv/detection/fasterrcnn/pytorch/start_scripts/train_fasterrcnn_resnet50_dist_torch.sh +++ b/cv/detection/fasterrcnn/pytorch/start_scripts/train_fasterrcnn_resnet50_dist_torch.sh @@ -1,24 +1,24 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +#!/bin/bash +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. # All Rights Reserved. # -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. source get_num_devices.sh export DRT_MEMCPYUSEKERNEL=20000000000 CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 python3 -m torch.distributed.launch --nproc_per_node=$IX_NUM_CUDA_VISIBLE_DEVICES --use_env \ ../train.py \ ---data-path /home/datasets/cv/coco \ ---dataset coco \ +--data-path /home/datasets/cv/VOC2012_sample \ --wd 0.000001 \ --lr 0.001 \ --batch-size 4 \ diff --git a/cv/detection/fasterrcnn/pytorch/start_scripts/train_fasterrcnn_resnet50_torch.sh b/cv/detection/fasterrcnn/pytorch/start_scripts/train_fasterrcnn_resnet50_torch.sh index 3d30264f3..37405b9db 100644 --- a/cv/detection/fasterrcnn/pytorch/start_scripts/train_fasterrcnn_resnet50_torch.sh +++ b/cv/detection/fasterrcnn/pytorch/start_scripts/train_fasterrcnn_resnet50_torch.sh @@ -1,20 +1,21 @@ -# Copyright (c) 2022, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +#!/bin/bash +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. # All Rights Reserved. # -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + CUDA_VISIBLE_DEVICES=0 python3 ../train.py \ ---data-path /home/datasets/cv/coco \ ---dataset coco \ +--data-path /home/datasets/cv/VOC2012_sample \ --lr 0.001 \ --batch-size 4 \ "$@" diff --git a/cv/detection/fasterrcnn/pytorch/train.py b/cv/detection/fasterrcnn/pytorch/train.py index 443a1b697..ff4e99813 100644 --- a/cv/detection/fasterrcnn/pytorch/train.py +++ b/cv/detection/fasterrcnn/pytorch/train.py @@ -1,4 +1,5 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. r"""PyTorch Detection Training. @@ -37,13 +38,30 @@ from model import create_model from model.generalized_rcnn_transform import GeneralizedRCNNTransform from torchvision.models.detection.backbone_utils import resnet_fpn_backbone, _validate_trainable_layers from torchvision.ops.feature_pyramid_network import LastLevelP6P7 +import ssl +ssl._create_default_https_context = ssl._create_unverified_context + +import torchvision.models.resnet +print("WARN: Using pretrained weights from torchvision-0.9.") +torchvision.models.resnet.model_urls = { + 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth', + 'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth', + 'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth', + 'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth', + 'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth', + 'resnext50_32x4d': 'https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth', + 'resnext101_32x8d': 'https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth', + 'wide_resnet50_2': 'https://download.pytorch.org/models/wide_resnet50_2-95faca4d.pth', + 'wide_resnet101_2': 'https://download.pytorch.org/models/wide_resnet101_2-32ee1156.pth', +} + def get_args_parser(add_help=True): import argparse parser = argparse.ArgumentParser(description='PyTorch Detection Training', add_help=add_help) parser.add_argument('--data-path', default='/datasets01/COCO/022719/', help='dataset') - parser.add_argument('--dataset', default='coco', help='dataset') + parser.add_argument('--dataset', default='voc', help='dataset') parser.add_argument('--device', default='cuda', help='device') parser.add_argument('-b', '--batch-size', default=2, type=int, help='images per gpu, the total batch size is $NGPU x batch_size') @@ -100,7 +118,7 @@ def get_args_parser(add_help=True): ) # distributed training parameters - parser.add_argument('--local_rank', default=-1, type=int, + parser.add_argument('--local_rank', '--local-rank', default=-1, type=int, help='Local rank') parser.add_argument('--world-size', default=1, type=int, help='number of distributed processes') @@ -172,7 +190,7 @@ def main(args): image_std = [0.229, 0.224, 0.225] model.transform = GeneralizedRCNNTransform(800, 1333, image_mean, image_std, fixed_size=(800, 800)) trainable_backbone_layers = _validate_trainable_layers(True, None, 5, 5) - model.bachbone = resnet_fpn_backbone('resnet50', True, norm_layer=torch.nn.BatchNorm2d, + model.backbone = resnet_fpn_backbone('resnet50', True, norm_layer=torch.nn.BatchNorm2d, returned_layers=[2, 3, 4], extra_blocks=LastLevelP6P7(256, 256), trainable_layers=trainable_backbone_layers) @@ -247,4 +265,9 @@ def main(args): if __name__ == "__main__": args = get_args_parser().parse_args() + try: + from dltest import show_training_arguments + show_training_arguments(args) + except: + pass main(args) diff --git a/cv/detection/fasterrcnn/pytorch/utils/__init__.py b/cv/detection/fasterrcnn/pytorch/utils/__init__.py index 19764a9fb..80a4f928b 100644 --- a/cv/detection/fasterrcnn/pytorch/utils/__init__.py +++ b/cv/detection/fasterrcnn/pytorch/utils/__init__.py @@ -5,6 +5,7 @@ import numpy as np from .dist import * from .metric_logger import * from .smooth_value import * +from .misc import * def collate_fn(batch): diff --git a/cv/detection/fasterrcnn/pytorch/utils/dist.py b/cv/detection/fasterrcnn/pytorch/utils/dist.py index 40bf6ba18..dcd2330d1 100644 --- a/cv/detection/fasterrcnn/pytorch/utils/dist.py +++ b/cv/detection/fasterrcnn/pytorch/utils/dist.py @@ -1,4 +1,5 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. @@ -56,6 +57,25 @@ def save_on_master(*args, **kwargs): torch.save(*args, **kwargs) +def get_dist_backend(args=None): + DIST_BACKEND_ENV = "PT_DIST_BACKEND" + if DIST_BACKEND_ENV in os.environ: + return os.environ[DIST_BACKEND_ENV] + + if args is None: + args = dict() + + backend_attr_name = "dist_backend" + + if hasattr(args, backend_attr_name): + return getattr(args, backend_attr_name) + + if backend_attr_name in args: + return args[backend_attr_name] + + return "nccl" + + def init_distributed_mode(args): if 'RANK' in os.environ and 'WORLD_SIZE' in os.environ: args.rank = int(os.environ["RANK"]) @@ -72,12 +92,9 @@ def init_distributed_mode(args): args.distributed = True torch.cuda.set_device(args.gpu) - if hasattr(args, "dist_backend"): - dist_backend = args.dist_backend - else: - dist_backend = "nccl" - print('| distributed init (rank {}): {}'.format( - args.rank, args.dist_url), flush=True) + dist_backend = get_dist_backend(args) + print('| distributed init (rank {}, backend {}): {}'.format( + args.rank, dist_backend, args.dist_url), flush=True) torch.distributed.init_process_group(backend=dist_backend, init_method=args.dist_url, world_size=args.world_size, rank=args.rank) torch.distributed.barrier() diff --git a/cv/detection/fasterrcnn/pytorch/utils/misc.py b/cv/detection/fasterrcnn/pytorch/utils/misc.py new file mode 100644 index 000000000..c0b35a0c0 --- /dev/null +++ b/cv/detection/fasterrcnn/pytorch/utils/misc.py @@ -0,0 +1,10 @@ +import os +import errno + + +def mkdir(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise diff --git a/cv/detection/fasterrcnn/pytorch/utils/smooth_value.py b/cv/detection/fasterrcnn/pytorch/utils/smooth_value.py index c90947b3d..bfe9279f4 100644 --- a/cv/detection/fasterrcnn/pytorch/utils/smooth_value.py +++ b/cv/detection/fasterrcnn/pytorch/utils/smooth_value.py @@ -1,4 +1,5 @@ -# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) 2022-2024, Shanghai Iluvatar CoreX Semiconductor Co., Ltd. +# All Rights Reserved. # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. @@ -38,7 +39,7 @@ class SmoothedValue(object): """ if not is_dist_avail_and_initialized(): return - t = torch.tensor([self.count, self.total], dtype=torch.float64, device='cuda') + t = torch.tensor([self.count, self.total], dtype=torch.float32, device='cuda') dist.barrier() dist.all_reduce(t) t = t.tolist() @@ -73,4 +74,4 @@ class SmoothedValue(object): avg=self.avg, global_avg=self.global_avg, max=self.max, - value=self.value) \ No newline at end of file + value=self.value) diff --git a/cv/detection/maskrcnn/pytorch/ABSTRACTIONS.md b/cv/detection/maskrcnn/pytorch/ABSTRACTIONS.md deleted file mode 100644 index cdb3c4287..000000000 --- a/cv/detection/maskrcnn/pytorch/ABSTRACTIONS.md +++ /dev/null @@ -1,65 +0,0 @@ -## Abstractions -The main abstractions introduced by `maskrcnn_benchmark` that are useful to -have in mind are the following: - -### ImageList -In PyTorch, the first dimension of the input to the network generally represents -the batch dimension, and thus all elements of the same batch have the same -height / width. -In order to support images with different sizes and aspect ratios in the same -batch, we created the `ImageList` class, which holds internally a batch of -images (os possibly different sizes). The images are padded with zeros such that -they have the same final size and batched over the first dimension. The original -sizes of the images before padding are stored in the `image_sizes` attribute, -and the batched tensor in `tensors`. -We provide a convenience function `to_image_list` that accepts a few different -input types, including a list of tensors, and returns an `ImageList` object. - -```python -from maskrcnn_benchmark.structures.image_list import to_image_list - -images = [torch.rand(3, 100, 200), torch.rand(3, 150, 170)] -batched_images = to_image_list(images) - -# it is also possible to make the final batched image be a multiple of a number -batched_images_32 = to_image_list(images, size_divisible=32) -``` - -### BoxList -The `BoxList` class holds a set of bounding boxes (represented as a `Nx4` tensor) for -a specific image, as well as the size of the image as a `(width, height)` tuple. -It also contains a set of methods that allow to perform geometric -transformations to the bounding boxes (such as cropping, scaling and flipping). -The class accepts bounding boxes from two different input formats: -- `xyxy`, where each box is encoded as a `x1`, `y1`, `x2` and `y2` coordinates, and -- `xywh`, where each box is encoded as `x1`, `y1`, `w` and `h`. - -Additionally, each `BoxList` instance can also hold arbitrary additional information -for each bounding box, such as labels, visibility, probability scores etc. - -Here is an example on how to create a `BoxList` from a list of coordinates: -```python -from maskrcnn_benchmark.structures.bounding_box import BoxList, FLIP_LEFT_RIGHT - -width = 100 -height = 200 -boxes = [ - [0, 10, 50, 50], - [50, 20, 90, 60], - [10, 10, 50, 50] -] -# create a BoxList with 3 boxes -bbox = BoxList(boxes, image_size=(width, height), mode='xyxy') - -# perform some box transformations, has similar API as PIL.Image -bbox_scaled = bbox.resize((width * 2, height * 3)) -bbox_flipped = bbox.transpose(FLIP_LEFT_RIGHT) - -# add labels for each bbox -labels = torch.tensor([0, 10, 1]) -bbox.add_field('labels', labels) - -# bbox also support a few operations, like indexing -# here, selects boxes 0 and 2 -bbox_subset = bbox[[0, 2]] -``` diff --git a/cv/detection/maskrcnn/pytorch/CODE_OF_CONDUCT.md b/cv/detection/maskrcnn/pytorch/CODE_OF_CONDUCT.md deleted file mode 100644 index 0f7ad8bfc..000000000 --- a/cv/detection/maskrcnn/pytorch/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,5 +0,0 @@ -# Code of Conduct - -Facebook has adopted a Code of Conduct that we expect project participants to adhere to. -Please read the [full text](https://code.fb.com/codeofconduct/) -so that you can understand what actions will and will not be tolerated. diff --git a/cv/detection/maskrcnn/pytorch/CONTRIBUTING.md b/cv/detection/maskrcnn/pytorch/CONTRIBUTING.md deleted file mode 100644 index fc14cd3c7..000000000 --- a/cv/detection/maskrcnn/pytorch/CONTRIBUTING.md +++ /dev/null @@ -1,39 +0,0 @@ -# Contributing to Mask-RCNN Benchmark -We want to make contributing to this project as easy and transparent as -possible. - -## Our Development Process -Minor changes and improvements will be released on an ongoing basis. Larger changes (e.g., changesets implementing a new paper) will be released on a more periodic basis. - -## Pull Requests -We actively welcome your pull requests. - -1. Fork the repo and create your branch from `master`. -2. If you've added code that should be tested, add tests. -3. If you've changed APIs, update the documentation. -4. Ensure the test suite passes. -5. Make sure your code lints. -6. If you haven't already, complete the Contributor License Agreement ("CLA"). - -## Contributor License Agreement ("CLA") -In order to accept your pull request, we need you to submit a CLA. You only need -to do this once to work on any of Facebook's open source projects. - -Complete your CLA here: - -## Issues -We use GitHub issues to track public bugs. Please ensure your description is -clear and has sufficient instructions to be able to reproduce the issue. - -Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe -disclosure of security bugs. In those cases, please go through the process -outlined on that page and do not file a public issue. - -## Coding Style -* 4 spaces for indentation rather than tabs -* 80 character line length -* PEP8 formatting following [Black](https://black.readthedocs.io/en/stable/) - -## License -By contributing to Mask-RCNN Benchmark, you agree that your contributions will be licensed -under the LICENSE file in the root directory of this source tree. diff --git a/cv/detection/maskrcnn/pytorch/Dockerfile b/cv/detection/maskrcnn/pytorch/Dockerfile deleted file mode 100644 index 66bd4406c..000000000 --- a/cv/detection/maskrcnn/pytorch/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -ARG FROM_IMAGE_NAME=gitlab-master.nvidia.com:5005/dl/dgx/pytorch:master-py3-devel -FROM ${FROM_IMAGE_NAME} - -# Install Python dependencies -RUN pip install --upgrade --no-cache-dir pip \ - && pip install --no-cache-dir \ - mlperf-compliance==0.0.10 \ - opencv-python==3.4.1.15 \ - yacs - -WORKDIR /opt -RUN git clone -b v0.1 https://github.com/NVIDIA/cocoapi.git \ - && cd cocoapi/PythonAPI \ - && pip install -e . - -# Copy detectron code and build -WORKDIR /workspace/object_detection -COPY . . -RUN pip install -e . - -ENV OMP_NUM_THREADS=1 diff --git a/cv/detection/maskrcnn/pytorch/INSTALL.md b/cv/detection/maskrcnn/pytorch/INSTALL.md deleted file mode 100644 index c7dab73f2..000000000 --- a/cv/detection/maskrcnn/pytorch/INSTALL.md +++ /dev/null @@ -1,72 +0,0 @@ -## Installation - -### Requirements: -- PyTorch 1.0 from a nightly release. Installation instructions can be found in https://pytorch.org/get-started/locally/ -- torchvision from master -- cocoapi -- yacs -- matplotlib -- GCC >= 4.9 -- (optional) OpenCV for the webcam demo - - -### Option 1: Step-by-step installation - -```bash -# first, make sure that your conda is setup properly with the right environment -# for that, check that `which conda`, `which pip` and `which python` points to the -# right path. From a clean conda env, this is what you need to do - -conda create --name maskrcnn_benchmark -source activate maskrcnn_benchmark - -# this installs the right pip and dependencies for the fresh python -conda install ipython - -# maskrcnn_benchmark and coco api dependencies -pip install ninja yacs cython matplotlib - -# follow PyTorch installation in https://pytorch.org/get-started/locally/ -# we give the instructions for CUDA 9.0 -conda install pytorch-nightly -c pytorch - -# install torchvision -cd ~/github -git clone https://github.com/pytorch/vision.git -cd vision -python setup.py install - -# install pycocotools -cd ~/github -git clone https://github.com/cocodataset/cocoapi.git -cd cocoapi/PythonAPI -python setup.py build_ext install - -# install PyTorch Detection -cd ~/github -git clone https://github.com/facebookresearch/maskrcnn-benchmark.git -cd maskrcnn-benchmark -# the following will install the lib with -# symbolic links, so that you can modify -# the files if you want and won't need to -# re-build it -python setup.py build develop - -# or if you are on macOS -# MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ python setup.py build develop -``` - -### Option 2: Docker Image (Requires CUDA, Linux only) - -Build image with defaults (`CUDA=9.0`, `CUDNN=7`): - - nvidia-docker build -t maskrcnn-benchmark docker/ - -Build image with other CUDA and CUDNN versions: - - nvidia-docker build -t maskrcnn-benchmark --build-arg CUDA=9.2 --build-arg CUDNN=7 docker/ - -Build and run image with built-in jupyter notebook(note that the password is used to log in jupyter notebook): - - nvidia-docker build -t maskrcnn-benchmark-jupyter docker/docker-jupyter/ - nvidia-docker run -td -p 8888:8888 -e PASSWORD= -v : maskrcnn-benchmark-jupyter \ No newline at end of file diff --git a/cv/detection/maskrcnn/pytorch/LICENSE b/cv/detection/maskrcnn/pytorch/LICENSE deleted file mode 100644 index 9cb17e2a1..000000000 --- a/cv/detection/maskrcnn/pytorch/LICENSE +++ /dev/null @@ -1,37 +0,0 @@ -Apache License, Version 2.0 - -Copyright 2021 NVIDIA CORPORATION. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -MIT License - -Copyright (c) 2018 Facebook - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/cv/detection/maskrcnn/pytorch/MODEL_ZOO.md b/cv/detection/maskrcnn/pytorch/MODEL_ZOO.md deleted file mode 100644 index a88546734..000000000 --- a/cv/detection/maskrcnn/pytorch/MODEL_ZOO.md +++ /dev/null @@ -1,89 +0,0 @@ -## Model Zoo and Baselines - -### Hardware -- 8 NVIDIA V100 GPUs - -### Software -- PyTorch version: 1.0.0a0+dd2c487 -- CUDA 9.2 -- CUDNN 7.1 -- NCCL 2.2.13-1 - -### End-to-end Faster and Mask R-CNN baselines - -All the baselines were trained using the exact same experimental setup as in Detectron. -We initialize the detection models with ImageNet weights from Caffe2, the same as used by Detectron. - -The pre-trained models are available in the link in the model id. - -backbone | type | lr sched | im / gpu | train mem(GB) | train time (s/iter) | total train time(hr) | inference time(s/im) | box AP | mask AP | model id --- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- -R-50-C4 | Fast | 1x | 1 | 5.8 | 0.4036 | 20.2 | 0.17130 | 34.8 | - | [6358800](https://download.pytorch.org/models/maskrcnn/e2e_faster_rcnn_R_50_C4_1x.pth) -R-50-FPN | Fast | 1x | 2 | 4.4 | 0.3530 | 8.8 | 0.12580 | 36.8 | - | [6358793](https://download.pytorch.org/models/maskrcnn/e2e_faster_rcnn_R_50_FPN_1x.pth) -R-101-FPN | Fast | 1x | 2 | 7.1 | 0.4591 | 11.5 | 0.143149 | 39.1 | - | [6358804](https://download.pytorch.org/models/maskrcnn/e2e_faster_rcnn_R_101_FPN_1x.pth) -X-101-32x8d-FPN | Fast | 1x | 1 | 7.6 | 0.7007 | 35.0 | 0.209965 | 41.2 | - | [6358717](https://download.pytorch.org/models/maskrcnn/e2e_faster_rcnn_X_101_32x8d_FPN_1x.pth) -R-50-C4 | Mask | 1x | 1 | 5.8 | 0.4520 | 22.6 | 0.17796 + 0.028 | 35.6 | 31.5 | [6358801](https://download.pytorch.org/models/maskrcnn/e2e_mask_rcnn_R_50_C4_1x.pth) -R-50-FPN | Mask | 1x | 2 | 5.2 | 0.4536 | 11.3 | 0.12966 + 0.034 | 37.8 | 34.2 | [6358792](https://download.pytorch.org/models/maskrcnn/e2e_mask_rcnn_R_50_FPN_1x.pth) -R-101-FPN | Mask | 1x | 2 | 7.9 | 0.5665 | 14.2 | 0.15384 + 0.034 | 40.1 | 36.1 | [6358805](https://download.pytorch.org/models/maskrcnn/e2e_mask_rcnn_R_101_FPN_1x.pth) -X-101-32x8d-FPN | Mask | 1x | 1 | 7.8 | 0.7562 | 37.8 | 0.21739 + 0.034 | 42.2 | 37.8 | [6358718](https://download.pytorch.org/models/maskrcnn/e2e_mask_rcnn_X_101_32x8d_FPN_1x.pth) - -For person keypoint detection: - -backbone | type | lr sched | im / gpu | train mem(GB) | train time (s/iter) | total train time(hr) | inference time(s/im) | box AP | keypoint AP | model id --- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- -R-50-FPN | Keypoint | 1x | 2 | 5.7 | 0.3771 | 9.4 | 0.10941 | 53.7 | 64.3 | 9981060 - - - -## Comparison with Detectron and mmdetection - -In the following section, we compare our implementation with [Detectron](https://github.com/facebookresearch/Detectron) -and [mmdetection](https://github.com/open-mmlab/mmdetection). -The same remarks from [mmdetection](https://github.com/open-mmlab/mmdetection/blob/master/MODEL_ZOO.md#training-speed) -about different hardware applies here. - -### Training speed - -The numbers here are in seconds / iteration. The lower, the better. - -type | Detectron (P100) | mmdetection (V100) | maskrcnn_benchmark (V100) --- | -- | -- | -- -Faster R-CNN R-50 C4 | 0.566 | - | 0.4036 -Faster R-CNN R-50 FPN | 0.544 | 0.554 | 0.3530 -Faster R-CNN R-101 FPN | 0.647 | - | 0.4591 -Faster R-CNN X-101-32x8d FPN | 0.799 | - | 0.7007 -Mask R-CNN R-50 C4 | 0.620 | - | 0.4520 -Mask R-CNN R-50 FPN | 0.889 | 0.690 | 0.4536 -Mask R-CNN R-101 FPN | 1.008 | - | 0.5665 -Mask R-CNN X-101-32x8d FPN | 0.961 | - | 0.7562 - -### Training memory - -The lower, the better - -type | Detectron (P100) | mmdetection (V100) | maskrcnn_benchmark (V100) --- | -- | -- | -- -Faster R-CNN R-50 C4 | 6.3 | - | 5.8 -Faster R-CNN R-50 FPN | 7.2 | 4.9 | 4.4 -Faster R-CNN R-101 FPN | 8.9 | - | 7.1 -Faster R-CNN X-101-32x8d FPN | 7.0 | - | 7.6 -Mask R-CNN R-50 C4 | 6.6 | - | 5.8 -Mask R-CNN R-50 FPN | 8.6 | 5.9 | 5.2 -Mask R-CNN R-101 FPN | 10.2 | - | 7.9 -Mask R-CNN X-101-32x8d FPN | 7.7 | - | 7.8 - -### Accuracy - -The higher, the better - -type | Detectron (P100) | mmdetection (V100) | maskrcnn_benchmark (V100) --- | -- | -- | -- -Faster R-CNN R-50 C4 | 34.8 | - | 34.8 -Faster R-CNN R-50 FPN | 36.7 | 36.7 | 36.8 -Faster R-CNN R-101 FPN | 39.4 | - | 39.1 -Faster R-CNN X-101-32x8d FPN | 41.3 | - | 41.2 -Mask R-CNN R-50 C4 | 35.8 & 31.4 | - | 35.6 & 31.5 -Mask R-CNN R-50 FPN | 37.7 & 33.9 | 37.5 & 34.4 | 37.8 & 34.2 -Mask R-CNN R-101 FPN | 40.0 & 35.9 | - | 40.1 & 36.1 -Mask R-CNN X-101-32x8d FPN | 42.1 & 37.3 | - | 42.2 & 37.8 - diff --git a/cv/detection/maskrcnn/pytorch/README.md b/cv/detection/maskrcnn/pytorch/README.md index 5b7f70b21..08824d816 100644 --- a/cv/detection/maskrcnn/pytorch/README.md +++ b/cv/detection/maskrcnn/pytorch/README.md @@ -5,7 +5,13 @@ Nuclei segmentation is both an important and in some ways ideal task for modern computer vision methods, e.g. convolutional neural networks. While recent developments in theory and open-source software have made these tools easier to implement, expert knowledge is still required to choose the right model architecture and training setup. We compare two popular segmentation frameworks, U-Net and Mask-RCNN in the nuclei segmentation task and find that they have different strengths and failures. To get the best of both worlds, we develop an ensemble model to combine their predictions that can outperform both models by a significant margin and should be considered when aiming for best nuclei segmentation performance. ## Step 1: Installing packages -``` +```bash +# Install libGL +## CentOS +yum install -y mesa-libGL +## Ubuntu +apt install -y libgl1-mesa-dev + pip3 install -r requirements.txt ``` @@ -41,26 +47,14 @@ ln -s /path/to/coco2017 ./datasets/coco ## Step 3: Training +### Single Card +python train.py --data-path ./datasets/coco --dataset coco --model maskrcnn_resnet50_fpn --lr 0.001 --batch-size 4 -### Mask R-CNN on Coco using 8 cards: -``` -bash run_8cards.sh -``` -### Mask R-CNN on Coco using 4 cards: -``` -bash run_4cards.sh -``` -### Mask R-CNN on Coco using 1 cards: -``` -bash run_single.sh -``` - -## Results on BI-V100 +### AMP +python train.py --data-path ./datasets/coco --dataset coco --model maskrcnn_resnet50_fpn --lr 0.001 --batch-size 1 --amp -| GPUs | FP16 | FPS | mAP | -|------|-------|-----|------| -| 1x8 | False | 8.5 | 0.377 | - - -## Reference -https://github.com/mlcommons/training +### DDP +``` +python -m torch.distributed.launch --nproc_per_node=8 --use_env train.py\ + --data-path ./datasets/coco --dataset coco --model maskrcnn_resnet50_fpn --wd 0.000001 --lr 0.001 --batch-size 4 +``` \ No newline at end of file diff --git a/cv/detection/maskrcnn/pytorch/TROUBLESHOOTING.md b/cv/detection/maskrcnn/pytorch/TROUBLESHOOTING.md deleted file mode 100644 index 63a8b598b..000000000 --- a/cv/detection/maskrcnn/pytorch/TROUBLESHOOTING.md +++ /dev/null @@ -1,67 +0,0 @@ -# Troubleshooting - -Here is a compilation if common issues that you might face -while compiling / running this code: - -## Compilation errors when compiling the library -If you encounter build errors like the following: -``` -/usr/include/c++/6/type_traits:1558:8: note: provided for ‘template struct std::is_convertible’ - struct is_convertible - ^~~~~~~~~~~~~~ -/usr/include/c++/6/tuple:502:1: error: body of constexpr function ‘static constexpr bool std::_TC<, _Elements>::_NonNestedTuple() [with _SrcTuple = std::tuple&&; bool = true; _Elements = {at::Tensor, at::Tensor, at::Tensor, at::Tensor}]’ not a return-statement - } - ^ -error: command '/usr/local/cuda/bin/nvcc' failed with exit status 1 -``` -check your CUDA version and your `gcc` version. -``` -nvcc --version -gcc --version -``` -If you are using CUDA 9.0 and gcc 6.4.0, then refer to https://github.com/facebookresearch/maskrcnn-benchmark/issues/25, -which has a summary of the solution. Basically, CUDA 9.0 is not compatible with gcc 6.4.0. - -## ImportError: No module named maskrcnn_benchmark.config when running webcam.py - -This means that `maskrcnn-benchmark` has not been properly installed. -Refer to https://github.com/facebookresearch/maskrcnn-benchmark/issues/22 for a few possible issues. -Note that we now support Python 2 as well. - - -## ImportError: Undefined symbol: __cudaPopCallConfiguration error when import _C - -This probably means that the inconsistent version of NVCC compile and your conda CUDAToolKit package. This is firstly mentioned in https://github.com/facebookresearch/maskrcnn-benchmark/issues/45 . All you need to do is: - -``` -# Check the NVCC compile version(e.g.) -/usr/cuda-9.2/bin/nvcc --version -# Check the CUDAToolKit version(e.g.) -~/anaconda3/bin/conda list | grep cuda - -# If you need to update your CUDAToolKit -~/anaconda3/bin/conda install -c anaconda cudatoolkit==9.2 -``` - -Both of them should have the **same** version. For example, if NVCC==9.2 and CUDAToolKit==9.2, this will be fine while when NVCC==9.2 but CUDAToolKit==9, it fails. - - -## Segmentation fault (core dumped) when running the library -This probably means that you have compiled the library using GCC < 4.9, which is ABI incompatible with PyTorch. -Indeed, during installation, you probably saw a message like -``` -Your compiler (g++ 4.8) may be ABI-incompatible with PyTorch! -Please use a compiler that is ABI-compatible with GCC 4.9 and above. -See https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html. - -See https://gist.github.com/goldsborough/d466f43e8ffc948ff92de7486c5216d6 -for instructions on how to install GCC 4.9 or higher. -``` -Follow the instructions on https://gist.github.com/goldsborough/d466f43e8ffc948ff92de7486c5216d6 -to install GCC 4.9 or higher, and try recompiling `maskrcnn-benchmark` again, after cleaning the -`build` folder with -``` -rm -rf build -``` - - diff --git a/cv/detection/maskrcnn/pytorch/__init__.py b/cv/detection/maskrcnn/pytorch/__init__.py new file mode 100644 index 000000000..ebd9f2e52 --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/__init__.py @@ -0,0 +1,2 @@ + +__all__ = [k for k in globals().keys() if not k.startswith("_")] diff --git a/cv/detection/maskrcnn/pytorch/coco_eval.py b/cv/detection/maskrcnn/pytorch/coco_eval.py new file mode 100644 index 000000000..dd63e8fd1 --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/coco_eval.py @@ -0,0 +1,351 @@ +import json +import tempfile + +import numpy as np +import copy +import time +import torch + +from pycocotools.cocoeval import COCOeval +from pycocotools.coco import COCO +import pycocotools.mask as mask_util + +from collections import defaultdict + +import utils + + +class CocoEvaluator(object): + def __init__(self, coco_gt, iou_types): + assert isinstance(iou_types, (list, tuple)) + coco_gt = copy.deepcopy(coco_gt) + self.coco_gt = coco_gt + + self.iou_types = iou_types + self.coco_eval = {} + for iou_type in iou_types: + self.coco_eval[iou_type] = COCOeval(coco_gt, iouType=iou_type) + + self.img_ids = [] + self.eval_imgs = {k: [] for k in iou_types} + + def update(self, predictions): + img_ids = list(np.unique(list(predictions.keys()))) + self.img_ids.extend(img_ids) + + for iou_type in self.iou_types: + results = self.prepare(predictions, iou_type) + coco_dt = loadRes(self.coco_gt, results) if results else COCO() + coco_eval = self.coco_eval[iou_type] + + coco_eval.cocoDt = coco_dt + coco_eval.params.imgIds = list(img_ids) + img_ids, eval_imgs = evaluate(coco_eval) + + self.eval_imgs[iou_type].append(eval_imgs) + + def synchronize_between_processes(self): + for iou_type in self.iou_types: + self.eval_imgs[iou_type] = np.concatenate(self.eval_imgs[iou_type], 2) + create_common_coco_eval(self.coco_eval[iou_type], self.img_ids, self.eval_imgs[iou_type]) + + def accumulate(self): + for coco_eval in self.coco_eval.values(): + coco_eval.accumulate() + + def summarize(self): + for iou_type, coco_eval in self.coco_eval.items(): + print("IoU metric: {}".format(iou_type)) + coco_eval.summarize() + + def prepare(self, predictions, iou_type): + if iou_type == "bbox": + return self.prepare_for_coco_detection(predictions) + elif iou_type == "segm": + return self.prepare_for_coco_segmentation(predictions) + elif iou_type == "keypoints": + return self.prepare_for_coco_keypoint(predictions) + else: + raise ValueError("Unknown iou type {}".format(iou_type)) + + def prepare_for_coco_detection(self, predictions): + coco_results = [] + for original_id, prediction in predictions.items(): + if len(prediction) == 0: + continue + + boxes = prediction["boxes"] + boxes = convert_to_xywh(boxes).tolist() + scores = prediction["scores"].tolist() + labels = prediction["labels"].tolist() + + coco_results.extend( + [ + { + "image_id": original_id, + "category_id": labels[k], + "bbox": box, + "score": scores[k], + } + for k, box in enumerate(boxes) + ] + ) + return coco_results + + def prepare_for_coco_segmentation(self, predictions): + coco_results = [] + for original_id, prediction in predictions.items(): + if len(prediction) == 0: + continue + + scores = prediction["scores"] + labels = prediction["labels"] + masks = prediction["masks"] + + masks = masks > 0.5 + + scores = prediction["scores"].tolist() + labels = prediction["labels"].tolist() + + rles = [ + mask_util.encode(np.array(mask[0, :, :, np.newaxis], dtype=np.uint8, order="F"))[0] + for mask in masks + ] + for rle in rles: + rle["counts"] = rle["counts"].decode("utf-8") + + coco_results.extend( + [ + { + "image_id": original_id, + "category_id": labels[k], + "segmentation": rle, + "score": scores[k], + } + for k, rle in enumerate(rles) + ] + ) + return coco_results + + def prepare_for_coco_keypoint(self, predictions): + coco_results = [] + for original_id, prediction in predictions.items(): + if len(prediction) == 0: + continue + + boxes = prediction["boxes"] + boxes = convert_to_xywh(boxes).tolist() + scores = prediction["scores"].tolist() + labels = prediction["labels"].tolist() + keypoints = prediction["keypoints"] + keypoints = keypoints.flatten(start_dim=1).tolist() + + coco_results.extend( + [ + { + "image_id": original_id, + "category_id": labels[k], + 'keypoints': keypoint, + "score": scores[k], + } + for k, keypoint in enumerate(keypoints) + ] + ) + return coco_results + + +def convert_to_xywh(boxes): + xmin, ymin, xmax, ymax = boxes.unbind(1) + return torch.stack((xmin, ymin, xmax - xmin, ymax - ymin), dim=1) + + +def merge(img_ids, eval_imgs): + all_img_ids = utils.all_gather(img_ids) + all_eval_imgs = utils.all_gather(eval_imgs) + + merged_img_ids = [] + for p in all_img_ids: + merged_img_ids.extend(p) + + merged_eval_imgs = [] + for p in all_eval_imgs: + merged_eval_imgs.append(p) + + merged_img_ids = np.array(merged_img_ids) + merged_eval_imgs = np.concatenate(merged_eval_imgs, 2) + + # keep only unique (and in sorted order) images + merged_img_ids, idx = np.unique(merged_img_ids, return_index=True) + merged_eval_imgs = merged_eval_imgs[..., idx] + + return merged_img_ids, merged_eval_imgs + + +def create_common_coco_eval(coco_eval, img_ids, eval_imgs): + img_ids, eval_imgs = merge(img_ids, eval_imgs) + img_ids = list(img_ids) + eval_imgs = list(eval_imgs.flatten()) + + coco_eval.evalImgs = eval_imgs + coco_eval.params.imgIds = img_ids + coco_eval._paramsEval = copy.deepcopy(coco_eval.params) + + +################################################################# +# From pycocotools, just removed the prints and fixed +# a Python3 bug about unicode not defined +################################################################# + +# Ideally, pycocotools wouldn't have hard-coded prints +# so that we could avoid copy-pasting those two functions + +def createIndex(self): + # create index + # print('creating index...') + anns, cats, imgs = {}, {}, {} + imgToAnns, catToImgs = defaultdict(list), defaultdict(list) + if 'annotations' in self.dataset: + for ann in self.dataset['annotations']: + imgToAnns[ann['image_id']].append(ann) + anns[ann['id']] = ann + + if 'images' in self.dataset: + for img in self.dataset['images']: + imgs[img['id']] = img + + if 'categories' in self.dataset: + for cat in self.dataset['categories']: + cats[cat['id']] = cat + + if 'annotations' in self.dataset and 'categories' in self.dataset: + for ann in self.dataset['annotations']: + catToImgs[ann['category_id']].append(ann['image_id']) + + # print('index created!') + + # create class members + self.anns = anns + self.imgToAnns = imgToAnns + self.catToImgs = catToImgs + self.imgs = imgs + self.cats = cats + + +maskUtils = mask_util + + +def loadRes(self, resFile): + """ + Load result file and return a result api object. + Args: + self (obj): coco object with ground truth annotations + resFile (str): file name of result file + Returns: + res (obj): result api object + """ + res = COCO() + res.dataset['images'] = [img for img in self.dataset['images']] + + # print('Loading and preparing results...') + # tic = time.time() + if isinstance(resFile, str): + anns = json.load(open(resFile)) + elif type(resFile) == np.ndarray: + anns = self.loadNumpyAnnotations(resFile) + else: + anns = resFile + assert type(anns) == list, 'results in not an array of objects' + annsImgIds = [ann['image_id'] for ann in anns] + assert set(annsImgIds) == (set(annsImgIds) & set(self.getImgIds())), \ + 'Results do not correspond to current coco set' + if 'caption' in anns[0]: + imgIds = set([img['id'] for img in res.dataset['images']]) & set([ann['image_id'] for ann in anns]) + res.dataset['images'] = [img for img in res.dataset['images'] if img['id'] in imgIds] + for id, ann in enumerate(anns): + ann['id'] = id + 1 + elif 'bbox' in anns[0] and not anns[0]['bbox'] == []: + res.dataset['categories'] = copy.deepcopy(self.dataset['categories']) + for id, ann in enumerate(anns): + bb = ann['bbox'] + x1, x2, y1, y2 = [bb[0], bb[0] + bb[2], bb[1], bb[1] + bb[3]] + if 'segmentation' not in ann: + ann['segmentation'] = [[x1, y1, x1, y2, x2, y2, x2, y1]] + ann['area'] = bb[2] * bb[3] + ann['id'] = id + 1 + ann['iscrowd'] = 0 + elif 'segmentation' in anns[0]: + res.dataset['categories'] = copy.deepcopy(self.dataset['categories']) + for id, ann in enumerate(anns): + # now only support compressed RLE format as segmentation results + ann['area'] = maskUtils.area(ann['segmentation']) + if 'bbox' not in ann: + ann['bbox'] = maskUtils.toBbox(ann['segmentation']) + ann['id'] = id + 1 + ann['iscrowd'] = 0 + elif 'keypoints' in anns[0]: + res.dataset['categories'] = copy.deepcopy(self.dataset['categories']) + for id, ann in enumerate(anns): + s = ann['keypoints'] + x = s[0::3] + y = s[1::3] + x1, x2, y1, y2 = np.min(x), np.max(x), np.min(y), np.max(y) + ann['area'] = (x2 - x1) * (y2 - y1) + ann['id'] = id + 1 + ann['bbox'] = [x1, y1, x2 - x1, y2 - y1] + # print('DONE (t={:0.2f}s)'.format(time.time()- tic)) + + res.dataset['annotations'] = anns + createIndex(res) + return res + + +def evaluate(self): + ''' + Run per image evaluation on given images and store results (a list of dict) in self.evalImgs + :return: None + ''' + # tic = time.time() + # print('Running per image evaluation...') + p = self.params + # add backward compatibility if useSegm is specified in params + if p.useSegm is not None: + p.iouType = 'segm' if p.useSegm == 1 else 'bbox' + print('useSegm (deprecated) is not None. Running {} evaluation'.format(p.iouType)) + # print('Evaluate annotation type *{}*'.format(p.iouType)) + p.imgIds = list(np.unique(p.imgIds)) + if p.useCats: + p.catIds = list(np.unique(p.catIds)) + p.maxDets = sorted(p.maxDets) + self.params = p + + self._prepare() + # loop through images, area range, max detection number + catIds = p.catIds if p.useCats else [-1] + + if p.iouType == 'segm' or p.iouType == 'bbox': + computeIoU = self.computeIoU + elif p.iouType == 'keypoints': + computeIoU = self.computeOks + self.ious = { + (imgId, catId): computeIoU(imgId, catId) + for imgId in p.imgIds + for catId in catIds} + + evaluateImg = self.evaluateImg + maxDet = p.maxDets[-1] + evalImgs = [ + evaluateImg(imgId, catId, areaRng, maxDet) + for catId in catIds + for areaRng in p.areaRng + for imgId in p.imgIds + ] + # this is NOT in the pycocotools code, but could be done outside + evalImgs = np.asarray(evalImgs).reshape(len(catIds), len(p.areaRng), len(p.imgIds)) + self._paramsEval = copy.deepcopy(self.params) + # toc = time.time() + # print('DONE (t={:0.2f}s).'.format(toc-tic)) + return p.imgIds, evalImgs + +################################################################# +# end of straight copy from pycocotools, just removing the prints +################################################################# diff --git a/cv/detection/maskrcnn/pytorch/common_utils/__init__.py b/cv/detection/maskrcnn/pytorch/common_utils/__init__.py new file mode 100644 index 000000000..32e8c4f57 --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/common_utils/__init__.py @@ -0,0 +1,23 @@ +import random + +import numpy as np + +from .dist import * +from .metric_logger import * +from .misc import * +from .smooth_value import * + +def manual_seed(seed, deterministic=False): + random.seed(seed) + np.random.seed(seed) + os.environ['PYTHONHASHSEED'] = str(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + + if deterministic: + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + else: + torch.backends.cudnn.deterministic = False + torch.backends.cudnn.benchmark = True \ No newline at end of file diff --git a/cv/detection/maskrcnn/pytorch/common_utils/dist.py b/cv/detection/maskrcnn/pytorch/common_utils/dist.py new file mode 100644 index 000000000..ea56ca267 --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/common_utils/dist.py @@ -0,0 +1,144 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist + + + +def setup_for_distributed(is_master): + """ + This function disables printing when not in master process + """ + import builtins as __builtin__ + builtin_print = __builtin__.print + + def print(*args, **kwargs): + force = kwargs.pop('force', False) + if is_master or force: + builtin_print(*args, **kwargs) + + __builtin__.print = print + + +def is_dist_avail_and_initialized(): + if not dist.is_available(): + return False + if not dist.is_initialized(): + return False + return True + + +def get_world_size(): + if not is_dist_avail_and_initialized(): + return 1 + return dist.get_world_size() + + +def get_rank(): + if not is_dist_avail_and_initialized(): + return 0 + return dist.get_rank() + + +def is_main_process(): + return get_rank() == 0 + + +def save_on_master(*args, **kwargs): + if is_main_process(): + torch.save(*args, **kwargs) + + +def get_dist_backend(args=None): + DIST_BACKEND_ENV = "PT_DIST_BACKEND" + if DIST_BACKEND_ENV in os.environ: + return os.environ[DIST_BACKEND_ENV] + + if args is None: + args = dict() + + backend_attr_name = "dist_backend" + + if hasattr(args, backend_attr_name): + return getattr(args, backend_attr_name) + + if backend_attr_name in args: + return args[backend_attr_name] + + return "nccl" + + +def init_distributed_mode(args): + if 'RANK' in os.environ and 'WORLD_SIZE' in os.environ: + args.rank = int(os.environ["RANK"]) + args.world_size = int(os.environ['WORLD_SIZE']) + args.gpu = int(os.environ['LOCAL_RANK']) + elif 'SLURM_PROCID' in os.environ: + args.rank = int(os.environ['SLURM_PROCID']) + args.gpu = args.rank % torch.cuda.device_count() + else: + print('Not using distributed mode') + args.distributed = False + return + + args.distributed = True + + torch.cuda.set_device(args.gpu) + dist_backend = get_dist_backend(args) + print('| distributed init (rank {}): {}'.format( + args.rank, args.dist_url), flush=True) + torch.distributed.init_process_group(backend=dist_backend, init_method=args.dist_url, + world_size=args.world_size, rank=args.rank) + torch.distributed.barrier() + setup_for_distributed(args.rank == 0) + + +def all_gather(data): + """ + Run all_gather on arbitrary picklable data (not necessarily tensors) + Args: + data: any picklable object + Returns: + list[data]: list of data gathered from each rank + """ + world_size = get_world_size() + if world_size == 1: + return [data] + data_list = [None] * world_size + dist.all_gather_object(data_list, data) + return data_list + + +def reduce_dict(input_dict, average=True): + """ + Args: + input_dict (dict): all the values will be reduced + average (bool): whether to do average or sum + Reduce the values in the dictionary from all processes so that all processes + have the averaged results. Returns a dict with the same fields as + input_dict, after reduction. + """ + world_size = get_world_size() + if world_size < 2: + return input_dict + with torch.no_grad(): + names = [] + values = [] + # sort the keys so that they are consistent across processes + for k in sorted(input_dict.keys()): + names.append(k) + values.append(input_dict[k]) + values = torch.stack(values, dim=0) + dist.all_reduce(values) + if average: + values /= world_size + reduced_dict = {k: v for k, v in zip(names, values)} + return reduced_dict diff --git a/cv/detection/maskrcnn/pytorch/common_utils/metric_logger.py b/cv/detection/maskrcnn/pytorch/common_utils/metric_logger.py new file mode 100644 index 000000000..960641c4d --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/common_utils/metric_logger.py @@ -0,0 +1,94 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict +import datetime +import time + +import torch +from .smooth_value import SmoothedValue + +""" +Examples: + +logger = MetricLogger(" ") + +>>> # For iter dataloader +>>> metric_logger.add_meter('img/s', utils.SmoothedValue(window_size=10, fmt='{value}')) +>>> header = 'Epoch: [{}]'.format(epoch) +>>> for image, target in metric_logger.log_every(data_loader, print_freq, header): +>>> ... +>>> logger.metric_logger.meters['img/s'].update(fps) + +""" + +class MetricLogger(object): + + def __init__(self, delimiter="\t"): + self.meters = defaultdict(SmoothedValue) + self.delimiter = delimiter + + def update(self, **kwargs): + for k, v in kwargs.items(): + if isinstance(v, torch.Tensor): + v = v.item() + assert isinstance(v, (float, int)) + self.meters[k].update(v) + + def __getattr__(self, attr): + if attr in self.meters: + return self.meters[attr] + if attr in self.__dict__: + return self.__dict__[attr] + raise AttributeError("'{}' object has no attribute '{}'".format( + type(self).__name__, attr)) + + def __str__(self): + loss_str = [] + for name, meter in self.meters.items(): + loss_str.append( + "{}: {}".format(name, str(meter)) + ) + return self.delimiter.join(loss_str) + + def synchronize_between_processes(self): + for meter in self.meters.values(): + meter.synchronize_between_processes() + + def add_meter(self, name, meter): + self.meters[name] = meter + + def log_every(self, iterable, print_freq, header=None): + i = 0 + if not header: + header = '' + start_time = time.time() + end = time.time() + iter_time = SmoothedValue(fmt='{avg:.4f}') + data_time = SmoothedValue(fmt='{avg:.4f}') + space_fmt = ':' + str(len(str(len(iterable)))) + 'd' + log_msg = self.delimiter.join([ + header, + '[{0' + space_fmt + '}/{1}]', + 'eta: {eta}', + '{meters}', + 'time: {time}', + 'data: {data}' + ]) + for obj in iterable: + data_time.update(time.time() - end) + yield obj + iter_time.update(time.time() - end) + if i % print_freq == 0: + eta_seconds = iter_time.global_avg * (len(iterable) - i) + eta_string = str(datetime.timedelta(seconds=int(eta_seconds))) + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time))) + i += 1 + end = time.time() + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('{} Total time: {}'.format(header, total_time_str)) diff --git a/cv/detection/maskrcnn/pytorch/common_utils/misc.py b/cv/detection/maskrcnn/pytorch/common_utils/misc.py new file mode 100644 index 000000000..a16b92d0e --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/common_utils/misc.py @@ -0,0 +1,33 @@ +import os +import sys +import errno +from typing import Any +from PIL import Image + +import torch + + +def mkdir(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST: + raise + +def _is_pil_image(img: Any) -> bool: + try: + import accimage + except ImportError: + accimage = None + if accimage is not None: + return isinstance(img, (Image.Image, accimage.Image)) + else: + return isinstance(img, Image.Image) + +def get_image_size(img): + if isinstance(img, torch.Tensor): + return [img.shape[-1], img.shape[-2]] + + if _is_pil_image(img): + return img.size + raise TypeError("Unexpected type {}".format(type(img))) \ No newline at end of file diff --git a/cv/detection/maskrcnn/pytorch/common_utils/smooth_value.py b/cv/detection/maskrcnn/pytorch/common_utils/smooth_value.py new file mode 100644 index 000000000..30cb89d60 --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/common_utils/smooth_value.py @@ -0,0 +1,75 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import defaultdict, deque +import datetime +import errno +import os +import time + +import torch +import torch.distributed as dist +from .dist import is_dist_avail_and_initialized + + +class SmoothedValue(object): + """Track a series of values and provide access to smoothed values over a + window or the global series average. + """ + + def __init__(self, window_size=20, fmt=None): + if fmt is None: + fmt = "{median:.4f} ({global_avg:.4f})" + self.deque = deque(maxlen=window_size) + self.total = 0.0 + self.count = 0 + self.fmt = fmt + + def update(self, value, n=1): + self.deque.append(value) + self.count += n + self.total += value * n + + def synchronize_between_processes(self): + """ + Warning: does not synchronize the deque! + """ + if not is_dist_avail_and_initialized(): + return + t = torch.tensor([self.count, self.total], dtype=torch.float32, device='cuda') + dist.barrier() + dist.all_reduce(t) + t = t.tolist() + self.count = int(t[0]) + self.total = t[1] + + @property + def median(self): + d = torch.tensor(list(self.deque)) + return d.median().item() + + @property + def avg(self): + d = torch.tensor(list(self.deque), dtype=torch.float32) + return d.mean().item() + + @property + def global_avg(self): + return self.total / self.count + + @property + def max(self): + return max(self.deque) + + @property + def value(self): + return self.deque[-1] + + def __str__(self): + return self.fmt.format( + median=self.median, + avg=self.avg, + global_avg=self.global_avg, + max=self.max, + value=self.value) \ No newline at end of file diff --git a/cv/detection/maskrcnn/pytorch/configs/e2e_mask_rcnn_R_50_FPN_1x.yaml b/cv/detection/maskrcnn/pytorch/configs/e2e_mask_rcnn_R_50_FPN_1x.yaml deleted file mode 100644 index 3576bfd1a..000000000 --- a/cv/detection/maskrcnn/pytorch/configs/e2e_mask_rcnn_R_50_FPN_1x.yaml +++ /dev/null @@ -1,40 +0,0 @@ -MODEL: - META_ARCHITECTURE: "GeneralizedRCNN" - WEIGHT: "catalog://ImageNetPretrained/MSRA/R-50" - BACKBONE: - CONV_BODY: "R-50-FPN" - OUT_CHANNELS: 256 - RPN: - USE_FPN: True - ANCHOR_STRIDE: (4, 8, 16, 32, 64) - PRE_NMS_TOP_N_TRAIN: 2000 - PRE_NMS_TOP_N_TEST: 1000 - POST_NMS_TOP_N_TEST: 1000 - FPN_POST_NMS_TOP_N_TEST: 1000 - ROI_HEADS: - USE_FPN: True - ROI_BOX_HEAD: - POOLER_RESOLUTION: 7 - POOLER_SCALES: (0.25, 0.125, 0.0625, 0.03125) - POOLER_SAMPLING_RATIO: 2 - FEATURE_EXTRACTOR: "FPN2MLPFeatureExtractor" - PREDICTOR: "FPNPredictor" - ROI_MASK_HEAD: - POOLER_SCALES: (0.25, 0.125, 0.0625, 0.03125) - FEATURE_EXTRACTOR: "MaskRCNNFPNFeatureExtractor" - PREDICTOR: "MaskRCNNC4Predictor" - POOLER_RESOLUTION: 14 - POOLER_SAMPLING_RATIO: 2 - RESOLUTION: 28 - SHARE_BOX_FEATURE_EXTRACTOR: False - MASK_ON: True -DATASETS: - TRAIN: ("coco_2017_train",) - TEST: ("coco_2017_val",) -DATALOADER: - SIZE_DIVISIBILITY: 32 -SOLVER: - BASE_LR: 0.02 - WEIGHT_DECAY: 0.0001 - STEPS: (60000, 80000) - MAX_ITER: 90000 diff --git a/cv/detection/maskrcnn/pytorch/dataloader/__init__.py b/cv/detection/maskrcnn/pytorch/dataloader/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/detection/maskrcnn/pytorch/dataloader/detection.py b/cv/detection/maskrcnn/pytorch/dataloader/detection.py new file mode 100644 index 000000000..ecd66e196 --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/dataloader/detection.py @@ -0,0 +1,37 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +from .utils.coco_utils import get_coco, get_coco_kp +from .utils import presets_detection as presets +from .utils.pascal_voc import get_voc + +""" +Examples: + +>>> dataset_train, num_classes = get_dataset("voc", "train", "/path/to/VOC2012_sample") +>>> dataset_val, _ = get_dataset("voc", "val", "/path/to/VOC2012_sample") +""" + + +def get_transform(train, data_augmentation="ssd"): + return presets.DetectionPresetTrain(data_augmentation) if train else presets.DetectionPresetEval() + + +def get_dataset(name, image_set, data_path): + transform = get_transform(image_set.lower() == "train") + paths = { + "coco": (data_path, get_coco, 91), + "coco_kp": (data_path, get_coco_kp, 2), + "voc": (data_path, get_voc, 21) + } + p, ds_fn, num_classes = paths[name] + + ds = ds_fn(p, image_set=image_set, transforms=transform) + return ds, num_classes \ No newline at end of file diff --git a/cv/detection/maskrcnn/pytorch/dataloader/segmentation.py b/cv/detection/maskrcnn/pytorch/dataloader/segmentation.py new file mode 100644 index 000000000..7c2f2b6ee --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/dataloader/segmentation.py @@ -0,0 +1,46 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +import torchvision + +from .utils.coco_seg_utils import get_coco +from .utils import presets_segmentation as presets +from .utils.camvid import get_camvid + +""" +Examples: + +>>> dataset_train, num_classes = get_dataset("/path/to/CamVid11", "camvid", "train") +>>> dataset_val, _ = get_dataset("/path/to/CamVid11", "camvid", "val") + +""" + + +def get_transform(train): + base_size = 520 + crop_size = 480 + return presets.SegmentationPresetTrain(base_size, crop_size) if train else presets.SegmentationPresetEval(base_size) + + +def get_dataset(dir_path, name, image_set): + transform = get_transform(image_set == 'train') + # name = 'camvid' + def sbd(*args, **kwargs): + return torchvision.datasets.SBDataset(*args, mode='segmentation', **kwargs) + paths = { + "voc": (dir_path, torchvision.datasets.VOCSegmentation, 21), + "voc_aug": (dir_path, sbd, 21), + "coco": (dir_path, get_coco, 21), + "camvid": (dir_path, get_camvid, 12) + } + p, ds_fn, num_classes = paths[name] + + ds = ds_fn(p, image_set=image_set, transforms=transform) + return ds, num_classes \ No newline at end of file diff --git a/cv/detection/maskrcnn/pytorch/dataloader/utils/__init__.py b/cv/detection/maskrcnn/pytorch/dataloader/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/cv/detection/maskrcnn/pytorch/dataloader/utils/camvid.py b/cv/detection/maskrcnn/pytorch/dataloader/utils/camvid.py new file mode 100644 index 000000000..0f5487480 --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/dataloader/utils/camvid.py @@ -0,0 +1,191 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright Declaration: This software, including all of its code and documentation, +# except for the third-party software it contains, is a copyrighted work of Shanghai Iluvatar CoreX +# Semiconductor Co., Ltd. and its affiliates ("Iluvatar CoreX") in accordance with the PRC Copyright +# Law and relevant international treaties, and all rights contained therein are enjoyed by Iluvatar +# CoreX. No user of this software shall have any right, ownership or interest in this software and +# any use of this software shall be in compliance with the terms and conditions of the End User +# License Agreement. + + +from collections import OrderedDict +import cv2 +from functools import lru_cache +import numpy as np +import os +import os.path as osp +from PIL import Image + +from .pascal_voc import BaseDataset + + +class SemanticSeg(BaseDataset): + + def __init__(self, + data_dir, + anno_dir, + data_suffix='.png', + anno_suffix='.png', + split=None, + classes=None, + label_rgb=False, + **kwargs): + super().__init__(**kwargs) + self.data_dir = data_dir + self.anno_dir = anno_dir + self.data_suffix = data_suffix + self.anno_suffix = anno_suffix + self.label_rgb = label_rgb + if classes is not None: + self.CLASSES = classes + self.COLORS = np.random.randint(0, 255, size=(len(self.CLASSES), 3)) + self.annotations = SemanticSeg.load_annotations(data_dir, + anno_dir, + data_suffix, + anno_suffix, + split) + self.image_ids = list(self.annotations.keys()) + + @staticmethod + def load_annotations(data_dir, anno_dir, data_suffix, anno_suffix, split): + """Load annotation from directory. + + Returns + ------- + dict[dict] + All image info of dataset. + """ + + img_infos = dict() + image_id = 0 + # 如果提供了 split file, 则根据 split file 中的文件名得到数据集的图片和标注 + if split is not None: + with open(split) as f: + for line in f: + img_name = line.strip() + img_file = osp.join(data_dir, img_name + data_suffix) + img_info = dict(file_path=img_file) + if anno_dir is not None: + anno_file = osp.join(anno_dir, img_name + anno_suffix) + img_info['anno_path'] = anno_file + img_infos[image_id] = img_info + image_id += 1 + else: + for img in scandir(data_dir, data_suffix): + img_file = osp.join(data_dir, img) + img_info = dict(file_path=img_file) + if anno_dir is not None: + anno_file = osp.join(anno_dir, + img.replace(data_suffix, anno_suffix)) + img_info['anno_path'] = anno_file + img_infos[image_id] = img_info + image_id += 1 + + return img_infos + + def get_data(self, idx): + image_id = self.image_ids[idx] + img = self._read_image(image_id) + mask = self._read_mask(image_id) + return img, mask + + def get_img_info(self, image_id): + return self.annotations[image_id] + + @lru_cache(maxsize=None) + def _read_image(self, image_id): + img_info = self.get_img_info(image_id) + image = Image.open(img_info['file_path']) + return image + + @lru_cache(maxsize=None) + def _read_mask(self, image_id): + img_info = self.get_img_info(image_id) + anno_path = img_info['anno_path'] + # TODO: 当 mask 是 RGB 格式时, 通过 self.COLORS 中 class_id 和 RGB 值 + # 的映射关系, 将 mask 转换为灰度图, 其中每个像素值都对应一个 class_id + if self.label_rgb: + # mask = cv2.imread(anno_path) + # mask = cv2.cvtColor(mask, cv2.COLOR_BGR2RGB) + raise NotImplementedError + else: + mask = Image.open(anno_path) + return mask + + @lru_cache(maxsize=None) + def get_class_name(self, class_id): + class_dict = {class_name: i for i, class_name in enumerate(self.CLASSES)} + class_id_dict = {cls_id: name for name, cls_id in class_dict.items()} + return class_id_dict[class_id] + + @lru_cache(maxsize=None) + def get_class_color(self, class_id): + if not isinstance(class_id, str): + class_name = self.get_class_name(class_id) + else: + class_name = class_id + CLASS_COLOR = OrderedDict(zip(self.CLASSES, self.COLORS)) + return CLASS_COLOR[class_name] + + +def scandir(dir_path, suffix=None): + file_paths = [] + for parent, _, fns in sorted(os.walk(dir_path)): + # 将 dir_path 从 parent 中去除, 使 parent 为相对路径 + parent = parent[len(dir_path):] + for fn in sorted(fns): + if fn.endswith(suffix): + # file 在 dir_path 中的相对路径 + path = os.path.join(parent, fn) + file_paths.append(path) + return file_paths + + + +class CamVid(SemanticSeg): + """CamVid dataset with 32 classes. + """ + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.CLASSES = ('Animal', 'Archway', 'Bicyclist', 'Bridge', + 'Building', 'Car', 'CartLuggagePram', 'Child', + 'Column_Pole', 'Fence', 'LaneMkgsDriv', 'LaneMkgsNonDriv', + 'Misc_Text', 'MotorcycleScooter', 'OtherMoving', 'ParkingBlock', + 'Pedestrian', 'Road', 'RoadShoulder', 'Sidewalk', + 'SignSymbol', 'Sky', 'SUVPickupTruck', 'TrafficCone', + 'TrafficLight', 'Train', 'Tree', 'Truck_Bus', + 'Tunnel', 'VegetationMisc', 'Void', 'Wall') + + self.COLORS = ([64,128,64], [192,0,128], [0,128,192], [0,128,64], + [128,0,0], [64,0,128], [64,0,192], [192,128,64], + [192,192,128], [64,64,128], [128,0,192], [192,0,64], + [128,128,64], [192,0,192], [128,64,64], [64,192,128], + [64,64,0], [128,64,128], [128,128,192], [0,0,192], + [192,128,128], [128,128,128], [64,128,192], [0,0,64], + [0,64,64], [192,64,128], [128,128,0], [192,128,192], + [64,0,64], [192,192,0], [0,0,0], [64,192,0]) + + + +class CamVid11(SemanticSeg): + """CamVid dataset with 11 classes. + """ + + def __init__(self, **kwargs): + super().__init__(**kwargs) + # 如果将 `Unlabelled` (对应 CamVid 中的 `Void` 类) 也算作一类的话, 则总共有 12 类. + self.CLASSES = ('Sky', 'Building', 'Pole', 'Road', + 'Pavement', 'Tree', 'SignSymbol', 'Fence', + 'Car', 'Pedestrian', 'Bicyclist', 'Unlabelled') + + self.COLORS = ([128,128,128], [128,0,0], [192,192,128], [128,64,128], + [60,40,222], [128,128,0], [192,128,128], [64,64,128], + [64,0,128], [64,64,0], [0,128,192], [0,0,0]) + + +def get_camvid(root, image_set, transforms): + data_dir = os.path.join(root, image_set) + anno_dir = os.path.join(root, image_set + "annot") + return CamVid11(data_dir=data_dir, anno_dir=anno_dir, transform=transforms) + diff --git a/cv/detection/maskrcnn/pytorch/dataloader/utils/coco_utils.py b/cv/detection/maskrcnn/pytorch/dataloader/utils/coco_utils.py new file mode 100644 index 000000000..cef31f4a8 --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/dataloader/utils/coco_utils.py @@ -0,0 +1,255 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +import copy +import os +from PIL import Image + +import torch +import torch.utils.data +import torchvision + +from pycocotools import mask as coco_mask +from pycocotools.coco import COCO + +from . import transforms_det as T + +class FilterAndRemapCocoCategories(object): + def __init__(self, categories, remap=True): + self.categories = categories + self.remap = remap + + def __call__(self, image, target): + anno = target["annotations"] + anno = [obj for obj in anno if obj["category_id"] in self.categories] + if not self.remap: + target["annotations"] = anno + return image, target + anno = copy.deepcopy(anno) + for obj in anno: + obj["category_id"] = self.categories.index(obj["category_id"]) + target["annotations"] = anno + return image, target + + +def convert_coco_poly_to_mask(segmentations, height, width): + masks = [] + for polygons in segmentations: + rles = coco_mask.frPyObjects(polygons, height, width) + mask = coco_mask.decode(rles) + if len(mask.shape) < 3: + mask = mask[..., None] + mask = torch.as_tensor(mask, dtype=torch.uint8) + mask = mask.any(dim=2) + masks.append(mask) + if masks: + masks = torch.stack(masks, dim=0) + else: + masks = torch.zeros((0, height, width), dtype=torch.uint8) + return masks + + +class ConvertCocoPolysToMask(object): + def __call__(self, image, target): + w, h = image.size + + image_id = target["image_id"] + image_id = torch.tensor([image_id]) + + anno = target["annotations"] + + anno = [obj for obj in anno if obj['iscrowd'] == 0] + + boxes = [obj["bbox"] for obj in anno] + # guard against no boxes via resizing + boxes = torch.as_tensor(boxes, dtype=torch.float32).reshape(-1, 4) + boxes[:, 2:] += boxes[:, :2] + boxes[:, 0::2].clamp_(min=0, max=w) + boxes[:, 1::2].clamp_(min=0, max=h) + + classes = [obj["category_id"] for obj in anno] + classes = torch.tensor(classes, dtype=torch.int64) + + segmentations = [obj["segmentation"] for obj in anno] + masks = convert_coco_poly_to_mask(segmentations, h, w) + + keypoints = None + if anno and "keypoints" in anno[0]: + keypoints = [obj["keypoints"] for obj in anno] + keypoints = torch.as_tensor(keypoints, dtype=torch.float32) + num_keypoints = keypoints.shape[0] + if num_keypoints: + keypoints = keypoints.view(num_keypoints, -1, 3) + + keep = (boxes[:, 3] > boxes[:, 1]) & (boxes[:, 2] > boxes[:, 0]) + boxes = boxes[keep] + classes = classes[keep] + masks = masks[keep] + if keypoints is not None: + keypoints = keypoints[keep] + + target = {} + target["boxes"] = boxes + target["labels"] = classes + target["masks"] = masks + target["image_id"] = image_id + if keypoints is not None: + target["keypoints"] = keypoints + + # for conversion to coco api + area = torch.tensor([obj["area"] for obj in anno]) + iscrowd = torch.tensor([obj["iscrowd"] for obj in anno]) + target["area"] = area + target["iscrowd"] = iscrowd + + return image, target + + +def _coco_remove_images_without_annotations(dataset, cat_list=None): + def _has_only_empty_bbox(anno): + return all(any(o <= 1 for o in obj["bbox"][2:]) for obj in anno) + + def _count_visible_keypoints(anno): + return sum(sum(1 for v in ann["keypoints"][2::3] if v > 0) for ann in anno) + + min_keypoints_per_image = 10 + + def _has_valid_annotation(anno): + # if it's empty, there is no annotation + if len(anno) == 0: + return False + # if all boxes have close to zero area, there is no annotation + if _has_only_empty_bbox(anno): + return False + # keypoints task have a slight different critera for considering + # if an annotation is valid + if "keypoints" not in anno[0]: + return True + # for keypoint detection tasks, only consider valid images those + # containing at least min_keypoints_per_image + if _count_visible_keypoints(anno) >= min_keypoints_per_image: + return True + return False + + assert isinstance(dataset, torchvision.datasets.CocoDetection) + ids = [] + for ds_idx, img_id in enumerate(dataset.ids): + ann_ids = dataset.coco.getAnnIds(imgIds=img_id, iscrowd=None) + anno = dataset.coco.loadAnns(ann_ids) + if cat_list: + anno = [obj for obj in anno if obj["category_id"] in cat_list] + if _has_valid_annotation(anno): + ids.append(ds_idx) + + dataset = torch.utils.data.Subset(dataset, ids) + return dataset + + +def convert_to_coco_api(ds): + coco_ds = COCO() + # annotation IDs need to start at 1, not 0, see torchvision issue #1530 + ann_id = 1 + dataset = {'images': [], 'categories': [], 'annotations': []} + categories = set() + for img_idx in range(len(ds)): + # find better way to get target + # targets = ds.get_annotations(img_idx) + img, targets = ds[img_idx] + image_id = targets["image_id"].item() + img_dict = {} + img_dict['id'] = image_id + img_dict['height'] = img.shape[-2] + img_dict['width'] = img.shape[-1] + dataset['images'].append(img_dict) + bboxes = targets["boxes"] + bboxes[:, 2:] -= bboxes[:, :2] + bboxes = bboxes.tolist() + labels = targets['labels'].tolist() + areas = targets['area'].tolist() + iscrowd = targets['iscrowd'].tolist() + if 'masks' in targets: + masks = targets['masks'] + # make masks Fortran contiguous for coco_mask + masks = masks.permute(0, 2, 1).contiguous().permute(0, 2, 1) + if 'keypoints' in targets: + keypoints = targets['keypoints'] + keypoints = keypoints.reshape(keypoints.shape[0], -1).tolist() + num_objs = len(bboxes) + for i in range(num_objs): + ann = {} + ann['image_id'] = image_id + ann['bbox'] = bboxes[i] + ann['category_id'] = labels[i] + categories.add(labels[i]) + ann['area'] = areas[i] + ann['iscrowd'] = iscrowd[i] + ann['id'] = ann_id + if 'masks' in targets: + ann["segmentation"] = coco_mask.encode(masks[i].numpy()) + if 'keypoints' in targets: + ann['keypoints'] = keypoints[i] + ann['num_keypoints'] = sum(k != 0 for k in keypoints[i][2::3]) + dataset['annotations'].append(ann) + ann_id += 1 + dataset['categories'] = [{'id': i} for i in sorted(categories)] + coco_ds.dataset = dataset + coco_ds.createIndex() + return coco_ds + + +def get_coco_api_from_dataset(dataset): + for _ in range(10): + if isinstance(dataset, torchvision.datasets.CocoDetection): + break + if isinstance(dataset, torch.utils.data.Subset): + dataset = dataset.dataset + if isinstance(dataset, torchvision.datasets.CocoDetection): + return dataset.coco + return convert_to_coco_api(dataset) + + +class CocoDetection(torchvision.datasets.CocoDetection): + def __init__(self, img_folder, ann_file, transforms): + super(CocoDetection, self).__init__(img_folder, ann_file) + self._transforms = transforms + + def __getitem__(self, idx): + img, target = super(CocoDetection, self).__getitem__(idx) + image_id = self.ids[idx] + target = dict(image_id=image_id, annotations=target) + if self._transforms is not None: + img, target = self._transforms(img, target) + return img, target + + +def get_coco(root, image_set, transforms, mode='instances'): + anno_file_template = "{}_{}2017.json" + PATHS = { + "train": ("train2017", os.path.join("annotations", anno_file_template.format(mode, "train"))), + "val": ("val2017", os.path.join("annotations", anno_file_template.format(mode, "val"))), + # "train": ("val2017", os.path.join("annotations", anno_file_template.format(mode, "val"))) + } + + t = [ConvertCocoPolysToMask()] + + if transforms is not None: + t.append(transforms) + transforms = T.Compose(t) + + img_folder, ann_file = PATHS[image_set] + img_folder = os.path.join(root, img_folder) + ann_file = os.path.join(root, ann_file) + + dataset = CocoDetection(img_folder, ann_file, transforms=transforms) + + if image_set == "train": + dataset = _coco_remove_images_without_annotations(dataset) + + # dataset = torch.common_utils.data.Subset(dataset, [i for i in range(500)]) + + return dataset + + +def get_coco_kp(root, image_set, transforms): + return get_coco(root, image_set, transforms, mode="person_keypoints") diff --git a/cv/detection/maskrcnn/pytorch/dataloader/utils/functional.py b/cv/detection/maskrcnn/pytorch/dataloader/utils/functional.py new file mode 100644 index 000000000..ea5212135 --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/dataloader/utils/functional.py @@ -0,0 +1,1324 @@ +import math +import numbers +import warnings +from enum import Enum + +import numpy as np +from PIL import Image + +import torch +from torch import Tensor +from typing import List, Tuple, Any, Optional + +try: + import accimage +except ImportError: + accimage = None + +from . import functional_pil as F_pil +from . import functional_tensor as F_t + + +class InterpolationMode(Enum): + """Interpolation modes + """ + NEAREST = "nearest" + BILINEAR = "bilinear" + BICUBIC = "bicubic" + # For PIL compatibility + BOX = "box" + HAMMING = "hamming" + LANCZOS = "lanczos" + + +# TODO: Once torchscript supports Enums with staticmethod +# this can be put into InterpolationMode as staticmethod +def _interpolation_modes_from_int(i: int) -> InterpolationMode: + inverse_modes_mapping = { + 0: InterpolationMode.NEAREST, + 2: InterpolationMode.BILINEAR, + 3: InterpolationMode.BICUBIC, + 4: InterpolationMode.BOX, + 5: InterpolationMode.HAMMING, + 1: InterpolationMode.LANCZOS, + } + return inverse_modes_mapping[i] + + +pil_modes_mapping = { + InterpolationMode.NEAREST: 0, + InterpolationMode.BILINEAR: 2, + InterpolationMode.BICUBIC: 3, + InterpolationMode.BOX: 4, + InterpolationMode.HAMMING: 5, + InterpolationMode.LANCZOS: 1, +} + +_is_pil_image = F_pil._is_pil_image +_parse_fill = F_pil._parse_fill + + +def _get_image_size(img: Tensor) -> List[int]: + """Returns image size as [w, h] + """ + if isinstance(img, torch.Tensor): + return F_t._get_image_size(img) + + return F_pil._get_image_size(img) + + +def _get_image_num_channels(img: Tensor) -> int: + """Returns number of image channels + """ + if isinstance(img, torch.Tensor): + return F_t._get_image_num_channels(img) + + return F_pil._get_image_num_channels(img) + + +@torch.jit.unused +def _is_numpy(img: Any) -> bool: + return isinstance(img, np.ndarray) + + +@torch.jit.unused +def _is_numpy_image(img: Any) -> bool: + return img.ndim in {2, 3} + + +def to_tensor(pic): + """Convert a ``PIL Image`` or ``numpy.ndarray`` to tensor. + This function does not support torchscript. + + See :class:`~torchvision.transforms.ToTensor` for more details. + + Args: + pic (PIL Image or numpy.ndarray): Image to be converted to tensor. + + Returns: + Tensor: Converted image. + """ + if not(F_pil._is_pil_image(pic) or _is_numpy(pic)): + raise TypeError('pic should be PIL Image or ndarray. Got {}'.format(type(pic))) + + if _is_numpy(pic) and not _is_numpy_image(pic): + raise ValueError('pic should be 2/3 dimensional. Got {} dimensions.'.format(pic.ndim)) + + default_float_dtype = torch.get_default_dtype() + + if isinstance(pic, np.ndarray): + # handle numpy array + if pic.ndim == 2: + pic = pic[:, :, None] + + img = torch.from_numpy(pic.transpose((2, 0, 1))).contiguous() + # backward compatibility + if isinstance(img, torch.ByteTensor): + return img.to(dtype=default_float_dtype).div(255) + else: + return img + + if accimage is not None and isinstance(pic, accimage.Image): + nppic = np.zeros([pic.channels, pic.height, pic.width], dtype=default_float_dtype) + pic.copyto(nppic) + return torch.from_numpy(nppic) + + # handle PIL Image + if pic.mode == 'I': + img = torch.from_numpy(np.array(pic, np.int32, copy=False)) + elif pic.mode == 'I;16': + img = torch.from_numpy(np.array(pic, np.int16, copy=False)) + elif pic.mode == 'F': + img = torch.from_numpy(np.array(pic, np.float32, copy=False)) + elif pic.mode == '1': + img = 255 * torch.from_numpy(np.array(pic, np.uint8, copy=False)) + else: + img = torch.ByteTensor(torch.ByteStorage.from_buffer(pic.tobytes())) + + img = img.view(pic.size[1], pic.size[0], len(pic.getbands())) + # put it from HWC to CHW format + img = img.permute((2, 0, 1)).contiguous() + if isinstance(img, torch.ByteTensor): + return img.to(dtype=default_float_dtype).div(255) + else: + return img + + +def pil_to_tensor(pic): + """Convert a ``PIL Image`` to a tensor of the same type. + This function does not support torchscript. + + See :class:`~torchvision.transforms.PILToTensor` for more details. + + Args: + pic (PIL Image): Image to be converted to tensor. + + Returns: + Tensor: Converted image. + """ + if not F_pil._is_pil_image(pic): + raise TypeError('pic should be PIL Image. Got {}'.format(type(pic))) + + if accimage is not None and isinstance(pic, accimage.Image): + # accimage format is always uint8 internally, so always return uint8 here + nppic = np.zeros([pic.channels, pic.height, pic.width], dtype=np.uint8) + pic.copyto(nppic) + return torch.as_tensor(nppic) + + # handle PIL Image + img = torch.as_tensor(np.asarray(pic)) + img = img.view(pic.size[1], pic.size[0], len(pic.getbands())) + # put it from HWC to CHW format + img = img.permute((2, 0, 1)) + return img + + +def convert_image_dtype(image: torch.Tensor, dtype: torch.dtype = torch.float) -> torch.Tensor: + """Convert a tensor image to the given ``dtype`` and scale the values accordingly + This function does not support PIL Image. + + Args: + image (torch.Tensor): Image to be converted + dtype (torch.dtype): Desired data type of the output + + Returns: + Tensor: Converted image + + .. note:: + + When converting from a smaller to a larger integer ``dtype`` the maximum values are **not** mapped exactly. + If converted back and forth, this mismatch has no effect. + + Raises: + RuntimeError: When trying to cast :class:`torch.float32` to :class:`torch.int32` or :class:`torch.int64` as + well as for trying to cast :class:`torch.float64` to :class:`torch.int64`. These conversions might lead to + overflow errors since the floating point ``dtype`` cannot store consecutive integers over the whole range + of the integer ``dtype``. + """ + if not isinstance(image, torch.Tensor): + raise TypeError('Input img should be Tensor Image') + + return F_t.convert_image_dtype(image, dtype) + + +def to_pil_image(pic, mode=None): + """Convert a tensor or an ndarray to PIL Image. This function does not support torchscript. + + See :class:`~torchvision.transforms.ToPILImage` for more details. + + Args: + pic (Tensor or numpy.ndarray): Image to be converted to PIL Image. + mode (`PIL.Image mode`_): color space and pixel depth of input data (optional). + + .. _PIL.Image mode: https://pillow.readthedocs.io/en/latest/handbook/concepts.html#concept-modes + + Returns: + PIL Image: Image converted to PIL Image. + """ + if not(isinstance(pic, torch.Tensor) or isinstance(pic, np.ndarray)): + raise TypeError('pic should be Tensor or ndarray. Got {}.'.format(type(pic))) + + elif isinstance(pic, torch.Tensor): + if pic.ndimension() not in {2, 3}: + raise ValueError('pic should be 2/3 dimensional. Got {} dimensions.'.format(pic.ndimension())) + + elif pic.ndimension() == 2: + # if 2D image, add channel dimension (CHW) + pic = pic.unsqueeze(0) + + # check number of channels + if pic.shape[-3] > 4: + raise ValueError('pic should not have > 4 channels. Got {} channels.'.format(pic.shape[-3])) + + elif isinstance(pic, np.ndarray): + if pic.ndim not in {2, 3}: + raise ValueError('pic should be 2/3 dimensional. Got {} dimensions.'.format(pic.ndim)) + + elif pic.ndim == 2: + # if 2D image, add channel dimension (HWC) + pic = np.expand_dims(pic, 2) + + # check number of channels + if pic.shape[-1] > 4: + raise ValueError('pic should not have > 4 channels. Got {} channels.'.format(pic.shape[-1])) + + npimg = pic + if isinstance(pic, torch.Tensor): + if pic.is_floating_point() and mode != 'F': + pic = pic.mul(255).byte() + npimg = np.transpose(pic.cpu().numpy(), (1, 2, 0)) + + if not isinstance(npimg, np.ndarray): + raise TypeError('Input pic must be a torch.Tensor or NumPy ndarray, ' + + 'not {}'.format(type(npimg))) + + if npimg.shape[2] == 1: + expected_mode = None + npimg = npimg[:, :, 0] + if npimg.dtype == np.uint8: + expected_mode = 'L' + elif npimg.dtype == np.int16: + expected_mode = 'I;16' + elif npimg.dtype == np.int32: + expected_mode = 'I' + elif npimg.dtype == np.float32: + expected_mode = 'F' + if mode is not None and mode != expected_mode: + raise ValueError("Incorrect mode ({}) supplied for input type {}. Should be {}" + .format(mode, np.dtype, expected_mode)) + mode = expected_mode + + elif npimg.shape[2] == 2: + permitted_2_channel_modes = ['LA'] + if mode is not None and mode not in permitted_2_channel_modes: + raise ValueError("Only modes {} are supported for 2D inputs".format(permitted_2_channel_modes)) + + if mode is None and npimg.dtype == np.uint8: + mode = 'LA' + + elif npimg.shape[2] == 4: + permitted_4_channel_modes = ['RGBA', 'CMYK', 'RGBX'] + if mode is not None and mode not in permitted_4_channel_modes: + raise ValueError("Only modes {} are supported for 4D inputs".format(permitted_4_channel_modes)) + + if mode is None and npimg.dtype == np.uint8: + mode = 'RGBA' + else: + permitted_3_channel_modes = ['RGB', 'YCbCr', 'HSV'] + if mode is not None and mode not in permitted_3_channel_modes: + raise ValueError("Only modes {} are supported for 3D inputs".format(permitted_3_channel_modes)) + if mode is None and npimg.dtype == np.uint8: + mode = 'RGB' + + if mode is None: + raise TypeError('Input type {} is not supported'.format(npimg.dtype)) + + return Image.fromarray(npimg, mode=mode) + + +def normalize(tensor: Tensor, mean: List[float], std: List[float], inplace: bool = False) -> Tensor: + """Normalize a tensor image with mean and standard deviation. + This transform does not support PIL Image. + + .. note:: + This transform acts out of place by default, i.e., it does not mutates the input tensor. + + See :class:`~torchvision.transforms.Normalize` for more details. + + Args: + tensor (Tensor): Tensor image of size (C, H, W) or (B, C, H, W) to be normalized. + mean (sequence): Sequence of means for each channel. + std (sequence): Sequence of standard deviations for each channel. + inplace(bool,optional): Bool to make this operation inplace. + + Returns: + Tensor: Normalized Tensor image. + """ + if not isinstance(tensor, torch.Tensor): + raise TypeError('Input tensor should be a torch tensor. Got {}.'.format(type(tensor))) + + if tensor.ndim < 3: + raise ValueError('Expected tensor to be a tensor image of size (..., C, H, W). Got tensor.size() = ' + '{}.'.format(tensor.size())) + + if not inplace: + tensor = tensor.clone() + + dtype = tensor.dtype + mean = torch.as_tensor(mean, dtype=dtype, device=tensor.device) + std = torch.as_tensor(std, dtype=dtype, device=tensor.device) + if (std == 0).any(): + raise ValueError('std evaluated to zero after conversion to {}, leading to division by zero.'.format(dtype)) + if mean.ndim == 1: + mean = mean.view(-1, 1, 1) + if std.ndim == 1: + std = std.view(-1, 1, 1) + tensor.sub_(mean).div_(std) + return tensor + + +def resize(img: Tensor, size: List[int], interpolation: InterpolationMode = InterpolationMode.BILINEAR) -> Tensor: + r"""Resize the input image to the given size. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions + + Args: + img (PIL Image or Tensor): Image to be resized. + size (sequence or int): Desired output size. If size is a sequence like + (h, w), the output size will be matched to this. If size is an int, + the smaller edge of the image will be matched to this number maintaining + the aspect ratio. i.e, if height > width, then image will be rescaled to + :math:`\left(\text{size} \times \frac{\text{height}}{\text{width}}, \text{size}\right)`. + In torchscript mode size as single int is not supported, use a sequence of length 1: ``[size, ]``. + interpolation (InterpolationMode): Desired interpolation enum defined by + :class:`torchvision.transforms.InterpolationMode`. + Default is ``InterpolationMode.BILINEAR``. If input is Tensor, only ``InterpolationMode.NEAREST``, + ``InterpolationMode.BILINEAR`` and ``InterpolationMode.BICUBIC`` are supported. + For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable. + + Returns: + PIL Image or Tensor: Resized image. + """ + # Backward compatibility with integer value + if isinstance(interpolation, int): + warnings.warn( + "Argument interpolation should be of type InterpolationMode instead of int. " + "Please, use InterpolationMode enum." + ) + interpolation = _interpolation_modes_from_int(interpolation) + + if not isinstance(interpolation, InterpolationMode): + raise TypeError("Argument interpolation should be a InterpolationMode") + + if not isinstance(img, torch.Tensor): + pil_interpolation = pil_modes_mapping[interpolation] + return F_pil.resize(img, size=size, interpolation=pil_interpolation) + + return F_t.resize(img, size=size, interpolation=interpolation.value) + + +def scale(*args, **kwargs): + warnings.warn("The use of the transforms.Scale transform is deprecated, " + + "please use transforms.Resize instead.") + return resize(*args, **kwargs) + + +def pad(img: Tensor, padding: List[int], fill: int = 0, padding_mode: str = "constant") -> Tensor: + r"""Pad the given image on all sides with the given "pad" value. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means at most 2 leading dimensions for mode reflect and symmetric, + at most 3 leading dimensions for mode edge, + and an arbitrary number of leading dimensions for mode constant + + Args: + img (PIL Image or Tensor): Image to be padded. + padding (int or sequence): Padding on each border. If a single int is provided this + is used to pad all borders. If sequence of length 2 is provided this is the padding + on left/right and top/bottom respectively. If a sequence of length 4 is provided + this is the padding for the left, top, right and bottom borders respectively. + In torchscript mode padding as single int is not supported, use a sequence of length 1: ``[padding, ]``. + fill (number or str or tuple): Pixel fill value for constant fill. Default is 0. + If a tuple of length 3, it is used to fill R, G, B channels respectively. + This value is only used when the padding_mode is constant. + Only number is supported for torch Tensor. + Only int or str or tuple value is supported for PIL Image. + padding_mode: Type of padding. Should be: constant, edge, reflect or symmetric. Default is constant. + + - constant: pads with a constant value, this value is specified with fill + + - edge: pads with the last value on the edge of the image, + if input a 5D torch Tensor, the last 3 dimensions will be padded instead of the last 2 + + - reflect: pads with reflection of image (without repeating the last value on the edge) + + padding [1, 2, 3, 4] with 2 elements on both sides in reflect mode + will result in [3, 2, 1, 2, 3, 4, 3, 2] + + - symmetric: pads with reflection of image (repeating the last value on the edge) + + padding [1, 2, 3, 4] with 2 elements on both sides in symmetric mode + will result in [2, 1, 1, 2, 3, 4, 4, 3] + + Returns: + PIL Image or Tensor: Padded image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.pad(img, padding=padding, fill=fill, padding_mode=padding_mode) + + return F_t.pad(img, padding=padding, fill=fill, padding_mode=padding_mode) + + +def crop(img: Tensor, top: int, left: int, height: int, width: int) -> Tensor: + """Crop the given image at specified location and output size. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions + + Args: + img (PIL Image or Tensor): Image to be cropped. (0,0) denotes the top left corner of the image. + top (int): Vertical component of the top left corner of the crop box. + left (int): Horizontal component of the top left corner of the crop box. + height (int): Height of the crop box. + width (int): Width of the crop box. + + Returns: + PIL Image or Tensor: Cropped image. + """ + + if not isinstance(img, torch.Tensor): + return F_pil.crop(img, top, left, height, width) + + return F_t.crop(img, top, left, height, width) + + +def center_crop(img: Tensor, output_size: List[int]) -> Tensor: + """Crops the given image at the center. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions. + If image size is smaller than output size along any edge, image is padded with 0 and then center cropped. + + Args: + img (PIL Image or Tensor): Image to be cropped. + output_size (sequence or int): (height, width) of the crop box. If int or sequence with single int, + it is used for both directions. + + Returns: + PIL Image or Tensor: Cropped image. + """ + if isinstance(output_size, numbers.Number): + output_size = (int(output_size), int(output_size)) + elif isinstance(output_size, (tuple, list)) and len(output_size) == 1: + output_size = (output_size[0], output_size[0]) + + image_width, image_height = _get_image_size(img) + crop_height, crop_width = output_size + + if crop_width > image_width or crop_height > image_height: + padding_ltrb = [ + (crop_width - image_width) // 2 if crop_width > image_width else 0, + (crop_height - image_height) // 2 if crop_height > image_height else 0, + (crop_width - image_width + 1) // 2 if crop_width > image_width else 0, + (crop_height - image_height + 1) // 2 if crop_height > image_height else 0, + ] + img = pad(img, padding_ltrb, fill=0) # PIL uses fill value 0 + image_width, image_height = _get_image_size(img) + if crop_width == image_width and crop_height == image_height: + return img + + crop_top = int(round((image_height - crop_height) / 2.)) + crop_left = int(round((image_width - crop_width) / 2.)) + return crop(img, crop_top, crop_left, crop_height, crop_width) + + +def resized_crop( + img: Tensor, top: int, left: int, height: int, width: int, size: List[int], + interpolation: InterpolationMode = InterpolationMode.BILINEAR +) -> Tensor: + """Crop the given image and resize it to desired size. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions + + Notably used in :class:`~torchvision.transforms.RandomResizedCrop`. + + Args: + img (PIL Image or Tensor): Image to be cropped. (0,0) denotes the top left corner of the image. + top (int): Vertical component of the top left corner of the crop box. + left (int): Horizontal component of the top left corner of the crop box. + height (int): Height of the crop box. + width (int): Width of the crop box. + size (sequence or int): Desired output size. Same semantics as ``resize``. + interpolation (InterpolationMode): Desired interpolation enum defined by + :class:`torchvision.transforms.InterpolationMode`. + Default is ``InterpolationMode.BILINEAR``. If input is Tensor, only ``InterpolationMode.NEAREST``, + ``InterpolationMode.BILINEAR`` and ``InterpolationMode.BICUBIC`` are supported. + For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable. + + Returns: + PIL Image or Tensor: Cropped image. + """ + img = crop(img, top, left, height, width) + img = resize(img, size, interpolation) + return img + + +def hflip(img: Tensor) -> Tensor: + """Horizontally flip the given image. + + Args: + img (PIL Image or Tensor): Image to be flipped. If img + is a Tensor, it is expected to be in [..., H, W] format, + where ... means it can have an arbitrary number of leading + dimensions. + + Returns: + PIL Image or Tensor: Horizontally flipped image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.hflip(img) + + return F_t.hflip(img) + + +def _get_perspective_coeffs( + startpoints: List[List[int]], endpoints: List[List[int]] +) -> List[float]: + """Helper function to get the coefficients (a, b, c, d, e, f, g, h) for the perspective transforms. + + In Perspective Transform each pixel (x, y) in the original image gets transformed as, + (x, y) -> ( (ax + by + c) / (gx + hy + 1), (dx + ey + f) / (gx + hy + 1) ) + + Args: + startpoints (list of list of ints): List containing four lists of two integers corresponding to four corners + ``[top-left, top-right, bottom-right, bottom-left]`` of the original image. + endpoints (list of list of ints): List containing four lists of two integers corresponding to four corners + ``[top-left, top-right, bottom-right, bottom-left]`` of the transformed image. + + Returns: + octuple (a, b, c, d, e, f, g, h) for transforming each pixel. + """ + a_matrix = torch.zeros(2 * len(startpoints), 8, dtype=torch.float) + + for i, (p1, p2) in enumerate(zip(endpoints, startpoints)): + a_matrix[2 * i, :] = torch.tensor([p1[0], p1[1], 1, 0, 0, 0, -p2[0] * p1[0], -p2[0] * p1[1]]) + a_matrix[2 * i + 1, :] = torch.tensor([0, 0, 0, p1[0], p1[1], 1, -p2[1] * p1[0], -p2[1] * p1[1]]) + + b_matrix = torch.tensor(startpoints, dtype=torch.float).view(8) + res = torch.lstsq(b_matrix, a_matrix)[0] + + output: List[float] = res.squeeze(1).tolist() + return output + + +def perspective( + img: Tensor, + startpoints: List[List[int]], + endpoints: List[List[int]], + interpolation: InterpolationMode = InterpolationMode.BILINEAR, + fill: Optional[List[float]] = None +) -> Tensor: + """Perform perspective transform of the given image. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions. + + Args: + img (PIL Image or Tensor): Image to be transformed. + startpoints (list of list of ints): List containing four lists of two integers corresponding to four corners + ``[top-left, top-right, bottom-right, bottom-left]`` of the original image. + endpoints (list of list of ints): List containing four lists of two integers corresponding to four corners + ``[top-left, top-right, bottom-right, bottom-left]`` of the transformed image. + interpolation (InterpolationMode): Desired interpolation enum defined by + :class:`torchvision.transforms.InterpolationMode`. Default is ``InterpolationMode.BILINEAR``. + If input is Tensor, only ``InterpolationMode.NEAREST``, ``InterpolationMode.BILINEAR`` are supported. + For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable. + fill (sequence or number, optional): Pixel fill value for the area outside the transformed + image. If given a number, the value is used for all bands respectively. + In torchscript mode single int/float value is not supported, please use a sequence + of length 1: ``[value, ]``. + If input is PIL Image, the options is only available for ``Pillow>=5.0.0``. + + Returns: + PIL Image or Tensor: transformed Image. + """ + + coeffs = _get_perspective_coeffs(startpoints, endpoints) + + # Backward compatibility with integer value + if isinstance(interpolation, int): + warnings.warn( + "Argument interpolation should be of type InterpolationMode instead of int. " + "Please, use InterpolationMode enum." + ) + interpolation = _interpolation_modes_from_int(interpolation) + + if not isinstance(interpolation, InterpolationMode): + raise TypeError("Argument interpolation should be a InterpolationMode") + + if not isinstance(img, torch.Tensor): + pil_interpolation = pil_modes_mapping[interpolation] + return F_pil.perspective(img, coeffs, interpolation=pil_interpolation, fill=fill) + + return F_t.perspective(img, coeffs, interpolation=interpolation.value, fill=fill) + + +def vflip(img: Tensor) -> Tensor: + """Vertically flip the given image. + + Args: + img (PIL Image or Tensor): Image to be flipped. If img + is a Tensor, it is expected to be in [..., H, W] format, + where ... means it can have an arbitrary number of leading + dimensions. + + Returns: + PIL Image or Tensor: Vertically flipped image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.vflip(img) + + return F_t.vflip(img) + + +def five_crop(img: Tensor, size: List[int]) -> Tuple[Tensor, Tensor, Tensor, Tensor, Tensor]: + """Crop the given image into four corners and the central crop. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions + + .. Note:: + This transform returns a tuple of images and there may be a + mismatch in the number of inputs and targets your ``Dataset`` returns. + + Args: + img (PIL Image or Tensor): Image to be cropped. + size (sequence or int): Desired output size of the crop. If size is an + int instead of sequence like (h, w), a square crop (size, size) is + made. If provided a sequence of length 1, it will be interpreted as (size[0], size[0]). + + Returns: + tuple: tuple (tl, tr, bl, br, center) + Corresponding top left, top right, bottom left, bottom right and center crop. + """ + if isinstance(size, numbers.Number): + size = (int(size), int(size)) + elif isinstance(size, (tuple, list)) and len(size) == 1: + size = (size[0], size[0]) + + if len(size) != 2: + raise ValueError("Please provide only two dimensions (h, w) for size.") + + image_width, image_height = _get_image_size(img) + crop_height, crop_width = size + if crop_width > image_width or crop_height > image_height: + msg = "Requested crop size {} is bigger than input size {}" + raise ValueError(msg.format(size, (image_height, image_width))) + + tl = crop(img, 0, 0, crop_height, crop_width) + tr = crop(img, 0, image_width - crop_width, crop_height, crop_width) + bl = crop(img, image_height - crop_height, 0, crop_height, crop_width) + br = crop(img, image_height - crop_height, image_width - crop_width, crop_height, crop_width) + + center = center_crop(img, [crop_height, crop_width]) + + return tl, tr, bl, br, center + + +def ten_crop(img: Tensor, size: List[int], vertical_flip: bool = False) -> List[Tensor]: + """Generate ten cropped images from the given image. + Crop the given image into four corners and the central crop plus the + flipped version of these (horizontal flipping is used by default). + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions + + .. Note:: + This transform returns a tuple of images and there may be a + mismatch in the number of inputs and targets your ``Dataset`` returns. + + Args: + img (PIL Image or Tensor): Image to be cropped. + size (sequence or int): Desired output size of the crop. If size is an + int instead of sequence like (h, w), a square crop (size, size) is + made. If provided a sequence of length 1, it will be interpreted as (size[0], size[0]). + vertical_flip (bool): Use vertical flipping instead of horizontal + + Returns: + tuple: tuple (tl, tr, bl, br, center, tl_flip, tr_flip, bl_flip, br_flip, center_flip) + Corresponding top left, top right, bottom left, bottom right and + center crop and same for the flipped image. + """ + if isinstance(size, numbers.Number): + size = (int(size), int(size)) + elif isinstance(size, (tuple, list)) and len(size) == 1: + size = (size[0], size[0]) + + if len(size) != 2: + raise ValueError("Please provide only two dimensions (h, w) for size.") + + first_five = five_crop(img, size) + + if vertical_flip: + img = vflip(img) + else: + img = hflip(img) + + second_five = five_crop(img, size) + return first_five + second_five + + +def adjust_brightness(img: Tensor, brightness_factor: float) -> Tensor: + """Adjust brightness of an image. + + Args: + img (PIL Image or Tensor): Image to be adjusted. + If img is torch Tensor, it is expected to be in [..., 1 or 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + brightness_factor (float): How much to adjust the brightness. Can be + any non negative number. 0 gives a black image, 1 gives the + original image while 2 increases the brightness by a factor of 2. + + Returns: + PIL Image or Tensor: Brightness adjusted image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.adjust_brightness(img, brightness_factor) + + return F_t.adjust_brightness(img, brightness_factor) + + +def adjust_contrast(img: Tensor, contrast_factor: float) -> Tensor: + """Adjust contrast of an image. + + Args: + img (PIL Image or Tensor): Image to be adjusted. + If img is torch Tensor, it is expected to be in [..., 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + contrast_factor (float): How much to adjust the contrast. Can be any + non negative number. 0 gives a solid gray image, 1 gives the + original image while 2 increases the contrast by a factor of 2. + + Returns: + PIL Image or Tensor: Contrast adjusted image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.adjust_contrast(img, contrast_factor) + + return F_t.adjust_contrast(img, contrast_factor) + + +def adjust_saturation(img: Tensor, saturation_factor: float) -> Tensor: + """Adjust color saturation of an image. + + Args: + img (PIL Image or Tensor): Image to be adjusted. + If img is torch Tensor, it is expected to be in [..., 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + saturation_factor (float): How much to adjust the saturation. 0 will + give a black and white image, 1 will give the original image while + 2 will enhance the saturation by a factor of 2. + + Returns: + PIL Image or Tensor: Saturation adjusted image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.adjust_saturation(img, saturation_factor) + + return F_t.adjust_saturation(img, saturation_factor) + + +def adjust_hue(img: Tensor, hue_factor: float) -> Tensor: + """Adjust hue of an image. + + The image hue is adjusted by converting the image to HSV and + cyclically shifting the intensities in the hue channel (H). + The image is then converted back to original image mode. + + `hue_factor` is the amount of shift in H channel and must be in the + interval `[-0.5, 0.5]`. + + See `Hue`_ for more details. + + .. _Hue: https://en.wikipedia.org/wiki/Hue + + Args: + img (PIL Image or Tensor): Image to be adjusted. + If img is torch Tensor, it is expected to be in [..., 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + If img is PIL Image mode "1", "L", "I", "F" and modes with transparency (alpha channel) are not supported. + hue_factor (float): How much to shift the hue channel. Should be in + [-0.5, 0.5]. 0.5 and -0.5 give complete reversal of hue channel in + HSV space in positive and negative direction respectively. + 0 means no shift. Therefore, both -0.5 and 0.5 will give an image + with complementary colors while 0 gives the original image. + + Returns: + PIL Image or Tensor: Hue adjusted image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.adjust_hue(img, hue_factor) + + return F_t.adjust_hue(img, hue_factor) + + +def adjust_gamma(img: Tensor, gamma: float, gain: float = 1) -> Tensor: + r"""Perform gamma correction on an image. + + Also known as Power Law Transform. Intensities in RGB mode are adjusted + based on the following equation: + + .. math:: + I_{\text{out}} = 255 \times \text{gain} \times \left(\frac{I_{\text{in}}}{255}\right)^{\gamma} + + See `Gamma Correction`_ for more details. + + .. _Gamma Correction: https://en.wikipedia.org/wiki/Gamma_correction + + Args: + img (PIL Image or Tensor): PIL Image to be adjusted. + If img is torch Tensor, it is expected to be in [..., 1 or 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + If img is PIL Image, modes with transparency (alpha channel) are not supported. + gamma (float): Non negative real number, same as :math:`\gamma` in the equation. + gamma larger than 1 make the shadows darker, + while gamma smaller than 1 make dark regions lighter. + gain (float): The constant multiplier. + Returns: + PIL Image or Tensor: Gamma correction adjusted image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.adjust_gamma(img, gamma, gain) + + return F_t.adjust_gamma(img, gamma, gain) + + +def _get_inverse_affine_matrix( + center: List[float], angle: float, translate: List[float], scale: float, shear: List[float] +) -> List[float]: + # Helper method to compute inverse matrix for affine transformation + + # As it is explained in PIL.Image.rotate + # We need compute INVERSE of affine transformation matrix: M = T * C * RSS * C^-1 + # where T is translation matrix: [1, 0, tx | 0, 1, ty | 0, 0, 1] + # C is translation matrix to keep center: [1, 0, cx | 0, 1, cy | 0, 0, 1] + # RSS is rotation with scale and shear matrix + # RSS(a, s, (sx, sy)) = + # = R(a) * S(s) * SHy(sy) * SHx(sx) + # = [ s*cos(a - sy)/cos(sy), s*(-cos(a - sy)*tan(x)/cos(y) - sin(a)), 0 ] + # [ s*sin(a + sy)/cos(sy), s*(-sin(a - sy)*tan(x)/cos(y) + cos(a)), 0 ] + # [ 0 , 0 , 1 ] + # + # where R is a rotation matrix, S is a scaling matrix, and SHx and SHy are the shears: + # SHx(s) = [1, -tan(s)] and SHy(s) = [1 , 0] + # [0, 1 ] [-tan(s), 1] + # + # Thus, the inverse is M^-1 = C * RSS^-1 * C^-1 * T^-1 + + rot = math.radians(angle) + sx, sy = [math.radians(s) for s in shear] + + cx, cy = center + tx, ty = translate + + # RSS without scaling + a = math.cos(rot - sy) / math.cos(sy) + b = -math.cos(rot - sy) * math.tan(sx) / math.cos(sy) - math.sin(rot) + c = math.sin(rot - sy) / math.cos(sy) + d = -math.sin(rot - sy) * math.tan(sx) / math.cos(sy) + math.cos(rot) + + # Inverted rotation matrix with scale and shear + # det([[a, b], [c, d]]) == 1, since det(rotation) = 1 and det(shear) = 1 + matrix = [d, -b, 0.0, -c, a, 0.0] + matrix = [x / scale for x in matrix] + + # Apply inverse of translation and of center translation: RSS^-1 * C^-1 * T^-1 + matrix[2] += matrix[0] * (-cx - tx) + matrix[1] * (-cy - ty) + matrix[5] += matrix[3] * (-cx - tx) + matrix[4] * (-cy - ty) + + # Apply center translation: C * RSS^-1 * C^-1 * T^-1 + matrix[2] += cx + matrix[5] += cy + + return matrix + + +def rotate( + img: Tensor, angle: float, interpolation: InterpolationMode = InterpolationMode.NEAREST, + expand: bool = False, center: Optional[List[int]] = None, + fill: Optional[List[float]] = None, resample: Optional[int] = None +) -> Tensor: + """Rotate the image by angle. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions. + + Args: + img (PIL Image or Tensor): image to be rotated. + angle (number): rotation angle value in degrees, counter-clockwise. + interpolation (InterpolationMode): Desired interpolation enum defined by + :class:`torchvision.transforms.InterpolationMode`. Default is ``InterpolationMode.NEAREST``. + If input is Tensor, only ``InterpolationMode.NEAREST``, ``InterpolationMode.BILINEAR`` are supported. + For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable. + expand (bool, optional): Optional expansion flag. + If true, expands the output image to make it large enough to hold the entire rotated image. + If false or omitted, make the output image the same size as the input image. + Note that the expand flag assumes rotation around the center and no translation. + center (sequence, optional): Optional center of rotation. Origin is the upper left corner. + Default is the center of the image. + fill (sequence or number, optional): Pixel fill value for the area outside the transformed + image. If given a number, the value is used for all bands respectively. + In torchscript mode single int/float value is not supported, please use a sequence + of length 1: ``[value, ]``. + If input is PIL Image, the options is only available for ``Pillow>=5.2.0``. + + Returns: + PIL Image or Tensor: Rotated image. + + .. _filters: https://pillow.readthedocs.io/en/latest/handbook/concepts.html#filters + + """ + if resample is not None: + warnings.warn( + "Argument resample is deprecated and will be removed since v0.10.0. Please, use interpolation instead" + ) + interpolation = _interpolation_modes_from_int(resample) + + # Backward compatibility with integer value + if isinstance(interpolation, int): + warnings.warn( + "Argument interpolation should be of type InterpolationMode instead of int. " + "Please, use InterpolationMode enum." + ) + interpolation = _interpolation_modes_from_int(interpolation) + + if not isinstance(angle, (int, float)): + raise TypeError("Argument angle should be int or float") + + if center is not None and not isinstance(center, (list, tuple)): + raise TypeError("Argument center should be a sequence") + + if not isinstance(interpolation, InterpolationMode): + raise TypeError("Argument interpolation should be a InterpolationMode") + + if not isinstance(img, torch.Tensor): + pil_interpolation = pil_modes_mapping[interpolation] + return F_pil.rotate(img, angle=angle, interpolation=pil_interpolation, expand=expand, center=center, fill=fill) + + center_f = [0.0, 0.0] + if center is not None: + img_size = _get_image_size(img) + # Center values should be in pixel coordinates but translated such that (0, 0) corresponds to image center. + center_f = [1.0 * (c - s * 0.5) for c, s in zip(center, img_size)] + + # due to current incoherence of rotation angle direction between affine and rotate implementations + # we need to set -angle. + matrix = _get_inverse_affine_matrix(center_f, -angle, [0.0, 0.0], 1.0, [0.0, 0.0]) + return F_t.rotate(img, matrix=matrix, interpolation=interpolation.value, expand=expand, fill=fill) + + +def affine( + img: Tensor, angle: float, translate: List[int], scale: float, shear: List[float], + interpolation: InterpolationMode = InterpolationMode.NEAREST, fill: Optional[List[float]] = None, + resample: Optional[int] = None, fillcolor: Optional[List[float]] = None +) -> Tensor: + """Apply affine transformation on the image keeping image center invariant. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions. + + Args: + img (PIL Image or Tensor): image to transform. + angle (number): rotation angle in degrees between -180 and 180, clockwise direction. + translate (sequence of integers): horizontal and vertical translations (post-rotation translation) + scale (float): overall scale + shear (float or sequence): shear angle value in degrees between -180 to 180, clockwise direction. + If a sequence is specified, the first value corresponds to a shear parallel to the x axis, while + the second value corresponds to a shear parallel to the y axis. + interpolation (InterpolationMode): Desired interpolation enum defined by + :class:`torchvision.transforms.InterpolationMode`. Default is ``InterpolationMode.NEAREST``. + If input is Tensor, only ``InterpolationMode.NEAREST``, ``InterpolationMode.BILINEAR`` are supported. + For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable. + fill (sequence or number, optional): Pixel fill value for the area outside the transformed + image. If given a number, the value is used for all bands respectively. + In torchscript mode single int/float value is not supported, please use a sequence + of length 1: ``[value, ]``. + If input is PIL Image, the options is only available for ``Pillow>=5.0.0``. + fillcolor (sequence, int, float): deprecated argument and will be removed since v0.10.0. + Please use the ``fill`` parameter instead. + resample (int, optional): deprecated argument and will be removed since v0.10.0. + Please use the ``interpolation`` parameter instead. + + Returns: + PIL Image or Tensor: Transformed image. + """ + if resample is not None: + warnings.warn( + "Argument resample is deprecated and will be removed since v0.10.0. Please, use interpolation instead" + ) + interpolation = _interpolation_modes_from_int(resample) + + # Backward compatibility with integer value + if isinstance(interpolation, int): + warnings.warn( + "Argument interpolation should be of type InterpolationMode instead of int. " + "Please, use InterpolationMode enum." + ) + interpolation = _interpolation_modes_from_int(interpolation) + + if fillcolor is not None: + warnings.warn( + "Argument fillcolor is deprecated and will be removed since v0.10.0. Please, use fill instead" + ) + fill = fillcolor + + if not isinstance(angle, (int, float)): + raise TypeError("Argument angle should be int or float") + + if not isinstance(translate, (list, tuple)): + raise TypeError("Argument translate should be a sequence") + + if len(translate) != 2: + raise ValueError("Argument translate should be a sequence of length 2") + + if scale <= 0.0: + raise ValueError("Argument scale should be positive") + + if not isinstance(shear, (numbers.Number, (list, tuple))): + raise TypeError("Shear should be either a single value or a sequence of two values") + + if not isinstance(interpolation, InterpolationMode): + raise TypeError("Argument interpolation should be a InterpolationMode") + + if isinstance(angle, int): + angle = float(angle) + + if isinstance(translate, tuple): + translate = list(translate) + + if isinstance(shear, numbers.Number): + shear = [shear, 0.0] + + if isinstance(shear, tuple): + shear = list(shear) + + if len(shear) == 1: + shear = [shear[0], shear[0]] + + if len(shear) != 2: + raise ValueError("Shear should be a sequence containing two values. Got {}".format(shear)) + + img_size = _get_image_size(img) + if not isinstance(img, torch.Tensor): + # center = (img_size[0] * 0.5 + 0.5, img_size[1] * 0.5 + 0.5) + # it is visually better to estimate the center without 0.5 offset + # otherwise image rotated by 90 degrees is shifted vs output image of torch.rot90 or F_t.affine + center = [img_size[0] * 0.5, img_size[1] * 0.5] + matrix = _get_inverse_affine_matrix(center, angle, translate, scale, shear) + pil_interpolation = pil_modes_mapping[interpolation] + return F_pil.affine(img, matrix=matrix, interpolation=pil_interpolation, fill=fill) + + translate_f = [1.0 * t for t in translate] + matrix = _get_inverse_affine_matrix([0.0, 0.0], angle, translate_f, scale, shear) + return F_t.affine(img, matrix=matrix, interpolation=interpolation.value, fill=fill) + + +@torch.jit.unused +def to_grayscale(img, num_output_channels=1): + """Convert PIL image of any mode (RGB, HSV, LAB, etc) to grayscale version of image. + This transform does not support torch Tensor. + + Args: + img (PIL Image): PIL Image to be converted to grayscale. + num_output_channels (int): number of channels of the output image. Value can be 1 or 3. Default is 1. + + Returns: + PIL Image: Grayscale version of the image. + if num_output_channels = 1 : returned image is single channel + + if num_output_channels = 3 : returned image is 3 channel with r = g = b + """ + if isinstance(img, Image.Image): + return F_pil.to_grayscale(img, num_output_channels) + + raise TypeError("Input should be PIL Image") + + +def rgb_to_grayscale(img: Tensor, num_output_channels: int = 1) -> Tensor: + """Convert RGB image to grayscale version of image. + If the image is torch Tensor, it is expected + to have [..., 3, H, W] shape, where ... means an arbitrary number of leading dimensions + + Note: + Please, note that this method supports only RGB images as input. For inputs in other color spaces, + please, consider using meth:`~torchvision.transforms.functional.to_grayscale` with PIL Image. + + Args: + img (PIL Image or Tensor): RGB Image to be converted to grayscale. + num_output_channels (int): number of channels of the output image. Value can be 1 or 3. Default, 1. + + Returns: + PIL Image or Tensor: Grayscale version of the image. + if num_output_channels = 1 : returned image is single channel + + if num_output_channels = 3 : returned image is 3 channel with r = g = b + """ + if not isinstance(img, torch.Tensor): + return F_pil.to_grayscale(img, num_output_channels) + + return F_t.rgb_to_grayscale(img, num_output_channels) + + +def erase(img: Tensor, i: int, j: int, h: int, w: int, v: Tensor, inplace: bool = False) -> Tensor: + """ Erase the input Tensor Image with given value. + This transform does not support PIL Image. + + Args: + img (Tensor Image): Tensor image of size (C, H, W) to be erased + i (int): i in (i,j) i.e coordinates of the upper left corner. + j (int): j in (i,j) i.e coordinates of the upper left corner. + h (int): Height of the erased region. + w (int): Width of the erased region. + v: Erasing value. + inplace(bool, optional): For in-place operations. By default is set False. + + Returns: + Tensor Image: Erased image. + """ + if not isinstance(img, torch.Tensor): + raise TypeError('img should be Tensor Image. Got {}'.format(type(img))) + + if not inplace: + img = img.clone() + + img[..., i:i + h, j:j + w] = v + return img + + +def gaussian_blur(img: Tensor, kernel_size: List[int], sigma: Optional[List[float]] = None) -> Tensor: + """Performs Gaussian blurring on the image by given kernel. + If the image is torch Tensor, it is expected + to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions. + + Args: + img (PIL Image or Tensor): Image to be blurred + kernel_size (sequence of ints or int): Gaussian kernel size. Can be a sequence of integers + like ``(kx, ky)`` or a single integer for square kernels. + In torchscript mode kernel_size as single int is not supported, use a sequence of length 1: ``[ksize, ]``. + sigma (sequence of floats or float, optional): Gaussian kernel standard deviation. Can be a + sequence of floats like ``(sigma_x, sigma_y)`` or a single float to define the + same sigma in both X/Y directions. If None, then it is computed using + ``kernel_size`` as ``sigma = 0.3 * ((kernel_size - 1) * 0.5 - 1) + 0.8``. + Default, None. In torchscript mode sigma as single float is + not supported, use a sequence of length 1: ``[sigma, ]``. + + Returns: + PIL Image or Tensor: Gaussian Blurred version of the image. + """ + if not isinstance(kernel_size, (int, list, tuple)): + raise TypeError('kernel_size should be int or a sequence of integers. Got {}'.format(type(kernel_size))) + if isinstance(kernel_size, int): + kernel_size = [kernel_size, kernel_size] + if len(kernel_size) != 2: + raise ValueError('If kernel_size is a sequence its length should be 2. Got {}'.format(len(kernel_size))) + for ksize in kernel_size: + if ksize % 2 == 0 or ksize < 0: + raise ValueError('kernel_size should have odd and positive integers. Got {}'.format(kernel_size)) + + if sigma is None: + sigma = [ksize * 0.15 + 0.35 for ksize in kernel_size] + + if sigma is not None and not isinstance(sigma, (int, float, list, tuple)): + raise TypeError('sigma should be either float or sequence of floats. Got {}'.format(type(sigma))) + if isinstance(sigma, (int, float)): + sigma = [float(sigma), float(sigma)] + if isinstance(sigma, (list, tuple)) and len(sigma) == 1: + sigma = [sigma[0], sigma[0]] + if len(sigma) != 2: + raise ValueError('If sigma is a sequence, its length should be 2. Got {}'.format(len(sigma))) + for s in sigma: + if s <= 0.: + raise ValueError('sigma should have positive values. Got {}'.format(sigma)) + + t_img = img + if not isinstance(img, torch.Tensor): + if not F_pil._is_pil_image(img): + raise TypeError('img should be PIL Image or Tensor. Got {}'.format(type(img))) + + t_img = to_tensor(img) + + output = F_t.gaussian_blur(t_img, kernel_size, sigma) + + if not isinstance(img, torch.Tensor): + output = to_pil_image(output) + return output + + +def invert(img: Tensor) -> Tensor: + """Invert the colors of an RGB/grayscale image. + + Args: + img (PIL Image or Tensor): Image to have its colors inverted. + If img is torch Tensor, it is expected to be in [..., 1 or 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + If img is PIL Image, it is expected to be in mode "L" or "RGB". + + Returns: + PIL Image or Tensor: Color inverted image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.invert(img) + + return F_t.invert(img) + + +def posterize(img: Tensor, bits: int) -> Tensor: + """Posterize an image by reducing the number of bits for each color channel. + + Args: + img (PIL Image or Tensor): Image to have its colors posterized. + If img is torch Tensor, it should be of type torch.uint8 and + it is expected to be in [..., 1 or 3, H, W] format, where ... means + it can have an arbitrary number of leading dimensions. + If img is PIL Image, it is expected to be in mode "L" or "RGB". + bits (int): The number of bits to keep for each channel (0-8). + Returns: + PIL Image or Tensor: Posterized image. + """ + if not (0 <= bits <= 8): + raise ValueError('The number if bits should be between 0 and 8. Got {}'.format(bits)) + + if not isinstance(img, torch.Tensor): + return F_pil.posterize(img, bits) + + return F_t.posterize(img, bits) + + +def solarize(img: Tensor, threshold: float) -> Tensor: + """Solarize an RGB/grayscale image by inverting all pixel values above a threshold. + + Args: + img (PIL Image or Tensor): Image to have its colors inverted. + If img is torch Tensor, it is expected to be in [..., 1 or 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + If img is PIL Image, it is expected to be in mode "L" or "RGB". + threshold (float): All pixels equal or above this value are inverted. + Returns: + PIL Image or Tensor: Solarized image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.solarize(img, threshold) + + return F_t.solarize(img, threshold) + + +def adjust_sharpness(img: Tensor, sharpness_factor: float) -> Tensor: + """Adjust the sharpness of an image. + + Args: + img (PIL Image or Tensor): Image to be adjusted. + If img is torch Tensor, it is expected to be in [..., 1 or 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + sharpness_factor (float): How much to adjust the sharpness. Can be + any non negative number. 0 gives a blurred image, 1 gives the + original image while 2 increases the sharpness by a factor of 2. + + Returns: + PIL Image or Tensor: Sharpness adjusted image. + """ + if not isinstance(img, torch.Tensor): + return F_pil.adjust_sharpness(img, sharpness_factor) + + return F_t.adjust_sharpness(img, sharpness_factor) + + +def autocontrast(img: Tensor) -> Tensor: + """Maximize contrast of an image by remapping its + pixels per channel so that the lowest becomes black and the lightest + becomes white. + + Args: + img (PIL Image or Tensor): Image on which autocontrast is applied. + If img is torch Tensor, it is expected to be in [..., 1 or 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + If img is PIL Image, it is expected to be in mode "L" or "RGB". + + Returns: + PIL Image or Tensor: An image that was autocontrasted. + """ + if not isinstance(img, torch.Tensor): + return F_pil.autocontrast(img) + + return F_t.autocontrast(img) + + +def equalize(img: Tensor) -> Tensor: + """Equalize the histogram of an image by applying + a non-linear mapping to the input in order to create a uniform + distribution of grayscale values in the output. + + Args: + img (PIL Image or Tensor): Image on which equalize is applied. + If img is torch Tensor, it is expected to be in [..., 1 or 3, H, W] format, + where ... means it can have an arbitrary number of leading dimensions. + If img is PIL Image, it is expected to be in mode "P", "L" or "RGB". + + Returns: + PIL Image or Tensor: An image that was equalized. + """ + if not isinstance(img, torch.Tensor): + return F_pil.equalize(img) + + return F_t.equalize(img) diff --git a/cv/detection/maskrcnn/pytorch/dataloader/utils/functional_pil.py b/cv/detection/maskrcnn/pytorch/dataloader/utils/functional_pil.py new file mode 100644 index 000000000..6999a2acf --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/dataloader/utils/functional_pil.py @@ -0,0 +1,349 @@ +import numbers +from typing import Any, List, Sequence + +import numpy as np +import torch +from PIL import Image, ImageOps, ImageEnhance, ImageFilter, __version__ as PILLOW_VERSION + +try: + import accimage +except ImportError: + accimage = None + + +@torch.jit.unused +def _is_pil_image(img: Any) -> bool: + if accimage is not None: + return isinstance(img, (Image.Image, accimage.Image)) + else: + return isinstance(img, Image.Image) + + +@torch.jit.unused +def _get_image_size(img: Any) -> List[int]: + if _is_pil_image(img): + return img.size + raise TypeError("Unexpected type {}".format(type(img))) + + +@torch.jit.unused +def _get_image_num_channels(img: Any) -> int: + if _is_pil_image(img): + return 1 if img.mode == 'L' else 3 + raise TypeError("Unexpected type {}".format(type(img))) + + +@torch.jit.unused +def hflip(img): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + return img.transpose(Image.FLIP_LEFT_RIGHT) + + +@torch.jit.unused +def vflip(img): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + return img.transpose(Image.FLIP_TOP_BOTTOM) + + +@torch.jit.unused +def adjust_brightness(img, brightness_factor): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + enhancer = ImageEnhance.Brightness(img) + img = enhancer.enhance(brightness_factor) + return img + + +@torch.jit.unused +def adjust_contrast(img, contrast_factor): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + enhancer = ImageEnhance.Contrast(img) + img = enhancer.enhance(contrast_factor) + return img + + +@torch.jit.unused +def adjust_saturation(img, saturation_factor): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + enhancer = ImageEnhance.Color(img) + img = enhancer.enhance(saturation_factor) + return img + + +@torch.jit.unused +def adjust_hue(img, hue_factor): + if not(-0.5 <= hue_factor <= 0.5): + raise ValueError('hue_factor ({}) is not in [-0.5, 0.5].'.format(hue_factor)) + + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + input_mode = img.mode + if input_mode in {'L', '1', 'I', 'F'}: + return img + + h, s, v = img.convert('HSV').split() + + np_h = np.array(h, dtype=np.uint8) + # uint8 addition take cares of rotation across boundaries + with np.errstate(over='ignore'): + np_h += np.uint8(hue_factor * 255) + h = Image.fromarray(np_h, 'L') + + img = Image.merge('HSV', (h, s, v)).convert(input_mode) + return img + + +@torch.jit.unused +def adjust_gamma(img, gamma, gain=1): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + if gamma < 0: + raise ValueError('Gamma should be a non-negative real number') + + input_mode = img.mode + img = img.convert('RGB') + gamma_map = [(255 + 1 - 1e-3) * gain * pow(ele / 255., gamma) for ele in range(256)] * 3 + img = img.point(gamma_map) # use PIL's point-function to accelerate this part + + img = img.convert(input_mode) + return img + + +@torch.jit.unused +def pad(img, padding, fill=0, padding_mode="constant"): + if not _is_pil_image(img): + raise TypeError("img should be PIL Image. Got {}".format(type(img))) + + if not isinstance(padding, (numbers.Number, tuple, list)): + raise TypeError("Got inappropriate padding arg") + if not isinstance(fill, (numbers.Number, str, tuple)): + raise TypeError("Got inappropriate fill arg") + if not isinstance(padding_mode, str): + raise TypeError("Got inappropriate padding_mode arg") + + if isinstance(padding, list): + padding = tuple(padding) + + if isinstance(padding, tuple) and len(padding) not in [1, 2, 4]: + raise ValueError("Padding must be an int or a 1, 2, or 4 element tuple, not a " + + "{} element tuple".format(len(padding))) + + if isinstance(padding, tuple) and len(padding) == 1: + # Compatibility with `functional_tensor.pad` + padding = padding[0] + + if padding_mode not in ["constant", "edge", "reflect", "symmetric"]: + raise ValueError("Padding mode should be either constant, edge, reflect or symmetric") + + if padding_mode == "constant": + opts = _parse_fill(fill, img, "2.3.0", name="fill") + if img.mode == "P": + palette = img.getpalette() + image = ImageOps.expand(img, border=padding, **opts) + image.putpalette(palette) + return image + + return ImageOps.expand(img, border=padding, **opts) + else: + if isinstance(padding, int): + pad_left = pad_right = pad_top = pad_bottom = padding + if isinstance(padding, tuple) and len(padding) == 2: + pad_left = pad_right = padding[0] + pad_top = pad_bottom = padding[1] + if isinstance(padding, tuple) and len(padding) == 4: + pad_left = padding[0] + pad_top = padding[1] + pad_right = padding[2] + pad_bottom = padding[3] + + p = [pad_left, pad_top, pad_right, pad_bottom] + cropping = -np.minimum(p, 0) + + if cropping.any(): + crop_left, crop_top, crop_right, crop_bottom = cropping + img = img.crop((crop_left, crop_top, img.width - crop_right, img.height - crop_bottom)) + + pad_left, pad_top, pad_right, pad_bottom = np.maximum(p, 0) + + if img.mode == 'P': + palette = img.getpalette() + img = np.asarray(img) + img = np.pad(img, ((pad_top, pad_bottom), (pad_left, pad_right)), padding_mode) + img = Image.fromarray(img) + img.putpalette(palette) + return img + + img = np.asarray(img) + # RGB image + if len(img.shape) == 3: + img = np.pad(img, ((pad_top, pad_bottom), (pad_left, pad_right), (0, 0)), padding_mode) + # Grayscale image + if len(img.shape) == 2: + img = np.pad(img, ((pad_top, pad_bottom), (pad_left, pad_right)), padding_mode) + + return Image.fromarray(img) + + +@torch.jit.unused +def crop(img: Image.Image, top: int, left: int, height: int, width: int) -> Image.Image: + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + return img.crop((left, top, left + width, top + height)) + + +@torch.jit.unused +def resize(img, size, interpolation=Image.BILINEAR): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + if not (isinstance(size, int) or (isinstance(size, Sequence) and len(size) in (1, 2))): + raise TypeError('Got inappropriate size arg: {}'.format(size)) + + if isinstance(size, int) or len(size) == 1: + if isinstance(size, Sequence): + size = size[0] + w, h = img.size + if (w <= h and w == size) or (h <= w and h == size): + return img + if w < h: + ow = size + oh = int(size * h / w) + return img.resize((ow, oh), interpolation) + else: + oh = size + ow = int(size * w / h) + return img.resize((ow, oh), interpolation) + else: + return img.resize(size[::-1], interpolation) + + +@torch.jit.unused +def _parse_fill(fill, img, min_pil_version, name="fillcolor"): + # Process fill color for affine transforms + major_found, minor_found = (int(v) for v in PILLOW_VERSION.split('.')[:2]) + major_required, minor_required = (int(v) for v in min_pil_version.split('.')[:2]) + if major_found < major_required or (major_found == major_required and minor_found < minor_required): + if fill is None: + return {} + else: + msg = ("The option to fill background area of the transformed image, " + "requires pillow>={}") + raise RuntimeError(msg.format(min_pil_version)) + + num_bands = len(img.getbands()) + if fill is None: + fill = 0 + if isinstance(fill, (int, float)) and num_bands > 1: + fill = tuple([fill] * num_bands) + if isinstance(fill, (list, tuple)): + if len(fill) != num_bands: + msg = ("The number of elements in 'fill' does not match the number of " + "bands of the image ({} != {})") + raise ValueError(msg.format(len(fill), num_bands)) + + fill = tuple(fill) + + return {name: fill} + + +@torch.jit.unused +def affine(img, matrix, interpolation=0, fill=None): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + output_size = img.size + opts = _parse_fill(fill, img, '5.0.0') + return img.transform(output_size, Image.AFFINE, matrix, interpolation, **opts) + + +@torch.jit.unused +def rotate(img, angle, interpolation=0, expand=False, center=None, fill=None): + if not _is_pil_image(img): + raise TypeError("img should be PIL Image. Got {}".format(type(img))) + + opts = _parse_fill(fill, img, '5.2.0') + return img.rotate(angle, interpolation, expand, center, **opts) + + +@torch.jit.unused +def perspective(img, perspective_coeffs, interpolation=Image.BICUBIC, fill=None): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + opts = _parse_fill(fill, img, '5.0.0') + + return img.transform(img.size, Image.PERSPECTIVE, perspective_coeffs, interpolation, **opts) + + +@torch.jit.unused +def to_grayscale(img, num_output_channels): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + if num_output_channels == 1: + img = img.convert('L') + elif num_output_channels == 3: + img = img.convert('L') + np_img = np.array(img, dtype=np.uint8) + np_img = np.dstack([np_img, np_img, np_img]) + img = Image.fromarray(np_img, 'RGB') + else: + raise ValueError('num_output_channels should be either 1 or 3') + + return img + + +@torch.jit.unused +def invert(img): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + return ImageOps.invert(img) + + +@torch.jit.unused +def posterize(img, bits): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + return ImageOps.posterize(img, bits) + + +@torch.jit.unused +def solarize(img, threshold): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + return ImageOps.solarize(img, threshold) + + +@torch.jit.unused +def adjust_sharpness(img, sharpness_factor): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + + enhancer = ImageEnhance.Sharpness(img) + img = enhancer.enhance(sharpness_factor) + return img + + +@torch.jit.unused +def autocontrast(img): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + return ImageOps.autocontrast(img) + + +@torch.jit.unused +def equalize(img): + if not _is_pil_image(img): + raise TypeError('img should be PIL Image. Got {}'.format(type(img))) + return ImageOps.equalize(img) diff --git a/cv/detection/maskrcnn/pytorch/dataloader/utils/functional_tensor.py b/cv/detection/maskrcnn/pytorch/dataloader/utils/functional_tensor.py new file mode 100644 index 000000000..69445e6a2 --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/dataloader/utils/functional_tensor.py @@ -0,0 +1,920 @@ +import warnings + +import torch +from torch import Tensor +from torch.nn.functional import grid_sample, conv2d, interpolate, pad as torch_pad +from torch.jit.annotations import BroadcastingList2 +from typing import Optional, Tuple, List + + +def _is_tensor_a_torch_image(x: Tensor) -> bool: + return x.ndim >= 2 + + +def _assert_image_tensor(img): + if not _is_tensor_a_torch_image(img): + raise TypeError("Tensor is not a torch image.") + + +def _get_image_size(img: Tensor) -> List[int]: + # Returns (w, h) of tensor image + _assert_image_tensor(img) + return [img.shape[-1], img.shape[-2]] + + +def _get_image_num_channels(img: Tensor) -> int: + if img.ndim == 2: + return 1 + elif img.ndim > 2: + return img.shape[-3] + + raise TypeError("Input ndim should be 2 or more. Got {}".format(img.ndim)) + + +def _max_value(dtype: torch.dtype) -> float: + # TODO: replace this method with torch.iinfo when it gets torchscript support. + # https://github.com/pytorch/pytorch/issues/41492 + + a = torch.tensor(2, dtype=dtype) + signed = 1 if torch.tensor(0, dtype=dtype).is_signed() else 0 + bits = 1 + max_value = torch.tensor(-signed, dtype=torch.long) + while True: + next_value = a.pow(bits - signed).sub(1) + if next_value > max_value: + max_value = next_value + bits *= 2 + else: + break + return max_value.item() + + +def _assert_channels(img: Tensor, permitted: List[int]) -> None: + c = _get_image_num_channels(img) + if c not in permitted: + raise TypeError("Input image tensor permitted channel values are {}, but found {}".format(permitted, c)) + + +def convert_image_dtype(image: torch.Tensor, dtype: torch.dtype = torch.float) -> torch.Tensor: + if image.dtype == dtype: + return image + + if image.is_floating_point(): + + # TODO: replace with dtype.is_floating_point when torchscript supports it + if torch.tensor(0, dtype=dtype).is_floating_point(): + return image.to(dtype) + + # float to int + if (image.dtype == torch.float32 and dtype in (torch.int32, torch.int64)) or ( + image.dtype == torch.float64 and dtype == torch.int64 + ): + msg = f"The cast from {image.dtype} to {dtype} cannot be performed safely." + raise RuntimeError(msg) + + # https://github.com/pytorch/vision/pull/2078#issuecomment-612045321 + # For data in the range 0-1, (float * 255).to(uint) is only 255 + # when float is exactly 1.0. + # `max + 1 - epsilon` provides more evenly distributed mapping of + # ranges of floats to ints. + eps = 1e-3 + max_val = _max_value(dtype) + result = image.mul(max_val + 1.0 - eps) + return result.to(dtype) + else: + input_max = _max_value(image.dtype) + + # int to float + # TODO: replace with dtype.is_floating_point when torchscript supports it + if torch.tensor(0, dtype=dtype).is_floating_point(): + image = image.to(dtype) + return image / input_max + + output_max = _max_value(dtype) + + # int to int + if input_max > output_max: + # factor should be forced to int for torch jit script + # otherwise factor is a float and image // factor can produce different results + factor = int((input_max + 1) // (output_max + 1)) + image = image // factor + return image.to(dtype) + else: + # factor should be forced to int for torch jit script + # otherwise factor is a float and image * factor can produce different results + factor = int((output_max + 1) // (input_max + 1)) + image = image.to(dtype) + return image * factor + + +def vflip(img: Tensor) -> Tensor: + _assert_image_tensor(img) + + return img.flip(-2) + + +def hflip(img: Tensor) -> Tensor: + _assert_image_tensor(img) + + return img.flip(-1) + + +def crop(img: Tensor, top: int, left: int, height: int, width: int) -> Tensor: + _assert_image_tensor(img) + + return img[..., top:top + height, left:left + width] + + +def rgb_to_grayscale(img: Tensor, num_output_channels: int = 1) -> Tensor: + if img.ndim < 3: + raise TypeError("Input image tensor should have at least 3 dimensions, but found {}".format(img.ndim)) + _assert_channels(img, [3]) + + if num_output_channels not in (1, 3): + raise ValueError('num_output_channels should be either 1 or 3') + + r, g, b = img.unbind(dim=-3) + # This implementation closely follows the TF one: + # https://github.com/tensorflow/tensorflow/blob/v2.3.0/tensorflow/python/ops/image_ops_impl.py#L2105-L2138 + l_img = (0.2989 * r + 0.587 * g + 0.114 * b).to(img.dtype) + l_img = l_img.unsqueeze(dim=-3) + + if num_output_channels == 3: + return l_img.expand(img.shape) + + return l_img + + +def adjust_brightness(img: Tensor, brightness_factor: float) -> Tensor: + if brightness_factor < 0: + raise ValueError('brightness_factor ({}) is not non-negative.'.format(brightness_factor)) + + _assert_image_tensor(img) + + _assert_channels(img, [1, 3]) + + return _blend(img, torch.zeros_like(img), brightness_factor) + + +def adjust_contrast(img: Tensor, contrast_factor: float) -> Tensor: + if contrast_factor < 0: + raise ValueError('contrast_factor ({}) is not non-negative.'.format(contrast_factor)) + + _assert_image_tensor(img) + + _assert_channels(img, [3]) + + dtype = img.dtype if torch.is_floating_point(img) else torch.float32 + mean = torch.mean(rgb_to_grayscale(img).to(dtype), dim=(-3, -2, -1), keepdim=True) + + return _blend(img, mean, contrast_factor) + + +def adjust_hue(img: Tensor, hue_factor: float) -> Tensor: + if not (-0.5 <= hue_factor <= 0.5): + raise ValueError('hue_factor ({}) is not in [-0.5, 0.5].'.format(hue_factor)) + + if not (isinstance(img, torch.Tensor)): + raise TypeError('Input img should be Tensor image') + + _assert_image_tensor(img) + + _assert_channels(img, [1, 3]) + if _get_image_num_channels(img) == 1: # Match PIL behaviour + return img + + orig_dtype = img.dtype + if img.dtype == torch.uint8: + img = img.to(dtype=torch.float32) / 255.0 + + img = _rgb2hsv(img) + h, s, v = img.unbind(dim=-3) + h = (h + hue_factor) % 1.0 + img = torch.stack((h, s, v), dim=-3) + img_hue_adj = _hsv2rgb(img) + + if orig_dtype == torch.uint8: + img_hue_adj = (img_hue_adj * 255.0).to(dtype=orig_dtype) + + return img_hue_adj + + +def adjust_saturation(img: Tensor, saturation_factor: float) -> Tensor: + if saturation_factor < 0: + raise ValueError('saturation_factor ({}) is not non-negative.'.format(saturation_factor)) + + _assert_image_tensor(img) + + _assert_channels(img, [3]) + + return _blend(img, rgb_to_grayscale(img), saturation_factor) + + +def adjust_gamma(img: Tensor, gamma: float, gain: float = 1) -> Tensor: + if not isinstance(img, torch.Tensor): + raise TypeError('Input img should be a Tensor.') + + _assert_channels(img, [1, 3]) + + if gamma < 0: + raise ValueError('Gamma should be a non-negative real number') + + result = img + dtype = img.dtype + if not torch.is_floating_point(img): + result = convert_image_dtype(result, torch.float32) + + result = (gain * result ** gamma).clamp(0, 1) + + result = convert_image_dtype(result, dtype) + result = result.to(dtype) + return result + + +def center_crop(img: Tensor, output_size: BroadcastingList2[int]) -> Tensor: + """DEPRECATED + """ + warnings.warn( + "This method is deprecated and will be removed in future releases. " + "Please, use ``F.center_crop`` instead." + ) + + _assert_image_tensor(img) + + _, image_width, image_height = img.size() + crop_height, crop_width = output_size + # crop_top = int(round((image_height - crop_height) / 2.)) + # Result can be different between python func and scripted func + # Temporary workaround: + crop_top = int((image_height - crop_height + 1) * 0.5) + # crop_left = int(round((image_width - crop_width) / 2.)) + # Result can be different between python func and scripted func + # Temporary workaround: + crop_left = int((image_width - crop_width + 1) * 0.5) + + return crop(img, crop_top, crop_left, crop_height, crop_width) + + +def five_crop(img: Tensor, size: BroadcastingList2[int]) -> List[Tensor]: + """DEPRECATED + """ + warnings.warn( + "This method is deprecated and will be removed in future releases. " + "Please, use ``F.five_crop`` instead." + ) + + _assert_image_tensor(img) + + assert len(size) == 2, "Please provide only two dimensions (h, w) for size." + + _, image_width, image_height = img.size() + crop_height, crop_width = size + if crop_width > image_width or crop_height > image_height: + msg = "Requested crop size {} is bigger than input size {}" + raise ValueError(msg.format(size, (image_height, image_width))) + + tl = crop(img, 0, 0, crop_width, crop_height) + tr = crop(img, image_width - crop_width, 0, image_width, crop_height) + bl = crop(img, 0, image_height - crop_height, crop_width, image_height) + br = crop(img, image_width - crop_width, image_height - crop_height, image_width, image_height) + center = center_crop(img, (crop_height, crop_width)) + + return [tl, tr, bl, br, center] + + +def ten_crop(img: Tensor, size: BroadcastingList2[int], vertical_flip: bool = False) -> List[Tensor]: + """DEPRECATED + """ + warnings.warn( + "This method is deprecated and will be removed in future releases. " + "Please, use ``F.ten_crop`` instead." + ) + + _assert_image_tensor(img) + + assert len(size) == 2, "Please provide only two dimensions (h, w) for size." + first_five = five_crop(img, size) + + if vertical_flip: + img = vflip(img) + else: + img = hflip(img) + + second_five = five_crop(img, size) + + return first_five + second_five + + +def _blend(img1: Tensor, img2: Tensor, ratio: float) -> Tensor: + ratio = float(ratio) + bound = 1.0 if img1.is_floating_point() else 255.0 + return (ratio * img1 + (1.0 - ratio) * img2).clamp(0, bound).to(img1.dtype) + + +def _rgb2hsv(img): + r, g, b = img.unbind(dim=-3) + + # Implementation is based on https://github.com/python-pillow/Pillow/blob/4174d4267616897df3746d315d5a2d0f82c656ee/ + # src/libImaging/Convert.c#L330 + maxc = torch.max(img, dim=-3).values + minc = torch.min(img, dim=-3).values + + # The algorithm erases S and H channel where `maxc = minc`. This avoids NaN + # from happening in the results, because + # + S channel has division by `maxc`, which is zero only if `maxc = minc` + # + H channel has division by `(maxc - minc)`. + # + # Instead of overwriting NaN afterwards, we just prevent it from occuring so + # we don't need to deal with it in case we save the NaN in a buffer in + # backprop, if it is ever supported, but it doesn't hurt to do so. + eqc = maxc == minc + + cr = maxc - minc + # Since `eqc => cr = 0`, replacing denominator with 1 when `eqc` is fine. + ones = torch.ones_like(maxc) + s = cr / torch.where(eqc, ones, maxc) + # Note that `eqc => maxc = minc = r = g = b`. So the following calculation + # of `h` would reduce to `bc - gc + 2 + rc - bc + 4 + rc - bc = 6` so it + # would not matter what values `rc`, `gc`, and `bc` have here, and thus + # replacing denominator with 1 when `eqc` is fine. + cr_divisor = torch.where(eqc, ones, cr) + rc = (maxc - r) / cr_divisor + gc = (maxc - g) / cr_divisor + bc = (maxc - b) / cr_divisor + + hr = (maxc == r) * (bc - gc) + hg = ((maxc == g) & (maxc != r)) * (2.0 + rc - bc) + hb = ((maxc != g) & (maxc != r)) * (4.0 + gc - rc) + h = (hr + hg + hb) + h = torch.fmod((h / 6.0 + 1.0), 1.0) + return torch.stack((h, s, maxc), dim=-3) + + +def _hsv2rgb(img): + h, s, v = img.unbind(dim=-3) + i = torch.floor(h * 6.0) + f = (h * 6.0) - i + i = i.to(dtype=torch.int32) + + p = torch.clamp((v * (1.0 - s)), 0.0, 1.0) + q = torch.clamp((v * (1.0 - s * f)), 0.0, 1.0) + t = torch.clamp((v * (1.0 - s * (1.0 - f))), 0.0, 1.0) + i = i % 6 + + mask = i.unsqueeze(dim=-3) == torch.arange(6, device=i.device).view(-1, 1, 1) + + a1 = torch.stack((v, q, p, p, t, v), dim=-3) + a2 = torch.stack((t, v, v, q, p, p), dim=-3) + a3 = torch.stack((p, p, t, v, v, q), dim=-3) + a4 = torch.stack((a1, a2, a3), dim=-4) + + return torch.einsum("...ijk, ...xijk -> ...xjk", mask.to(dtype=img.dtype), a4) + + +def _pad_symmetric(img: Tensor, padding: List[int]) -> Tensor: + # padding is left, right, top, bottom + + # crop if needed + if padding[0] < 0 or padding[1] < 0 or padding[2] < 0 or padding[3] < 0: + crop_left, crop_right, crop_top, crop_bottom = [-min(x, 0) for x in padding] + img = img[..., crop_top:img.shape[-2] - crop_bottom, crop_left:img.shape[-1] - crop_right] + padding = [max(x, 0) for x in padding] + + in_sizes = img.size() + + x_indices = [i for i in range(in_sizes[-1])] # [0, 1, 2, 3, ...] + left_indices = [i for i in range(padding[0] - 1, -1, -1)] # e.g. [3, 2, 1, 0] + right_indices = [-(i + 1) for i in range(padding[1])] # e.g. [-1, -2, -3] + x_indices = torch.tensor(left_indices + x_indices + right_indices) + + y_indices = [i for i in range(in_sizes[-2])] + top_indices = [i for i in range(padding[2] - 1, -1, -1)] + bottom_indices = [-(i + 1) for i in range(padding[3])] + y_indices = torch.tensor(top_indices + y_indices + bottom_indices) + + ndim = img.ndim + if ndim == 3: + return img[:, y_indices[:, None], x_indices[None, :]] + elif ndim == 4: + return img[:, :, y_indices[:, None], x_indices[None, :]] + else: + raise RuntimeError("Symmetric padding of N-D tensors are not supported yet") + + +def pad(img: Tensor, padding: List[int], fill: int = 0, padding_mode: str = "constant") -> Tensor: + _assert_image_tensor(img) + + if not isinstance(padding, (int, tuple, list)): + raise TypeError("Got inappropriate padding arg") + if not isinstance(fill, (int, float)): + raise TypeError("Got inappropriate fill arg") + if not isinstance(padding_mode, str): + raise TypeError("Got inappropriate padding_mode arg") + + if isinstance(padding, tuple): + padding = list(padding) + + if isinstance(padding, list) and len(padding) not in [1, 2, 4]: + raise ValueError("Padding must be an int or a 1, 2, or 4 element tuple, not a " + + "{} element tuple".format(len(padding))) + + if padding_mode not in ["constant", "edge", "reflect", "symmetric"]: + raise ValueError("Padding mode should be either constant, edge, reflect or symmetric") + + if isinstance(padding, int): + if torch.jit.is_scripting(): + # This maybe unreachable + raise ValueError("padding can't be an int while torchscripting, set it as a list [value, ]") + pad_left = pad_right = pad_top = pad_bottom = padding + elif len(padding) == 1: + pad_left = pad_right = pad_top = pad_bottom = padding[0] + elif len(padding) == 2: + pad_left = pad_right = padding[0] + pad_top = pad_bottom = padding[1] + else: + pad_left = padding[0] + pad_top = padding[1] + pad_right = padding[2] + pad_bottom = padding[3] + + p = [pad_left, pad_right, pad_top, pad_bottom] + + if padding_mode == "edge": + # remap padding_mode str + padding_mode = "replicate" + elif padding_mode == "symmetric": + # route to another implementation + return _pad_symmetric(img, p) + + need_squeeze = False + if img.ndim < 4: + img = img.unsqueeze(dim=0) + need_squeeze = True + + out_dtype = img.dtype + need_cast = False + if (padding_mode != "constant") and img.dtype not in (torch.float32, torch.float64): + # Here we temporary cast input tensor to float + # until pytorch issue is resolved : + # https://github.com/pytorch/pytorch/issues/40763 + need_cast = True + img = img.to(torch.float32) + + img = torch_pad(img, p, mode=padding_mode, value=float(fill)) + + if need_squeeze: + img = img.squeeze(dim=0) + + if need_cast: + img = img.to(out_dtype) + + return img + + +def resize(img: Tensor, size: List[int], interpolation: str = "bilinear") -> Tensor: + _assert_image_tensor(img) + + if not isinstance(size, (int, tuple, list)): + raise TypeError("Got inappropriate size arg") + if not isinstance(interpolation, str): + raise TypeError("Got inappropriate interpolation arg") + + if interpolation not in ["nearest", "bilinear", "bicubic"]: + raise ValueError("This interpolation mode is unsupported with Tensor input") + + if isinstance(size, tuple): + size = list(size) + + if isinstance(size, list) and len(size) not in [1, 2]: + raise ValueError("Size must be an int or a 1 or 2 element tuple/list, not a " + "{} element tuple/list".format(len(size))) + + w, h = _get_image_size(img) + + if isinstance(size, int): + size_w, size_h = size, size + elif len(size) < 2: + size_w, size_h = size[0], size[0] + else: + size_w, size_h = size[1], size[0] # Convention (h, w) + + if isinstance(size, int) or len(size) < 2: + if w < h: + size_h = int(size_w * h / w) + else: + size_w = int(size_h * w / h) + + if (w <= h and w == size_w) or (h <= w and h == size_h): + return img + + img, need_cast, need_squeeze, out_dtype = _cast_squeeze_in(img, [torch.float32, torch.float64]) + + # Define align_corners to avoid warnings + align_corners = False if interpolation in ["bilinear", "bicubic"] else None + + img = interpolate(img, size=[size_h, size_w], mode=interpolation, align_corners=align_corners) + + if interpolation == "bicubic" and out_dtype == torch.uint8: + img = img.clamp(min=0, max=255) + + img = _cast_squeeze_out(img, need_cast=need_cast, need_squeeze=need_squeeze, out_dtype=out_dtype) + + return img + + +def _assert_grid_transform_inputs( + img: Tensor, + matrix: Optional[List[float]], + interpolation: str, + fill: Optional[List[float]], + supported_interpolation_modes: List[str], + coeffs: Optional[List[float]] = None, +): + + if not (isinstance(img, torch.Tensor)): + raise TypeError("Input img should be Tensor") + + _assert_image_tensor(img) + + if matrix is not None and not isinstance(matrix, list): + raise TypeError("Argument matrix should be a list") + + if matrix is not None and len(matrix) != 6: + raise ValueError("Argument matrix should have 6 float values") + + if coeffs is not None and len(coeffs) != 8: + raise ValueError("Argument coeffs should have 8 float values") + + if fill is not None and not isinstance(fill, (int, float, tuple, list)): + warnings.warn("Argument fill should be either int, float, tuple or list") + + # Check fill + num_channels = _get_image_num_channels(img) + if isinstance(fill, (tuple, list)) and (len(fill) > 1 and len(fill) != num_channels): + msg = ("The number of elements in 'fill' cannot broadcast to match the number of " + "channels of the image ({} != {})") + raise ValueError(msg.format(len(fill), num_channels)) + + if interpolation not in supported_interpolation_modes: + raise ValueError("Interpolation mode '{}' is unsupported with Tensor input".format(interpolation)) + + +def _cast_squeeze_in(img: Tensor, req_dtypes: List[torch.dtype]) -> Tuple[Tensor, bool, bool, torch.dtype]: + need_squeeze = False + # make image NCHW + if img.ndim < 4: + img = img.unsqueeze(dim=0) + need_squeeze = True + + out_dtype = img.dtype + need_cast = False + if out_dtype not in req_dtypes: + need_cast = True + req_dtype = req_dtypes[0] + img = img.to(req_dtype) + return img, need_cast, need_squeeze, out_dtype + + +def _cast_squeeze_out(img: Tensor, need_cast: bool, need_squeeze: bool, out_dtype: torch.dtype): + if need_squeeze: + img = img.squeeze(dim=0) + + if need_cast: + if out_dtype in (torch.uint8, torch.int8, torch.int16, torch.int32, torch.int64): + # it is better to round before cast + img = torch.round(img) + img = img.to(out_dtype) + + return img + + +def _apply_grid_transform(img: Tensor, grid: Tensor, mode: str, fill: Optional[List[float]]) -> Tensor: + + img, need_cast, need_squeeze, out_dtype = _cast_squeeze_in(img, [grid.dtype, ]) + + if img.shape[0] > 1: + # Apply same grid to a batch of images + grid = grid.expand(img.shape[0], grid.shape[1], grid.shape[2], grid.shape[3]) + + # Append a dummy mask for customized fill colors, should be faster than grid_sample() twice + if fill is not None: + dummy = torch.ones((img.shape[0], 1, img.shape[2], img.shape[3]), dtype=img.dtype, device=img.device) + img = torch.cat((img, dummy), dim=1) + + img = grid_sample(img, grid, mode=mode, padding_mode="zeros", align_corners=False) + + # Fill with required color + if fill is not None: + mask = img[:, -1:, :, :] # N * 1 * H * W + img = img[:, :-1, :, :] # N * C * H * W + mask = mask.expand_as(img) + len_fill = len(fill) if isinstance(fill, (tuple, list)) else 1 + fill_img = torch.tensor(fill, dtype=img.dtype, device=img.device).view(1, len_fill, 1, 1).expand_as(img) + if mode == 'nearest': + mask = mask < 0.5 + img[mask] = fill_img[mask] + else: # 'bilinear' + img = img * mask + (1.0 - mask) * fill_img + + img = _cast_squeeze_out(img, need_cast, need_squeeze, out_dtype) + return img + + +def _gen_affine_grid( + theta: Tensor, w: int, h: int, ow: int, oh: int, +) -> Tensor: + # https://github.com/pytorch/pytorch/blob/74b65c32be68b15dc7c9e8bb62459efbfbde33d8/aten/src/ATen/native/ + # AffineGridGenerator.cpp#L18 + # Difference with AffineGridGenerator is that: + # 1) we normalize grid values after applying theta + # 2) we can normalize by other image size, such that it covers "extend" option like in PIL.Image.rotate + + d = 0.5 + base_grid = torch.empty(1, oh, ow, 3, dtype=theta.dtype, device=theta.device) + x_grid = torch.linspace(-ow * 0.5 + d, ow * 0.5 + d - 1, steps=ow, device=theta.device) + base_grid[..., 0].copy_(x_grid) + y_grid = torch.linspace(-oh * 0.5 + d, oh * 0.5 + d - 1, steps=oh, device=theta.device).unsqueeze_(-1) + base_grid[..., 1].copy_(y_grid) + base_grid[..., 2].fill_(1) + + rescaled_theta = theta.transpose(1, 2) / torch.tensor([0.5 * w, 0.5 * h], dtype=theta.dtype, device=theta.device) + output_grid = base_grid.view(1, oh * ow, 3).bmm(rescaled_theta) + return output_grid.view(1, oh, ow, 2) + + +def affine( + img: Tensor, matrix: List[float], interpolation: str = "nearest", fill: Optional[List[float]] = None +) -> Tensor: + _assert_grid_transform_inputs(img, matrix, interpolation, fill, ["nearest", "bilinear"]) + + dtype = img.dtype if torch.is_floating_point(img) else torch.float32 + theta = torch.tensor(matrix, dtype=dtype, device=img.device).reshape(1, 2, 3) + shape = img.shape + # grid will be generated on the same device as theta and img + grid = _gen_affine_grid(theta, w=shape[-1], h=shape[-2], ow=shape[-1], oh=shape[-2]) + return _apply_grid_transform(img, grid, interpolation, fill=fill) + + +def _compute_output_size(matrix: List[float], w: int, h: int) -> Tuple[int, int]: + + # Inspired of PIL implementation: + # https://github.com/python-pillow/Pillow/blob/11de3318867e4398057373ee9f12dcb33db7335c/src/PIL/Image.py#L2054 + + # pts are Top-Left, Top-Right, Bottom-Left, Bottom-Right points. + pts = torch.tensor([ + [-0.5 * w, -0.5 * h, 1.0], + [-0.5 * w, 0.5 * h, 1.0], + [0.5 * w, 0.5 * h, 1.0], + [0.5 * w, -0.5 * h, 1.0], + ]) + theta = torch.tensor(matrix, dtype=torch.float).reshape(1, 2, 3) + new_pts = pts.view(1, 4, 3).bmm(theta.transpose(1, 2)).view(4, 2) + min_vals, _ = new_pts.min(dim=0) + max_vals, _ = new_pts.max(dim=0) + + # Truncate precision to 1e-4 to avoid ceil of Xe-15 to 1.0 + tol = 1e-4 + cmax = torch.ceil((max_vals / tol).trunc_() * tol) + cmin = torch.floor((min_vals / tol).trunc_() * tol) + size = cmax - cmin + return int(size[0]), int(size[1]) + + +def rotate( + img: Tensor, matrix: List[float], interpolation: str = "nearest", + expand: bool = False, fill: Optional[List[float]] = None +) -> Tensor: + _assert_grid_transform_inputs(img, matrix, interpolation, fill, ["nearest", "bilinear"]) + w, h = img.shape[-1], img.shape[-2] + ow, oh = _compute_output_size(matrix, w, h) if expand else (w, h) + dtype = img.dtype if torch.is_floating_point(img) else torch.float32 + theta = torch.tensor(matrix, dtype=dtype, device=img.device).reshape(1, 2, 3) + # grid will be generated on the same device as theta and img + grid = _gen_affine_grid(theta, w=w, h=h, ow=ow, oh=oh) + + return _apply_grid_transform(img, grid, interpolation, fill=fill) + + +def _perspective_grid(coeffs: List[float], ow: int, oh: int, dtype: torch.dtype, device: torch.device): + # https://github.com/python-pillow/Pillow/blob/4634eafe3c695a014267eefdce830b4a825beed7/ + # src/libImaging/Geometry.c#L394 + + # + # x_out = (coeffs[0] * x + coeffs[1] * y + coeffs[2]) / (coeffs[6] * x + coeffs[7] * y + 1) + # y_out = (coeffs[3] * x + coeffs[4] * y + coeffs[5]) / (coeffs[6] * x + coeffs[7] * y + 1) + # + theta1 = torch.tensor([[ + [coeffs[0], coeffs[1], coeffs[2]], + [coeffs[3], coeffs[4], coeffs[5]] + ]], dtype=dtype, device=device) + theta2 = torch.tensor([[ + [coeffs[6], coeffs[7], 1.0], + [coeffs[6], coeffs[7], 1.0] + ]], dtype=dtype, device=device) + + d = 0.5 + base_grid = torch.empty(1, oh, ow, 3, dtype=dtype, device=device) + x_grid = torch.linspace(d, ow * 1.0 + d - 1.0, steps=ow, device=device) + base_grid[..., 0].copy_(x_grid) + y_grid = torch.linspace(d, oh * 1.0 + d - 1.0, steps=oh, device=device).unsqueeze_(-1) + base_grid[..., 1].copy_(y_grid) + base_grid[..., 2].fill_(1) + + rescaled_theta1 = theta1.transpose(1, 2) / torch.tensor([0.5 * ow, 0.5 * oh], dtype=dtype, device=device) + output_grid1 = base_grid.view(1, oh * ow, 3).bmm(rescaled_theta1) + output_grid2 = base_grid.view(1, oh * ow, 3).bmm(theta2.transpose(1, 2)) + + output_grid = output_grid1 / output_grid2 - 1.0 + return output_grid.view(1, oh, ow, 2) + + +def perspective( + img: Tensor, perspective_coeffs: List[float], interpolation: str = "bilinear", fill: Optional[List[float]] = None +) -> Tensor: + if not (isinstance(img, torch.Tensor)): + raise TypeError('Input img should be Tensor.') + + _assert_image_tensor(img) + + _assert_grid_transform_inputs( + img, + matrix=None, + interpolation=interpolation, + fill=fill, + supported_interpolation_modes=["nearest", "bilinear"], + coeffs=perspective_coeffs + ) + + ow, oh = img.shape[-1], img.shape[-2] + dtype = img.dtype if torch.is_floating_point(img) else torch.float32 + grid = _perspective_grid(perspective_coeffs, ow=ow, oh=oh, dtype=dtype, device=img.device) + return _apply_grid_transform(img, grid, interpolation, fill=fill) + + +def _get_gaussian_kernel1d(kernel_size: int, sigma: float) -> Tensor: + ksize_half = (kernel_size - 1) * 0.5 + + x = torch.linspace(-ksize_half, ksize_half, steps=kernel_size) + pdf = torch.exp(-0.5 * (x / sigma).pow(2)) + kernel1d = pdf / pdf.sum() + + return kernel1d + + +def _get_gaussian_kernel2d( + kernel_size: List[int], sigma: List[float], dtype: torch.dtype, device: torch.device +) -> Tensor: + kernel1d_x = _get_gaussian_kernel1d(kernel_size[0], sigma[0]).to(device, dtype=dtype) + kernel1d_y = _get_gaussian_kernel1d(kernel_size[1], sigma[1]).to(device, dtype=dtype) + kernel2d = torch.mm(kernel1d_y[:, None], kernel1d_x[None, :]) + return kernel2d + + +def gaussian_blur(img: Tensor, kernel_size: List[int], sigma: List[float]) -> Tensor: + if not (isinstance(img, torch.Tensor)): + raise TypeError('img should be Tensor. Got {}'.format(type(img))) + + _assert_image_tensor(img) + + dtype = img.dtype if torch.is_floating_point(img) else torch.float32 + kernel = _get_gaussian_kernel2d(kernel_size, sigma, dtype=dtype, device=img.device) + kernel = kernel.expand(img.shape[-3], 1, kernel.shape[0], kernel.shape[1]) + + img, need_cast, need_squeeze, out_dtype = _cast_squeeze_in(img, [kernel.dtype, ]) + + # padding = (left, right, top, bottom) + padding = [kernel_size[0] // 2, kernel_size[0] // 2, kernel_size[1] // 2, kernel_size[1] // 2] + img = torch_pad(img, padding, mode="reflect") + img = conv2d(img, kernel, groups=img.shape[-3]) + + img = _cast_squeeze_out(img, need_cast, need_squeeze, out_dtype) + return img + + +def invert(img: Tensor) -> Tensor: + + _assert_image_tensor(img) + + if img.ndim < 3: + raise TypeError("Input image tensor should have at least 3 dimensions, but found {}".format(img.ndim)) + + _assert_channels(img, [1, 3]) + + bound = torch.tensor(1 if img.is_floating_point() else 255, dtype=img.dtype, device=img.device) + return bound - img + + +def posterize(img: Tensor, bits: int) -> Tensor: + + _assert_image_tensor(img) + + if img.ndim < 3: + raise TypeError("Input image tensor should have at least 3 dimensions, but found {}".format(img.ndim)) + if img.dtype != torch.uint8: + raise TypeError("Only torch.uint8 image tensors are supported, but found {}".format(img.dtype)) + + _assert_channels(img, [1, 3]) + mask = -int(2**(8 - bits)) # JIT-friendly for: ~(2 ** (8 - bits) - 1) + return img & mask + + +def solarize(img: Tensor, threshold: float) -> Tensor: + + _assert_image_tensor(img) + + if img.ndim < 3: + raise TypeError("Input image tensor should have at least 3 dimensions, but found {}".format(img.ndim)) + + _assert_channels(img, [1, 3]) + + inverted_img = invert(img) + return torch.where(img >= threshold, inverted_img, img) + + +def _blurred_degenerate_image(img: Tensor) -> Tensor: + dtype = img.dtype if torch.is_floating_point(img) else torch.float32 + + kernel = torch.ones((3, 3), dtype=dtype, device=img.device) + kernel[1, 1] = 5.0 + kernel /= kernel.sum() + kernel = kernel.expand(img.shape[-3], 1, kernel.shape[0], kernel.shape[1]) + + result_tmp, need_cast, need_squeeze, out_dtype = _cast_squeeze_in(img, [kernel.dtype, ]) + result_tmp = conv2d(result_tmp, kernel, groups=result_tmp.shape[-3]) + result_tmp = _cast_squeeze_out(result_tmp, need_cast, need_squeeze, out_dtype) + + result = img.clone() + result[..., 1:-1, 1:-1] = result_tmp + + return result + + +def adjust_sharpness(img: Tensor, sharpness_factor: float) -> Tensor: + if sharpness_factor < 0: + raise ValueError('sharpness_factor ({}) is not non-negative.'.format(sharpness_factor)) + + _assert_image_tensor(img) + + _assert_channels(img, [1, 3]) + + if img.size(-1) <= 2 or img.size(-2) <= 2: + return img + + return _blend(img, _blurred_degenerate_image(img), sharpness_factor) + + +def autocontrast(img: Tensor) -> Tensor: + + _assert_image_tensor(img) + + if img.ndim < 3: + raise TypeError("Input image tensor should have at least 3 dimensions, but found {}".format(img.ndim)) + + _assert_channels(img, [1, 3]) + + bound = 1.0 if img.is_floating_point() else 255.0 + dtype = img.dtype if torch.is_floating_point(img) else torch.float32 + + minimum = img.amin(dim=(-2, -1), keepdim=True).to(dtype) + maximum = img.amax(dim=(-2, -1), keepdim=True).to(dtype) + eq_idxs = torch.where(minimum == maximum)[0] + minimum[eq_idxs] = 0 + maximum[eq_idxs] = bound + scale = bound / (maximum - minimum) + + return ((img - minimum) * scale).clamp(0, bound).to(img.dtype) + + +def _scale_channel(img_chan): + hist = torch.histc(img_chan.to(torch.float32), bins=256, min=0, max=255) + + nonzero_hist = hist[hist != 0] + step = nonzero_hist[:-1].sum() // 255 + if step == 0: + return img_chan + + lut = (torch.cumsum(hist, 0) + (step // 2)) // step + lut = torch.nn.functional.pad(lut, [1, 0])[:-1].clamp(0, 255) + + return lut[img_chan.to(torch.int64)].to(torch.uint8) + + +def _equalize_single_image(img: Tensor) -> Tensor: + return torch.stack([_scale_channel(img[c]) for c in range(img.size(0))]) + + +def equalize(img: Tensor) -> Tensor: + + _assert_image_tensor(img) + + if not (3 <= img.ndim <= 4): + raise TypeError("Input image tensor should have 3 or 4 dimensions, but found {}".format(img.ndim)) + if img.dtype != torch.uint8: + raise TypeError("Only torch.uint8 image tensors are supported, but found {}".format(img.dtype)) + + _assert_channels(img, [1, 3]) + + if img.ndim == 3: + return _equalize_single_image(img) + + return torch.stack([_equalize_single_image(x) for x in img]) diff --git a/cv/detection/maskrcnn/pytorch/dataloader/utils/pascal_voc.py b/cv/detection/maskrcnn/pytorch/dataloader/utils/pascal_voc.py new file mode 100644 index 000000000..d60942541 --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/dataloader/utils/pascal_voc.py @@ -0,0 +1,311 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from collections import OrderedDict +import cv2 +# from functools import lru_cache +import os +import numpy as np +import xml.etree.ElementTree as ET +from PIL import Image + + + +from abc import ABCMeta, abstractmethod + +try: + import torch + from torch.utils.data import Dataset +except: + torch = None + Dataset = object + +# 继承 BaseDataset 的 class 需要实现 get_data, 并填充 image_ids 字段 +class BaseDataset(Dataset, metaclass=ABCMeta): + def __init__(self, transform, **kwargs): + super(BaseDataset, self).__init__() + self.pipeline = transform + self.image_ids = [] + + def __len__(self): + return len(self.image_ids) + + def __getitem__(self, idx): + image, target = self.get_data(idx) + if self.pipeline is not None: + image, target = self.pipeline(image, target) + return image, target + + @abstractmethod + def get_data(self, idx): + pass + + +class PascalVOC(BaseDataset): + ALL_CLASSES = ('__background__', + 'aeroplane', 'bicycle', 'bird', 'boat', + 'bottle', 'bus', 'car', 'cat', 'chair', + 'cow', 'diningtable', 'dog', 'horse', + 'motorbike', 'person', 'pottedplant', + 'sheep', 'sofa', 'train', 'tvmonitor') + + COLORS = ([0, 0, 0], + [128, 0, 0], [0, 128, 0], [128, 128, 0], + [0, 0, 128], [128, 0, 128], [0, 128, 128], + [128, 128, 128], [64, 0, 0], [192, 0, 0], + [64, 128, 0], [192, 128, 0], [64, 0, 128], + [192, 0, 128], [64, 128, 128], [192, 128, 128], + [0, 64, 0], [128, 64, 0], [0, 192, 0], + [128, 192, 0], [0, 64, 128]) + + CLASS_COLOR = OrderedDict(zip(ALL_CLASSES, COLORS)) + + def __init__(self, + data_dir, + split, + image_sets_dir='Main', + classes=None, + keep_difficult=False, + to_torch_tensor=True, + **kwargs): + """Dataset for VOC data. + + Parameters + ---------- + data_dir : str + the root of the VOC2007 or VOC2012 dataset, the directory contains the + following sub-directories: + Annotations, ImageSets, JPEGImages, SegmentationClass, SegmentationObject. + split : str + "train" or "val" + """ + super().__init__(**kwargs) + self.data_dir = data_dir + self.split = split + image_sets_file = os.path.join( + self.data_dir, 'ImageSets', image_sets_dir, '%s.txt' % self.split) + if classes is not None: + self.CLASSES = classes + if '__background__' not in classes: + self.CLASSES.insert(0, '__background__') + else: + self.CLASSES = self.ALL_CLASSES + + self.image_ids = PascalVOC.get_image_ids(image_sets_file) + self.keep_difficult = keep_difficult + self.class_dict = {class_name: i for i, class_name in enumerate(self.CLASSES)} + self.class_id_dict = {cls_id: name for name, cls_id in self.class_dict.items()} + self.to_torch_tensor = to_torch_tensor and torch is not None + + def get_data(self, idx): + image_id = self.image_ids[idx] + bboxes, labels, difficult = self._get_annotation(image_id) + # 忽略掉 difficult 的 object + if not self.keep_difficult: + bboxes = bboxes[difficult==0] + labels = labels[difficult==0] + image = self._read_image(image_id) + img_info = self.get_img_info(idx) + # 因为使用了 lru cache, 为了确保 cache 中的数据不被 pipeline 更改, + # 需要将原始数据的拷贝送入 pipeline + area = (bboxes[:, 2] - bboxes[:, 0]) * (bboxes[:, 3] - bboxes[:, 1]) + image_id = image_id.replace('-', '') + image_id = int(image_id) + + if self.to_torch_tensor: + target = dict(boxes=torch.from_numpy(bboxes), + labels=torch.from_numpy(labels), + image_id=torch.tensor(image_id), + area=torch.from_numpy(area), + iscrowd=torch.tensor([False] * len(bboxes))) + else: + target = dict(boxes=torch.from_numpy(bboxes.copy()), + labels=torch.from_numpy(labels.copy()), + image_id=image_id, + area=area.copy(), + iscrowd=[False] * len(bboxes)) + return image, target + + # 获取索引为 idx 的 sample 的标注信息 + def get_annotation(self, idx): + image_id = self.image_ids[idx] + bboxes, labels, difficult = self._get_annotation(image_id) + anno = dict(image_id=image_id, + bboxes=bboxes, + labels=labels, + difficult=difficult) + return anno + + # @lru_cache(maxsize=None) + def _get_annotation(self, image_id): + annotation_file = os.path.join(self.data_dir, "Annotations", "%s.xml" % image_id) + objects = ET.parse(annotation_file).findall("object") + boxes = [] + labels = [] + difficult = [] + for obj in objects: + class_name = obj.find('name').text.lower().strip() + if class_name not in self.CLASSES: + continue + bbox = obj.find('bndbox') + # VOC dataset format follows Matlab, in which indices start from 0 + x1 = float(bbox.find('xmin').text) - 1 + y1 = float(bbox.find('ymin').text) - 1 + x2 = float(bbox.find('xmax').text) - 1 + y2 = float(bbox.find('ymax').text) - 1 + boxes.append([x1, y1, x2, y2]) + labels.append(self.class_dict[class_name]) + difficult_str = obj.find('difficult').text + difficult.append(int(difficult_str) if difficult_str else 0) + + return (np.array(boxes, dtype=np.float32), + np.array(labels, dtype=np.int64), + np.array(difficult, dtype=np.uint8)) + + @staticmethod + def get_image_ids(image_sets_file): + ids = [] + with open(image_sets_file) as f: + for line in f: + ids.append(line.rstrip()) + return ids + + # @lru_cache(maxsize=None) + def get_img_info(self, idx): + image_id = self.image_ids[idx] + annotation_file = os.path.join(self.data_dir, "Annotations", "%s.xml" % image_id) + anno = ET.parse(annotation_file).getroot() + size = anno.find("size") + im_info = tuple(map(int, (size.find("height").text, size.find("width").text))) + image_path = os.path.join(self.data_dir, "JPEGImages", "%s.jpg" % image_id) + return dict(id=image_id, + file_path=image_path, + height=im_info[0], + width=im_info[1]) + + # @lru_cache(maxsize=None) + def _read_image(self, image_id): + image_file = os.path.join(self.data_dir, "JPEGImages", "%s.jpg" % image_id) + image = Image.open(image_file) + return image + + def get_class_name(self, class_id): + return self.class_id_dict[class_id] + + def get_class_color(self, class_id): + if not isinstance(class_id, str): + class_name = self.get_class_name(class_id) + else: + class_name = class_id + return self.CLASS_COLOR[class_name] + + + +class VOCInstanceSeg(PascalVOC): + IGNORE_IMAGE_IDS = ("2008_005953", "2008_007355") + def __init__(self, **kwargs): + super().__init__(image_sets_dir='Segmentation', **kwargs) + + if "ignore_image_id" in kwargs and (isinstance(kwargs["ignore_image_id"], list) or isinstance(kwargs["ignore_image_id"], tuple)): + self.ignore_ids = kwargs["ignore_image_id"] + else: + self.ignore_ids = self.IGNORE_IMAGE_IDS + + self.image_ids = [id for id in self.image_ids if id not in self.ignore_ids] + + def get_data(self, idx): + image_id = self.image_ids[idx] + bboxes, labels, masks, difficult = self._get_annotation(image_id) + # 忽略掉 difficult 的 object + if not self.keep_difficult: + bboxes = bboxes[difficult==0] + labels = labels[difficult==0] + masks = np.transpose(masks, axes=(2, 0, 1)) + masks = masks[difficult==0] + # masks = np.transpose(masks, axes=(1, 2, 0)) + image = self._read_image(image_id) + img_info = self.get_img_info(idx) + # 因为使用了 lru cache, 为了确保 cache 中的数据不被 pipeline 更改, + # 需要将原始数据的拷贝送入 pipeline + area = (bboxes[:, 2] - bboxes[:, 0]) * (bboxes[:, 3] - bboxes[:, 1]) + image_id = image_id.replace('-', '') + image_id = int(image_id) + if self.to_torch_tensor: + target = dict(boxes=torch.from_numpy(bboxes), + labels=torch.from_numpy(labels), + image_id=torch.tensor(image_id), + area=torch.from_numpy(area), + iscrowd=torch.tensor([False] * len(bboxes)), + masks=torch.from_numpy(masks)) + else: + target = dict(boxes=bboxes.copy(), + labels=labels.copy(), + image_id=image_id, + area=area.copy(), + iscrowd=[False] * len(bboxes), + masks=masks.copy()) + return image, target + + # @lru_cache(maxsize=None) + def _get_annotation(self, image_id): + annotation_file = os.path.join(self.data_dir, "Annotations", "%s.xml" % image_id) + objects = ET.parse(annotation_file).findall("object") + mask = self._read_mask(image_id) + boxes = [] + labels = [] + objmasks = [] + difficult = [] + for obj in objects: + class_name = obj.find('name').text.lower().strip() + if class_name not in self.CLASSES: + continue + bbox = obj.find('bndbox') + # VOC dataset format follows Matlab, in which indices start from 0 + x1 = float(bbox.find('xmin').text) - 1 + y1 = float(bbox.find('ymin').text) - 1 + x2 = float(bbox.find('xmax').text) - 1 + y2 = float(bbox.find('ymax').text) - 1 + boxes.append([x1, y1, x2, y2]) + labels.append(self.class_dict[class_name]) + difficult_str = obj.find('difficult').text + difficult.append(int(difficult_str) if difficult_str else 0) + + class_color = self.get_class_color(class_name) + objmask = self._get_objmask(mask, (x1, y1, x2, y2), class_color) + objmasks.append(objmask) + + if len(objmasks) == 1: + objmasks = np.expand_dims(objmasks[0], axis=-1) + else: + objmasks = np.stack(objmasks, axis=-1) + + # masks 的 shape 为 (height, width, N), 目的是方便复用对 img 的 transform 操作 + return (np.array(boxes, dtype=np.float32), + np.array(labels, dtype=np.int64), + objmasks, + np.array(difficult, dtype=np.uint8)) + + def _read_mask(self, image_id): + mask_file = os.path.join(self.data_dir, "SegmentationClass", "%s.png" % image_id) + mask = cv2.imread(mask_file) + return mask + + def _get_objmask(self, mask, bbox, class_color): + im_heigt, im_width = mask.shape[0], mask.shape[1] + xmin, ymin, xmax, ymax = [int(coor) for coor in bbox] + + objmask = np.zeros((im_heigt, im_width), dtype=np.uint8) + for i in range(ymin, ymax): + for j in range(xmin, xmax): + # mask 为 BGR, class_color 为 RGB + if mask[i,j,0] == class_color[2] and \ + mask[i,j,1] == class_color[1] and \ + mask[i,j,2] == class_color[0]: + objmask[i,j] = 1 + + return objmask + + +def get_voc(root, image_set, transforms, to_torch_tensor=True): + return VOCInstanceSeg(data_dir=root, split=image_set, transform=transforms, to_torch_tensor=to_torch_tensor) \ No newline at end of file diff --git a/cv/detection/maskrcnn/pytorch/dataloader/utils/presets_detection.py b/cv/detection/maskrcnn/pytorch/dataloader/utils/presets_detection.py new file mode 100644 index 000000000..75946f078 --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/dataloader/utils/presets_detection.py @@ -0,0 +1,41 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from . import transforms_det as T + + +class DetectionPresetTrain: + def __init__(self, data_augmentation, hflip_prob=0.5, mean=(123., 117., 104.)): + if data_augmentation == 'hflip': + self.transforms = T.Compose([ + T.RandomHorizontalFlip(p=hflip_prob), + T.ToTensor(), + ]) + elif data_augmentation == 'ssd': + self.transforms = T.Compose([ + T.RandomPhotometricDistort(), + T.RandomZoomOut(fill=list(mean)), + T.RandomIoUCrop(), + T.RandomHorizontalFlip(p=hflip_prob), + T.ToTensor(), + ]) + elif data_augmentation == 'ssdlite': + self.transforms = T.Compose([ + T.RandomIoUCrop(), + T.RandomHorizontalFlip(p=hflip_prob), + T.ToTensor(), + ]) + else: + raise ValueError(f'Unknown data augmentation policy "{data_augmentation}"') + + def __call__(self, img, target): + return self.transforms(img, target) + + +class DetectionPresetEval: + def __init__(self): + self.transforms = T.ToTensor() + + def __call__(self, img, target): + return self.transforms(img, target) diff --git a/cv/detection/maskrcnn/pytorch/dataloader/utils/transforms_det.py b/cv/detection/maskrcnn/pytorch/dataloader/utils/transforms_det.py new file mode 100644 index 000000000..405e760c0 --- /dev/null +++ b/cv/detection/maskrcnn/pytorch/dataloader/utils/transforms_det.py @@ -0,0 +1,243 @@ +# Copyright (c) 2022 Iluvatar CoreX. All rights reserved. +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + + +from typing import List, Tuple, Dict, Optional + +import torch +from torch import nn, Tensor +import torchvision +from torchvision.transforms import transforms as T +from . import functional as F + + +def _flip_coco_person_keypoints(kps, width): + flip_inds = [0, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 16, 15] + flipped_data = kps[:, flip_inds] + flipped_data[..., 0] = width - flipped_data[..., 0] + # Maintain COCO convention that if visibility == 0, then x, y = 0 + inds = flipped_data[..., 2] == 0 + flipped_data[inds] = 0 + return flipped_data + + +class Compose(object): + def __init__(self, transforms): + self.transforms = transforms + + def __call__(self, image, target): + for t in self.transforms: + image, target = t(image, target) + return image, target + + +class RandomHorizontalFlip(T.RandomHorizontalFlip): + def forward(self, image: Tensor, + target: Optional[Dict[str, Tensor]] = None) -> Tuple[Tensor, Optional[Dict[str, Tensor]]]: + if torch.rand(1) < self.p: + image = F.hflip(image) + if target is not None: + width, _ = F._get_image_size(image) + target["boxes"][:, [0, 2]] = width - target["boxes"][:, [2, 0]] + if "masks" in target: + target["masks"] = target["masks"].flip(-1) + if "keypoints" in target: + keypoints = target["keypoints"] + keypoints = _flip_coco_person_keypoints(keypoints, width) + target["keypoints"] = keypoints + return image, target + + +class ToTensor(nn.Module): + def forward(self, image: Tensor, + target: Optional[Dict[str, Tensor]] = None) -> Tuple[Tensor, Optional[Dict[str, Tensor]]]: + image = F.to_tensor(image) + return image, target + + +class RandomIoUCrop(nn.Module): + def __init__(self, min_scale: float = 0.3, max_scale: float = 1.0, min_aspect_ratio: float = 0.5, + max_aspect_ratio: float = 2.0, sampler_options: Optional[List[float]] = None, trials: int = 40): + super().__init__() + # Configuration similar to https://github.com/weiliu89/caffe/blob/ssd/examples/ssd/ssd_coco.py#L89-L174 + self.min_scale = min_scale + self.max_scale = max_scale + self.min_aspect_ratio = min_aspect_ratio + self.max_aspect_ratio = max_aspect_ratio + if sampler_options is None: + sampler_options = [0.0, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0] + self.options = sampler_options + self.trials = trials + + def forward(self, image: Tensor, + target: Optional[Dict[str, Tensor]] = None) -> Tuple[Tensor, Optional[Dict[str, Tensor]]]: + if target is None: + raise ValueError("The targets can't be None for this transform.") + + if isinstance(image, torch.Tensor): + if image.ndimension() not in {2, 3}: + raise ValueError('image should be 2/3 dimensional. Got {} dimensions.'.format(image.ndimension())) + elif image.ndimension() == 2: + image = image.unsqueeze(0) + + orig_w, orig_h = F._get_image_size(image) + + while True: + # sample an option + idx = int(torch.randint(low=0, high=len(self.options), size=(1,))) + min_jaccard_overlap = self.options[idx] + if min_jaccard_overlap >= 1.0: # a value larger than 1 encodes the leave as-is option + return image, target + + for _ in range(self.trials): + # check the aspect ratio limitations + r = self.min_scale + (self.max_scale - self.min_scale) * torch.rand(2) + new_w = int(orig_w * r[0]) + new_h = int(orig_h * r[1]) + aspect_ratio = new_w / new_h + if not (self.min_aspect_ratio <= aspect_ratio <= self.max_aspect_ratio): + continue + + # check for 0 area crops + r = torch.rand(2) + left = int((orig_w - new_w) * r[0]) + top = int((orig_h - new_h) * r[1]) + right = left + new_w + bottom = top + new_h + if left == right or top == bottom: + continue + + # check for any valid boxes with centers within the crop area + cx = 0.5 * (target["boxes"][:, 0] + target["boxes"][:, 2]) + cy = 0.5 * (target["boxes"][:, 1] + target["boxes"][:, 3]) + is_within_crop_area = (left < cx) & (cx < right) & (top < cy) & (cy < bottom) + if not is_within_crop_area.any(): + continue + + # check at least 1 box with jaccard limitations + boxes = target["boxes"][is_within_crop_area] + ious = torchvision.ops.boxes.box_iou(boxes, torch.tensor([[left, top, right, bottom]], + dtype=boxes.dtype, device=boxes.device)) + if ious.max() < min_jaccard_overlap: + continue + + # keep only valid boxes and perform cropping + target["boxes"] = boxes + target["labels"] = target["labels"][is_within_crop_area] + target["boxes"][:, 0::2] -= left + target["boxes"][:, 1::2] -= top + target["boxes"][:, 0::2].clamp_(min=0, max=new_w) + target["boxes"][:, 1::2].clamp_(min=0, max=new_h) + image = F.crop(image, top, left, new_h, new_w) + + return image, target + + +class RandomZoomOut(nn.Module): + def __init__(self, fill: Optional[List[float]] = None, side_range: Tuple[float, float] = (1., 4.), p: float = 0.5): + super().__init__() + if fill is None: + fill = [0., 0., 0.] + self.fill = fill + self.side_range = side_range + if side_range[0] < 1. or side_range[0] > side_range[1]: + raise ValueError("Invalid canvas side range provided {}.".format(side_range)) + self.p = p + + @torch.jit.unused + def _get_fill_value(self, is_pil): + # type: (bool) -> int + # We fake the type to make it work on JIT + return tuple(int(x) for x in self.fill) if is_pil else 0 + + def forward(self, image: Tensor, + target: Optional[Dict[str, Tensor]] = None) -> Tuple[Tensor, Optional[Dict[str, Tensor]]]: + if isinstance(image, torch.Tensor): + if image.ndimension() not in {2, 3}: + raise ValueError('image should be 2/3 dimensional. Got {} dimensions.'.format(image.ndimension())) + elif image.ndimension() == 2: + image = image.unsqueeze(0) + + if torch.rand(1) < self.p: + return image, target + + orig_w, orig_h = F._get_image_size(image) + + r = self.side_range[0] + torch.rand(1) * (self.side_range[1] - self.side_range[0]) + canvas_width = int(orig_w * r) + canvas_height = int(orig_h * r) + + r = torch.rand(2) + left = int((canvas_width - orig_w) * r[0]) + top = int((canvas_height - orig_h) * r[1]) + right = canvas_width - (left + orig_w) + bottom = canvas_height - (top + orig_h) + + if torch.jit.is_scripting(): + fill = 0 + else: + fill = self._get_fill_value(F._is_pil_image(image)) + + image = F.pad(image, [left, top, right, bottom], fill=fill) + if isinstance(image, torch.Tensor): + v = torch.tensor(self.fill, device=image.device, dtype=image.dtype).view(-1, 1, 1) + image[..., :top, :] = image[..., :, :left] = image[..., (top + orig_h):, :] = \ + image[..., :, (left + orig_w):] = v + + if target is not None: + target["boxes"][:, 0::2] += left + target["boxes"][:, 1::2] += top + + return image, target + + +class RandomPhotometricDistort(nn.Module): + def __init__(self, contrast: Tuple[float] = (0.5, 1.5), saturation: Tuple[float] = (0.5, 1.5), + hue: Tuple[float] = (-0.05, 0.05), brightness: Tuple[float] = (0.875, 1.125), p: float = 0.5): + super().__init__() + self._brightness = T.ColorJitter(brightness=brightness) + self._contrast = T.ColorJitter(contrast=contrast) + self._hue = T.ColorJitter(hue=hue) + self._saturation = T.ColorJitter(saturation=saturation) + self.p = p + + def forward(self, image: Tensor, + target: Optional[Dict[str, Tensor]] = None) -> Tuple[Tensor, Optional[Dict[str, Tensor]]]: + if isinstance(image, torch.Tensor): + if image.ndimension() not in {2, 3}: + raise ValueError('image should be 2/3 dimensional. Got {} dimensions.'.format(image.ndimension())) + elif image.ndimension() == 2: + image = image.unsqueeze(0) + + r = torch.rand(7) + + if r[0] < self.p: + image = self._brightness(image) + + contrast_before = r[1] < 0.5 + if contrast_before: + if r[2] < self.p: + image = self._contrast(image) + + if r[3] < self.p: + image = self._saturation(image) + + if r[4] < self.p: + image = self._hue(image) + + if not contrast_before: + if r[5] < self.p: + image = self._contrast(image) + + if r[6] < self.p: + channels = F._get_image_num_channels(image) + permutation = torch.randperm(channels) + + is_pil = F._is_pil_image(image) + if is_pil: + image = F.to_tensor(image) + image = image[..., permutation, :, :] + if is_pil: + image = F.to_pil_image(image) + + return image, target diff --git a/cv/detection/maskrcnn/pytorch/demo/README.md b/cv/detection/maskrcnn/pytorch/demo/README.md deleted file mode 100644 index 393a064b0..000000000 --- a/cv/detection/maskrcnn/pytorch/demo/README.md +++ /dev/null @@ -1,45 +0,0 @@ -## Webcam and Jupyter notebook demo - -This folder contains a simple webcam demo that illustrates how you can use `maskrcnn_benchmark` for inference. - - -### With your preferred environment - -You can start it by running it from this folder, using one of the following commands: -```bash -# by default, it runs on the GPU -# for best results, use min-image-size 800 -python webcam.py --min-image-size 800 -# can also run it on the CPU -python webcam.py --min-image-size 300 MODEL.DEVICE cpu -# or change the model that you want to use -python webcam.py --config-file ../configs/caffe2/e2e_mask_rcnn_R_101_FPN_1x_caffe2.yaml --min-image-size 300 MODEL.DEVICE cpu -# in order to see the probability heatmaps, pass --show-mask-heatmaps -python webcam.py --min-image-size 300 --show-mask-heatmaps MODEL.DEVICE cpu -``` - -### With Docker - -Build the image with the tag `maskrcnn-benchmark` (check [INSTALL.md](../INSTALL.md) for instructions) - -Adjust permissions of the X server host (be careful with this step, refer to -[here](http://wiki.ros.org/docker/Tutorials/GUI) for alternatives) - -```bash -xhost + -``` - -Then run a container with the demo: - -``` -docker run --rm -it \ - -e DISPLAY=${DISPLAY} \ - --privileged \ - -v /tmp/.X11-unix:/tmp/.X11-unix \ - --device=/dev/video0:/dev/video0 \ - --ipc=host maskrcnn-benchmark \ - python demo/webcam.py --min-image-size 300 -``` - -**DISCLAIMER:** *This was tested for an Ubuntu 16.04 machine, -the volume mapping may vary depending on your platform* diff --git a/cv/detection/maskrcnn/pytorch/demo/demo_e2e_mask_rcnn_R_50_FPN_1x.png b/cv/detection/maskrcnn/pytorch/demo/demo_e2e_mask_rcnn_R_50_FPN_1x.png deleted file mode 100644 index 406351186fd7660e23f09f1e3f7c4aef658d9b02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 920219 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®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⁣&q_m~yi2dSjWb{YMNO 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⫬$}Bx~+_zMYIGwC`&I+dXt6)Z!B&tbj zsibaKNu+Q#hOW#jtZ!C}TYM#_E7^uwNs#SyPD@b>&RHJJY~_Qe5QPW%xHLR(m-b5( z_C}CHg!@FNY2P`ubcHNirWYJ=@ui~f3!=?1K9Ujb)8Hq%{*6@9qZNCfxrmfmMW#6W zbBVd>-@yqUUUC3fV;*?u^2H_B>Z>pJHWFvm)w&ty$oEZx>dkBq!S1e?@+dkH$p1sN z`QIYWJ;vix<&fmd<%V}{I!{?vVP1&y18*0zY!T~Uqxd6&}MA z*u$FQhqAsx4nsO8hsb(&f}g$6#|}<@`G4AQs=M=M{~yYxqn$V9*;|#@#2L4;SF+a4 zDy*6e=3vr+*a)z&6hJF)>GWxn;&IS)nbE7f)SW|%CT*s$Uo$z$VJR>vc~IueAWb?s ziGE=1=KaGR!?OeysD-djZXpVC1(#!~G-Y#)bj3XpE@jNnJ7f$ydFf(};jQz}^d+DM zGc^fL#-)e_v2(0P@ASXw+s+^IiS$K2z zd~>tT4wHqADhUrtZ-&+wa5fLlQP!?WL@L5~GB*PBjlM|px~GUWht|u?FP@@1xVt7A zt58~)oP51q&`jxUx$}s5ELM3SDeI!*9>e9qd+9e!iR&F1DU&2%8;`zVZyNoaUy(0_ z`R^bR!=Y3*d)X?a6BJe^*6~&!*ocb&^xWKc5R1_uKYx7Hk`vOzxV?+-x^o73E1eOO zx_O+wpudoXwTK8o`$Ftd03qfBa02=LL#vo;69jI%aKD zkv8bW*cD|_)5y>0TP&veH>!B()xo-m7mFXK<7cLS&DgTLC8esDZ~KGSGZwtV`x4#Y z{-IiA0O#qRZGCs#;1Nsg4Y+5DVS){gcg#={NH(th@rSMf>ggQByMKYC{_eM|xU^Z8x$R?c7j#e6toxyMh^}So@THpx2yj+ z&Ksr%gOhc|K%pYR>PVY~quKwCo2!l8he?sHmyxcAUR;Q%5Fb@8NFk|IxtI-#q=a*O zWsMdLnxid|lw5HkNQ{DrUeKW}J9)3k_Z7TcsR~F9dF`T)1IgO!(2@DtRr2btAxN+-0Q5l^F$AMD znfmPIVl+8SW~WnqR7bB94>%X2AHz58g{;+1`B9k5`AFnWU@2(`d((@kNT1oYlzKVx zCCHBw6fPct>qmofhRE`pD^t^a2MzsRdxW zzSZu-X-;C#AivuqFw6fy%C7o{=knRo`|uq`u6)WBlO;yGM1k7C-w6O@ZgtBFpn0x} zEQ~Kg|0|1VpCx&*)>o*&s&$TP?4>B9@Vnn&^ujbt4@=0AMx3YE1e&6FJaEBipXuH zk6jOdxuKlmqVrj%EEjBDkg2r|FGkE}oP5s?HW#76dF5#y}KbRPpmB+;V@i6O4G zmA13fhnChmB)#9+Y(Ix3DD9OV9$Kt9eRqOTKId<718_whcmPrS_4RQatn=;rWwzlX zW{1bbl$sjQQ9EqjNSE;N+dRv$P7n#6K(cscRM+HpeYU5$Au?rFIXw5Wt{542K0*RICJ3UV1xbd`EPnVYjncGd?JFP5|`nE{h2m*;wLqrHMb& zHwCrYeyeuycY-5dvbK^JiU$YReix+lS>>GIG_0weO%Or7TiqX_IVy9dOODLtI<%Mr zv#2>IhnD-uEMCNlnMlbJrjhcvk)qRVB3iAjZDWg( zWi7He!$2z>T&UhLD{V5G9`eiBoJ78|2}_lk{)^FEx$G*UJGLI;o7o#ri!t$qY{^MM{eWjd0NSJA(dU>ZMuJXKLf zo=iNC(~tmzg#95qc9G=1rx@Gq5!uvfVc2?D0q4&lnhrJ^>=^P$Lq9t)6i<c%-zgx1OAN+b%RXrZg#nU@I0yo?H8Qn11SktA0yZOMmMEMU^*oG&z;vtBIGrZ;+(m zc=1HUtcU46nSL^^x3&?^{YRgc+TR2JHSp{*MY3k)s^U;BmCRx*|95$gq_ z16i&p$w1DXtY*p74Bxp|;H}2V`rh26e-_kNdk=oYTI~;0)E-P^l2oPpw0yCS!lt*I zzrvSFgk|JON7y~}6Eo;?my~q#C73@)G$65+>ypGgXukhP_<4Ns~;4Zq`Bss;KXW9MU^9jE3s zli`Q>z4_@ZkOqOU>>mPSQ)B!XcI&2oKjied{pgDCKe+{M%{RcOEw8x~-q$qh8hfd} zFFw^I$RsqDGMY?9l}4RI7pNp4Lx%?IDavzD7=WAvO4%$4hmfIT`D7bh` zn_6e;ohq4W6Iydg276l>0b$R+|%Ti9M2UYPa@s-v%s|9?iZV% zY9v>?`LB;gMkgE4!97vetVj1kJw0oe_%T9ojk(tp%JWxn_(+8J{!WmS7=Z^ED1VO*M`Va+$@TVc3n3@%Y zmfFf-4d{cc(=C~(oo9{ZN&~TL87?dv1&~dr`?B~0<-C{tB{m3&fvY)9G4Meb3}nk& z5E;J#;j{zqXfoZM*lK0EtO?%v-i8kU{1pCj09c1TrPnKT=#LbwYftf0O5)lJQkPiB0BAE4a2?bYQLTqm2e%W;l_rLA4s===~|#DLemHu%F}z zG$zI*MW0r%EcyEA&!?4i4yB7c308v)6Z2c%VeI=?e6d#|v4dQa8{5&Qd3>EFk3UtQdJpd~r6}_DP*|2O zt*palf+%zj#%@h;X%WCt0=@x1hx`5Pq@G>a2Gmm(aT|_CQ)PLGPz5E~)T40GnZ(PH zoMUtPrPV$Y;g@ z*)a6P$#EZ!`GorN=n8Q91})fs{9wD$S+X;>@rf-E+-iTsAk0dk_>0>aO`27+@OM^> z)jcN>aB0!?ZItHlY-P+NN}N1-P`2HWI$OmfppCotB%&aOCRBHtJB6IQIPZgs{M$;K zA;(UyHUe3%-h$)at1OsJ>#il>o*0$7i07x#ktF>2*0kJXTMq&9LuA@j6h=A~w=YsM zq$|*QTOo{V>onV^oN9#OH1!##a}ANoEy8O*{qT`@@Qsgw)gm?21^ zgXyW3AJ$Yw=Sg`b5N4X9sgyTpUMPXtJ9wUE{JU_S#*WFNrkHM0MNBsTlBr{)m;U%Y z1RsDlIW-LQd<-7)t?Ul%H~qGSr=vMaIHoV+%0q6Q@H*%UauLj^FcyynU#Nl+_gq(s73||Uae1l$3vR_{V?(TG9l77$W^=kt8J&@Y{ zA2)=>G_!Y6bY7l zxhNJh8z3F1X8)G~`}%90Y#sWR?Whfg*t8FTzoiI~Tsg4+hnlh?`?ZW9YzeG?NrQ$+ zfghlR={9T}EnYa3+oPMFLXw;Nv*sfaDVaA$^WQ*a@6G@d{zw%G;^mH?EmNv*DF$MR-n&ZiI=A)sO7(Sj!e0f_fq%#<(5Ai@3=>OIqBCSQVLR9g|g- zNA}i=6r2*@%Jm{$8!kM&t-QR`i(wA@vTjPYIo(EiE z;|q&3qJ)IvO#>l=`pBmSW5gnEslQ!z*SFnWfP1cNqL1SG9Dz zBYfPA-4dcI75yF;O#l3OnP+?v)i&|4OYkO+SyZ330&oKLXs|yr5`Rr`__`uuBt+q} za(eyMjr*=%4FBa`#@;WkA23?b-}Tz{v><#t@ELs_*Q-PH8S59D{`Rv!o%VTYs-+Dp z{wb6Snx@nqp=sc&=)sar=R`yWOnAg8rO|e|H9j07YcSa&sW^7@)XQik<=oyB7#KJH zne2;zT8uo^&ZfX?xN9Lr66!$#&{~Lam%DNCcTP5(mh7j#wHAx-E3aex$VDbCA<-vj zoI-N4HYg~f*h0bC!?Ot+2}$>H)WK1yn9a+Y!~(j{Sx&_PMWT&DvcY+h0=dD#E=(z$ zo@QA|HdD))kLqaBR#rNO&+O6bW`>J2=(~<>Y4fNuEku;7Ay2q7jkKCg>OA6=forCLF~r;Qq})1GET2(V$(@X55b53e zQ|`u!*K<-5|0VaLoNs-4pUq!iyULM_XRvv2eQP?d$fVSj7$qa_T{#dqTlkwemm<0j z*gymGY%l6r=(;ClWQGnBg4=UeA+FMn(t`zlaZ?AuUQrhHc-rhI9U2_|>;c7Ck+8kc=)*m`!L8|Zh_3J(+{oAl;QLfh345?MI$AED8Ll_o6P541r4fxxaZ=u?4`E|*~(l0Gw5i_i<$3-L*oA+~SJ99^K>uanDPlt)0Bc|`5 zQZc3QR~N0N4V?k2cyC+wrF|kBb#+&9%FJH!IBy;My_#hnNo-^_r_;cu=`gjmet0*< zP0URRJ;`!sn4gZm7zp!NJslwuVOl?+%J1p{UCbL0x&9|G)vUZnUY^o_^HIj)$~F!- zZ4jmxP*^|XPwZgT>iLXk)J&i|?Uwjm2nQLl4iSo-Tp7|Xl!sUxT*SXFw8_qN3zs)| z5uWpf-9sQ8FL!L6z1D0)pRS%`{3eprdcn${vE)q0KKcc#SLc-;fNjp1=`g3S7q&+A zQr?#C0;Mm@)(Jf10M=Pf!v#oL*}Cv@%g6UhSd{5HXk07Abw#uJI2r*_Zn_hu9wqoO zD+iucPbUFIriwx4zl&&J%ks{Fz(ZHC*lW_v`I^kd6-2ydUv7KXj>S z-Zy=o4mSGv+i>W%;^SC;2BD{!vcF-_}54K!U`F@QxsE9{R}F zgTp-$Os9eJi#$}EKIO~*RC+>XIg%DXn9)wsEB)>zYwQlqN%NKClcziC&BPUH zT$ZS?7tJe;Q?D)k3god~Sv;tlF8SIZXQO#=e0lu_1YBa74sl@s3{Dwo;ptnSnDe_{ zbL;TLbHa@ zR(w?S;wSsok#0xG0w^AARF#pF>;4be7glK;=H>m(xg^${L+HHj?k~7{bnK3Qtz+@N zqAGCIJQtFD3cUwshw?~{K6`eVrW6znB~bbbE;FeBy=Z3!> zBSu}LGhmW*K`M>-=!&+A&+uj(yWFwe3@8a`nyp(K~d3+sFWZ8hXqa+kR6kj@?pPltW+1%9qhSivd3 z=!|A3;1P@zk)q!_P(4ka&Ncf+hwM@ztsn=&Rg5rg&VU}Nv?T<+8z+DcQN_<>_xq5qO3tKM7jZf)k+&VX3D&ZPtC7S)&GA&y?_0o zy%fzw_Um-A^mm?k3!H|x!emnD?@#w6WO+Ys3<)l3+-nr;a>|$&Yht_#Oybr~)Mb2l z_+|9JliZ|Jja8adB^QE)W@8M`hxWWW&>wKoBEki#^5B=;jTqsn7e{mW`?pseU2M;) zeEtaomS3Ee<4{eTH)Lq0YvEVSGU6RRN?zD#ZFoYuF@(-PZcega0RBU-7nQFOwQZ|Q z?z*lxVN?h)0kQ!>@D?!Ask<`)Cr=rn=PptN@uYmQ$D_WH$)?`Qcdr zjqc-a4^0{-A94BzI^u{Ri@gUhavbh)Q=8=A;FoB5*X;mzKe=z?D?;u!sCZ01ry}=R z$&&^&YK$rqvh!y>2aNY1(rUHNW-cNppFwiYg~hH~gzfxPF~+uvw>^-uKTZ__wKApzG<)TRQw56d?JNT zec%20NrBIXg0078@Spa`W_kj%GK@Nkj|40MbXb*sgQt4@8Y~OBqR;Fji&H4#__w3}^ze!6KD^2QUZ=tmpB3MSlv#H`$4!tzhEeMTGBnIhiOMl1yo{_UDAWIK{&^sw zExiw$$dQd<(ukx*Hf#It*dW$3+u!X~zphYVhR>26CpUE{N`ODp#x5o_FrUN@z{5(JAG#~j0Y>Jb`n;3D< z2pcVA;Fe;6S#^UjR#81caq(pm)_ghQJ(LysrpKs zCy8mny?yVcLylI0EK(}Ehn9un2xBsY0yd6&mXZ{*Z+j?jH##SJ3Xar_*mIKOCW#{2 zzgOqa;k8T7oZ9~t8;(zZ@+e@^D`~EA7H!rs9x=;v{pKrvPlWGK26{t z1$BxENMD95?Y&4xhH9kh22iWcWom43k$pgk!ejAG%;Z6Bd~@z0)6~+*M-pR~ z|5gUIO_H2RSzLj1JXqGIfDOmQ(k_UmU-dcp_0~UU)f)3U6e>G6ng9fvQhe<_s)1-Y zkJ@N3MVWkwRymqFjZ@*n)VX~GNlG#zs{|m8T0mCVR^HJ3iHq|~&yM-C(=EB^ z=9p*c-)2D?VNtFns(!cQn;gp|xRoSXze_6qr2a0~=UkMB4id6e$zm;bwk(5!JR8G$ zt^##yc0MNHNn=P1T+rugL^Tyf%a=gG*Cu?{&}8ptA>ny$u^HR>KbYc(*-~+RuI$*B z^Xe<)&IvFy*e$zD_*A9F=nNm#Tm&WGAh*x|X~pjYX|Y4Zs$y+M@8MQ&q;4hNn10#} zUH;L)mEL$Xp72j>Zq204#<=Lqwi)V$aX1#}vfJ(+dU3Y~nYuI(A%yY_{Cq#jNMT@l zEKyz&O;(V_C691x+5rYh1pn);QzHSo7y9H;4(!bBC(qIX_yH-e8lpm^titY9FI~1z zFkn3A%Cq zYr3k9x<0l+o_nZbDyZ`dRh{DO+IzZUNlAI~UC+SZrWoX+4@mFKCpzS;0XOtZ#GJfj zeYwjz$i2)mK@z`_8EqT>7hPu+6h{{>+9U)B!Gb#kcX$5@Fu1$R;BFZlg6lAW!QI{6 z3BlcAaCZytaJjeYyqv20wBP!ryQ{mtz4ltk?67+5yWRo$%&fdt-*9Rs5uT(9C-(0X z?FOtyQe;R!UlrDRv(JdFtjXR)}9zNXwH` zQkVLYj)Om8 z2I)R}J#Le)NS2K5wV~qLW1vKetrtO5IftJ7e6HvT4N`ahGtG&}Mkk|*A*yvev-*%U z^q;lfN&1XkKQ``D${}wFnFye$zm}K`CtQhT@-e+9cil3dh+{gO*(UL8a)tA?2ETtE zJhCOpW#`S=@0(t7jEZ(~s};oyHU-VGr}SAqdRxKKP)X7Rn}DD^VHeMmVt^^$kR{rO zYN9cDrm^2ggZ#HC`q2b7EAqvQw>;rsgz}QoLideLX9K&bG~m@r%L4`_JTt^9!>9f( zo|-$&lUt-8#1o)Ez9QQvKw6g7y-(@7MQX!e^8~1a4;tuBR=F;a&G5e+XnH++kN+-+ zdfDzAj9ks1dU1fi_Aoo+jv!yfYp}67fSmV@p&`_g7b`ZHQ`R*PeX;FtX9|8MyO?b@ zfSH*a5<6WYX9#|ES_B-RH2Qg(QLf$F11?OH1)jX)qvfDdFGChL$HBw>>BN^#s>pxP z^Ai3UQ9G)B&9fI*PVs#tSdbN$@G z@0wVl7~&$Ao|v<0{&$P2?2F^II2%3+Ha5!kfpHd66Snd@{2<93B5Xa+BYAOfyd{2~ZqqZ2x!^hy$ab(b}dsuXd3dY@?yOlbV`kY6UO{GNROn>or zAiX$x*8HfMycv;u{6EA`D{4v0{+GvH-BQEWde5NHIYKN1q`ws=db9Q{{*`(P(L&ZQZpdZvy_T#DmC)3Yih8H(<+r& z_u9k>3m+w=AUzD}QB%l@$WEW~)X;@rHqQWfy#MPZ-|xgbFy$I*d0AXS97WV(D>joh zf1wXJpb$uU?p(0>c0m-*sIA6tU}5o!9Zfa3_-uxp5vb~Y0%}CZipD1O_FU>-d#WuAvQNK&+r&Rj(kw>!Cs8sLw z#`4m`TxTo%L~OgZ*gt#m>jY)Edzuyp#hr?3BZ|g6gE_hVWHx%(UrBUH7Oz4=oa^D7 z>m8b6`?E)?3oY7zhIxoP_<4y_*XN3X7SNNwhKuxBD8^Rd$Q0|O_*LPRDW1Tp7Y2wL zISYwbo)#Q0=ZUl)Ub9B%KKru;4v7BA$B=sNR&w}<_}0hjDoX#m$16ukJGL!(%!0In z4D)N$D=~5T)pUW@J=9Uo7<4#{(zLF0f$WHc0iaHxX<>}f+J^03FD4zdNhrAvarN0{ znrdQuGBwJZnI;N;z3lR_Nq85BCuKe5{-Gl~D;GuP>hw5duw7QF0Y{*&F2hVsg#ZTK zS8iUMQwLlGb3|)aiT@ak%s6!LZ*2~^Oeve*F(Vz_+|qvKcp`3MG(_Jy^gXxb12-=_ z;e?TuK4h)tioNpJZqa99k*DkaI-Pf49h$gSloq&I+UzR0h^Bg}x00j?jZIL#IpZsC zLkC!A?B6LU?d1HFom9iO6M-n?M{H*x0%C_@?IHFyhRkoCW7H!BBqx18Mz5amDt)L3 zjLiSiptXf3bF|m0tKg{F12UBirmOaE=2EcH`jenHF`%gPF>}8Xu*CudS2%X z@+g=8WiV`uKvu_dymyU8%P>JFvEYOqpa6}rY0^m%5q~&T5Rr*|iI5DNbw^JTV0@*| z>I#`->oca?A0i2Nb~j*22Co|QPgG@VF;kI1A|CxPez*X>4vFKMlB0-of6C|YwNrx| zM#beq;9RYQwSe+uxG+6==8JS0IEgRi5g$7~ruKgTA4$$a!lccq@E4Mq>Kg<=4iPcl z6e|5MxPg(?lt1+;wyrknHRE52)vQML?);GC&O2v02@jHv{m_fXSk33ag zx>ITqxhX9!*+)#C6ab{8d>2$r0Sj%g1J1ITh%zdg7`uW0e_Fg|R(M1%ag569K6EBYS+HJWodhOM0wy8P_884rrpHfy{WSfD*Jesm zj~xA~VdX0CmMRgEIo!~uk&9Py3#dK}FUSuwOy-)DJFkI1+vt2l6)1ER0kp#)qwH9k z&rG@zuTk)7f@)U&r(88I=l$P_OEP@XmMZMM#z-J+D_AGh&WGJ%;_{ZfdnkE{`d~bx z+_N@CUM%=EgAgCEqyEE_Tp(!Rz=H=)yDV%wy|+rUEJSVOO2~uG$uep6FWKwy^2BEO z#KP5$kFe4IUe-?@^;9B!?);xJreaO?kF-+FFlmENUCMaxLhtdY8JFl1q>o!+^Hk!E z+jCpPi3eqyDGO0L<22;2wPwPZG%nX|@FxgE+G&=1Y=_ujSNZ^Yqr4_64TPS_|2oyp zP8;A$%7(c66KV42JDf3tKSB$=BmjsCT;nOAAHa8jQFY7J)$3z*^;_!Ep*Dm#~N>Ckf*zEdj9Cv}_)@vilrGUGhm zFr=h88F37k`cOCkTqdezMrI~J&sueSL=awI(F=s^mymOsDlg>OiuP;5OzfDgb|YS1 zUTxWe)u^X5^rm0j&|#&$=f9f#GrQBR+3RLp3_#OVdDc#*I_Dlx?Zp0UT3xxbM>D2* zNKuCJNm)R)_FGnWzOm`k0~ap$AjpSFv1=ZH^=-E{#w}W*-+FOp-NErRm%iy=n+A8o zYHqxM1rrSpdd0x-UaMJj;SC4{f#fl~9Xe-1`}22yc4<*62x8M24X9}$1k<1}02>np zcCJsXYvE#_hQ#80S|Y`2DhZxh5`S#hfBiaLePnz(MblEQE>~EH+x(F8pPZsX%yzYy z3hMbBPXmfPY9Zd|Pe0S`Rpj=xql-=Y)eJd~tu6L;NuXO;oD8pv&=y+b?(cl zd9>ZggPE!+&J*|rDQg3K^g8OmkA}%#uU=@=jvO~PE=akfgzl^d zgw~SHfZG8Ypj%RmC^?H}IfCOs30X0wBHEzOKo*D9gy`~4hwKHeX=jGQm0u~H6nl}Q zRkffi9-w7GtH2>jb?h*wI$Y@saJxRvJ`orDVmv#hkBZ>d!7HqKIijDYc7>H|$Sh4A zo2dg--^c2I!M)bSoN#oe+|xUYf0y|spMR?+hir&5qw^V)G!023IbYivh~E5VK!U9E zD&sCWHOBd^Q(cZCfDa3+^lwv^>BFp${mm!>m=uhGrha=Fq;&R=sOwR|2lt`;q2|Ma z<5{-5|2S^bNOom*4VD;+1#AuD;hKTE^Zw2s{Ps%rhWz36!03hD|K(J4v#jb%H;h*4 z&PvlZTB=V(M?takx)9cWydb)3EGm=3G>DM3F7rqg0$`nNyh_UTd0Td@Yo)4P)m^sm zR4i3&SVOo0KuRedTprrv)9hEoBu5h%*$Fa?Pdi~YVSS?otOg(YO3T;}>E?IRGgDG5 zw~dqHhtHPl5m;Cz#FWMj3p6dWN7&ivMfU0@=9}|Fv-XG453UCeN=Ds_X7c82INy)h zIqMSlK4X|h3VU@cyHc+jcvL;3x^b)@8=d*7N{8aLMq%STDy zbGPO!a%C^Khm$kQ=iBo)%nu@=`-KIhJPS#RlID2L$LR( zL`*|u8XsrJ#eBNx?ocqj{kFZ2Bf%+}t0se)f_(t2gG-(Fw@4mq67*nFo#JRMPv)ZG zC^elqbT8&*KvBOHJ*dfHPvNu24Brz(Z*ub7{kJ$uh?zhmE?JG&#TRzr(YyLmA6F35 z(hPABzb;!RVZ$HRA$%9u%3G8J`pW%W{{B57u4ujg5@Z-8y0JHudSK5*SXjxk+dek zYxHkRlKDiM`DGY$y<&|9ksx}w7Y$N$iOHqHKZW2LqQ3pYR`c@6&-S8v5^D3%F}a2T zf-CmDf4Q84cZnk!!>c@XMbs?PtKP#ePy<2(c!il`r=dpWqrH@rOBV}G@F~8H0d4OXm6Wd0dWTc>fh$q|grP zF=qO6(xGVzZSEdDEzAbpN3+0omGpG{LlNLQZ-z8`ZW&*Vu3V8~=u&5@?QVgT%_&Se zN#Z|{SF|epk&0*7y+pJP$H=9*bpT&$Q`87fFIT*;;JCRM>#e!fiODhgfuK5fLxTB1 z!^T_`Dj(Bq^@8^fiRp-`E~0K8#h%OZV1cbV+OE4sQARUoq)|*OtCs=1R4!Hfdd;o400e&Y$MB@9O+JGVu9YE@Ddxm13?V{hO(tqOIzG=X z-Mpf2pG$lQn}z~;=$_8Glkclu7v2BgNMp?ZZcfGM#+9!sW0Ur6ll7rP?h~0yX68SWb`JdYjY_H&=R+3k|;g&Nfi|E%3_S|#ju2J`G3hUvv; zQda*LWS@4HBml4#4VTiOW3XcM*EE!?8&AUdbv0|nbV)=(lRfbUOhb%-;ZdR7eOBw@ z@Z3C;+Q#GkuCP$OMOcwlSn=p(g?~mzD08% zn#hTKd@cpT(9(tRUx^|2_~+{wx*kO%;)TfrlUSa;FBo2wY>dr|)rR}KO`U_|zLG;^ z7f>}{tq%1}=n7I}ruNr!(`c`jfnCN+G$o#MAWv!u-p-xtg-)Y8nXAg!rC_HgM6D{) z`KgS|7rkyVvXe3t=xm4b@*{lNkXj85C9n-fltU)!!hekoVyY+;S8dkbRzt&h-67wK+mf7k^PQblbNZ~(j9+1ilH4Wx^y)8Sy|FONb-hA9v_2&*GSjwBVI6v7lDyIoStQ9^x38LEY+7p=J& zh%KqK(dFfxU)1>FQX3Igm7|uKKVm#$?Oltg(y;%F%AqrCN_%N<2X?KRTi9S`8NK?p z=oFa8{U+(5+R)IaG8y%pk zjMY&jSHn5Z6z*wMz=O2d8Ou?}Xk{o}anyBH$AgB*GR~@J`)#n=?FY-w?*T(bFV~13 zW2B?9yoc&mk7HExJN&to-{{MPuJF+qz&szD_` zo6{7u=$}(}TyCchcRx9=0gEtVobQT6aTZYxn#TmvUnf%N!>*&NwN#Q$F20o5kaClU z8cz<(IflpZ-g3Bo0*JhJ!bk7sM~+aJp>F zhjUKC56I*nQ;D4OqP8KmR;2sOeTwye!h4Ir!(~y^Jc+w=T-$8&J8t>_6Y-K@$=Euw zo*MCSAd{jS22Y55_)eT|?q?DgcU0lBB<|LR`JZDYiD})U$1!+8Vy<3%+_3#mlmB&# z4}fpff-5q?@yZN(Sb)brEH`1#(J>Hg#PaQ9Qv!5{Tu$y%=W6r&^0DAf{g#!HKL3RG zdS?vaMzc?!WLAzz82PgCufsYV%QHC_TVK{TSodvh(dBT9L+~IP;@LqaGV@-ea`~Y&lqi+J?(-)mpLuS-Dc?<$@UtueCo>m1e2|pVD zYh$`>_Bp~#L!Ymv*CWZ$$yXmY21UNCt{U~^?hO$91PGjp6loPGJ#IG;?>QuG{?~ce zoV>fW=-SM^g@+|Y@1==_7WM4gmXJRe=kBEUq8*J9M~`HNyo-%ig_Z&Mbmk&5ay&g$Xh=jk>KfgWL<%$& zR;sT7G05=D>XYs8`r)Hve1GRI(r3*+Z5zqFaOCwXAvQ3;^M&SU$VL6otsP6xRHSq0(ypU&&|74#T1u8{izHbkk68HuB82%Y`6p*~Hcsu8S~$gYd?h zOZRiR>0QS*hOTFXi|^*Qz3eRT6Q&QOiY0b^{C%L6{>`}=|JrMI&V!(B5pWbC1T3gP?g@7cja1+ ziAX;1+pP#;7#(fYivo&eMjGL#F(y#%z+2gP@&FD)|FXEN+MLMu131Z9wv`K@4Xao% zk?Qspzk&v@ATS{PB%jeT{(igpT*5At`RyjSep26mpT&cz{W=u3aBBk5Z34)YX}8UA zm+qpeBq&!L=%$M>d;6S22WC|@RCN~L7FY~7WmkIdK-Bb{S4UAWA`3;v+x;xzW~l{Q z0WzM}#S-%abIBxzK8%LxZ+S-q{4APQI;cJu$e2~yWuVNpW~oYA9w5chR;{U(q7}n+ zKLy`$?o0$!0EKSs?X`cf2x2C*t3sIUNsZ0kn`1m1b! z_8+1yM<2!YA~We{`={?@Gy6@g)@){Bwy#y zuX}!a$HT|AKMC(#CdyXCvJUp~5>{OGajtJr(L2xXjS2f$f)Cs-M^7En#*3x$Dw3VR zJS+@dyjG|OAb|tyg-FAyy!B&tNJ)~# z(7yX*Dz(GG?Q-_aOxa+Fu$c=G?X&a>4$VgGdUTIff01@;g(7o8`hj^xyp$sw`)tWy zDVPl7LS8nF!%J%6JIo6}Ipn=AHjjsHPuMZhSb{fSV7eFJt z4Tv>guqV%tfQtM^w$4rKx5xDzG6O^tD@NN;{qA4hGkQHg_IGEA^}lauiDhO1v?$Zp zVTNN?kC4mpGVWksg!I?|J%5HmgXyZwDWnTMq76N7%vu3V*ag~g#p9b!8&-!UC53bH zurVl+>F?JKaFR@`?ej_N=h~}eK5r0CkQ6rP#R|fz)HSFCDKI)QdLFrpIHJJ!HT|Q= zfaBwZ)ZcJQZm|7JM}xoR8(OuOMW$Yx`-ft@(q=YiPt971R9E&DSx<44CH~Xn&1FAu5i{Q=C8=^<;+E4L6SwjDPjGsJIck%6 zPj0lsIrRMPzel6WN7~}%zF&*XXn%8%R7(pLx8V{mpH1U% zIvbk69XZ`R)S~Nc7X(<0GoP`i#{c9?6@_JMq1_LVt(j~jTwT1QAMAlTL4yv>UrWLi z)>6Oy2v9ghNOM1D_XVIb`&aib-~xO9Vn(M~q!aJ-Zmt`y|5%X4aE+u~tI`TrfLRd_ z@6Mn6Q?+xWD>m6Y1rg=Rpfs}X&=?JwOhul^6B3bt1F{uRNvoQWDp*{8W5eys2lWX_ zlmQY}o|V?kFWNtM1hM0_x{yE!kEahBl65-%bQfU`@)xdhd7}zhAbpcPMNq?#RO`sf zPCMC%Ddjf{#cex9*~cwtx_dQ2Q!D9hsfrLnX%R^Lkrg8zeShmk=r8SNUtcSG({n7O zDO19`<^`6Z>2p@RZO=N6^^D7|cr;V$YLpiMT^! z9%={-wx+A~w;xxkt0ec60YBeQkrdKWVP(?Owt!B#x7cfF!-&Fs+8g>@YS|Oiino^>?pcks{_Z5d_f3+ zRNQS0{-!zI&>CanI+^QcWqF{x7oQ5}XJ`p=o?)7Y{^K@^FQQjlNPcRNB?nt7PryzZUj-j%}RYgiF?l#w_Y-VxLCvs^VC*l6A3nWXAb|M8w#)Tu$VR2=eA;pWNM;Wz+ z6<0eQkE&XJd(8Yj1sClUBaaJbMsww8XfAJUb@~k7m(&oUEE`3Z2dC@`m_Z^ zbwEc@oGcxA*6O?^4B6p1UxgyeOgirN;dKtF{J;zhiHC=(jbHdZ{{HbZ>z7>S2pRq* zS4NHsblukLBc{|{N(T0-Ynpg6c*_Xk1=BYpAX|?myTJt^t-E)XlAD3T%7V+n;vBB# z6i*3WS_|~0G_v(0?P2GMiGabbR_7~YySrYTm_3llS5C@Vs&iz+ncc*7vUtd|>>>IY zTJ`+3uN^k(9rm_pcwZ#0ok|(U;JkHyRDGb|ixWGJnAIUy8Ec_)=u`N+J7@BXW?n$= z=EpiU)iG@yp}rxNP!}-~InGJoWC~J9p6s0fN``>qZJ*KpBkG!$PFy|u`I?EdbEaex zY(`3n6d2>N$QP$gca&D(lkBDGdwN&z^LX&#!?Jv`c?SGT{C%E^-=Gwkevu%p=rTY+ zvz`W}l(ysKNOfrTFb3OJsU(2eOyw=3AEy!XB0NK`JmDhrC3r$^tFb*T+a=EAPWoA; z8%V)dYwQ%c)^-9c&ycor_dLVig~FASK9FXOj6mJu=rdrIdAGllM*xCVdl-b?y*_bF z3JxNhv%3UGo|3huBp2T`wx}q*Jsbq2C(kM7ehc41(yYm>WN3Dt?MN!N`57CZ>Q@Kf z?z#!9IzXtuI>4yoS!r8`z$s^=6RtIzfB-@r&|>~(+nq+$ru+v=@p|Ew&l&McD5#1{ zHkzISdcAwjPM$K4feU0J1zab&DJq{?r#@)RaT+jdM8MM~PerP<*L4BdjtXY6-|AZ$ zhDr`Smvs4+au(UQG#BbbI_0;LgM4G>mR{@={mvL(;@ka49E^vp3)OH|%edQw7WsEV zG+I`4xx_Q+D#Ho%epxj2lrVDqus+^I@M;5_=&aEvi56n9nX2Q>R&_o~)lGc3l%RH#KYX^Hu;<5=d(Cq?CR$gr}OjzQMgwtTZ+ zGFHxzR>yz;Cp{j#U~GPT|GQH$bEbG|^Gm7o_x|s?i6yyyPc_zHty@f^Gr02UC9 zHN*EJ%`Xg6n=0lt%aC`cO`xSucxs1xF7Y`m(#ZYHQQ6U*K<#{%De;djAgYuNJuDe} zN;z+-$E>loII<_N z_NP;b6TS50@Rqf1AJb{3ne=Zw59ijviA&^c#QJ;_atC5=*m zyj}I@d4+}#D4I|a1Vwf83v^5%Uw?c3`xQ&Kl-1^ zQI0-LT#(;tBgC}TDHLQvx8C4C)(C-~Z7VvxxOp1N?2bq7IfC7kN@Sb7e>k1yu>FP4K@{a72 z#R-!)*jymbh_Ez0Y=$^zvkwEyQU|)ScvTse`^vRPP*So5L%#$;NWOr@=^>0U{TnJn zsSOkf&I@z-GA2{`LtAbV%fd02l4|yMR~G-^SSB~naXsLF{4UKo61gg&{=-fwEWXr3}F_?J^sf(|b zjDFu>8PQTwv(bQw#$D}9A6KWCqwDd|qRPd^iYX?MqPCc#LXKi@TBE6ArhDZtbrOF; z5W&E7&pEthg)7D+v)}jN_kq*?HA>N*1S@Nw%k$h44e6Y1lg`v{*r>AzO+t|R=%0&j z(rv@Vrt`OZEF1o*0>}1R`PJ2drNnb)oL1~d;mj3i`D6J=pb?%iNO63$`{F}Sx%vn1 z7la-#cj0((V8$}%|o@8FU z0c6})s%vYLyVMpd98ce#>PHAkz0bQ5RMHXl4P8DOWvXQ2RDF3++s2UX1MXRD<+3V4 zg=9{g)00Yj4tH@lQm{erUCVMLIdiriFOk!Cip{wq2lB`nIeUi zUQsk#HuAl({}ufdKvscQB1ZADJGHO&`|jQ9WP;`@H4^l*X3VfHV7Me8M3}>JgG{_Xh#W1 z{iu+U-gl7+4qQ}(k&BoKx=M4e=f=f0|J|?Wm_=*3=fC?=@x!mUpPx%G-6%!ul{!E2 zIM~4TI&ACiImdI925vE4{lbj|{S@k3hz-oQ|4FQ1P(#!<`d>y|ZIeJ*Qf~z!l`X}x z(Tn%GnY({lDB&sAm(Z;4(c=ot9u-KbP2Z$#eYE62idvpkZHbU(6twmSxYrbj4Y`Vj z$e43XF~?2}b;2@mk9v62BJEXe*=ss8dmYs}LV3^)%fK}`u$TASX|_dek~OD?qTYri zOEx0HW}QQj{5{6hWSnHC4uM}*_Qv<8SImiX+?CecqL2Vhl~?UwkmwK~$wudzTx%>& zcYSL%Eim+`=rX$SQ!sWbZG&fTS=Sp;`bwNEC@+?FUCY=4WpT{~~49SdVkga2e;Nqf)Bp5i_M&h#TEJX7W#($kI2X{h5DT zD-8aR{ysz>7Pf>4`x+gyChM>>&6}v$VJ&?)ikTvkWjJXmd@H$C&T4w!GTib0oMAjI zu{IW`)_=zSJ7&yraX*E?2n>W-Pj6TI1!~Z)YY3LixW)Vn+g%s*g0s^Nu^#cOtM?vRTn(czjYsrPPo55>rr*MSnNF;BDfVZ?o6Jzq~9n}-L^E;^c=6Z56eq1 z*h=l9jGIIR5GKqQ-%TOFSz+dS-}r?cNxQusEh4u+ZM$1}@1{)f|F8oOGcCcZOcCD} zAGW&1D}>Jqs+S6I8P*K!CE`p(pS)!s=EZr8PBPTGp)9xGIV0H0pV-=2f3EO$qA+yErvA$wMMZlCuPQ#3ACv);1aLQ{=0 zw|T1!wq{np}()YaRI$gHY9@icz2J}H)heTr(P|1SP;LB5*j3e?OcfKpVN#O~u) zoI&S~7_JE9OqbPKN5SKd1gVmS1ve-LL0;>MXa;4nW|EUcAupI+cU6AZ?ug@$>(A(p z(O;D$P4*{7XG#;o0lkZz-upBvS#*xQB-v{8mj%4}`#ZF}qGkdcxt0k*S5uZs;Y|wi z2AF)o)_mXd94|I<{>XN{{!Mz=3EN4!#w*zD?p*`q&)bcz}+)H3vH_UN~>h&wcsdXnKsD4lVkbu;R;`t`G4emC~~vYp(148^B!VjV5l z8D1z#+VgB~l2nVm#y%n$+_x$nv9Fr>ZspGYU;J`~J5*jeoBgZjAwg@jR$sG*yzt)2 z$351_kKX4S^r1IQnaL-TrpiUni*SimgYffP4b`2@k&fKSNJPS1I6qqo)Gfjz`UsFZ zp-)6yy7v9JJ9Q9FequDZW1?E#DXV04&9(LOTjPe7LHbJs-!7%Dxt-_ZY0jsEZhJd0 z&F%#2YT_3W__1N3#a^pPRSmSRS$oV&Pj-5*L)`|MTooVyLvh99PM~{;j_xwMdL7(D z8@6oUG{s}Rs5tD(-#}C7OTGk(weOlUft_(Qepl>>WfeX?g|V9b6JWZ=xw$?&EwXY0 zm_|}{*9pm3xI^m7T-cwvGnKwv1 z{fKvpsVFR6M); z7uf_5;c?-)D*GwI!@J>=4Cz1js%9VMHT@b{KSGy}ve$72md=Rq>yP#QDRy{4j`5l^ zxFQgb@GM%7N0ozft|rI=T7RN;?St0Ihih( zqy(J2@yM0-HOw^dnLoc>tuUAlYm=vUuhy!>AcCkkN>fzop5^g^*o#NxOI9qbC5`z{ zdFo4PnOJ?%oXNIO`4(64(ux<*t>(Du87`X^Wyzp*6H_^`S;dlWb!`m(;O!kbdnP~6 zZ%LdnbHY^18f-f`NDzAoMfkDS98v@0Uv*Ti`04LPo2$=oq^yF=?lmd9FY6CZVRBXa zUI4nix&~NlteFtYM1z|{Lf3Q5_2HZs4ok*^If69-GiFNqjwNs9HaPP2U=(3^ks+@z zeGVKJ=AkGRQMSeb2K`sY*xK;qmURD&A652A)D0CpyX1TAHRaqk_g_u?{xo$?=K{fv z&vV2D$#Meo*xoeE>Iw9>_>@ut~M^g=;-KFX@t_OtyHoC;n7j8o_tWc*L13zYK%0JZs}~74 zCb5OThBZY#H13G@#&f^K^7uYMsaUz&aavjJ#i;8>@GYL~Rdl^@5zA9@TT5D`C4`mf zwh!ylITzFVCpc!?F14zQg34ILH%xvv+z@aL6ge=fF#3yynT55`*plSPQSk{-RH$%z zD)iYY}x0n5BCEcw|jG0PZa(ycyAaVn%3o>S1|kX+qWl8_7b94 z;~b;|iwsI z(Ni+ga;l8;&we9=k2VSU?bA>4lLe_O@X@+5c3w$)Vgsky`Z*cF327_}v$&C=$yxLKnaQzX%bTAD zUX467+&rZ!)4v543k&W8#+k~e7vuRhRrZo!gWDVf4=4OeZfp-)r)MbC~0__(tNG1A=568y}`yJ8|Qok`^vke||e2_kj; z<%LYhQmHoO?MsWRsa(Ip84YrYmT}UdINpr#nBN)Z-i4J+PP^ z6okZTc8Dq7husstQ}!cE07qjt&mW`3<7D%VQQb4Vj%o62Du!uWn7)UU&Zf! z{BM=dzaVYyee1&3r#08ktAeWnQ7u-Z{NgwXP*bMrJFwZ{ISRL?E=xQdBUJ@b0i`(58+QUbYoorlUGL3;r z515g)XoBA!&P5)huEL*B*I!y*yKCs&Xa!L4hv@7Cq7=pd`zU;j@=a7Er;LJ~+aBzi zBdPk6zgb~>?|Wn4FBR-$-$60E{p5X`o!Cjb`9YS5Uzuy3@{9=yy zU!HDn*>8=1-+^}C$U3sVXW!+qkH9wwycwxgE?RY4*7@>(yx3adgXWRXbL7V7R?zUq z2{JAB7JJj=#w*xhS~di(?E_rt^l#p?s-F1EP{S>1o!}CMd%kgt?cK_MZDuk(2@L8O7f3OeP-(f!rx16a@ z-(s3ufP7IxQBy~q?Dm1=^j+yM#~}_5Enyg_p*Q_SKi(VG#wpb3`5<5)#wq^eIA*3- zd!o0*kd(O9D@nEmpm3#jLQhs;78t3DJ67@ebxP$!jG_|KvVnYY`CsejZ;nL$dm8J~ zIiCrcTY~8_suy~C2I3kx?}eKY5UhA5zAx)q?SJ)9AG`na?@NUZl=6nn?Y^A~#LHf4 zw{)_C!apZBx1yx{Gll(N*RsDcRJP{tFG8OUq$hsg>fw+oX;z7?jSt(+Ld~27pE@-m znJ(Xc3NWVJBXK9uIr(=;m6ddtrKX>|V{ie%=r>};jHNnu__kH)31=0$}3v< zMwwalqsdk(?@u*y0jW4o&fL|^MC{&gMNL1gb=!Z>3FfNQD%!~7Vt;4qd_ZytpSaWP z|7Ieo8^5#tsQqD7+er+xbjOj{rdvV=D4FIqrlDcSf3I$2p;36ODjMjMVRvLxS~HOo z=|&cmxA+y2!CKI^^p@H`4!Y6xbGzAVO|I4Ci#Z-g!BvVzCi(#+qWl6mRXdx{Db$Y+ z_jcl>A>%>7$FpxAi&B|jj80zDf7YqbNb@%?a&S_*6x!v;T7lxuU$p-j zAR>mAU=y0DD-BO7$7hzvUn<`;5d1{>+&efcOIR*|Rn<;0_4FcvlGXn==MzC^+wE^x z@jQvyZ>ataa+)(zGdQ%5-FWWd9+HMXDDzg9+oIGy0=0G;ps6)9qO$EFp?inj8*MJo5NpyvxHDY-!4d)fVjhL(*q0T*!-N_ zCHFa_dX8N^Kl}7H59l6L!45{_*B4TdY8Y4|Z;S0#(ENlYRu7f(`F;uYhe!(|cr9qr z8X<$QJkOr9lWpYuEk|A)gn)?8VNp{o<;eowUde?)L3z>ZhxzNc1WqsolrcqBzSy^c z87Z!MY6=?$3!X+LCDcEq$8gs!3eyb8YTR50v;wfXVDYN?0FXMwP;#!Tv3k%#aBMu3 z?-34-j66+9T?FEockVr8hfIbB`?bt%|ve$=&Y> zSrvyHzo4qfh<=M0XRVZA86_o}Pr)VBg-2$RH)4$fG-(|ZHyE|;2YS^Jd7a8KnMSHg zAmRR6K_gNn(fQ&4C;GU9<-@@Fbke8ewaOum+oI(S`dxmJ}kvP~)**4_7AX^t=&2L@P|puyEZ&MWH!{STjMWc;o#EN4@5 z;W9IfvZoY)WZwjJD+?|@_wLha5a!1KM<=4PHaOjMR{8d(3hx>7W?X|2RuP)3dHA%?X6!5AgU&>o@u@ZgRD7 zDtDc6=1cMGjz=7sf!^+O6vyE4bbG>B8?TjB2yK61E?)((H-U78U#PD%mb1W(t?*-# z)P-A>oJh?`3KEi;mM`w`0DSvLXZ;EImvVtUlBPedoTt8$_E(Ho^LnQIN< zx``+eqywgSG?(6lZL$pHSR<%!fgnzFKOvxM`8YeMMWm*3Yt+GXkRP5y%) zvSmgDxPy<$BF0daK6loOLb~ogdgC^mDMOY`Xa&!^@c`^wKKLH1^MP^A|9Xm!_o8zQWQCsV)kN<$9JzID>7$0PR*!wpYUPDh)zMCR|7g-!9QE z8I_04|F8hrnt8=O(<()wg1j*V8PM;F7K|~=-*0ag%dk$!u#53=rs3cbxE;3OaSCh( zigoNq%ft-42i{+QK~L~xr`8t}&7hXR1ne4%7;ErB@TwP%!m_-Rv?M~kIYK*)DP!(( zPmjtL!*7DyX#*&m->B`RokQFt_TS5Q_#4z5jp=<|Gh~n7pJu z;zu0tuT?k;Fj;#1r213!(jaO?b7`%#Gjro6Y@oF8zn6W(Hx)Aq{|7DFCy|c!UwWHt zyEmw<3|lZz>z8x6`2B>p9WB@(X*EbGm)fu?!fH-onTZ;;`ybUn1$IJtB!0ujLzRER zL?*-ig?q5k6Om8r>#qbU)!X1@G3&10mD+e*k8%YEQIVaa!2Gv10n}sqi2j_@yoF@D;i zsT~&?EoTrLTjKh>3R*&4bZ=W0dz-P+D*#le8e7Zc^4*c0$4yv1EOBCTE9iCm=oaSU zEv>KXn~ZMnXVb=R{WlOuT?|Mol1(nhlw*hW*|3^?R52JEPaI>PLY4m)V zOA0o4B)#S22Mxz%h_2r`FP&ZWAjbKkwCuBE2a_V>ajiJ$Hs1j0!Q)As2YU8fx)Fh> zlLc`zc^e`G?jH1JXdm#~jirD=!;KQ@RZQb5vZU;J!)aGUQsukT!9P?>IR(D;nQ<6v zVc3NA^wHi?>j`jhi&d@n+h{C8YBSpwUX#l#%?bBs`&>_ouvS`Vrk2erqc^@Qtr-xtT=hQ)-*M_yA-1YCo2Ljra5hPr5v9gma3! zrbFnMMnf}ig2YR?TLwxi#o72|breS0JfF}k`^R%Rr8T;x%>w0RIV2ZE^fr_Hj!6{N z*EJfTxvCEA?1f%B4cuI(q_q5-k_LI=;t66l#|ny2_)b4D*#nCr%j}ijjRcg(wk-eX z@odBb&ZiItH{ul!suaA6LNSSib5d%|Q6t}2_qS`f4~F{bJ^UEvD&{xSHTb+;9^qpJ zUX%(s{MJ{AXSZndMPFpIO~Qdmyo16&KmPb07q2XFs>%Xwb`$NM*obo3My^z* zph|ptT^q&54sR>Cl%U&5D*c4IDtYf|b@**g9M`FKv1A3zBJ%;Sl|TF9CbPprpY2$jU;g?F z?5pXT@q;4MbA!CQF8W7TGcY|bnWJwlO|eoekn9N`8+&VPAksSt93V zhlz=q0k*azgQ(R;u`6AisT{Ib?0-2;({AmOuH-nQ7dbX~ngrc$VhPb1`n|f?6Rb^5r6Hgov zRd}W*4P+8=9;#l=7Z!&xl~pE5@4yo1({jlFCx(~NU`C@?P+&(Ta{V@c<#j1{{gWTO z%jB%+txO_8!`8yCT4+w2b2lc4&AV&Nx+VKsInLl$6vrHsDev>fHf9N7*z38Z~kFaGgiVI4_@(KG4|uDi^lp9%vXlSTmx>;Q+5$r8wG_g*3wRL)t+uUwV-rzjqCQo#ZK@Q919d%R$2q9*2U{aTy6@ z{p_{Hk1BV}^b0w*PeeyXJcIO{6*NY8Z%xTx60xvz^n=#P2ptxw#VZag>pJ z*-z~e84Z4kDPD2en5u~WIEo(-o{o@F)q+>go}*_Dp)ZNe_~7%;aJaN&_9Q#)8y&#d z)8LyFedFyINUDkFr`;-ElZ}2wY-(StL8A$fYluI4E;>Rs*G3Z+y*ONMa6hH+en&jG zD_R0UseGwrbHv+a23)d_&+k5FBs_{ID&Jd7H>szOsRzVYnzT77#AF{8tqy0i&$04X zhlXsJk|nCDhql*iP_1@})up28s(F^rc6S&U623K=ogCj0y4PI>?3NT}HD!9(I!q$H z!;nkt^G0c(LFWi_db!6Nx9(!QEPOjXdLDOD&&`cn0CZa>oIV{R3g>!#Yn@QggWD`; zvY8CTn|-1@ZO>W+m+Wv{bC@;MACL6mVbl0wZ%+$O}xhGDU_J#1eMNrb3 zI_(-N(N)L$~UiwfR7#a2GSyv@hxoCxTRjmNxp@i}}7rleXdvwCNn8RcNVcjp>Wi`>&&@4U^u z^~Y@HgkKh25%fJXRkO&C+hpbX%3a<(BM$dw>WEWiuwv~pk-Q1ikdX^kKZkB3mHVPt z^PM{0ev{F0*{64SoReIZmKr$GZ$V29Vrm)9#ExT}DD2ZK=gtruvysq>vtT=^Q88Os ze z4bnVS5EZWvpx1PHxFKhl?CVIGE6kV`?9JpL=Le_hj*D{q&fovPc;(!S>>P?=G8hcJ z{VTr;!1wQb#H$z2vU*MW)^b>JJB%bsBFNo*mGRIZC7miCDVA`V74UPXjMHVIR9AS5 z8W@R0$Q&F15cays*6KKXYCQLo6esZ$o_|U3qYBj`7FA|^a&$<@?WCR)BfX!@;Bi|S zS&#ruJf1{j>SIvON9L%&-0~c8HH=E5w^8ZJ_g*;nRm$}>ZeJJrOe_RRrt6#?mLOKW zl%mu!P|Zp(#2EImeMf%RKNFzW(4lR~d7l}bW_x9Y0aX^beCZ;a@lzh%l}EVFWI;3F zW@T6O(ca1qqpE+$YO}H5Y2XV>Y0Z_n0Jm@KbLD~p!`68i81eFGOL$=W;1s7@3}?OA zWYXopuApifHZ9@t35+7P-2CD$txlV1k8)h5hWXyjpI}l>&yX|1liM-~X8F1E^zB+= zNzsW53zyiry+%E#28^w2F@E*}O*J6uH-7zF{2%}HckuWn0G8h0CalVq&Ye9^vC>9w zl6x>4j5Nvx$_mr#jkmtZN1uJ4W=&*sBoJjt1^6~s;_U1`pMD47}d~(=}WU@Hu4OQ$?%tk zuL^1)m_4nCJ>3ik1hbObq*Ya40&$s*N)+D13Q% zROHe#lT;KeR-xR%*4N=un9j$I)>(@WN`J#Dap+( zrk2F!+FE+n3j2&tiVexPj0}5>tgebqn3;>x%8M*!50f};4b~nCt$*PcegWHnncLeU zFD9)4r@_o>O!Q#L=3;Y29=6ZE@jBY(3Bj=J`J+$1U}7+YQ?(*!dquk1E+bR&K-)V# zAa_vZ`sx8717ChRVt;foOf9z0L0WXVuBqqZh39d5G?o{QgRuc!wQ^wRj?RpbW65_Z9`zE$?yG+lB zE=r{GoS&TH^Ig#a(`U|;JUqcOEVK{mTxgp{woj!u?d;4Xi5qHW!R#=*hdX2xhGMbQ z$6X817!c>Y&_BT6(NbL%y?;6wXs;Nuc5bz9esR%lW+ful-dsjbzHl*!ubj5-!5iz_?%YEc2$zPJWnl_Af$Yy z|MP$Nd$^XC)jq~qJb#(C!eAX98lZM^$myxb;^zJlne`PWhLrsDOgy}IK%ypo)ySC% zY^r6(Jm5!r+60Txu1=pQLDH(-rqryXnFa?_^Kx!VhTh#i*hb%P(I^Nn87(aq zoTJ>lFFK-ScoMvB(oyNiQ+E5_Kk0I z=b?O7FRZ7(Z=f3#yI?9?XuHz16*N>Q)k33Mb+o-M_OO-Cv7BMwtmE~e%iOss5425V z5s%eO^-y^A*;ig8yC>&6vX)q!9i^ablY3ah=~jaCZ@dLSZs#6dou1ZB@hjGB8p2i+ z@m-;6hepy&e~V$Q0SU1m^UnI#aeFIfc2Hi)Sf!#5VgzbQlwU z@ccBlTo zBHaB*^jPR@h+$`hlESh+T|J@^=uI`o6MHmgu~II#scPgN zuDJ&FY@gknz11HpBw;CwY2z$zq|Su=MnigesQU9ips&t$925RK?+$RqN`}5 zp=yysB^S^9;#D?R#Q628@y6KmJV{7|#N!)dHM2u5CdN~zDWG#X=&RukR;wMO640*8 zLN<|NVoah3$#RO*jUr}+e>B>Mjo2}NbXx>PuMN{bNz>Ge&{Z4tFF~g<7%>_Q1Pr2t zE({Iu+}tGIIdNRhUs>Rf|MWkw92VjKadHR$T``8$Sr3y{3*Y#aUk2dXwReft3bYgo zY`k7$aX5m;ERm~nvcl~A2r+}owyke)L4m{Pdrc~p3O=Jm!RqZ6{j!F-ikK`+L>oiIKk-@Dey zJTp&BH6k0E zU5*PG{B9LR)wa>89zL7X!S?9^mC8?tMs)a@XTHJ@?@-A(Z7 zb1#xqMy%NF&{0F0X!NKyI#g7zkVmUSTh8$yCeFe)UwEBsTbsCT>fwBxq~Fw$Ruha* z)@zhal0EGedi>TezQf=8r+)~*f;UJp>L8-BV~${miFrNu9>^U#JT@$HMQF>XT>R=h zg)rcjkyz_O5$p6cK`)?U{N2%I{ zKBh_s*Q8{$ZhrDHGw08+`4chtzxu{oe0t|PX2nm#(-VBUu|~TfQTKuQIimh?uH6%w z++ADa{CPRQ)8pmvD21*r@-gjk5bNof85SMk>IP`Gb{QBHp3fC>WR%0>b-2;G4HPPp zRS3K6ST$A>Y5A;5sg2uXM{5^Fc>De__I{t?5ur!L(8jFretU%$sbrCfb8^SgN`jLb zyz~0Y0Q~;@e}tpqonTgj+$_dHGMNOqob;fyYexR^U;Rb0Clc*H*eZ}KwV693^vI<% zWK=&(&0eBaI3W~JfyLx8FeY=^U|TIKq+2l(86%5|ZS$!x~wjdFM>a{bxL8dHAZ`9Z&)y+R7F z>Jc>-sthlk!5&ouqk2uO*N7Bzv_7aF8uurm=7ee8IDk?7PvYo-<2-ZI4$QD?aXBN z3%v64VnaH)6H;X@Ydi7~F4a=}>87(hsVPiB&nU;0DjK8sByN`(PqD`jRFC>Q@4SN{o#rqu z^3~`VF{+1OCsrk63lLR-#%k?|*}02s-j={Z!=lBRYcizlLj2(o|I4RRx#2XM$u&BB z7EOi4rC6zHRH&}Y@3f@(b^Din#w6`U=u021EyWYmGrr~_&lP{PH`w1wYqGPmS z>XosoDx`p0N2yZAq-3{$nkDa36$J4nYpn)DeU*9ZRB&7U3`k6ugG8C-(NU@`2`(2? zI}{oT!ajLaxjX?rf28m$-EQn22kj%-=SnTh3$y3Y)P!G3E+_ePorlN5$Im@?meIc4 z;b+?`%+Ageam)AYayL=%JBh(J8JTF&&au%0gi5pE$a^2s43b_hJtKo zPVk#lw%y?*GUCFlW^vd}8j2@dWR69zmBQord%_PB=iKZ+OjA*nE!k!jTd9RJs_;&` zdUlRacwt8D)6qtp+HjMWnwO!C+L=^?b=Kc|NXlJfNCnKTCO7lb6MXW%=((FmDQreF zix=gy-6kusOa-e&>|~*?A>R_cwm7xK)}vKke_8a!{?S8H2^}@ntJ#j)xO8EXY1O~| z=;1ALZn^IlURWej)v#5UGq&q_T(%(1uILP-&4f|!q487wlYx$|LnbdinmgcOZ(aP? z>~WhbUmxIw;L8Afyt~HV{&Qv`pY}vPT~F83^>jU5PuJ7+bUj^9*VFZMJzan9^&dqh z^1u6Up6O@x5?yedY~ziLp;Isewnl|=S6=+(Qj$O)qGM8$V2+qLy?=shOrqVN{rDb{ z(I5k6iKdkOb-IZnL*o)*tks(Asw~2cFG{&tr=!Xs?S>G=)+PrPH8Og)hedDX?69Pr zcl z9Ax#MORwFjwa#yS_1o;Jsf{B8)3hB1j&>!xTGH#OC7Ku%j#nYQi(hM`s(Lf$hsH1? zQNopMl5DZephlwAbEA{^W=Hws@BKRfe*U%Bc%0m)vMNz|e;|z2BXoAOHS|Ls)~XV1 z3Z?2SojJ>vDhnL&g}I&BBcLWzwhb*#(@837^6Ti}0I1R)z|x?JYL_X zR+lup*=8neHqni!{-~85_L?aK7``%hmYfChT8Z?%9GK?w*fw-!A6}c6 z)1wns*CbkLa@pu1k(t5ZAj6R%0&dB=>>nNS+VW*iRCerOXB%5r!>B4(%-mb!EC0o> z@lXEl-vi+6+*!Oc5+ORg{+L>~!`_xeaV96Hd7M4PV-_AL)GD}4HZp3O>zRp3)^?@5 z;beP^&g*vB+IBqqHdEu3F^!go)YU=RCxk0+EHeH1aSkm?>7Ju63tHFW} zjX{3*?rDr?2c~(XN>WeaapuPsiI^nX``zt3EOpHsFN@6F{{7DwJ}23Y@=1;7E`|BL zCegMF<}p6me!xOc_;zq&42|`OyZ41&7O#aFyTU)~78rXr%%>0JZ@tw9S0^TDtI`Th zsflJRj73Gs5-S@#%9e1dEb-9u=Sa4tD!`H}GCLb(`$Xj9xq)S#WREDRp0?Lte}mnF zecFm%eXD_dU*z=Q;DE3t$c2kS+v{sFER{Y%)$h~n*9rLatR#f@?)~Ti(Gfc>1#@$p z&r?=-u6mt|kbjKPQH7!S=oX_vE83t$6kE9(I$NDHSEaZ2_K$CqXr59{3Vpmo0W>O8 zaD24G!r(l`tjLZL{Sq5k9hscTfDm? za_x8d84RlFfXynU!VwV#>$1Olgwx?8t4ewMO5;nX9jI_r7F+rh-GH6tAqG+{NySplJ&qrppOJ_~=`OPdz3tC*1_) zrpSw@qvNHY`u61}2B_ zDTpbI9qdj$1~pC5X7!OTXo#yG^{Oe$jAw+tS?s8@UB;~79kgx-rM)_BRreLEbvPy){_b-lL`y*}So}&2PN;D#-)UnU6YEURi#Ig+yrIIA5GFUaU8E?Zt$0YI- zW}H91{Sj8xp3^&Oq8+jEGp|ZXyY;w2+>}Er+vHZIOs+1nG`ToT$gM}OdLr9*HnC2O z(8~%fw_6p0Q==@O3NL;V+n}R*+xKH}=Fcr)Qqwl`8Y6w`pvsOlJrh1-56`Ihv+hcg z&2kxSOZav^5M?{1`qf=J;uIL|%FniQA>4j5msFqq?&Ei95$3fEQvLGL=QkMe>sZ~E zLz!$2TuL`3@+m423&X0Py|3#NGTOO!UnW>v++oHBrm>7jwyc~vA*AfhPV9huEKA!j z_IflrOI@eM+fk+E`!T%deH2w6{YlP>v8E-Zs-fJ2AtqES&Yyn#DWUKXdIghJv76Bb z)69=c-+k`xCdH7Er3vxTPY;jjt9`UOZBoaF%*@O07Ap;mrzOg&Rm0)&GPF2L;Xr(l zLM+LHhcU`c*_X|0Wq3yJbbQ=L>QF;PL3WtkHKw{_6e41CXQN^M%@6;G7hir`?s4l0 z1#6KJtJtq>VTYWm&@p87n8&nu+Crb5LY<-TFztrytC`5*baf~ym^wWg2O!b`TrY>OH8<(oINKc zVxtpY>Onp6ZDG(vKE?jND8J>|NwUQnnzVe+{LCyjHy+_M%LBp~36eWbqgRheO{al< zD8RPrHM+WZ9+y(+pRYc~t#$DHgp@JOOwRG!zxNO6tDcKz=Vl4oTy(r54CS>$PRc@? zfnX53&WUAS82$5q|9u=<6Q~B~jR&g?=q#L+q_k~$d5MNr!=5T%To@TaqsE+fEe701 zCx`c?WY_C;vsEc$Z;L`Zb8(U1`?vp$%V))L8#{NF-~AWAhu0uV{k7m2{_|ou636=t zyS;P<#bGR@i#)mim~SpiACh5al$}fhlu-}*{WKMpVJlH2P}TBSJ;*07pX27{3dV{E z)!f+y4iERSs!`q1*DurD-o>Phe#LC$_@3(FvpX@_o$QuGxy{>yjGtNLk$Sv#ybe+; z+nnST{@&(3lWNF}$LFS}HL$iKWjH!dfPg`dZBZQ4;Y5kXP>{V3q@}{{2HS6p8Ei`LF-huktu9C72O+fR&v*9R+qBv1u{+eXJgdBR3lu!Q2o9HaMjv zor=>^Bju9^`^+p%;P=QKw)V4(PfVlpsi7H-I{W1wW+hLCUXN5y=x%fBc|Po;qI$TV zbUSp5Edt}xJ9YiN4|#6+8TyKE-=uQRmP>^GSF3WJkT-WbvpU}Eg3>%Xn6?*++^!Q53kEjhbH$|r4MOt zau%IcL#io%UvD?C8Ko!1=k4J$Pt)p1zfZf4Ms9(Ffh z4#zR;&6E_Dmf1EyqgSDjR6`+-N(4sQ=v29QG1;SDO)@+@3c&9EA%4#|7PIhmD!Iex zu*hwr&?fA2VAiXLpM8LX!!4$sm9i(B)64Ga9^J0+xoa@Oqjlk(g_%LNwqgth>|7Xy($q$VAI;VeR2f5-I>xsakpM z*>`w!`#rfETy%A!n``|#L&F2qvl7to=?5w0s&stfs0BQO#FA-#vL!n5%9WS-kH|OLxRukq9ot5O-05^=2%H|; zIg!11x5dH!Hf9A&VbmM3m>onF@8$9-YSj{UtK7NSTx5Ad9%s9IWe!XgLJAOmw^QNg zo_(I~y4Z#G=nywQ`IO~BxpPgv%6)~2H*t26ne7y5H8;Snx3U%A#;RIqHsc9=(?i%R z^1a);M`(f`+^QX;c9LLLK^g7#()dFT(98dKRx@M=Yz&@?j&v*y^W@PD^nQ`euSDlC z_QdCk8*0Q>OQ4{=Jbeup&o6P55u4(%n^{?Vh+PdZc_}(eyj>Du7ySH+*%JU$@o97FGw2U)k};J6|@ucgk&&>S~@ zsBj^#E;AZ+Q|pO+y7kF5yn{h9x5e)oxwwd}BeXkgl<1af)CNT-MC`MKeIuA#qPsr) z_%1(OxySsn_#_dVnPxf7;fC-?p|3}y?(DKzPsNr*%|BvpI7)iw7`rM<@4HOlQ3~<~#X4)jQy}9)nntX?ZQ65cwPXNuKe3r)0 z$GH%MqUvQqORd&pFe3D@*$uSI6(Ua2U8nc@^J7$5rX| zgB`BUO0uDn-oO|hCNL{|)b@3B)^>5t$UaZ*Watm+(e=bH=;+GiwMMKx1*sE1WLUvZ zWzu;X<}OaV{7k2{V{93TL}eeh@2umpkKu6(J@bhw(WuZaepyPd*ITc_(u5ei$J?6}VBzGhj?Y?{!p9=C0ct+V=c|ukzJRP;0`Op9N_y6KN zBLB|+^kUywQUeycMTW|D7T(s5n?Dy_( zvl4s4s7IW!rSWMV$M*3%R7A4T|aPYWZ2!h>x4I#|^O zSaoea+s||B3+ekC9vGlr5T{pTHq+>LLDk4#otqU-{NIx%uI{OkY$$r+X<*SGM`3H>9DwP${yJ zlkDpF^bB`z-Xo_#m$iKz5r+q(!UA$>^w`jY%0f6QEgTcF$Jps1ua8|}t11FGbMXv! zcDG4y$!E7xMK-!w8b)EdbG9&Um+wnV#e>6LQW^Q%OP8PF#z!|9QXsm$dKYat%=M22 z|7pzQVer{W-kOs~|tm=O2BHx;u89N_x5#`+)=D0;oGv) z%+Ya-sB*Y3OiZ$;9>#~o92yL?>~hCzx9>AKHqJ>=6ziGMajNwSo;eX{PovL%Gsl9; zWFJ=xWOL#?`8TRSfl0~gm9=epJ4rkjBx>AAw>fiunGfC-py)w^3#S_JQ_%Owb{iZeL=gn4 zHJSrM-8THQm-~V`}+es&-S0})d{`gh%$#IG3g!akc8z9L?#!guGwAkGGm9hEUrmrT zYQE%;iMeSKn+0?Zxi_!d#UEe)2mtGtgXrNgUz-twroVEYO5Z?kPiSrR`Pkk+z^kU_ z-nx5_=$tr&vyox4?X53+JElEm>Y5IAm6bEL`)p@BI1S=(cg{N)gU>6* za67X@Pha5tlsJ7$vzPeZpS{oQq#A}po1NWb&Ycls($Tc&*1E)0)}~dd@XW0AJlbt- z{^Wa~u>6eh(w*D4v6vc6kI3Kpd>&2;8MdA%jIW^(wW2uryYW-3W-AjaQa(I4g+`-c zZCmbbduNlefguLfBRd(3k;;^bs$oOTMgy(F)~&TQ6i;&))f7;@-^1#SFL?O{xyLU) zTH}}g)7Sar3wfZ0hdiu2l!wkQ|NPg<9h`DgJ>CXgKE}ck`U>-_nlBQz`Dm+vm)mI| zn=KGflPiz+PH1by4$WO!qFpW1SHRH!&%gcwgUfS#@r67B7MF&(_`*DuE#cFQ$IR$J zgl82EEHkrw@KCZWsl6?t{W>o_qxv}YI)1daPAMh&=iK6pWKQ0GL^rK+{R$R0!ApzyL7?(thdLv*dFX zBB4?F+jfaDSD24(N+9y``7_u$TAnBz&CtR)?_QS&)7Zo~-BOOPT#>BPXW#!5x}E_n zYF`bb7fZFw@|5({ezN<37cUO+(ZBva0EXx^zxd9t3%?Y0=_le8%i^>&wRPGJ9Tt`0 zi4MBZ8+5F#OQvOLe1yKX&a~=LPt^U?dLkdC`X2!pnah`$obj{rNCJ^IiygDmfUzRZlznN6#mOnoUX<+6%8h$yN5$S)sv0&| z_PKvw?qPp3hF)tR6c&AKH|sEI42+J*8IMm>*sXR@(~r-bS^BcS-{o;)Z}-V7o1rrY zDOIzy5j&VnWpFuzWYpx~UZ;)IVIkg>VB?!_F0ipGa(j4EB{Ugj>w)m`LH<>6vGExG(!2 z^90GBbSdDKe6Ln#@a#10W3fMmYLmTAiDfl-=)vwfv!fGya7*su{P~M$o`{^T^b5?7 zjgwLR$&tDRv%$jW2NF04*gVt>4azm4r7se~lkCuHi*Jx|o?=ro9bB$KE9TZ(Qa(YC?0@-ezHP5`)Stnf*SDtu8|z zg)bcqv#Zbsv~~vMrU%4VsdkvKkPj@k3D2wps&^ zKFX=eQqI3PMZR{#>ALX8yvNUWJHfz|*yN$So`&k7tsESp8FdmI6T67*#%iaF7z zXHfy1F%5Qw`?zeIV80uq*AbmpIIa_`_bJC!715}Vh|hvw1y^(HCH50}x+>w|h`0$x zh3^ixpHS?WhMf&y@x~!bp7cx>RX7Nl*hO}RC(NOb1Twxnl zywnX6i0hY<+`YAmAt<^iWFMm5G;xrZ>V=3mO#VpCov;QVpk+KDa@)tt?*1;QnGS(K zfP<5DYN~p0Xf(u}HpHIFdYXJyY!&en4Al~e_zt5>lBqs^yum;Cv+bunkx$pt^>jU5 zPuJ7+bUj^9*VFZMJzY=N|G(=$icIAH`rp6MA3$F1^_C8U%T01aUfZ70L26EUrFW8f z3Z*)OqtauMY{VEc2H7jg@0JQZd}Ds{dlGFr>u{5p@$%#gDTnnKt+;FwO-NTO_&P=k zYMNEyQJzcBg!xeQtQhx;3=RbGx8V{%5KL^@W$HDrk}6nMpGF8a=Z_6dgtf_OKI|1Z&B48f12p z)HD+L)%R+A_42nUsItnUrorBtM6!H69m9+BBRTLtED^(y#meO5G^+}`?vuN>IPVDJSd#47#_Af?(>#NNQjV3V zXNZnU^s$l5Fzg?}>XPhG_GlYJaFCcPxxBP6Pph6~^*~DOnumG(SD)jP_a##qb=!I2 z{Qt||dw)rqUiW>U$~lMbs_IakV}1R#Q>!6VTUb*$)P$+CTpWgknX zEgc_`CMlT`Ku}BwL|W_un`UQrcG6Cso}SLRy1S~osw?N{AMUgJ7w8A^oOyqmIaOWn z`##T2&%NLKy;6DAm>OKW`4LvdP}Vg(NI8*XzabT3o6SY2JIu^ufvNZc9vpVr<7lXl z%zlQn>Qk&W>X@`fs=JE4s?)*eaA8$hvsfX;Xzwt3y)3@pA7sBOSukH+N2l9CU1f20 zmey!FJluXLnRB1f&d$LBPY(&^zMyT;{FIH=Zo+5i=i|EqH?-MXxW@a~y(5{=TC2n7 zUw)B_>en5d3ezapS>2RuRjJj&Z5O$#WQwdL68Ody``<#6cG*DOFYmCwu}IVz=G`@^ zw0*AW<=AgN#~=SlDj=P`1HSlce+z(r{|6U&bxMOt+9fG&=4iJ8wSIF8TQu_>qEV@) z&CTrd`4b{LXQO90`=!JD;!_W&txxRAq4rNNzcI6#S zV^JRMD;C&v8E318OF=t*<`5otfu2#xZ0>9)aC!smsj6qLP~uBp_!0m={Q1wQSE`JK zL_T)6bF@lj8d}McYV8&d_4je(x>Pg*%>se(5Ucx==^Pk8!d^z~g~?=P(Ah;JA=&(~ zL*2}6XQ(TPa{u9BHdfOZm402i_aS|bKGxI>u*N}(-~O8P$Nl2%pI~)+Nv1_k2O<;f zt?v?aNN-hS#KC@A!_7s}f$qLOF5MP5UUbMwe5b*&KB<(rd&10qbQ`-**2isi5_b5o z^eb4)#uf%!2VGOLm(99?LRFse@xnT^EojwD#WUxIIrhvslDqPba}O7AxO~`DrS1K< zKf$1tO56D(PtkpHjCa2$Ovqk~2cOMM`9SutZ@8CuwuHy7;6~F^T)lOZzG12CrZWXH zNm=K?-U0ra8CyF|MqM12bFlFX8g7inn32c154f}cK; z>ep_)j;B+kt>&yYY<^xl`wEFY(Vtq@L1JYCi%yd*usswlwT2XmDzFj z^6HG;%*?M+*NV;P@9JiDc8%eX*s5*QKJ{G%VL$vVr=1-%iU8rQySIop>YP*Pjm?b& zRgD3AQRK(zvyw<{P*ez%YQBj{wXJBhIywO@5j9fD)YXmCJItCw)fDu3ULG0Y6Ezds z>2`5=Vu+vo`CIaRpAKJ_z#uou8A9a(2X&FHiM}aDCcNCfzNOy3j>+X_S0k~IMx$Xw zwE#IJNH2Wt;Y~t*Hwo2GH{fjX(G{sK z-^yor=Gk*hS;fxn>r!Yd1uCjsliA#(FWf^`!S=n;2%QCi_il7*O!nF-D*MvJiJ?^F z?y|Jh#C1k~^Nm+nf4Bj_igusRPd&rjW2r>mTb?I)M9pR1&C>3-vzwKetG8p~u@OGs z`@GyMdk4Fx!G;=ZZ8O_gn#s@_l6cRjGtk}^IqcYdG?Oj-VTt{ADhYheI(r8q*ZHj^ z*5ea=@s#*_!?hXS+!K8{Hgts9W{sn&pWR|ikZ(6x+7Q3c>2^@u$zxfU-_nOYG?FQ- zQ}TV6rj5T(VxMxgMA+=+iee!TMfx#^G9(_WQDFHd_GkLADf{y1;V#qTqL;?}BJHM^ zy(NkLibg*gvyWI&#dp;TdPj{zC&VuVgWcFldEQd-O^?&Z@`mCInLdQKxl6|trPiru>d0Yg6^R9(KK?X| z@2!wg?HJaLCH5O4OP#I?GkYFRk4v?5?cyEYyx;hn184o#?|!9|SW~1k`4$zuj$SpW zzEI1c=3&ywXE6=*vp*xoFdPh%$Yxkn^wN4Qc)ebdsuXT*Yti-iiK!WAZoQfPf+(+} zZJ^iF#hij*p7Bhvt*>HKvkS+cJwbx!@F)z+AlD5(wvoPtC_uVcexp4Lbngdbb zy9b+;i)D_Q#Nb&c2Dtpg?=e0hMyil1F>y-GCd}_~<(+rvQ8VIt`@$T4`Wg0>1DM-O z5->T*DoAg?*@}Be&G=egfJ6;ruNe5v%q|b^KHzY-INqVdlb8y5<`AP)-Kpa08zP^V z^*ZkE;(k^P$4I`z!0BTo_S8(xnvsR<7Qb{@0)iRCHV^)4h70Eej<)O5bHWuuH!RQc z-QWE~riTS?m!M9&+@S0b#h7vi$!4>he?uC87jHZ!&^N%!&W76821%V9^qLR1@V@dC z*(G`ZsL?>SlV`pm!GpVFLT51J-50}YIxLHdmgub~Op z&@9P2cur05_Fw;)Gs6;G{rLKayb={y+nTkX zUoW4l;L+(>+E+vBD)kVka zqI*Er!;nKmwNu4mlmPj{8(-n(+zpZo zYM6TE0LyTMD<4VU&AB5HNqypy001BWNklF6=%DCaT!^Pv81BPoI+@Ppc^sX`~pLkhOpN%1t6Y z0S1He4yjh1j)H$zb~ZT|8f7jk!IajoXKyu0(5^6-rV3qwNxIZw<<8tA+U+*O;{wZm z_oJ({)JUXku}F89pTkpP=cXs3+_?D>88te=V(X%TidK`Nnkc{_7vIVE)VZZaCz*= zBp2_^(C3$yiN3%D*B@SEK=lYVT^c$?0~-pG`uv4awzs86*xTKW*-<0plVH0y;AU^9 zNUN;onZ|3_!T~B?S-qhf0!{cbiZ27d95K)c)w z_@T`o;a6UInVcFdKKJoFXAX}tyZY%YW-ZPhKXVUMf0RehV6IMZL~Mq+rQ_p^q90qO z41@hq>Z(~Y0#+YX?P`&bvdWlOdhcFNY>WcWoE#kV>d3gswdKgp;jlUY)X62 zz*$hQ490vM&oLr+x$|f=6P|-|5Z^Y=81V>{jNyRRQRYR#td@W91*@!oe zo#3ax_!vvS_-kEp14l^9@|?skUEXf=^$MFwi5mtZL&V!6OVw} z{vHX&x3&|ku5Hp4kpsn}xre;;>IGtP*_&XqMO~34*~1aGXA}4W^31V=HNt})3c+WB9N3$>bF>tO6&EpKEf*iQL+7K7#7MMCCcFbaQ;W3*S^~>SJ=o{g| z4}QvxqUg2DV`TEE9A=054rAWTb1NbI1}{dV+kJ$+H%5f&CdLGO}% zG8x50mH$Nlv6^wa1PoiMB} z6EAewFNzPDdi{Am-?H*Azx%%dpm&%FcZqB@S|3xr38uqWbN>xllm>Y8SLf19DF#b+cxx{qPd!$iN> zjD^c{v_cZ=9hw|v@tvF8S4E@A5f|pFiBsbeqiq1jKsmp)QWz?2)*gtBh#6`)^ld_Y zvX6;k8lOwz?0llg_I8>_*OKiqqq>CQ&81XepI^0>AXQw7QV0CDzX!CUSh%c+e z4)~AXk3E62KDnM;Pp&7|lk3U#%Z*vUqvPIkN(ducRnRMF{G+= z4G*%lp;$j75t^zI-_dAMuD2;E_Q*^;#eS=XM~$fn*qqcoCLXLy`M;}fr>W@>S5-Z& zR?AqAWF~x0FUd^iGh&pr*gO-_LHst!wrtk3xHV>clE|YFbaH)TkvD%R6-2isK&`6S zml{=?jShvnRH@u{3sH-M3+E&|b+T)WKTY4`y&p>%{##%FDn`}RKffO%67kYeS>y3T zQ|wf-T>OzhHbx^uBvW}t!b0wGWp)Nj&4SA;na=2F6q=F|vsf%xqP^Uk5%`dGr;H=e zq^X9{K3-hl<#XpTC~9|Oz)k!>vPa8vD_E>XPIXI0WXIMdAKRy@Ml@*kM!NgE7*wNI zHd0k=%@PeY7&sGK!=j*8$#x#?Zjs=aWM|t4M$WuALa8Vjq)RKem^Sz+R^*-Cwtj-9 zJb8os)_kW(FOm_nc8qKk4yb2kBla>$CcArCtVnjnTsKkP&9T^$EbFAk%u6pkC-1wy zPAZw@{*Gi`j@rD84))XcvQ$KW`oSeu7uFe4<1oTwgEX=!>S`ePku&E=s4)}znudC< z{TZy**O()Z+(4 z-7b0*!OwOzgSF7$@Ry`YG5_IpoC+GfbAOJ`aG1m+f%u#qJHp-lHEbP4q7jU;S}F7R zy2#pp_bXrHVPb(h*QGK!Tgx$~6M2Xivz!j~v(gm(D6H)Aw|@Dntj`Ha!0|VpVyjR> z`-#A6PoAD4t_J2>GFl4O3Z=AUg$v~tu|yh|PqC_H_Auy->?piUz!S!7(xR1ZrM6om zG%6X`m6atl6)nC|`CD7Z#@hZCT|v>Gr;bjueDIjM8gRXKEk$HNvVK;x5s!VJt+Jx} zue-VS_RqQSt3uMF@z{BIXNUC#$w2bYkD_(^Pm((PpimF?;o z>t(fK#$u4?*0kFA6wI&Q&~WGO1BTSv_VxtH9#lyvcwYbXVeT#N60u5V_?usQl^_1> zr}DQ|BkP+BSRHbovBRg)nM~|$NtQX<6@XTWz5&^%%wC0xQA4__p!s$W4!!)Y$L?Z# zZwrrNS+-Se_@XWxeyJR+FRfEM*k-s-DwY50|NecnPI&EQ(d8sB4eOfqiS?ByDL6oVt9(}@-~Tiu`h0OgHp-F zSiji04{pzL=7rB=(n%%LV~G$_(CM|<9ZY%`nws2qs5d~hV&KDjB5R#W36D<8z#);< z?Zh&N6|&wPqLrQ_0Nxv$<;qf#{B z=@UC1wS>?BWQ(6GnPqUa@A=D=o;Ez6D z!sgJ@XB9nfW%4)%BQ#Zs;o;m2N6v~pwZ{)=J00vQ)JT7K5M#5>vSN8m-Q8l}Tqo?5 zcii8uLp{s%NvU*SfAjkf9@|orsT8H%E-MueOgQ!+3)17 zhYXw@XE!5u%k7$`bFf20k+3xD4ZsGkpwi8mOO; znAH}Gv0cir9hc`k|J8GJXF5DwQ7rgwFCSl(u^WeX^H`O=p_OJ~J3&Y%^5oSS z+25PzD_;@RRbjo%UtPMy@L{nD4SSjRitIyAuN_O(im_W{y1sA1uJPdZO8lm^259;; zMC~F^jeHBU8pHCJ?_8z(@Bqav@wa3B0nC*Orcu$CNwV}xv3Ctn|4}*W(o>jc zbW*Zs$=)8w)X685&9FInoZBM2BYIwmbi=R<%a;7#Y4fwEVsu}#L9SL|VO^@2MyG|~ z&=9>&ku7s8M>rhjr;2PTGB`;l9!D1uTkmUhFer9-lgCdrQ%31$(3S(eVQg0U+l9;y(f(fC2NI*NCaSm%28QYCAL-9p|V|8CVAB;qNwPH_f0t%+JKLsqfK2AmG& zDsg%hjHz5}V`-~kVRV$m%O7**w7}kq9uu1%&oHjWaOfIv?8H(2!*~A#fb+xC#2&BH z-z$G_c6+FqB*-haj2Jvd>Ix=Wj+Gejx^O=&!PL_F1KP1Ro@dn>tfY9HTBEA^H>OMh zHp*fs*5_6+yB+i@*r+!gAoqBcTu{LmT^{la+k^%rNW6dV4rb9QzVgEB#I+6DY9!3= z;yf2#c$S~vmcG5-i7|}HB6Bf;V|A=LeA+hNZc(~+txY8>@-Q$sLN;BXUXvepbo;5t z_dkOc1U)`5X=xbM3@h3^o{@w@(B)^l+Q6*FOl(vRa5l7`jS_M81n}2exVj|pKiM}z zt5{*9F8fxjR%k0Zou8Ry{M2FGipKfup;KI5x{FRZafjK-TDgc>&4(hL$JFg6*pbgK zSCj0k*=ml11{ZqA8UH0Q*asg!5I$INc zTlxY#ehn9FU_%?eeOIB04*BwAWL3VO0|cZF+@9-_62JPk*p zxGWk*Cd59u+&)qnNp3_!JtVdZYO1w7 z86b+2?|dMKf{vzdCi84SS2L?+dJUpy|dDj4;0{eK{wep5omr(tq1G zJVd)*V@ff(Mtn{lEG-hyixW@Pt5i%ahFz*3)nMcLCj!MfapVLyidk$$IY6BpI>|vP z&Ouu2*ye14XFoTM$uEC9|KKr0Jv}&$;!ORa5c>sr|KZ_b66?!!l*12s`g!`b)BOAI z{3!q(4EWtne)JQ0=iBoK9DeQ$?zX_-Di#y%g6vC=(Z}?;5pF+_UT~YKNl)J(`J%}6 z_GX@)y?xF-e@w~O79OwI&yCm$ho*bj+Y*_+aqTYnZS;60p!C^&6q;rFRRUurvx{*r zMweIQxKt=|{EX~FuB1n&H={SIo-QIBr1r_HnVYfoG$E6px>lZ>hK6S*Wvs?ue)rqF z^y*2L9?IJ0%R26C#!%x+IWsxVsp)a@>YZbz0z=_m)Wil>=GJI-#Amb&X4W^dM0zE7 z=@`#Bsfs9#fVYFm;OEGEx&OiDG znA>Uo_%CE0;rZzkJepgkYf=vT>#K|S&0@EV1{vAmC+InSf( zqCZJZkzal7IW$^XpW^;wR(In#0}`AMMxtDO`#n%I1zQF)O{0l)Mgq)AO;1;s*zRV! z!s~B5%cZLloSR%V?B*7BpV+lxyFsp0Vq-z#8(Y&tSV2lNg)C;jm5E*n)E}lF@XE=j zxwk0$r0p27So(=Q6dPa+MVTIQlFh50(WZq*s;}yx+`tjmFxoHnruXGDtXz-5rr0j4 z*Gkg}>j`-d_fUv%Kx|ydXyTwzCc7$$R%e%=3rBmnwjoKGcmC)uyS^Z&)gd9b*QT64 zAP|+m&+k;(P)xsrT{S%2I*OYTLzE2~a=9FK1(!FOtfZ1@Y!0hhgB0B%A14Q8pYJ`| z#Gx2@9rYr*9t-AO)p{@(;2$1-j{o?V-a`ZEM~>pZp>*Z+O;gM$R3*d-JJJFKO4g~>hKZn>1IAD zhX9AgO*j}Py)ClVcG_5o%bxZ7!`!)ZhwfoHfLc9PuD^8$yHDQPnJZ!p+NhMoAOFr5 zzs6tw^)Y8*B8e)boGD_6`QNUUty>! zhAMmFY0g&D+Rug_PGXy}i6~o}G17{0x1y;M%VjvK5|x9- z7Bg#042(&!qOg%6QBKo;L}HL~K0}Wh`LdTzv%I~_*q9`TLQXHco9pNXB^ENR*U&kG z9H_omeZYiwSpIfraRsN>%i&}4x7~F=bBPty$W7w04Gv8X(cG4OxxKx@_|qX;3WnXb zcxdQlZ9BCCiuG*<2gT0CXG;99zyIFfoI~Wl`X68H1k`3_{01D==4aK#<@`Q9$bw#8 zdc?RtLP_Huc-qAgX>wG<_Ey^hgn#O9Fin~F_d3r(_kMgpc^|LuQAz#v(jN_rP#z>Q-4Mq8~> z)U`S85aT%Z%mBH$IJTg|a;Ns0bci!}T1ui$yee zZ1O(2LXDwzkwHb}oyq4I3Jp+I2aaUDLU}!o{jBr}+{xE1kCSWxk4n;OSY&2(NdmbrO2onID7Gv z8;n)Wq}0Cj^o2=nt+J35kk;y65QFT>G3OaXDUw;$bSdUTw&s;+IuHWN|sfNd*F3tgSpoqbj7&J$H(bKf;wcIV7d$ zb~tqO8Nw<7WY?GYx8MGE061y}3T7X5MOb3amazJQTvCTsOR~iwosR>%JQwCHQ(Z&c zE{Rbd&J?lKMNempKED60H+k*6>Rao?I5+Y%-D+t0R!zgixzpUd^d!<asKQmPJ4$3H>JP!YhQnr)ujwYMa(sIY>J<}_cm6QNguShC?wT5i$Ks?y2Z(FffW)X1%R zZJA=bK~Gqci(N)7H|CaE+!t95*sF9Y^g%LTLDR^i_>Oq-^QYKdT%t1|vgoL5cQVaxDKy3$?v5S)GwHqBi!Fv}klg`P$j*Ui{o0(xzAr77z9U>TzwS2G|V{jBTQ7xBABxT<=5(O}*MD(-A(CEtCee0GybJaq^R^iCB#1R8w zH$H=&%>xxj>@`>}Z1KX=QUUa|94zmOKUrRhq0^gLuZUegGN|LZ!%uzI?_Veu*;(17 zsJfl3b_bo3_}As^EbKgU2Mca!^YK%GG@Ymbc(Rm$knp!@i+QkAXRFRbBPRC zbs2u~=2ct{S=-s0+njn?_#N)Ne~ta7nNvrj0K`%;`n`i#RnKF&5~r=HGT1MNhKZmD z^PvG;s-mvZXjnc_J+n3ktxlVjM{>At1hs^9K`ze-aiY7S=RvZ`Xt&69r?iW?Wn@7m z$+vcD*xh0iq81y$UJa$RJjc+Om$MUtJXWKQQdX;-4ICBZfYSp(Y%CtZXh| zRB@)s9bu=mjYG{YpS!w5(_tdpQHi&eGKZ$TO!kVeZzMKY$V)u?+*22b=W8@ovBn?q zkxj=bXJsw5c01-yo%t2hFh6|nDh@Sj@1^r&3^aAz z&xs9fnvI<9>t$6XCday_nY|&0`;AhHxy4PA3Z;1da|75*W?JgV)onAepH0zJqcz;k z4)!BmxK$tQY9)o+BZ-niDn-53CSVo+yS=tW^vrSMJ0hF?;Q`hjFR-5%yH~X2`1PkG z>3uIf)T=0h($UZQ7k zgh)t|V&f-xdU8Fvo?K6^|FYMA6_v<;`j5|a zd*y>Xc+rj~l{x0+OjJZZ~mHc!`Fi0Nj1F$K!*? zwB}{KJmcMrbam6xN%nHL1jYI;mb_$08euQts_H3mc{qsg(bFdx=X@%KE}3UQQS{&2 zS;kyw;u@2T?DobElYY7He}4Eek(P;4Sh8HrOpaqC6I4~UEo(87_-KZqXCxC?-rVNM zb7#1_ELBdg-hx%-S-J;%xpn;-Po0)3)n9-7F0b~z!h;3L4EsV33>^&zn*s-MSv?F* zD@caE&f|E3h#D_rwV7DhSmlW7m1^XRxZM_-suw18P$6V>GrJ^NM8DrhCLUMv;3w56 z(Jso`HM1thdwOxHA-V5-{4QfXVKB>Dn*3fW@hpX$!tYx=oa&SNURhZno!IAvvoc^d zoz0NQ=g781jve{{b9ZjjrQi~|x(4U$1^}-=bCUGoKEC@MsVwO`diuM2IethgKJ7-H zj<(6-id1PdS|c8>pLSW+Q=_X8bO^LY+iv0Xz=N9#j(K*1+n?Mf5Rr`9gS}M(l@f1! zS^78L_-B92`Du}d=bkystl{R{u_H@WxLFIZZWN}IQ(#TXu7c2{~-x~x4M9UW&SDLu#g zxjG(88-K4_LS&=zY`|tQ|oMX z@h3mJMndhW+11Z${a|s+=gC^0W0NPzsXoVF`R1?ir{9%<_C5M2g;tij>Tj`m40Lx- zb5)I|F<6c4Ev#_5U$Urs$pl85fiHhe_UY1xm&s)m`wOpvIcg$pV-b4CZp>6tM1WI43EG0xeMHQaD$PFC;-hunNw#^@Snf`U6BC` zgd8D;`$Z;pGHEg~)f4-*udH^(zi zsot^O1q?bnZRGRmBPZ$VvoX6Sqpq3_3kE|!#+Fo~=I3+xRrU1f$Z2-V%LIag3VG|z zzo2j8JesojO+#pe-f%Y+)X;>_t!tUOIOg& z*b%WAS1&H|xvvXKrjjX>&+X9f6Ld=|mS%Ej5Ur}(S!_lMjWYXbx!25tb)u1RQYec4 z#2N>Rli}!vV|Z)h;w{|;^L)S9@^8O=kG3vC zxh2natlz-HPpWuEWZ%r%JU1Rmtm8LWDQWVA-D2mLcJ?rvjhHPW^No6oa=D7rq1taM zIePoLskKC2zVOl*wmrGe<%Jzi9X>%=D}FKVD&fr-cu!?tZaD^^Uhf+R?xh<8+y!U{EqLzR9B|%_Z`{~cn zDdx_-^(+HNqNJ9@XFN#6=^r?XGbYbF58fx~ zcY=h%)SY{r^GvbZP!MmMj)23(Lp2p5xwD0LEPy`qDXZ)@DaE_cHxR;K?@&^09({+W ziErkx^I4xe9aM7fe-<+8*K11~>IZ^gmmqWEL0w?oM2A)wH32Ca$St|@W~ z4z1nqu$|7*Y>GV1Wj2Wnihu311c=A?`D9UASzHb+fA-V#-yAsW@BRK)J9n&9o?))9EI*A`a|GWRzs5NJ|A%&rBXg+w148O9GGav<+k$HI9slA-#F$CR6@C zBC4o`xu4 zs>jYB{0F(OC1t~Ihpb-KvXBKsyMogo-#yyv!070Zwu$gt7Bl?%vtI__&~LrO?Hkwm zWK)!;JEx`TaS~Gp(l5XC8oTu}yQ>@W9O#+L?9*=*1Fv-%SjtME+DK-3?$BAbW2#Tj z*G11UGo=+Vj+^@>21BDf+7>5v#vf)t^;uoNa~*>H7*itLAI&}B`LV+kbs}(PhmCZu z%-oW|0PD>RLmn5|j09B2&kV8?ll!bD<_UR58Sa%pf5776R(p@^H3?L=+!e~XGJ}3O zWRISDilgHryn9(-bQ*(^#-(Z}8{e`W68H>?^;;Uo|$l{d+>JGWqHe zX3}w93XA>>AM52i-xYY!a7@G3zxfr`I^ytinJoRi5#D+40|2^15oQ+VDK=y;F1-8# zI(>;;Tx4Km(^$+h(B;9PmwWY&9^$>* z_W?K;ndGHc5A&untg-wq7LA@yek_NA-kt%%hsIdk7JKr}2Y30k;}MPx2^8s19xpR! z7kPMP(96d5It2woE><&i=^UJRRu0_Ron42fB|4HyXPG=IYqb;K zW??RdLlI0ET?X#XJm$Bb`fmaF{2LeeH$VONY<(g+)2LT?`Z=*pg>s8(M~h1(*7lt` z?!SGR-~9To0kAo@MBvmUn~!B5Gt?=zb*$f&M8U8B@)3UiU4bt44n%3%i=;A=NNbpK z^bL4v$5ro(9^64Mr=w~n=llu{og8Ea2B!FA^Aj4#b40sJ?9FZCP>uVsMuVQ-3+$`W z5t?ipW4q19k~~-2WMEGvJFFfjU0yGhwA|O{8^GeVFuyDguAXbt?J$u!$g1bmldSJx z)QSyBHd8oM&-AZ6dxS@e^Ng!RLM2%#>;1)r&v;iVhJ5pYm@4dJabKxBX8elJS>Nm(@!7g=CzxQt8q?O zZ{H!K#`p|JqqKD`R<jy_d+>n1(GL5wlJIc$^>F{9B!hH)E&9DZOq~|} ziM_i?1uX*;5}aQBSt>gMMVd`_Uo6V3`qcp5e zHx<1c+)nk4u(q{LS@oh;DjL=+NzS;$hAwuBxJnk5RB!l{A9Cq_)>W^a!&aoIRr7D# ze#-hf1~pf-(yUM{=4mKedi-hbb<~bAGsT8(Xgrds7Z*p+J-F9+BbqE`CVO)Mj#0)>_{v(`~hJMUAd0 zHrv>;Ix;Cq0NM259-lE( z7-8CMU^XTHHaERYKiedfmFL*s-lt^Bpiz+KM5068t>F*<{?`Dwc>ONB8(Y-W{N%G| zPH=cyPAv_(Q57dsEmtd^FEPQ8q~Tap3S@7-nSth~dpX9#})3{BN1pVjkzVgYxr z__||`2s`&?Nt(sB2TVa0x7IOE$aBS10}Jb!Z324v>{KMm%*I4m*O&Eh6q-My^bk=S_AP(@$M;q-`p zIh+9|+@iO3rvY=Qn_^6H17tH;TLy4T;=Y{OCLh~lHX|`t9v!2BAi0>>cAMEoWKh;; zX>AshzQyaW39(S6SRuK!$Esdr?M3?_51kcWpHyQ`4q{Zoa;})a^?*DOG>R|FBwAEV z6?)BL6C36to4NxUYa%~~2amG5vx(a(YqFj_z^zCE^^GD2r4`1GNvty-KE~&M>0kfN zIYfTv_rBg~o5aC+YaN#McPQ0GV5fQpnBA6udC>0X_WV5pD%0Q}?kAPYd^RIbuQgCf z+2E#bQv9d{#Re;gG+LlH@F>4 z7@Wk~lF8dzS);ov!ipkjS^4Nb8fO5FI_!>}K19`_vM0U($y^3)MzQt<^d#zL3I`G( z1bVE@-rVM?XB9!kCv$xBpZs(F=;9yC=OryorGPX#Ouq0mo15~Smh~j2i5@C7+2dMl zo4p|$`eiZ17oLBWKl=XnWUUNFYMBg9n`G*ay>g1R+&U;mRqbw)K*-PaOLE}wje0S6 z^yt+f<;fFAiKz?)eDq2>d5XT@!6GJ+YIyvdN}u-5Z?UHzx%H_J1l`n z$D(6(eFfB^(CE+*^o21gc`=$zqI4K!b<5HX+yMud* z6yeK#C%6=7vb7}#(ynw7@5nfbJCZmU2>H326&W8gM+mzt_|(wtYd7A*WYgmJi*al< zorL;F@ODYS@ZraAv9UMHks)z3#ioa*YATo*oo0O}i>{PXvggNPx02fxC;t5FFOyAG znOP7W$Y~NxjH`p5&djZ!eaxr!W31On&&VWKRq!xs?k6=r!+{#Wt2078lV-H82$GHm z$Rs4dG3vdDoh@7iJ%<0Ez4v^Q`^@fp zKb>=qGt)geV*m^?0fJxx)9#X+Ls_e^vXZZSt0Y^NZP}7-sVrUly7sPWB};4RdZo2& ztu`l;OLDm+K_U>qAWhD>XL`D)bB_1LdCdC{_=Q!4=hbv|PxtdX&q>b--=i+I%VFZ$|5Z zwi={dG8PCMg{PlC)s4~OCZ3gNJlHs-GZNykEqz=1;vw031Ba&WY0w{gU7pwFUm8%^WXuej|`B=i408LDDtH*{tE!!eB-P1 zL`Ol%-qv0meX~KPA^h{=<7ar|@4kh_FT6T&a+u|5(HBmu8)G;^NL@Axr4sMmUm$xZ z&-?MmpT=Hmv$3%Oz|j*??#;{+?iQzKY5$O!RgqJR!-=+?WOzU>g}J45jvqfxsiu&( zH<#$^pJ07g_}u2xbC8+i!q|ras3c3cqavr}W}4}@wirJty1Ae&l8$Au24$avK_Bnk zUdHEBy;Qj!w$*&a-}v2s&!7F_?{T(QbmaBaI+}w+d`HAtca#lWxqXGc9&uF9^!hMb zqm)!;skgU}?QOC3EBkAlJ#!3)nqBzA*MC4@;25a`k-ZLgKQ51+R7!ZYMUJML>D(wE z&~8G`EHdB9C3*I-3%qtk^t{*U!xxECT9AFGOAXv!4H=ur#M*2L@3@+?y1qv+)TW(K zy2%hAUCz-xEH=f^Y~a9vNdfT9uI>{WID*Y2pRcsiY^zLUFxpQsSz>Kg0u+IceljgP zKASkuJ1grrEM_Lv{N!viOG{-=>>4A6vX*2*>~Y>%MprT7>{I|HEBkCNCphYoee{hU zB~#Bbe^uz^nCwTR(2G0tHr7&GG!zcS-cFOx{o2p+&fk6$fKyLjU~_Gq_?85vx{e)T zOU;&Rl*(M{@8dgficeKFOEA92q5%4OoSgLpct;_IK5=A#AHOzFQ3Y~6_7(<@@R}ym zW;2x{;FSw#DwQIYO?`kvVKiB2Hei2GE~Fh{FB3;cXz3-eVmZh#-miMD6ESpFE60W; zlT@{|aO87@hos76>)vgWnjCgjg>~89X6l;Q>ez8VwoHMY8^VXqJ`06Zj+RgOL8o_9 zP3t*y2)|^rIzIoApC|RE1eX4GW0ps{L@uW`?htmkuw}*PdO#U%rbV?w=+laq*x231 zuP$8u(EvLI;g!pi$JpGxOGF{D_hoE+duoZ~uE=p(lclFKNM=ty|J3m@O6?jAHLq~3 ze#lFYJtnkW*`aRH(4~TywWFy{X7+ZPaNj72ZH3~V&vW|1 zWhy(e4`a~I>QC>`R4C=aaEMA+gWW36Y>Fq?+!GzpNa$%qOc0 zlr<$>4KGH8Or9=mU@pl2x{q9D;Oo;&XcXnzPe8i(2B<}ltjKZ3Vl3z zVubm$1PV;0Hckb?e)QZJ)9>EG?3BQg#pOe5cXI75>6Jfrq?hgOJ*L;i|EpS^L>x{E zY5A?4QW39J^wxzl$Jt7+Vb`cB2$>T0ju7J~g|F=!Wqx?&efkyn*v`%S9Qwp>JaV$1 zsKdoO|vlUb~ijLG-QlP=mG8&hA|~r~PCKYlrG+)HN`Bg50|& za($}1i+}gEsfRt057)!>a6McP*TeO2JzNjh!}V}ITo2d(+V#IhCi1WU<@4>%L5b`c ztu|VEEf4NVS%Sr4p`swd6X_HKK0hW^(oxg2NEUK54&)|2?CQkeaH020WXl(@@~!WE zpDvrE&JW`UR7?sL>+Tevf8oQFDyqIZufuGyp&5{f{OTJwI6peUl1?HbojwoQT2ExPTkyMSM^bZb$*H-A7|aFq<4B6eY1>RmDlJ44m1i< z-&U%U&ZQ`;GK1gv#b0K<)Z%Zy`d{UY&!1<0b^%XNP{nL+JN&E|CSgZV{{ zT%C7z)~KnBg);+x6%BEfsrC$yki55!>y+%PdS#ZUKluUxGYj{r7cvyJ^4-C3gu#&pkU$s{veH71A`jFahQx_F`9@dbk0Jdh`Y;) zJ}0FVtC?Nwb%~JeY;MzVGzb|agSAuI#OgBeSifZ6f(}35y76PsiR_;5J<38%BIC_Y z6C3lZoEw#hb0)uqu3bV~6j`f#^yJ85El9SgQ8ja5)N#ZrJv^)XTlicOtu4@?wzr9` zN91K`HOGM8OkSZbpE%pk#{2it80CMB-EQWlW1Ko7B}A2aoVuF%W^5Yh4LPu@(yfDd zmc8UMTD!_VJG-d0id5I+%=2R*QZu4IJX#n2J|jPPPjujON6#?XIgClog}in94)nN4 zE2#H~LB~!mPqb5IkMarjRk>=T)xv5s5Kw5NVZV>JAG|}CD(T+X-r-pHD9uBW&B&<% zlE`^4-&x0~FB9}h`Ox|{1bTwxcO_fos_F=I>Dfq2hGSu+O(Yn^siLepgPAQ=T5mMl zNFBtfs&YkZS0A*CBou}2`=vf__@S{fUb zr0Djl-p7$qE7SMY=$5FP?vTjR?(!l-Lq6`!$T?tgpm+Fr;;fVeWmoo?nwzKloRlJ2 zO$Oe-B0ctPPn8p)OK9p6`A%dCwCquK_T_b7z=Ek=Ag=mAZ{FReyVJ>FRK7b9cF;W} z*^KY}@C_WkGJ1{hZTM&>Q&U&@rJs}ej9>i2zrbena^j>^GZYF1#>Xak^9NFzZjE>` zwuE*rSCvqI1gBk?e)>k7w$sM4>Zi2g<-)O(+`KCheMhK+wn8TLd#r?yo#4upWW=AH zoaE+%O|&(6rV|$?*}lJyFC_ci&D1y-akHyLQ@p8uGOUvs-Iq)ePiFx0|9uAT}F4G#X~o z3Q?7^9dEan+=j?`v_Hbm^c;QT&jV1&G$?6`6zh@=308F!)QGW+F^kI?WNJ>TY=-)J zapww{6r%U_gH>KS@(8d0RFFJRJ@*{uggo!}w(fJuYvJCOoZ(!iNI93Hr4d;i8VGUR zVc=a=-SXk{=Xv9;8_Xo+w_kkvDQ+!@E$?yq**?rtE{LqT-MyHtCbo8@n!{{yP%nw- zfB*m>07*naRIXI1tEx1c&5X@vV5DD-aM-m|^NCKJbk)fDTlkIQS53pc#<~iCq$ka`4+RQKc+(s@5^OPp_-g&T$Z(PnaVhgame1)bOxs^8b;IfoCSQXxkTSD~qSTL!I1Lr^uO;#Q0^fFzK zM7W+1zsVmrU~NZ;sX4tTf)Q@tyFqk9D$n+2x0x6j!PF;qw9{fiyDxjPbU8uaqSnTY?&it6sB)*7?$^r2mMUl z+9a9341EUqNX3&i1>wEN*6Y z#ZE-JJyfz;@*7fm-WeTc`o5g^$mvILH`83bDtwtqX_*-F(kjV5x=v4!kLPfxF=J(Y z9j8t8>lX_g9Uj7{=6Jt<^Co-xeadFB5rds$bXomW%JR&bq=Sw03N}?rUDFz{Z1c zHH>Y#oKc3|!ap-JTLdS^NFRz0ncH8%XcYQ%TcS+ez0J@`kpYVy{`DWe^Y^Em^}qko zr`xM}DP60i3>+r*(W_oFo8C@a!HI{%C&5xcR}!UCs?=C%<#@^53qWDe#p0D~xP9`! zn+HakZY$kNNo_fxq?%tNb_b6RpP{JATUW|ESPp7fREj!LsM3mO2zlhYQq=^fh6l;+ z%Xihs%|x@}Z2jT#i`Qx8Fgq=AzydCNp)mSX6yBLxCvO#5Nkc zDAM)j4kw%uMmogtH}?2>|L#?KeCk5s)?-v9K>m&p8?jxoY1#8`EkWhxCMV8IkA>OY zrqEeKyDb8JB65PieD^ze_k>=iu!f9P9Oz)p&Eop|B-N1ir_Uc@acP5NUMiZHeJ{qT4~sLtcK<$wvYgZ8jVi zF78rSJW;pmSX>hWZns)-^>$HC$nWaBPNI4bg|=KM-ney(U!VLicekWWQlqPpGHBUV zWnbO;5)Cy<#TV^l?#?wndP+bc!fpfq@Aux~3y;f%{Z?t4y_-8^R2l9+{Ec7W?RNyc zL*LfZDz`Z761v}f;{nk?FY$uVx|vOJaCpF|np;K~TG=`Ld-B_RCMz95Q3Q8alUxo& z84ift`Y#Ui%Iqx$R5N}qo}=P$QrHxpI6g7P%geXex-0bRbGkXzC61WZV`Qtg&)Gh4 za--o+Uf)~cv*!guT3acyZfelp6y${@@F@5Mp~_eN!yUZ*oo~}WEFebl?KSec0$L?ArecA_p~zNb@HkO- zAFsV3rRCw_Zbsb&PDO-27RqT3b!93kF~oX9lWMVq+oR^;?#A&c>KSnW_5jI1Hop>X7UMZe%FA)<95kF}tZF2| zd$X%NHgcBufjEpOh8|_*-~kmijwe-VqcciyC%#p}66(RPn*OUZ_xKNA{B2(RS5ijm z(=2o9{0m%pQ(ixM`5C_PS2y|Pe=Pj-!@v1Ee&y$+-}tL<-@sb15zmNRAL;SqXu#=F zH7BsUm&FIOjH!!qzFJ1pkbNBK?dRHqw<(q7vthrLSbhWFknlsJTEo_`;8k`Y)u=-* zPDfY*IzelY`*-De+ioZRaERqux$OG`9!5hWEUrjkpkpM+($W%Ty<8H`O`c<21wZEI z?(oRq2=*>1znjk_(Cus!RHb}}c@3IW4cAd|5axCc`OM`DET|ctE}xcaLmb9>*-f%+ z=iO!L3pQu{boZn<)-QpB4(Bnxd+iD@epX7|9F-hbZccIXDG4UEGl$Hn675X3%|J+t z*&?!-Z-%j0wR8*$?`aKMOob{L)gNWG=$O4F`u6_PCcpO4bG)@FfkxDz@KYwaL@}=+rb?D8c`Lmk2_kfkSa`5mo4)O}5sWUXn*1nDIWO5UZ`k9@U@gc1uoPSnY zR|@eo2Wt(EpOipAJH1Ifl|rlL16S)MIy!tT9mwUbFFeHNsyu&gKY`wC!R}Dt9Jx3y z*Dz)UNTNS*G1Twjt~y73r;gF?F;c1=I-W~09h3Ja#wM_8_0$x2;bwe?7fyYGVpD7c zjWW9{c`yk5mXaH^wW6oe`za2aZL%tGjm1kvvyZVL0l7T0B8v4b66IQ1L{ zb@^^*tI7JdTtN34X)d4jd@#<-Z}$+e82HH>qPq>g27wMO<`t36E$c3ipK-H#PiVhz zkYrrV0yVj8m;*+hQ}gyT8XGNZj>kU!a{$bL>kV#B-^Z)Qc9?xmLR!^Zwz-J4-sZ@M zp8(*Zql>S6<0ll`@{D^2DY6D56W!tqZY*!nKN_MVB(Im2lN@Xd4`;L`x;#$0Mum^x zfA`zC1CJ33D-4VMExOP2lU2Yft1~NjQ)-@Rvw^0|Px?TD8@^zG<+)uPYRpZ4$2eU+ zFUcjj1o$TVS%39?%wytLyfk)}zx#&RBJD_saG^lENAz4E(o1N!#=Wpyq7B6q#l;+> zFUd1&w|4M#jj?(~S`%CYEw;0A=DMR4`o_=Sa7l`MukF6w*2z9`Q-j zseKyi674hFaa)ZfoO0%u&Y!|CJ;&7M1^{_&08P+C*estf?yO<%2%u?;&a_m_Wai|8 zRX=Iw#o;mj>WW-2$DqNUm&)MP{HtQRd0t(ox-`>j^T31}%fwAg9C6zG@LEwCXq<20Cp@_vo7#>;{4=m=^5z5U!d* z^_kCFle~CR+MH6;J1jTi1oQIVor63%$2MU#I?bgG;_onH(8xa8*%bR#4Xe{)gUmJ? zS-tR8wE<5I&-UH9wKKWEG5N|Ot$*j zspY9@#E6-STKvZbSh*$6#L?j+SUW}8zjgIpP9Hgm(! zAX9lU25ys%v5AxT!}7l`U%SH2^fpnm(7H2ZrEaWaPy<&hnH>M{Qy=9!v$B`J`|sc9 z!hq@lY$ma}%oOb6Fdr$jIrZ!$>xyA2$uy~Q2GqXwwJH`hx2$HcvAeoYxh=oru4gzg z)XQ33^?~B!jnzBk)%?nnXHPJ_K8I;r=rw*~f)0gjyXGyid-E304@)NcYwx_qFQ0sd z1+DPY#)D0s`NHS<%dbjb=X0HX+z!b3&wlp?zw&d>u%;ZZ`284tgTwsjp7iL@gnFro zsVJZK^aNSEy@SIi%Hf=^2ZKV<+TFU(VWY;dLN>K(YdGC*VqW3P{cM5uVH2l9L5#yQv*!*Q^_&$BN+&t6MDTe-7B@Wdz$1wQru?k3il zLfbqRCcC=C$Z4TRU$IQwp<(`}IFwqSgEIjiL7fRMY@L&Xa#`C<9&r2K zeMZJ4JAM1^EwakF{`gCu;A>z11|xon3U;0v;_trp=X~^%1ax-G6Iahv#>-iFZRNtw@^rl z9G`#wJe$)g5^3S3OV6C;r`NC0h>5I?k9oOz{Wed$@T&mqJ-EsA#yyTj1TiyRFtEHP zeMe)xqr{3iCMSfJuPh&6zqZ9I^U_!TU&sU9{UOqioWn4YgQ&SLwO)kAX=pMRQZLY$o>k&?f~XZRz?H zEvp5+M`#(_oWncb&u~zo3~y~Q7?#Va#@u3RImIQN=E3WJhHv%(^z^_&!AC`OS?KT5~U{6>);otvDZj{6ie7RR7%EECaoA zX7Ap3j}JZjt8B)khqRcA@yNslj23a+uUxr}(`%-GSk7YhUW36C!dv@?S^9J(GO8ap z+SyH`lIGr$*vz3*$1!WdG}N7GFJHxf%*Mf6YNlRaKW3dgZ>@BQ$riwxmP| zd#gsx;bluHNn>VMim%uS9{>X*#<5Xe|jH>!+VSSh0u^@~0MK`%j9_r>e z7lsAQ?Y*TXH0l!FWA?JJyG%<|BK~rj_f}$@?-K{xTyK-E)^LX813TMsHWDHa8(9sP zM|{kv-pt)}2B*e>TltZCLxI7bF3Kq(?v-6F&kVWfcgizzq5bW(I-fitdp_8o;ZR?rKPvP(JwA@7 zw9PVdF5UhR&b|n@za@cP&$)3bZ>hdctB$qA-UqzrGn+6ud&%yLJ=!hgshS-$59A!n z4K0E0E~u+v^TimQqY}7JuNDcO8Y8L`ee%ZjDMruqQJfXu@2$02CVK|aSA^#)l@jG) z*`vSK#%8Qzvx|->9#(L;+bph%{q5^QFb-*%+>SsDO-MjZidK(3xu>{pBC@mX7tP%Oe*`h+DZW8>)lzdaPr)v z=oY2Vb)ju0SgDd%{B!x-<5X)U9I7dKCAp5Ks_qpg3;R2}TnI}5_>IMPX=|*s3_`o6 zJIk5l7fF9pl1SS@J$|19Ls0hZTdxso|NnEWI|ql*RK@4=c+DIe9cMv-v+b8|v%jao zWK8zqFlfxwx5XC5 z?M=ci(PP7*2yeggBRZdX0)WKqAs7s&g>8z>SO4L$Xm$e*0F19z)7o^N9Q99tQ@4v#>m}J0Aok464C%TO4*Vx{m>@?CFlE_zeYZYs^^kdvv zUgwn`Y@)ZwcOO>kv?^M(ZFvTVMaz+3A61owacgaK_~o;A;t3Ap2Orc^hl3%^!4{r~ zM5o#_1@a*WX%!)Py#FlS=lV&kNT#y8+t2m;l2zH-+TvJ$A39YEu(Y#`!Dix4P5$@r z&+qW`KmIs-?@M&Wa@5L61%1C`PV=Sz`}_RfKm8m4)3ehA;#KNLB|@?pKOoRMh~a^R zF2>KECZQ=%T$2c;!*3v-kTW!Uf{Z&&yrWRn276s>EjF;5RLNK^PH$L4vnWy2bB{g4 zn>TNfQjz2B)H*o@A#SgE(6_+TkjTt|zQ(bvi}q=u*Ye$ElBF7Tg<_{E7l{lCKLmS2 z9B%F4>XMSMg}XC6d1j2rm_%pOg&1$XdW*VE@!EEo&-}(e;0MiJ;EL_qaINNyaG=qg$_C zrKZtQa!C|@?;uM)+r+A}+&lRa$(9zMQ}$)uJLLC&`wIZKW7@mFec|%G`9)ZBx1HZHBX>Ni`FdBeN8FBM=m~3OJ#6g z|H`+CeEKQ&UzZ+)15+DgL?J*P#2~jvr$LqdrT56!YMgjc5FhKy`vjvt>{*FyS&S~4 z+A6WMDg$%ZNh+k{%fIkBzVXW6@R%yO%=W@Yc8_C5DbqPIbdtTpB-?i+6M6nKAK~qv zN`@k3f||w6vt7c+TclZ8nB$CJ_Sx_{F?qa-+t-{DN} z2vaH(HxTV1QO@HqioC=cO-A}B+1!wf*rBF^ULzUscBM>;3a7>;V*kaz{!<=5ae{zO zW#f;X;>wL1SQLNkW_Kvn%wUwWI9#6R^S>Y&yxHk>LOvUNRiU-H;$UHVnWK-0j?5Nx z#FjGD)Odt}$w#<5EqlIr+{OBqg<3}T=<)c`(dOEUWWY|Ic!5l|j6ow&}IZkACqJeC6${m~Ke+^#AJ>4uRp+8f)76~Wq}5h4U<{R%RiQ$$mkIx z=N~`9cwOXcC%4JGs<64x`2_pBt86Od=odOC8qAG`IH-JM!OvJ z^osu3+0fE5N)Ou;7cP^}&4W(#)_?oPPk8ad@CSX1@%;?VQU}LQ^a8NF^Dd_^d`kB0 ztn%;v;BT<#gzn?fVKT8eo~UF7cG9c-@@GCwd0F^J8|k6AXW`8?>GK@vvEwd^jJr>a z<4Po$uBy>9py|4>)KuI5%Z_EI!_9!y@LZ7>5{%>&XI*+({C#2@P5+LZin zs@UedLM6{7V=Tot8S#m($;WIIC}6W|6|H+{HDb72qPr8BI!i0}(WFI(b`0yt8|C~@ z`l1vL4a}&Sy-%MxO>8PoOl3oiQ5WH2k%pR$s^4p}b_i*`WI_Ew8>rDurGlBk69LAR zUv<1=lyY*BwX#CJbUWGK+9jj>ijJ-*UWZfxv}$#7ehVE*@kdsgaWr_Cv#tpZfJy77qgQ0nT;D_I5`8f;ah~_zcn5P>zS~;05}RLQQT5tA`qU*J+?*nz zdd1H?ei5ItzccUO#oN_EEicbh+b>bBX*mA0lvw7ICH7+pEd9cJ#Y&s%YL1b|#V74I zTe$wVRML%KI?ej^>r|}5OJ@gMT(8C`?8+H;MnsQy z(Fuw4Cbe3H(TLd4T%d(<+lxl^oF|KOv~wj6D&n(-%x>yVu>n4`%+DRt$}5E3i;s{? zuVPiBY2qs-OhMlV8Nr3yTU>tn32whEvx==}Pt)xZIkuX8-1+v~=sqU4t0Qfpnrsp2 z6sFCORlk4Ou|{MrmgyN%67sWwO)>=QUP%n_5E)2+=pCPw;bs}`e! zr8*^#2eb0wSElEPo)Fvx)(_Yy@1j-JUM7zoZ-DHQP>Y5y;N?SLk)WU6fqwK!dA)6_;8IA)KBt%B z!aA{#*s_zpVe*LtF|F8vqNa_lt-+uwcB<_l8eN5uNBm(9(+rJ^oMtk4#vgkTzo;r6 z#1{DIKl{qxpK{i3|K6wCIXO#i-Cdx|+(k^yy&F8m3{?cH6Z$KOe(UzFkZZ@i9P0ZKgf(kJ=OwOQ7bQ&Z8K0W%iGWNBlQ zcCN;A{h|%66G6I8J6*W)q8u%Q4oaH^ z{7zx2Rw+tOBYhKSw9Ma#(?IrNv=0+>L&_k(l}PVmu=i58iE~w6&JziXfJR+G^ldFE zRgPD&H`%34{B=<>-A6nOO@z^##duWnRSs6~(soHdoi@I~#EWu)O5I5?(r;u>&8yj( zxlgTGL!(}|8#ZR&e3uta3cyeI(NS*1Vmx+SUO!k|BetzzylqzY(pi>NY00zw#|YZ3 zRQ*ERdn*f+GFd+IVVSe@V11rqTAr`V-N9kLOng~rwWmA4e^6y|P)fv%V@KF{aGksw zZ!qEZlgK7$MP#4;EeCP4D8qDWAJ=Y;bEoBEc+04zc4Z2mn#)u!7YVtXk+giF4w#^zF6u z2Q-@woF;LAM!jRCH#exO(u%gD#Pa$YI-?kqx>3tFmpVtd z{Ue!AnC@_}cgFoXewF8r#2ej^sg(K6uji(y{gS?0yZF7o?d{*UsE1G>OVHd=9)N{d)u3g zoStMx&0EA)!`gPEQ!@j-W-}8L=Xm3mK+F%GxgIxQ%9$|GWq}jAOJ~3 zK~%W6CC_x$qQ!LT6u!Fxx-t3lU*Kzh^-V&(N@wggc=QF4r$2q=Evow|s%nJ8&b>I7 zKQ_vL|EkOjvxmY2y*^fM%N)|)NGFAo@VL!r#I9|lSG2tF;&Z%m^(HCh$m?ix`T56* zZ;KA?4tPlE%Y5uv(Jz~u>x4t11I^D`?SfEX1LIV=u8s0&1YcPFuIf~In0cMlR= zI3q!*#Nj@P=^e5i8ntH+`}5lb)%c6&o;^)wVT*0mgKmqu;Lwg)E3}QL*YP@f0dO0= zbPsk=Qo)Vae)t-FBV&wp$ul)f8eW@OCJ+#rnY$CCT1@i^C4VNpj^#rMq>S}HLUXsx zSO4Z~axNDy&=W{AeJBA_*O-w$n;BJ2}e1s8@c|f z1Rl>m<;NV~CZjQAH8}Ndr1K+?Ix*d4aC(b5;rYQ4|n4CiY`kh zGMsq!92*bhnNo*E&R&v$#hvfILhDFBUNvJe;mBbuJ8`KJ)X0#TpWb{Aw@d7`wyvj9 z+~sJu*a?@Wi7%=KnD1sdI9$TkDNb)CZX~C#;jsw4w1x(Y>sf|6#ir~QH@H8eX096j zbcAe7jEHVtU)iRVZK2i3KCEU7*;0~<8s9ZII>fyzSJ8NdpXwDI@paXg81iE<>FM&w zdwQLQ<$Eib!@?iy2Rroj_3+*;u_?aD0LRBf2X@)ATz>2#|K>ma1ps}$Ph!*_plQhk zYhiPPOHX?F`j5&I3So6Y?H%%Pkhjx6Di^`(+AgU=j%_vMex+E%qh=X)2O=z#HYn_gt`1Fh zvN#jNr|=;*%1u7`i!W07so0DiOA1$|!u_Q9sLvlcNwp~Z*lyI&ZdX~UiZ3)U=%(9i zCa+p%ETKMv)jD@(#O7pz9>%?9?ySl)1+q;#j)`wLIW|IKDaX~K1Qi^aL-wmBhQ=f) z)ykEK4E1v`FFyOB&W0f;y1Ae)F%)%UiU_T%ds(uxX%=_-5~m;?EqWVgIR(4BwJZ34f(9YVaKjBaadOU7Bwx$ z#*dLvznkqh5H|XmRRJ$c)Xbn>bU^ifob-^9mYOH6ZR)YQ!Yro6w{eD?G;=jV4xv{r z=Edr9&=D0~UQMUbm=qvZR~N~p1xl(c-&xe~g+KV-!*bS#>*0F19(IXN8@KxG;>T$`s<$5A~?TsIB=XHs`{ru!0kN)g)EH6n0u+yZaWVJA9kSO7O ze~MZ>k1Z{coc=%ucNEf9tWlv@s8fnd^eN;BFmPgog*BmNuF&S}nSN?5q350D>(sV{ z?z*5Aj~kM^5?9a}!DcSwJ1r5}?9?KCehWQnxcp(cj<>&uH-GXv0MDI!f{+4Hxsh2X zb>jiS35mEC$~B^1H_fUl`&v2R^yCSg3jIt&gS}jdW?dmw>g*WQIFxLwNV!~MXjn=% zG=)6xuH3?-kmI6bBg8f~*jJhBmfe81B0SyW>*2lI5BSXIJ_*48c>67SO2ho1AI0)*wj_=ZjV_4(WTMDtbVWubALCzxl;CFjTZ|q@KB@eoG3ya-~(=JiP z!NC#Y8R64Zs>Sm!oZ|X*$rNm^&0?!r8C6l|TPrJAG{Yp-Oub}G{W=a zQc{;|)X_HEXnKUlXV=#7rnGp}7=o4C_gP(8Com$Bu8W<$Jo#}!8GG&RpK`owg1LQ> z)3DLYLVO#2Nq(#9aT1TkXgE~#zXcbDC0f6;vqQVEPc&HF)2lgPv0OIQ6M5-H`(nS@(P?G=;)wVqbFODvXpeaj2AEQHOXR` z!*14hx9L;}zUZ`MHYLLR)Vara_0?C&nq<%CI|rGmBxx#i&Ojhc@3CX-9Ljg?F0D~l zh;|O2A4AK)!j?on2Sx^2-mZKwGN{uIeZC&bO*OFD*uzeBpMI-6`0l0GeI=}z#{u+Sa`xpP1|MJ5>A!HSu&>sz9 z(vBM){oITZv&m;WxAHV!1KKwB$k-G8jZM?xA7T3f^R0>VBnl>f{Y-VUTNmue* zIwQ#2Tk$>o3aG78udo=aU{-_16RADCong-Pi%xJmOML6rTlgcQoAlZi9(vL1gg&lL z8`MRQB{vSJ)Y1fcdI7k7<9#k%9_N++D&v1XiV#(+O62a#Ui$5jdvR%0vH4;L_3B{SPSaGW2%@KaDv9c%oyxcdzPhP%GI3T*c`}_#6U%k#JUl4TUpS(JYBP9Cu!SyV^^_$P~ z+S`)#uJ}rv?VI3_|Fc4$v|4F5jd+FBgbV?OB0`(TPF|ww+vh7^77&$|#=~K)&GAXm zMa^7{{{Ap?n+o06(}}TJ;nF81Gkg07?~$&FY>khMGBvjjcFDBOZNJS#>3!S)+loKnq(KvT?2G?**V;k457=pK7i`he2eqS}CwuSYY(HWY6N888R8khLto`{>c#+ai8el zLwg&)#m>yTqPv})W{RaG&7>HOd-WU#@jA(jLaIOIq8OKqUZT?G=n*aLJ=OBir=yuu~SJvS1Fu)URKuPn4bGk%fe_6ilV ze7-)NC+n2+_UNlPy*9eK#V^n|4zUGIRJO%0sMQQq%Ua?dVcg@r1EiD=Fz6w3P$HwM zb^ggqzr+{+{qJBIl~GrdLm^gg&Y>BRbIR{0DK!cNRn~KNcZ=?b@IxpV<<%Pxs0pd@ z8JxJoZH%jO4zB)g=JyvcBeL0Y2I(+D-5`6()_j=MzE(0DggQF0`c)f8*~s=znfG_a z2f28nhfpTLlTV8sI9xg4dvl_TT^&JoYegnqiXLGbMuQ!LY=gC#Mf#t(Kz~)v;^yo% znh`UWyqYMWFH*M&oi~@)7`@m}d`9Fczg;1EYzT*4Y?;;4WOZ?q(P7Cb$5Sy@72Qki zGS7{l;7tWuHFok*-h26{SVBU(Gee`yuW#Uvh+gyR%RJb<&!Yp<`qXL~sK!)v*Z2^n z`8}p`Vi(PZ4jw-`N;@sGwr;5tv6?9&zn!QSh&a1&XjPf5CdKxBsggPS%o7xD?-17u zKV%oYpa zDf&Qgl<6x!p(iY~%npF|$~LCY$^~}&_$K4haxA?I+_&LMX_$0^%$pj=sO|&w6MBCUGHRnPYh9LBuZ&L z&Ez>zh7n(lrS~Mb(Q)x4-~Z8f=uF9(jy`{q+1+LQdU;(eRWW;<^mL1}x{_RHM~%O5 zpB`ps{*cpM!XI0X3T+FpugY?UhtW2S=+ro#xakm|In45HF+|h5iyxFI4+ICfcIPfn z4T!S8l}wY`F4Hq44q{i-3yE&_HblXXo(xf_ItV(%DUMgm?5~Rc$ZYPB(6<ioC&q+^dvqMXBF3xv<^aB9S zy!1H^)F__K?Fjt?HgXxE&wy3O&8a!QIwf-a3m-W}wN>Hz1EJT%a6cb=Nq*<*&Fier zWa&R8<%hFZZ=-Lu=^qngdiLlbnbi&aYToPg_AaHV1TWte8*=u9pGHIIbLNG|Ddh?T zT*8<3z6jcEhV=i(-g`D#dY<>4zms!5eM09Pr+cP*0%m~0U=Rk0pqL~|pd`vREy|Wl zmQ`MRop;OH^17VLDwXV#tfEOpk{~Ds0Yn;L089=&v2#u*boa?Q?~Cgu_Y3%iRW;A6 z?&@>;xxTK*S$sQK(Kcv}V z=lPeO1t3}2W7Ii9S>eFdYiaxep{-)RK}uUf8v3A}?8!Ozoc=rug&lTdvWGTr9=A)&wHsozoSmHH{LwG){WoO-@8A;ytW_^F-XUem zFF*StU;p|)2jH2PCpk!L^6G0c`Szdui*N8p|N3|N`fvX`S?8piXiQ65;o}~iFmkvp zdl>2IyiJFKa444wbcB6m^5R5o?mR%>5Posk z{q%GV6OBla+vu<{G1<#TM3vOF;%v?>VN!uNeKtd}q^6!0>f|hkbUMU9o?E=fsWZcT z{Gk}dUQduh&dF{OUEu91!l9p|bVj-TmaLqSLM z_H`3U#>gsYv^IAqYYJM$po2blmNvKSufJ`CYPHOk3f$jV+oWqSjN2^1#O-FBUfU6V zbW0`ypA1bAt!)C}8>#YiWsF0seD>ke56{qBmp)4I^?&#}dmoFee{&(h(Oxfi z)}*M&qt#*MlZOjD z`$g%^)7*)p^LGQG*BN>I~>C|dz)~e_mBFFydFF)X`fAYLa|AES!~vg(j1ja4QrR2U!WXf<-vW%-9oE%pNGvA zvGwkz_LzRMor+0H&7U6`BxpJyq_8eC^CmW?TWya2)AOi*yZ{3%B(FFYr4NvgYn8=fr z#sl2BBGENjXQ~CBE*GUGIiF&?mdP%0s;}R>gJa*oj)Hb_*Q^whIZmp;tm7ayp?+UDieTsFkJL!slJy z5TTIRSW63=WE^4a^UCox7WnVqeegHOiTuXze7?1o6XodXabStr2(`qZo~t|}URU7N zdnHVMEmp4()yCQezchK4s;cA7=TeOL0=yLw1*h3g(ckUEW)Vh;*7s>`$$O5BpTgE- zB%2h0<@Wff#Zx5ma`AbcmP9OpUNxiZvB`&W*9ABE#Ej8Bnk! zl~$2jP0K#w*!efJ48M4kd|A%A(`n~08zZ`}>d*68#*Rq#bZ#TY%@1$WQViGG_Hc?#R^^-60v0a34W-ZAx$4>FRAHNB}zx=t+@ri;0sFXE0eE~}Qk}b5C3+&h% z_|kIr`9>2@7S5cPsNwB2>Ke@+=Q=^PMBR#gQx8`Y#bZ~1y0q#0%^t~WMXWqT~5y3(?rksj6dg>fK zIz&c|=H5sgFg`TFwsLd={vLX|{j5i29c#O5G*aS37wbk&Uic#4{>yIy@N2KUg0{!T z%3YyjQ?bP>KmP)yq{^1unB%aK#95cKolWm!ZOZv%x04jd!VK)oXJ?)~%Er zEtiSrGPwLgn=YG$+qWcodvt1spg+vp^M4J%sIHBl|H9|_&RZAdyE|#z9vg=$TM|Fm z;qN{Bw|Hk=jE_P-!)UK4v_dk5$Jxf}z9>s`0iOQsXSsY&&ax})qSMtz{7~+3eRrFo zVNv+Yi}Uz(HVg)3q`1d<^Ts@0tH{u?P>AmKlU&>s8S&!G4Czvdm7B7T*S_=$@4PF0 zo6kRef&B`+`-4vvy#zU|tngf$$i%Us=eYRbKAi^Pk$?7^f16K|bA0e4G2Vasx4+6? z{8tII1xJU--2MBS^`mqpqooSl=C=o_6!eJ=D8Y??;djpnR~QM)+xM} zPinC=TJ#yjz%&f%ap`T8ifRBwbP=mVbYyQ}h*Y}B_!;57&GkhteX>YXMfXRZ9%J{x zKD&!T=W$1dPNNC;kQnb9b9&qgPUwZn84?RIRuxaQ4NUUr%E!cfqW6yY4)Gr9NgEUd z%0!s?%ThJ-izlbCHg()y>J`}cUKYwt3JPdFSF7?*|M8dEyDUNA>pL+Xs({6@3+MUZ zjXOA1KhuXR+c^DNMn`)ANN*nU>!1G;hkIhUIXz~QnI?vI(cjU9ZCY-T)%K>I^g)T9 zagp=Ky4v~Ho8RY`PD{V-(#`>8pO(Uo>OC6?V^q*p>+wBYB@Y_6+-WJ7B#Q!vd$Q{<1*pZf6vOAMa;nK>zE-Sjba_7}yHyd~F z(B3YYDPIwSZdHBJ*U6RHP0FelvA?~G+?9v;#$^AKJ>6V=^bpUq$c*k{ldY{CEFJRs z-NhnihwLd-GtjV>s1!v;Haxu?7&b8|sIXM3L?dM+mKB*0^!TubgkKU{Tlm^S)KuSZ zExtu(-Ge49PGhK$pceF#u!vqXEpC&xHqa{k_N_pZ!K$2dyWYyy{beSeIS;`7!zCOx zGx_qS+;Jwr#JGc`vN`m%AR2?no_yL%qh-Wp(yFsAa}do?vC4bv^*U3d?Hm~qyXjm1 z<70+qra7D!x?b$l(iRgwZrQDztXOy}CIP^We43wo^%$dN;k~tDna-{dsigP^ zi;Pany=Hfp2@Z-Z+O1@%HqErFA%aUgyJV~_l9$E#-FDPCG0?~6j@U2bV-9AXlIpLc z#t=(eH_6)Mp3aP&Wq0=hdnys;GzV#On<%IuiwBh&zD*mecKLg!T~Dzh=j@#tB)FSp zSyj>a488o$zxdYUp2)|~ZHezR4Y zljzFOligg~ULmt9ks3p-PDM>ioEh&SUr3_~$nWKYH05fAVpG1G*w{pC0-Z^fv-*8_ z9DX7XB~rTWFfwhBsE}C#DzBg^dpJh-oL(%x7|;pQ85|tg)S82Kq8rOReMB-E1-qH~ z8<**IOVoWgwZIFjzFkFPxaEZ1(o1%Q1xjJ510ep@0&Lp~qc zY3cvk+dAMEpL>N^QSNMZ;XY2UANGW{G+HCou!YjHWMZeEK2EieB41TKZoA9e*_4Qw zx#DCfJb({XLT5Z+tDM83dOh+b*vaJR9FlczEIgvC(ZVXNk%j#!of0J@vvIt#m1N3!uV>C+C zYtR}X+j23hO3$6tN$&1Z*2|v9+YOvLC+EJVsdMGZJ@ytP;(Fn=&lB|2nCg^1zIU$O zrm~kpJ1jiV%48_240_tBr(vn#Ry`?;_ve}J9AaI;%@pb-{^3`Cjhm{Br8AzS?s2lB zpc7Vh;~Y6Jnc174{*ZHnLwKALk)P=B@h9K^D+2vOm*vAG(Z$=8R2IhF)`uotW9Att zja=JL@)wt{0`U6Cc|O%AXE z(Qz()BALk-dV8tbwum8_ghtj)Jzpb}kh460vJ0EfO(`w+cq6t%Uyo#A4mJ)crsI6> zIVpj1k;_N(HV}^?AKR(*m&EnP)1vTw> z9vhkD(+6vGsB$WoZ-j@BZnLcV*-twH zG)g60Udfm~VQi=7cHo?lv){OTiGouy5c%yjP6V6$Xi50``QB5!_)BLvNQ<2NZ@>E; zJnaE8WmTfvY!NRD4Ie*yhT7J3)ZlEK?Zd3C#c8HxPhCM5MGE+Q60zT1+~$>+o}^}# zXz%L!8m+p>xrxyUg3dPn_zxvR>SzzK5s5QAAQ>Ku(@t_Pg0Ee8(`UD_vMzM!^R$t# zvw?zAwudnmOk+XCR z$YJKnc@nWYwUY2A#zV{_A@0qI+&kHRlDVx6KQdZM5HzM22U5fM6lji}5 z))vVeHkmjkI%DVRHb2QN~fBrB3 zo)^x239C=CLwg5t3P>iYqOWn@cZ{kkb&bY$Ntui66csF%FO0qFrkE5y?(YxslXor> zm=Rv=Ycr8iW!GKqJ~omC_Eq)4;7}WPw{Bsw34hI9U**hDCj((Af1Tgj$JS$FSY`c+ zH6N!YjsmbAS%IpKgDt5l@&+A5m$D=@vWI*n0fr`v55L zQ{$5a0(KVXrE(~i+@V(YkgLkyj~tm|#y7x!`ibT&!W6d_sU+mPO`n~* z?GRmAWJ0Ic&3gI}&7R7JthHEBy_kp1G{5xhah{Qn@>Ze1Q$0ick8l1N0O!7NoO6Ah z{N9_ghmfa}`9lj6UAyXknXyhuiREM<0IU0?^szsi7H%E$AX8kSley((d? zK3HQ>L3`C|HKs<#$*8K4VAzE>rr}XavYKh9p7yE(lUdC}*o;vzYuHz1>!+rk0F(S* zudRX6@1w&c{F2P2NhPx+)LEJuP*~X}reI4Z`g-YXokdujO_W9h3GgGqU4y&32lvL^ z-Q9u{2oT)e-QC@xaT<4myF=s6u$j#~v#Rx1i~6eWeeXH5h}9I!t?AXvy!@26gGy|T z&AnsKIK>48IedQ-at?}`#4%CWFztxZ;Y5Wu_cn!XTjwsX<~dmbF1pE9wyHePf`k77OMm(4EpcWRfF04l1_nm;@rZ0e>UP4}v1r#yt?N!D5-p>g zMBf%D%oxJNUGXC-HrfR_0vnzC0BUwFNFGs3e81xe9g6438kp08F*!0DFR=0U?TwOC z(!b0IUsHhHn|cWcixh37qCQO%y~?fepmURQp;Xl#FW>`DFd_L z0UjNSc-S;G8&YhH3Vk@?r1?aVyuI39gOnH~kP{{na|HNQ$z1=Cvf?S1{O#-ermu@W z*wEglF5@gq{e5iJJI9*yV~3?#cBXyaZaWSE!O{u0kT$fP&ihC5vTNRIX;DQS;F~Ds zg#}wAnW2!rPC=)k-9p^rxTsCoo}h_ktfJe^LBLyLFYiHB!0narvaH!3H2LrB{Vh-( z9U(s*w{wRH5~6K5tdVfFHZZ9gEM-?hLs_*Y^6aNBuEh<7I~A0P?=o4pJ|Lq}-w{*+ zglDgK9+COV#nx=IuUV_gKa#_NJs%*(DhForimO8>ADqa|AdN-SnImKn z5eG~84)bbB{CP!EbM^&a#9*;yo9cfbBmGd@{vV760?bIygO1((0g%Rk!Ocpxr~W!) z>rX|Mg*3Q?V&9@rAu^DvrT~?zkL4&s#+O!YM2B# zV+8sK#ATaBJuC3)Ue(3IEnC!6iqH0VfN7?e0XP4?;^idDT_{!huwINGh4uU*tPFyE z>ZkVPwY9gDe$aTn&>$XpbUu$j<@36vW!mf&`^Fj<%Aw)^;NHLC7WQ7X)iZ3NXAJcW zy^$O6lZ(`IZG9_d_@b}`7G-fl=G=pkDf7CIl*vO=ZChJ2t&B_2Txz;PiO%$qr)*T5 zrD{Qyke3Zms~Pn#(>C^Jsd1e?(ltHG_F#DsNvU1oh7DDli^8{+#(8#*O_>2sUb*be zfkLUfrUmJT{-c?~YIrC;!(V-s%sqkn?$2kV{Z)l z7ugJsG?U!1MEdo8+4SfVu)-|OBj*85Jxi@*s4Q8~ zq_2*X7*$8AJU)ESXD>>}J5KiLIGd-ER8Qg=y#x zRZPzv{Aaq*TDG?TT05O(4&c3dTwcD;WLW7Y^YJwn_T=`kssKZ^g2Zc-`cISGpMpt= zH&FFvF9SoGWn;=qhoQP(qCKC_g`X6*{H3BgjsS{{K2Ckzy$LyXi6}9zCn>;s!WfnTAq_(MVywNnj-S zI8S=CfAZmiYuSNo1;ANIzCR&0BUG{8HUyIS#1}%`^R~+D@~~@uu1`kp^Oa~LvRZWm zjr_4Z-J0vMP<#%7eTraUHi>`SOvP#wmQW+ryEZ0pevto4dbOH;R!I1jpjh?gJD zGRRG4=hNl&x9FfMN?*STPqfGahF_fkcL;OVd9UD~B(ET8OpjC4v3>lE>dqJ8u2&|; z(xi7?d_MUIrdSfe;fq)00^UVMK8yjYxx`J3ci$6;J_JW1fK$_3O-5h4X zu|{-%VdBgExISj_nYxH@1s`pPo4CU9I?LWWJNHU28)5k=fSYm|P?v!IACTUc?~vP> zkEo6WbxnV#Xv>%=HWoVK(K&;JX(yEC(Cg;)lfq3VsIW&N2)wajT)O5EX|MYM5pn_} z-;21V88YWL{_ZM23g30l-Tz=C(G(P(E?&ZLKuF`3-QNX0TEiM#wi+K0_9uS5A;8MG zN(*F?trXq|Ndylg6dG9HNtdt*sCBFQ?fhVuDqV?zS5#|O&Fa#o>V!f*%U898<7WDV*y-6Gn;>A$}o}F@<}*6 zmx_s>o^{^qty~h6z zebvM33N-!2ehVAT+_fEnl;hy*r6Xd1wx|%@2?r#OXR~~RINTh5fJebNbcFG|+(>ei z-E_qh`EMI$&QUl0+paBhU8x0il_t4X1Xs;w(w9&7W<<&n##Qv(zUR!M~TF@zh>_VwqJvn~?7LQ#E*Ba~Vb1Ja|I;m!f&IL_j`C>L#>nyA1Ym&pG^&z?V|LlY9~A|MUV%}p?u`_K8pFDUA-}W zamz%T^Cm)=#>`&)`6GX4&u0K5v9@>eu$g{;@w#Is3u5kne-se&UN$SfKC%4 z#7r8U{Gk4``Q~&1PmSwKr+@g?8G|*Jv z-xFj+UqV-OW=<#sVr|#^-BN+K@Fj01%P*$=(d^gK!6*M!a^Su|)|lJTb?5CHOyN0c zJ<;&is)E?}ohWJ)_&+ao<^75FVq)jCE4f!ek1vU0Iy3i6yrG_!KX%0K+v&>wx)*}_Oj`1lIUkEe5q(B5!w3gvia$wfkR;vw_%_quBb4c2m`N$G$d2fR)# zH1dLN3kc`V&fIvqF{8+?Q}X)+J#k}*H$oy-|-vdmkKip z{Y0a}cVd1hbmnvom~~ik-5u|+SseVw>*)?nw?*D7{CbY~_7tYcTUinh7Ix1Pe*ewG zw{t^00B7zdf;P9em+k9&MQ&h)BgUXorUYCr(-xP_K07X;ruoyBu=2$ZWl%BFcn8?} z>uyBKj+TMXwPnbIHkzK=QuxgcE+z;r+Yoyf_xkQc;-uXg(LJGxc$Oo4Hr#UjJ{sBi zG4wuP0nSIpERjR~W57UY*sLzBI14&~$%41y#VZPyvAz3S@XC#~UHNZUqi&a^L0s+!7@RN$il9-yuUc z#_lBKIJ?#B_Kz8n#uank;+ZpDRzpZO<3=?sispguh&@r;n%KrB_ScF*yZu#W_^tWJ z%PuJhm}xqYCZ<%}bHL;>%>zmXjshy2L^~ACQ3+kbiW|0)N=J;KuqF<PUGHd?fUdw% z`iPhASjXRTNlk+Lc^s{yV@&0CG;aJ)&6cruV8)&B>TIOJ!O=4QkgI^(OM*Y&{i__t za?UR$pDvg`5WeJYwDtSG1%~1~?IYT@XF9R2F`dmVxwW}x@2e&EMeb0C*9QFTp*XTU zKgnA{-x}x6;AzsZtC%zO_aUm*Kol}xDRs3H)L_tNb*uI!Kgq}t*ACrbS47oE^``mC zsvqsGW;Q>&Let+76J;QDh4OOp-NX5!mh$hz&YNHkFL`%|E%(*!L%=P{uMa`^P>DjH zt|!l4+4B~pD4iZp6w|)&W#-3}wZB0cmv-0tn4U|T>FG&7*TD{dVtQDS)%Sfbm(=g8 zu@!|M*}e?R#yoEIDuj)Xc!msA>0$Z*dUwt3|F?J@@Btx#cDh`+lYin5x1d;Y_PfFM z%ZC@RcR331yP$4+yCBpQbOu|*75j(|_5>n_2}{d0Wvx74tlW48+lxcm+o&aG=FhusIB;DK9@cy>|4wq^zPA%(ARr?u}? zU$!BK1WoP{4tze|5u0Ih0$J;rI_zn9XI7;_ucJ;TwdZ0Iynp;eoLD zu(z#`XXY>AmwanE6%jzDLUi>zByt)ooi0jIN6KbrhiQ4r0S~5YD ze2$s1cGkx#jFomqQ>Bo#XY=p%LMva}*ga-_qoamzzwAEeCk)#LQw@@S7Pn{*I?~>Z zg^4cPO9ATKJwvAP;<*uXeSxA1W_L!8D0iMMziyk#kw{E$GSvPxdBcfdP&d*cYdBSf4LLw{H)w|Pg#xX|wZ;cJz4gOa z?Vqc(j|$dxDRt3S=eI5>8=E2k3b*Gi$67U`|2^$*cG$#NtVReG*)5rWN5O9>4Zryk zpNuckI=-^eB~@5Uk}Y>knVm(B#cX^pJF+roktesqmdX`zUPQJsa^U#ljrG;t4mrLT z?wPRLPpk=HyrhPMRYlYh=ZLu;FW)TX{kg%NZHlmhC8EAu8M}rFf;7LB6`UlZSi&Tj zctMOv8;tur;u{IsBL4_LF*Az=ozS*8&=dvjVYEf!`|}AG71+m3n4O%ZjF&$Qn`cyb z`czcL0>Ri1DaXI{;NS8|@ijP0O=*Aol#z&a;sVN_)gx-`y*CTC*S~vvi*|n*a@AH@ zrLpFWQ?ztVU^!%2kYEP<5wt5J*Mlepfv9r?#T=!dp0+-xN$i=RnRw+AC8fe)sT#B1 zcO?JBK#*k-T%DbMOb&l|MV2AR3iupyjKS1jJtC<`d9Y_=wTJcnLaB^I%u0`sD3L52 z&Noo3|G{VBYZVo0FkRSMbCejC8>jhum^{azre<>Q1bcNOr{?Bn^Q>yT;kn?52krnr z5({*M;en!n#Zvncbxd6KTeHhbp*6Yx(%=mtm>oN^{^(ClP}aMoac<5uGO+;~frF5$ zvF6m*=eILUc&m)_awM0$oqu^*qfL|?tn2c(li}37ZB)}WJJL8c4aZcPovFJ!P$(&X zg!pnY2K?eIOG!J4TubT+FQ<*Cvq25U<%1uys+FzyX&4OdNdj*>^xPmv?V+Avw77uxU#l}D?X8wNG!zKaJaw5Q@v_QYMBrypd@ zY6oqK&6CTH#D7J|5v20&#QkSeUKvfY@shOmbqsp+{9m|PI}2TWMNwtU)>&S%Du_=t z0(h%f+HTG}Cp29$pl(AYn>MO{uAkKSF=+{?%L+GTtPl#nK+3sfX?KU2KyAYC6`@c~(>QN!RJC=o~Q6c4YN<^vr6h(+$QIOVKG|YImV0YU zwhdF5pR{bGrclqo9YUvzSgmOQe?O!}yxBnbae~2OZ-t+N4h*TTv}0sQbeGkh)E(M?n-*Wd=#ojrX?dMO;|Pcsht;*(`nA$k`+083v% z@^0Jv&dKC|6t;p{CC8p@$(;P}ILd6B#D<>71v`Q-TpC9QZ7q1yZ1D)C01Z&KwIRx? zaAYK$>rY0R0*a=a>GA))h!wx7dI&3cG<~PJomdq$?B3`0N8qYcV~!M9m8It4b-`qC zSt20W9Gc&jkyhaLKm4?9T|RD6CB)13tYh?*dAo2WDEHhW*2e297Z0v7PCv)o2FDiF zZfso?7VKHxj_fofj>(SM7wh~;mSq*9J?QSfM5WlpRpUJZ^oUNF3^~Mm+K-s<5Sbp^ zAdHaxqi(1DZPMK>GgJ39R#`T90!;bYQ5}3Bq8+;s@CyQFQ}pDW%WNC>>#)w3RwzX& zd>2u_)T2pWoxys(jQvc1X&QWdvLKjTCRF$;uiO*q)V1^zZX2Z;`pZq{F!ZWRy^iL? z2rm(dLXl9{-K!Z@Gi9U$PIN2zY4N$GE<*X5z;pg@JlWV(n5fXVaEqM+so|B`9e@@ z=_=nt8fzPxngU@!Qmt%{*6Ew_e~Kf-bqz;0tMVo6;y7FC4{fT3%!vH5p^(o-Q40H+ zAh*$QT~7slCtWZ1Ur=f`cc@c~2lA}JMsvq?D{2q1g~7j6rpE~;P3V1C7eYq4AGxhy zVo?{Qwe<>5P4cx#^GXtz@GS&%zWN!Lhq}OAOw#KuikHgO}RoA@Hiw`DXxS%f?3Z;i`30aJIycYUzh%b zcZkgF@ztPOQ|sKey}p8+VGs{2XnS z))lI5?9vU*v(MuTWdhjY6AC?tH^a6foVR7We|-skCqsFZn_Wa-cq9pE0QK^C2hm@3 zjgA@q!V0u}iFJA{SfPhWjqxwdtoY1ScDg2+kWC%%r+#X;PxpSL50|~1V!YxVepd!~ z{nFUxscNz7qA*B1RIu+LJ1}C)@&+3Y7mg$?%%_*Z$fZq5Q?n!NQS6aOa_~p69u?M)T z+uO7#V2`0TwP0@lY=T&pB6m98qvbbmSivX1rq>?HKNd^xb4X|i^m+hu=)mAEF7HF-@@nHBFZA+RHd#rr=)o)R!B zx$a-*3qjUAYHCH}@mA^Kkmo58YIlX;=&CI9%&Cb>kEDNbxR4gNkR2{o)cN1&k(Hjj zzCVKgy@g}gZRd`DG~knDPnujJ);sG*T^*GGl$Wiaj_prO&jNNiPBOYIffmM#Ac|&k z+4jnsQ4=Rs_2R*Ln{21e@RQQ)Ry;4f5-!5ydJ_aW=Q ziX_KTz>i7`%QU#+$1Uoe?G1j35p?c^h|lc}o`Ef-S93-3O`R=i-mu=(>d?D~ly8Q_wqtMN_~^iLz>D<9++A(E28Jm!)9q z6C^dn+g5h!7-vr~6oJi*6nATy*9nY&nQYtlOzo;cm6Rem^R&)-Dg6ijDZlpJhEH@7!ftSW0^uvYW^ z`NLr9;5Tcwx6Of+kM{Cbzf3KE_e1Th%eKgtTK=V64B%s369MIuVydjTr>26w*&}J2 z#SRidZFP~$zD-A)$AGi`t(9E6w}KQD>wqCg;w>Mb+;_QffA z0kv#WNXmzzOvP%6caA%Viahk3Ti81@CWfg=A-`IQCy+OLa38B$n1gjXdP0=BUL$e= zkRSt7p+~=ro0)TXs?N)7IxE)7EG>f9zqiA5&crSgVq>Tio~Lf!Jl*dhze+~MIk^`d zDO_TF>(r#{wt5Pt`d%B1oI5+S^q}1(_(=k3vcBG-?k;o^+a$iM9`V;0=UOG8$_Cs` z+V=jqcWWCS*nD++GTU75M9cBg2~yLjhKijQl{7ARhj4#HO^*?W4xoRTN+pojPb(=I zf5C{=U`57dwKgZ&wBH*lbSYi6Noc+h_%2aOqz}6r&kN0$Kc!|H+p!g6AhKwt;{67A z7QUk=*}3bL|MwRceD->!tivPX%7Ly5qb!%S%&eRL6O@Kq18vaVSc-0l(qswF8rPfU1BqlkdZuW@1Ft1PSpz^lXIn$yOwXGkf zwCVOzp^6W_cox#}ayGkdq4*t`JRLe95@k0(%V9+;_%xt;G|TZXJ61@=$4<<0>G~e$ z+aL${lEFQ=YR}>FOSgfd!jvb?8UBhK72EivN8Z8dA-SYt1jEPvt{>qzC>z%aWD8UU z(1-OO?H*q6-?z|r$u@=>`w++!Q&%H%cQ!*qwGNEPTea8IP+lquy%N^{3Tj?ApS&2vXOb8Wu?7G=a@ffWjOULrKp=vxX@x1QcB-4XQT7{2b8js}O2xF_f zWK4)>axrc{IXF=H6`Pt!qrToHge?|fox{ZClhKt`J|+ZS2qb824C~k$X;xpXnZFt= zTb`|9S#mcZh!Lyr_s}G>vp$nYL3CE1ytEVKm!^9~5hyj!_2$`B(zyO{w#tOK2-2_oOKB?yf-!F0anFHZRWEHO=^_jD2Zqi}o82^kX**;;x&P+N{8$`x{0qw_e zi<5n0Xj{JwGqz&)M!8oe1Q$`34!{Wp>!m*8n2O=BLY3lN!Bp(r36+G1fBOtqS4bC( zi%Ky1^H$4DZoQ<_%=M{E6W^n7t^KHkzjub~iG!2aZOiO0*ac`nAIXFNsfUF$>9lgE zP%m(gzqT?#{o4pROv24gktQ;o?RzDWb$xJrA6b5aypmkgD+2KXj6BH;3@ zbINKWV(*H+i9sRqCG$pHl+qj=@u;=MGhJ~o`DGU>pXPK8N_md|u7U%tLZ6B@exBfb2~+yZ;f{G^n}29~Kr2F?U^4$kFX_&mklt zwmR2QPrr}V2r+HHCsEJ*Wp9X=jfir4NC4QZ-Ql!J@ZT6k`9Z<${*H&;}Me;~wl zPG5VXC~c4+CV*Wu{deaX*T|QyvD0osD;vhxr=pg%oLp?)WUo(1LR9t(g0yZQnaqLET_awLm}KtfRQ&kmT$Ryz*J)_}XL8&K~)@n*YqL zQUvGh#C!ul!cTx8-PseiE%bAMLVIf`+?sfLYyZu9Cw^EOGkCn`h?71|mbSqbJx*(L z#?@p}2F>}fz#OAqJWFU%Ojp|6sx`W?7XLippwiG(#yIyIAK8|P z<}>+D)HvIn6DJtxC%&Jc2iFcoaFOeZb-Mna?*ocg)0?CGm*xZ1!fow=(^dWJq`fFF=lAl$!^NLC+4kz{ zFon!4{aNds#zd)AHC0#gcvut?J<&7lz1OEPW{l1|)I^2pK0U-ncSm^J-Zw`vIkDxa zbH^3De}g35Vp?NABS9m<@>!;PCVJM(rt_mr3gO0*VpYI&~;I@^WGa67l>)iOR z6@eXK0tu7PTYr{KLc{dmPeSEZmp@+X;=l!$naW?Xx0 zw;S>{;d{5+Dvc$<+~cwf-CQ&~7)|iV>;3{QRD1lk?uoW7(B z&i@hcrWv&Q`WW!A9DZ_YrPdiHV1JuSdKW?*1dNxWUy zd-orZO&SM@ke7Q9Ty=f%rt@c03$sydU2q|i7 zizAhgmBNciqQO`C@t+ZtNxf5NYFnq;r7X#1TT?b!;8 zJEq8x3{MhURWRqCi;)mh_#mI;uG zt?OxC07mS)XQ%Iy8jS9IgsGPPo1KQv-4SlA-JgB(Ni}3)Zg6GLD>GA1{K9|0(Cc+UB=oV0 z)8;7!-Y~hHuMI+=w{2xjAEZ#9+50AuBjgjG?m3)e0usrWUZR~@S^9J}@|9OiT)NT8 zwCXZ(Jz`tn;ql7rq0v`kH@R`kRxMH-6X6MtqgkejYB0H)H|FlY2vH?cSS74GM`_%& z-}98KS1oWfFh@&b>$fMWX@Hv=-%&H&Pd+>;C$5R0mF3c_c3%A)UQ_B+wri~(f?V^pmEsp+En~o$f3P^+4mDI zbXf`^WAcu&Xi+D6&;Kpr>m5Juwj%E5o7!XmuEJhF9Dz_#Nr*3`RMhWTIZ3RRBufz# zm0Yob?}Xt3dmZxCl8&m+HGoM`ZSQ2dBeLO~u0+dKPjl*F;dP^1IZC-8R7<2qd{-C)FP9#IaD3EF;&T?hus3E(M?{NF{`^Nl$^{0-&fXhOkWgbYlq7=G61x>AL%e4~A8XNS-u zFpIZycuoYM)AJm}+js1_XV`xE&2r?9EY;>25TbzvQ&nVgEP#Bf5Pr`4-GOZ#J^LK@ z;;`2LUVkkZJ${Bj?%2dDOXPRqK?IhN-!2EgWV@xX{}r-SY<7PhjT0j=Gp~ejK%HuV z#qxC<6DU#mWsOlZ1>43vJWs3IqGFM*xI(;lO7gpLs<`xYtouBM3&wf8w7R>nTus7J zFsHGpO{69+A^t!y?z+eL>u1PImMYxk7@tLm7LQP_Ui|UKfsP-2tiq=H8;4|5gkb&t zLPLGCl$>(GC6r>IxJUyLngW(sTP7ElaSx86g(G>Wr6O6bt_I_%AQ1zC8qu;Ox|mep zWQn+v{4^hNsyJ&4XXX>3F_K4kU=={z8nLA@MXCFE4iA5XjT!*&(OvhRb`lN!%s7^k zO?dg5X5Xw7h8>*Rt^Z6^%N)qm87oLOQ&*QWw7wdy*sSVhd5Eu%9nQ6t@e8PI2}guO z=_)APGuaMOCa#$fKsaW-baZ`!8Pl>GQn4%H5Bq#)@y-2HxSt-uS&XA85`UZSEI*{9@elD>Dt8B*Ol)!KW<7(+*+Yxm6BK`cBmkx+hdMPT8rv-G@T1@~eSf0kIV zXRL~CSeCdS+UJLI?`4-QBOEnCWP zdtevSN0mnMc*f%R9?0IKy}6%9myRl?6E;#8ILL$ts;;KXm&VOC33kQbXj(xi(lj&R zKcmuP2AimKBX|3g+|5HLJ{;S%@dq;$Q?PAqVH+Ke5D_wx2@`!4@!*!*Z=ncYJHEd1^MVIrRy@ zP=EGK-a9>?Alw0c$;$CJRplacS;n7OGK&ECH30H;^T+jjY;OJxLCIs1S#D`+$w_^< z9yS4%9Tx#|v5yZG(Qj6yI_x; zxt;}=mX;`|og_0GRiAk)JAfKI4&q(o@u?(n8Q(H9Qr7NaCkZMq`QONgAy`I_`ff&h z@5>p6`K!2xm1V4Bt3^}Q*QCh>+_N)_%a|W(WPX0X{<3s4u`Hx$kfg$Wd%k_}G%#J* zF{h&g#{RoTO@;Grb603E)z^e&)kNIa@c(qQ1Zt4mT9Odt=P;vbTvV25IH+vQ*QX~4 zzK1YFU|YtwMo|Y*NG3rZc4-GBhKW~@+eV}dV5B2cl&jLzj!OkL3ne>>qmY?Y<$0~{ z;O;J2z#jJp?U(xGM_>jxobj(7%7Z8BHri`LQ?mK_HEG7H?H8VGorHG6^tN#m4g|anr;H z^`O4mxdd`0lJT8Un6FEuW&mIopZ%&^WUYq0lfiX$c__S$_iB?H1WY;>T!s1RUmaE3 zIHsmDJtc0NUbiIMdy3PUwrX1W!)M{({7YdX#wd)klJpe_s5w@lLL59bV#5)!&FG^% zeu!sBV8l%AQ3E>LZ`0V{dwH^7BQmb4aC99gGTqssXt%Ck#N+OA+NY4S4FUuCiQH&b zj#XFzizecwI&lw%e8crl+}mBGoJ&HbM6nY9z~|ppm%n_`=D68p0(OSb_(CcnnGO60 zSj_IGvybFu>6O?cX26CAlWBK8d-7Poi`u>PT2VFZvX4F<+LVOLPiASskLbQ{&b2_2aUuk$M58T&`H%5egM!wo*M;TKgn3G6>KYESzNjzF%$9Jfa< z>`lEz?)i3I?w(6yOw{dm{Ej47yO?_>hn~n~HONycj=QMQ%}V1fEp;2pU$EJyYyLY_ zum8G~kn*R36f)jk$w0+TTU`xRw>f9Df2!W0Lbg}C*&%tSMsIi>{n5Nf~@C$PB!VmE`_OfMZ|Jm zf{f~KRt!WVhJXE+qs-ETryHsmhoq>pW1FDCS`+f6y4j2(II5w!*thCegm1m{qnEqq zEWxq7<}Wn^_1hgI@}AIX3YW@obzldQo{ApRlxS3Rly9ooHeja@*rUDFf){yv)zb`K zhMRst5n|oYV+<#sGlt*0Xoy!CewI8RIyK1?onp!gt3DR@bmn&FaL4B2BoikxC%|fr z35#s_JA=|I+MhyM{2xxst_+_p6(T<)DOHUapd{2BW&^3FNUqQ19?BjPk*Ai@X&q@-Tb-pQ_e&V$!PX0uY?e# zlR2=P@^3E{+?0m}$)gg^4Gah^dQ)DLC8$(t$nQ0T*&RJpC!IkyzK^0O}) zY#J7OQ|KJuydsKOI}LP@&lx)o+drgp2*!PL&3(cw9fV$-aF}$u+gZQY9)2e5_!X7> zmCb@6E|)5C+3E@Wl@MuIV^KT(Vf> z0dh`A8Jy!HpY>=|l*uarR?Ups69n~NrhKyk2pt?jMxgjRD{ypOP!jp2-4yLa{g@T| zR7?~(V?Pe)a}OT3;w)}VFNH2k5ik?jj+9p9ST*w>3z{hYI!c^y^WotoS@z@_h&ayI z6$c@c4y36^rluFe#DKGpwz7VndCJlDJiVpfbPoG(Xmv?>TPG`(E=-L$PP^<h4ijCag`LIdtPOEe2Qk- zUq?*2YBQR5Y67R|cKed+n^_Wsy66h`YS1kBzgYmJ^dV<^YkN}@=0Sj@*^5?ZG)ikh z)5$?64vAn1qg6i#1L_fp|CwKow>56Bi%_Uy^iAp&{OXF;?3mo>jB<4(@pg93@1Fm* z3iK=_W20Fh@7!%~4Sh$hi|CJP%nLEDmTkU==qR<3SuJpbLmsUw+FBT^_?M<&u%8Py zukgFSaOsTUj$+R<=a&qsy0R2*uHIBTaNtH(mhfSW#`qMSL+RTpag(YRi?^wbkmnp0A9$5vCq%rNo5xJ?3SOv8eQMnPz-LHqxZN z)+b7pYa$$HB2;ka{+RRrqae}d|dz6ra?5LHG5?i)hZaGs_wWc1t@GaW6XA!1>5yyA~sn%pOI>uGvu zKJ))ct#tNEuOCSmh@P+^YS)tpb;lI-{M6Rxc?mK``c0am{trC(TB*n5nd1%EKk8pX z$o2I~JJzqC*}Fr0QYV0ltdEeXrR=6e-gV#`3e|HNeYF6;c&Lmg~1;b3$>zBasU8zu0;HbDXpKjW|(vz$0=k|+^g2 zV>2K&U(+{<{;P4y5;v}G;+GMdo4^j2hG`;RQMUB`{w_VBkEE2<<&285d+s_DBA>#+ z9<~_VA*x4gl}{b=i0FSGpDEok?49$8CiWI3m?Y+VJoLt$1`BP$?Buj-m=Lt1U?KCVlFhDu*9qXtPtN zyL4ny7JXn3pR|yzp~Bx;Rr%B1-!oN1jv5rzC4?IuSG|u%kZAOikJyx!-d26=2$$a{ z1(BN|2hP*QpH1>88e^CTxR3!gqDo& z9yiF&Hj7Y%%%wu5wX;qa%4cycUNsdXw>A>Ijg-dE99rXqI(tF8$7|VN}Sr07@o`laLTm)2&8+t3Y#^DF1JL;x!G^ zbkmMBeDx}bG8`Y-kZW=ky9no$Q@)w%bwPBxYZ+tdzYQ_ zgqO)tS_LY`QmUjS(%M3v;BWkZbSY<#$fX>#Nx<{wsR4B6aKE@{{~ds6x3%{jUrHfi zQNr-3uSccwY&IvdfL%ZiMsw4_)dGiZcQ5 z#*hC2nLuX08?*B$CVXrTJr0|duu+j3MSM&x&Jt)yBs8Ht;Nr!L+ ztnD(^CDGZ=^C!6R{x}a}5@BqVwCJ@Cc6UT~FJC>w`ieZ0-)5n_pP{Bvw#aIt+tWg- zC{fc!!^8=DCxwFOPlf|r3er+H)mfZM5FU}W+sN42*t&cKldb)H<<-vs z@Vo!@{3oKR2i4yXtxeHnZ4-klRLk#6%+|cC1E|^!-_` z9u`RI8+XTP)f+hb70F5!7t73ODqJr}q(F-Jz?ihpvt;0DL|lYwK|~RcnP-gPx4O$+jYEtJiA`ge5yjsf@?vCbK1S zTM4u?Xf#n*jFDB7hMmQS962Ug-tDG~_*x8KK(bba%{=9d$jjr069n7F$SVSyV3&@) z%qEI3lv3SEz14-qBI~Kyj50YXD2UG9ON@2oNM}S(w+aP9flKTi$hoL%R!PoOxo}cY z67`CU*KfQ*TSzi>y)F;>R!Uf%x3_W(FBz=c!iFsn?;%w&Rt)qQ%-iM>g= zeRPIpU)CpH$F!%X?ods~gF{R&+@so*tU{nC!0R85GZ2tWY{ck7AM{eq%Xc^PCH9cN z^#=ty#(Ih61tyxhAEmC%VT;Hf^I;E*U8|(xeDux+4ZCDg+WXE?DJCe@ z#SUhzcDA>-(JRJ|SHF0b=;l1lsOZ;&nHlN{$=W{qD=)D*k)XaU$c}A)5vy;EeJUFK-l=F!z!DYl5*TGC~hK|wd2rKz$l3w1#V30koc3QA5h-~Rw zf}FW&)}WqJT9Y+fgX|DuAyFZ$_J0e*4Rxi($g*EY&+R9$@O(kU6A$I z+}on8(Ua1M+@7_D$W{-~+2oluEqaY^T%e z=GJ{dzu5W)sL!NWRON3MN6&FTAvQB-tI}DvFjW$r|HR-Jc3p`{g|Mic?%|xLlj)y+ z1c1}q!pI3hFLn3#u~p15@saqx@$5P_O9QR)Gwnq)I-8C9zWi=mpNpMZ4tG`7GoH)S z6PEkW?DQ-(i;h->l&aT?{G;EU{_6v0{lmZiYLgZ@@SNzl+vQX@iUXxO>Xs;GuNZu+}}oIEUouhEqlpWUPDqBx(w^9SF> zc*Dz={(*e=-P>=|+S*T2)&I121Zay$>fh5auwyq;+z@B;XwNXS#Vtw+xd=6_(BcVD z*p_FiXf>2gisCoAL1nduZ&=pn{3o8`n}7V@2`Rezx%@tftu%&xIV_GG>ScIvfOl?* za6jKUM0r#kzLm$*G!xqt-HH}Jx{W^)qL5RU+j5=O-W~>w5;&OLPcqUqz>T}&Y@F`s zpmtOY`oU}rjn+iTDMt5{-bUq2i0iKj48b3 z$NO*mm}f4Zq{lA@$&Ghz;O*|k;1VMqizPVQHA>%cF^<>f7f7qmUg+;;W@;H%Pz?A% zY8iL0i-V{fS`M9^lGezIqP=d{d$FsUq^U{<`(6`cn><5#Cc)rfFZUkFg-W-bpe^j? zm6ybzjTaltKboMkQ|9?7H&1y_WXILC60MjB8RU6geeDbsHM~DB&@8QPAK#9h_fulf z2L=r|syQzF(q#bNdG8U)59IRp+~61`r_Y z2*+>TBl9TAuY6V7E?9JRzH{eocDLoXKmFY2m{ga2tD(VwJ6{NqU0;@+uhg{H0IR zv{YG|kUCIrIDjJSWj|XamfXVbl*8fX(qq2(>I+QYmxIS(v64ybV^5jrDr&z9G;2kNw!@&I?7_3C-X64Yb%61 zm0st|_{?r@Pm3)&dH5)KvkiMtY}@?g9Ah042q^9Db6~8}*(J70U#qa!+$EvvJg27O zgu8mlDHcavy~x=i(dlYt9*@6=mWYD>YU|`S%k-;5{pPKA@wD6M91@*)?K`(Q`qU{} z%(9R3cV^gA@Ui2UE|S|#b40a>iN})|YB?tFiX*apEPEXjHXr%P;EELXx`X%a1oqZ=ANiRnO|!5h(%n%iW3Pi#Qg z*MYuXgOV!btm^6QJj;Li<3EwlcKUg=e1KE27SLogO*nk;R?ay zZT#GroGGurevfm*qCY=g*r3H+=4899?a|;ML4#=~WKew(2c zZ8W&Lmc_wtCQJkovlL5Nba`2m_io(BG%PxB%;#i1ZA7Oo z=E|OCf%-yn{7s3|qe<1qN2sSa8%ji@)nRvZPu%%3ES)Q?6uXE5?0wmbl zedKvwxZ1+X599(e?%F~Z72jrO-^fy`$(POu&%$fB-{8_KpQKf_-1=bs7OmkBhgAEJ z9eWEw)qZO7YmX?JUF^FhVDoIyLs_GzL$Sx3a`4_{j7DDkru}*g{liYucg5B(E^g9) z_6h)pf+nKz1zKwI3|n1!&UxF=nIzF+kF2801xY-Tpk3Ker05Xa688B>Ozu$Y6?@{- zRLR8`2-{^Iv5f*d**(0YBBz>-RW_@7w#o|nxSYfuP^_M(PBHq*Rc`&4Zvjwk3z6QA zvy_xGw7cyBVf_KQhRDywGdBM0-aP$gu{SG^msx5`0QmUjL0C<5YhLu+-sYn;0)&e)E6-3iAnB%SgBz?aC@!ak&_F zj(G@MI{5HI@$WWLNj~wFF9I+#HAOkGgHA!Gr{c>DUwxLd93v*m2xr4Az7<~ znufiD4O&ZbhGYr`s%8y!<K0qvH++m>EW?kd{wn~1;4$Xrqv#J* z4Sp?2qoBvqEw+7UbrpX&gnn6cA{}m`SNQn0j(+A7cM0~%9CsgO_z!;?{p)jy{BQs4 z*=AHxsE2eO9&V@EQkS*o#!e7dMWvJPUuU%KB>A#npuO%e<^4DV3Im-f>uDSDbMv|g ztdSZnXA5SRIQlMk1g}PnNK&)Qvz`-dDg5yK#6I;}jvZB3<7{L(G}I4PQ4~|-fSPka5s-8gks!kGon$#8*Xi7 zDBQ(cs`s3EHA76hMVDU$Hr>>+5?kOCN`NU9c&rp|J5?i8gO^{FtnG(W_lPFbY-Lqb z?$j22k#>fU%Ym5Qo2RpEqO9tJyq+L?`o_oaoxC&6Geh#;gAOYJiw%uI4ia6dLS}ml zS450sel>xwElhY+^~;)CWG}90ynEW{4m&aX#X$Y#>#uXk+|2`*I^>IcJUe=oM=EPo z+*-u!6=m-Y^q@DHIcSQatR|x9qPv)sBUZK9n7R9a(^tefjny*DU!UP`e@+gfvdPRl zD`M=|V)Jyj4{-gK9O_}e3Db6+;};~LQ7sqQt>w7j6(j6D)J9BQ>};hPIkOXus&TtB zvChKw8cyZZboKc$ZB{s=h(PXCqLkN?1Qd}-t~<=sra0U7?EU81m0YVICK zpO*SlN2HC{#&46jF7NmC1Q_gb@k*B>O(?;hRb=g>zxpcd78~UhgC6t zwtPS;vxiYH>!~d_Id)id?fXA?gL9WI@{RAw#pR_j2SK}@j863BW_*TWTZwz~BFBZa zov%E5p8GL5^go=R=Y``!q_g9$(eAd=&?=IqdYz%}2wRF!D^j|0k!6XBU*x>Ss5f&e8u&c<1ygCyJRWQF_s&MuB7uZzo4vslPy!}`2 zv8k7N)~Y#1h6ecXq1cm)M=uhc6-Q&cS*7f%GCjElK&!i*{mre9Yup~rJ*KmB5Q{+$ z(_0_hVxZMYs9ok=$R6mZ1_S$w zaz9|Q<7o}Cydr1Fi!Z*w&AAC$6$uM4HuA;>Z%GVC{#w1x!DP|+KB`lQru zfB(aGXcP}Pe@bNSTmR_~33i{LqMDXFTsF#i6Ct-at4@!VpIj5?$mD7zw>^W~rFxC6 zEz;KQC!LWCr`y{@T~okdk+W-VZj%9*mzQ6aRz`b;GV$qkB`*W4??h>H$UeMz`!1(1 zoTi}a#by>3(P@gj^t9Cc844xlC&qDF<(XsgBD;REMb8|)Ovbpyt|D6c;jMQW)>&x@ ziHdv7Hs?>xeB#S5WNQR5r_Cz|FU>$z|mEXFtIYU%$h?A}G;mjRe|t?50F_ z>s1RHr-zZecD?#5$VSZhWsUU4ATk{g&y6$URzkg8E^lFe_Djf?&ycaos$?tG-k z=&hKO@(kr}H^=o(7Iq~Fqp_OE=m20mqz`0Ioxv2P-Cfd)^4+z90h`~$OhRnrGb1Lh zFRBc=u7GBHhi5(|`gCvnFq#CoXc)!MIufkou(ngG%RK#&9^$LA9)?~Q zwS0hEKlngC+vDT%g%TcI-) zM4u4ZjFpWnOo-f_{oGjwQ=2SUWDi?&9b{{DN_tt#ewT)|Se8ObY}I$iZ!qRLNx~{N zb0FNnsMao4s?yct#paOpSiZkOpvA@Ad6CLcw;n zXC?C%Z*|dDJ>cw9p9G-P(375hOu-|+rOhYt+3Xl?;)85d4+yHh#Y^|^VY9lq^H6NW zsW1EzXTv_;{r2ksyz&qK7PY(6eETEGV%BGu_L)$mly--UzDO%!kG%KJ$|UXn zAnht)wHr^9KFCunDRM4T6{{jSOGT%-x0&ZF&r09W?|6_!_Ox$uqXsjjXP$aO|qsjMu;SUAm7C6Ud9Nyp+vwE%LCTZ`j+B|>fw;n&KiF`7ij3?vCcru=hC*#R@GMk3{^ySW zB{Gp;erljO(k{_`jZ@F_S1*u@DeCH=m!I4dcFo)MEzX%jWQub4UCnJUVjiMkkO*}> zAE$d@2;tFt;IQ4Mn_&sfh{-gqc*p{0d5oh>2a2ND4| z$Zyh$MB@ey1#ufRw0b1sI(WF7wbdjR1^sNE?dG5T!Rve#iHtt=tD~%LrBVG?*(&8f z&Xn}O_%t*39&+iBL|A_?eGk*Rj?qg3HMuu;6LZ9mXH%n|xj=W9tou$rO|WEU-Y5~p z%Z-1X001BWNkl}7plqAwP23)!75Y>Lqhi=K2kjaM&GvY|7_`K>>E2Y}!B z$|=I(Flp6*xxG}v7WA{eBiTiFB#bMkC9T>Y>~3t+a$=Z;n^KE6nTqjhr_A~3ufEJ5 z{l$O6Uluv_pE^oQ-b|lr=>2Hw1_uino_S7cMt`vS4#yN5-rUj(XT0s~tCW2qcYyiC zFe@s8cI&O{^j$oHwIcgs+27~F=oMc4r+>u%X#B&!FYO%q+zzaEKP@`h$A__1x*a|; zsx8OX(kg~FiLParqx_qnF8%+$>)-!PiNPq+Ys3JyZRO0Ak< zLp3{2uSMyJw6YOb4dA^kj2x5b?T0_T#^U2yF7yjr^-KTaGyLxV^PgFHQ{;B5n8VSQ zCZH(TC%?PQsf&@1p-W4#6n>ZJ;;6?<)l#CNm+1DvMvO#G!;8;IBlgVB1_4Wu?QMy= z_q2OCD2NQ-fAA3ndmCd%Bno<SD(AgPi92+e9j=T*+trq$h_+L zEFWDL2+P3nF_Lpp93jb!tSx5gFGIFXqPO$&^LVPzt6*{`^(DUifBgb)Oh{xnx?N{b zwHK;YD||4wPOTx)f19C-uX}*mpV9g6?96FhZTHIGkZum!!umnBOT43E*M?lO8oBG#{e=TG>;kxx=7 z3k=!QKg^py`T;dn({$M1&)Sl}d%gYb92$6U5{Uv_u_x}()B3nVdmHt30 zr%s8i-g*3xnqoaYK603Hp~!m^vggk}`z%W{b48)BiO<|4y$%550|I#IU^Lx zvQb^02#;1Is$N=2;cD~aP>js;`$_zI=g-i0EN?7wZ0r!xIaM7>Q4*#0b9-g|e6 zE0-jT8QoZ5Gn*kNM;Qi(5mU2FEhpzscmHvgr>8ikXyYAj7x98*2HZ6RV?6`R6(oyb zt83}#>|-bMZZNWi0^(Wea+Z?61l9_6?t8KfCd9i>;02G}&yPGZ&;q!^Zv`6H~La2gN>L z`|%8cpqnp!R@P*7cAJE%VJa5Plru1RLe}>FT!~<>pRHS}w%Jo6%ex#_%;frFiK@#(US(wpdJkb_9(q?0 zQ$5SvrpUBe)1ax<5HHBTyaPk>Z8 z@p}&*liJ@UVwLsMSPj%jF&vRng}49o8w|WG_EeiqvOJaG`OiM9_GtsHNzW~XV2WqA zIoK<5s7L16Kir3=B72;gTVvPKKx31$yVV?EeQAf3Mqck~4U(DPptE1>>(_6ra4Bpd zbW*Y-(aG<^_J*_ZM2(Ge4UbN(NHPvGUDx_qS|`640=*Y@%;jQ-BdQ> zJlGXGZ!2hdw75(6Wyy;72U^i*HS|Wro+M%=>Orw_u1Xob$4Qs+D{t=3;qds-ndNsv z1O1rqB*+9M<5fy+5imHYsB_ah8X>nOpN-#IC#@~8su3M%(FZXX?Ud`HFZ;S(boOlw z8C7!Cp&|X@K25*a1i!P!+=ke#c4vUHsmUe90{Z6lN0@znjvp1hEmNnF(^8Uc!CtDe zzN511ojvSDn;2TRsvTvh@kFuxOoU17f#h`{(gJdneEhZAlZI%~Aemqy*n3p}| zRIO_Fpcl)G*!?Z*4t8&o>2J!}_1I{n*(lLJCi8mUJxp@Cz~sK{g=0{IW5Iyql)Rpq z++a=V^`TxrncZEwoMIz$^<94Fn;U<9;H~v(+f=ZgX!@4!{GaolkbF zel^FBQK{vaUKdP!dM`^`(?C=8a4KnxSVA^Zs@5aFS;XnlU<-@zZzUHw6qZA{wc=&n zS0wdF4&Iir4syvH7F8TOa``gjkMGm3`iotkO7oYmf1j&^f=#~nFiDqk>U+C%4J@?Du=@dTMHaqPtr3a2EAvf zlX9)Z_&p&<@ppBz_*l~WVUw3aaT~O9LGSAx;-lya$wy*HK6mAL?xbhA;1>a`o7`w4 z9ujv2)}!wY@-P46j{*3dzwr#)g+1(Md8Yk6NM(0;YCtZAE2S-#mKJ#7Rk=j1tZy)J z;|_Y2!grhNywoA^vB=S0K3H2MyP)bsoE}U%EvpLx?K}6m^GvR%=t#?D_QU!tSKP8M zp3z>+yL-%T$$@Y7I&jx@BvcWK#p^>C-Q&b%k*~k_?qBk6|La4(dRmOsmCwCGG9_?d z^LB~f{PP=p>!1G;01K;gJoC&bt2r@BwuYJI#TeaP0`=S8%cEKp@wa~G8~oA#{WmZ- zL>|_%8J3r)__OazpyW4y{s`IaZCaHxk=br??BZo+^Rma^{x7d_QNc(9ZacsC`VD^Z zHw8w!_9(&MJAah4LFQSlgh@_K(5Vbb))GdWUO{6QBQ-g-h(XoqVXhK4)zECpCHj|6 zUMAS?W?@ebp4e8BY+;YHN97O?IlP$ka^M+#K_1L6(A6e}F>!aCu@h&R%*#X1F0JwY z6>aw>R^2ySqManPb;y&?Xkp`{nc5e4gxv1avx#hW@VPVGzjcTBzBoBU zV~1GY-Q#SZz`AB;W=Y1j8StofA=IckWRCmWo7fr-p1Y{pZoKn4*4`H8*W}XdEL&-} z^l$k$1wY}WaFybz}ZI4A$uB)&ydUPp|guUHQMTgEFR*j2j$dcl)f%MdIhB( zJ=KM|)?hIz-*vciklj5AW+aOHm@GcFH{_uG%o3OTylS_!!n94nC^ z+}#C;{V1#@*;jpfVn`h5c1@ zMK3x&=oM(^-oFo^BWM{N`f8s7M+IOl<(2={M}6H!pv zj*c3dg8Xiw*q~wAV>>Rgc<#gq^NAQ<)!%esHb&49VCz6^)vvz#3aNWSWD&m9i*>(+ z_a>yq*wtxew8c+d5lZy+9Om)+x5>7Pop+aQ*fe|Ws8&X!oyWPiDCbM>u$k!ndAi!g zKiS{NGv}VfZ9v_>#G0IzV`OPM5G?cH?+uGo%u}i%7J&~78q>a8d z4-rMgG_$!*Af%(D&WZCqAto1Bh_s0Qq$fWlVUYE)H(Su_^8|iIShAjEY^)D&TD2o8 zKg1Lm7;8=Z zCOHc|zTy9uz4r`nbS>|Ff9jTG$(Aj-NU|;WGWK|?r_XA$8f&o%p(L4#W23j<2Y`Mu$N43p zosNBWa*1m63JbtoXX8*?8{a!E^MGqTjf}MSFflLRPZ&zPqy7-jjS9$AL3p5FV`U{9 zr3#TJ`TBb53^rm3k;g7?9g(1%c{sj_d0R)KPUy0`wVT}XI*Tcx`H?++tRyt}l;6?3 z9Ar$jG2GN7*@u%#x5(zD`6XiI93zcl>!oyBs!Vo_s^u)TvP{@lW^c2!lq}Cqqg6rf zTu4Kvu9_i*4e;89tNh31|3XgWKmN&m6{|%U^wpQ%pu1}TZM8UtMT4FyRjZy}T<75N zLxgg|WX5a-t**ktgcvpE&JObV1P;3pg0;GgsbFIHS5fw+8U|I*(#to;$gZS$^lo9gNMIRf z?{2Qoh=Y3mx*u1Sh0SdRVp9xLm6iwF>hc`#>%`t6>$>&KbL@W0QO=Kv(|Av@01q7I zFFz;D|5x|yrSp+tHYbGXOhz+X4I)(Ac62nfG%~v_&s|S=&f`i;v|E1t2gz#~*&|!<v#u@dB-Vd-mo zW^IZueqD^(-~YwKOb6C^v_;JjymuE%i7>Zj!~i-lw2xRMNm!xEhOJc;3*fX1%{H_- zh?|Ak$6tE`bELxXz2b~~>-1@~WjLkg>kS|B@!H%Bs~5!CwRhLhkx^NLW)Eq%k;10X z_-b&Gt3C@i`h)MtSe4W4!ZY0wD3)XWz$qG=`>0jF^R6OIX{TeEid* zv>rL-rls1A!yxNC_SkVQKl3tHHS4e5?%<)jrJt!~@2~NjPj%r}Xm3Zm4-;J8q+_=T zxVh;q8ur}9(zG}v`CytFmk(c`4**Rd$<-whoTYj_&a#<`itJXZ^{^A7Yfv1F>SBrc ztJmr76v3jgn3$LqUirx}#X(=$>x04YFU6g$*ob$&5?| zLL3_!Vp}8hv6*d)tSau)WAk>w4QJv_Lda5Te- zw~CQ7cKa;58xK<7BWL>jxluwhE7U2}P<=FwzENl=9f@%K@GyC;d`4uOvIfRx<=%&f z_A|3E&yjFB+V0dkPnKr!*t4Cyl0RuBr ztK`)D+nX0hX*P>I8J(PDu+KppFB6cW1w1V!qY2#t(F4(_}m$uc&E_I`c#-EXB8C{-)QTt#Wmc;B?X{WOh;+< z_yDNr&78mRD#!Lo7PG0z&BgI^RGGzjd39ox?tvboUu6hAwah68yT;x^VB#h=mz=|PA=tu$G+MeN5zhXKy$E@Wmfstrg>{ zq1J*fWo5rx$)i{qd%b)X-&~_uS5K=~_;h{Y7A>A#Y~B)nOC}3s-8p=2ajNoj39^V$ z6Ejsv<_n}NVgU6tbx^I3;c@+EwM&p}p@*pjks+CMp1O7~Gb)g&&zW&3q|yi8_5i>0 ziNEK8ld7k7ZUd{u&6tv1SZ(z5^gwV~?j7`V>PhL3?5-5J;-AOq76Z~+-N54J8r^!? zpOO9n7K~+dvP?D>Z?UkNrOqO9AYHYFyGQ6_uXl*`_%?wFk*Bv!W$qm~$@L#e)y$zM zI`N;m$aH#qjdU7UXT3;@~Y2Hv&* zJ|_LLx4PL?{28I0yf)0XHi5-1`ey0U273-2rK4BqHXF#`zcx)>v*?9a<|jG$u<&Ut zzDhCXrw|i9j^*-1(*e5OV&o0&q z!HJU+^#1NwzK-|UAb zBDqF+hlZY3Za#OKU|F8^(Wl-?Y&}9WAUxsi=wdu3zfA;kRMog}B;>h`?jsm?=J}^9 z(m(n3`*yQ(d!5=AvG+RLyC{|w;__0ATgxFbEy9=Pijf1ECN?tiocxHLfq02^6{uY; z&Jl{5==2FMcI7Ihs)fdvcPe;|t~VW*+RPT4TjNZpg|DN@D4%-pEqvj53B0rq_OU$w z3^(OpKK@r9Vg40KHk6x^G^ZiuT9$p+^fHsNv$ZC&s(HAX!kCV-s$gmF_c9q5$Hi;1 zk}nsjQGt8CQOBiM&+_owq_=xVQ^1rgVNfW;flQXI^$a!@xNXszS)Udfrc`DtyF<81 z_#^5MQtND_SIwN9y?vX({XB|CM|u95(J-{onWhyqP1JrT&LCJ zuv*zvpm6%K5vQq!jgsimayd;?z0g*vx505^-MQ{ucdk3vo$JnZ=el#KNHfP+Jnec`LU?OK%r5YM#&5>NcnP_o7jKNmG+!9KQPmvuhFw z%m#Pp93DZJkg~Aq;e9;&&2IwG=x~roSlLc0G|s9TI+{HA>m{P}4_|#2x2=h+N*$Lf zd9qs)5wX;2v8Of}JRm9il2u2|PLlQkDO1_iSN!~A4fB1Xwr8W$SuTFKOH+jTTlOZ3%U-_GLv7;k+- z`X*+!LIlrE(6mdktK(~P3>0+ay%NoQ?6<%9|9hU_`rP}_S#(t8#s}s z!^hvchg%9|ZXj)D|J(25g%>1>eD{Gt_V4cC+iwVZSBKq8dL>DzL9zj#{L;&O<5%vZ z$tP#Am{`GRmWZ;=*hth=;X|Jil+S=>>;0&sOpk`iMQ<8%Y0rkIO*^@TNVSE|Ljklj?j-p_OwpgisoAN` z-R(@RXK<@?an^=$IlWAbNd&rVD^k(PbJO}T8aWNQ;ojbu~ns+t*FT)?mTOd18YZdzo9rKz6n=oTYQA_q4YR#b-@44gQ^Pk#P1)k+VX?A%4V&PZ6b2Q1#6$zx;s@ zpq&cyL4^|fJ0IN5_g@to!|Cm3Gw>>XMz4C`9X$Qb3_GOs@#f+ZI!78qR?cNJzJ;}3 z=(LcorVtKdZcu!kE#f<{hjS{^V$N%E*`4&&%Dv}T*U=b4G}O!g8?ha3%n8ps>a^5$ z))HTkv{TR9pCCK2j7yd4Wl~xE$vCN^WGOU-0?n#K+1=fO5iP}S;i$=Qh}q=`HZ`BJ zq0vN5O&xVPk=>_nEz{Rq!tIoM)9fNUCvyD7??pNMpqIfml@W}`IJCw2001BWNkl4Q<`UQ(<=OQR4?X-=09>b~?aOm+o^&!};f)BePG|(QU1UNEY;xnKgB$m|I&Ru_f=}t+(>8fAG5iyyJ;K z#`A$L-q|b2{a2p*S1QL_s8(gQW7{jZ>`kP1geJP`yO|NTrN6nEsjKIxQ}d(4!F4)z zA7M+i1Z1>%sy(f=#*}`hid=&gn?^F33!wmonAi*xO9{f75(io(d)U;ZrM;t<e-(?+HinNV{cjY)7F^s&#W+BU}L`T53e1{-|x@AgeK<3=4rft z08dzC##%mrc}L9(-bjp9ZwbAmJ=N^Dxk;(g;Fq6!3Fq;B?9R&G zzcGKC{y~-XzaGN6%Z^?zHpS-V6xA8Xcgz3fl}+rJ@$2PTtwRosM%k;^t}oDNT&KTN z%H=b;Hj-l@CbCj4-rMd%SFR(gT4+{h#_@WaXhH6kNtV&8drj}Gv9(^s*{~Qh@40u7 zZ~o-wf1PsHzxi(`DsQ|YPQYDH-iJ}ABdYqgn%v!_=aqvw?`I~t!JtQk?Bn-8hN;cK zrE4P4Z59u6p(!q`iUPFEZ}Y%or|{nt!{y#1eT0m{+|C^>+Fld4FUY}n_YAOd%TGZK zPmitcP^i)nRHZrbnLNJMCI;@7GMDW|J%LF%tb-?a@$Awz)f@8MYqtUn4m1$f%5T@x z)nqm+WL5u5CZj`BlJs>&1H(=&r|y!{PfN8GPm_bPNf_tKxmVe9{4Rd!&;JJiotq|p z;lUnuJt^glw=R#;r+Q-oxhTWdM(k?N(A30jG-*8!HG8YgsOQDCIrdn^uxo5?!&fE3 z|3*!K?Ua%IVKq8qe>dMh`!f1fanP%(s%YwH=F#64$LHTa@C2^?{VZ&V1N)nw{_dOS z`4{in#qs^S8B5E4+=P5$aDw;22BX94)xzyFlMqetH?(QIz< z@%CT+O8`bs{~fPfK8IE*gHL=>1nxs4((7SwDN<9>@|B-Vzxn(>_&phi61X(OaGQ_r zyQSP?#y`XKjZsFLd;nNmT3Ai&Fnv{i`|a1(dFQD*9#VaLKRth%7FB+zn=dg`*UWGH zyOi_(>`#7;oF&EVMJZ$HXz`)zcH>LR{$)x9?*3D8BES2U-yptOz@&g=wiaUaw3&F~ zV_$xA&A;zj>bj<$CFF0r<=Dc~Y-TVt32E8$1Sb z`644-;`}d}{lw<{n4I$e=}Q+0E=d2=NM}DQi7fHF7{Qi&l3|YxQ@7C1%JdvVz5Up2 z!f%6nhFD#f^EPUAtSv0EqX3VN40d6zWSLnKLqDiX(P6f8b5{NzX&xe;)-buD=DIoD zWUC~|Qd!=l;b0FDRqEGdbI@Qa(x70{U%nQ@?bM=glXcBq4iM4EXpu+n{UsLWQ>p5L+R=7q`FvSB`)%$%I76AyMg~d*XW1>7iLrt9-&jAHEaczM; zEsfk(KnM+W^(<-CVCd-u%8Q${?U(akSY5zstK)x$~#_5tkGDgXEr8u+t%KSrb?XGR;z=JNQjOGF-&%nY5dEx*wjofquoZSlq9h( z^b*~OlF5nTRn|w*y7pmJCH&pJyGTUyL>1(GS8p9vRwK>LLO;(u{~ZqZk1(?;_u47t zNe0zC&c<3wIt@=A5}k16!NCWY6vU9t5z|-YYHYTsXA)3q9>;cJ1hmn5V^0#!n@x04*vY#B`9NU z@v;~Rq1h1!!lm8DRL)^<5hpTha--YGvs4koZ6Y|!zBV}r{njSi$sKm7aVp77i22Dp zW}Tel<%vZeer%mpR~!x4Wl0DU++Bh@1ZiA?yE~1$yL)hV*B5t}#z}B@8g~y4joUD5 z&37|1SN#iWb=5iN*?TFN8{2TMoiuNl6jytpyDr&xN-CO;uj8j=-RWz+7^2c^zO_+QBSK_v7+q?v73YLR?fH)1%ZTl@5wc$&y^MuCma% z`}P28u20GuX#Vj~$@N#qIo2CS3U4V>OR>aCldAK25`f`F(;TIL%&w-TwAW>uhfvE* z`c`>I&Vk>rlzWs#a4^5QCfATKXo(}~NYNO@N!2=@7}w7@2!^K#y&uznyNk8w@Jhtb ztu^ZTs3C2r0wNu+AU07x+!=e&zj2@+ZP!J2J&vYJNOv;f9e&1*Dmgu2)MI1z^oOdR z?gDW_WKObv8I5~HNR>D<)siU+s&AJ(u2V3 z7s0zsMW~;?&BQ>FV|QmG=%rr*=bYcSwdi4z7=T)EYF@YIDbWp30x}YZ46q1K9K6B& zq!Z_W%7J%>AtC)Hw-P_FwO8e6Mgxpbd@vlTEX~vsGTRTMp0G?Rz(;?cXyF zCFRJ9*-((K7)g!-A7qkJaRgwnGZ<6HUE9`vPD(qnvagFNs(`UZ)4ta-&6NEK#EE5g zUwrbsKSYJP0$lVNFBz^Wf0+%d>hknT-VXB(Frk|yeeZ$v8NB{=(+POU_1$X_g5Eai zb&Htifevu`b>i)GLA^eu#4yO%-8lv%GG9(54l&c zubtWCl5lI!QYkGIBPR4VFxSRwK~T~-t%-P4J#SU8%N z0Wb5XYH#G{B08XuxhGWBrc41wCTQMe0C=X=CjyCo zE#QSS(LFISd`!Wd?F$O{1&W)?iC8Z+(OcXn_AM8WbGN$6$^2RhYb#MeO?)6tpIk2X zYyI^(Wjl#XK})o+K-&>sHkX5=^yFl7$iK)ow&r8AkIHI|iCFR@1+cd#m5!;6%&LEv zot{`S&lrLwdna+QfISBx#w)zN1f7npCjq4;W`wP3Rba}6wL`y7`L2cvI73f++dyCc zy>3se=iJGmL{EH?@5UU7SYEe{9X$8dcFt8z%Czf$M1X(I@B{Y0jhA}U5dU%0Xl*M|hRi$a` z`6=J^<3)7_$8xRf&Gkh*WKby~NC5zO_NF}=o4ANb zoWo!!a9Z3Y#t^NkG5-5H6aIL;6-|*LZ9XE2@lB0%i}dYW)*Ej;0&;PuLy;zm<>oGV#- z6X@94Sw(YKdoTGr#|+;8O4sURl(f9m>Ma1GcD34D2K)Yze_ZF#-faaqm~KK-gVeAU$JCuFs#ny@k9 zRMH&VN&a<1nQq52$>(j9CLq4f_b&StyoC@k8xU|QxNgry5d959LQ+w6X=4OCwZ>y0 zn5gXojjVS@%tMtXix*Htn`ml6KM<4EqOMtVlmd$DQr535TvXA#?5m5}u95myr|IeB z_`qy#QO46_uz9jhm`;lyBZiHqM3zTo4Q5p;J2ZFqE>|Rwru{Z}E|OFkBH}DBiGOxM z15~-CWp}hc=cL_hETxw{`a?o3N#$etkg<#sF=Gb-W582gSFLCdX5l2AAbVqfHD%K? zew7J)cx(Lc-)i=1Ca_H`R{C+gxB(qodwTgZ{P4Zpa|p_{9Vp_q`5dp|C^j|Akhci; zQ6fwO&rPgprWf3ts*xwRo9UrvbY)#!kcS^4FbH~CM@t693+Yx!Kg*wfxQC{FT(4x* z0jo8tFVUPP0vfT&-q~Hj?Q27exc*b`8zOsK_{1eCqt|hrlwZC4_UIdw(7Tn9q;hMr zYTUfwqMH)!C^C?5P+yt-+_LP|O2zrth44RoQxa1ztPf#(v zNs9NpUS%#_fmi78LvTS1PG`IfB!FlS_Af9kmyuc-ICgjcpC7&@dIT1&lNlpfke_ps{LAnu_92 zw2+IE^17jt6kYA^j*&-af8kv0E`;fngz^2Up-QmE-GGLqEO`O_7*r3j+gk zZKg)!m`!m93=8KS1gpv zi$A>o3TG}EW9A$u%GGq1CN=~fPo#>3#pQibVc4x!Yi;G>YKa5s?MrWF6o{Jqt$?ym zl8`DHywZ`gQ%6gFd=+l?5#7AJp~lGoia0VGXm zx3l>zuHXF(i;UcO>B4StVj~n)rfpBM88Oc`Gy2S42er-PnY6h4RXW)BUBLlvo^Fbj5=~WVBw2?UJ9@`@ zgR+No3erz8HF+E_zk_MgzR+6AFD&3$Dyajw3<7?`0=QhP59lvSnb+w>8}K#?N{7cb z-ASPmSLB1T(fh}drng`!WI#uC_%vCg*zzmBC`(H$P_}fv-i}iuHj-*wvR&-pY<6DyLHQnqTS(hq z{3Jy8#^>H0(i0k4ezxL?_9HIVPjOqk2ex^$iw_GHgkO{g&mMPke4eBB*jpVYVh%ge zvOcV?Tq*--e>bNxz*II%*B*t&^+J5|{}`Pe!Ke>Pwj{PMqA93PI(HWT)v)PHM9;Q! zv4imdjbLVv5B~}vO|KKAv8atk%aY%5d4_^7z0GjOwr2QDO99`{CL1+l%_#N={ZVKW z{`~UJHct+&ugpPUu1x@FE+3@@Zr4m|Z$8-c4&FILb>myR?lvIQi5pkB5($i^1|;hr z#ff^b3-C$q1ulc)+PO8;OJ}Q|+R_BhQ`qpWZLk%B=O?2OS}k&8m*-%H76$^|9Vqnc zaJ#?$*A(vl7=Pf^kxQ=FIL>RZ( zvNWYYfFg73dI4v(1&!ts*R=lB1{0D(6{WtkB;zJ5DxA8ov8qkOic=sgTn$p=)0U$C zStF~)Xg{6IMc_!9QvP}~j>~;~^Tm<5F=FG??5S0Z(*uEuVu4}T`9S?$SB*nSLK2Y;?RE)YMG`CFB1%Bysk&w+OKZvYz#ARrN8jeu|`P%OVW zm2FZRxkay1&E5UC!PA_L??HTt5Ls&unc>2Jh%(wkz)kNMA0Y?~l>dtw!-Ejyhr?Uj z1n{+~EN%`IfR;`F#ko4{Y3H6Y;KO6jVeSt|$Tem$M@;Me*rx1h=V4oy*M9=(b(Mj!VS9*H^Qj?`q@VdxGtYbSPc3f~dKK%5n$ zIx8Rcqc}($keeG1QAn9U9R7+x$Bci5+x-j<%#8c)*=fUO$x!yr?$2OxDHC z3JP4?HTDv@TO=Z)#*RbVN~G0Pw-bm(wy!Lus&cyoUK&G&xQX>Jkb9q28M}LbDE~bf z^un2{b7~WvGU)A$-8PP~6}Ggo+$Ldu8`f+P^8Ol_+79E2y%T?R%+{(WpMq_yN1s_z zRF|(Jai=z%Y#F7boXM@1iymK+wn;r|xt^D4&PIOFoj0X{bd4psRub~Ft;pzZ2+0mE z6z;VhFY^ftC-iJ}b?F;}7m0E=MgGUBVsh+vA*S%)uFGR#oFa?@|NGc%4hUCr)Mrpw zI_11n!5pQoa^KpMW;j!BzUlWEET_*eYN!i7II|%u1MK9d9N*beiFOW(o8*qvYLrFG z0uhhdljLO|Dfl1jQ(Wr?hpp^Z#>ZY8&+IDxC>r1$GRq$xPi>_m*Lm5fm5+67Nkz0*~deB#xfI>F{;rJIs! zspGiLRY6uajF$$ zSxI_T$cT}!wD4gasI4_OSHmyqKm5zOUED_L%;IK)SqDn37KzmB@J|%zpIhQ}ex(h7 zoGC7s4GFm3LsXe@zgLfR#}V_0>)ULX;Zg=CJ1yJ<&JSAuGNYH3#ky&XGhUIg=}ZO+ zC+5~pnKSYc?7EO2#kk$sVzWc|sGv2}>4jTn?0^Pbdt7yB8G5ikEZCD+0Ls-GGri_a zsY-!3pd71N9BN;y#EtM0*XNSYe{;>xiAMZ`F)@KevNHZ0JdvffnE+km#)9op-i2^& zH?ulMfo@hQELUuc$kI`rE_DaBHh}?%48H3^{ylDjl4uFk!%T--pR?TB>xrL&Q?yVR|5Z9lq)V|rYCHUNqfv!rf6AUal z77tf~&YT5nYEvS^`a9us)i`sQ)uvboUb)fUe6|x~O*d|^7d+lECD?#kdhFp?`i%v= z*=9WHsf(~(BSh^ue9!F{kE{6l)XFN@z#Ig;=7e6UV4Y`qadO~0LCbBy9so2PxrsF}UOrP&IT zjZNbAxp32^BE~Hur>G>bkq*U70fp3hAW;ANXkuww9oi_RXNFxI`5_uQq>GL3W`A)G z*sM=oA;c6&r~}M>Ld~zv{^95Fq6$=ga9VKA{AWj2gSNxMqFWgn0=c4>ZImEO=d6J~kt zO67&Xs(<=IX7rE9@-ci4#un@taWD~}@;c#Q8#Hd@Dr{Zo6Gcf=QQyKi&h7LoCn{6A zHixaf@}lOpDl7wUG$?pe4>lUrxOePnQc}9XItX|m@%!U!UAXY755@+C!5M^Dj1mOksq;n;zp-tV^*$*zO^M_aUdf zi4r@tEH>Y)7L=VrUboIF89t7&eYJ=3Oj3e+CuGQJiRbL6zY8RRh zIx-B^e;x#g9D})CviMB756$(eBgQOaeBoo_H-EkKHGcr=-YtU+9ITFNNRyN)ErQbV zW+R%=TZKU@k|>gBHLK%uDFV9N2) z2@58S!hVOma6SIZFQ>k&T5<$)ZY!4*TGYkNS)~PvejEU9(Bch~lxKMcnwIzP_sWKWF zer@MuNoja|PUu%O_oTzcGV>a__?*<#Pm9yJU$zZ;o?l+K5kE%W;(Le#f7v5OGY3hh zt|wNQ(UI&6)D+r&#@vi*3oaeJZ9JS~EWMX>Q2P1pp#o*Y0kYrP3O2=RM*|gp%de1Q zUkFf}=`W>Gw|U=wEf!PK+2Bm8jhQbSJNHE;A`@Z_ZTZ}`1Q=&-zJ*Ken*~e^ncoO| z^@fN?Gu4(T2!Fz;&CPS2=BkW+c`BWkA(Q-$B4gs_b6%g&enWjkqcwMBWj_j8S=Ee2 zI)YpTku^&RRW`OW>|sa6{NPD5q47$G4i(_%+Z3UDr{6c6CA>~DSeFI;g36>a%~&6) zT58LaHA&$~GvfRX@qzrE8lOSHo7;6X2Cl~QZmnn`N?OdPL93riMFgMhp|K{|_?MT?aoDN6F` zsKHk5pNud1NE`_<)c`qU=Kt9Ou=Y|qPiqzJ49pHYzSC$aIVL|v<5qZl8bq4C9o3^v zY-;HmIvNN52w)Tg^^gpaC0S{IWvj@u&$~2fVW8Nd$({wm39TCITFa7w z$Hj52+`@saHMfuxnTUTliKJZl_+|nGdx;=fp_{Y8)AbC4Vbf$6q-y&l+GvI7M8onr zd>{RsLS2>q=73~He>G3Tr$eNpGJBU_Cn-UC(^a^kWtTku5#D9#R_gYks-n#H0p1Ig zo%jxss4I!6mgI*v6Ka}^AGUD$M80sH*kJkHem)Z<&wo^7{Pms@{h)o? zo$ndLDI8(1-zzA;#;8#3{Bhwb4M{;%3=XU?YUV7c3tA=!kgeMn8LUqK%!^U*fzzQ1ozVjhQU7{@D` zpuL5cHLOBh_9tbR1!y6N#MX`c=EU0zG-P-`GS8%vKu_oj%%j7w9~0@ zJ+p)U{uXWkxBWOmW_#}v3TG1c)B?1l}iQQm!+^LOG(Tw zM9Eo}vl}ro5GbA}%v*j#gK5KyoCR;m9 z4#x_?Z5-J+>g>1^uM*X9ltWeF4)u0dQ|lZiK=_i5&44O!(p~xyax-T~B5)6%SidZY z6AjZ_Bi1Rsue|6CA1eycN!1=+S(vUFQ_~&KBMtxGm%#5iSD?_u!6wS{gE}j-e@ZH1`H7$vCSrsL}laYBO1>olMM%QcX z0ohnTeJjhu3tpF3afBSj+6#Y`w4$(#vKchr7#7f!mH*Mr9YxTcRtxPswBj03-TGNL zrfo}_pRxU|M;Da_`)%*+@q1pH_(GNhk^lI-|8wHh$2f}$!q$=hlxOOiD%Oq;kos>c zw#(k}2J97m6|~vvbQ*_Le@s}_n=&FA zI%QM+IdQHRT8!ksv)Y$_E&hrz0BGjbBD%wzgzxt3(D8INji`|?xDGuhuEyabt^{dg z%CfaL#EDN5pvc!Gs8XFhDFqq_fB&40cT33&_`IxEP!7b^+pw$Il2n6ALcH3CV!%k^ zb&mv@cT|kpi8SrbOz-D*_ek)cLsCH%ZRqQqXKIr9Zp{8X1yHV%wwy6pnrOc}Cx`8pkC zdY<oZXLj<3bqXV;7NM@o zdg*N%t)?9)0~t0cM5+y*UU9EmK@DnHVJ(s_uNSy1*E?lm*Bo4}gg9A}UWxiKv9Mn8(L+x;0Zz(5!9hUj!RtCvfAn0iY$a{}DY3ab9Ynu#H0WVQkX$Hb;+KOD;a;q`{;S1_ z+wqw_d1d76%#TZseOn7{vqQ}rWlqpn?ivJfM*BV^kDXS;^OJLe-CqH*BBD*|BQ-E9 zQ_RrYj;#qw+M%U>o#ji}TBr<0n&mV^Ky#2{VbJw6$9YT5XS?@K40YLYiPq9N*lvJ2 zIcHc`Hp$?sP51BdWZHj<j(pGzINANIDU^+^fs9D4fK%yBc_serceQ&1^JCm!87@@%gY@#s< zq1FT#HT{08As=7Yi(U6MLudk;E`lehhquVfLu5f3RyCKVx)k{>A)*u6cQHiin+^9- zv7~Z}^$bi3t(TAA!C(0v(EF5>F%Gyqyhi9Dr6#wUgxIs4u69LhHUDj>Mmy*`l~mz z4olKQJ_y`jA_l~8(xF)|gyu%aty_!Q3xZWjq-=z9N-fFyG2=Jm*BFjcHdg!U%;&XR zV?&l&>sL*Q^Zepf%upIY5=2|6Pts# zjE`35Nz<(&RC6Wpa46BDt!z%oDV$U0zmP^WB!!e0MeV`G-)&2!>4zF<=sH3XBAML?h|>@eDo1;&;Fe($SY7GBPpE@KB;iO6Dd{E}B8R$WZ(XmHlveMMy zPYv9Wlu&AVSY$thiJu(#`(rX0FJV;2Z25R*hdSRKdatrJl}pQ7(_?eEV=;@+t!-P{ zp7$TRnc=PzjthZ=JXu{#jFmi<=XdrUr`fDp0}AxD4u%u$5$7Y_M@O}@&0A8Q+CjQ6 zs9p^kYGmFGWY$aDRqr{Q zxx?DaFyp@hJ6&C}4BnQ9J=G|VAUDV2*7py$&%Mz#%g<^3kG)>0;^AE;KvllACS#h- ziDJi7SJUJ^mh@Ke%HoFNdL4b_b62ybdxp|qFdK89wSHMuH;G+WwhM#vP@Xqm>J_g5 zJ=ziBnYcxbu{aDNA>o=7ZtI90V)t1}v#pNU6td-nEiQuPo%N6@w^aI)L?h$MVcJeO zcVujS)fj%I$1(z?*K6BIkT`8jq2QgCY)egsd>Zow5k|Y%z=U@v}U=^9g-@i)m=*DW(&w(N`jS z^-xbMx;SZ=9@qUy0_PNq!7%$ZCjO89gHE1AkKqj`k9Fez`)BL&(SRJ|=~GKETRHfH zYumM0#Pt*hi?Z5xE7H&RypGa1xkAg51+T5ZIf=L0oYy8+j%h4*>*Aqt+PLuKKQT!h z8pO704dc_GDGm+*8!xk^34ii|lphx;D(HU6=ahSOT>(M3)9!>w_JSiT;lDQrebR&E zY2SocU=qdB#=@YlINHJiunCF7HAw?|B=y7Dt^; zcgDLTS%m~W^Z#6yU;4|dOD)`IQA5b+|DqS=VC*7Q!tU(jRF7a{V9q{n~ zP^H8Y!Q!78M>Tl=zU3$C|Dq`L0`>Q|AagHy$|6f1!9kti3lfw`1$XiiOJkiTIo-2u z41RGQ;Ejg3-)WNF(EKjKUxpu}3dlUTK@x+T3ojg60g>ipmW6;Zi%dIBQOZ7^c*0dSdH z83omC=Yt$t96CB`_BQoHaa4@TlcJLG$?#_Q+Q}_zTDeCniUPywTS-!svy3`+Filar z956(-l?V~)WvKEJl2mk3dJlPDVt{5E^B67kRGKI22E)%$Gi>HZO=KDx2;eII0jaUtvRSj$ez(fHKB~OI z#t_X%51E}lpuVUyG!n>8u8k^!B^&4q_jp@z&v*Luuc9YVW~|LbL9LAE4aKixUz;(aFveiyPGaJ z*!!p$@b%9y0i2ZkW7~V$k5^#TxF;b@ckPJ_TT5Gi5kl;Q}jY^dLznJ()q`TR1HSo2y2z!>)e;9+PT>C8xm9#c#=yqF=D4YC)WX*7Kg3{B7S5vD2mg+2fwK@8he@lZ&*j?Iw?F@y=GJGYL!*;$*JqE6 z%mn0p6!J`_-(E8D+C{pfth02UwQ`dLI{fw!B=g!xYpj)7*#!P32Zvj#t8L>-Fz-2e zujC{?I2`SIPQ)&&37J(<$Dg;QYnz?5ds9?0k>nKf7(Bm{|5$>Jl_5)ma#3M^eP7F~ z&t^7#f-2HWjLp491~#|9?^dcbb7{%esu-OW=0wuudFFsv#@pR8k@j!QQs@hJH>f%D z$t=`T6NtlSB|UzYnkkUU#`xJW>-$LeoR0kU*NsZbVO`i>T@t~b<$`0B*u?7c|3U1&Xn%Cb z7eXZA3L_rRV&e(vp@7obRkp8*VekHN<9IV5tr11%+#wD{a^V*@>mqx07V&{GDL=PM zIIxTPMp(l#_ggGn-~WU^78S`*1wALV1jhe5Js_IY_tI+x$AKwZw$2$6C`R4;vNW&7 zz!u(7fI~e*jc1?Ua*E{L&VNE)f*WwyAW4vsr*Ecm@P|jMa;6^{$tsiwAP7)sw-1N3 z=9Eck%K`(SK@Ny6)I@qmzdQ&v%fz@^h)Afb=+jPO}6#3=qa>mlLcgYv&v(UiYnO= z*JpL_E>Ok?*qdJGUwKlU&0@_x*&8P>KdQD;j-E%|Hnq5i(-ouh5Miqs!&Dv#_dm_1 z`6xHIpIE~+ycWZmKdq{-Kvj|O9=`tmsK0juaDqbyZx<{kY5);ykLeqe6Ux5G_cyU* z_HVpmy~k`y?${^l-3T7##=xj#8(gPzZLytW#<+=;_ziXm*RfKw!yo#+JDz3<YxyOw zFAHXxUDV5KBo5&!DSg*suCR~zt`RI)IGutEoh1;_Ozm%YvUoD zE)1QKU^3Y2z|O=$&|t$c5#Ih}yORckF;UQ6-^sUz>hcs7CvUm;CpeY$OjNLw2TT+U)tJtkkE;u>OH$oiN}*=kr$uesj1}zDn&^xvY5K zT{8bSM1wE?^%HMzK12(4KYtp5Ael4Z=%$4N#QG#4`8itU8}9pguZiC-rz&W`sS4!P z=645#pp2M3jGbrOmWVynhW2~D)l5u`ydn-_g}M^iDIE-EF)qLaiQVoR;({nVfxs`c zR*a7VyvF~MmOr+QHruaAIBTl)a_dllQ-Pc0)A*|s#NUR2L>wS2aE|--AtIV)+b+-sKIV=`^Ix4gaZtBc+g<2NG?f(XVr&gpEGZjD zn`-;UwZ`G-Qe|${g8e2G^l9pxSxxMr8`RRO_MCeZNCGk%T+*-HMjeh~cwkTe(WVbH z0HY=wBoBn+nd6k^u<#ub>YKB^SR{u&jqm24Ww1?LnvPD7l;}9rIU0k``gH{VzHNlJ zq2sREZ_S!4*zF>XnH2++f|*w)V|l)Cj6K^SNK9`X4qg7c*u8gKNf+o0&&)KS_}odL zq{@VK{D+JXHP@ALFmcTI?te{a%ZWHn=xk+QZ)fOuZtSHNxPl3{*50j8nS+~)c4gm$ zNw?h$f2Y4zDGtKx+oEr;tpYiqt<&7p*I6a7bia}-lHkbPoascw<$bary(zQi=9bsT z)`{EK)7^M0`LM~Ycog;YV232s2}8|=^d*Vr3LQ$*K+C|R!Fd`VUO^sNR_z~^uBssR zKmhq|{^9`q>N#GE^}9J!Q#2x^3XHMFe(K8E+JKE~P*KpGII&B>r8W(&AN{LTw7i%4 zgoaa>okCyAk(yT~=|EPHep%~b!|h|2hbj5Z(kaPYL@(;I$-s*|QMWHWU~JVPye77% z&`P}|Xy=rqw=#UtpB)C?Hq9)L|7Xp9KX$DB5AFc4XG6uwJLdanQm9DE3Hx{-O31%T z-E))g=MrM58p_f|)oSx7h`5sR`)Q_dB*Z8>K0rhT{rxPu2otgn#Ee zc{1^YF9SWka&)-4yQ^jCa>TZ@NY%+WjK`R2$B8K&h|TD@a+I&utwEy8o& z6Ka_F91@8DpFRl=R_TDI38%RfQx_RoX>2wdI~NmL#a*k!|3sLZCndxn(yEF=X>ysz zrPZBNCZ#@}T(GHYoo-K@VRh*@YILhgy2(!)G{&}@yfaA?IWq41gI`a#1Ow8iy#GHj!Dn{x z`u2(VCr`|O)ACvTZ;U8hb7>{Aqk#~{j?Ime-PsQvDxWTX*uhAE?)X=W^&b^!R#O}f zvri)*H)OqsV(>dG;mgVl?$t01I=sbP8w4ZLb_*2ErIa8Rrf&juEs#$8r(Xv=&)dnTTCIv0JC-aFVCBekv-wO*LVj!9T(?&l3RY6W%1JRA1y0R zjI(T~*un>VtAwnT^r3E|Z?BLlAxbk`*MbJqf~Kj-JpV|;2`mt+xJotEK8S|K+^b$T zsVz7BIbthoh@DY=ur|L}p{ID~^gL+f<9SZ7*`EFU%lFv;&X2w=zu*tTJw&I1!me&^ z;Q&#F>x}=r{BplA%jJF99xl-E{+}(t%ARC9Z|}rPda=$@6Y0m#-kZz(qdOKU@!8aK z`F{ccJXdmPIwmYZDy( zn)kiV^}bSGmQ$|-*TkD6^nC)2n5$qs*phdGt`W=gEk zRZz66Q<68=NN;QH4r5@Pc5<9Xb35<;l*nwpT<{DyyzX4?fTagPk{3clgXPs5FAp9! zcdB(eRw+$sOx4#@CE2fIei+DAtuW=KNVf7a3k4pALM6VbEliQj9UC=_wIlDEEd;fB z`G)nHQCMcT9?RvXc>l19AD{1FI{cW|qwYfWMV*w&e?QB4zkU*3@N|D&kAaWL`VM<*2ECIoBzV1pqKY8h@6YlwZX2| z5t1xo5^y_Q=i=iX`=*Bx`7{*2Sz4e<-eg~wms&%+(J5Y6vzbNa*7R$<8{_V-LtuTr zJN&YXPW@Cu)Cff@ggVRGO+}h|d8q>8KlO+G^1};OAZQad`|CE(uZ|z(&jX?JUGHGN z6m^^OkvNoS;MB?b9EZ6cpG0y_{B?s>lr(8L2?p`=#Xb-3ouGS_i1}M*Hhw;u&Uuos zWX;O&GQE3lKZ`Ui%7IZ->4T+w|09^MlT4Q9%fW|2Bigw;qW#D*6^ zGhEsH<~;#-7@cu~lrDK0`+a>d{GG-BElB4lI`lmIdDV3i`xL=7S^qZHUoo|#7?99M zd!1L5nZ~S{b}~O2TZeEq}(^~w^_=z_~HhObxHuxf9jBddla}x++y18 zjM2beZN)h{Wd_-D5f49PKBoekG*SGl)p*rTgTkD@D!&sWVUPgMJyAQvhkcbB;j@9$ zDOA;oz0hC{*EOfPD5FHW(iOn#50-AqL4~e&47;~R4Nc~nIk1MLl`_PtEoES5#JC}n z*`pV_!luL72;5kQ!;5dY=*9vq7_}A zOW&x9tp0 ze;UikR|Er)#k)s(7p;mm4vseXk`yq@?MjAl{!~{c0U_cVM@J1$`7pJe zUKHz{6z)&X*V5^-S{^40F)0ohmn0Y#V8!yDUu&@FWnEpz^Xla6=4w~_R0#HSYa^OW5ZaDcOJ)gX%8rwvN$rk~teHuKeg|V_ z`CAtz{uR0otLIguQ&}T_tkheG!#iY2`{OS_zx_vbpqyQt!8?qRg{@qO=jm$aBaukW zrf#ozKy>b`nO7|kan zt$~}V;{ThXbLb%R+sCtiL3n&z^-Py6#JBoiA4dGu;c4O0n;r8iDv^;yi<&i$SIT3F z^r2g&K>VcKg!h= z-*rP){X|<^N#c>7({lVQuTj=WiO_hT1`T~yhx7OjqpmfqNww{i03WIET4yz5IZ*1p zJ}=FjJ7lS{2tmen2f?&CY4|D`Sn$mg5}uWhm@Qq{-&Zx~1z%A2+)fcr;O)2T643C} z#BO@G^I;+C@Ds~FIvdkut$y{r>||-MO6*K!NHeFP;UsI%6@znii_{9{f~TB(-~fhJO+ZGOU8{6|yianEV84RqT7< z<;ov7-^A?PF{%@)Eesw-^edR;VmdT&GwIoNQ*ux_&lg(Wo$F&%S-s+*i`YOv2<#fZ)M{2X}W5?oQ(p8h6)55(v_`H}3B4?k>R^4{i+vcNjLa zna!;3A2|0&z4cTXfOleKSHamGm&gvqp^}PpZ2d2O6)?McY&=@FMbaK;Ej945@TW|4 z9?h(SzDFAWVuv*Aj7lOPVR%L6kIr6h zDAl`Gi{0HP3W9yR@khF&eWwv#8r|9?d$lJJ1VR9$E2W-vW+*9hmxc?EEaHsy#C!~i zrOlR}EmuQm@7L{TQ#gmD{^!?bfBbq~9H_>7U=V;EVJJyxG~|;)zDsG7dVdFZG!50| z^_q;xKq@1?w;M!tY|4JZNtJS{C%jbF_Qc5Ojj@%>e}7bFl=sogUa!mcbnTNA=uH4?;+P@m5eZgCTThQ)rO#-+R>hI8;qr@)DTH8ST?|SQ zZpLZ!clHV6i}FZT!1*fc%w5SYVn}gv&2iK<3~Oa-FqIxwP9TrAeIIani1a7deM7^I zJHVBep#1B-6A%%^(tqDHQp>0s>Q6hgQ5;-1`-PdKxl5{#?s3p|QG*G;CT&oS^S-y0 z9KBY~mP$+RuMH-$raLg)-ZGj}Y|j9|46D<*|AP`wbn)+^bB$$8&#%xa`P|Jv*~I{0 zwpi5CDKI5GENOIR&>_bWM_ZZ1ugeTbUW6c(`R*NRrM$=#*pC*@(qDR$4Bvt;e2+&( zx?ZNJ_otCFZGLEzNr8T9`+t*$VTj&DbnyCtJ*P*hU6*D*y77YJvhJ5bGsi_u$}FrX z<>Xy&r;yKOT2y-WsXq?jo=6kN*GHO_FLI~-aelsY`Q~oak#Yb&-|O09wssD)MX87P zC2e(z&QYZInR{XNaAjdMT{J!-ruR!>k?x_X8WR)q(F|n38(GhGc|v7&k#2|r!2Ne% zFtY7EGPS8sqI3Hn3F)P)HxMgb!Ei%J*)8q8T0A~ALZVhRr!0;$r`pEr{FwXp3>O(d zrN2G3od@fl3pTa|dTU)Pt^T03)pl^PQZ14HHDuACklyiz>D+R%5fIP%vaH=oiXCp# zT%F*eb}cbzP{49XUe2)D_TOZOr)^f!I7dM9`tNaT@_a8Ii^_y}K*nUS?GACk?gwNd z@nWCz6?>7ZHR0#oa&d_>=-zf#kj(vZy1JO_I`Ye18=v$20o=$wkP67L8CA(WW}cFc z#~)42YyHnny=)8~RRgzrb9{~d0}1*$0Y&v?Y6b#lbvGzA{|=ln#j0phTI&MTahrpV zkYw0bw)AlvK(Yc7a;HfwEY&l%!)GO;!~mlUw)7{cYg(tM7K_b%e^y4P*J<4vf>W&- z&;|1q>XMbK!6W-LlMc(nX(2v~3x#az%$!aoG3xRx9~zlI#E;icl=$~FYtayh+;UOF zOWy7Yu#MrVXX*{F&_$$`~b7;CiW0k7l6 z<}~F$^P|5t)!Gxz@0ojee~K855c$XJrzNwz%HIRZFB=l84cla>_S;V&+qV=ImdaNl zt`C+B3oZpTSOZGuRf7#ufZu(osylI2&W*%PFEi34vq%8PUGFfvm3_2t&`xEY*-x~UHCy=&J(Dv&y4}%)N3UyTaNJ`lZ%oV%V0?f#tl^*k#S>PY~h^1hq z2v)zp$kGcN9EShl)$;{@aQsVU_dWJ;o9{K9r+q)#dX|S%`7OHX)$>~J#{$SeOqe9E zt`T$We8lD#?xMrfAQ5&ZKIe^UB$ z_Na_$dBrKvhs1L2J1Ai$+H$CfPTdQ9ePr)9h@j$6{J(jWSdg$DChbC?mrx(dZw^aPH~O ztW1q46;U?7sWTnTGF9IRtnPZr?iO1OEhM}@RnRWwJtWW&=rJe_x^ZS8bhk!sv2IvO zSjT$G3=0jTmPB{|fS;wuyfif?MT51GXC}L?DhzMW>_O;D-N3+GYjvzezZS)5XI@Mbj{gnaf`kt1qADUN{+YQmrgHft;&S5gziGJx!5bIS{ zYoug4_|WY5vfb`2rn>gY!J)8`o@FJpVUI?a{baZa(5=jc#aL zlU>Tc@>_=XK#~skV~S)6hGCC}k&`4;Pza8cEBj=gja*SgiE6FQ*Txvp{wDa#(3ocKez-_no zLF$ZTh2lI5a?Z_9NN@?Qua{r3Hmo?}0fIgL;3f##R!XB`F<2bR)SBx}>j|4n+{ol?YMWm7aX1$Vb&%E<3} zloIY~2RvE6IkCj4klIvxG=M3b?-nommzo}UX{Ew83|ghs7+p*Cn`_kca=+pg)5Iy( z+jfJaetvfKrka-7yrUCyFnaEiKndTPnRf(V-GHQ5ft5?^tN1;k!Cehz16@02W` zjA)idx3Z7PE^j_}0;;mWr=mu(hAeNgPPZn5)d0TMkZBoT^4}UauimfQ?HN{NLojV` z9p;ab{M*mV+2f8$|9&us0~L!EcgyK(!oM$`xcL6K6GO=QqFIdd2b-NLRfrj}!P^tN zRu-#ScR#nh7E=v9C%24zZ_&9xf3YfeXfhSDk?-0}w`WD6+@X`Bh3jwCZYc`UQj8YTREVn?Ecx4euifzJwv+?6KMt$ve{q>xF5lRSF3>oL~e|fi4tiHWnsgNCH zVzP^iwq)(?klEyxJW8yC&M_}nO%{{Q(f+en;wnocoS_u$~Ajy^-dr@fystxJYjZnu=FUh%%%F`OzYXXnqdOnIHb z_++w$Mqz1u#WDW>{4zia;Tl5o{E~f@JA>zMpMLHOx3^;J?feO&tC6h>w;um)U;MSu z&MfdL=5{VIu|L{qI@}OdCuT~%u*_71GQ6R&{-djS|41@j4T(gOLsU#=*+_?Qo3fwl zd#qHr`1NThzuMU99#t2DV32)Z>fIh{2a#UkCNpGHTx{G%M1hO(v!0##ukf*MHuz_Q zZlUllttp9wrtsD&X>U0k9bIcZcuFfrp8*wG5s$EA7@wV{2$NH$qP9Q5jPey^PfN_a4L zF43mMj6~p!VKn2+(#=j}zdu^4;8jR)r!40mxf2sEUm9}E^2Z>&J zW=&^Y{=Z9l{`OQT1%~J?-;%?KdG%tRls?YD%`jw&6AJqn>}C<+A<&jSQ&N((3M20l zi4o(+Pq<)@9H;Qa1>y4+W5bX}K#UJmM&+UK<*-(E^@66(1m^{~u8lJqfa2^dZAfvK z|IY;_=dfx+A(i(D=3qg_gRg&RYemSSKOuwuY#mFSF8y25S1^-4RRFSlm1 z*=y(N`dX^~?%@Zq0*H)*>mjuBdAIH->S_YHqLE;ug)ZnRQbf7? zMkrV^NhTV6MKCj8$KUmPkxqH@ro5b3kl{R2Kb>Z#sCVg`D{-qA^oAJrXx$s;2jBgC+(>;5 zO=N~*+8wj|*q@=I{s+{k0)jjXL4S%beDqU61LOv7N8&np6- z3XXZsbKYAsHi}m$IyrkSWGVP8>WB~d<(U}UN_Cv(njKTXjD7!&Xopu6h~JmHXq~n( z?Dx_*lt`aO1JGJC_-wp6Q*2P@Z;^6pc{=4mH*6Y&t@mFSDXseSJZ`|J0rf)t#W4!! zePFNTqFrRmYOu2J@p25?#Vx-&PtxkD-5#Z|jQ3s{w5eQBPJaJC_|UP{I`0x~vB zdIMon`7%CTXWh`wjR|=mCR}x6f>|zJz09p#dIVNPL_XoQ+2`TBmW4q_r%l9BWpP*J{M7M$ zbgI)rA7WM#r_*iaTmz0R_;FXr{pmzkRc&3>Ef~lVI!Fl#apCV(9Tp=Mr2jSv+f$=S z)@~_Ck-#7o3<2EmPA01>Do1Y}V)0igSC}}Zye*$u=>)ja{x2j#QcC9sga)1`NFV(w@fSX zd0(9%&|)^%-smGydU<4dGHrIizMt>K^EgbslRYZc-eb&Vi(zRo4&CvOM!8Q5VVR;~ zdVZ@uoO)1c&)OkR;kyIiESl?~!(hafQQrUG9ryuWO@!NBM81my(Wmh5O3dqFj1wB!4=>eQ3W%YE zQq16t5Vy3bLsZ4hQMxu#+w-TyH!SiG!N}Yn)fJD89DmlHSeB|W2itHMb@{%a!aeDw zr>3Tu^DrGe{>v8&iM;VT&1>J_7mQNQrfwp?T@xWLXga3Vhzqs0g$*SpJt(tI0xRyY zjWM6L|5zOftxfOiZ`;beLoQVo1f3MOYa}%Ztn_%MK@oZp39qEJNQT_&1=3i%3$5I` zCr8&H-ROvFE^gj2b+-(>!UJDr)+p%<6vEJz5f#TDn%}y|=lV8c-md;%Bj}pM&4Nk* zEC{Z{`;5uGkS5U3rClVzHZo?DGVGG^NO!86M@G7_-zl+y>njOYIh|5l*;elB$BZm( zenk}BXm>Y`I%>5t8q=RCHs6TYj=;f`lDNFy=e;~tO4EOr;}3z8Su8xw$zK0{CcXvS zO?Zb#eJCq)yAp|sC^2@pIBGsuwLx-mH_B7>NQdMj;7=I_DjKvoqdDd$XK&y%h^g?V z6{vn}V$?0eMN29W(l(3;3eBiQ6{4Ywp{@A!Bk5}Aav8DRgA*^c9~V=z;!T#saYC+; zYbP(=g<)(f^pm~M!7(wd@|g(?O^lszWu>ILu|8iOyZe4$_m<(hV9Nf-f{8dtMpeYM zoit&x$5FrfP38PxbND{*)EQ3XW_9zS`t{8<2G#VGT`dEj>rDG?R&=MFcbcPuq4ec? z!WxXAoMsr6q7LIK##w*h%NQxvK#xAa_Xy?q|fuk zaQDY|U2-0_5yK*P3UHAzGWQpi5pDy&<^LTf++pi>NG-6;BS#I!(Bv{;dAeijZRV^} zO&2n=0#9Lo?H3jcR^e>h&=#=*^Rl3VJ^MHAq#EAYf)2a^55<3&URo1W{x%)XJbfC? zFkJsJvp6u;G(zbA(LXihDJFlcj$x_$a+~fx z9A=AccJhQ>kNRM*SYBR5mN5;R zI}$77KQw&vB;hu6<8IZ>h+9I)Zi#U{SDu66;JDdIF3$=BRn@K`gt#sZ!*jdxF~ z$6L8ml(+=e3Ue~cZD^$BI=gNaufOL8;xS=IOnff(z6dtyH-vb5&;(#X*IDW%MU1F z2@~-7h$b9R<+(px(k-Fr!>X|dUiWH=?A8r-jQy1JNJPC)s0r=wTu(yS*wTWGj-MV| z_!+XR5$s!(${sg;LT(z(qt_WRf8-AjoeF9n0j>`{BzP^2pA;Su_06+aZaIrCrIsjW za^|)GvMsAgmUh0O`Jub54i%lfr%6oRSMvuS_*{?7!vQEX+`|(tNqAD6Mr#>VsVsjt zSgRW$w%%9QAm4lIT$yQ>EK76x@Or8fwThI~N~Q}~e|$h{q{Fu77{A}uLOQfFYFBeS zWD<(i^)u#&uG{S2@1ppd1eB_bm{mX9N%8>{A7g|bMd}9GEFF-;yZmf_B{!jO^T+)% zpb~eLA(8{=`dR~j8`R<%*4OZ(-|*BnP+qdJlWo<$45z4`nl?x?F5e3Xl#jFwo&Y_b zxu!}aPZgsn5?1@Vtd3R&Ee?^he85#S_!2nbfDA;lY!1_tPkO60U!`c7A~S}Up;GPR z75*S3;|R%daZkJy{q@~fOEY6+DUwCItQ+$rTW#YQu>-0$ns8?5-{Bo@(_65T_iLkb z-*_J9dHcCh(a=7@3T0cn71~@05=SfbNi8l)eoQNrP7Y@-L$0<==bJkZT5pr#Jl}xv zsO>l(w*XI~!^YCw@bjMYEr}Z7_q0L&`2{wt4y^oed=((sYUC^IsYJA}AT&OncaY`# zNU!_`L+HONC*HXyWHuC89Fm;&Uf1==>x0c4+G}nj)cHuwau@MISCrKOnCR6O1iCs> z#V)f5jTStX;u({p8$!@sseB#w$5)@@9l0{n+I8XgspTf&jo-*(1d>(W)xWPe=Cr%ZODiJ%UE%)Z}42v@FTxSMx^dz#=ur#y%IOtEE?t z{;l~hrs0lGrZt>5i{mMZ%4nJlLzk+AQu7I|+9xz-G3E8vKGmQa>1QppC{p~RnhEwy z6)d)Ps9eM}djk6G0<_OU?>XJdB8jG$EwKrbkK+r!ty_D#rQvw~z90xn^s5FJli$va1+Hh0L7k$eTDYxyl=g%Zj z%x11G#m*Q_CNb&O_2CuD7T8erZEd`3f|5fvonR@=5&MhtSC?xt^vs)P|E(h%3w)?6ua1k-2ydQaV|oZZI*U|O}$S!{hAUo?Wrd|m>XrzaeG$37t6lxgKp6- z60$p$ZKLS8NCWO^D{_s;ot4Nf7bSTz9b&}<3FiMUYaF(-$KMOBP$8Blg>00idBYo^jrp#26 zAF|5ntoo&{mk1+!O;_R6mcO)R6nz%-TY7W%wQGm0si$T6t3{i82Iux*o-rZA%L+CJ zT^%|k8)5$av3UVXQ58;SdiYNX?dJDcSiI59o|OT!i31*G*8IUTX=d&qWq85M7Kp%* zkP=FPvp5?EZ-Jk9kpcbAxjSryN4*;+S;OVyR5EVxeaGD1J0wqc_kLvNn^q^Ed-Zx- z{}X}$yZg9Fz(b>k@83h=4iV<5#gPTw25J1Wl!|OqN6Mw?F7Y4@;zo?MH>T4YhL{1% zp`|}Pkk@~#aL|chdZ{^~>xp5w7Cumd!Y1FKOu*F(+*hsoLr&)q!~rCMmz8qgW5#a8 z?><0q^Ck#Ch~Y~Oqv~+8|E-_MKZQ4}pPa^TwjH-PlVZ+`&GHBgq;WCt7zcVc-ez>$ z$b3(SLy)R&;+x{okdexla)#QMV~hZC-4k;Y{S1nPQR$v<)!g$fQ1EYS+gM@$%X`ul zQvMOL=he+5{=`Nd+m1v|ieZzOUq|IGa(Z3^lU1&OXR!yn{9$DgY{Z+50%Zxa`tMw2 z;n*pZhvj;DC%?xm%3Ee~T{sO{3?^xuz`#iZy`D3x{OQW@vXt`Qj9)Up9N2nIJ6}HH zaO0_s@m6o-)=M0+mIC8*lqIBah4hH^O;|E1)3Ln;nm}Q@9w83^=c=^hl~Fo*1(gKI zXsYv?{bgk6E!mEW|G$rvo*iBgYmT@{b;g)L(^(`Ig;k|BL}wbP~* zuDoF?4b)i_PrbiHz&O)7O+)ZWY3C9PVi4M4O=bVR$ANWa0}kPYd5lvZ-;^H^tV5|P z>BeX`Jp(g!ko~01D%(pT+)}&)mIVtqe>(+T`0dw3M;ez{X1M4q7uMTC(=C-R?y>6@ zH}8A1w_T4fya$eSb(mvgBF-ww{$bnM|E+W!3BJO9yRPrJA8TGd#VKw%L~}Xo!wt-I zvG8H`ri+McvTtv-+8~cjyb97DKdCm_+sY4j;92|5pZESGbF~p-lNHgRD)X6JcxZ=0 zv6kfqCEPsE$SaY(-O)XMD5thK8M@_78A*W3V`gz@hbKRZRlQ!LW%=sZz#9lR%L>q3 zI#17n2-4FEs+5W2PAfnA8J^lrgxQ^$@rL1Wd?GYIU4s0%<_^ywh1nNo=nidAw$U`S z4G3bJ4p3|l%;R0Vot>BqP)zHlZ00((gair9ilS%`5&LH2XfYIe&J0|pH2g^#FyF(RX$#>v+a zPw1z#?1ICE?z$%d*UNY8Aj7Gj<&{CH4<`lq=FF8hMlMV^*m!()#KJS72 zsDg{-es%DmxM$*qfjgBQ0V&}$wK!7K!fq}pAz3~{)8R_aydD~Q8{aCmg|8@ZVJ2~% zX?LD82BWU0O$IZznWSr4oSqhviz|p}a(hkeW#Se|;vWK{M&!A8W9YSFDmB!E@hxK}Uz)RoE%JZ#qpJkR)!WLf(|waHHyVkGvzKFLa~SmmpD}^t4=&aOn$UyC{*2 zTsr>+#OjjfEx8d;y*2alDOWuKKywJ)ub;W0+$dyJHZ^|nxALBafZiIDQZoI*NCvUH z1%l>#a}r?Tu47h#)2D*Q+g`9vX+UrP z1LS80SUojv*?Wu#D3RxY66}^74iIjp0n6QRT=O>6HtkwY(Uc z^tK7KZ+I}X->Kzx(Oaer?UgDlDX#cl5}foI5m6A@LXS^=K1&EYaU#@x`uNL;wDo|F z(sH0XLbHB;A#9V_&d$`|gveg}+NS}FhLq(;eRzf*VP^}4Zl2x;uKH3ApKI}n|A${E5nO(N_$*^c0DWSu#A&Ff+j2%a_&!P^ZYA1 zKH4n75VZPhL;*H}K^8tmnmS1^XWd73t=UIJh78e}j>nQ2@S$M?Gb;n=FlUm#^Lr?to^~suv(u@xpxYW` z$y%xxm0Y)FaW%T%uO1^aLhirx-~(6RDZRHn-q$Sy3eoGh$1;CCJfRsg zDig)Ob>O}TBe{znN?;;OadD}g?F$}s61zKrynlrf znMsK6b6j187q88Y?c2mJ&uxG4zy2piqm^8`2<{Qt=tR_aWKM9+v$B^WF>M~AKN~^# zUCv@q)>gohWs%d0TW^euSt~Jhy4$C)U6O*Zid3-FT2z+5zVXLIAJff+KcPr3|D-qD<7ViF*P6ys`w3%I7hTS zmdh{0-P-j>0wV36!`=s$2E-^?49|T&jqyy+C|-|ExrurTch5ljbhjW`L9 znK|i!WZEpOLbsZ#%nM**pbct?Zq&>o{nW4NAC`?7+wlyMc}uBBA`R(aPpZ7DA0P}z z9DLc}fs%AeDUasHJV5sz(TYSZQypFt!N??w+7iRmz(`d2J>3^|2*rClKs}gSZ$&E3 z=;6qE!}EK~qf(Ti&dSZiCXPfrPrHeI(ytd6-ItZprPi*R(O4Pc=KMPtjh*7$AByOc ziE{jlv#<|>9qlpV#wgvdZG6nM9wM zR*XHOqR;2b?H^u$_DQ|agc>hiAOo3{yDt&8CY7s0OCMv3L4AxQ1@>b9vnq~i_545sDYfR*J zzqi?KcQEcA<-GHcM1cSXR1^9up@^?PArFkrrOc(z#WZAXdwiKs)Ha@b>*p@hy@)#M z3l~k>1ZR=q_0~>!I%r2;6-KB!^Z%WaOt!oL|8BPWfnQ$Qb-L-*rYd&>OwNUas8r6M zx>OFOSWjIZNb&N4>dG!B^p z#`=YR{Z#mjE*12Z5D|}PDkB)>6nGu2yXfWQ8C9%5FiUXVj7er^womebnc*p;fLDrV z!IQ`JWaGcw$iw&x*ayQ*^#7#dTvxq?g@Z&w+IFX^1K6aN;V5lb00N%hsVKP48y8K` z;GOh#g)V0SO#WoTCUIn*T01RD{1yJ*j>9H_Yq=r6#S6z9m%ny1B3#b($LuT+LN)5V zgxte6cg#2$&l?2=6yDUxJ_cbu)4|TLZow1!D=Wq2j6I$K6hC<1V2s&Yw;qXyfiV8~ z)vcfyt^14f7aq8`ZBRAl@vW<^JaHZ87W%az@RM=C0kRSy?bL9=IRnmUjhb>zGjsSu z?XW&{Xz~C=#rd!zC~9{6OWcms&QyfJv;{!yTo6Kz1Y zTVy4cvnKI*p2$pPuph^xn8Y~010MnV%m*JK;a{cL!B$fD3&CWr7M%KpIgBt4qLIHN zh7YlsEOM-E^C?H zvE&9sOJ7t`rmUlH&!W(7igO-4n8wgyJB|48at1@NWXJ=#*Soq4L4y4WX z)V>-m((azCwg)ARFf_(OkpPvyY;<>h91TfU4)GMYTSM=Ca6D&%^d;d6%c47mB1c)D z7d*BOeaiRMGnu4D!;-8=FPtgAqpc$Um37K814pq?|QmkQU}&XfAPuj*{T9m3q7S zcSA}$y{|2{1`kbsqNfKJ212QrbI~i1l{oNM^8sZ0GmIUOSt~i|WT(`DSmBj`=)I(L zSeTqN-&s!lRGhY_31^Y6V4~+@3ei8?8ys8=yEF6DFgvy!AL$?+g6W|{G`Wyv)0@~e zJpS%N71y%jh*Q6Z_A@WeeVzCcml0$=6cGqUdzp~yS4O@L2UL0V{U#vQ$-oeYIyrP@ z>^hX9TmTf(0y@W)Ru-ZM8DvS|*O~H$ouwP46|b{Av7-~`53fv?SHBf@gjQ#Hp05=c z=GtcoI!9lCr6QH;ip}UJw>>gyB!qd3F1bMM!?#4PSYb02T8_Va5YgUudw^+8X^-866nR-L zP!la|<i3U>W8XSscMAvSM^go9sS2>b89U^wxzI5z*)D ztZHi&u&3X_lcQ6fN7@Uz9NN-Rg&hlWTTy3m@Zl|9XC@X{XUd_z(fOyf6!Cr&q>t%c zh$a7}!~sA%n7*B}(563XBBcOf8U7*SfDJ=N7~MBDKPpyH zCRD)xb$dLiob0KhyS+=7xXAx5HZ?;M?M47Cq@PP*VF|wkV|JEmw!s#q{;CHf*X;4zyoe@sF7+9N^3D$c0NYUnEyo?EHKe$hzU_^MN1)&MJmThxb?);1`LXwt_Uk0gV|4&09}E`&b-| zA%_{B)#Bolyh?nG86xWn(n&DsgJE6%-%WWlTE6Cy1;aht75`Q}$}vvWv0}s-7t$w) zOPDnDXCewy^5!b#kE0v@mXz1kl?8EyBLk=@@q-PIiS`ObuD^t+39%_WnQtb3iM~4(u%;XDV5y zK=HA6uYY+8n#pA-gA1vO>O~o9G8wZ?$IaaOOb_t6A546EMa?pYq&WoHriEj?3z5HEeIX;i=|PM zHN6N&=hu;AYp(m9OV_~f1WdWx#1|1+4j{J8j42Wrl^1RaNDtY9n-s|%B!W}}9ga+B z>CbaTXLelS!2{-G4l&6<-81T}ikS`PPvuHmr~^%MFzLh3Bf2URO#k}ujlRWyG0N0U z&7o&X5})Jrl%sV;E--Y z@O7W{_47|b*YS#Krc1ZEq`kEhRI56y z(N>t5b!nQqG6y=#x`9BWBlnFbR?46KTC_M7rFSwktSTSXlbT3J3nDi&e>l*}n^w<6 zR9cx+*GJ4!J-uCyxEc_W?Od_S5jc$RlM|)dpV95UgAy1i-rp8f3{Oq;j<4=f84@fU zX*t-(wZ7MV3AO^)6otN`ZyA>3ZghEL!(|SOA&44#i@UA=fLHT`IFZi7ema} zMQ-^}dXTlZbyGgD0Gs$8ly#dU^iF;9Yi;xUXo@;Gx}u17dWNzZda0dV+@5@$9L~|W z?vu%1;$LCVJ*RyNoyFNEW`K@sAIR{7_Wv`@57c^^|A&;U(cVz{P;{i8RXB;W?ykNc z;JU|PXcI6P%-wX=&Q~t3U*8()Tjm{B&`18yjC%rdz|J_oHKd2ld9vE_RN%Ilt5jR^ z_+E)C^Dr@r$yI`CRU0^Iw{`DKUm0+pa0hih`O2TSB94CNgs@9;9;vcLw_1V{?rS~Y zzC2OnT#j|G$KRh-DC^m2Caa(E!9j7O;9aS>2po>4EI7;`jCA(FuB@x|cRnGH8h&yr z7<6DVp-GsSE5LsxR2ceat+@<4Lw+sG(v=(A5}6C^Rqe~IOWwt(T~;XY*fm-~cmhjd zEJLw6XO3>`a`CekcR9G;&#KN2m67KEN=&(%(%CZhF=Yb9;65xR^%7E&n(aL|f(l(1L8{t@u?r`6P0A(6K_- zIvE#?S^Z8F+$J^e?@Tul|2|CB6T_x{SlPkL)nUfFQ z!$=IL@Q^f*bu{bIE5S@46K1i5wendaiekM~o<0jSg<6ejSEs6(*EHp>wgSiPB@(ICPTV2`8Xi8WSNM}KghSQ)m^sr^@Uc{`G@Do6X9 z*+u3jPa3zcb~jw*t?iV(Ec?}d11^fYN=&Vx>{f$iH9RnC6vSXG_=Ve9&;9?lluKpx zyxE5C&Ngzw%_12Wi34sS8gEWxx&c?I8jS~{^nqqw4P&=2AC)t(e|6Uqnfn@lUgfKm zS^CQkq?VJ@-|->LK7EH98-fCr@p-JPKlO%@>ldFY2tA;bE+=)YkWzvVyV2`LhP!dG$9jV*Gt9cYdgbe zPiaRDfW)}5m7pAVL2&_`fq;-ZS9Dn3`s(uF>Y=%-(H~e;kVJ)@MHqv7a4t3udnN^l8cdp|AwHF zmD7+pq>Wv?gR;B0@x`K1-dp&Zd9nEOuasBA-xbM^WdvsY$b%!`)v`1Tnn)}My zFc`Eftj??b!Ha!nFqvK%K#P3o!P7gU#!gew>qGN)UG3B|=;UV!<6+Gd8hnb_{%WJ) zeFi(r84dqZb>%(#b=&I&_&sI9-3nQ}zse>fhh8f5zb*Dvd#j9_g7LgWtzpQ+v*Vkm zLF$*nCK2}09wbcvH|>392mh_o3rp9DEoxOGk)MY&WL)QNqvBq zT36Geq@t8aru3L0Kb@4=RRCT;7#hM9&w#3wr9OJcUQQB`jHZLrzRaor z>jfAyq6nTMzt*Tvv_3tf%El!Wh4K%m3hZtpeGqAPf3lCxjE$qU{wQ^14fKh% z_#PG0Yq@oK%v99y_0kZna-u{{$h?@KzbOgPcAHD|qK_bOkU}3Bwb+c!6=WOHTX*QM zFGG2u7$pGTV>wThqCD2>>2|A5cM@h-wzb)W!1eJ%MOi@WhLOziH-#Vi*RIP3MKd;( z4de$mCU$Xc=+3(ilPti-)*1pcjF(~rXp1PgpE7{=_nM@~-yMW>O&YuL-~2)0u!s9K&uCtUM*g_i;MVRf z0JlJBc39O9B~Hnm^%40(mNX)s*F&E%?l<3urxO7eLF?oHE=l<%e$jSaa|oV;>-A@k z*a0_*@p&;G+pCFz?q>LsZ^VZfM^*_rLHyO`xoi?KO@P}&)|zHR>EfSfZk1Y7?!+c{ zknfJE3_FZ+c@A6akPt!dJrEX*{?X74KGQ4FREPfL-^n$XxuoYhlIGDR{3yeZ)HYdE zh>g?{A^&JhGHoeCXh*kan200g&Q-WSOUBj|RO#99GOJbrTat@~P;hW+Ez3J>s@Nia zBa7;!L0$C)n&yM}Ymo+KQtKGBhjp>=-j(ER0W$F4=cOu%U$2T~8itOA`E9%Nx^`40 z0roPunz-X?QFgeO=|*VQr$@a9#|6z?NdQ7x7haG<^eIc#kmO0Kq)VDq@A zrlD`GR>;|aF0xuJHk!GL^ReZoP(pg5M8k$`vzKpTgJm%3`rz;-$q3|uy?z8@1$K3x zhgE$xb^xvs7l9ks;fWKXlm{6L%+=A7Fd)BU>G73ncAdER@wf6_8DX1`ILrX|9U0`$ zv4VW77Bwr}IPNX57-dPuu$q5le;TxymP(kv*6mx*GYvn1uh5;%ZOp-R0W|2+V`N7s z6R_#>KNdizctyUNm&F&XGu&=$-_mf-G7GdziQaex+LhMNwff;Ga5H(EXCJzM;&jHj zC66wX8kn~$XRI%zU%|-VY~6TgS569>sI!kc^cL}&p!8vz?AJQ1 z!B!?J&UUoB4uOm4tBJ?k1rC4J;9ky_P!xH z7DQ2LCdWuTbDD8aydEGqlKo|E%9V*so>$1*%aABB?d4KHWv&IY{4HhqYEJX)aJEX= zch1S)Is8HTL$9{<*(Fk}+Y%!NjmY%#(?6~TDG!o`S;bvmA!M6)|5e}((E}zXtN{79 zd;E3|tw&k6%E(ECjDp*r4mp%VR`h2YP-TQjlv*tM3oO>vsGAf}d9U8Ssjpf1fk1)TD*Y!5y=$@h^*5XN;! z8+1vPjzQPgWO;OD&C0u&__Tg&p2EjIpaqMMM{RsT!e=#{sw zO-02L#c#cj4kKUR3d0r(pyJY%PtYn+#q}iF*CA{U&yv(qe~!Cad>2nHL^Yj>(99Yy znG(blbfwst@DM3KV8`5yyPieaJ2OC|rEh5MEUwHbXRo(1#5@>cxB~4 zwyx{7B{^JA{-Vc6EOLC&=U+c9h)<3WoV+u^%qBCYco>MoX)OV|90JPC*$qXqnMzYV zMzb>;6bG-(qOa_k%o54u@!{nE#NcCpJ4IxLAI#tP$={)^g|3KdioI;r?k~5|dJU zdQ3YcJ+hF$IjyW7a<(2T-~fKxy#(kH?q-IFzbo?8*UH|?bAQ=VgFbhhzQIKhEbjR%D=J(Z<9wd=HTL#Js27jqYW1=rp>mq7>6LJDo#F3| zQ)*aR06>3bcwSXT>euFvlDs<|XWlT0#l7wxM$xwqt_&dPGHzc++yQ!HrdX6XXOzGvoVz{_AIU(E^hQulz{DMyrIgQbZT362jo!Yekg8Hzwwv zYH&FV5MUqT?Y=={;wKMmH_g}i5`50vt`kKaRtct@!)e-fwBJ9bKJw(hG!XSPA}c;8 zaQ@LKz)7f*wP_uI_494#<{Vfv@*3@q;dF4ibfmhxm}wK&BrOU#ll^(ncSSpsma*og z;nm!`{k@ojze*Y=B1F!J}emTqP4&pnv}yv?Kx?RVC+Vyc~idZF7aiFKSOJ z?p}4UQMlc2gr3iO=0U&!=MsSvefG06bx|s@^vqqJ(_1)4WBfELdxt!Go!!0F=4zL& z#$S@pkcRz1vDEMBBY9Dh;cHto?(p36U5yE3ogS&xFCjVgRV>u08Mp)~v{EJ9VRE$5 z6Dw;7Vlkm6)agw?DXqNKNP|R%grrGZKAd|ce!U*O+qn|ddSB$a3mlJN>jwOY`jn}^ zNMnxFS`fBW_Z%OF`XdI2zjceJN6s>|*Hba_+=V7;6ey#CP4Yi`GJri z!5xCTy9al7cM2!C6cXGaxD^mw3xd15ySrQATIl08Zs&dPwNC#u&e?m-xyIn%ySkaq zdbSFcc;NjeQem@^ZT(~HevI>$ZfK{$NldpCtx{^Fbe20}ymN*k6;u=E6uO_Ml--zR1`{~#zeR)lvdWxcM(ZSZ#!#m;0 zGPnNhg>eiU=c0p2K$*t6WJTQJA?LhLghWdy;dfQ9y`)&mU&C$nBLxbV?;5^2Vp(F* zOzXw8e(YsaE)SNH$fRuQxI`koPZr8XEdm2Ua83gqZ+jS? zE$ef#bi17XalbcZ$_+o&M?E8^B&zt`;E8zU7-bswn)m$r+l33G!*E4ITE}5 z`nPDEWWa%k;_vsXi>kA&`;c&REOdwsy1h#|sIa={z%W*fleoo(VTNT&Hb~?X;(==D z#~?LQVu_xe@HPoqO~ z2gOf_LQxS<*(^8Q{S)Cf+fl{AgGT-jjPl6fG|ZBDKj)zA4L`dwZZ}$fu_cH6b*eVL z`hQ|AijQ;qwJJD_HLVOa(7(#LXSb|Wi|T><=FA=KU$KH_GJ(7c+=ppGf5etNtf3>j zc^IH-iesOUV~uLsSpRJq`DdN;&7@i-n<#;YIhH{}(L>=5?;Jijn1j^~ih10>bu_VA z<@GVgwvJ9BI{VGF zDfAZ?Jzzq$+xk*PAB!&*H;RK#99yS?uLchRStNHg--{@d3c-n!-;FY@VESCFlgwW) z%!j8|5nKHi;}%N%29Dv{Y_Vs};eXHAA#b3y9g_D``Z#YglK)q%Lk#ZRyxSC~x<2hh ztH5HvCJrm&!I=@%*DB;mOq3J26Q_qkXeJviw*|>a)2`Z2Vp0PUSl5zhCae6QH*L-D zE<)Z?WWHI93Z8LRL%0-y7fj8i>@ou`zQCyDdJTNB1PPuWat8|>?aVS9m1Xl=)($52 zs$_M75~GA ziKu95Lb`6oyJuV_2IIl$7`a7&Th>-WPJs6C1MSW`vp5ck4kYwI@GK!Ki5VA}xJ9aI zV@CTkbin1m-0@KDq4~1QMimp=rkLk6po`YH;@@0UysX=c^CvoZc6F#d6XQ``NyHUE zqL9}m{N9q(Qtt*GKCab1OGtNZQPYko(|5UTC3!~i_wWVk`mLVV)Ruj(_m-4wa=ziy zj1gD4SYO)-=~k>DM~{(b-v7}NyKba@I4#H3E=5KcpQ$8#!<$l86ZU7g|Ms4ZpB`PL zoL2#YIg5!+QEST1UwY)4bRV*CVuynC_1fFaHj~Si=*}+!_ap%hr9x5BO?FOw)oy2F zjOX}s#75T0D$i37iyUu6#(D>5jLJAx%jJ#)RdI1iSx5Ng-zcyF5pCR$pEjutN3U}j zd#0)z%~und0$x`gP_QG^TyvglHXS;K|^xx8K z?^JPSexP@=my%UgC%LrED{N3u0xK6n56BMJ+{aCin36Vq2d(L_;?ZM7>*TL z{`|k3c-2IQs;oc7vHMMH(6u?C*ne@8euiW-o6m8=+lK^ZpbO<+Yt;%JPvtWNZnQOh zdzl_QH?*{JMr#>^dSiJWU(XmfGpYCPu1-7eIGc;dH8ck?lYECq4h)mBi^cu+G#i(f z^bbm@3w&ll%q*=P-8Qn1-u9NCJLi9|Qlc$+pzMO|lkEO5>KM1Sg)HxCdI*guJMwe3 z_n~6Y(>Fb5(%MVkLm#z-Mw4^G?R1)(B_A*jk1t2PIDq(G|JvngN#m#PQ!GKghMGpn zkfq~9YSwya^Aq_n%!;5U5}hr}(>774q@tN~DO=MEog(mirUF@xTIvOBvG7*3lK0x! zGL4~JdYM*D%<7extV+B>aZX3qlXtMX{9ao*W!Kxxgn#<`N=&9FtL$T2l8U7nZNoqT z;r#5<$lN)D9{qLa=F`1nb*oR-$M9_YPhI}Bq6&Le-nqd5j<(kHKNn; zGVFML{_NPWe`fWa5vZ5p-0uW(ZQPP11dRnP&|>iJWU36$FE& zj;;$>N>w;73|6#&Eag zT_CGPem{8jS9oUKK1Fx{uzDfm&WHaPcgA}*rmn`ytyd-Omo1Q-MMF#>px5M}Q`=O* z**ZD-v=KgZM^v5?`v&_0H=H8TXlCvm#8!cEi8-Rb;w}9kliNOdi+XcIGQ^+`Q2Q+^ zG@2c_B>eD#N8EA4H2zgW`_RK=k(Z{tMm1(t+Qb1ZPPD-_m;kqxJ{{W+dk(sh`Y-ohSi9m|kA}jmD zduoqT#8nflVeJK!LX2bj*{hNp&@)0diV=lWhPM-zuZDSM!4*wMUpD+*t=;0#N9vJ= zy?>(s(plJ%xqrrZ&B?nusE-Y@=US6}+e5v@?dg#_-eW`G6A<3u>*t}%}ZF?NXj8?-LSRB9lrS@4yOk^y! zX1y5B=$Mn_wvTM6Yv^_OM%`mfn_?RR)r0K!XZoVB7+9xCd8J}R$wUk?j~?2SX#}MT z_ZAAK|1~E{0FrA_bUDYs=jhiX9_7h|mgOO9u~U{c+7xAWm4hw7Uz!|!SQ(BgdPPYK zvl~p-t-!^i>3OW&A6x_5BGFkvJW*er=01Ax5r3B;f8^6L-|7?r4WiC9rl5%Id^PB%j(u>fZLE+(s&L*5g|$g@h~GalI-A zx*AHQisCavs%jgHA=}1j{pLvliKd+)nl=p957lXu&-x<^G(4kySi%;0tODeuJzuSX z_GUrGnQPYGlZusfGPN~IfhOM;KMXDrfd8(5_5k8W=B`^e4Ff!zA1`vZ^LE7c^TC>= zt?)Jqdu6e+h~|RC3xq|18RBQX!jzVJ{o*<>rSG)IBsiDU8@P>9BctTuDr)m*Box z-e}5OkAcHI)T+z* zV&OseC>BUPy$c5f5l!$uoob`SJ!q)`!fi!A<$aE;6N4+BqMzK{uJZ|2rk46(M;$Bj zW_d$XHJwl+h9qdaQRj;m&|MB3)bPo?zt&W7`?=(}LwsWJYrr=+KetRLo*Y-I7xc9g_B~w-Rf7alC zcN0YT6Nt4+j0abEk#ku4kXX*_ZTB70ff^qANrM-BJ%Y?M{*Z31MQrP#@~7t^>?{40 zaV%DIDL#HL$T1GCQiQVt_5H6D@Db6@SjT;DBhMx+hws7I53Z_IEoQk+>viI!%wF8D zWHeEb%X*_{D&fa+R2b<(45SbD#{QlTP=g#c=1U{_xQXrr@-Ge8$FzJ(t_NhKP(Q>> zVR_d(K_9=|)#!<#ZO^1yW|`%IFbwXYGTvMhM|Q86+Xf%;oV(Y^4d~hM zcb(!X@P_r)51_PCX41}@p1{^y{c7E_y1)L?@6pZfwK80oNr7iC;x#X<-?cWhkUJLRvW8h9BO`@Q4IEDR>m(MPTgG) zuUBM-6y_{WoWQW&S2kZm|8Q=N0SAoU(aH!l1;BLKTS`Z+;en9n&}{<^WoCCsVTFk< zFLO{(5bm77Dwzd5zTQxu1l7{|=eE~@%pDw@m?Z$-(_KX$p!xbVKOZ-r8vygPY{A9qn0P_A9bB0*v54Z?P~B%q)b) z_wUmf@lppAjOwB4L|vG2M_%pS@I)|9m`RuOb{H~Cnni5DPupkzqJJQfZMIy_7z8{x zHIY3eSv~Bo$(o@=F#hs#kEG6iD~6N^J?WA74pC1;&DOdDLkms$b+OcW9@cbDQan6& zD}5f+-#`B996F6{i;x+ESYlx0k}s`ZRT@~HdLP6urT{q`#O0%X_MP!hfWDxl;uIh~ zs}<)A;LFt;{Vw@a**i;EB&HOiWRYf~Fq3yHVI%<wrk1i)H5>HuNy%Bepkix%XvZ?1d$51t0`e(ft zdj4HX_9Rk}7!^MXEhr<({%*;N{fqDz4%wdjLj<@S+N4Q+p29^~hRJ2+!Pe3sz5F9U zKY>zqm;p@PH{r>{u23#^Toy#35r+t#!4uS^9;HVZ8_@dLba%z|jY9QM-LSQ=hHaO- zpB0#z=y^rlv51E~SjR+`dL9~!!1zVk+%0X!-@)gVQlxFp*cl=Ow(7DZHNZqD6T&q2 za7~_k4Xxu`%W7BR)%p*NpSx@S_xo^d4Z~B(t|v)#A=V0}q6DWVdCIzGfkvZ;}t$DDp7}=e+Pr#+1or=2dEccT45RM{soQ@_OCs_OdTe)VRa6Hs8QpnpGuOvoM#voC6c3oV|EJMf@QD$c3^!8d z!+naFI}Ko^rodCPz6==nt(h$5Xy5xc58B5oo7&Pq`C z|9)&*z`26cKW#Zq;rVJ*w+C$e7OoqeS~IrBN~h&_?e&DVq4mmT|Is<)LYzj&x; z^y2%86ijrTJKL;zS1b7@vqqU!8GOe*lo{r7dV`TH6>G<$tLr6*HWpR(wndOdI*U6_ z5m~!%^W9{1u|c5>;>oxv{oNaO>AV* zqlt$CFVls(wTbNt#y-oukWho@eMI2tV^;DQr{l^;L>1DPHfmX&VbS?9h5b7zrxByH zytFcS?3~LznmtDG*x31s)v}J>t{*)h!$)L=b$v|Td7Wvu_n#+CJC2#Vdm=BK;48_x z9?7%fyZ>S;IU@xbqAnkWHxK|3QX)L1)=T;FX6X~QKn{jsV4VDZ4YzENSOQf08{%NZ zPSA=Dz6lkD^zR(0>laNEnA)o3$vo=;Il{o&A9lAY+b?W>55&2eoHO~9W8l+9RzQdl zU#oWnWZ(;2u){ZaL0f`yn5KN%lW};$&(}HzgJx^KuRtr5V4-tjhU;WNHsuOgyzs*s z7m5@P<9N@Z9I#dUJKy;2O7mTB`JA3bXL?&q-vBnV5Mw;p!y^yV(hQ`vg}qutNUw+9 zOz=?{DAps|BPhI0j*bbW?_oZdo2zSg`*YnbWRiM$zTt`=gDe0aaTzBd*!LED>C;r2 znw}0Pppn~t)(`Fd-!4Em^`y(;NZ6WAlHIGaBX?0ckq|WVf5d#LBy3J%LYe+~U5*FalRg*0Bw9CbpU?w;^8<4mdK(rgYt167SC~Q3gU{bz3SRAL}fj70Eo8-J@P~ zD3UtSoG2_CJ{0`Tm+@sg=MC{}9KCyEI!zm-({~tOlhd(4bw59~w;#cpQ0{a>g7HL) zt%}qb%%iRR8@5!e2m{Cf5bto!0BPGN@OmBW;6dimxj3~2I+FL-fsJ1Gi3th~5WrN? z2(It7S1$J9^Tp)$clUf7)~#weji1lb!XdgTLv7}UMDT)@KVfR=^q_B@&%~Q=1CDGB zWY>>$l6SrB9Ydp<`^U@2+@Ih5V+r%>1)Qz!$Zh;0dUM2sqxD3(W zjirgHsks=#7}pQJJ{~?#f0(e3*qMr)nihtd2c?*+0!Ch`aCTC@08WwYk$1ZhofkPGCkr7BwazNCi$-fm3v!xAe@kU~?SNsS)6WT{=SVB8t35~b3HAmt;MoQDK&<{Khn&PC2nrP(-J*X5R^q3Y z(<6K;?5Jv?vOjjMe5~_GgnSgUd6SrUME*lXbWW(+uT7tX1o)tD_qxG?F_+eX1}h*^ z-P@ihsTH~c1%;*)@e2cj_NdP3n6hX}VmTAlTjLsR9i8i3qa+~TT|Gc2Gc!-b0=?3K zF<~C-_z1m&sIz@^7Dwq{%a1L$*@wo*xlz@WjvQUG^uPfw==sPRus{~OWsWAt^!oQp z06b9n$j8?)reguEg0@szTM@#-LJ?kS#w~>>H^5T}BPR)heVc6+-g1pHS5{m3ul-D_ zq)%dyq0fv5OTfv=B|x9A*HR%jm!xItRF>z=*$|mjxG*V^{{b$ysjQrB%4$oCX~eUf zyK?PAboXCo2{x#dtRH0Fe5U#D7~EEPPgLdAi!u*nUoyiG|FW zjNCkRg7%!)p65f_8*e_gr$UKi)hT+`daRA*$9>aADh}qbGykscF}bGNY;U+ zr3}y7N7LlZoRDcEi(kQ9AM&SAG?}Y5S?{zQ<|T2`H~yhYx?a7BRk~ES`vT3Ib|BF+ zmZSQmC3=;%(G&4MpNd%7qE!qeWA)t@=V>t;Ng@Ot=%%h%$Y93GvdZ6IaK(sv27<65 z;f9-sCIh{{=M(j@`Au%Okv_M-*3kqX5{U)) zsJ=beDG6HyrGyA>uTEFv*>NFA)?RvfsC{03#Z3o+eC0~w`$QsRqRWC=(`b{#9gc}K zD~{R9SxYUg>!4E)JggLbmUm481r>@JnBiM15JE5*xsG$h0FoM+l+2iDxMAULjpFfP ze3;JbPhn&!9#Fg`;qSihp)<^Z4ChtlP0|rRJU88Mgi*v?cXqzr8Ie}|#GPh-^(?W6 z1Ktuo>m>{u@oSqp%>`Fcz1%=IpAG0Tor$T;B3d5+h&2E0G`FU*%;C^1QhXp{&`h5f zi0a&@?Zd*XA*9u`-hzw;cbv*k{{@9w6@~Yb-rKUK=jPG>MI9#M_Kf>$pl@=coUI@- zxc4EWDuCH8i*HX@(`(uY6RFBQNd-op}hwL;aSIJQywOI&^HuGvbmb0a`Od)Z+A8!mEumW2-ciY`l|-3-4@_o z<@!&W*Is4ygqA@=HZEPVWyS-ABRP%?V_RBBlf&w;S(6gd$vOZxueZhbn;3td{fgdR zWKP>m76Vt)fT+WqJmc?{!e+b@v;S^?`f=RB?px4X7TdW}R*w(7`o`)f5qIfYhsI2^ zQ4@V46}(mW92%IDqmP4YRld<%c_LVMmO2HEQm=DXGyw<6P2&aXlHPa38sh6qGAWG$ zao0t@J_A7x=w=0H{kvlu81M%A+W7|!Xeu|;et^9wNR2`lRUyUr8TP_;YxKK07K~A9 z+yz6pO6n@ZuQUcVwJ{^G)urdDmFm=cj=)A3r?EHzX3?tNa<>kHOPdtt^!e|Y_ZRl`|q=Dbxn&SZO6f_ngg*1p00!$WCR)u7e)+}qiMCI7pJ_lD<87=}vb-O?K~ zhN$nlx`cqeIT`0wvN~^XZyn{xox`ej86y{J-&fJy{8YaIo2=vxNm?TiZ?uZxdQT}* z@QgG;)!R6hU_BV>8dlZ866A$MG+BTZ_#RDjzv`YbRR8i^Zj{<24hc8j*X-Vm6cpmJZQ z91gp^0&3$|#S*8U7)Q*)KWJE7bn9 zDJl5^B8;S@T0M?ctFE5i!~SDGU)De~Mx}1j7j z*ZqJQu~$Gx(~BbLV-R8Z0L^=*SzVw_BV{nr|J*82L3uw3}( z`|n#IAvW26rxUdaXd#PWPF3W{ZNJxIN9VU?0G^Q^mN%Y%xLST;t;(MLjTwMf|08M_ z!~0T-j~BM4q7;RS0sq0P%>jc2-4hVhW3HR~4}KcWvlTue1MWSb5OyUKl+!sNkt#{* zE8dO7!f586jWaWH0Mo=R+^}sQuD{2vK?)9&$|jkskM_vauALb*Rp=yzR% zWCJ0!-;YkP(^H9N;eR$7t()?uUKS;_F%(!ju?wW zor=fh`-8KYP#Rf!xKW}5Z$6#ik$3A)j4j1-Dps=w_jh#uq zm!5lsN%}E_hqsFx{|PTeXqKcXv(KrWP0=LHmPyec6Z%dGm!Ib=g}_E!y!FxB$fGrp zhcAnpFWA{=tdUX8Y`|xUNoE`nA+ZCc04++2&S4=y>rX$HjMSQbR%l!LU)lFqjYvB% z!O}9Cj*zn_&Rt`_aw8$Wv85BDedmz4C6~DdQ~{>Kj=Ku8_qaCWh}!O9&n zT`6X<5JO#B6Nkizv6O-YS<*_E#rH602Yw}!*4RQ3b8|XQ1s_(08WD5R-JjHhpWAu* z#LaA-g1%Am$~hJcX&ceo!_p3p@DegW`bcwI@*Dj}G)ZU?aK$QY(b!5AJ0Jert zB5dPxiyicS_?M#M%V$S9!liM|Qe`p(_>uec1rvzRGD%584`}?b+m}}6+TifrVG52} zE;_Y@_-AB|GmJ;hwl!sunN>_L(mqh*=mLD4L;vC%HE?lGZX}wymLGqP51|E_+1FY1 z4I85DTU|#Q$|u*P&0>>j@W!Q}P-_(x371GIGWX#M{L2L4vsw%vMjR#kofDC<*M>{p zT}o@&Hu3RKmpg;_weEYV+$-n*M#?-pfI% zf4maq+jJxiirlbNHhKV8M4g=@69%dUkGr~688aoz0-?S_cz*4*aTfR=O_M0oFts8H z6|c9O&qOVXw5XnR1?Nt77)-+(EZh zV=XT;$LJ}ez*59zf6gl&%mK4Lb-L{CI=LIiXftl*5_WeW?x|&Js$4m`#FUQx-VQHF zXj!YcGECexD{+wW+t&3Xq2roCkymP)8Yf2X{Jaz=HJio5&p)CHkJ|$~U&*@_CrUDQ z+nNVzbb8~Mzg^DR);n#@!KSDaK(BQp1E@YM0nZ1kAm0~;2hiKj@;@*&tiv*?A9}LQDXv4MnT>YPzQ{93XCJRla)Q ze4||*V-%)BscPrTP&O(EQn0+*&A%w|4%zD=6#kXr!6dt$y=mX4Tc6K}ILVDbG#81o zp&w84OC!HO4~x7>m1(+U(OKcR1`wNN4tChx!kd;l@S^8tYFAK&{n#%~-;V!5fV1xt zE6|wJ&ws7I`}f60dBt5y;7&A_MkjDwo9Z_cMZ<_1H&MrF=Z2t*~APjtbJuS;eaFkhm*Iw|zd z&d~87cq<7@c7xUAdh-b(_G8J;6`1*(?RVW1MY3{qjS;B&ezv&UBJ#gi(8%e|b}qkeVyD``t*Ld+8Cs&Y9ByghgCP$sV-7aR_}J%BZ%*^z|J<9U%d2$< z60loak!;2kxC$*!YJQ6y>t??`1DCY1m-F48hrs)|JfwiucI|xv4AjTscTxOdZ80Z# z4YWvy;-J;)*1C^f#HckkhxRbzped0};=}7#H#VEP@XezBBbMocuA0zL__y7M{%!mt z;vdE}Dk#JJp1N%mhjhUere>~4kX?DB&)l2wB?qcJN(SSW3`Oiq&M0NRWtG*c6!Edj znr3k?yzGl)Dr-TekH7>OR|$+FY}Nq}!G^dj?*6My4yqC^+lXL^^H;}7pKNX>{r*n5 z4V~I1u_oLws_MfGRbnmGnO*yF(2|~p{lRxaI+WbuD2GvN zaB!`hoqeR@`~#9R-H*1q7vsk-h4Mu#szRRwk+DcSGA{d$L;Pfxq#<3bz$&7JM4L5PuLpJ#pp`II>&T**+*w zgA?-x0$}Wr^cFkPZanegslDt*a+hjNWET@#Yop@QH9GtvG4C^ue#zozmJl0c>av7v zG2+IU96SYPTpE8Z=Psqw8`_+e79xFbz#T)nd&pDyVc4MxFf)m1(m0Q*FS965=`NN$ zO(!6%P+GVAIX-Q7?X|3O^NZlqSsH_T3U(jYoUNM=v~fbP?Ei}%u~{^VlHQ|MdOUeX z9WD9IKYQ^#1EEvm`yg~optr33v9OQO*G-tH6|Qgy_Zs5n6CI)v!Ny^u_pc!tSBR`+ z$-`(!CEUuYK~6DxUpZc{ESA-PMcDv+y~jSo<F`BFSU(VwedW;RQgw6-xR@At__N-Cqlxw@o*C!I$it& zwfORd<1Kv;0}E>KzQSoJr49#)4k%Dz)azn_pZKlHoSVuM*R!tm#tl$ZBRB=%MWpew z80$Pxw*~3l{&l_^bq>tLsSBzpi`lUc3j3dYgCTrcb4rlI(l@}#F)cpq-WtkvL{%YenyS5KYWzHBy2Kkv>Y2S^ zs7_bw-(GHvUU6XB>bWMaZ#l9PF4csz4#?wVPF93R+q$YUCyeP3zh;2Nmk4n8q*5z~ z`5#X~oj9h(jpnd(Ed=2kx~7!Wcf#r{aHFl5vOic_BonKi8p7Oz0Wf3I?voWEx5*NYxCy8I>bEBeArWRh}*wm7pU66sVZnk?QS=XsRCE z-#t{rDO)|{0?sYkc9fVjS+f^3%K8QqUHzs_x*t6uf5Koa{g11!KrW$`VQnnd-hy~_|Y z&Gnm-;Bfm8wqKMus}{CpDW)@_J&xCN&@g|nx=oYt_r8QGXGg=0E0XuB_uy7$(5|fR z)N7!ml39ylgUZyJDnsP-LDvG!kXNtef5l>NVEgN-*}3r`orohhkGnkf1s2y~FS~L1 zoqIQDH<7&0yb9^=i9;J0(67nZ7;vp^{?%5w;36{(iJ&r0jK+h?>0;GaNr(=1%FXTV zh#+&5E2x#iyQTHxlN51LxdRg3rAa3F3tV6C`VMYJe#nyz^aM|PD5-E}nZ0Ba1e~@}@3LAR& zV-LI0j&0`yf$<;TWdXy;dcx(TGIlo3e0rJx@> z+E*nxL^7xQLq8IpI^F6zn_lbQWjC^AQVu=|>CiGVJjd+lUr)ZIT& zn3AK#d`j_&=K3A8)X+1Mm5u2nrZX)Y9sM5U$i!hibF1ZqXlr)$a^=huXr3e2cs@Hl zFQJw>HLknhW6aIz=9VyyGgX#x3mGFr#WtmIGhKyTxVv0;ky2Soq|TQ~P?4kZ-W4oW z6vXt+{}*s}M=RP)iO-nl$hkl)Ux^=)HgqDEBhWk6nyRgn;M=16>DEmavrM_{`#9sS zCgY8-Hvv=|Ju9*Z+tsbk;TzGq=%7BCgb>JFE*Z1AS0jbNlwfgIXMuas>qV#9s5V|> zCCk2|VuYW;y!W=@QAA>myOz?GU)P-o(*RI;s?$bZdeb1-PI0(+q`p4e%YL(lGCs@x z<4IO+dZrZ^UXVa76ql$lpYCe!5OEBp1hwun2;Y?+HosnL#OA!|mHqZ~CMzj7Ux}VUu-NL3@o$xW_->XH z)SFc?7v_#q-`Mezb>EC5OeK~0XnaZzC*FsthV1Ezj(qYe7O2f0+0ZEiuRS%TlXVgWI!Z+oB2G@Ef*Q^V+Q%F=&Uh}Tz8 zSD{%}4n*O`SSm+C*{KJiGS%=AwtHG+fJFA|=;f64+duaQ z>Y1@;L2{@MTB`xm$b|c+My>IP3ia?lGM+h~?%$O?=kUT(lIlbWq*3#w{hDfUyWr2! zv4rLdoMO;Zu^|wm$jg!Wa61`#7$r%qBwnObYlnT`-eX&qd$h9l7tt zdA@UrwgYz9c(ivK;tz#yYUC9YqOmraFNu9bMjJ7szW+01@i;TM@8IVG{km^Klp{zN z>DC}Ld3K}oguiEaxMD*%Jm+OAIU_IlS>$V(;InF8A4E13TLZ%eaY2V3XL#c~`Js{j z9ZoZc7Zy;$>ScK_hoUG0ThPce^Buw;sYyK`!f9klmu2pz7~K_J{nL_7fr;tD^TAnOM=ShamlY}cSkw;jJHd9s^43Dd^J;-`j>ljlw*__-`sp~erd>(MKgKpgusAN z+tb%LlH_KM+F^^js5~U%=c!t8A}Z|jVIe(xe8mTMCxA+lb^D)!lZyD^QmD>8pL^6$ zTuXa!?mw}4a+E=Do{6sP^EZh!0M$@0a<| z*ryw2Mx-Yb&4lMJRIkP^k{FvRPAn8Q{wr3qvT?jl7}7~^E|}4~qm^voYZ7LjSXc5t zxBm3;s-9Oyuk{o^+s=+2WduLi9ov6uCmTePutMN!ygciLDr5_iqV$ia+)gQIONZ@&`O;?wU5rf%Ikbe1rOFBR#{x$k4hn@QTd- z^iqs#R!T-!VWvy8wS6=M-OHI5RGh1FbioFA>~0?wSf`?d&m17zE~Q;?Gp$3ahxg*) zRLsZ6tF?Xj686A-lclxi5ZW;7S|7n)gKtoD!1gbHro-=Sv~wc)`2o7n7$U4s%uieu z|GZcz39JJ$kC$D^2;uD?ct__vFDreXs!7{f8wai-N3=Pu5;7d+N0%k_MwFu>HlXA! zfqnvh?<-W(uJGg20`%uboQdh>D5DjT4_b!8bKi&^7ze)eTja#WxW^mn0Hgvw&z%rL zu(}q%gOz`MC{-1-6K+m1R|PQV!kP4d82xv&4LBN!Dbrkb#M@y*y77`yV zGt{dx3ukx6+QX#A)`@fEBV0pW354hLBgovCxNao_dLZ9cYi#8CDj!XqHzvq`s1WtK zdgD`GF@x}1#+`GxYIH?Ob5-WdDQjy)x_)qUc6Nw1YwxfR2IFAFe<4UFL`)sZQOSe_ z%5mdyw(>eZkXp~b6v-3=`1ryK#UO!MA&A*Tet}DoJU4D}iKU|9g6(9hcLchk^AyCv z6Y;tkbz+_^Y_-Dy=PD`cHf~tRr z#_^{2PnBd`Yp>hc+5N3Yl&-JJ&x+Q#>y<99hVm_=L~P8a-}1Z&VHeLgA=DBdjc z{AG^O2%Zrs^LHn~bMtSb+}{*}9}g**zjoX(ssO+AEJ*Gmv9mS?;!R*L zNc5Y#U7hiMqx!$i1{b9W!Q@SDHjPYm_}0-Fwz=-&FPlbetH7o@cH9xuhQ)JHR(UjV z3&U_Daq$oPgosM|luu+EzD;gEj~3Rx)&C-UKf{?^Fw!U~Gg_RC>EY@8rx!i#e|eF8 zpyjtq52LUDi_PlNn0^FquBiccMX!H~Vqi9NGkOP)l?4Z#+zOZ*UOFS+L3L45T~J~X zLi(JA!neY>^oh*%TeE=RAbh%~4${DGfB^@Ioh3D$sLY|=PQ)85)A+ZWRR!c|l<;4z z^&PVAE}8I@+M6SjTL$0{W>Heutj6x7mS(m6ms)~Vj^U7`k#u(zz&yPgTQPDI}4Ep z^kky71HJhHJpv|Y3i_1=*6FlsN;7X>Tpl%b9D*wC9cO1Vb^2zIS z4Ha1<=$gygS{jAA`nt?3HrM&s*3tLRKCNvc|5Wn6cnpepYBf9_zRMmc`|gT_=e$FT zmkhCT>rU!0vUK;0QNpkF^CmX=ZzT9u_+fwXfUWR@2jpheK+sDm=#y7^BD6XPFy-+S zE*t z8(Jr}0K|veoXv{^&GsP6HNWh2MI0-?(P~qc#y{38Yj3zaA_KMcXx-zf;%+cXbA4^c9@GZSF_e!)@`k- z`gZMCo`5pVe6Y+jILe_?MKj#3b4mgjw)zlffn)&A2;;>6k8aD%+>{WapPlr{obRGv z8{0r7dvht~c!GSKO$L_GU#v(l0frwvZNC-kyd62UZNm1Wg$UM^>qxnby*oe3hxqaGZf=m-LVeEoOig*l zgx+{=1op71s0ZO3@X^W~j_xAMC;sN9QJB2X`wE-7%6L0%f!!5K_H(tPTsfek%d7|@ zM>SsFN^Fk#oqb?&AWlY%BOICgIFJgaor>Ii>m_~zpZEkq?gk!&`BtUA2dLk)hiDc3 zu2Q)L=wY(|wc1-M_4w~bA?@wD*PZYAnf60!H^cTQ)M|x?i%{qW|p%J?)tFy$^IAOQ9jYum|`Bx28W0| zxzW(|T_cxw1>)W%b*tc`T)HmvYTGTDYtSKyDbwaf)79ybYKG_W^|wLRDWgi{pAWqQ zPmPU@f7K>ySvtmZiNw3&C#E;phvlR*nf}@IwGkpd*>K@NMt?ecLyFy>wAyKQJ$m+_>n{G zXGNBNRDXRs_YfRTW-Lss>uO4)BEtIp-nKkzcBg8cC-;5)3P))d zd^Y@2e=PE&c3#Fz_XBzmy||O^e|mS#?CO7__CEvV<9g236!fst2we^dpZd4-I;mZ* zl8raTqOk zQQiiw=IdG)vHixlso9x1xfPK(eS7)jq9lUX#$heZ9Js2Fk-Z@V*_|(1PM3livlYjd zHcRTF!CAw26F`hwA2pABhqJFR`7zsM$#plE#yU32W+ke2*ot+5v@*!sLS%r3qwXf?P+1#LNMZ+^w;F81TCzLk~m3GZwgJD0=tMJ@l|xh#|z+4w|zL zeuaxi#x~F^@%4xewvJ7@5{$sFAJ+&O49oF}Dzh=6<#lnHDRjqHhlz+j?=pIjJM=D6 zJnmmMIqHxqag_&Y1aSwN+9hs4=Wjj3I<=UDOP0!fd$0XdNCOX`B981gCrQ11-fN5ojC;{ z9^ItqV96u}kH->`$p_7i9@{TO)c~iZCZb+s*bs{{ew;^KYI^N2FPGr98_#;#2fGHT zA)}uzFa5pPd1>?{Cr(IiuIRO7y1hpD*=GzyZ9=>;?A08mQL$FZ5cgtF{4(2+!{(&} zeM&ezpC=MpkqfjBX*Zu;b3l5M`y7zg9$Jw%>c%mrHy(6tH%Rc_#=AEy^~rm>94#t5 zuebX#i(KgcCq*cW^8Y+*kvBV;vLL^VvI`cy_boekyk&H_no*3%zn}He_=xa~u#<$} zOfM~`Q2ojJ@tIS9ocf)Dn)o%QXnesUX|qe|!=P!#TnYlXQD@@Vo`6;kTnNp+X8|BMOMc*%lzI(yxQOnwW+@! z99BD~vdh2n=R93H`xv_VF0Y5Pi@2tM6~(-iZ(UWa>~{u9VKOmA5e-u(n=^gpAI9T8 zHMxmhE;sQfQy}QPpC!{c-t= zk5bBPNErqHrnElzG#L+ZJw}fEaetNf9jtah{@+i!F zWQy@%pF7ppH$%~B9XtR+=8a(&Y{(u;S ztcDOplV@{)&melS_69r(IrRr=6dB?*@XrhqA>6oE>HM+dxO|vCdw%nKeY1Tz(~00= z88|kpvAuk*ku%%qNHS(%`gGlQ!}NAvkh7iTjXhGmC%&rhGd!!0-Gsb|s69I`YqtF+ z^IS#n95`Gvrv`s4R{dNT_3?i7s{2QbbCRS6g=y<$y4;5fpkW%=N{ovN#y&lyyRt`>>W89wx}|ne$6sL!3$wG0-?4Ux z$+{NVj9ay;asTCwKw|?m_7`a0xA|X8een3@7$1_cqILTz^8UGpKqKRP!NGT@A}WiJ z((I?oxlj0?Q~O@?2%3c%E0cMqw(!;YCLd&9ouAO;Y&W4K0d(eajoHOXNJ{n0dvoB% z!Z$yMtlzSe^;edxJGN1^)^`pZ`;Pkv$Td|obUf_@7qX3%R=g&cmfGFa91X{aG0@Qy zZLtPoW`Re{A20hXK>Q7_7h)=2vzRr=?N2vd&*RoYFMEZ_t03O>LH4g5cB0{6mPTEN&E^Xq3 z#bUAo)=ng>!L%NT$>k0UWR7*#Qdn^lRkM8iZF+6p&*Y$4uI>dRG~ z9pxd_NyvsY#3SyybDLBUPSe4A*_nO`q8!ds5!<}NT!j6^>XsCcmCuMd#R+6^P*$w% zETGjrga3-x9{T6(F}KqkIN13H^aU;0lkZOgs1J^K}Hh%W+ev)tc zm=c#8X;?Y}z5nLZMv-eu;T@heAzlk5$>pdBHXdckt>33^gL(zOqnPe6|Hg=K+%c>_ z(KxzZK7hqbP5F@&F#sidR$sN4jBe#0%bCsJ6NWedSMF0D(Y~-8<4g3oI=du-JM}f| z@xMI|ymj$-=}Hz`B+j>Nb<{rm%{IJs*E}GcOV6r}kVRPd8eNX42+!bvjp|fGZMM-{ zz&8$AF<4BNkiyC4$F&LUB+oq=QM2ReudWIEM)eg5; zimEjV!%IQ_Mlw>kAgAQ3mQ&UFFKHPG*A3PB|CLs-)UJqOkS_NL?ybZBt=sHO1v`qf zc$QTEVTo*U*HVhaCVZGSt$evB3XqQD^=wH)1Z)+z5dUiQpmzwbw03Lve1L42<}pC? zH@i3<)kE4Er;8G~@<)cj}YO=V9tHn|Dm;gY_D9Rb$+00xEh-SFdtoXnljl9%I*d8b(;baw>C+D6!Xzs?GP-n>sRAC3#MS}G&-hphBMuIK2REnWXmJE79T3|B zxY}^}vm{-XS60}q8Y`Jm!Mt1S&tUPbp_uILEb^`ZPs#0}R0DYd-Mt$GYMf)8(5x)m zGLG_A!4Sp@V>QN(7M%56=NJ&Fr`6Gaic-Y*Qy|6L{*+gYIvB!ehV`6uZrpJv^+#Zl z^V?;gRdiz{%e!!taG7SMQ2;$`M{%woCIBP@26m}{r44?D9OR+V{)t=F9~?3ha2=dW zGDI@=Gg=mltx8j^=pw2*)=r8c78Xu`$!hb(5AE^EUJozn$0Udu(Mf>3wkccSn=v6L zyR%JZbGC>NLk7^^6Wp}JeQ9_33L#p)XhCGBkqk{On-%!_Xz{uXpJyBTFLQNk;FjbG zGt=y;*sP`z(~aHnjOR9pV$ar-7JaQM@id%-pc^tuN?oEyjZU2dwg;e+Pul+4zGvSZ zqF$l4ac-z(2X)I|{}Zu6Lgl>lC(Ys}`6H%>!A)VwsKA$7X1PxM<%A4`Up za@m(O_m(Q#lE2eClnta;u3sfXC}{Mvv)&0CFO$B@aBG$>CL2m6l-G~_B1g-mb!rQy zYAS2?DKU#RS*Cyq_Vu?pWW*@4f9Bl0>t;sgR%u(bMTO$s31<9EE(iOErZ&O~3zgFO zwjmE-(e77X+N>K*k?s}|2)*=#(G7g}#gk~C=wp#Te9xmBWm_RdTP5polAhnI^GoxY(tP~&|CUZHMm>E z?2LOT~(s({;;^sc)pgJp7{R|+_1Skeh5S5>J+_$+S24q{rfc*Lb5h5 zYb_{;0Jzhx`CQuun`H>J@F>j5tR6=dO$@lG?qLHoWF{4Cf7f-4p9jU4_cy_xkd5m* zeS>sh9ELDml^Z=jU!vrBKD5;7840(97&OhY95v!H5;daGO#@u_n*eNM)Qks_1m#2u zURdHU`Va!b_{0@`$RjK=c$%>Q0UYgA_WK>oaHI1DvZlmD6Ii4-{&t5&>xLv2bQMHl z(b*hYpxS$u2vlTIV&~v&VaE?^CYh+=csYeycPw{cmimUGKdP_2D?1Q8m(;U$LybTCX(k|kOT4L=1VDoOn zmz`F}(%vpJpT2WVkRR!6;Sxygaz@IEzbn$qj>$|`A)IpP9CjFpsWXjtp8B#WgI|4h zfm2_zkgZ~`*a0479^Ctp3%cOlyI;Ce9pWQTr>v(K{HPP2tYe!~BQ==Wd zXK&}+BK2nk?x#QC=z-HR?m2w_*|e0@-FK{d|04SxB!N`7?7KwT%ZgVJf|SR0ge5Ca zX!|SIT@VKQEHRMTQd{?pqt4eO_}MG*DZ#`eird|&?*6`ym)iB-@NMh;oO2@Y)Fk;6 zmN4t{SUDVXrr$9MKC-%vj&O8+G~z`~sorvlJXJ^SwPz~OAKGaNopS#oL zSOw-5`-XC`z5MsUi=TElJ+8&Ct`bm5PHb{Z0NW??5Z?1AT|xn-&{txI&hOne!9}IR z0R>s49bVd#1v}I9T{r)T=MoM0xV^#`t2gpDzCAnp*#6c>rx6li_j03vwUax^6T#!{3+cFR5vRp%Xo z1_cwql4=VU7CZb}M5z=-mJJyo+YAO^178Z>ZM&dZY#UJTR8d#Wq5Sv=uzdk zZ6o+8KbBO30j7zZw3uw!5U@;=D}gDZ5FY2EI=WL1BxNMbwc@3-@4J1)caSb0-7X(57&wg2-pVgYS#R$;}I;@5xpc+ybBLeeLy( zR*atCEfK$%V>W2+${H+zHj3+aV$4A+Yfd{XsIcmdv@9&2VV?{)9S$z76SszwAy;Jg%l>`J3NK&&h{{3nE-650n zUgjkpZDXg#lqYRU5aR_y{v_CfQr1)(K=^L|EzvIh@*ZC)E}=wA-_9a zo9ay>ZcVycN#|iF)F~?7l@@Rj5K!mW$WQ&2EE}Za;>)d{r^kZx>64hH3M%y((C#v15|Tg^4T^93kT65$tZ+kRfz^L$(~ z{cw%RmzJ*o6+)A;@%|6CR=yKyZFYXV31n;K_1FsB&LrPI04k!N`#E_;je%y5F;L*v z*$dg$4Q0qK>m!KRhqb#Jr=(wlm7e|rMM=Z)4eas zd8EQT2{h0!)l~><;V-wIEyh_qUS zopU)MT>}p=M7lUTC&-~6qEItwlv~=&V_N*Ty`QHw4HAXZX@#ttln0wFWX;Fn5*uFoJ&40Uo#)ajES*w#U zm9YN!ew16Gi}n%H*XJY@qDY8e6J=OjLxWPeH>_r>WUv*HBY@r$G(h*R^S#9uFytrY zE7T<#xraR`mkcP1l^A;)oYoIm6pzr7d3gv1v_YqO)4y&U4Ac0Zr2*R#5qp)|>b17-o#_g2)+(|qkmH};S)y=1rDiX z0L$03*~$(N{1}zfAAh&m?$$|wr2xxg!PU{WMV1+7)lAU|pTzL=tfRVC_Onx?mjEtN z*^_=YEf)nv5PR}^wv6d;9~~3aRppSouqOMt*sY{Zqd$c z(PEOZl^}@>dct&iTJYwZI~LEG==teR{Oyac@;*8fEA=o7sGySF=71dSGuvnv>ze1i zSVOcM1daibWi?SS?`VOu)g6=7Pt7{rjgXs99#L9^=8is(y_WwWcBg$fQ9p}$4v^*H zL_c9*h$EG8TwtY1JkW$IZxx~1#w0bfNn{Tqus6s_8q|o>1%W_`HY>T07+Q#S1SdQU z+Kz69Z_o-fc*Ry1r)Fq=4)j09#gc*dBiSNC85FQeVoMEVg66%O=Kr3K3Q4?VeV9X` z7Fl(Tueg(1JoDS7M=SrP;o}`qIW+Lt%H?>e&7|(@r-wp<#^=MOd$QWW80}1@G1yX) z^$xZDde(JEh7<~)>ME$Zx}sfj?Uz_ogbZr#Uxni~t(%ajua|02&K$>3!@+UT?{(F> zZA;RoNXGTNICfU%^gX3kB8_cdg5>f4gObL3ez7|dmZeUo$kxX5qo@917Q&h(Eqd?^ zUP5Pkxy0{~?T*Yk{1fcc-WHc2;&9@?;8NMi^!l-drqsTRu`9Z7*go+d*Rh!@uDYU; z+qeEM8o@4_|CH+ai6^r)?Cn90)@izh3txF8on^x&=OQOccE|BJR>P&BTHB<)H(VJo zSGSf?QbK~2fh0j3q0BglHQf=^X5i-yKk<1w*rlSaACspKep%QIf8d+EmK}VgE8ahA zx0b1hIuogwbG5vQaE&Z|<}NX{GqwR&{#d!j_YS{_l-Et?%SpAPj+;sJQKJ7mG$=h+ z?I^AScRy@lI&jt-PDVq&S#%~ zZ7+4^99oA{2Myn@VPIYfj$OB)c+9~zrg~!rE~b`jTNNyd@g>8ent2k~=EA8ZH(Wf@ z(`pK*X(UrfQMfpp=&qHDBh8>{6{zIAG0pM$av7uTvKqXl^RDW1<6F{eGL>$L33=K6aGsM)qFO4WrLCPRWP!P`eJNGvQ2@&A5RPB(5{OhiO}P6D!9! z9Rs}f_t%k-^O$3pw9Fh$Au#}XAcL7kj|`5iEXt*+ zQN?*gkjqNZvWbYv67EwOse+w@BSro`(}Nz)=lux^smC)R zoG|)#Y6Gd5;$pm2V0n=0WVM*?1k~#DT<^kMZ(IVf)mQ)Ly*OO3pGV9smY8s$D`%6N zXPA45M8|F4(f&mJzqRncpf$xVs-Dn++##i`WNFVK4<7jm%bvVx z|LIE+&4nX@a@SJEjy}t0f0*|ao$S<6*Oey!P<8d;VdhrwY4#^0#GVh579T#T1{6v% z=u|Y?z-C}g+w(ffor&8??wt#xGjfGgHImpAVzD27e2^qDV&iwebD7l7W8Bl*!MywP z!#jGr0Q1!-i}MU$p`d*I&hyMUhxqNzfRiQxp&+n7cmAhMqt;5SrFxNSkzVqTJwGm` zHoY$044Ad`v?Z6jaWN4X5iA`hHPMQK7{zE|=HP(YtxN&1NC>cQQfQ-q@^nsyPv#C;YH&A~ z;esu|s!FFj^{H$XP|mHFPAxEEW;v(U`jQD7V9!1Jx$kZ#!ZqM?XrHk_6<#FK$a!jq zfw%(;Ta|SOiygDG*(IA``Pz#FlIVoZ%Ez&Yaq9c=S>?;!1;~eMvlpTF@`Gb3Dr^IA zG6CO=3ZQYVvLfoWF zK-U_9G9s+{BoRzA$G1+${{{T*x>~M4dt7O~Z?A+L1*jE@cJxOKQ91SZgtSnq&*jm(Es5H3zv&L46X}szU$5J=2AsGybT;!W(!g33Gr64#`fRuK-vtb z{&l&oi?umif;f*0+AaIL^syUwY-MScHe!kd%Sid;*SR)w{TDYc>Ub-oj!6vH{ zG;fmDvW?SW-Gm552N!CivdxuoEGp20AHe|BM=y{@Nr)e)BdF-`E15jbF|PJWl^K*F z)zouFs=2fzGs#u2&6!LSFvL0KhxmJKaih?@r4En;ggFywv@kXzNEvL5I&iWx^8^VV zP&il{xr2mchmz~sAyA^7bsG>qA$q2n5;*J=8hU({C%d2-bJZb{vgj@DhZX!9<6CV$ zZ|!=@DbaJP#Vycr)u_k=(#P#XLU9x+qe{r9Qb&#lvyT-`N)i+4#0R95+CTmg#-dqz z_(xc@_nWm0hp%}c+Q~#-mmfR2=j5_|Soe7owuMt8$-)};-?|j2snq*d$N-prB)+a1 zKn8};imVz^mI*jSN0ANLE3!jb=qef(7x^B0aLg8WGJy?P6x7T-IcD;qH!NhrzJa0- zAbhipQ08C&=@t}D?63*Xex851@vNwoSO$DpP&{SCxYFA3q7t@8k5c4o5&a31YorcR zV_9_4TV_L1>R-tDliWj_6Hdm(g5kvWoJ zwP2j&6sadOvaU&eceg!v#_!4h1>pC_XqcBLKuhj6N8aK!)|@i>t`lpps2Of9xzF5>*0jNkxh4F0v8-P> zT^(WtYr<_?qFU21-0I`wkB_RGHM1pVM2hz)FCNHOUj1KiH@mBUSC8}Ai0bdYY~F1NUIXmS5M{Ia5*qWqU@1m=7f z^;dygOBRJ$Phcis@gT~sm`|9lR>m+4b%^qXAi=~3 zuUv;kUw?2lKB3N)aR5`)J3Z1c^)#g0BP5&0m@SjJ>vHYOH9Bl5%Nk2rMOkselarT0 z2`!=LX$b(>|I>+7sRqLwu3po;b>6)7S+AxpGrDv^Tr&@U&&$h;iyOSv!p@yV z16G3+t|CiHpCwO$=C69MDp{cfBX8Z@D8woZ$XYbi=w~-~T@CJP)&l~U4jfsFZdIKW zOnZghPh@Za!Cm5BWnl>&kL0KLuw%0 z_Jp~fGPQQwa2BX4wNMA?7)pSXfz7^4FjYjEo%)$?F{+GLHlnwb9rdg~BM#gcX~P}~ zZ|>mEmmd`s5J-!IGx7CXP));fChXXYQQY9>o7jBGU_%F?(NEDJ$}6`f1ruh4a+;42 zO+&NSLlM)M$Zvw=%e@_0W*&0E=1=2%#ES#9_f3by&(J*9^VbZM?-K3P8RW!05w2K`0@Fie5DwAKlQZ=ve?*(Z>!uAoj528x{@-)kq z%n;1-$8S?#Rz?4mRT+nQ)$-!hpXh!ptV}b&IaAfD$-Q`FX&D-;;{NaA4n-|vW5zpt zUDucL*QFL;q)X6CR)%b>Cv|hH!0&_mERDAac4M9*(J#-CyTeJce~#QMGaJ$jy7?L^ zLfzey=bKM$zx2_hA^OUSN0fE+jShu>mACLelrkn{TORvnI7+5JNa3JJiiV3n7@Pd){n$hOWKStdb9 zfML=>Y9qhzs3;$wF#eaNc2|`Ja?SBY&oOdxeQ(OE5v#p#+FhbhlZuhB{@#9YAlf|f zs{b<;xu05Yo+CK9-VpcfXI)^`ME`{fa_I9duYpl+ONwuUko(r)$84_)b zQi!_w-rVmaJ88%6gEV9&+U%QG#BB;(0Z>*;i;`YIMp_uS#>JHt3sJrxWRM(eRuQM9 zWqt0OFy%HnKcuseWYuCK>*)G=R8Ba*?uMcMdDNuRlwJFPp8>;Nmq(fdmm2$v=i_Qg zwmEgvkNmH|+>-eznEaX1>1VIocA-Y~&gl6w%KAn{s!Ec%0E1|kJ5`jC4N6RsJuvDS z6oA{MxZB`UOF4Mo{o2cVwJ3UzxYk+Twqy`^wDL`)X8A6Ll-$%9GI>)=uq3?3BGS?|v{}(uh~+iB3c{T>V}iBMq6LKu_2i z>V|ZqRd!m^*WPEZ)C0Alwu{gnOH>#~LLbKZRHUtwmTRN8aH@JAUdxIEAC=3kK+wKp zp5~T{M)Gc|h=@Eicd&iDh6BbQGFCiT)ZcT=y=fP*z67dKb*1lGMPV0s#9JP*%`2M}$bZr*%k0`yKLypQ%cqH-!1KcM@01Zt@m<0W_fEDXA1g!=6D|J%*Li< zMI5K&&7*dsBB>oxKa|~^B9){bScXP&mSL>V{iV(ANg!-F%tMs%$-~49w_#y27R6-3 zQ2Sq@W*~k>2nneKsh)Q7Rb$7OK?1g4);g0XGXo7Sq$FFbnS4*%nyG$&2_8x_bDV+r z!xf0yo6|gqHKBXii64yGV7V=`lU+m+I5C4(XGaBjyS2$C$Zi16x(O3P_r4LM?(?#S z#9n*Oy28NphJ<(K8|2S8CbFda*;7gy=E=XqxHAXaYG_&LfIG}KGd*i(lsmvB15UqR z&G}kMj)Kw^n4-=rbe!CMcYC={YUCWznZTrO*&N)Wkl~t4qEfsp*~McDfmb3k)}8P| zBS{m(VJYAqc7W?#3s!H)2#^TV($Phl*RzFm1Pz^S)z8QPAAJ&?sVM|Uvpuibb|h2t z(=m^(o8#0b_7u=5F=}#t>)y|Bu7>$NAi8!A$E@zMXakehCF&=uim2$T-iypi6D}YZ zGTZ#d%5b|9bLyt$;q&3nf_+`u4@4}i5$}ure59howMSLEYAIJvrT#(}3%Eff$Z)9C zE1EKRX6M9A3RXB`8NTfoTo@PwNuWlo%wLhuMl4$6+I7pDq9yZ=25GX?wK2VqPcBI9 zN}QemHJupRAC}vkB~xLti=ju&&$nJelEc&a@*^>uI5;Hye1AO6XJcq}I=b7(FJ#nB zI~G}M^N8HC`5XgJ9?Y8=#dD0avRM819!1G$FERc1cy$0Eg4LqNgwr@0Qcmpo`&m)H zxc^#gU4-xl6CL|PZ6|A<$3^!R@n1Q44RNKOkIPK8s)9P4ZuCDyTZi&L-@}h(Tt4LW zP$7dm&XPbt$F>zV#{Y_3xjwZbl_r~$l4o{*-_R1BrOXevUO_1Kx~ZHSpexVYnPlew z^!5y!`pjbyQTAApD5gx2xa}hz1vNbO3Fa+GImg@iy%OUZ+x~5;&b95~5@`=QA@5SX zUuXPd{--InEYL&<29v2FS{C72mw$7?d@qDpTQ%GpPo;Z4X+yA2cIR85#)=cHL-%1v z-68|*Swer0DJ+-g1L{atu)eg}h|OE{;o(d4dG`$bh9pd#f^Z)2XGKSlQd;i+HjfY0 z?+5A$KV#O6J(=u?>?VhhAy$RRbwz3;lV*)5NE8f&A3Jy6@Lmx&=?DvCVSqLK@7|hp zn5Rbt3Bb}iMCjh$PQ&?|Y_KT09o4#U1DjmcFMp;jebkBRg^bt;&OLQUbVf~M~YbNL5@L%xXx5MzE0?O z>V&;z9GL%f*R#brPNuJ}1)&D^m;5RkQ31w3SUv^|97ju>m&d9!kWRqtw|i-$s`WCE9N@1D#vX)BN#BJ#E=H26K%m%DOl zWd^Itx~{DEwGlxt%dQoh;;;1x*MeBt&3#>`!&OF+6_MhDAPJ`P^p;r({ot%TYuE7p z5bed}m`IT;Uhzmbc@2p-5Q^y46`txsB7`tldgQ*v(Gy<_A?WY@C=`~2M81G@fST|w^Ri;gbK z1iwH+jxJgUzgz-jCl4T+Q&?>9{N7Rmt|G60)htguli!1Nc1`d>FD7iSe++$MrbPWk zGmcEY7CUJV!m)hpH0k#_nL+Y-E_>3}$MH)uT>Y!9DYzRJ?01_UYOS?z|EH%4#M`Q# z(k&|T@?pdzj(whsd79%h#+mWLCsxE89z31_dYdWa|60)Q1SBY@s0f3{U3*!c4e>9~V&b!GwkGE((w4hVuc8lnlB5OmuT^_Bq z<-eur^KRSISnK?Zai>ngQGf7z0Jz$F^;)^JBZi$Um$&zuINNt}B$hQsRIjXiXYZEA9+~iAJjnhmg?40z@ z{}u4#;CHbem2;!Q4|*HRq9x7!qq$A!C@0dZu9Uqrjw z_e0DIzK@)|+tNzv81?^k9f!=8@CN7j;m|`R5IEmFr>aH=q#325Pg@a zOzEszArX2FK7RYheTj_IpG z{QTW~M3p^M|EJqrZcNoR-U@^K?AEUtn1#gb=d@oBm*zeeakL4(`YsQowjorYBS){L z$blbeQm02cvLBO;h01r&2Ii#@1#B~)en#CO0WoG5OAaZUp2n*x3+BfByedXUB*N%)&8IHYP|8d`N~n4GEkUPF=3NCahxg(_ z_%ync-9pysn$inrO>~*6cO!Hz@6#z*A4?EDynHfUci@?-!am_&qJbk;Ide;@iC&n` zB=^a$)JnsH+8~YM`Zl@Mr=4BhW6p+N(W97>r;u?L1N~O;B=x1GrO|K29ZBVDcbw%l z@mb17CK;z|%XBk!$^jRuFn6EKi^wHj=QwUIbHOh)!zl^HZuKWlc*3N3nJ^j~_B`)` zxqK_=)+I7#Dk`%DB1W9L8-OY9Yqt8Dr7sy7sZfuXEWdi0@Z+j*ho{u~x9P2KCESkn zYO&&Zx}g3}_P}!4EdzEXW%=w`Hi2_u;b8R?^^^&5^A_n|*5^1qeTy1Z3%U7EZgcW% zEbW~HMNe<%Nlreb2^R%17+ljIXo6<1L%k}r=Q zB5vQEYE|8cf8QR~4X*|@y8{PU>zUhXCysRE=lx2ZV_n$C>Xb*LMp_I#LN@Ljcu(gg z*CD6+ch%mDok#1gXYKB5|M#-{0zD+sXkrVRa{Bu{VR5Q#*b<;zOFxag zQJXd45n?U>N1E~rMTmYV9Cj60hWFPgp|K=|G>tQ8+dAsNUY68bu*&kwu|st+bq0eG zt9&q2V=j;M`l|hq!fm6@K$V<@BX8#A)*fA_CTMIAq22ZGJ-lE>Jb>?IpK$%P4}V{T zC2p*XNF*u z*}B}IXo>FfcuDLLaHr9*7&UcLIK+5&$R`jj^C$gU3C{m{(ng5uD>P}%BbfM1zoc9m zF3}ZzvJvxKMb6gPKDsOh*^Dh~EPisw(@TLuyc4O@=jn*`@`x;=_!%_HN94>33$wz_N;p$$FT8r($5b*@F5JEj22^6$&pr*SAzqGO4^E!4v%!ZT5`S52%Rw;fkX%n zRf{(JU_@-$c^Yr~Y!fA^<|Z#4bW1u*+Xk3q0dzH_L5GP)Kv3{t|7QVLhrzoSWG6Dc zqQEY17P-5y!D`%s6WqSan5IzQ)#caH$M9RD;Nh+6rC`spN#|boEO+t9iYG&>>hiV8 z3os8Hbr3|uxVFHFQ`dts%CLQfsroj^e#`#bQ~F&{^yc1|An?ESR+j1b0np;bQr7-T zu;{5?x9kP2$Y|y3+TzHS)2-i2$}3^_&4lO{tnVYg-fHGh^+g;Ds@<7w3gKb$@b1sE zZx|jc4};uJS<0q6CJMk$^hqb@MwPuj@cMNCPGi9kH44a$Fr=+m8Q9V&+ z-w__Yq_VI1^v&EQ;MLQvOI}Ct+k9Uh^?&%R+<#bM=!2b{t2U+4B?XI?H&H;<1L6$g zy73^G6~)mi<`UP9_nO^rw9kioW~Y`NX_6UgYy%Q1>TR4w(4SeDxNK>-jDPdUH+Lj8Do0% zquL|~$3T86=*v6vudjc-Yf{iEZoce+OZ4r4md{)5zHBo9Ymdu2Bc8ppxh))~3H%A} zILC}}q-4IUw{Z<-Ia572Lh6~eDW-)enkg^DjS^TMaj2=KA@F835(aDF*^ySjM(6c6 z$09D?D+ICKZYFJDLW*`-Zu5kT_B>eHBc>|aVbJVv>wG0Yu>_b)-kGPVJ3e}#%8T=; zloiL3a}qbq_@O^5*3G6FBN8<9X&!22!E2v$D-3TBI&WyWCCEsf@SKrRr6fXed;|Z| z%YYg$t^D>&5NJFcMMe#MwlL4C%L)|33|yJJQ9!FPr--8JxCfyRJo@wYsbJvi6r@%gTtoX9J{BPKC?cnbi%v%d^p%V$tf^B*go1y{35)4l@AxV zzYpara|^fEZLKO&U->uOIDBYnnJ`IKT4a`$<9P|fRr__&KJK}`xH#NGMJ#F}I^oS* z!|C($ucxpT627&KhqXx!$-ZNQnv&ybc`m-!qxAH`PFvlt>>eXgn4)$HOe@cw>jwi> zP6h*arV_I`Kt|Z@{A=C2`wU{1z!4&JP`E!JP7^R@x6@qC?Xz7sE1AMbBFcQb3R@J- z1ul)f+KqHbKIssk@QUMIkD6zHW?G zUdxhrutNcj0&0r#1-8nS0&O zCCEm7VB5%~GJ{cb&x(%VY5Vx|8U{{s`LSu$+F*d=fzIon#n*TktumHQd?nzQOQueB zu)mfW;BXY1yAz|J9PHk@Y6+lOR;|F&*D9XD=EqlbP`%aKS0~LnYNi5vBu5>lmB2N$Jv-e=4`yi=L)QotJgvoj|w_i z_KQuKssg$Su)|A(z_2#>=H_fFF^Y5b3!w6V>J8r!xtv8_gp8{4)yL1Wvt z?PMnSau;`VpKo=ZvpK7?c;6qV9gU>wd%h&xwZ;_xY2L&ozv zF-MY+pwZcW5Z9#AJzamsjOd3D%>Xy#5R3JJl4}zq`+Y+2og*83e)YN+*x+}GQKD09 zXXK~H8LoCUXo(T{Shw;Mfh7>%3<1)?(;{V{DF3^GNz&JuBv$TEVwLC#bL((<97>r( z@=gF8NF}(>SSv`~DB5;Io$m{dk=S+T12W}CkP9C@Qd^x%T+>>HCcvygLROrZ@9qbk z7LV!VGzy$~;mSX_j4@3aw?$Q)=rTjzCgATaZ>ibggiAP&S4+XX&|fGuTi<6?85Y8> zu6A_n^~?|A%Zca`uiJcr+3Fb^8CLP}W1`Ex>d3O>-CQ&IpUFUV_+1i)wGXt8dg#zI z**RLv0r_Ex{MNtvnyT7HRUqjU!7!?2q%bAnIefRR4&lUg16?kEBut7fN>}k^{E{%=GEQbhM_g+Nb#30s7)k>pd={DwcP0~aOS`)A3OEbVG zRV-c6nx(m!86gnR;L@d?1@D1LeAYZQIaSDxIJgXNs~0zx**W#$$#vEKS~kkxiHEh-9(FB@jc9iF;XrX!p#hb z(5lI+{*0}{EHefEht{)tY(u8Uq_U0`Bz@#kPz7A}6L>J#zzC?Z$_apkX{sRS5nlKi zBN_oZ(1Y7LNlMUh^rcnf$1SVK*LzW|M1sDW1nG9sGDmHAq^aR_|JOJvw=@@9TNzc-IwsygnD-F3e~y@6!Rde#{FwE&K~EoHa_4>d&5ze5 zN`NAf$@1>?@yb7o3HcNDt9gIvJ{=i0i3N?Muq;p)~%S2TYb2m6Z=K~hPGknY0^1yRXNf8y! zY^k0U^4rD8*^m9ClHqb~9ikKjZ(wS=Ah1x#1t_ujpg0%2nb3yg`Rl8ou$aHg-IE8d zj?lz!<5X?0t73%eEVvh!#3+-akj=KnNk8M1JgdAaMhjE6I$;kD!=W6X{kidw>bO`0 zCcoR^lD2w>FljNGp9Wv%Q2^LC zYyO^+wA`Gtp)1(g<8LT1{lDq~>g0nh5(4htrq9%dHnWM9ogW1D`rBz3yN2Wqu&hWBJ*-e00MK8IsI0YGPP%EHM|&X z0D$u@P10TF6CRu3#X}*^E(*?^7??m;Gl}?8@@r$W+cANOkCyo%X6EPn-cr{@Ffe^- z=^$^)?&v)KTHV6%uW93Wf8zO?ohiHlsKbB;uV8CSg0BWYgd9C};zIQfp z4i}Fr4VIWljcKDMQfH2M13dteNEVKj=pOPT_5^xknY5AeKl1)fqJ6vhAdTpn(bhjl z^WRaU!;0&ASLGb-B5eVFkM~B{3%WBVCK7FJ=oA;|dmTkhJ8Z5-$`dV7ZAclkrQ4^+ zefACUst{GKCP^l-o2Kx&?Me+AazsnDPXD%vp#UjM-foNn&0$5hKX$ij);8xg*Xs%& zY%g|Ru@6mS%WGz;x@}3MO+`HE5XE_?|K-@1gI-g)6;ecA|FUq{KHq!xlJAPb96Yhl0!*hVgXveN6j>Uk7Q$La z4k5*i?`=`H=Jh7gCtsp*>$^|00NpivaMrd|Ky7Ubg#IEJ{2ZLlK5N5< zG%kCzuKxch$L7P=*F6}V0Y@rN^#sV<04s3 zE0rQJ5L5xMBh!@CNU2kp3@9_=k&-un5)}(3%6jsV1EktjmyF|83qYeL?K&-rav*Ek zbsPV@fLE)yNGHwTUPWrrChej!*z6@Bhb-*ehKh{|Q6X!jdIeE|P|ynL z40*JCXha@O^SfNtcqW+*YgOneS|#@XoAW+k za1`=!@Bj6>$9wA_C`62&GcbPK*CU}|j{n|m1W)oH0%=lu{{ytuDGbmy_fdTA(XE~p z^!*RhW$Iv_O%SKA<_9Z|hTZhp@lbAp9$mqU|WHiofCM_t4^OOJj_HAyi`XpPAxZlxMBo z+DBE+8j3=uHg3En{<^unNAIVgC}Ph)K-K&~F2*jo&2K26U%kZ)Z#J0Nma3+)v6;k$ zwG1oSSN4pK<{n5Iv7*sS_Vo1_u}(JHtbP~fOj{loX2ia~Soy%fZ~{8Cal4hJow-;= zNvZvKM&OYbESWJuS3rVE@YqVP&Pgp<*=uxi}qY1SZ4irMhqDhi!niZmyK4 z9+3i3lRbip16eKGnMxGP&$^jIj9qNR}o zZvc5j+10C2wMjU`?db=ki}f3aOGn?iv71Z0gQ!Q9bb@r3u%%c%r-YzeRE0Ic^F_VF zi#k$t1hqnI==Xty{wOCVTgtwfNg7g$of5=5UP9cflJfLC?o4w|e}}AAAy?6Jg8ovR z?5)lwX+Wn8#A;{Fa60xd=V!yZY*r6*PKmA_zhjGpFC*z|9{dnJIQ%RPtL>(O2Eye$}fMo;n$qNAT%MiqR~# zq+MW@ZZ(mt@7J-RI+^JMVg-F2pmXG-H?DCllOJ?DDWg^V&i&sFmRmg{RYmrt!ls6v z1T**YzAOZ~4U2JPn44&rF8pm6)mOIE!M)yXc2O#m2|XKK+BDl=hPv~HZehz1KRzjQ zeBobd+4;ILUbFG5>m87(F>sqP^lYp0^arhUK<{F?7ynJ!0)#(=kQ;M+JR*8qPUEbv8!D({)JJ+rdG zm-`nD?e`X$B-m=7Z&yF#(hd&jak%n5bn7P{7QVvjuVj=kM8G|sBT?uztof^UIBS z6^S=+2l_EFcD+aLGh_9`E$Hm=oS|N-$+T$}5;rpc^!B&USyoLx%QBC4B>c{YG&Mvn z`cC*~N0?RM#5OM%&z3O@*_+$Ns0jwDH2!;(=gk-cMYRX}1HAbw!{YCoD{ka=V2mWABGC}c`DdVAY{CA;=G64$x1)ubDm?Me9r-EM3=xZb-F z;eQK>E{tz&2i=f8)^yI38&{cBjx|7_v(A%P2~QN|d`^+ElY>hpg5W%_`;DTIIWFyX z(A_!JXDZMe6Fr2q+0G|sgBt+0FddY zp7K1HxSG3gyt(Dz5TX1UuAEl*n{KrUww!%tWnsKWIGahHXs=;vJ7)ab%qMz<-|z_ca(=$Vpi-si`@eg z#aYutOXfB%V!&u;9~W(LZVBJ2eorQknn`0h?$airV@HNWIQc}pKy!xe*$ruA$xAfc zwDA7pEG+{g2K59?7(C;!zjxL4^{T z#e>F}ou~2{i;0F*qp1=e?fSMen@7Fz2}j_eA+6EDHa8s@AA6kuCt0KyLE?=A4OgmZ zjU?sE?i`$IgZ&cmfs=Re3KyQ&v!k1u#(W?d8+r4i9EfW|sgb3$@X*xQB^GSBu4OnVn%xCDi|MCKPeQ=*X9FHQx(Y8*Pe{>@=#PEvd`R(%@ z_=^%wVZvc2XzX>I>4Ohy#w&5Ti65HwUjuMh$qGIGDWZAwyw!VBX02C@FFWN&yQSGMNt@-WMD zTws>$?xy8*k1E$tPy1?I`z>v)^G_Ll&X#WLz;tWooQXM3UOx8PpLvuE`s$qmQL&Q< zsjmOk0=TMe##GN*9$S{B8B9(BesVZJEZ+}VT{6t9(23h}W*c8DLc=bE@E$Gh*`O*$ z{c+TuOTs(h{iQi#Ex{lWrtw^W#^UhaS%C?6V8hSOFG8DRW-I^f^x9=qZD(^UGsOr0 zYhOOoV|w&qsXz4zoVSq|QIsjfSuf>Q1J7>zI1H5$LrdZunLXYl-Or{iUEa)vvt_O` z@zc%i`5(dFU%lT*RpLPoo&WZg;QxcT$}cW*mXQiY%)X;6il(QROQB3$7RtlsS|OZecD0%6GCJ# z$I5PK;jeM8vf_mp)D*Oz2^=w)W@G*__z>2zRkU@{Nid(ux6!k%!;8DHT$<$(x#5!D z#qt6YwCvZ#c`2`QaoTxho$TAiY?Fvg)#k^Y*G#z#2rF8ECi_bIHQt&4vyqq;0otH! z(c`i0bI&>{YFPHZW0!Wy_6dPH3<$i=#z#w-GJyxvR7OV6vhfeM%4I{|n`AU&kmnNO z*$l~t5Y}LjNHLC>s#qcYQpGO4LtMQn)G4yKP5Ecz3=DQ?a>2@;hsk8mx}*xLQgwQq z=qT_8kAjwa!HYwO$;b1G72;RHA)iU^$MQvz%C2ol+>P`XSH?7mozTfB=|O(Q>Aix8 z6)k^Y30p;WF-0@XDdGBQJWitT?sEi$Yw8K&%jmUc@7>4R^xHZ|&Q-{CGca23_6jVh z6Afj^EM^E}>k1Duh#zF@I&gbRO#?O)$;=&CN~MK21>F-o#$H;>nP?0xtl2x#cGzAU_Oo2ZCtG>s1qd_a0iD_wr$o<@q1UYjV1U zt3G|o>^k{IKxKspLZ9<~Ou5r_ZFIQG&Z?A9%ApZ~l}v&--%~8l5`oyTfEKY5IZIGn zjLCvW9j!!(DZNoWu#}+R+`sjT8gl2fb(hBhIA?ToA(`-P*_&MrFGcvfyz2^mDc_9K zZzl;zFKl@_aR`@91rp><(^;I&<#0!{G!F5$6qyzqyZ<*Pj;n;E7JhUC@S(3P(+ca` z*vBxYii(MXxRBu4ohK{TJD zF?=4*_vUvC-T5hibtl*2aI{Q^ppux}>8&jBhU2<&01LYeOuLo(E74)xB5`EIhq`E) zsIlYOqp=kCI@(>$)3M=xF6GtV${AI^+SS0Z@5r;Kkp2F3&(_hEPs^y-z+ zDkmz_av()Ve-Z~;HNBo}uO+{Dm=?(Yt2wHSkax42pMJoePBv?h=|LbHbaPTO%6}IB zllA*`?f4>3bJK#MGBei_Hdpx+0EO`^Nzn@nDo z$+TQv(GCHTasQjoGxIl->~}}cl24QbJ1ieI)KB=4^5zoVG3fj61hSxh8$WcCh@ZD` zDs|n?TRH2=NQ#}ne}yedHs9}-tS3Ox&f}Dbut7WaKyr@wi#co201kX)eD+H}{fdRl zMFijdX$s>y(1-0jek-Bzamtuk{mc5s&CdfTysBc?CwH&Tce{x_3D*n~j@eIQMf0K= z6jYxvLAd}-(v}PE*%HtQ5&lxJ>9C}=aQ4uu$A|S3qc1 zAf!OU&|8}-J9F~DTqHh~*xo_EmAQAz!?K7t002ZMUAQ{RkE>$@Zla8i!fznhvKq#5 zJUzMJJ>EQYJKGDi`*HfEZVIrtF!?#e<)-Vj)YpY{6`A$qV*w~Uem^{SYqQs}SklpD5op%#T@3taC9AAu&N zImAg{8azLjIw_YAu`7H7MoNO#ZPqeN?4oiP@7Cd$UD|nM!Ct6+=P-$5mBtH;sb9C;iSS10MTB z8XS$yq(h1YU?>$T@ot++EVP4wbJl|cT3RiTH%8?)> zmk3f$WRS;4ku~3SDz06$J|c-9#21Mo&vz)$4P14*p~<4Kv`wwW;Z>wLoOfc|S7oXo zkZfL{Rh*sR3<;6Q-UHQi4%dkoV^-DFuyHUnwzV|`uB;6eycyr8u=418Ky;4ZoG2-I ziN$sFk7gNIpc;#bdE#1{f>S;wLR84$$E8(MSZi;S@6KD|Y zYaKLq;5yJ{3cfNtIX1e9VK+HGMN(w5{4B4#V-bnF2Sz2O`xvCu?5LPvIW6K+$WfvC z>a+GnZy+P=VCF!|a$vUCww__16o?HS?U7GkC51v zx$v|4WEIFQLdl#$MWpk|rJHTq=)EaYG(ex>(w%1;^-JnfbUIqe$?=xyX?^UTUS3`R zt^m72@yXVIo&P3yuKHe(CDoMd{c6j>Pr*7kpOxp`2~#iG1z28=ZcPmY>UZB6#EtX* z!!DT?akk~paAs4jH?B;{V#Fq?bb9T@g6h4&kr_BwtQyZ=VCrMg@ms!w9#=lJ?KhTqKYxSAMbM7xmRBs$}{ zKaN8=cb`K<2_oflus{9cO6&-I>MfzEvu^&}p&Bw?&G?IaAUyInWL;A`kFmnSSmOEF zS@4af+>XxL*%)()ofBQDLHRkk$VK@!^t7Y zQ|BP$r4P1R4ChCm7q7Kmbp(l(2xRJ)RCHl~gP0yX;%`_CNhO8&<6B!(7mIyhx?ru! znGtVbui0Vnm)S&68ZkXa8cpW3jw@yiS098E5Eil?Cy;^&MMV0G_p>&^*Vw*O{3Ctd zyT431u(AaKG-Rn%`~$u6nm zv{0t^yKbpJXu)OMZqqXi1i7)~*Oqsp2Uy>WzG`+*-j~~l>z|2hpqOy|>FdH-KWIA= z>zx7;NL;Dy*7}5HMF%O?qh`@tc?2 zO3ej`^-On|JEzK&Ui{sAhf$L)rg_DPPu${{7mlN{j=%VReY0#H*r82Cd|r3RjVJw9 zkVGsJQ+T@O*x?xr!myvNG#^t(px7-R5>puv162IP!AakgFxa#!ai@;rvaY ziA+j1JL$&mI0H{9kxad<%D!c{gw}z77^hU9j&JbYy&MfBZfVHUDQ~TT_y#Q0^WX~L zq+tEGJ`-M2YQ#W|ds+|r*ZmPxHIzC$bavy_L0t2HkM1il$tQ=S(S75yP~INA$?G$$ zD1xjG+NLF~V;xd$KBr5#n!Y?QcGVsIFF#Cek^||lbsW7Y_+LnMqF4!9tiP3K=<4s# z(oqNGd!%aznf&PR1!OO#*#B17p#Ck*PDVehY0e)bCrpTU7?)V8K7X^12@=%WYx;Oa zfHHuLaXFTw&n+ZTFcNvk6Ii4B0w1($*_-3_5+v;h)ba|B&D&}9-=n=LPkrMrPU~CP zK>RX2^l^V4qH*0mL;dARDHBIiqvVmwZP+zpOonk@)@wyA2HW2fVJYIhKPKq~mwb~U z6yXtacCvd~)VD7NFIrHWh!Zol33;;=XloA=GL09~nA0oA`#@?*W`?*@j35ii=Bt|$ z7MgA^D`kZy;NA(_Z>g6b^@FFp&QV#3e@2GO>{gr5vF48>Ur%12jMO%7ChDFQ_@1ba z$kWr@Po}LAa1qh7%GB8{AJFw!j-xG`z$81kO=^wW46Eh%FL~l7i7(t3X;d}RfuHQn z0WhR|l%A1^No5pggpRO4sDvs>N=5x*xXFuXLHdN6?0IkR7kg=G(Wor6<^|^~!dJNe zVuxax;{^py3pDA4i@ex(`%GDPYS&nB_tWiKGLn;%3mi!~E3jjSMA#W2$+DDXcV)Lf zt3p-Df2+F+5=^(TM7g86O6^}>!nG{tto7R_NN$bxk^%MP34ur=S~})TR6hrdg2H}J z4TtUV3f}lY2?(wY&!tfG-5eW6E+ipC`D~hFlPUR>XB5-ukP?h3GK@8KKgasVF}=Mn z{VA7kQ{eZGGmb@!l*vBZ*aGv*qrx*RM6>VJfTt^zSM2pcqaWaP8Ql#u{j3S`n~bdE zTc;YTL4~_b+a9I2F6+>4hd1b}%Hh(uY==M}dlJ2J6%$s!e@`FhOw14Ad^cI;JMeeZ z1}{N^FuDFV0a_tqm9~v@mh^M8^c@d_|=Evuk!WFD@#8n9cFKP(1 zy{+vHqx%^ow}Nkzf^a{&>ca)}ax0}1BVieER^dt)5s=rrIfBR-x);<(b#?Si43Paf zMY(XjAI$80UDP(Wj(eWgX(&r53ih@Tl~gz0W18;K%8g^8p2zt=)C`#6viTp9iA_^nqzc5U%}3+irE!U zX=QEVCQ&bvH$emjdxwXbUP%DtohsI0%bhsjyz=VFl;^P`(7rW!*9v#Ms6fhk0wt3L@l(Ag3BY0 z7Pc`O*>Or}18=s+p-=OhF)ack^Ke|6by6>SEk)6b*~HKhRQjGb&f}F*FLsrsMn0v{ zsGI*ii`pv?ga^S?ejo2yQjTo`1;U4XIjiF`WY(d^5iKDMRp;SgWDXs+i(inqn=fPB z_)0eP$Gn`7noTHYB93MMc}1a{{)*i-MF+_IXlO#k!~RU)TXOOBjecFzP-Z8B)Czgf zDP~ch2JfsM;(4{|ZgrY8-ZW`ixJcFA*}yf`uz4q2DQ$ki+%(A~xE9;?W%K8` zypjQxnz-NHe>5Rx3jHZ{UIgDaRVZBPDqi0Z71**=v@X2}6w=aPhJSjI;vyNBW5Uag z`E4lJ&lzj9Is`?_y>pEwmQI3PqTt)9d=NXxwI zNQ~@gusQAo?x&Wa>lGv*fL~SBxL85eu*AEIrOEs-rOl>SvAinxH3q|N4L+uRQ99PW0pHr!CH{XSA^0eE!f!b3c2ll zt=5fm+%tRr%Y6V<5ZD~;MR3}Vf)aMdXpv!=%f^>5G$9Dhj^}kB{hh7ifv%yjor-u!KzL~XC2u{x5nh*5HMhV@b0>k zR@@9-Wh+-%6Y<*lDvG0@gwBYWKnOnLsVSH4P$Fx9@Y>L%&iDF7#ifP^HV|(>hpp zHza8j@j9XUK#1wmsw29YZF^xTh*nUUR35OLjp!}z6O@6t_Bwi`PnwPemZZWs51CsA zk~idNZ~Y|N%3Y@cmo>JC*eVF7r}8by7Y95j-jC(6jCol`i-Y)ZifK!1s74@#gsUnh z6yVZfQ$?Gj1PfjoSXsp+e(JOz3CZ1OfQ;{|@$<>Si{ls3Eki6;r)>*r_64Q*oa2`8vkKkZkVzqd1Jgt z%n%fB)a7uZF@2zl;MR&zf!Bf1sOJzmEGm6o9X?x4V!OZiiZq0_yCqwqHStGvWo2zQ zaZYA#?zd-$@5R@_SiM~%VVL#zOYFzSw2otCz#2Z!lJD===I@PXftc^wo-u6`Jfn2j zr#fOiFTU8&k!sp!A#t2~u9i;;^ktOfWx^rg@{Zc7_#Oek;9z#*5q~R5{_n6t^kZ1P z5anuF{hhr=ab_Hs*F`FrEya_jztp9eotyu5W+^)r{`_n2^2CvomHAnA+HQTiti6&y z#yHisyBJ4<(5IJ^u`~CkVOfgv)rpO8uMdIaB5gzVrhJt%nJ%;9v?L{(}x z{66p2`Vz_Xh6csY5Y&1qo5{^ods`Nk8hErNZc{&mdbY)F--bdZpNfThUym8s4 z>F?B0?5J$yz;5iQa>Z)FncpiRX9jYLQ-pL8dRSPP#ALyYC7%=Z=M$6(2ggsaK`1pt zA!`iXRb^#8277NxZPszsXq7r$A|{MO zm%16{qJd4YQ6bPkFPKGyL?Wk+H`CO}O?4-Mm)ESWgB%a}Zek3H5_}n_+iyKEf}iP% z??G_7e#%K2wh|skRZ;>U*1tu-r#Y|iL%frJS26r}9Kjg(n$q-U1G!NgoKDZbMk^t{ zBAB2kpAYX@)q<6a{gAs7EuCc6?3hKrW@v)`TZBHVISh5C`-x(rcflzv;`Ah;SHnm| z5j|O(2q=FK2wIBxyZhnIEG zU1xy=#TdaH>dXGOFK-_&4_wUJM3xNbd<_L=ElbfC1iwe>kQ)te+J{3i{fw@;Q8z#S z&XGCQr1gE@YNtU7ojT10eUQ5s4{&=J+XS!K`{rOwA=ook+wT?i-i#@iyPCL1N2P}y zAmE(%?;_Q7zkP)oWQ>|C=ZI6ojcOGOQn2qcO*pC~I3Czuygq*8f@<~l#&=fBwyJ&N z4qc?h;`li8wy(LS6J(q0y@!P=Y7C7t$s+FrR-5OCjrO3M6eu$q%o66VC>40SXx%|ktsEX&^OVDrx4 z{j!piBlt!h9`YHpBg_P7!ICQz2K;DT(q#VYA`?CbHi`;dwDY0*wxv9uI3jOX+fE0T z)?t{|(-`WH;!d+Gfvb|TEUB)D7M@i2L^^vWM$RmS&0@MqUmM!4|6*87I+P-{g~M@K zvgxd-tRlOZVm>s<7Hi+$_PHtl+)FxWN@g{hJ8UH%U7s$wN1pf7$)^z z(q9{Gn)sFtponSkbPnT6@C4(B=o8c1{jH0rY{o2KZ*A+_@|8QJ3EQ={3Ehi|318Rg z0tm_Z!X;5UK4yl>OBZt^ma})t7|8er@k#vf3YgeCa1i-{_HZcAdT1NPxzqW}BWtx? zd>ob|om{oIgtm%{pQE&zW8=;Ez5qyXuxt1hf**Z*d#9T3` z3#bamIP{YB!XB$)1kI9M?mhW_Z zi3ns!Ru7r-mUpdn`#=#0nl>LQGoM{$X5`p=x_)GD<8;hrOxsxh4tt|6st{U_b??Pr zF#3|!&Nk-E)H!~^MX{Xfq~megnt5`5ie5n0S%^jH#7am(A~jcL{_MujGnnH4q~O3l z;!Ef4t!nMVcNK>|VcQZT^3A!;`lemVf}e@1mM%2ADuk^soSi zCkX7M*i1&$6l4@Tt~_nM=X^wf-|Z9+CZnm++dM5YU{j`5viOB()fL!t2P4m~f}94k zyN?Yo%rS!uexs;62MPX;P2YR#Eiu^U{kZ%X zm?P&r_TrOT*e9*0dXhWkK+5Ky?9VW^Sfvw6UwB4TS?v8Kll~wVm-lYsMeoS=CE>i2 zO>=N(8cjnE$T}#}2=udR$`ei)6R8r}_p00`C7f``*Ne7J&lcMQnm@)IF9()*U8hFv zBRXA#mJ@3yVdynFJiQ~S9~=W1U}+?f|8&ORWt4I(-F622gY&aH=G*f709fQ;g7`W? zaIhgJRhE(`9GJVLM!$X%2Ix~ouzrDZ{ranOuOqN}Y0KMVIq{?w!axus}-dRK6* zmU?uws^yzFkx8s2a1f5e``R#Wl~gTC)X^US0A8VO_M-w*goC)axZ9WI%g7UMWLudE zuNRJ_AKNL6YAP2OP`YoA8S^h4n#mjdz)S<1rss7I3qJ3(q3%nPPPiP6){x$Wk*3+yqEBSOh{{Bq0KdXG;gM!76N-|WH#kY!4m6!f@vuUn={ zofILVq-=|Ix!DyYw{z=)Po$QF2jvLE0l0|6NEl?bq3G3BjAp_uXYCXlB7S$ygEOL3zs3Hg@6b?C%@0(UIB-IQ2mB8TCvx zi{H}9FclP=!jmMz-PGQ*m+rE*d?wfgTf(c;P%r0k|bj)(L z==`bz3R@lHx-^=d?%$*?g|4+pijVz{YY8muT$_;K+Si^VFj@sbVy;JtQ25(jLSysmb3$rk zfV!kP91jA3(Dbs?;GFmw+OJvqx8UaR}PbO8k`yZ|Y(R_l znO5}XP-_7pIOYp0i{tAV9He%2y~Fvf0_ycVi7uFdLDVjMR{r^+5W#45jC74-g<6KT zBBx>B^Ce9~GE7vR;;}ao{|&~3bZH3K-YVdjQ?FtABxy^B5}Sra@uZ<%jN3*!c!a2s zV{vhsv9EU20RScKzL@x2oEgNwwcjIDTumx|binX@+{SkGTQfys)V2srNFG_$(`o`Z zoWoWTH;!8dQdh^w>rSK3iT{{jmZh>Rq%XJsocZX&#-T_#hulP(6h4XYdFa&NjS$n7 z5Wuy*AhV=Ga@u|!GLPFRWtR5D=nsOzJh`blDj1GdG(?&t6f(_CtdLtGeC#h_j|uU2 zwzFZ(+(rtP2#y6Q0hITa;qnxb)%Nl;JhNhk3fXlwtjzOXp(;bci_P|S%+tF+OE5e! zm<}9tWX7w;oQwM4b*D-r0uAGf)~q|6HK66xkooS`S?;mKmd&W$yoS%=tI`_G@tNSw zSB1i>yq8vDBfs!$F89S#3f=74B{(aMTdeW4GPN^Ym4;~}KH&_cZAi9Vr}dkAIJKs& z-I;~9_Zu=&E~P3^aaTF)o)sZIOfV!Si@IIzM`#Z35&XCA!c#jr-^jPeMip9ih~o$u zc&t4NbPz{ z@@-SyrKPP^-hS)Hnq!;2b#reg+#O70{+Kr?j7AJqQ)NQw(G}kdj2y)ouAAB68EInC z&%88%xO^{XQ}*T4HOU`rjCKkb7}-=PI(B5Q1yQ}a_yXgdHOT)qvvv3W#GB-lWH#k- z__L-pv|0`mbo)Z@TcaC@wG-F2Pds5paA@S3xdV=cv4ljEPE% zZ#>cQJUA*$A9;MIC(*{>*52>>YK1z|4p@Yy1vR@52khZme>o|+0ZVY*=*2@C&j`X{ zNliQQbHZdFM4jQz`$Ywjk(-tEr?2Bi2cJ+}bCs5#^78Oq2(Wr7JO?K^xmGpfqZwXo zsWA#V8)8IdIRx)h@Jaz5xR^dg;x#7r(jI)L9JA;5aSODaIc%)6#zwvplP5xe-bnt3 z{xwzQlYb)xa93#RN7@mh(+eXnOT>wigSYI9!+3rUPiV!9Zj+6F8bD^qdC01`N-N-8 zDnuaT-RL0e!w{ej|Dc_*B5pIzS=C)lVx>ns+dcL_lnC2aH~S z`}4}w;|~zFWp*IK?}=XC7GnNEQ33tfrM(9a{{M4)_%40?mp1(IMxs~g_Yt;x@Ue%J zV>ssf@1cc!ZhGII5=3X_CMNM!q|a?Jem6W;XH}Eq;6{_&i$Fe=FlOM}~6zRT@9X*#GaWJFgr-{QjVvb1$W;+YD_+?*_ ziE9kqsKc9AmUizae$sIEie|2IdA!O>0RsLiZ>O$m8s*u5klSX`@aBO+CPgd8L8V%L zVX_j}SrU9aA^Vzs?mMdI?0IGI7?HZVS)A zgLM=J_;B$D!aJ6Xz!&83O#rkM$~f?4)iaiv) zz5ca(8&z{6hHL7RegZRr2M1;-zvE}F z+c)}2>k8s~HMxJU81eQ}Qlf`!hX^DI_s4*S52_@2oeFPK$SpwiL>a`HWtq?(~d+B$Cc_q9j9y`L~_w(YUUzVsy*I_dq9N5p}vW~^-3^x zGUfFlU@wU2SH^XQYkS{7m_on#9xXQcu!vTQ2G&ePFm^3m$-oA`gnfVw*g)NbtH?Ap z^$?I|X1sEf=0cB(|C82YwM=uL)r_J6hqHx-A1}K=|7;K*K3t*hO-E6ZVaD{RpzpKB z>-+_&LKn3QWWsvsrR*-}$G$R!Fxy@K9{hRLMZNzaLW)B6UjAw0|IwrnM_;2xJ0NQi|J67H%L$@AksAS4$s!cgTQA1j>AI%*CJ%f^U;F18{ zL2BpMk{7VIfq6dzUgpMy+`0miN|@+-*Mq@(Jfgy<=`F=w3L;BSEGas3{_r+!0XWq<5brD|hT>QUkuzbpMk zEGbN6XssVrS7nW>@x3&|wy^u_CKb%liD7Erom3~|#GpqZ0^ADJi~i3^)A_hkWM7-& zl<)1}>G)9wZa7p1_A%w@m^faxcOB&4o7AI`s&w~=ZT^UhcV%WSav!=_omV2R*3cjH zd`>}%71yGZ#;q0<=#`-QjW091&ow{i(^j>k>Gwvvu`zVxF0=pK1ICt%z%h~WPJBR- zj~eTmy0V$o8lL5&*4#4X7VWkESA+v@e^|_T{f*ynE%9W3oni3I?hm#%L%9-a`Zsck zv1~2M`7x05#s#d_{fQkn9?+xPlSIvuhoR@*UX!39aquS7&cFErhLa_rC}6QX`Dg$C zxH`w+%G$umC-2N86Wf}JZQHhO+}zksW+t|6CpWfj+qQ0OZ~j}gTebV?`F^U-IrVh^ zy4$rbhgz{6lPCC^5%xdH6*H6OB8+$ycT!{|Bb9^DuU~$3inVo#16IStgz26bB8Gfb zSqq%gFRuV8q~Jb$LfFitGdkybR#deOcDX>_$s0(731x?B+>IUZ%9P@~R3l|amjrd9 zU@)^)GO2LHhPm=v5%+8H1D^niaI0p_?j$UL&+SZ#>oY3VKuCJo+$TNs9s5MU-h*%sU z>uHs8E^BF-a8uU1JacqP+R{iHt1;`k5%T5Kbs(gtd00DeK8+ufeZ0ASKtMQ!6F6aD zHU@Ak^|sw{@4r`sqSEz=s7B;LyU7SG7OR)!Q3t}yn;%->a8j6k#JUrjIA-Z5PlMd- zLofml=$p7p7EGth9H?l394fq$j zJd*&`(kMHor6kviq7z#c>uXC3afC;1M_}Q=>pu4xx{a87-Xp7%(@m66adsytGgYd_ zP9^KVi6N_FHH%6vSw{-0o7&2e!N|)h<=gg*76mIaztXR(G4ZKzJ%0z<5=4j1jHfJHa>%)oihsgq%|Y>q$|rvbF(D19%jb= zy21*BR4*WQW23g1q8U0*BQu}Kh6vN~NEgCY8$x5yXwlR;Nvgad5BZX@YocIgmP}-v zo-Fvsbz`G)SlC1(`i8N+ZtdH%@W04y{>g%6Uz8?1y_J7jB#*1gPmhq zf6rv1Kzik4jTc(|n`!?_%k;doi4{dpJ)<9ULsV&ve|G`I;27pNp+Z_4Oaa^Qz+fo{ z`BgacLG87l3ox-O_Xrv7-dSC@M4x;RE>?>2Wi77Rgt^6)e^;83UmrUKSKG+O#F;W^a-H8#pO zGbV#>LuNv*s8rcQ2~M%hSm{E?yB1B_U1#oSbML}A%h7|I87J@cb?AavdGh!kK%7Nc zSy^G_V)R^ThPh3kV@rrsqyrVeS+xuoAJ(c!M24C?Donp@z?eB!V?ve{lg7Lv$Uh(r z+I(8DQd0cywE_}BzH!ibd77m&a{MPGqVx|CoYzy(zAT>1)gs#0^@!llFjm)p-EH9< z%igR#=Q_PeJb0GAAGgUkJQQ-w!thH+0xp443)KT^ za`r1s!?K!_VoQpB!Ea9{{DQt{Ir1Z^la1IOtw0gxP7X<$DaT5 z+8`P0?@y|=!1xE4(jkgcOihP4$kKk-`vY@v;F+MJ)S_Ake(Ak}(=$7^QYBB5-HtyG zzpOSEW2mUEA#v-8=kw12o6_#q`_60oT^_5}bMfb?!OYU)U<*@ECRx1HX*%vm(!x`U zaOi#EKt2lyl-7>P zWwD)Cz_e7bglUyr_Aj98+y!#rN5`YDq=EsDd^Yp0HG6p~vlltn+?SO~|1m!YsOvpi zGAFun^SfArO!@qPHVw@qr!ahIy*6L(zf%V0Uq`6ON`#8{b1ndC0m9<+UF6IM591`2 z9oO6tSY8rb@CQs~MI^6{%-OtL(s{I4Td)GX#*THIRGdRI4>Ij{II&`OTkRtq1JK|i zQK{&8wmsoKPi?l`6RQCY2sW`lq4#2G8+6UI!1POoZu9OqjBl$-)Ya~kuY@7NOe?UgjZ$dMvIWwHE=d#yo{)c5F1y6I zn?oB6_7!Y0lX#kzv=F3W%;h4ZWs(nY#$JsY8y{hkM3LY*kd)=*ODd=?&pmS}MH;5JJSFALfeY3rQ@DdhQXl723Wf~7O$%}J3Dr1o`SKW!% zW1{oc8$6G8j-cFRNkR8t$ZLCy$~l$d$Ay*Hd$VH<%&(Hi?R#iPhEjJ5RRcYDFD}~J z{e!JIh)k<~8QVTB3Z)4>Mt^vaeptC2gZ8YC7+;lY+yGa%l0Pu>YmnbbI(S!#|GTXu zu4&O)WKa0G7v#sFAVA&3!?)dItpE3$@~%N?#9me?8N?6w^)j4`v+19gV9eF1DFJLlu!ci`3df$F+ z(mpG{%Q=XQdEBS%U*b(lod$f2D4MGxk&49?D}uDK68D_FIU|O*qc~PK1lS1LwLiG# zI&OjkT*KfI8^?SP-gvcjjZJl*@bhD9=|eiURlSUbgosykwuJI)m`XNxULxC$gV(AL z<~76mAPzwoBzaEW4g9kxY}BM*L`WxE6}5`M+I$aqf=Bhn@w#=-WIZEoFRK@MDM2=2 zohyUP<07ilhuVlsMTB0SEtR64ioLaXL%e3PKcx3!a@-DePL#_CJ=OElK6H>imLf?r z0boY3kp*t6nu(sV>@H4z1;C59N7~4Cki6BSZyJKZ4!gf+rbeWL4k35;Ht<;By)TNV zw^SpGB^{rB33h7i`}XmKv5V z9{4*4N&vkjvv1*fc+k){fElux2o(m1?K{)`()xX9wlLi2xAgxSeO#LcjH))q>ONIz zOi*g19=*l2@9vjXNu)TmQC(_@LhtoPq0n4FC@Q0XZHvzpqL2UaS#CbvOSSV4&)<7q ze4>*IpW_u?iVVjM`U>+`PnF{1#tNrt?!^Dzmv5Qnc{HzC26YqCm@)7KxD zg=LTkxbJ%H>Xe8tN~#x&z)o(8)KnKaRs{Att^9|X9}GI&yC~3_boY6?t7E=A`sNdQ zB9L5iQq}qLy(x{B)lUciK;!e-;+X-7XgWQX#g&~0=EuXb0jAK)NKKtJx2V0XoPyHQ zvb2*84+L}zb<`Vgmu?*8a@RCrF>0Bz691|ru{2focD;C>AlA#&)y5CY5)_1?8CGWp z3oT~3Z*oH|SM!`YEdP~!5)4JdZxb0zm5*g5$Y4+t@x;O+Ju{J`_T#*{W12Q-ixsor zo2A^Z5=|M8dSQWx!J`MvBhF!bYJOdHb^c>J1?9{^+N^J!JU>5_;Z6NF1H|*WfJrJ9 z>JF`X67r^`s9d9A|2T7q{F0}RW`Lj+*_)Q4!QiF6-aU-JHtcvK2h4za>N88mygA%q zDv26psImcP=Pl{dKP$9LphH$b`ymN5TWg~Tf9!w~iBj)-H#r4Q+fOpF9J4tbX6m+0 z(EUwG8hLFqq+r*Wf|CTA0p7>R@-UIX=Zz5`T@{}KC9U}Rb(Rsi-G-TqbjF}bvOIHF zZTL_XAt&*v-$j&b^EGe zv5vE)_nGFO7)uk|ZRmyT$FUp?YX&Y)xZaK!2fIHjJ3C7;zN^iw@8-MT3U1VU8}(xmqwZ{EJ_&2U=`zU0j9>Bk3ar%GZ zBuuP#D-ieRo;MgXzq2aYYqcqc7U1jgXk-$?F$M=k4b1F*XvxE6)8ki~fUNrWlAVC{ zYo(LoT8bI!TYP+s&sDK2Tvt?;@J;3q)VlD*;$(=^{!wlog6%Tv>|J$ zp_2&*k?$EbQ?~W=h3B8eR-qeEyrq>F2OLctosHcY3HHxzhcVwfZSCa=O)Z@`D~oe( z=QKvGVsMYUlfa%WjU68{?un)Ko}!VlL~w9Qr*Av~MgSN88!1|}>u}*d7x(HtxatRE z(Ee?mg+h+!q#=;S;o9O8hUop;4?~;zOp1Weu_Vpgr@BVg$&I0~7IX~yx?jG#QMrcb z-QQd@DQYel7XHU$=}5i+qgf?#R7GN%c$v&0tT@8lLQ~6Ie0_7u!q=Z1z=1`K7jf^y zi&hmCq#8ujPWN*3rYYcc{e2*8_|IgcHhe0v6NqU+QIDn#W5ptSL#_otjE|uW#5*zc zvW2k`_tmL%m^N#2N{R32a`Q^;a1Ns2Crcf^SRidO|Iu~^w(C2+k5hgc8M#7av!IVl z!_WcLzw$osBN9x75-z`<>akk%{}zxevd0E$Jxc=Ko{uD$xGL&B#RNJ&j{E}OO&L1> zy{Q}M<}0uLogsNGtm^;j1XGyWY!;!`&jYLcjbEjg&s?%^&Ig&-s}Y~sBC6)sIUR9p z>bn0amB5d)RUo>T%kyLB@CVzC$0A7m>0P@RT-+!AV0w@*8%UT^Qo8+j2F-1$B2@gG z4p)wUF{e-Qx1bXA_n$~`MHj)Jaryr?)S+%w^4fNr2py7tX7iHCVQFl2imUBFi~Qvi zC*bq;lCK^=j8d0xJO(h!JI)y%FP-JC(EG-jEE=>91C{gZXiNQw4C5=srM3XYn$wUrf&ZU4=EF5Zl~r@kEN8k&f4J9&T8Gp0}95 zKQ#T6e|&WO?A#W6enLaM(^(1|;`6v&s*`Q4i7XGG3Q4XMy&l8$K z`$n>tqN-AKSL3;hcDt|EJ78e&r2*bD5SkDZ{XZcW-V&37|HA_O-}uXC=b2xK?0!`z zud2(DnpRGqw2FT?S*B>?LuG}b64j!2dg`HW7Gb{V=-9dTSHH%!07>j7nm&hBTYAAg zsu|M3F1(Izfs)fCt7Y66>W}#qK3V>ZqtTf85G=(1mM5EAUSmbgcUzc~Te+`sNo&y> z69H=sd%&2Gcf1<2Q)N^P`ql}2Cyu%rN;A2&FI9w_AyXs{65*72{q2R%+U17uo}FNo zeZ*c{hv(_|1 z*pG``Gx}4}CzZ`4fLl%7@)+2U9pWU{VEa&6sqz|;a0lMf&N&+)q;~L-@M;tVn8cT; zSK2J$J#Q=~cEu9XGi2rDamVK^e67tzAF={nau$G6=4Jw4>}Ueb@x8EsX9ppj!09?@ z=`6aoxeyqrOE1AmtaNyOZgaDOkGL?(6E7V<_Fe+N0AM9_@mV;FICd7mh0dD+*n6xu zZ@??-rLcJ6@7M4Bm3EQyfuDwK6RGj=#QM|}D8yXS*h>BnWn}zp zRP0S1I<;2xF_N6l-2ZPw%(Q}spMSCb2Ru@xkKBX8*67(*>LG^!ja~@fz30aqhwsmB zhA!t+!nS9?UBs3i*+mHjGtJxG=va0?=jOmt7ctI4pJePB5}6LWX~ocfLrokqKVMPJ zF6UO?MTRK`Z3P>6BWuV!;rJt~R9J5^sEm^N_7AaTv?$-DH>aGHL{x2WqUeJs;4XPt zJ3hj2=VRN@1?6YDnb$iv6U~J8J%z;hi)=&DGB`$Sdv?=KbyEwYzR+JwU9saNyv$ zDgW+(_fpL(1E}hLXdQ;v^Ff<(aknptcroe?&Ci!y_}%{Wz|lDspWl}-+w1ozth2t3 zp>0*d);-Bi;DS1Vq=w4c;7(O~v260K11v^zY%OfynvO?6Pb;prfIP_@i^^Tk;i1*V zGSm%d%cxn2?rO|hp`tps0#=gY3he9V8;TlacZCYM;CH&;Ft;wysjFE%4(|BY2J^r# zjOb4Vs1(%97^1(cL{y;2S@+BLKj1I%bHqD6(`yRmBHfGu9}7bWu2`V(Sl%}1>Va~iHU znhH%EG<<0KCgP}8WP|Nj%F9Vb)lW=dR@1fZ%WB{4I$r4wo)!#9Tj^@4(3gKJP2TjX zFh5@Ak5E~Q#b!hEq;uY3W_@HNJn(tH@+~@&c}F@PHXV}Jus_aKR8OX>EVbP2V?dZ6 zJzTTRD`mw!Iz`UsRp3|W3_4+ik+-|B&7X-9RRk=;TqNPBnY}w99Z_nD*|OcgRGY!7 z&I#YnsKPsK{hC4?dAz->M7uScQ9aO=Q{yQ+_@1rUybb-BY*`LVa43hOy-u+HF$pcq za0Jvsz+6$y*Wg~m?-y<>iS1OpS5b8`!#mX}O+X7s{z3|7xBmXN=~GoB&IU$ze-5kprFB?V!UnH`9Ssj8@&@PkIR_40qAD2hEp_4?uAGX|Xg&?piJmT{j@cVD zM`RuAcU3E1^F(SK{9PWF<)}kr8i}i(e87eK-cauvmdQ!9TrjZQzZf*U1UD6hwh5;^ zN|T{YAGuJur|Cc3f<$D!Lv%uX$F>8xcEDfj5Xzxw#t)_viKo=Bo{O+F`%u8*?*cy=Z?-fO!h(l#$l*>k9jJ0qzbtP6-w{%Fv zN-0?AF+b```v+Dktg0qkAr4N=(Uo<9{c{Jv1_bKgkYtP_%S7Sc6o^nP>E~3ZxFI)I zP}GS%n*N@L{YC&DB303m7VZJ&=4)Nx$^4Pe-nCaaZZ0UbzxZdmJ7x*pGfc?zBm3$E z9!$qD{bw0pDH@?{AW}S1?zC>9x~{tKEn?bZ`gF&FpC(gp>-EM<7eSy`mt;lw%m}8g zFrtd~a-M@-R3tqyV3eOH-?I@tNhADk47*Ua#QvGQA)J+e=A`S0G4SRGuD8qjtKfys z3FrWA)jTOd7Yx2I{}9w0Gw#=W|NP%m?{npyw~s+hIOHTG9gjt|%wuhp54J@Xp7^-t z-w|-txpVX?>VJ|>uaKPQ=@~*ou3{K}hldBG^h8`{J!~vAS|>}XXq|K*T>7-v5M9i1 z%@S(w`qx8GmL91bhK4NhPTfEHU9#CD<<7CU;qmDdmfu~kzp@l~{m1GaDDNW{X&9b; zMH6TAVR%me6j(GQ5iD8AqMCHT|2G#Bv*=>xGbAHCS8>RDUerOvG?mIQ-$HQz{Gx~V z+k;ZQ=T{wey-0OMFn9VBRBnq*^M?OCcfYyb=4yV^qF|2LL(a&OJEuUal2aTEr)5R* z6ehjGZS?0cw4c{@dDj)o5OB&*4gE47DU*CUJ|BqD!^20E*Pzm4`%j!(g})LkrKiLl zOPn-3V~i?(Qf6mfnABYgTY#5InAV!^0McOPSbaWBE@XTM$NP}xGr_8H0jjbL%Z$mZ z5a%RNwg41kx5mRBH7Zv1XmX=}zly11<(O(eN6@k%volMp-|S?PB{7|vaSMtpFDKQ^ zX;7e`zu)9lSj)~r!5rZnw9ao zY2zQ&>g20%wYuCB+BVwNJ}?Y4KehP1c3pkkEFekD#zc?;>=%u&QJ)eyl*CzJGew6@ zkP@#9kPo|D&M+6Pv$pPs^*=jI@#KTkn?V9{1(6xvSJ+TZAR?JT`|^+;j;Cn{eZTh~ zQT5@wBVOwXvmT!QH=mJfD&8h{+axDDJ&7o_TqqxDMMg}xLOmwV6aQxRbHW_A;lxT7 zs!N}(%_91E35v z*Y*JtrK-IrtR||A+HG@UEvk?Q;|DA&M7@?{VqkSR_BL?h+Ijy$qY3+%)1qp3cTU>D za)mhSL`YhuoK}_bll^kjOvp@$k>c&FG)9S9I4Af9|5+Gi0zig>#a-dlT$)I|u8;YF zYiBc9XdZo#N2N@THUY|woj4-R;xsIf&uCHB;WL2Ke;;;wS#nXhJx%{_2+WXHuNzD@BN7r+DM63PMpCrGxi0FZS`=~#4I`B-6w6x161hW+5-e`NJ*hMqFQyZP2UYmei}sLb+t63Ux9RoIfPf4# zzkv<3xiR0nS%cGKH;p=P*<#JIpLo-IXCxTr-Sd}Z`oC(h8wuk zxtBM?YfO;8^(Z^5tfK@iM-&cY!X5+FPtg=IdQgydTtmm-P9w+gZ3up&#<`sWt<1?u z!WOlzuQ%NIw`Dtqi7O}+xG+hUN#&4@LkR|hbwV)%{a(iJ-8+0e#YOq2tT#xkk}ME4 z7N55tym(&|hR4CaIQb2s%2H3IzbyxL%atZVuTd(-(2miYfzP)8I;0aO<=HguE>Qm_ z{n{`NpMBZqs_gXop~p5_?U`9rDI($!3lVNQ`d@YAvw{D ze{$TrTW$YNu=AR!8F!IH55Y?(wCTPhHmMr}r6ffc+=xS5l=06i@xO)+S~<|T$yo92 z@Ser8x7@Gv{OAmmBf+PghfJRR3bQO;AHqNHvT5VKA?YwP0_R!k+PsjJ>xnZc?|XX@ zzwZRjLuL{kP~<{(%vcwU>}*Jhl0R8y{`>Zo`k9cSJDh>mq3RDyv2wJ%2>NyFbq4W- z_sz5qF?v&!5xLVWoDz)ukO=jEZ9T zb#!=#TjOQ3zKP~xB6%4ruUbmY&Atk3R@ANnf;8gN$eZg7kCMreir*O;}$C2e%w@=6*BEVL~)Ast0ad>JY0PF#?1oU8wqu4LdSSV@nYd_Vz6=$3r>UnOktX zIm$OEE@pnpqa^J0YN|S-RGTn#`G68dw!mdJQed|A8WdE53re9irf9)CTVB=AUm>ke zsYC}fE(74bhwa*y(LY6dVRoTtyhc&*1-K=X$E3XF_d_|R|14-s^%S*>Oz#=FJ`=(; zfrn<2bd6X(wL#;1mt(`DBsNE-#;o0l1ef}-jKx?n)h2a;zHP5P}PcwWS1 zQ3kxtwIgg4Y*d5l=@H|v1k73oBsT4Z^t z-eO<_nXK5vC*eK;i_Fj3VI3vMFAp`gahDY+Uw8!I#(f4aP{7kkqNR~};5uG84OM^i zD!=Qd_;IKFap~Q~Z^PjKKm6A2Ilc-*Qi;w$9<`_^xTiKl7YkbYNs@!zq5{u(qWKnE z?Gkr6;tlfc&N^+05=6WckDroZ?G#=?A>cr+$C83B1K(#eTzq|Q=<%T)LQKI2m@@f7 zx0r_9Yh`g=x?y5UUNnp6oK8g_X=a0JW_{(rD*}o%ZTqHKQ^pjaWZxdpmQ~gMMo)$j z%bqZf9tp)<8t~E?pED7?M0Pn7FgW8ID)=QLkas!%tNKu}{B^ZA#V?-5>F{Gs3LN;e z^=-T~>8S@UKjMw}0Kb!?2R&C}zd9XuH zUh+q*N`VHcisQpy2jAPr54*_>te-~3YoTk#7IFD8J9*z?%YUqN2=J>*GX-AZ-IMJhKNwc7ht+p}?&bfKu4%U?Ro@O~19 zI3RM1I!nyh0g{9hL~x|zm*XRzkCHx$i31M>O5~-p5k_Kfip^`{5Kg8 zAI{r;5Zi@T8c%O46wO>TTCFl}RSpID;qertwGB-TL%ZX!bOg1~R#xe%aJ}FBT)|!N z>F-*5->bGddbccNDy@FxDX*#?+sw-J?5}T_BigH zgDB^INLkhE%iR{$Yh?ep30x{Ui#aZ=$;fXhjvo~NyG3S^r+VW7I&^`U^nMfS{M^lk zymI5ejencG>>Soi?lw^ti#$0|vvv{whH!7#R48c&XOPXmF`D2>q%-gXl7_4;Bh`aR zyn1B(1|%J;;aZAkZA3?!Ds8nWIFMjIPD&YWot{`4H{4wpr4D+PVQrF1XlAp|ux}A; zpmwyTD03>W`Bx#&{O|dlaYh#2Uxyj4@Li9#@21mAgPlu&eMDOLVKtS$I&0jp!FSnv zT%KIra6dy-cS#)+V5x{OpCr5TgZw0XeFZ9 zh(&W^xtfvgYxWrqhOTRNxsLk`eAMBc^+UqadB{Q6=XG|!aGM3Gt55~rZm%ldspA4u zh~lO^=O!1%LzT!@M=9m-Gix(ZJ9uYAM=Q0wXKhZYIYX>I|F2}v^}~N1LqvO7!0sBq zYN;`MXav|?4BGC};_FyZU*bh<`igNPk`&rL-awLl-VPPG?uwT=#d5^o`9kHy+p!v{4|= z-@HMpnkp`0MKYF4R~zziP5$nVw{gc$UOVg=b2wjWm8>@~ zM|sm{a2G>gC>0QJNODa}=bQPUG{ReHn?7T$FHeZI-*>T#PeVmHS|#>42+2QZ+Il1IB)7R;I#RTRb5MF6*;tz|uZPSqux4#2ppM|oDEB^A z))`e;3x+%W)uv1=h5ub=%1sht@Z9oj`JmJR zl@qBs$&v-Jqv4DQ^DOy!fX3be<-%i(){EGow(Af$NAV{3oZxg+KLwYIC_H^H`Vwu z*bP%{YI#;Zrn@*`<-PSuy0y<%av85t=)>lhw^o`A#wUIY4D-PSg(;g=aEw#ub z+jDEh8E}1LiF{G+H+p`D*oA@&hiNcry_4Wj-%S((Rpyd`Uwk9UE>8g_I~8TB+uKZ? zd$<}b)lSE3EI0TXwaIRDGwoD2gOI0ZG3n}Jya#q~NP?28Knv)LWFWR#YFb*Guz3U) z%Pm}3yIr1TKYcxrP1f&{-CpNB*PkgJQH5+$0-QfkzD5%6^g-rl;rKJ1V(?mu5j z!y9T9*lj#4vQ;TZQl*TbI#hG4RS1gnP(TmN#dzpZEl_xKaP@amtM7tSbE>7WlHtac zb!{g<*_|pV!-{+2C0N3l+wXT>THhp?ey9~!719mza_>clJ2tmAhtrsLYcPZrzM3+W zCfe}w?K*!9RedliC`c}8%6NS%gXSr?lv-_8EUk@?aIBPq|L@$CqV96NV1hR4=4qQT zwFLQ6n_ZSyqgM^(_c}Z0puu{k#VH-vvAG?O&NKEy9wk%wQedIA&|?^J8e@ycgAz)H zy0kOjVvpT>_O9xue_b@1WB-cAWcilbkdDk8YZ;;ba zbLZO$C??gc)H)`@V`J%sa5igmO%~G9<$)2;i&Tc%3jC6LJU9ClT2;PUw@K~P#S41W zu#*?>30ifQH%D+obU~;>VE=Q-;M~3=*SNGIP#kU`E=t(xzc_gg0C9JQJO^ShwnuAa zJPjtIh3w(22es$d?#y}NfG~ZgiIdNnJyLB;yP$Dw4FM5%s8!}VKc zW*#z)XQU|e#6u&aGb1){Kv|j`c|_3a&L?I#j6VwNdvLo2BQ~NHv6Uwd>)vlx_0~DZ zr-)+=5OCdp#8A8ny+mE~36EAC)QUo|pjD~ZlcOP3irCQzj1Ak3-d4nGamUr;ij(V{(xdGgWjLV#&=V}tEjk_of>QK zUHhD2tMOZS*>%KoyZGt7pL&Zm@)Xec(Q(c#VBrAL54ZJ++4Tzw6erekhv08Ko=;mvJmrQm2Dh=fsXM63>#2ErKWz%368@b7 zQN4=A{*Xz1seflwZK8kHEQ=6*c!$QVEBs=1x=F1`ouU093L6x;A(VA~X+}chg)xb~ z5`Ce_820Uyv6ypZb96QdJH+C1M(c3%r_wT(0u^`D*m4Rm_d*W2-leu`Tjaw;f^$o6 zYW<&MH+K+Y+Kp<(X!qnipqx7-xW8J*7GXM9d?G1=N>>iDQC~G^x$nrqm2(8tI;0J$ z-{~E=<2wWANhKo@KfK9<$1`6&vr76pcT#(`8h(kg8fW2q8X6A>(jx|n`X8SBUN|s4 zM9PBRjz{uC{S3KMpjG<4J5f!5;e#$2U|;WFQM8}M)18@IrCfCwfq6=M=Q`?kw3x*C z+HFZ4_BgH%2aUHTTM`xhd^Q&0jI^slVxn&FPVUy^h@C!=(tM>zQ8g7(cUjBsG^uiU znlUX>uAN7sVJNoXkHbe8R-*^{8mAS>3FMg(qvtgB)7SEd9?Jeeg-FHPIymU(p8B{` zfpAx5KfTcbi}hz6Uwscr2(PDd-`#Xg(baM(cNO;Z`KF+hhdrj4v@#pIs>E6*}?@Uoi2DO-#R4Fr}ns=jqc51|A>EV{D8v&96sp|C<(OYnvz$J zsCvmks#8LjB}Xal>dIb3bnxzL^8hYVljmpX_>T;L^aqtR zH*8q0>iOQK9p|eQN8em{Z4J%k=F}>o6%w-|lx)I;^V}`L5nz^)(V8QrVyqyIbu+pU zxb$x(axW8pMbyxLVgz8FYh@PV)JRMU+*6iai#h8F86k)ZhyF)F-jCgZpKdno1C>tC z2)^p@U_PJ?OdGEBUito&g0%(=m|GeeDw=NhKDdDM{lWSul-4pnXl1!tQ1iMuqLV$Z zYcjgL#1As<@9(hxhXrsc zbb&YH3Z6Je=g&^n)7w8EseuW4r_*htZXG>!G_1$f;*YU{8xgFo=bS?#MB! z{H!h?n0#2rv_Z?W3&QmWms`iHur1PDBc!!{3uq+WzI~dxmF=KtxPm*wd4aOoPh?q4 zDKX-nl@oX2TiD}*O(hl&Kjq{(G4t{X*VO#C?SjeRWp#r{a9;79*OgHk`M2%e?q=jH z+xn00!Zr>7_Hj|JJb33^=ax0%(Rd@l>Kv=qRCS`I$g&v$lh&gqeO(4O z@?Y$h$4+{Nhw|Z~`K1lMYo490m3jZ+UZ#h`A-;`J@8jYzBCMV(J^^|5l!{Eq+kHxf zd$eqvx=B9#0qstM3qNSQEZd8t*Q(BOPj%-}%}a=-#JP1I{(nrV z`8npMk8VX=E)OdGjR#E})JAC?klvOnUC%eD9Z|H4;;|BADtN(}3;74I640+T<_8&= z$-S!Hw&PkgJ!xm_?JfKR#qwdD`srG~L9L3fZNbaSh;z&2T?i7eUQOz#3p{Dbf8H`A#P)4Y0-aYZ->xR#lPcVK%SjtHtQlbz^4?rf?1XF@`0r1Jih{a|+A<6fYxJEG z9866XKXcDg($RAdM}O*><*^BUi pY%2Q#z=d7$z66TCuI=q^4t0Q&hikYWYw{{E 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

!ApzUKjcp@6R#4W0;IqA_Ehn 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

uou`)7pe*rUQhfNHY4t0AGSu9KWSwd;F4O~M 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%0AqV#3!NGo6}c=HhmLEQplQnj@F?)-aOam#y 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%0DE80iVO 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

EpJZhhN%3WZe$3IuA$eCg&0 zVV2nPj^oT@Etka-4#%Rg#!+uWb7$EBzm zCFk0JF(usvCw)DHd_g>KEYq>wU4itwe|{sQSP*WV$$>`jQBpuR%9VKeG1k~WoSY*B ze72+#o=8+$Zi%6!-!2`x$xw!!THy`O9*~mTNcghd{Ey=PRb=t;;)hu1<4qVC3uUxU z@gn&K&j4qdd)a=v@_?a(sdIkjdZlE9nEML~ODq^!#;b6@cWIp~r443{A70_)gQioG^6UAu^* zq+zGUYadGq;2M7;R&^AkRLYbXvR2=IL{b4VX^gSjbtFR#;|5f2R-~D4`AlkN{IOol zjyON{B%ROJr-_A){9&c0uOxTv+Hn&wLy4H2?coZIfbhiK8&pR=uS^yhk$42n|04Jt z`+f$kCBQ$FDR>*UoX^!1?xugK$Jx167H2T>9(8H)>hAX}8PV#-gHeB6m+%E-U#d@} z=yekIiw&^|6Zj2UaQ~k8;DkiGrunBL+I$6B)9x6N4#@iQFC$g|v9O#Qj;Z%#C_(;Lmkw zS}CI&nBfZu4<@e^xW>3Ddwr?q-k6NEza?#4F|+A`w$Z zmF5;@lbXbh7DpGf`Iiq~u^pld$IW=T!EEY*#2UNn!?LRbZS z{DmVOuQa!GF-b2y4R=hcKQob@lSJf&{`{#=tT+K7NBpZ=7>)l3oU2W2u$vOFk;+=n zIi;wG7)Po{7tdOGpKCH_iAG_-Rp{jY(9M5T4Y^xD2vjq#3-rIq(!NLP6x~y;to%~A z7dA7g-GgX(h&(ledtb?m#Vw=8RT&g~hz9U#WRB(%%AY)_n`X|CVggwyF$)O8lH@j# z%j_sn;fC{n(vUpzgykUT^kPe&Oo)$jm@S?@fv z_9bRK4^<%K+1490Xc~Wh^;(W>yF&=$d14qh3ER!Lkr7KCY`K{E#a^X6C7T2dL*lb?Hk%e&dZ zmRg2DpY!m`qx|jqXqmWZCYu(>BYJI)11%1A_tF*)m(WEPv_exy%zm)k$1M3si^sC@ z5IGnGVnH>^T3I_aibB{=%1x%XZi#o3Bs-g%hfL!GLln=Rg^D=3&Xd8qRUuIT)f#Qt zjsB&aHHCJKS-P1!w5Q*T-+&WT(OkL`3(M0rFJlB)h1q*N=DE&(RCh(4*Lubo5lID( z&OQnq9u6HtZ*NLAz$3zw*i1SGWtkJc+gS@0rUOGja;E;h z4D;$nocmW z;ZvLc>LlB^O+43c{-I!DniSWvIxL%s_K7%p+)JPW4?PNMU*Y(GaFZhGx6$cwl^iPj z99+Yv61H8^@|swm)2EVgv|KP-@~D@D2niNze{K8%M^ngQ;PTiRUA*OP;(rV{*5V~P zH+w#IB`7wKzvBZ>oUAUhiaC8tW0a4FBuaQ2&wZ3_r$6P<&8AnrDY?-V_6&a+r zU`=iK)M~yPR->TSQzk$D)O9SU&A0NCr%ntjU_jSTqE_kg7m6#viYP6wVNquY({v7X zuo-3U0x@#`)F-aDp8oCHZlVVCY+iSZgBq{!4sk|Yz9uBUnu;cLBmLT3vgF7jka2Kw zZYy-ww~KV&=nhDZ<35hv&Uo}WPQ7_3Qu(^b8qTFkqd&gSpH^bf?RJ=iJWi}sRJye( z3x9Sf?S;_<02@2@+qM2A{#%u-4mb|!rF-vq_KTT*-yg8aNMuOvcU`I`>X2`f<78ffyRitVki zU~2iVttD2PAPm)P+ta6Q|G}&Z8K=}jO^bd*NJ*$<<al&Z8>2~2bsB0o%s*rjRmg+tx4Pj@rNdcffmmbaKTFLwNvx*)z$v_yQrv-8-$

(t6d;k}D*BgJ_ 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⋘_p=-$5ie(HWrgz+sLI3$m~mVh?Td%A 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 z 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

nuL#r>q3va%QN3}pa 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