From d05fb9285d90b792da2b50d48f8b9388d8a8d53c Mon Sep 17 00:00:00 2001 From: samithuang <285365963@qq.com> Date: Fri, 25 Nov 2022 08:30:40 +0000 Subject: [PATCH 01/32] update vit yaml and lr epoch stair name --- config.py | 2 +- configs/vit/vit_b32_224_ascend.yaml | 76 +++++++++++++++++++ ...t_b32_224.yaml => vit_l32_224_ascend.yaml} | 24 +++--- mindcv/scheduler/scheduler_factory.py | 14 +++- train.py | 2 +- 5 files changed, 101 insertions(+), 17 deletions(-) create mode 100644 configs/vit/vit_b32_224_ascend.yaml rename configs/vit/{vit_b32_224.yaml => vit_l32_224_ascend.yaml} (77%) diff --git a/config.py b/config.py index 444dac3e..4c12f048 100644 --- a/config.py +++ b/config.py @@ -170,7 +170,7 @@ def create_parser(): help='LR decay rate if scheduler supports') group.add_argument('--multi_step_decay_milestones', type=list, default=[30, 60, 90], help='list of epoch milestones for MultStepDecayLR, decay LR by decay_rate at the milestone epoch.') - group.add_argument('--stepwise_lr_sched', type=str2bool, nargs='?', const=True, default=True, help='If False, LR will be updated in the begin of each new epoch. Otherwise, update learning rate in each step. (default=False)') + group.add_argument('--lr_epoch_stair', type=str2bool, nargs='?', const=True, default=False, help='If True, LR will be updated in the begin of each new epoch and the LR will be consisent for each batch in one epoch. Otherwise, learning rate will be updated dynamically in each step. (default=False)') # Loss parameters group = parser.add_argument_group('Loss parameters') diff --git a/configs/vit/vit_b32_224_ascend.yaml b/configs/vit/vit_b32_224_ascend.yaml new file mode 100644 index 00000000..78a80ff1 --- /dev/null +++ b/configs/vit/vit_b32_224_ascend.yaml @@ -0,0 +1,76 @@ +# Copyright 2022 Huawei Technologies Co., Ltd +# +# 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. +# ============================================================================ + +# system config +mode: 0 +distribute: True +num_parallel_workers: 8 +val_while_train: True +val_interval: 1 + +# dataset config +dataset: 'imagenet' +data_dir: '/data/imagenet' +shuffle: True +dataset_download: False +batch_size: 256 +drop_remainder: True + +# Augmentation config +image_resize: 224 +scale: [0.08, 1.0] +ratio: [0.75, 1.333] +hflip: 0.5 +interpolation: 'bicubic' +re_prob: 0.1 +mixup: 0.2 +cutmix: 1.0 +cutmix_prob: 1.0 +crop_pct: 0.875 +color_jitter: [0.4, 0.4, 0.4] +auto_augment: 'randaug-m7-mstd0.5' + +# model config +model: 'vit_b_32_224' +drop_rate: 0.1 +drop_path_rate: 0.1 +num_classes: 1000 +pretrained: False +ckpt_path: '' +keep_checkpoint_max: 10 +ckpt_save_policy: 'top_k' +ckpt_save_dir: './vit_b32_224' +epoch_size: 300 +dataset_sink_mode: True +amp_level: 'O2' + +# loss config +loss: 'CE' +loss_scale: 1024.0 +label_smoothing: 0.1 + +# lr scheduler config +scheduler: 'warmup_cosine_decay' +lr: 0.003 # diff from 4xGPU +min_lr: 0.00001 +warmup_epochs: 38 +decay_epochs: 262 +lr_epoch_stair: False + +# optimizer config +opt: 'adamw' +weight_decay: 0.025 +filter_bias_and_bn: True +use_nesterov: False diff --git a/configs/vit/vit_b32_224.yaml b/configs/vit/vit_l32_224_ascend.yaml similarity index 77% rename from configs/vit/vit_b32_224.yaml rename to configs/vit/vit_l32_224_ascend.yaml index 68ee825f..df30fe7e 100644 --- a/configs/vit/vit_b32_224.yaml +++ b/configs/vit/vit_l32_224_ascend.yaml @@ -25,7 +25,7 @@ dataset: 'imagenet' data_dir: '/data/imagenet' shuffle: True dataset_download: False -batch_size: 256 +batch_size: 128 # diff from paper drop_remainder: True # Augmentation config @@ -40,18 +40,18 @@ cutmix: 1.0 cutmix_prob: 1.0 crop_pct: 0.875 color_jitter: [0.4, 0.4, 0.4] -#auto_augment: 'randaug-m7-mstd0.5' # 1118 part2 +auto_augment: 'randaug-m7-mstd0.5' # 1118 part2 # model config -model: 'vit_b_32_224' -drop_rate: 0.1 # 1119 add -drop_path_rate: 0.1 # 1119 add +model: 'vit_l_32_224' +drop_rate: 0.1 +drop_path_rate: 0.1 num_classes: 1000 pretrained: False ckpt_path: '' keep_checkpoint_max: 10 ckpt_save_policy: 'top_k' -ckpt_save_dir: './vit_b32_224' +ckpt_save_dir: './vit_l32_224' epoch_size: 300 dataset_sink_mode: True amp_level: 'O2' @@ -63,14 +63,14 @@ label_smoothing: 0.1 # Exp 1119, compared to 1118 part2 # lr scheduler config scheduler: 'warmup_cosine_decay' -lr: 0.00075 # # 0.000125 ~ 0.00025 for bs256x4 on imagentte # 0.0005-0.001 for imagenet on GPU. -min_lr: 0.00001 -warmup_epochs: 38 -decay_epochs: 262 -lr_epoch_stair: True # exp 1118, compare to 1116, better. +lr: 0.003 # +min_lr: 1e-6 +warmup_epochs: 32 +decay_epochs: 418 +lr_epoch_stair: False # optimizer config opt: 'adamw' -weight_decay: 0.025 # 1119 changed from 0.025 to 0.05 TODO :tune +weight_decay: 0.025 filter_bias_and_bn: True use_nesterov: False diff --git a/mindcv/scheduler/scheduler_factory.py b/mindcv/scheduler/scheduler_factory.py index 40233d77..efeef514 100644 --- a/mindcv/scheduler/scheduler_factory.py +++ b/mindcv/scheduler/scheduler_factory.py @@ -14,10 +14,13 @@ def create_scheduler( min_lr: float = 1e-6, warmup_epochs: int = 3, decay_epochs: int = 10, + cooldown_epochs: int = 0, decay_rate: float = 0.9, milestones: list = None, num_epochs: int = 200, - stepwise_sched: bool=True + lr_epoch_stair: bool=False, + cycle_mode: bool=False, + cycle_decay: float=1.0 ): r"""Creates learning rate scheduler by name. @@ -34,7 +37,7 @@ def create_scheduler( decay_rate: LR decay rate (default: 0.9) milestones: list of epoch milestones for multi_step_decay scheduler. Must be increasing. num_epochs: number of total epochs. - stepwise_sched: if False, lr inside an epoch will be the same, the lr is only updated in the beginning of a new epoch. Otherwise, lr will be updated every step. (default: False) + lr_epoch_stair: if False, lr inside an epoch will be the same, the lr is only updated in the beginning of a new epoch. Otherwise, lr will be updated every step. (default: False) Returns: Cell object for computing LR with input of current global steps @@ -42,14 +45,19 @@ def create_scheduler( if milestones is None: milestones = [] + elif scheduler!='multi_step_decay': + print(f"WARNING: milestones is not effective for {scheduler}") if scheduler == 'warmup_cosine_decay': lr_scheduler = WarmupCosineDecayLR(min_lr=min_lr, max_lr=lr, warmup_epochs=warmup_epochs, decay_epochs=decay_epochs, + cooldown_epochs=cooldown_epochs, steps_per_epoch=steps_per_epoch, - step_mode=stepwise_sched + step_mode=not lr_epoch_stair, + cycle_mode=cycle_mode, + cycle_decay=cycle_decay ) elif scheduler == 'exponential_decay': decay_steps = decay_epochs * steps_per_epoch diff --git a/train.py b/train.py index e2049ddf..39eac8f7 100644 --- a/train.py +++ b/train.py @@ -162,7 +162,7 @@ def train(args): decay_rate=args.decay_rate, milestones=args.multi_step_decay_milestones, num_epochs=args.epoch_size, - stepwise_sched=args.stepwise_lr_sched) + lr_epoch_stair=args.lr_epoch_stair) # resume training if ckpt_path is given if args.ckpt_path != '' and args.resume_opt: From 49599db72cb4ae8b2f1bba12f4845abb33d92387 Mon Sep 17 00:00:00 2001 From: samithuang <285365963@qq.com> Date: Fri, 25 Nov 2022 08:31:47 +0000 Subject: [PATCH 02/32] update readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index d08b8cf4..f7457022 100644 --- a/README.md +++ b/README.md @@ -291,6 +291,9 @@ Please see [configs](./configs) for the details about model performance and pret ## Notes ### What is New +- 2022/11/21 +1. Add visualization for loss and acc curves +2. Support epochwise lr warmup cosine decay (previous is stepwise) - 2022/11/09 1. Add 7 pretrained ViT models. 2. Add RandAugment augmentation. From 0ac176125b3b547dbe850f9bdf50c0760bcd681a Mon Sep 17 00:00:00 2001 From: samithuang <285365963@qq.com> Date: Fri, 25 Nov 2022 09:00:15 +0000 Subject: [PATCH 03/32] update param name and description --- config.py | 2 +- mindcv/scheduler/scheduler_factory.py | 19 +++++++------------ train.py | 5 ++--- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/config.py b/config.py index 4c12f048..5ee68b8f 100644 --- a/config.py +++ b/config.py @@ -169,7 +169,7 @@ def create_parser(): group.add_argument('--decay_rate', type=float, default=0.9, help='LR decay rate if scheduler supports') group.add_argument('--multi_step_decay_milestones', type=list, default=[30, 60, 90], - help='list of epoch milestones for MultStepDecayLR, decay LR by decay_rate at the milestone epoch.') + help='list of epoch milestones for lr decay, which is ONLY effective for the multi_step_decay scheduler. LR will be decay by decay_rate at the milestone epoch.') group.add_argument('--lr_epoch_stair', type=str2bool, nargs='?', const=True, default=False, help='If True, LR will be updated in the begin of each new epoch and the LR will be consisent for each batch in one epoch. Otherwise, learning rate will be updated dynamically in each step. (default=False)') # Loss parameters diff --git a/mindcv/scheduler/scheduler_factory.py b/mindcv/scheduler/scheduler_factory.py index efeef514..4df85469 100644 --- a/mindcv/scheduler/scheduler_factory.py +++ b/mindcv/scheduler/scheduler_factory.py @@ -14,13 +14,10 @@ def create_scheduler( min_lr: float = 1e-6, warmup_epochs: int = 3, decay_epochs: int = 10, - cooldown_epochs: int = 0, decay_rate: float = 0.9, milestones: list = None, num_epochs: int = 200, - lr_epoch_stair: bool=False, - cycle_mode: bool=False, - cycle_decay: float=1.0 + lr_epoch_stair: bool=True ): r"""Creates learning rate scheduler by name. @@ -37,27 +34,24 @@ def create_scheduler( decay_rate: LR decay rate (default: 0.9) milestones: list of epoch milestones for multi_step_decay scheduler. Must be increasing. num_epochs: number of total epochs. - lr_epoch_stair: if False, lr inside an epoch will be the same, the lr is only updated in the beginning of a new epoch. Otherwise, lr will be updated every step. (default: False) - + lr_epoch_stair: If True, LR will be updated in the begin of each new epoch and the LR will be consisent for each batch in one epoch. Otherwise, learning rate will be updated dynamically in each step. (default=False) Returns: Cell object for computing LR with input of current global steps """ if milestones is None: milestones = [] - elif scheduler!='multi_step_decay': - print(f"WARNING: milestones is not effective for {scheduler}") + + if warmup_epochs + decay_epochs > num_epochs: + print('====> WARNING: warmup_epochs + decay_epochs > num_epochs. Please check and reduce decay_epochs!') if scheduler == 'warmup_cosine_decay': lr_scheduler = WarmupCosineDecayLR(min_lr=min_lr, max_lr=lr, warmup_epochs=warmup_epochs, decay_epochs=decay_epochs, - cooldown_epochs=cooldown_epochs, steps_per_epoch=steps_per_epoch, - step_mode=not lr_epoch_stair, - cycle_mode=cycle_mode, - cycle_decay=cycle_decay + step_mode=not lr_epoch_stair ) elif scheduler == 'exponential_decay': decay_steps = decay_epochs * steps_per_epoch @@ -97,3 +91,4 @@ def create_scheduler( raise ValueError(f'Invalid scheduler: {scheduler}') return lr_scheduler + diff --git a/train.py b/train.py index fbf7134f..b6e549b4 100644 --- a/train.py +++ b/train.py @@ -183,13 +183,11 @@ def train(args): checkpoint_path=opt_ckpt_path) # Define eval metrics. - if num_classes >= 5: eval_metrics = {'Top_1_Accuracy': nn.Top1CategoricalAccuracy(), 'Top_5_Accuracy': nn.Top5CategoricalAccuracy()} else: eval_metrics = {'Top_1_Accuracy': nn.Top1CategoricalAccuracy()} - eval_name = list(eval_metrics.keys()) # init model if args.loss_scale > 1.0: @@ -215,7 +213,8 @@ def train(args): state_cb = StateMonitor(model, summary_dir=summary_dir, dataset_val=loader_eval, val_interval=args.val_interval, - metric_name=eval_name, + metric_name=list(eval_metrics.keys()) +, ckpt_dir=args.ckpt_save_dir, ckpt_save_interval=args.ckpt_save_interval, best_ckpt_name=args.model + '_best.ckpt', From 7434673c7b64e46cd8b288004ca9110a2eec9088 Mon Sep 17 00:00:00 2001 From: Samit <285365963@qq.com> Date: Fri, 25 Nov 2022 17:23:30 +0800 Subject: [PATCH 04/32] Update scheduler_factory.py --- mindcv/scheduler/scheduler_factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mindcv/scheduler/scheduler_factory.py b/mindcv/scheduler/scheduler_factory.py index 4df85469..53c08a2b 100644 --- a/mindcv/scheduler/scheduler_factory.py +++ b/mindcv/scheduler/scheduler_factory.py @@ -17,7 +17,7 @@ def create_scheduler( decay_rate: float = 0.9, milestones: list = None, num_epochs: int = 200, - lr_epoch_stair: bool=True + lr_epoch_stair: bool=False ): r"""Creates learning rate scheduler by name. From d458a4370fe03915ea0da9d4f0130dbeaf9add3f Mon Sep 17 00:00:00 2001 From: Genius Patrick Date: Sat, 26 Nov 2022 15:30:31 +0800 Subject: [PATCH 05/32] add "polynomial_lr", "exponential_lr", " step_lr" and "cosine_decay" to dynamic_lr --- mindcv/scheduler/dynamic_lr.py | 146 ++++++++++++++++++++++++++------ tests/modules/test_scheduler.py | 35 ++++++++ 2 files changed, 155 insertions(+), 26 deletions(-) diff --git a/mindcv/scheduler/dynamic_lr.py b/mindcv/scheduler/dynamic_lr.py index 5b0ee15d..bb524004 100644 --- a/mindcv/scheduler/dynamic_lr.py +++ b/mindcv/scheduler/dynamic_lr.py @@ -1,3 +1,24 @@ +"""Meta learning rate scheduler. + +This module implements exactly the same learning rate scheduler as native PyTorch, +see `"torch.optim.lr_scheduler" `_. +At present, only `constant_lr`, `linear_lr`, `polynomial_lr`, `exponential_lr`, `step_lr`, `multi_step_lr`, +`cosine_annealing_lr`, `cosine_annealing_warm_restarts_lr` are implemented. The number, name and usage of +the Positional Arguments are exactly the same as those of native PyTorch. + +However, due to the constraint of having to explicitly return the learning rate at each step, we have to +introduce additional Keyword Arguments. There are only three Keyword Arguments introduced, +namely `lr`, `steps_per_epoch` and `epochs`, explained as follows: +`lr`: the basic learning rate when creating optim in torch. +`steps_per_epoch`: the number of steps(iterations) of each epoch. +`epochs`: the number of epoch. It and `steps_per_epoch` determine the length of the returned lrs. + +Since most scheduler in PyTorch are coarse-grained, that is the learning rate is constant within a single epoch. +For non-stepwise scheduler, we introduce several fine-grained variation, that is the learning rate +is also changed within a single epoch. The function name of these variants have the `refined` keyword. +The implemented fine-grained variation are list as follows: `linear_refined_lr`, `polynomial_refined_lr`, etc. + +""" import math from bisect import bisect_right @@ -32,11 +53,57 @@ def linear_refined_lr(start_factor, end_factor, total_iters, *, lr, steps_per_ep start_lr = lr * start_factor end_lr = lr * end_factor for i in range(steps): - multiplier = min(i, total_iters * steps_per_epoch) / total_iters / steps_per_epoch + epoch_idx = i / steps_per_epoch + multiplier = min(epoch_idx, total_iters) / total_iters lrs.append(start_lr + multiplier * (end_lr - start_lr)) return lrs +def polynomial_lr(total_iters, power, *, lr, steps_per_epoch, epochs): + steps = steps_per_epoch * epochs + lrs = [] + for i in range(steps): + epoch_idx = math.floor(i / steps_per_epoch) + lrs.append(lr * (1 - min(epoch_idx, total_iters) / total_iters) ** power) + return lrs + + +def polynomial_refined_lr(total_iters, power, *, lr, steps_per_epoch, epochs): + steps = steps_per_epoch * epochs + lrs = [] + for i in range(steps): + epoch_idx = i / steps_per_epoch + lrs.append(lr * (1 - min(epoch_idx, total_iters) / total_iters) ** power) + return lrs + + +def exponential_lr(gamma, *, lr, steps_per_epoch, epochs): + steps = steps_per_epoch * epochs + lrs = [] + for i in range(steps): + epoch_idx = math.floor(i / steps_per_epoch) + lrs.append(lr * gamma ** epoch_idx) + return lrs + + +def exponential_refined_lr(gamma, *, lr, steps_per_epoch, epochs): + steps = steps_per_epoch * epochs + lrs = [] + for i in range(steps): + epoch_idx = i / steps_per_epoch + lrs.append(lr * gamma ** epoch_idx) + return lrs + + +def step_lr(step_size, gamma, *, lr, steps_per_epoch, epochs): + steps = steps_per_epoch * epochs + lrs = [] + for i in range(steps): + epoch_idx = math.floor(i / steps_per_epoch) + lrs.append(lr * gamma ** math.floor(epoch_idx / step_size)) + return lrs + + def multi_step_lr(milestones, gamma, *, lr, steps_per_epoch, epochs): steps = steps_per_epoch * epochs milestones = sorted(milestones) @@ -47,6 +114,28 @@ def multi_step_lr(milestones, gamma, *, lr, steps_per_epoch, epochs): return lrs +def cosine_decay_lr(t_max, eta_min, *, eta_max, steps_per_epoch, epochs): + steps = steps_per_epoch * epochs + delta = 0.5 * (eta_max - eta_min) + lrs = [] + for i in range(steps): + t_cur = math.floor(i / steps_per_epoch) + t_cur = min(t_cur, t_max) + lrs.append(eta_min + delta * (1.0 + math.cos(math.pi * t_cur / t_max))) + return lrs + + +def cosine_decay_refined_lr(t_max, eta_min, *, eta_max, steps_per_epoch, epochs): + steps = steps_per_epoch * epochs + delta = 0.5 * (eta_max - eta_min) + lrs = [] + for i in range(steps): + t_cur = i / steps_per_epoch + t_cur = min(t_cur, t_max) + lrs.append(eta_min + delta * (1.0 + math.cos(math.pi * t_cur / t_max))) + return lrs + + def cosine_annealing_lr(t_max, eta_min, *, eta_max, steps_per_epoch, epochs): steps = steps_per_epoch * epochs delta = 0.5 * (eta_max - eta_min) @@ -76,32 +165,37 @@ def cosine_annealing_warm_restarts_lr(te, tm, eta_min, *, eta_max, steps_per_epo if __name__ == '__main__': - # Demonstrate how these schedulers work by printing returned list. - print(constant_lr(0.5, 4, lr=0.05, steps_per_epoch=2, epochs=10)) - print(linear_lr(0.5, 1.0, 4, lr=0.05, steps_per_epoch=2, epochs=10)) - print(linear_refined_lr(0.5, 1.0, 4, lr=0.05, steps_per_epoch=2, epochs=10)) - print(multi_step_lr([3, 6], 0.5, lr=0.05, steps_per_epoch=2, epochs=10)) - print(cosine_annealing_lr(5, 0.0, eta_max=1.0, steps_per_epoch=2, epochs=15)) - print(cosine_annealing_warm_restarts_lr(5, 2, 0.0, eta_max=1.0, steps_per_epoch=2, epochs=15)) - - # Demonstrate how these schedulers work by visualizing the returned list. + # Demonstrate how these schedulers work by printing & visualizing the returned list. import matplotlib.pyplot as plt - fig = plt.figure() - for ax_idx, (title, lrs_ms) in enumerate([ - ("constant_lr", constant_lr(0.5, 4, lr=0.05, steps_per_epoch=2, epochs=10)), - ("multi_step_lr", multi_step_lr([3, 6], 0.5, lr=0.05, steps_per_epoch=2, epochs=10)), - ("linear_lr", linear_lr(0.5, 1.0, 4, lr=0.05, steps_per_epoch=2, epochs=10)), - ("linear_refined_lr", linear_refined_lr(0.5, 1.0, 4, lr=0.05, steps_per_epoch=2, epochs=10)), - ], start=1): - ax = plt.subplot(2, 2, ax_idx) - ax.plot(lrs_ms, marker="*") - ax.set_title(title) - ax.set_xlim(0, 20) - ax.set_xlabel("step") - ax.set_ylabel("lr") - fig.suptitle("lr=0.05, steps_per_epoch=2, epochs=10") - plt.tight_layout() - + table = ( + (("constant_lr", constant_lr(0.5, 4, lr=0.05, steps_per_epoch=2, epochs=10),),), + (("linear_lr", linear_lr(0.5, 1.0, 4, lr=0.05, steps_per_epoch=2, epochs=10)), + ("linear_refined_lr", linear_refined_lr(0.5, 1.0, 4, lr=0.05, steps_per_epoch=2, epochs=10)),), + (("polynomial_lr", polynomial_lr(4, 1.0, lr=0.05, steps_per_epoch=2, epochs=10)), + ("polynomial_refined_lr", polynomial_refined_lr(4, 1.0, lr=0.05, steps_per_epoch=2, epochs=10)),), + (("exponential_lr", exponential_lr(0.9, lr=0.05, steps_per_epoch=2, epochs=10)), + ("exponential_refined_lr", exponential_refined_lr(0.9, lr=0.05, steps_per_epoch=2, epochs=10)),), + (("step_lr", step_lr(3, 0.5, lr=0.05, steps_per_epoch=2, epochs=10)), + ("multi_step_lr", multi_step_lr([3, 6], 0.5, lr=0.05, steps_per_epoch=2, epochs=10)),), + (("cosine_decay_lr", cosine_decay_lr(5, 1.0, eta_max=2.0, steps_per_epoch=2, epochs=10)), + ("cosine_decay_refined_lr", cosine_decay_refined_lr(5, 1.0, eta_max=2.0, steps_per_epoch=2, epochs=10)),), + (("cosine_annealing_lr", cosine_annealing_lr(5, 0.0, eta_max=1.0, steps_per_epoch=2, epochs=15)), + ("cosine_annealing_warm_restarts_lr", cosine_annealing_warm_restarts_lr(5, 2, 0.0, eta_max=1.0, steps_per_epoch=2, epochs=15)),) + ) + for variants in table: + n_variants = len(variants) + fig = plt.figure(figsize=(4, 3 * n_variants)) + for ax_idx, (title, lrs_ms) in enumerate(variants, start=1): + print(f"name: {title}\nlrs: {lrs_ms}") + ax = plt.subplot(n_variants, 1, ax_idx) + ax.plot(lrs_ms, marker="*") + ax.set_title(title) + ax.set_xlim(0, len(lrs_ms)) # n_steps + ax.set_xlabel("step") + ax.set_ylabel("lr") + plt.tight_layout() + + # Compare the difference between cosine_annealing_lr and cosine_annealing_warm_restarts_lr. plt.figure() lrs_ms = cosine_annealing_lr(5, 0.0, eta_max=1.0, steps_per_epoch=10, epochs=35) plt.plot(lrs_ms) diff --git a/tests/modules/test_scheduler.py b/tests/modules/test_scheduler.py index 5e7438fa..9b954306 100644 --- a/tests/modules/test_scheduler.py +++ b/tests/modules/test_scheduler.py @@ -146,6 +146,41 @@ def test_scheduler_dynamic(): lrs_ms = dynamic_lr.linear_refined_lr(0.5, 1.0, 4, lr=0.05, steps_per_epoch=2, epochs=10) assert np.allclose(lrs_ms, lrs_manually) + # polynomial_lr + lrs_manually = [0.05, 0.05, 0.0375, 0.0375, 0.025, 0.025, 0.0125, 0.0125, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + lrs_ms = dynamic_lr.polynomial_lr(4, 1.0, lr=0.05, steps_per_epoch=2, epochs=10) + assert np.allclose(lrs_ms, lrs_manually) + + # polynomial_refined_lr + lrs_manually = [0.05, 0.04375, 0.0375, 0.03125, 0.025, 0.01875, 0.0125, 0.00625, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + lrs_ms = dynamic_lr.polynomial_refined_lr(4, 1.0, lr=0.05, steps_per_epoch=2, epochs=10) + assert np.allclose(lrs_ms, lrs_manually) + + # exponential_lr + lrs_manually = [0.05, 0.05, 0.045, 0.045, 0.0405, 0.0405, + 0.03645, 0.03645, 0.032805, 0.032805, 0.0295245, 0.0295245, + 0.02657205, 0.02657205, 0.023914845, 0.023914845, + 0.0215233605, 0.0215233605, 0.01937102445, 0.01937102445] + lrs_ms = dynamic_lr.exponential_lr(0.9, lr=0.05, steps_per_epoch=2, epochs=10) + assert np.allclose(lrs_ms, lrs_manually) + + # exponential_refined_lr + lrs_manually = [0.05, 0.047434164902525694, 0.045, 0.042690748412273126, 0.0405, 0.03842167357104581, + 0.03645, 0.03457950621394123, 0.032805, 0.031121555592547107, 0.0295245, 0.0280094000332924, + 0.02657205, 0.02520846002996316, 0.023914845, 0.022687614026966844, + 0.0215233605, 0.02041885262427016, 0.01937102445, 0.018376967361843143] + lrs_ms = dynamic_lr.exponential_refined_lr(0.9, lr=0.05, steps_per_epoch=2, epochs=10) + assert np.allclose(lrs_ms, lrs_manually) + + # step_lr + lrs_manually = [0.05, 0.05, 0.05, 0.05, 0.05, 0.05, + 0.025, 0.025, 0.025, 0.025, 0.025, 0.025, + 0.0125, 0.0125, 0.0125, 0.0125, 0.0125, 0.0125, 0.00625, 0.00625] + lrs_ms = dynamic_lr.step_lr(3, 0.5, lr=0.05, steps_per_epoch=2, epochs=10) + assert np.allclose(lrs_ms, lrs_manually) + # multi_step_lr lrs_manually = [0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.025, 0.025, 0.025, 0.025, 0.025, 0.025, From 24ffa854e004c1751afd22edfcaeb65ee91bec42 Mon Sep 17 00:00:00 2001 From: Genius Patrick Date: Sat, 26 Nov 2022 15:38:22 +0800 Subject: [PATCH 06/32] migrate scheduler_factory using dynamic_lr --- config.py | 9 +- mindcv/scheduler/scheduler_factory.py | 101 +++++++++++----------- quick_start.ipynb | 2 +- quick_start.md | 2 +- tests/modules/test_scheduler.py | 117 -------------------------- train.py | 1 + train_with_func.py | 1 + tutorials/learn_about_config.ipynb | 7 +- tutorials/learn_about_config.md | 7 +- 9 files changed, 65 insertions(+), 182 deletions(-) diff --git a/config.py b/config.py index 5ee68b8f..3f423a4d 100644 --- a/config.py +++ b/config.py @@ -155,8 +155,8 @@ def create_parser(): # Scheduler parameters group = parser.add_argument_group('Scheduler parameters') - group.add_argument('--scheduler', type=str, default='warmup_cosine_decay', - choices=['constant', 'warmup_cosine_decay', 'exponential_decay', 'step_decay', 'multi_step_decay'], + group.add_argument('--scheduler', type=str, default='cosine_decay', + choices=['constant', 'cosine_decay', 'exponential_decay', 'step_decay', 'multi_step_decay'], help='Type of scheduler (default="warmup_consine_decay")') group.add_argument('--lr', type=float, default=0.001, help='learning rate (default=0.001)') @@ -164,13 +164,16 @@ def create_parser(): help='The minimum value of learning rate if scheduler supports (default=None)') group.add_argument('--warmup_epochs', type=int, default=3, help='Warmup epochs (default=None)') + group.add_argument('--warmup_factor', type=float, default=0.0, + help='Warmup factor of learning rate (default=0.0)') group.add_argument('--decay_epochs', type=int, default=100, help='Decay epochs (default=None)') group.add_argument('--decay_rate', type=float, default=0.9, help='LR decay rate if scheduler supports') group.add_argument('--multi_step_decay_milestones', type=list, default=[30, 60, 90], help='list of epoch milestones for lr decay, which is ONLY effective for the multi_step_decay scheduler. LR will be decay by decay_rate at the milestone epoch.') - group.add_argument('--lr_epoch_stair', type=str2bool, nargs='?', const=True, default=False, help='If True, LR will be updated in the begin of each new epoch and the LR will be consisent for each batch in one epoch. Otherwise, learning rate will be updated dynamically in each step. (default=False)') + group.add_argument('--lr_epoch_stair', type=str2bool, nargs='?', const=True, default=False, + help='If True, LR will be updated in the begin of each new epoch and the LR will be consisent for each batch in one epoch. Otherwise, learning rate will be updated dynamically in each step. (default=False)') # Loss parameters group = parser.add_argument_group('Loss parameters') diff --git a/mindcv/scheduler/scheduler_factory.py b/mindcv/scheduler/scheduler_factory.py index 53c08a2b..8ddc641f 100644 --- a/mindcv/scheduler/scheduler_factory.py +++ b/mindcv/scheduler/scheduler_factory.py @@ -1,8 +1,8 @@ """Scheduler Factory""" -from mindspore.nn import ExponentialDecayLR, PolynomialDecayLR - -from .warmup_cosine_decay_lr import WarmupCosineDecayLR -from .multi_step_decay_lr import MultiStepDecayLR +from .dynamic_lr import (constant_lr, linear_lr, linear_refined_lr, polynomial_lr, polynomial_refined_lr, + exponential_lr, exponential_refined_lr, step_lr, multi_step_lr, + cosine_decay_lr, cosine_decay_refined_lr, + cosine_annealing_lr, cosine_annealing_warm_restarts_lr) __all__ = ["create_scheduler"] @@ -13,82 +13,79 @@ def create_scheduler( lr: float = 0.01, min_lr: float = 1e-6, warmup_epochs: int = 3, + warmup_factor: float = 0.0, decay_epochs: int = 10, decay_rate: float = 0.9, milestones: list = None, num_epochs: int = 200, - lr_epoch_stair: bool=False + lr_epoch_stair: bool = False ): r"""Creates learning rate scheduler by name. Args: steps_per_epoch: number of steps per epoch. - scheduler: scheduler name like 'constant', 'warmup_cosine_decay', 'step_decay', + scheduler: scheduler name like 'constant', 'cosine_decay', 'step_decay', 'exponential_decay', 'polynomial_decay', 'multi_step_decay'. Default: 'constant'. lr: learning rate value. Default: 0.01. - min_lr: lower lr bound for cyclic/cosine/polynomial schedulers. Default: 1e-6. + min_lr: lower lr bound for 'cosine_decay' schedulers. Default: 1e-6. warmup_epochs: epochs to warmup LR, if scheduler supports. Default: 3. - decay_epochs: epochs to decay LR to min_lr for cyclic and polynomial schedulers. - decay LR by a factor of decay_rate every `decay_epochs` for exponential scheduler and step LR scheduler. - Default: 10. + warmup_factor: the warmup phase of scheduler is a linearly increasing lr, + the beginning factor is `warmup_factor`, i.e., the lr of the first step/epoch is lr*warmup_factor, + and the ending lr in the warmup phase is lr. Default: 0.0 + decay_epochs: for 'cosine_decay' schedulers, decay LR to min_lr in `decay_epochs`. + For 'step_decay' scheduler, decay LR by a factor of `decay_rate` every `decay_epochs`. Default: 10. decay_rate: LR decay rate (default: 0.9) - milestones: list of epoch milestones for multi_step_decay scheduler. Must be increasing. + milestones: list of epoch milestones for 'multi_step_decay' scheduler. Must be increasing. num_epochs: number of total epochs. - lr_epoch_stair: If True, LR will be updated in the begin of each new epoch and the LR will be consisent for each batch in one epoch. Otherwise, learning rate will be updated dynamically in each step. (default=False) + lr_epoch_stair: If True, LR will be updated in the beginning of each new epoch + and the LR will be consistent for each batch in one epoch. + Otherwise, learning rate will be updated dynamically in each step. (default=False) Returns: Cell object for computing LR with input of current global steps """ - + # check params if milestones is None: milestones = [] if warmup_epochs + decay_epochs > num_epochs: - print('====> WARNING: warmup_epochs + decay_epochs > num_epochs. Please check and reduce decay_epochs!') + print('[WARNING]: warmup_epochs + decay_epochs > num_epochs. Please check and reduce decay_epochs!') - if scheduler == 'warmup_cosine_decay': - lr_scheduler = WarmupCosineDecayLR(min_lr=min_lr, - max_lr=lr, - warmup_epochs=warmup_epochs, - decay_epochs=decay_epochs, - steps_per_epoch=steps_per_epoch, - step_mode=not lr_epoch_stair - ) + # lr warmup phase + warmup_lr_scheduler = [] + if warmup_epochs > 0: + if warmup_factor == 0 and lr_epoch_stair: + print("[WARNING]: The warmup factor is set to 0, lr of 0-th epoch is always zero! " + "Recommend value is 0.01.") + warmup_func = linear_lr if lr_epoch_stair else linear_refined_lr + warmup_lr_scheduler = warmup_func(start_factor=warmup_factor, end_factor=1.0, total_iters=warmup_epochs, + lr=lr, steps_per_epoch=steps_per_epoch, epochs=warmup_epochs) + + # lr decay phase + main_epochs = num_epochs - warmup_epochs + if scheduler == 'cosine_decay': + cosine_func = cosine_decay_lr if lr_epoch_stair else cosine_decay_refined_lr + main_lr_scheduler = cosine_func(t_max=decay_epochs, eta_min=min_lr, + eta_max=lr, steps_per_epoch=steps_per_epoch, epochs=main_epochs) elif scheduler == 'exponential_decay': - decay_steps = decay_epochs * steps_per_epoch - lr_scheduler = ExponentialDecayLR(lr, - decay_rate, - decay_steps, - is_stair=False - ) + exponential_func = exponential_lr if lr_epoch_stair else exponential_refined_lr + main_lr_scheduler = exponential_func(gamma=decay_rate, + lr=lr, steps_per_epoch=steps_per_epoch, epochs=main_epochs) elif scheduler == 'polynomial_decay': - decay_steps = decay_epochs * steps_per_epoch - lr_scheduler = PolynomialDecayLR(lr, - min_lr, # end_learning_rate - decay_steps, - power=decay_rate, # overload decay_rate as polynomial power - update_decay_steps=False) - + polynomial_func = polynomial_lr if lr_epoch_stair else polynomial_refined_lr + main_lr_scheduler = polynomial_func(total_iters=main_epochs, power=decay_rate, + lr=lr, steps_per_epoch=steps_per_epoch, epochs=main_epochs) elif scheduler == 'step_decay': - decay_steps = decay_epochs * steps_per_epoch - # decay LR by decay_rate every `decay_steps` - lr_scheduler = ExponentialDecayLR(lr, - decay_rate, - decay_steps, - is_stair=True - ) + main_lr_scheduler = step_lr(step_size=decay_epochs, gamma=decay_rate, + lr=lr, steps_per_epoch=steps_per_epoch, epochs=main_epochs) elif scheduler == 'multi_step_decay': - lr_scheduler = MultiStepDecayLR(lr, - warmup_epochs, - decay_rate, - milestones, - steps_per_epoch, - num_epochs, - ) - + main_lr_scheduler = multi_step_lr(milestones=milestones, gamma=decay_rate, + lr=lr, steps_per_epoch=steps_per_epoch, epochs=main_epochs) elif scheduler == 'constant': - lr_scheduler = lr + main_lr_scheduler = [lr for _ in range(steps_per_epoch * main_epochs)] else: raise ValueError(f'Invalid scheduler: {scheduler}') - return lr_scheduler + # combine + lr_scheduler = warmup_lr_scheduler + main_lr_scheduler + return lr_scheduler diff --git a/quick_start.ipynb b/quick_start.ipynb index 42a9e694..546e99a8 100644 --- a/quick_start.ipynb +++ b/quick_start.ipynb @@ -278,7 +278,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "使用`create_scheduler`接口设置学习率策略(warmup_consine_decay)。" + "使用`create_scheduler`接口设置学习率策略。" ] }, { diff --git a/quick_start.md b/quick_start.md index d791474d..f01cce63 100644 --- a/quick_start.md +++ b/quick_start.md @@ -161,7 +161,7 @@ from mindcv.loss import create_loss loss = create_loss(name='CE') ``` -使用`create_scheduler`接口设置学习率策略(warmup_consine_decay)。 +使用`create_scheduler`接口设置学习率策略。 ```python diff --git a/tests/modules/test_scheduler.py b/tests/modules/test_scheduler.py index 9b954306..7483b440 100644 --- a/tests/modules/test_scheduler.py +++ b/tests/modules/test_scheduler.py @@ -10,123 +10,6 @@ from mindcv.scheduler import dynamic_lr -@pytest.mark.parametrize('decay_epochs', [5, 10]) -@pytest.mark.parametrize('steps_per_epoch', [100, 200]) -@pytest.mark.parametrize('decay_rate', [0.1, 0.5]) -@pytest.mark.parametrize('min_lr', [0.00001, 0.00005]) -@pytest.mark.parametrize('lr', [0.1, 0.001]) -@pytest.mark.parametrize('sched', ['polynomial_decay', 'exponential_decay', 'step_decay']) -def test_scheduler_poly_exp_step(sched, lr, min_lr, decay_rate, steps_per_epoch, decay_epochs): - - warmup_epochs = 0 - - scheduler = create_scheduler(steps_per_epoch, sched, lr=lr, min_lr=min_lr, warmup_epochs=warmup_epochs, - decay_epochs=decay_epochs, decay_rate=decay_rate) - - # check warmup - # check decay trend - cur_epoch = warmup_epochs + 1 - global_step = Tensor(cur_epoch * steps_per_epoch, ms.int32) - - - if sched in ['step_decay']: - cur_epoch = decay_epochs - global_step = Tensor(cur_epoch * steps_per_epoch, ms.int32) - cur_lr = scheduler(global_step) - else: - cur_epoch = warmup_epochs + 1 - global_step = Tensor(cur_epoch * steps_per_epoch, ms.int32) - cur_lr = scheduler(global_step) - assert cur_lr < lr, 'lr does NOT decrease' - - - # check value correctness - cur_epoch = warmup_epochs - global_step = Tensor(cur_epoch * steps_per_epoch, ms.int32) - cur_lr = scheduler(global_step) - cur_lr = Tensor.asnumpy(cur_lr) - assert abs(cur_lr - lr) < 1e-6, 'Incorrect lr' - - -@pytest.mark.parametrize('decay_epochs', [10]) -@pytest.mark.parametrize('steps_per_epoch', [200]) -@pytest.mark.parametrize('decay_rate', [0.5]) -@pytest.mark.parametrize('min_lr', [0.00001]) -@pytest.mark.parametrize('lr', [0.1, 0.001]) -@pytest.mark.parametrize('sched', ['constant']) -def test_scheduler_cons(sched, lr, min_lr, decay_rate, steps_per_epoch, decay_epochs): - - scheduler = create_scheduler(steps_per_epoch, sched, lr=lr, min_lr=min_lr, - decay_epochs=decay_epochs, decay_rate=decay_rate) - - cur_lr = scheduler - assert cur_lr == lr, 'lr is NOT constant' - - -@pytest.mark.parametrize('milestones', [[1, 3, 5, 7], [2, 4, 6, 8]]) -@pytest.mark.parametrize('steps_per_epoch', [100, 200]) -@pytest.mark.parametrize('decay_rate', [0.1, 0.5]) -@pytest.mark.parametrize('lr', [0.1, 0.001]) -@pytest.mark.parametrize('sched', ['multi_step_decay']) -def test_scheduler_multi_step(sched, lr, decay_rate, steps_per_epoch, milestones): - - warmup_epochs = 0 - - scheduler = create_scheduler(steps_per_epoch, sched, lr=lr, warmup_epochs=warmup_epochs, - decay_rate=decay_rate, milestones=milestones) - - # check decay trend - cur_epoch = warmup_epochs + 2 - global_step = cur_epoch * steps_per_epoch +1 - cur_lr = scheduler(global_step) - assert cur_lr < lr, 'lr does NOT decrease' - - # check value correctness - - global_step = milestones[3] * steps_per_epoch + 1 - cur_lr = scheduler(global_step) - x = cur_lr - y = lr * decay_rate ** 4 - assert (abs(x - y) / (0.5 * (x + y))) < 0.001, 'Incorrect lr' - - - -@pytest.mark.parametrize('warmup_epochs', [3, 10]) -@pytest.mark.parametrize('decay_epochs', [20, 50]) -@pytest.mark.parametrize('steps_per_epoch', [100, 200]) -@pytest.mark.parametrize('min_lr', [0.00001, 0.00005]) -@pytest.mark.parametrize('lr', [0.1, 0.001]) -@pytest.mark.parametrize('sched', ['warmup_cosine_decay']) -def test_scheduler_warm_cos(sched, lr, min_lr, steps_per_epoch, decay_epochs, warmup_epochs): - - warmup_epochs = warmup_epochs - - scheduler = create_scheduler(steps_per_epoch, sched, lr=lr, min_lr=min_lr, warmup_epochs=warmup_epochs, - decay_epochs=decay_epochs) - - # check warmup - global_step = Tensor(1, ms.int32) - warmup_epoch = warmup_epochs - warmup_step = Tensor(warmup_epoch * steps_per_epoch, ms.int32) - - cur_lr = scheduler(global_step) - warmup_lr = scheduler(warmup_step) - assert cur_lr < warmup_lr, "lr warmup is NOT incorrect" - - # check decay trend - cur_epoch = warmup_epochs + 1 - global_step = Tensor(cur_epoch * steps_per_epoch, ms.int32) - cur_lr = scheduler(global_step) - assert cur_lr < lr, 'lr does NOT decrease' - - # check value correctness - cur_epoch = warmup_epochs - global_step = Tensor(cur_epoch * steps_per_epoch, ms.int32) - - cur_lr = scheduler(global_step) - assert cur_lr == lr, 'Incorrect lr' - - def test_scheduler_dynamic(): # constant_lr lrs_manually = [0.025, 0.025, 0.025, 0.025, 0.025, 0.025, 0.025, 0.025, diff --git a/train.py b/train.py index b6e549b4..f045778d 100644 --- a/train.py +++ b/train.py @@ -158,6 +158,7 @@ def train(args): lr=args.lr, min_lr=args.min_lr, warmup_epochs=args.warmup_epochs, + warmup_factor=args.warmup_factor, decay_epochs=args.decay_epochs, decay_rate=args.decay_rate, milestones=args.multi_step_decay_milestones, diff --git a/train_with_func.py b/train_with_func.py index 0597bb9a..adad8db7 100644 --- a/train_with_func.py +++ b/train_with_func.py @@ -162,6 +162,7 @@ def train(args): lr=args.lr, min_lr=args.min_lr, warmup_epochs=args.warmup_epochs, + warmup_factor=args.warmup_factor, decay_epochs=args.decay_epochs, decay_rate=args.decay_rate, milestones=args.multi_step_decay_milestones, diff --git a/tutorials/learn_about_config.ipynb b/tutorials/learn_about_config.ipynb index a2b15c82..43fb0336 100644 --- a/tutorials/learn_about_config.ipynb +++ b/tutorials/learn_about_config.ipynb @@ -307,7 +307,7 @@ "2. yaml文件样例\n", "\n", "```text\n", - "scheduler: 'warmup_cosine_decay'\n", + "scheduler: 'cosine_decay'\n", "min_lr: 0.0\n", "lr: 0.01\n", "warmup_epochs: 0\n", @@ -318,7 +318,7 @@ "3. parse参数设置\n", "\n", "```text\n", - "python train.py ... --scheduler warmup_cosine_decay --min_lr 0.0 --lr 0.01 \\\n", + "python train.py ... --scheduler cosine_decay --min_lr 0.0 --lr 0.01 \\\n", " --warmup_epochs 0 --decay_epochs 200 ...\n", "```\n", "\n", @@ -332,8 +332,7 @@ " lr=args.lr,\n", " min_lr=args.min_lr,\n", " warmup_epochs=args.warmup_epochs,\n", - " decay_epochs=args.decay_epochs,\n", - " decay_rate=args.decay_rate)\n", + " decay_epochs=args.decay_epochs)\n", " ...\n", "```\n", "\n", diff --git a/tutorials/learn_about_config.md b/tutorials/learn_about_config.md index fafd61cd..83bd2d1e 100644 --- a/tutorials/learn_about_config.md +++ b/tutorials/learn_about_config.md @@ -300,7 +300,7 @@ def train(args): 2. yaml文件样例 ```text -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.01 warmup_epochs: 0 @@ -311,7 +311,7 @@ decay_epochs: 200 3. parse参数设置 ```text -python train.py ... --scheduler warmup_cosine_decay --min_lr 0.0 --lr 0.01 \ +python train.py ... --scheduler cosine_decay --min_lr 0.0 --lr 0.01 \ --warmup_epochs 0 --decay_epochs 200 ... ``` @@ -325,8 +325,7 @@ def train(args): lr=args.lr, min_lr=args.min_lr, warmup_epochs=args.warmup_epochs, - decay_epochs=args.decay_epochs, - decay_rate=args.decay_rate) + decay_epochs=args.decay_epochs) ... ``` From c0aa519f4edac11301c08a8836c4151f85ae49cd Mon Sep 17 00:00:00 2001 From: Genius Patrick Date: Sat, 26 Nov 2022 15:38:41 +0800 Subject: [PATCH 07/32] migrate 'warmup_cosine_decay' to 'cosine_decay' in existing yaml configs --- configs/convit/convit_tiny_ascend.yaml | 2 +- configs/convit/convit_tiny_gpu.yaml | 2 +- configs/convit/convit_tiny_plus_ascend.yaml | 2 +- configs/densenet/densenet_121_ascend.yaml | 2 +- configs/densenet/densenet_121_gpu.yaml | 2 +- configs/densenet/densenet_161_ascend.yaml | 2 +- configs/densenet/densenet_161_gpu.yaml | 2 +- configs/densenet/densenet_169_ascend.yaml | 2 +- configs/densenet/densenet_169_gpu.yaml | 2 +- configs/densenet/densenet_201_ascend.yaml | 2 +- configs/densenet/densenet_201_gpu.yaml | 2 +- configs/mobilenetv1/mobilenetv1_025_ascend.yaml | 2 +- configs/mobilenetv1/mobilenetv1_025_gpu.yaml | 2 +- configs/mobilenetv1/mobilenetv1_050_ascend.yaml | 2 +- configs/mobilenetv1/mobilenetv1_050_gpu.yaml | 2 +- configs/mobilenetv1/mobilenetv1_075_ascend.yaml | 2 +- configs/mobilenetv1/mobilenetv1_075_gpu.yaml | 2 +- configs/mobilenetv1/mobilenetv1_100_ascend.yaml | 2 +- configs/mobilenetv1/mobilenev1_100_gpu.yaml | 2 +- configs/mobilenetv2/mobilenetv2_075_ascend.yaml | 2 +- configs/mobilenetv2/mobilenetv2_100_ascend.yaml | 2 +- configs/mobilenetv2/mobilenetv2_140_ascend.yaml | 2 +- configs/mobilenetv3/v3_large_Ascend.yaml | 2 +- configs/mobilenetv3/v3_small_Ascend.yaml | 2 +- configs/res2net/res2net_101-v1b_ascend.yaml | 2 +- configs/res2net/res2net_101-v1b_gpu.yaml | 2 +- configs/res2net/res2net_50-v1b_ascend.yaml | 2 +- configs/res2net/res2net_50-v1b_gpu.yaml | 2 +- configs/res2net/res2net_50_ascend.yaml | 2 +- configs/res2net/res2net_50_gpu.yaml | 2 +- configs/resnet/resnet_101_ascend.yaml | 2 +- configs/resnet/resnet_101_gpu.yaml | 2 +- configs/resnet/resnet_152_ascend.yaml | 2 +- configs/resnet/resnet_152_gpu.yaml | 2 +- configs/resnet/resnet_18_ascend.yaml | 2 +- configs/resnet/resnet_18_gpu.yaml | 2 +- configs/resnet/resnet_34_ascend.yaml | 2 +- configs/resnet/resnet_34_gpu.yaml | 2 +- configs/resnet/resnet_50_ascend.yaml | 2 +- configs/resnet/resnet_50_gpu.yaml | 2 +- configs/rexnet/rexnet_x10.yaml | 2 +- configs/shufflenet_v1/shufflenet_v1_0.5_ascend.yaml | 2 +- configs/shufflenet_v1/shufflenet_v1_1.0_ascend.yaml | 2 +- configs/shufflenet_v1/shufflenet_v1_1.5_ascend.yaml | 2 +- configs/shufflenet_v1/shufflenet_v1_2.0_ascend.yaml | 2 +- configs/shufflenet_v2/shufflenet_v2_x0.5_ascend.yaml | 2 +- configs/shufflenet_v2/shufflenet_v2_x1.0_ascend.yaml | 2 +- configs/shufflenet_v2/shufflenet_v2_x1.5_ascend.yaml | 2 +- configs/shufflenet_v2/shufflenet_v2_x2.0_ascend.yaml | 2 +- configs/squeezenet/squeezenet_1.0_gpu.yaml | 2 +- configs/squeezenet/squeezenet_1.1_gpu.yaml | 2 +- configs/vit/vit_b32_224_ascend.yaml | 2 +- configs/vit/vit_l32_224_ascend.yaml | 2 +- 53 files changed, 53 insertions(+), 53 deletions(-) diff --git a/configs/convit/convit_tiny_ascend.yaml b/configs/convit/convit_tiny_ascend.yaml index 03beb0c2..4b1efe77 100644 --- a/configs/convit/convit_tiny_ascend.yaml +++ b/configs/convit/convit_tiny_ascend.yaml @@ -55,7 +55,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' lr: 0.00072 min_lr: 0.000001 warmup_epochs: 5 diff --git a/configs/convit/convit_tiny_gpu.yaml b/configs/convit/convit_tiny_gpu.yaml index f2289072..0a297506 100644 --- a/configs/convit/convit_tiny_gpu.yaml +++ b/configs/convit/convit_tiny_gpu.yaml @@ -53,7 +53,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' lr: 0.0005 min_lr: 0.00001 warmup_epochs: 10 diff --git a/configs/convit/convit_tiny_plus_ascend.yaml b/configs/convit/convit_tiny_plus_ascend.yaml index 2f6eadfd..84c70efa 100644 --- a/configs/convit/convit_tiny_plus_ascend.yaml +++ b/configs/convit/convit_tiny_plus_ascend.yaml @@ -55,7 +55,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' lr: 0.00072 min_lr: 0.000001 warmup_epochs: 40 diff --git a/configs/densenet/densenet_121_ascend.yaml b/configs/densenet/densenet_121_ascend.yaml index b081c145..9451082f 100644 --- a/configs/densenet/densenet_121_ascend.yaml +++ b/configs/densenet/densenet_121_ascend.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/densenet/densenet_121_gpu.yaml b/configs/densenet/densenet_121_gpu.yaml index b081c145..9451082f 100644 --- a/configs/densenet/densenet_121_gpu.yaml +++ b/configs/densenet/densenet_121_gpu.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/densenet/densenet_161_ascend.yaml b/configs/densenet/densenet_161_ascend.yaml index cee67a94..8206a904 100644 --- a/configs/densenet/densenet_161_ascend.yaml +++ b/configs/densenet/densenet_161_ascend.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/densenet/densenet_161_gpu.yaml b/configs/densenet/densenet_161_gpu.yaml index cee67a94..8206a904 100644 --- a/configs/densenet/densenet_161_gpu.yaml +++ b/configs/densenet/densenet_161_gpu.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/densenet/densenet_169_ascend.yaml b/configs/densenet/densenet_169_ascend.yaml index 4db98f94..329c4b36 100644 --- a/configs/densenet/densenet_169_ascend.yaml +++ b/configs/densenet/densenet_169_ascend.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/densenet/densenet_169_gpu.yaml b/configs/densenet/densenet_169_gpu.yaml index 4db98f94..329c4b36 100644 --- a/configs/densenet/densenet_169_gpu.yaml +++ b/configs/densenet/densenet_169_gpu.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/densenet/densenet_201_ascend.yaml b/configs/densenet/densenet_201_ascend.yaml index 8dc97cb1..4e014f68 100644 --- a/configs/densenet/densenet_201_ascend.yaml +++ b/configs/densenet/densenet_201_ascend.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/densenet/densenet_201_gpu.yaml b/configs/densenet/densenet_201_gpu.yaml index 8dc97cb1..4e014f68 100644 --- a/configs/densenet/densenet_201_gpu.yaml +++ b/configs/densenet/densenet_201_gpu.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/mobilenetv1/mobilenetv1_025_ascend.yaml b/configs/mobilenetv1/mobilenetv1_025_ascend.yaml index d5e56aa7..087dfde0 100644 --- a/configs/mobilenetv1/mobilenetv1_025_ascend.yaml +++ b/configs/mobilenetv1/mobilenetv1_025_ascend.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.4 warmup_epochs: 2 diff --git a/configs/mobilenetv1/mobilenetv1_025_gpu.yaml b/configs/mobilenetv1/mobilenetv1_025_gpu.yaml index d5e56aa7..087dfde0 100644 --- a/configs/mobilenetv1/mobilenetv1_025_gpu.yaml +++ b/configs/mobilenetv1/mobilenetv1_025_gpu.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.4 warmup_epochs: 2 diff --git a/configs/mobilenetv1/mobilenetv1_050_ascend.yaml b/configs/mobilenetv1/mobilenetv1_050_ascend.yaml index 13cd54d0..e3e1408c 100644 --- a/configs/mobilenetv1/mobilenetv1_050_ascend.yaml +++ b/configs/mobilenetv1/mobilenetv1_050_ascend.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.4 warmup_epochs: 2 diff --git a/configs/mobilenetv1/mobilenetv1_050_gpu.yaml b/configs/mobilenetv1/mobilenetv1_050_gpu.yaml index 13cd54d0..e3e1408c 100644 --- a/configs/mobilenetv1/mobilenetv1_050_gpu.yaml +++ b/configs/mobilenetv1/mobilenetv1_050_gpu.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.4 warmup_epochs: 2 diff --git a/configs/mobilenetv1/mobilenetv1_075_ascend.yaml b/configs/mobilenetv1/mobilenetv1_075_ascend.yaml index a4f0186f..9696a9f4 100644 --- a/configs/mobilenetv1/mobilenetv1_075_ascend.yaml +++ b/configs/mobilenetv1/mobilenetv1_075_ascend.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.4 warmup_epochs: 2 diff --git a/configs/mobilenetv1/mobilenetv1_075_gpu.yaml b/configs/mobilenetv1/mobilenetv1_075_gpu.yaml index a4f0186f..9696a9f4 100644 --- a/configs/mobilenetv1/mobilenetv1_075_gpu.yaml +++ b/configs/mobilenetv1/mobilenetv1_075_gpu.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.4 warmup_epochs: 2 diff --git a/configs/mobilenetv1/mobilenetv1_100_ascend.yaml b/configs/mobilenetv1/mobilenetv1_100_ascend.yaml index 46e2240c..2fd6fd56 100644 --- a/configs/mobilenetv1/mobilenetv1_100_ascend.yaml +++ b/configs/mobilenetv1/mobilenetv1_100_ascend.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.4 warmup_epochs: 2 diff --git a/configs/mobilenetv1/mobilenev1_100_gpu.yaml b/configs/mobilenetv1/mobilenev1_100_gpu.yaml index 7f8d6791..78d19ae2 100644 --- a/configs/mobilenetv1/mobilenev1_100_gpu.yaml +++ b/configs/mobilenetv1/mobilenev1_100_gpu.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.4 warmup_epochs: 2 diff --git a/configs/mobilenetv2/mobilenetv2_075_ascend.yaml b/configs/mobilenetv2/mobilenetv2_075_ascend.yaml index c8d70994..4be0adb7 100644 --- a/configs/mobilenetv2/mobilenetv2_075_ascend.yaml +++ b/configs/mobilenetv2/mobilenetv2_075_ascend.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.3 warmup_epochs: 4 diff --git a/configs/mobilenetv2/mobilenetv2_100_ascend.yaml b/configs/mobilenetv2/mobilenetv2_100_ascend.yaml index a07796c1..5989885c 100644 --- a/configs/mobilenetv2/mobilenetv2_100_ascend.yaml +++ b/configs/mobilenetv2/mobilenetv2_100_ascend.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.4 warmup_epochs: 4 diff --git a/configs/mobilenetv2/mobilenetv2_140_ascend.yaml b/configs/mobilenetv2/mobilenetv2_140_ascend.yaml index 030d78be..cc69ecb4 100644 --- a/configs/mobilenetv2/mobilenetv2_140_ascend.yaml +++ b/configs/mobilenetv2/mobilenetv2_140_ascend.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.4 warmup_epochs: 4 diff --git a/configs/mobilenetv3/v3_large_Ascend.yaml b/configs/mobilenetv3/v3_large_Ascend.yaml index 6192b237..5968c620 100644 --- a/configs/mobilenetv3/v3_large_Ascend.yaml +++ b/configs/mobilenetv3/v3_large_Ascend.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 1.08 warmup_epochs: 4 diff --git a/configs/mobilenetv3/v3_small_Ascend.yaml b/configs/mobilenetv3/v3_small_Ascend.yaml index 48d573e5..1c3b0a64 100644 --- a/configs/mobilenetv3/v3_small_Ascend.yaml +++ b/configs/mobilenetv3/v3_small_Ascend.yaml @@ -52,7 +52,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.77 warmup_epochs: 4 diff --git a/configs/res2net/res2net_101-v1b_ascend.yaml b/configs/res2net/res2net_101-v1b_ascend.yaml index 620122de..f31c0762 100644 --- a/configs/res2net/res2net_101-v1b_ascend.yaml +++ b/configs/res2net/res2net_101-v1b_ascend.yaml @@ -55,7 +55,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 4 diff --git a/configs/res2net/res2net_101-v1b_gpu.yaml b/configs/res2net/res2net_101-v1b_gpu.yaml index da8e248e..0da08132 100644 --- a/configs/res2net/res2net_101-v1b_gpu.yaml +++ b/configs/res2net/res2net_101-v1b_gpu.yaml @@ -56,7 +56,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 5 diff --git a/configs/res2net/res2net_50-v1b_ascend.yaml b/configs/res2net/res2net_50-v1b_ascend.yaml index a8af073d..710cae48 100644 --- a/configs/res2net/res2net_50-v1b_ascend.yaml +++ b/configs/res2net/res2net_50-v1b_ascend.yaml @@ -56,7 +56,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 5 diff --git a/configs/res2net/res2net_50-v1b_gpu.yaml b/configs/res2net/res2net_50-v1b_gpu.yaml index 25bc89a4..7ee5a45b 100644 --- a/configs/res2net/res2net_50-v1b_gpu.yaml +++ b/configs/res2net/res2net_50-v1b_gpu.yaml @@ -56,7 +56,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 5 diff --git a/configs/res2net/res2net_50_ascend.yaml b/configs/res2net/res2net_50_ascend.yaml index c4791849..02fb72cd 100644 --- a/configs/res2net/res2net_50_ascend.yaml +++ b/configs/res2net/res2net_50_ascend.yaml @@ -55,7 +55,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 4 diff --git a/configs/res2net/res2net_50_gpu.yaml b/configs/res2net/res2net_50_gpu.yaml index 403424f8..f0d42ecc 100644 --- a/configs/res2net/res2net_50_gpu.yaml +++ b/configs/res2net/res2net_50_gpu.yaml @@ -55,7 +55,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 4 diff --git a/configs/resnet/resnet_101_ascend.yaml b/configs/resnet/resnet_101_ascend.yaml index 87a2e217..04813f55 100644 --- a/configs/resnet/resnet_101_ascend.yaml +++ b/configs/resnet/resnet_101_ascend.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/resnet/resnet_101_gpu.yaml b/configs/resnet/resnet_101_gpu.yaml index 87a2e217..04813f55 100644 --- a/configs/resnet/resnet_101_gpu.yaml +++ b/configs/resnet/resnet_101_gpu.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/resnet/resnet_152_ascend.yaml b/configs/resnet/resnet_152_ascend.yaml index f36a5af5..266fe211 100644 --- a/configs/resnet/resnet_152_ascend.yaml +++ b/configs/resnet/resnet_152_ascend.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/resnet/resnet_152_gpu.yaml b/configs/resnet/resnet_152_gpu.yaml index f36a5af5..266fe211 100644 --- a/configs/resnet/resnet_152_gpu.yaml +++ b/configs/resnet/resnet_152_gpu.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/resnet/resnet_18_ascend.yaml b/configs/resnet/resnet_18_ascend.yaml index d47464ec..6c67b5c5 100644 --- a/configs/resnet/resnet_18_ascend.yaml +++ b/configs/resnet/resnet_18_ascend.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/resnet/resnet_18_gpu.yaml b/configs/resnet/resnet_18_gpu.yaml index d47464ec..6c67b5c5 100644 --- a/configs/resnet/resnet_18_gpu.yaml +++ b/configs/resnet/resnet_18_gpu.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/resnet/resnet_34_ascend.yaml b/configs/resnet/resnet_34_ascend.yaml index 35c03654..211d6d31 100644 --- a/configs/resnet/resnet_34_ascend.yaml +++ b/configs/resnet/resnet_34_ascend.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/resnet/resnet_34_gpu.yaml b/configs/resnet/resnet_34_gpu.yaml index 35c03654..211d6d31 100644 --- a/configs/resnet/resnet_34_gpu.yaml +++ b/configs/resnet/resnet_34_gpu.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/resnet/resnet_50_ascend.yaml b/configs/resnet/resnet_50_ascend.yaml index a321e7a3..4ecaa5c0 100644 --- a/configs/resnet/resnet_50_ascend.yaml +++ b/configs/resnet/resnet_50_ascend.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/resnet/resnet_50_gpu.yaml b/configs/resnet/resnet_50_gpu.yaml index a321e7a3..4ecaa5c0 100644 --- a/configs/resnet/resnet_50_gpu.yaml +++ b/configs/resnet/resnet_50_gpu.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 warmup_epochs: 0 diff --git a/configs/rexnet/rexnet_x10.yaml b/configs/rexnet/rexnet_x10.yaml index 970c4852..a9bf04ba 100644 --- a/configs/rexnet/rexnet_x10.yaml +++ b/configs/rexnet/rexnet_x10.yaml @@ -37,7 +37,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' # model_zoo中是polynomial +scheduler: 'cosine_decay' # model_zoo中是polynomial min_lr: 0.0 lr: 0.01 warmup_epochs: 0 diff --git a/configs/shufflenet_v1/shufflenet_v1_0.5_ascend.yaml b/configs/shufflenet_v1/shufflenet_v1_0.5_ascend.yaml index 1598b1ee..89eff6bb 100644 --- a/configs/shufflenet_v1/shufflenet_v1_0.5_ascend.yaml +++ b/configs/shufflenet_v1/shufflenet_v1_0.5_ascend.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.35 warmup_epochs: 4 diff --git a/configs/shufflenet_v1/shufflenet_v1_1.0_ascend.yaml b/configs/shufflenet_v1/shufflenet_v1_1.0_ascend.yaml index b3e98598..ccf15519 100644 --- a/configs/shufflenet_v1/shufflenet_v1_1.0_ascend.yaml +++ b/configs/shufflenet_v1/shufflenet_v1_1.0_ascend.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.4 warmup_epochs: 4 diff --git a/configs/shufflenet_v1/shufflenet_v1_1.5_ascend.yaml b/configs/shufflenet_v1/shufflenet_v1_1.5_ascend.yaml index 754a51e1..f6a0a6f0 100644 --- a/configs/shufflenet_v1/shufflenet_v1_1.5_ascend.yaml +++ b/configs/shufflenet_v1/shufflenet_v1_1.5_ascend.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.5 warmup_epochs: 4 diff --git a/configs/shufflenet_v1/shufflenet_v1_2.0_ascend.yaml b/configs/shufflenet_v1/shufflenet_v1_2.0_ascend.yaml index c653c7b1..c782a2a8 100644 --- a/configs/shufflenet_v1/shufflenet_v1_2.0_ascend.yaml +++ b/configs/shufflenet_v1/shufflenet_v1_2.0_ascend.yaml @@ -50,7 +50,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.5 warmup_epochs: 4 diff --git a/configs/shufflenet_v2/shufflenet_v2_x0.5_ascend.yaml b/configs/shufflenet_v2/shufflenet_v2_x0.5_ascend.yaml index 0726effe..6b0a5258 100644 --- a/configs/shufflenet_v2/shufflenet_v2_x0.5_ascend.yaml +++ b/configs/shufflenet_v2/shufflenet_v2_x0.5_ascend.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.35 warmup_epochs: 4 diff --git a/configs/shufflenet_v2/shufflenet_v2_x1.0_ascend.yaml b/configs/shufflenet_v2/shufflenet_v2_x1.0_ascend.yaml index 81d6df6c..e33fcd2c 100644 --- a/configs/shufflenet_v2/shufflenet_v2_x1.0_ascend.yaml +++ b/configs/shufflenet_v2/shufflenet_v2_x1.0_ascend.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.4 warmup_epochs: 5 diff --git a/configs/shufflenet_v2/shufflenet_v2_x1.5_ascend.yaml b/configs/shufflenet_v2/shufflenet_v2_x1.5_ascend.yaml index d34ad1ce..be4690ce 100644 --- a/configs/shufflenet_v2/shufflenet_v2_x1.5_ascend.yaml +++ b/configs/shufflenet_v2/shufflenet_v2_x1.5_ascend.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.5 warmup_epochs: 4 diff --git a/configs/shufflenet_v2/shufflenet_v2_x2.0_ascend.yaml b/configs/shufflenet_v2/shufflenet_v2_x2.0_ascend.yaml index 7b540697..0f9b831c 100644 --- a/configs/shufflenet_v2/shufflenet_v2_x2.0_ascend.yaml +++ b/configs/shufflenet_v2/shufflenet_v2_x2.0_ascend.yaml @@ -51,7 +51,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.5 warmup_epochs: 5 diff --git a/configs/squeezenet/squeezenet_1.0_gpu.yaml b/configs/squeezenet/squeezenet_1.0_gpu.yaml index c359389a..28cee507 100644 --- a/configs/squeezenet/squeezenet_1.0_gpu.yaml +++ b/configs/squeezenet/squeezenet_1.0_gpu.yaml @@ -37,7 +37,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' # model_zoo中是polynomial +scheduler: 'cosine_decay' # model_zoo中是polynomial min_lr: 0.0 lr: 0.01 warmup_epochs: 0 diff --git a/configs/squeezenet/squeezenet_1.1_gpu.yaml b/configs/squeezenet/squeezenet_1.1_gpu.yaml index 043342ba..177cfcd4 100644 --- a/configs/squeezenet/squeezenet_1.1_gpu.yaml +++ b/configs/squeezenet/squeezenet_1.1_gpu.yaml @@ -37,7 +37,7 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' # model_zoo中是polynomial +scheduler: 'cosine_decay' # model_zoo中是polynomial min_lr: 0.0 lr: 0.01 warmup_epochs: 0 diff --git a/configs/vit/vit_b32_224_ascend.yaml b/configs/vit/vit_b32_224_ascend.yaml index 78a80ff1..c6361407 100644 --- a/configs/vit/vit_b32_224_ascend.yaml +++ b/configs/vit/vit_b32_224_ascend.yaml @@ -62,7 +62,7 @@ loss_scale: 1024.0 label_smoothing: 0.1 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' lr: 0.003 # diff from 4xGPU min_lr: 0.00001 warmup_epochs: 38 diff --git a/configs/vit/vit_l32_224_ascend.yaml b/configs/vit/vit_l32_224_ascend.yaml index df30fe7e..425fff48 100644 --- a/configs/vit/vit_l32_224_ascend.yaml +++ b/configs/vit/vit_l32_224_ascend.yaml @@ -62,7 +62,7 @@ loss_scale: 1024.0 label_smoothing: 0.1 # Exp 1119, compared to 1118 part2 # lr scheduler config -scheduler: 'warmup_cosine_decay' +scheduler: 'cosine_decay' lr: 0.003 # min_lr: 1e-6 warmup_epochs: 32 From adeca3f83fee86d5f325fb7a132c88e3718f1a54 Mon Sep 17 00:00:00 2001 From: Li-chenyangV <18851659065@163.com> Date: Mon, 28 Nov 2022 00:27:57 +0800 Subject: [PATCH 08/32] add resnet readme --- configs/resnet/README.md | 78 ++++++++++++++++++++++++++++++++++++ configs/resnet/README_CN.md | 68 +++++++++++++++++++++++++++++++ configs/resnet/resnet.png | Bin 0 -> 16273 bytes 3 files changed, 146 insertions(+) create mode 100644 configs/resnet/README.md create mode 100644 configs/resnet/README_CN.md create mode 100644 configs/resnet/resnet.png diff --git a/configs/resnet/README.md b/configs/resnet/README.md new file mode 100644 index 00000000..51620df3 --- /dev/null +++ b/configs/resnet/README.md @@ -0,0 +1,78 @@ +# ResNet +> [Deep Residual Learning for Image Recognition](https://arxiv.org/pdf/1512.03385.pdf) + +## Introduction +*** + +Deeper neural networks are more difficult to train. Resnet is a residual learning framework to ease the training of networks that are substantially deeper than those used previously, which is explicitly reformulated that the layers are learning residual functions with reference to the layer inputs, instead of learning unreferenced functions. Lots of comprehensive empirical evidence showing that these residual networks are easier to optimize, and can gain accuracy from considerably increased depth. + +![](resnet.png) + +## Results +*** + +| Model | Context | Top-1 (%) | Top-5 (%) | Params (M) | Train T. | Infer T. | Download | Config | Log | +|-----------------|-----------|-------|-------|------------|-------|--------|---|--------|--------------| +| ResNet18 | D910x8-G | 70.10 | 89.58 | 11.70 | 118s/epoch | | [model]() | [cfg]() | [log]() | +| ResNet34 | D910x8-G | 74.19 | 91.76 | 21.81 | 122s/epoch | | [model]() | [cfg]() | [log]() | +| ResNet50 | D910x8-G | 76.78 | 93.42 | 25.61 | 213s/epoch | | [model]() | [cfg]() | [log]() | +| ResNet101 | D910x8-G | 78.06 | 94.15 | 44.65 | 327s/epoch | | [model]() | [cfg]() | [log]() | +| ResNet152 | D910x8-G | 78.37 | 94.09 | 60.34 | 456s/epoch | | [model]() | [cfg]() | [log]() | + +#### Notes + +- All models are trained on ImageNet-1K training set and the top-1 accuracy is reported on the validatoin set. +- Context: GPU_TYPE x pieces - G/F, G - graph mode, F - pynative mode with ms function. + +## Quick Start +*** +### Preparation + +#### Installation +Please refer to the [installation instruction](https://github.com/mindspore-ecosystem/mindcv#installation) in MindCV. + +#### Dataset Preparation +Please download the [ImageNet-1K](https://www.image-net.org/download.php) dataset for model training and validation. + +### Training + +- **Hyper-parameters.** The hyper-parameter configurations for producing the reported results are stored in the yaml files in `mindcv/configs/resnet` folder. For example, to train with one of these configurations, you can run: + + ```shell + # train resnet18 on 8 GPUs + export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 + mpirun -n 8 python train.py -c configs/resnet/resnet_18_gpu.yaml --data_dir /path/to/imagenet + ``` + + Note that the number of GPUs/Ascends and batch size will influence the training results. To reproduce the training result at most, it is recommended to use the **same number of GPUs/Ascneds** with the same batch size. + +- **Finetuning.** Here is an example for finetuning a pretrained resnet18 on CIFAR10 dataset using Momentum optimizer. + + ```shell + python train.py --model=resnet18 --pretrained --opt=momentum --lr=0.001 dataset=cifar10 --num_classes=10 --dataset_download + ``` + +Detailed adjustable parameters and their default value can be seen in [config.py](../../config.py). + +### Validation + +- To validate the trained model, you can use `validate.py`. Here is an example for resnet18 to verify the accuracy of + pretrained weights. + + ```shell + python validate.py --model=resnet18 --dataset=imagenet --val_split=val --pretrained + ``` + +- To validate the model, you can use `validate.py`. Here is an example for resnet18 to verify the accuracy of your + training. + + ```shell + python validate.py --model=resnet18 --dataset=imagenet --val_split=val --ckpt_path='./ckpt/resnet18-best.ckpt' + ``` + +### Deployment (optional) + +Please refer to the deployment tutorial in MindCV. + + + diff --git a/configs/resnet/README_CN.md b/configs/resnet/README_CN.md new file mode 100644 index 00000000..fb810a41 --- /dev/null +++ b/configs/resnet/README_CN.md @@ -0,0 +1,68 @@ +# ResNet + +*** +> [Deep Residual Learning for Image Recognition](https://arxiv.org/pdf/1512.03385.pdf) + +## 模型简介 + +*** +深层次的神经网络更加难训练,Resnet 是一个残差学习架构,用于简化比以前更深网络的训练,它被明确表述为具有参考层的输入学习残差网络,而不是学习未引用的网络结构。大量的经验证据表明,这些残差网络更容易优化,并且可以从大大增加的深度中获得准确性。 + +![](resnet.png) + +## 性能指标 + +*** + +| Model | Context | Top-1 (%) | Top-5 (%) | Params (M) | Train T. | Infer T. | Download | Config | Log | +|-----------------|-----------|-------|-------|------------|-------|--------|---|--------|--------------| +| ResNet18 | D910x8-G | 70.10 | 89.58 | 11.70 | 118s/epoch | | [model]() | [cfg]() | [log]() | +| ResNet34 | D910x8-G | 74.19 | 91.76 | 21.81 | 122s/epoch | | [model]() | [cfg]() | [log]() | +| ResNet50 | D910x8-G | 76.78 | 93.42 | 25.61 | 213s/epoch | | [model]() | [cfg]() | [log]() | +| ResNet101 | D910x8-G | 78.06 | 94.15 | 44.65 | 327s/epoch | | [model]() | [cfg]() | [log]() | +| ResNet152 | D910x8-G | 78.37 | 94.09 | 60.34 | 456s/epoch | | [model]() | [cfg]() | [log]() | + +#### 备注 + +- All models are trained on ImageNet-1K training set and the top-1 accuracy is reported on the validatoin set. +- Context: GPU_TYPE x pieces - G/F, G - graph mode, F - pynative mode with ms function. + +## 示例 + +- 以上模型均在ImageNet-1K数据集上训练和验证。 +- Context: GPU_TYPE x pieces - G/F, G - graph mode, F - pynative mode with ms function. + +*** + +### 训练 + +- 下面是使用预设的yaml配置文件启动训练的示例. + +> [configs文件夹](../../configs)中列出了mindcv套件所包含的模型的各个规格的yaml配置文件(在ImageNet数据集上训练和验证的配置)。 + + ```shell + export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 + mpirun -n 8 python train.py -c configs/resnet/resnet_18_gpu.yaml --data_dir /path/to/imagenet + ``` + +- 下面是使用在ImageNet上预训练的resnet18模型和Momentum优化器在CIFAR10数据集上进行微调的示例。 + + ```shell + python train.py --model=resnet18 --pretrained --opt=momentum --lr=0.001 dataset=cifar10 --num_classes=10 --dataset_download + ``` + +详细的可调参数及其默认值可以在[config.py](../../config.py)中查看。 + +### 验证 + +- 下面是使用`validate.py`文件验证resnet18的预训练模型的精度的示例。 + + ```shell + python validate.py --model=resnet18 --dataset=imagenet --val_split=val --pretrained + ``` + +- 下面是使用`validate.py`文件验证resnet18的自定义参数文件的精度的示例。 + + ```shell + python validate.py --model=resnet18 --dataset=imagenet --val_split=val --ckpt_path='./ckpt/densenet121-best.ckpt' + ``` diff --git a/configs/resnet/resnet.png b/configs/resnet/resnet.png new file mode 100644 index 0000000000000000000000000000000000000000..f6a9c55e71ce64122bb6522d6af4b984d3363c9c GIT binary patch literal 16273 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 Date: Mon, 28 Nov 2022 14:14:07 +0800 Subject: [PATCH 09/32] Update scheduler_factory.py --- mindcv/scheduler/scheduler_factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mindcv/scheduler/scheduler_factory.py b/mindcv/scheduler/scheduler_factory.py index 8ddc641f..a7cbce42 100644 --- a/mindcv/scheduler/scheduler_factory.py +++ b/mindcv/scheduler/scheduler_factory.py @@ -62,7 +62,7 @@ def create_scheduler( # lr decay phase main_epochs = num_epochs - warmup_epochs - if scheduler == 'cosine_decay': + if scheduler in ['cosine_decay', 'warmup_cosine_decay']: cosine_func = cosine_decay_lr if lr_epoch_stair else cosine_decay_refined_lr main_lr_scheduler = cosine_func(t_max=decay_epochs, eta_min=min_lr, eta_max=lr, steps_per_epoch=steps_per_epoch, epochs=main_epochs) From 1ee457cb0a9f04da75cf187da08586a4384ff818 Mon Sep 17 00:00:00 2001 From: "junnanleng@126.com" Date: Mon, 28 Nov 2022 19:40:20 +0800 Subject: [PATCH 10/32] modify shufflenetv1_block --- mindcv/models/shufflenetv1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mindcv/models/shufflenetv1.py b/mindcv/models/shufflenetv1.py index b378eb9d..8da70883 100644 --- a/mindcv/models/shufflenetv1.py +++ b/mindcv/models/shufflenetv1.py @@ -96,7 +96,7 @@ def construct(self, x: Tensor) -> Tensor: if self.stride == 1: out = self.relu(identify + x) else: - out = ops.concat((self.branch_proj(identify), self.relu(x)), axis=1) + out = self.relu(ops.concat((self.branch_proj(identify), x), axis=1)) return out From 6227370838f2838aec6f179a87523e0ee53ba0fd Mon Sep 17 00:00:00 2001 From: "junnanleng@126.com" Date: Mon, 28 Nov 2022 19:51:23 +0800 Subject: [PATCH 11/32] modify format of shufflenet_v2 --- mindcv/models/shufflenetv2.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/mindcv/models/shufflenetv2.py b/mindcv/models/shufflenetv2.py index 3ba20e82..3530431d 100644 --- a/mindcv/models/shufflenetv2.py +++ b/mindcv/models/shufflenetv2.py @@ -166,15 +166,6 @@ def __init__(self, self.classifier = nn.Dense(self.stage_out_channels[-1], num_classes, has_bias=False) self._initialize_weights() - def construct(self, x): - x = self.first_conv(x) - x = self.max_pool(x) - x = self.features(x) - x = self.conv_last(x) - x = self.pool(x) - x = self.classifier(x) - return x - def _initialize_weights(self): """Initialize weights for cells.""" for name, cell in self.cells_and_names(): @@ -196,6 +187,23 @@ def _initialize_weights(self): cell.bias.set_data( init.initializer('zeros', cell.bias.shape, cell.bias.dtype)) + def forward_features(self, x: Tensor) -> Tensor: + x = self.first_conv(x) + x = self.max_pool(x) + x = self.features(x) + return x + + def forward_head(self, x: Tensor) -> Tensor: + x = self.conv_last(x) + x = self.pool(x) + x = self.classifier(x) + return x + + def construct(self, x: Tensor) -> Tensor: + x = self.forward_features(x) + x = self.forward_head(x) + return x + @register_model def shufflenet_v2_x0_5(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs) -> ShuffleNetV2: From 0c5662eab424b6bd8566c749c2c55711cd127e4f Mon Sep 17 00:00:00 2001 From: SFoever <1239081717@qq.com> Date: Mon, 28 Nov 2022 22:43:33 +0800 Subject: [PATCH 12/32] Add regnet. --- configs/regnet/README.md | 61 ++ configs/regnet/README_CN.md | 59 ++ configs/regnet/regnet.png | Bin 0 -> 302252 bytes configs/regnet/regnet_x_800mf_ascend.yaml | 53 ++ mindcv/models/__init__.py | 4 +- mindcv/models/regnet.py | 723 ++++++++++++++++++++++ 6 files changed, 899 insertions(+), 1 deletion(-) create mode 100644 configs/regnet/README.md create mode 100644 configs/regnet/README_CN.md create mode 100644 configs/regnet/regnet.png create mode 100644 configs/regnet/regnet_x_800mf_ascend.yaml create mode 100644 mindcv/models/regnet.py diff --git a/configs/regnet/README.md b/configs/regnet/README.md new file mode 100644 index 00000000..02e6f44c --- /dev/null +++ b/configs/regnet/README.md @@ -0,0 +1,61 @@ +# RegNet +> [Designing Network Design Spaces](https://arxiv.org/pdf/2003.13678.pdf) + +## Introduction +*** + +In this work, we present a new network design paradigm that combines the advantages of manual design and NAS. Instead of focusing on designing individual network instances, we design design spaces that parametrize populations of networks. Like in manual design, we aim for interpretability and to discover general design principles that describe networks that are simple, work well, and generalize across settings. Like in NAS, we aim to take advantage of semi-automated procedures to help achieve these goals The general strategy we adopt is to progressively design simplified versions of an initial, relatively unconstrained, design space while maintaining or improving its quality. The overall process is analogous to manual design, elevated to the population level and guided via distribution estimates of network design spaces. As a testbed for this paradigm, our focus is on exploring network structure (e.g., width, depth, groups, etc.) assuming standard model families including VGG, ResNet, and ResNeXt. We start with a relatively unconstrained design space we call AnyNet (e.g., widths and depths vary freely across stages) and apply our humanin-the-loop methodology to arrive at a low-dimensional design space consisting of simple “regular” networks, that we call RegNet. The core of the RegNet design space is simple: stage widths and depths are determined by a quantized linear function. Compared to AnyNet, the RegNet design space has simpler models, is easier to interpret, and has a higher concentration of good models. + +![](regnet.png) + +## Results +*** + +| Model | Context | Top-1 (%) | Params (M) | Train T. | Infer T. | Download | Config | Log | +|-----------------|-----------|-------|------------|-------|--------|---|--------|--------------| +| RegNetX-800MF | D910x8-G | 76.09 | 7.3 | 115s/epoch | 1.8ms/step | [model]() | [cfg]() | [log]() | + +#### Notes + +- All models are trained on ImageNet-1K training set and the top-1 accuracy is reported on the validatoin set. +- Context: GPU_TYPE x pieces - G/F, G - graph mode, F - pynative mode with ms function. + +## Quick Start +*** +### Preparation + +#### Installation +Please refer to the [installation instruction](https://github.com/mindspore-ecosystem/mindcv#installation) in MindCV. + +#### Dataset Preparation +Please download the [ImageNet-1K](https://www.image-net.org/download.php) dataset for model training and validation. + +### Training + +- The yaml config files that yield competitive results on ImageNet for different models are listed in the configs folder. To trigger training using preset yaml config. + + ```shell + python train.py --model=regnet_x_800mf --config=configs/regnet/regnet_x_800mf_ascend.yaml + ``` + +### Validation + +- To validate the trained model, you can use `validate.py`. Here is an example for regnetx800mf to verify the accuracy of + pretrained weights. + + ```shell + python validate.py --model=regnet_x_800mf --dataset=imagenet --val_split=val --pretrained + ``` + +- To validate the model, you can use `validate.py`. Here is an example for regnetx800mf to verify the accuracy of your + training. + + ```shell + python validate.py --model=regnet_x_800mf --dataset=imagenet --val_split=val --ckpt_path='./ckpt/regnet_x_800mf-best.ckpt' + ``` + +### Deployment (optional) + +Please refer to the deployment tutorial in MindCV. + + diff --git a/configs/regnet/README_CN.md b/configs/regnet/README_CN.md new file mode 100644 index 00000000..02126097 --- /dev/null +++ b/configs/regnet/README_CN.md @@ -0,0 +1,59 @@ +# RegNet +> [Designing Network Design Spaces](https://arxiv.org/pdf/2003.13678.pdf) + +## 模型简介 +*** + +在这项工作中,我们提出了一种新的网络设计范式,它结合了人工设计和NAS的优点。我们不是专注于设计单个网络实例,而是设计参数化网络总体的设计空间。就像手工设计一样,我们的目标是可解释性,并发现描述网络的通用设计原则,这些原则简单、工作良好,并且可以跨设置进行推广。与NAS一样,我们的目标是利用半自动化的过程来帮助实现这些目标。我们采用的一般策略是逐步设计初始的、相对不受限制的设计空间的简化版本,同时保持或提高其质量。整个过程类似于手工设计,提升到群体水平,并通过网络设计空间的分布估计进行指导。作为这个范例的测试平台,我们的重点是在假设包括VGG、ResNet和ResNeXt在内的标准模型族的情况下探索网络结构(例如宽度、深度、组等)。我们从一个相对不受约束的设计空间开始,我们称之为AnyNet(例如,宽度和深度在不同阶段自由变化),到达一个由简单的“规则”网络组成的低维设计空间,我们称之为RegNet。RegNet设计空间的核心很简单:stage的宽度和深度由量化的线性函数决定。与AnyNet相比,RegNet设计空间的模型更简单,更容易解释,好模型的集中度更高。 + +![](regnet.png) + +## 性能指标 +*** + +| Model | Context | Top-1 (%) | Params (M) | Train T. | Infer T. | Download | Config | Log | +|-----------------|-----------|-------|------------|-------|--------|---|--------|--------------| +| RegNetX-800MF | D910x8-G | 76.09 | 7.3 | 115s/epoch | 1.8ms/step | [model]() | [cfg]() | [log]() | + +#### 备注 + +- All models are trained on ImageNet-1K training set and the top-1 accuracy is reported on the validatoin set. +- Context: GPU_TYPE x pieces - G/F, G - graph mode, F - pynative mode with ms function. + +## 示例 +*** +### 准备步骤 + +#### 安装仓库 +请参考MindCV中的 [安装指南](https://github.com/mindspore-ecosystem/mindcv#installation)。 + +#### 准备数据集 +请下载 [ImageNet-1K](https://www.image-net.org/download.php) 数据集以用作模型训练和验证。 + +### 模型训练 + +- 在configs文件夹中列出了在ImageNet上为不同模型产生竞争性结果的yaml配置文件。使用预设的yaml配置进行训练。 + + ```shell + python train.py --model=regnet_x_800mf --config=configs/regnet/regnet_x_800mf_ascend.yaml + ``` + +### 模型验证 + +- 要验证训练过的模型,可以使用 `validate.py`。下面是对regnetx800mf进行验证的一个示例,用于验证pretrained权重的准确性。 + + ```shell + python validate.py --model=regnet_x_800mf --dataset=imagenet --val_split=val --pretrained + ``` + +- 要验证模型权重,可以使用 `validate.py`。下面是对regnetx800mf权重进行验证的一个示例。 + + ```shell + python validate.py --model=regnet_x_800mf --dataset=imagenet --val_split=val --ckpt_path='./ckpt/regnet_x_800mf-best.ckpt' + ``` + +### 部署 (可选) + +请参考MindCV的部署教程。 + + diff --git a/configs/regnet/regnet.png b/configs/regnet/regnet.png new file mode 100644 index 0000000000000000000000000000000000000000..dacd4126e5489b6ea78cc9967736c9c17398db48 GIT binary patch literal 302252 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Π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

U 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

*c;+v-U^cbG zN#;6lWwQUsWF3dcx3I)YRmOnPaCYLV74Jtkw10K)4wDI0vgr``xWIl6%T$|OIX3_7 z-iBBtOF;y-phsoNMwlHw+NLtHo}2A(bbTUA}eW56q2vNA0AYCTU z))TC^&L&4|SDtqoN}~eiGvqPoAAAOfwF>JP`4GVZ_|!1%D*OLm^yQrt=UBHLm&(c@ z%O6VCS%73?2@kWRqTjCX#rzW@GIP`6abor?GU$l0J-}yjE}e~s>3`9zZD2%VItDjs zx6cE_5*Zn_e75aSB&xz1$JGj~eYOn!R;NpISE{V!Kl0+W=199_;2!rfS&4FpbBA3< z=OuxGu-ygrot9c?VGP^-nU8W@|5LS-`s0g2W!M0HR~;+>)zHh@Z@~wF;H+uy`U#>m^)8cAkUxlVqT# z6i?8ytAfFoAWn<=Ke+br4Px};(6{r(1cd?-;TBZEyg~_~HR>M^{XkgcgPaP^gB<7X zG)J#>T|I8Ot_XyK4wqV}dqyY&sW^~(QZ$vD)@RVn6H?x48Ao#_ZZj5scsgQq*RHOuj-H% zD)iY<%1nb{Cl7mXg!*?_S_FeS>#6)m#D4oWZ}6jV6xTRF6`X|BE5lCVPd|*Y$IHR0 zl1?$N;J`GiPz1143hsO*cyXO1TJfK{Og7Y`iu*yZQ_a!=E-fd#@kB=Dgv1(QmJ2~- z+cqV&?RmnX|CGhZ^TUzYaJPBEgcdI(Wy~T0vlRK(y&wZ>+$N?e3rbmpwX|&vaa$B2 z4toTHUxx9~y_IA(I2ti|=cG^reD+DM;t%Lkf>%P=R6VklV!hUaWQm_(k>r_Vj3384l`7WwU7hrf;Wt15Rc=8fIS$y2>`E*<3S*QOy)o-lWPaf{P@047UJhpHb10P^zq zf(^I)!}rz$$U;eJ)YT#=$QB|!CXyqx!=O1OLCw{xX$f@hFKh_1kjSu0cn%?UId+zB z)3l>P$D3W9>?mO&eMu(LO>#6p7)&h5tI&qW>*%{s!^C`NJDoeU2rSb68$%nnBhCFV zN?dRdjECq@n_j01m5?VU*AEL@Ui}l7k9l+N3+Q=SZb1n^?5YFdhm&4Cs zlzuPxh@puG50?~fud#HVn|(f|*N~G(yrkJ2u)krt&|(Yym>3)m-el%1Q|45fZQUYf zru+=DFQK{TJG`hwf(VLE=tuXO(|a}p;=nIAg6D2V;@d>?jkJ8Gf0PSET*G^jUjKuG zU89&cNW@!eSIkN{9L#Y}8xb8(j$doyQSuq>_3s^pa8M#VR@$T)Lps*?rQ#725}>*M z#ui%0D3m>u9wGb?+6mppeRDz`t9_+{uXjnFqZ$jze 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%0A*(l)D}IBy>jj3urhFAkX1%PPHI_? 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 literal 0 HcmV?d00001 diff --git a/configs/regnet/regnet_x_800mf_ascend.yaml b/configs/regnet/regnet_x_800mf_ascend.yaml new file mode 100644 index 00000000..8dc22b25 --- /dev/null +++ b/configs/regnet/regnet_x_800mf_ascend.yaml @@ -0,0 +1,53 @@ +# Minimum + Color Jitter + Random Erasing +# system config +mode: 0 +distribute: True +val_while_train: True +val_interval: 1 +log_interval: 100 + +# dataset config +dataset: 'imagenet' +data_dir: './imagenet' +shuffle: True +num_parallel_workers: 8 +batch_size: 64 + +# augmentation config +image_resize: 224 +scale: [0.08, 1.0] +ratio: [0.75, 1.333] +hflip: 0.5 +vflip: 0.0 +interpolation: 'bilinear' +color_jitter: 0.4 +re_prob: 0.1 + +# model config +model: '' +num_classes: 1000 +keep_checkpoint_max: 10 +ckpt_save_dir: './ckpt' +ckpt_save_interval: 1 +ckpt_save_policy: 'latest_k' +epoch_size: 200 +dataset_sink_mode: True +amp_level: 'O3' + +# loss config +loss: 'CE' +label_smoothing: 0.1 + +# lr scheduler config +scheduler: 'cosineannealing' +min_lr: 0.0 +lr: 0.1 +warmup_epochs: 5 + +# optimizer config +opt: 'momentum' +momentum: 0.9 +weight_decay: 0.0001 +loss_scale: 128 +use_nesterov: False +filter_bias_and_bn: True \ No newline at end of file diff --git a/mindcv/models/__init__.py b/mindcv/models/__init__.py index 6e7355a8..8a9c742a 100644 --- a/mindcv/models/__init__.py +++ b/mindcv/models/__init__.py @@ -1,6 +1,6 @@ """models init""" from . import layers, convnext, densenet, dpn, efficientnet, ghostnet, googlenet, inception_v3, inception_v4, mnasnet,\ - mobilenet_v1, mobilenet_v2, mobilenet_v3, model_factory, nasnet, pnasnet, registry, repvgg, res2net, resnet,\ + mobilenet_v1, mobilenet_v2, mobilenet_v3, model_factory, nasnet, pnasnet, registry, regnet, repvgg, res2net, resnet,\ rexnet, shufflenetv1, shufflenetv2, sknet, squeezenet, swin_transformer, vgg, xception, convit, vit __all__ = [] @@ -21,6 +21,7 @@ __all__.extend(nasnet.__all__) __all__.extend(pnasnet.__all__) __all__.extend(registry.__all__) +__all__.extend(regnet.__all__) __all__.extend(repvgg.__all__) __all__.extend(res2net.__all__) __all__.extend(resnet.__all__) @@ -47,6 +48,7 @@ from .xception import * from .model_factory import * from .registry import * +from .regnet import * from .resnet import * from .rexnet import * from .shufflenetv1 import * diff --git a/mindcv/models/regnet.py b/mindcv/models/regnet.py new file mode 100644 index 00000000..f261ca41 --- /dev/null +++ b/mindcv/models/regnet.py @@ -0,0 +1,723 @@ +import math +import numpy as np + +from mindspore import nn +import mindspore.common.initializer as init + +from .layers.pooling import GlobalAvgPooling +from .layers.squeeze_excite import SqueezeExcite +from .registry import register_model +from .utils import load_pretrained + +__all__ = [ + 'regnet_x_200mf', + 'regnet_x_400mf', + 'regnet_x_600mf', + 'regnet_x_800mf', + 'regnet_x_1_6gf', + 'regnet_x_3_2gf', + 'regnet_x_4_0gf', + 'regnet_x_6_4gf', + 'regnet_x_8_0gf', + 'regnet_x_12gf', + 'regnet_x_16gf', + 'regnet_x_32gf', + 'regnet_y_200mf', + 'regnet_y_400mf', + 'regnet_y_600mf', + 'regnet_y_800mf', + 'regnet_y_1_6gf', + 'regnet_y_3_2gf', + 'regnet_y_4_0gf', + 'regnet_y_6_4gf', + 'regnet_y_8_0gf', + 'regnet_y_12gf', + 'regnet_y_16gf', + 'regnet_y_32gf' +] + + +def _cfg(url='', **kwargs): + return { + 'url': url, + 'num_classes': 1000, + 'first_conv': '', 'classifier': 'classifier', + **kwargs + } + + +default_cfgs = { + 'regnet_x_200mf': _cfg(url=''), + 'regnet_x_400mf': _cfg(url=''), + 'regnet_x_600mf': _cfg(url=''), + 'regnet_x_800mf': _cfg(url=''), + 'regnet_x_1_6gf': _cfg(url=''), + 'regnet_x_3_2gf': _cfg(url=''), + 'regnet_x_4_0gf': _cfg(url=''), + 'regnet_x_6_4gf': _cfg(url=''), + 'regnet_x_8_0gf': _cfg(url=''), + 'regnet_x_12gf': _cfg(url=''), + 'regnet_x_16gf': _cfg(url=''), + 'regnet_x_32gf': _cfg(url=''), + 'regnet_y_200mf': _cfg(url=''), + 'regnet_y_400mf': _cfg(url=''), + 'regnet_y_600mf': _cfg(url=''), + 'regnet_y_800mf': _cfg(url=''), + 'regnet_y_1_6gf': _cfg(url=''), + 'regnet_y_3_2gf': _cfg(url=''), + 'regnet_y_4_0gf': _cfg(url=''), + 'regnet_y_6_4gf': _cfg(url=''), + 'regnet_y_8_0gf': _cfg(url=''), + 'regnet_y_12gf': _cfg(url=''), + 'regnet_y_16gf': _cfg(url=''), + 'regnet_y_32gf': _cfg(url=''), +} + + +def conv2d(w_in, w_out, k, *, stride=1, groups=1, bias=False): + """Helper for building a conv2d layer.""" + assert k % 2 == 1, "Only odd size kernels supported to avoid padding issues." + s, p, g, b = stride, (k - 1) // 2, groups, bias + return nn.Conv2d(w_in, w_out, k, stride=s, pad_mode='pad', padding=p, group=g, has_bias=b) + + +def norm2d(w_in, eps=1e-5, mom=0.9): + """Helper for building a norm2d layer.""" + return nn.BatchNorm2d(num_features=w_in, eps=eps, momentum=mom) + + +def pool2d(_w_in, k, *, stride=1): + """Helper for building a pool2d layer.""" + assert k % 2 == 1, "Only odd size kernels supported to avoid padding issues." + padding = (k - 1) // 2 + pad2d = nn.Pad(((0, 0), (0, 0), (padding, padding), (padding, padding)), mode="CONSTANT") + max_pool = nn.MaxPool2d(kernel_size=k, stride=stride, pad_mode="valid") + return nn.SequentialCell([pad2d, max_pool]) + + +def gap2d(keep_dims=False): + """Helper for building a gap2d layer.""" + return GlobalAvgPooling(keep_dims) + + +def linear(w_in, w_out, *, bias=False): + """Helper for building a linear layer.""" + return nn.Dense(w_in, w_out, has_bias=bias) + + +def activation(): + """Helper for building an activation layer.""" + return nn.ReLU() + + +class ResStemCifar(nn.Cell): + """ResNet stem for CIFAR: 3x3, BN, AF.""" + + def __init__(self, w_in, w_out): + super(ResStemCifar, self).__init__() + self.conv = conv2d(w_in, w_out, 3) + self.bn = norm2d(w_out) + self.af = activation() + + def construct(self, x): + x = self.conv(x) + x = self.bn(x) + x = self.af(x) + return x + + +class ResStem(nn.Cell): + """ResNet stem for ImageNet: 7x7, BN, AF, MaxPool.""" + + def __init__(self, w_in, w_out): + super(ResStem, self).__init__() + self.conv = conv2d(w_in, w_out, 7, stride=2) + self.bn = norm2d(w_out) + self.af = activation() + self.pool = pool2d(w_out, 3, stride=2) + + def construct(self, x): + x = self.conv(x) + x = self.bn(x) + x = self.af(x) + x = self.pool(x) + return x + + +class SimpleStem(nn.Cell): + """Simple stem for ImageNet: 3x3, BN, AF.""" + + def __init__(self, w_in, w_out): + super(SimpleStem, self).__init__() + self.conv = conv2d(w_in, w_out, 3, stride=2) + self.bn = norm2d(w_out) + self.af = activation() + + def construct(self, x): + x = self.conv(x) + x = self.bn(x) + x = self.af(x) + return x + + +class VanillaBlock(nn.Cell): + """Vanilla block: [3x3 conv, BN, Relu] x2.""" + + def __init__(self, w_in, w_out, stride, _params): + super(VanillaBlock, self).__init__() + self.a = conv2d(w_in, w_out, 3, stride=stride) + self.a_bn = norm2d(w_out) + self.a_af = activation() + self.b = conv2d(w_out, w_out, 3) + self.b_bn = norm2d(w_out) + self.b_af = activation() + + def construct(self, x): + x = self.a(x) + x = self.a_bn(x) + x = self.a_af(x) + x = self.b(x) + x = self.b_bn(x) + x = self.b_af(x) + return x + + +class BasicTransform(nn.Cell): + """Basic transformation: [3x3 conv, BN, Relu] x2.""" + + def __init__(self, w_in, w_out, stride, _params): + super(BasicTransform, self).__init__() + self.a = conv2d(w_in, w_out, 3, stride=stride) + self.a_bn = norm2d(w_out) + self.a_af = activation() + self.b = conv2d(w_out, w_out, 3) + self.b_bn = norm2d(w_out) + self.b_bn.final_bn = True + + def construct(self, x): + x = self.a(x) + x = self.a_bn(x) + x = self.a_af(x) + x = self.b(x) + x = self.b_bn(x) + return x + + +class ResBasicBlock(nn.Cell): + """Residual basic block: x + f(x), f = basic transform.""" + + def __init__(self, w_in, w_out, stride, params): + super(ResBasicBlock, self).__init__() + self.proj, self.bn = None, None + if (w_in != w_out) or (stride != 1): + self.proj = conv2d(w_in, w_out, 1, stride=stride) + self.bn = norm2d(w_out) + self.f = BasicTransform(w_in, w_out, stride, params) + self.af = activation() + + def construct(self, x): + x_p = self.bn(self.proj(x)) if self.proj is not None else x + return self.af(x_p + self.f(x)) + + +class BottleneckTransform(nn.Cell): + """Bottleneck transformation: 1x1, 3x3 [+SE], 1x1.""" + + def __init__(self, w_in, w_out, stride, params): + super(BottleneckTransform, self).__init__() + w_b = int(round(w_out * params["bot_mul"])) + w_se = int(round(w_in * params["se_r"])) + groups = w_b // params["group_w"] + self.a = conv2d(w_in, w_b, 1) + self.a_bn = norm2d(w_b) + self.a_af = activation() + self.b = conv2d(w_b, w_b, 3, stride=stride, groups=groups) + self.b_bn = norm2d(w_b) + self.b_af = activation() + self.se = SqueezeExcite(in_channels=w_b, rd_channels=w_se) if w_se else None + self.c = conv2d(w_b, w_out, 1) + self.c_bn = norm2d(w_out) + self.c_bn.final_bn = True + + def construct(self, x): + x = self.a(x) + x = self.a_bn(x) + x = self.a_af(x) + x = self.b(x) + x = self.b_bn(x) + x = self.b_af(x) + x = self.se(x) if self.se is not None else x + x = self.c(x) + x = self.c_bn(x) + return x + + +class ResBottleneckBlock(nn.Cell): + """Residual bottleneck block: x + f(x), f = bottleneck transform.""" + + def __init__(self, w_in, w_out, stride, params): + super(ResBottleneckBlock, self).__init__() + self.proj, self.bn = None, None + if (w_in != w_out) or (stride != 1): + self.proj = conv2d(w_in, w_out, 1, stride=stride) + self.bn = norm2d(w_out) + self.f = BottleneckTransform(w_in, w_out, stride, params) + self.af = activation() + + def construct(self, x): + x_p = self.bn(self.proj(x)) if self.proj is not None else x + return self.af(x_p + self.f(x)) + + +class ResBottleneckLinearBlock(nn.Cell): + """Residual linear bottleneck block: x + f(x), f = bottleneck transform.""" + + def __init__(self, w_in, w_out, stride, params): + super(ResBottleneckLinearBlock, self).__init__() + self.has_skip = (w_in == w_out) and (stride == 1) + self.f = BottleneckTransform(w_in, w_out, stride, params) + + def construct(self, x): + return x + self.f(x) if self.has_skip else self.f(x) + + +class AnyStage(nn.Cell): + """AnyNet stage (sequence of blocks w/ the same output shape).""" + + def __init__(self, w_in, w_out, stride, d, block_fun, params): + super(AnyStage, self).__init__() + self.blocks = nn.CellList() + for _ in range(d): + block = block_fun(w_in, w_out, stride, params) + self.blocks.append(block) + stride, w_in = 1, w_out + + def construct(self, x): + for block in self.blocks: + x = block(x) + return x + + +class AnyHead(nn.Cell): + """AnyNet head: optional conv, AvgPool, 1x1.""" + + def __init__(self, w_in, head_width, num_classes): + super(AnyHead, self).__init__() + self.head_width = head_width + if head_width > 0: + self.conv = conv2d(w_in, head_width, 1) + self.bn = norm2d(head_width) + self.af = activation() + w_in = head_width + self.avg_pool = gap2d() + self.fc = linear(w_in, num_classes, bias=True) + + def construct(self, x): + x = self.af(self.bn(self.conv(x))) if self.head_width > 0 else x + x = self.avg_pool(x) + x = self.fc(x) + return x + + +def get_stem_fun(stem_type): + """Retrieves the stem function by name.""" + stem_funs = { + "res_stem_cifar": ResStemCifar, + "res_stem_in": ResStem, + "simple_stem_in": SimpleStem, + } + err_str = "Stem type '{}' not supported" + assert stem_type in stem_funs.keys(), err_str.format(stem_type) + return stem_funs[stem_type] + + +def get_block_fun(block_type): + """Retrieves the block function by name.""" + block_funs = { + "vanilla_block": VanillaBlock, + "res_basic_block": ResBasicBlock, + "res_bottleneck_block": ResBottleneckBlock, + "res_bottleneck_linear_block": ResBottleneckLinearBlock, + } + err_str = "Block type '{}' not supported" + assert block_type in block_funs.keys(), err_str.format(block_type) + return block_funs[block_type] + + +class AnyNet(nn.Cell): + """AnyNet model.""" + + @staticmethod + def anynet_get_params(depths, stem_type, stem_w, block_type, widths, strides, bot_muls, group_ws, head_w, + num_classes, se_r): + nones = [None for _ in depths] + return { + "stem_type": stem_type, + "stem_w": stem_w, + "block_type": block_type, + "depths": depths, + "widths": widths, + "strides": strides, + "bot_muls": bot_muls if bot_muls else nones, + "group_ws": group_ws if group_ws else nones, + "head_w": head_w, + "se_r": se_r, + "num_classes": num_classes, + } + + def __init__(self, depths, stem_type, stem_w, block_type, widths, strides, bot_muls, group_ws, head_w, num_classes, + se_r, in_channels): + super(AnyNet, self).__init__() + p = AnyNet.anynet_get_params(depths, stem_type, stem_w, block_type, widths, strides, bot_muls, group_ws, head_w, + num_classes, se_r) + stem_fun = get_stem_fun(p["stem_type"]) + block_fun = get_block_fun(p["block_type"]) + self.stem = stem_fun(in_channels, p["stem_w"]) + prev_w = p["stem_w"] + keys = ["depths", "widths", "strides", "bot_muls", "group_ws"] + self.stages = nn.CellList() + for i, (d, w, s, b, g) in enumerate(zip(*[p[k] for k in keys])): + params = {"bot_mul": b, "group_w": g, "se_r": p["se_r"]} + stage = AnyStage(prev_w, w, s, d, block_fun, params) + self.stages.append(stage) + prev_w = w + self.head = AnyHead(prev_w, p["head_w"], p["num_classes"]) + self._initialize_weights() + + def _initialize_weights(self) -> None: + """Initialize weights for cells.""" + for _, cell in self.cells_and_names(): + if isinstance(cell, nn.Conv2d): + fan_out = cell.kernel_size[0] * cell.kernel_size[1] * cell.out_channels + cell.weight.set_data( + init.initializer(init.Normal(sigma=math.sqrt(2.0 / fan_out), mean=0.0), + cell.weight.shape, cell.weight.dtype)) + elif isinstance(cell, nn.BatchNorm2d): + cell.gamma.set_data(init.initializer('ones', cell.gamma.shape, cell.gamma.dtype)) + cell.beta.set_data(init.initializer('zeros', cell.beta.shape, cell.beta.dtype)) + elif isinstance(cell, nn.Dense): + cell.weight.set_data( + init.initializer(init.Normal(sigma=0.01, mean=0.0), cell.weight.shape, cell.weight.dtype)) + if cell.bias is not None: + cell.bias.set_data(init.initializer('zeros', cell.bias.shape, cell.bias.dtype)) + + def construct(self, x): + x = self.stem(x) + for module in self.stages: + x = module(x) + x = self.head(x) + return x + + +def adjust_block_compatibility(ws, bs, gs): + """Adjusts the compatibility of widths, bottlenecks, and groups.""" + assert len(ws) == len(bs) == len(gs) + assert all(w > 0 and b > 0 and g > 0 for w, b, g in zip(ws, bs, gs)) + assert all(b < 1 or b % 1 == 0 for b in bs) + vs = [int(max(1, w * b)) for w, b in zip(ws, bs)] + gs = [int(min(g, v)) for g, v in zip(gs, vs)] + ms = [np.lcm(g, int(b)) if b > 1 else g for g, b in zip(gs, bs)] + vs = [max(m, int(round(v / m) * m)) for v, m in zip(vs, ms)] + ws = [int(v / b) for v, b in zip(vs, bs)] + assert all(w * b % g == 0 for w, b, g in zip(ws, bs, gs)) + return ws, bs, gs + + +def generate_regnet(w_a, w_0, w_m, d, q=8): + """Generates per stage widths and depths from RegNet parameters.""" + assert w_a >= 0 and w_0 > 0 and w_m > 1 and w_0 % q == 0 + # Generate continuous per-block ws + ws_cont = np.arange(d) * w_a + w_0 + # Generate quantized per-block ws + ks = np.round(np.log(ws_cont / w_0) / np.log(w_m)) + ws_all = w_0 * np.power(w_m, ks) + ws_all = np.round(np.divide(ws_all, q)).astype(int) * q + # Generate per stage ws and ds (assumes ws_all are sorted) + ws, ds = np.unique(ws_all, return_counts=True) + # Compute number of actual stages and total possible stages + num_stages, total_stages = len(ws), ks.max() + 1 + # Convert numpy arrays to lists and return + ws, ds, ws_all, ws_cont = (x.tolist() for x in (ws, ds, ws_all, ws_cont)) + return ws, ds, num_stages, total_stages, ws_all, ws_cont + + +def generate_regnet_full(w_a, w_0, w_m, d, stride, bot_mul, group_w): + """Generates per stage ws, ds, gs, bs, and ss from RegNet cfg.""" + ws, ds = generate_regnet(w_a, w_0, w_m, d)[0:2] + ss = [stride for _ in ws] + bs = [bot_mul for _ in ws] + gs = [group_w for _ in ws] + ws, bs, gs = adjust_block_compatibility(ws, bs, gs) + return ws, ds, ss, bs, gs + + +class RegNet(AnyNet): + """RegNet model.""" + + @staticmethod + def regnet_get_params(w_a, w_0, w_m, d, stride, bot_mul, group_w, stem_type, stem_w, block_type, head_w, + num_classes, se_r): + """Get AnyNet parameters that correspond to the RegNet.""" + ws, ds, ss, bs, gs = generate_regnet_full(w_a, w_0, w_m, d, stride, bot_mul, group_w) + return { + "stem_type": stem_type, + "stem_w": stem_w, + "block_type": block_type, + "depths": ds, + "widths": ws, + "strides": ss, + "bot_muls": bs, + "group_ws": gs, + "head_w": head_w, + "se_r": se_r, + "num_classes": num_classes, + } + + def __init__(self, w_a, w_0, w_m, d, group_w, stride=2, bot_mul=1.0, stem_type='simple_stem_in', stem_w=32, + block_type='res_bottleneck_block', head_w=0, num_classes=1000, se_r=0.0, in_channels=3): + params = RegNet.regnet_get_params(w_a, w_0, w_m, d, stride, bot_mul, group_w, stem_type, stem_w, block_type, + head_w, num_classes, se_r) + print(params) + super(RegNet, self).__init__(params['depths'], params['stem_type'], params['stem_w'], params['block_type'], + params['widths'], params['strides'], params['bot_muls'], params['group_ws'], + params['head_w'], params['num_classes'], params['se_r'], in_channels) + + +@register_model +def regnet_x_200mf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_x_200mf'] + model = RegNet(36.44, 24, 2.49, 13, 8, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_x_400mf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_x_400mf'] + model = RegNet(24.48, 24, 2.54, 22, 16, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_x_600mf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_x_600mf'] + model = RegNet(36.97, 48, 2.24, 16, 24, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_x_800mf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_x_800mf'] + model = RegNet(35.73, 56, 2.28, 16, 16, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_x_1_6gf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_x_1_6gf'] + model = RegNet(34.01, 80, 2.25, 18, 24, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_x_3_2gf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_x_3_2gf'] + model = RegNet(26.31, 88, 2.25, 25, 48, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_x_4_0gf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_x_4_0gf'] + model = RegNet(38.65, 96, 2.43, 23, 40, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_x_6_4gf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_x_6_4gf'] + model = RegNet(60.83, 184, 2.07, 17, 56, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_x_8_0gf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_x_8_0gf'] + model = RegNet(49.56, 80, 2.88, 23, 120, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_x_12gf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_x_12gf'] + model = RegNet(73.36, 168, 2.37, 19, 112, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_x_16gf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_x_16gf'] + model = RegNet(55.59, 216, 2.1, 22, 128, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_x_32gf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_x_32gf'] + model = RegNet(69.86, 320, 2.0, 23, 168, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_y_200mf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_y_200mf'] + model = RegNet(36.44, 24, 2.49, 13, 8, se_r=0.25, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_y_400mf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_y_400mf'] + model = RegNet(27.89, 48, 2.09, 16, 8, se_r=0.25, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_y_600mf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_y_600mf'] + model = RegNet(32.54, 48, 2.32, 15, 16, se_r=0.25, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_y_800mf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_y_800mf'] + model = RegNet(38.84, 56, 2.4, 14, 16, se_r=0.25, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_y_1_6gf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_y_1_6gf'] + model = RegNet(20.71, 48, 2.65, 27, 24, se_r=0.25, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_y_3_2gf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_y_3_2gf'] + model = RegNet(42.63, 80, 2.66, 21, 24, se_r=0.25, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_y_4_0gf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_y_4_0gf'] + model = RegNet(31.41, 96, 2.24, 22, 64, se_r=0.25, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_y_6_4gf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_y_6_4gf'] + model = RegNet(33.22, 112, 2.27, 25, 72, se_r=0.25, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_y_8_0gf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_y_8_0gf'] + model = RegNet(76.82, 192, 2.19, 17, 56, se_r=0.25, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_y_12gf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_y_12gf'] + model = RegNet(73.36, 168, 2.37, 19, 112, se_r=0.25, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_y_16gf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_y_16gf'] + model = RegNet(106.23, 200, 2.48, 18, 112, se_r=0.25, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model + + +@register_model +def regnet_y_32gf(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs): + default_cfg = default_cfgs['regnet_y_32gf'] + model = RegNet(115.89, 232, 2.53, 20, 232, se_r=0.25, num_classes=num_classes, in_channels=in_channels, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + return model \ No newline at end of file From af4f6a9f0b0b1cd0f25dfc3ab1607f2d7b9b3a50 Mon Sep 17 00:00:00 2001 From: Genius Patrick Date: Tue, 29 Nov 2022 11:25:29 +0800 Subject: [PATCH 13/32] add docstring to regnet --- mindcv/models/regnet.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/mindcv/models/regnet.py b/mindcv/models/regnet.py index f261ca41..a8a31804 100644 --- a/mindcv/models/regnet.py +++ b/mindcv/models/regnet.py @@ -1,3 +1,8 @@ +""" +MindSpore implementation of `RegNet`. +Refer to: Designing Network Design Spaces +""" + import math import numpy as np @@ -401,13 +406,21 @@ def _initialize_weights(self) -> None: if cell.bias is not None: cell.bias.set_data(init.initializer('zeros', cell.bias.shape, cell.bias.dtype)) - def construct(self, x): + def forward_features(self, x): x = self.stem(x) for module in self.stages: x = module(x) + return x + + def forward_head(self, x): x = self.head(x) return x + def construct(self, x): + x = self.forward_features(x) + x = self.forward_head(x) + return x + def adjust_block_compatibility(ws, bs, gs): """Adjusts the compatibility of widths, bottlenecks, and groups.""" @@ -452,7 +465,9 @@ def generate_regnet_full(w_a, w_0, w_m, d, stride, bot_mul, group_w): class RegNet(AnyNet): - """RegNet model.""" + r"""RegNet model class, based on + `"Designing Network Design Spaces" `_ + """ @staticmethod def regnet_get_params(w_a, w_0, w_m, d, stride, bot_mul, group_w, stem_type, stem_w, block_type, head_w, @@ -720,4 +735,4 @@ def regnet_y_32gf(pretrained: bool = False, num_classes: int = 1000, in_channels if pretrained: load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) - return model \ No newline at end of file + return model From 9ce1976d970f51501adaf97e9262360e45babb40 Mon Sep 17 00:00:00 2001 From: samithuang <285365963@qq.com> Date: Wed, 30 Nov 2022 02:47:49 +0000 Subject: [PATCH 14/32] add volo network --- mindcv/models/__init__.py | 2 +- mindcv/models/volo.py | 947 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 948 insertions(+), 1 deletion(-) create mode 100644 mindcv/models/volo.py diff --git a/mindcv/models/__init__.py b/mindcv/models/__init__.py index 6e7355a8..b78905d0 100644 --- a/mindcv/models/__init__.py +++ b/mindcv/models/__init__.py @@ -1,7 +1,7 @@ """models init""" from . import layers, convnext, densenet, dpn, efficientnet, ghostnet, googlenet, inception_v3, inception_v4, mnasnet,\ mobilenet_v1, mobilenet_v2, mobilenet_v3, model_factory, nasnet, pnasnet, registry, repvgg, res2net, resnet,\ - rexnet, shufflenetv1, shufflenetv2, sknet, squeezenet, swin_transformer, vgg, xception, convit, vit + rexnet, shufflenetv1, shufflenetv2, sknet, squeezenet, swin_transformer, vgg, xception, convit, vit, volo __all__ = [] __all__.extend(layers.__all__) diff --git a/mindcv/models/volo.py b/mindcv/models/volo.py new file mode 100644 index 00000000..40bb0d25 --- /dev/null +++ b/mindcv/models/volo.py @@ -0,0 +1,947 @@ +# Copyright 2021 Sea Limited. +# +# 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. + +""" +Vision OutLOoker (VOLO) implementation +""" +import mindspore as ms +import mindspore.nn as nn +from mindspore import ops +from mindspore import Parameter +from mindspore import Tensor +from mindspore import dtype as mstype +import mindspore.common.initializer as init +import mindspore.ops.functional as F +from mindspore import numpy as msnp +from .registry import register_model +from mindspore.ops import constexpr + +import time +import math +import numpy as np + +__all__ = [ + 'volo_d1', +] + + +def _cfg(url='', **kwargs): + return { + 'url': url, + 'num_classes': 1000, 'input_size': (3, 224, 224), 'pool_size': None, + 'crop_pct': .96, 'interpolation': 'bicubic', + 'mean': [0.485 * 255, 0.456 * 255, 0.406 * 255], + 'std': [0.229 * 255, 0.224 * 255, 0.225 * 255], + 'first_conv': 'patch_embed.proj', 'classifier': 'head', + **kwargs + } + + +default_cfgs = { + 'volo': _cfg(crop_pct=0.96), + 'volo_large': _cfg(crop_pct=1.15), +} + +def drop_path(x: Tensor, + drop_prob: float = 0., + training: bool = False, + scale_by_keep: bool = True) -> Tensor: + """ DropPath (Stochastic Depth) regularization layers """ + if drop_prob == 0. or not training: + return x + keep_prob = 1 - drop_prob + shape = (x.shape[0],) + (1,) * (x.ndim - 1) + random_tensor = ops.Dropout(keep_prob=keep_prob)(msnp.ones(shape))[1] + random_tensor = ops.Cast()(random_tensor, x.dtype) + if keep_prob > 0. and scale_by_keep: + random_tensor = ops.div(random_tensor, keep_prob) + return x * random_tensor + + +class DropPath(nn.Cell): + """ DropPath (Stochastic Depth) regularization layers """ + def __init__(self, + drop_prob: float = 0., + scale_by_keep: bool = True) -> None: + super().__init__() + self.drop_prob = drop_prob + self.scale_by_keep = scale_by_keep + + def construct(self, x: Tensor) -> Tensor: + return drop_path(x, self.drop_prob, self.training, self.scale_by_keep) + +# def unfold(img, kernel_size, stride=1, pad=0, dilation=1): +# """ +# unfold function +# """ +# # img = img.asnumpy() +# start = time.time() +# print("\nunfold shape", img.shape) +# batch_num, channel, height, width = img.shape +# out_h = (height + pad + pad - kernel_size - (kernel_size - 1) * (dilation - 1)) // stride + 1 +# out_w = (width + pad + pad - kernel_size - (kernel_size - 1) * (dilation - 1)) // stride + 1 + +# img = msnp.pad(img, [(0, 0), (0, 0), (pad, pad), (pad, pad)], 'constant') +# col = msnp.zeros((batch_num, channel, kernel_size, kernel_size, out_h, out_w), dtype=img.dtype) + +# for y in range(kernel_size): +# y_max = y + stride * out_h +# for x in range(kernel_size): +# x_max = x + stride * out_w +# col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride] + +# col = msnp.reshape(col, (batch_num, channel*kernel_size*kernel_size, out_h*out_w)) +# # col = Tensor(col) +# end = time.time() +# print("unfold rum time", end - start) +# return col + +import numpy as np +import mindspore as ms +from mindspore import nn, ops + + +# class NewConv2DTranspose(nn.Cell): +# def __init__(self, c, kernel_size, _pad, _stride, _dilation): +# super().__init__() +# self.conv_transpose2d = ops.Conv2DTranspose(c, kernel_size, pad_mode="pad", pad=_pad, +# stride=_stride, dilation=_dilation, group=c) + +# def construct(self, tensor, weight, output_shape): +# out = self.conv_transpose2d(tensor, weight, output_shape) + +# return out + +class Fold(nn.Cell): + def __init__(self, channels, output_size, kernel_size, dilation=1, padding=0, stride=1) -> None: + """Alternative implementation of fold layer via transposed convolution. + All parameters are the same as `"torch.nn.Fold" `_, + except for the additional `channels` parameter. We need `channels` to calculate the pre-allocated memory size of the convolution kernel. + :param channels: same as the `C` in the document of `"torch.nn.Fold" `_ + :type channels: int + """ + super().__init__() + def int2tuple(a): + if isinstance(a, int): + return (a, a) + return a + self.output_size, self.kernel_size, self.dilation, self.padding, self.stride = map(int2tuple, (output_size, kernel_size, dilation, padding, stride)) + self.h = int((self.output_size[0] + 2 * self.padding[0] - self.dilation[0] * (self.kernel_size[0] - 1) - 1) / self.stride[0] + 1) + self.w = int((self.output_size[1] + 2 * self.padding[1] - self.dilation[1] * (self.kernel_size[1] - 1) - 1) / self.stride[1] + 1) + self.k = self.kernel_size[0] * self.kernel_size[1] + self.c = channels + self.ck = self.c * self.k + weight = np.zeros((self.ck, 1, self.kernel_size[0], self.kernel_size[1])) + for i in range(self.ck): + xy = i % self.k + x = xy // self.kernel_size[1] + y = xy % self.kernel_size[1] + weight[i, 0, x, y] = 1 + + self.weight = Parameter(ms.Tensor(weight, ms.float16), requires_grad=False) + self.conv_transpose2d = ops.Conv2DTranspose(self.c, self.kernel_size, + pad_mode="pad", pad=(self.padding[0], self.padding[0], self.padding[1], self.padding[1]), + stride=stride, dilation=dilation, group=self.c) + + + def construct(self, tensor): + b, ck, l = tensor.shape + # todo: assert is not allowed in construct, how to check the shape? + # assert ck == self.c * self.k + # assert l == self.h * self.w + tensor = tensor.view((b, ck, self.h, self.w)) + out = self.conv_transpose2d(tensor.view((b, ck, self.h, self.w)), self.weight, (b, self.c, self.output_size[0], self.output_size[1])) + + return out + +def fold(col, input_shape, kernel_size, stride=1, pad=0): + """ + fold function + """ + batch_num, channel, height, width = input_shape + out_h = (height + pad + pad - kernel_size) // stride + 1 + out_w = (width + pad + pad - kernel_size) // stride + 1 + + col = msnp.reshape(col, (batch_num, channel, kernel_size, kernel_size, out_h, out_w)) + img = msnp.zeros((batch_num, + channel, + height + pad + pad + stride - 1, + width + pad + pad + stride - 1), dtype=col.dtype) + for y in range(kernel_size): + y_max = y + stride * out_h + for x in range(kernel_size): + x_max = x + stride * out_w + img[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :] + + out = img[:, :, pad:height + pad, pad:width + pad] + return out + + +class OutlookAttention(nn.Cell): + """ + Implementation of outlook attention + --dim: hidden dim + --num_heads: number of heads + --kernel_size: kernel size in each window for outlook attention + return: token features after outlook attention + """ + + def __init__(self, dim, num_heads, kernel_size=3, padding=1, stride=1, + qkv_bias=False, qk_scale=None, attn_drop=0., proj_drop=0.): + super().__init__() + head_dim = dim // num_heads + self.num_heads = num_heads + self.kernel_size = kernel_size + self.padding = padding + self.stride = stride + self.scale = qk_scale or head_dim**-0.5 + + self.v = nn.Dense(dim, dim, has_bias=qkv_bias) + self.attn = nn.Dense(dim, kernel_size**4 * num_heads) + + self.attn_drop = nn.Dropout(1.0 - attn_drop) + self.proj = nn.Dense(dim, dim) + self.proj_drop = nn.Dropout(1.0 - proj_drop) + + self.unfold = nn.Unfold(ksizes=[1, kernel_size, kernel_size, 1], strides=[1, stride, stride, 1], + rates=[1, 1, 1, 1]) + self.pool = nn.AvgPool2d(kernel_size=stride, stride=stride) + self.softmax = nn.Softmax(axis=-1) + self.reshape = ops.Reshape() + self.transpose = ops.Transpose() + self.batch_mat_mul = ops.BatchMatMul() + + def construct(self, x): + B, H, W, C = x.shape + + v = ops.Transpose()(self.v(x), (0, 3, 1, 2)) # B, C, H, W + + h = int((H - 1) / self.stride + 1) + w = int((W - 1) / self.stride + 1) + # start = time.time() + v = ops.pad(v, ((0, 0), (0, 0), (1, 1), (1,1))) # 舍弃ms的算子,改用函数定义 + # v = unfold(v, self.kernel_size, self.stride, self.padding) + v = self.unfold(v) + # end = time.time() + # print("ms-unfold run time", end - start) + # start = time.time() + v = self.reshape(v, (B, self.num_heads, C // self.num_heads, + self.kernel_size * self.kernel_size, + h * w)) + # end = time.time() + # print("\n") + # print("reshape run time", end - start) + v = self.transpose(v, (0, 1, 4, 3, 2)) # B,H,N,kxk,C/H + + attn = self.pool(self.transpose(x, (0, 3, 1, 2))) + attn = self.transpose(attn, (0, 2, 3, 1)) + attn = self.reshape(self.attn(attn), + (B, h * w, self.num_heads, self.kernel_size * self.kernel_size, + self.kernel_size * self.kernel_size)) + attn = self.transpose(attn, (0, 2, 1, 3, 4)) # B,H,N,kxk,kxk + attn = attn * self.scale + attn = self.softmax(attn) + attn = self.attn_drop(attn) + + x = self.transpose(self.batch_mat_mul(attn, v), (0, 1, 4, 3, 2)) + x = self.reshape(x, + (B, C * self.kernel_size * self.kernel_size, h * w)) + # print("H+", H, "W", W) + #fold = Fold(C, (H, W), self.kernel_size, padding=self.padding, stride=self.stride) + # print("before", x.shape) + #x = fold(x) + x = fold(x, input_shape=(B, C, H, W), kernel_size=self.kernel_size, + stride=self.stride, pad=self.padding) + # print("after", x.shape) + x = self.proj(self.transpose(x, (0, 2, 3, 1))) + x = self.proj_drop(x) + + return x + + +class Outlooker(nn.Cell): + """ + Implementation of outlooker layer: which includes outlook attention + MLP + Outlooker is the first stage in our VOLO + --dim: hidden dim + --num_heads: number of heads + --mlp_ratio: mlp ratio + --kernel_size: kernel size in each window for outlook attention + return: outlooker layer + """ + def __init__(self, dim, kernel_size, padding, stride=1, + num_heads=1,mlp_ratio=3., attn_drop=0., + drop_path=0., act_layer=nn.GELU, + norm_layer=nn.LayerNorm, qkv_bias=False, + qk_scale=None): + super().__init__() + self.norm1 = norm_layer([dim]) + self.attn = OutlookAttention(dim, num_heads, kernel_size=kernel_size, + padding=padding, stride=stride, + qkv_bias=qkv_bias, qk_scale=qk_scale, + attn_drop=attn_drop) + + self.drop_path = DropPath( + drop_path) if drop_path > 0. else ops.Identity() + + self.norm2 = norm_layer([dim]) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp(in_features=dim, + hidden_features=mlp_hidden_dim, + act_layer=act_layer) + # print("dim---", dim, "kernel_size---", kernel_size, "strid---", stride) + + def construct(self, x): + # print("\nxx",x.shape) + # print("\nnorm", self.norm1(x).shape) + # print("\nattn", self.attn(self.norm1(x)).shape) + x = x + self.drop_path(self.attn(self.norm1(x))) + x = x + self.drop_path(self.mlp(self.norm2(x))) + return x + + +class Mlp(nn.Cell): + "Implementation of MLP" + + def __init__(self, in_features, hidden_features=None, + out_features=None, act_layer=nn.GELU, + drop=0.): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Dense(in_features, hidden_features) + self.act = act_layer() + self.fc2 = nn.Dense(hidden_features, out_features) + self.drop = nn.Dropout(1.0 - drop) + + def construct(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop(x) + x = self.fc2(x) + x = self.drop(x) + return x + + +class Attention(nn.Cell): + "Implementation of self-attention" + + def __init__(self, dim, num_heads=8, qkv_bias=False, + qk_scale=None, attn_drop=0., proj_drop=0.): + super().__init__() + self.num_heads = num_heads + head_dim = dim // num_heads + self.scale = qk_scale or head_dim**-0.5 + + self.qkv = nn.Dense(dim, dim * 3, has_bias=qkv_bias) + self.attn_drop = nn.Dropout(1.0 - attn_drop) + self.proj = nn.Dense(dim, dim) + self.proj_drop = nn.Dropout(1.0 - proj_drop) + self.reshape = ops.Reshape() + self.transpose = ops.Transpose() + self.softmax = nn.Softmax(axis=-1) + self.batch_mat_mul_transpose = ops.BatchMatMul(transpose_b=True) + self.batch_mat_mul = ops.BatchMatMul() + + + def construct(self, x): + B, H, W, C = x.shape + + qkv = self.qkv(x) + qkv = self.reshape(qkv, (B, H * W, 3, self.num_heads, + C // self.num_heads)) + qkv = self.transpose(qkv, (2, 0, 3, 1, 4)) + q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple) + + attn = self.batch_mat_mul_transpose(q, k) * self.scale + attn = self.softmax(attn) + attn = self.attn_drop(attn) + + x = self.transpose(self.batch_mat_mul(attn, v), (0, 2, 1, 3)) + x = self.reshape(x, (B, H, W, C)) + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class Transformer(nn.Cell): + """ + Implementation of Transformer, + Transformer is the second stage in our VOLO + """ + def __init__(self, dim, num_heads, mlp_ratio=4., qkv_bias=False, + qk_scale=None, attn_drop=0., drop_path=0., + act_layer=nn.GELU, norm_layer=nn.LayerNorm): + super().__init__() + self.norm1 = norm_layer([dim]) + self.attn = Attention(dim, num_heads=num_heads, qkv_bias=qkv_bias, + qk_scale=qk_scale, attn_drop=attn_drop) + + # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here + self.drop_path = DropPath( + drop_path) if drop_path > 0. else ops.Identity() + + self.norm2 = norm_layer([dim]) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp(in_features=dim, + hidden_features=mlp_hidden_dim, + act_layer=act_layer) + + def construct(self, x): + x = x + self.drop_path(self.attn(self.norm1(x))) + x = x + self.drop_path(self.mlp(self.norm2(x))) + return x + + +class ClassAttention(nn.Cell): + """ + Class attention layer from CaiT, see details in CaiT + Class attention is the post stage in our VOLO, which is optional. + """ + def __init__(self, dim, num_heads=8, head_dim=None, qkv_bias=False, + qk_scale=None, attn_drop=0., proj_drop=0.): + super().__init__() + self.num_heads = num_heads + if head_dim is not None: + self.head_dim = head_dim + else: + head_dim = dim // num_heads + self.head_dim = head_dim + self.scale = qk_scale or head_dim**-0.5 + + self.kv = nn.Dense(dim, + self.head_dim * self.num_heads * 2, + has_bias=qkv_bias) + self.q = nn.Dense(dim, self.head_dim * self.num_heads, has_bias=qkv_bias) + self.attn_drop = nn.Dropout(1.0 - attn_drop) + self.proj = nn.Dense(self.head_dim * self.num_heads, dim) + self.proj_drop = nn.Dropout(1.0 - proj_drop) + self.reshape = ops.Reshape() + self.transpose = ops.Transpose() + self.batch_mat_mul_transpose = ops.BatchMatMul(transpose_b=True) + self.batch_mat_mul = ops.BatchMatMul() + self.softmax = nn.Softmax(axis=-1) + + def construct(self, x): + B, N, C = x.shape + + kv = self.kv(x) + kv = self.reshape(kv, (B, N, 2, self.num_heads, + self.head_dim)) + kv = self.transpose(kv, (2, 0, 3, 1, 4)) + k, v = kv[0], kv[1] # make torchscript happy (cannot use tensor as tuple) + q = self.q(x[:, :1, :]) + q = self.reshape(q, (B, self.num_heads, 1, self.head_dim)) + attn =self.batch_mat_mul_transpose(q * self.scale, k) + attn = self.softmax(attn) + attn = self.attn_drop(attn) + + cls_embed = self.transpose(self.batch_mat_mul(attn, v), (0, 2, 1, 3)) + cls_embed = self.reshape(cls_embed, (B, 1, self.head_dim * self.num_heads)) + cls_embed = self.proj(cls_embed) + cls_embed = self.proj_drop(cls_embed) + return cls_embed + + +class ClassBlock(nn.Cell): + """ + Class attention block from CaiT, see details in CaiT + We use two-layers class attention in our VOLO, which is optional. + """ + + def __init__(self, dim, num_heads, head_dim=None, mlp_ratio=4., + qkv_bias=False, qk_scale=None, drop=0., attn_drop=0., + drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm): + super().__init__() + self.norm1 = norm_layer([dim]) + self.attn = ClassAttention( + dim, num_heads=num_heads, head_dim=head_dim, qkv_bias=qkv_bias, + qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop) + # NOTE: drop path for stochastic depth + self.drop_path = DropPath( + drop_path) if drop_path > 0. else ops.Identity() + self.norm2 = norm_layer([dim]) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp(in_features=dim, + hidden_features=mlp_hidden_dim, + act_layer=act_layer, + drop=drop) + self.concat = ops.Concat(1) + + def construct(self, x): + cls_embed = x[:, :1] + cls_embed = cls_embed + self.drop_path(self.attn(self.norm1(x))) + cls_embed = cls_embed + self.drop_path(self.mlp(self.norm2(cls_embed))) + x = self.concat([cls_embed, x[:, 1:]]) + return x + + +def get_block(block_type, **kargs): + """ + get block by name, specifically for class attention block in here + """ + if block_type == 'ca': + return ClassBlock(**kargs) + + +# def rand_bbox(size, lam, scale=1): +# """ +# get bounding box as token labeling (https://github.com/zihangJiang/TokenLabeling) +# return: bounding box +# """ +# W = size[1] // scale +# H = size[2] // scale +# cut_rat = np.sqrt(1. - lam) +# cut_w = np.int(W * cut_rat) +# cut_h = np.int(H * cut_rat) + +# # uniform +# cx = np.random.randint(W) +# cy = np.random.randint(H) + +# bbx1 = np.clip(cx - cut_w // 2, 0, W) +# bby1 = np.clip(cy - cut_h // 2, 0, H) +# bbx2 = np.clip(cx + cut_w // 2, 0, W) +# bby2 = np.clip(cy + cut_h // 2, 0, H) + +# return int(bbx1), int(bby1), int(bbx2), int(bby2) + + +class PatchEmbed(nn.Cell): + """ + Image to Patch Embedding. + Different with ViT use 1 conv layer, we use 4 conv layers to do patch embedding + """ + + def __init__(self, img_size=224, stem_conv=False, stem_stride=1, + patch_size=8, in_channels=3, hidden_dim=64, embed_dim=384): + super().__init__() + assert patch_size in [4, 8, 16] + + self.stem_conv = stem_conv + if stem_conv: + self.conv = nn.SequentialCell( + nn.Conv2d(in_channels, hidden_dim, 7, stem_stride, + pad_mode='pad', padding=3), # 112x112 + nn.BatchNorm2d(hidden_dim), + nn.ReLU(), + nn.Conv2d(hidden_dim, hidden_dim, 3, 1, + pad_mode='pad', padding=1), # 112x112 + nn.BatchNorm2d(hidden_dim), + nn.ReLU(), + nn.Conv2d(hidden_dim, hidden_dim, 3, 1, + pad_mode='pad', padding=1), # 112x112 + nn.BatchNorm2d(hidden_dim), + nn.ReLU(), + ) + + self.proj = nn.Conv2d(hidden_dim, + embed_dim, + kernel_size=patch_size // stem_stride, + stride=patch_size // stem_stride) + self.num_patches = (img_size // patch_size) * (img_size // patch_size) + + def construct(self, x): + if self.stem_conv: + x = self.conv(x) + x = self.proj(x) # B, C, H, W + return x + + +class Downsample(nn.Cell): + """ + Image to Patch Embedding, downsampling between stage1 and stage2 + """ + def __init__(self, in_embed_dim, out_embed_dim, patch_size): + super().__init__() + self.proj = nn.Conv2d(in_embed_dim, out_embed_dim, + kernel_size=patch_size, stride=patch_size) + self.transpose = ops.Transpose() + + def construct(self, x): + x = self.transpose(x, (0, 3, 1, 2)) + x = self.proj(x) # B, C, H, W + x = self.transpose(x, (0, 2, 3, 1)) + return x + + +def outlooker_blocks(block_fn, index, dim, layers, num_heads=1, kernel_size=3, + padding=1,stride=1, mlp_ratio=3., qkv_bias=False, qk_scale=None, + attn_drop=0, drop_path_rate=0., **kwargs): + """ + generate outlooker layer in stage1 + return: outlooker layers + """ + blocks = [] + for block_idx in range(layers[index]): + block_dpr = drop_path_rate * (block_idx + + sum(layers[:index])) / (sum(layers) - 1) + blocks.append(block_fn(dim, kernel_size=kernel_size, padding=padding, + stride=stride, num_heads=num_heads, mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, + drop_path=block_dpr)) + + blocks = nn.SequentialCell(*blocks) + + return blocks + + +def transformer_blocks(block_fn, index, dim, layers, num_heads, mlp_ratio=3., + qkv_bias=False, qk_scale=None, attn_drop=0, + drop_path_rate=0., **kwargs): + """ + generate transformer layers in stage2 + return: transformer layers + """ + blocks = [] + for block_idx in range(layers[index]): + block_dpr = drop_path_rate * (block_idx + + sum(layers[:index])) / (sum(layers) - 1) + blocks.append( + block_fn(dim, num_heads, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + qk_scale=qk_scale, + attn_drop=attn_drop, + drop_path=block_dpr)) + + blocks = nn.SequentialCell(*blocks) + + return blocks + + +class VOLO(nn.Cell): + """ + Vision Outlooker, the main class of our model + --layers: [x,x,x,x], four blocks in two stages, the first block is outlooker, the + other three are transformer, we set four blocks, which are easily + applied to downstream tasks + --img_size, --in_channels, --num_classes: these three are very easy to understand + --patch_size: patch_size in outlook attention + --stem_hidden_dim: hidden dim of patch embedding, d1-d4 is 64, d5 is 128 + --embed_dims, --num_heads: embedding dim, number of heads in each block + --downsamples: flags to apply downsampling or not + --outlook_attention: flags to apply outlook attention or not + --mlp_ratios, --qkv_bias, --qk_scale, --drop_rate: easy to undertand + --attn_drop_rate, --drop_path_rate, --norm_layer: easy to undertand + --post_layers: post layers like two class attention layers using [ca, ca], + if yes, return_mean=False + --return_mean: use mean of all feature tokens for classification, if yes, no class token + --return_dense: use token labeling, details are here: + https://github.com/zihangJiang/TokenLabeling + --mix_token: mixing tokens as token labeling, details are here: + https://github.com/zihangJiang/TokenLabeling + --pooling_scale: pooling_scale=2 means we downsample 2x + --out_kernel, --out_stride, --out_padding: kerner size, + stride, and padding for outlook attention + """ + def __init__(self, layers, img_size=224, in_channels=3, num_classes=1000, patch_size=8, + stem_hidden_dim=64, embed_dims=None, num_heads=None, downsamples=None, + outlook_attention=None, mlp_ratios=None, qkv_bias=False, qk_scale=None, + drop_rate=0., attn_drop_rate=0., drop_path_rate=0., norm_layer=nn.LayerNorm, + post_layers=None, return_mean=False, return_dense=True, mix_token=True, + pooling_scale=2, out_kernel=3, out_stride=2, out_padding=1): + + super().__init__() + self.num_classes = num_classes + self.patch_embed = PatchEmbed(stem_conv=True, stem_stride=2, patch_size=patch_size, + in_channels=in_channels, hidden_dim=stem_hidden_dim, + embed_dim=embed_dims[0]) + print() + # inital positional encoding, we add positional encoding after outlooker blocks + self.pos_embed = Parameter( + ops.Zeros()((1, img_size // patch_size // pooling_scale, + img_size // patch_size // pooling_scale, + embed_dims[-1]), mstype.float32)) + self.pos_drop = nn.Dropout(1.0 - drop_rate) + + # set the main block in network + network = [] + for i in range(len(layers)): + if outlook_attention[i]: + # stage 1 + stage = outlooker_blocks(Outlooker, i, embed_dims[i], layers, + downsample=downsamples[i], num_heads=num_heads[i], + kernel_size=out_kernel, stride=out_stride, + padding=out_padding, mlp_ratio=mlp_ratios[i], + qkv_bias=qkv_bias, qk_scale=qk_scale, + attn_drop=attn_drop_rate, norm_layer=norm_layer) + network.append(stage) + else: + # stage 2 + stage = transformer_blocks(Transformer, i, embed_dims[i], layers, + num_heads[i], mlp_ratio=mlp_ratios[i], + qkv_bias=qkv_bias, qk_scale=qk_scale, + drop_path_rate=drop_path_rate, + attn_drop=attn_drop_rate, + norm_layer=norm_layer) + network.append(stage) + + if downsamples[i]: + # downsampling between two stages + network.append(Downsample(embed_dims[i], embed_dims[i + 1], 2)) + + self.network = nn.CellList(network) + + # set post block, for example, class attention layers + self.post_network = None + if post_layers is not None: + self.post_network = nn.CellList([ + get_block(post_layers[i], + dim=embed_dims[-1], + num_heads=num_heads[-1], + mlp_ratio=mlp_ratios[-1], + qkv_bias=qkv_bias, + qk_scale=qk_scale, + attn_drop=attn_drop_rate, + drop_path=0., + norm_layer=norm_layer) + for i in range(len(post_layers)) + ]) + self.cls_token = Parameter(ops.Zeros()((1, 1, embed_dims[-1]), mstype.float32)) + self.cls_token.set_data(init.initializer(init.TruncatedNormal(sigma=.02), self.cls_token.data.shape)) + + # set output type + self.return_mean = return_mean # if yes, return mean, not use class token + self.return_dense = return_dense # if yes, return class token and all feature tokens + if return_dense: + assert not return_mean, "cannot return both mean and dense" + self.mix_token = mix_token + self.pooling_scale = pooling_scale + if mix_token: # enable token mixing, see token labeling for details. + self.beta = 1.0 + assert return_dense, "return all tokens if mix_token is enabled" + if return_dense: + self.aux_head = nn.Dense( + embed_dims[-1], + num_classes) if num_classes > 0 else ops.Identity() + self.norm = norm_layer([embed_dims[-1]]) + + # Classifier head + self.head = nn.Dense( + embed_dims[-1], num_classes) if num_classes > 0 else ops.Identity() + + self.pos_embed.set_data(init.initializer(init.TruncatedNormal(sigma=.02), self.pos_embed.data.shape)) + self._init_weights() + + def _init_weights(self): + for name, m in self.cells_and_names(): + if isinstance(m, nn.Dense): + m.weight.set_data(init.initializer(init.TruncatedNormal(sigma=.02), m.weight.data.shape)) + if m.bias is not None: + m.bias.set_data(init.initializer(init.Constant(0), m.bias.shape)) + elif isinstance(m, nn.LayerNorm): + m.gamma.set_data(init.initializer(init.Constant(1), m.gamma.shape)) + m.beta.set_data(init.initializer(init.Constant(0), m.beta.shape)) + + def forward_embeddings(self, x): + # patch embedding + x = self.patch_embed(x) + # B,C,H,W-> B,H,W,C + x = ops.Transpose()(x, (0, 2, 3, 1)) + return x + + def forward_tokens(self, x): + for idx, block in enumerate(self.network): + if idx == 2: # add positional encoding after outlooker blocks + x = x + self.pos_embed + x = self.pos_drop(x) + x = block(x) + + B, H, W, C = x.shape + x = ops.Reshape()(x, (B, -1, C)) + return x + + def forward_cls(self, x): + # B, N, C = x.shape + cls_tokens = ops.broadcast_to(self.cls_token, (x.shape[0], -1, -1)) + x = ops.Cast()(x, cls_tokens.dtype) + x = ops.Concat(1)([cls_tokens, x]) + for block in self.post_network: + x = block(x) + return x + + def construct(self, x): + # step1: patch embedding + x = self.forward_embeddings(x) + + # patch_h, patch_w = x.shape[1] // self.pooling_scale, x.shape[ + # 2] // self.pooling_scale + # # mix token, see token labeling for details. + # if self.mix_token and self._phase == 'train': + # lam = np.random.beta(self.beta, self.beta) + # bbx1, bby1, bbx2, bby2 = rand_bbox(x.shape, lam, scale=self.pooling_scale) + # temp_x = x.copy() + # sbbx1,sbby1,sbbx2,sbby2=self.pooling_scale*bbx1,self.pooling_scale*bby1,\ + # self.pooling_scale*bbx2,self.pooling_scale*bby2 + # temp_x[:, sbbx1:sbbx2, sbby1:sbby2, :] = ops.ReverseV2([0])(x)[:, sbbx1:sbbx2, sbby1:sbby2, :] + # x = temp_x + # else: + # bbx1, bby1, bbx2, bby2 = 0, 0, 0, 0 + + # step2: tokens learning in the two stages + x = self.forward_tokens(x) + + # step3: post network, apply class attention or not + if self.post_network is not None: + x = self.forward_cls(x) + x = self.norm(x) + + if self.return_mean: # if no class token, return mean + return self.head(ops.mean(x, 1)) + + x_cls = self.head(x[:, 0]) + if not self.return_dense: + return x_cls + + # x_aux = self.aux_head( + # x[:, 1:] + # ) # generate classes in all feature tokens, see token labeling + + # if self._phase == 'predict': + # return x_cls + 0.5 * x_aux.max(1)[0] + + # if self.mix_token and self._phase == 'train': # reverse "mix token", see token labeling for details. + # x_aux = ops.Reshape()(x_aux, (x_aux.shape[0], patch_h, patch_w, x_aux.shape[-1])) + + # temp_x = x_aux.copy() + # temp_x[:, bbx1:bbx2, bby1:bby2, :] = ops.ReverseV2([0])(x_aux)[:, bbx1:bbx2, bby1:bby2, :] + # x_aux = temp_x + + # x_aux = ops.Reshape()(x_aux, (x_aux.shape[0], patch_h * patch_w, x_aux.shape[-1])) + + # return these: 1. class token, 2. classes from all feature tokens, 3. bounding box + return x_cls # , x_aux, (bbx1, bby1, bbx2, bby2) + +@register_model +def volo_d1(pretrained=False, **kwargs): + """ + VOLO-D1 model, Params: 27M + --layers: [x,x,x,x], four blocks in two stages, the first stage(block) is outlooker, + the other three blocks are transformer, we set four blocks, which are easily + applied to downstream tasks + --embed_dims, --num_heads,: embedding dim, number of heads in each block + --downsamples: flags to apply downsampling or not in four blocks + --outlook_attention: flags to apply outlook attention or not + --mlp_ratios: mlp ratio in four blocks + --post_layers: post layers like two class attention layers using [ca, ca] + See detail for all args in the class VOLO() + """ + layers = [4, 4, 8, 2] # num of layers in the four blocks + embed_dims = [192, 384, 384, 384] + num_heads = [6, 12, 12, 12] + mlp_ratios = [3, 3, 3, 3] + downsamples = [True, False, False, False] # do downsampling after first block + outlook_attention = [True, False, False, False ] + # first block is outlooker (stage1), the other three are transformer (stage2) + model = VOLO(layers, + embed_dims=embed_dims, + num_heads=num_heads, + mlp_ratios=mlp_ratios, + downsamples=downsamples, + outlook_attention=outlook_attention, + post_layers=['ca', 'ca'], + **kwargs) + model.default_cfg = default_cfgs['volo'] + return model + +@register_model +def volo_d2(pretrained=False, **kwargs): + """ + VOLO-D2 model, Params: 59M + """ + layers = [6, 4, 10, 4] + embed_dims = [256, 512, 512, 512] + num_heads = [8, 16, 16, 16] + mlp_ratios = [3, 3, 3, 3] + downsamples = [True, False, False, False] + outlook_attention = [True, False, False, False] + model = VOLO(layers, + embed_dims=embed_dims, + num_heads=num_heads, + mlp_ratios=mlp_ratios, + downsamples=downsamples, + outlook_attention=outlook_attention, + post_layers=['ca', 'ca'], + **kwargs) + model.default_cfg = default_cfgs['volo'] + return model + +@register_model +def volo_d3(pretrained=False, **kwargs): + """ + VOLO-D3 model, Params: 86M + """ + layers = [8, 8, 16, 4] + embed_dims = [256, 512, 512, 512] + num_heads = [8, 16, 16, 16] + mlp_ratios = [3, 3, 3, 3] + downsamples = [True, False, False, False] + outlook_attention = [True, False, False, False] + model = VOLO(layers, + embed_dims=embed_dims, + num_heads=num_heads, + mlp_ratios=mlp_ratios, + downsamples=downsamples, + outlook_attention=outlook_attention, + post_layers=['ca', 'ca'], + **kwargs) + model.default_cfg = default_cfgs['volo'] + return model + +@register_model +def volo_d4(pretrained=False, **kwargs): + """ + VOLO-D4 model, Params: 193M + """ + layers = [8, 8, 16, 4] + embed_dims = [384, 768, 768, 768] + num_heads = [12, 16, 16, 16] + mlp_ratios = [3, 3, 3, 3] + downsamples = [True, False, False, False] + outlook_attention = [True, False, False, False] + model = VOLO(layers, + embed_dims=embed_dims, + num_heads=num_heads, + mlp_ratios=mlp_ratios, + downsamples=downsamples, + outlook_attention=outlook_attention, + post_layers=['ca', 'ca'], + **kwargs) + model.default_cfg = default_cfgs['volo_large'] + return model + +@register_model +def volo_d5(pretrained=False, **kwargs): + """ + VOLO-D5 model, Params: 296M + stem_hidden_dim=128, the dim in patch embedding is 128 for VOLO-D5 + """ + layers = [12, 12, 20, 4] + embed_dims = [384, 768, 768, 768] + num_heads = [12, 16, 16, 16] + mlp_ratios = [4, 4, 4, 4] + downsamples = [True, False, False, False] + outlook_attention = [True, False, False, False] + model = VOLO(layers, + embed_dims=embed_dims, + num_heads=num_heads, + mlp_ratios=mlp_ratios, + downsamples=downsamples, + outlook_attention=outlook_attention, + post_layers=['ca', 'ca'], + stem_hidden_dim=128, + **kwargs) + model.default_cfg = default_cfgs['volo_large'] + return model From efaec938630dcaf914127b8b87dfe889578492ef Mon Sep 17 00:00:00 2001 From: a1085728420 <1085728420@qq.com> Date: Wed, 30 Nov 2022 11:47:29 +0800 Subject: [PATCH 15/32] add convit_small_ascend.yaml --- configs/convit/README.md | 2 +- configs/convit/convit_small_ascend.yaml | 70 +++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 configs/convit/convit_small_ascend.yaml diff --git a/configs/convit/README.md b/configs/convit/README.md index 6faec5f9..7a0b6feb 100644 --- a/configs/convit/README.md +++ b/configs/convit/README.md @@ -26,7 +26,7 @@ ConViT combines e the strengths of convolutional architectures and Vision Transf | GPU | convit_tiny_plus | | | | | | | | | | Ascend | convit_tiny_plus | 77.00 | 93.60 | | | 247 | | | | | GPU | convit_small | | | | | | | | | -| Ascend | convit_small | | | | | | | | | +| Ascend | convit_small | 81.63 | 95.59 | | | 490 | | | | | GPU | convit_small_plus | | | | | | | | | | Ascend | convit_small_plus | | | | | | | | | | GPU | convit_base | | | | | | | | | diff --git a/configs/convit/convit_small_ascend.yaml b/configs/convit/convit_small_ascend.yaml new file mode 100644 index 00000000..cfad77fc --- /dev/null +++ b/configs/convit/convit_small_ascend.yaml @@ -0,0 +1,70 @@ +# Copyright 2022 Huawei Technologies Co., Ltd +# +# 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. +# ============================================================================ + +# system config +mode: 0 +distribute: True +num_parallel_workers: 8 + +# dataset config +dataset: 'imagenet' +data_dir: '' +shuffle: True +dataset_download: False +batch_size: 192 +drop_remainder: True + +# Augmentation config +image_resize: 224 +scale: [0.08, 1.0] +ratio: [0.75, 1.333] +hflip: 0.5 +interpolation: 'bicubic' +auto_augment: 'autoaug-mstd0.5' +re_prob: 0.1 +mixup: 0.2 +cutmix: 1.0 +cutmix_prob: 1.0 +crop_pct: 0.915 +color_jitter: 0.4 + +# model config +model: 'convit_small' +num_classes: 1000 +pretrained: False +ckpt_path: '' +keep_checkpoint_max: 10 +ckpt_save_dir: './ckpt' +epoch_size: 300 +dataset_sink_mode: True +amp_level: 'O2' + +# loss config +loss: 'CE' +label_smoothing: 0.1 + +# lr scheduler config +scheduler: 'warmup_cosine_decay' +lr: 0.0007 +min_lr: 0.000001 +warmup_epochs: 40 +decay_epochs: 260 + +# optimizer config +opt: 'adamw' +weight_decay: 0.05 +loss_scale: 1024 +filter_bias_and_bn: True +use_nesterov: False From 0a80ddac4ce350f731def1fd26f8f75b8ced1be8 Mon Sep 17 00:00:00 2001 From: CAOANJIA <1134692141@qq.com> Date: Wed, 30 Nov 2022 15:30:39 +0800 Subject: [PATCH 16/32] add mnasnet, modify eps config --- config.py | 2 + configs/mnasnet/README.md | 72 ++++++++++++++++++++++++ configs/mnasnet/README_CN.md | 61 ++++++++++++++++++++ configs/mnasnet/mnasnet.png | Bin 0 -> 56128 bytes configs/mnasnet/mnasnet0.75_ascend.yaml | 51 +++++++++++++++++ configs/mnasnet/mnasnet0.75_gpu.yaml | 51 +++++++++++++++++ configs/mnasnet/mnasnet1.0_ascend.yaml | 51 +++++++++++++++++ configs/mnasnet/mnasnet1.0_gpu.yaml | 51 +++++++++++++++++ configs/mnasnet/mnasnet1.4_ascend.yaml | 51 +++++++++++++++++ configs/mnasnet/mnasnet1.4_gpu.yaml | 51 +++++++++++++++++ mindcv/models/mnasnet.py | 30 +++++++--- mindcv/optim/optim_factory.py | 2 + train.py | 3 +- 13 files changed, 467 insertions(+), 9 deletions(-) create mode 100644 configs/mnasnet/README.md create mode 100644 configs/mnasnet/README_CN.md create mode 100644 configs/mnasnet/mnasnet.png create mode 100644 configs/mnasnet/mnasnet0.75_ascend.yaml create mode 100644 configs/mnasnet/mnasnet0.75_gpu.yaml create mode 100644 configs/mnasnet/mnasnet1.0_ascend.yaml create mode 100644 configs/mnasnet/mnasnet1.0_gpu.yaml create mode 100644 configs/mnasnet/mnasnet1.4_ascend.yaml create mode 100644 configs/mnasnet/mnasnet1.4_gpu.yaml diff --git a/config.py b/config.py index 3f423a4d..39c90373 100644 --- a/config.py +++ b/config.py @@ -152,6 +152,8 @@ def create_parser(): help='Enables the Nesterov momentum (default=False)') group.add_argument('--filter_bias_and_bn', type=str2bool, nargs='?', const=True, default=True, help='Filter Bias and BatchNorm (default=True)') + group.add_argument('--eps', type=float, default=1e-10, + help='Term Added to the Denominator to Improve Numerical Stability (default=1e-10)') # Scheduler parameters group = parser.add_argument_group('Scheduler parameters') diff --git a/configs/mnasnet/README.md b/configs/mnasnet/README.md new file mode 100644 index 00000000..063f37a9 --- /dev/null +++ b/configs/mnasnet/README.md @@ -0,0 +1,72 @@ +# MnasNet +> [MnasNet: Platform-Aware Neural Architecture Search for Mobile](https://arxiv.org/abs/1807.11626) + +## Introduction +*** + +Designing convolutional neural networks (CNN) for mobile devices is challenging because mobile models need to be small and fast, yet still accurate. Although significant efforts have been dedicated to design and improve mobile CNNs on all dimensions, it is very difficult to manually balance these trade-offs when there are so many architectural possibilities to consider. In this paper, we propose an automated mobile neural architecture search (MNAS) approach, which explicitly incorporate model latency into the main objective so that the search can identify a model that achieves a good trade-off between accuracy and latency. Unlike previous work, where latency is considered via another, often inaccurate proxy (e.g., FLOPS), our approach directly measures real-world inference latency by executing the model on mobile phones. To further strike the right balance between flexibility and search space size, we propose a novel factorized hierarchical search space that encourages layer diversity throughout the network. + +![](mnasnet.png) + +## Results +*** + +| Model | Context | Top-1 (%) | Top-5 (%) | Params (M) | Train T. | Infer T. | Download | Config | Log | +|-----------------|-----------|-------|-------|:----------:|-------|--------|---|--------|--------------| +| MnasNet-B1-0_75 | D910x8-G | 71.81 | 90.53 | 3.20 | 96s/epoch | | [model]() | [cfg]() | [log]() | +| MnasNet-B1-1_0 | D910x8-G | 74.28 | 91.70 | 4.42 | 96s/epoch | | [model]() | [cfg]() | [log]() | +| MnasNet-B1-1_4 | D910x8-G | 76.01 | 92.83 | 7.16 | 121s/epoch | | [model]() | [cfg]() | [log]() | + +#### Notes +- All models are trained on ImageNet-1K training set and the top-1 accuracy is reported on the validatoin set. +- Context: GPU_TYPE x pieces - G/F, G - graph mode, F - pynative mode with ms function. + +## Quick Start +*** +### Preparation + +#### Installation +Please refer to the [installation instruction](https://github.com/mindspore-ecosystem/mindcv#installation) in MindCV. + +#### Dataset Preparation +Please download the [ImageNet-1K](https://www.image-net.org/download.php) dataset for model training and validation. + +### Training + +- **Hyper-parameters.** The hyper-parameter configurations for producing the reported results are stored in the yaml files in `mindcv/configs/mnasnet` folder. For example, to train with one of these configurations, you can run: + + ```shell + export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 + mpirun -n 8 python train.py -c configs/mnasnet/mnasnet0.75_gpu.yaml --data_dir /path/to/imagenet + ``` + + Note that the number of GPUs/Ascends and batch size will influence the training results. To reproduce the training result at most, it is recommended to use the **same number of GPUs/Ascends** with the same batch size. + +- **Finetuning.** Here is an example for finetuning a pretrained mnasnet0_75 on CIFAR10 dataset using Rmsprop optimizer. + + ```shell + python train.py --model=mnasnet0_75 --pretrained --opt=rmsprop --lr=0.001 dataset=cifar10 --num_classes=10 --dataset_download + ``` + +Detailed adjustable parameters and their default value can be seen in [config.py](../../config.py). + +### Validation + +- To validate the trained model, you can use `validate.py`. Here is an example for mnasnet0_75 to verify the accuracy of pretrained weights. + + ```shell + python validate.py --model=mnasnet0_75 --dataset=imagenet --val_split=val --pretrained + ``` + +- To validate the model, you can use `validate.py`. Here is an example for mnasnet0_75 to verify the accuracy of your training. + + ```shell + python validate.py --model=mnasnet0_75 --dataset=imagenet --val_split=val --ckpt_path='./ckpt/mnasnet0_75-best.ckpt' + ``` + +### Deployment (optional) + +Please refer to the deployment tutorial in MindCV. + + + diff --git a/configs/mnasnet/README_CN.md b/configs/mnasnet/README_CN.md new file mode 100644 index 00000000..823f1ff9 --- /dev/null +++ b/configs/mnasnet/README_CN.md @@ -0,0 +1,61 @@ +# DenseNet + +*** +> [MnasNet: Platform-Aware Neural Architecture Search for Mobile](https://arxiv.org/abs/1807.11626) + +## 模型简介 + +*** +为移动设备设计卷积神经网络(CNN)是一个挑战,因为移动模型需要小而快,但仍然准确。尽管在设计和改进移动cnn方面已经付出了大量的努力,但当有这么多的架构可能性需要考虑时,手动平衡这些权衡是非常困难的。在本文中,我们提出了一种自动移动神经体系结构搜索(MNAS)方法,该方法显式地将模型延迟纳入主要目标,以便搜索可以识别一个在精度和延迟之间实现良好权衡的模型。与之前的工作不同,延迟是通过另一个通常不准确的代理(如FLOPS)来考虑的,我们的方法通过在手机上执行模型直接测量真实世界的推断延迟。为了进一步在灵活性和搜索空间大小之间取得平衡,我们提出了一种新的可分解层次搜索空间,以鼓励整个网络的层次多样性。 +![](mnasnet.png) + +## 性能指标 + +*** + +| | | | | Pynative | Pynative | Graph | Graph | | | +| :----: | ------------ | :-------: | :-------: | :-------------: | :--------: | :------------: | :--------: | :-------: | :--------: | +| | Model | Top-1 (%) | Top-5 (%) | train (s/epoch) | Infer (ms) | train(s/epoch) | Infer (ms) | Download | Config | +| GPU | MnasNet-B1-0_75 | 72.15 | 90.53 | | | | | | | +| Ascend | MnasNet-B1-0_75 | 71.81 | 90.53 | | | 96 | | [model]() | [config]() | +| GPU | MnasNet-B1-1_0 | 74.31 | 91.89 | | | | | | | +| Ascend | MnasNet-B1-1_0 | 74.28 | 91.70 | | | 96 | | [model]() | [config]() | +| GPU | MnasNet-B1-1_0 | | | | | | | | | +| Ascend | MnasNet-B1-1_4 | 76.01 | 92.83 | | | 121 | | [model]() | [config]() | + +## 示例 + +*** + +### 训练 + +- 下面是使用预设的yaml配置文件启动训练的示例. + +> [configs文件夹](../../configs)中列出了mindcv套件所包含的模型的各个规格的yaml配置文件(在ImageNet数据集上训练和验证的配置)。 + + ```shell + export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 + mpirun -n 8 python train.py -c configs/mnasnet/mnasnet0.75_gpu.yaml --data_dir /path/to/imagenet + ``` + +- 下面是使用在ImageNet上预训练的mnasnet0_75模型和Rmsprop优化器在CIFAR10数据集上进行微调的示例。 + + ```shell + python train.py --model=mnasnet0_75 --pretrained --opt=rmsprop --lr=0.001 dataset=cifar10 --num_classes=10 --dataset_download + ``` + +详细的可调参数及其默认值可以在[config.py](../../config.py)中查看。 + +### 验证 + +- 下面是使用`validate.py`文件验证mnasnet0_75的预训练模型的精度的示例。 + + ```shell + python validate.py --model=mnasnet0_75 --dataset=imagenet --val_split=val --pretrained + ``` + +- 下面是使用`validate.py`文件验证mnasnet0_75的自定义参数文件的精度的示例。 + + ```shell + python validate.py --model=mnasnet0_75 --dataset=imagenet --val_split=val --ckpt_path='./ckpt/mnasnet0_75-best.ckpt' + ``` diff --git a/configs/mnasnet/mnasnet.png b/configs/mnasnet/mnasnet.png new file mode 100644 index 0000000000000000000000000000000000000000..61f0728eb034eff381f2a71b0d30b3b227ea000f GIT binary patch literal 56128 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 literal 0 HcmV?d00001 diff --git a/configs/mnasnet/mnasnet0.75_ascend.yaml b/configs/mnasnet/mnasnet0.75_ascend.yaml new file mode 100644 index 00000000..c96bf9da --- /dev/null +++ b/configs/mnasnet/mnasnet0.75_ascend.yaml @@ -0,0 +1,51 @@ +# system config +mode: 0 +distribute: True +num_parallel_workers: 8 + +# dataset config +dataset: 'imagenet' +data_dir: '' +shuffle: True +dataset_download: False +batch_size: 256 +drop_remainder: True + +# Augmentation config +image_resize: 224 +scale: [0.08, 1.0] +ratio: [0.75, 1.333] +hflip: 0.5 +interpolation: 'bilinear' +crop_pct: 0.875 + +# model config +model: 'mnasnet0_75' +num_classes: 1000 +pretrained: False +ckpt_path: '' +keep_checkpoint_max: 10 +ckpt_save_dir: './mnasnet0.75_ckpt' +epoch_size: 350 +dataset_sink_mode: True +amp_level: 'O0' + +# loss config +loss: 'CE' +label_smoothing: 0.1 + +# lr scheduler config +scheduler: 'cosine_decay' +min_lr: 1e-10 +lr: 0.016 +warmup_epochs: 5 +decay_epochs: 345 + +# optimizer config +opt: 'rmsprop' +filter_bias_and_bn: True +momentum: 0.9 +weight_decay: 1e-5 +loss_scale: 256 +use_nesterov: False +eps: 1e-3 diff --git a/configs/mnasnet/mnasnet0.75_gpu.yaml b/configs/mnasnet/mnasnet0.75_gpu.yaml new file mode 100644 index 00000000..239525a8 --- /dev/null +++ b/configs/mnasnet/mnasnet0.75_gpu.yaml @@ -0,0 +1,51 @@ +# system config +mode: 0 +distribute: True +num_parallel_workers: 8 + +# dataset config +dataset: 'imagenet' +data_dir: '' +shuffle: True +dataset_download: False +batch_size: 128 +drop_remainder: True + +# Augmentation config +image_resize: 224 +scale: [0.08, 1.0] +ratio: [0.75, 1.333] +hflip: 0.5 +interpolation: 'bilinear' +crop_pct: 0.875 + +# model config +model: 'mnasnet0_75' +num_classes: 1000 +pretrained: False +ckpt_path: '' +keep_checkpoint_max: 10 +ckpt_save_dir: './mnasnet0.75_ckpt' +epoch_size: 350 +dataset_sink_mode: True +amp_level: 'O0' + +# loss config +loss: 'CE' +label_smoothing: 0.1 + +# lr scheduler config +scheduler: 'cosine_decay' +min_lr: 1e-10 +lr: 0.012 +warmup_epochs: 5 +decay_epochs: 345 + +# optimizer config +opt: 'rmsprop' +filter_bias_and_bn: True +momentum: 0.9 +weight_decay: 1e-5 +loss_scale: 256 +use_nesterov: False +eps: 1e-3 diff --git a/configs/mnasnet/mnasnet1.0_ascend.yaml b/configs/mnasnet/mnasnet1.0_ascend.yaml new file mode 100644 index 00000000..e1d39eab --- /dev/null +++ b/configs/mnasnet/mnasnet1.0_ascend.yaml @@ -0,0 +1,51 @@ +# system config +mode: 0 +distribute: True +num_parallel_workers: 8 + +# dataset config +dataset: 'imagenet' +data_dir: '' +shuffle: True +dataset_download: False +batch_size: 256 +drop_remainder: True + +# Augmentation config +image_resize: 224 +scale: [0.08, 1.0] +ratio: [0.75, 1.333] +hflip: 0.5 +interpolation: 'bilinear' +crop_pct: 0.875 + +# model config +model: 'mnasnet1_0' +num_classes: 1000 +pretrained: False +ckpt_path: '' +keep_checkpoint_max: 10 +ckpt_save_dir: './mnasnet1.0_ckpt' +epoch_size: 450 +dataset_sink_mode: True +amp_level: 'O0' + +# loss config +loss: 'CE' +label_smoothing: 0.1 + +# lr scheduler config +scheduler: 'cosine_decay' +min_lr: 1e-10 +lr: 0.016 +warmup_epochs: 5 +decay_epochs: 445 + +# optimizer config +opt: 'rmsprop' +filter_bias_and_bn: True +momentum: 0.9 +weight_decay: 1e-5 +loss_scale: 256 +use_nesterov: False +eps: 1e-3 \ No newline at end of file diff --git a/configs/mnasnet/mnasnet1.0_gpu.yaml b/configs/mnasnet/mnasnet1.0_gpu.yaml new file mode 100644 index 00000000..13b451cb --- /dev/null +++ b/configs/mnasnet/mnasnet1.0_gpu.yaml @@ -0,0 +1,51 @@ +# system config +mode: 0 +distribute: True +num_parallel_workers: 8 + +# dataset config +dataset: 'imagenet' +data_dir: '' +shuffle: True +dataset_download: False +batch_size: 128 +drop_remainder: True + +# Augmentation config +image_resize: 224 +scale: [0.08, 1.0] +ratio: [0.75, 1.333] +hflip: 0.5 +interpolation: 'bilinear' +crop_pct: 0.875 + +# model config +model: 'mnasnet1_0' +num_classes: 1000 +pretrained: False +ckpt_path: '' +keep_checkpoint_max: 10 +ckpt_save_dir: './mnasnet1.0_ckpt' +epoch_size: 450 +dataset_sink_mode: True +amp_level: 'O0' + +# loss config +loss: 'CE' +label_smoothing: 0.1 + +# lr scheduler config +scheduler: 'cosine_decay' +min_lr: 1e-10 +lr: 0.012 +warmup_epochs: 5 +decay_epochs: 445 + +# optimizer config +opt: 'rmsprop' +filter_bias_and_bn: True +momentum: 0.9 +weight_decay: 1e-5 +loss_scale: 256 +use_nesterov: False +eps: 1e-3 \ No newline at end of file diff --git a/configs/mnasnet/mnasnet1.4_ascend.yaml b/configs/mnasnet/mnasnet1.4_ascend.yaml new file mode 100644 index 00000000..e93dbb6b --- /dev/null +++ b/configs/mnasnet/mnasnet1.4_ascend.yaml @@ -0,0 +1,51 @@ +# system config +mode: 0 +distribute: True +num_parallel_workers: 8 + +# dataset config +dataset: 'imagenet' +data_dir: '' +shuffle: True +dataset_download: False +batch_size: 256 +drop_remainder: True + +# Augmentation config +image_resize: 224 +scale: [0.08, 1.0] +ratio: [0.75, 1.333] +hflip: 0.5 +interpolation: 'bilinear' +crop_pct: 0.875 + +# model config +model: 'mnasnet1_4' +num_classes: 1000 +pretrained: False +ckpt_path: '' +keep_checkpoint_max: 10 +ckpt_save_dir: './mnasnet1.4_ckpt' +epoch_size: 400 +dataset_sink_mode: True +amp_level: 'O0' + +# loss config +loss: 'CE' +label_smoothing: 0.1 + +# lr scheduler config +scheduler: 'cosine_decay' +min_lr: 1e-10 +lr: 0.016 +warmup_epochs: 5 +decay_epochs: 395 + +# optimizer config +opt: 'rmsprop' +filter_bias_and_bn: True +momentum: 0.9 +weight_decay: 1e-5 +loss_scale: 256 +use_nesterov: False +eps: 1e-3 \ No newline at end of file diff --git a/configs/mnasnet/mnasnet1.4_gpu.yaml b/configs/mnasnet/mnasnet1.4_gpu.yaml new file mode 100644 index 00000000..f6745211 --- /dev/null +++ b/configs/mnasnet/mnasnet1.4_gpu.yaml @@ -0,0 +1,51 @@ +# system config +mode: 0 +distribute: True +num_parallel_workers: 8 + +# dataset config +dataset: 'imagenet' +data_dir: '' +shuffle: True +dataset_download: False +batch_size: 64 +drop_remainder: True + +# Augmentation config +image_resize: 224 +scale: [0.08, 1.0] +ratio: [0.75, 1.333] +hflip: 0.5 +interpolation: 'bilinear' +crop_pct: 0.875 + +# model config +model: 'mnasnet1_4' +num_classes: 1000 +pretrained: False +ckpt_path: '' +keep_checkpoint_max: 10 +ckpt_save_dir: './mnasnet1.4_ckpt' +epoch_size: 400 +dataset_sink_mode: True +amp_level: 'O0' + +# loss config +loss: 'CE' +label_smoothing: 0.1 + +# lr scheduler config +scheduler: 'cosine_decay' +min_lr: 1e-10 +lr: 0.008 +warmup_epochs: 5 +decay_epochs: 395 + +# optimizer config +opt: 'rmsprop' +filter_bias_and_bn: True +momentum: 0.9 +weight_decay: 1e-5 +loss_scale: 256 +use_nesterov: False +eps: 1e-3 \ No newline at end of file diff --git a/mindcv/models/mnasnet.py b/mindcv/models/mnasnet.py index 545e6759..896e4519 100644 --- a/mindcv/models/mnasnet.py +++ b/mindcv/models/mnasnet.py @@ -18,6 +18,7 @@ 'mnasnet0_75', 'mnasnet1_0', 'mnasnet1_3', + 'mnasnet1_4', ] @@ -35,7 +36,7 @@ def _cfg(url='', **kwargs): 'mnasnet0.75': _cfg(url=''), 'mnasnet1.0': _cfg(url=''), 'mnasnet1.3': _cfg(url=''), - + 'mnasnet1.4': _cfg(url=''), } @@ -57,16 +58,16 @@ def __init__(self, self.layers = nn.SequentialCell([ # pw nn.Conv2d(in_channels, hidden_dim, kernel_size=1, stride=1), - nn.BatchNorm2d(hidden_dim, momentum=0.9997), + nn.BatchNorm2d(hidden_dim, momentum=0.99, eps=1e-3), nn.ReLU(), # dw nn.Conv2d(hidden_dim, hidden_dim, kernel_size=kernel_size, stride=stride, pad_mode='pad', padding=kernel_size // 2, group=hidden_dim), - nn.BatchNorm2d(hidden_dim, momentum=0.9997), + nn.BatchNorm2d(hidden_dim, momentum=0.99, eps=1e-3), nn.ReLU(), # pw-linear nn.Conv2d(hidden_dim, out_channels, kernel_size=1, stride=1), - nn.BatchNorm2d(out_channels, momentum=0.9997) + nn.BatchNorm2d(out_channels, momentum=0.99, eps=1e-3) ]) def construct(self, x: Tensor) -> Tensor: @@ -109,14 +110,14 @@ def __init__(self, features: List[nn.Cell] = [ nn.Conv2d(in_channels, mid_channels, kernel_size=3, stride=2, pad_mode='pad', padding=1), - nn.BatchNorm2d(mid_channels, momentum=0.9997), + nn.BatchNorm2d(mid_channels, momentum=0.99, eps=1e-3), nn.ReLU(), nn.Conv2d(mid_channels, mid_channels, kernel_size=3, stride=1, pad_mode='pad', padding=1, group=mid_channels), - nn.BatchNorm2d(mid_channels, momentum=0.9997), + nn.BatchNorm2d(mid_channels, momentum=0.99, eps=1e-3), nn.ReLU(), nn.Conv2d(mid_channels, input_channels, kernel_size=1, stride=1), - nn.BatchNorm2d(input_channels, momentum=0.9997) + nn.BatchNorm2d(input_channels, momentum=0.99, eps=1e-3) ] for t, c, n, s, k in inverted_residual_setting: @@ -129,7 +130,7 @@ def __init__(self, features.extend([ nn.Conv2d(input_channels, 1280, kernel_size=1, stride=1), - nn.BatchNorm2d(1280, momentum=0.9997), + nn.BatchNorm2d(1280, momentum=0.99, eps=1e-3), nn.ReLU() ]) self.features = nn.SequentialCell(features) @@ -224,3 +225,16 @@ def mnasnet1_3(pretrained: bool = False, num_classes: int = 1000, in_channels=3, load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) return model + + +@register_model +def mnasnet1_4(pretrained: bool = False, num_classes: int = 1000, in_channels=3, **kwargs) -> Mnasnet: + """Get MnasNet model with width scaled by 1.4. + Refer to the base class `models.Mnasnet` for more details.""" + default_cfg = default_cfgs['mnasnet1.4'] + model = Mnasnet(alpha=1.4, in_channels=in_channels, num_classes=num_classes, **kwargs) + + if pretrained: + load_pretrained(model, default_cfg, num_classes=num_classes, in_channels=in_channels) + + return model \ No newline at end of file diff --git a/mindcv/optim/optim_factory.py b/mindcv/optim/optim_factory.py index 7df3d825..0f70fe97 100644 --- a/mindcv/optim/optim_factory.py +++ b/mindcv/optim/optim_factory.py @@ -37,6 +37,7 @@ def create_optimizer( loss_scale: float = 1.0, schedule_decay: float = 4e-3, checkpoint_path: str = '', + eps: float = 1e-10, **kwargs): r"""Creates optimizer by name. @@ -118,6 +119,7 @@ def create_optimizer( momentum=momentum, weight_decay=weight_decay, loss_scale=loss_scale, + epsilon=eps, **opt_args ) elif opt == 'adagrad': diff --git a/train.py b/train.py index f045778d..5ff10964 100644 --- a/train.py +++ b/train.py @@ -181,7 +181,8 @@ def train(args): nesterov=args.use_nesterov, filter_bias_and_bn=args.filter_bias_and_bn, loss_scale=args.loss_scale, - checkpoint_path=opt_ckpt_path) + checkpoint_path=opt_ckpt_path, + eps=args.eps) # Define eval metrics. if num_classes >= 5: From c50cea3bf6a009bd61f6cc94686bc344b5e27970 Mon Sep 17 00:00:00 2001 From: Samit <285365963@qq.com> Date: Mon, 5 Dec 2022 10:52:02 +0800 Subject: [PATCH 17/32] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 902dd1f7..c11a81fa 100644 --- a/README.md +++ b/README.md @@ -291,6 +291,9 @@ Please see [configs](./configs) for the details about model performance and pret ## Notes ### What is New +- 2022/12/05 +1. Support lr warmup for all lr scheduling algorithms besides cosine decay. + - 2022/11/21 1. Add visualization for loss and acc curves 2. Support epochwise lr warmup cosine decay (previous is stepwise) From 3b0ce8b7cb84ba30e85bee14957cf6e6735b41c4 Mon Sep 17 00:00:00 2001 From: Samit <285365963@qq.com> Date: Mon, 5 Dec 2022 11:37:29 +0800 Subject: [PATCH 18/32] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index c11a81fa..6e19fe95 100644 --- a/README.md +++ b/README.md @@ -287,6 +287,9 @@ Please see [configs](./configs) for the details about model performance and pret * Label Smoothing * Stochastic Depth (depends on networks) * Dropout (depends on networks) +* Loss + * Cross Entropy (w/ class weight and auxilary logit support) + * Binary Cross Entropy (w/ class weight and auxilary logit support) ## Notes From 98306adbd73d98a41cf720f52ee9979500aaf433 Mon Sep 17 00:00:00 2001 From: CAOANJIA <1134692141@qq.com> Date: Mon, 5 Dec 2022 12:18:30 +0800 Subject: [PATCH 19/32] update mnasnet README --- configs/mnasnet/README.md | 16 ++++++++-------- configs/mnasnet/README_CN.md | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/configs/mnasnet/README.md b/configs/mnasnet/README.md index 063f37a9..8e0224bd 100644 --- a/configs/mnasnet/README.md +++ b/configs/mnasnet/README.md @@ -41,12 +41,6 @@ Please download the [ImageNet-1K](https://www.image-net.org/download.php) datase ``` Note that the number of GPUs/Ascends and batch size will influence the training results. To reproduce the training result at most, it is recommended to use the **same number of GPUs/Ascends** with the same batch size. - -- **Finetuning.** Here is an example for finetuning a pretrained mnasnet0_75 on CIFAR10 dataset using Rmsprop optimizer. - - ```shell - python train.py --model=mnasnet0_75 --pretrained --opt=rmsprop --lr=0.001 dataset=cifar10 --num_classes=10 --dataset_download - ``` Detailed adjustable parameters and their default value can be seen in [config.py](../../config.py). @@ -55,13 +49,19 @@ Detailed adjustable parameters and their default value can be seen in [config.py - To validate the trained model, you can use `validate.py`. Here is an example for mnasnet0_75 to verify the accuracy of pretrained weights. ```shell - python validate.py --model=mnasnet0_75 --dataset=imagenet --val_split=val --pretrained + python validate.py + -c configs/mnasnet/mnasnet0.75_ascend.yaml + --data_dir=/path/to/imagenet + --ckpt_path=/path/to/ckpt ``` - To validate the model, you can use `validate.py`. Here is an example for mnasnet0_75 to verify the accuracy of your training. ```shell - python validate.py --model=mnasnet0_75 --dataset=imagenet --val_split=val --ckpt_path='./ckpt/mnasnet0_75-best.ckpt' + python validate.py + -c configs/mnasnet/mnasnet0.75_ascend.yaml + --data_dir=/path/to/imagenet + --ckpt_path=/path/to/ckpt ``` ### Deployment (optional) diff --git a/configs/mnasnet/README_CN.md b/configs/mnasnet/README_CN.md index 823f1ff9..b7a26b82 100644 --- a/configs/mnasnet/README_CN.md +++ b/configs/mnasnet/README_CN.md @@ -1,4 +1,4 @@ -# DenseNet +# MnasNet *** > [MnasNet: Platform-Aware Neural Architecture Search for Mobile](https://arxiv.org/abs/1807.11626) @@ -38,12 +38,6 @@ mpirun -n 8 python train.py -c configs/mnasnet/mnasnet0.75_gpu.yaml --data_dir /path/to/imagenet ``` -- 下面是使用在ImageNet上预训练的mnasnet0_75模型和Rmsprop优化器在CIFAR10数据集上进行微调的示例。 - - ```shell - python train.py --model=mnasnet0_75 --pretrained --opt=rmsprop --lr=0.001 dataset=cifar10 --num_classes=10 --dataset_download - ``` - 详细的可调参数及其默认值可以在[config.py](../../config.py)中查看。 ### 验证 @@ -51,11 +45,17 @@ - 下面是使用`validate.py`文件验证mnasnet0_75的预训练模型的精度的示例。 ```shell - python validate.py --model=mnasnet0_75 --dataset=imagenet --val_split=val --pretrained + python validate.py + -c configs/mnasnet/mnasnet0.75_ascend.yaml + --data_dir=/path/to/imagenet + --ckpt_path=/path/to/ckpt ``` - 下面是使用`validate.py`文件验证mnasnet0_75的自定义参数文件的精度的示例。 ```shell - python validate.py --model=mnasnet0_75 --dataset=imagenet --val_split=val --ckpt_path='./ckpt/mnasnet0_75-best.ckpt' + python validate.py + -c configs/mnasnet/mnasnet0.75_ascend.yaml + --data_dir=/path/to/imagenet + --ckpt_path=/path/to/ckpt ``` From 7514663acf5df992374eb5d7162889c76d273da0 Mon Sep 17 00:00:00 2001 From: zp5070 Date: Mon, 5 Dec 2022 16:52:30 +0800 Subject: [PATCH 20/32] update densenet configs --- configs/densenet/README.md | 20 +++----------------- configs/densenet/README_CN.md | 17 ++--------------- 2 files changed, 5 insertions(+), 32 deletions(-) diff --git a/configs/densenet/README.md b/configs/densenet/README.md index 50580da9..eb0c8cc7 100644 --- a/configs/densenet/README.md +++ b/configs/densenet/README.md @@ -46,34 +46,20 @@ Please download the [ImageNet-1K](https://www.image-net.org/download.php) datase ```shell # train densenet121 on 8 GPUs - export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 - mpirun -n 8 python train.py -c configs/densenet/densenet_121_gpu.yaml --data_dir /path/to/imagenet + mpirun -n 8 python train.py --config configs/densenet/densenet_121_gpu.yaml --data_dir /path/to/imagenet ``` - - Note that the number of GPUs/Ascends and batch size will influence the training results. To reproduce the training result at most, it is recommended to use the **same number of GPUs/Ascneds** with the same batch size. -- **Finetuning.** Here is an example for finetuning a pretrained densenet121 on CIFAR10 dataset using Momentum optimizer. - - ```shell - python train.py --model=densenet121 --pretrained --opt=momentum --lr=0.001 dataset=cifar10 --num_classes=10 --dataset_download - ``` + Note that the number of GPUs/Ascends and batch size will influence the training results. To reproduce the training result at most, it is recommended to use the **same number of GPUs/Ascends** with the same batch size. Detailed adjustable parameters and their default value can be seen in [config.py](../../config.py). ### Validation -- To validate the trained model, you can use `validate.py`. Here is an example for densenet121 to verify the accuracy of - pretrained weights. - - ```shell - python validate.py --model=densenet121 --dataset=imagenet --val_split=val --pretrained - ``` - - To validate the model, you can use `validate.py`. Here is an example for densenet121 to verify the accuracy of your training. ```shell - python validate.py --model=densenet121 --dataset=imagenet --val_split=val --ckpt_path='./ckpt/densenet121-best.ckpt' + python validate.py --config configs/densenet/densenet_121_gpu.yaml --data_dir /path/to/imagenet --ckpt_path /path/to/densenet121.ckpt ``` ### Deployment (optional) diff --git a/configs/densenet/README_CN.md b/configs/densenet/README_CN.md index 40ca594a..c9b8f16e 100644 --- a/configs/densenet/README_CN.md +++ b/configs/densenet/README_CN.md @@ -41,28 +41,15 @@ > [configs文件夹](../../configs)中列出了mindcv套件所包含的模型的各个规格的yaml配置文件(在ImageNet数据集上训练和验证的配置)。 ```shell - export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 - mpirun -n 8 python train.py -c configs/densenet/densenet_121_gpu.yaml --data_dir /path/to/imagenet - ``` - -- 下面是使用在ImageNet上预训练的densenet121模型和Momentum优化器在CIFAR10数据集上进行微调的示例。 - - ```shell - python train.py --model=densenet121 --pretrained --opt=momentum --lr=0.001 dataset=cifar10 --num_classes=10 --dataset_download + mpirun -n 8 python train.py --config configs/densenet/densenet_121_gpu.yaml --data_dir /path/to/imagenet ``` 详细的可调参数及其默认值可以在[config.py](../../config.py)中查看。 ### 验证 -- 下面是使用`validate.py`文件验证densenet121的预训练模型的精度的示例。 - - ```shell - python validate.py --model=densenet121 --dataset=imagenet --val_split=val --pretrained - ``` - - 下面是使用`validate.py`文件验证densenet121的自定义参数文件的精度的示例。 ```shell - python validate.py --model=densenet121 --dataset=imagenet --val_split=val --ckpt_path='./ckpt/densenet121-best.ckpt' + python validate.py --config configs/densenet/densenet_121_gpu.yaml --data_dir /path/to/imagenet --ckpt_path /path/to/densenet121.ckpt ``` From ce9ff29363ae43965c2d6c278ce06e549616cef9 Mon Sep 17 00:00:00 2001 From: zp5070 Date: Mon, 5 Dec 2022 17:06:18 +0800 Subject: [PATCH 21/32] update res2net readme --- configs/res2net/README.md | 17 ++--------------- configs/res2net/README_CN.md | 16 ++-------------- configs/res2net/res2net_101_gpu.yaml | 10 +++++----- configs/res2net/res2net_50_gpu.yaml | 7 ++++--- 4 files changed, 13 insertions(+), 37 deletions(-) diff --git a/configs/res2net/README.md b/configs/res2net/README.md index 510969a0..66487fb7 100644 --- a/configs/res2net/README.md +++ b/configs/res2net/README.md @@ -40,29 +40,16 @@ and demonstrate consistent performance gains over baseline models. the `configs` folder. To trigger training using preset yaml config. ```shell - mpirun --allow-run-as-root -n 8 python train.py -c configs/res2net/res2net_50_gpu.yaml --data_dir /path/to/imagenet - ``` - -- Here is the example for finetuning a pretrained InceptionV3 on CIFAR10 dataset using Adam optimizer. - - ```shell - python train.py --model=res2net50 --pretrained --opt=momentum --lr=0.001 dataset=cifar10 --num_classes=10 --dataset_download + mpirun -n 8 python train.py --config configs/res2net/res2net_50_gpu.yaml --data_dir /path/to/imagenet ``` Detailed adjustable parameters and their default value can be seen in [config.py](../../config.py). ### Eval -- To validate the model, you can use `validate.py`. Here is an example for res2net50 to verify the accuracy of - pretrained weights. - - ```shell - python validate.py --model=res2net50 --dataset=imagenet --val_split=val --pretrained - ``` - - To validate the model, you can use `validate.py`. Here is an example for res2net50 to verify the accuracy of your training. ```shell - python validate.py --model=res2net50 --dataset=imagenet --val_split=val --ckpt_path='./ckpt/res2net50-best.ckpt' + python validate.py --config configs/res2net/res2net_50_gpu.yaml --data_dir /path/to/imagenet --ckpt_path /path/to/res2net50.ckpt ``` diff --git a/configs/res2net/README_CN.md b/configs/res2net/README_CN.md index b867f4a4..9d768cee 100644 --- a/configs/res2net/README_CN.md +++ b/configs/res2net/README_CN.md @@ -39,27 +39,15 @@ > [configs文件夹](../../configs)中列出了mindcv套件所包含的模型的各个规格的yaml配置文件(在ImageNet数据集上训练和验证的配置)。 ```shell - mpirun --allow-run-as-root -n 8 python train.py -c configs/res2net/res2net_50_gpu.yaml --data_dir /path/to/imagenet - ``` - -- 下面是使用在ImageNet上预训练的Res2Net50模型和Momentum优化器在CIFAR10数据集上进行微调的示例。 - - ```shell - python train.py --model=res2net50 --pretrained --opt=momentum --lr=0.001 dataset=cifar10 --num_classes=10 --dataset_download + mpirun -n 8 python train.py --config configs/res2net/res2net_50_gpu.yaml --data_dir /path/to/imagenet ``` 详细的可调参数及其默认值可以在[config.py](../../config.py)中查看。 ### 验证 -- 下面是使用`validate.py`文件验证Res2Net50的预训练模型的精度的示例。 - - ```shell - python validate.py --model=res2net50 --dataset=imagenet --val_split=val --pretrained - ``` - - 下面是使用`validate.py`文件验证Res2Net50的自定义参数文件的精度的示例。 ```shell - python validate.py --model=res2net50 --dataset=imagenet --val_split=val --ckpt_path='./ckpt/res2net50-best.ckpt' + python validate.py --config configs/res2net/res2net_50_gpu.yaml --data_dir /path/to/imagenet --ckpt_path /path/to/res2net50.ckpt ``` diff --git a/configs/res2net/res2net_101_gpu.yaml b/configs/res2net/res2net_101_gpu.yaml index 360c52e7..0824a441 100644 --- a/configs/res2net/res2net_101_gpu.yaml +++ b/configs/res2net/res2net_101_gpu.yaml @@ -37,6 +37,7 @@ interpolation: 'bilinear' crop_pct: 0.875 color_jitter: 0.4 re_prob: 0.5 +mixup: 0.2 # model config model: 'res2net101' @@ -46,7 +47,7 @@ ckpt_path: '' keep_checkpoint_max: 10 val_interval: 5 ckpt_save_dir: './ckpt' -epoch_size: 140 +epoch_size: 300 dataset_sink_mode: True amp_level: 'O2' @@ -55,12 +56,11 @@ loss: 'CE' label_smoothing: 0.1 # lr scheduler config -scheduler: 'step_decay' +scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 -warmup_epochs: 0 -decay_rate: 0.1 -decay_epochs: 30 +warmup_epochs: 5 +decay_epochs: 295 # optimizer config opt: 'momentum' diff --git a/configs/res2net/res2net_50_gpu.yaml b/configs/res2net/res2net_50_gpu.yaml index f0d42ecc..b9c25f25 100644 --- a/configs/res2net/res2net_50_gpu.yaml +++ b/configs/res2net/res2net_50_gpu.yaml @@ -37,6 +37,7 @@ interpolation: 'bilinear' crop_pct: 0.875 color_jitter: 0.4 re_prob: 0.5 +mixup: 0.2 # model config model: 'res2net50' @@ -46,7 +47,7 @@ ckpt_path: '' keep_checkpoint_max: 10 val_interval: 5 ckpt_save_dir: './ckpt' -epoch_size: 200 +epoch_size: 300 dataset_sink_mode: True amp_level: 'O2' @@ -58,8 +59,8 @@ label_smoothing: 0.1 scheduler: 'cosine_decay' min_lr: 0.0 lr: 0.1 -warmup_epochs: 4 -decay_epochs: 196 +warmup_epochs: 5 +decay_epochs: 295 # optimizer config opt: 'momentum' From c2c78056bbb385424a526094f6f0dabfba0a2451 Mon Sep 17 00:00:00 2001 From: samithuang <285365963@qq.com> Date: Mon, 5 Dec 2022 10:32:34 +0000 Subject: [PATCH 22/32] support subset distributed sampling --- mindcv/data/dataset_factory.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/mindcv/data/dataset_factory.py b/mindcv/data/dataset_factory.py index fae54292..05d67139 100644 --- a/mindcv/data/dataset_factory.py +++ b/mindcv/data/dataset_factory.py @@ -5,10 +5,11 @@ from typing import Optional import os -from mindspore.dataset import MnistDataset, Cifar10Dataset, Cifar100Dataset, ImageFolderDataset +from mindspore.dataset import MnistDataset, Cifar10Dataset, Cifar100Dataset, ImageFolderDataset, DistributedSampler import mindspore.dataset as ds from .dataset_download import MnistDownload, Cifar10Download, Cifar100Download +from .distributed_sampler import RepAugDistributedSampler __all__ = ["create_dataset"] @@ -72,21 +73,18 @@ def create_dataset( Returns: Dataset object """ + name = name.lower() - if num_samples is None: - sampler = None - elif num_samples > 0: - if shuffle: - sampler = ds.RandomSampler(replacement=False, num_samples=num_samples) - else: - sampler = ds.SequentialSampler(num_samples=num_samples) + if num_samples is not None and num_samples > 0: + sampler = DistributedSampler(num_shards, shard_id, shuffle=shuffle, num_samples=num_samples) shuffle = None # shuffle and sampler cannot be set at the same in mindspore datatset API + mindspore_kwargs = dict(shuffle=shuffle, sampler=sampler, + num_parallel_workers=num_parallel_workers, **kwargs) else: sampler = None - - name = name.lower() - mindspore_kwargs = dict(shuffle=shuffle, sampler=sampler, num_shards=num_shards, shard_id=shard_id, + mindspore_kwargs = dict(shuffle=shuffle, sampler=sampler, num_shards=num_shards, shard_id=shard_id, num_parallel_workers=num_parallel_workers, **kwargs) + if name in _MINDSPORE_BASIC_DATASET: dataset_class = _MINDSPORE_BASIC_DATASET[name][0] dataset_download = _MINDSPORE_BASIC_DATASET[name][1] From 653a08096cb3f5151e92b449791752ce0ad2ebff Mon Sep 17 00:00:00 2001 From: samithuang <285365963@qq.com> Date: Mon, 5 Dec 2022 10:33:29 +0000 Subject: [PATCH 23/32] fix volo --- mindcv/models/volo.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/mindcv/models/volo.py b/mindcv/models/volo.py index 40bb0d25..ba77e0e2 100644 --- a/mindcv/models/volo.py +++ b/mindcv/models/volo.py @@ -196,7 +196,6 @@ class OutlookAttention(nn.Cell): --kernel_size: kernel size in each window for outlook attention return: token features after outlook attention """ - def __init__(self, dim, num_heads, kernel_size=3, padding=1, stride=1, qkv_bias=False, qk_scale=None, attn_drop=0., proj_drop=0.): super().__init__() @@ -222,6 +221,7 @@ def __init__(self, dim, num_heads, kernel_size=3, padding=1, stride=1, self.transpose = ops.Transpose() self.batch_mat_mul = ops.BatchMatMul() + def construct(self, x): B, H, W, C = x.shape @@ -287,6 +287,11 @@ def __init__(self, dim, kernel_size, padding, stride=1, qk_scale=None): super().__init__() self.norm1 = norm_layer([dim]) + print(dim, num_heads, kernel_size, padding,stride, + qkv_bias, qk_scale, + attn_drop) + + self.attn = OutlookAttention(dim, num_heads, kernel_size=kernel_size, padding=padding, stride=stride, qkv_bias=qkv_bias, qk_scale=qk_scale, @@ -304,7 +309,8 @@ def __init__(self, dim, kernel_size, padding, stride=1, def construct(self, x): # print("\nxx",x.shape) - # print("\nnorm", self.norm1(x).shape) + print("\nnorm", self.norm1(x).shape) + #print("\nnorm output: ", self.norm1(x)) # print("\nattn", self.attn(self.norm1(x)).shape) x = x + self.drop_path(self.attn(self.norm1(x))) x = x + self.drop_path(self.mlp(self.norm2(x))) @@ -945,3 +951,14 @@ def volo_d5(pretrained=False, **kwargs): **kwargs) model.default_cfg = default_cfgs['volo_large'] return model + +if __name__ == '__main__': + dummy_input = ms.Tensor(np.random.rand(2, 28, 28, 192), dtype=ms.float32) + attn = OutlookAttention(192, 6, kernel_size=3, + padding=1, stride=2, + qkv_bias=False, qk_scale=None, + attn_drop=0.0) + output = attn(dummy_input) + print(output.shape) + + From cf6a23b23e33682a170358768cde32a523c09254 Mon Sep 17 00:00:00 2001 From: Samit <285365963@qq.com> Date: Tue, 6 Dec 2022 10:40:01 +0800 Subject: [PATCH 24/32] Update dataset_factory.py --- mindcv/data/dataset_factory.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mindcv/data/dataset_factory.py b/mindcv/data/dataset_factory.py index 05d67139..9d904806 100644 --- a/mindcv/data/dataset_factory.py +++ b/mindcv/data/dataset_factory.py @@ -9,7 +9,6 @@ import mindspore.dataset as ds from .dataset_download import MnistDownload, Cifar10Download, Cifar100Download -from .distributed_sampler import RepAugDistributedSampler __all__ = ["create_dataset"] From 79444f0b3ef5066477b276be5be1ad9b3adf476f Mon Sep 17 00:00:00 2001 From: Xuanmai <97332102+XuanmaiXue@users.noreply.github.com> Date: Tue, 6 Dec 2022 19:32:59 +0800 Subject: [PATCH 25/32] Create vit_b32_224_ascend.yaml --- configs/vit/vit_b32_224_ascend.yaml | 48 ++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/configs/vit/vit_b32_224_ascend.yaml b/configs/vit/vit_b32_224_ascend.yaml index c6361407..db933baf 100644 --- a/configs/vit/vit_b32_224_ascend.yaml +++ b/configs/vit/vit_b32_224_ascend.yaml @@ -18,14 +18,14 @@ mode: 0 distribute: True num_parallel_workers: 8 val_while_train: True -val_interval: 1 +val_interval: 1 # dataset config -dataset: 'imagenet' -data_dir: '/data/imagenet' +dataset: "imagenet" +data_dir: "/data0/imagenet2012" shuffle: True dataset_download: False -batch_size: 256 +batch_size: 256 drop_remainder: True # Augmentation config @@ -33,44 +33,44 @@ image_resize: 224 scale: [0.08, 1.0] ratio: [0.75, 1.333] hflip: 0.5 -interpolation: 'bicubic' -re_prob: 0.1 +interpolation: "bicubic" +re_prob: 0.1 # 1118 part2, compare to 1118 part1 mixup: 0.2 cutmix: 1.0 cutmix_prob: 1.0 crop_pct: 0.875 color_jitter: [0.4, 0.4, 0.4] -auto_augment: 'randaug-m7-mstd0.5' +auto_augment: "randaug-m7-mstd0.5" # 1118 part2 # model config -model: 'vit_b_32_224' -drop_rate: 0.1 -drop_path_rate: 0.1 +model: "vit_b_32_224" +drop_rate: 0.1 # 1119 add +drop_path_rate: 0.1 # 1119 add num_classes: 1000 pretrained: False -ckpt_path: '' +ckpt_path: "/home/mindspore/xmx/vit/vit_b_32_224_best.ckpt" keep_checkpoint_max: 10 -ckpt_save_policy: 'top_k' -ckpt_save_dir: './vit_b32_224' -epoch_size: 300 +ckpt_save_policy: "top_k" +ckpt_save_dir: "./vit_b32_224" +epoch_size: 600 dataset_sink_mode: True -amp_level: 'O2' +amp_level: "O2" # loss config -loss: 'CE' +loss: "CE" loss_scale: 1024.0 -label_smoothing: 0.1 +label_smoothing: 0.1 # lr scheduler config -scheduler: 'cosine_decay' +scheduler: "warmup_cosine_decay" lr: 0.003 # diff from 4xGPU -min_lr: 0.00001 -warmup_epochs: 38 -decay_epochs: 262 -lr_epoch_stair: False +min_lr: 1e-6 +warmup_epochs: 32 +decay_epochs: 562 +lr_epoch_stair: False # optimizer config -opt: 'adamw' -weight_decay: 0.025 +opt: "adamw" +weight_decay: 0.025 filter_bias_and_bn: True use_nesterov: False From 4e558f49a3be1621feac1d926368fb1bef647efb Mon Sep 17 00:00:00 2001 From: Xuanmai <97332102+XuanmaiXue@users.noreply.github.com> Date: Tue, 6 Dec 2022 19:34:16 +0800 Subject: [PATCH 26/32] Update vit_l32_224_ascend.yaml --- configs/vit/vit_l32_224_ascend.yaml | 40 ++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/configs/vit/vit_l32_224_ascend.yaml b/configs/vit/vit_l32_224_ascend.yaml index 425fff48..3bdaa9b8 100644 --- a/configs/vit/vit_l32_224_ascend.yaml +++ b/configs/vit/vit_l32_224_ascend.yaml @@ -18,14 +18,14 @@ mode: 0 distribute: True num_parallel_workers: 8 val_while_train: True -val_interval: 1 +val_interval: 1 # dataset config -dataset: 'imagenet' -data_dir: '/data/imagenet' +dataset: "imagenet" +data_dir: "/data/imagenet" shuffle: True dataset_download: False -batch_size: 128 # diff from paper +batch_size: 128 # diff from paper drop_remainder: True # Augmentation config @@ -33,44 +33,44 @@ image_resize: 224 scale: [0.08, 1.0] ratio: [0.75, 1.333] hflip: 0.5 -interpolation: 'bicubic' +interpolation: "bicubic" re_prob: 0.1 # 1118 part2, compare to 1118 part1 mixup: 0.2 cutmix: 1.0 cutmix_prob: 1.0 crop_pct: 0.875 color_jitter: [0.4, 0.4, 0.4] -auto_augment: 'randaug-m7-mstd0.5' # 1118 part2 +auto_augment: "randaug-m7-mstd0.5" # 1118 part2 # model config -model: 'vit_l_32_224' -drop_rate: 0.1 -drop_path_rate: 0.1 +model: "vit_l_32_224" +drop_rate: 0.1 +drop_path_rate: 0.1 num_classes: 1000 pretrained: False -ckpt_path: '' +ckpt_path: "" keep_checkpoint_max: 10 -ckpt_save_policy: 'top_k' -ckpt_save_dir: './vit_l32_224' +ckpt_save_policy: "top_k" +ckpt_save_dir: "./vit_l32_224" epoch_size: 300 dataset_sink_mode: True -amp_level: 'O2' +amp_level: "O2" # loss config -loss: 'CE' +loss: "CE" loss_scale: 1024.0 label_smoothing: 0.1 # Exp 1119, compared to 1118 part2 # lr scheduler config -scheduler: 'cosine_decay' -lr: 0.003 # +scheduler: "warmup_cosine_decay" +lr: 0.0015 # min_lr: 1e-6 warmup_epochs: 32 -decay_epochs: 418 -lr_epoch_stair: False +decay_epochs: 268 +lr_epoch_stair: False # optimizer config -opt: 'adamw' -weight_decay: 0.025 +opt: "adamw" +weight_decay: 0.025 filter_bias_and_bn: True use_nesterov: False From 6b80b6a05795bfafc6fc83d18f24145065af8821 Mon Sep 17 00:00:00 2001 From: Xuanmai <97332102+XuanmaiXue@users.noreply.github.com> Date: Tue, 6 Dec 2022 19:34:43 +0800 Subject: [PATCH 27/32] Update vit_b32_224_ascend.yaml --- configs/vit/vit_b32_224_ascend.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configs/vit/vit_b32_224_ascend.yaml b/configs/vit/vit_b32_224_ascend.yaml index db933baf..d7d1dfc5 100644 --- a/configs/vit/vit_b32_224_ascend.yaml +++ b/configs/vit/vit_b32_224_ascend.yaml @@ -22,7 +22,7 @@ val_interval: 1 # dataset config dataset: "imagenet" -data_dir: "/data0/imagenet2012" +data_dir: "/data/imagenet" shuffle: True dataset_download: False batch_size: 256 @@ -48,7 +48,7 @@ drop_rate: 0.1 # 1119 add drop_path_rate: 0.1 # 1119 add num_classes: 1000 pretrained: False -ckpt_path: "/home/mindspore/xmx/vit/vit_b_32_224_best.ckpt" +ckpt_path: "" keep_checkpoint_max: 10 ckpt_save_policy: "top_k" ckpt_save_dir: "./vit_b32_224" @@ -66,7 +66,7 @@ scheduler: "warmup_cosine_decay" lr: 0.003 # diff from 4xGPU min_lr: 1e-6 warmup_epochs: 32 -decay_epochs: 562 +decay_epochs: 568 lr_epoch_stair: False # optimizer config From fc2e32f1537a9e5ce5972ebfe5d9ab67105609c3 Mon Sep 17 00:00:00 2001 From: Xuanmai <97332102+XuanmaiXue@users.noreply.github.com> Date: Tue, 6 Dec 2022 19:35:14 +0800 Subject: [PATCH 28/32] Add files via upload --- configs/vit/vit_l16_224_ascend.yaml | 76 +++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 configs/vit/vit_l16_224_ascend.yaml diff --git a/configs/vit/vit_l16_224_ascend.yaml b/configs/vit/vit_l16_224_ascend.yaml new file mode 100644 index 00000000..5f58d538 --- /dev/null +++ b/configs/vit/vit_l16_224_ascend.yaml @@ -0,0 +1,76 @@ +# Copyright 2022 Huawei Technologies Co., Ltd +# +# 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. +# ============================================================================ + +# system config +mode: 0 +distribute: True +num_parallel_workers: 8 +val_while_train: True +val_interval: 1 + +# dataset config +dataset: "imagenet" +data_dir: "/data0/imagenet2012" +shuffle: True +dataset_download: False +batch_size: 48 # diff from paper +drop_remainder: True + +# Augmentation config +image_resize: 224 +scale: [0.08, 1.0] +ratio: [0.75, 1.333] +hflip: 0.5 +interpolation: "bicubic" +re_prob: 0.15 +mixup: 0.2 +cutmix: 1.0 +cutmix_prob: 1.0 +crop_pct: 0.875 +color_jitter: [0.4, 0.4, 0.4] +auto_augment: "randaug-m9-mstd0.5" + +# model config +model: "vit_l_16_224" +drop_rate: 0.12 +drop_path_rate: 0.1 +num_classes: 1000 +pretrained: False +ckpt_path: "/home/mindspore/xmx/vit/vit_l_16_224_best.ckpt" +keep_checkpoint_max: 10 +ckpt_save_policy: "top_k" +ckpt_save_dir: "" +epoch_size: 300 +dataset_sink_mode: True +amp_level: "O2" + +# loss config +loss: "CE" +loss_scale: 1024.0 +label_smoothing: 0.1 + +# lr scheduler config +scheduler: "warmup_cosine_decay" +lr: 0.0005 # +min_lr: 1e-5 +warmup_epochs: 32 +decay_epochs: 268 +lr_epoch_stair: False + +# optimizer config +opt: "adamw" +weight_decay: 0.05 +filter_bias_and_bn: True +use_nesterov: False From b311c40214eb1737b8b6e805f9aaa412fa20ef1e Mon Sep 17 00:00:00 2001 From: Xuanmai <97332102+XuanmaiXue@users.noreply.github.com> Date: Tue, 6 Dec 2022 19:35:59 +0800 Subject: [PATCH 29/32] Update vit_l16_224_ascend.yaml --- configs/vit/vit_l16_224_ascend.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/vit/vit_l16_224_ascend.yaml b/configs/vit/vit_l16_224_ascend.yaml index 5f58d538..85701ed6 100644 --- a/configs/vit/vit_l16_224_ascend.yaml +++ b/configs/vit/vit_l16_224_ascend.yaml @@ -22,7 +22,7 @@ val_interval: 1 # dataset config dataset: "imagenet" -data_dir: "/data0/imagenet2012" +data_dir: "/data/imagenet" shuffle: True dataset_download: False batch_size: 48 # diff from paper @@ -48,7 +48,7 @@ drop_rate: 0.12 drop_path_rate: 0.1 num_classes: 1000 pretrained: False -ckpt_path: "/home/mindspore/xmx/vit/vit_l_16_224_best.ckpt" +ckpt_path: "" keep_checkpoint_max: 10 ckpt_save_policy: "top_k" ckpt_save_dir: "" From 455d138693e1c2792210ceb1124541c31ef1d694 Mon Sep 17 00:00:00 2001 From: samithuang <285365963@qq.com> Date: Tue, 6 Dec 2022 16:38:37 +0000 Subject: [PATCH 30/32] add repeated augmentation --- config.py | 4 +- mindcv/data/dataset_factory.py | 44 +- mindcv/data/distributed_sampler.py | 98 +++ mindcv/models/__init__.py | 2 +- mindcv/models/volo.py | 964 ----------------------------- tests/modules/test_transforms.py | 73 +++ train.py | 3 +- 7 files changed, 217 insertions(+), 971 deletions(-) create mode 100644 mindcv/data/distributed_sampler.py delete mode 100644 mindcv/models/volo.py diff --git a/config.py b/config.py index 3f423a4d..d4f92eaf 100644 --- a/config.py +++ b/config.py @@ -63,6 +63,8 @@ def create_parser(): group.add_argument('--drop_remainder', type=str2bool, nargs='?', const=True, default=True, help='Determines whether or not to drop the last block whose data ' 'row number is less than batch size (default=True)') + group.add_argument('--aug_repeats', type=int, default=0, + help='Number of dataset repeatition for repeated augmentation. If 0 or 1, repeated augmentation is diabled. Otherwise, repeated augmentation is enabled and the common choice is 3. (Default: 0)') # Augmentation parameters group = parser.add_argument_group('Augmentation parameters') @@ -173,7 +175,7 @@ def create_parser(): group.add_argument('--multi_step_decay_milestones', type=list, default=[30, 60, 90], help='list of epoch milestones for lr decay, which is ONLY effective for the multi_step_decay scheduler. LR will be decay by decay_rate at the milestone epoch.') group.add_argument('--lr_epoch_stair', type=str2bool, nargs='?', const=True, default=False, - help='If True, LR will be updated in the begin of each new epoch and the LR will be consisent for each batch in one epoch. Otherwise, learning rate will be updated dynamically in each step. (default=False)') + help='If True, LR will be updated in the first step of an epoch and LRs are the same in the remaining steps in the epoch. Otherwise, learning rate is updated every step dynamically. (default=False)') # Loss parameters group = parser.add_argument_group('Loss parameters') diff --git a/mindcv/data/dataset_factory.py b/mindcv/data/dataset_factory.py index 05d67139..749fad3c 100644 --- a/mindcv/data/dataset_factory.py +++ b/mindcv/data/dataset_factory.py @@ -6,10 +6,12 @@ import os from mindspore.dataset import MnistDataset, Cifar10Dataset, Cifar100Dataset, ImageFolderDataset, DistributedSampler -import mindspore.dataset as ds from .dataset_download import MnistDownload, Cifar10Download, Cifar100Download -from .distributed_sampler import RepAugDistributedSampler +from .distributed_sampler import RepeatAugSampler + +#import mindspore.dataset as ds +#from .dataset_reader import ImageNetDataset __all__ = ["create_dataset"] @@ -19,6 +21,7 @@ cifar100=(Cifar100Dataset, Cifar100Download) ) +#_DATASET_SIZE = {'imagenet': } def create_dataset( name: str = '', @@ -30,6 +33,7 @@ def create_dataset( shard_id: Optional[int] = None, num_parallel_workers: Optional[int] = None, download: bool = False, + num_aug_repeats: int = 0, **kwargs ): r"""Creates dataset by name. @@ -47,6 +51,7 @@ def create_dataset( This argument can only be specified when `num_shards` is also specified. num_parallel_workers: Number of workers to read the data (default=None, set in the config). download: whether to download the dataset. Default: False + num_aug_repeats: Number of dataset repeatition for repeated augmentation. If 0 or 1, repeated augmentation is diabled. Otherwise, repeated augmentation is enabled and the common choice is 3. (Default: 0) Note: For custom datasets and imagenet, the dataset dir should follow the structure like: @@ -73,10 +78,16 @@ def create_dataset( Returns: Dataset object """ - name = name.lower() + assert (num_samples is None) or (num_aug_repeats==0), 'num_samples and num_aug_repeats can NOT be set together.' + + name = name.lower() + # subset sampling if num_samples is not None and num_samples > 0: + num_shards = 1 if num_shards is None else num_shards + shard_id = 0 if shard_id is None else shard_id sampler = DistributedSampler(num_shards, shard_id, shuffle=shuffle, num_samples=num_samples) + shuffle = None # shuffle and sampler cannot be set at the same in mindspore datatset API mindspore_kwargs = dict(shuffle=shuffle, sampler=sampler, num_parallel_workers=num_parallel_workers, **kwargs) @@ -85,6 +96,15 @@ def create_dataset( mindspore_kwargs = dict(shuffle=shuffle, sampler=sampler, num_shards=num_shards, shard_id=shard_id, num_parallel_workers=num_parallel_workers, **kwargs) + # sampler for repeated augmentation + if num_aug_repeats > 0: + dataset_size = get_dataset_size(name, root, split) + print(f'INFO: Repeated augmentation is enabled, num_aug_repeats: {num_aug_repeats}, original dataset size: ', dataset_size) + #since drop_remainder is usally True, we don't need to do rounding in sampling + sampler = RepeatAugSampler(dataset_size, num_shards=num_shards, rank_id=shard_id, num_repeats=num_aug_repeats, selected_round=0, shuffle=shuffle) + mindspore_kwargs = dict(shuffle=None, sampler=sampler, num_shards=None, shard_id=None, **kwargs) + + # create dataset if name in _MINDSPORE_BASIC_DATASET: dataset_class = _MINDSPORE_BASIC_DATASET[name][0] dataset_download = _MINDSPORE_BASIC_DATASET[name][1] @@ -114,5 +134,21 @@ def create_dataset( if os.path.isdir(root): root = os.path.join(root, split) dataset = ImageFolderDataset(dataset_dir=root, **mindspore_kwargs) - + ''' Another implementation which a bit slower than ImageFolderDataset + imagenet_dataset = ImageNetDataset(dataset_dir=root) + # TODO: why round by 256? + sampler = RepeatAugSampler(len(imagenet_dataset), num_shards=num_shards, rank_id=shard_id, num_repeats=repeated_aug, selected_round=1, shuffle=shuffle) + dataset = ds.GeneratorDataset(imagenet_dataset, column_names=imagenet_dataset.column_names, sampler=sampler) + ''' return dataset + +def get_dataset_size(name, root, split): + if name in _MINDSPORE_BASIC_DATASET: + dataset_class = _MINDSPORE_BASIC_DATASET[name][0] + dataset = dataset_class(dataset_dir=root, usage=split) + else: + if os.path.isdir(root): + root = os.path.join(root, split) + dataset = ImageFolderDataset(dataset_dir=root) + + return dataset.get_dataset_size() diff --git a/mindcv/data/distributed_sampler.py b/mindcv/data/distributed_sampler.py new file mode 100644 index 00000000..05f7c4de --- /dev/null +++ b/mindcv/data/distributed_sampler.py @@ -0,0 +1,98 @@ +''' distributed sampler ''' +import math +import numpy as np + +class RepeatAugSampler(): + """Sampler that restricts data loading to a subset of the dataset for distributed, + with repeated augmentation. + It ensures that different each augmented version of a sample will be visible to a + different process. + + This sampler was adapted from https://github.com/facebookresearch/deit/blob/0c4b8f60/samplers.py + + Args: + dataset_size: dataset size + num_shards: num devices + rank_id: device id + shuffle: shuffle + num_repeats: num of repeated instances in repeated augmentation, default:3 + selected_round: round the total num of samples by this factor + """ + def __init__( + self, + dataset_size, + num_shards=None, + rank_id=None, + shuffle=True, + num_repeats=3, + selected_round=256, + ): + if num_shards is None: + print("WARNING: num_shards is set to 1 in RepeatAugSampler since it is not passed in") + num_shards = 1 + if rank_id is None: + rank_id = 0 + + #assert isinstance(num_repeats, int), f'num_repeats should be Type integer, but got {type(num_repeats)}' + + self.dataset_size = dataset_size + self.num_shards = num_shards + self.rank_id = rank_id + self.shuffle = shuffle + self.num_repeats = int(num_repeats) + self.epoch = 0 + self.num_samples = int(math.ceil(self.dataset_size * num_repeats / self.num_shards)) + self.total_size = self.num_samples * self.num_shards + # Determine the number of samples to select per epoch for each rank. + if selected_round: + self.num_selected_samples = int(math.floor( + self.dataset_size // selected_round * selected_round / num_shards)) + else: + self.num_selected_samples = int(math.ceil(self.dataset_size / num_shards)) + + def __iter__(self): + # deterministically shuffle based on epoch + #print('__iter__ generating new shuffled indices: ', self.epoch) + if self.shuffle: + indices = np.random.RandomState(seed=self.epoch).permutation(self.dataset_size) + indices = indices.tolist() + self.epoch += 1 + #print(indices[:30]) + else: + indices = list(range(self.dataset_size)) + # produce repeats e.g. [0, 0, 0, 1, 1, 1, 2, 2, 2....] + indices = [ele for ele in indices for i in range(self.num_repeats)] + + # add extra samples to make it evenly divisible + padding_size = self.total_size - len(indices) + if padding_size > 0: + indices += indices[:padding_size] + assert len(indices) == self.total_size + + # subsample per rank + indices = indices[self.rank_id:self.total_size:self.num_shards] + assert len(indices) == self.num_samples + + # return up to num selected samples + return iter(indices[:self.num_selected_samples]) + + def __len__(self): + return self.num_selected_samples + + def set_epoch(self, epoch): + self.epoch = epoch + +if __name__=='__main__': + num_devices = 2 + dataset_size = 20 + num_repeats = 3 + selected_round = 1 + shuffle = True + sampler1 = RepeatAugSampler(dataset_size, num_shards=num_devices, rank_id=0, num_repeats=num_repeats, selected_round=selected_round, shuffle=shuffle) + sampler2 = RepeatAugSampler(dataset_size, num_shards=num_devices, rank_id=1, num_repeats=num_repeats, selected_round=selected_round, shuffle=shuffle) + output1 = list(sampler1.__iter__()) + output2 = list(sampler2.__iter__()) + print(output1) + print(output2) + print(len(output1), len(output2), len(output1+output2)) + assert len(output1 + output2) == dataset_size, 'sizes of sampled outputs do not match dataset size' diff --git a/mindcv/models/__init__.py b/mindcv/models/__init__.py index b78905d0..6e7355a8 100644 --- a/mindcv/models/__init__.py +++ b/mindcv/models/__init__.py @@ -1,7 +1,7 @@ """models init""" from . import layers, convnext, densenet, dpn, efficientnet, ghostnet, googlenet, inception_v3, inception_v4, mnasnet,\ mobilenet_v1, mobilenet_v2, mobilenet_v3, model_factory, nasnet, pnasnet, registry, repvgg, res2net, resnet,\ - rexnet, shufflenetv1, shufflenetv2, sknet, squeezenet, swin_transformer, vgg, xception, convit, vit, volo + rexnet, shufflenetv1, shufflenetv2, sknet, squeezenet, swin_transformer, vgg, xception, convit, vit __all__ = [] __all__.extend(layers.__all__) diff --git a/mindcv/models/volo.py b/mindcv/models/volo.py deleted file mode 100644 index ba77e0e2..00000000 --- a/mindcv/models/volo.py +++ /dev/null @@ -1,964 +0,0 @@ -# Copyright 2021 Sea Limited. -# -# 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. - -""" -Vision OutLOoker (VOLO) implementation -""" -import mindspore as ms -import mindspore.nn as nn -from mindspore import ops -from mindspore import Parameter -from mindspore import Tensor -from mindspore import dtype as mstype -import mindspore.common.initializer as init -import mindspore.ops.functional as F -from mindspore import numpy as msnp -from .registry import register_model -from mindspore.ops import constexpr - -import time -import math -import numpy as np - -__all__ = [ - 'volo_d1', -] - - -def _cfg(url='', **kwargs): - return { - 'url': url, - 'num_classes': 1000, 'input_size': (3, 224, 224), 'pool_size': None, - 'crop_pct': .96, 'interpolation': 'bicubic', - 'mean': [0.485 * 255, 0.456 * 255, 0.406 * 255], - 'std': [0.229 * 255, 0.224 * 255, 0.225 * 255], - 'first_conv': 'patch_embed.proj', 'classifier': 'head', - **kwargs - } - - -default_cfgs = { - 'volo': _cfg(crop_pct=0.96), - 'volo_large': _cfg(crop_pct=1.15), -} - -def drop_path(x: Tensor, - drop_prob: float = 0., - training: bool = False, - scale_by_keep: bool = True) -> Tensor: - """ DropPath (Stochastic Depth) regularization layers """ - if drop_prob == 0. or not training: - return x - keep_prob = 1 - drop_prob - shape = (x.shape[0],) + (1,) * (x.ndim - 1) - random_tensor = ops.Dropout(keep_prob=keep_prob)(msnp.ones(shape))[1] - random_tensor = ops.Cast()(random_tensor, x.dtype) - if keep_prob > 0. and scale_by_keep: - random_tensor = ops.div(random_tensor, keep_prob) - return x * random_tensor - - -class DropPath(nn.Cell): - """ DropPath (Stochastic Depth) regularization layers """ - def __init__(self, - drop_prob: float = 0., - scale_by_keep: bool = True) -> None: - super().__init__() - self.drop_prob = drop_prob - self.scale_by_keep = scale_by_keep - - def construct(self, x: Tensor) -> Tensor: - return drop_path(x, self.drop_prob, self.training, self.scale_by_keep) - -# def unfold(img, kernel_size, stride=1, pad=0, dilation=1): -# """ -# unfold function -# """ -# # img = img.asnumpy() -# start = time.time() -# print("\nunfold shape", img.shape) -# batch_num, channel, height, width = img.shape -# out_h = (height + pad + pad - kernel_size - (kernel_size - 1) * (dilation - 1)) // stride + 1 -# out_w = (width + pad + pad - kernel_size - (kernel_size - 1) * (dilation - 1)) // stride + 1 - -# img = msnp.pad(img, [(0, 0), (0, 0), (pad, pad), (pad, pad)], 'constant') -# col = msnp.zeros((batch_num, channel, kernel_size, kernel_size, out_h, out_w), dtype=img.dtype) - -# for y in range(kernel_size): -# y_max = y + stride * out_h -# for x in range(kernel_size): -# x_max = x + stride * out_w -# col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride] - -# col = msnp.reshape(col, (batch_num, channel*kernel_size*kernel_size, out_h*out_w)) -# # col = Tensor(col) -# end = time.time() -# print("unfold rum time", end - start) -# return col - -import numpy as np -import mindspore as ms -from mindspore import nn, ops - - -# class NewConv2DTranspose(nn.Cell): -# def __init__(self, c, kernel_size, _pad, _stride, _dilation): -# super().__init__() -# self.conv_transpose2d = ops.Conv2DTranspose(c, kernel_size, pad_mode="pad", pad=_pad, -# stride=_stride, dilation=_dilation, group=c) - -# def construct(self, tensor, weight, output_shape): -# out = self.conv_transpose2d(tensor, weight, output_shape) - -# return out - -class Fold(nn.Cell): - def __init__(self, channels, output_size, kernel_size, dilation=1, padding=0, stride=1) -> None: - """Alternative implementation of fold layer via transposed convolution. - All parameters are the same as `"torch.nn.Fold" `_, - except for the additional `channels` parameter. We need `channels` to calculate the pre-allocated memory size of the convolution kernel. - :param channels: same as the `C` in the document of `"torch.nn.Fold" `_ - :type channels: int - """ - super().__init__() - def int2tuple(a): - if isinstance(a, int): - return (a, a) - return a - self.output_size, self.kernel_size, self.dilation, self.padding, self.stride = map(int2tuple, (output_size, kernel_size, dilation, padding, stride)) - self.h = int((self.output_size[0] + 2 * self.padding[0] - self.dilation[0] * (self.kernel_size[0] - 1) - 1) / self.stride[0] + 1) - self.w = int((self.output_size[1] + 2 * self.padding[1] - self.dilation[1] * (self.kernel_size[1] - 1) - 1) / self.stride[1] + 1) - self.k = self.kernel_size[0] * self.kernel_size[1] - self.c = channels - self.ck = self.c * self.k - weight = np.zeros((self.ck, 1, self.kernel_size[0], self.kernel_size[1])) - for i in range(self.ck): - xy = i % self.k - x = xy // self.kernel_size[1] - y = xy % self.kernel_size[1] - weight[i, 0, x, y] = 1 - - self.weight = Parameter(ms.Tensor(weight, ms.float16), requires_grad=False) - self.conv_transpose2d = ops.Conv2DTranspose(self.c, self.kernel_size, - pad_mode="pad", pad=(self.padding[0], self.padding[0], self.padding[1], self.padding[1]), - stride=stride, dilation=dilation, group=self.c) - - - def construct(self, tensor): - b, ck, l = tensor.shape - # todo: assert is not allowed in construct, how to check the shape? - # assert ck == self.c * self.k - # assert l == self.h * self.w - tensor = tensor.view((b, ck, self.h, self.w)) - out = self.conv_transpose2d(tensor.view((b, ck, self.h, self.w)), self.weight, (b, self.c, self.output_size[0], self.output_size[1])) - - return out - -def fold(col, input_shape, kernel_size, stride=1, pad=0): - """ - fold function - """ - batch_num, channel, height, width = input_shape - out_h = (height + pad + pad - kernel_size) // stride + 1 - out_w = (width + pad + pad - kernel_size) // stride + 1 - - col = msnp.reshape(col, (batch_num, channel, kernel_size, kernel_size, out_h, out_w)) - img = msnp.zeros((batch_num, - channel, - height + pad + pad + stride - 1, - width + pad + pad + stride - 1), dtype=col.dtype) - for y in range(kernel_size): - y_max = y + stride * out_h - for x in range(kernel_size): - x_max = x + stride * out_w - img[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :] - - out = img[:, :, pad:height + pad, pad:width + pad] - return out - - -class OutlookAttention(nn.Cell): - """ - Implementation of outlook attention - --dim: hidden dim - --num_heads: number of heads - --kernel_size: kernel size in each window for outlook attention - return: token features after outlook attention - """ - def __init__(self, dim, num_heads, kernel_size=3, padding=1, stride=1, - qkv_bias=False, qk_scale=None, attn_drop=0., proj_drop=0.): - super().__init__() - head_dim = dim // num_heads - self.num_heads = num_heads - self.kernel_size = kernel_size - self.padding = padding - self.stride = stride - self.scale = qk_scale or head_dim**-0.5 - - self.v = nn.Dense(dim, dim, has_bias=qkv_bias) - self.attn = nn.Dense(dim, kernel_size**4 * num_heads) - - self.attn_drop = nn.Dropout(1.0 - attn_drop) - self.proj = nn.Dense(dim, dim) - self.proj_drop = nn.Dropout(1.0 - proj_drop) - - self.unfold = nn.Unfold(ksizes=[1, kernel_size, kernel_size, 1], strides=[1, stride, stride, 1], - rates=[1, 1, 1, 1]) - self.pool = nn.AvgPool2d(kernel_size=stride, stride=stride) - self.softmax = nn.Softmax(axis=-1) - self.reshape = ops.Reshape() - self.transpose = ops.Transpose() - self.batch_mat_mul = ops.BatchMatMul() - - - def construct(self, x): - B, H, W, C = x.shape - - v = ops.Transpose()(self.v(x), (0, 3, 1, 2)) # B, C, H, W - - h = int((H - 1) / self.stride + 1) - w = int((W - 1) / self.stride + 1) - # start = time.time() - v = ops.pad(v, ((0, 0), (0, 0), (1, 1), (1,1))) # 舍弃ms的算子,改用函数定义 - # v = unfold(v, self.kernel_size, self.stride, self.padding) - v = self.unfold(v) - # end = time.time() - # print("ms-unfold run time", end - start) - # start = time.time() - v = self.reshape(v, (B, self.num_heads, C // self.num_heads, - self.kernel_size * self.kernel_size, - h * w)) - # end = time.time() - # print("\n") - # print("reshape run time", end - start) - v = self.transpose(v, (0, 1, 4, 3, 2)) # B,H,N,kxk,C/H - - attn = self.pool(self.transpose(x, (0, 3, 1, 2))) - attn = self.transpose(attn, (0, 2, 3, 1)) - attn = self.reshape(self.attn(attn), - (B, h * w, self.num_heads, self.kernel_size * self.kernel_size, - self.kernel_size * self.kernel_size)) - attn = self.transpose(attn, (0, 2, 1, 3, 4)) # B,H,N,kxk,kxk - attn = attn * self.scale - attn = self.softmax(attn) - attn = self.attn_drop(attn) - - x = self.transpose(self.batch_mat_mul(attn, v), (0, 1, 4, 3, 2)) - x = self.reshape(x, - (B, C * self.kernel_size * self.kernel_size, h * w)) - # print("H+", H, "W", W) - #fold = Fold(C, (H, W), self.kernel_size, padding=self.padding, stride=self.stride) - # print("before", x.shape) - #x = fold(x) - x = fold(x, input_shape=(B, C, H, W), kernel_size=self.kernel_size, - stride=self.stride, pad=self.padding) - # print("after", x.shape) - x = self.proj(self.transpose(x, (0, 2, 3, 1))) - x = self.proj_drop(x) - - return x - - -class Outlooker(nn.Cell): - """ - Implementation of outlooker layer: which includes outlook attention + MLP - Outlooker is the first stage in our VOLO - --dim: hidden dim - --num_heads: number of heads - --mlp_ratio: mlp ratio - --kernel_size: kernel size in each window for outlook attention - return: outlooker layer - """ - def __init__(self, dim, kernel_size, padding, stride=1, - num_heads=1,mlp_ratio=3., attn_drop=0., - drop_path=0., act_layer=nn.GELU, - norm_layer=nn.LayerNorm, qkv_bias=False, - qk_scale=None): - super().__init__() - self.norm1 = norm_layer([dim]) - print(dim, num_heads, kernel_size, padding,stride, - qkv_bias, qk_scale, - attn_drop) - - - self.attn = OutlookAttention(dim, num_heads, kernel_size=kernel_size, - padding=padding, stride=stride, - qkv_bias=qkv_bias, qk_scale=qk_scale, - attn_drop=attn_drop) - - self.drop_path = DropPath( - drop_path) if drop_path > 0. else ops.Identity() - - self.norm2 = norm_layer([dim]) - mlp_hidden_dim = int(dim * mlp_ratio) - self.mlp = Mlp(in_features=dim, - hidden_features=mlp_hidden_dim, - act_layer=act_layer) - # print("dim---", dim, "kernel_size---", kernel_size, "strid---", stride) - - def construct(self, x): - # print("\nxx",x.shape) - print("\nnorm", self.norm1(x).shape) - #print("\nnorm output: ", self.norm1(x)) - # print("\nattn", self.attn(self.norm1(x)).shape) - x = x + self.drop_path(self.attn(self.norm1(x))) - x = x + self.drop_path(self.mlp(self.norm2(x))) - return x - - -class Mlp(nn.Cell): - "Implementation of MLP" - - def __init__(self, in_features, hidden_features=None, - out_features=None, act_layer=nn.GELU, - drop=0.): - super().__init__() - out_features = out_features or in_features - hidden_features = hidden_features or in_features - self.fc1 = nn.Dense(in_features, hidden_features) - self.act = act_layer() - self.fc2 = nn.Dense(hidden_features, out_features) - self.drop = nn.Dropout(1.0 - drop) - - def construct(self, x): - x = self.fc1(x) - x = self.act(x) - x = self.drop(x) - x = self.fc2(x) - x = self.drop(x) - return x - - -class Attention(nn.Cell): - "Implementation of self-attention" - - def __init__(self, dim, num_heads=8, qkv_bias=False, - qk_scale=None, attn_drop=0., proj_drop=0.): - super().__init__() - self.num_heads = num_heads - head_dim = dim // num_heads - self.scale = qk_scale or head_dim**-0.5 - - self.qkv = nn.Dense(dim, dim * 3, has_bias=qkv_bias) - self.attn_drop = nn.Dropout(1.0 - attn_drop) - self.proj = nn.Dense(dim, dim) - self.proj_drop = nn.Dropout(1.0 - proj_drop) - self.reshape = ops.Reshape() - self.transpose = ops.Transpose() - self.softmax = nn.Softmax(axis=-1) - self.batch_mat_mul_transpose = ops.BatchMatMul(transpose_b=True) - self.batch_mat_mul = ops.BatchMatMul() - - - def construct(self, x): - B, H, W, C = x.shape - - qkv = self.qkv(x) - qkv = self.reshape(qkv, (B, H * W, 3, self.num_heads, - C // self.num_heads)) - qkv = self.transpose(qkv, (2, 0, 3, 1, 4)) - q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple) - - attn = self.batch_mat_mul_transpose(q, k) * self.scale - attn = self.softmax(attn) - attn = self.attn_drop(attn) - - x = self.transpose(self.batch_mat_mul(attn, v), (0, 2, 1, 3)) - x = self.reshape(x, (B, H, W, C)) - x = self.proj(x) - x = self.proj_drop(x) - return x - - -class Transformer(nn.Cell): - """ - Implementation of Transformer, - Transformer is the second stage in our VOLO - """ - def __init__(self, dim, num_heads, mlp_ratio=4., qkv_bias=False, - qk_scale=None, attn_drop=0., drop_path=0., - act_layer=nn.GELU, norm_layer=nn.LayerNorm): - super().__init__() - self.norm1 = norm_layer([dim]) - self.attn = Attention(dim, num_heads=num_heads, qkv_bias=qkv_bias, - qk_scale=qk_scale, attn_drop=attn_drop) - - # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here - self.drop_path = DropPath( - drop_path) if drop_path > 0. else ops.Identity() - - self.norm2 = norm_layer([dim]) - mlp_hidden_dim = int(dim * mlp_ratio) - self.mlp = Mlp(in_features=dim, - hidden_features=mlp_hidden_dim, - act_layer=act_layer) - - def construct(self, x): - x = x + self.drop_path(self.attn(self.norm1(x))) - x = x + self.drop_path(self.mlp(self.norm2(x))) - return x - - -class ClassAttention(nn.Cell): - """ - Class attention layer from CaiT, see details in CaiT - Class attention is the post stage in our VOLO, which is optional. - """ - def __init__(self, dim, num_heads=8, head_dim=None, qkv_bias=False, - qk_scale=None, attn_drop=0., proj_drop=0.): - super().__init__() - self.num_heads = num_heads - if head_dim is not None: - self.head_dim = head_dim - else: - head_dim = dim // num_heads - self.head_dim = head_dim - self.scale = qk_scale or head_dim**-0.5 - - self.kv = nn.Dense(dim, - self.head_dim * self.num_heads * 2, - has_bias=qkv_bias) - self.q = nn.Dense(dim, self.head_dim * self.num_heads, has_bias=qkv_bias) - self.attn_drop = nn.Dropout(1.0 - attn_drop) - self.proj = nn.Dense(self.head_dim * self.num_heads, dim) - self.proj_drop = nn.Dropout(1.0 - proj_drop) - self.reshape = ops.Reshape() - self.transpose = ops.Transpose() - self.batch_mat_mul_transpose = ops.BatchMatMul(transpose_b=True) - self.batch_mat_mul = ops.BatchMatMul() - self.softmax = nn.Softmax(axis=-1) - - def construct(self, x): - B, N, C = x.shape - - kv = self.kv(x) - kv = self.reshape(kv, (B, N, 2, self.num_heads, - self.head_dim)) - kv = self.transpose(kv, (2, 0, 3, 1, 4)) - k, v = kv[0], kv[1] # make torchscript happy (cannot use tensor as tuple) - q = self.q(x[:, :1, :]) - q = self.reshape(q, (B, self.num_heads, 1, self.head_dim)) - attn =self.batch_mat_mul_transpose(q * self.scale, k) - attn = self.softmax(attn) - attn = self.attn_drop(attn) - - cls_embed = self.transpose(self.batch_mat_mul(attn, v), (0, 2, 1, 3)) - cls_embed = self.reshape(cls_embed, (B, 1, self.head_dim * self.num_heads)) - cls_embed = self.proj(cls_embed) - cls_embed = self.proj_drop(cls_embed) - return cls_embed - - -class ClassBlock(nn.Cell): - """ - Class attention block from CaiT, see details in CaiT - We use two-layers class attention in our VOLO, which is optional. - """ - - def __init__(self, dim, num_heads, head_dim=None, mlp_ratio=4., - qkv_bias=False, qk_scale=None, drop=0., attn_drop=0., - drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm): - super().__init__() - self.norm1 = norm_layer([dim]) - self.attn = ClassAttention( - dim, num_heads=num_heads, head_dim=head_dim, qkv_bias=qkv_bias, - qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop) - # NOTE: drop path for stochastic depth - self.drop_path = DropPath( - drop_path) if drop_path > 0. else ops.Identity() - self.norm2 = norm_layer([dim]) - mlp_hidden_dim = int(dim * mlp_ratio) - self.mlp = Mlp(in_features=dim, - hidden_features=mlp_hidden_dim, - act_layer=act_layer, - drop=drop) - self.concat = ops.Concat(1) - - def construct(self, x): - cls_embed = x[:, :1] - cls_embed = cls_embed + self.drop_path(self.attn(self.norm1(x))) - cls_embed = cls_embed + self.drop_path(self.mlp(self.norm2(cls_embed))) - x = self.concat([cls_embed, x[:, 1:]]) - return x - - -def get_block(block_type, **kargs): - """ - get block by name, specifically for class attention block in here - """ - if block_type == 'ca': - return ClassBlock(**kargs) - - -# def rand_bbox(size, lam, scale=1): -# """ -# get bounding box as token labeling (https://github.com/zihangJiang/TokenLabeling) -# return: bounding box -# """ -# W = size[1] // scale -# H = size[2] // scale -# cut_rat = np.sqrt(1. - lam) -# cut_w = np.int(W * cut_rat) -# cut_h = np.int(H * cut_rat) - -# # uniform -# cx = np.random.randint(W) -# cy = np.random.randint(H) - -# bbx1 = np.clip(cx - cut_w // 2, 0, W) -# bby1 = np.clip(cy - cut_h // 2, 0, H) -# bbx2 = np.clip(cx + cut_w // 2, 0, W) -# bby2 = np.clip(cy + cut_h // 2, 0, H) - -# return int(bbx1), int(bby1), int(bbx2), int(bby2) - - -class PatchEmbed(nn.Cell): - """ - Image to Patch Embedding. - Different with ViT use 1 conv layer, we use 4 conv layers to do patch embedding - """ - - def __init__(self, img_size=224, stem_conv=False, stem_stride=1, - patch_size=8, in_channels=3, hidden_dim=64, embed_dim=384): - super().__init__() - assert patch_size in [4, 8, 16] - - self.stem_conv = stem_conv - if stem_conv: - self.conv = nn.SequentialCell( - nn.Conv2d(in_channels, hidden_dim, 7, stem_stride, - pad_mode='pad', padding=3), # 112x112 - nn.BatchNorm2d(hidden_dim), - nn.ReLU(), - nn.Conv2d(hidden_dim, hidden_dim, 3, 1, - pad_mode='pad', padding=1), # 112x112 - nn.BatchNorm2d(hidden_dim), - nn.ReLU(), - nn.Conv2d(hidden_dim, hidden_dim, 3, 1, - pad_mode='pad', padding=1), # 112x112 - nn.BatchNorm2d(hidden_dim), - nn.ReLU(), - ) - - self.proj = nn.Conv2d(hidden_dim, - embed_dim, - kernel_size=patch_size // stem_stride, - stride=patch_size // stem_stride) - self.num_patches = (img_size // patch_size) * (img_size // patch_size) - - def construct(self, x): - if self.stem_conv: - x = self.conv(x) - x = self.proj(x) # B, C, H, W - return x - - -class Downsample(nn.Cell): - """ - Image to Patch Embedding, downsampling between stage1 and stage2 - """ - def __init__(self, in_embed_dim, out_embed_dim, patch_size): - super().__init__() - self.proj = nn.Conv2d(in_embed_dim, out_embed_dim, - kernel_size=patch_size, stride=patch_size) - self.transpose = ops.Transpose() - - def construct(self, x): - x = self.transpose(x, (0, 3, 1, 2)) - x = self.proj(x) # B, C, H, W - x = self.transpose(x, (0, 2, 3, 1)) - return x - - -def outlooker_blocks(block_fn, index, dim, layers, num_heads=1, kernel_size=3, - padding=1,stride=1, mlp_ratio=3., qkv_bias=False, qk_scale=None, - attn_drop=0, drop_path_rate=0., **kwargs): - """ - generate outlooker layer in stage1 - return: outlooker layers - """ - blocks = [] - for block_idx in range(layers[index]): - block_dpr = drop_path_rate * (block_idx + - sum(layers[:index])) / (sum(layers) - 1) - blocks.append(block_fn(dim, kernel_size=kernel_size, padding=padding, - stride=stride, num_heads=num_heads, mlp_ratio=mlp_ratio, - qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, - drop_path=block_dpr)) - - blocks = nn.SequentialCell(*blocks) - - return blocks - - -def transformer_blocks(block_fn, index, dim, layers, num_heads, mlp_ratio=3., - qkv_bias=False, qk_scale=None, attn_drop=0, - drop_path_rate=0., **kwargs): - """ - generate transformer layers in stage2 - return: transformer layers - """ - blocks = [] - for block_idx in range(layers[index]): - block_dpr = drop_path_rate * (block_idx + - sum(layers[:index])) / (sum(layers) - 1) - blocks.append( - block_fn(dim, num_heads, - mlp_ratio=mlp_ratio, - qkv_bias=qkv_bias, - qk_scale=qk_scale, - attn_drop=attn_drop, - drop_path=block_dpr)) - - blocks = nn.SequentialCell(*blocks) - - return blocks - - -class VOLO(nn.Cell): - """ - Vision Outlooker, the main class of our model - --layers: [x,x,x,x], four blocks in two stages, the first block is outlooker, the - other three are transformer, we set four blocks, which are easily - applied to downstream tasks - --img_size, --in_channels, --num_classes: these three are very easy to understand - --patch_size: patch_size in outlook attention - --stem_hidden_dim: hidden dim of patch embedding, d1-d4 is 64, d5 is 128 - --embed_dims, --num_heads: embedding dim, number of heads in each block - --downsamples: flags to apply downsampling or not - --outlook_attention: flags to apply outlook attention or not - --mlp_ratios, --qkv_bias, --qk_scale, --drop_rate: easy to undertand - --attn_drop_rate, --drop_path_rate, --norm_layer: easy to undertand - --post_layers: post layers like two class attention layers using [ca, ca], - if yes, return_mean=False - --return_mean: use mean of all feature tokens for classification, if yes, no class token - --return_dense: use token labeling, details are here: - https://github.com/zihangJiang/TokenLabeling - --mix_token: mixing tokens as token labeling, details are here: - https://github.com/zihangJiang/TokenLabeling - --pooling_scale: pooling_scale=2 means we downsample 2x - --out_kernel, --out_stride, --out_padding: kerner size, - stride, and padding for outlook attention - """ - def __init__(self, layers, img_size=224, in_channels=3, num_classes=1000, patch_size=8, - stem_hidden_dim=64, embed_dims=None, num_heads=None, downsamples=None, - outlook_attention=None, mlp_ratios=None, qkv_bias=False, qk_scale=None, - drop_rate=0., attn_drop_rate=0., drop_path_rate=0., norm_layer=nn.LayerNorm, - post_layers=None, return_mean=False, return_dense=True, mix_token=True, - pooling_scale=2, out_kernel=3, out_stride=2, out_padding=1): - - super().__init__() - self.num_classes = num_classes - self.patch_embed = PatchEmbed(stem_conv=True, stem_stride=2, patch_size=patch_size, - in_channels=in_channels, hidden_dim=stem_hidden_dim, - embed_dim=embed_dims[0]) - print() - # inital positional encoding, we add positional encoding after outlooker blocks - self.pos_embed = Parameter( - ops.Zeros()((1, img_size // patch_size // pooling_scale, - img_size // patch_size // pooling_scale, - embed_dims[-1]), mstype.float32)) - self.pos_drop = nn.Dropout(1.0 - drop_rate) - - # set the main block in network - network = [] - for i in range(len(layers)): - if outlook_attention[i]: - # stage 1 - stage = outlooker_blocks(Outlooker, i, embed_dims[i], layers, - downsample=downsamples[i], num_heads=num_heads[i], - kernel_size=out_kernel, stride=out_stride, - padding=out_padding, mlp_ratio=mlp_ratios[i], - qkv_bias=qkv_bias, qk_scale=qk_scale, - attn_drop=attn_drop_rate, norm_layer=norm_layer) - network.append(stage) - else: - # stage 2 - stage = transformer_blocks(Transformer, i, embed_dims[i], layers, - num_heads[i], mlp_ratio=mlp_ratios[i], - qkv_bias=qkv_bias, qk_scale=qk_scale, - drop_path_rate=drop_path_rate, - attn_drop=attn_drop_rate, - norm_layer=norm_layer) - network.append(stage) - - if downsamples[i]: - # downsampling between two stages - network.append(Downsample(embed_dims[i], embed_dims[i + 1], 2)) - - self.network = nn.CellList(network) - - # set post block, for example, class attention layers - self.post_network = None - if post_layers is not None: - self.post_network = nn.CellList([ - get_block(post_layers[i], - dim=embed_dims[-1], - num_heads=num_heads[-1], - mlp_ratio=mlp_ratios[-1], - qkv_bias=qkv_bias, - qk_scale=qk_scale, - attn_drop=attn_drop_rate, - drop_path=0., - norm_layer=norm_layer) - for i in range(len(post_layers)) - ]) - self.cls_token = Parameter(ops.Zeros()((1, 1, embed_dims[-1]), mstype.float32)) - self.cls_token.set_data(init.initializer(init.TruncatedNormal(sigma=.02), self.cls_token.data.shape)) - - # set output type - self.return_mean = return_mean # if yes, return mean, not use class token - self.return_dense = return_dense # if yes, return class token and all feature tokens - if return_dense: - assert not return_mean, "cannot return both mean and dense" - self.mix_token = mix_token - self.pooling_scale = pooling_scale - if mix_token: # enable token mixing, see token labeling for details. - self.beta = 1.0 - assert return_dense, "return all tokens if mix_token is enabled" - if return_dense: - self.aux_head = nn.Dense( - embed_dims[-1], - num_classes) if num_classes > 0 else ops.Identity() - self.norm = norm_layer([embed_dims[-1]]) - - # Classifier head - self.head = nn.Dense( - embed_dims[-1], num_classes) if num_classes > 0 else ops.Identity() - - self.pos_embed.set_data(init.initializer(init.TruncatedNormal(sigma=.02), self.pos_embed.data.shape)) - self._init_weights() - - def _init_weights(self): - for name, m in self.cells_and_names(): - if isinstance(m, nn.Dense): - m.weight.set_data(init.initializer(init.TruncatedNormal(sigma=.02), m.weight.data.shape)) - if m.bias is not None: - m.bias.set_data(init.initializer(init.Constant(0), m.bias.shape)) - elif isinstance(m, nn.LayerNorm): - m.gamma.set_data(init.initializer(init.Constant(1), m.gamma.shape)) - m.beta.set_data(init.initializer(init.Constant(0), m.beta.shape)) - - def forward_embeddings(self, x): - # patch embedding - x = self.patch_embed(x) - # B,C,H,W-> B,H,W,C - x = ops.Transpose()(x, (0, 2, 3, 1)) - return x - - def forward_tokens(self, x): - for idx, block in enumerate(self.network): - if idx == 2: # add positional encoding after outlooker blocks - x = x + self.pos_embed - x = self.pos_drop(x) - x = block(x) - - B, H, W, C = x.shape - x = ops.Reshape()(x, (B, -1, C)) - return x - - def forward_cls(self, x): - # B, N, C = x.shape - cls_tokens = ops.broadcast_to(self.cls_token, (x.shape[0], -1, -1)) - x = ops.Cast()(x, cls_tokens.dtype) - x = ops.Concat(1)([cls_tokens, x]) - for block in self.post_network: - x = block(x) - return x - - def construct(self, x): - # step1: patch embedding - x = self.forward_embeddings(x) - - # patch_h, patch_w = x.shape[1] // self.pooling_scale, x.shape[ - # 2] // self.pooling_scale - # # mix token, see token labeling for details. - # if self.mix_token and self._phase == 'train': - # lam = np.random.beta(self.beta, self.beta) - # bbx1, bby1, bbx2, bby2 = rand_bbox(x.shape, lam, scale=self.pooling_scale) - # temp_x = x.copy() - # sbbx1,sbby1,sbbx2,sbby2=self.pooling_scale*bbx1,self.pooling_scale*bby1,\ - # self.pooling_scale*bbx2,self.pooling_scale*bby2 - # temp_x[:, sbbx1:sbbx2, sbby1:sbby2, :] = ops.ReverseV2([0])(x)[:, sbbx1:sbbx2, sbby1:sbby2, :] - # x = temp_x - # else: - # bbx1, bby1, bbx2, bby2 = 0, 0, 0, 0 - - # step2: tokens learning in the two stages - x = self.forward_tokens(x) - - # step3: post network, apply class attention or not - if self.post_network is not None: - x = self.forward_cls(x) - x = self.norm(x) - - if self.return_mean: # if no class token, return mean - return self.head(ops.mean(x, 1)) - - x_cls = self.head(x[:, 0]) - if not self.return_dense: - return x_cls - - # x_aux = self.aux_head( - # x[:, 1:] - # ) # generate classes in all feature tokens, see token labeling - - # if self._phase == 'predict': - # return x_cls + 0.5 * x_aux.max(1)[0] - - # if self.mix_token and self._phase == 'train': # reverse "mix token", see token labeling for details. - # x_aux = ops.Reshape()(x_aux, (x_aux.shape[0], patch_h, patch_w, x_aux.shape[-1])) - - # temp_x = x_aux.copy() - # temp_x[:, bbx1:bbx2, bby1:bby2, :] = ops.ReverseV2([0])(x_aux)[:, bbx1:bbx2, bby1:bby2, :] - # x_aux = temp_x - - # x_aux = ops.Reshape()(x_aux, (x_aux.shape[0], patch_h * patch_w, x_aux.shape[-1])) - - # return these: 1. class token, 2. classes from all feature tokens, 3. bounding box - return x_cls # , x_aux, (bbx1, bby1, bbx2, bby2) - -@register_model -def volo_d1(pretrained=False, **kwargs): - """ - VOLO-D1 model, Params: 27M - --layers: [x,x,x,x], four blocks in two stages, the first stage(block) is outlooker, - the other three blocks are transformer, we set four blocks, which are easily - applied to downstream tasks - --embed_dims, --num_heads,: embedding dim, number of heads in each block - --downsamples: flags to apply downsampling or not in four blocks - --outlook_attention: flags to apply outlook attention or not - --mlp_ratios: mlp ratio in four blocks - --post_layers: post layers like two class attention layers using [ca, ca] - See detail for all args in the class VOLO() - """ - layers = [4, 4, 8, 2] # num of layers in the four blocks - embed_dims = [192, 384, 384, 384] - num_heads = [6, 12, 12, 12] - mlp_ratios = [3, 3, 3, 3] - downsamples = [True, False, False, False] # do downsampling after first block - outlook_attention = [True, False, False, False ] - # first block is outlooker (stage1), the other three are transformer (stage2) - model = VOLO(layers, - embed_dims=embed_dims, - num_heads=num_heads, - mlp_ratios=mlp_ratios, - downsamples=downsamples, - outlook_attention=outlook_attention, - post_layers=['ca', 'ca'], - **kwargs) - model.default_cfg = default_cfgs['volo'] - return model - -@register_model -def volo_d2(pretrained=False, **kwargs): - """ - VOLO-D2 model, Params: 59M - """ - layers = [6, 4, 10, 4] - embed_dims = [256, 512, 512, 512] - num_heads = [8, 16, 16, 16] - mlp_ratios = [3, 3, 3, 3] - downsamples = [True, False, False, False] - outlook_attention = [True, False, False, False] - model = VOLO(layers, - embed_dims=embed_dims, - num_heads=num_heads, - mlp_ratios=mlp_ratios, - downsamples=downsamples, - outlook_attention=outlook_attention, - post_layers=['ca', 'ca'], - **kwargs) - model.default_cfg = default_cfgs['volo'] - return model - -@register_model -def volo_d3(pretrained=False, **kwargs): - """ - VOLO-D3 model, Params: 86M - """ - layers = [8, 8, 16, 4] - embed_dims = [256, 512, 512, 512] - num_heads = [8, 16, 16, 16] - mlp_ratios = [3, 3, 3, 3] - downsamples = [True, False, False, False] - outlook_attention = [True, False, False, False] - model = VOLO(layers, - embed_dims=embed_dims, - num_heads=num_heads, - mlp_ratios=mlp_ratios, - downsamples=downsamples, - outlook_attention=outlook_attention, - post_layers=['ca', 'ca'], - **kwargs) - model.default_cfg = default_cfgs['volo'] - return model - -@register_model -def volo_d4(pretrained=False, **kwargs): - """ - VOLO-D4 model, Params: 193M - """ - layers = [8, 8, 16, 4] - embed_dims = [384, 768, 768, 768] - num_heads = [12, 16, 16, 16] - mlp_ratios = [3, 3, 3, 3] - downsamples = [True, False, False, False] - outlook_attention = [True, False, False, False] - model = VOLO(layers, - embed_dims=embed_dims, - num_heads=num_heads, - mlp_ratios=mlp_ratios, - downsamples=downsamples, - outlook_attention=outlook_attention, - post_layers=['ca', 'ca'], - **kwargs) - model.default_cfg = default_cfgs['volo_large'] - return model - -@register_model -def volo_d5(pretrained=False, **kwargs): - """ - VOLO-D5 model, Params: 296M - stem_hidden_dim=128, the dim in patch embedding is 128 for VOLO-D5 - """ - layers = [12, 12, 20, 4] - embed_dims = [384, 768, 768, 768] - num_heads = [12, 16, 16, 16] - mlp_ratios = [4, 4, 4, 4] - downsamples = [True, False, False, False] - outlook_attention = [True, False, False, False] - model = VOLO(layers, - embed_dims=embed_dims, - num_heads=num_heads, - mlp_ratios=mlp_ratios, - downsamples=downsamples, - outlook_attention=outlook_attention, - post_layers=['ca', 'ca'], - stem_hidden_dim=128, - **kwargs) - model.default_cfg = default_cfgs['volo_large'] - return model - -if __name__ == '__main__': - dummy_input = ms.Tensor(np.random.rand(2, 28, 28, 192), dtype=ms.float32) - attn = OutlookAttention(192, 6, kernel_size=3, - padding=1, stride=2, - qkv_bias=False, qk_scale=None, - attn_drop=0.0) - output = attn(dummy_input) - print(output.shape) - - diff --git a/tests/modules/test_transforms.py b/tests/modules/test_transforms.py index 10f513a2..36926e23 100644 --- a/tests/modules/test_transforms.py +++ b/tests/modules/test_transforms.py @@ -1,5 +1,6 @@ import os import sys +import collections sys.path.append('.') import pytest @@ -143,3 +144,75 @@ def test_transforms_standalone_imagenet_is_training(mode, name, image_resize): assert type(transform_list_train) == list assert type(transform_list_val) == list assert transform_list_train != transform_list_val + + +def test_repeated_aug(): + + distribute = False + #ms.set_context(mode=ms.PYNATIVE_MODE) + if distribute: + from mindspore.communication import init, get_rank, get_group_size + ms.set_context(mode=ms.GRAPH_MODE) + init() + device_num = get_group_size() + rank_id = get_rank() + ms.set_auto_parallel_context(device_num=device_num, + parallel_mode='data_parallel', + gradients_mean=True) + else: + device_num = 1 + rank_id = 0 + + name = 'imagenet' + ''' + data_dir = '/data/imagenette2-320' + num_classes = 10 + ''' + dataset_url = "https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/intermediate/Canidae_data.zip" + root_dir = "./" + + if not os.path.exists(os.path.join(root_dir, 'data/Canidae')): + DownLoad().download_and_extract_archive(dataset_url, root_dir) + data_dir = "./data/Canidae/" + num_classes = 2 + + num_aug_repeats = 3 + + dataset = create_dataset( + name=name, + root=data_dir, + split='val', + shuffle=True, + num_samples=None, + num_parallel_workers=8, + num_shards=device_num, + shard_id=rank_id, + download=False, + num_aug_repeats=num_aug_repeats + ) + + # load dataset + loader = create_loader( + dataset=dataset, + batch_size=32, + drop_remainder=True, + is_training=False, + transform=None, + num_classes=num_classes, + num_parallel_workers=2, + ) + for epoch in range(1): + #cnt = 1 + for batch, (data, label) in enumerate(loader.create_tuple_iterator()): + mean_vals = data.mean(axis=[1,2,3]) + #print(mean_vals, mean_vals.shape) + rounded = [int(val * 10e8) for val in mean_vals] + rep_ele = [item for item, count in collections.Counter(rounded).items() if count > 1] + #print('repeated instance indices: ', len(rep_ele)) #, rep_ele) + assert len(rep_ele) > 0, 'Not replicated instances found in the batch' + if batch == 0: + print('Epoch: ', epoch, 'Batch: ', batch, 'Rank: ', rank_id, 'Label: ', label[:4]) + + +if __name__=='__main__': + test_repeated_aug() diff --git a/train.py b/train.py index f045778d..8bc010e9 100644 --- a/train.py +++ b/train.py @@ -50,7 +50,8 @@ def train(args): num_shards=device_num, shard_id=rank_id, num_parallel_workers=args.num_parallel_workers, - download=args.dataset_download) + download=args.dataset_download, + num_aug_repeats=args.aug_repeats) if args.num_classes is None: num_classes = dataset_train.num_classes() From 1c8611a66eb9abfd5a4ee1d0a3b63eb0d71f18c7 Mon Sep 17 00:00:00 2001 From: samithuang <285365963@qq.com> Date: Tue, 6 Dec 2022 17:40:40 +0000 Subject: [PATCH 31/32] fix subset sample for mnist dataset --- mindcv/data/dataset_factory.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/mindcv/data/dataset_factory.py b/mindcv/data/dataset_factory.py index 749fad3c..516395e0 100644 --- a/mindcv/data/dataset_factory.py +++ b/mindcv/data/dataset_factory.py @@ -6,11 +6,10 @@ import os from mindspore.dataset import MnistDataset, Cifar10Dataset, Cifar100Dataset, ImageFolderDataset, DistributedSampler +import mindspore.dataset as ds from .dataset_download import MnistDownload, Cifar10Download, Cifar100Download from .distributed_sampler import RepeatAugSampler - -#import mindspore.dataset as ds #from .dataset_reader import ImageNetDataset __all__ = ["create_dataset"] @@ -84,12 +83,16 @@ def create_dataset( name = name.lower() # subset sampling if num_samples is not None and num_samples > 0: - num_shards = 1 if num_shards is None else num_shards - shard_id = 0 if shard_id is None else shard_id - sampler = DistributedSampler(num_shards, shard_id, shuffle=shuffle, num_samples=num_samples) - - shuffle = None # shuffle and sampler cannot be set at the same in mindspore datatset API - mindspore_kwargs = dict(shuffle=shuffle, sampler=sampler, + # TODO: rewrite ordered distributed sampler + if num_shards is not None and num_shards > 1: # distributed + print('ns', num_shards, 'num_samples', num_samples) + sampler = DistributedSampler(num_shards, shard_id, shuffle=shuffle, num_samples=num_samples) + else: # standalone + if shuffle: + sampler = ds.RandomSampler(replacement=False, num_samples=num_samples) + else: + sampler = ds.SequentialSampler(num_samples=num_samples) + mindspore_kwargs = dict(shuffle=None, sampler=sampler, num_parallel_workers=num_parallel_workers, **kwargs) else: sampler = None From 1826034581a29384bb1f9f2917906ce3df79a92f Mon Sep 17 00:00:00 2001 From: Samit <285365963@qq.com> Date: Wed, 7 Dec 2022 14:18:20 +0800 Subject: [PATCH 32/32] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e19fe95..05b572e9 100644 --- a/README.md +++ b/README.md @@ -262,6 +262,7 @@ Please see [configs](./configs) for the details about model performance and pret * Augmentation * [AutoAugment](https://arxiv.org/abs/1805.09501) * [RandAugment](https://arxiv.org/abs/1909.13719) + * [Repeated Augmentation](https://openaccess.thecvf.com/content_CVPR_2020/papers/Hoffer_Augment_Your_Batch_Improving_Generalization_Through_Instance_Repetition_CVPR_2020_paper.pdf) * RandErasing (Cutout) * CutMix * Mixup @@ -294,8 +295,9 @@ Please see [configs](./configs) for the details about model performance and pret ## Notes ### What is New -- 2022/12/05 +- 2022/12/07 1. Support lr warmup for all lr scheduling algorithms besides cosine decay. +2. Add repeated augmentation, which can be enabled by setting `--aug_repeats` to be a value larger than 1 (typically, 3 is a common choice). - 2022/11/21 1. Add visualization for loss and acc curves