test_predictor.py 16 KB

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