leilaghomashchi commited on
Commit
6e8e525
·
verified ·
1 Parent(s): 31cfccf

Delete app2.py

Browse files
Files changed (1) hide show
  1. app2.py +0 -1707
app2.py DELETED
@@ -1,1707 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- 🚀 Enhanced Bilingual Data Anonymization Benchmark System
5
- ====================================================================
6
- نسخه پیشرفته با مقایسه واقعی متون اصلی و ناشناس‌سازی شده
7
- """
8
-
9
- import gradio as gr
10
- import pandas as pd
11
- import numpy as np
12
- import json
13
- import time
14
- import os
15
- import re
16
- import logging
17
- import requests
18
- from datetime import datetime
19
- from functools import lru_cache
20
- from packaging import version
21
- from typing import Dict, List, Tuple, Any, Optional
22
- import warnings
23
- import gc
24
- import threading
25
- from collections import defaultdict, Counter
26
- import hashlib
27
- from concurrent.futures import ThreadPoolExecutor, as_completed
28
- import multiprocessing
29
- from dataclasses import dataclass
30
- from difflib import SequenceMatcher
31
-
32
- # Enhanced metrics imports
33
- try:
34
- import psutil
35
- PSUTIL_AVAILABLE = True
36
- except ImportError:
37
- PSUTIL_AVAILABLE = False
38
-
39
- try:
40
- from sklearn.metrics import precision_score, recall_score, f1_score, accuracy_score, classification_report
41
- from sklearn.feature_extraction.text import TfidfVectorizer
42
- from sklearn.cluster import KMeans
43
- SKLEARN_AVAILABLE = True
44
- except ImportError:
45
- SKLEARN_AVAILABLE = False
46
-
47
- try:
48
- import spacy
49
- SPACY_AVAILABLE = True
50
- except ImportError:
51
- SPACY_AVAILABLE = False
52
-
53
- warnings.filterwarnings('ignore')
54
-
55
- # تنظیم logging
56
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
57
- logger = logging.getLogger(__name__)
58
-
59
- # =============================================================================
60
- # Data Classes for Better Structure
61
- # =============================================================================
62
-
63
- @dataclass
64
- class ComparisonResult:
65
- """نتیجه مقایسه متن اصلی با ناشناس‌سازی شده"""
66
- index: int
67
- success: bool
68
- processing_time_ms: float
69
- original_text: str
70
- anonymized_text: str
71
- original_length: int
72
- anonymized_length: int
73
- entities_should_anonymize: int
74
- entities_correctly_anonymized: int
75
- entities_missed: int
76
- missed_entities_list: List[Dict]
77
- anonymization_accuracy: float
78
- precision: float
79
- recall: float
80
- f1_score: float
81
- detected_language: str
82
- confidence_score: float
83
- memory_used_mb: float
84
- entity_categories: Dict[str, int]
85
- error: Optional[str] = None
86
-
87
- @dataclass
88
- class BenchmarkConfig:
89
- """تنظیمات بنچمارک"""
90
- sample_size: int = 200
91
- max_workers: int = 4
92
- enable_parallel_processing: bool = True
93
- enable_memory_profiling: bool = True
94
- enable_language_detection: bool = True
95
- enable_confidence_scoring: bool = True
96
- stress_test_iterations: int = 50
97
- enable_clustering_analysis: bool = False
98
-
99
- # =============================================================================
100
- # Enhanced Pattern Library
101
- # =============================================================================
102
-
103
- class EnhancedPatternLibrary:
104
- """کتابخانه الگوهای پیشرفته"""
105
-
106
- def __init__(self):
107
- self.patterns = self._load_enhanced_patterns()
108
- self.compiled_patterns = self._compile_patterns()
109
-
110
- def _load_enhanced_patterns(self):
111
- """بارگذاری الگوهای پیشرفته و جامع‌تر"""
112
- return {
113
- 'PERSIAN_PERSON': [
114
- r'آقای\s+([آ-ی\u200C]{2,}(?:\s+[آ-ی\u200C]{2,})*)',
115
- r'خانم\s+([آ-ی\u200C]{2,}(?:\s+[آ-ی\u200C]{2,})*)',
116
- r'مهندس\s+([آ-ی\u200C]{2,}(?:\s+[آ-ی\u200C]{2,})*)',
117
- r'دکتر\s+([آ-ی\u200C]{2,}(?:\s+[آ-ی\u200C]{2,})*)',
118
- r'استاد\s+([آ-ی\u200C]{2,}(?:\s+[آ-ی\u200C]{2,})*)',
119
- r'([آ-ی\u200C]{3,}\s+[آ-ی\u200C]{3,})(?=\s+مدیرعامل|\s+رئیس|\s+مدیر|[،.]|\s*$)',
120
- r'\b([آ-ی\u200C]{3,}(?:\s+[آ-ی\u200C]{3,}){1,2})\b',
121
- # نام‌های متداول فارسی
122
- r'\b(احمد|علی|حسن|حسین|محمد|رضا|مهدی|امیر|سعید|مجید|فرهاد|بهرام|کامران|داود|یوسف|ابراهیم)\s+[آ-ی\u200C]{3,}\b',
123
- r'\b[آ-ی\u200C]{3,}\s+(احمدی|علوی|حسینی|محمدی|رضایی|کریمی|موسوی|صادقی|مرادی|فرهادی)\b',
124
- ],
125
- 'ENGLISH_PERSON': [
126
- r'(Mr\.|Mrs\.|Ms\.|Dr\.|Prof\.)\s+([A-Z][a-z]{2,}(?:\s+[A-Z][a-z]{2,})*)',
127
- r'\b([A-Z][a-z]{2,}\s+[A-Z][a-z]{2,})(?=\s+(?:CEO|President|Manager|Director|said|stated|announced))',
128
- r'\b([A-Z][a-z]{2,}(?:\s+[A-Z]\.)*\s+[A-Z][a-z]{2,})\b',
129
- # نام‌های متداول انگلیسی
130
- r'\b(John|James|Michael|William|David|Richard|Joseph|Thomas|Christopher|Daniel|Paul|Mark|Donald|Steven|Andrew|Kenneth|Paul|Joshua|Kevin|Brian|George|Timothy|Ronald|Jason|Edward|Jeffrey|Ryan|Jacob|Gary|Nicholas|Eric|Jonathan|Stephen|Larry|Justin|Scott|Brandon|Benjamin|Samuel|Gregory|Frank|Raymond|Alexander|Patrick|Jack|Dennis|Jerry|Tyler|Aaron|Jose|Henry|Adam|Douglas|Nathan|Zachary|Kyle)\s+[A-Z][a-z]{2,}\b',
131
- r'\b[A-Z][a-z]{2,}\s+(Smith|Johnson|Williams|Brown|Jones|Garcia|Miller|Davis|Rodriguez|Martinez|Hernandez|Lopez|Gonzalez|Wilson|Anderson|Thomas|Taylor|Moore|Jackson|Martin|Lee|Perez|Thompson|White|Harris|Sanchez|Clark|Ramirez|Lewis|Robinson|Walker|Young|Allen|King|Wright|Scott|Torres|Nguyen|Hill|Flores|Green|Adams|Nelson|Baker|Hall|Rivera|Campbell|Mitchell|Carter|Roberts)\b',
132
- ],
133
- 'ENHANCED_PHONE': [
134
- r'(?:تلفن|موبایل|تماس)[\s:]*(?:\+98|0098)?(?:0)?([۰-۹0-9]{10,11})',
135
- r'(?:\+98|0098)[\s\-]?([۰-۹0-9]{2,3})[\s\-]?([۰-۹0-9]{7,8})',
136
- r'(?:^|[^\d])0([۰-۹0-9]{2,3})[\s\-]?([۰-۹0-9]{7,8})',
137
- r'\b([۰-۹0-9]{4})[\s\-]([۰-۹0-9]{3})[\s\-]([۰-۹0-9]{4})\b',
138
- r'\b(\+\d{1,3}[\s\-]?\d{3}[\s\-]?\d{3}[\s\-]?\d{4})\b',
139
- r'\b(\d{3}[-.]?\d{3}[-.]?\d{4})\b',
140
- r'\b([۰-۹]{4}[\s\-][۰-۹]{7})\b',
141
- r'\b(09[۰-۹0-9]{9})\b', # شماره موبایل ایرانی
142
- # الگوهای تلفن بین‌المللی
143
- r'\b(\+1[\s\-]?\d{3}[\s\-]?\d{3}[\s\-]?\d{4})\b', # US
144
- r'\b(\+44[\s\-]?\d{4}[\s\-]?\d{6})\b', # UK
145
- ],
146
- 'ENHANCED_EMAIL': [
147
- r'\b([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})\b',
148
- r'(?:ایمیل|email)[\s:]*([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})',
149
- r'\b([a-zA-Z0-9._%+-]+@(?:gmail|yahoo|hotmail|outlook|aol)\.com)\b',
150
- ],
151
- 'ENHANCED_NATIONAL_ID': [
152
- r'(?:کد\s*ملی|شناسه\s*ملی)[\s:]*([۰-۹0-9]{10})',
153
- r'(?:National\s*ID)[\s:]*([0-9]{10})',
154
- r'(?:شماره\s*شناسنامه)[\s:]*([۰-۹0-9]{1,10})',
155
- r'\b([۰-۹0-9]{10})\b',
156
- r'(?:SSN|Social\s*Security)[\s:]*([0-9]{3}-[0-9]{2}-[0-9]{4})',
157
- ],
158
- 'ENHANCED_BANK_ACCOUNT': [
159
- r'(?:شماره\s*حساب|حساب\s*بانکی)[\s:]*([۰-۹0-9\-]{10,20})',
160
- r'(?:شبا|IBAN)[\s:]*IR([۰-۹0-9]{24})',
161
- r'(?:کارت\s*بانکی)[\s:]*([۰-۹0-9]{4}[\s\-]?[۰-۹0-9]{4}[\s\-]?[۰-۹0-9]{4}[\s\-]?[۰-۹0-9]{4})',
162
- r'\b([0-9]{4}[\s\-]?[0-9]{4}[\s\-]?[0-9]{4}[\s\-]?[0-9]{4})\b',
163
- r'\b([0-9]{10,20})\b(?=.*(?:account|حساب))',
164
- # الگوهای کارت اعتباری
165
- r'\b(4[0-9]{12}(?:[0-9]{3})?)\b', # Visa
166
- r'\b(5[1-5][0-9]{14})\b', # MasterCard
167
- r'\b(3[47][0-9]{13})\b', # American Express
168
- ],
169
- 'ENHANCED_AMOUNT': [
170
- r'(?:مبلغ)?\s*([۰-۹0-9,]+)\s*(?:میلیون|میلیارد|هزار)?\s*(?:تومان|ریال)',
171
- r'\$([0-9,]+(?:\.[0-9]{2})?)\s*(?:million|billion|thousand|M|B|K)?',
172
- r'€([0-9,]+(?:\.[0-9]{2})?)',
173
- r'£([0-9,]+(?:\.[0-9]{2})?)',
174
- r'\b([0-9,]+(?:\.[0-9]{2})?)\s*(?:dollar|euro|pound|USD|EUR|GBP)s?\b',
175
- r'\b([۰-۹0-9,]+)\s*(?:درهم|دینار|ین|یوان)\b',
176
- ],
177
- 'ENHANCED_DATE': [
178
- r'([۰-۹0-9]{4})[/\-]([۰-۹0-9]{1,2})[/\-]([۰-۹0-9]{1,2})',
179
- r'([۰-۹0-9]{1,2})[/\-]([۰-۹0-9]{1,2})[/\-]([۰-۹0-9]{4})',
180
- r'(\d{1,2})\s+(January|February|March|April|May|June|July|August|September|October|November|December)\s+(\d{4})',
181
- r'(\d{1,2})\s+(فروردین|اردیبهشت|خرداد|تیر|مرداد|شهریور|مهر|آبان|آذر|دی|بهمن|اسفند)\s+(\d{4})',
182
- r'\b(\d{1,2}/\d{1,2}/\d{2,4})\b',
183
- r'\b(\d{4}-\d{2}-\d{2})\b', # ISO format
184
- r'\b([۰-۹]{4}/[۰-۹]{1,2}/[۰-۹]{1,2})\b',
185
- ],
186
- 'ENHANCED_COMPANY': [
187
- r'(?:شرکت)\s+([آ-ی\u200C\s]{3,}?)(?=\s+در|\s+که|\s+با|[،.]|\s*$)',
188
- r'(?:بانک)\s+([آ-ی\u200C\s]{3,})',
189
- r'\b([A-Z][a-zA-Z\s&]{2,}(?:Inc|Corp|Corporation|Company|Ltd|Limited|LLC|LLP))\b',
190
- r'\b([A-Z][a-zA-Z\s&]{2,}Bank)\b',
191
- r'\b(Apple|Google|Microsoft|Amazon|Facebook|Tesla|Netflix|IBM|Oracle|Samsung|Sony|Toyota|BMW|Mercedes|Volkswagen|Ford|General Motors)\b',
192
- r'\b([آ-ی\u200C]{3,}(?:\s+[آ-ی\u200C]{3,})*)\s+(?:شرکت|گروه|هلدینگ|صنایع)\b',
193
- ],
194
- 'ENHANCED_LOCATION': [
195
- r'(?:شهر|استان)\s+([آ-ی\u200C\s]{2,})',
196
- r'\b(تهران|اصفهان|شیراز|مشهد|تبریز|اهواز|کرج|قم|کرمان|یزد|ساری|گرگان|رشت|ارومیه|زاهدان|کرمانشاه|همدان|اراک|قزوین|زنجان|سنندج|ایلام|یاسوج|بجنورد|گر��ان|بندرعباس|بوشهر)\b',
197
- r'(خیابان|کوچه|بلوار|میدان)\s+([آ-ی\u200C\s]{2,})',
198
- r'پلاک\s*([۰-۹0-9]+)',
199
- r'\b([A-Z][a-zA-Z\s]{2,}(?:Street|Avenue|Road|Boulevard|Drive|Lane|Way))\b',
200
- r'\b([A-Z][a-zA-Z\s]{2,},\s*[A-Z]{2}\s+\d{5})\b',
201
- r'\b(New York|Los Angeles|Chicago|Houston|Phoenix|Philadelphia|San Antonio|San Diego|Dallas|San Jose|Austin|Jacksonville|Fort Worth|Columbus|Charlotte|San Francisco|Indianapolis|Seattle|Denver|Washington|Boston|El Paso|Detroit|Nashville|Portland|Memphis|Oklahoma City|Las Vegas|Louisville|Baltimore|Milwaukee|Albuquerque|Tucson|Fresno|Sacramento|Long Beach|Kansas City|Mesa|Virginia Beach|Atlanta|Colorado Springs|Omaha|Raleigh|Miami|Oakland|Minneapolis|Tulsa|Cleveland|Wichita|Arlington)\b',
202
- # کشورها
203
- r'\b(Iran|Iraq|Turkey|Afghanistan|Pakistan|India|China|Russia|Germany|France|Italy|Spain|United Kingdom|Canada|Australia|Japan|South Korea|Brazil|Mexico|Argentina)\b',
204
- r'\b(ایران|عراق|ترکیه|افغانستان|پاکستان|هندوستان|چین|روسیه|آلمان|فرانسه|ایتالیا|اسپانیا|انگلستان|کانادا|استرالیا|ژاپن|کره جنوبی|برزیل|مکزیک|آرژانتین)\b',
205
- ],
206
- 'ENHANCED_PERCENTAGE': [
207
- r'([۰-۹0-9]+(?:\.[۰-۹0-9]+)?)\s*درصد',
208
- r'([۰-۹0-9]+(?:\.[۰-۹0-9]+)?)\s*%',
209
- r'(?:رشد|افزایش|کاهش|تغییر)\s+([۰-۹0-9]+(?:\.[۰-۹0-9]+)?)\s*درصدی',
210
- r'\b([0-9]+(?:\.[0-9]+)?)\s*percent\b',
211
- ],
212
- 'ENHANCED_ADDRESS': [
213
- r'([آ-ی\u200C\s]{2,})\s*،\s*(خیابان|کوچه|بلوار|میدان)\s+([آ-ی\u200C\s]{2,})\s*،\s*پلاک\s*([۰-۹0-9]+)',
214
- r'([0-9]+)\s+([A-Z][a-zA-Z\s]{2,}(?:Street|Avenue|Road|Boulevard|Drive|Lane))',
215
- r'\b([0-9]+\s+[A-Z][a-zA-Z\s]{2,},\s*[A-Z][a-zA-Z\s]{2,},\s*[A-Z]{2}\s+[0-9]{5})\b',
216
- ],
217
- 'ENHANCED_IP_ADDRESS': [
218
- r'\b((?:[0-9]{1,3}\.){3}[0-9]{1,3})\b',
219
- r'\b([0-9a-fA-F:]+::[0-9a-fA-F:]+)\b', # IPv6
220
- ],
221
- 'ENHANCED_URL': [
222
- r'\b(https?://[^\s]+)\b',
223
- r'\b(www\.[^\s]+\.[a-zA-Z]{2,})\b',
224
- ]
225
- }
226
-
227
- def _compile_patterns(self):
228
- """کامپایل الگوهای regex برای بهبود عملکرد"""
229
- compiled = {}
230
- for category, patterns in self.patterns.items():
231
- compiled[category] = [re.compile(pattern, re.IGNORECASE | re.UNICODE) for pattern in patterns]
232
- return compiled
233
-
234
- def find_matches(self, text: str, category: Optional[str] = None) -> Dict[str, List[Tuple[str, int, int]]]:
235
- """پیدا کردن تطبیق‌ها با بازگشت موقعیت"""
236
- matches = defaultdict(list)
237
-
238
- patterns_to_check = {category: self.compiled_patterns[category]} if category else self.compiled_patterns
239
-
240
- for cat, compiled_patterns in patterns_to_check.items():
241
- for pattern in compiled_patterns:
242
- for match in pattern.finditer(text):
243
- matched_text = match.group(1) if match.groups() else match.group(0)
244
- if matched_text and matched_text.strip():
245
- matches[cat].append((matched_text.strip(), match.start(), match.end()))
246
-
247
- return dict(matches)
248
-
249
- # =============================================================================
250
- # Enhanced Comparison Anonymizer
251
- # =============================================================================
252
-
253
- class EnhancedComparisonAnonymizer:
254
- """سیستم مقایسه و ارزیابی ناشناس‌سازی"""
255
-
256
- def __init__(self, config: BenchmarkConfig):
257
- self.config = config
258
- self.pattern_lib = EnhancedPatternLibrary()
259
- self.processing_stats = {
260
- 'total_processed': 0,
261
- 'total_entities_should_anonymize': 0,
262
- 'total_entities_correctly_anonymized': 0,
263
- 'total_entities_missed': 0,
264
- 'language_distribution': Counter(),
265
- }
266
-
267
- # Load models if available
268
- self.models_loaded = False
269
- self.load_models()
270
-
271
- def load_models(self):
272
- """بارگذاری مدل‌های NER"""
273
- try:
274
- if SPACY_AVAILABLE:
275
- try:
276
- import spacy
277
- self.nlp_en = spacy.load("en_core_web_sm")
278
- logger.info("✅ English spaCy model loaded")
279
- except OSError:
280
- logger.warning("⚠️ English spaCy model not found")
281
- self.nlp_en = None
282
-
283
- try:
284
- self.nlp_fa = spacy.load("fa_core_news_sm")
285
- logger.info("✅ Persian spaCy model loaded")
286
- except OSError:
287
- logger.warning("⚠️ Persian spaCy model not found")
288
- self.nlp_fa = None
289
-
290
- self.models_loaded = (self.nlp_en is not None) or (self.nlp_fa is not None)
291
-
292
- except Exception as e:
293
- logger.error(f"❌ Error loading models: {e}")
294
- self.models_loaded = False
295
-
296
- def detect_language(self, text: str) -> str:
297
- """تشخیص زبان متن"""
298
- persian_chars = len(re.findall(r'[\u0600-\u06FF]', text))
299
- english_chars = len(re.findall(r'[a-zA-Z]', text))
300
- total_chars = persian_chars + english_chars
301
-
302
- if total_chars == 0:
303
- return 'unknown'
304
-
305
- persian_ratio = persian_chars / total_chars
306
- english_ratio = english_chars / total_chars
307
-
308
- if persian_ratio > 0.7:
309
- return 'fa'
310
- elif english_ratio > 0.7:
311
- return 'en'
312
- elif persian_ratio > 0.3 and english_ratio > 0.3:
313
- return 'mixed'
314
- elif persian_ratio > english_ratio:
315
- return 'fa'
316
- else:
317
- return 'en'
318
-
319
- def compare_texts(self, original_text: str, anonymized_text: str) -> ComparisonResult:
320
- """مقایسه متن اصلی با ناشناس‌سازی شده"""
321
- start_time = time.time()
322
- memory_before = self._get_memory_usage()
323
-
324
- try:
325
- # تشخیص زبان
326
- detected_lang = self.detect_language(original_text)
327
-
328
- # پیدا کردن موجودیت‌هایی که باید ناشناس‌سازی شوند
329
- original_entities = self._find_all_entities(original_text, detected_lang)
330
-
331
- # بررسی اینکه کدام موجودیت‌ها ناشناس‌سازی شده‌اند
332
- correctly_anonymized, missed_entities = self._check_anonymization_quality(
333
- original_text, anonymized_text, original_entities
334
- )
335
-
336
- # محاسبه متریک‌ها
337
- total_should_anonymize = len(original_entities)
338
- correctly_anonymized_count = len(correctly_anonymized)
339
- missed_count = len(missed_entities)
340
-
341
- # محاسبه دقت، بازخوانی و F1
342
- precision = correctly_anonymized_count / max(1, correctly_anonymized_count + self._count_false_positives(anonymized_text))
343
- recall = correctly_anonymized_count / max(1, total_should_anonymize)
344
- f1 = 2 * (precision * recall) / max(0.001, precision + recall)
345
- anonymization_accuracy = correctly_anonymized_count / max(1, total_should_anonymize)
346
-
347
- # محاسبه آمار
348
- processing_time = (time.time() - start_time) * 1000
349
- memory_after = self._get_memory_usage()
350
- memory_used = max(0, memory_after - memory_before)
351
-
352
- confidence = self._calculate_confidence_score(original_entities, correctly_anonymized_count, total_should_anonymize)
353
-
354
- # شمارش دسته‌ها
355
- entity_categories = defaultdict(int)
356
- for entity in original_entities:
357
- entity_categories[entity['category']] += 1
358
-
359
- # به‌روزرسانی آمار کلی
360
- self.processing_stats['total_processed'] += 1
361
- self.processing_stats['total_entities_should_anonymize'] += total_should_anonymize
362
- self.processing_stats['total_entities_correctly_anonymized'] += correctly_anonymized_count
363
- self.processing_stats['total_entities_missed'] += missed_count
364
- self.processing_stats['language_distribution'][detected_lang] += 1
365
-
366
- return ComparisonResult(
367
- index=self.processing_stats['total_processed'],
368
- success=True,
369
- processing_time_ms=processing_time,
370
- original_text=original_text[:500] + "..." if len(original_text) > 500 else original_text,
371
- anonymized_text=anonymized_text[:500] + "..." if len(anonymized_text) > 500 else anonymized_text,
372
- original_length=len(original_text),
373
- anonymized_length=len(anonymized_text),
374
- entities_should_anonymize=total_should_anonymize,
375
- entities_correctly_anonymized=correctly_anonymized_count,
376
- entities_missed=missed_count,
377
- missed_entities_list=missed_entities,
378
- anonymization_accuracy=anonymization_accuracy,
379
- precision=precision,
380
- recall=recall,
381
- f1_score=f1,
382
- detected_language=detected_lang,
383
- confidence_score=confidence,
384
- memory_used_mb=memory_used,
385
- entity_categories=dict(entity_categories)
386
- )
387
-
388
- except Exception as e:
389
- processing_time = (time.time() - start_time) * 1000
390
- return ComparisonResult(
391
- index=self.processing_stats['total_processed'],
392
- success=False,
393
- processing_time_ms=processing_time,
394
- original_text=original_text[:200] + "..." if len(original_text) > 200 else original_text,
395
- anonymized_text=anonymized_text[:200] + "..." if len(anonymized_text) > 200 else anonymized_text,
396
- original_length=len(original_text),
397
- anonymized_length=len(anonymized_text),
398
- entities_should_anonymize=0,
399
- entities_correctly_anonymized=0,
400
- entities_missed=0,
401
- missed_entities_list=[],
402
- anonymization_accuracy=0.0,
403
- precision=0.0,
404
- recall=0.0,
405
- f1_score=0.0,
406
- detected_language='unknown',
407
- confidence_score=0.0,
408
- memory_used_mb=0.0,
409
- entity_categories={},
410
- error=str(e)
411
- )
412
-
413
- def _find_all_entities(self, text: str, language: str) -> List[Dict]:
414
- """پیدا کردن تمام موجودیت‌هایی که باید ناشناس‌سازی شوند"""
415
- entities = []
416
-
417
- # استفاده از pattern matching
418
- pattern_matches = self.pattern_lib.find_matches(text)
419
-
420
- for category, matches in pattern_matches.items():
421
- for match_text, start, end in matches:
422
- entities.append({
423
- 'text': match_text,
424
- 'category': category,
425
- 'start': start,
426
- 'end': end,
427
- 'source': 'pattern'
428
- })
429
-
430
- # استفاده از NER اگر در دسترس باشد
431
- if self.models_loaded:
432
- ner_entities = self._extract_entities_with_ner(text, language)
433
- for entity in ner_entities:
434
- entities.append({
435
- 'text': entity['text'],
436
- 'category': self._map_ner_label(entity['label']),
437
- 'start': entity['start'],
438
- 'end': entity['end'],
439
- 'source': entity['source']
440
- })
441
-
442
- # حذف تداخل‌ها
443
- entities = self._remove_overlapping_entities(entities)
444
-
445
- return entities
446
-
447
- def _extract_entities_with_ner(self, text: str, language: str) -> List[Dict]:
448
- """استخراج entities با مدل‌های NER"""
449
- entities = []
450
-
451
- try:
452
- if language in ['en', 'mixed'] and hasattr(self, 'nlp_en') and self.nlp_en:
453
- doc = self.nlp_en(text)
454
- for ent in doc.ents:
455
- entities.append({
456
- 'text': ent.text,
457
- 'label': ent.label_,
458
- 'start': ent.start_char,
459
- 'end': ent.end_char,
460
- 'source': 'spacy_en'
461
- })
462
-
463
- if language in ['fa', 'mixed'] and hasattr(self, 'nlp_fa') and self.nlp_fa:
464
- doc = self.nlp_fa(text)
465
- for ent in doc.ents:
466
- entities.append({
467
- 'text': ent.text,
468
- 'label': ent.label_,
469
- 'start': ent.start_char,
470
- 'end': ent.end_char,
471
- 'source': 'spacy_fa'
472
- })
473
-
474
- except Exception as e:
475
- logger.error(f"Error in NER extraction: {e}")
476
-
477
- return entities
478
-
479
- def _map_ner_label(self, ner_label: str) -> str:
480
- """نقشه‌برداری برچسب‌های NER"""
481
- mapping = {
482
- 'PERSON': 'PERSIAN_PERSON',
483
- 'PER': 'PERSIAN_PERSON',
484
- 'ORG': 'ENHANCED_COMPANY',
485
- 'ORGANIZATION': 'ENHANCED_COMPANY',
486
- 'LOC': 'ENHANCED_LOCATION',
487
- 'LOCATION': 'ENHANCED_LOCATION',
488
- 'GPE': 'ENHANCED_LOCATION',
489
- 'MONEY': 'ENHANCED_AMOUNT',
490
- 'PERCENT': 'ENHANCED_PERCENTAGE',
491
- 'DATE': 'ENHANCED_DATE',
492
- 'TIME': 'ENHANCED_DATE'
493
- }
494
- return mapping.get(ner_label.upper(), 'OTHER')
495
-
496
- def _remove_overlapping_entities(self, entities: List[Dict]) -> List[Dict]:
497
- """حذف موجودیت‌های همپوشان"""
498
- entities.sort(key=lambda x: (x['start'], x['end'] - x['start']))
499
-
500
- filtered_entities = []
501
- used_positions = []
502
-
503
- for entity in entities:
504
- start, end = entity['start'], entity['end']
505
- overlaps = any(not (end <= pos_start or start >= pos_end) for pos_start, pos_end in used_positions)
506
-
507
- if not overlaps:
508
- filtered_entities.append(entity)
509
- used_positions.append((start, end))
510
-
511
- return filtered_entities
512
-
513
- def _check_anonymization_quality(self, original_text: str, anonymized_text: str, entities: List[Dict]) -> Tuple[List[Dict], List[Dict]]:
514
- """بررسی کیفیت ناشناس‌سازی"""
515
- correctly_anonymized = []
516
- missed_entities = []
517
-
518
- for entity in entities:
519
- entity_text = entity['text']
520
-
521
- # بررسی اینکه آیا موجودیت در متن ناشناس‌سازی شده وجود دارد یا نه
522
- if entity_text in anonymized_text:
523
- # موجودیت ناشناس‌سازی نشده
524
- missed_entities.append({
525
- 'text': entity_text,
526
- 'category': entity['category'],
527
- 'reason': 'موجود در متن ناشناس‌سازی شده'
528
- })
529
- else:
530
- # بررسی اینکه آیا جایگزین شده یا حذف شده
531
- original_words = set(original_text.split())
532
- anonymized_words = set(anonymized_text.split())
533
- entity_words = set(entity_text.split())
534
-
535
- if entity_words.issubset(original_words) and not entity_words.issubset(anonymized_words):
536
- correctly_anonymized.append(entity)
537
- else:
538
- # بررسی دقیق‌تر با استفاده از similarity
539
- if self._is_anonymized_with_similarity(original_text, anonymized_text, entity_text):
540
- correctly_anonymized.append(entity)
541
- else:
542
- missed_entities.append({
543
- 'text': entity_text,
544
- 'category': entity['category'],
545
- 'reason': 'تشخیص ناشناس‌سازی ناموفق'
546
- })
547
-
548
- return correctly_anonymized, missed_entities
549
-
550
- def _is_anonymized_with_similarity(self, original: str, anonymized: str, entity_text: str) -> bool:
551
- """بررسی ناشناس‌سازی با استفاده از شباهت متنی"""
552
- try:
553
- # حذف موجودیت از متن اصلی
554
- original_without_entity = original.replace(entity_text, "[REMOVED]")
555
-
556
- # محاسبه شباهت
557
- similarity = SequenceMatcher(None, original_without_entity, anonymized).ratio()
558
-
559
- # اگر شباهت بالا باشد، احتمالاً ناشناس‌سازی شده
560
- return similarity > 0.7
561
-
562
- except:
563
- return False
564
-
565
- def _count_false_positives(self, anonymized_text: str) -> int:
566
- """شمارش کلمات اشتباه ناشناس‌سازی شده"""
567
- # شمارش کلماتی که به نظر placeholder هستند اما نباید باشند
568
- false_positive_patterns = [
569
- r'\b[A-Z_]+_\d+_ANONYMIZED\b',
570
- r'\[\w+\]',
571
- r'\*+',
572
- ]
573
-
574
- false_positives = 0
575
- for pattern in false_positive_patterns:
576
- false_positives += len(re.findall(pattern, anonymized_text))
577
-
578
- return false_positives
579
-
580
- def _calculate_confidence_score(self, entities: List[Dict], correctly_anonymized: int, total_entities: int) -> float:
581
- """محاسبه امتیاز اعتماد"""
582
- if total_entities == 0:
583
- return 1.0
584
-
585
- accuracy = correctly_anonymized / total_entities
586
- diversity = min(1.0, len(set(e['category'] for e in entities)) / 10)
587
-
588
- confidence = (accuracy * 0.8 + diversity * 0.2)
589
- return round(confidence, 3)
590
-
591
- def _get_memory_usage(self) -> float:
592
- """دریافت مصرف حافظه فعلی"""
593
- if not PSUTIL_AVAILABLE or not self.config.enable_memory_profiling:
594
- return 0.0
595
- try:
596
- process = psutil.Process()
597
- return process.memory_info().rss / 1024 / 1024 # MB
598
- except:
599
- return 0.0
600
-
601
- # =============================================================================
602
- # Enhanced Benchmark Interface
603
- # =============================================================================
604
-
605
- class EnhancedBenchmarkInterface:
606
- """رابط کاربری پیشرفته بنچمارک"""
607
-
608
- def __init__(self):
609
- self.current_results = None
610
- self.current_language = 'fa'
611
- self.config = BenchmarkConfig()
612
-
613
- try:
614
- self.anonymizer = EnhancedComparisonAnonymizer(self.config)
615
- self.system_ready = True
616
- logger.info("✅ Enhanced comparison system initialized")
617
- except Exception as e:
618
- logger.error(f"❌ System initialization failed: {e}")
619
- self.system_ready = False
620
-
621
- def load_local_datasets(self) -> Tuple[List[Tuple[str, str]], List[Tuple[str, str]]]:
622
- """بارگذاری دیتاست‌های محلی با جفت متون"""
623
- persian_pairs = []
624
- english_pairs = []
625
-
626
- def find_text_columns(df):
627
- """پیدا کردن ستون‌های متن اصلی و ناشناس‌سازی شده"""
628
- # تمیز کردن نام ستون‌ها
629
- df.columns = df.columns.str.strip()
630
-
631
- original_col = None
632
- anonymized_col = None
633
-
634
- # ستون‌های احتمالی برای متن اصلی
635
- original_candidates = ['original_text', 'original', 'text', 'sentence', 'content', 'input']
636
- # ستون‌های احتمالی برای متن ناشناس‌سازی شده
637
- anonymized_candidates = ['anonymized_text', 'anonymized', 'output', 'result', 'processed']
638
-
639
- logger.info(f"Available columns: {list(df.columns)}")
640
-
641
- for col in original_candidates:
642
- if col in df.columns:
643
- original_col = col
644
- logger.info(f"Found original text column: {col}")
645
- break
646
-
647
- for col in anonymized_candidates:
648
- if col in df.columns:
649
- anonymized_col = col
650
- logger.info(f"Found anonymized text column: {col}")
651
- break
652
-
653
- # اگر ستون‌های دقیق پیدا نشد، از دو ستون اول استفاده کن
654
- if not original_col and not anonymized_col and len(df.columns) >= 2:
655
- original_col = df.columns[0]
656
- anonymized_col = df.columns[1]
657
- logger.info(f"Using first two columns: {original_col}, {anonymized_col}")
658
-
659
- return original_col, anonymized_col
660
-
661
- try:
662
- # بارگذاری دیتاست فارسی
663
- fa_files = ['dataset-fa.csv', 'datasetfa.csv', 'datasetfa.txt', 'dataset_fa.csv']
664
- for filename in fa_files:
665
- if os.path.exists(filename):
666
- try:
667
- logger.info(f"Attempting to load Persian dataset: {filename}")
668
-
669
- if filename.endswith('.csv'):
670
- # تلاش برای خواندن با encoding های مختلف
671
- df_fa = None
672
- for encoding in ['utf-8', 'utf-8-sig', 'cp1256', 'iso-8859-1']:
673
- try:
674
- df_fa = pd.read_csv(filename, encoding=encoding)
675
- logger.info(f"Successfully read {filename} with encoding: {encoding}")
676
- break
677
- except UnicodeDecodeError:
678
- continue
679
-
680
- if df_fa is None:
681
- logger.error(f"Could not read {filename} with any encoding")
682
- continue
683
-
684
- logger.info(f"File shape: {df_fa.shape}")
685
- logger.info(f"Columns before cleaning: {list(df_fa.columns)}")
686
-
687
- original_col, anonymized_col = find_text_columns(df_fa)
688
-
689
- if original_col and anonymized_col:
690
- valid_pairs = 0
691
- for _, row in df_fa.iterrows():
692
- try:
693
- orig_text = str(row[original_col]).strip()
694
- anon_text = str(row[anonymized_col]).strip()
695
-
696
- # بررسی اینکه متن‌ها معتبر هستند
697
- if (orig_text and anon_text and
698
- orig_text != 'nan' and anon_text != 'nan' and
699
- len(orig_text) > 5 and len(anon_text) > 5):
700
- persian_pairs.append((orig_text, anon_text))
701
- valid_pairs += 1
702
- except Exception as e:
703
- logger.warning(f"Error processing row: {e}")
704
- continue
705
-
706
- logger.info(f"✅ Loaded {valid_pairs} Persian pairs from {filename}")
707
- if valid_pairs > 0:
708
- break
709
- else:
710
- logger.warning(f"❌ Could not find appropriate columns in {filename}")
711
- logger.warning(f"Available columns: {list(df_fa.columns)}")
712
-
713
- except Exception as e:
714
- logger.error(f"Error loading Persian {filename}: {e}")
715
- continue
716
-
717
- # بارگذاری دیتاست انگلیسی (همان منطق)
718
- en_files = ['dataset-en.csv', 'dataseten.csv', 'dataseten.txt', 'dataset_en.csv']
719
- for filename in en_files:
720
- if os.path.exists(filename):
721
- try:
722
- logger.info(f"Attempting to load English dataset: {filename}")
723
-
724
- if filename.endswith('.csv'):
725
- df_en = None
726
- for encoding in ['utf-8', 'utf-8-sig', 'cp1256', 'iso-8859-1']:
727
- try:
728
- df_en = pd.read_csv(filename, encoding=encoding)
729
- logger.info(f"Successfully read {filename} with encoding: {encoding}")
730
- break
731
- except UnicodeDecodeError:
732
- continue
733
-
734
- if df_en is None:
735
- logger.error(f"Could not read {filename} with any encoding")
736
- continue
737
-
738
- logger.info(f"File shape: {df_en.shape}")
739
- logger.info(f"Columns before cleaning: {list(df_en.columns)}")
740
-
741
- original_col, anonymized_col = find_text_columns(df_en)
742
-
743
- if original_col and anonymized_col:
744
- valid_pairs = 0
745
- for _, row in df_en.iterrows():
746
- try:
747
- orig_text = str(row[original_col]).strip()
748
- anon_text = str(row[anonymized_col]).strip()
749
-
750
- if (orig_text and anon_text and
751
- orig_text != 'nan' and anon_text != 'nan' and
752
- len(orig_text) > 5 and len(anon_text) > 5):
753
- english_pairs.append((orig_text, anon_text))
754
- valid_pairs += 1
755
- except Exception as e:
756
- logger.warning(f"Error processing row: {e}")
757
- continue
758
-
759
- logger.info(f"✅ Loaded {valid_pairs} English pairs from {filename}")
760
- if valid_pairs > 0:
761
- break
762
- else:
763
- logger.warning(f"❌ Could not find appropriate columns in {filename}")
764
- logger.warning(f"Available columns: {list(df_en.columns)}")
765
-
766
- except Exception as e:
767
- logger.error(f"Error loading English {filename}: {e}")
768
- continue
769
-
770
- except Exception as e:
771
- logger.error(f"❌ Error loading local datasets: {e}")
772
-
773
- logger.info(f"Final counts - Persian: {len(persian_pairs)}, English: {len(english_pairs)}")
774
- return persian_pairs, english_pairs
775
-
776
- def run_enhanced_benchmark(self, sample_size: int, enable_parallel: bool = True,
777
- enable_clustering: bool = False, progress=gr.Progress()):
778
- """اجرای بنچمارک پیشرفته با مقایسه واقعی"""
779
-
780
- if not self.system_ready:
781
- return self._get_error_response("System not ready")
782
-
783
- try:
784
- progress(0.05, desc="Loading local datasets...")
785
-
786
- # بارگذاری دیتاست‌های محلی
787
- persian_pairs, english_pairs = self.load_local_datasets()
788
-
789
- if not persian_pairs and not english_pairs:
790
- return self._get_error_response("No text pairs loaded from local datasets. Check file format and columns.")
791
-
792
- # ترکیب جفت‌ها
793
- all_pairs = persian_pairs + english_pairs
794
-
795
- # محدود کردن تعداد
796
- if len(all_pairs) > sample_size:
797
- all_pairs = all_pairs[:sample_size]
798
-
799
- # تنظیم پیکربندی
800
- self.config.sample_size = len(all_pairs)
801
- self.config.enable_parallel_processing = enable_parallel
802
- self.config.enable_clustering_analysis = enable_clustering
803
-
804
- progress(0.1, desc=f"Comparing {len(all_pairs)} text pairs...")
805
-
806
- # پردازش جفت‌ها
807
- results = []
808
- start_time = time.time()
809
-
810
- if enable_parallel and len(all_pairs) > 10:
811
- results = self._process_parallel(all_pairs, progress)
812
- else:
813
- results = self._process_sequential(all_pairs, progress)
814
-
815
- total_time = time.time() - start_time
816
-
817
- # محاسبه آمار کلی
818
- progress(0.85, desc="Calculating comprehensive metrics...")
819
-
820
- successful_results = [r for r in results if r.success]
821
-
822
- if not successful_results:
823
- return self._get_error_response("No successful results")
824
-
825
- # محاسبه متریک‌های پیشرفته
826
- summary = self._calculate_comprehensive_metrics(successful_results, total_time)
827
-
828
- # اجرای تحلیل‌های اضافی
829
- if enable_clustering and SKLEARN_AVAILABLE:
830
- progress(0.92, desc="Running clustering analysis...")
831
- clustering_results = self._run_clustering_analysis(successful_results)
832
- summary['clustering_analysis'] = clustering_results
833
-
834
- # تست استرس
835
- progress(0.95, desc="Running stress test...")
836
- if all_pairs:
837
- stress_results = self._run_stress_test(all_pairs[0])
838
- summary['stress_test'] = stress_results
839
-
840
- # ذخیره نتایج
841
- self.current_results = {
842
- 'summary': summary,
843
- 'detailed_results': [self._result_to_dict(r) for r in results],
844
- 'timestamp': datetime.now().isoformat(),
845
- 'benchmark_version': 'enhanced_comparison_v3.1',
846
- 'config': {
847
- 'sample_size': sample_size,
848
- 'parallel_processing': enable_parallel,
849
- 'clustering_enabled': enable_clustering,
850
- }
851
- }
852
-
853
- progress(1.0, desc="Enhanced benchmark completed!")
854
-
855
- # ایجاد گزارش جامع متنی
856
- detailed_report = self._create_comprehensive_report()
857
-
858
- success_msg = f"✅ Enhanced benchmark completed! Compared {len(all_pairs)} text pairs with real accuracy metrics"
859
-
860
- return (
861
- success_msg,
862
- detailed_report, # فقط گزارش متنی
863
- gr.update(visible=True), # results visibility
864
- gr.update(visible=True), # download button
865
- )
866
-
867
- except Exception as e:
868
- logger.error(f"❌ Benchmark error: {e}")
869
- return self._get_error_response(f"Benchmark failed: {str(e)}")
870
-
871
- def _process_sequential(self, pairs: List[Tuple[str, str]], progress) -> List[ComparisonResult]:
872
- """پردازش ترتیبی جفت‌ها"""
873
- results = []
874
-
875
- for i, (original, anonymized) in enumerate(pairs):
876
- progress(0.1 + (0.7 * i / len(pairs)),
877
- desc=f"Comparing pair {i+1}/{len(pairs)}")
878
-
879
- result = self.anonymizer.compare_texts(original, anonymized)
880
- results.append(result)
881
-
882
- if i % 50 == 0: # garbage collection هر 50 جفت
883
- gc.collect()
884
-
885
- return results
886
-
887
- def _process_parallel(self, pairs: List[Tuple[str, str]], progress) -> List[ComparisonResult]:
888
- """پردازش موازی جفت‌ها"""
889
- results = []
890
- completed = 0
891
-
892
- max_workers = min(self.config.max_workers, multiprocessing.cpu_count())
893
-
894
- with ThreadPoolExecutor(max_workers=max_workers) as executor:
895
- # ارسال وظایف
896
- future_to_index = {
897
- executor.submit(self.anonymizer.compare_texts, original, anonymized): i
898
- for i, (original, anonymized) in enumerate(pairs)
899
- }
900
-
901
- # جمع‌آوری نتایج
902
- index_to_result = {}
903
-
904
- for future in as_completed(future_to_index):
905
- index = future_to_index[future]
906
- try:
907
- result = future.result()
908
- index_to_result[index] = result
909
- except Exception as e:
910
- logger.error(f"Error processing pair {index}: {e}")
911
- original, anonymized = pairs[index] if index < len(pairs) else ("", "")
912
- index_to_result[index] = ComparisonResult(
913
- index=index,
914
- success=False,
915
- processing_time_ms=0,
916
- original_text=original[:200],
917
- anonymized_text=anonymized[:200],
918
- original_length=len(original),
919
- anonymized_length=len(anonymized),
920
- entities_should_anonymize=0,
921
- entities_correctly_anonymized=0,
922
- entities_missed=0,
923
- missed_entities_list=[],
924
- anonymization_accuracy=0.0,
925
- precision=0.0,
926
- recall=0.0,
927
- f1_score=0.0,
928
- detected_language='unknown',
929
- confidence_score=0.0,
930
- memory_used_mb=0.0,
931
- entity_categories={},
932
- error=str(e)
933
- )
934
-
935
- completed += 1
936
- progress(0.1 + (0.7 * completed / len(pairs)),
937
- desc=f"Completed {completed}/{len(pairs)} pairs")
938
-
939
- # مرتب‌سازی نتایج بر اساس ایندکس
940
- results = [index_to_result[i] for i in sorted(index_to_result.keys())]
941
-
942
- return results
943
-
944
- def _calculate_comprehensive_metrics(self, results: List[ComparisonResult], total_time: float) -> Dict:
945
- """محاسبه متریک‌های جامع بر اساس مقایسه واقعی"""
946
-
947
- # آمار پایه
948
- total_pairs = len(results)
949
- successful_pairs = sum(1 for r in results if r.success)
950
- success_rate = successful_pairs / total_pairs if total_pairs > 0 else 0
951
-
952
- processing_times = [r.processing_time_ms for r in results if r.success]
953
- avg_processing_time = np.mean(processing_times) if processing_times else 0
954
-
955
- # محاسبه آمار واقعی ناشناس‌سازی
956
- total_entities_should_anonymize = sum(r.entities_should_anonymize for r in results if r.success)
957
- total_correctly_anonymized = sum(r.entities_correctly_anonymized for r in results if r.success)
958
- total_missed = sum(r.entities_missed for r in results if r.success)
959
-
960
- # محاسبه متریک‌های کلی
961
- overall_precision = np.mean([r.precision for r in results if r.success and r.precision > 0])
962
- overall_recall = np.mean([r.recall for r in results if r.success and r.recall > 0])
963
- overall_f1 = np.mean([r.f1_score for r in results if r.success and r.f1_score > 0])
964
- overall_anonymization_rate = np.mean([r.anonymization_accuracy for r in results if r.success])
965
-
966
- # تعدیل نرخ موفقیت بر اساس entities جا افتاده
967
- if total_entities_should_anonymize > 0:
968
- entity_miss_penalty = (total_missed / total_entities_should_anonymize) * 100
969
- adjusted_success_rate = max(0, success_rate * 100 - entity_miss_penalty) / 100
970
- else:
971
- adjusted_success_rate = success_rate
972
-
973
- # محاسبه مقیاس‌پذیری
974
- scalability_score = self._calculate_scalability_score(processing_times)
975
-
976
- # آمار حافظه
977
- memory_usage = [r.memory_used_mb for r in results if r.success]
978
- memory_stats = {
979
- 'avg_memory_per_pair': np.mean(memory_usage) if memory_usage else 0,
980
- 'peak_memory': max(memory_usage) if memory_usage else 0,
981
- 'total_memory': sum(memory_usage) if memory_usage else 0,
982
- }
983
-
984
- # آمار زبان
985
- languages = [r.detected_language for r in results if r.success and r.detected_language]
986
- language_distribution = Counter(languages)
987
-
988
- # تحلیل دسته‌های جا افتاده
989
- missed_categories = Counter()
990
- for result in results:
991
- if result.success and result.missed_entities_list:
992
- for missed_entity in result.missed_entities_list:
993
- missed_categories[missed_entity['category']] += 1
994
-
995
- # امتیاز کلی عملکرد بر اساس مقایسه واقعی
996
- performance_score = self._calculate_realistic_performance_score(
997
- adjusted_success_rate, avg_processing_time, overall_anonymization_rate, scalability_score
998
- )
999
-
1000
- return {
1001
- 'total_pairs': total_pairs,
1002
- 'successful_pairs': successful_pairs,
1003
- 'success_rate': success_rate,
1004
- 'adjusted_success_rate': adjusted_success_rate, # تعدیل شده بر اساس entities جا افتاده
1005
- 'avg_processing_time_ms': avg_processing_time,
1006
- 'total_entities_should_anonymize': total_entities_should_anonymize,
1007
- 'total_correctly_anonymized': total_correctly_anonymized,
1008
- 'total_missed_entities': total_missed,
1009
- 'overall_anonymization_rate': overall_anonymization_rate,
1010
- 'pairs_per_minute': (successful_pairs / max(0.01, total_time / 60)),
1011
- 'total_benchmark_time': total_time,
1012
- 'scalability_score': scalability_score,
1013
- 'performance_score': performance_score,
1014
- 'memory_stats': memory_stats,
1015
- 'language_distribution': dict(language_distribution),
1016
- 'missed_categories_analysis': dict(missed_categories.most_common(10)),
1017
- 'quality_metrics': {
1018
- 'precision': round(overall_precision if not np.isnan(overall_precision) else 0, 1),
1019
- 'recall': round(overall_recall if not np.isnan(overall_recall) else 0, 1),
1020
- 'f1_score': round(overall_f1 if not np.isnan(overall_f1) else 0, 1),
1021
- 'accuracy': round(overall_anonymization_rate * 100, 1)
1022
- },
1023
- 'processing_time_percentiles': {
1024
- '50th': np.percentile(processing_times, 50) if processing_times else 0,
1025
- '95th': np.percentile(processing_times, 95) if processing_times else 0,
1026
- '99th': np.percentile(processing_times, 99) if processing_times else 0,
1027
- },
1028
- 'entity_miss_penalty_percent': (total_missed / max(1, total_entities_should_anonymize)) * 100
1029
- }
1030
-
1031
- def _calculate_realistic_performance_score(self, adjusted_success_rate: float, avg_time: float,
1032
- anonymization_rate: float, scalability: float) -> float:
1033
- """محاسبه امتیاز کلی عملکرد بر اساس مقایسه واقعی"""
1034
-
1035
- weights = {
1036
- 'adjusted_success': 0.35, # وزن بیشتر برای نرخ موفقیت تعدیل شده
1037
- 'anonymization_quality': 0.30, # کیفیت ناشناس‌سازی واقعی
1038
- 'speed': 0.20,
1039
- 'scalability': 0.15
1040
- }
1041
-
1042
- success_score = adjusted_success_rate * 100
1043
- quality_score = anonymization_rate * 100
1044
- speed_score = min(100, max(0, 100 - (avg_time / 10))) # کمتر بهتر
1045
- scalability_score = scalability
1046
-
1047
- total_score = (
1048
- weights['adjusted_success'] * success_score +
1049
- weights['anonymization_quality'] * quality_score +
1050
- weights['speed'] * speed_score +
1051
- weights['scalability'] * scalability_score
1052
- )
1053
-
1054
- return round(total_score, 1)
1055
-
1056
- def _calculate_scalability_score(self, processing_times: List[float]) -> float:
1057
- """محاسبه امتیاز مقیاس‌پذیری"""
1058
- if not processing_times:
1059
- return 100.0
1060
-
1061
- std_dev = np.std(processing_times)
1062
- mean_time = np.mean(processing_times)
1063
- cv = std_dev / mean_time if mean_time > 0 else 1
1064
-
1065
- scalability = max(0, 100 - (cv * 100))
1066
- return round(scalability, 1)
1067
-
1068
- def _run_clustering_analysis(self, results: List[ComparisonResult]) -> Dict:
1069
- """اجرای تحلیل کلاسترینگ"""
1070
- if not SKLEARN_AVAILABLE:
1071
- return {}
1072
-
1073
- try:
1074
- features = []
1075
- for result in results:
1076
- feature_vector = [
1077
- result.processing_time_ms,
1078
- result.original_length,
1079
- result.entities_should_anonymize,
1080
- result.anonymization_accuracy,
1081
- result.precision,
1082
- result.recall
1083
- ]
1084
- features.append(feature_vector)
1085
-
1086
- if len(features) < 3:
1087
- return {}
1088
-
1089
- kmeans = KMeans(n_clusters=min(3, len(features)), random_state=42)
1090
- clusters = kmeans.fit_predict(features)
1091
-
1092
- cluster_analysis = {}
1093
- for i in range(max(clusters) + 1):
1094
- cluster_results = [results[j] for j, c in enumerate(clusters) if c == i]
1095
- cluster_analysis[f'cluster_{i}'] = {
1096
- 'count': len(cluster_results),
1097
- 'avg_processing_time': np.mean([r.processing_time_ms for r in cluster_results]),
1098
- 'avg_entities': np.mean([r.entities_should_anonymize for r in cluster_results]),
1099
- 'avg_accuracy': np.mean([r.anonymization_accuracy for r in cluster_results])
1100
- }
1101
-
1102
- return cluster_analysis
1103
-
1104
- except Exception as e:
1105
- logger.error(f"Clustering analysis failed: {e}")
1106
- return {}
1107
-
1108
- def _run_stress_test(self, sample_pair: Tuple[str, str]) -> Dict:
1109
- """اجرای تست استرس"""
1110
- try:
1111
- iterations = self.config.stress_test_iterations
1112
- response_times = []
1113
- successful = 0
1114
- failed = 0
1115
-
1116
- original, anonymized = sample_pair
1117
-
1118
- for _ in range(iterations):
1119
- start_time = time.time()
1120
- try:
1121
- result = self.anonymizer.compare_texts(original, anonymized)
1122
- if result.success:
1123
- successful += 1
1124
- else:
1125
- failed += 1
1126
- except:
1127
- failed += 1
1128
-
1129
- response_time = (time.time() - start_time) * 1000
1130
- response_times.append(response_time)
1131
-
1132
- return {
1133
- 'iterations': iterations,
1134
- 'successful': successful,
1135
- 'failed': failed,
1136
- 'avg_response_time': np.mean(response_times),
1137
- 'max_response_time': max(response_times),
1138
- 'min_response_time': min(response_times),
1139
- 'throughput_per_sec': 1000 / np.mean(response_times) if response_times else 0
1140
- }
1141
-
1142
- except Exception as e:
1143
- logger.error(f"Stress test failed: {e}")
1144
- return {}
1145
-
1146
- def _result_to_dict(self, result: ComparisonResult) -> Dict:
1147
- """تبدیل ComparisonResult به دیکشنری"""
1148
- return {
1149
- 'index': result.index,
1150
- 'success': result.success,
1151
- 'processing_time_ms': result.processing_time_ms,
1152
- 'original_length': result.original_length,
1153
- 'anonymized_length': result.anonymized_length,
1154
- 'entities_should_anonymize': result.entities_should_anonymize,
1155
- 'entities_correctly_anonymized': result.entities_correctly_anonymized,
1156
- 'entities_missed': result.entities_missed,
1157
- 'missed_entities_list': result.missed_entities_list,
1158
- 'anonymization_accuracy': result.anonymization_accuracy,
1159
- 'precision': result.precision,
1160
- 'recall': result.recall,
1161
- 'f1_score': result.f1_score,
1162
- 'detected_language': result.detected_language,
1163
- 'confidence_score': result.confidence_score,
1164
- 'memory_used_mb': result.memory_used_mb,
1165
- 'entity_categories': result.entity_categories,
1166
- 'error': result.error,
1167
- }
1168
-
1169
- def _create_comprehensive_report(self) -> str:
1170
- """ایجاد گزارش جامع و تفصیلی"""
1171
- if not self.current_results:
1172
- return "No results available."
1173
-
1174
- summary = self.current_results['summary']
1175
- config = self.current_results['config']
1176
-
1177
- # تحلیل نتایج تفصیلی
1178
- detailed_results = self.current_results['detailed_results']
1179
- successful_results = [r for r in detailed_results if r.get('success', False)]
1180
-
1181
- # آمار تفصیلی
1182
- processing_times = [r['processing_time_ms'] for r in successful_results]
1183
- accuracy_scores = [r['anonymization_accuracy'] for r in successful_results]
1184
- precision_scores = [r['precision'] for r in successful_results if r['precision'] > 0]
1185
- recall_scores = [r['recall'] for r in successful_results if r['recall'] > 0]
1186
-
1187
- # تحلیل دسته‌بندی‌ها
1188
- all_categories = defaultdict(int)
1189
- missed_categories = defaultdict(int)
1190
-
1191
- for result in successful_results:
1192
- for category, count in result.get('entity_categories', {}).items():
1193
- all_categories[category] += count
1194
-
1195
- for missed_entity in result.get('missed_entities_list', []):
1196
- missed_categories[missed_entity['category']] += 1
1197
-
1198
- report = f"""
1199
- # 📊 گزارش جامع بنچمارک مقایسه واقعی - نسخه 3.1
1200
-
1201
- ## ⭐ خلاصه کلیدی
1202
- **امتیاز عملکرد کلی:** {summary['performance_score']:.1f}/100
1203
- **نرخ موفقیت تعدیل شده:** {summary['adjusted_success_rate']*100:.1f}%
1204
- **دقت ناشناس‌سازی واقعی:** {summary['overall_anonymization_rate']*100:.1f}%
1205
- **جریمه entities جا افتاده:** {summary['entity_miss_penalty_percent']:.1f}%
1206
-
1207
- ## 📈 آمار کلی عملیات
1208
- - **کل جفت متون بررسی شده**: {summary['total_pairs']:,}
1209
- - **جفت‌های موفق**: {summary['successful_pairs']:,}
1210
- - **نرخ موفقیت اولیه**: {summary['success_rate']*100:.1f}%
1211
- - **نرخ موفقیت تعدیل شده**: {summary['adjusted_success_rate']*100:.1f}% (پس از کسر جریمه entities جا افتاده)
1212
-
1213
- ## 🎯 تحلیل عمیق ناشناس‌سازی واقعی
1214
-
1215
- ### نتایج اصلی:
1216
- - **کل entities که باید ناشناس‌سازی می‌شدند**: {summary['total_entities_should_anonymize']:,}
1217
- - **entities صحیح ناشناس‌سازی شده**: {summary['total_correctly_anonymized']:,}
1218
- - **entities جا افتاده**: {summary['total_missed_entities']:,}
1219
- - **نرخ دقت واقعی**: {summary['overall_anonymization_rate']*100:.1f}%
1220
-
1221
- ### متریک‌های کیفیت پیشرفته:
1222
- - **دقت (Precision)**: {summary['quality_metrics']['precision']:.1f}%
1223
- - **بازخوانی (Recall)**: {summary['quality_metrics']['recall']:.1f}%
1224
- - **امتیاز F1**: {summary['quality_metrics']['f1_score']:.1f}%
1225
- - **صحت کلی (Accuracy)**: {summary['quality_metrics']['accuracy']:.1f}%
1226
-
1227
- ## ⚡ آنالیز عملکرد سیستم
1228
-
1229
- ### سرعت و توان عملیاتی:
1230
- - **متوسط زمان پردازش هر جفت**: {summary['avg_processing_time_ms']:.1f} میلی‌ثانیه
1231
- - **توان عملیاتی**: {summary['pairs_per_minute']:.0f} جفت/دقیقه
1232
- - **کل زمان بنچمارک**: {summary['total_benchmark_time']:.1f} ثانیه
1233
- - **امتیاز مقیاس‌پذیری**: {summary['scalability_score']:.1f}/100
1234
-
1235
- ### توزیع زمان پردازش:
1236
- """
1237
-
1238
- if processing_times:
1239
- report += f"""- **میانه زمان پردازش**: {np.median(processing_times):.1f} ms
1240
- - **حداقل زمان**: {min(processing_times):.1f} ms
1241
- - **حداکثر زمان**: {max(processing_times):.1f} ms
1242
- - **انحراف معیار**: {np.std(processing_times):.1f} ms
1243
- """
1244
-
1245
- percentiles = summary.get('processing_time_percentiles', {})
1246
- if percentiles:
1247
- report += f"""- **50درصدی زمان**: {percentiles.get('50th', 0):.1f} ms
1248
- - **95درصدی زمان**: {percentiles.get('95th', 0):.1f} ms
1249
- - **99درصدی زمان**: {percentiles.get('99th', 0):.1f} ms
1250
- """
1251
-
1252
- report += f"""
1253
- ## 💾 تحلیل مصرف حافظه
1254
- - **متوسط حافظه هر جفت**: {summary['memory_stats']['avg_memory_per_pair']:.2f} MB
1255
- - **حداکثر مصرف حافظه**: {summary['memory_stats']['peak_memory']:.2f} MB
1256
- - **کل حافظه استفاده شده**: {summary['memory_stats']['total_memory']:.2f} MB
1257
- - **امتیاز کارایی حافظه**: {max(0, 100 - summary['memory_stats']['avg_memory_per_pair']):.1f}/100
1258
-
1259
- ## 🌍 تحلیل زبان‌ها
1260
- """
1261
-
1262
- lang_dist = summary['language_distribution']
1263
- total_samples = sum(lang_dist.values())
1264
- for lang, count in lang_dist.items():
1265
- lang_name = {'fa': 'فارسی', 'en': 'انگلیسی', 'mixed': 'ترکیبی', 'unknown': 'نامشخص'}.get(lang, lang)
1266
- percentage = (count/total_samples)*100 if total_samples > 0 else 0
1267
- report += f"- **{lang_name}**: {count} جفت ({percentage:.1f}%)\n"
1268
-
1269
- report += f"""
1270
- ## 🔍 تحلیل عمیق دسته‌های entities
1271
-
1272
- ### توزیع کل دسته‌ها:
1273
- """
1274
-
1275
- for category, count in sorted(all_categories.items(), key=lambda x: x[1], reverse=True):
1276
- percentage = (count/sum(all_categories.values()))*100 if sum(all_categories.values()) > 0 else 0
1277
- report += f"- **{category}**: {count} مورد ({percentage:.1f}%)\n"
1278
-
1279
- report += f"""
1280
- ### تحلیل دسته‌های جا افتاده:
1281
- """
1282
-
1283
- if missed_categories:
1284
- for category, missed_count in sorted(missed_categories.items(), key=lambda x: x[1], reverse=True):
1285
- total_in_category = all_categories.get(category, 0)
1286
- miss_rate = (missed_count/total_in_category)*100 if total_in_category > 0 else 0
1287
- report += f"- **{category}**: {missed_count} جا افتاده از {total_in_category} ({miss_rate:.1f}% نرخ جا افتادگی)\n"
1288
- else:
1289
- report += "- هیچ entity جا نیافتاده! (عملکرد فوق‌العاده)\n"
1290
-
1291
- report += f"""
1292
- ## 📊 تحلیل آماری پیشرفته
1293
-
1294
- ### توزیع دقت ناشناس‌سازی:
1295
- """
1296
-
1297
- if accuracy_scores:
1298
- report += f"""- **میانگین دقت**: {np.mean(accuracy_scores)*100:.1f}%
1299
- - **میانه دقت**: {np.median(accuracy_scores)*100:.1f}%
1300
- - **حداقل دقت**: {min(accuracy_scores)*100:.1f}%
1301
- - **حداکثر دقت**: {max(accuracy_scores)*100:.1f}%
1302
- - **انحراف معیار**: {np.std(accuracy_scores)*100:.1f}%
1303
- - **نمونه‌های با دقت 100%**: {sum(1 for s in accuracy_scores if s >= 1.0)} از {len(accuracy_scores)}
1304
- - **نمونه‌های با دقت کمتر از 50%**: {sum(1 for s in accuracy_scores if s < 0.5)} از {len(accuracy_scores)}
1305
- """
1306
-
1307
- # تحلیل کارایی بر اساس طول متن
1308
- if successful_results:
1309
- long_texts = [r for r in successful_results if r['original_length'] > 200]
1310
- short_texts = [r for r in successful_results if r['original_length'] <= 200]
1311
-
1312
- if long_texts and short_texts:
1313
- long_avg_accuracy = np.mean([r['anonymization_accuracy'] for r in long_texts])
1314
- short_avg_accuracy = np.mean([r['anonymization_accuracy'] for r in short_texts])
1315
- long_avg_time = np.mean([r['processing_time_ms'] for r in long_texts])
1316
- short_avg_time = np.mean([r['processing_time_ms'] for r in short_texts])
1317
-
1318
- report += f"""
1319
- ### تحلیل بر اساس طول متن:
1320
- - **متون کوتاه (≤200 کاراکتر)**: {len(short_texts)} نمونه
1321
- - میانگین دقت: {short_avg_accuracy*100:.1f}%
1322
- - میانگین زمان: {short_avg_time:.1f} ms
1323
- - **متون طولانی (>200 کاراکتر)**: {len(long_texts)} نمونه
1324
- - میانگین دقت: {long_avg_accuracy*100:.1f}%
1325
- - میانگین زمان: {long_avg_time:.1f} ms
1326
- """
1327
-
1328
- stress = summary.get('stress_test', {})
1329
- if stress and 'iterations' in stress:
1330
- report += f"""
1331
- ## 🔥 نتایج تست استرس
1332
- - **کل تکرارها**: {stress['iterations']}
1333
- - **موفق**: {stress['successful']} ({stress['successful']/stress['iterations']*100:.1f}%)
1334
- - **ناموفق**: {stress['failed']} ({stress['failed']/stress['iterations']*100:.1f}%)
1335
- - **متوسط زمان پاسخ**: {stress['avg_response_time']:.1f} ms
1336
- - **حداکثر زمان پاسخ**: {stress['max_response_time']:.1f} ms
1337
- - **حداقل زمان پاسخ**: {stress['min_response_time']:.1f} ms
1338
- - **توان عملیاتی**: {stress['throughput_per_sec']:.1f} عملیات/ثانیه
1339
- """
1340
-
1341
- # ارزیابی و پیشنهادات
1342
- performance = summary['performance_score']
1343
- miss_penalty = summary['entity_miss_penalty_percent']
1344
-
1345
- if performance >= 85 and miss_penalty < 10:
1346
- report += """
1347
- ## ✅ سیستم شما عملکرد عالی دارد!
1348
-
1349
- ### نقاط قوت:
1350
- - ناشناس‌سازی با دقت بالا انجام می‌شود
1351
- - entities جا افتاده کم است
1352
- - سرعت پردازش مطلوب
1353
- - مقیاس‌پذیری خوب
1354
- - استفاده بهینه از حافظه
1355
-
1356
- ### توصیه‌ها:
1357
- - آماده برای استفاده در محیط تولید
1358
- - ادامه مانیتورینگ منظم
1359
- - حفظ کیفیت فعلی در به‌روزرسانی‌ها
1360
- - تست دوره‌ای با داده‌های جدید
1361
- """
1362
- elif performance >= 70 or miss_penalty < 20:
1363
- report += """
1364
- ## ⚠️ سیستم نیاز به بهبودهایی دارد:
1365
-
1366
- ### مشکلات شناسایی شده:
1367
- - نرخ entities جا افتاده قابل توجه است
1368
- - دقت ناشناس‌سازی نیاز به بهبود دارد
1369
- - عملکرد در برخی دسته‌ها ضعیف است
1370
-
1371
- ### پیشنهادات بهبود:
1372
- - بهینه‌سازی الگوریتم‌های تشخیص موجودیت
1373
- - کاهش entities جا افتاده با تنظیم threshold ها
1374
- - بهبود دقت pattern matching
1375
- - افزایش پوشش دسته‌های مختلف
1376
- - تنظیم دقیق‌تر پارامترهای سیستم
1377
- - آموزش مجدد مدل‌ها با داده‌های بیشتر
1378
-
1379
- ### اولویت‌های فوری:
1380
- 1. کاهش نرخ جا افتادگی در دسته‌های حساس
1381
- 2. بهبود دقت تشخیص entities
1382
- 3. تست با حجم بیشتر داده
1383
- """
1384
- else:
1385
- report += """
1386
- ## 🔧 سیستم نیاز به بازنگری اساسی دارد:
1387
-
1388
- ### مشکلات جدی شناسایی شده:
1389
- - نرخ بالای entities جا افتاده
1390
- - دقت ناشناس‌سازی پایین
1391
- - عملکرد غیرقابل اعتماد
1392
-
1393
- ### اقدامات ضروری:
1394
- - بازطراحی کامل الگوریتم‌های اصلی
1395
- - افزایش چشمگیر patterns تشخیص
1396
- - پیاده‌سازی مدل‌های NER بهتر
1397
- - بهبود preprocessing متون
1398
- - training مجدد با داده‌های بیشتر
1399
- - کاهش قابل توجه نرخ false negative ها
1400
- - تست گسترده قبل از استقرار
1401
-
1402
- ### پیشنهاد:
1403
- سیستم در حال حاضر آماده استقرار در محیط تولید نیست.
1404
- نیاز به توسعه اساسی و تست‌های گسترده‌تر دارد.
1405
- """
1406
-
1407
- report += f"""
1408
-
1409
- ## 📋 تنظیمات بنچمارک
1410
- - **اندازه نمونه**: {config['sample_size']}
1411
- - **پردازش موازی**: {'فعال' if config['parallel_processing'] else 'غیرفعال'}
1412
- - **تحلیل کلاسترینگ**: {'فعال' if config['clustering_enabled'] else 'غیرفعال'}
1413
-
1414
- ## 📌 نتیجه‌گیری
1415
- این گزارش بر اساس مقایسه واقعی {summary['total_pairs']} جفت متن تولید شده است.
1416
- نتایج نشان‌دهنده عملکرد واقعی سیستم ناشناس‌سازی با در نظر گیری
1417
- entities جا افتاده و دقت فعلی الگوریتم‌های تشخیص می‌باشد.
1418
-
1419
- **زمان تولید گزارش**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
1420
- **نسخه بنچمارک**: Enhanced Real Comparison v3.1
1421
-
1422
- ---
1423
- *این گزارش برای ارزیابی دقیق و بهبود سیستم ناشناس‌سازی طراحی شده است*
1424
- """
1425
-
1426
- return report
1427
-
1428
- def _get_error_response(self, error_msg: str):
1429
- """پاسخ استاندارد برای خطاها"""
1430
- return (
1431
- f"❌ {error_msg}",
1432
- "خطا در اجرای benchmark رخ داده است.",
1433
- gr.update(visible=False),
1434
- gr.update(visible=False)
1435
- )
1436
-
1437
- def download_results(self):
1438
- """دانلود نتایج"""
1439
- if not self.current_results:
1440
- return None
1441
-
1442
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
1443
- filename = f"real_comparison_benchmark_{timestamp}.json"
1444
-
1445
- with open(filename, 'w', encoding='utf-8') as f:
1446
- json.dump(self.current_results, f, ensure_ascii=False, indent=2, default=str)
1447
-
1448
- return filename
1449
-
1450
- # =============================================================================
1451
- # Main Interface Creation
1452
- # =============================================================================
1453
-
1454
- def create_enhanced_interface():
1455
- """ایجاد رابط کاربری پیشرفته"""
1456
-
1457
- benchmark_interface = EnhancedBenchmarkInterface()
1458
-
1459
- custom_css = """
1460
- .gradio-container {
1461
- font-family: 'Segoe UI', Tahoma, Arial, sans-serif !important;
1462
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
1463
- min-height: 100vh !important;
1464
- }
1465
-
1466
- .gradio-button {
1467
- border-radius: 25px !important;
1468
- font-weight: bold !important;
1469
- transition: all 0.3s ease !important;
1470
- margin: 5px 0 !important;
1471
- min-height: 50px !important;
1472
- }
1473
-
1474
- .gradio-button:hover {
1475
- transform: translateY(-2px) !important;
1476
- box-shadow: 0 6px 20px rgba(0,0,0,0.2) !important;
1477
- }
1478
-
1479
- h1, h2, h3 {
1480
- text-shadow: 2px 2px 4px rgba(0,0,0,0.3) !important;
1481
- margin: 10px 0 !important;
1482
- line-height: 1.2 !important;
1483
- }
1484
- """
1485
-
1486
- with gr.Blocks(title="🚀 Enhanced Real Comparison Benchmark", theme=gr.themes.Soft(), css=custom_css) as app:
1487
-
1488
- # Header
1489
- gr.HTML("""
1490
- <div style="text-align: center; padding: 20px;">
1491
- <h1>🚀 Enhanced Real Comparison Anonymization Benchmark v3.1</h1>
1492
- <p style="font-size: 1.2rem;">Real Performance Analysis with Original vs Anonymized Text Comparison</p>
1493
- <p>✨ Features: Real Entity Counting, Miss Rate Calculation, Adjusted Success Metrics</p>
1494
- </div>
1495
- """)
1496
-
1497
- with gr.Row():
1498
- with gr.Column(scale=1):
1499
- # Dataset Status
1500
- gr.HTML("""
1501
- <div style="background-color: #e8f4f8; padding: 15px; border-radius: 10px; margin-bottom: 15px;">
1502
- <h3>📁 Dataset Requirements</h3>
1503
- <p>System looks for files with columns:</p>
1504
- <ul>
1505
- <li><strong>original_text</strong> - Original sentences</li>
1506
- <li><strong>anonymized_text</strong> - Anonymized versions</li>
1507
- </ul>
1508
- <p><strong>Supported files:</strong></p>
1509
- <p>datasetfa.csv, dataset-fa.csv, dataseten.csv, dataset-en.csv</p>
1510
- </div>
1511
- """)
1512
-
1513
- sample_size = gr.Slider(
1514
- minimum=10,
1515
- maximum=500,
1516
- value=100,
1517
- step=5,
1518
- label="📊 Sample Size (Text Pairs to Compare)"
1519
- )
1520
-
1521
- with gr.Row():
1522
- enable_parallel = gr.Checkbox(
1523
- label="⚡ Enable Parallel Processing",
1524
- value=True
1525
- )
1526
-
1527
- enable_clustering = gr.Checkbox(
1528
- label="🎯 Enable Clustering Analysis",
1529
- value=False
1530
- )
1531
-
1532
- run_btn = gr.Button(
1533
- "🚀 Run Real Comparison Benchmark",
1534
- variant="primary",
1535
- size="lg"
1536
- )
1537
-
1538
- download_btn = gr.Button(
1539
- "📥 Download Complete Results",
1540
- variant="secondary",
1541
- visible=False
1542
- )
1543
-
1544
- status_output = gr.Textbox(
1545
- label="📋 Status",
1546
- interactive=False,
1547
- lines=4
1548
- )
1549
-
1550
- with gr.Column(scale=2):
1551
- # Results - Only Text Report
1552
- detailed_report = gr.Markdown(
1553
- "No results yet. Please run the real comparison benchmark first.",
1554
- visible=False,
1555
- label="📊 Comprehensive Analysis Report"
1556
- )
1557
-
1558
- # System Status with file check
1559
- system_status = "✅ Enhanced comparison system ready" if benchmark_interface.system_ready else "⚠️ Running in limited mode"
1560
-
1561
- # Check for datasets
1562
- fa_files = ['datasetfa.csv', 'dataset-fa.csv']
1563
- en_files = ['dataseten.csv', 'dataset-en.csv']
1564
-
1565
- fa_status = "❌ Not found"
1566
- en_status = "❌ Not found"
1567
-
1568
- for f in fa_files:
1569
- if os.path.exists(f):
1570
- fa_status = f"✅ Found: {f}"
1571
- break
1572
-
1573
- for f in en_files:
1574
- if os.path.exists(f):
1575
- en_status = f"✅ Found: {f}"
1576
- break
1577
-
1578
- gr.HTML(f"""
1579
- <div style="text-align: center; margin-top: 20px; padding: 15px; background-color: #e8f4f8; border-radius: 10px;">
1580
- <p><strong>System Status:</strong> {system_status}</p>
1581
- <p><strong>Persian Dataset:</strong> {fa_status}</p>
1582
- <p><strong>English Dataset:</strong> {en_status}</p>
1583
- <p><strong>Available Features:</strong>
1584
- {'✅ Parallel Processing' if multiprocessing.cpu_count() > 1 else '❌ Sequential Only'} |
1585
- {'✅ Advanced Metrics' if SKLEARN_AVAILABLE else '❌ Basic Metrics'} |
1586
- {'✅ Memory Profiling' if PSUTIL_AVAILABLE else '❌ No Memory Tracking'} |
1587
- {'✅ NER Models' if SPACY_AVAILABLE else '❌ Pattern-Only'}
1588
- </p>
1589
- </div>
1590
- """)
1591
-
1592
- # Usage Guide
1593
- with gr.Accordion("📖 Real Comparison Guide", open=False):
1594
- gr.HTML("""
1595
- <div style="padding: 20px;">
1596
- <h3>🚀 How It Works</h3>
1597
- <ol>
1598
- <li><strong>Dataset Preparation:</strong> CSV files with original_text and anonymized_text columns</li>
1599
- <li><strong>Real Entity Detection:</strong> System finds entities that should be anonymized in original text</li>
1600
- <li><strong>Comparison Analysis:</strong> Checks which entities were actually anonymized</li>
1601
- <li><strong>Miss Rate Calculation:</strong> Calculates percentage of missed entities</li>
1602
- <li><strong>Adjusted Metrics:</strong> Success rate adjusted based on missed entities</li>
1603
- </ol>
1604
-
1605
- <h3>📊 Key Metrics</h3>
1606
- <ul>
1607
- <li><strong>Real Anonymization Accuracy:</strong> Correctly anonymized / Should be anonymized</li>
1608
- <li><strong>Adjusted Success Rate:</strong> Initial success - entity miss penalty</li>
1609
- <li><strong>Entity Miss Penalty:</strong> (Missed entities / Total entities) × 100</li>
1610
- <li><strong>Precision & Recall:</strong> Based on actual entity detection and anonymization</li>
1611
- </ul>
1612
-
1613
- <h3>🎯 Expected File Format</h3>
1614
- <pre>
1615
- original_text,anonymized_text
1616
- "John Smith works at Apple Inc","PERSON_001 works at COMPANY_001"
1617
- "Call me at 555-1234","Call me at PHONE_001"
1618
- </pre>
1619
- </div>
1620
- """)
1621
-
1622
- # Event Handlers
1623
- run_btn.click(
1624
- fn=benchmark_interface.run_enhanced_benchmark,
1625
- inputs=[sample_size, enable_parallel, enable_clustering],
1626
- outputs=[
1627
- status_output,
1628
- detailed_report,
1629
- download_btn,
1630
- detailed_report # visibility toggle
1631
- ],
1632
- show_progress=True
1633
- )
1634
-
1635
- download_btn.click(
1636
- fn=benchmark_interface.download_results,
1637
- outputs=gr.File()
1638
- )
1639
-
1640
- return app
1641
-
1642
- # =============================================================================
1643
- # Main Function
1644
- # =============================================================================
1645
-
1646
- def main():
1647
- """تابع اصلی"""
1648
-
1649
- print("🚀 Starting Enhanced Real Comparison Benchmark System v3.1...")
1650
- print("=" * 80)
1651
-
1652
- # Check for datasets
1653
- datasets = [
1654
- ('dataset-fa.csv', 'Persian'),
1655
- ('dataset-fa.csv', 'Persian (Alt)'),
1656
- ('dataset-en.csv', 'English'),
1657
- ('dataset-en.csv', 'English (Alt)')
1658
- ]
1659
-
1660
- for filename, desc in datasets:
1661
- if os.path.exists(filename):
1662
- print(f"✅ {desc} dataset found: {filename}")
1663
- try:
1664
- df = pd.read_csv(filename, encoding='utf-8')
1665
- print(f" - Rows: {len(df)}")
1666
- print(f" - Columns: {list(df.columns)}")
1667
- if 'original_text' in df.columns and 'anonymized_text' in df.columns:
1668
- print(f" - ✅ Required columns present")
1669
- else:
1670
- print(f" - ❌ Missing required columns: original_text, anonymized_text")
1671
- except Exception as e:
1672
- print(f" - ❌ Error reading file: {e}")
1673
- else:
1674
- print(f"❌ {desc} dataset not found: {filename}")
1675
-
1676
- # System capabilities
1677
- features = []
1678
- if SKLEARN_AVAILABLE:
1679
- features.append("Advanced ML Metrics")
1680
- if PSUTIL_AVAILABLE:
1681
- features.append("Memory Profiling")
1682
- if SPACY_AVAILABLE:
1683
- features.append("NER Models")
1684
-
1685
- features.extend(["Real Entity Comparison", "Miss Rate Analysis", "Adjusted Metrics"])
1686
-
1687
- print(f"✨ Available features: {', '.join(features)}")
1688
- print(f"🖥️ CPU Cores: {multiprocessing.cpu_count()}")
1689
- print(f"🧠 Memory: {psutil.virtual_memory().total // (1024**3) if PSUTIL_AVAILABLE else 'Unknown'} GB")
1690
-
1691
- # Create and launch interface
1692
- demo = create_enhanced_interface()
1693
-
1694
- # Launch with enhanced configuration
1695
- demo.launch(
1696
- server_name="0.0.0.0",
1697
- server_port=7860,
1698
- share=False,
1699
- inbrowser=True,
1700
- show_error=True,
1701
- favicon_path=None,
1702
- ssl_verify=False,
1703
- max_file_size="50mb"
1704
- )
1705
-
1706
- if __name__ == "__main__":
1707
- main()