Outline

1 About me

  • Who am I ?
    • Nathan Randriamanana
    • Data scientist at Insee
    • INSEE statistician civil servant
    • working now at the Business Statistics Directorate
    • SIRENE business register

1.1 About my job

  • I wear two hats:
    • 🎩 Application Administrator: Acting as the functional lead for the SIRENE register. I bridge the gap between business needs and IT by writing functional specifications for system maintenance and evolution.
    • 🧢 Data Scientist: Managing the end-to-end ML workflow for APE classification from model training and delivery to its integration into the production application.

Example from presentation at the Cloud Native Days France 2026 conference regarding cloud technologies

Example from presentation at the Cloud Native Days France 2026 conference regarding cloud technologies

Linkedin

2 Table of contents

1️⃣ Context

2️⃣ State of play: How do we assign activity codes today ?

3️⃣ Why did we end up using Machine Learning ?

4️⃣ fastText: a simple and efficient classifier

5️⃣ Beyond fastText: introducing TorchTextClassifiers

6️⃣ NACE Rev 2.1: update our classifier using LLMs

3 1️⃣ Recap and context

3.1 Quick reminder: What is SIRENE ?

System for the Identification of the Register of Enterprises and Establishments

  • Sirene is the French national company registry

  • When a company registers:

    • sole authority for issuing SIREN and SIRET numbers
    • a main activity code is attributed (APE code)
  • Milestones and challenges faced:

    • Refactoring of the Sirene information system Sirene 4
    • NACE revision ➡️ dual coding in NACE rev 2 and 2.1
  • End of March 2024:

    • Officially switch from Sirene 3 to Sirene 4
  • Processing 5 million registrations across 2025–2026 🚀

  • 2027: Transition to NACE rev 2.1

  • Consequences: Ideal moment to innovate (but under the constraint!)

3.2 A one-stop shop

Another key actor: National Industrial Property Institute

  • INPI is the operator of the one-stop shop (Single Window)
  • This replaces the old paper-based CFE (Business formality centers)
  • It is the primary data source for the Sirene registry
  • Implementation of the PACTE Law (2019) to simplify the French economy

PACTE law

Since January 1, 2023, all business formalities must be filed online via a single portal.

3.3 The flow” of formalities

3.4 The administrative landscape

  • Siren number: company directory identification system
  • Principal activity code (APE)
  • Classification on a daily basis
  • Different administrations
  • Different information systems
  • Requirements: quick, responsive and flexible to updated instructions

4 2️⃣ State of play: How do we assign activity codes today ?

4.1 Assign an activy code: different processes

4.2 Assign an activy code: two outcomes

  • Automatic coding
  • Human review

4.3 Near-ubiquity of ML

4.4 Automatic activity classification

4.5 Human review

4.6 Model

  • Text classification model which uses additional categorical variables
  • Historically, a system expert named SICORE as automatic label coding system
  • First ML usecase with fastText library
  • Originally trained on legacy data annotated partly by the coding engine and partly manually

5 3️⃣ Why did we end up using Machine Learning ?

5.1 The methodology: a legacy

  • Methodology is not new
  • Automatic coding for difficult cases and manuel check for the easiest one
  • Coding systems since 1981 ! 🧙🏻‍♂️

5.2 The shock

  • data not clean anymore ➡️ Teams got overwhelmed
  • automation drops to 30%
Display an exemple of activity description from business formality centers
  BAKERY
Display an exemple of activity description from the one-stop-shop
 I am looking to developpe in the cleaning and maintainance of busines offices: changeing bins, vacuuming, and mopping

6 4️⃣ fastText: a simple and efficient classifier

6.1 A package from FAIR lab

  • Up until November 2022, we used an automatic label coding system, called Sicore
    • Based on a training file of encoding examples
    • Drawbacks : if the label did not match an encryption example, no code suggestion was returned. It was then coded manually by a human being
  • Since November 2022, we have implemented a new model based on machine
  • Very accurate even with literal descriptions that have never been coded before
  • A 100 % result even with a low accuracy rate (always outputs an answer …)
  • However we have decided to maintain a manual check if the accuracy is not good

6.2 Feature extraction

  • Word embedding: a method of vectorisation.
  • Pre-trained embeddings available in open-source.
  • We learn our own word embeddings.
  • Additionally, embeddings for:
    • word n-grams and character n-grams.

6.3 Linear classifer

  • 2 classification methods:
    • Softmax: a single multiclass classifier.
    • One-vs-all: multiple binary classifiers.
  • Optimisation: stochastic gradient descent algorithm.
  • Loss function: cross-entropy.

6.5 Handling categorical variables

  • Concatenation of the text description with the names and values of the auxiliary variables:
Text NAT TYP EVT SUR
Cours de musique NaN X 01P NaN
🢃

“Cours de musique NAT_NaN TYP_X EVT_01P SUR_NaN”

  • Imperfect method: 3-grams “AT_” or “T_0” used.

6.6 Preprocessing

  • Preprocessing essential for natural language processing.
  • Constraints: simple, light and easily reproducible in Java .
Transformation Text description
Input 3 D: La Deratisation - La Desinsectisation - La Desinfection
Lower-case conversion 3 d: la deratisation - la desinsectisation - la desinfection
Punctuations removal 3 d la deratisation la desinsectisation la desinfection

6.7 Preprocessing

Transformation Text description
Input 3 D: La Deratisation - La Desinsectisation - La Desinfection
… …
Numbers removal d la deratisation la desinsectisation la desinfection
One-letter word removal la deratisation la desinsectisation la desinfection
Stopwords removal deratisation desinsectisation desinfection

6.8 Preprocessing

Transformation Text description
Input 3 D: La Deratisation - La Desinsectisation - La Desinfection
… …
NaN removal deratisation desinsectisation desinfection
Stemming deratis desinsectis desinfect

6.9 Modeling

7 5️⃣ Beyond fastText: introducing TorchTextClassifiers

7.1 fastText: In Production, but Archived

  • fastText: The go-to for text classification at Insee.
  • Reliable and high-performing; deployed in 2021 for APE codification…
  • …but the repository has been archived since March 19, 2024.

7.2 The Stakes

  • Lack of library maintenance: eventual risks regarding stability and compatibility.
  • Most importantly: it stalls modernization.
  • Meanwhile, a highly dynamic Deep Learning/NLP ecosystem: PyTorch, Hugging Face, etc.

7.3 2025: Transitioning to PyTorch!

  • PyTorch model closely mirrors the fastText architecture: a smooth transition.
  • Discussed here:
    • PyTorch allows for customized architectures tailored to our needs (handling categorical variables).
    • Improved training monitoring.
    • Opportunities for modernization: explainability, calibration, higher-performance models…
  • A dedicated package to decouple model definition from its implementation.

7.4 From torchFastText to torchTextClassifiers

  • Evolution from a single package into a toolkit (a unified framework) for text classification with categorical variables.
    • Inspired by other PyTorch toolkits: Pythae for VAEs, TorchSeg for segmentation…
  • Conceptualization of the various components within a text classification model.
  • Seamless integration with the Hugging Face ecosystem.

7.5 Library scope and positioning

From a production standpoint

From a production standpoint
  • If you want to know more, get here and star this repo 🌟

7.6 Architecture

7.7 Features

  • Explainability 🔍 🤖 📊
  • Multi-label support 🏷️
  • Modular design 🧩⚙️

7.8 Get started

  1. check out the example notebook 📚 🚀
  2. import your own data in Load and preprocess data
  3. Choose your section based on your goal
    • torchTextClassifiers: a wrapper to handle them all ☺️ ➡️ Fast implementation: just grab-and-go 🛍️
    • Let’s dive into the different components 🤓 (experts) ➡️ More control: modular customization 🔧 👌 😎

Some tips for data import

Just update the import section and match your column names.
Works out-of-the-box with Parquet or CSV for instance (Can ask our AI chat)

8 6️⃣ NACE Rev 2.1: update our classifier using LLMs

8.1 Statistical classifications, briefly

The Problem

What NACE is

  • The EU classification of economic activities
  • Every business unit in production registers gets a NACE code
  • Hierarchical: section → division → group → class → sub-class
  • Revised every ~15 years — the current move: NACE 2.0 → NACE 2.1

The French specificity: NAF

  • Nomenclature d’Activités Française: The national adaptation of NACE, managed by INSEE
  • Same structure as NACE + an extra 5th character (a letter) for national specifics
  • Assigned to every French company as the well-known APE code (Activité Principale Exercée)

Note

Our work was actually conducted using the French NAF. However, I will use NACE in this presentation to make it more universally understandable and to demonstrate that our approach perfectly adapts to this broader European classification level.

8.2 When a classification is revised

A recurring challenge for every statistical institute (NACE, COICOP, ISCO, …).

Standard ML maintenance

  • Inputs may drift over time
  • We monitor and retrain on fresh labels
  • The target variable stays the same

Classification revision

  • The target variable itself changes
  • New labelled data are scarce or missing
  • Legacy labels are in the wrong space
  • Standard retraining is not possible
  • At Insee, a TorchTextClassifiers classifier (fastText-inspired) in production since 2025-2026 to assign NACE 2.0 codes
  • Trained on a 2.7-million-label training set.

Central question: how can we leverage years of accumulated production knowledge in the old classification to bootstrap a model in the new one?

8.3 Our strategy: shifting the old trainset

The Problem

8.4 NACE 2.0 → 2.1 and the one-to-many codes

The Problem

Scope of the change

  • Change in the number of sub-classes? Moderate…
    • from 746 to 732
  • Mostly splits at level 4, with a few merges and recompositions

Complexity lies in non-bijective cases!

  • Univocal: one-to-one mapping
  • One-to-many: 1-to-many relation (~2 to 5)

8.5 The scale of the problem

The Problem

One-to-many Univocal
Correspondence table 25 % 75 %
Actual training set 52 % 48 %

~1.4 M

observations that cannot be recoded by table lookup

The problem cannot be solved by a deterministic mapping table alone.

8.6 2. Human and Virtual Annotation

8.7 Human ground truth, machine scaling

Human and Virtual Annotation

NACE 2.1 ground truth campaign

  • ~30k annotations directly in NACE 2.1
  • ~25 NACE experts
  • Focused on one-to-many cases (the hard ones)

What we learned

Expert agreement is imperfect

  • 3-experts blind re-annotation: 62 % strict agreement, 96 % majority
  • Experts are far from converging systematically: we stay cautious when interpreting our precision metrics

Explicit reasoning protocol

  • Description, then check the legacy code, then consult candidates and explanatory notes, then decide
  • A sequence we can encode into an LLM prompt at scale

8.8 3. Methodology

8.9 Rule-Based Augmented Generation as
constrained selection

Methodology

Why not ask the LLM directly?

  • 732 codes is too large a space for reliable open generation.
  • Hallucinations are very frequent.
  • Capabilities depend heavily on the LLM’s implicit knowledge base.

Constrained selection instead:

  • For each observation, we already know the NACE 2.0 code
  • The correspondence table gives the set of admissible NACE 2.1 codes
  • The task becomes: pick the right code from a short, restricted list
  • Output aligned with official rules

8.10 The pipeline

Methodology

For each observation:

  1. Retrieve the activity description (free text + optional precisions: secondary activity, legal category)
  2. Retrieve the legacy NACE 2.0 code
  3. Use the correspondence table to get the set of candidate NACE 2.1 codes
  4. Inject the explanatory notes of each candidate code into the prompt
  5. Ask the LLM to pick the most appropriate code
  6. Parse the structured JSON response

A form of Rule-Based Augmented Generation (RBAG) with a deterministic retriever.


Inputs to the prompt

Element Source
Activity text + precisions Production register
NACE 2.0 code Insee NACE Experts
Candidate set Correspondence table
Explanatory notes Official NACE 2.1 documentation

8.11 The prompt structure

Methodology

System prompt — same for every observation

You are an expert in the NACE classification. Your task is to assign a NACE 2.1 code to a business based on its activity description, using a candidate list derived from the existing NACE 2.0 code…

User prompt — observation-specific

# Main activity : {{activity}}
# NACE 2.0 code : {{nace_old}}
# Candidate NACE 2.1 codes + notes : {{proposed_codes}}
========
# Instructions (7 rules)
  → candidate list only — no external code
  → first activity only
  → strict JSON — no explanation

Expected output

{
  "codable": true,
  "nace2.1": "0147J",
  "nace_legacy_valid": true,
  "confidence": 0.92
}
  • nace2.1: the chosen code (or null)
  • codable: was the description sufficient?
  • confidence: model self-assessment (0–1)
  • nace_legacy_valid: is the legacy code consistent?

{proposed_codes} — example:

86.95: Physiotherapy activities
  Includes: physiotherapy, medical massage, occupational therapy, praxitherapy…
  Does not include: osteopaths, chiropractors, non-medical massage parlors…

86.96: Traditional, complementary, and alternative medicine  [...]
86.99: Other human health activities n.e.c.  [...]

8.12 Multi-model annotation

Methodology

Model Size Inference speed Behaviour
Qwen3-6-35B MoE 35B Very fast (13 it/s) Moderately restrictive
Qwen3-6-35B MoE + thinking 35B Less fast (1 it/s) Less restrictive
Gemma4-26B MoE 26B Fast (8 it/s) Moderately restrictive

➕️ Aggregation: majority vote.

Different sizes, different throughput.

8.13 4. Infrastructure

8.14 Our MLOps stack on the SSPCloud

Infrastructure

SSPCloud is Insee’s data science platform, built on the open-source Onyxia project and powered by Kubernetes as its underlying technology stack.

Main elements of the technical stack:

  • MinIO — S3-compatible storage for datasets and model artifacts
  • llm.lab — SSPCloud’s LLM provider on premise, (OpenWebUI + vLLM)
  • Langfuse — prompt versioning
  • MLflow — experiment tracking
  • Argo Workflows — the end-to-end pipeline orchestrator

Entirely open-source, containerised, reproducible.

8.15 5. Results

8.16 Grading the LLM annotators

Results

  • Reference: the ~30,000 manually annotated one-to-many cases (NACE 2.1, level 5)
  • Three complementary accuracies — at the most detailed level (sub-class):
    1. Overall — across all observations
    2. Codable subset — restricted to predictions the LLM itself flagged as codable
    3. LLM-only: restricted to cases where the true code is in the candidate list (isolates the LLM’s choice quality)

8.17 Per-model and ensemble accuracy

Results

Accuracies at level 5 (sub-class) — % of cases where the predicted code matches the manual annotation.

Model Inference Overall Codable LLM-only
Qwen3-6-35B MoE 13 it/s 75.2 % 78.3 % 83.3 %
Qwen3-6-35B MoE + thinking ~1 it/s 75.7 % 80.4 % 84.1 %
Gemma4-26B MoE 8 it/s 75.2 % 80.0 % 83.5 %
Majority vote — 78.3 % — 86.9 %

The codable filter is per-model, hence the dash for the ensemble vote.

Ensemble accuracy comes at a real cost: three full inference passes, one of them in thinking mode (~10× slower).

A substantial share of disagreements are not LLM errors but ambiguous descriptions or legacy-coding mistakes.

8.18 Rebuilding the training set

Results

Once happy with the LLM on the benchmark, we apply at scale:

  • ~1.0 M one-to-many observations relabelled via the LLM pipeline
  • Combined with 1.3 M univocal observations (table lookup)
  • ~2.3 M labels in NACE 2.1
  • Distribution across predicted codes is very close to the labelled distribution

Tip

This is semi-synthetic training set: real businesses, real descriptions, but algorithmic labels.

8.19 Performance of the retrained classifier

Results

TorchTextClassifiers retrained on the semi-synthetic corpus

~80 %

overall accuracy on a representative NACE 2.1 test set

≈ the legacy NACE 2.0 classifier on its NACE 2.0 task

Last retrain used an earlier vintage of the corpus; today’s pipeline produces better labels.

Semi-synthetic LLM-generated labels can substitute for manual annotations in the early stages of a classification transition.

8.20 Recap

8.21 6. Conclusion and Perspectives

8.22 Take-aways and what’s next

Conclusion and Perspectives

Take-aways

  • A practical method to handle classification revisions in production ML
  • LLMs as virtual annotators scale where humans cannot
  • Semi-synthetic labels reach production-grade accuracy (~80 %)
  • Fully open-source, reproducible MLOps stack

Limits & ongoing work

  • Inference cost is non-trivial at ~1 M queries
  • Legacy errors are inherited
  • Explore RAG variants

9 Questions ?

  • Nathan RANDRIAMANANA
  • Data Scientist
  • Sirene
  • Insee — France
  • nathan.randriamanana@insee.fr