1.输入

1x3x224x224 vit_deit_tiny_patch16_224为例
image.png

2.PatchEmbed

  1. class PatchEmbed(nn.Module):
  2. """ Image to Patch Embedding
  3. """
  4. def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768, norm_layer=None):
  5. super().__init__()
  6. img_size = to_2tuple(img_size)
  7. patch_size = to_2tuple(patch_size)
  8. self.img_size = img_size
  9. self.patch_size = patch_size
  10. self.patch_grid = (img_size[0] // patch_size[0], img_size[1] // patch_size[1])
  11. self.num_patches = self.patch_grid[0] * self.patch_grid[1]
  12. self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
  13. self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity()
  14. def forward(self, x):
  15. # 1x3x224x224
  16. B, C, H, W = x.shape
  17. # FIXME look at relaxing size constraints
  18. assert H == self.img_size[0] and W == self.img_size[1], \
  19. f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]}*{self.img_size[1]})."
  20. # -> 1x192x14x14 -> 1x192x196 -> 1x196x192
  21. x = self.proj(x).flatten(2).transpose(1, 2)
  22. x = self.norm(x)
  23. return x

3.Attention

  1. class Attention(nn.Module):
  2. def __init__(self, dim, num_heads=8, qkv_bias=False, qk_scale=None, attn_drop=0., proj_drop=0.):
  3. super().__init__()
  4. self.num_heads = num_heads
  5. head_dim = dim // num_heads
  6. self.scale = qk_scale or head_dim ** -0.5
  7. # 192 x 576(192*3)
  8. self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
  9. self.attn_drop = nn.Dropout(attn_drop)
  10. self.proj = nn.Linear(dim, dim)
  11. self.proj_drop = nn.Dropout(proj_drop)
  12. def forward(self, x):
  13. # 1x196x192 196=14x14(patch大小) 192(embed_dim)
  14. B, N, C = x.shape
  15. # -> 1x196x576 -> 1x196x3x3x64 -> 3x1x3x196x64
  16. qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
  17. # 1x3x196x64
  18. q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple)
  19. # q -> 1x3x196x64
  20. # k.transpose(-2, -1) -> 1x3x64x196
  21. # attn -> 1x3x196x196
  22. attn = (q @ k.transpose(-2, -1)) * self.scale
  23. attn = attn.softmax(dim=-1)
  24. attn = self.attn_drop(attn)
  25. # 1x3x196x64 -> 1x196x3x64 -> 1x196x192
  26. x = (attn @ v).transpose(1, 2).reshape(B, N, C)
  27. # 1x196x192
  28. x = self.proj(x)
  29. x = self.proj_drop(x)
  30. return x

4.Mlp

  1. class Mlp(nn.Module):
  2. def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.):
  3. super().__init__()
  4. out_features = out_features or in_features
  5. hidden_features = hidden_features or in_features
  6. # in_features:192, hidden_features:768(192x4)
  7. self.fc1 = nn.Linear(in_features, hidden_features)
  8. self.act = act_layer()
  9. self.fc2 = nn.Linear(hidden_features, out_features)
  10. self.drop = nn.Dropout(drop)
  11. def forward(self, x):
  12. # x -> 1x196x192
  13. x = self.fc1(x)
  14. x = self.act(x)
  15. x = self.drop(x)
  16. x = self.fc2(x)
  17. x = self.drop(x)
  18. return x

5.

  1. class VisionTransformer(nn.Module):
  2. """ Vision Transformer
  3. A PyTorch impl of : `An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale`
  4. - https://arxiv.org/abs/2010.11929
  5. Includes distillation token & head support for `DeiT: Data-efficient Image Transformers`
  6. - https://arxiv.org/abs/2012.12877
  7. """
  8. def __init__(self, img_size=224, patch_size=16, in_chans=3, num_classes=1000, embed_dim=768, depth=12,
  9. num_heads=12, mlp_ratio=4., qkv_bias=True, qk_scale=None, representation_size=None, distilled=False,
  10. drop_rate=0., attn_drop_rate=0., drop_path_rate=0., embed_layer=PatchEmbed, norm_layer=None,
  11. act_layer=None, weight_init=''):
  12. """
  13. Args:
  14. img_size (int, tuple): input image size
  15. patch_size (int, tuple): patch size
  16. in_chans (int): number of input channels
  17. num_classes (int): number of classes for classification head
  18. embed_dim (int): embedding dimension
  19. depth (int): depth of transformer
  20. num_heads (int): number of attention heads
  21. mlp_ratio (int): ratio of mlp hidden dim to embedding dim
  22. qkv_bias (bool): enable bias for qkv if True
  23. qk_scale (float): override default qk scale of head_dim ** -0.5 if set
  24. representation_size (Optional[int]): enable and set representation layer (pre-logits) to this value if set
  25. distilled (bool): model includes a distillation token and head as in DeiT models
  26. drop_rate (float): dropout rate
  27. attn_drop_rate (float): attention dropout rate
  28. drop_path_rate (float): stochastic depth rate
  29. embed_layer (nn.Module): patch embedding layer
  30. norm_layer: (nn.Module): normalization layer
  31. weight_init: (str): weight init scheme
  32. """
  33. super().__init__()
  34. self.num_classes = num_classes
  35. self.num_features = self.embed_dim = embed_dim # num_features for consistency with other models
  36. self.num_tokens = 2 if distilled else 1
  37. norm_layer = norm_layer or partial(nn.LayerNorm, eps=1e-6)
  38. act_layer = act_layer or nn.GELU
  39. self.patch_embed = embed_layer(
  40. img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim)
  41. num_patches = self.patch_embed.num_patches
  42. self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
  43. self.dist_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) if distilled else None
  44. # 需要position embedding来编码tokens的位置信息
  45. self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + self.num_tokens, embed_dim))
  46. self.pos_drop = nn.Dropout(p=drop_rate)
  47. dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] # stochastic depth decay rule
  48. self.blocks = nn.Sequential(*[
  49. Block(
  50. dim=embed_dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale,
  51. drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[i], norm_layer=norm_layer, act_layer=act_layer)
  52. for i in range(depth)])
  53. self.norm = norm_layer(embed_dim)
  54. # Representation layer
  55. if representation_size and not distilled:
  56. self.num_features = representation_size
  57. self.pre_logits = nn.Sequential(OrderedDict([
  58. ('fc', nn.Linear(embed_dim, representation_size)),
  59. ('act', nn.Tanh())
  60. ]))
  61. else:
  62. self.pre_logits = nn.Identity()
  63. # Classifier head(s)
  64. self.head = nn.Linear(self.num_features, num_classes) if num_classes > 0 else nn.Identity()
  65. self.head_dist = None
  66. if distilled:
  67. self.head_dist = nn.Linear(self.embed_dim, self.num_classes) if num_classes > 0 else nn.Identity()
  68. # Weight init
  69. assert weight_init in ('jax', 'jax_nlhb', 'nlhb', '')
  70. head_bias = -math.log(self.num_classes) if 'nlhb' in weight_init else 0.
  71. trunc_normal_(self.pos_embed, std=.02)
  72. if self.dist_token is not None:
  73. trunc_normal_(self.dist_token, std=.02)
  74. if weight_init.startswith('jax'):
  75. # leave cls token as zeros to match jax impl
  76. for n, m in self.named_modules():
  77. _init_vit_weights(m, n, head_bias=head_bias, jax_impl=True)
  78. else:
  79. trunc_normal_(self.cls_token, std=.02)
  80. self.apply(_init_vit_weights)
  81. def _init_weights(self, m):
  82. # this fn left here for compat with downstream users
  83. _init_vit_weights(m)
  84. @torch.jit.ignore
  85. def no_weight_decay(self):
  86. return {'pos_embed', 'cls_token', 'dist_token'}
  87. def get_classifier(self):
  88. if self.dist_token is None:
  89. return self.head
  90. else:
  91. return self.head, self.head_dist
  92. def reset_classifier(self, num_classes, global_pool=''):
  93. self.num_classes = num_classes
  94. self.head = nn.Linear(self.embed_dim, num_classes) if num_classes > 0 else nn.Identity()
  95. if self.num_tokens == 2:
  96. self.head_dist = nn.Linear(self.embed_dim, self.num_classes) if num_classes > 0 else nn.Identity()
  97. def forward_features(self, x):
  98. x = self.patch_embed(x)
  99. cls_token = self.cls_token.expand(x.shape[0], -1, -1) # stole cls_tokens impl from Phil Wang, thanks
  100. if self.dist_token is None:
  101. # 增加cls_token进行分类,获取image feature
  102. x = torch.cat((cls_token, x), dim=1)
  103. else:
  104. x = torch.cat((cls_token, self.dist_token.expand(x.shape[0], -1, -1), x), dim=1)
  105. x = self.pos_drop(x + self.pos_embed)
  106. x = self.blocks(x)
  107. x = self.norm(x)
  108. if self.dist_token is None:
  109. # 返回的类似于pooling层后的特征
  110. return self.pre_logits(x[:, 0])
  111. else:
  112. return x[:, 0], x[:, 1]
  113. def forward(self, x):
  114. x = self.forward_features(x)
  115. if self.head_dist is not None:
  116. x, x_dist = self.head(x[0]), self.head_dist(x[1]) # x must be a tuple
  117. if self.training and not torch.jit.is_scripting():
  118. # during inference, return the average of both classifier predictions
  119. return x, x_dist
  120. else:
  121. return (x + x_dist) / 2
  122. else:
  123. x = self.head(x)
  124. return x

https://github.com/lucidrains/mlp-mixer-pytorch