add naswot
This commit is contained in:
		
							
								
								
									
										13
									
								
								graph_dit/naswot/config_utils/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								graph_dit/naswot/config_utils/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| from .configure_utils    import load_config, dict2config, configure2str | ||||
| from .basic_args         import obtain_basic_args | ||||
| from .attention_args     import obtain_attention_args | ||||
| from .random_baseline    import obtain_RandomSearch_args | ||||
| from .cls_kd_args        import obtain_cls_kd_args | ||||
| from .cls_init_args      import obtain_cls_init_args | ||||
| from .search_single_args import obtain_search_single_args | ||||
| from .search_args        import obtain_search_args | ||||
| # for network pruning | ||||
| from .pruning_args       import obtain_pruning_args | ||||
							
								
								
									
										22
									
								
								graph_dit/naswot/config_utils/attention_args.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								graph_dit/naswot/config_utils/attention_args.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| import random, argparse | ||||
| from .share_args import add_shared_args | ||||
|  | ||||
| def obtain_attention_args(): | ||||
|   parser = argparse.ArgumentParser(description='Train a classification model on typical image classification datasets.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) | ||||
|   parser.add_argument('--resume'      ,     type=str,                   help='Resume path.') | ||||
|   parser.add_argument('--init_model'  ,     type=str,                   help='The initialization model path.') | ||||
|   parser.add_argument('--model_config',     type=str,                   help='The path to the model configuration') | ||||
|   parser.add_argument('--optim_config',     type=str,                   help='The path to the optimizer configuration') | ||||
|   parser.add_argument('--procedure'   ,     type=str,                   help='The procedure basic prefix.') | ||||
|   parser.add_argument('--att_channel' ,     type=int,                   help='.') | ||||
|   parser.add_argument('--att_spatial' ,     type=str,                   help='.') | ||||
|   parser.add_argument('--att_active'  ,     type=str,                   help='.') | ||||
|   add_shared_args( parser ) | ||||
|   # Optimization options | ||||
|   parser.add_argument('--batch_size',       type=int,   default=2,      help='Batch size for training.') | ||||
|   args = parser.parse_args() | ||||
|  | ||||
|   if args.rand_seed is None or args.rand_seed < 0: | ||||
|     args.rand_seed = random.randint(1, 100000) | ||||
|   assert args.save_dir is not None, 'save-path argument can not be None' | ||||
|   return args | ||||
							
								
								
									
										24
									
								
								graph_dit/naswot/config_utils/basic_args.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								graph_dit/naswot/config_utils/basic_args.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2020 # | ||||
| ################################################## | ||||
| import random, argparse | ||||
| from .share_args import add_shared_args | ||||
|  | ||||
| def obtain_basic_args(): | ||||
|   parser = argparse.ArgumentParser(description='Train a classification model on typical image classification datasets.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) | ||||
|   parser.add_argument('--resume'      ,     type=str,                   help='Resume path.') | ||||
|   parser.add_argument('--init_model'  ,     type=str,                   help='The initialization model path.') | ||||
|   parser.add_argument('--model_config',     type=str,                   help='The path to the model configuration') | ||||
|   parser.add_argument('--optim_config',     type=str,                   help='The path to the optimizer configuration') | ||||
|   parser.add_argument('--procedure'   ,     type=str,                   help='The procedure basic prefix.') | ||||
|   parser.add_argument('--model_source',     type=str,  default='normal',help='The source of model defination.') | ||||
|   parser.add_argument('--extra_model_path', type=str,  default=None,    help='The extra model ckp file (help to indicate the searched architecture).') | ||||
|   add_shared_args( parser ) | ||||
|   # Optimization options | ||||
|   parser.add_argument('--batch_size',       type=int,  default=2,       help='Batch size for training.') | ||||
|   args = parser.parse_args() | ||||
|  | ||||
|   if args.rand_seed is None or args.rand_seed < 0: | ||||
|     args.rand_seed = random.randint(1, 100000) | ||||
|   assert args.save_dir is not None, 'save-path argument can not be None' | ||||
|   return args | ||||
							
								
								
									
										4
									
								
								graph_dit/naswot/config_utils/cifar-split.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								graph_dit/naswot/config_utils/cifar-split.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										20
									
								
								graph_dit/naswot/config_utils/cls_init_args.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								graph_dit/naswot/config_utils/cls_init_args.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| import random, argparse | ||||
| from .share_args import add_shared_args | ||||
|  | ||||
| def obtain_cls_init_args(): | ||||
|   parser = argparse.ArgumentParser(description='Train a classification model on typical image classification datasets.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) | ||||
|   parser.add_argument('--resume'      ,     type=str,                   help='Resume path.') | ||||
|   parser.add_argument('--init_model'  ,     type=str,                   help='The initialization model path.') | ||||
|   parser.add_argument('--model_config',     type=str,                   help='The path to the model configuration') | ||||
|   parser.add_argument('--optim_config',     type=str,                   help='The path to the optimizer configuration') | ||||
|   parser.add_argument('--procedure'   ,     type=str,                   help='The procedure basic prefix.') | ||||
|   parser.add_argument('--init_checkpoint',  type=str,                   help='The checkpoint path to the initial model.') | ||||
|   add_shared_args( parser ) | ||||
|   # Optimization options | ||||
|   parser.add_argument('--batch_size',       type=int,   default=2,      help='Batch size for training.') | ||||
|   args = parser.parse_args() | ||||
|  | ||||
|   if args.rand_seed is None or args.rand_seed < 0: | ||||
|     args.rand_seed = random.randint(1, 100000) | ||||
|   assert args.save_dir is not None, 'save-path argument can not be None' | ||||
|   return args | ||||
							
								
								
									
										23
									
								
								graph_dit/naswot/config_utils/cls_kd_args.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								graph_dit/naswot/config_utils/cls_kd_args.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| import random, argparse | ||||
| from .share_args import add_shared_args | ||||
|  | ||||
| def obtain_cls_kd_args(): | ||||
|   parser = argparse.ArgumentParser(description='Train a classification model on typical image classification datasets.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) | ||||
|   parser.add_argument('--resume'      ,     type=str,                   help='Resume path.') | ||||
|   parser.add_argument('--init_model'  ,     type=str,                   help='The initialization model path.') | ||||
|   parser.add_argument('--model_config',     type=str,                   help='The path to the model configuration') | ||||
|   parser.add_argument('--optim_config',     type=str,                   help='The path to the optimizer configuration') | ||||
|   parser.add_argument('--procedure'   ,     type=str,                   help='The procedure basic prefix.') | ||||
|   parser.add_argument('--KD_checkpoint',    type=str,                   help='The teacher checkpoint in knowledge distillation.') | ||||
|   parser.add_argument('--KD_alpha'    ,     type=float,                 help='The alpha parameter in knowledge distillation.') | ||||
|   parser.add_argument('--KD_temperature',   type=float,                 help='The temperature parameter in knowledge distillation.') | ||||
|   #parser.add_argument('--KD_feature',       type=float,                 help='Knowledge distillation at the feature level.') | ||||
|   add_shared_args( parser ) | ||||
|   # Optimization options | ||||
|   parser.add_argument('--batch_size',       type=int,   default=2,      help='Batch size for training.') | ||||
|   args = parser.parse_args() | ||||
|  | ||||
|   if args.rand_seed is None or args.rand_seed < 0: | ||||
|     args.rand_seed = random.randint(1, 100000) | ||||
|   assert args.save_dir is not None, 'save-path argument can not be None' | ||||
|   return args | ||||
							
								
								
									
										106
									
								
								graph_dit/naswot/config_utils/configure_utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								graph_dit/naswot/config_utils/configure_utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # All rights reserved. | ||||
| # | ||||
| # This source code is licensed under the license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
| # | ||||
| import os, json | ||||
| from os import path as osp | ||||
| from pathlib import Path | ||||
| from collections import namedtuple | ||||
|  | ||||
| support_types = ('str', 'int', 'bool', 'float', 'none') | ||||
|  | ||||
|  | ||||
| def convert_param(original_lists): | ||||
|   assert isinstance(original_lists, list), 'The type is not right : {:}'.format(original_lists) | ||||
|   ctype, value = original_lists[0], original_lists[1] | ||||
|   assert ctype in support_types, 'Ctype={:}, support={:}'.format(ctype, support_types) | ||||
|   is_list = isinstance(value, list) | ||||
|   if not is_list: value = [value] | ||||
|   outs = [] | ||||
|   for x in value: | ||||
|     if ctype == 'int': | ||||
|       x = int(x) | ||||
|     elif ctype == 'str': | ||||
|       x = str(x) | ||||
|     elif ctype == 'bool': | ||||
|       x = bool(int(x)) | ||||
|     elif ctype == 'float': | ||||
|       x = float(x) | ||||
|     elif ctype == 'none': | ||||
|       if x.lower() != 'none': | ||||
|         raise ValueError('For the none type, the value must be none instead of {:}'.format(x)) | ||||
|       x = None | ||||
|     else: | ||||
|       raise TypeError('Does not know this type : {:}'.format(ctype)) | ||||
|     outs.append(x) | ||||
|   if not is_list: outs = outs[0] | ||||
|   return outs | ||||
|  | ||||
|  | ||||
| def load_config(path, extra, logger): | ||||
|   path = str(path) | ||||
|   if hasattr(logger, 'log'): logger.log(path) | ||||
|   assert os.path.exists(path), 'Can not find {:}'.format(path) | ||||
|   # Reading data back | ||||
|   with open(path, 'r') as f: | ||||
|     data = json.load(f) | ||||
|   content = { k: convert_param(v) for k,v in data.items()} | ||||
|   assert extra is None or isinstance(extra, dict), 'invalid type of extra : {:}'.format(extra) | ||||
|   if isinstance(extra, dict): content = {**content, **extra} | ||||
|   Arguments = namedtuple('Configure', ' '.join(content.keys())) | ||||
|   content   = Arguments(**content) | ||||
|   if hasattr(logger, 'log'): logger.log('{:}'.format(content)) | ||||
|   return content | ||||
|  | ||||
|  | ||||
| def configure2str(config, xpath=None): | ||||
|   if not isinstance(config, dict): | ||||
|     config = config._asdict() | ||||
|   def cstring(x): | ||||
|     return "\"{:}\"".format(x) | ||||
|   def gtype(x): | ||||
|     if isinstance(x, list): x = x[0] | ||||
|     if isinstance(x, str)  : return 'str' | ||||
|     elif isinstance(x, bool) : return 'bool' | ||||
|     elif isinstance(x, int): return 'int' | ||||
|     elif isinstance(x, float): return 'float' | ||||
|     elif x is None           : return 'none' | ||||
|     else: raise ValueError('invalid : {:}'.format(x)) | ||||
|   def cvalue(x, xtype): | ||||
|     if isinstance(x, list): is_list = True | ||||
|     else: | ||||
|       is_list, x = False, [x] | ||||
|     temps = [] | ||||
|     for temp in x: | ||||
|       if xtype == 'bool'  : temp = cstring(int(temp)) | ||||
|       elif xtype == 'none': temp = cstring('None') | ||||
|       else                : temp = cstring(temp) | ||||
|       temps.append( temp ) | ||||
|     if is_list: | ||||
|       return "[{:}]".format( ', '.join( temps ) ) | ||||
|     else: | ||||
|       return temps[0] | ||||
|  | ||||
|   xstrings = [] | ||||
|   for key, value in config.items(): | ||||
|     xtype  = gtype(value) | ||||
|     string = '  {:20s} : [{:8s}, {:}]'.format(cstring(key), cstring(xtype), cvalue(value, xtype)) | ||||
|     xstrings.append(string) | ||||
|   Fstring = '{\n' + ',\n'.join(xstrings) + '\n}' | ||||
|   if xpath is not None: | ||||
|     parent = Path(xpath).resolve().parent | ||||
|     parent.mkdir(parents=True, exist_ok=True) | ||||
|     if osp.isfile(xpath): os.remove(xpath) | ||||
|     with open(xpath, "w") as text_file: | ||||
|       text_file.write('{:}'.format(Fstring)) | ||||
|   return Fstring | ||||
|  | ||||
|  | ||||
| def dict2config(xdict, logger): | ||||
|   assert isinstance(xdict, dict), 'invalid type : {:}'.format( type(xdict) ) | ||||
|   Arguments = namedtuple('Configure', ' '.join(xdict.keys())) | ||||
|   content   = Arguments(**xdict) | ||||
|   if hasattr(logger, 'log'): logger.log('{:}'.format(content)) | ||||
|   return content | ||||
							
								
								
									
										26
									
								
								graph_dit/naswot/config_utils/pruning_args.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								graph_dit/naswot/config_utils/pruning_args.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| import os, sys, time, random, argparse | ||||
| from .share_args import add_shared_args | ||||
|  | ||||
| def obtain_pruning_args(): | ||||
|   parser = argparse.ArgumentParser(description='Train a classification model on typical image classification datasets.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) | ||||
|   parser.add_argument('--resume'      ,     type=str,                   help='Resume path.') | ||||
|   parser.add_argument('--init_model'  ,     type=str,                   help='The initialization model path.') | ||||
|   parser.add_argument('--model_config',     type=str,                   help='The path to the model configuration') | ||||
|   parser.add_argument('--optim_config',     type=str,                   help='The path to the optimizer configuration') | ||||
|   parser.add_argument('--procedure'   ,     type=str,                   help='The procedure basic prefix.') | ||||
|   parser.add_argument('--keep_ratio'  ,     type=float,                 help='The left channel ratio compared to the original network.') | ||||
|   parser.add_argument('--model_version',    type=str,                   help='The network version.') | ||||
|   parser.add_argument('--KD_alpha'    ,     type=float,                 help='The alpha parameter in knowledge distillation.') | ||||
|   parser.add_argument('--KD_temperature',   type=float,                 help='The temperature parameter in knowledge distillation.') | ||||
|   parser.add_argument('--Regular_W_feat',   type=float,                 help='The .') | ||||
|   parser.add_argument('--Regular_W_conv',   type=float,                 help='The .') | ||||
|   add_shared_args( parser ) | ||||
|   # Optimization options | ||||
|   parser.add_argument('--batch_size',       type=int,  default=2,       help='Batch size for training.') | ||||
|   args = parser.parse_args() | ||||
|  | ||||
|   if args.rand_seed is None or args.rand_seed < 0: | ||||
|     args.rand_seed = random.randint(1, 100000) | ||||
|   assert args.save_dir is not None, 'save-path argument can not be None' | ||||
|   assert args.keep_ratio > 0 and args.keep_ratio <= 1, 'invalid keep ratio : {:}'.format(args.keep_ratio) | ||||
|   return args | ||||
							
								
								
									
										24
									
								
								graph_dit/naswot/config_utils/random_baseline.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								graph_dit/naswot/config_utils/random_baseline.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| import os, sys, time, random, argparse | ||||
| from .share_args import add_shared_args | ||||
|  | ||||
|  | ||||
| def obtain_RandomSearch_args(): | ||||
|   parser = argparse.ArgumentParser(description='Train a classification model on typical image classification datasets.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) | ||||
|   parser.add_argument('--resume'      ,     type=str,                   help='Resume path.') | ||||
|   parser.add_argument('--init_model'  ,     type=str,                   help='The initialization model path.') | ||||
|   parser.add_argument('--expect_flop',      type=float,                 help='The expected flop keep ratio.') | ||||
|   parser.add_argument('--arch_nums'   ,     type=int,                   help='The maximum number of running random arch generating..') | ||||
|   parser.add_argument('--model_config',     type=str,                   help='The path to the model configuration') | ||||
|   parser.add_argument('--optim_config',     type=str,                   help='The path to the optimizer configuration') | ||||
|   parser.add_argument('--random_mode', type=str, choices=['random', 'fix'], help='The path to the optimizer configuration') | ||||
|   parser.add_argument('--procedure'   ,     type=str,                   help='The procedure basic prefix.') | ||||
|   add_shared_args( parser ) | ||||
|   # Optimization options | ||||
|   parser.add_argument('--batch_size',       type=int,   default=2,      help='Batch size for training.') | ||||
|   args = parser.parse_args() | ||||
|  | ||||
|   if args.rand_seed is None or args.rand_seed < 0: | ||||
|     args.rand_seed = random.randint(1, 100000) | ||||
|   assert args.save_dir is not None, 'save-path argument can not be None' | ||||
|   #assert args.flop_ratio_min < args.flop_ratio_max, 'flop-ratio {:} vs {:}'.format(args.flop_ratio_min, args.flop_ratio_max) | ||||
|   return args | ||||
							
								
								
									
										32
									
								
								graph_dit/naswot/config_utils/search_args.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								graph_dit/naswot/config_utils/search_args.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| import os, sys, time, random, argparse | ||||
| from .share_args import add_shared_args | ||||
|  | ||||
|  | ||||
| def obtain_search_args(): | ||||
|   parser = argparse.ArgumentParser(description='Train a classification model on typical image classification datasets.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) | ||||
|   parser.add_argument('--resume'        ,   type=str,                   help='Resume path.') | ||||
|   parser.add_argument('--model_config'  ,   type=str,                   help='The path to the model configuration') | ||||
|   parser.add_argument('--optim_config'  ,   type=str,                   help='The path to the optimizer configuration') | ||||
|   parser.add_argument('--split_path'    ,   type=str,                   help='The split file path.') | ||||
|   #parser.add_argument('--arch_para_pure',   type=int,                   help='The architecture-parameter pure or not.') | ||||
|   parser.add_argument('--gumbel_tau_max',   type=float,                 help='The maximum tau for Gumbel.') | ||||
|   parser.add_argument('--gumbel_tau_min',   type=float,                 help='The minimum tau for Gumbel.') | ||||
|   parser.add_argument('--procedure'     ,   type=str,                   help='The procedure basic prefix.') | ||||
|   parser.add_argument('--FLOP_ratio'    ,   type=float,                 help='The expected FLOP ratio.') | ||||
|   parser.add_argument('--FLOP_weight'   ,   type=float,                 help='The loss weight for FLOP.') | ||||
|   parser.add_argument('--FLOP_tolerant' ,   type=float,                 help='The tolerant range for FLOP.') | ||||
|   # ablation studies | ||||
|   parser.add_argument('--ablation_num_select', type=int,                help='The number of randomly selected channels.') | ||||
|   add_shared_args( parser ) | ||||
|   # Optimization options | ||||
|   parser.add_argument('--batch_size'    ,   type=int,   default=2,      help='Batch size for training.') | ||||
|   args = parser.parse_args() | ||||
|  | ||||
|   if args.rand_seed is None or args.rand_seed < 0: | ||||
|     args.rand_seed = random.randint(1, 100000) | ||||
|   assert args.save_dir is not None, 'save-path argument can not be None' | ||||
|   assert args.gumbel_tau_max is not None and args.gumbel_tau_min is not None | ||||
|   assert args.FLOP_tolerant is not None and args.FLOP_tolerant > 0, 'invalid FLOP_tolerant : {:}'.format(FLOP_tolerant) | ||||
|   #assert args.arch_para_pure is not None, 'arch_para_pure is not None: {:}'.format(args.arch_para_pure) | ||||
|   #args.arch_para_pure = bool(args.arch_para_pure) | ||||
|   return args | ||||
							
								
								
									
										31
									
								
								graph_dit/naswot/config_utils/search_single_args.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								graph_dit/naswot/config_utils/search_single_args.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| import os, sys, time, random, argparse | ||||
| from .share_args import add_shared_args | ||||
|  | ||||
|  | ||||
| def obtain_search_single_args(): | ||||
|   parser = argparse.ArgumentParser(description='Train a classification model on typical image classification datasets.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) | ||||
|   parser.add_argument('--resume'        ,   type=str,                   help='Resume path.') | ||||
|   parser.add_argument('--model_config'  ,   type=str,                   help='The path to the model configuration') | ||||
|   parser.add_argument('--optim_config'  ,   type=str,                   help='The path to the optimizer configuration') | ||||
|   parser.add_argument('--split_path'    ,   type=str,                   help='The split file path.') | ||||
|   parser.add_argument('--search_shape'  ,   type=str,                   help='The shape to be searched.') | ||||
|   #parser.add_argument('--arch_para_pure',   type=int,                   help='The architecture-parameter pure or not.') | ||||
|   parser.add_argument('--gumbel_tau_max',   type=float,                 help='The maximum tau for Gumbel.') | ||||
|   parser.add_argument('--gumbel_tau_min',   type=float,                 help='The minimum tau for Gumbel.') | ||||
|   parser.add_argument('--procedure'     ,   type=str,                   help='The procedure basic prefix.') | ||||
|   parser.add_argument('--FLOP_ratio'    ,   type=float,                 help='The expected FLOP ratio.') | ||||
|   parser.add_argument('--FLOP_weight'   ,   type=float,                 help='The loss weight for FLOP.') | ||||
|   parser.add_argument('--FLOP_tolerant' ,   type=float,                 help='The tolerant range for FLOP.') | ||||
|   add_shared_args( parser ) | ||||
|   # Optimization options | ||||
|   parser.add_argument('--batch_size'    ,   type=int,   default=2,      help='Batch size for training.') | ||||
|   args = parser.parse_args() | ||||
|  | ||||
|   if args.rand_seed is None or args.rand_seed < 0: | ||||
|     args.rand_seed = random.randint(1, 100000) | ||||
|   assert args.save_dir is not None, 'save-path argument can not be None' | ||||
|   assert args.gumbel_tau_max is not None and args.gumbel_tau_min is not None | ||||
|   assert args.FLOP_tolerant is not None and args.FLOP_tolerant > 0, 'invalid FLOP_tolerant : {:}'.format(FLOP_tolerant) | ||||
|   #assert args.arch_para_pure is not None, 'arch_para_pure is not None: {:}'.format(args.arch_para_pure) | ||||
|   #args.arch_para_pure = bool(args.arch_para_pure) | ||||
|   return args | ||||
							
								
								
									
										17
									
								
								graph_dit/naswot/config_utils/share_args.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								graph_dit/naswot/config_utils/share_args.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| import os, sys, time, random, argparse | ||||
|  | ||||
| def add_shared_args( parser ): | ||||
|   # Data Generation | ||||
|   parser.add_argument('--dataset',          type=str,                   help='The dataset name.') | ||||
|   parser.add_argument('--data_path',        type=str,                   help='The dataset name.') | ||||
|   parser.add_argument('--cutout_length',    type=int,                   help='The cutout length, negative means not use.') | ||||
|   # Printing | ||||
|   parser.add_argument('--print_freq',       type=int,   default=100,    help='print frequency (default: 200)') | ||||
|   parser.add_argument('--print_freq_eval',  type=int,   default=100,    help='print frequency (default: 200)') | ||||
|   # Checkpoints | ||||
|   parser.add_argument('--eval_frequency',   type=int,   default=1,      help='evaluation frequency (default: 200)') | ||||
|   parser.add_argument('--save_dir',         type=str,                   help='Folder to save checkpoints and log.') | ||||
|   # Acceleration | ||||
|   parser.add_argument('--workers',          type=int,   default=8,      help='number of data loading workers (default: 8)') | ||||
|   # Random Seed | ||||
|   parser.add_argument('--rand_seed',        type=int,   default=-1,     help='manual seed') | ||||
							
								
								
									
										129
									
								
								graph_dit/naswot/datasets/DownsampledImageNet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								graph_dit/naswot/datasets/DownsampledImageNet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| import os, sys, hashlib, torch | ||||
| import numpy as np | ||||
| from PIL import Image | ||||
| import torch.utils.data as data | ||||
| if sys.version_info[0] == 2: | ||||
|   import cPickle as pickle | ||||
| else: | ||||
|   import pickle | ||||
|  | ||||
|  | ||||
| def calculate_md5(fpath, chunk_size=1024 * 1024): | ||||
|   md5 = hashlib.md5() | ||||
|   with open(fpath, 'rb') as f: | ||||
|     for chunk in iter(lambda: f.read(chunk_size), b''): | ||||
|       md5.update(chunk) | ||||
|   return md5.hexdigest() | ||||
|  | ||||
|  | ||||
| def check_md5(fpath, md5, **kwargs): | ||||
|   return md5 == calculate_md5(fpath, **kwargs) | ||||
|  | ||||
|  | ||||
| def check_integrity(fpath, md5=None): | ||||
|   if not os.path.isfile(fpath): return False | ||||
|   if md5 is None: return True | ||||
|   else          : return check_md5(fpath, md5) | ||||
|  | ||||
|  | ||||
| class ImageNet16(data.Dataset): | ||||
|   # http://image-net.org/download-images | ||||
|   # A Downsampled Variant of ImageNet as an Alternative to the CIFAR datasets | ||||
|   # https://arxiv.org/pdf/1707.08819.pdf | ||||
|    | ||||
|   train_list = [ | ||||
|         ['train_data_batch_1', '27846dcaa50de8e21a7d1a35f30f0e91'], | ||||
|         ['train_data_batch_2', 'c7254a054e0e795c69120a5727050e3f'], | ||||
|         ['train_data_batch_3', '4333d3df2e5ffb114b05d2ffc19b1e87'], | ||||
|         ['train_data_batch_4', '1620cdf193304f4a92677b695d70d10f'], | ||||
|         ['train_data_batch_5', '348b3c2fdbb3940c4e9e834affd3b18d'], | ||||
|         ['train_data_batch_6', '6e765307c242a1b3d7d5ef9139b48945'], | ||||
|         ['train_data_batch_7', '564926d8cbf8fc4818ba23d2faac7564'], | ||||
|         ['train_data_batch_8', 'f4755871f718ccb653440b9dd0ebac66'], | ||||
|         ['train_data_batch_9', 'bb6dd660c38c58552125b1a92f86b5d4'], | ||||
|         ['train_data_batch_10','8f03f34ac4b42271a294f91bf480f29b'], | ||||
|     ] | ||||
|   valid_list = [ | ||||
|         ['val_data', '3410e3017fdaefba8d5073aaa65e4bd6'], | ||||
|     ] | ||||
|  | ||||
|   def __init__(self, root, train, transform, use_num_of_class_only=None): | ||||
|     self.root      = root | ||||
|     self.transform = transform | ||||
|     self.train     = train  # training set or valid set | ||||
|     if not self._check_integrity(): raise RuntimeError('Dataset not found or corrupted.') | ||||
|  | ||||
|     if self.train: downloaded_list = self.train_list | ||||
|     else         : downloaded_list = self.valid_list | ||||
|     self.data    = [] | ||||
|     self.targets = [] | ||||
|    | ||||
|     # now load the picked numpy arrays | ||||
|     for i, (file_name, checksum) in enumerate(downloaded_list): | ||||
|       file_path = os.path.join(self.root, file_name) | ||||
|       #print ('Load {:}/{:02d}-th : {:}'.format(i, len(downloaded_list), file_path)) | ||||
|       with open(file_path, 'rb') as f: | ||||
|         if sys.version_info[0] == 2: | ||||
|           entry = pickle.load(f) | ||||
|         else: | ||||
|           entry = pickle.load(f, encoding='latin1') | ||||
|         self.data.append(entry['data']) | ||||
|         self.targets.extend(entry['labels']) | ||||
|     self.data = np.vstack(self.data).reshape(-1, 3, 16, 16) | ||||
|     self.data = self.data.transpose((0, 2, 3, 1))  # convert to HWC | ||||
|     if use_num_of_class_only is not None: | ||||
|       assert isinstance(use_num_of_class_only, int) and use_num_of_class_only > 0 and use_num_of_class_only < 1000, 'invalid use_num_of_class_only : {:}'.format(use_num_of_class_only) | ||||
|       new_data, new_targets = [], [] | ||||
|       for I, L in zip(self.data, self.targets): | ||||
|         if 1 <= L <= use_num_of_class_only: | ||||
|           new_data.append( I ) | ||||
|           new_targets.append( L ) | ||||
|       self.data    = new_data | ||||
|       self.targets = new_targets | ||||
|     #    self.mean.append(entry['mean']) | ||||
|     #self.mean = np.vstack(self.mean).reshape(-1, 3, 16, 16) | ||||
|     #self.mean = np.mean(np.mean(np.mean(self.mean, axis=0), axis=1), axis=1) | ||||
|     #print ('Mean : {:}'.format(self.mean)) | ||||
|     #temp      = self.data - np.reshape(self.mean, (1, 1, 1, 3)) | ||||
|     #std_data  = np.std(temp, axis=0) | ||||
|     #std_data  = np.mean(np.mean(std_data, axis=0), axis=0) | ||||
|     #print ('Std  : {:}'.format(std_data)) | ||||
|  | ||||
|   def __getitem__(self, index): | ||||
|     img, target = self.data[index], self.targets[index] - 1 | ||||
|  | ||||
|     img = Image.fromarray(img) | ||||
|  | ||||
|     if self.transform is not None: | ||||
|       img = self.transform(img) | ||||
|  | ||||
|     return img, target | ||||
|  | ||||
|   def __len__(self): | ||||
|     return len(self.data) | ||||
|  | ||||
|   def _check_integrity(self): | ||||
|     root = self.root | ||||
|     for fentry in (self.train_list + self.valid_list): | ||||
|       filename, md5 = fentry[0], fentry[1] | ||||
|       fpath = os.path.join(root, filename) | ||||
|       if not check_integrity(fpath, md5): | ||||
|         return False | ||||
|     return True | ||||
|  | ||||
| # | ||||
| if __name__ == '__main__': | ||||
|   train = ImageNet16('/data02/dongxuanyi/.torch/cifar.python/ImageNet16', True , None)  | ||||
|   valid = ImageNet16('/data02/dongxuanyi/.torch/cifar.python/ImageNet16', False, None)  | ||||
|  | ||||
|   print ( len(train) ) | ||||
|   print ( len(valid) ) | ||||
|   image, label = train[111] | ||||
|   trainX = ImageNet16('/data02/dongxuanyi/.torch/cifar.python/ImageNet16', True , None, 200) | ||||
|   validX = ImageNet16('/data02/dongxuanyi/.torch/cifar.python/ImageNet16', False , None, 200) | ||||
|   print ( len(trainX) ) | ||||
|   print ( len(validX) ) | ||||
|   #import pdb; pdb.set_trace() | ||||
							
								
								
									
										191
									
								
								graph_dit/naswot/datasets/LandmarkDataset.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								graph_dit/naswot/datasets/LandmarkDataset.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,191 @@ | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # All rights reserved. | ||||
| # | ||||
| # This source code is licensed under the license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
| # | ||||
| from os import path as osp | ||||
| from copy import deepcopy as copy | ||||
| from tqdm import tqdm | ||||
| import warnings, time, random, numpy as np | ||||
|  | ||||
| from pts_utils import generate_label_map | ||||
| from xvision import denormalize_points | ||||
| from xvision import identity2affine, solve2theta, affine2image | ||||
| from .dataset_utils import pil_loader | ||||
| from .landmark_utils import PointMeta2V | ||||
| from .augmentation_utils import CutOut | ||||
| import torch | ||||
| import torch.utils.data as data | ||||
|  | ||||
|  | ||||
| class LandmarkDataset(data.Dataset): | ||||
|  | ||||
|   def __init__(self, transform, sigma, downsample, heatmap_type, shape, use_gray, mean_file, data_indicator, cache_images=None): | ||||
|  | ||||
|     self.transform    = transform | ||||
|     self.sigma        = sigma | ||||
|     self.downsample   = downsample | ||||
|     self.heatmap_type = heatmap_type | ||||
|     self.dataset_name = data_indicator | ||||
|     self.shape        = shape # [H,W] | ||||
|     self.use_gray     = use_gray | ||||
|     assert transform is not None, 'transform : {:}'.format(transform) | ||||
|     self.mean_file    = mean_file | ||||
|     if mean_file is None: | ||||
|       self.mean_data  = None | ||||
|       warnings.warn('LandmarkDataset initialized with mean_data = None') | ||||
|     else: | ||||
|       assert osp.isfile(mean_file), '{:} is not a file.'.format(mean_file) | ||||
|       self.mean_data  = torch.load(mean_file) | ||||
|     self.reset() | ||||
|     self.cutout       = None | ||||
|     self.cache_images = cache_images | ||||
|     print ('The general dataset initialization done : {:}'.format(self)) | ||||
|     warnings.simplefilter( 'once' ) | ||||
|  | ||||
|  | ||||
|   def __repr__(self): | ||||
|     return ('{name}(point-num={NUM_PTS}, shape={shape}, sigma={sigma}, heatmap_type={heatmap_type}, length={length}, cutout={cutout}, dataset={dataset_name}, mean={mean_file})'.format(name=self.__class__.__name__, **self.__dict__)) | ||||
|  | ||||
|  | ||||
|   def set_cutout(self, length): | ||||
|     if length is not None and length >= 1: | ||||
|       self.cutout = CutOut( int(length) ) | ||||
|     else: self.cutout = None | ||||
|  | ||||
|  | ||||
|   def reset(self, num_pts=-1, boxid='default', only_pts=False): | ||||
|     self.NUM_PTS = num_pts | ||||
|     if only_pts: return | ||||
|     self.length  = 0 | ||||
|     self.datas   = [] | ||||
|     self.labels  = [] | ||||
|     self.NormDistances = [] | ||||
|     self.BOXID = boxid | ||||
|     if self.mean_data is None: | ||||
|       self.mean_face = None | ||||
|     else: | ||||
|       self.mean_face = torch.Tensor(self.mean_data[boxid].copy().T) | ||||
|       assert (self.mean_face >= -1).all() and (self.mean_face <= 1).all(), 'mean-{:}-face : {:}'.format(boxid, self.mean_face) | ||||
|     #assert self.dataset_name is not None, 'The dataset name is None' | ||||
|  | ||||
|  | ||||
|   def __len__(self): | ||||
|     assert len(self.datas) == self.length, 'The length is not correct : {}'.format(self.length) | ||||
|     return self.length | ||||
|  | ||||
|  | ||||
|   def append(self, data, label, distance): | ||||
|     assert osp.isfile(data), 'The image path is not a file : {:}'.format(data) | ||||
|     self.datas.append( data )             ;  self.labels.append( label ) | ||||
|     self.NormDistances.append( distance ) | ||||
|     self.length = self.length + 1 | ||||
|  | ||||
|  | ||||
|   def load_list(self, file_lists, num_pts, boxindicator, normalizeL, reset): | ||||
|     if reset: self.reset(num_pts, boxindicator) | ||||
|     else    : assert self.NUM_PTS == num_pts and self.BOXID == boxindicator, 'The number of point is inconsistance : {:} vs {:}'.format(self.NUM_PTS, num_pts) | ||||
|     if isinstance(file_lists, str): file_lists = [file_lists] | ||||
|     samples = [] | ||||
|     for idx, file_path in enumerate(file_lists): | ||||
|       print (':::: load list {:}/{:} : {:}'.format(idx, len(file_lists), file_path)) | ||||
|       xdata = torch.load(file_path) | ||||
|       if isinstance(xdata, list)  : data = xdata          # image or video dataset list | ||||
|       elif isinstance(xdata, dict): data = xdata['datas'] # multi-view dataset list | ||||
|       else: raise ValueError('Invalid Type Error : {:}'.format( type(xdata) )) | ||||
|       samples = samples + data | ||||
|     # samples is a dict, where the key is the image-path and the value is the annotation | ||||
|     # each annotation is a dict, contains 'points' (3,num_pts), and various box | ||||
|     print ('GeneralDataset-V2 : {:} samples'.format(len(samples))) | ||||
|  | ||||
|     #for index, annotation in enumerate(samples): | ||||
|     for index in tqdm( range( len(samples) ) ): | ||||
|       annotation = samples[index] | ||||
|       image_path  = annotation['current_frame'] | ||||
|       points, box = annotation['points'], annotation['box-{:}'.format(boxindicator)] | ||||
|       label = PointMeta2V(self.NUM_PTS, points, box, image_path, self.dataset_name) | ||||
|       if normalizeL is None: normDistance = None | ||||
|       else                 : normDistance = annotation['normalizeL-{:}'.format(normalizeL)] | ||||
|       self.append(image_path, label, normDistance) | ||||
|  | ||||
|     assert len(self.datas) == self.length, 'The length and the data is not right {} vs {}'.format(self.length, len(self.datas)) | ||||
|     assert len(self.labels) == self.length, 'The length and the labels is not right {} vs {}'.format(self.length, len(self.labels)) | ||||
|     assert len(self.NormDistances) == self.length, 'The length and the NormDistances is not right {} vs {}'.format(self.length, len(self.NormDistance)) | ||||
|     print ('Load data done for LandmarkDataset, which has {:} images.'.format(self.length)) | ||||
|  | ||||
|  | ||||
|   def __getitem__(self, index): | ||||
|     assert index >= 0 and index < self.length, 'Invalid index : {:}'.format(index) | ||||
|     if self.cache_images is not None and self.datas[index] in self.cache_images: | ||||
|       image = self.cache_images[ self.datas[index] ].clone() | ||||
|     else: | ||||
|       image = pil_loader(self.datas[index], self.use_gray) | ||||
|     target = self.labels[index].copy() | ||||
|     return self._process_(image, target, index) | ||||
|  | ||||
|  | ||||
|   def _process_(self, image, target, index): | ||||
|  | ||||
|     # transform the image and points | ||||
|     image, target, theta = self.transform(image, target) | ||||
|     (C, H, W), (height, width) = image.size(), self.shape | ||||
|  | ||||
|     # obtain the visiable indicator vector | ||||
|     if target.is_none(): nopoints = True | ||||
|     else               : nopoints = False | ||||
|     if index == -1: __path = None | ||||
|     else          : __path = self.datas[index] | ||||
|     if isinstance(theta, list) or isinstance(theta, tuple): | ||||
|       affineImage, heatmaps, mask, norm_trans_points, THETA, transpose_theta = [], [], [], [], [], [] | ||||
|       for _theta in theta: | ||||
|         _affineImage, _heatmaps, _mask, _norm_trans_points, _theta, _transpose_theta \ | ||||
|           = self.__process_affine(image, target, _theta, nopoints, 'P[{:}]@{:}'.format(index, __path)) | ||||
|         affineImage.append(_affineImage) | ||||
|         heatmaps.append(_heatmaps) | ||||
|         mask.append(_mask) | ||||
|         norm_trans_points.append(_norm_trans_points) | ||||
|         THETA.append(_theta) | ||||
|         transpose_theta.append(_transpose_theta) | ||||
|       affineImage, heatmaps, mask, norm_trans_points, THETA, transpose_theta = \ | ||||
|           torch.stack(affineImage), torch.stack(heatmaps), torch.stack(mask), torch.stack(norm_trans_points), torch.stack(THETA), torch.stack(transpose_theta) | ||||
|     else: | ||||
|       affineImage, heatmaps, mask, norm_trans_points, THETA, transpose_theta = self.__process_affine(image, target, theta, nopoints, 'S[{:}]@{:}'.format(index, __path)) | ||||
|  | ||||
|     torch_index = torch.IntTensor([index]) | ||||
|     torch_nopoints = torch.ByteTensor( [ nopoints ] ) | ||||
|     torch_shape = torch.IntTensor([H,W]) | ||||
|  | ||||
|     return affineImage, heatmaps, mask, norm_trans_points, THETA, transpose_theta, torch_index, torch_nopoints, torch_shape | ||||
|  | ||||
|    | ||||
|   def __process_affine(self, image, target, theta, nopoints, aux_info=None): | ||||
|     image, target, theta = image.clone(), target.copy(), theta.clone() | ||||
|     (C, H, W), (height, width) = image.size(), self.shape | ||||
|     if nopoints: # do not have label | ||||
|       norm_trans_points = torch.zeros((3, self.NUM_PTS)) | ||||
|       heatmaps          = torch.zeros((self.NUM_PTS+1, height//self.downsample, width//self.downsample)) | ||||
|       mask              = torch.ones((self.NUM_PTS+1, 1, 1), dtype=torch.uint8) | ||||
|       transpose_theta   = identity2affine(False) | ||||
|     else: | ||||
|       norm_trans_points = apply_affine2point(target.get_points(), theta, (H,W)) | ||||
|       norm_trans_points = apply_boundary(norm_trans_points) | ||||
|       real_trans_points = norm_trans_points.clone() | ||||
|       real_trans_points[:2, :] = denormalize_points(self.shape, real_trans_points[:2,:]) | ||||
|       heatmaps, mask = generate_label_map(real_trans_points.numpy(), height//self.downsample, width//self.downsample, self.sigma, self.downsample, nopoints, self.heatmap_type) # H*W*C | ||||
|       heatmaps = torch.from_numpy(heatmaps.transpose((2, 0, 1))).type(torch.FloatTensor) | ||||
|       mask     = torch.from_numpy(mask.transpose((2, 0, 1))).type(torch.ByteTensor) | ||||
|       if self.mean_face is None: | ||||
|         #warnings.warn('In LandmarkDataset use identity2affine for transpose_theta because self.mean_face is None.') | ||||
|         transpose_theta = identity2affine(False) | ||||
|       else: | ||||
|         if torch.sum(norm_trans_points[2,:] == 1) < 3: | ||||
|           warnings.warn('In LandmarkDataset after transformation, no visiable point, using identity instead. Aux: {:}'.format(aux_info)) | ||||
|           transpose_theta = identity2affine(False) | ||||
|         else: | ||||
|           transpose_theta = solve2theta(norm_trans_points, self.mean_face.clone()) | ||||
|  | ||||
|     affineImage = affine2image(image, theta, self.shape) | ||||
|     if self.cutout is not None: affineImage = self.cutout( affineImage ) | ||||
|  | ||||
|     return affineImage, heatmaps, mask, norm_trans_points, theta, transpose_theta | ||||
							
								
								
									
										46
									
								
								graph_dit/naswot/datasets/SearchDatasetWrap.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								graph_dit/naswot/datasets/SearchDatasetWrap.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| import torch, copy, random | ||||
| import torch.utils.data as data | ||||
|  | ||||
|  | ||||
| class SearchDataset(data.Dataset): | ||||
|  | ||||
|   def __init__(self, name, data, train_split, valid_split, check=True): | ||||
|     self.datasetname = name | ||||
|     if isinstance(data, (list, tuple)): # new type of SearchDataset | ||||
|       assert len(data) == 2, 'invalid length: {:}'.format( len(data) ) | ||||
|       self.train_data  = data[0] | ||||
|       self.valid_data  = data[1] | ||||
|       self.train_split = train_split.copy() | ||||
|       self.valid_split = valid_split.copy() | ||||
|       self.mode_str    = 'V2' # new mode  | ||||
|     else: | ||||
|       self.mode_str    = 'V1' # old mode  | ||||
|       self.data        = data | ||||
|       self.train_split = train_split.copy() | ||||
|       self.valid_split = valid_split.copy() | ||||
|       if check: | ||||
|         intersection = set(train_split).intersection(set(valid_split)) | ||||
|         assert len(intersection) == 0, 'the splitted train and validation sets should have no intersection' | ||||
|     self.length      = len(self.train_split) | ||||
|  | ||||
|   def __repr__(self): | ||||
|     return ('{name}(name={datasetname}, train={tr_L}, valid={val_L}, version={ver})'.format(name=self.__class__.__name__, datasetname=self.datasetname, tr_L=len(self.train_split), val_L=len(self.valid_split), ver=self.mode_str)) | ||||
|  | ||||
|   def __len__(self): | ||||
|     return self.length | ||||
|  | ||||
|   def __getitem__(self, index): | ||||
|     assert index >= 0 and index < self.length, 'invalid index = {:}'.format(index) | ||||
|     train_index = self.train_split[index] | ||||
|     valid_index = random.choice( self.valid_split ) | ||||
|     if self.mode_str == 'V1': | ||||
|       train_image, train_label = self.data[train_index] | ||||
|       valid_image, valid_label = self.data[valid_index] | ||||
|     elif self.mode_str == 'V2': | ||||
|       train_image, train_label = self.train_data[train_index] | ||||
|       valid_image, valid_label = self.valid_data[valid_index] | ||||
|     else: raise ValueError('invalid mode : {:}'.format(self.mode_str)) | ||||
|     return train_image, train_label, valid_image, valid_label | ||||
							
								
								
									
										6
									
								
								graph_dit/naswot/datasets/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								graph_dit/naswot/datasets/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| from .get_dataset_with_transform import get_datasets, get_nas_search_loaders | ||||
| from .SearchDatasetWrap import SearchDataset | ||||
| from .data import get_data | ||||
							
								
								
									
										69
									
								
								graph_dit/naswot/datasets/data.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								graph_dit/naswot/datasets/data.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| from datasets import get_datasets | ||||
| from config_utils import load_config | ||||
| import torch | ||||
| import torchvision | ||||
|  | ||||
| class AddGaussianNoise(object): | ||||
|     def __init__(self, mean=0., std=0.001): | ||||
|         self.std = std | ||||
|         self.mean = mean | ||||
|                                      | ||||
|     def __call__(self, tensor): | ||||
|         return tensor + torch.randn(tensor.size()) * self.std + self.mean | ||||
|                                                      | ||||
|     def __repr__(self): | ||||
|         return self.__class__.__name__ + '(mean={0}, std={1})'.format(self.mean, self.std) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class RepeatSampler(torch.utils.data.sampler.Sampler): | ||||
|     def __init__(self, samp, repeat): | ||||
|         self.samp = samp | ||||
|         self.repeat = repeat | ||||
|     def __iter__(self): | ||||
|         for i in self.samp: | ||||
|             for j in range(self.repeat): | ||||
|                 yield i | ||||
|     def __len__(self): | ||||
|         return self.repeat*len(self.samp) | ||||
|  | ||||
|  | ||||
| def get_data(dataset, data_loc, trainval, batch_size, augtype, repeat, args, pin_memory=True): | ||||
|     train_data, valid_data, xshape, class_num = get_datasets(dataset, data_loc, cutout=0) | ||||
|     if augtype == 'gaussnoise': | ||||
|         train_data.transform.transforms = train_data.transform.transforms[2:] | ||||
|         train_data.transform.transforms.append(AddGaussianNoise(std=args.sigma)) | ||||
|     elif augtype == 'cutout': | ||||
|         train_data.transform.transforms = train_data.transform.transforms[2:] | ||||
|         train_data.transform.transforms.append(torchvision.transforms.RandomErasing(p=0.9, scale=(0.02, 0.04))) | ||||
|     elif augtype == 'none': | ||||
|         train_data.transform.transforms = train_data.transform.transforms[2:] | ||||
|      | ||||
|     if dataset == 'cifar10': | ||||
|         acc_type = 'ori-test' | ||||
|         val_acc_type = 'x-valid' | ||||
|      | ||||
|     else: | ||||
|         acc_type = 'x-test' | ||||
|         val_acc_type = 'x-valid' | ||||
|      | ||||
|     if trainval and 'cifar10' in dataset: | ||||
|         cifar_split = load_config('config_utils/cifar-split.txt', None, None) | ||||
|         train_split, valid_split = cifar_split.train, cifar_split.valid | ||||
|         if repeat > 0: | ||||
|             train_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, | ||||
|                                                        num_workers=0, pin_memory=pin_memory, sampler= RepeatSampler(torch.utils.data.sampler.SubsetRandomSampler(train_split), repeat)) | ||||
|         else: | ||||
|             train_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, | ||||
|                                                        num_workers=0, pin_memory=pin_memory, sampler= torch.utils.data.sampler.SubsetRandomSampler(train_split)) | ||||
|          | ||||
|      | ||||
|     else: | ||||
|         if repeat > 0: | ||||
|             train_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, #shuffle=True, | ||||
|                                                        num_workers=0, pin_memory=pin_memory, sampler= RepeatSampler(torch.utils.data.sampler.SubsetRandomSampler(range(len(train_data))), repeat)) | ||||
|         else: | ||||
|             train_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, shuffle=True, | ||||
|                                                        num_workers=0, pin_memory=pin_memory) | ||||
|     return train_loader | ||||
							
								
								
									
										255
									
								
								graph_dit/naswot/datasets/get_dataset_with_transform.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								graph_dit/naswot/datasets/get_dataset_with_transform.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,255 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| import os, sys, torch | ||||
| import os.path as osp | ||||
| import numpy as np | ||||
| import torchvision.datasets as dset | ||||
| import torchvision.transforms as transforms | ||||
| from copy import deepcopy | ||||
| from PIL import Image | ||||
|  | ||||
| from .DownsampledImageNet import ImageNet16 | ||||
| from .SearchDatasetWrap import SearchDataset | ||||
| from config_utils import load_config | ||||
|  | ||||
|  | ||||
| Dataset2Class = {'cifar10' : 10, | ||||
|                  'cifar100': 100, | ||||
|                  'fake':10, | ||||
|                  'imagenet-1k-s':1000, | ||||
|                  'imagenette2' : 10, | ||||
|                  'imagenet-1k' : 1000, | ||||
|                  'ImageNet16'  : 1000, | ||||
|                  'ImageNet16-150': 150, | ||||
|                  'ImageNet16-120': 120, | ||||
|                  'ImageNet16-200': 200} | ||||
|  | ||||
|  | ||||
| class CUTOUT(object): | ||||
|  | ||||
|   def __init__(self, length): | ||||
|     self.length = length | ||||
|  | ||||
|   def __repr__(self): | ||||
|     return ('{name}(length={length})'.format(name=self.__class__.__name__, **self.__dict__)) | ||||
|  | ||||
|   def __call__(self, img): | ||||
|     h, w = img.size(1), img.size(2) | ||||
|     mask = np.ones((h, w), np.float32) | ||||
|     y = np.random.randint(h) | ||||
|     x = np.random.randint(w) | ||||
|  | ||||
|     y1 = np.clip(y - self.length // 2, 0, h) | ||||
|     y2 = np.clip(y + self.length // 2, 0, h) | ||||
|     x1 = np.clip(x - self.length // 2, 0, w) | ||||
|     x2 = np.clip(x + self.length // 2, 0, w) | ||||
|  | ||||
|     mask[y1: y2, x1: x2] = 0. | ||||
|     mask = torch.from_numpy(mask) | ||||
|     mask = mask.expand_as(img) | ||||
|     img *= mask | ||||
|     return img | ||||
|  | ||||
|  | ||||
| imagenet_pca = { | ||||
|     'eigval': np.asarray([0.2175, 0.0188, 0.0045]), | ||||
|     'eigvec': np.asarray([ | ||||
|         [-0.5675, 0.7192, 0.4009], | ||||
|         [-0.5808, -0.0045, -0.8140], | ||||
|         [-0.5836, -0.6948, 0.4203], | ||||
|     ]) | ||||
| } | ||||
|  | ||||
|  | ||||
| class Lighting(object): | ||||
|   def __init__(self, alphastd, | ||||
|          eigval=imagenet_pca['eigval'], | ||||
|          eigvec=imagenet_pca['eigvec']): | ||||
|     self.alphastd = alphastd | ||||
|     assert eigval.shape == (3,) | ||||
|     assert eigvec.shape == (3, 3) | ||||
|     self.eigval = eigval | ||||
|     self.eigvec = eigvec | ||||
|  | ||||
|   def __call__(self, img): | ||||
|     if self.alphastd == 0.: | ||||
|       return img | ||||
|     rnd = np.random.randn(3) * self.alphastd | ||||
|     rnd = rnd.astype('float32') | ||||
|     v = rnd | ||||
|     old_dtype = np.asarray(img).dtype | ||||
|     v = v * self.eigval | ||||
|     v = v.reshape((3, 1)) | ||||
|     inc = np.dot(self.eigvec, v).reshape((3,)) | ||||
|     img = np.add(img, inc) | ||||
|     if old_dtype == np.uint8: | ||||
|       img = np.clip(img, 0, 255) | ||||
|     img = Image.fromarray(img.astype(old_dtype), 'RGB') | ||||
|     return img | ||||
|  | ||||
|   def __repr__(self): | ||||
|     return self.__class__.__name__ + '()' | ||||
|  | ||||
|  | ||||
| def get_datasets(name, root, cutout): | ||||
|  | ||||
|   if name == 'cifar10': | ||||
|     mean = [x / 255 for x in [125.3, 123.0, 113.9]] | ||||
|     std  = [x / 255 for x in [63.0, 62.1, 66.7]] | ||||
|   elif name == 'cifar100': | ||||
|     mean = [x / 255 for x in [129.3, 124.1, 112.4]] | ||||
|     std  = [x / 255 for x in [68.2, 65.4, 70.4]] | ||||
|   elif name == 'fake': | ||||
|     mean = [x / 255 for x in [129.3, 124.1, 112.4]] | ||||
|     std  = [x / 255 for x in [68.2, 65.4, 70.4]] | ||||
|   elif name.startswith('imagenet-1k'): | ||||
|     mean, std = [0.485, 0.456, 0.406], [0.229, 0.224, 0.225] | ||||
|   elif name.startswith('imagenette'): | ||||
|     mean, std = [0.485, 0.456, 0.406], [0.229, 0.224, 0.225] | ||||
|   elif name.startswith('ImageNet16'): | ||||
|     mean = [x / 255 for x in [122.68, 116.66, 104.01]] | ||||
|     std  = [x / 255 for x in [63.22,  61.26 , 65.09]] | ||||
|   else: | ||||
|     raise TypeError("Unknow dataset : {:}".format(name)) | ||||
|  | ||||
|   # Data Argumentation | ||||
|   if name == 'cifar10' or name == 'cifar100': | ||||
|     lists = [transforms.RandomHorizontalFlip(), transforms.RandomCrop(32, padding=4), transforms.ToTensor(), transforms.Normalize(mean, std)] | ||||
|     if cutout > 0 : lists += [CUTOUT(cutout)] | ||||
|     train_transform = transforms.Compose(lists) | ||||
|     test_transform  = transforms.Compose([transforms.ToTensor(), transforms.Normalize(mean, std)]) | ||||
|     xshape = (1, 3, 32, 32) | ||||
|   elif name == 'fake': | ||||
|     lists = [transforms.RandomHorizontalFlip(), transforms.RandomCrop(32, padding=4), transforms.ToTensor(), transforms.Normalize(mean, std)] | ||||
|     if cutout > 0 : lists += [CUTOUT(cutout)] | ||||
|     train_transform = transforms.Compose(lists) | ||||
|     test_transform  = transforms.Compose([transforms.ToTensor(), transforms.Normalize(mean, std)]) | ||||
|     xshape = (1, 3, 32, 32) | ||||
|   elif name.startswith('ImageNet16'): | ||||
|     lists = [transforms.RandomHorizontalFlip(), transforms.RandomCrop(16, padding=2), transforms.ToTensor(), transforms.Normalize(mean, std)] | ||||
|     if cutout > 0 : lists += [CUTOUT(cutout)] | ||||
|     train_transform = transforms.Compose(lists) | ||||
|     test_transform  = transforms.Compose([transforms.ToTensor(), transforms.Normalize(mean, std)]) | ||||
|     xshape = (1, 3, 16, 16) | ||||
|   elif name == 'tiered': | ||||
|     lists = [transforms.RandomHorizontalFlip(), transforms.RandomCrop(80, padding=4), transforms.ToTensor(), transforms.Normalize(mean, std)] | ||||
|     if cutout > 0 : lists += [CUTOUT(cutout)] | ||||
|     train_transform = transforms.Compose(lists) | ||||
|     test_transform  = transforms.Compose([transforms.CenterCrop(80), transforms.ToTensor(), transforms.Normalize(mean, std)]) | ||||
|     xshape = (1, 3, 32, 32) | ||||
|   elif name.startswith('imagenette'): | ||||
|     normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) | ||||
|     xlists = [] | ||||
|     xlists.append( transforms.ToTensor() ) | ||||
|     xlists.append( normalize ) | ||||
|     #train_transform = transforms.Compose(xlists) | ||||
|     train_transform  = transforms.Compose([normalize, normalize, transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), normalize]) | ||||
|     test_transform  = transforms.Compose([normalize, normalize, transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), normalize]) | ||||
|     xshape = (1, 3, 224, 224) | ||||
|   elif name.startswith('imagenet-1k'): | ||||
|     normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) | ||||
|     if name == 'imagenet-1k': | ||||
|       xlists    = [transforms.RandomResizedCrop(224)] | ||||
|       xlists.append( | ||||
|         transforms.ColorJitter( | ||||
|         brightness=0.4, | ||||
|         contrast=0.4, | ||||
|         saturation=0.4, | ||||
|         hue=0.2)) | ||||
|       xlists.append( Lighting(0.1)) | ||||
|     elif name == 'imagenet-1k-s': | ||||
|       xlists    = [transforms.RandomResizedCrop(224, scale=(0.2, 1.0))] | ||||
|     else: raise ValueError('invalid name : {:}'.format(name)) | ||||
|     xlists.append( transforms.RandomHorizontalFlip(p=0.5) ) | ||||
|     xlists.append( transforms.ToTensor() ) | ||||
|     xlists.append( normalize ) | ||||
|     train_transform = transforms.Compose(xlists) | ||||
|     test_transform  = transforms.Compose([transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), normalize]) | ||||
|     xshape = (1, 3, 224, 224) | ||||
|   else: | ||||
|     raise TypeError("Unknow dataset : {:}".format(name)) | ||||
|  | ||||
|   if name == 'cifar10': | ||||
|     train_data = dset.CIFAR10 (root, train=True , transform=train_transform, download=True) | ||||
|     test_data  = dset.CIFAR10 (root, train=False, transform=test_transform , download=True) | ||||
|     assert len(train_data) == 50000 and len(test_data) == 10000 | ||||
|   elif name == 'cifar100': | ||||
|     train_data = dset.CIFAR100(root, train=True , transform=train_transform, download=True) | ||||
|     test_data  = dset.CIFAR100(root, train=False, transform=test_transform , download=True) | ||||
|     assert len(train_data) == 50000 and len(test_data) == 10000 | ||||
|   elif name == 'fake': | ||||
|     train_data = dset.FakeData(size=50000, image_size=(3, 32, 32), transform=train_transform) | ||||
|     test_data = dset.FakeData(size=10000, image_size=(3, 32, 32), transform=test_transform) | ||||
|   elif name.startswith('imagenette2'): | ||||
|     train_data = dset.ImageFolder(osp.join(root, 'train'), train_transform) | ||||
|     test_data  = dset.ImageFolder(osp.join(root, 'val'),   test_transform) | ||||
|   elif name.startswith('imagenet-1k'): | ||||
|     train_data = dset.ImageFolder(osp.join(root, 'train'), train_transform) | ||||
|     test_data  = dset.ImageFolder(osp.join(root, 'val'),   test_transform) | ||||
|     assert len(train_data) == 1281167 and len(test_data) == 50000, 'invalid number of images : {:} & {:} vs {:} & {:}'.format(len(train_data), len(test_data), 1281167, 50000) | ||||
|   elif name == 'ImageNet16': | ||||
|     train_data = ImageNet16(root, True , train_transform) | ||||
|     test_data  = ImageNet16(root, False, test_transform) | ||||
|     assert len(train_data) == 1281167 and len(test_data) == 50000 | ||||
|   elif name == 'ImageNet16-120': | ||||
|     train_data = ImageNet16(root, True , train_transform, 120) | ||||
|     test_data  = ImageNet16(root, False, test_transform , 120) | ||||
|     assert len(train_data) == 151700 and len(test_data) == 6000 | ||||
|   elif name == 'ImageNet16-150': | ||||
|     train_data = ImageNet16(root, True , train_transform, 150) | ||||
|     test_data  = ImageNet16(root, False, test_transform , 150) | ||||
|     assert len(train_data) == 190272 and len(test_data) == 7500 | ||||
|   elif name == 'ImageNet16-200': | ||||
|     train_data = ImageNet16(root, True , train_transform, 200) | ||||
|     test_data  = ImageNet16(root, False, test_transform , 200) | ||||
|     assert len(train_data) == 254775 and len(test_data) == 10000 | ||||
|   else: raise TypeError("Unknow dataset : {:}".format(name)) | ||||
|    | ||||
|   class_num = Dataset2Class[name] | ||||
|   return train_data, test_data, xshape, class_num | ||||
|  | ||||
|  | ||||
| def get_nas_search_loaders(train_data, valid_data, dataset, config_root, batch_size, workers): | ||||
|   if isinstance(batch_size, (list,tuple)): | ||||
|     batch, test_batch = batch_size | ||||
|   else: | ||||
|     batch, test_batch = batch_size, batch_size | ||||
|   if dataset == 'cifar10': | ||||
|     #split_Fpath = 'configs/nas-benchmark/cifar-split.txt' | ||||
|     cifar_split = load_config('{:}/cifar-split.txt'.format(config_root), None, None) | ||||
|     train_split, valid_split = cifar_split.train, cifar_split.valid # search over the proposed training and validation set | ||||
|     #logger.log('Load split file from {:}'.format(split_Fpath))      # they are two disjoint groups in the original CIFAR-10 training set | ||||
|     # To split data | ||||
|     xvalid_data  = deepcopy(train_data) | ||||
|     if hasattr(xvalid_data, 'transforms'): # to avoid a print issue | ||||
|       xvalid_data.transforms = valid_data.transform | ||||
|     xvalid_data.transform  = deepcopy( valid_data.transform ) | ||||
|     search_data   = SearchDataset(dataset, train_data, train_split, valid_split) | ||||
|     # data loader | ||||
|     search_loader = torch.utils.data.DataLoader(search_data, batch_size=batch, shuffle=True , num_workers=workers, pin_memory=True) | ||||
|     train_loader  = torch.utils.data.DataLoader(train_data , batch_size=batch, sampler=torch.utils.data.sampler.SubsetRandomSampler(train_split), num_workers=workers, pin_memory=True) | ||||
|     valid_loader  = torch.utils.data.DataLoader(xvalid_data, batch_size=test_batch, sampler=torch.utils.data.sampler.SubsetRandomSampler(valid_split), num_workers=workers, pin_memory=True) | ||||
|   elif dataset == 'cifar100': | ||||
|     cifar100_test_split = load_config('{:}/cifar100-test-split.txt'.format(config_root), None, None) | ||||
|     search_train_data = train_data | ||||
|     search_valid_data = deepcopy(valid_data) ; search_valid_data.transform = train_data.transform | ||||
|     search_data   = SearchDataset(dataset, [search_train_data,search_valid_data], list(range(len(search_train_data))), cifar100_test_split.xvalid) | ||||
|     search_loader = torch.utils.data.DataLoader(search_data, batch_size=batch, shuffle=True , num_workers=workers, pin_memory=True) | ||||
|     train_loader  = torch.utils.data.DataLoader(train_data , batch_size=batch, shuffle=True , num_workers=workers, pin_memory=True) | ||||
|     valid_loader  = torch.utils.data.DataLoader(valid_data , batch_size=test_batch, sampler=torch.utils.data.sampler.SubsetRandomSampler(cifar100_test_split.xvalid), num_workers=workers, pin_memory=True) | ||||
|   elif dataset == 'ImageNet16-120': | ||||
|     imagenet_test_split = load_config('{:}/imagenet-16-120-test-split.txt'.format(config_root), None, None) | ||||
|     search_train_data = train_data | ||||
|     search_valid_data = deepcopy(valid_data) ; search_valid_data.transform = train_data.transform | ||||
|     search_data   = SearchDataset(dataset, [search_train_data,search_valid_data], list(range(len(search_train_data))), imagenet_test_split.xvalid) | ||||
|     search_loader = torch.utils.data.DataLoader(search_data, batch_size=batch, shuffle=True , num_workers=workers, pin_memory=True) | ||||
|     train_loader  = torch.utils.data.DataLoader(train_data , batch_size=batch, shuffle=True , num_workers=workers, pin_memory=True) | ||||
|     valid_loader  = torch.utils.data.DataLoader(valid_data , batch_size=test_batch, sampler=torch.utils.data.sampler.SubsetRandomSampler(imagenet_test_split.xvalid), num_workers=workers, pin_memory=True) | ||||
|   else: | ||||
|     raise ValueError('invalid dataset : {:}'.format(dataset)) | ||||
|   return search_loader, train_loader, valid_loader | ||||
|  | ||||
| #if __name__ == '__main__': | ||||
| #  train_data, test_data, xshape, class_num = dataset = get_datasets('cifar10', '/data02/dongxuanyi/.torch/cifar.python/', -1) | ||||
| #  import pdb; pdb.set_trace() | ||||
							
								
								
									
										1
									
								
								graph_dit/naswot/datasets/landmark_utils/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								graph_dit/naswot/datasets/landmark_utils/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| from .point_meta import PointMeta2V, apply_affine2point, apply_boundary | ||||
							
								
								
									
										116
									
								
								graph_dit/naswot/datasets/landmark_utils/point_meta.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								graph_dit/naswot/datasets/landmark_utils/point_meta.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # All rights reserved. | ||||
| # | ||||
| # This source code is licensed under the license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
| # | ||||
| import copy, math, torch, numpy as np | ||||
| from xvision import normalize_points | ||||
| from xvision import denormalize_points | ||||
|  | ||||
|  | ||||
| class PointMeta(): | ||||
|   # points    : 3 x num_pts (x, y, oculusion) | ||||
|   # image_size: original [width, height] | ||||
|   def __init__(self, num_point, points, box, image_path, dataset_name): | ||||
|  | ||||
|     self.num_point = num_point | ||||
|     if box is not None: | ||||
|       assert (isinstance(box, tuple) or isinstance(box, list)) and len(box) == 4 | ||||
|       self.box = torch.Tensor(box) | ||||
|     else: self.box = None | ||||
|     if points is None: | ||||
|       self.points = points | ||||
|     else: | ||||
|       assert len(points.shape) == 2 and points.shape[0] == 3 and points.shape[1] == self.num_point, 'The shape of point is not right : {}'.format( points ) | ||||
|       self.points = torch.Tensor(points.copy()) | ||||
|     self.image_path = image_path | ||||
|     self.datasets = dataset_name | ||||
|  | ||||
|   def __repr__(self): | ||||
|     if self.box is None: boxstr = 'None' | ||||
|     else               : boxstr = 'box=[{:.1f}, {:.1f}, {:.1f}, {:.1f}]'.format(*self.box.tolist()) | ||||
|     return ('{name}(points={num_point}, '.format(name=self.__class__.__name__, **self.__dict__) + boxstr + ')') | ||||
|  | ||||
|   def get_box(self, return_diagonal=False): | ||||
|     if self.box is None: return None | ||||
|     if not return_diagonal: | ||||
|       return self.box.clone() | ||||
|     else: | ||||
|       W = (self.box[2]-self.box[0]).item() | ||||
|       H = (self.box[3]-self.box[1]).item() | ||||
|       return math.sqrt(H*H+W*W) | ||||
|  | ||||
|   def get_points(self, ignore_indicator=False): | ||||
|     if ignore_indicator: last = 2 | ||||
|     else               : last = 3 | ||||
|     if self.points is not None: return self.points.clone()[:last, :] | ||||
|     else                      : return torch.zeros((last, self.num_point)) | ||||
|  | ||||
|   def is_none(self): | ||||
|     #assert self.box is not None, 'The box should not be None' | ||||
|     return self.points is None | ||||
|     #if self.box is None: return True | ||||
|     #else               : return self.points is None | ||||
|  | ||||
|   def copy(self): | ||||
|     return copy.deepcopy(self) | ||||
|  | ||||
|   def visiable_pts_num(self): | ||||
|     with torch.no_grad(): | ||||
|       ans = self.points[2,:] > 0 | ||||
|       ans = torch.sum(ans) | ||||
|       ans = ans.item() | ||||
|     return ans | ||||
|    | ||||
|   def special_fun(self, indicator): | ||||
|     if indicator == '68to49': # For 300W or 300VW, convert the default 68 points to 49 points. | ||||
|       assert self.num_point == 68, 'num-point must be 68 vs. {:}'.format(self.num_point) | ||||
|       self.num_point = 49 | ||||
|       out = torch.ones((68), dtype=torch.uint8) | ||||
|       out[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,60,64]] = 0 | ||||
|       if self.points is not None: self.points = self.points.clone()[:, out] | ||||
|     else: | ||||
|       raise ValueError('Invalid indicator : {:}'.format( indicator )) | ||||
|  | ||||
|   def apply_horizontal_flip(self): | ||||
|     #self.points[0, :] = width - self.points[0, :] - 1 | ||||
|     # Mugsy spefic or Synthetic | ||||
|     if self.datasets.startswith('HandsyROT'): | ||||
|       ori = np.array(list(range(0, 42))) | ||||
|       pos = np.array(list(range(21,42)) + list(range(0,21))) | ||||
|       self.points[:, pos] = self.points[:, ori] | ||||
|     elif self.datasets.startswith('face68'): | ||||
|       ori = np.array(list(range(0, 68))) | ||||
|       pos = np.array([17,16,15,14,13,12,11,10, 9, 8,7,6,5,4,3,2,1, 27,26,25,24,23,22,21,20,19,18, 28,29,30,31, 36,35,34,33,32, 46,45,44,43,48,47, 40,39,38,37,42,41, 55,54,53,52,51,50,49,60,59,58,57,56,65,64,63,62,61,68,67,66])-1 | ||||
|       self.points[:, ori] = self.points[:, pos] | ||||
|     else: | ||||
|       raise ValueError('Does not support {:}'.format(self.datasets)) | ||||
|  | ||||
|  | ||||
|  | ||||
| # shape = (H,W) | ||||
| def apply_affine2point(points, theta, shape): | ||||
|   assert points.size(0) == 3, 'invalid points shape : {:}'.format(points.size()) | ||||
|   with torch.no_grad(): | ||||
|     ok_points = points[2,:] == 1 | ||||
|     assert torch.sum(ok_points).item() > 0, 'there is no visiable point' | ||||
|     points[:2,:] = normalize_points(shape, points[:2,:]) | ||||
|  | ||||
|     norm_trans_points = ok_points.unsqueeze(0).repeat(3, 1).float() | ||||
|  | ||||
|     trans_points, ___ = torch.gesv(points[:, ok_points], theta) | ||||
|  | ||||
|     norm_trans_points[:, ok_points] = trans_points | ||||
|      | ||||
|   return norm_trans_points | ||||
|  | ||||
|  | ||||
|  | ||||
| def apply_boundary(norm_trans_points): | ||||
|   with torch.no_grad(): | ||||
|     norm_trans_points = norm_trans_points.clone() | ||||
|     oks = torch.stack((norm_trans_points[0]>-1, norm_trans_points[0]<1, norm_trans_points[1]>-1, norm_trans_points[1]<1, norm_trans_points[2]>0)) | ||||
|     oks = torch.sum(oks, dim=0) == 5 | ||||
|     norm_trans_points[2, :] = oks | ||||
|   return norm_trans_points | ||||
							
								
								
									
										20
									
								
								graph_dit/naswot/datasets/test_utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								graph_dit/naswot/datasets/test_utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| import os | ||||
|  | ||||
|  | ||||
| def test_imagenet_data(imagenet): | ||||
|   total_length = len(imagenet) | ||||
|   assert total_length == 1281166 or total_length == 50000, 'The length of ImageNet is wrong : {}'.format(total_length) | ||||
|   map_id = {} | ||||
|   for index in range(total_length): | ||||
|     path, target = imagenet.imgs[index] | ||||
|     folder, image_name = os.path.split(path) | ||||
|     _, folder = os.path.split(folder) | ||||
|     if folder not in map_id: | ||||
|       map_id[folder] = target | ||||
|     else: | ||||
|       assert map_id[folder] == target, 'Class : {} is not {}'.format(folder, target) | ||||
|     assert image_name.find(folder) == 0, '{} is wrong.'.format(path) | ||||
|   print ('Check ImageNet Dataset OK') | ||||
							
								
								
									
										105
									
								
								graph_dit/naswot/models/CifarDenseNet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								graph_dit/naswot/models/CifarDenseNet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,105 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| import math, torch | ||||
| import torch.nn as nn | ||||
| import torch.nn.functional as F | ||||
| from .initialization import initialize_resnet | ||||
|  | ||||
|  | ||||
| class Bottleneck(nn.Module): | ||||
|   def __init__(self, nChannels, growthRate): | ||||
|     super(Bottleneck, self).__init__() | ||||
|     interChannels = 4*growthRate | ||||
|     self.bn1 = nn.BatchNorm2d(nChannels) | ||||
|     self.conv1 = nn.Conv2d(nChannels, interChannels, kernel_size=1, bias=False) | ||||
|     self.bn2 = nn.BatchNorm2d(interChannels) | ||||
|     self.conv2 = nn.Conv2d(interChannels, growthRate, kernel_size=3, padding=1, bias=False) | ||||
|  | ||||
|   def forward(self, x): | ||||
|     out = self.conv1(F.relu(self.bn1(x))) | ||||
|     out = self.conv2(F.relu(self.bn2(out))) | ||||
|     out = torch.cat((x, out), 1) | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class SingleLayer(nn.Module): | ||||
|   def __init__(self, nChannels, growthRate): | ||||
|     super(SingleLayer, self).__init__() | ||||
|     self.bn1 = nn.BatchNorm2d(nChannels) | ||||
|     self.conv1 = nn.Conv2d(nChannels, growthRate, kernel_size=3, padding=1, bias=False) | ||||
|  | ||||
|   def forward(self, x): | ||||
|     out = self.conv1(F.relu(self.bn1(x))) | ||||
|     out = torch.cat((x, out), 1) | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class Transition(nn.Module): | ||||
|   def __init__(self, nChannels, nOutChannels): | ||||
|     super(Transition, self).__init__() | ||||
|     self.bn1 = nn.BatchNorm2d(nChannels) | ||||
|     self.conv1 = nn.Conv2d(nChannels, nOutChannels, kernel_size=1, bias=False) | ||||
|  | ||||
|   def forward(self, x): | ||||
|     out = self.conv1(F.relu(self.bn1(x))) | ||||
|     out = F.avg_pool2d(out, 2) | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class DenseNet(nn.Module): | ||||
|   def __init__(self, growthRate, depth, reduction, nClasses, bottleneck): | ||||
|     super(DenseNet, self).__init__() | ||||
|  | ||||
|     if bottleneck:  nDenseBlocks = int( (depth-4) / 6 ) | ||||
|     else         :  nDenseBlocks = int( (depth-4) / 3 ) | ||||
|  | ||||
|     self.message = 'CifarDenseNet : block : {:}, depth : {:}, reduction : {:}, growth-rate = {:}, class = {:}'.format('bottleneck' if bottleneck else 'basic', depth, reduction, growthRate, nClasses) | ||||
|  | ||||
|     nChannels = 2*growthRate | ||||
|     self.conv1 = nn.Conv2d(3, nChannels, kernel_size=3, padding=1, bias=False) | ||||
|  | ||||
|     self.dense1 = self._make_dense(nChannels, growthRate, nDenseBlocks, bottleneck) | ||||
|     nChannels += nDenseBlocks*growthRate | ||||
|     nOutChannels = int(math.floor(nChannels*reduction)) | ||||
|     self.trans1 = Transition(nChannels, nOutChannels) | ||||
|  | ||||
|     nChannels = nOutChannels | ||||
|     self.dense2 = self._make_dense(nChannels, growthRate, nDenseBlocks, bottleneck) | ||||
|     nChannels += nDenseBlocks*growthRate | ||||
|     nOutChannels = int(math.floor(nChannels*reduction)) | ||||
|     self.trans2 = Transition(nChannels, nOutChannels) | ||||
|  | ||||
|     nChannels = nOutChannels | ||||
|     self.dense3 = self._make_dense(nChannels, growthRate, nDenseBlocks, bottleneck) | ||||
|     nChannels += nDenseBlocks*growthRate | ||||
|  | ||||
|     self.act = nn.Sequential( | ||||
|                   nn.BatchNorm2d(nChannels), nn.ReLU(inplace=True), | ||||
|                   nn.AvgPool2d(8)) | ||||
|     self.fc  = nn.Linear(nChannels, nClasses) | ||||
|  | ||||
|     self.apply(initialize_resnet) | ||||
|  | ||||
|   def get_message(self): | ||||
|     return self.message | ||||
|  | ||||
|   def _make_dense(self, nChannels, growthRate, nDenseBlocks, bottleneck): | ||||
|     layers = [] | ||||
|     for i in range(int(nDenseBlocks)): | ||||
|       if bottleneck: | ||||
|         layers.append(Bottleneck(nChannels, growthRate)) | ||||
|       else: | ||||
|         layers.append(SingleLayer(nChannels, growthRate)) | ||||
|       nChannels += growthRate | ||||
|     return nn.Sequential(*layers) | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     out = self.conv1( inputs ) | ||||
|     out = self.trans1(self.dense1(out)) | ||||
|     out = self.trans2(self.dense2(out)) | ||||
|     out = self.dense3(out) | ||||
|     features = self.act(out) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     out = self.fc(features) | ||||
|     return features, out | ||||
							
								
								
									
										157
									
								
								graph_dit/naswot/models/CifarResNet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								graph_dit/naswot/models/CifarResNet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | ||||
| import torch | ||||
| import torch.nn as nn | ||||
| import torch.nn.functional as F | ||||
| from .initialization import initialize_resnet | ||||
| from .SharedUtils    import additive_func | ||||
|  | ||||
|  | ||||
| class Downsample(nn.Module):   | ||||
|  | ||||
|   def __init__(self, nIn, nOut, stride): | ||||
|     super(Downsample, self).__init__()  | ||||
|     assert stride == 2 and nOut == 2*nIn, 'stride:{} IO:{},{}'.format(stride, nIn, nOut) | ||||
|     self.in_dim  = nIn | ||||
|     self.out_dim = nOut | ||||
|     self.avg  = nn.AvgPool2d(kernel_size=2, stride=2, padding=0)    | ||||
|     self.conv = nn.Conv2d(nIn, nOut, kernel_size=1, stride=1, padding=0, bias=False) | ||||
|  | ||||
|   def forward(self, x): | ||||
|     x   = self.avg(x) | ||||
|     out = self.conv(x) | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class ConvBNReLU(nn.Module): | ||||
|    | ||||
|   def __init__(self, nIn, nOut, kernel, stride, padding, bias, relu): | ||||
|     super(ConvBNReLU, self).__init__() | ||||
|     self.conv = nn.Conv2d(nIn, nOut, kernel_size=kernel, stride=stride, padding=padding, bias=bias) | ||||
|     self.bn   = nn.BatchNorm2d(nOut) | ||||
|     if relu: self.relu = nn.ReLU(inplace=True) | ||||
|     else   : self.relu = None | ||||
|     self.out_dim = nOut | ||||
|     self.num_conv = 1 | ||||
|  | ||||
|   def forward(self, x): | ||||
|     conv = self.conv( x ) | ||||
|     bn   = self.bn( conv ) | ||||
|     if self.relu: return self.relu( bn ) | ||||
|     else        : return bn | ||||
|  | ||||
|  | ||||
| class ResNetBasicblock(nn.Module): | ||||
|   expansion = 1 | ||||
|   def __init__(self, inplanes, planes, stride): | ||||
|     super(ResNetBasicblock, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     self.conv_a = ConvBNReLU(inplanes, planes, 3, stride, 1, False, True) | ||||
|     self.conv_b = ConvBNReLU(  planes, planes, 3,      1, 1, False, False) | ||||
|     if stride == 2: | ||||
|       self.downsample = Downsample(inplanes, planes, stride) | ||||
|     elif inplanes != planes: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes, 1, 1, 0, False, False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     self.out_dim = planes | ||||
|     self.num_conv = 2 | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|  | ||||
|     basicblock = self.conv_a(inputs) | ||||
|     basicblock = self.conv_b(basicblock) | ||||
|  | ||||
|     if self.downsample is not None: | ||||
|       residual = self.downsample(inputs) | ||||
|     else: | ||||
|       residual = inputs | ||||
|     out = additive_func(residual, basicblock) | ||||
|     return F.relu(out, inplace=True) | ||||
|  | ||||
|  | ||||
|  | ||||
| class ResNetBottleneck(nn.Module): | ||||
|   expansion = 4 | ||||
|   def __init__(self, inplanes, planes, stride): | ||||
|     super(ResNetBottleneck, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     self.conv_1x1 = ConvBNReLU(inplanes, planes, 1,      1, 0, False, True) | ||||
|     self.conv_3x3 = ConvBNReLU(  planes, planes, 3, stride, 1, False, True) | ||||
|     self.conv_1x4 = ConvBNReLU(planes, planes*self.expansion, 1, 1, 0, False, False) | ||||
|     if stride == 2: | ||||
|       self.downsample = Downsample(inplanes, planes*self.expansion, stride) | ||||
|     elif inplanes != planes*self.expansion: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes*self.expansion, 1, 1, 0, False, False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     self.out_dim = planes * self.expansion | ||||
|     self.num_conv = 3 | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|  | ||||
|     bottleneck = self.conv_1x1(inputs) | ||||
|     bottleneck = self.conv_3x3(bottleneck) | ||||
|     bottleneck = self.conv_1x4(bottleneck) | ||||
|  | ||||
|     if self.downsample is not None: | ||||
|       residual = self.downsample(inputs) | ||||
|     else: | ||||
|       residual = inputs | ||||
|     out = additive_func(residual, bottleneck) | ||||
|     return F.relu(out, inplace=True) | ||||
|  | ||||
|  | ||||
|  | ||||
| class CifarResNet(nn.Module): | ||||
|  | ||||
|   def __init__(self, block_name, depth, num_classes, zero_init_residual): | ||||
|     super(CifarResNet, self).__init__() | ||||
|  | ||||
|     #Model type specifies number of layers for CIFAR-10 and CIFAR-100 model | ||||
|     if block_name == 'ResNetBasicblock': | ||||
|       block = ResNetBasicblock | ||||
|       assert (depth - 2) % 6 == 0, 'depth should be one of 20, 32, 44, 56, 110' | ||||
|       layer_blocks = (depth - 2) // 6 | ||||
|     elif block_name == 'ResNetBottleneck': | ||||
|       block = ResNetBottleneck | ||||
|       assert (depth - 2) % 9 == 0, 'depth should be one of 164' | ||||
|       layer_blocks = (depth - 2) // 9 | ||||
|     else: | ||||
|       raise ValueError('invalid block : {:}'.format(block_name)) | ||||
|  | ||||
|     self.message     = 'CifarResNet : Block : {:}, Depth : {:}, Layers for each block : {:}'.format(block_name, depth, layer_blocks) | ||||
|     self.num_classes = num_classes | ||||
|     self.channels    = [16] | ||||
|     self.layers      = nn.ModuleList( [ ConvBNReLU(3, 16, 3, 1, 1, False, True) ] ) | ||||
|     for stage in range(3): | ||||
|       for iL in range(layer_blocks): | ||||
|         iC     = self.channels[-1] | ||||
|         planes = 16 * (2**stage) | ||||
|         stride = 2 if stage > 0 and iL == 0 else 1 | ||||
|         module = block(iC, planes, stride) | ||||
|         self.channels.append( module.out_dim ) | ||||
|         self.layers.append  ( module ) | ||||
|         self.message += "\nstage={:}, ilayer={:02d}/{:02d}, block={:03d}, iC={:3d}, oC={:3d}, stride={:}".format(stage, iL, layer_blocks, len(self.layers)-1, iC, module.out_dim, stride) | ||||
|  | ||||
|     self.avgpool = nn.AvgPool2d(8) | ||||
|     self.classifier = nn.Linear(module.out_dim, num_classes) | ||||
|     assert sum(x.num_conv for x in self.layers) + 1 == depth, 'invalid depth check {:} vs {:}'.format(sum(x.num_conv for x in self.layers)+1, depth) | ||||
|  | ||||
|     self.apply(initialize_resnet) | ||||
|     if zero_init_residual: | ||||
|       for m in self.modules(): | ||||
|         if isinstance(m, ResNetBasicblock): | ||||
|           nn.init.constant_(m.conv_b.bn.weight, 0) | ||||
|         elif isinstance(m, ResNetBottleneck): | ||||
|           nn.init.constant_(m.conv_1x4.bn.weight, 0) | ||||
|  | ||||
|   def get_message(self): | ||||
|     return self.message | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     x = inputs | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       x = layer( x ) | ||||
|     features = self.avgpool(x) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     logits   = self.classifier(features) | ||||
|     return features, logits | ||||
							
								
								
									
										94
									
								
								graph_dit/naswot/models/CifarWideResNet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								graph_dit/naswot/models/CifarWideResNet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| import torch | ||||
| import torch.nn as nn | ||||
| import torch.nn.functional as F | ||||
| from .initialization import initialize_resnet | ||||
|  | ||||
|  | ||||
| class WideBasicblock(nn.Module): | ||||
|   def __init__(self, inplanes, planes, stride, dropout=False): | ||||
|     super(WideBasicblock, self).__init__() | ||||
|  | ||||
|     self.bn_a = nn.BatchNorm2d(inplanes) | ||||
|     self.conv_a = nn.Conv2d(inplanes, planes, kernel_size=3, stride=stride, padding=1, bias=False) | ||||
|  | ||||
|     self.bn_b = nn.BatchNorm2d(planes) | ||||
|     if dropout: | ||||
|       self.dropout = nn.Dropout2d(p=0.5, inplace=True) | ||||
|     else: | ||||
|       self.dropout = None | ||||
|     self.conv_b = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False) | ||||
|  | ||||
|     if inplanes != planes: | ||||
|       self.downsample = nn.Conv2d(inplanes, planes, kernel_size=1, stride=stride, padding=0, bias=False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|  | ||||
|   def forward(self, x): | ||||
|  | ||||
|     basicblock = self.bn_a(x) | ||||
|     basicblock = F.relu(basicblock) | ||||
|     basicblock = self.conv_a(basicblock) | ||||
|  | ||||
|     basicblock = self.bn_b(basicblock) | ||||
|     basicblock = F.relu(basicblock) | ||||
|     if self.dropout is not None: | ||||
|       basicblock = self.dropout(basicblock) | ||||
|     basicblock = self.conv_b(basicblock) | ||||
|  | ||||
|     if self.downsample is not None: | ||||
|       x = self.downsample(x) | ||||
|      | ||||
|     return x + basicblock | ||||
|  | ||||
|  | ||||
| class CifarWideResNet(nn.Module): | ||||
|   """ | ||||
|   ResNet optimized for the Cifar dataset, as specified in | ||||
|   https://arxiv.org/abs/1512.03385.pdf | ||||
|   """ | ||||
|   def __init__(self, depth, widen_factor, num_classes, dropout): | ||||
|     super(CifarWideResNet, self).__init__() | ||||
|  | ||||
|     #Model type specifies number of layers for CIFAR-10 and CIFAR-100 model | ||||
|     assert (depth - 4) % 6 == 0, 'depth should be one of 20, 32, 44, 56, 110' | ||||
|     layer_blocks = (depth - 4) // 6 | ||||
|     print ('CifarPreResNet : Depth : {} , Layers for each block : {}'.format(depth, layer_blocks)) | ||||
|  | ||||
|     self.num_classes = num_classes | ||||
|     self.dropout = dropout | ||||
|     self.conv_3x3 = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1, bias=False) | ||||
|  | ||||
|     self.message  = 'Wide ResNet : depth={:}, widen_factor={:}, class={:}'.format(depth, widen_factor, num_classes) | ||||
|     self.inplanes = 16 | ||||
|     self.stage_1 = self._make_layer(WideBasicblock, 16*widen_factor, layer_blocks, 1) | ||||
|     self.stage_2 = self._make_layer(WideBasicblock, 32*widen_factor, layer_blocks, 2) | ||||
|     self.stage_3 = self._make_layer(WideBasicblock, 64*widen_factor, layer_blocks, 2) | ||||
|     self.lastact = nn.Sequential(nn.BatchNorm2d(64*widen_factor), nn.ReLU(inplace=True)) | ||||
|     self.avgpool = nn.AvgPool2d(8) | ||||
|     self.classifier = nn.Linear(64*widen_factor, num_classes) | ||||
|  | ||||
|     self.apply(initialize_resnet) | ||||
|  | ||||
|   def get_message(self): | ||||
|     return self.message | ||||
|  | ||||
|   def _make_layer(self, block, planes, blocks, stride): | ||||
|  | ||||
|     layers = [] | ||||
|     layers.append(block(self.inplanes, planes, stride, self.dropout)) | ||||
|     self.inplanes = planes | ||||
|     for i in range(1, blocks): | ||||
|       layers.append(block(self.inplanes, planes, 1, self.dropout)) | ||||
|  | ||||
|     return nn.Sequential(*layers) | ||||
|  | ||||
|   def forward(self, x): | ||||
|     x = self.conv_3x3(x) | ||||
|     x = self.stage_1(x) | ||||
|     x = self.stage_2(x) | ||||
|     x = self.stage_3(x) | ||||
|     x = self.lastact(x) | ||||
|     x = self.avgpool(x) | ||||
|     features = x.view(x.size(0), -1) | ||||
|     outs     = self.classifier(features) | ||||
|     return features, outs | ||||
							
								
								
									
										101
									
								
								graph_dit/naswot/models/ImageNet_MobileNetV2.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								graph_dit/naswot/models/ImageNet_MobileNetV2.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| # MobileNetV2: Inverted Residuals and Linear Bottlenecks, CVPR 2018 | ||||
| from torch import nn | ||||
| from .initialization import initialize_resnet | ||||
|  | ||||
|  | ||||
| class ConvBNReLU(nn.Module): | ||||
|   def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, groups=1): | ||||
|     super(ConvBNReLU, self).__init__() | ||||
|     padding = (kernel_size - 1) // 2 | ||||
|     self.conv = nn.Conv2d(in_planes, out_planes, kernel_size, stride, padding, groups=groups, bias=False) | ||||
|     self.bn   = nn.BatchNorm2d(out_planes) | ||||
|     self.relu = nn.ReLU6(inplace=True) | ||||
|    | ||||
|   def forward(self, x): | ||||
|     out = self.conv( x ) | ||||
|     out = self.bn  ( out ) | ||||
|     out = self.relu( out ) | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class InvertedResidual(nn.Module): | ||||
|   def __init__(self, inp, oup, stride, expand_ratio): | ||||
|     super(InvertedResidual, self).__init__() | ||||
|     self.stride = stride | ||||
|     assert stride in [1, 2] | ||||
|  | ||||
|     hidden_dim = int(round(inp * expand_ratio)) | ||||
|     self.use_res_connect = self.stride == 1 and inp == oup | ||||
|  | ||||
|     layers = [] | ||||
|     if expand_ratio != 1: | ||||
|       # pw | ||||
|       layers.append(ConvBNReLU(inp, hidden_dim, kernel_size=1)) | ||||
|     layers.extend([ | ||||
|       # dw | ||||
|       ConvBNReLU(hidden_dim, hidden_dim, stride=stride, groups=hidden_dim), | ||||
|       # pw-linear | ||||
|       nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), | ||||
|       nn.BatchNorm2d(oup), | ||||
|     ]) | ||||
|     self.conv = nn.Sequential(*layers) | ||||
|  | ||||
|   def forward(self, x): | ||||
|     if self.use_res_connect: | ||||
|       return x + self.conv(x) | ||||
|     else: | ||||
|       return self.conv(x) | ||||
|  | ||||
|  | ||||
| class MobileNetV2(nn.Module): | ||||
|   def __init__(self, num_classes, width_mult, input_channel, last_channel, block_name, dropout): | ||||
|     super(MobileNetV2, self).__init__() | ||||
|     if block_name == 'InvertedResidual': | ||||
|       block = InvertedResidual | ||||
|     else: | ||||
|       raise ValueError('invalid block name : {:}'.format(block_name)) | ||||
|     inverted_residual_setting = [ | ||||
|       # t, c,  n, s | ||||
|       [1, 16 , 1, 1], | ||||
|       [6, 24 , 2, 2], | ||||
|       [6, 32 , 3, 2], | ||||
|       [6, 64 , 4, 2], | ||||
|       [6, 96 , 3, 1], | ||||
|       [6, 160, 3, 2], | ||||
|       [6, 320, 1, 1], | ||||
|     ] | ||||
|  | ||||
|     # building first layer | ||||
|     input_channel = int(input_channel * width_mult) | ||||
|     self.last_channel = int(last_channel * max(1.0, width_mult)) | ||||
|     features = [ConvBNReLU(3, input_channel, stride=2)] | ||||
|     # building inverted residual blocks | ||||
|     for t, c, n, s in inverted_residual_setting: | ||||
|       output_channel = int(c * width_mult) | ||||
|       for i in range(n): | ||||
|         stride = s if i == 0 else 1 | ||||
|         features.append(block(input_channel, output_channel, stride, expand_ratio=t)) | ||||
|         input_channel = output_channel | ||||
|     # building last several layers | ||||
|     features.append(ConvBNReLU(input_channel, self.last_channel, kernel_size=1)) | ||||
|     # make it nn.Sequential | ||||
|     self.features = nn.Sequential(*features) | ||||
|  | ||||
|     # building classifier | ||||
|     self.classifier = nn.Sequential( | ||||
|       nn.Dropout(dropout), | ||||
|       nn.Linear(self.last_channel, num_classes), | ||||
|     ) | ||||
|     self.message = 'MobileNetV2 : width_mult={:}, in-C={:}, last-C={:}, block={:}, dropout={:}'.format(width_mult, input_channel, last_channel, block_name, dropout) | ||||
|  | ||||
|     # weight initialization | ||||
|     self.apply( initialize_resnet ) | ||||
|  | ||||
|   def get_message(self): | ||||
|     return self.message | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     features = self.features(inputs) | ||||
|     vectors  = features.mean([2, 3]) | ||||
|     predicts = self.classifier(vectors) | ||||
|     return features, predicts | ||||
							
								
								
									
										172
									
								
								graph_dit/naswot/models/ImageNet_ResNet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								graph_dit/naswot/models/ImageNet_ResNet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,172 @@ | ||||
| # Deep Residual Learning for Image Recognition, CVPR 2016 | ||||
| import torch.nn as nn | ||||
| from .initialization import initialize_resnet | ||||
|  | ||||
| def conv3x3(in_planes, out_planes, stride=1, groups=1): | ||||
|   return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=1, groups=groups, bias=False) | ||||
|  | ||||
|  | ||||
| def conv1x1(in_planes, out_planes, stride=1): | ||||
|   return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False) | ||||
|  | ||||
|  | ||||
| class BasicBlock(nn.Module): | ||||
|   expansion = 1 | ||||
|  | ||||
|   def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1, base_width=64): | ||||
|     super(BasicBlock, self).__init__() | ||||
|     if groups != 1 or base_width != 64: | ||||
|       raise ValueError('BasicBlock only supports groups=1 and base_width=64') | ||||
|     # Both self.conv1 and self.downsample layers downsample the input when stride != 1 | ||||
|     self.conv1 = conv3x3(inplanes, planes, stride) | ||||
|     self.bn1   = nn.BatchNorm2d(planes) | ||||
|     self.relu  = nn.ReLU(inplace=True) | ||||
|     self.conv2 = conv3x3(planes, planes) | ||||
|     self.bn2   = nn.BatchNorm2d(planes) | ||||
|     self.downsample = downsample | ||||
|     self.stride = stride | ||||
|  | ||||
|   def forward(self, x): | ||||
|     identity = x | ||||
|  | ||||
|     out = self.conv1(x) | ||||
|     out = self.bn1(out) | ||||
|     out = self.relu(out) | ||||
|  | ||||
|     out = self.conv2(out) | ||||
|     out = self.bn2(out) | ||||
|  | ||||
|     if self.downsample is not None: | ||||
|       identity = self.downsample(x) | ||||
|  | ||||
|     out += identity | ||||
|     out = self.relu(out) | ||||
|  | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class Bottleneck(nn.Module): | ||||
|   expansion = 4 | ||||
|  | ||||
|   def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1, base_width=64): | ||||
|     super(Bottleneck, self).__init__() | ||||
|     width = int(planes * (base_width / 64.)) * groups | ||||
|     # Both self.conv2 and self.downsample layers downsample the input when stride != 1 | ||||
|     self.conv1 = conv1x1(inplanes, width) | ||||
|     self.bn1   = nn.BatchNorm2d(width) | ||||
|     self.conv2 = conv3x3(width, width, stride, groups) | ||||
|     self.bn2   = nn.BatchNorm2d(width) | ||||
|     self.conv3 = conv1x1(width, planes * self.expansion) | ||||
|     self.bn3   = nn.BatchNorm2d(planes * self.expansion) | ||||
|     self.relu  = nn.ReLU(inplace=True) | ||||
|     self.downsample = downsample | ||||
|     self.stride = stride | ||||
|  | ||||
|   def forward(self, x): | ||||
|     identity = x | ||||
|  | ||||
|     out = self.conv1(x) | ||||
|     out = self.bn1(out) | ||||
|     out = self.relu(out) | ||||
|  | ||||
|     out = self.conv2(out) | ||||
|     out = self.bn2(out) | ||||
|     out = self.relu(out) | ||||
|  | ||||
|     out = self.conv3(out) | ||||
|     out = self.bn3(out) | ||||
|  | ||||
|     if self.downsample is not None: | ||||
|       identity = self.downsample(x) | ||||
|  | ||||
|     out += identity | ||||
|     out = self.relu(out) | ||||
|  | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class ResNet(nn.Module): | ||||
|  | ||||
|   def __init__(self, block_name, layers, deep_stem, num_classes, zero_init_residual, groups, width_per_group): | ||||
|     super(ResNet, self).__init__() | ||||
|  | ||||
|     #planes = [int(width_per_group * groups * 2 ** i) for i in range(4)] | ||||
|     if block_name == 'BasicBlock'  : block= BasicBlock | ||||
|     elif block_name == 'Bottleneck': block= Bottleneck | ||||
|     else                           : raise ValueError('invalid block-name : {:}'.format(block_name)) | ||||
|  | ||||
|     if not deep_stem: | ||||
|       self.conv = nn.Sequential( | ||||
|                    nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False), | ||||
|                    nn.BatchNorm2d(64), nn.ReLU(inplace=True)) | ||||
|     else: | ||||
|       self.conv = nn.Sequential( | ||||
|                    nn.Conv2d(           3, 32, kernel_size=3, stride=2, padding=1, bias=False), | ||||
|                    nn.BatchNorm2d(32), nn.ReLU(inplace=True), | ||||
|                    nn.Conv2d(32, 32, kernel_size=3, stride=1, padding=1, bias=False), | ||||
|                    nn.BatchNorm2d(32), nn.ReLU(inplace=True), | ||||
|                    nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1, bias=False), | ||||
|                    nn.BatchNorm2d(64), nn.ReLU(inplace=True)) | ||||
|     self.inplanes = 64 | ||||
|     self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) | ||||
|     self.layer1 = self._make_layer(block, 64 , layers[0], stride=1, groups=groups, base_width=width_per_group) | ||||
|     self.layer2 = self._make_layer(block, 128, layers[1], stride=2, groups=groups, base_width=width_per_group) | ||||
|     self.layer3 = self._make_layer(block, 256, layers[2], stride=2, groups=groups, base_width=width_per_group) | ||||
|     self.layer4 = self._make_layer(block, 512, layers[3], stride=2, groups=groups, base_width=width_per_group) | ||||
|     self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) | ||||
|     self.fc      = nn.Linear(512 * block.expansion, num_classes) | ||||
|     self.message = 'block = {:}, layers = {:}, deep_stem = {:}, num_classes = {:}'.format(block, layers, deep_stem, num_classes) | ||||
|  | ||||
|     self.apply( initialize_resnet ) | ||||
|  | ||||
|     # Zero-initialize the last BN in each residual branch, | ||||
|     # so that the residual branch starts with zeros, and each residual block behaves like an identity. | ||||
|     # This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677 | ||||
|     if zero_init_residual: | ||||
|       for m in self.modules(): | ||||
|         if isinstance(m, Bottleneck): | ||||
|           nn.init.constant_(m.bn3.weight, 0) | ||||
|         elif isinstance(m, BasicBlock): | ||||
|           nn.init.constant_(m.bn2.weight, 0) | ||||
|  | ||||
|   def _make_layer(self, block, planes, blocks, stride, groups, base_width): | ||||
|     downsample = None | ||||
|     if stride != 1 or self.inplanes != planes * block.expansion: | ||||
|       if stride == 2: | ||||
|         downsample = nn.Sequential( | ||||
|           nn.AvgPool2d(kernel_size=2, stride=2, padding=0), | ||||
|           conv1x1(self.inplanes, planes * block.expansion, 1), | ||||
|           nn.BatchNorm2d(planes * block.expansion), | ||||
|         ) | ||||
|       elif stride == 1: | ||||
|         downsample = nn.Sequential( | ||||
|           conv1x1(self.inplanes, planes * block.expansion, stride), | ||||
|           nn.BatchNorm2d(planes * block.expansion), | ||||
|         ) | ||||
|       else: raise ValueError('invalid stride [{:}] for downsample'.format(stride)) | ||||
|  | ||||
|     layers = [] | ||||
|     layers.append(block(self.inplanes, planes, stride, downsample, groups, base_width)) | ||||
|     self.inplanes = planes * block.expansion | ||||
|     for _ in range(1, blocks): | ||||
|       layers.append(block(self.inplanes, planes, 1, None, groups, base_width)) | ||||
|  | ||||
|     return nn.Sequential(*layers) | ||||
|  | ||||
|   def get_message(self): | ||||
|     return self.message | ||||
|  | ||||
|   def forward(self, x): | ||||
|     x = self.conv(x) | ||||
|     x = self.maxpool(x) | ||||
|  | ||||
|     x = self.layer1(x) | ||||
|     x = self.layer2(x) | ||||
|     x = self.layer3(x) | ||||
|     x = self.layer4(x) | ||||
|  | ||||
|     features = self.avgpool(x) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     logits   = self.fc(features) | ||||
|  | ||||
|     return features, logits | ||||
							
								
								
									
										34
									
								
								graph_dit/naswot/models/SharedUtils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								graph_dit/naswot/models/SharedUtils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| ##################################################### | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019.01 # | ||||
| ##################################################### | ||||
| import torch | ||||
| import torch.nn as nn | ||||
|  | ||||
|  | ||||
| def additive_func(A, B): | ||||
|   assert A.dim() == B.dim() and A.size(0) == B.size(0), '{:} vs {:}'.format(A.size(), B.size()) | ||||
|   C = min(A.size(1), B.size(1)) | ||||
|   if A.size(1) == B.size(1): | ||||
|     return A + B | ||||
|   elif A.size(1) < B.size(1): | ||||
|     out = B.clone() | ||||
|     out[:,:C] += A | ||||
|     return out | ||||
|   else: | ||||
|     out = A.clone() | ||||
|     out[:,:C] += B | ||||
|     return out | ||||
|  | ||||
|  | ||||
| def change_key(key, value): | ||||
|   def func(m): | ||||
|     if hasattr(m, key): | ||||
|       setattr(m, key, value) | ||||
|   return func | ||||
|  | ||||
|  | ||||
| def parse_channel_info(xstring): | ||||
|   blocks = xstring.split(' ') | ||||
|   blocks = [x.split('-') for x in blocks] | ||||
|   blocks = [[int(_) for _ in x] for x in blocks] | ||||
|   return blocks | ||||
							
								
								
									
										185
									
								
								graph_dit/naswot/models/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								graph_dit/naswot/models/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,185 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| from os import path as osp | ||||
| from typing import List, Text | ||||
| import torch | ||||
|  | ||||
| __all__ = ['change_key', 'get_cell_based_tiny_net', 'get_search_spaces', 'get_cifar_models', 'get_imagenet_models', \ | ||||
|            'obtain_model', 'obtain_search_model', 'load_net_from_checkpoint', \ | ||||
|            'CellStructure', 'CellArchitectures' | ||||
|            ] | ||||
|  | ||||
| # useful modules | ||||
| from config_utils import dict2config | ||||
| from .SharedUtils import change_key | ||||
| from .cell_searchs import CellStructure, CellArchitectures | ||||
|  | ||||
|  | ||||
| # Cell-based NAS Models | ||||
| def get_cell_based_tiny_net(config): | ||||
|   if isinstance(config, dict): config = dict2config(config, None) # to support the argument being a dict | ||||
|   super_type = getattr(config, 'super_type', 'basic') | ||||
|   group_names = ['DARTS-V1', 'DARTS-V2', 'GDAS', 'SETN', 'ENAS', 'RANDOM'] | ||||
|   if super_type == 'basic' and config.name in group_names: | ||||
|     from .cell_searchs import nas201_super_nets as nas_super_nets | ||||
|     try: | ||||
|       return nas_super_nets[config.name](config.C, config.N, config.max_nodes, config.num_classes, config.space, config.affine, config.track_running_stats) | ||||
|     except: | ||||
|       return nas_super_nets[config.name](config.C, config.N, config.max_nodes, config.num_classes, config.space) | ||||
|   elif super_type == 'nasnet-super': | ||||
|     from .cell_searchs import nasnet_super_nets as nas_super_nets | ||||
|     return nas_super_nets[config.name](config.C, config.N, config.steps, config.multiplier, \ | ||||
|                     config.stem_multiplier, config.num_classes, config.space, config.affine, config.track_running_stats) | ||||
|   elif config.name == 'infer.tiny': | ||||
|     from .cell_infers import TinyNetwork | ||||
|     if hasattr(config, 'genotype'): | ||||
|       genotype = config.genotype | ||||
|     elif hasattr(config, 'arch_str'): | ||||
|       genotype = CellStructure.str2structure(config.arch_str) | ||||
|     else: raise ValueError('Can not find genotype from this config : {:}'.format(config)) | ||||
|     return TinyNetwork(config.C, config.N, genotype, config.num_classes) | ||||
|   elif config.name == 'infer.shape.tiny': | ||||
|     from .shape_infers import DynamicShapeTinyNet | ||||
|     if isinstance(config.channels, str): | ||||
|       channels = tuple([int(x) for x in config.channels.split(':')]) | ||||
|     else: channels = config.channels | ||||
|     genotype = CellStructure.str2structure(config.genotype) | ||||
|     return DynamicShapeTinyNet(channels, genotype, config.num_classes) | ||||
|   elif config.name == 'infer.nasnet-cifar': | ||||
|     from .cell_infers import NASNetonCIFAR | ||||
|     raise NotImplementedError | ||||
|   else: | ||||
|     raise ValueError('invalid network name : {:}'.format(config.name)) | ||||
|  | ||||
|  | ||||
| # obtain the search space, i.e., a dict mapping the operation name into a python-function for this op | ||||
| def get_search_spaces(xtype, name) -> List[Text]: | ||||
|   if xtype == 'cell': | ||||
|     from .cell_operations import SearchSpaceNames | ||||
|     assert name in SearchSpaceNames, 'invalid name [{:}] in {:}'.format(name, SearchSpaceNames.keys()) | ||||
|     return SearchSpaceNames[name] | ||||
|   else: | ||||
|     raise ValueError('invalid search-space type is {:}'.format(xtype)) | ||||
|  | ||||
|  | ||||
| def get_cifar_models(config, extra_path=None): | ||||
|   super_type = getattr(config, 'super_type', 'basic') | ||||
|   if super_type == 'basic': | ||||
|     from .CifarResNet      import CifarResNet | ||||
|     from .CifarDenseNet    import DenseNet | ||||
|     from .CifarWideResNet  import CifarWideResNet | ||||
|     if config.arch == 'resnet': | ||||
|       return CifarResNet(config.module, config.depth, config.class_num, config.zero_init_residual) | ||||
|     elif config.arch == 'densenet': | ||||
|       return DenseNet(config.growthRate, config.depth, config.reduction, config.class_num, config.bottleneck) | ||||
|     elif config.arch == 'wideresnet': | ||||
|       return CifarWideResNet(config.depth, config.wide_factor, config.class_num, config.dropout) | ||||
|     else: | ||||
|       raise ValueError('invalid module type : {:}'.format(config.arch)) | ||||
|   elif super_type.startswith('infer'): | ||||
|     from .shape_infers import InferWidthCifarResNet | ||||
|     from .shape_infers import InferDepthCifarResNet | ||||
|     from .shape_infers import InferCifarResNet | ||||
|     from .cell_infers import NASNetonCIFAR | ||||
|     assert len(super_type.split('-')) == 2, 'invalid super_type : {:}'.format(super_type) | ||||
|     infer_mode = super_type.split('-')[1] | ||||
|     if infer_mode == 'width': | ||||
|       return InferWidthCifarResNet(config.module, config.depth, config.xchannels, config.class_num, config.zero_init_residual) | ||||
|     elif infer_mode == 'depth': | ||||
|       return InferDepthCifarResNet(config.module, config.depth, config.xblocks, config.class_num, config.zero_init_residual) | ||||
|     elif infer_mode == 'shape': | ||||
|       return InferCifarResNet(config.module, config.depth, config.xblocks, config.xchannels, config.class_num, config.zero_init_residual) | ||||
|     elif infer_mode == 'nasnet.cifar': | ||||
|       genotype = config.genotype | ||||
|       if extra_path is not None:  # reload genotype by extra_path | ||||
|         if not osp.isfile(extra_path): raise ValueError('invalid extra_path : {:}'.format(extra_path)) | ||||
|         xdata = torch.load(extra_path) | ||||
|         current_epoch = xdata['epoch'] | ||||
|         genotype = xdata['genotypes'][current_epoch-1] | ||||
|       C = config.C if hasattr(config, 'C') else config.ichannel | ||||
|       N = config.N if hasattr(config, 'N') else config.layers | ||||
|       return NASNetonCIFAR(C, N, config.stem_multi, config.class_num, genotype, config.auxiliary) | ||||
|     else: | ||||
|       raise ValueError('invalid infer-mode : {:}'.format(infer_mode)) | ||||
|   else: | ||||
|     raise ValueError('invalid super-type : {:}'.format(super_type)) | ||||
|  | ||||
|  | ||||
| def get_imagenet_models(config): | ||||
|   super_type = getattr(config, 'super_type', 'basic') | ||||
|   if super_type == 'basic': | ||||
|     from .ImageNet_ResNet import ResNet | ||||
|     from .ImageNet_MobileNetV2 import MobileNetV2 | ||||
|     if config.arch == 'resnet': | ||||
|       return ResNet(config.block_name, config.layers, config.deep_stem, config.class_num, config.zero_init_residual, config.groups, config.width_per_group) | ||||
|     elif config.arch == 'mobilenet_v2': | ||||
|       return MobileNetV2(config.class_num, config.width_multi, config.input_channel, config.last_channel, 'InvertedResidual', config.dropout) | ||||
|     else: | ||||
|       raise ValueError('invalid arch : {:}'.format( config.arch )) | ||||
|   elif super_type.startswith('infer'): # NAS searched architecture | ||||
|     assert len(super_type.split('-')) == 2, 'invalid super_type : {:}'.format(super_type) | ||||
|     infer_mode = super_type.split('-')[1] | ||||
|     if infer_mode == 'shape': | ||||
|       from .shape_infers import InferImagenetResNet | ||||
|       from .shape_infers import InferMobileNetV2 | ||||
|       if config.arch == 'resnet': | ||||
|         return InferImagenetResNet(config.block_name, config.layers, config.xblocks, config.xchannels, config.deep_stem, config.class_num, config.zero_init_residual) | ||||
|       elif config.arch == "MobileNetV2": | ||||
|         return InferMobileNetV2(config.class_num, config.xchannels, config.xblocks, config.dropout) | ||||
|       else: | ||||
|         raise ValueError('invalid arch-mode : {:}'.format(config.arch)) | ||||
|     else: | ||||
|       raise ValueError('invalid infer-mode : {:}'.format(infer_mode)) | ||||
|   else: | ||||
|     raise ValueError('invalid super-type : {:}'.format(super_type)) | ||||
|  | ||||
|  | ||||
| # Try to obtain the network by config. | ||||
| def obtain_model(config, extra_path=None): | ||||
|   if config.dataset == 'cifar': | ||||
|     return get_cifar_models(config, extra_path) | ||||
|   elif config.dataset == 'imagenet': | ||||
|     return get_imagenet_models(config) | ||||
|   else: | ||||
|     raise ValueError('invalid dataset in the model config : {:}'.format(config)) | ||||
|  | ||||
|  | ||||
| def obtain_search_model(config): | ||||
|   if config.dataset == 'cifar': | ||||
|     if config.arch == 'resnet': | ||||
|       from .shape_searchs import SearchWidthCifarResNet | ||||
|       from .shape_searchs import SearchDepthCifarResNet | ||||
|       from .shape_searchs import SearchShapeCifarResNet | ||||
|       if config.search_mode == 'width': | ||||
|         return SearchWidthCifarResNet(config.module, config.depth, config.class_num) | ||||
|       elif config.search_mode == 'depth': | ||||
|         return SearchDepthCifarResNet(config.module, config.depth, config.class_num) | ||||
|       elif config.search_mode == 'shape': | ||||
|         return SearchShapeCifarResNet(config.module, config.depth, config.class_num) | ||||
|       else: raise ValueError('invalid search mode : {:}'.format(config.search_mode)) | ||||
|     elif config.arch == 'simres': | ||||
|       from .shape_searchs import SearchWidthSimResNet | ||||
|       if config.search_mode == 'width': | ||||
|         return SearchWidthSimResNet(config.depth, config.class_num) | ||||
|       else: raise ValueError('invalid search mode : {:}'.format(config.search_mode)) | ||||
|     else: | ||||
|       raise ValueError('invalid arch : {:} for dataset [{:}]'.format(config.arch, config.dataset)) | ||||
|   elif config.dataset == 'imagenet': | ||||
|     from .shape_searchs import SearchShapeImagenetResNet | ||||
|     assert config.search_mode == 'shape', 'invalid search-mode : {:}'.format( config.search_mode ) | ||||
|     if config.arch == 'resnet': | ||||
|       return SearchShapeImagenetResNet(config.block_name, config.layers, config.deep_stem, config.class_num) | ||||
|     else: | ||||
|       raise ValueError('invalid model config : {:}'.format(config)) | ||||
|   else: | ||||
|     raise ValueError('invalid dataset in the model config : {:}'.format(config)) | ||||
|  | ||||
|  | ||||
| def load_net_from_checkpoint(checkpoint): | ||||
|   assert osp.isfile(checkpoint), 'checkpoint {:} does not exist'.format(checkpoint) | ||||
|   checkpoint   = torch.load(checkpoint) | ||||
|   model_config = dict2config(checkpoint['model-config'], None) | ||||
|   model        = obtain_model(model_config) | ||||
|   model.load_state_dict(checkpoint['base-model']) | ||||
|   return model | ||||
							
								
								
									
										5
									
								
								graph_dit/naswot/models/cell_infers/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								graph_dit/naswot/models/cell_infers/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| ##################################################### | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019.01 # | ||||
| ##################################################### | ||||
| from .tiny_network import TinyNetwork | ||||
| from .nasnet_cifar import NASNetonCIFAR | ||||
							
								
								
									
										120
									
								
								graph_dit/naswot/models/cell_infers/cells.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								graph_dit/naswot/models/cell_infers/cells.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| ##################################################### | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019.01 # | ||||
| ##################################################### | ||||
|  | ||||
| import torch | ||||
| import torch.nn as nn | ||||
| from copy import deepcopy | ||||
| from ..cell_operations import OPS | ||||
|  | ||||
|  | ||||
| # Cell for NAS-Bench-201 | ||||
| class InferCell(nn.Module): | ||||
|  | ||||
|   def __init__(self, genotype, C_in, C_out, stride): | ||||
|     super(InferCell, self).__init__() | ||||
|  | ||||
|     self.layers  = nn.ModuleList() | ||||
|     self.node_IN = [] | ||||
|     self.node_IX = [] | ||||
|     self.genotype = deepcopy(genotype) | ||||
|     for i in range(1, len(genotype)): | ||||
|       node_info = genotype[i-1] | ||||
|       cur_index = [] | ||||
|       cur_innod = [] | ||||
|       for (op_name, op_in) in node_info: | ||||
|         if op_in == 0: | ||||
|           layer = OPS[op_name](C_in , C_out, stride, True, True) | ||||
|         else: | ||||
|           layer = OPS[op_name](C_out, C_out,      1, True, True) | ||||
|         cur_index.append( len(self.layers) ) | ||||
|         cur_innod.append( op_in ) | ||||
|         self.layers.append( layer ) | ||||
|       self.node_IX.append( cur_index ) | ||||
|       self.node_IN.append( cur_innod ) | ||||
|     self.nodes   = len(genotype) | ||||
|     self.in_dim  = C_in | ||||
|     self.out_dim = C_out | ||||
|  | ||||
|   def extra_repr(self): | ||||
|     string = 'info :: nodes={nodes}, inC={in_dim}, outC={out_dim}'.format(**self.__dict__) | ||||
|     laystr = [] | ||||
|     for i, (node_layers, node_innods) in enumerate(zip(self.node_IX,self.node_IN)): | ||||
|       y = ['I{:}-L{:}'.format(_ii, _il) for _il, _ii in zip(node_layers, node_innods)] | ||||
|       x = '{:}<-({:})'.format(i+1, ','.join(y)) | ||||
|       laystr.append( x ) | ||||
|     return string + ', [{:}]'.format( ' | '.join(laystr) ) + ', {:}'.format(self.genotype.tostr()) | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     nodes = [inputs] | ||||
|     for i, (node_layers, node_innods) in enumerate(zip(self.node_IX,self.node_IN)): | ||||
|       node_feature = sum( self.layers[_il](nodes[_ii]) for _il, _ii in zip(node_layers, node_innods) ) | ||||
|       nodes.append( node_feature ) | ||||
|     return nodes[-1] | ||||
|  | ||||
|  | ||||
|  | ||||
| # Learning Transferable Architectures for Scalable Image Recognition, CVPR 2018 | ||||
| class NASNetInferCell(nn.Module): | ||||
|  | ||||
|   def __init__(self, genotype, C_prev_prev, C_prev, C, reduction, reduction_prev, affine, track_running_stats): | ||||
|     super(NASNetInferCell, self).__init__() | ||||
|     self.reduction = reduction | ||||
|     if reduction_prev: self.preprocess0 = OPS['skip_connect'](C_prev_prev, C, 2, affine, track_running_stats) | ||||
|     else             : self.preprocess0 = OPS['nor_conv_1x1'](C_prev_prev, C, 1, affine, track_running_stats) | ||||
|     self.preprocess1 = OPS['nor_conv_1x1'](C_prev, C, 1, affine, track_running_stats) | ||||
|  | ||||
|     if not reduction: | ||||
|       nodes, concats = genotype['normal'], genotype['normal_concat'] | ||||
|     else: | ||||
|       nodes, concats = genotype['reduce'], genotype['reduce_concat'] | ||||
|     self._multiplier = len(concats) | ||||
|     self._concats = concats | ||||
|     self._steps = len(nodes) | ||||
|     self._nodes = nodes | ||||
|     self.edges = nn.ModuleDict() | ||||
|     for i, node in enumerate(nodes): | ||||
|       for in_node in node: | ||||
|         name, j = in_node[0], in_node[1] | ||||
|         stride = 2 if reduction and j < 2 else 1 | ||||
|         node_str = '{:}<-{:}'.format(i+2, j) | ||||
|         self.edges[node_str] = OPS[name](C, C, stride, affine, track_running_stats) | ||||
|  | ||||
|   # [TODO] to support drop_prob in this function.. | ||||
|   def forward(self, s0, s1, unused_drop_prob): | ||||
|     s0 = self.preprocess0(s0) | ||||
|     s1 = self.preprocess1(s1) | ||||
|  | ||||
|     states = [s0, s1] | ||||
|     for i, node in enumerate(self._nodes): | ||||
|       clist = [] | ||||
|       for in_node in node: | ||||
|         name, j = in_node[0], in_node[1] | ||||
|         node_str = '{:}<-{:}'.format(i+2, j) | ||||
|         op = self.edges[ node_str ] | ||||
|         clist.append( op(states[j]) ) | ||||
|       states.append( sum(clist) ) | ||||
|     return torch.cat([states[x] for x in self._concats], dim=1) | ||||
|  | ||||
|  | ||||
| class AuxiliaryHeadCIFAR(nn.Module): | ||||
|  | ||||
|   def __init__(self, C, num_classes): | ||||
|     """assuming input size 8x8""" | ||||
|     super(AuxiliaryHeadCIFAR, self).__init__() | ||||
|     self.features = nn.Sequential( | ||||
|       nn.ReLU(inplace=True), | ||||
|       nn.AvgPool2d(5, stride=3, padding=0, count_include_pad=False), # image size = 2 x 2 | ||||
|       nn.Conv2d(C, 128, 1, bias=False), | ||||
|       nn.BatchNorm2d(128), | ||||
|       nn.ReLU(inplace=True), | ||||
|       nn.Conv2d(128, 768, 2, bias=False), | ||||
|       nn.BatchNorm2d(768), | ||||
|       nn.ReLU(inplace=True) | ||||
|     ) | ||||
|     self.classifier = nn.Linear(768, num_classes) | ||||
|  | ||||
|   def forward(self, x): | ||||
|     x = self.features(x) | ||||
|     x = self.classifier(x.view(x.size(0),-1)) | ||||
|     return x | ||||
							
								
								
									
										71
									
								
								graph_dit/naswot/models/cell_infers/nasnet_cifar.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								graph_dit/naswot/models/cell_infers/nasnet_cifar.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| ##################################################### | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019.01 # | ||||
| ##################################################### | ||||
| import torch | ||||
| import torch.nn as nn | ||||
| from copy import deepcopy | ||||
| from .cells import NASNetInferCell as InferCell, AuxiliaryHeadCIFAR | ||||
|  | ||||
|  | ||||
| # The macro structure is based on NASNet | ||||
| class NASNetonCIFAR(nn.Module): | ||||
|  | ||||
|   def __init__(self, C, N, stem_multiplier, num_classes, genotype, auxiliary, affine=True, track_running_stats=True): | ||||
|     super(NASNetonCIFAR, self).__init__() | ||||
|     self._C        = C | ||||
|     self._layerN   = N | ||||
|     self.stem = nn.Sequential( | ||||
|                     nn.Conv2d(3, C*stem_multiplier, kernel_size=3, padding=1, bias=False), | ||||
|                     nn.BatchNorm2d(C*stem_multiplier)) | ||||
|    | ||||
|     # config for each layer | ||||
|     layer_channels   = [C    ] * N + [C*2 ] + [C*2  ] * (N-1) + [C*4 ] + [C*4  ] * (N-1) | ||||
|     layer_reductions = [False] * N + [True] + [False] * (N-1) + [True] + [False] * (N-1) | ||||
|  | ||||
|     C_prev_prev, C_prev, C_curr, reduction_prev = C*stem_multiplier, C*stem_multiplier, C, False | ||||
|     self.auxiliary_index = None | ||||
|     self.auxiliary_head  = None | ||||
|     self.cells = nn.ModuleList() | ||||
|     for index, (C_curr, reduction) in enumerate(zip(layer_channels, layer_reductions)): | ||||
|       cell = InferCell(genotype, C_prev_prev, C_prev, C_curr, reduction, reduction_prev, affine, track_running_stats) | ||||
|       self.cells.append( cell ) | ||||
|       C_prev_prev, C_prev, reduction_prev = C_prev, cell._multiplier*C_curr, reduction | ||||
|       if reduction and C_curr == C*4 and auxiliary: | ||||
|         self.auxiliary_head = AuxiliaryHeadCIFAR(C_prev, num_classes) | ||||
|         self.auxiliary_index = index | ||||
|     self._Layer     = len(self.cells) | ||||
|     self.lastact    = nn.Sequential(nn.BatchNorm2d(C_prev), nn.ReLU(inplace=True)) | ||||
|     self.global_pooling = nn.AdaptiveAvgPool2d(1) | ||||
|     self.classifier = nn.Linear(C_prev, num_classes) | ||||
|     self.drop_path_prob = -1 | ||||
|  | ||||
|   def update_drop_path(self, drop_path_prob): | ||||
|     self.drop_path_prob = drop_path_prob | ||||
|  | ||||
|   def auxiliary_param(self): | ||||
|     if self.auxiliary_head is None: return [] | ||||
|     else: return list( self.auxiliary_head.parameters() ) | ||||
|  | ||||
|   def get_message(self): | ||||
|     string = self.extra_repr() | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       string += '\n {:02d}/{:02d} :: {:}'.format(i, len(self.cells), cell.extra_repr()) | ||||
|     return string | ||||
|  | ||||
|   def extra_repr(self): | ||||
|     return ('{name}(C={_C}, N={_layerN}, L={_Layer})'.format(name=self.__class__.__name__, **self.__dict__)) | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     stem_feature, logits_aux = self.stem(inputs), None | ||||
|     cell_results = [stem_feature, stem_feature] | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       cell_feature = cell(cell_results[-2], cell_results[-1], self.drop_path_prob) | ||||
|       cell_results.append( cell_feature ) | ||||
|       if self.auxiliary_index is not None and i == self.auxiliary_index and self.training: | ||||
|         logits_aux = self.auxiliary_head( cell_results[-1] ) | ||||
|     out = self.lastact(cell_results[-1]) | ||||
|     out = self.global_pooling( out ) | ||||
|     out = out.view(out.size(0), -1) | ||||
|     logits = self.classifier(out) | ||||
|     if logits_aux is None: return out, logits | ||||
|     else: return out, [logits, logits_aux] | ||||
							
								
								
									
										58
									
								
								graph_dit/naswot/models/cell_infers/tiny_network.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								graph_dit/naswot/models/cell_infers/tiny_network.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| ##################################################### | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019.01 # | ||||
| ##################################################### | ||||
| import torch.nn as nn | ||||
| from ..cell_operations import ResNetBasicblock | ||||
| from .cells import InferCell | ||||
|  | ||||
|  | ||||
| # The macro structure for architectures in NAS-Bench-201 | ||||
| class TinyNetwork(nn.Module): | ||||
|  | ||||
|   def __init__(self, C, N, genotype, num_classes): | ||||
|     super(TinyNetwork, self).__init__() | ||||
|     self._C               = C | ||||
|     self._layerN          = N | ||||
|  | ||||
|     self.stem = nn.Sequential( | ||||
|                     nn.Conv2d(3, C, kernel_size=3, padding=1, bias=False), | ||||
|                     nn.BatchNorm2d(C)) | ||||
|    | ||||
|     layer_channels   = [C    ] * N + [C*2 ] + [C*2  ] * N + [C*4 ] + [C*4  ] * N     | ||||
|     layer_reductions = [False] * N + [True] + [False] * N + [True] + [False] * N | ||||
|  | ||||
|     C_prev = C | ||||
|     self.cells = nn.ModuleList() | ||||
|     for index, (C_curr, reduction) in enumerate(zip(layer_channels, layer_reductions)): | ||||
|       if reduction: | ||||
|         cell = ResNetBasicblock(C_prev, C_curr, 2, True) | ||||
|       else: | ||||
|         cell = InferCell(genotype, C_prev, C_curr, 1) | ||||
|       self.cells.append( cell ) | ||||
|       C_prev = cell.out_dim | ||||
|     self._Layer= len(self.cells) | ||||
|  | ||||
|     self.lastact = nn.Sequential(nn.BatchNorm2d(C_prev), nn.ReLU(inplace=True)) | ||||
|     self.global_pooling = nn.AdaptiveAvgPool2d(1) | ||||
|     self.classifier = nn.Linear(C_prev, num_classes) | ||||
|  | ||||
|   def get_message(self): | ||||
|     string = self.extra_repr() | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       string += '\n {:02d}/{:02d} :: {:}'.format(i, len(self.cells), cell.extra_repr()) | ||||
|     return string | ||||
|  | ||||
|   def extra_repr(self): | ||||
|     return ('{name}(C={_C}, N={_layerN}, L={_Layer})'.format(name=self.__class__.__name__, **self.__dict__)) | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     feature = self.stem(inputs) | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       feature = cell(feature) | ||||
|  | ||||
|     out = self.lastact(feature) | ||||
|     out = self.global_pooling( out ) | ||||
|     out = out.view(out.size(0), -1) | ||||
|     logits = self.classifier(out) | ||||
|  | ||||
|     return logits, out | ||||
							
								
								
									
										297
									
								
								graph_dit/naswot/models/cell_operations.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								graph_dit/naswot/models/cell_operations.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,297 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| import torch | ||||
| import torch.nn as nn | ||||
|  | ||||
| __all__ = ['OPS', 'ResNetBasicblock', 'SearchSpaceNames'] | ||||
|  | ||||
| OPS = { | ||||
|   'none'         : lambda C_in, C_out, stride, affine, track_running_stats: Zero(C_in, C_out, stride), | ||||
|   'avg_pool_3x3' : lambda C_in, C_out, stride, affine, track_running_stats: POOLING(C_in, C_out, stride, 'avg', affine, track_running_stats), | ||||
|   'max_pool_3x3' : lambda C_in, C_out, stride, affine, track_running_stats: POOLING(C_in, C_out, stride, 'max', affine, track_running_stats), | ||||
|   'nor_conv_7x7' : lambda C_in, C_out, stride, affine, track_running_stats: ReLUConvBN(C_in, C_out, (7,7), (stride,stride), (3,3), (1,1), affine, track_running_stats), | ||||
|   'nor_conv_3x3' : lambda C_in, C_out, stride, affine, track_running_stats: ReLUConvBN(C_in, C_out, (3,3), (stride,stride), (1,1), (1,1), affine, track_running_stats), | ||||
|   'nor_conv_1x1' : lambda C_in, C_out, stride, affine, track_running_stats: ReLUConvBN(C_in, C_out, (1,1), (stride,stride), (0,0), (1,1), affine, track_running_stats), | ||||
|   'dua_sepc_3x3' : lambda C_in, C_out, stride, affine, track_running_stats: DualSepConv(C_in, C_out, (3,3), (stride,stride), (1,1), (1,1), affine, track_running_stats), | ||||
|   'dua_sepc_5x5' : lambda C_in, C_out, stride, affine, track_running_stats: DualSepConv(C_in, C_out, (5,5), (stride,stride), (2,2), (1,1), affine, track_running_stats), | ||||
|   'dil_sepc_3x3' : lambda C_in, C_out, stride, affine, track_running_stats: SepConv(C_in, C_out, (3,3), (stride,stride), (2,2), (2,2), affine, track_running_stats), | ||||
|   'dil_sepc_5x5' : lambda C_in, C_out, stride, affine, track_running_stats: SepConv(C_in, C_out, (5,5), (stride,stride), (4,4), (2,2), affine, track_running_stats), | ||||
|   'skip_connect' : lambda C_in, C_out, stride, affine, track_running_stats: Identity() if stride == 1 and C_in == C_out else FactorizedReduce(C_in, C_out, stride, affine, track_running_stats), | ||||
| } | ||||
|  | ||||
| CONNECT_NAS_BENCHMARK = ['none', 'skip_connect', 'nor_conv_3x3'] | ||||
| NAS_BENCH_201         = ['none', 'skip_connect', 'nor_conv_1x1', 'nor_conv_3x3', 'avg_pool_3x3'] | ||||
| DARTS_SPACE           = ['none', 'skip_connect', 'dua_sepc_3x3', 'dua_sepc_5x5', 'dil_sepc_3x3', 'dil_sepc_5x5', 'avg_pool_3x3', 'max_pool_3x3'] | ||||
|  | ||||
| SearchSpaceNames = {'connect-nas'  : CONNECT_NAS_BENCHMARK, | ||||
|                     'nas-bench-201': NAS_BENCH_201, | ||||
|                     'darts'        : DARTS_SPACE} | ||||
|  | ||||
|  | ||||
| class ReLUConvBN(nn.Module): | ||||
|  | ||||
|   def __init__(self, C_in, C_out, kernel_size, stride, padding, dilation, affine, track_running_stats=True): | ||||
|     super(ReLUConvBN, self).__init__() | ||||
|     self.op = nn.Sequential( | ||||
|       nn.ReLU(inplace=False), | ||||
|       nn.Conv2d(C_in, C_out, kernel_size, stride=stride, padding=padding, dilation=dilation, bias=False), | ||||
|       nn.BatchNorm2d(C_out, affine=affine, track_running_stats=track_running_stats) | ||||
|     ) | ||||
|  | ||||
|   def forward(self, x): | ||||
|     return self.op(x) | ||||
|  | ||||
|  | ||||
| class SepConv(nn.Module): | ||||
|      | ||||
|   def __init__(self, C_in, C_out, kernel_size, stride, padding, dilation, affine, track_running_stats=True): | ||||
|     super(SepConv, self).__init__() | ||||
|     self.op = nn.Sequential( | ||||
|       nn.ReLU(inplace=False), | ||||
|       nn.Conv2d(C_in, C_in, kernel_size=kernel_size, stride=stride, padding=padding, dilation=dilation, groups=C_in, bias=False), | ||||
|       nn.Conv2d(C_in, C_out, kernel_size=1, padding=0, bias=False), | ||||
|       nn.BatchNorm2d(C_out, affine=affine, track_running_stats=track_running_stats), | ||||
|       ) | ||||
|  | ||||
|   def forward(self, x): | ||||
|     return self.op(x) | ||||
|  | ||||
|  | ||||
| class DualSepConv(nn.Module): | ||||
|      | ||||
|   def __init__(self, C_in, C_out, kernel_size, stride, padding, dilation, affine, track_running_stats=True): | ||||
|     super(DualSepConv, self).__init__() | ||||
|     self.op_a = SepConv(C_in, C_in , kernel_size, stride, padding, dilation, affine, track_running_stats) | ||||
|     self.op_b = SepConv(C_in, C_out, kernel_size, 1, padding, dilation, affine, track_running_stats) | ||||
|  | ||||
|   def forward(self, x): | ||||
|     x = self.op_a(x) | ||||
|     x = self.op_b(x) | ||||
|     return x | ||||
|  | ||||
|  | ||||
| class ResNetBasicblock(nn.Module): | ||||
|  | ||||
|   def __init__(self, inplanes, planes, stride, affine=True): | ||||
|     super(ResNetBasicblock, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     self.conv_a = ReLUConvBN(inplanes, planes, 3, stride, 1, 1, affine) | ||||
|     self.conv_b = ReLUConvBN(  planes, planes, 3,      1, 1, 1, affine) | ||||
|     if stride == 2: | ||||
|       self.downsample = nn.Sequential( | ||||
|                            nn.AvgPool2d(kernel_size=2, stride=2, padding=0), | ||||
|                            nn.Conv2d(inplanes, planes, kernel_size=1, stride=1, padding=0, bias=False)) | ||||
|     elif inplanes != planes: | ||||
|       self.downsample = ReLUConvBN(inplanes, planes, 1, 1, 0, 1, affine) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     self.in_dim  = inplanes | ||||
|     self.out_dim = planes | ||||
|     self.stride  = stride | ||||
|     self.num_conv = 2 | ||||
|  | ||||
|   def extra_repr(self): | ||||
|     string = '{name}(inC={in_dim}, outC={out_dim}, stride={stride})'.format(name=self.__class__.__name__, **self.__dict__) | ||||
|     return string | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|  | ||||
|     basicblock = self.conv_a(inputs) | ||||
|     basicblock = self.conv_b(basicblock) | ||||
|  | ||||
|     if self.downsample is not None: | ||||
|       residual = self.downsample(inputs) | ||||
|     else: | ||||
|       residual = inputs | ||||
|     return residual + basicblock | ||||
|  | ||||
|  | ||||
| class POOLING(nn.Module): | ||||
|  | ||||
|   def __init__(self, C_in, C_out, stride, mode, affine=True, track_running_stats=True): | ||||
|     super(POOLING, self).__init__() | ||||
|     if C_in == C_out: | ||||
|       self.preprocess = None | ||||
|     else: | ||||
|       self.preprocess = ReLUConvBN(C_in, C_out, 1, 1, 0, 1, affine, track_running_stats) | ||||
|     if mode == 'avg'  : self.op = nn.AvgPool2d(3, stride=stride, padding=1, count_include_pad=False) | ||||
|     elif mode == 'max': self.op = nn.MaxPool2d(3, stride=stride, padding=1) | ||||
|     else              : raise ValueError('Invalid mode={:} in POOLING'.format(mode)) | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.preprocess: x = self.preprocess(inputs) | ||||
|     else              : x = inputs | ||||
|     return self.op(x) | ||||
|  | ||||
|  | ||||
| class Identity(nn.Module): | ||||
|  | ||||
|   def __init__(self): | ||||
|     super(Identity, self).__init__() | ||||
|  | ||||
|   def forward(self, x): | ||||
|     return x | ||||
|  | ||||
|  | ||||
| class Zero(nn.Module): | ||||
|  | ||||
|   def __init__(self, C_in, C_out, stride): | ||||
|     super(Zero, self).__init__() | ||||
|     self.C_in   = C_in | ||||
|     self.C_out  = C_out | ||||
|     self.stride = stride | ||||
|     self.is_zero = True | ||||
|  | ||||
|   def forward(self, x): | ||||
|     if self.C_in == self.C_out: | ||||
|       if self.stride == 1: return x.mul(0.) | ||||
|       else               : return x[:,:,::self.stride,::self.stride].mul(0.) | ||||
|     else: | ||||
|       shape = list(x.shape) | ||||
|       shape[1] = self.C_out | ||||
|       zeros = x.new_zeros(shape, dtype=x.dtype, device=x.device) | ||||
|       return zeros | ||||
|  | ||||
|   def extra_repr(self): | ||||
|     return 'C_in={C_in}, C_out={C_out}, stride={stride}'.format(**self.__dict__) | ||||
|  | ||||
|  | ||||
| class FactorizedReduce(nn.Module): | ||||
|  | ||||
|   def __init__(self, C_in, C_out, stride, affine, track_running_stats): | ||||
|     super(FactorizedReduce, self).__init__() | ||||
|     self.stride = stride | ||||
|     self.C_in   = C_in   | ||||
|     self.C_out  = C_out   | ||||
|     self.relu   = nn.ReLU(inplace=False) | ||||
|     if stride == 2: | ||||
|       #assert C_out % 2 == 0, 'C_out : {:}'.format(C_out) | ||||
|       C_outs = [C_out // 2, C_out - C_out // 2] | ||||
|       self.convs = nn.ModuleList() | ||||
|       for i in range(2): | ||||
|         self.convs.append( nn.Conv2d(C_in, C_outs[i], 1, stride=stride, padding=0, bias=False) ) | ||||
|       self.pad = nn.ConstantPad2d((0, 1, 0, 1), 0) | ||||
|     elif stride == 1: | ||||
|       self.conv = nn.Conv2d(C_in, C_out, 1, stride=stride, padding=0, bias=False) | ||||
|     else: | ||||
|       raise ValueError('Invalid stride : {:}'.format(stride)) | ||||
|     self.bn = nn.BatchNorm2d(C_out, affine=affine, track_running_stats=track_running_stats) | ||||
|  | ||||
|   def forward(self, x): | ||||
|     if self.stride == 2: | ||||
|       x = self.relu(x) | ||||
|       y = self.pad(x) | ||||
|       out = torch.cat([self.convs[0](x), self.convs[1](y[:,:,1:,1:])], dim=1) | ||||
|     else: | ||||
|       out = self.conv(x) | ||||
|     out = self.bn(out) | ||||
|     return out | ||||
|  | ||||
|   def extra_repr(self): | ||||
|     return 'C_in={C_in}, C_out={C_out}, stride={stride}'.format(**self.__dict__) | ||||
|  | ||||
|  | ||||
| # Auto-ReID: Searching for a Part-Aware ConvNet for Person Re-Identification, ICCV 2019 | ||||
| class PartAwareOp(nn.Module): | ||||
|  | ||||
|   def __init__(self, C_in, C_out, stride, part=4): | ||||
|     super().__init__() | ||||
|     self.part   = 4 | ||||
|     self.hidden = C_in // 3 | ||||
|     self.avg_pool = nn.AdaptiveAvgPool2d(1) | ||||
|     self.local_conv_list = nn.ModuleList() | ||||
|     for i in range(self.part): | ||||
|       self.local_conv_list.append( | ||||
|             nn.Sequential(nn.ReLU(), nn.Conv2d(C_in, self.hidden, 1), nn.BatchNorm2d(self.hidden, affine=True)) | ||||
|             ) | ||||
|     self.W_K = nn.Linear(self.hidden, self.hidden) | ||||
|     self.W_Q = nn.Linear(self.hidden, self.hidden) | ||||
|  | ||||
|     if stride == 2  : self.last = FactorizedReduce(C_in + self.hidden, C_out, 2) | ||||
|     elif stride == 1: self.last = FactorizedReduce(C_in + self.hidden, C_out, 1) | ||||
|     else:             raise ValueError('Invalid Stride : {:}'.format(stride)) | ||||
|  | ||||
|   def forward(self, x): | ||||
|     batch, C, H, W = x.size() | ||||
|     assert H >= self.part, 'input size too small : {:} vs {:}'.format(x.shape, self.part) | ||||
|     IHs = [0] | ||||
|     for i in range(self.part): IHs.append( min(H, int((i+1)*(float(H)/self.part))) ) | ||||
|     local_feat_list = [] | ||||
|     for i in range(self.part): | ||||
|       feature = x[:, :, IHs[i]:IHs[i+1], :] | ||||
|       xfeax   = self.avg_pool(feature) | ||||
|       xfea    = self.local_conv_list[i]( xfeax ) | ||||
|       local_feat_list.append( xfea ) | ||||
|     part_feature = torch.cat(local_feat_list, dim=2).view(batch, -1, self.part) | ||||
|     part_feature = part_feature.transpose(1,2).contiguous() | ||||
|     part_K       = self.W_K(part_feature) | ||||
|     part_Q       = self.W_Q(part_feature).transpose(1,2).contiguous() | ||||
|     weight_att   = torch.bmm(part_K, part_Q) | ||||
|     attention    = torch.softmax(weight_att, dim=2) | ||||
|     aggreateF    = torch.bmm(attention, part_feature).transpose(1,2).contiguous() | ||||
|     features = [] | ||||
|     for i in range(self.part): | ||||
|       feature = aggreateF[:, :, i:i+1].expand(batch, self.hidden, IHs[i+1]-IHs[i]) | ||||
|       feature = feature.view(batch, self.hidden, IHs[i+1]-IHs[i], 1) | ||||
|       features.append( feature ) | ||||
|     features  = torch.cat(features, dim=2).expand(batch, self.hidden, H, W) | ||||
|     final_fea = torch.cat((x,features), dim=1) | ||||
|     outputs   = self.last( final_fea ) | ||||
|     return outputs | ||||
|  | ||||
|  | ||||
| # Searching for A Robust Neural Architecture in Four GPU Hours | ||||
| class GDAS_Reduction_Cell(nn.Module): | ||||
|  | ||||
|   def __init__(self, C_prev_prev, C_prev, C, reduction_prev, multiplier, affine, track_running_stats): | ||||
|     super(GDAS_Reduction_Cell, self).__init__() | ||||
|     if reduction_prev: | ||||
|       self.preprocess0 = FactorizedReduce(C_prev_prev, C, 2, affine, track_running_stats) | ||||
|     else: | ||||
|       self.preprocess0 = ReLUConvBN(C_prev_prev, C, 1, 1, 0, 1, affine, track_running_stats) | ||||
|     self.preprocess1 = ReLUConvBN(C_prev, C, 1, 1, 0, 1, affine, track_running_stats) | ||||
|     self.multiplier  = multiplier | ||||
|  | ||||
|     self.reduction = True | ||||
|     self.ops1 = nn.ModuleList( | ||||
|                   [nn.Sequential( | ||||
|                       nn.ReLU(inplace=False), | ||||
|                       nn.Conv2d(C, C, (1, 3), stride=(1, 2), padding=(0, 1), groups=8, bias=False), | ||||
|                       nn.Conv2d(C, C, (3, 1), stride=(2, 1), padding=(1, 0), groups=8, bias=False), | ||||
|                       nn.BatchNorm2d(C, affine=True), | ||||
|                       nn.ReLU(inplace=False), | ||||
|                       nn.Conv2d(C, C, 1, stride=1, padding=0, bias=False), | ||||
|                       nn.BatchNorm2d(C, affine=True)), | ||||
|                    nn.Sequential( | ||||
|                       nn.ReLU(inplace=False), | ||||
|                       nn.Conv2d(C, C, (1, 3), stride=(1, 2), padding=(0, 1), groups=8, bias=False), | ||||
|                       nn.Conv2d(C, C, (3, 1), stride=(2, 1), padding=(1, 0), groups=8, bias=False), | ||||
|                       nn.BatchNorm2d(C, affine=True), | ||||
|                       nn.ReLU(inplace=False), | ||||
|                       nn.Conv2d(C, C, 1, stride=1, padding=0, bias=False), | ||||
|                       nn.BatchNorm2d(C, affine=True))]) | ||||
|  | ||||
|     self.ops2 = nn.ModuleList( | ||||
|                   [nn.Sequential( | ||||
|                       nn.MaxPool2d(3, stride=1, padding=1), | ||||
|                       nn.BatchNorm2d(C, affine=True)), | ||||
|                    nn.Sequential( | ||||
|                       nn.MaxPool2d(3, stride=2, padding=1), | ||||
|                       nn.BatchNorm2d(C, affine=True))]) | ||||
|  | ||||
|   def forward(self, s0, s1, drop_prob = -1): | ||||
|     s0 = self.preprocess0(s0) | ||||
|     s1 = self.preprocess1(s1) | ||||
|  | ||||
|     X0 = self.ops1[0] (s0) | ||||
|     X1 = self.ops1[1] (s1) | ||||
|     if self.training and drop_prob > 0.: | ||||
|       X0, X1 = drop_path(X0, drop_prob), drop_path(X1, drop_prob) | ||||
|  | ||||
|     #X2 = self.ops2[0] (X0+X1) | ||||
|     X2 = self.ops2[0] (s0) | ||||
|     X3 = self.ops2[1] (s1) | ||||
|     if self.training and drop_prob > 0.: | ||||
|       X2, X3 = drop_path(X2, drop_prob), drop_path(X3, drop_prob) | ||||
|     return torch.cat([X0, X1, X2, X3], dim=1) | ||||
							
								
								
									
										24
									
								
								graph_dit/naswot/models/cell_searchs/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								graph_dit/naswot/models/cell_searchs/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| # The macro structure is defined in NAS-Bench-201 | ||||
| from .search_model_darts    import TinyNetworkDarts | ||||
| from .search_model_gdas     import TinyNetworkGDAS | ||||
| from .search_model_setn     import TinyNetworkSETN | ||||
| from .search_model_enas     import TinyNetworkENAS | ||||
| from .search_model_random   import TinyNetworkRANDOM | ||||
| from .genotypes             import Structure as CellStructure, architectures as CellArchitectures | ||||
| # NASNet-based macro structure | ||||
| from .search_model_gdas_nasnet import NASNetworkGDAS | ||||
| from .search_model_darts_nasnet import NASNetworkDARTS | ||||
|  | ||||
|  | ||||
| nas201_super_nets = {'DARTS-V1': TinyNetworkDarts, | ||||
|                      "DARTS-V2": TinyNetworkDarts, | ||||
|                      "GDAS": TinyNetworkGDAS, | ||||
|                      "SETN": TinyNetworkSETN, | ||||
|                      "ENAS": TinyNetworkENAS, | ||||
|                      "RANDOM": TinyNetworkRANDOM} | ||||
|  | ||||
| nasnet_super_nets = {"GDAS": NASNetworkGDAS, | ||||
|                      "DARTS": NASNetworkDARTS} | ||||
							
								
								
									
										12
									
								
								graph_dit/naswot/models/cell_searchs/_test_module.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								graph_dit/naswot/models/cell_searchs/_test_module.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| import torch | ||||
| from search_model_enas_utils import Controller | ||||
|  | ||||
| def main(): | ||||
|   controller = Controller(6, 4) | ||||
|   predictions = controller() | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|   main() | ||||
							
								
								
									
										199
									
								
								graph_dit/naswot/models/cell_searchs/genotypes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								graph_dit/naswot/models/cell_searchs/genotypes.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,199 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| from copy import deepcopy | ||||
|  | ||||
|  | ||||
|  | ||||
| def get_combination(space, num): | ||||
|   combs = [] | ||||
|   for i in range(num): | ||||
|     if i == 0: | ||||
|       for func in space: | ||||
|         combs.append( [(func, i)] ) | ||||
|     else: | ||||
|       new_combs = [] | ||||
|       for string in combs: | ||||
|         for func in space: | ||||
|           xstring = string + [(func, i)] | ||||
|           new_combs.append( xstring ) | ||||
|       combs = new_combs | ||||
|   return combs | ||||
|    | ||||
|  | ||||
|  | ||||
| class Structure: | ||||
|  | ||||
|   def __init__(self, genotype): | ||||
|     assert isinstance(genotype, list) or isinstance(genotype, tuple), 'invalid class of genotype : {:}'.format(type(genotype)) | ||||
|     self.node_num = len(genotype) + 1 | ||||
|     self.nodes    = [] | ||||
|     self.node_N   = [] | ||||
|     for idx, node_info in enumerate(genotype): | ||||
|       assert isinstance(node_info, list) or isinstance(node_info, tuple), 'invalid class of node_info : {:}'.format(type(node_info)) | ||||
|       assert len(node_info) >= 1, 'invalid length : {:}'.format(len(node_info)) | ||||
|       for node_in in node_info: | ||||
|         assert isinstance(node_in, list) or isinstance(node_in, tuple), 'invalid class of in-node : {:}'.format(type(node_in)) | ||||
|         assert len(node_in) == 2 and node_in[1] <= idx, 'invalid in-node : {:}'.format(node_in) | ||||
|       self.node_N.append( len(node_info) ) | ||||
|       self.nodes.append( tuple(deepcopy(node_info)) ) | ||||
|  | ||||
|   def tolist(self, remove_str): | ||||
|     # convert this class to the list, if remove_str is 'none', then remove the 'none' operation. | ||||
|     # note that we re-order the input node in this function | ||||
|     # return the-genotype-list and success [if unsuccess, it is not a connectivity] | ||||
|     genotypes = [] | ||||
|     for node_info in self.nodes: | ||||
|       node_info = list( node_info ) | ||||
|       node_info = sorted(node_info, key=lambda x: (x[1], x[0])) | ||||
|       node_info = tuple(filter(lambda x: x[0] != remove_str, node_info)) | ||||
|       if len(node_info) == 0: return None, False | ||||
|       genotypes.append( node_info ) | ||||
|     return genotypes, True | ||||
|  | ||||
|   def node(self, index): | ||||
|     assert index > 0 and index <= len(self), 'invalid index={:} < {:}'.format(index, len(self)) | ||||
|     return self.nodes[index] | ||||
|  | ||||
|   def tostr(self): | ||||
|     strings = [] | ||||
|     for node_info in self.nodes: | ||||
|       string = '|'.join([x[0]+'~{:}'.format(x[1]) for x in node_info]) | ||||
|       string = '|{:}|'.format(string) | ||||
|       strings.append( string ) | ||||
|     return '+'.join(strings) | ||||
|  | ||||
|   def check_valid(self): | ||||
|     nodes = {0: True} | ||||
|     for i, node_info in enumerate(self.nodes): | ||||
|       sums = [] | ||||
|       for op, xin in node_info: | ||||
|         if op == 'none' or nodes[xin] is False: x = False | ||||
|         else: x = True | ||||
|         sums.append( x ) | ||||
|       nodes[i+1] = sum(sums) > 0 | ||||
|     return nodes[len(self.nodes)] | ||||
|  | ||||
|   def to_unique_str(self, consider_zero=False): | ||||
|     # this is used to identify the isomorphic cell, which rerquires the prior knowledge of operation | ||||
|     # two operations are special, i.e., none and skip_connect | ||||
|     nodes = {0: '0'} | ||||
|     for i_node, node_info in enumerate(self.nodes): | ||||
|       cur_node = [] | ||||
|       for op, xin in node_info: | ||||
|         if consider_zero is None: | ||||
|           x = '('+nodes[xin]+')' + '@{:}'.format(op) | ||||
|         elif consider_zero: | ||||
|           if op == 'none' or nodes[xin] == '#': x = '#' # zero | ||||
|           elif op == 'skip_connect': x = nodes[xin] | ||||
|           else: x = '('+nodes[xin]+')' + '@{:}'.format(op) | ||||
|         else: | ||||
|           if op == 'skip_connect': x = nodes[xin] | ||||
|           else: x = '('+nodes[xin]+')' + '@{:}'.format(op) | ||||
|         cur_node.append(x) | ||||
|       nodes[i_node+1] = '+'.join( sorted(cur_node) ) | ||||
|     return nodes[ len(self.nodes) ] | ||||
|  | ||||
|   def check_valid_op(self, op_names): | ||||
|     for node_info in self.nodes: | ||||
|       for inode_edge in node_info: | ||||
|         #assert inode_edge[0] in op_names, 'invalid op-name : {:}'.format(inode_edge[0]) | ||||
|         if inode_edge[0] not in op_names: return False | ||||
|     return True | ||||
|  | ||||
|   def __repr__(self): | ||||
|     return ('{name}({node_num} nodes with {node_info})'.format(name=self.__class__.__name__, node_info=self.tostr(), **self.__dict__)) | ||||
|  | ||||
|   def __len__(self): | ||||
|     return len(self.nodes) + 1 | ||||
|  | ||||
|   def __getitem__(self, index): | ||||
|     return self.nodes[index] | ||||
|  | ||||
|   @staticmethod | ||||
|   def str2structure(xstr): | ||||
|     assert isinstance(xstr, str), 'must take string (not {:}) as input'.format(type(xstr)) | ||||
|     nodestrs = xstr.split('+') | ||||
|     genotypes = [] | ||||
|     for i, node_str in enumerate(nodestrs): | ||||
|       inputs = list(filter(lambda x: x != '', node_str.split('|'))) | ||||
|       for xinput in inputs: assert len(xinput.split('~')) == 2, 'invalid input length : {:}'.format(xinput) | ||||
|       inputs = ( xi.split('~') for xi in inputs ) | ||||
|       input_infos = tuple( (op, int(IDX)) for (op, IDX) in inputs) | ||||
|       genotypes.append( input_infos ) | ||||
|     return Structure( genotypes ) | ||||
|  | ||||
|   @staticmethod | ||||
|   def str2fullstructure(xstr, default_name='none'): | ||||
|     assert isinstance(xstr, str), 'must take string (not {:}) as input'.format(type(xstr)) | ||||
|     nodestrs = xstr.split('+') | ||||
|     genotypes = [] | ||||
|     for i, node_str in enumerate(nodestrs): | ||||
|       inputs = list(filter(lambda x: x != '', node_str.split('|'))) | ||||
|       for xinput in inputs: assert len(xinput.split('~')) == 2, 'invalid input length : {:}'.format(xinput) | ||||
|       inputs = ( xi.split('~') for xi in inputs ) | ||||
|       input_infos = list( (op, int(IDX)) for (op, IDX) in inputs) | ||||
|       all_in_nodes= list(x[1] for x in input_infos) | ||||
|       for j in range(i): | ||||
|         if j not in all_in_nodes: input_infos.append((default_name, j)) | ||||
|       node_info = sorted(input_infos, key=lambda x: (x[1], x[0])) | ||||
|       genotypes.append( tuple(node_info) ) | ||||
|     return Structure( genotypes ) | ||||
|  | ||||
|   @staticmethod | ||||
|   def gen_all(search_space, num, return_ori): | ||||
|     assert isinstance(search_space, list) or isinstance(search_space, tuple), 'invalid class of search-space : {:}'.format(type(search_space)) | ||||
|     assert num >= 2, 'There should be at least two nodes in a neural cell instead of {:}'.format(num) | ||||
|     all_archs = get_combination(search_space, 1) | ||||
|     for i, arch in enumerate(all_archs): | ||||
|       all_archs[i] = [ tuple(arch) ] | ||||
|    | ||||
|     for inode in range(2, num): | ||||
|       cur_nodes = get_combination(search_space, inode) | ||||
|       new_all_archs = [] | ||||
|       for previous_arch in all_archs: | ||||
|         for cur_node in cur_nodes: | ||||
|           new_all_archs.append( previous_arch + [tuple(cur_node)] ) | ||||
|       all_archs = new_all_archs | ||||
|     if return_ori: | ||||
|       return all_archs | ||||
|     else: | ||||
|       return [Structure(x) for x in all_archs] | ||||
|  | ||||
|  | ||||
|  | ||||
| ResNet_CODE = Structure( | ||||
|   [(('nor_conv_3x3', 0), ), # node-1  | ||||
|    (('nor_conv_3x3', 1), ), # node-2 | ||||
|    (('skip_connect', 0), ('skip_connect', 2))] # node-3 | ||||
|   ) | ||||
|  | ||||
| AllConv3x3_CODE = Structure( | ||||
|   [(('nor_conv_3x3', 0), ), # node-1  | ||||
|    (('nor_conv_3x3', 0), ('nor_conv_3x3', 1)), # node-2 | ||||
|    (('nor_conv_3x3', 0), ('nor_conv_3x3', 1), ('nor_conv_3x3', 2))] # node-3 | ||||
|   ) | ||||
|  | ||||
| AllFull_CODE = Structure( | ||||
|   [(('skip_connect', 0), ('nor_conv_1x1', 0), ('nor_conv_3x3', 0), ('avg_pool_3x3', 0)), # node-1  | ||||
|    (('skip_connect', 0), ('nor_conv_1x1', 0), ('nor_conv_3x3', 0), ('avg_pool_3x3', 0), ('skip_connect', 1), ('nor_conv_1x1', 1), ('nor_conv_3x3', 1), ('avg_pool_3x3', 1)), # node-2 | ||||
|    (('skip_connect', 0), ('nor_conv_1x1', 0), ('nor_conv_3x3', 0), ('avg_pool_3x3', 0), ('skip_connect', 1), ('nor_conv_1x1', 1), ('nor_conv_3x3', 1), ('avg_pool_3x3', 1), ('skip_connect', 2), ('nor_conv_1x1', 2), ('nor_conv_3x3', 2), ('avg_pool_3x3', 2))] # node-3 | ||||
|   ) | ||||
|  | ||||
| AllConv1x1_CODE = Structure( | ||||
|   [(('nor_conv_1x1', 0), ), # node-1  | ||||
|    (('nor_conv_1x1', 0), ('nor_conv_1x1', 1)), # node-2 | ||||
|    (('nor_conv_1x1', 0), ('nor_conv_1x1', 1), ('nor_conv_1x1', 2))] # node-3 | ||||
|   ) | ||||
|  | ||||
| AllIdentity_CODE = Structure( | ||||
|   [(('skip_connect', 0), ), # node-1  | ||||
|    (('skip_connect', 0), ('skip_connect', 1)), # node-2 | ||||
|    (('skip_connect', 0), ('skip_connect', 1), ('skip_connect', 2))] # node-3 | ||||
|   ) | ||||
|  | ||||
| architectures = {'resnet'  : ResNet_CODE, | ||||
|                  'all_c3x3': AllConv3x3_CODE, | ||||
|                  'all_c1x1': AllConv1x1_CODE, | ||||
|                  'all_idnt': AllIdentity_CODE, | ||||
|                  'all_full': AllFull_CODE} | ||||
							
								
								
									
										197
									
								
								graph_dit/naswot/models/cell_searchs/search_cells.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								graph_dit/naswot/models/cell_searchs/search_cells.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,197 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| import math, random, torch | ||||
| import warnings | ||||
| import torch.nn as nn | ||||
| import torch.nn.functional as F | ||||
| from copy import deepcopy | ||||
| from ..cell_operations import OPS | ||||
|  | ||||
|  | ||||
| # This module is used for NAS-Bench-201, represents a small search space with a complete DAG | ||||
| class NAS201SearchCell(nn.Module): | ||||
|  | ||||
|   def __init__(self, C_in, C_out, stride, max_nodes, op_names, affine=False, track_running_stats=True): | ||||
|     super(NAS201SearchCell, self).__init__() | ||||
|  | ||||
|     self.op_names  = deepcopy(op_names) | ||||
|     self.edges     = nn.ModuleDict() | ||||
|     self.max_nodes = max_nodes | ||||
|     self.in_dim    = C_in | ||||
|     self.out_dim   = C_out | ||||
|     for i in range(1, max_nodes): | ||||
|       for j in range(i): | ||||
|         node_str = '{:}<-{:}'.format(i, j) | ||||
|         if j == 0: | ||||
|           xlists = [OPS[op_name](C_in , C_out, stride, affine, track_running_stats) for op_name in op_names] | ||||
|         else: | ||||
|           xlists = [OPS[op_name](C_in , C_out,      1, affine, track_running_stats) for op_name in op_names] | ||||
|         self.edges[ node_str ] = nn.ModuleList( xlists ) | ||||
|     self.edge_keys  = sorted(list(self.edges.keys())) | ||||
|     self.edge2index = {key:i for i, key in enumerate(self.edge_keys)} | ||||
|     self.num_edges  = len(self.edges) | ||||
|  | ||||
|   def extra_repr(self): | ||||
|     string = 'info :: {max_nodes} nodes, inC={in_dim}, outC={out_dim}'.format(**self.__dict__) | ||||
|     return string | ||||
|  | ||||
|   def forward(self, inputs, weightss): | ||||
|     nodes = [inputs] | ||||
|     for i in range(1, self.max_nodes): | ||||
|       inter_nodes = [] | ||||
|       for j in range(i): | ||||
|         node_str = '{:}<-{:}'.format(i, j) | ||||
|         weights  = weightss[ self.edge2index[node_str] ] | ||||
|         inter_nodes.append( sum( layer(nodes[j]) * w for layer, w in zip(self.edges[node_str], weights) ) ) | ||||
|       nodes.append( sum(inter_nodes) ) | ||||
|     return nodes[-1] | ||||
|  | ||||
|   # GDAS | ||||
|   def forward_gdas(self, inputs, hardwts, index): | ||||
|     nodes   = [inputs] | ||||
|     for i in range(1, self.max_nodes): | ||||
|       inter_nodes = [] | ||||
|       for j in range(i): | ||||
|         node_str = '{:}<-{:}'.format(i, j) | ||||
|         weights  = hardwts[ self.edge2index[node_str] ] | ||||
|         argmaxs  = index[ self.edge2index[node_str] ].item() | ||||
|         weigsum  = sum( weights[_ie] * edge(nodes[j]) if _ie == argmaxs else weights[_ie] for _ie, edge in enumerate(self.edges[node_str]) ) | ||||
|         inter_nodes.append( weigsum ) | ||||
|       nodes.append( sum(inter_nodes) ) | ||||
|     return nodes[-1] | ||||
|  | ||||
|   # joint | ||||
|   def forward_joint(self, inputs, weightss): | ||||
|     nodes = [inputs] | ||||
|     for i in range(1, self.max_nodes): | ||||
|       inter_nodes = [] | ||||
|       for j in range(i): | ||||
|         node_str = '{:}<-{:}'.format(i, j) | ||||
|         weights  = weightss[ self.edge2index[node_str] ] | ||||
|         #aggregation = sum( layer(nodes[j]) * w for layer, w in zip(self.edges[node_str], weights) ) / weights.numel() | ||||
|         aggregation = sum( layer(nodes[j]) * w for layer, w in zip(self.edges[node_str], weights) ) | ||||
|         inter_nodes.append( aggregation ) | ||||
|       nodes.append( sum(inter_nodes) ) | ||||
|     return nodes[-1] | ||||
|  | ||||
|   # uniform random sampling per iteration, SETN | ||||
|   def forward_urs(self, inputs): | ||||
|     nodes = [inputs] | ||||
|     for i in range(1, self.max_nodes): | ||||
|       while True: # to avoid select zero for all ops | ||||
|         sops, has_non_zero = [], False | ||||
|         for j in range(i): | ||||
|           node_str   = '{:}<-{:}'.format(i, j) | ||||
|           candidates = self.edges[node_str] | ||||
|           select_op  = random.choice(candidates) | ||||
|           sops.append( select_op ) | ||||
|           if not hasattr(select_op, 'is_zero') or select_op.is_zero is False: has_non_zero=True | ||||
|         if has_non_zero: break | ||||
|       inter_nodes = [] | ||||
|       for j, select_op in enumerate(sops): | ||||
|         inter_nodes.append( select_op(nodes[j]) ) | ||||
|       nodes.append( sum(inter_nodes) ) | ||||
|     return nodes[-1] | ||||
|  | ||||
|   # select the argmax | ||||
|   def forward_select(self, inputs, weightss): | ||||
|     nodes = [inputs] | ||||
|     for i in range(1, self.max_nodes): | ||||
|       inter_nodes = [] | ||||
|       for j in range(i): | ||||
|         node_str = '{:}<-{:}'.format(i, j) | ||||
|         weights  = weightss[ self.edge2index[node_str] ] | ||||
|         inter_nodes.append( self.edges[node_str][ weights.argmax().item() ]( nodes[j] ) ) | ||||
|         #inter_nodes.append( sum( layer(nodes[j]) * w for layer, w in zip(self.edges[node_str], weights) ) ) | ||||
|       nodes.append( sum(inter_nodes) ) | ||||
|     return nodes[-1] | ||||
|  | ||||
|   # forward with a specific structure | ||||
|   def forward_dynamic(self, inputs, structure): | ||||
|     nodes = [inputs] | ||||
|     for i in range(1, self.max_nodes): | ||||
|       cur_op_node = structure.nodes[i-1] | ||||
|       inter_nodes = [] | ||||
|       for op_name, j in cur_op_node: | ||||
|         node_str = '{:}<-{:}'.format(i, j) | ||||
|         op_index = self.op_names.index( op_name ) | ||||
|         inter_nodes.append( self.edges[node_str][op_index]( nodes[j] ) ) | ||||
|       nodes.append( sum(inter_nodes) ) | ||||
|     return nodes[-1] | ||||
|  | ||||
|  | ||||
|  | ||||
| class MixedOp(nn.Module): | ||||
|  | ||||
|   def __init__(self, space, C, stride, affine, track_running_stats): | ||||
|     super(MixedOp, self).__init__() | ||||
|     self._ops = nn.ModuleList() | ||||
|     for primitive in space: | ||||
|       op = OPS[primitive](C, C, stride, affine, track_running_stats) | ||||
|       self._ops.append(op) | ||||
|  | ||||
|   def forward_gdas(self, x, weights, index): | ||||
|     return self._ops[index](x) * weights[index] | ||||
|  | ||||
|   def forward_darts(self, x, weights): | ||||
|     return sum(w * op(x) for w, op in zip(weights, self._ops)) | ||||
|  | ||||
|  | ||||
| # Learning Transferable Architectures for Scalable Image Recognition, CVPR 2018 | ||||
| class NASNetSearchCell(nn.Module): | ||||
|  | ||||
|   def __init__(self, space, steps, multiplier, C_prev_prev, C_prev, C, reduction, reduction_prev, affine, track_running_stats): | ||||
|     super(NASNetSearchCell, self).__init__() | ||||
|     self.reduction = reduction | ||||
|     self.op_names  = deepcopy(space) | ||||
|     if reduction_prev: self.preprocess0 = OPS['skip_connect'](C_prev_prev, C, 2, affine, track_running_stats) | ||||
|     else             : self.preprocess0 = OPS['nor_conv_1x1'](C_prev_prev, C, 1, affine, track_running_stats) | ||||
|     self.preprocess1 = OPS['nor_conv_1x1'](C_prev, C, 1, affine, track_running_stats) | ||||
|     self._steps = steps | ||||
|     self._multiplier = multiplier | ||||
|  | ||||
|     self._ops = nn.ModuleList() | ||||
|     self.edges     = nn.ModuleDict() | ||||
|     for i in range(self._steps): | ||||
|       for j in range(2+i): | ||||
|         node_str = '{:}<-{:}'.format(i, j)  # indicate the edge from node-(j) to node-(i+2) | ||||
|         stride = 2 if reduction and j < 2 else 1 | ||||
|         op = MixedOp(space, C, stride, affine, track_running_stats) | ||||
|         self.edges[ node_str ] = op | ||||
|     self.edge_keys  = sorted(list(self.edges.keys())) | ||||
|     self.edge2index = {key:i for i, key in enumerate(self.edge_keys)} | ||||
|     self.num_edges  = len(self.edges) | ||||
|  | ||||
|   def forward_gdas(self, s0, s1, weightss, indexs): | ||||
|     s0 = self.preprocess0(s0) | ||||
|     s1 = self.preprocess1(s1) | ||||
|  | ||||
|     states = [s0, s1] | ||||
|     for i in range(self._steps): | ||||
|       clist = [] | ||||
|       for j, h in enumerate(states): | ||||
|         node_str = '{:}<-{:}'.format(i, j) | ||||
|         op = self.edges[ node_str ] | ||||
|         weights = weightss[ self.edge2index[node_str] ] | ||||
|         index   = indexs[ self.edge2index[node_str] ].item() | ||||
|         clist.append( op.forward_gdas(h, weights, index) ) | ||||
|       states.append( sum(clist) ) | ||||
|  | ||||
|     return torch.cat(states[-self._multiplier:], dim=1) | ||||
|  | ||||
|   def forward_darts(self, s0, s1, weightss): | ||||
|     s0 = self.preprocess0(s0) | ||||
|     s1 = self.preprocess1(s1) | ||||
|  | ||||
|     states = [s0, s1] | ||||
|     for i in range(self._steps): | ||||
|       clist = [] | ||||
|       for j, h in enumerate(states): | ||||
|         node_str = '{:}<-{:}'.format(i, j) | ||||
|         op = self.edges[ node_str ] | ||||
|         weights = weightss[ self.edge2index[node_str] ] | ||||
|         clist.append( op.forward_darts(h, weights) ) | ||||
|       states.append( sum(clist) ) | ||||
|  | ||||
|     return torch.cat(states[-self._multiplier:], dim=1) | ||||
							
								
								
									
										97
									
								
								graph_dit/naswot/models/cell_searchs/search_model_darts.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								graph_dit/naswot/models/cell_searchs/search_model_darts.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ######################################################## | ||||
| # DARTS: Differentiable Architecture Search, ICLR 2019 # | ||||
| ######################################################## | ||||
| import torch | ||||
| import torch.nn as nn | ||||
| from copy import deepcopy | ||||
| from ..cell_operations import ResNetBasicblock | ||||
| from .search_cells     import NAS201SearchCell as SearchCell | ||||
| from .genotypes        import Structure | ||||
|  | ||||
|  | ||||
| class TinyNetworkDarts(nn.Module): | ||||
|  | ||||
|   def __init__(self, C, N, max_nodes, num_classes, search_space, affine, track_running_stats): | ||||
|     super(TinyNetworkDarts, self).__init__() | ||||
|     self._C        = C | ||||
|     self._layerN   = N | ||||
|     self.max_nodes = max_nodes | ||||
|     self.stem = nn.Sequential( | ||||
|                     nn.Conv2d(3, C, kernel_size=3, padding=1, bias=False), | ||||
|                     nn.BatchNorm2d(C)) | ||||
|    | ||||
|     layer_channels   = [C    ] * N + [C*2 ] + [C*2  ] * N + [C*4 ] + [C*4  ] * N     | ||||
|     layer_reductions = [False] * N + [True] + [False] * N + [True] + [False] * N | ||||
|  | ||||
|     C_prev, num_edge, edge2index = C, None, None | ||||
|     self.cells = nn.ModuleList() | ||||
|     for index, (C_curr, reduction) in enumerate(zip(layer_channels, layer_reductions)): | ||||
|       if reduction: | ||||
|         cell = ResNetBasicblock(C_prev, C_curr, 2) | ||||
|       else: | ||||
|         cell = SearchCell(C_prev, C_curr, 1, max_nodes, search_space, affine, track_running_stats) | ||||
|         if num_edge is None: num_edge, edge2index = cell.num_edges, cell.edge2index | ||||
|         else: assert num_edge == cell.num_edges and edge2index == cell.edge2index, 'invalid {:} vs. {:}.'.format(num_edge, cell.num_edges) | ||||
|       self.cells.append( cell ) | ||||
|       C_prev = cell.out_dim | ||||
|     self.op_names   = deepcopy( search_space ) | ||||
|     self._Layer     = len(self.cells) | ||||
|     self.edge2index = edge2index | ||||
|     self.lastact    = nn.Sequential(nn.BatchNorm2d(C_prev), nn.ReLU(inplace=True)) | ||||
|     self.global_pooling = nn.AdaptiveAvgPool2d(1) | ||||
|     self.classifier = nn.Linear(C_prev, num_classes) | ||||
|     self.arch_parameters = nn.Parameter( 1e-3*torch.randn(num_edge, len(search_space)) ) | ||||
|  | ||||
|   def get_weights(self): | ||||
|     xlist = list( self.stem.parameters() ) + list( self.cells.parameters() ) | ||||
|     xlist+= list( self.lastact.parameters() ) + list( self.global_pooling.parameters() ) | ||||
|     xlist+= list( self.classifier.parameters() ) | ||||
|     return xlist | ||||
|  | ||||
|   def get_alphas(self): | ||||
|     return [self.arch_parameters] | ||||
|  | ||||
|   def show_alphas(self): | ||||
|     with torch.no_grad(): | ||||
|       return 'arch-parameters :\n{:}'.format( nn.functional.softmax(self.arch_parameters, dim=-1).cpu() ) | ||||
|  | ||||
|   def get_message(self): | ||||
|     string = self.extra_repr() | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       string += '\n {:02d}/{:02d} :: {:}'.format(i, len(self.cells), cell.extra_repr()) | ||||
|     return string | ||||
|  | ||||
|   def extra_repr(self): | ||||
|     return ('{name}(C={_C}, Max-Nodes={max_nodes}, N={_layerN}, L={_Layer})'.format(name=self.__class__.__name__, **self.__dict__)) | ||||
|  | ||||
|   def genotype(self): | ||||
|     genotypes = [] | ||||
|     for i in range(1, self.max_nodes): | ||||
|       xlist = [] | ||||
|       for j in range(i): | ||||
|         node_str = '{:}<-{:}'.format(i, j) | ||||
|         with torch.no_grad(): | ||||
|           weights = self.arch_parameters[ self.edge2index[node_str] ] | ||||
|           op_name = self.op_names[ weights.argmax().item() ] | ||||
|         xlist.append((op_name, j)) | ||||
|       genotypes.append( tuple(xlist) ) | ||||
|     return Structure( genotypes ) | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     alphas  = nn.functional.softmax(self.arch_parameters, dim=-1) | ||||
|  | ||||
|     feature = self.stem(inputs) | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       if isinstance(cell, SearchCell): | ||||
|         feature = cell(feature, alphas) | ||||
|       else: | ||||
|         feature = cell(feature) | ||||
|  | ||||
|     out = self.lastact(feature) | ||||
|     out = self.global_pooling( out ) | ||||
|     out = out.view(out.size(0), -1) | ||||
|     logits = self.classifier(out) | ||||
|  | ||||
|     return out, logits | ||||
| @@ -0,0 +1,108 @@ | ||||
| #################### | ||||
| # DARTS, ICLR 2019 # | ||||
| #################### | ||||
| import torch | ||||
| import torch.nn as nn | ||||
| from copy import deepcopy | ||||
| from typing import List, Text, Dict | ||||
| from .search_cells import NASNetSearchCell as SearchCell | ||||
|  | ||||
|  | ||||
| # The macro structure is based on NASNet | ||||
| class NASNetworkDARTS(nn.Module): | ||||
|  | ||||
|   def __init__(self, C: int, N: int, steps: int, multiplier: int, stem_multiplier: int, | ||||
|                num_classes: int, search_space: List[Text], affine: bool, track_running_stats: bool): | ||||
|     super(NASNetworkDARTS, self).__init__() | ||||
|     self._C        = C | ||||
|     self._layerN   = N | ||||
|     self._steps    = steps | ||||
|     self._multiplier = multiplier | ||||
|     self.stem = nn.Sequential( | ||||
|                     nn.Conv2d(3, C*stem_multiplier, kernel_size=3, padding=1, bias=False), | ||||
|                     nn.BatchNorm2d(C*stem_multiplier)) | ||||
|    | ||||
|     # config for each layer | ||||
|     layer_channels   = [C    ] * N + [C*2 ] + [C*2  ] * (N-1) + [C*4 ] + [C*4  ] * (N-1) | ||||
|     layer_reductions = [False] * N + [True] + [False] * (N-1) + [True] + [False] * (N-1) | ||||
|  | ||||
|     num_edge, edge2index = None, None | ||||
|     C_prev_prev, C_prev, C_curr, reduction_prev = C*stem_multiplier, C*stem_multiplier, C, False | ||||
|  | ||||
|     self.cells = nn.ModuleList() | ||||
|     for index, (C_curr, reduction) in enumerate(zip(layer_channels, layer_reductions)): | ||||
|       cell = SearchCell(search_space, steps, multiplier, C_prev_prev, C_prev, C_curr, reduction, reduction_prev, affine, track_running_stats) | ||||
|       if num_edge is None: num_edge, edge2index = cell.num_edges, cell.edge2index | ||||
|       else: assert num_edge == cell.num_edges and edge2index == cell.edge2index, 'invalid {:} vs. {:}.'.format(num_edge, cell.num_edges) | ||||
|       self.cells.append( cell ) | ||||
|       C_prev_prev, C_prev, reduction_prev = C_prev, multiplier*C_curr, reduction | ||||
|     self.op_names   = deepcopy( search_space ) | ||||
|     self._Layer     = len(self.cells) | ||||
|     self.edge2index = edge2index | ||||
|     self.lastact    = nn.Sequential(nn.BatchNorm2d(C_prev), nn.ReLU(inplace=True)) | ||||
|     self.global_pooling = nn.AdaptiveAvgPool2d(1) | ||||
|     self.classifier = nn.Linear(C_prev, num_classes) | ||||
|     self.arch_normal_parameters = nn.Parameter( 1e-3*torch.randn(num_edge, len(search_space)) ) | ||||
|     self.arch_reduce_parameters = nn.Parameter( 1e-3*torch.randn(num_edge, len(search_space)) ) | ||||
|  | ||||
|   def get_weights(self) -> List[torch.nn.Parameter]: | ||||
|     xlist = list( self.stem.parameters() ) + list( self.cells.parameters() ) | ||||
|     xlist+= list( self.lastact.parameters() ) + list( self.global_pooling.parameters() ) | ||||
|     xlist+= list( self.classifier.parameters() ) | ||||
|     return xlist | ||||
|  | ||||
|   def get_alphas(self) -> List[torch.nn.Parameter]: | ||||
|     return [self.arch_normal_parameters, self.arch_reduce_parameters] | ||||
|  | ||||
|   def show_alphas(self) -> Text: | ||||
|     with torch.no_grad(): | ||||
|       A = 'arch-normal-parameters :\n{:}'.format( nn.functional.softmax(self.arch_normal_parameters, dim=-1).cpu() ) | ||||
|       B = 'arch-reduce-parameters :\n{:}'.format( nn.functional.softmax(self.arch_reduce_parameters, dim=-1).cpu() ) | ||||
|     return '{:}\n{:}'.format(A, B) | ||||
|  | ||||
|   def get_message(self) -> Text: | ||||
|     string = self.extra_repr() | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       string += '\n {:02d}/{:02d} :: {:}'.format(i, len(self.cells), cell.extra_repr()) | ||||
|     return string | ||||
|  | ||||
|   def extra_repr(self) -> Text: | ||||
|     return ('{name}(C={_C}, N={_layerN}, steps={_steps}, multiplier={_multiplier}, L={_Layer})'.format(name=self.__class__.__name__, **self.__dict__)) | ||||
|  | ||||
|   def genotype(self) -> Dict[Text, List]: | ||||
|     def _parse(weights): | ||||
|       gene = [] | ||||
|       for i in range(self._steps): | ||||
|         edges = [] | ||||
|         for j in range(2+i): | ||||
|           node_str = '{:}<-{:}'.format(i, j) | ||||
|           ws = weights[ self.edge2index[node_str] ] | ||||
|           for k, op_name in enumerate(self.op_names): | ||||
|             if op_name == 'none': continue | ||||
|             edges.append( (op_name, j, ws[k]) ) | ||||
|         edges = sorted(edges, key=lambda x: -x[-1]) | ||||
|         selected_edges = edges[:2] | ||||
|         gene.append( tuple(selected_edges) ) | ||||
|       return gene | ||||
|     with torch.no_grad(): | ||||
|       gene_normal = _parse(torch.softmax(self.arch_normal_parameters, dim=-1).cpu().numpy()) | ||||
|       gene_reduce = _parse(torch.softmax(self.arch_reduce_parameters, dim=-1).cpu().numpy()) | ||||
|     return {'normal': gene_normal, 'normal_concat': list(range(2+self._steps-self._multiplier, self._steps+2)), | ||||
|             'reduce': gene_reduce, 'reduce_concat': list(range(2+self._steps-self._multiplier, self._steps+2))} | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|  | ||||
|     normal_w = nn.functional.softmax(self.arch_normal_parameters, dim=1) | ||||
|     reduce_w = nn.functional.softmax(self.arch_reduce_parameters, dim=1) | ||||
|  | ||||
|     s0 = s1 = self.stem(inputs) | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       if cell.reduction: ww = reduce_w | ||||
|       else             : ww = normal_w | ||||
|       s0, s1 = s1, cell.forward_darts(s0, s1, ww) | ||||
|     out = self.lastact(s1) | ||||
|     out = self.global_pooling( out ) | ||||
|     out = out.view(out.size(0), -1) | ||||
|     logits = self.classifier(out) | ||||
|  | ||||
|     return out, logits | ||||
							
								
								
									
										94
									
								
								graph_dit/naswot/models/cell_searchs/search_model_enas.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								graph_dit/naswot/models/cell_searchs/search_model_enas.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ########################################################################## | ||||
| # Efficient Neural Architecture Search via Parameters Sharing, ICML 2018 # | ||||
| ########################################################################## | ||||
| import torch | ||||
| import torch.nn as nn | ||||
| from copy import deepcopy | ||||
| from ..cell_operations import ResNetBasicblock | ||||
| from .search_cells     import NAS201SearchCell as SearchCell | ||||
| from .genotypes        import Structure | ||||
| from .search_model_enas_utils import Controller | ||||
|  | ||||
|  | ||||
| class TinyNetworkENAS(nn.Module): | ||||
|  | ||||
|   def __init__(self, C, N, max_nodes, num_classes, search_space, affine, track_running_stats): | ||||
|     super(TinyNetworkENAS, self).__init__() | ||||
|     self._C        = C | ||||
|     self._layerN   = N | ||||
|     self.max_nodes = max_nodes | ||||
|     self.stem = nn.Sequential( | ||||
|                     nn.Conv2d(3, C, kernel_size=3, padding=1, bias=False), | ||||
|                     nn.BatchNorm2d(C)) | ||||
|    | ||||
|     layer_channels   = [C    ] * N + [C*2 ] + [C*2  ] * N + [C*4 ] + [C*4  ] * N     | ||||
|     layer_reductions = [False] * N + [True] + [False] * N + [True] + [False] * N | ||||
|  | ||||
|     C_prev, num_edge, edge2index = C, None, None | ||||
|     self.cells = nn.ModuleList() | ||||
|     for index, (C_curr, reduction) in enumerate(zip(layer_channels, layer_reductions)): | ||||
|       if reduction: | ||||
|         cell = ResNetBasicblock(C_prev, C_curr, 2) | ||||
|       else: | ||||
|         cell = SearchCell(C_prev, C_curr, 1, max_nodes, search_space, affine, track_running_stats) | ||||
|         if num_edge is None: num_edge, edge2index = cell.num_edges, cell.edge2index | ||||
|         else: assert num_edge == cell.num_edges and edge2index == cell.edge2index, 'invalid {:} vs. {:}.'.format(num_edge, cell.num_edges) | ||||
|       self.cells.append( cell ) | ||||
|       C_prev = cell.out_dim | ||||
|     self.op_names   = deepcopy( search_space ) | ||||
|     self._Layer     = len(self.cells) | ||||
|     self.edge2index = edge2index | ||||
|     self.lastact    = nn.Sequential(nn.BatchNorm2d(C_prev), nn.ReLU(inplace=True)) | ||||
|     self.global_pooling = nn.AdaptiveAvgPool2d(1) | ||||
|     self.classifier = nn.Linear(C_prev, num_classes) | ||||
|     # to maintain the sampled architecture | ||||
|     self.sampled_arch = None | ||||
|  | ||||
|   def update_arch(self, _arch): | ||||
|     if _arch is None: | ||||
|       self.sampled_arch = None | ||||
|     elif isinstance(_arch, Structure): | ||||
|       self.sampled_arch = _arch | ||||
|     elif isinstance(_arch, (list, tuple)): | ||||
|       genotypes = [] | ||||
|       for i in range(1, self.max_nodes): | ||||
|         xlist = [] | ||||
|         for j in range(i): | ||||
|           node_str = '{:}<-{:}'.format(i, j) | ||||
|           op_index = _arch[ self.edge2index[node_str] ] | ||||
|           op_name  = self.op_names[ op_index ] | ||||
|           xlist.append((op_name, j)) | ||||
|         genotypes.append( tuple(xlist) ) | ||||
|       self.sampled_arch = Structure(genotypes) | ||||
|     else: | ||||
|       raise ValueError('invalid type of input architecture : {:}'.format(_arch)) | ||||
|     return self.sampled_arch | ||||
|      | ||||
|   def create_controller(self): | ||||
|     return Controller(len(self.edge2index), len(self.op_names)) | ||||
|  | ||||
|   def get_message(self): | ||||
|     string = self.extra_repr() | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       string += '\n {:02d}/{:02d} :: {:}'.format(i, len(self.cells), cell.extra_repr()) | ||||
|     return string | ||||
|  | ||||
|   def extra_repr(self): | ||||
|     return ('{name}(C={_C}, Max-Nodes={max_nodes}, N={_layerN}, L={_Layer})'.format(name=self.__class__.__name__, **self.__dict__)) | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|  | ||||
|     feature = self.stem(inputs) | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       if isinstance(cell, SearchCell): | ||||
|         feature = cell.forward_dynamic(feature, self.sampled_arch) | ||||
|       else: feature = cell(feature) | ||||
|  | ||||
|     out = self.lastact(feature) | ||||
|     out = self.global_pooling( out ) | ||||
|     out = out.view(out.size(0), -1) | ||||
|     logits = self.classifier(out) | ||||
|  | ||||
|     return out, logits | ||||
| @@ -0,0 +1,55 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ########################################################################## | ||||
| # Efficient Neural Architecture Search via Parameters Sharing, ICML 2018 # | ||||
| ########################################################################## | ||||
| import torch | ||||
| import torch.nn as nn | ||||
| from torch.distributions.categorical import Categorical | ||||
|  | ||||
| class Controller(nn.Module): | ||||
|   # we refer to https://github.com/TDeVries/enas_pytorch/blob/master/models/controller.py | ||||
|   def __init__(self, num_edge, num_ops, lstm_size=32, lstm_num_layers=2, tanh_constant=2.5, temperature=5.0): | ||||
|     super(Controller, self).__init__() | ||||
|     # assign the attributes | ||||
|     self.num_edge  = num_edge | ||||
|     self.num_ops   = num_ops | ||||
|     self.lstm_size = lstm_size | ||||
|     self.lstm_N    = lstm_num_layers | ||||
|     self.tanh_constant = tanh_constant | ||||
|     self.temperature   = temperature | ||||
|     # create parameters | ||||
|     self.register_parameter('input_vars', nn.Parameter(torch.Tensor(1, 1, lstm_size))) | ||||
|     self.w_lstm = nn.LSTM(input_size=self.lstm_size, hidden_size=self.lstm_size, num_layers=self.lstm_N) | ||||
|     self.w_embd = nn.Embedding(self.num_ops, self.lstm_size) | ||||
|     self.w_pred = nn.Linear(self.lstm_size, self.num_ops) | ||||
|  | ||||
|     nn.init.uniform_(self.input_vars         , -0.1, 0.1) | ||||
|     nn.init.uniform_(self.w_lstm.weight_hh_l0, -0.1, 0.1) | ||||
|     nn.init.uniform_(self.w_lstm.weight_ih_l0, -0.1, 0.1) | ||||
|     nn.init.uniform_(self.w_embd.weight      , -0.1, 0.1) | ||||
|     nn.init.uniform_(self.w_pred.weight      , -0.1, 0.1) | ||||
|  | ||||
|   def forward(self): | ||||
|  | ||||
|     inputs, h0 = self.input_vars, None | ||||
|     log_probs, entropys, sampled_arch = [], [], [] | ||||
|     for iedge in range(self.num_edge): | ||||
|       outputs, h0 = self.w_lstm(inputs, h0) | ||||
|        | ||||
|       logits = self.w_pred(outputs) | ||||
|       logits = logits / self.temperature | ||||
|       logits = self.tanh_constant * torch.tanh(logits) | ||||
|       # distribution | ||||
|       op_distribution = Categorical(logits=logits) | ||||
|       op_index    = op_distribution.sample() | ||||
|       sampled_arch.append( op_index.item() ) | ||||
|  | ||||
|       op_log_prob = op_distribution.log_prob(op_index) | ||||
|       log_probs.append( op_log_prob.view(-1) ) | ||||
|       op_entropy  = op_distribution.entropy() | ||||
|       entropys.append( op_entropy.view(-1) ) | ||||
|        | ||||
|       # obtain the input embedding for the next step | ||||
|       inputs = self.w_embd(op_index) | ||||
|     return torch.sum(torch.cat(log_probs)), torch.sum(torch.cat(entropys)), sampled_arch | ||||
							
								
								
									
										111
									
								
								graph_dit/naswot/models/cell_searchs/search_model_gdas.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								graph_dit/naswot/models/cell_searchs/search_model_gdas.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| ########################################################################### | ||||
| # Searching for A Robust Neural Architecture in Four GPU Hours, CVPR 2019 # | ||||
| ########################################################################### | ||||
| import torch | ||||
| import torch.nn as nn | ||||
| from copy import deepcopy | ||||
| from ..cell_operations import ResNetBasicblock | ||||
| from .search_cells     import NAS201SearchCell as SearchCell | ||||
| from .genotypes        import Structure | ||||
|  | ||||
|  | ||||
| class TinyNetworkGDAS(nn.Module): | ||||
|  | ||||
|   #def __init__(self, C, N, max_nodes, num_classes, search_space, affine=False, track_running_stats=True): | ||||
|   def __init__(self, C, N, max_nodes, num_classes, search_space, affine, track_running_stats): | ||||
|     super(TinyNetworkGDAS, self).__init__() | ||||
|     self._C        = C | ||||
|     self._layerN   = N | ||||
|     self.max_nodes = max_nodes | ||||
|     self.stem = nn.Sequential( | ||||
|                     nn.Conv2d(3, C, kernel_size=3, padding=1, bias=False), | ||||
|                     nn.BatchNorm2d(C)) | ||||
|    | ||||
|     layer_channels   = [C    ] * N + [C*2 ] + [C*2  ] * N + [C*4 ] + [C*4  ] * N     | ||||
|     layer_reductions = [False] * N + [True] + [False] * N + [True] + [False] * N | ||||
|  | ||||
|     C_prev, num_edge, edge2index = C, None, None | ||||
|     self.cells = nn.ModuleList() | ||||
|     for index, (C_curr, reduction) in enumerate(zip(layer_channels, layer_reductions)): | ||||
|       if reduction: | ||||
|         cell = ResNetBasicblock(C_prev, C_curr, 2) | ||||
|       else: | ||||
|         cell = SearchCell(C_prev, C_curr, 1, max_nodes, search_space, affine, track_running_stats) | ||||
|         if num_edge is None: num_edge, edge2index = cell.num_edges, cell.edge2index | ||||
|         else: assert num_edge == cell.num_edges and edge2index == cell.edge2index, 'invalid {:} vs. {:}.'.format(num_edge, cell.num_edges) | ||||
|       self.cells.append( cell ) | ||||
|       C_prev = cell.out_dim | ||||
|     self.op_names   = deepcopy( search_space ) | ||||
|     self._Layer     = len(self.cells) | ||||
|     self.edge2index = edge2index | ||||
|     self.lastact    = nn.Sequential(nn.BatchNorm2d(C_prev), nn.ReLU(inplace=True)) | ||||
|     self.global_pooling = nn.AdaptiveAvgPool2d(1) | ||||
|     self.classifier = nn.Linear(C_prev, num_classes) | ||||
|     self.arch_parameters = nn.Parameter( 1e-3*torch.randn(num_edge, len(search_space)) ) | ||||
|     self.tau        = 10 | ||||
|  | ||||
|   def get_weights(self): | ||||
|     xlist = list( self.stem.parameters() ) + list( self.cells.parameters() ) | ||||
|     xlist+= list( self.lastact.parameters() ) + list( self.global_pooling.parameters() ) | ||||
|     xlist+= list( self.classifier.parameters() ) | ||||
|     return xlist | ||||
|  | ||||
|   def set_tau(self, tau): | ||||
|     self.tau = tau | ||||
|  | ||||
|   def get_tau(self): | ||||
|     return self.tau | ||||
|  | ||||
|   def get_alphas(self): | ||||
|     return [self.arch_parameters] | ||||
|  | ||||
|   def show_alphas(self): | ||||
|     with torch.no_grad(): | ||||
|       return 'arch-parameters :\n{:}'.format( nn.functional.softmax(self.arch_parameters, dim=-1).cpu() ) | ||||
|  | ||||
|   def get_message(self): | ||||
|     string = self.extra_repr() | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       string += '\n {:02d}/{:02d} :: {:}'.format(i, len(self.cells), cell.extra_repr()) | ||||
|     return string | ||||
|  | ||||
|   def extra_repr(self): | ||||
|     return ('{name}(C={_C}, Max-Nodes={max_nodes}, N={_layerN}, L={_Layer})'.format(name=self.__class__.__name__, **self.__dict__)) | ||||
|  | ||||
|   def genotype(self): | ||||
|     genotypes = [] | ||||
|     for i in range(1, self.max_nodes): | ||||
|       xlist = [] | ||||
|       for j in range(i): | ||||
|         node_str = '{:}<-{:}'.format(i, j) | ||||
|         with torch.no_grad(): | ||||
|           weights = self.arch_parameters[ self.edge2index[node_str] ] | ||||
|           op_name = self.op_names[ weights.argmax().item() ] | ||||
|         xlist.append((op_name, j)) | ||||
|       genotypes.append( tuple(xlist) ) | ||||
|     return Structure( genotypes ) | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     while True: | ||||
|       gumbels = -torch.empty_like(self.arch_parameters).exponential_().log() | ||||
|       logits  = (self.arch_parameters.log_softmax(dim=1) + gumbels) / self.tau | ||||
|       probs   = nn.functional.softmax(logits, dim=1) | ||||
|       index   = probs.max(-1, keepdim=True)[1] | ||||
|       one_h   = torch.zeros_like(logits).scatter_(-1, index, 1.0) | ||||
|       hardwts = one_h - probs.detach() + probs | ||||
|       if (torch.isinf(gumbels).any()) or (torch.isinf(probs).any()) or (torch.isnan(probs).any()): | ||||
|         continue | ||||
|       else: break | ||||
|  | ||||
|     feature = self.stem(inputs) | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       if isinstance(cell, SearchCell): | ||||
|         feature = cell.forward_gdas(feature, hardwts, index) | ||||
|       else: | ||||
|         feature = cell(feature) | ||||
|     out = self.lastact(feature) | ||||
|     out = self.global_pooling( out ) | ||||
|     out = out.view(out.size(0), -1) | ||||
|     logits = self.classifier(out) | ||||
|  | ||||
|     return out, logits | ||||
							
								
								
									
										125
									
								
								graph_dit/naswot/models/cell_searchs/search_model_gdas_nasnet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								graph_dit/naswot/models/cell_searchs/search_model_gdas_nasnet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | ||||
| ########################################################################### | ||||
| # Searching for A Robust Neural Architecture in Four GPU Hours, CVPR 2019 # | ||||
| ########################################################################### | ||||
| import torch | ||||
| import torch.nn as nn | ||||
| from copy import deepcopy | ||||
| from .search_cells import NASNetSearchCell as SearchCell | ||||
|  | ||||
|  | ||||
| # The macro structure is based on NASNet | ||||
| class NASNetworkGDAS(nn.Module): | ||||
|  | ||||
|   def __init__(self, C, N, steps, multiplier, stem_multiplier, num_classes, search_space, affine, track_running_stats): | ||||
|     super(NASNetworkGDAS, self).__init__() | ||||
|     self._C        = C | ||||
|     self._layerN   = N | ||||
|     self._steps    = steps | ||||
|     self._multiplier = multiplier | ||||
|     self.stem = nn.Sequential( | ||||
|                     nn.Conv2d(3, C*stem_multiplier, kernel_size=3, padding=1, bias=False), | ||||
|                     nn.BatchNorm2d(C*stem_multiplier)) | ||||
|    | ||||
|     # config for each layer | ||||
|     layer_channels   = [C    ] * N + [C*2 ] + [C*2  ] * (N-1) + [C*4 ] + [C*4  ] * (N-1) | ||||
|     layer_reductions = [False] * N + [True] + [False] * (N-1) + [True] + [False] * (N-1) | ||||
|  | ||||
|     num_edge, edge2index = None, None | ||||
|     C_prev_prev, C_prev, C_curr, reduction_prev = C*stem_multiplier, C*stem_multiplier, C, False | ||||
|  | ||||
|     self.cells = nn.ModuleList() | ||||
|     for index, (C_curr, reduction) in enumerate(zip(layer_channels, layer_reductions)): | ||||
|       cell = SearchCell(search_space, steps, multiplier, C_prev_prev, C_prev, C_curr, reduction, reduction_prev, affine, track_running_stats) | ||||
|       if num_edge is None: num_edge, edge2index = cell.num_edges, cell.edge2index | ||||
|       else: assert num_edge == cell.num_edges and edge2index == cell.edge2index, 'invalid {:} vs. {:}.'.format(num_edge, cell.num_edges) | ||||
|       self.cells.append( cell ) | ||||
|       C_prev_prev, C_prev, reduction_prev = C_prev, multiplier*C_curr, reduction | ||||
|     self.op_names   = deepcopy( search_space ) | ||||
|     self._Layer     = len(self.cells) | ||||
|     self.edge2index = edge2index | ||||
|     self.lastact    = nn.Sequential(nn.BatchNorm2d(C_prev), nn.ReLU(inplace=True)) | ||||
|     self.global_pooling = nn.AdaptiveAvgPool2d(1) | ||||
|     self.classifier = nn.Linear(C_prev, num_classes) | ||||
|     self.arch_normal_parameters = nn.Parameter( 1e-3*torch.randn(num_edge, len(search_space)) ) | ||||
|     self.arch_reduce_parameters = nn.Parameter( 1e-3*torch.randn(num_edge, len(search_space)) ) | ||||
|     self.tau        = 10 | ||||
|  | ||||
|   def get_weights(self): | ||||
|     xlist = list( self.stem.parameters() ) + list( self.cells.parameters() ) | ||||
|     xlist+= list( self.lastact.parameters() ) + list( self.global_pooling.parameters() ) | ||||
|     xlist+= list( self.classifier.parameters() ) | ||||
|     return xlist | ||||
|  | ||||
|   def set_tau(self, tau): | ||||
|     self.tau = tau | ||||
|  | ||||
|   def get_tau(self): | ||||
|     return self.tau | ||||
|  | ||||
|   def get_alphas(self): | ||||
|     return [self.arch_normal_parameters, self.arch_reduce_parameters] | ||||
|  | ||||
|   def show_alphas(self): | ||||
|     with torch.no_grad(): | ||||
|       A = 'arch-normal-parameters :\n{:}'.format( nn.functional.softmax(self.arch_normal_parameters, dim=-1).cpu() ) | ||||
|       B = 'arch-reduce-parameters :\n{:}'.format( nn.functional.softmax(self.arch_reduce_parameters, dim=-1).cpu() ) | ||||
|     return '{:}\n{:}'.format(A, B) | ||||
|  | ||||
|   def get_message(self): | ||||
|     string = self.extra_repr() | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       string += '\n {:02d}/{:02d} :: {:}'.format(i, len(self.cells), cell.extra_repr()) | ||||
|     return string | ||||
|  | ||||
|   def extra_repr(self): | ||||
|     return ('{name}(C={_C}, N={_layerN}, steps={_steps}, multiplier={_multiplier}, L={_Layer})'.format(name=self.__class__.__name__, **self.__dict__)) | ||||
|  | ||||
|   def genotype(self): | ||||
|     def _parse(weights): | ||||
|       gene = [] | ||||
|       for i in range(self._steps): | ||||
|         edges = [] | ||||
|         for j in range(2+i): | ||||
|           node_str = '{:}<-{:}'.format(i, j) | ||||
|           ws = weights[ self.edge2index[node_str] ] | ||||
|           for k, op_name in enumerate(self.op_names): | ||||
|             if op_name == 'none': continue | ||||
|             edges.append( (op_name, j, ws[k]) ) | ||||
|         edges = sorted(edges, key=lambda x: -x[-1]) | ||||
|         selected_edges = edges[:2] | ||||
|         gene.append( tuple(selected_edges) ) | ||||
|       return gene | ||||
|     with torch.no_grad(): | ||||
|       gene_normal = _parse(torch.softmax(self.arch_normal_parameters, dim=-1).cpu().numpy()) | ||||
|       gene_reduce = _parse(torch.softmax(self.arch_reduce_parameters, dim=-1).cpu().numpy()) | ||||
|     return {'normal': gene_normal, 'normal_concat': list(range(2+self._steps-self._multiplier, self._steps+2)), | ||||
|             'reduce': gene_reduce, 'reduce_concat': list(range(2+self._steps-self._multiplier, self._steps+2))} | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     def get_gumbel_prob(xins): | ||||
|       while True: | ||||
|         gumbels = -torch.empty_like(xins).exponential_().log() | ||||
|         logits  = (xins.log_softmax(dim=1) + gumbels) / self.tau | ||||
|         probs   = nn.functional.softmax(logits, dim=1) | ||||
|         index   = probs.max(-1, keepdim=True)[1] | ||||
|         one_h   = torch.zeros_like(logits).scatter_(-1, index, 1.0) | ||||
|         hardwts = one_h - probs.detach() + probs | ||||
|         if (torch.isinf(gumbels).any()) or (torch.isinf(probs).any()) or (torch.isnan(probs).any()): | ||||
|           continue | ||||
|         else: break | ||||
|       return hardwts, index | ||||
|  | ||||
|     normal_hardwts, normal_index = get_gumbel_prob(self.arch_normal_parameters) | ||||
|     reduce_hardwts, reduce_index = get_gumbel_prob(self.arch_reduce_parameters) | ||||
|  | ||||
|     s0 = s1 = self.stem(inputs) | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       if cell.reduction: hardwts, index = reduce_hardwts, reduce_index | ||||
|       else             : hardwts, index = normal_hardwts, normal_index | ||||
|       s0, s1 = s1, cell.forward_gdas(s0, s1, hardwts, index) | ||||
|     out = self.lastact(s1) | ||||
|     out = self.global_pooling( out ) | ||||
|     out = out.view(out.size(0), -1) | ||||
|     logits = self.classifier(out) | ||||
|  | ||||
|     return out, logits | ||||
							
								
								
									
										81
									
								
								graph_dit/naswot/models/cell_searchs/search_model_random.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								graph_dit/naswot/models/cell_searchs/search_model_random.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ############################################################################## | ||||
| # Random Search and Reproducibility for Neural Architecture Search, UAI 2019 #  | ||||
| ############################################################################## | ||||
| import torch, random | ||||
| import torch.nn as nn | ||||
| from copy import deepcopy | ||||
| from ..cell_operations import ResNetBasicblock | ||||
| from .search_cells     import NAS201SearchCell as SearchCell | ||||
| from .genotypes        import Structure | ||||
|  | ||||
|  | ||||
| class TinyNetworkRANDOM(nn.Module): | ||||
|  | ||||
|   def __init__(self, C, N, max_nodes, num_classes, search_space, affine, track_running_stats): | ||||
|     super(TinyNetworkRANDOM, self).__init__() | ||||
|     self._C        = C | ||||
|     self._layerN   = N | ||||
|     self.max_nodes = max_nodes | ||||
|     self.stem = nn.Sequential( | ||||
|                     nn.Conv2d(3, C, kernel_size=3, padding=1, bias=False), | ||||
|                     nn.BatchNorm2d(C)) | ||||
|    | ||||
|     layer_channels   = [C    ] * N + [C*2 ] + [C*2  ] * N + [C*4 ] + [C*4  ] * N     | ||||
|     layer_reductions = [False] * N + [True] + [False] * N + [True] + [False] * N | ||||
|  | ||||
|     C_prev, num_edge, edge2index = C, None, None | ||||
|     self.cells = nn.ModuleList() | ||||
|     for index, (C_curr, reduction) in enumerate(zip(layer_channels, layer_reductions)): | ||||
|       if reduction: | ||||
|         cell = ResNetBasicblock(C_prev, C_curr, 2) | ||||
|       else: | ||||
|         cell = SearchCell(C_prev, C_curr, 1, max_nodes, search_space, affine, track_running_stats) | ||||
|         if num_edge is None: num_edge, edge2index = cell.num_edges, cell.edge2index | ||||
|         else: assert num_edge == cell.num_edges and edge2index == cell.edge2index, 'invalid {:} vs. {:}.'.format(num_edge, cell.num_edges) | ||||
|       self.cells.append( cell ) | ||||
|       C_prev = cell.out_dim | ||||
|     self.op_names   = deepcopy( search_space ) | ||||
|     self._Layer     = len(self.cells) | ||||
|     self.edge2index = edge2index | ||||
|     self.lastact    = nn.Sequential(nn.BatchNorm2d(C_prev), nn.ReLU(inplace=True)) | ||||
|     self.global_pooling = nn.AdaptiveAvgPool2d(1) | ||||
|     self.classifier = nn.Linear(C_prev, num_classes) | ||||
|     self.arch_cache = None | ||||
|      | ||||
|   def get_message(self): | ||||
|     string = self.extra_repr() | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       string += '\n {:02d}/{:02d} :: {:}'.format(i, len(self.cells), cell.extra_repr()) | ||||
|     return string | ||||
|  | ||||
|   def extra_repr(self): | ||||
|     return ('{name}(C={_C}, Max-Nodes={max_nodes}, N={_layerN}, L={_Layer})'.format(name=self.__class__.__name__, **self.__dict__)) | ||||
|  | ||||
|   def random_genotype(self, set_cache): | ||||
|     genotypes = [] | ||||
|     for i in range(1, self.max_nodes): | ||||
|       xlist = [] | ||||
|       for j in range(i): | ||||
|         node_str = '{:}<-{:}'.format(i, j) | ||||
|         op_name  = random.choice( self.op_names ) | ||||
|         xlist.append((op_name, j)) | ||||
|       genotypes.append( tuple(xlist) ) | ||||
|     arch = Structure( genotypes ) | ||||
|     if set_cache: self.arch_cache = arch | ||||
|     return arch | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|  | ||||
|     feature = self.stem(inputs) | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       if isinstance(cell, SearchCell): | ||||
|         feature = cell.forward_dynamic(feature, self.arch_cache) | ||||
|       else: feature = cell(feature) | ||||
|  | ||||
|     out = self.lastact(feature) | ||||
|     out = self.global_pooling( out ) | ||||
|     out = out.view(out.size(0), -1) | ||||
|     logits = self.classifier(out) | ||||
|     return out, logits | ||||
							
								
								
									
										152
									
								
								graph_dit/naswot/models/cell_searchs/search_model_setn.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								graph_dit/naswot/models/cell_searchs/search_model_setn.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| ##################################################### | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019.01 # | ||||
| ###################################################################################### | ||||
| # One-Shot Neural Architecture Search via Self-Evaluated Template Network, ICCV 2019 # | ||||
| ###################################################################################### | ||||
| import torch, random | ||||
| import torch.nn as nn | ||||
| from copy import deepcopy | ||||
| from ..cell_operations import ResNetBasicblock | ||||
| from .search_cells     import NAS201SearchCell as SearchCell | ||||
| from .genotypes        import Structure | ||||
|  | ||||
|  | ||||
| class TinyNetworkSETN(nn.Module): | ||||
|  | ||||
|   def __init__(self, C, N, max_nodes, num_classes, search_space, affine, track_running_stats): | ||||
|     super(TinyNetworkSETN, self).__init__() | ||||
|     self._C        = C | ||||
|     self._layerN   = N | ||||
|     self.max_nodes = max_nodes | ||||
|     self.stem = nn.Sequential( | ||||
|                     nn.Conv2d(3, C, kernel_size=3, padding=1, bias=False), | ||||
|                     nn.BatchNorm2d(C)) | ||||
|    | ||||
|     layer_channels   = [C    ] * N + [C*2 ] + [C*2  ] * N + [C*4 ] + [C*4  ] * N     | ||||
|     layer_reductions = [False] * N + [True] + [False] * N + [True] + [False] * N | ||||
|  | ||||
|     C_prev, num_edge, edge2index = C, None, None | ||||
|     self.cells = nn.ModuleList() | ||||
|     for index, (C_curr, reduction) in enumerate(zip(layer_channels, layer_reductions)): | ||||
|       if reduction: | ||||
|         cell = ResNetBasicblock(C_prev, C_curr, 2) | ||||
|       else: | ||||
|         cell = SearchCell(C_prev, C_curr, 1, max_nodes, search_space, affine, track_running_stats) | ||||
|         if num_edge is None: num_edge, edge2index = cell.num_edges, cell.edge2index | ||||
|         else: assert num_edge == cell.num_edges and edge2index == cell.edge2index, 'invalid {:} vs. {:}.'.format(num_edge, cell.num_edges) | ||||
|       self.cells.append( cell ) | ||||
|       C_prev = cell.out_dim | ||||
|     self.op_names   = deepcopy( search_space ) | ||||
|     self._Layer     = len(self.cells) | ||||
|     self.edge2index = edge2index | ||||
|     self.lastact    = nn.Sequential(nn.BatchNorm2d(C_prev), nn.ReLU(inplace=True)) | ||||
|     self.global_pooling = nn.AdaptiveAvgPool2d(1) | ||||
|     self.classifier = nn.Linear(C_prev, num_classes) | ||||
|     self.arch_parameters = nn.Parameter( 1e-3*torch.randn(num_edge, len(search_space)) ) | ||||
|     self.mode       = 'urs' | ||||
|     self.dynamic_cell = None | ||||
|      | ||||
|   def set_cal_mode(self, mode, dynamic_cell=None): | ||||
|     assert mode in ['urs', 'joint', 'select', 'dynamic'] | ||||
|     self.mode = mode | ||||
|     if mode == 'dynamic': self.dynamic_cell = deepcopy( dynamic_cell ) | ||||
|     else                : self.dynamic_cell = None | ||||
|  | ||||
|   def get_cal_mode(self): | ||||
|     return self.mode | ||||
|  | ||||
|   def get_weights(self): | ||||
|     xlist = list( self.stem.parameters() ) + list( self.cells.parameters() ) | ||||
|     xlist+= list( self.lastact.parameters() ) + list( self.global_pooling.parameters() ) | ||||
|     xlist+= list( self.classifier.parameters() ) | ||||
|     return xlist | ||||
|  | ||||
|   def get_alphas(self): | ||||
|     return [self.arch_parameters] | ||||
|  | ||||
|   def get_message(self): | ||||
|     string = self.extra_repr() | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       string += '\n {:02d}/{:02d} :: {:}'.format(i, len(self.cells), cell.extra_repr()) | ||||
|     return string | ||||
|  | ||||
|   def extra_repr(self): | ||||
|     return ('{name}(C={_C}, Max-Nodes={max_nodes}, N={_layerN}, L={_Layer})'.format(name=self.__class__.__name__, **self.__dict__)) | ||||
|  | ||||
|   def genotype(self): | ||||
|     genotypes = [] | ||||
|     for i in range(1, self.max_nodes): | ||||
|       xlist = [] | ||||
|       for j in range(i): | ||||
|         node_str = '{:}<-{:}'.format(i, j) | ||||
|         with torch.no_grad(): | ||||
|           weights = self.arch_parameters[ self.edge2index[node_str] ] | ||||
|           op_name = self.op_names[ weights.argmax().item() ] | ||||
|         xlist.append((op_name, j)) | ||||
|       genotypes.append( tuple(xlist) ) | ||||
|     return Structure( genotypes ) | ||||
|  | ||||
|   def dync_genotype(self, use_random=False): | ||||
|     genotypes = [] | ||||
|     with torch.no_grad(): | ||||
|       alphas_cpu = nn.functional.softmax(self.arch_parameters, dim=-1) | ||||
|     for i in range(1, self.max_nodes): | ||||
|       xlist = [] | ||||
|       for j in range(i): | ||||
|         node_str = '{:}<-{:}'.format(i, j) | ||||
|         if use_random: | ||||
|           op_name  = random.choice(self.op_names) | ||||
|         else: | ||||
|           weights  = alphas_cpu[ self.edge2index[node_str] ] | ||||
|           op_index = torch.multinomial(weights, 1).item() | ||||
|           op_name  = self.op_names[ op_index ] | ||||
|         xlist.append((op_name, j)) | ||||
|       genotypes.append( tuple(xlist) ) | ||||
|     return Structure( genotypes ) | ||||
|  | ||||
|   def get_log_prob(self, arch): | ||||
|     with torch.no_grad(): | ||||
|       logits = nn.functional.log_softmax(self.arch_parameters, dim=-1) | ||||
|     select_logits = [] | ||||
|     for i, node_info in enumerate(arch.nodes): | ||||
|       for op, xin in node_info: | ||||
|         node_str = '{:}<-{:}'.format(i+1, xin) | ||||
|         op_index = self.op_names.index(op) | ||||
|         select_logits.append( logits[self.edge2index[node_str], op_index] ) | ||||
|     return sum(select_logits).item() | ||||
|  | ||||
|  | ||||
|   def return_topK(self, K): | ||||
|     archs = Structure.gen_all(self.op_names, self.max_nodes, False) | ||||
|     pairs = [(self.get_log_prob(arch), arch) for arch in archs] | ||||
|     if K < 0 or K >= len(archs): K = len(archs) | ||||
|     sorted_pairs = sorted(pairs, key=lambda x: -x[0]) | ||||
|     return_pairs = [sorted_pairs[_][1] for _ in range(K)] | ||||
|     return return_pairs | ||||
|  | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     alphas  = nn.functional.softmax(self.arch_parameters, dim=-1) | ||||
|     with torch.no_grad(): | ||||
|       alphas_cpu = alphas.detach().cpu() | ||||
|  | ||||
|     feature = self.stem(inputs) | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       if isinstance(cell, SearchCell): | ||||
|         if self.mode == 'urs': | ||||
|           feature = cell.forward_urs(feature) | ||||
|         elif self.mode == 'select': | ||||
|           feature = cell.forward_select(feature, alphas_cpu) | ||||
|         elif self.mode == 'joint': | ||||
|           feature = cell.forward_joint(feature, alphas) | ||||
|         elif self.mode == 'dynamic': | ||||
|           feature = cell.forward_dynamic(feature, self.dynamic_cell) | ||||
|         else: raise ValueError('invalid mode={:}'.format(self.mode)) | ||||
|       else: feature = cell(feature) | ||||
|  | ||||
|     out = self.lastact(feature) | ||||
|     out = self.global_pooling( out ) | ||||
|     out = out.view(out.size(0), -1) | ||||
|     logits = self.classifier(out) | ||||
|  | ||||
|     return out, logits | ||||
							
								
								
									
										139
									
								
								graph_dit/naswot/models/cell_searchs/search_model_setn_nasnet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								graph_dit/naswot/models/cell_searchs/search_model_setn_nasnet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | ||||
| ##################################################### | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019.01 # | ||||
| ###################################################################################### | ||||
| # One-Shot Neural Architecture Search via Self-Evaluated Template Network, ICCV 2019 # | ||||
| ###################################################################################### | ||||
| import torch | ||||
| import torch.nn as nn | ||||
| from copy import deepcopy | ||||
| from typing import List, Text, Dict | ||||
| from .search_cells     import NASNetSearchCell as SearchCell | ||||
|  | ||||
|  | ||||
| # The macro structure is based on NASNet | ||||
| class NASNetworkSETN(nn.Module): | ||||
|  | ||||
|   def __init__(self, C: int, N: int, steps: int, multiplier: int, stem_multiplier: int, | ||||
|                num_classes: int, search_space: List[Text], affine: bool, track_running_stats: bool): | ||||
|     super(NASNetworkSETN, self).__init__() | ||||
|     self._C        = C | ||||
|     self._layerN   = N | ||||
|     self._steps    = steps | ||||
|     self._multiplier = multiplier | ||||
|     self.stem = nn.Sequential( | ||||
|                     nn.Conv2d(3, C*stem_multiplier, kernel_size=3, padding=1, bias=False), | ||||
|                     nn.BatchNorm2d(C*stem_multiplier)) | ||||
|    | ||||
|     # config for each layer | ||||
|     layer_channels   = [C    ] * N + [C*2 ] + [C*2  ] * (N-1) + [C*4 ] + [C*4  ] * (N-1) | ||||
|     layer_reductions = [False] * N + [True] + [False] * (N-1) + [True] + [False] * (N-1) | ||||
|  | ||||
|     num_edge, edge2index = None, None | ||||
|     C_prev_prev, C_prev, C_curr, reduction_prev = C*stem_multiplier, C*stem_multiplier, C, False | ||||
|  | ||||
|     self.cells = nn.ModuleList() | ||||
|     for index, (C_curr, reduction) in enumerate(zip(layer_channels, layer_reductions)): | ||||
|       cell = SearchCell(search_space, steps, multiplier, C_prev_prev, C_prev, C_curr, reduction, reduction_prev, affine, track_running_stats) | ||||
|       if num_edge is None: num_edge, edge2index = cell.num_edges, cell.edge2index | ||||
|       else: assert num_edge == cell.num_edges and edge2index == cell.edge2index, 'invalid {:} vs. {:}.'.format(num_edge, cell.num_edges) | ||||
|       self.cells.append( cell ) | ||||
|       C_prev_prev, C_prev, reduction_prev = C_prev, multiplier*C_curr, reduction | ||||
|     self.op_names   = deepcopy( search_space ) | ||||
|     self._Layer     = len(self.cells) | ||||
|     self.edge2index = edge2index | ||||
|     self.lastact    = nn.Sequential(nn.BatchNorm2d(C_prev), nn.ReLU(inplace=True)) | ||||
|     self.global_pooling = nn.AdaptiveAvgPool2d(1) | ||||
|     self.classifier = nn.Linear(C_prev, num_classes) | ||||
|     self.arch_normal_parameters = nn.Parameter( 1e-3*torch.randn(num_edge, len(search_space)) ) | ||||
|     self.arch_reduce_parameters = nn.Parameter( 1e-3*torch.randn(num_edge, len(search_space)) ) | ||||
|     self.mode = 'urs' | ||||
|     self.dynamic_cell = None | ||||
|  | ||||
|   def set_cal_mode(self, mode, dynamic_cell=None): | ||||
|     assert mode in ['urs', 'joint', 'select', 'dynamic'] | ||||
|     self.mode = mode | ||||
|     if mode == 'dynamic': | ||||
|       self.dynamic_cell = deepcopy(dynamic_cell) | ||||
|     else: | ||||
|       self.dynamic_cell = None | ||||
|  | ||||
|   def get_weights(self): | ||||
|     xlist = list( self.stem.parameters() ) + list( self.cells.parameters() ) | ||||
|     xlist+= list( self.lastact.parameters() ) + list( self.global_pooling.parameters() ) | ||||
|     xlist+= list( self.classifier.parameters() ) | ||||
|     return xlist | ||||
|  | ||||
|   def get_alphas(self): | ||||
|     return [self.arch_normal_parameters, self.arch_reduce_parameters] | ||||
|  | ||||
|   def show_alphas(self): | ||||
|     with torch.no_grad(): | ||||
|       A = 'arch-normal-parameters :\n{:}'.format( nn.functional.softmax(self.arch_normal_parameters, dim=-1).cpu() ) | ||||
|       B = 'arch-reduce-parameters :\n{:}'.format( nn.functional.softmax(self.arch_reduce_parameters, dim=-1).cpu() ) | ||||
|     return '{:}\n{:}'.format(A, B) | ||||
|  | ||||
|   def get_message(self): | ||||
|     string = self.extra_repr() | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       string += '\n {:02d}/{:02d} :: {:}'.format(i, len(self.cells), cell.extra_repr()) | ||||
|     return string | ||||
|  | ||||
|   def extra_repr(self): | ||||
|     return ('{name}(C={_C}, N={_layerN}, steps={_steps}, multiplier={_multiplier}, L={_Layer})'.format(name=self.__class__.__name__, **self.__dict__)) | ||||
|  | ||||
|   def dync_genotype(self, use_random=False): | ||||
|     genotypes = [] | ||||
|     with torch.no_grad(): | ||||
|       alphas_cpu = nn.functional.softmax(self.arch_parameters, dim=-1) | ||||
|     for i in range(1, self.max_nodes): | ||||
|       xlist = [] | ||||
|       for j in range(i): | ||||
|         node_str = '{:}<-{:}'.format(i, j) | ||||
|         if use_random: | ||||
|           op_name  = random.choice(self.op_names) | ||||
|         else: | ||||
|           weights  = alphas_cpu[ self.edge2index[node_str] ] | ||||
|           op_index = torch.multinomial(weights, 1).item() | ||||
|           op_name  = self.op_names[ op_index ] | ||||
|         xlist.append((op_name, j)) | ||||
|       genotypes.append( tuple(xlist) ) | ||||
|     return Structure( genotypes ) | ||||
|  | ||||
|   def genotype(self): | ||||
|     def _parse(weights): | ||||
|       gene = [] | ||||
|       for i in range(self._steps): | ||||
|         edges = [] | ||||
|         for j in range(2+i): | ||||
|           node_str = '{:}<-{:}'.format(i, j) | ||||
|           ws = weights[ self.edge2index[node_str] ] | ||||
|           for k, op_name in enumerate(self.op_names): | ||||
|             if op_name == 'none': continue | ||||
|             edges.append( (op_name, j, ws[k]) ) | ||||
|         edges = sorted(edges, key=lambda x: -x[-1]) | ||||
|         selected_edges = edges[:2] | ||||
|         gene.append( tuple(selected_edges) ) | ||||
|       return gene | ||||
|     with torch.no_grad(): | ||||
|       gene_normal = _parse(torch.softmax(self.arch_normal_parameters, dim=-1).cpu().numpy()) | ||||
|       gene_reduce = _parse(torch.softmax(self.arch_reduce_parameters, dim=-1).cpu().numpy()) | ||||
|     return {'normal': gene_normal, 'normal_concat': list(range(2+self._steps-self._multiplier, self._steps+2)), | ||||
|             'reduce': gene_reduce, 'reduce_concat': list(range(2+self._steps-self._multiplier, self._steps+2))} | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     normal_hardwts = nn.functional.softmax(self.arch_normal_parameters, dim=-1) | ||||
|     reduce_hardwts = nn.functional.softmax(self.arch_reduce_parameters, dim=-1) | ||||
|  | ||||
|     s0 = s1 = self.stem(inputs) | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       # [TODO] | ||||
|       raise NotImplementedError | ||||
|       if cell.reduction: hardwts, index = reduce_hardwts, reduce_index | ||||
|       else             : hardwts, index = normal_hardwts, normal_index | ||||
|       s0, s1 = s1, cell.forward_gdas(s0, s1, hardwts, index) | ||||
|     out = self.lastact(s1) | ||||
|     out = self.global_pooling( out ) | ||||
|     out = out.view(out.size(0), -1) | ||||
|     logits = self.classifier(out) | ||||
|  | ||||
|     return out, logits | ||||
							
								
								
									
										62
									
								
								graph_dit/naswot/models/clone_weights.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								graph_dit/naswot/models/clone_weights.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| import torch | ||||
| import torch.nn as nn | ||||
|  | ||||
|  | ||||
| def copy_conv(module, init): | ||||
|   assert isinstance(module, nn.Conv2d), 'invalid module : {:}'.format(module) | ||||
|   assert isinstance(init  , nn.Conv2d), 'invalid module : {:}'.format(init) | ||||
|   new_i, new_o = module.in_channels, module.out_channels | ||||
|   module.weight.copy_( init.weight.detach()[:new_o, :new_i] ) | ||||
|   if module.bias is not None: | ||||
|     module.bias.copy_( init.bias.detach()[:new_o] ) | ||||
|  | ||||
| def copy_bn  (module, init): | ||||
|   assert isinstance(module, nn.BatchNorm2d), 'invalid module : {:}'.format(module) | ||||
|   assert isinstance(init  , nn.BatchNorm2d), 'invalid module : {:}'.format(init) | ||||
|   num_features = module.num_features | ||||
|   if module.weight is not None: | ||||
|     module.weight.copy_( init.weight.detach()[:num_features] ) | ||||
|   if module.bias is not None: | ||||
|     module.bias.copy_( init.bias.detach()[:num_features] ) | ||||
|   if module.running_mean is not None: | ||||
|     module.running_mean.copy_( init.running_mean.detach()[:num_features] ) | ||||
|   if module.running_var  is not None: | ||||
|     module.running_var.copy_( init.running_var.detach()[:num_features] ) | ||||
|  | ||||
| def copy_fc  (module, init): | ||||
|   assert isinstance(module, nn.Linear), 'invalid module : {:}'.format(module) | ||||
|   assert isinstance(init  , nn.Linear), 'invalid module : {:}'.format(init) | ||||
|   new_i, new_o = module.in_features, module.out_features | ||||
|   module.weight.copy_( init.weight.detach()[:new_o, :new_i] ) | ||||
|   if module.bias is not None: | ||||
|     module.bias.copy_( init.bias.detach()[:new_o] ) | ||||
|  | ||||
| def copy_base(module, init): | ||||
|   assert type(module).__name__ in ['ConvBNReLU', 'Downsample'], 'invalid module : {:}'.format(module) | ||||
|   assert type(  init).__name__ in ['ConvBNReLU', 'Downsample'], 'invalid module : {:}'.format(  init) | ||||
|   if module.conv is not None: | ||||
|     copy_conv(module.conv, init.conv) | ||||
|   if module.bn is not None: | ||||
|     copy_bn  (module.bn, init.bn) | ||||
|  | ||||
| def copy_basic(module, init): | ||||
|   copy_base(module.conv_a, init.conv_a) | ||||
|   copy_base(module.conv_b, init.conv_b) | ||||
|   if module.downsample is not None: | ||||
|     if init.downsample is not None: | ||||
|       copy_base(module.downsample, init.downsample) | ||||
|     #else: | ||||
|     # import pdb; pdb.set_trace() | ||||
|  | ||||
|  | ||||
| def init_from_model(network, init_model): | ||||
|   with torch.no_grad(): | ||||
|     copy_fc(network.classifier, init_model.classifier) | ||||
|     for base, target in zip(init_model.layers, network.layers): | ||||
|       assert type(base).__name__  == type(target).__name__, 'invalid type : {:} vs {:}'.format(base, target) | ||||
|       if type(base).__name__ == 'ConvBNReLU': | ||||
|         copy_base(target, base) | ||||
|       elif type(base).__name__ == 'ResNetBasicblock': | ||||
|         copy_basic(target, base) | ||||
|       else: | ||||
|         raise ValueError('unknown type name : {:}'.format( type(base).__name__ )) | ||||
							
								
								
									
										18
									
								
								graph_dit/naswot/models/initialization.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								graph_dit/naswot/models/initialization.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| import torch | ||||
| import torch.nn as nn | ||||
|  | ||||
|  | ||||
| def initialize_resnet(m): | ||||
|   if isinstance(m, nn.Conv2d): | ||||
|     nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') | ||||
|     if m.bias is not None: | ||||
|       nn.init.constant_(m.bias, 0) | ||||
|   elif isinstance(m, nn.BatchNorm2d): | ||||
|     nn.init.constant_(m.weight, 1) | ||||
|     if m.bias is not None: | ||||
|       nn.init.constant_(m.bias, 0) | ||||
|   elif isinstance(m, nn.Linear): | ||||
|     nn.init.normal_(m.weight, 0, 0.01) | ||||
|     nn.init.constant_(m.bias, 0) | ||||
|  | ||||
|  | ||||
							
								
								
									
										167
									
								
								graph_dit/naswot/models/shape_infers/InferCifarResNet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								graph_dit/naswot/models/shape_infers/InferCifarResNet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,167 @@ | ||||
| ##################################################### | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019.01 # | ||||
| ##################################################### | ||||
| import torch.nn as nn | ||||
| import torch.nn.functional as F | ||||
| from ..initialization import initialize_resnet | ||||
|  | ||||
|  | ||||
| class ConvBNReLU(nn.Module): | ||||
|    | ||||
|   def __init__(self, nIn, nOut, kernel, stride, padding, bias, has_avg, has_bn, has_relu): | ||||
|     super(ConvBNReLU, self).__init__() | ||||
|     if has_avg : self.avg = nn.AvgPool2d(kernel_size=2, stride=2, padding=0) | ||||
|     else       : self.avg = None | ||||
|     self.conv = nn.Conv2d(nIn, nOut, kernel_size=kernel, stride=stride, padding=padding, dilation=1, groups=1, bias=bias) | ||||
|     if has_bn  : self.bn  = nn.BatchNorm2d(nOut) | ||||
|     else       : self.bn  = None | ||||
|     if has_relu: self.relu = nn.ReLU(inplace=True) | ||||
|     else       : self.relu = None | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.avg : out = self.avg( inputs ) | ||||
|     else        : out = inputs | ||||
|     conv = self.conv( out ) | ||||
|     if self.bn  : out = self.bn( conv ) | ||||
|     else        : out = conv | ||||
|     if self.relu: out = self.relu( out ) | ||||
|     else        : out = out | ||||
|  | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class ResNetBasicblock(nn.Module): | ||||
|   num_conv  = 2 | ||||
|   expansion = 1 | ||||
|   def __init__(self, iCs, stride): | ||||
|     super(ResNetBasicblock, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     assert isinstance(iCs, tuple) or isinstance(iCs, list), 'invalid type of iCs : {:}'.format( iCs ) | ||||
|     assert len(iCs) == 3,'invalid lengths of iCs : {:}'.format(iCs) | ||||
|      | ||||
|     self.conv_a = ConvBNReLU(iCs[0], iCs[1], 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_b = ConvBNReLU(iCs[1], iCs[2], 3,      1, 1, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|     residual_in = iCs[0] | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(iCs[0], iCs[2], 1, 1, 0, False, has_avg=True, has_bn=False, has_relu=False) | ||||
|       residual_in = iCs[2] | ||||
|     elif iCs[0] != iCs[2]: | ||||
|       self.downsample = ConvBNReLU(iCs[0], iCs[2], 1, 1, 0, False, has_avg=False,has_bn=True , has_relu=False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     #self.out_dim  = max(residual_in, iCs[2]) | ||||
|     self.out_dim  = iCs[2] | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     basicblock = self.conv_a(inputs) | ||||
|     basicblock = self.conv_b(basicblock) | ||||
|  | ||||
|     if self.downsample is not None: | ||||
|       residual = self.downsample(inputs) | ||||
|     else: | ||||
|       residual = inputs | ||||
|     out = residual + basicblock | ||||
|     return F.relu(out, inplace=True) | ||||
|  | ||||
|  | ||||
|  | ||||
| class ResNetBottleneck(nn.Module): | ||||
|   expansion = 4 | ||||
|   num_conv  = 3 | ||||
|   def __init__(self, iCs, stride): | ||||
|     super(ResNetBottleneck, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     assert isinstance(iCs, tuple) or isinstance(iCs, list), 'invalid type of iCs : {:}'.format( iCs ) | ||||
|     assert len(iCs) == 4,'invalid lengths of iCs : {:}'.format(iCs) | ||||
|     self.conv_1x1 = ConvBNReLU(iCs[0], iCs[1], 1,      1, 0, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_3x3 = ConvBNReLU(iCs[1], iCs[2], 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_1x4 = ConvBNReLU(iCs[2], iCs[3], 1,      1, 0, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|     residual_in = iCs[0] | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(iCs[0], iCs[3], 1, 1, 0, False, has_avg=True , has_bn=False, has_relu=False) | ||||
|       residual_in     = iCs[3] | ||||
|     elif iCs[0] != iCs[3]: | ||||
|       self.downsample = ConvBNReLU(iCs[0], iCs[3], 1, 1, 0, False, has_avg=False, has_bn=False, has_relu=False) | ||||
|       residual_in     = iCs[3] | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     #self.out_dim = max(residual_in, iCs[3]) | ||||
|     self.out_dim = iCs[3] | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|  | ||||
|     bottleneck = self.conv_1x1(inputs) | ||||
|     bottleneck = self.conv_3x3(bottleneck) | ||||
|     bottleneck = self.conv_1x4(bottleneck) | ||||
|  | ||||
|     if self.downsample is not None: | ||||
|       residual = self.downsample(inputs) | ||||
|     else: | ||||
|       residual = inputs | ||||
|     out = residual + bottleneck | ||||
|     return F.relu(out, inplace=True) | ||||
|  | ||||
|  | ||||
|  | ||||
| class InferCifarResNet(nn.Module): | ||||
|  | ||||
|   def __init__(self, block_name, depth, xblocks, xchannels, num_classes, zero_init_residual): | ||||
|     super(InferCifarResNet, self).__init__() | ||||
|  | ||||
|     #Model type specifies number of layers for CIFAR-10 and CIFAR-100 model | ||||
|     if block_name == 'ResNetBasicblock': | ||||
|       block = ResNetBasicblock | ||||
|       assert (depth - 2) % 6 == 0, 'depth should be one of 20, 32, 44, 56, 110' | ||||
|       layer_blocks = (depth - 2) // 6 | ||||
|     elif block_name == 'ResNetBottleneck': | ||||
|       block = ResNetBottleneck | ||||
|       assert (depth - 2) % 9 == 0, 'depth should be one of 164' | ||||
|       layer_blocks = (depth - 2) // 9 | ||||
|     else: | ||||
|       raise ValueError('invalid block : {:}'.format(block_name)) | ||||
|     assert len(xblocks) == 3, 'invalid xblocks : {:}'.format(xblocks) | ||||
|  | ||||
|     self.message     = 'InferWidthCifarResNet : Depth : {:} , Layers for each block : {:}'.format(depth, layer_blocks) | ||||
|     self.num_classes = num_classes | ||||
|     self.xchannels   = xchannels | ||||
|     self.layers      = nn.ModuleList( [ ConvBNReLU(xchannels[0], xchannels[1], 3, 1, 1, False, has_avg=False, has_bn=True, has_relu=True) ] ) | ||||
|     last_channel_idx = 1 | ||||
|     for stage in range(3): | ||||
|       for iL in range(layer_blocks): | ||||
|         num_conv = block.num_conv  | ||||
|         iCs      = self.xchannels[last_channel_idx:last_channel_idx+num_conv+1] | ||||
|         stride   = 2 if stage > 0 and iL == 0 else 1 | ||||
|         module   = block(iCs, stride) | ||||
|         last_channel_idx += num_conv | ||||
|         self.xchannels[last_channel_idx] = module.out_dim | ||||
|         self.layers.append  ( module ) | ||||
|         self.message += "\nstage={:}, ilayer={:02d}/{:02d}, block={:03d}, iCs={:}, oC={:3d}, stride={:}".format(stage, iL, layer_blocks, len(self.layers)-1, iCs, module.out_dim, stride) | ||||
|         if iL + 1 == xblocks[stage]: # reach the maximum depth | ||||
|           out_channel = module.out_dim | ||||
|           for iiL in range(iL+1, layer_blocks): | ||||
|             last_channel_idx += num_conv | ||||
|           self.xchannels[last_channel_idx] = module.out_dim | ||||
|           break | ||||
|    | ||||
|     self.avgpool    = nn.AvgPool2d(8) | ||||
|     self.classifier = nn.Linear(self.xchannels[-1], num_classes) | ||||
|      | ||||
|     self.apply(initialize_resnet) | ||||
|     if zero_init_residual: | ||||
|       for m in self.modules(): | ||||
|         if isinstance(m, ResNetBasicblock): | ||||
|           nn.init.constant_(m.conv_b.bn.weight, 0) | ||||
|         elif isinstance(m, ResNetBottleneck): | ||||
|           nn.init.constant_(m.conv_1x4.bn.weight, 0) | ||||
|  | ||||
|   def get_message(self): | ||||
|     return self.message | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     x = inputs | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       x = layer( x ) | ||||
|     features = self.avgpool(x) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     logits   = self.classifier(features) | ||||
|     return features, logits | ||||
							
								
								
									
										150
									
								
								graph_dit/naswot/models/shape_infers/InferCifarResNet_depth.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								graph_dit/naswot/models/shape_infers/InferCifarResNet_depth.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,150 @@ | ||||
| ##################################################### | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019.01 # | ||||
| ##################################################### | ||||
| import torch.nn as nn | ||||
| import torch.nn.functional as F | ||||
| from ..initialization import initialize_resnet | ||||
|  | ||||
|  | ||||
| class ConvBNReLU(nn.Module): | ||||
|    | ||||
|   def __init__(self, nIn, nOut, kernel, stride, padding, bias, has_avg, has_bn, has_relu): | ||||
|     super(ConvBNReLU, self).__init__() | ||||
|     if has_avg : self.avg = nn.AvgPool2d(kernel_size=2, stride=2, padding=0) | ||||
|     else       : self.avg = None | ||||
|     self.conv = nn.Conv2d(nIn, nOut, kernel_size=kernel, stride=stride, padding=padding, dilation=1, groups=1, bias=bias) | ||||
|     if has_bn  : self.bn  = nn.BatchNorm2d(nOut) | ||||
|     else       : self.bn  = None | ||||
|     if has_relu: self.relu = nn.ReLU(inplace=True) | ||||
|     else       : self.relu = None | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.avg : out = self.avg( inputs ) | ||||
|     else        : out = inputs | ||||
|     conv = self.conv( out ) | ||||
|     if self.bn  : out = self.bn( conv ) | ||||
|     else        : out = conv | ||||
|     if self.relu: out = self.relu( out ) | ||||
|     else        : out = out | ||||
|  | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class ResNetBasicblock(nn.Module): | ||||
|   num_conv  = 2 | ||||
|   expansion = 1 | ||||
|   def __init__(self, inplanes, planes, stride): | ||||
|     super(ResNetBasicblock, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|      | ||||
|     self.conv_a = ConvBNReLU(inplanes, planes, 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_b = ConvBNReLU(  planes, planes, 3,      1, 1, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes, 1, 1, 0, False, has_avg=True, has_bn=False, has_relu=False) | ||||
|     elif inplanes != planes: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes, 1, 1, 0, False, has_avg=False,has_bn=True , has_relu=False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     self.out_dim  = planes | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     basicblock = self.conv_a(inputs) | ||||
|     basicblock = self.conv_b(basicblock) | ||||
|  | ||||
|     if self.downsample is not None: | ||||
|       residual = self.downsample(inputs) | ||||
|     else: | ||||
|       residual = inputs | ||||
|     out = residual + basicblock | ||||
|     return F.relu(out, inplace=True) | ||||
|  | ||||
|  | ||||
|  | ||||
| class ResNetBottleneck(nn.Module): | ||||
|   expansion = 4 | ||||
|   num_conv  = 3 | ||||
|   def __init__(self, inplanes, planes, stride): | ||||
|     super(ResNetBottleneck, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     self.conv_1x1 = ConvBNReLU(inplanes, planes, 1,      1, 0, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_3x3 = ConvBNReLU(  planes, planes, 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_1x4 = ConvBNReLU(planes, planes*self.expansion, 1,      1, 0, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes*self.expansion, 1, 1, 0, False, has_avg=True , has_bn=False, has_relu=False) | ||||
|     elif inplanes != planes*self.expansion: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes*self.expansion, 1, 1, 0, False, has_avg=False, has_bn=False, has_relu=False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     self.out_dim = planes*self.expansion | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|  | ||||
|     bottleneck = self.conv_1x1(inputs) | ||||
|     bottleneck = self.conv_3x3(bottleneck) | ||||
|     bottleneck = self.conv_1x4(bottleneck) | ||||
|  | ||||
|     if self.downsample is not None: | ||||
|       residual = self.downsample(inputs) | ||||
|     else: | ||||
|       residual = inputs | ||||
|     out = residual + bottleneck | ||||
|     return F.relu(out, inplace=True) | ||||
|  | ||||
|  | ||||
|  | ||||
| class InferDepthCifarResNet(nn.Module): | ||||
|  | ||||
|   def __init__(self, block_name, depth, xblocks, num_classes, zero_init_residual): | ||||
|     super(InferDepthCifarResNet, self).__init__() | ||||
|  | ||||
|     #Model type specifies number of layers for CIFAR-10 and CIFAR-100 model | ||||
|     if block_name == 'ResNetBasicblock': | ||||
|       block = ResNetBasicblock | ||||
|       assert (depth - 2) % 6 == 0, 'depth should be one of 20, 32, 44, 56, 110' | ||||
|       layer_blocks = (depth - 2) // 6 | ||||
|     elif block_name == 'ResNetBottleneck': | ||||
|       block = ResNetBottleneck | ||||
|       assert (depth - 2) % 9 == 0, 'depth should be one of 164' | ||||
|       layer_blocks = (depth - 2) // 9 | ||||
|     else: | ||||
|       raise ValueError('invalid block : {:}'.format(block_name)) | ||||
|     assert len(xblocks) == 3, 'invalid xblocks : {:}'.format(xblocks) | ||||
|  | ||||
|     self.message     = 'InferWidthCifarResNet : Depth : {:} , Layers for each block : {:}'.format(depth, layer_blocks) | ||||
|     self.num_classes = num_classes | ||||
|     self.layers      = nn.ModuleList( [ ConvBNReLU(3, 16, 3, 1, 1, False, has_avg=False, has_bn=True, has_relu=True) ] ) | ||||
|     self.channels    = [16] | ||||
|     for stage in range(3): | ||||
|       for iL in range(layer_blocks): | ||||
|         iC       = self.channels[-1] | ||||
|         planes = 16 * (2**stage) | ||||
|         stride   = 2 if stage > 0 and iL == 0 else 1 | ||||
|         module   = block(iC, planes, stride) | ||||
|         self.channels.append( module.out_dim ) | ||||
|         self.layers.append  ( module ) | ||||
|         self.message += "\nstage={:}, ilayer={:02d}/{:02d}, block={:03d}, iC={:}, oC={:3d}, stride={:}".format(stage, iL, layer_blocks, len(self.layers)-1, planes, module.out_dim, stride) | ||||
|         if iL + 1 == xblocks[stage]: # reach the maximum depth | ||||
|           break | ||||
|    | ||||
|     self.avgpool    = nn.AvgPool2d(8) | ||||
|     self.classifier = nn.Linear(self.channels[-1], num_classes) | ||||
|      | ||||
|     self.apply(initialize_resnet) | ||||
|     if zero_init_residual: | ||||
|       for m in self.modules(): | ||||
|         if isinstance(m, ResNetBasicblock): | ||||
|           nn.init.constant_(m.conv_b.bn.weight, 0) | ||||
|         elif isinstance(m, ResNetBottleneck): | ||||
|           nn.init.constant_(m.conv_1x4.bn.weight, 0) | ||||
|  | ||||
|   def get_message(self): | ||||
|     return self.message | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     x = inputs | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       x = layer( x ) | ||||
|     features = self.avgpool(x) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     logits   = self.classifier(features) | ||||
|     return features, logits | ||||
							
								
								
									
										160
									
								
								graph_dit/naswot/models/shape_infers/InferCifarResNet_width.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								graph_dit/naswot/models/shape_infers/InferCifarResNet_width.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,160 @@ | ||||
| ##################################################### | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019.01 # | ||||
| ##################################################### | ||||
| import torch.nn as nn | ||||
| import torch.nn.functional as F | ||||
| from ..initialization import initialize_resnet | ||||
|  | ||||
|  | ||||
| class ConvBNReLU(nn.Module): | ||||
|    | ||||
|   def __init__(self, nIn, nOut, kernel, stride, padding, bias, has_avg, has_bn, has_relu): | ||||
|     super(ConvBNReLU, self).__init__() | ||||
|     if has_avg : self.avg = nn.AvgPool2d(kernel_size=2, stride=2, padding=0) | ||||
|     else       : self.avg = None | ||||
|     self.conv = nn.Conv2d(nIn, nOut, kernel_size=kernel, stride=stride, padding=padding, dilation=1, groups=1, bias=bias) | ||||
|     if has_bn  : self.bn  = nn.BatchNorm2d(nOut) | ||||
|     else       : self.bn  = None | ||||
|     if has_relu: self.relu = nn.ReLU(inplace=True) | ||||
|     else       : self.relu = None | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.avg : out = self.avg( inputs ) | ||||
|     else        : out = inputs | ||||
|     conv = self.conv( out ) | ||||
|     if self.bn  : out = self.bn( conv ) | ||||
|     else        : out = conv | ||||
|     if self.relu: out = self.relu( out ) | ||||
|     else        : out = out | ||||
|  | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class ResNetBasicblock(nn.Module): | ||||
|   num_conv  = 2 | ||||
|   expansion = 1 | ||||
|   def __init__(self, iCs, stride): | ||||
|     super(ResNetBasicblock, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     assert isinstance(iCs, tuple) or isinstance(iCs, list), 'invalid type of iCs : {:}'.format( iCs ) | ||||
|     assert len(iCs) == 3,'invalid lengths of iCs : {:}'.format(iCs) | ||||
|      | ||||
|     self.conv_a = ConvBNReLU(iCs[0], iCs[1], 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_b = ConvBNReLU(iCs[1], iCs[2], 3,      1, 1, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|     residual_in = iCs[0] | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(iCs[0], iCs[2], 1, 1, 0, False, has_avg=True, has_bn=False, has_relu=False) | ||||
|       residual_in = iCs[2] | ||||
|     elif iCs[0] != iCs[2]: | ||||
|       self.downsample = ConvBNReLU(iCs[0], iCs[2], 1, 1, 0, False, has_avg=False,has_bn=True , has_relu=False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     #self.out_dim  = max(residual_in, iCs[2]) | ||||
|     self.out_dim  = iCs[2] | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     basicblock = self.conv_a(inputs) | ||||
|     basicblock = self.conv_b(basicblock) | ||||
|  | ||||
|     if self.downsample is not None: | ||||
|       residual = self.downsample(inputs) | ||||
|     else: | ||||
|       residual = inputs | ||||
|     out = residual + basicblock | ||||
|     return F.relu(out, inplace=True) | ||||
|  | ||||
|  | ||||
|  | ||||
| class ResNetBottleneck(nn.Module): | ||||
|   expansion = 4 | ||||
|   num_conv  = 3 | ||||
|   def __init__(self, iCs, stride): | ||||
|     super(ResNetBottleneck, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     assert isinstance(iCs, tuple) or isinstance(iCs, list), 'invalid type of iCs : {:}'.format( iCs ) | ||||
|     assert len(iCs) == 4,'invalid lengths of iCs : {:}'.format(iCs) | ||||
|     self.conv_1x1 = ConvBNReLU(iCs[0], iCs[1], 1,      1, 0, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_3x3 = ConvBNReLU(iCs[1], iCs[2], 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_1x4 = ConvBNReLU(iCs[2], iCs[3], 1,      1, 0, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|     residual_in = iCs[0] | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(iCs[0], iCs[3], 1, 1, 0, False, has_avg=True , has_bn=False, has_relu=False) | ||||
|       residual_in     = iCs[3] | ||||
|     elif iCs[0] != iCs[3]: | ||||
|       self.downsample = ConvBNReLU(iCs[0], iCs[3], 1, 1, 0, False, has_avg=False, has_bn=False, has_relu=False) | ||||
|       residual_in     = iCs[3] | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     #self.out_dim = max(residual_in, iCs[3]) | ||||
|     self.out_dim = iCs[3] | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|  | ||||
|     bottleneck = self.conv_1x1(inputs) | ||||
|     bottleneck = self.conv_3x3(bottleneck) | ||||
|     bottleneck = self.conv_1x4(bottleneck) | ||||
|  | ||||
|     if self.downsample is not None: | ||||
|       residual = self.downsample(inputs) | ||||
|     else: | ||||
|       residual = inputs | ||||
|     out = residual + bottleneck | ||||
|     return F.relu(out, inplace=True) | ||||
|  | ||||
|  | ||||
|  | ||||
| class InferWidthCifarResNet(nn.Module): | ||||
|  | ||||
|   def __init__(self, block_name, depth, xchannels, num_classes, zero_init_residual): | ||||
|     super(InferWidthCifarResNet, self).__init__() | ||||
|  | ||||
|     #Model type specifies number of layers for CIFAR-10 and CIFAR-100 model | ||||
|     if block_name == 'ResNetBasicblock': | ||||
|       block = ResNetBasicblock | ||||
|       assert (depth - 2) % 6 == 0, 'depth should be one of 20, 32, 44, 56, 110' | ||||
|       layer_blocks = (depth - 2) // 6 | ||||
|     elif block_name == 'ResNetBottleneck': | ||||
|       block = ResNetBottleneck | ||||
|       assert (depth - 2) % 9 == 0, 'depth should be one of 164' | ||||
|       layer_blocks = (depth - 2) // 9 | ||||
|     else: | ||||
|       raise ValueError('invalid block : {:}'.format(block_name)) | ||||
|  | ||||
|     self.message     = 'InferWidthCifarResNet : Depth : {:} , Layers for each block : {:}'.format(depth, layer_blocks) | ||||
|     self.num_classes = num_classes | ||||
|     self.xchannels   = xchannels | ||||
|     self.layers      = nn.ModuleList( [ ConvBNReLU(xchannels[0], xchannels[1], 3, 1, 1, False, has_avg=False, has_bn=True, has_relu=True) ] ) | ||||
|     last_channel_idx = 1 | ||||
|     for stage in range(3): | ||||
|       for iL in range(layer_blocks): | ||||
|         num_conv = block.num_conv  | ||||
|         iCs      = self.xchannels[last_channel_idx:last_channel_idx+num_conv+1] | ||||
|         stride   = 2 if stage > 0 and iL == 0 else 1 | ||||
|         module   = block(iCs, stride) | ||||
|         last_channel_idx += num_conv | ||||
|         self.xchannels[last_channel_idx] = module.out_dim | ||||
|         self.layers.append  ( module ) | ||||
|         self.message += "\nstage={:}, ilayer={:02d}/{:02d}, block={:03d}, iCs={:}, oC={:3d}, stride={:}".format(stage, iL, layer_blocks, len(self.layers)-1, iCs, module.out_dim, stride) | ||||
|    | ||||
|     self.avgpool    = nn.AvgPool2d(8) | ||||
|     self.classifier = nn.Linear(self.xchannels[-1], num_classes) | ||||
|      | ||||
|     self.apply(initialize_resnet) | ||||
|     if zero_init_residual: | ||||
|       for m in self.modules(): | ||||
|         if isinstance(m, ResNetBasicblock): | ||||
|           nn.init.constant_(m.conv_b.bn.weight, 0) | ||||
|         elif isinstance(m, ResNetBottleneck): | ||||
|           nn.init.constant_(m.conv_1x4.bn.weight, 0) | ||||
|  | ||||
|   def get_message(self): | ||||
|     return self.message | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     x = inputs | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       x = layer( x ) | ||||
|     features = self.avgpool(x) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     logits   = self.classifier(features) | ||||
|     return features, logits | ||||
							
								
								
									
										170
									
								
								graph_dit/naswot/models/shape_infers/InferImagenetResNet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								graph_dit/naswot/models/shape_infers/InferImagenetResNet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,170 @@ | ||||
| ##################################################### | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019.01 # | ||||
| ##################################################### | ||||
| import torch.nn as nn | ||||
| import torch.nn.functional as F | ||||
| from ..initialization import initialize_resnet | ||||
|  | ||||
|  | ||||
| class ConvBNReLU(nn.Module): | ||||
|    | ||||
|   num_conv  = 1 | ||||
|   def __init__(self, nIn, nOut, kernel, stride, padding, bias, has_avg, has_bn, has_relu): | ||||
|     super(ConvBNReLU, self).__init__() | ||||
|     if has_avg : self.avg = nn.AvgPool2d(kernel_size=2, stride=2, padding=0) | ||||
|     else       : self.avg = None | ||||
|     self.conv = nn.Conv2d(nIn, nOut, kernel_size=kernel, stride=stride, padding=padding, dilation=1, groups=1, bias=bias) | ||||
|     if has_bn  : self.bn  = nn.BatchNorm2d(nOut) | ||||
|     else       : self.bn  = None | ||||
|     if has_relu: self.relu = nn.ReLU(inplace=True) | ||||
|     else       : self.relu = None | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.avg : out = self.avg( inputs ) | ||||
|     else        : out = inputs | ||||
|     conv = self.conv( out ) | ||||
|     if self.bn  : out = self.bn( conv ) | ||||
|     else        : out = conv | ||||
|     if self.relu: out = self.relu( out ) | ||||
|     else        : out = out | ||||
|  | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class ResNetBasicblock(nn.Module): | ||||
|   num_conv  = 2 | ||||
|   expansion = 1 | ||||
|   def __init__(self, iCs, stride): | ||||
|     super(ResNetBasicblock, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     assert isinstance(iCs, tuple) or isinstance(iCs, list), 'invalid type of iCs : {:}'.format( iCs ) | ||||
|     assert len(iCs) == 3,'invalid lengths of iCs : {:}'.format(iCs) | ||||
|      | ||||
|     self.conv_a = ConvBNReLU(iCs[0], iCs[1], 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_b = ConvBNReLU(iCs[1], iCs[2], 3,      1, 1, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|     residual_in = iCs[0] | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(iCs[0], iCs[2], 1, 1, 0, False, has_avg=True, has_bn=True, has_relu=False) | ||||
|       residual_in = iCs[2] | ||||
|     elif iCs[0] != iCs[2]: | ||||
|       self.downsample = ConvBNReLU(iCs[0], iCs[2], 1, 1, 0, False, has_avg=False,has_bn=True , has_relu=False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     #self.out_dim  = max(residual_in, iCs[2]) | ||||
|     self.out_dim  = iCs[2] | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     basicblock = self.conv_a(inputs) | ||||
|     basicblock = self.conv_b(basicblock) | ||||
|  | ||||
|     if self.downsample is not None: | ||||
|       residual = self.downsample(inputs) | ||||
|     else: | ||||
|       residual = inputs | ||||
|     out = residual + basicblock | ||||
|     return F.relu(out, inplace=True) | ||||
|  | ||||
|  | ||||
|  | ||||
| class ResNetBottleneck(nn.Module): | ||||
|   expansion = 4 | ||||
|   num_conv  = 3 | ||||
|   def __init__(self, iCs, stride): | ||||
|     super(ResNetBottleneck, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     assert isinstance(iCs, tuple) or isinstance(iCs, list), 'invalid type of iCs : {:}'.format( iCs ) | ||||
|     assert len(iCs) == 4,'invalid lengths of iCs : {:}'.format(iCs) | ||||
|     self.conv_1x1 = ConvBNReLU(iCs[0], iCs[1], 1,      1, 0, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_3x3 = ConvBNReLU(iCs[1], iCs[2], 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_1x4 = ConvBNReLU(iCs[2], iCs[3], 1,      1, 0, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|     residual_in = iCs[0] | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(iCs[0], iCs[3], 1, 1, 0, False, has_avg=True , has_bn=True, has_relu=False) | ||||
|       residual_in     = iCs[3] | ||||
|     elif iCs[0] != iCs[3]: | ||||
|       self.downsample = ConvBNReLU(iCs[0], iCs[3], 1, 1, 0, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|       residual_in     = iCs[3] | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     #self.out_dim = max(residual_in, iCs[3]) | ||||
|     self.out_dim = iCs[3] | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|  | ||||
|     bottleneck = self.conv_1x1(inputs) | ||||
|     bottleneck = self.conv_3x3(bottleneck) | ||||
|     bottleneck = self.conv_1x4(bottleneck) | ||||
|  | ||||
|     if self.downsample is not None: | ||||
|       residual = self.downsample(inputs) | ||||
|     else: | ||||
|       residual = inputs | ||||
|     out = residual + bottleneck | ||||
|     return F.relu(out, inplace=True) | ||||
|  | ||||
|  | ||||
|  | ||||
| class InferImagenetResNet(nn.Module): | ||||
|  | ||||
|   def __init__(self, block_name, layers, xblocks, xchannels, deep_stem, num_classes, zero_init_residual): | ||||
|     super(InferImagenetResNet, self).__init__() | ||||
|  | ||||
|     #Model type specifies number of layers for CIFAR-10 and CIFAR-100 model | ||||
|     if block_name == 'BasicBlock': | ||||
|       block = ResNetBasicblock | ||||
|     elif block_name == 'Bottleneck': | ||||
|       block = ResNetBottleneck | ||||
|     else: | ||||
|       raise ValueError('invalid block : {:}'.format(block_name)) | ||||
|     assert len(xblocks) == len(layers), 'invalid layers : {:} vs xblocks : {:}'.format(layers, xblocks) | ||||
|  | ||||
|     self.message     = 'InferImagenetResNet : Depth : {:} -> {:}, Layers for each block : {:}'.format(sum(layers)*block.num_conv, sum(xblocks)*block.num_conv, xblocks) | ||||
|     self.num_classes = num_classes | ||||
|     self.xchannels   = xchannels | ||||
|     if not deep_stem: | ||||
|       self.layers      = nn.ModuleList( [ ConvBNReLU(xchannels[0], xchannels[1], 7, 2, 3, False, has_avg=False, has_bn=True, has_relu=True) ] ) | ||||
|       last_channel_idx = 1 | ||||
|     else: | ||||
|       self.layers      = nn.ModuleList( [ ConvBNReLU(xchannels[0], xchannels[1], 3, 2, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|                                          ,ConvBNReLU(xchannels[1], xchannels[2], 3, 1, 1, False, has_avg=False, has_bn=True, has_relu=True) ] ) | ||||
|       last_channel_idx = 2 | ||||
|     self.layers.append( nn.MaxPool2d(kernel_size=3, stride=2, padding=1) ) | ||||
|     for stage, layer_blocks in enumerate(layers): | ||||
|       for iL in range(layer_blocks): | ||||
|         num_conv = block.num_conv  | ||||
|         iCs      = self.xchannels[last_channel_idx:last_channel_idx+num_conv+1] | ||||
|         stride   = 2 if stage > 0 and iL == 0 else 1 | ||||
|         module   = block(iCs, stride) | ||||
|         last_channel_idx += num_conv | ||||
|         self.xchannels[last_channel_idx] = module.out_dim | ||||
|         self.layers.append  ( module ) | ||||
|         self.message += "\nstage={:}, ilayer={:02d}/{:02d}, block={:03d}, iCs={:}, oC={:3d}, stride={:}".format(stage, iL, layer_blocks, len(self.layers)-1, iCs, module.out_dim, stride) | ||||
|         if iL + 1 == xblocks[stage]: # reach the maximum depth | ||||
|           out_channel = module.out_dim | ||||
|           for iiL in range(iL+1, layer_blocks): | ||||
|             last_channel_idx += num_conv | ||||
|           self.xchannels[last_channel_idx] = module.out_dim | ||||
|           break | ||||
|     assert last_channel_idx + 1 == len(self.xchannels), '{:} vs {:}'.format(last_channel_idx, len(self.xchannels)) | ||||
|     self.avgpool    = nn.AdaptiveAvgPool2d((1,1)) | ||||
|     self.classifier = nn.Linear(self.xchannels[-1], num_classes) | ||||
|      | ||||
|     self.apply(initialize_resnet) | ||||
|     if zero_init_residual: | ||||
|       for m in self.modules(): | ||||
|         if isinstance(m, ResNetBasicblock): | ||||
|           nn.init.constant_(m.conv_b.bn.weight, 0) | ||||
|         elif isinstance(m, ResNetBottleneck): | ||||
|           nn.init.constant_(m.conv_1x4.bn.weight, 0) | ||||
|  | ||||
|   def get_message(self): | ||||
|     return self.message | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     x = inputs | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       x = layer( x ) | ||||
|     features = self.avgpool(x) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     logits   = self.classifier(features) | ||||
|     return features, logits | ||||
							
								
								
									
										122
									
								
								graph_dit/naswot/models/shape_infers/InferMobileNetV2.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								graph_dit/naswot/models/shape_infers/InferMobileNetV2.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| ##################################################### | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019.01 # | ||||
| ##################################################### | ||||
| # MobileNetV2: Inverted Residuals and Linear Bottlenecks, CVPR 2018 | ||||
| from torch import nn | ||||
| from ..initialization import initialize_resnet | ||||
| from ..SharedUtils    import parse_channel_info | ||||
|  | ||||
|  | ||||
| class ConvBNReLU(nn.Module): | ||||
|   def __init__(self, in_planes, out_planes, kernel_size, stride, groups, has_bn=True, has_relu=True): | ||||
|     super(ConvBNReLU, self).__init__() | ||||
|     padding = (kernel_size - 1) // 2 | ||||
|     self.conv = nn.Conv2d(in_planes, out_planes, kernel_size, stride, padding, groups=groups, bias=False) | ||||
|     if has_bn: self.bn = nn.BatchNorm2d(out_planes) | ||||
|     else     : self.bn = None | ||||
|     if has_relu: self.relu = nn.ReLU6(inplace=True) | ||||
|     else       : self.relu = None | ||||
|    | ||||
|   def forward(self, x): | ||||
|     out = self.conv( x ) | ||||
|     if self.bn:   out = self.bn  ( out ) | ||||
|     if self.relu: out = self.relu( out ) | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class InvertedResidual(nn.Module): | ||||
|   def __init__(self, channels, stride, expand_ratio, additive): | ||||
|     super(InvertedResidual, self).__init__() | ||||
|     self.stride = stride | ||||
|     assert stride in [1, 2], 'invalid stride : {:}'.format(stride) | ||||
|     assert len(channels) in [2, 3], 'invalid channels : {:}'.format(channels) | ||||
|  | ||||
|     if len(channels) == 2: | ||||
|       layers = [] | ||||
|     else: | ||||
|       layers = [ConvBNReLU(channels[0], channels[1], 1, 1, 1)] | ||||
|     layers.extend([ | ||||
|       # dw | ||||
|       ConvBNReLU(channels[-2], channels[-2], 3, stride, channels[-2]), | ||||
|       # pw-linear | ||||
|       ConvBNReLU(channels[-2], channels[-1], 1, 1, 1, True, False), | ||||
|     ]) | ||||
|     self.conv = nn.Sequential(*layers) | ||||
|     self.additive = additive | ||||
|     if self.additive and channels[0] != channels[-1]: | ||||
|       self.shortcut = ConvBNReLU(channels[0], channels[-1], 1, 1, 1, True, False) | ||||
|     else: | ||||
|       self.shortcut = None | ||||
|     self.out_dim  = channels[-1] | ||||
|  | ||||
|   def forward(self, x): | ||||
|     out = self.conv(x) | ||||
|     # if self.additive: return additive_func(out, x) | ||||
|     if self.shortcut: return out + self.shortcut(x) | ||||
|     else            : return out | ||||
|  | ||||
|  | ||||
| class InferMobileNetV2(nn.Module): | ||||
|   def __init__(self, num_classes, xchannels, xblocks, dropout): | ||||
|     super(InferMobileNetV2, self).__init__() | ||||
|     block = InvertedResidual | ||||
|     inverted_residual_setting = [ | ||||
|       # t, c,  n, s | ||||
|       [1, 16 , 1, 1], | ||||
|       [6, 24 , 2, 2], | ||||
|       [6, 32 , 3, 2], | ||||
|       [6, 64 , 4, 2], | ||||
|       [6, 96 , 3, 1], | ||||
|       [6, 160, 3, 2], | ||||
|       [6, 320, 1, 1], | ||||
|     ] | ||||
|     assert len(inverted_residual_setting) == len(xblocks), 'invalid number of layers : {:} vs {:}'.format(len(inverted_residual_setting), len(xblocks)) | ||||
|     for block_num, ir_setting in zip(xblocks, inverted_residual_setting): | ||||
|       assert block_num <= ir_setting[2], '{:} vs {:}'.format(block_num, ir_setting) | ||||
|     xchannels = parse_channel_info(xchannels) | ||||
|     #for i, chs in enumerate(xchannels): | ||||
|     #  if i > 0: assert chs[0] == xchannels[i-1][-1], 'Layer[{:}] is invalid {:} vs {:}'.format(i, xchannels[i-1], chs) | ||||
|     self.xchannels = xchannels | ||||
|     self.message     = 'InferMobileNetV2 : xblocks={:}'.format(xblocks) | ||||
|     # building first layer | ||||
|     features = [ConvBNReLU(xchannels[0][0], xchannels[0][1], 3, 2, 1)] | ||||
|     last_channel_idx = 1 | ||||
|  | ||||
|     # building inverted residual blocks | ||||
|     for stage, (t, c, n, s) in enumerate(inverted_residual_setting): | ||||
|       for i in range(n): | ||||
|         stride = s if i == 0 else 1 | ||||
|         additv = True if i > 0 else False | ||||
|         module = block(self.xchannels[last_channel_idx], stride, t, additv) | ||||
|         features.append(module) | ||||
|         self.message += "\nstage={:}, ilayer={:02d}/{:02d}, block={:03d}, Cs={:}, stride={:}, expand={:}, original-C={:}".format(stage, i, n, len(features), self.xchannels[last_channel_idx], stride, t, c) | ||||
|         last_channel_idx += 1 | ||||
|         if i + 1 == xblocks[stage]: | ||||
|           out_channel = module.out_dim | ||||
|           for iiL in range(i+1, n): | ||||
|             last_channel_idx += 1 | ||||
|           self.xchannels[last_channel_idx][0] = module.out_dim | ||||
|           break | ||||
|     # building last several layers | ||||
|     features.append(ConvBNReLU(self.xchannels[last_channel_idx][0], self.xchannels[last_channel_idx][1], 1, 1, 1)) | ||||
|     assert last_channel_idx + 2 == len(self.xchannels), '{:} vs {:}'.format(last_channel_idx, len(self.xchannels)) | ||||
|     # make it nn.Sequential | ||||
|     self.features = nn.Sequential(*features) | ||||
|  | ||||
|     # building classifier | ||||
|     self.classifier = nn.Sequential( | ||||
|       nn.Dropout(dropout), | ||||
|       nn.Linear(self.xchannels[last_channel_idx][1], num_classes), | ||||
|     ) | ||||
|  | ||||
|     # weight initialization | ||||
|     self.apply( initialize_resnet ) | ||||
|  | ||||
|   def get_message(self): | ||||
|     return self.message | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     features = self.features(inputs) | ||||
|     vectors  = features.mean([2, 3]) | ||||
|     predicts = self.classifier(vectors) | ||||
|     return features, predicts | ||||
							
								
								
									
										58
									
								
								graph_dit/naswot/models/shape_infers/InferTinyCellNet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								graph_dit/naswot/models/shape_infers/InferTinyCellNet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| ##################################################### | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019.01 # | ||||
| ##################################################### | ||||
| from typing import List, Text, Any | ||||
| import torch.nn as nn | ||||
| from models.cell_operations import ResNetBasicblock | ||||
| from models.cell_infers.cells import InferCell | ||||
|  | ||||
|  | ||||
| class DynamicShapeTinyNet(nn.Module): | ||||
|  | ||||
|   def __init__(self, channels: List[int], genotype: Any, num_classes: int): | ||||
|     super(DynamicShapeTinyNet, self).__init__() | ||||
|     self._channels = channels | ||||
|     if len(channels) % 3 != 2: | ||||
|       raise ValueError('invalid number of layers : {:}'.format(len(channels))) | ||||
|     self._num_stage = N = len(channels) // 3 | ||||
|  | ||||
|     self.stem = nn.Sequential( | ||||
|                     nn.Conv2d(3, channels[0], kernel_size=3, padding=1, bias=False), | ||||
|                     nn.BatchNorm2d(channels[0])) | ||||
|  | ||||
|     # layer_channels   = [C    ] * N + [C*2 ] + [C*2  ] * N + [C*4 ] + [C*4  ] * N     | ||||
|     layer_reductions = [False] * N + [True] + [False] * N + [True] + [False] * N | ||||
|  | ||||
|     c_prev = channels[0] | ||||
|     self.cells = nn.ModuleList() | ||||
|     for index, (c_curr, reduction) in enumerate(zip(channels, layer_reductions)): | ||||
|       if reduction : cell = ResNetBasicblock(c_prev, c_curr, 2, True) | ||||
|       else         : cell = InferCell(genotype, c_prev, c_curr, 1) | ||||
|       self.cells.append( cell ) | ||||
|       c_prev = cell.out_dim | ||||
|     self._num_layer = len(self.cells) | ||||
|  | ||||
|     self.lastact = nn.Sequential(nn.BatchNorm2d(c_prev), nn.ReLU(inplace=True)) | ||||
|     self.global_pooling = nn.AdaptiveAvgPool2d(1) | ||||
|     self.classifier = nn.Linear(c_prev, num_classes) | ||||
|  | ||||
|   def get_message(self) -> Text: | ||||
|     string = self.extra_repr() | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       string += '\n {:02d}/{:02d} :: {:}'.format(i, len(self.cells), cell.extra_repr()) | ||||
|     return string | ||||
|  | ||||
|   def extra_repr(self): | ||||
|     return ('{name}(C={_channels}, N={_num_stage}, L={_num_layer})'.format(name=self.__class__.__name__, **self.__dict__)) | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     feature = self.stem(inputs) | ||||
|     for i, cell in enumerate(self.cells): | ||||
|       feature = cell(feature) | ||||
|  | ||||
|     out = self.lastact(feature) | ||||
|     out = self.global_pooling( out ) | ||||
|     out = out.view(out.size(0), -1) | ||||
|     logits = self.classifier(out) | ||||
|  | ||||
|     return out, logits | ||||
							
								
								
									
										9
									
								
								graph_dit/naswot/models/shape_infers/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								graph_dit/naswot/models/shape_infers/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| ##################################################### | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019.01 # | ||||
| ##################################################### | ||||
| from .InferCifarResNet_width import InferWidthCifarResNet | ||||
| from .InferImagenetResNet import InferImagenetResNet | ||||
| from .InferCifarResNet_depth import InferDepthCifarResNet | ||||
| from .InferCifarResNet import InferCifarResNet | ||||
| from .InferMobileNetV2 import InferMobileNetV2 | ||||
| from .InferTinyCellNet import DynamicShapeTinyNet | ||||
							
								
								
									
										5
									
								
								graph_dit/naswot/models/shape_infers/shared_utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								graph_dit/naswot/models/shape_infers/shared_utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| def parse_channel_info(xstring): | ||||
|   blocks = xstring.split(' ') | ||||
|   blocks = [x.split('-') for x in blocks] | ||||
|   blocks = [[int(_) for _ in x] for x in blocks] | ||||
|   return blocks | ||||
							
								
								
									
										502
									
								
								graph_dit/naswot/models/shape_searchs/SearchCifarResNet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										502
									
								
								graph_dit/naswot/models/shape_searchs/SearchCifarResNet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,502 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| import math, torch | ||||
| from collections import OrderedDict | ||||
| from bisect import bisect_right | ||||
| import torch.nn as nn | ||||
| from ..initialization import initialize_resnet | ||||
| from ..SharedUtils    import additive_func | ||||
| from .SoftSelect      import select2withP, ChannelWiseInter | ||||
| from .SoftSelect      import linear_forward | ||||
| from .SoftSelect      import get_width_choices | ||||
|  | ||||
|  | ||||
| def get_depth_choices(nDepth, return_num): | ||||
|   if nDepth == 2: | ||||
|     choices = (1, 2) | ||||
|   elif nDepth == 3: | ||||
|     choices = (1, 2, 3) | ||||
|   elif nDepth > 3: | ||||
|     choices = list(range(1, nDepth+1, 2)) | ||||
|     if choices[-1] < nDepth: choices.append(nDepth) | ||||
|   else: | ||||
|     raise ValueError('invalid nDepth : {:}'.format(nDepth)) | ||||
|   if return_num: return len(choices) | ||||
|   else         : return choices | ||||
|    | ||||
|  | ||||
| def conv_forward(inputs, conv, choices): | ||||
|   iC = conv.in_channels | ||||
|   fill_size = list(inputs.size()) | ||||
|   fill_size[1] = iC - fill_size[1] | ||||
|   filled  = torch.zeros(fill_size, device=inputs.device) | ||||
|   xinputs = torch.cat((inputs, filled), dim=1) | ||||
|   outputs = conv(xinputs) | ||||
|   selecteds = [outputs[:,:oC] for oC in choices] | ||||
|   return selecteds | ||||
|  | ||||
|  | ||||
| class ConvBNReLU(nn.Module): | ||||
|   num_conv  = 1 | ||||
|   def __init__(self, nIn, nOut, kernel, stride, padding, bias, has_avg, has_bn, has_relu): | ||||
|     super(ConvBNReLU, self).__init__() | ||||
|     self.InShape  = None | ||||
|     self.OutShape = None | ||||
|     self.choices  = get_width_choices(nOut) | ||||
|     self.register_buffer('choices_tensor', torch.Tensor( self.choices )) | ||||
|  | ||||
|     if has_avg : self.avg = nn.AvgPool2d(kernel_size=2, stride=2, padding=0) | ||||
|     else       : self.avg = None | ||||
|     self.conv = nn.Conv2d(nIn, nOut, kernel_size=kernel, stride=stride, padding=padding, dilation=1, groups=1, bias=bias) | ||||
|     #if has_bn  : self.bn  = nn.BatchNorm2d(nOut) | ||||
|     #else       : self.bn  = None | ||||
|     self.has_bn = has_bn | ||||
|     self.BNs  = nn.ModuleList() | ||||
|     for i, _out in enumerate(self.choices): | ||||
|       self.BNs.append(nn.BatchNorm2d(_out)) | ||||
|     if has_relu: self.relu = nn.ReLU(inplace=True) | ||||
|     else       : self.relu = None | ||||
|     self.in_dim   = nIn | ||||
|     self.out_dim  = nOut | ||||
|     self.search_mode = 'basic' | ||||
|  | ||||
|   def get_flops(self, channels, check_range=True, divide=1): | ||||
|     iC, oC = channels | ||||
|     if check_range: assert iC <= self.conv.in_channels and oC <= self.conv.out_channels, '{:} vs {:}  |  {:} vs {:}'.format(iC, self.conv.in_channels, oC, self.conv.out_channels) | ||||
|     assert isinstance(self.InShape, tuple) and len(self.InShape) == 2, 'invalid in-shape : {:}'.format(self.InShape) | ||||
|     assert isinstance(self.OutShape, tuple) and len(self.OutShape) == 2, 'invalid out-shape : {:}'.format(self.OutShape) | ||||
|     #conv_per_position_flops = self.conv.kernel_size[0] * self.conv.kernel_size[1] * iC * oC / self.conv.groups | ||||
|     conv_per_position_flops = (self.conv.kernel_size[0] * self.conv.kernel_size[1] * 1.0 / self.conv.groups) | ||||
|     all_positions = self.OutShape[0] * self.OutShape[1] | ||||
|     flops = (conv_per_position_flops * all_positions / divide) * iC * oC | ||||
|     if self.conv.bias is not None: flops += all_positions / divide | ||||
|     return flops | ||||
|  | ||||
|   def get_range(self): | ||||
|     return [self.choices] | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.search_mode == 'basic': | ||||
|       return self.basic_forward(inputs) | ||||
|     elif self.search_mode == 'search': | ||||
|       return self.search_forward(inputs) | ||||
|     else: | ||||
|       raise ValueError('invalid search_mode = {:}'.format(self.search_mode)) | ||||
|  | ||||
|   def search_forward(self, tuple_inputs): | ||||
|     assert isinstance(tuple_inputs, tuple) and len(tuple_inputs) == 5, 'invalid type input : {:}'.format( type(tuple_inputs) ) | ||||
|     inputs, expected_inC, probability, index, prob = tuple_inputs | ||||
|     index, prob = torch.squeeze(index).tolist(), torch.squeeze(prob) | ||||
|     probability = torch.squeeze(probability) | ||||
|     assert len(index) == 2, 'invalid length : {:}'.format(index) | ||||
|     # compute expected flop | ||||
|     #coordinates   = torch.arange(self.x_range[0], self.x_range[1]+1).type_as(probability) | ||||
|     expected_outC = (self.choices_tensor * probability).sum() | ||||
|     expected_flop = self.get_flops([expected_inC, expected_outC], False, 1e6) | ||||
|     if self.avg : out = self.avg( inputs ) | ||||
|     else        : out = inputs | ||||
|     # convolutional layer | ||||
|     out_convs = conv_forward(out, self.conv, [self.choices[i] for i in index]) | ||||
|     out_bns   = [self.BNs[idx](out_conv) for idx, out_conv in zip(index, out_convs)] | ||||
|     # merge | ||||
|     out_channel = max([x.size(1) for x in out_bns]) | ||||
|     outA = ChannelWiseInter(out_bns[0], out_channel) | ||||
|     outB = ChannelWiseInter(out_bns[1], out_channel) | ||||
|     out  = outA * prob[0] + outB * prob[1] | ||||
|     #out = additive_func(out_bns[0]*prob[0], out_bns[1]*prob[1]) | ||||
|  | ||||
|     if self.relu: out = self.relu( out ) | ||||
|     else        : out = out | ||||
|     return out, expected_outC, expected_flop | ||||
|  | ||||
|   def basic_forward(self, inputs): | ||||
|     if self.avg : out = self.avg( inputs ) | ||||
|     else        : out = inputs | ||||
|     conv = self.conv( out ) | ||||
|     if self.has_bn:out= self.BNs[-1]( conv ) | ||||
|     else        : out = conv | ||||
|     if self.relu: out = self.relu( out ) | ||||
|     else        : out = out | ||||
|     if self.InShape is None: | ||||
|       self.InShape  = (inputs.size(-2), inputs.size(-1)) | ||||
|       self.OutShape = (out.size(-2)   , out.size(-1)) | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class ResNetBasicblock(nn.Module): | ||||
|   expansion = 1 | ||||
|   num_conv  = 2 | ||||
|   def __init__(self, inplanes, planes, stride): | ||||
|     super(ResNetBasicblock, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     self.conv_a = ConvBNReLU(inplanes, planes, 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_b = ConvBNReLU(  planes, planes, 3,      1, 1, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes, 1, 1, 0, False, has_avg=True, has_bn=False, has_relu=False) | ||||
|     elif inplanes != planes: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes, 1, 1, 0, False, has_avg=False,has_bn=True , has_relu=False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     self.out_dim     = planes | ||||
|     self.search_mode = 'basic' | ||||
|  | ||||
|   def get_range(self): | ||||
|     return self.conv_a.get_range() + self.conv_b.get_range() | ||||
|  | ||||
|   def get_flops(self, channels): | ||||
|     assert len(channels) == 3, 'invalid channels : {:}'.format(channels) | ||||
|     flop_A = self.conv_a.get_flops([channels[0], channels[1]]) | ||||
|     flop_B = self.conv_b.get_flops([channels[1], channels[2]]) | ||||
|     if hasattr(self.downsample, 'get_flops'): | ||||
|       flop_C = self.downsample.get_flops([channels[0], channels[-1]]) | ||||
|     else: | ||||
|       flop_C = 0 | ||||
|     if channels[0] != channels[-1] and self.downsample is None: # this short-cut will be added during the infer-train | ||||
|       flop_C = channels[0] * channels[-1] * self.conv_b.OutShape[0] * self.conv_b.OutShape[1] | ||||
|     return flop_A + flop_B + flop_C | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.search_mode == 'basic'   : return self.basic_forward(inputs) | ||||
|     elif self.search_mode == 'search': return self.search_forward(inputs) | ||||
|     else: raise ValueError('invalid search_mode = {:}'.format(self.search_mode)) | ||||
|  | ||||
|   def search_forward(self, tuple_inputs): | ||||
|     assert isinstance(tuple_inputs, tuple) and len(tuple_inputs) == 5, 'invalid type input : {:}'.format( type(tuple_inputs) ) | ||||
|     inputs, expected_inC, probability, indexes, probs = tuple_inputs | ||||
|     assert indexes.size(0) == 2 and probs.size(0) == 2 and probability.size(0) == 2 | ||||
|     out_a, expected_inC_a, expected_flop_a = self.conv_a( (inputs, expected_inC  , probability[0], indexes[0], probs[0]) ) | ||||
|     out_b, expected_inC_b, expected_flop_b = self.conv_b( (out_a , expected_inC_a, probability[1], indexes[1], probs[1]) ) | ||||
|     if self.downsample is not None: | ||||
|       residual, _, expected_flop_c = self.downsample( (inputs, expected_inC  , probability[1], indexes[1], probs[1]) ) | ||||
|     else: | ||||
|       residual, expected_flop_c = inputs, 0 | ||||
|     out = additive_func(residual, out_b) | ||||
|     return nn.functional.relu(out, inplace=True), expected_inC_b, sum([expected_flop_a, expected_flop_b, expected_flop_c]) | ||||
|  | ||||
|   def basic_forward(self, inputs): | ||||
|     basicblock = self.conv_a(inputs) | ||||
|     basicblock = self.conv_b(basicblock) | ||||
|     if self.downsample is not None: residual = self.downsample(inputs) | ||||
|     else                          : residual = inputs | ||||
|     out = additive_func(residual, basicblock) | ||||
|     return nn.functional.relu(out, inplace=True) | ||||
|  | ||||
|  | ||||
|  | ||||
| class ResNetBottleneck(nn.Module): | ||||
|   expansion = 4 | ||||
|   num_conv  = 3 | ||||
|   def __init__(self, inplanes, planes, stride): | ||||
|     super(ResNetBottleneck, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     self.conv_1x1 = ConvBNReLU(inplanes, planes, 1,      1, 0, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_3x3 = ConvBNReLU(  planes, planes, 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_1x4 = ConvBNReLU(planes, planes*self.expansion, 1, 1, 0, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes*self.expansion, 1, 1, 0, False, has_avg=True, has_bn=False, has_relu=False) | ||||
|     elif inplanes != planes*self.expansion: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes*self.expansion, 1, 1, 0, False, has_avg=False,has_bn=True , has_relu=False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     self.out_dim     = planes * self.expansion | ||||
|     self.search_mode = 'basic' | ||||
|  | ||||
|   def get_range(self): | ||||
|     return self.conv_1x1.get_range() + self.conv_3x3.get_range() + self.conv_1x4.get_range() | ||||
|  | ||||
|   def get_flops(self, channels): | ||||
|     assert len(channels) == 4, 'invalid channels : {:}'.format(channels) | ||||
|     flop_A = self.conv_1x1.get_flops([channels[0], channels[1]]) | ||||
|     flop_B = self.conv_3x3.get_flops([channels[1], channels[2]]) | ||||
|     flop_C = self.conv_1x4.get_flops([channels[2], channels[3]]) | ||||
|     if hasattr(self.downsample, 'get_flops'): | ||||
|       flop_D = self.downsample.get_flops([channels[0], channels[-1]]) | ||||
|     else: | ||||
|       flop_D = 0 | ||||
|     if channels[0] != channels[-1] and self.downsample is None: # this short-cut will be added during the infer-train | ||||
|       flop_D = channels[0] * channels[-1] * self.conv_1x4.OutShape[0] * self.conv_1x4.OutShape[1] | ||||
|     return flop_A + flop_B + flop_C + flop_D | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.search_mode == 'basic'   : return self.basic_forward(inputs) | ||||
|     elif self.search_mode == 'search': return self.search_forward(inputs) | ||||
|     else: raise ValueError('invalid search_mode = {:}'.format(self.search_mode)) | ||||
|  | ||||
|   def basic_forward(self, inputs): | ||||
|     bottleneck = self.conv_1x1(inputs) | ||||
|     bottleneck = self.conv_3x3(bottleneck) | ||||
|     bottleneck = self.conv_1x4(bottleneck) | ||||
|     if self.downsample is not None: residual = self.downsample(inputs) | ||||
|     else                          : residual = inputs | ||||
|     out = additive_func(residual, bottleneck) | ||||
|     return nn.functional.relu(out, inplace=True) | ||||
|  | ||||
|   def search_forward(self, tuple_inputs): | ||||
|     assert isinstance(tuple_inputs, tuple) and len(tuple_inputs) == 5, 'invalid type input : {:}'.format( type(tuple_inputs) ) | ||||
|     inputs, expected_inC, probability, indexes, probs = tuple_inputs | ||||
|     assert indexes.size(0) == 3 and probs.size(0) == 3 and probability.size(0) == 3 | ||||
|     out_1x1, expected_inC_1x1, expected_flop_1x1 = self.conv_1x1( (inputs, expected_inC    , probability[0], indexes[0], probs[0]) ) | ||||
|     out_3x3, expected_inC_3x3, expected_flop_3x3 = self.conv_3x3( (out_1x1,expected_inC_1x1, probability[1], indexes[1], probs[1]) ) | ||||
|     out_1x4, expected_inC_1x4, expected_flop_1x4 = self.conv_1x4( (out_3x3,expected_inC_3x3, probability[2], indexes[2], probs[2]) ) | ||||
|     if self.downsample is not None: | ||||
|       residual, _, expected_flop_c = self.downsample( (inputs, expected_inC  , probability[2], indexes[2], probs[2]) ) | ||||
|     else: | ||||
|       residual, expected_flop_c = inputs, 0 | ||||
|     out = additive_func(residual, out_1x4) | ||||
|     return nn.functional.relu(out, inplace=True), expected_inC_1x4, sum([expected_flop_1x1, expected_flop_3x3, expected_flop_1x4, expected_flop_c]) | ||||
|  | ||||
|  | ||||
| class SearchShapeCifarResNet(nn.Module): | ||||
|  | ||||
|   def __init__(self, block_name, depth, num_classes): | ||||
|     super(SearchShapeCifarResNet, self).__init__() | ||||
|  | ||||
|     #Model type specifies number of layers for CIFAR-10 and CIFAR-100 model | ||||
|     if block_name == 'ResNetBasicblock': | ||||
|       block = ResNetBasicblock | ||||
|       assert (depth - 2) % 6 == 0, 'depth should be one of 20, 32, 44, 56, 110' | ||||
|       layer_blocks = (depth - 2) // 6 | ||||
|     elif block_name == 'ResNetBottleneck': | ||||
|       block = ResNetBottleneck | ||||
|       assert (depth - 2) % 9 == 0, 'depth should be one of 164' | ||||
|       layer_blocks = (depth - 2) // 9 | ||||
|     else: | ||||
|       raise ValueError('invalid block : {:}'.format(block_name)) | ||||
|  | ||||
|     self.message      = 'SearchShapeCifarResNet : Depth : {:} , Layers for each block : {:}'.format(depth, layer_blocks) | ||||
|     self.num_classes  = num_classes | ||||
|     self.channels     = [16] | ||||
|     self.layers       = nn.ModuleList( [ ConvBNReLU(3, 16, 3, 1, 1, False, has_avg=False, has_bn=True, has_relu=True) ] ) | ||||
|     self.InShape      = None | ||||
|     self.depth_info   = OrderedDict() | ||||
|     self.depth_at_i   = OrderedDict() | ||||
|     for stage in range(3): | ||||
|       cur_block_choices = get_depth_choices(layer_blocks, False) | ||||
|       assert cur_block_choices[-1] == layer_blocks, 'stage={:}, {:} vs {:}'.format(stage, cur_block_choices, layer_blocks) | ||||
|       self.message += "\nstage={:} ::: depth-block-choices={:} for {:} blocks.".format(stage, cur_block_choices, layer_blocks) | ||||
|       block_choices, xstart = [], len(self.layers) | ||||
|       for iL in range(layer_blocks): | ||||
|         iC     = self.channels[-1] | ||||
|         planes = 16 * (2**stage) | ||||
|         stride = 2 if stage > 0 and iL == 0 else 1 | ||||
|         module = block(iC, planes, stride) | ||||
|         self.channels.append( module.out_dim ) | ||||
|         self.layers.append  ( module ) | ||||
|         self.message += "\nstage={:}, ilayer={:02d}/{:02d}, block={:03d}, iC={:3d}, oC={:3d}, stride={:}".format(stage, iL, layer_blocks, len(self.layers)-1, iC, module.out_dim, stride) | ||||
|         # added for depth | ||||
|         layer_index = len(self.layers) - 1 | ||||
|         if iL + 1 in cur_block_choices: block_choices.append( layer_index ) | ||||
|         if iL + 1 == layer_blocks: | ||||
|           self.depth_info[layer_index] = {'choices': block_choices, | ||||
|                                           'stage'  : stage, | ||||
|                                           'xstart' : xstart} | ||||
|     self.depth_info_list = [] | ||||
|     for xend, info in self.depth_info.items(): | ||||
|       self.depth_info_list.append( (xend, info) ) | ||||
|       xstart, xstage = info['xstart'], info['stage'] | ||||
|       for ilayer in range(xstart, xend+1): | ||||
|         idx = bisect_right(info['choices'], ilayer-1) | ||||
|         self.depth_at_i[ilayer] = (xstage, idx) | ||||
|  | ||||
|     self.avgpool     = nn.AvgPool2d(8) | ||||
|     self.classifier  = nn.Linear(module.out_dim, num_classes) | ||||
|     self.InShape     = None | ||||
|     self.tau         = -1 | ||||
|     self.search_mode = 'basic' | ||||
|     #assert sum(x.num_conv for x in self.layers) + 1 == depth, 'invalid depth check {:} vs {:}'.format(sum(x.num_conv for x in self.layers)+1, depth) | ||||
|      | ||||
|     # parameters for width | ||||
|     self.Ranges = [] | ||||
|     self.layer2indexRange = [] | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       start_index = len(self.Ranges) | ||||
|       self.Ranges += layer.get_range() | ||||
|       self.layer2indexRange.append( (start_index, len(self.Ranges)) ) | ||||
|     assert len(self.Ranges) + 1 == depth, 'invalid depth check {:} vs {:}'.format(len(self.Ranges) + 1, depth) | ||||
|  | ||||
|     self.register_parameter('width_attentions', nn.Parameter(torch.Tensor(len(self.Ranges), get_width_choices(None)))) | ||||
|     self.register_parameter('depth_attentions', nn.Parameter(torch.Tensor(3, get_depth_choices(layer_blocks, True)))) | ||||
|     nn.init.normal_(self.width_attentions, 0, 0.01) | ||||
|     nn.init.normal_(self.depth_attentions, 0, 0.01) | ||||
|     self.apply(initialize_resnet) | ||||
|  | ||||
|   def arch_parameters(self, LR=None): | ||||
|     if LR is None: | ||||
|       return [self.width_attentions, self.depth_attentions] | ||||
|     else: | ||||
|       return [ | ||||
|                {"params": self.width_attentions, "lr": LR}, | ||||
|                {"params": self.depth_attentions, "lr": LR}, | ||||
|              ] | ||||
|  | ||||
|   def base_parameters(self): | ||||
|     return list(self.layers.parameters()) + list(self.avgpool.parameters()) + list(self.classifier.parameters()) | ||||
|  | ||||
|   def get_flop(self, mode, config_dict, extra_info): | ||||
|     if config_dict is not None: config_dict = config_dict.copy() | ||||
|     # select channels  | ||||
|     channels = [3] | ||||
|     for i, weight in enumerate(self.width_attentions): | ||||
|       if mode == 'genotype': | ||||
|         with torch.no_grad(): | ||||
|           probe = nn.functional.softmax(weight, dim=0) | ||||
|           C = self.Ranges[i][ torch.argmax(probe).item() ] | ||||
|       elif mode == 'max': | ||||
|         C = self.Ranges[i][-1] | ||||
|       elif mode == 'fix': | ||||
|         C = int( math.sqrt( extra_info ) * self.Ranges[i][-1] ) | ||||
|       elif mode == 'random': | ||||
|         assert isinstance(extra_info, float), 'invalid extra_info : {:}'.format(extra_info) | ||||
|         with torch.no_grad(): | ||||
|           prob = nn.functional.softmax(weight, dim=0) | ||||
|           approximate_C = int( math.sqrt( extra_info ) * self.Ranges[i][-1] ) | ||||
|           for j in range(prob.size(0)): | ||||
|             prob[j] = 1 / (abs(j - (approximate_C-self.Ranges[i][j])) + 0.2) | ||||
|           C = self.Ranges[i][ torch.multinomial(prob, 1, False).item() ] | ||||
|       else: | ||||
|         raise ValueError('invalid mode : {:}'.format(mode)) | ||||
|       channels.append( C ) | ||||
|     # select depth | ||||
|     if mode == 'genotype': | ||||
|       with torch.no_grad(): | ||||
|         depth_probs = nn.functional.softmax(self.depth_attentions, dim=1) | ||||
|         choices = torch.argmax(depth_probs, dim=1).cpu().tolist() | ||||
|     elif mode == 'max' or mode == 'fix': | ||||
|       choices = [depth_probs.size(1)-1 for _ in range(depth_probs.size(0))] | ||||
|     elif mode == 'random': | ||||
|       with torch.no_grad(): | ||||
|         depth_probs = nn.functional.softmax(self.depth_attentions, dim=1) | ||||
|         choices = torch.multinomial(depth_probs, 1, False).cpu().tolist() | ||||
|     else: | ||||
|       raise ValueError('invalid mode : {:}'.format(mode)) | ||||
|     selected_layers = [] | ||||
|     for choice, xvalue in zip(choices, self.depth_info_list): | ||||
|       xtemp = xvalue[1]['choices'][choice] - xvalue[1]['xstart'] + 1 | ||||
|       selected_layers.append(xtemp) | ||||
|     flop = 0 | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       s, e = self.layer2indexRange[i] | ||||
|       xchl = tuple( channels[s:e+1] ) | ||||
|       if i in self.depth_at_i: | ||||
|         xstagei, xatti = self.depth_at_i[i] | ||||
|         if xatti <= choices[xstagei]: # leave this depth | ||||
|           flop+= layer.get_flops(xchl) | ||||
|         else: | ||||
|           flop+= 0 # do not use this layer | ||||
|       else: | ||||
|         flop+= layer.get_flops(xchl) | ||||
|     # the last fc layer | ||||
|     flop += channels[-1] * self.classifier.out_features | ||||
|     if config_dict is None: | ||||
|       return flop / 1e6 | ||||
|     else: | ||||
|       config_dict['xchannels']  = channels | ||||
|       config_dict['xblocks']    = selected_layers | ||||
|       config_dict['super_type'] = 'infer-shape' | ||||
|       config_dict['estimated_FLOP'] = flop / 1e6 | ||||
|       return flop / 1e6, config_dict | ||||
|  | ||||
|   def get_arch_info(self): | ||||
|     string = "for depth and width, there are {:} + {:} attention probabilities.".format(len(self.depth_attentions), len(self.width_attentions)) | ||||
|     string+= '\n{:}'.format(self.depth_info) | ||||
|     discrepancy = [] | ||||
|     with torch.no_grad(): | ||||
|       for i, att in enumerate(self.depth_attentions): | ||||
|         prob = nn.functional.softmax(att, dim=0) | ||||
|         prob = prob.cpu() ; selc = prob.argmax().item() ; prob = prob.tolist() | ||||
|         prob = ['{:.3f}'.format(x) for x in prob] | ||||
|         xstring = '{:03d}/{:03d}-th : {:}'.format(i, len(self.depth_attentions), ' '.join(prob)) | ||||
|         logt = ['{:.4f}'.format(x) for x in att.cpu().tolist()] | ||||
|         xstring += '  ||  {:17s}'.format(' '.join(logt)) | ||||
|         prob = sorted( [float(x) for x in prob] ) | ||||
|         disc = prob[-1] - prob[-2] | ||||
|         xstring += '  || discrepancy={:.2f} || select={:}/{:}'.format(disc, selc, len(prob)) | ||||
|         discrepancy.append( disc ) | ||||
|         string += '\n{:}'.format(xstring) | ||||
|       string += '\n-----------------------------------------------' | ||||
|       for i, att in enumerate(self.width_attentions): | ||||
|         prob = nn.functional.softmax(att, dim=0) | ||||
|         prob = prob.cpu() ; selc = prob.argmax().item() ; prob = prob.tolist() | ||||
|         prob = ['{:.3f}'.format(x) for x in prob] | ||||
|         xstring = '{:03d}/{:03d}-th : {:}'.format(i, len(self.width_attentions), ' '.join(prob)) | ||||
|         logt = ['{:.3f}'.format(x) for x in att.cpu().tolist()] | ||||
|         xstring += '  ||  {:52s}'.format(' '.join(logt)) | ||||
|         prob = sorted( [float(x) for x in prob] ) | ||||
|         disc = prob[-1] - prob[-2] | ||||
|         xstring += '  || dis={:.2f} || select={:}/{:}'.format(disc, selc, len(prob)) | ||||
|         discrepancy.append( disc ) | ||||
|         string += '\n{:}'.format(xstring) | ||||
|     return string, discrepancy | ||||
|  | ||||
|   def set_tau(self, tau_max, tau_min, epoch_ratio): | ||||
|     assert epoch_ratio >= 0 and epoch_ratio <= 1, 'invalid epoch-ratio : {:}'.format(epoch_ratio) | ||||
|     tau = tau_min + (tau_max-tau_min) * (1 + math.cos(math.pi * epoch_ratio)) / 2 | ||||
|     self.tau = tau | ||||
|  | ||||
|   def get_message(self): | ||||
|     return self.message | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.search_mode == 'basic': | ||||
|       return self.basic_forward(inputs) | ||||
|     elif self.search_mode == 'search': | ||||
|       return self.search_forward(inputs) | ||||
|     else: | ||||
|       raise ValueError('invalid search_mode = {:}'.format(self.search_mode)) | ||||
|  | ||||
|   def search_forward(self, inputs): | ||||
|     flop_width_probs = nn.functional.softmax(self.width_attentions, dim=1) | ||||
|     flop_depth_probs = nn.functional.softmax(self.depth_attentions, dim=1) | ||||
|     flop_depth_probs = torch.flip( torch.cumsum( torch.flip(flop_depth_probs, [1]), 1 ), [1] ) | ||||
|     selected_widths, selected_width_probs = select2withP(self.width_attentions, self.tau) | ||||
|     selected_depth_probs = select2withP(self.depth_attentions, self.tau, True) | ||||
|     with torch.no_grad(): | ||||
|       selected_widths = selected_widths.cpu() | ||||
|  | ||||
|     x, last_channel_idx, expected_inC, flops = inputs, 0, 3, [] | ||||
|     feature_maps = [] | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       selected_w_index = selected_widths     [last_channel_idx: last_channel_idx+layer.num_conv] | ||||
|       selected_w_probs = selected_width_probs[last_channel_idx: last_channel_idx+layer.num_conv] | ||||
|       layer_prob       = flop_width_probs    [last_channel_idx: last_channel_idx+layer.num_conv] | ||||
|       x, expected_inC, expected_flop = layer( (x, expected_inC, layer_prob, selected_w_index, selected_w_probs) ) | ||||
|       feature_maps.append( x ) | ||||
|       last_channel_idx += layer.num_conv | ||||
|       if i in self.depth_info: # aggregate the information | ||||
|         choices = self.depth_info[i]['choices'] | ||||
|         xstagei = self.depth_info[i]['stage'] | ||||
|         #print ('iL={:}, choices={:}, stage={:}, probs={:}'.format(i, choices, xstagei, selected_depth_probs[xstagei].cpu().tolist())) | ||||
|         #for A, W in zip(choices, selected_depth_probs[xstagei]): | ||||
|         #  print('Size = {:}, W = {:}'.format(feature_maps[A].size(), W)) | ||||
|         possible_tensors = [] | ||||
|         max_C = max( feature_maps[A].size(1) for A in choices ) | ||||
|         for tempi, A in enumerate(choices): | ||||
|           xtensor = ChannelWiseInter(feature_maps[A], max_C) | ||||
|           #drop_ratio = 1-(tempi+1.0)/len(choices) | ||||
|           #xtensor = drop_path(xtensor, drop_ratio) | ||||
|           possible_tensors.append( xtensor ) | ||||
|         weighted_sum = sum( xtensor * W for xtensor, W in zip(possible_tensors, selected_depth_probs[xstagei]) ) | ||||
|         x = weighted_sum | ||||
|          | ||||
|       if i in self.depth_at_i: | ||||
|         xstagei, xatti = self.depth_at_i[i] | ||||
|         x_expected_flop = flop_depth_probs[xstagei, xatti] * expected_flop | ||||
|       else: | ||||
|         x_expected_flop = expected_flop | ||||
|       flops.append( x_expected_flop ) | ||||
|     flops.append( expected_inC * (self.classifier.out_features*1.0/1e6) ) | ||||
|     features = self.avgpool(x) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     logits   = linear_forward(features, self.classifier) | ||||
|     return logits, torch.stack( [sum(flops)] ) | ||||
|  | ||||
|   def basic_forward(self, inputs): | ||||
|     if self.InShape is None: self.InShape = (inputs.size(-2), inputs.size(-1)) | ||||
|     x = inputs | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       x = layer( x ) | ||||
|     features = self.avgpool(x) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     logits   = self.classifier(features) | ||||
|     return features, logits | ||||
							
								
								
									
										340
									
								
								graph_dit/naswot/models/shape_searchs/SearchCifarResNet_depth.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										340
									
								
								graph_dit/naswot/models/shape_searchs/SearchCifarResNet_depth.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,340 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| import math, torch | ||||
| from collections import OrderedDict | ||||
| from bisect import bisect_right | ||||
| import torch.nn as nn | ||||
| from ..initialization import initialize_resnet | ||||
| from ..SharedUtils    import additive_func | ||||
| from .SoftSelect      import select2withP, ChannelWiseInter | ||||
| from .SoftSelect      import linear_forward | ||||
| from .SoftSelect      import get_width_choices | ||||
|  | ||||
|  | ||||
| def get_depth_choices(nDepth, return_num): | ||||
|   if nDepth == 2: | ||||
|     choices = (1, 2) | ||||
|   elif nDepth == 3: | ||||
|     choices = (1, 2, 3) | ||||
|   elif nDepth > 3: | ||||
|     choices = list(range(1, nDepth+1, 2)) | ||||
|     if choices[-1] < nDepth: choices.append(nDepth) | ||||
|   else: | ||||
|     raise ValueError('invalid nDepth : {:}'.format(nDepth)) | ||||
|   if return_num: return len(choices) | ||||
|   else         : return choices | ||||
|  | ||||
|  | ||||
|  | ||||
| class ConvBNReLU(nn.Module): | ||||
|   num_conv  = 1 | ||||
|   def __init__(self, nIn, nOut, kernel, stride, padding, bias, has_avg, has_bn, has_relu): | ||||
|     super(ConvBNReLU, self).__init__() | ||||
|     self.InShape  = None | ||||
|     self.OutShape = None | ||||
|     self.choices  = get_width_choices(nOut) | ||||
|     self.register_buffer('choices_tensor', torch.Tensor( self.choices )) | ||||
|  | ||||
|     if has_avg : self.avg = nn.AvgPool2d(kernel_size=2, stride=2, padding=0) | ||||
|     else       : self.avg = None | ||||
|     self.conv = nn.Conv2d(nIn, nOut, kernel_size=kernel, stride=stride, padding=padding, dilation=1, groups=1, bias=bias) | ||||
|     if has_bn  : self.bn  = nn.BatchNorm2d(nOut) | ||||
|     else       : self.bn  = None | ||||
|     if has_relu: self.relu = nn.ReLU(inplace=False) | ||||
|     else       : self.relu = None | ||||
|     self.in_dim   = nIn | ||||
|     self.out_dim  = nOut | ||||
|  | ||||
|   def get_flops(self, divide=1): | ||||
|     iC, oC = self.in_dim, self.out_dim | ||||
|     assert iC <= self.conv.in_channels and oC <= self.conv.out_channels, '{:} vs {:}  |  {:} vs {:}'.format(iC, self.conv.in_channels, oC, self.conv.out_channels) | ||||
|     assert isinstance(self.InShape, tuple) and len(self.InShape) == 2, 'invalid in-shape : {:}'.format(self.InShape) | ||||
|     assert isinstance(self.OutShape, tuple) and len(self.OutShape) == 2, 'invalid out-shape : {:}'.format(self.OutShape) | ||||
|     #conv_per_position_flops = self.conv.kernel_size[0] * self.conv.kernel_size[1] * iC * oC / self.conv.groups | ||||
|     conv_per_position_flops = (self.conv.kernel_size[0] * self.conv.kernel_size[1] * 1.0 / self.conv.groups) | ||||
|     all_positions = self.OutShape[0] * self.OutShape[1] | ||||
|     flops = (conv_per_position_flops * all_positions / divide) * iC * oC | ||||
|     if self.conv.bias is not None: flops += all_positions / divide | ||||
|     return flops | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.avg : out = self.avg( inputs ) | ||||
|     else        : out = inputs | ||||
|     conv = self.conv( out ) | ||||
|     if self.bn  : out = self.bn( conv ) | ||||
|     else        : out = conv | ||||
|     if self.relu: out = self.relu( out ) | ||||
|     else        : out = out | ||||
|     if self.InShape is None: | ||||
|       self.InShape  = (inputs.size(-2), inputs.size(-1)) | ||||
|       self.OutShape = (out.size(-2)   , out.size(-1)) | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class ResNetBasicblock(nn.Module): | ||||
|   expansion = 1 | ||||
|   num_conv  = 2 | ||||
|   def __init__(self, inplanes, planes, stride): | ||||
|     super(ResNetBasicblock, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     self.conv_a = ConvBNReLU(inplanes, planes, 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_b = ConvBNReLU(  planes, planes, 3,      1, 1, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes, 1, 1, 0, False, has_avg=True, has_bn=False, has_relu=False) | ||||
|     elif inplanes != planes: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes, 1, 1, 0, False, has_avg=False,has_bn=True , has_relu=False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     self.out_dim     = planes | ||||
|     self.search_mode = 'basic' | ||||
|  | ||||
|   def get_flops(self, divide=1): | ||||
|     flop_A = self.conv_a.get_flops(divide) | ||||
|     flop_B = self.conv_b.get_flops(divide) | ||||
|     if hasattr(self.downsample, 'get_flops'): | ||||
|       flop_C = self.downsample.get_flops(divide) | ||||
|     else: | ||||
|       flop_C = 0 | ||||
|     return flop_A + flop_B + flop_C | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     basicblock = self.conv_a(inputs) | ||||
|     basicblock = self.conv_b(basicblock) | ||||
|     if self.downsample is not None: residual = self.downsample(inputs) | ||||
|     else                          : residual = inputs | ||||
|     out = additive_func(residual, basicblock) | ||||
|     return nn.functional.relu(out, inplace=True) | ||||
|  | ||||
|  | ||||
|  | ||||
| class ResNetBottleneck(nn.Module): | ||||
|   expansion = 4 | ||||
|   num_conv  = 3 | ||||
|   def __init__(self, inplanes, planes, stride): | ||||
|     super(ResNetBottleneck, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     self.conv_1x1 = ConvBNReLU(inplanes, planes, 1,      1, 0, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_3x3 = ConvBNReLU(  planes, planes, 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_1x4 = ConvBNReLU(planes, planes*self.expansion, 1, 1, 0, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes*self.expansion, 1, 1, 0, False, has_avg=True, has_bn=False, has_relu=False) | ||||
|     elif inplanes != planes*self.expansion: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes*self.expansion, 1, 1, 0, False, has_avg=False,has_bn=True , has_relu=False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     self.out_dim     = planes * self.expansion | ||||
|     self.search_mode = 'basic' | ||||
|  | ||||
|   def get_range(self): | ||||
|     return self.conv_1x1.get_range() + self.conv_3x3.get_range() + self.conv_1x4.get_range() | ||||
|  | ||||
|   def get_flops(self, divide): | ||||
|     flop_A = self.conv_1x1.get_flops(divide) | ||||
|     flop_B = self.conv_3x3.get_flops(divide) | ||||
|     flop_C = self.conv_1x4.get_flops(divide) | ||||
|     if hasattr(self.downsample, 'get_flops'): | ||||
|       flop_D = self.downsample.get_flops(divide) | ||||
|     else: | ||||
|       flop_D = 0 | ||||
|     return flop_A + flop_B + flop_C + flop_D | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     bottleneck = self.conv_1x1(inputs) | ||||
|     bottleneck = self.conv_3x3(bottleneck) | ||||
|     bottleneck = self.conv_1x4(bottleneck) | ||||
|     if self.downsample is not None: residual = self.downsample(inputs) | ||||
|     else                          : residual = inputs | ||||
|     out = additive_func(residual, bottleneck) | ||||
|     return nn.functional.relu(out, inplace=True) | ||||
|  | ||||
|  | ||||
| class SearchDepthCifarResNet(nn.Module): | ||||
|  | ||||
|   def __init__(self, block_name, depth, num_classes): | ||||
|     super(SearchDepthCifarResNet, self).__init__() | ||||
|  | ||||
|     #Model type specifies number of layers for CIFAR-10 and CIFAR-100 model | ||||
|     if block_name == 'ResNetBasicblock': | ||||
|       block = ResNetBasicblock | ||||
|       assert (depth - 2) % 6 == 0, 'depth should be one of 20, 32, 44, 56, 110' | ||||
|       layer_blocks = (depth - 2) // 6 | ||||
|     elif block_name == 'ResNetBottleneck': | ||||
|       block = ResNetBottleneck | ||||
|       assert (depth - 2) % 9 == 0, 'depth should be one of 164' | ||||
|       layer_blocks = (depth - 2) // 9 | ||||
|     else: | ||||
|       raise ValueError('invalid block : {:}'.format(block_name)) | ||||
|  | ||||
|     self.message      = 'SearchShapeCifarResNet : Depth : {:} , Layers for each block : {:}'.format(depth, layer_blocks) | ||||
|     self.num_classes  = num_classes | ||||
|     self.channels     = [16] | ||||
|     self.layers       = nn.ModuleList( [ ConvBNReLU(3, 16, 3, 1, 1, False, has_avg=False, has_bn=True, has_relu=True) ] ) | ||||
|     self.InShape      = None | ||||
|     self.depth_info   = OrderedDict() | ||||
|     self.depth_at_i   = OrderedDict() | ||||
|     for stage in range(3): | ||||
|       cur_block_choices = get_depth_choices(layer_blocks, False) | ||||
|       assert cur_block_choices[-1] == layer_blocks, 'stage={:}, {:} vs {:}'.format(stage, cur_block_choices, layer_blocks) | ||||
|       self.message += "\nstage={:} ::: depth-block-choices={:} for {:} blocks.".format(stage, cur_block_choices, layer_blocks) | ||||
|       block_choices, xstart = [], len(self.layers) | ||||
|       for iL in range(layer_blocks): | ||||
|         iC     = self.channels[-1] | ||||
|         planes = 16 * (2**stage) | ||||
|         stride = 2 if stage > 0 and iL == 0 else 1 | ||||
|         module = block(iC, planes, stride) | ||||
|         self.channels.append( module.out_dim ) | ||||
|         self.layers.append  ( module ) | ||||
|         self.message += "\nstage={:}, ilayer={:02d}/{:02d}, block={:03d}, iC={:3d}, oC={:3d}, stride={:}".format(stage, iL, layer_blocks, len(self.layers)-1, iC, module.out_dim, stride) | ||||
|         # added for depth | ||||
|         layer_index = len(self.layers) - 1 | ||||
|         if iL + 1 in cur_block_choices: block_choices.append( layer_index ) | ||||
|         if iL + 1 == layer_blocks: | ||||
|           self.depth_info[layer_index] = {'choices': block_choices, | ||||
|                                           'stage'  : stage, | ||||
|                                           'xstart' : xstart} | ||||
|     self.depth_info_list = [] | ||||
|     for xend, info in self.depth_info.items(): | ||||
|       self.depth_info_list.append( (xend, info) ) | ||||
|       xstart, xstage = info['xstart'], info['stage'] | ||||
|       for ilayer in range(xstart, xend+1): | ||||
|         idx = bisect_right(info['choices'], ilayer-1) | ||||
|         self.depth_at_i[ilayer] = (xstage, idx) | ||||
|  | ||||
|     self.avgpool     = nn.AvgPool2d(8) | ||||
|     self.classifier  = nn.Linear(module.out_dim, num_classes) | ||||
|     self.InShape     = None | ||||
|     self.tau         = -1 | ||||
|     self.search_mode = 'basic' | ||||
|     #assert sum(x.num_conv for x in self.layers) + 1 == depth, 'invalid depth check {:} vs {:}'.format(sum(x.num_conv for x in self.layers)+1, depth) | ||||
|      | ||||
|  | ||||
|     self.register_parameter('depth_attentions', nn.Parameter(torch.Tensor(3, get_depth_choices(layer_blocks, True)))) | ||||
|     nn.init.normal_(self.depth_attentions, 0, 0.01) | ||||
|     self.apply(initialize_resnet) | ||||
|  | ||||
|   def arch_parameters(self): | ||||
|     return [self.depth_attentions] | ||||
|  | ||||
|   def base_parameters(self): | ||||
|     return list(self.layers.parameters()) + list(self.avgpool.parameters()) + list(self.classifier.parameters()) | ||||
|  | ||||
|   def get_flop(self, mode, config_dict, extra_info): | ||||
|     if config_dict is not None: config_dict = config_dict.copy() | ||||
|     # select depth | ||||
|     if mode == 'genotype': | ||||
|       with torch.no_grad(): | ||||
|         depth_probs = nn.functional.softmax(self.depth_attentions, dim=1) | ||||
|         choices = torch.argmax(depth_probs, dim=1).cpu().tolist() | ||||
|     elif mode == 'max': | ||||
|       choices = [depth_probs.size(1)-1 for _ in range(depth_probs.size(0))] | ||||
|     elif mode == 'random': | ||||
|       with torch.no_grad(): | ||||
|         depth_probs = nn.functional.softmax(self.depth_attentions, dim=1) | ||||
|         choices = torch.multinomial(depth_probs, 1, False).cpu().tolist() | ||||
|     else: | ||||
|       raise ValueError('invalid mode : {:}'.format(mode)) | ||||
|     selected_layers = [] | ||||
|     for choice, xvalue in zip(choices, self.depth_info_list): | ||||
|       xtemp = xvalue[1]['choices'][choice] - xvalue[1]['xstart'] + 1 | ||||
|       selected_layers.append(xtemp) | ||||
|     flop = 0 | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       if i in self.depth_at_i: | ||||
|         xstagei, xatti = self.depth_at_i[i] | ||||
|         if xatti <= choices[xstagei]: # leave this depth | ||||
|           flop+= layer.get_flops() | ||||
|         else: | ||||
|           flop+= 0 # do not use this layer | ||||
|       else: | ||||
|         flop+= layer.get_flops() | ||||
|     # the last fc layer | ||||
|     flop += self.classifier.in_features * self.classifier.out_features | ||||
|     if config_dict is None: | ||||
|       return flop / 1e6 | ||||
|     else: | ||||
|       config_dict['xblocks']    = selected_layers | ||||
|       config_dict['super_type'] = 'infer-depth' | ||||
|       config_dict['estimated_FLOP'] = flop / 1e6 | ||||
|       return flop / 1e6, config_dict | ||||
|  | ||||
|   def get_arch_info(self): | ||||
|     string = "for depth, there are {:} attention probabilities.".format(len(self.depth_attentions)) | ||||
|     string+= '\n{:}'.format(self.depth_info) | ||||
|     discrepancy = [] | ||||
|     with torch.no_grad(): | ||||
|       for i, att in enumerate(self.depth_attentions): | ||||
|         prob = nn.functional.softmax(att, dim=0) | ||||
|         prob = prob.cpu() ; selc = prob.argmax().item() ; prob = prob.tolist() | ||||
|         prob = ['{:.3f}'.format(x) for x in prob] | ||||
|         xstring = '{:03d}/{:03d}-th : {:}'.format(i, len(self.depth_attentions), ' '.join(prob)) | ||||
|         logt = ['{:.4f}'.format(x) for x in att.cpu().tolist()] | ||||
|         xstring += '  ||  {:17s}'.format(' '.join(logt)) | ||||
|         prob = sorted( [float(x) for x in prob] ) | ||||
|         disc = prob[-1] - prob[-2] | ||||
|         xstring += '  || discrepancy={:.2f} || select={:}/{:}'.format(disc, selc, len(prob)) | ||||
|         discrepancy.append( disc ) | ||||
|         string += '\n{:}'.format(xstring) | ||||
|     return string, discrepancy | ||||
|  | ||||
|   def set_tau(self, tau_max, tau_min, epoch_ratio): | ||||
|     assert epoch_ratio >= 0 and epoch_ratio <= 1, 'invalid epoch-ratio : {:}'.format(epoch_ratio) | ||||
|     tau = tau_min + (tau_max-tau_min) * (1 + math.cos(math.pi * epoch_ratio)) / 2 | ||||
|     self.tau = tau | ||||
|  | ||||
|   def get_message(self): | ||||
|     return self.message | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.search_mode == 'basic': | ||||
|       return self.basic_forward(inputs) | ||||
|     elif self.search_mode == 'search': | ||||
|       return self.search_forward(inputs) | ||||
|     else: | ||||
|       raise ValueError('invalid search_mode = {:}'.format(self.search_mode)) | ||||
|  | ||||
|   def search_forward(self, inputs): | ||||
|     flop_depth_probs = nn.functional.softmax(self.depth_attentions, dim=1) | ||||
|     flop_depth_probs = torch.flip( torch.cumsum( torch.flip(flop_depth_probs, [1]), 1 ), [1] ) | ||||
|     selected_depth_probs = select2withP(self.depth_attentions, self.tau, True) | ||||
|  | ||||
|     x, flops = inputs, [] | ||||
|     feature_maps = [] | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       layer_i = layer( x ) | ||||
|       feature_maps.append( layer_i ) | ||||
|       if i in self.depth_info: # aggregate the information | ||||
|         choices = self.depth_info[i]['choices'] | ||||
|         xstagei = self.depth_info[i]['stage'] | ||||
|         possible_tensors = [] | ||||
|         for tempi, A in enumerate(choices): | ||||
|           xtensor = feature_maps[A] | ||||
|           possible_tensors.append( xtensor ) | ||||
|         weighted_sum = sum( xtensor * W for xtensor, W in zip(possible_tensors, selected_depth_probs[xstagei]) ) | ||||
|         x = weighted_sum | ||||
|       else: | ||||
|         x = layer_i | ||||
|         | ||||
|       if i in self.depth_at_i: | ||||
|         xstagei, xatti = self.depth_at_i[i] | ||||
|         #print ('layer-{:03d}, stage={:}, att={:}, prob={:}, flop={:}'.format(i, xstagei, xatti, flop_depth_probs[xstagei, xatti].item(), layer.get_flops(1e6))) | ||||
|         x_expected_flop = flop_depth_probs[xstagei, xatti] * layer.get_flops(1e6) | ||||
|       else: | ||||
|         x_expected_flop = layer.get_flops(1e6) | ||||
|       flops.append( x_expected_flop ) | ||||
|     flops.append( (self.classifier.in_features * self.classifier.out_features*1.0/1e6) ) | ||||
|  | ||||
|     features = self.avgpool(x) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     logits   = linear_forward(features, self.classifier) | ||||
|     return logits, torch.stack( [sum(flops)] ) | ||||
|  | ||||
|   def basic_forward(self, inputs): | ||||
|     if self.InShape is None: self.InShape = (inputs.size(-2), inputs.size(-1)) | ||||
|     x = inputs | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       x = layer( x ) | ||||
|     features = self.avgpool(x) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     logits   = self.classifier(features) | ||||
|     return features, logits | ||||
							
								
								
									
										393
									
								
								graph_dit/naswot/models/shape_searchs/SearchCifarResNet_width.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										393
									
								
								graph_dit/naswot/models/shape_searchs/SearchCifarResNet_width.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,393 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| import math, torch | ||||
| import torch.nn as nn | ||||
| from ..initialization import initialize_resnet | ||||
| from ..SharedUtils    import additive_func | ||||
| from .SoftSelect      import select2withP, ChannelWiseInter | ||||
| from .SoftSelect      import linear_forward | ||||
| from .SoftSelect      import get_width_choices as get_choices | ||||
|  | ||||
|  | ||||
| def conv_forward(inputs, conv, choices): | ||||
|   iC = conv.in_channels | ||||
|   fill_size = list(inputs.size()) | ||||
|   fill_size[1] = iC - fill_size[1] | ||||
|   filled  = torch.zeros(fill_size, device=inputs.device) | ||||
|   xinputs = torch.cat((inputs, filled), dim=1) | ||||
|   outputs = conv(xinputs) | ||||
|   selecteds = [outputs[:,:oC] for oC in choices] | ||||
|   return selecteds | ||||
|  | ||||
|  | ||||
| class ConvBNReLU(nn.Module): | ||||
|   num_conv  = 1 | ||||
|   def __init__(self, nIn, nOut, kernel, stride, padding, bias, has_avg, has_bn, has_relu): | ||||
|     super(ConvBNReLU, self).__init__() | ||||
|     self.InShape  = None | ||||
|     self.OutShape = None | ||||
|     self.choices  = get_choices(nOut) | ||||
|     self.register_buffer('choices_tensor', torch.Tensor( self.choices )) | ||||
|  | ||||
|     if has_avg : self.avg = nn.AvgPool2d(kernel_size=2, stride=2, padding=0) | ||||
|     else       : self.avg = None | ||||
|     self.conv = nn.Conv2d(nIn, nOut, kernel_size=kernel, stride=stride, padding=padding, dilation=1, groups=1, bias=bias) | ||||
|     #if has_bn  : self.bn  = nn.BatchNorm2d(nOut) | ||||
|     #else       : self.bn  = None | ||||
|     self.has_bn = has_bn | ||||
|     self.BNs  = nn.ModuleList() | ||||
|     for i, _out in enumerate(self.choices): | ||||
|       self.BNs.append(nn.BatchNorm2d(_out)) | ||||
|     if has_relu: self.relu = nn.ReLU(inplace=True) | ||||
|     else       : self.relu = None | ||||
|     self.in_dim   = nIn | ||||
|     self.out_dim  = nOut | ||||
|     self.search_mode = 'basic' | ||||
|  | ||||
|   def get_flops(self, channels, check_range=True, divide=1): | ||||
|     iC, oC = channels | ||||
|     if check_range: assert iC <= self.conv.in_channels and oC <= self.conv.out_channels, '{:} vs {:}  |  {:} vs {:}'.format(iC, self.conv.in_channels, oC, self.conv.out_channels) | ||||
|     assert isinstance(self.InShape, tuple) and len(self.InShape) == 2, 'invalid in-shape : {:}'.format(self.InShape) | ||||
|     assert isinstance(self.OutShape, tuple) and len(self.OutShape) == 2, 'invalid out-shape : {:}'.format(self.OutShape) | ||||
|     #conv_per_position_flops = self.conv.kernel_size[0] * self.conv.kernel_size[1] * iC * oC / self.conv.groups | ||||
|     conv_per_position_flops = (self.conv.kernel_size[0] * self.conv.kernel_size[1] * 1.0 / self.conv.groups) | ||||
|     all_positions = self.OutShape[0] * self.OutShape[1] | ||||
|     flops = (conv_per_position_flops * all_positions / divide) * iC * oC | ||||
|     if self.conv.bias is not None: flops += all_positions / divide | ||||
|     return flops | ||||
|  | ||||
|   def get_range(self): | ||||
|     return [self.choices] | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.search_mode == 'basic': | ||||
|       return self.basic_forward(inputs) | ||||
|     elif self.search_mode == 'search': | ||||
|       return self.search_forward(inputs) | ||||
|     else: | ||||
|       raise ValueError('invalid search_mode = {:}'.format(self.search_mode)) | ||||
|  | ||||
|   def search_forward(self, tuple_inputs): | ||||
|     assert isinstance(tuple_inputs, tuple) and len(tuple_inputs) == 5, 'invalid type input : {:}'.format( type(tuple_inputs) ) | ||||
|     inputs, expected_inC, probability, index, prob = tuple_inputs | ||||
|     index, prob = torch.squeeze(index).tolist(), torch.squeeze(prob) | ||||
|     probability = torch.squeeze(probability) | ||||
|     assert len(index) == 2, 'invalid length : {:}'.format(index) | ||||
|     # compute expected flop | ||||
|     #coordinates   = torch.arange(self.x_range[0], self.x_range[1]+1).type_as(probability) | ||||
|     expected_outC = (self.choices_tensor * probability).sum() | ||||
|     expected_flop = self.get_flops([expected_inC, expected_outC], False, 1e6) | ||||
|     if self.avg : out = self.avg( inputs ) | ||||
|     else        : out = inputs | ||||
|     # convolutional layer | ||||
|     out_convs = conv_forward(out, self.conv, [self.choices[i] for i in index]) | ||||
|     out_bns   = [self.BNs[idx](out_conv) for idx, out_conv in zip(index, out_convs)] | ||||
|     # merge | ||||
|     out_channel = max([x.size(1) for x in out_bns]) | ||||
|     outA = ChannelWiseInter(out_bns[0], out_channel) | ||||
|     outB = ChannelWiseInter(out_bns[1], out_channel) | ||||
|     out  = outA * prob[0] + outB * prob[1] | ||||
|     #out = additive_func(out_bns[0]*prob[0], out_bns[1]*prob[1]) | ||||
|  | ||||
|     if self.relu: out = self.relu( out ) | ||||
|     else        : out = out | ||||
|     return out, expected_outC, expected_flop | ||||
|  | ||||
|   def basic_forward(self, inputs): | ||||
|     if self.avg : out = self.avg( inputs ) | ||||
|     else        : out = inputs | ||||
|     conv = self.conv( out ) | ||||
|     if self.has_bn:out= self.BNs[-1]( conv ) | ||||
|     else        : out = conv | ||||
|     if self.relu: out = self.relu( out ) | ||||
|     else        : out = out | ||||
|     if self.InShape is None: | ||||
|       self.InShape  = (inputs.size(-2), inputs.size(-1)) | ||||
|       self.OutShape = (out.size(-2)   , out.size(-1)) | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class ResNetBasicblock(nn.Module): | ||||
|   expansion = 1 | ||||
|   num_conv  = 2 | ||||
|   def __init__(self, inplanes, planes, stride): | ||||
|     super(ResNetBasicblock, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     self.conv_a = ConvBNReLU(inplanes, planes, 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_b = ConvBNReLU(  planes, planes, 3,      1, 1, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes, 1, 1, 0, False, has_avg=True, has_bn=False, has_relu=False) | ||||
|     elif inplanes != planes: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes, 1, 1, 0, False, has_avg=False,has_bn=True , has_relu=False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     self.out_dim     = planes | ||||
|     self.search_mode = 'basic' | ||||
|  | ||||
|   def get_range(self): | ||||
|     return self.conv_a.get_range() + self.conv_b.get_range() | ||||
|  | ||||
|   def get_flops(self, channels): | ||||
|     assert len(channels) == 3, 'invalid channels : {:}'.format(channels) | ||||
|     flop_A = self.conv_a.get_flops([channels[0], channels[1]]) | ||||
|     flop_B = self.conv_b.get_flops([channels[1], channels[2]]) | ||||
|     if hasattr(self.downsample, 'get_flops'): | ||||
|       flop_C = self.downsample.get_flops([channels[0], channels[-1]]) | ||||
|     else: | ||||
|       flop_C = 0 | ||||
|     if channels[0] != channels[-1] and self.downsample is None: # this short-cut will be added during the infer-train | ||||
|       flop_C = channels[0] * channels[-1] * self.conv_b.OutShape[0] * self.conv_b.OutShape[1] | ||||
|     return flop_A + flop_B + flop_C | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.search_mode == 'basic'   : return self.basic_forward(inputs) | ||||
|     elif self.search_mode == 'search': return self.search_forward(inputs) | ||||
|     else: raise ValueError('invalid search_mode = {:}'.format(self.search_mode)) | ||||
|  | ||||
|   def search_forward(self, tuple_inputs): | ||||
|     assert isinstance(tuple_inputs, tuple) and len(tuple_inputs) == 5, 'invalid type input : {:}'.format( type(tuple_inputs) ) | ||||
|     inputs, expected_inC, probability, indexes, probs = tuple_inputs | ||||
|     assert indexes.size(0) == 2 and probs.size(0) == 2 and probability.size(0) == 2 | ||||
|     out_a, expected_inC_a, expected_flop_a = self.conv_a( (inputs, expected_inC  , probability[0], indexes[0], probs[0]) ) | ||||
|     out_b, expected_inC_b, expected_flop_b = self.conv_b( (out_a , expected_inC_a, probability[1], indexes[1], probs[1]) ) | ||||
|     if self.downsample is not None: | ||||
|       residual, _, expected_flop_c = self.downsample( (inputs, expected_inC  , probability[1], indexes[1], probs[1]) ) | ||||
|     else: | ||||
|       residual, expected_flop_c = inputs, 0 | ||||
|     out = additive_func(residual, out_b) | ||||
|     return nn.functional.relu(out, inplace=True), expected_inC_b, sum([expected_flop_a, expected_flop_b, expected_flop_c]) | ||||
|  | ||||
|   def basic_forward(self, inputs): | ||||
|     basicblock = self.conv_a(inputs) | ||||
|     basicblock = self.conv_b(basicblock) | ||||
|     if self.downsample is not None: residual = self.downsample(inputs) | ||||
|     else                          : residual = inputs | ||||
|     out = additive_func(residual, basicblock) | ||||
|     return nn.functional.relu(out, inplace=True) | ||||
|  | ||||
|  | ||||
|  | ||||
| class ResNetBottleneck(nn.Module): | ||||
|   expansion = 4 | ||||
|   num_conv  = 3 | ||||
|   def __init__(self, inplanes, planes, stride): | ||||
|     super(ResNetBottleneck, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     self.conv_1x1 = ConvBNReLU(inplanes, planes, 1,      1, 0, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_3x3 = ConvBNReLU(  planes, planes, 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_1x4 = ConvBNReLU(planes, planes*self.expansion, 1, 1, 0, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes*self.expansion, 1, 1, 0, False, has_avg=True, has_bn=False, has_relu=False) | ||||
|     elif inplanes != planes*self.expansion: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes*self.expansion, 1, 1, 0, False, has_avg=False,has_bn=True , has_relu=False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     self.out_dim     = planes * self.expansion | ||||
|     self.search_mode = 'basic' | ||||
|  | ||||
|   def get_range(self): | ||||
|     return self.conv_1x1.get_range() + self.conv_3x3.get_range() + self.conv_1x4.get_range() | ||||
|  | ||||
|   def get_flops(self, channels): | ||||
|     assert len(channels) == 4, 'invalid channels : {:}'.format(channels) | ||||
|     flop_A = self.conv_1x1.get_flops([channels[0], channels[1]]) | ||||
|     flop_B = self.conv_3x3.get_flops([channels[1], channels[2]]) | ||||
|     flop_C = self.conv_1x4.get_flops([channels[2], channels[3]]) | ||||
|     if hasattr(self.downsample, 'get_flops'): | ||||
|       flop_D = self.downsample.get_flops([channels[0], channels[-1]]) | ||||
|     else: | ||||
|       flop_D = 0 | ||||
|     if channels[0] != channels[-1] and self.downsample is None: # this short-cut will be added during the infer-train | ||||
|       flop_D = channels[0] * channels[-1] * self.conv_1x4.OutShape[0] * self.conv_1x4.OutShape[1] | ||||
|     return flop_A + flop_B + flop_C + flop_D | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.search_mode == 'basic'   : return self.basic_forward(inputs) | ||||
|     elif self.search_mode == 'search': return self.search_forward(inputs) | ||||
|     else: raise ValueError('invalid search_mode = {:}'.format(self.search_mode)) | ||||
|  | ||||
|   def basic_forward(self, inputs): | ||||
|     bottleneck = self.conv_1x1(inputs) | ||||
|     bottleneck = self.conv_3x3(bottleneck) | ||||
|     bottleneck = self.conv_1x4(bottleneck) | ||||
|     if self.downsample is not None: residual = self.downsample(inputs) | ||||
|     else                          : residual = inputs | ||||
|     out = additive_func(residual, bottleneck) | ||||
|     return nn.functional.relu(out, inplace=True) | ||||
|  | ||||
|   def search_forward(self, tuple_inputs): | ||||
|     assert isinstance(tuple_inputs, tuple) and len(tuple_inputs) == 5, 'invalid type input : {:}'.format( type(tuple_inputs) ) | ||||
|     inputs, expected_inC, probability, indexes, probs = tuple_inputs | ||||
|     assert indexes.size(0) == 3 and probs.size(0) == 3 and probability.size(0) == 3 | ||||
|     out_1x1, expected_inC_1x1, expected_flop_1x1 = self.conv_1x1( (inputs, expected_inC    , probability[0], indexes[0], probs[0]) ) | ||||
|     out_3x3, expected_inC_3x3, expected_flop_3x3 = self.conv_3x3( (out_1x1,expected_inC_1x1, probability[1], indexes[1], probs[1]) ) | ||||
|     out_1x4, expected_inC_1x4, expected_flop_1x4 = self.conv_1x4( (out_3x3,expected_inC_3x3, probability[2], indexes[2], probs[2]) ) | ||||
|     if self.downsample is not None: | ||||
|       residual, _, expected_flop_c = self.downsample( (inputs, expected_inC  , probability[2], indexes[2], probs[2]) ) | ||||
|     else: | ||||
|       residual, expected_flop_c = inputs, 0 | ||||
|     out = additive_func(residual, out_1x4) | ||||
|     return nn.functional.relu(out, inplace=True), expected_inC_1x4, sum([expected_flop_1x1, expected_flop_3x3, expected_flop_1x4, expected_flop_c]) | ||||
|  | ||||
|  | ||||
| class SearchWidthCifarResNet(nn.Module): | ||||
|  | ||||
|   def __init__(self, block_name, depth, num_classes): | ||||
|     super(SearchWidthCifarResNet, self).__init__() | ||||
|  | ||||
|     #Model type specifies number of layers for CIFAR-10 and CIFAR-100 model | ||||
|     if block_name == 'ResNetBasicblock': | ||||
|       block = ResNetBasicblock | ||||
|       assert (depth - 2) % 6 == 0, 'depth should be one of 20, 32, 44, 56, 110' | ||||
|       layer_blocks = (depth - 2) // 6 | ||||
|     elif block_name == 'ResNetBottleneck': | ||||
|       block = ResNetBottleneck | ||||
|       assert (depth - 2) % 9 == 0, 'depth should be one of 164' | ||||
|       layer_blocks = (depth - 2) // 9 | ||||
|     else: | ||||
|       raise ValueError('invalid block : {:}'.format(block_name)) | ||||
|  | ||||
|     self.message     = 'SearchWidthCifarResNet : Depth : {:} , Layers for each block : {:}'.format(depth, layer_blocks) | ||||
|     self.num_classes = num_classes | ||||
|     self.channels    = [16] | ||||
|     self.layers      = nn.ModuleList( [ ConvBNReLU(3, 16, 3, 1, 1, False, has_avg=False, has_bn=True, has_relu=True) ] ) | ||||
|     self.InShape     = None | ||||
|     for stage in range(3): | ||||
|       for iL in range(layer_blocks): | ||||
|         iC     = self.channels[-1] | ||||
|         planes = 16 * (2**stage) | ||||
|         stride = 2 if stage > 0 and iL == 0 else 1 | ||||
|         module = block(iC, planes, stride) | ||||
|         self.channels.append( module.out_dim ) | ||||
|         self.layers.append  ( module ) | ||||
|         self.message += "\nstage={:}, ilayer={:02d}/{:02d}, block={:03d}, iC={:3d}, oC={:3d}, stride={:}".format(stage, iL, layer_blocks, len(self.layers)-1, iC, module.out_dim, stride) | ||||
|    | ||||
|     self.avgpool     = nn.AvgPool2d(8) | ||||
|     self.classifier  = nn.Linear(module.out_dim, num_classes) | ||||
|     self.InShape     = None | ||||
|     self.tau         = -1 | ||||
|     self.search_mode = 'basic' | ||||
|     #assert sum(x.num_conv for x in self.layers) + 1 == depth, 'invalid depth check {:} vs {:}'.format(sum(x.num_conv for x in self.layers)+1, depth) | ||||
|      | ||||
|     # parameters for width | ||||
|     self.Ranges = [] | ||||
|     self.layer2indexRange = [] | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       start_index = len(self.Ranges) | ||||
|       self.Ranges += layer.get_range() | ||||
|       self.layer2indexRange.append( (start_index, len(self.Ranges)) ) | ||||
|     assert len(self.Ranges) + 1 == depth, 'invalid depth check {:} vs {:}'.format(len(self.Ranges) + 1, depth) | ||||
|  | ||||
|     self.register_parameter('width_attentions', nn.Parameter(torch.Tensor(len(self.Ranges), get_choices(None)))) | ||||
|     nn.init.normal_(self.width_attentions, 0, 0.01) | ||||
|     self.apply(initialize_resnet) | ||||
|  | ||||
|   def arch_parameters(self): | ||||
|     return [self.width_attentions] | ||||
|  | ||||
|   def base_parameters(self): | ||||
|     return list(self.layers.parameters()) + list(self.avgpool.parameters()) + list(self.classifier.parameters()) | ||||
|  | ||||
|   def get_flop(self, mode, config_dict, extra_info): | ||||
|     if config_dict is not None: config_dict = config_dict.copy() | ||||
|     #weights = [F.softmax(x, dim=0) for x in self.width_attentions] | ||||
|     channels = [3] | ||||
|     for i, weight in enumerate(self.width_attentions): | ||||
|       if mode == 'genotype': | ||||
|         with torch.no_grad(): | ||||
|           probe = nn.functional.softmax(weight, dim=0) | ||||
|           C = self.Ranges[i][ torch.argmax(probe).item() ] | ||||
|       elif mode == 'max': | ||||
|         C = self.Ranges[i][-1] | ||||
|       elif mode == 'fix': | ||||
|         C = int( math.sqrt( extra_info ) * self.Ranges[i][-1] ) | ||||
|       elif mode == 'random': | ||||
|         assert isinstance(extra_info, float), 'invalid extra_info : {:}'.format(extra_info) | ||||
|         with torch.no_grad(): | ||||
|           prob = nn.functional.softmax(weight, dim=0) | ||||
|           approximate_C = int( math.sqrt( extra_info ) * self.Ranges[i][-1] ) | ||||
|           for j in range(prob.size(0)): | ||||
|             prob[j] = 1 / (abs(j - (approximate_C-self.Ranges[i][j])) + 0.2) | ||||
|           C = self.Ranges[i][ torch.multinomial(prob, 1, False).item() ] | ||||
|       else: | ||||
|         raise ValueError('invalid mode : {:}'.format(mode)) | ||||
|       channels.append( C ) | ||||
|     flop = 0 | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       s, e = self.layer2indexRange[i] | ||||
|       xchl = tuple( channels[s:e+1] ) | ||||
|       flop+= layer.get_flops(xchl) | ||||
|     # the last fc layer | ||||
|     flop += channels[-1] * self.classifier.out_features | ||||
|     if config_dict is None: | ||||
|       return flop / 1e6 | ||||
|     else: | ||||
|       config_dict['xchannels']  = channels | ||||
|       config_dict['super_type'] = 'infer-width' | ||||
|       config_dict['estimated_FLOP'] = flop / 1e6 | ||||
|       return flop / 1e6, config_dict | ||||
|  | ||||
|   def get_arch_info(self): | ||||
|     string = "for width, there are {:} attention probabilities.".format(len(self.width_attentions)) | ||||
|     discrepancy = [] | ||||
|     with torch.no_grad(): | ||||
|       for i, att in enumerate(self.width_attentions): | ||||
|         prob = nn.functional.softmax(att, dim=0) | ||||
|         prob = prob.cpu() ; selc = prob.argmax().item() ; prob = prob.tolist() | ||||
|         prob = ['{:.3f}'.format(x) for x in prob] | ||||
|         xstring = '{:03d}/{:03d}-th : {:}'.format(i, len(self.width_attentions), ' '.join(prob)) | ||||
|         logt = ['{:.3f}'.format(x) for x in att.cpu().tolist()] | ||||
|         xstring += '  ||  {:52s}'.format(' '.join(logt)) | ||||
|         prob = sorted( [float(x) for x in prob] ) | ||||
|         disc = prob[-1] - prob[-2] | ||||
|         xstring += '  || dis={:.2f} || select={:}/{:}'.format(disc, selc, len(prob)) | ||||
|         discrepancy.append( disc ) | ||||
|         string += '\n{:}'.format(xstring) | ||||
|     return string, discrepancy | ||||
|  | ||||
|   def set_tau(self, tau_max, tau_min, epoch_ratio): | ||||
|     assert epoch_ratio >= 0 and epoch_ratio <= 1, 'invalid epoch-ratio : {:}'.format(epoch_ratio) | ||||
|     tau = tau_min + (tau_max-tau_min) * (1 + math.cos(math.pi * epoch_ratio)) / 2 | ||||
|     self.tau = tau | ||||
|  | ||||
|   def get_message(self): | ||||
|     return self.message | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.search_mode == 'basic': | ||||
|       return self.basic_forward(inputs) | ||||
|     elif self.search_mode == 'search': | ||||
|       return self.search_forward(inputs) | ||||
|     else: | ||||
|       raise ValueError('invalid search_mode = {:}'.format(self.search_mode)) | ||||
|  | ||||
|   def search_forward(self, inputs): | ||||
|     flop_probs = nn.functional.softmax(self.width_attentions, dim=1) | ||||
|     selected_widths, selected_probs = select2withP(self.width_attentions, self.tau) | ||||
|     with torch.no_grad(): | ||||
|       selected_widths = selected_widths.cpu() | ||||
|  | ||||
|     x, last_channel_idx, expected_inC, flops = inputs, 0, 3, [] | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       selected_w_index = selected_widths[last_channel_idx: last_channel_idx+layer.num_conv] | ||||
|       selected_w_probs = selected_probs[last_channel_idx: last_channel_idx+layer.num_conv] | ||||
|       layer_prob       = flop_probs[last_channel_idx: last_channel_idx+layer.num_conv] | ||||
|       x, expected_inC, expected_flop = layer( (x, expected_inC, layer_prob, selected_w_index, selected_w_probs) ) | ||||
|       last_channel_idx += layer.num_conv | ||||
|       flops.append( expected_flop ) | ||||
|     flops.append( expected_inC * (self.classifier.out_features*1.0/1e6) ) | ||||
|     features = self.avgpool(x) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     logits   = linear_forward(features, self.classifier) | ||||
|     return logits, torch.stack( [sum(flops)] ) | ||||
|  | ||||
|   def basic_forward(self, inputs): | ||||
|     if self.InShape is None: self.InShape = (inputs.size(-2), inputs.size(-1)) | ||||
|     x = inputs | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       x = layer( x ) | ||||
|     features = self.avgpool(x) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     logits   = self.classifier(features) | ||||
|     return features, logits | ||||
							
								
								
									
										482
									
								
								graph_dit/naswot/models/shape_searchs/SearchImagenetResNet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										482
									
								
								graph_dit/naswot/models/shape_searchs/SearchImagenetResNet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,482 @@ | ||||
| import math, torch | ||||
| from collections import OrderedDict | ||||
| from bisect import bisect_right | ||||
| import torch.nn as nn | ||||
| from ..initialization import initialize_resnet | ||||
| from ..SharedUtils    import additive_func | ||||
| from .SoftSelect      import select2withP, ChannelWiseInter | ||||
| from .SoftSelect      import linear_forward | ||||
| from .SoftSelect      import get_width_choices | ||||
|  | ||||
|  | ||||
| def get_depth_choices(layers): | ||||
|   min_depth = min(layers) | ||||
|   info = {'num': min_depth} | ||||
|   for i, depth in enumerate(layers): | ||||
|     choices = [] | ||||
|     for j in range(1, min_depth+1): | ||||
|       choices.append( int( float(depth)*j/min_depth ) ) | ||||
|     info[i] = choices | ||||
|   return info | ||||
|  | ||||
|  | ||||
| def conv_forward(inputs, conv, choices): | ||||
|   iC = conv.in_channels | ||||
|   fill_size = list(inputs.size()) | ||||
|   fill_size[1] = iC - fill_size[1] | ||||
|   filled  = torch.zeros(fill_size, device=inputs.device) | ||||
|   xinputs = torch.cat((inputs, filled), dim=1) | ||||
|   outputs = conv(xinputs) | ||||
|   selecteds = [outputs[:,:oC] for oC in choices] | ||||
|   return selecteds | ||||
|  | ||||
|  | ||||
| class ConvBNReLU(nn.Module): | ||||
|   num_conv  = 1 | ||||
|   def __init__(self, nIn, nOut, kernel, stride, padding, bias, has_avg, has_bn, has_relu, last_max_pool=False): | ||||
|     super(ConvBNReLU, self).__init__() | ||||
|     self.InShape  = None | ||||
|     self.OutShape = None | ||||
|     self.choices  = get_width_choices(nOut) | ||||
|     self.register_buffer('choices_tensor', torch.Tensor( self.choices )) | ||||
|  | ||||
|     if has_avg : self.avg = nn.AvgPool2d(kernel_size=2, stride=2, padding=0) | ||||
|     else       : self.avg = None | ||||
|     self.conv = nn.Conv2d(nIn, nOut, kernel_size=kernel, stride=stride, padding=padding, dilation=1, groups=1, bias=bias) | ||||
|     #if has_bn  : self.bn  = nn.BatchNorm2d(nOut) | ||||
|     #else       : self.bn  = None | ||||
|     self.has_bn = has_bn | ||||
|     self.BNs  = nn.ModuleList() | ||||
|     for i, _out in enumerate(self.choices): | ||||
|       self.BNs.append(nn.BatchNorm2d(_out)) | ||||
|     if has_relu: self.relu = nn.ReLU(inplace=True) | ||||
|     else       : self.relu = None | ||||
|    | ||||
|     if last_max_pool: self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) | ||||
|     else            : self.maxpool = None | ||||
|     self.in_dim   = nIn | ||||
|     self.out_dim  = nOut | ||||
|     self.search_mode = 'basic' | ||||
|  | ||||
|   def get_flops(self, channels, check_range=True, divide=1): | ||||
|     iC, oC = channels | ||||
|     if check_range: assert iC <= self.conv.in_channels and oC <= self.conv.out_channels, '{:} vs {:}  |  {:} vs {:}'.format(iC, self.conv.in_channels, oC, self.conv.out_channels) | ||||
|     assert isinstance(self.InShape, tuple) and len(self.InShape) == 2, 'invalid in-shape : {:}'.format(self.InShape) | ||||
|     assert isinstance(self.OutShape, tuple) and len(self.OutShape) == 2, 'invalid out-shape : {:}'.format(self.OutShape) | ||||
|     #conv_per_position_flops = self.conv.kernel_size[0] * self.conv.kernel_size[1] * iC * oC / self.conv.groups | ||||
|     conv_per_position_flops = (self.conv.kernel_size[0] * self.conv.kernel_size[1] * 1.0 / self.conv.groups) | ||||
|     all_positions = self.OutShape[0] * self.OutShape[1] | ||||
|     flops = (conv_per_position_flops * all_positions / divide) * iC * oC | ||||
|     if self.conv.bias is not None: flops += all_positions / divide | ||||
|     return flops | ||||
|  | ||||
|   def get_range(self): | ||||
|     return [self.choices] | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.search_mode == 'basic': | ||||
|       return self.basic_forward(inputs) | ||||
|     elif self.search_mode == 'search': | ||||
|       return self.search_forward(inputs) | ||||
|     else: | ||||
|       raise ValueError('invalid search_mode = {:}'.format(self.search_mode)) | ||||
|  | ||||
|   def search_forward(self, tuple_inputs): | ||||
|     assert isinstance(tuple_inputs, tuple) and len(tuple_inputs) == 5, 'invalid type input : {:}'.format( type(tuple_inputs) ) | ||||
|     inputs, expected_inC, probability, index, prob = tuple_inputs | ||||
|     index, prob = torch.squeeze(index).tolist(), torch.squeeze(prob) | ||||
|     probability = torch.squeeze(probability) | ||||
|     assert len(index) == 2, 'invalid length : {:}'.format(index) | ||||
|     # compute expected flop | ||||
|     #coordinates   = torch.arange(self.x_range[0], self.x_range[1]+1).type_as(probability) | ||||
|     expected_outC = (self.choices_tensor * probability).sum() | ||||
|     expected_flop = self.get_flops([expected_inC, expected_outC], False, 1e6) | ||||
|     if self.avg : out = self.avg( inputs ) | ||||
|     else        : out = inputs | ||||
|     # convolutional layer | ||||
|     out_convs = conv_forward(out, self.conv, [self.choices[i] for i in index]) | ||||
|     out_bns   = [self.BNs[idx](out_conv) for idx, out_conv in zip(index, out_convs)] | ||||
|     # merge | ||||
|     out_channel = max([x.size(1) for x in out_bns]) | ||||
|     outA = ChannelWiseInter(out_bns[0], out_channel) | ||||
|     outB = ChannelWiseInter(out_bns[1], out_channel) | ||||
|     out  = outA * prob[0] + outB * prob[1] | ||||
|     #out = additive_func(out_bns[0]*prob[0], out_bns[1]*prob[1]) | ||||
|  | ||||
|     if self.relu   : out = self.relu( out ) | ||||
|     if self.maxpool: out = self.maxpool(out) | ||||
|     return out, expected_outC, expected_flop | ||||
|  | ||||
|   def basic_forward(self, inputs): | ||||
|     if self.avg : out = self.avg( inputs ) | ||||
|     else        : out = inputs | ||||
|     conv = self.conv( out ) | ||||
|     if self.has_bn:out= self.BNs[-1]( conv ) | ||||
|     else        : out = conv | ||||
|     if self.relu: out = self.relu( out ) | ||||
|     else        : out = out | ||||
|     if self.InShape is None: | ||||
|       self.InShape  = (inputs.size(-2), inputs.size(-1)) | ||||
|       self.OutShape = (out.size(-2)   , out.size(-1)) | ||||
|     if self.maxpool: out = self.maxpool(out) | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class ResNetBasicblock(nn.Module): | ||||
|   expansion = 1 | ||||
|   num_conv  = 2 | ||||
|   def __init__(self, inplanes, planes, stride): | ||||
|     super(ResNetBasicblock, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     self.conv_a = ConvBNReLU(inplanes, planes, 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_b = ConvBNReLU(  planes, planes, 3,      1, 1, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes, 1, 1, 0, False, has_avg=True, has_bn=True, has_relu=False) | ||||
|     elif inplanes != planes: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes, 1, 1, 0, False, has_avg=False,has_bn=True, has_relu=False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     self.out_dim     = planes | ||||
|     self.search_mode = 'basic' | ||||
|  | ||||
|   def get_range(self): | ||||
|     return self.conv_a.get_range() + self.conv_b.get_range() | ||||
|  | ||||
|   def get_flops(self, channels): | ||||
|     assert len(channels) == 3, 'invalid channels : {:}'.format(channels) | ||||
|     flop_A = self.conv_a.get_flops([channels[0], channels[1]]) | ||||
|     flop_B = self.conv_b.get_flops([channels[1], channels[2]]) | ||||
|     if hasattr(self.downsample, 'get_flops'): | ||||
|       flop_C = self.downsample.get_flops([channels[0], channels[-1]]) | ||||
|     else: | ||||
|       flop_C = 0 | ||||
|     if channels[0] != channels[-1] and self.downsample is None: # this short-cut will be added during the infer-train | ||||
|       flop_C = channels[0] * channels[-1] * self.conv_b.OutShape[0] * self.conv_b.OutShape[1] | ||||
|     return flop_A + flop_B + flop_C | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.search_mode == 'basic'   : return self.basic_forward(inputs) | ||||
|     elif self.search_mode == 'search': return self.search_forward(inputs) | ||||
|     else: raise ValueError('invalid search_mode = {:}'.format(self.search_mode)) | ||||
|  | ||||
|   def search_forward(self, tuple_inputs): | ||||
|     assert isinstance(tuple_inputs, tuple) and len(tuple_inputs) == 5, 'invalid type input : {:}'.format( type(tuple_inputs) ) | ||||
|     inputs, expected_inC, probability, indexes, probs = tuple_inputs | ||||
|     assert indexes.size(0) == 2 and probs.size(0) == 2 and probability.size(0) == 2 | ||||
|     #import pdb; pdb.set_trace() | ||||
|     out_a, expected_inC_a, expected_flop_a = self.conv_a( (inputs, expected_inC  , probability[0], indexes[0], probs[0]) ) | ||||
|     out_b, expected_inC_b, expected_flop_b = self.conv_b( (out_a , expected_inC_a, probability[1], indexes[1], probs[1]) ) | ||||
|     if self.downsample is not None: | ||||
|       residual, _, expected_flop_c = self.downsample( (inputs, expected_inC  , probability[1], indexes[1], probs[1]) ) | ||||
|     else: | ||||
|       residual, expected_flop_c = inputs, 0 | ||||
|     out = additive_func(residual, out_b) | ||||
|     return nn.functional.relu(out, inplace=True), expected_inC_b, sum([expected_flop_a, expected_flop_b, expected_flop_c]) | ||||
|  | ||||
|   def basic_forward(self, inputs): | ||||
|     basicblock = self.conv_a(inputs) | ||||
|     basicblock = self.conv_b(basicblock) | ||||
|     if self.downsample is not None: residual = self.downsample(inputs) | ||||
|     else                          : residual = inputs | ||||
|     out = additive_func(residual, basicblock) | ||||
|     return nn.functional.relu(out, inplace=True) | ||||
|  | ||||
|  | ||||
|  | ||||
| class ResNetBottleneck(nn.Module): | ||||
|   expansion = 4 | ||||
|   num_conv  = 3 | ||||
|   def __init__(self, inplanes, planes, stride): | ||||
|     super(ResNetBottleneck, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     self.conv_1x1 = ConvBNReLU(inplanes, planes, 1,      1, 0, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_3x3 = ConvBNReLU(  planes, planes, 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     self.conv_1x4 = ConvBNReLU(planes, planes*self.expansion, 1, 1, 0, False, has_avg=False, has_bn=True, has_relu=False) | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes*self.expansion, 1, 1, 0, False, has_avg=True, has_bn=True, has_relu=False) | ||||
|     elif inplanes != planes*self.expansion: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes*self.expansion, 1, 1, 0, False, has_avg=False,has_bn=True, has_relu=False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     self.out_dim     = planes * self.expansion | ||||
|     self.search_mode = 'basic' | ||||
|  | ||||
|   def get_range(self): | ||||
|     return self.conv_1x1.get_range() + self.conv_3x3.get_range() + self.conv_1x4.get_range() | ||||
|  | ||||
|   def get_flops(self, channels): | ||||
|     assert len(channels) == 4, 'invalid channels : {:}'.format(channels) | ||||
|     flop_A = self.conv_1x1.get_flops([channels[0], channels[1]]) | ||||
|     flop_B = self.conv_3x3.get_flops([channels[1], channels[2]]) | ||||
|     flop_C = self.conv_1x4.get_flops([channels[2], channels[3]]) | ||||
|     if hasattr(self.downsample, 'get_flops'): | ||||
|       flop_D = self.downsample.get_flops([channels[0], channels[-1]]) | ||||
|     else: | ||||
|       flop_D = 0 | ||||
|     if channels[0] != channels[-1] and self.downsample is None: # this short-cut will be added during the infer-train | ||||
|       flop_D = channels[0] * channels[-1] * self.conv_1x4.OutShape[0] * self.conv_1x4.OutShape[1] | ||||
|     return flop_A + flop_B + flop_C + flop_D | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.search_mode == 'basic'   : return self.basic_forward(inputs) | ||||
|     elif self.search_mode == 'search': return self.search_forward(inputs) | ||||
|     else: raise ValueError('invalid search_mode = {:}'.format(self.search_mode)) | ||||
|  | ||||
|   def basic_forward(self, inputs): | ||||
|     bottleneck = self.conv_1x1(inputs) | ||||
|     bottleneck = self.conv_3x3(bottleneck) | ||||
|     bottleneck = self.conv_1x4(bottleneck) | ||||
|     if self.downsample is not None: residual = self.downsample(inputs) | ||||
|     else                          : residual = inputs | ||||
|     out = additive_func(residual, bottleneck) | ||||
|     return nn.functional.relu(out, inplace=True) | ||||
|  | ||||
|   def search_forward(self, tuple_inputs): | ||||
|     assert isinstance(tuple_inputs, tuple) and len(tuple_inputs) == 5, 'invalid type input : {:}'.format( type(tuple_inputs) ) | ||||
|     inputs, expected_inC, probability, indexes, probs = tuple_inputs | ||||
|     assert indexes.size(0) == 3 and probs.size(0) == 3 and probability.size(0) == 3 | ||||
|     out_1x1, expected_inC_1x1, expected_flop_1x1 = self.conv_1x1( (inputs, expected_inC    , probability[0], indexes[0], probs[0]) ) | ||||
|     out_3x3, expected_inC_3x3, expected_flop_3x3 = self.conv_3x3( (out_1x1,expected_inC_1x1, probability[1], indexes[1], probs[1]) ) | ||||
|     out_1x4, expected_inC_1x4, expected_flop_1x4 = self.conv_1x4( (out_3x3,expected_inC_3x3, probability[2], indexes[2], probs[2]) ) | ||||
|     if self.downsample is not None: | ||||
|       residual, _, expected_flop_c = self.downsample( (inputs, expected_inC  , probability[2], indexes[2], probs[2]) ) | ||||
|     else: | ||||
|       residual, expected_flop_c = inputs, 0 | ||||
|     out = additive_func(residual, out_1x4) | ||||
|     return nn.functional.relu(out, inplace=True), expected_inC_1x4, sum([expected_flop_1x1, expected_flop_3x3, expected_flop_1x4, expected_flop_c]) | ||||
|  | ||||
|  | ||||
| class SearchShapeImagenetResNet(nn.Module): | ||||
|  | ||||
|   def __init__(self, block_name, layers, deep_stem, num_classes): | ||||
|     super(SearchShapeImagenetResNet, self).__init__() | ||||
|  | ||||
|     #Model type specifies number of layers for CIFAR-10 and CIFAR-100 model | ||||
|     if block_name == 'BasicBlock': | ||||
|       block = ResNetBasicblock | ||||
|     elif block_name == 'Bottleneck': | ||||
|       block = ResNetBottleneck | ||||
|     else: | ||||
|       raise ValueError('invalid block : {:}'.format(block_name)) | ||||
|      | ||||
|     self.message      = 'SearchShapeCifarResNet : Depth : {:} , Layers for each block : {:}'.format(sum(layers)*block.num_conv, layers) | ||||
|     self.num_classes  = num_classes | ||||
|     if not deep_stem: | ||||
|       self.layers       = nn.ModuleList( [ ConvBNReLU(3, 64, 7, 2, 3, False, has_avg=False, has_bn=True, has_relu=True, last_max_pool=True) ] ) | ||||
|       self.channels     = [64] | ||||
|     else: | ||||
|       self.layers       = nn.ModuleList( [ ConvBNReLU(3, 32, 3, 2, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|                                           ,ConvBNReLU(32,64, 3, 1, 1, False, has_avg=False, has_bn=True, has_relu=True, last_max_pool=True) ] ) | ||||
|       self.channels     = [32, 64] | ||||
|  | ||||
|     meta_depth_info   = get_depth_choices(layers) | ||||
|     self.InShape      = None | ||||
|     self.depth_info   = OrderedDict() | ||||
|     self.depth_at_i   = OrderedDict() | ||||
|     for stage, layer_blocks in enumerate(layers): | ||||
|       cur_block_choices = meta_depth_info[stage] | ||||
|       assert cur_block_choices[-1] == layer_blocks, 'stage={:}, {:} vs {:}'.format(stage, cur_block_choices, layer_blocks) | ||||
|       block_choices, xstart = [], len(self.layers) | ||||
|       for iL in range(layer_blocks): | ||||
|         iC     = self.channels[-1] | ||||
|         planes = 64 * (2**stage) | ||||
|         stride = 2 if stage > 0 and iL == 0 else 1 | ||||
|         module = block(iC, planes, stride) | ||||
|         self.channels.append( module.out_dim ) | ||||
|         self.layers.append  ( module ) | ||||
|         self.message += "\nstage={:}, ilayer={:02d}/{:02d}, block={:03d}, iC={:3d}, oC={:3d}, stride={:}".format(stage, iL, layer_blocks, len(self.layers)-1, iC, module.out_dim, stride) | ||||
|         # added for depth | ||||
|         layer_index = len(self.layers) - 1 | ||||
|         if iL + 1 in cur_block_choices: block_choices.append( layer_index ) | ||||
|         if iL + 1 == layer_blocks: | ||||
|           self.depth_info[layer_index] = {'choices': block_choices, | ||||
|                                           'stage'  : stage, | ||||
|                                           'xstart' : xstart} | ||||
|     self.depth_info_list = [] | ||||
|     for xend, info in self.depth_info.items(): | ||||
|       self.depth_info_list.append( (xend, info) ) | ||||
|       xstart, xstage = info['xstart'], info['stage'] | ||||
|       for ilayer in range(xstart, xend+1): | ||||
|         idx = bisect_right(info['choices'], ilayer-1) | ||||
|         self.depth_at_i[ilayer] = (xstage, idx) | ||||
|  | ||||
|     self.avgpool     = nn.AdaptiveAvgPool2d((1,1)) | ||||
|     self.classifier  = nn.Linear(module.out_dim, num_classes) | ||||
|     self.InShape     = None | ||||
|     self.tau         = -1 | ||||
|     self.search_mode = 'basic' | ||||
|     #assert sum(x.num_conv for x in self.layers) + 1 == depth, 'invalid depth check {:} vs {:}'.format(sum(x.num_conv for x in self.layers)+1, depth) | ||||
|      | ||||
|     # parameters for width | ||||
|     self.Ranges = [] | ||||
|     self.layer2indexRange = [] | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       start_index = len(self.Ranges) | ||||
|       self.Ranges += layer.get_range() | ||||
|       self.layer2indexRange.append( (start_index, len(self.Ranges)) ) | ||||
|  | ||||
|     self.register_parameter('width_attentions', nn.Parameter(torch.Tensor(len(self.Ranges), get_width_choices(None)))) | ||||
|     self.register_parameter('depth_attentions', nn.Parameter(torch.Tensor(len(layers), meta_depth_info['num']))) | ||||
|     nn.init.normal_(self.width_attentions, 0, 0.01) | ||||
|     nn.init.normal_(self.depth_attentions, 0, 0.01) | ||||
|     self.apply(initialize_resnet) | ||||
|  | ||||
|   def arch_parameters(self, LR=None): | ||||
|     if LR is None: | ||||
|       return [self.width_attentions, self.depth_attentions] | ||||
|     else: | ||||
|       return [ | ||||
|                {"params": self.width_attentions, "lr": LR}, | ||||
|                {"params": self.depth_attentions, "lr": LR}, | ||||
|              ] | ||||
|  | ||||
|   def base_parameters(self): | ||||
|     return list(self.layers.parameters()) + list(self.avgpool.parameters()) + list(self.classifier.parameters()) | ||||
|  | ||||
|   def get_flop(self, mode, config_dict, extra_info): | ||||
|     if config_dict is not None: config_dict = config_dict.copy() | ||||
|     # select channels  | ||||
|     channels = [3] | ||||
|     for i, weight in enumerate(self.width_attentions): | ||||
|       if mode == 'genotype': | ||||
|         with torch.no_grad(): | ||||
|           probe = nn.functional.softmax(weight, dim=0) | ||||
|           C = self.Ranges[i][ torch.argmax(probe).item() ] | ||||
|       else: | ||||
|         raise ValueError('invalid mode : {:}'.format(mode)) | ||||
|       channels.append( C ) | ||||
|     # select depth | ||||
|     if mode == 'genotype': | ||||
|       with torch.no_grad(): | ||||
|         depth_probs = nn.functional.softmax(self.depth_attentions, dim=1) | ||||
|         choices = torch.argmax(depth_probs, dim=1).cpu().tolist() | ||||
|     else: | ||||
|       raise ValueError('invalid mode : {:}'.format(mode)) | ||||
|     selected_layers = [] | ||||
|     for choice, xvalue in zip(choices, self.depth_info_list): | ||||
|       xtemp = xvalue[1]['choices'][choice] - xvalue[1]['xstart'] + 1 | ||||
|       selected_layers.append(xtemp) | ||||
|     flop = 0 | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       s, e = self.layer2indexRange[i] | ||||
|       xchl = tuple( channels[s:e+1] ) | ||||
|       if i in self.depth_at_i: | ||||
|         xstagei, xatti = self.depth_at_i[i] | ||||
|         if xatti <= choices[xstagei]: # leave this depth | ||||
|           flop+= layer.get_flops(xchl) | ||||
|         else: | ||||
|           flop+= 0 # do not use this layer | ||||
|       else: | ||||
|         flop+= layer.get_flops(xchl) | ||||
|     # the last fc layer | ||||
|     flop += channels[-1] * self.classifier.out_features | ||||
|     if config_dict is None: | ||||
|       return flop / 1e6 | ||||
|     else: | ||||
|       config_dict['xchannels']  = channels | ||||
|       config_dict['xblocks']    = selected_layers | ||||
|       config_dict['super_type'] = 'infer-shape' | ||||
|       config_dict['estimated_FLOP'] = flop / 1e6 | ||||
|       return flop / 1e6, config_dict | ||||
|  | ||||
|   def get_arch_info(self): | ||||
|     string = "for depth and width, there are {:} + {:} attention probabilities.".format(len(self.depth_attentions), len(self.width_attentions)) | ||||
|     string+= '\n{:}'.format(self.depth_info) | ||||
|     discrepancy = [] | ||||
|     with torch.no_grad(): | ||||
|       for i, att in enumerate(self.depth_attentions): | ||||
|         prob = nn.functional.softmax(att, dim=0) | ||||
|         prob = prob.cpu() ; selc = prob.argmax().item() ; prob = prob.tolist() | ||||
|         prob = ['{:.3f}'.format(x) for x in prob] | ||||
|         xstring = '{:03d}/{:03d}-th : {:}'.format(i, len(self.depth_attentions), ' '.join(prob)) | ||||
|         logt = ['{:.4f}'.format(x) for x in att.cpu().tolist()] | ||||
|         xstring += '  ||  {:17s}'.format(' '.join(logt)) | ||||
|         prob = sorted( [float(x) for x in prob] ) | ||||
|         disc = prob[-1] - prob[-2] | ||||
|         xstring += '  || discrepancy={:.2f} || select={:}/{:}'.format(disc, selc, len(prob)) | ||||
|         discrepancy.append( disc ) | ||||
|         string += '\n{:}'.format(xstring) | ||||
|       string += '\n-----------------------------------------------' | ||||
|       for i, att in enumerate(self.width_attentions): | ||||
|         prob = nn.functional.softmax(att, dim=0) | ||||
|         prob = prob.cpu() ; selc = prob.argmax().item() ; prob = prob.tolist() | ||||
|         prob = ['{:.3f}'.format(x) for x in prob] | ||||
|         xstring = '{:03d}/{:03d}-th : {:}'.format(i, len(self.width_attentions), ' '.join(prob)) | ||||
|         logt = ['{:.3f}'.format(x) for x in att.cpu().tolist()] | ||||
|         xstring += '  ||  {:52s}'.format(' '.join(logt)) | ||||
|         prob = sorted( [float(x) for x in prob] ) | ||||
|         disc = prob[-1] - prob[-2] | ||||
|         xstring += '  || dis={:.2f} || select={:}/{:}'.format(disc, selc, len(prob)) | ||||
|         discrepancy.append( disc ) | ||||
|         string += '\n{:}'.format(xstring) | ||||
|     return string, discrepancy | ||||
|  | ||||
|   def set_tau(self, tau_max, tau_min, epoch_ratio): | ||||
|     assert epoch_ratio >= 0 and epoch_ratio <= 1, 'invalid epoch-ratio : {:}'.format(epoch_ratio) | ||||
|     tau = tau_min + (tau_max-tau_min) * (1 + math.cos(math.pi * epoch_ratio)) / 2 | ||||
|     self.tau = tau | ||||
|  | ||||
|   def get_message(self): | ||||
|     return self.message | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.search_mode == 'basic': | ||||
|       return self.basic_forward(inputs) | ||||
|     elif self.search_mode == 'search': | ||||
|       return self.search_forward(inputs) | ||||
|     else: | ||||
|       raise ValueError('invalid search_mode = {:}'.format(self.search_mode)) | ||||
|  | ||||
|   def search_forward(self, inputs): | ||||
|     flop_width_probs = nn.functional.softmax(self.width_attentions, dim=1) | ||||
|     flop_depth_probs = nn.functional.softmax(self.depth_attentions, dim=1) | ||||
|     flop_depth_probs = torch.flip( torch.cumsum( torch.flip(flop_depth_probs, [1]), 1 ), [1] ) | ||||
|     selected_widths, selected_width_probs = select2withP(self.width_attentions, self.tau) | ||||
|     selected_depth_probs = select2withP(self.depth_attentions, self.tau, True) | ||||
|     with torch.no_grad(): | ||||
|       selected_widths = selected_widths.cpu() | ||||
|  | ||||
|     x, last_channel_idx, expected_inC, flops = inputs, 0, 3, [] | ||||
|     feature_maps = [] | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       selected_w_index = selected_widths     [last_channel_idx: last_channel_idx+layer.num_conv] | ||||
|       selected_w_probs = selected_width_probs[last_channel_idx: last_channel_idx+layer.num_conv] | ||||
|       layer_prob       = flop_width_probs    [last_channel_idx: last_channel_idx+layer.num_conv] | ||||
|       x, expected_inC, expected_flop = layer( (x, expected_inC, layer_prob, selected_w_index, selected_w_probs) ) | ||||
|       feature_maps.append( x ) | ||||
|       last_channel_idx += layer.num_conv | ||||
|       if i in self.depth_info: # aggregate the information | ||||
|         choices = self.depth_info[i]['choices'] | ||||
|         xstagei = self.depth_info[i]['stage'] | ||||
|         #print ('iL={:}, choices={:}, stage={:}, probs={:}'.format(i, choices, xstagei, selected_depth_probs[xstagei].cpu().tolist())) | ||||
|         #for A, W in zip(choices, selected_depth_probs[xstagei]): | ||||
|         #  print('Size = {:}, W = {:}'.format(feature_maps[A].size(), W)) | ||||
|         possible_tensors = [] | ||||
|         max_C = max( feature_maps[A].size(1) for A in choices ) | ||||
|         for tempi, A in enumerate(choices): | ||||
|           xtensor = ChannelWiseInter(feature_maps[A], max_C) | ||||
|           possible_tensors.append( xtensor ) | ||||
|         weighted_sum = sum( xtensor * W for xtensor, W in zip(possible_tensors, selected_depth_probs[xstagei]) ) | ||||
|         x = weighted_sum | ||||
|          | ||||
|       if i in self.depth_at_i: | ||||
|         xstagei, xatti = self.depth_at_i[i] | ||||
|         x_expected_flop = flop_depth_probs[xstagei, xatti] * expected_flop | ||||
|       else: | ||||
|         x_expected_flop = expected_flop | ||||
|       flops.append( x_expected_flop ) | ||||
|     flops.append( expected_inC * (self.classifier.out_features*1.0/1e6) ) | ||||
|     features = self.avgpool(x) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     logits   = linear_forward(features, self.classifier) | ||||
|     return logits, torch.stack( [sum(flops)] ) | ||||
|  | ||||
|   def basic_forward(self, inputs): | ||||
|     if self.InShape is None: self.InShape = (inputs.size(-2), inputs.size(-1)) | ||||
|     x = inputs | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       x = layer( x ) | ||||
|     features = self.avgpool(x) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     logits   = self.classifier(features) | ||||
|     return features, logits | ||||
							
								
								
									
										316
									
								
								graph_dit/naswot/models/shape_searchs/SearchSimResNet_width.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										316
									
								
								graph_dit/naswot/models/shape_searchs/SearchSimResNet_width.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,316 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| import math, torch | ||||
| import torch.nn as nn | ||||
| from ..initialization import initialize_resnet | ||||
| from ..SharedUtils    import additive_func | ||||
| from .SoftSelect      import select2withP, ChannelWiseInter | ||||
| from .SoftSelect      import linear_forward | ||||
| from .SoftSelect      import get_width_choices as get_choices | ||||
|  | ||||
|  | ||||
| def conv_forward(inputs, conv, choices): | ||||
|   iC = conv.in_channels | ||||
|   fill_size = list(inputs.size()) | ||||
|   fill_size[1] = iC - fill_size[1] | ||||
|   filled  = torch.zeros(fill_size, device=inputs.device) | ||||
|   xinputs = torch.cat((inputs, filled), dim=1) | ||||
|   outputs = conv(xinputs) | ||||
|   selecteds = [outputs[:,:oC] for oC in choices] | ||||
|   return selecteds | ||||
|  | ||||
|  | ||||
| class ConvBNReLU(nn.Module): | ||||
|   num_conv  = 1 | ||||
|   def __init__(self, nIn, nOut, kernel, stride, padding, bias, has_avg, has_bn, has_relu): | ||||
|     super(ConvBNReLU, self).__init__() | ||||
|     self.InShape  = None | ||||
|     self.OutShape = None | ||||
|     self.choices  = get_choices(nOut) | ||||
|     self.register_buffer('choices_tensor', torch.Tensor( self.choices )) | ||||
|  | ||||
|     if has_avg : self.avg = nn.AvgPool2d(kernel_size=2, stride=2, padding=0) | ||||
|     else       : self.avg = None | ||||
|     self.conv = nn.Conv2d(nIn, nOut, kernel_size=kernel, stride=stride, padding=padding, dilation=1, groups=1, bias=bias) | ||||
|     #if has_bn  : self.bn  = nn.BatchNorm2d(nOut) | ||||
|     #else       : self.bn  = None | ||||
|     self.has_bn = has_bn | ||||
|     self.BNs  = nn.ModuleList() | ||||
|     for i, _out in enumerate(self.choices): | ||||
|       self.BNs.append(nn.BatchNorm2d(_out)) | ||||
|     if has_relu: self.relu = nn.ReLU(inplace=True) | ||||
|     else       : self.relu = None | ||||
|     self.in_dim   = nIn | ||||
|     self.out_dim  = nOut | ||||
|     self.search_mode = 'basic' | ||||
|  | ||||
|   def get_flops(self, channels, check_range=True, divide=1): | ||||
|     iC, oC = channels | ||||
|     if check_range: assert iC <= self.conv.in_channels and oC <= self.conv.out_channels, '{:} vs {:}  |  {:} vs {:}'.format(iC, self.conv.in_channels, oC, self.conv.out_channels) | ||||
|     assert isinstance(self.InShape, tuple) and len(self.InShape) == 2, 'invalid in-shape : {:}'.format(self.InShape) | ||||
|     assert isinstance(self.OutShape, tuple) and len(self.OutShape) == 2, 'invalid out-shape : {:}'.format(self.OutShape) | ||||
|     #conv_per_position_flops = self.conv.kernel_size[0] * self.conv.kernel_size[1] * iC * oC / self.conv.groups | ||||
|     conv_per_position_flops = (self.conv.kernel_size[0] * self.conv.kernel_size[1] * 1.0 / self.conv.groups) | ||||
|     all_positions = self.OutShape[0] * self.OutShape[1] | ||||
|     flops = (conv_per_position_flops * all_positions / divide) * iC * oC | ||||
|     if self.conv.bias is not None: flops += all_positions / divide | ||||
|     return flops | ||||
|  | ||||
|   def get_range(self): | ||||
|     return [self.choices] | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.search_mode == 'basic': | ||||
|       return self.basic_forward(inputs) | ||||
|     elif self.search_mode == 'search': | ||||
|       return self.search_forward(inputs) | ||||
|     else: | ||||
|       raise ValueError('invalid search_mode = {:}'.format(self.search_mode)) | ||||
|  | ||||
|   def search_forward(self, tuple_inputs): | ||||
|     assert isinstance(tuple_inputs, tuple) and len(tuple_inputs) == 5, 'invalid type input : {:}'.format( type(tuple_inputs) ) | ||||
|     inputs, expected_inC, probability, index, prob = tuple_inputs | ||||
|     index, prob = torch.squeeze(index).tolist(), torch.squeeze(prob) | ||||
|     probability = torch.squeeze(probability) | ||||
|     assert len(index) == 2, 'invalid length : {:}'.format(index) | ||||
|     # compute expected flop | ||||
|     #coordinates   = torch.arange(self.x_range[0], self.x_range[1]+1).type_as(probability) | ||||
|     expected_outC = (self.choices_tensor * probability).sum() | ||||
|     expected_flop = self.get_flops([expected_inC, expected_outC], False, 1e6) | ||||
|     if self.avg : out = self.avg( inputs ) | ||||
|     else        : out = inputs | ||||
|     # convolutional layer | ||||
|     out_convs = conv_forward(out, self.conv, [self.choices[i] for i in index]) | ||||
|     out_bns   = [self.BNs[idx](out_conv) for idx, out_conv in zip(index, out_convs)] | ||||
|     # merge | ||||
|     out_channel = max([x.size(1) for x in out_bns]) | ||||
|     outA = ChannelWiseInter(out_bns[0], out_channel) | ||||
|     outB = ChannelWiseInter(out_bns[1], out_channel) | ||||
|     out  = outA * prob[0] + outB * prob[1] | ||||
|     #out = additive_func(out_bns[0]*prob[0], out_bns[1]*prob[1]) | ||||
|  | ||||
|     if self.relu: out = self.relu( out ) | ||||
|     else        : out = out | ||||
|     return out, expected_outC, expected_flop | ||||
|  | ||||
|   def basic_forward(self, inputs): | ||||
|     if self.avg : out = self.avg( inputs ) | ||||
|     else        : out = inputs | ||||
|     conv = self.conv( out ) | ||||
|     if self.has_bn:out= self.BNs[-1]( conv ) | ||||
|     else        : out = conv | ||||
|     if self.relu: out = self.relu( out ) | ||||
|     else        : out = out | ||||
|     if self.InShape is None: | ||||
|       self.InShape  = (inputs.size(-2), inputs.size(-1)) | ||||
|       self.OutShape = (out.size(-2)   , out.size(-1)) | ||||
|     return out | ||||
|  | ||||
|  | ||||
| class SimBlock(nn.Module): | ||||
|   expansion = 1 | ||||
|   num_conv  = 1 | ||||
|   def __init__(self, inplanes, planes, stride): | ||||
|     super(SimBlock, self).__init__() | ||||
|     assert stride == 1 or stride == 2, 'invalid stride {:}'.format(stride) | ||||
|     self.conv = ConvBNReLU(inplanes, planes, 3, stride, 1, False, has_avg=False, has_bn=True, has_relu=True) | ||||
|     if stride == 2: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes, 1, 1, 0, False, has_avg=True, has_bn=False, has_relu=False) | ||||
|     elif inplanes != planes: | ||||
|       self.downsample = ConvBNReLU(inplanes, planes, 1, 1, 0, False, has_avg=False,has_bn=True , has_relu=False) | ||||
|     else: | ||||
|       self.downsample = None | ||||
|     self.out_dim     = planes | ||||
|     self.search_mode = 'basic' | ||||
|  | ||||
|   def get_range(self): | ||||
|     return self.conv.get_range() | ||||
|  | ||||
|   def get_flops(self, channels): | ||||
|     assert len(channels) == 2, 'invalid channels : {:}'.format(channels) | ||||
|     flop_A = self.conv.get_flops([channels[0], channels[1]]) | ||||
|     if hasattr(self.downsample, 'get_flops'): | ||||
|       flop_C = self.downsample.get_flops([channels[0], channels[-1]]) | ||||
|     else: | ||||
|       flop_C = 0 | ||||
|     if channels[0] != channels[-1] and self.downsample is None: # this short-cut will be added during the infer-train | ||||
|       flop_C = channels[0] * channels[-1] * self.conv.OutShape[0] * self.conv.OutShape[1] | ||||
|     return flop_A + flop_C | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.search_mode == 'basic'   : return self.basic_forward(inputs) | ||||
|     elif self.search_mode == 'search': return self.search_forward(inputs) | ||||
|     else: raise ValueError('invalid search_mode = {:}'.format(self.search_mode)) | ||||
|  | ||||
|   def search_forward(self, tuple_inputs): | ||||
|     assert isinstance(tuple_inputs, tuple) and len(tuple_inputs) == 5, 'invalid type input : {:}'.format( type(tuple_inputs) ) | ||||
|     inputs, expected_inC, probability, indexes, probs = tuple_inputs | ||||
|     assert indexes.size(0) == 1 and probs.size(0) == 1 and probability.size(0) == 1, 'invalid size : {:}, {:}, {:}'.format(indexes.size(), probs.size(), probability.size()) | ||||
|     out, expected_next_inC, expected_flop = self.conv( (inputs, expected_inC  , probability[0], indexes[0], probs[0]) ) | ||||
|     if self.downsample is not None: | ||||
|       residual, _, expected_flop_c = self.downsample( (inputs, expected_inC  , probability[-1], indexes[-1], probs[-1]) ) | ||||
|     else: | ||||
|       residual, expected_flop_c = inputs, 0 | ||||
|     out = additive_func(residual, out) | ||||
|     return nn.functional.relu(out, inplace=True), expected_next_inC, sum([expected_flop, expected_flop_c]) | ||||
|  | ||||
|   def basic_forward(self, inputs): | ||||
|     basicblock = self.conv(inputs) | ||||
|     if self.downsample is not None: residual = self.downsample(inputs) | ||||
|     else                          : residual = inputs | ||||
|     out = additive_func(residual, basicblock) | ||||
|     return nn.functional.relu(out, inplace=True) | ||||
|  | ||||
|  | ||||
|  | ||||
| class SearchWidthSimResNet(nn.Module): | ||||
|  | ||||
|   def __init__(self, depth, num_classes): | ||||
|     super(SearchWidthSimResNet, self).__init__() | ||||
|  | ||||
|     assert (depth - 2) % 3 == 0, 'depth should be one of 5, 8, 11, 14, ... instead of {:}'.format(depth) | ||||
|     layer_blocks = (depth - 2) // 3 | ||||
|     self.message     = 'SearchWidthSimResNet : Depth : {:} , Layers for each block : {:}'.format(depth, layer_blocks) | ||||
|     self.num_classes = num_classes | ||||
|     self.channels    = [16] | ||||
|     self.layers      = nn.ModuleList( [ ConvBNReLU(3, 16, 3, 1, 1, False, has_avg=False, has_bn=True, has_relu=True) ] ) | ||||
|     self.InShape     = None | ||||
|     for stage in range(3): | ||||
|       for iL in range(layer_blocks): | ||||
|         iC     = self.channels[-1] | ||||
|         planes = 16 * (2**stage) | ||||
|         stride = 2 if stage > 0 and iL == 0 else 1 | ||||
|         module = SimBlock(iC, planes, stride) | ||||
|         self.channels.append( module.out_dim ) | ||||
|         self.layers.append  ( module ) | ||||
|         self.message += "\nstage={:}, ilayer={:02d}/{:02d}, block={:03d}, iC={:3d}, oC={:3d}, stride={:}".format(stage, iL, layer_blocks, len(self.layers)-1, iC, module.out_dim, stride) | ||||
|    | ||||
|     self.avgpool     = nn.AvgPool2d(8) | ||||
|     self.classifier  = nn.Linear(module.out_dim, num_classes) | ||||
|     self.InShape     = None | ||||
|     self.tau         = -1 | ||||
|     self.search_mode = 'basic' | ||||
|     #assert sum(x.num_conv for x in self.layers) + 1 == depth, 'invalid depth check {:} vs {:}'.format(sum(x.num_conv for x in self.layers)+1, depth) | ||||
|      | ||||
|     # parameters for width | ||||
|     self.Ranges = [] | ||||
|     self.layer2indexRange = [] | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       start_index = len(self.Ranges) | ||||
|       self.Ranges += layer.get_range() | ||||
|       self.layer2indexRange.append( (start_index, len(self.Ranges)) ) | ||||
|     assert len(self.Ranges) + 1 == depth, 'invalid depth check {:} vs {:}'.format(len(self.Ranges) + 1, depth) | ||||
|  | ||||
|     self.register_parameter('width_attentions', nn.Parameter(torch.Tensor(len(self.Ranges), get_choices(None)))) | ||||
|     nn.init.normal_(self.width_attentions, 0, 0.01) | ||||
|     self.apply(initialize_resnet) | ||||
|  | ||||
|   def arch_parameters(self): | ||||
|     return [self.width_attentions] | ||||
|  | ||||
|   def base_parameters(self): | ||||
|     return list(self.layers.parameters()) + list(self.avgpool.parameters()) + list(self.classifier.parameters()) | ||||
|  | ||||
|   def get_flop(self, mode, config_dict, extra_info): | ||||
|     if config_dict is not None: config_dict = config_dict.copy() | ||||
|     #weights = [F.softmax(x, dim=0) for x in self.width_attentions] | ||||
|     channels = [3] | ||||
|     for i, weight in enumerate(self.width_attentions): | ||||
|       if mode == 'genotype': | ||||
|         with torch.no_grad(): | ||||
|           probe = nn.functional.softmax(weight, dim=0) | ||||
|           C = self.Ranges[i][ torch.argmax(probe).item() ] | ||||
|       elif mode == 'max': | ||||
|         C = self.Ranges[i][-1] | ||||
|       elif mode == 'fix': | ||||
|         C = int( math.sqrt( extra_info ) * self.Ranges[i][-1] ) | ||||
|       elif mode == 'random': | ||||
|         assert isinstance(extra_info, float), 'invalid extra_info : {:}'.format(extra_info) | ||||
|         with torch.no_grad(): | ||||
|           prob = nn.functional.softmax(weight, dim=0) | ||||
|           approximate_C = int( math.sqrt( extra_info ) * self.Ranges[i][-1] ) | ||||
|           for j in range(prob.size(0)): | ||||
|             prob[j] = 1 / (abs(j - (approximate_C-self.Ranges[i][j])) + 0.2) | ||||
|           C = self.Ranges[i][ torch.multinomial(prob, 1, False).item() ] | ||||
|       else: | ||||
|         raise ValueError('invalid mode : {:}'.format(mode)) | ||||
|       channels.append( C ) | ||||
|     flop = 0 | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       s, e = self.layer2indexRange[i] | ||||
|       xchl = tuple( channels[s:e+1] ) | ||||
|       flop+= layer.get_flops(xchl) | ||||
|     # the last fc layer | ||||
|     flop += channels[-1] * self.classifier.out_features | ||||
|     if config_dict is None: | ||||
|       return flop / 1e6 | ||||
|     else: | ||||
|       config_dict['xchannels']  = channels | ||||
|       config_dict['super_type'] = 'infer-width' | ||||
|       config_dict['estimated_FLOP'] = flop / 1e6 | ||||
|       return flop / 1e6, config_dict | ||||
|  | ||||
|   def get_arch_info(self): | ||||
|     string = "for width, there are {:} attention probabilities.".format(len(self.width_attentions)) | ||||
|     discrepancy = [] | ||||
|     with torch.no_grad(): | ||||
|       for i, att in enumerate(self.width_attentions): | ||||
|         prob = nn.functional.softmax(att, dim=0) | ||||
|         prob = prob.cpu() ; selc = prob.argmax().item() ; prob = prob.tolist() | ||||
|         prob = ['{:.3f}'.format(x) for x in prob] | ||||
|         xstring = '{:03d}/{:03d}-th : {:}'.format(i, len(self.width_attentions), ' '.join(prob)) | ||||
|         logt = ['{:.3f}'.format(x) for x in att.cpu().tolist()] | ||||
|         xstring += '  ||  {:52s}'.format(' '.join(logt)) | ||||
|         prob = sorted( [float(x) for x in prob] ) | ||||
|         disc = prob[-1] - prob[-2] | ||||
|         xstring += '  || dis={:.2f} || select={:}/{:}'.format(disc, selc, len(prob)) | ||||
|         discrepancy.append( disc ) | ||||
|         string += '\n{:}'.format(xstring) | ||||
|     return string, discrepancy | ||||
|  | ||||
|   def set_tau(self, tau_max, tau_min, epoch_ratio): | ||||
|     assert epoch_ratio >= 0 and epoch_ratio <= 1, 'invalid epoch-ratio : {:}'.format(epoch_ratio) | ||||
|     tau = tau_min + (tau_max-tau_min) * (1 + math.cos(math.pi * epoch_ratio)) / 2 | ||||
|     self.tau = tau | ||||
|  | ||||
|   def get_message(self): | ||||
|     return self.message | ||||
|  | ||||
|   def forward(self, inputs): | ||||
|     if self.search_mode == 'basic': | ||||
|       return self.basic_forward(inputs) | ||||
|     elif self.search_mode == 'search': | ||||
|       return self.search_forward(inputs) | ||||
|     else: | ||||
|       raise ValueError('invalid search_mode = {:}'.format(self.search_mode)) | ||||
|  | ||||
|   def search_forward(self, inputs): | ||||
|     flop_probs = nn.functional.softmax(self.width_attentions, dim=1) | ||||
|     selected_widths, selected_probs = select2withP(self.width_attentions, self.tau) | ||||
|     with torch.no_grad(): | ||||
|       selected_widths = selected_widths.cpu() | ||||
|  | ||||
|     x, last_channel_idx, expected_inC, flops = inputs, 0, 3, [] | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       selected_w_index = selected_widths[last_channel_idx: last_channel_idx+layer.num_conv] | ||||
|       selected_w_probs = selected_probs[last_channel_idx: last_channel_idx+layer.num_conv] | ||||
|       layer_prob       = flop_probs[last_channel_idx: last_channel_idx+layer.num_conv] | ||||
|       x, expected_inC, expected_flop = layer( (x, expected_inC, layer_prob, selected_w_index, selected_w_probs) ) | ||||
|       last_channel_idx += layer.num_conv | ||||
|       flops.append( expected_flop ) | ||||
|     flops.append( expected_inC * (self.classifier.out_features*1.0/1e6) ) | ||||
|     features = self.avgpool(x) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     logits   = linear_forward(features, self.classifier) | ||||
|     return logits, torch.stack( [sum(flops)] ) | ||||
|  | ||||
|   def basic_forward(self, inputs): | ||||
|     if self.InShape is None: self.InShape = (inputs.size(-2), inputs.size(-1)) | ||||
|     x = inputs | ||||
|     for i, layer in enumerate(self.layers): | ||||
|       x = layer( x ) | ||||
|     features = self.avgpool(x) | ||||
|     features = features.view(features.size(0), -1) | ||||
|     logits   = self.classifier(features) | ||||
|     return features, logits | ||||
							
								
								
									
										111
									
								
								graph_dit/naswot/models/shape_searchs/SoftSelect.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								graph_dit/naswot/models/shape_searchs/SoftSelect.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| import math, torch | ||||
| import torch.nn as nn | ||||
|  | ||||
|  | ||||
| def select2withP(logits, tau, just_prob=False, num=2, eps=1e-7): | ||||
|   if tau <= 0: | ||||
|     new_logits = logits | ||||
|     probs = nn.functional.softmax(new_logits, dim=1) | ||||
|   else       : | ||||
|     while True: # a trick to avoid the gumbels bug | ||||
|       gumbels = -torch.empty_like(logits).exponential_().log() | ||||
|       new_logits = (logits.log_softmax(dim=1) + gumbels) / tau | ||||
|       probs = nn.functional.softmax(new_logits, dim=1) | ||||
|       if (not torch.isinf(gumbels).any()) and (not torch.isinf(probs).any()) and (not torch.isnan(probs).any()): break | ||||
|  | ||||
|   if just_prob: return probs | ||||
|  | ||||
|   #with torch.no_grad(): # add eps for unexpected torch error | ||||
|   #  probs = nn.functional.softmax(new_logits, dim=1) | ||||
|   #  selected_index = torch.multinomial(probs + eps, 2, False) | ||||
|   with torch.no_grad(): # add eps for unexpected torch error | ||||
|     probs          = probs.cpu() | ||||
|     selected_index = torch.multinomial(probs + eps, num, False).to(logits.device) | ||||
|   selected_logit = torch.gather(new_logits, 1, selected_index) | ||||
|   selcted_probs  = nn.functional.softmax(selected_logit, dim=1) | ||||
|   return selected_index, selcted_probs | ||||
|  | ||||
|  | ||||
| def ChannelWiseInter(inputs, oC, mode='v2'): | ||||
|   if mode == 'v1': | ||||
|     return ChannelWiseInterV1(inputs, oC) | ||||
|   elif mode == 'v2': | ||||
|     return ChannelWiseInterV2(inputs, oC) | ||||
|   else: | ||||
|     raise ValueError('invalid mode : {:}'.format(mode)) | ||||
|  | ||||
|  | ||||
| def ChannelWiseInterV1(inputs, oC): | ||||
|   assert inputs.dim() == 4, 'invalid dimension : {:}'.format(inputs.size()) | ||||
|   def start_index(a, b, c): | ||||
|     return int( math.floor(float(a * c) / b) ) | ||||
|   def end_index(a, b, c): | ||||
|     return int( math.ceil(float((a + 1) * c) / b) ) | ||||
|   batch, iC, H, W = inputs.size() | ||||
|   outputs = torch.zeros((batch, oC, H, W), dtype=inputs.dtype, device=inputs.device) | ||||
|   if iC == oC: return inputs | ||||
|   for ot in range(oC): | ||||
|     istartT, iendT = start_index(ot, oC, iC), end_index(ot, oC, iC) | ||||
|     values = inputs[:, istartT:iendT].mean(dim=1)  | ||||
|     outputs[:, ot, :, :] = values | ||||
|   return outputs | ||||
|  | ||||
|  | ||||
| def ChannelWiseInterV2(inputs, oC): | ||||
|   assert inputs.dim() == 4, 'invalid dimension : {:}'.format(inputs.size()) | ||||
|   batch, C, H, W = inputs.size() | ||||
|   if C == oC: return inputs | ||||
|   else      : return nn.functional.adaptive_avg_pool3d(inputs, (oC,H,W)) | ||||
|   #inputs_5D = inputs.view(batch, 1, C, H, W) | ||||
|   #otputs_5D = nn.functional.interpolate(inputs_5D, (oC,H,W), None, 'area', None) | ||||
|   #otputs    = otputs_5D.view(batch, oC, H, W) | ||||
|   #otputs_5D = nn.functional.interpolate(inputs_5D, (oC,H,W), None, 'trilinear', False) | ||||
|   #return otputs | ||||
|  | ||||
|  | ||||
| def linear_forward(inputs, linear): | ||||
|   if linear is None: return inputs | ||||
|   iC = inputs.size(1) | ||||
|   weight = linear.weight[:, :iC] | ||||
|   if linear.bias is None: bias = None | ||||
|   else                  : bias = linear.bias | ||||
|   return nn.functional.linear(inputs, weight, bias) | ||||
|  | ||||
|  | ||||
| def get_width_choices(nOut): | ||||
|   xsrange = [0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0] | ||||
|   if nOut is None: | ||||
|     return len(xsrange) | ||||
|   else: | ||||
|     Xs = [int(nOut * i) for i in xsrange] | ||||
|     #xs = [ int(nOut * i // 10) for i in range(2, 11)] | ||||
|     #Xs = [x for i, x in enumerate(xs) if i+1 == len(xs) or xs[i+1] > x+1] | ||||
|     Xs = sorted( list( set(Xs) ) ) | ||||
|     return tuple(Xs) | ||||
|  | ||||
|  | ||||
| def get_depth_choices(nDepth): | ||||
|   if nDepth is None: | ||||
|     return 3 | ||||
|   else: | ||||
|     assert nDepth >= 3, 'nDepth should be greater than 2 vs {:}'.format(nDepth) | ||||
|     if nDepth == 1  : return (1, 1, 1) | ||||
|     elif nDepth == 2: return (1, 1, 2) | ||||
|     elif nDepth >= 3: | ||||
|       return (nDepth//3, nDepth*2//3, nDepth) | ||||
|     else: | ||||
|       raise ValueError('invalid Depth : {:}'.format(nDepth)) | ||||
|  | ||||
|  | ||||
| def drop_path(x, drop_prob): | ||||
|   if drop_prob > 0.: | ||||
|     keep_prob = 1. - drop_prob | ||||
|     mask = x.new_zeros(x.size(0), 1, 1, 1) | ||||
|     mask = mask.bernoulli_(keep_prob) | ||||
|     x = x * (mask / keep_prob) | ||||
|     #x.div_(keep_prob) | ||||
|     #x.mul_(mask) | ||||
|   return x | ||||
							
								
								
									
										8
									
								
								graph_dit/naswot/models/shape_searchs/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								graph_dit/naswot/models/shape_searchs/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| from .SearchCifarResNet_width import SearchWidthCifarResNet | ||||
| from .SearchCifarResNet_depth import SearchDepthCifarResNet | ||||
| from .SearchCifarResNet       import SearchShapeCifarResNet | ||||
| from .SearchSimResNet_width   import SearchWidthSimResNet | ||||
| from .SearchImagenetResNet    import SearchShapeImagenetResNet | ||||
							
								
								
									
										20
									
								
								graph_dit/naswot/models/shape_searchs/test.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								graph_dit/naswot/models/shape_searchs/test.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| ################################################## | ||||
| # Copyright (c) Xuanyi Dong [GitHub D-X-Y], 2019 # | ||||
| ################################################## | ||||
| import torch | ||||
| import torch.nn as nn | ||||
| from SoftSelect import ChannelWiseInter | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|  | ||||
|   tensors = torch.rand((16, 128, 7, 7)) | ||||
|    | ||||
|   for oc in range(200, 210): | ||||
|     out_v1  = ChannelWiseInter(tensors, oc, 'v1') | ||||
|     out_v2  = ChannelWiseInter(tensors, oc, 'v2') | ||||
|     assert (out_v1 == out_v2).any().item() == 1 | ||||
|   for oc in range(48, 160): | ||||
|     out_v1  = ChannelWiseInter(tensors, oc, 'v1') | ||||
|     out_v2  = ChannelWiseInter(tensors, oc, 'v2') | ||||
|     assert (out_v1 == out_v2).any().item() == 1 | ||||
							
								
								
									
										1
									
								
								graph_dit/naswot/nas_101_api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								graph_dit/naswot/nas_101_api/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
|   | ||||
							
								
								
									
										65
									
								
								graph_dit/naswot/nas_101_api/base_ops.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								graph_dit/naswot/nas_101_api/base_ops.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| """Base operations used by the modules in this search space.""" | ||||
|  | ||||
| from __future__ import absolute_import | ||||
| from __future__ import division | ||||
| from __future__ import print_function | ||||
|  | ||||
| import torch | ||||
| import torch.nn as nn | ||||
| import torch.nn.functional as F | ||||
|  | ||||
| class ConvBnRelu(nn.Module): | ||||
|     def __init__(self, in_channels, out_channels, kernel_size=1, stride=1, padding=0): | ||||
|         super(ConvBnRelu, self).__init__() | ||||
|  | ||||
|         self.conv_bn_relu = nn.Sequential( | ||||
|             #nn.ReLU(), | ||||
|             nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding, bias=False), | ||||
|             nn.BatchNorm2d(out_channels), | ||||
|             #nn.ReLU(inplace=True) | ||||
|             nn.ReLU() | ||||
|         ) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         return self.conv_bn_relu(x) | ||||
|  | ||||
| class Conv3x3BnRelu(nn.Module): | ||||
|     """3x3 convolution with batch norm and ReLU activation.""" | ||||
|     def __init__(self, in_channels, out_channels): | ||||
|         super(Conv3x3BnRelu, self).__init__() | ||||
|  | ||||
|         self.conv3x3 = ConvBnRelu(in_channels, out_channels, 3, 1, 1) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         x = self.conv3x3(x) | ||||
|         return x | ||||
|  | ||||
| class Conv1x1BnRelu(nn.Module): | ||||
|     """1x1 convolution with batch norm and ReLU activation.""" | ||||
|     def __init__(self, in_channels, out_channels): | ||||
|         super(Conv1x1BnRelu, self).__init__() | ||||
|  | ||||
|         self.conv1x1 = ConvBnRelu(in_channels, out_channels, 1, 1, 0) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         x = self.conv1x1(x) | ||||
|         return x | ||||
|  | ||||
| class MaxPool3x3(nn.Module): | ||||
|     """3x3 max pool with no subsampling.""" | ||||
|     def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, padding=1): | ||||
|         super(MaxPool3x3, self).__init__() | ||||
|  | ||||
|         self.maxpool = nn.MaxPool2d(kernel_size, stride, padding) | ||||
|         #self.maxpool = nn.AvgPool2d(kernel_size, stride, padding) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         x = self.maxpool(x) | ||||
|         return x | ||||
|  | ||||
| # Commas should not be used in op names | ||||
| OP_MAP = { | ||||
|     'conv3x3-bn-relu': Conv3x3BnRelu, | ||||
|     'conv1x1-bn-relu': Conv1x1BnRelu, | ||||
|     'maxpool3x3': MaxPool3x3 | ||||
| } | ||||
							
								
								
									
										167
									
								
								graph_dit/naswot/nas_101_api/graph_util.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								graph_dit/naswot/nas_101_api/graph_util.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,167 @@ | ||||
| # Copyright 2019 The Google Research Authors. | ||||
| # | ||||
| # 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. | ||||
|  | ||||
| """Utility functions used by generate_graph.py.""" | ||||
| from __future__ import absolute_import | ||||
| from __future__ import division | ||||
| from __future__ import print_function | ||||
|  | ||||
| import hashlib | ||||
| import itertools | ||||
|  | ||||
| import numpy as np | ||||
|  | ||||
|  | ||||
| def gen_is_edge_fn(bits): | ||||
|   """Generate a boolean function for the edge connectivity. | ||||
|  | ||||
|   Given a bitstring FEDCBA and a 4x4 matrix, the generated matrix is | ||||
|     [[0, A, B, D], | ||||
|      [0, 0, C, E], | ||||
|      [0, 0, 0, F], | ||||
|      [0, 0, 0, 0]] | ||||
|  | ||||
|   Note that this function is agnostic to the actual matrix dimension due to | ||||
|   order in which elements are filled out (column-major, starting from least | ||||
|   significant bit). For example, the same FEDCBA bitstring (0-padded) on a 5x5 | ||||
|   matrix is | ||||
|     [[0, A, B, D, 0], | ||||
|      [0, 0, C, E, 0], | ||||
|      [0, 0, 0, F, 0], | ||||
|      [0, 0, 0, 0, 0], | ||||
|      [0, 0, 0, 0, 0]] | ||||
|  | ||||
|   Args: | ||||
|     bits: integer which will be interpreted as a bit mask. | ||||
|  | ||||
|   Returns: | ||||
|     vectorized function that returns True when an edge is present. | ||||
|   """ | ||||
|   def is_edge(x, y): | ||||
|     """Is there an edge from x to y (0-indexed)?""" | ||||
|     if x >= y: | ||||
|       return 0 | ||||
|     # Map x, y to index into bit string | ||||
|     index = x + (y * (y - 1) // 2) | ||||
|     return (bits >> index) % 2 == 1 | ||||
|  | ||||
|   return np.vectorize(is_edge) | ||||
|  | ||||
|  | ||||
| def is_full_dag(matrix): | ||||
|   """Full DAG == all vertices on a path from vert 0 to (V-1). | ||||
|  | ||||
|   i.e. no disconnected or "hanging" vertices. | ||||
|  | ||||
|   It is sufficient to check for: | ||||
|     1) no rows of 0 except for row V-1 (only output vertex has no out-edges) | ||||
|     2) no cols of 0 except for col 0 (only input vertex has no in-edges) | ||||
|  | ||||
|   Args: | ||||
|     matrix: V x V upper-triangular adjacency matrix | ||||
|  | ||||
|   Returns: | ||||
|     True if the there are no dangling vertices. | ||||
|   """ | ||||
|   shape = np.shape(matrix) | ||||
|  | ||||
|   rows = matrix[:shape[0]-1, :] == 0 | ||||
|   rows = np.all(rows, axis=1)     # Any row with all 0 will be True | ||||
|   rows_bad = np.any(rows) | ||||
|  | ||||
|   cols = matrix[:, 1:] == 0 | ||||
|   cols = np.all(cols, axis=0)     # Any col with all 0 will be True | ||||
|   cols_bad = np.any(cols) | ||||
|  | ||||
|   return (not rows_bad) and (not cols_bad) | ||||
|  | ||||
|  | ||||
| def num_edges(matrix): | ||||
|   """Computes number of edges in adjacency matrix.""" | ||||
|   return np.sum(matrix) | ||||
|  | ||||
|  | ||||
| def hash_module(matrix, labeling): | ||||
|   """Computes a graph-invariance MD5 hash of the matrix and label pair. | ||||
|  | ||||
|   Args: | ||||
|     matrix: np.ndarray square upper-triangular adjacency matrix. | ||||
|     labeling: list of int labels of length equal to both dimensions of | ||||
|       matrix. | ||||
|  | ||||
|   Returns: | ||||
|     MD5 hash of the matrix and labeling. | ||||
|   """ | ||||
|   vertices = np.shape(matrix)[0] | ||||
|   in_edges = np.sum(matrix, axis=0).tolist() | ||||
|   out_edges = np.sum(matrix, axis=1).tolist() | ||||
|  | ||||
|   assert len(in_edges) == len(out_edges) == len(labeling) | ||||
|   hashes = list(zip(out_edges, in_edges, labeling)) | ||||
|   hashes = [hashlib.md5(str(h).encode('utf-8')).hexdigest() for h in hashes] | ||||
|   # Computing this up to the diameter is probably sufficient but since the | ||||
|   # operation is fast, it is okay to repeat more times. | ||||
|   for _ in range(vertices): | ||||
|     new_hashes = [] | ||||
|     for v in range(vertices): | ||||
|       in_neighbors = [hashes[w] for w in range(vertices) if matrix[w, v]] | ||||
|       out_neighbors = [hashes[w] for w in range(vertices) if matrix[v, w]] | ||||
|       new_hashes.append(hashlib.md5( | ||||
|           (''.join(sorted(in_neighbors)) + '|' + | ||||
|            ''.join(sorted(out_neighbors)) + '|' + | ||||
|            hashes[v]).encode('utf-8')).hexdigest()) | ||||
|     hashes = new_hashes | ||||
|   fingerprint = hashlib.md5(str(sorted(hashes)).encode('utf-8')).hexdigest() | ||||
|  | ||||
|   return fingerprint | ||||
|  | ||||
|  | ||||
| def permute_graph(graph, label, permutation): | ||||
|   """Permutes the graph and labels based on permutation. | ||||
|  | ||||
|   Args: | ||||
|     graph: np.ndarray adjacency matrix. | ||||
|     label: list of labels of same length as graph dimensions. | ||||
|     permutation: a permutation list of ints of same length as graph dimensions. | ||||
|  | ||||
|   Returns: | ||||
|     np.ndarray where vertex permutation[v] is vertex v from the original graph | ||||
|   """ | ||||
|   # vertex permutation[v] in new graph is vertex v in the old graph | ||||
|   forward_perm = zip(permutation, list(range(len(permutation)))) | ||||
|   inverse_perm = [x[1] for x in sorted(forward_perm)] | ||||
|   edge_fn = lambda x, y: graph[inverse_perm[x], inverse_perm[y]] == 1 | ||||
|   new_matrix = np.fromfunction(np.vectorize(edge_fn), | ||||
|                                (len(label), len(label)), | ||||
|                                dtype=np.int8) | ||||
|   new_label = [label[inverse_perm[i]] for i in range(len(label))] | ||||
|   return new_matrix, new_label | ||||
|  | ||||
|  | ||||
| def is_isomorphic(graph1, graph2): | ||||
|   """Exhaustively checks if 2 graphs are isomorphic.""" | ||||
|   matrix1, label1 = np.array(graph1[0]), graph1[1] | ||||
|   matrix2, label2 = np.array(graph2[0]), graph2[1] | ||||
|   assert np.shape(matrix1) == np.shape(matrix2) | ||||
|   assert len(label1) == len(label2) | ||||
|  | ||||
|   vertices = np.shape(matrix1)[0] | ||||
|   # Note: input and output in our constrained graphs always map to themselves | ||||
|   # but this script does not enforce that. | ||||
|   for perm in itertools.permutations(range(0, vertices)): | ||||
|     pmatrix1, plabel1 = permute_graph(matrix1, label1, perm) | ||||
|     if np.array_equal(pmatrix1, matrix2) and plabel1 == label2: | ||||
|       return True | ||||
|  | ||||
|   return False | ||||
							
								
								
									
										252
									
								
								graph_dit/naswot/nas_101_api/model.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								graph_dit/naswot/nas_101_api/model.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,252 @@ | ||||
| """Builds the Pytorch computational graph. | ||||
|  | ||||
| Tensors flowing into a single vertex are added together for all vertices | ||||
| except the output, which is concatenated instead. Tensors flowing out of input | ||||
| are always added. | ||||
|  | ||||
| If interior edge channels don't match, drop the extra channels (channels are | ||||
| guaranteed non-decreasing). Tensors flowing out of the input as always | ||||
| projected instead. | ||||
| """ | ||||
|  | ||||
| from __future__ import absolute_import | ||||
| from __future__ import division | ||||
| from __future__ import print_function | ||||
|  | ||||
| import numpy as np | ||||
| import math | ||||
|  | ||||
| from .base_ops import * | ||||
|  | ||||
| import torch | ||||
| import torch.nn as nn | ||||
| import torch.nn.functional as F | ||||
|  | ||||
| class Network(nn.Module): | ||||
|     def __init__(self, spec, args, searchspace=[]): | ||||
|         super(Network, self).__init__() | ||||
|  | ||||
|         self.layers = nn.ModuleList([]) | ||||
|  | ||||
|         in_channels = 3 | ||||
|         out_channels = args.stem_out_channels | ||||
|  | ||||
|         # initial stem convolution | ||||
|         stem_conv = ConvBnRelu(in_channels, out_channels, 3, 1, 1) | ||||
|         self.layers.append(stem_conv) | ||||
|  | ||||
|         in_channels = out_channels | ||||
|         for stack_num in range(args.num_stacks): | ||||
|             if stack_num > 0: | ||||
|                 #downsample = nn.MaxPool2d(kernel_size=3, stride=2) | ||||
|                 downsample = nn.MaxPool2d(kernel_size=2, stride=2) | ||||
|                 #downsample = nn.AvgPool2d(kernel_size=2, stride=2) | ||||
|                 #downsample = nn.Conv2d(in_channels, out_channels, kernel_size=(2, 2), stride=2) | ||||
|                 self.layers.append(downsample) | ||||
|  | ||||
|                 out_channels *= 2 | ||||
|  | ||||
|             for module_num in range(args.num_modules_per_stack): | ||||
|                 cell = Cell(spec, in_channels, out_channels) | ||||
|                 self.layers.append(cell) | ||||
|                 in_channels = out_channels | ||||
|  | ||||
|         self.classifier = nn.Linear(out_channels, args.num_labels) | ||||
|  | ||||
|         # for DARTS search | ||||
|         num_edge = np.shape(spec.matrix)[0] | ||||
|         self.arch_parameters = nn.Parameter( 1e-3*torch.randn(num_edge, len(searchspace))) | ||||
|  | ||||
|         self._initialize_weights() | ||||
|  | ||||
|     def forward(self, x, get_ints=True): | ||||
|         ints = [] | ||||
|         for _, layer in enumerate(self.layers): | ||||
|             x = layer(x) | ||||
|             ints.append(x) | ||||
|         out = torch.mean(x, (2, 3)) | ||||
|         ints.append(out) | ||||
|         out = self.classifier(out) | ||||
|         if get_ints: | ||||
|             return out, ints[-1] | ||||
|         else: | ||||
|             return out | ||||
|  | ||||
|     def _initialize_weights(self): | ||||
|         for m in self.modules(): | ||||
|             if isinstance(m, nn.Conv2d): | ||||
|                 n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels | ||||
|                 m.weight.data.normal_(0, math.sqrt(2.0 / n)) | ||||
|                 if m.bias is not None: | ||||
|                     m.bias.data.zero_() | ||||
|                 pass | ||||
|             elif isinstance(m, nn.BatchNorm2d): | ||||
|                 m.weight.data.fill_(1) | ||||
|                 m.bias.data.zero_() | ||||
|                 pass | ||||
|             elif isinstance(m, nn.Linear): | ||||
|                 n = m.weight.size(1) | ||||
|                 m.weight.data.normal_(0, 0.01) | ||||
|                 m.bias.data.zero_() | ||||
|                 pass | ||||
|  | ||||
|     def get_weights(self): | ||||
|         xlist = [] | ||||
|         for m in self.modules(): | ||||
|             xlist.append(m.parameters()) | ||||
|         return xlist | ||||
|  | ||||
|     def get_alphas(self): | ||||
|         return [self.arch_parameters] | ||||
|  | ||||
|     def genotype(self): | ||||
|         return str(spec) | ||||
|  | ||||
|  | ||||
| class Cell(nn.Module): | ||||
|     """ | ||||
|     Builds the model using the adjacency matrix and op labels specified. Channels | ||||
|     controls the module output channel count but the interior channels are | ||||
|     determined via equally splitting the channel count whenever there is a | ||||
|     concatenation of Tensors. | ||||
|     """ | ||||
|     def __init__(self, spec, in_channels, out_channels): | ||||
|         super(Cell, self).__init__() | ||||
|  | ||||
|         self.spec = spec | ||||
|         self.num_vertices = np.shape(self.spec.matrix)[0] | ||||
|  | ||||
|         # vertex_channels[i] = number of output channels of vertex i | ||||
|         self.vertex_channels = ComputeVertexChannels(in_channels, out_channels, self.spec.matrix) | ||||
|         #self.vertex_channels = [in_channels] + [out_channels] * (self.num_vertices - 1) | ||||
|  | ||||
|         # operation for each node | ||||
|         self.vertex_op = nn.ModuleList([None]) | ||||
|         for t in range(1, self.num_vertices-1): | ||||
|             op = OP_MAP[spec.ops[t]](self.vertex_channels[t], self.vertex_channels[t]) | ||||
|             self.vertex_op.append(op) | ||||
|  | ||||
|         # operation for input on each vertex | ||||
|         self.input_op = nn.ModuleList([None]) | ||||
|         for t in range(1, self.num_vertices): | ||||
|             if self.spec.matrix[0, t]: | ||||
|                 self.input_op.append(Projection(in_channels, self.vertex_channels[t])) | ||||
|             else: | ||||
|                 self.input_op.append(None) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         tensors = [x] | ||||
|         out_concat = [] | ||||
|         for t in range(1, self.num_vertices-1): | ||||
|             fan_in = [Truncate(tensors[src], self.vertex_channels[t]) for src in range(1, t) if self.spec.matrix[src, t]] | ||||
|             fan_in_inds = [src for src in range(1, t) if self.spec.matrix[src, t]] | ||||
|  | ||||
|             if self.spec.matrix[0, t]: | ||||
|                 fan_in.append(self.input_op[t](x)) | ||||
|                 fan_in_inds = [0] + fan_in_inds | ||||
|  | ||||
|             # perform operation on node | ||||
|             #vertex_input = torch.stack(fan_in, dim=0).sum(dim=0) | ||||
|             vertex_input = sum(fan_in) | ||||
|             #vertex_input = sum(fan_in) / len(fan_in) | ||||
|             vertex_output = self.vertex_op[t](vertex_input) | ||||
|  | ||||
|             tensors.append(vertex_output) | ||||
|             if self.spec.matrix[t, self.num_vertices-1]: | ||||
|                 out_concat.append(tensors[t]) | ||||
|  | ||||
|         if not out_concat: # empty list | ||||
|             assert self.spec.matrix[0, self.num_vertices-1] | ||||
|             outputs = self.input_op[self.num_vertices-1](tensors[0]) | ||||
|         else: | ||||
|             if len(out_concat) == 1: | ||||
|                 outputs = out_concat[0] | ||||
|             else: | ||||
|                 outputs = torch.cat(out_concat, 1) | ||||
|  | ||||
|             if self.spec.matrix[0, self.num_vertices-1]: | ||||
|                 outputs += self.input_op[self.num_vertices-1](tensors[0]) | ||||
|  | ||||
|             #if self.spec.matrix[0, self.num_vertices-1]: | ||||
|             #    out_concat.append(self.input_op[self.num_vertices-1](tensors[0])) | ||||
|             #outputs = sum(out_concat) / len(out_concat) | ||||
|  | ||||
|         return outputs | ||||
|  | ||||
| def Projection(in_channels, out_channels): | ||||
|     """1x1 projection (as in ResNet) followed by batch normalization and ReLU.""" | ||||
|     return ConvBnRelu(in_channels, out_channels, 1) | ||||
|  | ||||
| def Truncate(inputs, channels): | ||||
|     """Slice the inputs to channels if necessary.""" | ||||
|     input_channels = inputs.size()[1] | ||||
|     if input_channels < channels: | ||||
|         raise ValueError('input channel < output channels for truncate') | ||||
|     elif input_channels == channels: | ||||
|         return inputs   # No truncation necessary | ||||
|     else: | ||||
|         # Truncation should only be necessary when channel division leads to | ||||
|         # vertices with +1 channels. The input vertex should always be projected to | ||||
|         # the minimum channel count. | ||||
|         assert input_channels - channels == 1 | ||||
|         return inputs[:, :channels, :, :] | ||||
|  | ||||
| def ComputeVertexChannels(in_channels, out_channels, matrix): | ||||
|     """Computes the number of channels at every vertex. | ||||
|  | ||||
|     Given the input channels and output channels, this calculates the number of | ||||
|     channels at each interior vertex. Interior vertices have the same number of | ||||
|     channels as the max of the channels of the vertices it feeds into. The output | ||||
|     channels are divided amongst the vertices that are directly connected to it. | ||||
|     When the division is not even, some vertices may receive an extra channel to | ||||
|     compensate. | ||||
|  | ||||
|     Returns: | ||||
|         list of channel counts, in order of the vertices. | ||||
|     """ | ||||
|     num_vertices = np.shape(matrix)[0] | ||||
|  | ||||
|     vertex_channels = [0] * num_vertices | ||||
|     vertex_channels[0] = in_channels | ||||
|     vertex_channels[num_vertices - 1] = out_channels | ||||
|  | ||||
|     if num_vertices == 2: | ||||
|         # Edge case where module only has input and output vertices | ||||
|         return vertex_channels | ||||
|  | ||||
|     # Compute the in-degree ignoring input, axis 0 is the src vertex and axis 1 is | ||||
|     # the dst vertex. Summing over 0 gives the in-degree count of each vertex. | ||||
|     in_degree = np.sum(matrix[1:], axis=0) | ||||
|     interior_channels = out_channels // in_degree[num_vertices - 1] | ||||
|     correction = out_channels % in_degree[num_vertices - 1]  # Remainder to add | ||||
|  | ||||
|     # Set channels of vertices that flow directly to output | ||||
|     for v in range(1, num_vertices - 1): | ||||
|       if matrix[v, num_vertices - 1]: | ||||
|           vertex_channels[v] = interior_channels | ||||
|           if correction: | ||||
|               vertex_channels[v] += 1 | ||||
|               correction -= 1 | ||||
|  | ||||
|     # Set channels for all other vertices to the max of the out edges, going | ||||
|     # backwards. (num_vertices - 2) index skipped because it only connects to | ||||
|     # output. | ||||
|     for v in range(num_vertices - 3, 0, -1): | ||||
|         if not matrix[v, num_vertices - 1]: | ||||
|             for dst in range(v + 1, num_vertices - 1): | ||||
|                 if matrix[v, dst]: | ||||
|                     vertex_channels[v] = max(vertex_channels[v], vertex_channels[dst]) | ||||
|         assert vertex_channels[v] > 0 | ||||
|  | ||||
|     # Sanity check, verify that channels never increase and final channels add up. | ||||
|     final_fan_in = 0 | ||||
|     for v in range(1, num_vertices - 1): | ||||
|         if matrix[v, num_vertices - 1]: | ||||
|             final_fan_in += vertex_channels[v] | ||||
|         for dst in range(v + 1, num_vertices - 1): | ||||
|             if matrix[v, dst]: | ||||
|                 assert vertex_channels[v] >= vertex_channels[dst] | ||||
|     assert final_fan_in == out_channels or num_vertices == 2 | ||||
|     # num_vertices == 2 means only input/output nodes, so 0 fan-in | ||||
|  | ||||
|     return vertex_channels | ||||
							
								
								
									
										152
									
								
								graph_dit/naswot/nas_101_api/model_spec.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								graph_dit/naswot/nas_101_api/model_spec.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| """Model specification for module connectivity individuals. | ||||
|  | ||||
| This module handles pruning the unused parts of the computation graph but should | ||||
| avoid creating any TensorFlow models (this is done inside model_builder.py). | ||||
| """ | ||||
|  | ||||
| from __future__ import absolute_import | ||||
| from __future__ import division | ||||
| from __future__ import print_function | ||||
|  | ||||
| import copy | ||||
| import numpy as np | ||||
|  | ||||
| from . import graph_util | ||||
|  | ||||
| # Graphviz is optional and only required for visualization. | ||||
| try: | ||||
|   import graphviz   # pylint: disable=g-import-not-at-top | ||||
| except ImportError: | ||||
|   pass | ||||
|  | ||||
|  | ||||
| class ModelSpec(object): | ||||
|   """Model specification given adjacency matrix and labeling.""" | ||||
|  | ||||
|   def __init__(self, matrix, ops, data_format='channels_last'): | ||||
|     """Initialize the module spec. | ||||
|  | ||||
|     Args: | ||||
|       matrix: ndarray or nested list with shape [V, V] for the adjacency matrix. | ||||
|       ops: V-length list of labels for the base ops used. The first and last | ||||
|         elements are ignored because they are the input and output vertices | ||||
|         which have no operations. The elements are retained to keep consistent | ||||
|         indexing. | ||||
|       data_format: channels_last or channels_first. | ||||
|  | ||||
|     Raises: | ||||
|       ValueError: invalid matrix or ops | ||||
|     """ | ||||
|     if not isinstance(matrix, np.ndarray): | ||||
|       matrix = np.array(matrix) | ||||
|     shape = np.shape(matrix) | ||||
|     if len(shape) != 2 or shape[0] != shape[1]: | ||||
|       raise ValueError('matrix must be square') | ||||
|     if shape[0] != len(ops): | ||||
|       raise ValueError('length of ops must match matrix dimensions') | ||||
|     if not is_upper_triangular(matrix): | ||||
|       raise ValueError('matrix must be upper triangular') | ||||
|  | ||||
|     # Both the original and pruned matrices are deep copies of the matrix and | ||||
|     # ops so any changes to those after initialization are not recognized by the | ||||
|     # spec. | ||||
|     self.original_matrix = copy.deepcopy(matrix) | ||||
|     self.original_ops = copy.deepcopy(ops) | ||||
|  | ||||
|     self.matrix = copy.deepcopy(matrix) | ||||
|     self.ops = copy.deepcopy(ops) | ||||
|     self.valid_spec = True | ||||
|     self._prune() | ||||
|  | ||||
|     self.data_format = data_format | ||||
|  | ||||
|   def _prune(self): | ||||
|     """Prune the extraneous parts of the graph. | ||||
|  | ||||
|     General procedure: | ||||
|       1) Remove parts of graph not connected to input. | ||||
|       2) Remove parts of graph not connected to output. | ||||
|       3) Reorder the vertices so that they are consecutive after steps 1 and 2. | ||||
|  | ||||
|     These 3 steps can be combined by deleting the rows and columns of the | ||||
|     vertices that are not reachable from both the input and output (in reverse). | ||||
|     """ | ||||
|     num_vertices = np.shape(self.original_matrix)[0] | ||||
|  | ||||
|     # DFS forward from input | ||||
|     visited_from_input = set([0]) | ||||
|     frontier = [0] | ||||
|     while frontier: | ||||
|       top = frontier.pop() | ||||
|       for v in range(top + 1, num_vertices): | ||||
|         if self.original_matrix[top, v] and v not in visited_from_input: | ||||
|           visited_from_input.add(v) | ||||
|           frontier.append(v) | ||||
|  | ||||
|     # DFS backward from output | ||||
|     visited_from_output = set([num_vertices - 1]) | ||||
|     frontier = [num_vertices - 1] | ||||
|     while frontier: | ||||
|       top = frontier.pop() | ||||
|       for v in range(0, top): | ||||
|         if self.original_matrix[v, top] and v not in visited_from_output: | ||||
|           visited_from_output.add(v) | ||||
|           frontier.append(v) | ||||
|  | ||||
|     # Any vertex that isn't connected to both input and output is extraneous to | ||||
|     # the computation graph. | ||||
|     extraneous = set(range(num_vertices)).difference( | ||||
|         visited_from_input.intersection(visited_from_output)) | ||||
|  | ||||
|     # If the non-extraneous graph is less than 2 vertices, the input is not | ||||
|     # connected to the output and the spec is invalid. | ||||
|     if len(extraneous) > num_vertices - 2: | ||||
|       self.matrix = None | ||||
|       self.ops = None | ||||
|       self.valid_spec = False | ||||
|       return | ||||
|  | ||||
|     self.matrix = np.delete(self.matrix, list(extraneous), axis=0) | ||||
|     self.matrix = np.delete(self.matrix, list(extraneous), axis=1) | ||||
|     for index in sorted(extraneous, reverse=True): | ||||
|       del self.ops[index] | ||||
|  | ||||
|   def hash_spec(self, canonical_ops): | ||||
|     """Computes the isomorphism-invariant graph hash of this spec. | ||||
|  | ||||
|     Args: | ||||
|       canonical_ops: list of operations in the canonical ordering which they | ||||
|         were assigned (i.e. the order provided in the config['available_ops']). | ||||
|  | ||||
|     Returns: | ||||
|       MD5 hash of this spec which can be used to query the dataset. | ||||
|     """ | ||||
|     # Invert the operations back to integer label indices used in graph gen. | ||||
|     labeling = [-1] + [canonical_ops.index(op) for op in self.ops[1:-1]] + [-2] | ||||
|     return graph_util.hash_module(self.matrix, labeling) | ||||
|  | ||||
|   def visualize(self): | ||||
|     """Creates a dot graph. Can be visualized in colab directly.""" | ||||
|     num_vertices = np.shape(self.matrix)[0] | ||||
|     g = graphviz.Digraph() | ||||
|     g.node(str(0), 'input') | ||||
|     for v in range(1, num_vertices - 1): | ||||
|       g.node(str(v), self.ops[v]) | ||||
|     g.node(str(num_vertices - 1), 'output') | ||||
|  | ||||
|     for src in range(num_vertices - 1): | ||||
|       for dst in range(src + 1, num_vertices): | ||||
|         if self.matrix[src, dst]: | ||||
|           g.edge(str(src), str(dst)) | ||||
|  | ||||
|     return g | ||||
|  | ||||
|  | ||||
| def is_upper_triangular(matrix): | ||||
|   """True if matrix is 0 on diagonal and below.""" | ||||
|   for src in range(np.shape(matrix)[0]): | ||||
|     for dst in range(0, src + 1): | ||||
|       if matrix[src, dst] != 0: | ||||
|         return False | ||||
|  | ||||
|   return True | ||||
							
								
								
									
										361
									
								
								graph_dit/naswot/nasspace.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										361
									
								
								graph_dit/naswot/nasspace.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,361 @@ | ||||
| from models import get_cell_based_tiny_net, get_search_spaces | ||||
| from nas_201_api import NASBench201API as API | ||||
| from nasbench import api as nasbench101api | ||||
| from nas_101_api.model import Network | ||||
| from nas_101_api.model_spec import ModelSpec | ||||
| import itertools | ||||
| import random | ||||
| import numpy as np | ||||
| from models.cell_searchs.genotypes import Structure | ||||
| from copy import deepcopy | ||||
| from pycls.models.nas.nas import NetworkImageNet, NetworkCIFAR | ||||
| from pycls.models.anynet import AnyNet | ||||
| from pycls.models.nas.genotypes import GENOTYPES, Genotype | ||||
| import json | ||||
| import torch | ||||
|  | ||||
|  | ||||
| class Nasbench201: | ||||
|     def __init__(self, dataset, apiloc): | ||||
|         self.dataset = dataset | ||||
|         self.api = API(apiloc, verbose=False) | ||||
|         self.epochs = '12' | ||||
|     def get_network(self, uid): | ||||
|         #config = self.api.get_net_config(uid, self.dataset) | ||||
|         config = self.api.get_net_config(uid, 'cifar10-valid') | ||||
|         print(config) | ||||
|         config['num_classes'] = 1 | ||||
|         network = get_cell_based_tiny_net(config) | ||||
|         return network | ||||
|     def __iter__(self): | ||||
|         for uid in range(len(self)): | ||||
|             network = self.get_network(uid) | ||||
|             yield uid, network | ||||
|     def __getitem__(self, index): | ||||
|         return index | ||||
|     def __len__(self): | ||||
|         return 15625 | ||||
|     def num_activations(self): | ||||
|         network = self.get_network(0) | ||||
|         return network.classifier.in_features | ||||
|     #def get_12epoch_accuracy(self, uid, acc_type, trainval, traincifar10=False): | ||||
|     #    archinfo = self.api.query_meta_info_by_index(uid) | ||||
|     #    if (self.dataset == 'cifar10' or traincifar10) and trainval: | ||||
|     #        #return archinfo.get_metrics('cifar10-valid', acc_type, iepoch=12)['accuracy'] | ||||
|     #        return archinfo.get_metrics('cifar10-valid', 'x-valid', iepoch=12)['accuracy'] | ||||
|     #    elif traincifar10: | ||||
|     #        return archinfo.get_metrics('cifar10', acc_type, iepoch=12)['accuracy'] | ||||
|     #    else: | ||||
|     #        return archinfo.get_metrics(self.dataset, 'ori-test', iepoch=12)['accuracy'] | ||||
|     def get_12epoch_accuracy(self, uid, acc_type, trainval, traincifar10=False): | ||||
|         #archinfo = self.api.query_meta_info_by_index(uid) | ||||
|         #if (self.dataset == 'cifar10' and trainval) or traincifar10: | ||||
|         info = self.api.get_more_info(uid, 'cifar10-valid', iepoch=None, hp=self.epochs, is_random=True) | ||||
|         #else: | ||||
|         #    info = self.api.get_more_info(uid, self.dataset, iepoch=None, hp=self.epochs, is_random=True) | ||||
|         return info['valid-accuracy'] | ||||
|     def get_final_accuracy(self, uid, acc_type, trainval): | ||||
|         #archinfo = self.api.query_meta_info_by_index(uid) | ||||
|         if self.dataset == 'cifar10' and trainval: | ||||
|             info = self.api.query_meta_info_by_index(uid, hp='200').get_metrics('cifar10-valid', 'x-valid') | ||||
|             #info = self.api.query_by_index(uid, 'cifar10-valid', hp='200') | ||||
|             #info = self.api.get_more_info(uid, 'cifar10-valid', iepoch=None, hp='200', is_random=True) | ||||
|         else: | ||||
|             info = self.api.query_meta_info_by_index(uid, hp='200').get_metrics(self.dataset, acc_type) | ||||
|             #info = self.api.query_by_index(uid, self.dataset, hp='200') | ||||
|             #info = self.api.get_more_info(uid, self.dataset, iepoch=None, hp='200', is_random=True) | ||||
|         return info['accuracy'] | ||||
|         #return info['valid-accuracy'] | ||||
|         #if self.dataset == 'cifar10' and trainval: | ||||
|         #    return archinfo.get_metrics('cifar10-valid', acc_type, iepoch=11)['accuracy'] | ||||
|         #else: | ||||
|         #    #return archinfo.get_metrics(self.dataset, 'ori-test', iepoch=12)['accuracy'] | ||||
|         #    return archinfo.get_metrics(self.dataset, 'x-test', iepoch=11)['accuracy'] | ||||
|         ##dataset = self.dataset | ||||
|         ##if self.dataset == 'cifar10' and trainval: | ||||
|         ##    dataset = 'cifar10-valid' | ||||
|         ##archinfo = self.api.get_more_info(uid, dataset, iepoch=None, use_12epochs_result=True, is_random=True) | ||||
|         ##return archinfo['valid-accuracy'] | ||||
|  | ||||
|     def get_accuracy(self, uid, acc_type, trainval=True): | ||||
|         archinfo = self.api.query_meta_info_by_index(uid) | ||||
|         if self.dataset == 'cifar10' and trainval: | ||||
|             return archinfo.get_metrics('cifar10-valid', acc_type)['accuracy'] | ||||
|         else: | ||||
|             return archinfo.get_metrics(self.dataset, acc_type)['accuracy'] | ||||
|  | ||||
|     def get_accuracy_for_all_datasets(self, uid): | ||||
|         archinfo = self.api.query_meta_info_by_index(uid,hp='200') | ||||
|  | ||||
|         c10 = archinfo.get_metrics('cifar10', 'ori-test')['accuracy'] | ||||
|         c10_val = archinfo.get_metrics('cifar10-valid', 'x-valid')['accuracy'] | ||||
|  | ||||
|         c100 = archinfo.get_metrics('cifar100', 'x-test')['accuracy'] | ||||
|         c100_val = archinfo.get_metrics('cifar100', 'x-valid')['accuracy'] | ||||
|  | ||||
|         imagenet = archinfo.get_metrics('ImageNet16-120', 'x-test')['accuracy'] | ||||
|         imagenet_val = archinfo.get_metrics('ImageNet16-120', 'x-valid')['accuracy'] | ||||
|  | ||||
|         return c10, c10_val, c100, c100_val, imagenet, imagenet_val | ||||
|  | ||||
|     #def train_and_eval(self, arch, dataname, acc_type, trainval=True): | ||||
|     #    unique_hash = self.__getitem__(arch) | ||||
|     #    time = self.get_training_time(unique_hash) | ||||
|     #    acc12 = self.get_12epoch_accuracy(unique_hash, acc_type, trainval) | ||||
|     #    acc = self.get_final_accuracy(unique_hash, acc_type, trainval) | ||||
|     #    return acc12, acc, time | ||||
|     def train_and_eval(self, arch, dataname, acc_type, trainval=True, traincifar10=False): | ||||
|         unique_hash = self.__getitem__(arch) | ||||
|         time = self.get_training_time(unique_hash) | ||||
|         acc12 = self.get_12epoch_accuracy(unique_hash, acc_type, trainval, traincifar10) | ||||
|         acc = self.get_final_accuracy(unique_hash, acc_type, trainval) | ||||
|         return acc12, acc, time | ||||
|     def random_arch(self): | ||||
|         return random.randint(0, len(self)-1) | ||||
|     def get_training_time(self, unique_hash): | ||||
|         #info = self.api.get_more_info(unique_hash, 'cifar10-valid' if self.dataset == 'cifar10' else self.dataset, iepoch=None, use_12epochs_result=True, is_random=True) | ||||
|  | ||||
|  | ||||
|         #info = self.api.get_more_info(unique_hash, 'cifar10-valid', iepoch=None, use_12epochs_result=True, is_random=True) | ||||
|         info = self.api.get_more_info(unique_hash, 'cifar10-valid', iepoch=None, hp='12', is_random=True) | ||||
|         return info['train-all-time'] + info['valid-per-time'] | ||||
|         #if self.dataset == 'cifar10' and trainval: | ||||
|         #    info = self.api.get_more_info(unique_hash, 'cifar10-valid', iepoch=None, hp=self.epochs, is_random=True) | ||||
|         #else: | ||||
|         #    info = self.api.get_more_info(unique_hash, self.dataset, iepoch=None, hp=self.epochs, is_random=True) | ||||
|  | ||||
|         ##info = self.api.get_more_info(unique_hash, 'cifar10-valid', iepoch=None, use_12epochs_result=True, is_random=True) | ||||
|         #return info['train-all-time'] + info['valid-per-time'] | ||||
|     def mutate_arch(self, arch): | ||||
|         op_names = get_search_spaces('cell', 'nas-bench-201') | ||||
|         #config = self.api.get_net_config(arch, self.dataset) | ||||
|         config = self.api.get_net_config(arch, 'cifar10-valid') | ||||
|         parent_arch = Structure(self.api.str2lists(config['arch_str'])) | ||||
|         child_arch = deepcopy( parent_arch ) | ||||
|         node_id = random.randint(0, len(child_arch.nodes)-1) | ||||
|         node_info = list( child_arch.nodes[node_id] ) | ||||
|         snode_id = random.randint(0, len(node_info)-1) | ||||
|         xop = random.choice( op_names ) | ||||
|         while xop == node_info[snode_id][0]: | ||||
|           xop = random.choice( op_names ) | ||||
|         node_info[snode_id] = (xop, node_info[snode_id][1]) | ||||
|         child_arch.nodes[node_id] = tuple( node_info ) | ||||
|         arch_index = self.api.query_index_by_arch( child_arch ) | ||||
|         return arch_index | ||||
|  | ||||
| class Nasbench101: | ||||
|     def __init__(self, dataset, apiloc, args): | ||||
|         self.dataset = dataset | ||||
|         self.api = nasbench101api.NASBench(apiloc) | ||||
|         self.args = args | ||||
|     def get_accuracy(self, unique_hash, acc_type, trainval=True): | ||||
|         spec = self.get_spec(unique_hash) | ||||
|         _, stats = self.api.get_metrics_from_spec(spec) | ||||
|         maxacc = 0. | ||||
|         for ep in stats: | ||||
|             for statmap in stats[ep]: | ||||
|                 newacc = statmap['final_test_accuracy'] | ||||
|                 if newacc > maxacc: | ||||
|                     maxacc = newacc | ||||
|         return maxacc | ||||
|     def get_final_accuracy(self, uid, acc_type, trainval): | ||||
|         return self.get_accuracy(uid, acc_type, trainval) | ||||
|     def get_training_time(self, unique_hash): | ||||
|         spec = self.get_spec(unique_hash) | ||||
|         _, stats = self.api.get_metrics_from_spec(spec) | ||||
|         maxacc = -1. | ||||
|         maxtime = 0. | ||||
|         for ep in stats: | ||||
|             for statmap in stats[ep]: | ||||
|                 newacc = statmap['final_test_accuracy'] | ||||
|                 if newacc > maxacc: | ||||
|                     maxacc = newacc | ||||
|                     maxtime = statmap['final_training_time'] | ||||
|         return maxtime | ||||
|     def get_network(self, unique_hash): | ||||
|         spec = self.get_spec(unique_hash) | ||||
|         network = Network(spec, self.args) | ||||
|         return network | ||||
|     def get_spec(self, unique_hash): | ||||
|         matrix = self.api.fixed_statistics[unique_hash]['module_adjacency'] | ||||
|         operations = self.api.fixed_statistics[unique_hash]['module_operations'] | ||||
|         spec = ModelSpec(matrix, operations) | ||||
|         return spec | ||||
|     def __iter__(self): | ||||
|         for unique_hash in self.api.hash_iterator(): | ||||
|             network = self.get_network(unique_hash) | ||||
|             yield unique_hash, network | ||||
|     def __getitem__(self, index): | ||||
|         return next(itertools.islice(self.api.hash_iterator(), index, None)) | ||||
|     def __len__(self): | ||||
|         return len(self.api.hash_iterator()) | ||||
|     def num_activations(self): | ||||
|         for unique_hash in self.api.hash_iterator(): | ||||
|             network = self.get_network(unique_hash) | ||||
|             return network.classifier.in_features | ||||
|     def train_and_eval(self, arch, dataname, acc_type, trainval=True, traincifar10=False): | ||||
|         unique_hash = self.__getitem__(arch) | ||||
|         time =12.* self.get_training_time(unique_hash)/108. | ||||
|         acc = self.get_accuracy(unique_hash, acc_type, trainval) | ||||
|         return acc, acc, time | ||||
|     def random_arch(self): | ||||
|         return random.randint(0, len(self)-1) | ||||
|     def mutate_arch(self, arch): | ||||
|         unique_hash = self.__getitem__(arch) | ||||
|         matrix = self.api.fixed_statistics[unique_hash]['module_adjacency'] | ||||
|         operations = self.api.fixed_statistics[unique_hash]['module_operations'] | ||||
|         coords = [ (i, j) for i in range(matrix.shape[0]) for j in range(i+1, matrix.shape[1])] | ||||
|         random.shuffle(coords) | ||||
|         # loop through changes until we find change thats allowed | ||||
|         for i, j in coords: | ||||
|             # try the ops in a particular order | ||||
|             for k in [m for m in np.unique(matrix) if m != matrix[i, j]]: | ||||
|                 newmatrix = matrix.copy() | ||||
|                 newmatrix[i, j] = k | ||||
|                 spec = ModelSpec(newmatrix, operations) | ||||
|                 try: | ||||
|                     newhash = self.api._hash_spec(spec) | ||||
|                     if newhash in self.api.fixed_statistics: | ||||
|                         return [n for n, m in enumerate(self.api.fixed_statistics.keys()) if m == newhash][0] | ||||
|                 except: | ||||
|                     pass | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class ReturnFeatureLayer(torch.nn.Module): | ||||
|     def __init__(self, mod): | ||||
|         super(ReturnFeatureLayer, self).__init__() | ||||
|         self.mod = mod | ||||
|     def forward(self, x): | ||||
|         return self.mod(x), x | ||||
|                  | ||||
|  | ||||
| def return_feature_layer(network, prefix=''): | ||||
|     #for attr_str in dir(network): | ||||
|     #    target_attr = getattr(network, attr_str) | ||||
|     #    if isinstance(target_attr, torch.nn.Linear): | ||||
|     #        setattr(network, attr_str, ReturnFeatureLayer(target_attr)) | ||||
|     for n, ch in list(network.named_children()): | ||||
|         if isinstance(ch, torch.nn.Linear): | ||||
|             setattr(network, n, ReturnFeatureLayer(ch)) | ||||
|         else: | ||||
|             return_feature_layer(ch, prefix + '\t') | ||||
|               | ||||
|  | ||||
| class NDS: | ||||
|     def __init__(self, searchspace): | ||||
|         self.searchspace = searchspace | ||||
|         data = json.load(open(f'nds_data/{searchspace}.json', 'r')) | ||||
|         try: | ||||
|             data = data['top'] + data['mid'] | ||||
|         except Exception as e: | ||||
|             pass | ||||
|         self.data = data | ||||
|     def __iter__(self): | ||||
|         for unique_hash in range(len(self)): | ||||
|             network = self.get_network(unique_hash) | ||||
|             yield unique_hash, network | ||||
|     def get_network_config(self, uid): | ||||
|         return self.data[uid]['net'] | ||||
|     def get_network_optim_config(self, uid): | ||||
|         return self.data[uid]['optim'] | ||||
|     def get_network(self, uid): | ||||
|         netinfo = self.data[uid] | ||||
|         config = netinfo['net'] | ||||
|         #print(config) | ||||
|         if 'genotype' in config: | ||||
|             #print('geno') | ||||
|             gen = config['genotype'] | ||||
|             genotype = Genotype(normal=gen['normal'], normal_concat=gen['normal_concat'], reduce=gen['reduce'], reduce_concat=gen['reduce_concat']) | ||||
|             if '_in' in self.searchspace: | ||||
|                 network = NetworkImageNet(config['width'], 1, config['depth'], config['aux'],  genotype) | ||||
|             else: | ||||
|                 network = NetworkCIFAR(config['width'], 1, config['depth'], config['aux'],  genotype) | ||||
|             network.drop_path_prob = 0. | ||||
|             #print(config) | ||||
|             #print('genotype') | ||||
|             L = config['depth'] | ||||
|         else: | ||||
|             if 'bot_muls' in config and 'bms' not in config: | ||||
|                 config['bms'] = config['bot_muls'] | ||||
|                 del config['bot_muls'] | ||||
|             if 'num_gs' in config and 'gws' not in config: | ||||
|                 config['gws'] = config['num_gs'] | ||||
|                 del config['num_gs'] | ||||
|             config['nc'] = 1 | ||||
|             config['se_r'] = None | ||||
|             config['stem_w'] = 12 | ||||
|             L = sum(config['ds']) | ||||
|             if 'ResN' in self.searchspace: | ||||
|                 config['stem_type'] = 'res_stem_in' | ||||
|             else: | ||||
|                 config['stem_type'] = 'simple_stem_in' | ||||
|             #"res_stem_cifar": ResStemCifar, | ||||
|             #"res_stem_in": ResStemIN, | ||||
|             #"simple_stem_in": SimpleStemIN, | ||||
|             if config['block_type'] == 'double_plain_block': | ||||
|                 config['block_type'] = 'vanilla_block' | ||||
|             network = AnyNet(**config) | ||||
|         return_feature_layer(network) | ||||
|         return network | ||||
|     def __getitem__(self, index): | ||||
|         return index | ||||
|     def __len__(self): | ||||
|         return len(self.data) | ||||
|     def random_arch(self): | ||||
|         return random.randint(0, len(self.data)-1) | ||||
|     def get_final_accuracy(self, uid, acc_type, trainval): | ||||
|         return 100.-self.data[uid]['test_ep_top1'][-1] | ||||
|  | ||||
|  | ||||
| def get_search_space(args): | ||||
|     if args.nasspace == 'nasbench201': | ||||
|         return Nasbench201(args.dataset, args.api_loc) | ||||
|     elif args.nasspace == 'nasbench101': | ||||
|         return Nasbench101(args.dataset, args.api_loc, args) | ||||
|     elif args.nasspace == 'nds_resnet': | ||||
|         return NDS('ResNet') | ||||
|     elif args.nasspace == 'nds_amoeba': | ||||
|         return NDS('Amoeba') | ||||
|     elif args.nasspace == 'nds_amoeba_in': | ||||
|         return NDS('Amoeba_in') | ||||
|     elif args.nasspace == 'nds_darts_in': | ||||
|         return NDS('DARTS_in') | ||||
|     elif args.nasspace == 'nds_darts': | ||||
|         return NDS('DARTS') | ||||
|     elif args.nasspace == 'nds_darts_fix-w-d': | ||||
|         return NDS('DARTS_fix-w-d') | ||||
|     elif args.nasspace == 'nds_darts_lr-wd': | ||||
|         return NDS('DARTS_lr-wd') | ||||
|     elif args.nasspace == 'nds_enas': | ||||
|         return NDS('ENAS') | ||||
|     elif args.nasspace == 'nds_enas_in': | ||||
|         return NDS('ENAS_in') | ||||
|     elif args.nasspace == 'nds_enas_fix-w-d': | ||||
|         return NDS('ENAS_fix-w-d') | ||||
|     elif args.nasspace == 'nds_pnas': | ||||
|         return NDS('PNAS') | ||||
|     elif args.nasspace == 'nds_pnas_fix-w-d': | ||||
|         return NDS('PNAS_fix-w-d') | ||||
|     elif args.nasspace == 'nds_pnas_in': | ||||
|         return NDS('PNAS_in') | ||||
|     elif args.nasspace == 'nds_nasnet': | ||||
|         return NDS('NASNet') | ||||
|     elif args.nasspace == 'nds_nasnet_in': | ||||
|         return NDS('NASNet_in') | ||||
|     elif args.nasspace == 'nds_resnext-a': | ||||
|         return NDS('ResNeXt-A') | ||||
|     elif args.nasspace == 'nds_resnext-a_in': | ||||
|         return NDS('ResNeXt-A_in') | ||||
|     elif args.nasspace == 'nds_resnext-b': | ||||
|         return NDS('ResNeXt-B') | ||||
|     elif args.nasspace == 'nds_resnext-b_in': | ||||
|         return NDS('ResNeXt-B_in') | ||||
|     elif args.nasspace == 'nds_vanilla': | ||||
|         return NDS('Vanilla') | ||||
|     elif args.nasspace == 'nds_vanilla_lr-wd': | ||||
|         return NDS('Vanilla_lr-wd') | ||||
|     elif args.nasspace == 'nds_vanilla_lr-wd_in': | ||||
|         return NDS('Vanilla_lr-wd_in') | ||||
|  | ||||
							
								
								
									
										0
									
								
								graph_dit/naswot/pycls/core/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								graph_dit/naswot/pycls/core/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										136
									
								
								graph_dit/naswot/pycls/core/benchmark.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								graph_dit/naswot/pycls/core/benchmark.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """Benchmarking functions.""" | ||||
|  | ||||
| import pycls.core.logging as logging | ||||
| import pycls.datasets.loader as loader | ||||
| import torch | ||||
| from pycls.core.config import cfg | ||||
| from pycls.core.timer import Timer | ||||
|  | ||||
|  | ||||
| logger = logging.get_logger(__name__) | ||||
|  | ||||
|  | ||||
| @torch.no_grad() | ||||
| def compute_time_eval(model): | ||||
|     """Computes precise model forward test time using dummy data.""" | ||||
|     # Use eval mode | ||||
|     model.eval() | ||||
|     # Generate a dummy mini-batch and copy data to GPU | ||||
|     im_size, batch_size = cfg.TRAIN.IM_SIZE, int(cfg.TEST.BATCH_SIZE / cfg.NUM_GPUS) | ||||
|     if cfg.TASK == "jig": | ||||
|         inputs = torch.rand(batch_size, cfg.JIGSAW_GRID ** 2, cfg.MODEL.INPUT_CHANNELS, im_size, im_size).cuda(non_blocking=False) | ||||
|     else: | ||||
|         inputs = torch.zeros(batch_size, cfg.MODEL.INPUT_CHANNELS, im_size, im_size).cuda(non_blocking=False) | ||||
|     # Compute precise forward pass time | ||||
|     timer = Timer() | ||||
|     total_iter = cfg.PREC_TIME.NUM_ITER + cfg.PREC_TIME.WARMUP_ITER | ||||
|     for cur_iter in range(total_iter): | ||||
|         # Reset the timers after the warmup phase | ||||
|         if cur_iter == cfg.PREC_TIME.WARMUP_ITER: | ||||
|             timer.reset() | ||||
|         # Forward | ||||
|         timer.tic() | ||||
|         model(inputs) | ||||
|         torch.cuda.synchronize() | ||||
|         timer.toc() | ||||
|     return timer.average_time | ||||
|  | ||||
|  | ||||
| def compute_time_train(model, loss_fun): | ||||
|     """Computes precise model forward + backward time using dummy data.""" | ||||
|     # Use train mode | ||||
|     model.train() | ||||
|     # Generate a dummy mini-batch and copy data to GPU | ||||
|     im_size, batch_size = cfg.TRAIN.IM_SIZE, int(cfg.TRAIN.BATCH_SIZE / cfg.NUM_GPUS) | ||||
|     if cfg.TASK == "jig": | ||||
|         inputs = torch.rand(batch_size, cfg.JIGSAW_GRID ** 2, cfg.MODEL.INPUT_CHANNELS, im_size, im_size).cuda(non_blocking=False) | ||||
|     else: | ||||
|         inputs = torch.rand(batch_size, cfg.MODEL.INPUT_CHANNELS, im_size, im_size).cuda(non_blocking=False) | ||||
|     if cfg.TASK in ['col', 'seg']: | ||||
|         labels = torch.zeros(batch_size, im_size, im_size, dtype=torch.int64).cuda(non_blocking=False) | ||||
|     else: | ||||
|         labels = torch.zeros(batch_size, dtype=torch.int64).cuda(non_blocking=False) | ||||
|     # Cache BatchNorm2D running stats | ||||
|     bns = [m for m in model.modules() if isinstance(m, torch.nn.BatchNorm2d)] | ||||
|     bn_stats = [[bn.running_mean.clone(), bn.running_var.clone()] for bn in bns] | ||||
|     # Compute precise forward backward pass time | ||||
|     fw_timer, bw_timer = Timer(), Timer() | ||||
|     total_iter = cfg.PREC_TIME.NUM_ITER + cfg.PREC_TIME.WARMUP_ITER | ||||
|     for cur_iter in range(total_iter): | ||||
|         # Reset the timers after the warmup phase | ||||
|         if cur_iter == cfg.PREC_TIME.WARMUP_ITER: | ||||
|             fw_timer.reset() | ||||
|             bw_timer.reset() | ||||
|         # Forward | ||||
|         fw_timer.tic() | ||||
|         preds = model(inputs) | ||||
|         if isinstance(preds, tuple): | ||||
|             loss = loss_fun(preds[0], labels) + cfg.NAS.AUX_WEIGHT * loss_fun(preds[1], labels) | ||||
|             preds = preds[0] | ||||
|         else: | ||||
|             loss = loss_fun(preds, labels) | ||||
|         torch.cuda.synchronize() | ||||
|         fw_timer.toc() | ||||
|         # Backward | ||||
|         bw_timer.tic() | ||||
|         loss.backward() | ||||
|         torch.cuda.synchronize() | ||||
|         bw_timer.toc() | ||||
|     # Restore BatchNorm2D running stats | ||||
|     for bn, (mean, var) in zip(bns, bn_stats): | ||||
|         bn.running_mean, bn.running_var = mean, var | ||||
|     return fw_timer.average_time, bw_timer.average_time | ||||
|  | ||||
|  | ||||
| def compute_time_loader(data_loader): | ||||
|     """Computes loader time.""" | ||||
|     timer = Timer() | ||||
|     loader.shuffle(data_loader, 0) | ||||
|     data_loader_iterator = iter(data_loader) | ||||
|     total_iter = cfg.PREC_TIME.NUM_ITER + cfg.PREC_TIME.WARMUP_ITER | ||||
|     total_iter = min(total_iter, len(data_loader)) | ||||
|     for cur_iter in range(total_iter): | ||||
|         if cur_iter == cfg.PREC_TIME.WARMUP_ITER: | ||||
|             timer.reset() | ||||
|         timer.tic() | ||||
|         next(data_loader_iterator) | ||||
|         timer.toc() | ||||
|     return timer.average_time | ||||
|  | ||||
|  | ||||
| def compute_time_full(model, loss_fun, train_loader, test_loader): | ||||
|     """Times model and data loader.""" | ||||
|     logger.info("Computing model and loader timings...") | ||||
|     # Compute timings | ||||
|     test_fw_time = compute_time_eval(model) | ||||
|     train_fw_time, train_bw_time = compute_time_train(model, loss_fun) | ||||
|     train_fw_bw_time = train_fw_time + train_bw_time | ||||
|     train_loader_time = compute_time_loader(train_loader) | ||||
|     # Output iter timing | ||||
|     iter_times = { | ||||
|         "test_fw_time": test_fw_time, | ||||
|         "train_fw_time": train_fw_time, | ||||
|         "train_bw_time": train_bw_time, | ||||
|         "train_fw_bw_time": train_fw_bw_time, | ||||
|         "train_loader_time": train_loader_time, | ||||
|     } | ||||
|     logger.info(logging.dump_log_data(iter_times, "iter_times")) | ||||
|     # Output epoch timing | ||||
|     epoch_times = { | ||||
|         "test_fw_time": test_fw_time * len(test_loader), | ||||
|         "train_fw_time": train_fw_time * len(train_loader), | ||||
|         "train_bw_time": train_bw_time * len(train_loader), | ||||
|         "train_fw_bw_time": train_fw_bw_time * len(train_loader), | ||||
|         "train_loader_time": train_loader_time * len(train_loader), | ||||
|     } | ||||
|     logger.info(logging.dump_log_data(epoch_times, "epoch_times")) | ||||
|     # Compute data loader overhead (assuming DATA_LOADER.NUM_WORKERS>1) | ||||
|     overhead = max(0, train_loader_time - train_fw_bw_time) / train_fw_bw_time | ||||
|     logger.info("Overhead of data loader is {:.2f}%".format(overhead * 100)) | ||||
							
								
								
									
										88
									
								
								graph_dit/naswot/pycls/core/builders.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								graph_dit/naswot/pycls/core/builders.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """Model and loss construction functions.""" | ||||
|  | ||||
| import torch | ||||
| from pycls.core.config import cfg | ||||
| from pycls.models.anynet import AnyNet | ||||
| from pycls.models.effnet import EffNet | ||||
| from pycls.models.regnet import RegNet | ||||
| from pycls.models.resnet import ResNet | ||||
| from pycls.models.nas.nas import NAS | ||||
| from pycls.models.nas.nas_search import NAS_Search | ||||
| from pycls.models.nas_bench.model_builder import NAS_Bench | ||||
|  | ||||
|  | ||||
| class LabelSmoothedCrossEntropyLoss(torch.nn.Module): | ||||
|     """CrossEntropyLoss with label smoothing.""" | ||||
|     def __init__(self): | ||||
|         super(LabelSmoothedCrossEntropyLoss, self).__init__() | ||||
|         self.eps = cfg.MODEL.LABEL_SMOOTHING_EPS | ||||
|         self.num_classes = cfg.MODEL.NUM_CLASSES | ||||
|  | ||||
|     def forward(self, logits, target): | ||||
|         pred = logits.log_softmax(dim=-1) | ||||
|         with torch.no_grad(): | ||||
|             target_dist = torch.ones_like(pred) * self.eps / (self.num_classes - 1) | ||||
|             target_dist.scatter_(-1, target.unsqueeze(-1), 1 - self.eps) | ||||
|         return (-target_dist * pred).sum(dim=-1).mean() | ||||
|  | ||||
|  | ||||
| # Supported models | ||||
| _models = { | ||||
|     "anynet": AnyNet, | ||||
|     "effnet": EffNet, | ||||
|     "resnet": ResNet, | ||||
|     "regnet": RegNet, | ||||
|     "nas": NAS, | ||||
|     "nas_search": NAS_Search, | ||||
|     "nas_bench": NAS_Bench, | ||||
| } | ||||
|  | ||||
| # Supported loss functions | ||||
| _loss_funs = { | ||||
|     "cross_entropy": torch.nn.CrossEntropyLoss, | ||||
|     "label_smoothed_cross_entropy": LabelSmoothedCrossEntropyLoss, | ||||
| } | ||||
|  | ||||
|  | ||||
| def get_model(): | ||||
|     """Gets the model class specified in the config.""" | ||||
|     err_str = "Model type '{}' not supported" | ||||
|     assert cfg.MODEL.TYPE in _models.keys(), err_str.format(cfg.MODEL.TYPE) | ||||
|     return _models[cfg.MODEL.TYPE] | ||||
|  | ||||
|  | ||||
| def get_loss_fun(): | ||||
|     """Gets the loss function class specified in the config.""" | ||||
|     err_str = "Loss function type '{}' not supported" | ||||
|     assert cfg.MODEL.LOSS_FUN in _loss_funs.keys(), err_str.format(cfg.TRAIN.LOSS) | ||||
|     return _loss_funs[cfg.MODEL.LOSS_FUN] | ||||
|  | ||||
|  | ||||
| def build_model(): | ||||
|     """Builds the model.""" | ||||
|     return get_model()() | ||||
|  | ||||
|  | ||||
| def build_loss_fun(): | ||||
|     """Build the loss function.""" | ||||
|     if cfg.TASK == "seg": | ||||
|         return get_loss_fun()(ignore_index=255) | ||||
|     else: | ||||
|         return get_loss_fun()() | ||||
|  | ||||
|  | ||||
| def register_model(name, ctor): | ||||
|     """Registers a model dynamically.""" | ||||
|     _models[name] = ctor | ||||
|  | ||||
|  | ||||
| def register_loss_fun(name, ctor): | ||||
|     """Registers a loss function dynamically.""" | ||||
|     _loss_funs[name] = ctor | ||||
							
								
								
									
										98
									
								
								graph_dit/naswot/pycls/core/checkpoint.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								graph_dit/naswot/pycls/core/checkpoint.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """Functions that handle saving and loading of checkpoints.""" | ||||
|  | ||||
| import os | ||||
|  | ||||
| import pycls.core.distributed as dist | ||||
| import torch | ||||
| from pycls.core.config import cfg | ||||
|  | ||||
|  | ||||
| # Common prefix for checkpoint file names | ||||
| _NAME_PREFIX = "model_epoch_" | ||||
| # Checkpoints directory name | ||||
| _DIR_NAME = "checkpoints" | ||||
|  | ||||
|  | ||||
| def get_checkpoint_dir(): | ||||
|     """Retrieves the location for storing checkpoints.""" | ||||
|     return os.path.join(cfg.OUT_DIR, _DIR_NAME) | ||||
|  | ||||
|  | ||||
| def get_checkpoint(epoch): | ||||
|     """Retrieves the path to a checkpoint file.""" | ||||
|     name = "{}{:04d}.pyth".format(_NAME_PREFIX, epoch) | ||||
|     return os.path.join(get_checkpoint_dir(), name) | ||||
|  | ||||
|  | ||||
| def get_last_checkpoint(): | ||||
|     """Retrieves the most recent checkpoint (highest epoch number).""" | ||||
|     checkpoint_dir = get_checkpoint_dir() | ||||
|     # Checkpoint file names are in lexicographic order | ||||
|     checkpoints = [f for f in os.listdir(checkpoint_dir) if _NAME_PREFIX in f] | ||||
|     last_checkpoint_name = sorted(checkpoints)[-1] | ||||
|     return os.path.join(checkpoint_dir, last_checkpoint_name) | ||||
|  | ||||
|  | ||||
| def has_checkpoint(): | ||||
|     """Determines if there are checkpoints available.""" | ||||
|     checkpoint_dir = get_checkpoint_dir() | ||||
|     if not os.path.exists(checkpoint_dir): | ||||
|         return False | ||||
|     return any(_NAME_PREFIX in f for f in os.listdir(checkpoint_dir)) | ||||
|  | ||||
|  | ||||
| def save_checkpoint(model, optimizer, epoch): | ||||
|     """Saves a checkpoint.""" | ||||
|     # Save checkpoints only from the master process | ||||
|     if not dist.is_master_proc(): | ||||
|         return | ||||
|     # Ensure that the checkpoint dir exists | ||||
|     os.makedirs(get_checkpoint_dir(), exist_ok=True) | ||||
|     # Omit the DDP wrapper in the multi-gpu setting | ||||
|     sd = model.module.state_dict() if cfg.NUM_GPUS > 1 else model.state_dict() | ||||
|     # Record the state | ||||
|     if isinstance(optimizer, list): | ||||
|         checkpoint = { | ||||
|             "epoch": epoch, | ||||
|             "model_state": sd, | ||||
|             "optimizer_w_state": optimizer[0].state_dict(), | ||||
|             "optimizer_a_state": optimizer[1].state_dict(), | ||||
|             "cfg": cfg.dump(), | ||||
|         } | ||||
|     else: | ||||
|         checkpoint = { | ||||
|             "epoch": epoch, | ||||
|             "model_state": sd, | ||||
|             "optimizer_state": optimizer.state_dict(), | ||||
|             "cfg": cfg.dump(), | ||||
|         } | ||||
|     # Write the checkpoint | ||||
|     checkpoint_file = get_checkpoint(epoch + 1) | ||||
|     torch.save(checkpoint, checkpoint_file) | ||||
|     return checkpoint_file | ||||
|  | ||||
|  | ||||
| def load_checkpoint(checkpoint_file, model, optimizer=None): | ||||
|     """Loads the checkpoint from the given file.""" | ||||
|     err_str = "Checkpoint '{}' not found" | ||||
|     assert os.path.exists(checkpoint_file), err_str.format(checkpoint_file) | ||||
|     # Load the checkpoint on CPU to avoid GPU mem spike | ||||
|     checkpoint = torch.load(checkpoint_file, map_location="cpu") | ||||
|     # Account for the DDP wrapper in the multi-gpu setting | ||||
|     ms = model.module if cfg.NUM_GPUS > 1 else model | ||||
|     ms.load_state_dict(checkpoint["model_state"]) | ||||
|     # Load the optimizer state (commonly not done when fine-tuning) | ||||
|     if optimizer: | ||||
|         if isinstance(optimizer, list): | ||||
|             optimizer[0].load_state_dict(checkpoint["optimizer_w_state"]) | ||||
|             optimizer[1].load_state_dict(checkpoint["optimizer_a_state"]) | ||||
|         else: | ||||
|             optimizer.load_state_dict(checkpoint["optimizer_state"]) | ||||
|     return checkpoint["epoch"] | ||||
							
								
								
									
										500
									
								
								graph_dit/naswot/pycls/core/config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										500
									
								
								graph_dit/naswot/pycls/core/config.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,500 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """Configuration file (powered by YACS).""" | ||||
|  | ||||
| import argparse | ||||
| import os | ||||
| import sys | ||||
|  | ||||
| from pycls.core.io import cache_url | ||||
| from yacs.config import CfgNode as CfgNode | ||||
|  | ||||
|  | ||||
| # Global config object | ||||
| _C = CfgNode() | ||||
|  | ||||
| # Example usage: | ||||
| #   from core.config import cfg | ||||
| cfg = _C | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| # Model options | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| _C.MODEL = CfgNode() | ||||
|  | ||||
| # Model type | ||||
| _C.MODEL.TYPE = "" | ||||
|  | ||||
| # Number of weight layers | ||||
| _C.MODEL.DEPTH = 0 | ||||
|  | ||||
| # Number of input channels | ||||
| _C.MODEL.INPUT_CHANNELS = 3 | ||||
|  | ||||
| # Number of classes | ||||
| _C.MODEL.NUM_CLASSES = 10 | ||||
|  | ||||
| # Loss function (see pycls/core/builders.py for options) | ||||
| _C.MODEL.LOSS_FUN = "cross_entropy" | ||||
|  | ||||
| # Label smoothing eps | ||||
| _C.MODEL.LABEL_SMOOTHING_EPS = 0.0 | ||||
|  | ||||
| # ASPP channels | ||||
| _C.MODEL.ASPP_CHANNELS = 256 | ||||
|  | ||||
| # ASPP dilation rates | ||||
| _C.MODEL.ASPP_RATES = [6, 12, 18] | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| # ResNet options | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| _C.RESNET = CfgNode() | ||||
|  | ||||
| # Transformation function (see pycls/models/resnet.py for options) | ||||
| _C.RESNET.TRANS_FUN = "basic_transform" | ||||
|  | ||||
| # Number of groups to use (1 -> ResNet; > 1 -> ResNeXt) | ||||
| _C.RESNET.NUM_GROUPS = 1 | ||||
|  | ||||
| # Width of each group (64 -> ResNet; 4 -> ResNeXt) | ||||
| _C.RESNET.WIDTH_PER_GROUP = 64 | ||||
|  | ||||
| # Apply stride to 1x1 conv (True -> MSRA; False -> fb.torch) | ||||
| _C.RESNET.STRIDE_1X1 = True | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| # AnyNet options | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| _C.ANYNET = CfgNode() | ||||
|  | ||||
| # Stem type | ||||
| _C.ANYNET.STEM_TYPE = "simple_stem_in" | ||||
|  | ||||
| # Stem width | ||||
| _C.ANYNET.STEM_W = 32 | ||||
|  | ||||
| # Block type | ||||
| _C.ANYNET.BLOCK_TYPE = "res_bottleneck_block" | ||||
|  | ||||
| # Depth for each stage (number of blocks in the stage) | ||||
| _C.ANYNET.DEPTHS = [] | ||||
|  | ||||
| # Width for each stage (width of each block in the stage) | ||||
| _C.ANYNET.WIDTHS = [] | ||||
|  | ||||
| # Strides for each stage (applies to the first block of each stage) | ||||
| _C.ANYNET.STRIDES = [] | ||||
|  | ||||
| # Bottleneck multipliers for each stage (applies to bottleneck block) | ||||
| _C.ANYNET.BOT_MULS = [] | ||||
|  | ||||
| # Group widths for each stage (applies to bottleneck block) | ||||
| _C.ANYNET.GROUP_WS = [] | ||||
|  | ||||
| # Whether SE is enabled for res_bottleneck_block | ||||
| _C.ANYNET.SE_ON = False | ||||
|  | ||||
| # SE ratio | ||||
| _C.ANYNET.SE_R = 0.25 | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| # RegNet options | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| _C.REGNET = CfgNode() | ||||
|  | ||||
| # Stem type | ||||
| _C.REGNET.STEM_TYPE = "simple_stem_in" | ||||
|  | ||||
| # Stem width | ||||
| _C.REGNET.STEM_W = 32 | ||||
|  | ||||
| # Block type | ||||
| _C.REGNET.BLOCK_TYPE = "res_bottleneck_block" | ||||
|  | ||||
| # Stride of each stage | ||||
| _C.REGNET.STRIDE = 2 | ||||
|  | ||||
| # Squeeze-and-Excitation (RegNetY) | ||||
| _C.REGNET.SE_ON = False | ||||
| _C.REGNET.SE_R = 0.25 | ||||
|  | ||||
| # Depth | ||||
| _C.REGNET.DEPTH = 10 | ||||
|  | ||||
| # Initial width | ||||
| _C.REGNET.W0 = 32 | ||||
|  | ||||
| # Slope | ||||
| _C.REGNET.WA = 5.0 | ||||
|  | ||||
| # Quantization | ||||
| _C.REGNET.WM = 2.5 | ||||
|  | ||||
| # Group width | ||||
| _C.REGNET.GROUP_W = 16 | ||||
|  | ||||
| # Bottleneck multiplier (bm = 1 / b from the paper) | ||||
| _C.REGNET.BOT_MUL = 1.0 | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| # EfficientNet options | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| _C.EN = CfgNode() | ||||
|  | ||||
| # Stem width | ||||
| _C.EN.STEM_W = 32 | ||||
|  | ||||
| # Depth for each stage (number of blocks in the stage) | ||||
| _C.EN.DEPTHS = [] | ||||
|  | ||||
| # Width for each stage (width of each block in the stage) | ||||
| _C.EN.WIDTHS = [] | ||||
|  | ||||
| # Expansion ratios for MBConv blocks in each stage | ||||
| _C.EN.EXP_RATIOS = [] | ||||
|  | ||||
| # Squeeze-and-Excitation (SE) ratio | ||||
| _C.EN.SE_R = 0.25 | ||||
|  | ||||
| # Strides for each stage (applies to the first block of each stage) | ||||
| _C.EN.STRIDES = [] | ||||
|  | ||||
| # Kernel sizes for each stage | ||||
| _C.EN.KERNELS = [] | ||||
|  | ||||
| # Head width | ||||
| _C.EN.HEAD_W = 1280 | ||||
|  | ||||
| # Drop connect ratio | ||||
| _C.EN.DC_RATIO = 0.0 | ||||
|  | ||||
| # Dropout ratio | ||||
| _C.EN.DROPOUT_RATIO = 0.0 | ||||
|  | ||||
|  | ||||
| # ---------------------------------------------------------------------------- # | ||||
| # NAS options | ||||
| # ---------------------------------------------------------------------------- # | ||||
| _C.NAS = CfgNode() | ||||
|  | ||||
| # Cell genotype | ||||
| _C.NAS.GENOTYPE = 'nas' | ||||
|  | ||||
| # Custom genotype | ||||
| _C.NAS.CUSTOM_GENOTYPE = [] | ||||
|  | ||||
| # Base NAS width | ||||
| _C.NAS.WIDTH = 16 | ||||
|  | ||||
| # Total number of cells | ||||
| _C.NAS.DEPTH = 20 | ||||
|  | ||||
| # Auxiliary heads | ||||
| _C.NAS.AUX = False | ||||
|  | ||||
| # Weight for auxiliary heads | ||||
| _C.NAS.AUX_WEIGHT = 0.4 | ||||
|  | ||||
| # Drop path probability | ||||
| _C.NAS.DROP_PROB = 0.0 | ||||
|  | ||||
| # Matrix in NAS Bench | ||||
| _C.NAS.MATRIX = [] | ||||
|  | ||||
| # Operations in NAS Bench | ||||
| _C.NAS.OPS = [] | ||||
|  | ||||
| # Number of stacks in NAS Bench | ||||
| _C.NAS.NUM_STACKS = 3 | ||||
|  | ||||
| # Number of modules per stack in NAS Bench | ||||
| _C.NAS.NUM_MODULES_PER_STACK = 3 | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| # Batch norm options | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| _C.BN = CfgNode() | ||||
|  | ||||
| # BN epsilon | ||||
| _C.BN.EPS = 1e-5 | ||||
|  | ||||
| # BN momentum (BN momentum in PyTorch = 1 - BN momentum in Caffe2) | ||||
| _C.BN.MOM = 0.1 | ||||
|  | ||||
| # Precise BN stats | ||||
| _C.BN.USE_PRECISE_STATS = False | ||||
| _C.BN.NUM_SAMPLES_PRECISE = 1024 | ||||
|  | ||||
| # Initialize the gamma of the final BN of each block to zero | ||||
| _C.BN.ZERO_INIT_FINAL_GAMMA = False | ||||
|  | ||||
| # Use a different weight decay for BN layers | ||||
| _C.BN.USE_CUSTOM_WEIGHT_DECAY = False | ||||
| _C.BN.CUSTOM_WEIGHT_DECAY = 0.0 | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| # Optimizer options | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| _C.OPTIM = CfgNode() | ||||
|  | ||||
| # Base learning rate | ||||
| _C.OPTIM.BASE_LR = 0.1 | ||||
|  | ||||
| # Learning rate policy select from {'cos', 'exp', 'steps'} | ||||
| _C.OPTIM.LR_POLICY = "cos" | ||||
|  | ||||
| # Exponential decay factor | ||||
| _C.OPTIM.GAMMA = 0.1 | ||||
|  | ||||
| # Steps for 'steps' policy (in epochs) | ||||
| _C.OPTIM.STEPS = [] | ||||
|  | ||||
| # Learning rate multiplier for 'steps' policy | ||||
| _C.OPTIM.LR_MULT = 0.1 | ||||
|  | ||||
| # Maximal number of epochs | ||||
| _C.OPTIM.MAX_EPOCH = 200 | ||||
|  | ||||
| # Momentum | ||||
| _C.OPTIM.MOMENTUM = 0.9 | ||||
|  | ||||
| # Momentum dampening | ||||
| _C.OPTIM.DAMPENING = 0.0 | ||||
|  | ||||
| # Nesterov momentum | ||||
| _C.OPTIM.NESTEROV = True | ||||
|  | ||||
| # L2 regularization | ||||
| _C.OPTIM.WEIGHT_DECAY = 5e-4 | ||||
|  | ||||
| # Start the warm up from OPTIM.BASE_LR * OPTIM.WARMUP_FACTOR | ||||
| _C.OPTIM.WARMUP_FACTOR = 0.1 | ||||
|  | ||||
| # Gradually warm up the OPTIM.BASE_LR over this number of epochs | ||||
| _C.OPTIM.WARMUP_EPOCHS = 0 | ||||
|  | ||||
| # Update the learning rate per iter | ||||
| _C.OPTIM.ITER_LR = False | ||||
|  | ||||
| # Base learning rate for arch | ||||
| _C.OPTIM.ARCH_BASE_LR = 0.0003 | ||||
|  | ||||
| # L2 regularization for arch | ||||
| _C.OPTIM.ARCH_WEIGHT_DECAY = 0.001 | ||||
|  | ||||
| # Optimizer for arch | ||||
| _C.OPTIM.ARCH_OPTIM = 'adam' | ||||
|  | ||||
| # Epoch to start optimizing arch | ||||
| _C.OPTIM.ARCH_EPOCH = 0.0 | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| # Training options | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| _C.TRAIN = CfgNode() | ||||
|  | ||||
| # Dataset and split | ||||
| _C.TRAIN.DATASET = "" | ||||
| _C.TRAIN.SPLIT = "train" | ||||
|  | ||||
| # Total mini-batch size | ||||
| _C.TRAIN.BATCH_SIZE = 128 | ||||
|  | ||||
| # Image size | ||||
| _C.TRAIN.IM_SIZE = 224 | ||||
|  | ||||
| # Evaluate model on test data every eval period epochs | ||||
| _C.TRAIN.EVAL_PERIOD = 1 | ||||
|  | ||||
| # Save model checkpoint every checkpoint period epochs | ||||
| _C.TRAIN.CHECKPOINT_PERIOD = 1 | ||||
|  | ||||
| # Resume training from the latest checkpoint in the output directory | ||||
| _C.TRAIN.AUTO_RESUME = True | ||||
|  | ||||
| # Weights to start training from | ||||
| _C.TRAIN.WEIGHTS = "" | ||||
|  | ||||
| # Percentage of gray images in jig | ||||
| _C.TRAIN.GRAY_PERCENTAGE = 0.0 | ||||
|  | ||||
| # Portion to create trainA/trainB split | ||||
| _C.TRAIN.PORTION = 1.0 | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| # Testing options | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| _C.TEST = CfgNode() | ||||
|  | ||||
| # Dataset and split | ||||
| _C.TEST.DATASET = "" | ||||
| _C.TEST.SPLIT = "val" | ||||
|  | ||||
| # Total mini-batch size | ||||
| _C.TEST.BATCH_SIZE = 200 | ||||
|  | ||||
| # Image size | ||||
| _C.TEST.IM_SIZE = 256 | ||||
|  | ||||
| # Weights to use for testing | ||||
| _C.TEST.WEIGHTS = "" | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| # Common train/test data loader options | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| _C.DATA_LOADER = CfgNode() | ||||
|  | ||||
| # Number of data loader workers per process | ||||
| _C.DATA_LOADER.NUM_WORKERS = 8 | ||||
|  | ||||
| # Load data to pinned host memory | ||||
| _C.DATA_LOADER.PIN_MEMORY = True | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| # Memory options | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| _C.MEM = CfgNode() | ||||
|  | ||||
| # Perform ReLU inplace | ||||
| _C.MEM.RELU_INPLACE = True | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| # CUDNN options | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| _C.CUDNN = CfgNode() | ||||
|  | ||||
| # Perform benchmarking to select the fastest CUDNN algorithms to use | ||||
| # Note that this may increase the memory usage and will likely not result | ||||
| # in overall speedups when variable size inputs are used (e.g. COCO training) | ||||
| _C.CUDNN.BENCHMARK = True | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| # Precise timing options | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| _C.PREC_TIME = CfgNode() | ||||
|  | ||||
| # Number of iterations to warm up the caches | ||||
| _C.PREC_TIME.WARMUP_ITER = 3 | ||||
|  | ||||
| # Number of iterations to compute avg time | ||||
| _C.PREC_TIME.NUM_ITER = 30 | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| # Misc options | ||||
| # ------------------------------------------------------------------------------------ # | ||||
|  | ||||
| # Number of GPUs to use (applies to both training and testing) | ||||
| _C.NUM_GPUS = 1 | ||||
|  | ||||
| # Task (cls, seg, rot, col, jig) | ||||
| _C.TASK = "cls" | ||||
|  | ||||
| # Grid in Jigsaw (2, 3); no effect if TASK is not jig | ||||
| _C.JIGSAW_GRID = 3 | ||||
|  | ||||
| # Output directory | ||||
| _C.OUT_DIR = "/tmp" | ||||
|  | ||||
| # Config destination (in OUT_DIR) | ||||
| _C.CFG_DEST = "config.yaml" | ||||
|  | ||||
| # Note that non-determinism may still be present due to non-deterministic | ||||
| # operator implementations in GPU operator libraries | ||||
| _C.RNG_SEED = 1 | ||||
|  | ||||
| # Log destination ('stdout' or 'file') | ||||
| _C.LOG_DEST = "stdout" | ||||
|  | ||||
| # Log period in iters | ||||
| _C.LOG_PERIOD = 10 | ||||
|  | ||||
| # Distributed backend | ||||
| _C.DIST_BACKEND = "nccl" | ||||
|  | ||||
| # Hostname and port for initializing multi-process groups | ||||
| _C.HOST = "localhost" | ||||
| _C.PORT = 10001 | ||||
|  | ||||
| # Models weights referred to by URL are downloaded to this local cache | ||||
| _C.DOWNLOAD_CACHE = "/tmp/pycls-download-cache" | ||||
|  | ||||
|  | ||||
| # ------------------------------------------------------------------------------------ # | ||||
| # Deprecated keys | ||||
| # ------------------------------------------------------------------------------------ # | ||||
|  | ||||
| _C.register_deprecated_key("PREC_TIME.BATCH_SIZE") | ||||
| _C.register_deprecated_key("PREC_TIME.ENABLED") | ||||
|  | ||||
|  | ||||
| def assert_and_infer_cfg(cache_urls=True): | ||||
|     """Checks config values invariants.""" | ||||
|     err_str = "The first lr step must start at 0" | ||||
|     assert not _C.OPTIM.STEPS or _C.OPTIM.STEPS[0] == 0, err_str | ||||
|     data_splits = ["train", "val", "test"] | ||||
|     err_str = "Data split '{}' not supported" | ||||
|     assert _C.TRAIN.SPLIT in data_splits, err_str.format(_C.TRAIN.SPLIT) | ||||
|     assert _C.TEST.SPLIT in data_splits, err_str.format(_C.TEST.SPLIT) | ||||
|     err_str = "Mini-batch size should be a multiple of NUM_GPUS." | ||||
|     assert _C.TRAIN.BATCH_SIZE % _C.NUM_GPUS == 0, err_str | ||||
|     assert _C.TEST.BATCH_SIZE % _C.NUM_GPUS == 0, err_str | ||||
|     err_str = "Precise BN stats computation not verified for > 1 GPU" | ||||
|     assert not _C.BN.USE_PRECISE_STATS or _C.NUM_GPUS == 1, err_str | ||||
|     err_str = "Log destination '{}' not supported" | ||||
|     assert _C.LOG_DEST in ["stdout", "file"], err_str.format(_C.LOG_DEST) | ||||
|     if cache_urls: | ||||
|         cache_cfg_urls() | ||||
|  | ||||
|  | ||||
| def cache_cfg_urls(): | ||||
|     """Download URLs in config, cache them, and rewrite cfg to use cached file.""" | ||||
|     _C.TRAIN.WEIGHTS = cache_url(_C.TRAIN.WEIGHTS, _C.DOWNLOAD_CACHE) | ||||
|     _C.TEST.WEIGHTS = cache_url(_C.TEST.WEIGHTS, _C.DOWNLOAD_CACHE) | ||||
|  | ||||
|  | ||||
| def dump_cfg(): | ||||
|     """Dumps the config to the output directory.""" | ||||
|     cfg_file = os.path.join(_C.OUT_DIR, _C.CFG_DEST) | ||||
|     with open(cfg_file, "w") as f: | ||||
|         _C.dump(stream=f) | ||||
|  | ||||
|  | ||||
| def load_cfg(out_dir, cfg_dest="config.yaml"): | ||||
|     """Loads config from specified output directory.""" | ||||
|     cfg_file = os.path.join(out_dir, cfg_dest) | ||||
|     _C.merge_from_file(cfg_file) | ||||
|  | ||||
|  | ||||
| def load_cfg_fom_args(description="Config file options."): | ||||
|     """Load config from command line arguments and set any specified options.""" | ||||
|     parser = argparse.ArgumentParser(description=description) | ||||
|     help_s = "Config file location" | ||||
|     parser.add_argument("--cfg", dest="cfg_file", help=help_s, required=True, type=str) | ||||
|     help_s = "See pycls/core/config.py for all options" | ||||
|     parser.add_argument("opts", help=help_s, default=None, nargs=argparse.REMAINDER) | ||||
|     if len(sys.argv) == 1: | ||||
|         parser.print_help() | ||||
|         sys.exit(1) | ||||
|     args = parser.parse_args() | ||||
|     _C.merge_from_file(args.cfg_file) | ||||
|     _C.merge_from_list(args.opts) | ||||
							
								
								
									
										157
									
								
								graph_dit/naswot/pycls/core/distributed.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								graph_dit/naswot/pycls/core/distributed.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """Distributed helpers.""" | ||||
|  | ||||
| import multiprocessing | ||||
| import os | ||||
| import signal | ||||
| import threading | ||||
| import traceback | ||||
|  | ||||
| import torch | ||||
| from pycls.core.config import cfg | ||||
|  | ||||
|  | ||||
| def is_master_proc(): | ||||
|     """Determines if the current process is the master process. | ||||
|  | ||||
|     Master process is responsible for logging, writing and loading checkpoints. In | ||||
|     the multi GPU setting, we assign the master role to the rank 0 process. When | ||||
|     training using a single GPU, there is a single process which is considered master. | ||||
|     """ | ||||
|     return cfg.NUM_GPUS == 1 or torch.distributed.get_rank() == 0 | ||||
|  | ||||
|  | ||||
| def init_process_group(proc_rank, world_size): | ||||
|     """Initializes the default process group.""" | ||||
|     # Set the GPU to use | ||||
|     torch.cuda.set_device(proc_rank) | ||||
|     # Initialize the process group | ||||
|     torch.distributed.init_process_group( | ||||
|         backend=cfg.DIST_BACKEND, | ||||
|         init_method="tcp://{}:{}".format(cfg.HOST, cfg.PORT), | ||||
|         world_size=world_size, | ||||
|         rank=proc_rank, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def destroy_process_group(): | ||||
|     """Destroys the default process group.""" | ||||
|     torch.distributed.destroy_process_group() | ||||
|  | ||||
|  | ||||
| def scaled_all_reduce(tensors): | ||||
|     """Performs the scaled all_reduce operation on the provided tensors. | ||||
|  | ||||
|     The input tensors are modified in-place. Currently supports only the sum | ||||
|     reduction operator. The reduced values are scaled by the inverse size of the | ||||
|     process group (equivalent to cfg.NUM_GPUS). | ||||
|     """ | ||||
|     # There is no need for reduction in the single-proc case | ||||
|     if cfg.NUM_GPUS == 1: | ||||
|         return tensors | ||||
|     # Queue the reductions | ||||
|     reductions = [] | ||||
|     for tensor in tensors: | ||||
|         reduction = torch.distributed.all_reduce(tensor, async_op=True) | ||||
|         reductions.append(reduction) | ||||
|     # Wait for reductions to finish | ||||
|     for reduction in reductions: | ||||
|         reduction.wait() | ||||
|     # Scale the results | ||||
|     for tensor in tensors: | ||||
|         tensor.mul_(1.0 / cfg.NUM_GPUS) | ||||
|     return tensors | ||||
|  | ||||
|  | ||||
| class ChildException(Exception): | ||||
|     """Wraps an exception from a child process.""" | ||||
|  | ||||
|     def __init__(self, child_trace): | ||||
|         super(ChildException, self).__init__(child_trace) | ||||
|  | ||||
|  | ||||
| class ErrorHandler(object): | ||||
|     """Multiprocessing error handler (based on fairseq's). | ||||
|  | ||||
|     Listens for errors in child processes and propagates the tracebacks to the parent. | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, error_queue): | ||||
|         # Shared error queue | ||||
|         self.error_queue = error_queue | ||||
|         # Children processes sharing the error queue | ||||
|         self.children_pids = [] | ||||
|         # Start a thread listening to errors | ||||
|         self.error_listener = threading.Thread(target=self.listen, daemon=True) | ||||
|         self.error_listener.start() | ||||
|         # Register the signal handler | ||||
|         signal.signal(signal.SIGUSR1, self.signal_handler) | ||||
|  | ||||
|     def add_child(self, pid): | ||||
|         """Registers a child process.""" | ||||
|         self.children_pids.append(pid) | ||||
|  | ||||
|     def listen(self): | ||||
|         """Listens for errors in the error queue.""" | ||||
|         # Wait until there is an error in the queue | ||||
|         child_trace = self.error_queue.get() | ||||
|         # Put the error back for the signal handler | ||||
|         self.error_queue.put(child_trace) | ||||
|         # Invoke the signal handler | ||||
|         os.kill(os.getpid(), signal.SIGUSR1) | ||||
|  | ||||
|     def signal_handler(self, _sig_num, _stack_frame): | ||||
|         """Signal handler.""" | ||||
|         # Kill children processes | ||||
|         for pid in self.children_pids: | ||||
|             os.kill(pid, signal.SIGINT) | ||||
|         # Propagate the error from the child process | ||||
|         raise ChildException(self.error_queue.get()) | ||||
|  | ||||
|  | ||||
| def run(proc_rank, world_size, error_queue, fun, fun_args, fun_kwargs): | ||||
|     """Runs a function from a child process.""" | ||||
|     try: | ||||
|         # Initialize the process group | ||||
|         init_process_group(proc_rank, world_size) | ||||
|         # Run the function | ||||
|         fun(*fun_args, **fun_kwargs) | ||||
|     except KeyboardInterrupt: | ||||
|         # Killed by the parent process | ||||
|         pass | ||||
|     except Exception: | ||||
|         # Propagate exception to the parent process | ||||
|         error_queue.put(traceback.format_exc()) | ||||
|     finally: | ||||
|         # Destroy the process group | ||||
|         destroy_process_group() | ||||
|  | ||||
|  | ||||
| def multi_proc_run(num_proc, fun, fun_args=(), fun_kwargs=None): | ||||
|     """Runs a function in a multi-proc setting (unless num_proc == 1).""" | ||||
|     # There is no need for multi-proc in the single-proc case | ||||
|     fun_kwargs = fun_kwargs if fun_kwargs else {} | ||||
|     if num_proc == 1: | ||||
|         fun(*fun_args, **fun_kwargs) | ||||
|         return | ||||
|     # Handle errors from training subprocesses | ||||
|     error_queue = multiprocessing.SimpleQueue() | ||||
|     error_handler = ErrorHandler(error_queue) | ||||
|     # Run each training subprocess | ||||
|     ps = [] | ||||
|     for i in range(num_proc): | ||||
|         p_i = multiprocessing.Process( | ||||
|             target=run, args=(i, num_proc, error_queue, fun, fun_args, fun_kwargs) | ||||
|         ) | ||||
|         ps.append(p_i) | ||||
|         p_i.start() | ||||
|         error_handler.add_child(p_i.pid) | ||||
|     # Wait for each subprocess to finish | ||||
|     for p in ps: | ||||
|         p.join() | ||||
							
								
								
									
										77
									
								
								graph_dit/naswot/pycls/core/io.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								graph_dit/naswot/pycls/core/io.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """IO utilities (adapted from Detectron)""" | ||||
|  | ||||
| import logging | ||||
| import os | ||||
| import re | ||||
| import sys | ||||
| from urllib import request as urlrequest | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
| _PYCLS_BASE_URL = "https://dl.fbaipublicfiles.com/pycls" | ||||
|  | ||||
|  | ||||
| def cache_url(url_or_file, cache_dir): | ||||
|     """Download the file specified by the URL to the cache_dir and return the path to | ||||
|     the cached file. If the argument is not a URL, simply return it as is. | ||||
|     """ | ||||
|     is_url = re.match(r"^(?:http)s?://", url_or_file, re.IGNORECASE) is not None | ||||
|     if not is_url: | ||||
|         return url_or_file | ||||
|     url = url_or_file | ||||
|     err_str = "pycls only automatically caches URLs in the pycls S3 bucket: {}" | ||||
|     assert url.startswith(_PYCLS_BASE_URL), err_str.format(_PYCLS_BASE_URL) | ||||
|     cache_file_path = url.replace(_PYCLS_BASE_URL, cache_dir) | ||||
|     if os.path.exists(cache_file_path): | ||||
|         return cache_file_path | ||||
|     cache_file_dir = os.path.dirname(cache_file_path) | ||||
|     if not os.path.exists(cache_file_dir): | ||||
|         os.makedirs(cache_file_dir) | ||||
|     logger.info("Downloading remote file {} to {}".format(url, cache_file_path)) | ||||
|     download_url(url, cache_file_path) | ||||
|     return cache_file_path | ||||
|  | ||||
|  | ||||
| def _progress_bar(count, total): | ||||
|     """Report download progress. Credit: | ||||
|     https://stackoverflow.com/questions/3173320/text-progress-bar-in-the-console/27871113 | ||||
|     """ | ||||
|     bar_len = 60 | ||||
|     filled_len = int(round(bar_len * count / float(total))) | ||||
|     percents = round(100.0 * count / float(total), 1) | ||||
|     bar = "=" * filled_len + "-" * (bar_len - filled_len) | ||||
|     sys.stdout.write( | ||||
|         "  [{}] {}% of {:.1f}MB file  \r".format(bar, percents, total / 1024 / 1024) | ||||
|     ) | ||||
|     sys.stdout.flush() | ||||
|     if count >= total: | ||||
|         sys.stdout.write("\n") | ||||
|  | ||||
|  | ||||
| def download_url(url, dst_file_path, chunk_size=8192, progress_hook=_progress_bar): | ||||
|     """Download url and write it to dst_file_path. Credit: | ||||
|     https://stackoverflow.com/questions/2028517/python-urllib2-progress-hook | ||||
|     """ | ||||
|     req = urlrequest.Request(url) | ||||
|     response = urlrequest.urlopen(req) | ||||
|     total_size = response.info().get("Content-Length").strip() | ||||
|     total_size = int(total_size) | ||||
|     bytes_so_far = 0 | ||||
|     with open(dst_file_path, "wb") as f: | ||||
|         while 1: | ||||
|             chunk = response.read(chunk_size) | ||||
|             bytes_so_far += len(chunk) | ||||
|             if not chunk: | ||||
|                 break | ||||
|             if progress_hook: | ||||
|                 progress_hook(bytes_so_far, total_size) | ||||
|             f.write(chunk) | ||||
|     return bytes_so_far | ||||
							
								
								
									
										138
									
								
								graph_dit/naswot/pycls/core/logging.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								graph_dit/naswot/pycls/core/logging.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,138 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """Logging.""" | ||||
|  | ||||
| import builtins | ||||
| import decimal | ||||
| import logging | ||||
| import os | ||||
| import sys | ||||
|  | ||||
| import pycls.core.distributed as dist | ||||
| import simplejson | ||||
| from pycls.core.config import cfg | ||||
|  | ||||
|  | ||||
| # Show filename and line number in logs | ||||
| _FORMAT = "[%(filename)s: %(lineno)3d]: %(message)s" | ||||
|  | ||||
| # Log file name (for cfg.LOG_DEST = 'file') | ||||
| _LOG_FILE = "stdout.log" | ||||
|  | ||||
| # Data output with dump_log_data(data, data_type) will be tagged w/ this | ||||
| _TAG = "json_stats: " | ||||
|  | ||||
| # Data output with dump_log_data(data, data_type) will have data[_TYPE]=data_type | ||||
| _TYPE = "_type" | ||||
|  | ||||
|  | ||||
| def _suppress_print(): | ||||
|     """Suppresses printing from the current process.""" | ||||
|  | ||||
|     def ignore(*_objects, _sep=" ", _end="\n", _file=sys.stdout, _flush=False): | ||||
|         pass | ||||
|  | ||||
|     builtins.print = ignore | ||||
|  | ||||
|  | ||||
| def setup_logging(): | ||||
|     """Sets up the logging.""" | ||||
|     # Enable logging only for the master process | ||||
|     if dist.is_master_proc(): | ||||
|         # Clear the root logger to prevent any existing logging config | ||||
|         # (e.g. set by another module) from messing with our setup | ||||
|         logging.root.handlers = [] | ||||
|         # Construct logging configuration | ||||
|         logging_config = {"level": logging.INFO, "format": _FORMAT} | ||||
|         # Log either to stdout or to a file | ||||
|         if cfg.LOG_DEST == "stdout": | ||||
|             logging_config["stream"] = sys.stdout | ||||
|         else: | ||||
|             logging_config["filename"] = os.path.join(cfg.OUT_DIR, _LOG_FILE) | ||||
|         # Configure logging | ||||
|         logging.basicConfig(**logging_config) | ||||
|     else: | ||||
|         _suppress_print() | ||||
|  | ||||
|  | ||||
| def get_logger(name): | ||||
|     """Retrieves the logger.""" | ||||
|     return logging.getLogger(name) | ||||
|  | ||||
|  | ||||
| def dump_log_data(data, data_type, prec=4): | ||||
|     """Covert data (a dictionary) into tagged json string for logging.""" | ||||
|     data[_TYPE] = data_type | ||||
|     data = float_to_decimal(data, prec) | ||||
|     data_json = simplejson.dumps(data, sort_keys=True, use_decimal=True) | ||||
|     return "{:s}{:s}".format(_TAG, data_json) | ||||
|  | ||||
|  | ||||
| def float_to_decimal(data, prec=4): | ||||
|     """Convert floats to decimals which allows for fixed width json.""" | ||||
|     if isinstance(data, dict): | ||||
|         return {k: float_to_decimal(v, prec) for k, v in data.items()} | ||||
|     if isinstance(data, float): | ||||
|         return decimal.Decimal(("{:." + str(prec) + "f}").format(data)) | ||||
|     else: | ||||
|         return data | ||||
|  | ||||
|  | ||||
| def get_log_files(log_dir, name_filter="", log_file=_LOG_FILE): | ||||
|     """Get all log files in directory containing subdirs of trained models.""" | ||||
|     names = [n for n in sorted(os.listdir(log_dir)) if name_filter in n] | ||||
|     files = [os.path.join(log_dir, n, log_file) for n in names] | ||||
|     f_n_ps = [(f, n) for (f, n) in zip(files, names) if os.path.exists(f)] | ||||
|     files, names = zip(*f_n_ps) if f_n_ps else ([], []) | ||||
|     return files, names | ||||
|  | ||||
|  | ||||
| def load_log_data(log_file, data_types_to_skip=()): | ||||
|     """Loads log data into a dictionary of the form data[data_type][metric][index].""" | ||||
|     # Load log_file | ||||
|     assert os.path.exists(log_file), "Log file not found: {}".format(log_file) | ||||
|     with open(log_file, "r") as f: | ||||
|         lines = f.readlines() | ||||
|     # Extract and parse lines that start with _TAG and have a type specified | ||||
|     lines = [l[l.find(_TAG) + len(_TAG) :] for l in lines if _TAG in l] | ||||
|     lines = [simplejson.loads(l) for l in lines] | ||||
|     lines = [l for l in lines if _TYPE in l and not l[_TYPE] in data_types_to_skip] | ||||
|     # Generate data structure accessed by data[data_type][index][metric] | ||||
|     data_types = [l[_TYPE] for l in lines] | ||||
|     data = {t: [] for t in data_types} | ||||
|     for t, line in zip(data_types, lines): | ||||
|         del line[_TYPE] | ||||
|         data[t].append(line) | ||||
|     # Generate data structure accessed by data[data_type][metric][index] | ||||
|     for t in data: | ||||
|         metrics = sorted(data[t][0].keys()) | ||||
|         err_str = "Inconsistent metrics in log for _type={}: {}".format(t, metrics) | ||||
|         assert all(sorted(d.keys()) == metrics for d in data[t]), err_str | ||||
|         data[t] = {m: [d[m] for d in data[t]] for m in metrics} | ||||
|     return data | ||||
|  | ||||
|  | ||||
| def sort_log_data(data): | ||||
|     """Sort each data[data_type][metric] by epoch or keep only first instance.""" | ||||
|     for t in data: | ||||
|         if "epoch" in data[t]: | ||||
|             assert "epoch_ind" not in data[t] and "epoch_max" not in data[t] | ||||
|             data[t]["epoch_ind"] = [int(e.split("/")[0]) for e in data[t]["epoch"]] | ||||
|             data[t]["epoch_max"] = [int(e.split("/")[1]) for e in data[t]["epoch"]] | ||||
|             epoch = data[t]["epoch_ind"] | ||||
|             if "iter" in data[t]: | ||||
|                 assert "iter_ind" not in data[t] and "iter_max" not in data[t] | ||||
|                 data[t]["iter_ind"] = [int(i.split("/")[0]) for i in data[t]["iter"]] | ||||
|                 data[t]["iter_max"] = [int(i.split("/")[1]) for i in data[t]["iter"]] | ||||
|                 itr = zip(epoch, data[t]["iter_ind"], data[t]["iter_max"]) | ||||
|                 epoch = [e + (i_ind - 1) / i_max for e, i_ind, i_max in itr] | ||||
|             for m in data[t]: | ||||
|                 data[t][m] = [v for _, v in sorted(zip(epoch, data[t][m]))] | ||||
|         else: | ||||
|             data[t] = {m: d[0] for m, d in data[t].items()} | ||||
|     return data | ||||
							
								
								
									
										435
									
								
								graph_dit/naswot/pycls/core/meters.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										435
									
								
								graph_dit/naswot/pycls/core/meters.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,435 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """Meters.""" | ||||
|  | ||||
| from collections import deque | ||||
|  | ||||
| import numpy as np | ||||
| import pycls.core.logging as logging | ||||
| import torch | ||||
| from pycls.core.config import cfg | ||||
| from pycls.core.timer import Timer | ||||
|  | ||||
|  | ||||
| logger = logging.get_logger(__name__) | ||||
|  | ||||
|  | ||||
| def time_string(seconds): | ||||
|     """Converts time in seconds to a fixed-width string format.""" | ||||
|     days, rem = divmod(int(seconds), 24 * 3600) | ||||
|     hrs, rem = divmod(rem, 3600) | ||||
|     mins, secs = divmod(rem, 60) | ||||
|     return "{0:02},{1:02}:{2:02}:{3:02}".format(days, hrs, mins, secs) | ||||
|  | ||||
|  | ||||
| def inter_union(preds, labels, num_classes): | ||||
|     _, preds = torch.max(preds, 1) | ||||
|     preds = preds.type(torch.uint8) + 1 | ||||
|     labels = labels.type(torch.uint8) + 1 | ||||
|     preds = preds * (labels > 0).type(torch.uint8) | ||||
|  | ||||
|     inter = preds * (preds == labels).type(torch.uint8) | ||||
|     area_inter = torch.histc(inter.type(torch.int64), bins=num_classes, min=1, max=num_classes) | ||||
|     area_preds = torch.histc(preds.type(torch.int64), bins=num_classes, min=1, max=num_classes) | ||||
|     area_labels = torch.histc(labels.type(torch.int64), bins=num_classes, min=1, max=num_classes) | ||||
|     area_union = area_preds + area_labels - area_inter | ||||
|  | ||||
|     return [area_inter.type(torch.float64) / labels.size(0), area_union.type(torch.float64) / labels.size(0)] | ||||
|  | ||||
|  | ||||
| def topk_errors(preds, labels, ks): | ||||
|     """Computes the top-k error for each k.""" | ||||
|     err_str = "Batch dim of predictions and labels must match" | ||||
|     assert preds.size(0) == labels.size(0), err_str | ||||
|     # Find the top max_k predictions for each sample | ||||
|     _top_max_k_vals, top_max_k_inds = torch.topk( | ||||
|         preds, max(ks), dim=1, largest=True, sorted=True | ||||
|     ) | ||||
|     # (batch_size, max_k) -> (max_k, batch_size) | ||||
|     top_max_k_inds = top_max_k_inds.t() | ||||
|     # (batch_size, ) -> (max_k, batch_size) | ||||
|     rep_max_k_labels = labels.view(1, -1).expand_as(top_max_k_inds) | ||||
|     # (i, j) = 1 if top i-th prediction for the j-th sample is correct | ||||
|     top_max_k_correct = top_max_k_inds.eq(rep_max_k_labels) | ||||
|     # Compute the number of topk correct predictions for each k | ||||
|     topks_correct = [top_max_k_correct[:k, :].view(-1).float().sum() for k in ks] | ||||
|     return [(1.0 - x / preds.size(0)) * 100.0 for x in topks_correct] | ||||
|  | ||||
|  | ||||
| def gpu_mem_usage(): | ||||
|     """Computes the GPU memory usage for the current device (MB).""" | ||||
|     mem_usage_bytes = torch.cuda.max_memory_allocated() | ||||
|     return mem_usage_bytes / 1024 / 1024 | ||||
|  | ||||
|  | ||||
| class ScalarMeter(object): | ||||
|     """Measures a scalar value (adapted from Detectron).""" | ||||
|  | ||||
|     def __init__(self, window_size): | ||||
|         self.deque = deque(maxlen=window_size) | ||||
|         self.total = 0.0 | ||||
|         self.count = 0 | ||||
|  | ||||
|     def reset(self): | ||||
|         self.deque.clear() | ||||
|         self.total = 0.0 | ||||
|         self.count = 0 | ||||
|  | ||||
|     def add_value(self, value): | ||||
|         self.deque.append(value) | ||||
|         self.count += 1 | ||||
|         self.total += value | ||||
|  | ||||
|     def get_win_median(self): | ||||
|         return np.median(self.deque) | ||||
|  | ||||
|     def get_win_avg(self): | ||||
|         return np.mean(self.deque) | ||||
|  | ||||
|     def get_global_avg(self): | ||||
|         return self.total / self.count | ||||
|  | ||||
|  | ||||
| class TrainMeter(object): | ||||
|     """Measures training stats.""" | ||||
|  | ||||
|     def __init__(self, epoch_iters): | ||||
|         self.epoch_iters = epoch_iters | ||||
|         self.max_iter = cfg.OPTIM.MAX_EPOCH * epoch_iters | ||||
|         self.iter_timer = Timer() | ||||
|         self.loss = ScalarMeter(cfg.LOG_PERIOD) | ||||
|         self.loss_total = 0.0 | ||||
|         self.lr = None | ||||
|         # Current minibatch errors (smoothed over a window) | ||||
|         self.mb_top1_err = ScalarMeter(cfg.LOG_PERIOD) | ||||
|         self.mb_top5_err = ScalarMeter(cfg.LOG_PERIOD) | ||||
|         # Number of misclassified examples | ||||
|         self.num_top1_mis = 0 | ||||
|         self.num_top5_mis = 0 | ||||
|         self.num_samples = 0 | ||||
|  | ||||
|     def reset(self, timer=False): | ||||
|         if timer: | ||||
|             self.iter_timer.reset() | ||||
|         self.loss.reset() | ||||
|         self.loss_total = 0.0 | ||||
|         self.lr = None | ||||
|         self.mb_top1_err.reset() | ||||
|         self.mb_top5_err.reset() | ||||
|         self.num_top1_mis = 0 | ||||
|         self.num_top5_mis = 0 | ||||
|         self.num_samples = 0 | ||||
|  | ||||
|     def iter_tic(self): | ||||
|         self.iter_timer.tic() | ||||
|  | ||||
|     def iter_toc(self): | ||||
|         self.iter_timer.toc() | ||||
|  | ||||
|     def update_stats(self, top1_err, top5_err, loss, lr, mb_size): | ||||
|         # Current minibatch stats | ||||
|         self.mb_top1_err.add_value(top1_err) | ||||
|         self.mb_top5_err.add_value(top5_err) | ||||
|         self.loss.add_value(loss) | ||||
|         self.lr = lr | ||||
|         # Aggregate stats | ||||
|         self.num_top1_mis += top1_err * mb_size | ||||
|         self.num_top5_mis += top5_err * mb_size | ||||
|         self.loss_total += loss * mb_size | ||||
|         self.num_samples += mb_size | ||||
|  | ||||
|     def get_iter_stats(self, cur_epoch, cur_iter): | ||||
|         cur_iter_total = cur_epoch * self.epoch_iters + cur_iter + 1 | ||||
|         eta_sec = self.iter_timer.average_time * (self.max_iter - cur_iter_total) | ||||
|         mem_usage = gpu_mem_usage() | ||||
|         stats = { | ||||
|             "epoch": "{}/{}".format(cur_epoch + 1, cfg.OPTIM.MAX_EPOCH), | ||||
|             "iter": "{}/{}".format(cur_iter + 1, self.epoch_iters), | ||||
|             "time_avg": self.iter_timer.average_time, | ||||
|             "time_diff": self.iter_timer.diff, | ||||
|             "eta": time_string(eta_sec), | ||||
|             "top1_err": self.mb_top1_err.get_win_median(), | ||||
|             "top5_err": self.mb_top5_err.get_win_median(), | ||||
|             "loss": self.loss.get_win_median(), | ||||
|             "lr": self.lr, | ||||
|             "mem": int(np.ceil(mem_usage)), | ||||
|         } | ||||
|         return stats | ||||
|  | ||||
|     def log_iter_stats(self, cur_epoch, cur_iter): | ||||
|         if (cur_iter + 1) % cfg.LOG_PERIOD != 0: | ||||
|             return | ||||
|         stats = self.get_iter_stats(cur_epoch, cur_iter) | ||||
|         logger.info(logging.dump_log_data(stats, "train_iter")) | ||||
|  | ||||
|     def get_epoch_stats(self, cur_epoch): | ||||
|         cur_iter_total = (cur_epoch + 1) * self.epoch_iters | ||||
|         eta_sec = self.iter_timer.average_time * (self.max_iter - cur_iter_total) | ||||
|         mem_usage = gpu_mem_usage() | ||||
|         top1_err = self.num_top1_mis / self.num_samples | ||||
|         top5_err = self.num_top5_mis / self.num_samples | ||||
|         avg_loss = self.loss_total / self.num_samples | ||||
|         stats = { | ||||
|             "epoch": "{}/{}".format(cur_epoch + 1, cfg.OPTIM.MAX_EPOCH), | ||||
|             "time_avg": self.iter_timer.average_time, | ||||
|             "eta": time_string(eta_sec), | ||||
|             "top1_err": top1_err, | ||||
|             "top5_err": top5_err, | ||||
|             "loss": avg_loss, | ||||
|             "lr": self.lr, | ||||
|             "mem": int(np.ceil(mem_usage)), | ||||
|         } | ||||
|         return stats | ||||
|  | ||||
|     def log_epoch_stats(self, cur_epoch): | ||||
|         stats = self.get_epoch_stats(cur_epoch) | ||||
|         logger.info(logging.dump_log_data(stats, "train_epoch")) | ||||
|  | ||||
|  | ||||
| class TestMeter(object): | ||||
|     """Measures testing stats.""" | ||||
|  | ||||
|     def __init__(self, max_iter): | ||||
|         self.max_iter = max_iter | ||||
|         self.iter_timer = Timer() | ||||
|         # Current minibatch errors (smoothed over a window) | ||||
|         self.mb_top1_err = ScalarMeter(cfg.LOG_PERIOD) | ||||
|         self.mb_top5_err = ScalarMeter(cfg.LOG_PERIOD) | ||||
|         # Min errors (over the full test set) | ||||
|         self.min_top1_err = 100.0 | ||||
|         self.min_top5_err = 100.0 | ||||
|         # Number of misclassified examples | ||||
|         self.num_top1_mis = 0 | ||||
|         self.num_top5_mis = 0 | ||||
|         self.num_samples = 0 | ||||
|  | ||||
|     def reset(self, min_errs=False): | ||||
|         if min_errs: | ||||
|             self.min_top1_err = 100.0 | ||||
|             self.min_top5_err = 100.0 | ||||
|         self.iter_timer.reset() | ||||
|         self.mb_top1_err.reset() | ||||
|         self.mb_top5_err.reset() | ||||
|         self.num_top1_mis = 0 | ||||
|         self.num_top5_mis = 0 | ||||
|         self.num_samples = 0 | ||||
|  | ||||
|     def iter_tic(self): | ||||
|         self.iter_timer.tic() | ||||
|  | ||||
|     def iter_toc(self): | ||||
|         self.iter_timer.toc() | ||||
|  | ||||
|     def update_stats(self, top1_err, top5_err, mb_size): | ||||
|         self.mb_top1_err.add_value(top1_err) | ||||
|         self.mb_top5_err.add_value(top5_err) | ||||
|         self.num_top1_mis += top1_err * mb_size | ||||
|         self.num_top5_mis += top5_err * mb_size | ||||
|         self.num_samples += mb_size | ||||
|  | ||||
|     def get_iter_stats(self, cur_epoch, cur_iter): | ||||
|         mem_usage = gpu_mem_usage() | ||||
|         iter_stats = { | ||||
|             "epoch": "{}/{}".format(cur_epoch + 1, cfg.OPTIM.MAX_EPOCH), | ||||
|             "iter": "{}/{}".format(cur_iter + 1, self.max_iter), | ||||
|             "time_avg": self.iter_timer.average_time, | ||||
|             "time_diff": self.iter_timer.diff, | ||||
|             "top1_err": self.mb_top1_err.get_win_median(), | ||||
|             "top5_err": self.mb_top5_err.get_win_median(), | ||||
|             "mem": int(np.ceil(mem_usage)), | ||||
|         } | ||||
|         return iter_stats | ||||
|  | ||||
|     def log_iter_stats(self, cur_epoch, cur_iter): | ||||
|         if (cur_iter + 1) % cfg.LOG_PERIOD != 0: | ||||
|             return | ||||
|         stats = self.get_iter_stats(cur_epoch, cur_iter) | ||||
|         logger.info(logging.dump_log_data(stats, "test_iter")) | ||||
|  | ||||
|     def get_epoch_stats(self, cur_epoch): | ||||
|         top1_err = self.num_top1_mis / self.num_samples | ||||
|         top5_err = self.num_top5_mis / self.num_samples | ||||
|         self.min_top1_err = min(self.min_top1_err, top1_err) | ||||
|         self.min_top5_err = min(self.min_top5_err, top5_err) | ||||
|         mem_usage = gpu_mem_usage() | ||||
|         stats = { | ||||
|             "epoch": "{}/{}".format(cur_epoch + 1, cfg.OPTIM.MAX_EPOCH), | ||||
|             "time_avg": self.iter_timer.average_time, | ||||
|             "top1_err": top1_err, | ||||
|             "top5_err": top5_err, | ||||
|             "min_top1_err": self.min_top1_err, | ||||
|             "min_top5_err": self.min_top5_err, | ||||
|             "mem": int(np.ceil(mem_usage)), | ||||
|         } | ||||
|         return stats | ||||
|  | ||||
|     def log_epoch_stats(self, cur_epoch): | ||||
|         stats = self.get_epoch_stats(cur_epoch) | ||||
|         logger.info(logging.dump_log_data(stats, "test_epoch")) | ||||
|  | ||||
|  | ||||
| class TrainMeterIoU(object): | ||||
|     """Measures training stats.""" | ||||
|  | ||||
|     def __init__(self, epoch_iters): | ||||
|         self.epoch_iters = epoch_iters | ||||
|         self.max_iter = cfg.OPTIM.MAX_EPOCH * epoch_iters | ||||
|         self.iter_timer = Timer() | ||||
|         self.loss = ScalarMeter(cfg.LOG_PERIOD) | ||||
|         self.loss_total = 0.0 | ||||
|         self.lr = None | ||||
|  | ||||
|         self.mb_miou = ScalarMeter(cfg.LOG_PERIOD) | ||||
|  | ||||
|         self.num_inter = np.zeros(cfg.MODEL.NUM_CLASSES) | ||||
|         self.num_union = np.zeros(cfg.MODEL.NUM_CLASSES) | ||||
|         self.num_samples = 0 | ||||
|  | ||||
|     def reset(self, timer=False): | ||||
|         if timer: | ||||
|             self.iter_timer.reset() | ||||
|         self.loss.reset() | ||||
|         self.loss_total = 0.0 | ||||
|         self.lr = None | ||||
|         self.mb_miou.reset() | ||||
|         self.num_inter = np.zeros(cfg.MODEL.NUM_CLASSES) | ||||
|         self.num_union = np.zeros(cfg.MODEL.NUM_CLASSES) | ||||
|         self.num_samples = 0 | ||||
|  | ||||
|     def iter_tic(self): | ||||
|         self.iter_timer.tic() | ||||
|  | ||||
|     def iter_toc(self): | ||||
|         self.iter_timer.toc() | ||||
|  | ||||
|     def update_stats(self, inter, union, loss, lr, mb_size): | ||||
|         # Current minibatch stats | ||||
|         self.mb_miou.add_value((inter / (union + 1e-10)).mean()) | ||||
|         self.loss.add_value(loss) | ||||
|         self.lr = lr | ||||
|         # Aggregate stats | ||||
|         self.num_inter += inter * mb_size | ||||
|         self.num_union += union * mb_size | ||||
|         self.loss_total += loss * mb_size | ||||
|         self.num_samples += mb_size | ||||
|  | ||||
|     def get_iter_stats(self, cur_epoch, cur_iter): | ||||
|         cur_iter_total = cur_epoch * self.epoch_iters + cur_iter + 1 | ||||
|         eta_sec = self.iter_timer.average_time * (self.max_iter - cur_iter_total) | ||||
|         mem_usage = gpu_mem_usage() | ||||
|         stats = { | ||||
|             "epoch": "{}/{}".format(cur_epoch + 1, cfg.OPTIM.MAX_EPOCH), | ||||
|             "iter": "{}/{}".format(cur_iter + 1, self.epoch_iters), | ||||
|             "time_avg": self.iter_timer.average_time, | ||||
|             "time_diff": self.iter_timer.diff, | ||||
|             "eta": time_string(eta_sec), | ||||
|             "miou": self.mb_miou.get_win_median(), | ||||
|             "loss": self.loss.get_win_median(), | ||||
|             "lr": self.lr, | ||||
|             "mem": int(np.ceil(mem_usage)), | ||||
|         } | ||||
|         return stats | ||||
|  | ||||
|     def log_iter_stats(self, cur_epoch, cur_iter): | ||||
|         if (cur_iter + 1) % cfg.LOG_PERIOD != 0: | ||||
|             return | ||||
|         stats = self.get_iter_stats(cur_epoch, cur_iter) | ||||
|         logger.info(logging.dump_log_data(stats, "train_iter")) | ||||
|  | ||||
|     def get_epoch_stats(self, cur_epoch): | ||||
|         cur_iter_total = (cur_epoch + 1) * self.epoch_iters | ||||
|         eta_sec = self.iter_timer.average_time * (self.max_iter - cur_iter_total) | ||||
|         mem_usage = gpu_mem_usage() | ||||
|         miou = (self.num_inter / (self.num_union + 1e-10)).mean() | ||||
|         avg_loss = self.loss_total / self.num_samples | ||||
|         stats = { | ||||
|             "epoch": "{}/{}".format(cur_epoch + 1, cfg.OPTIM.MAX_EPOCH), | ||||
|             "time_avg": self.iter_timer.average_time, | ||||
|             "eta": time_string(eta_sec), | ||||
|             "miou": miou, | ||||
|             "loss": avg_loss, | ||||
|             "lr": self.lr, | ||||
|             "mem": int(np.ceil(mem_usage)), | ||||
|         } | ||||
|         return stats | ||||
|  | ||||
|     def log_epoch_stats(self, cur_epoch): | ||||
|         stats = self.get_epoch_stats(cur_epoch) | ||||
|         logger.info(logging.dump_log_data(stats, "train_epoch")) | ||||
|  | ||||
|  | ||||
| class TestMeterIoU(object): | ||||
|     """Measures testing stats.""" | ||||
|  | ||||
|     def __init__(self, max_iter): | ||||
|         self.max_iter = max_iter | ||||
|         self.iter_timer = Timer() | ||||
|  | ||||
|         self.mb_miou = ScalarMeter(cfg.LOG_PERIOD) | ||||
|  | ||||
|         self.max_miou = 0.0 | ||||
|  | ||||
|         self.num_inter = np.zeros(cfg.MODEL.NUM_CLASSES) | ||||
|         self.num_union = np.zeros(cfg.MODEL.NUM_CLASSES) | ||||
|         self.num_samples = 0 | ||||
|  | ||||
|     def reset(self, min_errs=False): | ||||
|         if min_errs: | ||||
|             self.max_miou = 0.0 | ||||
|         self.iter_timer.reset() | ||||
|         self.mb_miou.reset() | ||||
|         self.num_inter = np.zeros(cfg.MODEL.NUM_CLASSES) | ||||
|         self.num_union = np.zeros(cfg.MODEL.NUM_CLASSES) | ||||
|         self.num_samples = 0 | ||||
|  | ||||
|     def iter_tic(self): | ||||
|         self.iter_timer.tic() | ||||
|  | ||||
|     def iter_toc(self): | ||||
|         self.iter_timer.toc() | ||||
|  | ||||
|     def update_stats(self, inter, union, mb_size): | ||||
|         self.mb_miou.add_value((inter / (union + 1e-10)).mean()) | ||||
|         self.num_inter += inter * mb_size | ||||
|         self.num_union += union * mb_size | ||||
|         self.num_samples += mb_size | ||||
|  | ||||
|     def get_iter_stats(self, cur_epoch, cur_iter): | ||||
|         mem_usage = gpu_mem_usage() | ||||
|         iter_stats = { | ||||
|             "epoch": "{}/{}".format(cur_epoch + 1, cfg.OPTIM.MAX_EPOCH), | ||||
|             "iter": "{}/{}".format(cur_iter + 1, self.max_iter), | ||||
|             "time_avg": self.iter_timer.average_time, | ||||
|             "time_diff": self.iter_timer.diff, | ||||
|             "miou": self.mb_miou.get_win_median(), | ||||
|             "mem": int(np.ceil(mem_usage)), | ||||
|         } | ||||
|         return iter_stats | ||||
|  | ||||
|     def log_iter_stats(self, cur_epoch, cur_iter): | ||||
|         if (cur_iter + 1) % cfg.LOG_PERIOD != 0: | ||||
|             return | ||||
|         stats = self.get_iter_stats(cur_epoch, cur_iter) | ||||
|         logger.info(logging.dump_log_data(stats, "test_iter")) | ||||
|  | ||||
|     def get_epoch_stats(self, cur_epoch): | ||||
|         miou = (self.num_inter / (self.num_union + 1e-10)).mean() | ||||
|         self.max_miou = max(self.max_miou, miou) | ||||
|         mem_usage = gpu_mem_usage() | ||||
|         stats = { | ||||
|             "epoch": "{}/{}".format(cur_epoch + 1, cfg.OPTIM.MAX_EPOCH), | ||||
|             "time_avg": self.iter_timer.average_time, | ||||
|             "miou": miou, | ||||
|             "max_miou": self.max_miou, | ||||
|             "mem": int(np.ceil(mem_usage)), | ||||
|         } | ||||
|         return stats | ||||
|  | ||||
|     def log_epoch_stats(self, cur_epoch): | ||||
|         stats = self.get_epoch_stats(cur_epoch) | ||||
|         logger.info(logging.dump_log_data(stats, "test_epoch")) | ||||
							
								
								
									
										129
									
								
								graph_dit/naswot/pycls/core/net.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								graph_dit/naswot/pycls/core/net.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """Functions for manipulating networks.""" | ||||
|  | ||||
| import itertools | ||||
| import math | ||||
|  | ||||
| import torch | ||||
| import torch.nn as nn | ||||
| from pycls.core.config import cfg | ||||
|  | ||||
|  | ||||
| def init_weights(m): | ||||
|     """Performs ResNet-style weight initialization.""" | ||||
|     if isinstance(m, nn.Conv2d): | ||||
|         # Note that there is no bias due to BN | ||||
|         fan_out = m.kernel_size[0] * m.kernel_size[1] * m.out_channels | ||||
|         m.weight.data.normal_(mean=0.0, std=math.sqrt(2.0 / fan_out)) | ||||
|     elif isinstance(m, nn.BatchNorm2d): | ||||
|         zero_init_gamma = cfg.BN.ZERO_INIT_FINAL_GAMMA | ||||
|         zero_init_gamma = hasattr(m, "final_bn") and m.final_bn and zero_init_gamma | ||||
|         m.weight.data.fill_(0.0 if zero_init_gamma else 1.0) | ||||
|         m.bias.data.zero_() | ||||
|     elif isinstance(m, nn.Linear): | ||||
|         m.weight.data.normal_(mean=0.0, std=0.01) | ||||
|         m.bias.data.zero_() | ||||
|  | ||||
|  | ||||
| @torch.no_grad() | ||||
| def compute_precise_bn_stats(model, loader): | ||||
|     """Computes precise BN stats on training data.""" | ||||
|     # Compute the number of minibatches to use | ||||
|     num_iter = min(cfg.BN.NUM_SAMPLES_PRECISE // loader.batch_size, len(loader)) | ||||
|     # Retrieve the BN layers | ||||
|     bns = [m for m in model.modules() if isinstance(m, torch.nn.BatchNorm2d)] | ||||
|     # Initialize stats storage | ||||
|     mus = [torch.zeros_like(bn.running_mean) for bn in bns] | ||||
|     sqs = [torch.zeros_like(bn.running_var) for bn in bns] | ||||
|     # Remember momentum values | ||||
|     moms = [bn.momentum for bn in bns] | ||||
|     # Disable momentum | ||||
|     for bn in bns: | ||||
|         bn.momentum = 1.0 | ||||
|     # Accumulate the stats across the data samples | ||||
|     for inputs, _labels in itertools.islice(loader, num_iter): | ||||
|         model(inputs.cuda()) | ||||
|         # Accumulate the stats for each BN layer | ||||
|         for i, bn in enumerate(bns): | ||||
|             m, v = bn.running_mean, bn.running_var | ||||
|             sqs[i] += (v + m * m) / num_iter | ||||
|             mus[i] += m / num_iter | ||||
|     # Set the stats and restore momentum values | ||||
|     for i, bn in enumerate(bns): | ||||
|         bn.running_var = sqs[i] - mus[i] * mus[i] | ||||
|         bn.running_mean = mus[i] | ||||
|         bn.momentum = moms[i] | ||||
|  | ||||
|  | ||||
| def reset_bn_stats(model): | ||||
|     """Resets running BN stats.""" | ||||
|     for m in model.modules(): | ||||
|         if isinstance(m, torch.nn.BatchNorm2d): | ||||
|             m.reset_running_stats() | ||||
|  | ||||
|  | ||||
| def complexity_conv2d(cx, w_in, w_out, k, stride, padding, groups=1, bias=False): | ||||
|     """Accumulates complexity of Conv2D into cx = (h, w, flops, params, acts).""" | ||||
|     h, w, flops, params, acts = cx["h"], cx["w"], cx["flops"], cx["params"], cx["acts"] | ||||
|     h = (h + 2 * padding - k) // stride + 1 | ||||
|     w = (w + 2 * padding - k) // stride + 1 | ||||
|     flops += k * k * w_in * w_out * h * w // groups | ||||
|     params += k * k * w_in * w_out // groups | ||||
|     flops += w_out if bias else 0 | ||||
|     params += w_out if bias else 0 | ||||
|     acts += w_out * h * w | ||||
|     return {"h": h, "w": w, "flops": flops, "params": params, "acts": acts} | ||||
|  | ||||
|  | ||||
| def complexity_batchnorm2d(cx, w_in): | ||||
|     """Accumulates complexity of BatchNorm2D into cx = (h, w, flops, params, acts).""" | ||||
|     h, w, flops, params, acts = cx["h"], cx["w"], cx["flops"], cx["params"], cx["acts"] | ||||
|     params += 2 * w_in | ||||
|     return {"h": h, "w": w, "flops": flops, "params": params, "acts": acts} | ||||
|  | ||||
|  | ||||
| def complexity_maxpool2d(cx, k, stride, padding): | ||||
|     """Accumulates complexity of MaxPool2d into cx = (h, w, flops, params, acts).""" | ||||
|     h, w, flops, params, acts = cx["h"], cx["w"], cx["flops"], cx["params"], cx["acts"] | ||||
|     h = (h + 2 * padding - k) // stride + 1 | ||||
|     w = (w + 2 * padding - k) // stride + 1 | ||||
|     return {"h": h, "w": w, "flops": flops, "params": params, "acts": acts} | ||||
|  | ||||
|  | ||||
| def complexity(model): | ||||
|     """Compute model complexity (model can be model instance or model class).""" | ||||
|     size = cfg.TRAIN.IM_SIZE | ||||
|     cx = {"h": size, "w": size, "flops": 0, "params": 0, "acts": 0} | ||||
|     cx = model.complexity(cx) | ||||
|     return {"flops": cx["flops"], "params": cx["params"], "acts": cx["acts"]} | ||||
|  | ||||
|  | ||||
| def drop_connect(x, drop_ratio): | ||||
|     """Drop connect (adapted from DARTS).""" | ||||
|     keep_ratio = 1.0 - drop_ratio | ||||
|     mask = torch.empty([x.shape[0], 1, 1, 1], dtype=x.dtype, device=x.device) | ||||
|     mask.bernoulli_(keep_ratio) | ||||
|     x.div_(keep_ratio) | ||||
|     x.mul_(mask) | ||||
|     return x | ||||
|  | ||||
|  | ||||
| def get_flat_weights(model): | ||||
|     """Gets all model weights as a single flat vector.""" | ||||
|     return torch.cat([p.data.view(-1, 1) for p in model.parameters()], 0) | ||||
|  | ||||
|  | ||||
| def set_flat_weights(model, flat_weights): | ||||
|     """Sets all model weights from a single flat vector.""" | ||||
|     k = 0 | ||||
|     for p in model.parameters(): | ||||
|         n = p.data.numel() | ||||
|         p.data.copy_(flat_weights[k : (k + n)].view_as(p.data)) | ||||
|         k += n | ||||
|     assert k == flat_weights.numel() | ||||
							
								
								
									
										95
									
								
								graph_dit/naswot/pycls/core/optimizer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								graph_dit/naswot/pycls/core/optimizer.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """Optimizer.""" | ||||
|  | ||||
| import numpy as np | ||||
| import torch | ||||
| from pycls.core.config import cfg | ||||
|  | ||||
|  | ||||
| def construct_optimizer(model): | ||||
|     """Constructs the optimizer. | ||||
|  | ||||
|     Note that the momentum update in PyTorch differs from the one in Caffe2. | ||||
|     In particular, | ||||
|  | ||||
|         Caffe2: | ||||
|             V := mu * V + lr * g | ||||
|             p := p - V | ||||
|  | ||||
|         PyTorch: | ||||
|             V := mu * V + g | ||||
|             p := p - lr * V | ||||
|  | ||||
|     where V is the velocity, mu is the momentum factor, lr is the learning rate, | ||||
|     g is the gradient and p are the parameters. | ||||
|  | ||||
|     Since V is defined independently of the learning rate in PyTorch, | ||||
|     when the learning rate is changed there is no need to perform the | ||||
|     momentum correction by scaling V (unlike in the Caffe2 case). | ||||
|     """ | ||||
|     if cfg.BN.USE_CUSTOM_WEIGHT_DECAY: | ||||
|         # Apply different weight decay to Batchnorm and non-batchnorm parameters. | ||||
|         p_bn = [p for n, p in model.named_parameters() if "bn" in n] | ||||
|         p_non_bn = [p for n, p in model.named_parameters() if "bn" not in n] | ||||
|         optim_params = [ | ||||
|             {"params": p_bn, "weight_decay": cfg.BN.CUSTOM_WEIGHT_DECAY}, | ||||
|             {"params": p_non_bn, "weight_decay": cfg.OPTIM.WEIGHT_DECAY}, | ||||
|         ] | ||||
|     else: | ||||
|         optim_params = model.parameters() | ||||
|     return torch.optim.SGD( | ||||
|         optim_params, | ||||
|         lr=cfg.OPTIM.BASE_LR, | ||||
|         momentum=cfg.OPTIM.MOMENTUM, | ||||
|         weight_decay=cfg.OPTIM.WEIGHT_DECAY, | ||||
|         dampening=cfg.OPTIM.DAMPENING, | ||||
|         nesterov=cfg.OPTIM.NESTEROV, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def lr_fun_steps(cur_epoch): | ||||
|     """Steps schedule (cfg.OPTIM.LR_POLICY = 'steps').""" | ||||
|     ind = [i for i, s in enumerate(cfg.OPTIM.STEPS) if cur_epoch >= s][-1] | ||||
|     return cfg.OPTIM.BASE_LR * (cfg.OPTIM.LR_MULT ** ind) | ||||
|  | ||||
|  | ||||
| def lr_fun_exp(cur_epoch): | ||||
|     """Exponential schedule (cfg.OPTIM.LR_POLICY = 'exp').""" | ||||
|     return cfg.OPTIM.BASE_LR * (cfg.OPTIM.GAMMA ** cur_epoch) | ||||
|  | ||||
|  | ||||
| def lr_fun_cos(cur_epoch): | ||||
|     """Cosine schedule (cfg.OPTIM.LR_POLICY = 'cos').""" | ||||
|     base_lr, max_epoch = cfg.OPTIM.BASE_LR, cfg.OPTIM.MAX_EPOCH | ||||
|     return 0.5 * base_lr * (1.0 + np.cos(np.pi * cur_epoch / max_epoch)) | ||||
|  | ||||
|  | ||||
| def get_lr_fun(): | ||||
|     """Retrieves the specified lr policy function""" | ||||
|     lr_fun = "lr_fun_" + cfg.OPTIM.LR_POLICY | ||||
|     if lr_fun not in globals(): | ||||
|         raise NotImplementedError("Unknown LR policy:" + cfg.OPTIM.LR_POLICY) | ||||
|     return globals()[lr_fun] | ||||
|  | ||||
|  | ||||
| def get_epoch_lr(cur_epoch): | ||||
|     """Retrieves the lr for the given epoch according to the policy.""" | ||||
|     lr = get_lr_fun()(cur_epoch) | ||||
|     # Linear warmup | ||||
|     if cur_epoch < cfg.OPTIM.WARMUP_EPOCHS: | ||||
|         alpha = cur_epoch / cfg.OPTIM.WARMUP_EPOCHS | ||||
|         warmup_factor = cfg.OPTIM.WARMUP_FACTOR * (1.0 - alpha) + alpha | ||||
|         lr *= warmup_factor | ||||
|     return lr | ||||
|  | ||||
|  | ||||
| def set_lr(optimizer, new_lr): | ||||
|     """Sets the optimizer lr to the specified value.""" | ||||
|     for param_group in optimizer.param_groups: | ||||
|         param_group["lr"] = new_lr | ||||
							
								
								
									
										132
									
								
								graph_dit/naswot/pycls/core/plotting.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								graph_dit/naswot/pycls/core/plotting.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,132 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """Plotting functions.""" | ||||
|  | ||||
| import colorlover as cl | ||||
| import matplotlib.pyplot as plt | ||||
| import plotly.graph_objs as go | ||||
| import plotly.offline as offline | ||||
| import pycls.core.logging as logging | ||||
|  | ||||
|  | ||||
| def get_plot_colors(max_colors, color_format="pyplot"): | ||||
|     """Generate colors for plotting.""" | ||||
|     colors = cl.scales["11"]["qual"]["Paired"] | ||||
|     if max_colors > len(colors): | ||||
|         colors = cl.to_rgb(cl.interp(colors, max_colors)) | ||||
|     if color_format == "pyplot": | ||||
|         return [[j / 255.0 for j in c] for c in cl.to_numeric(colors)] | ||||
|     return colors | ||||
|  | ||||
|  | ||||
| def prepare_plot_data(log_files, names, metric="top1_err"): | ||||
|     """Load logs and extract data for plotting error curves.""" | ||||
|     plot_data = [] | ||||
|     for file, name in zip(log_files, names): | ||||
|         d, data = {}, logging.sort_log_data(logging.load_log_data(file)) | ||||
|         for phase in ["train", "test"]: | ||||
|             x = data[phase + "_epoch"]["epoch_ind"] | ||||
|             y = data[phase + "_epoch"][metric] | ||||
|             d["x_" + phase], d["y_" + phase] = x, y | ||||
|             d[phase + "_label"] = "[{:5.2f}] ".format(min(y) if y else 0) + name | ||||
|         plot_data.append(d) | ||||
|     assert len(plot_data) > 0, "No data to plot" | ||||
|     return plot_data | ||||
|  | ||||
|  | ||||
| def plot_error_curves_plotly(log_files, names, filename, metric="top1_err"): | ||||
|     """Plot error curves using plotly and save to file.""" | ||||
|     plot_data = prepare_plot_data(log_files, names, metric) | ||||
|     colors = get_plot_colors(len(plot_data), "plotly") | ||||
|     # Prepare data for plots (3 sets, train duplicated w and w/o legend) | ||||
|     data = [] | ||||
|     for i, d in enumerate(plot_data): | ||||
|         s = str(i) | ||||
|         line_train = {"color": colors[i], "dash": "dashdot", "width": 1.5} | ||||
|         line_test = {"color": colors[i], "dash": "solid", "width": 1.5} | ||||
|         data.append( | ||||
|             go.Scatter( | ||||
|                 x=d["x_train"], | ||||
|                 y=d["y_train"], | ||||
|                 mode="lines", | ||||
|                 name=d["train_label"], | ||||
|                 line=line_train, | ||||
|                 legendgroup=s, | ||||
|                 visible=True, | ||||
|                 showlegend=False, | ||||
|             ) | ||||
|         ) | ||||
|         data.append( | ||||
|             go.Scatter( | ||||
|                 x=d["x_test"], | ||||
|                 y=d["y_test"], | ||||
|                 mode="lines", | ||||
|                 name=d["test_label"], | ||||
|                 line=line_test, | ||||
|                 legendgroup=s, | ||||
|                 visible=True, | ||||
|                 showlegend=True, | ||||
|             ) | ||||
|         ) | ||||
|         data.append( | ||||
|             go.Scatter( | ||||
|                 x=d["x_train"], | ||||
|                 y=d["y_train"], | ||||
|                 mode="lines", | ||||
|                 name=d["train_label"], | ||||
|                 line=line_train, | ||||
|                 legendgroup=s, | ||||
|                 visible=False, | ||||
|                 showlegend=True, | ||||
|             ) | ||||
|         ) | ||||
|     # Prepare layout w ability to toggle 'all', 'train', 'test' | ||||
|     titlefont = {"size": 18, "color": "#7f7f7f"} | ||||
|     vis = [[True, True, False], [False, False, True], [False, True, False]] | ||||
|     buttons = zip(["all", "train", "test"], [[{"visible": v}] for v in vis]) | ||||
|     buttons = [{"label": b, "args": v, "method": "update"} for b, v in buttons] | ||||
|     layout = go.Layout( | ||||
|         title=metric + " vs. epoch<br>[dash=train, solid=test]", | ||||
|         xaxis={"title": "epoch", "titlefont": titlefont}, | ||||
|         yaxis={"title": metric, "titlefont": titlefont}, | ||||
|         showlegend=True, | ||||
|         hoverlabel={"namelength": -1}, | ||||
|         updatemenus=[ | ||||
|             { | ||||
|                 "buttons": buttons, | ||||
|                 "direction": "down", | ||||
|                 "showactive": True, | ||||
|                 "x": 1.02, | ||||
|                 "xanchor": "left", | ||||
|                 "y": 1.08, | ||||
|                 "yanchor": "top", | ||||
|             } | ||||
|         ], | ||||
|     ) | ||||
|     # Create plotly plot | ||||
|     offline.plot({"data": data, "layout": layout}, filename=filename) | ||||
|  | ||||
|  | ||||
| def plot_error_curves_pyplot(log_files, names, filename=None, metric="top1_err"): | ||||
|     """Plot error curves using matplotlib.pyplot and save to file.""" | ||||
|     plot_data = prepare_plot_data(log_files, names, metric) | ||||
|     colors = get_plot_colors(len(names)) | ||||
|     for ind, d in enumerate(plot_data): | ||||
|         c, lbl = colors[ind], d["test_label"] | ||||
|         plt.plot(d["x_train"], d["y_train"], "--", c=c, alpha=0.8) | ||||
|         plt.plot(d["x_test"], d["y_test"], "-", c=c, alpha=0.8, label=lbl) | ||||
|     plt.title(metric + " vs. epoch\n[dash=train, solid=test]", fontsize=14) | ||||
|     plt.xlabel("epoch", fontsize=14) | ||||
|     plt.ylabel(metric, fontsize=14) | ||||
|     plt.grid(alpha=0.4) | ||||
|     plt.legend() | ||||
|     if filename: | ||||
|         plt.savefig(filename) | ||||
|         plt.clf() | ||||
|     else: | ||||
|         plt.show() | ||||
							
								
								
									
										39
									
								
								graph_dit/naswot/pycls/core/timer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								graph_dit/naswot/pycls/core/timer.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """Timer.""" | ||||
|  | ||||
| import time | ||||
|  | ||||
|  | ||||
| class Timer(object): | ||||
|     """A simple timer (adapted from Detectron).""" | ||||
|  | ||||
|     def __init__(self): | ||||
|         self.total_time = None | ||||
|         self.calls = None | ||||
|         self.start_time = None | ||||
|         self.diff = None | ||||
|         self.average_time = None | ||||
|         self.reset() | ||||
|  | ||||
|     def tic(self): | ||||
|         # using time.time as time.clock does not normalize for multithreading | ||||
|         self.start_time = time.time() | ||||
|  | ||||
|     def toc(self): | ||||
|         self.diff = time.time() - self.start_time | ||||
|         self.total_time += self.diff | ||||
|         self.calls += 1 | ||||
|         self.average_time = self.total_time / self.calls | ||||
|  | ||||
|     def reset(self): | ||||
|         self.total_time = 0.0 | ||||
|         self.calls = 0 | ||||
|         self.start_time = 0.0 | ||||
|         self.diff = 0.0 | ||||
|         self.average_time = 0.0 | ||||
							
								
								
									
										419
									
								
								graph_dit/naswot/pycls/core/trainer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										419
									
								
								graph_dit/naswot/pycls/core/trainer.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,419 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """Tools for training and testing a model.""" | ||||
|  | ||||
| import os | ||||
| from thop import profile | ||||
|  | ||||
| import numpy as np | ||||
| import pycls.core.benchmark as benchmark | ||||
| import pycls.core.builders as builders | ||||
| import pycls.core.checkpoint as checkpoint | ||||
| import pycls.core.config as config | ||||
| import pycls.core.distributed as dist | ||||
| import pycls.core.logging as logging | ||||
| import pycls.core.meters as meters | ||||
| import pycls.core.net as net | ||||
| import pycls.core.optimizer as optim | ||||
| import pycls.datasets.loader as loader | ||||
| import torch | ||||
| import torch.nn.functional as F | ||||
| from pycls.core.config import cfg | ||||
|  | ||||
|  | ||||
| logger = logging.get_logger(__name__) | ||||
|  | ||||
|  | ||||
| def setup_env(): | ||||
|     """Sets up environment for training or testing.""" | ||||
|     if dist.is_master_proc(): | ||||
|         # Ensure that the output dir exists | ||||
|         os.makedirs(cfg.OUT_DIR, exist_ok=True) | ||||
|         # Save the config | ||||
|         config.dump_cfg() | ||||
|     # Setup logging | ||||
|     logging.setup_logging() | ||||
|     # Log the config as both human readable and as a json | ||||
|     logger.info("Config:\n{}".format(cfg)) | ||||
|     logger.info(logging.dump_log_data(cfg, "cfg")) | ||||
|     # Fix the RNG seeds (see RNG comment in core/config.py for discussion) | ||||
|     np.random.seed(cfg.RNG_SEED) | ||||
|     torch.manual_seed(cfg.RNG_SEED) | ||||
|     # Configure the CUDNN backend | ||||
|     torch.backends.cudnn.benchmark = cfg.CUDNN.BENCHMARK | ||||
|  | ||||
|  | ||||
| def setup_model(): | ||||
|     """Sets up a model for training or testing and log the results.""" | ||||
|     # Build the model | ||||
|     model = builders.build_model() | ||||
|     logger.info("Model:\n{}".format(model)) | ||||
|     # Log model complexity | ||||
|     # logger.info(logging.dump_log_data(net.complexity(model), "complexity")) | ||||
|     if cfg.TASK == "seg" and cfg.TRAIN.DATASET == "cityscapes": | ||||
|         h, w = 1025, 2049 | ||||
|     else: | ||||
|         h, w = cfg.TRAIN.IM_SIZE, cfg.TRAIN.IM_SIZE | ||||
|     if cfg.TASK == "jig": | ||||
|         x = torch.randn(1, cfg.JIGSAW_GRID ** 2, cfg.MODEL.INPUT_CHANNELS, h, w) | ||||
|     else: | ||||
|         x = torch.randn(1, cfg.MODEL.INPUT_CHANNELS, h, w) | ||||
|     macs, params = profile(model, inputs=(x, ), verbose=False) | ||||
|     logger.info("Params: {:,}".format(params)) | ||||
|     logger.info("Flops: {:,}".format(macs)) | ||||
|     # Transfer the model to the current GPU device | ||||
|     err_str = "Cannot use more GPU devices than available" | ||||
|     assert cfg.NUM_GPUS <= torch.cuda.device_count(), err_str | ||||
|     cur_device = torch.cuda.current_device() | ||||
|     model = model.cuda(device=cur_device) | ||||
|     # Use multi-process data parallel model in the multi-gpu setting | ||||
|     if cfg.NUM_GPUS > 1: | ||||
|         # Make model replica operate on the current device | ||||
|         model = torch.nn.parallel.DistributedDataParallel( | ||||
|             module=model, device_ids=[cur_device], output_device=cur_device | ||||
|         ) | ||||
|         # Set complexity function to be module's complexity function | ||||
|         # model.complexity = model.module.complexity | ||||
|     return model | ||||
|  | ||||
|  | ||||
| def train_epoch(train_loader, model, loss_fun, optimizer, train_meter, cur_epoch): | ||||
|     """Performs one epoch of training.""" | ||||
|     # Update drop path prob for NAS | ||||
|     if cfg.MODEL.TYPE == "nas": | ||||
|         m = model.module if cfg.NUM_GPUS > 1 else model | ||||
|         m.set_drop_path_prob(cfg.NAS.DROP_PROB * cur_epoch / cfg.OPTIM.MAX_EPOCH) | ||||
|     # Shuffle the data | ||||
|     loader.shuffle(train_loader, cur_epoch) | ||||
|     # Update the learning rate per epoch | ||||
|     if not cfg.OPTIM.ITER_LR: | ||||
|         lr = optim.get_epoch_lr(cur_epoch) | ||||
|         optim.set_lr(optimizer, lr) | ||||
|     # Enable training mode | ||||
|     model.train() | ||||
|     train_meter.iter_tic() | ||||
|     for cur_iter, (inputs, labels) in enumerate(train_loader): | ||||
|         # Update the learning rate per iter | ||||
|         if cfg.OPTIM.ITER_LR: | ||||
|             lr = optim.get_epoch_lr(cur_epoch + cur_iter / len(train_loader)) | ||||
|             optim.set_lr(optimizer, lr) | ||||
|         # Transfer the data to the current GPU device | ||||
|         inputs, labels = inputs.cuda(), labels.cuda(non_blocking=True) | ||||
|         # Perform the forward pass | ||||
|         preds = model(inputs) | ||||
|         # Compute the loss | ||||
|         if isinstance(preds, tuple): | ||||
|             loss = loss_fun(preds[0], labels) + cfg.NAS.AUX_WEIGHT * loss_fun(preds[1], labels) | ||||
|             preds = preds[0] | ||||
|         else: | ||||
|             loss = loss_fun(preds, labels) | ||||
|         # Perform the backward pass | ||||
|         optimizer.zero_grad() | ||||
|         loss.backward() | ||||
|         # Update the parameters | ||||
|         optimizer.step() | ||||
|         # Compute the errors | ||||
|         if cfg.TASK == "col": | ||||
|             preds = preds.permute(0, 2, 3, 1) | ||||
|             preds = preds.reshape(-1, preds.size(3)) | ||||
|             labels = labels.reshape(-1) | ||||
|             mb_size = inputs.size(0) * inputs.size(2) * inputs.size(3) * cfg.NUM_GPUS | ||||
|         else: | ||||
|             mb_size = inputs.size(0) * cfg.NUM_GPUS | ||||
|         if cfg.TASK == "seg": | ||||
|             # top1_err is in fact inter; top5_err is in fact union | ||||
|             top1_err, top5_err = meters.inter_union(preds, labels, cfg.MODEL.NUM_CLASSES) | ||||
|         else: | ||||
|             ks = [1, min(5, cfg.MODEL.NUM_CLASSES)]  # rot only has 4 classes | ||||
|             top1_err, top5_err = meters.topk_errors(preds, labels, ks) | ||||
|         # Combine the stats across the GPUs (no reduction if 1 GPU used) | ||||
|         loss, top1_err, top5_err = dist.scaled_all_reduce([loss, top1_err, top5_err]) | ||||
|         # Copy the stats from GPU to CPU (sync point) | ||||
|         loss = loss.item() | ||||
|         if cfg.TASK == "seg": | ||||
|             top1_err, top5_err = top1_err.cpu().numpy(), top5_err.cpu().numpy() | ||||
|         else: | ||||
|             top1_err, top5_err = top1_err.item(), top5_err.item() | ||||
|         train_meter.iter_toc() | ||||
|         # Update and log stats | ||||
|         train_meter.update_stats(top1_err, top5_err, loss, lr, mb_size) | ||||
|         train_meter.log_iter_stats(cur_epoch, cur_iter) | ||||
|         train_meter.iter_tic() | ||||
|     # Log epoch stats | ||||
|     train_meter.log_epoch_stats(cur_epoch) | ||||
|     train_meter.reset() | ||||
|  | ||||
|  | ||||
| def search_epoch(train_loader, model, loss_fun, optimizer, train_meter, cur_epoch): | ||||
|     """Performs one epoch of differentiable architecture search.""" | ||||
|     m = model.module if cfg.NUM_GPUS > 1 else model | ||||
|     # Shuffle the data | ||||
|     loader.shuffle(train_loader[0], cur_epoch) | ||||
|     loader.shuffle(train_loader[1], cur_epoch) | ||||
|     # Update the learning rate per epoch | ||||
|     if not cfg.OPTIM.ITER_LR: | ||||
|         lr = optim.get_epoch_lr(cur_epoch) | ||||
|         optim.set_lr(optimizer[0], lr) | ||||
|     # Enable training mode | ||||
|     model.train() | ||||
|     train_meter.iter_tic() | ||||
|     trainB_iter = iter(train_loader[1]) | ||||
|     for cur_iter, (inputs, labels) in enumerate(train_loader[0]): | ||||
|         # Update the learning rate per iter | ||||
|         if cfg.OPTIM.ITER_LR: | ||||
|             lr = optim.get_epoch_lr(cur_epoch + cur_iter / len(train_loader[0])) | ||||
|             optim.set_lr(optimizer[0], lr) | ||||
|         # Transfer the data to the current GPU device | ||||
|         inputs, labels = inputs.cuda(), labels.cuda(non_blocking=True) | ||||
|         # Update architecture | ||||
|         if cur_epoch + cur_iter / len(train_loader[0]) >= cfg.OPTIM.ARCH_EPOCH: | ||||
|             try: | ||||
|                 inputsB, labelsB = next(trainB_iter) | ||||
|             except StopIteration: | ||||
|                 trainB_iter = iter(train_loader[1]) | ||||
|                 inputsB, labelsB = next(trainB_iter) | ||||
|             inputsB, labelsB = inputsB.cuda(), labelsB.cuda(non_blocking=True) | ||||
|             optimizer[1].zero_grad() | ||||
|             loss = m._loss(inputsB, labelsB) | ||||
|             loss.backward() | ||||
|             optimizer[1].step() | ||||
|         # Perform the forward pass | ||||
|         preds = model(inputs) | ||||
|         # Compute the loss | ||||
|         loss = loss_fun(preds, labels) | ||||
|         # Perform the backward pass | ||||
|         optimizer[0].zero_grad() | ||||
|         loss.backward() | ||||
|         torch.nn.utils.clip_grad_norm(model.parameters(), 5.0) | ||||
|         # Update the parameters | ||||
|         optimizer[0].step() | ||||
|         # Compute the errors | ||||
|         if cfg.TASK == "col": | ||||
|             preds = preds.permute(0, 2, 3, 1) | ||||
|             preds = preds.reshape(-1, preds.size(3)) | ||||
|             labels = labels.reshape(-1) | ||||
|             mb_size = inputs.size(0) * inputs.size(2) * inputs.size(3) * cfg.NUM_GPUS | ||||
|         else: | ||||
|             mb_size = inputs.size(0) * cfg.NUM_GPUS | ||||
|         if cfg.TASK == "seg": | ||||
|             # top1_err is in fact inter; top5_err is in fact union | ||||
|             top1_err, top5_err = meters.inter_union(preds, labels, cfg.MODEL.NUM_CLASSES) | ||||
|         else: | ||||
|             ks = [1, min(5, cfg.MODEL.NUM_CLASSES)]  # rot only has 4 classes | ||||
|             top1_err, top5_err = meters.topk_errors(preds, labels, ks) | ||||
|         # Combine the stats across the GPUs (no reduction if 1 GPU used) | ||||
|         loss, top1_err, top5_err = dist.scaled_all_reduce([loss, top1_err, top5_err]) | ||||
|         # Copy the stats from GPU to CPU (sync point) | ||||
|         loss = loss.item() | ||||
|         if cfg.TASK == "seg": | ||||
|             top1_err, top5_err = top1_err.cpu().numpy(), top5_err.cpu().numpy() | ||||
|         else: | ||||
|             top1_err, top5_err = top1_err.item(), top5_err.item() | ||||
|         train_meter.iter_toc() | ||||
|         # Update and log stats | ||||
|         train_meter.update_stats(top1_err, top5_err, loss, lr, mb_size) | ||||
|         train_meter.log_iter_stats(cur_epoch, cur_iter) | ||||
|         train_meter.iter_tic() | ||||
|     # Log epoch stats | ||||
|     train_meter.log_epoch_stats(cur_epoch) | ||||
|     train_meter.reset() | ||||
|     # Log genotype | ||||
|     genotype = m.genotype() | ||||
|     logger.info("genotype = %s", genotype) | ||||
|     logger.info(F.softmax(m.net_.alphas_normal, dim=-1)) | ||||
|     logger.info(F.softmax(m.net_.alphas_reduce, dim=-1)) | ||||
|  | ||||
|  | ||||
| @torch.no_grad() | ||||
| def test_epoch(test_loader, model, test_meter, cur_epoch): | ||||
|     """Evaluates the model on the test set.""" | ||||
|     # Enable eval mode | ||||
|     model.eval() | ||||
|     test_meter.iter_tic() | ||||
|     for cur_iter, (inputs, labels) in enumerate(test_loader): | ||||
|         # Transfer the data to the current GPU device | ||||
|         inputs, labels = inputs.cuda(), labels.cuda(non_blocking=True) | ||||
|         # Compute the predictions | ||||
|         preds = model(inputs) | ||||
|         # Compute the errors | ||||
|         if cfg.TASK == "col": | ||||
|             preds = preds.permute(0, 2, 3, 1) | ||||
|             preds = preds.reshape(-1, preds.size(3)) | ||||
|             labels = labels.reshape(-1) | ||||
|             mb_size = inputs.size(0) * inputs.size(2) * inputs.size(3) * cfg.NUM_GPUS | ||||
|         else: | ||||
|             mb_size = inputs.size(0) * cfg.NUM_GPUS | ||||
|         if cfg.TASK == "seg": | ||||
|             # top1_err is in fact inter; top5_err is in fact union | ||||
|             top1_err, top5_err = meters.inter_union(preds, labels, cfg.MODEL.NUM_CLASSES) | ||||
|         else: | ||||
|             ks = [1, min(5, cfg.MODEL.NUM_CLASSES)]  # rot only has 4 classes | ||||
|             top1_err, top5_err = meters.topk_errors(preds, labels, ks) | ||||
|         # Combine the errors across the GPUs  (no reduction if 1 GPU used) | ||||
|         top1_err, top5_err = dist.scaled_all_reduce([top1_err, top5_err]) | ||||
|         # Copy the errors from GPU to CPU (sync point) | ||||
|         if cfg.TASK == "seg": | ||||
|             top1_err, top5_err = top1_err.cpu().numpy(), top5_err.cpu().numpy() | ||||
|         else: | ||||
|             top1_err, top5_err = top1_err.item(), top5_err.item() | ||||
|         test_meter.iter_toc() | ||||
|         # Update and log stats | ||||
|         test_meter.update_stats(top1_err, top5_err, mb_size) | ||||
|         test_meter.log_iter_stats(cur_epoch, cur_iter) | ||||
|         test_meter.iter_tic() | ||||
|     # Log epoch stats | ||||
|     test_meter.log_epoch_stats(cur_epoch) | ||||
|     test_meter.reset() | ||||
|  | ||||
|  | ||||
| def train_model(): | ||||
|     """Trains the model.""" | ||||
|     # Setup training/testing environment | ||||
|     setup_env() | ||||
|     # Construct the model, loss_fun, and optimizer | ||||
|     model = setup_model() | ||||
|     loss_fun = builders.build_loss_fun().cuda() | ||||
|     if "search" in cfg.MODEL.TYPE: | ||||
|         params_w = [v for k, v in model.named_parameters() if "alphas" not in k] | ||||
|         params_a = [v for k, v in model.named_parameters() if "alphas" in k] | ||||
|         optimizer_w = torch.optim.SGD( | ||||
|             params=params_w, | ||||
|             lr=cfg.OPTIM.BASE_LR, | ||||
|             momentum=cfg.OPTIM.MOMENTUM, | ||||
|             weight_decay=cfg.OPTIM.WEIGHT_DECAY, | ||||
|             dampening=cfg.OPTIM.DAMPENING, | ||||
|             nesterov=cfg.OPTIM.NESTEROV | ||||
|         ) | ||||
|         if cfg.OPTIM.ARCH_OPTIM == "adam": | ||||
|             optimizer_a = torch.optim.Adam( | ||||
|                 params=params_a, | ||||
|                 lr=cfg.OPTIM.ARCH_BASE_LR, | ||||
|                 betas=(0.5, 0.999), | ||||
|                 weight_decay=cfg.OPTIM.ARCH_WEIGHT_DECAY | ||||
|             ) | ||||
|         elif cfg.OPTIM.ARCH_OPTIM == "sgd": | ||||
|             optimizer_a = torch.optim.SGD( | ||||
|                 params=params_a, | ||||
|                 lr=cfg.OPTIM.ARCH_BASE_LR, | ||||
|                 momentum=cfg.OPTIM.MOMENTUM, | ||||
|                 weight_decay=cfg.OPTIM.ARCH_WEIGHT_DECAY, | ||||
|                 dampening=cfg.OPTIM.DAMPENING, | ||||
|                 nesterov=cfg.OPTIM.NESTEROV | ||||
|             ) | ||||
|         optimizer = [optimizer_w, optimizer_a] | ||||
|     else: | ||||
|         optimizer = optim.construct_optimizer(model) | ||||
|     # Load checkpoint or initial weights | ||||
|     start_epoch = 0 | ||||
|     if cfg.TRAIN.AUTO_RESUME and checkpoint.has_checkpoint(): | ||||
|         last_checkpoint = checkpoint.get_last_checkpoint() | ||||
|         checkpoint_epoch = checkpoint.load_checkpoint(last_checkpoint, model, optimizer) | ||||
|         logger.info("Loaded checkpoint from: {}".format(last_checkpoint)) | ||||
|         start_epoch = checkpoint_epoch + 1 | ||||
|     elif cfg.TRAIN.WEIGHTS: | ||||
|         checkpoint.load_checkpoint(cfg.TRAIN.WEIGHTS, model) | ||||
|         logger.info("Loaded initial weights from: {}".format(cfg.TRAIN.WEIGHTS)) | ||||
|     # Create data loaders and meters | ||||
|     if cfg.TRAIN.PORTION < 1: | ||||
|         if "search" in cfg.MODEL.TYPE: | ||||
|             train_loader = [loader._construct_loader( | ||||
|                 dataset_name=cfg.TRAIN.DATASET, | ||||
|                 split=cfg.TRAIN.SPLIT, | ||||
|                 batch_size=int(cfg.TRAIN.BATCH_SIZE / cfg.NUM_GPUS), | ||||
|                 shuffle=True, | ||||
|                 drop_last=True, | ||||
|                 portion=cfg.TRAIN.PORTION, | ||||
|                 side="l" | ||||
|             ), | ||||
|             loader._construct_loader( | ||||
|                 dataset_name=cfg.TRAIN.DATASET, | ||||
|                 split=cfg.TRAIN.SPLIT, | ||||
|                 batch_size=int(cfg.TRAIN.BATCH_SIZE / cfg.NUM_GPUS), | ||||
|                 shuffle=True, | ||||
|                 drop_last=True, | ||||
|                 portion=cfg.TRAIN.PORTION, | ||||
|                 side="r" | ||||
|             )] | ||||
|         else: | ||||
|             train_loader = loader._construct_loader( | ||||
|                 dataset_name=cfg.TRAIN.DATASET, | ||||
|                 split=cfg.TRAIN.SPLIT, | ||||
|                 batch_size=int(cfg.TRAIN.BATCH_SIZE / cfg.NUM_GPUS), | ||||
|                 shuffle=True, | ||||
|                 drop_last=True, | ||||
|                 portion=cfg.TRAIN.PORTION, | ||||
|                 side="l" | ||||
|             ) | ||||
|         test_loader = loader._construct_loader( | ||||
|             dataset_name=cfg.TRAIN.DATASET, | ||||
|             split=cfg.TRAIN.SPLIT, | ||||
|             batch_size=int(cfg.TRAIN.BATCH_SIZE / cfg.NUM_GPUS), | ||||
|             shuffle=False, | ||||
|             drop_last=False, | ||||
|             portion=cfg.TRAIN.PORTION, | ||||
|             side="r" | ||||
|         ) | ||||
|     else: | ||||
|         train_loader = loader.construct_train_loader() | ||||
|         test_loader = loader.construct_test_loader() | ||||
|     train_meter_type = meters.TrainMeterIoU if cfg.TASK == "seg" else meters.TrainMeter | ||||
|     test_meter_type = meters.TestMeterIoU if cfg.TASK == "seg" else meters.TestMeter | ||||
|     l = train_loader[0] if isinstance(train_loader, list) else train_loader | ||||
|     train_meter = train_meter_type(len(l)) | ||||
|     test_meter = test_meter_type(len(test_loader)) | ||||
|     # Compute model and loader timings | ||||
|     if start_epoch == 0 and cfg.PREC_TIME.NUM_ITER > 0: | ||||
|         l = train_loader[0] if isinstance(train_loader, list) else train_loader | ||||
|         benchmark.compute_time_full(model, loss_fun, l, test_loader) | ||||
|     # Perform the training loop | ||||
|     logger.info("Start epoch: {}".format(start_epoch + 1)) | ||||
|     for cur_epoch in range(start_epoch, cfg.OPTIM.MAX_EPOCH): | ||||
|         # Train for one epoch | ||||
|         f = search_epoch if "search" in cfg.MODEL.TYPE else train_epoch | ||||
|         f(train_loader, model, loss_fun, optimizer, train_meter, cur_epoch) | ||||
|         # Compute precise BN stats | ||||
|         if cfg.BN.USE_PRECISE_STATS: | ||||
|             net.compute_precise_bn_stats(model, train_loader) | ||||
|         # Save a checkpoint | ||||
|         if (cur_epoch + 1) % cfg.TRAIN.CHECKPOINT_PERIOD == 0: | ||||
|             checkpoint_file = checkpoint.save_checkpoint(model, optimizer, cur_epoch) | ||||
|             logger.info("Wrote checkpoint to: {}".format(checkpoint_file)) | ||||
|         # Evaluate the model | ||||
|         next_epoch = cur_epoch + 1 | ||||
|         if next_epoch % cfg.TRAIN.EVAL_PERIOD == 0 or next_epoch == cfg.OPTIM.MAX_EPOCH: | ||||
|             test_epoch(test_loader, model, test_meter, cur_epoch) | ||||
|  | ||||
|  | ||||
| def test_model(): | ||||
|     """Evaluates a trained model.""" | ||||
|     # Setup training/testing environment | ||||
|     setup_env() | ||||
|     # Construct the model | ||||
|     model = setup_model() | ||||
|     # Load model weights | ||||
|     checkpoint.load_checkpoint(cfg.TEST.WEIGHTS, model) | ||||
|     logger.info("Loaded model weights from: {}".format(cfg.TEST.WEIGHTS)) | ||||
|     # Create data loaders and meters | ||||
|     test_loader = loader.construct_test_loader() | ||||
|     test_meter = meters.TestMeter(len(test_loader)) | ||||
|     # Evaluate the model | ||||
|     test_epoch(test_loader, model, test_meter, 0) | ||||
|  | ||||
|  | ||||
| def time_model(): | ||||
|     """Times model and data loader.""" | ||||
|     # Setup training/testing environment | ||||
|     setup_env() | ||||
|     # Construct the model and loss_fun | ||||
|     model = setup_model() | ||||
|     loss_fun = builders.build_loss_fun().cuda() | ||||
|     # Create data loaders | ||||
|     train_loader = loader.construct_train_loader() | ||||
|     test_loader = loader.construct_test_loader() | ||||
|     # Compute model and loader timings | ||||
|     benchmark.compute_time_full(model, loss_fun, train_loader, test_loader) | ||||
							
								
								
									
										0
									
								
								graph_dit/naswot/pycls/models/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								graph_dit/naswot/pycls/models/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										406
									
								
								graph_dit/naswot/pycls/models/anynet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										406
									
								
								graph_dit/naswot/pycls/models/anynet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,406 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """AnyNet models.""" | ||||
|  | ||||
| import pycls.core.net as net | ||||
| import torch.nn as nn | ||||
| from pycls.core.config import cfg | ||||
|  | ||||
|  | ||||
| def get_stem_fun(stem_type): | ||||
|     """Retrieves the stem function by name.""" | ||||
|     stem_funs = { | ||||
|         "res_stem_cifar": ResStemCifar, | ||||
|         "res_stem_in": ResStemIN, | ||||
|         "simple_stem_in": SimpleStemIN, | ||||
|     } | ||||
|     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, | ||||
|     } | ||||
|     err_str = "Block type '{}' not supported" | ||||
|     assert block_type in block_funs.keys(), err_str.format(block_type) | ||||
|     return block_funs[block_type] | ||||
|  | ||||
|  | ||||
| class AnyHead(nn.Module): | ||||
|     """AnyNet head: AvgPool, 1x1.""" | ||||
|  | ||||
|     def __init__(self, w_in, nc): | ||||
|         super(AnyHead, self).__init__() | ||||
|         self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) | ||||
|         self.fc = nn.Linear(w_in, nc, bias=True) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         x = self.avg_pool(x) | ||||
|         x = x.view(x.size(0), -1) | ||||
|         x = self.fc(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, nc): | ||||
|         cx["h"], cx["w"] = 1, 1 | ||||
|         cx = net.complexity_conv2d(cx, w_in, nc, 1, 1, 0, bias=True) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class VanillaBlock(nn.Module): | ||||
|     """Vanilla block: [3x3 conv, BN, Relu] x2.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out, stride, bm=None, gw=None, se_r=None): | ||||
|         err_str = "Vanilla block does not support bm, gw, and se_r options" | ||||
|         assert bm is None and gw is None and se_r is None, err_str | ||||
|         super(VanillaBlock, self).__init__() | ||||
|         self.a = nn.Conv2d(w_in, w_out, 3, stride=stride, padding=1, bias=False) | ||||
|         self.a_bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.a_relu = nn.ReLU(inplace=cfg.MEM.RELU_INPLACE) | ||||
|         self.b = nn.Conv2d(w_out, w_out, 3, stride=1, padding=1, bias=False) | ||||
|         self.b_bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.b_relu = nn.ReLU(inplace=cfg.MEM.RELU_INPLACE) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         for layer in self.children(): | ||||
|             x = layer(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out, stride, bm=None, gw=None, se_r=None): | ||||
|         err_str = "Vanilla block does not support bm, gw, and se_r options" | ||||
|         assert bm is None and gw is None and se_r is None, err_str | ||||
|         cx = net.complexity_conv2d(cx, w_in, w_out, 3, stride, 1) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|         cx = net.complexity_conv2d(cx, w_out, w_out, 3, 1, 1) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class BasicTransform(nn.Module): | ||||
|     """Basic transformation: [3x3 conv, BN, Relu] x2.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out, stride): | ||||
|         super(BasicTransform, self).__init__() | ||||
|         self.a = nn.Conv2d(w_in, w_out, 3, stride=stride, padding=1, bias=False) | ||||
|         self.a_bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.a_relu = nn.ReLU(inplace=cfg.MEM.RELU_INPLACE) | ||||
|         self.b = nn.Conv2d(w_out, w_out, 3, stride=1, padding=1, bias=False) | ||||
|         self.b_bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.b_bn.final_bn = True | ||||
|  | ||||
|     def forward(self, x): | ||||
|         for layer in self.children(): | ||||
|             x = layer(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out, stride): | ||||
|         cx = net.complexity_conv2d(cx, w_in, w_out, 3, stride, 1) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|         cx = net.complexity_conv2d(cx, w_out, w_out, 3, 1, 1) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class ResBasicBlock(nn.Module): | ||||
|     """Residual basic block: x + F(x), F = basic transform.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out, stride, bm=None, gw=None, se_r=None): | ||||
|         err_str = "Basic transform does not support bm, gw, and se_r options" | ||||
|         assert bm is None and gw is None and se_r is None, err_str | ||||
|         super(ResBasicBlock, self).__init__() | ||||
|         self.proj_block = (w_in != w_out) or (stride != 1) | ||||
|         if self.proj_block: | ||||
|             self.proj = nn.Conv2d(w_in, w_out, 1, stride=stride, padding=0, bias=False) | ||||
|             self.bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.f = BasicTransform(w_in, w_out, stride) | ||||
|         self.relu = nn.ReLU(cfg.MEM.RELU_INPLACE) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         if self.proj_block: | ||||
|             x = self.bn(self.proj(x)) + self.f(x) | ||||
|         else: | ||||
|             x = x + self.f(x) | ||||
|         x = self.relu(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out, stride, bm=None, gw=None, se_r=None): | ||||
|         err_str = "Basic transform does not support bm, gw, and se_r options" | ||||
|         assert bm is None and gw is None and se_r is None, err_str | ||||
|         proj_block = (w_in != w_out) or (stride != 1) | ||||
|         if proj_block: | ||||
|             h, w = cx["h"], cx["w"] | ||||
|             cx = net.complexity_conv2d(cx, w_in, w_out, 1, stride, 0) | ||||
|             cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|             cx["h"], cx["w"] = h, w  # parallel branch | ||||
|         cx = BasicTransform.complexity(cx, w_in, w_out, stride) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class SE(nn.Module): | ||||
|     """Squeeze-and-Excitation (SE) block: AvgPool, FC, ReLU, FC, Sigmoid.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_se): | ||||
|         super(SE, self).__init__() | ||||
|         self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) | ||||
|         self.f_ex = nn.Sequential( | ||||
|             nn.Conv2d(w_in, w_se, 1, bias=True), | ||||
|             nn.ReLU(inplace=cfg.MEM.RELU_INPLACE), | ||||
|             nn.Conv2d(w_se, w_in, 1, bias=True), | ||||
|             nn.Sigmoid(), | ||||
|         ) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         return x * self.f_ex(self.avg_pool(x)) | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_se): | ||||
|         h, w = cx["h"], cx["w"] | ||||
|         cx["h"], cx["w"] = 1, 1 | ||||
|         cx = net.complexity_conv2d(cx, w_in, w_se, 1, 1, 0, bias=True) | ||||
|         cx = net.complexity_conv2d(cx, w_se, w_in, 1, 1, 0, bias=True) | ||||
|         cx["h"], cx["w"] = h, w | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class BottleneckTransform(nn.Module): | ||||
|     """Bottleneck transformation: 1x1, 3x3 [+SE], 1x1.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out, stride, bm, gw, se_r): | ||||
|         super(BottleneckTransform, self).__init__() | ||||
|         w_b = int(round(w_out * bm)) | ||||
|         g = w_b // gw | ||||
|         self.a = nn.Conv2d(w_in, w_b, 1, stride=1, padding=0, bias=False) | ||||
|         self.a_bn = nn.BatchNorm2d(w_b, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.a_relu = nn.ReLU(inplace=cfg.MEM.RELU_INPLACE) | ||||
|         self.b = nn.Conv2d(w_b, w_b, 3, stride=stride, padding=1, groups=g, bias=False) | ||||
|         self.b_bn = nn.BatchNorm2d(w_b, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.b_relu = nn.ReLU(inplace=cfg.MEM.RELU_INPLACE) | ||||
|         if se_r: | ||||
|             w_se = int(round(w_in * se_r)) | ||||
|             self.se = SE(w_b, w_se) | ||||
|         self.c = nn.Conv2d(w_b, w_out, 1, stride=1, padding=0, bias=False) | ||||
|         self.c_bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.c_bn.final_bn = True | ||||
|  | ||||
|     def forward(self, x): | ||||
|         for layer in self.children(): | ||||
|             x = layer(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out, stride, bm, gw, se_r): | ||||
|         w_b = int(round(w_out * bm)) | ||||
|         g = w_b // gw | ||||
|         cx = net.complexity_conv2d(cx, w_in, w_b, 1, 1, 0) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_b) | ||||
|         cx = net.complexity_conv2d(cx, w_b, w_b, 3, stride, 1, g) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_b) | ||||
|         if se_r: | ||||
|             w_se = int(round(w_in * se_r)) | ||||
|             cx = SE.complexity(cx, w_b, w_se) | ||||
|         cx = net.complexity_conv2d(cx, w_b, w_out, 1, 1, 0) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class ResBottleneckBlock(nn.Module): | ||||
|     """Residual bottleneck block: x + F(x), F = bottleneck transform.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out, stride, bm=1.0, gw=1, se_r=None): | ||||
|         super(ResBottleneckBlock, self).__init__() | ||||
|         # Use skip connection with projection if shape changes | ||||
|         self.proj_block = (w_in != w_out) or (stride != 1) | ||||
|         if self.proj_block: | ||||
|             self.proj = nn.Conv2d(w_in, w_out, 1, stride=stride, padding=0, bias=False) | ||||
|             self.bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.f = BottleneckTransform(w_in, w_out, stride, bm, gw, se_r) | ||||
|         self.relu = nn.ReLU(cfg.MEM.RELU_INPLACE) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         if self.proj_block: | ||||
|             x = self.bn(self.proj(x)) + self.f(x) | ||||
|         else: | ||||
|             x = x + self.f(x) | ||||
|         x = self.relu(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out, stride, bm=1.0, gw=1, se_r=None): | ||||
|         proj_block = (w_in != w_out) or (stride != 1) | ||||
|         if proj_block: | ||||
|             h, w = cx["h"], cx["w"] | ||||
|             cx = net.complexity_conv2d(cx, w_in, w_out, 1, stride, 0) | ||||
|             cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|             cx["h"], cx["w"] = h, w  # parallel branch | ||||
|         cx = BottleneckTransform.complexity(cx, w_in, w_out, stride, bm, gw, se_r) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class ResStemCifar(nn.Module): | ||||
|     """ResNet stem for CIFAR: 3x3, BN, ReLU.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out): | ||||
|         super(ResStemCifar, self).__init__() | ||||
|         self.conv = nn.Conv2d(w_in, w_out, 3, stride=1, padding=1, bias=False) | ||||
|         self.bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.relu = nn.ReLU(cfg.MEM.RELU_INPLACE) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         for layer in self.children(): | ||||
|             x = layer(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out): | ||||
|         cx = net.complexity_conv2d(cx, w_in, w_out, 3, 1, 1) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class ResStemIN(nn.Module): | ||||
|     """ResNet stem for ImageNet: 7x7, BN, ReLU, MaxPool.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out): | ||||
|         super(ResStemIN, self).__init__() | ||||
|         self.conv = nn.Conv2d(w_in, w_out, 7, stride=2, padding=3, bias=False) | ||||
|         self.bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.relu = nn.ReLU(cfg.MEM.RELU_INPLACE) | ||||
|         self.pool = nn.MaxPool2d(3, stride=2, padding=1) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         for layer in self.children(): | ||||
|             x = layer(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out): | ||||
|         cx = net.complexity_conv2d(cx, w_in, w_out, 7, 2, 3) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|         cx = net.complexity_maxpool2d(cx, 3, 2, 1) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class SimpleStemIN(nn.Module): | ||||
|     """Simple stem for ImageNet: 3x3, BN, ReLU.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out): | ||||
|         super(SimpleStemIN, self).__init__() | ||||
|         self.conv = nn.Conv2d(w_in, w_out, 3, stride=2, padding=1, bias=False) | ||||
|         self.bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.relu = nn.ReLU(cfg.MEM.RELU_INPLACE) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         for layer in self.children(): | ||||
|             x = layer(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out): | ||||
|         cx = net.complexity_conv2d(cx, w_in, w_out, 3, 2, 1) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class AnyStage(nn.Module): | ||||
|     """AnyNet stage (sequence of blocks w/ the same output shape).""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out, stride, d, block_fun, bm, gw, se_r): | ||||
|         super(AnyStage, self).__init__() | ||||
|         for i in range(d): | ||||
|             b_stride = stride if i == 0 else 1 | ||||
|             b_w_in = w_in if i == 0 else w_out | ||||
|             name = "b{}".format(i + 1) | ||||
|             self.add_module(name, block_fun(b_w_in, w_out, b_stride, bm, gw, se_r)) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         for block in self.children(): | ||||
|             x = block(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out, stride, d, block_fun, bm, gw, se_r): | ||||
|         for i in range(d): | ||||
|             b_stride = stride if i == 0 else 1 | ||||
|             b_w_in = w_in if i == 0 else w_out | ||||
|             cx = block_fun.complexity(cx, b_w_in, w_out, b_stride, bm, gw, se_r) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class AnyNet(nn.Module): | ||||
|     """AnyNet model.""" | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_args(): | ||||
|         return { | ||||
|             "stem_type": cfg.ANYNET.STEM_TYPE, | ||||
|             "stem_w": cfg.ANYNET.STEM_W, | ||||
|             "block_type": cfg.ANYNET.BLOCK_TYPE, | ||||
|             "ds": cfg.ANYNET.DEPTHS, | ||||
|             "ws": cfg.ANYNET.WIDTHS, | ||||
|             "ss": cfg.ANYNET.STRIDES, | ||||
|             "bms": cfg.ANYNET.BOT_MULS, | ||||
|             "gws": cfg.ANYNET.GROUP_WS, | ||||
|             "se_r": cfg.ANYNET.SE_R if cfg.ANYNET.SE_ON else None, | ||||
|             "nc": cfg.MODEL.NUM_CLASSES, | ||||
|         } | ||||
|  | ||||
|     def __init__(self, **kwargs): | ||||
|         super(AnyNet, self).__init__() | ||||
|         kwargs = self.get_args() if not kwargs else kwargs | ||||
|         #print(kwargs) | ||||
|         self._construct(**kwargs) | ||||
|         self.apply(net.init_weights) | ||||
|  | ||||
|     def _construct(self, stem_type, stem_w, block_type, ds, ws, ss, bms, gws, se_r, nc): | ||||
|         # Generate dummy bot muls and gs for models that do not use them | ||||
|         bms = bms if bms else [None for _d in ds] | ||||
|         gws = gws if gws else [None for _d in ds] | ||||
|         stage_params = list(zip(ds, ws, ss, bms, gws)) | ||||
|         stem_fun = get_stem_fun(stem_type) | ||||
|         self.stem = stem_fun(3, stem_w) | ||||
|         block_fun = get_block_fun(block_type) | ||||
|         prev_w = stem_w | ||||
|         for i, (d, w, s, bm, gw) in enumerate(stage_params): | ||||
|             name = "s{}".format(i + 1) | ||||
|             self.add_module(name, AnyStage(prev_w, w, s, d, block_fun, bm, gw, se_r)) | ||||
|             prev_w = w | ||||
|         self.head = AnyHead(w_in=prev_w, nc=nc) | ||||
|  | ||||
|     def forward(self, x, get_ints=False): | ||||
|         for module in self.children(): | ||||
|             x = module(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, **kwargs): | ||||
|         """Computes model complexity. If you alter the model, make sure to update.""" | ||||
|         kwargs = AnyNet.get_args() if not kwargs else kwargs | ||||
|         return AnyNet._complexity(cx, **kwargs) | ||||
|  | ||||
|     @staticmethod | ||||
|     def _complexity(cx, stem_type, stem_w, block_type, ds, ws, ss, bms, gws, se_r, nc): | ||||
|         bms = bms if bms else [None for _d in ds] | ||||
|         gws = gws if gws else [None for _d in ds] | ||||
|         stage_params = list(zip(ds, ws, ss, bms, gws)) | ||||
|         stem_fun = get_stem_fun(stem_type) | ||||
|         cx = stem_fun.complexity(cx, 3, stem_w) | ||||
|         block_fun = get_block_fun(block_type) | ||||
|         prev_w = stem_w | ||||
|         for d, w, s, bm, gw in stage_params: | ||||
|             cx = AnyStage.complexity(cx, prev_w, w, s, d, block_fun, bm, gw, se_r) | ||||
|             prev_w = w | ||||
|         cx = AnyHead.complexity(cx, prev_w, nc) | ||||
|         return cx | ||||
							
								
								
									
										108
									
								
								graph_dit/naswot/pycls/models/common.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								graph_dit/naswot/pycls/models/common.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| import torch | ||||
| import torch.nn as nn | ||||
|  | ||||
| from pycls.core.config import cfg | ||||
|  | ||||
|  | ||||
| def Preprocess(x): | ||||
|     if cfg.TASK == 'jig': | ||||
|         assert len(x.shape) == 5, 'Wrong tensor dimension for jigsaw' | ||||
|         assert x.shape[1] == cfg.JIGSAW_GRID ** 2, 'Wrong grid for jigsaw' | ||||
|         x = x.view([x.shape[0] * x.shape[1], x.shape[2], x.shape[3], x.shape[4]]) | ||||
|     return x | ||||
|  | ||||
|  | ||||
| class Classifier(nn.Module): | ||||
|     def __init__(self, channels, num_classes): | ||||
|         super(Classifier, self).__init__() | ||||
|         if cfg.TASK == 'jig': | ||||
|             self.jig_sq = cfg.JIGSAW_GRID ** 2 | ||||
|             self.pooling = nn.AdaptiveAvgPool2d(1) | ||||
|             self.classifier = nn.Linear(channels * self.jig_sq, num_classes) | ||||
|         elif cfg.TASK == 'col': | ||||
|             self.classifier = nn.Conv2d(channels, num_classes, kernel_size=1, stride=1) | ||||
|         elif cfg.TASK == 'seg': | ||||
|             self.classifier = ASPP(channels, cfg.MODEL.ASPP_CHANNELS, num_classes, cfg.MODEL.ASPP_RATES) | ||||
|         else: | ||||
|             self.pooling = nn.AdaptiveAvgPool2d(1) | ||||
|             self.classifier = nn.Linear(channels, num_classes) | ||||
|  | ||||
|     def forward(self, x, shape): | ||||
|         if cfg.TASK == 'jig': | ||||
|             x = self.pooling(x) | ||||
|             x = x.view([x.shape[0] // self.jig_sq, x.shape[1] * self.jig_sq, x.shape[2], x.shape[3]]) | ||||
|             x = self.classifier(x.view(x.size(0), -1)) | ||||
|         elif cfg.TASK in ['col', 'seg']: | ||||
|             x = self.classifier(x) | ||||
|             x = nn.Upsample(shape, mode='bilinear', align_corners=True)(x) | ||||
|         else: | ||||
|             x = self.pooling(x) | ||||
|             x = self.classifier(x.view(x.size(0), -1)) | ||||
|         return x | ||||
|  | ||||
|  | ||||
| class ASPP(nn.Module): | ||||
|     def __init__(self, in_channels, out_channels, num_classes, rates): | ||||
|         super(ASPP, self).__init__() | ||||
|         assert len(rates) in [1, 3] | ||||
|         self.rates = rates | ||||
|         self.global_pooling = nn.AdaptiveAvgPool2d(1) | ||||
|         self.aspp1 = nn.Sequential( | ||||
|             nn.Conv2d(in_channels, out_channels, 1, bias=False), | ||||
|             nn.BatchNorm2d(out_channels), | ||||
|             nn.ReLU(inplace=True) | ||||
|         ) | ||||
|         self.aspp2 = nn.Sequential( | ||||
|             nn.Conv2d(in_channels, out_channels, 3, dilation=rates[0], | ||||
|                 padding=rates[0], bias=False), | ||||
|             nn.BatchNorm2d(out_channels), | ||||
|             nn.ReLU(inplace=True) | ||||
|         ) | ||||
|         if len(self.rates) == 3: | ||||
|             self.aspp3 = nn.Sequential( | ||||
|                 nn.Conv2d(in_channels, out_channels, 3, dilation=rates[1], | ||||
|                     padding=rates[1], bias=False), | ||||
|                 nn.BatchNorm2d(out_channels), | ||||
|                 nn.ReLU(inplace=True) | ||||
|             ) | ||||
|             self.aspp4 = nn.Sequential( | ||||
|                 nn.Conv2d(in_channels, out_channels, 3, dilation=rates[2], | ||||
|                     padding=rates[2], bias=False), | ||||
|                 nn.BatchNorm2d(out_channels), | ||||
|                 nn.ReLU(inplace=True) | ||||
|             ) | ||||
|         self.aspp5 = nn.Sequential( | ||||
|             nn.Conv2d(in_channels, out_channels, 1, bias=False), | ||||
|             nn.BatchNorm2d(out_channels), | ||||
|             nn.ReLU(inplace=True) | ||||
|         ) | ||||
|         self.classifier = nn.Sequential( | ||||
|             nn.Conv2d(out_channels * (len(rates) + 2), out_channels, 1, | ||||
|                 bias=False), | ||||
|             nn.BatchNorm2d(out_channels), | ||||
|             nn.ReLU(inplace=True), | ||||
|             nn.Conv2d(out_channels, num_classes, 1) | ||||
|         ) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         x1 = self.aspp1(x) | ||||
|         x2 = self.aspp2(x) | ||||
|         x5 = self.global_pooling(x) | ||||
|         x5 = self.aspp5(x5) | ||||
|         x5 = nn.Upsample((x.shape[2], x.shape[3]), mode='bilinear', | ||||
|                 align_corners=True)(x5) | ||||
|         if len(self.rates) == 3: | ||||
|             x3 = self.aspp3(x) | ||||
|             x4 = self.aspp4(x) | ||||
|             x = torch.cat((x1, x2, x3, x4, x5), 1) | ||||
|         else: | ||||
|             x = torch.cat((x1, x2, x5), 1) | ||||
|         x = self.classifier(x) | ||||
|         return x | ||||
							
								
								
									
										232
									
								
								graph_dit/naswot/pycls/models/effnet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										232
									
								
								graph_dit/naswot/pycls/models/effnet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,232 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """EfficientNet models.""" | ||||
|  | ||||
| import pycls.core.net as net | ||||
| import torch | ||||
| import torch.nn as nn | ||||
| from pycls.core.config import cfg | ||||
|  | ||||
|  | ||||
| class EffHead(nn.Module): | ||||
|     """EfficientNet head: 1x1, BN, Swish, AvgPool, Dropout, FC.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out, nc): | ||||
|         super(EffHead, self).__init__() | ||||
|         self.conv = nn.Conv2d(w_in, w_out, 1, stride=1, padding=0, bias=False) | ||||
|         self.conv_bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.conv_swish = Swish() | ||||
|         self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) | ||||
|         if cfg.EN.DROPOUT_RATIO > 0.0: | ||||
|             self.dropout = nn.Dropout(p=cfg.EN.DROPOUT_RATIO) | ||||
|         self.fc = nn.Linear(w_out, nc, bias=True) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         x = self.conv_swish(self.conv_bn(self.conv(x))) | ||||
|         x = self.avg_pool(x) | ||||
|         x = x.view(x.size(0), -1) | ||||
|         x = self.dropout(x) if hasattr(self, "dropout") else x | ||||
|         x = self.fc(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out, nc): | ||||
|         cx = net.complexity_conv2d(cx, w_in, w_out, 1, 1, 0) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|         cx["h"], cx["w"] = 1, 1 | ||||
|         cx = net.complexity_conv2d(cx, w_out, nc, 1, 1, 0, bias=True) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class Swish(nn.Module): | ||||
|     """Swish activation function: x * sigmoid(x).""" | ||||
|  | ||||
|     def __init__(self): | ||||
|         super(Swish, self).__init__() | ||||
|  | ||||
|     def forward(self, x): | ||||
|         return x * torch.sigmoid(x) | ||||
|  | ||||
|  | ||||
| class SE(nn.Module): | ||||
|     """Squeeze-and-Excitation (SE) block w/ Swish: AvgPool, FC, Swish, FC, Sigmoid.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_se): | ||||
|         super(SE, self).__init__() | ||||
|         self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) | ||||
|         self.f_ex = nn.Sequential( | ||||
|             nn.Conv2d(w_in, w_se, 1, bias=True), | ||||
|             Swish(), | ||||
|             nn.Conv2d(w_se, w_in, 1, bias=True), | ||||
|             nn.Sigmoid(), | ||||
|         ) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         return x * self.f_ex(self.avg_pool(x)) | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_se): | ||||
|         h, w = cx["h"], cx["w"] | ||||
|         cx["h"], cx["w"] = 1, 1 | ||||
|         cx = net.complexity_conv2d(cx, w_in, w_se, 1, 1, 0, bias=True) | ||||
|         cx = net.complexity_conv2d(cx, w_se, w_in, 1, 1, 0, bias=True) | ||||
|         cx["h"], cx["w"] = h, w | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class MBConv(nn.Module): | ||||
|     """Mobile inverted bottleneck block w/ SE (MBConv).""" | ||||
|  | ||||
|     def __init__(self, w_in, exp_r, kernel, stride, se_r, w_out): | ||||
|         # expansion, 3x3 dwise, BN, Swish, SE, 1x1, BN, skip_connection | ||||
|         super(MBConv, self).__init__() | ||||
|         self.exp = None | ||||
|         w_exp = int(w_in * exp_r) | ||||
|         if w_exp != w_in: | ||||
|             self.exp = nn.Conv2d(w_in, w_exp, 1, stride=1, padding=0, bias=False) | ||||
|             self.exp_bn = nn.BatchNorm2d(w_exp, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|             self.exp_swish = Swish() | ||||
|         dwise_args = {"groups": w_exp, "padding": (kernel - 1) // 2, "bias": False} | ||||
|         self.dwise = nn.Conv2d(w_exp, w_exp, kernel, stride=stride, **dwise_args) | ||||
|         self.dwise_bn = nn.BatchNorm2d(w_exp, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.dwise_swish = Swish() | ||||
|         self.se = SE(w_exp, int(w_in * se_r)) | ||||
|         self.lin_proj = nn.Conv2d(w_exp, w_out, 1, stride=1, padding=0, bias=False) | ||||
|         self.lin_proj_bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         # Skip connection if in and out shapes are the same (MN-V2 style) | ||||
|         self.has_skip = stride == 1 and w_in == w_out | ||||
|  | ||||
|     def forward(self, x): | ||||
|         f_x = x | ||||
|         if self.exp: | ||||
|             f_x = self.exp_swish(self.exp_bn(self.exp(f_x))) | ||||
|         f_x = self.dwise_swish(self.dwise_bn(self.dwise(f_x))) | ||||
|         f_x = self.se(f_x) | ||||
|         f_x = self.lin_proj_bn(self.lin_proj(f_x)) | ||||
|         if self.has_skip: | ||||
|             if self.training and cfg.EN.DC_RATIO > 0.0: | ||||
|                 f_x = net.drop_connect(f_x, cfg.EN.DC_RATIO) | ||||
|             f_x = x + f_x | ||||
|         return f_x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, exp_r, kernel, stride, se_r, w_out): | ||||
|         w_exp = int(w_in * exp_r) | ||||
|         if w_exp != w_in: | ||||
|             cx = net.complexity_conv2d(cx, w_in, w_exp, 1, 1, 0) | ||||
|             cx = net.complexity_batchnorm2d(cx, w_exp) | ||||
|         padding = (kernel - 1) // 2 | ||||
|         cx = net.complexity_conv2d(cx, w_exp, w_exp, kernel, stride, padding, w_exp) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_exp) | ||||
|         cx = SE.complexity(cx, w_exp, int(w_in * se_r)) | ||||
|         cx = net.complexity_conv2d(cx, w_exp, w_out, 1, 1, 0) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class EffStage(nn.Module): | ||||
|     """EfficientNet stage.""" | ||||
|  | ||||
|     def __init__(self, w_in, exp_r, kernel, stride, se_r, w_out, d): | ||||
|         super(EffStage, self).__init__() | ||||
|         for i in range(d): | ||||
|             b_stride = stride if i == 0 else 1 | ||||
|             b_w_in = w_in if i == 0 else w_out | ||||
|             name = "b{}".format(i + 1) | ||||
|             self.add_module(name, MBConv(b_w_in, exp_r, kernel, b_stride, se_r, w_out)) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         for block in self.children(): | ||||
|             x = block(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, exp_r, kernel, stride, se_r, w_out, d): | ||||
|         for i in range(d): | ||||
|             b_stride = stride if i == 0 else 1 | ||||
|             b_w_in = w_in if i == 0 else w_out | ||||
|             cx = MBConv.complexity(cx, b_w_in, exp_r, kernel, b_stride, se_r, w_out) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class StemIN(nn.Module): | ||||
|     """EfficientNet stem for ImageNet: 3x3, BN, Swish.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out): | ||||
|         super(StemIN, self).__init__() | ||||
|         self.conv = nn.Conv2d(w_in, w_out, 3, stride=2, padding=1, bias=False) | ||||
|         self.bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.swish = Swish() | ||||
|  | ||||
|     def forward(self, x): | ||||
|         for layer in self.children(): | ||||
|             x = layer(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out): | ||||
|         cx = net.complexity_conv2d(cx, w_in, w_out, 3, 2, 1) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class EffNet(nn.Module): | ||||
|     """EfficientNet model.""" | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_args(): | ||||
|         return { | ||||
|             "stem_w": cfg.EN.STEM_W, | ||||
|             "ds": cfg.EN.DEPTHS, | ||||
|             "ws": cfg.EN.WIDTHS, | ||||
|             "exp_rs": cfg.EN.EXP_RATIOS, | ||||
|             "se_r": cfg.EN.SE_R, | ||||
|             "ss": cfg.EN.STRIDES, | ||||
|             "ks": cfg.EN.KERNELS, | ||||
|             "head_w": cfg.EN.HEAD_W, | ||||
|             "nc": cfg.MODEL.NUM_CLASSES, | ||||
|         } | ||||
|  | ||||
|     def __init__(self): | ||||
|         err_str = "Dataset {} is not supported" | ||||
|         assert cfg.TRAIN.DATASET in ["imagenet"], err_str.format(cfg.TRAIN.DATASET) | ||||
|         assert cfg.TEST.DATASET in ["imagenet"], err_str.format(cfg.TEST.DATASET) | ||||
|         super(EffNet, self).__init__() | ||||
|         self._construct(**EffNet.get_args()) | ||||
|         self.apply(net.init_weights) | ||||
|  | ||||
|     def _construct(self, stem_w, ds, ws, exp_rs, se_r, ss, ks, head_w, nc): | ||||
|         stage_params = list(zip(ds, ws, exp_rs, ss, ks)) | ||||
|         self.stem = StemIN(3, stem_w) | ||||
|         prev_w = stem_w | ||||
|         for i, (d, w, exp_r, stride, kernel) in enumerate(stage_params): | ||||
|             name = "s{}".format(i + 1) | ||||
|             self.add_module(name, EffStage(prev_w, exp_r, kernel, stride, se_r, w, d)) | ||||
|             prev_w = w | ||||
|         self.head = EffHead(prev_w, head_w, nc) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         for module in self.children(): | ||||
|             x = module(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx): | ||||
|         """Computes model complexity. If you alter the model, make sure to update.""" | ||||
|         return EffNet._complexity(cx, **EffNet.get_args()) | ||||
|  | ||||
|     @staticmethod | ||||
|     def _complexity(cx, stem_w, ds, ws, exp_rs, se_r, ss, ks, head_w, nc): | ||||
|         stage_params = list(zip(ds, ws, exp_rs, ss, ks)) | ||||
|         cx = StemIN.complexity(cx, 3, stem_w) | ||||
|         prev_w = stem_w | ||||
|         for d, w, exp_r, stride, kernel in stage_params: | ||||
|             cx = EffStage.complexity(cx, prev_w, exp_r, kernel, stride, se_r, w, d) | ||||
|             prev_w = w | ||||
|         cx = EffHead.complexity(cx, prev_w, head_w, nc) | ||||
|         return cx | ||||
							
								
								
									
										634
									
								
								graph_dit/naswot/pycls/models/nas/genotypes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										634
									
								
								graph_dit/naswot/pycls/models/nas/genotypes.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,634 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """NAS genotypes (adopted from DARTS).""" | ||||
|  | ||||
| from collections import namedtuple | ||||
|  | ||||
|  | ||||
| Genotype = namedtuple('Genotype', 'normal normal_concat reduce reduce_concat') | ||||
|  | ||||
|  | ||||
| # NASNet ops | ||||
| NASNET_OPS = [ | ||||
|     'skip_connect', | ||||
|     'conv_3x1_1x3', | ||||
|     'conv_7x1_1x7', | ||||
|     'dil_conv_3x3', | ||||
|     'avg_pool_3x3', | ||||
|     'max_pool_3x3', | ||||
|     'max_pool_5x5', | ||||
|     'max_pool_7x7', | ||||
|     'conv_1x1', | ||||
|     'conv_3x3', | ||||
|     'sep_conv_3x3', | ||||
|     'sep_conv_5x5', | ||||
|     'sep_conv_7x7', | ||||
| ] | ||||
|  | ||||
| # ENAS ops | ||||
| ENAS_OPS = [ | ||||
|     'skip_connect', | ||||
|     'sep_conv_3x3', | ||||
|     'sep_conv_5x5', | ||||
|     'avg_pool_3x3', | ||||
|     'max_pool_3x3', | ||||
| ] | ||||
|  | ||||
| # AmoebaNet ops | ||||
| AMOEBA_OPS = [ | ||||
|     'skip_connect', | ||||
|     'sep_conv_3x3', | ||||
|     'sep_conv_5x5', | ||||
|     'sep_conv_7x7', | ||||
|     'avg_pool_3x3', | ||||
|     'max_pool_3x3', | ||||
|     'dil_sep_conv_3x3', | ||||
|     'conv_7x1_1x7', | ||||
| ] | ||||
|  | ||||
| # NAO ops | ||||
| NAO_OPS = [ | ||||
|     'skip_connect', | ||||
|     'conv_1x1', | ||||
|     'conv_3x3', | ||||
|     'conv_3x1_1x3', | ||||
|     'conv_7x1_1x7', | ||||
|     'max_pool_2x2', | ||||
|     'max_pool_3x3', | ||||
|     'max_pool_5x5', | ||||
|     'avg_pool_2x2', | ||||
|     'avg_pool_3x3', | ||||
|     'avg_pool_5x5', | ||||
| ] | ||||
|  | ||||
| # PNAS ops | ||||
| PNAS_OPS = [ | ||||
|     'sep_conv_3x3', | ||||
|     'sep_conv_5x5', | ||||
|     'sep_conv_7x7', | ||||
|     'conv_7x1_1x7', | ||||
|     'skip_connect', | ||||
|     'avg_pool_3x3', | ||||
|     'max_pool_3x3', | ||||
|     'dil_conv_3x3', | ||||
| ] | ||||
|  | ||||
| # DARTS ops | ||||
| DARTS_OPS = [ | ||||
|     'none', | ||||
|     'max_pool_3x3', | ||||
|     'avg_pool_3x3', | ||||
|     'skip_connect', | ||||
|     'sep_conv_3x3', | ||||
|     'sep_conv_5x5', | ||||
|     'dil_conv_3x3', | ||||
|     'dil_conv_5x5', | ||||
| ] | ||||
|  | ||||
|  | ||||
| NASNet = Genotype( | ||||
|     normal=[ | ||||
|         ('sep_conv_5x5', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_5x5', 0), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('avg_pool_3x3', 1), | ||||
|         ('skip_connect', 0), | ||||
|         ('avg_pool_3x3', 0), | ||||
|         ('avg_pool_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('skip_connect', 1), | ||||
|     ], | ||||
|     normal_concat=[2, 3, 4, 5, 6], | ||||
|     reduce=[ | ||||
|         ('sep_conv_5x5', 1), | ||||
|         ('sep_conv_7x7', 0), | ||||
|         ('max_pool_3x3', 1), | ||||
|         ('sep_conv_7x7', 0), | ||||
|         ('avg_pool_3x3', 1), | ||||
|         ('sep_conv_5x5', 0), | ||||
|         ('skip_connect', 3), | ||||
|         ('avg_pool_3x3', 2), | ||||
|         ('sep_conv_3x3', 2), | ||||
|         ('max_pool_3x3', 1), | ||||
|     ], | ||||
|     reduce_concat=[4, 5, 6], | ||||
| ) | ||||
|  | ||||
|  | ||||
| PNASNet = Genotype( | ||||
|     normal=[ | ||||
|         ('sep_conv_5x5', 0), | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('sep_conv_7x7', 1), | ||||
|         ('max_pool_3x3', 1), | ||||
|         ('sep_conv_5x5', 1), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 4), | ||||
|         ('max_pool_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('skip_connect', 1), | ||||
|     ], | ||||
|     normal_concat=[2, 3, 4, 5, 6], | ||||
|     reduce=[ | ||||
|         ('sep_conv_5x5', 0), | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('sep_conv_7x7', 1), | ||||
|         ('max_pool_3x3', 1), | ||||
|         ('sep_conv_5x5', 1), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 4), | ||||
|         ('max_pool_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('skip_connect', 1), | ||||
|     ], | ||||
|     reduce_concat=[2, 3, 4, 5, 6], | ||||
| ) | ||||
|  | ||||
|  | ||||
| AmoebaNet = Genotype( | ||||
|     normal=[ | ||||
|         ('avg_pool_3x3', 0), | ||||
|         ('max_pool_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_5x5', 2), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('avg_pool_3x3', 3), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('skip_connect', 1), | ||||
|         ('skip_connect', 0), | ||||
|         ('avg_pool_3x3', 1), | ||||
|     ], | ||||
|     normal_concat=[4, 5, 6], | ||||
|     reduce=[ | ||||
|         ('avg_pool_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('sep_conv_7x7', 2), | ||||
|         ('sep_conv_7x7', 0), | ||||
|         ('avg_pool_3x3', 1), | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('max_pool_3x3', 1), | ||||
|         ('conv_7x1_1x7', 0), | ||||
|         ('sep_conv_3x3', 5), | ||||
|     ], | ||||
|     reduce_concat=[3, 4, 6] | ||||
| ) | ||||
|  | ||||
|  | ||||
| DARTS_V1 = Genotype( | ||||
|     normal=[ | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('skip_connect', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('skip_connect', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('skip_connect', 2) | ||||
|     ], | ||||
|     normal_concat=[2, 3, 4, 5], | ||||
|     reduce=[ | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('max_pool_3x3', 1), | ||||
|         ('skip_connect', 2), | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('skip_connect', 2), | ||||
|         ('skip_connect', 2), | ||||
|         ('avg_pool_3x3', 0) | ||||
|     ], | ||||
|     reduce_concat=[2, 3, 4, 5] | ||||
| ) | ||||
|  | ||||
|  | ||||
| DARTS_V2 = Genotype( | ||||
|     normal=[ | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('skip_connect', 0), | ||||
|         ('skip_connect', 0), | ||||
|         ('dil_conv_3x3', 2) | ||||
|     ], | ||||
|     normal_concat=[2, 3, 4, 5], | ||||
|     reduce=[ | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('max_pool_3x3', 1), | ||||
|         ('skip_connect', 2), | ||||
|         ('max_pool_3x3', 1), | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('skip_connect', 2), | ||||
|         ('skip_connect', 2), | ||||
|         ('max_pool_3x3', 1) | ||||
|     ], | ||||
|     reduce_concat=[2, 3, 4, 5] | ||||
| ) | ||||
|  | ||||
| PDARTS = Genotype( | ||||
|     normal=[ | ||||
|         ('skip_connect', 0), | ||||
|         ('dil_conv_3x3', 1), | ||||
|         ('skip_connect', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 3), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('dil_conv_5x5', 4) | ||||
|     ], | ||||
|     normal_concat=range(2, 6), | ||||
|     reduce=[ | ||||
|         ('avg_pool_3x3', 0), | ||||
|         ('sep_conv_5x5', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('dil_conv_5x5', 2), | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('dil_conv_3x3', 1), | ||||
|         ('dil_conv_3x3', 1), | ||||
|         ('dil_conv_5x5', 3) | ||||
|     ], | ||||
|     reduce_concat=range(2, 6) | ||||
| ) | ||||
|  | ||||
| PCDARTS_C10 = Genotype( | ||||
|     normal=[ | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('skip_connect', 0), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('dil_conv_3x3', 1), | ||||
|         ('sep_conv_5x5', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('avg_pool_3x3', 0), | ||||
|         ('dil_conv_3x3', 1) | ||||
|     ], | ||||
|     normal_concat=range(2, 6), | ||||
|     reduce=[ | ||||
|         ('sep_conv_5x5', 1), | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('sep_conv_5x5', 1), | ||||
|         ('sep_conv_5x5', 2), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 3), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 2) | ||||
|     ], | ||||
|     reduce_concat=range(2, 6) | ||||
| ) | ||||
|  | ||||
| PCDARTS_IN1K = Genotype( | ||||
|     normal=[ | ||||
|         ('skip_connect', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('skip_connect', 1), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 3), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('dil_conv_5x5', 4) | ||||
|     ], | ||||
|     normal_concat=range(2, 6), | ||||
|     reduce=[ | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('skip_connect', 1), | ||||
|         ('dil_conv_5x5', 2), | ||||
|         ('max_pool_3x3', 1), | ||||
|         ('sep_conv_3x3', 2), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_5x5', 0), | ||||
|         ('sep_conv_3x3', 3) | ||||
|     ], | ||||
|     reduce_concat=range(2, 6) | ||||
| ) | ||||
|  | ||||
| UNNAS_IMAGENET_CLS = Genotype( | ||||
|     normal=[ | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 2), | ||||
|         ('sep_conv_5x5', 1), | ||||
|         ('sep_conv_3x3', 0) | ||||
|     ], | ||||
|     normal_concat=range(2, 6), | ||||
|     reduce=[ | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('skip_connect', 1), | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('dil_conv_5x5', 2), | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('sep_conv_3x3', 2), | ||||
|         ('sep_conv_3x3', 4), | ||||
|         ('dil_conv_5x5', 3) | ||||
|     ], | ||||
|     reduce_concat=range(2, 6) | ||||
| ) | ||||
|  | ||||
| UNNAS_IMAGENET_ROT = Genotype( | ||||
|     normal=[ | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1) | ||||
|     ], | ||||
|     normal_concat=range(2, 6), | ||||
|     reduce=[ | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 2), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 2), | ||||
|         ('sep_conv_3x3', 4), | ||||
|         ('sep_conv_5x5', 2) | ||||
|     ], | ||||
|     reduce_concat=range(2, 6) | ||||
| ) | ||||
|  | ||||
| UNNAS_IMAGENET_COL = Genotype( | ||||
|     normal=[ | ||||
|         ('skip_connect', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('skip_connect', 0), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 3), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 2) | ||||
|     ], | ||||
|     normal_concat=range(2, 6), | ||||
|     reduce=[ | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('sep_conv_5x5', 3), | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('sep_conv_3x3', 4) | ||||
|     ], | ||||
|     reduce_concat=range(2, 6) | ||||
| ) | ||||
|  | ||||
| UNNAS_IMAGENET_JIG = Genotype( | ||||
|     normal=[ | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 3), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_5x5', 0) | ||||
|     ], | ||||
|     normal_concat=range(2, 6), | ||||
|     reduce=[ | ||||
|         ('sep_conv_5x5', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_5x5', 0), | ||||
|         ('sep_conv_3x3', 1) | ||||
|     ], | ||||
|     reduce_concat=range(2, 6) | ||||
| ) | ||||
|  | ||||
| UNNAS_IMAGENET22K_CLS = Genotype( | ||||
|     normal=[ | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('skip_connect', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 2), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 2), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0) | ||||
|     ], | ||||
|     normal_concat=range(2, 6), | ||||
|     reduce=[ | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('max_pool_3x3', 1), | ||||
|         ('dil_conv_5x5', 2), | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('dil_conv_5x5', 3), | ||||
|         ('dil_conv_5x5', 2), | ||||
|         ('dil_conv_5x5', 4), | ||||
|         ('dil_conv_5x5', 3) | ||||
|     ], | ||||
|     reduce_concat=range(2, 6) | ||||
| ) | ||||
|  | ||||
| UNNAS_IMAGENET22K_ROT = Genotype( | ||||
|     normal=[ | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1) | ||||
|     ], | ||||
|     normal_concat=range(2, 6), | ||||
|     reduce=[ | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('sep_conv_5x5', 1), | ||||
|         ('dil_conv_5x5', 2), | ||||
|         ('sep_conv_5x5', 0), | ||||
|         ('dil_conv_5x5', 3), | ||||
|         ('sep_conv_3x3', 2), | ||||
|         ('sep_conv_3x3', 4), | ||||
|         ('sep_conv_3x3', 3) | ||||
|     ], | ||||
|     reduce_concat=range(2, 6) | ||||
| ) | ||||
|  | ||||
| UNNAS_IMAGENET22K_COL = Genotype( | ||||
|     normal=[ | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 2), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 3), | ||||
|         ('sep_conv_3x3', 0) | ||||
|     ], | ||||
|     normal_concat=range(2, 6), | ||||
|     reduce=[ | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('skip_connect', 1), | ||||
|         ('dil_conv_5x5', 2), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 3), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 4), | ||||
|         ('sep_conv_5x5', 1) | ||||
|     ], | ||||
|     reduce_concat=range(2, 6) | ||||
| ) | ||||
|  | ||||
| UNNAS_IMAGENET22K_JIG = Genotype( | ||||
|     normal=[ | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 4) | ||||
|     ], | ||||
|     normal_concat=range(2, 6), | ||||
|     reduce=[ | ||||
|         ('sep_conv_5x5', 0), | ||||
|         ('skip_connect', 1), | ||||
|         ('sep_conv_5x5', 0), | ||||
|         ('sep_conv_3x3', 2), | ||||
|         ('sep_conv_5x5', 0), | ||||
|         ('sep_conv_5x5', 3), | ||||
|         ('sep_conv_5x5', 0), | ||||
|         ('sep_conv_5x5', 4) | ||||
|     ], | ||||
|     reduce_concat=range(2, 6) | ||||
| ) | ||||
|  | ||||
| UNNAS_CITYSCAPES_SEG = Genotype( | ||||
|     normal=[ | ||||
|         ('skip_connect', 0), | ||||
|         ('sep_conv_5x5', 1), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1) | ||||
|     ], | ||||
|     normal_concat=range(2, 6), | ||||
|     reduce=[ | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('avg_pool_3x3', 1), | ||||
|         ('avg_pool_3x3', 1), | ||||
|         ('sep_conv_5x5', 0), | ||||
|         ('sep_conv_3x3', 2), | ||||
|         ('sep_conv_5x5', 0), | ||||
|         ('sep_conv_3x3', 4), | ||||
|         ('sep_conv_5x5', 2) | ||||
|     ], | ||||
|     reduce_concat=range(2, 6) | ||||
| ) | ||||
|  | ||||
| UNNAS_CITYSCAPES_ROT = Genotype( | ||||
|     normal=[ | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 2), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 3), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0) | ||||
|     ], | ||||
|     normal_concat=range(2, 6), | ||||
|     reduce=[ | ||||
|         ('max_pool_3x3', 0), | ||||
|         ('sep_conv_5x5', 1), | ||||
|         ('sep_conv_5x5', 2), | ||||
|         ('sep_conv_5x5', 1), | ||||
|         ('sep_conv_5x5', 3), | ||||
|         ('dil_conv_5x5', 2), | ||||
|         ('sep_conv_5x5', 2), | ||||
|         ('sep_conv_5x5', 0) | ||||
|     ], | ||||
|     reduce_concat=range(2, 6) | ||||
| ) | ||||
|  | ||||
| UNNAS_CITYSCAPES_COL = Genotype( | ||||
|     normal=[ | ||||
|         ('dil_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('skip_connect', 0), | ||||
|         ('sep_conv_5x5', 2), | ||||
|         ('dil_conv_3x3', 3), | ||||
|         ('skip_connect', 0), | ||||
|         ('skip_connect', 0), | ||||
|         ('sep_conv_3x3', 1) | ||||
|     ], | ||||
|     normal_concat=range(2, 6), | ||||
|     reduce=[ | ||||
|         ('avg_pool_3x3', 1), | ||||
|         ('avg_pool_3x3', 0), | ||||
|         ('avg_pool_3x3', 1), | ||||
|         ('avg_pool_3x3', 0), | ||||
|         ('avg_pool_3x3', 1), | ||||
|         ('avg_pool_3x3', 0), | ||||
|         ('avg_pool_3x3', 1), | ||||
|         ('skip_connect', 4) | ||||
|     ], | ||||
|     reduce_concat=range(2, 6) | ||||
| ) | ||||
|  | ||||
| UNNAS_CITYSCAPES_JIG = Genotype( | ||||
|     normal=[ | ||||
|         ('dil_conv_5x5', 1), | ||||
|         ('sep_conv_5x5', 0), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 1), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('sep_conv_3x3', 2), | ||||
|         ('sep_conv_3x3', 0), | ||||
|         ('dil_conv_5x5', 1) | ||||
|     ], | ||||
|     normal_concat=range(2, 6), | ||||
|     reduce=[ | ||||
|         ('avg_pool_3x3', 0), | ||||
|         ('skip_connect', 1), | ||||
|         ('dil_conv_5x5', 1), | ||||
|         ('dil_conv_5x5', 2), | ||||
|         ('dil_conv_5x5', 2), | ||||
|         ('dil_conv_5x5', 0), | ||||
|         ('dil_conv_5x5', 3), | ||||
|         ('dil_conv_5x5', 2) | ||||
|     ], | ||||
|     reduce_concat=range(2, 6) | ||||
| ) | ||||
|  | ||||
|  | ||||
| # Supported genotypes | ||||
| GENOTYPES = { | ||||
|     'nas': NASNet, | ||||
|     'pnas': PNASNet, | ||||
|     'amoeba': AmoebaNet, | ||||
|     'darts_v1': DARTS_V1, | ||||
|     'darts_v2': DARTS_V2, | ||||
|     'pdarts': PDARTS, | ||||
|     'pcdarts_c10': PCDARTS_C10, | ||||
|     'pcdarts_in1k': PCDARTS_IN1K, | ||||
|     'unnas_imagenet_cls': UNNAS_IMAGENET_CLS, | ||||
|     'unnas_imagenet_rot': UNNAS_IMAGENET_ROT, | ||||
|     'unnas_imagenet_col': UNNAS_IMAGENET_COL, | ||||
|     'unnas_imagenet_jig': UNNAS_IMAGENET_JIG, | ||||
|     'unnas_imagenet22k_cls': UNNAS_IMAGENET22K_CLS, | ||||
|     'unnas_imagenet22k_rot': UNNAS_IMAGENET22K_ROT, | ||||
|     'unnas_imagenet22k_col': UNNAS_IMAGENET22K_COL, | ||||
|     'unnas_imagenet22k_jig': UNNAS_IMAGENET22K_JIG, | ||||
|     'unnas_cityscapes_seg': UNNAS_CITYSCAPES_SEG, | ||||
|     'unnas_cityscapes_rot': UNNAS_CITYSCAPES_ROT, | ||||
|     'unnas_cityscapes_col': UNNAS_CITYSCAPES_COL, | ||||
|     'unnas_cityscapes_jig': UNNAS_CITYSCAPES_JIG, | ||||
|     'custom': None, | ||||
| } | ||||
							
								
								
									
										299
									
								
								graph_dit/naswot/pycls/models/nas/nas.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										299
									
								
								graph_dit/naswot/pycls/models/nas/nas.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,299 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """NAS network (adopted from DARTS).""" | ||||
|  | ||||
| from torch.autograd import Variable | ||||
|  | ||||
| import torch | ||||
| import torch.nn as nn | ||||
|  | ||||
| import pycls.core.logging as logging | ||||
|  | ||||
| from pycls.core.config import cfg | ||||
| from pycls.models.common import Preprocess | ||||
| from pycls.models.common import Classifier | ||||
| from pycls.models.nas.genotypes import GENOTYPES | ||||
| from pycls.models.nas.genotypes import Genotype | ||||
| from pycls.models.nas.operations import FactorizedReduce | ||||
| from pycls.models.nas.operations import OPS | ||||
| from pycls.models.nas.operations import ReLUConvBN | ||||
| from pycls.models.nas.operations import Identity | ||||
|  | ||||
|  | ||||
| logger = logging.get_logger(__name__) | ||||
|  | ||||
|  | ||||
| def drop_path(x, drop_prob): | ||||
|     """Drop path (ported from DARTS).""" | ||||
|     if drop_prob > 0.: | ||||
|         keep_prob = 1.-drop_prob | ||||
|         mask = Variable( | ||||
|             torch.cuda.FloatTensor(x.size(0), 1, 1, 1).bernoulli_(keep_prob) | ||||
|         ) | ||||
|         x.div_(keep_prob) | ||||
|         x.mul_(mask) | ||||
|     return x | ||||
|  | ||||
|  | ||||
| class Cell(nn.Module): | ||||
|     """NAS cell (ported from DARTS).""" | ||||
|  | ||||
|     def __init__(self, genotype, C_prev_prev, C_prev, C, reduction, reduction_prev): | ||||
|         super(Cell, self).__init__() | ||||
|         logger.info('{}, {}, {}'.format(C_prev_prev, C_prev, C)) | ||||
|  | ||||
|         if reduction_prev: | ||||
|             self.preprocess0 = FactorizedReduce(C_prev_prev, C) | ||||
|         else: | ||||
|             self.preprocess0 = ReLUConvBN(C_prev_prev, C, 1, 1, 0) | ||||
|         self.preprocess1 = ReLUConvBN(C_prev, C, 1, 1, 0) | ||||
|  | ||||
|         if reduction: | ||||
|             op_names, indices = zip(*genotype.reduce) | ||||
|             concat = genotype.reduce_concat | ||||
|         else: | ||||
|             op_names, indices = zip(*genotype.normal) | ||||
|             concat = genotype.normal_concat | ||||
|         self._compile(C, op_names, indices, concat, reduction) | ||||
|  | ||||
|     def _compile(self, C, op_names, indices, concat, reduction): | ||||
|         assert len(op_names) == len(indices) | ||||
|         self._steps = len(op_names) // 2 | ||||
|         self._concat = concat | ||||
|         self.multiplier = len(concat) | ||||
|  | ||||
|         self._ops = nn.ModuleList() | ||||
|         for name, index in zip(op_names, indices): | ||||
|             stride = 2 if reduction and index < 2 else 1 | ||||
|             op = OPS[name](C, stride, True) | ||||
|             self._ops += [op] | ||||
|         self._indices = indices | ||||
|  | ||||
|     def forward(self, s0, s1, drop_prob): | ||||
|         s0 = self.preprocess0(s0) | ||||
|         s1 = self.preprocess1(s1) | ||||
|  | ||||
|         states = [s0, s1] | ||||
|         for i in range(self._steps): | ||||
|             h1 = states[self._indices[2*i]] | ||||
|             h2 = states[self._indices[2*i+1]] | ||||
|             op1 = self._ops[2*i] | ||||
|             op2 = self._ops[2*i+1] | ||||
|             h1 = op1(h1) | ||||
|             h2 = op2(h2) | ||||
|             if self.training and drop_prob > 0.: | ||||
|                 if not isinstance(op1, Identity): | ||||
|                     h1 = drop_path(h1, drop_prob) | ||||
|                 if not isinstance(op2, Identity): | ||||
|                     h2 = drop_path(h2, drop_prob) | ||||
|             s = h1 + h2 | ||||
|             states += [s] | ||||
|         return torch.cat([states[i] for i in self._concat], dim=1) | ||||
|  | ||||
|  | ||||
| class AuxiliaryHeadCIFAR(nn.Module): | ||||
|  | ||||
|     def __init__(self, C, num_classes): | ||||
|         """assuming input size 8x8""" | ||||
|         super(AuxiliaryHeadCIFAR, self).__init__() | ||||
|         self.features = nn.Sequential( | ||||
|             nn.ReLU(inplace=True), | ||||
|             nn.AvgPool2d(5, stride=3, padding=0, count_include_pad=False), # image size = 2 x 2 | ||||
|             nn.Conv2d(C, 128, 1, bias=False), | ||||
|             nn.BatchNorm2d(128), | ||||
|             nn.ReLU(inplace=True), | ||||
|             nn.Conv2d(128, 768, 2, bias=False), | ||||
|             nn.BatchNorm2d(768), | ||||
|             nn.ReLU(inplace=True) | ||||
|         ) | ||||
|         self.classifier = nn.Linear(768, num_classes) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         x = self.features(x) | ||||
|         x = self.classifier(x.view(x.size(0),-1)) | ||||
|         return x | ||||
|  | ||||
|  | ||||
| class AuxiliaryHeadImageNet(nn.Module): | ||||
|  | ||||
|     def __init__(self, C, num_classes): | ||||
|         """assuming input size 14x14""" | ||||
|         super(AuxiliaryHeadImageNet, self).__init__() | ||||
|         self.features = nn.Sequential( | ||||
|             nn.ReLU(inplace=True), | ||||
|             nn.AvgPool2d(5, stride=2, padding=0, count_include_pad=False), | ||||
|             nn.Conv2d(C, 128, 1, bias=False), | ||||
|             nn.BatchNorm2d(128), | ||||
|             nn.ReLU(inplace=True), | ||||
|             nn.Conv2d(128, 768, 2, bias=False), | ||||
|             # NOTE: This batchnorm was omitted in my earlier implementation due to a typo. | ||||
|             # Commenting it out for consistency with the experiments in the paper. | ||||
|             # nn.BatchNorm2d(768), | ||||
|             nn.ReLU(inplace=True) | ||||
|         ) | ||||
|         self.classifier = nn.Linear(768, num_classes) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         x = self.features(x) | ||||
|         x = self.classifier(x.view(x.size(0),-1)) | ||||
|         return x | ||||
|  | ||||
|  | ||||
| class NetworkCIFAR(nn.Module): | ||||
|     """CIFAR network (ported from DARTS).""" | ||||
|  | ||||
|     def __init__(self, C, num_classes, layers, auxiliary, genotype): | ||||
|         super(NetworkCIFAR, self).__init__() | ||||
|         self._layers = layers | ||||
|         self._auxiliary = auxiliary | ||||
|  | ||||
|         stem_multiplier = 3 | ||||
|         C_curr = stem_multiplier*C | ||||
|         self.stem = nn.Sequential( | ||||
|             nn.Conv2d(cfg.MODEL.INPUT_CHANNELS, C_curr, 3, padding=1, bias=False), | ||||
|             nn.BatchNorm2d(C_curr) | ||||
|         ) | ||||
|  | ||||
|         C_prev_prev, C_prev, C_curr = C_curr, C_curr, C | ||||
|         self.cells = nn.ModuleList() | ||||
|         reduction_prev = False | ||||
|         for i in range(layers): | ||||
|             if i in [layers//3, 2*layers//3]: | ||||
|                 C_curr *= 2 | ||||
|                 reduction = True | ||||
|             else: | ||||
|                 reduction = False | ||||
|             cell = Cell(genotype, C_prev_prev, C_prev, C_curr, reduction, reduction_prev) | ||||
|             reduction_prev = reduction | ||||
|             self.cells += [cell] | ||||
|             C_prev_prev, C_prev = C_prev, cell.multiplier*C_curr | ||||
|             if i == 2*layers//3: | ||||
|                 C_to_auxiliary = C_prev | ||||
|  | ||||
|         if auxiliary: | ||||
|             self.auxiliary_head = AuxiliaryHeadCIFAR(C_to_auxiliary, num_classes) | ||||
|         self.classifier = Classifier(C_prev, num_classes) | ||||
|  | ||||
|     def forward(self, input): | ||||
|         input = Preprocess(input) | ||||
|         logits_aux = None | ||||
|         s0 = s1 = self.stem(input) | ||||
|         for i, cell in enumerate(self.cells): | ||||
|             s0, s1 = s1, cell(s0, s1, self.drop_path_prob) | ||||
|             if i == 2*self._layers//3: | ||||
|                 if self._auxiliary and self.training: | ||||
|                     logits_aux = self.auxiliary_head(s1) | ||||
|         logits = self.classifier(s1, input.shape[2:]) | ||||
|         if self._auxiliary and self.training: | ||||
|             return logits, logits_aux | ||||
|         return logits | ||||
|  | ||||
|  | ||||
| class NetworkImageNet(nn.Module): | ||||
|     """ImageNet network (ported from DARTS).""" | ||||
|  | ||||
|     def __init__(self, C, num_classes, layers, auxiliary, genotype): | ||||
|         super(NetworkImageNet, self).__init__() | ||||
|         self._layers = layers | ||||
|         self._auxiliary = auxiliary | ||||
|  | ||||
|         self.stem0 = nn.Sequential( | ||||
|             nn.Conv2d(cfg.MODEL.INPUT_CHANNELS, C // 2, kernel_size=3, stride=2, padding=1, bias=False), | ||||
|             nn.BatchNorm2d(C // 2), | ||||
|             nn.ReLU(inplace=True), | ||||
|             nn.Conv2d(C // 2, C, 3, stride=2, padding=1, bias=False), | ||||
|             nn.BatchNorm2d(C), | ||||
|         ) | ||||
|  | ||||
|         self.stem1 = nn.Sequential( | ||||
|             nn.ReLU(inplace=True), | ||||
|             nn.Conv2d(C, C, 3, stride=2, padding=1, bias=False), | ||||
|             nn.BatchNorm2d(C), | ||||
|         ) | ||||
|  | ||||
|         C_prev_prev, C_prev, C_curr = C, C, C | ||||
|  | ||||
|         self.cells = nn.ModuleList() | ||||
|         reduction_prev = True | ||||
|         reduction_layers = [layers//3] if cfg.TASK == 'seg' else [layers//3, 2*layers//3] | ||||
|         for i in range(layers): | ||||
|             if i in reduction_layers: | ||||
|                 C_curr *= 2 | ||||
|                 reduction = True | ||||
|             else: | ||||
|                 reduction = False | ||||
|             cell = Cell(genotype, C_prev_prev, C_prev, C_curr, reduction, reduction_prev) | ||||
|             reduction_prev = reduction | ||||
|             self.cells += [cell] | ||||
|             C_prev_prev, C_prev = C_prev, cell.multiplier * C_curr | ||||
|             if i == 2 * layers // 3: | ||||
|                 C_to_auxiliary = C_prev | ||||
|  | ||||
|         if auxiliary: | ||||
|             self.auxiliary_head = AuxiliaryHeadImageNet(C_to_auxiliary, num_classes) | ||||
|         self.classifier = Classifier(C_prev, num_classes) | ||||
|  | ||||
|     def forward(self, input): | ||||
|         input = Preprocess(input) | ||||
|         logits_aux = None | ||||
|         s0 = self.stem0(input) | ||||
|         s1 = self.stem1(s0) | ||||
|         for i, cell in enumerate(self.cells): | ||||
|             s0, s1 = s1, cell(s0, s1, self.drop_path_prob) | ||||
|             if i == 2 * self._layers // 3: | ||||
|                 if self._auxiliary and self.training: | ||||
|                     logits_aux = self.auxiliary_head(s1) | ||||
|         logits = self.classifier(s1, input.shape[2:]) | ||||
|         if self._auxiliary and self.training: | ||||
|             return logits, logits_aux | ||||
|         return logits | ||||
|  | ||||
|  | ||||
| class NAS(nn.Module): | ||||
|     """NAS net wrapper (delegates to nets from DARTS).""" | ||||
|  | ||||
|     def __init__(self): | ||||
|         assert cfg.TRAIN.DATASET in ['cifar10', 'imagenet', 'cityscapes'], \ | ||||
|             'Training on {} is not supported'.format(cfg.TRAIN.DATASET) | ||||
|         assert cfg.TEST.DATASET in ['cifar10', 'imagenet', 'cityscapes'], \ | ||||
|             'Testing on {} is not supported'.format(cfg.TEST.DATASET) | ||||
|         assert cfg.NAS.GENOTYPE in GENOTYPES, \ | ||||
|             'Genotype {} not supported'.format(cfg.NAS.GENOTYPE) | ||||
|         super(NAS, self).__init__() | ||||
|         logger.info('Constructing NAS: {}'.format(cfg.NAS)) | ||||
|         # Use a custom or predefined genotype | ||||
|         if cfg.NAS.GENOTYPE == 'custom': | ||||
|             genotype = Genotype( | ||||
|                 normal=cfg.NAS.CUSTOM_GENOTYPE[0], | ||||
|                 normal_concat=cfg.NAS.CUSTOM_GENOTYPE[1], | ||||
|                 reduce=cfg.NAS.CUSTOM_GENOTYPE[2], | ||||
|                 reduce_concat=cfg.NAS.CUSTOM_GENOTYPE[3], | ||||
|             ) | ||||
|         else: | ||||
|             genotype = GENOTYPES[cfg.NAS.GENOTYPE] | ||||
|         # Determine the network constructor for dataset | ||||
|         if 'cifar' in cfg.TRAIN.DATASET: | ||||
|             net_ctor = NetworkCIFAR | ||||
|         else: | ||||
|             net_ctor = NetworkImageNet | ||||
|         # Construct the network | ||||
|         self.net_ = net_ctor( | ||||
|             C=cfg.NAS.WIDTH, | ||||
|             num_classes=cfg.MODEL.NUM_CLASSES, | ||||
|             layers=cfg.NAS.DEPTH, | ||||
|             auxiliary=cfg.NAS.AUX, | ||||
|             genotype=genotype | ||||
|         ) | ||||
|         # Drop path probability (set / annealed based on epoch) | ||||
|         self.net_.drop_path_prob = 0.0 | ||||
|  | ||||
|     def set_drop_path_prob(self, drop_path_prob): | ||||
|         self.net_.drop_path_prob = drop_path_prob | ||||
|  | ||||
|     def forward(self, x): | ||||
|         return self.net_.forward(x) | ||||
							
								
								
									
										201
									
								
								graph_dit/naswot/pycls/models/nas/operations.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								graph_dit/naswot/pycls/models/nas/operations.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,201 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
|  | ||||
| """NAS ops (adopted from DARTS).""" | ||||
|  | ||||
| import torch | ||||
| import torch.nn as nn | ||||
|  | ||||
|  | ||||
| OPS = { | ||||
|     'none': lambda C, stride, affine: | ||||
|         Zero(stride), | ||||
|     'avg_pool_2x2': lambda C, stride, affine: | ||||
|         nn.AvgPool2d(2, stride=stride, padding=0, count_include_pad=False), | ||||
|     'avg_pool_3x3': lambda C, stride, affine: | ||||
|         nn.AvgPool2d(3, stride=stride, padding=1, count_include_pad=False), | ||||
|     'avg_pool_5x5': lambda C, stride, affine: | ||||
|         nn.AvgPool2d(5, stride=stride, padding=2, count_include_pad=False), | ||||
|     'max_pool_2x2': lambda C, stride, affine: | ||||
|         nn.MaxPool2d(2, stride=stride, padding=0), | ||||
|     'max_pool_3x3': lambda C, stride, affine: | ||||
|         nn.MaxPool2d(3, stride=stride, padding=1), | ||||
|     'max_pool_5x5': lambda C, stride, affine: | ||||
|         nn.MaxPool2d(5, stride=stride, padding=2), | ||||
|     'max_pool_7x7': lambda C, stride, affine: | ||||
|         nn.MaxPool2d(7, stride=stride, padding=3), | ||||
|     'skip_connect': lambda C, stride, affine: | ||||
|         Identity() if stride == 1 else FactorizedReduce(C, C, affine=affine), | ||||
|     'conv_1x1': lambda C, stride, affine: | ||||
|         nn.Sequential( | ||||
|             nn.ReLU(inplace=False), | ||||
|             nn.Conv2d(C, C, 1, stride=stride, padding=0, bias=False), | ||||
|             nn.BatchNorm2d(C, affine=affine) | ||||
|         ), | ||||
|     'conv_3x3': lambda C, stride, affine: | ||||
|         nn.Sequential( | ||||
|             nn.ReLU(inplace=False), | ||||
|             nn.Conv2d(C, C, 3, stride=stride, padding=1, bias=False), | ||||
|             nn.BatchNorm2d(C, affine=affine) | ||||
|         ), | ||||
|     'sep_conv_3x3': lambda C, stride, affine: | ||||
|         SepConv(C, C, 3, stride, 1, affine=affine), | ||||
|     'sep_conv_5x5': lambda C, stride, affine: | ||||
|         SepConv(C, C, 5, stride, 2, affine=affine), | ||||
|     'sep_conv_7x7': lambda C, stride, affine: | ||||
|         SepConv(C, C, 7, stride, 3, affine=affine), | ||||
|     'dil_conv_3x3': lambda C, stride, affine: | ||||
|         DilConv(C, C, 3, stride, 2, 2, affine=affine), | ||||
|     'dil_conv_5x5': lambda C, stride, affine: | ||||
|         DilConv(C, C, 5, stride, 4, 2, affine=affine), | ||||
|     'dil_sep_conv_3x3': lambda C, stride, affine: | ||||
|         DilSepConv(C, C, 3, stride, 2, 2, affine=affine), | ||||
|     'conv_3x1_1x3': lambda C, stride, affine: | ||||
|         nn.Sequential( | ||||
|             nn.ReLU(inplace=False), | ||||
|             nn.Conv2d(C, C, (1,3), stride=(1, stride), padding=(0, 1), bias=False), | ||||
|             nn.Conv2d(C, C, (3,1), stride=(stride, 1), padding=(1, 0), bias=False), | ||||
|             nn.BatchNorm2d(C, affine=affine) | ||||
|         ), | ||||
|     'conv_7x1_1x7': lambda C, stride, affine: | ||||
|         nn.Sequential( | ||||
|             nn.ReLU(inplace=False), | ||||
|             nn.Conv2d(C, C, (1,7), stride=(1, stride), padding=(0, 3), bias=False), | ||||
|             nn.Conv2d(C, C, (7,1), stride=(stride, 1), padding=(3, 0), bias=False), | ||||
|             nn.BatchNorm2d(C, affine=affine) | ||||
|         ), | ||||
| } | ||||
|  | ||||
|  | ||||
| class ReLUConvBN(nn.Module): | ||||
|  | ||||
|     def __init__(self, C_in, C_out, kernel_size, stride, padding, affine=True): | ||||
|         super(ReLUConvBN, self).__init__() | ||||
|         self.op = nn.Sequential( | ||||
|             nn.ReLU(inplace=False), | ||||
|             nn.Conv2d( | ||||
|                 C_in, C_out, kernel_size, stride=stride, | ||||
|                 padding=padding, bias=False | ||||
|             ), | ||||
|             nn.BatchNorm2d(C_out, affine=affine) | ||||
|         ) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         return self.op(x) | ||||
|  | ||||
|  | ||||
| class DilConv(nn.Module): | ||||
|  | ||||
|     def __init__( | ||||
|         self, C_in, C_out, kernel_size, stride, padding, dilation, affine=True | ||||
|     ): | ||||
|         super(DilConv, self).__init__() | ||||
|         self.op = nn.Sequential( | ||||
|             nn.ReLU(inplace=False), | ||||
|             nn.Conv2d( | ||||
|                 C_in, C_in, kernel_size=kernel_size, stride=stride, | ||||
|                 padding=padding, dilation=dilation, groups=C_in, bias=False | ||||
|             ), | ||||
|             nn.Conv2d(C_in, C_out, kernel_size=1, padding=0, bias=False), | ||||
|             nn.BatchNorm2d(C_out, affine=affine), | ||||
|         ) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         return self.op(x) | ||||
|  | ||||
|  | ||||
| class SepConv(nn.Module): | ||||
|  | ||||
|     def __init__(self, C_in, C_out, kernel_size, stride, padding, affine=True): | ||||
|         super(SepConv, self).__init__() | ||||
|         self.op = nn.Sequential( | ||||
|             nn.ReLU(inplace=False), | ||||
|             nn.Conv2d( | ||||
|                 C_in, C_in, kernel_size=kernel_size, stride=stride, | ||||
|                 padding=padding, groups=C_in, bias=False | ||||
|             ), | ||||
|             nn.Conv2d(C_in, C_in, kernel_size=1, padding=0, bias=False), | ||||
|             nn.BatchNorm2d(C_in, affine=affine), | ||||
|             nn.ReLU(inplace=False), | ||||
|             nn.Conv2d( | ||||
|                 C_in, C_in, kernel_size=kernel_size, stride=1, | ||||
|                 padding=padding, groups=C_in, bias=False | ||||
|             ), | ||||
|             nn.Conv2d(C_in, C_out, kernel_size=1, padding=0, bias=False), | ||||
|             nn.BatchNorm2d(C_out, affine=affine), | ||||
|         ) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         return self.op(x) | ||||
|  | ||||
|  | ||||
| class DilSepConv(nn.Module): | ||||
|  | ||||
|     def __init__( | ||||
|         self, C_in, C_out, kernel_size, stride, padding, dilation, affine=True | ||||
|     ): | ||||
|         super(DilSepConv, self).__init__() | ||||
|         self.op = nn.Sequential( | ||||
|             nn.ReLU(inplace=False), | ||||
|             nn.Conv2d( | ||||
|                 C_in, C_in, kernel_size=kernel_size, stride=stride, | ||||
|                 padding=padding, dilation=dilation, groups=C_in, bias=False | ||||
|             ), | ||||
|             nn.Conv2d(C_in, C_in, kernel_size=1, padding=0, bias=False), | ||||
|             nn.BatchNorm2d(C_in, affine=affine), | ||||
|             nn.ReLU(inplace=False), | ||||
|             nn.Conv2d( | ||||
|                 C_in, C_in, kernel_size=kernel_size, stride=1, | ||||
|                 padding=padding, dilation=dilation, groups=C_in, bias=False | ||||
|             ), | ||||
|             nn.Conv2d(C_in, C_out, kernel_size=1, padding=0, bias=False), | ||||
|             nn.BatchNorm2d(C_out, affine=affine), | ||||
|         ) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         return self.op(x) | ||||
|  | ||||
|  | ||||
| class Identity(nn.Module): | ||||
|  | ||||
|     def __init__(self): | ||||
|         super(Identity, self).__init__() | ||||
|  | ||||
|     def forward(self, x): | ||||
|         return x | ||||
|  | ||||
|  | ||||
| class Zero(nn.Module): | ||||
|  | ||||
|     def __init__(self, stride): | ||||
|         super(Zero, self).__init__() | ||||
|         self.stride = stride | ||||
|  | ||||
|     def forward(self, x): | ||||
|         if self.stride == 1: | ||||
|             return x.mul(0.) | ||||
|         return x[:,:,::self.stride,::self.stride].mul(0.) | ||||
|  | ||||
|  | ||||
| class FactorizedReduce(nn.Module): | ||||
|  | ||||
|     def __init__(self, C_in, C_out, affine=True): | ||||
|         super(FactorizedReduce, self).__init__() | ||||
|         assert C_out % 2 == 0 | ||||
|         self.relu = nn.ReLU(inplace=False) | ||||
|         self.conv_1 = nn.Conv2d(C_in, C_out // 2, 1, stride=2, padding=0, bias=False) | ||||
|         self.conv_2 = nn.Conv2d(C_in, C_out // 2, 1, stride=2, padding=0, bias=False) | ||||
|         self.bn = nn.BatchNorm2d(C_out, affine=affine) | ||||
|         self.pad = nn.ConstantPad2d((0, 1, 0, 1), 0) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         x = self.relu(x) | ||||
|         y = self.pad(x) | ||||
|         out = torch.cat([self.conv_1(x), self.conv_2(y[:,:,1:,1:])], dim=1) | ||||
|         out = self.bn(out) | ||||
|         return out | ||||
							
								
								
									
										89
									
								
								graph_dit/naswot/pycls/models/regnet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								graph_dit/naswot/pycls/models/regnet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """RegNet models.""" | ||||
|  | ||||
| import numpy as np | ||||
| from pycls.core.config import cfg | ||||
| from pycls.models.anynet import AnyNet | ||||
|  | ||||
|  | ||||
| def quantize_float(f, q): | ||||
|     """Converts a float to closest non-zero int divisible by q.""" | ||||
|     return int(round(f / q) * q) | ||||
|  | ||||
|  | ||||
| def adjust_ws_gs_comp(ws, bms, gs): | ||||
|     """Adjusts the compatibility of widths and groups.""" | ||||
|     ws_bot = [int(w * b) for w, b in zip(ws, bms)] | ||||
|     gs = [min(g, w_bot) for g, w_bot in zip(gs, ws_bot)] | ||||
|     ws_bot = [quantize_float(w_bot, g) for w_bot, g in zip(ws_bot, gs)] | ||||
|     ws = [int(w_bot / b) for w_bot, b in zip(ws_bot, bms)] | ||||
|     return ws, gs | ||||
|  | ||||
|  | ||||
| def get_stages_from_blocks(ws, rs): | ||||
|     """Gets ws/ds of network at each stage from per block values.""" | ||||
|     ts_temp = zip(ws + [0], [0] + ws, rs + [0], [0] + rs) | ||||
|     ts = [w != wp or r != rp for w, wp, r, rp in ts_temp] | ||||
|     s_ws = [w for w, t in zip(ws, ts[:-1]) if t] | ||||
|     s_ds = np.diff([d for d, t in zip(range(len(ts)), ts) if t]).tolist() | ||||
|     return s_ws, s_ds | ||||
|  | ||||
|  | ||||
| def generate_regnet(w_a, w_0, w_m, d, q=8): | ||||
|     """Generates per block ws from RegNet parameters.""" | ||||
|     assert w_a >= 0 and w_0 > 0 and w_m > 1 and w_0 % q == 0 | ||||
|     ws_cont = np.arange(d) * w_a + w_0 | ||||
|     ks = np.round(np.log(ws_cont / w_0) / np.log(w_m)) | ||||
|     ws = w_0 * np.power(w_m, ks) | ||||
|     ws = np.round(np.divide(ws, q)) * q | ||||
|     num_stages, max_stage = len(np.unique(ws)), ks.max() + 1 | ||||
|     ws, ws_cont = ws.astype(int).tolist(), ws_cont.tolist() | ||||
|     return ws, num_stages, max_stage, ws_cont | ||||
|  | ||||
|  | ||||
| class RegNet(AnyNet): | ||||
|     """RegNet model.""" | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_args(): | ||||
|         """Convert RegNet to AnyNet parameter format.""" | ||||
|         # Generate RegNet ws per block | ||||
|         w_a, w_0, w_m, d = cfg.REGNET.WA, cfg.REGNET.W0, cfg.REGNET.WM, cfg.REGNET.DEPTH | ||||
|         ws, num_stages, _, _ = generate_regnet(w_a, w_0, w_m, d) | ||||
|         # Convert to per stage format | ||||
|         s_ws, s_ds = get_stages_from_blocks(ws, ws) | ||||
|         # Use the same gw, bm and ss for each stage | ||||
|         s_gs = [cfg.REGNET.GROUP_W for _ in range(num_stages)] | ||||
|         s_bs = [cfg.REGNET.BOT_MUL for _ in range(num_stages)] | ||||
|         s_ss = [cfg.REGNET.STRIDE for _ in range(num_stages)] | ||||
|         # Adjust the compatibility of ws and gws | ||||
|         s_ws, s_gs = adjust_ws_gs_comp(s_ws, s_bs, s_gs) | ||||
|         # Get AnyNet arguments defining the RegNet | ||||
|         return { | ||||
|             "stem_type": cfg.REGNET.STEM_TYPE, | ||||
|             "stem_w": cfg.REGNET.STEM_W, | ||||
|             "block_type": cfg.REGNET.BLOCK_TYPE, | ||||
|             "ds": s_ds, | ||||
|             "ws": s_ws, | ||||
|             "ss": s_ss, | ||||
|             "bms": s_bs, | ||||
|             "gws": s_gs, | ||||
|             "se_r": cfg.REGNET.SE_R if cfg.REGNET.SE_ON else None, | ||||
|             "nc": cfg.MODEL.NUM_CLASSES, | ||||
|         } | ||||
|  | ||||
|     def __init__(self): | ||||
|         kwargs = RegNet.get_args() | ||||
|         super(RegNet, self).__init__(**kwargs) | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, **kwargs): | ||||
|         """Computes model complexity. If you alter the model, make sure to update.""" | ||||
|         kwargs = RegNet.get_args() if not kwargs else kwargs | ||||
|         return AnyNet.complexity(cx, **kwargs) | ||||
							
								
								
									
										280
									
								
								graph_dit/naswot/pycls/models/resnet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								graph_dit/naswot/pycls/models/resnet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,280 @@ | ||||
| #!/usr/bin/env python3 | ||||
|  | ||||
| # Copyright (c) Facebook, Inc. and its affiliates. | ||||
| # | ||||
| # This source code is licensed under the MIT license found in the | ||||
| # LICENSE file in the root directory of this source tree. | ||||
|  | ||||
| """ResNe(X)t models.""" | ||||
|  | ||||
| import pycls.core.net as net | ||||
| import torch.nn as nn | ||||
| from pycls.core.config import cfg | ||||
|  | ||||
|  | ||||
| # Stage depths for ImageNet models | ||||
| _IN_STAGE_DS = {50: (3, 4, 6, 3), 101: (3, 4, 23, 3), 152: (3, 8, 36, 3)} | ||||
|  | ||||
|  | ||||
| def get_trans_fun(name): | ||||
|     """Retrieves the transformation function by name.""" | ||||
|     trans_funs = { | ||||
|         "basic_transform": BasicTransform, | ||||
|         "bottleneck_transform": BottleneckTransform, | ||||
|     } | ||||
|     err_str = "Transformation function '{}' not supported" | ||||
|     assert name in trans_funs.keys(), err_str.format(name) | ||||
|     return trans_funs[name] | ||||
|  | ||||
|  | ||||
| class ResHead(nn.Module): | ||||
|     """ResNet head: AvgPool, 1x1.""" | ||||
|  | ||||
|     def __init__(self, w_in, nc): | ||||
|         super(ResHead, self).__init__() | ||||
|         self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) | ||||
|         self.fc = nn.Linear(w_in, nc, bias=True) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         x = self.avg_pool(x) | ||||
|         x = x.view(x.size(0), -1) | ||||
|         x = self.fc(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, nc): | ||||
|         cx["h"], cx["w"] = 1, 1 | ||||
|         cx = net.complexity_conv2d(cx, w_in, nc, 1, 1, 0, bias=True) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class BasicTransform(nn.Module): | ||||
|     """Basic transformation: 3x3, BN, ReLU, 3x3, BN.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out, stride, w_b=None, num_gs=1): | ||||
|         err_str = "Basic transform does not support w_b and num_gs options" | ||||
|         assert w_b is None and num_gs == 1, err_str | ||||
|         super(BasicTransform, self).__init__() | ||||
|         self.a = nn.Conv2d(w_in, w_out, 3, stride=stride, padding=1, bias=False) | ||||
|         self.a_bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.a_relu = nn.ReLU(inplace=cfg.MEM.RELU_INPLACE) | ||||
|         self.b = nn.Conv2d(w_out, w_out, 3, stride=1, padding=1, bias=False) | ||||
|         self.b_bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.b_bn.final_bn = True | ||||
|  | ||||
|     def forward(self, x): | ||||
|         for layer in self.children(): | ||||
|             x = layer(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out, stride, w_b=None, num_gs=1): | ||||
|         err_str = "Basic transform does not support w_b and num_gs options" | ||||
|         assert w_b is None and num_gs == 1, err_str | ||||
|         cx = net.complexity_conv2d(cx, w_in, w_out, 3, stride, 1) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|         cx = net.complexity_conv2d(cx, w_out, w_out, 3, 1, 1) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class BottleneckTransform(nn.Module): | ||||
|     """Bottleneck transformation: 1x1, BN, ReLU, 3x3, BN, ReLU, 1x1, BN.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out, stride, w_b, num_gs): | ||||
|         super(BottleneckTransform, self).__init__() | ||||
|         # MSRA -> stride=2 is on 1x1; TH/C2 -> stride=2 is on 3x3 | ||||
|         (s1, s3) = (stride, 1) if cfg.RESNET.STRIDE_1X1 else (1, stride) | ||||
|         self.a = nn.Conv2d(w_in, w_b, 1, stride=s1, padding=0, bias=False) | ||||
|         self.a_bn = nn.BatchNorm2d(w_b, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.a_relu = nn.ReLU(inplace=cfg.MEM.RELU_INPLACE) | ||||
|         self.b = nn.Conv2d(w_b, w_b, 3, stride=s3, padding=1, groups=num_gs, bias=False) | ||||
|         self.b_bn = nn.BatchNorm2d(w_b, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.b_relu = nn.ReLU(inplace=cfg.MEM.RELU_INPLACE) | ||||
|         self.c = nn.Conv2d(w_b, w_out, 1, stride=1, padding=0, bias=False) | ||||
|         self.c_bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.c_bn.final_bn = True | ||||
|  | ||||
|     def forward(self, x): | ||||
|         for layer in self.children(): | ||||
|             x = layer(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out, stride, w_b, num_gs): | ||||
|         (s1, s3) = (stride, 1) if cfg.RESNET.STRIDE_1X1 else (1, stride) | ||||
|         cx = net.complexity_conv2d(cx, w_in, w_b, 1, s1, 0) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_b) | ||||
|         cx = net.complexity_conv2d(cx, w_b, w_b, 3, s3, 1, num_gs) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_b) | ||||
|         cx = net.complexity_conv2d(cx, w_b, w_out, 1, 1, 0) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class ResBlock(nn.Module): | ||||
|     """Residual block: x + F(x).""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out, stride, trans_fun, w_b=None, num_gs=1): | ||||
|         super(ResBlock, self).__init__() | ||||
|         # Use skip connection with projection if shape changes | ||||
|         self.proj_block = (w_in != w_out) or (stride != 1) | ||||
|         if self.proj_block: | ||||
|             self.proj = nn.Conv2d(w_in, w_out, 1, stride=stride, padding=0, bias=False) | ||||
|             self.bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.f = trans_fun(w_in, w_out, stride, w_b, num_gs) | ||||
|         self.relu = nn.ReLU(cfg.MEM.RELU_INPLACE) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         if self.proj_block: | ||||
|             x = self.bn(self.proj(x)) + self.f(x) | ||||
|         else: | ||||
|             x = x + self.f(x) | ||||
|         x = self.relu(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out, stride, trans_fun, w_b, num_gs): | ||||
|         proj_block = (w_in != w_out) or (stride != 1) | ||||
|         if proj_block: | ||||
|             h, w = cx["h"], cx["w"] | ||||
|             cx = net.complexity_conv2d(cx, w_in, w_out, 1, stride, 0) | ||||
|             cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|             cx["h"], cx["w"] = h, w  # parallel branch | ||||
|         cx = trans_fun.complexity(cx, w_in, w_out, stride, w_b, num_gs) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class ResStage(nn.Module): | ||||
|     """Stage of ResNet.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out, stride, d, w_b=None, num_gs=1): | ||||
|         super(ResStage, self).__init__() | ||||
|         for i in range(d): | ||||
|             b_stride = stride if i == 0 else 1 | ||||
|             b_w_in = w_in if i == 0 else w_out | ||||
|             trans_fun = get_trans_fun(cfg.RESNET.TRANS_FUN) | ||||
|             res_block = ResBlock(b_w_in, w_out, b_stride, trans_fun, w_b, num_gs) | ||||
|             self.add_module("b{}".format(i + 1), res_block) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         for block in self.children(): | ||||
|             x = block(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out, stride, d, w_b=None, num_gs=1): | ||||
|         for i in range(d): | ||||
|             b_stride = stride if i == 0 else 1 | ||||
|             b_w_in = w_in if i == 0 else w_out | ||||
|             trans_f = get_trans_fun(cfg.RESNET.TRANS_FUN) | ||||
|             cx = ResBlock.complexity(cx, b_w_in, w_out, b_stride, trans_f, w_b, num_gs) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class ResStemCifar(nn.Module): | ||||
|     """ResNet stem for CIFAR: 3x3, BN, ReLU.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out): | ||||
|         super(ResStemCifar, self).__init__() | ||||
|         self.conv = nn.Conv2d(w_in, w_out, 3, stride=1, padding=1, bias=False) | ||||
|         self.bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.relu = nn.ReLU(cfg.MEM.RELU_INPLACE) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         for layer in self.children(): | ||||
|             x = layer(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out): | ||||
|         cx = net.complexity_conv2d(cx, w_in, w_out, 3, 1, 1) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class ResStemIN(nn.Module): | ||||
|     """ResNet stem for ImageNet: 7x7, BN, ReLU, MaxPool.""" | ||||
|  | ||||
|     def __init__(self, w_in, w_out): | ||||
|         super(ResStemIN, self).__init__() | ||||
|         self.conv = nn.Conv2d(w_in, w_out, 7, stride=2, padding=3, bias=False) | ||||
|         self.bn = nn.BatchNorm2d(w_out, eps=cfg.BN.EPS, momentum=cfg.BN.MOM) | ||||
|         self.relu = nn.ReLU(cfg.MEM.RELU_INPLACE) | ||||
|         self.pool = nn.MaxPool2d(3, stride=2, padding=1) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         for layer in self.children(): | ||||
|             x = layer(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx, w_in, w_out): | ||||
|         cx = net.complexity_conv2d(cx, w_in, w_out, 7, 2, 3) | ||||
|         cx = net.complexity_batchnorm2d(cx, w_out) | ||||
|         cx = net.complexity_maxpool2d(cx, 3, 2, 1) | ||||
|         return cx | ||||
|  | ||||
|  | ||||
| class ResNet(nn.Module): | ||||
|     """ResNet model.""" | ||||
|  | ||||
|     def __init__(self): | ||||
|         datasets = ["cifar10", "imagenet"] | ||||
|         err_str = "Dataset {} is not supported" | ||||
|         assert cfg.TRAIN.DATASET in datasets, err_str.format(cfg.TRAIN.DATASET) | ||||
|         assert cfg.TEST.DATASET in datasets, err_str.format(cfg.TEST.DATASET) | ||||
|         super(ResNet, self).__init__() | ||||
|         if "cifar" in cfg.TRAIN.DATASET: | ||||
|             self._construct_cifar() | ||||
|         else: | ||||
|             self._construct_imagenet() | ||||
|         self.apply(net.init_weights) | ||||
|  | ||||
|     def _construct_cifar(self): | ||||
|         err_str = "Model depth should be of the format 6n + 2 for cifar" | ||||
|         assert (cfg.MODEL.DEPTH - 2) % 6 == 0, err_str | ||||
|         d = int((cfg.MODEL.DEPTH - 2) / 6) | ||||
|         self.stem = ResStemCifar(3, 16) | ||||
|         self.s1 = ResStage(16, 16, stride=1, d=d) | ||||
|         self.s2 = ResStage(16, 32, stride=2, d=d) | ||||
|         self.s3 = ResStage(32, 64, stride=2, d=d) | ||||
|         self.head = ResHead(64, nc=cfg.MODEL.NUM_CLASSES) | ||||
|  | ||||
|     def _construct_imagenet(self): | ||||
|         g, gw = cfg.RESNET.NUM_GROUPS, cfg.RESNET.WIDTH_PER_GROUP | ||||
|         (d1, d2, d3, d4) = _IN_STAGE_DS[cfg.MODEL.DEPTH] | ||||
|         w_b = gw * g | ||||
|         self.stem = ResStemIN(3, 64) | ||||
|         self.s1 = ResStage(64, 256, stride=1, d=d1, w_b=w_b, num_gs=g) | ||||
|         self.s2 = ResStage(256, 512, stride=2, d=d2, w_b=w_b * 2, num_gs=g) | ||||
|         self.s3 = ResStage(512, 1024, stride=2, d=d3, w_b=w_b * 4, num_gs=g) | ||||
|         self.s4 = ResStage(1024, 2048, stride=2, d=d4, w_b=w_b * 8, num_gs=g) | ||||
|         self.head = ResHead(2048, nc=cfg.MODEL.NUM_CLASSES) | ||||
|  | ||||
|     def forward(self, x): | ||||
|         for module in self.children(): | ||||
|             x = module(x) | ||||
|         return x | ||||
|  | ||||
|     @staticmethod | ||||
|     def complexity(cx): | ||||
|         """Computes model complexity. If you alter the model, make sure to update.""" | ||||
|         if "cifar" in cfg.TRAIN.DATASET: | ||||
|             d = int((cfg.MODEL.DEPTH - 2) / 6) | ||||
|             cx = ResStemCifar.complexity(cx, 3, 16) | ||||
|             cx = ResStage.complexity(cx, 16, 16, stride=1, d=d) | ||||
|             cx = ResStage.complexity(cx, 16, 32, stride=2, d=d) | ||||
|             cx = ResStage.complexity(cx, 32, 64, stride=2, d=d) | ||||
|             cx = ResHead.complexity(cx, 64, nc=cfg.MODEL.NUM_CLASSES) | ||||
|         else: | ||||
|             g, gw = cfg.RESNET.NUM_GROUPS, cfg.RESNET.WIDTH_PER_GROUP | ||||
|             (d1, d2, d3, d4) = _IN_STAGE_DS[cfg.MODEL.DEPTH] | ||||
|             w_b = gw * g | ||||
|             cx = ResStemIN.complexity(cx, 3, 64) | ||||
|             cx = ResStage.complexity(cx, 64, 256, 1, d=d1, w_b=w_b, num_gs=g) | ||||
|             cx = ResStage.complexity(cx, 256, 512, 2, d=d2, w_b=w_b * 2, num_gs=g) | ||||
|             cx = ResStage.complexity(cx, 512, 1024, 2, d=d3, w_b=w_b * 4, num_gs=g) | ||||
|             cx = ResStage.complexity(cx, 1024, 2048, 2, d=d4, w_b=w_b * 8, num_gs=g) | ||||
|             cx = ResHead.complexity(cx, 2048, nc=cfg.MODEL.NUM_CLASSES) | ||||
|         return cx | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										304
									
								
								graph_dit/naswot/score_networks.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										304
									
								
								graph_dit/naswot/score_networks.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,304 @@ | ||||
| import argparse | ||||
| import nasspace | ||||
| import datasets | ||||
| import random | ||||
| import numpy as np | ||||
| import torch | ||||
| import os | ||||
| from scores import get_score_func | ||||
| from scipy import stats | ||||
| import time | ||||
| # from pycls.models.nas.nas import Cell | ||||
| from utils import add_dropout, init_network  | ||||
|  | ||||
| parser = argparse.ArgumentParser(description='NAS Without Training') | ||||
| parser.add_argument('--data_loc', default='../cifardata/', type=str, help='dataset folder') | ||||
| parser.add_argument('--api_loc', default='../NAS-Bench-201-v1_0-e61699.pth', | ||||
|                     type=str, help='path to API') | ||||
| parser.add_argument('--save_loc', default='results', type=str, help='folder to save results') | ||||
| parser.add_argument('--save_string', default='naswot', type=str, help='prefix of results file') | ||||
| parser.add_argument('--score', default='hook_logdet', type=str, help='the score to evaluate') | ||||
| parser.add_argument('--nasspace', default='nasbench201', type=str, help='the nas search space to use') | ||||
| parser.add_argument('--batch_size', default=128, type=int) | ||||
| parser.add_argument('--repeat', default=1, type=int, help='how often to repeat a single image with a batch') | ||||
| parser.add_argument('--augtype', default='none', type=str, help='which perturbations to use') | ||||
| parser.add_argument('--sigma', default=0.05, type=float, help='noise level if augtype is "gaussnoise"') | ||||
| parser.add_argument('--GPU', default='0', type=str) | ||||
| parser.add_argument('--seed', default=1, type=int) | ||||
| parser.add_argument('--init', default='', type=str) | ||||
| parser.add_argument('--trainval', action='store_true') | ||||
| parser.add_argument('--dropout', action='store_true') | ||||
| parser.add_argument('--dataset', default='cifar10', type=str) | ||||
| parser.add_argument('--maxofn', default=1, type=int, help='score is the max of this many evaluations of the network') | ||||
| parser.add_argument('--n_samples', default=100, type=int) | ||||
| parser.add_argument('--n_runs', default=500, type=int) | ||||
| parser.add_argument('--stem_out_channels', default=16, type=int, help='output channels of stem convolution (nasbench101)') | ||||
| parser.add_argument('--num_stacks', default=3, type=int, help='#stacks of modules (nasbench101)') | ||||
| parser.add_argument('--num_modules_per_stack', default=3, type=int, help='#modules per stack (nasbench101)') | ||||
| parser.add_argument('--num_labels', default=1, type=int, help='#classes (nasbench101)') | ||||
|  | ||||
| args = parser.parse_args() | ||||
| os.environ['CUDA_VISIBLE_DEVICES'] = args.GPU | ||||
|  | ||||
| # Reproducibility | ||||
| torch.backends.cudnn.deterministic = True | ||||
| torch.backends.cudnn.benchmark = False | ||||
| random.seed(args.seed) | ||||
| np.random.seed(args.seed) | ||||
| torch.manual_seed(args.seed) | ||||
|  | ||||
|  | ||||
| def get_batch_jacobian(net, x, target, device, args=None): | ||||
|     net.zero_grad() | ||||
|     x.requires_grad_(True) | ||||
|     y, out = net(x) | ||||
|     y.backward(torch.ones_like(y)) | ||||
|     jacob = x.grad.detach() | ||||
|     return jacob, target.detach(), y.detach(), out.detach() | ||||
|  | ||||
| def get_nasbench201_idx_score(idx, train_loader, searchspace, args): | ||||
|     device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") | ||||
|     # searchspace = nasspace.get_search_space(args) | ||||
|     if 'valid' in args.dataset: | ||||
|         args.dataset = args.dataset.replace('-valid', '') | ||||
|  | ||||
|     # train_loader = datasets.get_data(args.dataset, args.data_loc, args.trainval, args.batch_size, args.augtype, args.repeat, args) | ||||
|     # os.makedirs(args.save_loc, exist_ok=True) | ||||
|     # filename = f'{args.save_loc}/{args.save_string}_{args.score}_{args.nasspace}_{args.dataset}{"_" + args.init + "_" if args.init != "" else args.init}_{"_dropout" if args.dropout else ""}_{args.augtype}_{args.sigma}_{args.repeat}_{args.trainval}_{args.batch_size}_{args.maxofn}_{args.seed}' | ||||
|     # accfilename = f'{args.save_loc}/{args.save_string}_accs_{args.nasspace}_{args.dataset}_{args.trainval}' | ||||
|     # scores = np.zeros(len(searchspace)) | ||||
|  | ||||
|     # accs = np.zeros(len(searchspace)) | ||||
|  | ||||
|     i = idx | ||||
|     uid = idx | ||||
|     print(f'uid: {uid}') | ||||
|     print(f'get network') | ||||
|     network = searchspace.get_network(uid) | ||||
|     print(f'get network done') | ||||
|     try: | ||||
|         if args.dropout: | ||||
|             add_dropout(network, args.sigma) | ||||
|         if args.init != '': | ||||
|             init_network(network, args.init) | ||||
|         if 'hook_' in args.score: | ||||
|             network.K = np.zeros((args.batch_size, args.batch_size)) | ||||
|             def counting_forward_hook(module, inp, out): | ||||
|                 try: | ||||
|                     if not module.visited_backwards: | ||||
|                         return | ||||
|                     if isinstance(inp, tuple): | ||||
|                         # print(len(inp)) | ||||
|                         inp = inp[0] | ||||
|                     inp = inp.view(inp.size(0), -1) | ||||
|                     x = (inp > 0).float() | ||||
|                     K = x @ x.t() | ||||
|                     K2 = (1.-x) @ (1.-x.t()) | ||||
|                     network.K = network.K + K.cpu().numpy() + K2.cpu().numpy() | ||||
|                 except: | ||||
|                     pass | ||||
|  | ||||
|                  | ||||
|             def counting_backward_hook(module, inp, out): | ||||
|                 module.visited_backwards = True | ||||
|  | ||||
|                  | ||||
|             for name, module in network.named_modules(): | ||||
|                 if 'ReLU' in str(type(module)): | ||||
|                     #hooks[name] = module.register_forward_hook(counting_hook) | ||||
|                     module.register_forward_hook(counting_forward_hook) | ||||
|                     module.register_backward_hook(counting_backward_hook) | ||||
|  | ||||
|         network = network.to(device) | ||||
|         random.seed(args.seed) | ||||
|         np.random.seed(args.seed) | ||||
|         torch.manual_seed(args.seed) | ||||
|         s = [] | ||||
|         for j in range(args.maxofn): | ||||
|             data_iterator = iter(train_loader) | ||||
|             x, target = next(data_iterator) | ||||
|             x2 = torch.clone(x) | ||||
|             x2 = x2.to(device) | ||||
|             x, target = x.to(device), target.to(device) | ||||
|             jacobs, labels, y, out = get_batch_jacobian(network, x, target, device, args) | ||||
|  | ||||
|  | ||||
|             if 'hook_' in args.score: | ||||
|                 network(x2.to(device)) | ||||
|                 s.append(get_score_func(args.score)(network.K, target)) | ||||
|             else: | ||||
|                 s.append(get_score_func(args.score)(jacobs, labels)) | ||||
|         return np.mean(s) | ||||
|         scores[i] = np.mean(s) | ||||
|         accs[i] = searchspace.get_final_accuracy(uid, acc_type, args.trainval) | ||||
|         accs_ = accs[~np.isnan(scores)] | ||||
|         scores_ = scores[~np.isnan(scores)] | ||||
|         numnan = np.isnan(scores).sum() | ||||
|         tau, p = stats.kendalltau(accs_[:max(i-numnan, 1)], scores_[:max(i-numnan, 1)]) | ||||
|         print(f'{tau}') | ||||
|         if i % 1000 == 0: | ||||
|             np.save(filename, scores) | ||||
|             np.save(accfilename, accs) | ||||
|     except Exception as e: | ||||
|         print(e) | ||||
|     print('final result') | ||||
|     return np.nan | ||||
|  | ||||
| class Args: | ||||
|     pass | ||||
| args = Args() | ||||
| args.trainval = True | ||||
| args.augtype = 'none' | ||||
| args.repeat = 1 | ||||
| args.score = 'hook_logdet' | ||||
| args.sigma = 0.05 | ||||
| args.nasspace = 'nasbench201' | ||||
| args.batch_size = 128 | ||||
| args.GPU = '0' | ||||
| args.dataset = 'cifar10-valid' | ||||
| args.api_loc = '/home/stud/hanzhang/nasbenchDiT/graph_dit/NAS-Bench-201-v1_1-096897.pth' | ||||
| args.data_loc = '../cifardata/' | ||||
| args.seed = 777 | ||||
| args.init = '' | ||||
| args.save_loc = 'results' | ||||
| args.save_string = 'naswot' | ||||
| args.dropout = False | ||||
| args.maxofn = 1 | ||||
| args.n_samples = 100 | ||||
| args.n_runs = 500 | ||||
| args.stem_out_channels = 16 | ||||
| args.num_stacks = 3 | ||||
| args.num_modules_per_stack = 3 | ||||
| args.num_labels = 1 | ||||
|  | ||||
| if 'valid' in args.dataset: | ||||
|     args.dataset = args.dataset.replace('-valid', '') | ||||
| print('start to get search space') | ||||
| start_time = time.time() | ||||
| searchspace = nasspace.get_search_space(args) | ||||
| end_time = time.time() | ||||
| print(f'search space time: {end_time - start_time}') | ||||
| train_loader = datasets.get_data(args.dataset, args.data_loc, args.trainval, args.batch_size, args.augtype, args.repeat, args) | ||||
| print('start to get score') | ||||
| print('5374') | ||||
| start_time = time.time() | ||||
| print(get_nasbench201_idx_score(5374,train_loader=train_loader, searchspace=searchspace, args=args)) | ||||
| end_time = time.time() | ||||
| print(f'5374 time: {end_time - start_time}') | ||||
| print('5375') | ||||
| start_time = time.time() | ||||
| print(get_nasbench201_idx_score(5375,train_loader=train_loader, searchspace=searchspace, args=args)) | ||||
| end_time = time.time() | ||||
| print(f'5375 time: {end_time - start_time}') | ||||
| print('5376') | ||||
| start_time = time.time() | ||||
| print(get_nasbench201_idx_score(5376,train_loader=train_loader, searchspace=searchspace, args=args)) | ||||
| end_time = time.time() | ||||
| print(f'5376 time: {end_time - start_time}') | ||||
|  | ||||
| # device = "cuda:0" | ||||
| # dataset = dataset | ||||
|  | ||||
| # device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") | ||||
| # savedataset = args.dataset | ||||
| # dataset = 'fake' if 'fake' in args.dataset else args.dataset | ||||
| # args.dataset = args.dataset.replace('fake', '') | ||||
| # if args.dataset == 'cifar10': | ||||
| #     args.dataset = args.dataset + '-valid' | ||||
| # searchspace = nasspace.get_search_space(args) | ||||
| # if 'valid' in args.dataset: | ||||
| #     args.dataset = args.dataset.replace('-valid', '') | ||||
| # train_loader = datasets.get_data(args.dataset, args.data_loc, args.trainval, args.batch_size, args.augtype, args.repeat, args) | ||||
| # os.makedirs(args.save_loc, exist_ok=True) | ||||
|  | ||||
| # filename = f'{args.save_loc}/{args.save_string}_{args.score}_{args.nasspace}_{savedataset}{"_" + args.init + "_" if args.init != "" else args.init}_{"_dropout" if args.dropout else ""}_{args.augtype}_{args.sigma}_{args.repeat}_{args.trainval}_{args.batch_size}_{args.maxofn}_{args.seed}' | ||||
| # accfilename = f'{args.save_loc}/{args.save_string}_accs_{args.nasspace}_{savedataset}_{args.trainval}' | ||||
|  | ||||
| # if args.dataset == 'cifar10': | ||||
| #     acc_type = 'ori-test' | ||||
| #     val_acc_type = 'x-valid' | ||||
| # else: | ||||
| #     acc_type = 'x-test' | ||||
| #     val_acc_type = 'x-valid' | ||||
|  | ||||
|  | ||||
| # scores = np.zeros(len(searchspace)) | ||||
| # try: | ||||
| #     accs = np.load(accfilename + '.npy') | ||||
| # except: | ||||
| #     accs = np.zeros(len(searchspace)) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| # for i, (uid, network) in enumerate(searchspace): | ||||
| #     # Reproducibility | ||||
| #     try: | ||||
| #         if args.dropout: | ||||
| #             add_dropout(network, args.sigma) | ||||
| #         if args.init != '': | ||||
| #             init_network(network, args.init) | ||||
| #         if 'hook_' in args.score: | ||||
| #             network.K = np.zeros((args.batch_size, args.batch_size)) | ||||
| #             def counting_forward_hook(module, inp, out): | ||||
| #                 try: | ||||
| #                     if not module.visited_backwards: | ||||
| #                         return | ||||
| #                     if isinstance(inp, tuple): | ||||
| #                         print(len(inp)) | ||||
| #                         inp = inp[0] | ||||
| #                     inp = inp.view(inp.size(0), -1) | ||||
| #                     x = (inp > 0).float() | ||||
| #                     K = x @ x.t() | ||||
| #                     K2 = (1.-x) @ (1.-x.t()) | ||||
| #                     network.K = network.K + K.cpu().numpy() + K2.cpu().numpy() | ||||
| #                 except: | ||||
| #                     pass | ||||
|  | ||||
|                  | ||||
| #             def counting_backward_hook(module, inp, out): | ||||
| #                 module.visited_backwards = True | ||||
|  | ||||
|                  | ||||
| #             for name, module in network.named_modules(): | ||||
| #                 if 'ReLU' in str(type(module)): | ||||
| #                     #hooks[name] = module.register_forward_hook(counting_hook) | ||||
| #                     module.register_forward_hook(counting_forward_hook) | ||||
| #                     module.register_backward_hook(counting_backward_hook) | ||||
|  | ||||
| #         network = network.to(device) | ||||
| #         random.seed(args.seed) | ||||
| #         np.random.seed(args.seed) | ||||
| #         torch.manual_seed(args.seed) | ||||
| #         s = [] | ||||
| #         for j in range(args.maxofn): | ||||
| #             data_iterator = iter(train_loader) | ||||
| #             x, target = next(data_iterator) | ||||
| #             x2 = torch.clone(x) | ||||
| #             x2 = x2.to(device) | ||||
| #             x, target = x.to(device), target.to(device) | ||||
| #             jacobs, labels, y, out = get_batch_jacobian(network, x, target, device, args) | ||||
|  | ||||
|  | ||||
| #             if 'hook_' in args.score: | ||||
| #                 network(x2.to(device)) | ||||
| #                 s.append(get_score_func(args.score)(network.K, target)) | ||||
| #             else: | ||||
| #                 s.append(get_score_func(args.score)(jacobs, labels)) | ||||
| #         scores[i] = np.mean(s) | ||||
| #         accs[i] = searchspace.get_final_accuracy(uid, acc_type, args.trainval) | ||||
| #         accs_ = accs[~np.isnan(scores)] | ||||
| #         scores_ = scores[~np.isnan(scores)] | ||||
| #         numnan = np.isnan(scores).sum() | ||||
| #         tau, p = stats.kendalltau(accs_[:max(i-numnan, 1)], scores_[:max(i-numnan, 1)]) | ||||
| #         print(f'{tau}') | ||||
| #         if i % 1000 == 0: | ||||
| #             np.save(filename, scores) | ||||
| #             np.save(accfilename, accs) | ||||
| #     except Exception as e: | ||||
| #         print(e) | ||||
| #         accs[i] = searchspace.get_final_accuracy(uid, acc_type, args.trainval) | ||||
| #         scores[i] = np.nan | ||||
| # np.save(filename, scores) | ||||
| # np.save(accfilename, accs) | ||||
							
								
								
									
										21
									
								
								graph_dit/naswot/scores.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								graph_dit/naswot/scores.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| import numpy as np | ||||
| import torch | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| def hooklogdet(K, labels=None): | ||||
|     s, ld = np.linalg.slogdet(K) | ||||
|     return ld | ||||
|  | ||||
| def random_score(jacob, label=None): | ||||
|     return np.random.normal() | ||||
|  | ||||
|  | ||||
| _scores = { | ||||
|         'hook_logdet': hooklogdet, | ||||
|         'random': random_score | ||||
|         } | ||||
|  | ||||
| def get_score_func(score_name): | ||||
|     return _scores[score_name] | ||||
							
								
								
									
										100
									
								
								graph_dit/naswot/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								graph_dit/naswot/utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| import torch | ||||
| from pycls.models.nas.nas import Cell | ||||
|  | ||||
| class DropChannel(torch.nn.Module): | ||||
|     def __init__(self, p, mod): | ||||
|         super(DropChannel, self).__init__() | ||||
|         self.mod = mod | ||||
|         self.p = p | ||||
|     def forward(self, s0, s1, droppath): | ||||
|         ret = self.mod(s0, s1, droppath) | ||||
|         return ret | ||||
|  | ||||
|  | ||||
| class DropConnect(torch.nn.Module): | ||||
|     def __init__(self, p): | ||||
|         super(DropConnect, self).__init__() | ||||
|         self.p = p | ||||
|     def forward(self, inputs): | ||||
|         batch_size = inputs.shape[0] | ||||
|         dim1 = inputs.shape[2] | ||||
|         dim2 = inputs.shape[3] | ||||
|         channel_size = inputs.shape[1] | ||||
|         keep_prob = 1 - self.p | ||||
|         # generate binary_tensor mask according to probability (p for 0, 1-p for 1) | ||||
|         random_tensor = keep_prob | ||||
|         random_tensor += torch.rand([batch_size, channel_size, 1, 1], dtype=inputs.dtype, device=inputs.device) | ||||
|         binary_tensor = torch.floor(random_tensor) | ||||
|         output = inputs / keep_prob * binary_tensor | ||||
|         return output     | ||||
|  | ||||
| def add_dropout(network, p, prefix=''): | ||||
|     #p = 0.5 | ||||
|     for attr_str in dir(network): | ||||
|         target_attr = getattr(network, attr_str) | ||||
|         if isinstance(target_attr, torch.nn.Conv2d): | ||||
|             setattr(network, attr_str, torch.nn.Sequential(target_attr, DropConnect(p))) | ||||
|         elif isinstance(target_attr, Cell): | ||||
|             setattr(network, attr_str, DropChannel(p, target_attr)) | ||||
|     for n, ch in list(network.named_children()): | ||||
|         #print(f'{prefix}add_dropout {n}') | ||||
|         if isinstance(ch, torch.nn.Conv2d): | ||||
|             setattr(network, n, torch.nn.Sequential(ch, DropConnect(p))) | ||||
|         elif isinstance(ch, Cell): | ||||
|             setattr(network, n, DropChannel(p, ch)) | ||||
|         else: | ||||
|             add_dropout(ch, p, prefix + '\t') | ||||
|               | ||||
|  | ||||
|  | ||||
|  | ||||
| def orth_init(m): | ||||
|     if isinstance(m, (torch.nn.Conv2d, torch.nn.Linear)): | ||||
|         torch.nn.init.orthogonal_(m.weight) | ||||
|  | ||||
| def uni_init(m): | ||||
|     if isinstance(m, (torch.nn.Conv2d, torch.nn.Linear)): | ||||
|         torch.nn.init.uniform_(m.weight) | ||||
|  | ||||
| def uni2_init(m): | ||||
|     if isinstance(m, (torch.nn.Conv2d, torch.nn.Linear)): | ||||
|         torch.nn.init.uniform_(m.weight, -1., 1.) | ||||
|  | ||||
| def uni3_init(m): | ||||
|     if isinstance(m, (torch.nn.Conv2d, torch.nn.Linear)): | ||||
|         torch.nn.init.uniform_(m.weight, -.5, .5) | ||||
|  | ||||
| def norm_init(m): | ||||
|     if isinstance(m, (torch.nn.Conv2d, torch.nn.Linear)): | ||||
|         torch.nn.init.norm_(m.weight) | ||||
|  | ||||
| def eye_init(m): | ||||
|     if isinstance(m, torch.nn.Linear): | ||||
|         torch.nn.init.eye_(m.weight) | ||||
|     elif isinstance(m, torch.nn.Conv2d): | ||||
|         torch.nn.init.dirac_(m.weight) | ||||
|  | ||||
|  | ||||
|  | ||||
| def fixup_init(m): | ||||
|     if isinstance(m, torch.nn.Conv2d): | ||||
|         torch.nn.init.zero_(m.weight) | ||||
|     elif isinstance(m, torch.nn.Linear): | ||||
|         torch.nn.init.zero_(m.weight) | ||||
|         torch.nn.init.zero_(m.bias) | ||||
|  | ||||
|  | ||||
| def init_network(network, init): | ||||
|     if init == 'orthogonal': | ||||
|         network.apply(orth_init) | ||||
|     elif init == 'uniform': | ||||
|         print('uniform') | ||||
|         network.apply(uni_init) | ||||
|     elif init == 'uniform2': | ||||
|         network.apply(uni2_init) | ||||
|     elif init == 'uniform3': | ||||
|         network.apply(uni3_init) | ||||
|     elif init == 'normal': | ||||
|         network.apply(norm_init) | ||||
|     elif init == 'identity': | ||||
|         network.apply(eye_init) | ||||
		Reference in New Issue
	
	Block a user