mnm-matin commited on
Commit
1b2e03a
·
verified ·
1 Parent(s): 78442f3

Upgrade geospatial demo to HyperView 0.6.2

Browse files
.hyperview/extensions/geospatial-readout/panel.js CHANGED
@@ -62,9 +62,9 @@ function Section({ title, children }) {
62
 
63
  function Walkthrough() {
64
  const steps = [
65
- ["Pick", "Choose a ranked query."],
66
- ["Baseline", "Show CLIP neighbors."],
67
- ["Compare", "Switch to Hyper3-CLIP and count the top 10."],
68
  ];
69
 
70
  return React.createElement(
@@ -104,6 +104,33 @@ function Walkthrough() {
104
  );
105
  }
106
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  function BenchmarkTable({ benchmark }) {
108
  if (!benchmark || !Array.isArray(benchmark.rows)) return null;
109
  return React.createElement(
@@ -132,7 +159,7 @@ function BenchmarkTable({ benchmark }) {
132
  React.createElement(
133
  "tr",
134
  { style: { background: colors.cardBgSoft } },
135
- ["Metric", "CLIP", "Hyper3", "Delta"].map((heading) =>
136
  React.createElement(
137
  "th",
138
  {
@@ -428,6 +455,21 @@ function ExampleCard({
428
  ),
429
  deltaLine(item),
430
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  React.createElement(ModelCompareTable, { item, models, loadingKey, onSelectQuery }),
432
  );
433
  }
@@ -512,7 +554,12 @@ export default function GeospatialComparisonPanel() {
512
  },
513
  React.createElement(
514
  Section,
515
- { title: "How to read it" },
 
 
 
 
 
516
  React.createElement(Walkthrough, null),
517
  ),
518
  warnings.length
@@ -534,7 +581,7 @@ export default function GeospatialComparisonPanel() {
534
  : null,
535
  React.createElement(
536
  Section,
537
- { title: "Best live examples" },
538
  examples.length && models.length
539
  ? React.createElement(
540
  "div",
@@ -564,7 +611,7 @@ export default function GeospatialComparisonPanel() {
564
  : null,
565
  React.createElement(
566
  Section,
567
- { title: "Benchmark table" },
568
  React.createElement(BenchmarkTable, { benchmark: panelProps.benchmark }),
569
  ),
570
  ),
 
62
 
63
  function Walkthrough() {
64
  const steps = [
65
+ ["Pick", "Start from a failing scene query."],
66
+ ["Compare", "Show CLIP, then Hyper3-CLIP."],
67
+ ["Audit", "Check exact scene and parent group."],
68
  ];
69
 
70
  return React.createElement(
 
104
  );
105
  }
106
 
107
+ function BusinessCase() {
108
+ return React.createElement(
109
+ "div",
110
+ {
111
+ style: {
112
+ border: `1px solid ${colors.border}`,
113
+ borderRadius: 4,
114
+ background: colors.cardBg,
115
+ padding: 10,
116
+ display: "flex",
117
+ flexDirection: "column",
118
+ gap: 6,
119
+ },
120
+ },
121
+ React.createElement(
122
+ "div",
123
+ { style: { color: colors.strongText, fontSize: 13, fontWeight: 800, lineHeight: 1.25 } },
124
+ "Retrieval audit for aerial image libraries",
125
+ ),
126
+ React.createElement(
127
+ "div",
128
+ { style: { color: colors.bodyText, fontSize: 11, lineHeight: 1.35 } },
129
+ "When an analyst asks for more scenes like this, the top results should stay in the right scene class and operational group. This demo shows where CLIP drifts and where Hyper3-CLIP keeps the neighborhood cleaner.",
130
+ ),
131
+ );
132
+ }
133
+
134
  function BenchmarkTable({ benchmark }) {
135
  if (!benchmark || !Array.isArray(benchmark.rows)) return null;
136
  return React.createElement(
 
159
  React.createElement(
160
  "tr",
161
  { style: { background: colors.cardBgSoft } },
162
+ ["Metric", "CLIP", "Hyper3-CLIP", "Delta"].map((heading) =>
163
  React.createElement(
164
  "th",
165
  {
 
455
  ),
456
  deltaLine(item),
457
  ),
458
+ item.insight
459
+ ? React.createElement(
460
+ "div",
461
+ {
462
+ style: {
463
+ color: colors.bodyText,
464
+ fontSize: 11,
465
+ lineHeight: 1.35,
466
+ borderLeft: `2px solid ${colors.accent}`,
467
+ paddingLeft: 8,
468
+ },
469
+ },
470
+ item.insight,
471
+ )
472
+ : null,
473
  React.createElement(ModelCompareTable, { item, models, loadingKey, onSelectQuery }),
474
  );
475
  }
 
554
  },
555
  React.createElement(
556
  Section,
557
+ { title: "Business case" },
558
+ React.createElement(BusinessCase, null),
559
+ ),
560
+ React.createElement(
561
+ Section,
562
+ { title: "What to do" },
563
  React.createElement(Walkthrough, null),
564
  ),
565
  warnings.length
 
581
  : null,
582
  React.createElement(
583
  Section,
584
+ { title: "Queries to inspect" },
585
  examples.length && models.length
586
  ? React.createElement(
587
  "div",
 
611
  : null,
612
  React.createElement(
613
  Section,
614
+ { title: "Benchmark context" },
615
  React.createElement(BenchmarkTable, { benchmark: panelProps.benchmark }),
616
  ),
617
  ),
Dockerfile CHANGED
@@ -20,7 +20,8 @@ WORKDIR $HOME/app
20
 
21
  RUN pip install --upgrade pip
22
 
23
- ARG HYPERVIEW_VERSION=0.6.1
 
24
 
25
  # Install CPU-only PyTorch first so the Space does not pull the default CUDA bundle.
26
  RUN pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
@@ -33,6 +34,7 @@ import hyperview as hv
33
  print("hyperview", hv.__version__, inspect.signature(hv.launch))
34
  PY
35
  RUN pip install \
 
36
  "datasets>=4.5.0" \
37
  "Pillow>=12.0.0" \
38
  "timm>=1.0.0" \
 
20
 
21
  RUN pip install --upgrade pip
22
 
23
+ ARG HYPERVIEW_VERSION=0.6.2
24
+ ARG HYPER_MODELS_VERSION=0.3.0
25
 
26
  # Install CPU-only PyTorch first so the Space does not pull the default CUDA bundle.
27
  RUN pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
 
34
  print("hyperview", hv.__version__, inspect.signature(hv.launch))
35
  PY
36
  RUN pip install \
37
+ "hyper-models[ml]==${HYPER_MODELS_VERSION}" \
38
  "datasets>=4.5.0" \
39
  "Pillow>=12.0.0" \
40
  "timm>=1.0.0" \
README.md CHANGED
@@ -24,7 +24,7 @@ This demo builds a balanced NWPU-RESISC45 remote-sensing subset and opens
24
  HyperView with two pinned scatter panels plus a compact scene-retrieval readout:
25
 
26
  - CLIP ViT-B/32 in a Euclidean 2D layout
27
- - Hyper3-CLIP `hyper3labs/hyper3-clip-v0.5` in a Poincare 2D layout
28
 
29
  The readout panel ranks live query examples by where Hyper3-CLIP improves the
30
  top-10 neighborhood over CLIP. The demo is designed around remote-sensing review
@@ -79,9 +79,9 @@ remote-sensing model.
79
  This folder is intended to deploy to `hyper3labs/HyperView-EuroSAT-Geospatial`
80
  from the `hyperview-spaces` deployment repository.
81
 
82
- Hyper3-CLIP weights are loaded from the gated
83
- `hyper3labs/hyper3-clip-v0.5` model repository at runtime. The Space needs an
84
- `HF_TOKEN` secret with access to that model.
85
 
86
  If that secret is missing or lacks gated-model access, the Space starts with a
87
  clearly labeled CLIP fallback for the Hyper3-CLIP panel instead of exiting
 
24
  HyperView with two pinned scatter panels plus a compact scene-retrieval readout:
25
 
26
  - CLIP ViT-B/32 in a Euclidean 2D layout
27
+ - Hyper3-CLIP `hyper3-clip-v0.5` from `hyper-models` in a Poincare 2D layout
28
 
29
  The readout panel ranks live query examples by where Hyper3-CLIP improves the
30
  top-10 neighborhood over CLIP. The demo is designed around remote-sensing review
 
79
  This folder is intended to deploy to `hyper3labs/HyperView-EuroSAT-Geospatial`
80
  from the `hyperview-spaces` deployment repository.
81
 
82
+ Hyper3-CLIP weights are loaded through the `hyper-models` catalog entry for the
83
+ gated `hyper3labs/hyper3-clip-v0.5` model repository at runtime. The Space needs
84
+ an `HF_TOKEN` secret with access to that model.
85
 
86
  If that secret is missing or lacks gated-model access, the Space starts with a
87
  clearly labeled CLIP fallback for the Hyper3-CLIP panel instead of exiting
demo.py CHANGED
@@ -208,12 +208,12 @@ MODEL_SPECS = [
208
  "Inspect Hyper3-CLIP neighbors",
209
  ),
210
  "provider": env_value(
211
- "GEOSPATIAL_CANDIDATE_PROVIDER", "EUROSAT_CANDIDATE_PROVIDER", "hyper3-clip"
212
  ),
213
  "model": env_value(
214
  "GEOSPATIAL_CANDIDATE_MODEL",
215
  "EUROSAT_CANDIDATE_MODEL",
216
- "hyper3labs/hyper3-clip-v0.5",
217
  ),
218
  "layout": env_value(
219
  "GEOSPATIAL_CANDIDATE_LAYOUT", "EUROSAT_CANDIDATE_LAYOUT", "poincare:2d"
@@ -267,6 +267,37 @@ def readable_group(label: str) -> str:
267
  return label.replace("_", " ").replace("-", " ").title()
268
 
269
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  def save_image(image: Image.Image, destination: Path) -> None:
271
  if (
272
  destination.exists()
@@ -559,6 +590,7 @@ def build_examples(
559
  "score": score,
560
  "classDelta": class_delta,
561
  "parentDelta": parent_delta,
 
562
  "summaries": {
563
  "clip": clip_summary,
564
  "candidate": candidate_summary,
@@ -609,31 +641,58 @@ def aggregate_metrics(
609
  return metrics
610
 
611
 
612
- def register_hyper3_clip_provider() -> None:
613
- from hyperview.runtime import ProviderRegistry
614
-
615
- ProviderRegistry().register_python(
616
- "hyper3-clip",
617
- "hyper3_clip_provider:Hyper3ClipEmbeddings",
618
- description="Hyper3-CLIP v0.5 image embeddings from hyper3labs/hyper3-clip-v0.5",
619
- overwrite=True,
620
- )
621
-
622
-
623
  def build_demo_view(dataset: hv.Dataset, layouts: dict[str, str]) -> hv.ui.View:
624
  summaries = collect_neighbor_summaries(dataset)
625
- scatter_panels = [
626
- hv.ui.Scatter(
627
- id=f"{spec['key']}-geospatial-map",
628
- title=spec["panel_title"],
629
- layout_key=layouts[spec["key"]],
630
- geometry=spec["geometry"],
631
- layout_dimension=spec["layout_dimension"],
632
- )
633
- for spec in MODEL_SPECS
634
- ]
635
  return hv.ui.View(
636
- hv.ui.Horizontal(*scatter_panels),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
637
  hv.ui.ExtensionPanel(
638
  id="geospatial-retrieval-readout",
639
  extension="geospatial-readout",
@@ -649,6 +708,10 @@ def build_demo_view(dataset: hv.Dataset, layouts: dict[str, str]) -> hv.ui.View:
649
  "models": model_panel_props(layouts),
650
  "examples": build_examples(dataset, summaries),
651
  "warnings": RUNTIME_WARNINGS,
 
 
 
 
652
  },
653
  ),
654
  )
@@ -673,7 +736,6 @@ def launch_demo(dataset: hv.Dataset, layouts: dict[str, str]) -> hv.Session:
673
 
674
 
675
  def main() -> None:
676
- register_hyper3_clip_provider()
677
  dataset, layouts = build_dataset()
678
  print("Layouts:", flush=True)
679
  for spec in MODEL_SPECS:
 
208
  "Inspect Hyper3-CLIP neighbors",
209
  ),
210
  "provider": env_value(
211
+ "GEOSPATIAL_CANDIDATE_PROVIDER", "EUROSAT_CANDIDATE_PROVIDER", "hyper-models"
212
  ),
213
  "model": env_value(
214
  "GEOSPATIAL_CANDIDATE_MODEL",
215
  "EUROSAT_CANDIDATE_MODEL",
216
+ "hyper3-clip-v0.5",
217
  ),
218
  "layout": env_value(
219
  "GEOSPATIAL_CANDIDATE_LAYOUT", "EUROSAT_CANDIDATE_LAYOUT", "poincare:2d"
 
267
  return label.replace("_", " ").replace("-", " ").title()
268
 
269
 
270
+ def example_insight(
271
+ label: str,
272
+ clip_summary: dict[str, Any],
273
+ candidate_summary: dict[str, Any],
274
+ ) -> str:
275
+ clip_same = clip_summary["sameClassHits"]
276
+ clip_parent = clip_summary["parentHits"]
277
+ candidate_same = candidate_summary["sameClassHits"]
278
+ candidate_parent = candidate_summary["parentHits"]
279
+ total = candidate_summary.get("total", 10)
280
+ if label == "airplane":
281
+ return (
282
+ f"Transport audit: Hyper3-CLIP raises the neighborhood from {clip_parent}/{total} "
283
+ f"to {candidate_parent}/{total} transport scenes and finds {candidate_same}/{total} exact airplanes."
284
+ )
285
+ if label == "meadow":
286
+ return (
287
+ f"Land-cover QA: Hyper3-CLIP keeps {candidate_parent}/{total} neighbors in agriculture/vegetation "
288
+ f"versus CLIP's {clip_parent}/{total}, with {candidate_same}/{total} exact meadows."
289
+ )
290
+ if label == "runway":
291
+ return (
292
+ f"Infrastructure search: Hyper3-CLIP finds {candidate_same}/{total} exact runways versus "
293
+ f"CLIP's {clip_same}/{total}, while doubling the transport-group neighborhood."
294
+ )
295
+ return (
296
+ f"Hyper3-CLIP improves exact scene hits from {clip_same}/{total} to {candidate_same}/{total} "
297
+ f"and parent-group hits from {clip_parent}/{total} to {candidate_parent}/{total}."
298
+ )
299
+
300
+
301
  def save_image(image: Image.Image, destination: Path) -> None:
302
  if (
303
  destination.exists()
 
590
  "score": score,
591
  "classDelta": class_delta,
592
  "parentDelta": parent_delta,
593
+ "insight": example_insight(label, clip_summary, candidate_summary),
594
  "summaries": {
595
  "clip": clip_summary,
596
  "candidate": candidate_summary,
 
641
  return metrics
642
 
643
 
 
 
 
 
 
 
 
 
 
 
 
644
  def build_demo_view(dataset: hv.Dataset, layouts: dict[str, str]) -> hv.ui.View:
645
  summaries = collect_neighbor_summaries(dataset)
646
+ clip_spec = MODEL_SPECS[0]
647
+ candidate_spec = MODEL_SPECS[1]
648
+ samples_panel_id = "grid"
649
+ clip_panel_id = "clip-geospatial-map"
650
+ candidate_panel_id = "candidate-geospatial-map"
 
 
 
 
 
651
  return hv.ui.View(
652
+ hv.ui.Samples(
653
+ id=samples_panel_id,
654
+ title="Samples",
655
+ position="center",
656
+ props={
657
+ "dockview": {
658
+ "minimumHeight": 220,
659
+ "minimumWidth": 360,
660
+ },
661
+ },
662
+ ),
663
+ hv.ui.Scatter(
664
+ id=clip_panel_id,
665
+ title=clip_spec["panel_title"],
666
+ layout_key=layouts[clip_spec["key"]],
667
+ position="center",
668
+ reference_panel_id=samples_panel_id,
669
+ direction="below",
670
+ geometry=clip_spec["geometry"],
671
+ layout_dimension=clip_spec["layout_dimension"],
672
+ props={
673
+ "dockview": {
674
+ "initialHeight": 260,
675
+ "minimumHeight": 190,
676
+ "minimumWidth": 280,
677
+ },
678
+ },
679
+ ),
680
+ hv.ui.Scatter(
681
+ id=candidate_panel_id,
682
+ title=candidate_spec["panel_title"],
683
+ layout_key=layouts[candidate_spec["key"]],
684
+ position="center",
685
+ reference_panel_id=clip_panel_id,
686
+ direction="right",
687
+ geometry=candidate_spec["geometry"],
688
+ layout_dimension=candidate_spec["layout_dimension"],
689
+ props={
690
+ "dockview": {
691
+ "minimumHeight": 190,
692
+ "minimumWidth": 280,
693
+ },
694
+ },
695
+ ),
696
  hv.ui.ExtensionPanel(
697
  id="geospatial-retrieval-readout",
698
  extension="geospatial-readout",
 
708
  "models": model_panel_props(layouts),
709
  "examples": build_examples(dataset, summaries),
710
  "warnings": RUNTIME_WARNINGS,
711
+ "dockview": {
712
+ "initialWidth": 330,
713
+ "minimumWidth": 290,
714
+ },
715
  },
716
  ),
717
  )
 
736
 
737
 
738
  def main() -> None:
 
739
  dataset, layouts = build_dataset()
740
  print("Layouts:", flush=True)
741
  for spec in MODEL_SPECS: