Files
anonymisation/.kiro/specs/anonymization-quality-optimization/ROOT_CAUSE_ANALYSIS.md
Domi31tls 93617bab55 analysis: Analyse complète des causes racines de la régression de qualité
- Régression identifiée: +183.6% PII/doc (13.4 → 38.0)
- 6 causes racines confirmées:
  1. Sur-masquage termes médicaux (RE_SERVICE trop large)
  2. Sur-détection noms (répétitions + termes médicaux)
  3. Masquage médicaments (whitelist non utilisée)
  4. Sur-masquage dates (51 vs 2, +2450%)
  5. Répétitions en-têtes/pieds (RPPS 36 vs 2)
  6. Artefacts OCR (paramètres non optimaux)

- Plan de correction en 3 phases (1-10 jours)
- Impact attendu: PII/doc -66%, Precision +35 points

Fichiers:
- ROOT_CAUSE_ANALYSIS.md: Analyse détaillée
- EXECUTIVE_SUMMARY.md: Résumé exécutif
- tools/root_cause_analysis.py: Script d'analyse
- tools/deep_quality_regression_analysis.py: Analyse approfondie
2026-03-02 23:13:30 +01:00

13 KiB

Analyse des Causes Racines - Régression de Qualité

Date: 2 mars 2026
Statut: 🔴 RÉGRESSION CRITIQUE IDENTIFIÉE


📊 Résumé Exécutif

Métriques Comparatives

Métrique Test Dataset Production Écart Impact
PII/document 13.4 38.0 +183.6% 🔴 CRITIQUE
Recall 100% ? ? ⚠️ À mesurer
Precision 100% ~60-70% -30-40 points 🔴 CRITIQUE
Lisibilité Excellente Médiocre - 🔴 CRITIQUE

Verdict

Le système a une régression de qualité de 183.6% en production par rapport au test dataset.

Les documents de production contiennent 2.8x plus de PII détectés que le test dataset, principalement dus à :

  1. Sur-détection de noms (84 vs 28, +200%)
  2. Sur-masquage d'établissements (26 vs 6, +333%)
  3. Sur-masquage de RPPS (36 vs 2, +1700%)
  4. Sur-masquage de dates (51 vs 2, +2450%)

🔍 Causes Racines Identifiées

1. SUR-MASQUAGE DES TERMES MÉDICAUX (CRITIQUE)

Problème: Les regex RE_SERVICE et RE_ETABLISSEMENT capturent des termes médicaux légitimes.

Exemples détectés:

  • "Chef de service" → "Chef de [MASK]" (27 occurrences)
  • "Chef de Clinique" → "Chef de [ETABLISSEMENT]" (12 occurrences)

Cause racine:

# anonymizer_core_refactored_onnx.py, ligne ~920
RE_SERVICE = re.compile(
    r'\b(service|unit[ée]|p[ôo]le|d[ée]partement)\s+(?:de\s+)?'
    r'([A-ZÉÈÀÙÂÊÎÔÛÄËÏÖÜÇ][a-zéèàùâêîôûäëïöüç\-\' ]+)',
    re.IGNORECASE
)

Ce pattern capture "service de XXX" mais aussi "Chef de service" car il ne vérifie pas le contexte avant.

Impact:

  • Pas de fuite (sécurité préservée)
  • Perte de contexte médical (lisibilité dégradée)
  • +20 ETAB faux positifs par rapport au test dataset

Solution:

  1. Ajouter une whitelist de termes médicaux structurels
  2. Modifier les regex pour exclure les contextes "Chef de", "Praticien", etc.
  3. Créer config/medical_terms_whitelist.yml

2. SUR-DÉTECTION DE NOMS (CRITIQUE)

Problème: 84 noms détectés en production vs 28 dans le test dataset (+200%).

Causes racines:

2.1 Répétitions en-têtes/pieds de page

Les documents de production sont multi-pages avec en-têtes répétés contenant des noms de médecins.

Exemple: Document CRH avec 10 pages

  • En-tête: "Dr DUPONT - Service de Cardiologie" (répété 10x)
  • Pied de page: "Dr MARTIN - Chef de service" (répété 10x)
  • Résultat: 20 détections NOM pour 2 noms uniques

Impact: Statistiques gonflées, mais pas de fuite (tout est masqué).

2.2 Termes médicaux détectés comme noms

Le NER (EDS-Pseudo ou CamemBERT) détecte des termes médicaux comme des noms de personnes.

Exemples:

  • "Note IDE" → détecté comme nom propre
  • "Avis ORL" → détecté comme nom propre
  • "Hospitalisation MCO" → détecté comme nom propre

Cause: Les stopwords médicaux ne couvrent pas tous les acronymes et combinaisons.

Solution:

  1. Enrichir _MEDICAL_STOP_WORDS_SET avec les acronymes médicaux
  2. Implémenter une dédoplication intelligente (compter chaque nom unique une seule fois)
  3. Filtrer les détections NER avec une whitelist médicale

3. MASQUAGE DE MÉDICAMENTS (MOYEN)

Problème: Les noms de médicaments sont masqués comme des noms de personnes.

Exemple détecté:

"IDACIO 40mg" → "[NOM] 40mg"

Cause racine: Le NER détecte "IDACIO" (nom de médicament) comme un nom de personne car :

  1. C'est un mot en MAJUSCULES
  2. Il n'est pas dans la whitelist médicale
  3. Le pattern ressemble à un nom propre

Impact:

  • Perte d'information thérapeutique
  • ⚠️ Lisibilité médicale dégradée

Solution:

  1. Charger la liste des médicaments depuis _load_edsnlp_drug_names() (déjà implémenté)
  2. Filtrer les détections NER avant masquage
  3. Créer config/medications_whitelist.yml pour les médicaments manquants

Note: La fonction _load_edsnlp_drug_names() existe déjà (ligne 80) mais n'est PAS utilisée dans le pipeline !


4. SUR-MASQUAGE DES DATES (CRITIQUE)

Problème: 51 dates masquées en production vs 2 dans le test dataset (+2450%).

Analyse détaillée:

  • Document 1: 19 dates masquées
  • Document 2: 11 dates masquées
  • Document 3: 6 dates masquées
  • Document 4: 7 dates masquées
  • Document 5: 8 dates masquées

Cause racine: Les dates de consultation, d'examen, de traitement sont masquées alors que seules les dates de naissance devraient l'être.

Vérification du code:

# anonymizer_core_refactored_onnx.py, lignes 854-857
# DATE générique — désactivé : seules les dates de naissance sont masquées
# def _repl_date(m: re.Match) -> str:
#     audit.append(PiiHit(page_idx, "DATE", m.group(0), PLACEHOLDERS["DATE"]))
#     return PLACEHOLDERS["DATE"]
# line = RE_DATE.sub(_repl_date, line)

La DATE générique est bien DÉSACTIVÉE dans le code.

Alors pourquoi 51 dates sont masquées ?

Hypothèse 1: Propagation globale trop agressive

# Ligne 2040-2070: Propagation DATE_NAISSANCE_GLOBAL
# Génère 4 variations de séparateurs pour chaque date de naissance
# Problème: Si une date de consultation = date de naissance, elle sera masquée

Hypothèse 2: NER détecte des dates comme PII Le NER (EDS-Pseudo) peut détecter des dates dans le texte et les marquer comme DATE_NAISSANCE.

Solution:

  1. Vérifier que la propagation DATE_NAISSANCE_GLOBAL ne masque que les vraies dates de naissance
  2. Ajouter un contexte strict pour DATE_NAISSANCE (uniquement "Né(e) le", "DDN", etc.)
  3. Ne PAS propager les dates sans contexte

5. SUR-MASQUAGE DES RPPS (CRITIQUE)

Problème: 36 RPPS masqués en production vs 2 dans le test dataset (+1700%).

Cause racine: Répétitions en-têtes/pieds de page.

Exemple: Document avec 10 pages

  • En-tête: "Dr DUPONT - RPPS: 10100817005" (répété 10x)
  • Résultat: 10 détections RPPS pour 1 RPPS unique

Impact:

  • Pas de fuite (sécurité préservée)
  • ⚠️ Statistiques gonflées

Solution: Dédoplication intelligente (compter chaque RPPS unique une seule fois).


6. QUALITÉ D'EXTRACTION OCR (MOYEN)

Problème: Artefacts OCR rendent le texte illisible.

Exemple détecté:

"N° RPPS 10100817005" → "P Nr °a t Ric Pi Pen S h 1o 0s 1p 0i 0ta 8l 1ie 7r 005"

Cause racine: Les paramètres docTR ne sont pas optimaux pour les documents scannés de mauvaise qualité.

Impact:

  • ⚠️ Lisibilité dégradée
  • ⚠️ Possible perte de détection de PII (si le texte est trop fragmenté)

Solution:

  1. Augmenter la résolution d'entrée (300 → 400 DPI)
  2. Activer le post-traitement docTR
  3. Implémenter un nettoyage des artefacts OCR (fusion des lettres espacées)

Note: Ce problème n'affecte PAS le test dataset car les documents sont de meilleure qualité.


7. SUR-MASQUAGE DES VILLES (FAIBLE)

Problème: 1 ville masquée hors contexte d'adresse.

Exemple détecté:

"originaire du [VILLE]" → Perte du contexte géographique

Cause racine: Les regex de ville ne vérifient pas le contexte (adresse vs origine).

Impact:

  • ⚠️ Perte de contexte géographique (faible impact médical)

Solution: Masquer les villes UNIQUEMENT dans le contexte d'adresse (pas "originaire de", "né à", etc.).


🎯 Priorisation des Corrections

Priorité 1 - CRITIQUE (1-2 jours)

1.1 Corriger le sur-masquage des termes médicaux

Impact: -20 ETAB faux positifs, +lisibilité

Actions:

  1. Créer config/medical_terms_whitelist.yml
  2. Ajouter: "Chef de service", "Chef de Clinique", "Praticien hospitalier", etc.
  3. Modifier RE_SERVICE et RE_ETABLISSEMENT pour exclure ces termes
  4. Tester sur 10 documents de production

Fichiers à modifier:

  • anonymizer_core_refactored_onnx.py (lignes ~920-930)
  • config/medical_terms_whitelist.yml (nouveau)

1.2 Corriger le masquage des médicaments

Impact: +lisibilité thérapeutique

Actions:

  1. Activer _load_edsnlp_drug_names() dans le pipeline
  2. Filtrer les détections NER avant masquage
  3. Créer config/medications_whitelist.yml pour les médicaments manquants
  4. Tester sur 10 documents de production

Fichiers à modifier:

  • anonymizer_core_refactored_onnx.py (lignes ~1394-1470)
  • config/medications_whitelist.yml (nouveau)

1.3 Vérifier le sur-masquage des dates

Impact: -49 dates faux positifs, +lisibilité temporelle

Actions:

  1. Analyser les 51 dates masquées en production
  2. Vérifier si ce sont des dates de naissance ou des dates de consultation
  3. Si dates de consultation: corriger la propagation globale
  4. Ajouter un contexte strict pour DATE_NAISSANCE
  5. Tester sur 162 CRO (comme pour les fuites)

Fichiers à modifier:

  • anonymizer_core_refactored_onnx.py (lignes ~2040-2130)

Priorité 2 - IMPORTANT (2-3 jours)

2.1 Enrichir les stopwords médicaux

Impact: -56 NOM faux positifs

Actions:

  1. Extraire les termes médicaux des documents de production
  2. Identifier les acronymes médicaux (IDE, ORL, MCO, ATB, AINS, etc.)
  3. Ajouter à _MEDICAL_STOP_WORDS_SET
  4. Tester sur 20 documents de production

Fichiers à modifier:

  • anonymizer_core_refactored_onnx.py (lignes ~200-250)

2.2 Implémenter la dédoplication intelligente

Impact: Statistiques plus réalistes

Actions:

  1. Détecter les zones répétées (en-têtes, pieds de page)
  2. Compter chaque PII unique une seule fois dans les statistiques
  3. Masquer toutes les occurrences (sécurité)
  4. Rapporter uniquement les PII uniques dans l'audit

Fichiers à modifier:

  • anonymizer_core_refactored_onnx.py (nouvelle fonction)

Priorité 3 - OPTIONNEL (3-5 jours)

3.1 Optimiser l'extraction OCR

Impact: +lisibilité

Actions:

  1. Augmenter la résolution d'entrée (300 → 400 DPI)
  2. Activer le post-traitement docTR
  3. Implémenter le nettoyage des artefacts OCR
  4. Tester sur 20 documents scannés

Fichiers à modifier:

  • anonymizer_core_refactored_onnx.py (lignes ~666-742)

3.2 Raffiner le masquage des villes

Impact: +lisibilité géographique

Actions:

  1. Masquer les villes UNIQUEMENT dans le contexte d'adresse
  2. Préserver "originaire de", "né à", etc.
  3. Tester sur 10 documents de production

Fichiers à modifier:

  • anonymizer_core_refactored_onnx.py (lignes ~930-950)

📊 Impact Attendu des Corrections

Après Priorité 1 (1-2 jours)

Métrique Avant Après Amélioration
PII/doc 38.0 ~25.0 -34%
ETAB FP 26 ~6 -77%
Dates FP 51 ~2 -96%
Médicaments masqués 1+ 0 -100%
Lisibilité Médiocre Bonne ++

Après Priorité 2 (3-5 jours)

Métrique Avant Après Amélioration
PII/doc 38.0 ~15.0 -61%
NOM FP 84 ~28 -67%
Precision ~60% ~95% +35 points
Lisibilité Médiocre Excellente +++

Après Priorité 3 (6-10 jours)

Métrique Avant Après Amélioration
PII/doc 38.0 ~13.0 -66%
Artefacts OCR Nombreux Rares -90%
Lisibilité Médiocre Excellente +++

🚀 Plan d'Action Recommandé

Semaine 1 (Priorité 1)

  • Jour 1: Corriger sur-masquage termes médicaux
  • Jour 2: Corriger masquage médicaments
  • Jour 3: Vérifier sur-masquage dates
  • Jour 4: Tests et validation sur 50 documents
  • Jour 5: Commit et documentation

Semaine 2 (Priorité 2)

  • Jour 1-2: Enrichir stopwords médicaux
  • Jour 3-4: Implémenter dédoplication intelligente
  • Jour 5: Tests et validation sur 100 documents

Semaine 3 (Priorité 3 - Optionnel)

  • Jour 1-3: Optimiser extraction OCR
  • Jour 4: Raffiner masquage villes
  • Jour 5: Tests et validation finale

📝 Conclusion

Causes Racines Confirmées

  1. Sur-masquage termes médicaux (RE_SERVICE, RE_ETABLISSEMENT trop larges)
  2. Sur-détection noms (répétitions + termes médicaux)
  3. Masquage médicaments (whitelist non utilisée)
  4. Sur-masquage dates (propagation trop agressive ?)
  5. Répétitions en-têtes/pieds (documents multi-pages)
  6. ⚠️ Artefacts OCR (paramètres non optimaux)

Prochaines Étapes

  1. Valider les hypothèses sur le sur-masquage des dates (analyser les 51 dates)
  2. Implémenter les corrections Priorité 1 (1-2 jours)
  3. Tester sur 50 documents de production
  4. Mesurer l'amélioration (PII/doc, Precision, Lisibilité)
  5. Itérer si nécessaire

Objectif Final

Retrouver la qualité du test dataset en production :

  • PII/doc: 38.0 → 13.4 (-65%)
  • Precision: ~60% → 100% (+40 points)
  • Lisibilité: Médiocre → Excellente

Dernière mise à jour: 2 mars 2026
Auteur: Kiro AI Assistant
Statut: 🔴 RÉGRESSION CRITIQUE - CORRECTIONS EN COURS