Update app.py
Browse files
app.py
CHANGED
|
@@ -15,19 +15,10 @@ 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 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
entity_coverage_rate: float
|
| 30 |
-
quality_score: float
|
| 31 |
|
| 32 |
class AnonymizationBenchmark:
|
| 33 |
"""کلاس اصلی بنچمارک ناشناسسازی"""
|
|
@@ -91,98 +82,50 @@ class AnonymizationBenchmark:
|
|
| 91 |
|
| 92 |
return max(total_entities, 1) # حداقل 1 برای جلوگیری از تقسیم بر صفر
|
| 93 |
|
| 94 |
-
def
|
| 95 |
-
"""
|
| 96 |
-
|
| 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 |
-
|
| 155 |
-
anonymized_words = len(anonymized_text.split())
|
| 156 |
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
score += word_ratio * 2 # وزن بیشتر برای حفظ تعداد کلمات
|
| 160 |
-
total_checks += 2
|
| 161 |
|
| 162 |
-
|
|
|
|
|
|
|
| 163 |
|
| 164 |
-
def
|
| 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
|
| 173 |
-
"""محاسبه
|
| 174 |
-
|
| 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 |
-
|
| 182 |
-
for
|
| 183 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
|
| 185 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
|
| 187 |
def analyze_model(self, model_name: str, df: pd.DataFrame) -> BenchmarkMetrics:
|
| 188 |
"""تحلیل یک مدل"""
|
|
@@ -190,63 +133,32 @@ class AnonymizationBenchmark:
|
|
| 190 |
|
| 191 |
total_texts = len(df)
|
| 192 |
|
| 193 |
-
# محاسبه
|
| 194 |
-
|
| 195 |
-
|
| 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 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 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 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 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]:
|
|
@@ -282,19 +194,10 @@ class AnonymizationBenchmark:
|
|
| 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 |
-
'
|
| 294 |
-
'
|
| 295 |
-
'
|
| 296 |
-
'پوشش موجودیت (%)': f"{metrics.entity_coverage_rate*100:.1f}",
|
| 297 |
-
'🏆 امتیاز کلی': f"{metrics.quality_score:.3f}"
|
| 298 |
})
|
| 299 |
|
| 300 |
# تولید HTML جدول
|
|
@@ -318,8 +221,8 @@ class AnonymizationBenchmark:
|
|
| 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" # سبز
|
|
@@ -343,24 +246,48 @@ class AnonymizationBenchmark:
|
|
| 343 |
return ""
|
| 344 |
|
| 345 |
models = list(self.benchmark_results.keys())
|
| 346 |
-
|
|
|
|
|
|
|
| 347 |
|
| 348 |
-
# نمودار
|
| 349 |
chart_html = """
|
| 350 |
<div style="margin: 20px 0;">
|
| 351 |
-
<h3 style="text-align: center; color: #333;">مقایسه
|
| 352 |
-
<div style="display: flex; justify-content: center;
|
| 353 |
"""
|
| 354 |
|
| 355 |
colors = ['#4CAF50', '#2196F3', '#FF9800']
|
| 356 |
-
|
| 357 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 358 |
chart_html += f"""
|
| 359 |
-
<div style="text-align: center;">
|
| 360 |
-
<
|
| 361 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 362 |
</div>
|
| 363 |
-
<div style="font-weight: bold; color: #333;">{model}</div>
|
| 364 |
</div>
|
| 365 |
"""
|
| 366 |
|
|
@@ -516,61 +443,59 @@ class AnonymizationBenchmark:
|
|
| 516 |
if not self.benchmark_results:
|
| 517 |
return "<p>دادهای برای تحلیل یافت نشد</p>"
|
| 518 |
|
| 519 |
-
# یافتن بهترین مدل
|
| 520 |
-
|
| 521 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 522 |
|
| 523 |
-
|
|
|
|
|
|
|
|
|
|
| 524 |
|
| 525 |
analysis = f"""
|
| 526 |
<div class="metrics-grid">
|
| 527 |
<div class="metric-card">
|
| 528 |
-
<div class="metric-number"
|
| 529 |
-
<div class="metric-label">بهترین
|
|
|
|
|
|
|
|
|
|
| 530 |
</div>
|
| 531 |
<div class="metric-card">
|
| 532 |
-
<div class="metric-number"
|
| 533 |
-
<div class="metric-label"
|
|
|
|
|
|
|
|
|
|
| 534 |
</div>
|
| 535 |
<div class="metric-card">
|
| 536 |
-
<div class="metric-number"
|
| 537 |
-
<div class="metric-label"
|
|
|
|
|
|
|
|
|
|
| 538 |
</div>
|
| 539 |
</div>
|
| 540 |
|
| 541 |
-
<div style="background-color: #
|
| 542 |
-
<h4
|
| 543 |
<ul style="margin-top: 10px; padding-right: 20px;">
|
| 544 |
-
|
| 545 |
-
|
| 546 |
-
|
| 547 |
-
|
| 548 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 574 |
</ul>
|
| 575 |
</div>
|
| 576 |
"""
|
|
|
|
| 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 |
"""کلاس اصلی بنچمارک ناشناسسازی"""
|
|
|
|
| 82 |
|
| 83 |
return max(total_entities, 1) # حداقل 1 برای جلوگیری از تقسیم بر صفر
|
| 84 |
|
| 85 |
+
def calculate_accuracy(self, original_text: str, anonymized_text: str) -> float:
|
| 86 |
+
"""محاسبه درستی کلی ناشناسسازی"""
|
| 87 |
+
entities = self.extract_entities_from_text(anonymized_text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
+
# بررسی درستی اندیسگذاری
|
| 90 |
+
indexing_score = self.check_indexing_correctness(entities)
|
|
|
|
| 91 |
|
| 92 |
+
# بررسی حفظ ساختار
|
| 93 |
+
structure_score = self.calculate_structure_preservation(original_text, anonymized_text)
|
|
|
|
|
|
|
| 94 |
|
| 95 |
+
# میانگین وزنی
|
| 96 |
+
accuracy = (indexing_score * 0.6) + (structure_score * 0.4)
|
| 97 |
+
return accuracy
|
| 98 |
|
| 99 |
+
def calculate_recall(self, original_text: str, anonymized_text: str) -> float:
|
| 100 |
+
"""محاسبه پوشش موجودیتها (Recall)"""
|
| 101 |
original_entity_count = self.count_original_entities(original_text)
|
| 102 |
entities = self.extract_entities_from_text(anonymized_text)
|
| 103 |
anonymized_entity_count = sum(len(set(v)) for v in entities.values())
|
| 104 |
|
| 105 |
return min(anonymized_entity_count / original_entity_count, 1.0)
|
| 106 |
|
| 107 |
+
def calculate_precision(self, anonymized_text: str) -> float:
|
| 108 |
+
"""محاسبه دقت شناسایی (Precision)"""
|
| 109 |
+
entities = self.extract_entities_from_text(anonymized_text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
+
# بررسی کیفیت موجودیتهای شناسایی شده
|
| 112 |
+
total_entities = sum(len(v) for v in entities.values())
|
| 113 |
+
if total_entities == 0:
|
| 114 |
+
return 0.0
|
| 115 |
+
|
| 116 |
+
# بررسی درستی فرمت شناسهها
|
| 117 |
+
correct_entities = 0
|
| 118 |
+
for entity_type, indices in entities.items():
|
| 119 |
+
for idx in indices:
|
| 120 |
+
if idx.isdigit() and int(idx) > 0:
|
| 121 |
+
correct_entities += 1
|
| 122 |
|
| 123 |
+
# بررسی عدم تکرار غیرضروری
|
| 124 |
+
unique_entities = sum(len(set(v)) for v in entities.values())
|
| 125 |
+
consistency_bonus = unique_entities / total_entities if total_entities > 0 else 0
|
| 126 |
+
|
| 127 |
+
base_precision = correct_entities / total_entities if total_entities > 0 else 0
|
| 128 |
+
return (base_precision * 0.7) + (consistency_bonus * 0.3)
|
| 129 |
|
| 130 |
def analyze_model(self, model_name: str, df: pd.DataFrame) -> BenchmarkMetrics:
|
| 131 |
"""تحلیل یک مدل"""
|
|
|
|
| 133 |
|
| 134 |
total_texts = len(df)
|
| 135 |
|
| 136 |
+
# محاسبه متریکها برای هر متن
|
| 137 |
+
accuracy_scores = []
|
| 138 |
+
recall_scores = []
|
| 139 |
+
precision_scores = []
|
| 140 |
+
total_entities = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
|
| 142 |
for _, row in df.iterrows():
|
| 143 |
original = str(row['original_text'])
|
| 144 |
anonymized = str(row['anonymized_text'])
|
| 145 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
# محاسبه متریکها
|
| 147 |
+
accuracy_scores.append(self.calculate_accuracy(original, anonymized))
|
| 148 |
+
recall_scores.append(self.calculate_recall(original, anonymized))
|
| 149 |
+
precision_scores.append(self.calculate_precision(anonymized))
|
| 150 |
+
|
| 151 |
+
# شمارش موجودیتها
|
| 152 |
+
entities = self.extract_entities_from_text(anonymized)
|
| 153 |
+
total_entities += sum(len(set(v)) for v in entities.values())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
|
| 155 |
return BenchmarkMetrics(
|
| 156 |
model_name=model_name,
|
| 157 |
total_texts=total_texts,
|
| 158 |
+
total_entities=total_entities,
|
| 159 |
+
accuracy=round(np.mean(accuracy_scores), 3),
|
| 160 |
+
recall=round(np.mean(recall_scores), 3),
|
| 161 |
+
precision=round(np.mean(precision_scores), 3)
|
|
|
|
|
|
|
| 162 |
)
|
| 163 |
|
| 164 |
def run_benchmark(self) -> Tuple[bool, str, str]:
|
|
|
|
| 194 |
table_data.append({
|
| 195 |
'مدل': model_name,
|
| 196 |
'تعداد متنها': metrics.total_texts,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
'کل موجودیتها': metrics.total_entities,
|
| 198 |
+
'🎯 دقت (Accuracy)': f"{metrics.accuracy:.3f}",
|
| 199 |
+
'📊 بازیابی (Recall)': f"{metrics.recall:.3f}",
|
| 200 |
+
'✅ دقت شناسایی (Precision)': f"{metrics.precision:.3f}"
|
|
|
|
|
|
|
| 201 |
})
|
| 202 |
|
| 203 |
# تولید HTML جدول
|
|
|
|
| 221 |
html += f"<tr style='background-color: {bg_color};'>"
|
| 222 |
|
| 223 |
for j, (key, value) in enumerate(row.items()):
|
| 224 |
+
# رنگبندی ستونهای متریکها
|
| 225 |
+
if key in ['🎯 دقت (Accuracy)', '📊 بازیابی (Recall)', '✅ دقت شناسایی (Precision)']:
|
| 226 |
score = float(value)
|
| 227 |
if score >= 0.8:
|
| 228 |
color = "#4CAF50" # سبز
|
|
|
|
| 246 |
return ""
|
| 247 |
|
| 248 |
models = list(self.benchmark_results.keys())
|
| 249 |
+
accuracy_scores = [self.benchmark_results[model].accuracy for model in models]
|
| 250 |
+
recall_scores = [self.benchmark_results[model].recall for model in models]
|
| 251 |
+
precision_scores = [self.benchmark_results[model].precision for model in models]
|
| 252 |
|
| 253 |
+
# نمودار مقایسه سه متریک
|
| 254 |
chart_html = """
|
| 255 |
<div style="margin: 20px 0;">
|
| 256 |
+
<h3 style="text-align: center; color: #333;">مقایسه متریکهای عملکرد مدلها</h3>
|
| 257 |
+
<div style="display: flex; justify-content: center; gap: 40px; background-color: #f9f9f9; padding: 30px; border-radius: 15px;">
|
| 258 |
"""
|
| 259 |
|
| 260 |
colors = ['#4CAF50', '#2196F3', '#FF9800']
|
| 261 |
+
|
| 262 |
+
for i, model in enumerate(models):
|
| 263 |
+
accuracy = accuracy_scores[i]
|
| 264 |
+
recall = recall_scores[i]
|
| 265 |
+
precision = precision_scores[i]
|
| 266 |
+
|
| 267 |
chart_html += f"""
|
| 268 |
+
<div style="text-align: center; min-width: 200px;">
|
| 269 |
+
<h4 style="margin-bottom: 15px; color: #333;">{model}</h4>
|
| 270 |
+
|
| 271 |
+
<div style="margin-bottom: 10px;">
|
| 272 |
+
<div style="font-size: 12px; color: #666; margin-bottom: 5px;">Accuracy</div>
|
| 273 |
+
<div style="background-color: {colors[0]}; width: 60px; height: {accuracy*100}px; margin: 0 auto; border-radius: 3px; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 10px;">
|
| 274 |
+
{accuracy:.3f}
|
| 275 |
+
</div>
|
| 276 |
+
</div>
|
| 277 |
+
|
| 278 |
+
<div style="margin-bottom: 10px;">
|
| 279 |
+
<div style="font-size: 12px; color: #666; margin-bottom: 5px;">Recall</div>
|
| 280 |
+
<div style="background-color: {colors[1]}; width: 60px; height: {recall*100}px; margin: 0 auto; border-radius: 3px; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 10px;">
|
| 281 |
+
{recall:.3f}
|
| 282 |
+
</div>
|
| 283 |
+
</div>
|
| 284 |
+
|
| 285 |
+
<div style="margin-bottom: 10px;">
|
| 286 |
+
<div style="font-size: 12px; color: #666; margin-bottom: 5px;">Precision</div>
|
| 287 |
+
<div style="background-color: {colors[2]}; width: 60px; height: {precision*100}px; margin: 0 auto; border-radius: 3px; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 10px;">
|
| 288 |
+
{precision:.3f}
|
| 289 |
+
</div>
|
| 290 |
</div>
|
|
|
|
| 291 |
</div>
|
| 292 |
"""
|
| 293 |
|
|
|
|
| 443 |
if not self.benchmark_results:
|
| 444 |
return "<p>دادهای برای تحلیل یافت نشد</p>"
|
| 445 |
|
| 446 |
+
# یافتن بهترین مدل در هر متریک
|
| 447 |
+
best_accuracy = max(self.benchmark_results.keys(),
|
| 448 |
+
key=lambda k: self.benchmark_results[k].accuracy)
|
| 449 |
+
best_recall = max(self.benchmark_results.keys(),
|
| 450 |
+
key=lambda k: self.benchmark_results[k].recall)
|
| 451 |
+
best_precision = max(self.benchmark_results.keys(),
|
| 452 |
+
key=lambda k: self.benchmark_results[k].precision)
|
| 453 |
|
| 454 |
+
# محاسبه میانگین
|
| 455 |
+
avg_accuracy = np.mean([m.accuracy for m in self.benchmark_results.values()])
|
| 456 |
+
avg_recall = np.mean([m.recall for m in self.benchmark_results.values()])
|
| 457 |
+
avg_precision = np.mean([m.precision for m in self.benchmark_results.values()])
|
| 458 |
|
| 459 |
analysis = f"""
|
| 460 |
<div class="metrics-grid">
|
| 461 |
<div class="metric-card">
|
| 462 |
+
<div class="metric-number">🎯</div>
|
| 463 |
+
<div class="metric-label">بهترین Accuracy: {best_accuracy}</div>
|
| 464 |
+
<div style="color: #4CAF50; font-weight: bold;">
|
| 465 |
+
{self.benchmark_results[best_accuracy].accuracy:.3f}
|
| 466 |
+
</div>
|
| 467 |
</div>
|
| 468 |
<div class="metric-card">
|
| 469 |
+
<div class="metric-number">📊</div>
|
| 470 |
+
<div class="metric-label">بهترین Recall: {best_recall}</div>
|
| 471 |
+
<div style="color: #2196F3; font-weight: bold;">
|
| 472 |
+
{self.benchmark_results[best_recall].recall:.3f}
|
| 473 |
+
</div>
|
| 474 |
</div>
|
| 475 |
<div class="metric-card">
|
| 476 |
+
<div class="metric-number">✅</div>
|
| 477 |
+
<div class="metric-label">بهترین Precision: {best_precision}</div>
|
| 478 |
+
<div style="color: #FF9800; font-weight: bold;">
|
| 479 |
+
{self.benchmark_results[best_precision].precision:.3f}
|
| 480 |
+
</div>
|
| 481 |
</div>
|
| 482 |
</div>
|
| 483 |
|
| 484 |
+
<div style="background-color: #e3f2fd; border: 1px solid #2196F3; border-radius: 8px; padding: 20px; margin-top: 20px;">
|
| 485 |
+
<h4>📈 آمار کلی:</h4>
|
| 486 |
<ul style="margin-top: 10px; padding-right: 20px;">
|
| 487 |
+
<li><strong>میانگین Accuracy:</strong> {avg_accuracy:.3f}</li>
|
| 488 |
+
<li><strong>میانگین Recall:</strong> {avg_recall:.3f}</li>
|
| 489 |
+
<li><strong>میانگین Precision:</strong> {avg_precision:.3f}</li>
|
| 490 |
+
</ul>
|
| 491 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 492 |
|
| 493 |
+
<div style="background-color: #fff3cd; border: 1px solid #ffeaa7; border-radius: 8px; padding: 20px; margin-top: 20px;">
|
| 494 |
+
<h4>💡 تفسیر نتایج:</h4>
|
| 495 |
+
<ul style="margin-top: 10px; padding-right: 20px;">
|
| 496 |
+
<li><strong>Accuracy:</strong> دقت کلی ناشناسسازی (ترکیب اندیسگذاری صحیح و حفظ ساختار)</li>
|
| 497 |
+
<li><strong>Recall:</strong> پوشش موجودیتها (چه درصدی از موجودیتها شناسایی شدند)</li>
|
| 498 |
+
<li><strong>Precision:</strong> دقت شناسایی (چه درصدی از شناسهها صحیح هستند)</li>
|
| 499 |
</ul>
|
| 500 |
</div>
|
| 501 |
"""
|