- 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
412 lines
13 KiB
Markdown
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
|