test_predictor.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  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 paddlers as pdrs
  19. from paddlers.transforms import decode_image
  20. from testing_utils import CommonTest, run_script
  21. __all__ = [
  22. 'TestCDPredictor', 'TestClasPredictor', 'TestDetPredictor',
  23. 'TestSegPredictor'
  24. ]
  25. class TestPredictor(CommonTest):
  26. MODULE = pdrs.tasks
  27. TRAINER_NAME_TO_EXPORT_OPTS = {}
  28. WHITE_LIST = []
  29. @staticmethod
  30. def add_tests(cls):
  31. """
  32. Automatically patch testing functions to cls.
  33. """
  34. def _test_predictor(trainer_name):
  35. def _test_predictor_impl(self):
  36. trainer_class = getattr(self.MODULE, trainer_name)
  37. # Construct trainer with default parameters
  38. # TODO: Load pretrained weights to avoid numeric problems
  39. trainer = trainer_class()
  40. with tempfile.TemporaryDirectory() as td:
  41. dynamic_model_dir = osp.join(td, "dynamic")
  42. static_model_dir = osp.join(td, "static")
  43. # HACK: BaseModel.save_model() requires BaseModel().optimizer to be set
  44. optimizer = mock.Mock()
  45. optimizer.state_dict.return_value = {'foo': 'bar'}
  46. trainer.optimizer = optimizer
  47. trainer.save_model(dynamic_model_dir)
  48. export_cmd = f"python export_model.py --model_dir {dynamic_model_dir} --save_dir {static_model_dir} "
  49. if trainer_name in self.TRAINER_NAME_TO_EXPORT_OPTS:
  50. export_cmd += self.TRAINER_NAME_TO_EXPORT_OPTS[
  51. trainer_name]
  52. elif '_default' in self.TRAINER_NAME_TO_EXPORT_OPTS:
  53. export_cmd += self.TRAINER_NAME_TO_EXPORT_OPTS[
  54. '_default']
  55. run_script(export_cmd, wd="../deploy/export")
  56. # Construct predictor
  57. # TODO: Test trt and mkl
  58. predictor = pdrs.deploy.Predictor(
  59. static_model_dir,
  60. use_gpu=paddle.device.get_device().startswith('gpu'))
  61. self.check_predictor(predictor, trainer)
  62. return _test_predictor_impl
  63. for trainer_name in cls.MODULE.__all__:
  64. if trainer_name in cls.WHITE_LIST:
  65. continue
  66. setattr(cls, 'test_' + trainer_name, _test_predictor(trainer_name))
  67. return cls
  68. def check_predictor(self, predictor, trainer):
  69. raise NotImplementedError
  70. def check_dict_equal(
  71. self,
  72. dict_,
  73. expected_dict,
  74. ignore_keys=('label_map', 'mask', 'category', 'category_id')):
  75. # By default do not compare label_maps, masks, or categories,
  76. # because numeric errors could result in large difference in labels.
  77. if isinstance(dict_, list):
  78. self.assertIsInstance(expected_dict, list)
  79. self.assertEqual(len(dict_), len(expected_dict))
  80. for d1, d2 in zip(dict_, expected_dict):
  81. self.check_dict_equal(d1, d2, ignore_keys=ignore_keys)
  82. else:
  83. assert isinstance(dict_, dict)
  84. assert isinstance(expected_dict, dict)
  85. self.assertEqual(dict_.keys(), expected_dict.keys())
  86. ignore_keys = set() if ignore_keys is None else set(ignore_keys)
  87. for key in dict_.keys():
  88. if key in ignore_keys:
  89. continue
  90. # Use higher tolerance
  91. self.check_output_equal(
  92. dict_[key], expected_dict[key], rtol=1.e-4, atol=1.e-6)
  93. @TestPredictor.add_tests
  94. class TestCDPredictor(TestPredictor):
  95. MODULE = pdrs.tasks.change_detector
  96. TRAINER_NAME_TO_EXPORT_OPTS = {
  97. '_default': "--fixed_input_shape [-1,3,256,256]"
  98. }
  99. # HACK: Skip CDNet.
  100. # These models are heavily affected by numeric errors.
  101. WHITE_LIST = ['CDNet']
  102. def check_predictor(self, predictor, trainer):
  103. t1_path = "data/ssmt/optical_t1.bmp"
  104. t2_path = "data/ssmt/optical_t2.bmp"
  105. single_input = (t1_path, t2_path)
  106. num_inputs = 2
  107. transforms = pdrs.transforms.Compose([
  108. pdrs.transforms.DecodeImg(), pdrs.transforms.Normalize(),
  109. pdrs.transforms.ArrangeChangeDetector('test')
  110. ])
  111. # Expected failure
  112. with self.assertRaises(ValueError):
  113. predictor.predict(t1_path, transforms=transforms)
  114. # Single input (file paths)
  115. input_ = single_input
  116. out_single_file_p = predictor.predict(input_, transforms=transforms)
  117. out_single_file_t = trainer.predict(input_, transforms=transforms)
  118. self.check_dict_equal(out_single_file_p, out_single_file_t)
  119. out_single_file_list_p = predictor.predict(
  120. [input_], transforms=transforms)
  121. self.assertEqual(len(out_single_file_list_p), 1)
  122. self.check_dict_equal(out_single_file_list_p[0], out_single_file_p)
  123. out_single_file_list_t = trainer.predict(
  124. [input_], transforms=transforms)
  125. self.check_dict_equal(out_single_file_list_p[0],
  126. out_single_file_list_t[0])
  127. # Single input (ndarrays)
  128. input_ = (decode_image(
  129. t1_path, to_rgb=False), decode_image(
  130. t2_path, to_rgb=False)) # Reuse the name `input_`
  131. out_single_array_p = predictor.predict(input_, transforms=transforms)
  132. self.check_dict_equal(out_single_array_p, out_single_file_p)
  133. out_single_array_t = trainer.predict(input_, transforms=transforms)
  134. self.check_dict_equal(out_single_array_p, out_single_array_t)
  135. out_single_array_list_p = predictor.predict(
  136. [input_], transforms=transforms)
  137. self.assertEqual(len(out_single_array_list_p), 1)
  138. self.check_dict_equal(out_single_array_list_p[0], out_single_array_p)
  139. out_single_array_list_t = trainer.predict(
  140. [input_], transforms=transforms)
  141. self.check_dict_equal(out_single_array_list_p[0],
  142. out_single_array_list_t[0])
  143. # Multiple inputs (file paths)
  144. input_ = [single_input] * num_inputs # Reuse the name `input_`
  145. out_multi_file_p = predictor.predict(input_, transforms=transforms)
  146. self.assertEqual(len(out_multi_file_p), num_inputs)
  147. out_multi_file_t = trainer.predict(input_, transforms=transforms)
  148. self.assertEqual(len(out_multi_file_t), num_inputs)
  149. # Multiple inputs (ndarrays)
  150. input_ = [(decode_image(
  151. t1_path, to_rgb=False), decode_image(
  152. t2_path, to_rgb=False))] * num_inputs # Reuse the name `input_`
  153. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  154. self.assertEqual(len(out_multi_array_p), num_inputs)
  155. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  156. self.assertEqual(len(out_multi_array_t), num_inputs)
  157. @TestPredictor.add_tests
  158. class TestClasPredictor(TestPredictor):
  159. MODULE = pdrs.tasks.classifier
  160. TRAINER_NAME_TO_EXPORT_OPTS = {
  161. '_default': "--fixed_input_shape [-1,3,256,256]"
  162. }
  163. def check_predictor(self, predictor, trainer):
  164. single_input = "data/ssmt/optical_t1.bmp"
  165. num_inputs = 2
  166. transforms = pdrs.transforms.Compose([
  167. pdrs.transforms.DecodeImg(), pdrs.transforms.Normalize(),
  168. pdrs.transforms.ArrangeClassifier('test')
  169. ])
  170. labels = list(range(2))
  171. trainer.labels = labels
  172. predictor._model.labels = labels
  173. # Single input (file path)
  174. input_ = single_input
  175. out_single_file_p = predictor.predict(input_, transforms=transforms)
  176. out_single_file_t = trainer.predict(input_, transforms=transforms)
  177. self.check_dict_equal(out_single_file_p, out_single_file_t)
  178. out_single_file_list_p = predictor.predict(
  179. [input_], transforms=transforms)
  180. self.assertEqual(len(out_single_file_list_p), 1)
  181. self.check_dict_equal(out_single_file_list_p[0], out_single_file_p)
  182. out_single_file_list_t = trainer.predict(
  183. [input_], transforms=transforms)
  184. self.check_dict_equal(out_single_file_list_p[0],
  185. out_single_file_list_t[0])
  186. # Single input (ndarray)
  187. input_ = decode_image(
  188. single_input, to_rgb=False) # Reuse the name `input_`
  189. out_single_array_p = predictor.predict(input_, transforms=transforms)
  190. self.check_dict_equal(out_single_array_p, out_single_file_p)
  191. out_single_array_t = trainer.predict(input_, transforms=transforms)
  192. self.check_dict_equal(out_single_array_p, out_single_array_t)
  193. out_single_array_list_p = predictor.predict(
  194. [input_], transforms=transforms)
  195. self.assertEqual(len(out_single_array_list_p), 1)
  196. self.check_dict_equal(out_single_array_list_p[0], out_single_array_p)
  197. out_single_array_list_t = trainer.predict(
  198. [input_], transforms=transforms)
  199. self.check_dict_equal(out_single_array_list_p[0],
  200. out_single_array_list_t[0])
  201. # Multiple inputs (file paths)
  202. input_ = [single_input] * num_inputs # Reuse the name `input_`
  203. out_multi_file_p = predictor.predict(input_, transforms=transforms)
  204. self.assertEqual(len(out_multi_file_p), num_inputs)
  205. out_multi_file_t = trainer.predict(input_, transforms=transforms)
  206. # Check value consistence
  207. self.check_dict_equal(out_multi_file_p, out_multi_file_t)
  208. # Multiple inputs (ndarrays)
  209. input_ = [decode_image(
  210. single_input, to_rgb=False)] * num_inputs # Reuse the name `input_`
  211. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  212. self.assertEqual(len(out_multi_array_p), num_inputs)
  213. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  214. self.check_dict_equal(out_multi_array_p, out_multi_array_t)
  215. @TestPredictor.add_tests
  216. class TestDetPredictor(TestPredictor):
  217. MODULE = pdrs.tasks.object_detector
  218. TRAINER_NAME_TO_EXPORT_OPTS = {
  219. '_default': "--fixed_input_shape [-1,3,256,256]"
  220. }
  221. def check_predictor(self, predictor, trainer):
  222. # For detection tasks, do NOT ensure the consistence of bboxes.
  223. # This is because the coordinates of bboxes were observed to be very sensitive to numeric errors,
  224. # given that the network is (partially?) randomly initialized.
  225. single_input = "data/ssmt/optical_t1.bmp"
  226. num_inputs = 2
  227. transforms = pdrs.transforms.Compose([
  228. pdrs.transforms.DecodeImg(), pdrs.transforms.Normalize(),
  229. pdrs.transforms.ArrangeDetector('test')
  230. ])
  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, to_rgb=False) # 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, to_rgb=False)] * num_inputs # Reuse the name `input_`
  264. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  265. self.assertEqual(len(out_multi_array_p), num_inputs)
  266. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  267. self.assertEqual(len(out_multi_array_t), num_inputs)
  268. @TestPredictor.add_tests
  269. class TestSegPredictor(TestPredictor):
  270. MODULE = pdrs.tasks.segmenter
  271. TRAINER_NAME_TO_EXPORT_OPTS = {
  272. '_default': "--fixed_input_shape [-1,3,256,256]"
  273. }
  274. def check_predictor(self, predictor, trainer):
  275. single_input = "data/ssmt/optical_t1.bmp"
  276. num_inputs = 2
  277. transforms = pdrs.transforms.Compose([
  278. pdrs.transforms.DecodeImg(), pdrs.transforms.Normalize(),
  279. pdrs.transforms.ArrangeSegmenter('test')
  280. ])
  281. # Single input (file path)
  282. input_ = single_input
  283. out_single_file_p = predictor.predict(input_, transforms=transforms)
  284. out_single_file_t = trainer.predict(input_, transforms=transforms)
  285. self.check_dict_equal(out_single_file_p, out_single_file_t)
  286. out_single_file_list_p = predictor.predict(
  287. [input_], transforms=transforms)
  288. self.assertEqual(len(out_single_file_list_p), 1)
  289. self.check_dict_equal(out_single_file_list_p[0], out_single_file_p)
  290. out_single_file_list_t = trainer.predict(
  291. [input_], transforms=transforms)
  292. self.check_dict_equal(out_single_file_list_p[0],
  293. out_single_file_list_t[0])
  294. # Single input (ndarray)
  295. input_ = decode_image(
  296. single_input, to_rgb=False) # Reuse the name `input_`
  297. out_single_array_p = predictor.predict(input_, transforms=transforms)
  298. self.check_dict_equal(out_single_array_p, out_single_file_p)
  299. out_single_array_t = trainer.predict(input_, transforms=transforms)
  300. self.check_dict_equal(out_single_array_p, out_single_array_t)
  301. out_single_array_list_p = predictor.predict(
  302. [input_], transforms=transforms)
  303. self.assertEqual(len(out_single_array_list_p), 1)
  304. self.check_dict_equal(out_single_array_list_p[0], out_single_array_p)
  305. out_single_array_list_t = trainer.predict(
  306. [input_], transforms=transforms)
  307. self.check_dict_equal(out_single_array_list_p[0],
  308. out_single_array_list_t[0])
  309. # Multiple inputs (file paths)
  310. input_ = [single_input] * num_inputs # Reuse the name `input_`
  311. out_multi_file_p = predictor.predict(input_, transforms=transforms)
  312. self.assertEqual(len(out_multi_file_p), num_inputs)
  313. out_multi_file_t = trainer.predict(input_, transforms=transforms)
  314. self.assertEqual(len(out_multi_file_t), num_inputs)
  315. # Multiple inputs (ndarrays)
  316. input_ = [decode_image(
  317. single_input, to_rgb=False)] * num_inputs # Reuse the name `input_`
  318. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  319. self.assertEqual(len(out_multi_array_p), num_inputs)
  320. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  321. self.assertEqual(len(out_multi_array_t), num_inputs)