vit-casting-defect / model.py
Zainulabedin1's picture
Add missing model.py
998740d verified
Raw History Blame Contribute Delete
2.77 kB
"""TinyViT for surface-defect image classification (standalone, no project deps)."""
import torch
import torch.nn as nn
class TinyViT(nn.Module):
"""Image-classification ViT: img_size x img_size -> patch x patch patches."""
def __init__(self, img_size: int = 64, patch: int = 8, d_model: int = 64,
n_layers: int = 2, n_heads: int = 4, n_classes: int = 6,
in_ch: int = 1, dropout: float = 0.1):
super().__init__()
assert img_size % patch == 0, "img_size must be divisible by patch"
self.img_size = img_size
self.patch = patch
self.n_patches = (img_size // patch) ** 2
self.patch_embed = nn.Conv2d(in_ch, d_model,
kernel_size=patch, stride=patch)
self.cls = nn.Parameter(torch.zeros(1, 1, d_model))
self.pos = nn.Parameter(
torch.zeros(1, self.n_patches + 1, d_model))
layer = nn.TransformerEncoderLayer(
d_model=d_model, nhead=n_heads,
dim_feedforward=4 * d_model, dropout=dropout,
batch_first=True, activation="gelu")
self.encoder = nn.TransformerEncoder(layer, num_layers=n_layers)
self.norm = nn.LayerNorm(d_model)
self.head = nn.Sequential(
nn.Dropout(dropout),
nn.Linear(d_model, d_model),
nn.GELU(),
nn.Linear(d_model, n_classes))
def forward(self, x):
B = x.shape[0]
t = self.patch_embed(x).flatten(2).transpose(1, 2) # (B, P, d)
t = torch.cat([self.cls.expand(B, -1, -1), t], dim=1) + self.pos
t = self.encoder(t)
return self.head(self.norm(t)[:, 0])
CLASS_NAMES = ["crazing", "inclusion", "patches",
"pitted_surface", "rolled-in_scale", "scratches"]
# Normalisation statistics from training (uint8/255 scale)
MEAN = 0.5013
STD = 0.2075
def load_model(weights_path: str, device: str = "cpu") -> TinyViT:
model = TinyViT()
state = torch.load(weights_path, map_location=device)
model.load_state_dict(state)
model.eval()
return model
def predict_image(model: TinyViT, image) -> tuple[int, "np.ndarray"]:
"""Classify a PIL image or numpy array (grayscale or RGB).
Returns (predicted_class_id, class_probabilities[6]).
"""
import numpy as np
from PIL import Image
if isinstance(image, np.ndarray):
image = Image.fromarray(image)
image = image.convert("L").resize((64, 64), Image.BILINEAR)
x = np.asarray(image, dtype=np.float32) / 255.0
x = (x - MEAN) / STD
xt = torch.from_numpy(x)[None, None].to(next(model.parameters()).device)
with torch.no_grad():
probs = torch.softmax(model(xt), dim=1).cpu().numpy()[0]
return int(probs.argmax()), probs