Compartilhando dependências de ML em imagem de contêiner entre múltiplas AWS Lambdas

Andre Ruas
Senior Sistemas
Published in
5 min readMar 16, 2022
Créditos: Erwan Hesry em Unsplash

André Ruas
Paula Campigotto

Em dezembro de 2020 a AWS anunciou uma novidade: o suporte a imagens de contêiner pelas funções Lambda. Essa novidade possibilita implantar funções Lambda como imagens de contêiner de até 10 GB de tamanho, em contrapartida aos 50MB compactados (.zip) e 250MB não compactados disponíveis, já anteriormente, para o deployment package.

O suporte a imagens de contêiner abriu uma gama de possibilidades para criação de API’s serverless utilizando Lambdas, incluindo a implantação de modelos de inteligência artificial, tarefas de manipulação de dados, ou quaisquer outras cargas de trabalho que exigem um maior número de dependências.

Neste post vamos explicar uma forma simples de como empacotar múltiplas funções Lambda e suas dependências (de forma compartilhada) em uma imagem de contêiner e realizar o deploy para a AWS utilizando o serverless framework.

Arquitetura do projeto

A fim de demonstrar a utilização de imagem de contêiner para empacotar as Lambdas, construiremos um pipeline de Machine Learning (ML) para classificação de texto utilizando o scikit-learn para criação dos modelos e o pandas para manipulação dos dados . Os arquivos utilizados para a construção dessa aplicação estão disponíveis neste repositório.

A Figura 1 ilustra a arquitetura proposta, na qual serão criadas três Lambdas: uma Lambda conectada ao AWS API Gateway (1) que irá salvar os dados de treinamento no S3 (2), uma Lambda de treinamento que será invocada assim que os dados forem inseridos no S3 (3), e fará a criação de um modelo de classificação utilizando o Logistic Regression e a exportação do modelo e de sua acurácia para o S3 (4). E uma terceira Lambda, de inferência, que irá receber os dados para classificação (5), carregar o modelo do S3 (6) e retornar a acurácia do modelo e a predição (7).

Desenvolvimento

Nesse projeto, utilizaremos os seguintes serviço da AWS:

Template da Arquitetura

Para realizar o deploy da arquitetura para AWS foi utilizado o serverless framework que provê uma abstração simples do processo de criação e gerenciamento dos serviços da AWS.

Vamos explicar três pontos importantes do template abaixo (serverless.yml). O primeiro é a criação do bucket S3, em resources, nele é configurado aNotificationConfiguration , a qual configura um evento para indicar que quando um arquivo com sufixo .csv for criado no S3, ele deve chamar a Lambda de treinamento.

O segundo ponto envolve as permissões: a label S3InvokeLambdaPermission, define a permissão para o S3 invocar a Lambda de treinamento, e as manipulações permitidas no bucket são definidas na label iamRoleStatements .

E o terceiro ponto, envolve as functions e o ecr , e funciona da seguinte forma (conforme a documentação): no ecr é definido o nome da imagem e o caminho do Dockerfile que será utilizado para criação da imagem.

Já nas functions , que geram as Lambdas, adicionamos a propriedade image que indica ao serverless framework que iremos realizar o deploy da Lambda para o ECR e não para o S3. Na propriedade image definimos ainda: o name , nome da imagem que a Lambda fará parte, o command que segue a notação nome_do_arquivo.função, tal que,train.handler indica a execução da função handler no arquivo train.py. Por fim definimos também a propriedade entryPoint que está diretamente relacionada com a imagem base que será determinada no Dockerfile.

Dockerfile

Precisamos criar agora o Dockerfile, que será utilizado para criação do contêiner assim que o serverless framework identificar a propriedade ecr no serverless.yml. A primeira coisa que precisamos fazer é definir nossa base image, no caso, utilizaremos a public.ecr.aws/lambda/python:3.8, uma base image para python fornecida pela AWS.

Na sequência adicionamos as Lambdas no diretório definido pela variável LAMBDA_TASK_ROOT, fornecida pela nossa base image. E por último copiamos e instalamos os requirements no contêiner.

Requirements

Para a execução deste projeto, foram definidos os seguintes requisitos:

Funcionamento da Aplicação

A ideia aqui foi criar uma aplicação usual mas que ilustra a utilização tanto da arquitetura, quanto do compartilhamento das dependências entre as Lambdas.

Sendo assim, foi criada uma aplicação que deve receber um valor para a chave model_id , que define o nome do modelo que será salvo no S3, e outro valor para a chave dataset, que será o conjunto dos dados de treinamento (constituído por um arquivo csvcom a primeira coluna sendo a informação textual e a segunda as labels) convertido para base64.

E para a realização da inferência, é necessário o envio do model_id do modelo que se deseja utilizar e a inference que corresponde ao conjunto de dados de predição (constituído por um arquivo csvcom apenas a coluna de informações textuais) convertido para base64.

Lambdas

O arquivo saveData.py é responsável apenas por decodificar os dados de entrada e salvá-los no S3 no formato csv para que o trigger do S3 seja ativado e ele invoque a Lambda de treinamento.

Já o arquivo train.py contém o código responsável por aplicar o pipeline de criação do modelo, aplicando a vetorização e extração de características dos textos de entrada e utilizando o Logistic Regression para treinar o modelo. Após o treinamento, essa Lambda salva no S3 o modelo resultante e a acurácia do modelo.

A terceira Lambda, inference.py, é responsável por receber um conjunto de dados a serem classificados, carregar o modelo do S3, e retornar tanto a acurácia do modelo, quanto a predição dos dados de entrada.

Payload de envio dos dados de treinamento

{ 
"model_id" : "123",
"dataset": "LEZ1bGxfVGV4dCxnb3QsZ3JQsd2F0LGZyZWUsdGV4dCx0eH[...]"
}

Payload de envio das inferências

{ 
"model_id" : "123",
"inference": "GF5LG1vbmV5LGJhYmUsc29tZXRoaW5nLHdhaXRpb[...]"
}

Lembrando que tanto para o conjunto de dados de treinamento(dataset) quanto para os dados de predição (inference) é necessária a codificação em base64 antes do envio.

Obs: Neste módulo estão armazenados dois arquivos csv para realizar o treinamento (treinamento.csv)e a inferência (inferencia.csv) dos modelos, os quais podem ser codificados para base64 utilizando a função do arquivo encodeb64.py .

Deploy

Antes de realizar o deploy certifique-se de que todos arquivos estão no mesmo diretório, conforme a estrutura de arquivos a seguir.

├── Dockerfile
├── requirements.txt
├── saveData.py
├── train.py
├── inference.py
└── serverless.yml

Para realizar o deploy da aplicação inteira para AWS é necessário apenas realizar o comando $ serverless deploy no terminal, dentro do diretório. O serverless framework irá criar a imagem de contêiner local, criar o ECR na AWS, realizar o push da imagem de contêiner para o ECR, e criar os serviços especificados no arquivo yml 🥵.

Conclusão

O objetivo desse POST foi mostrar uma forma simples de empacotar múltiplas AWS Lambdas em imagem de contêiner, podendo além de compartilhar seus requirements, ter até 10GB de capacidade. Para isso, foi criado um pipeline de machine learning para classificação de texto, e uma arquitetura que possibilita a realização dos treinamentos e inferências.

É isso! Espero que eu tenha conseguido simplificar todo o processo e incentivado você a utilizar AWS Lambdas para criar seu projeto de machine learning, data science, ou qualquer outro, enfim, o céu é o limite ! … ou melhor , os 10GB … 😝

--

--