Image Feature Extraction
Transformers
Safetensors
clip_vitb_mini
feature-extraction
clip
knowledge-distillation
consensus-distillation
vit
custom_code
Instructions to use AbstractPhil/clip-vitb-mini-distilled with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use AbstractPhil/clip-vitb-mini-distilled with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-feature-extraction", model="AbstractPhil/clip-vitb-mini-distilled", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("AbstractPhil/clip-vitb-mini-distilled", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 4,233 Bytes
dac64fd a403c2d dac64fd a403c2d dac64fd a403c2d dac64fd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | """Standalone loader for clip-vitb-mini-distilled (no repo imports).
Usage:
from loader import load_model, embed_images
model = load_model("student.pt") # or a hf_hub_download path
emb = embed_images(model, batch01) # (B,3,H,W) in [0,1] -> (B,512)
The student is an 8.66M-parameter ViT (d=240, depth 12, heads 4, patch 16,
img 160) distilled from CLIP ViT-B/16 (LAION-2B) pooled image features on
COCO-2017 train (118,287 images, ~17 epochs). Outputs live in the teacher's
512-d projection space: compatible with the teacher's TEXT tower for
zero-shot/retrieval. See the model card for measured capability and caveats.
"""
import torch
import torch.nn as nn
import torch.nn.functional as F
CLIP_MEAN = (0.48145466, 0.4578275, 0.40821073)
CLIP_STD = (0.26862954, 0.26130258, 0.27577711)
class Block(nn.Module):
def __init__(self, d, heads):
super().__init__()
self.n1 = nn.LayerNorm(d)
self.qkv = nn.Linear(d, 3 * d)
self.proj = nn.Linear(d, d)
self.n2 = nn.LayerNorm(d)
self.fc1 = nn.Linear(d, 4 * d)
self.fc2 = nn.Linear(4 * d, d)
self.heads = heads
def forward(self, x):
B, N, C = x.shape
q, k, v = (self.qkv(self.n1(x))
.reshape(B, N, 3, self.heads, C // self.heads)
.permute(2, 0, 3, 1, 4))
a = F.scaled_dot_product_attention(q, k, v)
x = x + self.proj(a.transpose(1, 2).reshape(B, N, C))
return x + self.fc2(F.gelu(self.fc1(self.n2(x))))
class Student(nn.Module):
"""CLS-token readout, Linear head to the teacher's 512-d space."""
def __init__(self, out_dim=512, d=240, depth=12, heads=4, patch=16,
img=160):
super().__init__()
self.patch = nn.Conv2d(3, d, patch, patch)
self.cls = nn.Parameter(torch.zeros(1, 1, d))
self.pos = nn.Parameter(torch.zeros(1, (img // patch) ** 2 + 1, d))
self.blocks = nn.ModuleList(Block(d, heads) for _ in range(depth))
self.norm = nn.LayerNorm(d)
self.head = nn.Linear(d, out_dim)
def forward_features(self, x):
x = self.patch(x).flatten(2).transpose(1, 2)
x = torch.cat([self.cls.expand(x.shape[0], -1, -1), x], 1) + self.pos
for b in self.blocks:
x = b(x)
return self.norm(x)[:, 0]
def forward(self, x):
return self.head(self.forward_features(x))
def load_model(path, device=None):
device = device or ("cuda" if torch.cuda.is_available() else "cpu")
ck = torch.load(path, map_location="cpu", weights_only=True)
sd = ck["state_dict"] if "state_dict" in ck else ck
model = Student(out_dim=sd["head.weight"].shape[0])
model.load_state_dict(sd, strict=True)
return model.to(device).eval()
def load_rotation(path):
"""Load a frame rotation shipped beside a consensus-distilled student.
The rotation maps the student's consensus frame into the CLIP-B/16
(LAION-2B) deployment frame; it was fitted once, offline, on 2,500
COCO-val pairs (fp64 orthogonal Procrustes) and is FROZEN."""
d = torch.load(path, map_location="cpu", weights_only=True)
return d["R"] if isinstance(d, dict) else d
@torch.no_grad()
def embed_images(model, x01, batch=256, rotation=None):
"""x01: (B,3,H,W) float in [0,1]. Returns L2-normalized (B,512).
Pass `rotation` (from load_rotation) with the consensus-distilled
champion to place outputs in the LAION text tower's frame — required
for zero-shot/retrieval against that tower, and how every headline
number on the card was measured."""
dev = next(model.parameters()).device
mean = torch.tensor(CLIP_MEAN).view(1, 3, 1, 1).to(dev)
std = torch.tensor(CLIP_STD).view(1, 3, 1, 1).to(dev)
out = []
for i in range(0, len(x01), batch):
x = x01[i:i + batch].to(dev)
x = F.interpolate(x, size=(160, 160), mode="bicubic",
align_corners=False).clamp(0, 1)
z = F.normalize(model((x - mean) / std), dim=-1)
if rotation is not None:
z = F.normalize(z.double() @ rotation.to(dev).double(),
dim=-1).float()
out.append(z.cpu())
return torch.cat(out)
|