Aller au contenu principal

Module 8 — Transformation par lots pour les gros volumes

L'application mobile a besoin du point de terminaison temps réel du module 7 pour afficher le score d'un client qui ouvre son espace personnel. L'équipe marketing, elle, veut chaque premier du mois la probabilité de résiliation des 5 millions de clients pour cibler une campagne de rétention. Servir cela par le point de terminaison serait absurde ; ce module explique pourquoi et comment utiliser la transformation par lots.

Trois raisons de choisir le lot plutôt que le point de terminaison

Le calcul est immédiat. 5 millions d'invocations à 10 ms chacune font 833 heures de calcul cumulé ; sur ml.m5.large à 0,115 $/h avec un peu de parallélisme, l'exercice prend une trentaine d'heures et coûte environ 3,50 $. Mais surtout :

Latence non contraignante — la campagne part le 5 du mois, il n'y a aucune raison de répondre en 100 ms. Le batch tolère 30 secondes par requête.

Optimisation de débit — en lot, un même appel de conteneur traite 1 000 lignes d'un coup. Le coût par ligne chute d'un facteur 20 à 50 par rapport à des appels HTTP unitaires.

Pas d'infrastructure permanente — le point de terminaison qui aurait pu faire ce travail coûte 83 $/mois qu'il serve ou non ; la tâche batch coûte le temps qu'elle prend, une fois, puis rien.

Sur le fil rouge, un scoring mensuel unique sur ml.m5.xlarge (0,23 $/h) prend 55 minutes, coûte 0,21 $, et libère toute l'infrastructure aussitôt.

Une tâche de transformation, ligne par ligne

Batch Transform prend un Model (le même objet qu'au module 7) et un préfixe S3 d'entrée, produit un préfixe S3 de sortie, et s'arrête.

from sagemaker.transformer import Transformer

transformer = Transformer(
model_name="resiliation-v4", # créé par estimateur.create_model()
instance_count=2, # deux instances en parallèle
instance_type="ml.m5.xlarge", # 0,23 $/h
output_path="s3://resiliation-donnees/scores/2026-09/",
accept="text/csv",
strategy="MultiRecord", # regrouper les lignes par appel
max_payload=6, # Mo par requête au conteneur
max_concurrent_transforms=8, # 8 requêtes parallèles par instance
assemble_with="Line", # concatener les sorties ligne par ligne
)

transformer.transform(
data="s3://resiliation-donnees/prepare/mensuel/2026-09/clients_a_scorer.parquet",
content_type="application/x-parquet",
split_type="Line",
input_filter="$[1:]", # ignorer la première colonne (identifiant)
join_source="Input", # rattacher chaque score à sa ligne d'entrée
output_filter="$[0,-1]", # ne garder que id + score
job_name="resiliation-batch-2026-09",
)
transformer.wait()

Six paramètres décident du bon fonctionnement :

strategy="MultiRecord" et max_payload=6 : SageMaker regroupe autant de lignes que possible dans une requête de 6 Mo au conteneur. Sur des lignes de 200 octets, cela fait environ 30 000 lignes par appel — d'où le gain de débit face à un appel unitaire par ligne.

split_type="Line" : SageMaker découpe le fichier d'entrée à chaque saut de ligne. Pour Parquet, ce paramètre est ignoré au profit du découpage natif par groupe de lignes.

input_filter, output_filter, join_source : ils règlent la question centrale du lot — rattacher un score à un identifiant.

Le piège du score orphelin

Le conteneur d'inférence reçoit une matrice de variables et renvoie une probabilité par ligne. Il ne renvoie pas l'identifiant du client, parce qu'il ne l'a pas reçu — l'identifiant a été retiré des variables d'entrée pour ne pas influencer le modèle.

Sans précaution, la sortie du batch est un fichier predictions.csv avec 5 millions de probabilités sans savoir à quel client elles correspondent. C'est un piège classique, découvert trop tard.

Deux mécanismes règlent cela.

join_source="Input" : SageMaker rattache automatiquement chaque ligne de sortie à la ligne d'entrée correspondante, dans le même ordre.

input_filter="$[1:]" et output_filter="$[0,-1]" : le premier envoie au modèle toutes les colonnes sauf la première (l'identifiant) ; le second garde la première colonne (l'identifiant) et la dernière (la probabilité). Le résultat est un CSV client_id,proba_resiliation directement exploitable.

Sur le fil rouge, cette précaution divise par cinq le temps d'ingestion du fichier de sortie côté marketing, parce qu'il n'y a pas à ré-joindre manuellement sur la position de ligne — approche fragile, cassée dès qu'un caractère de saut de ligne parasite décale un enregistrement.

Parallélisme : instances et concurrence

Deux niveaux de parallélisme s'additionnent :

  • instance_count : combien d'instances travaillent en parallèle, chacune sur une part du fichier.
  • max_concurrent_transforms : combien de requêtes le conteneur d'une instance traite en même temps.

Sur ml.m5.xlarge (4 vCPU), max_concurrent_transforms=8 sature bien les cœurs pour un modèle scikit-learn dont l'inférence tient en 5 ms par ligne. Pour un modèle GPU (transformeurs), max_concurrent_transforms=1 par instance et instance_count élevé donne un meilleur rendement.

Doubler instance_count de 2 à 4 sur le fil rouge ferait passer la tâche de 55 à 30 minutes pour un coût presque égal (30 min × 4 instances ≈ 55 min × 2 instances). Le levier utile est plutôt le type d'instance : passer de ml.m5.xlarge à ml.c5.2xlarge (mieux optimisé CPU) descend à 20 minutes pour 0,25 $.

Le point de reprise en cas d'échec

Batch Transform ne gère pas nativement le point de reprise : si la tâche échoue à 80 %, il faut la relancer intégralement. Trois pratiques limitent la douleur :

Découper l'entrée en préfixes datés : 2026-09/lot-01.parquet, 2026-09/lot-02.parquet... Un échec ne perd qu'un lot.

Utiliser Spot : Transformer supporte Spot depuis 2023, avec les mêmes règles qu'à l'entraînement (max_wait ≥ max_run). Une remise de 40 à 60 % sur des tâches déjà bon marché.

Vérifier les erreurs par ligne : Transformer écrit un fichier failure.log à côté des sorties. Sur 5 millions de lignes, 0,1 % d'erreurs (5 000 lignes) est acceptable et se rejoue sur un mini-lot.

Comparaison chiffrée avec le temps réel

Sur le fil rouge, deux régimes de scoring existent en parallèle :

RégimeVolume mensuelModeCoût mensuel
Temps réel (application mobile)~200 000 requêtesSans serveur~14 $
Batch mensuel (marketing)5 000 000 lignes, 1 foisml.c5.2xlarge × 30 min0,25 $
Total~14,25 $

Comparaison avec la solution naïve où le batch passe par le point de terminaison temps réel : 83 $/mois pour un ml.m5.large en 24/24 + latence marketing insupportable. Le facteur de coût est proche de 6 pour le mois et pire pédagogiquement, parce qu'il masque la nature différente des deux usages.

Quand préférer un vrai pipeline

Une tâche batch isolée est simple, mais dès que le scoring mensuel doit être précédé de :

  • une extraction depuis Redshift ou Snowflake,
  • une vérification de qualité (schéma, valeurs manquantes),
  • une validation que le modèle utilisé est bien la version approuvée du registre,
  • un envoi du fichier de sortie au service marketing,

la logique déborde d'un simple transformer.transform(). C'est le rôle de SageMaker Pipelines du module 9, qui enchaîne les étapes et versionne l'exécution.

Le sans-serveur peut-il remplacer le batch ?

Non, pas économiquement. Le sans-serveur du module 7 facture à la milliseconde de calcul, ce qui paraît idéal pour un scoring ponctuel. Mais chaque invocation traite une ligne et supporte les frais fixes du démarrage du conteneur, d'un appel HTTP, d'un chiffrement TLS. Sur 5 millions de lignes, la note grimpe à plusieurs centaines de dollars — 30 à 100 fois plus qu'un batch qui regroupe. La règle est stable : temps réel ou sans-serveur pour l'unitaire, batch pour le massif.

En résumé

  • La transformation par lots est le bon outil dès que la latence n'est pas contraignante et que le volume dépasse quelques dizaines de milliers de lignes en une passe.
  • strategy="MultiRecord" et max_payload regroupent des milliers de lignes par appel, faisant chuter le coût par ligne d'un facteur 20 à 50.
  • join_source="Input" avec input_filter / output_filter sépare l'identifiant du client des variables et l'appose au score en sortie ; sans cela, le fichier est inexploitable.
  • Le batch reste 6 à 100 fois moins cher qu'un point de terminaison persistant pour un scoring massif ; le temps réel et le batch coexistent naturellement, chacun sur son cas d'usage.

Module suivant : SageMaker Pipelines et le registre de modèles, pour enchaîner traitement, entraînement, évaluation et enregistrement d'un modèle approuvé.