import torch import numpy as np import torch.nn as nn from dataclasses import dataclass @dataclass class ModelConfig: img_size: int = 32 patch_size: int = 4 in_channels: int = 3 embed_dim: int = 768 config = ModelConfig() class PatchEmbeddings(nn.Module): def __init__(self, img_size, patch_size, in_channels, embed_dim): super().__init__() self.img_size = img_size self.patch_size = patch_size self.in_channels = in_channels self.embed_dim = embed_dim self.proj = nn.Conv2d( in_channels, embed_dim, kernel_size = patch_size, stride=patch_size ) def forward(self, x): x = self.proj(x) # [batch, embed, H/P(8), W/P(8)] x = x.flatten(2) # [batch, embed, 64 (8 x 8 )] x = x.transpose(1, 2) #[batch, 64, embed] return x class Attention(nn.Module): def __init__(self, dim, n_heads=12, qkv_bias=True, attn_drop=0., proj_drop=0.): super().__init__() self.n_heads = n_heads self.dim = dim self.head_dim = dim // n_heads self.scale = self.head_dim ** -0.5 self.qkv = nn.Linear(dim, dim*3, bias=qkv_bias) self.attn_drop = nn.Dropout(attn_drop) self.proj = nn.Linear(dim, dim) self.proj_drop = nn.Dropout(proj_drop) def forward(self, x): B, N, C = x.shape qkv = self.qkv(x).reshape(B, N, 3, self.n_heads, self.head_dim).permute(2, 0, 3, 1, 4) q, k, v = qkv[0], qkv[1], qkv[2] # (B, H, N, D) attn = (q @ k.transpose(-2, -1)) * self.scale attn = attn.softmax(dim=-1) attn = self.attn_drop(attn) x = (attn @ v).transpose(1, 2).reshape(B, N, C) x = self.proj(x) x = self.proj_drop(x) return x class MLP(nn.Module): def __init__(self, in_features, hidden_features=None, out_features=None, drop=0.): super().__init__() out_features = out_features or in_features hidden_features = hidden_features or in_features self.fc1 = nn.Linear(in_features, hidden_features) self.act = nn.GELU() self.fc2 = nn.Linear(hidden_features, out_features) self.drop = nn.Dropout(drop) def forward(self, x): x = self.fc1(x) x = self.act(x) x = self.drop(x) x = self.fc2(x) x = self.drop(x) return x class Block(nn.Module): def __init__(self, dim, n_heads, mlp_ratio=4., qkv_bias=True, attn_drop=0., proj_drop=0.): super().__init__() self.norm1 = nn.LayerNorm(dim) self.attn = Attention(dim=dim, n_heads=n_heads, qkv_bias=qkv_bias, attn_drop=attn_drop, proj_drop=proj_drop) self.norm2 = nn.LayerNorm(dim) self.mlp = MLP( in_features = dim, hidden_features = int(dim * mlp_ratio), drop = proj_drop ) def forward(self, x): x = x + self.attn(self.norm1(x)) x = x + self.mlp(self.norm2(x)) return x class VisionTransformer(nn.Module): def __init__( self, img_size=config.img_size, patch_size=config.patch_size, in_channels=config.in_channels, num_classes = 10, embed_dim = 768, depth=12, n_heads=12, mlp_ratio=4, qkv_bias=True, attn_drop=0., proj_drop=0., ): super().__init__() self.num_classes=num_classes self.embed_dim=embed_dim self.patch_embed=PatchEmbeddings( img_size=img_size, patch_size=patch_size, in_channels=in_channels, embed_dim=embed_dim ) num_patches = (img_size // patch_size) ** 2 self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) self.pos_embed = nn.Parameter(torch.zeros(1, num_patches+1, embed_dim)) self.pos_drop = nn.Dropout(p=proj_drop) self.blocks = nn.Sequential(*[ Block( dim=embed_dim, n_heads=n_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, attn_drop=attn_drop, proj_drop=proj_drop ) for _ in range(depth) ]) self.norm = nn.LayerNorm(embed_dim) self.head = nn.Linear(embed_dim, num_classes) if num_classes > 0 else nn.Identity() self._init_weights() def _init_weights(self): torch.nn.init.normal_(self.cls_token, std=0.02) torch.nn.init.normal_(self.pos_embed, std=0.02) self.apply(self._init_other) def _init_other(self, m): if isinstance(m, nn.Linear): torch.nn.init.xavier_uniform_(m.weight) if isinstance(m, nn.Linear) and m.bias is not None: nn.init.constant_(m.bias, 0) elif isinstance(m ,nn.LayerNorm): nn.init.constant_(m.bias, 0) nn.init.constant_(m.weight, 1.0) def forward(self, x): B = x.shape[0] x = self.patch_embed(x) cls_token = self.cls_token.expand(B, -1, -1) x = torch.cat((cls_token, x), dim=1) x = x + self.pos_embed x = self.pos_drop(x) x = self.blocks(x) x = self.norm(x) cls_final = x[:, 0] logits = self.head(cls_final) return logits