test_yolox_head.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. # Copyright (c) OpenMMLab. All rights reserved.
  2. from unittest import TestCase
  3. import torch
  4. from mmcv.cnn import ConvModule, DepthwiseSeparableConvModule
  5. from mmengine.config import Config
  6. from mmengine.model import bias_init_with_prob
  7. from mmengine.structures import InstanceData
  8. from mmengine.testing import assert_allclose
  9. from mmdet.models.dense_heads import YOLOXHead
  10. class TestYOLOXHead(TestCase):
  11. def test_init_weights(self):
  12. head = YOLOXHead(
  13. num_classes=4, in_channels=1, stacked_convs=1, use_depthwise=False)
  14. head.init_weights()
  15. bias_init = bias_init_with_prob(0.01)
  16. for conv_cls, conv_obj in zip(head.multi_level_conv_cls,
  17. head.multi_level_conv_obj):
  18. assert_allclose(conv_cls.bias.data,
  19. torch.ones_like(conv_cls.bias.data) * bias_init)
  20. assert_allclose(conv_obj.bias.data,
  21. torch.ones_like(conv_obj.bias.data) * bias_init)
  22. def test_predict_by_feat(self):
  23. s = 256
  24. img_metas = [{
  25. 'img_shape': (s, s, 3),
  26. 'scale_factor': (1.0, 1.0),
  27. }]
  28. test_cfg = Config(
  29. dict(score_thr=0.01, nms=dict(type='nms', iou_threshold=0.65)))
  30. head = YOLOXHead(
  31. num_classes=4,
  32. in_channels=1,
  33. stacked_convs=1,
  34. use_depthwise=False,
  35. test_cfg=test_cfg)
  36. feat = [
  37. torch.rand(1, 1, s // feat_size, s // feat_size)
  38. for feat_size in [4, 8, 16]
  39. ]
  40. cls_scores, bbox_preds, objectnesses = head.forward(feat)
  41. head.predict_by_feat(
  42. cls_scores,
  43. bbox_preds,
  44. objectnesses,
  45. img_metas,
  46. cfg=test_cfg,
  47. rescale=True,
  48. with_nms=True)
  49. head.predict_by_feat(
  50. cls_scores,
  51. bbox_preds,
  52. objectnesses,
  53. img_metas,
  54. cfg=test_cfg,
  55. rescale=False,
  56. with_nms=False)
  57. def test_loss_by_feat(self):
  58. s = 256
  59. img_metas = [{
  60. 'img_shape': (s, s, 3),
  61. 'scale_factor': 1,
  62. }]
  63. train_cfg = Config(
  64. dict(
  65. assigner=dict(
  66. type='SimOTAAssigner',
  67. center_radius=2.5,
  68. candidate_topk=10,
  69. iou_weight=3.0,
  70. cls_weight=1.0)))
  71. head = YOLOXHead(
  72. num_classes=4,
  73. in_channels=1,
  74. stacked_convs=1,
  75. use_depthwise=False,
  76. train_cfg=train_cfg)
  77. assert not head.use_l1
  78. assert isinstance(head.multi_level_cls_convs[0][0], ConvModule)
  79. feat = [
  80. torch.rand(1, 1, s // feat_size, s // feat_size)
  81. for feat_size in [4, 8, 16]
  82. ]
  83. cls_scores, bbox_preds, objectnesses = head.forward(feat)
  84. # Test that empty ground truth encourages the network to predict
  85. # background
  86. gt_instances = InstanceData(
  87. bboxes=torch.empty((0, 4)), labels=torch.LongTensor([]))
  88. empty_gt_losses = head.loss_by_feat(cls_scores, bbox_preds,
  89. objectnesses, [gt_instances],
  90. img_metas)
  91. # When there is no truth, the cls loss should be nonzero but there
  92. # should be no box loss.
  93. empty_cls_loss = empty_gt_losses['loss_cls'].sum()
  94. empty_box_loss = empty_gt_losses['loss_bbox'].sum()
  95. empty_obj_loss = empty_gt_losses['loss_obj'].sum()
  96. self.assertEqual(
  97. empty_cls_loss.item(), 0,
  98. 'there should be no cls loss when there are no true boxes')
  99. self.assertEqual(
  100. empty_box_loss.item(), 0,
  101. 'there should be no box loss when there are no true boxes')
  102. self.assertGreater(empty_obj_loss.item(), 0,
  103. 'objectness loss should be non-zero')
  104. # When truth is non-empty then both cls and box loss should be nonzero
  105. # for random inputs
  106. head = YOLOXHead(
  107. num_classes=4,
  108. in_channels=1,
  109. stacked_convs=1,
  110. use_depthwise=True,
  111. train_cfg=train_cfg)
  112. assert isinstance(head.multi_level_cls_convs[0][0],
  113. DepthwiseSeparableConvModule)
  114. head.use_l1 = True
  115. gt_instances = InstanceData(
  116. bboxes=torch.Tensor([[23.6667, 23.8757, 238.6326, 151.8874]]),
  117. labels=torch.LongTensor([2]))
  118. one_gt_losses = head.loss_by_feat(cls_scores, bbox_preds, objectnesses,
  119. [gt_instances], img_metas)
  120. onegt_cls_loss = one_gt_losses['loss_cls'].sum()
  121. onegt_box_loss = one_gt_losses['loss_bbox'].sum()
  122. onegt_obj_loss = one_gt_losses['loss_obj'].sum()
  123. onegt_l1_loss = one_gt_losses['loss_l1'].sum()
  124. self.assertGreater(onegt_cls_loss.item(), 0,
  125. 'cls loss should be non-zero')
  126. self.assertGreater(onegt_box_loss.item(), 0,
  127. 'box loss should be non-zero')
  128. self.assertGreater(onegt_obj_loss.item(), 0,
  129. 'obj loss should be non-zero')
  130. self.assertGreater(onegt_l1_loss.item(), 0,
  131. 'l1 loss should be non-zero')
  132. # Test groud truth out of bound
  133. gt_instances = InstanceData(
  134. bboxes=torch.Tensor([[s * 4, s * 4, s * 4 + 10, s * 4 + 10]]),
  135. labels=torch.LongTensor([2]))
  136. empty_gt_losses = head.loss_by_feat(cls_scores, bbox_preds,
  137. objectnesses, [gt_instances],
  138. img_metas)
  139. # When gt_bboxes out of bound, the assign results should be empty,
  140. # so the cls and bbox loss should be zero.
  141. empty_cls_loss = empty_gt_losses['loss_cls'].sum()
  142. empty_box_loss = empty_gt_losses['loss_bbox'].sum()
  143. empty_obj_loss = empty_gt_losses['loss_obj'].sum()
  144. self.assertEqual(
  145. empty_cls_loss.item(), 0,
  146. 'there should be no cls loss when gt_bboxes out of bound')
  147. self.assertEqual(
  148. empty_box_loss.item(), 0,
  149. 'there should be no box loss when gt_bboxes out of bound')
  150. self.assertGreater(empty_obj_loss.item(), 0,
  151. 'objectness loss should be non-zero')