Module 5 — Conteneurs personnalisés et scripts d'entraînement
XGBoost intégré couvre 80 % des cas tabulaires ; le fil rouge est dans les 20 % restants. La direction veut comparer XGBoost avec un pipeline scikit-learn qui combine une régression logistique calibrée et une forêt aléatoire. Ce module explique comment faire tourner votre script Python sur SageMaker sans construire d'image, puis quand basculer sur une image entièrement personnalisée.
Trois niveaux de contrôle
SageMaker distingue trois façons d'exécuter votre code :
| Niveau | Image | Vous fournissez | Utile quand |
|---|---|---|---|
| Intégré | AWS | Rien, seulement des hyperparamètres | L'algorithme existe déjà (XGBoost, KMeans) |
| Mode script | AWS pour votre framework | Un script Python | Vous restez dans sklearn/torch/tf, dépendances standard |
| Image personnalisée | Vous | Un Dockerfile complet | Dépendances système exotiques, version de framework hors catalogue |
Le mode script est le point d'équilibre : liberté totale sur le code Python, mais AWS gère l'image, la sécurité, les mises à jour de la base. C'est ce qui portera le fil rouge dans ce module.
Le mode script scikit-learn, appliqué
Voici l'orchestration côté carnet :
from sagemaker.sklearn.estimator import SKLearn
estimateur = SKLearn(
entry_point="entrainement.py",
source_dir="./src", # tout ce dossier est zippé et envoyé
framework_version="1.2-1",
role=role,
instance_type="ml.m5.xlarge",
output_path="s3://resiliation-modeles/sklearn/",
hyperparameters={
"n_estimators": 200,
"max_depth": 8,
"cv_folds": 5,
},
)
estimateur.fit({"train": "s3://resiliation-donnees/prepare/v3/train.parquet"})
SageMaker zippe ./src, dépose l'archive sur S3, la télécharge dans le conteneur, et invoque python entrainement.py --n_estimators 200 --max_depth 8 --cv_folds 5. Tout votre script doit lire ses paramètres depuis sys.argv — ou plutôt argparse — et ses chemins depuis des variables d'environnement.
Les variables d'environnement SageMaker à connaître
Le conteneur d'entraînement expose une dizaine de variables ; six suffisent en pratique.
| Variable | Contenu | Exemple |
|---|---|---|
SM_MODEL_DIR | Dossier où déposer les artefacts | /opt/ml/model |
SM_OUTPUT_DATA_DIR | Dossier pour les autres sorties (métriques, courbes) | /opt/ml/output/data |
SM_CHANNEL_TRAIN | Emplacement du canal train | /opt/ml/input/data/train |
SM_CHANNEL_VALIDATION | Emplacement du canal validation | /opt/ml/input/data/validation |
SM_NUM_CPUS | Nombre de vCPU disponibles | 4 |
SM_NUM_GPUS | Nombre de GPU disponibles | 0 |
SM_HPS | Hyperparamètres en JSON | {"n_estimators": 200, ...} |
Un script correct commence donc invariablement ainsi :
# src/entrainement.py
import os
import argparse
import json
import joblib
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import roc_auc_score
def analyseur():
p = argparse.ArgumentParser()
p.add_argument("--n_estimators", type=int, default=100)
p.add_argument("--max_depth", type=int, default=8)
p.add_argument("--cv_folds", type=int, default=5)
# Chemins par défaut = variables d'environnement SageMaker
p.add_argument("--model_dir", type=str, default=os.environ["SM_MODEL_DIR"])
p.add_argument("--train", type=str, default=os.environ["SM_CHANNEL_TRAIN"])
return p.parse_args()
def main():
args = analyseur()
df = pd.read_parquet(os.path.join(args.train, "train.parquet"))
X, y = df.drop(columns=["a_resilie"]), df["a_resilie"]
modele = RandomForestClassifier(
n_estimators=args.n_estimators,
max_depth=args.max_depth,
n_jobs=int(os.environ.get("SM_NUM_CPUS", 1)),
)
modele.fit(X, y)
# 1. Écrire le modèle
joblib.dump(modele, os.path.join(args.model_dir, "modele.joblib"))
# 2. Écrire une métrique lisible par la console SageMaker
auc = roc_auc_score(y, modele.predict_proba(X)[:, 1])
print(f"train-auc={auc:.4f};") # le point-virgule est capté par les regex de métrique
if __name__ == "__main__":
main()
Trois points souvent ratés au premier essai. L'écriture dans SM_MODEL_DIR est la seule copiée automatiquement dans model.tar.gz sur S3 ; écrire ailleurs est perdu à la fin de la tâche. Le nombre de vCPU vient de SM_NUM_CPUS — utiliser os.cpu_count() renverrait le nombre de vCPU de l'hôte physique, souvent supérieur, et provoquerait du sur-abonnement CPU. Le format des métriques imprimées suit une expression régulière (train-auc=(\d+\.\d+);) à déclarer côté estimateur pour qu'AWS les remonte dans la console.
Déclarer les métriques pour SageMaker et le réglage automatique
Sans déclaration, le script imprime des lignes que personne ne lit. Avec :
estimateur = SKLearn(
...,
metric_definitions=[
{"Name": "train:auc", "Regex": r"train-auc=(\S+);"},
{"Name": "validation:auc", "Regex": r"validation-auc=(\S+);"},
],
)
Les métriques apparaissent alors dans la console SageMaker, dans CloudWatch Metrics, et surtout deviennent la métrique objectif que le réglage automatique du module 6 optimisera.
Dépendances au-delà de l'image
Le script peut avoir besoin d'un paquet absent de l'image sklearn:1.2-1 — par exemple lightgbm pour comparer. Deux approches :
Simple : un requirements.txt à la racine de source_dir. SageMaker le détecte et exécute pip install -r requirements.txt avant de lancer votre script. Coûte 20 à 40 secondes de temps de tâche, sans risque particulier.
# src/requirements.txt
lightgbm==4.3.0
optuna==3.6.1
Verrouillé : construire une image ECR dérivée qui contient déjà ces paquets. Le démarrage est immédiat et l'installation ne peut pas échouer en cas d'incident réseau PyPI. C'est la solution de production, développée dans la section suivante.
Image entièrement personnalisée
Quand ni le mode script ni un requirements.txt ne suffisent — code C++ à compiler, version de CUDA particulière, framework non couvert — on construit une image Docker complète, publiée sur ECR, puis on utilise Estimator générique :
# Dockerfile
FROM 763104351884.dkr.ecr.us-east-1.amazonaws.com/sagemaker-scikit-learn:1.2-1-cpu-py3
RUN apt-get update && apt-get install -y libomp-dev
RUN pip install lightgbm==4.3.0 optuna==3.6.1 great-expectations==0.18.16
Le FROM d'une image AWS conserve tous les crochets d'exécution SageMaker (variables SM_*, script d'amorçage) sans avoir à les recoder. Puis :
aws ecr get-login-password | docker login --username AWS \
--password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
docker build -t 123456789012.dkr.ecr.us-east-1.amazonaws.com/resiliation:1.0 .
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/resiliation:1.0
Côté carnet :
from sagemaker.estimator import Estimator
estimateur = Estimator(
image_uri="123456789012.dkr.ecr.us-east-1.amazonaws.com/resiliation:1.0",
entry_point="entrainement.py",
source_dir="./src",
role=role,
instance_type="ml.m5.xlarge",
output_path="s3://resiliation-modeles/lgbm/",
)
Construire depuis une image AWS coûte 10 minutes de configuration ; construire depuis zéro (FROM ubuntu) impose de recoder les crochets SM_* et double le travail.
Débogage local, la seule vraie économie
Une tâche d'entraînement à distance prend 4 à 8 minutes de bout en bout, dont 90 secondes de provisionnement — inacceptable pour itérer sur une faute de frappe. SageMaker propose deux modes de débogage local :
estimateur = SKLearn(
...,
instance_type="local", # exécute dans le carnet Studio, sans provisionner
)
Le conteneur sklearn est tiré une fois puis mis en cache ; les itérations suivantes démarrent en 10 secondes. Le canal train peut pointer sur un fichier local (file:///home/sagemaker-user/mini_train.parquet) ou sur un préfixe S3 restreint.
Une fois le script propre, on repasse en ml.m5.xlarge pour la vraie tâche. Cette discipline — échouer vite en local, réussir loin dans le cloud — divise typiquement le temps de développement d'un script par 5.
Beaucoup d'équipes utilisent le mode script en production, jamais l'image personnalisée. La raison : à chaque publication d'image dérivée, il faut la faire scanner par la sécurité, la republier à chaque montée de version de sklearn, et documenter son maintien. Un requirements.txt verrouillé (==) avec un mode script est plus rapide, plus auditable et couvre les cas où les dépendances sont dans PyPI.
En résumé
- Le mode script garde le contrôle du code Python tout en laissant AWS gérer l'image ; un
requirements.txtcouvre la plupart des dépendances supplémentaires. - Les variables
SM_MODEL_DIR,SM_CHANNEL_TRAIN,SM_NUM_CPUSetSM_HPSsont le contrat entre votre script et SageMaker ; écrire hors deSM_MODEL_DIRfait perdre l'artefact. - Les
metric_definitionstransforment des lignes imprimées en métriquesCloudWatch, exploitables par la console et par le réglage automatique du module 6. - Une image
ECRpersonnalisée basée sur une image AWS conserve les crochetsSM_*; le mode local (instance_type="local") divise le temps d'itération par 5 en développement.
Module suivant : le réglage automatique bayésien qui va explorer les hyperparamètres du modèle scikit-learn et du modèle XGBoost.