Blog de Amazon Web Services (AWS)

Construyendo un workflow de Machine Learning punta a punta en Amazon SageMaker utilizando Spark y AWS Step Functions

Por María Gaska, Arquitecta de Soluciones en AWS
Andrés Palacios, Arquitecto de Soluciones especialista en Analytics en AWS

 

Con el lanzamiento de cada vez más servicios que ayudan a democratizar Machine Learning, acelerar su adopción, ciclo de desarrollo y reducir los costos de cómputo, la demanda de este tipo de aplicaciones ha aumentado considerablemente. Este aumento de la demanda plantea un reto para las áreas encargadas de llevar este tipo de proyectos a producción de manera ágil y controlada. Esto es particularmente desafiante en industrias como telecomunicaciones y retail, entre otras, donde la se cuenta con grandes volúmenes de datos nuevos que necesitan ser procesados de manera distribuida.

El pre-procesamiento de grandes volúmenes de datos supone un desafío adicional: en muchos casos es deseable que este entorno funcione en un clúster con herramientas distribuidas (como por ejemplo Spark) mientras que el modelo de Machine Learning a utilizar se buscaría ejecutar en otro entorno, como por ejemplo Python.

Este blog describe la implementación de un workflow de Machine Learning de punta a punta. La solución propuesta utiliza tareas síncronas de AWS Step Funcions, además de pre-procesamiento distribuído en Spark mediante Amazon SageMaker Processing. Esta combinación de funcionalidades y servicios permite reducir la cantidad de pasos requeridos para ejecutar el workflow.

 

Diseño de la solución

La implementación utiliza funcionalidades de Amazon SageMaker para el pre-procesamiento, el entrenamiento, la optimización de hiperparámetros y la inferencia. Se aprovechan las integraciones nativas entre este servicio y AWS Step Function para orquestar el workflow. Para el pre-procesamiento se diseña un entorno totalmente propio utilizando una imagen de Docker con Spark que SageMaker va a utilizar como entorno para el trabajo de pre-procesamiento.

 

 

  1. El data set se disponibiliza en un bucket de Amazon S3, lo cual dispara la ejecución del workflow en AWS Step Functions.
  2. Se genera una imagen de Spark en Amazon ECR.
  3. El data set se procesa a través de un Amazon SageMaker processing job que implementa la imagen creada en el punto anterior y se generan los conjuntos de entrenamiento y validación. Estos se disponibilizan en Amazon S3.
  4. Se ejecuta una tarea de entrenamiento en Amazon SageMaker utilizando el contenedor manejado de XGBoost.
  5. Se construye un Pipeline de SageMaker uniendo los artefactos del pre-procesamiento y del modelo predictivo con el fin de poder reproducir las transformaciones hechas en el momento del entrenamiento a la hora de la inferencia.
  6. Se aplica el Pipeline de inferencia sobre datos nuevos, a través de un batch transform job en Amazon SageMaker

Toda la solución está disponible en un template de CloudFormation para poder reproducirse en su propia cuenta de AWS.

En las secciones siguientes se ejemplifica la implementación de esta solución utilizando como entrada el data set público Abalone. Este data set será pre-procesado y posteriormente se entrenará un modelo de regresión utilizando el algoritmo XGBoost para predecir la edad de los moluscos a partir de sus características.

 

1. Preparando y construyendo los pasos en el entorno de desarrollo

Lo primero será preparar el entorno de desarrollo, con el fin de observar el paso a paso del ciclo de vida de Machine Learning.  Para esto, vamos a aprovisionar una instancia de tipo ml.m5.xlarge. Sobre esta instancia se montará un notebook de referencia con el paso a paso del workflow, el cual se incluye en el siguiente link: https://github.com/githubmg/sagemaker-airflow-stepfunctions/blob/master/notebooks/end-to-end-pipeline.ipynb

 

 

2. Desplegando la solución y ensamblando los pasos

Este paso a paso incluido en el Notebook se invocará usando las tareas síncronas de AWS Step Functions para orquestar todo el proceso de punta a punta.

Link al template de CloudFormation

Para desplegar la solución completa, basta con desplegar el siguiente template de CloudFormation, el cual aprovisionará el stack incluyendo el workflow de AWS Step Functions,

 

3. Construyendo el pre-procesamiento distribuido en Spark

Para usar Spark el stack creará una instancia EC2, en la cual construirá la imagen del contenedor de Docker y la registrará en Amazon ECR para su uso distribuido mediante los Processing Jobs de Spark.

 

Para hacer uso del pre-procesamiento distribuido en Spark desde AWS Step Functions, se usará la tarea nativa síncrona de procesamiento.

Ejemplo del código JSON de la tarea de pre-procesamiento:

"SageMakerCreateProcessingJob": {

                      "Type": "Task",

                      "Resource": "arn:aws:states:::sagemaker:createProcessingJob.sync",

                      "Parameters": {

                        "AppSpecification": {

                          "ContainerArguments": [

                            "s3_input_bucket",

                            "",

                            "s3_input_key_prefix",

                            "${P_s3_input_key_prefix}",

                            "s3_output_bucket",

                            "${P_s3_output_bucket}",

                            "s3_output_key_prefix",

                            "${P_s3_output_key_prefix}",

                            "s3_model_bucket",

                            "${P_s3_model_bucket}",

                            "s3_mleap_model_prefix",

                            "${P_s3_mleap_model_prefix}"

                          ],

                          "ContainerEntrypoint": [

                            "/opt/program/submit",

                            "${P_preprocessing_script_path}"

                          ],

                          "ImageUri": "XXXXXX.dkr.ecr.us-east-1.amazonaws.com/sagemaker-spark-example:latest"

                        },

                        "Environment": {

                          "mode": "python"

                        },

                        "ProcessingInputs": [

                          {

                            "InputName": "code",

                            "S3Input": {

                              "LocalPath": "/opt/ml/processing/input/code",

                              "S3CompressionType": "None",

                              "S3DataDistributionType": "FullyReplicated",

                              "S3DataType": "S3Prefix",

                              "S3InputMode": "File",

                              "S3Uri": "s3://bucket_name/script/preprocess.py"

                            }

                          }

                        ],

                        "ProcessingJobName": "Preprocspark-${AWS::StackId}",

                        "ProcessingResources": {

                          "ClusterConfig": {

                            "InstanceCount": 2,

                            "InstanceType": "ml.r5.xlarge",

                            "VolumeSizeInGB": 30

                          }

                        },

                        "RoleArn": "${StepFunctionsSageMakerExecutionArn}",

                        "StoppingCondition": {

                          "MaxRuntimeInSeconds": 3000

                        }

                      },

                      "Next": "XGBoostTrainingJob"

 

En este ejemplo, el script de Spark utilizado para el preprocesamiento hará el one hot encoding del único feature categórico, ensamblará el Vector, serializará y almacenará el modelo usando MLeap.

A continuación se muestran los pasos del flujo completo de la orquestación. Como se puede ver, son pocos pasos para todas las fases del ciclo de vida de Machine Learning, iniciando con el pre-procesamiento, realizando el entrenamiento, optimizando los hiperparámetros, creando el pipeline y realizando la inferencia para almacenar el output final:

 

4. Entrenando el modelo

Una vez se construyen los features y el pipeline con MLeap (preprocesamiento), el siguiente paso es entrenar los parámetros del modelo usando una imágen del algoritmo XGBoost sobre SageMaker Training Jobs. Esta es la tarea descrita en el workflow de Step Functions como: XGBoostTrainingJob.

A continuación se muestra un ejemplo del código JSON para crear la tarea en AWS Step Functions:

    "XGBoostTrainingJob": {

      "Resource": "arn:aws:states:::sagemaker:createTrainingJob.sync",

      "Parameters": {

        "AlgorithmSpecification": {

          "TrainingImage": "683313688378.dkr.ecr.us-east-1.amazonaws.com/sagemaker-xgboost:0.90-1-cpu-py3",

          "TrainingInputMode": "File"

        },

        "OutputDataConfig": {

          "S3OutputPath": "test-577857857857855"

        },

        "StoppingCondition": {

          "MaxRuntimeInSeconds": 86400

        },

        "ResourceConfig": {

          "InstanceCount": 1,

          "InstanceType": "ml.m4.xlarge",

          "VolumeSizeInGB": 20

        },

        "RoleArn": "arn:aws:iam::837454905712:role/service-role/StepFunctionsSageMakerExecutionRole-test-577857857857855",

        "InputDataConfig": [

          {

            "DataSource": {

              "S3DataSource": {

                "S3DataDistributionType": "FullyReplicated",

                "S3DataType": "S3Prefix",

                "S3Uri.$": "$.train_data"

              }

            },

            "ChannelName": "train",

            "ContentType": "text/csv"

          },

          {

            "DataSource": {

              "S3DataSource": {

                "S3DataDistributionType": "FullyReplicated",

                "S3DataType": "S3Prefix",

                "S3Uri.$": "$.validation_data"

              }

            },

            "ChannelName": "validation",

            "ContentType": "text/csv"

          }

        ],

        "HyperParameters": {

          "objective": "reg:linear",

          "eta": "0.2",

          "gamma": "4",

          "max_depth": "5",

          "num_round": "10",

          "subsample": "0.7",

          "silent": "0",

          "min_child_weight": "6"

        },

        "TrainingJobName.$": "$.training_job"

      },

      "Type": "Task",

      "Next": "CreatePipeline"

    },

 

5. Construyendo el pipeline y realizando la inferencia final

El siguiente paso que se ejecutará en la orquestación es el de la creación del pipeline

"CreatePipeline": {

      "Type": "Task",

      "Resource": "arn:aws:lambda:us-east-1:XXXXXX:function:createPipelineModel",

      "ResultPath": "$",

      "Next": "ExecuteBatchTransform"

    },

 

Una vez creado el pipeline, se realiza la inferencia con base en los nuevos datos que se disponibilicen. Para esto se invoca un SageMaker Batch Transform, que dejará el resultado de la inferencia final en el bucket especificado en el código.

A continuación un ejemplo del código de la tarea en JSON de AWS Step Functions:

"ExecuteBatchTransform": {

      "Type": "Task",

      "Resource": "arn:aws:lambda:us-east-1:XXXXXX:function:executeBatchTransform",

      "ResultPath": "$",

      "End": true

    }

 

Eliminar el stack de la solución

Si se desea evitar cargos futuros, se pueden eliminar los recursos creados en esta implementación. Para esto se debe abrir la consola de AWS CloudFormation. Luego, bajo Stacks, elegir el nombre del stack que fue creado. Por último, seleccionar Delete y confirmar la eliminación seleccionando Delete Stack.

 

Conclusión

En este blog se describió la implementación de un workflow de Machine Learning de punta a punta, usando Amazon SageMaker y sus jobs nativos de pre-procesamiento para diferentes volúmenes de datos.

La solución brinda la flexibilidad y escalabilidad de construir un workflow de punta a punta con pocas tareas, usando recursos de cómputo a la medida y de propósito específico. De este modo, dentro del diseño se incorporaron las buenas prácticas de la industria a la hora de industrializar un proyecto de Machine Learning de principio a fin.

 


Sobre los autores

María Gaska es arquitecta de soluciones en AWS especializada en AI/ ML. En su rol, ayuda a los clientes tanto a determinar la mejor arquitectura para sus distintas aplicaciones como a encontrar los mejores algoritmos para resolver problemas de Machine Learning e IA. Antes de AWS, trabajó como desarrolladora de modelos de deep learning en un startup enfocada en NLP y chatbots y también como profesora full time en una coding school a cargo de un curso de data science.

 

 

 

Andrés Palacios es arquitecto de soluciones especialista en Analytics en AWS. En su rol apoya a los clientes a encontrar la mejor solución y arquitectura para sus necesidades al igual que aprovechar los servicios de AI/ML para generar innovación y mejorar la productividad. Antes de AWS, trabajó para grandes consultoras en las áreas de Data y Analytics, tanto como en consultoría estratégica, arquitectura e implementación de soluciones de procesamiento y consumo distribuidas.

 

 

 

Sobre los revisores

Sergio Beltrán es arquitecto de soluciones especialista en AI/ML en AWS. En su rol apoya a los clientes a encontrar la mejor solución y arquitectura para sus necesidades al igual que aprovechar los servicios de AI/ML para generar innovación y mejorar la productividad. Antes de AWS, trabajó como data scientist y gerente de business development en la industria de Telco.

 

 

 

Rodrigo Alarcón es arquitecto de soluciones senior en AWS. En su rol, ayuda a empresas de distinto tamaño a generar valor para su negocio mediante tecnologías de computación en la nube. Sus intereses incluyen Machine Learning y ciberseguridad. Se ha desempeñado por más de 10 años como profesional de tecnologías de la información, con foco en ciberseguridad e infraestructura de redes.