test_lad_head.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. # Copyright (c) OpenMMLab. All rights reserved.
  2. from unittest import TestCase
  3. import numpy as np
  4. import torch
  5. from mmengine import Config
  6. from mmengine.structures import InstanceData
  7. from mmdet import * # noqa
  8. from mmdet.models.dense_heads import LADHead, lad_head
  9. from mmdet.models.dense_heads.lad_head import levels_to_images
  10. class TestLADHead(TestCase):
  11. def test_lad_head_loss(self):
  12. """Tests lad head loss when truth is empty and non-empty."""
  13. class mock_skm:
  14. def GaussianMixture(self, *args, **kwargs):
  15. return self
  16. def fit(self, loss):
  17. pass
  18. def predict(self, loss):
  19. components = np.zeros_like(loss, dtype=np.long)
  20. return components.reshape(-1)
  21. def score_samples(self, loss):
  22. scores = np.random.random(len(loss))
  23. return scores
  24. lad_head.skm = mock_skm()
  25. s = 256
  26. img_metas = [{
  27. 'img_shape': (s, s, 3),
  28. 'pad_shape': (s, s, 3),
  29. 'scale_factor': 1
  30. }]
  31. train_cfg = Config(
  32. dict(
  33. assigner=dict(
  34. type='MaxIoUAssigner',
  35. pos_iou_thr=0.1,
  36. neg_iou_thr=0.1,
  37. min_pos_iou=0,
  38. ignore_iof_thr=-1),
  39. allowed_border=-1,
  40. pos_weight=-1,
  41. debug=False))
  42. # since Focal Loss is not supported on CPU
  43. # since Focal Loss is not supported on CPU
  44. lad = LADHead(
  45. num_classes=4,
  46. in_channels=1,
  47. train_cfg=train_cfg,
  48. loss_cls=dict(
  49. type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
  50. loss_bbox=dict(type='GIoULoss', loss_weight=1.3),
  51. loss_centerness=dict(
  52. type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.5))
  53. teacher_model = LADHead(
  54. num_classes=4,
  55. in_channels=1,
  56. train_cfg=train_cfg,
  57. loss_cls=dict(
  58. type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
  59. loss_bbox=dict(type='GIoULoss', loss_weight=1.3),
  60. loss_centerness=dict(
  61. type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.5))
  62. feat = [
  63. torch.rand(1, 1, s // feat_size, s // feat_size)
  64. for feat_size in [4, 8, 16, 32, 64]
  65. ]
  66. lad.init_weights()
  67. teacher_model.init_weights()
  68. # Test that empty ground truth encourages the network to predict
  69. # background
  70. gt_instances = InstanceData()
  71. gt_instances.bboxes = torch.empty((0, 4))
  72. gt_instances.labels = torch.LongTensor([])
  73. batch_gt_instances_ignore = None
  74. outs_teacher = teacher_model(feat)
  75. label_assignment_results = teacher_model.get_label_assignment(
  76. *outs_teacher, [gt_instances], img_metas,
  77. batch_gt_instances_ignore)
  78. outs = teacher_model(feat)
  79. empty_gt_losses = lad.loss_by_feat(*outs, [gt_instances], img_metas,
  80. batch_gt_instances_ignore,
  81. label_assignment_results)
  82. # When there is no truth, the cls loss should be nonzero but there
  83. # should be no box loss.
  84. empty_cls_loss = empty_gt_losses['loss_cls']
  85. empty_box_loss = empty_gt_losses['loss_bbox']
  86. empty_iou_loss = empty_gt_losses['loss_iou']
  87. self.assertGreater(empty_cls_loss.item(), 0,
  88. 'cls loss should be non-zero')
  89. self.assertEqual(
  90. empty_box_loss.item(), 0,
  91. 'there should be no box loss when there are no true boxes')
  92. self.assertEqual(
  93. empty_iou_loss.item(), 0,
  94. 'there should be no box loss when there are no true boxes')
  95. # When truth is non-empty then both cls and box loss should be nonzero
  96. # for random inputs
  97. gt_instances = InstanceData()
  98. gt_instances.bboxes = torch.Tensor(
  99. [[23.6667, 23.8757, 238.6326, 151.8874]])
  100. gt_instances.labels = torch.LongTensor([2])
  101. batch_gt_instances_ignore = None
  102. label_assignment_results = teacher_model.get_label_assignment(
  103. *outs_teacher, [gt_instances], img_metas,
  104. batch_gt_instances_ignore)
  105. one_gt_losses = lad.loss_by_feat(*outs, [gt_instances], img_metas,
  106. batch_gt_instances_ignore,
  107. label_assignment_results)
  108. onegt_cls_loss = one_gt_losses['loss_cls']
  109. onegt_box_loss = one_gt_losses['loss_bbox']
  110. onegt_iou_loss = one_gt_losses['loss_iou']
  111. self.assertGreater(onegt_cls_loss.item(), 0,
  112. 'cls loss should be non-zero')
  113. self.assertGreater(onegt_box_loss.item(), 0,
  114. 'box loss should be non-zero')
  115. self.assertGreater(onegt_iou_loss.item(), 0,
  116. 'box loss should be non-zero')
  117. n, c, h, w = 10, 4, 20, 20
  118. mlvl_tensor = [torch.ones(n, c, h, w) for i in range(5)]
  119. results = levels_to_images(mlvl_tensor)
  120. self.assertEqual(len(results), n)
  121. self.assertEqual(results[0].size(), (h * w * 5, c))
  122. self.assertTrue(lad.with_score_voting)
  123. lad = LADHead(
  124. num_classes=4,
  125. in_channels=1,
  126. train_cfg=train_cfg,
  127. anchor_generator=dict(
  128. type='AnchorGenerator',
  129. ratios=[1.0],
  130. octave_base_scale=8,
  131. scales_per_octave=1,
  132. strides=[8]),
  133. loss_cls=dict(
  134. type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
  135. loss_bbox=dict(type='GIoULoss', loss_weight=1.3),
  136. loss_centerness=dict(
  137. type='CrossEntropyLoss', use_sigmoid=True, loss_weight=0.5))
  138. cls_scores = [torch.ones(2, 4, 5, 5)]
  139. bbox_preds = [torch.ones(2, 4, 5, 5)]
  140. iou_preds = [torch.ones(2, 1, 5, 5)]
  141. cfg = Config(
  142. dict(
  143. nms_pre=1000,
  144. min_bbox_size=0,
  145. score_thr=0.05,
  146. nms=dict(type='nms', iou_threshold=0.6),
  147. max_per_img=100))
  148. rescale = False
  149. lad.predict_by_feat(
  150. cls_scores, bbox_preds, iou_preds, img_metas, cfg, rescale=rescale)