NOPE LinkedIn

Catégories:
IA

AnonyNER v3.1 : bilan d'entraînement — 30 labels, 16 000 exemples, évaluation par label

AnonyNER v3.1 : bilan d'entraînement — 30 labels, 16 000 exemples, évaluation par label image

Rubrique: IA Tag: Anonymisation Tag: NER Tag: spaCy Tag: Logs Tag: Cybersécurité Tag: MLOps Tag: Dataset Tag: GPU Tag: Évaluation

AnonyNER est le composant NER du projet Victor — un modèle spaCy entraîné spécifiquement sur des entités sensibles de logs de sécurité. La version 3.1 marque un changement d’échelle significatif : passage de 12 à 30 labels, corpus multiplié par 8, et pour la première fois une évaluation complète par label sur un jeu de test indépendant.

Cet article est un bilan technique, pas un tutoriel. L’objectif est de montrer où en est le modèle, ce qui fonctionne réellement, et ce qui reste à faire pour une utilisation en production.


Ce qui a changé dans v3.1

Labels : de 12 à 30, répartis en quatre groupes.

La v3 couvrait les entités réseau et firewall (IP, hostname, interface, CVE…). La v3.1 ajoute les entités Linux/Unix (UNIX_USER, UNIX_GROUP), Windows (WIN_USER, WIN_HOST, WIN_SID, WIN_GROUP, EVENT_ID, REGISTRY_KEY, SCHEDULED_TASK, WIN_SERVICE) et des labels génériques cross-plateformes (PID, PROCESS_NAME, COMMAND_LINE, FILE_HASH, ACTION, ASN, PROTOCOL).

Corpus : de ~2 000 à 15 851 exemples annotés.

Source Exemples Taux d’annotation auto
Logs OPNsense (corpus existant) 1 997
SSH Linux — Elastic auth.log 6 966 97.8%
Windows Security Events — OTRF 4 191 83.8%
Apache access logs — Elastic 2 977 99.2%
Synthétiques (labels rares) 92 100% (généré)

L’annotation est LLM-assistée : chaque ligne de log est envoyée à qwen2.5-coder:7b via Ollama, avec un prompt listant les 30 labels et leurs exemples. Un score de confiance composite décide si l’annotation passe automatiquement ou part en revue humaine.

Architecture du pipeline :

Logs bruts
  └─► annotate_corpus.py  (LLM → JSONL annoté)
        └─► clean_corpus.py       (filtre labels invalides, déduplique)
              └─► prepare_spacy_dataset.py  (JSONL → .spacy train/dev)
                    └─► train_anonyner.py   (fine-tuning spaCy, GPU auto-détecté)
                          └─► model-best    (meilleur checkpoint)

Source de vérité unique : labels.py centralise les 30 labels avec description et exemples. Le prompt LLM, les filtres de validation et les scripts d’entraînement en dérivent tous — ajouter un label revient à ajouter une entrée dans un dictionnaire.


Résultats globaux

F1 : 86.46%   Precision : 85.95%   Recall : 86.97%

Le modèle est entraîné par fine-tuning depuis en_anonyner (v3) — les poids existants sont préservés, seuls les deltas sont appris. L’early stopping intervient vers le step 5 400 (epoch 8), avec convergence réelle dès l’epoch 2.


Évaluation par label

Le F1 global à 86% cache des disparités importantes. Voici les résultats sur le dev set (~3 200 exemples, 20% du corpus, non vu pendant l’entraînement) :

Label P R F1
REGISTRY_KEY 99.7% 99.7% 99.7%
UNIX_USER 96.6% 96.9% 96.8%
DOMAIN 95.7% 97.8% 96.7%
WIN_USER 93.7% 98.3% 96.0%
CVE 91.7% 100% 95.7%
ACTION 100% 91.7% 95.7%
WIN_SID 86.0% 100% 92.5%
MAC_ADDRESS 93.5% 87.9% 90.6%
SERVICE_ACCOUNT 88.9% 90.6% 89.7%
FILE_PATH 81.3% 95.6% 87.8%
INTERFACE 84.4% 91.5% 87.8%
IP_ADDRESS 89.8% 84.7% 87.2%
WIN_HOST 76.7% 94.3% 84.6%
VPN_USER 92.3% 80.0% 85.7%
ASN 100% 75.0% 85.7%
IP_SUBNET 92.0% 86.8% 89.3%
URL_URI 75.3% 78.2% 76.7%
PID 75.9% 75.9% 75.9%
FIREWALL_RULE 79.2% 55.9% 65.5%
PORT_NUMBER 62.6% 64.0% 63.3%
PROCESS_NAME 50.6% 79.2% 61.8%
FILE_HASH 100% 40.0% 57.1%
HOSTNAME 51.9% 58.7% 55.1%
PROTOCOL 92.3% 34.3% 50.0%
COMMAND_LINE 61.9% 28.9% 39.4%
SCHEDULED_TASK 0%
EVENT_ID 0%
WIN_GROUP 0%

Lectures

9 labels dépassent F1 90% : REGISTRY_KEY, UNIX_USER, DOMAIN, WIN_USER, CVE, ACTION, WIN_SID, MAC_ADDRESS, IP_SUBNET. Ce sont les labels les mieux représentés dans le corpus et les plus stables en termes de format.

Precision haute, Recall faible (PROTOCOL P=92%, R=34% ; ASN P=100%, R=75%) : le modèle reconnaît correctement ces entités quand il les prédit, mais il en manque la majorité. Cause : trop peu d’exemples variés — le modèle est prudent, pas ignorant.

Labels à 0% (SCHEDULED_TASK, EVENT_ID, WIN_GROUP) : le corpus contient respectivement 3, 14 et 1 exemples — insuffisant pour l’apprentissage. Ces labels existent dans le modèle mais ne sont jamais prédits avec assez de confiance.

HOSTNAME à 55% : collision entre noms de machines Linux (srv-db01), Windows (DESKTOP-AB12CD) et domaines (corp.local). Le modèle hésite sur la frontière — les critères d’annotation sont à clarifier.


Distance avec la production

Pour un usage en anonymisation de logs sensibles, le Recall est la métrique critique : une entité manquée est une fuite d’information. Les cibles de production sont F1 ≥ 90% global et R ≥ 93%, avec aucun label à F1 < 80%.

État actuel : F1=86.5%, R=87.0%. Environ 4 points sous la cible de production.

Trois axes d’amélioration identifiés :

1. Données pour les labels déficitairesPROTOCOL (R=34%), COMMAND_LINE (R=29%), SCHEDULED_TASK (0%), EVENT_ID (0%), WIN_GROUP (0%) ont besoin de plusieurs centaines d’exemples supplémentaires depuis des logs réels, pas de données synthétiques.

2. Résoudre la confusion HOSTNAME/WIN_HOST/DOMAIN — revoir les critères d’annotation pour distinguer clairement ces trois types sur des exemples ambigus.

3. Migration vers spaCy Transformers — l’architecture tok2vec atteint un plafond naturel vers 87-88% F1 sur un corpus aussi hétérogène. Passer à DistilBERT ou un transformer spécialisé logs permettrait de franchir ce plafond. Gain estimé : +4-6% F1. Prérequis : session GPU dédiée (~4h sur RTX 4070 Ti).


Accélération GPU sur WSL2 sans toolkit CUDA

La machine de développement tourne sous WSL2 avec une RTX 4070 Ti. Le CUDA toolkit n’est pas installé système — seul Ollama embarque ses propres .so. spaCy/thinc exige CuPy, qui exige libcublas, libcurand, libnvrtc.

La solution : les paquets pip NVIDIA fournissent ces librairies directement dans le venv :

pip install cupy-cuda12x \
    nvidia-curand-cu12 \
    nvidia-cuda-nvrtc-cu12 \
    nvidia-cublas-cu12 \
    nvidia-cuda-runtime-cu12

train_anonyner.py construit dynamiquement le LD_LIBRARY_PATH depuis les sous-dossiers nvidia/*/lib/ du venv avant de lancer spacy train --gpu-id 0. La détection GPU est transparente — si CuPy n’est pas disponible, l’entraînement bascule silencieusement sur CPU.


Prochaines étapes

  • Annotation ciblée sur les logs contenant des PROTOCOL, COMMAND_LINE, SCHEDULED_TASK et EVENT_ID explicites
  • Résolution de la confusion HOSTNAME/WIN_HOST/DOMAIN dans les critères d’annotation
  • Intégration du modèle v3.1 dans le pipeline de production Victor et mesure des gaps résiduels sur logs réels
  • Étude de faisabilité spaCy Transformers (DistilBERT) pour franchir le seuil de 90% F1

Le code source du pipeline d’entraînement est disponible dans le dépôt Victor sur GitHub, répertoire training/.


Références datasets : LogHub (Linux, Apache), OTRF Security Datasets (Windows Security Events), Elastic Examples (auth.log, Apache).