train.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. # Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from __future__ import absolute_import
  15. from __future__ import division
  16. from __future__ import print_function
  17. import os
  18. import sys
  19. # add python path of PadleDetection to sys.path
  20. parent_path = os.path.abspath(os.path.join(__file__, *(['..'] * 3)))
  21. if parent_path not in sys.path:
  22. sys.path.append(parent_path)
  23. import time
  24. import numpy as np
  25. import datetime
  26. from collections import deque
  27. import shutil
  28. from paddle import fluid
  29. import logging
  30. FORMAT = '%(asctime)s-%(levelname)s: %(message)s'
  31. logging.basicConfig(level=logging.INFO, format=FORMAT)
  32. logger = logging.getLogger(__name__)
  33. try:
  34. from ppdet.core.workspace import load_config, merge_config, create
  35. from ppdet.data.reader import create_reader
  36. from ppdet.utils import dist_utils
  37. from ppdet.utils.eval_utils import parse_fetches, eval_run, eval_results
  38. from ppdet.utils.stats import TrainingStats
  39. from ppdet.utils.cli import ArgsParser
  40. from ppdet.utils.check import check_gpu, check_version, check_config, enable_static_mode
  41. import ppdet.utils.checkpoint as checkpoint
  42. except ImportError as e:
  43. if sys.argv[0].find('static') >= 0:
  44. logger.error("Importing ppdet failed when running static model "
  45. "with error: {}\n"
  46. "please try:\n"
  47. "\t1. run static model under PaddleDetection/static "
  48. "directory\n"
  49. "\t2. run 'pip uninstall ppdet' to uninstall ppdet "
  50. "dynamic version firstly.".format(e))
  51. sys.exit(-1)
  52. else:
  53. raise e
  54. from paddleslim.quant import quant_aware
  55. from pact import pact, get_optimizer
  56. def save_checkpoint(exe, prog, path, train_prog):
  57. if os.path.isdir(path):
  58. shutil.rmtree(path)
  59. logger.info('Save model to {}.'.format(path))
  60. fluid.io.save_persistables(exe, path, main_program=prog)
  61. def main():
  62. if FLAGS.eval is False:
  63. raise ValueError(
  64. "Currently only supports `--eval==True` while training in `quantization`."
  65. )
  66. env = os.environ
  67. FLAGS.dist = 'PADDLE_TRAINER_ID' in env \
  68. and 'PADDLE_TRAINERS_NUM' in env \
  69. and int(env['PADDLE_TRAINERS_NUM']) > 1
  70. num_trainers = int(env.get('PADDLE_TRAINERS_NUM', 1))
  71. if FLAGS.dist:
  72. trainer_id = int(env['PADDLE_TRAINER_ID'])
  73. import random
  74. local_seed = (99 + trainer_id)
  75. random.seed(local_seed)
  76. np.random.seed(local_seed)
  77. cfg = load_config(FLAGS.config)
  78. merge_config(FLAGS.opt)
  79. check_config(cfg)
  80. # check if set use_gpu=True in paddlepaddle cpu version
  81. check_gpu(cfg.use_gpu)
  82. # check if paddlepaddle version is satisfied
  83. check_version()
  84. main_arch = cfg.architecture
  85. if cfg.use_gpu:
  86. devices_num = fluid.core.get_cuda_device_count()
  87. else:
  88. devices_num = int(os.environ.get('CPU_NUM', 1))
  89. if 'FLAGS_selected_gpus' in env:
  90. device_id = int(env['FLAGS_selected_gpus'])
  91. else:
  92. device_id = 0
  93. place = fluid.CUDAPlace(device_id) if cfg.use_gpu else fluid.CPUPlace()
  94. exe = fluid.Executor(place)
  95. lr_builder = create('LearningRate')
  96. optim_builder = create('OptimizerBuilder')
  97. # build program
  98. startup_prog = fluid.Program()
  99. train_prog = fluid.Program()
  100. with fluid.program_guard(train_prog, startup_prog):
  101. with fluid.unique_name.guard():
  102. model = create(main_arch)
  103. inputs_def = cfg['TrainReader']['inputs_def']
  104. feed_vars, train_loader = model.build_inputs(**inputs_def)
  105. if FLAGS.use_pact:
  106. feed_vars['image'].stop_gradient = False
  107. train_fetches = model.train(feed_vars)
  108. loss = train_fetches['loss']
  109. lr = lr_builder()
  110. optimizer = optim_builder(lr)
  111. optimizer.minimize(loss)
  112. # parse train fetches
  113. train_keys, train_values, _ = parse_fetches(train_fetches)
  114. train_values.append(lr)
  115. if FLAGS.eval:
  116. eval_prog = fluid.Program()
  117. with fluid.program_guard(eval_prog, startup_prog):
  118. with fluid.unique_name.guard():
  119. model = create(main_arch)
  120. inputs_def = cfg['EvalReader']['inputs_def']
  121. feed_vars, eval_loader = model.build_inputs(**inputs_def)
  122. fetches = model.eval(feed_vars)
  123. eval_prog = eval_prog.clone(True)
  124. eval_reader = create_reader(cfg.EvalReader)
  125. # When iterable mode, set set_sample_list_generator(eval_reader, place)
  126. eval_loader.set_sample_list_generator(eval_reader)
  127. # parse eval fetches
  128. extra_keys = []
  129. if cfg.metric == 'COCO':
  130. extra_keys = ['im_info', 'im_id', 'im_shape']
  131. if cfg.metric == 'VOC':
  132. extra_keys = ['gt_bbox', 'gt_class', 'is_difficult']
  133. if cfg.metric == 'WIDERFACE':
  134. extra_keys = ['im_id', 'im_shape', 'gt_bbox']
  135. eval_keys, eval_values, eval_cls = parse_fetches(fetches, eval_prog,
  136. extra_keys)
  137. # compile program for multi-devices
  138. build_strategy = fluid.BuildStrategy()
  139. build_strategy.fuse_all_optimizer_ops = False
  140. build_strategy.fuse_elewise_add_act_ops = True
  141. build_strategy.fuse_all_reduce_ops = False
  142. # only enable sync_bn in multi GPU devices
  143. sync_bn = getattr(model.backbone, 'norm_type', None) == 'sync_bn'
  144. sync_bn = False
  145. build_strategy.sync_batch_norm = sync_bn and devices_num > 1 \
  146. and cfg.use_gpu
  147. exec_strategy = fluid.ExecutionStrategy()
  148. # iteration number when CompiledProgram tries to drop local execution scopes.
  149. # Set it to be 1 to save memory usages, so that unused variables in
  150. # local execution scopes can be deleted after each iteration.
  151. exec_strategy.num_iteration_per_drop_scope = 1
  152. if FLAGS.dist:
  153. dist_utils.prepare_for_multi_process(exe, build_strategy, startup_prog,
  154. train_prog)
  155. exec_strategy.num_threads = 1
  156. exe.run(startup_prog)
  157. not_quant_pattern = []
  158. if FLAGS.not_quant_pattern:
  159. not_quant_pattern = FLAGS.not_quant_pattern
  160. config = {
  161. 'weight_quantize_type': 'channel_wise_abs_max',
  162. 'activation_quantize_type': 'moving_average_abs_max',
  163. 'quantize_op_types': ['depthwise_conv2d', 'mul', 'conv2d'],
  164. 'not_quant_pattern': not_quant_pattern
  165. }
  166. ignore_params = cfg.finetune_exclude_pretrained_params \
  167. if 'finetune_exclude_pretrained_params' in cfg else []
  168. fuse_bn = getattr(model.backbone, 'norm_type', None) == 'affine_channel'
  169. if cfg.pretrain_weights and fuse_bn and not ignore_params:
  170. checkpoint.load_and_fusebn(exe, train_prog, cfg.pretrain_weights)
  171. elif cfg.pretrain_weights:
  172. checkpoint.load_params(
  173. exe, train_prog, cfg.pretrain_weights, ignore_params=ignore_params)
  174. if FLAGS.use_pact:
  175. act_preprocess_func = pact
  176. optimizer_func = get_optimizer
  177. executor = exe
  178. else:
  179. act_preprocess_func = None
  180. optimizer_func = None
  181. executor = None
  182. # insert quantize op in train_prog, return type is CompiledProgram
  183. train_prog_quant = quant_aware(
  184. train_prog,
  185. place,
  186. config,
  187. scope=None,
  188. act_preprocess_func=act_preprocess_func,
  189. optimizer_func=optimizer_func,
  190. executor=executor,
  191. for_test=False)
  192. compiled_train_prog = train_prog_quant.with_data_parallel(
  193. loss_name=loss.name,
  194. build_strategy=build_strategy,
  195. exec_strategy=exec_strategy)
  196. if FLAGS.eval:
  197. # insert quantize op in eval_prog
  198. eval_prog = quant_aware(
  199. eval_prog,
  200. place,
  201. config,
  202. scope=None,
  203. act_preprocess_func=act_preprocess_func,
  204. optimizer_func=optimizer_func,
  205. executor=executor,
  206. for_test=True)
  207. compiled_eval_prog = fluid.CompiledProgram(eval_prog)
  208. start_iter = 0
  209. train_reader = create_reader(
  210. cfg.TrainReader, (cfg.max_iters - start_iter) * devices_num,
  211. cfg,
  212. devices_num=devices_num,
  213. num_trainers=num_trainers)
  214. # When iterable mode, set set_sample_list_generator(train_reader, place)
  215. train_loader.set_sample_list_generator(train_reader)
  216. # whether output bbox is normalized in model output layer
  217. is_bbox_normalized = False
  218. if hasattr(model, 'is_bbox_normalized') and \
  219. callable(model.is_bbox_normalized):
  220. is_bbox_normalized = model.is_bbox_normalized()
  221. # if map_type not set, use default 11point, only use in VOC eval
  222. map_type = cfg.map_type if 'map_type' in cfg else '11point'
  223. train_stats = TrainingStats(cfg.log_iter, train_keys)
  224. train_loader.start()
  225. start_time = time.time()
  226. end_time = time.time()
  227. cfg_name = os.path.basename(FLAGS.config).split('.')[0]
  228. save_dir = os.path.join(cfg.save_dir, cfg_name)
  229. time_stat = deque(maxlen=cfg.log_iter)
  230. best_box_ap_list = [0.0, 0] #[map, iter]
  231. for it in range(start_iter, cfg.max_iters):
  232. start_time = end_time
  233. end_time = time.time()
  234. time_stat.append(end_time - start_time)
  235. time_cost = np.mean(time_stat)
  236. eta_sec = (cfg.max_iters - it) * time_cost
  237. eta = str(datetime.timedelta(seconds=int(eta_sec)))
  238. outs = exe.run(compiled_train_prog, fetch_list=train_values)
  239. stats = {k: np.array(v).mean() for k, v in zip(train_keys, outs[:-1])}
  240. train_stats.update(stats)
  241. logs = train_stats.log()
  242. if it % cfg.log_iter == 0 and (not FLAGS.dist or trainer_id == 0):
  243. strs = 'iter: {}, lr: {:.6f}, {}, time: {:.3f}, eta: {}'.format(
  244. it, np.mean(outs[-1]), logs, time_cost, eta)
  245. logger.info(strs)
  246. if (it > 0 and it % cfg.snapshot_iter == 0 or it == cfg.max_iters - 1) \
  247. and (not FLAGS.dist or trainer_id == 0):
  248. save_name = str(it) if it != cfg.max_iters - 1 else "model_final"
  249. if FLAGS.eval:
  250. # evaluation
  251. results = eval_run(
  252. exe,
  253. compiled_eval_prog,
  254. eval_loader,
  255. eval_keys,
  256. eval_values,
  257. eval_cls,
  258. cfg=cfg)
  259. resolution = None
  260. if 'mask' in results[0]:
  261. resolution = model.mask_head.resolution
  262. box_ap_stats = eval_results(
  263. results, cfg.metric, cfg.num_classes, resolution,
  264. is_bbox_normalized, FLAGS.output_eval, map_type,
  265. cfg['EvalReader']['dataset'])
  266. if box_ap_stats[0] > best_box_ap_list[0]:
  267. best_box_ap_list[0] = box_ap_stats[0]
  268. best_box_ap_list[1] = it
  269. save_checkpoint(exe, eval_prog,
  270. os.path.join(save_dir, "best_model"),
  271. train_prog)
  272. logger.info("Best test box ap: {}, in iter: {}".format(
  273. best_box_ap_list[0], best_box_ap_list[1]))
  274. train_loader.reset()
  275. if __name__ == '__main__':
  276. enable_static_mode()
  277. parser = ArgsParser()
  278. parser.add_argument(
  279. "--loss_scale",
  280. default=8.,
  281. type=float,
  282. help="Mixed precision training loss scale.")
  283. parser.add_argument(
  284. "--eval",
  285. action='store_true',
  286. default=False,
  287. help="Whether to perform evaluation in train")
  288. parser.add_argument(
  289. "--output_eval",
  290. default=None,
  291. type=str,
  292. help="Evaluation directory, default is current directory.")
  293. parser.add_argument(
  294. "--not_quant_pattern",
  295. nargs='+',
  296. type=str,
  297. help="Layers which name_scope contains string in not_quant_pattern will not be quantized"
  298. )
  299. parser.add_argument(
  300. "--use_pact", nargs='+', type=bool, help="Whether to use PACT or not.")
  301. FLAGS = parser.parse_args()
  302. main()