1 juillet 2026 • Ibexcode
Construire seul un produit ML de bout en bout : des données à l’application mobile

Sommaire
- 1. Le problème : classer plutôt que simplement prédire
- 2. Une architecture ML multi-niveaux
- 3. Le problème central : ne jamais apprendre du futur
- 4. Séparer ranking et confiance
- 5. Le modèle n’est qu’une partie du système
- 6. Du serveur ML au backend cloud
- 7. Une application Flutter comme dernière couche du système
- 8. Automatiser le déploiement et l’exploitation
- 9. Du modèle expérimental au système ML en production
- 10. Une architecture qui traverse plusieurs métiers
- 11. Conclusion
En Machine Learning, entraîner un modèle n’est souvent qu’une petite partie du problème.
Il faut collecter et structurer les données, éviter les fuites temporelles, construire une stratégie de validation crédible, orchestrer les traitements, déployer les modèles, surveiller la qualité des données, exposer les résultats à un backend puis, finalement, construire le produit qui les utilise.
C’est précisément le type de projet que j’ai développé ces dernières années : un produit mobile reposant sur un système de ranking prédictif appliqué à des événements temporels comportant plusieurs candidats.
Le projet est aujourd’hui constitué d’une application Flutter disponible sur iOS et Android, d’un backend cloud, d’une infrastructure de traitement automatisée et d’une chaîne Machine Learning complète allant de l’ingestion des données jusqu’à la production de scores et de niveaux de confiance.
Cet article ne présente volontairement ni le domaine métier précis, ni les données utilisées, ni les variables, ni les performances détaillées des modèles. L’objectif est différent : présenter les principaux problèmes d’ingénierie rencontrés lors de la construction d’un tel système.
1. Le problème : classer plutôt que simplement prédire
Le problème ML n’est pas une classification classique où chaque observation est indépendante.
Chaque événement contient plusieurs candidats. Le système doit produire un classement relatif à l’intérieur de chaque événement.
Schématiquement :

Cette différence influence une grande partie de l’architecture.
Prédire correctement la valeur absolue associée à un candidat n’est pas nécessairement équivalent à le positionner correctement par rapport aux autres candidats du même événement.
Le système combine plusieurs objectifs de modélisation afin d’exploiter des représentations complémentaires du problème.
Ces objectifs n’imposent pas exactement la même représentation du problème. Leur combinaison pose alors une question d’architecture : comment exploiter plusieurs modèles sans se limiter à une moyenne de leurs prédictions ?
2. Une architecture ML multi-niveaux
La combinaison des modèles repose sur une architecture de stacking multi-niveaux.
Sa version conceptuelle peut être représentée ainsi :

Le principe du stacking est simple : les prédictions produites par les premiers modèles deviennent elles-mêmes des informations utilisables par les modèles suivants.
Sa mise en œuvre correcte sur des données temporelles l’est beaucoup moins.
Les modèles combinés ne sont pas nécessairement homogènes : plusieurs familles de modèles peuvent coexister afin d’exploiter différentes représentations des candidats et de leurs interactions.
3. Le problème central : ne jamais apprendre du futur
Sur des données temporelles, un K-fold aléatoire classique peut produire des résultats extrêmement trompeurs.
Si une observation datant de 2025 contribue à l’entraînement d’un modèle qui génère une prédiction pour 2023, cette prédiction n’est plus représentative de ce qui aurait réellement été possible en 2023.
Pour un modèle isolé, le problème est déjà important. Pour du stacking, il devient critique.
Imaginons que le premier niveau apprenne sur l’ensemble des données puis génère des scores utilisés pour entraîner le deuxième niveau.
Le deuxième modèle dispose indirectement d’informations provenant de la cible qu’il cherche à prédire. Les métriques deviennent artificiellement bonnes, sans être représentatives des performances réellement atteignables en production.
Des prédictions OOF strictement temporelles
Les différents niveaux sont donc construits autour de prédictions Out-Of-Fold future-only.
Le principe est :

Pour chaque période, le modèle ne peut utiliser que les informations disponibles antérieurement.
Les prédictions ainsi générées deviennent les features du niveau suivant. Le processus doit être répété à chaque étage du stack.
Cela coûte beaucoup plus cher qu’un entraînement classique, mais permet d’obtenir quelque chose d’essentiel : une simulation beaucoup plus réaliste des conditions de production.
4. Séparer ranking et confiance
L’architecture sépare explicitement le problème de ranking du problème d’estimation de confiance.
La première question est :
Quel candidat doit être classé devant les autres ?
La seconde :
À quel point pouvons-nous avoir confiance dans ce classement pour une utilisation donnée ?
Ce ne sont pas exactement les mêmes problèmes.
Le pipeline principal produit donc le ranking, tandis qu’une étape ultérieure analyse différents signaux issus du système de prédiction.
Cette étape peut exploiter de nombreuses informations issues du système. À titre d’exemple, on peut notamment retrouver des signaux liés :
- aux scores produits par différents niveaux
- à la dispersion entre modèles
- aux écarts dans le classement
- aux caractéristiques du classement obtenu
- à l’accord ou au désaccord entre plusieurs signaux
Il ne s’agit ici que de familles générales : en pratique, cette représentation peut reposer sur un espace de variables beaucoup plus large.
Cette couche produit une probabilité.
Mais une probabilité affichée à un utilisateur doit avoir une signification.
Un modèle annonçant régulièrement 0,80 alors que l’événement
correspondant ne se produit que 60 % du temps peut être utile pour
classer des observations, mais sa probabilité est mauvaise.
La calibration des sorties est donc mesurée, notamment avec le Brier Score et des analyses par intervalles de probabilité.
L’objectif devient alors :
Probabilité prédite ≈ fréquence réellement observée
Cette distinction entre discrimination et calibration est particulièrement importante lorsque les sorties du modèle sont utilisées directement par le produit.
5. Le modèle n’est qu’une partie du système
À ce stade, le problème ne se limite plus à la qualité du modèle. Un modèle parfaitement entraîné ne sert à rien si :
- les données quotidiennes n’arrivent pas
- leur schéma change silencieusement
- une étape du pipeline échoue
- les features ne sont plus calculées de la même manière
- l’inférence utilise une version incompatible du modèle
- les résultats ne sont pas publiés
- l’application ne sait pas qu’une nouvelle prédiction existe
La chaîne ML est donc traitée comme un système de production, et non comme un script Python exécuté manuellement.
Orchestrer les traitements avec Airflow
Les traitements sont orchestrés avec Apache Airflow le pipeline quotidien peut être simplifié ainsi :

Airflow apporte notamment :
- gestion des dépendances
- retries
- historique des exécutions
- parallélisation contrôlée
- observabilité
- reprise après erreur
- séparation des différentes responsabilités du pipeline
Une caractéristique importante du système est également son idempotence.
Relancer une étape après un échec ne doit pas produire un état incohérent ou dupliquer arbitrairement les données.
C’est un aspect beaucoup moins spectaculaire qu’un nouveau modèle ML, mais considérablement plus important une fois le système en production.
Les données doivent elles aussi être monitorées
Une panne franche est relativement simple à détecter. Les problèmes les plus dangereux sont souvent silencieux.
Une source peut continuer à répondre avec un code HTTP valide tout en ayant changé :
- un champ
- une catégorie
- une distribution
- un volume
- une convention de valeur
- une partie de son schéma
Le pipeline continue alors parfois à fonctionner. Et c’est précisément le problème. Le pipeline intègre donc des contrôles de qualité dès l’ingestion, permettant de comparer les données reçues avec ce que le système considère comme normal.
Cela comprend différents contrôles de :
- présence des données attendues
- structure
- valeurs inhabituelles
- catégories inconnues
- distributions
- volumes
L’objectif est de détecter les anomalies de données avant qu’elles ne se traduisent par une dégradation des métriques ML.
Un changement de distribution détecté au moment de l’ingestion est beaucoup plus facile à diagnostiquer qu’une baisse de performance constatée plusieurs semaines plus tard.
Reproductibilité : figer ce que le modèle connaît
La reproductibilité impose de versionner les transformations et informations nécessaires à l’utilisation du modèle.
Les variables catégorielles en donnent un exemple simple.
Lors de l’entraînement, un modèle apprend une représentation correspondant à un vocabulaire donné. Si ce vocabulaire est reconstruit différemment lors de l’inférence, deux catégories peuvent recevoir des représentations différentes.
Le pipeline utilise donc un vocabulaire versionné et conservé avec les artefacts nécessaires au modèle.
Un modèle n’est finalement pas seulement :
model.bin
Il correspond plutôt à :
model/
├── weights
├── configuration
├── categorical vocabulary
├── preprocessing information
├── calibration
└── metrics
Le déploiement doit considérer cet ensemble comme une unité cohérente.
Préserver la cohérence entre entraînement et production
Versionner les artefacts ne suffit cependant pas si les données réellement présentées au modèle diffèrent entre l’entraînement et l’inférence.
Un même modèle peut par exemple recevoir une variable construite d’une certaine manière pendant son apprentissage, puis calculée avec une logique légèrement différente une fois déployé.
Le pipeline peut alors continuer à fonctionner normalement. Les prédictions sont produites, aucun service ne tombe et aucune erreur technique évidente n’apparaît.
Pourtant, le modèle n’est plus utilisé dans les conditions dans lesquelles il a été entraîné et validé.
Ce type d’incohérence entre la manière dont les données sont préparées à l’entraînement et celle dont elles sont produites à l’inférence est généralement désigné sous le terme de train/serve skew.
Il peut apparaître à différents endroits de la chaîne :
- définition ou ordre des features
- transformations et normalisations
- encodage des catégories
- fenêtres utilisées pour les agrégations historiques
- valeurs par défaut ou traitement des données manquantes
- version des référentiels
- manière de produire ou de combiner des signaux intermédiaires
Le risque apparaît notamment lorsqu’une même information est reconstruite par deux chemins différents :
ENTRAÎNEMENT ──► construction des features ──► modèle
INFÉRENCE ──► construction des features ──► modèle
Ces deux chemins sont censés produire la même représentation.
Mais s’ils reposent sur des implémentations, configurations ou versions différentes, ils peuvent progressivement diverger à mesure que le système évolue.
La reproductibilité ne consiste donc pas seulement à être capable de recharger les poids d’un modèle.
Elle suppose également de préserver le contrat entre le modèle et les données qu’il reçoit.
Cela implique notamment de versionner les transformations et les schémas, de partager les mêmes traitements lorsque cela est possible et de vérifier que les représentations produites restent cohérentes entre entraînement et inférence.
Dans une architecture multi-niveaux, cette contrainte ne concerne d’ailleurs pas uniquement les features construites directement à partir des données brutes.
Les sorties d’un modèle peuvent devenir les entrées d’un autre. La manière dont ces signaux sont produits fait alors elle-même partie du contrat à préserver.
Il faut enfin distinguer ce problème du data leakage. Un système peut parfaitement respecter la causalité temporelle tout en présentant un train/serve skew.
Les deux problèmes répondent à des questions différentes :
Data leakage : le modèle a-t-il utilisé une information qu’il n’aurait pas dû connaître au moment simulé ?
Train/serve skew : le modèle reçoit-il en production des informations construites de manière cohérente avec celles sur lesquelles il a appris ?
La première question concerne la validité de l’apprentissage et de l’évaluation.
La seconde concerne la fidélité entre le système validé et celui réellement exécuté en production.
Dans les deux cas, un pipeline techniquement fonctionnel peut produire des prédictions sans qu’aucune erreur applicative ne soit visible. C’est précisément ce qui rend ces problèmes particulièrement importants à contrôler dans un système ML exploité en production.
6. Du serveur ML au backend cloud
Une fois les prédictions produites et validées, elles doivent devenir accessibles au produit.
Les responsabilités sont volontairement séparées. L’infrastructure ML réalise les traitements lourds. Le backend cloud gère principalement :
- exposition des résultats
- authentification
- gestion des utilisateurs
- droits d’accès
- logique d’abonnement
- synchronisation avec les plateformes mobiles
Les prédictions validées sont publiées dans une base cloud consommable par l’application. Cette séparation permet notamment d’éviter que l’application mobile dépende directement du serveur d’inférence.

Une interruption temporaire du pipeline ML n’implique donc pas nécessairement une indisponibilité de l’application.
Les abonnements mobiles sont un système distribué à eux seuls
La commercialisation sur iOS et Android ajoute un sous-système qui n’a pratiquement plus rien à voir avec le Machine Learning.
L’utilisateur achète depuis Apple ou Google, mais le backend doit déterminer de manière fiable quels droits lui accorder.
Le système doit notamment gérer :
- création d’un abonnement
- renouvellement
- expiration
- annulation
- restauration d’achat
- notifications serveur
- synchronisation de l’état
- changement d’appareil
La logique d’autorisation ne peut pas simplement reposer sur une valeur envoyée par l’application. Le backend conserve donc une représentation serveur de l’état des droits, synchronisée avec les informations provenant des plateformes.
Ce sous-système illustre l’étendue des responsabilités d’un produit initialement centré sur le ML lorsqu’il est exploité réellement sur mobile.
7. Une application Flutter comme dernière couche du système
Le client final est développé en Flutter afin de partager une base de code entre iOS et Android.
L’application n’a pas connaissance de la complexité du pipeline ML. Elle reçoit essentiellement des données déjà préparées :
Événement
│
├── candidats classés
├── scores
├── niveaux de confiance
└── informations associées
C’est volontaire.
Le téléphone ne doit ni reproduire le feature engineering ni exécuter la chaîne complète d’inférence.
Cette séparation permet également de faire évoluer les modèles sans imposer une nouvelle version de l’application à chaque modification du pipeline ML.
8. Automatiser le déploiement et l’exploitation
L’infrastructure est conçue pour limiter les opérations manuelles récurrentes.
Le projet utilise plusieurs briques d’automatisation autour de :
- Docker
- CI/CD
- déploiements automatisés
- infrastructure as code
- gestion des services
- monitoring
Dans un contexte de maintenance par une seule personne, l’automatisation constitue une contrainte architecturale : toute opération récurrente manuelle augmente à la fois le coût d’exploitation et le risque d’erreur.
L’objectif n’est donc pas de multiplier les composants d’infrastructure, mais de réduire les interventions nécessaires au fonctionnement courant du système.
9. Du modèle expérimental au système ML en production
La difficulté d’un système ML en production ne se limite pas à la performance de ses modèles.
Elle réside également dans la capacité à garantir la reproductibilité des entraînements, la validité des prédictions, la qualité des données et la continuité de l’ensemble de la chaîne.
En production, plusieurs propriétés doivent pouvoir être vérifiées :
- un entraînement doit pouvoir être reproduit dans des conditions maîtrisées
- une prédiction temporelle doit être générée sans utiliser d’information future
- les anomalies ou dérives de données doivent pouvoir être détectées
- l’échec d’une tâche doit permettre une reprise contrôlée
- une version précédente doit pouvoir être redéployée proprement
- une exécution partielle du pipeline doit être identifiable
- l’indisponibilité temporaire de l’inférence ne doit pas nécessairement rendre le produit indisponible
- les transformations utilisées à l’inférence doivent rester cohérentes avec celles utilisées lors de l’entraînement
- une nouvelle version du modèle doit être évaluée relativement au système déjà en place
Ce dernier point est particulièrement important dans une architecture composée de plusieurs modèles.
Ne pas confondre expérimentation et amélioration
Plusieurs familles de modèles, nouvelles variables, architectures et stratégies de combinaison peuvent être évaluées au cours du développement.
Une partie de ces expériences n’améliore pas le système. C’est une propriété normale d’un processus expérimental correctement conduit.
Toute modification candidate est donc soumise à un protocole de validation avant d’être considérée comme une amélioration du système :

Une amélioration locale d’un modèle n’est pas nécessairement une amélioration du produit.
Un modèle légèrement moins performant individuellement peut parfois apporter une information complémentaire utile à un ensemble.
Inversement, un modèle présentant de bonnes métriques isolées peut n’apporter strictement aucune information supplémentaire lorsqu’il est ajouté au stack.
L’évaluation ne porte donc pas uniquement sur la performance isolée. Elle mesure également la corrélation des erreurs, la complémentarité entre modèles, la robustesse statistique du gain et son impact après réintégration dans le pipeline complet.
Ce sujet mérite à lui seul un article.
10. Une architecture qui traverse plusieurs métiers
L’intérêt de cette architecture réside dans l’intégration de plusieurs disciplines au sein d’un même système.

J’ai conçu et implémenté les différentes couches de cette architecture, de la chaîne Data/ML jusqu’au backend et aux clients mobiles.
Concevoir seul un système de cette ampleur impose des arbitrages d’architecture et oblige à concentrer la complexité là où elle apporte réellement de la valeur.
Cette responsabilité de bout en bout demande surtout de maîtriser non seulement les différents composants, mais également leurs interactions.
- Comment une décision de feature engineering affecte-t-elle l’inférence ?
- Comment versionner un modèle avec son preprocessing ?
- Comment exposer ses résultats sans coupler le mobile au pipeline ML ?
- Comment déployer une évolution sans interrompre le service ?
- Comment distinguer une panne applicative d’un problème de données ?
- Comment faire évoluer indépendamment le modèle, le backend et les clients mobiles ?
Ces questions d’intégration représentent une part importante de l’ingénierie d’un système ML exploité en production.
11. Conclusion
Cette architecture réunit Data Engineering, Machine Learning, MLOps, backend cloud, DevOps et développement mobile autour d’un même système de production.
Au fil des expérimentations et de la mise en production, le projet mobilise un écosystème technique large : Python, LightGBM, CatBoost, DeepSets, Set Transformers, architectures MLP et Transformers tabulaires, PyTorch, Airflow, Docker, infrastructure as code, Firebase et Flutter. Mais l’enjeu principal se situe moins dans chacune de ces briques prise isolément que dans leur intégration au sein d’une chaîne cohérente et exploitable.
Mettre réellement un système ML entre les mains d’utilisateurs oblige à considérer toute la chaîne : données → feature engineering → expérimentation → validation → entraînement → calibration → inférence → orchestration → monitoring → backend → produit
Cela implique également qu’un modèle plus complexe, une nouvelle feature ou une meilleure métrique locale ne constitue pas nécessairement une amélioration du système.
Dans les prochains articles, je reviendrai plus précisément sur certains des problèmes rencontrés : validation temporelle et stacking sans data leakage, mesure de la diversité entre modèles, analyse des expériences ML négatives et monitoring d’un pipeline de données en production.