You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

validate.py 17 kB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. # Copyright 2019 Huawei Technologies Co., Ltd
  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. # ============================================================================
  15. """Validate the parameters."""
  16. import re
  17. from marshmallow import ValidationError
  18. from mindinsight.lineagemgr.common.exceptions.error_code import LineageErrors, LineageErrorMsg
  19. from mindinsight.lineagemgr.common.exceptions.exceptions import LineageParamTypeError, LineageParamValueError
  20. from mindinsight.lineagemgr.common.log import logger as log
  21. from mindinsight.lineagemgr.common.validator.validate_path import safe_normalize_path
  22. from mindinsight.lineagemgr.querier.query_model import FIELD_MAPPING
  23. from mindinsight.utils.exceptions import MindInsightException, ParamValueError
  24. # Named string regular expression
  25. _name_re = r"^\w+[0-9a-zA-Z\_\.]*$"
  26. TRAIN_RUN_CONTEXT_ERROR_MAPPING = {
  27. 'optimizer': LineageErrors.PARAM_OPTIMIZER_ERROR,
  28. 'loss_fn': LineageErrors.PARAM_LOSS_FN_ERROR,
  29. 'net_outputs': LineageErrors.PARAM_NET_OUTPUTS_ERROR,
  30. 'train_network': LineageErrors.PARAM_TRAIN_NETWORK_ERROR,
  31. 'train_dataset': LineageErrors.PARAM_DATASET_ERROR,
  32. 'epoch_num': LineageErrors.PARAM_EPOCH_NUM_ERROR,
  33. 'batch_num': LineageErrors.PARAM_BATCH_NUM_ERROR,
  34. 'parallel_mode': LineageErrors.PARAM_TRAIN_PARALLEL_ERROR,
  35. 'device_number': LineageErrors.PARAM_DEVICE_NUMBER_ERROR,
  36. 'list_callback': LineageErrors.PARAM_CALLBACK_LIST_ERROR,
  37. 'train_dataset_size': LineageErrors.PARAM_DATASET_SIZE_ERROR,
  38. }
  39. SEARCH_MODEL_ERROR_MAPPING = {
  40. 'summary_dir': LineageErrors.LINEAGE_PARAM_SUMMARY_DIR_ERROR,
  41. 'loss_function': LineageErrors.LINEAGE_PARAM_LOSS_FUNCTION_ERROR,
  42. 'train_dataset_path': LineageErrors.LINEAGE_PARAM_TRAIN_DATASET_PATH_ERROR,
  43. 'train_dataset_count': LineageErrors.LINEAGE_PARAM_TRAIN_DATASET_COUNT_ERROR,
  44. 'test_dataset_path': LineageErrors.LINEAGE_PARAM_TEST_DATASET_PATH_ERROR,
  45. 'test_dataset_count': LineageErrors.LINEAGE_PARAM_TEST_DATASET_COUNT_ERROR,
  46. 'network': LineageErrors.LINEAGE_PARAM_NETWORK_ERROR,
  47. 'optimizer': LineageErrors.LINEAGE_PARAM_OPTIMIZER_ERROR,
  48. 'learning_rate': LineageErrors.LINEAGE_PARAM_LEARNING_RATE_ERROR,
  49. 'epoch': LineageErrors.LINEAGE_PARAM_EPOCH_ERROR,
  50. 'batch_size': LineageErrors.LINEAGE_PARAM_BATCH_SIZE_ERROR,
  51. 'device_num': LineageErrors.LINEAGE_PARAM_DEVICE_NUM_ERROR,
  52. 'limit': LineageErrors.PARAM_VALUE_ERROR,
  53. 'offset': LineageErrors.PARAM_VALUE_ERROR,
  54. 'loss': LineageErrors.LINEAGE_PARAM_LOSS_ERROR,
  55. 'model_size': LineageErrors.LINEAGE_PARAM_MODEL_SIZE_ERROR,
  56. 'sorted_name': LineageErrors.LINEAGE_PARAM_SORTED_NAME_ERROR,
  57. 'sorted_type': LineageErrors.LINEAGE_PARAM_SORTED_TYPE_ERROR,
  58. 'dataset_mark': LineageErrors.LINEAGE_PARAM_DATASET_MARK_ERROR,
  59. 'lineage_type': LineageErrors.LINEAGE_PARAM_LINEAGE_TYPE_ERROR
  60. }
  61. TRAIN_RUN_CONTEXT_ERROR_MSG_MAPPING = {
  62. 'optimizer': LineageErrorMsg.PARAM_OPTIMIZER_ERROR.value,
  63. 'loss_fn': LineageErrorMsg.PARAM_LOSS_FN_ERROR.value,
  64. 'net_outputs': LineageErrorMsg.PARAM_NET_OUTPUTS_ERROR.value,
  65. 'train_network': LineageErrorMsg.PARAM_TRAIN_NETWORK_ERROR.value,
  66. 'epoch_num': LineageErrorMsg.PARAM_EPOCH_NUM_ERROR.value,
  67. 'batch_num': LineageErrorMsg.PARAM_BATCH_NUM_ERROR.value,
  68. 'parallel_mode': LineageErrorMsg.PARAM_TRAIN_PARALLEL_ERROR.value,
  69. 'device_number': LineageErrorMsg.PARAM_DEVICE_NUMBER_ERROR.value,
  70. 'list_callback': LineageErrorMsg.PARAM_CALLBACK_LIST_ERROR.value
  71. }
  72. SEARCH_MODEL_ERROR_MSG_MAPPING = {
  73. 'summary_dir': LineageErrorMsg.LINEAGE_PARAM_SUMMARY_DIR_ERROR.value,
  74. 'loss_function': LineageErrorMsg.LINEAGE_LOSS_FUNCTION_ERROR.value,
  75. 'train_dataset_path': LineageErrorMsg.LINEAGE_TRAIN_DATASET_PATH_ERROR.value,
  76. 'train_dataset_count': LineageErrorMsg.LINEAGE_TRAIN_DATASET_COUNT_ERROR.value,
  77. 'test_dataset_path': LineageErrorMsg.LINEAGE_TEST_DATASET_PATH_ERROR.value,
  78. 'test_dataset_count': LineageErrorMsg.LINEAGE_TEST_DATASET_COUNT_ERROR.value,
  79. 'network': LineageErrorMsg.LINEAGE_NETWORK_ERROR.value,
  80. 'optimizer': LineageErrorMsg.LINEAGE_OPTIMIZER_ERROR.value,
  81. 'learning_rate': LineageErrorMsg.LINEAGE_LEARNING_RATE_ERROR.value,
  82. 'epoch': LineageErrorMsg.PARAM_EPOCH_NUM_ERROR.value,
  83. 'batch_size': LineageErrorMsg.PARAM_BATCH_SIZE_ERROR.value,
  84. 'device_num': LineageErrorMsg.PARAM_DEVICE_NUM_ERROR.value,
  85. 'limit': LineageErrorMsg.PARAM_LIMIT_ERROR.value,
  86. 'offset': LineageErrorMsg.PARAM_OFFSET_ERROR.value,
  87. 'loss': LineageErrorMsg.LINEAGE_LOSS_ERROR.value,
  88. 'model_size': LineageErrorMsg.LINEAGE_MODEL_SIZE_ERROR.value,
  89. 'sorted_name': LineageErrorMsg.LINEAGE_PARAM_SORTED_NAME_ERROR.value,
  90. 'sorted_type': LineageErrorMsg.LINEAGE_PARAM_SORTED_TYPE_ERROR.value,
  91. 'dataset_mark': LineageErrorMsg.LINEAGE_PARAM_DATASET_MARK_ERROR.value,
  92. 'lineage_type': LineageErrorMsg.LINEAGE_PARAM_LINEAGE_TYPE_ERROR.value
  93. }
  94. EVAL_RUN_CONTEXT_ERROR_MAPPING = {
  95. 'valid_dataset': LineageErrors.PARAM_DATASET_ERROR,
  96. 'metrics': LineageErrors.PARAM_EVAL_METRICS_ERROR
  97. }
  98. EVAL_RUN_CONTEXT_ERROR_MSG_MAPPING = {
  99. 'metrics': LineageErrorMsg.PARAM_EVAL_METRICS_ERROR.value,
  100. }
  101. def validate_int_params(int_param, param_name):
  102. """
  103. Verify the parameter which type is integer valid or not.
  104. Args:
  105. int_param (int): parameter that is integer,
  106. including epoch, dataset_batch_size, step_num
  107. param_name (str): the name of parameter,
  108. including epoch, dataset_batch_size, step_num
  109. Raises:
  110. MindInsightException: If the parameters are invalid.
  111. """
  112. if not isinstance(int_param, int) or int_param <= 0 or int_param > pow(2, 63) - 1:
  113. if param_name == 'step_num':
  114. log.error('Invalid step_num. The step number should be a positive integer.')
  115. raise MindInsightException(error=LineageErrors.PARAM_STEP_NUM_ERROR,
  116. message=LineageErrorMsg.PARAM_STEP_NUM_ERROR.value)
  117. if param_name == 'dataset_batch_size':
  118. log.error('Invalid dataset_batch_size. '
  119. 'The batch size should be a positive integer.')
  120. raise MindInsightException(error=LineageErrors.PARAM_BATCH_SIZE_ERROR,
  121. message=LineageErrorMsg.PARAM_BATCH_SIZE_ERROR.value)
  122. def validate_file_path(file_path, allow_empty=False):
  123. """
  124. Verify that the file_path is valid.
  125. Args:
  126. file_path (str): Input file path.
  127. allow_empty (bool): Whether file_path can be empty.
  128. Raises:
  129. MindInsightException: If the parameters are invalid.
  130. """
  131. try:
  132. if allow_empty and not file_path:
  133. return file_path
  134. return safe_normalize_path(file_path, raise_key='dataset_path', safe_prefixes=None)
  135. except ValidationError as error:
  136. log.error(str(error))
  137. raise MindInsightException(error=LineageErrors.PARAM_FILE_PATH_ERROR,
  138. message=str(error))
  139. def validate_eval_run_context(schema, data):
  140. """
  141. Validate mindspore evaluation job run_context data according to schema.
  142. Args:
  143. schema (Schema): data schema.
  144. data (dict): data to check schema.
  145. Raises:
  146. MindInsightException: If the parameters are invalid.
  147. """
  148. errors = schema().validate(data)
  149. for error_key, error_msg in errors.items():
  150. if error_key in EVAL_RUN_CONTEXT_ERROR_MAPPING.keys():
  151. error_code = EVAL_RUN_CONTEXT_ERROR_MAPPING.get(error_key)
  152. if EVAL_RUN_CONTEXT_ERROR_MSG_MAPPING.get(error_key):
  153. error_msg = EVAL_RUN_CONTEXT_ERROR_MSG_MAPPING.get(error_key)
  154. log.error(error_msg)
  155. raise MindInsightException(error=error_code, message=error_msg)
  156. def validate_search_model_condition(schema, data):
  157. """
  158. Validate search model condition.
  159. Args:
  160. schema (Schema): Data schema.
  161. data (dict): Data to check schema.
  162. Raises:
  163. MindInsightException: If the parameters are invalid.
  164. """
  165. error = schema().validate(data)
  166. for (error_key, error_msgs) in error.items():
  167. if error_key in SEARCH_MODEL_ERROR_MAPPING.keys():
  168. error_code = SEARCH_MODEL_ERROR_MAPPING.get(error_key)
  169. error_msg = SEARCH_MODEL_ERROR_MSG_MAPPING.get(error_key)
  170. for err_msg in error_msgs:
  171. if 'operation' in err_msg.lower():
  172. error_msg = f'The parameter {error_key} is invalid. {err_msg}'
  173. break
  174. log.error(error_msg)
  175. raise MindInsightException(error=error_code, message=error_msg)
  176. def validate_raise_exception(raise_exception):
  177. """
  178. Validate raise_exception.
  179. Args:
  180. raise_exception (bool): decide raise exception or not,
  181. if True, raise exception; else, catch exception and continue.
  182. Raises:
  183. MindInsightException: If the parameters are invalid.
  184. """
  185. if not isinstance(raise_exception, bool):
  186. log.error("Invalid raise_exception. It should be True or False.")
  187. raise MindInsightException(
  188. error=LineageErrors.PARAM_RAISE_EXCEPTION_ERROR,
  189. message=LineageErrorMsg.PARAM_RAISE_EXCEPTION_ERROR.value
  190. )
  191. def validate_condition(search_condition):
  192. """
  193. Verify the param in search_condition is valid or not.
  194. Args:
  195. search_condition (dict): The search condition.
  196. Raises:
  197. LineageParamTypeError: If the type of the param in search_condition is invalid.
  198. LineageParamValueError: If the value of the param in search_condition is invalid.
  199. """
  200. if not isinstance(search_condition, dict):
  201. log.error("Invalid search_condition type, it should be dict.")
  202. raise LineageParamTypeError("Invalid search_condition type, "
  203. "it should be dict.")
  204. if "limit" in search_condition:
  205. if isinstance(search_condition.get("limit"), bool) \
  206. or not isinstance(search_condition.get("limit"), int):
  207. log.error("The limit must be int.")
  208. raise LineageParamTypeError("The limit must be int.")
  209. if "offset" in search_condition:
  210. if isinstance(search_condition.get("offset"), bool) \
  211. or not isinstance(search_condition.get("offset"), int):
  212. log.error("The offset must be int.")
  213. raise LineageParamTypeError("The offset must be int.")
  214. if "sorted_name" in search_condition:
  215. sorted_name = search_condition.get("sorted_name")
  216. err_msg = "The sorted_name must be in {} or start with " \
  217. "`metric/` or `user_defined/`.".format(list(FIELD_MAPPING.keys()))
  218. if not isinstance(sorted_name, str):
  219. log.error(err_msg)
  220. raise LineageParamValueError(err_msg)
  221. if not (sorted_name in FIELD_MAPPING
  222. or (sorted_name.startswith('metric/') and len(sorted_name) > len('metric/'))
  223. or (sorted_name.startswith('user_defined/') and len(sorted_name) > len('user_defined/'))
  224. or sorted_name in ['tag']):
  225. log.error(err_msg)
  226. raise LineageParamValueError(err_msg)
  227. sorted_type_param = ['ascending', 'descending', None]
  228. if "sorted_type" in search_condition:
  229. if "sorted_name" not in search_condition:
  230. log.error("The sorted_name have to exist when sorted_type exists.")
  231. raise LineageParamValueError("The sorted_name have to exist when sorted_type exists.")
  232. if search_condition.get("sorted_type") not in sorted_type_param:
  233. err_msg = "The sorted_type must be ascending or descending."
  234. log.error(err_msg)
  235. raise LineageParamValueError(err_msg)
  236. def validate_user_defined_info(user_defined_info):
  237. """
  238. Validate user defined info, delete the item if its key is in lineage.
  239. Args:
  240. user_defined_info (dict): The user defined info.
  241. Raises:
  242. LineageParamTypeError: If the type of parameters is invalid.
  243. LineageParamValueError: If user defined keys have been defined in lineage.
  244. """
  245. if not isinstance(user_defined_info, dict):
  246. log.error("Invalid user defined info. It should be a dict.")
  247. raise LineageParamTypeError("Invalid user defined info. It should be dict.")
  248. for key, value in user_defined_info.items():
  249. if not isinstance(key, str):
  250. error_msg = "Dict key type {} is not supported in user defined info." \
  251. "Only str is permitted now.".format(type(key))
  252. log.error(error_msg)
  253. raise LineageParamTypeError(error_msg)
  254. if not isinstance(value, (int, str, float)):
  255. error_msg = "Dict value type {} is not supported in user defined info." \
  256. "Only str, int and float are permitted now.".format(type(value))
  257. log.error(error_msg)
  258. raise LineageParamTypeError(error_msg)
  259. field_map = set(FIELD_MAPPING.keys())
  260. user_defined_keys = set(user_defined_info.keys())
  261. insertion = list(field_map & user_defined_keys)
  262. if insertion:
  263. for key in insertion:
  264. user_defined_info.pop(key)
  265. raise LineageParamValueError("There are some keys have defined in lineage. "
  266. "Duplicated key(s): %s. " % insertion)
  267. def validate_train_id(relative_path):
  268. """
  269. Check if train_id is valid.
  270. Args:
  271. relative_path (str): Train ID of a summary directory, e.g. './log1'.
  272. Returns:
  273. bool, if train id is valid, return True.
  274. """
  275. if not relative_path.startswith('./'):
  276. log.warning("The relative_path does not start with './'.")
  277. raise ParamValueError(
  278. "Summary dir should be relative path starting with './'."
  279. )
  280. if len(relative_path.split("/")) > 2:
  281. log.warning("The relative_path contains multiple '/'.")
  282. raise ParamValueError(
  283. "Summary dir should be relative path starting with './'."
  284. )
  285. def validate_range(name, value, min_value, max_value):
  286. """
  287. Check if value is in [min_value, max_value].
  288. Args:
  289. name (str): Value name.
  290. value (Union[int, float]): Value to be check.
  291. min_value (Union[int, float]): Min value.
  292. max_value (Union[int, float]): Max value.
  293. Raises:
  294. LineageParamValueError, if value type is invalid or value is out of [min_value, max_value].
  295. """
  296. if not isinstance(value, (int, float)):
  297. raise LineageParamValueError("Value should be int or float.")
  298. if value < min_value or value > max_value:
  299. raise LineageParamValueError("The %s should in [%d, %d]." % (name, min_value, max_value))
  300. def validate_added_info(added_info: dict):
  301. """
  302. Check if added_info is valid.
  303. Args:
  304. added_info (dict): The added info.
  305. Raises:
  306. bool, if added_info is valid, return True.
  307. """
  308. added_info_keys = ["tag", "remark"]
  309. if not set(added_info.keys()).issubset(added_info_keys):
  310. err_msg = "Keys of added_info must be in {}.".format(added_info_keys)
  311. raise LineageParamValueError(err_msg)
  312. for key, value in added_info.items():
  313. if key == "tag":
  314. if not isinstance(value, int):
  315. raise LineageParamValueError("'tag' must be int.")
  316. # tag should be in [0, 10].
  317. validate_range("tag", value, min_value=0, max_value=10)
  318. elif key == "remark":
  319. if not isinstance(value, str):
  320. raise LineageParamValueError("'remark' must be str.")
  321. # length of remark should be in [0, 128].
  322. validate_range("length of remark", len(value), min_value=0, max_value=128)
  323. def validate_str_by_regular(target, reg=None, flag=re.ASCII):
  324. """
  325. Validate string by given regular.
  326. Args:
  327. target: target string.
  328. reg: pattern.
  329. flag: pattern mode.
  330. Raises:
  331. LineageParamValueError, if string not match given pattern.
  332. Returns:
  333. bool, if target matches pattern, return True.
  334. """
  335. if reg is None:
  336. reg = _name_re
  337. if re.match(reg, target, flag) is None:
  338. raise LineageParamValueError("'{}' is illegal, it should be match "
  339. "regular'{}' by flags'{}'".format(target, reg, flag))
  340. return True