leilaghomashchi commited on
Commit
0e8df19
·
verified ·
1 Parent(s): bb578da

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +142 -101
app.py CHANGED
@@ -1,7 +1,7 @@
1
  #!/usr/bin/env python3
2
  # -*- coding: utf-8 -*-
3
  """
4
- سیستم benchmark ناشناس‌سازی - ورژن اصلاح شده با الگوهای دقیق
5
  """
6
 
7
  import pandas as pd
@@ -33,42 +33,52 @@ def convert_to_serializable(obj):
33
  else:
34
  return obj
35
 
36
- # ===== کلاس پردازش entities با الگوهای دقیق =====
37
- class EntityExtractor:
38
  def __init__(self):
39
- # الگوهای دقیق‌تر با lookahead و lookbehind
40
  self.patterns = {
41
  'COMPANY': [
42
- r'شرکت\s+پتروشیمی\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]+?(?=\s|$|،|\.)',
43
- r'شرکت\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,30}?(?=\s+(?:با|در|که|مربوط)|$|،|\.)',
44
- r'بانک\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,20}?(?=\s+(?:با|در|که|ارائه|صادر)|$|،|\.)',
45
- r'شرکت\s+تولیدی\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,20}',
46
- r'شرکت\s+پردازش\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,30}',
 
47
  ],
48
 
49
  'LOCATION': [
50
- r'بندر\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,15}',
 
 
 
 
51
  r'\b(?:تهران|اصفهان|ماهشهر|عسلویه|بندرعباس|اهواز|شیراز|مشهد|تبریز|کرج|قم|رشت|کرمان|یزد|زاهدان|بوشهر|خرمشهر|آبادان|اراک|قزوین|خوزستان)\b',
 
52
  r'جمهوری\s+اسلامی\s+ایران',
 
53
  ],
54
 
55
  'PERSON': [
56
- r'آقای\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,25}?(?=\s+با\s+کد|\s+مدیر|$|،|\.)',
57
- r'خانم\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,25}?(?=\s+با\s+کد|\s+همسر|$|،|\.)',
58
- r'(?:[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,25})\s+مدیرعامل',
59
- r'مدیر\s+مالی\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,25}',
 
60
  ],
61
 
62
  'DATE': [
 
 
63
  r'\d{4}\/\d{1,2}\/\d{1,2}',
64
- r'سال\s+مالی\s+منتهی\s+به\s+[۰-۹\u06F0-\u06F9]{1,2}\s+[\u0600-\u06FF\u0750-\u077F]+\s+[۰-۹\u06F0-\u06F9]{4}',
65
- r'[۰-۹\u06F0-\u06F9]{1,2}\s+[\u0600-\u06FF\u0750-\u077F]+\s+[۰-۹\u06F0-\u06F9]{4}',
66
  ],
67
 
68
  'PHONE': [
69
- r'09\d{9}(?!\d)', # شماره موبایل
70
- r'021-\d{8}', # تهران
71
- r'0\d{2,3}-\d{7,8}', # سایر شهرها
 
72
  ],
73
 
74
  'EMAIL': [
@@ -76,21 +86,22 @@ class EntityExtractor:
76
  ],
77
 
78
  'AMOUNT': [
79
- r'\d{6,}\s*تومان', # مبالغ 6 رقمی یا بیشتر
80
- r'مبلغ\s+\d{6,}(?:\s*تومان)?',
81
  r'\d+\s*درصد',
82
  ],
83
 
84
  'ACCOUNT': [
85
- r'\d{3}-\d{3}-\d{3}-\d', # شماره حساب
86
- r'\d{4}-\d{4}-\d{4}-\d{4}', # شماره کارت
87
  ],
88
 
89
  'ID_NUMBER': [
 
90
  r'کد\s+ملی\s+\d{10}',
91
  r'شناسه\s+ملی\s+\d{11}',
92
- r'(?<!\d)\d{10}(?!\d)', # کد ملی 10 رقمی مستقل
93
- r'(?<!\d)\d{11}(?!\d)', # شناسه 11 رقمی مستقل
94
  ],
95
 
96
  'DOCUMENT_NUMBER': [
@@ -101,33 +112,53 @@ class EntityExtractor:
101
  }
102
 
103
  def clean_entity(self, text):
104
- """تمیز کردن entity استخراج شده"""
105
  # حذف کلمات اضافی در انتها
106
- text = re.sub(r'\s*(در|که|با|به|از|را|و|یا|است|بوده|نموده|صادر|ارائه|معرفی)\s*$', '', text, flags=re.IGNORECASE)
107
  # حذف فاصله‌های اضافی
108
  text = re.sub(r'\s+', ' ', text).strip()
109
  return text
110
 
111
- def is_valid_entity(self, text):
112
- """بررسی معتبر بودن entity"""
113
- if len(text) < 2:
114
  return False
115
 
116
- # کلمات ممنوع که نباید به تنهایی entity باشند
117
- forbidden_alone = ['شد', 'کرد', 'است', 'بود', 'در', 'که', 'با', 'از', 'به', 'را', 'و', 'یا',
118
- رکت', 'بانک', 'آقای', 'خانم', 'تومان', 'مبلغ']
 
 
119
 
120
- if text.lower().strip() in forbidden_alone:
121
  return False
122
 
123
- # بررسی طول منطقی
124
- if len(text) > 50: # خیلی طولانی است
125
- return False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
  return True
128
 
129
  def extract_entities(self, text):
130
- """استخراج entities از متن"""
131
  if not text or text.strip() == '':
132
  return {}
133
 
@@ -143,7 +174,7 @@ class EntityExtractor:
143
 
144
  for match in matches:
145
  entity = self.clean_entity(match.group(0))
146
- if self.is_valid_entity(entity):
147
  found_entities.append(entity)
148
 
149
  except re.error as e:
@@ -152,7 +183,18 @@ class EntityExtractor:
152
 
153
  # حذف تکراری‌ها و مرتب‌سازی
154
  if found_entities:
155
- entities[category] = sorted(list(set(found_entities)))
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  return entities
158
 
@@ -184,25 +226,24 @@ class EntityExtractor:
184
  return codes
185
 
186
  # ===== کلاس Benchmark =====
187
- class AnonymizationBenchmark:
188
  def __init__(self):
189
- self.extractor = EntityExtractor()
190
 
191
  def analyze_single_row(self, original_text, anonymized_text, row_number):
192
- """تحلیل یک ردیف از CSV با نمایش کامل entities"""
193
  print(f"\n{'='*80}")
194
- print(f"تحلیل ردیف {row_number}")
195
  print(f"{'='*80}")
196
 
197
  print(f"\n📝 متن اصلی ({len(original_text)} کاراکتر):")
198
- # نمایش 200 کاراکتر اول
199
- print(f"'{original_text[:200]}{'...' if len(original_text) > 200 else ''}'")
200
 
201
  print(f"\n🔒 متن ناشناس‌سازی شده ({len(anonymized_text)} کاراکتر):")
202
- print(f"'{anonymized_text[:200]}{'...' if len(anonymized_text) > 200 else ''}'")
203
 
204
  # استخراج entities از متن اصلی
205
- print(f"\n🔍 Entities استخراج شده از متن اصلی:")
206
  original_entities = self.extractor.extract_entities(original_text)
207
 
208
  total_original_entities = 0
@@ -218,7 +259,7 @@ class AnonymizationBenchmark:
218
  print(f"\n✅ مجموع entities یافت شده: {total_original_entities}")
219
 
220
  # استخراج کدهای ناشناس‌سازی
221
- print(f"\n🔍 کدهای ناشناس‌سازی استخراج شده:")
222
  anonymized_codes = self.extractor.extract_anonymized_codes(anonymized_text)
223
 
224
  total_anonymized_codes = 0
@@ -233,27 +274,27 @@ class AnonymizationBenchmark:
233
  else:
234
  print(f"\n✅ مجموع کدهای ناشناس‌سازی: {total_anonymized_codes}")
235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  # محاسبه متریک‌ها
237
- print(f"\n📊 محاسبه متریک‌ها:")
238
  category_metrics = {}
239
  total_tp, total_fp, total_fn = 0, 0, 0
240
 
241
- all_categories = set(original_entities.keys()) | set(anonymized_codes.keys())
242
-
243
- if not all_categories:
244
- print(" ⚠️ هیچ category ای برای محاسبه یافت نشد!")
245
- return {
246
- 'original_entities': {},
247
- 'anonymized_codes': {},
248
- 'category_metrics': {},
249
- 'overall_metrics': {
250
- 'total_original_entities': 0,
251
- 'total_anonymized_entities': 0,
252
- 'total_tp': 0, 'total_fp': 0, 'total_fn': 0,
253
- 'precision': 0, 'recall': 0, 'f1_score': 0, 'accuracy': 0
254
- }
255
- }
256
-
257
  for category in all_categories:
258
  original_count = len(original_entities.get(category, []))
259
  anonymized_count = len(anonymized_codes.get(category, []))
@@ -266,11 +307,6 @@ class AnonymizationBenchmark:
266
  recall = tp / (tp + fn) if (tp + fn) > 0 else 0
267
  f1_score = 2 * (precision * recall) / (precision + recall) if (precision + recall) > 0 else 0
268
 
269
- print(f"\n 📈 {category}:")
270
- print(f" اصلی: {original_count}, ناشناس‌سازی: {anonymized_count}")
271
- print(f" TP: {tp}, FP: {fp}, FN: {fn}")
272
- print(f" Precision: {precision:.4f}, Recall: {recall:.4f}, F1: {f1_score:.4f}")
273
-
274
  category_metrics[category] = {
275
  'original_count': original_count, 'anonymized_count': anonymized_count,
276
  'tp': tp, 'fp': fp, 'fn': fn,
@@ -287,7 +323,8 @@ class AnonymizationBenchmark:
287
  overall_f1 = 2 * (overall_precision * overall_recall) / (overall_precision + overall_recall) if (overall_precision + overall_recall) > 0 else 0
288
  accuracy = total_tp / total_original_entities if total_original_entities > 0 else 0
289
 
290
- print(f"\n🎯 متریک‌های کلی ردیف {row_number}:")
 
291
  print(f" Precision: {overall_precision:.4f}")
292
  print(f" Recall: {overall_recall:.4f}")
293
  print(f" F1-Score: {overall_f1:.4f}")
@@ -309,7 +346,7 @@ class AnonymizationBenchmark:
309
  def process_csv(self, csv_file_path):
310
  """پردازش فایل CSV"""
311
  try:
312
- # خواندن فایل با encoding های مختلف
313
  df = None
314
  for encoding in ['utf-8', 'utf-8-sig', 'cp1256', 'windows-1256']:
315
  try:
@@ -320,7 +357,7 @@ class AnonymizationBenchmark:
320
  continue
321
 
322
  if df is None:
323
- return "❌ خطا: نمی‌توان فایل را با هیچ encoding خواند"
324
 
325
  print(f"\n📋 اطلاعات فایل CSV:")
326
  print(f" تعداد ردیف‌ها: {len(df)}")
@@ -376,13 +413,13 @@ class AnonymizationBenchmark:
376
 
377
  # ===== رابط Gradio =====
378
  def process_uploaded_file(file):
379
- """پردازش فایل آپلود شده کاربر"""
380
  if file is None:
381
  return "❌ لطفاً ابتدا فایل CSV را آپلود کنید.", None
382
 
383
- print(f"\n🚀 شروع پردازش فایل: {file.name}")
384
 
385
- benchmark = AnonymizationBenchmark()
386
  result = benchmark.process_csv(file.name)
387
 
388
  if isinstance(result, str):
@@ -408,12 +445,12 @@ def process_uploaded_file(file):
408
  # گزارش نهایی
409
  report = f"""
410
  {'='*80}
411
- 🎯 گزارش نهایی Benchmark فایل شما
412
  {'='*80}
413
 
414
- 📈 آمار کلی:
415
  • تعداد ردیف‌های پردازش شده: {total_rows}
416
- • مجموع Entities اصلی در همه ردیف‌ها: {total_original}
417
  • مجموع کدهای ناشناس‌سازی: {total_anonymized}
418
  • True Positives (درست شناسایی شده): {total_tp}
419
  • False Positives (اشتباه شناسایی شده): {total_fp}
@@ -426,15 +463,18 @@ def process_uploaded_file(file):
426
  • Accuracy: {avg_accuracy:.4f}
427
 
428
  📊 توضیحات:
429
- • این آمار بر اساس entities واقعی موجود در فایل شما محاسبه شده
430
- جزئیات کامل هر ردیف در بالا نمایش داده شده
431
- • فایل نتایج کامل در جدول زیر قابل مشاهده است
 
 
 
432
  """
433
 
434
  # ذخیره نتایج
435
  try:
436
- results_df.to_csv("benchmark_results.csv", index=False, encoding='utf-8-sig')
437
- print("✅ نتایج در فایل benchmark_results.csv ذخیره شد")
438
  except Exception as e:
439
  print(f"⚠️ خطا در ذخیره فایل: {e}")
440
 
@@ -448,17 +488,17 @@ def process_uploaded_file(file):
448
 
449
  def download_results():
450
  """دانلود نتایج"""
451
- if os.path.exists("benchmark_results.csv"):
452
- return "benchmark_results.csv"
453
  return None
454
 
455
  # ===== رابط اصلی =====
456
  def main():
457
- with gr.Blocks(title="Precise Benchmark System", theme=gr.themes.Soft()) as demo:
458
 
459
  gr.HTML("""
460
  <h1 style='text-align: center; color: #2E86AB; margin-bottom: 30px;'>
461
- 🎯 سیستم Benchmark دقیق ناشناس‌سازی
462
  </h1>
463
  """)
464
 
@@ -466,12 +506,13 @@ def main():
466
  with gr.Column():
467
  gr.HTML("""
468
  <div style='background: #e8f5e8; padding: 15px; border-radius: 10px; margin-bottom: 15px;'>
469
- <h3> ویژگی‌های جدید:</h3>
470
  <ul>
471
- <li><b>الگوهای دقیق:</b> entities را دقیق‌تر شناسایی می‌کند</li>
472
- <li><b>نمایش کامل:</b> تمام entities پیدا شده لیست می‌شود</li>
473
- <li><b>تحلیل مفصل:</b> هر ردیف جداگانه تحلیل می‌شود</li>
474
- <li><b>محاسبه دقیق:</b> متریکهای صحیح بر اساس entities واقعی</li>
 
475
  </ul>
476
  </div>
477
  """)
@@ -482,14 +523,14 @@ def main():
482
  file_count="single"
483
  )
484
 
485
- process_btn = gr.Button("🚀 تحلیل فایل با نمایش کامل Entities", variant="primary", size="lg")
486
 
487
  with gr.Row():
488
  with gr.Column():
489
- gr.HTML("<h3>📊 گزارش کامل + نمایش Entities</h3>")
490
 
491
  results_output = gr.Textbox(
492
- label="گزارش شامل لیست کامل entities پیدا شده",
493
  lines=30,
494
  max_lines=35,
495
  interactive=False
@@ -497,18 +538,18 @@ def main():
497
 
498
  with gr.Row():
499
  with gr.Column():
500
- gr.HTML("<h3>📋 جدول نتایج</h3>")
501
 
502
  results_table = gr.Dataframe(
503
- label="خلاصه متریک‌های هر ردیف",
504
  interactive=False,
505
  wrap=True
506
  )
507
 
508
  with gr.Row():
509
  with gr.Column():
510
- download_btn = gr.Button("💾 دانلود نتایج کامل", variant="secondary")
511
- download_file = gr.File(label="فایل نتایج", visible=False)
512
 
513
  # Event handlers
514
  process_btn.click(
 
1
  #!/usr/bin/env python3
2
  # -*- coding: utf-8 -*-
3
  """
4
+ سیستم benchmark ناشناس‌سازی - ورژن نهایی با الگوهای فوق‌العاده دقیق
5
  """
6
 
7
  import pandas as pd
 
33
  else:
34
  return obj
35
 
36
+ # ===== کلاس پردازش entities با الگوهای فوق‌العاده دقیق =====
37
+ class PreciseEntityExtractor:
38
  def __init__(self):
39
+ # الگوهای فوق‌العاده دقیق با boundaries مشخص
40
  self.patterns = {
41
  'COMPANY': [
42
+ # شرکت + نام مشخص (فقط اسم شرکت، نه جملات)
43
+ r'شرکت\s+پتروشیمی\s+[\u0600-\u06FF\u0750-\u077F]+(?=\s|$|،|\.)',
44
+ r'شرکت\s+تولیدی\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]+?(?=\s+(?:با|در|که|مربوط|صادر)|$|،|\.)',
45
+ r'شرکت\s+پردازش\s+[\u0600-\u06FF\u0750-\u077F\s\u200C\-]+?(?=\s+(?:با|در|که|مربوط)|$|،|\.)',
46
+ r'بانک\s+[\u0600-\u06FF\u0750-\u077F]+(?=\s+(?:با|در|که|مربوط|ارائه|صادر)|$|،|\.)',
47
+ r'بانک\s+مرکزی\s+جمهوری\s+اسلامی\s+[\u0600-\u06FF\u0750-\u077F]+',
48
  ],
49
 
50
  'LOCATION': [
51
+ # فقط نام مکان، نه عبارات کامل
52
+ r'بندر\s+[\u0600-\u06FF\u0750-\u077F]+(?=\s+(?:برگزار|واقع|در)|$|،|\.)',
53
+ r'شهر\s+[\u0600-\u06FF\u0750-\u077F]+(?=\s+(?:برگزار|واقع|در)|$|،|\.)',
54
+ r'استان\s+[\u0600-\u06FF\u0750-\u077F]+(?=\s+(?:برگزار|واقع|در)|$|،|\.)',
55
+ # نام شهرهای مشخص
56
  r'\b(?:تهران|اصفهان|ماهشهر|عسلویه|بندرعباس|اهواز|شیراز|مشهد|تبریز|کرج|قم|رشت|کرمان|یزد|زاهدان|بوشهر|خرمشهر|آبادان|اراک|قزوین|خوزستان)\b',
57
+ # اسامی کشورها
58
  r'جمهوری\s+اسلامی\s+ایران',
59
+ r'\b(?:ایران|عراق|کویت|عربستان|امارات|قطر|عمان|بحرین|ترکیه)\b',
60
  ],
61
 
62
  'PERSON': [
63
+ # فقط اسم کامل افراد
64
+ r'آقای\s+[\u0600-\u06FF\u0750-\u077F]+\s+[\u0600-\u06FF\u0750-\u077F]+(?=\s+(?:با|مدیر)|$|،|\.)',
65
+ r'خانم\s+[\u0600-\u06FF\u0750-\u077F]+\s+[\u0600-\u06FF\u0750-\u077F]+(?=\s+(?:بامسر)|$|،|\.)',
66
+ r'مدیرعامل\s+[\u0600-\u06FF\u0750-\u077F]+\s+[\u0600-\u06FF\u0750-\u077F]+',
67
+ r'مدیر\s+مالی\s+[\u0600-\u06FF\u0750-\u077F]+\s+[\u0600-\u06FF\u0750-\u077F]+',
68
  ],
69
 
70
  'DATE': [
71
+ # فقط تاریخ‌های مشخص، نه عبارات طولانی
72
+ r'[۰-۹\u06F0-\u06F9]{1,2}\s+(?:فروردین|اردیبهشت|خرداد|تیر|مرداد|شهریور|مهر|آبان|آذر|دی|بهمن|اسفند)\s+[۰-۹\u06F0-\u06F9]{4}',
73
  r'\d{4}\/\d{1,2}\/\d{1,2}',
74
+ # حذف الگوی طولانی "سال مالی منتهی به..."
 
75
  ],
76
 
77
  'PHONE': [
78
+ # شماره‌های تلفن دقیق
79
+ r'09\d{9}(?!\d)',
80
+ r'021-\d{8}',
81
+ r'0\d{2,3}-\d{7,8}',
82
  ],
83
 
84
  'EMAIL': [
 
86
  ],
87
 
88
  'AMOUNT': [
89
+ # فقط مبالغ خالص
90
+ r'\d{6,}\s*تومان',
91
  r'\d+\s*درصد',
92
  ],
93
 
94
  'ACCOUNT': [
95
+ r'\d{3}-\d{3}-\d{3}-\d',
96
+ r'\d{4}-\d{4}-\d{4}-\d{4}',
97
  ],
98
 
99
  'ID_NUMBER': [
100
+ # کدهای ملی و شناسه دقیق
101
  r'کد\s+ملی\s+\d{10}',
102
  r'شناسه\s+ملی\s+\d{11}',
103
+ r'(?<!\d)\d{10}(?!\d)',
104
+ r'(?<!\d)\d{11}(?!\d)',
105
  ],
106
 
107
  'DOCUMENT_NUMBER': [
 
112
  }
113
 
114
  def clean_entity(self, text):
115
+ """تمیز کردن دقیق entity"""
116
  # حذف کلمات اضافی در انتها
117
+ text = re.sub(r'\s*(در|که|با|به|از|را|و|یا|است|بوده|نموده|صادر|ارائه|معرفی|برگزار|واقع|مربوط|مطرح|شد|شده)\s*$', '', text, flags=re.IGNORECASE)
118
  # حذف فاصله‌های اضافی
119
  text = re.sub(r'\s+', ' ', text).strip()
120
  return text
121
 
122
+ def is_valid_entity(self, text, category):
123
+ """بررسی دقیق‌تر معتبر بودن entity"""
124
+ if len(text) < 3 or len(text) > 60:
125
  return False
126
 
127
+ # کلمات ممنوع که نباید entity باشند
128
+ forbidden_words = [
129
+ د', 'کرد', 'است', 'بود', 'در', 'که', 'با', 'از', 'به', 'را', 'و', 'یا',
130
+ 'شرکت', 'بانک', 'آقای', 'خانم', 'تومان', 'مبلغ', 'برگزار', 'مطرح', 'واقع'
131
+ ]
132
 
133
+ if text.lower().strip() in forbidden_words:
134
  return False
135
 
136
+ # بررسی‌های خاص برای هر category
137
+ if category == 'COMPANY':
138
+ # نباید شامل فعل یا کلمات اضافی باشد
139
+ if any(word in text.lower() for word in ['برگزار', 'مطرح', 'شد', 'است', 'نموده']):
140
+ return False
141
+ # باید حداقل یک اسم خاص داشته باشد
142
+ if text.strip() in ['شرکت', 'بانک', 'شرکت در', 'بانک در']:
143
+ return False
144
+
145
+ elif category == 'LOCATION':
146
+ # نباید شامل فعل باشد
147
+ if any(word in text.lower() for word in ['برگزار', 'شد', 'است', 'واقع']):
148
+ return False
149
+ # باید نام مکان واقعی باشد
150
+ if text.strip() in ['شهر', 'بندر', 'استان']:
151
+ return False
152
+
153
+ elif category == 'DATE':
154
+ # نباید عبارات طولانی باشد
155
+ if 'سال مالی' in text:
156
+ return False
157
 
158
  return True
159
 
160
  def extract_entities(self, text):
161
+ """استخراج entities با دقت بالا"""
162
  if not text or text.strip() == '':
163
  return {}
164
 
 
174
 
175
  for match in matches:
176
  entity = self.clean_entity(match.group(0))
177
+ if self.is_valid_entity(entity, category):
178
  found_entities.append(entity)
179
 
180
  except re.error as e:
 
183
 
184
  # حذف تکراری‌ها و مرتب‌سازی
185
  if found_entities:
186
+ # حذف entities که زیرمجموعه entities دیگر هستند
187
+ unique_entities = []
188
+ for entity in found_entities:
189
+ is_subset = False
190
+ for other in found_entities:
191
+ if entity != other and entity in other:
192
+ is_subset = True
193
+ break
194
+ if not is_subset:
195
+ unique_entities.append(entity)
196
+
197
+ entities[category] = sorted(list(set(unique_entities)))
198
 
199
  return entities
200
 
 
226
  return codes
227
 
228
  # ===== کلاس Benchmark =====
229
+ class PreciseAnonymizationBenchmark:
230
  def __init__(self):
231
+ self.extractor = PreciseEntityExtractor()
232
 
233
  def analyze_single_row(self, original_text, anonymized_text, row_number):
234
+ """تحلیل دقیق یک ردیف"""
235
  print(f"\n{'='*80}")
236
+ print(f"تحلیل دقیق ردیف {row_number}")
237
  print(f"{'='*80}")
238
 
239
  print(f"\n📝 متن اصلی ({len(original_text)} کاراکتر):")
240
+ print(f"'{original_text[:150]}{'...' if len(original_text) > 150 else ''}'")
 
241
 
242
  print(f"\n🔒 متن ناشناس‌سازی شده ({len(anonymized_text)} کاراکتر):")
243
+ print(f"'{anonymized_text[:150]}{'...' if len(anonymized_text) > 150 else ''}'")
244
 
245
  # استخراج entities از متن اصلی
246
+ print(f"\n🔍 Entities دقیق استخراج شده از متن اصلی:")
247
  original_entities = self.extractor.extract_entities(original_text)
248
 
249
  total_original_entities = 0
 
259
  print(f"\n✅ مجموع entities یافت شده: {total_original_entities}")
260
 
261
  # استخراج کدهای ناشناس‌سازی
262
+ print(f"\n🔍 کدهای ناشناس‌سازی:")
263
  anonymized_codes = self.extractor.extract_anonymized_codes(anonymized_text)
264
 
265
  total_anonymized_codes = 0
 
274
  else:
275
  print(f"\n✅ مجموع کدهای ناشناس‌سازی: {total_anonymized_codes}")
276
 
277
+ # تطبیق دقیق entities و codes
278
+ print(f"\n🔄 تطبیق Entities با کدهای ناشناس‌سازی:")
279
+ all_categories = set(original_entities.keys()) | set(anonymized_codes.keys())
280
+
281
+ for category in sorted(all_categories):
282
+ orig_count = len(original_entities.get(category, []))
283
+ anon_count = len(anonymized_codes.get(category, []))
284
+
285
+ print(f"\n 📈 {category}:")
286
+ print(f" Entities اصلی: {orig_count}")
287
+ print(f" کدهای ناشناس‌سازی: {anon_count}")
288
+
289
+ if orig_count > 0:
290
+ print(f" لیست اصلی: {original_entities[category]}")
291
+ if anon_count > 0:
292
+ print(f" لیست کدها: {anonymized_codes[category]}")
293
+
294
  # محاسبه متریک‌ها
 
295
  category_metrics = {}
296
  total_tp, total_fp, total_fn = 0, 0, 0
297
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  for category in all_categories:
299
  original_count = len(original_entities.get(category, []))
300
  anonymized_count = len(anonymized_codes.get(category, []))
 
307
  recall = tp / (tp + fn) if (tp + fn) > 0 else 0
308
  f1_score = 2 * (precision * recall) / (precision + recall) if (precision + recall) > 0 else 0
309
 
 
 
 
 
 
310
  category_metrics[category] = {
311
  'original_count': original_count, 'anonymized_count': anonymized_count,
312
  'tp': tp, 'fp': fp, 'fn': fn,
 
323
  overall_f1 = 2 * (overall_precision * overall_recall) / (overall_precision + overall_recall) if (overall_precision + overall_recall) > 0 else 0
324
  accuracy = total_tp / total_original_entities if total_original_entities > 0 else 0
325
 
326
+ print(f"\n🎯 متریک‌های نهایی ردیف {row_number}:")
327
+ print(f" TP: {total_tp}, FP: {total_fp}, FN: {total_fn}")
328
  print(f" Precision: {overall_precision:.4f}")
329
  print(f" Recall: {overall_recall:.4f}")
330
  print(f" F1-Score: {overall_f1:.4f}")
 
346
  def process_csv(self, csv_file_path):
347
  """پردازش فایل CSV"""
348
  try:
349
+ # خواندن فایل
350
  df = None
351
  for encoding in ['utf-8', 'utf-8-sig', 'cp1256', 'windows-1256']:
352
  try:
 
357
  continue
358
 
359
  if df is None:
360
+ return "❌ خطا: نمی‌توان فایل را خواند"
361
 
362
  print(f"\n📋 اطلاعات فایل CSV:")
363
  print(f" تعداد ردیف‌ها: {len(df)}")
 
413
 
414
  # ===== رابط Gradio =====
415
  def process_uploaded_file(file):
416
+ """پردازش فایل آپلود شده"""
417
  if file is None:
418
  return "❌ لطفاً ابتدا فایل CSV را آپلود کنید.", None
419
 
420
+ print(f"\n🚀 شروع تحلیل دقیق فایل: {file.name}")
421
 
422
+ benchmark = PreciseAnonymizationBenchmark()
423
  result = benchmark.process_csv(file.name)
424
 
425
  if isinstance(result, str):
 
445
  # گزارش نهایی
446
  report = f"""
447
  {'='*80}
448
+ 🎯 گزارش نهایی Benchmark دقیق فایل شما
449
  {'='*80}
450
 
451
+ 📈 آمار کلی (با الگوهای دقیق):
452
  • تعداد ردیف‌های پردازش شده: {total_rows}
453
+ • مجموع Entities دقیق در همه ردیف‌ها: {total_original}
454
  • مجموع کدهای ناشناس‌سازی: {total_anonymized}
455
  • True Positives (درست شناسایی شده): {total_tp}
456
  • False Positives (اشتباه شناسایی شده): {total_fp}
 
463
  • Accuracy: {avg_accuracy:.4f}
464
 
465
  📊 توضیحات:
466
+ • الگوهای regex دقیق‌تر شده‌اند
467
+ entities اضافی و غلط حذف شده‌اند
468
+ • فقط entities واقعی و معنادار شناسایی می‌شوند
469
+ • جزئیات کامل در بالا نمایش داده شده
470
+
471
+ ✅ حالا تعداد entities منطقی‌تر و دقیق‌تر است!
472
  """
473
 
474
  # ذخیره نتایج
475
  try:
476
+ results_df.to_csv("precise_benchmark_results.csv", index=False, encoding='utf-8-sig')
477
+ print("✅ نتایج دقیق در فایل precise_benchmark_results.csv ذخیره شد")
478
  except Exception as e:
479
  print(f"⚠️ خطا در ذخیره فایل: {e}")
480
 
 
488
 
489
  def download_results():
490
  """دانلود نتایج"""
491
+ if os.path.exists("precise_benchmark_results.csv"):
492
+ return "precise_benchmark_results.csv"
493
  return None
494
 
495
  # ===== رابط اصلی =====
496
  def main():
497
+ with gr.Blocks(title="Ultra Precise Benchmark", theme=gr.themes.Soft()) as demo:
498
 
499
  gr.HTML("""
500
  <h1 style='text-align: center; color: #2E86AB; margin-bottom: 30px;'>
501
+ 🎯 سیستم Benchmark فوق‌العاده دقیق ناشناس‌سازی
502
  </h1>
503
  """)
504
 
 
506
  with gr.Column():
507
  gr.HTML("""
508
  <div style='background: #e8f5e8; padding: 15px; border-radius: 10px; margin-bottom: 15px;'>
509
+ <h3>🚀 ویژگی‌های ورژن دقیق:</h3>
510
  <ul>
511
+ <li><b>الگوهای فوق‌العاده دقیق:</b> حذف entities غلط و اضافی</li>
512
+ <li><b>تمیزکاری پیشرفته:</b> حذف کلمات اضافی مانند "برگزار شد"</li>
513
+ <li><b>اعتبارسنجی هوشمند:</b> بررسی معناداری هر entity</li>
514
+ <li><b>حذف تکراری هوشمند:</b> entities که زیرمجموعه دیگران هستند حذف می‌شوند</li>
515
+ <li><b>نمایش مقایسه‌ای:</b> entities vs کدهای ناشناس‌سازی</li>
516
  </ul>
517
  </div>
518
  """)
 
523
  file_count="single"
524
  )
525
 
526
+ process_btn = gr.Button("🎯 تحلیل فوق‌العاده دقیق", variant="primary", size="lg")
527
 
528
  with gr.Row():
529
  with gr.Column():
530
+ gr.HTML("<h3>📊 گزارش دقیق + لیست Entities صحیح</h3>")
531
 
532
  results_output = gr.Textbox(
533
+ label="گزارش کامل با entities دقیق",
534
  lines=30,
535
  max_lines=35,
536
  interactive=False
 
538
 
539
  with gr.Row():
540
  with gr.Column():
541
+ gr.HTML("<h3>📋 جدول نتایج دقیق</h3>")
542
 
543
  results_table = gr.Dataframe(
544
+ label="متریک‌های دقیق هر ردیف",
545
  interactive=False,
546
  wrap=True
547
  )
548
 
549
  with gr.Row():
550
  with gr.Column():
551
+ download_btn = gr.Button("💾 دانلود نتایج دقیق", variant="secondary")
552
+ download_file = gr.File(label="فایل نتایج دقیق", visible=False)
553
 
554
  # Event handlers
555
  process_btn.click(