utils.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. # copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve.
  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. import sys
  15. import time
  16. import os
  17. import os.path as osp
  18. import numpy as np
  19. import six
  20. import yaml
  21. import math
  22. from . import logging
  23. def seconds_to_hms(seconds):
  24. h = math.floor(seconds / 3600)
  25. m = math.floor((seconds - h * 3600) / 60)
  26. s = int(seconds - h * 3600 - m * 60)
  27. hms_str = "{}:{}:{}".format(h, m, s)
  28. return hms_str
  29. def setting_environ_flags():
  30. if 'FLAGS_eager_delete_tensor_gb' not in os.environ:
  31. os.environ['FLAGS_eager_delete_tensor_gb'] = '0.0'
  32. if 'FLAGS_allocator_strategy' not in os.environ:
  33. os.environ['FLAGS_allocator_strategy'] = 'auto_growth'
  34. if "CUDA_VISIBLE_DEVICES" in os.environ:
  35. if os.environ["CUDA_VISIBLE_DEVICES"].count("-1") > 0:
  36. os.environ["CUDA_VISIBLE_DEVICES"] = ""
  37. def get_environ_info():
  38. setting_environ_flags()
  39. import paddle.fluid as fluid
  40. info = dict()
  41. info['place'] = 'cpu'
  42. info['num'] = int(os.environ.get('CPU_NUM', 1))
  43. if os.environ.get('CUDA_VISIBLE_DEVICES', None) != "":
  44. if hasattr(fluid.core, 'get_cuda_device_count'):
  45. gpu_num = 0
  46. try:
  47. gpu_num = fluid.core.get_cuda_device_count()
  48. except:
  49. os.environ['CUDA_VISIBLE_DEVICES'] = ''
  50. pass
  51. if gpu_num > 0:
  52. info['place'] = 'cuda'
  53. info['num'] = fluid.core.get_cuda_device_count()
  54. return info
  55. def parse_param_file(param_file, return_shape=True):
  56. from paddle.fluid.proto.framework_pb2 import VarType
  57. f = open(param_file, 'rb')
  58. version = np.fromstring(f.read(4), dtype='int32')
  59. lod_level = np.fromstring(f.read(8), dtype='int64')
  60. for i in range(int(lod_level)):
  61. _size = np.fromstring(f.read(8), dtype='int64')
  62. _ = f.read(_size)
  63. version = np.fromstring(f.read(4), dtype='int32')
  64. tensor_desc = VarType.TensorDesc()
  65. tensor_desc_size = np.fromstring(f.read(4), dtype='int32')
  66. tensor_desc.ParseFromString(f.read(int(tensor_desc_size)))
  67. tensor_shape = tuple(tensor_desc.dims)
  68. if return_shape:
  69. f.close()
  70. return tuple(tensor_desc.dims)
  71. if tensor_desc.data_type != 5:
  72. raise Exception(
  73. "Unexpected data type while parse {}".format(param_file))
  74. data_size = 4
  75. for i in range(len(tensor_shape)):
  76. data_size *= tensor_shape[i]
  77. weight = np.fromstring(f.read(data_size), dtype='float32')
  78. f.close()
  79. return np.reshape(weight, tensor_shape)
  80. def fuse_bn_weights(exe, main_prog, weights_dir):
  81. import paddle.fluid as fluid
  82. logging.info("Try to fuse weights of batch_norm...")
  83. bn_vars = list()
  84. for block in main_prog.blocks:
  85. ops = list(block.ops)
  86. for op in ops:
  87. if op.type == 'affine_channel':
  88. scale_name = op.input('Scale')[0]
  89. bias_name = op.input('Bias')[0]
  90. prefix = scale_name[:-5]
  91. mean_name = prefix + 'mean'
  92. variance_name = prefix + 'variance'
  93. if not osp.exists(osp.join(
  94. weights_dir, mean_name)) or not osp.exists(
  95. osp.join(weights_dir, variance_name)):
  96. logging.info(
  97. "There's no batch_norm weight found to fuse, skip fuse_bn."
  98. )
  99. return
  100. bias = block.var(bias_name)
  101. pretrained_shape = parse_param_file(
  102. osp.join(weights_dir, bias_name))
  103. actual_shape = tuple(bias.shape)
  104. if pretrained_shape != actual_shape:
  105. continue
  106. bn_vars.append(
  107. [scale_name, bias_name, mean_name, variance_name])
  108. eps = 1e-5
  109. for names in bn_vars:
  110. scale_name, bias_name, mean_name, variance_name = names
  111. scale = parse_param_file(
  112. osp.join(weights_dir, scale_name), return_shape=False)
  113. bias = parse_param_file(
  114. osp.join(weights_dir, bias_name), return_shape=False)
  115. mean = parse_param_file(
  116. osp.join(weights_dir, mean_name), return_shape=False)
  117. variance = parse_param_file(
  118. osp.join(weights_dir, variance_name), return_shape=False)
  119. bn_std = np.sqrt(np.add(variance, eps))
  120. new_scale = np.float32(np.divide(scale, bn_std))
  121. new_bias = bias - mean * new_scale
  122. scale_tensor = fluid.global_scope().find_var(scale_name).get_tensor()
  123. bias_tensor = fluid.global_scope().find_var(bias_name).get_tensor()
  124. scale_tensor.set(new_scale, exe.place)
  125. bias_tensor.set(new_bias, exe.place)
  126. if len(bn_vars) == 0:
  127. logging.info(
  128. "There's no batch_norm weight found to fuse, skip fuse_bn.")
  129. else:
  130. logging.info("There's {} batch_norm ops been fused.".format(
  131. len(bn_vars)))
  132. def load_pdparams(exe, main_prog, model_dir):
  133. import paddle.fluid as fluid
  134. from paddle.fluid.proto.framework_pb2 import VarType
  135. from paddle.fluid.framework import Program
  136. vars_to_load = list()
  137. import pickle
  138. with open(osp.join(model_dir, 'model.pdparams'), 'rb') as f:
  139. params_dict = pickle.load(f) if six.PY2 else pickle.load(
  140. f, encoding='latin1')
  141. unused_vars = list()
  142. for var in main_prog.list_vars():
  143. if not isinstance(var, fluid.framework.Parameter):
  144. continue
  145. if var.name not in params_dict:
  146. raise Exception("{} is not in saved paddlex model".format(
  147. var.name))
  148. if var.shape != params_dict[var.name].shape:
  149. unused_vars.append(var.name)
  150. logging.warning(
  151. "[SKIP] Shape of pretrained weight {} doesn't match.(Pretrained: {}, Actual: {})"
  152. .format(var.name, params_dict[var.name].shape, var.shape))
  153. continue
  154. vars_to_load.append(var)
  155. logging.debug("Weight {} will be load".format(var.name))
  156. for var_name in unused_vars:
  157. del params_dict[var_name]
  158. fluid.io.set_program_state(main_prog, params_dict)
  159. if len(vars_to_load) == 0:
  160. logging.warning(
  161. "There is no pretrain weights loaded, maybe you should check you pretrain model!"
  162. )
  163. else:
  164. logging.info("There are {} varaibles in {} are loaded.".format(
  165. len(vars_to_load), model_dir))
  166. def load_pretrain_weights(exe, main_prog, weights_dir, fuse_bn=False):
  167. if not osp.exists(weights_dir):
  168. raise Exception("Path {} not exists.".format(weights_dir))
  169. if osp.exists(osp.join(weights_dir, "model.pdparams")):
  170. return load_pdparams(exe, main_prog, weights_dir)
  171. import paddle.fluid as fluid
  172. vars_to_load = list()
  173. for var in main_prog.list_vars():
  174. if not isinstance(var, fluid.framework.Parameter):
  175. continue
  176. if not osp.exists(osp.join(weights_dir, var.name)):
  177. logging.debug(
  178. "[SKIP] Pretrained weight {}/{} doesn't exist".format(
  179. weights_dir, var.name))
  180. continue
  181. pretrained_shape = parse_param_file(osp.join(weights_dir, var.name))
  182. actual_shape = tuple(var.shape)
  183. if pretrained_shape != actual_shape:
  184. logging.warning(
  185. "[SKIP] Shape of pretrained weight {}/{} doesn't match.(Pretrained: {}, Actual: {})"
  186. .format(weights_dir, var.name, pretrained_shape, actual_shape))
  187. continue
  188. vars_to_load.append(var)
  189. logging.debug("Weight {} will be load".format(var.name))
  190. fluid.io.load_vars(
  191. executor=exe,
  192. dirname=weights_dir,
  193. main_program=main_prog,
  194. vars=vars_to_load)
  195. if len(vars_to_load) == 0:
  196. logging.warning(
  197. "There is no pretrain weights loaded, maybe you should check you pretrain model!"
  198. )
  199. else:
  200. logging.info("There are {} varaibles in {} are loaded.".format(
  201. len(vars_to_load), weights_dir))
  202. if fuse_bn:
  203. fuse_bn_weights(exe, main_prog, weights_dir)
  204. class EarlyStop:
  205. def __init__(self, patience, thresh):
  206. self.patience = patience
  207. self.counter = 0
  208. self.score = None
  209. self.max = 0
  210. self.thresh = thresh
  211. if patience < 1:
  212. raise Exception("Argument patience should be a positive integer.")
  213. def __call__(self, current_score):
  214. if self.score is None:
  215. self.score = current_score
  216. return False
  217. elif current_score > self.max:
  218. self.counter = 0
  219. self.score = current_score
  220. self.max = current_score
  221. return False
  222. else:
  223. if (abs(self.score - current_score) < self.thresh
  224. or current_score < self.score):
  225. self.counter += 1
  226. self.score = current_score
  227. logging.debug(
  228. "EarlyStopping: %i / %i" % (self.counter, self.patience))
  229. if self.counter >= self.patience:
  230. logging.info("EarlyStopping: Stop training")
  231. return True
  232. return False
  233. else:
  234. self.counter = 0
  235. self.score = current_score
  236. return False