Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -149,16 +149,19 @@ def _compute_avg_from_file():
|
|
| 149 |
return "No eval_runs.jsonl yet. Run at least one evaluation."
|
| 150 |
|
| 151 |
|
|
|
|
|
|
|
| 152 |
# ---------- UI ----------
|
| 153 |
with gr.Blocks() as demo:
|
| 154 |
-
gr.Markdown("JWST multimodal search
|
| 155 |
|
|
|
|
| 156 |
n = gr.Slider(50, 1000, value=200, step=10, label="How many images to index")
|
| 157 |
build_btn = gr.Button("Build index")
|
| 158 |
status = gr.Textbox(label="Status", lines=2)
|
| 159 |
-
|
| 160 |
build_btn.click(build_index, inputs=n, outputs=status)
|
| 161 |
|
|
|
|
| 162 |
gr.Markdown("Search the index with text or an example image")
|
| 163 |
|
| 164 |
q_text = gr.Textbox(label="Text query", placeholder="e.g., spiral galaxy")
|
|
@@ -166,28 +169,66 @@ with gr.Blocks() as demo:
|
|
| 166 |
k = gr.Slider(1, 12, value=6, step=1, label="Top K")
|
| 167 |
|
| 168 |
search_btn = gr.Button("Search")
|
|
|
|
|
|
|
|
|
|
| 169 |
|
| 170 |
-
|
| 171 |
with gr.Accordion("Evaluation", open=False):
|
| 172 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
eval_img = gr.Image(label="Evaluation image (optional)", type="pil")
|
| 174 |
-
|
| 175 |
|
| 176 |
run_and_label = gr.Button("Run and label this query")
|
| 177 |
|
| 178 |
-
eval_gallery
|
| 179 |
relevant_picker = gr.CheckboxGroup(label="Select indices of relevant results (1..K)")
|
| 180 |
-
eval_md
|
| 181 |
|
|
|
|
| 182 |
eval_state = gr.State({"result_indices": [], "k": 5, "query": ""})
|
| 183 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
def _run_eval_query(q_txt, q_img_in, k_in, state):
|
| 185 |
items, idxs, _ = _search_topk_for_eval(q_txt, q_img_in, k_in)
|
| 186 |
state["result_indices"] = idxs
|
| 187 |
state["k"] = int(k_in)
|
| 188 |
state["query"] = q_txt if (q_txt and q_txt.strip()) else "[image query]"
|
| 189 |
choice_labels = [str(i+1) for i in range(len(idxs))]
|
| 190 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 191 |
|
| 192 |
run_and_label.click(
|
| 193 |
fn=_run_eval_query,
|
|
@@ -197,36 +238,26 @@ with gr.Blocks() as demo:
|
|
| 197 |
|
| 198 |
compute_btn = gr.Button("Compute metrics")
|
| 199 |
|
|
|
|
| 200 |
def _compute_pk(selected_indices, state):
|
| 201 |
-
|
| 202 |
query = state.get("query", "")
|
| 203 |
-
# user marks which of the K are relevant; count is the hits
|
| 204 |
hits = len(selected_indices)
|
| 205 |
-
p_at_k = hits / max(
|
| 206 |
record = {
|
| 207 |
"ts": int(time.time()),
|
| 208 |
"query": query,
|
| 209 |
-
"k":
|
| 210 |
"relevant_indices": sorted([int(s) for s in selected_indices]),
|
| 211 |
"precision_at_k": p_at_k
|
| 212 |
}
|
| 213 |
_save_eval_run(record)
|
| 214 |
-
return _format_eval_summary(query,
|
| 215 |
|
| 216 |
-
compute_btn.click(
|
| 217 |
-
fn=_compute_pk,
|
| 218 |
-
inputs=[relevant_picker, eval_state],
|
| 219 |
-
outputs=eval_md
|
| 220 |
-
)
|
| 221 |
|
| 222 |
avg_btn = gr.Button("Compute average across saved runs")
|
| 223 |
avg_md = gr.Markdown()
|
| 224 |
-
|
| 225 |
avg_btn.click(fn=_compute_avg_from_file, outputs=avg_md)
|
| 226 |
-
|
| 227 |
-
gallery = gr.Gallery(label="Results", columns=6, height=300)
|
| 228 |
-
info2 = gr.Textbox(label="Search status", lines=1)
|
| 229 |
-
|
| 230 |
-
search_btn.click(search, inputs=[q_text, q_img, k], outputs=[gallery, info2])
|
| 231 |
|
| 232 |
demo.launch()
|
|
|
|
| 149 |
return "No eval_runs.jsonl yet. Run at least one evaluation."
|
| 150 |
|
| 151 |
|
| 152 |
+
|
| 153 |
+
|
| 154 |
# ---------- UI ----------
|
| 155 |
with gr.Blocks() as demo:
|
| 156 |
+
gr.Markdown("JWST multimodal search build the index")
|
| 157 |
|
| 158 |
+
# Build
|
| 159 |
n = gr.Slider(50, 1000, value=200, step=10, label="How many images to index")
|
| 160 |
build_btn = gr.Button("Build index")
|
| 161 |
status = gr.Textbox(label="Status", lines=2)
|
|
|
|
| 162 |
build_btn.click(build_index, inputs=n, outputs=status)
|
| 163 |
|
| 164 |
+
# Search
|
| 165 |
gr.Markdown("Search the index with text or an example image")
|
| 166 |
|
| 167 |
q_text = gr.Textbox(label="Text query", placeholder="e.g., spiral galaxy")
|
|
|
|
| 169 |
k = gr.Slider(1, 12, value=6, step=1, label="Top K")
|
| 170 |
|
| 171 |
search_btn = gr.Button("Search")
|
| 172 |
+
gallery = gr.Gallery(label="Results", columns=6, height=300)
|
| 173 |
+
info2 = gr.Textbox(label="Search status", lines=1)
|
| 174 |
+
search_btn.click(search, inputs=[q_text, q_img, k], outputs=[gallery, info2])
|
| 175 |
|
| 176 |
+
# ---------- Evaluation (guided) ----------
|
| 177 |
with gr.Accordion("Evaluation", open=False):
|
| 178 |
+
gr.Markdown(
|
| 179 |
+
"### What this does\n"
|
| 180 |
+
"We evaluate text to image retrieval using Precision at K.\n"
|
| 181 |
+
"Steps: pick a preset or type a query, click **Run and label**, "
|
| 182 |
+
"tick the results that match the rule shown, then click **Compute metrics**. "
|
| 183 |
+
"Each run is saved so you can average later."
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
# Preset prompts with plain English relevance rules
|
| 187 |
+
PRESETS = {
|
| 188 |
+
"star with spikes": "Relevant = bright point source with clear 4 to 6 diffraction spikes. Minimal extended glow.",
|
| 189 |
+
"edge-on galaxy": "Relevant = thin elongated streak. Looks like a narrow line. No round diffuse blob.",
|
| 190 |
+
"spiral galaxy": "Relevant = visible spiral arms or a spiral outline. Arms can be faint.",
|
| 191 |
+
"diffuse nebula": "Relevant = fuzzy cloud like structure. No sharp round core.",
|
| 192 |
+
"ring or annulus": "Relevant = ring or donut shape is the main feature.",
|
| 193 |
+
"two merging objects": "Relevant = two bright blobs touching or overlapping."
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
with gr.Row():
|
| 197 |
+
preset = gr.Dropdown(choices=list(PRESETS.keys()), label="Preset query (optional)")
|
| 198 |
+
eval_k = gr.Slider(1, 12, value=6, step=1, label="K for evaluation")
|
| 199 |
+
|
| 200 |
+
eval_query = gr.Textbox(label="Evaluation query (you can edit or type your own)")
|
| 201 |
eval_img = gr.Image(label="Evaluation image (optional)", type="pil")
|
| 202 |
+
rules_md = gr.Markdown()
|
| 203 |
|
| 204 |
run_and_label = gr.Button("Run and label this query")
|
| 205 |
|
| 206 |
+
eval_gallery = gr.Gallery(label="Eval top K results", columns=6, height=300)
|
| 207 |
relevant_picker = gr.CheckboxGroup(label="Select indices of relevant results (1..K)")
|
| 208 |
+
eval_md = gr.Markdown()
|
| 209 |
|
| 210 |
+
# state bag for this panel
|
| 211 |
eval_state = gr.State({"result_indices": [], "k": 5, "query": ""})
|
| 212 |
|
| 213 |
+
def _on_preset_change(name):
|
| 214 |
+
if name in PRESETS:
|
| 215 |
+
return gr.update(value=name), PRESETS[name]
|
| 216 |
+
return gr.update(), ""
|
| 217 |
+
|
| 218 |
+
preset.change(fn=_on_preset_change, inputs=preset, outputs=[eval_query, rules_md])
|
| 219 |
+
|
| 220 |
+
# uses helper _search_topk_for_eval defined above
|
| 221 |
def _run_eval_query(q_txt, q_img_in, k_in, state):
|
| 222 |
items, idxs, _ = _search_topk_for_eval(q_txt, q_img_in, k_in)
|
| 223 |
state["result_indices"] = idxs
|
| 224 |
state["k"] = int(k_in)
|
| 225 |
state["query"] = q_txt if (q_txt and q_txt.strip()) else "[image query]"
|
| 226 |
choice_labels = [str(i+1) for i in range(len(idxs))]
|
| 227 |
+
help_text = PRESETS.get((q_txt or "").strip().lower(), "Mark results that match the concept you typed.")
|
| 228 |
+
return (items,
|
| 229 |
+
gr.update(choices=choice_labels, value=[]),
|
| 230 |
+
f"**Relevance rule:** {help_text}\n\nThen click **Compute metrics**.",
|
| 231 |
+
state)
|
| 232 |
|
| 233 |
run_and_label.click(
|
| 234 |
fn=_run_eval_query,
|
|
|
|
| 238 |
|
| 239 |
compute_btn = gr.Button("Compute metrics")
|
| 240 |
|
| 241 |
+
# uses helpers _save_eval_run and _format_eval_summary defined above
|
| 242 |
def _compute_pk(selected_indices, state):
|
| 243 |
+
k_val = int(state.get("k", 5))
|
| 244 |
query = state.get("query", "")
|
|
|
|
| 245 |
hits = len(selected_indices)
|
| 246 |
+
p_at_k = hits / max(k_val, 1)
|
| 247 |
record = {
|
| 248 |
"ts": int(time.time()),
|
| 249 |
"query": query,
|
| 250 |
+
"k": k_val,
|
| 251 |
"relevant_indices": sorted([int(s) for s in selected_indices]),
|
| 252 |
"precision_at_k": p_at_k
|
| 253 |
}
|
| 254 |
_save_eval_run(record)
|
| 255 |
+
return _format_eval_summary(query, k_val, hits, p_at_k)
|
| 256 |
|
| 257 |
+
compute_btn.click(fn=_compute_pk, inputs=[relevant_picker, eval_state], outputs=eval_md)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 258 |
|
| 259 |
avg_btn = gr.Button("Compute average across saved runs")
|
| 260 |
avg_md = gr.Markdown()
|
|
|
|
| 261 |
avg_btn.click(fn=_compute_avg_from_file, outputs=avg_md)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 262 |
|
| 263 |
demo.launch()
|