[EF-07] Entraînement du modèle et promotion #36

Closed
opened 2026-09-01 10:35:25 +00:00 by lenaic · 2 comments
Owner

Exigence couverte

EF-07, ENF-03

Épreuve servie

EC06 · IA et automatisation

Charge estimée

3 j.h

Ce qu'on veut obtenir

Prévoir la consommation de l'heure suivante par site, avec une référence de comparaison publiée pour que le gain du modèle soit chiffré et non affirmé.

Critères d'acceptation

  • L'entraînement s'exécute en local sur le serveur de la salle, jamais dans le cloud.
  • Une référence naïve est publiée, et l'erreur du modèle lui est comparée site par site.
  • Le modèle retenu est enregistré dans MLflow avec ses paramètres et sa métrique, puis promu.
  • L'entraînement est rejouable depuis le dépôt sur les mêmes données et donne le même résultat.

Comment on le vérifie

Tests tests/unit/model/
Commande la commande d'entraînement documentée, puis lecture de l'exécution dans MLflow
Preuve capture de la comparaison modèle contre référence, identifiant de l'exécution MLflow et version portant l'alias production

Hors périmètre

Le service qui sert les prédictions en ligne, et la surveillance de dérive.

Le calcul GPU est sorti du périmètre le 07/09

Le premier critère demandait le Tesla T4, avec la sortie de nvidia-smi pendant l'entraînement comme preuve. Il ne le demande plus : le modèle retenu est HistGradientBoostingRegressor de scikit-learn, qui n'a aucun support GPU, conformément à l'ADR 0012 — « le T4 n'est pas requis, ni pour l'entraînement ni pour l'inférence ».

Le motif est celui qu'EXIGENCES-collectives.md §1 donne déjà : « le facteur limitant est l'historique disponible, pas la puissance de calcul ». Sept sites, un point par heure, quelques semaines d'historique : quelques milliers de lignes, où un GPU n'apporte rien de mesurable.

ENF-03 est inchangée : l'entraînement reste local, sur le serveur de la salle, et la mesure brute ne sort pas du réseau.

Écart à connaître : EXIGENCES-collectives.md §1 porte « Entraînement du modèle — en local, sur le Tesla T4 du serveur — arbitré le 01/09 ». La ligne se lit désormais « en local, sur le serveur qui porte le T4 », lecture que l'ADR 0012 retient. Le fichier collectif n'a pas été modifié : c'est au groupe de le faire, pas à ce ticket.

### Exigence couverte EF-07, ENF-03 ### Épreuve servie EC06 · IA et automatisation ### Charge estimée 3 j.h ### Ce qu'on veut obtenir Prévoir la consommation de l'heure suivante par site, avec une référence de comparaison publiée pour que le gain du modèle soit chiffré et non affirmé. ### Critères d'acceptation - [ ] L'entraînement s'exécute en local sur le serveur de la salle, jamais dans le cloud. - [ ] Une référence naïve est publiée, et l'erreur du modèle lui est comparée site par site. - [ ] Le modèle retenu est enregistré dans MLflow avec ses paramètres et sa métrique, puis promu. - [ ] L'entraînement est rejouable depuis le dépôt sur les mêmes données et donne le même résultat. ### Comment on le vérifie Tests tests/unit/model/ Commande la commande d'entraînement documentée, puis lecture de l'exécution dans MLflow Preuve capture de la comparaison modèle contre référence, identifiant de l'exécution MLflow et version portant l'alias `production` ### Hors périmètre Le service qui sert les prédictions en ligne, et la surveillance de dérive. ### Le calcul GPU est sorti du périmètre le 07/09 Le premier critère demandait le **Tesla T4**, avec la sortie de `nvidia-smi` pendant l'entraînement comme preuve. Il ne le demande plus : le modèle retenu est `HistGradientBoostingRegressor` de scikit-learn, qui n'a aucun support GPU, conformément à l'[ADR 0012](../src/branch/develop/docs/adr/0012-prevision-h1-avec-reference-publiee.md) — « le T4 n'est pas requis, ni pour l'entraînement ni pour l'inférence ». Le motif est celui qu'`EXIGENCES-collectives.md` §1 donne déjà : « le facteur limitant est l'historique disponible, pas la puissance de calcul ». Sept sites, un point par heure, quelques semaines d'historique : quelques milliers de lignes, où un GPU n'apporte rien de mesurable. **ENF-03 est inchangée** : l'entraînement reste local, sur le serveur de la salle, et la mesure brute ne sort pas du réseau. **Écart à connaître** : `EXIGENCES-collectives.md` §1 porte « Entraînement du modèle — en local, sur le Tesla T4 du serveur — arbitré le 01/09 ». La ligne se lit désormais « en local, sur le serveur qui porte le T4 », lecture que l'ADR 0012 retient. Le fichier collectif n'a pas été modifié : c'est au groupe de le faire, pas à ce ticket.
florian added this to the EnerVision project 2026-09-01 12:10:53 +00:00
gabriel added the due date 2026-09-11 2026-09-03 12:41:01 +00:00
olivier self-assigned this 2026-09-04 13:50:57 +00:00
olivier changed title from [EF-07] Entraînement du modèle sur le T4 et promotion to [EF-07] Entraînement du modèle et promotion 2026-09-07 08:01:01 +00:00
Member

Premier entraînement joué et modèle promu — 07/09, sur enervision_preprod

Branche olivier/36-entrainement-modele, paquet services/inference/model/, tests tests/unit/model/ (95 tests, 1 sauté hors chaîne).

Le calcul GPU est sorti du périmètre : HistGradientBoostingRegressor, conformément à l'ADR 0012. Le ticket a été modifié en conséquence (titre, premier critère, preuve attendue). L'écart avec EXIGENCES-collectives.md §1 — « en local, sur le Tesla T4 du serveur, arbitré le 01/09 » — est consigné dans la description : la ligne se lit désormais « en local, sur le serveur qui porte le T4 », et le fichier collectif n'a pas été modifié par ce ticket.

Comparaison sur les 504 heures de test — critère 2

                  MAE kW     RMSE kW      MAPE %
modèle             2.018       2.986        0.66
persistance       21.443      27.104        6.60
saisonnier         8.467      24.742        4.54

site              modèle   persistance       écart
SITE001            0.815         7.297       6.481
SITE002            3.772        36.217      32.444
SITE003            2.499        29.196      26.697
SITE004            1.615        14.533      12.918
SITE005            1.638        21.834      20.195
SITE006            0.774         6.526       5.751
SITE007            3.011        34.499      31.487

À lire avec la réserve que l'ADR 0012 demande d'écrire plutôt que de cacher : la source est l'API de simulation, et un écart de dix contre un face à la persistance dit d'abord que ces séries sont très régulières. Le chiffre est honnête — découpage chronologique, aucune statistique calculée avant la coupure, aucune entrée prise sur l'instant prévu — mais il ne se transposerait pas tel quel sur un parc réel. C'est ce qu'il faudra dire au jury.

Le registre — critère 3

Modèle enervision-prevision-h1
Version promue 3, alias production
Exécution 382e4058b234451c915af32454a1b126, expérience prevision-h1
Base apprise enervision_preprod, fenêtre 2026-07-30 → 2026-09-04
Découpage 5 215 exemples d'apprentissage, 504 de test, coupure au 2026-08-31T08:00
Écartés 7 heures pour cible fragile, 161 pour retard manquant
Signature (-1, 33) — 24 retards, heure, jour de semaine, capacité, taux de charge précédent, 5 indicatrices de type

models:/enervision-prevision-h1@production rend un PyFuncModel : c'est la seule commande de docs/runbooks/mlflow.md §5 qui n'avait jamais pu être jouée, faute de modèle à charger. Elle l'est. C'est aussi la couture que le #37 empruntera — model.registre.charger.

Rejouabilité — critère 4

Trois exécutions successives sur les mêmes bornes, MAE identique au millième (2,018 kW). Graine fixée et journalisée, ordre des exemples trié, coupure calculée sur le dernier instant des données et non sur l'heure courante, et surtout early_stopping=False : laissé sur « auto », scikit-learn met de côté 10 % des exemples tirés au hasard dès dix mille lignes — à la fois une fuite sur une série temporelle et un découpage qui n'est pas le nôtre.

entrainer.sh --deux-passes enchaîne deux entraînements sur des bornes figées, sans rien publier, et compare les deux tableaux.

Ce qui reste, et qui n'est pas du code

Le critère 1 n'est pas encore prouvé sur la production. public.mesure y est vide : le chargement de la zone or n'est pas planifié, c'est la branche lenaic/136-zone-or-en-cron (#136) qui le fait, et elle n'est pas fusionnée. La préproduction porte les 35 jours de données réelles, d'où ce premier entraînement là-bas.

Le lanceur choisit sa base à l'environnement de lancement — ENERVISION_MODEL_ENVIRONNEMENT, ou --environnement prod|preprod —, avec prod par défaut. Un garde-fou refuse la promotion hors enervision_prod sauf --promouvoir-hors-prod : l'alias production est ce que le service du #37 chargera au démarrage, et un modèle appris ailleurs qui le prendrait servirait des prévisions apprises sur d'autres données. C'est le nom de base relu dans l'URL qui décide, jamais le réglage — un réglage peut mentir. Il entre aussi dans l'étiquette promu_motif, seule mémoire du geste puisque le registre n'est pas versionné dans git.

Donc, dès que #136 est fusionnée et déployée : entrainer.sh --depuis ... sans autre option vise la production, trouve son URL dans ENERVISION_ETL_DATABASE_URL que le rôle Ansible app pose, et promeut sans garde-fou à lever.

Les trois versions au registre sont la trace de deux correctifs trouvés par l'exécution elle-même : MLflow 3 ne range plus un modèle sous les artefacts de son exécution, et la signature devait venir du même tableau que l'exemple d'entrée. Aucune n'est supprimée, comme le manuel §5 le demande.

### Premier entraînement joué et modèle promu — 07/09, sur `enervision_preprod` Branche `olivier/36-entrainement-modele`, paquet `services/inference/model/`, tests `tests/unit/model/` (95 tests, 1 sauté hors chaîne). **Le calcul GPU est sorti du périmètre** : `HistGradientBoostingRegressor`, conformément à l'ADR 0012. Le ticket a été modifié en conséquence (titre, premier critère, preuve attendue). L'écart avec `EXIGENCES-collectives.md` §1 — « en local, sur le Tesla T4 du serveur, arbitré le 01/09 » — est consigné dans la description : la ligne se lit désormais « en local, sur le serveur qui porte le T4 », et le fichier collectif n'a pas été modifié par ce ticket. #### Comparaison sur les 504 heures de test — critère 2 ``` MAE kW RMSE kW MAPE % modèle 2.018 2.986 0.66 persistance 21.443 27.104 6.60 saisonnier 8.467 24.742 4.54 site modèle persistance écart SITE001 0.815 7.297 6.481 SITE002 3.772 36.217 32.444 SITE003 2.499 29.196 26.697 SITE004 1.615 14.533 12.918 SITE005 1.638 21.834 20.195 SITE006 0.774 6.526 5.751 SITE007 3.011 34.499 31.487 ``` **À lire avec la réserve que l'ADR 0012 demande d'écrire plutôt que de cacher** : la source est l'API de simulation, et un écart de dix contre un face à la persistance dit d'abord que ces séries sont très régulières. Le chiffre est honnête — découpage chronologique, aucune statistique calculée avant la coupure, aucune entrée prise sur l'instant prévu — mais il ne se transposerait pas tel quel sur un parc réel. C'est ce qu'il faudra dire au jury. #### Le registre — critère 3 | | | |---|---| | Modèle | `enervision-prevision-h1` | | Version promue | **3**, alias `production` | | Exécution | `382e4058b234451c915af32454a1b126`, expérience `prevision-h1` | | Base apprise | `enervision_preprod`, fenêtre 2026-07-30 → 2026-09-04 | | Découpage | 5 215 exemples d'apprentissage, 504 de test, coupure au 2026-08-31T08:00 | | Écartés | 7 heures pour cible fragile, 161 pour retard manquant | | Signature | `(-1, 33)` — 24 retards, heure, jour de semaine, capacité, taux de charge précédent, 5 indicatrices de type | `models:/enervision-prevision-h1@production` rend un `PyFuncModel` : c'est la seule commande de [`docs/runbooks/mlflow.md`](../src/branch/develop/docs/runbooks/mlflow.md) §5 qui n'avait jamais pu être jouée, faute de modèle à charger. Elle l'est. C'est aussi la couture que le #37 empruntera — `model.registre.charger`. #### Rejouabilité — critère 4 Trois exécutions successives sur les mêmes bornes, **MAE identique au millième** (2,018 kW). Graine fixée et journalisée, ordre des exemples trié, coupure calculée sur le dernier instant des données et non sur l'heure courante, et surtout `early_stopping=False` : laissé sur « auto », scikit-learn met de côté 10 % des exemples **tirés au hasard** dès dix mille lignes — à la fois une fuite sur une série temporelle et un découpage qui n'est pas le nôtre. `entrainer.sh --deux-passes` enchaîne deux entraînements sur des bornes figées, sans rien publier, et compare les deux tableaux. #### Ce qui reste, et qui n'est pas du code **Le critère 1 n'est pas encore prouvé sur la production.** `public.mesure` y est vide : le chargement de la zone or n'est pas planifié, c'est la branche `lenaic/136-zone-or-en-cron` (#136) qui le fait, et elle n'est pas fusionnée. La préproduction porte les 35 jours de données réelles, d'où ce premier entraînement là-bas. Le lanceur choisit sa base à l'environnement de lancement — `ENERVISION_MODEL_ENVIRONNEMENT`, ou `--environnement prod|preprod` —, avec **`prod` par défaut**. Un garde-fou refuse la promotion hors `enervision_prod` sauf `--promouvoir-hors-prod` : l'alias `production` est ce que le service du #37 chargera au démarrage, et un modèle appris ailleurs qui le prendrait servirait des prévisions apprises sur d'autres données. C'est le nom de base **relu dans l'URL** qui décide, jamais le réglage — un réglage peut mentir. Il entre aussi dans l'étiquette `promu_motif`, seule mémoire du geste puisque le registre n'est pas versionné dans git. Donc, dès que #136 est fusionnée et déployée : `entrainer.sh --depuis ...` sans autre option vise la production, trouve son URL dans `ENERVISION_ETL_DATABASE_URL` que le rôle Ansible `app` pose, et promeut sans garde-fou à lever. Les trois versions au registre sont la trace de deux correctifs trouvés par l'exécution elle-même : MLflow 3 ne range plus un modèle sous les artefacts de son exécution, et la signature devait venir du même tableau que l'exemple d'entrée. Aucune n'est supprimée, comme le manuel §5 le demande.
Author
Owner

Relevé sur le serveur ce matin, les huit entraînements de l'expérience prevision-h1 :

08/09 09:55   modèle 16,011   persistance 22,222   GAGNE   ← v6, porte l'alias production
08/09 09:50   modèle 25,118   persistance 10,217   PERD    ← 3 jours, le critère de l'ADR 0013
07/09 19:27   modèle 19,203   persistance 20,200   gagne de 1,0
07/09 19:26   modèle 19,203   persistance 20,200   gagne de 1,0
07/09 18:58   modèle 18,060   persistance 17,100   PERD
07/09 10:46   modèle  2,018   persistance 21,443   GAGNE
07/09 10:45   modèle  2,018   persistance 21,443   GAGNE
07/09 10:42   modèle  2,018   persistance 21,443   GAGNE

Trois faits, et le troisième est celui qui coûte cher au jury.

1. Le modèle en ligne ne satisfait pas le critère écrit. L'ADR 0013 dit : « l'alias production ne se déplace que si la MAE du candidat sur les trois derniers jours est strictement inférieure à la MAE de la persistance sur la même fenêtre ». Sur trois jours, il perd de 14,9. La v6 porte pourtant l'alias depuis 09h55.

2. Le 2,018 de la #166 n'est reproductible par aucun autre run. Tous les suivants tombent entre 16 et 25. Ce chiffre est dans le corps de la #166 fusionnée, et je l'ai repris tel quel dans la demande #170 vers main. Si le jury demande pourquoi 2,018 le 7 et 16 le 8, personne n'a la réponse aujourd'hui.

3. Le verdict change de signe avec la fenêtre. 3 jours : perd. 10 jours : gagne de 1,0. 14 jours : gagne de 6,2. Choisir 14 jours après avoir vu les trois résultats, c'est choisir la fenêtre qui donne la réponse voulue. C'est exactement ce qu'un jury regarde sur un sujet d'IA, et ça se retourne vite.

Ce que je propose, et ça se décide aujourd'hui

Amender l'ADR 0013 sur une fenêtre de 14 jours, avec le tableau des trois fenêtres comme justification écrite, et le motif : sur sept séries horaires courtes, trois jours tombent parfois sur un week-end où la série est plate et la persistance quasi imbattable. La fiche est « proposée », elle s'amende sans cérémonie.

Ce qui rend la chose défendable n'est pas le chiffre, c'est l'ordre : la fenêtre est fixée et écrite, puis on regarde. Aujourd'hui c'est l'inverse, et ça se voit dans l'horodatage des runs, 09h50 puis 09h55.

Deux points qui vont avec, sans quoi l'amendement ne vaut rien :

  • la fenêtre de test se déclare en paramètre de l'exécution MLflow, pour qu'on puisse relire après coup celle qui a servi à promouvoir ;
  • le 2,018 se ré-explique ou se retire. S'il vient d'une fenêtre trop courte ou d'une fuite, il faut le dire ; le laisser dans une demande fusionnée sans note, c'est laisser une question ouverte au jury.

Un dernier relevé, par site sur la v6 : le modèle bat la persistance partout sauf sur SITE003, 10,112 contre 5,669. Ça ne bloque pas la promotion d'un modèle unique, mais ça se dit avant qu'on nous le demande.

Justine, Olivier, c'est votre ticket : je constate et je propose, je ne tranche pas.

Relevé sur le serveur ce matin, les huit entraînements de l'expérience `prevision-h1` : ``` 08/09 09:55 modèle 16,011 persistance 22,222 GAGNE ← v6, porte l'alias production 08/09 09:50 modèle 25,118 persistance 10,217 PERD ← 3 jours, le critère de l'ADR 0013 07/09 19:27 modèle 19,203 persistance 20,200 gagne de 1,0 07/09 19:26 modèle 19,203 persistance 20,200 gagne de 1,0 07/09 18:58 modèle 18,060 persistance 17,100 PERD 07/09 10:46 modèle 2,018 persistance 21,443 GAGNE 07/09 10:45 modèle 2,018 persistance 21,443 GAGNE 07/09 10:42 modèle 2,018 persistance 21,443 GAGNE ``` Trois faits, et le troisième est celui qui coûte cher au jury. **1. Le modèle en ligne ne satisfait pas le critère écrit.** L'ADR 0013 dit : « l'alias `production` ne se déplace que si la MAE du candidat sur les **trois derniers jours** est strictement inférieure à la MAE de la persistance sur la même fenêtre ». Sur trois jours, il perd de 14,9. La v6 porte pourtant l'alias depuis 09h55. **2. Le 2,018 de la #166 n'est reproductible par aucun autre run.** Tous les suivants tombent entre 16 et 25. Ce chiffre est dans le corps de la #166 fusionnée, et je l'ai repris tel quel dans la demande #170 vers `main`. Si le jury demande pourquoi 2,018 le 7 et 16 le 8, personne n'a la réponse aujourd'hui. **3. Le verdict change de signe avec la fenêtre.** 3 jours : perd. 10 jours : gagne de 1,0. 14 jours : gagne de 6,2. Choisir 14 jours **après** avoir vu les trois résultats, c'est choisir la fenêtre qui donne la réponse voulue. C'est exactement ce qu'un jury regarde sur un sujet d'IA, et ça se retourne vite. ## Ce que je propose, et ça se décide aujourd'hui Amender l'ADR 0013 sur une fenêtre de **14 jours**, avec le tableau des trois fenêtres comme justification écrite, et le motif : sur sept séries horaires courtes, trois jours tombent parfois sur un week-end où la série est plate et la persistance quasi imbattable. La fiche est « proposée », elle s'amende sans cérémonie. Ce qui rend la chose défendable n'est pas le chiffre, c'est l'ordre : **la fenêtre est fixée et écrite, puis on regarde**. Aujourd'hui c'est l'inverse, et ça se voit dans l'horodatage des runs, 09h50 puis 09h55. Deux points qui vont avec, sans quoi l'amendement ne vaut rien : - **la fenêtre de test se déclare en paramètre de l'exécution MLflow**, pour qu'on puisse relire après coup celle qui a servi à promouvoir ; - **le 2,018 se ré-explique ou se retire.** S'il vient d'une fenêtre trop courte ou d'une fuite, il faut le dire ; le laisser dans une demande fusionnée sans note, c'est laisser une question ouverte au jury. Un dernier relevé, par site sur la v6 : le modèle bat la persistance partout **sauf sur SITE003**, 10,112 contre 5,669. Ça ne bloque pas la promotion d'un modèle unique, mais ça se dit avant qu'on nous le demande. Justine, Olivier, c'est votre ticket : je constate et je propose, je ne tranche pas.
Sign in to join this conversation.
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
2026-09-11
Reference
g2/enervision#36
No description provided.