TinySSL / app.py
emranabdu
Upload app.py
e99f416 verified
Raw
History Blame Contribute Delete
5.28 kB
import gradio as gr
import torch
import torch.nn.functional as F
from torchvision import transforms
from huggingface_hub import hf_hub_download
import numpy as np
from PIL import Image
class TinySSLBase(torch.nn.Module):
def __init__(self, out_dim=256):
super().__init__()
self.patch_embed = torch.nn.Conv2d(3, 256, kernel_size=3, stride=4, padding=1)
self.proj = torch.nn.Linear(256, out_dim)
encoder_layer = torch.nn.TransformerEncoderLayer(d_model=out_dim, nhead=4, dim_feedforward=512, batch_first=True, dropout=0.1)
self.encoder = torch.nn.TransformerEncoder(encoder_layer, num_layers=4)
self.cls_token = torch.nn.Parameter(torch.randn(1, 1, out_dim) * 0.02)
self.out_dim = out_dim
def forward(self, x):
B = x.shape[0]
x = self.patch_embed(x).flatten(2).transpose(1, 2)
x = self.proj(x)
cls = self.cls_token.expand(B, -1, -1)
x = torch.cat([cls, x], dim=1)
x = self.encoder(x)
return {"cls": x[:, 0], "patches": x[:, 1:]}
class TinySSLTiny(torch.nn.Module):
def __init__(self, out_dim=128):
super().__init__()
self.patch_embed = torch.nn.Conv2d(3, 128, kernel_size=3, stride=4, padding=1)
self.proj = torch.nn.Linear(128, out_dim)
encoder_layer = torch.nn.TransformerEncoderLayer(d_model=out_dim, nhead=4, dim_feedforward=256, batch_first=True, dropout=0.1)
self.encoder = torch.nn.TransformerEncoder(encoder_layer, num_layers=2)
self.out_dim = out_dim
def forward(self, x):
x = self.patch_embed(x).flatten(2).transpose(1, 2)
x = self.proj(x)
x = self.encoder(x)
cls = x.mean(dim=1)
return {"cls": cls, "patches": x}
class TinySSLCNN(torch.nn.Module):
def __init__(self, out_dim=256):
super().__init__()
self.blocks = torch.nn.Sequential(
torch.nn.Conv2d(3, 32, 3, stride=2, padding=1), torch.nn.BatchNorm2d(32), torch.nn.ReLU(inplace=True),
torch.nn.Conv2d(32, 64, 3, stride=2, padding=1), torch.nn.BatchNorm2d(64), torch.nn.ReLU(inplace=True),
torch.nn.Conv2d(64, 128, 3, stride=2, padding=1), torch.nn.BatchNorm2d(128), torch.nn.ReLU(inplace=True),
torch.nn.Conv2d(128, 256, 3, stride=2, padding=1), torch.nn.BatchNorm2d(256), torch.nn.ReLU(inplace=True),
)
self.head = torch.nn.Linear(256, out_dim)
self.out_dim = out_dim
def forward(self, x):
feat = self.blocks(x)
cls = self.head(feat.mean(dim=(2, 3)))
patches = feat.flatten(2).transpose(1, 2)
return {"cls": cls, "patches": patches}
MODEL_MAP = {"base": TinySSLBase, "tiny": TinySSLTiny, "cnn": TinySSLCNN}
DATASET_INFO = {"flowers102": 102, "oxford_pets": 37, "eurosat": 10, "breastmnist": 2}
DATASET_LABELS = {
"flowers102": [f"Flower {i}" for i in range(102)],
"eurosat": ["AnnualCrop", "Forest", "HerbaceousVegetation", "Highway", "Industrial", "Pasture", "PermanentCrop", "Residential", "River", "SeaLake"],
"oxford_pets": [f"Pet Class {i}" for i in range(37)],
"breastmnist": ["Malignant", "Benign"],
}
IMAGENET_MEAN = [0.485, 0.456, 0.406]
IMAGENET_STD = [0.229, 0.224, 0.225]
eval_t = transforms.Compose([
transforms.Resize(256), transforms.CenterCrop(224),
transforms.ToTensor(), transforms.Normalize(IMAGENET_MEAN, IMAGENET_STD),
])
loaded_models = {}
def load_model(model_name, dataset):
key = f"{model_name}_{dataset}"
if key in loaded_models:
return loaded_models[key]
ckpt_path = hf_hub_download(repo_id="emran696996966/TinySSL", filename=f"checkpoints/{key}.pt")
model_cls = MODEL_MAP[model_name]
model = model_cls()
model.load_state_dict(torch.load(ckpt_path, weights_only=False)["model"])
model.eval()
num_classes = DATASET_INFO[dataset]
head = torch.nn.Linear(model.out_dim, num_classes)
loaded_models[key] = (model, head)
return model, head
def predict(image, model_name, dataset):
if image is None:
return "Please upload an image."
img = Image.fromarray(np.array(image)).convert("RGB")
x = eval_t(img).unsqueeze(0)
model, head = load_model(model_name, dataset)
with torch.no_grad():
feat = model(x)["cls"]
logits = head(feat)
probs = F.softmax(logits, dim=-1)[0]
top5 = torch.topk(probs, min(5, probs.shape[-1]))
labels = DATASET_LABELS.get(dataset, [f"Class {i}" for i in range(DATASET_INFO[dataset])])
lines = []
for prob, idx in zip(top5.values, top5.indices):
label = labels[idx.item()] if idx.item() < len(labels) else f"Class {idx.item()}"
lines.append(f"{label}: {prob.item()*100:.1f}%")
return "\n".join(lines)
demo = gr.Interface(
fn=predict,
inputs=[
gr.Image(label="Upload Image"),
gr.Dropdown(choices=["base", "tiny", "cnn"], value="base", label="Model"),
gr.Dropdown(choices=["flowers102", "oxford_pets", "eurosat", "breastmnist"], value="flowers102", label="Dataset"),
],
outputs=gr.Textbox(label="Top-5 Predictions"),
title="TinySSL: Distilling DINOv2 into Tiny Vision Models",
description="2M-param students trained on DINOv2 features. Upload an image to classify.",
)
if __name__ == "__main__":
demo.launch()