BiliSakura commited on
Commit
fd380f8
·
verified ·
1 Parent(s): 270f50f

Resolve class labels via id2label comma-separated synonyms

Browse files
Files changed (2) hide show
  1. app.py +24 -8
  2. model_loader.py +109 -7
app.py CHANGED
@@ -29,7 +29,13 @@ from model_catalog import (
29
  get_profile_by_label,
30
  parse_model_label,
31
  )
32
- from model_loader import PIPELINE_MANAGER, _to_float, _to_int, run_inference
 
 
 
 
 
 
33
 
34
 
35
  DEFAULT_MODEL = MODEL_LABELS[0]
@@ -162,24 +168,33 @@ def _gpu_duration(
162
  return int(min(300, max(base, step_budget * 0.6 + 30)))
163
 
164
 
165
- def _load_model_core(model_label: str) -> str:
166
  collection, variant = parse_model_label(model_label)
167
  message, _ = PIPELINE_MANAGER.load(collection, variant)
168
  PIPELINE_MANAGER.move_to_cuda()
169
- return message
 
 
 
170
 
171
 
172
  @spaces.GPU(size="xlarge", duration=120)
173
- def _load_on_gpu(model_label: str) -> str:
174
  return _load_model_core(model_label)
175
 
176
 
177
  def load_model(model_label: str):
178
  try:
179
- message = _load_on_gpu(model_label)
180
  except Exception as exc:
181
  raise gr.Error(f"Failed to load `{model_label}`: {exc}") from exc
182
- return (message, *_config_from_profile(get_profile_by_label(model_label)))
 
 
 
 
 
 
183
 
184
 
185
  @spaces.GPU(size="xlarge", duration=_gpu_duration)
@@ -318,7 +333,7 @@ def build_demo() -> gr.Blocks:
318
  class_label = gr.Textbox(
319
  label="class_labels",
320
  value=DEFAULT_PROFILE.default_class_label,
321
- info="ImageNet class name (e.g. golden retriever) or id (e.g. 207)",
322
  )
323
  with gr.Row():
324
  seed = gr.Number(label="seed", value=DEFAULT_PROFILE.default_seed, precision=0)
@@ -421,7 +436,8 @@ def build_demo() -> gr.Blocks:
421
  examples=[
422
  [DEFAULT_MODEL, "golden retriever", 42],
423
  [DEFAULT_MODEL, "207", 0],
424
- [DEFAULT_MODEL, "tabby, tabby cat", 123],
 
425
  ],
426
  inputs=[model, class_label, seed],
427
  label="Quick examples",
 
29
  get_profile_by_label,
30
  parse_model_label,
31
  )
32
+ from model_loader import (
33
+ PIPELINE_MANAGER,
34
+ _to_float,
35
+ _to_int,
36
+ default_class_label_for_pipe,
37
+ run_inference,
38
+ )
39
 
40
 
41
  DEFAULT_MODEL = MODEL_LABELS[0]
 
168
  return int(min(300, max(base, step_budget * 0.6 + 30)))
169
 
170
 
171
+ def _load_model_core(model_label: str) -> tuple[str, str | None]:
172
  collection, variant = parse_model_label(model_label)
173
  message, _ = PIPELINE_MANAGER.load(collection, variant)
174
  PIPELINE_MANAGER.move_to_cuda()
175
+ pipe = PIPELINE_MANAGER.pipe
176
+ profile = get_profile_by_label(model_label)
177
+ suggested_label = default_class_label_for_pipe(pipe, profile) if pipe is not None else None
178
+ return message, suggested_label
179
 
180
 
181
  @spaces.GPU(size="xlarge", duration=120)
182
+ def _load_on_gpu(model_label: str) -> tuple[str, str | None]:
183
  return _load_model_core(model_label)
184
 
185
 
186
  def load_model(model_label: str):
187
  try:
188
+ message, suggested_label = _load_on_gpu(model_label)
189
  except Exception as exc:
190
  raise gr.Error(f"Failed to load `{model_label}`: {exc}") from exc
191
+ profile = get_profile_by_label(model_label)
192
+ config = _config_from_profile(profile)
193
+ if suggested_label:
194
+ config = list(config)
195
+ config[1] = gr.update(value=suggested_label)
196
+ config = tuple(config)
197
+ return (message, *config)
198
 
199
 
200
  @spaces.GPU(size="xlarge", duration=_gpu_duration)
 
333
  class_label = gr.Textbox(
334
  label="class_labels",
335
  value=DEFAULT_PROFILE.default_class_label,
336
+ info="ImageNet class id (e.g. 207) or any synonym from id2label (e.g. golden retriever, tabby)",
337
  )
338
  with gr.Row():
339
  seed = gr.Number(label="seed", value=DEFAULT_PROFILE.default_seed, precision=0)
 
436
  examples=[
437
  [DEFAULT_MODEL, "golden retriever", 42],
438
  [DEFAULT_MODEL, "207", 0],
439
+ [DEFAULT_MODEL, "tabby", 123],
440
+ [DEFAULT_MODEL, "tabby, tabby cat", 456],
441
  ],
442
  inputs=[model, class_label, seed],
443
  label="Quick examples",
model_loader.py CHANGED
@@ -202,6 +202,110 @@ def build_inference_steps(profile: ModelProfile, steps: Any) -> int | list[int]:
202
  return steps
203
 
204
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  def run_inference(
206
  profile: ModelProfile,
207
  pipe: DiffusionPipeline,
@@ -228,13 +332,11 @@ def run_inference(
228
  }
229
  call_kwargs.update(extra_kwargs if extra_kwargs is not None else profile.extra_call_kwargs)
230
 
231
- label = str(class_label or "").strip() or profile.default_class_label
232
- if label.isdigit():
233
- call_kwargs["class_labels"] = int(label)
234
- elif label.replace(".", "", 1).isdigit():
235
- call_kwargs["class_labels"] = int(float(label))
236
- else:
237
- call_kwargs["class_labels"] = label
238
 
239
  native = profile.infer_resolution()
240
  if height > 0 and width > 0:
 
202
  return steps
203
 
204
 
205
+ def _split_synonyms(label_text: str) -> list[str]:
206
+ return [part.strip() for part in label_text.split(",") if part.strip()]
207
+
208
+
209
+ def _get_pipe_id2label(pipe: DiffusionPipeline) -> dict[int, str]:
210
+ id2label: dict[int, str] | None = None
211
+ if hasattr(pipe, "id2label"):
212
+ raw = pipe.id2label
213
+ if isinstance(raw, dict) and raw:
214
+ id2label = {int(key): str(value) for key, value in raw.items()}
215
+ if id2label is None:
216
+ config = getattr(pipe, "config", None)
217
+ raw = getattr(config, "id2label", None) if config is not None else None
218
+ if isinstance(raw, dict) and raw:
219
+ id2label = {int(key): str(value) for key, value in raw.items()}
220
+ return id2label or {}
221
+
222
+
223
+ def _build_label2id(id2label: dict[int, str]) -> dict[str, int]:
224
+ label2id: dict[str, int] = {}
225
+ for class_id, value in id2label.items():
226
+ synonyms = _split_synonyms(value)
227
+ if not synonyms:
228
+ continue
229
+ for synonym in synonyms:
230
+ label2id[synonym] = int(class_id)
231
+ label2id[value.strip()] = int(class_id)
232
+ return label2id
233
+
234
+
235
+ def resolve_class_labels(
236
+ pipe: DiffusionPipeline,
237
+ class_label: str,
238
+ *,
239
+ default: str,
240
+ ) -> int | str:
241
+ """Resolve a class name or id using the loaded model's id2label synonyms."""
242
+ label = str(class_label or "").strip() or default
243
+ if label.isdigit():
244
+ return int(label)
245
+ if label.replace(".", "", 1).isdigit():
246
+ return int(float(label))
247
+
248
+ id2label = _get_pipe_id2label(pipe)
249
+ if not id2label:
250
+ return label
251
+
252
+ label2id = _build_label2id(id2label)
253
+ if label in label2id:
254
+ return label2id[label]
255
+
256
+ for part in _split_synonyms(label):
257
+ if part in label2id:
258
+ return label2id[part]
259
+
260
+ normalized = label.casefold()
261
+ for class_id, value in id2label.items():
262
+ if value.strip().casefold() == normalized:
263
+ return int(class_id)
264
+ for part in _split_synonyms(value):
265
+ if part.casefold() == normalized:
266
+ return int(class_id)
267
+
268
+ if hasattr(pipe, "get_label_ids"):
269
+ for candidate in _split_synonyms(label):
270
+ try:
271
+ return pipe.get_label_ids(candidate)[0]
272
+ except (ValueError, TypeError):
273
+ continue
274
+
275
+ return label
276
+
277
+
278
+ def primary_label_for_id(pipe: DiffusionPipeline, class_id: int, *, fallback: str) -> str:
279
+ """Return the first synonym from id2label for a class id."""
280
+ id2label = _get_pipe_id2label(pipe)
281
+ value = id2label.get(int(class_id))
282
+ if not value:
283
+ return fallback
284
+ synonyms = _split_synonyms(value)
285
+ return synonyms[0] if synonyms else fallback
286
+
287
+
288
+ def default_class_label_for_pipe(pipe: DiffusionPipeline, profile: ModelProfile) -> str:
289
+ """Pick a sensible default label using id2label synonyms when available."""
290
+ id2label = _get_pipe_id2label(pipe)
291
+ if not id2label:
292
+ return profile.default_class_label
293
+
294
+ preferred_ids = (207, 285, 281)
295
+ for class_id in preferred_ids:
296
+ if class_id in id2label:
297
+ return primary_label_for_id(pipe, class_id, fallback=profile.default_class_label)
298
+
299
+ for value in id2label.values():
300
+ for synonym in _split_synonyms(value):
301
+ if synonym.casefold() == profile.default_class_label.casefold():
302
+ return synonym
303
+
304
+ first_value = next(iter(id2label.values()), profile.default_class_label)
305
+ synonyms = _split_synonyms(first_value)
306
+ return synonyms[0] if synonyms else profile.default_class_label
307
+
308
+
309
  def run_inference(
310
  profile: ModelProfile,
311
  pipe: DiffusionPipeline,
 
332
  }
333
  call_kwargs.update(extra_kwargs if extra_kwargs is not None else profile.extra_call_kwargs)
334
 
335
+ call_kwargs["class_labels"] = resolve_class_labels(
336
+ pipe,
337
+ class_label,
338
+ default=profile.default_class_label,
339
+ )
 
 
340
 
341
  native = profile.infer_resolution()
342
  if height > 0 and width > 0: