yolov8fpn.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. from typing import List, Union
  2. import torch
  3. import torch.nn as nn
  4. from mmdet.utils import ConfigType, OptMultiConfig
  5. from mmengine.model import BaseModule
  6. from mmengine.registry import MODELS
  7. from mmcv.cnn import ConvModule
  8. from ..layers import CSPLayerWithTwoConv
  9. from ..utils import make_divisible, make_round
  10. @MODELS.register_module()
  11. class YOLOv8PAFPN(BaseModule):
  12. """Path Aggregation Network used in YOLOv8.
  13. Args:
  14. in_channels (List[int]): Number of input channels per scale.
  15. out_channels (int): Number of output channels (used at each scale)
  16. deepen_factor (float): Depth multiplier, multiply number of
  17. blocks in CSP layer by this amount. Defaults to 1.0.
  18. widen_factor (float): Width multiplier, multiply number of
  19. channels in each layer by this amount. Defaults to 1.0.
  20. num_csp_blocks (int): Number of bottlenecks in CSPLayer. Defaults to 1.
  21. freeze_all(bool): Whether to freeze the model
  22. norm_cfg (dict): Config dict for normalization layer.
  23. Defaults to dict(type='BN', momentum=0.03, eps=0.001).
  24. act_cfg (dict): Config dict for activation layer.
  25. Defaults to dict(type='SiLU', inplace=True).
  26. init_cfg (dict or list[dict], optional): Initialization config dict.
  27. Defaults to None.
  28. """
  29. def __init__(self,
  30. in_channels: List[int],
  31. out_channels: Union[List[int], int],
  32. deepen_factor: float = 1.0,
  33. widen_factor: float = 1.0,
  34. num_csp_blocks: int = 3,
  35. freeze_all: bool = False,
  36. upsample_feats_cat_first=True,
  37. norm_cfg: ConfigType = dict(
  38. type='BN', momentum=0.03, eps=0.001),
  39. act_cfg: ConfigType = dict(type='SiLU', inplace=True),
  40. init_cfg: OptMultiConfig = None):
  41. super().__init__(init_cfg)
  42. self.in_channels = in_channels
  43. self.out_channels = out_channels
  44. self.deepen_factor = deepen_factor
  45. self.widen_factor = widen_factor
  46. self.upsample_feats_cat_first = upsample_feats_cat_first
  47. self.freeze_all = freeze_all
  48. self.norm_cfg = norm_cfg
  49. self.act_cfg = act_cfg
  50. self.num_csp_blocks = num_csp_blocks
  51. # build top-down blocks
  52. self.upsample_layers = nn.ModuleList()
  53. self.top_down_layers = nn.ModuleList()
  54. for idx in range(len(in_channels) - 1, 0, -1):
  55. self.upsample_layers.append(self.build_upsample_layer(idx))
  56. self.top_down_layers.append(self.build_top_down_layer(idx))
  57. # build bottom-up blocks
  58. self.downsample_layers = nn.ModuleList()
  59. self.bottom_up_layers = nn.ModuleList()
  60. for idx in range(len(in_channels) - 1):
  61. self.downsample_layers.append(self.build_downsample_layer(idx))
  62. self.bottom_up_layers.append(self.build_bottom_up_layer(idx))
  63. def build_upsample_layer(self, *args, **kwargs) -> nn.Module:
  64. """build upsample layer."""
  65. return nn.Upsample(scale_factor=2, mode='nearest')
  66. def build_top_down_layer(self, idx: int) -> nn.Module:
  67. """build top down layer.
  68. Args:
  69. idx (int): layer idx.
  70. Returns:
  71. nn.Module: The top down layer.
  72. """
  73. return CSPLayerWithTwoConv(
  74. make_divisible((self.in_channels[idx - 1] + self.in_channels[idx]),
  75. self.widen_factor),
  76. make_divisible(self.out_channels[idx - 1], self.widen_factor),
  77. num_blocks=make_round(self.num_csp_blocks, self.deepen_factor),
  78. add_identity=False,
  79. norm_cfg=self.norm_cfg,
  80. act_cfg=self.act_cfg)
  81. def build_bottom_up_layer(self, idx: int) -> nn.Module:
  82. """build bottom up layer.
  83. Args:
  84. idx (int): layer idx.
  85. Returns:
  86. nn.Module: The bottom up layer.
  87. """
  88. return CSPLayerWithTwoConv(
  89. make_divisible(
  90. (self.out_channels[idx] + self.out_channels[idx + 1]),
  91. self.widen_factor),
  92. make_divisible(self.out_channels[idx + 1], self.widen_factor),
  93. num_blocks=make_round(self.num_csp_blocks, self.deepen_factor),
  94. add_identity=False,
  95. norm_cfg=self.norm_cfg,
  96. act_cfg=self.act_cfg)
  97. def build_downsample_layer(self, idx: int) -> nn.Module:
  98. """build downsample layer.
  99. Args:
  100. idx (int): layer idx.
  101. Returns:
  102. nn.Module: The downsample layer.
  103. """
  104. return ConvModule(
  105. make_divisible(self.in_channels[idx], self.widen_factor),
  106. make_divisible(self.in_channels[idx], self.widen_factor),
  107. kernel_size=3,
  108. stride=2,
  109. padding=1,
  110. norm_cfg=self.norm_cfg,
  111. act_cfg=self.act_cfg)
  112. def init_weights(self):
  113. if self.init_cfg is None:
  114. """Initialize the parameters."""
  115. for m in self.modules():
  116. if isinstance(m, torch.nn.Conv2d):
  117. # In order to be consistent with the source code,
  118. # reset the Conv2d initialization parameters
  119. m.reset_parameters()
  120. else:
  121. super().init_weights()
  122. def forward(self, inputs: List[torch.Tensor]) -> tuple:
  123. """Forward function."""
  124. assert len(inputs) == len(self.in_channels)
  125. # top-down path
  126. inner_outs = [inputs[-1]]
  127. for idx in range(len(self.in_channels) - 1, 0, -1):
  128. feat_high = inner_outs[0]
  129. feat_low = inputs[idx - 1]
  130. upsample_feat = self.upsample_layers[len(self.in_channels) - 1 -
  131. idx](
  132. feat_high)
  133. if self.upsample_feats_cat_first:
  134. top_down_layer_inputs = torch.cat([upsample_feat, feat_low], 1)
  135. else:
  136. top_down_layer_inputs = torch.cat([feat_low, upsample_feat], 1)
  137. inner_out = self.top_down_layers[len(self.in_channels) - 1 - idx](
  138. top_down_layer_inputs)
  139. inner_outs.insert(0, inner_out)
  140. # bottom-up path
  141. outs = [inner_outs[0]]
  142. for idx in range(len(self.in_channels) - 1):
  143. feat_low = outs[-1]
  144. feat_high = inner_outs[idx + 1]
  145. downsample_feat = self.downsample_layers[idx](feat_low)
  146. out = self.bottom_up_layers[idx](
  147. torch.cat([downsample_feat, feat_high], 1))
  148. outs.append(out)
  149. # out_layers
  150. results = []
  151. for idx in range(len(self.in_channels)):
  152. results.append(outs[idx])
  153. print(outs[idx].size())
  154. input()
  155. return tuple(results)