leilaghomashchi commited on
Commit
a8a1e42
·
verified ·
1 Parent(s): 2e7fc4a

Upload anonymization_benchmark (3).py

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