La gestión del ciclo de vida del modelo de visión por computadora en toda la flota es un problema que surge en el momento en que tiene éxito la prueba de concepto de inspección de calidad. Una cámara y un modelo funcionan bien. Luego, la planta solicita el mismo resultado en 40 líneas, cada una con varias cámaras, y cada cámara ve el mundo de manera diferente. Cada una tiene un ángulo distinto, un deslumbramiento diferente sobre la misma superficie inoxidable y una luz ambiental diferente dependiendo de qué puerta se abre. En las implementaciones de producción en las que hemos trabajado, un único modelo entrenado con imágenes combinadas no funciona lo suficiente frente a las cámaras individuales; por lo que cada cámara termina teniendo su propio modelo ajustado. Ahora, el verdero problema ya no es entrenar bien un solo modelo. Se trata de entrenar, evaluar, versionar y desplegar miles de modelos sin la intervención humana en cada uno de ellos.
Este artículo actualiza el patrón introducido en la serie de 2023 Crear una pipeline MLOps de extremo a extremo para la inspección de calidad visual en el borde para la actual Amazon SageMaker Python SDK v3. Reemplaza al ya descontinuado Amazon SageMaker Edge Manager por Amazon SageMaker Neo para la optimización en el borde. Expliqué un Pipeline Amazon SageMaker de doble rama que entrena un detector de anomalías y un clasificador de defectos por cámara, con puertas de calidad que bloquean los modelos de bajo rendimiento. Una etapa de compilación de SageMaker Neo dentro del pipeline asegura que cada modelo registrado se envíe con un artefacto optimizado para dispositivos, y una capa de orquestación de AWS Step Functions coordina el entrenamiento de la flota. Se implementa todo con el AWS Cloud Development Kit (AWS CDK), y una ejecución completa del pipeline en datos sintéticos cuesta aproximadamente $0.07 en computación.
La muestra completa está disponible en el repositorio GitHub de aws-samples.
Descripción general de la solución
El siguiente diagrama muestra el ciclo de vida del modelo en toda la flota del muestreo. Una capa de orquestación agrupa las solicitudes de reentrenamiento y limita el trabajo simultáneo. Un pipeline de entrenamiento por cámara prepara datos, entrena y evalúa un detector de anomalías y un clasificador, y genera artefactos de modelos versionados para dispositivos de borde.
Figura 1: Arquitectura de solución
La mitad de entrenamiento es un único Pipeline de SageMaker, parametrizado por la cámara, que ejecuta 13 pasos por ejecución. Tres pasos de entrada se ejecutan en paralelo: Preprocesar, Obtener la Anomalía Más Reciente y Obtener el Clasificador Más Reciente. El pipeline preprocesa las imágenes de esa cámara y obtiene el modelo aprobado más reciente de la cámara para cada rama, luego entrena dos modelos. El primero es un pequeño autoencoder convolucional que aprende cómo se ve un producto normal y señala las anomalías mediante el error de reconstrucción. El segundo es un clasificador ResNet18 refinado para confirmar el defecto. Cada rama exporta su modelo a ONNX, evalúa el artefacto exportado contra datos de prueba y pasa por una puerta de calidad. Después de pasar la puerta, el modelo se compila con SageMaker Neo para el objetivo de la frontera configurado, y solo entonces se registra en el SageMaker Model Registry. Como resultado, cada paquete aprobado tiene un artefacto compilado listo para dispositivos. Los paquetes llevan el ID de la cámara como metadatos, y los filtros de recuperación de calor de cada rama se basan en él; por lo tanto, el reentrenamiento siempre se inicia a partir de la historia propia de esa cámara.
La mitad de la orquestación maneja la flota. Las solicitudes de reentrenamiento se almacenan en una cola FIFO del Amazon Simple Queue Service (Amazon SQS), y una función AWS Lambda inicia una ejecución de Step Functions por cada solicitud. Cada ejecución obtiene una plaza a través de un semáforo DynamoDB antes de comenzar su pipeline, de modo que una oleada de solicitudes se agota a un ritmo constante, en lugar de todas lanzarse al mismo tiempo. Las completaciones se publican en el Amazon Simple Notification Service (Amazon SNS), y las fallas operativas llegan al mismo tema a través de una regla de Amazon EventBridge, con el error original conservado.
El resultado del sistema está versionado y sometido a control de calidad, en formato de artefactos del modelo por cámara. El Registro de Modelos almacena el ONNX portátil, y S3 contiene la versión compilada en Neo de ese modelo, que es una biblioteca compartida optimizada para dispositivos, cargada por el tiempo de ejecución DLR en lugar de ONNX. Ambos están listos para cualquier mecanismo de entrega utilizado por su planta.
Requisitos previos
Para desplegar el ejemplo, necesita lo siguiente:
- Un cuenta de AWS con permisos para crear roles de Gestión de Identidad y Acceso de AWS (IAM).
- Un bucket de Amazon Simple Storage Service (Amazon S3).
- Un repositorio de Amazon Elastic Container Registry (Amazon ECR).
- Los recursos de SageMaker AI, AWS Step Functions, AWS Lambda, Amazon Simple Queue Service (Amazon SQS), Amazon Simple Notification Service (Amazon SNS), Amazon EventBridge y Amazon DynamoDB en las estructuras.
- Python 3.10, 3.11 o 3.12, Docker.
- La versión 2.1131.0 o posterior de la CLI de AWS CDK.
Las versiones anteriores de la CLI fallan debido a una discrepancia en el esquema de montaje de la nube con las versiones recientes de aws-cdk-lib. No es necesario aumentar la cuota de servicio. El capa de orquestación admite, por defecto, al menos cinco ejecuciones de pipeline a la vez, lo que permite mantener los trabajos de entrenamiento de SageMaker concurrentes dentro de la cuota estándar del cuenta.
Si se despliega en una región de AWS diferente de la predeterminada de la CLI, establezca los tres valores AWS_REGION, AWS_DEFAULT_REGION y CDK_DEFAULT_REGION en la propia orden de despliegue, en lugar de en el perfil del shell, por ejemplo: AWS_REGION=us-east-2 AWS_DEFAULT_REGION=us-east-2 CDK_DEFAULT_REGION=us-east-2 cdk deploy --all. La CLI de CDK, Boto3 y la interfaz de línea de comandos de AWS (AWS CLI) resuelven la región a partir de conjuntos de variables que se superponen pero no son idénticas, y una discrepancia produce despliegues parciales confusos. El prefijo del comando establece los tres valores solo para esa invocación, por lo que la región predeterminada del usuario permanece sin cambios.
Definir la cadena de procesamiento de entrenamiento con SDK v3
El código en esta sección está extraído de la aplicación CDK en el repositorio, y no es un script independiente. Se ejecuta durante el proceso de cdk deploy, cuando la pila sintetiza la definición del pipeline. La configuración del entorno y las órdenes de deploy se realizan posteriormente, en “Deploying and running the sample”. El SDK v3 reorganizó el SageMaker Python SDK en paquetes modulares, y las importaciones muestran la nueva estructura de la biblioteca:
from sagemaker.train import ModelTrainer
from sagemaker.train.configs import InputData, Compute, OutputDataConfig
from sagemaker.core.training.configs import MetricDefinition
from sagemaker.serve.model_builder import ModelBuilder
from sagemaker.mlops.workflow.pipeline import Pipeline
from sagemaker.mlops.workflow.steps import ProcessingStep, TrainingStep
from sagemaker.mlops.workflow.model_step import ModelStep
from sagemaker.mlops.workflow.condition_step import ConditionStep
from sagemaker.core.workflow.conditions import ConditionGreaterThanOrEqualTo
from sagemaker.core.workflow.functions import JsonGet
Al instalar sagemaker>=3.0.0,<4, se incluyen sagemaker-core, sagemaker-train, sagemaker-serve y sagemaker-mlops como paquetes separados. La configuración de entrenamiento que v2 distribuía entre los argumentos del constructor Estimator ahora reside en objetos de configuración tipados que se pasan a ModelTrainer. Este es el entrenador de la rama de anomalías, literalmente del ejemplo:
anomalib_trainer = ModelTrainer(
training_image=custom_training_image_uri,
compute=Compute(
instance_type=training_instance_type.to_string(),
instance_count=1,
),
base_job_name="cv-pipeline-anomalib-train",
sagemaker_session=pipeline_session,
role=role_arn,
hyperparameters={
"program": "train_anomalib.py",
"model_name": "autoencoder",
"image_size": "256",
"num_epochs": "3",
"batch_size": "16",
"learning_rate": "0.001",
},
input_data_config=anomalib_input_data,
output_data_config=OutputDataConfig(
s3_output_path=run_prefix("anomalib-training")
),
).with_metric_definitions(
[
MetricDefinition(
name="image_auroc",
regex=r"validation:image_auroc=([0-9]*\.?[0-9]+)",
),
]
)
anomalib_input_data conecta cuatro canales (train, validation, test, pretrained_model) a partir de las propiedades del paso superior, y run_prefix asigna cada ruta S3 al ID de ejecución del pipeline, de modo que las ejecuciones concurrentes del flotilla nunca se colisionen. Ambos están en el repositorio.
Dos comportamientos de v3 son importantes cuando el pipeline se encuentra dentro de una aplicación CDK, y ninguno aparece en el changelog como cambio quebrantador. Primero, SDK v3 valida el rol de ejecución del pipeline frente a IAM en tiempo real mientras construye la definición del pipeline. Esta validación ocurre en el momento de síntesis, no en el momento de ejecución. Una aplicación CDK no puede crear el rol y el pipeline en una sola implementación, porque el rol aún no existe cuando SDK lo valida. Esto es separado de cdk bootstrap, que proporciona los recursos del toolkit CDK una vez por cuenta y región, y podría ya estar hecho en su cuenta. La implementación de dos fases del ejemplo está controlada por una flag de contexto que compartirá ese nombre. Una pasada inicial con -c bootstrap=true crea el rol, el bucket y el repositorio ECR, y la segunda pasada sintetiza el pipeline frente al rol ya existente. El nombre de recurso de Amazon (ARN) del rol incrustado en la definición del pipeline también debe ser una cadena concreta, no un token CDK no resuelto.
En segundo lugar, el validador de roles v3 simula las permisividades de extracción de ECR sin un ARN de recurso, por lo que un rol de ejecución delimitado a un único repositorio ECR falla en la validación, aunque funcionaría en tiempo de ejecución. Por esta razón, el rol del ejemplo tiene una concesión más amplia de ECR.
Por último, una costumbre v3 que utiliza el ejemplo: construir la pipeline con PipelineSession(default_bucket=your_bucket) para redirigir los cargamentos gestionados por el SDK, como el código de paso, al bucket propio de la pipeline; de esta manera, todos los artefactos se alojan en un único lugar y las permisiones del rol de ejecución cubren un solo bucket. (Permitir al rol acceso al bucket por defecto del SDK también funciona. Este enfoque es más ordenado.)
Puertas de calidad que coinciden con el comportamiento de los datos de defectos
Cada modelo entrenado se evalúa antes de su registro, y la evaluación se realiza contra el artefacto ONNX exportado, en lugar del modelo PyTorch en memoria. Esta distinción es importante: el artefacto que se utiliza es el mismo que se envía, por lo que un problema en la exportación nunca puede dejar pasar un modelo defectuoso después de una ejecución de entrenamiento que parece sana.
La rama de anomalías en AUROC tiene al menos un 0.8 de gates. La rama del clasificador tiene gates en dos métricas al mismo tiempo, y la elección de las métricas está determinada por la propiedad característica de los datos de defectos: el desequilibrio grave en las clases. En este ejemplo, aproximadamente el 90 por ciento de las imágenes son buenas, lo cual es realista para una línea de producción. Contra esa distribución, la precisión simple no tiene sentido, porque un modelo que predice “bueno” para cada imagen obtiene un puntaje de aproximadamente 0.9, mientras no detecta ningún defecto. Precisión equilibrada, que es la media de la recuperación por clase, corrige eso, pero tiene su propio punto débil. Un modelo con una recuperación perfecta en las partes buenas y solo un 20 por ciento de recuperación en los defectos todavía obtiene una puntuación de (1.0 + 0.2) / 2 = 0.6. Así que la puerta requiere una precisión equilibrada de al menos 0.6 y una recuperación de defectos de al menos 0.5, juntas:
step_check_classifier = ConditionStep(
name="CheckClassifierQuality",
conditions=[
ConditionGreaterThanOrEqualTo(
left=JsonGet(
step_name=step_eval_classifier.name,
property_file="evaluation",
json_path="balanced_accuracy",
),
right=min_accuracy,
),
ConditionGreaterThanOrEqualTo(
left=JsonGet(
step_name=step_eval_classifier.name,
property_file="evaluation",
json_path="defect_recall",
),
right=min_defect_recall,
),
],
if_steps=[step_compile_classifier, step_register_classifier],
else_steps=[step_fail_classifier],
depends_on=[step_eval_classifier],
)
Un ConditionStep combina sus condiciones; por lo tanto, ambas deben cumplirse. Los umbrales son parámetros de la pipeline, que pueden ajustarse durante la ejecución sin necesidad de redeployar. Si un gate falla, un FailStep indica que la ejecución fracasó y el modelo de ese ramal nunca llega al registro (ambos ramales funcionan de forma independiente, así que el modelo del otro ramal puede seguir registrarse). La flota sigue proporcionando el modelo anteriormente aprobado para esa cámara.
El código de entrenamiento aborda el mismo desequilibrio desde el otro lado. El clasificador es un resnet18 fino-tunado con pesos de ImageNet y una cabeza de dos clases. En tamaños de conjunto de datos por cámara, el fino-tunear los pesos preentrenados es lo que evita que el modelo se incline hacia la clase mayoritaria. El código de entrenamiento equilibra los lotes de entrenamiento con un WeightedRandomSampler utilizando frecuencias inversas de clases, mientras que la pérdida permanece sin ponderación. Combinar una pérdida ponderada con el muestreo ponderado corrige en exceso hacia la clase minoritaria.
Compilación para el borde con SageMaker Neo, dentro del pipeline
Note el if_steps del portal en el código anterior: después de pasar por el portal, la compilación se ejecuta primero y el registro depende de ella. Ambos scripts de entrenamiento exportan ONNX con ejes de lote dinámicos. Los portales de calidad evalúan esos artefactos exportados bajo ONNX Runtime, y luego un paso CompileAnomalibNeo o CompileClassifierNeo entrega el artefacto gated a SageMaker Neo. El proceso es: portal, luego compilación, luego registro. Un modelo solo se convierte en un paquete Aprobado si también se compiló para el destino de borde del flota, por lo que el registro nunca anuncia un modelo que los dispositivos no puedan ejecutar.
La etapa de compilación encapsula tres detalles propensos a los errores. Neo requiere que el archivo tarball de entrada contenga únicamente el archivo del modelo, pero las tareas de entrenamiento emiten model.tar.gz con el checkpoint y las métricas junto al modelo ONNX. La etapa repackagea primero un tarball solo con ONNX. Neo también requiere formas de entrada concretas, mientras que los exportes declaran un eje de lote dinámico, por lo que el tamaño del lote 1 está fijado en la solicitud de compilación. Y como las etapas de la pipeline pueden repetir intentos, el nombre de la tarea de compilación se deriva determinísticamente del ID de ejecución, de modo que un intento de repetición reanuda la verificación de la tarea que ya comenzó, en lugar de dejarla abandonada.
El objetivo es un parámetro de pipeline: NeoTargetDevice selecciona un dispositivo nombrado como jetson_xavier o rasp4b, y su valor por defecto se compila para la plataforma NeoTargetOs/NeoTargetArch, LINUX/X86_64 por defecto. El paquete compilado contiene el compiled.so optimizado más el runtime DLR para su carga. El resultado por defecto funciona en cualquier host Linux x86_64, por lo que puede validarlo antes de comenzar a utilizar objetivos específicos para cada dispositivo. La compilación en sí no implica costos adicionales. Usted paga por la instancia de procesamiento que la realiza y por el almacenamiento S3 de los resultados. Para compilar un modelo ya registrado para tipos de dispositivos adicionales más tarde, sin reentrenamiento, el repositorio incluye un script ad hoc que ejecuta el mismo flujo contra el Model Registry.
Modelos por cámara en el Registro de Modelos
Cada rama se registra en su propia grupo de paquetes de modelo, y cada paquete lleva el ID de la cámara y sus métricas de evaluación. En el ejemplo, la conexión de las métricas se omite:
classifier_model_builder = ModelBuilder(
s3_model_data_url=classifier_model_data,
image_uri=custom_training_image_uri,
sagemaker_session=pipeline_session,
role_arn=role_arn,
)
step_register_classifier = ModelStep(
name="RegisterClassifierModel",
depends_on=[step_compile_classifier],
step_args=classifier_model_builder.register(
model_package_group_name="sm1k-classifier-model-group",
approval_status="Approved",
model_metrics=classifier_metrics,
customer_metadata_properties={"CameraId": camera_id},
),
)
La identidad de la cámara en customer_metadata_properties es lo que convierte dos grupos de paquetes compartidos en un catálogo de flota. Los paquetes de todas las cámaras residen en los mismos grupos, y el historial por cámara se recupera filtrando los metadatos. Cada rama tiene una etapa de recuperación que recorre los paquetes aprobados de nuevo al más reciente hasta encontrar el modelo más reciente de la cámara solicitante, porque ListModelPackages no puede filtrar en los metadatos del cliente desde el servidor. En la primera ejecución de una cámara, la recuperación no encuentra nada y el entrenamiento retrocede a su inicialización de arranque frío (pesos ImageNet para el clasificador, inicialización aleatoria para el autoencoder). Cada ejecución posterior se reanuda desde el modelo previo de esa cámara, lo que hace viable un entrenamiento rellenado frecuente, económico e incremental en toda la flota.
Observa qué entradas de registro son y no son: catalogan artefactos versionados y con control de calidad, pero su referencia de contenedor es la imagen de entrenamiento, que no sirve para ningún punto final de inferencia. Existen para un flujo de trabajo de flota en el que un sistema descendente extrae artefactos y los envía a dispositivos, no para CreateEndpoint.
Orquestando las solicitudes de entrenamiento de flota
Cada cuenta de AWS tiene cuotas de servicio para trabajos de entrenamiento concurrentes de SageMaker, y cada ejecución de pipeline inicia varios trabajos, por lo que el reentrenamiento del flota necesita un control de velocidad. Algo debe evitar que una ráfaga de 500 solicitudes se convierta en 500 lanzamientos de pipeline simultáneos. El ejemplo utiliza un semáforo, un contador de plazas disponibles que funciona como un estacionamiento con cinco espacios. Antes de que la máquina de estado de Step Functions inicie un pipeline, toma una posición incrementando atomicmente un contador en DynamoDB, con una condición que impide el incremento una vez que se alcanza el límite:
acquire_semaphore = tasks.DynamoUpdateItem(
self, "AcquireSemaphore",
table=semaphore_table,
key={"LockName": tasks.DynamoAttributeValue.from_string("pipeline-concurrency")},
update_expression="SET ActiveCount = if_not_exists(ActiveCount, :zero) + :one",
condition_expression="attribute_not_exists(ActiveCount) OR ActiveCount < :limit",
)
Si todos los slots están ocupados, la ejecución espera y se intenta volver a intentar. Cuando su pipeline termina, disminuye el contador para liberar el slot. Un detalle que vale la pena incluir en sus propios diseños: la expresión de condición de DynamoDB impone el límite en una única operación atómica, en lugar de que la máquina de estado lea el contador y tome una decisión, lo cual dejaría un intervalo de competencia entre la lectura y la escritura. El repositorio implementa el resto de los procedimientos de refuerzo operativo, incluyendo la liberación de slots en cada camino de fallo y el reenvío de las ejecuciones fallidas a Amazon SNS a través de Amazon EventBridge. Su README aborda el dimensionamiento de los límites de espera.
El límite de cinco es un parámetro de stack, que se establece en un valor bajo para que la muestra se desplegue y funcione en cualquier cuenta estándar sin necesidad de solicitar aumentos de cuota. Ajuste ese valor según las cuotas de trabajo de entrenamiento de su cuenta. Una vez superadas las cuotas de una sola cuenta, el escalado del entrenamiento de la flota es un tema aparte. Las herramientas estándar son aumentar las cuotas de servicio, distribuir el entrenamiento entre tipos de instancias o Regiones, y distribuir la flota entre múltiples cuentas. También puede retener el trabajo pendiente en SQS y admitir trabajo solo cuando haya más capacidad disponible. Esas opciones dependen del tamaño y la estructura de su flota. El ejemplo le proporciona el esqueleto funcional para ampliarlo.
Desplegar y ejecutar el ejemplo
La guía en el README del repositorio es la versión oficial. La forma abreviada siguiente comienza con un checkout limpio y configura el entorno antes de la primera implementación:
git clone https://github.com/aws-samples/sample-sagemaker-fleet-cv-pipeline
cd sample-sagemaker-fleet-cv-pipeline && python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cdk bootstrap # once per account/region
cdk deploy SageMakerCVPipelineStack -c bootstrap=true # roles, bucket, ECR
cdk deploy --all # pipeline + orchestration
./scripts/push_ecr_image.sh # build and push training image
Por defecto, la pipeline genera imágenes de líneas de producción sintéticas, por lo que se puede validar todo el sistema de extremo a extremo sin necesidad de conjuntos de datos ni aprobaciones de datos. Una ejecución completa realiza todos los 13 pasos, incluyendo las compilaciones de Neo, en aproximadamente 15 minutos y cuesta aproximadamente $0.07 en tiempo de ml.m5.xlarge facturado (Región US East (Ohio) (us-east-2), julio de 2026, a petición). Los recursos de orquestación suman poco menos de $0.10 al mes a escala de muestreo.
Para ejecutar el pipeline sobre imágenes reales, el repositorio incluye un descargador para el conjunto de datos BCCD. El conjunto de datos es una colección de 364 imágenes de esputo sanguíneo microscopía anotadas, originalmente open source en GitHub y reorganizadas en formato Pascal VOC bajo una licencia MIT. El mismo conjunto de datos también se distribuye por Roboflow como dominio público. La script hace que la presencia de plaquetas se asocie a la clase de defecto, divide opcionalmente las imágenes por cámara para simular una flota, y sube los archivos de entrenamiento al bucket del pipeline. El notebook que lo acompaña ejecuta todo el pipeline sobre datos reales y realiza inferencias ONNX lado a lado en un muestra normal y una muestra anómala. Tratar las plaquetas como defectos es una representación ilustrativa de la distribución de defectos de fabricación, no una afirmación clínica.
Limpiar
Los paquetes de modelo se crean durante el tiempo de ejecución del pipeline, por lo que AWS CloudFormation no puede eliminarlos. Para eliminarlos, primero use la AWS CLI y luego destruya las estructuras con cdk destroy --all -c bootstrap=true --force. El README enumera los comandos exactos.
Conclusión
En este artículo, mostré una arquitectura de referencia del SDK v3 de SageMaker en funcionamiento para el ciclo de vida del visión por computadora a escala de flota. La arquitectura incluye un pipeline de entrenamiento con dos ramas, donde cada modelo se verifica su calidad a través de su artefacto ONNX exportado, se compila con SageMaker Neo y, solo entonces, se registra. Un diseño del Registro de Modelos utiliza los metadatos de la cámara para convertir los grupos de paquetes compartidos en un historial que puede iniciarse en cada cámara, y una capa de orquestación de Step Functions controla el entrenamiento de la flota dentro de las cuotas del cuenta. Para equipos cuyas necesidades se satisfacen con opciones gestionadas, como Amazon Lookout for Vision, ese es el camino más sencillo. Este patrón se enfoca en la limitación a nivel de flota por cámara que las opciones gestionadas no abordan. Las reglas de diseño se extienden más allá de la visión computacional: se prioriza una precisión equilibrada junto con el recordado de clases minoritarias en lugar de la precisión, se evalúa y compila el artefacto que se envía, y se notifica los fallos operativos en lugar de terminar silenciosamente.
El ejemplo se desplega en minutos y una ejecución completa de validación cuesta aproximadamente siete centavos de computación, por lo que un paso siguiente de bajo costo es clonar el repositorio y ejecutarlo. A partir de ahí, dirija al descargador BCCD hacia un bucket real, establezca NeoTargetDevice para su hardware, o ajuste los límites de orquestación para su flota. Para la migración del SDK v3 en sí, la documentación del SDK Python de SageMaker y la serie de dos partes que anuncia el nuevo SDK son las referencias que he mantenido abiertas durante todo.
Si necesita ayuda para adaptar este patrón a su entorno de producción, comuníquese con el equipo de su cuenta en AWS.