leilaghomashchi commited on
Commit
7944ac7
·
verified ·
1 Parent(s): e46997f

Upload evaluation_tool.py

Browse files
Files changed (1) hide show
  1. evaluation_tool.py +408 -0
evaluation_tool.py ADDED
@@ -0,0 +1,408 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ import re
4
+ from typing import Dict, List, Tuple, Set
5
+ import gradio as gr
6
+ from datetime import datetime
7
+ import io
8
+
9
+ class AnonymizationEvaluator:
10
+ """ابزار ارزیابی ناشناس‌سازی با استفاده از متن مرجع"""
11
+
12
+ def __init__(self):
13
+ self.results_df = None
14
+
15
+ def extract_entities_from_text(self, text: str) -> Dict[str, Set[str]]:
16
+ """استخراج موجودیت‌ها از متن"""
17
+ if pd.isna(text) or not isinstance(text, str):
18
+ return {'companies': set(), 'persons': set(), 'amounts': set(), 'percents': set(), 'groups': set()}
19
+
20
+ entities = {
21
+ 'companies': set(re.findall(r'company-(\d+)', text)),
22
+ 'persons': set(re.findall(r'person-(\d+)', text)),
23
+ 'amounts': set(re.findall(r'amount-(\d+)', text)),
24
+ 'percents': set(re.findall(r'percent-(\d+)', text)),
25
+ 'groups': set(re.findall(r'group-(\d+)', text))
26
+ }
27
+ return entities
28
+
29
+ def calculate_precision_recall_f1(self, reference_entities: Dict[str, Set[str]],
30
+ predicted_entities: Dict[str, Set[str]]) -> Tuple[float, float, float]:
31
+ """محاسبه Precision, Recall و F1-Score"""
32
+
33
+ # ترکیب همه موجودیت‌ها
34
+ ref_all = set()
35
+ pred_all = set()
36
+
37
+ for entity_type in ['companies', 'persons', 'amounts', 'percents', 'groups']:
38
+ # اضافه کردن prefix برای جلوگیری از تداخل
39
+ ref_entities = {f"{entity_type}:{e}" for e in reference_entities.get(entity_type, set())}
40
+ pred_entities = {f"{entity_type}:{e}" for e in predicted_entities.get(entity_type, set())}
41
+
42
+ ref_all.update(ref_entities)
43
+ pred_all.update(pred_entities)
44
+
45
+ if len(pred_all) == 0 and len(ref_all) == 0:
46
+ return 1.0, 1.0, 1.0 # هر دو خالی هستند
47
+ elif len(pred_all) == 0:
48
+ return 0.0, 0.0, 0.0 # predicted خالی ولی reference دارد
49
+ elif len(ref_all) == 0:
50
+ return 0.0, 1.0, 0.0 # reference خالی ولی predicted دارد
51
+
52
+ # محاسبه True Positive
53
+ true_positive = len(ref_all.intersection(pred_all))
54
+
55
+ # محاسبه Precision, Recall
56
+ precision = true_positive / len(pred_all) if len(pred_all) > 0 else 0.0
57
+ recall = true_positive / len(ref_all) if len(ref_all) > 0 else 0.0
58
+
59
+ # محاسبه F1-Score
60
+ if precision + recall == 0:
61
+ f1 = 0.0
62
+ else:
63
+ f1 = 2 * (precision * recall) / (precision + recall)
64
+
65
+ return precision, recall, f1
66
+
67
+ def calculate_accuracy(self, reference_text: str, predicted_text: str) -> float:
68
+ """محاسبه Accuracy بر اساس تطابق کامل موجودیت‌ها"""
69
+ ref_entities = self.extract_entities_from_text(reference_text)
70
+ pred_entities = self.extract_entities_from_text(predicted_text)
71
+
72
+ # شمارش کل موجودیت‌ها
73
+ ref_total = sum(len(entities) for entities in ref_entities.values())
74
+
75
+ if ref_total == 0:
76
+ return 1.0 if sum(len(entities) for entities in pred_entities.values()) == 0 else 0.0
77
+
78
+ # شمارش موجودیت‌های صحیح
79
+ correct = 0
80
+ for entity_type in ref_entities.keys():
81
+ correct += len(ref_entities[entity_type].intersection(pred_entities[entity_type]))
82
+
83
+ return correct / ref_total
84
+
85
+ def evaluate_single_row(self, reference_text: str, predicted_text: str) -> Tuple[float, float, float]:
86
+ """ارزیابی یک سطر"""
87
+ try:
88
+ # استخراج موجودیت‌ها
89
+ ref_entities = self.extract_entities_from_text(reference_text)
90
+ pred_entities = self.extract_entities_from_text(predicted_text)
91
+
92
+ # محاسبه متریک‌ها
93
+ precision, recall, f1 = self.calculate_precision_recall_f1(ref_entities, pred_entities)
94
+
95
+ return precision, recall, f1
96
+
97
+ except Exception as e:
98
+ print(f"خطا در ارزیابی: {str(e)}")
99
+ return 0.0, 0.0, 0.0
100
+
101
+ def evaluate_dataset(self, file_path: str) -> Tuple[bool, str, pd.DataFrame]:
102
+ """ارزیابی کل دیتاست"""
103
+ try:
104
+ # بارگذاری فایل
105
+ df = pd.read_csv(file_path)
106
+
107
+ # بررسی ستون‌های مورد نیاز
108
+ required_columns = ['original_text', 'Reference_text', 'anonymized_text']
109
+ missing_columns = [col for col in required_columns if col not in df.columns]
110
+
111
+ if missing_columns:
112
+ return False, f"ستون‌های مفقود: {', '.join(missing_columns)}", pd.DataFrame()
113
+
114
+ # محاسبه متریک‌ها برای هر سطر
115
+ precisions = []
116
+ recalls = []
117
+ f1_scores = []
118
+
119
+ for index, row in df.iterrows():
120
+ precision, recall, f1 = self.evaluate_single_row(
121
+ row['Reference_text'],
122
+ row['anonymized_text']
123
+ )
124
+
125
+ precisions.append(round(precision, 4))
126
+ recalls.append(round(recall, 4))
127
+ f1_scores.append(round(f1, 4))
128
+
129
+ # اضافه کردن ستون‌های جدید
130
+ df['Precision'] = precisions
131
+ df['Recall'] = recalls
132
+ df['F1_Score'] = f1_scores
133
+
134
+ # ذخیره نتایج
135
+ self.results_df = df
136
+
137
+ return True, "ارزیابی با موفقیت انجام شد", df
138
+
139
+ except Exception as e:
140
+ return False, f"خطا در پردازش فایل: {str(e)}", pd.DataFrame()
141
+
142
+ def generate_summary_report(self, df: pd.DataFrame) -> str:
143
+ """تولید گزارش خلاصه"""
144
+ if df.empty:
145
+ return "هیچ داده‌ای برای گزارش یافت نشد"
146
+
147
+ # محاسبه آمار کلی
148
+ avg_precision = df['Precision'].mean()
149
+ avg_recall = df['Recall'].mean()
150
+ avg_f1 = df['F1_Score'].mean()
151
+
152
+ # محاسبه آمار تفصیلی
153
+ total_rows = len(df)
154
+ high_precision_count = len(df[df['Precision'] >= 0.8])
155
+ high_recall_count = len(df[df['Recall'] >= 0.8])
156
+ high_f1_count = len(df[df['F1_Score'] >= 0.8])
157
+
158
+ # بهترین و بدترین نتایج
159
+ best_f1_idx = df['F1_Score'].idxmax()
160
+ worst_f1_idx = df['F1_Score'].idxmin()
161
+
162
+ report = f"""
163
+ ## 📊 گزارش جامع ارزیابی
164
+
165
+ ### آمار کلی:
166
+ - **تعداد کل سطرها:** {total_rows}
167
+ - **میانگین Precision:** {avg_precision:.4f}
168
+ - **میانگین Recall:** {avg_recall:.4f}
169
+ - **میانگین F1-Score:** {avg_f1:.4f}
170
+
171
+ ### توزیع عملکرد (امتیاز ≥ 0.8):
172
+ - **Precision بالا:** {high_precision_count} سطر ({high_precision_count/total_rows*100:.1f}%)
173
+ - **Recall بالا:** {high_recall_count} سطر ({high_recall_count/total_rows*100:.1f}%)
174
+ - **F1-Score بالا:** {high_f1_count} سطر ({high_f1_count/total_rows*100:.1f}%)
175
+
176
+ ### نمونه‌های برتر و ضعیف:
177
+ - **بهترین F1-Score:** {df.loc[best_f1_idx, 'F1_Score']:.4f} (سطر {best_f1_idx + 1})
178
+ - **ضعیف‌ترین F1-Score:** {df.loc[worst_f1_idx, 'F1_Score']:.4f} (سطر {worst_f1_idx + 1})
179
+ """
180
+
181
+ return report
182
+
183
+ def create_downloadable_csv(self) -> str:
184
+ """ایجاد فایل CSV قابل دانلود"""
185
+ if self.results_df is None or self.results_df.empty:
186
+ return None
187
+
188
+ # تولید نام فایل با زمان
189
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
190
+ filename = f"evaluation_results_{timestamp}.csv"
191
+
192
+ # ذخیره فایل
193
+ self.results_df.to_csv(filename, index=False, encoding='utf-8-sig')
194
+
195
+ return filename
196
+
197
+ def create_evaluation_interface():
198
+ """ایجاد رابط کاربری ارزیابی"""
199
+ evaluator = AnonymizationEvaluator()
200
+
201
+ with gr.Blocks(
202
+ title="ارزیابی ناشناس‌سازی",
203
+ theme=gr.themes.Soft(),
204
+ css="""
205
+ .gradio-container {
206
+ font-family: 'Tahoma', 'Arial', sans-serif !important;
207
+ direction: rtl;
208
+ max-width: 1200px;
209
+ margin: 0 auto;
210
+ }
211
+ .upload-area {
212
+ border: 2px dashed #4CAF50;
213
+ border-radius: 15px;
214
+ padding: 30px;
215
+ text-align: center;
216
+ background: linear-gradient(145deg, #f8f9fa, #e9ecef);
217
+ margin: 20px 0;
218
+ }
219
+ .results-table {
220
+ direction: ltr;
221
+ font-family: monospace;
222
+ font-size: 12px;
223
+ }
224
+ .summary-box {
225
+ background-color: #e3f2fd;
226
+ border: 1px solid #2196F3;
227
+ border-radius: 10px;
228
+ padding: 20px;
229
+ margin: 15px 0;
230
+ }
231
+ """
232
+ ) as interface:
233
+
234
+ gr.Markdown("""
235
+ # 📊 ابزار ارزیابی ناشناس‌سازی با متن مرجع
236
+ ### آپلود فایل CSV شامل ستون‌های: original_text, Reference_text, anonymized_text
237
+ """)
238
+
239
+ with gr.Row():
240
+ with gr.Column(scale=1):
241
+ gr.Markdown("### 📁 بارگذاری فایل")
242
+
243
+ file_input = gr.File(
244
+ label="انتخاب فایل CSV",
245
+ file_types=[".csv"],
246
+ elem_classes=["upload-area"]
247
+ )
248
+
249
+ evaluate_btn = gr.Button(
250
+ "🚀 شروع ارزیابی",
251
+ variant="primary",
252
+ size="lg",
253
+ interactive=False
254
+ )
255
+
256
+ download_btn = gr.Button(
257
+ "💾 دانلود نتایج",
258
+ variant="secondary",
259
+ visible=False
260
+ )
261
+
262
+ with gr.Column(scale=2):
263
+ status_output = gr.Markdown("وضعیت: آماده بارگذاری فایل...")
264
+
265
+ summary_output = gr.Markdown(
266
+ visible=False,
267
+ elem_classes=["summary-box"]
268
+ )
269
+
270
+ # جدول نتایج
271
+ results_table = gr.Dataframe(
272
+ label="نتایج تفصیلی (نمایش اول)",
273
+ visible=False,
274
+ elem_classes=["results-table"],
275
+ height=400,
276
+ wrap=True
277
+ )
278
+
279
+ # فایل دانلود
280
+ download_file = gr.File(
281
+ visible=False,
282
+ label="فایل نتایج"
283
+ )
284
+
285
+ def on_file_upload(file):
286
+ if file is None:
287
+ return "❌ لطفاً فایل را انتخاب کنید", gr.Button(interactive=False)
288
+
289
+ return "✅ فایل بارگذاری شد، آماده ارزیابی", gr.Button(interactive=True)
290
+
291
+ def evaluate_file(file):
292
+ if file is None:
293
+ return (
294
+ "❌ هیچ فایلی انتخاب نشده",
295
+ gr.Markdown(visible=False),
296
+ gr.Dataframe(visible=False),
297
+ gr.Button(visible=False),
298
+ gr.File(visible=False)
299
+ )
300
+
301
+ try:
302
+ success, message, df = evaluator.evaluate_dataset(file.name)
303
+
304
+ if not success:
305
+ return (
306
+ f"❌ {message}",
307
+ gr.Markdown(visible=False),
308
+ gr.Dataframe(visible=False),
309
+ gr.Button(visible=False),
310
+ gr.File(visible=False)
311
+ )
312
+
313
+ # تولید گزارش خلاصه
314
+ summary = evaluator.generate_summary_report(df)
315
+
316
+ # نمایش 10 سطر اول برای نمونه
317
+ display_df = df.head(10)
318
+
319
+ return (
320
+ f"✅ {message} - {len(df)} سطر پردازش شد",
321
+ gr.Markdown(value=summary, visible=True),
322
+ gr.Dataframe(value=display_df, visible=True),
323
+ gr.Button(visible=True),
324
+ gr.File(visible=False)
325
+ )
326
+
327
+ except Exception as e:
328
+ return (
329
+ f"❌ خطای غیرمنتظره: {str(e)}",
330
+ gr.Markdown(visible=False),
331
+ gr.Dataframe(visible=False),
332
+ gr.Button(visible=False),
333
+ gr.File(visible=False)
334
+ )
335
+
336
+ def download_results():
337
+ try:
338
+ filename = evaluator.create_downloadable_csv()
339
+ if filename:
340
+ return (
341
+ "✅ فایل نتایج آماده دانلود است",
342
+ gr.File(value=filename, visible=True)
343
+ )
344
+ else:
345
+ return (
346
+ "❌ خطا در ایجاد فایل دانلود",
347
+ gr.File(visible=False)
348
+ )
349
+ except Exception as e:
350
+ return (
351
+ f"❌ خطا در دانلود: {str(e)}",
352
+ gr.File(visible=False)
353
+ )
354
+
355
+ # اتصال رویدادها
356
+ file_input.change(
357
+ fn=on_file_upload,
358
+ inputs=[file_input],
359
+ outputs=[status_output, evaluate_btn]
360
+ )
361
+
362
+ evaluate_btn.click(
363
+ fn=evaluate_file,
364
+ inputs=[file_input],
365
+ outputs=[status_output, summary_output, results_table, download_btn, download_file]
366
+ )
367
+
368
+ download_btn.click(
369
+ fn=download_results,
370
+ outputs=[status_output, download_file]
371
+ )
372
+
373
+ # راهنمای استفاده
374
+ with gr.Accordion("📖 راهنمای استفاده", open=False):
375
+ gr.Markdown("""
376
+ ### فرمت فایل CSV مورد نیاز:
377
+
378
+ فایل شما باید حاوی دقیقاً این سه ستون باشد:
379
+ - **original_text**: متن اصلی
380
+ - **Reference_text**: متن ناشناس‌شده مرجع (Ground Truth)
381
+ - **anonymized_text**: متن ناشناس‌شده مورد ارزیابی
382
+
383
+ ### متریک‌های محاسبه شده:
384
+
385
+ - **Precision**: دقت = (تعداد موجودیت‌های صحیح شناسایی شده) / (کل موجودیت‌های شناسایی شده)
386
+ - **Recall**: بازیابی = (تعداد موجودیت‌های صحیح شناسایی شده) / (کل موجودیت‌های مرجع)
387
+ - **F1-Score**: میانگین هارمونیک Precision و Recall
388
+
389
+ ### مراحل کار:
390
+
391
+ 1. فایل CSV را آپلود کنید
392
+ 2. روی "شروع ارزیابی" کلیک کنید
393
+ 3. گزارش خلاصه و جدول نتایج را مشاهده کنید
394
+ 4. فایل نتایج کامل را دانلود کنید
395
+
396
+ ### نکات مهم:
397
+
398
+ - فایل خروجی شامل ستون‌های اصلی + سه ستون متریک خواهد بود
399
+ - متریک‌ها برای هر سطر جداگانه محاسبه می‌شوند
400
+ - آمار کلی در گزارش خلاصه نمایش داده می‌شود
401
+ """)
402
+
403
+ return interface
404
+
405
+ # اجرای برنامه
406
+ if __name__ == "__main__":
407
+ interface = create_evaluation_interface()
408
+ interface.launch()