CanerDedeoglu commited on
Commit
18308a9
·
verified ·
1 Parent(s): a8b66ea

Update handler.py

Browse files
Files changed (1) hide show
  1. handler.py +130 -39
handler.py CHANGED
@@ -1,6 +1,6 @@
1
  # -*- coding: utf-8 -*-
2
  """
3
- PULSE ECG Handler — Demo Parity + Style Hint + Robust Fallbacks + Debug
4
  - Demo app.py ile aynı üretim ayarları:
5
  do_sample=True, temperature=0.05, top_p=1.0, max_new_tokens=4096
6
  - Stopping: konuşma ayırıcıda (conv.sep/sep2) güvenli token-eşleşmeli kriter
@@ -11,6 +11,7 @@ PULSE ECG Handler — Demo Parity + Style Hint + Robust Fallbacks + Debug
11
  - Post-process: yalnızca whitespace/biçim temizliği
12
  - Ekler:
13
  * DEBUG yardımcıları (ENV: DEBUG=1)
 
14
  * image_processor fallback (AutoProcessor → CLIPImageProcessor)
15
  * process_images fallback (torchvision + CLIP norm)
16
  * FastAPI wrapper: /health, /info, /query, /debug
@@ -158,6 +159,53 @@ def _normalize_whitespace(text: str) -> str:
158
  def _postprocess_min(text: str) -> str:
159
  return _normalize_whitespace(text)
160
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  # ====== Güvenli Stop Kriteri (conv separator) ======
162
  class SafeKeywordsStoppingCriteria(StoppingCriteria):
163
  def __init__(self, keyword: str, tokenizer):
@@ -193,7 +241,7 @@ class ChatSessionManager:
193
  def __init__(self):
194
  self.chatbot = None
195
  self.args = None
196
- self.model_path = None
197
  def init_if_needed(self, args, model_path, tokenizer, model, image_processor, context_len):
198
  if self.chatbot is None:
199
  self.args = args
@@ -263,31 +311,49 @@ def generate_response(
263
  device = next(chatbot.model.parameters()).device
264
  dtype = torch.float16
265
 
266
- # Görüntü ön-işleme → tensör (fallback'lı)
 
 
 
 
 
267
  try:
268
- dbg(f"[pre] PIL image size={pil_img.size}, mode={pil_img.mode}, processor={type(chatbot.image_processor)}")
269
- processed = process_images([pil_img], chatbot.image_processor, chatbot.model.config)
270
- dbg("[pre] process_images ok")
271
-
272
- if isinstance(processed, (list, tuple)) and len(processed) > 0:
273
- image_tensor = processed[0]
274
- elif isinstance(processed, torch.Tensor):
275
- image_tensor = processed[0] if processed.ndim == 4 else processed
 
 
276
  else:
277
- raise ValueError("Image processing returned empty")
278
-
279
- if image_tensor.ndim == 3:
280
- image_tensor = image_tensor.unsqueeze(0)
281
- image_tensor = image_tensor.to(device=device, dtype=dtype)
282
- dbg(f"[pre] tensor shape={tuple(image_tensor.shape)} dtype={image_tensor.dtype} device={image_tensor.device}")
283
- except Exception as e:
284
- warn(f"[pre] process_images failed: {e} → manual CLIP preprocess fallback kullanılacak.")
285
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
  from torchvision import transforms
287
  from torchvision.transforms import InterpolationMode
288
  preprocess = transforms.Compose([
289
- transforms.Resize(224, interpolation=InterpolationMode.BICUBIC),
290
- transforms.CenterCrop(224),
291
  transforms.ToTensor(),
292
  transforms.Normalize(
293
  mean=[0.48145466, 0.4578275, 0.40821073],
@@ -295,9 +361,10 @@ def generate_response(
295
  ),
296
  ])
297
  image_tensor = preprocess(pil_img).unsqueeze(0).to(device=device, dtype=dtype)
298
- dbg("[pre] manual CLIP preprocess fallback ok → tensor shape=" + str(tuple(image_tensor.shape)))
299
- except Exception as ee:
300
- return {"error": f"Image processing failed (and fallback failed): {ee}"}
 
301
 
302
  msg = (message_text or "").strip()
303
  msg = f"{msg}\n\n{STYLE_HINT}"
@@ -454,33 +521,51 @@ def initialize_model():
454
  model_.eval()
455
  dbg(f"[init] device={next(model_.parameters()).device}, cuda_available={torch.cuda.is_available()}")
456
 
457
- # --- image_processor fallback zinciri ---
 
 
 
 
458
  try:
459
  if image_processor_ is None:
460
- dbg("[init] image_processor None → AutoProcessor fallback deneniyor…")
461
  try:
462
  from transformers import AutoProcessor
463
  image_processor_ = AutoProcessor.from_pretrained(args.model_path)
464
- dbg("[init] image_processor: AutoProcessor.from_pretrained(model_path) ile yüklendi.")
465
  except Exception as _e1:
466
- dbg(f"[init] AutoProcessor failed: {_e1} → CLIPImageProcessor fallback deneniyor…")
467
- from transformers import CLIPImageProcessor
468
- image_processor_ = CLIPImageProcessor.from_pretrained("openai/clip-vit-large-patch14")
469
- warn("[init] image_processor: CLIPImageProcessor(openai/clip-vit-large-patch14) fallback kullanılıyor.")
 
 
 
 
 
 
 
470
  except Exception as _e:
471
- warn(f"[init] image_processor fallback failed: {_e}")
 
 
 
 
 
 
 
472
 
473
  # --- image_processor introspection ---
474
  try:
475
  ip = image_processor_
476
  if ip is not None:
477
  crop_sz = getattr(getattr(ip, "crop_size", None), "height", None) or getattr(ip, "crop_size", None)
478
- size_sz = getattr(getattr(ip, "size", None), "height", None) or getattr(ip, "size", None)
479
  dbg(f"[init] image_processor crop_size={crop_sz} size={size_sz} class={ip.__class__.__name__}")
480
  else:
481
  warn("[init] image_processor yine None (fallback da başarısız).")
482
- except Exception as e_ip:
483
- warn(f"[init] image_processor inspect error: {e_ip}")
484
 
485
  globals()["tokenizer"] = tokenizer_
486
  globals()["model"] = model_
@@ -510,7 +595,7 @@ class EndpointHandler:
510
  return get_model_info()
511
 
512
  if __name__ == "__main__":
513
- print("Handler ready (Demo Parity + Style Hint + whitespace post-process + fallbacks + debug). Use `EndpointHandler` or `query`.")
514
 
515
  # ===================== Minimal FastAPI Wrapper =====================
516
  try:
@@ -567,9 +652,14 @@ if FASTAPI_AVAILABLE:
567
  ip = image_processor
568
  ip_cls = ip.__class__.__name__ if ip else None
569
  crop_sz = getattr(getattr(ip, "crop_size", None), "height", None) or getattr(ip, "crop_size", None)
570
- size_sz = getattr(getattr(ip, "size", None), "height", None) or getattr(ip, "size", None)
 
 
 
 
 
571
  except Exception:
572
- ip_cls, crop_sz, size_sz = None, None, None
573
 
574
  return {
575
  "debug": bool(DEBUG),
@@ -579,7 +669,8 @@ if FASTAPI_AVAILABLE:
579
  "context_len": context_len,
580
  "image_processor_class": ip_cls,
581
  "image_processor_crop_size": crop_sz,
582
- "image_processor_size": size_sz,
 
583
  "model_path": args.model_path if args else None,
584
  }
585
 
 
1
  # -*- coding: utf-8 -*-
2
  """
3
+ PULSE ECG Handler — Demo Parity + Style Hint + Robust Fallbacks + Debug + Dynamic Vision Size
4
  - Demo app.py ile aynı üretim ayarları:
5
  do_sample=True, temperature=0.05, top_p=1.0, max_new_tokens=4096
6
  - Stopping: konuşma ayırıcıda (conv.sep/sep2) güvenli token-eşleşmeli kriter
 
11
  - Post-process: yalnızca whitespace/biçim temizliği
12
  - Ekler:
13
  * DEBUG yardımcıları (ENV: DEBUG=1)
14
+ * Dynamic vision size: vision tower -> processor + preprocess/fallback
15
  * image_processor fallback (AutoProcessor → CLIPImageProcessor)
16
  * process_images fallback (torchvision + CLIP norm)
17
  * FastAPI wrapper: /health, /info, /query, /debug
 
159
  def _postprocess_min(text: str) -> str:
160
  return _normalize_whitespace(text)
161
 
162
+ # ====== Vision helpers (dynamic size) ======
163
+ def get_vision_expected_size(m, default: int = 336) -> int:
164
+ """
165
+ Modelin vision tower'ının beklediği input boyutunu döndürür (örn. 336).
166
+ LLaVA/CLIP konfiglerinde genelde `image_size` bulunur.
167
+ """
168
+ try:
169
+ vt = m.get_vision_tower()
170
+ vt_cfg = getattr(getattr(vt, "vision_tower", vt), "config", None)
171
+ if vt_cfg is None:
172
+ return default
173
+ if getattr(vt_cfg, "image_size", None):
174
+ return int(vt_cfg.image_size)
175
+ vc = getattr(vt_cfg, "vision_config", None)
176
+ if vc and getattr(vc, "image_size", None):
177
+ return int(vc.image_size)
178
+ except Exception as e:
179
+ dbg(f"[get_vision_expected_size] fallback default={default} because: {e}")
180
+ return default
181
+
182
+ def force_processor_size(proc, size: int):
183
+ """Processor'ın resize/crop alanlarını güvenle hedef boyuta zorlar."""
184
+ try:
185
+ # size
186
+ if hasattr(proc, "size"):
187
+ if isinstance(proc.size, dict):
188
+ proc.size["shortest_edge"] = size
189
+ else:
190
+ try:
191
+ proc.size.shortest_edge = size # type: ignore[attr-defined]
192
+ except Exception:
193
+ proc.size = {"shortest_edge": size}
194
+ # crop_size
195
+ if hasattr(proc, "crop_size"):
196
+ if isinstance(proc.crop_size, dict):
197
+ proc.crop_size["height"] = size
198
+ proc.crop_size["width"] = size
199
+ else:
200
+ try:
201
+ proc.crop_size.height = size # type: ignore[attr-defined]
202
+ proc.crop_size.width = size # type: ignore[attr-defined]
203
+ except Exception:
204
+ proc.crop_size = {"height": size, "width": size}
205
+ dbg(f"[processor] forced size={size}")
206
+ except Exception as e:
207
+ warn(f"[processor] force size failed: {e}")
208
+
209
  # ====== Güvenli Stop Kriteri (conv separator) ======
210
  class SafeKeywordsStoppingCriteria(StoppingCriteria):
211
  def __init__(self, keyword: str, tokenizer):
 
241
  def __init__(self):
242
  self.chatbot = None
243
  self.args = None
244
+ self.model_path = None
245
  def init_if_needed(self, args, model_path, tokenizer, model, image_processor, context_len):
246
  if self.chatbot is None:
247
  self.args = args
 
311
  device = next(chatbot.model.parameters()).device
312
  dtype = torch.float16
313
 
314
+ # === Görüntü ön-işleme → tensör (dinamik boy) ===
315
+ expected_size = get_vision_expected_size(chatbot.model, default=336)
316
+ dbg(f"[pre] dynamic expected_size={expected_size} | processor={type(chatbot.image_processor)}")
317
+
318
+ # 3.1) Processor.preprocess varsa kullan (en stabil yol)
319
+ image_tensor = None
320
  try:
321
+ if hasattr(chatbot.image_processor, "preprocess"):
322
+ px = chatbot.image_processor.preprocess(pil_img, return_tensors="pt")
323
+ image_tensor = px.get("pixel_values", px)
324
+ if not isinstance(image_tensor, torch.Tensor):
325
+ # Bazı processor'lar nested dict döndürebilir
326
+ image_tensor = image_tensor["pixel_values"]
327
+ if image_tensor.ndim == 3:
328
+ image_tensor = image_tensor.unsqueeze(0)
329
+ image_tensor = image_tensor.to(device=device, dtype=dtype)
330
+ dbg(f"[pre] processor.preprocess ok → {tuple(image_tensor.shape)}")
331
  else:
332
+ raise AttributeError("processor has no preprocess")
333
+ except Exception as e_pre:
334
+ warn(f"[pre] processor.preprocess not used: {e_pre} → process_images denenecek…")
335
+ # 3.2) LLaVA'nın process_images yolu
 
 
 
 
336
  try:
337
+ processed = process_images([pil_img], chatbot.image_processor, chatbot.model.config)
338
+ if isinstance(processed, (list, tuple)) and len(processed) > 0:
339
+ image_tensor = processed[0]
340
+ elif isinstance(processed, torch.Tensor):
341
+ image_tensor = processed[0] if processed.ndim == 4 else processed
342
+ else:
343
+ raise ValueError("process_images returned empty")
344
+
345
+ if image_tensor.ndim == 3:
346
+ image_tensor = image_tensor.unsqueeze(0)
347
+ image_tensor = image_tensor.to(device=device, dtype=dtype)
348
+ dbg(f"[pre] process_images ok → {tuple(image_tensor.shape)}")
349
+ except Exception as e_proc:
350
+ warn(f"[pre] process_images failed: {e_proc} → manual CLIP fallback (dinamik size) kullanılacak.")
351
+ # 3.3) Manuel CLIP fallback (dinamik expected_size)
352
  from torchvision import transforms
353
  from torchvision.transforms import InterpolationMode
354
  preprocess = transforms.Compose([
355
+ transforms.Resize(expected_size, interpolation=InterpolationMode.BICUBIC),
356
+ transforms.CenterCrop(expected_size),
357
  transforms.ToTensor(),
358
  transforms.Normalize(
359
  mean=[0.48145466, 0.4578275, 0.40821073],
 
361
  ),
362
  ])
363
  image_tensor = preprocess(pil_img).unsqueeze(0).to(device=device, dtype=dtype)
364
+ dbg(f"[pre] manual fallback ok → {tuple(image_tensor.shape)}")
365
+
366
+ if image_tensor is None:
367
+ return {"error": "Image processing failed (no tensor produced)"}
368
 
369
  msg = (message_text or "").strip()
370
  msg = f"{msg}\n\n{STYLE_HINT}"
 
521
  model_.eval()
522
  dbg(f"[init] device={next(model_.parameters()).device}, cuda_available={torch.cuda.is_available()}")
523
 
524
+ # --- vision tower beklenen image_size'ı al ---
525
+ expected_size = get_vision_expected_size(model_, default=336)
526
+ dbg(f"[init] vision expected image_size={expected_size}")
527
+
528
+ # --- image_processor fallback zinciri (model path > AutoProcessor > CLIP 224/336) ---
529
  try:
530
  if image_processor_ is None:
531
+ dbg("[init] image_processor None → AutoProcessor(model_path) deneniyor…")
532
  try:
533
  from transformers import AutoProcessor
534
  image_processor_ = AutoProcessor.from_pretrained(args.model_path)
535
+ dbg("[init] image_processor: AutoProcessor.from_pretrained(model_path) yüklendi.")
536
  except Exception as _e1:
537
+ dbg(f"[init] AutoProcessor(model_path) failed: {_e1}")
538
+ try:
539
+ from transformers import AutoProcessor
540
+ clip_id = "openai/clip-vit-large-patch14-336" if expected_size >= 336 else "openai/clip-vit-large-patch14"
541
+ image_processor_ = AutoProcessor.from_pretrained(clip_id)
542
+ dbg(f"[init] AutoProcessor({clip_id}) yüklendi.")
543
+ except Exception as _e2:
544
+ from transformers import CLIPImageProcessor
545
+ clip_id = "openai/clip-vit-large-patch14-336" if expected_size >= 336 else "openai/clip-vit-large-patch14"
546
+ image_processor_ = CLIPImageProcessor.from_pretrained(clip_id)
547
+ warn(f"[init] CLIPImageProcessor({clip_id}) fallback kullanılıyor.")
548
  except Exception as _e:
549
+ warn(f"[init] image_processor fallback chain failed: {_e}")
550
+
551
+ # --- processor'ın boyutlarını vision tower'a uydur ---
552
+ try:
553
+ if image_processor_ is not None:
554
+ force_processor_size(image_processor_, expected_size)
555
+ except Exception as e_ip:
556
+ warn(f"[init] processor size set error: {e_ip}")
557
 
558
  # --- image_processor introspection ---
559
  try:
560
  ip = image_processor_
561
  if ip is not None:
562
  crop_sz = getattr(getattr(ip, "crop_size", None), "height", None) or getattr(ip, "crop_size", None)
563
+ size_sz = getattr(getattr(ip, "size", None), "shortest_edge", None) or getattr(ip, "size", None)
564
  dbg(f"[init] image_processor crop_size={crop_sz} size={size_sz} class={ip.__class__.__name__}")
565
  else:
566
  warn("[init] image_processor yine None (fallback da başarısız).")
567
+ except Exception as e_ip2:
568
+ warn(f"[init] image_processor inspect error: {e_ip2}")
569
 
570
  globals()["tokenizer"] = tokenizer_
571
  globals()["model"] = model_
 
595
  return get_model_info()
596
 
597
  if __name__ == "__main__":
598
+ print("Handler ready (Demo Parity + Style Hint + whitespace post-process + dynamic size + fallbacks + debug). Use `EndpointHandler` or `query`.")
599
 
600
  # ===================== Minimal FastAPI Wrapper =====================
601
  try:
 
652
  ip = image_processor
653
  ip_cls = ip.__class__.__name__ if ip else None
654
  crop_sz = getattr(getattr(ip, "crop_size", None), "height", None) or getattr(ip, "crop_size", None)
655
+ size_short = getattr(getattr(ip, "size", None), "shortest_edge", None) or getattr(ip, "size", None)
656
+ except Exception:
657
+ ip_cls, crop_sz, size_short = None, None, None
658
+
659
+ try:
660
+ ve = get_vision_expected_size(model, default=None) if model else None
661
  except Exception:
662
+ ve = None
663
 
664
  return {
665
  "debug": bool(DEBUG),
 
669
  "context_len": context_len,
670
  "image_processor_class": ip_cls,
671
  "image_processor_crop_size": crop_sz,
672
+ "image_processor_size": {"shortest_edge": size_short},
673
+ "vision_expected_image_size": ve,
674
  "model_path": args.model_path if args else None,
675
  }
676