ML Detection Iteration Log — 2026-03-17¶
Status: Experiment Audience: Team, AI agents Purpose: Historical detailed iteration log for the
feat/ml-detection-improvementbranch
English summary¶
This is the longest historical log in the folder. It documents:
the first
niamoto-scorebaselinethe diagnosis of weak holdout families such as
forest_inventoryearly changes to value features and fusion features
the move toward a more product-oriented benchmark
the first surrogate/autoresearch decisions
the first acquisition lots and their effect on the benchmark
The detailed body below is preserved as historical working material and still contains French wording from the original session notes.
Objet¶
Ce journal capture l’état des expérimentations réalisées sur la branche
feat/ml-detection-improvement, avec focus sur :
le nouveau protocole
niamoto-scoreles points faibles révélés par les holdouts
les corrections tentées
les décisions prises pour les itérations suivantes
Ce document est daté et doit pouvoir être complété par de nouvelles entrées au fil de l’expérimentation.
Contexte de départ¶
La branche a migré d’une logique historique plus monolithique vers un pipeline hybride :
alias exacts
modèle header
modèle values
modèle de fusion
projection vers
semantic_profileet affordances
En parallèle, le protocole d’évaluation a été renforcé :
suppression de la fuite de données dans l’évaluation fusion
réalignement progressif train/runtime
ajout du
NiamotoOfflineScoreajout de holdouts langue, famille et anonymous
ajout d’un logging de progression pour les runs longues
Mesure de référence observée¶
Run complet niamoto-score observé avant les itérations ciblées de ce journal :
NiamotoOfflineScore = 78.8351
Sous-scores et holdouts marquants :
primary:78.835holdout_lang[fr]:67.491holdout_lang[es]:88.599holdout_lang[de]:88.949holdout_lang[zh]:100.000holdout_family[dwc_gbif]:71.407holdout_family[forest_inventory]:47.918holdout_family[research_traits]:66.891holdout_family[tropical_field]:54.146holdout_anonymous:94.583diagnostic_synthetic:67.388
Lecture initiale¶
Le signal principal n’est pas un problème global de colonne anonyme. Au contraire, le holdout anonymous est fort.
Les faiblesses se concentrent sur :
certaines familles de datasets réels ;
en particulier
forest_inventory;puis
tropical_field;et, dans une moindre mesure,
fr.
Conclusion provisoire :
la stack sait bien exploiter les patterns forts et les headers lisibles ;
elle généralise encore mal sur certains domaines métier ;
le problème semble plus domain-specific que purement linguistique.
Diagnostic ciblé sur forest_inventory¶
Un diagnostic dédié a été exécuté sur le holdout forest_inventory.
Mesure ciblée de référence :
forest_role_f1 = 0.2769forest_concept_f1 = 0.2008
Constat principal¶
La fusion suivait trop souvent la branche values sur des erreurs systématiques.
Distribution des sources d’erreur observées :
89erreursfollowed_value_wrong32erreursfollowed_header_wrong27erreursheader_right_value_wrong12erreursboth_wrong_same
Lecture :
la branche
valuesdominait trop sur ce domaine ;elle poussait des concepts inadaptés sur des colonnes codées métier ;
la fusion ne corrigeait pas suffisamment ce biais.
Confusions saillantes¶
Confusions de rôle :
category -> statisticidentifier -> statisticcategory -> locationmeasurement -> statistic
Confusions de concepts :
location.admin_area -> event.dateidentifier.plot -> statistic.countcategory.tree_condition -> statistic.countmeasurement.cover -> statistic.countidentifier.record -> statistic.count
Exemples d’erreurs à haute confiance¶
Exemples observés :
TCLpréditstatistic.countau lieu demeasurement.coverTCApréditstatistic.countau lieu demeasurement.coverPEUPNRpréditstatistic.countau lieu decategory.managementAGENTCDpréditstatistic.countau lieu decategory.damageCOUNTYCDpréditstatistic.countau lieu delocation.admin_area
Lecture :
les colonnes codées d’inventaire forestier étaient mal reconnues ;
le système les interprétait trop souvent comme des comptes ou des mesures simples.
Hypothèse de travail¶
Hypothèse centrale de cette itération :
Le principal défaut sur
forest_inventoryn’est pas l’absence totale de signal, mais une sur-dominance de la branchevaluessur des colonnes codées métier, combinée à une fusion trop permissive.
Changements tentés¶
1. Factorisation des features values¶
Objectif :
supprimer le mismatch train/runtime sur les features values ;
disposer d’un point unique pour enrichir les signaux utiles aux colonnes codées.
Changements :
création de
src/niamoto/core/imports/ml/value_features.pyréutilisation depuis :
ml/scripts/train/train_value_model.pysrc/niamoto/core/imports/ml/classifier.py
Nouveaux signaux ajoutés :
n_unique_valuesdominant_ratiofixed_length_ratioshort_code_ratiodense_integer_domaintiny_integer_domain
2. Enrichissement des meta-features de fusion¶
Objectif :
rendre la fusion plus sensible au désaccord header/values ;
détecter les cas où
valuespoussestatistic.countsur un header codé.
Changements :
création de
src/niamoto/core/imports/ml/fusion_features.pyajout dans la fusion de :
max proba
marge top1-top2
entropie
accord/désaccord
flags
statistic.countdétection de header code-like
3. Heuristique ciblée de damping¶
Une première tentative d’injecter l’information “coded header” dans le modèle
header lui-même a été testée puis retirée, car elle dégradait forest_role_f1.
La correction conservée est plus ciblée :
si un header est manifestement code-like ;
et si une branche pousse fortement
statistic.count;alors la confiance
statistic.countest amortie avant la fusion.
But :
corriger un faux positif fréquent ;
sans reconfigurer agressivement le modèle header.
Résultats ciblés après itération¶
Baseline ciblée¶
forest_role_f1 = 0.2769forest_concept_f1 = 0.2008
Après factorisation values + meta-features de fusion + damping¶
forest_role_f1 = 0.2960forest_concept_f1 = 0.1938
Interprétation¶
Le signal obtenu est cohérent avec l’objectif produit :
amélioration du rôle sur le domaine difficile ;
pas encore d’amélioration sur le concept fin.
À ce stade, l’itération semble surtout améliorer le comportement produit pertinent, pas la précision académique du concept.
Validation globale encore requise :
rerun complet
niamoto-scorevérification qu’on n’améliore pas
forest_inventoryen cassant d’autres familles
Validation globale de l’itération¶
Le rerun complet niamoto-score a été exécuté après cette itération.
Résultat final :
NiamotoOfflineScore = 79.1135
Comparaison avec la baseline observée avant l’itération :
baseline :
78.8351itération :
79.1135delta global :
+0.2784
Sous-scores et holdouts après itération¶
primary:79.113holdout_lang[fr]:66.465holdout_lang[es]:91.329holdout_lang[de]:92.380holdout_lang[zh]:99.444holdout_family[dwc_gbif]:68.267holdout_family[forest_inventory]:41.654holdout_family[research_traits]:65.081holdout_family[tropical_field]:57.131holdout_anonymous:100.000diagnostic_synthetic:71.785
Ce qui s’améliore¶
primarymonte légèrementesetdemontent nettementtropical_fieldmonteanonymousdevient excellentforest_inventoryaméliore bien sonrole_f1
Ce qui se dégrade¶
frbaisse légèrementdwc_gbifbaisse nettementresearch_traitsbaisseforest_inventorybaisse fortement en score global
Point critique sur forest_inventory¶
Le résultat forest_inventory après itération est particulièrement important :
NiamotoOfflineScore = 41.654role = 0.296critical = 0.533pairs = 0.390confQ = 0.500hc_err = 0.500
Lecture :
le rôle s’améliore bien par rapport à la baseline ciblée ;
mais la confiance, la cohérence dataset-level et les erreurs à haute confiance se dégradent trop fortement ;
le mécanisme actuel améliore un aspect local mais abîme trop le comportement global sur ce garde-fou critique.
Verdict sur l’itération¶
Verdict retenu :
itération non validée comme nouveau baseline de la stack
Raison :
le gain global est trop faible ;
il est obtenu au prix d’une dégradation trop forte sur
forest_inventory;la baisse sur
dwc_gbifetresearch_traitsrend l’amélioration globale peu défendable ;l’hypothèse était bonne, mais l’implémentation du correctif reste trop agressive ou mal calibrée.
Ce qui est conservé de cette itération :
le diagnostic détaillé ;
la compréhension du biais
values -> statistic.count;la nécessité de traiter
forest_inventorycomme garde-fou explicite.
Décisions prises¶
conserver la factorisation shared des features values ;
conserver les meta-features de fusion ;
ne pas promouvoir automatiquement le damping ciblé actuel comme nouveau baseline sans nouvelle itération plus prudente ;
ne pas conserver l’injection directe de
codeddans le texte header ;utiliser
forest_inventorycomme garde-fou explicite dans les prochaines décisions d’autoresearch.
Ce que cette itération nous apprend¶
Le vrai point faible n’est pas d’abord la langue française.
Le vrai point faible est le comportement sur certains domaines métier, notamment l’inventaire forestier.
Les gains les plus utiles passent par :
une meilleure fusion ;
une meilleure prudence ;
des signaux plus explicites sur les colonnes codées ;
des garde-fous orientés domaine.
Prochaines itérations recommandées¶
Court terme¶
finir la validation complète
niamoto-scorecomparer avant/après sur tous les holdouts
décider si l’itération est gardée au niveau stack complet
Si le gain global tient¶
ajouter un reporting dédié sur les prédictions
-> statistic.countintégrer
forest_inventorycomme garde-fouautoresearchattaquer ensuite
tropical_field
Si le gain global ne tient pas¶
conserver l’analyse ;
réduire la portée de l’heuristique ;
ou déplacer la logique vers une fusion plus structurée au lieu d’un damping simple
Évolution décidée du benchmark¶
Après cette run, il a été décidé de faire évoluer le benchmark avant une nouvelle itération ML.
Objectif :
rendre les diagnostics plus honnêtes ;
mieux séparer anglais standard et anglais métier ;
éviter de surinterpréter des splits faciles comme
zh.
Changements ajoutés au reporting de ml/scripts/eval/evaluate.py :
diagnostic_lang[zh]comme diagnostic secondaire et non plus holdout stratégiquediagnostic_subset[en_standard]diagnostic_subset[en_field]diagnostic_subset[coded_headers]diagnostic_subset[gbif_core_standard]diagnostic_subset[gbif_extended]split interne de
forest_inventory:forest_inventory/ifn_frforest_inventory/fia_enforest_inventory/nordic_inventory
split interne de
dwc_gbif:dwc_gbif/core_standarddwc_gbif/extended
Décision associée :
ne pas changer immédiatement la formule du
NiamotoOfflineScorechanger d’abord le reporting et les garde-fous
relancer ensuite une run complète pour identifier le vrai point faible :
IFN,FIA,GBIF standard,GBIF étendu,en_field, ou headers codés
Commandes utiles¶
Validation complète avec progression :
OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 VECLIB_MAXIMUM_THREADS=1 NUMEXPR_NUM_THREADS=1 LOKY_MAX_CPU_COUNT=1 .venv/bin/python -m ml.scripts.eval.evaluate --model all --metric niamoto-score --splits 3 --verbose-progress
Boucle silencieuse compatible autoresearch :
.venv/bin/python -m ml.scripts.eval.evaluate --model all --metric niamoto-score --splits 3 --quiet
Run recommandée après évolution du benchmark :
OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 VECLIB_MAXIMUM_THREADS=1 NUMEXPR_NUM_THREADS=1 LOKY_MAX_CPU_COUNT=1 .venv/bin/python -m ml.scripts.eval.evaluate --model all --metric niamoto-score --splits 3 --verbose-progress
Lot Guyane 1 — acquisition et intégration¶
Un premier lot Guyane a été traité pour rapprocher le benchmark des jeux réellement visés côté produit.
Vérification d’accès Dataverse¶
Jeux inspectés :
ForestScan / ParacouTrinite Forest CensusesTresor Forest CensusesTibourou Forest CensusesMontagne Tortue Forest Censuses
Résultat vérifié :
le CSV principal
ForestScan / Paracouest publiquement téléchargeable ;les CSV principaux
Trinite,Tresor,TibourouetMontagne Tortuesont restreints et exposés viafileAccessRequest;leurs fichiers de description restent publics et ont été récupérés localement comme support de préparation.
Fichiers récupérés¶
Sous ml/data/silver/guyane/ :
paracou/FGPlotsCensusData2023.csvparacou/FGPlotsDescription.csvtrinite/TriniteDescription.csvtresor/TresorDescription.csvtibourou/TibourouDescription.csvmontagne_tortue/Montagne_TortueDescription.csv
Lecture du schéma¶
Constats utiles :
le census Paracou contient
38colonnes ;les fichiers de description Guyafor ont un schéma homogène de
53colonnes ;le census Paracou est immédiatement utile pour le benchmark
tropical_field / plot_inventory;les fichiers de description sont utiles pour préparer les futurs labels, mais pas encore assez sûrs pour être injectés dans le gold set sans risque de bruit et de redondance.
Intégration retenue¶
Choix fait :
intégrer seulement le census
Paracou / ForestScan;ne pas intégrer les fichiers de description ;
garder les autres datasets Guyane en attente d’accès à leurs CSV principaux.
Changements réalisés :
ajout d’un bloc
FORESTSCAN_PARACOU_LABELSdansml/scripts/data/build_gold_set.pyajout de la source
forestscan_paracou_censusdansSOURCESrégénération du gold set
Résultat :
forestscan_paracou_censusapporte34colonnes gold ;le gold set passe à
2265colonnes au total, dont1669gold et596synthetic.
Correction d’outillage connexe¶
Défaut corrigé dans
ml/scripts/data/build_gold_set.py :
uv run python -m ml.scripts.data.build_gold_setcassait surModuleNotFoundError: scriptsle script ajoute maintenant à la fois
src/et la racine du repo ausys.pathles deux modes fonctionnent désormais :
uv run python -m ml.scripts.data.build_gold_setuv run python -m ml.scripts.data.build_gold_set
Décision¶
État du lot Guyane 1 :
Paracou / ForestScan: intégréTrinite / Tresor / Tibourou / Montagne Tortue: documentés localement mais non intégrés faute d’accès aux CSV census
Suite logique :
ajouter ensuite un premier lot
GBIF cibléou intégrer des datasets d’instances réelles dès qu’ils sont disponibles
Lot GBIF ciblé 1 — régional général¶
Un premier lot GBIF régional a été récupéré via l’API publique
occurrence/search avec les contraintes suivantes :
régions :
NC,GF,GA,CMrègne :
Plantaevolume cible :
5000occurrences par région
Sorties créées sous ml/data/silver/gbif_targeted/ :
new_caledonia/occurrences.csvguyane/occurrences.csvgabon/occurrences.csvcameroon/occurrences.csv
Volumes récupérés :
new_caledonia:5000 / 442442guyane:5000 / 299072gabon:5000 / 583825cameroon:5000 / 621574
Lecture¶
Ce lot est utile pour construire un sous-benchmark GBIF régional, mais il est
fortement tiré par les jeux observationnels grand public, en particulier des
datasets type iNaturalist.
Conclusion :
bon lot
GBIF régional généralmauvais candidat comme unique approximation du GBIF institutionnel
Intégration gold set¶
Les sources suivantes ont été ajoutées dans
ml/scripts/data/build_gold_set.py :
gbif_targeted_new_caledoniagbif_targeted_guyanegbif_targeted_gabongbif_targeted_cameroon
Apport observé :
41colonnes pournew_caledonia38colonnes pourguyane37colonnes pourgabon39colonnes pourcameroon
Lot GBIF ciblé 2 — institutionnel¶
Un second flux a ensuite été ajouté pour isoler un sous-corpus plus institutionnel.
Filtre retenu :
basisOfRecord in {PRESERVED_SPECIMEN, MATERIAL_SAMPLE, OCCURRENCE}présence d’au moins un champ institutionnel :
institutionCodecollectionCodeinstitutionIDcollectionKey
exclusion explicite des gros jeux observationnels :
iNaturalistobservation.orgPl@ntNeteBirdquelques autres catalogues analogues
Test de rendement¶
Un sample institutional a été exécuté sur 5000 enregistrements scannés par
région.
Résultat :
new_caledonia:6guyane:16gabon:200(cap atteint)cameroon:200(cap atteint)
Lecture :
NCetGFont très peu d’institutionnel dans les premiers résultats régionaux ;GAetCMont un rendement institutionnel suffisant ;il est pertinent de conserver
NC/GFdans le lot général etGA/CMdans un lot institutionnel dédié.
Lot institutionnel retenu¶
Le lot complet retenu à ce stade est :
gaboncameroon
Sorties créées sous ml/data/silver/gbif_targeted_institutional/ :
gabon/occurrences.csvcameroon/occurrences.csv
Volumes retenus :
gabon:2000cameroon:2000
Qualité observée :
forte présence de
PRESERVED_SPECIMENjeux type
Tropicossignal nettement plus institutionnel que le lot GBIF régional général
Intégration gold set¶
Les sources suivantes ont été ajoutées dans
ml/scripts/data/build_gold_set.py :
gbif_targeted_institutional_gabongbif_targeted_institutional_cameroon
Apport observé :
36colonnes pourgabon36colonnes pourcameroon
Effet cumulé sur le gold set¶
Après intégration du lot GBIF général puis du lot institutionnel GA/CM :
ml/data/gold_set.jsonpasse à2492colonnesdont
1896gold et596synthetic
Décision retenue :
conserver
gbif_targeted/comme sous-corpus régional généralconserver
gbif_targeted_institutional/gabonetcamerooncomme sous-corpus institutionnelne pas forcer pour l’instant un scan institutionnel profond sur
new_caledoniaetguyane
Entrée du 2026-03-18 — run sur gold set enrichi¶
Avant cette run, les copies de référence locales ont été internalisées dans
ml/data/silver/ et ml/scripts/data/build_gold_set.py
a été réaligné pour ne plus dépendre de copies hors repo pour :
guyadiv_treesguyadiv_plotsafrique_occafrique_plotsnc_occnc_plots
Le gold set reste à ce stade à :
2492colonnes au total1896gold596synthetic
Résultat global¶
Run complète observée :
NiamotoOfflineScore = 78.6199
Comparaison avec la run documentée précédente :
run précédente :
79.1135nouvelle run :
78.6199delta global :
-0.4936
Diagnostics marquants¶
primary:78.620holdout_lang[fr]:66.176holdout_lang[es]:89.457holdout_lang[de]:92.742holdout_family[dwc_gbif]:70.210holdout_family[tropical_field]:63.952holdout_family[research_traits]:71.370holdout_anonymous:100.000diagnostic_synthetic:70.892
Diagnostics structurels :
diagnostic_subset[en_standard]:95.656diagnostic_subset[en_field]:75.917diagnostic_subset[coded_headers]:72.606diagnostic_subset[gbif_core_standard]:96.322diagnostic_subset[gbif_extended]:87.025
Lecture des nouveaux sous-buckets¶
Le split forest_inventory confirme très nettement que le problème principal
de ce bloc n’est pas homogène :
forest_inventory:41.841forest_inventory/ifn_fr:17.950forest_inventory/fia_en:50.306forest_inventory/nordic_inventory:65.225
Lecture :
ifn_frest de loin le sous-bucket le plus faible ;fia_enest difficile mais moins catastrophique ;nordic_inventoryreste le sous-cas le plus tolérable.
Interprétation¶
Cette run ne change pas la conclusion de fond :
les points forts restent
GBIF standard,anglais standard,anonymous;les points faibles restent
en_field, lescoded_headers, et certains domaines métier compacts ;forest_inventory, et surtoutifn_fr, est un bon révélateur de fragilité, mais ne doit pas piloter seul la roadmap produit.
Pour la cible produit visée à ce stade, les signaux les plus utiles restent :
tropical_fieldresearch_traitsgbif_core_standardgbif_extendeden_field
Décision sur le protocole¶
Décision prise après cette run :
retirer
zhdu protocole courant ;ne plus l’utiliser comme diagnostic reporté dans la boucle standard ;
garder l’accent sur
fr,es,de,tropical_field,research_traits,gbif_*,en_fieldetcoded_headers.
Conséquence :
la prochaine run sera un peu plus propre côté reporting ;
zhest désormais considéré comme non stratégique pour la décision produit.
Entrée du 2026-03-18 — baseline ProductScore¶
Le protocole de décision a ensuite été enrichi avec un ProductScore
spécifique à la cible produit courante.
Poids retenus :
tropical_field:30%research_traits:15%gbif_core_standard:20%gbif_extended:10%en_field:15%anonymous:10%
Baseline observée :
ProductScore = 79.2454
Détail de la baseline :
tropical_field:63.952research_traits:71.370gbif_core_standard:96.322gbif_extended:87.025en_field:75.917anonymous:100.000
Décision associée :
utiliser
product-scorecomme cible principale d’autoresearchconserver
niamoto-scorecomme garde-fou global secondaireconserver
forest_inventorycomme stress test secondaire, sans le laisser piloter la décision finale
Entrée du 2026-03-18 — pivot autoresearch¶
Après plusieurs essais, le constat est devenu clair :
les boucles stack complètes trouvent parfois des candidats plausibles ;
mais la validation complète est trop coûteuse pour une itération autonome dense ;
même les variantes
fastrestent trop lentes pour atteindre un vrai rythmeautoresearch.
Conclusion¶
Le problème principal n’est plus la seule qualité de la métrique.
Le vrai problème est la granularité de la recherche :
on réentraîne trop de choses pour des changements locaux ;
on dépense le budget en certification au lieu d’explorer ;
la boucle autonome reste trop lente pour délivrer sa valeur.
Décision¶
Pivot décidé :
documenter puis construire une surrogate loop fusion-only
séparer l’exploration autonome de la validation stack complète
réserver
product-scoreetniamoto-scoreà la promotion des meilleurs candidats, pas à chaque itération
Document de référence :
docs/05-ml-detection/autoresearch-surrogate-loop.md
Entrée du 2026-03-18 — première mesure surrogate loop¶
La surrogate loop fusion-only a été implémentée puis mesurée sur le gold set
courant.
Première tentative¶
builder du cache interrompu après environ
16 mindiagnostic :
coût du build trop élevé
principale cause identifiée : prédictions
header/valuefaites record par record
Correctif appliqué¶
batch prediction pour
headerbatch prediction pour
valuesextraction metadata batchée
logging par fold dans le builder
Mesures après optimisation¶
Build du cache :
build_fusion_surrogate_cache --splits 3
real = 490.08s
Soit environ 8m10s de préchauffe one-shot.
Baseline surrogate-fast sur cache chaud :
FusionSurrogateFast = 55.6326
real = 1.66s
Baseline surrogate-mid sur cache chaud :
FusionSurrogateMid = 59.2746
real = 1.31s
Lecture¶
le build du cache reste un coût réel, mais il est désormais ponctuel ;
les évaluations
surrogate-fastetsurrogate-midsont enfin dans la bonne zone pourautoresearch;la surrogate loop devient donc exploitable pour une exploration dense ;
le prochain enjeu n’est plus la faisabilité du pivot, mais la stratégie de promotion des candidats gagnants vers
product-score-fast-fast, puisproduct-score, puisniamoto-score.
Entrée du 2026-03-19 — corrections runner et premier gain autoresearch¶
Corrections du runner surrogate¶
Trois problèmes identifiés et corrigés dans
ml/scripts/research/run_fusion_surrogate_autoresearch.py :
Validation stack trop coûteuse — Quand un candidat passait
surrogate-mid, le runner lançaitproduct-score-fast-fast(bloquant pendant des heures). Correction :--defer-stack-validation(défaut true), les candidats passants vont dans.autoresearch/fusion-surrogate-promotions.jsonl.Fichiers hors périmètre — L’iter 11 du run Codex avait touché le log d’expérimentations. Correction : statut
reject_scope, rejet immédiat si des fichiers horsDEFAULT_ALLOWED_PATHSsont touchés.Pre-commit hooks bloquants — Le commit automatique échouait car
ruff-formatreformatait les fichiers stagés. Correction :--no-verifypour les commits automatiques du runner.restore_paths crashait sur fichiers untracked —
git restore --source=HEADéchoue sur les fichiers qui n’existent pas dans HEAD. Correction : séparer tracked (restore) et untracked (unlink).
Remplacement Codex → Claude¶
Après épuisement des crédits Codex, le moteur a été remplacé par
claude -p --dangerously-skip-permissions.
Run Codex (2026-03-18) — 150 itérations¶
141codex_error(symlinks cassés~/.codex/skills/)9itérations utiles :7reject_fast,2reject_midmeilleur fast :
55.8177(baseline55.6326)meilleur mid :
58.9922(baseline59.2746)0promotion
Premier gain Claude — cross-rank reciprocity¶
L’itération 2 du premier run Claude a produit un candidat gagnant
(fast 55.6326 → 56.5524, delta +0.9198). Le candidat a aussi amélioré
mid (59.2746 → 60.1680, delta +0.8934).
4 nouvelles features de fusion ajoutées :
header_top1_value_rank— position du concept #1 header dans le classement values (0 = aussi #1, 1 = dernier)value_top1_header_rank— l’inversetop2_cross_match— le #2 d’une branche correspond au #1 de l’autreboth_weak— les deux branches sont faibles (max proba < 0.3)
Fichiers modifiés : train_fusion.py et classifier.py.
Nouvelles baselines après gain¶
surrogate-fast = 56.5524 (était 55.6326, +0.92)
surrogate-mid = 60.1680 (était 59.2746, +0.89)
Run Claude post-gain — 50 itérations (2026-03-19)¶
50itérations complétées50reject_fastmeilleur fast :
56.1821(baseline56.5524)0promotion
Lecture¶
Le gain cross-rank reciprocity semble être un plateau pour le périmètre actuel de recherche. 50 itérations supplémentaires n’ont pas réussi à battre la nouvelle baseline.
Hypothèses pour débloquer :
élargir
DEFAULT_ALLOWED_PATHS(ajouterfusion_features.py)enrichir le prompt avec des hypothèses plus ciblées
relancer un run stack complète sur header ou values
reconstruire le cache surrogate après un gain header/values
Décision¶
conserver le gain cross-rank reciprocity comme nouvelle baseline fusion
ne pas relancer autoresearch fusion immédiatement
passer à l’évaluation end-to-end par instance (niamoto-subset) pour mesurer l’impact réel du ML sur les suggestions de widgets
Entrée du 2026-03-20 — réentraînement, validation ProductScore, optimisation batch¶
Réentraînement des 3 modèles¶
Les modèles .joblib dans ml/models/ étaient toujours les anciens — l’autoresearch
modifiait le code des features de fusion mais n’entraînait pas les modèles. Les
scores surrogate étaient un proxy local, pas le vrai score produit.
Réentraînement complet exécuté :
header : macro-F1 = 0.7614
values : macro-F1 = 0.3783
fusion : mean macro-F1 = 0.6899 (leak-free CV, 5 folds)
Durée totale : ~5h (goulot = extraction record par record dans le leak-free CV).
Validation ProductScore — gain confirmé¶
ProductScore = 80.0372 (baseline 79.2454, delta +0.79)
Détail par bucket :
Bucket |
Avant |
Après |
Delta |
|---|---|---|---|
tropical_field |
63.95 |
64.88 |
+0.93 |
research_traits |
71.37 |
70.98 |
-0.39 |
gbif_core_standard |
96.32 |
95.87 |
-0.46 |
gbif_extended |
87.03 |
89.75 |
+2.72 |
en_field |
75.92 |
78.53 |
+2.61 |
anonymous |
100.0 |
100.0 |
= |
Gains notables : en_field +2.61, gbif_extended +2.72, tropical_field +0.93.
Régression : fr -4.07 (holdout, pas dans le ProductScore).
Optimisation batch de l’entraînement fusion¶
Le goulot de l’entraînement était extract_fusion_features() appelée record
par record (~15 000 appels séquentiels pour le leak-free CV).
La version batch extract_fusion_branch_probabilities_batch() existait déjà
pour le cache surrogate. Elle a été câblée dans train_fusion.py pour
remplacer les 3 boucles record par record.
Résultat :
scores strictement identiques (même macro-F1 par fold au centième)
temps : 5h → 15 min (~20x plus rapide)
zéro perte de qualité
Évaluation par instance — niamoto-subset¶
Un script ml/scripts/eval/evaluate_instance.py a été créé pour comparer la
détection ML avec l’import.yml validé d’une instance réelle.
Résultats sur niamoto-subset (29 colonnes, 9 évaluées via ground truth) :
Mode |
Role correct |
Concept correct |
|---|---|---|
Alias seul |
4/9 (44%) |
4/9 (44%) |
ML (modèles réentraînés) |
6/9 (67%) |
5/9 (56%) |
Gains ML par rapport aux alias seuls :
id_plot: non détecté →identifier.plot(rôle correct)geo_pt: non détecté →location.coordinate(rôle + concept corrects, 0.93)
Erreurs restantes :
infra→measurement.diameter(faux, confiance faible 0.32)plot_name→location.locality(alias impose à confiance 1.0)location→ pas trouvé (champ schema, pas colonne CSV)
Décisions de cette session¶
ProductScore 80.04 validé comme nouvelle baseline
modèles réentraînés commités
batch optimization commitée (entraînement 20x plus rapide)
évaluation par instance opérationnelle sur niamoto-subset
prochaine étape : câbler le ML dans l’auto-config (plan documenté dans
docs/05-ml-detection/README.mdand the active reference docs indocs/05-ml-detection/)