¡Bienvenido de nuevo! Como sabe que implementará este modelo a través de Docker en Lambda, eso dicta cómo se debe estructurar su proceso de inferencia.
Necesita construir un “controlador”. ¿Qué es eso exactamente? Es solo una función que acepta el objeto JSON que se pasa a Lambda y devuelve los resultados de su modelo, nuevamente en una carga útil JSON. Por lo tanto, todo lo que hará su canal de inferencia debe llamarse dentro de esta función.
En el caso de mi proyecto, tengo una base de código completa de funciones de ingeniería de características: montañas de cosas que involucran incrustaciones semánticas, un montón de agregaciones, expresiones regulares y más. Los he consolidado en un FeatureEngineering clase, que tiene varios métodos privados pero solo uno público, feature_eng. Entonces, a partir del JSON que se pasa al modelo, ese método puede ejecutar todos los pasos necesarios para obtener los datos desde “sin procesar” hasta “características”. Me gusta configurar de esta manera porque abstrae mucha complejidad de la función del controlador en sí. Literalmente puedo simplemente llamar:
fe = FeatureEngineering(input=json_object)
processed_features = fe.feature_eng()
Y me voy a las carreras, mis rasgos aparecen limpios y listos para comenzar.
Tenga en cuenta: he escrito pruebas unitarias exhaustivas sobre todos los aspectos internos de esta clase porque, si bien es bueno escribirla de esta manera, aún necesito ser extremadamente consciente de cualquier cambio que pueda ocurrir bajo el capó. ¡Escribe tus pruebas unitarias! Si realiza un pequeño cambio, es posible que no pueda darse cuenta inmediatamente de que ha roto algo en el proceso hasta que ya esté causando problemas.
La segunda mitad es el trabajo de inferencia y, en mi caso, esta es una clase separada. He optado por un enfoque muy similar, que solo incluye algunos argumentos.
ps = PredictionStage(features=processed_features)
predictions = ps.predict(
feature_file="feature_set.json",
model_file="classifier",
)
La inicialización de la clase acepta el resultado del método de la clase de ingeniería de características, de modo que el protocolo de enlace esté claramente definido. Luego, el método de predicción toma dos elementos: el conjunto de características (un archivo JSON que enumera todos los nombres de las características) y el objeto modelo, en mi caso un clasificador CatBoost que ya entrené y guardé. Estoy usando el método de guardado nativo CatBoost, pero cualquier cosa que uses y cualquier algoritmo de modelo que uses está bien. El punto es que este método abstrae un montón de cosas subyacentes y devuelve claramente el resultado. predictions objeto, que es lo que mi Lambda le dará cuando se ejecute.
Entonces, para resumir, mi función de “controlador” es esencialmente esta:
def lambda_handler(json_object, _context):fe = FeatureEngineering(input=json_object)
processed_features = fe.feature_eng()
ps = PredictionStage(features=processed_features)
predictions = ps.predict(
feature_file="feature_set.json",
model_file="classifier",
)
return predictions.to_dict("records")
¡Nada más! Es posible que desee agregar algunos controles para entradas con formato incorrecto, de modo que si su Lambda obtiene un JSON vacío, una lista o alguna otra cosa extraña, esté listo, pero eso no es necesario. Sin embargo, asegúrese de que su salida esté en formato JSON o similar (aquí le devuelvo un dictado).
Todo esto es genial, tenemos un proyecto de Poetry con un entorno completamente definido y todas las dependencias, además de la capacidad de cargar los módulos que creamos, etc. Buen material. Pero ahora necesitamos traducir eso en una imagen de Docker que podamos colocar en AWS.
Aquí les muestro un esqueleto del archivo acoplable para esta situación. Primero, utilizamos AWS para obtener la imagen base correcta para Lambda. A continuación, debemos configurar la estructura de archivos que se utilizará dentro de la imagen de Docker. Esto puede o no ser exactamente igual a lo que tienes en tu proyecto de Poesía; el mío no lo es, porque tengo un montón de basura extra aquí y allá que no es necesaria para el proceso de inferencia de producción, incluido mi código de entrenamiento. . Sólo necesito poner las inferencias en esta imagen, eso es todo.
El comienzo del archivo acoplable
FROM public.ecr.aws/lambda/python:3.9ARG YOUR_ENV
ENV NLTK_DATA=/tmp
ENV HF_HOME=/tmp
En este proyecto, todo lo que copie vivirá en un /tmp carpeta, por lo que si tiene paquetes en su proyecto que intentarán guardar datos en cualquier momento, debe dirigirlos al lugar correcto.
También debes asegurarte de que Poetry se instale directamente en tu imagen de Docker; eso es lo que hará que todas tus dependencias cuidadosamente seleccionadas funcionen correctamente. Aquí estoy configurando la versión y contando pip instalar Poetry antes de continuar.
ENV YOUR_ENV=${YOUR_ENV} \
POETRY_VERSION=1.7.1
ENV SKIP_HACK=trueRUN pip install "poetry==$POETRY_VERSION"
El siguiente problema es asegurarse de que todos los archivos y carpetas que su proyecto usa localmente se agreguen correctamente a esta nueva imagen: la copia de Docker a veces aplana los directorios de manera irritante, por lo que si construye esto y comienza a ver problemas de “módulo no encontrado”, verifique para hacer Seguro que eso no te está pasando. Pista: añadir RUN ls -R al dockerfile una vez que todo esté copiado para ver cómo se ve el directorio. Podrá ver esos registros en Docker y podría revelar cualquier problema.
Además, ¡asegúrate de copiar todo lo que necesitas! Eso incluye el archivo Lambda, sus archivos de poesía, su archivo de lista de funciones y su modelo. Todo esto será necesario a menos que los almacene en otro lugar, como en S3, y haga que Lambda los descargue sobre la marcha. (Esa es una estrategia perfectamente razonable para desarrollar algo como esto, pero no lo que estamos haciendo hoy).
WORKDIR ${LAMBDA_TASK_ROOT}COPY /poetry.lock ${LAMBDA_TASK_ROOT}
COPY /pyproject.toml ${LAMBDA_TASK_ROOT}
COPY /new_package/lambda_dir/lambda_function.py ${LAMBDA_TASK_ROOT}
COPY /new_package/preprocessing ${LAMBDA_TASK_ROOT}/new_package/preprocessing
COPY /new_package/tools ${LAMBDA_TASK_ROOT}/new_package/tools
COPY /new_package/modeling/feature_set.json ${LAMBDA_TASK_ROOT}/new_package
COPY /data/models/classifier ${LAMBDA_TASK_ROOT}/new_package
¡Ya casi hemos terminado! Lo último que debe hacer es instalar su entorno Poetry y luego configurar su controlador para que se ejecute. Hay un par de banderas importantes aquí, incluyendo --no-dev que le indica a Poetry que no agregue ninguna herramienta de desarrollo que tenga en su entorno, tal vez como pytest o black.
El final del archivo docker
RUN poetry config virtualenvs.create false
RUN poetry install --no-devCMD [ "lambda_function.lambda_handler" ]
Eso es todo, ¡ya tienes tu archivo acoplable! Ahora es el momento de construirlo.
- Asegúrese de que Docker esté instalado y ejecutándose en su computadora. Esto puede tardar un segundo pero no será demasiado difícil.
- Vaya al directorio donde está su dockerfile, que debería ser el nivel superior de su proyecto, y ejecute
docker build .Deje que Docker haga lo suyo y luego, cuando haya completado la compilación, dejará de devolver mensajes. Puede ver en la consola de la aplicación Docker si se creó correctamente. - Vuelve a la terminal y ejecuta
docker image lsy verá la nueva imagen que acaba de crear y tendrá un número de identificación adjunto. - Desde la terminal una vez más, ejecuta
docker run -p 9000:8080 IMAGE ID NUMBERcon su número de identificación del paso 3 completado. ¡Ahora su imagen de Docker comenzará a ejecutarse! - Abra una nueva terminal (Docker está adjunto a su ventana anterior, simplemente déjela allí) y podrá pasar algo a su Lambda, que ahora se ejecuta a través de Docker. Personalmente me gusta poner mis entradas en un archivo JSON, como
lambda_cases.jsony ejecútelos así:
curl -d @lambda_cases.json http://localhost:9000/2015-03-31/functions/function/invocations
Si el resultado en la terminal son las predicciones del modelo, entonces estás listo para rockear. De lo contrario, revise los errores y vea qué podría estar mal. Lo más probable es que tengas que depurar un poco y resolver algunos problemas antes de que todo funcione sin problemas, pero eso es parte del proceso.
La siguiente etapa dependerá en gran medida de la configuración de su organización y no soy un experto en Devops, por lo que tendré que ser un poco vago. Nuestro sistema utiliza AWS Elastic Container Registry (ECR) para almacenar la imagen de Docker creada y Lambda accede a ella desde allí.
Cuando esté completamente satisfecho con la imagen de Docker del paso anterior, deberá compilarla una vez más, utilizando el formato siguiente. La primera bandera indica la plataforma que está utilizando para Lambda. (Ponga un marcador en eso, volverá a aparecer más tarde). El elemento después del indicador -t es la ruta a donde van sus imágenes de AWS ECR; complete su número de cuenta, región y nombre de proyecto correctos.
docker build . --platform=linux/arm64 -t accountnumber.dkr.ecr.us-east-1.amazonaws.com/your_lambda_project:latest
Después de esto, debe autenticarse en un registro de Amazon ECR en su terminal, probablemente usando el comando aws ecr get-login-password y utilizando las banderas apropiadas.
Finalmente, puede enviar su nueva imagen de Docker a ECR:
docker push accountnumber.dkr.ecr.us-east-1.amazonaws.com/your_lambda_project:latest
Si te has autenticado correctamente, esto sólo debería llevarte un momento.
Hay un paso más antes de que esté listo para comenzar: configurar Lambda en la interfaz de usuario de AWS. Inicie sesión en su cuenta de AWS y busque el producto “Lambda”.
Abra el menú de la izquierda y busque “Funciones”.
Aquí es donde irás para encontrar tu proyecto específico. Si aún no ha configurado Lambda, presione “Crear función” y siga las instrucciones para crear una nueva función basada en la imagen de su contenedor.
Si ya ha creado una función, busque esa. A partir de ahí, todo lo que necesitas hacer es presionar “Implementar nueva imagen”. Independientemente de si se trata de una función completamente nueva o simplemente de una imagen nueva, asegúrese de seleccionar la plataforma que coincida con lo que hizo en su compilación de Docker. (¿Recuerdas ese alfiler?)
La última tarea, y la razón por la que he seguido explicando hasta este punto, es probar su imagen en el entorno Lambda real. ¡Esto puede generar errores que no encontró en sus pruebas locales! Vaya a la pestaña Prueba y cree una nueva prueba ingresando un cuerpo JSON que refleje lo que su modelo verá en producción. Ejecute la prueba y asegúrese de que su modelo haga lo previsto.
Si funciona, entonces ¡lo lograste! Has implementado tu modelo. ¡Felicidades!
Sin embargo, hay una serie de posibles contratiempos que pueden aparecer aquí. ¡Pero que no cunda el pánico si tiene un error! Hay soluciones.
- Si su Lambda se queda sin memoria, vaya a la pestaña Configuraciones y aumente la memoria.
- Si la imagen no funcionó porque es demasiado grande (10 GB es el máximo), regrese a la etapa de creación de Docker e intente reducir el tamaño del contenido. No empaquete archivos extremadamente grandes si el modelo puede prescindir de ellos. En el peor de los casos, es posible que necesite guardar su modelo en S3 y hacer que la función lo cargue.
- Si tiene problemas para navegar por AWS, no es el primero. Consulte con su equipo de TI o Devops para obtener ayuda. ¡No cometa un error que le costará mucho dinero a su empresa!
- Si tiene otro problema que no se menciona, publique un comentario y haré todo lo posible para asesorarlo.
¡Buena suerte, feliz modelaje!