1import torch
2import torch.nn as nn
3
4
5class ViT(nn.Module):
6 """Dosovitskiy et al., 2020 — patches in, attention does the rest."""
7
8 def __init__(self, img_size=224, patch_size=16, in_ch=3,
9 dim=768, depth=12, heads=12, mlp_dim=3072,
10 num_classes=1000):
11 super().__init__()
12 n_patches = (img_size // patch_size) ** 2 # 196 for 224/16
13
14 # Patchify + linear projection in ONE conv call:
15 # a stride-P, kernel-P convolution IS patch extraction + embedding.
16 self.patch_embed = nn.Conv2d(in_ch, dim, kernel_size=patch_size,
17 stride=patch_size)
18
19 self.cls_token = nn.Parameter(torch.zeros(1, 1, dim))
20 self.pos_embed = nn.Parameter(torch.zeros(1, n_patches + 1, dim))
21
22 # Standard pre-norm Transformer encoder -- identical machinery
23 # to a text Transformer, just fed patches instead of words.
24 layer = nn.TransformerEncoderLayer(
25 d_model=dim, nhead=heads, dim_feedforward=mlp_dim,
26 activation="gelu", norm_first=True, batch_first=True,
27 )
28 self.encoder = nn.TransformerEncoder(layer, num_layers=depth)
29 self.head = nn.Linear(dim, num_classes)
30
31 def forward(self, x: torch.Tensor) -> torch.Tensor:
32 B = x.shape[0]
33 x = self.patch_embed(x) # (B, dim, 14, 14)
34 x = x.flatten(2).transpose(1, 2) # (B, 196, dim) -- 196 "words"
35
36 cls = self.cls_token.expand(B, -1, -1)
37 x = torch.cat([cls, x], dim=1) # (B, 197, dim)
38 x = x + self.pos_embed # learned position info
39
40 x = self.encoder(x) # every patch attends to every patch
41 return self.head(x[:, 0]) # classify from the [class] token
42
43
44vit = ViT()
45out = vit(torch.randn(1, 3, 224, 224))
46print(out.shape) # torch.Size([1, 1000])