leilaghomashchi commited on
Commit
ed59e8c
·
verified ·
1 Parent(s): cf0a723

Upload anonymization_benchmark (4).py

Browse files
Files changed (1) hide show
  1. anonymization_benchmark (4).py +707 -0
anonymization_benchmark (4).py ADDED
@@ -0,0 +1,707 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import re
3
+ import numpy as np
4
+ import json
5
+ from typing import Dict, List, Any, Tuple
6
+ import gradio as gr
7
+ from pathlib import Path
8
+ import plotly.graph_objects as go
9
+ import plotly.express as px
10
+ from dataclasses import dataclass
11
+ from datetime import datetime
12
+
13
+ @dataclass
14
+ class BenchmarkMetrics:
15
+ """کلاس متریک‌های بنچمارک"""
16
+ model_name: str
17
+ total_texts: int
18
+ total_entities: int
19
+ accuracy: float # درستی کلی ناشناس‌سازی
20
+ recall: float # پوشش موجودیت‌ها
21
+ precision: float # دقت شناسایی
22
+
23
+ class AnonymizationBenchmark:
24
+ """کلاس اصلی بنچمارک ناشناس‌سازی"""
25
+
26
+ def __init__(self):
27
+ self.models_data = {}
28
+ self.benchmark_results = {}
29
+
30
+ def load_csv_files(self, chatgpt_file, grok_file, llama_file, manual_file):
31
+ """بارگذاری فایل‌های CSV"""
32
+ try:
33
+ # بارگذاری فایل‌ها
34
+ chatgpt_df = pd.read_csv(chatgpt_file)
35
+ grok_df = pd.read_csv(grok_file)
36
+ llama_df = pd.read_csv(llama_file)
37
+ manual_df = pd.read_csv(manual_file)
38
+
39
+ # بررسی ستون‌ها
40
+ required_columns = ['original_text', 'anonymized_text']
41
+
42
+ for df_name, df in [('ChatGPT', chatgpt_df), ('Grok', grok_df), ('Llama', llama_df), ('Manual', manual_df)]:
43
+ if not all(col in df.columns for col in required_columns):
44
+ raise ValueError(f"فایل {df_name} فاقد ستون‌های مورد نیاز است")
45
+
46
+ self.models_data = {
47
+ 'ChatGPT': chatgpt_df,
48
+ 'Grok': grok_df,
49
+ 'Llama-3.1-8B': llama_df,
50
+ 'Manual (دستی)': manual_df
51
+ }
52
+
53
+ return True, "فایل‌ها با موفقیت بارگذاری شدند"
54
+
55
+ except Exception as e:
56
+ return False, f"خطا در بارگذاری فایل‌ها: {str(e)}"
57
+
58
+ def extract_entities_from_text(self, text: str) -> Dict[str, List[str]]:
59
+ """استخراج موجودیت‌ها از متن"""
60
+ entities = {
61
+ 'companies': re.findall(r'company-(\d+)', text),
62
+ 'persons': re.findall(r'person-(\d+)', text),
63
+ 'amounts': re.findall(r'amount-(\d+)', text),
64
+ 'percents': re.findall(r'percent-(\d+)', text),
65
+ 'groups': re.findall(r'group-(\d+)', text)
66
+ }
67
+ return entities
68
+
69
+ def count_original_entities(self, text: str) -> int:
70
+ """تخمین تعداد موجودیت‌های قابل ناشناس‌سازی در متن اصلی"""
71
+ # الگوهای شناسایی موجودیت‌ها در متن فارسی
72
+ patterns = [
73
+ r'[۰-۹]+(?:\.[۰-۹]+)?\s*(?:میلیارد|میلیون|هزار)?\s*(?:تومان|ریال|دلار|یورو)', # اعداد پولی
74
+ r'[۰-۹]+(?:\.[۰-۹]+)?\s*درصد', # درصدها
75
+ r'\b[آ-ی\s]{2,30}\b(?:\s*(?:شرکت|بانک|گروه|سازمان))', # شرکت‌ها
76
+ r'\b[آ-ی\s]{2,20}\b(?:\s*(?:مدیرعامل|رئیس|مدیر))', # اشخاص
77
+ r'[۰-۹]+(?:\.[۰-۹]+)?(?:\s*(?:میلیون|میلیارد|هزار))?', # سایر اعداد
78
+ ]
79
+
80
+ total_entities = 0
81
+ for pattern in patterns:
82
+ matches = re.findall(pattern, text)
83
+ total_entities += len(matches)
84
+
85
+ return max(total_entities, 1) # حداقل 1 برای جلوگیری از تقسیم بر صفر
86
+
87
+ def check_indexing_correctness(self, entities: Dict[str, List[str]]) -> float:
88
+ """بررسی درستی اندیس‌گذاری"""
89
+ total_checks = 0
90
+ passed_checks = 0
91
+
92
+ for entity_type, indices in entities.items():
93
+ if not indices:
94
+ continue
95
+
96
+ total_checks += 1
97
+ unique_indices = sorted([int(x) for x in set(indices)])
98
+
99
+ # بررسی شروع از 1
100
+ if unique_indices[0] == 1:
101
+ passed_checks += 0.5
102
+
103
+ # بررسی پیوستگی
104
+ expected = list(range(1, len(unique_indices) + 1))
105
+ if unique_indices == expected:
106
+ passed_checks += 0.5
107
+
108
+ return passed_checks / total_checks if total_checks > 0 else 0.0
109
+
110
+ def calculate_structure_preservation(self, original_text: str, anonymized_text: str) -> float:
111
+ """محاسبه امتیاز حفظ ساختار"""
112
+ # کلمات مهم که باید حفظ شوند
113
+ important_words = [
114
+ 'میلیارد', 'میلیون', 'تومان', 'ریال', 'درصد', 'سود', 'زیان',
115
+ 'مدیرعامل', 'شرکت', 'بانک', 'درآمد', 'سال', 'ماه'
116
+ ]
117
+
118
+ score = 0.0
119
+ total_checks = len(important_words)
120
+
121
+ for word in important_words:
122
+ if word in original_text and word in anonymized_text:
123
+ score += 1.0
124
+ elif word not in original_text:
125
+ total_checks -= 1
126
+
127
+ # بررسی حفظ تعداد کلمات (تقریبی)
128
+ original_words = len(original_text.split())
129
+ anonymized_words = len(anonymized_text.split())
130
+
131
+ if original_words > 0:
132
+ word_ratio = min(anonymized_words / original_words, 1.0)
133
+ score += word_ratio * 2
134
+ total_checks += 2
135
+
136
+ return score / total_checks if total_checks > 0 else 0.0
137
+
138
+ def calculate_accuracy(self, original_text: str, anonymized_text: str) -> float:
139
+ """محاسبه درستی کلی ناشناس‌سازی"""
140
+ entities = self.extract_entities_from_text(anonymized_text)
141
+
142
+ # بررسی درستی اندیس‌گذاری
143
+ indexing_score = self.check_indexing_correctness(entities)
144
+
145
+ # بررسی حفظ ساختار
146
+ structure_score = self.calculate_structure_preservation(original_text, anonymized_text)
147
+
148
+ # میانگین وزنی
149
+ accuracy = (indexing_score * 0.6) + (structure_score * 0.4)
150
+ return accuracy
151
+
152
+ def calculate_recall(self, original_text: str, anonymized_text: str) -> float:
153
+ """محاسبه پوشش موجودیت‌ها (Recall)"""
154
+ original_entity_count = self.count_original_entities(original_text)
155
+ entities = self.extract_entities_from_text(anonymized_text)
156
+ anonymized_entity_count = sum(len(set(v)) for v in entities.values())
157
+
158
+ return min(anonymized_entity_count / original_entity_count, 1.0)
159
+
160
+ def calculate_precision(self, anonymized_text: str) -> float:
161
+ """محاسبه دقت شناسایی (Precision)"""
162
+ entities = self.extract_entities_from_text(anonymized_text)
163
+
164
+ # بررسی کیفیت موجودیت‌های شناسایی شده
165
+ total_entities = sum(len(v) for v in entities.values())
166
+ if total_entities == 0:
167
+ return 0.0
168
+
169
+ # بررسی درستی فرمت شناسه‌ها
170
+ correct_entities = 0
171
+ for entity_type, indices in entities.items():
172
+ for idx in indices:
173
+ if idx.isdigit() and int(idx) > 0:
174
+ correct_entities += 1
175
+
176
+ # بررسی عدم تکرار غیرضروری
177
+ unique_entities = sum(len(set(v)) for v in entities.values())
178
+ consistency_bonus = unique_entities / total_entities if total_entities > 0 else 0
179
+
180
+ base_precision = correct_entities / total_entities if total_entities > 0 else 0
181
+ return (base_precision * 0.7) + (consistency_bonus * 0.3)
182
+
183
+ def analyze_model(self, model_name: str, df: pd.DataFrame) -> BenchmarkMetrics:
184
+ """تحلیل یک مدل"""
185
+ print(f"تحلیل مدل {model_name}...")
186
+
187
+ total_texts = len(df)
188
+
189
+ # محاسبه متریک‌ها برای هر متن
190
+ accuracy_scores = []
191
+ recall_scores = []
192
+ precision_scores = []
193
+ total_entities = 0
194
+
195
+ for _, row in df.iterrows():
196
+ original = str(row['original_text'])
197
+ anonymized = str(row['anonymized_text'])
198
+
199
+ # محاسبه متریک‌ها
200
+ accuracy_scores.append(self.calculate_accuracy(original, anonymized))
201
+ recall_scores.append(self.calculate_recall(original, anonymized))
202
+ precision_scores.append(self.calculate_precision(anonymized))
203
+
204
+ # شمارش موجودیت‌ها
205
+ entities = self.extract_entities_from_text(anonymized)
206
+ total_entities += sum(len(set(v)) for v in entities.values())
207
+
208
+ return BenchmarkMetrics(
209
+ model_name=model_name,
210
+ total_texts=total_texts,
211
+ total_entities=total_entities,
212
+ accuracy=round(np.mean(accuracy_scores), 3),
213
+ recall=round(np.mean(recall_scores), 3),
214
+ precision=round(np.mean(precision_scores), 3)
215
+ )
216
+
217
+ def run_benchmark(self) -> Tuple[bool, str, str]:
218
+ """اجرای بنچمارک کامل"""
219
+ if not self.models_data:
220
+ return False, "ابتدا فایل‌ها را بارگذاری کنید", ""
221
+
222
+ try:
223
+ results = {}
224
+
225
+ # تحلیل هر مدل
226
+ for model_name, df in self.models_data.items():
227
+ results[model_name] = self.analyze_model(model_name, df)
228
+
229
+ self.benchmark_results = results
230
+
231
+ # تولید HTML
232
+ html_report = self.generate_html_report()
233
+
234
+ return True, "بنچمارک با موفقیت انجام شد", html_report
235
+
236
+ except Exception as e:
237
+ return False, f"خطا در اجرای بنچمارک: {str(e)}", ""
238
+
239
+ def generate_comparison_table(self) -> str:
240
+ """تولید جدول مقایسه"""
241
+ if not self.benchmark_results:
242
+ return "<p>هنوز بنچمارکی انجام نشده است</p>"
243
+
244
+ # آماده‌سازی داده‌ها برای جدول
245
+ table_data = []
246
+ for model_name, metrics in self.benchmark_results.items():
247
+ table_data.append({
248
+ 'مدل': model_name,
249
+ 'تعداد متن‌ها': metrics.total_texts,
250
+ 'کل موجودیت‌ها': metrics.total_entities,
251
+ '🎯 دقت (Accuracy)': f"{metrics.accuracy:.3f}",
252
+ '📊 بازیابی (Recall)': f"{metrics.recall:.3f}",
253
+ '✅ دقت شناسایی (Precision)': f"{metrics.precision:.3f}"
254
+ })
255
+
256
+ # تولید HTML جدول
257
+ html = """
258
+ <div style="overflow-x: auto; margin: 20px 0;">
259
+ <table style="width: 100%; border-collapse: collapse; font-family: 'Tahoma', sans-serif;">
260
+ <thead>
261
+ <tr style="background-color: #4CAF50; color: white;">
262
+ """
263
+
264
+ # سرستون‌ها
265
+ headers = list(table_data[0].keys())
266
+ for header in headers:
267
+ html += f"<th style='border: 1px solid #ddd; padding: 12px; text-align: center;'>{header}</th>"
268
+
269
+ html += "</tr></thead><tbody>"
270
+
271
+ # ردیف‌ها
272
+ for i, row in enumerate(table_data):
273
+ bg_color = "#f2f2f2" if i % 2 == 0 else "white"
274
+ html += f"<tr style='background-color: {bg_color};'>"
275
+
276
+ for j, (key, value) in enumerate(row.items()):
277
+ # رنگ‌بندی ستون‌های متریک‌ها
278
+ if key in ['🎯 دقت (Accuracy)', '📊 بازیابی (Recall)', '✅ دقت شناسایی (Precision)']:
279
+ score = float(value)
280
+ if score >= 0.8:
281
+ color = "#4CAF50" # سبز
282
+ elif score >= 0.6:
283
+ color = "#FF9800" # نارنجی
284
+ else:
285
+ color = "#F44336" # قرمز
286
+ html += f"<td style='border: 1px solid #ddd; padding: 12px; text-align: center; font-weight: bold; color: {color};'>{value}</td>"
287
+ else:
288
+ html += f"<td style='border: 1px solid #ddd; padding: 12px; text-align: center;'>{value}</td>"
289
+
290
+ html += "</tr>"
291
+
292
+ html += "</tbody></table></div>"
293
+
294
+ return html
295
+
296
+ def generate_charts(self) -> str:
297
+ """تولید نمودارها"""
298
+ if not self.benchmark_results:
299
+ return ""
300
+
301
+ models = list(self.benchmark_results.keys())
302
+ accuracy_scores = [self.benchmark_results[model].accuracy for model in models]
303
+ recall_scores = [self.benchmark_results[model].recall for model in models]
304
+ precision_scores = [self.benchmark_results[model].precision for model in models]
305
+
306
+ # نمودار مقایسه سه متریک
307
+ chart_html = """
308
+ <div style="margin: 20px 0;">
309
+ <h3 style="text-align: center; color: #333;">مقایسه متریک‌های عملکرد مدل‌ها</h3>
310
+ <div style="display: flex; justify-content: center; gap: 30px; background-color: #f9f9f9; padding: 30px; border-radius: 15px; flex-wrap: wrap;">
311
+ """
312
+
313
+ colors = ['#4CAF50', '#2196F3', '#FF9800', '#9C27B0'] # اضافه کردن رنگ چهارم
314
+
315
+ for i, model in enumerate(models):
316
+ accuracy = accuracy_scores[i]
317
+ recall = recall_scores[i]
318
+ precision = precision_scores[i]
319
+
320
+ chart_html += f"""
321
+ <div style="text-align: center; min-width: 180px; margin-bottom: 20px;">
322
+ <h4 style="margin-bottom: 15px; color: #333; font-size: 14px;">{model}</h4>
323
+
324
+ <div style="margin-bottom: 10px;">
325
+ <div style="font-size: 11px; color: #666; margin-bottom: 5px;">Accuracy</div>
326
+ <div style="background-color: {colors[0]}; width: 50px; height: {accuracy*80}px; margin: 0 auto; border-radius: 3px; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 9px;">
327
+ {accuracy:.3f}
328
+ </div>
329
+ </div>
330
+
331
+ <div style="margin-bottom: 10px;">
332
+ <div style="font-size: 11px; color: #666; margin-bottom: 5px;">Recall</div>
333
+ <div style="background-color: {colors[1]}; width: 50px; height: {recall*80}px; margin: 0 auto; border-radius: 3px; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 9px;">
334
+ {recall:.3f}
335
+ </div>
336
+ </div>
337
+
338
+ <div style="margin-bottom: 10px;">
339
+ <div style="font-size: 11px; color: #666; margin-bottom: 5px;">Precision</div>
340
+ <div style="background-color: {colors[2]}; width: 50px; height: {precision*80}px; margin: 0 auto; border-radius: 3px; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 9px;">
341
+ {precision:.3f}
342
+ </div>
343
+ </div>
344
+ </div>
345
+ """
346
+
347
+ chart_html += "</div></div>"
348
+
349
+ return chart_html
350
+
351
+ def generate_html_report(self) -> str:
352
+ """تولید گزارش HTML کامل"""
353
+ current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
354
+
355
+ html = f"""
356
+ <!DOCTYPE html>
357
+ <html lang="fa" dir="rtl">
358
+ <head>
359
+ <meta charset="UTF-8">
360
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
361
+ <title>گزارش بنچمارک ناشناس‌سازی</title>
362
+ <style>
363
+ * {{
364
+ margin: 0;
365
+ padding: 0;
366
+ box-sizing: border-box;
367
+ }}
368
+ body {{
369
+ font-family: 'Tahoma', 'Arial', sans-serif;
370
+ line-height: 1.6;
371
+ color: #333;
372
+ background-color: #f5f5f5;
373
+ padding: 20px;
374
+ }}
375
+ .container {{
376
+ max-width: 1400px;
377
+ margin: 0 auto;
378
+ background-color: white;
379
+ border-radius: 10px;
380
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
381
+ overflow: hidden;
382
+ }}
383
+ .header {{
384
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
385
+ color: white;
386
+ padding: 30px;
387
+ text-align: center;
388
+ }}
389
+ .header h1 {{
390
+ font-size: 2.5em;
391
+ margin-bottom: 10px;
392
+ }}
393
+ .header p {{
394
+ font-size: 1.2em;
395
+ opacity: 0.9;
396
+ }}
397
+ .content {{
398
+ padding: 30px;
399
+ }}
400
+ .summary {{
401
+ background-color: #e8f5e8;
402
+ border-right: 5px solid #4CAF50;
403
+ padding: 20px;
404
+ margin-bottom: 30px;
405
+ border-radius: 5px;
406
+ }}
407
+ .section {{
408
+ margin-bottom: 40px;
409
+ }}
410
+ .section h2 {{
411
+ color: #333;
412
+ border-bottom: 2px solid #4CAF50;
413
+ padding-bottom: 10px;
414
+ margin-bottom: 20px;
415
+ }}
416
+ .metrics-grid {{
417
+ display: grid;
418
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
419
+ gap: 20px;
420
+ margin-bottom: 30px;
421
+ }}
422
+ .metric-card {{
423
+ background-color: #f8f9fa;
424
+ border: 1px solid #dee2e6;
425
+ border-radius: 8px;
426
+ padding: 20px;
427
+ text-align: center;
428
+ transition: transform 0.2s;
429
+ }}
430
+ .metric-card:hover {{
431
+ transform: translateY(-5px);
432
+ box-shadow: 0 5px 15px rgba(0,0,0,0.1);
433
+ }}
434
+ .metric-number {{
435
+ font-size: 2em;
436
+ font-weight: bold;
437
+ color: #4CAF50;
438
+ margin-bottom: 5px;
439
+ }}
440
+ .metric-label {{
441
+ color: #666;
442
+ font-size: 0.9em;
443
+ }}
444
+ .footer {{
445
+ background-color: #f8f9fa;
446
+ padding: 20px;
447
+ text-align: center;
448
+ color: #666;
449
+ border-top: 1px solid #dee2e6;
450
+ }}
451
+ </style>
452
+ </head>
453
+ <body>
454
+ <div class="container">
455
+ <div class="header">
456
+ <h1>🏆 گزارش بنچمارک ناشناس‌سازی</h1>
457
+ <p>مقایسه عملکرد مدل‌های ChatGPT، Grok و Llama-3.1-8B</p>
458
+ </div>
459
+
460
+ <div class="content">
461
+ <div class="summary">
462
+ <h3>📋 خلاصه نتایج</h3>
463
+ <p>این گزارش نتایج بنچم��رک سه مدل مختلف برای ناشناس‌سازی متون فارسی را نشان می‌دهد.
464
+ متریک‌های ارزیابی شامل درستی اندیس‌گذاری، ثبات استفاده از شناسه‌ها، حفظ ساختار متن و پوشش موجودیت‌ها می‌باشد.</p>
465
+ </div>
466
+
467
+ <div class="section">
468
+ <h2>📊 جدول مقایسه کامل</h2>
469
+ {self.generate_comparison_table()}
470
+ </div>
471
+
472
+ <div class="section">
473
+ <h2>📈 نمودار مقایسه</h2>
474
+ {self.generate_charts()}
475
+ </div>
476
+
477
+ <div class="section">
478
+ <h2>🔍 تحلیل تفصیلی</h2>
479
+ {self.generate_detailed_analysis()}
480
+ </div>
481
+ </div>
482
+
483
+ <div class="footer">
484
+ <p>گزارش تولید شده در تاریخ: {current_time}</p>
485
+ <p>ابزار بنچمارک ناشناس‌سازی متون فارسی</p>
486
+ </div>
487
+ </div>
488
+ </body>
489
+ </html>
490
+ """
491
+
492
+ return html
493
+
494
+ def generate_detailed_analysis(self) -> str:
495
+ """تولید تحلیل تفصیلی"""
496
+ if not self.benchmark_results:
497
+ return "<p>داده‌ای برای تحلیل یافت نشد</p>"
498
+
499
+ # یافتن بهترین مدل در هر متریک
500
+ best_accuracy = max(self.benchmark_results.keys(),
501
+ key=lambda k: self.benchmark_results[k].accuracy)
502
+ best_recall = max(self.benchmark_results.keys(),
503
+ key=lambda k: self.benchmark_results[k].recall)
504
+ best_precision = max(self.benchmark_results.keys(),
505
+ key=lambda k: self.benchmark_results[k].precision)
506
+
507
+ # محاسبه میانگین
508
+ avg_accuracy = np.mean([m.accuracy for m in self.benchmark_results.values()])
509
+ avg_recall = np.mean([m.recall for m in self.benchmark_results.values()])
510
+ avg_precision = np.mean([m.precision for m in self.benchmark_results.values()])
511
+
512
+ analysis = f"""
513
+ <div class="metrics-grid">
514
+ <div class="metric-card">
515
+ <div class="metric-number">🎯</div>
516
+ <div class="metric-label">بهترین Accuracy: {best_accuracy}</div>
517
+ <div style="color: #4CAF50; font-weight: bold;">
518
+ {self.benchmark_results[best_accuracy].accuracy:.3f}
519
+ </div>
520
+ </div>
521
+ <div class="metric-card">
522
+ <div class="metric-number">📊</div>
523
+ <div class="metric-label">بهترین Recall: {best_recall}</div>
524
+ <div style="color: #2196F3; font-weight: bold;">
525
+ {self.benchmark_results[best_recall].recall:.3f}
526
+ </div>
527
+ </div>
528
+ <div class="metric-card">
529
+ <div class="metric-number">✅</div>
530
+ <div class="metric-label">بهترین Precision: {best_precision}</div>
531
+ <div style="color: #FF9800; font-weight: bold;">
532
+ {self.benchmark_results[best_precision].precision:.3f}
533
+ </div>
534
+ </div>
535
+ </div>
536
+
537
+ <div style="background-color: #e3f2fd; border: 1px solid #2196F3; border-radius: 8px; padding: 20px; margin-top: 20px;">
538
+ <h4>📈 آمار کلی:</h4>
539
+ <ul style="margin-top: 10px; padding-right: 20px;">
540
+ <li><strong>میانگین Accuracy:</strong> {avg_accuracy:.3f}</li>
541
+ <li><strong>میانگین Recall:</strong> {avg_recall:.3f}</li>
542
+ <li><strong>میانگین Precision:</strong> {avg_precision:.3f}</li>
543
+ </ul>
544
+ </div>
545
+
546
+ <div style="background-color: #fff3cd; border: 1px solid #ffeaa7; border-radius: 8px; padding: 20px; margin-top: 20px;">
547
+ <h4>💡 تفسیر نتایج:</h4>
548
+ <ul style="margin-top: 10px; padding-right: 20px;">
549
+ <li><strong>Accuracy:</strong> دقت کلی ناشناس‌سازی (ترکیب اندیس‌گذاری صحیح و حفظ ساختار)</li>
550
+ <li><strong>Recall:</strong> پوشش موجودیت‌ها (چه درصدی از موجودیت‌ها شناسایی شدند)</li>
551
+ <li><strong>Precision:</strong> دقت شناسایی (چه درصدی از شناسه‌ها صحیح هستند)</li>
552
+ </ul>
553
+ </div>
554
+ """
555
+
556
+ return analysis
557
+
558
+ # رابط کاربری Gradio
559
+ def create_benchmark_interface():
560
+ """ایجاد رابط کاربری بنچمارک"""
561
+ benchmark = AnonymizationBenchmark()
562
+
563
+ with gr.Blocks(
564
+ title="بنچمارک ناشناس‌سازی",
565
+ theme=gr.themes.Soft(),
566
+ css="""
567
+ .gradio-container {
568
+ font-family: 'Tahoma', 'Arial', sans-serif !important;
569
+ direction: rtl;
570
+ max-width: 1400px;
571
+ margin: 0 auto;
572
+ }
573
+ .upload-box {
574
+ border: 2px dashed #4CAF50;
575
+ border-radius: 10px;
576
+ padding: 20px;
577
+ text-align: center;
578
+ background-color: #f8f9fa;
579
+ margin: 10px 0;
580
+ }
581
+ """
582
+ ) as interface:
583
+
584
+ gr.Markdown("""
585
+ # 🏆 ابزار بنچمارک ناشناس‌سازی متون فارسی
586
+ ### مقایسه عملکرد مدل‌های ChatGPT، Grok، Llama-3.1-8B و ناشناس‌سازی دستی
587
+ """)
588
+
589
+ with gr.Row():
590
+ with gr.Column(scale=1):
591
+ gr.Markdown("### 📁 بارگذاری فایل‌های CSV")
592
+
593
+ chatgpt_file = gr.File(
594
+ label="📄 فایل ChatGPT",
595
+ file_types=[".csv"],
596
+ elem_classes=["upload-box"]
597
+ )
598
+
599
+ grok_file = gr.File(
600
+ label="📄 فایل Grok",
601
+ file_types=[".csv"],
602
+ elem_classes=["upload-box"]
603
+ )
604
+
605
+ llama_file = gr.File(
606
+ label="📄 فایل Llama-3.1-8B",
607
+ file_types=[".csv"],
608
+ elem_classes=["upload-box"]
609
+ )
610
+
611
+ manual_file = gr.File(
612
+ label="📄 فایل ناشناس‌سازی دستی",
613
+ file_types=[".csv"],
614
+ elem_classes=["upload-box"]
615
+ )
616
+
617
+ load_btn = gr.Button(
618
+ "📂 بارگذاری فایل‌ها",
619
+ variant="primary",
620
+ size="lg"
621
+ )
622
+
623
+ benchmark_btn = gr.Button(
624
+ "🚀 اجرای بنچمارک",
625
+ variant="secondary",
626
+ size="lg",
627
+ interactive=False
628
+ )
629
+
630
+ with gr.Column(scale=2):
631
+ status_output = gr.Markdown("وضعیت: آماده بارگذاری فایل‌ها")
632
+
633
+ results_html = gr.HTML(
634
+ label="📊 نتایج بنچمارک",
635
+ visible=False
636
+ )
637
+
638
+ def load_files(chatgpt, grok, llama, manual):
639
+ if not all([chatgpt, grok, llama, manual]):
640
+ return "❌ لطفاً هر چهار فایل را انتخاب کنید", gr.Button(interactive=False), gr.HTML(visible=False)
641
+
642
+ success, message = benchmark.load_csv_files(
643
+ chatgpt.name, grok.name, llama.name, manual.name
644
+ )
645
+
646
+ if success:
647
+ return (
648
+ f"✅ {message}",
649
+ gr.Button(interactive=True),
650
+ gr.HTML(visible=False)
651
+ )
652
+ else:
653
+ return (
654
+ f"❌ {message}",
655
+ gr.Button(interactive=False),
656
+ gr.HTML(visible=False)
657
+ )
658
+
659
+ def run_benchmark():
660
+ success, message, html_report = benchmark.run_benchmark()
661
+
662
+ if success:
663
+ return (
664
+ f"✅ {message}",
665
+ gr.HTML(value=html_report, visible=True)
666
+ )
667
+ else:
668
+ return (
669
+ f"❌ {message}",
670
+ gr.HTML(visible=False)
671
+ )
672
+
673
+ # اتصال رویدادها
674
+ load_btn.click(
675
+ fn=load_files,
676
+ inputs=[chatgpt_file, grok_file, llama_file, manual_file],
677
+ outputs=[status_output, benchmark_btn, results_html]
678
+ )
679
+
680
+ benchmark_btn.click(
681
+ fn=run_benchmark,
682
+ outputs=[status_output, results_html]
683
+ )
684
+
685
+ # راهنمای استفاده
686
+ with gr.Accordion("📖 راهنمای استفاده", open=False):
687
+ gr.Markdown("""
688
+ ### نحوه استفاده:
689
+ 1. **بارگذاری فایل‌ها:** سه فایل CSV مربوط به نتایج ناشناس‌سازی مدل‌های مختلف را انتخاب کنید
690
+ 2. **بررسی فرمت:** هر فایل باید دارای ستون‌های `original_text` و `anonymized_text` باشد
691
+ 3. **اجرای بنچمارک:** روی دکمه "اجرای بنچمارک" کلیک کنید
692
+ 4. **مشاهده نتایج:** گزارش HTML کامل با جداول و نمودارها نمایش داده م��‌شود
693
+
694
+ ### متریک‌های ارزیابی:
695
+ - **درستی اندیس‌گذاری:** بررسی شروع از 01 و پیوستگی شماره‌ها
696
+ - **ثبات شناسه‌ها:** استفاده مداوم از یک شناسه برای یک موجودیت
697
+ - **حفظ ساختار:** حفظ واژگان مهم و ساختار جمله
698
+ - **پوشش موجودیت‌ها:** درصد موجودیت‌های شناسایی و ناشناس شده
699
+ - **امتیاز کلی:** ترکیب وزنی همه متریک‌ها
700
+ """)
701
+
702
+ return interface
703
+
704
+ # اجرای برنامه
705
+ if __name__ == "__main__":
706
+ interface = create_benchmark_interface()
707
+ interface.launch()