Le cas de la transition vers la NAF 2025 pour Sirene
07/2026
Ampleur du changement
La difficulté vient des cas non bijectifs !
Pour un code multivoque, la table de correspondance ne suffit pas : il faut la description de l’activité et un jugement.
| Multivoque | Univoque | |
|---|---|---|
| Table de correspondance | 25 % | 75 % |
| Jeu d’entraînement réel | 52 % | 48 % |
« Quel code parmi les 732 ? »
→ Sélection contrainte (Context-Augmented Generation, CAG)
CAG : candidats issus de la table de correspondance (déterministe). RAG : candidats issus d’une recherche sémantique dans une base vectorielle. Squelette de prompt identique.
| Source | Contenu | Rôle | Notes |
|---|---|---|---|
| Extraction Sirene 4 | Libellé d’activité + variables additionnelles | Décrire l’entreprise | Initialement mars à octobre 2024 |
Code NAF 2008 (apet2008) |
Code en NAF 2008 | Restreindre les candidats | Labellisation par modèle mais considérée comme très fiable |
| Table de correspondance | NAF 2008 → {NAF 2025 admissibles} | Liste fermée de candidats | |
| Notes explicatives | Définitions officielles (comprend / ne comprend pas) | Départager les candidats |
| Colonne | Signification | Usage LLM |
|---|---|---|
liasse_numero |
Identifiant unique de la liasse | clé (jointures, dédup) |
apet2008 |
Code APE / NAF rév. 2 (0111Z) |
candidats |
libelle |
Description libre de l’activité | signal principal |
activ_sec_agri_et |
Précisions activité agricole | précision |
activ_nat_et_libelle |
Autre nature d’activité | précision |
cj_libelle |
Catégorie juridique (libellé) | précision |
activ_surf_et_libelle |
Surface de vente (m²) | précision |
Table de correspondance (officielle)
NAF 2008 (sous-classe) → NAF 2025 (sous-classes admissibles)apet2008, l’ensemble fermé de codes 2025 possiblesNotes explicatives (par code 2025)
comprend — exemples inclusne comprend pas — exclusionsnote générale — définition→ le même matériel de référence qu’un expert humain.
Campagne d’annotation NACE 2025
Ce qu’on en apprend
Pour chaque observation, on assemble 4 « emplacements » :
| Emplacement | Contenu | Provenance |
|---|---|---|
activity |
libellé + précisions | Sirene 4 |
nace_old |
01.11Z : Culture de céréales… |
table (code 2008 + intitulé) |
proposed_codes |
candidats 2025 + notes explicatives | table + notes |
list_proposed_codes |
'01.11Z', '01.19Z', … |
liste fermée de choix |
Prompt système — identique pour toutes les lignes
Tu es un expert de la NACE, chargé du changement de nomenclature. Attribue un code NACE 2025 à partir du descriptif d’activité et d’une liste de candidats issue du code NACE 2008 existant…
null si l’info est insuffisantePrompt utilisateur — spécifique à la ligne
# Activité principale :
{activity}
# Ancien code NACE 2008 :
{nace_old}
# Codes NACE 2025 candidats + notes :
{proposed_codes}
========
# Choisir parmi : {list_proposed_codes}
# Répondre en JSON
Texte illustratif (prompt géré dans Langfuse).
Schéma imposé (CAGResponse)
nace2025 : le code choisi (ou null)codable : la description est-elle suffisante ?nace08_valid : l’ancien code semble-t-il cohérent ? (bonus : détecte des erreurs historiques)confidence : auto-évaluation 0–1furnished_rental : cas métier (location de logement meublé)JSON strict imposé côté serveur (JSON-Schema) : indispensable pour parser des millions de réponses.
Appel LLM (base.py)
chat.completions.parsetemperature = 0,01, seed = 2025Options
null → traitée comme non codableSSPCloud — plateforme data science de l’Insee (projet open-source Onyxia, sur Kubernetes).
Entièrement open-source, conteneurisé, reproductible.
Pourquoi ?
Avant le run : base vectorielle (Qdrant) des notes NACE 2025. Pendant le run : on embed la description, on récupère les top-k codes proches, injectés dans le même squelette de prompt. Coût : un modèle d’embedding + une base à maintenir.
| Modèle | Taille | Vitesse | Comportement |
|---|---|---|---|
| Qwen3-6-35B MoE | 35B | Très rapide (13 it/s) | Modérément restrictif |
| Qwen3-6-35B MoE + thinking | 35B | Lent (~1 it/s) | Moins restrictif |
| Gemma4-26B MoE | 26B | Rapide (8 it/s) | Modérément restrictif |
Le vrai code n’est pas toujours atteignable (erreurs de codage historiques) → une évaluation naïve sous-estime le modèle.
Niveau 5 (sous-classe) — 28 346 observations annotées. % de codes prédits = annotation manuelle.
| Modèle | Overall | Codable | Mapping-ok |
|---|---|---|---|
| Qwen3-6-35B MoE | 75,2 % | 78,3 % | 83,3 % |
| Qwen3-6-35B MoE + thinking | 75,7 % | 80,4 % | 84,1 % |
| Gemma4-26B MoE | 75,2 % | 80,0 % | 83,5 % |
| Vote majoritaire | 78,3 % | — | 86,9 % |
qwen3-6-35b-moe · ~28 500 obs multivoques · seule la source des candidats diffère
| Décomposition (niveau 5) | CAG | RAG |
|---|---|---|
| Vrai code dans les candidats | 90,0 % | 83,5 % |
| LLM choisit juste (si présent) | 83,4 % | 78,1 % |
| Overall | 75,3 % | 65,3 % |
Le goulot, c’est le retriever
Réduire l’écart passe par une meilleure qualité de retriever (embeddings, reranker), pas un top-k plus large.
Comment on construit la description d’activité
Principe
libelleExemple de bloc envoyé au LLM
Le contexte enrichi (catégorie juridique, surface, …) aide à trancher, ex. commerce de détail vs de gros.