object_detector.py 89 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156
  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 collections
  15. import copy
  16. import os
  17. import os.path as osp
  18. import numpy as np
  19. import paddle
  20. from paddle.static import InputSpec
  21. import paddlers
  22. import paddlers.models.ppdet as ppdet
  23. from paddlers.models.ppdet.modeling.proposal_generator.target_layer import BBoxAssigner, MaskAssigner
  24. from paddlers.transforms import decode_image
  25. from paddlers.transforms.operators import _NormalizeBox, _PadBox, _BboxXYXY2XYWH, Resize, Pad
  26. from paddlers.transforms.batch_operators import BatchCompose, BatchRandomResize, BatchRandomResizeByShort, \
  27. _BatchPad, _Gt2YoloTarget
  28. from paddlers.models.ppdet.optimizer import ModelEMA
  29. import paddlers.utils.logging as logging
  30. from paddlers.utils.checkpoint import det_pretrain_weights_dict
  31. from .base import BaseModel
  32. from .utils.det_metrics import VOCMetric, COCOMetric
  33. __all__ = [
  34. "YOLOv3", "FasterRCNN", "PPYOLO", "PPYOLOTiny", "PPYOLOv2", "MaskRCNN"
  35. ]
  36. class BaseDetector(BaseModel):
  37. def __init__(self, model_name, num_classes=80, **params):
  38. self.init_params.update(locals())
  39. if 'with_net' in self.init_params:
  40. del self.init_params['with_net']
  41. super(BaseDetector, self).__init__('detector')
  42. if not hasattr(ppdet.modeling, model_name):
  43. raise ValueError("ERROR: There is no model named {}.".format(
  44. model_name))
  45. self.model_name = model_name
  46. self.num_classes = num_classes
  47. self.labels = None
  48. if params.get('with_net', True):
  49. params.pop('with_net', None)
  50. self.net = self.build_net(**params)
  51. def build_net(self, **params):
  52. with paddle.utils.unique_name.guard():
  53. net = ppdet.modeling.__dict__[self.model_name](**params)
  54. return net
  55. def _fix_transforms_shape(self, image_shape):
  56. raise NotImplementedError("_fix_transforms_shape: not implemented!")
  57. def _define_input_spec(self, image_shape):
  58. input_spec = [{
  59. "image": InputSpec(
  60. shape=image_shape, name='image', dtype='float32'),
  61. "im_shape": InputSpec(
  62. shape=[image_shape[0], 2], name='im_shape', dtype='float32'),
  63. "scale_factor": InputSpec(
  64. shape=[image_shape[0], 2], name='scale_factor', dtype='float32')
  65. }]
  66. return input_spec
  67. def _check_image_shape(self, image_shape):
  68. if len(image_shape) == 2:
  69. image_shape = [1, 3] + image_shape
  70. if image_shape[-2] % 32 > 0 or image_shape[-1] % 32 > 0:
  71. raise ValueError(
  72. "Height and width in fixed_input_shape must be a multiple of 32, but received {}.".
  73. format(image_shape[-2:]))
  74. return image_shape
  75. def _get_test_inputs(self, image_shape):
  76. if image_shape is not None:
  77. image_shape = self._check_image_shape(image_shape)
  78. self._fix_transforms_shape(image_shape[-2:])
  79. else:
  80. image_shape = [None, 3, -1, -1]
  81. self.fixed_input_shape = image_shape
  82. return self._define_input_spec(image_shape)
  83. def _get_backbone(self, backbone_name, **params):
  84. backbone = getattr(ppdet.modeling, backbone_name)(**params)
  85. return backbone
  86. def run(self, net, inputs, mode):
  87. net_out = net(inputs)
  88. if mode in ['train', 'eval']:
  89. outputs = net_out
  90. else:
  91. outputs = dict()
  92. for key in net_out:
  93. outputs[key] = net_out[key].numpy()
  94. return outputs
  95. def default_optimizer(self,
  96. parameters,
  97. learning_rate,
  98. warmup_steps,
  99. warmup_start_lr,
  100. lr_decay_epochs,
  101. lr_decay_gamma,
  102. num_steps_each_epoch,
  103. reg_coeff=1e-04,
  104. scheduler='Piecewise',
  105. num_epochs=None):
  106. if scheduler.lower() == 'piecewise':
  107. if warmup_steps > 0 and warmup_steps > lr_decay_epochs[
  108. 0] * num_steps_each_epoch:
  109. logging.error(
  110. "In function train(), parameters must satisfy: "
  111. "warmup_steps <= lr_decay_epochs[0] * num_samples_in_train_dataset. "
  112. "See this doc for more information: "
  113. "https://github.com/PaddlePaddle/PaddleRS/blob/develop/docs/parameters.md",
  114. exit=False)
  115. logging.error(
  116. "Either `warmup_steps` be less than {} or lr_decay_epochs[0] be greater than {} "
  117. "must be satisfied, please modify 'warmup_steps' or 'lr_decay_epochs' in train function".
  118. format(lr_decay_epochs[0] * num_steps_each_epoch,
  119. warmup_steps // num_steps_each_epoch),
  120. exit=True)
  121. boundaries = [b * num_steps_each_epoch for b in lr_decay_epochs]
  122. values = [(lr_decay_gamma**i) * learning_rate
  123. for i in range(len(lr_decay_epochs) + 1)]
  124. scheduler = paddle.optimizer.lr.PiecewiseDecay(boundaries, values)
  125. elif scheduler.lower() == 'cosine':
  126. if num_epochs is None:
  127. logging.error(
  128. "`num_epochs` must be set while using cosine annealing decay scheduler, but received {}".
  129. format(num_epochs),
  130. exit=False)
  131. if warmup_steps > 0 and warmup_steps > num_epochs * num_steps_each_epoch:
  132. logging.error(
  133. "In function train(), parameters must satisfy: "
  134. "warmup_steps <= num_epochs * num_samples_in_train_dataset. "
  135. "See this doc for more information: "
  136. "https://github.com/PaddlePaddle/PaddleRS/blob/develop/docs/parameters.md",
  137. exit=False)
  138. logging.error(
  139. "`warmup_steps` must be less than the total number of steps({}), "
  140. "please modify 'num_epochs' or 'warmup_steps' in train function".
  141. format(num_epochs * num_steps_each_epoch),
  142. exit=True)
  143. T_max = num_epochs * num_steps_each_epoch - warmup_steps
  144. scheduler = paddle.optimizer.lr.CosineAnnealingDecay(
  145. learning_rate=learning_rate,
  146. T_max=T_max,
  147. eta_min=0.0,
  148. last_epoch=-1)
  149. else:
  150. logging.error(
  151. "Invalid learning rate scheduler: {}!".format(scheduler),
  152. exit=True)
  153. if warmup_steps > 0:
  154. scheduler = paddle.optimizer.lr.LinearWarmup(
  155. learning_rate=scheduler,
  156. warmup_steps=warmup_steps,
  157. start_lr=warmup_start_lr,
  158. end_lr=learning_rate)
  159. optimizer = paddle.optimizer.Momentum(
  160. scheduler,
  161. momentum=.9,
  162. weight_decay=paddle.regularizer.L2Decay(coeff=reg_coeff),
  163. parameters=parameters)
  164. return optimizer
  165. def train(self,
  166. num_epochs,
  167. train_dataset,
  168. train_batch_size=64,
  169. eval_dataset=None,
  170. optimizer=None,
  171. save_interval_epochs=1,
  172. log_interval_steps=10,
  173. save_dir='output',
  174. pretrain_weights='IMAGENET',
  175. learning_rate=.001,
  176. warmup_steps=0,
  177. warmup_start_lr=0.0,
  178. lr_decay_epochs=(216, 243),
  179. lr_decay_gamma=0.1,
  180. metric=None,
  181. use_ema=False,
  182. early_stop=False,
  183. early_stop_patience=5,
  184. use_vdl=True,
  185. resume_checkpoint=None):
  186. """
  187. Train the model.
  188. Args:
  189. num_epochs (int): Number of epochs.
  190. train_dataset (paddlers.datasets.COCODetDataset|paddlers.datasets.VOCDetDataset):
  191. Training dataset.
  192. train_batch_size (int, optional): Total batch size among all cards used in
  193. training. Defaults to 64.
  194. eval_dataset (paddlers.datasets.COCODetDataset|paddlers.datasets.VOCDetDataset, optional):
  195. Evaluation dataset. If None, the model will not be evaluated during training
  196. process. Defaults to None.
  197. optimizer (paddle.optimizer.Optimizer|None, optional): Optimizer used for
  198. training. If None, a default optimizer will be used. Defaults to None.
  199. save_interval_epochs (int, optional): Epoch interval for saving the model.
  200. Defaults to 1.
  201. log_interval_steps (int, optional): Step interval for printing training
  202. information. Defaults to 10.
  203. save_dir (str, optional): Directory to save the model. Defaults to 'output'.
  204. pretrain_weights (str|None, optional): None or name/path of pretrained
  205. weights. If None, no pretrained weights will be loaded.
  206. Defaults to 'IMAGENET'.
  207. learning_rate (float, optional): Learning rate for training. Defaults to .001.
  208. warmup_steps (int, optional): Number of steps of warm-up training.
  209. Defaults to 0.
  210. warmup_start_lr (float, optional): Start learning rate of warm-up training.
  211. Defaults to 0..
  212. lr_decay_epochs (list|tuple, optional): Epoch milestones for learning
  213. rate decay. Defaults to (216, 243).
  214. lr_decay_gamma (float, optional): Gamma coefficient of learning rate decay.
  215. Defaults to .1.
  216. metric (str|None, optional): Evaluation metric. Choices are {'VOC', 'COCO', None}.
  217. If None, determine the metric according to the dataset format.
  218. Defaults to None.
  219. use_ema (bool, optional): Whether to use exponential moving average
  220. strategy. Defaults to False.
  221. early_stop (bool, optional): Whether to adopt early stop strategy.
  222. Defaults to False.
  223. early_stop_patience (int, optional): Early stop patience. Defaults to 5.
  224. use_vdl(bool, optional): Whether to use VisualDL to monitor the training
  225. process. Defaults to True.
  226. resume_checkpoint (str|None, optional): Path of the checkpoint to resume
  227. training from. If None, no training checkpoint will be resumed. At most
  228. Aone of `resume_checkpoint` and `pretrain_weights` can be set simultaneously.
  229. Defaults to None.
  230. """
  231. args = self._pre_train(locals())
  232. return self._real_train(**args)
  233. def _pre_train(self, in_args):
  234. return in_args
  235. def _real_train(self,
  236. num_epochs,
  237. train_dataset,
  238. train_batch_size=64,
  239. eval_dataset=None,
  240. optimizer=None,
  241. save_interval_epochs=1,
  242. log_interval_steps=10,
  243. save_dir='output',
  244. pretrain_weights='IMAGENET',
  245. learning_rate=.001,
  246. warmup_steps=0,
  247. warmup_start_lr=0.0,
  248. lr_decay_epochs=(216, 243),
  249. lr_decay_gamma=0.1,
  250. metric=None,
  251. use_ema=False,
  252. early_stop=False,
  253. early_stop_patience=5,
  254. use_vdl=True,
  255. resume_checkpoint=None):
  256. if self.status == 'Infer':
  257. logging.error(
  258. "Exported inference model does not support training.",
  259. exit=True)
  260. if pretrain_weights is not None and resume_checkpoint is not None:
  261. logging.error(
  262. "pretrain_weights and resume_checkpoint cannot be set simultaneously.",
  263. exit=True)
  264. if train_dataset.__class__.__name__ == 'VOCDetDataset':
  265. train_dataset.data_fields = {
  266. 'im_id', 'image_shape', 'image', 'gt_bbox', 'gt_class',
  267. 'difficult'
  268. }
  269. elif train_dataset.__class__.__name__ == 'CocoDetection':
  270. if self.__class__.__name__ == 'MaskRCNN':
  271. train_dataset.data_fields = {
  272. 'im_id', 'image_shape', 'image', 'gt_bbox', 'gt_class',
  273. 'gt_poly', 'is_crowd'
  274. }
  275. else:
  276. train_dataset.data_fields = {
  277. 'im_id', 'image_shape', 'image', 'gt_bbox', 'gt_class',
  278. 'is_crowd'
  279. }
  280. if metric is None:
  281. if eval_dataset.__class__.__name__ == 'VOCDetDataset':
  282. self.metric = 'voc'
  283. elif eval_dataset.__class__.__name__ == 'COCODetDataset':
  284. self.metric = 'coco'
  285. else:
  286. assert metric.lower() in ['coco', 'voc'], \
  287. "Evaluation metric {} is not supported. Please choose from 'COCO' and 'VOC'."
  288. self.metric = metric.lower()
  289. self.labels = train_dataset.labels
  290. self.num_max_boxes = train_dataset.num_max_boxes
  291. train_dataset.batch_transforms = self._compose_batch_transform(
  292. train_dataset.transforms, mode='train')
  293. # Build optimizer if not defined
  294. if optimizer is None:
  295. num_steps_each_epoch = len(train_dataset) // train_batch_size
  296. self.optimizer = self.default_optimizer(
  297. parameters=self.net.parameters(),
  298. learning_rate=learning_rate,
  299. warmup_steps=warmup_steps,
  300. warmup_start_lr=warmup_start_lr,
  301. lr_decay_epochs=lr_decay_epochs,
  302. lr_decay_gamma=lr_decay_gamma,
  303. num_steps_each_epoch=num_steps_each_epoch)
  304. else:
  305. self.optimizer = optimizer
  306. # Initiate weights
  307. if pretrain_weights is not None and not osp.exists(pretrain_weights):
  308. if pretrain_weights not in det_pretrain_weights_dict['_'.join(
  309. [self.model_name, self.backbone_name])]:
  310. logging.warning(
  311. "Path of pretrain_weights('{}') does not exist!".format(
  312. pretrain_weights))
  313. pretrain_weights = det_pretrain_weights_dict['_'.join(
  314. [self.model_name, self.backbone_name])][0]
  315. logging.warning("Pretrain_weights is forcibly set to '{}'. "
  316. "If you don't want to use pretrain weights, "
  317. "set pretrain_weights to be None.".format(
  318. pretrain_weights))
  319. elif pretrain_weights is not None and osp.exists(pretrain_weights):
  320. if osp.splitext(pretrain_weights)[-1] != '.pdparams':
  321. logging.error(
  322. "Invalid pretrain weights. Please specify a '.pdparams' file.",
  323. exit=True)
  324. pretrained_dir = osp.join(save_dir, 'pretrain')
  325. self.net_initialize(
  326. pretrain_weights=pretrain_weights,
  327. save_dir=pretrained_dir,
  328. resume_checkpoint=resume_checkpoint,
  329. is_backbone_weights=(pretrain_weights == 'IMAGENET' and
  330. 'ESNet_' in self.backbone_name))
  331. if use_ema:
  332. ema = ModelEMA(model=self.net, decay=.9998, use_thres_step=True)
  333. else:
  334. ema = None
  335. # Start train loop
  336. self.train_loop(
  337. num_epochs=num_epochs,
  338. train_dataset=train_dataset,
  339. train_batch_size=train_batch_size,
  340. eval_dataset=eval_dataset,
  341. save_interval_epochs=save_interval_epochs,
  342. log_interval_steps=log_interval_steps,
  343. save_dir=save_dir,
  344. ema=ema,
  345. early_stop=early_stop,
  346. early_stop_patience=early_stop_patience,
  347. use_vdl=use_vdl)
  348. def quant_aware_train(self,
  349. num_epochs,
  350. train_dataset,
  351. train_batch_size=64,
  352. eval_dataset=None,
  353. optimizer=None,
  354. save_interval_epochs=1,
  355. log_interval_steps=10,
  356. save_dir='output',
  357. learning_rate=.00001,
  358. warmup_steps=0,
  359. warmup_start_lr=0.0,
  360. lr_decay_epochs=(216, 243),
  361. lr_decay_gamma=0.1,
  362. metric=None,
  363. use_ema=False,
  364. early_stop=False,
  365. early_stop_patience=5,
  366. use_vdl=True,
  367. resume_checkpoint=None,
  368. quant_config=None):
  369. """
  370. Quantization-aware training.
  371. Args:
  372. num_epochs (int): Number of epochs.
  373. train_dataset (paddlers.datasets.COCODetDataset|paddlers.datasets.VOCDetDataset):
  374. Training dataset.
  375. train_batch_size (int, optional): Total batch size among all cards used in
  376. training. Defaults to 64.
  377. eval_dataset (paddlers.datasets.COCODetDataset|paddlers.datasets.VOCDetDataset, optional):
  378. Evaluation dataset. If None, the model will not be evaluated during training
  379. process. Defaults to None.
  380. optimizer (paddle.optimizer.Optimizer or None, optional): Optimizer used for
  381. training. If None, a default optimizer will be used. Defaults to None.
  382. save_interval_epochs (int, optional): Epoch interval for saving the model.
  383. Defaults to 1.
  384. log_interval_steps (int, optional): Step interval for printing training
  385. information. Defaults to 10.
  386. save_dir (str, optional): Directory to save the model. Defaults to 'output'.
  387. learning_rate (float, optional): Learning rate for training.
  388. Defaults to .00001.
  389. warmup_steps (int, optional): Number of steps of warm-up training.
  390. Defaults to 0.
  391. warmup_start_lr (float, optional): Start learning rate of warm-up training.
  392. Defaults to 0..
  393. lr_decay_epochs (list or tuple, optional): Epoch milestones for learning rate
  394. decay. Defaults to (216, 243).
  395. lr_decay_gamma (float, optional): Gamma coefficient of learning rate decay.
  396. Defaults to .1.
  397. metric (str|None, optional): Evaluation metric. Choices are {'VOC', 'COCO', None}.
  398. If None, determine the metric according to the dataset format.
  399. Defaults to None.
  400. use_ema (bool, optional): Whether to use exponential moving average strategy.
  401. Defaults to False.
  402. early_stop (bool, optional): Whether to adopt early stop strategy.
  403. Defaults to False.
  404. early_stop_patience (int, optional): Early stop patience. Defaults to 5.
  405. use_vdl (bool, optional): Whether to use VisualDL to monitor the training
  406. process. Defaults to True.
  407. quant_config (dict or None, optional): Quantization configuration. If None,
  408. a default rule of thumb configuration will be used. Defaults to None.
  409. resume_checkpoint (str|None, optional): Path of the checkpoint to resume
  410. quantization-aware training from. If None, no training checkpoint will
  411. be resumed. Defaults to None.
  412. """
  413. self._prepare_qat(quant_config)
  414. self.train(
  415. num_epochs=num_epochs,
  416. train_dataset=train_dataset,
  417. train_batch_size=train_batch_size,
  418. eval_dataset=eval_dataset,
  419. optimizer=optimizer,
  420. save_interval_epochs=save_interval_epochs,
  421. log_interval_steps=log_interval_steps,
  422. save_dir=save_dir,
  423. pretrain_weights=None,
  424. learning_rate=learning_rate,
  425. warmup_steps=warmup_steps,
  426. warmup_start_lr=warmup_start_lr,
  427. lr_decay_epochs=lr_decay_epochs,
  428. lr_decay_gamma=lr_decay_gamma,
  429. metric=metric,
  430. use_ema=use_ema,
  431. early_stop=early_stop,
  432. early_stop_patience=early_stop_patience,
  433. use_vdl=use_vdl,
  434. resume_checkpoint=resume_checkpoint)
  435. def evaluate(self,
  436. eval_dataset,
  437. batch_size=1,
  438. metric=None,
  439. return_details=False):
  440. """
  441. Evaluate the model.
  442. Args:
  443. eval_dataset (paddlers.datasets.COCODetDataset|paddlers.datasets.VOCDetDataset):
  444. Evaluation dataset.
  445. batch_size (int, optional): Total batch size among all cards used for
  446. evaluation. Defaults to 1.
  447. metric (str|None, optional): Evaluation metric. Choices are {'VOC', 'COCO', None}.
  448. If None, determine the metric according to the dataset format.
  449. Defaults to None.
  450. return_details (bool, optional): Whether to return evaluation details.
  451. Defaults to False.
  452. Returns:
  453. collections.OrderedDict with key-value pairs:
  454. {"mAP(0.50, 11point)":`mean average precision`}.
  455. """
  456. if metric is None:
  457. if not hasattr(self, 'metric'):
  458. if eval_dataset.__class__.__name__ == 'VOCDetDataset':
  459. self.metric = 'voc'
  460. elif eval_dataset.__class__.__name__ == 'COCODetDataset':
  461. self.metric = 'coco'
  462. else:
  463. assert metric.lower() in ['coco', 'voc'], \
  464. "Evaluation metric {} is not supported. Please choose from 'COCO' and 'VOC'."
  465. self.metric = metric.lower()
  466. if self.metric == 'voc':
  467. eval_dataset.data_fields = {
  468. 'im_id', 'image_shape', 'image', 'gt_bbox', 'gt_class',
  469. 'difficult'
  470. }
  471. elif self.metric == 'coco':
  472. if self.__class__.__name__ == 'MaskRCNN':
  473. eval_dataset.data_fields = {
  474. 'im_id', 'image_shape', 'image', 'gt_bbox', 'gt_class',
  475. 'gt_poly', 'is_crowd'
  476. }
  477. else:
  478. eval_dataset.data_fields = {
  479. 'im_id', 'image_shape', 'image', 'gt_bbox', 'gt_class',
  480. 'is_crowd'
  481. }
  482. eval_dataset.batch_transforms = self._compose_batch_transform(
  483. eval_dataset.transforms, mode='eval')
  484. self._check_transforms(eval_dataset.transforms, 'eval')
  485. self.net.eval()
  486. nranks = paddle.distributed.get_world_size()
  487. local_rank = paddle.distributed.get_rank()
  488. if nranks > 1:
  489. # Initialize parallel environment if not done.
  490. if not paddle.distributed.parallel.parallel_helper._is_parallel_ctx_initialized(
  491. ):
  492. paddle.distributed.init_parallel_env()
  493. if batch_size > 1:
  494. logging.warning(
  495. "Detector only supports single card evaluation with batch_size=1 "
  496. "during evaluation, so batch_size is forcibly set to 1.")
  497. batch_size = 1
  498. if nranks < 2 or local_rank == 0:
  499. self.eval_data_loader = self.build_data_loader(
  500. eval_dataset, batch_size=batch_size, mode='eval')
  501. is_bbox_normalized = False
  502. if eval_dataset.batch_transforms is not None:
  503. is_bbox_normalized = any(
  504. isinstance(t, _NormalizeBox)
  505. for t in eval_dataset.batch_transforms.batch_transforms)
  506. if self.metric == 'voc':
  507. eval_metric = VOCMetric(
  508. labels=eval_dataset.labels,
  509. coco_gt=copy.deepcopy(eval_dataset.coco_gt),
  510. is_bbox_normalized=is_bbox_normalized,
  511. classwise=False)
  512. else:
  513. eval_metric = COCOMetric(
  514. coco_gt=copy.deepcopy(eval_dataset.coco_gt),
  515. classwise=False)
  516. scores = collections.OrderedDict()
  517. logging.info(
  518. "Start to evaluate(total_samples={}, total_steps={})...".format(
  519. eval_dataset.num_samples, eval_dataset.num_samples))
  520. with paddle.no_grad():
  521. for step, data in enumerate(self.eval_data_loader):
  522. outputs = self.run(self.net, data, 'eval')
  523. eval_metric.update(data, outputs)
  524. eval_metric.accumulate()
  525. self.eval_details = eval_metric.details
  526. scores.update(eval_metric.get())
  527. eval_metric.reset()
  528. if return_details:
  529. return scores, self.eval_details
  530. return scores
  531. def predict(self, img_file, transforms=None):
  532. """
  533. Do inference.
  534. Args:
  535. img_file (list[np.ndarray|str] | str | np.ndarray): Image path or decoded
  536. image data, which also could constitute a list, meaning all images to be
  537. predicted as a mini-batch.
  538. transforms (paddlers.transforms.Compose|None, optional): Transforms for
  539. inputs. If None, the transforms for evaluation process will be used.
  540. Defaults to None.
  541. Returns:
  542. If `img_file` is a string or np.array, the result is a list of dict with
  543. key-value pairs:
  544. {"category_id": `category_id`, "category": `category`, "bbox": `[x, y, w, h]`, "score": `score`}.
  545. If `img_file` is a list, the result is a list composed of dicts with the
  546. corresponding fields:
  547. category_id(int): the predicted category ID. 0 represents the first
  548. category in the dataset, and so on.
  549. category(str): category name
  550. bbox(list): bounding box in [x, y, w, h] format
  551. score(str): confidence
  552. mask(dict): Only for instance segmentation task. Mask of the object in
  553. RLE format
  554. """
  555. if transforms is None and not hasattr(self, 'test_transforms'):
  556. raise ValueError("transforms need to be defined, now is None.")
  557. if transforms is None:
  558. transforms = self.test_transforms
  559. if isinstance(img_file, (str, np.ndarray)):
  560. images = [img_file]
  561. else:
  562. images = img_file
  563. batch_samples = self.preprocess(images, transforms)
  564. self.net.eval()
  565. outputs = self.run(self.net, batch_samples, 'test')
  566. prediction = self.postprocess(outputs)
  567. if isinstance(img_file, (str, np.ndarray)):
  568. prediction = prediction[0]
  569. return prediction
  570. def preprocess(self, images, transforms, to_tensor=True):
  571. self._check_transforms(transforms, 'test')
  572. batch_samples = list()
  573. for im in images:
  574. if isinstance(im, str):
  575. im = decode_image(im, to_rgb=False)
  576. sample = {'image': im}
  577. sample = transforms(sample)
  578. batch_samples.append(sample)
  579. batch_transforms = self._compose_batch_transform(transforms, 'test')
  580. batch_samples = batch_transforms(batch_samples)
  581. if to_tensor:
  582. for k in batch_samples:
  583. batch_samples[k] = paddle.to_tensor(batch_samples[k])
  584. return batch_samples
  585. def postprocess(self, batch_pred):
  586. infer_result = {}
  587. if 'bbox' in batch_pred:
  588. bboxes = batch_pred['bbox']
  589. bbox_nums = batch_pred['bbox_num']
  590. det_res = []
  591. k = 0
  592. for i in range(len(bbox_nums)):
  593. det_nums = bbox_nums[i]
  594. for j in range(det_nums):
  595. dt = bboxes[k]
  596. k = k + 1
  597. num_id, score, xmin, ymin, xmax, ymax = dt.tolist()
  598. if int(num_id) < 0:
  599. continue
  600. category = self.labels[int(num_id)]
  601. w = xmax - xmin
  602. h = ymax - ymin
  603. bbox = [xmin, ymin, w, h]
  604. dt_res = {
  605. 'category_id': int(num_id),
  606. 'category': category,
  607. 'bbox': bbox,
  608. 'score': score
  609. }
  610. det_res.append(dt_res)
  611. infer_result['bbox'] = det_res
  612. if 'mask' in batch_pred:
  613. masks = batch_pred['mask']
  614. bboxes = batch_pred['bbox']
  615. mask_nums = batch_pred['bbox_num']
  616. seg_res = []
  617. k = 0
  618. for i in range(len(mask_nums)):
  619. det_nums = mask_nums[i]
  620. for j in range(det_nums):
  621. mask = masks[k].astype(np.uint8)
  622. score = float(bboxes[k][1])
  623. label = int(bboxes[k][0])
  624. k = k + 1
  625. if label == -1:
  626. continue
  627. category = self.labels[int(label)]
  628. sg_res = {
  629. 'category_id': int(label),
  630. 'category': category,
  631. 'mask': mask.astype('uint8'),
  632. 'score': score
  633. }
  634. seg_res.append(sg_res)
  635. infer_result['mask'] = seg_res
  636. bbox_num = batch_pred['bbox_num']
  637. results = []
  638. start = 0
  639. for num in bbox_num:
  640. end = start + num
  641. curr_res = infer_result['bbox'][start:end]
  642. if 'mask' in infer_result:
  643. mask_res = infer_result['mask'][start:end]
  644. for box, mask in zip(curr_res, mask_res):
  645. box.update(mask)
  646. results.append(curr_res)
  647. start = end
  648. return results
  649. def _check_transforms(self, transforms, mode):
  650. super()._check_transforms(transforms, mode)
  651. if not isinstance(transforms.arrange,
  652. paddlers.transforms.ArrangeDetector):
  653. raise TypeError(
  654. "`transforms.arrange` must be an ArrangeDetector object.")
  655. class PicoDet(BaseDetector):
  656. def __init__(self,
  657. num_classes=80,
  658. backbone='ESNet_m',
  659. nms_score_threshold=.025,
  660. nms_topk=1000,
  661. nms_keep_topk=100,
  662. nms_iou_threshold=.6,
  663. **params):
  664. self.init_params = locals()
  665. if backbone not in {
  666. 'ESNet_s', 'ESNet_m', 'ESNet_l', 'LCNet', 'MobileNetV3',
  667. 'ResNet18_vd'
  668. }:
  669. raise ValueError(
  670. "backbone: {} is not supported. Please choose one of "
  671. "{'ESNet_s', 'ESNet_m', 'ESNet_l', 'LCNet', 'MobileNetV3', 'ResNet18_vd'}.".
  672. format(backbone))
  673. self.backbone_name = backbone
  674. if params.get('with_net', True):
  675. if backbone == 'ESNet_s':
  676. backbone = self._get_backbone(
  677. 'ESNet',
  678. scale=.75,
  679. feature_maps=[4, 11, 14],
  680. act="hard_swish",
  681. channel_ratio=[
  682. 0.875, 0.5, 0.5, 0.5, 0.625, 0.5, 0.625, 0.5, 0.5, 0.5,
  683. 0.5, 0.5, 0.5
  684. ])
  685. neck_out_channels = 96
  686. head_num_convs = 2
  687. elif backbone == 'ESNet_m':
  688. backbone = self._get_backbone(
  689. 'ESNet',
  690. scale=1.0,
  691. feature_maps=[4, 11, 14],
  692. act="hard_swish",
  693. channel_ratio=[
  694. 0.875, 0.5, 1.0, 0.625, 0.5, 0.75, 0.625, 0.625, 0.5,
  695. 0.625, 1.0, 0.625, 0.75
  696. ])
  697. neck_out_channels = 128
  698. head_num_convs = 4
  699. elif backbone == 'ESNet_l':
  700. backbone = self._get_backbone(
  701. 'ESNet',
  702. scale=1.25,
  703. feature_maps=[4, 11, 14],
  704. act="hard_swish",
  705. channel_ratio=[
  706. 0.875, 0.5, 1.0, 0.625, 0.5, 0.75, 0.625, 0.625, 0.5,
  707. 0.625, 1.0, 0.625, 0.75
  708. ])
  709. neck_out_channels = 160
  710. head_num_convs = 4
  711. elif backbone == 'LCNet':
  712. backbone = self._get_backbone(
  713. 'LCNet', scale=1.5, feature_maps=[3, 4, 5])
  714. neck_out_channels = 128
  715. head_num_convs = 4
  716. elif backbone == 'MobileNetV3':
  717. backbone = self._get_backbone(
  718. 'MobileNetV3',
  719. scale=1.0,
  720. with_extra_blocks=False,
  721. extra_block_filters=[],
  722. feature_maps=[7, 13, 16])
  723. neck_out_channels = 128
  724. head_num_convs = 4
  725. else:
  726. backbone = self._get_backbone(
  727. 'ResNet',
  728. depth=18,
  729. variant='d',
  730. return_idx=[1, 2, 3],
  731. freeze_at=-1,
  732. freeze_norm=False,
  733. norm_decay=0.)
  734. neck_out_channels = 128
  735. head_num_convs = 4
  736. neck = ppdet.modeling.CSPPAN(
  737. in_channels=[i.channels for i in backbone.out_shape],
  738. out_channels=neck_out_channels,
  739. num_features=4,
  740. num_csp_blocks=1,
  741. use_depthwise=True)
  742. head_conv_feat = ppdet.modeling.PicoFeat(
  743. feat_in=neck_out_channels,
  744. feat_out=neck_out_channels,
  745. num_fpn_stride=4,
  746. num_convs=head_num_convs,
  747. norm_type='bn',
  748. share_cls_reg=True, )
  749. loss_class = ppdet.modeling.VarifocalLoss(
  750. use_sigmoid=True, iou_weighted=True, loss_weight=1.0)
  751. loss_dfl = ppdet.modeling.DistributionFocalLoss(loss_weight=.25)
  752. loss_bbox = ppdet.modeling.GIoULoss(loss_weight=2.0)
  753. assigner = ppdet.modeling.SimOTAAssigner(
  754. candidate_topk=10, iou_weight=6, num_classes=num_classes)
  755. nms = ppdet.modeling.MultiClassNMS(
  756. nms_top_k=nms_topk,
  757. keep_top_k=nms_keep_topk,
  758. score_threshold=nms_score_threshold,
  759. nms_threshold=nms_iou_threshold)
  760. head = ppdet.modeling.PicoHead(
  761. conv_feat=head_conv_feat,
  762. num_classes=num_classes,
  763. fpn_stride=[8, 16, 32, 64],
  764. prior_prob=0.01,
  765. reg_max=7,
  766. cell_offset=.5,
  767. loss_class=loss_class,
  768. loss_dfl=loss_dfl,
  769. loss_bbox=loss_bbox,
  770. assigner=assigner,
  771. feat_in_chan=neck_out_channels,
  772. nms=nms)
  773. params.update({
  774. 'backbone': backbone,
  775. 'neck': neck,
  776. 'head': head,
  777. })
  778. super(PicoDet, self).__init__(
  779. model_name='PicoDet', num_classes=num_classes, **params)
  780. def _compose_batch_transform(self, transforms, mode='train'):
  781. default_batch_transforms = [_BatchPad(pad_to_stride=32)]
  782. if mode == 'eval':
  783. collate_batch = True
  784. else:
  785. collate_batch = False
  786. custom_batch_transforms = []
  787. for i, op in enumerate(transforms.transforms):
  788. if isinstance(op, (BatchRandomResize, BatchRandomResizeByShort)):
  789. if mode != 'train':
  790. raise ValueError(
  791. "{} cannot be present in the {} transforms.".format(
  792. op.__class__.__name__, mode) +
  793. "Please check the {} transforms.".format(mode))
  794. custom_batch_transforms.insert(0, copy.deepcopy(op))
  795. batch_transforms = BatchCompose(
  796. custom_batch_transforms + default_batch_transforms,
  797. collate_batch=collate_batch)
  798. return batch_transforms
  799. def _fix_transforms_shape(self, image_shape):
  800. if getattr(self, 'test_transforms', None):
  801. has_resize_op = False
  802. resize_op_idx = -1
  803. normalize_op_idx = len(self.test_transforms.transforms)
  804. for idx, op in enumerate(self.test_transforms.transforms):
  805. name = op.__class__.__name__
  806. if name == 'Resize':
  807. has_resize_op = True
  808. resize_op_idx = idx
  809. if name == 'Normalize':
  810. normalize_op_idx = idx
  811. if not has_resize_op:
  812. self.test_transforms.transforms.insert(
  813. normalize_op_idx,
  814. Resize(
  815. target_size=image_shape, interp='CUBIC'))
  816. else:
  817. self.test_transforms.transforms[
  818. resize_op_idx].target_size = image_shape
  819. def _get_test_inputs(self, image_shape):
  820. if image_shape is not None:
  821. image_shape = self._check_image_shape(image_shape)
  822. self._fix_transforms_shape(image_shape[-2:])
  823. else:
  824. image_shape = [None, 3, 320, 320]
  825. if getattr(self, 'test_transforms', None):
  826. for idx, op in enumerate(self.test_transforms.transforms):
  827. name = op.__class__.__name__
  828. if name == 'Resize':
  829. image_shape = [None, 3] + list(
  830. self.test_transforms.transforms[idx].target_size)
  831. logging.warning(
  832. '[Important!!!] When exporting inference model for {}, '
  833. 'if fixed_input_shape is not set, it will be forcibly set to {}. '
  834. 'Please ensure image shape after transforms is {}, if not, '
  835. 'fixed_input_shape should be specified manually.'
  836. .format(self.__class__.__name__, image_shape, image_shape[1:]))
  837. self.fixed_input_shape = image_shape
  838. return self._define_input_spec(image_shape)
  839. def _pre_train(self, in_args):
  840. optimizer = in_args['optimizer']
  841. if optimizer is None:
  842. num_steps_each_epoch = len(in_args['train_dataset']) // in_args[
  843. 'train_batch_size']
  844. optimizer = self.default_optimizer(
  845. parameters=self.net.parameters(),
  846. learning_rate=in_args['learning_rate'],
  847. warmup_steps=in_args['warmup_steps'],
  848. warmup_start_lr=in_args['warmup_start_lr'],
  849. lr_decay_epochs=in_args['lr_decay_epochs'],
  850. lr_decay_gamma=in_args['lr_decay_gamma'],
  851. num_steps_each_epoch=in_args['num_steps_each_epoch'],
  852. reg_coeff=4e-05,
  853. scheduler='Cosine',
  854. num_epochs=in_args['num_epochs'])
  855. in_args['optimizer'] = optimizer
  856. return in_args
  857. class YOLOv3(BaseDetector):
  858. def __init__(self,
  859. num_classes=80,
  860. backbone='MobileNetV1',
  861. anchors=[[10, 13], [16, 30], [33, 23], [30, 61], [62, 45],
  862. [59, 119], [116, 90], [156, 198], [373, 326]],
  863. anchor_masks=[[6, 7, 8], [3, 4, 5], [0, 1, 2]],
  864. ignore_threshold=0.7,
  865. nms_score_threshold=0.01,
  866. nms_topk=1000,
  867. nms_keep_topk=100,
  868. nms_iou_threshold=0.45,
  869. label_smooth=False,
  870. **params):
  871. self.init_params = locals()
  872. if backbone not in {
  873. 'MobileNetV1', 'MobileNetV1_ssld', 'MobileNetV3',
  874. 'MobileNetV3_ssld', 'DarkNet53', 'ResNet50_vd_dcn', 'ResNet34'
  875. }:
  876. raise ValueError(
  877. "backbone: {} is not supported. Please choose one of "
  878. "{'MobileNetV1', 'MobileNetV1_ssld', 'MobileNetV3', 'MobileNetV3_ssld', 'DarkNet53', "
  879. "'ResNet50_vd_dcn', 'ResNet34'}.".format(backbone))
  880. self.backbone_name = backbone
  881. if params.get('with_net', True):
  882. if paddlers.env_info['place'] == 'gpu' and paddlers.env_info[
  883. 'num'] > 1 and not os.environ.get('PADDLERS_EXPORT_STAGE'):
  884. norm_type = 'sync_bn'
  885. else:
  886. norm_type = 'bn'
  887. if 'MobileNetV1' in backbone:
  888. norm_type = 'bn'
  889. backbone = self._get_backbone('MobileNet', norm_type=norm_type)
  890. elif 'MobileNetV3' in backbone:
  891. backbone = self._get_backbone(
  892. 'MobileNetV3',
  893. norm_type=norm_type,
  894. feature_maps=[7, 13, 16])
  895. elif backbone == 'ResNet50_vd_dcn':
  896. backbone = self._get_backbone(
  897. 'ResNet',
  898. norm_type=norm_type,
  899. variant='d',
  900. return_idx=[1, 2, 3],
  901. dcn_v2_stages=[3],
  902. freeze_at=-1,
  903. freeze_norm=False)
  904. elif backbone == 'ResNet34':
  905. backbone = self._get_backbone(
  906. 'ResNet',
  907. depth=34,
  908. norm_type=norm_type,
  909. return_idx=[1, 2, 3],
  910. freeze_at=-1,
  911. freeze_norm=False,
  912. norm_decay=0.)
  913. else:
  914. backbone = self._get_backbone('DarkNet', norm_type=norm_type)
  915. neck = ppdet.modeling.YOLOv3FPN(
  916. norm_type=norm_type,
  917. in_channels=[i.channels for i in backbone.out_shape])
  918. loss = ppdet.modeling.YOLOv3Loss(
  919. num_classes=num_classes,
  920. ignore_thresh=ignore_threshold,
  921. label_smooth=label_smooth)
  922. yolo_head = ppdet.modeling.YOLOv3Head(
  923. in_channels=[i.channels for i in neck.out_shape],
  924. anchors=anchors,
  925. anchor_masks=anchor_masks,
  926. num_classes=num_classes,
  927. loss=loss)
  928. post_process = ppdet.modeling.BBoxPostProcess(
  929. decode=ppdet.modeling.YOLOBox(num_classes=num_classes),
  930. nms=ppdet.modeling.MultiClassNMS(
  931. score_threshold=nms_score_threshold,
  932. nms_top_k=nms_topk,
  933. keep_top_k=nms_keep_topk,
  934. nms_threshold=nms_iou_threshold))
  935. params.update({
  936. 'backbone': backbone,
  937. 'neck': neck,
  938. 'yolo_head': yolo_head,
  939. 'post_process': post_process
  940. })
  941. super(YOLOv3, self).__init__(
  942. model_name='YOLOv3', num_classes=num_classes, **params)
  943. self.anchors = anchors
  944. self.anchor_masks = anchor_masks
  945. def _compose_batch_transform(self, transforms, mode='train'):
  946. if mode == 'train':
  947. default_batch_transforms = [
  948. _BatchPad(pad_to_stride=-1), _NormalizeBox(),
  949. _PadBox(getattr(self, 'num_max_boxes', 50)), _BboxXYXY2XYWH(),
  950. _Gt2YoloTarget(
  951. anchor_masks=self.anchor_masks,
  952. anchors=self.anchors,
  953. downsample_ratios=getattr(self, 'downsample_ratios',
  954. [32, 16, 8]),
  955. num_classes=self.num_classes)
  956. ]
  957. else:
  958. default_batch_transforms = [_BatchPad(pad_to_stride=-1)]
  959. if mode == 'eval' and self.metric == 'voc':
  960. collate_batch = False
  961. else:
  962. collate_batch = True
  963. custom_batch_transforms = []
  964. for i, op in enumerate(transforms.transforms):
  965. if isinstance(op, (BatchRandomResize, BatchRandomResizeByShort)):
  966. if mode != 'train':
  967. raise ValueError(
  968. "{} cannot be present in the {} transforms. ".format(
  969. op.__class__.__name__, mode) +
  970. "Please check the {} transforms.".format(mode))
  971. custom_batch_transforms.insert(0, copy.deepcopy(op))
  972. batch_transforms = BatchCompose(
  973. custom_batch_transforms + default_batch_transforms,
  974. collate_batch=collate_batch)
  975. return batch_transforms
  976. def _fix_transforms_shape(self, image_shape):
  977. if getattr(self, 'test_transforms', None):
  978. has_resize_op = False
  979. resize_op_idx = -1
  980. normalize_op_idx = len(self.test_transforms.transforms)
  981. for idx, op in enumerate(self.test_transforms.transforms):
  982. name = op.__class__.__name__
  983. if name == 'Resize':
  984. has_resize_op = True
  985. resize_op_idx = idx
  986. if name == 'Normalize':
  987. normalize_op_idx = idx
  988. if not has_resize_op:
  989. self.test_transforms.transforms.insert(
  990. normalize_op_idx,
  991. Resize(
  992. target_size=image_shape, interp='CUBIC'))
  993. else:
  994. self.test_transforms.transforms[
  995. resize_op_idx].target_size = image_shape
  996. class FasterRCNN(BaseDetector):
  997. def __init__(self,
  998. num_classes=80,
  999. backbone='ResNet50',
  1000. with_fpn=True,
  1001. with_dcn=False,
  1002. aspect_ratios=[0.5, 1.0, 2.0],
  1003. anchor_sizes=[[32], [64], [128], [256], [512]],
  1004. keep_top_k=100,
  1005. nms_threshold=0.5,
  1006. score_threshold=0.05,
  1007. fpn_num_channels=256,
  1008. rpn_batch_size_per_im=256,
  1009. rpn_fg_fraction=0.5,
  1010. test_pre_nms_top_n=None,
  1011. test_post_nms_top_n=1000,
  1012. **params):
  1013. self.init_params = locals()
  1014. if backbone not in {
  1015. 'ResNet50', 'ResNet50_vd', 'ResNet50_vd_ssld', 'ResNet34',
  1016. 'ResNet34_vd', 'ResNet101', 'ResNet101_vd', 'HRNet_W18'
  1017. }:
  1018. raise ValueError(
  1019. "backbone: {} is not supported. Please choose one of "
  1020. "{'ResNet50', 'ResNet50_vd', 'ResNet50_vd_ssld', 'ResNet34', 'ResNet34_vd', "
  1021. "'ResNet101', 'ResNet101_vd', 'HRNet_W18'}.".format(backbone))
  1022. self.backbone_name = backbone
  1023. if params.get('with_net', True):
  1024. dcn_v2_stages = [1, 2, 3] if with_dcn else [-1]
  1025. if backbone == 'HRNet_W18':
  1026. if not with_fpn:
  1027. logging.warning(
  1028. "Backbone {} should be used along with fpn enabled, 'with_fpn' is forcibly set to True".
  1029. format(backbone))
  1030. with_fpn = True
  1031. if with_dcn:
  1032. logging.warning(
  1033. "Backbone {} should be used along with dcn disabled, 'with_dcn' is forcibly set to False".
  1034. format(backbone))
  1035. backbone = self._get_backbone(
  1036. 'HRNet', width=18, freeze_at=0, return_idx=[0, 1, 2, 3])
  1037. elif backbone == 'ResNet50_vd_ssld':
  1038. if not with_fpn:
  1039. logging.warning(
  1040. "Backbone {} should be used along with fpn enabled, 'with_fpn' is forcibly set to True".
  1041. format(backbone))
  1042. with_fpn = True
  1043. backbone = self._get_backbone(
  1044. 'ResNet',
  1045. variant='d',
  1046. norm_type='bn',
  1047. freeze_at=0,
  1048. return_idx=[0, 1, 2, 3],
  1049. num_stages=4,
  1050. lr_mult_list=[0.05, 0.05, 0.1, 0.15],
  1051. dcn_v2_stages=dcn_v2_stages)
  1052. elif 'ResNet50' in backbone:
  1053. if with_fpn:
  1054. backbone = self._get_backbone(
  1055. 'ResNet',
  1056. variant='d' if '_vd' in backbone else 'b',
  1057. norm_type='bn',
  1058. freeze_at=0,
  1059. return_idx=[0, 1, 2, 3],
  1060. num_stages=4,
  1061. dcn_v2_stages=dcn_v2_stages)
  1062. else:
  1063. if with_dcn:
  1064. logging.warning(
  1065. "Backbone {} without fpn should be used along with dcn disabled, 'with_dcn' is forcibly set to False".
  1066. format(backbone))
  1067. backbone = self._get_backbone(
  1068. 'ResNet',
  1069. variant='d' if '_vd' in backbone else 'b',
  1070. norm_type='bn',
  1071. freeze_at=0,
  1072. return_idx=[2],
  1073. num_stages=3)
  1074. elif 'ResNet34' in backbone:
  1075. if not with_fpn:
  1076. logging.warning(
  1077. "Backbone {} should be used along with fpn enabled, 'with_fpn' is forcibly set to True".
  1078. format(backbone))
  1079. with_fpn = True
  1080. backbone = self._get_backbone(
  1081. 'ResNet',
  1082. depth=34,
  1083. variant='d' if 'vd' in backbone else 'b',
  1084. norm_type='bn',
  1085. freeze_at=0,
  1086. return_idx=[0, 1, 2, 3],
  1087. num_stages=4,
  1088. dcn_v2_stages=dcn_v2_stages)
  1089. else:
  1090. if not with_fpn:
  1091. logging.warning(
  1092. "Backbone {} should be used along with fpn enabled, 'with_fpn' is forcibly set to True".
  1093. format(backbone))
  1094. with_fpn = True
  1095. backbone = self._get_backbone(
  1096. 'ResNet',
  1097. depth=101,
  1098. variant='d' if 'vd' in backbone else 'b',
  1099. norm_type='bn',
  1100. freeze_at=0,
  1101. return_idx=[0, 1, 2, 3],
  1102. num_stages=4,
  1103. dcn_v2_stages=dcn_v2_stages)
  1104. rpn_in_channel = backbone.out_shape[0].channels
  1105. if with_fpn:
  1106. self.backbone_name = self.backbone_name + '_fpn'
  1107. if 'HRNet' in self.backbone_name:
  1108. neck = ppdet.modeling.HRFPN(
  1109. in_channels=[i.channels for i in backbone.out_shape],
  1110. out_channel=fpn_num_channels,
  1111. spatial_scales=[
  1112. 1.0 / i.stride for i in backbone.out_shape
  1113. ],
  1114. share_conv=False)
  1115. else:
  1116. neck = ppdet.modeling.FPN(
  1117. in_channels=[i.channels for i in backbone.out_shape],
  1118. out_channel=fpn_num_channels,
  1119. spatial_scales=[
  1120. 1.0 / i.stride for i in backbone.out_shape
  1121. ])
  1122. rpn_in_channel = neck.out_shape[0].channels
  1123. anchor_generator_cfg = {
  1124. 'aspect_ratios': aspect_ratios,
  1125. 'anchor_sizes': anchor_sizes,
  1126. 'strides': [4, 8, 16, 32, 64]
  1127. }
  1128. train_proposal_cfg = {
  1129. 'min_size': 0.0,
  1130. 'nms_thresh': .7,
  1131. 'pre_nms_top_n': 2000,
  1132. 'post_nms_top_n': 1000,
  1133. 'topk_after_collect': True
  1134. }
  1135. test_proposal_cfg = {
  1136. 'min_size': 0.0,
  1137. 'nms_thresh': .7,
  1138. 'pre_nms_top_n': 1000
  1139. if test_pre_nms_top_n is None else test_pre_nms_top_n,
  1140. 'post_nms_top_n': test_post_nms_top_n
  1141. }
  1142. head = ppdet.modeling.TwoFCHead(
  1143. in_channel=neck.out_shape[0].channels, out_channel=1024)
  1144. roi_extractor_cfg = {
  1145. 'resolution': 7,
  1146. 'spatial_scale': [1. / i.stride for i in neck.out_shape],
  1147. 'sampling_ratio': 0,
  1148. 'aligned': True
  1149. }
  1150. with_pool = False
  1151. else:
  1152. neck = None
  1153. anchor_generator_cfg = {
  1154. 'aspect_ratios': aspect_ratios,
  1155. 'anchor_sizes': anchor_sizes,
  1156. 'strides': [16]
  1157. }
  1158. train_proposal_cfg = {
  1159. 'min_size': 0.0,
  1160. 'nms_thresh': .7,
  1161. 'pre_nms_top_n': 12000,
  1162. 'post_nms_top_n': 2000,
  1163. 'topk_after_collect': False
  1164. }
  1165. test_proposal_cfg = {
  1166. 'min_size': 0.0,
  1167. 'nms_thresh': .7,
  1168. 'pre_nms_top_n': 6000
  1169. if test_pre_nms_top_n is None else test_pre_nms_top_n,
  1170. 'post_nms_top_n': test_post_nms_top_n
  1171. }
  1172. head = ppdet.modeling.Res5Head()
  1173. roi_extractor_cfg = {
  1174. 'resolution': 14,
  1175. 'spatial_scale':
  1176. [1. / i.stride for i in backbone.out_shape],
  1177. 'sampling_ratio': 0,
  1178. 'aligned': True
  1179. }
  1180. with_pool = True
  1181. rpn_target_assign_cfg = {
  1182. 'batch_size_per_im': rpn_batch_size_per_im,
  1183. 'fg_fraction': rpn_fg_fraction,
  1184. 'negative_overlap': .3,
  1185. 'positive_overlap': .7,
  1186. 'use_random': True
  1187. }
  1188. rpn_head = ppdet.modeling.RPNHead(
  1189. anchor_generator=anchor_generator_cfg,
  1190. rpn_target_assign=rpn_target_assign_cfg,
  1191. train_proposal=train_proposal_cfg,
  1192. test_proposal=test_proposal_cfg,
  1193. in_channel=rpn_in_channel)
  1194. bbox_assigner = BBoxAssigner(num_classes=num_classes)
  1195. bbox_head = ppdet.modeling.BBoxHead(
  1196. head=head,
  1197. in_channel=head.out_shape[0].channels,
  1198. roi_extractor=roi_extractor_cfg,
  1199. with_pool=with_pool,
  1200. bbox_assigner=bbox_assigner,
  1201. num_classes=num_classes)
  1202. bbox_post_process = ppdet.modeling.BBoxPostProcess(
  1203. num_classes=num_classes,
  1204. decode=ppdet.modeling.RCNNBox(num_classes=num_classes),
  1205. nms=ppdet.modeling.MultiClassNMS(
  1206. score_threshold=score_threshold,
  1207. keep_top_k=keep_top_k,
  1208. nms_threshold=nms_threshold))
  1209. params.update({
  1210. 'backbone': backbone,
  1211. 'neck': neck,
  1212. 'rpn_head': rpn_head,
  1213. 'bbox_head': bbox_head,
  1214. 'bbox_post_process': bbox_post_process
  1215. })
  1216. else:
  1217. if backbone not in {'ResNet50', 'ResNet50_vd'}:
  1218. with_fpn = True
  1219. self.with_fpn = with_fpn
  1220. super(FasterRCNN, self).__init__(
  1221. model_name='FasterRCNN', num_classes=num_classes, **params)
  1222. def _pre_train(self, in_args):
  1223. train_dataset = in_args['train_dataset']
  1224. if train_dataset.pos_num < len(train_dataset.file_list):
  1225. # In-place modification
  1226. train_dataset.num_workers = 0
  1227. return in_args
  1228. def _compose_batch_transform(self, transforms, mode='train'):
  1229. if mode == 'train':
  1230. default_batch_transforms = [
  1231. _BatchPad(pad_to_stride=32 if self.with_fpn else -1)
  1232. ]
  1233. else:
  1234. default_batch_transforms = [
  1235. _BatchPad(pad_to_stride=32 if self.with_fpn else -1)
  1236. ]
  1237. custom_batch_transforms = []
  1238. for i, op in enumerate(transforms.transforms):
  1239. if isinstance(op, (BatchRandomResize, BatchRandomResizeByShort)):
  1240. if mode != 'train':
  1241. raise ValueError(
  1242. "{} cannot be present in the {} transforms. ".format(
  1243. op.__class__.__name__, mode) +
  1244. "Please check the {} transforms.".format(mode))
  1245. custom_batch_transforms.insert(0, copy.deepcopy(op))
  1246. batch_transforms = BatchCompose(
  1247. custom_batch_transforms + default_batch_transforms,
  1248. collate_batch=False)
  1249. return batch_transforms
  1250. def _fix_transforms_shape(self, image_shape):
  1251. if getattr(self, 'test_transforms', None):
  1252. has_resize_op = False
  1253. resize_op_idx = -1
  1254. normalize_op_idx = len(self.test_transforms.transforms)
  1255. for idx, op in enumerate(self.test_transforms.transforms):
  1256. name = op.__class__.__name__
  1257. if name == 'ResizeByShort':
  1258. has_resize_op = True
  1259. resize_op_idx = idx
  1260. if name == 'Normalize':
  1261. normalize_op_idx = idx
  1262. if not has_resize_op:
  1263. self.test_transforms.transforms.insert(
  1264. normalize_op_idx,
  1265. Resize(
  1266. target_size=image_shape,
  1267. keep_ratio=True,
  1268. interp='CUBIC'))
  1269. else:
  1270. self.test_transforms.transforms[resize_op_idx] = Resize(
  1271. target_size=image_shape, keep_ratio=True, interp='CUBIC')
  1272. self.test_transforms.transforms.append(
  1273. Pad(im_padding_value=[0., 0., 0.]))
  1274. def _get_test_inputs(self, image_shape):
  1275. if image_shape is not None:
  1276. image_shape = self._check_image_shape(image_shape)
  1277. self._fix_transforms_shape(image_shape[-2:])
  1278. else:
  1279. image_shape = [None, 3, -1, -1]
  1280. if self.with_fpn:
  1281. self.test_transforms.transforms.append(
  1282. Pad(im_padding_value=[0., 0., 0.]))
  1283. self.fixed_input_shape = image_shape
  1284. return self._define_input_spec(image_shape)
  1285. class PPYOLO(YOLOv3):
  1286. def __init__(self,
  1287. num_classes=80,
  1288. backbone='ResNet50_vd_dcn',
  1289. anchors=None,
  1290. anchor_masks=None,
  1291. use_coord_conv=True,
  1292. use_iou_aware=True,
  1293. use_spp=True,
  1294. use_drop_block=True,
  1295. scale_x_y=1.05,
  1296. ignore_threshold=0.7,
  1297. label_smooth=False,
  1298. use_iou_loss=True,
  1299. use_matrix_nms=True,
  1300. nms_score_threshold=0.01,
  1301. nms_topk=-1,
  1302. nms_keep_topk=100,
  1303. nms_iou_threshold=0.45,
  1304. **params):
  1305. self.init_params = locals()
  1306. if backbone not in {
  1307. 'ResNet50_vd_dcn', 'ResNet18_vd', 'MobileNetV3_large',
  1308. 'MobileNetV3_small'
  1309. }:
  1310. raise ValueError(
  1311. "backbone: {} is not supported. Please choose one of "
  1312. "{'ResNet50_vd_dcn', 'ResNet18_vd', 'MobileNetV3_large', 'MobileNetV3_small'}.".
  1313. format(backbone))
  1314. self.backbone_name = backbone
  1315. self.downsample_ratios = [
  1316. 32, 16, 8
  1317. ] if backbone == 'ResNet50_vd_dcn' else [32, 16]
  1318. if params.get('with_net', True):
  1319. if paddlers.env_info['place'] == 'gpu' and paddlers.env_info[
  1320. 'num'] > 1 and not os.environ.get('PADDLERS_EXPORT_STAGE'):
  1321. norm_type = 'sync_bn'
  1322. else:
  1323. norm_type = 'bn'
  1324. if anchors is None and anchor_masks is None:
  1325. if 'MobileNetV3' in backbone:
  1326. anchors = [[11, 18], [34, 47], [51, 126], [115, 71],
  1327. [120, 195], [254, 235]]
  1328. anchor_masks = [[3, 4, 5], [0, 1, 2]]
  1329. elif backbone == 'ResNet50_vd_dcn':
  1330. anchors = [[10, 13], [16, 30], [33, 23], [30, 61],
  1331. [62, 45], [59, 119], [116, 90], [156, 198],
  1332. [373, 326]]
  1333. anchor_masks = [[6, 7, 8], [3, 4, 5], [0, 1, 2]]
  1334. else:
  1335. anchors = [[10, 14], [23, 27], [37, 58], [81, 82],
  1336. [135, 169], [344, 319]]
  1337. anchor_masks = [[3, 4, 5], [0, 1, 2]]
  1338. elif anchors is None or anchor_masks is None:
  1339. raise ValueError("Please define both anchors and anchor_masks.")
  1340. if backbone == 'ResNet50_vd_dcn':
  1341. backbone = self._get_backbone(
  1342. 'ResNet',
  1343. variant='d',
  1344. norm_type=norm_type,
  1345. return_idx=[1, 2, 3],
  1346. dcn_v2_stages=[3],
  1347. freeze_at=-1,
  1348. freeze_norm=False,
  1349. norm_decay=0.)
  1350. elif backbone == 'ResNet18_vd':
  1351. backbone = self._get_backbone(
  1352. 'ResNet',
  1353. depth=18,
  1354. variant='d',
  1355. norm_type=norm_type,
  1356. return_idx=[2, 3],
  1357. freeze_at=-1,
  1358. freeze_norm=False,
  1359. norm_decay=0.)
  1360. elif backbone == 'MobileNetV3_large':
  1361. backbone = self._get_backbone(
  1362. 'MobileNetV3',
  1363. model_name='large',
  1364. norm_type=norm_type,
  1365. scale=1,
  1366. with_extra_blocks=False,
  1367. extra_block_filters=[],
  1368. feature_maps=[13, 16])
  1369. elif backbone == 'MobileNetV3_small':
  1370. backbone = self._get_backbone(
  1371. 'MobileNetV3',
  1372. model_name='small',
  1373. norm_type=norm_type,
  1374. scale=1,
  1375. with_extra_blocks=False,
  1376. extra_block_filters=[],
  1377. feature_maps=[9, 12])
  1378. neck = ppdet.modeling.PPYOLOFPN(
  1379. norm_type=norm_type,
  1380. in_channels=[i.channels for i in backbone.out_shape],
  1381. coord_conv=use_coord_conv,
  1382. drop_block=use_drop_block,
  1383. spp=use_spp,
  1384. conv_block_num=0
  1385. if ('MobileNetV3' in self.backbone_name or
  1386. self.backbone_name == 'ResNet18_vd') else 2)
  1387. loss = ppdet.modeling.YOLOv3Loss(
  1388. num_classes=num_classes,
  1389. ignore_thresh=ignore_threshold,
  1390. downsample=self.downsample_ratios,
  1391. label_smooth=label_smooth,
  1392. scale_x_y=scale_x_y,
  1393. iou_loss=ppdet.modeling.IouLoss(
  1394. loss_weight=2.5, loss_square=True)
  1395. if use_iou_loss else None,
  1396. iou_aware_loss=ppdet.modeling.IouAwareLoss(loss_weight=1.0)
  1397. if use_iou_aware else None)
  1398. yolo_head = ppdet.modeling.YOLOv3Head(
  1399. in_channels=[i.channels for i in neck.out_shape],
  1400. anchors=anchors,
  1401. anchor_masks=anchor_masks,
  1402. num_classes=num_classes,
  1403. loss=loss,
  1404. iou_aware=use_iou_aware)
  1405. if use_matrix_nms:
  1406. nms = ppdet.modeling.MatrixNMS(
  1407. keep_top_k=nms_keep_topk,
  1408. score_threshold=nms_score_threshold,
  1409. post_threshold=.05
  1410. if 'MobileNetV3' in self.backbone_name else .01,
  1411. nms_top_k=nms_topk,
  1412. background_label=-1)
  1413. else:
  1414. nms = ppdet.modeling.MultiClassNMS(
  1415. score_threshold=nms_score_threshold,
  1416. nms_top_k=nms_topk,
  1417. keep_top_k=nms_keep_topk,
  1418. nms_threshold=nms_iou_threshold)
  1419. post_process = ppdet.modeling.BBoxPostProcess(
  1420. decode=ppdet.modeling.YOLOBox(
  1421. num_classes=num_classes,
  1422. conf_thresh=.005
  1423. if 'MobileNetV3' in self.backbone_name else .01,
  1424. scale_x_y=scale_x_y),
  1425. nms=nms)
  1426. params.update({
  1427. 'backbone': backbone,
  1428. 'neck': neck,
  1429. 'yolo_head': yolo_head,
  1430. 'post_process': post_process
  1431. })
  1432. super(YOLOv3, self).__init__(
  1433. model_name='YOLOv3', num_classes=num_classes, **params)
  1434. self.anchors = anchors
  1435. self.anchor_masks = anchor_masks
  1436. self.model_name = 'PPYOLO'
  1437. def _get_test_inputs(self, image_shape):
  1438. if image_shape is not None:
  1439. image_shape = self._check_image_shape(image_shape)
  1440. self._fix_transforms_shape(image_shape[-2:])
  1441. else:
  1442. image_shape = [None, 3, 608, 608]
  1443. if getattr(self, 'test_transforms', None):
  1444. for idx, op in enumerate(self.test_transforms.transforms):
  1445. name = op.__class__.__name__
  1446. if name == 'Resize':
  1447. image_shape = [None, 3] + list(
  1448. self.test_transforms.transforms[idx].target_size)
  1449. logging.warning(
  1450. '[Important!!!] When exporting inference model for {}, '
  1451. 'if fixed_input_shape is not set, it will be forcibly set to {}. '
  1452. 'Please ensure image shape after transforms is {}, if not, '
  1453. 'fixed_input_shape should be specified manually.'
  1454. .format(self.__class__.__name__, image_shape, image_shape[1:]))
  1455. self.fixed_input_shape = image_shape
  1456. return self._define_input_spec(image_shape)
  1457. class PPYOLOTiny(YOLOv3):
  1458. def __init__(self,
  1459. num_classes=80,
  1460. backbone='MobileNetV3',
  1461. anchors=[[10, 15], [24, 36], [72, 42], [35, 87], [102, 96],
  1462. [60, 170], [220, 125], [128, 222], [264, 266]],
  1463. anchor_masks=[[6, 7, 8], [3, 4, 5], [0, 1, 2]],
  1464. use_iou_aware=False,
  1465. use_spp=True,
  1466. use_drop_block=True,
  1467. scale_x_y=1.05,
  1468. ignore_threshold=0.5,
  1469. label_smooth=False,
  1470. use_iou_loss=True,
  1471. use_matrix_nms=False,
  1472. nms_score_threshold=0.005,
  1473. nms_topk=1000,
  1474. nms_keep_topk=100,
  1475. nms_iou_threshold=0.45,
  1476. **params):
  1477. self.init_params = locals()
  1478. if backbone != 'MobileNetV3':
  1479. logging.warning("PPYOLOTiny only supports MobileNetV3 as backbone. "
  1480. "Backbone is forcibly set to MobileNetV3.")
  1481. self.backbone_name = 'MobileNetV3'
  1482. self.downsample_ratios = [32, 16, 8]
  1483. if params.get('with_net', True):
  1484. if paddlers.env_info['place'] == 'gpu' and paddlers.env_info[
  1485. 'num'] > 1 and not os.environ.get('PADDLERS_EXPORT_STAGE'):
  1486. norm_type = 'sync_bn'
  1487. else:
  1488. norm_type = 'bn'
  1489. backbone = self._get_backbone(
  1490. 'MobileNetV3',
  1491. model_name='large',
  1492. norm_type=norm_type,
  1493. scale=.5,
  1494. with_extra_blocks=False,
  1495. extra_block_filters=[],
  1496. feature_maps=[7, 13, 16])
  1497. neck = ppdet.modeling.PPYOLOTinyFPN(
  1498. detection_block_channels=[160, 128, 96],
  1499. in_channels=[i.channels for i in backbone.out_shape],
  1500. spp=use_spp,
  1501. drop_block=use_drop_block)
  1502. loss = ppdet.modeling.YOLOv3Loss(
  1503. num_classes=num_classes,
  1504. ignore_thresh=ignore_threshold,
  1505. downsample=self.downsample_ratios,
  1506. label_smooth=label_smooth,
  1507. scale_x_y=scale_x_y,
  1508. iou_loss=ppdet.modeling.IouLoss(
  1509. loss_weight=2.5, loss_square=True)
  1510. if use_iou_loss else None,
  1511. iou_aware_loss=ppdet.modeling.IouAwareLoss(loss_weight=1.0)
  1512. if use_iou_aware else None)
  1513. yolo_head = ppdet.modeling.YOLOv3Head(
  1514. in_channels=[i.channels for i in neck.out_shape],
  1515. anchors=anchors,
  1516. anchor_masks=anchor_masks,
  1517. num_classes=num_classes,
  1518. loss=loss,
  1519. iou_aware=use_iou_aware)
  1520. if use_matrix_nms:
  1521. nms = ppdet.modeling.MatrixNMS(
  1522. keep_top_k=nms_keep_topk,
  1523. score_threshold=nms_score_threshold,
  1524. post_threshold=.05,
  1525. nms_top_k=nms_topk,
  1526. background_label=-1)
  1527. else:
  1528. nms = ppdet.modeling.MultiClassNMS(
  1529. score_threshold=nms_score_threshold,
  1530. nms_top_k=nms_topk,
  1531. keep_top_k=nms_keep_topk,
  1532. nms_threshold=nms_iou_threshold)
  1533. post_process = ppdet.modeling.BBoxPostProcess(
  1534. decode=ppdet.modeling.YOLOBox(
  1535. num_classes=num_classes,
  1536. conf_thresh=.005,
  1537. downsample_ratio=32,
  1538. clip_bbox=True,
  1539. scale_x_y=scale_x_y),
  1540. nms=nms)
  1541. params.update({
  1542. 'backbone': backbone,
  1543. 'neck': neck,
  1544. 'yolo_head': yolo_head,
  1545. 'post_process': post_process
  1546. })
  1547. super(YOLOv3, self).__init__(
  1548. model_name='YOLOv3', num_classes=num_classes, **params)
  1549. self.anchors = anchors
  1550. self.anchor_masks = anchor_masks
  1551. self.model_name = 'PPYOLOTiny'
  1552. def _get_test_inputs(self, image_shape):
  1553. if image_shape is not None:
  1554. image_shape = self._check_image_shape(image_shape)
  1555. self._fix_transforms_shape(image_shape[-2:])
  1556. else:
  1557. image_shape = [None, 3, 320, 320]
  1558. if getattr(self, 'test_transforms', None):
  1559. for idx, op in enumerate(self.test_transforms.transforms):
  1560. name = op.__class__.__name__
  1561. if name == 'Resize':
  1562. image_shape = [None, 3] + list(
  1563. self.test_transforms.transforms[idx].target_size)
  1564. logging.warning(
  1565. '[Important!!!] When exporting inference model for {},'.format(
  1566. self.__class__.__name__) +
  1567. ' if fixed_input_shape is not set, it will be forcibly set to {}. '.
  1568. format(image_shape) +
  1569. 'Please check image shape after transforms is {}, if not, fixed_input_shape '.
  1570. format(image_shape[1:]) + 'should be specified manually.')
  1571. self.fixed_input_shape = image_shape
  1572. return self._define_input_spec(image_shape)
  1573. class PPYOLOv2(YOLOv3):
  1574. def __init__(self,
  1575. num_classes=80,
  1576. backbone='ResNet50_vd_dcn',
  1577. anchors=[[10, 13], [16, 30], [33, 23], [30, 61], [62, 45],
  1578. [59, 119], [116, 90], [156, 198], [373, 326]],
  1579. anchor_masks=[[6, 7, 8], [3, 4, 5], [0, 1, 2]],
  1580. use_iou_aware=True,
  1581. use_spp=True,
  1582. use_drop_block=True,
  1583. scale_x_y=1.05,
  1584. ignore_threshold=0.7,
  1585. label_smooth=False,
  1586. use_iou_loss=True,
  1587. use_matrix_nms=True,
  1588. nms_score_threshold=0.01,
  1589. nms_topk=-1,
  1590. nms_keep_topk=100,
  1591. nms_iou_threshold=0.45,
  1592. **params):
  1593. self.init_params = locals()
  1594. if backbone not in {'ResNet50_vd_dcn', 'ResNet101_vd_dcn'}:
  1595. raise ValueError(
  1596. "backbone: {} is not supported. Please choose one of "
  1597. "{'ResNet50_vd_dcn', 'ResNet101_vd_dcn'}.".format(backbone))
  1598. self.backbone_name = backbone
  1599. self.downsample_ratios = [32, 16, 8]
  1600. if params.get('with_net', True):
  1601. if paddlers.env_info['place'] == 'gpu' and paddlers.env_info[
  1602. 'num'] > 1 and not os.environ.get('PADDLERS_EXPORT_STAGE'):
  1603. norm_type = 'sync_bn'
  1604. else:
  1605. norm_type = 'bn'
  1606. if backbone == 'ResNet50_vd_dcn':
  1607. backbone = self._get_backbone(
  1608. 'ResNet',
  1609. variant='d',
  1610. norm_type=norm_type,
  1611. return_idx=[1, 2, 3],
  1612. dcn_v2_stages=[3],
  1613. freeze_at=-1,
  1614. freeze_norm=False,
  1615. norm_decay=0.)
  1616. elif backbone == 'ResNet101_vd_dcn':
  1617. backbone = self._get_backbone(
  1618. 'ResNet',
  1619. depth=101,
  1620. variant='d',
  1621. norm_type=norm_type,
  1622. return_idx=[1, 2, 3],
  1623. dcn_v2_stages=[3],
  1624. freeze_at=-1,
  1625. freeze_norm=False,
  1626. norm_decay=0.)
  1627. neck = ppdet.modeling.PPYOLOPAN(
  1628. norm_type=norm_type,
  1629. in_channels=[i.channels for i in backbone.out_shape],
  1630. drop_block=use_drop_block,
  1631. block_size=3,
  1632. keep_prob=.9,
  1633. spp=use_spp)
  1634. loss = ppdet.modeling.YOLOv3Loss(
  1635. num_classes=num_classes,
  1636. ignore_thresh=ignore_threshold,
  1637. downsample=self.downsample_ratios,
  1638. label_smooth=label_smooth,
  1639. scale_x_y=scale_x_y,
  1640. iou_loss=ppdet.modeling.IouLoss(
  1641. loss_weight=2.5, loss_square=True)
  1642. if use_iou_loss else None,
  1643. iou_aware_loss=ppdet.modeling.IouAwareLoss(loss_weight=1.0)
  1644. if use_iou_aware else None)
  1645. yolo_head = ppdet.modeling.YOLOv3Head(
  1646. in_channels=[i.channels for i in neck.out_shape],
  1647. anchors=anchors,
  1648. anchor_masks=anchor_masks,
  1649. num_classes=num_classes,
  1650. loss=loss,
  1651. iou_aware=use_iou_aware,
  1652. iou_aware_factor=.5)
  1653. if use_matrix_nms:
  1654. nms = ppdet.modeling.MatrixNMS(
  1655. keep_top_k=nms_keep_topk,
  1656. score_threshold=nms_score_threshold,
  1657. post_threshold=.01,
  1658. nms_top_k=nms_topk,
  1659. background_label=-1)
  1660. else:
  1661. nms = ppdet.modeling.MultiClassNMS(
  1662. score_threshold=nms_score_threshold,
  1663. nms_top_k=nms_topk,
  1664. keep_top_k=nms_keep_topk,
  1665. nms_threshold=nms_iou_threshold)
  1666. post_process = ppdet.modeling.BBoxPostProcess(
  1667. decode=ppdet.modeling.YOLOBox(
  1668. num_classes=num_classes,
  1669. conf_thresh=.01,
  1670. downsample_ratio=32,
  1671. clip_bbox=True,
  1672. scale_x_y=scale_x_y),
  1673. nms=nms)
  1674. params.update({
  1675. 'backbone': backbone,
  1676. 'neck': neck,
  1677. 'yolo_head': yolo_head,
  1678. 'post_process': post_process
  1679. })
  1680. super(YOLOv3, self).__init__(
  1681. model_name='YOLOv3', num_classes=num_classes, **params)
  1682. self.anchors = anchors
  1683. self.anchor_masks = anchor_masks
  1684. self.model_name = 'PPYOLOv2'
  1685. def _get_test_inputs(self, image_shape):
  1686. if image_shape is not None:
  1687. image_shape = self._check_image_shape(image_shape)
  1688. self._fix_transforms_shape(image_shape[-2:])
  1689. else:
  1690. image_shape = [None, 3, 640, 640]
  1691. if getattr(self, 'test_transforms', None):
  1692. for idx, op in enumerate(self.test_transforms.transforms):
  1693. name = op.__class__.__name__
  1694. if name == 'Resize':
  1695. image_shape = [None, 3] + list(
  1696. self.test_transforms.transforms[idx].target_size)
  1697. logging.warning(
  1698. '[Important!!!] When exporting inference model for {},'.format(
  1699. self.__class__.__name__) +
  1700. ' if fixed_input_shape is not set, it will be forcibly set to {}. '.
  1701. format(image_shape) +
  1702. 'Please check image shape after transforms is {}, if not, fixed_input_shape '.
  1703. format(image_shape[1:]) + 'should be specified manually.')
  1704. self.fixed_input_shape = image_shape
  1705. return self._define_input_spec(image_shape)
  1706. class MaskRCNN(BaseDetector):
  1707. def __init__(self,
  1708. num_classes=80,
  1709. backbone='ResNet50_vd',
  1710. with_fpn=True,
  1711. with_dcn=False,
  1712. aspect_ratios=[0.5, 1.0, 2.0],
  1713. anchor_sizes=[[32], [64], [128], [256], [512]],
  1714. keep_top_k=100,
  1715. nms_threshold=0.5,
  1716. score_threshold=0.05,
  1717. fpn_num_channels=256,
  1718. rpn_batch_size_per_im=256,
  1719. rpn_fg_fraction=0.5,
  1720. test_pre_nms_top_n=None,
  1721. test_post_nms_top_n=1000,
  1722. **params):
  1723. self.init_params = locals()
  1724. if backbone not in {
  1725. 'ResNet50', 'ResNet50_vd', 'ResNet50_vd_ssld', 'ResNet101',
  1726. 'ResNet101_vd'
  1727. }:
  1728. raise ValueError(
  1729. "backbone: {} is not supported. Please choose one of "
  1730. "{'ResNet50', 'ResNet50_vd', 'ResNet50_vd_ssld', 'ResNet101', 'ResNet101_vd'}.".
  1731. format(backbone))
  1732. self.backbone_name = backbone + '_fpn' if with_fpn else backbone
  1733. dcn_v2_stages = [1, 2, 3] if with_dcn else [-1]
  1734. if params.get('with_net', True):
  1735. if backbone == 'ResNet50':
  1736. if with_fpn:
  1737. backbone = self._get_backbone(
  1738. 'ResNet',
  1739. norm_type='bn',
  1740. freeze_at=0,
  1741. return_idx=[0, 1, 2, 3],
  1742. num_stages=4,
  1743. dcn_v2_stages=dcn_v2_stages)
  1744. else:
  1745. if with_dcn:
  1746. logging.warning(
  1747. "Backbone {} should be used along with dcn disabled, 'with_dcn' is forcibly set to False".
  1748. format(backbone))
  1749. backbone = self._get_backbone(
  1750. 'ResNet',
  1751. norm_type='bn',
  1752. freeze_at=0,
  1753. return_idx=[2],
  1754. num_stages=3)
  1755. elif 'ResNet50_vd' in backbone:
  1756. if not with_fpn:
  1757. logging.warning(
  1758. "Backbone {} should be used along with fpn enabled, 'with_fpn' is forcibly set to True".
  1759. format(backbone))
  1760. with_fpn = True
  1761. backbone = self._get_backbone(
  1762. 'ResNet',
  1763. variant='d',
  1764. norm_type='bn',
  1765. freeze_at=0,
  1766. return_idx=[0, 1, 2, 3],
  1767. num_stages=4,
  1768. lr_mult_list=[0.05, 0.05, 0.1, 0.15]
  1769. if '_ssld' in backbone else [1.0, 1.0, 1.0, 1.0],
  1770. dcn_v2_stages=dcn_v2_stages)
  1771. else:
  1772. if not with_fpn:
  1773. logging.warning(
  1774. "Backbone {} should be used along with fpn enabled, 'with_fpn' is forcibly set to True".
  1775. format(backbone))
  1776. with_fpn = True
  1777. backbone = self._get_backbone(
  1778. 'ResNet',
  1779. variant='d' if '_vd' in backbone else 'b',
  1780. depth=101,
  1781. norm_type='bn',
  1782. freeze_at=0,
  1783. return_idx=[0, 1, 2, 3],
  1784. num_stages=4,
  1785. dcn_v2_stages=dcn_v2_stages)
  1786. rpn_in_channel = backbone.out_shape[0].channels
  1787. if with_fpn:
  1788. neck = ppdet.modeling.FPN(
  1789. in_channels=[i.channels for i in backbone.out_shape],
  1790. out_channel=fpn_num_channels,
  1791. spatial_scales=[
  1792. 1.0 / i.stride for i in backbone.out_shape
  1793. ])
  1794. rpn_in_channel = neck.out_shape[0].channels
  1795. anchor_generator_cfg = {
  1796. 'aspect_ratios': aspect_ratios,
  1797. 'anchor_sizes': anchor_sizes,
  1798. 'strides': [4, 8, 16, 32, 64]
  1799. }
  1800. train_proposal_cfg = {
  1801. 'min_size': 0.0,
  1802. 'nms_thresh': .7,
  1803. 'pre_nms_top_n': 2000,
  1804. 'post_nms_top_n': 1000,
  1805. 'topk_after_collect': True
  1806. }
  1807. test_proposal_cfg = {
  1808. 'min_size': 0.0,
  1809. 'nms_thresh': .7,
  1810. 'pre_nms_top_n': 1000
  1811. if test_pre_nms_top_n is None else test_pre_nms_top_n,
  1812. 'post_nms_top_n': test_post_nms_top_n
  1813. }
  1814. bb_head = ppdet.modeling.TwoFCHead(
  1815. in_channel=neck.out_shape[0].channels, out_channel=1024)
  1816. bb_roi_extractor_cfg = {
  1817. 'resolution': 7,
  1818. 'spatial_scale': [1. / i.stride for i in neck.out_shape],
  1819. 'sampling_ratio': 0,
  1820. 'aligned': True
  1821. }
  1822. with_pool = False
  1823. m_head = ppdet.modeling.MaskFeat(
  1824. in_channel=neck.out_shape[0].channels,
  1825. out_channel=256,
  1826. num_convs=4)
  1827. m_roi_extractor_cfg = {
  1828. 'resolution': 14,
  1829. 'spatial_scale': [1. / i.stride for i in neck.out_shape],
  1830. 'sampling_ratio': 0,
  1831. 'aligned': True
  1832. }
  1833. mask_assigner = MaskAssigner(
  1834. num_classes=num_classes, mask_resolution=28)
  1835. share_bbox_feat = False
  1836. else:
  1837. neck = None
  1838. anchor_generator_cfg = {
  1839. 'aspect_ratios': aspect_ratios,
  1840. 'anchor_sizes': anchor_sizes,
  1841. 'strides': [16]
  1842. }
  1843. train_proposal_cfg = {
  1844. 'min_size': 0.0,
  1845. 'nms_thresh': .7,
  1846. 'pre_nms_top_n': 12000,
  1847. 'post_nms_top_n': 2000,
  1848. 'topk_after_collect': False
  1849. }
  1850. test_proposal_cfg = {
  1851. 'min_size': 0.0,
  1852. 'nms_thresh': .7,
  1853. 'pre_nms_top_n': 6000
  1854. if test_pre_nms_top_n is None else test_pre_nms_top_n,
  1855. 'post_nms_top_n': test_post_nms_top_n
  1856. }
  1857. bb_head = ppdet.modeling.Res5Head()
  1858. bb_roi_extractor_cfg = {
  1859. 'resolution': 14,
  1860. 'spatial_scale':
  1861. [1. / i.stride for i in backbone.out_shape],
  1862. 'sampling_ratio': 0,
  1863. 'aligned': True
  1864. }
  1865. with_pool = True
  1866. m_head = ppdet.modeling.MaskFeat(
  1867. in_channel=bb_head.out_shape[0].channels,
  1868. out_channel=256,
  1869. num_convs=0)
  1870. m_roi_extractor_cfg = {
  1871. 'resolution': 14,
  1872. 'spatial_scale':
  1873. [1. / i.stride for i in backbone.out_shape],
  1874. 'sampling_ratio': 0,
  1875. 'aligned': True
  1876. }
  1877. mask_assigner = MaskAssigner(
  1878. num_classes=num_classes, mask_resolution=14)
  1879. share_bbox_feat = True
  1880. rpn_target_assign_cfg = {
  1881. 'batch_size_per_im': rpn_batch_size_per_im,
  1882. 'fg_fraction': rpn_fg_fraction,
  1883. 'negative_overlap': .3,
  1884. 'positive_overlap': .7,
  1885. 'use_random': True
  1886. }
  1887. rpn_head = ppdet.modeling.RPNHead(
  1888. anchor_generator=anchor_generator_cfg,
  1889. rpn_target_assign=rpn_target_assign_cfg,
  1890. train_proposal=train_proposal_cfg,
  1891. test_proposal=test_proposal_cfg,
  1892. in_channel=rpn_in_channel)
  1893. bbox_assigner = BBoxAssigner(num_classes=num_classes)
  1894. bbox_head = ppdet.modeling.BBoxHead(
  1895. head=bb_head,
  1896. in_channel=bb_head.out_shape[0].channels,
  1897. roi_extractor=bb_roi_extractor_cfg,
  1898. with_pool=with_pool,
  1899. bbox_assigner=bbox_assigner,
  1900. num_classes=num_classes)
  1901. mask_head = ppdet.modeling.MaskHead(
  1902. head=m_head,
  1903. roi_extractor=m_roi_extractor_cfg,
  1904. mask_assigner=mask_assigner,
  1905. share_bbox_feat=share_bbox_feat,
  1906. num_classes=num_classes)
  1907. bbox_post_process = ppdet.modeling.BBoxPostProcess(
  1908. num_classes=num_classes,
  1909. decode=ppdet.modeling.RCNNBox(num_classes=num_classes),
  1910. nms=ppdet.modeling.MultiClassNMS(
  1911. score_threshold=score_threshold,
  1912. keep_top_k=keep_top_k,
  1913. nms_threshold=nms_threshold))
  1914. mask_post_process = ppdet.modeling.MaskPostProcess(binary_thresh=.5)
  1915. params.update({
  1916. 'backbone': backbone,
  1917. 'neck': neck,
  1918. 'rpn_head': rpn_head,
  1919. 'bbox_head': bbox_head,
  1920. 'mask_head': mask_head,
  1921. 'bbox_post_process': bbox_post_process,
  1922. 'mask_post_process': mask_post_process
  1923. })
  1924. self.with_fpn = with_fpn
  1925. super(MaskRCNN, self).__init__(
  1926. model_name='MaskRCNN', num_classes=num_classes, **params)
  1927. def _pre_train(self, in_args):
  1928. train_dataset = in_args['train_dataset']
  1929. if train_dataset.pos_num < len(train_dataset.file_list):
  1930. # In-place modification
  1931. train_dataset.num_workers = 0
  1932. return in_args
  1933. def _compose_batch_transform(self, transforms, mode='train'):
  1934. if mode == 'train':
  1935. default_batch_transforms = [
  1936. _BatchPad(pad_to_stride=32 if self.with_fpn else -1)
  1937. ]
  1938. else:
  1939. default_batch_transforms = [
  1940. _BatchPad(pad_to_stride=32 if self.with_fpn else -1)
  1941. ]
  1942. custom_batch_transforms = []
  1943. for i, op in enumerate(transforms.transforms):
  1944. if isinstance(op, (BatchRandomResize, BatchRandomResizeByShort)):
  1945. if mode != 'train':
  1946. raise ValueError(
  1947. "{} cannot be present in the {} transforms. ".format(
  1948. op.__class__.__name__, mode) +
  1949. "Please check the {} transforms.".format(mode))
  1950. custom_batch_transforms.insert(0, copy.deepcopy(op))
  1951. batch_transforms = BatchCompose(
  1952. custom_batch_transforms + default_batch_transforms,
  1953. collate_batch=False)
  1954. return batch_transforms
  1955. def _fix_transforms_shape(self, image_shape):
  1956. if getattr(self, 'test_transforms', None):
  1957. has_resize_op = False
  1958. resize_op_idx = -1
  1959. normalize_op_idx = len(self.test_transforms.transforms)
  1960. for idx, op in enumerate(self.test_transforms.transforms):
  1961. name = op.__class__.__name__
  1962. if name == 'ResizeByShort':
  1963. has_resize_op = True
  1964. resize_op_idx = idx
  1965. if name == 'Normalize':
  1966. normalize_op_idx = idx
  1967. if not has_resize_op:
  1968. self.test_transforms.transforms.insert(
  1969. normalize_op_idx,
  1970. Resize(
  1971. target_size=image_shape,
  1972. keep_ratio=True,
  1973. interp='CUBIC'))
  1974. else:
  1975. self.test_transforms.transforms[resize_op_idx] = Resize(
  1976. target_size=image_shape, keep_ratio=True, interp='CUBIC')
  1977. self.test_transforms.transforms.append(
  1978. Pad(im_padding_value=[0., 0., 0.]))
  1979. def _get_test_inputs(self, image_shape):
  1980. if image_shape is not None:
  1981. image_shape = self._check_image_shape(image_shape)
  1982. self._fix_transforms_shape(image_shape[-2:])
  1983. else:
  1984. image_shape = [None, 3, -1, -1]
  1985. if self.with_fpn:
  1986. self.test_transforms.transforms.append(
  1987. Pad(im_padding_value=[0., 0., 0.]))
  1988. self.fixed_input_shape = image_shape
  1989. return self._define_input_spec(image_shape)