Spaces:
Sleeping
Sleeping
Add DreamSim (human-aligned) encoder: LoRA-tuned DINO ViT-B/16 patch tokens + CLS attention
Browse files- app.py +32 -14
- requirements.txt +1 -0
app.py
CHANGED
|
@@ -33,6 +33,7 @@ MODELS = {
|
|
| 33 |
"DINOv3 ViT-7B · 6.7B (needs GPU)": dict(repo="facebook/dinov3-vit7b16-pretrain-lvd1689m", kind="dinov3", patch=16, norm=IMNET),
|
| 34 |
"MAE ViT-L (pixel-reconstruction)": dict(repo="facebook/vit-mae-large", kind="mae", patch=16, norm=IMNET),
|
| 35 |
"CLIP ViT-L/14 (language-aligned)": dict(repo="openai/clip-vit-large-patch14", kind="clip", patch=14, norm=CLIPN),
|
|
|
|
| 36 |
}
|
| 37 |
DEFAULT_MODEL = next(iter(MODELS))
|
| 38 |
|
|
@@ -40,25 +41,38 @@ TREE_HTML = open("tree_widget.html").read()
|
|
| 40 |
REVEAL_HTML = open("reveal_widget.html").read()
|
| 41 |
|
| 42 |
# single-slot cache: keep only the most-recently-used model (avoids OOM when switching)
|
| 43 |
-
_cur = {"repo": None, "model": None}
|
| 44 |
def get_model(spec):
|
| 45 |
repo = spec["repo"]
|
| 46 |
if _cur["repo"] != repo:
|
| 47 |
-
_cur["model"] = None
|
| 48 |
import gc; gc.collect()
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
mdl
|
| 55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
_cur["repo"] = repo
|
| 57 |
return _cur["model"]
|
| 58 |
|
| 59 |
|
| 60 |
def encode(model, spec, x):
|
| 61 |
"""-> (feat[P,C], att[P]) with patches in spatial row-major order."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
with torch.no_grad():
|
| 63 |
o = model(x, output_attentions=True)
|
| 64 |
h, a = o.last_hidden_state, o.attentions[-1][0] # a: (heads, S, S)
|
|
@@ -76,10 +90,13 @@ def build_tree(image, model_label):
|
|
| 76 |
spec = MODELS.get(model_label, MODELS[DEFAULT_MODEL])
|
| 77 |
model = get_model(spec)
|
| 78 |
mdt = next(model.parameters()).dtype
|
| 79 |
-
mean, std = spec["norm"]
|
| 80 |
img = image.convert("RGB").resize((RES, RES), Image.BICUBIC)
|
| 81 |
-
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
feat, att = encode(model, spec, x)
|
| 84 |
att_pct = 100.0 * att / att.sum()
|
| 85 |
P = feat.shape[0]; g = int(round(P ** 0.5))
|
|
@@ -153,9 +170,10 @@ if gr is not None:
|
|
| 153 |
btn = gr.Button("Build tree + reveal", variant="primary")
|
| 154 |
gr.Markdown("<small>Compare encoder families: **DINOv3** (self-supervised, semantic), "
|
| 155 |
"**MAE** (pixel-reconstruction — more texture/appearance driven), "
|
| 156 |
-
"**CLIP** (language-aligned — 16×16 grid, patch-14)
|
|
|
|
| 157 |
"ViT-H+/7B are large — slow on the free CPU; **ViT-7B realistically needs a GPU Space**. "
|
| 158 |
-
"First use of each model downloads its weights (one-time).</small>")
|
| 159 |
with gr.Tab("Condensed tree (hover)"):
|
| 160 |
out_tree = gr.HTML()
|
| 161 |
with gr.Tab("Reveal animation"):
|
|
|
|
| 33 |
"DINOv3 ViT-7B · 6.7B (needs GPU)": dict(repo="facebook/dinov3-vit7b16-pretrain-lvd1689m", kind="dinov3", patch=16, norm=IMNET),
|
| 34 |
"MAE ViT-L (pixel-reconstruction)": dict(repo="facebook/vit-mae-large", kind="mae", patch=16, norm=IMNET),
|
| 35 |
"CLIP ViT-L/14 (language-aligned)": dict(repo="openai/clip-vit-large-patch14", kind="clip", patch=14, norm=CLIPN),
|
| 36 |
+
"DreamSim (human-aligned)": dict(repo="dreamsim:dino_vitb16", kind="dreamsim", patch=16, norm=None),
|
| 37 |
}
|
| 38 |
DEFAULT_MODEL = next(iter(MODELS))
|
| 39 |
|
|
|
|
| 41 |
REVEAL_HTML = open("reveal_widget.html").read()
|
| 42 |
|
| 43 |
# single-slot cache: keep only the most-recently-used model (avoids OOM when switching)
|
| 44 |
+
_cur = {"repo": None, "model": None, "pre": None}
|
| 45 |
def get_model(spec):
|
| 46 |
repo = spec["repo"]
|
| 47 |
if _cur["repo"] != repo:
|
| 48 |
+
_cur["model"] = _cur["pre"] = None
|
| 49 |
import gc; gc.collect()
|
| 50 |
+
print(f"[app] loading {repo}", flush=True)
|
| 51 |
+
if spec["kind"] == "dreamsim":
|
| 52 |
+
from dreamsim import dreamsim as _ds # lazy: heavy dep, only when selected
|
| 53 |
+
cache = os.environ.get("DREAMSIM_CACHE", "/tmp/dreamsim_cache")
|
| 54 |
+
os.makedirs(cache, exist_ok=True)
|
| 55 |
+
mdl, pre = _ds(pretrained=True, dreamsim_type="dino_vitb16", cache_dir=cache, device=DEV)
|
| 56 |
+
_cur["model"], _cur["pre"] = mdl.eval(), pre
|
| 57 |
+
else:
|
| 58 |
+
dt = torch.bfloat16 if "vit7b16" in repo else torch.float32 # 7B in bf16 to fit
|
| 59 |
+
cls = {"dinov3": AutoModel, "mae": ViTMAEModel, "clip": CLIPVisionModel}[spec["kind"]]
|
| 60 |
+
mdl = cls.from_pretrained(repo, dtype=dt, token=TOKEN, attn_implementation="eager")
|
| 61 |
+
if spec["kind"] == "mae":
|
| 62 |
+
mdl.config.mask_ratio = 0.0 # keep ALL patches (MAE masks 75% by default)
|
| 63 |
+
_cur["model"] = mdl.eval().to(DEV)
|
| 64 |
_cur["repo"] = repo
|
| 65 |
return _cur["model"]
|
| 66 |
|
| 67 |
|
| 68 |
def encode(model, spec, x):
|
| 69 |
"""-> (feat[P,C], att[P]) with patches in spatial row-major order."""
|
| 70 |
+
if spec["kind"] == "dreamsim": # LoRA-tuned DINO ViT-B/16 inside DreamSim
|
| 71 |
+
vit = model.extractor_list[0].model
|
| 72 |
+
with torch.no_grad():
|
| 73 |
+
tok = vit.get_intermediate_layers(x, 1)[0] # (1, 1+P, C)
|
| 74 |
+
a = vit.get_last_selfattention(x) # (1, heads, S, S)
|
| 75 |
+
return tok[0, 1:].float().cpu().numpy(), a[0, :, 0, 1:].mean(0).float().cpu().numpy()
|
| 76 |
with torch.no_grad():
|
| 77 |
o = model(x, output_attentions=True)
|
| 78 |
h, a = o.last_hidden_state, o.attentions[-1][0] # a: (heads, S, S)
|
|
|
|
| 90 |
spec = MODELS.get(model_label, MODELS[DEFAULT_MODEL])
|
| 91 |
model = get_model(spec)
|
| 92 |
mdt = next(model.parameters()).dtype
|
|
|
|
| 93 |
img = image.convert("RGB").resize((RES, RES), Image.BICUBIC)
|
| 94 |
+
if spec["kind"] == "dreamsim":
|
| 95 |
+
x = _cur["pre"](img).to(DEV) # DreamSim ships its own transform
|
| 96 |
+
else:
|
| 97 |
+
mean, std = spec["norm"]
|
| 98 |
+
arr = np.asarray(img).astype(np.float32) / 255
|
| 99 |
+
x = ((torch.from_numpy(arr).permute(2, 0, 1) - mean) / std).unsqueeze(0).to(DEV, mdt)
|
| 100 |
feat, att = encode(model, spec, x)
|
| 101 |
att_pct = 100.0 * att / att.sum()
|
| 102 |
P = feat.shape[0]; g = int(round(P ** 0.5))
|
|
|
|
| 170 |
btn = gr.Button("Build tree + reveal", variant="primary")
|
| 171 |
gr.Markdown("<small>Compare encoder families: **DINOv3** (self-supervised, semantic), "
|
| 172 |
"**MAE** (pixel-reconstruction — more texture/appearance driven), "
|
| 173 |
+
"**CLIP** (language-aligned — 16×16 grid, patch-14), "
|
| 174 |
+
"**DreamSim** (tuned on *human* similarity judgments).<br>"
|
| 175 |
"ViT-H+/7B are large — slow on the free CPU; **ViT-7B realistically needs a GPU Space**. "
|
| 176 |
+
"First use of each model downloads its weights (one-time; DreamSim is ~1 GB).</small>")
|
| 177 |
with gr.Tab("Condensed tree (hover)"):
|
| 178 |
out_tree = gr.HTML()
|
| 179 |
with gr.Tab("Reveal animation"):
|
requirements.txt
CHANGED
|
@@ -5,3 +5,4 @@ scipy
|
|
| 5 |
pillow
|
| 6 |
numpy
|
| 7 |
gradio
|
|
|
|
|
|
| 5 |
pillow
|
| 6 |
numpy
|
| 7 |
gradio
|
| 8 |
+
dreamsim
|