test_predictor.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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([pdrs.transforms.Normalize()])
  108. # Expected failure
  109. with self.assertRaises(ValueError):
  110. predictor.predict(t1_path, transforms=transforms)
  111. # Single input (file paths)
  112. input_ = single_input
  113. out_single_file_p = predictor.predict(input_, transforms=transforms)
  114. out_single_file_t = trainer.predict(input_, transforms=transforms)
  115. self.check_dict_equal(out_single_file_p, out_single_file_t)
  116. out_single_file_list_p = predictor.predict(
  117. [input_], transforms=transforms)
  118. self.assertEqual(len(out_single_file_list_p), 1)
  119. self.check_dict_equal(out_single_file_list_p[0], out_single_file_p)
  120. out_single_file_list_t = trainer.predict(
  121. [input_], transforms=transforms)
  122. self.check_dict_equal(out_single_file_list_p[0],
  123. out_single_file_list_t[0])
  124. # Single input (ndarrays)
  125. input_ = (decode_image(
  126. t1_path, to_rgb=False), decode_image(
  127. t2_path, to_rgb=False)) # Reuse the name `input_`
  128. out_single_array_p = predictor.predict(input_, transforms=transforms)
  129. self.check_dict_equal(out_single_array_p, out_single_file_p)
  130. out_single_array_t = trainer.predict(input_, transforms=transforms)
  131. self.check_dict_equal(out_single_array_p, out_single_array_t)
  132. out_single_array_list_p = predictor.predict(
  133. [input_], transforms=transforms)
  134. self.assertEqual(len(out_single_array_list_p), 1)
  135. self.check_dict_equal(out_single_array_list_p[0], out_single_array_p)
  136. out_single_array_list_t = trainer.predict(
  137. [input_], transforms=transforms)
  138. self.check_dict_equal(out_single_array_list_p[0],
  139. out_single_array_list_t[0])
  140. # Multiple inputs (file paths)
  141. input_ = [single_input] * num_inputs # Reuse the name `input_`
  142. out_multi_file_p = predictor.predict(input_, transforms=transforms)
  143. self.assertEqual(len(out_multi_file_p), num_inputs)
  144. out_multi_file_t = trainer.predict(input_, transforms=transforms)
  145. self.assertEqual(len(out_multi_file_t), num_inputs)
  146. # Multiple inputs (ndarrays)
  147. input_ = [(decode_image(
  148. t1_path, to_rgb=False), decode_image(
  149. t2_path, to_rgb=False))] * num_inputs # Reuse the name `input_`
  150. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  151. self.assertEqual(len(out_multi_array_p), num_inputs)
  152. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  153. self.assertEqual(len(out_multi_array_t), num_inputs)
  154. @TestPredictor.add_tests
  155. class TestClasPredictor(TestPredictor):
  156. MODULE = pdrs.tasks.classifier
  157. TRAINER_NAME_TO_EXPORT_OPTS = {
  158. '_default': "--fixed_input_shape [-1,3,256,256]"
  159. }
  160. def check_predictor(self, predictor, trainer):
  161. single_input = "data/ssmt/optical_t1.bmp"
  162. num_inputs = 2
  163. transforms = pdrs.transforms.Compose([pdrs.transforms.Normalize()])
  164. labels = list(range(2))
  165. trainer.labels = labels
  166. predictor._model.labels = labels
  167. # Single input (file path)
  168. input_ = single_input
  169. out_single_file_p = predictor.predict(input_, transforms=transforms)
  170. out_single_file_t = trainer.predict(input_, transforms=transforms)
  171. self.check_dict_equal(out_single_file_p, out_single_file_t)
  172. out_single_file_list_p = predictor.predict(
  173. [input_], transforms=transforms)
  174. self.assertEqual(len(out_single_file_list_p), 1)
  175. self.check_dict_equal(out_single_file_list_p[0], out_single_file_p)
  176. out_single_file_list_t = trainer.predict(
  177. [input_], transforms=transforms)
  178. self.check_dict_equal(out_single_file_list_p[0],
  179. out_single_file_list_t[0])
  180. # Single input (ndarray)
  181. input_ = decode_image(
  182. single_input, to_rgb=False) # Reuse the name `input_`
  183. out_single_array_p = predictor.predict(input_, transforms=transforms)
  184. self.check_dict_equal(out_single_array_p, out_single_file_p)
  185. out_single_array_t = trainer.predict(input_, transforms=transforms)
  186. self.check_dict_equal(out_single_array_p, out_single_array_t)
  187. out_single_array_list_p = predictor.predict(
  188. [input_], transforms=transforms)
  189. self.assertEqual(len(out_single_array_list_p), 1)
  190. self.check_dict_equal(out_single_array_list_p[0], out_single_array_p)
  191. out_single_array_list_t = trainer.predict(
  192. [input_], transforms=transforms)
  193. self.check_dict_equal(out_single_array_list_p[0],
  194. out_single_array_list_t[0])
  195. # Multiple inputs (file paths)
  196. input_ = [single_input] * num_inputs # Reuse the name `input_`
  197. out_multi_file_p = predictor.predict(input_, transforms=transforms)
  198. self.assertEqual(len(out_multi_file_p), num_inputs)
  199. out_multi_file_t = trainer.predict(input_, transforms=transforms)
  200. # Check value consistence
  201. self.check_dict_equal(out_multi_file_p, out_multi_file_t)
  202. # Multiple inputs (ndarrays)
  203. input_ = [decode_image(
  204. single_input, to_rgb=False)] * num_inputs # Reuse the name `input_`
  205. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  206. self.assertEqual(len(out_multi_array_p), num_inputs)
  207. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  208. self.check_dict_equal(out_multi_array_p, out_multi_array_t)
  209. @TestPredictor.add_tests
  210. class TestDetPredictor(TestPredictor):
  211. MODULE = pdrs.tasks.object_detector
  212. TRAINER_NAME_TO_EXPORT_OPTS = {
  213. '_default': "--fixed_input_shape [-1,3,256,256]"
  214. }
  215. def check_predictor(self, predictor, trainer):
  216. # For detection tasks, do NOT ensure the consistence of bboxes.
  217. # This is because the coordinates of bboxes were observed to be very sensitive to numeric errors,
  218. # given that the network is (partially?) randomly initialized.
  219. single_input = "data/ssmt/optical_t1.bmp"
  220. num_inputs = 2
  221. transforms = pdrs.transforms.Compose([pdrs.transforms.Normalize()])
  222. labels = list(range(80))
  223. trainer.labels = labels
  224. predictor._model.labels = labels
  225. # Single input (file path)
  226. input_ = single_input
  227. predictor.predict(input_, transforms=transforms)
  228. trainer.predict(input_, transforms=transforms)
  229. out_single_file_list_p = predictor.predict(
  230. [input_], transforms=transforms)
  231. self.assertEqual(len(out_single_file_list_p), 1)
  232. out_single_file_list_t = trainer.predict(
  233. [input_], transforms=transforms)
  234. self.assertEqual(len(out_single_file_list_t), 1)
  235. # Single input (ndarray)
  236. input_ = decode_image(
  237. single_input, to_rgb=False) # Reuse the name `input_`
  238. predictor.predict(input_, transforms=transforms)
  239. trainer.predict(input_, transforms=transforms)
  240. out_single_array_list_p = predictor.predict(
  241. [input_], transforms=transforms)
  242. self.assertEqual(len(out_single_array_list_p), 1)
  243. out_single_array_list_t = trainer.predict(
  244. [input_], transforms=transforms)
  245. self.assertEqual(len(out_single_array_list_t), 1)
  246. # Multiple inputs (file paths)
  247. input_ = [single_input] * num_inputs # Reuse the name `input_`
  248. out_multi_file_p = predictor.predict(input_, transforms=transforms)
  249. self.assertEqual(len(out_multi_file_p), num_inputs)
  250. out_multi_file_t = trainer.predict(input_, transforms=transforms)
  251. self.assertEqual(len(out_multi_file_t), num_inputs)
  252. # Multiple inputs (ndarrays)
  253. input_ = [decode_image(
  254. single_input, to_rgb=False)] * num_inputs # Reuse the name `input_`
  255. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  256. self.assertEqual(len(out_multi_array_p), num_inputs)
  257. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  258. self.assertEqual(len(out_multi_array_t), num_inputs)
  259. @TestPredictor.add_tests
  260. class TestSegPredictor(TestPredictor):
  261. MODULE = pdrs.tasks.segmenter
  262. TRAINER_NAME_TO_EXPORT_OPTS = {
  263. '_default': "--fixed_input_shape [-1,3,256,256]"
  264. }
  265. def check_predictor(self, predictor, trainer):
  266. single_input = "data/ssmt/optical_t1.bmp"
  267. num_inputs = 2
  268. transforms = pdrs.transforms.Compose([pdrs.transforms.Normalize()])
  269. # Single input (file path)
  270. input_ = single_input
  271. out_single_file_p = predictor.predict(input_, transforms=transforms)
  272. out_single_file_t = trainer.predict(input_, transforms=transforms)
  273. self.check_dict_equal(out_single_file_p, out_single_file_t)
  274. out_single_file_list_p = predictor.predict(
  275. [input_], transforms=transforms)
  276. self.assertEqual(len(out_single_file_list_p), 1)
  277. self.check_dict_equal(out_single_file_list_p[0], out_single_file_p)
  278. out_single_file_list_t = trainer.predict(
  279. [input_], transforms=transforms)
  280. self.check_dict_equal(out_single_file_list_p[0],
  281. out_single_file_list_t[0])
  282. # Single input (ndarray)
  283. input_ = decode_image(
  284. single_input, to_rgb=False) # Reuse the name `input_`
  285. out_single_array_p = predictor.predict(input_, transforms=transforms)
  286. self.check_dict_equal(out_single_array_p, out_single_file_p)
  287. out_single_array_t = trainer.predict(input_, transforms=transforms)
  288. self.check_dict_equal(out_single_array_p, out_single_array_t)
  289. out_single_array_list_p = predictor.predict(
  290. [input_], transforms=transforms)
  291. self.assertEqual(len(out_single_array_list_p), 1)
  292. self.check_dict_equal(out_single_array_list_p[0], out_single_array_p)
  293. out_single_array_list_t = trainer.predict(
  294. [input_], transforms=transforms)
  295. self.check_dict_equal(out_single_array_list_p[0],
  296. out_single_array_list_t[0])
  297. # Multiple inputs (file paths)
  298. input_ = [single_input] * num_inputs # Reuse the name `input_`
  299. out_multi_file_p = predictor.predict(input_, transforms=transforms)
  300. self.assertEqual(len(out_multi_file_p), num_inputs)
  301. out_multi_file_t = trainer.predict(input_, transforms=transforms)
  302. self.assertEqual(len(out_multi_file_t), num_inputs)
  303. # Multiple inputs (ndarrays)
  304. input_ = [decode_image(
  305. single_input, to_rgb=False)] * num_inputs # Reuse the name `input_`
  306. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  307. self.assertEqual(len(out_multi_array_p), num_inputs)
  308. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  309. self.assertEqual(len(out_multi_array_t), num_inputs)