ShesterG commited on
Commit
403700d
·
1 Parent(s): 4e25ac8

Add DreamSim (human-aligned) encoder: LoRA-tuned DINO ViT-B/16 patch tokens + CLS attention

Browse files
Files changed (2) hide show
  1. app.py +32 -14
  2. 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
- dt = torch.bfloat16 if "vit7b16" in repo else torch.float32 # 7B in bf16 to fit
50
- print(f"[app] loading {repo} ({dt})", flush=True)
51
- cls = {"dinov3": AutoModel, "mae": ViTMAEModel, "clip": CLIPVisionModel}[spec["kind"]]
52
- mdl = cls.from_pretrained(repo, dtype=dt, token=TOKEN, attn_implementation="eager")
53
- if spec["kind"] == "mae":
54
- mdl.config.mask_ratio = 0.0 # keep ALL patches (MAE masks 75% by default)
55
- _cur["model"] = mdl.eval().to(DEV)
 
 
 
 
 
 
 
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
- arr = np.asarray(img).astype(np.float32) / 255
82
- x = ((torch.from_numpy(arr).permute(2, 0, 1) - mean) / std).unsqueeze(0).to(DEV, mdt)
 
 
 
 
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).<br>"
 
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