Entrena, ajusta y despliega de manera eficiente conjuntos personalizados utilizando Amazon SageMaker
Entrena, ajusta y despliega conjuntos personalizados eficientemente con Amazon SageMaker
La inteligencia artificial (IA) se ha convertido en un tema importante y popular en la comunidad tecnológica. A medida que la IA ha evolucionado, hemos visto diferentes tipos de modelos de aprendizaje automático (ML) surgir. Un enfoque, conocido como modelado de conjunto, ha estado ganando rápidamente popularidad entre los científicos de datos y los profesionales. En esta publicación, discutimos qué son los modelos de conjunto y por qué su uso puede ser beneficioso. Luego, proporcionamos un ejemplo de cómo puede entrenar, optimizar e implementar sus conjuntos personalizados utilizando Amazon SageMaker.
El aprendizaje de conjunto se refiere al uso de múltiples modelos y algoritmos de aprendizaje para obtener predicciones más precisas que cualquier algoritmo de aprendizaje individual. Se ha demostrado que son eficientes en diversas aplicaciones y entornos de aprendizaje, como ciberseguridad [1] y detección de fraudes, teledetección, predicción de los mejores próximos pasos en la toma de decisiones financieras, diagnóstico médico e incluso tareas de visión por computadora y procesamiento del lenguaje natural (NLP). Solemos categorizar los conjuntos por las técnicas utilizadas para entrenarlos, su composición y la forma en que fusionan las diferentes predicciones en una sola inferencia. Estas categorías incluyen:
- Boosting – Entrenar secuencialmente múltiples modelos débiles, donde cada predicción incorrecta de los modelos anteriores en la secuencia se le asigna un peso mayor y se ingresa al siguiente modelo, creando así un modelo más fuerte. Ejemplos incluyen AdaBoost, Gradient Boosting y XGBoost.
- Bagging – Utiliza múltiples modelos para reducir la varianza de un solo modelo. Ejemplos incluyen Random Forest y Extra Trees.
- Stacking (mezcla) – A menudo utiliza modelos heterogéneos, donde las predicciones de cada estimador individual se apilan juntas y se utilizan como entrada para un estimador final que maneja la predicción. El proceso de entrenamiento de este estimador final a menudo utiliza validación cruzada.
Existen múltiples métodos para combinar las predicciones en una única predicción que finalmente produce el modelo, por ejemplo, utilizando un meta-estimador como un modelo lineal, un método de votación que utiliza múltiples modelos para realizar una predicción basada en la votación mayoritaria para tareas de clasificación, o un promedio de conjunto para regresión.
Aunque varias bibliotecas y marcos de trabajo proporcionan implementaciones de modelos de conjunto, como XGBoost, CatBoost o random forest de scikit-learn, en esta publicación nos enfocamos en traer sus propios modelos y usarlos como un conjunto de mezcla. Sin embargo, en lugar de utilizar recursos dedicados para cada modelo (trabajos de entrenamiento y ajuste dedicados y puntos de enlace de alojamiento por modelo), entrenamos, ajustamos e implementamos un conjunto personalizado (múltiples modelos) utilizando un solo trabajo de entrenamiento de SageMaker y un solo trabajo de ajuste, y lo implementamos en un solo punto de enlace, reduciendo así posibles costos y cargas operativas.
BYOE: Trae tu propio conjunto de mezcla
Existen varias formas de entrenar e implementar modelos de conjunto heterogéneos con SageMaker: puede entrenar cada modelo en un trabajo de entrenamiento separado y optimizar cada modelo por separado utilizando la sintonización automática de modelos de Amazon SageMaker. Al alojar estos modelos, SageMaker proporciona diversas formas rentables de alojar múltiples modelos en la misma infraestructura de inquilino. Los patrones de implementación detallados para este tipo de configuraciones se pueden encontrar en Patrones de alojamiento de modelos en Amazon SageMaker, Parte 1: Patrones de diseño comunes para construir aplicaciones de ML en Amazon SageMaker. Estos patrones incluyen el uso de múltiples puntos de enlace (para cada modelo entrenado) o un solo punto de enlace multi-modelo, e incluso un solo punto de enlace multi-contenedor donde los contenedores se pueden invocar individualmente o encadenados en una tubería. Todas estas soluciones incluyen un meta-estimador (por ejemplo, en una función de AWS Lambda) que invoca a cada modelo e implementa la función de mezcla o votación.
- SimPer Aprendizaje auto-supervisado simple de objetivos periódicos
- El reconocimiento facial en los Estados Unidos está a punto de enfr...
- Una forma sorprendentemente simple de frustrar a los ladrones de autos
Sin embargo, ejecutar múltiples trabajos de entrenamiento puede introducir cargas operativas y costos adicionales, especialmente si su conjunto requiere entrenamiento en los mismos datos. De manera similar, alojar diferentes modelos en puntos de enlace o contenedores separados y combinar sus resultados de predicción para obtener una mayor precisión requiere múltiples invocaciones, y por lo tanto introduce esfuerzos adicionales de gestión, costos y monitoreo. Por ejemplo, SageMaker admite modelos de conjunto de ML utilizando Triton Inference Server, pero esta solución requiere que los modelos o conjuntos de modelos sean compatibles con el backend de Triton. Además, se requieren esfuerzos adicionales por parte del cliente para configurar el servidor Triton y aprender a comprender cómo funcionan los diferentes backends de Triton. Por lo tanto, los clientes prefieren una forma más sencilla de implementar soluciones donde solo necesitan enviar la invocación una vez al punto de enlace y tener la flexibilidad de controlar cómo se agregan los resultados para generar la salida final.
Resumen de la solución
Para abordar estas preocupaciones, revisamos un ejemplo de entrenamiento de conjunto utilizando un solo trabajo de entrenamiento, optimizando los hiperparámetros del modelo e implementándolo utilizando un solo contenedor en un punto de enlace sin servidor. Utilizamos dos modelos para nuestro conjunto de mezcla: CatBoost y XGBoost (ambos son conjuntos de refuerzo). Para nuestros datos, utilizamos el conjunto de datos de diabetes [2] de la biblioteca scikit-learn: consta de 10 características (edad, sexo, masa corporal, presión arterial y seis mediciones de suero sanguíneo), y nuestro modelo predice la progresión de la enfermedad 1 año después de recopilar las características iniciales (un modelo de regresión).
El repositorio completo del código se puede encontrar en GitHub.
Entrena múltiples modelos en un solo trabajo de SageMaker
Para entrenar nuestros modelos, utilizamos trabajos de entrenamiento de SageMaker en modo Script. Con el modo Script, puedes escribir código personalizado de entrenamiento (y posteriormente de inferencia) mientras utilizas los contenedores del framework de SageMaker. Los contenedores del framework te permiten utilizar entornos predefinidos administrados por AWS que incluyen toda la configuración y los módulos necesarios. Para demostrar cómo puedes personalizar un contenedor del framework, utilizamos como ejemplo el contenedor preconstruido de SKLearn, que no incluye los paquetes XGBoost y CatBoost. Hay dos opciones para agregar estos paquetes: extender el contenedor incorporado para instalar CatBoost y XGBoost (y luego implementar como un contenedor personalizado), o utilizar la función de modo de script del trabajo de entrenamiento de SageMaker, que te permite proporcionar un archivo requirements.txt
al crear el estimador de entrenamiento. El trabajo de entrenamiento de SageMaker instala las bibliotecas enlistadas en el archivo requirements.txt
durante la ejecución. De esta forma, no necesitas administrar tu propio repositorio de imágenes de Docker y proporciona más flexibilidad para ejecutar scripts de entrenamiento que requieren paquetes adicionales de Python.
El siguiente bloque de código muestra el código que utilizamos para iniciar el entrenamiento. El parámetro entry_point
apunta a nuestro script de entrenamiento. También utilizamos dos características destacadas de la API del SDK de SageMaker:
- Primero, especificamos la ruta local a nuestro directorio de origen y dependencias en los parámetros
source_dir
ydependencies
, respectivamente. El SDK comprimirá y cargará esos directorios en Amazon Simple Storage Service (Amazon S3) y SageMaker los pondrá a disposición en la instancia de entrenamiento en el directorio de trabajo/opt/ml/code
. - Segundo, utilizamos el objeto estimador
SKLearn
del SDK con nuestra versión preferida de Python y del framework, para que SageMaker descargue el contenedor correspondiente. También hemos definido una métrica de entrenamiento personalizada ‘validation:rmse
‘, que se registrará en los registros de entrenamiento y capturará SageMaker. Luego, utilizamos esta métrica como la métrica objetivo en el trabajo de ajuste.
hyperparameters = {"num_round": 6, "max_depth": 5}
estimator_parameters = {
"entry_point": "multi_model_hpo.py",
"source_dir": "code",
"dependencies": ["my_custom_library"],
"instance_type": training_instance_type,
"instance_count": 1,
"hyperparameters": hyperparameters,
"role": role,
"base_job_name": "xgboost-model",
"framework_version": "1.0-1",
"keep_alive_period_in_seconds": 60,
"metric_definitions":[
{'Name': 'validation:rmse', 'Regex': 'validation-rmse:(.*?);'}
]
}
estimator = SKLearn(**estimator_parameters)
A continuación, escribimos nuestro script de entrenamiento (multi_model_hpo.py). Nuestro script sigue un flujo simple: captura los hiperparámetros con los que se configuró el trabajo y entrena el modelo CatBoost y el modelo XGBoost. También implementamos una función de validación cruzada k-fold. Observa el siguiente código:
if __name__ == "__main__":
parser = argparse.ArgumentParser()
# Argumentos específicos de SageMaker. Los valores predeterminados se establecen en las variables de entorno.
parser.add_argument("--output-data-dir", type=str, default=os.environ["SM_OUTPUT_DATA_DIR"])
parser.add_argument("--model-dir", type=str, default=os.environ["SM_MODEL_DIR"])
parser.add_argument("--train", type=str, default=os.environ["SM_CHANNEL_TRAIN"])
parser.add_argument("--validation", type=str, default=os.environ["SM_CHANNEL_VALIDATION"])
.
.
.
"""
Entrena CatBoost
"""
K = args.k_fold
catboost_hyperparameters = {
"max_depth": args.max_depth,
"eta": args.eta,
}
rmse_list, model_catboost = cross_validation_catboost(train_df, K, catboost_hyperparameters)
.
.
.
"""
Entrena el modelo XGBoost
"""
hyperparameters = {
"max_depth": args.max_depth,
"eta": args.eta,
"objective": args.objective,
"num_round": args.num_round,
}
rmse_list, model_xgb = cross_validation(train_df, K, hyperparameters)
Después de entrenar los modelos, calculamos la media de las predicciones de CatBoost y XGBoost. El resultado, pred_mean
, es la predicción final de nuestro conjunto. Luego, determinamos el mean_squared_error
contra el conjunto de validación. val_rmse
se utiliza para la evaluación de todo el conjunto durante el entrenamiento. Observa que también imprimimos el valor de RMSE en un patrón que se ajusta a la expresión regular que usamos en metric_definitions
. Más tarde, SageMaker Automatic Model Tuning lo utilizará para capturar la métrica objetivo. Mira el siguiente código:
pred_mean = np.mean(np.array([pred_catboost, pred_xgb]), axis=0)
val_rmse = mean_squared_error(y_validation, pred_mean, squared=False)
print(f"Resultado final de evaluación: validation-rmse:{val_rmse}")
Finalmente, nuestro script guarda los artefactos de ambos modelos en la carpeta de salida ubicada en /opt/ml/model
.
Cuando se completa un trabajo de entrenamiento, SageMaker empaqueta y copia el contenido del directorio /opt/ml/model
como un único objeto en formato TAR comprimido a la ubicación de S3 que especificaste en la configuración del trabajo. En nuestro caso, SageMaker empaqueta los dos modelos en un archivo TAR y lo carga en Amazon S3 al final del trabajo de entrenamiento. Mira el siguiente código:
model_file_name = 'catboost-regressor-model.dump'
# Guardar modelo de CatBoost
path = os.path.join(args.model_dir, model_file_name)
print('guardando archivo de modelo en {}'.format(path))
model.save_model(path)
.
.
.
# Guardar modelo de XGBoost
model_location = args.model_dir + "/xgboost-model"
pickle.dump(model, open(model_location, "wb"))
logging.info("Modelo entrenado almacenado en {}".format(model_location))
En resumen, debes observar que en este procedimiento descargamos los datos una vez y entrenamos dos modelos usando un único trabajo de entrenamiento.
Ajuste automático del modelo de conjunto
Dado que estamos construyendo una colección de modelos de aprendizaje automático, explorar todas las posibles combinaciones de hiperparámetros es impráctico. SageMaker ofrece Ajuste Automático de Modelos (AMT), que busca los mejores hiperparámetros del modelo centrándose en las combinaciones más prometedoras de valores dentro de los rangos que especificas (depende de ti definir los rangos adecuados para explorar). SageMaker admite múltiples métodos de optimización para que elijas.
Comenzamos definiendo las dos partes del proceso de optimización: la métrica objetivo y los hiperparámetros que queremos ajustar. En nuestro ejemplo, utilizamos el RMSE de validación como métrica objetivo y ajustamos eta
y max_depth
(para obtener información sobre otros hiperparámetros, consulta los hiperparámetros de XGBoost e hiperparámetros de CatBoost):
from sagemaker.tuner import (
IntegerParameter,
ContinuousParameter,
HyperparameterTuner,
)
hyperparameter_ranges = {
"eta": ContinuousParameter(0.2, 0.3),
"max_depth": IntegerParameter(3, 4)
}
metric_definitions = [{"Name": "validation:rmse", "Regex": "validation-rmse:([0-9\\.]+)"}]
objective_metric_name = "validation:rmse"
También debemos asegurarnos de que en el script de entrenamiento nuestros hiperparámetros no estén codificados de forma rígida y se extraigan de los argumentos de ejecución de SageMaker:
catboost_hyperparameters = {
"max_depth": args.max_depth,
"eta": args.eta,
}
SageMaker también escribe los hiperparámetros en un archivo JSON y se pueden leer desde /opt/ml/input/config/hyperparameters.json
en la instancia de entrenamiento.
Al igual que CatBoost, también capturamos los hiperparámetros para el modelo de XGBoost (observa que objective
y num_round
no se ajustan):
catboost_hyperparameters = {
"max_depth": args.max_depth,
"eta": args.eta,
}
Finalmente, lanzamos el trabajo de ajuste de hiperparámetros utilizando estas configuraciones:
tuner = HyperparameterTuner(
estimator,
objective_metric_name,
hyperparameter_ranges,
max_jobs=4,
max_parallel_jobs=2,
objective_type='Minimize'
)
tuner.fit({"train": train_location, "validation": validation_location}, include_cls_metadata=False)
Cuando el trabajo esté completo, puedes obtener los valores del mejor trabajo de entrenamiento (con un RMSE mínimo):
job_name=tuner.latest_tuning_job.name
attached_tuner = HyperparameterTuner.attach(job_name)
attached_tuner.describe()["BestTrainingJob"]
Para obtener más información sobre AMT, consulta Realizar Ajuste Automático de Modelo con SageMaker.
Implementación
Para implementar nuestro conjunto personalizado, necesitamos proporcionar un script para manejar la solicitud de inferencia y configurar el hospedaje de SageMaker. En este ejemplo, utilizamos un solo archivo que incluye tanto el código de entrenamiento como el de inferencia (multi_model_hpo.py). SageMaker utiliza el código bajo el if _ name _ == "_ main _"
para el entrenamiento y las funciones model_fn
, input_fn
y predict_fn
al implementar y servir el modelo.
Script de inferencia
Al igual que con el entrenamiento, utilizamos el contenedor de framework SKLearn de SageMaker con nuestro propio script de inferencia. El script implementará tres métodos requeridos por SageMaker.
Primero, el método model_fn
lee nuestros archivos de artefacto de modelo guardados y los carga en la memoria. En nuestro caso, el método devuelve nuestro conjunto como all_model
, que es una lista de Python, pero también puedes usar un diccionario con los nombres de los modelos como claves.
def model_fn(model_dir):
catboost_model = CatBoostRegressor()
catboost_model.load_model(os.path.join(model_dir, model_file_name))
model_file = "xgboost-model"
model = pickle.load(open(os.path.join(model_dir, model_file), "rb"))
all_model = [catboost_model, model]
return all_model
Segundo, el método input_fn
deserializa los datos de entrada de la solicitud para pasarlos a nuestro controlador de inferencia. Para obtener más información sobre los controladores de entrada, consulta Adaptar tu propio contenedor de inferencia.
def input_fn(input_data, content_type):
dtype=None
payload = StringIO(input_data)
return np.genfromtxt(payload, dtype=dtype, delimiter=",")
Tercero, el método predict_fn
se encarga de obtener predicciones a partir de los modelos. El método toma como parámetros el modelo y los datos devueltos por input_fn
y devuelve la predicción final. En nuestro ejemplo, obtenemos el resultado de CatBoost del primer miembro de la lista de modelos (model[0]
) y XGBoost del segundo miembro (model[1]
), y utilizamos una función de combinación que devuelve la media de ambas predicciones:
def predict_fn(input_data, model):
predictions_catb = model[0].predict(input_data)
dtest = xgb.DMatrix(input_data)
predictions_xgb = model[1].predict(dtest,
ntree_limit=getattr(model, "best_ntree_limit", 0),
validate_features=False)
return np.mean(np.array([predictions_catb, predictions_xgb]), axis=0)
Ahora que tenemos nuestros modelos entrenados y el script de inferencia, podemos configurar el entorno para implementar nuestro conjunto.
Inferencia sin servidor de SageMaker
Aunque hay muchas opciones de hospedaje en SageMaker, en este ejemplo utilizamos un punto de conexión sin servidor. Los puntos de conexión sin servidor lanzan automáticamente recursos informáticos y los escalan según el tráfico. Esto elimina la carga pesada de gestionar servidores. Esta opción es ideal para cargas de trabajo que tienen períodos de inactividad entre ráfagas de tráfico y que pueden tolerar arranques en frío.
La configuración del punto de conexión sin servidor es sencilla porque no necesitamos elegir tipos de instancia ni administrar políticas de escalado. Solo necesitamos proporcionar dos parámetros: tamaño de memoria y concurrencia máxima. El punto de conexión sin servidor asigna automáticamente recursos informáticos proporcionales a la memoria que seleccionas. Si eliges un tamaño de memoria mayor, tu contenedor tiene acceso a más vCPU. Siempre debes elegir el tamaño de memoria del punto de conexión según el tamaño de tu modelo. El segundo parámetro que debemos proporcionar es la concurrencia máxima. Para un solo punto de conexión, este parámetro se puede configurar hasta 200 (en el momento de escribir esto, el límite para el número total de puntos de conexión sin servidor en una región es de 50). Debes tener en cuenta que la concurrencia máxima para un punto de conexión individual evita que el punto de conexión consuma todas las invocaciones permitidas para tu cuenta, ya que cualquier invocación adicional al límite máximo se limita (para obtener más información sobre la concurrencia total para todos los puntos de conexión sin servidor por región, consulta Puntos de conexión y cuotas de Amazon SageMaker).
from sagemaker.serverless.serverless_inference_config import ServerlessInferenceConfig
serverless_config = ServerlessInferenceConfig(
memory_size_in_mb=6144,
max_concurrency=1,
)
Ahora que hemos configurado el punto final, finalmente podemos implementar el modelo que fue seleccionado en nuestro trabajo de optimización de hiperparámetros:
estimador=attached_tuner.best_estimator()
predictor = estimador.deploy(serverless_inference_config=serverless_config)
Limpieza
Aunque los puntos finales sin servidor no tienen coste cuando no se utilizan, cuando haya terminado de ejecutar este ejemplo, asegúrese de eliminar el punto final:
predictor.delete_endpoint(predictor.endpoint)
Conclusión
En esta publicación, hemos cubierto un enfoque para entrenar, optimizar e implementar un conjunto personalizado. Detallamos el proceso de utilizar un único trabajo de entrenamiento para entrenar múltiples modelos, cómo utilizar la sintonización automática del modelo para optimizar los hiperparámetros del conjunto y cómo implementar un único punto final sin servidor que combina las inferencias de múltiples modelos.
El uso de este método resuelve posibles problemas de coste y operacionales. El coste de un trabajo de entrenamiento se basa en los recursos que utiliza durante la duración de uso. Al descargar los datos solo una vez para entrenar los dos modelos, reducimos a la mitad la fase de descarga de datos del trabajo y el volumen utilizado para almacenar los datos, reduciendo así el coste total del trabajo de entrenamiento. Además, el trabajo de AMT ejecutó cuatro trabajos de entrenamiento, cada uno con el tiempo y el almacenamiento reducidos mencionados anteriormente, ¡lo que representa un ahorro de costes de 4 veces! En cuanto a la implementación del modelo en un punto final sin servidor, debido a que también se paga por la cantidad de datos procesados, al invocar el punto final solo una vez para dos modelos, se paga la mitad de los cargos de datos de E/S.
Aunque esta publicación solo mostró los beneficios con dos modelos, puede utilizar este método para entrenar, ajustar e implementar numerosos modelos de conjunto para obtener un efecto aún mayor.
Referencias
[1] Raj Kumar, P. Arun; Selvakumar, S. (2011). “Distributed denial of service attack detection using an ensemble of neural classifier”. Computer Communications. 34 (11): 1328–1341. doi:10.1016/j.comcom.2011.01.012.
[2] Bradley Efron, Trevor Hastie, Iain Johnstone and Robert Tibshirani (2004) “Least Angle Regression,” Annals of Statistics (with discussion), 407-499. (https://web.stanford.edu/~hastie/Papers/LARS/LeastAngle_2002.pdf)