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

412 lines
13 KiB
Markdown

# 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**:
```python
# 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**:
```python
# 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
```python
# 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