| 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)
|
| x = x.flatten(2)
|
| x = x.transpose(1, 2)
|
| 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]
|
|
|
| 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 |