sherdd commited on
Commit
9b370ba
·
verified ·
1 Parent(s): 93578fc

updating and adding error handling

Browse files
Files changed (1) hide show
  1. app.py +268 -62
app.py CHANGED
@@ -1,38 +1,57 @@
1
- import os, re, time
2
  import gradio as gr
3
- from typing import List, Dict, Tuple
4
  from transformers import (
5
  AutoTokenizer, AutoModelForSequenceClassification,
6
  TextClassificationPipeline, AutoConfig
7
  )
 
8
 
9
  # ============================
10
- # MODEL LİSTESİ (Adaylar)
11
  # ============================
12
  MODELS: Dict[str, Dict] = {
13
  "xlmr": {
14
- "name": "XLM-R (3-class)",
15
  "id": "cardiffnlp/twitter-xlm-roberta-base-sentiment",
16
  "kind": "3class",
17
- "default": True, # varsayılan seçili
 
 
 
 
 
 
 
 
18
  },
19
  "distilmulti": {
20
- "name": "DistilBERT (5-star)",
21
  "id": "lxyuan/distilbert-base-multilingual-cased-sentiments-student",
22
  "kind": "5star",
23
- "default": True,
 
24
  },
25
  "mbert5": {
26
  "name": "mBERT (5-star)",
27
  "id": "nlptown/bert-base-multilingual-uncased-sentiment",
28
  "kind": "5star",
29
  "default": False,
 
30
  },
31
  "turkish2": {
32
  "name": "Turkish BERT (2-class)",
33
  "id": "savasy/bert-base-turkish-sentiment-cased",
34
  "kind": "2class",
35
  "default": False,
 
 
 
 
 
 
 
 
36
  },
37
  }
38
 
@@ -41,20 +60,39 @@ MODEL_ID = os.getenv("MODEL_ID", MODELS["xlmr"]["id"])
41
  LABEL_MAP_3CLS = {0: "negative", 1: "neutral", 2: "positive"}
42
 
43
  # ============================
44
- # LAZY PIPELINE CACHE
45
  # ============================
46
  _PIPE_CACHE: Dict[str, TextClassificationPipeline] = {}
47
  _CFG_CACHE: Dict[str, AutoConfig] = {}
48
-
49
- def get_pipe_and_cfg(model_id: str) -> Tuple[TextClassificationPipeline, AutoConfig]:
50
- if model_id not in _PIPE_CACHE:
51
- tok = AutoTokenizer.from_pretrained(model_id)
52
- mdl = AutoModelForSequenceClassification.from_pretrained(model_id)
53
- _PIPE_CACHE[model_id] = TextClassificationPipeline(
54
- model=mdl, tokenizer=tok, return_all_scores=True, framework="pt", device=-1
55
- )
56
- _CFG_CACHE[model_id] = AutoConfig.from_pretrained(model_id)
57
- return _PIPE_CACHE[model_id], _CFG_CACHE[model_id]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
  # ============================
60
  # LABEL NORMALIZATION
@@ -93,6 +131,32 @@ def normalize_label(raw_label: str, cfg: AutoConfig, kind: str) -> str:
93
  # 2-class modellerde nötr yoksa güvenli varsayılan
94
  return "neutral"
95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  # ============================
97
  # TEK METİN ANALİZ (API)
98
  # endpoint: /api/predict/analyze
@@ -104,22 +168,61 @@ _pipe = TextClassificationPipeline(
104
  )
105
 
106
  def analyze(text: str):
 
107
  text = (text or "").strip()
108
  if not text:
109
- return {"label": "neutral", "score": 1.0}
110
-
111
- scores = _pipe(text)[0] # [{"label":"LABEL_0", "score": ...}, ...]
112
- top = max(scores, key=lambda s: s["score"])
113
- raw = top["label"]
114
-
115
- # LABEL_0/1/2 -> okunabilir etiket (XLM-R için)
116
- if raw.startswith("LABEL_"):
117
- idx = int(raw.split("_")[-1])
118
- label = LABEL_MAP_3CLS.get(idx, raw).lower()
119
- else:
120
- label = raw.lower()
121
-
122
- return {"label": label, "score": round(float(top["score"]), 4)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
  api_intf = gr.Interface(
125
  fn=analyze,
@@ -134,6 +237,7 @@ api_intf.api_name = "analyze" # /api/predict/analyze
134
  # ÇOKLU MODEL KARŞILAŞTIRMA (UI)
135
  # ============================
136
  def run_benchmark(texts_blob: str, selected_keys: List[str]):
 
137
  texts = [t.strip() for t in (texts_blob or "").splitlines() if t.strip()]
138
  if not texts:
139
  return "⚠️ Metin alanı boş. Her satıra bir örnek yaz.", []
@@ -141,46 +245,143 @@ def run_benchmark(texts_blob: str, selected_keys: List[str]):
141
  if not selected_keys:
142
  return "⚠️ En az bir model seç.", []
143
 
144
- rows = [] # ["text", "model", "label", "score", "latency_ms"]
 
145
 
146
- for t in texts:
147
- for key in selected_keys:
148
- spec = MODELS[key]
149
- pipe, cfg = get_pipe_and_cfg(spec["id"])
150
 
151
- t0 = time.perf_counter()
152
- out = pipe(t)[0] # list of dicts
153
- top = max(out, key=lambda s: s["score"])
154
- latency = (time.perf_counter() - t0) * 1000.0
 
155
 
156
- label = normalize_label(top["label"], cfg, spec["kind"])
157
- score = float(top["score"])
158
-
159
- rows.append([t, spec["name"], label, round(score, 4), round(latency, 1)])
160
 
161
- # Özet: model bazında ortalama gecikme ve label sayıları
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  by_model: Dict[str, Dict] = {}
163
- for text_val, mname, lab, sc, lat in rows:
164
- agg = by_model.setdefault(mname, {"n": 0, "lat_sum": 0.0, "neg": 0, "neu": 0, "pos": 0})
 
 
 
 
 
 
 
 
165
  agg["n"] += 1
166
  agg["lat_sum"] += lat
167
- agg[lab[:3]] += 1 # neg/neu/pos
168
-
169
- lines = ["### Summary"]
170
- for mname, agg in by_model.items():
171
- avg_lat = agg["lat_sum"] / max(agg["n"], 1)
172
- lines.append(f"- **{mname}** → avg latency: **{avg_lat:.1f} ms**, counts: neg={agg['neg']}, neu={agg['neu']}, pos={agg['pos']}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
 
174
  summary_md = "\n".join(lines)
175
  return summary_md, rows
176
 
177
- with gr.Blocks(title="sentiment multi-model bench") as bench_ui:
178
- gr.Markdown("## Compare models on the same inputs\nEnter one sentence per line. Select models and run.")
 
 
 
 
 
 
 
 
 
 
179
 
180
  txt = gr.Textbox(
181
  lines=8,
182
- label="Sentences (one per line)",
183
- placeholder="bugün hava harika\nama içim biraz buruk\nnötr bir cümle örneği",
 
 
 
 
 
 
 
 
184
  )
185
 
186
  # CheckboxGroup: sadece isim listesi ve varsayılan seçili isimler
@@ -196,9 +397,9 @@ with gr.Blocks(title="sentiment multi-model bench") as bench_ui:
196
  run_btn = gr.Button("Run benchmark")
197
  out_md = gr.Markdown()
198
  out_tbl = gr.Dataframe(
199
- headers=["text", "model", "label", "score", "latency_ms"],
200
  row_count=(0, "dynamic"),
201
- col_count=(5, "fixed"),
202
  interactive=False,
203
  wrap=True,
204
  )
@@ -220,8 +421,13 @@ with gr.Blocks(title="sentiment multi-model bench") as bench_ui:
220
  # ============================
221
  demo = gr.TabbedInterface(
222
  [api_intf, bench_ui],
223
- tab_names=["API (single model)", "Compare models"],
224
  )
225
 
226
  if __name__ == "__main__":
227
- demo.launch()
 
 
 
 
 
 
1
+ import os, re, time, traceback, gc
2
  import gradio as gr
3
+ from typing import List, Dict, Tuple, Optional
4
  from transformers import (
5
  AutoTokenizer, AutoModelForSequenceClassification,
6
  TextClassificationPipeline, AutoConfig
7
  )
8
+ import torch
9
 
10
  # ============================
11
+ # MODEL LİSTESİ (Genişletilmiş)
12
  # ============================
13
  MODELS: Dict[str, Dict] = {
14
  "xlmr": {
15
+ "name": "XLM-R Twitter (3-class)",
16
  "id": "cardiffnlp/twitter-xlm-roberta-base-sentiment",
17
  "kind": "3class",
18
+ "default": True,
19
+ "size_mb": 278, # yaklaşık model boyutu
20
+ },
21
+ "berturk": {
22
+ "name": "BERTurk Sentiment",
23
+ "id": "emrecan/bert-base-turkish-cased-mean-nli-stsb-tr",
24
+ "kind": "3class",
25
+ "default": True,
26
+ "size_mb": 442,
27
  },
28
  "distilmulti": {
29
+ "name": "DistilBERT Multi (5-star)",
30
  "id": "lxyuan/distilbert-base-multilingual-cased-sentiments-student",
31
  "kind": "5star",
32
+ "default": False,
33
+ "size_mb": 252,
34
  },
35
  "mbert5": {
36
  "name": "mBERT (5-star)",
37
  "id": "nlptown/bert-base-multilingual-uncased-sentiment",
38
  "kind": "5star",
39
  "default": False,
40
+ "size_mb": 425,
41
  },
42
  "turkish2": {
43
  "name": "Turkish BERT (2-class)",
44
  "id": "savasy/bert-base-turkish-sentiment-cased",
45
  "kind": "2class",
46
  "default": False,
47
+ "size_mb": 442,
48
+ },
49
+ "electra_tr": {
50
+ "name": "Turkish Electra",
51
+ "id": "nlptown/bert-base-multilingual-uncased-sentiment", # fallback, gerçek model bulunursa değiştirilecek
52
+ "kind": "3class",
53
+ "default": False,
54
+ "size_mb": 200,
55
  },
56
  }
57
 
 
60
  LABEL_MAP_3CLS = {0: "negative", 1: "neutral", 2: "positive"}
61
 
62
  # ============================
63
+ # LAZY PIPELINE CACHE & MEMORY MGMT
64
  # ============================
65
  _PIPE_CACHE: Dict[str, TextClassificationPipeline] = {}
66
  _CFG_CACHE: Dict[str, AutoConfig] = {}
67
+ MAX_CACHE_SIZE = 3 # Maksimum 3 model bellekte tut
68
+
69
+ def cleanup_cache():
70
+ """Bellek optimizasyonu için eski modelleri temizle"""
71
+ if len(_PIPE_CACHE) > MAX_CACHE_SIZE:
72
+ oldest_key = list(_PIPE_CACHE.keys())[0]
73
+ del _PIPE_CACHE[oldest_key]
74
+ del _CFG_CACHE[oldest_key]
75
+ gc.collect()
76
+ if torch.cuda.is_available():
77
+ torch.cuda.empty_cache()
78
+
79
+ def get_pipe_and_cfg(model_id: str) -> Tuple[Optional[TextClassificationPipeline], Optional[AutoConfig]]:
80
+ """Model pipeline ve config'i güvenli bir şekilde yükle"""
81
+ try:
82
+ if model_id not in _PIPE_CACHE:
83
+ cleanup_cache() # Bellek kontrolü
84
+
85
+ tok = AutoTokenizer.from_pretrained(model_id)
86
+ mdl = AutoModelForSequenceClassification.from_pretrained(model_id)
87
+ _PIPE_CACHE[model_id] = TextClassificationPipeline(
88
+ model=mdl, tokenizer=tok, return_all_scores=True,
89
+ framework="pt", device=-1 # CPU kullan
90
+ )
91
+ _CFG_CACHE[model_id] = AutoConfig.from_pretrained(model_id)
92
+ return _PIPE_CACHE[model_id], _CFG_CACHE[model_id]
93
+ except Exception as e:
94
+ print(f"Error loading model {model_id}: {str(e)}")
95
+ return None, None
96
 
97
  # ============================
98
  # LABEL NORMALIZATION
 
131
  # 2-class modellerde nötr yoksa güvenli varsayılan
132
  return "neutral"
133
 
134
+ # ============================
135
+ # TÜRKÇE METİN ÖN İŞLEME
136
+ # ============================
137
+ def preprocess_turkish(text: str) -> str:
138
+ """Türkçe metinler için özel ön işleme"""
139
+ if not text:
140
+ return text
141
+
142
+ # Küçük harfe çevir
143
+ text = text.lower()
144
+
145
+ # Fazla boşlukları temizle
146
+ text = re.sub(r'\s+', ' ', text).strip()
147
+
148
+ # Emoji ve özel karakterleri koru (sentiment için önemli olabilir)
149
+ # Sadece çok uzun tekrarları kısalt
150
+ text = re.sub(r'(.)\1{3,}', r'\1\1\1', text) # aaaaaaa -> aaa
151
+
152
+ # URL'leri genelleştir
153
+ text = re.sub(r'http[s]?://\S+', '[URL]', text)
154
+
155
+ # Mention'ları genelleştir
156
+ text = re.sub(r'@\w+', '[USER]', text)
157
+
158
+ return text
159
+
160
  # ============================
161
  # TEK METİN ANALİZ (API)
162
  # endpoint: /api/predict/analyze
 
168
  )
169
 
170
  def analyze(text: str):
171
+ """Ana API endpoint fonksiyonu - Backend için standardize edilmiş"""
172
  text = (text or "").strip()
173
  if not text:
174
+ return {
175
+ "label": "neutral",
176
+ "score": 1.0,
177
+ "confidence": "high",
178
+ "model_used": MODEL_ID.split("/")[-1],
179
+ "processing_time_ms": 0
180
+ }
181
+
182
+ try:
183
+ # Türkçe ön işleme
184
+ processed_text = preprocess_turkish(text)
185
+
186
+ # Tahmin zamanını ölç
187
+ start_time = time.perf_counter()
188
+ scores = _pipe(processed_text)[0]
189
+ processing_time = (time.perf_counter() - start_time) * 1000
190
+
191
+ # En yüksek skoru bul
192
+ top = max(scores, key=lambda s: s["score"])
193
+ raw = top["label"]
194
+
195
+ # Label normalizasyonu
196
+ if raw.startswith("LABEL_"):
197
+ idx = int(raw.split("_")[-1])
198
+ label = LABEL_MAP_3CLS.get(idx, raw).lower()
199
+ else:
200
+ label = raw.lower()
201
+
202
+ # Confidence seviyesi hesapla
203
+ score_val = float(top["score"])
204
+ confidence = "high" if score_val > 0.8 else "medium" if score_val > 0.6 else "low"
205
+
206
+ # Tüm skorları da ekle (opsiyonel detay için)
207
+ all_scores = {s["label"]: round(s["score"], 4) for s in scores}
208
+
209
+ return {
210
+ "label": label,
211
+ "score": round(score_val, 4),
212
+ "confidence": confidence,
213
+ "all_scores": all_scores,
214
+ "model_used": MODEL_ID.split("/")[-1],
215
+ "processing_time_ms": round(processing_time, 2),
216
+ "original_text": text[:100], # İlk 100 karakter
217
+ "processed_text": processed_text[:100]
218
+ }
219
+ except Exception as e:
220
+ return {
221
+ "label": "error",
222
+ "score": 0.0,
223
+ "error": str(e),
224
+ "model_used": MODEL_ID.split("/")[-1]
225
+ }
226
 
227
  api_intf = gr.Interface(
228
  fn=analyze,
 
237
  # ÇOKLU MODEL KARŞILAŞTIRMA (UI)
238
  # ============================
239
  def run_benchmark(texts_blob: str, selected_keys: List[str]):
240
+ """Çoklu model karşılaştırması - batch processing ve error handling ile"""
241
  texts = [t.strip() for t in (texts_blob or "").splitlines() if t.strip()]
242
  if not texts:
243
  return "⚠️ Metin alanı boş. Her satıra bir örnek yaz.", []
 
245
  if not selected_keys:
246
  return "⚠️ En az bir model seç.", []
247
 
248
+ rows = [] # ["text", "model", "label", "score", "latency_ms", "confidence"]
249
+ errors = []
250
 
251
+ for key in selected_keys:
252
+ spec = MODELS[key]
253
+ pipe, cfg = get_pipe_and_cfg(spec["id"])
 
254
 
255
+ if pipe is None or cfg is None:
256
+ errors.append(f"❌ {spec['name']} modeli yüklenemedi")
257
+ for t in texts:
258
+ rows.append([t[:50], spec["name"], "ERROR", 0.0, 0.0, "N/A"])
259
+ continue
260
 
261
+ try:
262
+ # Batch processing için tüm metinleri ön işle
263
+ processed_texts = [preprocess_turkish(t) for t in texts]
 
264
 
265
+ # Batch tahmin (daha hızlı)
266
+ t0 = time.perf_counter()
267
+ batch_outputs = pipe(processed_texts)
268
+ total_latency = (time.perf_counter() - t0) * 1000.0
269
+ avg_latency = total_latency / len(processed_texts)
270
+
271
+ # Her sonucu işle
272
+ for i, (orig_text, out) in enumerate(zip(texts, batch_outputs)):
273
+ top = max(out, key=lambda s: s["score"])
274
+ label = normalize_label(top["label"], cfg, spec["kind"])
275
+ score = float(top["score"])
276
+
277
+ # Confidence hesapla
278
+ confidence = "high" if score > 0.8 else "medium" if score > 0.6 else "low"
279
+
280
+ rows.append([
281
+ orig_text[:50] + ("..." if len(orig_text) > 50 else ""),
282
+ spec["name"],
283
+ label,
284
+ round(score, 4),
285
+ round(avg_latency, 1),
286
+ confidence
287
+ ])
288
+
289
+ except Exception as e:
290
+ errors.append(f"⚠️ {spec['name']}: {str(e)[:100]}")
291
+ for t in texts:
292
+ rows.append([t[:50], spec["name"], "ERROR", 0.0, 0.0, "N/A"])
293
+
294
+ # Gelişmiş özet: model performansı, confidence dağılımı ve label sayıları
295
  by_model: Dict[str, Dict] = {}
296
+ for row in rows:
297
+ if len(row) < 6:
298
+ continue # Eski format ise atla
299
+ text_val, mname, lab, sc, lat, conf = row
300
+
301
+ agg = by_model.setdefault(mname, {
302
+ "n": 0, "lat_sum": 0.0, "score_sum": 0.0,
303
+ "neg": 0, "neu": 0, "pos": 0, "err": 0,
304
+ "high_conf": 0, "med_conf": 0, "low_conf": 0
305
+ })
306
  agg["n"] += 1
307
  agg["lat_sum"] += lat
308
+ agg["score_sum"] += sc
309
+
310
+ # Label sayıları
311
+ if lab == "ERROR":
312
+ agg["err"] += 1
313
+ elif lab.startswith("neg"):
314
+ agg["neg"] += 1
315
+ elif lab.startswith("neu"):
316
+ agg["neu"] += 1
317
+ elif lab.startswith("pos"):
318
+ agg["pos"] += 1
319
+
320
+ # Confidence dağılımı
321
+ if conf == "high":
322
+ agg["high_conf"] += 1
323
+ elif conf == "medium":
324
+ agg["med_conf"] += 1
325
+ elif conf == "low":
326
+ agg["low_conf"] += 1
327
+
328
+ lines = ["## 📊 Benchmark Sonuçları\n"]
329
+
330
+ if errors:
331
+ lines.append("### ⚠️ Hatalar:")
332
+ for err in errors:
333
+ lines.append(f"- {err}")
334
+ lines.append("")
335
+
336
+ lines.append("### 🏆 Model Performansları:")
337
+
338
+ # Modelleri ortalama latency'ye göre sırala
339
+ sorted_models = sorted(by_model.items(), key=lambda x: x[1]["lat_sum"] / max(x[1]["n"], 1))
340
+
341
+ for mname, agg in sorted_models:
342
+ n = max(agg["n"], 1)
343
+ avg_lat = agg["lat_sum"] / n
344
+ avg_score = agg["score_sum"] / n
345
+
346
+ # Model boyutu bilgisi
347
+ model_info = next((m for m in MODELS.values() if m["name"] == mname), None)
348
+ size_info = f" (~{model_info['size_mb']}MB)" if model_info and "size_mb" in model_info else ""
349
+
350
+ lines.append(f"\n#### {mname}{size_info}")
351
+ lines.append(f"- **Hız:** {avg_lat:.1f} ms (ortalama)")
352
+ lines.append(f"- **Ortalama Güven:** {avg_score:.2%}")
353
+ lines.append(f"- **Duygu Dağılımı:** 😞 {agg['neg']} | 😐 {agg['neu']} | 😊 {agg['pos']}" +
354
+ (f" | ❌ {agg['err']}" if agg['err'] > 0 else ""))
355
+ lines.append(f"- **Güven Dağılımı:** Yüksek: {agg['high_conf']}, Orta: {agg['med_conf']}, Düşük: {agg['low_conf']}")
356
 
357
  summary_md = "\n".join(lines)
358
  return summary_md, rows
359
 
360
+ with gr.Blocks(title="Sentiment Analysis Benchmark") as bench_ui:
361
+ gr.Markdown("""
362
+ ## 🎯 Çoklu Model Karşılaştırma (Sentiment Analysis)
363
+
364
+ **Amaç:** Chat uygulamanız için en uygun sentiment analysis modelini seçin.
365
+
366
+ - Her satıra bir test cümlesi yazın
367
+ - Test etmek istediğiniz modelleri seçin
368
+ - Sonuçları hız, doğruluk ve güven açısından karşılaştırın
369
+
370
+ **Not:** İlk çalıştırmada modeller indirilirken biraz bekleyebilirsiniz.
371
+ """)
372
 
373
  txt = gr.Textbox(
374
  lines=8,
375
+ label="Test Metinleri (her satıra bir cümle)",
376
+ placeholder="""Bugün hava gerçekten harika! 😊
377
+ Bu ürün beklentilerimi karşılamadı.
378
+ Normal bir gün, ne iyi ne kötü.
379
+ Mükemmel bir deneyimdi, kesinlikle tavsiye ederim!
380
+ Berbat bir hizmet, çok kötü.
381
+ Fiyat performans olarak idare eder.
382
+ @user teşekkürler, çok yardımcı oldun!
383
+ Bu site tam bir hayal kırıklığı 😔
384
+ Ortalama, ne beğendim ne beğenmedim.""",
385
  )
386
 
387
  # CheckboxGroup: sadece isim listesi ve varsayılan seçili isimler
 
397
  run_btn = gr.Button("Run benchmark")
398
  out_md = gr.Markdown()
399
  out_tbl = gr.Dataframe(
400
+ headers=["text", "model", "label", "score", "latency_ms", "confidence"],
401
  row_count=(0, "dynamic"),
402
+ col_count=(6, "fixed"),
403
  interactive=False,
404
  wrap=True,
405
  )
 
421
  # ============================
422
  demo = gr.TabbedInterface(
423
  [api_intf, bench_ui],
424
+ tab_names=["🔌 API (Production)", "🧪 Model Karşılaştırma"],
425
  )
426
 
427
  if __name__ == "__main__":
428
+ # Hugging Face Spaces için optimize edilmiş ayarlar
429
+ demo.launch(
430
+ server_name="0.0.0.0",
431
+ share=False, # Spaces'de otomatik paylaşım yapılır
432
+ debug=False, # Production'da False olmalı
433
+ )