diff --git a/demo/auto_prune/README.md b/demo/auto_prune/README.md index 9ada4cf4c848ef24e296aa3ebf4724c707354199..3d562bf235600c73efdb8763a974da9c9a1eba70 100644 --- a/demo/auto_prune/README.md +++ b/demo/auto_prune/README.md @@ -15,7 +15,7 @@ ## 2. 运行示例 -提供两种自动裁剪模式,直接以裁剪目标进行一次自动裁剪,和多次迭代的方式进行裁剪。 +提供两种自动裁剪模式,直接以裁剪目标进行一次自动裁剪,和多次迭代的方式进行裁剪。 ###2.1一次裁剪 diff --git a/demo/auto_prune/train_finetune.py b/demo/auto_prune/train_finetune.py index 6508f435bb52cfc0421c77bc0c893bff7f315d03..534820b429b6f322afedaade31233a0af9fa3b3f 100644 --- a/demo/auto_prune/train_finetune.py +++ b/demo/auto_prune/train_finetune.py @@ -34,12 +34,12 @@ add_arg('config_file', str, None, "The config file for comp model_list = [m for m in dir(models) if "__" not in m] ratiolist = [ -# [0.06, 0.0, 0.09, 0.03, 0.09, 0.02, 0.05, 0.03, 0.0, 0.07, 0.07, 0.05, 0.08], -# [0.08, 0.02, 0.03, 0.13, 0.1, 0.06, 0.03, 0.04, 0.14, 0.02, 0.03, 0.02, 0.01], - ] + # [0.06, 0.0, 0.09, 0.03, 0.09, 0.02, 0.05, 0.03, 0.0, 0.07, 0.07, 0.05, 0.08], + # [0.08, 0.02, 0.03, 0.13, 0.1, 0.06, 0.03, 0.04, 0.14, 0.02, 0.03, 0.02, 0.01], +] -def save_model(args, exe, train_prog, eval_prog,info): +def save_model(args, exe, train_prog, eval_prog, info): model_path = os.path.join(args.model_save_dir, args.model, str(info)) if not os.path.isdir(model_path): os.makedirs(model_path) @@ -58,29 +58,31 @@ def piecewise_decay(args): regularization=fluid.regularizer.L2Decay(args.l2_decay)) return optimizer + def cosine_decay(args): step = int(math.ceil(float(args.total_images) / args.batch_size)) learning_rate = fluid.layers.cosine_decay( - learning_rate=args.lr, - step_each_epoch=step, - epochs=args.num_epochs) + learning_rate=args.lr, step_each_epoch=step, epochs=args.num_epochs) optimizer = fluid.optimizer.Momentum( learning_rate=learning_rate, momentum=args.momentum_rate, regularization=fluid.regularizer.L2Decay(args.l2_decay)) return optimizer + def create_optimizer(args): if args.lr_strategy == "piecewise_decay": return piecewise_decay(args) elif args.lr_strategy == "cosine_decay": return cosine_decay(args) + def compress(args): - class_dim=1000 - image_shape="3,224,224" + class_dim = 1000 + image_shape = "3,224,224" image_shape = [int(m) for m in image_shape.split(",")] - assert args.model in model_list, "{} is not in lists: {}".format(args.model, model_list) + assert args.model in model_list, "{} is not in lists: {}".format( + args.model, model_list) image = fluid.layers.data(name='image', shape=image_shape, dtype='float32') label = fluid.layers.data(name='label', shape=[1], dtype='int64') # model definition @@ -98,10 +100,13 @@ def compress(args): exe.run(fluid.default_startup_program()) if args.pretrained_model: + def if_exist(var): - exist = os.path.exists(os.path.join(args.pretrained_model, var.name)) - print("exist",exist) + exist = os.path.exists( + os.path.join(args.pretrained_model, var.name)) + print("exist", exist) return exist + #fluid.io.load_vars(exe, args.pretrained_model, predicate=if_exist) val_reader = paddle.batch(reader.val(), batch_size=args.batch_size) @@ -109,7 +114,8 @@ def compress(args): reader.train(), batch_size=args.batch_size, drop_last=True) train_feeder = feeder = fluid.DataFeeder([image, label], place) - val_feeder = feeder = fluid.DataFeeder([image, label], place, program=val_program) + val_feeder = feeder = fluid.DataFeeder( + [image, label], place, program=val_program) def test(epoch, program): batch_id = 0 @@ -117,80 +123,99 @@ def compress(args): acc_top5_ns = [] for data in val_reader(): start_time = time.time() - acc_top1_n, acc_top5_n = exe.run(program, - feed=train_feeder.feed(data), - fetch_list=[acc_top1.name, acc_top5.name]) + acc_top1_n, acc_top5_n = exe.run( + program, + feed=train_feeder.feed(data), + fetch_list=[acc_top1.name, acc_top5.name]) end_time = time.time() - print("Eval epoch[{}] batch[{}] - acc_top1: {}; acc_top5: {}; time: {}".format(epoch, batch_id, np.mean(acc_top1_n), np.mean(acc_top5_n), end_time-start_time)) + print( + "Eval epoch[{}] batch[{}] - acc_top1: {}; acc_top5: {}; time: {}". + format(epoch, batch_id, + np.mean(acc_top1_n), + np.mean(acc_top5_n), end_time - start_time)) acc_top1_ns.append(np.mean(acc_top1_n)) acc_top5_ns.append(np.mean(acc_top5_n)) batch_id += 1 - print("Final eval epoch[{}] - acc_top1: {}; acc_top5: {}".format(epoch, np.mean(np.array(acc_top1_ns)), np.mean(np.array(acc_top5_ns)))) - + print("Final eval epoch[{}] - acc_top1: {}; acc_top5: {}".format( + epoch, + np.mean(np.array(acc_top1_ns)), np.mean(np.array(acc_top5_ns)))) def train(epoch, program): build_strategy = fluid.BuildStrategy() exec_strategy = fluid.ExecutionStrategy() train_program = fluid.compiler.CompiledProgram( - program).with_data_parallel( - loss_name=avg_cost.name, - build_strategy=build_strategy, - exec_strategy=exec_strategy) + program).with_data_parallel( + loss_name=avg_cost.name, + build_strategy=build_strategy, + exec_strategy=exec_strategy) batch_id = 0 for data in train_reader(): start_time = time.time() - loss_n, acc_top1_n, acc_top5_n,lr_n = exe.run(train_program, - feed=train_feeder.feed(data), - fetch_list=[avg_cost.name, acc_top1.name, acc_top5.name,"learning_rate"]) + loss_n, acc_top1_n, acc_top5_n, lr_n = exe.run( + train_program, + feed=train_feeder.feed(data), + fetch_list=[ + avg_cost.name, acc_top1.name, acc_top5.name, + "learning_rate" + ]) end_time = time.time() loss_n = np.mean(loss_n) acc_top1_n = np.mean(acc_top1_n) acc_top5_n = np.mean(acc_top5_n) lr_n = np.mean(lr_n) - print("epoch[{}]-batch[{}] - loss: {}; acc_top1: {}; acc_top5: {};lrn: {}; time: {}".format(epoch, batch_id, loss_n, acc_top1_n, acc_top5_n, lr_n,end_time-start_time)) + print( + "epoch[{}]-batch[{}] - loss: {}; acc_top1: {}; acc_top5: {};lrn: {}; time: {}". + format(epoch, batch_id, loss_n, acc_top1_n, acc_top5_n, lr_n, + end_time - start_time)) batch_id += 1 params = [] for param in fluid.default_main_program().global_block().all_parameters(): #if "_weights" in param.name and "conv1_weights" not in param.name: - if "_sep_weights" in param.name: + if "_sep_weights" in param.name: params.append(param.name) - print("fops before pruning: {}".format(flops(fluid.default_main_program()))) + print("fops before pruning: {}".format( + flops(fluid.default_main_program()))) pruned_program_iter = fluid.default_main_program() pruned_val_program_iter = val_program for ratios in ratiolist: pruner = Pruner() - pruned_val_program_iter = pruner.prune(pruned_val_program_iter, - fluid.global_scope(), - params=params, - ratios=ratios, - place=place, - only_graph=True) - - - pruned_program_iter = pruner.prune(pruned_program_iter, - fluid.global_scope(), - params=params, - ratios=ratios, - place=place) + pruned_val_program_iter = pruner.prune( + pruned_val_program_iter, + fluid.global_scope(), + params=params, + ratios=ratios, + place=place, + only_graph=True) + + pruned_program_iter = pruner.prune( + pruned_program_iter, + fluid.global_scope(), + params=params, + ratios=ratios, + place=place) print("fops after pruning: {}".format(flops(pruned_program_iter))) - """ do not inherit learning rate """ - if(os.path.exists(args.pretrained_model + "/learning_rate")): - os.remove( args.pretrained_model + "/learning_rate") - if(os.path.exists(args.pretrained_model + "/@LR_DECAY_COUNTER@")): - os.remove( args.pretrained_model + "/@LR_DECAY_COUNTER@") - fluid.io.load_vars(exe, args.pretrained_model , main_program = pruned_program_iter, predicate=if_exist) + if (os.path.exists(args.pretrained_model + "/learning_rate")): + os.remove(args.pretrained_model + "/learning_rate") + if (os.path.exists(args.pretrained_model + "/@LR_DECAY_COUNTER@")): + os.remove(args.pretrained_model + "/@LR_DECAY_COUNTER@") + fluid.io.load_vars( + exe, + args.pretrained_model, + main_program=pruned_program_iter, + predicate=if_exist) pruned_program = pruned_program_iter pruned_val_program = pruned_val_program_iter for i in range(args.num_epochs): train(i, pruned_program) test(i, pruned_val_program) - save_model(args,exe,pruned_program,pruned_val_program,i) + save_model(args, exe, pruned_program, pruned_val_program, i) + def main(): args = parser.parse_args() diff --git a/demo/auto_prune/train_iterator.py b/demo/auto_prune/train_iterator.py index b30531ea9b7e7247fe44db958f1dd08759580cfb..f78660b4b9c29781e96932100ec25eb1be123c64 100644 --- a/demo/auto_prune/train_iterator.py +++ b/demo/auto_prune/train_iterator.py @@ -41,9 +41,10 @@ add_arg('test_period', int, 10, "Test period in epoches.") model_list = [m for m in dir(models) if "__" not in m] ratiolist = [ -# [0.06, 0.0, 0.09, 0.03, 0.09, 0.02, 0.05, 0.03, 0.0, 0.07, 0.07, 0.05, 0.08], -# [0.08, 0.02, 0.03, 0.13, 0.1, 0.06, 0.03, 0.04, 0.14, 0.02, 0.03, 0.02, 0.01], - ] + # [0.06, 0.0, 0.09, 0.03, 0.09, 0.02, 0.05, 0.03, 0.0, 0.07, 0.07, 0.05, 0.08], + # [0.08, 0.02, 0.03, 0.13, 0.1, 0.06, 0.03, 0.04, 0.14, 0.02, 0.03, 0.02, 0.01], +] + def piecewise_decay(args): step = int(math.ceil(float(args.total_images) / args.batch_size)) @@ -194,21 +195,26 @@ def compress(args): for ratios in ratiolist: pruner = Pruner() - pruned_val_program_iter = pruner.prune(pruned_val_program_iter, - fluid.global_scope(), - params=params, - ratios=ratios, - place=place, - only_graph=True) - - - pruned_program_iter = pruner.prune(pruned_program_iter, - fluid.global_scope(), - params=params, - ratios=ratios, - place=place) + pruned_val_program_iter = pruner.prune( + pruned_val_program_iter, + fluid.global_scope(), + params=params, + ratios=ratios, + place=place, + only_graph=True) + + pruned_program_iter = pruner.prune( + pruned_program_iter, + fluid.global_scope(), + params=params, + ratios=ratios, + place=place) print("fops after pruning: {}".format(flops(pruned_program_iter))) - fluid.io.load_vars(exe, args.pretrained_model , main_program = pruned_program_iter, predicate=if_exist) + fluid.io.load_vars( + exe, + args.pretrained_model, + main_program=pruned_program_iter, + predicate=if_exist) pruner = AutoPruner( pruned_val_program_iter, @@ -238,8 +244,6 @@ def compress(args): pruner.reward(score) - - def main(): args = parser.parse_args() print_arguments(args) diff --git a/demo/nas/search_space_doc.md b/demo/nas/search_space_doc.md index 682b0eac801bae4ae59b523475e8fa3c66586190..193b95919907d797e3c7b5bc63f992cd100a719e 100644 --- a/demo/nas/search_space_doc.md +++ b/demo/nas/search_space_doc.md @@ -3,22 +3,22 @@ 1. 根据原本模型结构构造搜索空间: 1.1 MobileNetV2Space - + 1.2 MobileNetV1Space - + 1.3 ResNetSpace 2. 根据相应模型的block构造搜索空间 2.1 MobileNetV1BlockSpace - + 2.2 MobileNetV2BlockSpace - + 2.3 ResNetBlockSpace - + 2.4 InceptionABlockSpace - + 2.5 InceptionCBlockSpace @@ -29,7 +29,7 @@ **block_num(int|None)**:`block_num`表示搜索空间中block的数量。 **block_mask(list|None)**:`block_mask`表示当前的block是一个reduction block还是一个normal block,是一组由0、1组成的列表,0表示当前block是normal block,1表示当前block是reduction block。如果设置了`block_mask`,则主要以`block_mask`为主要配置,`input_size`,`output_size`和`block_num`三种配置是无效的。 -**Note:** +**Note:** 1. reduction block表示经过这个block之后的feature map大小下降为之前的一半,normal block表示经过这个block之后feature map大小不变。 2. `input_size`和`output_size`用来计算整个模型结构中reduction block数量。 @@ -80,7 +80,7 @@ class ResNetBlockSpace2(SearchSpaceBase): self.bottleneck_params_list = [] for i in range(len(self.block_mask)): - self.bottleneck_params_list.append(self.filter_num[tokens[i * 3 + 0]], + self.bottleneck_params_list.append(self.filter_num[tokens[i * 3 + 0]], self.filter_num[tokens[i * 3 + 1]], self.filter_num[tokens[i * 3 + 2]], 2 if self.block_mask[i] == 1 else 1) @@ -113,4 +113,4 @@ class ResNetBlockSpace2(SearchSpaceBase): conv = fluid.layers.conv2d(input, num_filters, filter_size, stride, name=name+'_conv') bn = fluid.layers.batch_norm(conv, act=act, name=name+'_bn') return bn -``` +``` diff --git a/demo/optimizer.py b/demo/optimizer.py index 6b8962749b6f5000fadc67356dbb302b57d4c3e7..a01722f6e68bb1c487504df5ab07ca8f7a19e856 100644 --- a/demo/optimizer.py +++ b/demo/optimizer.py @@ -99,8 +99,8 @@ def exponential_decay_with_warmup(learning_rate, (step_each_epoch * warmup_epoch)) fluid.layers.assign(input=decayed_lr, output=lr) with switch.default(): - div_res = (global_step - warmup_epoch * step_each_epoch - ) / decay_epochs + div_res = ( + global_step - warmup_epoch * step_each_epoch) / decay_epochs div_res = ops.floor(div_res) decayed_lr = learning_rate * (decay_rate**div_res) fluid.layers.assign(input=decayed_lr, output=lr) diff --git a/demo/utility.py b/demo/utility.py index 475468f2777ae40427465327ff7b78355cfcbbeb..a214c48564c9d448f48d446b1e68fbb92b076d03 100644 --- a/demo/utility.py +++ b/demo/utility.py @@ -36,7 +36,8 @@ logging.basicConfig(format='%(asctime)s-%(levelname)s: %(message)s') _logger = logging.getLogger(__name__) _logger.setLevel(logging.INFO) -DOWNLOAD_RETRY_LIMIT=3 +DOWNLOAD_RETRY_LIMIT = 3 + def print_arguments(args): """Print argparse's arguments. @@ -211,6 +212,7 @@ def _download(url, path, md5sum=None): return fullname + def _md5check(fullname, md5sum=None): if md5sum is None: return True @@ -224,10 +226,11 @@ def _md5check(fullname, md5sum=None): if calc_md5sum != md5sum: _logger.info("File {} md5 check failed, {}(calc) != " - "{}(base)".format(fullname, calc_md5sum, md5sum)) + "{}(base)".format(fullname, calc_md5sum, md5sum)) return False return True + def _decompress(fname): """ Decompress for zip and tar file @@ -261,6 +264,7 @@ def _decompress(fname): shutil.rmtree(fpath_tmp) os.remove(fname) + def _move_and_merge_tree(src, dst): """ Move src directory to dst, if dst is already exists, diff --git a/docs/zh_cn/model_zoo.md b/docs/zh_cn/model_zoo.md index 2f28a6856933a8a107ae26e972430bf1e36d5737..2d961a91f828c842de7f4ed1d9dee1cb63b758cd 100644 --- a/docs/zh_cn/model_zoo.md +++ b/docs/zh_cn/model_zoo.md @@ -16,7 +16,7 @@ | MobileNetV2 | quant_aware |72.05%/90.63% (-0.1%/-0.02%)| 4.0 | - | [下载链接](https://paddlemodels.bj.bcebos.com/PaddleSlim/MobileNetV2_quant_aware.tar) | |ResNet50|-|76.50%/93.00%| 99 | 2.71 | [下载链接](http://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_pretrained.tar) | |ResNet50|quant_post|76.33%/93.02% (-0.17%/+0.02%)| 25.1| 1.19 | [下载链接](https://paddlemodels.bj.bcebos.com/PaddleSlim/ResNet50_quant_post.tar) | -|ResNet50|quant_aware| 76.48%/93.11% (-0.02%/+0.11%)| 25.1 | 1.17 | [下载链接](https://paddlemodels.bj.bcebos.com/PaddleSlim/ResNet50_quant_awre.tar) | +|ResNet50|quant_aware| 76.48%/93.11% (-0.02%/+0.11%)| 25.1 | 1.17 | [下载链接](https://paddlemodels.bj.bcebos.com/PaddleSlim/ResNet50_quant_awre.tar) | @@ -357,8 +357,8 @@ | 模型 | 压缩方法 | 数据集 | Image/GPU | 输入608 Box AP | 输入416 Box AP | 输入320 Box AP | 模型体积(MB) | TensorRT时延(V100, ms) | 下载 | | :----------------------------: | :---------: | :----: | :-------: | :------------: | :------------: | :------------: | :------------: | :----------: |:----------: | | MobileNet-V1-YOLOv3 | - | COCO | 8 | 29.3 | 29.3 | 27.1 | 95 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1.tar) | -| MobileNet-V1-YOLOv3 | quant_post | COCO | 8 | 27.9 (-1.4)| 28.0 (-1.3) | 26.0 (-1.0) | 25 | - | [下载链接](https://paddlemodels.bj.bcebos.com/PaddleSlim/yolov3_mobilenetv1_coco_quant_post.tar) | -| MobileNet-V1-YOLOv3 | quant_aware | COCO | 8 | 28.1 (-1.2)| 28.2 (-1.1) | 25.8 (-1.2) | 26.3 | - | [下载链接](https://paddlemodels.bj.bcebos.com/PaddleSlim/yolov3_mobilenet_coco_quant_aware.tar) | +| MobileNet-V1-YOLOv3 | quant_post | COCO | 8 | 27.9 (-1.4)| 28.0 (-1.3) | 26.0 (-1.0) | 25 | - | [下载链接](https://paddlemodels.bj.bcebos.com/PaddleSlim/yolov3_mobilenetv1_coco_quant_post.tar) | +| MobileNet-V1-YOLOv3 | quant_aware | COCO | 8 | 28.1 (-1.2)| 28.2 (-1.1) | 25.8 (-1.2) | 26.3 | - | [下载链接](https://paddlemodels.bj.bcebos.com/PaddleSlim/yolov3_mobilenet_coco_quant_aware.tar) | | R34-YOLOv3 | - | COCO | 8 | 36.2 | 34.3 | 31.4 | 162 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_r34.tar) | | R34-YOLOv3 | quant_post | COCO | 8 | 35.7 (-0.5) | - | - | 42.7 | - | [下载链接](https://paddlemodels.bj.bcebos.com/PaddleSlim/yolov3_r34_coco_quant_post.tar) | | R34-YOLOv3 | quant_aware | COCO | 8 | 35.2 (-1.0) | 33.3 (-1.0) | 30.3 (-1.1)| 44 | - | [下载链接](https://paddlemodels.bj.bcebos.com/PaddleSlim/yolov3_r34_coco_quant_aware.tar) | diff --git a/tests/test_sa_nas.py b/tests/test_sa_nas.py index 8630136ef256075f6d83e934b9bb2e30f8d22ef6..a379943b147520acecc3f67e27696d557c02ea02 100644 --- a/tests/test_sa_nas.py +++ b/tests/test_sa_nas.py @@ -19,24 +19,27 @@ from paddleslim.nas import SANAS from paddleslim.analysis import flops import numpy as np + def compute_op_num(program): params = {} ch_list = [] for block in program.blocks: for param in block.all_parameters(): - if len(param.shape) == 4: + if len(param.shape) == 4: params[param.name] = param.shape ch_list.append(int(param.shape[0])) return params, ch_list + class TestSANAS(unittest.TestCase): def setUp(self): self.init_test_case() port = np.random.randint(8337, 8773) - self.sanas = SANAS(configs=self.configs, server_addr=("", port), save_checkpoint=None) + self.sanas = SANAS( + configs=self.configs, server_addr=("", port), save_checkpoint=None) def init_test_case(self): - self.configs=[('MobileNetV2BlockSpace', {'block_mask':[0]})] + self.configs = [('MobileNetV2BlockSpace', {'block_mask': [0]})] self.filter_num = np.array([ 3, 4, 8, 12, 16, 24, 32, 48, 64, 80, 96, 128, 144, 160, 192, 224, 256, 320, 384, 512 @@ -53,7 +56,10 @@ class TestSANAS(unittest.TestCase): conv_list, ch_pro = compute_op_num(program) ### assert conv number - self.assertTrue((repeat_num * 3) == len(conv_list), "the number of conv is NOT match, the number compute from token: {}, actual conv number: {}".format(repeat_num * 3, len(conv_list))) + self.assertTrue((repeat_num * 3) == len( + conv_list + ), "the number of conv is NOT match, the number compute from token: {}, actual conv number: {}". + format(repeat_num * 3, len(conv_list))) ### assert number of channels ch_token = [] @@ -64,7 +70,10 @@ class TestSANAS(unittest.TestCase): ch_token.append(filter_num) init_ch_num = filter_num - self.assertTrue(str(ch_token) == str(ch_pro), "channel num is WRONG, channel num from token is {}, channel num come fom program is {}".format(str(ch_token), str(ch_pro))) + self.assertTrue( + str(ch_token) == str(ch_pro), + "channel num is WRONG, channel num from token is {}, channel num come fom program is {}". + format(str(ch_token), str(ch_pro))) def test_all_function(self): ### unittest for next_archs @@ -73,7 +82,8 @@ class TestSANAS(unittest.TestCase): token2arch_program = fluid.Program() with fluid.program_guard(next_program, startup_program): - inputs = fluid.data(name='input', shape=[None, 3, 32, 32], dtype='float32') + inputs = fluid.data( + name='input', shape=[None, 3, 32, 32], dtype='float32') archs = self.sanas.next_archs() for arch in archs: output = arch(inputs) @@ -85,8 +95,10 @@ class TestSANAS(unittest.TestCase): ### uniitest for tokens2arch with fluid.program_guard(token2arch_program, startup_program): - inputs = fluid.data(name='input', shape=[None, 3, 32, 32], dtype='float32') - arch = self.sanas.tokens2arch(self.sanas.current_info()['current_tokens']) + inputs = fluid.data( + name='input', shape=[None, 3, 32, 32], dtype='float32') + arch = self.sanas.tokens2arch(self.sanas.current_info()[ + 'current_tokens']) for arch in archs: output = arch(inputs) inputs = output @@ -94,7 +106,11 @@ class TestSANAS(unittest.TestCase): ### unittest for current_info current_info = self.sanas.current_info() - self.assertTrue(isinstance(current_info, dict), "the type of current info must be dict, but now is {}".format(type(current_info))) + self.assertTrue( + isinstance(current_info, dict), + "the type of current info must be dict, but now is {}".format( + type(current_info))) + if __name__ == '__main__': unittest.main()