当前位置:   article > 正文

Swin Transformer 代码学习笔记(目标检测)_swin transformer目标检测

swin transformer目标检测

        本文主要针对目标检测部分的代码。

源码地址:GitHub - SwinTransformer/Swin-Transformer-Object-Detection: This is an official implementation for "Swin Transformer: Hierarchical Vision Transformer using Shifted Windows" on Object Detection and Instance Segmentation.

 论文地址:https://arxiv.org/abs/2103.14030

         开始之前,先上一下swin transformer 结构图

        首先从模型训练开始,训练模型py文件位于项目根目录/tools/train.py,该文件中整体结构简单,仅有一个main函数。为了方便程序运行,我直接在配置项中将config配置成

\configs\swin\mask_rcnn_swin_tiny_patch4_window7_mstrain_480-800_adamw_1x_coco.py

        main函数中主要关注161行后的代码,以下是代码片段

  1. model = build_detector(
  2. cfg.model,
  3. train_cfg=cfg.get('train_cfg'),
  4. test_cfg=cfg.get('test_cfg'))
  5. datasets = [build_dataset(cfg.data.train)]
  6. if len(cfg.workflow) == 2:
  7. val_dataset = copy.deepcopy(cfg.data.val)
  8. val_dataset.pipeline = cfg.data.train.pipeline
  9. datasets.append(build_dataset(val_dataset))
  10. if cfg.checkpoint_config is not None:
  11. # save mmdet version, config file content and class names in
  12. # checkpoints as meta data
  13. cfg.checkpoint_config.meta = dict(
  14. mmdet_version=__version__ + get_git_hash()[:7],
  15. CLASSES=datasets[0].CLASSES)
  16. # add an attribute for visualization convenience
  17. model.CLASSES = datasets[0].CLASSES
  18. train_detector(
  19. model,
  20. datasets,
  21. cfg,
  22. distributed=distributed,
  23. validate=(not args.no_validate),
  24. timestamp=timestamp,
  25. meta=meta)

        这部分主要是构建模型,构建数据集,模型训练函数

        这里用的Mask RCNN结构,构建模型的时候,会分别构建如下文件夹中的相应组件:

mmdet/models/detectors/mask_rcnn.py 中的Mask RCNN类

mmdet/models/detectors/two_stage.py 中的TwoStageDetector类

mmdet/models/backbones/swin_transformer.py 中的SwinTransformer类(算法关键)

mmdet/models/necks/fpn.py 中的FPN类

mmdet/models/dense_heads/rpn_head.py 中的RPNHead类,其中还会构建各种损失函数和一些功能组件

mmdet/models/roi_heads/base_roi_head.py 中的BaseRoIHead 类

mmdet/models/roi_heads/bbox_heads/convfc_bbox_head.py 中的ConvFCBBoxHead类

mmdet/models/losses/cross_entropy_loss.py 中的CrossEntropyLoss类

mmdet/models/losses/smooth_l1_loss.py 中的L1Loss类

mmdet/core/bbox/assigners/max_iou_assigner.py 中的MaxIoUAssigner类

mmdet/core/bbox/samplers/random_sampler.py中的RandomSampler类

         构建完的模型:

  1. MaskRCNN(
  2. (backbone): SwinTransformer(
  3. (patch_embed): PatchEmbed(
  4. (proj): Conv2d(3, 96, kernel_size=(4, 4), stride=(4, 4))
  5. (norm): LayerNorm((96,), eps=1e-05, elementwise_affine=True)
  6. )
  7. (pos_drop): Dropout(p=0.0, inplace=False)
  8. (layers): ModuleList(
  9. (0): BasicLayer(
  10. (blocks): ModuleList(
  11. (0): SwinTransformerBlock(
  12. (norm1): LayerNorm((96,), eps=1e-05, elementwise_affine=True)
  13. (attn): WindowAttention(
  14. (qkv): Linear(in_features=96, out_features=288, bias=True)
  15. (attn_drop): Dropout(p=0.0, inplace=False)
  16. (proj): Linear(in_features=96, out_features=96, bias=True)
  17. (proj_drop): Dropout(p=0.0, inplace=False)
  18. (softmax): Softmax(dim=-1)
  19. )
  20. (drop_path): Identity()
  21. (norm2): LayerNorm((96,), eps=1e-05, elementwise_affine=True)
  22. (mlp): Mlp(
  23. (fc1): Linear(in_features=96, out_features=384, bias=True)
  24. (act): GELU()
  25. (fc2): Linear(in_features=384, out_features=96, bias=True)
  26. (drop): Dropout(p=0.0, inplace=False)
  27. )
  28. )
  29. (1): SwinTransformerBlock(
  30. (norm1): LayerNorm((96,), eps=1e-05, elementwise_affine=True)
  31. (attn): WindowAttention(
  32. (qkv): Linear(in_features=96, out_features=288, bias=True)
  33. (attn_drop): Dropout(p=0.0, inplace=False)
  34. (proj): Linear(in_features=96, out_features=96, bias=True)
  35. (proj_drop): Dropout(p=0.0, inplace=False)
  36. (softmax): Softmax(dim=-1)
  37. )
  38. (drop_path): DropPath()
  39. (norm2): LayerNorm((96,), eps=1e-05, elementwise_affine=True)
  40. (mlp): Mlp(
  41. (fc1): Linear(in_features=96, out_features=384, bias=True)
  42. (act): GELU()
  43. (fc2): Linear(in_features=384, out_features=96, bias=True)
  44. (drop): Dropout(p=0.0, inplace=False)
  45. )
  46. )
  47. )
  48. (downsample): PatchMerging(
  49. (reduction): Linear(in_features=384, out_features=192, bias=False)
  50. (norm): LayerNorm((384,), eps=1e-05, elementwise_affine=True)
  51. )
  52. )
  53. (1): BasicLayer(
  54. (blocks): ModuleList(
  55. (0): SwinTransformerBlock(
  56. (norm1): LayerNorm((192,), eps=1e-05, elementwise_affine=True)
  57. (attn): WindowAttention(
  58. (qkv): Linear(in_features=192, out_features=576, bias=True)
  59. (attn_drop): Dropout(p=0.0, inplace=False)
  60. (proj): Linear(in_features=192, out_features=192, bias=True)
  61. (proj_drop): Dropout(p=0.0, inplace=False)
  62. (softmax): Softmax(dim=-1)
  63. )
  64. (drop_path): DropPath()
  65. (norm2): LayerNorm((192,), eps=1e-05, elementwise_affine=True)
  66. (mlp): Mlp(
  67. (fc1): Linear(in_features=192, out_features=768, bias=True)
  68. (act): GELU()
  69. (fc2): Linear(in_features=768, out_features=192, bias=True)
  70. (drop): Dropout(p=0.0, inplace=False)
  71. )
  72. )
  73. (1): SwinTransformerBlock(
  74. (norm1): LayerNorm((192,), eps=1e-05, elementwise_affine=True)
  75. (attn): WindowAttention(
  76. (qkv): Linear(in_features=192, out_features=576, bias=True)
  77. (attn_drop): Dropout(p=0.0, inplace=False)
  78. (proj): Linear(in_features=192, out_features=192, bias=True)
  79. (proj_drop): Dropout(p=0.0, inplace=False)
  80. (softmax): Softmax(dim=-1)
  81. )
  82. (drop_path): DropPath()
  83. (norm2): LayerNorm((192,), eps=1e-05, elementwise_affine=True)
  84. (mlp): Mlp(
  85. (fc1): Linear(in_features=192, out_features=768, bias=True)
  86. (act): GELU()
  87. (fc2): Linear(in_features=768, out_features=192, bias=True)
  88. (drop): Dropout(p=0.0, inplace=False)
  89. )
  90. )
  91. )
  92. (downsample): PatchMerging(
  93. (reduction): Linear(in_features=768, out_features=384, bias=False)
  94. (norm): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
  95. )
  96. )
  97. (2): BasicLayer(
  98. (blocks): ModuleList(
  99. (0): SwinTransformerBlock(
  100. (norm1): LayerNorm((384,), eps=1e-05, elementwise_affine=True)
  101. (attn): WindowAttention(
  102. (qkv): Linear(in_features=384, out_features=1152, bias=True)
  103. (attn_drop): Dropout(p=0.0, inplace=False)
  104. (proj): Linear(in_features=384, out_features=384, bias=True)
  105. (proj_drop): Dropout(p=0.0, inplace=False)
  106. (softmax): Softmax(dim=-1)
  107. )
  108. (drop_path): DropPath()
  109. (norm2): LayerNorm((384,), eps=1e-05, elementwise_affine=True)
  110. (mlp): Mlp(
  111. (fc1): Linear(in_features=384, out_features=1536, bias=True)
  112. (act): GELU()
  113. (fc2): Linear(in_features=1536, out_features=384, bias=True)
  114. (drop): Dropout(p=0.0, inplace=False)
  115. )
  116. )
  117. (1): SwinTransformerBlock(
  118. (norm1): LayerNorm((384,), eps=1e-05, elementwise_affine=True)
  119. (attn): WindowAttention(
  120. (qkv): Linear(in_features=384, out_features=1152, bias=True)
  121. (attn_drop): Dropout(p=0.0, inplace=False)
  122. (proj): Linear(in_features=384, out_features=384, bias=True)
  123. (proj_drop): Dropout(p=0.0, inplace=False)
  124. (softmax): Softmax(dim=-1)
  125. )
  126. (drop_path): DropPath()
  127. (norm2): LayerNorm((384,), eps=1e-05, elementwise_affine=True)
  128. (mlp): Mlp(
  129. (fc1): Linear(in_features=384, out_features=1536, bias=True)
  130. (act): GELU()
  131. (fc2): Linear(in_features=1536, out_features=384, bias=True)
  132. (drop): Dropout(p=0.0, inplace=False)
  133. )
  134. )
  135. (2): SwinTransformerBlock(
  136. (norm1): LayerNorm((384,), eps=1e-05, elementwise_affine=True)
  137. (attn): WindowAttention(
  138. (qkv): Linear(in_features=384, out_features=1152, bias=True)
  139. (attn_drop): Dropout(p=0.0, inplace=False)
  140. (proj): Linear(in_features=384, out_features=384, bias=True)
  141. (proj_drop): Dropout(p=0.0, inplace=False)
  142. (softmax): Softmax(dim=-1)
  143. )
  144. (drop_path): DropPath()
  145. (norm2): LayerNorm((384,), eps=1e-05, elementwise_affine=True)
  146. (mlp): Mlp(
  147. (fc1): Linear(in_features=384, out_features=1536, bias=True)
  148. (act): GELU()
  149. (fc2): Linear(in_features=1536, out_features=384, bias=True)
  150. (drop): Dropout(p=0.0, inplace=False)
  151. )
  152. )
  153. (3): SwinTransformerBlock(
  154. (norm1): LayerNorm((384,), eps=1e-05, elementwise_affine=True)
  155. (attn): WindowAttention(
  156. (qkv): Linear(in_features=384, out_features=1152, bias=True)
  157. (attn_drop): Dropout(p=0.0, inplace=False)
  158. (proj): Linear(in_features=384, out_features=384, bias=True)
  159. (proj_drop): Dropout(p=0.0, inplace=False)
  160. (softmax): Softmax(dim=-1)
  161. )
  162. (drop_path): DropPath()
  163. (norm2): LayerNorm((384,), eps=1e-05, elementwise_affine=True)
  164. (mlp): Mlp(
  165. (fc1): Linear(in_features=384, out_features=1536, bias=True)
  166. (act): GELU()
  167. (fc2): Linear(in_features=1536, out_features=384, bias=True)
  168. (drop): Dropout(p=0.0, inplace=False)
  169. )
  170. )
  171. (4): SwinTransformerBlock(
  172. (norm1): LayerNorm((384,), eps=1e-05, elementwise_affine=True)
  173. (attn): WindowAttention(
  174. (qkv): Linear(in_features=384, out_features=1152, bias=True)
  175. (attn_drop): Dropout(p=0.0, inplace=False)
  176. (proj): Linear(in_features=384, out_features=384, bias=True)
  177. (proj_drop): Dropout(p=0.0, inplace=False)
  178. (softmax): Softmax(dim=-1)
  179. )
  180. (drop_path): DropPath()
  181. (norm2): LayerNorm((384,), eps=1e-05, elementwise_affine=True)
  182. (mlp): Mlp(
  183. (fc1): Linear(in_features=384, out_features=1536, bias=True)
  184. (act): GELU()
  185. (fc2): Linear(in_features=1536, out_features=384, bias=True)
  186. (drop): Dropout(p=0.0, inplace=False)
  187. )
  188. )
  189. (5): SwinTransformerBlock(
  190. (norm1): LayerNorm((384,), eps=1e-05, elementwise_affine=True)
  191. (attn): WindowAttention(
  192. (qkv): Linear(in_features=384, out_features=1152, bias=True)
  193. (attn_drop): Dropout(p=0.0, inplace=False)
  194. (proj): Linear(in_features=384, out_features=384, bias=True)
  195. (proj_drop): Dropout(p=0.0, inplace=False)
  196. (softmax): Softmax(dim=-1)
  197. )
  198. (drop_path): DropPath()
  199. (norm2): LayerNorm((384,), eps=1e-05, elementwise_affine=True)
  200. (mlp): Mlp(
  201. (fc1): Linear(in_features=384, out_features=1536, bias=True)
  202. (act): GELU()
  203. (fc2): Linear(in_features=1536, out_features=384, bias=True)
  204. (drop): Dropout(p=0.0, inplace=False)
  205. )
  206. )
  207. )
  208. (downsample): PatchMerging(
  209. (reduction): Linear(in_features=1536, out_features=768, bias=False)
  210. (norm): LayerNorm((1536,), eps=1e-05, elementwise_affine=True)
  211. )
  212. )
  213. (3): BasicLayer(
  214. (blocks): ModuleList(
  215. (0): SwinTransformerBlock(
  216. (norm1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
  217. (attn): WindowAttention(
  218. (qkv): Linear(in_features=768, out_features=2304, bias=True)
  219. (attn_drop): Dropout(p=0.0, inplace=False)
  220. (proj): Linear(in_features=768, out_features=768, bias=True)
  221. (proj_drop): Dropout(p=0.0, inplace=False)
  222. (softmax): Softmax(dim=-1)
  223. )
  224. (drop_path): DropPath()
  225. (norm2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
  226. (mlp): Mlp(
  227. (fc1): Linear(in_features=768, out_features=3072, bias=True)
  228. (act): GELU()
  229. (fc2): Linear(in_features=3072, out_features=768, bias=True)
  230. (drop): Dropout(p=0.0, inplace=False)
  231. )
  232. )
  233. (1): SwinTransformerBlock(
  234. (norm1): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
  235. (attn): WindowAttention(
  236. (qkv): Linear(in_features=768, out_features=2304, bias=True)
  237. (attn_drop): Dropout(p=0.0, inplace=False)
  238. (proj): Linear(in_features=768, out_features=768, bias=True)
  239. (proj_drop): Dropout(p=0.0, inplace=False)
  240. (softmax): Softmax(dim=-1)
  241. )
  242. (drop_path): DropPath()
  243. (norm2): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
  244. (mlp): Mlp(
  245. (fc1): Linear(in_features=768, out_features=3072, bias=True)
  246. (act): GELU()
  247. (fc2): Linear(in_features=3072, out_features=768, bias=True)
  248. (drop): Dropout(p=0.0, inplace=False)
  249. )
  250. )
  251. )
  252. )
  253. )
  254. (norm0): LayerNorm((96,), eps=1e-05, elementwise_affine=True)
  255. (norm1): LayerNorm((192,), eps=1e-05, elementwise_affine=True)
  256. (norm2): LayerNorm((384,), eps=1e-05, elementwise_affine=True)
  257. (norm3): LayerNorm((768,), eps=1e-05, elementwise_affine=True)
  258. )
  259. (neck): FPN(
  260. (lateral_convs): ModuleList(
  261. (0): ConvModule(
  262. (conv): Conv2d(96, 256, kernel_size=(1, 1), stride=(1, 1))
  263. )
  264. (1): ConvModule(
  265. (conv): Conv2d(192, 256, kernel_size=(1, 1), stride=(1, 1))
  266. )
  267. (2): ConvModule(
  268. (conv): Conv2d(384, 256, kernel_size=(1, 1), stride=(1, 1))
  269. )
  270. (3): ConvModule(
  271. (conv): Conv2d(768, 256, kernel_size=(1, 1), stride=(1, 1))
  272. )
  273. )
  274. (fpn_convs): ModuleList(
  275. (0): ConvModule(
  276. (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  277. )
  278. (1): ConvModule(
  279. (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  280. )
  281. (2): ConvModule(
  282. (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  283. )
  284. (3): ConvModule(
  285. (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  286. )
  287. )
  288. )
  289. (rpn_head): RPNHead(
  290. (loss_cls): CrossEntropyLoss()
  291. (loss_bbox): L1Loss()
  292. (rpn_conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  293. (rpn_cls): Conv2d(256, 3, kernel_size=(1, 1), stride=(1, 1))
  294. (rpn_reg): Conv2d(256, 12, kernel_size=(1, 1), stride=(1, 1))
  295. )
  296. (roi_head): StandardRoIHead(
  297. (bbox_roi_extractor): SingleRoIExtractor(
  298. (roi_layers): ModuleList(
  299. (0): RoIAlign(output_size=(7, 7), spatial_scale=0.25, sampling_ratio=0, pool_mode=avg, aligned=True, use_torchvision=False)
  300. (1): RoIAlign(output_size=(7, 7), spatial_scale=0.125, sampling_ratio=0, pool_mode=avg, aligned=True, use_torchvision=False)
  301. (2): RoIAlign(output_size=(7, 7), spatial_scale=0.0625, sampling_ratio=0, pool_mode=avg, aligned=True, use_torchvision=False)
  302. (3): RoIAlign(output_size=(7, 7), spatial_scale=0.03125, sampling_ratio=0, pool_mode=avg, aligned=True, use_torchvision=False)
  303. )
  304. )
  305. (bbox_head): Shared2FCBBoxHead(
  306. (loss_cls): CrossEntropyLoss()
  307. (loss_bbox): L1Loss()
  308. (fc_cls): Linear(in_features=1024, out_features=21, bias=True)
  309. (fc_reg): Linear(in_features=1024, out_features=80, bias=True)
  310. (shared_convs): ModuleList()
  311. (shared_fcs): ModuleList(
  312. (0): Linear(in_features=12544, out_features=1024, bias=True)
  313. (1): Linear(in_features=1024, out_features=1024, bias=True)
  314. )
  315. (cls_convs): ModuleList()
  316. (cls_fcs): ModuleList()
  317. (reg_convs): ModuleList()
  318. (reg_fcs): ModuleList()
  319. (relu): ReLU(inplace=True)
  320. )
  321. )
  322. )

        backbone部分的就是swin transformer的精髓,下图是mask rcnn的结构图

        Swin Transformer就是用transformer块替换了图中CNN的结构,作为特征采集器。扯了这么多,是时候进入正题了,代码的关键算法都位于mmdet/models/backbones/swin_transformer.py文件中。主体位于SwinTransformer类中。

  1. class SwinTransformer(nn.Module):
  2. """ Swin Transformer backbone.
  3. A PyTorch impl of : `Swin Transformer: Hierarchical Vision Transformer using Shifted Windows` -
  4. https://arxiv.org/pdf/2103.14030
  5. Args:
  6. pretrain_img_size (int): Input image size for training the pretrained model,
  7. used in absolute postion embedding. Default 224.
  8. patch_size (int | tuple(int)): Patch size. Default: 4.
  9. in_chans (int): Number of input image channels. Default: 3.
  10. embed_dim (int): Number of linear projection output channels. Default: 96.
  11. depths (tuple[int]): Depths of each Swin Transformer stage.
  12. num_heads (tuple[int]): Number of attention head of each stage.
  13. window_size (int): Window size. Default: 7.
  14. mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. Default: 4.
  15. qkv_bias (bool): If True, add a learnable bias to query, key, value. Default: True
  16. qk_scale (float): Override default qk scale of head_dim ** -0.5 if set.
  17. drop_rate (float): Dropout rate.
  18. attn_drop_rate (float): Attention dropout rate. Default: 0.
  19. drop_path_rate (float): Stochastic depth rate. Default: 0.2.
  20. norm_layer (nn.Module): Normalization layer. Default: nn.LayerNorm.
  21. ape (bool): If True, add absolute position embedding to the patch embedding. Default: False.
  22. patch_norm (bool): If True, add normalization after patch embedding. Default: True.
  23. out_indices (Sequence[int]): Output from which stages.
  24. frozen_stages (int): Stages to be frozen (stop grad and set eval mode).
  25. -1 means not freezing any parameters.
  26. use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False.
  27. """
  28. def __init__(self,
  29. pretrain_img_size=224,
  30. patch_size=4,
  31. in_chans=3,
  32. embed_dim=96,
  33. depths=[2, 2, 6, 2],
  34. num_heads=[3, 6, 12, 24],
  35. window_size=7,
  36. mlp_ratio=4.,
  37. qkv_bias=True,
  38. qk_scale=None,
  39. drop_rate=0.,
  40. attn_drop_rate=0.,
  41. drop_path_rate=0.2,
  42. norm_layer=nn.LayerNorm,
  43. ape=False,
  44. patch_norm=True,
  45. out_indices=(0, 1, 2, 3),
  46. frozen_stages=-1,
  47. use_checkpoint=False):
  48. super().__init__()
  49. self.pretrain_img_size = pretrain_img_size
  50. self.num_layers = len(depths)
  51. self.embed_dim = embed_dim
  52. self.ape = ape
  53. self.patch_norm = patch_norm
  54. self.out_indices = out_indices
  55. self.frozen_stages = frozen_stages
  56. # split image into non-overlapping patches
  57. self.patch_embed = PatchEmbed(
  58. patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim,
  59. norm_layer=norm_layer if self.patch_norm else None)
  60. # absolute position embedding
  61. if self.ape:
  62. pretrain_img_size = to_2tuple(pretrain_img_size)
  63. patch_size = to_2tuple(patch_size)
  64. patches_resolution = [pretrain_img_size[0] // patch_size[0], pretrain_img_size[1] // patch_size[1]]
  65. # 对应网络结构中的 linear embedding 网络结构
  66. self.absolute_pos_embed = nn.Parameter(torch.zeros(1, embed_dim, patches_resolution[0], patches_resolution[1]))
  67. # 绝对位置编码参数初始化
  68. trunc_normal_(self.absolute_pos_embed, std=.02)
  69. self.pos_drop = nn.Dropout(p=drop_rate)
  70. # stochastic depth
  71. # 给网络层数每层设置随机dropout rate
  72. dpr = [x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))] # stochastic depth decay rule
  73. # build layers
  74. self.layers = nn.ModuleList()
  75. # 构建四层网络结构
  76. # mlp_ratio Ratio of mlp hidden dim to embedding dim.
  77. # downsample 下采样 前三个block 会进行下采样 第四个block 不会在进行下采样
  78. for i_layer in range(self.num_layers):
  79. layer = BasicLayer(
  80. dim=int(embed_dim * 2 ** i_layer),
  81. depth=depths[i_layer],
  82. num_heads=num_heads[i_layer],
  83. window_size=window_size,
  84. mlp_ratio=mlp_ratio,
  85. qkv_bias=qkv_bias,
  86. qk_scale=qk_scale,
  87. drop=drop_rate,
  88. attn_drop=attn_drop_rate,
  89. drop_path=dpr[sum(depths[:i_layer]):sum(depths[:i_layer + 1])],
  90. norm_layer=norm_layer,
  91. downsample=PatchMerging if (i_layer < self.num_layers - 1) else None,
  92. use_checkpoint=use_checkpoint)
  93. self.layers.append(layer)
  94. num_features = [int(embed_dim * 2 ** i) for i in range(self.num_layers)]
  95. self.num_features = num_features
  96. # add a norm layer for each output
  97. for i_layer in out_indices:
  98. layer = norm_layer(num_features[i_layer])
  99. layer_name = f'norm{i_layer}'
  100. self.add_module(layer_name, layer)
  101. self._freeze_stages()
  102. def _freeze_stages(self):
  103. if self.frozen_stages >= 0:
  104. self.patch_embed.eval()
  105. for param in self.patch_embed.parameters():
  106. param.requires_grad = False
  107. if self.frozen_stages >= 1 and self.ape:
  108. self.absolute_pos_embed.requires_grad = False
  109. if self.frozen_stages >= 2:
  110. self.pos_drop.eval()
  111. for i in range(0, self.frozen_stages - 1):
  112. m = self.layers[i]
  113. m.eval()
  114. for param in m.parameters():
  115. param.requires_grad = False
  116. def init_weights(self, pretrained=None):
  117. """Initialize the weights in backbone.
  118. Args:
  119. pretrained (str, optional): Path to pre-trained weights.
  120. Defaults to None.
  121. """
  122. def _init_weights(m):
  123. if isinstance(m, nn.Linear):
  124. trunc_normal_(m.weight, std=.02)
  125. if isinstance(m, nn.Linear) and m.bias is not None:
  126. nn.init.constant_(m.bias, 0)
  127. elif isinstance(m, nn.LayerNorm):
  128. nn.init.constant_(m.bias, 0)
  129. nn.init.constant_(m.weight, 1.0)
  130. if isinstance(pretrained, str):
  131. self.apply(_init_weights)
  132. logger = get_root_logger()
  133. load_checkpoint(self, pretrained, strict=False, logger=logger)
  134. elif pretrained is None:
  135. self.apply(_init_weights)
  136. else:
  137. raise TypeError('pretrained must be a str or None')
  138. def forward(self, x):
  139. """Forward function."""
  140. x = self.patch_embed(x)
  141. Wh, Ww = x.size(2), x.size(3)
  142. if self.ape:
  143. # interpolate the position embedding to the corresponding size
  144. absolute_pos_embed = F.interpolate(self.absolute_pos_embed, size=(Wh, Ww), mode='bicubic')
  145. x = (x + absolute_pos_embed).flatten(2).transpose(1, 2) # B Wh*Ww C
  146. else:
  147. x = x.flatten(2).transpose(1, 2)
  148. x = self.pos_drop(x)
  149. outs = []
  150. for i in range(self.num_layers):
  151. layer = self.layers[i]
  152. x_out, H, W, x, Wh, Ww = layer(x, Wh, Ww)
  153. if i in self.out_indices:
  154. norm_layer = getattr(self, f'norm{i}')
  155. x_out = norm_layer(x_out)
  156. out = x_out.view(-1, H, W, self.num_features[i]).permute(0, 3, 1, 2).contiguous()
  157. outs.append(out)
  158. return tuple(outs)
  159. def train(self, mode=True):
  160. """Convert the model into training mode while keep layers freezed."""
  161. super(SwinTransformer, self).train(mode)
  162. self._freeze_stages()

        为了方便将img_scale设置为[(224,224)],此时的数据集中的图片会进行resize,并且将短边padding成32的倍数,按最长边与224的比例缩放最短边,即 x=短边 / (500/224),x即为缩放后的实际长度,比如输入时500*287(w*h)的图片,缩放后的短边长为287 * 224 / 500 = 129,由于输入的图像尺寸需要是32的倍数,此时需要向上取整,所以padding后的短边长为160。
        吃了贫穷的亏,电脑只有单卡3080ti,batch size设置为2,使用未修改的img_scale训练coco数据集时还经常贴着最大显存跑,生怕训练的时候爆显存。。。所以单卡训练batch size就设置成2吧,每个batch中的数据会根据边长最大的图片再进行一次padding,比如其中一张第一次padding后大小为(160, 224, 3),另一张为(192, 224, 3),那么最终输入网络的数据尺寸为[2, 3, 192, 224] (B,C,H,W)

        之后将会以输入[2, 3, 192, 224]为例进行讲解,按网络结构的顺序来。

        PatchEmbed 通过一个size为4,stride为4的2d卷积来达到图像缩小4倍,并将维度升到embed_dim,PatchEmbed可以简单理解为包含了网络结构中的patch partition和linear embedding,所以embed_dim为输入transformer block的维度而不是论文中的48。

  1. class PatchEmbed(nn.Module):
  2. """ Image to Patch Embedding
  3. Args:
  4. patch_size (int): Patch token size. Default: 4.
  5. in_chans (int): Number of input image channels. Default: 3.
  6. embed_dim (int): Number of linear projection output channels. Default: 96.
  7. norm_layer (nn.Module, optional): Normalization layer. Default: None
  8. """
  9. def __init__(self, patch_size=4, in_chans=3, embed_dim=96, norm_layer=None):
  10. super().__init__()
  11. patch_size = to_2tuple(patch_size)
  12. self.patch_size = patch_size
  13. self.in_chans = in_chans
  14. self.embed_dim = embed_dim
  15. # 用2d卷积实现图像缩小四倍 kernel_size = stride = patch_size = 4
  16. self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
  17. if norm_layer is not None:
  18. self.norm = norm_layer(embed_dim)
  19. else:
  20. self.norm = None
  21. def forward(self, x):
  22. """Forward function."""
  23. # padding
  24. _, _, H, W = x.size()
  25. if W % self.patch_size[1] != 0:
  26. x = F.pad(x, (0, self.patch_size[1] - W % self.patch_size[1]))
  27. if H % self.patch_size[0] != 0:
  28. x = F.pad(x, (0, 0, 0, self.patch_size[0] - H % self.patch_size[0]))
  29. x = self.proj(x) # B C Wh Ww
  30. if self.norm is not None:
  31. # x的shape变化 B, C, H, W --> B, C, h, w --> B, C, h * w --> B, h * w, c
  32. # x shape 为 [2, 96, 48, 56]
  33. Wh, Ww = x.size(2), x.size(3)
  34. # x shape 为 [2, 2688, 96]
  35. x = x.flatten(2).transpose(1, 2)
  36. x = self.norm(x)
  37. # x shape 为 [2, 96, 48, 56]
  38. x = x.transpose(1, 2).view(-1, self.embed_dim, Wh, Ww)
  39. return x

        经过PatchEmbed后的输出维度为[2, 96, 48, 56],之后会再经过一个x.flatten(2).transpose(1, 2)将输入维度转换为transformer block能够接收的输入,即[2, 2688, 96]。

        接下来是BasicLayer

  1. class BasicLayer(nn.Module):
  2. """ A basic Swin Transformer layer for one stage.
  3. Args:
  4. dim (int): Number of feature channels
  5. depth (int): Depths of this stage.
  6. num_heads (int): Number of attention head.
  7. window_size (int): Local window size. Default: 7.
  8. mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. Default: 4.
  9. qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True
  10. qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set.
  11. drop (float, optional): Dropout rate. Default: 0.0
  12. attn_drop (float, optional): Attention dropout rate. Default: 0.0
  13. drop_path (float | tuple[float], optional): Stochastic depth rate. Default: 0.0
  14. norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm
  15. downsample (nn.Module | None, optional): Downsample layer at the end of the layer. Default: None
  16. use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False.
  17. """
  18. def __init__(self,
  19. dim,
  20. depth,
  21. num_heads,
  22. window_size=7,
  23. mlp_ratio=4.,
  24. qkv_bias=True,
  25. qk_scale=None,
  26. drop=0.,
  27. attn_drop=0.,
  28. drop_path=0.,
  29. norm_layer=nn.LayerNorm,
  30. downsample=None,
  31. use_checkpoint=False):
  32. super().__init__()
  33. self.window_size = window_size
  34. self.shift_size = window_size // 2
  35. self.depth = depth
  36. self.use_checkpoint = use_checkpoint
  37. # build blocks
  38. # 序号为偶数的block进行W-MSA,奇数进行SW-MSA
  39. # 这样让输出特征包含local window attention和跨窗口的 window attention
  40. self.blocks = nn.ModuleList([
  41. SwinTransformerBlock(
  42. dim=dim,
  43. num_heads=num_heads,
  44. window_size=window_size,
  45. shift_size=0 if (i % 2 == 0) else window_size // 2,
  46. mlp_ratio=mlp_ratio,
  47. qkv_bias=qkv_bias,
  48. qk_scale=qk_scale,
  49. drop=drop,
  50. attn_drop=attn_drop,
  51. drop_path=drop_path[i] if isinstance(drop_path, list) else drop_path,
  52. norm_layer=norm_layer)
  53. for i in range(depth)])
  54. # patch merging layer
  55. # 只有前三个block执行patch merging,最后一个block不会执行 patch merging
  56. if downsample is not None:
  57. self.downsample = downsample(dim=dim, norm_layer=norm_layer)
  58. else:
  59. self.downsample = None
  60. def forward(self, x, H, W):
  61. """ Forward function.
  62. Args:
  63. x: Input feature, tensor size (B, H*W, C).
  64. H, W: Spatial resolution of the input feature.
  65. """
  66. # calculate attention mask for SW-MSA
  67. Hp = int(np.ceil(H / self.window_size)) * self.window_size
  68. Wp = int(np.ceil(W / self.window_size)) * self.window_size
  69. img_mask = torch.zeros((1, Hp, Wp, 1), device=x.device) # 1 Hp Wp 1
  70. h_slices = (slice(0, -self.window_size),
  71. slice(-self.window_size, -self.shift_size),
  72. slice(-self.shift_size, None))
  73. w_slices = (slice(0, -self.window_size),
  74. slice(-self.window_size, -self.shift_size),
  75. slice(-self.shift_size, None))
  76. cnt = 0
  77. for h in h_slices:
  78. for w in w_slices:
  79. img_mask[:, h, w, :] = cnt
  80. cnt += 1
  81. mask_windows = window_partition(img_mask, self.window_size) # nW, window_size, window_size, 1
  82. mask_windows = mask_windows.view(-1, self.window_size * self.window_size)
  83. attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2)
  84. attn_mask = attn_mask.masked_fill(attn_mask != 0, float(-100.0)).masked_fill(attn_mask == 0, float(0.0))
  85. for blk in self.blocks:
  86. blk.H, blk.W = H, W
  87. if self.use_checkpoint:
  88. x = checkpoint.checkpoint(blk, x, attn_mask)
  89. else:
  90. x = blk(x, attn_mask)
  91. if self.downsample is not None:
  92. x_down = self.downsample(x, H, W)
  93. Wh, Ww = (H + 1) // 2, (W + 1) // 2
  94. return x, H, W, x_down, Wh, Ww
  95. else:
  96. return x, H, W, x, H, W

        BasicLayer构建了一个stage的swin transformer基本结构,包含了带窗(SW-MSA)和不带窗(W-MSA)的transformer block以及一个PatchMerging,可以理解为网络结构图中的swin transformer block + patch merging。

        在训练的过程中序号为偶数的block进行W-MSA,奇数进行SW-MSA,比如说第一个transformer block 有一个W-MSA和一个SW-MSA,先计算W-MSA,再计算SW-MSA,这样让输出特征包含local window attention和跨窗口的 window attention,而patch merging layer 仅在前三个block执行。接下来把这部分代码拆分出来解读。

  1. def window_partition(x, window_size):
  2. """
  3. Args:
  4. x: (B, H, W, C)
  5. window_size (int): window size
  6. Returns:
  7. windows: (num_windows*B, window_size, window_size, C)
  8. """
  9. # B, H, W, C = x.shape
  10. # x = x.view(B, H // window_size, window_size, W // window_size, window_size, C)
  11. # windows = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, C)
  12. H, W = x.shape
  13. x = x.view(H // window_size, window_size, W // window_size, window_size)
  14. windows = x.permute(0, 2, 1, 3).contiguous().view(-1,window_size, window_size)
  15. return windows
  16. Hp = 14
  17. Wp = 14
  18. window_size = 7
  19. shift_size = window_size //2
  20. # img_mask = torch.zeros((1, Hp, Wp, 1)) # 1 Hp Wp 1
  21. img_mask = torch.zeros((Hp, Wp))
  22. h_slices = (slice(0, -window_size),
  23. slice(-window_size, -shift_size),
  24. slice(-shift_size, None))
  25. w_slices = (slice(0, -window_size),
  26. slice(-window_size, -shift_size),
  27. slice(-shift_size, None))
  28. cnt = 0
  29. for h in h_slices:
  30. for w in w_slices:
  31. # img_mask[:, h, w, :] = cnt
  32. img_mask[h, w] = cnt
  33. cnt += 1
  34. print(img_mask)
  35. # tensor([[0., 0., 0., 0., 0., 0., 0.,| 1., 1., 1., 1., 2., 2., 2.],
  36. # [0., 0., 0., 0., 0., 0., 0.,| 1., 1., 1., 1., 2., 2., 2.],
  37. # [0., 0., 0., 0., 0., 0., 0.,| 1., 1., 1., 1., 2., 2., 2.],
  38. # [0., 0., 0., 0., 0., 0., 0.,| 1., 1., 1., 1., 2., 2., 2.],
  39. # [0., 0., 0., 0., 0., 0., 0.,| 1., 1., 1., 1., 2., 2., 2.],
  40. # [0., 0., 0., 0., 0., 0., 0.,| 1., 1., 1., 1., 2., 2., 2.],
  41. # [0., 0., 0., 0., 0., 0., 0.,| 1., 1., 1., 1., 2., 2., 2.],
  42. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  43. # [3., 3., 3., 3., 3., 3., 3.,| 4., 4., 4., 4., 5., 5., 5.],
  44. # [3., 3., 3., 3., 3., 3., 3.,| 4., 4., 4., 4., 5., 5., 5.],
  45. # [3., 3., 3., 3., 3., 3., 3.,| 4., 4., 4., 4., 5., 5., 5.],
  46. # [3., 3., 3., 3., 3., 3., 3.,| 4., 4., 4., 4., 5., 5., 5.],
  47. # [6., 6., 6., 6., 6., 6., 6.,| 7., 7., 7., 7., 8., 8., 8.],
  48. # [6., 6., 6., 6., 6., 6., 6.,| 7., 7., 7., 7., 8., 8., 8.],
  49. # [6., 6., 6., 6., 6., 6., 6.,| 7., 7., 7., 7., 8., 8., 8.]])
  50. mask_windows = window_partition(img_mask, window_size) # nW, window_size, window_size, 1
  51. mask_windows = mask_windows.view(-1, window_size * window_size)
  52. attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2)
  53. attn_mask = attn_mask.masked_fill(attn_mask != 0, float(-100.0)).masked_fill(attn_mask == 0, float(0.0))

        这部分代码是用于生成mask的,源码中的维度比较高,不是很直观,这里将其降为后输出成低维张量,就很直观了,张量的高宽设置为window_size的两倍,这样正好能够完整的看出其中张量做了什么操作,shift_size保持不变,打印出的img_mask可以看出,mask大体上分为四个部分。
        window_partition函数则是将img_mask按照每个部分展开,即将张量分成N个[window_size,window_size]的小窗张量,此时的张量shape为[4,7,7]。之后view成[4,49],再在扩充对应的维度,再相减,张量中不为0的填充为-100,最后得到的attn_mask的shape为[4,49,49],这里的尺寸就和后面自注意力中的大小对应上了。

        具体形式如下图

         其中:

# 白色色块
# [0., 0., 0., 0., 0., 0., 0.,
#  0., 0., 0., 0., 0., 0., 0.,
#  0., 0., 0., 0., 0., 0., 0.,
#  0., 0., 0., 0., 0., 0., 0.,
#  0., 0., 0., 0., 0., 0., 0.,
#  0., 0., 0., 0., 0., 0., 0.,
#  0., 0., 0., 0., 0., 0., 0.],
# 蓝色色块
# [   0.,    0.,    0.,    0., -100., -100., -100.,
#     0.,    0.,    0.,    0., -100., -100., -100.,
#     0.,    0.,    0.,    0., -100., -100., -100.,
#     0.,    0.,    0.,    0., -100., -100., -100.,
#  -100., -100., -100., -100.,    0.,    0.,    0.,
#  -100., -100., -100., -100.,    0.,    0.,    0.,
#  -100., -100., -100., -100.,    0.,    0.,    0.,],
# 黄色色块
# [-100., -100., -100., -100., -100., -100., -100.,
#  -100., -100., -100., -100., -100., -100., -100.,
#  -100., -100., -100., -100., -100., -100., -100.,
#  -100., -100., -100., -100., -100., -100., -100.,
#  -100., -100., -100., -100., -100., -100., -100.,
#  -100., -100., -100., -100., -100., -100., -100.,
#  -100., -100., -100., -100., -100., -100., -100.,]

        实际上输出的attn_mask的shape为[56,49,49],其中的56=(Hp / window_size) * (Wp / window_size)

        之后就是transformer block

  1. class SwinTransformerBlock(nn.Module):
  2. """ Swin Transformer Block.
  3. Args:
  4. dim (int): Number of input channels.
  5. num_heads (int): Number of attention heads.
  6. window_size (int): Window size.
  7. shift_size (int): Shift size for SW-MSA.
  8. mlp_ratio (float): Ratio of mlp hidden dim to embedding dim.
  9. qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True
  10. qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set.
  11. drop (float, optional): Dropout rate. Default: 0.0
  12. attn_drop (float, optional): Attention dropout rate. Default: 0.0
  13. drop_path (float, optional): Stochastic depth rate. Default: 0.0
  14. act_layer (nn.Module, optional): Activation layer. Default: nn.GELU
  15. norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm
  16. """
  17. def __init__(self, dim, num_heads, window_size=7, shift_size=0,
  18. mlp_ratio=4., qkv_bias=True, qk_scale=None, drop=0., attn_drop=0., drop_path=0.,
  19. act_layer=nn.GELU, norm_layer=nn.LayerNorm):
  20. super().__init__()
  21. self.dim = dim
  22. self.num_heads = num_heads
  23. # window_size默认大小 7
  24. self.window_size = window_size
  25. # 进行 SW-MSA shift-size 7//2=3
  26. # 进行 W-MSA shift-size 0
  27. self.shift_size = shift_size
  28. # multi self attention 最后神经网络的隐藏层的维度的倍率
  29. self.mlp_ratio = mlp_ratio
  30. assert 0 <= self.shift_size < self.window_size, "shift_size must in 0-window_size"
  31. self.norm1 = norm_layer(dim)
  32. # local window multi head self attention
  33. self.attn = WindowAttention(
  34. dim, window_size=to_2tuple(self.window_size), num_heads=num_heads,
  35. qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop)
  36. self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
  37. self.norm2 = norm_layer(dim)
  38. mlp_hidden_dim = int(dim * mlp_ratio)
  39. self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop)
  40. self.H = None
  41. self.W = None
  42. def forward(self, x, mask_matrix):
  43. """ Forward function.
  44. Args:
  45. x: Input feature, tensor size (B, H*W, C).
  46. H, W: Spatial resolution of the input feature.
  47. mask_matrix: Attention mask for cyclic shift.
  48. """
  49. # x的shape为[2,2688,96]
  50. B, L, C = x.shape
  51. H, W = self.H, self.W
  52. assert L == H * W, "input feature has wrong size"
  53. shortcut = x
  54. # 进行LN,再将x展开为[2,48,56,96]
  55. x = self.norm1(x)
  56. x = x.view(B, H, W, C)
  57. # pad feature maps to multiples of window size
  58. # 此处需要根据窗的大小对特征图进行pad操作,pad之后的shape为[2,49,56,96]
  59. pad_l = pad_t = 0
  60. pad_r = (self.window_size - W % self.window_size) % self.window_size
  61. pad_b = (self.window_size - H % self.window_size) % self.window_size
  62. x = F.pad(x, (0, 0, pad_l, pad_r, pad_t, pad_b))
  63. _, Hp, Wp, _ = x.shape
  64. # cyclic shift
  65. if self.shift_size > 0:
  66. # 如果是进行 sw-msa 将数据进行变换
  67. shifted_x = torch.roll(x, shifts=(-self.shift_size, -self.shift_size), dims=(1, 2))
  68. attn_mask = mask_matrix
  69. else:
  70. shifted_x = x
  71. attn_mask = None
  72. # partition windows
  73. # x_windows 的shape为[112,7,7,96]
  74. x_windows = window_partition(shifted_x, self.window_size) # nW*B, window_size, window_size, C
  75. x_windows = x_windows.view(-1, self.window_size * self.window_size, C) # nW*B, window_size*window_size, C
  76. # W-MSA/SW-MSA
  77. attn_windows = self.attn(x_windows, mask=attn_mask) # nW*B, window_size*window_size, C
  78. # merge windows
  79. attn_windows = attn_windows.view(-1, self.window_size, self.window_size, C)
  80. # shifted_x的shape为[2,49,56,96]
  81. shifted_x = window_reverse(attn_windows, self.window_size, Hp, Wp) # B H' W' C
  82. # reverse cyclic shift
  83. if self.shift_size > 0:
  84. x = torch.roll(shifted_x, shifts=(self.shift_size, self.shift_size), dims=(1, 2))
  85. else:
  86. x = shifted_x
  87. if pad_r > 0 or pad_b > 0:
  88. # 映射回输入时的大小
  89. x = x[:, :H, :W, :].contiguous()
  90. x = x.view(B, H * W, C)
  91. # FFN
  92. x = shortcut + self.drop_path(x)
  93. x = x + self.drop_path(self.mlp(self.norm2(x)))
  94. return x

        SwinTransformerBlock由LN,W-MSA/SW-MSA以及MLP组成。在W-MSA中输入为[2,2688,96],通过window_partition函数,将输入划分为window_size大小的窗,此时的shape为[112,7,7,96],输入自注意力层时打平成[112,49,96],其中自注意力层就是标准的自注意力结构,这里就不多说了,要是不知道的话可以参考我的另一篇博文:ref

        计算自注意力的代码如下:

  1. class WindowAttention(nn.Module):
  2. """ Window based multi-head self attention (W-MSA) module with relative position bias.
  3. It supports both of shifted and non-shifted window.
  4. Args:
  5. dim (int): Number of input channels.
  6. window_size (tuple[int]): The height and width of the window.
  7. num_heads (int): Number of attention heads.
  8. qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True
  9. qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set
  10. attn_drop (float, optional): Dropout ratio of attention weight. Default: 0.0
  11. proj_drop (float, optional): Dropout ratio of output. Default: 0.0
  12. """
  13. def __init__(self, dim, window_size, num_heads, qkv_bias=True, qk_scale=None, attn_drop=0., proj_drop=0.):
  14. super().__init__()
  15. self.dim = dim
  16. self.window_size = window_size # Wh, Ww
  17. self.num_heads = num_heads
  18. head_dim = dim // num_heads
  19. self.scale = qk_scale or head_dim ** -0.5
  20. # define a parameter table of relative position bias
  21. self.relative_position_bias_table = nn.Parameter(
  22. torch.zeros((2 * window_size[0] - 1) * (2 * window_size[1] - 1), num_heads)) # 2*Wh-1 * 2*Ww-1, nH
  23. # get pair-wise relative position index for each token inside the window
  24. coords_h = torch.arange(self.window_size[0])
  25. coords_w = torch.arange(self.window_size[1])
  26. coords = torch.stack(torch.meshgrid([coords_h, coords_w])) # 2, Wh, Ww
  27. coords_flatten = torch.flatten(coords, 1) # 2, Wh*Ww
  28. relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] # 2, Wh*Ww, Wh*Ww
  29. relative_coords = relative_coords.permute(1, 2, 0).contiguous() # Wh*Ww, Wh*Ww, 2
  30. relative_coords[:, :, 0] += self.window_size[0] - 1 # shift to start from 0
  31. relative_coords[:, :, 1] += self.window_size[1] - 1
  32. relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1
  33. relative_position_index = relative_coords.sum(-1) # Wh*Ww, Wh*Ww
  34. self.register_buffer("relative_position_index", relative_position_index)
  35. self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
  36. self.attn_drop = nn.Dropout(attn_drop)
  37. self.proj = nn.Linear(dim, dim)
  38. self.proj_drop = nn.Dropout(proj_drop)
  39. trunc_normal_(self.relative_position_bias_table, std=.02)
  40. self.softmax = nn.Softmax(dim=-1)
  41. def forward(self, x, mask=None):
  42. """ Forward function.
  43. Args:
  44. x: input features with shape of (num_windows*B, N, C)
  45. mask: (0/-inf) mask with shape of (num_windows, Wh*Ww, Wh*Ww) or None
  46. """
  47. B_, N, C = x.shape
  48. qkv = self.qkv(x).reshape(B_, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
  49. q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple)
  50. q = q * self.scale
  51. attn = (q @ k.transpose(-2, -1))
  52. relative_position_bias = self.relative_position_bias_table[self.relative_position_index.view(-1)].view(
  53. self.window_size[0] * self.window_size[1], self.window_size[0] * self.window_size[1], -1) # Wh*Ww,Wh*Ww,nH
  54. relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous() # nH, Wh*Ww, Wh*Ww
  55. attn = attn + relative_position_bias.unsqueeze(0)
  56. if mask is not None:
  57. nW = mask.shape[0]
  58. attn = attn.view(B_ // nW, nW, self.num_heads, N, N) + mask.unsqueeze(1).unsqueeze(0)
  59. attn = attn.view(-1, self.num_heads, N, N)
  60. attn = self.softmax(attn)
  61. else:
  62. attn = self.softmax(attn)
  63. attn = self.attn_drop(attn)
  64. x = (attn @ v).transpose(1, 2).reshape(B_, N, C)
  65. x = self.proj(x)
  66. x = self.proj_drop(x)
  67. return x

        这里有个细节就是在计算自注意力后,加了个截断正太分布的relative_position_bias

        回归transformer block,算法为了使每个窗之间有相互联系,设计了shift window,这一设计能够大幅降低计算量。在计算SW-MSA时,先要使用torch.roll将输入[2,49,56,96]在1,2维上(也就是高宽上)做滚动。

        下面有个小例子,还是将维度降低,方便看数据形式,容易理解

  1. import torch
  2. a = torch.arange(0, 49).reshape((7, 7))
  3. print(a)
  4. b = torch.roll(a, shifts=(-3, -3), dims=(0, 1))
  5. print(b)
  6. # tensor([[ 0, 1, 2, 3, 4, 5, 6],
  7. # [ 7, 8, 9, 10, 11, 12, 13],
  8. # [14, 15, 16, 17, 18, 19, 20],
  9. # [21, 22, 23, 24, 25, 26, 27],
  10. # [28, 29, 30, 31, 32, 33, 34],
  11. # [35, 36, 37, 38, 39, 40, 41],
  12. # [42, 43, 44, 45, 46, 47, 48]])
  13. # tensor([[24, 25, 26, 27, |21, 22, 23],
  14. # [31, 32, 33, 34, |28, 29, 30],
  15. # [38, 39, 40, 41, |35, 36, 37],
  16. # [45, 46, 47, 48, |42, 43, 44],
  17. # — — - - - - - - - - - - -- - -
  18. # [ 3, 4, 5, 6, | 0, 1, 2],
  19. # [10, 11, 12, 13, | 7, 8, 9],
  20. # [17, 18, 19, 20, |14, 15, 16]])

        也是就论文中的这张图中间部分:

         进入自注意力层后,基本操作和W-MSA一致,就是多加了个之前生成的mask

        最后就是PatchMerging

  1. class PatchMerging(nn.Module):
  2. """ Patch Merging Layer
  3. Args:
  4. dim (int): Number of input channels.
  5. norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm
  6. """
  7. def __init__(self, dim, norm_layer=nn.LayerNorm):
  8. super().__init__()
  9. self.dim = dim
  10. self.reduction = nn.Linear(4 * dim, 2 * dim, bias=False)
  11. self.norm = norm_layer(4 * dim)
  12. def forward(self, x, H, W):
  13. """ Forward function.
  14. Args:
  15. x: Input feature, tensor size (B, H*W, C).
  16. H, W: Spatial resolution of the input feature.
  17. """
  18. B, L, C = x.shape
  19. assert L == H * W, "input feature has wrong size"
  20. x = x.view(B, H, W, C)
  21. # padding
  22. pad_input = (H % 2 == 1) or (W % 2 == 1)
  23. if pad_input:
  24. x = F.pad(x, (0, 0, 0, W % 2, 0, H % 2))
  25. # 这里实现path merging 图片缩小一半
  26. # 0::2 从 0 开始 隔一个点取一个值
  27. # 1::2 从 1 开始 隔一个点取一个值
  28. x0 = x[:, 0::2, 0::2, :] # B H/2 W/2 C
  29. x1 = x[:, 1::2, 0::2, :] # B H/2 W/2 C
  30. x2 = x[:, 0::2, 1::2, :] # B H/2 W/2 C
  31. x3 = x[:, 1::2, 1::2, :] # B H/2 W/2 C
  32. x = torch.cat([x0, x1, x2, x3], -1) # B H/2 W/2 4*C
  33. x = x.view(B, -1, 4 * C) # B H/2*W/2 4*C
  34. x = self.norm(x)
  35. # 降维到2 * dim 图片缩小一半 通道维度增加一倍
  36. x = self.reduction(x)
  37. return x

        PatchMerging实现的功能有点像yolov5中的focus操作,将图像缩小一半,再将通道数增加一倍。

        到这里基本将swin transformer的主体结构讲完了。

做一些补充:

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号