test_predictor.py 19 KB

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