Download LW_VIT.py from IncreasingLoss/ChineseCharacterClassifier_LW_Vit: direct link, hf CLI and curl.
- Browser
- Download file 6.88 kB
-
https://huggingface.co/IncreasingLoss/ChineseCharacterClassifier_LW_Vit/resolve/main/LW_VIT.py
- Command line
-
hf download hf://IncreasingLoss/ChineseCharacterClassifier_LW_Vit/LW_VIT.py
-
curl -L -o LW_VIT.py https://huggingface.co/IncreasingLoss/ChineseCharacterClassifier_LW_Vit/resolve/main/LW_VIT.py
6.88 kB
| """LW-Vit""" | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| class HSwish(nn.Module): | |
| """Hard Swish activation function""" | |
| def forward(self, x): | |
| out = x * F.relu6(x + 3, inplace=True) / 6 | |
| return out | |
| class MV2_Block(nn.Module): | |
| """MobileNetV2 Inverted Residual Block with h-swish activation""" | |
| def __init__(self, in_channels, expand_channels, out_channels, stride): | |
| super().__init__() | |
| self.stride = stride | |
| self.use_res_connect = self.stride == 1 and in_channels == out_channels | |
| self.expand = nn.Sequential( | |
| nn.Conv2d(in_channels, expand_channels, kernel_size=1, stride=1, padding=0, bias=False), | |
| nn.BatchNorm2d(expand_channels), | |
| HSwish() | |
| ) | |
| self.depthwise = nn.Sequential( | |
| nn.Conv2d(expand_channels, expand_channels, kernel_size=3, stride=stride, | |
| padding=1, groups=expand_channels, bias=False), | |
| nn.BatchNorm2d(expand_channels), | |
| HSwish() | |
| ) | |
| self.project = nn.Sequential( | |
| nn.Conv2d(expand_channels, out_channels, kernel_size=1, stride=1, padding=0, bias=False), | |
| nn.BatchNorm2d(out_channels) | |
| ) | |
| def forward(self, x): | |
| if self.use_res_connect: | |
| return x + self.project(self.depthwise(self.expand(x))) | |
| else: | |
| return self.project(self.depthwise(self.expand(x))) | |
| class LW_ViT_Transformer_Block(nn.Module): | |
| """Lightweight Vision Transformer Block""" | |
| def __init__(self, in_channels, out_channels, patch_size=2, heads=4, dim=128): | |
| super().__init__() | |
| self.patch_size = patch_size | |
| self.in_channels = in_channels | |
| self.out_channels = out_channels | |
| self.dim = dim | |
| # Downsampling and channel adjustment | |
| self.downsample = nn.Conv2d(in_channels, in_channels, kernel_size=3, stride=2, padding=1) | |
| self.channel_adjust = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1) | |
| # Calculate patch dimension | |
| patch_dim = out_channels * patch_size * patch_size | |
| # Add projection layer if needed | |
| self.projection = nn.Identity() if patch_dim == dim else nn.Linear(patch_dim, dim) | |
| # Transformer components | |
| self.norm1 = nn.LayerNorm(dim) | |
| self.attn = nn.MultiheadAttention(embed_dim=dim, num_heads=heads, batch_first=True) | |
| self.norm2 = nn.LayerNorm(dim) | |
| self.ffn = nn.Sequential( | |
| nn.Linear(dim, dim * 4), | |
| nn.GELU(), | |
| nn.Linear(dim * 4, dim) | |
| ) | |
| # Final processing | |
| self.final_conv = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False) | |
| self.norm_out = nn.BatchNorm2d(out_channels) | |
| self.act = HSwish() | |
| def forward(self, x): | |
| # Initial downsampling and channel adjustment | |
| x = self.downsample(x) | |
| x = self.channel_adjust(x) | |
| B, C, H, W = x.shape | |
| # Convert feature map to patches and embed | |
| # Reshape to [B, C, H/P, P, W/P, P] | |
| x_reshaped = x.reshape(B, C, H // self.patch_size, self.patch_size, | |
| W // self.patch_size, self.patch_size) | |
| # Permute to [B, H/P, W/P, C, P, P] | |
| x_permuted = x_reshaped.permute(0, 2, 4, 1, 3, 5) | |
| # Reshape to [B, H/P * W/P, C * P * P] | |
| patches = x_permuted.reshape(B, (H // self.patch_size) * (W // self.patch_size), -1) | |
| # Apply projection if needed | |
| patches = self.projection(patches) | |
| # Apply transformer operations | |
| normed_patches = self.norm1(patches) | |
| attn_out, _ = self.attn(normed_patches, normed_patches, normed_patches) | |
| patches = patches + attn_out | |
| normed_patches = self.norm2(patches) | |
| ffn_out = self.ffn(normed_patches) | |
| patches = patches + ffn_out | |
| # Reshape back to feature map | |
| # Reshape to [B, H/P, W/P, C, P, P] | |
| x_back = patches.reshape(B, H // self.patch_size, W // self.patch_size, | |
| C, self.patch_size, self.patch_size) | |
| # Permute to [B, C, H/P, P, W/P, P] | |
| x_back = x_back.permute(0, 3, 1, 4, 2, 5) | |
| # Reshape to [B, C, H, W] | |
| x_out = x_back.reshape(B, C, H, W) | |
| # Final processing | |
| out = self.final_conv(x_out) | |
| out = self.norm_out(out) | |
| out = self.act(out) | |
| return out | |
| class LW_ViT(nn.Module): | |
| """Lightweight Vision Transformer for Chinese Character Recognition""" | |
| def __init__(self, base_channels=16, num_classes=3892): | |
| super().__init__() | |
| # Stem layer | |
| self.stem = nn.Sequential( | |
| nn.Conv2d(3, base_channels, 3, stride=2, padding=1, bias=False), | |
| nn.BatchNorm2d(base_channels), | |
| HSwish() | |
| ) | |
| # Feature extraction layers | |
| self.features = nn.Sequential( | |
| # MV2 blocks | |
| MV2_Block(base_channels, 4*base_channels, base_channels, stride=1), | |
| MV2_Block(base_channels, 4*base_channels, base_channels, stride=2), | |
| MV2_Block(base_channels, 4*base_channels, base_channels, stride=1), | |
| MV2_Block(base_channels, 4*base_channels, base_channels, stride=2), | |
| # Transformer block | |
| LW_ViT_Transformer_Block( | |
| in_channels=base_channels, | |
| out_channels=2*base_channels, | |
| patch_size=2, | |
| heads=4, | |
| dim=128 | |
| ), | |
| # Final convolutional layer | |
| nn.Conv2d(2*base_channels, 4*base_channels, kernel_size=1, stride=1), | |
| nn.BatchNorm2d(4*base_channels), | |
| HSwish() | |
| ) | |
| # Global pooling and classifier | |
| self.global_pool = nn.AdaptiveAvgPool2d(1) | |
| self.dropout = nn.Dropout(0.1) # Added dropout for regularization | |
| self.classifier = nn.Sequential( | |
| nn.Linear(4*base_channels, 8*base_channels), | |
| nn.BatchNorm1d(8*base_channels), | |
| HSwish(), | |
| nn.Dropout(0.1), # Added dropout for regularization | |
| nn.Linear(8*base_channels, num_classes) | |
| ) | |
| def forward(self, x): | |
| x = self.stem(x) | |
| x = self.features(x) | |
| x = self.global_pool(x) | |
| x = torch.flatten(x, 1) | |
| x = self.dropout(x) | |
| x = self.classifier(x) | |
| return x | |
| num_cls = 3892 | |
| num_epochs = 100 | |
| user_device = "cuda" if torch.cuda.is_available() else "cpu" | |
| LW_model = LW_ViT(base_channels=16, num_classes=num_cls) |