test_predictor.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. # Copyright (c) 2022 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. import os.path as osp
  15. import sys
  16. import tempfile
  17. import unittest.mock as mock
  18. import paddle
  19. import numpy as np
  20. import paddlers as pdrs
  21. from paddlers.transforms import decode_image
  22. from testing_utils import CommonTest, run_script
  23. __all__ = [
  24. 'TestCDPredictor', 'TestClasPredictor', 'TestDetPredictor',
  25. 'TestResPredictor', 'TestSegPredictor'
  26. ]
  27. class TestPredictor(CommonTest):
  28. MODULE = pdrs.tasks
  29. TRAINER_NAME_TO_EXPORT_OPTS = {}
  30. WHITE_LIST = []
  31. @staticmethod
  32. def add_tests(cls):
  33. """
  34. Automatically patch testing functions to cls.
  35. """
  36. def _test_predictor(trainer_name):
  37. def _test_predictor_impl(self):
  38. trainer_class = getattr(self.MODULE, trainer_name)
  39. # Construct trainer with default parameters
  40. # TODO: Load pretrained weights to avoid numeric problems
  41. trainer = trainer_class()
  42. with tempfile.TemporaryDirectory() as td:
  43. dynamic_model_dir = osp.join(td, "dynamic")
  44. static_model_dir = osp.join(td, "static")
  45. # HACK: BaseModel.save_model() requires BaseModel().optimizer to be set
  46. optimizer = mock.Mock()
  47. optimizer.state_dict.return_value = {'foo': 'bar'}
  48. trainer.optimizer = optimizer
  49. trainer.save_model(dynamic_model_dir)
  50. export_cmd = f"{sys.executable} export_model.py --model_dir {dynamic_model_dir} --save_dir {static_model_dir} "
  51. if trainer_name in self.TRAINER_NAME_TO_EXPORT_OPTS:
  52. export_cmd += self.TRAINER_NAME_TO_EXPORT_OPTS[
  53. trainer_name]
  54. elif '_default' in self.TRAINER_NAME_TO_EXPORT_OPTS:
  55. export_cmd += self.TRAINER_NAME_TO_EXPORT_OPTS[
  56. '_default']
  57. run_script(export_cmd, wd="../deploy/export")
  58. # Construct predictor
  59. # TODO: Test trt and mkl
  60. predictor = pdrs.deploy.Predictor(
  61. static_model_dir,
  62. use_gpu=paddle.device.get_device().startswith('gpu'))
  63. trainer.net.eval()
  64. with paddle.no_grad():
  65. self.check_predictor(predictor, trainer)
  66. return _test_predictor_impl
  67. for trainer_name in cls.MODULE.__all__:
  68. if trainer_name in cls.WHITE_LIST:
  69. continue
  70. setattr(cls, 'test_' + trainer_name, _test_predictor(trainer_name))
  71. return cls
  72. def check_predictor(self, predictor, trainer):
  73. raise NotImplementedError
  74. def check_dict_equal(self,
  75. dict_,
  76. expected_dict,
  77. ignore_keys=('label_map', 'mask', 'class_ids_map',
  78. 'label_names_map', 'category',
  79. 'category_id')):
  80. # By default do not compare label_maps, masks, or categories,
  81. # because numeric errors could result in large difference in labels.
  82. if isinstance(dict_, list):
  83. self.assertIsInstance(expected_dict, list)
  84. self.assertEqual(len(dict_), len(expected_dict))
  85. for d1, d2 in zip(dict_, expected_dict):
  86. self.check_dict_equal(d1, d2, ignore_keys=ignore_keys)
  87. else:
  88. assert isinstance(dict_, dict)
  89. assert isinstance(expected_dict, dict)
  90. self.assertEqual(dict_.keys(), expected_dict.keys())
  91. ignore_keys = set() if ignore_keys is None else set(ignore_keys)
  92. for key in dict_.keys():
  93. if key in ignore_keys:
  94. continue
  95. diff = np.abs(
  96. np.asarray(dict_[key]) - np.asarray(expected_dict[
  97. key])).ravel()
  98. cnt = (diff > (1.e-4 * diff + 1.e-4)).sum()
  99. self.assertLess(cnt / diff.size, 0.03)
  100. @TestPredictor.add_tests
  101. class TestCDPredictor(TestPredictor):
  102. MODULE = pdrs.tasks.change_detector
  103. TRAINER_NAME_TO_EXPORT_OPTS = {
  104. '_default': "--fixed_input_shape [-1,3,256,256]"
  105. }
  106. # HACK: Skip DSIFN.
  107. # These models are heavily affected by numeric errors.
  108. WHITE_LIST = ['DSIFN']
  109. def check_predictor(self, predictor, trainer):
  110. t1_path = "data/ssmt/optical_t1.bmp"
  111. t2_path = "data/ssmt/optical_t2.bmp"
  112. single_input = (t1_path, t2_path)
  113. num_inputs = 2
  114. transforms = pdrs.transforms.Compose([pdrs.transforms.Normalize()])
  115. # Expected failure
  116. with self.assertRaises(ValueError):
  117. predictor.predict(t1_path, transforms=transforms)
  118. # Single input (file paths)
  119. input_ = single_input
  120. out_single_file_p = predictor.predict(input_, transforms=transforms)
  121. out_single_file_t = trainer.predict(input_, transforms=transforms)
  122. self.check_dict_equal(out_single_file_p, out_single_file_t)
  123. out_single_file_list_p = predictor.predict(
  124. [input_], transforms=transforms)
  125. self.assertEqual(len(out_single_file_list_p), 1)
  126. self.check_dict_equal(out_single_file_list_p[0], out_single_file_p)
  127. out_single_file_list_t = trainer.predict(
  128. [input_], transforms=transforms)
  129. self.check_dict_equal(out_single_file_list_p[0],
  130. out_single_file_list_t[0])
  131. # Single input (ndarrays)
  132. input_ = (decode_image(
  133. t1_path, read_raw=True), decode_image(
  134. t2_path, read_raw=True)) # Reuse the name `input_`
  135. out_single_array_p = predictor.predict(input_, transforms=transforms)
  136. self.check_dict_equal(out_single_array_p, out_single_file_p)
  137. out_single_array_t = trainer.predict(input_, transforms=transforms)
  138. self.check_dict_equal(out_single_array_p, out_single_array_t)
  139. out_single_array_list_p = predictor.predict(
  140. [input_], transforms=transforms)
  141. self.assertEqual(len(out_single_array_list_p), 1)
  142. self.check_dict_equal(out_single_array_list_p[0], out_single_array_p)
  143. out_single_array_list_t = trainer.predict(
  144. [input_], transforms=transforms)
  145. self.check_dict_equal(out_single_array_list_p[0],
  146. out_single_array_list_t[0])
  147. # Multiple inputs (file paths)
  148. input_ = [single_input] * num_inputs # Reuse the name `input_`
  149. out_multi_file_p = predictor.predict(input_, transforms=transforms)
  150. self.assertEqual(len(out_multi_file_p), num_inputs)
  151. out_multi_file_t = trainer.predict(input_, transforms=transforms)
  152. self.assertEqual(len(out_multi_file_t), num_inputs)
  153. # Multiple inputs (ndarrays)
  154. input_ = [(decode_image(
  155. t1_path, read_raw=True), decode_image(
  156. t2_path,
  157. read_raw=True))] * num_inputs # Reuse the name `input_`
  158. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  159. self.assertEqual(len(out_multi_array_p), num_inputs)
  160. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  161. self.assertEqual(len(out_multi_array_t), num_inputs)
  162. @TestPredictor.add_tests
  163. class TestClasPredictor(TestPredictor):
  164. MODULE = pdrs.tasks.classifier
  165. TRAINER_NAME_TO_EXPORT_OPTS = {
  166. '_default': "--fixed_input_shape [-1,3,256,256]"
  167. }
  168. def check_predictor(self, predictor, trainer):
  169. single_input = "data/ssst/optical.bmp"
  170. num_inputs = 2
  171. transforms = pdrs.transforms.Compose([pdrs.transforms.Normalize()])
  172. labels = list(range(2))
  173. trainer.labels = labels
  174. predictor._model.labels = labels
  175. # Single input (file path)
  176. input_ = single_input
  177. out_single_file_p = predictor.predict(input_, transforms=transforms)
  178. out_single_file_t = trainer.predict(input_, transforms=transforms)
  179. self.check_dict_equal(out_single_file_p, out_single_file_t)
  180. out_single_file_list_p = predictor.predict(
  181. [input_], transforms=transforms)
  182. self.assertEqual(len(out_single_file_list_p), 1)
  183. self.check_dict_equal(out_single_file_list_p[0], out_single_file_p)
  184. out_single_file_list_t = trainer.predict(
  185. [input_], transforms=transforms)
  186. self.check_dict_equal(out_single_file_list_p[0],
  187. out_single_file_list_t[0])
  188. # Single input (ndarray)
  189. input_ = decode_image(
  190. single_input, read_raw=True) # Reuse the name `input_`
  191. out_single_array_p = predictor.predict(input_, transforms=transforms)
  192. self.check_dict_equal(out_single_array_p, out_single_file_p)
  193. out_single_array_t = trainer.predict(input_, transforms=transforms)
  194. self.check_dict_equal(out_single_array_p, out_single_array_t)
  195. out_single_array_list_p = predictor.predict(
  196. [input_], transforms=transforms)
  197. self.assertEqual(len(out_single_array_list_p), 1)
  198. self.check_dict_equal(out_single_array_list_p[0], out_single_array_p)
  199. out_single_array_list_t = trainer.predict(
  200. [input_], transforms=transforms)
  201. self.check_dict_equal(out_single_array_list_p[0],
  202. out_single_array_list_t[0])
  203. # Multiple inputs (file paths)
  204. input_ = [single_input] * num_inputs # Reuse the name `input_`
  205. out_multi_file_p = predictor.predict(input_, transforms=transforms)
  206. self.assertEqual(len(out_multi_file_p), num_inputs)
  207. out_multi_file_t = trainer.predict(input_, transforms=transforms)
  208. # Check value consistence
  209. self.check_dict_equal(out_multi_file_p, out_multi_file_t)
  210. # Multiple inputs (ndarrays)
  211. input_ = [decode_image(
  212. single_input,
  213. read_raw=True)] * num_inputs # Reuse the name `input_`
  214. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  215. self.assertEqual(len(out_multi_array_p), num_inputs)
  216. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  217. self.check_dict_equal(out_multi_array_p, out_multi_array_t)
  218. @TestPredictor.add_tests
  219. class TestDetPredictor(TestPredictor):
  220. MODULE = pdrs.tasks.object_detector
  221. TRAINER_NAME_TO_EXPORT_OPTS = {
  222. '_default': "--fixed_input_shape [-1,3,256,256]"
  223. }
  224. def check_predictor(self, predictor, trainer):
  225. # For detection tasks, do NOT ensure the consistence of bboxes.
  226. # This is because the coordinates of bboxes were observed to be very sensitive to numeric errors,
  227. # given that the network is (partially?) randomly initialized.
  228. single_input = "data/ssst/optical.bmp"
  229. num_inputs = 2
  230. transforms = pdrs.transforms.Compose([pdrs.transforms.Normalize()])
  231. labels = list(range(80))
  232. trainer.labels = labels
  233. predictor._model.labels = labels
  234. # Single input (file path)
  235. input_ = single_input
  236. predictor.predict(input_, transforms=transforms)
  237. trainer.predict(input_, transforms=transforms)
  238. out_single_file_list_p = predictor.predict(
  239. [input_], transforms=transforms)
  240. self.assertEqual(len(out_single_file_list_p), 1)
  241. out_single_file_list_t = trainer.predict(
  242. [input_], transforms=transforms)
  243. self.assertEqual(len(out_single_file_list_t), 1)
  244. # Single input (ndarray)
  245. input_ = decode_image(
  246. single_input, read_raw=True) # Reuse the name `input_`
  247. predictor.predict(input_, transforms=transforms)
  248. trainer.predict(input_, transforms=transforms)
  249. out_single_array_list_p = predictor.predict(
  250. [input_], transforms=transforms)
  251. self.assertEqual(len(out_single_array_list_p), 1)
  252. out_single_array_list_t = trainer.predict(
  253. [input_], transforms=transforms)
  254. self.assertEqual(len(out_single_array_list_t), 1)
  255. # Multiple inputs (file paths)
  256. input_ = [single_input] * num_inputs # Reuse the name `input_`
  257. out_multi_file_p = predictor.predict(input_, transforms=transforms)
  258. self.assertEqual(len(out_multi_file_p), num_inputs)
  259. out_multi_file_t = trainer.predict(input_, transforms=transforms)
  260. self.assertEqual(len(out_multi_file_t), num_inputs)
  261. # Multiple inputs (ndarrays)
  262. input_ = [decode_image(
  263. single_input,
  264. read_raw=True)] * num_inputs # Reuse the name `input_`
  265. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  266. self.assertEqual(len(out_multi_array_p), num_inputs)
  267. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  268. self.assertEqual(len(out_multi_array_t), num_inputs)
  269. @TestPredictor.add_tests
  270. class TestResPredictor(TestPredictor):
  271. MODULE = pdrs.tasks.restorer
  272. TRAINER_NAME_TO_EXPORT_OPTS = {
  273. '_default': "--fixed_input_shape [-1,3,256,256]"
  274. }
  275. def __init__(self, methodName='runTest'):
  276. super(TestResPredictor, self).__init__(methodName=methodName)
  277. # Do not test with CPUs as it will take long long time.
  278. self.places.pop(self.places.index('cpu'))
  279. def check_predictor(self, predictor, trainer):
  280. # For restoration tasks, do NOT ensure the consistence of numeric values,
  281. # because the output is of uint8 type.
  282. single_input = "data/ssst/optical.bmp"
  283. num_inputs = 2
  284. transforms = pdrs.transforms.Compose([pdrs.transforms.Normalize()])
  285. # Single input (file path)
  286. input_ = single_input
  287. predictor.predict(input_, transforms=transforms)
  288. trainer.predict(input_, transforms=transforms)
  289. out_single_file_list_p = predictor.predict(
  290. [input_], transforms=transforms)
  291. self.assertEqual(len(out_single_file_list_p), 1)
  292. out_single_file_list_t = trainer.predict(
  293. [input_], transforms=transforms)
  294. self.assertEqual(len(out_single_file_list_t), 1)
  295. # Single input (ndarray)
  296. input_ = decode_image(
  297. single_input, read_raw=True) # Reuse the name `input_`
  298. predictor.predict(input_, transforms=transforms)
  299. trainer.predict(input_, transforms=transforms)
  300. out_single_array_list_p = predictor.predict(
  301. [input_], transforms=transforms)
  302. self.assertEqual(len(out_single_array_list_p), 1)
  303. out_single_array_list_t = trainer.predict(
  304. [input_], transforms=transforms)
  305. self.assertEqual(len(out_single_array_list_t), 1)
  306. # Multiple inputs (file paths)
  307. input_ = [single_input] * num_inputs # Reuse the name `input_`
  308. out_multi_file_p = predictor.predict(input_, transforms=transforms)
  309. self.assertEqual(len(out_multi_file_p), num_inputs)
  310. out_multi_file_t = trainer.predict(input_, transforms=transforms)
  311. self.assertEqual(len(out_multi_file_t), num_inputs)
  312. # Multiple inputs (ndarrays)
  313. input_ = [decode_image(
  314. single_input,
  315. read_raw=True)] * num_inputs # Reuse the name `input_`
  316. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  317. self.assertEqual(len(out_multi_array_p), num_inputs)
  318. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  319. self.assertEqual(len(out_multi_array_t), num_inputs)
  320. @TestPredictor.add_tests
  321. class TestSegPredictor(TestPredictor):
  322. MODULE = pdrs.tasks.segmenter
  323. TRAINER_NAME_TO_EXPORT_OPTS = {
  324. '_default': "--fixed_input_shape [-1,3,256,256]"
  325. }
  326. def check_predictor(self, predictor, trainer):
  327. single_input = "data/ssst/optical.bmp"
  328. num_inputs = 2
  329. transforms = pdrs.transforms.Compose([pdrs.transforms.Normalize()])
  330. # Single input (file path)
  331. input_ = single_input
  332. out_single_file_p = predictor.predict(input_, transforms=transforms)
  333. out_single_file_t = trainer.predict(input_, transforms=transforms)
  334. self.check_dict_equal(out_single_file_p, out_single_file_t)
  335. out_single_file_list_p = predictor.predict(
  336. [input_], transforms=transforms)
  337. self.assertEqual(len(out_single_file_list_p), 1)
  338. self.check_dict_equal(out_single_file_list_p[0], out_single_file_p)
  339. out_single_file_list_t = trainer.predict(
  340. [input_], transforms=transforms)
  341. self.check_dict_equal(out_single_file_list_p[0],
  342. out_single_file_list_t[0])
  343. # Single input (ndarray)
  344. input_ = decode_image(
  345. single_input, read_raw=True) # Reuse the name `input_`
  346. out_single_array_p = predictor.predict(input_, transforms=transforms)
  347. self.check_dict_equal(out_single_array_p, out_single_file_p)
  348. out_single_array_t = trainer.predict(input_, transforms=transforms)
  349. self.check_dict_equal(out_single_array_p, out_single_array_t)
  350. out_single_array_list_p = predictor.predict(
  351. [input_], transforms=transforms)
  352. self.assertEqual(len(out_single_array_list_p), 1)
  353. self.check_dict_equal(out_single_array_list_p[0], out_single_array_p)
  354. out_single_array_list_t = trainer.predict(
  355. [input_], transforms=transforms)
  356. self.check_dict_equal(out_single_array_list_p[0],
  357. out_single_array_list_t[0])
  358. # Multiple inputs (file paths)
  359. input_ = [single_input] * num_inputs # Reuse the name `input_`
  360. out_multi_file_p = predictor.predict(input_, transforms=transforms)
  361. self.assertEqual(len(out_multi_file_p), num_inputs)
  362. out_multi_file_t = trainer.predict(input_, transforms=transforms)
  363. self.assertEqual(len(out_multi_file_t), num_inputs)
  364. # Multiple inputs (ndarrays)
  365. input_ = [decode_image(
  366. single_input,
  367. read_raw=True)] * num_inputs # Reuse the name `input_`
  368. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  369. self.assertEqual(len(out_multi_array_p), num_inputs)
  370. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  371. self.assertEqual(len(out_multi_array_t), num_inputs)