feat: Filtre hospitalier pour éliminer les faux positifs
- Ajout config/hospital_stopwords.yml avec adresses/téléphones hôpitaux - Ajout detectors/hospital_filter.py pour filtrer les FP - Intégration dans anonymizer_core_refactored_onnx.py - Test sur document: 40 -> 32 détections (-8 FP) - Élimine: adresses hôpitaux, codes postaux CEDEX, épisodes dans noms de fichiers
This commit is contained in:
87
tools/analyze_false_positives.py
Executable file
87
tools/analyze_false_positives.py
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Analyse détaillée des faux positifs pour identifier les patterns problématiques.
|
||||
"""
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from collections import defaultdict
|
||||
import sys
|
||||
|
||||
def analyze_false_positives():
|
||||
"""Analyse les faux positifs par type et identifie les patterns."""
|
||||
|
||||
# Charger les résultats d'évaluation
|
||||
eval_file = Path("tests/ground_truth/quality_evaluation/baseline_quality_evaluation.json")
|
||||
if not eval_file.exists():
|
||||
print(f"❌ Fichier non trouvé: {eval_file}")
|
||||
return
|
||||
|
||||
with open(eval_file, 'r', encoding='utf-8') as f:
|
||||
eval_data = json.load(f)
|
||||
|
||||
# Charger les fichiers audit pour analyser les FP
|
||||
audit_dir = Path("tests/ground_truth/pdfs/baseline_anonymized")
|
||||
|
||||
fp_examples = defaultdict(list)
|
||||
|
||||
# Parcourir les fichiers audit
|
||||
for audit_file in audit_dir.glob("*.audit.jsonl"):
|
||||
with open(audit_file, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
detection = json.loads(line)
|
||||
pii_type = detection.get('type', 'UNKNOWN')
|
||||
text = detection.get('text', '')
|
||||
|
||||
# Collecter des exemples de chaque type
|
||||
if len(fp_examples[pii_type]) < 20: # Limiter à 20 exemples par type
|
||||
fp_examples[pii_type].append({
|
||||
'text': text,
|
||||
'file': audit_file.stem.replace('.audit', ''),
|
||||
'page': detection.get('page', 0)
|
||||
})
|
||||
|
||||
# Afficher l'analyse
|
||||
print("=" * 80)
|
||||
print("ANALYSE DES FAUX POSITIFS")
|
||||
print("=" * 80)
|
||||
print()
|
||||
|
||||
# Focus sur les types problématiques
|
||||
problematic_types = ['EPISODE', 'VILLE', 'CODE_POSTAL', 'ADRESSE', 'TEL']
|
||||
|
||||
for pii_type in problematic_types:
|
||||
type_metrics = eval_data['by_type'].get(pii_type, {})
|
||||
fp_count = type_metrics.get('false_positives', 0)
|
||||
precision = type_metrics.get('precision', 0)
|
||||
|
||||
if fp_count == 0:
|
||||
continue
|
||||
|
||||
print(f"\n{'=' * 80}")
|
||||
print(f"Type: {pii_type}")
|
||||
print(f"Faux positifs: {fp_count}")
|
||||
print(f"Précision: {precision:.2%}")
|
||||
print(f"{'=' * 80}")
|
||||
|
||||
examples = fp_examples.get(pii_type, [])
|
||||
if examples:
|
||||
print(f"\nExemples de détections (premiers 20):")
|
||||
for i, ex in enumerate(examples[:20], 1):
|
||||
print(f" {i:2d}. '{ex['text']}' (page {ex['page']})")
|
||||
else:
|
||||
print("\n⚠️ Aucun exemple trouvé dans les fichiers audit")
|
||||
|
||||
# Statistiques globales
|
||||
print(f"\n{'=' * 80}")
|
||||
print("STATISTIQUES GLOBALES")
|
||||
print(f"{'=' * 80}")
|
||||
global_metrics = eval_data['global_metrics']
|
||||
print(f"Précision: {global_metrics['precision']:.2%}")
|
||||
print(f"Rappel: {global_metrics['recall']:.2%}")
|
||||
print(f"F1-Score: {global_metrics['f1_score']:.2%}")
|
||||
print(f"Faux positifs totaux: {global_metrics['false_positives']}")
|
||||
print()
|
||||
|
||||
if __name__ == "__main__":
|
||||
analyze_false_positives()
|
||||
Reference in New Issue
Block a user