Spaces:
Sleeping
Sleeping
Deploy from CI
Browse files- README.md +3 -3
- app.py +7 -10
- requirements.txt +2 -0
README.md
CHANGED
|
@@ -6,13 +6,13 @@ colorTo: green
|
|
| 6 |
sdk: gradio
|
| 7 |
app_file: app.py
|
| 8 |
pinned: false
|
| 9 |
-
short_description: Semantic search over live-camera snapshots with
|
| 10 |
---
|
| 11 |
|
| 12 |
# Worldscope Search
|
| 13 |
|
| 14 |
Semantic image search over a bucket of live-camera snapshots. Type a description and
|
| 15 |
-
|
| 16 |
pipeline and stored in a public storage bucket; this Space loads them and searches
|
| 17 |
in memory.
|
| 18 |
|
|
@@ -20,5 +20,5 @@ in memory.
|
|
| 20 |
|
| 21 |
- `HF_BUCKET` β bucket holding `embeddings.parquet` (default `shrnik/worldscope`)
|
| 22 |
- `EMBEDDINGS_PATH` β file name in the bucket (default `embeddings.parquet`)
|
| 23 |
-
- `
|
| 24 |
- `TOP_K` β number of results (default `100`)
|
|
|
|
| 6 |
sdk: gradio
|
| 7 |
app_file: app.py
|
| 8 |
pinned: false
|
| 9 |
+
short_description: Semantic search over live-camera snapshots with TIPS v2
|
| 10 |
---
|
| 11 |
|
| 12 |
# Worldscope Search
|
| 13 |
|
| 14 |
Semantic image search over a bucket of live-camera snapshots. Type a description and
|
| 15 |
+
TIPS v2 ranks the snapshots by similarity. Embeddings are precomputed by the HF Jobs
|
| 16 |
pipeline and stored in a public storage bucket; this Space loads them and searches
|
| 17 |
in memory.
|
| 18 |
|
|
|
|
| 20 |
|
| 21 |
- `HF_BUCKET` β bucket holding `embeddings.parquet` (default `shrnik/worldscope`)
|
| 22 |
- `EMBEDDINGS_PATH` β file name in the bucket (default `embeddings.parquet`)
|
| 23 |
+
- `EMBED_MODEL` β must match the embed job (default `google/tipsv2-b14`)
|
| 24 |
- `TOP_K` β number of results (default `100`)
|
app.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
"""Worldscope semantic image search β Hugging Face Space (Gradio).
|
| 2 |
|
| 3 |
Self-contained search frontend:
|
| 4 |
-
- loads the
|
| 5 |
- pulls embeddings.parquet from the public storage bucket,
|
| 6 |
- does brute-force cosine search in memory,
|
| 7 |
- shows the matching camera snapshots (served via the bucket's public URLs).
|
|
@@ -20,33 +20,30 @@ import numpy as np
|
|
| 20 |
import pandas as pd
|
| 21 |
import plotly.express as px
|
| 22 |
import torch
|
| 23 |
-
from transformers import
|
| 24 |
|
| 25 |
HF_BUCKET = os.environ.get("HF_BUCKET", "shrnik/worldscope")
|
| 26 |
HF_ENDPOINT = os.environ.get("HF_ENDPOINT", "https://huggingface.co")
|
| 27 |
EMBEDDINGS_PATH = os.environ.get("EMBEDDINGS_PATH", "embeddings.parquet")
|
| 28 |
-
|
| 29 |
TOP_K = int(os.environ.get("TOP_K", "100"))
|
| 30 |
|
| 31 |
EMBEDDINGS_URL = f"{HF_ENDPOINT}/buckets/{HF_BUCKET}/resolve/{EMBEDDINGS_PATH}"
|
| 32 |
|
| 33 |
# --- model -------------------------------------------------------------------
|
| 34 |
-
_model =
|
| 35 |
-
_processor = CLIPProcessor.from_pretrained(CLIP_MODEL)
|
| 36 |
|
| 37 |
|
| 38 |
@torch.inference_mode()
|
| 39 |
def embed_text(text: str) -> np.ndarray:
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
feats = out if torch.is_tensor(out) else out.pooler_output # v4 tensor / v5 object
|
| 43 |
-
vec = feats.cpu().numpy().astype(np.float32)[0]
|
| 44 |
norm = np.linalg.norm(vec) or 1.0
|
| 45 |
return vec / norm
|
| 46 |
|
| 47 |
|
| 48 |
# --- index -------------------------------------------------------------------
|
| 49 |
-
_embeddings = np.empty((0,
|
| 50 |
_meta: list[dict] = []
|
| 51 |
|
| 52 |
|
|
|
|
| 1 |
"""Worldscope semantic image search β Hugging Face Space (Gradio).
|
| 2 |
|
| 3 |
Self-contained search frontend:
|
| 4 |
+
- loads the TIPS v2 text tower (same checkpoint the embed job used),
|
| 5 |
- pulls embeddings.parquet from the public storage bucket,
|
| 6 |
- does brute-force cosine search in memory,
|
| 7 |
- shows the matching camera snapshots (served via the bucket's public URLs).
|
|
|
|
| 20 |
import pandas as pd
|
| 21 |
import plotly.express as px
|
| 22 |
import torch
|
| 23 |
+
from transformers import AutoModel
|
| 24 |
|
| 25 |
HF_BUCKET = os.environ.get("HF_BUCKET", "shrnik/worldscope")
|
| 26 |
HF_ENDPOINT = os.environ.get("HF_ENDPOINT", "https://huggingface.co")
|
| 27 |
EMBEDDINGS_PATH = os.environ.get("EMBEDDINGS_PATH", "embeddings.parquet")
|
| 28 |
+
EMBED_MODEL = os.environ.get("EMBED_MODEL", "google/tipsv2-b14")
|
| 29 |
TOP_K = int(os.environ.get("TOP_K", "100"))
|
| 30 |
|
| 31 |
EMBEDDINGS_URL = f"{HF_ENDPOINT}/buckets/{HF_BUCKET}/resolve/{EMBEDDINGS_PATH}"
|
| 32 |
|
| 33 |
# --- model -------------------------------------------------------------------
|
| 34 |
+
_model = AutoModel.from_pretrained(EMBED_MODEL, trust_remote_code=True).eval()
|
|
|
|
| 35 |
|
| 36 |
|
| 37 |
@torch.inference_mode()
|
| 38 |
def embed_text(text: str) -> np.ndarray:
|
| 39 |
+
# encode_text tokenizes internally and returns (1, dim).
|
| 40 |
+
vec = _model.encode_text([text]).cpu().numpy().astype(np.float32)[0]
|
|
|
|
|
|
|
| 41 |
norm = np.linalg.norm(vec) or 1.0
|
| 42 |
return vec / norm
|
| 43 |
|
| 44 |
|
| 45 |
# --- index -------------------------------------------------------------------
|
| 46 |
+
_embeddings = np.empty((0, 768), dtype=np.float32)
|
| 47 |
_meta: list[dict] = []
|
| 48 |
|
| 49 |
|
requirements.txt
CHANGED
|
@@ -1,6 +1,8 @@
|
|
| 1 |
gradio>=5.0
|
| 2 |
transformers>=4.46
|
| 3 |
torch>=2.4
|
|
|
|
|
|
|
| 4 |
numpy>=2.0
|
| 5 |
pandas>=2.2
|
| 6 |
pyarrow>=18.0
|
|
|
|
| 1 |
gradio>=5.0
|
| 2 |
transformers>=4.46
|
| 3 |
torch>=2.4
|
| 4 |
+
# tipsv2's remote code needs the CamelCase sentencepiece API, removed in 0.2.1+
|
| 5 |
+
sentencepiece==0.2.0
|
| 6 |
numpy>=2.0
|
| 7 |
pandas>=2.2
|
| 8 |
pyarrow>=18.0
|