VeilleAnalytics — Classifieur thématique zero-shot + fine-tuné

Classe un article de veille tech (titre + résumé) dans 7 thèmes. Deux approches cohabitent dans ce dépôt :

  • Zero-shot multilingue (MoritzLaurer/mDeBERTa-v3-base-mnli-xnli), composant ML du projet veille-analytics (Étape 13). Le contenu étant en français, on utilise un modèle multilingue avec des hypothèses NLI descriptives en français plutôt que le modèle anglophone initialement prévu.
  • Fine-tuné local (camembert-base, Étape 17) — entraîné sur les labels faibles Mistral pour tenter de rattraper l'écart avec la baseline LLM. Voir section dédiée plus bas.

Multi-étiquette : un article peut relever de plusieurs thèmes (scores sigmoïdes indépendants).

Thèmes : IA/ML · DevOps/Infrastructure · Architecture · Sécurité · Développement · Pratiques/Qualité · Productivité/Outils.

Fichiers

  • classifier.py — cœur partagé zero-shot (thèmes, hypothèses FR, pipeline zero-shot, classify()).
  • app.py — UI Gradio + API auto-exposée (point d'entrée du Space).
  • evaluate.py — évaluation locale (zero-shot vs Mistral, + fine-tuné via --model-dir).
  • train.py — fine-tuning multi-label de camembert-base (Étape 17).
  • finetuned.py — pendant de classifier.py pour le modèle fine-tuné (classify_finetuned()).
  • model-finetuned/ — poids du modèle fine-tuné (généré par train.py, gitignoré).
  • eval_results.jsonartefact produit par evaluate.py : métriques micro/macro par système (zero-shot, fine-tuné, Mistral) et par thème, réutilisé tel quel dans le M3.2.
  • train_results.jsonartefact produit par train.py : courbe de validation par epoch et best.threshold, relu ensuite par evaluate.py pour le report « au seuil de validation ».
  • requirements.txt — dépendances (torch CPU). Inchangé par l'Étape 17 : la boucle d'entraînement est écrite en PyTorch pur (pas de Trainer HF), pour éviter la dépendance accelerate et les changements d'API transformers v5.
  • ruff.toml — configuration du lint Python (qualité de code, C19), voir ci-dessous.

Versionnement. Ce dépôt n'a pas de remote GitHub : il vit sur le Space Hugging Face (hf, déploiement différé — cf. Hébergement) et est versionné localement. La qualité de code n'est donc pas portée par une action CI + SonarCloud comme sur veille-analytics, mais par ruff en local.

Développement local

uv venv --python 3.12
uv pip install torch --index-url https://download.pytorch.org/whl/cpu
uv pip install transformers sentencepiece protobuf gradio

.venv/bin/python app.py        # UI locale sur http://127.0.0.1:7860
.venv/bin/python evaluate.py   # précision vs ../veille-analytics/data/annotations.csv

uv pip install ruff            # qualité de code (C19)
.venv/bin/ruff check .         # lint local — doit sortir « All checks passed! »

# Étape 17 — fine-tuning + éval à 3 (zero-shot / fine-tuné / Mistral)
.venv/bin/python train.py --data-dir ../veille-analytics/data
.venv/bin/python evaluate.py --data-dir ../veille-analytics/data --model-dir ./model-finetuned

# Orchestration des deux étapes ci-dessus (pendant de run-spark-batch.sh côté veille-analytics)
./run-eval.sh                # pipeline complet (fine-tuning ~25 min + éval)
./run-eval.sh --skip-train   # rejoue l'éval sur le modèle existant

Évaluation (100 articles annotés à la main — Étape 12)

Précision / rappel / F1 (micro) sur l'espace des 7 thèmes, jeu de validation indépendant. Depuis l'Étape 17, evaluate.py --model-dir ./model-finetuned ajoute une troisième colonne (le fine-tuné, cf. section suivante) et régénère train_results.json / eval_results.json.

Approche Point opérant Précision Rappel F1 (micro)
ML zero-shot (mDeBERTa) seuil 0,7 0,49 0,57 0,525
Baseline Mistral (themes_mistral) 0,57 0,81 0,667

Le zero-shot prêt-à-l'emploi fournit une base honnête mais reste sous la classification Mistral (LLM plus grand + prompt dédié). Fort sur Sécurité (F1 0,75), IA/ML (0,62) et DevOps (0,62) ; faible sur Pratiques/Qualité (0,22). Détail complet dans eval_results.json (généré par evaluate.py). Le fine-tuning (Étape 17, ci-dessous) est le levier testé pour combler l'écart — avec un résultat en demi-teinte.

Fine-tuning CamemBERT — Étape 17

Peut-on rattraper Mistral en entraînant un petit modèle local sur ses propres étiquettes ? train.py fine-tune camembert-base (110M paramètres) en multi-label sur les 7 thèmes, à partir des labels faibles themes_mistral (pas des annotations manuelles, réservées au test).

Méthode

  • Modèle : camembert-base, tête de classification multi-label (BCEWithLogitsLoss), boucle d'entraînement PyTorch manuelle — pas de Trainer Hugging Face, pour éviter la dépendance accelerate et les changements d'API transformers v5. Aucune dépendance ajoutée : requirements.txt est inchangé par cette étape.
  • Données : 529 articles exportés de D1. Après exclusion des 100 articles annotés à la main (le test set — les mêmes textes au train constitueraient une fuite) et des articles sans thème themes_mistral canonique, il reste 426 candidats, séparés en train 383 / val 43 (seed 42, VAL_FRACTION = 0.10).
  • Hyperparamètres : LR 2e-5, batch 16, longueur max 256 tokens, AdamW (weight decay 0,01), warmup linéaire 10 %, jusqu'à 10 epochs avec early stopping (patience 2) sur le micro-F1 val — balayé sur les seuils 0,3 à 0,7 via le même prf() que evaluate.py.
  • Coût : 25 min sur CPU (WSL2, 4 cœurs). Modèle sauvé dans model-finetuned/ (445 Mo, gitignoré — reproductible via le seed 42 ; un push vers le Hub HF est possible mais pas requis pour ce projet).
.venv/bin/python train.py --data-dir ../veille-analytics/data
.venv/bin/python evaluate.py --data-dir ../veille-analytics/data --model-dir ./model-finetuned

Courbe d'entraînement (train_results.json)

Micro-F1 val (43 articles, labels Mistral) au fil des epochs :

0,495 (ep. 1) → 0,565 (ep. 4) → 0,685 (ep. 8, meilleur, seuil 0,4) → early stopping ep. 10.

Résultats sur la vérité terrain (100 articles annotés — eval_results.json)

Système Précision Rappel F1 (micro)
Zero-shot mDeBERTa (seuil 0,7) 0,490 0,565 0,525
Fine-tuné CamemBERT (seuil 0,5, optimisé sur le test*) 0,742 0,548 0,630
Fine-tuné CamemBERT (seuil 0,4, retenu sur la validation) 0,465 0,702 0,559
Baseline Mistral (themes_mistral) 0,567 0,810 0,667

* Le seuil 0,5 est choisi en balayant directement les 100 articles annotés : il surajuste, exactement comme le seuil 0,7 du zero-shot ci-dessus. Le chiffre méthodologiquement propre est celui au seuil 0,4, fixé sur la validation (383/43, jamais sur le test).

F1 par thème (au meilleur seuil test de chaque système) :

Thème Zero-shot Fine-tuné Mistral
IA/ML 0,618 0,737 0,839
DevOps/Infrastructure 0,615 0,650 0,610
Architecture 0,531 0,630 0,676
Sécurité 0,754 0,824 0,885
Développement 0,424 0,702 0,615
Pratiques/Qualité 0,222 0,000 0,407
Productivité/Outils 0,316 0,000 0,556

Bilan honnête

  • Le fine-tuning bat nettement le zero-shot (+0,105 de micro-F1 au meilleur seuil, +0,034 au seuil validation) et dépasse même Mistral sur 2 thèmes — Développement (0,702 vs 0,615) et DevOps/Infrastructure (0,650 vs 0,610) — avec une précision micro bien meilleure (0,742 vs 0,567 pour Mistral).
  • Mais il reste sous Mistral en global (0,630 vs 0,667) et s'effondre sur les deux thèmes les plus rares, Pratiques/Qualité et Productivité/Outils : F1 à 0,000 au seuil 0,5 (le modèle ne dépasse presque jamais 0,5 sur ces thèmes ; au seuil 0,4 ils réapparaissent partiellement). Deux causes probables : seulement ~30-40 exemples positifs par classe rare dans le train, et des labels d'entraînement bruités par Mistral (qui plafonne la qualité atteignable).
  • Leviers documentés mais non testés : pos_weight par thème dans la BCE pour compenser le déséquilibre, plus de données, seuils par thème plutôt qu'un seuil global.
  • Enseignement (M3.2) : avec ~400 exemples faiblement labellisés, un petit modèle local fine-tuné se hisse près du LLM qui l'a supervisé — mais la vérité terrain manuelle reste indispensable pour le voir. Le micro-F1 val donnait 0,685 (contre Mistral comme juge), la vérité terrain humaine ramène ça à 0,559-0,630 selon le seuil.

Hébergement

Statut : local pour l'instant. Le déploiement sur Hugging Face Spaces est différé : depuis juillet 2026, HF gate CPU basic ET ZeroGPU derrière un compte PRO (le build ZeroGPU refuse par ailleurs torch==2.13.0+cpu). Le code Gradio (app.py, en-tête YAML du Space, requirements.txt) reste prêt si un compte PRO est pris plus tard.

Pour l'exécution : .venv/bin/python app.py (UI locale). Pour un endpoint cloud appelable depuis le Worker (Étape 14), on visera l'Inference API serverless de HF (appel du modèle hébergé via token, sans Space à héberger) — dispo à vérifier à ce moment-là.

Mise à jour juillet 2026 (Étape 14). Piste confirmée : le modèle est servi par l'Inference API serverless de Hugging Face, provider hf-inference, endpoint https://router.huggingface.co/hf-inference/models/MoritzLaurer/mDeBERTa-v3-base-mnli-xnli (token avec permission « Inference Providers », free tier avec rate limits). C'est ce qu'utilise désormais le Worker veille-analytics : module src/lib/classifyMl.ts, qui porte le contrat de classifier.py (mêmes hypothèses FR, même template, seuil 0,7). Le Space Gradio ci-dessus reste optionnel/différé — il n'est plus sur le chemin critique de la prod.

Déploiement Space (si compte PRO)
hf auth login                              # token HF (write)
# Créer le Space sur https://huggingface.co/new-space (SDK: Gradio, hardware CPU basic), puis :
git init && git add . && git commit -m "Étape 13 — classifieur zero-shot"
git remote add hf https://huggingface.co/spaces/<user>/<space>
git push hf main

Le build lit l'en-tête YAML (sdk: gradio, app_file: app.py) et installe requirements.txt.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for antoinecoulon/veille-ml

Finetuned
(171)
this model