首先看config文件中,以Faster-RCNN为例,backbone的主要参数为:

    1. backbone=dict(
    2. type='ResNet', #backbone名称
    3. depth=50,    #网络的深度50 --> ResNet50
    4. num_stages=4, #4个残差块
    5. out_indices=(0, 1, 2, 3), #指输出哪几层卷积后的特征作为输出,0-C2,1-C3,2-C4,3-C5
    6. frozen_stages=1, # 第一层冻结,参数不参与训练
    7. style='pytorch')  
    8. '''
    9. style 源码中的解释:
    10. If style is "pytorch", the stride-two layer is the 3x3 conv layer,
    11. if it is "caffe", the stride-two layer is the first 1x1 conv layer.
    12. '''

    然后转到/mmdet/models/backbone/resnet.py中查看构建backbone:

    1. class Bottleneck(nn.Module):
    2. expansion = 4 # 表示输出的channel的膨胀系数为4 64-->256
    3. def __init__(self,
    4. inplanes,
    5. planes,
    6. stride=1,
    7. dilation=1,  # 空洞卷积:正常卷积核的空洞率为dilation=1
    8. downsample=None,
    9. style='pytorch',
    10. with_cp=False,
    11. conv_cfg=None,
    12. norm_cfg=dict(type='BN'),
    13. dcn=None, # 添加可变形卷积
    14. gcb=None, #
    15. gen_attention=None): # 添加注意力机制模块
    16. """Bottleneck block for ResNet.
    17. If style is "pytorch", the stride-two layer is the 3x3 conv layer,
    18. if it is "caffe", the stride-two layer is the first 1x1 conv layer.
    19. """
    20. super(Bottleneck, self).__init__()
    21. assert style in ['pytorch', 'caffe']
    22. assert dcn is None or isinstance(dcn, dict)
    23. assert gcb is None or isinstance(gcb, dict)
    24. assert gen_attention is None or isinstance(gen_attention, dict)
    25. self.inplanes = inplanes # input
    26. self.planes = planes # output
    27. self.stride = stride
    28. self.dilation = dilation
    29. self.style = style
    30. self.with_cp = with_cp
    31. self.conv_cfg = conv_cfg # 设定卷积核的一些参数
    32. self.norm_cfg = norm_cfg # 设定normlize层的参数:BN or GN
    33. self.dcn = dcn
    34. self.with_dcn = dcn is not None
    35. self.gcb = gcb
    36. self.with_gcb = gcb is not None
    37. self.gen_attention = gen_attention
    38. self.with_gen_attention = gen_attention is not None

    首先是一些基础参数;

    1. """
    2. Bottleneck block for ResNet.
    3. If style is "pytorch", the stride-two layer is the 3x3 conv layer,
    4. if it is "caffe", the stride-two layer is the first 1x1 conv layer.
    5. """
    6. if self.style == 'pytorch':
    7. self.conv1_stride = 1
    8. self.conv2_stride = stride
    9. else:
    10. self.conv1_stride = stride
    11. self.conv2_stride = 1
    12. self.norm1_name, norm1 = build_norm_layer(norm_cfg, planes, postfix=1)
    13. self.norm2_name, norm2 = build_norm_layer(norm_cfg, planes, postfix=2)
    14. self.norm3_name, norm3 = build_norm_layer(
    15. norm_cfg, planes * self.expansion, postfix=3)
    16. # 添加normlize层,一个残差块共三个conv层和三个norm层
    17. # build_norm_layer返回的是该norm层的名字’str‘与其 'nn.model'的对象
    18. self.conv1 = build_conv_layer(
    19. conv_cfg, # conv_cfg = None build常规卷积核
    20. inplanes,
    21. planes,
    22. kernel_size=1,
    23. stride=self.conv1_stride,
    24. bias=False)
    25. self.add_module(self.norm1_name, norm1) # 将norm对象赋值给self.norm1_name
    26. fallback_on_stride = False
    27. if self.with_dcn:  # 是否添加可变形卷积
    28. fallback_on_stride = dcn.pop('fallback_on_stride', False)
    29. if not self.with_dcn or fallback_on_stride:
    30. self.conv2 = build_conv_layer(
    31. conv_cfg,
    32. planes,
    33. planes,
    34. kernel_size=3,
    35. stride=self.conv2_stride,
    36. padding=dilation,
    37. dilation=dilation,
    38. bias=False)
    39. else: # 添加常规卷积核
    40. assert self.conv_cfg is None, 'conv_cfg cannot be None for DCN'
    41. self.conv2 = build_conv_layer(
    42. dcn,
    43. planes,
    44. planes,
    45. kernel_size=3,
    46. stride=self.conv2_stride,
    47. padding=dilation,
    48. dilation=dilation,
    49. bias=False)
    50. self.add_module(self.norm2_name, norm2)
    51. self.conv3 = build_conv_layer(
    52. conv_cfg,
    53. planes,
    54. planes * self.expansion,
    55. kernel_size=1,
    56. bias=False)
    57. self.add_module(self.norm3_name, norm3)
    58. self.relu = nn.ReLU(inplace=True)
    59. self.downsample = downsample
    60. if self.with_gcb:
    61. gcb_inplanes = planes * self.expansion
    62. self.context_block = ContextBlock(inplanes=gcb_inplanes, **gcb)
    63. # gen_attention
    64. if self.with_gen_attention:
    65. self.gen_attention_block = GeneralizedAttention(
    66. planes, **gen_attention)

    将残差块搭建完成;

    1. @property
    2. def norm1(self):
    3. return getattr(self, self.norm1_name)
    4. @property
    5. def norm2(self):
    6. return getattr(self, self.norm2_name)
    7. @property
    8. def norm3(self):
    9. return getattr(self, self.norm3_name)

    @property装饰器就是负责把一个方法变成属性调用的;**getattr()** 函数用于返回一个对象属性值。

    1. # 每一个残差块的forward前向传播
    2. def forward(self, x):
    3. def _inner_forward(x):
    4. identity = x
    5. out = self.conv1(x)
    6. out = self.norm1(out)
    7. out = self.relu(out)
    8. out = self.conv2(out)
    9. out = self.norm2(out)
    10. out = self.relu(out)
    11. if self.with_gen_attention:
    12. out = self.gen_attention_block(out)
    13. out = self.conv3(out)
    14. out = self.norm3(out)
    15. if self.with_gcb:
    16. out = self.context_block(out)
    17. if self.downsample is not None:
    18. identity = self.downsample(x)
    19. out += identity
    20. return out
    21. if self.with_cp and x.requires_grad:
    22. out = cp.checkpoint(_inner_forward, x)
    23. else:
    24. out = _inner_forward(x)
    25. out = self.relu(out)
    26. return out
    1. def make_res_layer(block,
    2. inplanes,
    3. planes,
    4. blocks,
    5. stride=1,
    6. dilation=1,
    7. style='pytorch',
    8. with_cp=False,
    9. conv_cfg=None,
    10. norm_cfg=dict(type='BN'),
    11. dcn=None,
    12. gcb=None,
    13. gen_attention=None,
    14. gen_attention_blocks=[]):
    15. downsample = None
    16. if stride != 1 or inplanes != planes * block.expansion:
    17. downsample = nn.Sequential(
    18. build_conv_layer(
    19. conv_cfg,
    20. inplanes,
    21. planes * block.expansion,
    22. kernel_size=1,
    23. stride=stride,
    24. bias=False),
    25. build_norm_layer(norm_cfg, planes * block.expansion)[1],
    26. )
    27. layers = []
    28. layers.append(
    29. block(
    30. inplanes=inplanes,
    31. planes=planes,
    32. stride=stride,
    33. dilation=dilation,
    34. downsample=downsample,
    35. style=style,
    36. with_cp=with_cp,
    37. conv_cfg=conv_cfg,
    38. norm_cfg=norm_cfg,
    39. dcn=dcn,
    40. gcb=gcb,
    41. gen_attention=gen_attention if
    42. (0 in gen_attention_blocks) else None))
    43. inplanes = planes * block.expansion
    44. for i in range(1, blocks):
    45. layers.append(
    46. block(
    47. inplanes=inplanes,
    48. planes=planes,
    49. stride=1,
    50. dilation=dilation,
    51. style=style,
    52. with_cp=with_cp,
    53. conv_cfg=conv_cfg,
    54. norm_cfg=norm_cfg,
    55. dcn=dcn,
    56. gcb=gcb,
    57. gen_attention=gen_attention if
    58. (i in gen_attention_blocks) else None))
    59. return nn.Sequential(*layers)

    下面是最主要的构建ResNet网络的代码块:

    1. @BACKBONES.register_module
    2. class ResNet(nn.Module):
    3. """ResNet backbone.
    4. Args:
    5. depth (int): Depth of resnet, from {18, 34, 50, 101, 152}.
    6. in_channels (int): Number of input image channels. Normally 3.
    7. num_stages (int): Resnet stages, normally 4.
    8. strides (Sequence[int]): Strides of the first block of each stage.
    9. dilations (Sequence[int]): Dilation of each stage.
    10. out_indices (Sequence[int]): Output from which stages.
    11. style (str): `pytorch` or `caffe`. If set to "pytorch", the stride-two
    12. layer is the 3x3 conv layer, otherwise the stride-two layer is
    13. the first 1x1 conv layer.
    14. frozen_stages (int): Stages to be frozen (stop grad and set eval mode).
    15. -1 means not freezing any parameters.
    16. norm_cfg (dict): dictionary to construct and config norm layer.
    17. norm_eval (bool): Whether to set norm layers to eval mode, namely,
    18. freeze running stats (mean and var). Note: Effect on Batch Norm
    19. and its variants only.
    20. with_cp (bool): Use checkpoint or not. Using checkpoint will save some
    21. memory while slowing down the training speed.
    22. zero_init_residual (bool): whether to use zero init for last norm layer
    23. in resblocks to let them behave as identity.
    24. Example:
    25. >>> from mmdet.models import ResNet
    26. >>> import torch
    27. >>> self = ResNet(depth=18)
    28. >>> self.eval()
    29. >>> inputs = torch.rand(1, 3, 32, 32)
    30. >>> level_outputs = self.forward(inputs)
    31. >>> for level_out in level_outputs:
    32. ... print(tuple(level_out.shape))
    33. (1, 64, 8, 8)
    34. (1, 128, 4, 4)
    35. (1, 256, 2, 2)
    36. (1, 512, 1, 1)
    37. """
    38. arch_settings = {
    39. 18: (BasicBlock, (2, 2, 2, 2)),
    40. 34: (BasicBlock, (3, 4, 6, 3)),
    41. 50: (Bottleneck, (3, 4, 6, 3)),
    42. 101: (Bottleneck, (3, 4, 23, 3)),
    43. 152: (Bottleneck, (3, 8, 36, 3))
    44. }
    45. def __init__(self,
    46. depth,
    47. in_channels=3,
    48. num_stages=4,
    49. strides=(1, 2, 2, 2),
    50. dilations=(1, 1, 1, 1),
    51. out_indices=(0, 1, 2, 3),
    52. style='pytorch',
    53. frozen_stages=-1,
    54. conv_cfg=None,
    55. norm_cfg=dict(type='BN', requires_grad=True),
    56. norm_eval=True,
    57. dcn=None,
    58. stage_with_dcn=(False, False, False, False),
    59. gcb=None,
    60. stage_with_gcb=(False, False, False, False),
    61. gen_attention=None,
    62. stage_with_gen_attention=((), (), (), ()),
    63. with_cp=False,
    64. zero_init_residual=True):
    65. super(ResNet, self).__init__()
    66. if depth not in self.arch_settings:
    67. raise KeyError('invalid depth {} for resnet'.format(depth))
    68. self.depth = depth
    69. self.num_stages = num_stages
    70. assert num_stages >= 1 and num_stages <= 4
    71. self.strides = strides
    72. self.dilations = dilations
    73. assert len(strides) == len(dilations) == num_stages
    74. self.out_indices = out_indices
    75. assert max(out_indices) < num_stages
    76. self.style = style
    77. self.frozen_stages = frozen_stages
    78. self.conv_cfg = conv_cfg
    79. self.norm_cfg = norm_cfg
    80. self.with_cp = with_cp
    81. self.norm_eval = norm_eval
    82. self.dcn = dcn
    83. self.stage_with_dcn = stage_with_dcn
    84. if dcn is not None:
    85. assert len(stage_with_dcn) == num_stages
    86. self.gen_attention = gen_attention
    87. self.gcb = gcb
    88. self.stage_with_gcb = stage_with_gcb
    89. if gcb is not None:
    90. assert len(stage_with_gcb) == num_stages
    91. self.zero_init_residual = zero_init_residual
    92. self.block, stage_blocks = self.arch_settings[depth]
    93. self.stage_blocks = stage_blocks[:num_stages]
    94. self.inplanes = 64
    95. self._make_stem_layer(in_channels) # channel:3 --> 64
    96. # stem_layer,进入残差块之前需要对图片进行一次卷及操作
    97. self.res_layers = []
    98. for i, num_blocks in enumerate(self.stage_blocks):
    99. stride = strides[i]
    100. dilation = dilations[i]
    101. dcn = self.dcn if self.stage_with_dcn[i] else None
    102. gcb = self.gcb if self.stage_with_gcb[i] else None
    103. planes = 64 * 2**i
    104. res_layer = make_res_layer(
    105. self.block,
    106. self.inplanes,
    107. planes,
    108. num_blocks,
    109. stride=stride,
    110. dilation=dilation,
    111. style=self.style,
    112. with_cp=with_cp,
    113. conv_cfg=conv_cfg,
    114. norm_cfg=norm_cfg,
    115. dcn=dcn,
    116. gcb=gcb,
    117. gen_attention=gen_attention,
    118. gen_attention_blocks=stage_with_gen_attention[i])
    119. self.inplanes = planes * self.block.expansion
    120. layer_name = 'layer{}'.format(i + 1)
    121. self.add_module(layer_name, res_layer)
    122. self.res_layers.append(layer_name)
    123. # 将4stage的残差块添加到res_layer的list中
    124. self._freeze_stages()
    125. self.feat_dim = self.block.expansion * 64 * 2**(
    126. len(self.stage_blocks) - 1)
    127. @property
    128. def norm1(self):
    129. return getattr(self, self.norm1_name)
    130. def _make_stem_layer(self, in_channels):
    131. self.conv1 = build_conv_layer(
    132. self.conv_cfg,
    133. in_channels,
    134. 64,
    135. kernel_size=7,
    136. stride=2,
    137. padding=3,
    138. bias=False)
    139. self.norm1_name, norm1 = build_norm_layer(self.norm_cfg, 64, postfix=1)
    140. self.add_module(self.norm1_name, norm1)
    141. self.relu = nn.ReLU(inplace=True)
    142. self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
    143. # frezze某些层的参数不参与训练
    144. def _freeze_stages(self):
    145. if self.frozen_stages >= 0:
    146. self.norm1.eval()
    147. for m in [self.conv1, self.norm1]:
    148. for param in m.parameters():
    149. param.requires_grad = False
    150. for i in range(1, self.frozen_stages + 1):
    151. m = getattr(self, 'layer{}'.format(i))
    152. m.eval()
    153. for param in m.parameters():
    154. param.requires_grad = False
    155. def init_weights(self, pretrained=None):
    156. if isinstance(pretrained, str):
    157. logger = get_root_logger()
    158. load_checkpoint(self, pretrained, strict=False, logger=logger)
    159. elif pretrained is None:
    160. for m in self.modules():
    161. if isinstance(m, nn.Conv2d):
    162. kaiming_init(m)
    163. elif isinstance(m, (_BatchNorm, nn.GroupNorm)):
    164. constant_init(m, 1)
    165. if self.dcn is not None:
    166. for m in self.modules():
    167. if isinstance(m, Bottleneck) and hasattr(
    168. m, 'conv2_offset'):
    169. constant_init(m.conv2_offset, 0)
    170. if self.zero_init_residual:
    171. for m in self.modules():
    172. if isinstance(m, Bottleneck):
    173. constant_init(m.norm3, 0)
    174. elif isinstance(m, BasicBlock):
    175. constant_init(m.norm2, 0)
    176. else:
    177. raise TypeError('pretrained must be a str or None')
    178. def forward(self, x):
    179. x = self.conv1(x)
    180. x = self.norm1(x)
    181. x = self.relu(x)
    182. x = self.maxpool(x)
    183. outs = []
    184. for i, layer_name in enumerate(self.res_layers):
    185. res_layer = getattr(self, layer_name)
    186. x = res_layer(x)
    187. if i in self.out_indices:
    188. outs.append(x)
    189. return tuple(outs)
    190. def train(self, mode=True):
    191. super(ResNet, self).train(mode)
    192. self._freeze_stages()
    193. if mode and self.norm_eval:
    194. for m in self.modules():
    195. # trick: eval have effect on BatchNorm only
    196. if isinstance(m, _BatchNorm):
    197. m.eval()