[{"content":"","date":"9 novembre 2026","externalUrl":null,"permalink":"/tags/claude-code/","section":"Tags","summary":"","title":"Claude Code","type":"tags"},{"content":"","date":"9 novembre 2026","externalUrl":null,"permalink":"/tags/fastapi/","section":"Tags","summary":"","title":"Fastapi","type":"tags"},{"content":"","date":"9 novembre 2026","externalUrl":null,"permalink":"/tags/pwa/","section":"Tags","summary":"","title":"Pwa","type":"tags"},{"content":"","date":"9 novembre 2026","externalUrl":null,"permalink":"/tags/python/","section":"Tags","summary":"","title":"Python","type":"tags"},{"content":"","date":"9 novembre 2026","externalUrl":null,"permalink":"/tags/react/","section":"Tags","summary":"","title":"React","type":"tags"},{"content":" Savoir où en est le foyer face à une coupure d\u0026rsquo;eau, une tempête ou une évacuation : ce qui est prêt, ce qui manque, ce qui a périmé. L\u0026rsquo;application tourne sur le téléphone, sans réseau, et se synchronise avec un PC de la maison. StatutEn développement · v2 hors ligne livrée, catalogue revu à chaque conseil PériodeDepuis novembre 2026 RôleSolo : spécification, référentiel, développement Stack Python 3.13FastAPISQLModelSQLiteReactTypeScriptVitePWAIndexedDBGSAPTailscale Code source 279\nobjectifs\nsur 14 axes et 9 paliers, décrits en YAML 2\nmoteurs de calcul\nPython et TypeScript, tenus identiques par des cas de test partagés 98\ntests automatisés\n57 côté serveur, 41 côté application Note de transparence. Ces travaux ont été menés à titre personnel, dans un but d\u0026rsquo;apprentissage, avec l\u0026rsquo;assistance d\u0026rsquo;une IA :\npour le code, la relecture technique et méthodologique ; pour comprendre les outils et les méthodes mathématiques ; pour l\u0026rsquo;aide à la rédaction, la traduction et la correction du texte ; pour générer l\u0026rsquo;image de couverture de cette fiche. Le choix du sujet, le protocole, la vérification des résultats et les conclusions sont les miens, et j\u0026rsquo;en assume les erreurs. Comment j\u0026rsquo;utilise l\u0026rsquo;IA\nProjet en cours. Cette fiche décrit l\u0026rsquo;état du dépôt au 9 novembre 2026, une semaine après le premier commit (9 pull requests fusionnées). Les chiffres sont comptés dans le catalogue et les tests, et seront mis à jour avec le projet. Toutes les captures montrent un foyer fictif (Camille, Sam, leur fille Léa et le chat Moka), saisi sur une instance de démonstration. L\u0026rsquo;application n\u0026rsquo;existe qu\u0026rsquo;en français : les captures sont celles de l\u0026rsquo;interface réelle. Le problème # Les guides officiels de préparation (gouvernement, Croix-Rouge) disent quoi faire : un stock d\u0026rsquo;eau, une trousse, un sac d\u0026rsquo;évacuation, des contacts sur papier. Ils ne disent pas où en est mon foyer, ni ce qui a cessé d\u0026rsquo;être vrai depuis : les bouteilles qui périment, la batterie jamais rechargée, la formation aux premiers secours d\u0026rsquo;il y a trois ans, le bébé arrivé entre-temps.\nJe voulais un outil qui :\nmesure la préparation par des critères vérifiables (« 14 jours d\u0026rsquo;eau pour ce foyer »), pas par un sentiment ; régresse quand la réalité change : un stock périmé ne compte plus, une compétence non pratiquée expire ; s\u0026rsquo;adapte au foyer : un appartement au 6e étage n\u0026rsquo;a pas les mêmes priorités qu\u0026rsquo;une maison isolée, un foyer avec un enfant ou un traitement à délivrance limitée non plus ; marche sans réseau, puisque c\u0026rsquo;est précisément le jour où le réseau manque qu\u0026rsquo;on en a besoin ; garde les données à la maison : la composition du foyer, ses fragilités de santé et l\u0026rsquo;emplacement de son matériel n\u0026rsquo;ont rien à faire sur un serveur tiers. Où en est le projet # v1 · Le moteur et le référentiel 2 novembre Une spécification issue d\u0026#39;une interview Spécification écrite à partir d'une interview, puis catalogue YAML (niveaux, axes, objectifs, besoins, questionnaire), moteur de niveaux en Python pur, API FastAPI et interface React avec la colonne en SVG. v2 · Hors ligne sur téléphone 2 novembre Le moteur porté en TypeScript Application installable (PWA), journal d'opérations dans le navigateur, synchronisation avec le serveur du PC par Tailscale. Le moteur Python reste la référence ; sa copie TypeScript rejoue les mêmes cas de test. Quatre conseils de relecture 6 au 9 novembre Le catalogue confronté à trois points de vue À chaque conseil, trois agents relisent l'application et le catalogue, chacun sous un angle différent, puis débattent ; je valide le plan avant sa mise en œuvre. Ils ont apporté l'écran Crise, le sursis de 30 jours, les axes Moral, Défense et Cybersécurité, la condition physique, et corrigé des contenus faux ou dangereux. Inventaire en cartes et compétences 8 et 9 novembre 99 objets, 117 talents L'inventaire devient un jeu de cartes à cinq niveaux avec un état des lieux chiffré ; les compétences deviennent neuf arbres de talents. Ce que fait l\u0026rsquo;application # Un monument de neuf paliers # Le niveau du foyer est un monument de neuf paliers, une colonne romaine par défaut. Chaque palier correspond à un horizon de résilience :\nNiveau Nom Ce que le foyer tient 1 Initié Quelques heures de coupure : 24 h d\u0026rsquo;eau et de vivres, une lumière, les numéros d\u0026rsquo;urgence 2 Éveillé Connaît ses risques, a ses papiers, ses contacts et un plan de base 3 Paré 3 jours sans eau courante, électricité ni ravitaillement 4 Prévoyant 14 jours confiné ou coupé du réseau 5 Endurant 3 mois de rupture partielle, une épargne de précaution 6 Aguerri 6 mois de réserves, des compétences pratiques entretenues 7 Autonome Produit une partie de son eau, de sa nourriture et de son énergie 8 Résilient Intégré à une communauté d\u0026rsquo;entraide, transmet ce qu\u0026rsquo;il sait 9 Anti-fragile Tout est éprouvé en conditions réelles, chaque crise améliore le plan Les paliers sont bloquants : le niveau 4 n\u0026rsquo;est atteint que si tous les objectifs requis des niveaux 1 à 4 sont validés, sur tous les axes. Un foyer très avancé sur l\u0026rsquo;eau mais sans plan d\u0026rsquo;évacuation reste donc au palier de son maillon faible, et l\u0026rsquo;accueil dit lequel.\nLe foyer fictif est au niveau 3, à 22 objectifs sur 38 du niveau 4. Le tambour III est fissuré : une validation expire dans le mois. À droite, les trois actions les plus utiles. Le niveau 9 ne s\u0026rsquo;achète pas : il demande d\u0026rsquo;éprouver le foyer (7 jours sans eau du réseau, 72 heures disjoncteur coupé, une évacuation réelle en moins de 15 minutes, un exercice inopiné avec son groupe), et presque tous ses critères sont à refaire chaque année.\nLe monument se choisit parmi quatorze : pyramide, arbre, tour Eiffel, Grande Mosquée de Djenné, moaï, fusée, volcan sous-marin… Changer de monument ne change rien au niveau.\nChaque monument a ses neuf paliers et son couronnement, qui s\u0026rsquo;illumine au niveau 9. Un niveau qui peut baisser # Le niveau ne se garde pas une fois pour toutes. Il redescend quand :\nun stock périme : un article périmé cesse aussitôt de compter dans l\u0026rsquo;autonomie ; une validation expire : une compétence à pratiquer, un équipement à tester, un exercice à refaire ont une durée de validité ; une échéance saisonnière passe : le bois rentré avant novembre, le ramonage avant octobre ; le foyer change : un membre de plus augmente les besoins et peut rendre applicables de nouveaux objectifs. Pour éviter qu\u0026rsquo;une seule batterie oubliée fasse tomber plusieurs paliers d\u0026rsquo;un coup, une validation expirée entre d\u0026rsquo;abord en sursis de 30 jours : le tambour se fissure mais reste debout. Les stocks périmés, eux, n\u0026rsquo;ont pas de sursis. Ce compromis est sorti du premier conseil : un stock non renouvelé n\u0026rsquo;existe pas, mais une chute brutale fait abandonner l\u0026rsquo;outil.\nLe foyer fictif monte au niveau 3, retombe au niveau 1 quand une grand-mère sous traitement le rejoint (de nouveaux objectifs deviennent requis), puis remonte quand elle repart. 279 objectifs adaptés au foyer # Les objectifs sont répartis sur 14 axes : eau, alimentation, énergie, santé, abri, sécurité, communication, mobilité, compétences, finances, communauté, moral, défense personnelle et cybersécurité. Chacun a un niveau, une explication (« pourquoi c\u0026rsquo;est important ») et une nature : une action cochée à la main, une compétence à revalider, ou un stock calculé depuis l\u0026rsquo;inventaire.\nUn objectif ne s\u0026rsquo;affiche qu\u0026rsquo;aux foyers qu\u0026rsquo;il concerne :\nle lieu de vie : un appartement n\u0026rsquo;a pas à faire de feu de camp ni à stocker du carburant à la cave, mais doit savoir utiliser un réchaud sans s\u0026rsquo;intoxiquer ; les membres : enfant, bébé, senior, animal ; les particularités de santé : douze au total, des lunettes au traitement à délivrance limitée. Pour les lunettes, une paire de secours et une ordonnance copiée au niveau 2, une paire dans le sac d\u0026rsquo;évacuation au niveau 3, deux paires et un kit de réparation au niveau 5 ; les risques déclarés : inondation, froid, canicule, risque industriel. Les épreuves physiques (marcher 15 km avec un sac, savoir nager) ont une variante adaptée pour les foyers dont un membre a une mobilité réduite : l\u0026rsquo;exigence porte sur la capacité du foyer, pas sur la forme de chacun.\nFiltres par niveau, axe et statut. Chaque objectif dit d\u0026rsquo;où vient sa validation (interview ou action) et sa date. À l\u0026rsquo;inscription, un questionnaire adaptatif de 26 questions décrit le foyer, son lieu de vie actuel et visé (« une maison à la campagne dans deux ans »), ce qui est déjà en place et le niveau visé. L\u0026rsquo;application en déduit un niveau de départ. Les objectifs propres au lieu visé sont proposés à part, dans une section « Préparer la suite ».\nUn inventaire en jours d\u0026rsquo;autonomie # L\u0026rsquo;inventaire répond à une question : combien de jours tient le foyer ? Chaque article a une quantité, une date de péremption et un emplacement ; l\u0026rsquo;autonomie est la somme des articles non périmés divisée par le besoin journalier du foyer, calculé membre par membre (4 L d\u0026rsquo;eau par adulte, 2,5 L par enfant, 0,3 L pour un chat ; 2 200 kcal par adulte).\nLe matériel prend la forme de 99 cartes réparties en dix catégories, chacune avec cinq niveaux (rustique, commun, confirmé, épique, légendaire). Le niveau d\u0026rsquo;une carte dépend de sa qualité, de ses options, de la quantité, du contenu minimal (pour une trousse ou un sac) ou, pour une réserve, des jours d\u0026rsquo;autonomie. Une carte indique les objectifs qu\u0026rsquo;elle sert, mais posséder un objet ne valide rien : il faut le confirmer.\nLes cartes possédées sont en couleur, avec leur niveau ; les autres restent grisées. Les cartes qui ne concernent pas le foyer sont rangées derrière « Voir aussi ». La réserve d\u0026rsquo;eau : son niveau découle de l\u0026rsquo;autonomie (25 jours), et la fiche liste les objectifs qu\u0026rsquo;elle sert, validés ou à faire. L\u0026rsquo;état des lieux résume l\u0026rsquo;inventaire : le maillon faible, l\u0026rsquo;autonomie par catégorie face aux repères de 3 jours, 14 jours, 3 mois et 6 mois, et une projection sur douze mois qui montre quand l\u0026rsquo;autonomie chute au fil des péremptions, sans rien consommer ni racheter.\nLe maillon faible est le combustible de cuisson (15 jours). Dans la projection, l\u0026rsquo;eau tombe à zéro en août 2027, quand les packs périment. Neuf arbres de compétences # Les compétences sont rangées en neuf spécialités (condition physique, soigneur, bricoleur, cuisinier, protecteur, rassembleur…), chacune un arbre de talents en cinq rangs et trois branches. Un talent se coche quand ses prérequis sont acquis, avec ce qui le prouve (un diplôme, une épreuve), une date et une note. Comme pour les objets, cocher un talent ne valide aucun objectif.\nLa spécialité Condition physique : trois talents acquis au rang Novice, trois disponibles au rang suivant. Un écran pour le jour J # Le bouton rouge « Crise » reste visible sur chaque page. Il ouvre un écran pensé pour une personne sous stress, qui fonctionne hors ligne et s\u0026rsquo;imprime :\nles boutons d\u0026rsquo;appel (15, 112, 18, 17, et le 114 par SMS) et un rappel de respiration ; les fiches réflexes, repliées : premiers secours (arrêt cardiaque, hémorragie, monoxyde de carbone, coup de chaleur…) puis situations (coupure d\u0026rsquo;eau, inondation, évacuation, intrusion…), filtrées selon les risques et les membres du foyer ; les fiches du foyer : contacts, points de rendez-vous, qui fait quoi, fiches médicales ; où est le matériel, d\u0026rsquo;après l\u0026rsquo;inventaire, et les réserves en eau de boisson seule. Les numéros d\u0026rsquo;urgence d\u0026rsquo;abord, puis les fiches de premiers secours et les situations qui concernent ce foyer. Sur le téléphone, sans réseau # L\u0026rsquo;application s\u0026rsquo;installe sur Android comme une application. Le moteur, le catalogue et les données vivent sur le téléphone : en mode avion, tout fonctionne. Le serveur du PC sert à l\u0026rsquo;installer, à synchroniser le téléphone et l\u0026rsquo;ordinateur, et de sauvegarde. Il est joignable par Tailscale, en HTTPS, à la maison comme dehors, sans rien exposer sur Internet.\nÀ gauche, l\u0026rsquo;accueil et sa barre d\u0026rsquo;onglets ; à droite, l\u0026rsquo;écran Crise avec la fiche Arrêt cardiaque ouverte. Comment c\u0026rsquo;est construit # Un journal d\u0026rsquo;opérations plutôt qu\u0026rsquo;un état # Rien n\u0026rsquo;est stocké tel quel : chaque modification est une opération immuable (valider un objectif, ajouter un article, changer le foyer), horodatée et identifiée. L\u0026rsquo;état du foyer, son niveau et tout son historique se reconstruisent en rejouant le journal dans l\u0026rsquo;ordre.\nflowchart LR T[\"TéléphonePWA + IndexedDB\"] --\u003e|opérations| S[\"Serveur du PCFastAPI + SQLite\"] P[\"Navigateur du PCPWA + IndexedDB\"] --\u003e|opérations| S S --\u003e|opérations des autres appareils| T S --\u003e|opérations des autres appareils| P T --\u003e M1[\"Moteur TypeScriptrejoue le journal\"] S -.-\u003e|lecture seule| M2[\"Moteur Pythonla référence\"] Ce choix règle trois problèmes d\u0026rsquo;un coup :\nla synchronisation : chaque appareil envoie les opérations que le serveur ne connaît pas et reçoit celles des autres. Deux modifications de la même chose se départagent par l\u0026rsquo;horodatage : la dernière écrite gagne ; l\u0026rsquo;historique : la courbe du niveau n\u0026rsquo;est pas enregistrée mais recalculée, y compris aux dates où un stock périme ou un sursis se termine, les seuls moments où le niveau change sans action ; les changements de règles : quand un conseil déplace un objectif d\u0026rsquo;un palier, le niveau se recalcule sur tout le journal, sans migration de données. Seule contrainte : un identifiant d\u0026rsquo;objectif ne se renomme jamais, puisqu\u0026rsquo;il est stocké. Le serveur ne calcule rien pour l\u0026rsquo;application : il garde la copie partagée du journal et sert les fichiers. Une sauvegarde JSON du journal s\u0026rsquo;exporte depuis l\u0026rsquo;onglet Foyer.\nDeux moteurs, un seul comportement # Pour fonctionner hors ligne, le moteur de niveaux doit tourner dans le téléphone, donc en TypeScript. Mais le moteur Python est plus simple à tester et à faire relire. Les deux existent, et le Python fait foi : un script génère depuis lui des cas de test (foyers, journaux, dates), que les tests du front rejouent pour vérifier que le moteur TypeScript donne exactement le même résultat. Un test échoue si les cas n\u0026rsquo;ont pas été régénérés après un changement du catalogue.\nLe contenu hors du code # Les objectifs, les besoins journaliers, le questionnaire, les cartes d\u0026rsquo;objets, les talents et les fiches Crise vivent dans des fichiers YAML, pas dans le code. Ajouter un objectif, c\u0026rsquo;est ajouter une entrée :\n- id: eau.exemple level: 4 # palier kind: check # check | skill | stock required: true # false : bonus, ne bloque pas le palier contexts: [periurbain, rural] revalidate_days: 365 # expire et fait baisser le niveau title: … why: … Le catalogue est validé au démarrage (références, niveaux, ordre des niveaux d\u0026rsquo;un objet), et un test vérifie que chaque lieu de vie garde au moins un objectif requis par palier, pour qu\u0026rsquo;aucun foyer n\u0026rsquo;atteigne le sommet sur des paliers vides.\nUn catalogue relu en conseil # Le catalogue est la partie la plus délicate : un critère mal placé décourage, un contenu faux peut être dangereux. Je l\u0026rsquo;ai fait relire par un conseil de trois agents IA, chacun avec un angle : le Pisteur (autonomie en milieu rural), le Tacticien (crise urbaine et logistique) et la Sentinelle (santé, publics vulnérables, psychologie). Chacun analyse seul, sans connaître l\u0026rsquo;avis des autres ; l\u0026rsquo;agent principal vérifie leurs constats dans le code, mène le débat et rédige une synthèse que je valide ou non. Chaque conseil a son compte rendu daté dans le dépôt.\nQuelques corrections qui en sont sorties :\nConstat Décision Un feu de camp exigé en appartement Feu et carburant réservés au périurbain et au rural, réchaud sûr en ville Les épreuves physiques bloquaient à vie un foyer avec un membre à mobilité réduite Une variante adaptée, l\u0026rsquo;exigence reste pour les autres Le niveau 3 comptait 27 critères requis, dont plusieurs sans rapport avec « tenir 72 h » Allégé à 19, le reste déplacé au niveau 4 Une formation PSC1 exigée au niveau 3, à réserver des semaines à l\u0026rsquo;avance « Gestes qui sauvent » (2 h, gratuit) au niveau 3, PSC1 au niveau 4 Les stocks estimés à l\u0026rsquo;inscription ne périmaient jamais Ils périment au bout de six mois s\u0026rsquo;ils ne sont pas confirmés Des pastilles à l\u0026rsquo;argent données pour purifier l\u0026rsquo;eau, un masque FFP2 contre les fumées Chlore ou dioxyde de chlore pour l\u0026rsquo;eau ; le FFP2 arrête les particules, ni le CO ni les gaz Limite connue : ces relectures sont faites par des agents IA, pas par des secouristes, des médecins ou des juristes. Les fiches de premiers secours reprennent des gestes publics et renvoient vers les formations officielles (Gestes qui sauvent, PSC1), mais le catalogue n\u0026rsquo;a pas été validé par un professionnel. L\u0026rsquo;application aide à s\u0026rsquo;organiser ; elle ne remplace ni une formation ni les consignes des autorités. La suite # Le conseil du 9 novembre est en cours de mise en œuvre : n\u0026rsquo;afficher les cartes d\u0026rsquo;armes, d\u0026rsquo;alcool et de tabac qu\u0026rsquo;aux foyers qui les déclarent (elles s\u0026rsquo;affichent encore à tous), retirer les doublons entre cartes qui proposent deux fois le même objectif, et corriger quelques contenus. Les pistes suivantes sont des rappels de revalidation, une fiche médicale structurée par membre, et un essai sur un petit serveur domestique. Le scénario de bout en bout (téléphone et PC, mode avion, synchronisation) existe dans le dépôt ; il reste à le faire tourner en intégration continue.\nEssayer # git clone https://github.com/gwils28/Shelterkwak cd Shelterkwak make install # dépendances Python (uv) et Node make check # lint, tests, parité des moteurs, build make run # l\u0026#39;application sur http://127.0.0.1:8000 L\u0026rsquo;installation sur un téléphone Android, avec Tailscale, est décrite dans docs/telephone.md.\n","date":"9 novembre 2026","externalUrl":null,"permalink":"/projects/shelterkwak/","section":"Projets","summary":"Application installable sur téléphone, sans compte ni service tiers : un référentiel de 279 objectifs en neuf paliers bloquants, un niveau qui baisse quand les stocks périment, un inventaire qui calcule les jours d’autonomie du foyer, et un écran Crise hors ligne pour le jour J.","title":"Shelterkwak : la préparation d'un foyer aux crises, suivie palier par palier","type":"projects"},{"content":"","date":"9 novembre 2026","externalUrl":null,"permalink":"/tags/sqlite/","section":"Tags","summary":"","title":"Sqlite","type":"tags"},{"content":"","date":"9 novembre 2026","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"9 novembre 2026","externalUrl":null,"permalink":"/tags/typescript/","section":"Tags","summary":"","title":"Typescript","type":"tags"},{"content":"","date":"8 novembre 2026","externalUrl":null,"permalink":"/tags/backtest/","section":"Tags","summary":"","title":"Backtest","type":"tags"},{"content":"","date":"8 novembre 2026","externalUrl":null,"permalink":"/tags/chronos-2/","section":"Tags","summary":"","title":"Chronos-2","type":"tags"},{"content":"","date":"8 novembre 2026","externalUrl":null,"permalink":"/tags/conformal-prediction/","section":"Tags","summary":"","title":"Conformal Prediction","type":"tags"},{"content":"","date":"8 novembre 2026","externalUrl":null,"permalink":"/tags/dagster/","section":"Tags","summary":"","title":"Dagster","type":"tags"},{"content":"","date":"8 novembre 2026","externalUrl":null,"permalink":"/tags/%C3%A9nergie/","section":"Tags","summary":"","title":"Énergie","type":"tags"},{"content":"","date":"8 novembre 2026","externalUrl":null,"permalink":"/en/tags/energy/","section":"Tags","summary":"","title":"Energy","type":"tags"},{"content":"","date":"8 novembre 2026","externalUrl":null,"permalink":"/tags/forecasting/","section":"Tags","summary":"","title":"Forecasting","type":"tags"},{"content":"","date":"8 novembre 2026","externalUrl":null,"permalink":"/en/tags/foundation-models/","section":"Tags","summary":"","title":"Foundation Models","type":"tags"},{"content":"","date":"8 novembre 2026","externalUrl":null,"permalink":"/tags/lightgbm/","section":"Tags","summary":"","title":"LightGBM","type":"tags"},{"content":"","date":"8 novembre 2026","externalUrl":null,"permalink":"/tags/mlflow/","section":"Tags","summary":"","title":"MLflow","type":"tags"},{"content":"","date":"8 novembre 2026","externalUrl":null,"permalink":"/tags/mod%C3%A8les-de-fondation/","section":"Tags","summary":"","title":"Modèles De Fondation","type":"tags"},{"content":" Passer de la question de veille, « quand un modèle de fondation bat-il un spécialiste ? », à celle d\u0026rsquo;un client : que nommer chaque veille à 9 h pour un portefeuille de 3 517 sites tertiaires, avec quels intervalles, et combien cela rapporte face à ce qu\u0026rsquo;il fait déjà. StatutTerminé · 5 blocs, service local, addendum du 16 octobre Période18 août – 8 octobre 2026, addendum du 16 au 31 octobre Stack Python 3.12LightGBMChronos-2TimestralTimesFM 3.0statsforecastDagsterMLflowFastAPIDockerQuartouvpytest Code source 2,7 %\nde MAPE sur le portefeuille\nmélange LightGBM + Chronos-2, contre 5,6 % pour la prévision RTE ramenée au portefeuille 1,10\ncontre RTE débiaisée\nratio de MAE au national, IC 95 % [0,99 ; 1,22] : une correction d\u0026rsquo;une ligne fait jeu égal avec tous les modèles −6,8 %\nde MASE en mélangeant\ncontre −1,8 % pour le meilleur router possible, connu a posteriori Note de transparence. Ces travaux ont été menés à titre personnel, dans un but d\u0026rsquo;apprentissage, avec l\u0026rsquo;assistance d\u0026rsquo;une IA :\npour le code, la relecture technique et méthodologique ; pour comprendre les outils et les méthodes mathématiques ; pour l\u0026rsquo;aide à la rédaction, la traduction et la correction du texte ; pour générer l\u0026rsquo;image de couverture de cette fiche. Le choix du sujet, le protocole, la vérification des résultats et les conclusions sont les miens, et j\u0026rsquo;en assume les erreurs. Comment j\u0026rsquo;utilise l\u0026rsquo;IA\nCe dépôt applique l\u0026rsquo;article Les modèles de fondation pour séries temporelles : zero-shot contre spécialiste à un cahier des charges client. L\u0026rsquo;article posait la question sur une seule série et reconstruisait un routeur ; ce projet la repose avec un vrai benchmark à battre, des covariables, une métrique en euros et un service à faire tourner. Le dépôt, son rapport en ligne et ses figures sont en français.\nL\u0026rsquo;objectif # FlexGrid Énergie est un responsable d\u0026rsquo;équilibre (BRP) fictif. Chaque veille, il doit nommer au pas de 30 minutes la consommation de son portefeuille pour le lendemain, avant la clôture du marché day-ahead à midi. Tout écart entre la nomination et le réalisé se règle au prix de l\u0026rsquo;équilibrage. Le brief annonçait une prévision actuelle à environ 6 % de MAPE et fixait un critère sans appel : battre la prévision J-1 de RTE publiée dans éCO2mix, sinon le projet n\u0026rsquo;a pas de valeur démontrable.\nLes données sont publiques et réelles : la consommation nationale éCO2mix et la prévision J-1 de RTE, les courbes de charge agrégées d\u0026rsquo;Enedis (sites de plus de 36 kVA, par région, tranche et secteur), les prévisions météo archivées d\u0026rsquo;Open-Meteo et les prix day-ahead d\u0026rsquo;Energy-Charts. L\u0026rsquo;année de test va du 1er juillet 2025 au 30 juin 2026 : 365 jours de livraison, une prévision émise chaque veille à 9 h avec uniquement ce qui est connu à cette heure-là.\nCe que je voulais apprendre tenait en trois questions :\nce que devient la conclusion de la veille (un TSFM zero-shot battait des spécialistes univariés) quand le spécialiste reçoit la météo et le calendrier ; comment juger un modèle contre un benchmark public quand ce benchmark a ses propres défauts ; ce qu\u0026rsquo;il faut ajouter à un backtest pour qu\u0026rsquo;il devienne un service qu\u0026rsquo;on peut surveiller. La démarche # Le travail suit les cinq blocs du brief, un dossier et un README de résultats chacun. Les décisions prises avant d\u0026rsquo;écrire du code sont dans SPEC.md, avec quatorze angles morts repérés dans les données ; le protocole d\u0026rsquo;évaluation est figé dans configs/protocol.yaml avant le premier calcul, et chaque écart est daté dans IMPLEMENTATION_NOTES.md. La méthode de travail est celle de Claude Code en data science : ce qui change dans ma méthode.\n1 Données Sources figées par sha256, transitions d\u0026rsquo;heure réparées par une règle explicite, horloge de nomination reconstituée : conso nationale connue jusqu\u0026rsquo;à 8 h, courbes des sites jusqu\u0026rsquo;à la fin de J-2, météo de J prévue 48 h avant. Le portefeuille est la somme de deux segments Enedis réels : 3 517 sites, 815 GWh par an. 2 Modèles et backtest Naïves, MSTL et ETS ; LightGBM quantile sur retards, calendrier et météo, réappris chaque semaine ; Chronos-2 avec et sans covariables ; le mélange des quantiles des deux. Origine glissante sur l\u0026rsquo;année de test, après six mois de validation. Verdicts pré-enregistrés contre RTE publiée et RTE débiaisée, sous deux lectures du quart d\u0026rsquo;heure. 3 Router et cold-start Un router par segment sur un panel de 81 segments tertiaires, appris sur la validation et comparé à l\u0026rsquo;oracle et au mélange. Des nouveaux portefeuilles simulés sur trois régions tenues à l\u0026rsquo;écart, avec 7 à 56 jours d\u0026rsquo;historique. 4 Du MW à l\u0026rsquo;euro Écart de nomination valorisé au prix day-ahead, avec plusieurs niveaux de pénalité, et coût évité avec un intervalle de confiance par blocs de sept jours. 5 Service et surveillance Dagster (une partition par jour de livraison, planning à 9 h la veille), registre MLflow, API FastAPI des quantiles et des nominations, image Docker, puis la surveillance rejouée sur l\u0026rsquo;année de test : dérive des données, dérive du concept, calibration, coût mensuel. Un addendum, écrit le 16 octobre avant toute prévision de ces modèles, a ajouté Timestral (Mistral AI) et TimesFM 3.0 (Google Research), sortis après la fin du projet.\nCe que les données ont corrigé au brief # Le brief dit Ce que les données disent « 5 000 sites tertiaires et des bornes de recharge » Aucune donnée publique de bornes au pas de 30 minutes. Le portefeuille devient deux segments Enedis réels (3 517 sites, 815 GWh/an), proches de l\u0026rsquo;ordre de grandeur demandé. « Battre la prévision J-1 de RTE » RTE ne prévoit que le national, et sa prévision sous-estime le réalisé d\u0026rsquo;environ 1,1 GW depuis 2023. Le critère devient : battre RTE publiée et RTE débiaisée. « Températures départementales » La température observée est une fuite : à 9 h la veille, on n\u0026rsquo;a qu\u0026rsquo;une prévision. Les verdicts utilisent la météo prévue 48 h avant. « Coût = volume × prix d\u0026rsquo;écart » Le prix de règlement des écarts n\u0026rsquo;est pas téléchargeable sans compte. Le coût est valorisé au prix day-ahead, avec un coefficient de pénalité de 1 (définition du brief) à 0,1. « Prévision actuelle à 6 % de MAPE » C\u0026rsquo;est presque exactement le MAPE de la prévision RTE ramenée au portefeuille par demi-heure (5,6 %) : celui d\u0026rsquo;une méthode sans modèle. Les résultats # Tous les chiffres viennent des tableaux versionnés dans results/, produits par le CLI flexgrid. Les intervalles de confiance sont des bootstraps par blocs de sept jours.\nLe benchmark avait un biais # De 2015 à 2021, la prévision J-1 de RTE a un MAPE de 1,4 à 1,6 %. Depuis 2023, il est d\u0026rsquo;environ 2,7 %, et la prévision sous-estime le réalisé d\u0026rsquo;environ 1 GW chaque année. Avant tout modèle, le bloc données a montré deux choses sur la référence. Son biais est stable, positif chaque mois de l\u0026rsquo;année de test : un BRP peut le corriger en une ligne, en retranchant l\u0026rsquo;erreur moyenne des 28 derniers jours connus pour chaque demi-heure. Et la façon de lire une prévision au quart d\u0026rsquo;heure sur un pas de 30 minutes déplace son MAE de 6,5 % (1 400 ou 1 310 MW) sans que la documentation tranche. Un modèle ne « bat RTE » ici que s\u0026rsquo;il bat les deux lectures.\nLes sources avaient aussi leurs pièges : éCO2mix publie des heures qui n\u0026rsquo;existent pas au passage à l\u0026rsquo;heure d\u0026rsquo;été (48 horodatages UTC en double sur 2015-2026) et omet l\u0026rsquo;heure dupliquée d\u0026rsquo;octobre chaque année.\nAu national : RTE publiée battue, RTE débiaisée non # Sous 1, le modèle fait mieux que la référence. À gauche, presque tous les modèles avec covariables battent RTE publiée. À droite, aucun ne bat RTE débiaisée ; le mélange LightGBM + Chronos-2 est à 1,10. Le mélange LightGBM + Chronos-2 a un MAE de 897 MW contre 1 401 MW pour RTE publiée (ratio 0,64, IC 95 % [0,56 ; 0,73]). Le critère du brief est rempli au sens littéral. Mais RTE débiaisée fait 816 MW : contre elle, le mélange ne montre pas de différence (1,10 [0,99 ; 1,22]) et perd sous l\u0026rsquo;autre lecture du quart d\u0026rsquo;heure (1,14 [1,02 ; 1,26]).\nNational, année de test MAE (MW) MAPE couverture 90 % RTE J-1 débiaisée 816 1,61 % (ponctuelle) Mélange LightGBM + Chronos-2 897 1,73 % 0,86 LightGBM 972 1,87 % 0,76 Chronos-2 + covariables 1 109 2,15 % 0,83 RTE J-1 publiée 1 401 2,79 % (ponctuelle) Chronos-2 sans covariables 1 752 3,33 % 0,81 MSTL 2 284 4,47 % 0,79 Trois lectures :\nLes covariables comptent plus que le modèle. Chronos-2 passe de 1 752 à 1 109 MW quand il reçoit la météo et le calendrier. En zero-shot sans covariables, un TSFM ne sait pas qu\u0026rsquo;il va faire froid demain. La météo parfaite n\u0026rsquo;aurait presque rien changé. Nourri avec la météo observée au lieu de la météo prévue, LightGBM ne gagne que 4 % de MAE. Ce qui sépare les modèles de RTE, c\u0026rsquo;est l\u0026rsquo;information sur la consommation elle-même : RTE révise sa prévision plus tard, avec sa propre télémesure. Le mélange gagne l\u0026rsquo;été, perd l\u0026rsquo;hiver. Il bat RTE débiaisée de juillet à octobre (592 contre 706 MW) et un jour sur deux sur l\u0026rsquo;année, mais ses mauvais jours sont des jours d\u0026rsquo;hiver, où les erreurs se comptent en GW. Sur le portefeuille : le modèle divise par deux les 6 % du brief # Sur le portefeuille, le mélange LightGBM + Chronos-2 fait 0,49 fois le MAE de RTE ramenée au portefeuille et 0,36 fois celui de la naïve hebdomadaire. RTE ne prévoit pas le portefeuille, donc pas de RTE débiaisée qui tienne. La meilleure méthode sans modèle est la prévision RTE multipliée par la part récente du portefeuille, par demi-heure. Ce benchmark a été ajouté après le protocole, et déclaré comme tel : sans lui, la seule référence du portefeuille était une naïve.\nPortefeuille, année de test MAE (MW) MAPE couverture 90 % Mélange LightGBM + Chronos-2 2,57 2,72 % 0,85 LightGBM 2,64 2,78 % 0,75 Chronos-2 + covariables 3,16 3,36 % 0,84 RTE J-1 au prorata 5,28 5,59 % (ponctuelle) Naïve hebdomadaire 7,17 7,55 % 0,87 L\u0026rsquo;écart est bien plus grand qu\u0026rsquo;au national parce que le portefeuille ne ressemble pas à la France : un pic de bureau vers 10 h, un dimanche plat, et une climatisation l\u0026rsquo;été que le national n\u0026rsquo;a presque pas. Une prévision nationale, même mise à l\u0026rsquo;échelle demi-heure par demi-heure, porte la forme du national. Le mélange bat RTE au prorata tous les mois et 86 % des jours.\nLes jours fériés, point faible des TSFM # Le 25 décembre, Chronos-2 et RTE au prorata prévoient une journée de travail. LightGBM, qui a vu dix ans de fériés à l\u0026rsquo;apprentissage, sait que les bureaux sont fermés. C\u0026rsquo;était déjà le point faible du zero-shot dans la veille, et il le reste avec un calendrier en covariable. Sur les onze jours fériés de l\u0026rsquo;année, LightGBM fait 0,42 fois le MAE de RTE au prorata, Chronos-2 1,25. Le mélange hérite donc des erreurs de Chronos-2 ces jours-là. Une règle « LightGBM seul les jours fériés » serait un gain facile, mais elle n\u0026rsquo;était pas pré-enregistrée : elle n\u0026rsquo;est pas appliquée. Sur les quatre ponts, tous les modèles perdent : un pont dans le tertiaire, c\u0026rsquo;est la moitié des bureaux fermés, ce qu\u0026rsquo;aucune variable du calendrier ne mesure.\nLes intervalles # Bruts, ils ne tiennent pas leur promesse. L\u0026rsquo;intervalle à 90 % de LightGBM couvre 75 % des pas sur le portefeuille, 76 % au national. Le SPEC attribuait cette sous-couverture au décalage entre la météo observée à l\u0026rsquo;apprentissage et la météo prévue en service ; la mesure l\u0026rsquo;a démentie (77 % avec la météo observée en entrée). La cause principale est la régression quantile, qui s\u0026rsquo;ajuste trop bien à ses données d\u0026rsquo;apprentissage.\nLa réparation conforme de la veille (médiane conservée, quantiles reconstruits sur les erreurs des 56 derniers jours) ramène tous les modèles à 0,90-0,91 sur l\u0026rsquo;année. Elle suit les saisons avec retard : la couverture réparée du mélange tombe à 0,78 en novembre, quand l\u0026rsquo;erreur monte avec le chauffage.\nMélanger plutôt que router # MASE moyen de chaque politique divisé par celui de « toujours LightGBM ». Le router appris gagne 0,4 %, l\u0026rsquo;oracle 1,8 %, le mélange 6,8 %. Sous le tiret, les politiques de l\u0026rsquo;addendum ; le dernier mélange donne ½ à LightGBM et ⅙ à chacun des trois TSFM. Sur les 81 segments du panel, un stump (une variable, un seuil) appris sur la validation n\u0026rsquo;envoie qu\u0026rsquo;un seul segment vers Chronos-2. Même le router oracle, qui connaît la bonne réponse, ne gagne que 1,8 % : LightGBM a le MASE le plus bas sur 66 segments sur 81, et là où Chronos-2 gagne (les segments chauffés à l\u0026rsquo;électricité, les plus gros sites), il gagne peu. Le mélange gagne 6,8 % et bat l\u0026rsquo;oracle sur 70 segments. Deux modèles qui se trompent à des moments différents se corrigent dans le temps ; un router ne choisit qu\u0026rsquo;une fois par segment.\nLa décision dérive aussi, comme dans la veille : réappliqué chaque semaine, le stump envoie de 0 à 50 segments vers Chronos-2 selon la saison, et 4,3 segments changent de branche par semaine en moyenne. Le service nomme donc le mélange, qui n\u0026rsquo;a pas de décision à surveiller.\nUn nouveau portefeuille se nomme d\u0026rsquo;abord à la naïve # À 7 jours d\u0026rsquo;historique, la naïve hebdomadaire (vert) bat presque tout ; seul Timestral avec les segments frères en contexte passe dessous. Dès 14 jours, les modèles convergent sous la naïve. WAPE 7 jours 14 jours 28 jours 56 jours Naïve hebdomadaire 6,6 % 6,6 % 6,6 % 6,6 % LightGBM appris sur les autres régions 12,6 % 3,9 % 3,9 % 3,9 % Chronos-2 + covariables 19,2 % 4,4 % 3,9 % 3,6 % Chronos-2 few-shot in-context 19,7 % 4,4 % 4,0 % 3,8 % Timestral few-shot (addendum) 5,7 % 4,2 % 4,0 % 3,8 % Avec une semaine d\u0026rsquo;historique, Chronos-2 ne voit qu\u0026rsquo;un cycle hebdomadaire, ne le reconnaît pas comme une saison et prévoit un mardi à mi-chemin entre un jour ouvré et un dimanche. LightGBM reçoit un retard de 14 jours manquant, qu\u0026rsquo;il n\u0026rsquo;a jamais vu à l\u0026rsquo;apprentissage. La naïve, elle, recopie le bon jour. Dès 14 jours, LightGBM appris sur les autres régions passe devant, à 0,5 point de ce qu\u0026rsquo;il fait avec tout l\u0026rsquo;historique. Le few-shot in-context de Chronos-2, que le brief proposait pour le cold-start, n\u0026rsquo;aide pas. Celui de Timestral est le premier modèle sous la naïve à 7 jours, sans qu\u0026rsquo;on puisse conclure qu\u0026rsquo;il la bat (ratio 0,87 [0,73 ; 1,04]).\nLa règle qui en sort : la naïve pendant deux semaines, puis le modèle global. Une limite : un segment agrège des centaines de sites. Ce qui est simulé, c\u0026rsquo;est l\u0026rsquo;arrivée d\u0026rsquo;un nouveau portefeuille, pas d\u0026rsquo;un site isolé, bien plus bruité.\nTimestral et TimesFM 3.0 : le contexte compte plus que le modèle # MAE divisé par celui de Chronos-2 avec météo et calendrier. À contexte égal, ni Timestral ni TimesFM 3.0 ne font mieux ; avec presque deux ans de contexte, Timestral passe devant. L\u0026rsquo;addendum posait trois questions, avec les mêmes origines, les mêmes entrées et les réglages publiés par défaut :\nUn nouveau TSFM bat-il Chronos-2 ? À contexte égal (2 048 pas), non : Timestral fait 1,18 fois son MAE au national, TimesFM 3.0 1,19. Sans covariables, les trois se valent : l\u0026rsquo;écart vient de la façon dont chacun lit les covariables. Avec tout son contexte (32 768 pas, presque deux ans), Timestral passe devant : 990 MW, 0,89 fois Chronos-2. Plus de contexte n\u0026rsquo;aide ni Chronos-2 ni TimesFM 3.0. Le calendrier en covariable aide-t-il les fériés ? Oui, il retire environ un quart de l\u0026rsquo;erreur de Chronos-2 et de Timestral ces jours-là, presque rien à TimesFM 3.0. Les TSFM restent loin de LightGBM : une covariable binaire « férié » ne remplace pas dix ans de fériés vus à l\u0026rsquo;apprentissage. Les nouveaux TSFM améliorent-ils le mélange ? Non. Deux mélanges passent la règle de bascule pré-enregistrée sur la validation, aucun n\u0026rsquo;est confirmé sur l\u0026rsquo;année de test. Le service garde LightGBM + Chronos-2. Un résultat exploratoire, déclaré comme tel : le mélange LightGBM + Timestral à contexte long, construit après avoir vu les résultats, est le meilleur modèle du dépôt. Au national, il fait 832 MW et jeu égal avec RTE débiaisée (1,02 [0,92 ; 1,13]). Choisi en regardant l\u0026rsquo;année de test, ce chiffre est optimiste ; il ne compte dans aucun verdict et sera candidat d\u0026rsquo;un prochain protocole, sur une fenêtre qu\u0026rsquo;il n\u0026rsquo;a pas vue.\nDu MW à l\u0026rsquo;euro : le coût ne suit pas l\u0026rsquo;erreur # L\u0026rsquo;erreur du mélange est étalée sur la journée de bureau ; son coût se concentre sur les deux bosses du prix, vers 8 h et 19 h. Le creux solaire de midi rend bon marché une erreur de mi-journée. Sous la définition du brief (écart × prix day-ahead), le mélange évite 1,24 M€ par an face à RTE au prorata (IC 95 % [0,93 ; 1,56] M€), soit 0,12 à 0,62 M€ avec une pénalité plus réaliste. Le classement des modèles ne dépend pas de la pénalité, seuls les montants en dépendent. L\u0026rsquo;intervalle est large parce qu\u0026rsquo;une année ne contient qu\u0026rsquo;un hiver : de novembre à janvier, trois mois font 47 % du coût annuel.\nLe coût n\u0026rsquo;est pas un MAE déguisé. RTE au prorata se trompe surtout en milieu de journée, aux heures où le prix est bas. Le mélange évite 51 % de l\u0026rsquo;énergie en écart mais seulement 47 % du coût. Un modèle qui viserait le coût plutôt que le MAE pondérerait ses erreurs par le prix attendu : ce n\u0026rsquo;est pas fait ici, c\u0026rsquo;est la suite naturelle.\nLe service et sa surveillance # Une partition Dagster par jour de livraison, planifiée à 9 h la veille. LightGBM est réappris chaque semaine et enregistré dans MLflow ; Chronos-2 tourne sur GPU, avec un repli sur LightGBM au-delà de 300 secondes. Trois partitions ont été matérialisées de bout en bout, dont les deux jours de changement d\u0026rsquo;heure (46 et 50 pas). La production reproduit le backtest : mêmes quantiles pour Chronos-2, et pour LightGBM le jour où le service et le backtest réapprennent à la même date. C\u0026rsquo;est le contrôle qui relie les verdicts au code servi.\nLa surveillance a été rejouée sur l\u0026rsquo;année de test, et elle le dit : il n\u0026rsquo;y a pas eu de vraie mise en service. Deux constats en sortent. Le seuil d\u0026rsquo;alerte naïf, « le modèle fait moins bien que RTE débiaisée », sonne 208 jours sur 365 au national : il décrit un état connu depuis le backtest et ne détecte rien. Le seuil retenu est auto-référencé (1,15 fois le ratio du modèle sur la validation). Et la couverture brute de LightGBM descend à 0,52 sur le portefeuille en janvier : le « 90 % qui dérive à 70 % » du brief s\u0026rsquo;est produit, ce que la réparation conforme rattrape avec retard.\nCe qui a coincé # Le benchmark était la première question. Le critère « battre RTE » paraissait fixé. Le biais de +1,1 GW et l\u0026rsquo;ambiguïté du quart d\u0026rsquo;heure l\u0026rsquo;ont transformé en deux benchmarks et deux lectures, et la conclusion a changé de signe entre RTE publiée et RTE débiaisée. Les fuites viennent de l\u0026rsquo;horloge. Un retard de deux jours compté en pas UTC lisait, le jour du passage à l\u0026rsquo;heure d\u0026rsquo;hiver, une valeur postérieure au cutoff des segments ; un test l\u0026rsquo;a trouvé avant tout calcul. Plus tard, un backfill du 26 octobre 2025 a réutilisé un modèle du registre appris pour le 30 juin 2026, huit mois dans le futur. Une règle testée l\u0026rsquo;empêche désormais. Une règle de contrôle retirait de vraies valeurs. Le filtre de plage pré-enregistré supprimait 7 399 pas réels d\u0026rsquo;un segment chauffé à l\u0026rsquo;électricité, dont l\u0026rsquo;hiver vaut six fois l\u0026rsquo;été. Il a été remplacé avant toute prévision, et l\u0026rsquo;écart est daté. Le calcul. Le backtest complet prend plusieurs heures sur une RTX 5070 et 32 cœurs, dont 4 h 30 pour LightGBM sur le panel, et l\u0026rsquo;addendum ajoute environ 9 h de GPU. Deux calculs LightGBM lancés en même temps multipliaient la durée par cinq : les calculs lourds passent l\u0026rsquo;un après l\u0026rsquo;autre. L\u0026rsquo;image Docker pèse 6,9 Go compressés, presque tout pour torch et CUDA, alors que l\u0026rsquo;API seule n\u0026rsquo;en a pas besoin. Une image séparée pour l\u0026rsquo;API est la prochaine étape. Ce que j\u0026rsquo;en retiens # La conclusion de la veille ne se transpose pas telle quelle. Contre des spécialistes univariés, Chronos-2 gagnait partout ; contre un LightGBM nourri de météo et de calendrier, il perd sur 66 segments sur 81. Ce qui fait la différence, c\u0026rsquo;est ce que chaque modèle reçoit en entrée. « Battre le benchmark » se vérifie contre la version corrigée du benchmark. Une correction de biais d\u0026rsquo;une ligne a fait jeu égal avec tous mes modèles au national. Sur le portefeuille, où elle n\u0026rsquo;existe pas, l\u0026rsquo;écart est réel : 2,7 % contre 5,6 %. Entre le spécialiste et le TSFM, la meilleure réponse a été « les deux ». Le mélange bat le meilleur router possible, et il retire une décision à surveiller. Les fériés restent le point faible des TSFM, même avec le calendrier en covariable. Un modèle qui en a vu dix ans fait mieux. Un rejeu honnête dit ce qu\u0026rsquo;il ne montre pas : les seuils d\u0026rsquo;alerte ont été fixés après avoir vu la saturation du seuil naïf, et un vrai monitoring les fixerait sur la validation pour un an. Reproduire # git clone https://github.com/gwils28/flexgrid_dayahead cd flexgrid_dayahead uv sync --extra prod # Python 3.12 ; Chronos-2 tourne sur GPU (un CPU suffit, en beaucoup plus lent) make data # télécharge et fige les sources (sha256 dans data/MANIFEST.json) uv run flexgrid build # tables intermédiaires uv run flexgrid forecast national snaive_day snaive_week mstl ets_seasonal lgbm chronos2 chronos2_cov uv run flexgrid forecast segments snaive_day snaive_week mstl ets_seasonal lgbm chronos2 chronos2_cov uv run flexgrid score national segments \u0026amp;\u0026amp; uv run flexgrid tables uv run flexgrid router \u0026amp;\u0026amp; uv run flexgrid coldstart make figures report # figures de tous les blocs, puis le rapport Quarto dans docs/ uv run pytest Les modèles de l\u0026rsquo;addendum se lancent de la même façon (liste dans configs/protocol.yaml). Les prévisions brutes ne sont pas versionnées ; les tableaux par origine le sont, et toutes les figures et tous les verdicts en partent. Licences : Chronos-2 Apache-2.0 ; Timestral, code Apache-2.0 et poids sous licence MIT modifiée ; TimesFM 3.0, poids non commerciaux, qu\u0026rsquo;un vrai BRP ne pourrait pas servir sans autre licence.\n","date":"8 novembre 2026","externalUrl":null,"permalink":"/projects/flexgrid-prevision-j1/","section":"Projets","summary":"La veille TSFM appliquée à un responsable d’équilibre fictif, sur données publiques réelles : LightGBM, Chronos-2 et leur mélange contre la prévision J-1 de RTE, publiée et débiaisée. Un MAPE de 2,7 % sur le portefeuille contre 5,6 % sans modèle, un router qui ne bat pas le mélange, un cold-start à la naïve, Timestral et TimesFM 3.0 en addendum, un service Dagster et sa surveillance rejouée.","title":"Modèles de fondation pour séries temporelles : prévoir la consommation électrique du lendemain de 3 517 bâtiments tertiaires","type":"projects"},{"content":"","date":"8 novembre 2026","externalUrl":null,"permalink":"/tags/s%C3%A9ries-temporelles/","section":"Tags","summary":"","title":"Séries Temporelles","type":"tags"},{"content":"","date":"8 novembre 2026","externalUrl":null,"permalink":"/en/tags/time-series/","section":"Tags","summary":"","title":"Time Series","type":"tags"},{"content":"","date":"6 novembre 2026","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"6 novembre 2026","externalUrl":null,"permalink":"/tags/chronos/","section":"Tags","summary":"","title":"Chronos","type":"tags"},{"content":"","date":"6 novembre 2026","externalUrl":null,"permalink":"/tags/intervalles-de-pr%C3%A9vision/","section":"Tags","summary":"","title":"Intervalles De Prévision","type":"tags"},{"content":" Un modèle de fondation pour séries temporelles prévoit une série qu\u0026rsquo;il n\u0026rsquo;a jamais vue, sans entraînement. Un modèle statistique classique est réajusté sur chaque série, à chaque prévision. Lequel l\u0026rsquo;emporte, sur quel horizon, quels jours, et avec des intervalles auxquels on peut se fier ? J\u0026rsquo;ai posé la question sur un an de consommation électrique française, heure par heure. Temps de lecture : environ 30 minutes. Les formules sont suivies de leur intuition : on peut les survoler sans perdre le fil. Le dépôt est en anglais, ses figures aussi.\nNote de transparence. Ces travaux ont été menés à titre personnel, dans un but d\u0026rsquo;apprentissage, avec l\u0026rsquo;assistance d\u0026rsquo;une IA :\npour le code, la relecture technique et méthodologique ; pour comprendre les outils et les méthodes mathématiques ; pour l\u0026rsquo;aide à la rédaction, la traduction et la correction du texte ; pour générer l\u0026rsquo;image de couverture de l\u0026rsquo;article. Le choix du sujet, le protocole, la vérification des résultats et les conclusions sont les miens, et j\u0026rsquo;en assume les erreurs. Comment j\u0026rsquo;utilise l\u0026rsquo;IA\nCet article restitue deux semaines de veille active (du 2 au 15 août 2026) sur les modèles de fondation pour séries temporelles, abrégés TSFM (time series foundation models). Le plan tenait en quatre blocs : trois ressources récentes à lire à la source, un banc de backtest opposant un TSFM utilisé tel quel à des modèles statistiques solides, une décision d\u0026rsquo;architecture pour le « routeur de complexité » d\u0026rsquo;un article de mai 2026, et l\u0026rsquo;article fondateur du MASE à relire. Son critère de réussite : dire en une phrase sur quel horizon et quel régime le TSFM gagne ou perd, en s\u0026rsquo;appuyant sur la couverture des intervalles et pas seulement sur l\u0026rsquo;erreur moyenne. Tout est reproductible dans le dépôt W25_tsfm_viability.\nEn bref. Les principaux résultats, avec leur périmètre :\nSur la charge française de 2019, Chronos-Bolt base bat le modèle de référence à chaque pas d\u0026rsquo;horizon et chaque type de jour ordinaire, sans avoir jamais vu la série. Contre le spécialiste le plus solide (MSTL), l\u0026rsquo;écart est plus modeste : 15 % d\u0026rsquo;erreur en moins pour Bolt base, 24 % pour Chronos-2. L\u0026rsquo;intervalle « à 90 % » de Chronos-Bolt est son intervalle à 80 %, par construction. Une réparation conforme, à partir de ses propres erreurs passées, le ramène à 0,902. Les jours fériés sont l\u0026rsquo;endroit où le zero-shot perd : son intervalle à 90 % y couvre 37 % des heures. Avec le calendrier en covariable (exploratoire), Chronos-2 y divise son erreur par deux. Un intervalle bien calibré n\u0026rsquo;est pas forcément utile : la référence, qui prévoit une ligne plate, couvre 0,899 des heures avec un intervalle large de 45 % de la charge. Sur 50 séries européennes, Chronos-2 gagne sur les 200 unités (série × longueur d\u0026rsquo;historique), surtout en démarrage à froid. Un routeur ne peut alors qu\u0026rsquo;échanger de la précision contre un coût, et le coût mesuré ne justifie pas cet échange. La décision de routage dérive : 40 séries sur 50 changent de branche dans l\u0026rsquo;année. 1. Le problème : un modèle par série, ou un modèle pour toutes # La prévision de séries temporelles repose classiquement sur un modèle par série : on choisit une famille (lissage exponentiel, ARIMA, décomposition saisonnière), on l\u0026rsquo;ajuste sur l\u0026rsquo;historique et on la réajuste quand les données arrivent. J\u0026rsquo;appelle ce modèle le spécialiste : il ne connaît que sa série, mais il la connaît bien.\nUn modèle de fondation est pré-entraîné une fois, sur un grand corpus de séries hétérogènes (énergie, trafic, ventes, météo, données synthétiques), pour apprendre ce qu\u0026rsquo;elles ont en commun : saisonnalités, tendances, niveaux qui bougent. À l\u0026rsquo;usage, on lui donne le passé récent d\u0026rsquo;une série nouvelle (son contexte) et il prévoit la suite sans aucun ajustement : c\u0026rsquo;est le zero-shot. Le même modèle sert tout le portefeuille.\nLes deux modèles étudiés viennent de la famille Chronos d\u0026rsquo;Amazon :\nChronos-Bolt (tailles small et base) découpe le contexte en morceaux de quelques pas et prévoit directement les 24 heures suivantes sous la forme de neuf quantiles, de 0,1 à 0,9. Il ne voit que la série. Chronos-2, plus récent, prévoit 21 quantiles de 0,01 à 0,99 et accepte des covariables : des séries connues dans le passé, ou connues à l\u0026rsquo;avance, comme un calendrier. Un quantile 0,9 est la valeur sous laquelle le modèle s\u0026rsquo;attend à voir la réalité 90 % du temps. Entre les quantiles 0,05 et 0,95, on a un intervalle de prévision à 90 % : bien calibré, il contient la réalité 9 heures sur 10. C\u0026rsquo;est cette promesse, autant que l\u0026rsquo;erreur moyenne, que le plan demandait de vérifier.\n2. Ce que disent les sources # J\u0026rsquo;ai relu les trois ressources du plan à la source (articles, fiches des modèles, code publié, résultats du classement GIFT-Eval recalculés), dernière vérification le 30 octobre 2026.\nAffirmation du plan Ce que dit la source Verdict Les trois modèles de prévision d\u0026rsquo;IBM (mars 2026) émettent des quantiles nativement Vrai pour FlowState r1.1 (9 quantiles), TTM-r3 (nouvelle tête multi-quantiles) et PatchTST-FM r1 (99 quantiles). Seul TTM est nouveau sur ce point tient Le leader de GIFT-Eval en prévision ponctuelle ne fait que 18,5 M de paramètres Le 12 avril 2026, FlowState r1.1 était premier au MASE seulement avec les filtres « zero-shot » et « code de réplication » ; sans filtre, 13ᵉ. Le 30 octobre 2026, il est 13ᵉ même filtré en partie, et daté Le routeur de complexité (arXiv:2605.24381) donne une meilleure précision pour un coût bien moindre qu\u0026rsquo;un TSFM universel Le chiffre de tête classe les séries par leur MASE de test réalisé : un oracle. La règle déployée, dans le dépôt des auteurs, fait 0,981 contre 0,989 pour le TSFM seul, avec 33 % d\u0026rsquo;appels en moins. Le ratio de coût (1 000 pour 1) est supposé en partie Les spécialistes gagnent sur les systèmes à contraintes physiques Le régime « énergie » du papier est ETTh1 : la température d\u0026rsquo;huile de 7 transformateurs. Aucune série de charge ne se transpose pas L\u0026rsquo;article de blog classe les limites des TSFM en contournables, non contournables et ouvertes Publié le 24 avril 2026. Deux groupes seulement : les vrais défis (perte, contexte, biais haute fréquence, lois d\u0026rsquo;échelle, covariables) et les faux problèmes (interprétabilité, fuite de données) en partie Le troisième point a orienté la suite. Le routeur de complexité du papier calcule quatre indicateurs sur l\u0026rsquo;historique de chaque série (entropie spectrale, coefficient de variation, autocorrélation saisonnière, part de tendance) ; la série va au TSFM quand au moins deux des quatre seuils sont franchis, sinon à un spécialiste.\nÀ gauche, le MASE sur Traffic et M4 Daily : le routeur tel que déployé (0,981) fait à peu près comme le TSFM seul (0,989) ; le 0,964 n\u0026rsquo;est atteint qu\u0026rsquo;en classant les séries d\u0026rsquo;après leur erreur de test. À droite, la part d\u0026rsquo;appels au TSFM évités : 70 % pour l\u0026rsquo;oracle, 33 % pour la règle déployée. Un oracle utilise une information indisponible au moment de décider, ici l\u0026rsquo;erreur que chaque modèle fera sur le test. Il borne ce qu\u0026rsquo;un routeur parfait gagnerait, pas ce qu\u0026rsquo;un routeur réel gagne. D\u0026rsquo;où une règle pour le bloc 3 : évaluer le routeur hors échantillon dès le départ.\nDeux autres vérifications. La contamination : la liste d\u0026rsquo;entraînement de Chronos-2 ne contient ni ENTSO-E ni OPSD, les sources de ma série ; celle de Chronos-Bolt n\u0026rsquo;est pas détaillée, et pourrait contenir la charge espagnole de 2015-2018 (même fournisseur, autre pays). Rien ne dit que le score de Bolt est optimiste, rien ne permet de l\u0026rsquo;exclure. Les licences : les modèles Chronos sont sous Apache-2.0 ; TTM-r3 d\u0026rsquo;IBM, sous CC-BY-NC-SA-4.0, ne peut pas entrer dans un livrable commercial.\n3. La série et ses pièges # La série est la consommation électrique nationale française, heure par heure, publiée par ENTSO-E et reprise par Open Power System Data (version du 6 octobre 2020, empreinte vérifiée).\nMoyenne journalière et étendue min-max. Les pics d\u0026rsquo;hiver viennent du chauffage électrique. En bleu, l\u0026rsquo;année de test (365 origines) ; en rouge, la fenêtre du premier confinement, traitée à part. Au-delà du cycle annuel dû au chauffage, la charge superpose deux cycles : 24 heures (nuit, montée du matin, plateau, pointe du soir) et 168 heures (cinq jours ouvrés, un week-end plus bas). Un modèle qui n\u0026rsquo;a que l\u0026rsquo;un des deux se trompe de façon systématique.\nÀ gauche, le profil journalier moyen par jour de la semaine (2017-2018, heure de Paris) : les jours ouvrés se ressemblent, le samedi et le dimanche sont nettement plus bas. À droite, deux semaines de janvier 2018 : le cycle de 24 heures et le creux du week-end. Les défauts trouvés avant de modéliser # À gauche, 9 trous de 1 à 13 heures, 42 heures sur 50 398. À droite, une heure à 158 GW le 7 juillet 2020, au-dessus de toute valeur physiquement possible. Une heure à 158 GW, pour un record national d\u0026rsquo;environ 102 GW, et neuf trous. Les valeurs hors de [20 ; 110] GW sont mises à manquant. Les trous sont comblés pour les entrées des modèles seulement (linéairement jusqu\u0026rsquo;à 3 heures, sinon par la semaine précédente), et une heure imputée n\u0026rsquo;est jamais notée : un modèle ne doit pas être récompensé pour avoir retrouvé une valeur inventée.\nDeux naïfs plutôt qu\u0026rsquo;un # Le plan demandait comme base le SeasonalNaive de saison 24 : demain est la copie d\u0026rsquo;aujourd\u0026rsquo;hui. Il prévoit donc le lundi avec le dimanche, et je m\u0026rsquo;attendais à un homme de paille. J\u0026rsquo;ai ajouté le naïf hebdomadaire (la copie de la semaine dernière). La mesure m\u0026rsquo;a donné tort.\nÀ gauche, un lundi : la copie du dimanche (vert) rate le niveau d\u0026rsquo;un jour ouvré, la copie du lundi précédent (orange) est trop haute. À droite, l\u0026rsquo;erreur absolue moyenne par jour de la semaine sur 2017-2018 : aucun naïf ne domine. Le naïf journalier perd nettement le lundi et le samedi, les jours qui suivent un changement de régime ; les cinq autres jours, hier ressemble plus à aujourd\u0026rsquo;hui que la semaine dernière. Les deux restent dans le banc.\n4. Le protocole : un backtest à origine glissante # Le backtest à origine glissante (rolling origin) rejoue l\u0026rsquo;histoire : on se place à une date passée (l\u0026rsquo;origine), on ne donne aux modèles que les données antérieures, on prévoit les 24 heures suivantes, on compare à la réalité, puis on avance d\u0026rsquo;un jour. Le plan demandait au moins 6 origines ; j\u0026rsquo;en ai pris une par jour sur 2019, soit 365, plus une fenêtre de stress à part pendant le premier confinement (55 origines).\nÀ chaque origine, les modèles statistiques sont réajustés sur leur fenêtre, les naïfs copient un jour ou une semaine, et les modèles Chronos lisent 2 048 heures de contexte sans rien ajuster. Les 24 heures suivantes sont notées. Modèle Ajustement Fenêtre Intervalles SeasonalNaive(24), SeasonalNaive(168) aucun dernier jour, dernière semaine à partir des résidus AutoETS, saison 24 réajusté à chaque origine, sélection par AICc 672 h (4 semaines) issus du modèle ETS(Z,Z,A), saisonnalité imposée (écart déclaré) réajusté 672 h issus du modèle MSTL [24, 168] + tendance AutoETS réajusté à chaque origine 1 344 h (8 semaines) issus du modèle Chronos-Bolt small, base zero-shot contexte de 2 048 h quantiles 0,1 à 0,9 Chronos-2 zero-shot, une série à la fois contexte de 2 048 h quantiles 0,01 à 0,99 ETS est la famille des lissages exponentiels : un niveau, éventuellement une tendance et une saisonnalité, mis à jour en pondérant davantage le récent ; AutoETS choisit la combinaison par un critère d\u0026rsquo;information. MSTL décompose la série en plusieurs saisonnalités (24 et 168 heures) et une tendance, prévoit chaque morceau et les recompose : c\u0026rsquo;est le seul modèle statistique du banc qui connaît les deux cycles.\nLe protocole, les seuils de verdict et la règle d\u0026rsquo;incertitude ont été commités avant la première prévision. Toute valeur changée après coup est un écart, déclaré partout où il apparaît (il y en a eu un). Des tests corrompent les valeurs futures et vérifient que les prévisions ne bougent pas : aucune fuite de l\u0026rsquo;avenir.\nMesurer l\u0026rsquo;incertitude d\u0026rsquo;un écart # « Chronos fait 15 % d\u0026rsquo;erreur en moins que MSTL » : sur une autre année, ce serait peut-être 5 % ou 25 %. Le bootstrap donne un intervalle de confiance : on retire au hasard, avec remise, des origines parmi les 365, on recalcule le ratio, 2 000 fois, et on garde les 95 % centraux. On tire des origines entières, parce que les 24 heures d\u0026rsquo;une prévision ne sont pas indépendantes, et par blocs de 7 jours consécutifs (moving block bootstrap), parce que deux jours voisins se ressemblent.\nLa règle de verdict pré-déclarée : le challenger gagne si l\u0026rsquo;intervalle du ratio de MASE (challenger / référence) est entièrement sous 1 et si la couverture de son intervalle à 90 % est dans la bande [0,85 ; 0,95] ; il gagne sur la précision seulement si la couverture sort de la bande ; il perd si l\u0026rsquo;intervalle du ratio est au-dessus de 1 ; sinon, on ne conclut pas.\n5. Mesurer une prévision : le MASE et ses alternatives # Le quatrième bloc du plan était une lecture, Hyndman et Koehler (2006), Another look at measures of forecast accuracy, qui a introduit le MASE. Je la place ici parce que tout ce qui suit en dépend.\nL\u0026rsquo;erreur absolue moyenne (MAE) s\u0026rsquo;exprime en mégawatts : on ne peut pas la moyenner entre l\u0026rsquo;Allemagne et la Lettonie. Le MASE divise l\u0026rsquo;erreur par celle d\u0026rsquo;une méthode naïve sur l\u0026rsquo;historique de la même série :\n$$ \\text{MASE} = \\frac{\\dfrac{1}{H}\\sum_{h=1}^{H} \\lvert y_{T+h} - \\hat y_{T+h} \\rvert} {\\dfrac{1}{T-m}\\sum_{t=m+1}^{T} \\lvert y_t - y_{t-m} \\rvert} $$Le numérateur est l\u0026rsquo;erreur sur les \\(H = 24\\) heures prévues ; le dénominateur, l\u0026rsquo;erreur moyenne qu\u0026rsquo;aurait faite sur l\u0026rsquo;historique la copie de la valeur d\u0026rsquo;il y a \\(m\\) heures. Avec \\(m = 24\\), un MASE de 0,4 veut dire « 40 % de l\u0026rsquo;erreur de la copie de la veille ». En relisant l\u0026rsquo;article, j\u0026rsquo;ai noté qu\u0026rsquo;il définit le MASE avec le naïf à un pas (\\(m = 1\\)) ; la version saisonnière est celle du manuel FPP3 (section 5.8), qu\u0026rsquo;il faut citer pour \\(m = 24\\).\nPourquoi pas le MAPE, ni le sMAPE # À gauche, la même série décalée vers zéro, avec les mêmes erreurs de prévision à chaque niveau. À droite, le MAPE et le sMAPE augmentent de deux ordres de grandeur quand la série approche zéro ; le MASE reste à 1,29. Le MAPE divise chaque erreur par la valeur observée : les mêmes erreurs donnent un MAPE cent fois plus grand près de zéro, et il n\u0026rsquo;est pas défini en zéro. Il suppose aussi un zéro qui a un sens, ce qui n\u0026rsquo;est pas le cas des degrés Celsius. Le sMAPE, dit « symétrique », divise par la moyenne de l\u0026rsquo;observé et de la prévision, et ne l\u0026rsquo;est pas.\nPour y = 100, une erreur de 60 coûte 86 % si la prévision est trop basse et 46 % si elle est trop haute : un modèle qui surestime paraît meilleur. La charge française ne descend jamais sous 29 GW : le MAPE y resterait utilisable. L\u0026rsquo;argument pour le MASE est ici la comparabilité entre séries. (La bibliothèque utilsforecast calcule un sMAPE égal à la moitié de celui des manuels ; il ne sert à aucune décision.)\nMême modèle (Chronos-2, 12 semaines de contexte) sur les 50 séries du bloc 3. À gauche, la MAE s\u0026rsquo;étend sur un facteur 70, de 18 MW (Lettonie) à 1 245 MW (Grande-Bretagne), et classe les pays par taille. À droite, le MASE va de 0,25 à 0,87 et classe les séries par difficulté de prévision. La MAE classe les pays par taille ; le MASE les classe par difficulté (la Sardaigne est la plus dure, une zone allemande la plus facile). C\u0026rsquo;est ce qui rend possible le routeur du bloc 3.\nLe choix de l\u0026rsquo;échelle change le sens de « 1 » # À gauche, l\u0026rsquo;erreur du naïf à décalage m sur l\u0026rsquo;historique : 1,81 GW pour m = 1, 3,08 GW pour m = 24, 3,62 GW pour m = 168. À droite, le MASE des mêmes prévisions sous les trois échelles : le classement ne change jamais, la lecture du seuil 1 si. Avec \\(m = 24\\), le naïf journalier obtient 1,01 : il refait hors échantillon ce qui définit l\u0026rsquo;échelle. Avec \\(m = 1\\), les mêmes prévisions obtiennent 1,72, parce qu\u0026rsquo;on compare une prévision à 24 heures à l\u0026rsquo;erreur d\u0026rsquo;une prévision à une heure. Un MASE n\u0026rsquo;a de sens qu\u0026rsquo;avec son \\(m\\).\n6. Mesurer un intervalle : couverture, largeur et score # Le plan insistait : « si le TSFM gagne en MASE mais que sa couverture réelle à 90 % est de 70 %, tu dois savoir le dire ». Quatre mesures servent pour les intervalles :\nla couverture, part des heures où la réalité tombe dans l\u0026rsquo;intervalle (0,90 visé à 90 %) ; la largeur moyenne, en part de la charge ; le score de Winkler, qui combine les deux pour un intervalle \\([l, u]\\) à \\(1-\\alpha = 90\\,\\%\\) : $$ W = (u - l) + \\frac{2}{\\alpha}\\,(l - y)\\,\\mathbb{1}_{y \u003c l} + \\frac{2}{\\alpha}\\,(y - u)\\,\\mathbb{1}_{y \u003e u} $$On paie la largeur, plus vingt fois l\u0026rsquo;écart (\\(2/\\alpha = 20\\)) quand la réalité tombe dehors : un intervalle étroit qui rate paie les pénalités, un intervalle immense paie sa largeur ;\nla perte quantile (pinball loss), \\(\\rho_\\tau(y, q_\\tau) = (\\tau - \\mathbb{1}_{y \u003c q_\\tau})(y - q_\\tau)\\), qui note chaque quantile : pour \\(\\tau = 0{,}9\\), être sous la réalité coûte 0,9 par unité d\u0026rsquo;écart, être au-dessus 0,1, et la valeur qui minimise cette perte est exactement le quantile 0,9. Moyennée sur 0,1 à 0,9, elle note toute la distribution. 7. Les résultats sur l\u0026rsquo;année 2019 # Le tableau imprimé par w25 backtest, identique d\u0026rsquo;une exécution à l\u0026rsquo;autre (au bit près sur le même GPU, à 0,016 MW près entre CPU et GPU) :\nModèle MASE WAPE cov80 cov90 largeur 90 Winkler 90 perte quantile SeasonalNaive(24) 1,001 0,059 0,824 0,879 0,293 7,08 0,429 SeasonalNaive(168) 1,084 0,061 0,854 0,915 0,330 6,92 0,448 AutoETS 1,715 0,102 0,772 0,899 0,450 8,74 0,669 AutoETS + conforme 1,715 0,102 0,806 0,908 0,337 6,34 0,609 ETS(Z,Z,A), écart 1,010 0,061 0,652 0,736 0,176 6,96 0,410 MSTL 0,479 0,028 0,689 0,779 0,090 3,49 0,195 Chronos-Bolt small 0,435 0,026 0,859 0,859 0,088 2,79 0,173 Chronos-Bolt base 0,406 0,024 0,858 0,858 0,082 2,65 0,162 Bolt base + conforme 0,406 0,024 0,795 0,902 0,108 2,72 0,163 Chronos-2 0,365 0,021 0,761 0,862 0,076 2,37 0,146 cov80 et cov90 : couverture des intervalles nominaux à 80 et 90 %. Largeur en part de la charge. Les lignes « conforme » sont décrites à la section 8.\nEn abscisse le MASE (plus bas = mieux), en ordonnée la couverture de l\u0026rsquo;intervalle à 90 %. La bande grise est la bande pré-déclarée [0,85 ; 0,95]. Les barres sont les intervalles de confiance à 95 % ; les losanges, la réparation conforme du même modèle. La figure se lit par coins : MSTL en bas à gauche, précis mais sous-couvert ; AutoETS en haut à droite, bien couvert mais très imprécis ; les modèles Chronos en haut à gauche, seuls à être à la fois précis et proches de la bande.\nLe modèle de référence prévoit une ligne plate # Le MASE de 1,715 d\u0026rsquo;AutoETS, pire que le naïf, m\u0026rsquo;a fait regarder ce qu\u0026rsquo;il choisissait : sur 572 des 608 origines, ETS(A,N,N), un niveau seul qui prévoit 24 fois la dernière valeur. Son critère de sélection, l\u0026rsquo;AICc, repose sur la vraisemblance des prévisions à un pas ; sur une série horaire lisse, la meilleure prévision de l\u0026rsquo;heure suivante est à peu près l\u0026rsquo;heure présente, et le niveau seul gagne ce concours. À 24 pas, il ne sait plus rien du cycle journalier.\nLe verdict pré-déclaré reste rapporté (« Bolt base fait quatre fois moins d\u0026rsquo;erreur »), mais il dit peu. J\u0026rsquo;ai ajouté, en écart déclaré, un ETS à saisonnalité imposée, ETS(Z,Z,A), au niveau du naïf journalier (1,010). Même contre lui, Bolt base fait 2,5 fois moins d\u0026rsquo;erreur. Le vrai concurrent reste MSTL.\nUne ligne plate peut être « parfaitement calibrée » # AutoETS couvre 0,899 des heures, presque exactement le nominal, avec un intervalle large de 45 % de la charge : de 30 à 60 GW pour une journée d\u0026rsquo;été. Son score de Winkler, 8,74, est le pire du tableau. La couverture seule récompense un intervalle inutile.\nMédiane et intervalle à 90 % de Chronos-Bolt base et Chronos-2, prévision moyenne de MSTL, sur un mardi ordinaire, le 1er mai et Noël. Deux jours de contexte sont affichés avant l\u0026rsquo;origine. Un mardi de février, les deux TSFM ont la bonne forme de journée et leurs intervalles enveloppent la réalité. Les deux autres panneaux reviennent à la section 9.\n8. L\u0026rsquo;intervalle à 90 % de Chronos-Bolt # Dans le tableau, Chronos-Bolt a la même couverture à 80 % et à 90 % : 0,858.\nÀ gauche, pour chaque quantile prévu, la part des heures où la réalité est en dessous : sur la diagonale, le modèle est calibré. Chez Bolt, les quantiles 0,05 et 0,1 donnent la même valeur. À droite, Bolt n\u0026rsquo;a que 9 quantiles entraînés ; demandé à 0,05 et 0,95, il rend 0,1 et 0,9, donc son intervalle à 80 %. Entraîné sur les quantiles 0,1 à 0,9, Chronos-Bolt renvoie 0,1 et 0,9 quand on lui demande 0,05 et 0,95 (un test du dépôt le vérifie sur les vrais poids). Son « intervalle à 90 % » est son intervalle à 80 %. J\u0026rsquo;avais repéré ce point en lisant le code avant de lancer quoi que ce soit.\nIl tombe pourtant dans la bande [0,85 ; 0,95], parce que son intervalle à 80 % sur-couvre, et l\u0026rsquo;intervalle de confiance de cette couverture (0,833 à 0,884) déborde sous la bande. La règle dit « gagne » ; je ne signerais pas un intervalle à 90 % sur cette base. Chronos-2, entraîné de 0,01 à 0,99, a un vrai intervalle à 90 % (0,862), mais sous-couvre à 80 % (0,761).\nLa réparation conforme # Plutôt que de faire confiance aux quantiles d\u0026rsquo;un modèle, la prédiction conforme les reconstruit à partir de ses erreurs passées. La version utilisée ici :\ngarder la médiane du modèle comme prévision ponctuelle ; pour chaque pas d\u0026rsquo;horizon \\(h\\), rassembler les erreurs absolues \\(\\lvert y - \\text{médiane} \\rvert\\) du modèle au même pas sur les 56 origines précédentes, toutes observées au moment de prévoir ; pour un intervalle à 90 %, ajouter et retrancher à la médiane le quantile à 90 % de ces erreurs (avec la correction d\u0026rsquo;échantillon fini de la méthode). Si les erreurs futures ressemblent aux erreurs passées, la réalité tombe dans l\u0026rsquo;intervalle 90 % du temps, quel que soit le modèle. Pour Bolt base, la couverture passe de 0,858 à 0,902, avec un intervalle un peu plus large (0,108 au lieu de 0,082). Je l\u0026rsquo;ai appliquée aussi à AutoETS, pour que la réparation ne favorise pas un seul camp. Sa limite : elle calibre en moyenne sur les 56 jours précédents, pas sur un régime rare.\n9. Par horizon et par régime # À gauche, l\u0026rsquo;erreur moyenne mise à l\u0026rsquo;échelle par pas d\u0026rsquo;horizon. À droite, la couverture de l\u0026rsquo;intervalle à 90 % par pas ; la zone rose correspond aux pas 13 à 18, de 14 h à 20 h à Paris en été. En tirets, Bolt base après réparation conforme. Au premier pas, tous les modèles qui lisent la dernière heure font bien : Bolt base égale MSTL, Chronos-2 en fait 77 %. L\u0026rsquo;écart s\u0026rsquo;ouvre à partir du pas 7, la montée du matin, où les naïfs et l\u0026rsquo;ETS saisonnier se trompent le plus. La couverture des TSFM descend à 0,79-0,81 sur les pas 13 à 18, l\u0026rsquo;après-midi et la montée du soir ; celle de MSTL s\u0026rsquo;effondre sur la montée du matin (0,58 au pas 7). La réparation conforme, calculée pas par pas, aplatit le profil autour de 0,90.\nChaque ligne est un régime, avec son nombre d\u0026rsquo;origines. Les deux premiers panneaux donnent le ratio de MASE du TSFM contre l\u0026rsquo;ETS saisonnier et contre MSTL (à gauche de 1, le TSFM est plus précis), avec l\u0026rsquo;intervalle de confiance à 95 %. Le troisième donne la couverture à 90 %, avec la bande pré-déclarée en gris. Ce forest plot résume le banc. Contre l\u0026rsquo;ETS saisonnier, les deux TSFM sont à gauche de 1 sur tous les régimes sauf les fériés et les ponts. Contre MSTL, ils sont plus près de 1, et quelques intervalles le touchent (le dimanche, le printemps et l\u0026rsquo;automne pour Bolt base). La couverture reste dans la bande ou à son bord, sauf une ligne : les jours fériés.\nLes panneaux du 1er mai et de Noël, plus haut, montrent pourquoi. Un férié ressemble à un dimanche, mais rien dans les heures précédentes ne l\u0026rsquo;annonce à un TSFM univarié. Les deux tombent un mercredi : il prévoit un mercredi ordinaire, trop haut de plusieurs gigawatts, et la réalité passe sous son intervalle. Sur les 11 fériés de 2019, Bolt base a un MASE de 1,49, quatre fois son niveau ordinaire et pire que le naïf journalier (1,42). Contre l\u0026rsquo;ETS saisonnier, le ratio vaut 1,12 [0,85 ; 1,55] : pas mieux. Son intervalle à 90 % couvre 37 % des heures (Chronos-2 : 35 %, MSTL : 26 %).\nAucun modèle du banc ne reçoit d\u0026rsquo;indicateur de férié. La différence est qu\u0026rsquo;on peut en donner un à un spécialiste, alors qu\u0026rsquo;un TSFM univarié en zero-shot ne peut pas être prévenu ; Chronos-2, lui, peut l\u0026rsquo;être (section 11). Les deux ponts de 2019 ne suffisent pas : sous 10 origines, pas de verdict.\n10. Trois vérifications de robustesse # Six origines ne décident rien quand l\u0026rsquo;écart est faible # Distribution du ratio de MASE sur 2 000 tirages de 6 origines parmi les 365. Contre l\u0026rsquo;ETS saisonnier, 0,2 % des tirages inversent le classement ; contre MSTL, 24,2 %. Contre l\u0026rsquo;ETS saisonnier (écart d\u0026rsquo;un facteur 2,5), presque tous les tirages de 6 origines donnent le bon classement. Contre MSTL (écart de 15 %), un sur quatre l\u0026rsquo;inverse. Six origines détectent un grand écart, pas un écart serré. Ici, les 608 origines de huit modèles prennent environ six minutes.\nLe confinement # MASE en moyenne glissante sur 7 origines, de janvier à juin 2020. En rose, la fenêtre de stress après le 17 mars. Tous les modèles sont touchés pendant environ une semaine après le 17 mars, puis se remettent. Le classement tient : Chronos-2 0,392, Bolt base 0,477, MSTL 0,506, ETS saisonnier 0,631, naïf journalier 0,672. L\u0026rsquo;erreur du naïf journalier baisse même d\u0026rsquo;un tiers : sans différence entre jours ouvrés et week-end, copier la veille devient une bonne stratégie.\nLa longueur de contexte (exploratoire) # À gauche, le MASE de 2019 selon la longueur de contexte, avec les niveaux de MSTL et de l\u0026rsquo;ETS saisonnier en pointillés. À droite, la couverture à 90 %. Explorée après les résultats, donc hors protocole. Avec une seule semaine de contexte, Bolt base (0,452) fait déjà mieux que MSTL ajusté sur 8 semaines (0,479). Son meilleur contexte est 1 024 heures (0,392), pas les 2 048 du protocole (0,406) ; je n\u0026rsquo;ai pas changé le protocole, ce choix serait fait sur l\u0026rsquo;année de test. Le résultat annonce le bloc 3 : un TSFM a besoin de bien moins d\u0026rsquo;historique qu\u0026rsquo;un spécialiste.\n11. Donner le calendrier au TSFM # Chronos-2 accepte des covariables connues à l\u0026rsquo;avance, dont les fériés et les ponts. Pour chaque heure, deux indicateurs 0/1 (férié, pont) sont passés au modèle sur le contexte et sur l\u0026rsquo;horizon. Même modèle, même contexte, aucun réentraînement. Cette partie est exploratoire : décidée après les résultats, sur l\u0026rsquo;année de test.\nChronos-2, 2019 MASE cov80 cov90 perte quantile fériés : MASE fériés : cov90 zero-shot, univarié 0,365 0,761 0,862 0,146 1,446 0,352 + indicateurs férié et pont 0,331 0,808 0,899 0,130 0,745 0,769 À gauche, l\u0026rsquo;erreur de Chronos-2 par type de jour, sans et avec les indicateurs : seule la ligne des fériés bouge nettement. Au centre, la couverture à 90 %. À droite, le 1er mai 2019 : avec les indicateurs, la médiane descend vers la réalité et l\u0026rsquo;intervalle l\u0026rsquo;englobe. Sur les fériés, l\u0026rsquo;erreur est divisée par deux (ratio 0,52 [0,39 ; 0,67]) et la couverture passe de 0,35 à 0,77. Sur les autres jours, elle baisse un peu (ratio 0,96) : mon hypothèse, non testée, est que les fériés passés du contexte ne ressemblent plus à des anomalies une fois signalés. Sur l\u0026rsquo;année, la couverture à 90 % atteint 0,899. « Zero-shot » ne veut donc pas forcément dire « univarié ». Trouvé sur 11 jours de l\u0026rsquo;année de test, ce résultat motive un test pré-déclaré sur une autre année ; il ne le remplace pas.\n12. Le routeur de complexité, reconstruit sur 50 séries # Le troisième bloc demandait un raisonnement d\u0026rsquo;architecture, sans code, pour mettre en production le routeur du papier. Pour que chaque chiffre soit mesuré, j\u0026rsquo;ai d\u0026rsquo;abord reconstruit le routeur.\nLe panel. 50 pays et zones de marché européens du paquet OPSD, avec moins de 0,5 % d\u0026rsquo;heures manquantes sur 2015-2019 ; 73 origines en 2019 (une tous les 5 jours, pour couvrir tous les jours de la semaine), horizon de 24 heures. Le démarrage à froid (cold start), une série nouvelle avec peu d\u0026rsquo;historique, est simulé en ne laissant lire que les 1, 2, 4 ou 12 dernières semaines : 200 unités (série × longueur d\u0026rsquo;historique). Le TSFM est Chronos-2 en zero-shot ; le spécialiste, MSTL [24, 168] réajusté à chaque origine (un ETS saisonnier sous deux semaines d\u0026rsquo;historique : un cycle hebdomadaire ne s\u0026rsquo;estime pas sur une semaine).\nÀ gauche, chaque point est une unité : MASE du spécialiste en abscisse, de Chronos-2 en ordonnée. Tous les points sont sous la diagonale. À droite, le MASE moyen selon l\u0026rsquo;historique disponible : 1,21 contre 0,62 avec une semaine, 0,60 contre 0,48 avec douze. Le TSFM gagne sur les 200 unités, avec un gain de 5 % à 70 %, le plus grand en démarrage à froid : avec une semaine, le spécialiste n\u0026rsquo;a qu\u0026rsquo;un cycle hebdomadaire pour s\u0026rsquo;ajuster, le TSFM apporte ce qu\u0026rsquo;il a appris ailleurs. Sur ce point, le papier est confirmé.\nQuelles caractéristiques prédisent le gain # Un routeur décide sur des caractéristiques calculées avant la prévision (ex ante), jamais sur l\u0026rsquo;erreur future.\nGain du TSFM sur le spécialiste (en %) selon six caractéristiques, avec la corrélation de rang de Spearman. La couleur indique la longueur d\u0026rsquo;historique, du plus clair (1 semaine) au plus foncé (12 semaines). La longueur d\u0026rsquo;historique est la plus liée au gain (corrélation de rang −0,62), suivie des autocorrélations à 168 et 24 heures ; le coefficient de variation et l\u0026rsquo;entropie spectrale du papier disent peu ici. Plusieurs indicateurs bougent d\u0026rsquo;ailleurs avec la longueur d\u0026rsquo;historique elle-même. L\u0026rsquo;intermittence et les contraintes physiques, deux caractéristiques du plan, ne peuvent pas être testées : la charge nationale n\u0026rsquo;a ni l\u0026rsquo;une ni l\u0026rsquo;autre.\nLes politiques pré-déclarées # Politique MASE moyen couverture 90 % envoyé au TSFM écart au « toujours TSFM » [IC 95 %] toujours le spécialiste 0,801 0,745 0 % +0,266 [0,243 ; 0,296] toujours le TSFM 0,535 0,846 100 % 0 règle du papier, seuils publiés 0,566 0,833 92 % +0,030 [0,014 ; 0,056] souche apprise, un pays laissé de côté 0,535 0,846 100 % 0 oracle (gagnant de chaque unité) 0,535 0,846 100 % 0 La souche de décision (decision stump) est l\u0026rsquo;arbre le plus simple : une caractéristique, un seuil. Ses seuils sont appris sur les autres pays et appliqués à celui laissé de côté, pour qu\u0026rsquo;aucun pays ne note une règle qu\u0026rsquo;il a servi à construire. Sur les 28 plis, elle ne trouve aucun seuil qui améliore « toujours le TSFM », et l\u0026rsquo;oracle se confond avec lui. La règle du papier, avec ses seuils réglés sur Traffic et M4 Daily, envoie 8 % des séries au spécialiste et perd 0,030 de MASE hors échantillon.\nCe que le routeur peut encore acheter : du coût # Si le TSFM gagne partout, un routeur ne peut plus qu\u0026rsquo;économiser des appels au TSFM, au prix de la précision. J\u0026rsquo;ai ajouté, hors protocole, une souche qui paie une pénalité par série envoyée au TSFM, avec une pénalité variable.\nMASE moyen sur les 200 unités selon la part de séries envoyées au TSFM. En pointillés, un routage au hasard ; en rouge, le classement oracle ; en bleu, la souche sensible au coût, hors échantillon ; le carré noir est la règle du papier. La souche reste à moins de 0,015 de MASE de l\u0026rsquo;oracle entre 25 % et 100 % d\u0026rsquo;appels au TSFM. Économiser 22 % des appels coûte 5 % de MASE, 44 % en coûtent 11 %. La règle du papier se place au-dessus de la droite du hasard : sur ce panel, elle route moins bien qu\u0026rsquo;un tirage au sort.\nL\u0026rsquo;économie vaut-elle quelque chose ? Le papier suppose qu\u0026rsquo;un appel au TSFM coûte mille appels au spécialiste. Mesuré : Chronos-2 prend environ 95 ms par série sur 4 cœurs CPU (environ 5 ms en lot sur GPU), un réajustement de MSTL 183 ms sur un cœur. L\u0026rsquo;hypothèse vaut pour un spécialiste qui ne fait que prédire, pas pour un spécialiste réajusté à chaque prévision. Les millisecondes viennent d\u0026rsquo;une seule machine ; c\u0026rsquo;est le ratio qui se transpose.\n13. La décision de routage dérive # Le plan désignait un « piège senior » : la décision d\u0026rsquo;un routeur peut dériver comme celle d\u0026rsquo;un modèle. Pour le mesurer, j\u0026rsquo;ai pris une souche sensible au coût réglée pour envoyer environ deux tiers des séries au TSFM (le point de fonctionnement du routeur déployé du papier), avec une règle sur l\u0026rsquo;autocorrélation à 24 heures, réévaluée à chaque origine sur les quatre dernières semaines.\nChaque ligne est une série, chaque colonne une origine de 2019 : violet pour le TSFM, vert pour le spécialiste. À droite, la part de séries envoyées au TSFM : de 12 % à 78 % selon la période. 40 séries sur 50 changent de branche au moins une fois, pour 323 bascules, et la part envoyée au TSFM oscille entre 12 % et 78 %. Les bascules suivent les fériés et les saisons : les zones allemandes changent ensemble au tournant de l\u0026rsquo;année, les séries britanniques et danoises en juin. Chaque bascule change du jour au lendemain le modèle qui produit la prévision d\u0026rsquo;une série. Une bande d\u0026rsquo;hystérésis (ne changer de branche que si l\u0026rsquo;indicateur dépasse le seuil d\u0026rsquo;une marge donnée) réduit ces allers-retours :\nBande sur l\u0026rsquo;autocorrélation à 24 h bascules en 2019 séries qui basculent maximum pour une série aucune 323 40 22 ± 0,01 218 38 16 ± 0,02 168 38 16 ± 0,05 78 35 6 Une autre leçon est venue d\u0026rsquo;une erreur : ma première version ajustait la souche sur des indicateurs calculés sur 2 048 heures et l\u0026rsquo;appliquait à des indicateurs calculés sur 672 heures. L\u0026rsquo;entropie spectrale dépend de la fenêtre, et la souche envoyait 4 à 20 % des séries au TSFM au lieu d\u0026rsquo;environ 56 %. C\u0026rsquo;est le décalage entre entraînement et service, version routeur.\nLe raisonnement d\u0026rsquo;architecture # Les séries entrent dans le routeur, qui choisit une branche par série et par jour. Les branches TSFM et spécialiste se servent mutuellement de repli. Le suivi hebdomadaire porte sur le routeur lui-même : répartition des routes, bascules, prévision fantôme de l\u0026rsquo;autre branche. Le livrable demandé tenait en 8 à 12 lignes. Avec les mesures précédentes :\nLa longueur d\u0026rsquo;historique prédit l\u0026rsquo;ampleur du gain du TSFM, pas le gagnant. Aucun seuil ne se justifie sur la précision ; le plan disait que c\u0026rsquo;est le signe d\u0026rsquo;une caractéristique de routage mal choisie, ou ici d\u0026rsquo;une absence de route à choisir. Par défaut, le TSFM pour toutes les séries, avec le calendrier en covariable, plutôt qu\u0026rsquo;un changement de branche les jours fériés. Le spécialiste comme repli : délai dépassé ou valeur manquante côté TSFM ; l\u0026rsquo;inverse si l\u0026rsquo;ajustement échoue ou si l\u0026rsquo;historique fait moins de deux jours ; le naïf journalier, signalé, si les deux échouent. Les deux branches en lot quotidien ; le temps réel seulement pour les re-prévisions infra-journalières, sur le TSFM, qui n\u0026rsquo;a rien à réajuster. Chaque prévision porte sa route et la version du modèle. Le routeur est suivi comme un modèle : répartition des routes, bascules par série (alerte au-delà de deux par mois), hystérésis, prévision fantôme de l\u0026rsquo;autre branche sur 5 % des séries. Un responsable nommé décide des seuils ; tout changement est une version évaluée hors échantillon. Pas de routeur de complexité dont le seul gain serait un coût que la mesure ne montre pas. 14. Ce que j\u0026rsquo;en retiens # Ces constats valent pour des séries de charge horaires, sans température, face à des spécialistes statistiques univariés.\nUn TSFM zero-shot peut battre un spécialiste réajusté sur une série qu\u0026rsquo;il n\u0026rsquo;a jamais vue, et l\u0026rsquo;écart dépend surtout du spécialiste : un facteur 4 contre une ligne plate, 15 % contre MSTL. La référence mérite d\u0026rsquo;être regardée avant d\u0026rsquo;être battue : le critère à un pas d\u0026rsquo;AutoETS ne correspond pas à un horizon de 24 heures. La couverture ne se lit qu\u0026rsquo;avec la largeur : la ligne plate est le modèle le mieux calibré et le moins utile du tableau. Un intervalle se vérifie à la source : celui de Bolt à 90 % est son intervalle à 80 %, et la réparation conforme le corrige en moyenne, pas sur les régimes rares. Le calendrier est la faiblesse du zero-shot univarié, et une covariable la corrige sans réentraînement. Le démarrage à froid est le cas où l\u0026rsquo;écart est le plus grand : deux fois moins d\u0026rsquo;erreur avec une semaine d\u0026rsquo;historique. Un routeur ne vaut que l\u0026rsquo;écart qu\u0026rsquo;il exploite, et ici aucun ne favorise le spécialiste. La décision de routage dérive et demande le même suivi qu\u0026rsquo;un modèle. Limites et questions ouvertes. Une seule série pour le bloc 2 et pas de température, facteur de premier ordre pour la charge nationale. Les spécialistes sont univariés : un spécialiste avec un indicateur de férié est le prochain test, le plus susceptible d\u0026rsquo;inverser des unités. Les origines sont à minuit UTC, alors qu\u0026rsquo;une prévision pour le marché du lendemain est émise vers midi la veille. L\u0026rsquo;effet du calendrier sur Chronos-2 reste à confirmer sur une autre année, protocole déclaré avant.\nUne question pour vérifier sa compréhension. Un fournisseur présente un TSFM dont l\u0026rsquo;intervalle à 90 % couvre 91 % des heures sur un backtest de six mois. Est-ce suffisant pour le mettre en production ? Il manque au moins trois informations. La largeur ou le score de Winkler : une ligne plate couvre aussi 90 % (section 7). La couverture par régime et par horizon : une moyenne correcte peut cacher 37 % sur les fériés ou 79 % l\u0026rsquo;après-midi (section 9). Et les quantiles sur lesquels le modèle a été entraîné (section 8). Reproduire et aller plus loin # Le code, les tests et les résultats sont dans le dépôt gwils28/W25_tsfm_viability (Python 3.11, versions verrouillées par uv) ; make all refait tout, des données au rapport, GPU optionnel. Les décisions prises avant d\u0026rsquo;écrire du code sont dans SPEC.md, les écarts et corrections dans IMPLEMENTATION_NOTES.md, dont une relecture indépendante qui a corrigé le bootstrap sur les régimes rares. Le rapport en ligne reprend les figures dans leur ordre d\u0026rsquo;origine.\nLa suite est un cas client : la fiche Modèles de fondation pour séries temporelles : prévoir la consommation électrique du lendemain de 3 517 bâtiments tertiaires repose la question avec la météo et le calendrier en covariables, la prévision J-1 de RTE comme benchmark et un coût en euros. Contre un LightGBM qui reçoit ces covariables, Chronos-2 ne gagne plus partout, et le mélange des deux fait mieux que tout routeur.\nSources\nSoni, Das et Guduguntla, Assessing the operational viability of foundation models for time series forecasting, arXiv, mai 2026, et le code des auteurs. Ansari et al., Chronos: Learning the language of time series, 2024 ; le rapport technique de Chronos-2, 2025 ; la fiche de Chronos-Bolt base. TSFM.ai, IBM\u0026rsquo;s March 2026 time-series refresh, avril 2026. Kafritsas, Time series foundation models: a deep dive into strengths and limitations, AI Horizon Forecast, avril 2026. Hyndman et Koehler, Another look at measures of forecast accuracy, International Journal of Forecasting, 22(4), 2006 ; Hyndman et Athanasopoulos, Forecasting: Principles and Practice, 3ᵉ édition, section 5.8. Open Power System Data, paquet de séries temporelles, version du 6 octobre 2020. ","date":"6 novembre 2026","externalUrl":null,"permalink":"/posts/tsfm-zero-shot-specialiste/","section":"Articles \u0026 veille","summary":"Quand un modèle de fondation utilisé sans entraînement bat-il un modèle ajusté sur la série ? Ce que j’ai observé sur un an de charge électrique française, avec la couverture des intervalles et pas seulement l’erreur moyenne, puis en reconstruisant un routeur entre les deux sur 50 séries.","title":"Les modèles de fondation pour séries temporelles : zero-shot contre spécialiste","type":"posts"},{"content":"","date":"6 novembre 2026","externalUrl":null,"permalink":"/tags/mase/","section":"Tags","summary":"","title":"MASE","type":"tags"},{"content":"","date":"6 novembre 2026","externalUrl":null,"permalink":"/en/tags/prediction-intervals/","section":"Tags","summary":"","title":"Prediction Intervals","type":"tags"},{"content":"","date":"6 novembre 2026","externalUrl":null,"permalink":"/tags/statsforecast/","section":"Tags","summary":"","title":"Statsforecast","type":"tags"},{"content":"","date":"6 novembre 2026","externalUrl":null,"permalink":"/en/categories/tech-watch/","section":"Categories","summary":"","title":"Tech Watch","type":"categories"},{"content":"","date":"6 novembre 2026","externalUrl":null,"permalink":"/categories/veille/","section":"Categories","summary":"","title":"Veille","type":"categories"},{"content":"","date":"6 novembre 2026","externalUrl":null,"permalink":"/tags/zero-shot/","section":"Tags","summary":"","title":"Zero-Shot","type":"tags"},{"content":"","date":"3 novembre 2026","externalUrl":null,"permalink":"/en/tags/ai/","section":"Tags","summary":"","title":"AI","type":"tags"},{"content":"","date":"3 novembre 2026","externalUrl":null,"permalink":"/tags/docker/","section":"Tags","summary":"","title":"Docker","type":"tags"},{"content":"","date":"3 novembre 2026","externalUrl":null,"permalink":"/tags/ia/","section":"Tags","summary":"","title":"IA","type":"tags"},{"content":"","date":"3 novembre 2026","externalUrl":null,"permalink":"/en/categories/method/","section":"Categories","summary":"","title":"Method","type":"categories"},{"content":"","date":"3 novembre 2026","externalUrl":null,"permalink":"/categories/m%C3%A9thode/","section":"Categories","summary":"","title":"Méthode","type":"categories"},{"content":"","date":"3 novembre 2026","externalUrl":null,"permalink":"/tags/mlops/","section":"Tags","summary":"","title":"MLOps","type":"tags"},{"content":"","date":"3 novembre 2026","externalUrl":null,"permalink":"/tags/s%C3%A9curit%C3%A9/","section":"Tags","summary":"","title":"Sécurité","type":"tags"},{"content":"","date":"3 novembre 2026","externalUrl":null,"permalink":"/en/tags/security/","section":"Tags","summary":"","title":"Security","type":"tags"},{"content":"","date":"3 novembre 2026","externalUrl":null,"permalink":"/tags/tailscale/","section":"Tags","summary":"","title":"Tailscale","type":"tags"},{"content":" Un agent de code qui travaille seul pendant une heure a besoin d\u0026rsquo;une machine qui ne se met pas en veille, qu\u0026rsquo;on peut casser sans conséquence, et qui ne détient aucune clé de la production. Un petit VPS remplit ces trois conditions, à condition de le configurer en conséquence. Temps de lecture : environ 20 minutes. État des lieux au 31 octobre 2026 : prix, versions et comportements par défaut ont changé plusieurs fois cette année, chaque chiffre est daté.\nNote de transparence. Ces travaux ont été menés à titre personnel, dans un but d\u0026rsquo;apprentissage, avec l\u0026rsquo;assistance d\u0026rsquo;une IA :\npour le code, la relecture technique et méthodologique ; pour comprendre les outils et les méthodes mathématiques ; pour l\u0026rsquo;aide à la rédaction, la traduction et la correction du texte ; pour générer l\u0026rsquo;image de couverture de l\u0026rsquo;article. Le choix du sujet, le protocole, la vérification des résultats et les conclusions sont les miens, et j\u0026rsquo;en assume les erreurs. Comment j\u0026rsquo;utilise l\u0026rsquo;IA\nDans ma méthode de travail avec Claude Code, l\u0026rsquo;agent tourne dans le terminal de mon portable. Ça suffit tant que je reste devant. Dès qu\u0026rsquo;une tâche dure, qu\u0026rsquo;on veut la suivre depuis un téléphone ou lancer plusieurs agents en parallèle, le portable devient la contrainte : il se met en veille, il contient mes clés et mes documents, et un rm -rf mal placé y coûte cher.\nCet article rassemble ce que j\u0026rsquo;ai retenu en étudiant la question : documentation de Claude Code, guides des éditeurs (Doppler, Tailscale), grilles tarifaires des hébergeurs et retours d\u0026rsquo;expérience de la communauté. Il vise un profil data scientist ou ingénieur MLOps basé en France. Les sources sont en fin d\u0026rsquo;article.\nEn bref. La configuration qui ressort combine trois machines aux rôles distincts :\nUn VPS CPU européen dédié à l\u0026rsquo;agent, joignable seulement par réseau privé (Tailscale), utilisé dans tmux, jetable et sauvegardé par snapshots. Une machine de production séparée, déployée par la CI, jamais par l\u0026rsquo;agent. Du GPU loué à l\u0026rsquo;heure, démarré par script quand un entraînement le demande. Les secrets sont injectés à l\u0026rsquo;exécution par un gestionnaire (Doppler ou équivalent), en lecture seule et à durée limitée. Compter 25 à 130 € par mois selon l\u0026rsquo;usage, abonnement à l\u0026rsquo;agent compris, qui pèse 70 à 90 % de la facture.\nPourquoi faire tourner un agent sur un VPS # Besoin Ce que le VPS apporte Sessions longues et persistantes L\u0026rsquo;agent continue quand le portable se met en veille ; tmux conserve l\u0026rsquo;état Travail asynchrone On lance une tâche, on part, on reçoit une notification Accès mobile SSH (Termius, Blink) via Tailscale, ou Remote Control vers l\u0026rsquo;app Claude Isolation du poste personnel Un rm -rf ou une dépendance malveillante touche le VPS, pas les clés ni les documents Mode autonome Un environnement jetable, restaurable par snapshot Parallélisation Plusieurs agents en fenêtres tmux ou worktrees, sans saturer sa machine Proximité avec la production Mêmes images Docker, même architecture Deux évolutions de 2026 changent la donne :\nRemote Control (février 2026) permet de piloter depuis l\u0026rsquo;app mobile ou claude.ai/code une session Claude Code qui tourne sur le VPS. La session ne fait que des requêtes HTTPS sortantes : aucun port à ouvrir. Limites : une connexion distante par session, un terminal ouvert obligatoire, une expiration après environ 10 minutes de coupure réseau. L\u0026rsquo;auto mode par défaut (14 août 2026) remplace la demande de permission à chaque étape par un classifieur qui examine les actions. Dans un test d\u0026rsquo;Anthropic sur 1 053 testeurs, la revue humaine interceptait 13,6 % des commandes dangereuses, l\u0026rsquo;auto mode 89 %. Les règles permissions.deny et ask s\u0026rsquo;appliquent avant le classifieur. Depuis la v2.1.302, les exécutions headless (claude -p) démarrent en mode manuel : il faut passer --permission-mode auto explicitement. L\u0026rsquo;auto mode réduit le besoin de lancer l\u0026rsquo;agent sans aucune permission (le « mode YOLO »), mais il reste un filtre probabiliste. Il ne remplace pas l\u0026rsquo;isolation, ce qui motive la suite.\nLes choix qui structurent la configuration # Douze idées reviennent d\u0026rsquo;une source à l\u0026rsquo;autre. Je les reprends comme un ensemble cohérent plutôt que comme des règles : chacune répond à un risque précis, et on peut en assouplir une en sachant ce qu\u0026rsquo;on perd.\nLe VPS est un poste de travail jetable. Ce qui compte vit dans git, dans le gestionnaire de secrets et dans des sauvegardes externes ; la machine se reconstruit en un quart d\u0026rsquo;heure. La machine de l\u0026rsquo;agent et la production sont distinctes. L\u0026rsquo;agent peut casser son VPS, pas la production. Pas de port public sans raison. Tailscale ou WireGuard pour l\u0026rsquo;accès, Cloudflare Tunnel ou un reverse proxy pour ce qui doit être public. L\u0026rsquo;autonomie se paie en isolation. --dangerously-skip-permissions seulement dans un conteneur ou une VM sans secret de valeur ; ailleurs, auto mode et sandbox. Les secrets sont injectés à l\u0026rsquo;exécution, limités, en lecture seule, à durée limitée. L\u0026rsquo;agent ne remplit jamais ses propres secrets. La « trifecta létale » est cassée : données privées, contenu non fiable et communication externe ne sont pas réunis sans garde-fou réseau. La persistance se gère au niveau du terminal : tmux ou zellij, mosh pour les réseaux instables, Remote Control en option. On parallélise par worktrees, pas par copies : un agent par branche, un humain qui relit les diffs. Les notifications remplacent la surveillance : hooks Stop et Notification vers ntfy ou Telegram. Le CPU tourne en permanence, le GPU à la demande. Louer un GPU 24 h/24 pour coder ne se justifie pas. Le déploiement reste ennuyeux : Compose, PaaS auto-hébergé ou Kamal, avec une CI. Pas de docker run improvisé par l\u0026rsquo;agent en production. Tout est daté. Prix, versions et comportements par défaut changent chaque trimestre. L\u0026rsquo;architecture d\u0026rsquo;ensemble # flowchart TD T[\"Téléphone ou portableTermius · mosh · VS Code Remote-SSH · Remote Control\"] T --\u003e|Tailscale| A subgraph A[\"VPS agent · UE · aucun port public\"] direction TB A1[\"utilisateur non root · tmux · worktrees\"] A2[\"Claude Code : auto mode + sandbox\"] A3[\"Doppler : config dev_agent, jeton lecture seule\"] A4[\"Docker · uv · MLflow\"] A5[\"hooks → ntfy privé · snapshots + restic → S3\"] A1 ~~~ A2 ~~~ A3 ~~~ A4 ~~~ A5 end A --\u003e|\"git push (jeton limité au dépôt)\"| G[\"GitHub + Actionstests · build · revue de PR · registre\"] G --\u003e|\"déploiement Kamal / Coolify / Compose\"| P[\"VPS production · séparéCaddy · API FastAPI · base · Uptime Kuma\"] A -.-\u003e|\"script de démarrage\"| GPU[\"GPU à l'heure · Scaleway L4/L40Sentraînement → MLflow → arrêt automatique\"] Le point clé du schéma : aucune flèche ne va du VPS de l\u0026rsquo;agent vers la production. Le code transite par git et la CI, qui détient seule la clé de déploiement.\nChoisir son VPS # 2026 a été une année de hausses, attribuées au coût de la RAM et des disques. Hetzner a augmenté ses prix cloud deux fois (1er avril et 15 juin) ; OVHcloud a fait passer son VPS-1 de 4,49 € à 6,49 € avant de lancer une gamme « VPS 2027 » ; Scaleway et netcup ont aussi relevé leurs tarifs.\nComparatif d\u0026rsquo;entrée de gamme # Fournisseur Offre Prix indicatif (oct. 2026) Localisation Remarques Hetzner CX23 : 2 vCPU, 4 Go, 40 Go 5,49 € HT/mois (hors IPv4) Allemagne, Finlande Meilleur rapport prix/RAM ; gammes CX/CAX souvent indisponibles Hetzner CAX11 (Arm) : 2 vCPU, 4 Go 5,99 €/mois Allemagne, Finlande Vérifier la compatibilité Arm des wheels Python et ML OVHcloud VPS-1 2027 : 2 vCores, 4 Go, 40 Go NVMe 3,81 à 4,49 € HT selon engagement France Sauvegarde quotidienne incluse OVHcloud VPS-2 : 6 vCores, 12 Go, 100 Go ≈ 11,99 € HT France Bon candidat pour plusieurs agents et MLflow Scaleway DEV1-M ≈ 14,74 € HT hors stockage Paris, Amsterdam, Varsovie GPU à l\u0026rsquo;heure dans le même compte Hostinger KVM 1/2, modèle Claude Code préinstallé Promotions ; renouvellement ×2 à ×3 Plusieurs, dont UE Le plus simple pour débuter ; serveur MCP officiel ; performances moyennes DigitalOcean, Vultr, Linode 1 vCPU, 2 Go ≈ 12 $/mois (DO) Francfort, Amsterdam, Paris Plus cher à RAM égale, excellente API Contabo Gros VPS Très bas Allemagne Beaucoup de RAM ; I/O et support irréguliers selon la communauté Oracle Free Tier Arm Ampere 0 € Francfort, Paris, Marseille Capacité aléatoire ; peu adapté comme poste principal Les sources se contredisent sur certains prix (CX23, gamme OVH 2027) : ce sont des ordres de grandeur, à vérifier sur la grille officielle avant de commander. Le prix de renouvellement compte plus que le prix promotionnel.\nDimensionnement # Un agent, développement web ou Python léger : 2 vCPU, 4 Go, 40 Go. La mémoire de Claude Code se surveille avec htop. Deux à quatre agents en parallèle : 4 à 8 vCPU, 8 à 16 Go, 80 à 160 Go NVMe. Data science (pandas, scikit-learn, LightGBM) : au moins 16 Go. La RAM est presque toujours le facteur limitant. Avec un PaaS sur la même machine : Coolify ou Dokploy demandent au moins 2 Go de plus. Disque : images Docker, environnements uv et jeux de données remplissent 40 Go en quelques semaines. Un volume séparé ou un stockage S3 évite la saturation. Localisation et RGPD # Un VPS chez un fournisseur européen garde les données au repos dans l\u0026rsquo;UE, hors des lois extraterritoriales américaines. Mais le code et les extraits que l\u0026rsquo;agent lit partent vers l\u0026rsquo;API du modèle (Anthropic, OpenAI, Google), où que soit le VPS : l\u0026rsquo;hébergement européen ne règle pas ce transfert. Pour des données personnelles réelles, les options sont de travailler sur des échantillons pseudonymisés ou synthétiques, de vérifier la rétention de son offre (options Zero Data Retention en Team et Enterprise), ou d\u0026rsquo;utiliser un modèle auto-hébergé.\nLe GPU à la demande # Option Prix (oct. 2026) Usage Scaleway L4 24 Go à partir de 0,79 €/h Inférence de LLM 7-8B quantifiés, vision, LoRA léger Scaleway L40S 48 Go à partir de 1,46 €/h Inférence plus lourde, génération d\u0026rsquo;images Scaleway H100 PCIe ≈ 2,73 €/h Entraînement sérieux Modal à la seconde Jobs GPU ponctuels depuis Python GPU loué au mois L4 24 h/24 ≈ 577 €/mois Rarement rentable en solo Vingt heures de L4 par mois coûtent environ 16 €, contre près de 580 € pour une instance permanente. Le VPS de l\u0026rsquo;agent reste en CPU ; l\u0026rsquo;agent écrit les scripts qui démarrent l\u0026rsquo;instance GPU, lancent l\u0026rsquo;entraînement, journalisent dans MLflow puis éteignent l\u0026rsquo;instance.\nMise en place et sécurité # Le durcissement de base # un utilisateur non root avec sudo ; l\u0026rsquo;agent tourne sous cet utilisateur, jamais en root ; SSH par clé uniquement : PasswordAuthentication no, PermitRootLogin no ; UFW en refus par défaut des connexions entrantes ; SSH n\u0026rsquo;est ensuite autorisé que sur l\u0026rsquo;interface tailscale0 ; fail2ban tant que SSH reste exposé, unattended-upgrades pour les correctifs de sécurité ; Tailscale ou WireGuard pour l\u0026rsquo;accès ; Cloudflare Tunnel, Caddy ou Traefik sur 80/443 pour ce qui doit être public ; un snapshot avant chaque grosse session autonome, et des sauvegardes externes (restic ou borg vers S3). La séquence d\u0026rsquo;installation correspondante, pour Ubuntu 24.04 (à adapter) :\n# --- En root, une seule fois --- adduser dev \u0026amp;\u0026amp; usermod -aG sudo dev mkdir -p /home/dev/.ssh \u0026amp;\u0026amp; cp ~/.ssh/authorized_keys /home/dev/.ssh/ \u0026amp;\u0026amp; chown -R dev:dev /home/dev/.ssh sed -i \u0026#39;s/^#\\?PasswordAuthentication.*/PasswordAuthentication no/; s/^#\\?PermitRootLogin.*/PermitRootLogin no/\u0026#39; /etc/ssh/sshd_config systemctl restart ssh apt update \u0026amp;\u0026amp; apt -y upgrade apt -y install ufw fail2ban unattended-upgrades tmux mosh git curl jq bubblewrap socat dpkg-reconfigure -plow unattended-upgrades ufw default deny incoming \u0026amp;\u0026amp; ufw default allow outgoing \u0026amp;\u0026amp; ufw allow OpenSSH \u0026amp;\u0026amp; ufw --force enable # --- Réseau privé --- curl -fsSL https://tailscale.com/install.sh | sh \u0026amp;\u0026amp; tailscale up --ssh # Une fois la connexion via l\u0026#39;IP Tailscale vérifiée : ufw allow in on tailscale0 \u0026amp;\u0026amp; ufw delete allow OpenSSH # --- En tant que dev --- curl -fsSL https://claude.ai/install.sh | bash # Claude Code curl -LsSf https://astral.sh/uv/install.sh | sh # Python/uv # Doppler : config dev_agent_claude, jeton lecture seule expirant, # puis : doppler configure set token \u0026lt;jeton\u0026gt; --scope . # Dans le projet : /sandbox, règles deny (~/.ssh, push --force, infra/prod) tmux new -s projet \u0026#34;doppler run -- claude\u0026#34; L\u0026rsquo;ordre des deux lignes Tailscale compte : on ne retire l\u0026rsquo;accès SSH public qu\u0026rsquo;après avoir vérifié que la connexion par le réseau privé fonctionne, sinon on s\u0026rsquo;enferme dehors.\nAuthentifier l\u0026rsquo;agent # Claude Code s\u0026rsquo;installe avec le script officiel, puis claude et /login. Si l\u0026rsquo;OAuth bloque faute de navigateur sur le serveur, un tunnel ssh -L 15735:localhost:15735 user@vps depuis le portable fonctionne. Une clé API ne va jamais dans .bashrc ; à défaut de gestionnaire de secrets, un fichier en chmod 600 chargé par le service.\nLes secrets # Le guide de Doppler pour Claude Code formalise une pratique qui se transpose à d\u0026rsquo;autres gestionnaires :\nune configuration réservée à l\u0026rsquo;agent, dérivée de l\u0026rsquo;environnement de développement (par exemple dev_agent_claude), avec seulement les secrets nécessaires à la tâche ; l\u0026rsquo;agent ne crée ni ne modifie jamais ses propres secrets ; un jeton de service en lecture seule, expirant, limité au répertoire (--scope .) ; un lancement via doppler run : les secrets passent en variables d\u0026rsquo;environnement et disparaissent à la fin du processus ; le serveur MCP de Doppler, s\u0026rsquo;il est utilisé, avec ce même jeton et --read-only ; jamais d\u0026rsquo;identifiants de production : seulement développement et test. Les alternatives sont Infisical (open source), 1Password CLI (op run), sops avec age, ou les secrets réseau de Claude Code on the web.\nLimite. Un secret en variable d\u0026rsquo;environnement reste lisible par l\u0026rsquo;agent (env). Le gestionnaire réduit la durée de vie, la portée et les fuites par fichier, mais n\u0026rsquo;empêche pas un agent compromis d\u0026rsquo;exfiltrer ce qu\u0026rsquo;il voit. D\u0026rsquo;où le filtrage réseau et des jetons à faible privilège : un jeton GitHub limité à un dépôt, une base de test. Injection de prompt et exfiltration # Simon Willison a nommé en juin 2025 la « lethal trifecta » : données privées, contenu non fiable, communication externe. La « Rule of Two » de Meta (octobre 2025) en découle : jamais plus de deux des trois dans une même session. Un agent de code réunit les trois par défaut : le dépôt et ses secrets, des README, issues et dépendances écrits par d\u0026rsquo;autres, et curl ou git push pour communiquer.\nLes leviers concrets pour casser au moins une branche :\nLe sandbox natif de Claude Code (Linux : sudo apt-get install bubblewrap socat, puis /sandbox). Il isole fichiers et réseau via un proxy à liste de domaines. Deux pièges : si le sandbox ne démarre pas, Claude Code continue sans lui avec un simple avertissement ; et l\u0026rsquo;échappatoire dangerouslyDisableSandbox existe. Vérifier au démarrage, et l\u0026rsquo;interdire si besoin (documentation). @anthropic-ai/sandbox-runtime enveloppe tout le processus, MCP et hooks compris, avec réseau et écriture refusés par défaut. Un conteneur ou un devcontainer avec politique réseau et seccomp ; pour une isolation au niveau du noyau, gVisor ou une VM séparée. Codex CLI : le mode workspace-write coupe le réseau par défaut ; --yolo équivaut à --dangerously-bypass-approvals-and-sandbox. L\u0026rsquo;hygiène MCP : ne pas réunir dans une même session un MCP qui lit du contenu externe (mails, tickets, web) et un MCP qui détient des secrets ou des droits d\u0026rsquo;écriture. L\u0026rsquo;échelle d\u0026rsquo;isolation # Niveau Mise en œuvre Quand 0 Agent directement sur le VPS, mode manuel Découverte 1 Auto mode, sandbox bubblewrap, règles deny Usage quotidien 2 Agent dans un conteneur sans secrets de production, réseau filtré Mode autonome, tâches longues 3 VM ou VPS jetable par projet, ou microVM (E2B, Firecracker) Code non fiable, plusieurs clients 4 Sandbox cloud managé (Claude Code on the web, réseau None ou Trusted) Tâches asynchrones depuis le mobile Le niveau 1 couvre l\u0026rsquo;essentiel d\u0026rsquo;un usage quotidien ; on monte d\u0026rsquo;un cran quand on retire les permissions ou quand le code vient de l\u0026rsquo;extérieur.\nLe travail au quotidien # Persistance et accès # tmux est le standard : une session par projet, tmux attach -t projet. Des configurations dédiées (par exemple tmux-claude-code) activent allow-passthrough on et un history-limit élevé. zellij en alternative moderne, mosh pour résister aux changements de réseau du téléphone. Sur mobile : Termius, Blink ou Echo sur l\u0026rsquo;adresse Tailscale, ou Remote Control (claude remote-control ou /rc) pour un accès en chat depuis l\u0026rsquo;app Claude. VS Code Remote-SSH ou Cursor en distant pour relire les diffs et ouvrir les notebooks du VPS sans exposer JupyterLab. Paralléliser # git worktree add ../projet-feature-x feature-x donne à chaque agent son répertoire et sa branche, dans sa fenêtre tmux, avec des ports de développement distincts et des PR relues par un humain. Deux agents sur le même répertoire de travail se marchent dessus. Les sous-agents parallélisent à l\u0026rsquo;intérieur d\u0026rsquo;une tâche ; les worktrees, entre les tâches.\nContexte et automatisation # CLAUDE.md ou AGENTS.md : commandes de test, conventions, interdits (« ne jamais toucher à infra/prod »). En auto mode, ce fichier alimente aussi le classifieur : il fait partie de la politique de sécurité. Hooks : PreToolUse pour bloquer, Stop et Notification pour prévenir via ntfy (claude-ntfy-hook), sur un topic difficile à deviner ou un ntfy auto-hébergé. MCP : le strict minimum, en lecture seule. Un MCP Coolify ou Dokploy qui pilote la production depuis le VPS de l\u0026rsquo;agent annule la séparation. Headless : claude -p \u0026quot;…\u0026quot; --permission-mode auto en cron ou timer systemd, pour un rapport de dérive ou une mise à jour de dépendances. Claude Code on the web et routines : claude --cloud et /schedule, avec un niveau réseau None, Trusted, Custom ou Full (documentation). GitHub Actions : l\u0026rsquo;action Claude Code ou codex exec pour la revue de PR, avec des jetons à portée minimale. Pour un profil data et MLOps # Environnements : uv et une image Docker de base commune au développement et à la production. Données : stockage objet S3 (Scaleway, OVHcloud) ; DVC ou lakeFS si les jeux évoluent. Suivi d\u0026rsquo;expériences : MLflow sur le VPS (SQLite ou Postgres, artefacts sur S3), accessible par Tailscale seulement. L\u0026rsquo;agent y journalise ses runs, ce qui laisse une trace auditable. Pipelines : Prefect, Dagster, ou un Makefile ou just. Notebooks : l\u0026rsquo;agent écrit des modules testés ; les notebooks restent pour l\u0026rsquo;exploration, au format jupytext ou Marimo, plus faciles à éditer et à relire en diff. Serving léger : FastAPI ou BentoML en CPU (scikit-learn, LightGBM, ONNX Runtime) ; LLM quantifiés sur GPU à l\u0026rsquo;heure (vLLM). Déployer # Même VPS ou VPS séparé ? # Le même VPS se défend pour un prototype ou une préproduction. Dès qu\u0026rsquo;il y a de vrais utilisateurs ou de vraies données, séparer les machines coûte 5 à 10 € par mois, peu face au risque d\u0026rsquo;un agent qui redémarre des conteneurs ou lit les secrets de production.\nLes outils auto-hébergés # Outil Licence Version récente Modèle Pour qui Docker Compose + Caddy libre — Fichiers versionnés, HTTPS automatique Le plus simple ; 1 à 3 services ML Coolify Apache-2.0 v4.4.6 (9 oct. 2026) Panneau web, Traefik, applications en un clic Un « Heroku chez soi » Dokploy Apache-2.0 + partie propriétaire v0.30.8 (29 sept. 2026) Panneau web, Swarm, Traefik Projets en Compose, besoin de clustering Kamal MIT v2.12.0 (18 juin 2026) CLI + YAML, déploiement par SSH, pas de panneau Surface d\u0026rsquo;attaque minimale CapRover, Dokku Apache-2.0 / MIT — Swarm, style Heroku Petites machines Un comparatif recense en 2026 61 avis de sécurité GitHub pour Coolify (dont 16 critiques) et 56 pour Dokploy (33 critiques), contre aucun pour Kamal et CapRover. Un panneau d\u0026rsquo;administration a donc sa place derrière Tailscale, et à jour.\nLa chaîne de déploiement # L\u0026rsquo;agent pousse une branche. La CI (GitHub Actions) teste, construit l\u0026rsquo;image et la pousse vers un registre. La CI déploie (Kamal, webhook Coolify ou Dokploy, ou docker compose pull \u0026amp;\u0026amp; up -d avec une clé de déploiement restreinte) ; l\u0026rsquo;agent ne déploie pas depuis son VPS. Un reverse proxy (Caddy, Traefik ou Nginx) sert l\u0026rsquo;application. L\u0026rsquo;observabilité suit : Uptime Kuma, logs (Loki ou docker logs), métriques (Prometheus et Grafana, ou Netdata) ; côté ML, latence, erreurs et dérive des entrées (Evidently). Les sauvegardes de production partent vers S3, et leur restauration est testée. Les alternatives au VPS # Option Points forts Points faibles Coût indicatif Quand la choisir VPS dédié à l\u0026rsquo;agent Contrôle, persistance, prix fixe, UE Administration et sécurité à sa charge 5 à 15 €/mois Usage quotidien, profil technique Machine locale Zéro latence, GPU local, gratuit Veille, isolation faible, pas d\u0026rsquo;asynchrone 0 € Exploration interactive, avec sandbox Serveur maison, mini-PC Beaucoup de RAM, GPU grand public Électricité, IP résidentielle, réseau domestique exposé 300 à 1 000 € à l\u0026rsquo;achat Gros besoins en RAM ou GPU Claude Code on the web Rien à administrer, mobile, routines Dépend de GitHub, environnement contraint Inclus dans l\u0026rsquo;abonnement Tâches asynchrones délimitées Codespaces, Gitpod/Ona, Coder Devcontainers standardisés, équipe Facturation horaire, sessions longues coûteuses Variable Équipes, onboarding E2B, Daytona microVM, démarrage rapide, SDK Pensés pour des produits, pas un poste de dev ≈ 0,05 $/vCPU-h Construire un agent Modal GPU à la seconde, natif Python Sandbox CPU 3 fois plus cher que les fonctions ≈ 0,14 $/cœur-h Jobs GPU ponctuels Fly.io Machines/Sprites Démarrage rapide, à l\u0026rsquo;usage Moins orienté UE et RGPD ≈ 0,04 $/CPU-h Environnements éphémères Pour un individu, le VPS reste la base la plus souple ; les sandboxes managés viennent en complément pour l\u0026rsquo;asynchrone, le GPU ou un produit.\nCe que ça coûte # Abonnements Claude en octobre 2026 : Pro à 20 $/mois, Max 5x à 100 $, Max 20x à 200 $, Claude Code inclus avec des limites partagées (fenêtre de 5 heures et plafond hebdomadaire). Certaines sources décrivent une restructuration en « Max 100 $ / Ultra 200 $ » : la grille officielle fait foi.\nScénario Agent VPS agent Production GPU Total indicatif Découverte Pro 20 $ ≈ 4 à 5,5 € — — ≈ 25 €/mois Data scientist solo régulier Max 5x 100 $ 8-12 Go, 10 à 15 € 5 à 6 € 20 h de L4 ≈ 16 € ≈ 120 à 130 €/mois Usage intensif, plusieurs agents Max 20x 200 $ (+ un autre agent) 16 Go, 20 à 30 € ≈ 10 € 50 h de L4 ≈ 40 € ≈ 250 à 300 €/mois Hors TVA, avec 1 $ ≈ 0,9 €. L\u0026rsquo;abonnement représente 70 à 90 % de la facture : gagner quelques euros sur le VPS compte peu, oublier un GPU allumé compte beaucoup.\nLes pièges fréquents # un agent en root sur la machine de production, à côté du panneau, de la base et des secrets ; le .env de production copié sur le VPS de l\u0026rsquo;agent, ou des clés dans .bashrc ; un sandbox qu\u0026rsquo;on croit actif alors qu\u0026rsquo;il s\u0026rsquo;est désactivé faute de bubblewrap ou de socat ; --dangerously-skip-permissions hors d\u0026rsquo;un conteneur jetable, ou des règles « allow » génériques (Bash(*)) ; JupyterLab, MLflow, ttyd ou le panneau Coolify sur l\u0026rsquo;IP publique, même avec un mot de passe ; un topic ntfy public qui contient des résumés de code ; une instance GPU laissée allumée, faute d\u0026rsquo;un trap ou d\u0026rsquo;un délai d\u0026rsquo;arrêt automatique ; deux agents sur le même répertoire au lieu de deux worktrees ; des sauvegardes jamais testées en restauration ; un VPS choisi sur son prix promotionnel sans regarder le renouvellement ; des MCP « lecteurs de contenu externe » et « détenteurs de secrets » dans la même session ; un claude -p en cron qu\u0026rsquo;on suppose en auto mode, alors qu\u0026rsquo;il démarre en manuel depuis la v2.1.302. Ma liste de vérification # Choix\nFournisseur européen, au moins 4 Go de RAM (8 à 16 Go pour la data ou plusieurs agents), prix de renouvellement vérifié. VPS de production distinct ; GPU loué à l\u0026rsquo;heure. Durcissement\nUtilisateur non root, SSH par clé, root désactivé. UFW en refus par défaut, SSH via Tailscale seulement, fail2ban, unattended-upgrades. Aucun service d\u0026rsquo;administration sur l\u0026rsquo;IP publique. Agent\nbubblewrap et socat installés, /sandbox actif et vérifié. Auto mode (ou manuel pour le sensible), règles deny explicites, mode sans permission seulement en conteneur jetable. CLAUDE.md ou AGENTS.md avec commandes, conventions et interdits. MCP minimaux, en lecture seule, sans trifecta. Scripts headless avec --permission-mode explicite. Secrets\nGestionnaire de secrets, configuration propre à l\u0026rsquo;agent, jeton en lecture seule et expirant. Aucun secret de production sur le VPS de l\u0026rsquo;agent ; jetons à portée minimale. Workflow\ntmux ou zellij et mosh, client mobile sur Tailscale. Un worktree par agent, revue humaine des PR. Notifications sur un topic privé. MLflow privé, artefacts sur S3, environnements uv reproductibles. Déploiement et exploitation\nUne CI qui teste et construit ; le déploiement passe par la CI. Reverse proxy HTTPS, supervision, logs. Snapshots avant les sessions autonomes ; sauvegardes testées en restauration. Revue trimestrielle des prix, des versions et des comportements par défaut. Réserves # Dates. État au 31 octobre 2026 ; prix, gammes et comportements par défaut ont changé plusieurs fois cette année. Fait ou pratique. Les configurations tmux, les outils ntfy, Marimo et jupytext, la préférence pour Kamal et les avis sur Contabo et Oracle relèvent de pratiques communautaires. Les chiffres de l\u0026rsquo;auto mode viennent d\u0026rsquo;Anthropic. Sources secondaires. Plusieurs prix viennent d\u0026rsquo;agrégateurs qui se contredisent parfois ; les compteurs d\u0026rsquo;avis de sécurité de Coolify et Dokploy viennent d\u0026rsquo;un seul comparatif. Couverture. Codespaces, Gitpod/Ona, Gemini CLI, Cursor, OpenCode et Aider n\u0026rsquo;ont pas été examinés en détail ; les principes d\u0026rsquo;isolation, de secrets et de réseau s\u0026rsquo;y appliquent de la même façon. Sources # Hostinger : plugin Claude Hostinger : installer Claude Code sur un VPS Doppler : agents et Claude Code Claude Code : sandboxing Claude Code : sandbox environments Claude Code : secure deployment (Agent SDK) Claude Code on the web Claude Code : cloud environments Claude Code : routines Anthropic : auto mode Help Net Security : auto mode VentureBeat : Remote Control Hetzner : ajustement des prix stackvaluelab : hausse Hetzner 2026 zen-cloud : hausse OVH 2026 Scaleway : choisir son GPU Qovery : comparatif des prix GPU 2026 La trifecta létale pour les agents de code Modes sandbox des CLI d\u0026rsquo;IA comparés NetworkChuck : remote-claude-code Guide Claude Code sur VPS (Medium) pethost : Coolify, Dokploy, CapRover et Dokku comparés haloy : outils de déploiement auto-hébergés Fly.io : prix des sandboxes IA ssdnodes : plans Claude ","date":"3 novembre 2026","externalUrl":null,"permalink":"/posts/agents-code-vps/","section":"Articles \u0026 veille","summary":"Un VPS CPU dédié à l’agent, joignable seulement par réseau privé, une production séparée et du GPU loué à l’heure : l’état des lieux au 31 octobre 2026, de l’isolation aux secrets et aux coûts.","title":"Un agent de code sur un VPS : isolation, secrets et coûts","type":"posts"},{"content":"","date":"3 novembre 2026","externalUrl":null,"permalink":"/tags/vps/","section":"Tags","summary":"","title":"VPS","type":"tags"},{"content":"","date":"29 octobre 2026","externalUrl":null,"permalink":"/tags/bsts/","section":"Tags","summary":"","title":"BSTS","type":"tags"},{"content":"","date":"29 octobre 2026","externalUrl":null,"permalink":"/en/tags/causal-inference/","section":"Tags","summary":"","title":"Causal Inference","type":"tags"},{"content":" Fin septembre, je décrivais ma méthode de travail avec Claude Code en cinq règles. Un mois plus tard, trois choses ont bougé : la doctrine d\u0026rsquo;Anthropic sur ce qu\u0026rsquo;il faut écrire à l\u0026rsquo;agent, le mode de permission par défaut, et les outils de vérification. Voici ce que je garde, ce que je change, et les fichiers qui vont avec. Temps de lecture : environ 30 minutes. Cet article complète la première version sans la remplacer : les principes y sont expliqués en détail, ici je ne reprends que ce qui change. Chaque fonctionnalité est présentée la première fois qu\u0026rsquo;elle apparaît.\nNote de transparence. Ces travaux ont été menés à titre personnel, dans un but d\u0026rsquo;apprentissage, avec l\u0026rsquo;assistance d\u0026rsquo;une IA :\npour le code, la relecture technique et méthodologique ; pour comprendre les outils et les méthodes mathématiques ; pour l\u0026rsquo;aide à la rédaction, la traduction et la correction du texte ; pour générer l\u0026rsquo;image de couverture de l\u0026rsquo;article. Le choix du sujet, le protocole, la vérification des résultats et les conclusions sont les miens, et j\u0026rsquo;en assume les erreurs. Comment j\u0026rsquo;utilise l\u0026rsquo;IA\nLa première version tenait en cinq règles : le contexte est la ressource rare, le CLAUDE.md est court, j\u0026rsquo;explore et je planifie avant de coder, rien n\u0026rsquo;est fini sans preuve, et je reste responsable de chaque ligne. Aucune n\u0026rsquo;est devenue fausse. Mais la façon de les tenir a changé, parce que l\u0026rsquo;outil et la documentation ont changé. J\u0026rsquo;ai relu la documentation officielle de Claude Code (version 2.1.312, au 27 octobre 2026), le billet d\u0026rsquo;Anthropic du 24 juillet sur les nouvelles règles du context engineering pour les modèles Claude 5, et le guide de juillet sur la façon de lever ses inconnues avec Fable. Cet article en tire les conséquences pour un data scientist qui code dans le terminal et commite lui-même.\nEn bref. Les cinq règles restent, et chacune prend une forme plus précise :\nLe contexte se mesure, il ne se devine pas : /context au début d\u0026rsquo;une tâche, et je sais ce qui survit à une compaction. Le CLAUDE.md décrit des critères, pas des interdits : le jugement pour le style, les mécanismes pour ce qui ne doit jamais arriver. Avant de planifier, je lève mes inconnues : angles morts, interview, prototype jetable, références plutôt que descriptions. La vérification est une échelle : dans le prompt, puis /goal, un hook Stop, un relecteur en contexte neuf, et moi. Je reste propriétaire du code, et c\u0026rsquo;est écrit dans la configuration : règles deny, hooks, commit manuel. 1. Ce qui a changé depuis septembre # Le tableau résume les six changements qui ont fait bouger ma pratique. Les sections suivantes les reprennent un par un.\nSujet En septembre Au 29 octobre 2026 Philosophie du contexte Règles explicites, exemples, un CLAUDE.md qui rassemble tout ce qu\u0026rsquo;il faut savoir Anthropic a retiré plus de 80 % du prompt système de Claude Code pour les modèles 5, sans perte mesurée. Doctrine : jugement plutôt que règles, interfaces plutôt qu\u0026rsquo;exemples, divulgation progressive. /doctor propose les coupes Mode de permission Manuel par défaut, listes d\u0026rsquo;autorisation, sandbox L\u0026rsquo;auto mode est le mode de départ des sessions interactives : un classifieur revoit les actions non triviales. Les règles deny restent la seule garantie dure Mémoire CLAUDE.md et le raccourci # Mémoire automatique activée par défaut, règles par chemin dans .claude/rules/, audit avec /doctor prompt-audit Vérification Tests et revue humaine /goal (condition de fin évaluée à chaque tour), hooks Stop de type prompt ou agent, /code-review en sous-agent frais, /verify Parallélisme Sous-agents, équipes d\u0026rsquo;agents expérimentales Sous-agents en arrière-plan par défaut, forks avec /subtask, workflows dynamiques écrits en JavaScript Modèles Lignée 4 Lignée 5 : Opus 5.5 par défaut, Sonnet 5.5, Haiku 5.5, Fable 5.1 ; contexte d\u0026rsquo;un million de tokens ; niveaux d\u0026rsquo;effort de low à max Le fil conducteur tient en une phrase : le levier n\u0026rsquo;est plus d\u0026rsquo;écrire de bons prompts, mais d\u0026rsquo;assembler le bon contexte au bon moment, et de déléguer les règles dures à des mécanismes déterministes. Tout le reste en découle.\n2. Le contexte : mesurer, pas deviner # Ce qui entre avant ma première question # Ce que je tape n\u0026rsquo;est qu\u0026rsquo;une petite part de ce que le modèle voit. Le reste est assemblé par Claude Code, et chaque pièce a un coût et un comportement propres. Je ne le savais qu\u0026rsquo;en gros ; la documentation le détaille désormais, et cela change la façon de ranger ses instructions.\nÉlément Quand il charge Ce que j\u0026rsquo;en retiens Prompt système de Claude Code Toujours Environ 4 000 tokens, fortement réduit pour les modèles 5 CLAUDE.md (utilisateur, projet) et rules sans paths Toujours, à chaque requête Coût permanent : chaque ligne se paie à chaque tour Mémoire automatique (MEMORY.md) Toujours Les 200 premières lignes ou 25 ko, au premier atteint Skills La description seulement Le corps n\u0026rsquo;arrive qu\u0026rsquo;à l\u0026rsquo;invocation ; le listing est budgété à environ 1 % de la fenêtre Serveurs MCP Les noms d\u0026rsquo;outils seulement Les schémas restent différés jusqu\u0026rsquo;à l\u0026rsquo;usage Rules avec paths, CLAUDE.md de sous-dossier Au premier fichier correspondant Une convention locale ne coûte rien tant qu\u0026rsquo;on ne touche pas à sa zone Hooks Jamais Coût nul, sauf si le hook renvoie du contenu La conséquence pratique : ce qui doit être vrai à chaque session va en haut de la hiérarchie, ce qui ne sert que parfois descend vers une skill ou une règle par chemin. C\u0026rsquo;est le principe de divulgation progressive, et la section 4 en donne la table de placement.\nCe qui survit à une compaction # Quand la fenêtre se remplit, Claude Code résume l\u0026rsquo;historique (/autocompact règle le seuil). Le résumé est utile, mais avec perte, et je ne savais pas précisément laquelle. La figure montre ce que j\u0026rsquo;ai retenu.\nAprès une compaction, le prompt système, le CLAUDE.md racine, la mémoire et le plan reviennent du disque. Les fichiers récents et le corps des skills reviennent en partie. Les consignes données en conversation sont perdues. Cliquer pour agrandir. Deux lignes de cette figure ont changé ma façon de faire.\nLes consignes données en conversation sont perdues. « Ne pousse pas sur main », dit à l\u0026rsquo;oral au début d\u0026rsquo;une session, ne survit pas. Une limite conversationnelle n\u0026rsquo;est pas une règle : si elle compte, elle va dans une règle deny ou un hook. Le corps des skills revient tronqué, 5 000 tokens par skill et 25 000 au total, la plus ancienne éliminée d\u0026rsquo;abord. Donc l\u0026rsquo;essentiel d\u0026rsquo;un SKILL.md va en tête de fichier, et les détails dans des fichiers annexes. J\u0026rsquo;ajoute à mes CLAUDE.md une consigne de compaction, du type : « lors d\u0026rsquo;une compaction, conserver la liste des fichiers modifiés, les commandes de test et les décisions de modélisation prises ». Et pour une longue session utile, /compact avec une instruction cible, ou /rewind puis « résumer à partir d\u0026rsquo;ici » pour ne compacter qu\u0026rsquo;une partie.\nLes repères que j\u0026rsquo;utilise # Les seuils ci-dessous ne sont pas des dogmes. Ce sont des signaux qui déclenchent un geste précis, et plusieurs sont nouveaux.\nSignal Ce que je fais Début d\u0026rsquo;une tâche complexe /context : vérifier que le CLAUDE.md attendu est bien chargé, repérer un serveur MCP ou une skill gourmands Changement de sujet /clear. Une session propre bat une longue session pleine de corrections Deux corrections ratées sur le même point /clear, puis un premier message qui intègre ce que j\u0026rsquo;ai appris Question annexe sans suite /btw : la réponse reste hors historique, zéro croissance du contexte Exploration large d\u0026rsquo;un dépôt Un sous-agent Explore, ou une recherche strictement cadrée Contexte au-delà de 50 à 60 % sur un modèle à 200 000 tokens, ou bruit visible sur un million Documenter l\u0026rsquo;état dans un fichier, commiter, /clear, repartir en lisant ce fichier L\u0026rsquo;agent ignore une règle du CLAUDE.md Le fichier est trop long ou ambigu : élaguer, préciser, ou convertir en hook Le million de tokens de contexte des modèles 5 ne change pas ces réflexes. Ce n\u0026rsquo;est pas la taille qui protège, c\u0026rsquo;est la pertinence de ce qui est dedans : la dégradation avec le bruit reste réelle, elle arrive seulement plus tard.\n3. Du règlement au jugement # Le billet d\u0026rsquo;Anthropic du 24 juillet expose six bascules « avant / maintenant » pour les modèles Claude 5. Elles ont été intégrées dans /doctor, la commande de diagnostic. Voici chacune, avec ce qu\u0026rsquo;elle change dans mes fichiers.\nAvant Maintenant Ce que je change Donner des règles Laisser l\u0026rsquo;agent juger « Jamais de commentaires », « fonctions de moins de 30 lignes » deviennent des critères : « du code qui ressemble au code environnant », « la reproductibilité d\u0026rsquo;abord », « pas d\u0026rsquo;abstraction non demandée » Donner des exemples Concevoir des interfaces Un script dont la signature parle, backtest.py --horizon 14 --quantiles 0.1,0.5,0.9, en dit plus qu\u0026rsquo;un paragraphe d\u0026rsquo;exemples, et il ne contraint pas l\u0026rsquo;exploration Tout mettre en haut Divulgation progressive CLAUDE.md court ; la procédure de vérification dans une skill ; les conventions d\u0026rsquo;infrastructure dans une règle limitée à infra/** Se répéter Décrire une fois Pas la même instruction dans le CLAUDE.md, dans la skill et dans le prompt. L\u0026rsquo;usage d\u0026rsquo;un outil va dans sa description, une seule fois La mémoire dans le CLAUDE.md Mémoire automatique Claude note lui-même mes corrections et préférences ; le CLAUDE.md garde ce qui se partage avec une équipe Des specs simples Des références riches Une spec peut être une suite de tests, une fonction d\u0026rsquo;un autre dépôt à porter, une maquette HTML, une grille qu\u0026rsquo;un agent vérificateur appliquera La raison donnée : les règles absolues créent des conflits entre le prompt système, les skills et la demande du jour, et l\u0026rsquo;arbitrage coûte en qualité ; les exemples contraignent l\u0026rsquo;exploration plus qu\u0026rsquo;ils ne l\u0026rsquo;orientent.\nGarde-fous et règles de style ne sont pas la même chose # C\u0026rsquo;est la nuance que je ne veux pas perdre. « Laisser l\u0026rsquo;agent juger » concerne les préférences et les conventions. Cela ne supprime pas les garde-fous. La bonne séparation est : le jugement pour le style et les décisions techniques ; des mécanismes déterministes pour ce qui ne doit jamais arriver, comme toucher data/raw/, pousser sur main, lancer terraform apply en production. On retire les règles qui coûtent plus qu\u0026rsquo;elles ne protègent, et on déplace les autres dans l\u0026rsquo;infrastructure. La section 7 montre les miennes.\nAuditer son setup # Trois commandes font ce que je faisais à la main.\n/doctor : pour un CLAUDE.md versionné, propose des coupes sur ce qui est dérivable du code (arborescence, dépendances, vue d\u0026rsquo;architecture) et garde les pièges, les raisons et les conventions qui diffèrent des défauts. /doctor prompt-audit : cherche les instructions écrites pour d\u0026rsquo;anciens modèles, les références à des fichiers ou commandes qui n\u0026rsquo;existent plus, et les contradictions entre CLAUDE.md, rules, skills et sous-agents. Rien n\u0026rsquo;est modifié tant qu\u0026rsquo;on n\u0026rsquo;applique pas. /skill-doctor : le coût en contexte et l\u0026rsquo;usage réel de chaque skill. Mes CLAUDE.md font entre 29 et 66 lignes selon le dépôt. Le plus long est celui de ce blog, parce qu\u0026rsquo;il décrit des gabarits surchargés et des conventions de contenu que rien dans le code n\u0026rsquo;explique. Le test que j\u0026rsquo;applique ligne par ligne n\u0026rsquo;a pas changé : si je l\u0026rsquo;enlève, l\u0026rsquo;agent se trompera-t-il ? Et j\u0026rsquo;ai ajouté un test de non-régression des règles, suggéré par la documentation : une consigne anodine et vérifiable en fin de fichier. Si elle cesse d\u0026rsquo;être suivie, le budget d\u0026rsquo;instructions est saturé.\n4. Où va chaque consigne # La question « où est-ce que j\u0026rsquo;écris ça ? » revient à chaque projet. Claude Code charge dix couches, dans un ordre fixe : CLAUDE.md d\u0026rsquo;organisation, CLAUDE.md personnel, règles personnelles, CLAUDE.md du projet, règles du projet sans chemin, règles avec chemin, CLAUDE.local.md ignoré par git, CLAUDE.md de sous-dossier, mémoire automatique, skills. Les fichiers sont concaténés, pas hiérarchisés : deux instructions contradictoires donnent un choix arbitraire. D\u0026rsquo;où une table de placement, que je tiens à jour.\nSi c\u0026rsquo;est… Alors ça va dans… Un fait vrai à chaque session, non déductible du code : commande de build, variable d\u0026rsquo;environnement requise, piège Le CLAUDE.md du projet, moins de 200 lignes Une convention valable pour une partie de l\u0026rsquo;arbre seulement Une règle avec paths: dans .claude/rules/ Une procédure en plusieurs étapes, un savoir utilisé parfois Une skill, de 50 à 300 lignes ; au-delà, scinder en reference.md, examples.md, scripts/ Un workflow à effets de bord que je veux lancer moi-même Une skill avec disable-model-invocation: true : coût nul tant que je ne l\u0026rsquo;appelle pas Une correction que j\u0026rsquo;ai déjà faite en conversation La mémoire automatique la capte ; si c\u0026rsquo;est une règle d\u0026rsquo;équipe, je l\u0026rsquo;ajoute explicitement au CLAUDE.md Quelque chose qui doit se produire à chaque fois, sans exception Un hook, ou une règle deny / ask dans settings.json Un accès à des données ou à un outil externe Un serveur MCP, ou plus souvent une CLI : gcloud, bq, gh, mlflow Un setup à reproduire sur un second dépôt Un plugin La mémoire automatique : l\u0026rsquo;utiliser sans la subir # En septembre, je décrivais la mémoire comme de courtes notes que l\u0026rsquo;agent écrit quand je lui donne une consigne durable. Elle est maintenant active par défaut, et structurée en quatre types : user (mon rôle, mes préférences), feedback (corrections, approches validées), project (travaux en cours, décisions), reference (où trouver l\u0026rsquo;information hors dépôt). Elle vit par dépôt, dans ~/.claude/projects/\u0026lt;projet\u0026gt;/memory/, partagée entre worktrees, jamais synchronisée vers le cloud.\nCelle de ce blog contient aujourd\u0026rsquo;hui une dizaine de notes. Trois d\u0026rsquo;entre elles ont été écrites après une correction de ma part, et c\u0026rsquo;est leur intérêt :\nles articles visent 30 minutes au compteur de lecture de Hugo ; un article à 40 minutes a été jugé trop long et ramené à 30, en condensant la prose et jamais les figures ; la durée de veille annoncée dans une introduction vient des dates de commit du dépôt associé, pas du plan de départ : « une journée de veille » est devenu « trois semaines » après lecture du git log ; jamais de ligne de co-auteur dans un commit. Je relis ces notes de temps en temps avec /memory, et je supprime celles qui ne sont plus vraies. Chez un client avec des contraintes de confidentialité, la mémoire se désactive par projet. Un point qui m\u0026rsquo;a surpris : les sous-agents n\u0026rsquo;héritent pas de la mémoire principale, sauf les forks. Si une règle doit les atteindre, elle se répète dans le prompt de délégation.\nLes skills : une procédure, pas un manuel # Les commandes et les skills ont fusionné : un fichier .claude/commands/x.md et un dossier .claude/skills/x/SKILL.md créent tous deux /x. Je n\u0026rsquo;écris plus que des skills. Deux exemples réels, tirés de mes dépôts.\nLa première prépare un commit sans le faire. Elle existe parce que je veux rester le seul contributeur de ces dépôts : l\u0026rsquo;agent vérifie que le diff indexé ne couvre qu\u0026rsquo;un sujet, lance les contrôles rapides, et propose un message au format Conventional Commits. Sa description dit quand l\u0026rsquo;appeler, et c\u0026rsquo;est ce que l\u0026rsquo;agent lit :\n--- name: prepare-commit description: Prepare a commit for the maintainer to run: check the staged diff covers one topic, run checks, and propose a Conventional Commit message. Use when the user asks to commit, prepare a commit, or write a commit/PR message. --- Claude never runs `git commit` or `git push` in this repo: the maintainer must be the sole GitHub contributor. 1. Run `git status` and `git diff --staged` […] 2. If the staged diff mixes topics, stop and propose how to split it. 3. Run `make check-fast` and report. Do not propose a commit while it fails. La seconde encadre une migration de base de données, et commence par le test qui échoue. Le reste, ce sont les conventions que l\u0026rsquo;agent ne peut pas deviner : montants en Numeric(14, 2), intervalles en timestamptz UTC semi-ouverts, jamais de Float.\nDimensionnement : moins de 500 lignes pour SKILL.md, l\u0026rsquo;essentiel en tête à cause de la troncature à la compaction, les scripts dans scripts/ où ils sont exécutés sans jamais entrer dans le contexte.\n5. Lever les inconnues avant de planifier # C\u0026rsquo;est l\u0026rsquo;ajout le plus net à ma boucle. Le guide de juillet sur Fable décrit la qualité du travail de l\u0026rsquo;agent comme limitée d\u0026rsquo;abord par la façon dont l\u0026rsquo;utilisateur clarifie ses inconnues : ce qu\u0026rsquo;il sait et dit, ce qu\u0026rsquo;il sait ne pas savoir, ce qu\u0026rsquo;il ne reconnaîtra qu\u0026rsquo;en le voyant, et ce à quoi il n\u0026rsquo;a pas pensé. En data science, la quatrième catégorie est la plus coûteuse : c\u0026rsquo;est la censure des ventes par les ruptures de stock, la variable calculée après coup, le calendrier qui change en cours d\u0026rsquo;historique.\nLa boucle de septembre comptait cinq temps. Elle en compte six.\n1 Explorer En mode plan, l\u0026rsquo;agent lit et répond sans rien modifier. Je cadre l\u0026rsquo;exploration (« lis forecast/cv/ et features/base.py, explique comment les origines glissantes sont calculées ») ou je la délègue à un sous-agent si elle est large. 2 Lever les inconnues Avant d\u0026rsquo;écrire une ligne : une passe « angles morts » (« quelles considérations n\u0026rsquo;ai-je probablement pas envisagées pour ce pricing sous contrainte de capacité ? ») ; puis, pour une fonctionnalité non triviale, une interview, une question à la fois, en priorisant celles dont la réponse changerait l\u0026rsquo;architecture, qui se termine par un SPEC.md. La session suivante, propre, exécute la spec. 3 Planifier Je demande un plan et je l\u0026rsquo;annote dans l\u0026rsquo;éditeur (Ctrl+G), puis « traite toutes mes notes, n\u0026rsquo;implémente pas encore ». Le plan survit à la compaction. Pour un changement descriptible en une phrase, je saute cette étape. 4 Coder En demandant explicitement la vérification : « implémente d\u0026rsquo;après le plan, écris les tests de rolling_origin, lance make test-fast et corrige ». Et un fichier de notes d\u0026rsquo;implémentation, où l\u0026rsquo;agent consigne ses déviations et ses choix conservateurs. 5 Vérifier L\u0026rsquo;échelle de la section 6. 6 Commiter, moi Après revue du diff. Avant de signer un changement que je n\u0026rsquo;ai pas écrit, je demande un explainer et je me fais quizzer : « que se passe-t-il si la série a moins de 14 points ? ». Si je ne sais pas répondre, je ne suis pas prêt à commiter. Deux pratiques du guide m\u0026rsquo;ont paru contre-intuitives, puis utiles. Le prototype jetable avant le vrai code : une maquette de dashboard ou un notebook de cinquante lignes révèle les critères qu\u0026rsquo;on ne sait formuler qu\u0026rsquo;en les voyant. Et les références plutôt que les descriptions : pointer un code existant, même dans un autre langage, une suite de tests ou une maquette HTML. Le code est le langage que l\u0026rsquo;agent lit le mieux ; une maquette est une meilleure spec qu\u0026rsquo;un paragraphe.\n6. La vérification : une échelle, pas une étape # « Rien n\u0026rsquo;est fini sans preuve » reste la règle. Ce qui a changé, c\u0026rsquo;est que la documentation propose maintenant une gradation officielle des moyens de la tenir, et que plusieurs barreaux sont nouveaux.\nCinq niveaux, du moins au plus exigeant. Les trois premiers ferment la boucle sans moi ; les deux derniers sont ceux où je décide. Cliquer pour agrandir. Dans le prompt # Toute demande contient le moyen de prouver qu\u0026rsquo;elle est remplie : « écris la fonction, voici trois cas de test, lance les tests après ». C\u0026rsquo;était déjà le cas en septembre. Ça reste le barreau le plus rentable, parce qu\u0026rsquo;il ne coûte rien à mettre en place.\n/goal : une condition de fin évaluée à chaque tour # Nouveau. /goal \u0026quot;make test exits 0 and MASE on backtest \u0026lt;= 0.82, or stop after 20 turns\u0026quot; confie à un petit modèle le soin de juger la condition après chaque tour ; l\u0026rsquo;agent continue jusqu\u0026rsquo;à « atteint » ou « impossible ». Les règles qui marchent : une seule condition mesurable, un check nommé, une borne de tours. C\u0026rsquo;est l\u0026rsquo;outil du travail non surveillé, couplé à l\u0026rsquo;auto mode. Je ne l\u0026rsquo;ai pas encore utilisé sur une tâche réelle ; la première session documentée (condition, tours, preuves) est le premier point de ma liste de novembre.\nLe hook Stop : une porte déterministe # Celui-là, je l\u0026rsquo;ai depuis le début, et il porte le nom de la règle. Sur mon dépôt de mentorat, qui contient plusieurs projets Python, il relance pytest dans chaque projet dont src/ ou tests/ a changé depuis le dernier commit, et refuse de rendre la main tant qu\u0026rsquo;un test échoue :\n#!/usr/bin/env bash # Stop : « rien n\u0026#39;est fini sans preuve ». Pour chaque projet dont src/ ou tests/ # a changé depuis le dernier commit, lance pytest ; si un test échoue, Claude ne # peut pas s\u0026#39;arrêter. input=$(cat) [[ $(jq -r \u0026#39;.stop_hook_active // false\u0026#39; \u0026lt;\u0026lt;\u0026lt;\u0026#34;$input\u0026#34;) == \u0026#34;true\u0026#34; ]] \u0026amp;\u0026amp; exit 0 # … détection des projets modifiés via git status --porcelain … for p in \u0026#34;${!projets[@]}\u0026#34;; do sortie=$(cd \u0026#34;$p\u0026#34; \u0026amp;\u0026amp; uv run --quiet pytest -q 2\u0026gt;\u0026amp;1); code=$? # 5 = aucun test collecté : rien à prouver, pas un échec (( code != 0 \u0026amp;\u0026amp; code != 5 )) \u0026amp;\u0026amp; echecs+=$\u0026#39;\\n\u0026#39;\u0026#34;### $p\u0026#34;$\u0026#39;\\n\u0026#39;\u0026#34;$(tail -n 25 \u0026lt;\u0026lt;\u0026lt;\u0026#34;$sortie\u0026#34;)\u0026#34; done [[ -n \u0026#34;$echecs\u0026#34; ]] \u0026amp;\u0026amp; jq -n --arg r \u0026#34;Des tests échouent : corriger avant de terminer.$echecs\u0026#34; \\ \u0026#39;{decision: \u0026#34;block\u0026#34;, reason: $r}\u0026#39; Le détail qui compte : le code de retour 5 de pytest (aucun test collecté) n\u0026rsquo;est pas un échec, sinon un projet tout juste initialisé ne pourrait jamais rendre la main. Et le hook lit stop_hook_active pour ne pas se bloquer lui-même. Les hooks Stop existent maintenant en trois types : command, prompt (une question oui / non posée à un modèle, « toutes les tâches sont-elles complètes ? ») et agent (un sous-agent multi-tours, expérimental). Je reste sur command : une suite de tests qui passe est une preuve ; une réponse « oui » d\u0026rsquo;un modèle n\u0026rsquo;en est pas une.\nLe relecteur en contexte neuf # Un agent qui vient d\u0026rsquo;écrire du code est mal placé pour le critiquer. /code-review lance un sous-agent frais qui ne voit que le diff ; /code-review ultra en lance plusieurs dans le cloud. Mon relecteur maison est un fichier dans .claude/agents/, en lecture seule, qui rapporte par gravité décroissante : la justesse (unités, dates sans fuseau, intervalles semi-ouverts, regard vers le futur dans tout ce qui touche aux dates), les chiffres sans provenance, le respect des couches, les tests manquants, et le mélange de sujets dans un même diff. Sa dernière ligne est la plus importante : « si rien n\u0026rsquo;est faux, dis-le simplement ; pas de remarques de style que ruff impose déjà ».\nUn relecteur sommé de trouver des écarts en trouvera, même sur un travail sain. Je lui demande de ne signaler que ce qui touche à la justesse ou aux exigences énoncées, et de traiter le reste comme optionnel. Sinon on dérive vers la sur-ingénierie : couches d\u0026rsquo;abstraction, code défensif, tests de cas impossibles.\nMoi # /verify construit et lance l\u0026rsquo;application pour confirmer qu\u0026rsquo;un changement marche dans le vrai produit, pas seulement dans les tests. Puis je relis le diff dans VS Code et je demande les preuves, sortie de test et commande lancée, plutôt que des assertions de succès. Ça marche aussi pour les sessions que je n\u0026rsquo;ai pas regardées.\nLe test d\u0026rsquo;abord, en deux sessions # L\u0026rsquo;agent écrit toujours l\u0026rsquo;implémentation d\u0026rsquo;abord par défaut, et la formulation qui marche n\u0026rsquo;a pas changé : « écris un test qui échoue pour X, n\u0026rsquo;implémente rien, lance-le et montre-moi l\u0026rsquo;échec », puis « implémente le minimum pour le faire passer, sans toucher au test ». La documentation recommande désormais une variante structurelle que je retiens pour les fonctions qui comptent : deux sessions. La session A écrit les tests ; la session B implémente, sans avoir vu la discussion sur le test. L\u0026rsquo;implémentation ne peut pas « saigner » dans la logique de test. Avec deux terminaux et --worktree, c\u0026rsquo;est cinq minutes de mise en place.\n7. Les garde-fous : l\u0026rsquo;auto mode et ce qui ne doit jamais arriver # Les modes de permission # Depuis la version 2.1.283, les sessions interactives démarrent en auto mode : tout s\u0026rsquo;exécute, sous contrôle d\u0026rsquo;un classifieur en arrière-plan (Sonnet 5 par défaut) qui revoit les actions non triviales. Il bloque par défaut le curl | bash, l\u0026rsquo;envoi de données sensibles à l\u0026rsquo;extérieur, les déploiements et migrations de production, git push --force, git reset --hard, terraform destroy, l\u0026rsquo;octroi de permissions IAM, l\u0026rsquo;affichage de secrets. Après trois blocages consécutifs, il se met en pause et redemande.\nMode Ce qui s\u0026rsquo;exécute sans demander Quand je l\u0026rsquo;utilise default (manuel) Les lectures seulement Premier contact avec un dépôt client, données réelles acceptEdits Lectures, éditions, commandes de fichiers dans le répertoire de travail Refactors locaux que je suis à l\u0026rsquo;écran plan Lectures ; aucune édition tant que le plan n\u0026rsquo;est pas approuvé Exploration et plan auto Tout, sous contrôle du classifieur Le travail courant, /goal, les runs non surveillés dontAsk Uniquement ce qui est pré-approuvé ; le reste est refusé Les lots claude -p en boucle bypassPermissions Tout, sans contrôle Un conteneur isolé sans internet, jamais ailleurs Ce qui reste vrai quel que soit le mode : les règles deny s\u0026rsquo;appliquent partout, même en bypass ; les chemins protégés (.git, .claude, .mcp.json, .bashrc) ne sont jamais auto-approuvés ; et rm sur la racine, le home ou le répertoire de travail ne peut être autorisé par aucune règle. La documentation le dit elle-même : l\u0026rsquo;auto mode réduit les demandes, il ne garantit pas la sécurité. D\u0026rsquo;où un sandbox (/sandbox) ou un devcontainer en plus, pour la défense en profondeur.\nMa configuration # Sur mes dépôts applicatifs, settings.json porte la même liste deny, et elle dit ce que je ne délègue pas :\n\u0026#34;permissions\u0026#34;: { \u0026#34;deny\u0026#34;: [ \u0026#34;Bash(git commit:*)\u0026#34;, \u0026#34;Bash(git push:*)\u0026#34;, \u0026#34;Bash(git rebase:*)\u0026#34;, \u0026#34;Bash(git reset --hard:*)\u0026#34;, \u0026#34;Bash(gh pr merge:*)\u0026#34;, \u0026#34;Read(./.env)\u0026#34;, \u0026#34;Read(./.env.*)\u0026#34;, \u0026#34;Edit(./data/**)\u0026#34;, \u0026#34;Write(./data/**)\u0026#34; ] } Le commit est à moi, la fusion d\u0026rsquo;une pull request aussi, les secrets ne sont jamais lus, les données ne sont jamais modifiées par l\u0026rsquo;agent. Trois hooks complètent la liste, parce que l\u0026rsquo;agent peut aussi modifier des fichiers via le shell :\nun PreToolUse sur Bash, qui n\u0026rsquo;inspecte que les invocations de git et refuse les en-têtes de co-auteur, --no-verify et tout changement de core.hooksPath ; un PreToolUse sur les éditions, qui refuse data/raw/ avec une raison lisible (« les données brutes sont téléchargées et vérifiées par SHA-256 par le code du projet ; écrire dans data/processed/ ») ; un PostToolUse qui lance ruff format sur chaque fichier Python modifié, depuis le projet uv qui le contient. Ce blog, lui, n\u0026rsquo;a ni settings.json ni hook. L\u0026rsquo;enjeu est faible, la preuve est le build, et quand je demande explicitement un commit et un push, l\u0026rsquo;agent les fait, sans ligne de co-auteur. Le niveau de garde-fou suit l\u0026rsquo;enjeu, pas une règle générale : chez un client, je démarre en mode manuel, j\u0026rsquo;active le blocage des lectures hors du répertoire de travail, et j\u0026rsquo;ajoute des règles ask comme points de contrôle sur git push, terraform apply, dvc push et les déploiements.\nSans surveillance # claude -p \u0026quot;…\u0026quot; tourne sans interface, dans la CI par exemple. Deux changements depuis septembre : le mode non interactif démarre désormais en manuel (version 2.1.302), il faut préciser le mode voulu ; et --bare ignore hooks, skills, serveurs MCP et CLAUDE.md locaux, pour rendre le run reproductible. Ma revue de pull request en lecture seule :\nclaude -p \u0026#34;…\u0026#34; --output-format json --allowedTools \u0026#34;Read,Grep,Glob\u0026#34; --max-turns 10 \\ --permission-mode dontAsk --bare 8. Déléguer : sous-agents, forks, workflows # Le tableau de la section 7 de septembre reste valable, avec trois mécanismes de plus. Je le donne sous forme de décision.\nBesoin Mécanisme Pourquoi celui-là L\u0026rsquo;agent se trompe deux fois sur une convention CLAUDE.md Fait permanent, non déductible du code Le même prompt retapé une troisième fois Skill À la demande, avec des scripts exécutés sans entrer dans le contexte Une tâche annexe inonde la conversation Sous-agent Contexte isolé ; seul le résumé remonte Le même traitement sur des dizaines d\u0026rsquo;éléments, ou des résultats à recouper Workflow dynamique Un script décide, pas l\u0026rsquo;agent tour par tour ; le contexte ne garde que le résultat final Quelque chose doit arriver à chaque fois Hook Déterministe Un second dépôt a besoin du même setup Plugin Emballe skills, hooks, sous-agents et MCP ; testable avec claude plugin eval Ce qui a changé pour les sous-agents # Ils tournent en arrière-plan par défaut, /tasks montre ce qui tourne, et un sous-agent ne voit que son propre prompt système, la tâche, la hiérarchie des CLAUDE.md et l\u0026rsquo;état git. Pas l\u0026rsquo;historique, pas la mémoire, pas le style de sortie ; les sous-agents Explore et Plan sautent même le CLAUDE.md. Les forks sont nouveaux : /subtask crée un sous-agent qui hérite de toute la conversation et réutilise le cache de prompt, souvent moins cher qu\u0026rsquo;un sous-agent vierge quand le contexte compte. Un Explore vierge n\u0026rsquo;a d\u0026rsquo;intérêt que si la fenêtre propre est le but. Et l\u0026rsquo;agent délègue rarement de lui-même : je le demande (« utilise un sous-agent pour… ») ou je l\u0026rsquo;inscris dans le CLAUDE.md.\nLes champs utiles dans la définition d\u0026rsquo;un sous-agent : model (haiku pour l\u0026rsquo;exploration bon marché, opus ou fable pour la revue), effort, memory: project, maxTurns, isolation: worktree. Mon statisticien de septembre, en lecture seule, n\u0026rsquo;a pas changé d\u0026rsquo;une ligne.\nLes workflows dynamiques # Un workflow est un script JavaScript que l\u0026rsquo;agent écrit et qu\u0026rsquo;un runtime exécute en arrière-plan : agent(prompt, {schema}), pipeline(), parallel(). Les résultats intermédiaires restent dans les variables du script ; le contexte principal ne reçoit que la conclusion. Il se déclenche par « utilise un workflow » ou le mot-clé ultracode, et /workflows permet de suivre, mettre en pause et sauvegarder un script pour le rejouer.\nLes cas qui me concernent : un audit de fuites temporelles sur tout un dépôt, avec vérification adverse (un agent trouve, un autre tente de réfuter) ; la migration de dizaines de notebooks vers des modules. Je ne l\u0026rsquo;ai pas encore utilisé sur un vrai dépôt, et je commencerai par une petite tranche. Les équipes d\u0026rsquo;agents restent expérimentales ; je ne les utilise pas.\nModèles et effort # La lignée 5 remplace la lignée 4. Mon réglage principal est Fable 5.1, pour les sessions longues et les causes racines ambiguës ; Opus 5.5, le défaut, suffit à la planification et à la revue ; Haiku 5.5 convient aux sous-agents en lecture seule et à l\u0026rsquo;évaluateur de /goal. L\u0026rsquo;alias opusplan (Opus en mode plan, Sonnet en exécution) est un bon compromis coût / qualité. Pour l\u0026rsquo;effort, je commence bas et je monte si la réponse est superficielle ; max reste réservé aux décisions d\u0026rsquo;architecture et au débogage difficile.\n9. Côté métier : forecasting, MLOps, GCP # L\u0026rsquo;arborescence .claude/ d\u0026rsquo;un projet de prévision # Voici comment je range un dépôt de forecasting aujourd\u0026rsquo;hui, en appliquant la table de placement de la section 4 :\n.claude/ ├── CLAUDE.md # \u0026lt; 200 lignes : objectif, commandes, pièges, façon de travailler, compaction ├── rules/ │ ├── temporal-safety.md # sans paths : règle d\u0026#39;or anti-fuite, splits, horizons │ ├── infra.md # paths: [\u0026#34;infra/**\u0026#34;, \u0026#34;*.tf\u0026#34;] │ ├── notebooks.md # paths: [\u0026#34;notebooks/**\u0026#34;] : jetable, pas de logique métier │ └── serving.md # paths: [\u0026#34;src/serving/**\u0026#34;] : FastAPI, latence, schémas ├── skills/ │ ├── verify-forecast/ # tests + backtest + comparaison au baseline │ ├── eda/ # EDA reproductible (scripts/profile.py) │ ├── new-experiment/ # disable-model-invocation : branche + config + run MLflow │ └── causal-check/ # DAG, sanity checks avant toute conclusion causale ├── agents/ │ ├── data-explorer.md # haiku, lecture seule, bq query --dry_run │ ├── forecast-reviewer.md # opus, lecture seule │ └── terraform-engineer.md # plan / validate, jamais apply ├── hooks/ │ └── protect-data.sh # PreToolUse : bloque data/raw/, models/, .env └── settings.json # permissions deny / ask / allow, hooks, sandbox Ce qui n\u0026rsquo;est pas dans le CLAUDE.md : l\u0026rsquo;arborescence (déductible), la liste des dépendances (dans pyproject.toml), la théorie du MASE (le modèle la connaît), les étapes détaillées de vérification (dans la skill). La skill verify-forecast tient en quatre étapes : la suite rapide doit être verte, sinon s\u0026rsquo;arrêter ; lancer le backtest de référence ; comparer MASE et pinball aux chiffres du baseline.json versionné ; rapporter les deltas, les fichiers modifiés et les commandes exécutées. Et une clause qui vient de la pratique : ne pas conclure « amélioration » si la couverture empirique du quantile 0,9 sort de [0,85 ; 0,95].\nCe que l\u0026rsquo;agent a vraiment fait # Le point nouveau côté MLOps, c\u0026rsquo;est la traçabilité des sessions elles-mêmes. MLflow fournit mlflow autolog claude, qui installe des hooks traçant chaque appel d\u0026rsquo;outil comme un span, avec tokens et coût. Pour un data scientist, c\u0026rsquo;est une trace de vérité : ce que l\u0026rsquo;agent a fait, dans l\u0026rsquo;ordre, avec entrées et sorties, relisible après coup et interrogeable en ligne de commande. C\u0026rsquo;est le second point de ma liste de novembre.\nPour le reste, les choix de septembre tiennent : BigQuery en lecture seule (chaque requête est un job facturé), bq --dry_run dans la liste d\u0026rsquo;autorisation et l\u0026rsquo;exécution réelle en ask ; Terraform en boucle generate, validate, plan, lecture, correction, avec une règle limitée à infra/** pour les interdits (pas de 0.0.0.0/0, chiffrement obligatoire, moindre privilège), apply en ask et destroy en deny. Et toujours pas de Claude Code comme orchestrateur de production : les workflows dynamiques servent aux traitements en masse ponctuels, Prefect ou Airflow à la production.\nLes maquettes comme spec # Claude Design est intégré à Claude Code depuis le 16 septembre (/design) ; le site autonome ferme le 14 décembre 2026, et les projets se migrent avant. Pour mon usage, l\u0026rsquo;intérêt est la règle des « références riches » : une maquette de dashboard de prévisions, avec bandes de quantiles et alertes de dérive, montrée au client avant d\u0026rsquo;écrire une ligne de front, puis donnée à l\u0026rsquo;agent comme spec. C\u0026rsquo;est un complément de Figma, pas un remplacement.\n10. Ce que je garde, ce que j\u0026rsquo;ajoute # Pour rester honnête sur l\u0026rsquo;état réel de ma pratique au 29 octobre, voici où j\u0026rsquo;en suis.\nEn place En cours À venir Fichiers CLAUDE.md de 29 à 66 lignes ; skills ; sous-agents statisticien et relecteur ; mémoire par dépôt Consigne de compaction dans chaque CLAUDE.md ; passage de /doctor et prompt-audit Règles par chemin (infra/**, notebooks/**) sur le prochain projet de prévision Garde-fous deny sur commit, push, rebase, merge, .env, data/ ; hooks PreToolUse, PostToolUse, Stop Sandbox pour le travail quotidien en auto mode Mode manuel et blocage des lectures hors répertoire chez un client Vérification Dans le prompt ; hook Stop de tests ; relecteur en contexte neuf ; test d\u0026rsquo;abord /code-review systématique avant commit ; notes d\u0026rsquo;implémentation et quiz Une session /goal complète documentée ; tests en deux sessions sur une fonction qui compte Délégation Sous-agents en lecture seule Forks avec /subtask Premier workflow dynamique sur une tranche : audit de fuites temporelles Traçabilité Commits manuels, README « ce que je corrige par rapport au plan » Tracing MLflow des sessions sur un projet Ce que je ne délègue pas n\u0026rsquo;a pas bougé : le choix de la question, le protocole fixé avant les résultats, l\u0026rsquo;interprétation, les opérations irréversibles, la publication. La différence, c\u0026rsquo;est que ces règles ne reposent plus seulement sur ma vigilance : trois d\u0026rsquo;entre elles sont dans un settings.json.\nLes signaux qui me font changer de cap ont gagné trois lignes.\nSignal Ce que je fais L\u0026rsquo;agent ignore une règle malgré l\u0026rsquo;insistance Élaguer le CLAUDE.md ; convertir en hook si c\u0026rsquo;est un « toujours / jamais » La qualité chute en cours de session /context ; documenter et /clear Un sous-agent revient avec un pavé Resserrer sa description et le format de sortie demandé Je retape la même procédure Une skill L\u0026rsquo;auto mode bloque à répétition Lire la règle nommée dans le motif ; ajuster une règle allow précise, ou signaler le faux positif Je ne sais pas expliquer un changement Ne pas commiter ; demander un explainer et un quiz ; relire le diff Conclusion # En septembre, je concluais que Claude Code avait rendu ma façon de travailler plus exigeante. Un mois plus tard, l\u0026rsquo;exigence s\u0026rsquo;est déplacée. Elle porte moins sur ce que j\u0026rsquo;écris à l\u0026rsquo;agent, puisque les modèles 5 se passent de la plupart des règles, et davantage sur ce que je mets autour : un contexte mesuré et rangé par couches, des mécanismes qui ne dépendent pas de son jugement, et une vérification en plusieurs niveaux dont je suis le dernier.\nSi je ne devais garder qu\u0026rsquo;une phrase de cette révision, ce serait celle-ci : une limite dite en conversation n\u0026rsquo;est pas une règle. Ce qui ne doit jamais arriver va dans un fichier que l\u0026rsquo;agent ne peut pas ignorer. Le reste, il le juge mieux que mes listes d\u0026rsquo;interdits.\nLes outils changent chaque semaine ; dix minutes par mois sur le journal des modifications et un /doctor suffisent à suivre. Ce qui ne change pas : à la fin, le code est à mon nom.\nPour aller plus loin\nBest practices for Claude Code, la référence canonique, mise à jour en place. T. Shihipar, The new rules of context engineering for Claude 5 generation models, Anthropic, 24 juillet 2026 : la doctrine actuelle, à relire avant chaque refonte de CLAUDE.md. T. Shihipar, A field guide to Claude Fable 5: finding your unknowns, Anthropic, 6 juillet 2026 : interviews, prototypes, notes d\u0026rsquo;implémentation, quiz. A harness for every task: dynamic workflows in Claude Code, Anthropic, juin 2026. Documentation : How Claude remembers your project, Explore the context window, Choose a permission mode, Automate actions with hooks, Keep Claude working toward a goal. MLflow × Claude Code : le tracing des sessions. ","date":"29 octobre 2026","externalUrl":null,"permalink":"/posts/claude-code-methode-revisee/","section":"Articles \u0026 veille","summary":"Le levier n’est plus le prompt mais le contexte : ce qu’on y met, quand, et ce qu’on confie à des mécanismes qui ne dépendent pas du modèle. Ma méthode révisée au 29 octobre 2026.","title":"Claude Code en data science : ce qui change dans ma méthode","type":"posts"},{"content":"","date":"29 octobre 2026","externalUrl":null,"permalink":"/tags/context-engineering/","section":"Tags","summary":"","title":"Context Engineering","type":"tags"},{"content":"","date":"29 octobre 2026","externalUrl":null,"permalink":"/tags/contrefactuel/","section":"Tags","summary":"","title":"Contrefactuel","type":"tags"},{"content":"","date":"29 octobre 2026","externalUrl":null,"permalink":"/en/tags/counterfactual/","section":"Tags","summary":"","title":"Counterfactual","type":"tags"},{"content":"","date":"29 octobre 2026","externalUrl":null,"permalink":"/tags/data-science/","section":"Tags","summary":"","title":"Data Science","type":"tags"},{"content":"","date":"29 octobre 2026","externalUrl":null,"permalink":"/tags/double-ml/","section":"Tags","summary":"","title":"Double ML","type":"tags"},{"content":" Passer du contrefactuel validé sur panels synthétiques à un cas client : des promotions qui n\u0026rsquo;ont pas de date de début, un pool de contrôle qui cannibalise, des zéros qui ne sont pas des ventes nulles, et une direction qui veut savoir quelle part de son budget promo ne sert à rien. StatutTerminé · 6 blocs, 7 notebooks, pipeline local Période22 juin – 27 juillet 2026 Stack Python 3.13causal_tspandasstatsmodelsscikit-learnDoubleMLDagsterMLflowuvpytest Code source 2,4 %\ndes sauts de promotion mesurables\n1 592 campagnes sur 67 458 sauts de onpromotion, par une règle fixée avant les résultats 0,83 à 0,90\ncouverture à 95 % sur vérité plantée\nniveau local BSTS sur les vraies séries ; 0,5 à 0,7 pour OLS, contrôle synthétique et DiD 64 %\ndu budget promo non dépensé\nsur la borne basse des intervalles, presque aucune campagne ne vaut ses jours Note de transparence. Ces travaux ont été menés à titre personnel, dans un but d\u0026rsquo;apprentissage, avec l\u0026rsquo;assistance d\u0026rsquo;une IA :\npour le code, la relecture technique et méthodologique ; pour comprendre les outils et les méthodes mathématiques ; pour l\u0026rsquo;aide à la rédaction, la traduction et la correction du texte ; pour générer l\u0026rsquo;image de couverture de cette fiche. Le choix du sujet, le protocole, la vérification des résultats et les conclusions sont les miens, et j\u0026rsquo;en assume les erreurs. Comment j\u0026rsquo;utilise l\u0026rsquo;IA\nCe dépôt applique l\u0026rsquo;article L\u0026rsquo;impact causal en séries temporelles : le contrefactuel et ses placebos à un cahier des charges client. Le package causal_ts de la veille (contrefactuel, placebos, isolation, baselines) est réutilisé en dépendance épinglée ; tout ce qui est propre aux promotions est nouveau. Le dépôt, ses notebooks et ses figures sont en français.\nL\u0026rsquo;objectif # NovaRetail est une chaîne d\u0026rsquo;épicerie fictive qui multiplie les promotions sans connaître leur incrémentalité. Le soupçon de la direction : une large part des ventes « en promo » auraient eu lieu de toute façon, avec en prime du pull-forward (le creux après la promotion) et de la cannibalisation entre familles. Le cahier des charges demandait un contrefactuel par campagne, un uplift avec intervalle, une courbe d\u0026rsquo;élasticité par famille, un backtesting par placebos, une stack orchestrée et un monitoring.\nLes données sont celles de la compétition Kaggle Store Sales (Corporación Favorita, Équateur) : 54 magasins, 33 familles, 1 684 jours, un compte d\u0026rsquo;articles en promotion par jour et par famille, le prix du pétrole, les fériés, et le séisme du 16 avril 2016 comme choc exogène.\nCe que je voulais apprendre tenait en trois questions :\nce que devient un design validé sur panels propres quand le traitement n\u0026rsquo;est pas défini, que les zéros ont plusieurs sens et que les contrôles se cannibalisent ; comment noter un estimateur sur de vraies séries, sans vérité connue, autrement qu\u0026rsquo;en regardant la largeur de ses intervalles ; ce qu\u0026rsquo;un pipeline et un monitoring peuvent surveiller d\u0026rsquo;une quantité que personne n\u0026rsquo;observe jamais, un contrefactuel. La démarche # Le travail suit six blocs, un dossier chacun, avec un README de résultats par bloc. Le code vit dans un package (src/novaretail), un CLI nr enchaîne les étapes et écrit ses résumés dans data/results/*.json, et les sept notebooks appellent le package sans logique propre. Le cahier des charges et les décisions de cadrage sont dans plan.md, et le protocole (seuils de crédibilité, modèle de référence, vérité plantée) y a été fixé avant le premier résultat. La méthode de travail est celle de Claude Code en data science : ce qui change dans ma méthode.\n1 Données Un panel dense, cinq sortes de zéros structurels (magasin pas ouvert, famille jamais vendue, fermeture, famille pas encore référencée, longue série de zéros) mis à NaN plutôt qu\u0026rsquo;imputés, un calendrier avec fériés transférés, paie et gradient du pétrole, et un monde synthétique « façon Favorita » à contrefactuel connu pour développer toute la chaîne avant les fichiers Kaggle. 2 Identification Le traitement est construit : une campagne est un saut de onpromotion au-dessus de sa médiane glissante, pendant au moins trois jours, précédé de 120 jours calmes. Le pool de contrôle est un registre de règles (arêtes de substitution déclarées, pas de promotion dans la fenêtre, pas de même ville), jamais un choix par l\u0026rsquo;ajustement. Puis isolation des donneurs par placebo dans l\u0026rsquo;espace. 3 Estimation Contrefactuel à niveau local de causal_ts par campagne, effets en unités lus trajectoire par trajectoire et scindés pendant, après et net ; contrôle synthétique, contrôle synthétique augmenté, DiD et OLS sur la même interface ; Double ML pour la réponse à l\u0026rsquo;intensité ; le séisme comme banc d\u0026rsquo;essai. 4 Backtesting Vérité plantée dans les vraies séries : effets de 0, 5, 15 et 30 %, formes marche, rampe et impulsion, avec ou sans pull-forward et cannibalisation. Chaque estimateur est noté par sa couverture, ses faux positifs à effet nul et sa puissance, pas par sa largeur. 5 Pipeline Assets Dagster en local (panel, campagnes, estimations, backtest, monitoring, calendrier), MLflow en registre d\u0026rsquo;estimations et de leurs vérifications, manifeste sha256 des fichiers bruts à la place de DVC. Pas de Docker, pas de cloud. 6 Monitoring et calendrier Quatre signaux (ajustement de pré-période, placebo roulant, isolation, stabilité donneur-cible), une réconciliation de marge sur grand livre simulé, et une réallocation du calendrier à budget constant sur la borne basse des intervalles. Ce que les fichiers ont corrigé au cahier des charges # Le cahier des charges décrivait un autre jeu de données et une autre question que ceux que les fichiers permettent. Chaque écart a été tranché sur les données, puis consigné.\nLe cahier des charges dit Ce que les données disent « Favorita n\u0026rsquo;inclut pas les zéros, décider de les reconstruire » Le fichier est dense (une ligne par magasin, famille et jour, sauf les 25 décembre). Le travail est l\u0026rsquo;inverse : trier des zéros présents. 31 % des lignes sont à zéro, et 80 % de ces zéros sont structurels. « Courbe d\u0026rsquo;élasticité-prix par famille » Aucun prix dans Favorita ; onpromotion compte des articles. Le livrable devient une réponse à l\u0026rsquo;intensité promotionnelle par Double ML, nommée ainsi. « Traitement : onpromotion \u0026gt; 0 » La plupart des familles sont promues en permanence à bas niveau. Une campagne est un saut au-dessus du fond, et la part des jours promus mesurables par ce design est le premier résultat. « Familles non promues du même magasin comme contrôles » C\u0026rsquo;est le cas que la veille avait montré biaisé (+26 % avec un donneur cannibalisé, placebo dans le temps vert). Le pool est un registre, puis une isolation. « Intervalle crédible » Intervalles de prévision fréquentistes, notés par leur couverture mesurée sur vérité plantée dans le panel réel. « Séisme : choc exogène parfait » Banc d\u0026rsquo;essai de la méthode, pas une promotion. Les donneurs ont eux-mêmes vendu plus ; l\u0026rsquo;effet est une borne basse. « Uplift prédit contre marge réalisée » Pas de marge dans Favorita. La réconciliation est démontrée sur un grand livre simulé avec une dérive injectée. Les résultats # Tous les chiffres viennent de data/results/*.json, produits par le CLI sur les fichiers Kaggle.\nCombien de promotions sont mesurables # Sur 67 458 sauts de onpromotion, la règle de détection en garde 1 592. Le reste est trop court (le bruit d\u0026rsquo;un compte d\u0026rsquo;articles), tombe dans une pré-période déjà occupée par une autre campagne, ou chevauche un zéro structurel. Les campagnes gardées durent 3 à 20 jours (déciles), 6 en médiane. Sur un échantillon stratifié de 301 campagnes estimées, 170 passent le protocole de crédibilité (R² de pré-période, couverture un pas en avant, Ljung-Box, placebo dans le temps, isolation des donneurs), 114 échouent et 17 n\u0026rsquo;ont pas assez de donneurs.\nÀ gauche, le sort de chaque saut de onpromotion : 1 592 campagnes mesurables, 63 302 sauts trop courts. À droite, les campagnes mesurables par famille : dix familles sur 33 n\u0026rsquo;en ont aucune. Ce que vaut une campagne # Sur les 170 campagnes crédibles, le lift médian pendant la campagne est de +1,2 %, avec des déciles à −8 % et +13 %. 7 % des campagnes ont une borne basse positive pendant la campagne, 5 % sur le net (campagne plus 14 jours) ; 1 à 2 % ont une borne haute négative. La somme nette sur les 170 campagnes vaut −0,6 % du contrefactuel. Le pull-forward n\u0026rsquo;est pas mesurable à ce niveau : la part médiane des unités reprises après la campagne est −14 %, un léger surplus plutôt qu\u0026rsquo;un creux, avec des intervalles qui contiennent zéro presque partout.\nUne campagne mesurable, dans les trois panneaux classiques. La bande orange est la fenêtre de campagne ; les 14 jours qui suivent servent à voir le creux de pull-forward. Intervalles par 3 000 trajectoires simulées et 40 tirages de paramètres. Ce que vaut le design # C\u0026rsquo;est la partie qui compte. Sur des fenêtres calmes de vraies séries, avec le vrai registre de donneurs, un effet connu est écrit dans la série, et chaque estimateur est noté sur l\u0026rsquo;effet cumulé.\nCouverture de l\u0026rsquo;intervalle à 95 % sur une marche de +15 % plantée dans une trentaine de fenêtres réelles. Personne n\u0026rsquo;atteint 0,95 ; le niveau local est le moins loin. Estimateur Couverture à 95 % (marche 15 %) Faux positifs à effet nul Puissance à +5 % / +15 % BSTS niveau local 0,83 17 % 20 % / 40 % Contrôle synthétique augmenté 0,90 38 % 43 % / 23 % OLS et BSTS statique 0,60 41 % 37 % / 60 % Contrôle synthétique 0,80 41 % 47 % / 27 % Différence de différences 0,73 34 % 20 % / 30 % Trois lectures :\nPersonne n\u0026rsquo;atteint 95 % sur les vraies séries. La veille mesurait 0,92 à 0,94 pour le niveau local sur panels synthétiques ; ici 0,83 à 0,90 selon la spécification. Les estimateurs à erreurs iid sont à 0,5 à 0,7 et se déclenchent sur du vide quatre fois sur dix. Le taux de faux positifs de 17 % est la référence des placebos. Avec huit fausses dates, on attend 1,4 déclenchement sur du vide ; le seuil du protocole (au plus 2 sur 8) rejette donc un quart des campagnes par construction, et c\u0026rsquo;est voulu. La puissance est faible. Un lift de 5 % sur sept jours est détecté deux fois sur dix, à peine au-dessus du taux de faux positifs ; 15 %, quatre fois sur dix. Ce design ne sait pas mesurer une petite promotion sur une seule série magasin-famille. Part des intervalles qui excluent zéro selon le lift planté, de 0 % (faux positifs) à 30 %. Les estimateurs iid détectent plus parce qu\u0026rsquo;ils se déclenchent plus, y compris sur rien. Le séisme, banc d\u0026rsquo;essai # Les magasins des provinces frappées (Manabí, Esmeraldas) contre vingt magasins de provinces éloignées : +39 % en unités sur huit semaines [+32 % ; +46 %], soit 572 000 unités, et un effet qui croît de +10 % la première semaine à +45 à +55 % jusqu\u0026rsquo;à la huitième. Le placebo dans l\u0026rsquo;espace montre que 5 donneurs sur 20 ont un effet propre qui exclut zéro : les achats d\u0026rsquo;aide d\u0026rsquo;urgence ont fait vendre tout le pays. L\u0026rsquo;effet sur les provinces frappées est donc une borne basse, et l\u0026rsquo;interférence est visible parce qu\u0026rsquo;on sait où regarder.\nÀ gauche, le placebo dans le temps sur quatre fausses dates : une se déclenche. À droite, le placebo dans l\u0026rsquo;espace : chaque donneur à son tour comme cible ; en rouge, ceux dont l\u0026rsquo;effet propre exclut zéro. La réponse à l\u0026rsquo;intensité, et pourquoi elle ne sert pas au calendrier # Le Double ML donne un θ pooled de 0,15 à 0,59 selon ce que les nuisances contiennent (retards, mois, tendance), avec des erreurs standard minuscules. Holdout 0,36 contre 0,34 à l\u0026rsquo;entraînement, DoubleML 0,38 en contre-vérification, dose-réponse monotone par paliers. La concordance avec les lifts des 170 campagnes mesurées est nulle (corrélation 0,02). L\u0026rsquo;instabilité est le résultat : la date d\u0026rsquo;une promotion n\u0026rsquo;est pas « comme aléatoire » sachant des contrôles de calendrier, et le DML ne répare pas cela. Le θ de PRODUCE (5) vient d\u0026rsquo;un onpromotion qui vaut environ 200 un jour par semaine et 0 ou 1 les autres jours : un régime d\u0026rsquo;enregistrement, pas une réponse.\nMonitoring et calendrier # Le placebo roulant sur des fenêtres jamais traitées dépasse 10 % de faux positifs sur deux tiers de la période : le design est en alerte permanente, et c\u0026rsquo;est l\u0026rsquo;information juste. Avec 17 % de faux positifs par donneur et douze donneurs, l\u0026rsquo;isolation en retire deux par bruit sur 114 campagnes ; les coefficients donneur par donneur changent de signe 223 fois entre deux campagnes d\u0026rsquo;une même cible. Les signaux 3 et 4 ne sont lisibles qu\u0026rsquo;une fois le signal 2 revenu sous 10 %.\nÉcart mensuel entre marge réalisée et marge impliquée par les estimations. À gauche, un grand livre propre : une fausse alarme sur 44 mois. À droite, une dérive de 2 % par mois injectée en avril 2016 : première alarme en juillet, trois mois après, comme la règle le demande. Le calendrier réalloue les jours de campagne par magasin, à budget constant, vers les familles dont la valeur par jour est la plus élevée sur la borne basse. Sur 2 327 jours et 53 magasins, 48 jours bougent (2 %) et 1 500 ne sont pas dépensés (64 %) : aucune famille mesurée du magasin n\u0026rsquo;a de valeur positive sur la borne basse. Dix familles sur 33 n\u0026rsquo;ont jamais de campagne mesurable.\nJours de campagne historiques et réalloués par famille, tous magasins. Les familles non mesurées sont inchangées ; presque tout le reste est retiré. Ce qui a coincé # Le traitement était le problème, pas l\u0026rsquo;estimateur. onpromotion est un compte avec un fond permanent, un pic hebdomadaire sur PRODUCE et des régimes d\u0026rsquo;enregistrement qui changent en 2016. La moitié du temps du projet est allée à décider ce qu\u0026rsquo;est un zéro et ce qu\u0026rsquo;est une campagne. Le choix entre BSTS et contrôle synthétique, lui, se tranche par la couverture. Le cahier des charges décrivait un autre fichier. La version 2017 du jeu Favorita omet les ventes nulles ; la version 2021 est dense. Trois sortes de zéros structurels de plus que prévu sont apparues au chargement : 595 des 1 729 séries magasin-famille commencent plus de 30 jours après l\u0026rsquo;ouverture du magasin, PRODUCE a des trimestres entiers à zéro en 2014 et 2015, et les séries de zéros longues ne sont pas de l\u0026rsquo;intermittence. Les trajectoires simulées divergent parfois. Sur l\u0026rsquo;échelle log1p, une trajectoire qui s\u0026rsquo;emballe pèse des millions d\u0026rsquo;unités une fois retransformée. Les trajectoires divergentes sont retirées, comptées et contrôlées ; une campagne qui en a plus de 5 % n\u0026rsquo;est pas crédible par protocole. Les vraies séries coûtent dix points de couverture. Zéros, valeurs extrêmes, changements de régime : la différence entre un panel propre et un fichier. Les RMSE de certains estimateurs viennent de quelques fenêtres où un donneur saute et où un modèle sans niveau stochastique extrapole ce saut. Taille des études. 30 fenêtres par cellule du backtesting, soit ±0,1 sur chaque couverture ; 300 campagnes estimées sur 1 592 (13 minutes), un seul tirage de la stratification. Les ordres de grandeur sont stables d\u0026rsquo;une exécution à l\u0026rsquo;autre, la deuxième décimale ne l\u0026rsquo;est pas. Ce que j\u0026rsquo;en retiens # Le soupçon de la direction tient, mais la preuve est faible : la somme nette des 170 campagnes crédibles est nulle à −0,6 %, et le même design ne détecte qu\u0026rsquo;une fois sur cinq un lift de 5 % planté dans ces séries. « Pas d\u0026rsquo;effet mesurable » et « pas d\u0026rsquo;effet » sont deux phrases différentes. Un contrefactuel est une prévision, et son contenu causal tient au design : quelles campagnes, quels donneurs, quels placebos. Ce design se note sur vérité connue avant de produire un chiffre, et la note sur de vraies séries est plus basse que sur des panels synthétiques. Le pool de contrôle se déclare, il ne s\u0026rsquo;ajuste pas : le meilleur ajustement est souvent le substitut le plus proche. Les θ du DML ne sont pas des élasticités, et leur sensibilité à la spécification dit que l\u0026rsquo;hypothèse d\u0026rsquo;identification ne tient pas sur ce panel. Ils sont rapportés pour cela. Ce que la méthode ne voit pas, rien ici ne peut le voir : une relation donneurs-cible qui casse à la date de campagne. Seuls un rapprochement avec une marge réalisée ou un holdout réel tranchent. La recommandation à NovaRetail est de mesurer moins de campagnes et d\u0026rsquo;en ancrer quelques-unes par l\u0026rsquo;expérimentation, avec des magasins témoins tirés au sort. Reproduire # git clone https://github.com/gwils28/novaretail-promo-incrementality cd novaretail-promo-incrementality make install # uv sync (Python 3.13), causal_ts en dépendance git épinglée make test-fast # tests unitaires sur le monde synthétique # déposer store-sales-time-series-forecasting.zip (Kaggle) dans data/raw/, puis : uv run nr ingest \u0026amp;\u0026amp; uv run nr campaigns uv run nr estimate --param-draws 40 # registre, contrefactuel, placebos, isolation, verdict uv run nr backtest --n 30 # vérité plantée, scores par estimateur (2 min) uv run nr quake \u0026amp;\u0026amp; uv run nr dml # séisme (15 s), réponse à l\u0026#39;intensité (3 min) uv run nr monitor \u0026amp;\u0026amp; uv run nr calendar # signaux, réconciliation simulée, réallocation make notebooks # ré-exécute les sept notebooks (10 min) Sans fichiers Kaggle, uv run nr ingest --source synthetic exécute la même chaîne sur le monde synthétique. Données : Corporación Favorita, compétition Kaggle Store Sales - Time Series Forecasting.\n","date":"29 octobre 2026","externalUrl":null,"permalink":"/projects/novaretail-incrementalite-promos/","section":"Projets","summary":"Le contrefactuel BSTS de la veille appliqué aux promotions d’une chaîne d’épicerie fictive sur les données Favorita : campagnes détectées plutôt que supposées, registre de donneurs, couverture mesurée sur vérité plantée (0,83 à 0,90 contre 0,5 à 0,7 pour les estimateurs iid), séisme en banc d’essai, Double ML, pipeline Dagster et monitoring.","title":"Impact causal en séries temporelles : mesurer ce que les promotions d'une chaîne d'épicerie rapportent vraiment","type":"projects"},{"content":"","date":"29 octobre 2026","externalUrl":null,"permalink":"/tags/inf%C3%A9rence-causale/","section":"Tags","summary":"","title":"Inférence Causale","type":"tags"},{"content":"","date":"29 octobre 2026","externalUrl":null,"permalink":"/tags/ml-engineering/","section":"Tags","summary":"","title":"ML Engineering","type":"tags"},{"content":"","date":"29 octobre 2026","externalUrl":null,"permalink":"/en/tags/placebo-tests/","section":"Tags","summary":"","title":"Placebo Tests","type":"tags"},{"content":"","date":"29 octobre 2026","externalUrl":null,"permalink":"/tags/retail/","section":"Tags","summary":"","title":"Retail","type":"tags"},{"content":"","date":"29 octobre 2026","externalUrl":null,"permalink":"/tags/tests-placebo/","section":"Tags","summary":"","title":"Tests Placebo","type":"tags"},{"content":"","date":"27 octobre 2026","externalUrl":null,"permalink":"/tags/causalimpact/","section":"Tags","summary":"","title":"CausalImpact","type":"tags"},{"content":"","date":"27 octobre 2026","externalUrl":null,"permalink":"/tags/espace-d%C3%A9tats/","section":"Tags","summary":"","title":"Espace D'états","type":"tags"},{"content":" Quel a été l\u0026rsquo;effet d\u0026rsquo;une promotion, d\u0026rsquo;un changement de prix, d\u0026rsquo;une annonce, sur une série que l\u0026rsquo;on n\u0026rsquo;a pas pu randomiser ? La réponse habituelle est un contrefactuel : une prévision de ce que la série aurait fait sans l\u0026rsquo;intervention. En la construisant à la main, j\u0026rsquo;ai surtout appris ce que cette prévision ne peut pas dire d\u0026rsquo;elle-même, et ce que les tests placebo en vérifient. Temps de lecture : environ 30 minutes. Les formules sont toujours suivies de leur intuition : on peut les survoler sans perdre le fil. Le dépôt est en anglais, ses figures aussi.\nNote de transparence. Ces travaux ont été menés à titre personnel, dans un but d\u0026rsquo;apprentissage, avec l\u0026rsquo;assistance d\u0026rsquo;une IA :\npour le code, la relecture technique et méthodologique ; pour comprendre les outils et les méthodes mathématiques ; pour l\u0026rsquo;aide à la rédaction, la traduction et la correction du texte ; pour générer l\u0026rsquo;image de couverture de l\u0026rsquo;article. Le choix du sujet, le protocole, la vérification des résultats et les conclusions sont les miens, et j\u0026rsquo;en assume les erreurs. Comment j\u0026rsquo;utilise l\u0026rsquo;IA\nMise à jour du 29 octobre 2026. Cette veille a depuis été appliquée à un cas client fictif, les promotions d\u0026rsquo;une chaîne d\u0026rsquo;épicerie sur le panel Favorita : Impact causal en séries temporelles : mesurer ce que les promotions d\u0026rsquo;une chaîne d\u0026rsquo;épicerie rapportent vraiment. La fiche montre ce que devient le design sur de vraies séries, avec des campagnes à détecter et un pool de contrôle à déclarer. Cet article restitue trois semaines de veille active sur l\u0026rsquo;inférence causale appliquée aux séries temporelles, annoncées à la fin de l\u0026rsquo;article sur la fréquence de réentraînement. Le plan de départ tenait en quatre blocs : deux articles récents à lire à la source, un contrefactuel à construire et à valider par placebo, une décision d\u0026rsquo;architecture pour une mesure d\u0026rsquo;incrémentalité en continu, et l\u0026rsquo;article fondateur de Brodersen et ses collègues à relire. Son critère de réussite : un effet réel dont l\u0026rsquo;intervalle exclut zéro, un effet placebo dont l\u0026rsquo;intervalle contient zéro. Je l\u0026rsquo;ai atteint, puis j\u0026rsquo;ai cherché ce qu\u0026rsquo;il garantit vraiment, sur un panel où la vérité est connue. Tout est reproductible dans le dépôt time_series_causal_statistics.\nEn bref. Les principaux résultats, avec leur périmètre :\nLe critère du plan est atteint sur une vérité connue et sur Volkswagen : −20,9 % [−33,9 % ; −1,7 %] sur le cours de VW après l\u0026rsquo;avis de l\u0026rsquo;EPA, douze fausses dates toutes propres. Le placebo dans le temps ne valide que le modèle pré-intervention. Sur 100 panels où un contrôle est contaminé par l\u0026rsquo;intervention, il est vert aussi souvent que sur des panels propres. La contamination demande le placebo dans l\u0026rsquo;espace ; une relation qui casse à la date de l\u0026rsquo;intervention n\u0026rsquo;est vue par rien. Un placebo qui « trouve » un effet est en partie une propriété du placebo lui-même : avec une pré-période raccourcie, il se déclenche sur 11 à 17 % des panels propres, pour un nominal de 5 %. Les estimateurs statiques (OLS sur les contrôles, contrôle synthétique convexe, différence de différences) sont aussi précis que le niveau local sur l\u0026rsquo;estimation ponctuelle, et six à sept fois trop confiants : 80 % de faux positifs à effet nul. Le port Python du CausalImpact de Google donne les mêmes estimations ponctuelles avec des intervalles deux fois plus étroits. C\u0026rsquo;est son a priori par défaut sur l\u0026rsquo;écart-type du niveau, pas les données : sur 30 panels à vérité connue, il couvre 77 % du temps, contre 90 % à la main. 1. Le problème : une unité, deux mondes # L\u0026rsquo;effet causal d\u0026rsquo;une intervention sur une unité à la date \\(t\\) est la différence entre l\u0026rsquo;observé, \\(y_t\\), et ce qu\u0026rsquo;on aurait observé sans l\u0026rsquo;intervention, \\(y^0_t\\). La seconde quantité n\u0026rsquo;existe plus dès que l\u0026rsquo;intervention a eu lieu : une analyse contrefactuelle est donc une prévision de \\(y^0_t\\) sur la post-période, et l\u0026rsquo;effet est l\u0026rsquo;écart entre l\u0026rsquo;observé et cette prévision.\nSur le panel synthétique, le contrefactuel y0 est connu (en vert) : la différence avec l\u0026rsquo;observé (en bleu) est l\u0026rsquo;effet réel, ici une hausse de 10 % plantée sur 90 jours. En pratique, seule la courbe bleue existe. Tout l\u0026rsquo;enjeu est de rendre cette prévision crédible et honnête sur son incertitude. Mais une bonne prévision n\u0026rsquo;est pas un modèle causal. Le contrefactuel se construit à partir de séries de contrôle : des unités comparables, non touchées, dont la relation avec l\u0026rsquo;unité traitée, apprise avant la date, est extrapolée après. Trois hypothèses portent le résultat, et aucune n\u0026rsquo;est visible dans la qualité de l\u0026rsquo;ajustement pré-intervention :\nHypothèse Ce qu\u0026rsquo;elle signifie pour une série traitée Ce qui la teste la date d\u0026rsquo;intervention est « comme aléatoire » étant donné le passé pas d\u0026rsquo;anticipation, pas d\u0026rsquo;événement simultané propre à l\u0026rsquo;unité traitée placebo dans le temps : une fausse date dans la pré-période doit donner un effet dont l\u0026rsquo;intervalle contient zéro les contrôles ne sont pas affectés, directement ni par ricochet pas d\u0026rsquo;interférence : la promotion sur A ne cannibalise pas B, utilisé comme contrôle placebo dans l\u0026rsquo;espace : chaque contrôle traité comme s\u0026rsquo;il était l\u0026rsquo;unité d\u0026rsquo;intérêt, à la vraie date la relation pré-intervention persiste les coefficients qui lient contrôles et cible restent valables après la date diagnostics de stabilité sur la pré-période, et couverture sur un panel à vérité connue Le protocole : ajuster un modèle structurel sur la pré-période seulement, prévoir le contrefactuel avec un intervalle à 95 %, puis rejouer la même analyse avec une fausse date placée dans la pré-période. Je l\u0026rsquo;ai appliqué sur un panel semi-synthétique à vérité connue, puis sur le panel Volkswagen du dieselgate.\n2. Ce que disent les travaux récents # Time-Aware Synthetic Control (Rho, Illick, Narasipura, Abadie, Hsu et Misra, arXiv 2601.03099, janvier 2026). Le contrôle synthétique classique traite les indices temporels de la pré-période comme interchangeables ; TASC encode le temps par un modèle espace-état linéaire gaussien appris par EM. Il gagne quand le bruit d\u0026rsquo;observation est fort, perd face au contrôle synthétique robuste quand il est faible. Deux écarts avec le plan : Alberto Abadie est le quatrième de six auteurs, pas le signataire principal ; et les « intervalles plus étroits » que CausalImpact sont une affirmation sur la largeur, sans aucune couverture rapportée. La partie 9 mesure ce qu\u0026rsquo;un intervalle étroit peut coûter.\nWhen are time series predictions causal? (Carlson et Shephard, arXiv 2603.20394, mars 2026). L\u0026rsquo;article conceptuel. Son théorème central tient en une phrase : sous une condition d\u0026rsquo;assignation adaptée, une différence de prévisions est un effet causal. Sans cette condition, c\u0026rsquo;est une différence de prévisions. Le tableau de la partie 1 est ma traduction de cette condition pour une seule série traitée.\nBrodersen, Gallusser, Koehler, Remy et Scott (2015), Inferring causal impact using Bayesian structural time-series models, Annals of Applied Statistics. Un espace d\u0026rsquo;états assemblé par blocs (niveau local, tendance, saisonnalité, régression sur les contrôles), des a priori sur les variances et les coefficients, inféré par Gibbs ; la distribution prédictive est définie conjointement sur la post-période pour que la structure sérielle se propage dans l\u0026rsquo;effet cumulé. Trois choses m\u0026rsquo;ont servi : leur étude de simulation note la méthode par deux courbes, puissance et couverture ; leur troisième analyse applique le modèle aux régions non traitées et exige un résultat nul, ce qui est le placebo dans l\u0026rsquo;espace ; et leurs trois hypothèses nommées : covariables non affectées par l\u0026rsquo;intervention, relation pré-intervention qui persiste, structure du modèle intacte. Le plan ne citait que la première.\n3. Le modèle : un niveau local et une régression sur les contrôles # J\u0026rsquo;ai construit le modèle à la main avec statsmodels, pour savoir ce qu\u0026rsquo;il y a dedans, et gardé le port de Google comme contrôle croisé (partie 9). C\u0026rsquo;est celui de Brodersen sans sa couche bayésienne :\n$$ y_t = \\mu_t + \\beta' x_t + \\varepsilon_t, \\qquad \\mu_{t+1} = \\mu_t + \\eta_t, \\qquad \\varepsilon_t \\sim \\mathcal N(0, \\sigma^2_\\varepsilon),\\ \\eta_t \\sim \\mathcal N(0, \\sigma^2_\\eta). $$L\u0026rsquo;intuition. La régression \\(\\beta' x_t\\) explique la part que l\u0026rsquo;unité traitée partage avec ses contrôles ; le niveau \\(\\mu_t\\), une marche aléatoire, absorbe ce que la cible fait toute seule. Les deux variances sont estimées par maximum de vraisemblance sur la pré-période seulement ; le contrefactuel est la prévision du filtre de Kalman sur la post-période, simulée en trajectoires conjointes pour l\u0026rsquo;intervalle de l\u0026rsquo;effet cumulé. Trois modèles sur le même panel :\nstatique niveau local tendance locale RMSE à un pas (pré-période) 3,10 2,94 2,98 R² à un pas 0,57 0,61 0,61 Ljung-Box sur les résidus (p) 0,000 0,94 0,90 effet cumulé estimé (vérité 831) 681 [617 ; 748] 569 [207 ; 926] 530 [120 ; 947] Les résidus de la régression statique sont autocorrélés : il reste une composante lente, que le niveau local absorbe. Mais la vraie différence est dans la prévision : le modèle statique suppose que la part inexpliquée est un bruit indépendant chaque jour, le niveau local suppose qu\u0026rsquo;elle peut dériver.\nMême panel, même pré-période. La régression statique (à gauche) donne un intervalle étroit qui manque la vérité ; le niveau local (au centre) un intervalle large qui la contient ; la tendance locale (à droite) un intervalle plus large encore. La variance de prévision d\u0026rsquo;une régression statique est constante. Un niveau local ajoute une variance linéaire en l\u0026rsquo;horizon \\(h\\), donc un intervalle en \\(\\sqrt h\\) ; une tendance linéaire locale ajoute une pente aléatoire et la variance croît en \\(h^3\\). Pour l\u0026rsquo;effet cumulé, ces puissances montent de deux.\nLargeur de l\u0026rsquo;intervalle à 95 % sur un jour, en fonction de l\u0026rsquo;horizon : plate pour la régression statique, en racine de h pour le niveau local, plus rapide encore pour la tendance locale. Sur ce panel, l\u0026rsquo;intervalle statique manque la vérité et celui du niveau local la contient. Lequel est le bon est une question de couverture, que la partie 5 mesure sur 100 panels.\nUne deuxième source d\u0026rsquo;incertitude. La prévision au maximum de vraisemblance ignore que les paramètres sont eux-mêmes estimés. CausalImpact les intègre par MCMC ; l\u0026rsquo;équivalent fréquentiste bon marché est de les tirer dans la normale asymptotique du maximum de vraisemblance et de mélanger la prévision sur ces tirages. Avec 365 jours de pré-période, l\u0026rsquo;intervalle cumulé bouge à peine : [207 ; 926] devient [203 ; 914].\n4. Le protocole sur une vérité connue # Le panel : une unité traitée et cinq contrôles, 365 jours avant l\u0026rsquo;intervention et 90 après. Toutes les unités chargent sur deux facteurs latents (des marches aléatoires) et un motif hebdomadaire, avec des poids propres, plus un petit niveau propre en marche aléatoire et un bruit d\u0026rsquo;observation. Les contrôles sont des proxys bruités de ce qui fait bouger la cible, pas des copies. L\u0026rsquo;effet planté est une hausse de 10 % sur la post-période, soit 831 unités cumulées, et le contrefactuel \\(y^0\\) est conservé pour noter chaque estimateur.\neffet relatif, IC 95 % effet cumulé probabilité de queue intervention réelle (vérité 10 %, 831) +6,6 % [+2,3 % ; +11,3 %] 569 [207 ; 926] 0,000 placebo au jour 243, données tronquées à la vraie date +0,3 % [−5,3 % ; +6,7 %] 38 [−655 ; 742] 0,461 En haut, l\u0026rsquo;analyse à la vraie date : l\u0026rsquo;intervalle cumulé exclut zéro et contient la vérité. En bas, la même analyse avec une fausse date à deux tiers de la pré-période, sur des données qui s\u0026rsquo;arrêtent à la vraie date : l\u0026rsquo;effet cumulé reste autour de zéro. Les deux critères sont atteints : l\u0026rsquo;intervalle réel exclut zéro et contient la vérité, l\u0026rsquo;intervalle placebo contient zéro. Le placebo est calculé sur des données tronquées à la vraie date, pour qu\u0026rsquo;aucune donnée traitée n\u0026rsquo;entre dans l\u0026rsquo;analyse.\nUn panel prouve que le protocole tourne, pas que la méthode est bonne. Deux extensions rendent le placebo plus informatif :\nLe placebo dans le temps comme balayage. Une fausse date est un tirage. Huit fausses dates réparties sur la pré-période donnent la distribution des « effets » que le design produit quand il ne se passe rien : c\u0026rsquo;est l\u0026rsquo;étalon de l\u0026rsquo;effet réel. Ici, les huit intervalles contiennent zéro. Le placebo dans l\u0026rsquo;espace comme classement. Chaque contrôle est traité à son tour comme l\u0026rsquo;unité d\u0026rsquo;intérêt, à la vraie date. L\u0026rsquo;unité réelle se classe première sur six par effet absolu. À gauche, huit fausses dates avec des fenêtres post de 90 jours : les intervalles contiennent zéro, l\u0026rsquo;effet réel (en orange) est en dehors du nuage. À droite, chaque unité traitée comme si elle était la cible : l\u0026rsquo;unité réelle est la seule dont l\u0026rsquo;intervalle exclut zéro. 5. L\u0026rsquo;intervalle à 95 % est-il un intervalle à 95 % ? # Deux courbes notent la méthode : la part des intervalles qui excluent zéro selon l\u0026rsquo;effet réel (la puissance, et à effet nul le taux de faux positifs), et la part qui contient la vérité (la couverture, qui doit rester près de 95 %). Je les ai tracées sur 100 panels par taille d\u0026rsquo;effet, pour l\u0026rsquo;intervalle au maximum de vraisemblance, pour l\u0026rsquo;intervalle mélangé sur des tirages de paramètres, et pour le placebo dans le temps sur les mêmes panels. Comme l\u0026rsquo;effet ne touche ni la pré-période ni les contrôles, couverture, largeur et biais sont les mêmes à toutes les tailles d\u0026rsquo;effet ; seule la détection bouge.\nestimateur couverture faux positifs à 0 % détection à 1 % 2 % 5 % 10 % largeur moyenne niveau local, intervalle MLE 0,92 0,08 0,10 0,15 0,51 0,94 874 niveau local, 40 tirages de paramètres 0,93 0,07 0,07 0,13 0,47 0,92 912 placebo dans le temps sur les mêmes panels (pré-période de 243 jours, vérité 0) 0,89 0,11 1 321 À gauche, la part des intervalles qui excluent zéro selon l\u0026rsquo;effet réel. À droite, la part qui contient la vérité : un peu sous 95 % pour l\u0026rsquo;intervalle MLE, un point de plus avec les tirages de paramètres, 89 % pour le placebo. La détection est modeste. Une hausse de 1 % est invisible, 5 % est vue une fois sur deux, 10 % presque toujours, avec 90 jours de post-période. C\u0026rsquo;est l\u0026rsquo;ordre de grandeur de la simulation de Brodersen.\nLe placebo se déclenche plus souvent que 5 %. Les fenêtres placebo ont une pré-période plus courte (243 jours au lieu de 365) et un intervalle moins bien calibré : 11 % de faux positifs. Un placebo sur neuf trouve donc un effet sur un design parfaitement valide. Lire le balayage contre son propre taux sur panels propres est plus juste qu\u0026rsquo;une date isolée contre un seuil de 5 %.\nLa longueur de la pré-période compte, parce qu\u0026rsquo;une variance demande plus de points qu\u0026rsquo;une moyenne. Couverture sur 50 panels par cellule, à 5 % d\u0026rsquo;effet :\npré-période (jours) 60 120 240 365 730 niveau local, MLE 0,72 0,84 0,92 0,88 0,92 niveau local, tirages de paramètres 0,88 0,90 0,92 0,92 0,92 OLS sur les contrôles 0,18 0,20 0,22 0,10 0,16 L\u0026rsquo;intervalle MLE sous-couvre sur les pré-périodes courtes ; les tirages de paramètres le corrigent. OLS reste autour de 0,2 à toutes les longueurs. Les tirages de paramètres servent sur les pré-périodes courtes, où la variance de niveau estimée peut s\u0026rsquo;écraser à zéro et redonner un modèle statique trop confiant. Un a priori corrige cela aussi, et c\u0026rsquo;est une raison honnête pour laquelle CausalImpact en met un sur l\u0026rsquo;écart-type du niveau. La partie 9 en montre le revers.\n6. Neuf estimateurs sur les mêmes panels # Chaque estimateur s\u0026rsquo;ajuste sur la pré-période et renvoie l\u0026rsquo;effet cumulé avec l\u0026rsquo;intervalle qu\u0026rsquo;un praticien calculerait pour lui (erreurs indépendantes pour les plus simples). Mêmes 100 panels, effets de 0 et 5 %.\nestimateur couverture faux positifs à 0 % détection à 5 % largeur RMSE moyenne de la pré-période 0,10 0,90 0,88 262 954 tendance seule (tendance locale, sans contrôle) 0,96 0,04 0,15 2 224 559 différence de différences (moyenne des contrôles) 0,12 0,88 0,90 148 400 OLS sur les contrôles, intervalle iid 0,19 0,81 0,93 124 274 contrôle synthétique convexe, intervalle iid 0,17 0,83 0,91 142 335 régression statique en espace d\u0026rsquo;états 0,21 0,79 0,93 135 274 niveau local + régression (MLE) 0,94 0,06 0,57 886 227 niveau local + régression, tirages de paramètres 0,95 0,05 0,53 938 227 tendance locale + régression 0,92 0,08 0,44 1 065 280 En rouge, les estimateurs dont la couverture est sous 90 % (à gauche) ou le taux de faux positifs au-dessus de 10 % (à droite). Seuls les modèles à niveau stochastique et la tendance seule passent les deux. OLS sur les contrôles et la régression statique en espace d\u0026rsquo;états sont le même estimateur. Estimation ponctuelle presque aussi précise que le niveau local (RMSE 274 contre 227), intervalle six à sept fois trop étroit : 80 % de faux positifs à effet nul. Quatre promotions sur cinq qui n\u0026rsquo;ont rien fait seraient déclarées efficaces. C\u0026rsquo;est la version mesurée des « inférences sur-optimistes » que Brodersen cite, d\u0026rsquo;après Bertrand, Duflo et Mullainathan. Le contrôle synthétique convexe est presque sans biais mais hérite du même intervalle iid. La méthode classique le sait : elle rapporte des rangs de permutation plutôt que des intervalles. La différence de différences est moins bonne sur les deux tableaux : la moyenne des contrôles est un mauvais contrôle synthétique quand les chargements diffèrent entre unités. L\u0026rsquo;extrapolation de tendance sans contrôle couvre, avec un intervalle deux fois et demie plus large, et détecte une hausse de 5 % une fois sur sept. Le niveau local est le seul estimateur avec une couverture proche du nominal et des faux positifs près de 5 %. La tendance locale paie sa composante supplémentaire en largeur et en puissance. Ce tableau est, de ces trois semaines, le meilleur argument pour le placebo : le placebo dans le temps d\u0026rsquo;un design OLS se déclencherait lui aussi quatre fois sur cinq.\n7. Quand le design casse : qui attrape quoi # Le vrai livrable n\u0026rsquo;est pas l\u0026rsquo;effet, mais la capacité à dire pourquoi une mesure est invalide. J\u0026rsquo;ai planté quatre violations une à une, avec une hausse de 5 %, 100 panels par scénario :\ncontrôle cannibalisé : l\u0026rsquo;effet traité est retranché, un pour un, des contrôles c0 et c1 (une promotion sur A qui mange les ventes de B et C, utilisés comme contrôles) ; halo sur les contrôles : la même quantité est ajoutée à c0 et c1 ; tendance propre : l\u0026rsquo;unité traitée dérive de +0,1 par jour toute seule depuis le premier jour (environ 36 % par an sur un niveau de 100) ; rupture de relation : les chargements de l\u0026rsquo;unité traitée sur les facteurs communs changent de +30 % à la date de l\u0026rsquo;intervention. Quatre vérifications sur chacune : le placebo dans le temps, le placebo dans l\u0026rsquo;espace, un test de Chow sur les différences entre les deux moitiés de la pré-période, et le biais contre la vérité, qu\u0026rsquo;on ne connaît jamais en pratique. La ligne « propre » donne le taux de fausse alerte de chaque vérification.\nscénario couverture biais relatif placebo temps se déclenche placebo espace signale c0 / c1 signale un contrôle propre Chow rejette propre 0,89 −0,5 % 0,17 0,10 / 0,08 0,24 0,15 contrôle cannibalisé 0,86 +26 % 0,17 0,39 / 0,36 0,34 0,15 halo sur les contrôles 0,82 −27 % 0,17 0,38 / 0,45 0,31 0,15 tendance propre 0,69 +71 % 0,39 0,10 / 0,08 0,24 0,15 rupture de relation 0,56 −11 % 0,17 0,10 / 0,08 0,24 0,15 À gauche, le taux de déclenchement de chaque vérification par scénario. À droite, le biais relatif de l\u0026rsquo;effet cumulé et la couverture. La rupture de relation est biaisée et sous-couverte sans qu\u0026rsquo;aucune vérification ne dépasse son taux de fausse alerte. Contrôle cannibalisé et halo. Le biais est grand (+26 % et −27 %), et pourtant le placebo dans le temps est exactement aussi souvent vert que sur les panels propres : la contamination commence à l\u0026rsquo;intervention, rien ne cloche avant. Le placebo dans l\u0026rsquo;espace signale chaque contrôle contaminé environ quatre fois plus souvent qu\u0026rsquo;un contrôle propre : de la puissance, mais loin de la certitude. Il signale aussi plus souvent des contrôles propres : un donneur contaminé contamine les placebos qui l\u0026rsquo;utilisent. Tendance propre. Le placebo dans le temps se déclenche dans 39 % des panels, plus de deux fois son taux propre : une unité qui dérive toute seule produit des « effets » à n\u0026rsquo;importe quelle date. Le biais est de +71 %, parce que le niveau local prévoit à plat et que la dérive continue. Rupture de relation. Biaisée, sous-couverte, et aucune vérification ne se déclenche au-dessus de son taux propre. Une relation qui cesse de tenir à la date de l\u0026rsquo;intervention est indiscernable, dans les données, d\u0026rsquo;un effet de l\u0026rsquo;intervention : l\u0026rsquo;hypothèse est intestable par construction. Les seules défenses sont extérieures aux données : une raison de croire que la relation persiste, et la réconciliation métier de la partie 10. Isoler un contrôle contaminé # Quand le placebo dans l\u0026rsquo;espace signale plusieurs contrôles, l\u0026rsquo;un peut être le coupable et les autres ses victimes. La procédure retire le pire (la plus petite probabilité de queue), relance la vérification sur le reste, et recommence jusqu\u0026rsquo;à ce que rien ne soit signalé.\nEffet propre de chaque contrôle à la vraie date, avec son intervalle. Les contrôles retirés par l\u0026rsquo;isolation sont marqués ; c0 et c1 sont les deux contrôles contaminés par construction. Sur ce panel, l\u0026rsquo;isolation retire c1, puis c0, puis un contrôle propre, c2. Avec tous les contrôles, l\u0026rsquo;effet est estimé 20 % trop haut ; avec les deux gardés, il est couvert mais très imprécis. Retirer des contrôles échange du biais contre de la variance, et avec cinq donneurs l\u0026rsquo;échange est mauvais. D\u0026rsquo;où la règle de la partie 10 : un réservoir de 10 à 20 donneurs choisis avant la campagne parmi des produits qui ne peuvent pas interagir avec le produit promu. La vérification est une garde, pas une procédure de sélection.\n8. Un cas réel : Volkswagen, 18 septembre 2015 # J\u0026rsquo;ai pris le panel public que le port de CausalImpact utilise comme fixture de test : les cours hebdomadaires de clôture de Volkswagen, BMW et Allianz, 325 semaines de janvier 2011 à mars 2017. L\u0026rsquo;intervention est datée : l\u0026rsquo;avis de violation de l\u0026rsquo;EPA américaine contre Volkswagen, le vendredi 18 septembre 2015. Ici aucune vérité n\u0026rsquo;est disponible, et chaque affirmation doit être défendue par un placebo ou une analyse de sensibilité.\nVW, BMW et Allianz, avec la date de l\u0026rsquo;avis de l\u0026rsquo;EPA. Le cours de VW perd 28 % en une semaine. La première chose à remarquer # La semaine du dimanche 20 septembre 2015 contient déjà le krach : VW perd 28 %. La pré-période s\u0026rsquo;arrête donc au 13 septembre (246 semaines) et la post-période commence au 20 septembre (79 semaines). Mais BMW baisse la même semaine (−8 %) pendant qu\u0026rsquo;Allianz gagne 1 % : tout le secteur a été re-tarifé avant de se reprendre en octobre. Si BMW a réagi au même événement, ce n\u0026rsquo;est pas un contrôle non traité.\nLa relation pré-intervention est-elle stable ? # Une régression glissante sur 52 semaines montre des coefficients qui se promènent : trois cours co-évoluent par le facteur de marché, mais les chargements ne sont pas une loi. Un test de Chow entre les deux moitiés de la pré-période, calculé sur les différences hebdomadaires, ne rejette pas la stabilité (p = 0,42). Assez stable pour essayer, pas assez pour se passer de placebo.\nL\u0026rsquo;effet, avec BMW et Allianz comme contrôles # valeur effet moyen par semaine −33,5 [−65,1 ; −2,1] effet cumulé sur 79 semaines −2 650 [−5 140 ; −169] effet relatif −20,9 % [−33,9 % ; −1,7 %] probabilité de queue 0,018 ajustement à un pas sur la pré-période R² 0,99, couverture 0,92, Ljung-Box p = 1,0 coefficients BMW 0,63, Allianz 0,30 Les trois panneaux habituels : observé et contrefactuel, effet ponctuel, effet cumulé. L\u0026rsquo;intervalle s\u0026rsquo;élargit vite : 79 semaines est un long horizon pour un niveau en marche aléatoire. L\u0026rsquo;intervalle exclut zéro, mais de peu, et il s\u0026rsquo;élargit vite : 79 semaines est un long horizon pour un niveau en marche aléatoire. VW a coté environ un cinquième sous son contrefactuel pendant les dix-huit mois suivants. Le R² à un pas de 0,99 ne dit rien : la prévision à un pas d\u0026rsquo;une marche aléatoire est toujours excellente. Plus parlant : la dispersion des résidus après la date vaut 3,4 fois celle d\u0026rsquo;avant.\nLe placebo dans le temps, un an plus tôt # Je fais comme si l\u0026rsquo;intervention avait eu lieu le 14 septembre 2014, sur des données qui s\u0026rsquo;arrêtent au 13 septembre 2015. L\u0026rsquo;effet estimé est bien indiscernable de zéro : +5,4 % [−8,2 % ; +23,1 %]. Puis le balayage : douze fausses dates entre février 2012 et mars 2015, fenêtres post de 26 semaines.\nDouze fausses dates, fenêtres post de 26 semaines, intervalles à 95 %. Tous contiennent zéro ; l\u0026rsquo;effet réel sur 79 semaines (en orange) est en dehors du nuage. Les douze intervalles contiennent zéro, les estimations placebo se dispersent entre −7,6 % et +9,0 %, et l\u0026rsquo;effet réel est bien en dehors de ce nuage. C\u0026rsquo;est la moitié (b) du critère, celle qui rend la moitié (a) rapportable. Ce placebo valide le modèle pré-intervention ; il ne dit rien sur BMW, qui n\u0026rsquo;a été touché par rien dans les fenêtres placebo.\nLes contrôles ont-ils été traités eux-mêmes ? # La troisième analyse de Brodersen : chaque contrôle comme s\u0026rsquo;il était l\u0026rsquo;unité traitée, avec l\u0026rsquo;autre contrôle comme donneur, à la vraie date.\neffet propre sur 79 semaines sur les 14 semaines après l\u0026rsquo;avis BMW (contrefactuel depuis Allianz) −11,3 % [−27,3 % ; +13,8 %] −1,5 % [−10,5 % ; +9,6 %] Allianz (contrefactuel depuis BMW) +14,7 % [−3,7 % ; +41,6 %] +9,4 % [+1,0 % ; +19,3 %] BMW prévu depuis Allianz, et Allianz prévu depuis BMW, sur les 14 semaines après l\u0026rsquo;avis. Allianz « monte » de 9,4 % par rapport à BMW : c\u0026rsquo;est BMW qui a baissé. Avec deux contrôles, cette vérification est un miroir : « BMW a baissé par rapport à Allianz » et « Allianz a monté par rapport à BMW » sont le même fait. BMW a perdu 8 % puis repris 12 % en trois semaines, Allianz n\u0026rsquo;a pas bougé : BMW est un contrôle douteux, au moins à court terme. Plutôt que de trancher, j\u0026rsquo;ai rapporté l\u0026rsquo;effet sous chaque jeu de contrôles.\nSensibilité : quelle part de la réponse est un choix de modélisation ? # spécification effet relatif IC 95 % exclut zéro BMW + Allianz, niveau local (référence) −20,9 % [−33,9 ; −1,7] oui Allianz seul −27,2 % [−41,8 ; −2,8] oui BMW seul −16,8 % [−31,5 ; +5,9] non sans contrôle, tendance linéaire locale −22,9 % [−45,0 ; +27,2] non BMW + Allianz, régression statique −30,1 % [−31,0 ; −29,1] oui BMW + Allianz, tendance linéaire locale −19,7 % [−35,1 ; +4,4] non BMW + Allianz, log des cours −4,3 % [−8,4 ; +0,2] non BMW + Allianz, pré-période depuis 2013 −21,5 % [−34,9 ; −1,2] oui BMW + Allianz, pré-période depuis 2014 −21,4 % [−34,6 ; −1,5] oui Neuf spécifications. En gris, celles dont l\u0026rsquo;intervalle contient zéro. Le signe ne change jamais ; c\u0026rsquo;est l\u0026rsquo;intervalle qui bouge. Toute spécification en prix avec un contrôle place l\u0026rsquo;estimation entre −15 % et −30 % environ, et le signe ne change jamais. Ce qui bouge, c\u0026rsquo;est l\u0026rsquo;intervalle : la régression statique est étroite avec une assurance que la partie 6 a montrée imméritée, la tendance linéaire locale trop incertaine à 79 semaines pour conclure, BMW seul non significatif. Le rapport défendable est un intervalle de −17 % à −27 %, avec la question du jeu de contrôles signalée.\nLe modèle utilise-t-il l\u0026rsquo;ordre du temps ? # Une vérification bon marché, inspirée de TASC : mélanger les lignes de la pré-période et réajuster. Une régression statique donne la même réponse ; un modèle à niveau stochastique non, parce qu\u0026rsquo;une marche aléatoire mélangée n\u0026rsquo;en est plus une et que la variance du niveau s\u0026rsquo;effondre.\nSur 20 permutations de la pré-période, l\u0026rsquo;effet cumulé du niveau local converge vers la réponse statique, avec un intervalle dix fois plus étroit que l\u0026rsquo;original. Sur 20 permutations, l\u0026rsquo;effet cumulé du niveau local converge vers la réponse statique et son intervalle étroit (largeur 518 contre 4 971 dans l\u0026rsquo;ordre original). L\u0026rsquo;intervalle plus large du niveau local est le prix de la dépendance sérielle ; l\u0026rsquo;intervalle étroit de la régression statique est ce qu\u0026rsquo;on obtient en faisant comme si les semaines étaient échangeables.\n9. Comparaison avec le CausalImpact de Google # Le modèle construit à la main est un cousin fréquentiste de CausalImpact : même espace d\u0026rsquo;états, maximum de vraisemblance au lieu d\u0026rsquo;a priori et de MCMC. J\u0026rsquo;ai fait tourner tfcausalimpact, le port TensorFlow Probability, sur le panel Volkswagen et sur des panels synthétiques.\ncas méthode effet cumulé, IC 95 % largeur VW, BMW + Allianz construit à la main, MLE −2 650 [−5 140 ; −169] 4 971 tfcausalimpact, a priori par défaut sur l\u0026rsquo;écart-type du niveau (0,01) −3 355 [−4 427 ; −2 233] 2 193 tfcausalimpact, a priori à 0,1 −2 878 [−5 212 ; −428] 4 784 Effet cumulé et intervalle à 95 % sous quatre variantes, pour les deux jeux de contrôles. Les estimations ponctuelles sont dans les intervalles les unes des autres ; les largeurs non. Mêmes données, même espace d\u0026rsquo;états, estimations ponctuelles chacune dans l\u0026rsquo;intervalle de l\u0026rsquo;autre, et un intervalle moitié moins large. L\u0026rsquo;a priori par défaut de CausalImpact sur l\u0026rsquo;écart-type du niveau est 0,01, en unités de la série standardisée ; le maximum de vraisemblance donne ici 0,085, huit fois plus. Un a priori serré garde le niveau presque déterministe, qui prévoit à 79 semaines avec une variance presque constante. En le desserrant à 0,1, l\u0026rsquo;intervalle s\u0026rsquo;ouvre jusqu\u0026rsquo;à celui construit à la main. L\u0026rsquo;intervalle étroit était l\u0026rsquo;a priori, pas les données. Ce n\u0026rsquo;est un défaut d\u0026rsquo;aucune implémentation, mais la largeur de l\u0026rsquo;intervalle sur un cas réel est un choix de modélisation, à rapporter comme tel. Sur une vérité connue, on peut demander qui couvre :\n30 panels, hausse de 5 % couverture détection largeur moyenne RMSE secondes par ajustement construit à la main, MLE 0,90 0,53 887 219 0,1 tfcausalimpact, a priori par défaut 0,77 0,80 494 262 15,7 Chaque segment est un intervalle à 95 % recentré sur la vérité : un segment qui manque zéro a manqué la vérité. Les intervalles de tfcausalimpact sont plus courts et en manquent davantage. L\u0026rsquo;a priori par défaut achète un intervalle 45 % plus étroit et une détection plus élevée au prix de la couverture : 77 % contre 90 %. C\u0026rsquo;est un défaut raisonnable pour les séries marketing quotidiennes pour lesquelles la méthode a été conçue, et un mauvais défaut pour une série avec une grande marche aléatoire propre, comme un cours de bourse. Un balayage placebo le révélerait : son taux de faux positifs serait bien au-dessus de 5 %.\nNi « étroit » ni « bayésien » ne sont des synonymes de « calibré ». La couverture sur une vérité connue, ou un balayage placebo sur des données réelles, sont les seules preuves que j\u0026rsquo;ai trouvées, et c\u0026rsquo;est ce qui me ramène aux intervalles « plus étroits » de TASC. Un point pratique, enfin : 0,1 seconde par ajustement contre 12 à 16, ce qui compte pour les balayages et les études de Monte-Carlo.\n10. Mesurer l\u0026rsquo;incrémentalité des promotions en continu # Le troisième bloc du plan était une décision d\u0026rsquo;architecture, sans code : un moteur de promotions lance des campagnes sur des produits en magasin, et pour chaque campagne on veut l\u0026rsquo;effet causal sur les ventes par contrefactuel, parce que le métier n\u0026rsquo;acceptera pas un groupe témoin à chaque promotion. Trois points décident si le chiffre vaut quelque chose.\nSélection et intégrité des contrôles. Avec une part de la hausse cannibalisée sur un produit utilisé comme contrôle, l\u0026rsquo;effet est surestimé d\u0026rsquo;un quart et le placebo dans le temps reste vert dans 83 % des cas. Avant la campagne, les candidats contrôles viennent donc d\u0026rsquo;un registre de donneurs éligibles construit sur le graphe produit : même groupe de magasins et même saisonnalité que la cible, mais une catégorie différente et aucune arête de substitution, quelle que soit la qualité de l\u0026rsquo;ajustement, parce que le contrôle qui s\u0026rsquo;ajuste le mieux est souvent le substitut le plus proche. Le réservoir compte 10 à 20 donneurs, pour pouvoir en perdre. Après la campagne, le placebo dans l\u0026rsquo;espace en isolation leave-one-out signale les donneurs qui ont bougé : un effet propre négatif est une cannibalisation, un effet positif un halo, et l\u0026rsquo;effet est rapporté sur le réservoir conservé.\nSurveiller l\u0026rsquo;hypothèse causale dans le temps. Le contrefactuel est inobservable. Quatre substituts, chacun lié à un mode de défaillance que le panel reproduit :\nSignal Ce qu\u0026rsquo;il protège Règle ajustement pré-période du modèle déployé (R² à un pas, Ljung-Box, couverture de l\u0026rsquo;intervalle à un pas), par campagne le modèle est de la bonne taille pour la série couverture à un pas hors de [0,90 ; 0,98] ou Ljung-Box p \u0026lt; 0,01 bloque le rapport placebo dans le temps glissant sur des produits jamais traités, même modèle, mêmes longueurs, chaque semaine la calibration du design là où la vérité est zéro part des intervalles placebo excluant zéro sur les 50 derniers runs au-dessus de 10 % (le panel a mesuré 11 % sur pré-périodes courtes) lève une alerte ; le taux sur panels propres est la référence placebo dans l\u0026rsquo;espace sur chaque campagne l\u0026rsquo;interférence tout tour d\u0026rsquo;isolation au-delà du premier sur une cible déclenche une revue stabilité de la relation donneur-cible : Chow sur les différences, dérive des coefficients entre campagnes successives de la même cible la relation qui est extrapolée p \u0026lt; 0,01 ou changement de signe d\u0026rsquo;un coefficient bloque le donneur Ce que cette surveillance ne peut pas voir, et le tableau de bord le dit : une relation qui casse à la date de la campagne. Seul le troisième point l\u0026rsquo;attrape.\nLe garde-fou métier. L\u0026rsquo;effet estimé est un résidu de prévision. Il est relié à l\u0026rsquo;argent par une quantité observée plus tard : la marge incrémentale réalisée, réconciliée chaque mois contre la somme des marges contrefactuelles et incrémentales estimées. L\u0026rsquo;écart de réconciliation est une série temporelle : son niveau est bruité, sa dérive est l\u0026rsquo;alarme. Un écart qui croît dans une direction pendant trois mois signifie que les contrefactuels sont systématiquement faux, et c\u0026rsquo;est à cela que ressemble, vu de l\u0026rsquo;extérieur, une rupture de relation que rien d\u0026rsquo;autre ne voit. Deux ancres de plus : un vrai groupe témoin occasionnel sur une petite part des campagnes, comparé à l\u0026rsquo;effet contrefactuel ; et la somme des effets d\u0026rsquo;une même catégorie, qui ne peut pas dépasser longtemps sa croissance observée. Tant que l\u0026rsquo;écart n\u0026rsquo;est pas refermé pour une famille de produits, l\u0026rsquo;effet qui entre dans une décision est la borne basse de l\u0026rsquo;intervalle.\n11. Ce que j\u0026rsquo;en retiens # Ces constats valent pour les dispositifs étudiés ici : un panel semi-synthétique à cinq contrôles et 90 jours de post-période, et un panel de trois cours de bourse.\nLe contrefactuel est une prévision, et son incertitude est celle du modèle sur ce que fait la part inexpliquée dans le temps. Sur ces panels, seul un niveau qui peut dériver donne un intervalle à 95 % qui couvre à 95 %. Les estimateurs statiques sont précis et trop confiants : 80 % de faux positifs à effet nul. C\u0026rsquo;est l\u0026rsquo;argument le plus fort pour ne pas sauter le placebo. Le placebo dans le temps valide le modèle pré-intervention, et rien de ce qui commence à la date de l\u0026rsquo;intervention. Il est aveugle à un contrôle contaminé et à une rupture de relation. Le placebo dans l\u0026rsquo;espace est la vérification de l\u0026rsquo;interférence, avec une puissance modeste et une isolation leave-one-out nécessaire, parce qu\u0026rsquo;un donneur contaminé signale ses voisins. Une relation qui casse à la date de l\u0026rsquo;intervention n\u0026rsquo;est vue par aucune vérification sur les données. Choix des contrôles avant, réconciliation métier après : c\u0026rsquo;est le seul traitement que j\u0026rsquo;ai trouvé. Un placebo qui se déclenche n\u0026rsquo;invalide pas forcément le design. Je lis désormais un balayage contre son taux propre, pas une date contre 5 %. Sur Volkswagen, l\u0026rsquo;effet de l\u0026rsquo;avis de l\u0026rsquo;EPA est de −17 % à −27 % sur 79 semaines, selon le jeu de contrôles, placebo propre à douze fausses dates, BMW signalé comme contrôle douteux. La largeur de l\u0026rsquo;intervalle sur un cas réel est un choix de modélisation. L\u0026rsquo;a priori par défaut de CausalImpact donne un intervalle moitié moins large, qui couvre 77 % au lieu de 90 %. Questions encore ouvertes. Je n\u0026rsquo;ai pas implémenté TASC, ni mesuré sa couverture. Une rampe et une impulsion restent à étudier à la place de l\u0026rsquo;effet en marche d\u0026rsquo;escalier, et le placebo dans l\u0026rsquo;espace n\u0026rsquo;a été mesuré que pour une taille de contamination. La surveillance de la partie 10 est un raisonnement, pas un système en production.\nUne question pour vérifier sa compréhension. Une promotion sur un produit A est mesurée par contrefactuel avec, comme contrôles, trois produits du même rayon choisis pour la qualité de leur ajustement sur la pré-période. L\u0026rsquo;effet est de +12 % [+4 % ; +20 %], et le placebo dans le temps à trois fausses dates est propre. Que vaut ce chiffre ? Le placebo dans le temps ne teste que la pré-période, où les contrôles n\u0026rsquo;étaient contaminés par rien (partie 7). Si la promotion a pris des ventes au même rayon, le contrefactuel est tiré vers le bas et l\u0026rsquo;effet surestimé sans qu\u0026rsquo;aucun placebo dans le temps ne le voie. Il faut le placebo dans l\u0026rsquo;espace sur chacun des trois contrôles, et une raison de les avoir choisis autre que leur ajustement. Reproduire et aller plus loin # Le code, les cinq notebooks et les 22 tests sont dans le dépôt gwils28/time_series_causal_statistics (Python 3.13, versions verrouillées par uv). Chaque tableau de cet article sort d\u0026rsquo;une commande cts synth, cts vw, cts study all ou cts tfci, avec des graines fixées ; les études de Monte-Carlo prennent une quinzaine de minutes et le contrôle croisé une dizaine.\nSources\nBrodersen, Gallusser, Koehler, Remy et Scott, Inferring causal impact using Bayesian structural time-series models, The Annals of Applied Statistics, 9(1), 2015, et l\u0026rsquo;implémentation CausalImpact. Rho, Illick, Narasipura, Abadie, Hsu et Misra, Time-Aware Synthetic Control, arXiv, janvier 2026. Carlson et Shephard, When are time series predictions causal? The potential system and dynamic causal effects, arXiv, mars 2026. tfcausalimpact, port Python de CausalImpact sur TensorFlow Probability, et sa fixture de test pour le panel Volkswagen. ","date":"27 octobre 2026","externalUrl":null,"permalink":"/posts/impact-causal-series-temporelles/","section":"Articles \u0026 veille","summary":"Un contrefactuel est une prévision. Ce que j’ai observé en le construisant à la main, en mesurant sa couverture sur 100 panels à vérité connue, en plantant quatre violations du design et en l’appliquant au cours de Volkswagen après le dieselgate.","title":"L'impact causal en séries temporelles : le contrefactuel et ses placebos","type":"posts"},{"content":"","date":"27 octobre 2026","externalUrl":null,"permalink":"/en/tags/state-space/","section":"Tags","summary":"","title":"State Space","type":"tags"},{"content":" À quelle fréquence réentraîner un modèle de prévision ? La question paraît opérationnelle. En la creusant, j\u0026rsquo;ai trouvé qu\u0026rsquo;elle dépend de ce qu\u0026rsquo;on attend du service : une erreur faible, un modèle récent, des prévisions qui ne changent pas sans raison. Ces trois attentes ne mènent pas à la même fréquence. Temps de lecture : environ 34 minutes. Les formules sont toujours suivies de leur intuition : on peut les survoler sans perdre le fil. Le dépôt est en anglais, ses figures aussi.\nNote de transparence. Ces travaux ont été menés à titre personnel, dans un but d\u0026rsquo;apprentissage, avec l\u0026rsquo;assistance d\u0026rsquo;une IA :\npour le code, la relecture technique et méthodologique ; pour comprendre les outils et les méthodes mathématiques ; pour l\u0026rsquo;aide à la rédaction, la traduction et la correction du texte ; pour générer l\u0026rsquo;image de couverture de l\u0026rsquo;article. Le choix du sujet, le protocole, la vérification des résultats et les conclusions sont les miens, et j\u0026rsquo;en assume les erreurs. Comment j\u0026rsquo;utilise l\u0026rsquo;IA\nCet article restitue ma veille active de la semaine 44 (octobre 2026). Je suis parti d\u0026rsquo;un plan de travail détaillé, avec ses scripts, ses chiffres et ses conclusions. J\u0026rsquo;ai d\u0026rsquo;abord rejoué chaque script tel quel : tous reproduisent leurs chiffres au chiffre près. Puis j\u0026rsquo;ai vérifié chaque lecture de ces chiffres. Plusieurs ne tiennent pas, et c\u0026rsquo;est ce qui m\u0026rsquo;a le plus appris. J\u0026rsquo;ai aussi relu les sources à la source (licences sur le Hub, corps des articles plutôt que leurs résumés) et j\u0026rsquo;ai confronté le tout à des données réelles, sous un protocole écrit avant les résultats. Tout est reproductible dans le dépôt W44_retraining_frequency.\nEn bref. Les principaux résultats, avec leur périmètre :\nSur un banc simulé, un détecteur de ruptures parfait obtient un MASE de 1,37, contre 0,98 pour un réentraînement hebdomadaire fixe. L\u0026rsquo;écart vient surtout de la fenêtre sur laquelle le modèle est réajusté après la détection. Avec une fenêtre qui redémarre à la rupture et un réentraînement hebdomadaire en complément, la même détection fait mieux que le réentraînement continu, sur 5 graines sur 5, avec 1 % de ses refits. Réentraîner moins souvent réduit la fréquence des révisions publiées, mais augmente leur taille. Du continu au mensuel, le saut moyen est multiplié par 224 dans le modèle analytique. Le budget de fraîcheur dépend du quantile servi d\u0026rsquo;un facteur 2 en simulation, et non de 7 comme l\u0026rsquo;indiquait le plan. Sur données réelles, la règle de décision pré-enregistrée ne tranche pas. La latence de clôture des données modifie trois mesures : l\u0026rsquo;erreur du backtest (sous-estimée d\u0026rsquo;environ 6 %), la baseline et le déclencheur. Un tableau de bord avant/après affiche 197 % de l\u0026rsquo;effet réel avec un réentraînement qui ne fait rien, à cause de la régression vers la moyenne. Le seuil de déclenchement permet aussi d\u0026rsquo;estimer l\u0026rsquo;effet au seuil, par régression sur discontinuité. 1. Le problème : choisir une fréquence de réentraînement # Un service de prévision publie, à chaque origine \\(t_0\\), des prévisions pour les \\(H\\) pas suivants. Le modèle qui les produit a été ajusté sur une fenêtre d\u0026rsquo;historique de longueur \\(w\\), à un certain moment. La question opérationnelle est : tous les combien de pas \\(k\\) faut-il le réajuster ? \\(k = 1\\), c\u0026rsquo;est le réentraînement continu ; en données horaires, \\(k = 24\\) est quotidien, \\(k = 168\\) hebdomadaire et \\(k = 720\\) mensuel.\nOn peut décrire ce qu\u0026rsquo;on attend du service selon trois axes :\nAxe L\u0026rsquo;attente Ce qu\u0026rsquo;elle implique pour la fréquence Exactitude l\u0026rsquo;erreur moyenne reste basse réentraîner souvent (un modèle récent suit mieux le niveau) Fraîcheur le modèle servi n\u0026rsquo;a jamais plus de X heures une borne sur \\(k\\), qui peut dépendre de ce qu\u0026rsquo;on sert Stabilité la prévision publiée ne change pas sans raison dépend de la mesure retenue : taille ou fréquence des révisions À cela s\u0026rsquo;ajoute le coût : chaque refit consomme du calcul, ce qui compte sur un parc de centaines de milliers de séries.\nUn dernier paramètre intervient tout au long de l\u0026rsquo;article : la latence de clôture \\(d\\). Une donnée n\u0026rsquo;est pas utilisable dès qu\u0026rsquo;elle existe. Une consommation électrique, une vente, une facture sont consolidées, corrigées, validées, puis seulement disponibles. Ici, \\(d = 48\\) heures : un modèle « réentraîné maintenant », à l\u0026rsquo;instant \\(t\\), est ajusté sur une fenêtre qui se termine à \\(t - 48\\).\nNotation Sens Valeur dans le banc \\(k\\) cadence : pas entre deux refits de 1 à 720 heures \\(w\\) longueur de la fenêtre d\u0026rsquo;ajustement 240 heures \\(d\\) latence de clôture des données 48 heures \\(H\\) horizon de prévision 24 heures \\(\\tau\\) niveau de quantile servi 0,50 · 0,80 · 0,95 Ce paramètre \\(d\\) intervient à trois endroits de l\u0026rsquo;analyse : le backtest, la baseline et le déclencheur.\n2. Ce que disent les travaux récents # J\u0026rsquo;ai lu cinq travaux récents et j\u0026rsquo;en ai écarté un. Pour chacun, je suis allé chercher les chiffres dans le corps de l\u0026rsquo;article, pas dans le résumé, qui n\u0026rsquo;en contient parfois aucun.\nLes deux articles de Marco Zanotti posent le cadre. Le premier (arXiv 2505.00356) étudie dix modèles globaux sur les données de distribution M5 et VN1, du réentraînement continu à l\u0026rsquo;absence de réentraînement. Le second (arXiv 2506.05776) introduit une mesure de stabilité des prévisions probabilistes, le Scaled Multi-Quantile Change (SMQC). Ce qu\u0026rsquo;on trouve dans les corps des articles :\nen prévision ponctuelle, ne jamais réentraîner dégrade l\u0026rsquo;erreur de moins de 5 % sur ces jeux ; un réentraînement mensuel réduit le temps de calcul d\u0026rsquo;environ 75 % ; la prévision probabiliste est plus sensible : la fréquence optimale est d\u0026rsquo;environ 2 semaines sur M5 contre 3 à 4 semaines pour la prévision ponctuelle ; la stabilité ne suit pas une règle simple : sur VN1, réentraîner moins souvent rend NBEATSx et CatBoost plus stables, mais rend le MLP et la régression linéaire moins stables. Sur M5, presque rien ne bouge. La prévision probabiliste semble donc demander des réentraînements un peu plus fréquents que la prévision ponctuelle, avec un effet modéré. Ce point revient en partie 6.\nHarrison Katz (arXiv 2604.06438) traite le réentraînement comme une décision coûteuse. Il surveille une « dette d\u0026rsquo;apprentissage » : la divergence de Kullback-Leibler entre une postérieure fantôme, mise à jour en continu, et la postérieure gelée du modèle déployé. On réentraîne quand le regret attendu de l\u0026rsquo;attente dépasse le coût du refit. Sa politique fait mieux qu\u0026rsquo;un calendrier réglé dans 72 cellules de scénario sur 72, et mieux qu\u0026rsquo;un CUSUM réglé dans 58 sur 72, avec un objectif relatif moyen de 0,975 face au CUSUM. L\u0026rsquo;écart avec un CUSUM bien réglé est donc faible ; l\u0026rsquo;apport principal est un seuil exprimé en unités de coût. L\u0026rsquo;article se présente d\u0026rsquo;ailleurs comme une « preuve de concept » sur simulation, « pas un remplacement universel de la détection de dérive ».\nPILOT (Kwak, Jung, Park, arXiv 2609.39789) apprend un déclencheur de réentraînement. Le résumé ne donne aucun chiffre de gain. J\u0026rsquo;en ai retenu un choix de conception : l\u0026rsquo;étiquette d\u0026rsquo;entraînement du déclencheur est une quantité future (la hausse d\u0026rsquo;erreur à venir), alors que le déclencheur déployé ne lit que des erreurs déjà closes. Cette séparation évite que le déclencheur utilise des données indisponibles au moment de décider. La partie 4 montre l\u0026rsquo;effet de ce type de fuite sur un CUSUM.\nTimestral (Mistral, 15 octobre 2026) est une famille de foundation models de séries temporelles (5M, 28M, 123M et 209M paramètres) qui tourne sur CPU, à 140 ms pour le plus gros sur 8 threads. Elle concerne surtout la mise en service des modèles ; j\u0026rsquo;y reviens en partie 8, notamment pour sa licence.\nÉcarté : DriftGuard (arXiv 2601.08928). Il annonce « 97,8 % de rappel de détection en 4,2 jours » et un « retour sur investissement allant jusqu\u0026rsquo;à 417 », sans unité. Faute d\u0026rsquo;unité, je n\u0026rsquo;ai pas pu interpréter ce second chiffre, et je n\u0026rsquo;ai pas retenu l\u0026rsquo;article comme source.\n3. Le banc d\u0026rsquo;essai # Pour comparer des politiques de réentraînement, il faut un terrain où l\u0026rsquo;on connaît la vérité. J\u0026rsquo;ai donc simulé un parc de 120 séries horaires sur 2 400 pas, servies à horizon 24 par un modèle volontairement simple : la moyenne de la fenêtre, plus un profil journalier sur 24 heures, ajustés sur \\(w = 240\\) heures.\nLes séries subissent deux dérives de nature différente :\ndes ruptures de niveau brutales, tirées selon un processus de Poisson (3,7 par série en moyenne, 448 au total) ; une dérive lente de l\u0026rsquo;amplitude saisonnière, sinusoïdale. Dix politiques sont comparées : jamais ; un calendrier à \\(k \\in \\{1, 24, 48, 168, 720\\}\\) ; un CUSUM bilatéral sur l\u0026rsquo;erreur réalisée ; la dette KL de Katz à deux seuils ; et un détecteur parfait, qui connaît la date exacte de chaque rupture et réentraîne dessus. On mesure l\u0026rsquo;exactitude (MASE), le coût (refits par série) et la stabilité (partie 5).\nPolitique MASE Refits / série jamais 1,6003 0 calendrier mensuel (\\(k = 720\\)) 1,2520 2 calendrier hebdomadaire (\\(k = 168\\)) 0,9829 12 calendrier quotidien (\\(k = 24\\)) 0,9042 85 continu (\\(k = 1\\)) 0,8915 2 063 CUSUM sur l\u0026rsquo;erreur réalisée 0,9189 92,5 dette KL (seuil 2) 0,9546 30,3 détecteur parfait 1,3685 3,7 Effet de la latence de clôture sur le backtest # Première vérification : que se passe-t-il si le backtest ignore \\(d\\), c\u0026rsquo;est-à-dire s\u0026rsquo;il ajuste le modèle sur des données qui, en production, ne seraient pas encore closes ?\nEn bleu, la fenêtre utilisable. La rupture tombe dans la zone pas encore close : le niveau servi (10,03) ne reflète pas le nouveau niveau (12,05). Un backtest sans latence (d = 0) aurait servi 10,24. Sans la latence, le MASE est sous-estimé d\u0026rsquo;environ 6 % pour toutes les politiques : +6,3 % en quotidien, +5,6 % en hebdomadaire, +6,2 % pour CUSUM. Comme l\u0026rsquo;écart est à peu près le même partout, le classement des politiques ne change pas : toute la courbe se décale. Un écart uniforme est difficile à repérer, puisqu\u0026rsquo;il ne modifie pas les comparaisons. Il modifie en revanche le niveau d\u0026rsquo;erreur annoncé.\n4. Le cas du détecteur de ruptures parfait # Ce résultat m\u0026rsquo;a surpris. Un détecteur qui connaît toutes les ruptures, sans fausse alarme ni retard, et qui réentraîne 3,7 fois par série au moment de chaque rupture, obtient un MASE de 1,3685. Un réentraînement hebdomadaire fixe obtient 0,9829. Le détecteur parfait arrive avant-dernier, devant la seule politique qui ne réentraîne jamais.\nLe plan de travail proposait une explication : la dérive saisonnière lente n\u0026rsquo;est pas une rupture, donc un détecteur de ruptures ne la corrige pas. J\u0026rsquo;ai testé cette explication ; les vérifications ci-dessous montrent qu\u0026rsquo;elle ne rend compte que d\u0026rsquo;une petite partie de l\u0026rsquo;écart.\nTrois vérifications # (a) Sans dérive saisonnière, l\u0026rsquo;écart demeure. Avec les seules ruptures de niveau (mêmes ruptures, plus de dérive lente), l\u0026rsquo;hebdomadaire obtient 0,9425 et le détecteur parfait 1,2620. Il ne reste qu\u0026rsquo;un type de dérive, celui que le détecteur repère sans erreur, et l\u0026rsquo;écart est encore de 34 %.\n(b) La fenêtre de réajustement ne contient pas la rupture. Le détecteur se déclenche à l\u0026rsquo;instant \\(t\\) de la rupture. Mais à cause de \\(d\\), la fenêtre d\u0026rsquo;ajustement se termine à \\(t - 48\\) : elle ne contient aucun point postérieur à la rupture. Le refit est ajusté sur l\u0026rsquo;ancien régime. Retarder le refit améliore le score, lentement :\nRetard du refit après la rupture 0 48 120 168 288 (\\(= d + w\\)) MASE 1,3685 1,3699 1,2071 1,1384 1,1110 48 heures après la rupture, la fenêtre ne contient encore qu\u0026rsquo;un point postérieur sur 240. Une moyenne sur 240 pas en est à peine modifiée.\nEn orange, le détecteur parfait avec le réajustement du plan : le niveau servi reste proche de l\u0026rsquo;ancien. En gris, le réentraînement hebdomadaire suit par paliers. En bleu, le détecteur avec une fenêtre qui redémarre à la rupture. (c) Changer ce qui suit la détection. J\u0026rsquo;ai gardé la même détection et changé le réajustement : après une rupture en \\(c\\), réajuster en \\(c + d + 24\\), \\(c + d + 72\\) et \\(c + d + 240\\), sur une fenêtre qui commence à la rupture au lieu de couvrir l\u0026rsquo;ancien régime.\nPolitique MASE Refits / série hebdomadaire 0,9829 12,0 continu 0,8915 2 063 détecteur parfait, réajustement du plan 1,3685 3,7 détecteur parfait + fenêtre redémarrée 0,9489 10,2 détecteur parfait + fenêtre redémarrée + hebdomadaire 0,8789 22,2 La dernière ligne fait mieux que le réentraînement continu, avec 1 % de ses refits. Comme il s\u0026rsquo;agit d\u0026rsquo;un seul tirage, j\u0026rsquo;ai rejoué l\u0026rsquo;expérience sur cinq graines et comparé les politiques par graine (chacune voit les mêmes séries) :\nÀ droite : la fenêtre redémarrée + hebdomadaire fait mieux que le continu sur les 5 graines. À gauche : la fenêtre redémarrée seule fait mieux que l\u0026rsquo;hebdomadaire sur 4 graines sur 5, un écart compatible avec une égalité. Sur ce banc, ce n\u0026rsquo;est donc pas la détection qui pose problème, mais ce qui la suit. Deux choix interviennent : quand réajuster, et sur quelle fenêtre. Avec une fenêtre fixe, la détection apporte peu. Seule, elle ne traite pas la dérive lente. Combinée à une fenêtre qui redémarre à la rupture et à un réentraînement hebdomadaire, elle donne le meilleur score du banc.\nLimite. Ce résultat suppose un détecteur parfait. Avec un vrai CUSUM qui redémarre la fenêtre à son estimation de la date de rupture, le MASE se dégrade (1,0445 contre 0,9179 sans redémarrage), et l\u0026rsquo;instabilité est multipliée par 7 : les fausses alarmes réduisent la fenêtre à quelques cycles bruités. Redémarrer la fenêtre semble donc utile surtout quand la rupture est connue par ailleurs (un changement de tarif, une ouverture de magasin, un incident daté). Je n\u0026rsquo;ai pas réglé la variante CUSUM après coup, pour ne pas choisir une variante après avoir vu son résultat. Le CUSUM et les erreurs non closes # Dans le plan, le CUSUM se distinguait par son ciblage : 40,9 % de ses refits tombaient moins de 72 heures après une rupture, contre 12,2 % pour un calendrier, soit 3,4 fois plus. Mais ce CUSUM lisait l\u0026rsquo;erreur de la prévision pour \\(t\\) à l\u0026rsquo;instant \\(t\\), alors que le banc suppose que les observations ne sont closes qu\u0026rsquo;à \\(t + 48\\). Le déclencheur utilisait donc des données que le modèle, lui, ne pouvait pas utiliser.\nAvec des erreurs closes au même délai, le MASE change à peine (0,9179 contre 0,9189), mais le ciblage tombe à 20,0 % : 1,6 fois le calendrier, et non 3,4. Environ la moitié de l\u0026rsquo;avantage venait de cette fuite. C\u0026rsquo;est la séparation que fait PILOT : le déclencheur déployé ne lit que des erreurs closes.\nL\u0026rsquo;indicateur lui-même mérite une remarque. « Part des refits à moins de 72 heures d\u0026rsquo;une rupture » compte aussi des refits qui ne peuvent rien corriger : avec \\(d = 48\\), un refit moins de 48 heures après une rupture ne la voit pas. C\u0026rsquo;est en partie pour cela que le détecteur parfait atteint 100 % sur cet indicateur tout en ayant l\u0026rsquo;un des moins bons MASE. Sur ce banc, le rappel de détection ne renseigne donc pas sur la précision obtenue.\n5. La stabilité des prévisions publiées # Quand un utilisateur consulte la prévision de demain le lundi, puis le mardi, il s\u0026rsquo;attend à ce qu\u0026rsquo;elle ne change que si quelque chose a changé. Chaque refit révise la prévision publiée. On appelle churn l\u0026rsquo;ampleur moyenne de ces révisions par pas de temps.\nOn pourrait penser que réentraîner moins souvent rend le service plus stable. C\u0026rsquo;est vrai pour la fréquence des révisions, pas pour leur taille.\nChaque point est une politique du banc. Clause A : aucun saut au-dessus de 10 % de l\u0026rsquo;échelle (sous la ligne horizontale). Clause B : une révision visible sur au plus 1 % des heures (à gauche de la ligne verticale). Aucun point n\u0026rsquo;est dans le quadrant inférieur gauche. Le réentraînement mensuel publie des sauts dont le 95e percentile vaut 293 % de l\u0026rsquo;échelle, sur 0,1 % des origines. Le continu publie des sauts de 1,3 %, sur 12,6 % des origines. Les deux manières habituelles d\u0026rsquo;exprimer une exigence de stabilité, « aucune révision supérieure à X % » et « au plus N révisions par mois », classent les politiques dans des ordres opposés. Sur ce banc, aucune cadence ne satisfait les deux à la fois.\nLe churn moyen, souvent suivi, a une limite : il vaut exactement zéro pour un modèle jamais réentraîné. Il ne distingue donc pas un modèle stable d\u0026rsquo;un modèle figé.\nUn modèle analytique : le niveau local # Pour comprendre d\u0026rsquo;où vient cette opposition, j\u0026rsquo;ai pris le modèle le plus simple possible, où tout se calcule à la main : un niveau local. La série vaut un niveau qui suit une marche aléatoire, plus du bruit :\n$$ y_t = L_t + \\varepsilon_t, \\qquad L_t = L_{t-1} + u_t, \\qquad u_t \\sim \\mathcal N(0, q), \\quad \\varepsilon_t \\sim \\mathcal N(0, \\sigma^2). $$Le service publie la moyenne des \\(w\\) dernières observations, réestimée tous les \\(k\\) pas. Le modèle servi a un âge moyen \\(\\bar a = (k-1)/2\\). Son erreur quadratique moyenne se décompose exactement :\n$$ \\text{MSE}(k, w) = \\underbrace{\\sigma^2\\left(1 + \\tfrac{1}{w}\\right)}_{\\text{bruit}} + q\\Big[\\underbrace{(\\bar a + 1)}_{\\text{âge du modèle}} + \\underbrace{\\tfrac{(w-1)(2w-1)}{6w}}_{\\text{retard de la fenêtre}}\\Big]. $$Le dernier terme est le retard du centroïde : une moyenne sur \\(w\\) pas estime le niveau tel qu\u0026rsquo;il était \\((w-1)/2\\) pas plus tôt, et non le niveau actuel. Annuler la dérivée en \\(w\\) donne la fenêtre optimale :\n$$ w^\\star = \\sqrt{\\frac{3\\,(\\sigma^2 + q/6)}{q}} \\approx 27{,}4 \\quad (\\sigma = 1,\\ q = 0{,}004). $$L\u0026rsquo;intuition. Les deux paramètres agissent sur deux termes additifs. La fenêtre détermine son propre retard, la cadence détermine l\u0026rsquo;âge du modèle. Réentraîner plus souvent ne réduit pas le retard dû à une fenêtre trop longue. Le cas d\u0026rsquo;une tendance l\u0026rsquo;illustre : sur \\(y_t = 0{,}01\\,t + \\text{bruit}\\) avec \\(w = 240\\), passer du mensuel au continu supprime 75 % du biais. Les 25 % restants viennent entièrement de la fenêtre, et aucune cadence ne les réduit. Ce biais relève de la spécification du modèle (terme de tendance, pondération) plutôt que de la fréquence de réentraînement.\nPour la stabilité, la variance du saut \\(R\\) entre deux réestimations espacées de \\(k\\) pas (avec \\(a = \\min(k, w)\\) et \\(b = \\max(k, w)\\)) se décompose en deux termes :\n$$ \\operatorname{Var}(R) = \\underbrace{\\frac{2a\\,\\sigma^2}{w^2}}_{\\text{fluctuation d'estimation}} + \\underbrace{\\frac{q}{w^2}\\left[a^2(b - a + 1) + \\frac{(a-1)\\,a\\,(2a-1)}{3}\\right]}_{\\text{mouvement réel du niveau}}. $$Le premier terme correspond au bruit : la fenêtre a glissé, quelques observations bruitées sont entrées et sorties, et la prévision bouge sans que le niveau ait changé. Le second correspond à l\u0026rsquo;information : le niveau a réellement bougé. La part de bruit est le rapport du premier terme à la variance totale, c\u0026rsquo;est-à-dire la part des révisions publiées qui ne reflète pas un changement du niveau.\nJ\u0026rsquo;ai confronté ces formules à une simulation de Monte-Carlo de 300 000 pas sur huit configurations : le MSE concorde à 0,6 % près, la variance des sauts à 1,8 % près, un écart inférieur à l\u0026rsquo;erreur de Monte-Carlo elle-même mesurée sur cinq graines.\nCadence \\(k\\) Saut moyen Churn par pas Saut vs \\(k = 1\\) Churn vs \\(k = 1\\) Part de bruit 1 0,0057 0,00572 ×1 ×1 67,6 % 24 0,0802 0,00334 ×14 ×0,58 8,2 % 168 0,4830 0,00288 ×84 ×0,50 1,6 % 720 1,2787 0,00178 ×224 ×0,31 0,3 % Du continu au mensuel, le saut publié est multiplié par 224 alors que le churn par pas n\u0026rsquo;est divisé que par 3,2. Dans ce modèle, réentraîner moins souvent réduit peu l\u0026rsquo;instabilité totale : elle est publiée moins souvent, mais par sauts plus grands.\nPart de bruit dans la variance des révisions en fonction de la cadence, pour la fenêtre du banc (w = 240) et pour la fenêtre optimale (w* = 27). À cadence continue sur une fenêtre de 240 pas, deux tiers de la variance des révisions viennent du bruit d\u0026rsquo;estimation. Avec la fenêtre optimale \\(w^\\star = 27\\), c\u0026rsquo;est 95 %. Plus la fenêtre est courte, plus cette part est élevée. Autrement dit, à cadence continue, la plupart des révisions publiées reflètent la fluctuation de l\u0026rsquo;estimation plutôt qu\u0026rsquo;un changement du niveau.\nUne correction au plan. Le plan affirmait que le churn par pas atteint un plancher constant, au-delà duquel la cadence n\u0026rsquo;aurait plus d\u0026rsquo;effet. Ce n\u0026rsquo;est vrai que pour \\(k \\ll w\\). À \\(k = w\\), le churn vaut 82 % de ce « plancher », et au-delà il continue de baisser en \\(\\sqrt{q/k}\\) : à \\(k = 720\\), il est déjà en dessous. La cadence garde donc un effet sur le churn, faible (×0,47 de \\(k = 1\\) à \\(k = 240\\)) mais réel. 6. Fraîcheur du modèle et quantile servi # Un service probabiliste ne publie pas une valeur mais des quantiles : la médiane (\\(\\tau = 0{,}50\\)) pour planifier, le quantile 0,95 pour dimensionner un stock ou une capacité. Chacun s\u0026rsquo;évalue avec la perte pinball :\n$$ \\rho_\\tau(y, \\hat q) = \\begin{cases} \\tau\\,(y - \\hat q) \u0026 \\text{si } y \\ge \\hat q,\\\\ (1 - \\tau)\\,(\\hat q - y) \u0026 \\text{sinon.}\\end{cases} $$L\u0026rsquo;intuition. Au quantile 0,95, une prévision trop basse coûte 19 fois plus qu\u0026rsquo;une prévision trop haute du même écart. La perte est minimale quand 95 % des observations tombent sous la prévision.\nLa question de cette partie : à partir de quel âge un modèle cesse-t-il de faire mieux qu\u0026rsquo;une baseline naïve ? J\u0026rsquo;appelle \\(k_{\\max}\\) la plus grande cadence à laquelle le modèle fait encore mieux que la naïve saisonnière (« demain sera comme aujourd\u0026rsquo;hui à la même heure »). C\u0026rsquo;est le budget de fraîcheur qu\u0026rsquo;on pourrait inscrire dans un contrat de service.\nLe résultat de départ # Sur 200 séries simulées, avec des quantiles gaussiens (moyenne plus \\(z_\\tau\\) fois l\u0026rsquo;écart-type des résidus dans la fenêtre), le plan obtenait :\n\\(\\tau = 0{,}50\\) \\(\\tau = 0{,}80\\) \\(\\tau = 0{,}95\\) \\(k_{\\max}\\) 168 h 120 h 24 h dégradation de \\(k = 1\\) à \\(k = 720\\) +41 % +52 % +97 % Le quantile 0,95 demanderait donc un modèle sept fois plus récent que la médiane, ce qui plaiderait pour un budget de fraîcheur propre à chaque quantile. En vérifiant ce résultat, j\u0026rsquo;ai trouvé deux effets qui l\u0026rsquo;expliquent en grande partie.\nLa couverture des quantiles gaussiens # Un quantile 0,95 bien calibré est dépassé 5 % du temps. Celui-ci couvre 90,9 % des observations quand le modèle est récent, et 83,5 % quand il a un mois.\nGaussien (orange) : 91 % de couverture pour un modèle récent, 84 % à un mois. Conforme sur erreurs closes (bleu) : stable autour de 92 %, quel que soit l\u0026rsquo;âge. L\u0026rsquo;écart-type est mesuré dans la fenêtre, sur des résidus in-sample. Il ne tient pas compte de ce que l\u0026rsquo;erreur hors échantillon augmente avec l\u0026rsquo;âge du modèle. Les « +97 % au quantile 0,95 » reflètent donc surtout un défaut de calibration, plus qu\u0026rsquo;un coût de fraîcheur.\nUne alternative : placer les quantiles au quantile empirique des erreurs closes du modèle lui-même, par série et par horizon, sur les 240 dernières origines, recalculé toutes les 24 heures. C\u0026rsquo;est une forme de prédiction conforme. La dégradation de \\(k = 1\\) à \\(k = 720\\) devient alors +19 %, +19 % et +21 % pour les trois quantiles, presque la même. La couverture au quantile 0,95 reste entre 0,915 et 0,923 à toutes les cadences. Elle n\u0026rsquo;atteint pas 0,95 : la garantie conforme suppose des erreurs échangeables, ce que les ruptures de niveau ne respectent pas.\nLes données lues par la baseline # La naïve à 24 h lit les 24 heures les plus récentes, que le modèle n\u0026rsquo;a pas le droit de voir. La naïve à 72 h, sur une fenêtre close, lit ce que le modèle peut lire. À l\u0026rsquo;origine \\(t_0\\), la naïve saisonnière du plan copie \\(y_{t_0-23}, \\dots, y_{t_0}\\). Le modèle, lui, n\u0026rsquo;a accès qu\u0026rsquo;aux données closes à \\(t_0 - 48\\). La comparaison donne à la baseline 48 heures de données auxquelles le modèle n\u0026rsquo;a pas accès. La baseline comparable, sous \\(d = 48\\), est la naïve à 72 heures (la même heure, trois jours plus tôt), et elle est nettement moins bonne.\nC\u0026rsquo;est la situation inverse du backtest de la partie 3 : ici, ignorer la latence avantage la baseline.\nRésultats après correction # Quantiles Baseline \\(k_{\\max}\\) 0,50 0,80 0,95 Rapport 0,50 / 0,95 gaussiens naïve 24 h (plan) 168 120 24 7,0 gaussiens naïve close 168 168 72 2,3 conformes naïve 24 h (plan) 168 168 36 4,7 conformes naïve close 336 336 168 2,0 Avec les deux corrections, le budget de fraîcheur dépend toujours du quantile servi, mais d\u0026rsquo;un facteur 2, stable sur cinq graines. Ce résultat va dans le même sens et a le même ordre de grandeur que ceux de Zanotti : la prévision probabiliste demande des réentraînements un peu plus fréquents que la prévision ponctuelle (2 semaines contre 3 à 4 sur M5), et non sept fois plus.\nLa stabilité dépend aussi de la méthode de calcul des quantiles. Avec des quantiles gaussiens, seul le réentraînement continu respecte « saut p95 ≤ 10 % » au quantile 0,95. Avec des quantiles conformes, toutes les cadences le respectent (de 2,9 % à 8,4 %), mais la recalibration publie une révision visible sur 19 % à 45 % des origines. On retrouve l\u0026rsquo;opposition entre taille et fréquence des révisions de la partie 5.\n7. Données réelles : Monash Electricity # Les résultats précédents viennent de données simulées. Pour les confronter à des données réelles, j\u0026rsquo;ai repris l\u0026rsquo;analyse sur Monash Electricity Hourly : 321 séries de consommation électrique horaire, de 2012 à 2014, sous licence CC-BY-4.0.\nLe protocole # Avant de lancer le moindre calcul, j\u0026rsquo;ai écrit et commité un protocole : les séries retenues, la période d\u0026rsquo;évaluation, les baselines, les métriques et la règle de décision. Sept séries ont été exclues pour plus de 10 % de zéros (dont une entièrement nulle). Restent 314 séries et 8 760 origines horaires sur 2014, avec une perte pinball normalisée série par série.\nLa règle de décision pré-enregistrée : avec des quantiles conformes, comparer \\(k_{\\max}\\) entre la médiane et le quantile 0,95 contre la naïve à 24 heures. L\u0026rsquo;hypothèse du plan (le budget de fraîcheur dépend du quantile) est confirmée si le rapport vaut au moins 4 sur l\u0026rsquo;année et dans au moins 3 trimestres sur 4, contredite s\u0026rsquo;il vaut 1 ou moins sur l\u0026rsquo;année.\nComparaison aux baselines # \\(\\tau = 0{,}50\\) \\(\\tau = 0{,}80\\) \\(\\tau = 0{,}95\\) couverture 0,95 naïve 24 h (lit des données non closes) 0,5001 0,4269 0,1941 0,945 naïve 72 h (close) 0,6410 0,5303 0,2343 0,938 naïve 168 h (close, hebdomadaire) 0,5586 0,4654 0,2190 0,932 modèle gaussien, \\(k = 1\\) 0,5461 0,4289 0,2054 0,909 modèle gaussien, \\(k = 720\\) 0,6294 0,4900 0,2442 0,895 modèle conforme, \\(k = 1\\) 0,5932 0,4713 0,2175 0,909 modèle conforme, \\(k = 720\\) 0,6334 0,4877 0,2208 0,909 Le modèle est moins bon que la naïve à 24 h, qui lit des données non closes (pointillés bas), et meilleur que la naïve close à 72 h (pointillés hauts). Application de la règle de décision # Appliquée telle qu\u0026rsquo;écrite, la règle pré-enregistrée ne tranche pas. Contre la naïve à 24 heures, le modèle ne fait jamais mieux à la médiane, à aucune cadence et à aucun trimestre : \\(k_{\\max} = 0\\) et le rapport n\u0026rsquo;est pas défini. L\u0026rsquo;hypothèse n\u0026rsquo;est ni confirmée, ni contredite.\nJ\u0026rsquo;ai ajouté après coup la comparaison à la naïve close, parce que la partie 6 a montré que la naïve à 24 heures lit des données non closes. C\u0026rsquo;est un écart au protocole, et je le présente comme tel. Contre cette baseline, avec des quantiles conformes, le modèle reste meilleur jusqu\u0026rsquo;à 720 heures à tous les quantiles : le rapport vaut 1,0 et l\u0026rsquo;hypothèse est contredite. Je rapporte les deux résultats ; seul le premier correspond au protocole pré-enregistré.\nCe que montrent ces données :\nContre une baseline qui lit les données les plus récentes, aucune cadence ne fait mieux. Sur cette consommation électrique, la naïve à 24 heures est assez forte pour que ses 48 heures de données supplémentaires suffisent à dépasser le modèle, quelle que soit la cadence. Ignorer la latence pour la baseline fait conclure que le modèle n\u0026rsquo;apporte rien ; l\u0026rsquo;ignorer pour le modèle le fait paraître meilleur qu\u0026rsquo;il n\u0026rsquo;est (+12,8 % au quantile 0,95, en quotidien). Contre la baseline close, la dépendance au quantile est faible avec des quantiles calibrés, et instable sans. En gaussien, j\u0026rsquo;ai obtenu 720 / 720 / 168, puis 336 / 336 / 168 en décalant simplement le calendrier des refits de 240 heures. Une valeur qui varie d\u0026rsquo;un facteur 2 avec ce seul décalage me paraît trop fragile pour servir de seuil contractuel. Le deuxième trimestre fait exception. D\u0026rsquo;avril à juin 2014, le modèle est moins bon que la naïve close à toutes les cadences en conforme. Un budget de fraîcheur unique, mesuré sur l\u0026rsquo;année, masque cette variation. Gaussien à gauche, conforme à droite. Le deuxième trimestre tombe à zéro. Aucune des deux méthodes n\u0026rsquo;est calibrée : environ 91 % de couverture pour un quantile 0,95, à toutes les cadences. Le conforme garde une couverture stable quand le modèle vieillit, mais pas à son niveau nominal : sur une année de consommation, les erreurs ne sont pas échangeables. Le cycle hebdomadaire compte pour un sixième des séries. La semaine moyenne de la série médiane est plate, mais 52 séries (17 %) ont des week-ends au moins 10 % plus bas. Sur ces séries, la naïve hebdomadaire fait nettement mieux que la naïve à 72 heures. Le modèle, avec son profil sur 24 heures, ne capte pas non plus ce cycle. Pour ces séries, ajouter un profil hebdomadaire serait probablement plus utile que de changer la cadence. 8. Le service de prévision en production # Les parties précédentes portent sur le modèle. Celle-ci porte sur le système qui le sert.\nL\u0026rsquo;Open Inference Protocol et la dimension temporelle # Le protocole d\u0026rsquo;inférence standard de l\u0026rsquo;écosystème, l\u0026rsquo;Open Inference Protocol V2 (KServe, Seldon, MLServer, Triton), décrit des requêtes et des réponses faites de tenseurs. Je l\u0026rsquo;ai relu champ par champ : il n\u0026rsquo;a aucun champ pour le temps. Pas d\u0026rsquo;as_of, pas d\u0026rsquo;horizon, pas de niveaux de quantile. Il y a un model_version, mais ni date d\u0026rsquo;entraînement ni version des variables. Le seul point d\u0026rsquo;extension, parameters, est décrit ainsi par la spécification : « Currently no parameters are defined ». Ce qu\u0026rsquo;on y met dépend donc du serveur.\nConséquence pratique : deux requêtes identiques envoyées à deux dates différentes sont la même requête pour le protocole. Un cache dont la clé est (série, horizon) renvoie la même prévision un mois plus tard, et après un réentraînement. En écrivant le test correspondant, j\u0026rsquo;ai d\u0026rsquo;ailleurs constaté que ma propre clé de cache, dans le contrat, omettait la date d\u0026rsquo;entraînement. Avec une clé trop large, le cache peut continuer à servir une prévision ancienne sans que rien ne le signale, ce qui peut masquer, par exemple, un réentraînement qui n\u0026rsquo;a pas eu lieu.\nUne solution est de placer cette information au-dessus du protocole, dans une passerelle de prévision qui ajoute as_of, horizon, quantiles, date d\u0026rsquo;entraînement et versions, et qui construit la clé de cache avec l\u0026rsquo;ensemble. La même passerelle peut refuser de servir un modèle plus ancien que le budget de fraîcheur du quantile demandé, et revenir alors à la naïve close.\nCôté outillage, deux constats en vérifiant l\u0026rsquo;installation sur Python 3.13. mlserver, l\u0026rsquo;implémentation de référence du protocole, n\u0026rsquo;a pas publié de version stable depuis juin 2025 et déclare Requires-Python \u0026lt;3.13. Pourtant, pip install mlserver n\u0026rsquo;échoue pas : il installe sans avertissement la version 1.3.5, de juillet 2023, la dernière sans borne déclarée. Et uv installe la 1.7.1 sans tenir compte de la borne supérieure. Dans les deux cas, aucun message ne signale le problème ; il est donc utile de vérifier la version effectivement installée et les bornes déclarées, pas seulement le succès de l\u0026rsquo;installation. KServe, maintenu, implémente le même protocole.\nCoût de service d\u0026rsquo;un foundation model sur CPU # Timestral rend envisageable un foundation model servi sur CPU. D\u0026rsquo;après les latences publiées, rafraîchir 500 000 séries par jour avec le 209M consomme 163 € de CPU par mois. Mais s\u0026rsquo;il faut tout recalculer dans une fenêtre nocturne de 30 minutes, il faut 39 machines à 8 vCPU, qui coûtent 7 862 € par mois si elles restent allouées.\n500 000 séries : le coût dépend de la capacité de pointe (×48 pour le 209M), pas des secondes de CPU consommées. Soit un facteur 48. Le coût dépend alors de la fenêtre de calcul plus que de la cadence, sauf si la plateforme peut libérer les machines entre deux fenêtres. Et à 1 € de perte de décision par série et par mois, avec un refit toutes les 6 heures, un TSFM n\u0026rsquo;a besoin de faire mieux que le modèle classique que de 0,13 % pour couvrir son coût de calcul. Dans ce scénario, le calcul pèse peu ; l\u0026rsquo;exactitude sur les données concernées et la licence pèsent davantage.\nLa licence des poids de Timestral # Le code de Timestral est sous Apache-2.0, mais pas les poids. Le fichier LICENSE des quatre dépôts du Hub est une « Modified MIT License » qui contient une clause importante : aucun droit d\u0026rsquo;utilisation si le chiffre d\u0026rsquo;affaires mensuel consolidé de l\u0026rsquo;entreprise, ou de l\u0026rsquo;employeur de l\u0026rsquo;utilisateur, dépasse 20 millions de dollars, dérivés et fine-tunes compris. Pour un consultant, c\u0026rsquo;est donc le chiffre d\u0026rsquo;affaires du client qui compte. Les poids de Chronos-2, eux, sont bien sous Apache-2.0 : cette restriction est propre à Timestral.\nLa licence des poids se trouve donc sur le Hub, pas sur le badge du dépôt de code, et son application dépend du client.\nUn contrat de service vérifiable # J\u0026rsquo;ai regroupé ces éléments dans un fichier de contrat, forecast_service.yaml, vérifié par une porte de déploiement déterministe avant toute mise en production. Six règles : la licence est valide pour ce client, la fenêtre de calcul est tenable, la cadence respecte le budget de fraîcheur de chaque quantile, une seule exigence de stabilité est retenue, l\u0026rsquo;effet du réentraînement est mesurable, et la latence de clôture s\u0026rsquo;applique au backtest, à la baseline et au déclencheur.\nPASS G1 weights licence ALLOW under the cap (Modified MIT), re-check monthly PASS G2 compute window 1 workers needed, 2 provisioned PASS G3 freshness budget cadence 24 vs tightest budget 168 FAIL G4 stability clause jump p95 0.540 vs clause 0.1 PASS G5 measurable effect holdout 5%, rdd logging True PASS G6 closing lag d = 48, missing: none BLOCKED (fail_closed) Le candidat (Timestral-28M, 20 000 séries, refit quotidien) est bloqué : un refit quotidien publie au quantile 0,95 des sauts de 54 % de l\u0026rsquo;échelle au 95e percentile, au-delà du seuil de 10 %. Avec l\u0026rsquo;exigence de fréquence à la place (4,2 % d\u0026rsquo;origines révisées, pour un plafond de 15 %), il passe. Le choix entre les deux relève du métier ; la porte de déploiement oblige simplement à l\u0026rsquo;expliciter.\n9. Mesurer l\u0026rsquo;effet d\u0026rsquo;un réentraînement # Dernière question : une fois la politique en place, comment savoir si réentraîner a amélioré les prévisions ? Une réponse fréquente est un tableau de bord qui compare l\u0026rsquo;erreur avant et après chaque refit.\nCe tableau de bord est biaisé par construction. Le refit se déclenche quand l\u0026rsquo;erreur récente dépasse un seuil. Or une erreur élevée vient en partie d\u0026rsquo;un vrai problème et en partie du hasard. Après le refit, le hasard ne se répète pas forcément, et l\u0026rsquo;erreur baisse même si le refit n\u0026rsquo;a rien changé. C\u0026rsquo;est la régression vers la moyenne. La mesure est exacte ; c\u0026rsquo;est la règle de sélection qui produit l\u0026rsquo;écart.\nPour le chiffrer, j\u0026rsquo;ai simulé 400 000 séries, chacune avec un biais lié à l\u0026rsquo;âge du modèle, et un refit qui retire 30 % de ce biais. Effet réel sur les séries déclenchées, mesuré par randomisation : −0,1053.\nDispositif Estimation Lecture tableau de bord avant/après −0,3119 environ trois fois l\u0026rsquo;effet réel placebo : même tableau de bord, refit sans effet −0,2072 197 % de l\u0026rsquo;effet réel, sans aucun changement A/B parmi les séries déclenchées −0,0993, IC [−0,1141 ; −0,0844] l\u0026rsquo;intervalle contient l\u0026rsquo;effet réel A/B, placebo +0,0066, IC [−0,0102 ; +0,0234] zéro est dans l\u0026rsquo;intervalle Environ 66 % de l\u0026rsquo;amélioration affichée par le tableau de bord vient de la régression vers la moyenne. L\u0026rsquo;écart dépend aussi du seuil :\nEffet placebo, en pourcentage de l\u0026rsquo;effet réel, selon la part du parc déclenchée. Plus le seuil est strict (à droite), plus l\u0026rsquo;effet placebo est grand. Plus le seuil est strict, plus l\u0026rsquo;effet placebo est grand : 85 % de l\u0026rsquo;effet réel quand 64 % du parc se déclenche, 284 % quand seulement 1,3 % se déclenche. Un seuil strict retient les séries dont l\u0026rsquo;erreur récente doit le plus au hasard, et dont l\u0026rsquo;erreur baisse donc le plus d\u0026rsquo;elle-même.\nLe groupe témoin et son coût # L\u0026rsquo;approche classique est un groupe témoin (holdout) : laisser sans refit une petite part des séries déclenchées, tirée au hasard, et comparer. Avec 5 % de témoins, l\u0026rsquo;A/B estime l\u0026rsquo;effet à ±0,035 près. Le plan évaluait le coût de ce témoin à 0,0057 d\u0026rsquo;erreur supplémentaire « sur le parc ». C\u0026rsquo;est en fait le coût par série déclenchée. Comme seules 20 % des séries se déclenchent, le coût par série du parc est de 0,0012, cinq fois moins.\nLa régression sur discontinuité au seuil # Le plan concluait que réentraîner 100 % des séries déclenchées rend l\u0026rsquo;effet « incalculable par construction ». C\u0026rsquo;est vrai pour l\u0026rsquo;effet moyen. Mais une règle à seuil déterministe correspond au dispositif d\u0026rsquo;une régression sur discontinuité (RDD) : les séries juste sous le seuil et juste au-dessus sont comparables, et seul le refit les distingue. Une régression linéaire locale de chaque côté mesure le saut au seuil.\nErreur après refit en fonction de l\u0026rsquo;erreur qui a déclenché (ou non) le refit. Le saut au seuil (−0,063, erreur-type 0,003) est l\u0026rsquo;effet du refit à cet endroit. Sans aucun témoin, la RDD retrouve −0,059 à −0,064 pour un effet réel de −0,0625 au seuil, et le placebo correspondant est proche de zéro. Elle ne demande pas de groupe témoin, seulement d\u0026rsquo;avoir enregistré la variable qui déclenche le refit.\nElle a une limite : elle mesure l\u0026rsquo;effet au seuil, pas l\u0026rsquo;effet moyen sur les séries déclenchées (−0,1053), parce que les séries très au-dessus du seuil ont un biais plus fort et bénéficient davantage du refit. Les deux dispositifs répondent à deux questions différentes :\n« faut-il abaisser un peu le seuil ? » relève de la RDD ; « que rapporte la politique de réentraînement en moyenne ? » demande le groupe témoin. Dans le contrat, j\u0026rsquo;ai donc demandé l\u0026rsquo;un ou l\u0026rsquo;autre : un groupe témoin permanent, ou l\u0026rsquo;enregistrement de la variable de déclenchement.\n10. Ce que j\u0026rsquo;en retiens # Ces constats valent pour les dispositifs étudiés ici : un banc simulé, un modèle analytique simple, une année de consommation électrique. Je les présente comme tels.\nLes trois attentes ne mènent pas à la même fréquence. Sur le banc, le réentraînement continu donne la meilleure exactitude, mais publie des révisions sur 12,6 % des origines ; le mensuel publie rarement, mais par sauts de 293 % de l\u0026rsquo;échelle. Sur le banc, la détection compte moins que ce qui la suit. Le même détecteur parfait passe d\u0026rsquo;un MASE de 1,37 à 0,88 selon la fenêtre de réajustement et le calendrier qui l\u0026rsquo;accompagne. Avec un CUSUM réel, redémarrer la fenêtre dégrade au contraire le résultat : l\u0026rsquo;intérêt de cette approche dépend de la fiabilité de la détection. La latence de clôture a modifié trois mesures : l\u0026rsquo;erreur du backtest (environ 6 %), la comparaison à la baseline sur données réelles, et le ciblage du CUSUM (de 40,9 % à 20,0 %). La taille et la fréquence des révisions évoluent en sens opposés, et le churn moyen ne distingue pas un modèle stable d\u0026rsquo;un modèle figé. Dans le modèle du niveau local, la fenêtre et la cadence agissent sur deux termes distincts de l\u0026rsquo;erreur. Je n\u0026rsquo;ai vérifié cette séparation que dans ce modèle ; avec des ruptures (partie 4), les deux choix interagissent. L\u0026rsquo;écart de fraîcheur entre quantiles venait en grande partie de la calibration et de la baseline. Après correction : un facteur 2 en simulation, autour de 1 sur les données réelles. Sur les données d\u0026rsquo;électricité, le budget de fraîcheur varie fortement selon le trimestre, de 720 heures à zéro. La comparaison avant/après surestime l\u0026rsquo;effet d\u0026rsquo;un réentraînement déclenché par seuil, et d\u0026rsquo;autant plus que le seuil est strict. Un groupe témoin ou une RDD au seuil permettent d\u0026rsquo;estimer l\u0026rsquo;effet, chacun pour une question différente. Questions encore ouvertes. Je n\u0026rsquo;ai pas testé de détecteur réel plus précis que le CUSUM, ni de fenêtre redémarrée avec une longueur minimale. La transposition des latences publiées de Timestral à un parc horaire n\u0026rsquo;est pas mesurée. Et je n\u0026rsquo;ai pas cherché pourquoi le deuxième trimestre 2014 fait exception.\nUne question pour vérifier sa compréhension. Un service réentraîne un modèle quand son erreur dépasse un seuil. Le tableau de bord affiche −30 % d\u0026rsquo;erreur après chaque refit. On relève le seuil pour réduire le calcul, et le gain affiché passe à −45 %. Comment l\u0026rsquo;interpréter ? Un seuil plus strict retient des séries dont l\u0026rsquo;erreur récente doit davantage au hasard, et la régression vers la moyenne augmente (partie 9). Le gain affiché ne permet donc pas de conclure. Pour estimer l\u0026rsquo;effet réel, il faut un groupe témoin, ou une RDD au seuil si la variable de déclenchement est enregistrée, et une erreur « après » mesurée sur des données closes. La suite. La partie 9 portait sur un effet de sélection lié à un déclenchement par seuil. Le même mécanisme se retrouve dans d\u0026rsquo;autres interventions, par exemple une promotion lancée après un creux de demande. La prochaine veille portera sur l\u0026rsquo;inférence causale appliquée aux séries temporelles : contrôle synthétique, effets d\u0026rsquo;une intervention datée, placebos dans l\u0026rsquo;espace et dans le temps.\nReproduire et aller plus loin # Le code, les sept notebooks et les 55 tests sont dans le dépôt gwils28/W44_retraining_frequency (Python 3.13, versions verrouillées par uv). Chaque tableau de cet article sort d\u0026rsquo;une commande uv run w44 …, avec des graines fixées. Le jeu de données réel est vérifié par son empreinte sha256 avant usage, et le protocole qui le concerne a été commité avant les résultats.\nSources\nZanotti, On the retraining frequency of global models in retail demand forecasting, Machine Learning with Applications (DOI 10.1016/j.mlwa.2025.100769). Zanotti, Analyzing the retraining frequency of global forecasting models: towards more stable forecasting systems, arXiv, 2026. Katz, Cost-sensitive retraining via posterior learning debt, arXiv, 2026. Kwak, Jung, Park, Pseudo-Label-Triggered Retraining from Forecast Errors for Online Time Series Forecasting, arXiv, 2026. Mistral AI, Timestral, annonce du 15 octobre 2026, et la licence des poids sur le Hub (mistralai/Timestral-*). Open Inference Protocol V2, spécification. Données : Godahewa et al., Monash Electricity Hourly, Zenodo, CC-BY-4.0. ","date":"17 octobre 2026","externalUrl":null,"permalink":"/posts/frequence-reentrainement/","section":"Articles \u0026 veille","summary":"Exactitude, fraîcheur et stabilité ne demandent pas la même fréquence de réentraînement. Ce que j’ai observé sur un banc de dix politiques, un modèle analytique, les données d’électricité Monash et une simulation causale.","title":"La fréquence de réentraînement en forecasting : exactitude, fraîcheur et stabilité","type":"posts"},{"content":"","date":"17 octobre 2026","externalUrl":null,"permalink":"/tags/r%C3%A9entra%C3%AEnement/","section":"Tags","summary":"","title":"Réentraînement","type":"tags"},{"content":"","date":"17 octobre 2026","externalUrl":null,"permalink":"/en/tags/retraining/","section":"Tags","summary":"","title":"Retraining","type":"tags"},{"content":"","date":"17 octobre 2026","externalUrl":null,"permalink":"/tags/serving/","section":"Tags","summary":"","title":"Serving","type":"tags"},{"content":"","date":"17 octobre 2026","externalUrl":null,"permalink":"/tags/stabilit%C3%A9/","section":"Tags","summary":"","title":"Stabilité","type":"tags"},{"content":"","date":"17 octobre 2026","externalUrl":null,"permalink":"/en/tags/stability/","section":"Tags","summary":"","title":"Stability","type":"tags"},{"content":"","date":"8 octobre 2026","externalUrl":null,"permalink":"/tags/%C3%A9co2mix/","section":"Tags","summary":"","title":"ÉCO2mix","type":"tags"},{"content":"","date":"8 octobre 2026","externalUrl":null,"permalink":"/en/tags/hierarchical-reconciliation/","section":"Tags","summary":"","title":"Hierarchical Reconciliation","type":"tags"},{"content":" Savoir où part l\u0026rsquo;argent du foyer, mois après mois, ce que vaut son patrimoine et quand ses projets seront financés, sans confier ses comptes à un service en ligne. Tout tourne sur une machine de la maison. StatutEn développement · phases 1 à 4 livrées, phase 5 en cours PériodeDepuis octobre 2026 RôleSolo : spécifications, architecture, développement, exploitation Stack Python 3.13FastAPISQLAlchemy 2PostgreSQL 17NumPyReact 19TypeScriptTailwindDocker ComposeCaddyGitHub Actions Code source 137\npull requests fusionnées\nchacune bloquée tant que la CI n\u0026rsquo;est pas verte 736\ntests automatisés\ndont 49 tests de propriété sur les invariants financiers 8\ndécisions d\u0026rsquo;architecture\nécrites, avec les alternatives rejetées Note de transparence. Ces travaux ont été menés à titre personnel, dans un but d\u0026rsquo;apprentissage, avec l\u0026rsquo;assistance d\u0026rsquo;une IA :\npour le code, la relecture technique et méthodologique ; pour comprendre les outils et les méthodes mathématiques ; pour l\u0026rsquo;aide à la rédaction, la traduction et la correction du texte ; pour générer l\u0026rsquo;image de couverture de cette fiche. Le choix du sujet, le protocole, la vérification des résultats et les conclusions sont les miens, et j\u0026rsquo;en assume les erreurs. Comment j\u0026rsquo;utilise l\u0026rsquo;IA\nProjet en cours. Cette fiche décrit l\u0026rsquo;état du dépôt au 24 octobre 2026. Les chiffres ci-dessus sont comptés dans le code, et seront mis à jour à chaque phase livrée. Il y a moins de tests qu\u0026rsquo;au 15 octobre : la refonte des dépenses a supprimé l\u0026rsquo;import bancaire et tout ce qui en dépendait, tests compris. Toutes les captures montrent des données fictives. Elles viennent d\u0026rsquo;une instance de démonstration, alimentée par un foyer inventé (Camille et Sam, dix mois de dépenses, deux ans de patrimoine, neuf objectifs). Aucun montant, libellé ni solde ne provient de comptes réels. Le problème # Les applications de budget grand public demandent presque toutes la même chose : un accès à vos comptes bancaires, et l\u0026rsquo;hébergement de votre historique financier sur leurs serveurs. Les tableurs, eux, ne demandent rien, mais s\u0026rsquo;abandonnent au bout de trois mois : la saisie y est lente et les totaux se cassent au premier copier-coller.\nJe voulais un outil entre les deux :\nprivé par construction : les données restent sur une machine du foyer, sans télémétrie ni service tiers ; rapide à tenir : une séance de saisie par semaine, au clavier, sans rien à trier ensuite ; juste au centime : chaque total doit pouvoir se recalculer à partir des lignes saisies ; tourné vers l\u0026rsquo;avenir : dire si les projets du foyer seront financés à temps, et où le patrimoine peut aller ; prêt pour la data science : simulation, prévision et assistant en langage naturel doivent pouvoir s\u0026rsquo;ajouter sans refaire le socle. C\u0026rsquo;est aussi mon premier produit : un projet qui doit tenir dans la durée, pas une expérience jetable. Il me sert donc de terrain d\u0026rsquo;application pour ma méthode de travail avec Claude Code.\nOù en est le projet # Phase 0 · Fondations Livrée Socle technique Spécifications, architecture et décisions écrites avant la première ligne de code. Dépôt, outillage, intégration continue, pile Docker Compose, configuration de Claude Code et charte graphique reprise de ce blog. Phases 1 et 2 · Budget Livrées, puis refaites Le cœur du produit Authentification à double facteur, foyer et invitations, comptes, import et rapprochement bancaires, règles de catégorisation, virements, plans budgétaires par période avec leur bilan, opérations récurrentes, rapports et journal d'activité. Toute la partie comptes et import a ensuite été remplacée par la refonte v1.0. Phase 3 · Patrimoine Livrée Actifs et passifs, mois par mois Une matrice de patrimoine mensuelle : catégories et lignes saisies à la main, dettes, valeur nette, et quatre graphiques (historique, répartition en sunburst et en mosaïque, contributions à la variation). Phase 4 · Objectifs financiers Livrée Une somme à réunir pour une date Les objectifs rangés en pyramide (l'épargne de précaution en fondation, puis le long, le moyen et le court terme, sous une coiffe dorée pour les envies), financés par des enveloppes prises sur les lignes de patrimoine, avec leur suivi mensuel et un plan d'effort pour le mois. Refonte v1.0 · Des dépenses plus simples Livrée Ce que je fais vraiment, pas ce que j\u0026#39;avais imaginé Plus de comptes, d'import ni de virements : chaque dépense est une ligne saisie à la main, dans une grille pensée pour le clavier. Un seul niveau de catégories, des dépenses fixes à confirmer, des objectifs mensuels avec leur matrice et une vue annuelle. Les données des dépenses repartent de zéro. Phase 5 · Analyse et data science En cours Là où mon métier entre en jeu Livrés : l'analyse des objectifs (six vues et des points à regarder calculés par des règles) et le moteur de projection du patrimoine par Monte-Carlo. À venir : la page de projection, la chance d'atteindre chaque objectif, la retraite, puis la prévision de trésorerie quand les dépenses auront assez d'historique. Phase 6 · IA générative locale À venir Sans que rien ne sorte de la machine Un assistant qui répond en langage naturel sur ses propres données, à partir d'un modèle de langage local (Ollama), limité à une vue analytique en lecture seule. Pourquoi l\u0026rsquo;import bancaire a disparu # L\u0026rsquo;import était le point de départ du projet : déposer l\u0026rsquo;export CSV de la banque, laisser les règles ranger les dépenses. À l\u0026rsquo;usage, je saisissais déjà mes dépenses à la main chaque semaine, et le relevé arrivait ensuite comme une corvée : des lignes à trier de nouveau, des doublons de ce qui était déjà saisi, un rapprochement à vérifier. La v1.0 a donc retiré les comptes, l\u0026rsquo;import, les virements, les règles, les récurrences, le mois budgétaire et les plans, avec leurs données, pour garder ce qui sert : une saisie rapide et un budget lisible. L\u0026rsquo;écart est consigné dans le README du dépôt, comme chaque écart au plan initial.\nCe qui fonctionne aujourd\u0026rsquo;hui # Un foyer, trois points de vue # Le propriétaire crée le foyer et invite les autres membres par un lien à usage unique ; il n\u0026rsquo;y a pas d\u0026rsquo;inscription publique. Connexion par mot de passe (argon2id) et code TOTP obligatoire.\nLes dépenses appartiennent au foyer : chaque membre voit et modifie chaque ligne. Le patrimoine et les objectifs, eux, ont une visibilité, ligne par ligne :\nprivé : son titulaire seul le voit ; partagé : tout le foyer le voit, mais il reste à son titulaire ; commun : le bien du foyer, qui n\u0026rsquo;est à personne. Le patrimoine et les objectifs se lisent ensuite à l\u0026rsquo;échelle du foyer, du commun ou de mes lignes. L\u0026rsquo;application est rangée en trois espaces, Dépenses, Patrimoine et Objectifs ; la vue d\u0026rsquo;ensemble, en tête, résume les trois.\nOctobre à l\u0026rsquo;échelle du foyer : le patrimoine net et sa variation, la progression de chaque étage de la pyramide, puis les chiffres du mois. Données fictives. Saisir vite, au clavier # La page principale des dépenses est une grille : un mois, ses lignes, chaque case modifiable sur place. Une ligne vide attend toujours en bas ; Tab passe à la case suivante, Entrée enregistre et ouvre une nouvelle ligne à la même date. Tout se fait sans la souris.\nAu-dessus, une ligne rapide accepte une dépense tapée dans n\u0026rsquo;importe quel ordre : boulangerie 12,50 hier, 45 sncf 13/10 ou carrefour 87.40 #foyer.\nla date se reconnaît (« hier », un jour de la semaine, jj/mm) ; le montant est le dernier nombre, les autres restent dans le libellé ; #mot force la catégorie qui commence par ce mot ; le reste est le libellé, et sa catégorie vient de la mémoire des libellés : choisir un libellé déjà utilisé reprend la catégorie de sa dernière ligne. La ligne rapide a compris « cinéma 24,50 vendredi #loisirs » avant même la validation ; en dessous, les dépenses fixes à confirmer, grisées, puis les lignes du mois. Données fictives. Un en-tête rappelle la dernière ligne saisie et quand, et un point la marque dans la grille, pour reprendre là où l\u0026rsquo;on s\u0026rsquo;est arrêté.\nLe point vert marque la dernière ligne saisie ; la ligne vide attend la suivante, à la même date. Données fictives. Une ligne peut aussi être ventilée sur plusieurs catégories : la dernière part prend le reste, si bien que la somme tombe toujours juste.\nUn passage à l\u0026rsquo;hypermarché : 88,51 € de courses, le reste (36 €) pour le foyer. Données fictives. Des catégories à trois natures # Un seul niveau de catégories, chacune d\u0026rsquo;une nature :\ndépense : le loyer, les courses, les loisirs ; épargne : l\u0026rsquo;argent mis de côté, qui n\u0026rsquo;est jamais une dépense mais réduit le reste du mois ; revenu : salaires, aides, remboursements. Le revenu d\u0026rsquo;un mois est la somme de ses lignes de revenu, et le reste vaut revenus moins dépenses moins épargne. Les catégories ponctuelles (loyer, abonnements), payées en une fois, ne sont jamais jugées sur le rythme du mois.\nLes dépenses fixes # Loyer, assistante maternelle, abonnements, assurances : une dépense fixe décrit une ligne qui revient chaque mois, trimestre ou année. Chaque échéance due apparaît grisée en haut de la grille du mois. Entrée la confirme et crée la ligne, modifiable avant ; on peut aussi la sauter pour le mois. Rien n\u0026rsquo;est jamais créé tout seul.\nLoyer, assistante maternelle, abonnements : chaque dépense fixe avec sa fréquence, son jour et sa catégorie. Données fictives. La vue d\u0026rsquo;ensemble liste ce qui reste à confirmer et le reste projeté en fin de mois.\nTrois échéances d\u0026rsquo;octobre encore à confirmer, et le reste projeté une fois qu\u0026rsquo;elles seront payées. Données fictives. Des objectifs mensuels # Chaque catégorie de dépense peut recevoir un objectif mensuel, fixé à partir d\u0026rsquo;un mois et gardé jusqu\u0026rsquo;à ce qu\u0026rsquo;on le change ; les mois passés gardent les leurs. Une catégorie d\u0026rsquo;épargne reçoit un minimum à atteindre.\nle mois en cours, sur la vue d\u0026rsquo;ensemble : une barre par catégorie, triée par part consommée, avec un trait qui marque la part du mois écoulée, si bien qu\u0026rsquo;une dépense en avance sur le calendrier se voit avant de dépasser ; la matrice : une ligne par catégorie, une colonne par mois, chaque case colorée selon l\u0026rsquo;écart à l\u0026rsquo;objectif (vert en dessous, gris à ±5 %, terre brûlée au-dessus) ; l\u0026rsquo;année : les objectifs cumulés à date et sur l\u0026rsquo;année entière, l\u0026rsquo;écart, et combien de mois ont été au-dessus, dans ou sous l\u0026rsquo;objectif. Au 24 octobre : le transport et le shopping ont dépassé leur cible ; le trait vertical marque la part du mois écoulée, et le loyer, payé en une fois, n\u0026rsquo;est pas jugé sur le rythme. Données fictives. Six mois de budget : les revenus reçus en haut, puis chaque catégorie face à sa cible. Vert : sous la cible ; gris : à ±5 % ; orange : au-dessus. Données fictives. L\u0026rsquo;année 2026 à date : cible cumulée, dépensé, écart, et combien de mois au-dessus, dans et sous la cible. Données fictives. Des rapports sur n\u0026rsquo;importe quelle période # Un mois, un trimestre, une année ou toute période jusqu\u0026rsquo;à dix ans, comparée à la précédente de même longueur. Une période en cours s\u0026rsquo;arrête au mois courant : « cette année » se compare aux mêmes mois de l\u0026rsquo;an dernier, pas à une année entière.\nSeptembre face à août : les totaux, puis chaque catégorie avec sa moyenne, sa part et son évolution. Données fictives. Le rapport donne aussi un graphique mensuel empilé par catégorie, les dépenses cumulées jour après jour, et les flux d\u0026rsquo;argent de la période : les revenus à gauche ; dépenses, épargne et reste non alloué à droite, les deux côtés toujours au même total.\n2026 mois par mois : les six plus grosses catégories en couleur, les autres en gris, face à la courbe des revenus. Données fictives. Les flux de 2026 : salaires et aides à gauche ; chaque catégorie, l\u0026rsquo;épargne et le reste non alloué à droite. Données fictives. Le patrimoine, mois par mois # Le patrimoine se suit comme dans un tableur : une ligne par placement, rangée dans une catégorie, une colonne par mois, et une valeur saisie à la main une fois par mois.\nLe suivi mensuel : valeur nette, actif brut et dettes, puis les lignes par catégorie et la part de chacune. Données fictives. un mois sans saisie reprend la valeur précédente, affichée comme reportée ; « Mettre à jour ce mois-ci » liste toutes les lignes avec leur dernière valeur, pour tout saisir d\u0026rsquo;un coup ; chaque ligne a un titulaire et une visibilité ; un livret réglementé (livret A, LDDS, LEP, PEL, livret jeune) connaît son plafond. Une page Analyse donne quatre graphiques, chaque catégorie dans sa couleur. Comme dix teintes ne peuvent pas toutes rester distinctes pour tous les daltonismes, chaque graphique garde aussi ses libellés et son tableau de données.\nLes catégories empilées mois par mois, les dettes sous zéro, et la valeur nette en ligne. Données fictives. La répartition d\u0026rsquo;octobre : catégories puis lignes dans le sunburst, une tuile par catégorie dans la mosaïque. Données fictives. De novembre 2025 à octobre 2026, la contribution de chaque catégorie à la valeur nette ; le prêt remboursé la fait monter. Données fictives. Les objectifs, en pyramide # Un objectif est une somme à réunir pour une date : une épargne de précaution, un voyage, un apport immobilier. Ils sont rangés en pyramide, dessinée en pierre :\nen fondation, une colonne romaine : l\u0026rsquo;épargne de précaution, à remplir en premier. L\u0026rsquo;application propose son montant, six mois de dépenses fixes par défaut ; trois étages, donnés par l\u0026rsquo;échéance et jamais choisis : le long terme (au-delà de cinq ans) à la base, le moyen terme au milieu, le court terme (douze mois au plus) au sommet. Un objectif monte d\u0026rsquo;étage tout seul à mesure que son échéance approche ; une coiffe dorée pour les envies (des gants de boxe, une platine, une belle bouteille de rhum), sans échéance, verrouillée tant que l\u0026rsquo;essentiel n\u0026rsquo;est pas en ordre : précaution remplie et au moins trois quarts des objectifs en avance. La pyramide du foyer : la colonne de la précaution à 96 %, les trois étages, la coiffe verrouillée ; à droite, le plan d\u0026rsquo;effort du mois, la précaution servie d\u0026rsquo;abord. Données fictives. Un objectif ne demande aucune saisie de plus : il est financé par des enveloppes, la part d\u0026rsquo;une ligne de patrimoine mise de côté pour lui. Quand la ligne monte, chaque objectif reçoit sa part de la hausse ; quand elle baisse, la baisse sort d\u0026rsquo;abord de la partie libre, et les objectifs ne perdent qu\u0026rsquo;une fois la ligne descendue sous ce qu\u0026rsquo;ils détiennent, les envies en premier et la précaution en dernier.\nflowchart LR L[\"Ligne de patrimoineAssurance-vie fonds euros\"] --\u003e|50 % des hausses| A[\"EnveloppeVacances au Portugal\"] L --\u003e|30 % des hausses| B[\"EnveloppeNouvelle voiture\"] L --\u003e|20 % des hausses| C[\"EnveloppeApport immobilier\"] L -.-\u003e|baisses d'abord| F[\"Partie libre\"] Chaque objectif a son statut (atteint, en avance, dans les temps, en retard), comparé à une trajectoire droite du départ à la cible, et ses estimations : l\u0026rsquo;effort mensuel nécessaire, l\u0026rsquo;effort réel des six derniers mois, la date estimée à ce rythme. Le plan d\u0026rsquo;effort du mois répartit la capacité d\u0026rsquo;épargne du foyer : la précaution d\u0026rsquo;abord, dans la limite d\u0026rsquo;une part réglable, puis les autres objectifs selon leur effort nécessaire, puis les envies déverrouillées. C\u0026rsquo;est une recommandation : rien n\u0026rsquo;est déplacé.\nLe court terme : deux objectifs en avance, chacun avec sa jauge, son repère de rythme et ses estimations. Données fictives. La coiffe : deux envies verrouillées tant que l\u0026rsquo;essentiel n\u0026rsquo;est pas en ordre. Données fictives. Chaque objectif a aussi sa page : ses estimations, sa trajectoire, ses enveloppes ligne par ligne et son suivi mois par mois.\nVacances au Portugal : en avance de trois mois ; la trajectoire montre les valeurs, la ligne attendue et la projection au rythme actuel. Données fictives. Son enveloppe sur l\u0026rsquo;assurance-vie, puis chaque mois : atteint, gagné, attendu et écart. Données fictives. L\u0026rsquo;analyse des objectifs # Un onglet Analyse regarde les objectifs ensemble, à un mois de référence : le dernier mois où toutes les lignes qui les financent ont une valeur saisie, pour qu\u0026rsquo;un mois pas encore saisi ne passe jamais pour un mois sans gain.\nl\u0026rsquo;effort nécessaire face à la capacité sur les deux ans à venir, les mois sur-engagés grisés ; une frise : chaque objectif de son départ à son échéance, avec sa date estimée au rythme réel et au rythme du plan ; horizon et risque : chaque objectif placé selon les mois qui restent et la volatilité de ses enveloppes, avec des zones à surveiller (court et volatil, long et rongé par l\u0026rsquo;inflation) et la perte dans un cas sur dix ; où est l\u0026rsquo;argent : un diagramme de flux des catégories aux lignes, puis aux enveloppes et à la partie libre ; le statut mois par mois de chaque objectif. Les efforts requis empilés par objectif face à la capacité : novembre 2026 dépasse, le temps que la précaution de Sam se remplisse. Données fictives. Chaque objectif de son départ à son échéance : la date estimée au rythme réel (●) et au rythme du plan (◇). Données fictives. Le vélo électrique, à six mois de son échéance, est financé par des lignes volatiles : il tombe dans la zone à surveiller. Données fictives. Des catégories aux lignes, puis aux enveloppes des objectifs et à la partie libre de chaque ligne. Données fictives. Le statut de chaque objectif, mois après mois. Données fictives. Au-dessus, des points à regarder, calculés par une quinzaine de règles fixes : un objectif en retard depuis trois mois, une précaution trop mince ou placée sur des lignes volatiles, un livret proche de son plafond, de l\u0026rsquo;argent qui dort sur un compte courant, des objectifs qui demandent plus que la capacité (avec le report d\u0026rsquo;échéance qui suffirait). Chaque point donne les faits, les chiffres, pourquoi c\u0026rsquo;est important et des options ; jamais une consigne d\u0026rsquo;achat ou de vente, ni un produit nommé. Trois au plus s\u0026rsquo;affichent, et chaque membre peut en reporter ou en écarter un.\nTrois points à regarder : une précaution qui couvre moins de trois mois de charges fixes, deux objectifs proches financés par des lignes volatiles. Données fictives. Des sauvegardes vérifiées, des actions tracées # Une sauvegarde complète chaque nuit, contrôlée avant d\u0026rsquo;être conservée, avec une rotation (7 jours, 4 semaines, 12 mois) et une procédure de restauration écrite.\nUn journal d\u0026rsquo;activité garde les actions sensibles, écrites dans la même transaction que l\u0026rsquo;action elle-même : connexions, invitations, suppressions. Le propriétaire voit tout le foyer, un membre ses propres entrées.\nConnexions (dont un code erroné), invitation envoyée puis annulée, réglages, suppression d\u0026rsquo;une ligne. Données fictives. Comment c\u0026rsquo;est construit # Une architecture simple, pensée pour la suite # Tout démarre d\u0026rsquo;un seul docker compose up : un proxy Caddy en TLS sur le réseau local, une API FastAPI, une base PostgreSQL et un service de sauvegarde. L\u0026rsquo;interface React est servie depuis la même origine que l\u0026rsquo;API, ce qui permet des sessions par cookie sans configuration CORS.\nflowchart LR N[\"Navigateurréseau local\"] --\u003e|HTTPS| C[\"CaddyTLS + interface React\"] C --\u003e|/api| A[\"API FastAPIkwak_api\"] A --\u003e K[\"kwak_coretoutes les règles métier\"] A -.-\u003e|page à venir| M[\"kwak_analyticsMonte-Carlo, NumPy\"] M --\u003e K A --\u003e P[(\"PostgreSQL\")] S[\"Sauvegarde nocturnepg_dump vérifié\"] --\u003e P Côté Python, le code est découpé en paquets avec une règle stricte : kwak_core contient toutes les règles métier (montants, ligne rapide, ventilation, objectifs mensuels, dépenses fixes, rapports, patrimoine, enveloppes et plan d\u0026rsquo;effort, règles de l\u0026rsquo;analyse) et ne dépend de rien. Il se teste sans base de données. L\u0026rsquo;API et la couche analytique kwak_analytics en dépendent, jamais l\u0026rsquo;inverse : un même chiffre n\u0026rsquo;a qu\u0026rsquo;une seule définition.\nLe contrat entre l\u0026rsquo;API et l\u0026rsquo;interface est le schéma OpenAPI : le client TypeScript en est généré, jamais écrit à la main. La CI échoue si quelqu\u0026rsquo;un oublie de le régénérer.\nL\u0026rsquo;argent ne supporte pas l\u0026rsquo;à-peu-près # Les montants sont des décimaux exacts (Decimal en Python, NUMERIC en base), jamais des flottants. Et les règles qui doivent toujours être vraies sont écrites dans les spécifications puis vérifiées par des tests de propriété (Hypothesis) : au lieu de quelques exemples choisis à la main, le test génère des centaines de cas et cherche celui qui casse la règle.\nInvariant Ce que le test vérifie Ligne rapide Chaque mot tapé finit dans exactement un champ : date, montant, catégorie ou libellé Ventilation Les parts d\u0026rsquo;une ligne ventilée font exactement son montant, chacune positive Épargne L\u0026rsquo;épargne n\u0026rsquo;est jamais une dépense, et le reste du mois est la somme de ses lignes Objectifs mensuels Un seul objectif en vigueur par catégorie et par mois, et l\u0026rsquo;objectif annuel est la somme des mois Dépenses fixes Une ligne honore au plus une échéance, et le reste projeté déduit exactement celles qui restent Cohérence Un rapport, la matrice, l\u0026rsquo;année et le mois en cours donnent toujours les mêmes chiffres Flux Les deux côtés du diagramme de flux ont le même total Patrimoine La valeur nette d\u0026rsquo;un mois vaut ses actifs moins ses dettes, et le foyer est la somme exacte de ses périmètres Enveloppes Les enveloppes d\u0026rsquo;une ligne ne sont jamais négatives et ne dépassent jamais sa valeur, ni un objectif sa cible Plan d\u0026rsquo;effort Le plan ne distribue jamais plus que la capacité, et la précaution est servie la première Coiffe Une envie verrouillée ne reçoit rien, ni des hausses ni du plan Analyse Les poids d\u0026rsquo;un objectif sur ses lignes font 1, et le report proposé suffit, un mois de moins non Projection Sans volatilité, tous les chemins coïncident avec le calcul exact ; l\u0026rsquo;argent est conservé à chaque mois Les tests d\u0026rsquo;API tournent contre un vrai PostgreSQL lancé dans un conteneur, jamais contre une base simulée.\nUne projection par Monte-Carlo # La première brique de la phase 5 est le moteur qui dira où le patrimoine peut être dans 1 à 40 ans. Le point de départ est le mois courant de la matrice ; chaque catégorie a un rendement attendu et une volatilité, réglables, et l\u0026rsquo;épargne mensuelle du foyer s\u0026rsquo;ajoute chaque mois, répartie entre les catégories.\n5 000 chemins, mois par mois : chaque catégorie croît d\u0026rsquo;un rendement log-normal tiré de son propre flux aléatoire, calibré pour que douze mois composent exactement la moyenne et la volatilité annuelles choisies ; une graine fixe : les mêmes hypothèses donnent toujours le même résultat, et deux scénarios partagent les mêmes tirages, si bien que leur écart ne vient que des hypothèses ; les dettes se remboursent selon leur mensualité et leur taux, et ce qu\u0026rsquo;elles libèrent rejoint l\u0026rsquo;épargne ; une épargne négative puise dans les comptes courants puis les livrets, jamais dans le logement ni les placements bloqués ; le manque devient une dette empruntée à 8 %. C\u0026rsquo;est le seul endroit où l\u0026rsquo;argent passe en flottant, pour la vitesse : les entrées sont des Decimal, les résultats y reviennent au centime. Les invariants de la projection (conservation de l\u0026rsquo;argent, percentiles ordonnés, chemins identiques sans volatilité, moyenne simulée égale au rendement choisi) sont testés comme le reste, et une version du moteur est figée par un test de référence.\nDes décisions écrites # Chaque choix structurant fait l\u0026rsquo;objet d\u0026rsquo;une note de décision (ADR), datée, avec ses alternatives rejetées. Une décision qui change donne une nouvelle note ; l\u0026rsquo;ancienne n\u0026rsquo;est jamais réécrite.\nDécision Plutôt que Auto-hébergement en Docker Compose Un hébergement cloud : coût inutile et données financières exposées FastAPI, SQLAlchemy 2, Alembic Django : plus lourd, et l\u0026rsquo;interface d\u0026rsquo;administration ne sert pas PostgreSQL, puis DuckDB pour l\u0026rsquo;analyse SQLite seul : écritures concurrentes, pas d\u0026rsquo;extension vectorielle Euro uniquement Une comptabilité multidevise complète, trop coûteuse pour un seul besoin réel Sessions serveur et TOTP obligatoire Des jetons JWT, difficiles à révoquer Modèle de langage local (Ollama) Une API dans le cloud : les données ne doivent jamais quitter la machine Les écarts au plan initial sont, eux aussi, journalisés dans le README, au jour près.\nLa suite # Le moteur de projection est prêt : reste à lui donner sa page, un éventail des issues possibles (médiane, bandes 25–75 % et 10–90 %), la chance d\u0026rsquo;atteindre un montant à une date, la médiane par catégorie et deux scénarios à comparer, avec ses limites écrites en clair. Le même moteur donnera ensuite à chaque objectif sa chance d\u0026rsquo;être atteint à temps, puis une projection de retraite.\nLa prévision de trésorerie attend, elle, que les dépenses repartent avec assez d\u0026rsquo;historique depuis la v1.0. Elle sera traitée comme un vrai problème de séries temporelles : protocole écrit d\u0026rsquo;avance, backtest sans fuite du futur, et un modèle naïf à battre avant tout le reste. Cette fiche sera mise à jour à chaque étape livrée.\nEssayer # git clone https://github.com/gwils28/kwak-finance cd kwak-finance make setup # dépendances et hooks Git make check # tout ce que lance la CI make up # la pile complète sur https://localhost:8443 Les étapes de création du compte propriétaire et de la clé de chiffrement sont dans le README, et l\u0026rsquo;application elle-même propose, en bas de chaque page, un guide pour la lancer, l\u0026rsquo;arrêter et la mettre à jour.\n","date":"8 octobre 2026","externalUrl":null,"permalink":"/projects/kwak-finance/","section":"Projets","summary":"Application web auto-hébergée de budget et de patrimoine familial. Saisie au clavier, dépenses fixes, objectifs budgétaires, rapports, matrice de patrimoine mensuelle, objectifs financiers en pyramide avec leur analyse ; une projection du patrimoine par Monte-Carlo est en route.","title":"Kwak Finance : le budget et le patrimoine d'un foyer, auto-hébergés","type":"projects"},{"content":"","date":"8 octobre 2026","externalUrl":null,"permalink":"/tags/mint/","section":"Tags","summary":"","title":"MinT","type":"tags"},{"content":"","date":"8 octobre 2026","externalUrl":null,"permalink":"/tags/postgresql/","section":"Tags","summary":"","title":"Postgresql","type":"tags"},{"content":"","date":"8 octobre 2026","externalUrl":null,"permalink":"/tags/r%C3%A9conciliation-hi%C3%A9rarchique/","section":"Tags","summary":"","title":"Réconciliation Hiérarchique","type":"tags"},{"content":" Comprendre la réconciliation hiérarchique assez finement pour savoir quand elle aide, quand elle nuit, et pourquoi. Pas l\u0026rsquo;appeler : la réécrire, puis la mettre à l\u0026rsquo;épreuve sur de vraies données. Stack Python 3.12NumPystatsforecastHierarchicalForecastBayesReconPyuvpytest Code source 1,07e-5\nécart à la bibliothèque\nMinT(shrink) maison face à HierarchicalForecast +2,3 %\nMASE régional\nMinT shrink face au bottom-up, sur 119 origines éCO2mix de 2024 356\norigines backtestées\n2023 et 2024, MSTL et AutoETS, face au naïf et à RTE Note de transparence. Ces travaux ont été menés à titre personnel, dans un but d\u0026rsquo;apprentissage, avec l\u0026rsquo;assistance d\u0026rsquo;une IA :\npour le code, la relecture technique et méthodologique ; pour comprendre les outils et les méthodes mathématiques ; pour l\u0026rsquo;aide à la rédaction, la traduction et la correction du texte ; pour générer l\u0026rsquo;image de couverture de cette fiche. Le choix du sujet, le protocole, la vérification des résultats et les conclusions sont les miens, et j\u0026rsquo;en assume les erreurs. Comment j\u0026rsquo;utilise l\u0026rsquo;IA\nCe dépôt est le laboratoire de l\u0026rsquo;article La réconciliation hiérarchique en forecasting : promesses et limites. L\u0026rsquo;article raconte ce que j\u0026rsquo;en ai tiré ; cette fiche montre comment le travail est organisé et comment le reproduire.\nL\u0026rsquo;objectif # Des prévisions produites série par série ne somment pas : la somme des régions ne donne pas le total national, et quelqu\u0026rsquo;un doit trancher. La réconciliation (MinT en tête) promet de supprimer cet arbitrage. Je voulais savoir trois choses :\nécrire la projection P = S(S'W⁻¹S)⁻¹S'W⁻¹ de ma main, et retrouver la bibliothèque au chiffre près ; identifier où MinT casse : biais, quantiles, mur numérique ; vérifier sur de vraies données si la réconciliation améliore la précision, ou seulement la cohérence. La démarche # Le travail suit six blocs, un dossier chacun. Le code vit dans un seul package (src/w37_reconciliation), les notebooks pédagogiques l\u0026rsquo;appellent, et chaque bloc a son README (résultats) et son TUTORIEL (pour relancer).\n1 Veille Quatre articles de 2026 vérifiés, une synthèse en trois points et une note sur ce qui est surcoté : la réconciliation vendue comme un gain de précision. 2 MinT de zéro Shrinkage de Schäfer–Strimmer, projection oblique, trois assertions. Écart à HierarchicalForecast : 1,07e-5. Il ne vient pas de λ, comme le plan le supposait, mais de l\u0026rsquo;estimateur de covariance (centrage et division par T−1). 3 System design Un job batch matérialisé avec un contrat YAML versionné, un hash de la hiérarchie et un quality gate qui bloque la publication. 4 Fondamentaux MinT propage le biais d\u0026rsquo;une seule prévision à toutes les feuilles. Appliqué à des quantiles, il donne un vecteur cohérent et faux : 0,764 de couverture au lieu de 0,900. Seul un échantillonnage joint est calibré. 5 Mini-projet éCO2mix 12 régions → France, données RTE 2013-2024, backtest à origines glissantes, puis un benchmark face au naïf et à la prévision de RTE, et trois études de confirmation. Le détail est plus bas. 6 BayesReconPy Sur des ventes intermittentes (M5), le MinT gaussien produit des ventes négatives ; le conditionnement bayésien n\u0026rsquo;en produit aucune, et il est le plus précis. Le mini-projet éCO2mix # L\u0026rsquo;objectif métier : livrer des prévisions de consommation électrique dont la somme des 12 régions reproduit exactement le total France.\nLe protocole, fixé avant les résultats dans configs/eco2mix.yaml :\npas horaire ; modèles de base SeasonalNaive(168) et MSTL ; fenêtre d\u0026rsquo;entraînement de 52 semaines, strictement antérieure à chaque origine ; horizon de 24 h ; cinq réconciliations, comparées par MASE niveau par niveau. Le résultat principal # Le résultat se lit en deux temps. Sur les 4 origines du protocole principal, MinT shrink semblait gagner (−2,2 % de MASE régional). Sur 119 origines couvrant toute l\u0026rsquo;année 2024, il fait moins bien que le bottom-up, sur les 13 séries.\nGain agrégé de MinT shrink sur le bottom-up (régions) selon le nombre d\u0026rsquo;origines utilisées, en 2024 et en 2023. La bande grise contient 90 % de 200 ordres aléatoires : avec 4 origines, tout est possible. La cause est démontrée par une expérience contrôlée. MinT estime sa matrice W sur les résidus in-sample. Ceux de MSTL sont des restes de lissage, corrélés à 0,11 entre régions, alors que les vraies erreurs le sont à 0,56. Estimée sur les erreurs des origines passées, W ramène MinT au niveau du bottom-up (−0,1 %, non significatif).\nLe benchmark : où se fait le gain # Un résultat entre réconciliations ne dit pas ce qu\u0026rsquo;elles valent face à rien du tout. J\u0026rsquo;ai donc placé les cinq réconciliations dans un benchmark complet : quatre baselines naïves (la dernière valeur, la moyenne de 4 semaines, la même heure la semaine ou la veille), MSTL seul, et la prévision J−1 officielle de RTE. Celle-ci n\u0026rsquo;est pas un concurrent à information égale : elle utilise la météo prévue. Telle que publiée, elle sous-estime aussi la consommation d\u0026rsquo;environ 1,1 GW en permanence ; une version débiaisée lui retire son erreur moyenne des 52 semaines précédentes, sans fuite.\nMASE moyen sur les 119 origines de 2024, du plus simple au plus élaboré. Les réconciliations forment un plateau ; seule la prévision RTE débiaisée, qui a la météo, passe dessous. L\u0026rsquo;escalier se lit en trois marches :\nLe modèle statistique fait presque tout. MSTL divise l\u0026rsquo;erreur du naïf saisonnier par 2,6 au niveau France (61 % de gain). Les réconciliations forment un plateau. Leur MASE France va de 0,352 à 0,370, et le bottom-up en est le point bas, aux deux niveaux. La réconciliation déplace l\u0026rsquo;erreur de quelques pourcents ; le modèle de base l\u0026rsquo;avait déplacée de 60 %. Seule la météo passe sous le plateau. La prévision RTE débiaisée fait 13,6 % de mieux que le bottom-up en 2024, et 12 % en 2023. Le classement ne dépend pas de la métrique : il est le même en MASE, en MAE (MW), en RMSE et en MAPE. Il dépend en revanche de l\u0026rsquo;heure prévue, et c\u0026rsquo;est le résultat le plus nouveau.\nGain sur le bottom-up à chaque heure du jour prévu (2024). Au niveau France, la prévision directe et MinT gagnent les 6 premières heures, puis perdent tout le reste de la journée. Au niveau France, la prévision MSTL directe du total et MinT shrink battent le bottom-up de 7,5 % et 8,6 % sur les heures 1 à 6 : juste après l\u0026rsquo;origine, la dernière valeur connue du total est très informative, et MinT lui donne du poids. Ensuite la prévision du total dérive, alors que la somme de 12 prévisions régionales compense une partie de leurs erreurs. Une journée compte 6 heures favorables et 18 défavorables : le bottom-up gagne en moyenne. Pour une prévision infra-journalière, la conclusion pourrait s\u0026rsquo;inverser.\nTrois études de confirmation # Ajoutées après les résultats, ces études changent une seule chose à la fois et gardent tout le reste du protocole :\nUne autre année. Sur 118 origines de 2023, l\u0026rsquo;escalier est le même et MinT shrink perd encore face au bottom-up (−0,8 % en région, −1,5 % au niveau France), mais les écarts sont trois fois plus petits et aucun n\u0026rsquo;est significatif après Holm. Un autre modèle de base. L\u0026rsquo;explication par W fait une prédiction testable : avec un modèle dont les résidus in-sample sont de vraies erreurs à un pas, le handicap de MinT doit disparaître. AutoETS est un filtre, qui ne regarde que le passé. Il est bien moins précis que MSTL (MASE France 0,64 contre 0,37), mais le handicap de MinT disparaît : −0,0 % en région. La cause, rejouée. Estimer W sur les erreurs des origines passées efface aussi le handicap en 2023 (−1,0 % → −0,3 % en région). De 3 à 99 origines passées, MinT reste à ±3 % du bottom-up : il n\u0026rsquo;y a pas de fenêtre magique, il faut seulement de vraies erreurs. La mesure directe du mécanisme tient en un tableau :\nModèle de base Corrélation vue par W (in-sample) Corrélation réelle (hors échantillon) MinT shrink vs BU (régions) MSTL 0,11 à 0,13 0,56 −2,8 % AutoETS 0,49 à 0,57 0,66 −0,0 % Quand W voit des corrélations proches de la réalité, MinT ne perd plus rien. Quand elle voit des régions presque indépendantes alors qu\u0026rsquo;elles se trompent ensemble, MinT répartit mal ses corrections. Avec un bon W, il ne fait pas mieux que le bottom-up pour autant.\nGain agrégé sur le bottom-up dans chaque étude. Les 4 mardis de novembre sont l\u0026rsquo;exception ; avec MSTL, le bottom-up est toujours devant ou à égalité, et avec AutoETS toutes les méthodes se resserrent autour de lui. Le bilan : sur 356 origines et deux modèles de base, le bottom-up n\u0026rsquo;est jamais battu de façon significative. Ce qui s\u0026rsquo;affaiblit, c\u0026rsquo;est l\u0026rsquo;ampleur : en 2023 ou avec AutoETS, choisir entre BU, WLS et OLS ne change presque rien.\nCe qui a coincé # Les données avant les modèles. La Nouvelle-Aquitaine s\u0026rsquo;arrête fin 2024. La source a aussi un défaut de changement d\u0026rsquo;heure chaque année : doublons en mars, heure manquante en octobre. Une ligne absente ne lève aucune erreur, elle fausse le total en silence. 4 origines ne classent rien. En tirant les origines au hasard, le gain de MinT sur le bottom-up va de −10,8 % à +4,3 % avec 4 origines. Il en faut 22 en 2024, et 96 en 2023 où l\u0026rsquo;écart est plus petit, pour que tous les tirages donnent le même signe. D\u0026rsquo;où des tests appariés avec correction de Holm avant de conclure. Une moyenne de rapports peut mentir. Face au bottom-up, la prévision RTE débiaisée fait 13,6 % de mieux en gain agrégé, mais 51,6 % de moins bien en moyenne des gains par origine : les jours calmes, l\u0026rsquo;erreur du bottom-up est minuscule et le rapport explose. Pour comparer des méthodes très différentes, on compare des moyennes d\u0026rsquo;erreurs, pas des moyennes de rapports. Le λ naïf ne passe pas à l\u0026rsquo;échelle. Le tenseur T × m × m pèse 31 Go à m = 5 000. Passer par la matrice de Gram T × T ramène ce coût à 0,6 Go, pour le même résultat. Vérifier un paquet avant de le recommander. BayesReconPy 0.5.0 ne s\u0026rsquo;installe plus tel quel (PuLP 4), et sa licence est contradictoire. Ce que j\u0026rsquo;en retiens # Cohérent ne veut pas dire calibré, ni plus précis : MinT garantit la cohérence, le reste est un bonus. Sur une hiérarchie plate et très corrélée, le bottom-up est le choix le plus précis et le plus simple à défendre : il n\u0026rsquo;est jamais battu de façon significative, sur deux années et deux modèles de base. Le levier est le modèle de base et l\u0026rsquo;information qu\u0026rsquo;il reçoit : la météo vaut plus que toute réconciliation. Une hypothèse cachée (W in-sample) peut renverser un résultat : il faut la tester, pas la supposer. Ici, trois expériences indépendantes pointent la même cause. Une conclusion se confirme en changeant une seule chose à la fois : l\u0026rsquo;année, le modèle, la façon d\u0026rsquo;estimer W. Reproduire # git clone https://github.com/gwils28/W37_hierarchical_reconciliation cd W37_hierarchical_reconciliation uv sync \u0026amp;\u0026amp; uv run pytest # 100 tests : 89 unitaires, 11 de bout en bout make eco2mix # télécharge les données RTE, backtest, rapport make eco2mix-robustness # 119 origines de 2024 (≈ 40 min) make eco2mix-confirmation # 2023, puis AutoETS sur 2024 (≈ 25 min) Données : Open Data Réseaux Énergies (ODRÉ) — RTE, Licence Ouverte v2.0.\n","date":"8 octobre 2026","externalUrl":null,"permalink":"/projects/w37-reconciliation-hierarchique/","section":"Projets","summary":"MinT réécrit de zéro, ses hypothèses testées une à une, et un mini-projet éCO2mix (12 régions → France) où le bottom-up bat la méthode de référence, sur 356 origines, deux années et deux modèles de base.","title":"Réconciliation hiérarchique : MinT de zéro, testé sur la consommation électrique de 12 régions françaises","type":"projects"},{"content":" Le total national prévu ne vaut pas la somme des prévisions régionales. Quelqu\u0026rsquo;un doit trancher, chaque mois. La réconciliation hiérarchique promet de supprimer cet arbitrage. Elle tient cette promesse… et seulement celle-là. Temps de lecture : environ 23 minutes. Les formules sont toujours suivies de leur intuition : on peut les survoler sans perdre le fil.\nNote de transparence. Ces travaux ont été menés à titre personnel, dans un but d\u0026rsquo;apprentissage, avec l\u0026rsquo;assistance d\u0026rsquo;une IA :\npour le code, la relecture technique et méthodologique ; pour comprendre les outils et les méthodes mathématiques ; pour l\u0026rsquo;aide à la rédaction, la traduction et la correction du texte ; pour générer l\u0026rsquo;image de couverture de l\u0026rsquo;article. Le choix du sujet, le protocole, la vérification des résultats et les conclusions sont les miens, et j\u0026rsquo;en assume les erreurs. Comment j\u0026rsquo;utilise l\u0026rsquo;IA\nCet article restitue ma veille active de la semaine 37 (septembre 2026). Je ne me suis pas contenté de lire : j\u0026rsquo;ai vérifié les sources, réécrit MinT de zéro, monté des expériences contrôlées, réconcilié les prévisions de consommation électrique des 12 régions françaises et testé un paquet de réconciliation bayésienne. Tout est reproductible, avec les versions épinglées, dans le dépôt W37_hierarchical_reconciliation.\nEn bref. La réconciliation garantit la cohérence, pas la précision ni la calibration. J\u0026rsquo;en ai trouvé quatre preuves :\nEn théorie : un biais sur une seule prévision contamine toutes les autres. En probabiliste : un intervalle annoncé à 90 % au niveau total peut n\u0026rsquo;en couvrir que 76 %. Sur des données réelles : sur 119 jours de 2024, la méthode la plus simple (bottom-up) bat MinT. Sur des ventes au détail : 98 % des intervalles réconciliés par MinT commencent sous zéro. 1. Le problème : des prévisions qui ne s\u0026rsquo;additionnent pas # Prenons une coopérative qui vend dans deux régions, chacune par deux canaux : la grande distribution (GMS) et la restauration (RHD). Cela fait 7 séries à prévoir : le total, 2 régions et 4 couples région × canal. Chaque série a son modèle, réglé au mieux. Mais personne n\u0026rsquo;a demandé aux modèles de se mettre d\u0026rsquo;accord.\nLes régions prévoient 1 030 tonnes, le total en prévoit 1 000. Lequel croire ? La structure se décrit par une matrice de sommation \\(S\\). Les séries du bas, les feuilles \\(b_t\\), déterminent tout le reste :\n$$ y_t = S\\,b_t, \\qquad S = \\begin{pmatrix} 1 \u0026 1 \u0026 1 \u0026 1\\\\ 1 \u0026 1 \u0026 0 \u0026 0\\\\ 0 \u0026 0 \u0026 1 \u0026 1\\\\ 1 \u0026 0 \u0026 0 \u0026 0\\\\ 0 \u0026 1 \u0026 0 \u0026 0\\\\ 0 \u0026 0 \u0026 1 \u0026 0\\\\ 0 \u0026 0 \u0026 0 \u0026 1 \\end{pmatrix} \\begin{matrix}\\leftarrow \\text{total}\\\\ \\leftarrow \\text{Bretagne}\\\\ \\leftarrow \\text{Pays de la Loire}\\\\ \\leftarrow \\text{Bretagne / GMS}\\\\ \\leftarrow \\text{Bretagne / RHD}\\\\ \\leftarrow \\text{PdL / GMS}\\\\ \\leftarrow \\text{PdL / RHD}\\end{matrix} $$Un vecteur de prévisions est cohérent s\u0026rsquo;il s\u0026rsquo;écrit \\(S\\,b\\) pour un certain \\(b\\) : toutes les sommes tombent juste. Les prévisions de base \\(\\hat y\\), produites série par série, ne le sont presque jamais.\nLes deux réponses classiques jettent de l\u0026rsquo;information :\nle bottom-up additionne les feuilles et ignore les prévisions des agrégats, souvent plus stables ; le top-down répartit le total selon des proportions historiques et ignore ce que savent les modèles des feuilles. S\u0026rsquo;il calcule ses proportions sur un historique qui inclut la période de test, il fuit en plus de l\u0026rsquo;information du futur, sans que rien ne le signale dans les métriques. La réconciliation optimale se propose d\u0026rsquo;utiliser toutes les prévisions à la fois.\n2. MinT en cinq minutes : réconcilier, c\u0026rsquo;est projeter # Le cadre # On cherche une matrice \\(G\\) qui ramène les \\(m\\) prévisions de base à \\(n_b\\) feuilles, que l\u0026rsquo;on ré-additionne ensuite :\n$$ \\tilde y = S\\,G\\,\\hat y . $$Deux conditions encadrent le choix de \\(G\\) :\nCondition Formule Ce qu\u0026rsquo;elle veut dire préservation du non-biais \\(SGS = S\\) un vecteur déjà cohérent n\u0026rsquo;est pas modifié variance de l\u0026rsquo;erreur réconciliée \\(\\operatorname{Var}(y - \\tilde y) = S G W G^\\top S^\\top\\) avec \\(W = \\operatorname{Var}(y - \\hat y)\\), la covariance des erreurs de base MinT (Minimum Trace, Wickramasuriya, Athanasopoulos et Hyndman, 2019) minimise la trace de cette variance sous la contrainte \\(SGS = S\\). La solution a une forme fermée :\n$$ G = \\left(S^\\top W^{-1} S\\right)^{-1} S^\\top W^{-1}, \\qquad P = S\\,G . $$ L\u0026rsquo;intuition géométrique # \\(P\\) est une projection (\\(P^2 = P\\)). Les vecteurs cohérents forment un sous-espace de dimension \\(n_b\\) dans \\(\\mathbb{R}^m\\), et réconcilier revient à ramener \\(\\hat y\\) sur ce sous-espace. Toute la question est : dans quelle direction ?\nOLS projette au plus court. MinT cherche le point cohérent le plus plausible compte tenu des erreurs. OLS (\\(W = I\\)) projette orthogonalement : toutes les séries sont jugées aussi fiables. MinT projette selon \\(W^{-1}\\). Il retient le point cohérent qui est le plus plausible compte tenu de la forme des erreurs : il bouge beaucoup les séries mal prévues et peu les autres. Le bottom-up est aussi une projection, avec \\(G = [\\,0 \\mid I\\,]\\) : il ne bouge que les agrégats. Méthode Matrice \\(W\\) utilisée Ce qu\u0026rsquo;elle suppose bottom-up — (\\(G = [0 \\mid I]\\)) les agrégats n\u0026rsquo;apportent rien OLS \\(I\\) toutes les erreurs se valent WLS struct \\(\\operatorname{diag}(S\\mathbf{1})\\) variance proportionnelle au nombre de feuilles agrégées WLS var \\(\\operatorname{diag}(\\hat W)\\) variances propres, pas de corrélations MinT shrink \\(\\lambda D + (1 - \\lambda)\\hat W\\) variances et corrélations, régularisées Le détail qui comptera plus loin # \\(W\\) est inconnue. En pratique, on l\u0026rsquo;estime sur les résidus in-sample des modèles de base, puis on la régularise par le shrinkage de Schäfer et Strimmer : on tire les corrélations vers zéro avec une intensité \\(\\lambda \\in [0, 1]\\) estimée sur les données. Gardez ce point en tête : c\u0026rsquo;est lui qui fera perdre MinT sur les données réelles (partie 6).\n3. Ce que dit la veille 2026 # J\u0026rsquo;ai lu six travaux, choisis pour couvrir les angles morts de MinT. Pour chacun, j\u0026rsquo;ai confronté ce qu\u0026rsquo;on en disait à son résumé officiel. Le plan de lecture en sortait parfois enjolivé.\nChaque travail récent attaque un angle mort de MinT. Travail Ce qu\u0026rsquo;il apporte Ce que la vérification a corrigé Li, Chen, Taylor, Mao — A Forecast Combination Framework for Hierarchical and Grouped Time Series Reconciliation (2608.13886) Les poids de combinaison optimaux retrouvent exactement MinT. Quarante ans de littérature sur la combinaison de prévisions (shrinkage, pénalités, positivité) deviennent réutilisables. — Rønlev-Knudsen, Madsen, Møller — Online forecast reconciliation using linear models (2606.23326) La réconciliation comme régression régularisée, mise à jour récursivement : plus besoin de réestimer \\(W\\) à chaque run. Le cas d\u0026rsquo;étude est une hiérarchie temporelle, pas géographique. À valider avant de transposer. Nugteren, Abolghasemi, Mengersen, Drovandi — Hierarchical Bayes meets hierarchical forecasting (2606.23009) Un modèle bayésien qui cible le niveau qui porte la décision. La cohérence y est souple (pénalisée), pas exacte. Wang, Johnson, Klee, Malloy — Billions-Scale Forecast Reconciliation (2602.05030) Réconciliation à plus de quatre milliards de valeurs ; moindres carrés et répartition par parts coïncident sous conditions. — Panagiotelis, Gamakumara, Athanasopoulos, Hyndman — Probabilistic forecast reconciliation (EJOR, 2023) La bonne définition de la réconciliation d\u0026rsquo;une distribution, évaluée par energy score et variogram score. — Biswas, Zambon, Nespoli, Corani — Nonlinear Probabilistic Forecast Reconciliation (2604.26668) Ce qui se passe quand \\(y = Sb\\) ne tient plus : logs, ratios, prix moyens. — La vérification s\u0026rsquo;est limitée aux résumés et aux pages officielles, pas aux PDF complets.\nTrois tendances s\u0026rsquo;en dégagent :\nLa réconciliation devient un estimateur, plus un post-traitement. Vue comme combinaison de prévisions ou comme régression régularisée, elle se régularise, se teste et se défend devant une revue d\u0026rsquo;architecture. Les objections d\u0026rsquo;échelle tombent. Quatre milliards de valeurs d\u0026rsquo;un côté, une estimation récursive de l\u0026rsquo;autre. Le front ouvert est le probabiliste et le non-linéaire. C\u0026rsquo;est précisément là que MinT ponctuel ne dit plus rien. La partie 5 le montre. 4. Réécrire MinT soi-même # Appeler MinTrace(method=\u0026quot;mint_shrink\u0026quot;) prend une ligne. Mais tant qu\u0026rsquo;on n\u0026rsquo;a pas écrit la formule soi-même, on ne sait pas expliquer un résultat bizarre. J\u0026rsquo;ai donc réimplémenté MinT shrink en NumPy, en m\u0026rsquo;interdisant de lire le code de HierarchicalForecast avant d\u0026rsquo;avoir figé le mien. Voici le cœur, condensé :\nimport numpy as np def mint_shrink(E, S): \u0026#34;\u0026#34;\u0026#34;E : résidus in-sample (T × m), S : matrice de sommation (m × nb). Renvoie P = SG.\u0026#34;\u0026#34;\u0026#34; T, m = E.shape W1 = E.T @ E / T # covariance empirique Z = E / np.sqrt(np.diag(W1)) # résidus standardisés R = Z.T @ Z / T # corrélations w = Z[:, :, None] * Z[:, None, :] # T × m × m : naïf, voir plus bas var_r = T / (T - 1) ** 3 * ((w - R) ** 2).sum(axis=0) off = ~np.eye(m, dtype=bool) lam = np.clip(var_r[off].sum() / (R[off] ** 2).sum(), 0, 1) # λ de Schäfer–Strimmer W = lam * np.diag(np.diag(W1)) + (1 - lam) * W1 WiS = np.linalg.solve(W, S) # W⁻¹S, sans inverser W G = np.linalg.solve(S.T @ WiS, WiS.T) # (S\u0026#39;W⁻¹S)⁻¹ S\u0026#39;W⁻¹ return S @ G Sur une hiérarchie synthétique (2 régions × 2 canaux, 80 trimestres, modèles AutoETS), les contrôles passent :\nContrôle Seuil Mesuré \\(P^2 = P\\) et \\(SGS = S\\) \u0026lt; 1e-12 2,2e-16 incohérence des prévisions réconciliées précision machine 5,7e-14 écart relatif à HierarchicalForecast 1.5.1 \u0026lt; 1e-3 1,07e-5 écart non nul \u0026gt; 0 oui Le dernier contrôle est volontaire : un écart exactement nul aurait voulu dire que j\u0026rsquo;avais recopié leur code. Ce petit écart résiduel m\u0026rsquo;a appris trois choses.\nLeçon 1 : un même nom de méthode recouvre plusieurs estimateurs # Le plan de veille attribuait l\u0026rsquo;écart à une différence dans le calcul de \\(\\lambda\\). C\u0026rsquo;est faux : les deux \\(\\lambda\\) sont égaux à 2e-5 près. Environ 99,8 % de l\u0026rsquo;écart vient de la covariance de base. La bibliothèque centre les résidus et divise par \\(T - 1\\) (np.cov), alors que ma version prend \\(E^\\top E / T\\). Avec la même convention, l\u0026rsquo;écart tombe à 3e-6.\nConséquence pratique : mint_shrink désigne au moins trois choix d\u0026rsquo;implémentation (centrage, ddof, variante de \\(\\lambda\\)) qui changent le chiffre publié. Si deux équipes comparent leurs runs, il faut épingler la version et documenter l\u0026rsquo;estimateur.\nLeçon 2 : le shrinkage est une condition d\u0026rsquo;existence # Même avec 72 observations pour 6 séries, la covariance empirique est mal conditionnée (conditionnement ≈ 3·10⁴). Le résidu d\u0026rsquo;un agrégat est presque la somme des résidus de ses feuilles, donc les colonnes de \\(E\\) sont quasi colinéaires. Le shrinkage ramène le conditionnement à ≈ 23.\nAvec plus de séries que d\u0026rsquo;observations (\\(m \u003e T\\)), c\u0026rsquo;est pire : \\(\\hat W\\) est de rang \\(T\\), donc singulière. Avec \\(T = 4\\) et \\(m = 6\\), son conditionnement atteint 1,7·10¹⁷. Le shrinkage choisit alors seul \\(\\lambda = 0{,}907\\) et ramène le conditionnement à 10. Le shrinkage n\u0026rsquo;est pas un confort : sans lui, MinT n\u0026rsquo;existe pas sur un catalogue réel. 40 000 références sur trois ans d\u0026rsquo;historique hebdomadaire, c\u0026rsquo;est \\(m/T \\approx 250\\).\nLeçon 3 : le mur est aussi en mémoire # La ligne marquée « naïf » construit un tenseur \\(T \\times m \\times m\\) : 31 Go pour 5 000 séries. Ma première version a fait tuer le noyau Jupyter par le système. Or \\(\\lambda\\) ne dépend que de sommes hors diagonale, qu\u0026rsquo;on peut calculer à partir de la matrice de Gram \\(T \\times T\\). La mémoire passe de \\(O(T m^2)\\) à \\(O(T^2 + Tm)\\), soit 0,6 Go, pour un résultat identique à 1e-10 près. Au-delà, c\u0026rsquo;est \\(W\\) elle-même qu\u0026rsquo;il ne faut plus former. Une diagonale plus une matrice de rang \\(T\\) s\u0026rsquo;inverse par l\u0026rsquo;identité de Woodbury, sans jamais construire de matrice \\(m \\times m\\).\n5. Trois endroits où MinT casse # MinT est optimal sous hypothèses : prévisions de base sans biais, \\(W\\) connue, contraintes linéaires, quantités continues. J\u0026rsquo;ai monté une expérience contrôlée pour chacune de ces hypothèses. Chaque fois, j\u0026rsquo;ai cherché où elle cède.\n5.1 MinT propage le biais # La condition \\(SGS = S\\) préserve l\u0026rsquo;absence de biais, elle ne la crée pas. Le contre-exemple tient en quatre séries : un total et trois feuilles. Les prévisions sont exactes partout, sauf un biais de +15 sur le total. \\(W\\) fait 9 fois plus confiance au total qu\u0026rsquo;à chaque feuille.\nSérie Vérité Base Bottom-up MinT Erreur MinT Total 60 75 60 74,46 14,46 Feuille 1 10 10 10 14,82 4,82 Feuille 2 20 20 20 24,82 4,82 Feuille 3 30 30 30 34,82 4,82 Le bottom-up ignore le total et ne voit rien. MinT, qui lui fait confiance, injecte le biais dans chaque feuille. Comme la réconciliation est linéaire, le biais réconcilié vaut \\(P \\times\\) le biais de base : il se répand selon une colonne de \\(P\\).\nUn biais sur le seul total se retrouve dans toutes les feuilles. Ce n\u0026rsquo;est pas tout ou rien : il existe un seuil. Par simulation (20 000 tirages), MinT reste meilleur que le bottom-up tant que le biais du total reste sous ≈ 4,5. Au-delà, il devient pire au total ; au-delà de ≈ 5,25, il est pire aussi aux feuilles, donc à tous les niveaux.\nErreur moyenne selon le biais du total : MinT perd au-delà d\u0026rsquo;un seuil, au total puis aux feuilles. Le réflexe. Tester la moyenne des résidus par niveau, puis corriger les prévisions de base avant de réconcilier. Ne jamais retoucher une prévision réconciliée après coup : modifier une seule série casse la cohérence. 5.2 MinT réconcilie des moyennes, pas des quantiles # C\u0026rsquo;est le cœur de la semaine. Le gestionnaire de stock ne décide pas sur une moyenne. Il décide sur un quantile : le stock qui couvre la demande 90 % du temps. Or le quantile d\u0026rsquo;une somme n\u0026rsquo;est pas la somme des quantiles.\nAdditionner les quantiles à 90 % des régions donne un total qui couvre 93,1 % du temps, pas 90 %. Conséquence : aucun vecteur de quantiles n\u0026rsquo;est cohérent. La cohérence est une propriété de la loi jointe, pas des marginales.\nL\u0026rsquo;expérience, entièrement simulée pour connaître la vérité : un total et trois régions gaussiennes, corrélées à \\(\\rho = 0{,}6\\) (la météo les fait bouger ensemble). Chaque modèle de base est parfait sur sa propre série. Les moyennes sont déjà cohérentes, donc tout ce qu\u0026rsquo;on observe est un pur effet « quantile ». On compare trois façons d\u0026rsquo;obtenir des quantiles à 90 % réconciliés :\nA. Appliquer \\(P\\) directement aux quantiles. C\u0026rsquo;est l\u0026rsquo;erreur la plus courante. B. Tirer des échantillons de chaque série indépendamment, projeter chaque échantillon, puis lire les quantiles. C. Pareil, mais avec des échantillons joints, qui respectent la dépendance entre séries. Série Couverture A Couverture B Couverture C Total 0,922 0,764 0,900 Région 1 0,887 0,878 0,900 Région 2 0,890 0,888 0,899 Région 3 0,893 0,894 0,901 Cible : 0,900. 400 000 tirages, graine fixe.\nSeule la méthode C, qui garde la dépendance, atteint la couverture visée à tous les niveaux. Comment lire ce tableau :\nA est fausse dans les deux sens à la fois. Elle sur-couvre au total et sous-couvre dans les régions. Aucun facteur global ne peut la corriger. Le vecteur obtenu somme parfaitement, mais il n\u0026rsquo;est le quantile de rien. B, qui a l\u0026rsquo;air rigoureuse, est la pire : 76 % de couverture au total pour 90 % annoncés. Projeter des échantillons est la bonne mécanique. Mais des tirages indépendants effacent la dépendance, et la variance de la somme est massivement sous-estimée. C est calibrée partout. Sa seule différence avec B est la structure de dépendance. Le balayage en corrélation rend le résultat difficile à contester :\nQuand la corrélation monte, l\u0026rsquo;erreur naïve (A) se voit de moins en moins et l\u0026rsquo;erreur savante (B) coûte de plus en plus cher. \\(\\rho\\) Écart « somme des q90 » / « q90 de la somme » Couverture B au total 0,0 +6,6 % 0,816 0,3 +4,2 % 0,787 0,6 +2,2 % 0,764 0,9 +0,5 % 0,746 Le fait contre-intuitif. Les deux erreurs vont en sens opposé. Plus les séries sont corrélées, moins l\u0026rsquo;erreur naïve se voit, et plus l\u0026rsquo;erreur savante coûte cher. C\u0026rsquo;est exactement le régime de l\u0026rsquo;énergie (la météo) et du commerce de détail (les promotions). En pratique, on ne connaît pas la vraie covariance. La solution est le bootstrap joint des résidus in-sample : on rééchantillonne des instants, et toutes les séries sont tirées au même instant. On retrouve 0,902 au total. Le bootstrap série par série reproduit l\u0026rsquo;erreur de B (0,760). Dans HierarchicalForecast, ce sont les méthodes Bootstrap et PERMBU.\nRééchantillonner des instants, et non des séries, suffit à retrouver la calibration. Une règle simple en découle : même instant, toutes les séries. Un bootstrap qui tire des résidus à des dates différentes selon les séries (historiques de longueurs inégales, trous) est une méthode B déguisée.\n5.3 MinT ne connaît pas le zéro # Troisième hypothèse implicite : les quantités sont continues et symétriques. Sur des ventes d\u0026rsquo;articles à rotation lente (souvent 0, parfois un pic), une gaussienne de la bonne variance déborde largement sous zéro.\nUne gaussienne ajustée sur des comptages place une partie de sa masse sur des ventes négatives. Je l\u0026rsquo;ai mesuré sur un magasin du jeu M5 (Walmart, magasin CA_1 : 3 049 articles et 11 agrégats, prévision à un jour), en comparant MinT gaussien à BayesReconPy. Ce paquet ne projette pas : il conditionne. Il garde la loi jointe des articles sur les entiers positifs et la repondère par ce que disent les prévisions des agrégats (règle de Bayes).\nMinT gaussien bottom-up MixCond TD-cond articles à moyenne négative 11 0 0 0 articles dont la borne à 90 % est \u0026lt; 0 2 992 (98 %) 0 0 0 RPS articles (plus bas = mieux) 0,819 0,676 0,674 0,669 MIS agrégats (plus bas = mieux) 1 825 2 330 2 312 444 Le chiffre qui compte dépend de la décision. Pour une décision sur la moyenne, 0,36 % des prévisions sont négatives. Pour un niveau de service, c\u0026rsquo;est 98 %. Le mécanisme est lisible : avec une \\(W\\) diagonale, l\u0026rsquo;ajustement de chaque article est proportionnel à sa variance. Les articles « presque toujours 0, parfois un gros pic » absorbent donc l\u0026rsquo;essentiel de la correction et passent sous zéro.\nLes articles passés sous zéro ont une petite moyenne et une dispersion énorme. Deux observations de plus :\nLe bottom-up et MixCond rendent les intervalles des agrégats cinq fois moins bons. Supposer 3 049 articles indépendants donne un total beaucoup trop étroit : c\u0026rsquo;est la méthode B de la section précédente, à grande échelle. TD-cond, qui part des agrégats, évite ce piège et reste le plus précis aux deux niveaux. Mes chiffres reproduisent la vignette R officielle du paquet bayesRecon sur le même magasin, avec un écart médian de 0,15 point de skill score. Vérifier un paquet avant de le recommander. BayesReconPy 0.5.0 ne s\u0026rsquo;installait plus tel quel au moment du test : la sortie de PuLP 4 casse un import (il faut pulp\u0026lt;4). Sa licence est contradictoire : MIT dans les métadonnées, LGPL-3.0 dans le dépôt. Enfin, reconc_td_cond modifie ses entrées en place. C\u0026rsquo;est un excellent outil spécialisé, mais il faut en connaître les conditions d\u0026rsquo;emploi. 6. Sur des données réelles : douze régions, une France # Les expériences précédentes sont contrôlées. Restait à confronter MinT à des données réelles. J\u0026rsquo;ai pris la consommation électrique des 12 régions métropolitaines (éCO2mix, RTE, via ODRÉ, Licence Ouverte v2.0). L\u0026rsquo;objectif métier : livrer des prévisions horaires à J+1 dont la somme des régions reproduit exactement le total France.\nVérifier les données avant de prévoir # Trois découvertes ont précédé la première prévision :\nLa Nouvelle-Aquitaine s\u0026rsquo;arrête fin 2024, alors que les autres régions continuent. Depuis 2025, la somme des régions n\u0026rsquo;est plus le total France. Le protocole se limite donc à 2013–2024. La source a un défaut de changement d\u0026rsquo;heure, chaque année et dans chaque région : deux doublons fin mars, une heure manquante fin octobre. Une ligne absente fausse un total sans lever la moindre erreur, ce qui est plus dangereux qu\u0026rsquo;un NaN. Le total officiel n\u0026rsquo;est pas toujours la somme des régions. L\u0026rsquo;écart dépasse 200 MW en octobre 2016. Un test année par année montre qu\u0026rsquo;il reste sous 6 MW (l\u0026rsquo;arrondi) de 2014 à 2024, hors 2016 : sur la période réellement utilisée, réconcilier vers ce total a donc un sens. Chaque année, deux doublons en mars et une heure manquante en octobre. Le protocole, fixé avant les résultats # Pas horaire : moyenne des demi-heures, en UTC. Des MW restent des MW, et aucun jour ne compte 23 ou 25 heures. Modèles de base : SeasonalNaive(168) comme plancher, MSTL à double saisonnalité (24 h et 168 h) comme modèle réconcilié. Fenêtre : 52 semaines glissantes, strictement antérieures à chaque origine. Méthodes comparées : bottom-up, OLS, WLS struct, WLS var, MinT shrink. Métrique : MASE par niveau (France, puis moyenne des régions), jamais en moyenne globale. Le plan initial prévoyait 4 origines : quatre mardis de novembre 2024. J\u0026rsquo;ai ajouté une étude de robustesse sur 119 origines, une tous les trois jours sur l\u0026rsquo;année 2024.\nLe classement s\u0026rsquo;inverse # La cohérence est garantie : l\u0026rsquo;incohérence maximale est exactement 0 MW après réconciliation, contre 672 MW pour les prévisions de base. Mais la précision raconte une autre histoire selon le nombre d\u0026rsquo;origines :\nMASE moyen, 119 origines France Régions SeasonalNaive (plancher) 0,947 0,939 bottom-up 0,352 0,427 WLS var 0,356 0,430 WLS struct 0,358 0,432 OLS 0,366 0,439 MinT shrink 0,370 0,439 Sur 4 origines, MinT semblait gagner au niveau régional (−2,2 %), sans significativité. Sur 119 origines, le bottom-up est la meilleure méthode aux deux niveaux, et MinT shrink fait moins bien que lui sur les 13 séries, sans exception. Les quatre mardis étaient tombés du côté favorable d\u0026rsquo;une distribution très étalée. Après correction de Holm, la preuve reste modérée (p ≈ 0,05), mais le signe est constant. Quatre origines ne suffisent pas à classer des méthodes.\nLa cause : une hypothèse cachée de mint_shrink # MinT suppose que la covariance estimée \\(W\\) ressemble à celle des vraies erreurs de prévision. J\u0026rsquo;ai testé cette hypothèse directement. Les résidus in-sample de MSTL sont des restes de lissage, corrélés à 0,11 en moyenne entre régions. Les vraies erreurs hors échantillon le sont à 0,56 (intervalle bootstrap [0,45 ; 0,65]). MinT réconcilie donc avec une carte des erreurs fausse.\nÀ gauche, ce que voit W ; à droite, ce que sont les vraies erreurs. Pour isoler la cause, j\u0026rsquo;ai monté une expérience contrôlée. Tout reste identique, sauf \\(W\\), que j\u0026rsquo;estime sur les erreurs hors échantillon des origines passées, donc toujours sans fuite.\n99 origines MASE régional vs bottom-up p (Wilcoxon) MinT shrink, \\(W\\) in-sample +2,6 % 0,017 MinT, \\(W\\) estimée sur erreurs passées −0,1 % 0,83 Corriger W supprime le handicap de MinT, sans lui permettre de battre le bottom-up. Corriger \\(W\\) supprime le handicap, mais ne fait pas gagner MinT. C\u0026rsquo;est plausible : les erreurs régionales sont fortement corrélées et de même signe (une vague de froid touche tout le monde), et il n\u0026rsquo;y a donc pas d\u0026rsquo;information croisée à exploiter.\nMa recommandation sur ce cas :\nLivrer le bottom-up. Il remplit l\u0026rsquo;objectif (cohérence exacte), il est le plus précis ici, et il s\u0026rsquo;explique en une phrase : « le national est la somme des régions ». Ne pas utiliser mint_shrink avec les résidus in-sample d\u0026rsquo;un modèle de lissage. Si MinT est nécessaire, estimer \\(W\\) sur des erreurs de backtest glissant. Investir dans les modèles de base (température, jours fériés). Les plus grosses erreurs sont communes à toutes les régions, et aucune réconciliation ne les rattrape. 7. En production : où vit la réconciliation ? # Dernière question, d\u0026rsquo;architecture : la réconciliation doit-elle être un job batch, une étape dans l\u0026rsquo;API, une vue SQL ou un estimateur mis à jour en ligne ? J\u0026rsquo;ai retenu un job batch matérialisé avec un contrôle qualité bloquant. Les raisons tiennent dans un schéma :\nflowchart TD H[Historique et covariables] --\u003e B[\"1. Prévisions de baseun modèle par série → ŷ incohérent\"] B --\u003e|ŷ + résidus du train| R[\"2. RéconciliationP = S(S'W⁻¹S)⁻¹S'W⁻¹\"] S[\"S versionnée + empreinte\"] --\u003e R R --\u003e Q([\"3. Contrôle qualitécohérence · négatifs · MASE par niveau\"]) Q --\u003e|échec| X[Publication bloquée + alerte] Q --\u003e|succès| T[(\"Table forecast_reconciledartefact immuable : run_id, empreinte de S, version de W\")] T --\u003e A[\"API /forecast?level=lecture seule\"] Le point non négociable : l\u0026rsquo;API ne réconcilie jamais à la volée. Sinon deux appels simultanés sur deux niveaux peuvent renvoyer des chiffres qui ne somment pas, c\u0026rsquo;est-à-dire exactement le problème qu\u0026rsquo;on cherchait à supprimer. La cohérence est une propriété de l\u0026rsquo;artefact, pas de la requête.\nOption Pour Contre Batch matérialisé cohérence par construction, auditable latence de rafraîchissement ✅ retenu À la volée dans l\u0026rsquo;API toujours à jour cohérence non garantie entre appels ❌ Vue SQL zéro infrastructure MinT en SQL est ingérable, on retombe sur du bottom-up déguisé ❌ Estimation récursive en ligne \\(W\\) suit les changements de régime état mutable à versionner et à rejouer ⏳ plus tard Écrire ce contrat en code exécutable, avec des tests où chaque règle échoue, a corrigé quatre choses dans la version sur papier :\nLe repli vers une méthode plus simple ne doit pas se déclencher quand \\(m \u003e T\\). C\u0026rsquo;est précisément le cas pour lequel le shrinkage existe (leçon 2). Il se déclenche sur le conditionnement mesuré de \\(W\\). Une tolérance absolue de cohérence n\u0026rsquo;a pas le même sens sur 10 MW et sur 50 GW. J\u0026rsquo;ai ajouté une tolérance relative. La règle « pas de dégradation du MASE » s\u0026rsquo;applique niveau par niveau, jamais en moyenne. Sinon, un gain au national masque une perte au niveau de décision. L\u0026rsquo;empreinte de \\(S\\) couvre aussi les libellés. Une feuille renommée change la hiérarchie sans changer la matrice. reconciler: method: mint_shrink fallback: wls_struct # déclenché par cond(W) mesuré, pas par m \u0026gt; T covariance_window: 104 # semaines, strictement antérieures à l\u0026#39;origine quality_gate: coherence_tol_abs: 1e-6 coherence_tol_rel: 1e-9 # le bruit flottant croît avec les valeurs max_negative_share: 0.001 # MinT gaussien sur M5 : 0,0036 → bloqué compare_to_baseline: bottom_up # échec si le MASE se dégrade de plus de 2 % à UN niveau fail_action: block_publish 8. Ce que j\u0026rsquo;en retiens # La réconciliation vend de la cohérence, pas de la précision. Le gain de précision est fréquent, jamais garanti, et il dépend d\u0026rsquo;hypothèses qu\u0026rsquo;on peut tester. Toujours comparer au bottom-up, niveau par niveau. Battre des prévisions incohérentes ne prouve rien, et une moyenne globale cache le niveau qui porte la décision. \\(W\\) est le maillon faible. Estimée sur des résidus in-sample, elle peut décrire des erreurs qui n\u0026rsquo;existent pas. Le shrinkage est indispensable, mais il ne corrige pas une mauvaise source. Ne jamais réconcilier des quantiles. Réconcilier une distribution, c\u0026rsquo;est projeter des échantillons joints. Des tirages indépendants donnent un intervalle à 90 % qui en couvre 76. Les comptages exigent d\u0026rsquo;autres outils. Une gaussienne n\u0026rsquo;a rien à faire près de zéro. Le conditionnement (TD-cond) est à la fois cohérent, positif et plus précis. Vérifier avant de croire : les données (heure d\u0026rsquo;été, région manquante), les sources (ce que dit vraiment le résumé), les paquets (installation, licence) et les classements (4 origines contre 119). Le test de compréhension. « Vous réconciliez avec MinT. Le MASE s\u0026rsquo;améliore au national mais se dégrade au niveau article. Que se passe-t-il ? » Trois hypothèses, dans l\u0026rsquo;ordre : une prévision de base biaisée (5.1), une \\(W\\) mal estimée (partie 6), une fuite ou une fenêtre d\u0026rsquo;estimation qui chevauche un changement de régime. Et la vraie question est métier : quel niveau porte la décision ? Si l\u0026rsquo;approvisionnement se décide à l\u0026rsquo;article, un gain au national payé à l\u0026rsquo;article est une régression. La suite. Tout ce qui précède s\u0026rsquo;effondre si \\(W\\) est estimée sur des résidus contaminés, si la hiérarchie change sans prévenir ou si une feuille a un trou. La veille de la semaine 38 porte donc sur la construction de variables temporelles sans fuite : fenêtres glissantes correctes, données « telles que connues à la date », référentiels qui changent dans le temps. Le fil rouge : ce n\u0026rsquo;est pas le modèle qui fuit, c\u0026rsquo;est la jointure.\nReproduire et aller plus loin # Le code, les 14 notebooks et les 88 tests sont dans le dépôt gwils28/W37_hierarchical_reconciliation (Python 3.12, HierarchicalForecast 1.5.1, statsforecast 2.1.1, BayesReconPy 0.5.0, versions verrouillées par uv). Chaque chiffre de cet article sort d\u0026rsquo;une commande uv run w37 ….\nSources\nWickramasuriya, Athanasopoulos, Hyndman, Optimal forecast reconciliation for hierarchical and grouped time series through trace minimization, JASA, 2019. Li, Chen, Taylor, Mao, A Forecast Combination Framework for Hierarchical and Grouped Time Series Reconciliation, arXiv, 2026. Rønlev-Knudsen, Madsen, Møller, Online forecast reconciliation using linear models, arXiv, 2026. Nugteren, Abolghasemi, Mengersen, Drovandi, Hierarchical Bayes meets hierarchical forecasting, arXiv, 2026. Wang, Johnson, Klee, Malloy, Billions-Scale Forecast Reconciliation, arXiv, 2026. Panagiotelis, Gamakumara, Athanasopoulos, Hyndman, Probabilistic forecast reconciliation: properties, evaluation and score optimisation, EJOR 306(2), 2023. Biswas, Zambon, Nespoli, Corani, Nonlinear Probabilistic Forecast Reconciliation, arXiv, 2026. Schäfer, Strimmer, A shrinkage approach to large-scale covariance matrix estimation, SAGMB, 2005. Biswas et al., BayesReconPy, JOSS 10(111), 2025. Bibliographie vivante : awesome-forecast-reconciliation. Données : éCO2mix régional, Open Data Réseaux Énergies (ODRÉ), RTE, Licence Ouverte v2.0 ; M5 Forecasting. ","date":"6 octobre 2026","externalUrl":null,"permalink":"/posts/reconciliation-hierarchique/","section":"Articles \u0026 veille","summary":"MinT garantit la cohérence, pas la précision ni la calibration. Démonstration par la théorie, la simulation, les données RTE et des ventes de comptage.","title":"La réconciliation hiérarchique en forecasting : promesses et limites","type":"posts"},{"content":"","date":"6 octobre 2026","externalUrl":null,"permalink":"/tags/probabiliste/","section":"Tags","summary":"","title":"Probabiliste","type":"tags"},{"content":"","date":"6 octobre 2026","externalUrl":null,"permalink":"/en/tags/probabilistic/","section":"Tags","summary":"","title":"Probabilistic","type":"tags"},{"content":" Un agent qui écrit du code plus vite que moi ne m\u0026rsquo;a pas rendu meilleur data scientist. Ce qui a changé la donne, c\u0026rsquo;est la méthode que j\u0026rsquo;ai construite autour : savoir quoi lui donner, quoi lui demander, et surtout quoi vérifier. Temps de lecture : environ 29 minutes. Pas besoin de connaître Claude Code pour suivre : chaque fonctionnalité est expliquée la première fois qu\u0026rsquo;elle apparaît.\nNote de transparence. Ces travaux ont été menés à titre personnel, dans un but d\u0026rsquo;apprentissage, avec l\u0026rsquo;assistance d\u0026rsquo;une IA :\npour le code, la relecture technique et méthodologique ; pour comprendre les outils et les méthodes mathématiques ; pour l\u0026rsquo;aide à la rédaction, la traduction et la correction du texte ; pour générer l\u0026rsquo;image de couverture de l\u0026rsquo;article. Le choix du sujet, le protocole, la vérification des résultats et les conclusions sont les miens, et j\u0026rsquo;en assume les erreurs. Comment j\u0026rsquo;utilise l\u0026rsquo;IA\nMise à jour du 29 octobre 2026. Une version révisée de cette méthode existe : Claude Code en data science : ce qui change dans ma méthode. Cet article reste tel qu\u0026rsquo;il a été publié ; les principes y sont toujours valables. Claude Code est un agent de programmation qui tourne dans le terminal. Contrairement à un chatbot, il ne se contente pas de répondre : il lit les fichiers du projet, lance des commandes, modifie le code, exécute les tests et itère jusqu\u0026rsquo;à ce que le travail lui semble fini. C\u0026rsquo;est précisément ce « lui semble » qui demande une méthode.\nJe l\u0026rsquo;utilise au quotidien : pour mes projets de data science et de ML-AI engineering, pour ma veille, et pour construire ce blog. Cet article décrit comment je travaille avec lui. Ce n\u0026rsquo;est pas un guide exhaustif des fonctionnalités : la documentation officielle le fait mieux, et elle évolue chaque semaine. C\u0026rsquo;est la méthode que j\u0026rsquo;ai retenue, avec ses raisons.\nEn bref. Ma méthode tient en cinq règles :\nLe contexte est la ressource rare : une session par tâche, et je repars de zéro plutôt que de corriger trois fois. Le CLAUDE.md est court : il ne contient que ce que l\u0026rsquo;agent ne peut pas deviner. J\u0026rsquo;explore et je planifie avant de coder, dès que la tâche touche plusieurs fichiers. Rien n\u0026rsquo;est fini sans preuve : un test, un build, une capture, jamais un « c\u0026rsquo;est fait ». Je reste responsable de chaque ligne, et je décide de chaque commit. 1. Pourquoi j\u0026rsquo;ai eu besoin d\u0026rsquo;une méthode # Le terme vibe coding désigne une façon de programmer où l\u0026rsquo;on décrit ce qu\u0026rsquo;on veut, on accepte le code produit sans le lire, on renvoie les erreurs à l\u0026rsquo;IA et on recommence jusqu\u0026rsquo;à ce que ça marche. Pour un prototype jetable, c\u0026rsquo;est très efficace. Pour un modèle de prévision qui va piloter des décisions réelles, c\u0026rsquo;est une catastrophe annoncée.\nDeux praticiens ont posé la distinction qui me sert de boussole. Simon Willison d\u0026rsquo;abord, dans un billet de mars 2025 : si un LLM a écrit le code, mais que vous l\u0026rsquo;avez relu, testé à fond, et que vous savez expliquer à quelqu\u0026rsquo;un d\u0026rsquo;autre comment il fonctionne, ce n\u0026rsquo;est pas du vibe coding, c\u0026rsquo;est du développement logiciel. Kent Beck ensuite, qui parle d\u0026rsquo;augmented coding : en vibe coding, on ne se soucie que du comportement du système ; en augmented coding, on se soucie aussi du code, de sa complexité, des tests et de leur couverture.\nMa règle découle de là : je suis responsable du code produit, quelle que soit la façon dont il a été écrit. Si je ne sais pas expliquer une ligne, elle n\u0026rsquo;a rien à faire dans un dépôt public. Tout le reste de la méthode sert à tenir cette règle sans perdre le bénéfice de l\u0026rsquo;agent.\nCe bénéfice est réel : en une semaine, un agent bien cadré permet d\u0026rsquo;écrire une implémentation de zéro, un backtest complet, des dizaines de tests et des notebooks pédagogiques. Mais une partie de ce travail consiste à contredire ce qui a été annoncé, par l\u0026rsquo;agent comme par les sources, et c\u0026rsquo;est ce qui lui donne sa valeur. J\u0026rsquo;y reviens plus bas.\n2. Le contexte est la ressource rare # C\u0026rsquo;est le principe dont tout le reste découle, et la documentation officielle le dit d\u0026rsquo;emblée : la fenêtre de contexte se remplit vite, et les performances se dégradent à mesure qu\u0026rsquo;elle se remplit.\nCe qu\u0026rsquo;est le contexte # La fenêtre de contexte, c\u0026rsquo;est tout ce que le modèle « voit » à un instant donné : mes messages, ses réponses, chaque fichier qu\u0026rsquo;il a lu, chaque sortie de commande. Une séance de débogage ou l\u0026rsquo;exploration d\u0026rsquo;un dépôt peut consommer des dizaines de milliers de tokens. Quand la fenêtre est pleine, l\u0026rsquo;agent oublie des instructions données plus tôt et commet plus d\u0026rsquo;erreurs. Avant même ma première question, une partie est déjà occupée par les instructions système, la définition des outils et le CLAUDE.md du projet.\nL\u0026rsquo;analogie qui me parle, en tant que data scientist : le contexte est un budget d\u0026rsquo;attention. Chaque fichier lu « pour voir » est une dépense. Un log de 2 000 lignes collé en entier dilue les trois lignes qui comptent.\nMes réflexes # 1 Une tâche, une session Je commence chaque tâche indépendante par /clear, qui remet le contexte à zéro. Mélanger « corrige ce bug », puis « au fait, que penses-tu de ce graphique ? », puis revenir au bug, c\u0026rsquo;est ce que la documentation appelle la kitchen sink session : un contexte rempli de bruit. 2 Deux corrections, pas trois Si l\u0026rsquo;agent se trompe deux fois sur le même point, je ne corrige pas une troisième fois. Le contexte est déjà pollué par les mauvaises pistes. Je repars d\u0026rsquo;une session propre, avec un meilleur premier message qui intègre ce que j\u0026rsquo;ai appris. C\u0026rsquo;est contre-intuitif, et c\u0026rsquo;est presque toujours plus rapide. 3 Revenir en arrière plutôt qu\u0026rsquo;empiler Échap deux fois, ou /rewind, ramène la conversation et le code à un point antérieur. Une tentative ratée s\u0026rsquo;efface au lieu de rester dans le contexte comme un contre-exemple. 4 Compacter en choisissant ce qui reste Sur une longue session utile, /compact résume l\u0026rsquo;historique pour libérer de la place. Je lui dis quoi garder : /compact garde la liste des fichiers modifiés et les commandes de test. Un résumé automatique perd des détails, et ce ne sont pas toujours ceux que j\u0026rsquo;aurais choisis. Écrire l\u0026rsquo;état sur disque # Le corollaire, c\u0026rsquo;est de ne jamais faire du contexte le seul endroit où vit une décision. Ce qui doit survivre à la session va dans un fichier : le plan dans un PLAN.md, le protocole dans un YAML versionné, les conclusions dans le README du bloc. Je commite souvent. Une session devient alors jetable : si elle part dans le décor, je la ferme, et rien d\u0026rsquo;important n\u0026rsquo;est perdu.\nPour un projet de modélisation, cela vaut surtout pour le protocole : je l\u0026rsquo;écris dans un YAML versionné avant de voir le moindre résultat. Personne, ni moi ni l\u0026rsquo;agent, ne peut l\u0026rsquo;ajuster après coup pour embellir un chiffre.\n3. Le CLAUDE.md : ce que l\u0026rsquo;agent ne peut pas deviner # Le CLAUDE.md est un fichier Markdown à la racine du projet, que Claude Code lit au début de chaque session. C\u0026rsquo;est l\u0026rsquo;endroit où l\u0026rsquo;on met les conventions, les commandes, les pièges. C\u0026rsquo;est le levier le plus puissant, et aussi le plus facile à gâcher.\nCourt, ou ignoré # Le piège, c\u0026rsquo;est d\u0026rsquo;y mettre tout. Un CLAUDE.md trop long est partiellement ignoré : les règles importantes se noient dans le bruit. HumanLayer donne un ordre de grandeur utile : les modèles de pointe suivent environ 150 à 200 instructions de façon cohérente, et le prompt système de Claude Code en contient déjà une cinquantaine. Leur propre fichier racine fait moins de soixante lignes.\nLa documentation officielle propose un test que j\u0026rsquo;applique ligne par ligne : si je retire cette ligne, l\u0026rsquo;agent fera-t-il une erreur ? Si non, je la supprime.\n✅ J\u0026rsquo;y mets ❌ Je n\u0026rsquo;y mets pas Les commandes qu\u0026rsquo;on ne devine pas Ce qui se lit dans le code Les choix d\u0026rsquo;architecture propres au projet Les conventions standard du langage Les pièges et comportements surprenants La documentation détaillée d\u0026rsquo;une API (un lien suffit) La façon de vérifier (build, tests) Les tutoriels et longues explications Le pourquoi des règles importantes Les évidences (« écris du code propre ») Un exemple réel : ce blog # Ce blog a son propre CLAUDE.md. Il ne décrit pas Hugo, que le modèle connaît déjà. Il décrit ce qu\u0026rsquo;un développeur, humain ou non, découvrirait à ses dépens :\nBlowfish requires the `config/_default/` layout. There is **no root `hugo.toml`** — adding one would shadow this directory. `themes/blowfish/` is a git submodule — never edit inside it; changes are lost on update. **`public/` is gitignored** and must stay that way — it\u0026#39;s a build artifact produced by CI, not source. Chaque règle porte sa raison. Ce n\u0026rsquo;est pas un détail : une règle expliquée se généralise. « Ne modifie jamais le thème » ne dit rien du cas où il faudrait surcharger un gabarit ; « le thème est un sous-module, les modifications sont perdues à la mise à jour » permet à l\u0026rsquo;agent de déduire qu\u0026rsquo;il faut copier le gabarit dans layouts/.\nLe traiter comme du code # Je fais générer la première version avec /init, qui analyse le dépôt, puis je l\u0026rsquo;élague. Une version générée et livrée sans relecture contient toujours des descriptions de fichiers que l\u0026rsquo;agent aurait trouvées seul. Ensuite, le fichier vit avec le projet : quand l\u0026rsquo;agent répète une erreur malgré une règle, c\u0026rsquo;est souvent que le fichier est trop long, pas qu\u0026rsquo;il faut crier plus fort. Et quand une fonctionnalité change, je mets le CLAUDE.md à jour dans le même commit.\nLa mémoire : ce que j\u0026rsquo;ai dit une fois # Claude Code garde aussi une mémoire entre les sessions : de courtes notes qu\u0026rsquo;il écrit lui-même quand je lui donne une consigne durable, ou quand il découvre un piège. Les miennes sont modestes et concrètes :\ntoujours me répondre en français, y compris pour les points d\u0026rsquo;étape ; le Hugo installé ici est un paquet snap : son /tmp est privé, donc un build de vérification écrit dans /tmp « réussit » sans rien produire de visible. Il faut construire ailleurs avant d\u0026rsquo;inspecter le HTML ; le serveur de développement ne re-rend pas toujours une page modifiée : on fait foi d\u0026rsquo;un vrai build, pas de l\u0026rsquo;aperçu. Les deux dernières sont nées d\u0026rsquo;erreurs réelles. Chacune m\u0026rsquo;a coûté du temps une fois ; la mémoire fait qu\u0026rsquo;elle ne le coûte pas deux fois. Je relis ces notes de temps en temps, et je supprime celles qui ne sont plus vraies : une mémoire fausse est pire qu\u0026rsquo;une absence de mémoire.\n4. La boucle : explorer, planifier, coder, vérifier, commiter # La documentation recommande quatre phases. J\u0026rsquo;en ajoute une, explicite, parce que c\u0026rsquo;est elle qui fait la différence.\n1 Explorer En mode plan (Maj+Tab), l\u0026rsquo;agent lit et répond, mais ne modifie rien. Je lui demande de comprendre avant d\u0026rsquo;agir : « lis src/backtest/ et explique-moi comment les fenêtres de test sont découpées ». Je découvre souvent à cette étape que ma demande reposait sur une hypothèse fausse. 2 Planifier Je fais écrire un plan détaillé : fichiers touchés, ordre des étapes, façon de vérifier. Je l\u0026rsquo;annote directement (Ctrl+G l\u0026rsquo;ouvre dans l\u0026rsquo;éditeur) avant toute ligne de code. Corriger un plan coûte une phrase ; corriger une implémentation partie dans la mauvaise direction coûte une session. 3 Coder L\u0026rsquo;agent implémente en suivant le plan. J\u0026rsquo;itère sur le diff, pas sur la description : « ligne 42, pourquoi une moyenne et pas une somme ? » est plus efficace que « le résultat me semble bizarre ». 4 Vérifier Tests, build, capture d\u0026rsquo;écran. C\u0026rsquo;est la section suivante, et c\u0026rsquo;est la plus importante. 5 Commiter L\u0026rsquo;agent propose ; je décide. Je relis le diff complet avant chaque commit, et un commit ne mélange jamais deux sujets. Sur ce blog, quand une modification de CSS touche à la fois le menu et la page Projets, ce sont deux commits. Quand je saute le plan # Le mode plan a un coût. La documentation donne un critère simple que j\u0026rsquo;ai adopté : si je peux décrire le diff en une phrase, je saute le plan. Renommer une variable, corriger une coquille, ajouter une ligne de log : je demande directement. Dès que la tâche touche plusieurs fichiers, que je ne connais pas le code, ou que je ne sais pas encore comment je m\u0026rsquo;y prendrais, je planifie.\n5. Vérifier : la seule étape non négociable # L\u0026rsquo;agent s\u0026rsquo;arrête quand le travail a l\u0026rsquo;air fini. Sans moyen de vérifier, « avoir l\u0026rsquo;air fini » est le seul signal disponible, et c\u0026rsquo;est moi qui deviens la boucle de vérification. La documentation le dit sans détour : si vous ne pouvez pas le vérifier, ne le livrez pas.\nDonner une vérification que l\u0026rsquo;agent peut lancer # Ma demande contient toujours le moyen de prouver qu\u0026rsquo;elle est remplie : un test, un build, un script qui compare une sortie à une référence. L\u0026rsquo;agent fait le travail, lance la vérification, lit le résultat, et itère jusqu\u0026rsquo;à ce qu\u0026rsquo;elle passe. Et je lui demande de montrer la preuve plutôt que d\u0026rsquo;affirmer : la sortie des tests, la commande lancée et son résultat. Relire une preuve est plus rapide que refaire la vérification.\nLe test d\u0026rsquo;abord, imposé explicitement # Par défaut, l\u0026rsquo;agent écrit l\u0026rsquo;implémentation, puis les tests qui la valident. Des tests écrits après le code ont une fâcheuse tendance à tester ce que le code fait, pas ce qu\u0026rsquo;il devrait faire. Pour une fonction qui compte, j\u0026rsquo;impose l\u0026rsquo;ordre :\n« écris un test qui échoue, n\u0026rsquo;implémente rien » ; je vérifie qu\u0026rsquo;il échoue pour la bonne raison ; « implémente le minimum pour le faire passer » ; je relis, puis on remanie en gardant le test au vert. Pour une métrique, cela donne des tests sur les cas qui fâchent : une série constante sur la période d\u0026rsquo;entraînement (dénominateur nul), une prévision parfaite, des valeurs manquantes. Pour une transformation, une propriété à préserver : des prévisions qui doivent sommer, une sortie qui doit rester positive.\nUn œil neuf pour relire # Un agent qui vient d\u0026rsquo;écrire du code est mal placé pour le critiquer : son contexte est rempli du raisonnement qui l\u0026rsquo;a produit. Pour une relecture sérieuse, j\u0026rsquo;utilise un contexte neuf, qui ne voit que le diff et les critères, et je lui demande de ne signaler que ce qui touche à la justesse. Sinon, un relecteur à qui l\u0026rsquo;on demande des défauts en trouve toujours. La section 8 montre comment l\u0026rsquo;organiser avec un sous-agent dédié.\nNe jamais se fier à un seul signal # L\u0026rsquo;exemple le plus parlant est tout récent, et il vient de ce blog. En ajoutant un encadré à un article, l\u0026rsquo;aperçu du serveur de développement ne le montrait pas. Le journal disait « source modifiée », la page servie restait l\u0026rsquo;ancienne. Un build de production, lui, contenait bien l\u0026rsquo;encadré. Le premier réflexe aurait été de « corriger » un problème qui n\u0026rsquo;existait pas.\nDepuis, la règle est en mémoire : le build fait foi, l\u0026rsquo;aperçu sert à regarder. Et pour l\u0026rsquo;interface, je demande à l\u0026rsquo;agent de vérifier lui-même dans un vrai navigateur, grâce à l\u0026rsquo;extension Chrome de Claude Code : il ouvre la page, clique sur le filtre, lit l\u0026rsquo;état du DOM et prend une capture. C\u0026rsquo;est ainsi qu\u0026rsquo;il a repéré un texte en gras presque blanc sur un fond jaune, illisible en mode sombre, que ni le build ni les tests n\u0026rsquo;auraient signalé.\nVérifier aussi ce qu\u0026rsquo;on m\u0026rsquo;annonce # La vérification ne porte pas que sur le code. Un plan, une réponse de l\u0026rsquo;agent ou un article contiennent des affirmations précises : d\u0026rsquo;où vient un écart, jusqu\u0026rsquo;à quand court un jeu de données, combien de fenêtres de test suffisent pour classer deux modèles. Chacune est une hypothèse, pas un fait. Je les vérifie une par une, et une affirmation fausse est souvent la découverte la plus intéressante du projet.\nC\u0026rsquo;est pour cela que mes README de projet ont une section « ce que je corrige par rapport au plan ». Elle garde la trace de ce qui était annoncé, de ce qui a été mesuré, et de l\u0026rsquo;écart entre les deux.\n6. Ce qui change en data science et en ML engineering # La plupart des conseils sur Claude Code viennent du développement logiciel. Là-bas, un code qui passe ses tests fait à peu près ce qu\u0026rsquo;on attend de lui. En data science, ce n\u0026rsquo;est pas vrai : un code peut être juste et le résultat faux. Le pipeline tourne, les tests passent, la métrique est excellente… parce qu\u0026rsquo;une information du futur s\u0026rsquo;est glissée dans l\u0026rsquo;entraînement.\nJe vérifie donc à trois niveaux, et l\u0026rsquo;agent doit les connaître tous les trois :\nNiveau La question Comment je le vérifie Le code Fait-il ce qu\u0026rsquo;il prétend ? Tests unitaires, cas limites, comparaison à une référence Les données Sont-elles ce que je crois ? Contrôles de qualité exécutés à chaque chargement La validité statistique Le résultat veut-il dire ce que j\u0026rsquo;en conclus ? Protocole fixé avant, baselines, variance entre fenêtres, relecture du statisticien Un agent est naturellement bon sur le premier niveau, correct sur le deuxième si on le lui demande, et faible sur le troisième. C\u0026rsquo;est là que se concentre mon attention.\nLes données avant les modèles # Un agent qui reçoit un jeu de données a envie de modéliser. Je lui demande d\u0026rsquo;abord de vérifier : trous, doublons, unités, bornes temporelles, cohérence des agrégats. Les données horaires, par exemple, portent souvent la trace du changement d\u0026rsquo;heure : des doublons au printemps, une heure manquante à l\u0026rsquo;automne. Une ligne absente ne lève aucune erreur ; elle fausse un total en silence. C\u0026rsquo;est le genre de défaut qu\u0026rsquo;un modèle avale sans broncher.\nMa demande type ne dit pas « fais une EDA ». Elle dit : « avant toute modélisation, liste ce qui pourrait rendre ces données trompeuses, et écris pour chaque point un contrôle exécutable. » Les contrôles deviennent des tests, lancés à chaque chargement, et non une cellule de notebook qu\u0026rsquo;on oublie de relancer :\ndef test_pas_de_trou_dans_la_grille_horaire(conso): attendu = pd.date_range(conso.ts.min(), conso.ts.max(), freq=\u0026#34;h\u0026#34;, tz=\u0026#34;UTC\u0026#34;) manquants = attendu.difference(conso.ts) assert manquants.empty, f\u0026#34;{len(manquants)} heures manquantes, ex. {manquants[:3].tolist()}\u0026#34; def test_le_total_vaut_la_somme_des_parties(conso, total): ecart = (conso.groupby(\u0026#34;ts\u0026#34;).valeur.sum() - total.set_index(\u0026#34;ts\u0026#34;).valeur).abs() assert ecart.max() \u0026lt; 1.0, f\u0026#34;écart max {ecart.max():.1f} le {ecart.idxmax()}\u0026#34; Le message d\u0026rsquo;erreur compte autant que l\u0026rsquo;assertion : quand le test échoue, l\u0026rsquo;agent doit pouvoir lire où et combien, pas seulement que.\nCe que je sais du métier va dans une skill de dictionnaire de données : le sens de chaque colonne, son unité, ses pièges connus (« les ventes à zéro pendant une rupture ne sont pas une demande nulle »). L\u0026rsquo;agent la charge quand il touche aux données, et je n\u0026rsquo;ai plus à le répéter.\nLes fuites, le piège numéro un # Une fuite, c\u0026rsquo;est une information disponible à l\u0026rsquo;entraînement qui ne le sera pas au moment de prévoir. Elle rend le modèle excellent en validation et médiocre en production. Un agent y tombe aussi facilement qu\u0026rsquo;un humain pressé, et pour la même raison : le code le plus court est souvent celui qui fuit.\nFuite Le code qui fuit Ce que je demande Temporelle train_test_split(X, y, shuffle=True) sur une série Un découpage par origine glissante Prétraitement StandardScaler().fit(X) avant le découpage Le prétraitement dans un Pipeline, ajusté sur le train seulement Imputation df.fillna(df.mean()) sur tout l\u0026rsquo;historique Une moyenne calculée sur le passé de chaque origine Cible Une variable calculée après l\u0026rsquo;événement (stock de fin de semaine) La date de disponibilité de chaque feature, documentée Groupe Le même client dans le train et le test Un découpage par groupe La règle est écrite dans le protocole et dans le CLAUDE.md, avec sa raison : tout ce qui est estimé doit l\u0026rsquo;être strictement avant chaque origine. Et elle a son test, que je fais écrire en premier :\ndef test_aucune_feature_ne_lit_le_futur(construire_features, historique): origine = pd.Timestamp(\u0026#34;2025-06-01\u0026#34;) avant = construire_features(historique, origine) # On falsifie tout ce qui suit l\u0026#39;origine : les features ne doivent pas bouger. falsifie = historique.assign( valeur=historique.valeur.where(historique.ts \u0026lt; origine, -999.0)) pd.testing.assert_frame_equal(avant, construire_features(falsifie, origine)) Ce test ne vérifie pas une implémentation particulière ; il vérifie une propriété. Il reste valable quand l\u0026rsquo;agent réécrit les features, et c\u0026rsquo;est exactement ce qu\u0026rsquo;il faut pour encadrer un collaborateur qui écrit vite.\nUne baseline, toujours, et avant le reste # Un agent propose volontiers le modèle le plus sophistiqué qu\u0026rsquo;il connaisse. Je lui impose l\u0026rsquo;ordre inverse : d\u0026rsquo;abord une baseline naïve, puis le modèle. Pour une série hebdomadaire, c\u0026rsquo;est « la même semaine l\u0026rsquo;an dernier » ; pour une classification, la classe majoritaire et une régression logistique. Si le modèle ne bat pas nettement la baseline, rien d\u0026rsquo;autre ne compte, et je l\u0026rsquo;ai appris avant d\u0026rsquo;avoir passé une journée sur les hyperparamètres.\nLes suggestions de l\u0026rsquo;agent sur ces hyperparamètres, justement, sont des plages de recherche, pas des recommandations. Le choix final se justifie par la validation, pas par l\u0026rsquo;aplomb de la réponse.\nÉvaluer comme on décidera # L\u0026rsquo;agent rapporte spontanément un chiffre : « MAPE de 8,3 % sur le jeu de test ». C\u0026rsquo;est presque toujours insuffisant. Je demande trois choses.\nLa bonne métrique, au bon niveau. Celle qui correspond à la décision : une erreur par produit si l\u0026rsquo;approvisionnement se décide par produit, une perte quantile si la décision porte sur un niveau de stock. Jamais une moyenne globale qui mélange tout. La dispersion, pas seulement la moyenne. Une métrique sur plusieurs fenêtres de test, avec son écart-type. Un modèle meilleur en moyenne mais sur deux fenêtres sur cinq n\u0026rsquo;est pas meilleur. Un test apparié quand je compare deux modèles sur les mêmes fenêtres, avec une correction si je fais beaucoup de comparaisons. Sans cela, j\u0026rsquo;ai toutes les chances de « découvrir » un gain qui n\u0026rsquo;est que du bruit. C\u0026rsquo;est typiquement le moment où je fais intervenir le sous-agent statisticien : avant de regarder les résultats, pour fixer le protocole ; après, pour me dire ce que je n\u0026rsquo;ai pas le droit d\u0026rsquo;en conclure.\nPrédire n\u0026rsquo;est pas expliquer # Un piège plus subtil guette les rapports. Un agent à qui l\u0026rsquo;on demande d\u0026rsquo;interpréter un modèle écrit volontiers « la température fait augmenter la demande de 12 % » à partir d\u0026rsquo;une importance de variable ou de valeurs SHAP. Ce sont des descriptions du modèle, pas du monde. Elles disent sur quoi le modèle s\u0026rsquo;appuie pour prévoir, pas ce qui se passerait si l\u0026rsquo;on intervenait.\nMa consigne est explicite : pas de vocabulaire causal dans un rapport sans stratégie d\u0026rsquo;identification. Pour mesurer l\u0026rsquo;effet d\u0026rsquo;une promotion, il faut un groupe de comparaison, une expérience ou une méthode quasi expérimentale, et c\u0026rsquo;est une autre question que la prévision. Un agent le sait très bien quand on le lui demande ; il l\u0026rsquo;oublie quand on ne le lui demande pas.\nLes chiffres viennent du code, jamais de la conversation # C\u0026rsquo;est une de mes règles de publication : un chiffre publié est la sortie d\u0026rsquo;un code exécuté et versionné. Jamais une valeur qu\u0026rsquo;un modèle de langage a « calculée » de tête dans une réponse, ni un pourcentage recopié d\u0026rsquo;un résumé. Concrètement, chaque tableau de résultats est produit par une commande (make report) que n\u0026rsquo;importe qui peut relancer, avec :\nles versions des bibliothèques épinglées dans uv.lock ; les graines aléatoires fixées et consignées ; l\u0026rsquo;empreinte (sha256) des données brutes dans un manifeste, pour savoir sur quoi le chiffre a été calculé. Le code dans un package, les notebooks pour expliquer # Un agent produit volontiers des notebooks de deux cents cellules où la logique est dupliquée. Je sépare : le code vit dans un seul package Python, testé ; les notebooks l\u0026rsquo;appellent et servent à expliquer. Une fonction d\u0026rsquo;estimation n\u0026rsquo;est écrite qu\u0026rsquo;une fois, et tous les notebooks qui en ont besoin l\u0026rsquo;importent. Les notebooks sont exécutés et versionnés avec leurs sorties, ce qui permet à l\u0026rsquo;agent de les lire, figures comprises.\nDes maths au code, puis à la référence # Traduire une équation d\u0026rsquo;article en code est l\u0026rsquo;une des tâches où l\u0026rsquo;agent fait gagner le plus de temps. C\u0026rsquo;est aussi l\u0026rsquo;une des plus dangereuses : une transposée oubliée donne un résultat plausible et faux. Ma règle : toute implémentation « de zéro » est confrontée à une implémentation de référence, avec un écart chiffré et expliqué. Si l\u0026rsquo;écart est exactement nul, je soupçonne une copie ; s\u0026rsquo;il est grand, je cherche l\u0026rsquo;erreur ; s\u0026rsquo;il est petit, je dois savoir dire d\u0026rsquo;où il vient. Quand il n\u0026rsquo;existe pas de référence, je teste les propriétés mathématiques : une projection appliquée deux fois ne change rien, une matrice de covariance est symétrique et positive.\nDu modèle au système # Côté ML engineering, la difficulté se déplace : le modèle marche, il faut qu\u0026rsquo;il continue à marcher. Trois points reviennent dans chaque projet.\nLe même code à l\u0026rsquo;entraînement et en production. Une feature calculée en pandas dans le notebook et réécrite en SQL pour la production finit toujours par diverger. Je demande à l\u0026rsquo;agent une seule implémentation, testée, appelée des deux côtés. Les versions de tout. Données, code, modèle et paramètres sont versionnés ensemble, et chaque run est tracé dans MLflow. Un chiffre en production doit pouvoir être rejoué. La surveillance. Dérive des entrées, dérive des erreurs, part de prévisions aberrantes. L\u0026rsquo;agent écrit les contrôles ; c\u0026rsquo;est moi qui fixe les seuils qui déclenchent une alerte. Pour les applications à base de LLM, la logique est la même, avec une difficulté en plus : la sortie n\u0026rsquo;est pas déterministe. Je constitue donc un jeu d\u0026rsquo;évaluation versionné, avec les cas difficiles, avant de toucher au prompt ; je versionne les prompts comme du code ; et quand un LLM sert de juge, je vérifie d\u0026rsquo;abord sur un échantillon annoté à la main qu\u0026rsquo;il juge comme moi.\n7. Étendre l\u0026rsquo;outil sans l\u0026rsquo;alourdir # Claude Code peut être étendu de plusieurs façons. Le risque est d\u0026rsquo;empiler des extensions dont chacune consomme du contexte en permanence. Ma règle : une extension doit répondre à un besoin qui revient. Si je fais une chose plus d\u0026rsquo;une fois par jour, elle mérite une automatisation ; sinon, une phrase dans la demande suffit.\nExtension Ce que c\u0026rsquo;est Quand je m\u0026rsquo;en sers CLAUDE.md Règles lues à chaque session Conventions permanentes du projet Skill Procédure chargée à la demande (SKILL.md) Savoir-faire occasionnel : un protocole de backtest, un dictionnaire de données Hook Script lancé automatiquement à un moment précis Ce qui doit arriver à chaque fois, sans exception Sous-agent Instance isolée, avec son propre contexte Recherche ou relecture, pour garder le bruit hors de ma session MCP Connecteur vers un outil externe Accès structuré à une base ou à un registre de modèles Les hooks, parce que le CLAUDE.md est consultatif # La différence est fondamentale : une instruction du CLAUDE.md est un conseil, que l\u0026rsquo;agent suit… la plupart du temps. Un hook est déterministe : c\u0026rsquo;est un script que Claude Code exécute lui-même à un point précis, par exemple après chaque modification de fichier, ou avant de rendre la main. Interdire l\u0026rsquo;écriture dans data/raw/, lancer le formateur après chaque édition, refuser de terminer tant que les tests échouent : ce sont des règles qui ne supportent pas d\u0026rsquo;exception, donc des hooks, pas des phrases.\nLes CLI avant les connecteurs # Pour parler aux services externes, je privilégie les outils en ligne de commande que l\u0026rsquo;agent sait déjà utiliser : gh pour GitHub, gcloud pour GCP. C\u0026rsquo;est la façon la plus économe en contexte. Un connecteur MCP vaut le coup quand l\u0026rsquo;accès structuré apporte vraiment quelque chose, par exemple interroger un registre MLflow. Pour une base de données, l\u0026rsquo;accès est en lecture seule : chaque requête est un vrai job, potentiellement facturé, lancé par un agent qui n\u0026rsquo;a pas la même prudence qu\u0026rsquo;un analyste.\nSans surveillance : le mode non interactif # Avec claude -p \u0026quot;…\u0026quot;, Claude Code tourne sans interface, par exemple dans la CI pour relire une pull request. Les précautions changent alors d\u0026rsquo;échelle : outils autorisés réduits au strict nécessaire (--allowedTools), clé dédiée et plafonnée, et jamais de permissions larges hors d\u0026rsquo;un conteneur isolé.\n8. Un exemple complet : prévoir la demande # Pour rendre tout cela concret, voici à quoi ressemble une configuration complète sur un cas typique : prévoir la demande hebdomadaire de 2 000 produits, avec un pipeline déployé sur GCP. C\u0026rsquo;est un exemple construit pour l\u0026rsquo;article. Il rassemble les pièces décrites plus haut, et montre comment elles s\u0026rsquo;articulent.\nLa session principale reçoit les règles, la mémoire et mes décisions ; elle charge des skills, déclenche des hooks, délègue à cinq sous-agents et appelle des outils externes. Le résultat passe par la vérification, la relecture, mon commit, puis la CI. Cliquer pour agrandir. Ce qui entoure la session # En entrée, trois sources : mes décisions (la question, le protocole), le CLAUDE.md du projet (conventions, commandes, règle anti-fuite temporelle) et la mémoire (les pièges déjà rencontrés). À gauche, les skills, chargées seulement quand la tâche les appelle : le protocole de backtest de l\u0026rsquo;équipe, le dictionnaire de données (ce que signifie chaque colonne, ses unités, ses pièges), et le gabarit du rapport de modèle. À droite, les hooks, qui s\u0026rsquo;exécutent sans exception : interdiction d\u0026rsquo;écrire dans data/raw/, formatage et tests ciblés après chaque modification, refus de rendre la main tant qu\u0026rsquo;un test échoue. En dessous, les outils : BigQuery en lecture seule, MLflow pour les runs et le registre de modèles, les CLI gh, gcloud et terraform, et Chrome pour regarder un dashboard. Cinq sous-agents, cinq rôles # Un sous-agent est un fichier Markdown dans .claude/agents/ : un nom, une description qui dit quand l\u0026rsquo;appeler, la liste des outils autorisés, le modèle à utiliser, et des instructions. Chacun travaille dans son propre contexte et ne renvoie qu\u0026rsquo;un résumé à la session principale.\nSous-agent Son rôle Ses outils explorateur-donnees Explorer et contrôler les données : trous, doublons, unités, ruptures de série Lecture seule, BigQuery ; un modèle rapide suffit statisticien Discuter les méthodes, les biais, la causalité et le protocole, et objecter Lecture seule ; il ne code pas ingenieur-ml Construire les features et les modèles, en écrivant d\u0026rsquo;abord le test qui échoue Édition, pytest, MLflow expert-mlops Concevoir le pipeline, le déploiement, le monitoring et l\u0026rsquo;infrastructure gcloud, terraform plan ; jamais apply relecteur Relire le diff dans un contexte neuf et signaler ce qui touche à la justesse Lecture seule, le diff et les critères Les outils sont le vrai garde-fou. Le statisticien n\u0026rsquo;a pas le droit d\u0026rsquo;éditer : son travail est d\u0026rsquo;argumenter, pas d\u0026rsquo;implémenter. L\u0026rsquo;expert MLOps peut planifier une modification d\u0026rsquo;infrastructure, jamais l\u0026rsquo;appliquer : terraform apply, c\u0026rsquo;est moi.\nLe statisticien, en détail # C\u0026rsquo;est le sous-agent qui apporte le plus, parce qu\u0026rsquo;il joue un rôle qu\u0026rsquo;un agent de code ne joue pas spontanément : celui du collègue qui demande « tu es sûr ? ». Sa définition tient en quelques lignes :\n--- name: statisticien description: Statisticien senior. À consulter avant de fixer un protocole, de choisir une métrique ou d\u0026#39;interpréter un résultat. Discute les méthodes, les biais et la causalité ; ne modifie jamais de fichier. tools: Read, Grep, Glob model: opus --- Tu es un statisticien senior, spécialiste des séries temporelles et de l\u0026#39;inférence causale. Ton rôle est d\u0026#39;objecter, pas d\u0026#39;approuver. Pour chaque proposition : 1. Nomme les hypothèses implicites (stationnarité, indépendance, absence de fuite temporelle, données manquantes au hasard…). 2. Cherche les biais : sélection, survie, censure, fuite, multiplicité des comparaisons. 3. Distingue ce qui est prédictif de ce qui est causal. Une corrélation ne justifie pas une décision d\u0026#39;intervention sans stratégie d\u0026#39;identification. 4. Propose le test ou l\u0026#39;expérience qui trancherait. Réponds par une liste d\u0026#39;objections classées par gravité, puis les questions à poser au métier. Si la proposition est solide, dis-le en une phrase. Sur notre cas, voici le genre d\u0026rsquo;objection qu\u0026rsquo;il soulève dès le premier tour. Les ventes observées ne sont pas la demande : quand un produit est en rupture de stock, les ventes sont censurées, et un modèle entraîné dessus apprend à sous-prévoir précisément les produits qui manquent le plus. Il demande aussi si l\u0026rsquo;effet des promotions doit être prévu ou mesuré : le premier cas est un problème de prévision ; le second est une question causale, qui demande un groupe de comparaison, pas un meilleur modèle.\nUn tour de boucle # 1 Explorer L\u0026rsquo;explorateur interroge BigQuery en lecture seule et rapporte : 4 % de semaines à zéro vente, dont une partie coïncide avec des ruptures de stock ; des changements de codes produit en cours d\u0026rsquo;historique. 2 Contester Le statisticien lit ce rapport et le plan. Il soulève la censure de la demande, la fuite possible via une variable de stock calculée après coup, et propose une validation à fenêtres glissantes plutôt qu\u0026rsquo;un découpage aléatoire. 3 Trancher Moi. Je fixe le protocole dans un YAML versionné : métrique, fenêtres, baselines, traitement des ruptures. La skill protocole-backtest en donne la structure ; les décisions sont les miennes. 4 Construire L\u0026rsquo;ingénieur ML écrit d\u0026rsquo;abord le test qui échoue (« aucune feature ne lit une date postérieure à l\u0026rsquo;origine »), puis l\u0026rsquo;implémentation. Après chaque modification, le hook lance le formateur et les tests concernés ; les runs partent dans MLflow. 5 Industrialiser L\u0026rsquo;expert MLOps conçoit le pipeline d\u0026rsquo;entraînement planifié, le suivi de dérive et le module Terraform. Il produit un terraform plan que je lis avant de l\u0026rsquo;appliquer moi-même. 6 Relire et livrer Le relecteur reçoit le diff et les critères, sans le raisonnement qui l\u0026rsquo;a produit. Le hook Stop refuse de rendre la main tant qu\u0026rsquo;un test échoue. Je relis le diff, je commite, et la CI relance une revue automatique de la pull request avec claude -p. Pourquoi découper ainsi # Trois raisons. Le contexte, d\u0026rsquo;abord : l\u0026rsquo;exploration des données et la relecture lisent beaucoup de fichiers ; dans des sous-agents, ce bruit ne remplit pas la session principale. Le désaccord, ensuite : un statisticien qui objecte et un ingénieur qui implémente, dans deux contextes séparés, reproduisent la tension utile d\u0026rsquo;une vraie équipe. Un seul agent qui fait les deux a tendance à valider ses propres choix. Le moindre privilège, enfin : chaque rôle n\u0026rsquo;a que les outils dont il a besoin.\nCe découpage a un coût : chaque sous-agent consomme ses propres tokens, et coordonner cinq rôles demande plus d\u0026rsquo;attention qu\u0026rsquo;une session unique. Je ne le recommanderais pas d\u0026rsquo;emblée. On commence par une session et un CLAUDE.md ; on ajoute un sous-agent quand un rôle revient souvent, en commençant généralement par le relecteur ou le statisticien.\n9. Ce que je ne délègue pas # L\u0026rsquo;agent fait beaucoup. Certaines choses restent à moi, par principe.\nLe choix du sujet et de la question. Savoir quelle question vaut la peine d\u0026rsquo;être posée, c\u0026rsquo;est le métier. Le protocole. Métriques, découpage, baselines : fixés avant les résultats, par écrit. L\u0026rsquo;interprétation. Une métrique qui s\u0026rsquo;améliore de 2 % sur quatre fenêtres de test n\u0026rsquo;est pas un résultat ; c\u0026rsquo;est une hypothèse à tester. L\u0026rsquo;agent produit les chiffres ; leur sens est ma responsabilité. Les opérations irréversibles. Suppressions, réécritures d\u0026rsquo;historique, publication : l\u0026rsquo;agent les prépare, je les déclenche. Les fichiers irremplaçables sont commités ou sauvegardés avant qu\u0026rsquo;il y ait accès. La publication. Rien ne sort sur ce blog sans que je l\u0026rsquo;aie relu et que je sache l\u0026rsquo;expliquer. Ces règles sont publiques, sur la page Comment j\u0026rsquo;utilise l\u0026rsquo;IA. Les écrire m\u0026rsquo;oblige à les tenir.\n10. Les signaux qui me font changer de cap # Avec le temps, j\u0026rsquo;ai appris à reconnaître quelques signaux. Chacun appelle un geste précis.\nSignal Ce que je fais L\u0026rsquo;agent ignore une règle du CLAUDE.md J\u0026rsquo;élague le fichier, au lieu d\u0026rsquo;ajouter des majuscules Deux corrections ratées sur le même point /clear, et un meilleur premier message L\u0026rsquo;agent lit des dizaines de fichiers « pour comprendre » Je resserre la question, ou je délègue à un sous-agent Le résultat est « fini » sans preuve Je demande la sortie des tests ou la capture Un écart exactement nul avec une référence Je cherche la copie Un chiffre surprenant, dans le bon sens Je cherche la fuite avant de m\u0026rsquo;en réjouir Je fais la même demande pour la troisième fois Elle devient une règle, une skill ou un hook Conclusion # Claude Code n\u0026rsquo;a pas remplacé ma façon de travailler : il l\u0026rsquo;a rendue plus exigeante. Quand écrire du code ne coûte presque plus rien, la valeur se déplace vers ce qui coûte toujours : poser la bonne question, fixer le protocole avant de regarder, et prouver que le résultat est juste.\nSi je ne devais garder qu\u0026rsquo;une règle, ce serait celle-ci : rien n\u0026rsquo;est fini sans preuve. Le reste, la gestion du contexte, le CLAUDE.md court, le plan avant le code, sert à produire des preuves plus vite.\nLes outils changent chaque semaine ; cette méthode, je la fais évoluer avec eux. Ce qui ne change pas, c\u0026rsquo;est qu\u0026rsquo;à la fin, le code est à mon nom.\nPour aller plus loin\nBest practices for Claude Code, la documentation officielle, mise à jour en continu. Writing a good CLAUDE.md, HumanLayer : le budget d\u0026rsquo;instructions et la structure quoi / pourquoi / comment. Not all AI-assisted programming is vibe coding, Simon Willison, mars 2025. Augmented Coding: Beyond the Vibes, Kent Beck, juin 2025. ","date":"29 septembre 2026","externalUrl":null,"permalink":"/posts/claude-code-data-science/","section":"Articles \u0026 veille","summary":"Un agent qui code vite ne remplace pas la méthode. Ma façon de travailler avec Claude Code : le contexte comme ressource rare, la vérification comme seule étape non négociable.","title":"Claude Code en data science et ML-AI Engineering : ma méthode de travail","type":"posts"},{"content":"Medley entre le vert fertile tropical et le noir volcanique du sable. Quatorze battements de tambour, un pour chaque année d\u0026rsquo;absence. Retour à la terre de ma naissance, pour y repartir armé de sa bénédiction.\n","date":"20 janvier 2026","externalUrl":null,"permalink":"/migration/martinique-anse-ceron-2026/","section":"Migration","summary":"","title":"Habitation \u0026 plage Anse Céron","type":"migration"},{"content":"Je n\u0026rsquo;ai jamais vraiment aimé la Guinness. Et c\u0026rsquo;est bien là une des erreurs notables de ma vie. Ma dette morale pour ce préjudice fut payée à la source.\n","date":"22 mai 2024","externalUrl":null,"permalink":"/migration/guinness-2024/","section":"Migration","summary":"","title":"Guinness city","type":"migration"},{"content":"Sur les sentiers sacrés du mont Kurama : une ascension hors du temps, guidée par la sagesse des cèdres millénaires, l\u0026rsquo;endurance d\u0026rsquo;une amitié et l\u0026rsquo;empreinte invisible du reiki.\n","date":"12 avril 2019","externalUrl":null,"permalink":"/migration/japon-kuramadera-2019/","section":"Migration","summary":"","title":"Kurama dera","type":"migration"},{"content":"Data scientist passionné de jazz, j\u0026rsquo;ai un goût prononcé pour les statistiques. Spécialisé en séries temporelles et inférence causale, j\u0026rsquo;ai aussi une solide expérience en LLM et en orchestration d\u0026rsquo;agents IA. Je m\u0026rsquo;intéresse à tous les terrains où mon métier peut s\u0026rsquo;appliquer.\nCe qui m\u0026rsquo;anime, c\u0026rsquo;est de construire des modèles et des systèmes qui aident à mieux comprendre comment les choses fonctionnent et qui facilitent la prise de meilleures décisions. Toujours partant pour une bonne énigme statistique et soucieux du détail, je m\u0026rsquo;applique depuis 2014 à développer des systèmes autonomes qui prédisent et simulent des phénomènes complexes avec le moins de biais possible.\nSi j\u0026#39;étais… un animal je serais Un canard🦆 pourquoi ? parce que Sur terre, sur l’eau ou dans les airs, il est à l’aise partout sans dépendre d’aucun terrain. Il m’inspire par son adaptabilité et sa grande résilience, malgré son allure parfois nonchalante.🦆 une musique je serais So WhatMiles Davis🎺 pourquoi ? parce que La basse de Paul Chambers pose une question, les cuivres répondent « so what ». Tout Kind of Blue tient dans cet échange : de l’espace, peu de notes et une profonde liberté.🎺 une boisson je serais Ti\u0026#39;punch pourquoi ? parce que Selon ma tradition, chacun dose lui-même sa propre mort en ajustant comme bon lui semble les 3 ingrédients. C’est là le meilleur symbole de tolérance et d’ouverture que je connaisse. un élément je serais L\u0026#39;air🌬️ pourquoi ? parce que Sans lui, on suffoquerait en quelques instants ; pourtant, on pense plus volontiers à l’eau comme essentielle. C’est la force tranquille et discrète.🌬️ un plat je serais Une pizza🍕 pourquoi ? parce que Il y a autant de recettes possibles que l’imagination nous le permet, et tout le monde a une idée précise de ce qu’est une bonne pizza. Je me reconnais dans cette diversité.🍕 un livre je serais EssaisMichel de Montaigne📖 pourquoi ? parce que Douter de soi (« que sais-je ? »), se méfier de ses certitudes (« chacun appelle barbarie ce qui n’est pas de son usage ») et se reconnaître en chacun (« chaque homme porte la forme entière de l’humaine condition »).📖 un arbre je serais Un cocotier🌴 pourquoi ? parce que Élancé et perché en hauteur, le cocotier surplombe le paysage sans jamais imposer d’ombre étouffante.🌴 une pièce d\u0026#39;échiquier je serais Un fou pourquoi ? parce que Il ne marche pas en ligne droite, n’a pas une force brute, il attend son heure quand le jeu est fermé, mais il est redoutable dans les positions ouvertes. un moment de la journée je serais L\u0026#39;aube🌅 pourquoi ? parce que C’est un moment de sérénité absolue, mais qui porte en lui toute l’énergie de la journée à venir.🌅 ","externalUrl":null,"permalink":"/about/","section":"Accueil","summary":"","title":"À propos","type":"page"},{"content":"J\u0026rsquo;écris ici ce que j\u0026rsquo;apprends : notes de veille, lectures de papiers, et les projets que je construis pour comprendre les choses en les faisant.\n","externalUrl":null,"permalink":"/","section":"Accueil","summary":"","title":"Accueil","type":"page"},{"content":"Mes notes techniques : ce que je lis, ce que je teste, ce qui marche et ce qui ne marche pas.\n","externalUrl":null,"permalink":"/posts/","section":"Articles \u0026 veille","summary":"","title":"Articles \u0026 veille","type":"posts"},{"content":"Data Scientist et ML Engineer expérimenté, avec un solide background en statistique. Cette page retrace mon parcours : expériences salariées, freelance et bénévoles, formation et certifications.\nExpérience # Salarié5 Freelance1 Bénévole1 Salarié déc. 2023 – aujourd\u0026#39;hui3 ans Data Scientist \u0026amp; ML Engineer Orange · Rennes En poste Je conçois et industrialise les modèles de prévision qui aident Orange à anticiper la charge de ses techniciens sur tout le réseau télécom français, et j\u0026#39;explore les agents IA pour automatiser le back-office.\nPrévision à l'échelle nationale : modèles de séries temporelles qui anticipent les flux d'activité des techniciens sur l'ensemble des domaines du réseau, pour mieux dimensionner les équipes. IA générative (R\u0026amp;D) : étude de faisabilité d'architectures d'agents IA et de LLM pour automatiser des processus métier et accélérer la prise de décision. MLOps sur GCP : pipelines automatisés d'ingestion, de prétraitement et d'entraînement, avec versionnage, suivi de performance et déploiement continu. Référent Data Science : appui technique de l'équipe, encadrement de stagiaires et d'alternants. PythonSéries temporellesLLM \u0026amp; agentsGCPAirflowCI/CDGitLabDockerLinux janv. 2021 – nov. 20232 ans 11 mois Data Scientist \u0026amp; MLOps BPCE-SI · Aix-en-Provence Au cœur de l\u0026#39;informatique du groupe BPCE, j\u0026#39;ai fait passer des cas d\u0026#39;usage marketing du prototype à la production, et contribué à l\u0026#39;outillage qui standardise ce passage pour tout le groupe.\nConnaissance client : segmentation avancée, prédiction de l'attrition, analyse de sentiment des retours clients et moteur de recommandation. Moments de vie : modèles qui prédisent les étapes clés du parcours client (retraite, premier emploi, déménagement), déployés en production pour proposer le bon conseil au bon moment. IA générative : mise en œuvre de méthodes d'évaluation d'un agent conversationnel fondé sur le RAG, pour mesurer la pertinence et la fiabilité de ses réponses. Librairie MLOps interne : contribution à l'outil Python qui automatise et standardise le packaging et le déploiement des modèles de machine learning du groupe. Industrialisation et cloud : CI/CD, monitoring en production et migration des cas d'usage vers GCP (BigQuery, Vertex AI), main dans la main avec les Data Engineers et DataOps. Agilité : projets menés en Scrum, avec des restitutions régulières aux parties prenantes. PythonPySparkSéries temporellesLLMBigQueryVertex AIAirflowMLOpsCI/CDNLPScrumLinux juil. 2019 – déc. 20201 an 6 mois Data Scientist \u0026amp; IoT Analytics Traxens · Marseille Chez ce spécialiste du conteneur connecté, j\u0026#39;ai transformé les données de capteurs IoT en outils de pilotage pour la logistique maritime : maintenance, chaîne du froid et sécurité.\nMaintenance prédictive : estimation de l'autonomie restante des batteries des balises de géolocalisation, pour planifier les interventions et gérer le cycle de vie du parc. Chaîne du froid : prévision de la température des conteneurs frigorifiques, au cœur des systèmes d'alerte. Sécurité : algorithmes de détection d'ouverture de porte à partir des signaux capteurs. Vision : traitement et classification d'images satellites pour annoter les zones utiles au transport maritime. Qualité des données : gouvernance, monitoring et fiabilisation des pipelines d'analyse et de modélisation. Collaboration avec la recherche : travaux menés avec un laboratoire de recherche sur le nettoyage automatique des données capteurs par apprentissage par renforcement. Référent Data Science et statistique : appui méthodologique de l'équipe sur la modélisation et l'analyse des données. PythonIoTSéries temporellesVisionMaintenance prédictiveReinforcement learningLinux oct. 2017 – juin 20191 an 9 mois Data Scientist IT-CE · Marseille Pour l\u0026#39;informatique des Caisses d\u0026#39;Épargne, j\u0026#39;ai mis le machine learning au service du crédit, de la lutte contre la fraude, de la fidélisation des clients et du traitement automatique des documents.\nLutte contre la fraude : modèle non supervisé de détection d'anomalies pour repérer les chèques frauduleux et mieux protéger les clients. Scoring crédit : construction d'un modèle de scoring pour déterminer le taux des crédits proposés aux clients. Bénéficiaires d'assurance vie : identification automatique des bénéficiaires à partir de documents hétérogènes (PDF, courriers manuscrits numérisés, verbatims), en combinant reconnaissance d'entités nommées (NER) et vision par ordinateur. Rétention : modèles de prédiction de l'attrition pour les jeunes, les professionnels et les entreprises, socle de stratégies de fidélisation ciblées. Data Lake : ingestion et structuration des données avec PySpark pour centraliser l'analyse et le déploiement des modèles. Aide à la décision : tableaux de bord interactifs qui traduisent les modèles en plans d'action pour les métiers. PythonPySparkSQLNERVisionScoring créditDétection d\u0026#39;anomaliesChurnData LakeLinux sept. 2014 – sept. 20173 ans 1 mois Biostatisticien INSERM · Paris En recherche médicale, j\u0026#39;ai appliqué la statistique à la génomique et à l\u0026#39;épidémiologie : c\u0026#39;est là que s\u0026#39;est forgée ma rigueur méthodologique.\nGénomique : analyses inférentielles et classification de profils génomiques pour révéler des sous-groupes de patients biologiquement pertinents. Grande dimension : sélection de gènes candidats parmi des milliers de variables, par régression pénalisée (lasso) et contrôle du taux de fausses découvertes (FDR). Oncologie : modélisation de l'impact clinique de biomarqueurs sur la progression de la leucémie, par des méthodes multivariées. Analyse de survie : modèles de Cox et courbes de Kaplan-Meier pour mesurer la valeur pronostique de ces biomarqueurs sur la survie sans progression. Épidémiologie : modèle de propagation de la dengue intégrant des facteurs biologiques et environnementaux pour simuler la dynamique des foyers. Inférence causale : estimation de l'effet des actions de lutte antivectorielle sur l'incidence de la dengue, en explicitant les facteurs de confusion par des graphes causaux (DAG) et en les corrigeant par pondération sur le score de propension. RStatistique multivariéeInférence causaleAnalyse de survieGénomiqueÉpidémiologieModélisationLinux Freelance août 2021 – aujourd\u0026#39;hui5 ans 4 mois Mentor \u0026amp; Formateur OpenClassrooms · À distance Mission en cours J\u0026#39;accompagne et forme des apprenants des parcours Data Scientist Machine Learning, Data Engineer et AI Engineer, projet après projet, du cadrage jusqu\u0026#39;au déploiement.\n18 apprenants accompagnés et formés tout au long de leur parcours, en mentorat individuel. Machine learning et MLOps : classification de textes et d'images, déploiement de modèles derrière une API, suivi d'expériences et pipelines CI/CD. IA générative : systèmes RAG, évaluation de LLM, agents IA et fine-tuning. Data engineering : bases SQL et NoSQL, pipelines d'ingestion et d'orchestration, infrastructures cloud. PythonSQLscikit-learnMLflowFastAPIStreamlitLangChainPySparkKafkaKestraCI/CDPédagogie Bénévole févr. 2019 – juin 20195 mois Data Scientist Data For Good Au sein de ce collectif de bénévoles, j\u0026#39;ai travaillé sur le projet Open Food Facts, la base de données ouverte des produits alimentaires : analyse statistique, prédiction du Nutri-Score et qualité des données.\nAnalyse et rapport statistique sur les données du projet Open Food Facts. Prédiction du Nutri-Score : modèle qui l'estime pour les produits qui n'en ont pas encore. Qualité des données : audit de la base, puis amélioration de sa qualité. PythonpandasNumPyscikit-learnstatsmodelsFastAPIQualité des données Formation # Organisation Diplôme Aix-Marseille Université Marseille Master en Biostatistique Université des Antilles Martinique Licence Mathématiques, Informatique et Biologie Certifications # Une certification ne remplace pas l\u0026rsquo;expérience en production, mais elle en consolide les bases. C\u0026rsquo;est aussi ma façon de continuer à apprendre, et de vérifier que ce que je sais tient face à un examen exigeant.\nProfessional Machine Learning Engineer Google Cloud2025 – 2027 2025 – 2027 Compétences # Catégorie Compétences / technos Langages Python, R, SQL, Bash, Rust, TypeScript Séries temporelles prévision, pricing, pattern mining, détection d\u0026#39;anomalies Statistique inférence causale, A/B testing, inférence bayésienne, analyse de survie, statistique multivariée, régression pénalisée Machine learning gradient boosting, deep learning, clustering, systèmes de recommandation, vision par ordinateur, apprentissage par renforcement IA générative LLM, agents IA, RAG Frameworks \u0026amp; outils scikit-learn, PyTorch, statsmodels, LangGraph, MLflow, dbt, Airflow, PySpark, BigQuery Cloud \u0026amp; MLOps GCP, Vertex AI, Docker, Terraform, Git, CI/CD, Linux ","externalUrl":null,"permalink":"/cv/","section":"Accueil","summary":"","title":"CV","type":"page"},{"content":"Je note ici ce que j\u0026rsquo;écoute, ce que j\u0026rsquo;apprends et ce que j\u0026rsquo;explore : des albums et des musiciens découverts, des progrès (et des galères) à l\u0026rsquo;instrument, des détours par l\u0026rsquo;histoire et la théorie.\n","externalUrl":null,"permalink":"/jazz/","section":"Jazz","summary":"","title":"Jazz","type":"jazz"},{"content":"Et, tout comme les canards… il m\u0026rsquo;arrive parfois de migrer autour du globe.\n","externalUrl":null,"permalink":"/migration/","section":"Migration","summary":"","title":"Migration","type":"migration"},{"content":"","externalUrl":null,"permalink":"/jazz/pantheon/","section":"Jazz","summary":"","title":"Panthéon","type":"jazz"},{"content":"Les projets que je construis en dehors du boulot, pour apprendre ou pour répondre à une question qui m\u0026rsquo;intriguait. Code disponible pour chacun.\n","externalUrl":null,"permalink":"/projects/","section":"Projets","summary":"","title":"Projets","type":"projects"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"}]