NOPE LinkedIn

Catégories:
IA

AnonyNER v3.15 : transformer, diagnostic FN et 7 points de F1 gagnés

AnonyNER v3.15 : transformer, diagnostic FN et 7 points de F1 gagnés image

Rubrique: IA Tag: Anonymisation Tag: NER Tag: spaCy Tag: Transformer Tag: RoBERTa Tag: Logs Tag: Cybersécurité Tag: Dataset Tag: Évaluation

Suite directe du bilan v3.11. Ce cycle a trois étapes : migration de l’architecture tok2vec vers un transformer, diagnostic systématique des faux négatifs par label, puis correction ciblée des causes racines identifiées. Résultat : F1 global 88% → 95,9%.

Contexte v3.12→v3.14 — Entre v3.11 et v3.14, trois cycles d’amélioration ont produit des résultats décevants : ajout de 500 exemples synthétiques EC2 pour HOSTNAME (+2 points de recall), tentatives de rééquilibrage des labels PID et MAC_ADDRESS. La migration vers l’architecture transformer (v3.14) a apporté un gain net de +4 points de F1 global, mais le recall HOSTNAME restait bloqué à 59%. C’est ce plafond persistant qui a déclenché le diagnostic décrit ici.


Étape 1 — Migration vers spaCy Transformers

L’article v3.11 concluait : “tok2vec atteint son plafond naturel.” À F1=88%, le modèle ne progressait plus malgré les ajouts de corpus. Le passage à RoBERTa s’est fait en changeant deux paramètres dans la config spaCy :

# Avant (tok2vec, efficiency)
python training/scripts/train_anonyner.py

# Après (RoBERTa, accuracy)
python training/scripts/train_anonyner.py --transformer

Le script train_anonyner.py génère la config appropriée selon le mode. En mode transformer, spaCy utilise roberta-base via spacy-transformers, avec un learning rate warmup et un scheduler cosine — les hyperparamètres par défaut de spaCy pour ce mode sont bien calibrés, aucun réglage manuel nécessaire.

Résultats de la migration (corpus v3.14, ~20 800 exemples) :

F1 : 91.9%   Precision : 92.8%   Recall : 91.1%

Soit +3.9 points de F1 par rapport au tok2vec. La migration paie, mais le recall HOSTNAME reste anormalement bas.


Étape 2 — Diagnostic systématique des faux négatifs

Un script générique d’analyse des faux négatifs par label a été développé. Pour chaque entité gold manquée par le modèle, il affiche ce que le modèle a prédit à la place et le contexte environnant :

python training/scripts/analyze_fn.py --label HOSTNAME
Total FN HOSTNAME : 208

Labels prédits à la place de HOSTNAME :
  IP_ADDRESS           175  (84.1%)
  —                     33  (15.9%)

── Prédit comme IP_ADDRESS (175 cas) ──
  175×  'ip-10-77-20-248'

── Exemples de contexte ──────────────────────────────────────
  gold='ip-10-77-20-248'  prédit=IP_ADDRESS
  Mar  5 12:01:32 «ip-10-77-20-248»[→IP_ADDRESS] sshd[1234]: ...

Lecture immédiate : 84% des HOSTNAME manqués sont une seule valeur — ip-10-77-20-248 — systématiquement prédit comme IP_ADDRESS. Le modèle n’a pas un problème de recall HOSTNAME en général, il a un problème avec cette chaîne spécifique.


Étape 3 — Diagnostic de corpus : le mislabel à 2 350 entrées

Un audit du corpus a révélé la cause racine :

ip-10-77-20-248 : 2 350× IP_ADDRESS + 1 318× HOSTNAME

La même chaîne est annotée IP_ADDRESS dans 64% des cas et HOSTNAME dans 36%. Ce n’est pas une ambiguïté métier — c’est un bug d’annotation. Le script d’auto-annotation utilisait la forme lexicale (ip-X-X-X-X ressemble à une IP) sans tenir compte de la position dans le log.

Le format syslog est non ambigu :

<Mois> <Jour> <HH:MM:SS> <hostname> <service>[<pid>]: <message>

Dans ce contexte, ip-10-77-20-248 est un hostname EC2 AWS en position syslog prefix — pas une adresse IP. Ce sont des noms de machines AWS en DNS inverse (PTR records) : ip-10-77-20-248.eu-west-1.compute.internal.

Effet sur l’apprentissage : le modèle reçoit des gradients contradictoires pour la même chaîne dans le même contexte. Avec 64% de signal IP_ADDRESS, il apprend à ignorer la position et à se fier à la forme — exactement l’inverse de ce qu’on veut.

Correction : diversify_hostname_corpus.py

Le script de correction fait deux choses :

  1. Relabellise les IP_ADDRESSHOSTNAME quand la chaîne est en position syslog prefix (détection par regex)
  2. Substitue toutes les occurrences HOSTNAME de ip-10-77-20-248 par des valeurs variées d’un pool EC2 (18 variantes), forçant le modèle à apprendre sur la position plutôt que sur la valeur
Résultats dry-run :
  2 350 mislabels corrigés  (IP_ADDRESS → HOSTNAME)
  3 534 valeurs substituées (ip-10-77-20-248 → pool EC2)
    168 valeurs conservées  (tirage aléatoire identique)

Le corpus résultant (v3.15) contient 21 844 enregistrements.


Résultats après correction corpus

L’entraînement sur corpus v3.15 — avec les 2 350 mislabels corrigés et les valeurs EC2 diversifiées — donne des résultats immédiats :

F1 : 95.3%   Precision : 94.8%   Recall : 95.4%

Le score HOSTNAME seul :

Métrique v3.14 (tok2vec) v3 transformer v3.15 (fix)
Precision 61.2% ~85% 99.1%
Recall 52.5% 59.5% 98.2%
F1 56.5% 71.7% 98.6%
FN ~180 208 19

Le recall HOSTNAME passe de 52.5% à 98.2% en corrigeant une seule source d’erreur dans le corpus. Le transformer ne pouvait pas apprendre correctement contre 2 350 mislabels contradictoires — les 500 exemples synthétiques ajoutés entre-temps ne représentaient que 15% du signal, insuffisant pour contre-balancer.


Étape 4 — COMMAND_LINE : données synthétiques ciblées

L’analyse FN sur COMMAND_LINE donnait :

Total FN COMMAND_LINE : 21 (100% non détectés)

  14/21 = chpasswd[pid]   format syslog service
   2/21 = User-Agent Mozilla/5.0
   1/21 = C:\windows\...\powershell.exe
   1/21 = /ausearch

Cause : seulement 200 exemples COMMAND_LINE dans le corpus, sur 4 patterns très différents. Le pattern dominant des FN — chpasswd[31100] en position syslog service — est structurellement identique au label PROCESS_NAME (sshd[1234]). Le modèle ne différencie pas les deux sans exemples explicites.

Un générateur synthétique ciblé (generate_command_line_contexts.py) produit 500 exemples répartis sur les 4 familles manquantes :

Générateur Exemples Pattern
gen_syslog_cmd_service 250 chpasswd[pid]:, passwd[pid]:, useradd[pid]: en position service
gen_unix_commands 120 Commandes Unix dans logs audit/cron/sudo
gen_windows_cmd 80 C:\windows\...\powershell.exe dans EventLog
gen_useragent 50 Mozilla/5.0 ... dans logs web/proxy

Résultats sur corpus v3.16 (v3.15 + 500 exemples COMMAND_LINE) :

F1 : 95.9%   Precision : 94.9%   Recall : 96.9%

COMMAND_LINE : F1 25.9% → 86.4%, Recall 16.7% → 83.1%.

Effet secondaire positif : les templates Windows incluaient des annotations EVENT_ID et PID, ce qui a dopé ces labels sans ciblage explicite (EVENT_ID 0% → 78.6%, PID 55% → 94.3%).


Tableau de bord final — v3.16

Label P R F1 vs v3.11
IP_ADDRESS 99.0% 99.9% 99.4% +11.9
HOSTNAME 99.0% 98.8% 98.9% +42.4
REGISTRY_KEY 99.3% 99.7% 99.5% +0.1
WIN_USER 98.2% 100.0% 99.1% +1.0
UNIX_USER 98.8% 99.2% 99.0% +2.1
DOMAIN 98.2% 96.8% 97.5% +1.8
CVE 100.0% 100.0% 100.0% +4.2
PROCESS_NAME 98.6% 96.9% 97.7% +4.8
FILE_PATH 90.1% 96.8% 93.3% +2.3
COMMAND_LINE 90.1% 83.1% 86.4% +60.5
PORT_NUMBER 88.8% 94.1% 91.4% +23.9
EVENT_ID 68.8% 91.7% 78.6% +78.6
PID 89.2% 100.0% 94.3% +39.3
URL_URI 80.0% 81.8% 80.9% +4.4
MAC_ADDRESS 71.9% 82.1% 76.7% +5.9
FILE_HASH 33.3% 66.7% 44.4% +15.8
SCHEDULED_TASK 50.0% 33.3% 40.0% +40.0
GLOBAL 94.9% 96.9% 95.9% +7.9

Ce que ce cycle enseigne

Le diagnostic FN est plus utile que l’ajout aveugle de données. Trois cycles d’ajout de données synthétiques sur HOSTNAME (v3.13, v3.14) ont produit des gains marginaux (+1-2%). Un seul audit de corpus a produit +42 points de F1. La question à poser avant d’ajouter des données n’est pas “combien en manque-t-il ?” mais “pourquoi le modèle se trompe-t-il ?”

Les mislabels contradictoires sont des poisons lents. 2 350 entrées mal étiquetées dans un corpus de 20 000 (11.7%) ont suffi à plafonner le recall HOSTNAME à 60% malgré un transformer. Le signal contradictoire force le modèle à moyenner les gradients — il apprend à hésiter plutôt qu’à décider.

Les données synthétiques ciblées fonctionnent pour les labels structurels. Pour COMMAND_LINE, 500 exemples générés par template ont réparé un F1 à 25.9%. La condition : cibler le pattern exact qui échoue (position syslog service pour chpasswd[pid]), pas le label en général.

Le transformer ne compense pas un corpus corrompu. Passer de tok2vec à RoBERTa a apporté +4 points de F1. Corriger les mislabels HOSTNAME a apporté +42 points sur ce label seul. L’architecture compte, mais les données comptent davantage.


Prochaines priorités

Label F1 actuel FN Action
URL_URI 80.9% 66 Données synthétiques Apache/nginx avec chemins diversifiés
COMMAND_LINE 86.4% 24 Analyse FN résiduelle — patterns encore manqués
MAC_ADDRESS 76.7% 5 Logs ARP/DHCP supplémentaires
FILE_HASH 44.4% 1 Trop peu d’exemples dev — vérifier le corpus
SCHEDULED_TASK 40.0% 2 Logs schtasks.exe / Event 7045

L’entraînement v3.17 — corpus v3.16 + 500 exemples URL_URI synthétiques — est en cours.


Scripts disponibles dans Victor/training/scripts/ : eval_anonyner.py, analyze_fn.py, diversify_hostname_corpus.py, generate_hostname_contexts.py, generate_command_line_contexts.py, generate_url_uri_contexts.py.