test_predictor.py 18 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 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. 'TestResPredictor', '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, read_raw=True), decode_image(
  130. t2_path, read_raw=True)) # 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, read_raw=True), decode_image(
  152. t2_path,
  153. read_raw=True))] * 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/ssst/optical.bmp"
  166. num_inputs = 2
  167. transforms = pdrs.transforms.Compose([
  168. pdrs.transforms.DecodeImg(), pdrs.transforms.Normalize(),
  169. pdrs.transforms.ArrangeClassifier('test')
  170. ])
  171. labels = list(range(2))
  172. trainer.labels = labels
  173. predictor._model.labels = labels
  174. # Single input (file path)
  175. input_ = single_input
  176. out_single_file_p = predictor.predict(input_, transforms=transforms)
  177. out_single_file_t = trainer.predict(input_, transforms=transforms)
  178. self.check_dict_equal(out_single_file_p, out_single_file_t)
  179. out_single_file_list_p = predictor.predict(
  180. [input_], transforms=transforms)
  181. self.assertEqual(len(out_single_file_list_p), 1)
  182. self.check_dict_equal(out_single_file_list_p[0], out_single_file_p)
  183. out_single_file_list_t = trainer.predict(
  184. [input_], transforms=transforms)
  185. self.check_dict_equal(out_single_file_list_p[0],
  186. out_single_file_list_t[0])
  187. # Single input (ndarray)
  188. input_ = decode_image(
  189. single_input, read_raw=True) # Reuse the name `input_`
  190. out_single_array_p = predictor.predict(input_, transforms=transforms)
  191. self.check_dict_equal(out_single_array_p, out_single_file_p)
  192. out_single_array_t = trainer.predict(input_, transforms=transforms)
  193. self.check_dict_equal(out_single_array_p, out_single_array_t)
  194. out_single_array_list_p = predictor.predict(
  195. [input_], transforms=transforms)
  196. self.assertEqual(len(out_single_array_list_p), 1)
  197. self.check_dict_equal(out_single_array_list_p[0], out_single_array_p)
  198. out_single_array_list_t = trainer.predict(
  199. [input_], transforms=transforms)
  200. self.check_dict_equal(out_single_array_list_p[0],
  201. out_single_array_list_t[0])
  202. # Multiple inputs (file paths)
  203. input_ = [single_input] * num_inputs # Reuse the name `input_`
  204. out_multi_file_p = predictor.predict(input_, transforms=transforms)
  205. self.assertEqual(len(out_multi_file_p), num_inputs)
  206. out_multi_file_t = trainer.predict(input_, transforms=transforms)
  207. # Check value consistence
  208. self.check_dict_equal(out_multi_file_p, out_multi_file_t)
  209. # Multiple inputs (ndarrays)
  210. input_ = [decode_image(
  211. single_input,
  212. read_raw=True)] * num_inputs # Reuse the name `input_`
  213. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  214. self.assertEqual(len(out_multi_array_p), num_inputs)
  215. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  216. self.check_dict_equal(out_multi_array_p, out_multi_array_t)
  217. @TestPredictor.add_tests
  218. class TestDetPredictor(TestPredictor):
  219. MODULE = pdrs.tasks.object_detector
  220. TRAINER_NAME_TO_EXPORT_OPTS = {
  221. '_default': "--fixed_input_shape [-1,3,256,256]"
  222. }
  223. def check_predictor(self, predictor, trainer):
  224. # For detection tasks, do NOT ensure the consistence of bboxes.
  225. # This is because the coordinates of bboxes were observed to be very sensitive to numeric errors,
  226. # given that the network is (partially?) randomly initialized.
  227. single_input = "data/ssst/optical.bmp"
  228. num_inputs = 2
  229. transforms = pdrs.transforms.Compose([
  230. pdrs.transforms.DecodeImg(), pdrs.transforms.Normalize(),
  231. pdrs.transforms.ArrangeDetector('test')
  232. ])
  233. labels = list(range(80))
  234. trainer.labels = labels
  235. predictor._model.labels = labels
  236. # Single input (file path)
  237. input_ = single_input
  238. predictor.predict(input_, transforms=transforms)
  239. trainer.predict(input_, transforms=transforms)
  240. out_single_file_list_p = predictor.predict(
  241. [input_], transforms=transforms)
  242. self.assertEqual(len(out_single_file_list_p), 1)
  243. out_single_file_list_t = trainer.predict(
  244. [input_], transforms=transforms)
  245. self.assertEqual(len(out_single_file_list_t), 1)
  246. # Single input (ndarray)
  247. input_ = decode_image(
  248. single_input, read_raw=True) # Reuse the name `input_`
  249. predictor.predict(input_, transforms=transforms)
  250. trainer.predict(input_, transforms=transforms)
  251. out_single_array_list_p = predictor.predict(
  252. [input_], transforms=transforms)
  253. self.assertEqual(len(out_single_array_list_p), 1)
  254. out_single_array_list_t = trainer.predict(
  255. [input_], transforms=transforms)
  256. self.assertEqual(len(out_single_array_list_t), 1)
  257. # Multiple inputs (file paths)
  258. input_ = [single_input] * num_inputs # Reuse the name `input_`
  259. out_multi_file_p = predictor.predict(input_, transforms=transforms)
  260. self.assertEqual(len(out_multi_file_p), num_inputs)
  261. out_multi_file_t = trainer.predict(input_, transforms=transforms)
  262. self.assertEqual(len(out_multi_file_t), num_inputs)
  263. # Multiple inputs (ndarrays)
  264. input_ = [decode_image(
  265. single_input,
  266. read_raw=True)] * num_inputs # Reuse the name `input_`
  267. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  268. self.assertEqual(len(out_multi_array_p), num_inputs)
  269. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  270. self.assertEqual(len(out_multi_array_t), num_inputs)
  271. @TestPredictor.add_tests
  272. class TestResPredictor(TestPredictor):
  273. MODULE = pdrs.tasks.restorer
  274. def check_predictor(self, predictor, trainer):
  275. # For restoration tasks, do NOT ensure the consistence of numeric values,
  276. # because the output is of uint8 type.
  277. single_input = "data/ssst/optical.bmp"
  278. num_inputs = 2
  279. transforms = pdrs.transforms.Compose([
  280. pdrs.transforms.DecodeImg(), pdrs.transforms.Normalize(),
  281. pdrs.transforms.ArrangeRestorer('test')
  282. ])
  283. # Single input (file path)
  284. input_ = single_input
  285. predictor.predict(input_, transforms=transforms)
  286. trainer.predict(input_, transforms=transforms)
  287. out_single_file_list_p = predictor.predict(
  288. [input_], transforms=transforms)
  289. self.assertEqual(len(out_single_file_list_p), 1)
  290. out_single_file_list_t = trainer.predict(
  291. [input_], transforms=transforms)
  292. self.assertEqual(len(out_single_file_list_t), 1)
  293. # Single input (ndarray)
  294. input_ = decode_image(
  295. single_input, read_raw=True) # Reuse the name `input_`
  296. predictor.predict(input_, transforms=transforms)
  297. trainer.predict(input_, transforms=transforms)
  298. out_single_array_list_p = predictor.predict(
  299. [input_], transforms=transforms)
  300. self.assertEqual(len(out_single_array_list_p), 1)
  301. out_single_array_list_t = trainer.predict(
  302. [input_], transforms=transforms)
  303. self.assertEqual(len(out_single_array_list_t), 1)
  304. # Multiple inputs (file paths)
  305. input_ = [single_input] * num_inputs # Reuse the name `input_`
  306. out_multi_file_p = predictor.predict(input_, transforms=transforms)
  307. self.assertEqual(len(out_multi_file_p), num_inputs)
  308. out_multi_file_t = trainer.predict(input_, transforms=transforms)
  309. self.assertEqual(len(out_multi_file_t), num_inputs)
  310. # Multiple inputs (ndarrays)
  311. input_ = [decode_image(
  312. single_input,
  313. read_raw=True)] * num_inputs # Reuse the name `input_`
  314. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  315. self.assertEqual(len(out_multi_array_p), num_inputs)
  316. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  317. self.assertEqual(len(out_multi_array_t), num_inputs)
  318. @TestPredictor.add_tests
  319. class TestSegPredictor(TestPredictor):
  320. MODULE = pdrs.tasks.segmenter
  321. TRAINER_NAME_TO_EXPORT_OPTS = {
  322. '_default': "--fixed_input_shape [-1,3,256,256]"
  323. }
  324. def check_predictor(self, predictor, trainer):
  325. single_input = "data/ssst/optical.bmp"
  326. num_inputs = 2
  327. transforms = pdrs.transforms.Compose([
  328. pdrs.transforms.DecodeImg(), pdrs.transforms.Normalize(),
  329. pdrs.transforms.ArrangeSegmenter('test')
  330. ])
  331. # Single input (file path)
  332. input_ = single_input
  333. out_single_file_p = predictor.predict(input_, transforms=transforms)
  334. out_single_file_t = trainer.predict(input_, transforms=transforms)
  335. self.check_dict_equal(out_single_file_p, out_single_file_t)
  336. out_single_file_list_p = predictor.predict(
  337. [input_], transforms=transforms)
  338. self.assertEqual(len(out_single_file_list_p), 1)
  339. self.check_dict_equal(out_single_file_list_p[0], out_single_file_p)
  340. out_single_file_list_t = trainer.predict(
  341. [input_], transforms=transforms)
  342. self.check_dict_equal(out_single_file_list_p[0],
  343. out_single_file_list_t[0])
  344. # Single input (ndarray)
  345. input_ = decode_image(
  346. single_input, read_raw=True) # Reuse the name `input_`
  347. out_single_array_p = predictor.predict(input_, transforms=transforms)
  348. self.check_dict_equal(out_single_array_p, out_single_file_p)
  349. out_single_array_t = trainer.predict(input_, transforms=transforms)
  350. self.check_dict_equal(out_single_array_p, out_single_array_t)
  351. out_single_array_list_p = predictor.predict(
  352. [input_], transforms=transforms)
  353. self.assertEqual(len(out_single_array_list_p), 1)
  354. self.check_dict_equal(out_single_array_list_p[0], out_single_array_p)
  355. out_single_array_list_t = trainer.predict(
  356. [input_], transforms=transforms)
  357. self.check_dict_equal(out_single_array_list_p[0],
  358. out_single_array_list_t[0])
  359. # Multiple inputs (file paths)
  360. input_ = [single_input] * num_inputs # Reuse the name `input_`
  361. out_multi_file_p = predictor.predict(input_, transforms=transforms)
  362. self.assertEqual(len(out_multi_file_p), num_inputs)
  363. out_multi_file_t = trainer.predict(input_, transforms=transforms)
  364. self.assertEqual(len(out_multi_file_t), num_inputs)
  365. # Multiple inputs (ndarrays)
  366. input_ = [decode_image(
  367. single_input,
  368. read_raw=True)] * num_inputs # Reuse the name `input_`
  369. out_multi_array_p = predictor.predict(input_, transforms=transforms)
  370. self.assertEqual(len(out_multi_array_p), num_inputs)
  371. out_multi_array_t = trainer.predict(input_, transforms=transforms)
  372. self.assertEqual(len(out_multi_array_t), num_inputs)