shrnik commited on
Commit
1696856
Β·
verified Β·
1 Parent(s): 4565f2d

Deploy from CI

Browse files
Files changed (3) hide show
  1. README.md +3 -3
  2. app.py +7 -10
  3. 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 CLIP
10
  ---
11
 
12
  # Worldscope Search
13
 
14
  Semantic image search over a bucket of live-camera snapshots. Type a description and
15
- CLIP 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,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
- - `CLIP_MODEL` β€” must match the embed job (default `openai/clip-vit-base-patch16`)
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 CLIP text model (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,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 CLIPModel, CLIPProcessor
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
- CLIP_MODEL = os.environ.get("CLIP_MODEL", "openai/clip-vit-base-patch16")
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 = CLIPModel.from_pretrained(CLIP_MODEL).eval()
35
- _processor = CLIPProcessor.from_pretrained(CLIP_MODEL)
36
 
37
 
38
  @torch.inference_mode()
39
  def embed_text(text: str) -> np.ndarray:
40
- inputs = _processor(text=[text], return_tensors="pt", padding=True, truncation=True)
41
- out = _model.get_text_features(**inputs)
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, 512), dtype=np.float32)
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