Aller au contenu principal

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 :

NiveauImageVous fournissezUtile quand
IntégréAWSRien, seulement des hyperparamètresL'algorithme existe déjà (XGBoost, KMeans)
Mode scriptAWS pour votre frameworkUn script PythonVous restez dans sklearn/torch/tf, dépendances standard
Image personnaliséeVousUn Dockerfile completDé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.

VariableContenuExemple
SM_MODEL_DIRDossier où déposer les artefacts/opt/ml/model
SM_OUTPUT_DATA_DIRDossier pour les autres sorties (métriques, courbes)/opt/ml/output/data
SM_CHANNEL_TRAINEmplacement du canal train/opt/ml/input/data/train
SM_CHANNEL_VALIDATIONEmplacement du canal validation/opt/ml/input/data/validation
SM_NUM_CPUSNombre de vCPU disponibles4
SM_NUM_GPUSNombre de GPU disponibles0
SM_HPSHyperparamè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.

Le mode script est plus qu'un raccourci

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.txt couvre la plupart des dépendances supplémentaires.
  • Les variables SM_MODEL_DIR, SM_CHANNEL_TRAIN, SM_NUM_CPUS et SM_HPS sont le contrat entre votre script et SageMaker ; écrire hors de SM_MODEL_DIR fait perdre l'artefact.
  • Les metric_definitions transforment des lignes imprimées en métriques CloudWatch, exploitables par la console et par le réglage automatique du module 6.
  • Une image ECR personnalisée basée sur une image AWS conserve les crochets SM_* ; 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.