10. Report
Rôle de l’étape
report génère un rapport HTML auto-contenu (Quarto + Python) à la fin du pipeline. Il existe en deux variantes, choisies automatiquement selon la présence ou non d’une vérité terrain.
- Code :
report/— entréereport/main.py - Sortie :
…/{run}/report/report.html - Activé par défaut (
skip-reportvaut"false"dansargo/codif-pipeline.yaml) ; désactivable avec-p skip-report=true.
Détection du mode
report/main.py lit les prédictions de decide-coicop et teste si tous les codes de référence sont nuls :
row = con.sql(
f"SELECT bool_and(code IS NULL) FROM read_parquet('{input_path}')"
).fetchone()
...
qmd = "prediction_report.qmd" if prediction else "report.qmd"| Mode | Condition | Gabarit | Entrée |
|---|---|---|---|
| Évaluation | vérité terrain code présente |
report.qmd |
decide-coicop/predictions.parquet |
| Prédiction | code partout NULL |
prediction_report.qmd |
final-output/{fichier} + decide-coicop/predictions.parquet |
Le CSV BDF est codé sans vérité terrain → c’est le mode prédiction (prediction_report.qmd) qui s’applique.
Mode prédiction (cas du CSV BDF)
Sections réelles du gabarit prediction_report.qmd, et la question à laquelle chacune répond :
| Section du rapport | Question |
|---|---|
| Métadonnées du run | quel run, quel fichier d’entrée, quels modèles, quelles durées d’étapes ? |
| Distribution des prédictions par niveau COICOP | quels postes ressortent, à chaque profondeur de code ? |
| Source des prédictions | combien de lignes codées par regex / consensus / LLM, et avec quelle confiance ? |
| Accord entre les classifieurs | sur combien de lignes les classifieurs amont s’accordaient-ils (et le consensus a-t-il joué) ? |
| Accord de chaque classifieur avec le juge LLM (+ détail par groupe COICOP L2 sur les cas arbitrés) | quel modèle amont le juge suit-il le plus souvent ? |
| Décisions du juge hors des prédictions des classifieurs | sur quelles lignes le LLM a-t-il choisi un code qu’aucun modèle n’avait proposé ? |
Les classifieurs amont sont détectés dynamiquement (jusqu’à 4 : LCS, RAG notices, RAG annotations, TTC) — le bloc RAG-annotations n’est inclus que s’il est présent dans les données.
Le rapport décrit la campagne de codification réalisée, sans calcul d’exactitude :
Répartition par source de prédiction : nombre de lignes codées par
regex, parconsensus, parllm, et nombre d’erreurs LLM (llm_error).n_regex = (df["prediction_source"] == "regex").sum() n_consensus = (df["prediction_source"] == "consensus").sum() n_llm = (df["prediction_source"] == "llm").sum()Distribution par niveau COICOP : comptage des
predicted_codetronqués à chaque niveau, enrichi du libellé (llm_libelle).Calibration : répartition des décisions LLM par score de
llm_confiance(1 à 5).Consensus vs désaccord : pour les cas arbitrés par le LLM, comparaison de
llm_codeaveclcs_code/rag_code/ragann_code/ttc_code_1— mesure l’apport de l’arbitrage par rapport à chaque modèle amont.
Les lignes sans code (prediction_source vide) et les erreurs LLM (llm_error) ressortent ici : leur nombre signale la part de produits non codés automatiquement. Voir l’éventail des valeurs possibles de predicted_code sur la page 9. Final output.
Mode évaluation (jeu annoté)
Quand une vérité terrain est disponible (jeu de test annoté, hors CSV BDF), report.qmd calcule l’exactitude. Ses sections :
- Métadonnées du run (dont la colonne de vérité terrain effectivement scorée) ;
- Accuracy globale par niveau COICOP pour LCS, RAG notices, RAG annotations, TTC et LLM (tableau + graphique), en convention stricte ;
- Accuracy sur toutes les observations, en convention inclusive (voir ci-dessous) ;
- Accuracy par groupe : niveau COICOP 1 et 2,
shop,shop_type_name, décile debudget; - Analyse d’erreurs : matrice de confusion (top 20 paires
codevsllm_codeau niveau 4), calibration (accuracy par tranche dellm_confiance) ; - Accord entre les classifieurs et valeur ajoutée de l’arbitrage (combien de modèles trouvent la vérité, et ce que le juge apporte) ;
- Accord de chaque classifieur avec le juge LLM ;
- Décisions du juge hors des prédictions des classifieurs ;
- Récapitulatif.
Sur quelle vérité terrain le rapport score-t-il ?
Sur code_lvl4, la version canonique de l’annotation produite par decide-coicop — pas sur code brut. Les prédictions vivant dans l’espace de codes pruné, la vérité doit y vivre aussi : sinon une prédiction canonique exacte (01.3 face à une annotation 01.3.0.0.x) est comptée fausse, un biais qui touche ~24 % des Postes de la nomenclature. Les runs antérieurs à l’introduction de code_lvl4 retombent automatiquement sur code ; la colonne réellement utilisée est affichée dans les métadonnées du rapport.
Les deux conventions d’accuracy
Le rapport publie deux accuracies par niveau, qui répondent à deux questions distinctes. Le tableau récapitulatif les met côte à côte, et MLflow reçoit les deux séries (accuracy_<méthode>_niv<k> et accuracy_all_<méthode>_niv<k>).
| Convention stricte | Convention inclusive | |
|---|---|---|
| Dénominateur | seules les observations dont la vérité atteint le niveau k |
toutes les observations disposant d’une vérité terrain |
Vérité moins profonde que k |
exclue (non évaluable) | incluse : la prédiction attendue est le code court lui-même |
Prédiction plus courte que k |
erreur | erreur (sauf si elle égale la vérité) |
| Prédiction plus profonde que la vérité | correcte si le préfixe correspond | erreur : dans l’espace pruné, ce code n’existe pas |
| Question | parmi les observations codables à la profondeur k, quelle part est juste ? |
sur toute la population, quelle part est correctement placée dans la grille de niveau k ? |
| Comparaison entre niveaux | impossible (population variable) | possible (dénominateur constant) |
La convention inclusive est celle qui répond à « quel est mon taux de réussite au niveau 4 ? ». Elle n’a de sens que sur des codes canoniques : une fois les hiérarchies linéaires élaguées, une vérité 01.3 signifie que 01.3 est la réponse de niveau 4 — ce n’est pas une troncature faute de mieux, c’est le code le plus fin qui existe pour ce poste (8 codes sont des feuilles dès le niveau 2). Le rapport affiche aussi, pour chaque niveau, combien d’observations ont une vérité moins profonde que k : c’est la part de la population qui sépare les deux chiffres.
Comme chaque niveau porte sur une population différente, l’accuracy stricte peut augmenter quand k augmente : les observations qui restent sont celles dont la vérité est fine, souvent les plus faciles. Ce n’est pas une amélioration de la codification en profondeur, c’est un effet de sélection. C’est la raison d’être de la convention inclusive.
Une vérité peu profonde a deux causes possibles : le code est un code canonique terminal (la convention inclusive est alors exacte), ou l’annotateur s’est arrêté avant le niveau 4 alors que des codes plus fins existent. Dans ce second cas, une prédiction plus fine est comptée fausse sans qu’on puisse la vérifier. L’accuracy inclusive est donc une borne inférieure, la stricte une mesure sur un sous-ensemble : lire les deux ensemble.
Avec une vérité canonique, le niveau 5 n’est plus affiché : les codes prunés ont au plus 4 segments, la colonne serait structurellement vide.
Rendre le rapport à la main
Les gabarits lisent leurs entrées via des variables d’environnement plutôt que des paramètres Quarto : REPORT_INPUT_PATH (fichier de prédictions), REPORT_DECIDE_PATH (sortie decide-coicop, mode prédiction), REPORT_RUN_ID, REPORT_RUN_DATE. C’est report/main.py qui les positionne avant d’appeler quarto render ; pour itérer sur le gabarit en local, il suffit de les exporter soi-même puis de rendre le .qmd directement.
Traçabilité MLflow
En plus du HTML, l’étape loggue le run dans MLflow (log_to_mlflow() dans report/main.py, expérience codif-coicop-eval par défaut). C’est facultatif et sans effet de bord : si MLFLOW_TRACKING_URI n’est pas défini, l’étape passe l’enregistrement.
Sont enregistrés notamment :
- Paramètres du pipeline pour rejouer/comparer un run :
run_id,run_date,model_name(LLM RAG),decide_model,decide_concurrency,ttc_model_uri(URI du modèle utilisé parrun-ttc),skip_vector_db,truth_column(la colonne de vérité terrain scorée), l’emplacement S3 de sortie, etc. - Tags : mode (
prediction/evaluation), commit et branche git. - Métriques : durées des étapes Argo, nombre d’observations, distribution des codes prédits par niveau ; et en mode évaluation, pour chaque méthode (LCS/RAG/RAG-annot/TTC/LLM) et chaque niveau, les deux conventions —
accuracy_<méthode>_niv<k>(stricte) etaccuracy_all_<méthode>_niv<k>(inclusive) — plustruth_shallower_than_niv<k>_count, qui donne la population séparant les deux. - Artefacts : le
report.html,prediction_distribution.jsonettruth_depth_distribution.json.
Logguer ttc_model_uri permet de savoir, pour chaque run, quel modèle TTC a produit les prédictions — utile pour comparer deux versions du classifieur.
Sortie
| Fichier | Contenu |
|---|---|
…/{run}/report/report.html |
rapport HTML auto-contenu (prédiction ou évaluation) |
Retour à l’accueil