leilaghomashchi commited on
Commit
989a66d
·
verified ·
1 Parent(s): 4bc5097

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +129 -204
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
- 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
  """کلاس اصلی بنچمارک ناشناس‌سازی"""
@@ -91,98 +82,50 @@ class AnonymizationBenchmark:
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
  """تحلیل یک مدل"""
@@ -190,63 +133,32 @@ class AnonymizationBenchmark:
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]:
@@ -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
- 'درستی اندیس (%)': 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 جدول
@@ -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
- 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
 
@@ -516,61 +443,59 @@ class AnonymizationBenchmark:
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
  """
 
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
  """